实践 GitLab CI/CD

前置条件

在部署 GitLab Runner 时,注意正确配置 rbac 和 serviceAccountName。

什么是 CI/CD

CI(持续集成)是指持续地集成代码到主干。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。

CD (持续交付) 是指持续地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就可以部署到生产环境。

在云原生环境下,交付物是 Docker 镜像。

README-2022-07-02-12-13-54

CI/CD Pipeline 工具有很多,由于我们的代码是放在 GitLab 上的,GitLab 自带一个轻量级的 CI 工具,已经可以满足大部分需要,因此作者使用 GitLab CI 工具。

我们需要一份源代码来实践 CI/CD。

gohttpserveropen in new window 是一个简单的用 Go 编写的服务。首先将该项目 fork 到自建的 GitLab 仓库。

读者只需要关注 .gitlab-ci.yml 文件如何编写,以及 k8s 清单文件如何编写即可。

我们先来看下完整的 .gitlab-ci.yml 文件。

before_script:
  - export

stages:
  - build
  - deploy

build:
  stage: build
  tags:
    - k8s
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - >-
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --destination "$CI_REGISTRY_IMAGE:${CI_COMMIT_TAG}"
      --destination "$CI_REGISTRY_IMAGE:latest"
  rules:
    - if: $CI_COMMIT_TAG

deploy:
  stage: deploy
  tags:
    - k8s
  image: ongres/kubectl:latest
  dependencies:
    - build
  script:
    - sed -i "s/latest/$CI_COMMIT_TAG/g" deploy/deploy.yaml
    - kubectl apply -f deploy/
  rules:
    - if: $CI_COMMIT_TAG

使用 Kaniko 构建 Docker 镜像

Kanikoopen in new window 是一种在容器或 Kubernetes 集群内使用 Dockerfile 构建容器镜像的工具。它解决了 Docker-in-Docker 构建方法的两个问题:

  • Docker-in-Docker 需要特权模式才能运行,这是一个重要的安全问题。

  • Docker-in-Docker 通常会导致性能下降,而且速度可能非常慢。

我们需要使用容器镜像仓库的身份验证信息来创建 Docker config.json 文件,格式如下:

{
  "auths": {
    "$CI_REGISTRY": {
      "username": "$CI_REGISTRY_USER",
      "password": "$CI_REGISTRY_PASSWORD"
    }
  }
}

其中,$CI_REGISTRY 是镜像仓库的地址,$CI_REGISTRY_USER 是用户名,$CI_REGISTRY_PASSWORD 是密码。

在本文实践中,作者会把构建好的 Docker 镜像推送到 Docker Hub,所以 $CI_REGISTRY 的值是 https://index.docker.io/v1/。值得注意的是 $CI_REGISTRY_USER 是用户名而不是邮箱地址。

前往 GitLab,为 gohttpserver 添加如下 CI/CD 环境变量。其中 listenzz 是作者的 DockerHub 用户名。

README-2022-07-02-13-08-59

准备就绪后,将代码推送到 GitLab 仓库,并打上 TAG,就会触发 CI/CD 流水线,构建并上传 Docker 镜像。

README-2022-07-02-13-16-58

使用新镜像部署应用

构建好 Docker 镜像后,就可以使用新镜像重新部署应用了。部署方式有多种,这里演示的是使用 kubectl 命令来部署应用。

和一般教程不同的是,我们的 kubectl 镜像使用的不是 bitnami/kubectl,而是 ongres/kubectl,这是因为作者的 k8s 集群是部署在一台 ARM64 架构的 Mac 电脑上。作者到 Docker Hub 上找了个替代品。

这部分的脚本很简单,首先更改 docker 镜像的 tag,然后使用 kubectl 命令来部署应用。

script:
  - sed -i "s/latest/$CI_COMMIT_TAG/g" deploy/deploy.yaml
  - kubectl apply -f deploy/

值得注意的是,我们需要在清单文件中,指定 namespace 的值为 default,尽管 namespace 的值默认就是 default。

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: gohttpserver



 

这是因为我们的 gitlab-runner 是跑在 gitlab 名字空间的,默认会将应用部署到 gitlab 名字空间下。

README-2022-07-02-13-55-28

使用 kubectl 命令来部署应用,有一个缺点。需要给 GitLab Runner 授权访问 kubernetes API,如下所示:

apiVersion: rbac.authorization.k8s.io/v1
kind: "ClusterRole"
metadata:
  name: gitlab-runner
  labels:
    app: gitlab-runner
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["*"]
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["*"]

这可能存在安全隐患,待作者找到更好的办法,回来更新此文。

上次更新: