Please enable Javascript to view the contents

KubeSphere DevOps 3.0 流水线开发指南

 ·  ☕ 7 分钟

KubeSphere DevOps 包含 S2I 和 Pipeline 两部分。在社区中,openshift 提供了一个打包应用的工具 S2I,具体请参考 使用 S2I 构建云原生应用
。KubeSphere 将其做成了服务,采用 CRD 使用一个单独的 Operator 对其进行管理,功能比较独立。而在 3.0 中 Pipeline 与 KubeShere Core 耦合依然十分紧密,在搭建环境和调试上略显复杂。本篇主要提供开发者维护和二次开发 KubeSphere DevOps 3.0 指引。

1. DevOps 流水线架构

下图是流水线的整体架构:

1.1 存储模型

产品概念 Kubernetes 对象 Jenkins 对象
DevOps 工程 DevopsProject 文件夹
流水线 Pipeline 流水线/多分支流水线
凭证 Credential 文件夹下的凭据

1.2 数据流

主要涉及两类,一类是创建类型的操作,另一类是触发类型的操作。

创建类型的操作,主要包括三种 CRD 类型的创建,DevopsProject、Pipeline、Credential 。用户通过前端,调用 ks-apiserver 接口,创建对应资源存储在 Etcd 中,然后 ks-controller-manager 不断地将这些对象同步到 Jenkins 。

触发类型的操作,主要是执行、审核流水线等瞬时动作。用户通过前端,调用 ks-apiserver 经过数据转换,直接调用 Jenkins API 。

2. DevOps 流水线相关的组件

2.1 系统核心组件

  • ks-apiserver

ks-apiserver 是访问服务的 API 入口。在 3.0 中,ks-apigateway、ks-account 被合并到了 ks-apiserver。因此,ks-apiserver 承载了这两个组件的功能。

  • ks-controller-manager

在 3.0 中,DevOps 依然与 KubeSphere Core 代码紧密耦合,没有运行单独的 Operator 处理相关的 CRD 资源。DevOps 所有 CRD 资源的处理都在 ks-controller-manager 中进行。

2.2 Jenkins 流水线

  • ks-jenkins

Jenkins 采用的是 Helm 进行安装和维护,相关的配置可以查看 GitHub 上 ks-installer 仓库

其中 Jenkins 镜像使用的是官方的,没有进行任何定制。

  • uc-jenkins-update-center

uc 是提供给 Jenkins 下载插件的服务。有两方面的原因需要 uc: 一方面是适配离线环境,同时线上官方地址下载慢;另一方面是有自行开发的插件需要集成。

uc 提供的只是一个 Nginx 下载服务,相关的镜像内容也只是为了存储 Jenkins 插件而已。

3. 如何搭建流水线的开发环境

3.1 本地安装 Git、Go、Kubebuilder 基础环境

在此不会详细描述,仅以 OS X 为例。

  • 安装 Git
1
brew install git

查看版本

1
2
3
git version

git version 2.26.2
  • 安装 Golang
1
brew install golang

查看版本

1
2
3
go version

go version go1.14.4 darwin/amd64
  • 安装 Kubebuilder
1
brew install kubebuilder

查看版本

1
2
3
kubebuilder version

Version: version.Version{KubeBuilderVersion:"2.3.0", KubernetesVendor:"1.16.4", GitCommit:"800f63a7e41a6a8016d4cb9d583e1705b0812c9d", BuildDate:"2020-02-28T19:15:41Z", GoOs:"unknown", GoArch:"unknown"}

3.2 安装并配置本地访问

  • 安装 Kubernetes 集群

推荐安装工具 Kubekey。现在主流的集群安装工具是基于 Kubeadm 的二次封装。Kubekey 的优势是国内安装快,配置简单。使用 Kubeadm 半小时的工作量,Kubekey 两分钟解决,还集成了不少插件。这也符合我提倡的,技能文档化,文档工具化,工具产品化,产品服务化 的想法。

在安装时,需要注意一个参数。

1
2
controlPlaneEndpoint:
    domain: k2

由于 kube-apiserver 采用的是 https 通信,这里的 domain 会被设置到 certSANs 中生成证书。通过 certSANs 中地址进行地访问才是合法的。

  • 开发环境配置 hosts

完成安装 Kubernetes 集群之后,在开发环境配置 hosts ,即可直接通过 domain 进行远程访问 kube-apiserver 。下面是我本地的 /etc/hosts 配置,一共有三个集群:

