前提,已经有开发环境,但是因为开发环境更新很频繁,会影响到测试,于是准备新建一个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 ttop.sit@172.31.17.55发现不再需要输入密码。
#确认89机器的jenkins用户ssh免密登录至55那台机,且能使用sudo命令
bash-4.2$ ssh ttop.sit@172.31.17.55
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;
}
}