DEVOPS-迁移SpringMVC应用到生产K8S集群

释放双眼,带上耳机,听听看~!

一、背景

     公司项目有springmvc和springboot两种类型,springmvc项目主要通过tomcat启动,springboot项目主要通过java  [option] -jar *.jar的方式启动。我们准备把springmvc项目和springboot项目先做成镜像先在灰度环境跑,如果没有问题我们再把镜像push到生产镜像仓库,最后选择在灰度验证通过的镜像运行在k8s上从而实现springmvc迁移。

二、实现原理

2.1 主要流程

  1. 从测试环境拉取已经编译好的代码
  2. 修改测试配置为生产配置
  3. 通过Dockerfile把已经修改好配置的程序打包成业务镜像
  4. 把镜像push到灰度镜像仓库,然后通过灰度镜像仓库的业务镜像启动
  5. 如果需要增量更新,可以预先把程序通过docker cp 拷贝出来,然后更新指定文件后docker cp回去,docker restart imageName,如果没有启动没有问题,可以docker commit  imageName  newImageName,最后push到生产镜像仓库。

三、应用运行在灰度环境

3.1 参数化构建过程参数

  • 项目名称,包含容器名称和项目名称

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 版本号,用于打镜像tag

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 测试环境地址,包含ip地址,测试环境标识

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 是否为springboot项目,springmvc和springboot项目配置文件结构不一样,走的逻辑不同

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 是否更新到灰度环境【1.226】,因为这个自由风格的项目运行在一台专门打镜像的机器上【0.26】,所以如果需要更新到灰度机器需要连接到灰度机器【1.226】执行运行指定业务镜像的脚本

DEVOPS-迁移SpringMVC应用到生产K8S集群

3.2 参数化构建的执行时shell