1
2
3
4
5
cat /etc/hosts

139.198.x.x  k1
139.198.x.x  k2
139.198.x.x  k3
  • 开发环境配置 kubeconfig

简单一点,可以直接拷贝服务器的文件到本地保存。这里提供一份脚本,可以快速切换多个环境。注意,替换 your_password 为你的远程登陆密码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Switch K8s
function on_k8s() {
   if test -f ~/.kube/config.bk; then
     rm -rf ~/.kube/config.bk
   fi
   if test -f ~/.kube/config; then
     mv ~/.kube/config ~/.kube/config.bk
   fi
   sed -i'.s' -e '/$1/d'  ~/.ssh/known_hosts
   sshpass  -p "your_password" ssh -o StrictHostKeyChecking=no [email protected]$1 "cat /etc/kubernetes/admin.conf" > ~/.kube/config
   sed -i'.s' -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}'/$1/ ~/.kube/config
   sed -i'.s' -E 's/kubernetes[email protected]'/$1/ ~/.kube/config
}

使用时,执行 on_k8s k1 切换到 k1 环境,执行 on_k8s k2 切换到 k2 环境。

  • 验证配置是否成功
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 切换环境
on_k8s k2

# 执行 kubectl 命令
kubectl get node

NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   54d   v1.17.9
node1    Ready    worker   54d   v1.17.9
node2    Ready    worker   54d   v1.17.9

3.3 克隆 KubeSphere 仓库代码

star & fork GitHub 项目 kubesphere/kubesphere 。Git 的提交流程可以参考文档: 一个完整的 Git 提交流程

  • 克隆代码
1
git clone https://github.com/shaowenchen/kubesphere
  • 进入项目目录
1
cd kubesphere

3.4 配置 Webhook 证书

执行如下命令,在 $TMPDIR 目录下生成证书。

