记一次 A 云厂商服务器 ->B 云厂商服务器的应用迁移总结

本贴最后更新于 1800 天前,其中的信息可能已经东海扬尘

--ALL 注意:以下信息已脱敏

应用评估

基础框架

SpringCloud:Finchley.RELEASE
SpringBoot:2.1.0.RELEASE

集成软件(以下按照"不同类型"区分)

1)jdk:1.8.0_172

相关总结
https://blog.llyweb.com/articles/2016/05/16/1578161292443.html
2)
maven:3.5.2
nexus:2.12.0
jenkins:2.171
gitlab:8.14.0
相关总结
https://blog.llyweb.com/articles/2016/05/10/578161732665.html
3)
node:0.8.14
nginx:1.10.2
4)
apollo:1.3.0
sentinel:1.6.3
5)
elasticsearch:6.1.4
redis:3.2.12
mongodb:4.0.13
mysql:5.7
相关总结
https://blog.llyweb.com/articles/2016/12/11/1578156355793.html
https://blog.llyweb.com/articles/2016/12/11/1578156421076.html
https://blog.llyweb.com/articles/2016/12/11/1578156738950.html
https://blog.llyweb.com/articles/2016/12/11/1578156806878.html
https://blog.llyweb.com/articles/2017/06/15/1578154900950.html
https://blog.llyweb.com/articles/2017/06/12/1578155301294.html
https://blog.llyweb.com/articles/2017/06/08/1578155455409.html
6)
marathon:1.6.322
mesos:1.8.0
7)
elasticjob:2.1.5
rocketmq:4.3
zookeeper:3.4.11

应用类型划分

大体概览

微服务(网关、注册中心、配置中心、监控中心、各种后台应用)
RPC(thrift/grpc)
定时任务(近实时增量、非实时批量、全量)

应用细分

微服务网关 zuul:

1)确认网关业务类型,有哪些独立的网关等。
2)确认微服务应用数,即经过微服务网关层的所有应用数量。
3)确认网关是否可插拔接入版本切换模块、限流模块、熔断降级模块、黑白名单模块、动态配置模块等。
4)评估网关的可用性、扩展性。
5)评估网关的流量承载能力,并根据第三条、四条中的内容作出相应的配置。
6)后续需逐步把各个网关融合到一起,并集成上述不同的模块,根据业务适配不同的应用类型。

微服务注册中心:eureka

1)确认注册 eureka 的微服务数量及服务类型(网关、配置中心、监控中心、各种后台应用)。
2)评估 eureka 的可用性、扩展性。
3)后续根据业务发展情况是否设置统一注册中心平台。

微服务注册中心(备份):eureka

1)根据上述注册中心评估备份注册中心的内容。
2)作为注册中心备份。
3)作为域名切换、整体环境切换等。(比如在同一台机器运行配置不同、但应用相同的后台,直接切换注册中心即可)

微服务配置中心 apollo:

1)确认集成 apollo 的微服务类型。
2)确认集成 apollo 的微服务应用数量。
3)根据技术部组织架构及应用类型划分 apollo 的私有配置和公共配置。
4)根据第三条开通人员配置权限(开发环境、应用配置)。
5)后续加入不同的环境配置,且能做到在 apollo 中切换环境标识就能直接切换应用当前的环境配置,且做到应用不用重启就可把配置全部刷新到应用内存中。

微服务监控中心 sentinel、springbootadmin 等:

1)确认监控的应用类型及应用数量。
2)确认可接入的应用类型。
3)评估不同应用调整合适的配置参数。
4)后续 sentinel 要充分融入到网关层作为公共处理,并下放到各个应用层做二次处理。

微服务后台应用

1)确认后台应用类型(只读/修改)。
2)确认后台的部署方式(手工传包与手工脚本运行、自动传包与配置脚本与自动调用运行)。
3)确认应用的接口数量
4)确认应用与内网其他应用的调用方式、调用关系、调用层级。
5)确认应用是否可接入的模块(模块:配置中心、监控中心。是否可接入,是否可逐步接入。)
6)确认应用与第三方接入的接口(支付宝、微信。是否需要在第三方平台修改 IP、证书配置、加密文件配置等。)
7)评估应用使用的技术栈与适配的业务类型(是否适配、调整方案、调整时间、测试时间、是否可逐步修改、应用影响范围、应用规划)
8)评估应用中不同接口的入口/出口数据量与单位时间内的接口承载能力。
9)后续在开发微服务的过程中需细化业务,把持业务粒度,提高性能。
RPC(thrift/grpc)
1)确认客户端和服务端的应用接入人员、业务名称、业务数量。
2)确认是否需要提供新的 jar 包供客户端依赖。
3)由于不同环境需依赖不同的包,依赖性太强且开发人员结构多变,最简单的解决方案,后续融入微服务应用中。

