前提,已經有開發環境,但是因爲開發環境更新很頻繁,會影響到測試,於是準備新建一個sit環境。
前置條件:
已經有一臺jenkins服務器,有新建jenkins用戶,ip爲172.31.17.89.
新開一臺linux虛擬機,安裝好docker,ip爲172.31.17.55.
1、在55上新建一個普通用戶,開通sudo權限
新建用戶 useradd ttop.sit ,再使用passwd 修改密碼。可參考之前的文章
開通sudo權限,編輯/etc/sudoers,加入NOPASSWD:ALL。Linux給普通用戶sudo權限
2、登錄到89這臺機,切換到jenkins用戶,能使用剛新建的用戶 免密登錄到55那臺機
使用ssh-keygen 生成一個密鑰對
再使用ssh-copy-id [USER_NAME]@IP將公鑰發到55那臺機器上,再使用ssh [email protected]發現不再需要輸入密碼。
#確認89機器的jenkins用戶ssh免密登錄至55那臺機,且能使用sudo命令
bash-4.2$ ssh [email protected]
Last failed login: Tue Jun 16 16:06:03 CST 2020 from 172.31.17.89 on ssh:notty
There were 4 failed login attempts since the last successful login.
Last login: Tue Jun 16 14:46:33 2020
[ttop.sit@sit-jm-vcsms ~]$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e1400f61dda repo.ttooc.xyz/jm/ms_file:036745cf "sh -c 'chmod +x /en…" 3 hours ago Up 21 seconds 5701/udp, 8080/tcp jm_file_1
01790ca91b0f nginx "/docker-entrypoint.…" 3 hours ago Up 3 hours 0.0.0.0:80->80/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8761->8761/tcp nginx
3、運行registry容器
docker run \
-e SPRING_PROFILES_ACTIVE=dev,composite,swagger,peer1,zipkin,no-liquibase \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI="https://devops.ttotox.net/XXXXXXXX/_git/profile-sit(存放配置文件的git庫地址)" \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH-PATHS="/" \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_USERNAME=cicd.xxxx (能查看git庫的用戶) \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_PASSWORD=xxxxxxx (密碼) \
-e JHIPSTER_CORS_ALLOWED-ORIGINS="*" \
-e JHIPSTER_CORS_ALLOWED-METHODS="*" \
-e JHIPSTER_CORS_ALLOWED-HEADERS="*" \
-e JHIPSTER_CORS_EXPOSED-HEADERS="Authorization,Link,X-Total-Count" \
-e JHIPSTER_CORS_ALLOWED-CREDENTIALS=true \
-e JHIPSTER_CORS_MAX-AGE=1800 \
-e ENCRYPT_KEY=jmvcsmscryptkeysit \
-e EUREKA_CLIENT_SERVICE-URL_DEFAULTZONE=http://admin:admin@eureka-peer-2:8762/eureka/ \
-v /etc/localtime:/etc/localtime \
--network jm \
--ip 172.20.0.100 \
--add-host eureka-peer-1:172.20.0.100 \
--add-host eureka-peer-2:172.20.0.101 \
--add-host localhost:127.0.0.1 \
--restart=always \
-itd --name jm_registry_sit_peer1 \
jhipster/jhipster-registry:v6.2.0
docker run \
-e SPRING_PROFILES_ACTIVE=dev,composite,swagger,peer2,zipkin,no-liquibase \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI="https://devops.ttotox.net/XXXXXXXX/_git/profile-sit" \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH-PATHS="/" \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_USERNAME=cicd.xxx \
-e SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_PASSWORD=xxxxxx \
-e JHIPSTER_CORS_ALLOWED-ORIGINS="*" \
-e JHIPSTER_CORS_ALLOWED-METHODS="*" \
-e JHIPSTER_CORS_ALLOWED-HEADERS="*" \
-e JHIPSTER_CORS_EXPOSED-HEADERS="Authorization,Link,X-Total-Count" \
-e JHIPSTER_CORS_ALLOWED-CREDENTIALS=true \
-e JHIPSTER_CORS_MAX-AGE=1800 \
-e ENCRYPT_KEY=jmvcsmscryptkeysit \
-e EUREKA_CLIENT_SERVICE-URL_DEFAULTZONE=http://admin:admin@eureka-peer-1:8761/eureka/ \
-v /etc/localtime:/etc/localtime \
--network jm \
--ip 172.20.0.101 \
--add-host eureka-peer-1:172.20.0.100 \
--add-host eureka-peer-2:172.20.0.101 \
--add-host localhost:127.0.0.1 \
--restart=always \
-itd --name jm_registry_sit_peer2 \
jhipster/jhipster-registry:v6.2.0
SPRING_PROFILES_ACTIVE 此變量prod,composite,swagger,peer1,zipkin,no-liquibase
prod或者dev是registry自帶有兩個配置文檔,如果想自定義uat則在項目下新建uat的配置文檔即可,使用prod則說明優先讀prod的配置
composite,peer1此爲組合使用,涉及到eureka註冊地址
swagger,開啓swagger查看api zipkin,監控相關
no-liquibase,不使用liquibase記錄數據庫表結構變更
上面起了兩個registry後,接下來就是新建jenkins流水線,因爲CI之前已經弄好了,這裏只需要弄好CD即可。
流水線新建完成,這裏的git庫用戶憑據是之前就生成了的,若沒有,可自己新建一個。jenkins->憑據->系統->全局憑據->新建憑據
再查看一下git庫下的dev_cd_sit中的腳本。總的來說就是給個微服務的名稱,然後給個鏡像的版本號,就可以自動部署起來。裏面還有一個參數sshId,這是一個遠程的憑據,是從jenkins服務器連到要部署微服務的那臺服務器,也就是89到55,再看下面的腳本用的是 sshUserPrivateKey,說明這是一個PrivateKey憑據,貌似之前的免密白搞了,於是再生成一個私鑰憑證。
pipeline {
agent any
parameters {
string(name: 'MS_NAME', defaultValue: '', description: '微服務名稱')
string(name: 'COMMIT_ID', defaultValue: '', description: 'COMMIT_ID')
}
environment {
PATH = "$PATH"
remote="172.31.17.55"
serverName="${params.MS_NAME}"
container="jm_${serverName}_1"
dockerRegistryUser="impos"
dockerRegistryPassword="impos"
dockerRegistry="repo.ttoocx.xyz"
dockerTag="${dockerRegistry}/jm/ms_${serverName}"
sshId="301a3c42-657d-4a2c-a003-e220466xxxxx"
dockerJavaOpts="-Xmx800M"
dockerRunParams=""
}
stages {
stage("Initialization") {
steps {
// use name of the patchset as the build name
buildName "#${BUILD_NUMBER}-${serverName}-${params.COMMIT_ID}"
buildDescription "${serverName}"
}
}
stage('通過SSH發佈到DEV') {
steps {
script{
withCredentials([sshUserPrivateKey(credentialsId: "${sshId}", keyFileVariable: 'PRIVATEKEYFILE', passphraseVariable: '', usernameVariable: 'USERNAME')]) {
script{
try {
sh "ssh -o \"StrictHostKeyChecking no\" ${USERNAME}@${remote} -i ${PRIVATEKEYFILE} sudo docker rm -f ${container}"
sh "ssh -o \"StrictHostKeyChecking no\" ${USERNAME}@${remote} -i ${PRIVATEKEYFILE} sudo docker system prune -a -f"
}
catch(error){
echo "停止舊容器發生錯誤,可能是已經被刪除,跳過"
}
}
sleep 5
def dockerRunParams=""
if (params.MS_NAME == 'gateway') {
dockerRunParams="--ip 172.20.0.102";
}
if (params.MS_NAME == 'file') {
dockerRunParams="-v /opt/jm/files:/files:Z";
}
if (params.MS_NAME == 'websocket') {
dockerRunParams="-p 2222:8081";
}
if(params.MS_NAME=='wechat'){
dockerRunParams = "-v /opt/jm/wechat/apiclient_cert.p12:/wechat/cert.p12";
}
sh "ssh -o \"StrictHostKeyChecking no\" ${USERNAME}@${remote} -i ${PRIVATEKEYFILE} sudo docker login -p ${dockerRegistryUser} -u ${dockerRegistryPassword} ${dockerRegistry}"
sh "ssh -o \"StrictHostKeyChecking no\" ${USERNAME}@${remote} -i ${PRIVATEKEYFILE} sudo docker run ${dockerRunParams} -e EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@eureka-peer-1:8761/eureka,http://admin:admin@eureka-peer-2:8762/eureka -e SPRING_CLOUD_CONFIG_URI=http://admin:admin@eureka-peer-1:8761/config,http://admin:admin@eureka-peer-2:8762/config -e JHIPSTER_REGISTRY_PASSWORD=admin -e SPRING_PROFILES_ACTIVE=dev,composite,swagger,zipkin,no-liquibase -e JAVA_OPTS=\"${dockerJavaOpts}\" --network jm --add-host eureka-peer-1:172.20.0.100 --add-host eureka-peer-2:172.20.0.101 --restart=always --name ${container} -itd ${dockerTag}:${params.COMMIT_ID}"
}
}
}
}
}
}
生成私鑰憑證
仔細思考一下,如果89要連到55去操作,而之前免密的時候就已經把公鑰發到了55這臺服務器,那麼jenkins(89)想要操作55,是不是隻需要把與那個公鑰配對的那個私鑰配到這個憑證裏,之後就可以登錄55那臺機操作了?
登錄89,找到私鑰,一開始進到jenkins用戶,發現沒有.ssh目錄,於是再運行ssh-keygen看它保存的路徑在哪裏。在此路徑下找到id_rsa文件,把裏面的內容配到憑證 私鑰位置。注意整個放進去,不要去掉開頭和結尾的----
保存憑據,拿到id,配置到腳本的sshId處。
運行流水線,看是否能成功啓動容器,以及容器是否註冊進registry。
遇到的問題:
1、一開始憑據使用的是用戶名密碼,運行流水線後,報錯ERROR: Credentials ‘f27ebc14-a76e-4d13-8990-701b7acda412’ is of type ‘Username with password’ where ‘com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey’ was expected
查看腳本後,發現使用的是SSHUserPrivateKey,而我使用的憑據是用戶名密碼的,於是更換憑據,得到解決。
2、私鑰憑證,一開始有些換亂,不知道是要哪個私鑰,後來仔細思考,這個憑證到底是幹啥用的?
看腳本是要ssh連接到55那臺機使用的,那如何才能ssh連接到服務器?之前學習的時候有了解到可以使用用戶密碼以及公鑰私鑰登錄,但是具體的原理沒有深入去理解,ssh-kengen這是生成一個密鑰對,一個公鑰一個私鑰,ssh-copy-id其實是把公鑰放到服務器,那爲什麼這樣就可以遠程?客戶端拿着私鑰去到服務器,服務器中的公鑰可以與之配對,於是得以通過認證,連接到服務器。那麼如果jenkins要連接到服務器,也就是隻要拿着與之匹配的私鑰過去即可,於是想到拿jenkins生成的那條私鑰,配上,成功。
3、容器運行起來後,發現沒有註冊進registry,檢查腳本後,發現是參數SPRING_PROFILES_ACTIVE ,啓動registry與CD腳本中啓動容器的值不一致,修改爲一致的之後得到解決。
4、之前是直接暴露registry容器地址,以此訪問registry後臺,但是這次的腳本中發現沒有暴露registry的端口,而開發環境中確實可以通過該端口訪問registry的,查看之後發現加了一層,用了nginx來轉發,把nginx運行起來,通過nginx來暴露registry的端口,給到外部訪問,該問題得到解決。
啓動nginx的命令及配置文件如下:
sudo docker run --privileged=true -v /opt/jm/nginx/:/etc/nginx/conf.d/:ro -p 80:80 -p 8761:8761 -p 8080:8080 --network jm --restart=always --name nginx -d nginx
查看掛載的/opt/jm/nginx文件目錄下有兩個文件,一個是default.conf和registry.conf
default.conf文件內容如下
upstream gateway {
server 172.20.0.102:8080; #查看之前CD腳本,這是gateway的ip
}
server {
listen 80;
listen 8080;
server_name localhost;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.default.log error;
location / {
proxy_pass http://gateway;
proxy_http_version 1.1;
proxy_buffer_size 128k;
proxy_buffers 32 256k;
proxy_busy_buffers_size 256k;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /html {
root /usr/share/nginx;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
registry.conf文件內容如下
upstream registry {
server 172.20.0.100:8761;
server 172.20.0.101:8762;
}
server {
listen 8761;
server_name localhost;
location / {
proxy_pass http://registry;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}