内容简介:【编者的话】本文作者通过一个简单的小项目详细介绍了如何使用Docker, GitLab, Portainer等组件搭建一套CICD流水线长文预警现如今,使用市面上的一些工具配置一套简单的CI/CD流水线并不是一件难事。给一个副项目弄一套这样的流水线也是一个学习许多东西的好方法。Docker,Gitlab,Portainer这些优秀的组件可以用来搭建这个流水线。
【编者的话】本文作者通过一个简单的小项目详细介绍了如何使用Docker, GitLab, Portainer等组件搭建一套CICD流水线
{<11>}长文预警
现如今,使用市面上的一些 工具 配置一套简单的CI/CD流水线并不是一件难事。给一个副项目弄一套这样的流水线也是一个学习许多东西的好方法。Docker,Gitlab,Portainer这些优秀的组件可以用来搭建这个流水线。
示例项目
作为一名法国索菲亚科技园区(位于法国南部)的技术活动组织者,我经常被问到是否有办法知道所有即将举行的活动(会议,灌水,由当地协会组织的聚会等...)。由于此前并没有一个单独的地方列出所有的这些活动,我便开发了 https://sophia.events ,这是一个非常简单的网站页面,它会尝试维护一份最新的活动列表。此项目的代码可以在 Gitlab 上找到。
声明:这个项目超级简单,但是项目本身的复杂度并不是本文的重点。这里我们将详细介绍到的CI/CD流水线的各个组件可以用几乎相同的方式应用到更复杂的项目上。它们也非常适合微服务的场景。
快速过一下代码
为了简化起见,这里有一份events.json文件,每个新事件均会被添加到里面。该文件的部分内容见下面的代码段(抱歉里面掺杂了一些法语):
{
“events”: [
{
“title”: “All Day DevOps 2018”,
“desc”: “We’re back with 100, 30-minute practitioner-led sessions and live Q&A on Slack. Our 5 tracks include CI/CD, Cloud-Native Infrastructure, DevSecOps, Cultural Transformations, and Site Reliability Engineering. 24 hours. 112 speakers. Free online.”,
“date”: “17 octobre 2018, online event”,
“ts”: “20181017T000000”,
“link”: “https://www.alldaydevops.com/",
“sponsors”: [{“name”: “all-day-devops”}]
},
{
“title”: “Création d’une Blockchain d’entreprise (lab) & introduction aux smart contracts”,
“desc”: “Venez avec votre laptop ! Nous vous proposons de nous rejoindre pour réaliser la création d’un premier prototype d’une Blockchain d’entreprise (Lab) et avoir une introduction aux smart contracts.”,
“ts”: “20181004T181500”,
“date”: “4 octobre à 18h15 au CEEI”,
“link”: “https://www.meetup.com/fr-FR/IBM-Cloud-Cote-d-Azur-Meetup/events/254472667/",
“sponsors”: [{“name”: “ibm”}]
},
…
]
}
此文件将会被一个mustache 模板 渲染并生成最终的网站素材。
Docker多阶段构建
一旦生成了最终的网站素材,它们将会被拷贝到一个nginx镜像里,该镜像将会被部署到目标机器上。
得益于多阶段构建(multi-stage build),本次构建分为两部分:
- 网站素材的生成
- 包含网站素材的最终镜像的创建
用来构建镜像的Dockerfile如下:
生成素材
FROM node:8.12.0-alpine AS build
COPY . /build
WORKDIR /build
RUN npm i
RUN node clean.js
RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html
构建托管它们的最终镜像
FROM nginx:1.14.0
COPY --from=build /build/*.html /usr/share/nginx/html/
COPY events.json /usr/share/nginx/html/
COPY css /usr/share/nginx/html/css
COPY js /usr/share/nginx/html/js
COPY img /usr/share/nginx/html/img
本地测试
为了测试生成站点,只需克隆该仓库然后运行test.sh脚本即可。它将随后创建出一个镜像并运行一个容器:
$ git clone git@gitlab.com:lucj/sophia.events.git $ cd sophia.events $ ./test.sh Sending build context to Docker daemon 2.588MB Step 1/12 : FROM node:8.12.0-alpine AS build ---> df48b68da02a Step 2/12 : COPY . /build ---> f4005274aadf Step 3/12 : WORKDIR /build ---> Running in 5222c3b6cf12 Removing intermediate container 5222c3b6cf12 ---> 81947306e4af Step 4/12 : RUN npm i ---> Running in de4e6182036b npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN www@1.0.0 No repository field. added 2 packages from 3 contributors and audited 2 packages in 1.675s found 0 vulnerabilities Removing intermediate container de4e6182036b ---> d0eb4627e01f Step 5/12 : RUN node clean.js ---> Running in f4d3c4745901 Removing intermediate container f4d3c4745901 ---> 602987ce7162 Step 6/12 : RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html ---> Running in 05b5ebd73b89 Removing intermediate container 05b5ebd73b89 ---> d982ff9cc61c Step 7/12 : FROM nginx:1.14.0 ---> 86898218889a Step 8/12 : COPY --from=build /build/*.html /usr/share/nginx/html/ ---> Using cache ---> e0c25127223f Step 9/12 : COPY events.json /usr/share/nginx/html/ ---> Using cache ---> 64e8a1c5e79d Step 10/12 : COPY css /usr/share/nginx/html/css ---> Using cache ---> e524c31b64c2 Step 11/12 : COPY js /usr/share/nginx/html/js ---> Using cache ---> 1ef9dece9bb4 Step 12/12 : COPY img /usr/share/nginx/html/img ---> e50bf7836d2f Successfully built e50bf7836d2f Successfully tagged registry.gitlab.com/lucj/sophia.events:latest => web site available on http://localhost:32768
我们可以使用上述输出的末尾提供的URL访问网站页面。
{<2>}目标环境
云厂商创建的一台虚拟机
或许你也注意到了,这个网站并不是那么关键(每天只有几十次访问),也因此它只需要跑在一台单个的虚拟机上即可。该虚拟机是由 Exoscale ,一个伟大的欧洲云厂商,它上面的Docker Machine创建出来的。
顺便一提,如果你想试试Exoscale的服务的话,知会我一声,我可以提供20欧元的优惠券。
以swarm模式启动的 docker 守护进程
在上面这台虚拟机上运行的Docker守护进程被配置成以Swarm模式运行,因此它支持使用Docker Swarm原生提供的stack,service,config以及secret等原语和它强大(且易于使用)的编排功能。
以docker stack形式运行的应用
下述文件内容里定义了一个包含网站素材的nginx web服务器作为一个服务(service)运行。
version: "3.7"
services:
www:
image: registry.gitlab.com/lucj/sophia.events
networks:
- proxy
deploy:
mode: replicated
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
networks:
proxy:
external: true
这里有几处需要解释下:
- 镜像存储在托管到gitlab.com的私有镜像仓库(这里没涉及到Docker Hub)
- 服务是以2个副本的形式运行在副本模式下,这也就意味着同一时间该服务会有两个正在运行中的任务/容器。Swarm的service会关联一个VIP(虚拟IP地址),这样一来目标是该服务的每个请求会在两个副本之间实现负载均衡。
- 每次完成服务更新时(部署一个新版本的网站),其中一个副本会被更新,然后在10秒后更新第二个副本。这可以确保在更新期间整个网站仍然可用。我们也可以使用回滚策略,但是在这里没有必要。
- 服务会被绑定到一个外部的代理网络,这样一来TLS termination(在swarm里部署的,跑在另外一个服务里,但是超出本项目的范畴)可以发送请求给www服务。
要运行这个stack只需要执行如下命令:
$ docker stack deploy -c sophia.yml sophia_events
统御一切的Portainer
Portainer 是一套很棒的wbe UI工具,它可以很方便地管理Docker宿主机和Docker Swarm集群。下面是Portainer操作界面的一张截图,里面列出了swarm集群里当前可用的stack。
{<3>}当前设定下有3个stack:
- Portainer自己
- 包含了跑着我们网站的服务的sophia_events
- tls,TLS termination服务
如果列出跑在sophia_events stack里的 www 服务的明细的话,我们将可以看到 该服务的webhook 已经处于激活状态。Portainer 1.19.2(迄今为止最新的版本)已经加入了这一功能的支持,它允许定义一个HTTP Post端点,可以在被调用后触发一次服务的更新。正如我们稍后将会看到的,Gitlab runner会负责调用这个webhook。
{<4>}备注:从屏幕截图中可以看到,笔者是通过 localhost:8888 这个地址访问Portainer的用户界面。由于笔者不想将Portainer实例对外暴露,因此是通过ssh隧道访问,该隧道可以通过如下命令开启:
ssh -i ~/.docker/machine/machines/labs/id_rsa -NL 8888:localhost:9000 $USER@$HOST
这样一来,目标是本地机器上的8888端口的所有请求均会通过ssh转发到虚拟机上的9000端口上。9000端口是Portainer在虚拟机上运行时监听的端口,但是并未对外开放,因为它被Exoscale配置的一个安全组禁用了。
备注:在上述命令里,用来连接虚拟机的ssh key是在虚拟机创建时由Docker Machine生成的一个key。
GitLab runner
Gitlab的runner是一个负责执行定义在.gitlab-ci.yml文件里的一组action的进程。就我们这个项目来说,我们定义了一个我们自己的runner,它在虚拟机上以一个容器的形式运行。
第一步就是带上一堆参数来注册该runner。
CONFIG_FOLDER=/tmp/gitlab-runner-config docker run — rm -t -i \ -v $CONFIG_FOLDER:/etc/gitlab-runner \ gitlab/gitlab-runner register \ --non-interactive \ --executor "docker" \ —-docker-image docker:stable \ --url "https://gitlab.com/" \ —-registration-token "$PROJECT_TOKEN" \ —-description "Exoscale Docker Runner" \ --tag-list "docker" \ --run-untagged \ —-locked="false" \ --docker-privileged
在上述参数中,PROJECT_TOKEN可以在Gitlab.com的项目页面上找到,并可以用来注册外部的runner。
{<5>}用来注册一个新的runner的注册token。
一旦runner注册上了,我们需要启动它:
CONFIG_FOLDER=/tmp/gitlab-runner-config docker run -d \ --name gitlab-runner \ —-restart always \ -v $CONFIG_FOLDER:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest
等到它注册上了而且启动起来了,该runner便会出现在gitlab.com上的项目页面里。
{<6>}为此项目创建的runner。
每当有新的commit推送到仓库,此runner随后便会接收到一些要做的任务。它会按顺序执行.gitlab-ci.yml文件里定义好的测试、构建和部署几个阶段。
variables: CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH DOCKER_HOST: tcp://docker:2375 stages: - test - build - deploy test: stage: test image: node:8.12.0-alpine script: - npm i - npm test build: stage: build image: docker:stable services: - docker:dind script: - docker image build -t $CONTAINER_IMAGE:$CI_BUILD_REF -t $CONTAINER_IMAGE:latest . - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com - docker image push $CONTAINER_IMAGE:latest - docker image push $CONTAINER_IMAGE:$CI_BUILD_REF only: - master deploy: stage: deploy image: alpine script: - apk add --update curl - curl -XPOST $WWW_WEBHOOK only: - master
- 测试阶段(test stage)将会运行一些预备检查,确保events.json文件格式正确,并且这里没有遗漏镜像
- 构建阶段(build stage)会做镜像的构建并将它推送到Gitlab上的镜像仓库
- 部署阶段(deploy stage)将会通过发送给Portainer的一个webhook触发一次服务的更新。WWW_WEBHOOK变量的定义可以在Gitlab.com上项目页面的CI/CD设置里找到。
备注:
- runner在swarm上是以一个容器的形式运行。我们可以使用一个共享的runner,这是一些公用的runner,它们会在托管到Gitlab的不同项目所需的任务之间分配时间。但是,由于runner需要访问Portainer的端点(用来发送webhook),也因为笔者不希望Portainer能够从外界访问到,将runner跑在集群里会更安全一些。
- 再者,由于runner跑在一个容器里,为了能够通过Portainer暴露在宿主机上的9000端口连到Portainer,它会将webhook请求发送到Docker0桥接网络上的IP地址。也因此,webhook将遵循如下格式: http://172.17.0.1:9000/api […]a7-4af2-a95b-b748d92f1b3b
部署流程
新版本的站点更新遵循如下流程:
{<8>}- 一个开发者推送了一些变更到Gitlab。这些变更基本上囊括了events.json文件里一个或多个新的事件加上一些额外赞助商的logo。
- Gitlab runner执行在.gitlab-ci.yml里定义好的一组action。
- Gitlab runner调用在Portainer中定义的webhook。
- 在接收到webhook后,Portainer将会部署新版本的www服务。它通过调用Docker Swarm的API实现这一点。Portainer可以通过在启动时绑定挂载的/var/run/docker.sock套接字来访问该API。
如果你想知道更多此unix套接字用法的相关信息,也许你会对之前这篇文章 About /var/run/docker.sock 感兴趣。
- 随后,用户便能看到新版本的站点。
示例
让我们一起来修改代码里的一些内容随后提交/推送这些变更。
$ git commit -m 'Fix image' $ git push origin master
如下截图展示了Gitlab.com上的项目页面里的commit触发的流水线作业。
{<9>}在Portainer一侧,它将会收到一个webhook请求,随后会执行一次服务的更新操作。这里可能看不太清,但是一个副本已经完成了更新,通过第二个副本可以访问站点。随后,几秒钟之后,第二个副本也更新完毕。
{<10>}小结
即便对于这样一个小项目,为它建立一套CI/CD流水线也是一个很好的练习,尤其是可以更加熟悉GitLab(这一直在笔者要学习的列表里面),它是一个非常出色而且专业的产品。这也是一次体验大家期待已久的Portainer的最新版本(1.19.2)推出的webhook功能的机会。此外,对于像这样的副项目,Docker Swarm的使用是无脑上手的,很酷而且易于使用......
原文链接: even the smallest side project deserves its ci cd pipeline (译者:吴佳兴)
以上所述就是小编给大家介绍的《即便一个小项目也有它的CI/CD流水线》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Building Websites with Joomla!
H Graf / Packt Publishing / 2006-01-20 / USD 44.99
This book is a fast paced tutorial to creating a website using Joomla!. If you've never used Joomla!, or even any web content management system before, then this book will walk you through each step i......一起来看看 《Building Websites with Joomla!》 这本书的介绍吧!