最近在做基于Docker+Jenkins+Github+Maven的持續(xù)集成環(huán)境,目的是自動化構建springboot項目并發(fā)布到生產環(huán)境扒接。小公司沒有自動化構建系統(tǒng)纷纫,項目發(fā)布過程是這樣的:
1、在本地寫好代碼、測試募狂,測試通過后打成war包或jar包;
2、將war包或jar包拷貝到云服務器上锌蓄;
3、重啟服務撑柔。
整個過程瘸爽,特別是第2、3步靠手工操作可能會引入錯誤(比如命令行下不小心可能會刪掉某個文件)铅忿,不利于項目的可靠性剪决。為此,想起上家公司采用了Jenkins實現(xiàn)了項目的自動化構建,不需要本地打包柑潦、上傳享言,因此干脆就做一回運維,花點時間自己搭建一個持續(xù)集成系統(tǒng)渗鬼,以實現(xiàn):
只需本地寫好代碼并完成測試览露,然后將代碼push到Github,后面的編譯譬胎、打包差牛、發(fā)布等工作交由持續(xù)集成系統(tǒng)自動完成。
恩堰乔,理想很豐滿偏化,現(xiàn)實嘛骨感。以前工作中都是使用現(xiàn)成镐侯,第一次自己搭建踩了無數(shù)的坑侦讨。好在經過一段時間的學習、實操苟翻,今天終于初步達成了目標韵卤。在此,要對Google搜索同學提出表揚崇猫。
由于比較忙怜俐,先大致說一下整個過程。
本地開發(fā)環(huán)境:
操作系統(tǒng):Ubuntu 16.04
Java開發(fā)集成環(huán)境:Intellij idea 2019.1.3(Ultimate Edition)
JDK版本:1.8.0_191
數(shù)據(jù)庫:Mongodb 4.0.10邓尤、Redis 3.0.6
Maven:3.6.1
Tomcat:8.5.41
云服務器環(huán)境:
操作系統(tǒng):阿里云ECS拍鲤,Ubuntu 16.04
JDK版本:使用鏡像openjdk8,目前版本1.8.0_212
數(shù)據(jù)庫:Mongodb 4.0.10汞扎、Redis 3.0.6
Maven:3.6.1
Tomcat:2.1.1季稳,Springboot自帶
大致步驟(后面有時間再補充):
注意:我的賬戶默認是root,因此下面的命令均不需要sudo前綴
第1步:安裝Docker(按照官網安裝即可)
(1)apt-get update
(2)apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
(3)curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
(4)add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
(5)apt-get update
(6)apt-get install docker-ce docker-ce-cli containerd.io
安裝好后澈魄,運行“docker run hello-world”看是否安裝正確景鼠。
第2步:安裝Jenkins
這里通過Jenkins鏡像來安裝。注意:Jenkins鏡像版本越高越好痹扇,否則可能會出現(xiàn)某些插件不兼容的問題铛漓。安裝過程見我的另一篇博客http://www.reibang.com/p/15c1addd1733。這篇博客中的漢化方法有些問題鲫构,重啟Jenkins后會出現(xiàn)部分中文部分英文的情況浓恶,問題還沒有找到。在此建議先不要漢化结笨,因為很多問題可以在google上搜索包晰,漢化后搜索結果的質量你懂的湿镀。
第3步:配置Jenkins和Github
這一步的目的是,當我們將本地項目push到Github后伐憾,會主動觸發(fā)Jenkins從Github上拉取該項目勉痴,然后運行配置好的腳本。具體配置過程見我的另一篇博客http://www.reibang.com/p/29d2a339a57a树肃。
第4步:配置Springboot項目
這一步主要是在項目pom.xml中引入dockerfile-maven-plugin插件蒸矛,在該插件中可以指定項目打包后的名稱、版本以及本地JAR包的位置胸嘴。另外雏掠,還需要在項目根目錄下編寫Dockerfile以及build.sh、run.sh腳本筛谚。
Dockerfile:用于構建Springboot項目的鏡像
build.sh:包含編譯Dockerfile的命令
run.sh:用于基于項目鏡像啟動Docker容器,即運行項目
注意:這三個文件在項目根目錄下停忿,與src同級驾讲,見下圖
4.1 引入dockerfile-maven-plugin插件
4.1.1 在pom.xml的plugins下插入一下配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.company.testproject.TestProjectApplication</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.10</version>
<configuration>
<repository>testproject</repository>
<tag>20190628-1.0</tag>
<buildArgs>
<JAR_FILE>/target/testproject-0.0.1-SNAPSHOT.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
注意:如果你原來將項目打包為war包,那么需要將pox.xml中的<packaging>war</packaging>注釋掉(如果有的話)
4.1.2 配置Tomcat
由于在本地開發(fā)及測試時使用的是單獨安裝的Tomcat席赂,沒有使用Springboot自帶的Tomcat吮铭,因此在pom.xml中是將該自帶的Tomcat依賴去掉了的,即原配置如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>-->
<exclusion>-->
<groupId>org.springframework.boot</groupId>-->
<artifactId>spring-boot-starter-tomcat</artifactIyincid>-->
</exclusion>-->
</exclusions>-->
</dependency>
由于阿里云內存空間有限颅停,為了減少不必要的內存開銷谓晌,同時啟動/停止項目均通過容器來實現(xiàn),不需要顯式執(zhí)行Tomcat的startup.sh腳本來啟動項目癞揉,因此沒必要單獨安裝Tomcat纸肉。故將上面的依賴修改為:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同時,因為使用內置的Tomcat喊熟,因此還要增加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
4.2 編寫Dockerfile
Dockerfile用于構建項目鏡像柏肪。在項目的根目錄下新建文件Dockerfile,內容如下:
FROM openjdk:8
ARG JAR_FILE
RUN apt-get update
RUN apt-get install vim -y
RUN echo "Asia/Shanghai" > /etc/timezone
RUN dpkg-reconfigure -f noninteractive tzdata
RUN mkdir /testproject
ADD ${JAR_FILE} /testproject
EXPOSE 9081
ENTRYPOINT ["java","-jar","/testproject/testproject-0.0.1-SNAPSHOT.jar"]
這里芥牌,我沒有使用宿主機中安裝的JDK烦味,而是基于基礎鏡像openjdk:8來構建項目鏡像。
這里簡要說明該Dockerfile的內容:
第1行:表示當前要構建的鏡像的基礎鏡像是openjdk:8
第2行:設置環(huán)境變量壁拉,暫未指定值谬俄,在后面指定
第3、4行:可選弃理。這兩行目的是更新并安裝vim溃论,這兩行主要是為了安裝vim,因為基礎鏡像中除了jdk之外痘昌,沒有其他軟件可用蔬芥。為了容器啟動后可用對某些配置進行編輯或查看(當然也可以用cat命令查看梆靖,但我習慣了用vim)。如果你不需要vim笔诵,可以不安裝
第5返吻、6行:設置容器中的時區(qū)為東八區(qū)(北京時間)。容器的默認時區(qū)不是東八區(qū)乎婿,因此需要進行設置测僵。需要注意的是,通過這兩行設置后的時間也早于當前時間8小時谢翎。網上有方法是在啟動容器時捍靠,加上參數(shù)-v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone,但我沒有驗證此方法森逮。
第7行:在阿里云服務器上創(chuàng)建目錄testproject榨婆,用于保存testproject-0.0.1-SNAPSHOT.jar
第8行:將環(huán)境變了JAR_FILE指向/testproject
第9行:將容器的端口9081暴露到容器外,即在宿主機可以訪問該端口(即訪問服務的端口)
第10行:指定容器啟動時執(zhí)行的程序及參數(shù)褒侧。這里表示在容器啟動后就發(fā)布項目良风,如果不想立即發(fā)布項目,可以替換為其他命令闷供,比如ENTRYPOINT ["ls", "-l", "/testproject"]
4.3 編寫build.sh
這里不多說烟央,見下面的代碼:
mvn clean
mvn package -DskipTests
docker rmi -f testproject:20190628-1.0
mvn Dockerfile:build
docker images
其中,第3行表示刪除上一個版本的鏡像(可選歪脏,如果要考慮回滾的話就不要刪除)疑俭;第四行是根據(jù)Dockerfile重新生成新鏡像。
4.4 編寫run.sh
這個腳本主要是用于啟動容器婿失,代碼如下:
docker ps -a
aa-remove-unknown
docker stop ruleparser
docker rm -f ruleparser
docker run -d --name ruleparser --network jids --network-alias jids -p 9081:9081 -v /var/jenkins_home/workspace/java_tale/data:/data -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone ruleparser:20190628-1.0
這里要稍微解釋一下:
第2行:防止第3行命令執(zhí)行后出現(xiàn)”cannot stop container”的情況
第3钞艇、4行:停止前一版本的容器,并刪除該容器(不刪的話豪硅,第5行的docker run命令會因存在相同名稱的容器而失斚懔А)
第5行:啟動容器。參數(shù)說明如下:
--name:指定了容器名稱舟误;
--network:指定該容器所在的網橋葡秒,該網橋要與后文的mongodb容器所在網橋相同,否則容器不能訪問mongod嵌溢;
--network-alias:表示網橋的別名眯牧,如果沒有別名的話不需要此參數(shù);
-p:端口映射赖草,格式為"-p 宿主機端口:容器端口"学少;
-v /var/jenkins_home/workspace/java_tale/data:/data:表示將宿主機的目錄/var/jenkins_home/workspace/java_tale/data映射到容器目錄/data。宿主機目錄存放的是項目需要的數(shù)據(jù)秧骑,在項目中位于data目錄下版确,data目錄與src目錄同級扣囊,見第4步附圖。這樣配置后绒疗,需要在項目的properties文件中侵歇,制定數(shù)據(jù)路徑,如下圖:
-v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone:指定容器中的時區(qū)吓蘑,主要是方便查看日志
ruleparser:20190628-1.0:鏡像名稱及標簽惕虑,注意:名稱及標簽要與pom.xml中的配置相同。
第5步:mongodb容器及配置主從節(jié)點
參考博客https://outmanzzq.github.io/2019/01/30/docker-mongo-replica/
這里貼出我的配置過程磨镶。
5.1 運行mongodb replica的三個容器
docker run -dit -p 27000:27017 --name mongo-master -v /server/data/mongodb-docker/db/master:/data/db --network jids --network-alias jids mongo mongod --replSet rs
docker run -dit -p 27001:27017 --name mongo-slave-1 -v /server/data/mongodb-docker/db/slave-1:/data/db --network jids --network-alias jids mongo mongod --replSet rs
docker run -dit -p 27002:27017 --name mongo-slave-2 -v /server/data/mongodb-docker/db/slave-2:/data/db --network jids --network-alias jids mongo mongod --replSet rs
其中:
-p 27000:27017 表示映射宿主機27000端口到容器的27017端口
--name mongo-master 表示容器名為mongo-master
-v /server/data/mongodb-docker/db/master:/data/db 表示映射宿主機目錄到mongodb容器/data/db目錄 (mongodb容器運行時的默認目錄)溃蔫,兩個目錄可以自定義
--net jids 指定容器網絡為local-mongo-cluster
mongo 容器使用的mongodb鏡像
mongod --replSet rs 執(zhí)行mongod命令,將該實例(容器)添加到名為rs的副本集
5.2 進入mongo-master容器的mongo命令行
docker exec -it mongo-master mongo
(進入容器的命令是docker exec -it mongo-master /bin/bash琳猫,不要搞混)
5.3 配置主從節(jié)點
use jids
(1)確定主從配置
config = {
"_id" : "rs",
"members" : [
{
"_id" : 0,
"host" : "mongo-master:27017"
},
{
"_id" : 1,
"host" : "mongo-slave-1:27017"
},
{
"_id" : 2,
"host" : "mongo-slave-2:27017"
}
]
}
(2)初始化
rs.initiate(config)
(3)切換到MASTER節(jié)點(如果切換后還是SECONDARY伟叛,可能由于MASTER連接問題,多嘗試幾次即可)
rs.config()
5.4 從宿主機導入數(shù)據(jù)到容器
5.4.1 首先進入容器并在/root下創(chuàng)建data文件夾
docker exec -it mongo-master /bin/bash
mkdir /root/data
5.4.2 然后退出容器回到宿主機脐嫂,復制數(shù)據(jù)到容器统刮。命令格式為:docker cp 數(shù)據(jù)文件夾 容器名:容器內保存數(shù)據(jù)的目錄。例如:
//假設在宿主機中保存bson和json文件的jids文件夾在/root/jids/data下
docker cp /root/jids/data/jids mongo-master:/root/data
然后就可以驗證主從節(jié)點是否有效了(沒有包括更多驗證雹锣,如某個節(jié)點宕機)网沾,附常用命令:
(1)創(chuàng)建數(shù)據(jù)庫
use 數(shù)據(jù)庫名
(2)創(chuàng)建集合
db.createCollection("集合名")
(3)顯示所有數(shù)據(jù)庫
show dbs
注意:當使用show dbs時癞蚕,會提示Error: listDatabases failed:..."errmsg" : "not master and slaveOk=false"蕊爵。此時執(zhí)行一下命令rs.slaveOk()即可
(4)顯示所有集合
show collections
(5)顯示集合中的所有文檔
db.集合名.find()
第6步:在項目中配置mongodb和redis
修改項目的properties文件,根據(jù)宿主機及容器情況配置mongodb和redis的IP和Port
6.1 配置mongodb
spring.data.mongodb.uri=mongodb://172.17.0.1:27000,172.17.0.1:27001,172.17.0.1:27002/jids?replicaSet=rs
這里的172.17.0.1是宿主機中輸入ifconfig后桦山,docker0對應的虛擬IP地址攒射。這個地址被所有容器共享,相當于容器組成的虛擬網絡的localhost恒水。注意会放,不要使用127.0.0.1來試圖使用宿主機的服務,因為容器所在的虛擬網絡與宿主機所在的網絡是隔斷的钉凌。
6.2 配置redis
redis.host=172.17.0.1
redis.port=6379
這里的host同6.1中的IP咧最。
第7步:訪問項目
這里有個坑。假如測試項目的controller如下
要訪問的功能模塊如下:
如果將war包扔在tomcat的webapps中御雕,那么訪問該模塊時需要采用"IP:PORT/testproject/test_parser?"這樣的URI矢沿。然而,由于我們在Docker中使用的是Springboot內置的Tomcat酸纲,訪問的URI變?yōu)榱?IP:PORT/test_parser?"捣鲸。這一點需要注意。
到此闽坡,整個構建過程基本完成了栽惶。過程這么長愁溜,可以想象遇到了多少坑。上面的流程都是在填完坑后的總結外厂,其中的心酸就不提了冕象。還有很多細節(jié)還可以優(yōu)化,只有待后面熟悉Docker和Jenkins后再做打算了酣衷。