首发于 Jenkins 中文社区java
本文要点:nginx
本次实验涉及如下多个代码仓库:git
% tree -L 1
├── 1-cd-platform # 实验环境相关代码
├── 1-env-conf # 环境配置代码-实现配置独立
└── 1-springboot # Spring Boot 应用的代码及其部署代码
复制代码
1-springboot 的目录结构以下:github
% cd 1-springboot
% tree -L 1
├── Jenkinsfile # 流水线代码
├── README.md
├── deploy # 部署代码
├── pom.xml
└── src # 业务代码
复制代码
全部代码,均放在 GitHub:github.com/cd-in-pract…spring
笔者使用 Docker Compose + Vagrant 进行实验。环境包括如下几个系统:docker
使用 Vagrant 是为了启动虚拟机,用于部署 Spring Boot 应用。若是你的开发机器没法使用 Vagrant,使用 VirtualBox 也能够达到一样的效果。可是有一点须要注意,那就是网络。若是在虚拟机中要访问 Docker 容器内提供的服务,须要在 DNS 上或者 hosts 上作相应的调整。全部的虚拟机的镜像使用 Centos7。shell
另,接下来笔者的全部教程都将使用 Artifactory 做为制品库。在此申明,笔者没有收 JFrog——研发 Artifactory 产品的公司——任何广告费。 笔者只是想试用商业产品,以便了解商业产品是如何应对制品管理问题的。express
启动 Artifactory 后,须要添加 “Virtual Repository” 及 “Local Repository”。具体请查看 Artifactory 的官方文档。若是你当前使用的是 Nexus,参考本教程,作一些调整,问题也不大。centos
若是想使用已有制品库,能够修改 1-cd-platform 仓库中的 settings-docker.xml 文件,指向本身的制品库。springboot
实验环境近期的整体结构图以下:
architecture.png
之因此说是“近期的”,是由于上图与本篇介绍的结构有小差别。本篇文章尚未介绍 Nginx 与 Springboot 配置共用,可是整体不影响读者理解。
Springboot 流水线有两个阶段:
流水线的全部逻辑都写在 Jenkinsfile 文件。接下来,分别介绍这两个阶段。
此阶段核心代码:
docker.image('jenkins-docker-maven:3.6.1-jdk8')
.inside("--network 1-cd-platform_cd-in-practice -v $HOME/.m2:/root/.m2") {
sh """ mvn versions:set -DnewVersion=${APP_VERSION} mvn clean test package mvn deploy """
}
复制代码
它首先启动一个装有 Maven 的容器,而后在容器内执行编译、单元测试、发布制品的操做。
而 mvn versions:set -DnewVersion=${APP_VERSION}
的做用是更改 pom.xml
文件中的版本。这样就能够实现每次提交对应一个版本的效果。
注意: 这部分须要一些 Ansible 的知识。
首先看部署脚本的入口 1-springboot/deploy/playbook.yaml:
---
- hosts: "springboot"
become: yes
roles:
- {"role": "ansible-role-java", "java_home": "{{JAVA_HOME}}"}
- springboot
复制代码
先安装 JDK,再安装 Spring Boot。JDK 的安装,使用了现成 Ansible role: github.com/geerlingguy…。
重点在 Spring Boot 部署的核心逻辑。它主要包含如下几部分:
以上步骤实如今 1-springboot/deploy/roles/springboot 中。
流水线的部署阶段的核心代码以下:
docker.image('williamyeh/ansible:centos7').inside("--network 1-cd-platform_cd-in-practice") {
checkout([$class: 'GitSCM', branches: [[name: "master"]], doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "env-conf"]], submoduleCfg: [],
userRemoteConfigs: [[url: "https://github.com/cd-in-practice/1-env-conf.git"]]])
sh "ls -al"
sh """ ansible-playbook --syntax-check deploy/playbook.yaml -i env-conf/dev ansible-playbook deploy/playbook.yaml -i env-conf/dev --extra-vars '{"app_version": "${APP_VERSION}"}' """
}
复制代码
它首先将配置变量仓库的代码 clone 下来,而后对 playbook 进行语法上的检查,最后执行 ansible-playbook
命令进行部署。--extra-vars
参数的 app_version
用于指定将要部署的应用的版本。
在 1-springboot/Jenkinsfile 中实现了简易的指定版本部署。核心代码以下:
parameters { string(name: 'SPECIFIC_APP_VERSION',
defaultValue: '', description: '') }
复制代码
stage("build and upload"){
// 若是不指定部署版本,则执行构建
when {
expression{ return params.SPECIFIC_APP_VERSION == "" }
}
// 构建并上传制品的逻辑
steps{...}
}
复制代码
之因此说是“简易”,是由于部署时只指定了制品的版本,并无指定的部署逻辑和配置的版本。这三者的版本要同步,部署才真正作到准确。
全部的配置项都放在 1-env-conf 仓库中。Ansible 执行部署时会读取此仓库的配置。
将配置放在 Git 仓库中有两个好处:
有好处并不表明没有成本。那就是开发人员必须开始关心软件的配置(笔者发现很多开发者忽视配置项管理的重要性。)。
本文重点不在配置管理,后面会有文章重点介绍。
事实上,整个实验,工做量大的地方有两处:一是 Spring Boot 流水线自己的设计;二是整个实验环境的自动化。读者朋友之因此能一两条简单的命令就能启动整个实验环境,是由于笔者作了不少自动化的工做。笔者认为有必要在本篇介绍这些工做。接下来的文章将再也不详细介绍。
流水线中,咱们须要将制品上传到 artifactory(settings.xml 配置的仓库地址是 http://artifactory:8081),可是发现没法解析 host。这是由于流水线中的 Docker 容器所在网络与 Docker compose 建立的网络不一样。因此,解决办法就是让流水线中的 Docker 容器加入到 Docker compose 的网络。
具体解决办法就是在启动容器时,加入参数:--network 1-cd-platform_cd-in-practice
在没有作任何设置的状况启动 Jenkins,会出现一个配置向导。这个过程必须是手工的。笔者但愿这一步也是自动化的。Jenkins 启动时会执行 init.groovy.d/
目录下的 Groovy 脚本。
http://artifactory 部署在 Docker 容器中。Spring Boot 应用的制品要部署到虚拟机中,须要从 http://artifactory 中拉取制品,也就是要在虚拟机里访问容器里提供的服务。虚拟机与容器之间的网络是不通的。那怎么办呢?笔者的解决方案是使用宿主机的 IP 作中转。具体作法就是在虚拟机中加一条 host 记录:
machine.vm.provision "shell" do |s|
s.inline = "echo '192.168.52.1 artifactory' >> /etc/hosts"
end
复制代码
以上是使用了 Vagrant 的 provision
技术,在执行命令 vagrant up
启动虚拟机时,就自动执行那段内联 shell。192.168.52.1
是虚拟宿主机的 IP。因此,虚拟机里访问 http://artifactory:8081 时,实际上访问的是 http://192.168.52.1:8081。
网络结构能够总结为下图:
network.png
目前遗留问题:
这些遗留问题在后期会逐个解决。就像现实同样,常常须要面对各类遗留项目的遗留问题。
本文做者:翟志军