服务背景
通过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