定时任务

1)确认定时任务类型:近实时增量、非实时批量、全量,是否存在向用户发送通知等类似功能。
2)确认定时任务的触发机制、触发时长、业务类型、数据回滚方法。
3)在被迁移云服务器提前设置对应的触发机制,并设置数据源为可读不可写状态。
4)导出被迁移云服务器的数据到迁移云服务器中,设置为只读状态,并关闭增量开关。
5)在迁移云服务中处理所有的全量,并检验数据的正确性,开启增量开关并开启所有的触发机制。
6)后续加入分布式事物、分布式调度中心,开发分布式定时任务开发框架。

操作系统部分

操作系统版本号

cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

操作系统内核

uname -r
3.10.0-957.21.3.el7.x86_64

ansible 批量系统配置工具

#可参考(下述大部分内容都可以通过 ansible 来完成)
https://blog.llyweb.com/articles/2019/09/24/1578148496700.html

服务器名称

统一名称加 IP 后缀组成(统一 eureka 名称、各个其他配置名称)
/etc/hosts
10.10.0.10 ms10
10.10.0.11 redis_server_xx
10.10.0.12 mysql_server_xx

10.10.0.13 mongo_server_xx

设置文件描述符

echo '* - nofile 65535 ' >>/etc/security/limits.conf

设置 yum 数据源为国内阿里云数据源

rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

设置服务器时间同步

/usr/sbin/ntpdate ntp1.aliyun.com > /dev/null 2>&1

建立统一用户,并设置提权部分权限
useradd yonghu

echo "balabala"|passwd --stdin yonghu

磁盘统一挂载目录

/XX/YY

建立统一路径结构

项目部署目录、日志输出目录、文件落地目录。权限统一为普通用户。
mkdir -pv /XX/YY/application/{project,logs,files}
对应下述内容
/XX/YY/application/project
/XX/YY/application/logs
/XX/YY/application/files

二进制安装软件存放目录、安装软件目录。root 统一安装。
mkdir -pv /XX/YY/software/${workspace,install}
对应下述内容
/XX/YY/software/workspace
/XX/YY/software/install

项目脚本目录(普通用户调用)、软件及系统脚本目录(root 用户调用)
mkdir -pv /XX/YY/scripts/${application,software}
对应下述内容
/XX/YY/scripts/application
/XX/YY/scripts/software

常用命令

yum -y install pcre pcre-devel openssl openssl-devel openssh-server gcc+ unzip zip lrzsz tree telnet-server telnet.* gcc gcc-c++ gcc-g77 make cmake bison ncurses-devel autoconf automake zlib* fiex* libxml* libmcrypt* libtool-ltdl-devel* libaio libaio-devel bzr libtool ncurses5-devel imake libxml2-devel expat-devel ncurses-devel fontconfig mkfontscale httpd-tools

防火墙端口(永久)开放

firewall-cmd --zone=public --add-port= 自定义端口/tcp --permanent

限制 IP 访问

包括阿里云局域网中各个服务器的访问限制(比如 SSH、scp 等)、
公司内网对阿里云局域网的直接访问限制(比如代码服务器)、
登录阿里云服务器通过跳板机跳转访问(不同用户对机器的访问列表不同)

服务器申请

IP 尽可能网段一致,跨网段并不友好。

调用流程

方式 1(应用 1):公网 SLB(域名)-> 私网 nginx-> 后台应用->mysql 中间件/redis/mongodb
方式 2、1(微服务应用 2):公网 SLB(域名)-> 私网 nginx-> 后台应用-> 微服务网关-> 微服务后台应用->mysql 中间件/redis/mongodb/ES 集群
方式 2、2(微服务应用 2 迭代):公网 SLB(域名)-> 私网 nginx-> 微服务网关-> 微服务后台应用->mysql 中间件/redis/mongodb/ES 集群
方式 3(微服务应用 3):公网 SLB(域名)-> 私网 nginx(前台静态文件)-> 微服务网关-> 微服务后台应用->mysql 中间件/redis
方式 4、1(静态文件服务器 1):公网 SLB(域名)-> 私网 nginx(静态文件)
方式 4、2(静态文件服务器 2):公网 SLB(域名)-> 私网 nginx-> 微服务网关-> 微服务上传下载应用-> 阿里云 OSS 静态文件服务器

