随着项目的不断增多,最开始单体项目手动执行 docker build
命令,手动发布项目就不再适用了。一两个项目可能还吃得消,10 多个项目每天让你构建一次还是够呛。即便你的项目少,每次花费在发布上面的时间累计起来都够你改几个 BUG 了。
所以我们需要自动化这个流程,让项目的发布和测试不再这么繁琐。在这里我使用了 Jenkins 作为基础的 CI/CD Pipeline 工具,关于 Jenkins 的具体介绍这里就不再赘述。在版本管理、构建项目、单元测试、集成测试、环境部署我分别使用到了 Gogs、Docker、Docker Swarm(已与 Docker 整合) 这几个软件协同工作。
以下步骤我参考了 Continuous Integration with Jenkins and Docker 一文,并使用了作者提供的 groovy 文件和 slave.py
文件。
关于 Docker-CE 的安装,请参考我的另一篇博文 《Linux 下的 Docker 安装与使用》 。
一、Jenkins 的部署
既然都用了 Docker,我是不想在实体机上面安装一堆环境,所以我使用了 Docker 的形式来部署 Jenkins 的 Master 和 Slave,省时省力。Master 就是调度管道任务的主机,也是唯一有 UI 供用户操作的。而 Slave 就是具体的工作节点,用于执行具体的管道任务。
1.1 构建 Master 镜像
第一步,我们在主机上建立一个 master 文件夹,并使用 vi
创建两个 groovy 文件,这两个文件在后面的 Dockerfile 会被使用到,下面是 default-user.groovy
文件的代码:
import jenkins.model.*
import hudson.security.*
def env = System.getenv()
def jenkins = Jenkins.getInstance()
jenkins.setSecurityRealm(new HudsonPrivateSecurityRealm(false))
jenkins.setAuthorizationStrategy(new GlobalMatrixAuthorizationStrategy())
def user = jenkins.getSecurityRealm().createAccount(env.JENKINS_USER, env.JENKINS_PASS)
user.save()
jenkins.getAuthorizationStrategy().add(Jenkins.ADMINISTER, env.JENKINS_USER)
jenkins.save()
接着再用 vi
创建一个新的 executors.groovy
文件,并输入以下内容:
import jenkins.model.*
Jenkins.instance.setNumExecutors(0)
以上动作完成之后,在 master 文件夹下面应该有两个 groovy 文件。
两个 master 所需要的 groovy 文件已经编写完成,下面来编写 master 镜像的 Dockerfile 文件,每一步的作用我已经用中文进行了标注。
# 使用官方的 Jenkins 镜像作为基础镜像。
FROM jenkins/jenkins:latest
# 使用内置的 install-plugins.sh 来安装插件。
RUN /usr/local/bin/install-plugins.sh git matrix-auth workflow-aggregator docker-workflow blueocean credentials-binding
# 设置 Jenkins 的管理员账户和密码。
ENV JENKINS_USER admin
ENV JENKINS_PASS admin
# 跳过初始化安装向导。
ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
# 将刚刚编写的两个 groovy 脚本复制到初始化文件夹内。
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/
COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
# 挂载 jenkins_home 目录到 Docker 卷。
VOLUME /var/jenkins_home
接着我们通过命令构建出 Master 镜像。
docker build -t jenkins-master .
1.2 构建 Slave 镜像
Slave 镜像的核心是一个 slave.py
的 python 脚本,它主要执行的动作是运行 slave.jar
并和 Master 建立通信,这样你的管道任务就能够交给 Slave 进行执行。这个脚本所做的工作流程如下:
我们再建立一个 slave 文件夹,并使用 vi
将 python 脚本复制进去。
slave.py
的内容:
from jenkins import Jenkins, JenkinsError, NodeLaunchMethod
import os
import signal
import sys
import urllib
import subprocess
import shutil
import requests
import time
slave_jar = '/var/lib/jenkins/slave.jar'
slave_name = os.environ['SLAVE_NAME'] if os.environ['SLAVE_NAME'] != '' else 'docker-slave-' + os.environ['HOSTNAME']
jnlp_url = os.environ['JENKINS_URL'] + '/computer/' + slave_name + '/slave-agent.jnlp'
slave_jar_url = os.environ['JENKINS_URL'] + '/jnlpJars/slave.jar'
print(slave_jar_url)
process = None
def clean_dir(dir):
for root, dirs, files in os.walk(dir):
for f in files:
os.unlink(os.path.join(root, f))
for d in dirs:
shutil.rmtree(os.path.join(root, d))
def slave_create(node_name, working_dir, executors, labels):
j = Jenkins(os.environ['JENKINS_URL'], os.environ['JENKINS_USER'], os.environ['JENKINS_PASS'])
j.node_create(node_name, working_dir, num_executors = int(executors), labels = labels, launcher = NodeLaunchMethod.JNLP)
def slave_delete(node_name):
j = Jenkins(os.environ['JENKINS_URL'], os.en