GitLab CICD與Kubernetes實踐·部署Flask Web服務(wù)
服務(wù)背景
通過Gitlab CI完成Flask web Service服務(wù)代碼風(fēng)格檢查、單元測試、打包、發(fā)布到k8s環(huán)境里面,同時我們會在.gitlab-ci.yml文件中配置基于分支branch和tag的匹配執(zhí)行相應(yīng)的操作任務(wù)。Flask web Service是一個帶有web登錄界面的測試代碼服務(wù),服務(wù)運行的端口為5000,下面是該服務(wù)構(gòu)建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
然后為項目準(zhǔn)備.gitlab-ci.yml文件,這個文件稍微有點長,可以通過👉遠(yuǎn)程調(diào)用模板庫的方式優(yōu)化配置,此處我們不在多說:
- stages: # 此處分為五個階段,按順序執(zhí)行對應(yīng)的環(huán)節(jié)
- - style
- - test
- - release
- - review
- - deploy
- pep8: # pep8是自定義命名的jobs
- image: python:2.7 # 指定下面script塊的指令在哪個鏡像運行的容器環(huán)境內(nèi)運行
- stage: style # 聲明該pep8的job是屬于哪個stage階段運行
- script: # 該階段執(zhí)行的操作,其實就像在terminal里面執(zhí)行命令一樣。
- - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
- - tox -e pep8 # 使用tox命令進(jìn)行pep8代碼格式檢查規(guī)范性檢查,配置文件為當(dāng)前項目下的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虛擬環(huán)境
- 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虛擬環(huán)境
- buildimage:
- image: docker:latest # 該環(huán)節(jié)需要構(gòu)建鏡像,需要docker二進(jìn)制命令,所以指定一個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" # 配置鏡像加速,當(dāng)?shù)卿浰接戌R像倉庫的時候,如果倉庫的證書不受信任,可以在下方添加`--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 . # 構(gòu)建鏡像
- - 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 # 該環(huán)節(jié)需要創(chuàng)建k8s資源,需要kubectl二進(jìn)制命令
- stage: review
- only:
- - branches # 該stage直對分支有效
- except:
- - tags # 創(chuàng)建tags該stage不被執(zhí)行
- environment: # 定義jobs將被部署在的環(huán)境,如果沒有將會被指定,keyword(name,url,kubernetes...)
- name: dev
- url: https://dev-gitlab-k8s-demo.*******.cn-beijing.alicontainer.com
- on_stop: stop_review # 定義stop的時候執(zhí)行的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 # 手動觸發(fā)是否繼續(xù)執(zhí)行
- 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 # 服務(wù)的訪問域名
- 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持續(xù)構(gòu)建持續(xù)部署的配置文件,配置文件中主要是.gitlab-ci.yaml的語法[1]到諸多的配置環(huán)境變量[2],需要仔細(xì)的閱讀和掌握才能很好的玩轉(zhuǎn)CI.
K8s資源對象聲明
正如上面看到的,k8s的資源定義文件在項目.gitlab-ci.yml同級目錄manifests內(nèi)
- 🐳 👉 ls
- README.md deployment.yaml ingress.yaml service.yaml
服務(wù)部署的配置文件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暴露的外網(wǎng)訪問的資源對象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環(huán)境變量
上面的.gitlab-ci.yml中引用的變量就是從這里配置的,變量分為項目變量,gitlab group級別的,具體按需使用
Gitlab平臺上配置Runner環(huán)境變量
配置完成之后就可以使用了。
查看效果
master分支變化
將代碼推送到master分支,gitlab會自動的創(chuàng)建一個pipeline交由gitlab runner,當(dāng)master分支發(fā)生變化時,CI的效果圖如下:
master分支變化時Gitlab CI Pipeline
切換到一個新的分支上feature-01上看下CI會執(zhí)行那些jobs,如下圖,可以在.gitlab-ci.yaml中通過only/except按需定義。
其他分支變化時Gitlab CI Pipeline
其他分支
在Review環(huán)節(jié)需要手動的觸發(fā),當(dāng)結(jié)果沒有問題之后,就可以手動觸發(fā)stop_review刪除部署測試服務(wù)
其他分支變化時包含deploy_review與stop_review
deploy_review
deploy_review任務(wù)執(zhí)行日志
stop_review
然后我們手動的觸發(fā)stop_review刪除剛才部署的已經(jīng)沒用的測試環(huán)境
stop_review執(zhí)行日志
創(chuàng)建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
當(dāng)推送一個新的tag到gitlab之后,就會觸發(fā)一個pipeline,匹配到那個tag的jobs
創(chuàng)建Tags后觸發(fā)的Gitlab CI Pipeline
這說明是一個比較穩(wěn)定的可以上線的版本了,
穩(wěn)定版本Tags后上線日志
查看一下創(chuàng)建的服務(wù)
查看服務(wù)的運行狀態(tài)
然后我們訪問一下服務(wù),查看是否可以正常使用
Flask web服務(wù)登錄后的界面
可以正常登錄并且顯示如下表示服務(wù)運行成功了,測試到這里,基本上通過實踐操作說清楚.gitlab-ci.yml里面配置的各項指令含義以及通過Gitlab CI pipeline進(jìn)行持續(xù)集成、持續(xù)部署、持續(xù)交付等實踐。如果有什么不清楚的,大家可以留言,我們一起交流學(xué)習(xí)。
參考資料
[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