基于 Kubernetes Gitlab CICD

在大多数情况,构建项目都会占用大量的系统资源,如果让 gitlab 本身来运行构建任务的话,显然 Gitlab 的性能会大幅度下降。gitlab ci 最大的作用就是管理各个项目的构建状态,因此,运行构建任务这种浪费资源的事情交给一个独立的 gitlab runner 来做就会好很多,更重要的是 gitlab runner 可以安装到不同的机器上,甚至是我们本机,这样完全就不会影响 Gitlab 本身了。

从 gitlab8.0 开始,gitlab CI 就已经集成在 Gitlab 中,我们只需要在项目中添加一个.gitlab-ci.yaml 文件,然后运行一个 Runner,即可进行持续集成。

什么是 Runner?
Gitlab Runner 是一个开源项目,用于运行您的作业并将结果发送给 gitlab。它与 Gitlab CI 结合使用,gitlab ci 是 Gitlab 随附的用于协调作用的开源持续集成服务。

Gitlab Runner 是用 Go 编写的,可以作为一个二进制文件运行,不需要特定于语言的要求
它皆在 GNU/Linux,MacOS 和 Windows 操作系统上运行。如果要使用 Docker,Gitlab Runner 需要最少 Docker v1.13.0

image_1dlm944o51fvmi72kfc1jrke6f5h.png-298.8kB

本文项目演示图

A7624C76-2F87-48C1-837B-CCD55AB35584.png-178kB

Gitlab 安装
gitlab 官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装,但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置,所以我们这里自定义的方式来安装,也就是自己来自定义一些资源清单文件。

gitlab 主要涉及 3 个应用:Redis、Postgresql、Gitlab 核心程序

部署服务也可以在不跑在容器上,不影响 gitlab 的 cicd。我这里使用 k8s 安装 gitlab,并且没有使用持久化存储,如果想使用持久化存储可以在参考一下 prometheus 搭建的文章

创建命名空间

kubectl create namespace kube-ops
首先需要先部署 Redis 服务 (gitlab-redis.yaml)

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: redis
namespace: kube-ops
labels:
name: redis
spec:
template:
metadata:
name: redis
labels:
name: redis
spec:
containers:
- name: redis
image: sameersbn/redis
imagePullPolicy: IfNotPresent
ports:
- name: redis
containerPort: 6379
volumeMounts:
- mountPath: /var/lib/redis
name: data
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
timeoutSeconds: 1
volumes:
- name: data
emptyDir: {}

apiVersion: v1
kind: Service
metadata:
name: redis
namespace: kube-ops
labels:
name: redis
spec:
ports:
- name: redis
port: 6379
targetPort: redis
selector:
name: redis
数据库 Postgresql 部署 (gitlab-postgresql.yaml)

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: postgresql
namespace: kube-ops
labels:
name: postgresql
spec:
template:
metadata:
name: postgresql
labels:
name: postgresql
spec:
containers:
- name: postgresql
image: sameersbn/postgresql:10
imagePullPolicy: IfNotPresent
env:
- name: DB_USER
value: gitlab
- name: DB_PASS
value: passw0rd
- name: DB_NAME
value: gitlab_production
- name: DB_EXTENSION
value: pg_trgm
ports:
- name: postgres
containerPort: 5432
volumeMounts:
- mountPath: /var/lib/postgresql
name: data
livenessProbe:
exec:
command:
- pg_isready
- -h
- localhost
- -U
- postgres
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
exec:
command:
- pg_isready
- -h
- localhost
- -U
- postgres
initialDelaySeconds: 5
timeoutSeconds: 1
volumes:
- name: data
emptyDir: {}

apiVersion: v1
kind: Service
metadata:
name: postgresql
namespace: kube-ops
labels:
name: postgresql
spec:
ports:
- name: postgres
port: 5432
targetPort: postgres
selector:
name: postgresql
gitlab 应用配置如下 (gitlab.yaml)

这里没有使用官方的镜像仓库,而是使用第三方
http://www.damagehead.com/docker-gitlab/

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: gitlab
namespace: kube-ops
labels:
name: gitlab
spec:
template:
metadata:
name: gitlab
labels:
name: gitlab
spec:
containers:
- name: gitlab
image: sameersbn/gitlab:11.8.1
imagePullPolicy: IfNotPresent
env:
- name: TZ
value: Asia/Shanghai
- name: GITLAB_TIMEZONE
value: Beijing
- name: GITLAB_SECRETS_DB_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_SECRETS_SECRET_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_SECRETS_OTP_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_ROOT_PASSWORD
value: admin321
- name: GITLAB_ROOT_EMAIL
value: 381493251@qq.com
- name: GITLAB_HOST
value: 10.4.82.135:30004
- name: GITLAB_PORT
value: “80”
- name: GITLAB_SSH_PORT
value: “22”
- name: GITLAB_NOTIFY_ON_BROKEN_BUILDS
value: “true”
- name: GITLAB_NOTIFY_PUSHER
value: “false”
- name: GITLAB_BACKUP_SCHEDULE
value: daily
- name: GITLAB_BACKUP_TIME
value: 01:00
- name: DB_TYPE
value: postgres
- name: DB_HOST
value: postgresql
- name: DB_PORT
value: “5432”
- name: DB_USER
value: gitlab
- name: DB_PASS
value: passw0rd
- name: DB_NAME
value: gitlab_production
- name: REDIS_HOST
value: redis
- name: REDIS_PORT
value: “6379”
ports:
- name: http
containerPort: 80
- name: ssh
containerPort: 22
volumeMounts:
- mountPath: /home/git/data
name: data
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 180
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
timeoutSeconds: 1
volumes:
- name: data
emptyDir: {}