软件集成

Jenkins

1)基本流程(基于公司内网):Jenkins 执行 build 操作--拉取 GitLab 分支代码--maven 编译--集成 nexus 私服 jar--打包上传到阿里云内网
2)根据公司业务架构和应用类型给相关人员设置相应的权限。
3)项目编译只保留 7 天且编译 3 次的编译文件
3)项目编译设置 tag
4)项目构建前,创建项目运行目录、停止项目进程、删除上次编译的项目文件等操作。
5)项目构建时,执行 maven 编译操作并不执行项目测试操作。
6)项目构建后,执行解压包并执行不同项目中的启动脚本。
7)启动脚本中包括 JVM 参数、运行的 Java 文件、业务日志及 GC 日志输出目录。

项目优化

1)日志部分

基于 logback,评估不同的业务系统在已有几个月和将来几个月的日志输出量修改不同应用输出的每个文件的日志大小为 512MB,且保留时间为 2 天(定时任务)-7 天(业务系统和第三方配置)-14 天(业务系统)。

2)JVM

根据不同应用业务类型、业务访问量、代码质量、相关硬件资源,JVM 参数根据这几个基本影响因素主要调整了以下的参数(以下都是参考值)

第一部分

-Xms1024m -Xmx4096m
堆内存大小,2~4G 均可

-Xss256k
在堆之外,线程占用栈内存,默认每条线程为 1M(以前是 256K)。存放方法调用出参入参的栈,局部变量,标量替换后掉局部变量等,有人喜欢把它设回 256k,节约内存并开更多线程,有人则会在遇到错误后把它再设大点,特别是有很深的 JSON 解析之类的递归调用时。

-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m
元数据大小
MetaspaceSize 默认 20.8M 左右(x86 下开启 c2 模式),主要是控制 metaspaceGC 发生的初始阈值,也是最小阈值,但是触发 metaspaceGC 的阈值是不断变化的,与之对比的主要是指 Klass Metaspace 与 NoKlass Metaspace 两块 committed 的内存和。
MaxMetaspaceSize 默认基本是无穷大,但是我还是建议大家设置这个参数,因为很可能会因为没有限制而导致 metaspace 被无止境使用(一般是内存泄漏)而被 OS Kill。这个参数会限制 metaspace(包括了 Klass Metaspace 以及 NoKlass Metaspace)被 committed 的内存大小,会保证 committed 的内存不会超过这个值,一旦超过就会触发 GC,
这里要注意和 MaxPermSize 的区别,MaxMetaspaceSize 并不会在 jvm 启动的时候分配一块这么大的内存出来,而 MaxPermSize 是会分配一块这么大的内存的。
https://www.jianshu.com/p/b448c21d2e71
http://lovestblog.cn/blog/2016/10/29/metaspace/

-XX:NewSize=2730m -XX:MaxNewSize=2730m
如果老生代里没多少长期对象的话,占 2/3 通常太多了
设置的低,GC 频率高,老年代长期对象相对少
设置的高,GC 频率低,老年代长期对象相对多
从低到高,逐渐降低 GC 的回收频率,所以在老年代

-XX:SurvivorRatio=8
新生代中每个存活区的大小,默认为 8,即 1/10 的新生代 1/(SurvivorRatio+2),有人喜欢设小点省点给新生代如 Cassandra,但要避免太小使得存活区放不下临时对象而被迫晋升到老生代,还是从 GC 日志里看实际情况了。

第二部分

-XX:+UseParNewGC
年轻代多线程收集,需手工开启

-XX:ParallelGCThreads=4
并行收集器的线程数,此值最好配置与处理器数目相等 同样适用于 CMS

-XX:MaxTenuringThreshold=9
这是改动效果最明显的一个参数了。对象在 Survivor 区最多熬过多少次 Young GC 后晋升到年老代,JDK8 里 CMS 默认是 6,其他如 G1 是 15。

-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
因为我们的监控系统会通过 JMX 监控内存达到 90% 的状况,所以设置让它 60% 就开始跑了,早点开始也能减少 Full GC 等意外情况(概念重申,这种主动的 CMS GC,和 JVM 的老生代、永久代、堆外内存完全不能分配内存了而强制 Full GC 是不同的概念)。为了让这个设置生效,还要设置-XX:+UseCMSInitiatingOccupancyOnly,否则 60%只被用来做开始的参考值,后面还是 JVM 自己算。

-XX:+CMSParallelRemarkEnabled
手动配置开启并行标记,节省年轻代标记时间,JDK1.6 以前不需要配置,默认开启

