本文通过Docker + Gitlab + Gitlab CI实现容器的自动化部署。
环境如下:
iproledomain192.168.1.51prod servernone192.168.1.55dnsnone192.168.1.56gitlab ci/dev servernone192.168.1.57gitlab servergitlab.lzxlinux.cn192.168.1.59harbor serverharbor.lzxlinux.cn注意:为了方便后面实验,建议自定义的域名不要与外部域名冲突,否则在连接外网情况下容易解析错误。
通过gitlab和gitlab ci进行flask项目CI演示。
添加docker runner: # gitlab-ci-multi-runner register \ --tls-ca-file=/etc/gitlab/ssl/gitlab.lzxlinux.cn.crt \ --url "https://gitlab.lzxlinux.cn/" \ --registration-token "4kr9ZmLMWasYxqB2tSzQ" \ --name "docker-python2.7" \ --tag-list "python2.7" \ --run-untagged="false" \ --locked="false" \ --executor "docker" \ --docker-image python:2.7 # gitlab-ci-multi-runner register \ --tls-ca-file=/etc/gitlab/ssl/gitlab.lzxlinux.cn.crt \ --url "https://gitlab.lzxlinux.cn/" \ --registration-token "4kr9ZmLMWasYxqB2tSzQ" \ --name "docker-python3.7" \ --tag-list "python3.7" \ --run-untagged="false" \ --locked="false" \ --executor "docker" \ --docker-image python:3.7 # gitlab-ci-multi-runner list Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml worker4 Executor=shell Token=aLzTn6bfk1tXNBLRBbSD URL=https://gitlab.lzxlinux.cn/ docker-python2.7 Executor=docker Token=xBRbYbafrCbxCqcZVrpe URL=https://gitlab.lzxlinux.cn/ docker-python3.7 Executor=docker Token=mc4yojxLhokcVqeBzxxd URL=https://gitlab.lzxlinux.cn/ 新建项目flask-demo:从github导入项目到gitlab中,
新建文件.gitlab-ci.yml: stages: - style - test pep8: stage: style script: - pip install tox - tox -e pep8 tags: - python2.7 #指定对应标签的runner执行 unittest-py27: stage: test script: - pip install tox - tox -e py27 tags: - python2.7 unittest-py37: stage: test script: - pip install tox - tox -e py37 tags: - python3.7 查看运行情况:打开CI/CD → 流水线,
pep8代码格式检查成功
单元测试python2.7成功
单元测试python3.7成功
流水线通用配置:自定义流水线配置,查看流水线状态和覆盖率报告。
设置 → CI/CD → Pipeline status → Coverage report,
分别拷贝markdown部分到README.md中,同时针对代码设置测试覆盖率解析,
最后我们就可以在项目的README.md中看到流水线状态和覆盖率报告,
在上面的基础上,通过gitlab和gitlab ci进行flask项目CI/CD演示,即增加部署操作。
编辑Dockerfile: FROM python:2.7.17-alpine LABEL author="LZX <admin@lzxlinux.cn>" RUN apk add --no-cache gcc musl-dev openssl-dev libffi-dev COPY . /skeleton WORKDIR /skeleton RUN pip install -i https://pypi.mirrors.ustc.edu.cn/simple -r requirements.txt EXPOSE 5000 ENTRYPOINT ["sh", "scripts/dev.sh"] 修改.gitlab-ci.yml: stages: - style - test - deploy pep8: stage: style script: - pip install tox - tox -e pep8 tags: - python2.7 unittest-py27: stage: test script: - pip install tox - tox -e py27 tags: - python2.7 unittest-py37: stage: test script: - pip install tox - tox -e py37 tags: - python3.7 docker-deploy: stage: deploy script: - docker build -t flask-demo . - docker run -d -p 5000:5000 --name flask-demo flask-demo tags: - demo 查看运行情况:打开CI/CD → 流水线,
在gitlab ci服务器上查看容器运行情况,
# docker ps |grep flask-demo e4ec89847a75 flask-demo "sh scripts/dev.sh" 5 minutes ago Up 5 minutes 0.0.0.0:5000->5000/tcp flask-demo可以看到容器构建并启动成功,打开浏览器访问192.168.1.56:5000,
flask项目部署成功。
项目虽然部署成功,但实际上仍存在问题。
1. 实际情况下是不允许这样直接修改master分支代码,然后直接部署到测试环境的; 2. 在持续部署(CD)时,可能之前已经启动了容器,导致再次`docker run`时会失败; 3. style和test阶段可以放到开发人员自己测试时进行,当开发人员在个人的dev分支下修改完代码且测试成功后才允许merge到master分支,此时才允许部署到测试环境; 4. 至于生产环境,仅允许部署稳定版本到生产环境,而latest版本需要部署在测试环境。 gitlab设置:设置 → 仓库 → Protected Branches,设置No one Allow to push到master分支,
设置 → 通用 → 合并请求,勾选流水线必须成功,
修改代码:然后创建新的dev分支,克隆代码到本地主机
# cd /home # git config --global user.name "admin" # git config --global user.email "admin@lzxlinux.cn" # git -c http.sslVerify=false clone https://gitlab.lzxlinux.cn/test/flask-demo.git 正克隆到 'flask-demo'... Username for 'https://gitlab.lzxlinux.cn': root Password for 'https://root@gitlab.lzxlinux.cn': remote: Enumerating objects: 115, done. remote: Counting objects: 100% (115/115), done. remote: Compressing objects: 100% (89/89), done. remote: Total 115 (delta 33), reused 97 (delta 23) 接收对象中: 100% (115/115), 32.12 KiB | 0 bytes/s, done. 处理 delta 中: 100% (33/33), done. # cd flask-demo/ # git checkout dev # vim skeleton/client/templates/footer.html <footer> <div class="container"> <small> <span>© <a href="mailto:xiaoquwl@gmail.com">Gitlab Demo, LZX</a></span> </small> </div> </footer> # vim tests/test_main.py # Copyright 2016 Cisco Systems, Inc. # All rights reserved. import unittest from base import BaseTestCase class TestMainBlueprint(BaseTestCase): def test_index(self): # Ensure Flask is setup. response = self.client.get('/', follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertIn(b'Welcome!', response.data) self.assertIn(b'Register/Login', response.data) def test_footer(self): response = self.client.get('/', follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertIn(b'Gitlab Demo, LZX', response.data) def test_about(self): # Ensure about route behaves correctly. response = self.client.get('/about', follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertIn(b'About', response.data) def test_404(self): # Ensure 404 error is handled. response = self.client.get('/404') self.assert404(response) self.assertTemplateUsed('errors/404.html') if __name__ == '__main__': unittest.main() # vim .gitlab-ci.yml stages: - style - test - deploy pep8: stage: style script: - pip install tox - tox -e pep8 tags: - python2.7 unittest-py27: stage: test script: - pip install tox - tox -e py27 tags: - python2.7 unittest-py37: stage: test script: - pip install tox - tox -e py37 tags: - python3.7 docker-deploy: stage: deploy script: - docker build -t flask-demo . - if [ $(docker ps -aq --filter name=flask-demo) ]; then docker rm -f flask-demo; fi - docker run -d -p 5000:5000 --name flask-demo flask-demo tags: - demo only: - master修改完代码之后,应该先在本地进行代码风格及单元测试检查,同时本地运行flask项目,看是否存在问题,这样可以在merge到master分支前避免低级错误。
提交到gitlab:在本地检查没问题后,提交到gitlab,并发起merge请求,
# git add . # git commit -m 'Update .gitlab-ci.yml' [dev 1c225c0] Update .gitlab-ci.yml 3 files changed, 6 insertions(+), 3 deletions(-) # git -c http.sslVerify=false push origin dev Username for 'https://gitlab.lzxlinux.cn': root Password for 'https://root@gitlab.lzxlinux.cn': Counting objects: 17, done. Delta compression using up to 2 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (9/9), 864 bytes | 0 bytes/s, done. Total 9 (delta 6), reused 0 (delta 0) remote: remote: To create a merge request for dev, visit: remote: https://gitlab.lzxlinux.cn/test/flask-demo/merge_requests/new?merge_request%5Bsource_branch%5D=dev remote: To https://gitlab.lzxlinux.cn/test/flask-demo.git 301e01b..1c225c0 dev -> devCI/CD → 流水线,
pipeline通过后同意merge到master分支,查看变更,确认无误后点击合并
CI/CD → 流水线,
刷新浏览器192.168.1.56:5000,
可以看到merge代码之后部署成功,这里的gitlab ci服务器即测试环境的服务器。
同时部署到测试环境及生产环境,测试环境为latest版本,生产环境为稳定版本,实现版本自动发布。
配置dns服务器:凡是需要解析自定义域名的机器都需要配置dns,这里192.168.1.51作为生产环境服务器,需要配置dns。
# vim /etc/resolv.conf #增加一行,且放在公网dns前面 nameserver 192.168.1.55 #该ip为dns容器所在主机ip nameserver 223.5.5.5 安装gitlab runner: # echo '54.153.54.194 packages.gitlab.com' >> /etc/hosts # curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash # yum install -y gitlab-ci-multi-runner # gitlab-ci-multi-runner status gitlab-runner: Service is running! # usermod -aG docker gitlab-runner # systemctl restart docker # gitlab-ci-multi-runner restart 注册到gitlab服务器:192.168.1.51作为生产环境服务器,也需要注册runner。
# mkdir -p /etc/gitlab/ssl # scp root@gitlab.lzxlinux.cn:/etc/gitlab/ssl/gitlab.lzxlinux.cn.crt /etc/gitlab/ssl # gitlab-ci-multi-runner register \ --tls-ca-file=/etc/gitlab/ssl/gitlab.lzxlinux.cn.crt \ --url "https://gitlab.lzxlinux.cn/" \ --registration-token "4kr9ZmLMWasYxqB2tSzQ" \ --name "prod-runner" \ --tag-list "prod" \ --run-untagged="false" \ --locked="false" \ --executor "shell" # gitlab-ci-multi-runner list Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml prod-runner Executor=shell Token=ESSgXjGeExCxSmtD6Sey URL=https://gitlab.lzxlinux.cn/ 生产环境服务器配置harbor登录: # vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry=harbor.lzxlinux.cn # systemctl daemon-reload && systemctl restart docker.service gitlab设置变量: gitlab ci服务器(192.168.1.56)修改代码: # cd /home/flask-demo/ # vim .gitlab-ci.yml stages: - style - test - deploy - release pep8: stage: style script: - pip install -i https://pypi.mirrors.ustc.edu.cn/simple tox - tox -e pep8 tags: - python2.7 except: #当发布新的tag(版本)时不执行该阶段 - tags unittest-py27: stage: test script: - pip install -i https://pypi.mirrors.ustc.edu.cn/simple tox - tox -e py27 tags: - python2.7 except: - tags unittest-py37: stage: test script: - pip install -i https://pypi.mirrors.ustc.edu.cn/simple tox - tox -e py37 tags: - python3.7 except: - tags docker-deploy: stage: deploy script: - docker build -t flask-demo . - if [ $(docker ps -aq --filter name=flask-demo) ]; then docker rm -f flask-demo; fi - docker run -d -p 5000:5000 --name flask-demo flask-demo - docker image prune -f - docker rm `docker ps -aq --filter status=exited` || docker ps -a tags: - demo only: - master docker-image-release: stage: release script: - docker login harbor.lzxlinux.cn -u $HARBOR_USER -p $HARBOR_PASS - docker build -t harbor.lzxlinux.cn/flask/flask-demo:$CI_COMMIT_TAG . - docker push harbor.lzxlinux.cn/flask/flask-demo:$CI_COMMIT_TAG - if [ $(docker ps -aq --filter name=web) ]; then docker rm -f web; fi - docker run -d -p 5000:5000 --name web harbor.lzxlinux.cn/flask/flask-demo:$CI_COMMIT_TAG - docker image prune -f - docker rm `docker ps -aq --filter status=exited` || docker ps -a - docker images -aq --filter reference=harbor.lzxlinux.cn/flask/flask-demo |grep -v `docker images -aq --filter reference=harbor.lzxlinux.cn/flask/flask-demo:$CI_COMMIT_TAG` | xargs docker rmi -f tags: - prod only: - tags$CI_COMMIT_TAG为预定义的环境变量,即代码新建tag(版本)时tag的值。harbor页面新建一个项目flask,方便后面自动push镜像到该项目中。
# git add . # git commit -m 'Update .gitlab-ci.yml' # git -c http.sslVerify=false push origin dev 查看运行情况:打开CI/CD → 流水线,
pipeline通过后同意merge到master分支,查看变更,确认无误后点击合并
CI/CD → 流水线,
新建标签v1.0.0,CI/CD → 流水线,
打开浏览器,访问192.168.1.51:5000,
到生产环境服务器上查看,
# docker ps |grep web 90a476b48c6e harbor.lzxlinux.cn/flask/flask-demo:v1.0.0 "sh scripts/dev.sh" About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp web # docker images |grep flask-demo harbor.lzxlinux.cn/flask/flask-demo v1.0.0 96294dd392ff 2 minutes ago 226MB到harbor仓库查看镜像,
可以看到merge代码之后部署到生产环境成功,同时harbor仓库也有push的项目版本为v1.0.0的镜像。
这就实现了测试环境为项目的latest版本,生产环境为项目的稳定版本,而且版本可以自动发布。当master分支代码有改变时,自动部署到测试环境;当发布新的tag(版本)时,自动部署到生产环境。
此外,gitlab ci的优势在于它是分布式的,部署项目的机器只需要注册runner到gitlab服务器即可。