apiVersion: v1
kind: Service
metadata:
name: gitlab
namespace: kube-ops
labels:
name: gitlab
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: http
nodePort: 30004
- name: ssh
port: 22
targetPort: ssh
selector:
name: gitlab
################
GITLAB_HOST = 这里的域名是我们 Git clone 代码的域名,我这里直接使用 svc+port 进行演示
接下来进行部署,需要提前创建好命名空间,上面我们已经创建了

kubectl apply -f .
deployment.apps/postgresql created
service/postgresql created
deployment.apps/redis created
service/redis created
deployment.apps/gitlab created
service/gitlab created
检查 pod 和 svc 运行状况

[root@abcdocker gitlab]# kubectl get pod,svc -n kube-ops
NAME READY STATUS RESTARTS AGE
pod/gitlab-57cf47cd7d-cfpmn 0/1 Running 1 5m14s
pod/postgresql-7c6bf8974d-r9djh 1/1 Running 0 5m19s
pod/redis-c45ffd79b-75vbd 1/1 Running 0 5m17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/gitlab NodePort 10.254.212.182 80:30004/TCP,22:8530/TCP 5m11s
service/postgresql ClusterIP 10.254.255.216 5432/TCP 5m18s
service/redis ClusterIP 10.254.83.148 6379/TCP 5m15s
这里我采用的是 nodePort 的方式,然后我们直接访问节点任意 IP+30004 端口即可访问 (如果 pod 没有报错,耐心等待一会就可以访问了)

用户名: root
密码:admin321
用户名和密码配置在 gitlab 配置文件中!

123.png-202.8kB

接下来创建个项目要进行演示

image_1dllo23au1bqg1f3oghpfio1r073a.png-159.9kB
image_1dllo35t01gfhh5i1b251tkbpsc3n.png-343.2kB

接下来添加提交代码
如何提交代码我这里就不详细介绍了,项目的 REDME 也提示我们了

image_1dllo72evptg1rka17hv1g901pq844.png-240.8kB

[root@abcdocker tmp]# git clone http://10.4.82.135:30004/root/abcdocker.git
正克隆到 ‘abcdocker’…
Username for ‘http://10.4.82.135:30004’: root
Password for ‘http://root@10.4.82.135:30004’:
warning: 您似乎克隆了一个空版本库。
wget http://down.i4t.com/abcdocker-gitlab-demo.tar.gz
#git 代码提交
git add *
git add .* (记得提交隐藏文件)
git commit -m “abcdocker”
git push -u origin master
Gitlab Runner 安装
gitlab runner 支持多种方式安装,我这里就采取在 k8s 中安装。

官方文档地址: https://docs.gitlab.com/runner/install/

image_1dlm9cmqti6p1guq7h4aqe7n6e.png-209.9kB

首先我们需要检查一下 k8s 集群的状态

[root@abcdocker ~]# kubectl cluster-info
Kubernetes master is running at https://10.4.82.139:8443
CoreDNS is running at https://10.4.82.139:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.
接下来我们打开 gitlab 地址

如下图所示,左边代表 runner 状态,右边是配置 runner 信息
image_1dlma89l83erat1u6urk1hfq7a.png-359.8kB

接下来进行配置 gitlab runner 资源清单 (runner-configmap.yaml)

apiVersion: v1
data:
REGISTER_NON_INTERACTIVE: “true”
REGISTER_LOCKED: “false”
METRICS_SERVER: “0.0.0.0:9100”
CI_SERVER_URL: “http://gitlab.kube-ops.svc.cluster.local/ci
RUNNER_REQUEST_CONCURRENCY: “4”
RUNNER_EXECUTOR: “kubernetes”
KUBERNETES_NAMESPACE: “kube-ops”
KUBERNETES_PRIVILEGED: “true”
KUBERNETES_CPU_LIMIT: “1”
KUBERNETES_CPU_REQUEST: “500m”
KUBERNETES_MEMORY_LIMIT: “1Gi”
KUBERNETES_SERVICE_CPU_LIMIT: “1”
KUBERNETES_SERVICE_MEMORY_LIMIT: “1Gi”
KUBERNETES_HELPER_CPU_LIMIT: “500m”
KUBERNETES_HELPER_MEMORY_LIMIT: “100Mi”
KUBERNETES_PULL_POLICY: “if-not-present”
KUBERNETES_TERMINATIONGRACEPERIODSECONDS: “10”
KUBERNETES_POLL_INTERVAL: “5”
KUBERNETES_POLL_TIMEOUT: “360”
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-cm
namespace: kube-ops
CI_SERVER_URL 这个地址是 gitlab 的地址,如果 gitlab 在宿主机直接写宿主机的 ip 即可,容器是格式为 svc 名称. 命名空间.svc.cluster.local (如果都按照我的文档来进行安装不需要修改别的配置了)
如果定义的 gitlab 域名并不是通过外网 DNS 解析,而是通过 /etc/hosts 进行映射,那么我们需要在 Runner 的 Pod 中去添加对应的 hosts(我这里使用的是 ip+ 端口)。我们需要通过–pre-clone-script 参数来指定一段脚本来添加 hosts 信息,也就是在 ConfigMap 中添加环境变量 RUNNER_PRE_CLONE_SCRIPT 的值