-XX:SoftRefLRUPolicyMSPerMB=0
SoftReference 的生命周期不会跨 GC 周期,能很快被回收掉

-XX:+CMSClassUnloadingEnabled
如果启用 CMSClassUnloadingEnabledGC ,也将扫描 PermGen,并删除不再使用的类。

-XX:+DisableExplicitGC
(-XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses)
https://www.iteye.com/blog/breezylee-2043056
(-XX:+DisableExplicitGC)
跟上面的第一个例子的-XX:+DisableExplicitGC 一样,这两个参数也是用来改变 System.gc()的默认行为用的;不同的 是这两个参数只能配合 CMS 使用(-XX:+UseConcMarkSweepGC),而且 System.gc()还是会触发 GC 的,只不过不是触发一个 完全 stop-the-world 的 full GC,而是一次并发 GC 周期。
CMS GC 周期中也会做 reference processing。所以如果用这两个参数的其中一个,而不是用-XX:+DisableExplicitGC 的话,就避开了由 full GC 带来的长 GC pause,同时 NIO direct memory 的 OOM 也不会那么容易发生。
教训是:如果你在使用 Oracle/Sun JDK 6,应用里有任何地方用了 direct memory,那么使用-XX:+DisableExplicitGC 要小心。如果用了该参数而且遇到 direct memory 的 OOM,可以尝试去掉该参数看是否能避开这种 OOM。如果担心 System.gc()调用造成 full GC 频繁,可以尝试下面提到 -XX:+ExplicitGCInvokesConcurrent 参数
-XX:+ScavengeBeforeFullGC
https://www.zhihu.com/question/48780091
https://blog.csdn.net/m0_37970699/article/details/102793577
指定 -XX:-ScavengeBeforeFullGC 就可以不在执行 full GC 的时候先执行一次 PS Scavenge。先 Young GC (Full GC (Allocation Failure)之前)。

-XX:+ExplicitGCInvokesConcurrent
参考-XX:+DisableExplicitGC 中的描述

-XX:-OmitStackTraceInFastThrow
https://www.jianshu.com/p/e87d166380eb
这是 HotSpot VM 专门针对异常做的一个优化,称为 fast throw,当一些异常在代码里某个特定位置被抛出很多次的话,HotSpot Server Compiler(C2)会用 fast throw 来优化这个抛出异常的地方,直接抛出一个事先分配好的、类型匹配的对象,这个对象的 message 和 stack trace 都被清空。可以明确:抛出这个异常非常快,不用额外分配内存,也不用爬栈。副作用:正好是需要知道哪里出问题的时候看不到 stack trace 了,不利于排查问题。如果遇到没有 stack trace 的问题,可以考虑通过 -XX:-OmitStackTraceInFastThrow 禁用该默认的优化
-Duser.timezone=Asia/Shanghai
-Dclient.encoding.override=UTF-8
-Dfile.encoding=UTF-8
-Djava.security.egd=file:/dev/./urandom"

第三部分(为了分析 GC 加入了 GC 日志的输出)

-XX:+PrintGCDetails
Java GC 日志可以通过 +PrintGCDetails 开启

-XX:+PrintGCDateStamps
用 PrintGCDateStamps 而不是 PrintGCTimeStamps,打印可读的日期而不是时间戳。GC 发生的时间信息。

-XX:+PrintGCApplicationConcurrentTime
打印应用程序的执行时间

-XX:+PrintHeapAtGC
每次 GC 前后打印堆信息

-XX:+UseGCLogFileRotation
不推荐用这个来记录 GC 日志

-XX:+HeapDumpOnOutOfMemoryError
导出内存溢出的堆信息(hprof 文件)

-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=5M
-Xloggc: $GC_LOG_DIR/gc.log
-XX:HeapDumpPath=$ HEAP_DUMP_PATH

第四部分

为了分析 JVM 使用了 arthas 工具(https://github.com/alibaba/arthas)
其他
https://blog.llyweb.com/articles/2019/12/20/1578147529730.html
https://blog.llyweb.com/articles/2019/12/20/1578147799845.html

3)springcloud 版本

迁移前后的操作系统版本不一样,问题如下,此问题通过调整内存参数并没有解决
org.apache.catalina.loader.WebappClassLoaderBase The Web application appears to have started a thread named[RxIoScheduler-1(Evitor)] but has failed to stop it.This is very likely to create a memory leak.
Stack trace of thread:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
java.util.concurrent.locks.AbstractQueuedSynchronizer $ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
java.util.concurrent.ScheduledThreadPoolExecutor$ DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
java.util.concurrent.ScheduledThreadPoolExecutor $DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
java.util.concurrent.ThreadPoolExecutor$ Worker.run(ThreadPoolExecutor.java:624)
java.lang.Thread.run(Thread.java:748)

