Gitlab CI 與 DevOps
來自專欄 扇貝技術架構小組
上一篇講到了扇貝的微服務實踐,尤其是關於「人」的部分。
本文將就「技術」方案部分做一個簡單的分享。
區分不同的環境
在扇貝,我們維護了 「集成測試環境」 和 「生產環境」 兩個 kubernetes
集群。
- 集成測試環境負責:單元測試,構建鏡像,集成測試部署
- 生產環境負責:預發布,正式發布
CI/CD 的搭建
我們的 CI/CD 是基於 GitLab Pipeline 搭建的。
架構組負責搭建和維護 runner(Pipeline 的執行環境),DevOps 小組負責 Pipeline 腳本的編寫。
Gitlab Pipeline
Gitlab pipeline
指 一組按照 stage
執行的job
(每個 stage
包含若干個 job
),當一個 stage
的 job
都成功執行後,開始執行下一個 stage
的 job
。pipeline
定義在項目的 .gitlab-ci.yml
里。關於 .gitlab-ci.yml
的詳細參考文檔,請見:Configuration of your jobs with .gitlab-ci.yml
。
.gitlab-ci.yml
的編寫和維護
在扇貝,每個 DevOps 小組負責編寫和維護自己負責項目的 .gitlab-ci.yml
,定義自己的 pipeline
。當然,架構組會提供一個 .gitlab-ci.yml
模版。這個模版 包含 test
, build-image
, deploy-integration
, deploy-staging
, deploy-production
5個 stage
。基於這樣的 pipeline
可以實現這樣的 CI/CD
工作流:
組員新建分支,開發功能,創建一個 Merge Request
,這時候觸發第一個 stage
: test
。test
中包含所有單元測試的 job
。當 test
通過後,組長 Review Merge Request
,這其中可能還會提一些修改意見,組員進行對應的修改,再次觸發 test
。當且僅當組長 Review 通過後,執行 Merge
,這時候開始觸發第二個 stage
: build-image
(也就是構建 Docker Image)。構建成功後進入到 deploy-integration
。集成測試沒有問題,再依次deploy-staging
-> 預發布驗證 -> deploy-production
。至此整個 CI/CD
工作流就完成了。
一個大概的 .gitlab-ci.yml
模版如下:
stages: - test - build - deploy_integration - deploy_staging - deploy_productionvariables: MYSQL_DATABASE: test MYSQL_ALLOW_EMPTY_PASSWORD: yes SEA_ENV: testing DOCKER_HOST: tcp://dockerd:2375 IMAGE: registry.mydocker.com/devops/${CI_PROJECT_NAMESPACE}-${CI_PROJECT_NAME}before_script: - IMAGE_TAG=${IMAGE}:${CI_COMMIT_SHA:0:8}#========================================= Unit Testing ================================================test_all: image: python:3.7 stage: test services: - name: mysql:5.6 alias: mysql - name: redis:4 alias: redis before_script: - pip install -U -r requirements.txt script: - flake8 app jobs - sea test#========================================== Build Image =================================================build_image: stage: build only: - master tags: - build script: - docker build -t ${IMAGE_TAG} -f Dockerfile . - docker push ${IMAGE_TAG}deploy_rpc_integration: stage: deploy_integration only: - master tags: - deploy-integration script: - kubectl -n xyz set image deploy/examples-rpc "app=${IMAGE_TAG}" --recorddeploy_staging: stage: deploy_staging only: - master tags: - deploy-production when: manual script: - kubectl -n xyz-staging set image deploy/examples-celery "app=${IMAGE_TAG}" --record - kubectl -n xyz-staging set image deploy/examples-rpc "app=${IMAGE_TAG}" --recorddeploy_production: stage: deploy_production only: - master tags: - deploy-production when: manual script: - kubectl -n xyz set image deploy/examples-celery "app=${IMAGE_TAG}" --record - kubectl -n xyz set image deploy/examples-rpc "app=${IMAGE_TAG}" --record
下圖是一個執行的例子,圖中可以看到 stage 執行到哪一步,結果分別是什麼。
GitLab Runner
除了各個小組能夠維護自己的 .gitlab-ci.yml
,接下來就要架構組構建能夠執行這些 pipeline
的 runner 了。
Gitlab 提供了 GitLab Runner 來管理 runner
。 GitLab Runner
負責註冊,運行和反註冊 runner
。
我們可以利用 k8s
來很方便地運行 GitLab Runner
,並且選擇 k8s
作為executor
來運行 job
。一個示例配置如下:
apiVersion: v1metadata: labels: app: gitlab-builder name: gitlab-builder-cm namespace: cicddata: REGISTER_NON_INTERACTIVE: "true" REGISTER_LOCKED: "false" CI_SERVER_URL: "https://gitlab.com/ci" RUNNER_CONCURRENT_BUILDS: "4" RUNNER_REQUEST_CONCURRENCY: "4" RUNNER_TAG_LIST: "build" RUNNER_EXECUTOR: "kubernetes" KUBERNETES_NAMESPACE: "cicd" KUBERNETES_IMAGE: "docker:17.11" KUBERNETES_SERVICE_ACCOUNT: "builder"kind: ConfigMap---apiVersion: apps/v1beta2kind: Deploymentmetadata: name: builder namespace: cicd labels: app: builderspec: replicas: 1 selector: matchLabels: app: builder template: metadata: labels: app: builder spec: containers: - name: ci-builder image: gitlab/gitlab-runner:v10.6.0 command: - /usr/bin/gitlab-ci-multi-runner - run imagePullPolicy: IfNotPresent envFrom: - configMapRef: name: gitlab-builder-cm volumeMounts: - mountPath: /etc/gitlab-runner/ name: config-volume lifecycle: preStop: exec: command: - /bin/bash - -c - "/usr/bin/gitlab-ci-multi-runner unregister -t xxxxxx -n builder" initContainers: - name: register-runner image: gitlab/gitlab-runner:v10.6.0 command: ["sh", "-c", "/usr/bin/gitlab-ci-multi-runner unregister -t xxxxxx -n builder; /usr/bin/gitlab-ci-multi-runner register -r xxxxxx;"] volumeMounts: - mountPath: /etc/gitlab-runner/ name: config-volume envFrom: - configMapRef: name: gitlab-builder-cm volumes: - name: config-volume emptyDir: {} restartPolicy: Always---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: namespace: cicd name: builderrules: - apiGroups: [""] resources: ["pods", "pods/exec"] verbs: ["*"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: namespace: cicd name: buildersubjects:- kind: ServiceAccount name: builder namespace: cicdroleRef: kind: Role name: builder apiGroup: rbac.authorization.k8s.io
這樣我們就可以得到一個能夠build docker image 的runner。在 .gitlab-ci.yml
中指定 tag 為 build
就可以使用。
最小化運維
我們堅持「最小化運維」的理念,除了日常的 DevOps,我們儘可能地利用 git + pipeline 的方式完成日常工作。我們堅信這樣的工作方式能夠最大化降低手動運維帶來的風險和不確定性。
例如我們 k8s
的證書籤發就是基於 git + pipeline 來做的。大家知道,要能夠使用 kubectl,每個人得有經過 k8s 的簽發的 crt 才可以通過 k8s
的認證。我們簽發的流程就是:
- 有一個存放大家csr的 git repo
- 新人生成自己的csr,添加到 git repo,提交 merge request
- ci 開始 validate csr合法性(例如name的格式,是否包含什麼信息,不包含什麼信息等等)
- 集群管理員 validate csr name 和申請人是否相符,如果相符,則合併該 merge request
- ci 開始簽發 integration, production 兩個集群的 crt
在扇貝,幾乎所有的日常運維工作都是基於 CI/CD 完成的
推薦閱讀:
※基礎架構的持續集成和應用部署
※運維團隊能力建設的另一思路
※微服務介紹與實踐總結
※在Raspberry Pi 3 + 64位Debian上編譯Docker?