基于Kubernetes Gitlab CICD

在大多数情况,构建项目都会占用大量的系统资源,如果让gitlab本身来运行构建任务的话,显然Gitlab的性能会大幅度下降。gitlab ci最大的作用就是管理各个项目的构建状态,因此,运行构建任务这种浪费资源的事情交给一个独立的gitlab runner来做就会好很多,更重要的是gitlab runner可以安装到不同的机器上,甚至是我们本机,这样完全就不会影响Gitlab本身了。从gitlab8.0开始,gitlab CI就已经集成在Gitlab中,我们只需要在项目中添加一个.gitlab-ci.yaml文件,然后运行一个Runner,即可进行持续集成。
基于Kubernetes Gitlab CICD
Kubernetes
在大多数情况,构建项目都会占用大量的系统资源,如果让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
基于Kubernetes Gitlab CICD-每日运维
本文项目演示图
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配置文件中!
基于Kubernetes Gitlab CICD-每日运维
接下来创建个项目要进行演示
基于Kubernetes Gitlab CICD-每日运维
基于Kubernetes Gitlab CICD-每日运维
接下来添加提交代码
如何提交代码我这里就不详细介绍了,项目的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/
基于Kubernetes Gitlab CICD-每日运维
首先我们需要检查一下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信息
基于Kubernetes Gitlab CICD-每日运维
接下来进行配置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环境上需要
    如下图
    基于Kubernetes Gitlab CICD-每日运维
    接下来使用上面的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会有影响
    基于Kubernetes Gitlab CICD-每日运维
    项目介绍
    本次环境使用的是go环境,只是为了演示。Java和php的构建方式也差不多相同。初次演示可以通过我的模板进行演示
    基于Kubernetes Gitlab CICD-每日运维
    这里实际上是引用了类似于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.i4t.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 也有添加证书的位置,我们进行添加配置
    基于Kubernetes Gitlab CICD-每日运维
    点击添加集群
    基于Kubernetes Gitlab CICD-每日运维
    这里简单说一下

    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,方法相同
    

    基于Kubernetes Gitlab CICD-每日运维
    然后我们点击ADD
    基于Kubernetes Gitlab CICD-每日运维
    如果我们点击添加提示下方报错,就需要添加一个允许钩子的一个操作
    错误图
    基于Kubernetes Gitlab CICD-每日运维
    解决方法
    Admin area => Settings => Network ,然后点击 Outbound requests 右边 的“expand”按钮
    基于Kubernetes Gitlab CICD-每日运维
    添加完成如下图 (现在我们是已经可以访问到我们的集群)
    基于Kubernetes Gitlab CICD-每日运维
    在.gitlab-ci.yaml文件里面,我们引用了很多环境变量 (比如docker账号密码文件等)这种文件不可以明文显示,所以在配置文件使用了变量,现在就需要去gitlab上配置一下这个变量
    基于Kubernetes Gitlab CICD-每日运维
    基于Kubernetes Gitlab CICD-每日运维
    在下面添加镜像地址,容器仓库的账号密码就可以
    CI_REGISTRY_USER
    CI_REGISTRY_PASSWORD
    CI_REGISTRY_IMAGE
    gitlab ci变量文档https://docs.gitlab.com/ee/ci/variables/README.html#predefined-environment-variables
    基于Kubernetes Gitlab CICD-每日运维
    在前面我们已经提交了git代码,如果没有提交测试可以解压下面的文件,在提交一次

    wget http://down.i4t.com/abcdocker-gitlab-demo.tar.gz
    代码文件,里面包含gitlab ci脚本及本次演示环境
    

    我们这里提交一个代码进去,进行演示
    基于Kubernetes Gitlab CICD-每日运维
    在我们的kube-ops命名空间下,gitlab会生成runner镜像,里面包含我们ci脚本定义的镜像以及runner镜像
    基于Kubernetes Gitlab CICD-每日运维
    接下来我们可以在gitlab查看ci构建步骤
    构建请注意,如果没有.gitlab-ci.yaml文件,gitlab不会触发ci脚本
    基于Kubernetes Gitlab CICD-每日运维
    点击下方可以看到详细的构建步骤
    基于Kubernetes Gitlab CICD-每日运维
    可以查看到详细的构建步骤,和jenkins pipline作用相同
    基于Kubernetes Gitlab CICD-每日运维
    在Build阶段我们添加了一个变量,使我们build完成后可以将打包的文件下载下来
    基于Kubernetes Gitlab CICD-每日运维
    构建完成后就会出现下方,并且还可以重复构建
    基于Kubernetes Gitlab CICD-每日运维
    接下来我们可以查看一下pod和svc等相关状态
    基于Kubernetes Gitlab CICD-每日运维
    可以使用ingress访问,或者通过nodeport方式访问。这里我使用nodeport方式进行访问演示
    基于Kubernetes Gitlab CICD-每日运维
    我们可以根据自己的需求进行修改
    小结
    1.这里pod的版本号我使用的是commit版本号,可以确保唯一性
    2.这里提交代码只有的master上会进行处罚
    3.这里我的镜像仓库使用的是阿里云
    文档如果有问题请在下方评论,我会及时进行修改并进行答疑。 着急可以点击右下方加群,群里@管理员
    2019年10月15日21:54:47

    相关文章:

    1. Kubernetes 1.14 二进制集群安装
    2. Kuerbernetes 1.11 集群二进制安装
    3. Kubenetes 1.13.5 集群二进制安装
    4. Kubernetes Traefik Ingress