实践 GitLab CI/CD
前置条件
在部署 GitLab Runner 时,注意正确配置 rbac 和 serviceAccountName。
什么是 CI/CD
CI(持续集成)是指持续地集成代码到主干。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。
CD (持续交付) 是指持续地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就可以部署到生产环境。
在云原生环境下,交付物是 Docker 镜像。
CI/CD Pipeline 工具有很多,由于我们的代码是放在 GitLab 上的,GitLab 自带一个轻量级的 CI 工具,已经可以满足大部分需要,因此作者使用 GitLab CI 工具。
我们需要一份源代码来实践 CI/CD。
gohttpserver 是一个简单的用 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 镜像
Kaniko 是一种在容器或 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 用户名。
准备就绪后,将代码推送到 GitLab 仓库,并打上 TAG,就会触发 CI/CD 流水线,构建并上传 Docker 镜像。
使用新镜像部署应用
构建好 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 名字空间下。
使用 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: ["*"]
这可能存在安全隐患,待作者找到更好的办法,回来更新此文。