背景:
现在K8s容器化是技术趋势,很多部署在虚拟机上的服务都要迁移到容器化。而迁移到容器化的步骤是要先对服务代码进行容器化改造,之后发布再将生产环境流量从虚拟机切到容器化。 这里以Java服务为例,说下容器化改造需要的准备和流程
配置改动
Java服务接入容器化,需要准备Dockerfile文件,启动脚本,配置文件迁移到Nacos
- Dockerfile文件:
使用Maven作为项目构建工具
js
FROM xxxx/maven:3.8.5-openjdk-17 AS builder
# 设置工作目录
COPY . /app
WORKDIR /app
# 构建项目编译命令
RUN mvn -B -U -f pom.xml -s ./settings.xml clean install -e -Dmaven.test.skip=true -P trait-boot-jar
# 使用 JDK 17 作为运行时镜像
FROM xxxx/openjdk:17
# 设置工作目录
WORKDIR /java
# 从 Maven 镜像中拷贝构建好的 JAR 文件
COPY --from=builder /app/scripts/*.sh ./scripts/
# 这里根据实际生成的制品名称来进行修改拷贝
COPY --from=builder /app/application/target/application-*.jar ./application.jar
COPY --from=builder /app/application/src/main/resources/log4j2.xml ./config/
# 服务端口
EXPOSE 8080
# 监控指标端口
EXPOSE 8082
# 设置容器启动命令
CMD ["/bin/sh", "./scripts/start.sh"]
使用Gradle作为项目构建工具
js
FROM gradle:8.4.0-jdk17 as builder
WORKDIR /app
COPY . .
RUN gradle clean bootJar
FROM xxx/openjdk:17
WORKDIR /java
RUN mkdir config \
&& mkdir scripts \
&& groupadd -g 5000 java \
&& useradd -d /java -m -u 5000 -g 5000 -s /bin/sh java \
&& chown -R java:java /java
COPY --from=builder /app/application/build/libs/application-*.jar ./application.jar
COPY --from=builder /app/application/src/main/resources/log4j2.xml ./config/
COPY --from=builder /app/scripts/start.sh ./scripts/
CMD ["/bin/sh", "./scripts/start.sh"]
EXPOSE 8080
- 启动脚本 start.sh
js
#!/bin/sh
java ${APP_OPTS} \
-Dspring.cloud.nacos.config.enabled=true \
-Dspring.cloud.nacos.config.server-addr=${NACOS_HOST}:${NACOS_PORT} \
-Dspring.cloud.nacos.config.namespace=${NACOS_TENANT} \
-Dspring.config.import[0]=nacos:${NACOS_DATA_ID} \
-Dspring.cloud.nacos.config.file-extension=yaml \
-Dspring.cloud.nacos.config.username=${NACOS_USERNAME} \
-Dspring.cloud.nacos.config.password=${NACOS_PASSWORD} \
-Dfile.encoding=UTF-8\
-server \
${JAVA_OPTS} -jar application.jar
- 容器内环境变量配置
js
- name: ENV_NAME
value: "prod"
- name: JAVA_OPTS
value: "-XX:InitialRAMPercentage=70.0
-XX:MaxRAMPercentage=70.0
-XX:+UseZGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/java"
- name: APP_OPTS
value: "-Dreactor.netty.ioWorkerCount=50
-Dreactor.netty.pool.leasingStrategy=lifo
-Dreactor.netty.pool.maxIdleTime=1000"
js
NACOS_DATA_ID: xxx
NACOS_HOST: xxx
NACOS_PORT: xxx
NACOS_USERNAME: xxx
NACOS_PASSWORD: xxx
NACOS_TENANT: xxx
- 配置文件从本地的application-{env}.yaml迁移到Nacos,定义好NameSpace,Group和Data Id。
预估资源配置
容器化改造需要先预估所需要的生产资源,比如多少核的CPU还有多少G内存,还有副本多少,这涉及到容器的HPA策略,生产一般是横向扩容,比如给定2C4G的资源,副本pod最小2个,最大5个。副本数量会根据生产负载进行扩容
生产迁移
当服务在生产容器化启动成功时,还需要做最后的流量切换,让API网关把流量转发从虚拟机服务转发到容器化服务,因为容器化服务提供出来的域名地址与虚拟机服务的IP地址是不一样。如图所示:
相比于前面配置改造,这个迁移过程更应该注重流程的重要性。这是比较大的变更动作,作为开发,需要提前同步API网关同事,测试同事,运维同事要做这个变更,协调好各位职能的时间来完成这个变更动作。
-
在服务日志方面,需要配置好日志采集监控,做好接口日志打印,有请求日志后就可以知道已经有流量进来了
-
在监控方面,需要事先在Grafana上配置服务接口监控,还要对虚拟机的接口监控和容器化的接口监控进行对比,在网关流量切换时可以看到虚拟机的接口监控请求逐渐减少,容器化的接口监控请求逐渐增加,这才是符合预期的变化。常见的接口监控配置有以下两种:
- Top5接口QPS监控:展示QPS最高的前5个接口
jstopk(5, sum(rate(http_server_requests_seconds_count[1m])) by (method, uri))
- P95接口监控:展示所有接口的P95响应时间,是个统计值
jshistogram_quantile(0.95, sum by(method, uri, le) (rate(http_server_requests_seconds_bucket{status="200"}[5m])))
-
在网关流量切换方面,不能全部切换,可以先切20%的流量到容器化服务,然后观察一段时间,没问题的话就可以切50%,最后100%全部切换。
-
在测试方面,需要让测试同事回归,确保切换后的服务功能没问题,但可能需要在流量全部切换过去后才能测试,因为不确定测试的账户是否已经切换到新服务。
-
FailOver,在网关流量切换过程中如果有问题,需要把流量重新切换到虚拟机服务。就算切换成功后,原有的虚拟机服务也不能直接下掉,需要保持一段时间,方便后续发现问题可以切换回去,相当于是一个备份副本,这是一种FailOver机制。