GitLab CICD与Kubernetes实践·部署Flask Web服务,


服务背景

通过Gitlab CI完成Flask web Service服务代码风格检查、单元测试、打包、发布到k8s环境里面,同时我们会在.gitlab-ci.yml文件中配置基于分支branch和tag的匹配执行相应的操作任务。Flask web Service是一个带有web登录界面的测试代码服务,服务运行的端口为5000,下面是该服务构建Docker镜像的Dockerfile

  1. FROM python:3.4 
  2.  
  3. COPY . /skeleton 
  4. WORKDIR /skeleton 
  5. RUN pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com # 配置pip源,加速下载 
  6. EXPOSE 5000 
  7. ENTRYPOINT ["sh", "scripts/dev.sh"] 

定义.gitlab-ci.yml

然后为项目准备.gitlab-ci.yml文件,这个文件稍微有点长,可以通过👉远程调用模板库的方式优化配置,此处我们不在多说:

  1. stages: # 此处分为五个阶段,按顺序执行对应的环节 
  2.   - style 
  3.   - test 
  4.   - release 
  5.   - review 
  6.   - deploy 
  7.  
  8. pep8: # pep8是自定义命名的jobs 
  9.   image: python:2.7 # 指定下面script块的指令在哪个镜像运行的容器环境内运行 
  10.   stage: style # 声明该pep8的job是属于哪个stage阶段运行 
  11.   script: # 该阶段执行的操作,其实就像在terminal里面执行命令一样。 
  12.     - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox 
  13.     - tox -e pep8 # 使用tox命令进行pep8代码格式检查规范性检查,配置文件为当前项目下的tox.ini 
  14.  
  15. unittest-py2.7: 
  16.   image: python:2.7 
  17.   stage: test 
  18.   script: 
  19.     - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox 
  20.     - tox -e py27 # 指定使用py27虚拟环境 
  21.  
  22. unittest-py3.4: 
  23.   image: python:3.4 
  24.   stage: test 
  25.   script: 
  26.     - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox 
  27.     - tox -e py34 # 指定使用py34虚拟环境 
  28.  
  29. buildimage: 
  30.   image: docker:latest # 该环节需要构建镜像,需要docker二进制命令,所以指定一个docker镜像 
  31.   variables: # 给buildimage这个job传递的变量 
  32.     DOCKER_DRIVER: overlay 
  33.     DOCKER_HOST: tcp://localhost:2375 # 与service指定容器通信 
  34.   services: 
  35.     - name: docker:17.03-dind 
  36.       command: 
  37.           - "--registry-mirror=https://*****.mirror.aliyuncs.com" # 配置镜像加速,当登录私有镜像仓库的时候,如果仓库的证书不受信任,可以在下方添加`--insecure-registry=*****`选项 
  38.   stage: release 
  39.   script: 
  40.     - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" ${CI_REGISTRY_REPO_URL} # 登录私有或者共有镜像仓库 
  41.     - docker build -t "${CI_REGISTRY_IMAGE}:latest" -f ./Dockerfile . # 构建镜像 
  42.     - docker tag "${CI_REGISTRY_IMAGE}:latest" "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" # 给镜像打个推送到镜像仓库的地址 
  43.     - test ! -z "${CI_COMMIT_TAG}" && docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:latest" # 判断CI_COMMIT_TAG是否存在 
  44.     - docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" # 推送到镜像仓库 
  45.  
  46. deploy_review: 
  47.   image: bitnami/kubectl # 该环节需要创建k8s资源,需要kubectl二进制命令 
  48.   stage: review 
  49.   only: 
  50.     - branches # 该stage直对分支有效 
  51.   except: 
  52.     - tags # 创建tags该stage不被执行 
  53.   environment: # 定义jobs将被部署在的环境,如果没有将会被指定,keyword(name,url,kubernetes...) 
  54.     name: dev 
  55.     url: https://dev-gitlab-k8s-demo.*******.cn-beijing.alicontainer.com 
  56.     on_stop: stop_review # 定义stop的时候执行的jobs 
  57.   script: 
  58.     - kubectl version 
  59.     - cd manifests/ 
  60.     - sed -i "s/__CI_ENVIRONMENT_SLUG__/${CI_ENVIRONMENT_SLUG}/" deployment.yaml ingress.yaml service.yaml 
  61.     - sed -i "s/__VERSION__/${CI_COMMIT_REF_NAME}/" deployment.yaml ingress.yaml service.yaml 
  62.     - | 
  63.       if kubectl apply -f deployment.yaml | grep -q unchanged; then 
  64.           echo "=> Patching deployment to force image update." 
  65.           kubectl patch -f deployment.yaml -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"ci-last-updated\":\"$(date +'%s')\"}}}}}" 
  66.       else 
  67.           echo "=> Deployment apply has changed the object, no need to force image update." 
  68.       fi 
  69.     - kubectl apply -f service.yaml || true 
  70.     - kubectl apply -f ingress.yaml 
  71.     - kubectl rollout status -f deployment.yaml 
  72.     - kubectl get all,ing -n devops 
  73.   when: manual 
  74.  
  75. stop_review: 
  76.   image: bitnami/kubectl 
  77.   stage: review 
  78.   variables: 
  79.     GIT_STRATEGY: none # 声明此jobs不会再做代码的check out 
  80.   when: manual # 手动触发是否继续执行 
  81.   only: 
  82.     - branches 
  83.   except: 
  84.     - master # 除了master分支与tags的变化 
  85.     - tags 
  86.   environment: 
  87.     name: dev 
  88.     action: stop 
  89.   script: 
  90.     - kubectl version 
  91.     - kubectl delete ing -l ref=${CI_ENVIRONMENT_SLUG} 
  92.     - kubectl delete all -l ref=${CI_ENVIRONMENT_SLUG} 
  93.  
  94. deploy: 
  95.   image: bitnami/kubectl 
  96.   stage: deploy 
  97.   environment: 
  98.     name: live 
  99.     url: https://${$CI_ENVIRONMENT_SLUG}.****.cn-beijing.alicontainer.com # 服务的访问域名 
  100.   only: 
  101.     - tags 
  102.   when: manual 
  103.   script: 
  104.     - kubectl version 
  105.     - cd manifests/ 
  106.     - sed -i "s/__CI_ENVIRONMENT_SLUG__/${CI_ENVIRONMENT_SLUG}/" deployment.yaml ingress.yaml service.yaml 
  107.     - sed -i "s/__VERSION__/${CI_COMMIT_REF_NAME}/" deployment.yaml ingress.yaml service.yaml 
  108.     - kubectl apply -f deployment.yaml service.yaml ingress.yaml 
  109.     - kubectl rollout status -f deployment.yaml 
  110.     - 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内

  1. 🐳 👉 ls 
  2. README.md       deployment.yaml ingress.yaml    service.yaml 