1
2
3
4
5
mkdir -p .keys && openssl req -nodes -new -x509 -keyout ./.keys/ca.key -out ./.keys/ca.crt -subj "/CN=cronprimer CA"
openssl genrsa -out ./.keys/tls.key 2048
openssl req -new -key ./.keys/tls.key -subj "/CN=webhook-server.webhook.svc" | openssl x509 -req -CA ./.keys/ca.crt -CAkey ./.keys/ca.key -CAcreateserial -out ./.keys/tls.crt
mkdir -p $TMPDIR/k8s-webhook-server/serving-certs
cp ./.keys/* $TMPDIR/k8s-webhook-server/serving-certs/
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tree $TMPDIR/k8s-webhook-server

/Users/shaowenchen/Temp/k8s-webhook-server
└── serving-certs
    ├── ca.crt
    ├── ca.key
    ├── ca.srl
    ├── tls.crt
    └── tls.key

1 directory, 5 files

3.5 配置 kubesphere.yaml

  • 在项目根目录下,新增 kubesphere.yaml 文件。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
kubernetes:
  kubeconfig: "/Users/shaowenchen/.kube/config"
  master: k2:6443
  qps: 1e+06
  burst: 1000000

devops:
  host: http://k2:30180/
  username: admin
  password: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluQGt1YmVzcGhlcmUuaW8iLCJ1c2VybmFtZSI6ImFkbWluIiwidG9rZW5fdHlwZSI6InN0YXRpY190b2tlbiJ9.eoVAs9uWPi54YTknQ4NaaomVdq3q-THsZMOb4TwChU4
  maxConnections: 100

sonarQube:
  host: http://k2:30594
  token: cfa96640569d9ce3b6f84ae287bcc1a970973958
s3:
  endpoint: http://k2:9001
  region: us-east-1
  disableSSL: true
  forcePathStyle: true
  accessKeyID: openpitrixminioaccesskey
  secretAccessKey: openpitrixminiosecretkey
  bucket: s2i-binaries
authentication:
  authenticateRateLimiterMaxTries: 10
  authenticateRateLimiterDuration: 10m0s
  loginHistoryRetentionPeriod: 168h
  maximumClockSkew: 10s
  multipleLogin: true
  kubectlImage: kubesphere/kubectl:v1.0.0
  jwtSecret: "2b0WJPoacvbvMB82bTEWO4s4vFlmzoPd"
  oauthOptions:
    accessTokenMaxAge: 0
    AccessTokenInactivityTimeout: 0
  authorization:
    mode: "RBAC"
    # mode: "AlwaysAllow"
monitoring:
  endpoint: FAKE
ldap:
  host: FAKE
redis:
  host: FAKE

这里我直接使用远程环境的访问地址,比使用 telepresence 通过集群服务地址访问效率更高。kubesphere.yaml 中相关的配置值,可以从 kubectl get cm kubesphere-config -n kubesphere-system -o yaml 的输出中获取,粘贴到开发环境中即可。

值得注意的是 3.0 中,采用外置的 Sonarqube ,因此,需要根据官方文档进行单独配置。kubeconfig 字段指向的是开发环境 kubeconfig 文件地址,而线上使用的是 serviceaccount 。S2I 依赖于 S3 服务存储二进制文件,流水线不需要 S3。

3.6 本地运行测试服务

  • 运行 ks-apiserver
1
go run cmd/ks-apiserver/apiserver.go --logtostderr=true --v=8 --debug=true

服务启动之后,会打印 Start listening on :9090 表示,ks-apiserver 监听在 9090 端口,可以访问。

通过 Postman 进行访问时,需要带上集群前端的 Token,也可以使用 kubesphere-config 中的永久 Token,还可以将 mode 改为 AlwaysAllow 关掉鉴权。

在 Postman 中,可以设置一下变量,通过 {{ VAR_NAME }} 的形式可以引用,十分方便。下面有两类 API 的调用示例:

一种是 CRD 资源的增删改查,带上 Token 即可。

另一种是透传 Jenkins API ,不仅需要带上 Token,还需要带上 Jenkins Crumb 。这些参数通过页面访问时,在 Cookies 中都可以拿到。

  • 运行 ks-controller-manager

为了避免干扰,需要先暂停集群中的 ks-controller-manager 。

1
kubectl scale deploy ks-controller-manager --replicas=0 -n kubesphere-system

运行本地的 ks-controller-manager

1
go run cmd/controller-manager/controller-manager.go --logtostderr=true --v=8 --multiple-clusters=false

4. 如何发布到集群环境

建议将相关服务的 imagePullPolicy 改为 Always ,确保每次使用的都是最新的镜像。

4.1 更新插件

建立如下目录结构:

1
2
3
4
5
6
tree -L 1
.
|-- Dockerfile
`-- webroot

1 directory, 2 files

Dockerfile 内容

FROM busybox:1.29.3

COPY webroot/ /webroot/

webroot 中的内容,可以先运行 docker run -it -d kubesphere/jenkins-uc:v3.0.0 获取到容器 ID ,然后使用 docker cp {UC_ContainerID}:/webroot ./ 命令拷贝插件,自由替换、增删其中的离线插件。

最后使用 docker build . -t shaowenchen/jenkins-uc:latest 命令打包,推送镜像。然后执行命令 kubectl -n kubesphere-devops-system edit deploy uc-jenkins-update-center ,修改 uc 的服务镜像为 shaowenchen/jenkins-uc:latest ,重启 deploy 即可。

这里需要注意的是 Jenkins 只有在首次初始化时,访问 uc 获取插件。初始化插件列表在 kubectl -n kubesphere-devops-system get cm ks-jenkins -o yaml 中可以查看。

4.2 更新 ks-apiserver 或 ks-controller-manger

  • 编译 ks-apiserver
1
make ks-apiserver
  • 编译并推送 ks-apiserver 镜像
docker build -f build/ks-apiserver/Dockerfile -t shaowenchen/ks-apiserver:latest .
docker push shaowenchen/ks-apiserver:latest
  • 更新 ks-apiserver 服务

执行命令,更新镜像为 shaowenchen/ks-apiserver:latest 即可。

1
kubectl -n kubesphere-system edit deploy ks-apiserver
  • 编译 ks-controller-manager
1
make controller-manager
  • 编译并推送 ks-controller-manager 镜像
docker build -f build/ks-controller-manager/Dockerfile -t shaowenchen/ks-controller-manager:latest .
docker push shaowenchen/ks-controller-manager:latest
  • 更新 ks-controller-manger 服务

执行命令,更新镜像为 shaowenchen/ks-controller-manager:latest 即可。

1
kubectl -n kubesphere-system edit deploy ks-controller-manager

5. 关于鉴权插件

kubesphere-token-auth-plugin 插件主要是为了集成 KubeSphere 的权限体系,使 Jenkins 与之保持一致。如下图,无论是 CRD 资源类型,还是触发动作类型,在调用 Jenkins 时,都需要经过 ks-apiserver 进行 token 的 review。

在 KubeSphere 中 Token 是 bearer 类型,但是插件是基于 Basic 鉴权进行的扩展,所以需要转换。代码如下:

https://github.com/kubesphere/kubesphere/blob/release-3.0/pkg/simple/client/devops/jenkins/request.go#L47:6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func SetBasicBearTokenHeader(header *http.Header) error {
	bearTokenArray := strings.Split(header.Get("Authorization"), " ")
	bearFlag := bearTokenArray[0]
	var err error
	if strings.ToLower(bearFlag) == "bearer" {
		bearToken := bearTokenArray[1]
		if err != nil {
			return err
		}
		claim := authtoken.Claims{}
		parser := jwt.Parser{}
		_, _, err = parser.ParseUnverified(bearToken, &claim)
		if err != nil {
			return err
		}
		creds := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", claim.Username, bearToken)))
		header.Set("Authorization", fmt.Sprintf("Basic %s", creds))
	}
	return nil
}

插件中的逻辑主要是,调用 ks-apiserver 鉴权接口,返回合法用户。

6. 后端代码结构及逻辑

6.1 代码目录

代码路径以 https://github.com/kubesphere/kubesphere/tree/release-3.0 为例。

  • pkg/apiserver/apiserver.go

通过 ks-apiserver 对外提供的接口,都需要在这里进行注册,配置 GVR 等。

  • pkg/kapis/devops

KubeSphere 以 /kapis 为前缀提供 API, 在这里还需要对每个 URL 进行更详细的描述和处理,通常会调用到很多 models 中的函数方法。API 文档也是在这里进行描述的。

  • pkg/models/devops

对于数据层的操作,被聚合在 models 中。这里提供了很多对数据的操作方法。

  • pkg/controller/devopscredential

credential 的 controller 处理部分。

  • pkg/controller/devopsproject

devopsproject 的 controller 处理部分。

  • pkg/controller/pipeline

pipeline 的 controller 处理部分。

  • pkg/simple/client/devops

上面的代码理解起来相对容易,这部分会有些难度。这里的主要功能是提供对 Jenkins 的操作函数方法。在实现时,抽象了一个 Interface ,希望能够对接不同的编排工具。

简单说了下代码目录,可能还是不够清晰。下面一起通过调用逻辑看看代码。

6.2 关于 CRD 类型的代码逻辑

以创建 DevOps 工程为例。

  1. 前端调用 /kapis/devops.kubesphere.io/v1alpha3/workspaces/liuxin-test/devops/ ,传递参数

  2. 后端处理 https://github.com/kubesphere/kubesphere/blob/release-3.0/pkg/kapis/devops/v1alpha3/register.go#L154 ,通过生成的 client 写入 Etcd

  3. ks-controller-manager 处理 https://github.com/kubesphere/kubesphere/blob/release-3.0/pkg/controller/devopsproject/devopsproject_controller.go#L205 ,这里单步运行,可以缕清整个链路。最终是调用 pkg/simple/client/devops 中的接口,在 Jenkins 中,创建一个文件夹。

6.3 关于触发类型的代码逻辑

以触发代码扫描为例。

  1. 前端调用 kapis/devops.kubesphere.io/v1alpha2/devops/test2-projectwvb2n/pipelines/test1-pipeline/scan/ , 传递参数

  2. 后端处理 https://github.com/kubesphere/kubesphere/blob/release-3.0/pkg/kapis/devops/v1alpha2/register.go#L479 ,这是就不需要经过 ks-controller-manager,单步运行调试会发现,依然会进入 pkg/simple/client/devops 然后组装 xml 调用 Jenkins API 。

7. 前端代码结构和逻辑

前端的代码结构清晰,非常直观,文件名能与页面对应上。下面以仓库地址 https://github.com/kubesphere/console/tree/release-3.0 为例进行说明。另外,前端采用的是 React 框架。

7.1 代码目录

主要的逻辑都在 src/pages/devops 目录。

  • components

流水线相关的组件。而 src/components 中是整个项目公共的组件。

  • containers

对应流水线相关的页面。如下图,左侧选项卡与文件夹中文件名一一对应,包括流水线图形化编辑页面。

  • routes

单页面应用的路由配置。

7.2 环境搭建

请参考 https://github.com/kubesphere/console 的文档。


微信公众号
作者
微信公众号