本教程包括。
- 克隆和设置演示项目
- 添加CircleCI配置文件,包括作业结构和工作流程
- 使用并行性和拆分测试
持续集成已经成为软件项目的一个广泛接受的做法。随着更多的技术被引入到持续集成和软件开发中,开发人员正在寻找实用的方法来从中受益。对于现实生活中的从业者来说,涵盖玩具实例的基本教程并不总是足够的。作为Django、Docker和CircleCI的实际使用者,这无疑是我的一个痛点。这就是我写这个教程的原因。
在本指南中,你将从一位同行那里学到如何为一个生产就绪的Docker化Django 3.2应用程序建立持续集成管道。
为了让你能轻松上手,我创建了一个演示项目,让你从GitHub上克隆。
当你完成本教程后,每次推送到你的项目的GitHub repo都会自动触发构建,并将覆盖文档上传到像Codecov.io这样的代码覆盖云提供商。你甚至会学到如何使用CircleCI的并行模式来更快地运行你的测试,实现更短的迭代周期。
前提条件
要从本教程中获得最大的收获,你将需要。
- 在GitHub仓库中的一个Docker化的Django 3.2项目演示。
- 一个GitHub账户。
- 一个CircleCI账户。
- 在你的本地机器上安装Docker Desktop。你可以遵循Docker自己的Windows或macOS教程。
- 在您的本地机器上安装Git。你可以按照GitHub自己的教程来安装它。
- 一个Codecov账户。
- 一个Docker Hub账户。
我们的教程是与平台无关的,但以CircleCI为例。如果你没有CircleCI账户,可以**在这里注册一个免费账户。**
因为每个Django项目都是不同的,我将带领你完成使用演示项目建立持续集成管道的步骤。
我建议你从头到尾学习本教程两次。第一遍,使用演示项目。第二遍,使用你自己的Django项目,在概念上重复同样的步骤,但在必要时对过程进行调整。
我将在本教程的后面部分提供一些提示,以便为你自己的项目调整步骤。现在,请专注于跟随演示项目,这样你就能对整个过程从头到尾的运作形成直觉。
克隆演示项目
在这一步,你将git克隆代码库,并确保你对该代码库能在本地运行测试感到满意。基本上,这个代码库遵循Django文档中相同的经典七部曲教程。在演示项目代码库中,有一些不同之处。
主要的区别在于。
- 演示项目维护了基于函数的视图,而不是教程中使用的基于类的视图。
- 演示项目有一个
config文件夹,其中的settings子文件夹包含base.py、local.py和production.py,而不是只有settings.py。 - 演示项目有一个
dockerized_django_demo_circleci文件夹和一个users子文件夹。
用Git克隆该代码库。
$ git clone https://github.com/CIRCLECI-GWP/dockerized-django-demo.git
下面是结果的输出。
[secondary_label Output]
Cloning into 'dockerized-django-demo-circleci'...
remote: Enumerating objects: 325, done.
remote: Counting objects: 100% (325/325), done.
remote: Compressing objects: 100% (246/246), done.
remote: Total 325 (delta 83), reused 293 (delta 63), pack-reused 0
Receiving objects: 100% (325/325), 663.35 KiB | 679.00 KiB/s, done.
Resolving deltas: 100% (83/83), done.
现在你已经克隆了演示项目,你已经准备好测试本地版本的Docker化演示项目了。
在本地运行测试
在这一步,你将继续上一步的工作,确保测试可以在本地机器上成功运行。
启动你的Docker桌面应用程序。如果你需要回顾一下如何做,请参考本教程。
{: .zoomable }
进入你的Docker容器的bash,为Django运行测试。
$ cd dockerized-django-demo
$ docker-compose -f local.yml run web_django bash
按照这个输出,进入Django Docker容器的bash shell。
[secondary_label Output]
Creating network "dockerized-django-demo_default" with the default driver
Creating dockerized-django-demo_db_postgres_1 ... done
Creating dockerized-django-demo_web_django_run ... done
Going to use psycopg2 to connect to postgres
psycopg2 successfully connected to postgres
PostgreSQL is available
root@abc77c0122b3:/code#
现在,运行测试。
root@abc77c0122b3:/code# python3 manage.py test --keepdb
输出结果将是。
[secondary_label Output]
Using existing test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 10 tests in 0.454s
OK
Preserving test database for alias 'default'...
这个输出意味着你的测试是好的。当你把这个推送到CircleCI时,这应该是同样的结果。
要退出你的bash shell,输入exit
root@abc77c0122b3:/code# exit
回到你的主机操作系统。
[secondary_label Output]
exit
$
要正确关闭Docker。
$ docker-compose -f local.yml down --remove-orphans
下面是输出结果。
[secondary_label Output]
Stopping dockerized-django-demo_db_postgres_1 ... done
Removing dockerized-django-demo_web_django_run_8eeefb5e1d1a ... done
Removing dockerized-django-demo_db_postgres_1 ... done
Removing network dockerized-django-demo_default
你现在可以安全地停止Docker Desktop了。
现在,你的Django项目和测试案例的工作应该令你满意。现在是时候让你把代码推送到GitHub了。
将您自己的GitHub仓库连接到CircleCI上
如果你一直在使用演示项目,并且你已经创建了自己的GitHub repo,那么你需要重新命名你克隆代码的远程。添加你的GitHub repo作为远程origin 。
将所有提到的greendeploy-io/test-ddp 替换为你的 repo 的实际名称。org-name/repo-name
$ git remote rename origin upstream
$ git remote add origin git@github.com:greendeploy-io/test-ddp.git
$ git push -u origin main
[secondary_label Output]
Enumerating objects: 305, done.
Counting objects: 100% (305/305), done.
Delta compression using up to 10 threads
Compressing objects: 100% (222/222), done.
Writing objects: 100% (305/305), 658.94 KiB | 2.80 MiB/s, done.
Total 305 (delta 70), reused 297 (delta 67), pack-reused 0
remote: Resolving deltas: 100% (70/70), done.
To github.com:greendeploy-io/test-ddp.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
添加CircleCI配置文件
在这一步,你将创建一个CircleCI配置文件,并编写脚本,为你的项目配置一个持续集成管道。首先,创建一个名为.circleci 的文件夹,并在其中创建一个config.yml 文件。现在,打开新创建的文件,输入这些内容。
# Python CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-python/ for more details
#
version: 2.1
# adding dockerhub auth because dockerhub change their policy
# See https://discuss.circleci.com/t/authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/23
# and https://support.circleci.com/hc/en-us/articles/360050623311-Docker-Hub-rate-limiting-FAQ
docker-auth: &docker-auth
auth:
username: $DOCKERHUB_USERNAME
password: $DOCKERHUB_PAT
orbs:
codecov: codecov/codecov@3.2.2
jobs:
build:
docker:
# specify the version you desire here
# use `-browsers` prefix for selenium tests, e.g. `3.7.7-browsers`
- image: cimg/python:3.8.12
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
environment:
DATABASE_URL: postgresql://root@localhost/circle_test?sslmode=disable
USE_DOCKER: no
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
- image: cimg/postgres:14.1 # database image for service container available at `localhost:<port>`
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
environment: # environment variables for database
POSTGRES_USER: root
POSTGRES_DB: circle_test
working_directory: ~/repo
# can check if resource is suitable at resources tab in builds
resource_class: large
# turn on parallelism to speed up
parallelism: 4
steps: # a collection of executable commands
# add deploy key when needed esp when requirements point to github url
# - add_ssh_keys:
# fingerprints:
# - "ab:cd:ef..."
- checkout # special step to check out source code to the working directory
# using dockerize to wait for dependencies
- run:
name: install dockerize
command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
environment:
DOCKERIZE_VERSION: v0.4.0
# the actual wait for database
- run:
name: Wait for db
command: dockerize -wait tcp://localhost:5432 -timeout 1m
- restore_cache: # restores saved dependency cache if the Branch key template or requirements.txt files have not changed since the previous run
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
- run: # install and activate virtual environment with pip
command: |
python3 -m venv venv
. venv/bin/activate
pip install --upgrade setuptools && pip install wheel
pip install --upgrade pip==22.0.4
pip install --upgrade pip-tools
pip-sync requirements/base.txt requirements/local.txt
- save_cache: # special step to save dependency cache
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
paths:
- "venv"
- run:
name: run collectstatic
command: |
. venv/bin/activate
python3 manage.py collectstatic --noinput
- run: # run tests
name: run tests using manage.py
command: |
# get test files while ignoring __init__ files
TESTFILES=$(circleci tests glob "*/tests/*.py" | sed 's/\S\+__init__.py//g' | sed 's/\S\+factories.py//g')
echo $TESTFILES | tr ' ' '\n' | sort | uniq > circleci_test_files.txt
TESTFILES=$(circleci tests split --split-by=timings circleci_test_files.txt | tr "/" "." | sed 's/\.py//g')
. venv/bin/activate
# coverage's --parallel-mode will generate a .coverage-{random} file
# usage: https://docs.djangoproject.com/en/3.2/topics/testing/advanced/#integration-with-coverage-py
# add `--verbosity=3` between $TESTFILES --keepdb if need to debug
coverage run --parallel-mode manage.py test --failfast $TESTFILES --keepdb
# name: run tests using pytest
# command: |
# . venv/bin/activate
# pytest -c pytest.ini -x --cov-report xml --cov-config=.coveragerc --cov
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: tr1
# save coverage file to workspace
- persist_to_workspace:
root: ~/repo
paths:
- .coverage*
fan-in_coverage:
docker:
# specify the version you desire here
# use `-browsers` prefix for selenium tests, e.g. `3.7.7-browsers`
- image: cimg/python:3.8.12
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
working_directory: ~/repo
resource_class: small
parallelism: 1
steps:
- checkout
- attach_workspace:
at: ~/repo
- restore_cache: # restores saved dependency cache if the Branch key template or requirements.txt files have not changed since the previous run
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
- run: # install and activate virtual environment with pip
command: |
python3 -m venv venv
. venv/bin/activate
pip install --upgrade setuptools && pip install wheel
# because of https://github.com/jazzband/pip-tools/issues/1617#issuecomment-1124289479
pip install --upgrade pip==22.0.4
pip install --upgrade pip-tools
pip-sync requirements/base.txt requirements/local.txt
- save_cache: # special step to save dependency cache
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
paths:
- "venv"
- run:
name: combine coverage and generate XML report
command: |
. venv/bin/activate
coverage combine
# at this point, if combine succeeded, we should see a combined .coverage file
ls -lah .coverage
# this will generate a .coverage.xml file
coverage xml
- codecov/upload:
# xtra_args: '-F'
upload_name: "${CIRCLE_BUILD_NUM}"
workflows:
main:
jobs:
- build
- fan-in_coverage:
requires:
- build
为了从这个脚本中轻松访问Docker注册表,你在一个上下文中设置了一个docker-auth 字段,并提供了你的Docker Hub凭证。这些凭证将在后面的教程中作为环境变量列入。
接下来,Codecov球体从CircleCI球体注册表中被拉入。这个球体有助于将你的覆盖率报告上传到Codecov,而无需任何复杂的配置。
两个独立的工作构建项目,在CircleCI上运行测试,并将覆盖率报告部署到Codecov。
buildfan-in_coverage
配置的工作流程将确保build 工作在fan-in_coverage 开始之前完全运行,因为它的输出将被上传到Codecov。
工作结构和工作流程
如上一节所述,有两个作业:build 和fan-in_coverage 。我在这里将它们折叠起来,以便于回顾。
jobs: build:...
fan-in_coverage:...
workflows:
main:
jobs:
- build
- fan-in_coverage:
requires:
- build
请注意fan-in_coverage 需要来自build 工作的输出,以及fan-in_coverage 如何将覆盖工件上传到codecov.io。使用两个作业允许你使用并行性,并且仍然允许上传工件。
平行性
为什么要使用并行性?答案很简单:并行可以加快构建的速度。
# turn on parallelism to speed up
parallelism: 4
为了允许并行,你需要拆分测试。为了拆分测试,CircleCI需要知道测试文件的完整列表。
通过拆分测试实现并行化
第一行试图找到所有相关的测试文件,跳过__init__ 和factories.py 。然后,文件列表被写入circleci_test_files.txt 。
TESTFILES=$(circleci tests glob "*/tests/*.py" | sed 's/\S\+__init__.py//g' | sed 's/\S\+factories.py//g')
echo $TESTFILES | tr ' ' '\n' | sort | uniq > circleci_test_files.txt
TESTFILES=$(circleci tests split --split-by=timings circleci_test_files.txt | tr "/" "." | sed 's/\.py//g')
然后,CircleCI将使用coverage.py 帮助以并行模式运行测试。如果你需要调试,添加一个--verbosity=3 标志。
# add `--verbosity=3` between $TESTFILES --keepdb if need to debug
coverage run --parallel-mode manage.py test --failfast $TESTFILES --keepdb
存储工件和文件
最后,配置告诉CircleCI在哪里存储工件和其他覆盖率相关的文件,以便在fan-in_coverage 。
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: tr1
# save coverage file to workspace
- persist_to_workspace:
root: ~/repo
paths:
- .coverage*
提交所有的修改并更新你在GitHub上的项目库。
将您的项目连接到CircleCI
要将您的项目连接到CircleCI,请登录您的CircleCI账户。如果你用你的GitHub账户注册,你所有的仓库将在你的项目仪表板上可用。从列表中找到你的项目;在这种情况下,它是dockerized-django-demo 。点击Set Up Project。
{: .zoomable }。
你将被提示在你的项目中选择.circleci/config.yml 文件。然后,输入分支的名称并点击设置项目。
{: .zoomable }
你的工作流程将启动并成功构建。
将Docker Hub凭证作为环境变量添加到项目设置中
接下来,你需要将你的Docker Hub用户名和个人访问令牌作为环境变量添加到项目设置中。
进入GitHub repo的CircleCI项目页面,点击项目设置。从左边的菜单侧边栏选择环境变量。
创建两个环境变量:DOCKERHUB_PAT 和DOCKERHUB_USERNAME 。使用你的Docker Hub账户作为数值的来源。
 {: .zoomable }
当你完成后,每次推送到你的GitHub仓库或拉动请求都会导致在你的CircleCI上构建。
因为演示项目是一个公开的版本,CircleCI项目的构建页面和Codecov覆盖页面也是公开的。
查看演示项目的CircleCI项目构建页面,了解在完成所有步骤后,您的构建页面应该是什么样子。
总结
在这篇文章中,你克隆了Django 3.2演示项目的生产准备,Docker化的演示项目。现在你可以为你自己的Docker化Django 3.2项目做同样的事情。更重要的是,在文章的后半部分,我深入探讨了config.yml 文件中应包含的内容。
如前所述,我建议你第一次使用演示项目按照教程进行操作。第二次,当使用你自己的项目时,只需用your Django project 替换所有提到的demo Django project 。
我强调了解释的某些部分,以便你知道它们是如何帮助你加快CI/CD构建的。使用并行性可以加快构建速度,而不会牺牲像Docker和上传到代码覆盖供应商的关键部分。需要注意的一点是,这里的设置是为了持续交付,而不是持续部署。请访问这个页面,了解更多的区别。
KimSia是一个独立的软件工程师,为企业编写运营软件作为一种服务。他是一个狂热的Python开发者,其项目包括GreenDeploy和AutonomyFirst.app。