RUNNER_PRE_CLONE_SCRIPT = “echo ‘xx.xx.xxx.xx git.i4t.com’ >> /etc/hosts”
注意: 在 ConfigMap 添加新选项后,需要删除 Gitlab ci Runner Pod
因为我们使用 envFrom 来注入上面的这些环境变量而不是直接使用 env(envfrom 通过将环境变量放置到 ConfigMaps 或 Secrets 来帮助减小清单文件)

如果我们想添加其他选项,可以在 Pod 中运行 gitlab-ci-multi-runner register –help 命令来查看所有可使用的选项,只需要为配置的标志添加 env 变量即可

gitlab-runner@gitlab-ci-runner-0:/$ gitlab-ci-multi-runner register –help
[…]
–kubernetes-cpu-limit value The CPU allocation given to build containers (default: “1”) [$KUBERNETES_CPU_LIMIT]
–kubernetes-memory-limit value The amount of memory allocated to build containers (default: “4Gi”) [$KUBERNETES_MEMORY_LIMIT]
–kubernetes-service-cpu-limit value The CPU allocation given to build service containers (default: “1”) [$KUBERNETES_SERVICE_CPU_LIMIT]
–kubernetes-service-memory-limit value The amount of memory allocated to build service containers (default: “1Gi”) [$KUBERNETES_SERVICE_MEMORY_LIMIT]
–kubernetes-helper-cpu-limit value The CPU allocation given to build helper containers (default: “500m”) [$KUBERNETES_HELPER_CPU_LIMIT]
–kubernetes-helper-memory-limit value The amount of memory allocated to build helper containers (default: “3Gi”) [$KUBERNETES_HELPER_MEMORY_LIMIT]
–kubernetes-cpu-request value The CPU allocation requested for build containers [$KUBERNETES_CPU_REQUEST]

–pre-clone-script value Runner-specific command script executed before code is pulled [$RUNNER_PRE_CLONE_SCRIPT]
[…]
创建

[root@abcdocker gitlab]# kubectl apply -f runner-configmap.yaml
configmap/gitlab-ci-runner-cm created
[root@abcdocker gitlab]# kubectl get configmaps -n kube-ops
NAME DATA AGE
gitlab-ci-runner-cm 19 12s
我们还需要配置一个用于注册、运行和取消 gitlab ci runner 的小脚本。只有当 Pod 正常通过 Kubernetes (TERM 信号) 终止时,才会触发注销注册。如果强行终止 Pod(SIGKILL 信号),Runner 将不会自己注销自身。必须手动完成对这种 Runner 的清理 (二进制安装非 K8s 上不受这个影响)

(runner-scripts-cm.yaml)

apiVersion: v1
data:
run.sh: |
#!/bin/bash
unregister() {
kill %1
echo “Unregistering runner ${RUNNER_NAME} …”
/usr/bin/gitlab-ci-multi-runner unregister -t “$(/usr/bin/gitlab-ci-multi-runner list 2>&1 | tail -n1 | awk ‘{print $4}’ | cut -d’=’ -f2)” -n ${RUNNER_NAME}
exit $?
}
trap ‘unregister’ EXIT HUP INT QUIT PIPE TERM
echo “Registering runner ${RUNNER_NAME} …”
/usr/bin/gitlab-ci-multi-runner register -r ${GITLAB_CI_TOKEN}
sed -i ‘s/^concurrent.*/concurrent = ‘“${RUNNER_REQUEST_CONCURRENCY}”’/’ /home/gitlab-runner/.gitlab-runner/config.toml
echo “Starting runner ${RUNNER_NAME} …”
/usr/bin/gitlab-ci-multi-runner run -n ${RUNNER_NAME} &
wait
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-scripts
namespace: kube-ops
创建

[root@abcdocker gitlab]# kubectl apply -f runner-scripts-cm.yaml
configmap/gitlab-ci-runner-scripts created
我们需要创建一个 GITLAB_CI_TOKEN, 然后我们使用 gitlab ci runner token 来创建一个 Kubernetes secret 对象。需要提前对 token 进行 base64 转码

[root@abcdocker gitlab]# echo BMxb1ezMiTYFxtZTsVxP|base64 -w0
Qk14YjFlek1pVFlGeHRaVHNWeFAK
这里的 token 就是我们 gitlab runner 上截图的地方,base64 只有在 k8s 环境上需要

如下图
image_1dlmgfl3pqe1ghk1sdd18hgmcb8n.png-89.1kB

接下来使用上面的 token 创建一个 Sercret 对象 (gitlab-ci-token-secret.yaml)

