天天看點

CI/CD:使用 Jenkins 的 Pipeline 測試 python 程式并制作 docker 鏡像加入自動單元測試使用 Jenkinsfile 定義 pipelineJenkins 流程項目檔案Jenkins 配置注意完成

上文書實踐了 Jendkins 的 freestyle 項目,這次使用 pipeline 進行測試和建構打包

加入自動單元測試

啟動 Jenkins 的 docker 以後,freestyle 是通過執行 shell 指令來逐漸實作測試、建構和打包的,shell 指令是在 Jenkins 的 docker 容器内執行的,如果要将應用打成 docker 鏡像,需要将主控端的 docker 指令和 socket 檔案映射進 Jenkins 的容器裡。如果要測試 python 程式的話,直接執行 shell 指令會是在 Jenkins 的容器裡面,依賴的 python 版本和庫都不具備,是以更好的方式是建構一個測試用的 docker 鏡像,運作起來進行測試,測試完後銷毀。這個鏡像和 python 程式最終要打包成的 docker 鏡像的依賴完全一緻。

使用 Jenkinsfile 定義 pipeline

Jenkins 的流水線(pipeline)建構方式可以根據一個 Jenkinsfile 檔案中聲明的步驟來逐漸執行 CI/CD 的流程,這個 Jenkinsfile 檔案可以放在源碼包裡由 SCM 進行版本控制。

Jenkins 流程

我這次添加了一個單元測試檔案,Jenkinsfile 中增加了自動執行單元測試的步驟,整體流程是

項目檔案

目錄結構

flask_docker_jenkins_demo/
├── Dockerfile
├── Jenkinsfile
├── README.md
├── app.py
├── requirements.txt
└── test.py
           

app.py

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'


@app.route('/hello/<username>')
def hello_user(username):
    return f'Hello {username}'


@app.route('/health')
def health_checking():
    ret = {'status': 'UP'}
    return jsonify(ret)


if __name__ == '__main__':
    app.run(port=5000, debug=False)

           

test.py

import unittest
import app


class TestHome(unittest.TestCase):
    def setUp(self):
        app.app.testing = True
        self.app = app.app.test_client()

    def test_home(self):
        res = self.app.get('/')
        self.assertEqual(res.status, '200 OK')
        self.assertEqual(res.data, b'Hello, World!')

    def test_hello_user(self):
        name = 'Yngwie'
        res = self.app.get(f'/hello/{name}')
        self.assertEqual(res.status, '200 OK')
        self.assertIn(bytearray(f'{name}', 'utf-8'), res.data)


if __name__ == '__main__':
    import xmlrunner
    runner = xmlrunner.XMLTestRunner(output='test-reports')
    unittest.main(testRunner=runner)
    unittest.main()

           

requirements.txt

Flask
gunicorn
xmlrunner
           

Dockerfile

FROM python:3.6.9-alpine

ADD . /app

RUN pip install --no-cache-dir -i http://mirrors.aliyun.com/pypi/simple/ \
--trusted-host mirrors.aliyun.com -r /app/requirements.txt

ENV GUNICORN_CMD_ARGS="--bind=0.0.0.0:5001 --chdir=./app/ --workers=2"

CMD ["gunicorn", "app:app"]
           

Jenkinsfile

pipeline {
  agent none
  stages {
    stage('build and test') {
      agent { docker { image 'python:3.6.9-alpine' } }
      stages {
        stage('build'){
          steps {
            sh 'pip install --no-cache-dir -r requirements.txt'
          }
        }
        stage('test') {
          steps {
            sh 'python test.py'
          }
          post {
            always {
              junit 'test-reports/*.xml'
            }
          }
        }
      }
    }
    stage('build docker image'){
      agent any
      steps{
        sh 'docker build -t my-flask-image:latest .'
        sh 'a=`docker images -f "dangling=true" -q | wc -l`'
        sh 'if [ $a -ge 0 ];then docker rmi $(docker images -f "dangling=true" -q);fi'
      }
    }
  }
}
           

Jenkins 配置

Jenkins 任務

  1. 【建立任務】 - 起名,選擇流水線類型 - 确定
  2. 【建構觸發器】 - 【輪詢 SCM】 - 【日程表】填

    * * * * *

  3. 【流水線】 - 【定義】 - 選【Pipeline script from SCM】 - 【SCM】- 選【Git】,填寫倉庫位址 - 【腳本路徑】 - 填Jenkinsfile - 儲存

注意

Jenkinsfile 中根 agent 設定為 none,這樣可以在後續的 stages 和 stage 中跟别定義不同的 agent,測試使用臨時建構的 docker 鏡像,而打包要使用Jenkins 的 docker 容器執行,容器執行主控端映射進來的 docker 指令。

完成

這樣,在每次向代碼庫 push 新的代碼以後,Jenkins 會自動拉取代碼,建構測試鏡像測試,然後打包成 docker 鏡像。