解决方案(修改 pom.xml)
SpringBoot 版本从 2.0.3.RELEASE 改为 2.1.0.RELEASE
加入
spring-boot-autoconfigure 排除 gson
main 方法加入
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,GsonAutoConfiguration.class})

4)nginx

worker_processes 和 worker_connections 理解误差
worker_processes 为 nginx 要开启的进程数,可以根据机器 CPU 核数来设置,设置合适的值可以减少机器 IO 带来的影响。
个人由于把 worker_connections 的默认值 1024 修改到了 worker_processes 上,导致 nginx 启动了大量的线程,机器出现了假死的现象,应用出现了不能正常访问的现象(页面未正常打开等)。
worker_connections 为单个工作进程可以允许同时建立外部连接的数量

上述两个值应该根据系统文件描述符的最大值来设置
nginx 作为 http 服务器的时候:
max_clients(小于系统进程打开的文件总数) = worker_processes * worker_connections/2
nginx 作为反向代理服务器的时候:
max_clients(小于系统进程打开的文件总数) = worker_processes * worker_connections/4

总结

基于此次大规模迁移,除了上述描述的,下述做个基本总结

大概操作:

1)确认个人负责迁移的项目。
2)与网管、运维、DBA 评估服务器分配参数。
3)与项目负责人商议项目配置。
4)联系项目负责人获取迁移前/后的 MySQL、Redis 的配置,项目启动方式,与其他项目的调用关系,是否有第三方的主动/被动调用。
5)根据现有项目配置及云服务器的配置适当对项目调优。
6)对项目负责人讲解现有项目的调优及集成其他内容的方案。
7)整理迁移文档,主要为改动前后的基本信息:项目配置、IP、机器名称、实例个数、项目运行目录、日志日志输出目录、端口、访问路径、操作用户、GitLab、Jenkins 等。
8)合理规划服务器 IP,相应人员服务器权限,并合理规划服务器结构(系统初始化、目录结构、操作用户、定时任务、访问权限等)。

遇到的问题:

1)由于不同负责人迁移内容不一样,在一起申请 IP 时,部分网段弄错了,开通了不必要的网段访问。比如通过网管开通了网段的限制。
2)由于不可预知错误,导致修改了个别项目的框架版本。比如现在升级了 SpringBoot 版本。
3)迁移前部分考虑不周到,导致在 Web 层面以及多机部署运维层面浪费了时间,比如现在集成了 ansible 可通过脚本一键安装所有服务器。
4)迁移过程中发现部分项目的代码不规范。比如相关配置并没有统一管理,现在逐步集成了 apollo 分布式配置中心。
5)晚上迁移过程中由于微小的问题导致不必要的时长消耗。比如往 MyBatis 中传值不成功,直接修改 Java 方法参数传值即可,处理这种问题要果断。
6)涉及的第三方配置。比如微信小程序要设置证书、加密文件、应用部署服务器公网出口 IP,这些必要的配置要提前设置好,避免不能实时生效的问题。
7)涉及的域名切换问题,要提前询问到底哪些项目用哪些域名,一定要确认。比如小程序修改域名或域名对应的链接就需要微信审核 3-7 天,如果小程序给所有商户提供了模板,那么所有的商户都需要修改,会给公司造成没必要的损失。

技术演变:

每小节从上往下依次迭代

1)单实例应用-tomcat-域名
多实例应用-tomcat-nginx-域名
多实例应用-tomcat-多 nginx 实例-公网 SLB-域名
微服务多实例应用-微服务多实例网关-多 nginx 实例-公网 SLB-域名
2)
http
http->RPC
RPC-> 微服务
3)
MySQL 单节点
MySQL 主从
MySQL 主从-redis
MySQL 主从-redis 主从
MySQL 集群-redis 集群-mongodb 集群-elasticsearch 集群
4)
jdbc-spring(XML)
mybatis(XML)-spring(XML)
mybatis(XML+ 注解)-spring(XML+ 注解)
mybatis(XML+ 注解)-springboot(配置 + 注解)
mybatis(XML+ 注解)-springboot(配置 + 注解)-springcloud(配置 + 注解)

  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    946 引用 • 943 回帖
  • 迁移
    5 引用 • 10 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...