首發於 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
目前遺留問題:
這些遺留問題在後期會逐個解決。就像現實同樣,常常須要面對各類遺留項目的遺留問題。
本文做者:翟志軍