GitLab CICD與Kubernetes實踐·部署Flask Web服務
服務背景
通過Gitlab CI完成Flask web Service服務代碼風格檢查、單元測試、打包、發布到k8s環境里面,同時我們會在.gitlab-ci.yml文件中配置基于分支branch和tag的匹配執行相應的操作任務。Flask web Service是一個帶有web登錄界面的測試代碼服務,服務運行的端口為5000,下面是該服務構建Docker鏡像的Dockerfile
- FROM python:3.4
- COPY . /skeleton
- WORKDIR /skeleton
- RUN pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com # 配置pip源,加速下載
- EXPOSE 5000
- ENTRYPOINT ["sh", "scripts/dev.sh"]
定義.gitlab-ci.yml
然后為項目準備.gitlab-ci.yml文件,這個文件稍微有點長,可以通過👉遠程調用模板庫的方式優化配置,此處我們不在多說:
- stages: # 此處分為五個階段,按順序執行對應的環節
- - style
- - test
- - release
- - review
- - deploy
- pep8: # pep8是自定義命名的jobs
- image: python:2.7 # 指定下面script塊的指令在哪個鏡像運行的容器環境內運行
- stage: style # 聲明該pep8的job是屬于哪個stage階段運行
- script: # 該階段執行的操作,其實就像在terminal里面執行命令一樣。
- - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
- - tox -e pep8 # 使用tox命令進行pep8代碼格式檢查規范性檢查,配置文件為當前項目下的tox.ini
- unittest-py2.7:
- image: python:2.7
- stage: test
- script:
- - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
- - tox -e py27 # 指定使用py27虛擬環境
- unittest-py3.4:
- image: python:3.4
- stage: test
- script:
- - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
- - tox -e py34 # 指定使用py34虛擬環境
- buildimage:
- image: docker:latest # 該環節需要構建鏡像,需要docker二進制命令,所以指定一個docker鏡像
- variables: # 給buildimage這個job傳遞的變量
- DOCKER_DRIVER: overlay
- DOCKER_HOST: tcp://localhost:2375 # 與service指定容器通信
- services:
- - name: docker:17.03-dind
- command:
- - "--registry-mirror=https://*****.mirror.aliyuncs.com" # 配置鏡像加速,當登錄私有鏡像倉庫的時候,如果倉庫的證書不受信任,可以在下方添加`--insecure-registry=*****`選項
- stage: release
- script:
- - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" ${CI_REGISTRY_REPO_URL} # 登錄私有或者共有鏡像倉庫
- - docker build -t "${CI_REGISTRY_IMAGE}:latest" -f ./Dockerfile . # 構建鏡像
- - docker tag "${CI_REGISTRY_IMAGE}:latest" "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" # 給鏡像打個推送到鏡像倉庫的地址
- - test ! -z "${CI_COMMIT_TAG}" && docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:latest" # 判斷CI_COMMIT_TAG是否存在
- - docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" # 推送到鏡像倉庫
- deploy_review:
- image: bitnami/kubectl # 該環節需要創建k8s資源,需要kubectl二進制命令
- stage: review
- only:
- - branches # 該stage直對分支有效
- except:
- - tags # 創建tags該stage不被執行
- environment: # 定義jobs將被部署在的環境,如果沒有將會被指定,keyword(name,url,kubernetes...)
- name: dev
- url: https://dev-gitlab-k8s-demo.*******.cn-beijing.alicontainer.com
- on_stop: stop_review # 定義stop的時候執行的jobs
- script:
- - 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 -n devops
- when: manual
- stop_review:
- image: bitnami/kubectl
- stage: review
- variables:
- GIT_STRATEGY: none # 聲明此jobs不會再做代碼的check out
- when: manual # 手動觸發是否繼續執行
- only:
- - branches
- except:
- - master # 除了master分支與tags的變化
- - tags
- environment:
- name: dev
- action: stop
- script:
- - kubectl version
- - kubectl delete ing -l ref=${CI_ENVIRONMENT_SLUG}
- - kubectl delete all -l ref=${CI_ENVIRONMENT_SLUG}
- deploy:
- image: bitnami/kubectl
- stage: deploy
- environment:
- name: live
- url: https://${$CI_ENVIRONMENT_SLUG}.****.cn-beijing.alicontainer.com # 服務的訪問域名
- only:
- - tags
- when: manual
- script:
- - 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 service.yaml ingress.yaml
- - kubectl rollout status -f deployment.yaml
- - kubectl get all,ing -l ref=${CI_ENVIRONMENT_SLUG}
上面便是運行Flask web service的Gitlab持續構建持續部署的配置文件,配置文件中主要是.gitlab-ci.yaml的語法[1]到諸多的配置環境變量[2],需要仔細的閱讀和掌握才能很好的玩轉CI.
K8s資源對象聲明
正如上面看到的,k8s的資源定義文件在項目.gitlab-ci.yml同級目錄manifests內
- 🐳 👉 ls
- README.md deployment.yaml ingress.yaml service.yaml
服務部署的配置文件deployment.yaml
- ---
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
- namespace: devops
- labels:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
- track: stable
- spec:
- replicas: 2
- selector:
- matchLabels:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
- template:
- metadata:
- labels:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
- track: stable
- spec:
- imagePullSecrets:
- - name: myregistry
- containers:
- - name: app
- image: registry.cn-beijing.aliyuncs.com/*****/gitlab-ci-flaskapp-test:__VERSION__ # 前面是鏡像的地址
- imagePullPolicy: Always
- ports:
- - name: web
- protocol: TCP
- containerPort: 5000 # flask web service暴露的端口
- livenessProbe:
- httpGet:
- path: /
- port: 5000
- initialDelaySeconds: 3
- timeoutSeconds: 2
- readinessProbe:
- httpGet:
- path: /
- port: 5000
- initialDelaySeconds: 3
- timeoutSeconds: 2
Flask web service暴露的svc資源對象聲明:
- apiVersion: v1
- kind: Service
- metadata:
- name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
- namespace: devops
- labels:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
- annotations:
- prometheus.io/scrape: "true"
- prometheus.io/port: "5000"
- prometheus.io/scheme: "http"
- prometheus.io/path: "/"
- spec:
- type: ClusterIP
- ports:
- - name: http-metrics
- port: 5000
- protocol: TCP
- selector:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
Flask web service暴露的外網訪問的資源對象ingress聲明:
- ---
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
- namespace: devops
- labels:
- app: gitlab-k8s-demo
- ref: __CI_ENVIRONMENT_SLUG__
- annotations:
- nginx.ingress.kubernetes.io/service-weight: ''
- spec:
- rules:
- - host: __CI_ENVIRONMENT_SLUG__-gitlab-k8s-demo.****.cn-beijing.alicontainer.com
- http:
- paths:
- - path: /
- backend:
- serviceName: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
- servicePort: 5000
配置Runner環境變量
上面的.gitlab-ci.yml中引用的變量就是從這里配置的,變量分為項目變量,gitlab group級別的,具體按需使用
Gitlab平臺上配置Runner環境變量
配置完成之后就可以使用了。
查看效果
master分支變化
將代碼推送到master分支,gitlab會自動的創建一個pipeline交由gitlab runner,當master分支發生變化時,CI的效果圖如下:
master分支變化時Gitlab CI Pipeline
切換到一個新的分支上feature-01上看下CI會執行那些jobs,如下圖,可以在.gitlab-ci.yaml中通過only/except按需定義。
其他分支變化時Gitlab CI Pipeline
其他分支
在Review環節需要手動的觸發,當結果沒有問題之后,就可以手動觸發stop_review刪除部署測試服務
其他分支變化時包含deploy_review與stop_review
deploy_review
deploy_review任務執行日志
stop_review
然后我們手動的觸發stop_review刪除剛才部署的已經沒用的測試環境
stop_review執行日志
創建Tags
- git tag v2.0
- 🐳 👉 git push origin --tags
- Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
- To http://code.*******.cn-beijing.alicontainer.com/root/flask-ci-demo.git
- * [new tag] v2.0 -> v2.0
當推送一個新的tag到gitlab之后,就會觸發一個pipeline,匹配到那個tag的jobs
創建Tags后觸發的Gitlab CI Pipeline
這說明是一個比較穩定的可以上線的版本了,
穩定版本Tags后上線日志
查看一下創建的服務
查看服務的運行狀態
然后我們訪問一下服務,查看是否可以正常使用
Flask web服務登錄后的界面
可以正常登錄并且顯示如下表示服務運行成功了,測試到這里,基本上通過實踐操作說清楚.gitlab-ci.yml里面配置的各項指令含義以及通過Gitlab CI pipeline進行持續集成、持續部署、持續交付等實踐。如果有什么不清楚的,大家可以留言,我們一起交流學習。
參考資料
[1]gitlab-ci reference: https://docs.gitlab.com/ee/ci/yaml/README.html
[2]runner variables: https://docs.gitlab.com/ee/ci/variables/README.html