3.2.1 执行时shell的基本步骤

  1. 通过shell字符串切割把传入的projectVals拆分成想要的字符串

  2. 把指定测试环境的服务代码拷贝到执行此项目的机器上【0.26】

  3. 进入WEB-INF/classes目录下把测试环境配置替换为生产配置,为制作生产镜像做准备

  4. 拷贝一份已经写好的springmvc的Dockerfile 【在3.2.2节中已说明】,替换项目名称为传入的项目名称

  5. 通过修改后的Dockerfile生成镜像,并push到灰度镜像仓库

  6. 如果需要在灰度机器上跑已经制作好的镜像,可以远程到灰度机器,执行projectName.sh脚本 【在3.2.3节中已说明】

  7. 如果需要增量更新,通过docker cp 把webapps/finalName 文件夹拷贝出来,增量更新后 restart容器,启动无误可以commit,最后push到生产镜像仓库供线上k8s使用


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
1contentName="$project"
2currentTime=`date "+%Y%m%d%H%M"`
3echo $currentTime
4export PATH="/opt/apache-maven-3.3.3/bin:$PATH"
5
6projectVals=(${projectVals//,/ })
7projectName=${projectVals[0]}
8containerArray=(${projectName//-/ })
9container=${containerArray[1]}
10project=${projectVals[1]}
11finalName=${projectVals[2]}
12
13if [ "$finalName" == "" ]; then
14  finalName=$project
15fi
16
17tag=$finalName-${gitVersion##*/}-$currentTime
18
19TestEnvs=(${TestEnv//,/ })
20serverName=${TestEnvs[0]}
21serverHost=${TestEnvs[1]}
22
23if [ "$gitVersion" == "" ]; then
24  echo "gitVersion不能为空"
25  exit
26fi
27
28cd /plk/source
29
30if [ "true" != "$isSpringBoot" ] ; then
31  rm -rf $finalName
32  sshpass -p $remotePasswd scp -r -P 3721 $serverHost:/programs/$project/target/$finalName .
33  
34  cd $finalName/WEB-INF/classes
35  pwd
36  ls
37  if [ -f "$finalName-mq.properties" ] ; then
38      sed -i 's|.mg.addr|-mg-addr|g' $finalName-mq.properties
39      sed -i 's|mq-mg-addr|mq1-mg-addr:9876;mq2-mg-addr:9876;mq3-mg-addr|g' $finalName-mq.properties
40  fi
41  
42  echo "set prod zookeeper"
43  sed -i 's|{dubbo.registry.address}|{dubbo.registry.cluster.address}|g' $finalName-*.xml
44    
45  if [ ! -z "`grep 'jdbc.properties' $finalName-spring-context-server.xml`" ]; then
46    echo "set prod mysql config"
47    sed -i 's|jdbc.mg.addr:3306/moni|rm-***.mysql.rds.aliyuncs.com:3306/moni_b|g' *jdbc.properties
48    sed -i 's|jdbc.mg.addr:3306/game|rm-***.mysql.rds.aliyuncs.com:3306/game_b|g' *jdbc.properties
49    sed -i 's|.username=***|.username=***|g' *jdbc.properties
50    sed -i 's|.password=***|.password=***|g' *jdbc.properties
51  fi
52  
53  if [ ! -z "`grep 'mongo.properties' $finalName-spring-context-server.xml`" ]; then
54    echo "set prod mongo config"
55    sed -i 's|=mongo.mg.addr:27017|=dds-***.mongodb.rds.aliyuncs.com:3717,dds-***.mongodb.rds.aliyuncs.com:3717|g' *-mongo.properties
56    sed -i 's|mongo.mg.addr|dds-***.mongodb.rds.aliyuncs.com|g' *-mongo.properties
57    sed -i 's|mongo.readonly.mg.addr|dds-***.mongodb.rds.aliyuncs.com|g' *-mongo.properties
58    
59    sed -i 's|database=statistics|database=statistics_b|g' *-mongo.properties
60    sed -i 's|database=everydaykline|database=everydaykline_b|g' *-mongo.properties
61    sed -i 's|database=evaluation|database=evaluation_b|g' *-mongo.properties
62    sed -i 's|database=safedata|database=safedata|g' *-mongo.properties
63    
64    sed -i 's|credentials=evaluation:***@evaluation|credentials=***:***@evaluation_b|g' *-mongo.properties
65    sed -i 's|credentials=statistics:***@statistics|credentials=***:***@statistics_b|g' *-mongo.properties
66    sed -i 's|credentials=everydaykline:***@everydaykline|credentials=***:***@everydaykline_b|g' *-mongo.properties
67    sed -i 's|credentials=safedata:***@safedata|credentials=***:***@safedata|g' *-mongo.properties
68    sed -i 's|27017|3717|g' *-mongo.properties
69  fi
70  
71  if [ ! -z "`grep 'redis' $finalName-spring-context-server.xml`" ]; then
72    echo "set prod redis config"
73    sed -i 's|redis-cluster|redis|g' $finalName-spring-context-server.xml
74    sed -i 's|=redis.mg.addr|=mgsc-***.redis.rds.aliyuncs.com|g' $finalName-*redis.properties
75    sed -i 's|=Mogu07550831..|=Mogu07550831**|g' $finalName-*redis.properties
76  fi
77
78  cd /plk/source
79  cp -f /plk/source/Dockerfile.mvc Dockerfile/Dockerfile.${finalName}
80  sed -i "s|{project}|${finalName}|g" Dockerfile/Dockerfile.${finalName}
81
82else
83     //次数逻辑为springboot项目逻辑,下篇文章详细展开
84fi
85
86sudo docker build -f /plk/source/Dockerfile/Dockerfile.${finalName} -t registry-vpc.cn-shenzhen.aliyuncs.com/moguyun-pre/$dockerStore:$tag .
87sudo docker login --username $dockerHubName --password $dockerHubPasswd registry-vpc.cn-shenzhen.aliyuncs.com
88sudo docker push registry-vpc.cn-shenzhen.aliyuncs.com/moguyun-pre/$dockerStore:$tag
89
90if [ "true" == "$isPreUpdate" ] ; then
91  sshpass -p Mogupro0601sz ssh root@$preEnvHost "cd /docker/k8s; sh -x ${projectName}.sh $tag 2>&1 &"
92fi
93
94if [ "true" == "$isIncUpdate" ] ; then
95  sshpass -p Mogupro0601sz ssh root@$preEnvHost "cd /opt/apps; rm -rf $finalName && docker cp  $container:/opt/tomcat/webapps/$finalName ."
96fi
97
98
99

3.2.2 springMVC项目的Dockerfile模板

  1. 定义源路径和目标路径,为拷贝文件做准备
  2. 定义RUN_OPTION,JVM内存大小分配【 tomcat 生产内存调优】,字符集编码
  3. 定义pname,这个参数会作为sysinit.sh 的第二个位置参数传递给脚本,最终传入CATALINA_OPTS,tomcat启动加载CATALINA_OPTS实现向pinpoint服务端的注册。【详情参考:通过Dockerfile编写tomcat镜像  pinpoint 客户端配置】
  4. 定义RUNPORT,这个参数会作为sysinit.sh 的第三个参数传递给脚本,最终会做一个if判断,如果RUNPORT为""或者为0,tomcat里面的server.xml默认的8080端口就不会改变。【详情参考: 通过Dockerfile编写tomcat镜像】

] # vim Dockerfile.mvc


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1FROM registry.moguyun.com/tomcat:9.26
2MAINTAINER lijun moguyun.com
3
4ENV SOURCEPATH {project}
5ENV TARGETPATH /opt/tomcat/webapps
6ENV RUN_OPTION "-Xms1g -Xmx4g -Xmn256m -Dfile.encoding=UTF-8"
7ENV PNAME {project}
8ENV RUNPORT 0
9
10ADD $SOURCEPATH $TARGETPATH/$SOURCEPATH
11
12EXPOSE 8080
13CMD ["sh", "-c", "/bin/sysinit.sh \"$RUN_OPTION\" \"$PNAME\" \"$RUNPORT\" "]
14

3.2.3 灰度机器启动镜像脚本

  1. 定义项目名称,用于拼接项目日志路径
  2. 定义pname,通过docker -e加载的参数会覆盖Dockerfile里面定义的pname 
  3. 定义运行端口号,使用默认的桥接模式可以允许所有容器都使用8080端口,暴露在宿主机上的端口自定义 【详情参考: Docker网络模型对比 】
  4. 定义镜像名称,${3} 为镜像tag,通过jenkins传入
  5. 定义远程DEBUG端口号,这个参数被JVM_OPTION加载,JVM_OPTION作为一个变量被RUN_OPTIONS应用,作为sysinit的第一个位置参数,最终被CATALINA_OPTS变量引用,作为一个启动参数注入到tomcat里面  【详情参考: 通过Dockerfile编写tomcat镜像】
  6. 通过docker 默认的桥接网络类型启动镜像,docker run -e 指定参数会覆盖在Dockerfile里面定义好的变量【详情参考:Docker run 命令的使用方法】

] # vim commom.sh


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1#!/bin/sh
2project=${1}
3pro=${project//_/-}
4pname=${2}
5RUN_PORT=${4}
6DEBUG_PORT=${5}
7version="registry-vpc.cn-shenzhen.aliyuncs.com/moguyun-pre/env-b:${3}"
8JVM_OPTION="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${DEBUG_PORT}
9-Duser.timezone=GMT+08 -Dfile.encoding=UTF-8  -server -Xms256m  -Xmx1024m "
10
11echo "/docker/tomcat/logs/$project"
12echo "JVM_OPTION=$JVM_OPTION"
13echo "a1=$1"
14echo "a2=$2"
15echo "a3=$3"
16echo "a4=$4"
17echo "a5=$5"
18
19
20docker stop $pname && docker rm $pname
21docker run -d --name $pname  \
22  -v /opt/image_data:/image_data:rw \
23  -v /opt/image_data:/opt/image_data:rw \
24  -v /etc/hosts:/etc/hosts:ro \
25  -v /docker/tomcat/logs/$project:/opt/tomcat/logs:rw \
26  -p $RUN_PORT:8080 \
27  -p $DEBUG_PORT:$DEBUG_PORT \
28  -e RUN_OPTION="$JVM_OPTION" \
29  -e PNAME="$pname" \
30  $version
31
32sleep 35
33docker logs  --tail 400 $pname
34
  1. 依次传入 project,pname,镜像tag,宿主机上运行端口号,远程debug端口号

] # vim financial.sh


1
2
1/docker/k8s/common.run financial-shop-server financial $1 6207 9909
2

四、增量更新灰度应用

开发有时需要增量更新一个类,而不是全量,因为这个服务可能多个开发同时在开发,如果在上一个自由风格项目勾选了【isPreUpdate】,就会把容器里面的代码拷贝一份出来到 /opt/apps目录,然后通过winscp连接到灰度【1.226】机器,把增量类替换/opt/apps目录下的服务,然后docker cp拷贝到容器里面,重启容器。

4.1 增量更新

  • 通过winscp连到灰度机器【1.226】,然后把需要更新的类替换/opt/apps/目录下的指定服务类,下图就是已经把本地的类替换到灰度机器【1.226】的指定目录

DEVOPS-迁移SpringMVC应用到生产K8S集群

4.2 重启容器

通过执行restartFinancial就可以重新启动了,我们把restartFinancial定义成一个别名,这个别名执行restartServer.sh脚本

4.2.1 重启服务脚本

  1. $1为应用名称
  2. $2为容器名称

] # vim restartServer.sh


1
2
3
1#!/bin/bash
2docker cp /opt/apps/$1  $2:/opt/tomcat/webapps/ && docker restart $2 ; docker logs $2 -f --tail 400
3

4.2.2 定义别名

] # vim ~/.bashrc


1
2
1alias restartFinancial='sh -x /opt/apps/restartServer.sh financial-shop-server financial'
2

五、推送镜像到生产

推送镜像到远程仓库主要实现过程就是docker commit容器成镜像,然后docker push到生产镜像仓库。

5.1 参数化构建过程参数

  • 项目名称,包含容器名称和项目名称

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 镜像仓库名称,定义了生产镜像存放仓库

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 镜像仓库用户名/密码,用于登录远程镜像仓库

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 运行节点,灰度机器【1.226】

DEVOPS-迁移SpringMVC应用到生产K8S集群

5.2 执行时shell

  1. 把灰度重启之后不报错的容器打包成镜像

  2. 登录远程镜像仓库,由于本地登录远程镜像仓库的信息隔一段时间就会失效,所以每次push前都会docker login一次

  3. 推送镜像到远程仓库


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1currentTime=`date "+%Y%m%d%H%M"`
2echo $currentTime
3
4projectVals=(${projectVals//,/ })
5containerName=${projectVals[0]}
6projectName=${projectVals[1]}
7
8echo "cd /opt/apps"
9cd /opt/apps
10
11echo "tag=${projectName}_${currentTime}"
12tag=${projectName}_${currentTime}
13
14echo "docker commit -m "$containerName is commited at $currentTime" $containerName registry-vpc.cn-***.com/moguyun-prod/env-b:$tag"
15docker commit -m "$containerName is commited at $currentTime" $containerName registry-vpc.cn-***.com/moguyun-prod/env-b:$tag
16
17echo "docker login --username $dockerHubName --password $dockerHubPasswd registry-vpc.cn-shenzhen.aliyuncs.com"
18docker login --username $dockerHubName --password $dockerHubPasswd registry-vpc.cn-shenzhen.aliyuncs.com
19
20echo "docker push registry-vpc.cn-shenzhen.aliyuncs.com/moguyun-prod/$repository:$tag"
21docker push registry-vpc.cn-shenzhen.aliyuncs.com/moguyun-prod/$repository:$tag
22

六、更新生产镜像

6.1 使用指定镜像更新生产K8S

  • 进入K8S无状态应用编辑页面,选取指定的镜像名称和tag进行更新

DEVOPS-迁移SpringMVC应用到生产K8S集群DEVOPS-迁移SpringMVC应用到生产K8S集群

6.2 验证应用

  • 无状态应用app-financial管理的pod全部都跑起来了

DEVOPS-迁移SpringMVC应用到生产K8S集群

  • 查看financial应用的启动日志,由图中可以看出启动没有报错

DEVOPS-迁移SpringMVC应用到生产K8S集群

至此:已经完成了SpringMVC应用迁移到生产K8S集群的所有步骤

 

 

给TA打赏
共{{data.count}}人
人已打赏
安全运维

故障复盘的简洁框架-黄金三问

2021-9-30 19:18:23

安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索