jagadishmanchala@Jagadish-Local:/Volumes/Work$ tree jenkins-complete/
jenkins-complete/
├── Dockerfile
├── README.md
├── credentials.xml
├── default-user.groovy
├── executors.groovy
├── install-plugins.sh
├── sample-maven-job_config.xml
├── create-credential.groovy
└── trigger-job.sh
我们来看看它们都是干嘛的:
GLOBALgithubgithubjagadish***{AQAAABAAAAAQoj3DDFSH1******
仔细观察上面的代码,我们可以看到一个 id 为 “github” 的用户名以及加密后的密码。这个 id 很重要,我们会在后面的流水线中用到。
Jenkins server -> Manage Jenkins -> Script console
,然后在输入框里输入下面的代码
import hudson.util.Secret
def secret = Secret.fromString("password")
println(secret.getEncryptedValue())
将 “password” 换成你自己的密码,点击运行,你就得到了加密后的内容。再把这个内容粘贴到
credentials.xml
文件里面就可以了。DockerHub 的密码加密过程同上。
Jenkins console
里创建一个名为“sample-maven-job”的 job,这个文件包含了它的详细配置。FROM jenkins/jenkins:lts
ARG HOST_DOCKER_GROUP_ID
# 使用内置的 install-plugins.sh 脚本安装我们所需的插件
RUN install-plugins.sh pipeline-graph-analysis:1.9 \
cloudbees-folder:6.7 \
docker-commons:1.14 \
jdk-tool:1.2 \
script-security:1.56 \
pipeline-rest-api:2.10 \
command-launcher:1.3 \
docker-workflow:1.18 \
docker-plugin:1.1.6
# 设置 admin 用户的环境变量
ENV JENKINS_USER admin
ENV JENKINS_PASS admin
# 跳过初始设置向导
ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
# 启动脚本,设置执行器的数量、创建 admin 用户
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/
COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
COPY create-credential.groovy /usr/share/jenkins/ref/init.groovy.d/
# 命名 job
ARG job_name_1="sample-maven-job"
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/latest/
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/builds/1/
COPY ${job_name_1}_config.xml /usr/share/jenkins/ref/jobs/${job_name_1}/config.xml
COPY credentials.xml /usr/share/jenkins/ref/
COPY trigger-job.sh /usr/share/jenkins/ref/
# 添加自定义配置到容器里
#COPY ${job_name_1}_config.xml "$JENKINS_HOME"/jobs/${job_name_1}/config.xml
USER root
#RUN chown -R jenkins:jenkins "$JENKINS_HOME"/
RUN chmod -R 777 /usr/share/jenkins/ref/trigger-job.sh
# 用给定的用户组 ID 创建 'Docker' 用户组
# 将 'jenkins' 用户加到 'Docker' 用户组
RUN groupadd docker -g ${HOST_DOCKER_GROUP_ID} && \
usermod -a -G docker jenkins
RUN apt-get update && apt-get install -y tree nano curl sudo
RUN curl https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz | tar xvz -C /tmp/ && mv /tmp/docker/docker /usr/bin/docker
RUN curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
RUN chmod 755 /usr/local/bin/docker-compose
RUN usermod -a -G sudo jenkins
RUN echo "jenkins ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN newgrp docker
USER jenkins
#ENTRYPOINT ["/bin/sh -c /var/jenkins_home/trigger-job.sh"]
getent group Docker
获得。
HOST_DOCKER_GROUP_ID
被设为了构建参数,我们要在构建时将宿主机的 Docker 用户组 id 做为参数传进来参与构建。
# 使用内置的 install-plugins.sh 脚本安装我们所需的插件
RUN install-plugins.sh pipeline-graph-analysis:1.9 \
cloudbees-folder:6.7 \
docker-commons:1.14 \
接下来是 install-plugins.sh 脚本,把要安装的插件作为参数传给脚本。这个脚本是默认提供的,也可以从宿主机复制一份。
ENV JENKINS_USER admin
ENV JENKINS_PASS admin
我们设置了
JENKINS_USER
和
JENKINS_PASS
两个环境变量,
default-user.groovy
脚本会用它们创建帐号 admin 用户(密码 admin)。
# 跳过初始设置向导
ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
这个使得 Jenkins 以静默模式安装
# 设置启动器数量和创建 admin 用户的启动脚本
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/
COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
COPY create-credential.groovy /usr/share/jenkins/ref/init.groovy.d/
像我们讨论的那样,上面的脚本会设置执行器个数为 5,创建默认用户 admin/admin。需要注意的是,如果去看 Jenkins 官方的 Docker 镜像,你会看到有一个 VOLUME 指向了
/vars/jenkins_home
目录。这个意思是设置 Jenkins 的家目录,类似于物理机上使用包管理器安装 Jenkins 时的目录
/var/lib/jenkins
。但是,当 volume 挂载好以后,就只有 root 用户有权限在那里编辑或者添加文件。为了让未授权的 jenkins 用户复制内容到 volume, 将所有东西复制到
/usr/share/Jenkins/ref/
。这样当容器启动后,Jenkins 会自动使用 Jenkins 用户把这个位置的文 件拷贝一份到
/vars/jenkins_home
中。同样,复制到
/usr/share/jenkins/ref/init.groovy.d/
的脚本会在 Jenkins 启动后被执行。
# 命名 job
ARG job_name_1="sample-maven-job"
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/latest/
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/builds/1/
COPY ${job_name_1}_config.xml /usr/share/jenkins/ref/jobs/${job_name_1}/config.xml
COPY credentials.xml /usr/share/jenkins/ref/
COPY trigger-job.sh /usr/share/jenkins/ref/
在上面的例子中,我把我的 job 名字设置为 “sample-maven-job”,然后创建目录,复制一些文件。
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/latest/
RUN mkdir -p "$JENKINS_HOME"/jobs/${job_name_1}/builds/1/
这些说明很重要,它们在 Jenkins 家目录创建了一些用来存放配置文件的文件夹。
latest/
和
builds/1
存放的目录也需要与其 job 相对应。这些创建好以后,我们把已经复制到
/var/share/jenkins/ref
的文件 “sample-maven-job_config.xml”,再让 Jenkins 复制 到
/var/jenkins_home/jobs/
,这样就有了 sample-maven-job。最后,我们同样把
credentials.xml
和
trigger-job.sh
文件复制到
/usr/share/jenkins/ref
。当容器启动以后, 所有这个目录下的文件都会以 Jenkins 用户的权限移动到
/var/jenkins_home
。
USER root
#RUN chown -R jenkins:jenkins "$JENKINS_HOME"/
RUN chmod -R 777 /usr/share/jenkins/ref/trigger-job.sh
# 用指定的用户组组 ID 创建 'docker' 用户组
# 并将 'jenkins' 用户添加到该组
RUN groupadd docker -g ${HOST_DOCKER_GROUP_ID} && \
usermod -a -G docker jenkins
RUN apt-get update && apt-get install -y tree nano curl sudo
RUN curl https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz | tar xvz -C /tmp/ && mv /tmp/docker/docker /usr/bin/docker
RUN curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
RUN chmod 755 /usr/local/bin/docker-compose
RUN usermod -a -G sudo jenkins
RUN echo "jenkins ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN newgrp docker
USER jenkins
下面的指令以 root 用户执行。在 root 用户的指令下,我们使用宿主机上的 Docker group ID 在容器里创建新的 Docker 用户组。然后把 Jenkins 用户加到 Docker 组当中。通过这些,我们就可以使用 Jenkins 用户创建容器了。这样就能突破只有 root 用户能创建容器的限制。为了让 Jenkins 用户能创建容器,我们需要把 Jenkins 用户添加到 Docker 用户组当中去。在下面的指令里,我们安装了 docker-ce 和 docker-compose 工具。我们设置了 Docker-compose 的权限。最后,我们把 Jenkins 用户加到 sudoers 文件里,以给到 root 用户特定的权限。
RUN newgrp docker
这个指令非常重要。通常我们修改一个用户的用户组,都需要重新登录以使新的设置生效。为了略过这一步,我们使用 Docker 命令 newgrp 使设置直接生效。最后,我们回到 Jenkins 用户。
docker build --build-arg HOST_DOCKER_GROUP_ID="`getent group docker | cut -d':' -f3`" -t jenkins1 .
在 Dockerfile 的所在目录下运行上面的 Docker 构建指令。在上面的命令中,我们传了 Docker 用户组 ID 给 build-arg。这个值会传给
HOST_DOCKER_GROUP_ID
,用来在 Jenkins 容器里创建相同 ID 的用户组。下载以及安装 Jenkins 插件会增加构建镜像的时间。
docker run -itd -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker -p 8880:8080 -p 50000:50000 jenkins1
关于卷挂载有两件重要的事。第一是我们把 Docker 命令挂载到了容器里,当需要其它容器时,就可以在当前容器创建了。另一个重要的是挂载
/var/run/Docker.sock
。Docker.sock 是 Docker 守护进程监听的一个 UNIX socket。这是访问 Docker API 的主要入口点。它也可以是 TCP 类型的 socket,但是出于安全原因,默认设定是 UNIX 类型的。Docker 默认通过这个 socket 执行命令。我们把它挂载到 Docker 容器里,是为了能在容器里启动新的其它容器。这个挂载也可以用于服务自省和日志目的。但这增加了被攻击的风险,使用的时候要小心。上面的命令执行后,我们就得到一个运行着的 Jenkins 容器。可以通过 URL
:8880
查看 Jenkins 控制台。使用 “admin/admin” 登录 Jenkins。我们就可以看到还没有运行过的、使用 SCM,Token 和凭据创建的 sample-maven-job。
docker exec /bin/sh -C /var/jenkins_home/trigger-job.sh
运行后我们就可以看到流水线的构建开始了。
stage("build"){
agent {
docker {
image 'maven:3-alpine'
args '-v /root/.m2:/root/.m2'
}
steps {
sh 'mvn -B -DskipTests clean package'
stash includes: 'target/*.jar', name: 'targetfiles'
}
}
}
在上面的 stage 中,我们设置它的 agent 环境为 Docker 镜像 “maven:3-alpine.” 这样 Jenkins 就会触发 maven:3-alpine 容器, 然后执行定义在步骤里的命令
mvn -B -DskipTests clean package
。同样的,单元测试也是以这样的方式运行。docker 启动一个 Maven 镜像,然后执行
mvn test
。
environment {
registry = "docker.io//"
registryCredential = 'dockerhub'
dockerImage = ''
}
另一件重要的事是定义环境。我定义了名为
docker.io/jagadesh1982/sample
的仓库,意味着使用最终制品(jar 包)所创建的镜像名称也将遵循这个格式
docker.io/jagadesh1982/sample:
。如果你的镜像需要推送到 Dockerhub 的话,记住这一点是非常重要的。Dockerhub 希望镜像名按照
docker.io//
这样的风格命名,以方便上传。当构建结束后,新的镜像会被上传到 Dockerhub,本地的镜像则会被删除。
my-app-1.0-SNAPSHOT.jar
到镜像中去。它的内容是这样:FROM alpine:3.2
RUN apk --update add openjdk7-jre
CMD ["/usr/bin/java", "-version"]
COPY /target/my-app-1.0-SNAPSHOT.jar /
CMD /usr/bin/java -jar /my-app-1.0-SNAPSHOT.jar
推荐阅读
Jenkins CLI,助你轻松管理 Jenkins
Jenkins X 新 logo
介绍新的 GitLab 分支源插件
Jenkins 线上技术交流
Jenkins CLI 命令行 v0.0.22
译者:tomatofrommars