[root@abcdocker gitlab]# cat gitlab-ci-token-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: gitlab-ci-token
namespace: kube-ops
labels:
app: gitlab-ci-runner
data:
GITLAB_CI_TOKEN: Qk14YjFlek1pVFlGeHRaVHNWeFAK
创建

[root@abcdocker gitlab]# kubectl apply -f gitlab-ci-token-secret.yaml
secret/gitlab-ci-token created
接下来创建真正运行 Runner 的控制器镜像,这里使用 Statefulset,在开始运行的时候,尝试取消注册所有的同名 Runner,当节点丢失时 (即 NodeLost 事件),这尤其有用,然后再尝试注册自己并开始运行。在正常停止 Pod 的时候,Runner 将会运行 unregister 命令来尝试取消自己,所以 gitlab 就不能再使用这个 Runner,这个则是通过 kubernetes Pod 生命周期中的 hooks 来完成的

runner-statefulset.yaml

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: gitlab-ci-runner
namespace: kube-ops
labels:
app: gitlab-ci-runner
spec:
updateStrategy:
type: RollingUpdate
replicas: 2
serviceName: gitlab-ci-runner
template:
metadata:
labels:
app: gitlab-ci-runner
spec:
volumes:
- name: gitlab-ci-runner-scripts
projected:
sources:
- configMap:
name: gitlab-ci-runner-scripts
items:
- key: run.sh
path: run.sh
mode: 0755
serviceAccountName: gitlab-ci
securityContext:
runAsNonRoot: true
runAsUser: 999
supplementalGroups: [999]
containers:
- image: gitlab/gitlab-runner:latest
name: gitlab-ci-runner
command:
- /scripts/run.sh
envFrom:
- configMapRef:
name: gitlab-ci-runner-cm
- secretRef:
name: gitlab-ci-token
env:
- name: RUNNER_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 9100
name: http-metrics
protocol: TCP
volumeMounts:
- name: gitlab-ci-runner-scripts
mountPath: “/scripts”
readOnly: true
restartPolicy: Always
上面我们命名了一个 gitlab-ci 的 serviceAccount,这里要新建一个 rbac 文件 (runner-rbac.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-ci
namespace: kube-ops

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: kube-ops
rules:
- apiGroups: [""]
resources: [“*”]
verbs: [“*”]

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: kube-ops
subjects:
- kind: ServiceAccount
name: gitlab-ci
namespace: kube-ops
roleRef:
kind: Role
name: gitlab-ci
apiGroup: rbac.authorization.k8s.io
创建完毕

[root@abcdocker gitlab]# kubectl apply -f runner-rbac.yaml
serviceaccount/gitlab-ci created
role.rbac.authorization.k8s.io/gitlab-ci created
rolebinding.rbac.authorization.k8s.io/gitlab-ci created
[root@abcdocker gitlab]# kubectl apply -f runner-statefulset.yaml
statefulset.apps/gitlab-ci-runner created
接下来我们检查我们创建的

[root@abcdocker gitlab]# kubectl get pod,svc,cm -n kube-ops
NAME READY STATUS RESTARTS AGE
pod/gitlab-57cf47cd7d-cfpmn 1/1 Running 1 47h
pod/gitlab-ci-runner-0 1/1 Running 0 2m51s
pod/gitlab-ci-runner-1 1/1 Running 0 119s
pod/postgresql-7c6bf8974d-r9djh 1/1 Running 0 47h
pod/redis-c45ffd79b-75vbd 1/1 Running 0 47h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/gitlab NodePort 10.254.212.182 80:30004/TCP,22:8530/TCP 47h
service/postgresql ClusterIP 10.254.255.216 5432/TCP 47h
service/redis ClusterIP 10.254.83.148 6379/TCP 47h
NAME DATA AGE
configmap/gitlab-ci-runner-cm 19 150m
configmap/gitlab-ci-runner-scripts 1 78m
#目前 pod gitlab-ci-runner0 和 1 都已经属于正常的运行状态
此时我们查看 gitlab 中的 Runners 就已经将这 2 个 pod 节点添加进来了

这里我们也可以更改 Runner 的一些配置,比如添加 tag 标签等

123.png-334.7kB

gitlab CICD 配置
首先我们需要有一个项目,接下来我们进行 gitlab 配置演示

#项目下载
wget http://down.i4t.com/gitlab-ci-k8s-demo.tar.gz #需要修改
需要各位自己进行配置 gitlab,新建一个项目并上传。详细步骤这里不再介绍

如果项目里面有勾选 Auto DevOps 记得取消,对我们的 cicd 会有影响
image_1dlu1qh731viml6v1vho1qem12iib9.png-252.1kB

项目介绍
本次环境使用的是 go 环境,只是为了演示。Java 和 php 的构建方式也差不多相同。初次演示可以通过我的模板进行演示

image_1dlu3cathjm3c681esr3051eu2cj.png-143.3kB

这里实际上是引用了类似于 Jenkins pipline 的脚本格式

gitlab-ci.yaml 介绍

image:
name: golang:1.10.3-stretch #这里的全局镜像就是当下面的 stage 里面没有定义镜像时,使用的就是这里全局的镜像地址。但是当我们指定镜像时,就不会使用全局变量
entrypoint: [“/bin/sh”, “-c”]

The problem is that to be able to use go get, one needs to put

the repository in the $GOPATH. So for example if your gitlab domain

is mydomainperso.com, and that your repository is repos/projectname, and

the default GOPATH being /go, then you’d need to have your

repository in /go/src/mydomainperso.com/repos/projectname

Thus, making a symbolic link corrects this.

before_script: #在我们的 go 环境中,默认的 go path 是在我们 go 的目录下面,所以我们要在 go 目录下面创建一个 src。相当于我们项目中的一个地址 。然后我们将项目地址 ln 到我们的 go path 地址中去
- mkdir -p “/go/src/git.qikqiak.com/${CI_PROJECT_NAMESPACE}”
- ln -sf “${CI_PROJECT_DIR}” “/go/src/git.i4t.com/${CI_PROJECT_PATH}”
- cd “/go/src/git.i4t.com/${CI_PROJECT_PATH}/”
stages: ## 这里 stages 代表一个项目,每个名称下面可以创建一个或多个任务,并且是同时执行的。前面是 stages 执行完毕后才会执行下一个
- test
- build
- release
- review
- deploy
test1:
stage: test #这里通过 stage 进行匹配任务标签,并且 test1 和 test2 是属于队形操作,并不是串行操作
script:
- make test
test2:
stage: test
script:
- sleep 3
- echo “We did it! Something else runs in parallel!”
compile: #这 build 阶段执行一个 compile 任务
stage: build #当我们上面 test 执行完毕后,就会开始执行 build 操作
script: #通过 script 执行一个具体的命令,这里是进行 make build 操作
# Add here all the dependencies, or use glide/govendor/…
# to get them automatically.
- make build #这里的 build 是在我们 build 我们 Makefile 里面的脚本,当然也可以修改其他成命令
artifacts: #这里的就是将我们 build 的一个值挂载一下,可以到下面的 job 进行获取,我们在浏览器上也可以获取到这个文件,可以直接下载。相当于将 app 这个标签的文件暴露出来
paths:
- app
image_build: #这里到了 release 阶段,做了一个 imageBuild 操作(这里指的就是镜像构建)这里使用 docker build 就需要 docker 命令
stage: release
image: docker:latest #这里的镜像就是使用 docker 的镜像,这个镜像是提供了一个 docker 命令,当我们 docker build 的时候需要将所有的文件提交到一个 docker daemon 里面去做的一个构建工作。所以这里还需要一个 service,来指定 docker 所依赖的服务。下面我们就引用了一个 docker in docker 的一个镜像。这个 service 实际上就是我们 docker 镜像所依赖的一个服务,如果我们还依赖其他镜像,直接在下面添加就可以了
variables: #这里的配置主要是指定 docker 的一个 host
DOCKER_DRIVER: overlay
DOCKER_HOST: tcp://localhost:2375
services: #这里就是代表我们 service 暴露的端口就是 2375,相当于将我们 docker 的客户端和 service 关联起来了。这样当我们执行 docker build 的命令就会调用我们的 docker daemon 来进行一个 build 的操作
- name: docker:17.03-dind
command: [“–insecure-registry=registry.i4t.com”] #这里可以写私有仓库,但是为了演示,我这里直接修改为公有仓库,如果这里不写仓库地址会出现 509 的错误
script: #这里就是执行一个真正的脚本,来进行 build 一个操作
- docker info
- docker login -u “${CI_REGISTRY_USER}” -p “${CI_REGISTRY_PASSWORD}” registry.i4t.com #这里的用户名密码不方便写死,所以这里写成变量。在 gitlab 项目中进行添加配置
- docker build -t “${CI_REGISTRY_IMAGE}:latest” .
- docker tag “${CI_REGISTRY_IMAGE}:latest” “${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}” #需要说明一点,这里的 ci_commit_ref_name 实际上就是 gitlab commit 版本号,在之前的 jenkins 中已经使用过很多次,具体不详细介绍 (需要请点击 i4t.com 找 jenkins 文档)
- test ! -z “${CI_COMMIT_TAG}” && docker push “${CI_REGISTRY_IMAGE}:latest”
- docker push “${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}”
deploy_review: #这里这一个预览环境,相当于开发环境。
image: registry.cn-beijing.aliyuncs.com/abcdocker/k8s:kubectl #这里的 image 是一个 kubectl 的命令,这个镜像实际上就是内置了一个二进制的 kubectl 命令,但是我们知道如果我们想使用 kubectl 命令是需要提供 apiserver 的一些相关配置,才可以连接到我们的 kubernetes 集群
stage: review
only:
- branches #只有提交到我们的 gitlab 指定的分支上,才会执行我们的这个 job 任务。如果提交的是 tag 上来,是不会触发我们的任务的
except:
- tags
environment: #这里我们定义了一个环境,并且在里面定义了一个 url。这样当我们触发了 review job,我们可以通过下面的域名进行访问
name: dev
url: https://dev-gitlab-k8s-demo.i4t.com #这里的如果安装了 Traefik 可以直接绑定 host 或者通过 dns 进行配置,也可以是 svc
on_stop: stop_review #这里我们添加了一个回调,当我们停止我们 dev 环境时去做的一件事情(这里的 stop_review 在下面进行查看)
script: #这里的命令就是替换经常镜像版本号的一些操作,deployment 等相关文件都需要提交的 gitlab 上
- kubectl version
- cd manifests/
- sed -i “s/CI_ENVIRONMENT_SLUG/${CI_ENVIRONMENT_SLUG}/” deployment.yaml ingress.yaml service.yaml
- sed -i “s/VERSION/${CI_COMMIT_REF_NAME}/” deployment.yaml ingress.yaml service.yaml
- |
if kubectl apply -f deployment.yaml | grep -q unchanged; then
echo “=> Patching deployment to force image update.”
kubectl patch -f deployment.yaml -p “{"spec":{"template":{"metadata":{"annotations":{"ci-last-updated":"$(date +‘%s’)"}}}}}”
else
echo “=> Deployment apply has changed the object, no need to force image update.”
fi
- kubectl apply -f service.yaml || true
- kubectl apply -f ingress.yaml
- kubectl rollout status -f deployment.yaml
- kubectl get all,ing -l ref=${CI_ENVIRONMENT_SLUG}
stop_review: #这里就是上面 review 中引用的 stop_review。停止前做的一件事情
image: registry.cn-beijing.aliyuncs.com/abcdocker/k8s:kubectl
stage: review
variables:
GIT_STRATEGY: none
when: manual
only: #这里除了我们的 master 分支和 tag,其他的分支都可以进行一个 branches job 任务
- branches
except:
- master
- tags
environment:
name: dev #这里的环境也是 dev
action: stop #执行的动作,这里代表执行 stop 动作
script: #这里执行的命令就是将之前构建的项目中的镜像及相关配置都进行删除的一个操作
- kubectl version
- kubectl delete ing -l ref=${CI_ENVIRONMENT_SLUG} #这里通过 ref 标签进行匹配删除(这里中的环境配置都是在我们 manifests 目录中引用)
- kubectl delete all -l ref=${CI_ENVIRONMENT_SLUG}
deploy_live: #这里是真正的 deploy 阶段,真正去部署(这里是定义了一个 deploy_live 的任务)
image: registry.cn-beijing.aliyuncs.com/abcdocker/k8s:kubectl #这里还是使用的 kubectl 命令
stage: deploy
environment: #这里定义了一个标签为 live 的一个线上环境
name: live
url: https://live-gitlab-k8s-demo.i4t.com #这里就是部署完成之后线上的一个地址
only:
- tags #只有我们提交一个 tag 标签的时候,才会执行 job 任务
when: manual #这里的参数代表手动的进行执行
script: #下面的命令操作和前面的 dev 环境中配置的命令是一样的,实际上就是一个替换镜像的一个操作
- kubectl version
- cd manifests/
- sed -i “s/CI_ENVIRONMENT_SLUG/${CI_ENVIRONMENT_SLUG}/” deployment.yaml ingress.yaml service.yaml
- sed -i “s/VERSION/${CI_COMMIT_REF_NAME}/” deployment.yaml ingress.yaml service.yaml
- kubectl apply -f deployment.yaml
- kubectl apply -f service.yaml
- kubectl apply -f ingress.yaml
- kubectl rollout status -f deployment.yaml
- kubectl get all,ing -l ref=${CI_ENVIRONMENT_SLUG}
现在我们要配置一下 kubectl,默认情况下 kubectl 需要使用证书才可以连接到 k8s 集群。在服务器上默认使用的是 /root/.kube/config,gitlab 也有添加证书的位置,我们进行添加配置

1111.png-244.4kB

点击添加集群

image_1dmsvht9cnhv1rsc15k31g1g93l1u.png-124.1kB

这里简单说一下

Kubernetes cluster name 集群名称可以随便写
API URL 这里实际上就是 apiserver 地址,通过 kubectl cluster-info 查看到
CA TOken 可以通过 /root/.kube/config 里面获取到,也可以使用下面的方式获取到
获取证书 token 等相关配置

#除了使用 /root/.kube/config 获取还可以通过 获取
#这里先创建一个命名空间
kubectl create ns gitlab
#因为在操作的时候会涉及 rbac 权限的问题,这里要创建一个 rbac 的文件。我们将集群的 cluster 的权限绑定到 ServerAccount
[root@yzsjhl82-135 ~]# cat gitlabdemo-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab
namespace: gitlab

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: gitlab
subjects:
- kind: ServiceAccount
name: gitlab
namespace: gitlab
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
cluster-admin 是内置的一个角色,拥有集群所有权限的一个角色

然后我们进行创建

[root@abcdocker ~]# kubectl create ns gitlab
[root@abcdocker ~]# kubectl apply -f gitlabdemo-sa.yaml
serviceaccount/gitlab unchanged
clusterrolebinding.rbac.authorization.k8s.io/gitlab created
[root@abcdocker ~]# kubectl get sa -n gitlab
NAME SECRETS AGE
default 1 74m
gitlab 1 3m10s
我们创建的 serviceaccount 实际上就是一个 secret,接下来我们进行获取 token 和 ca

[root@abcdocker ~]# kubectl get secret -n gitlab
NAME TYPE DATA AGE
default-token-pmlvw kubernetes.io/service-account-token 3 112m
gitlab-token-5cgx2 kubernetes.io/service-account-token 3 40m
[root@abcdocker ~]# kubectl get secrets gitlab-token-5cgx2 -n gitlab -o yaml
apiVersion: v1
data:
ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR4akNDQXE2Z0F3SUJBZ0lVR29Balg2cGdQVUJkL1ZEU2RrbzlvOFR5ckQwd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2FERUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFVcHBibWN4RURBT0JnTlZCQWNUQjBKbAphVXBwYm1jeEREQUtCZ05WQkFvVEEyczRjekVTTUJBR0ExVUVDeE1KTkZCaGNtRmthV2R0TVJNd0VRWURWUVFECkV3cHJkV0psY201bGRHVnpNQ0FYRFRFNU1EZ3lOakV6TURNd01Gb1lEekl4TVRrd09EQXlNVE13TXpBd1dqQm8KTVFzd0NRWURWUVFHRXdKRFRqRVFNQTRHQTFVRUNCTUhRbVZwU21sdVp6RVFNQTRHQTFVRUJ4TUhRbVZwU21sdQpaekVNTUFvR0ExVUVDaE1EYXpoek1SSXdFQVlEVlFRTEV3azBVR0Z5WVdScFoyMHhFekFSQmdOVkJBTVRDbXQxClltVnlibVYwWlhNd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURLNm02UXd2dUwKYWw1S1h2aVFlbDJXL0pFTm5OSVVsalNmUHhraVE2NUloUStLaEVaeEJ3S0hZczNzMFdYMDFVQUtVeDNVVmMxSgo2V3pQL3MvbGlCaC8xVmxwOXpNRFR1OEhjbWdKVW01VnBmTTFhQ1d4eXk2bHZXVWl4V1FadVNCSjFZT2NJN3R4ClZYbktxR3FnS2RuTEJyamJGSWNtdnJFV01RMmRUcjBOWUZuaVd5UkRmQXlpdW11NEtnZEVZSmlIYXFXNm1xYVMKK1d3czFaWEZzRHl6SzNvQWxmTTZ4ZEhXMk9OQ0JadFovaTFtMTBsV3FTS0hsZlBRU1VOcDRMaURHWElaTFpKeApOQ1Z5T2lINFI4T1RaM3dhME9yOVhEeVEzMmIrNkhYa01FMTAvaFc5bnRlanRCZXExVzNjMTVOeVRzemJYYWQzCjJyZ3ZvZ1JtcFNwUkFnTUJBQUdqWmpCa01BNEdBMVVkRHdFQi93UUVBd0lCQmpBU0JnTlZIUk1CQWY4RUNEQUcKQVFIL0FnRUNNQjBHQTFVZERnUVdCQlErQ3RaTWZrbGV1MmtCY0hFc2RzcVMxRVJ5YkRBZkJnTlZIU01FR0RBVwpnQlErQ3RaTWZrbGV1MmtCY0hFc2RzcVMxRVJ5YkRBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQUF4Wlo5R0NOCmdJMzVWaDNvdE9IUkw4eXJSSEw5b05qbzdVQnZEU2JBZkp2dDd1WGx3WmF0bXBZTUN1MEpVUnJnMWM0Rng3cWUKUHdOQVNnQUFuVHZKZmdEYlZRWDlTM25CcytJYzdTUE5wdXVacTloZzhmSHkyaHRYZ0RiL3lOMkdpNlBBK29zTwpWdWRTYXI5S0tMcVJzNnBuU2tPOEJkbmtPR2llUks5WnJIOVlnS3VodFVhbmZGSEhJeTE0ZXJqQ0s3YUV0S2FOCmxJQVNhV2crYkNnK29JdklqcjdyRzZoU1VFZWkwdVJGbHg5OXN0OVBSeUVaVTZ2MVpETEt6bjY0MUp1elREeDkKTVhLdUtKQUF1anUyc1c0VjZ2VEowR1pGUGNobkxrZSs1NC9QSnVsLytzVExRS1puVE9EMm9KdW1VMEFiVnFMRwpZa1VLUWJ2dUhzMW9sUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
namespace: Z2l0bGFi
token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSm5hWFJzWVdJaUxDSnJkV0psY201bGRHVnpMbWx2TDNObGNuWnBZMlZoWTJOdmRXNTBMM05sWTNKbGRDNXVZVzFsSWpvaVoybDBiR0ZpTFhSdmEyVnVMVFZqWjNneUlpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WlhKMmFXTmxMV0ZqWTI5MWJuUXVibUZ0WlNJNkltZHBkR3hoWWlJc0ltdDFZbVZ5Ym1WMFpYTXVhVzh2YzJWeWRtbGpaV0ZqWTI5MWJuUXZjMlZ5ZG1salpTMWhZMk52ZFc1MExuVnBaQ0k2SWpFNFptWXlabVpqTFdWak1UVXRNVEZsT1MxaE5UZGxMVFV5TlRRd01HUTNZakk0TkNJc0luTjFZaUk2SW5ONWMzUmxiVHB6WlhKMmFXTmxZV05qYjNWdWREcG5hWFJzWVdJNloybDBiR0ZpSW4wLmxacC02bGdnXzUzcnJiYjI1Y0xQWnlXSlpLQ0JQYUJSc0IybDAzMmNHSEFzSWpLdmJOemVpQnJ4M2U4SGNoVjVxNGJ5NDljem53dmkyeXAzZDJ0TGpZOTZTTUt3UDRQQUJjcFozci1YM2NMUFEzTGZrUkhXZlJyQktNY3BYbm1mRmRnZUJ4LXFTRS00ZFo3LTF3ZzNwTDV1ZkZWaEo1OTlZbHBqXzU4OFpxc0c2YTk4WjR1NDVyeTlpSHd2QV9KRDVDcGRvU1FObDk1ZGw2dDlra0c0NHZET2N1cG5WQkIxS1ByeXJsY3FyMlozVjJ4MEVHQWRxSjdUU0JwZWxSaTIyVmFHcGRyMmdhUjV4dTU4cEVKUjdDYl9BUnBHRVZaZ0JvZF9SWmtWU0FtTkdGVVV2YWZSdHBNZ3RLVTM4RkMzZ1drVVBleVZMY2o4Yk9DQTdscndnUQ==
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: gitlab
kubernetes.io/service-account.uid: 18ff2ffc-ec15-11e9-a57e-525400d7b284
creationTimestamp: “2019-10-11T10:51:36Z”
name: gitlab-token-5cgx2
namespace: gitlab
resourceVersion: “6743518”
selfLink: /api/v1/namespaces/gitlab/secrets/gitlab-token-5cgx2
uid: 18f0217c-ec15-11e9-8945-525400a79155
type: kubernetes.io/service-account-token
在上面我们已经看到了 ca.crt 和 token,但是我们是不可以使用。还需要使用 base64 进行转码

echo “xxxx”|base64 -d
#需要复制全部,包括–BEGIN
#生成 token,方法相同
image_1dmta8fckp9d1r711c7gblp1mhb2b.png-854.5kB

然后我们点击 ADD
image_1dmtaehpb1frs19111qa12nkrmp2o.png-119.7kB

如果我们点击添加提示下方报错,就需要添加一个允许钩子的一个操作

错误图

image_1dmtbejc51aj9sfd1s5n1nj61v3q35.png-187.1kB

解决方法
Admin area => Settings => Network ,然后点击 Outbound requests 右边 的“expand”按钮
image_1dmtbgobn4351o81fqub2a16t93i.png-110.9kB

添加完成如下图 (现在我们是已经可以访问到我们的集群)
image_1dmtbi4ar12lvelfdqt13k91h0o3v.png-218.6kB

在.gitlab-ci.yaml 文件里面,我们引用了很多环境变量 (比如 docker 账号密码文件等) 这种文件不可以明文显示,所以在配置文件使用了变量,现在就需要去 gitlab 上配置一下这个变量

image_1dmtd5bi5eebnnm8cq19ibhf64c.png-151.7kB

image_1dmtd5p7ejvh19ui1p57niq1b014p.png-147.1kB

在下面添加镜像地址,容器仓库的账号密码就可以
CI_REGISTRY_USER
CI_REGISTRY_PASSWORD
CI_REGISTRY_IMAGE

gitlab ci 变量文档https://docs.gitlab.com/ee/ci/variables/README.html#predefined-environment-variables

image_1dmtdcjo3qol1jv1f4h1pbuppr56.png-85.8kB

在前面我们已经提交了 git 代码,如果没有提交测试可以解压下面的文件,在提交一次

wget http://down.i4t.com/abcdocker-gitlab-demo.tar.gz
代码文件, 里面包含 gitlab ci 脚本及本次演示环境
我们这里提交一个代码进去,进行演示

1111111111111.png-266kB

在我们的 kube-ops 命名空间下,gitlab 会生成 runner 镜像,里面包含我们 ci 脚本定义的镜像以及 runner 镜像
image_1dn7piuas1aku1io6lfhken3061e.png-98.2kB

接下来我们可以在 gitlab 查看 ci 构建步骤

构建请注意, 如果没有.gitlab-ci.yaml 文件,gitlab 不会触发 ci 脚本

image_1dn7plbis1kdedhqokr5fj1usa2b.png-173.3kB

点击下方可以看到详细的构建步骤

image_1dn7pn3j7njgmn21som1ln1ovs48.png-122.9kB

可以查看到详细的构建步骤,和 jenkins pipline 作用相同

3333.png-182.4kB

在 Build 阶段我们添加了一个变量,使我们 build 完成后可以将打包的文件下载下来
image_1dn7qsdh01op91ofl16hhat6ju078.png-359.4kB

构建完成后就会出现下方,并且还可以重复构建
image_1dn7qtjf0epj18i81gd73371cs87l.png-79.4kB

接下来我们可以查看一下 pod 和 svc 等相关状态
image_1dn7qvqo9giqmsp12jcog81evi82.png-138.9kB

可以使用 ingress 访问,或者通过 nodeport 方式访问。这里我使用 nodeport 方式进行访问演示
image_1dn7r1qct2q91r0i19a31n1ags38f.png-52.1kB

我们可以根据自己的需求进行修改