服务部署的配置文件deployment.yaml

  1. --- 
  2. apiVersion: apps/v1 
  3. kind: Deployment 
  4. metadata: 
  5.   name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__ 
  6.   namespace: devops 
  7.   labels: 
  8.     app: gitlab-k8s-demo 
  9.     ref: __CI_ENVIRONMENT_SLUG__ 
  10.     track: stable 
  11. spec: 
  12.   replicas: 2 
  13.   selector: 
  14.     matchLabels: 
  15.       app: gitlab-k8s-demo 
  16.       ref: __CI_ENVIRONMENT_SLUG__ 
  17.   template: 
  18.     metadata: 
  19.       labels: 
  20.         app: gitlab-k8s-demo 
  21.         ref: __CI_ENVIRONMENT_SLUG__ 
  22.         track: stable 
  23.     spec: 
  24.       imagePullSecrets: 
  25.         - name: myregistry 
  26.       containers: 
  27.       - name: app 
  28.         image: registry.cn-beijing.aliyuncs.com/*****/gitlab-ci-flaskapp-test:__VERSION__ # 前面是镜像的地址 
  29.         imagePullPolicy: Always 
  30.         ports: 
  31.         - name: web 
  32.           protocol: TCP 
  33.           containerPort: 5000 # flask web service暴露的端口 
  34.         livenessProbe: 
  35.           httpGet: 
  36.             path: / 
  37.             port: 5000 
  38.           initialDelaySeconds: 3 
  39.           timeoutSeconds: 2 
  40.         readinessProbe: 
  41.           httpGet: 
  42.             path: / 
  43.             port: 5000 
  44.           initialDelaySeconds: 3 
  45.           timeoutSeconds: 2 

Flask web service暴露的svc资源对象声明:

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__ 
  5.   namespace: devops 
  6.   labels: 
  7.     app: gitlab-k8s-demo 
  8.     ref: __CI_ENVIRONMENT_SLUG__ 
  9.   annotations: 
  10.     prometheus.io/scrape: "true" 
  11.     prometheus.io/port: "5000" 
  12.     prometheus.io/scheme: "http" 
  13.     prometheus.io/path: "/" 
  14. spec: 
  15.   type: ClusterIP 
  16.   ports: 
  17.     - name: http-metrics 
  18.       port: 5000 
  19.       protocol: TCP 
  20.   selector: 
  21.     app: gitlab-k8s-demo 
  22.     ref: __CI_ENVIRONMENT_SLUG__ 

Flask web service暴露的外网访问的资源对象ingress声明:

  1. --- 
  2. apiVersion: extensions/v1beta1 
  3. kind: Ingress 
  4. metadata: 
  5.   name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__ 
  6.   namespace: devops 
  7.   labels: 
  8.     app: gitlab-k8s-demo 
  9.     ref: __CI_ENVIRONMENT_SLUG__ 
  10.   annotations: 
  11.     nginx.ingress.kubernetes.io/service-weight: '' 
  12. spec: 
  13.   rules: 
  14.   - host: __CI_ENVIRONMENT_SLUG__-gitlab-k8s-demo.****.cn-beijing.alicontainer.com 
  15.     http: 
  16.       paths: 
  17.       - path: / 
  18.         backend: 
  19.           serviceName: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__ 
  20.           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

  1. git tag v2.0 
  2. 🐳 👉 git push origin --tags                 
  3. Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 
  4. To http://code.*******.cn-beijing.alicontainer.com/root/flask-ci-demo.git 
  5.  * [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

相关内容