# 云原生 · K8s :传统业务迁移的步骤详解与部署案例
传统业务系统迁移到 Kubernetes 集群的完整指南第一部分迁移的理论步骤“云原生”化流程将传统业务系统迁移到 Kubernetes 集群本质上是将应用逐步“云原生”化的过程。核心思路是先将应用“容器化”再将其“编排”到 K8s 集群中。以下是标准步骤1.1 将应用封装进容器镜像构建应用容器化是迁移的第一步需要设计并规划好 Docker 镜像的构建方案。由于 Docker 镜像具有分层特性建议按以下层次构建分层构建有利于复用和加速构建操作系统层制作公司常用的系统版本如 Rocky Linux、Ubuntu可在官方基础镜像上添加自己需要的软件包如网络工具、vim 等。运行环境层在操作系统层之上打包业务常用的运行环境例如JDK 7 / JDK 8 / JDK 11JDK 8 Tomcat 8 / Tomcat 9Python 3 环境Nginx PHP-FPM 等这些可作为通用基础镜像模板供不同项目使用。应用层在通用运行环境的基础上根据具体应用进行调整如添加特定依赖、配置文件最后将编译好的代码JAR/WAR 或源码放入镜像中。1.2 将容器放入 Pod 中应用容器化后就需要考虑如何在Pod中运行。Pod 是 Kubernetes 管理的最小单元Kubernetes 不直接管理容器而是管理 Pod一个 Pod 可以包含一个或多个容器。需要决策单容器 Pod还是多容器 Pod例如主容器 辅助容器如日志收集 sidecar为 Pod 设置资源限制CPU/内存 requests 和 limits。配置健康检查livenessProbe 和 readinessProbe。确定是否需要数据持久化挂载 Volume。1.3 使用控制器 Controllers 管理 Pod单一 Pod 如果出现故障会影响业务连续性因此需要多副本类似传统集群。Kubernetes 提供了多种Controller需根据应用类型选择合适的控制器只需在 Pod 模板上封装对应配置即可控制器用途Deployment封装了 Pod 的副本管理、滚动更新、回滚、扩缩容等功能适用于无状态应用最常用。DaemonSet保证集群中每个 Node 上有且只有一个 Pod 在运行常用于节点监控、日志收集等。StatefulSet为有状态应用提供稳定的网络标识如主机名和有序部署/扩缩容适用于数据库、消息队列等。Job运行一次性任务如批处理任务完成后 Pod 自动退出。CronJob运行定时任务类似 Linux crontab。1.4 使用 Service 管理 Pod 访问使用 Deployment 通过多副本保证了 Pod 的高可用和横向扩展此时需要负载均衡将流量分发到多个 Pod。KubernetesService就是实现此功能的资源对象它为 Pod 提供稳定的访问入口ClusterIP。Service 的负载均衡实现方式支持两种模式iptables和ipvs后者性能更高。Service 类型主要有ClusterIP默认仅集群内部访问。NodePort在每个 Node 上开放一个端口供外部访问。LoadBalancer对接云厂商的负载均衡器。1.5 使用 Ingress 提供外部访问七层路由集群内部可以直接使用 Service 名称DNS 名进行通信但外部访问集群内部服务时由于网络隔离通常需要通过 NodePort 或 LoadBalancer 暴露端口。但这些属于四层负载均衡TCP/UDP若要实现七层HTTP/HTTPS路由如按域名、路径转发Kubernetes 提供了Ingress资源。Ingress 需要配合Ingress Controller如 Nginx Ingress Controller、Traefik才能生效。注意Ingress Controller 是独立组件不包含在 kube-controller-manager 中需要单独部署。1.6 使用 PV / PVC 管理持久化数据容器中的存储是临时的Pod 重启后数据会丢失。对于需要持久化数据的应用如数据库、文件上传必须使用外部存储方案。Kubernetes 通过PersistentVolumePV和PersistentVolumeClaimPVC抽象存储资源PV是集群中的存储资源由管理员预先创建或动态供给。PVC是用户对存储的请求声明Pod 通过 PVC 挂载卷。这样应用无需关心底层存储实现NFS、Ceph、云存储等。也结合存储类实现自动划分PV1.7 使用 ConfigMap 管理应用配置文件在 DevOps 实践中强调代码与配置分离。Kubernetes 提供了ConfigMap和Secret用于管理配置信息环境变量、配置文件等ConfigMap存储非敏感配置如应用参数。Secret存储敏感信息如密码、Token。可以从文件、目录或字面值创建 ConfigMap/Secret然后在 Pod 中通过环境变量或挂载卷的方式使用。1.8 日志收集为确保日志能够集中管理建议应用将日志输出到标准输出stdout和标准错误stderr这样 Kubernetes 默认日志机制如kubectl logs能够捕获。然后可集成 EFKElasticsearch Fluentd Kibana或 ELKElasticsearch Logstash Kibana等日志系统统一收集、存储和展示。1.9 监控告警集群和应用的状态监控至关重要。一般集成Prometheus采集指标配合Grafana进行可视化展示并设置告警规则通过 AlertManager。可以监控 Pod 资源使用、应用性能、集群健康等。1.10 关于有状态应用如数据库的迁移策略数据库等有状态服务迁移到 K8s 较为复杂涉及数据一致性、主从同步、备份恢复等。一个常见的策略是在迁移初期将数据库保留在 K8s 集群外部如使用云数据库或自建物理机让应用先通过 Service 访问外部数据库使用 ExternalName 或 Endpoints。待应用稳定运行后再评估是否使用StatefulSetPersistentVolume将其也容器化。第二部分服务部署与迁移实战案例以下案例逐步展示如何将不同类型的应用部署到 Kubernetes 集群包括 WordPressMySQL WordPress、LNMP 环境NginxPHP-FPM MySQL、Spring Boot 应用和 Tomcat 应用。前置条件已搭建好 Kubernetes 集群并配置了 NFS 存储类storageClassName: nfs关于存储类的创建可前往KubernetesK8s笔记Day06查看或替换为其他 StorageClass。所有 YAML 文件需在 Master 节点或任意可访问集群的机器上执行kubectl apply -f file。案例 1LAMP 架构部署 WordPress使用 MySQL 官方镜像 WordPress 镜像本案例通过 MySQL 与 WordPress 官方镜像部署持久化的博客网站使用 Deployment PVC 保存数据并通过 NodePort 对外访问。这个案例把应用运行所需的参数比如数据库地址、账号密码从代码内部“抽”出来放到代码外部比如环境变量里去设置是配置外部化的典范利用公共镜像做到了“即插即用”。如果业务本身遵循云原生设计或官方已提供支持迁移就是改几个变量的事这也是迁移收益最高、风险最低的方式1.1 部署 MySQL创建wp-mysql.yaml文件包含 Service、PVC 和 Deployment需要提前将mysql:5.7镜像上传到hd2hd3[roothd1 wp]# vim wp-mysql.yaml#创建serviceapiVersion:v1kind:Servicemetadata:name:mysql-svcspec:type:ClusterIPports:-port:3306targetPort:3306selector:app:wp-mysql---#创建数据库需要的存储----pvcapiVersion:v1kind:PersistentVolumeClaimmetadata:name:mysql-pvclabels:app:wp-mysqlspec:accessModes:-ReadWriteManystorageClassName:nfsresources:requests:storage:2Gi---#创建控制器apiVersion:apps/v1kind:Deploymentmetadata:name:wp-mysqlspec:selector:matchLabels:app:wp-mysqlreplicas:1template:metadata:labels:app:wp-mysqlspec:containers:-name:wp-mysqlimage:docker.io/mysql:5.7imagePullPolicy:IfNotPresentports:-containerPort:3306env:-name:MYSQL_ROOT_PASSWORDvalue:123456-name:MYSQL_DATABASEvalue:wordpress-name:MYSQL_USERvalue:wordpress-name:MYSQL_PASSWORDvalue:wordpressvolumeMounts:-name:mysqlmountPath:/var/lib/mysqlvolumes:-name:mysqlpersistentVolumeClaim:claimName:mysql-pvc说明Servicemysql-svc仅供集群内部访问ClusterIP。PVCmysql-pvc申请 2Gi 存储访问模式为ReadWriteManyNFS 支持。Deployment 仅 1 个副本环境变量注入数据库初始配置。1.2 部署 WordPress创建wp-wordpress.yaml文件[roothd1 wp]# vim wp-wordpress.yaml#创建wordpress的serviceapiVersion:v1kind:Servicemetadata:name:wordpressspec:type:NodePortports:-port:80nodePort:30001selector:app:wordpress---#创建wordpress的存储pvcapiVersion:v1kind:PersistentVolumeClaimmetadata:name:wordpress-pvclabels:app:wordpressspec:accessModes:-ReadWriteManystorageClassName:nfsresources:requests:storage:2Gi---#创建wordpress业务系统以及控制器apiVersion:apps/v1kind:Deploymentmetadata:name:wp-wordpressspec:selector:matchLabels:app:wordpressreplicas:1template:metadata:labels:app:wordpressspec:containers:-name:wp-wordpressimage:wordpress:6.2.1-apacheimagePullPolicy:IfNotPresentports:-containerPort:80env:-name:WORDPRESS_DB_HOSTvalue:mysql-svc# 通过 Service 名称访问 MySQL-name:WORDPRESS_DB_USERvalue:wordpress-name:WORDPRESS_DB_PASSWORDvalue:wordpressvolumeMounts:-name:wordpressmountPath:/var/www/html# 挂载 WordPress 程序文件可持久化volumes:-name:wordpresspersistentVolumeClaim:claimName:wordpress-pvc说明Service 类型为NodePort对外暴露端口30001。WordPress 通过环境变量WORDPRESS_DB_HOST指向 MySQL 的 Service 名称集群内 DNS 解析。PVC 挂载/var/www/html以保存上传的插件、主题等。1.3 初始化和测试依次执行kubectl apply-fwp-mysql.yaml kubectl apply-fwp-wordpress.yaml等待 Pod 就绪后打开浏览器访问http://任意NodeIP:30001进入 WordPress 安装界面如图 1。填写站点信息、管理员账户点击“安装”。登录后即可看到博客后台如图 2、图 3。注意若使用 NFS 作为 PV 供给需确保 NFS 服务正常运行且 PVC 绑定成功。案例 2LNMP 架构Nginx PHP-FPM MySQL运行 WordPress本案例更贴近传统 PHP 项目部署方式业务组件NginxPHP和 MySQL 跑在容器里但 PHP 业务源码WordPress 代码 放在容器外通过 PVC 挂载进容器。这个案例是最保守的“平移”策略适合应急过渡2.1 部署 Nginx PHP-FPM创建lnmp-nginx-php.yaml[roothd1 lnmp]# vim lnmp-nginx-php.yamlapiVersion:v1kind:Servicemetadata:name:nginx-php-svclabels:app:lnmp-nginx-phpspec:type:NodePortports:-port:80nodePort:30001selector:app:lnmp-nginx-php---apiVersion:v1kind:PersistentVolumeClaimmetadata:name:lnmp-web-pvclabels:app:wordpressspec:accessModes:-ReadWriteManystorageClassName:nfsresources:requests:storage:2Gi---apiVersion:apps/v1kind:Deploymentmetadata:name:lnmp-nginx-phpspec:selector:matchLabels:app:lnmp-nginx-phpreplicas:1template:metadata:labels:app:lnmp-nginx-phpspec:containers:-name:lnmp-nginximage:richarvey/nginx-php-fpm# 集成了 Nginx PHP-FPM 的镜像imagePullPolicy:IfNotPresentports:-containerPort:80-containerPort:9000volumeMounts:-name:nginx-datamountPath:/var/www/html/# 网站根目录volumes:-name:nginx-datapersistentVolumeClaim:claimName:lnmp-web-pvc镜像richarvey/nginx-php-fpm是一个由第三方开发者 richarvey 在 Docker Hub 上构建并维护的公开镜像可以直接使用docker pull 拉取。GitHub 源码仓库地址https://github.com/richarvey/nginx-php-fpm2.2 部署 MySQL创建lnmp-mysql.yaml[roothd1 lnmp]# vim lnmp-mysql.yamlapiVersion:v1kind:Servicemetadata:name:lnmp-mysql-svcspec:type:ClusterIPports:-port:3306targetPort:3306selector:app:lnmp-mysql---apiVersion:v1kind:PersistentVolumeClaimmetadata:name:lnmp-mysql-pvclabels:app:lnmp-mysqlspec:accessModes:-ReadWriteManystorageClassName:nfsresources:requests:storage:2Gi---apiVersion:apps/v1kind:Deploymentmetadata:name:lnmp-mysqlspec:selector:matchLabels:app:lnmp-mysqlreplicas:1template:metadata:labels:app:lnmp-mysqlspec:containers:-name:lnmp-mysqlimage:docker.io/mysql:5.7imagePullPolicy:IfNotPresentports:-containerPort:3306env:-name:MYSQL_ROOT_PASSWORDvalue:123456-name:MYSQL_DATABASEvalue:wordpress-name:MYSQL_USERvalue:wordpress-name:MYSQL_PASSWORDvalue:wordpressvolumeMounts:-name:mysqlmountPath:/var/lib/mysqlvolumes:-name:mysqlpersistentVolumeClaim:claimName:lnmp-mysql-pvc2.3 部署 WordPress 源码WordPress 源码需要提前放入 PVC 挂载的目录中NFS 共享目录。下载 WordPress 安装包官网https://cn.wordpress.org[roothd1 ~]# tar xf wordpress-6.7.1-zh_CN.tar.gz[roothd1 ~]# cd wordpress/# 将解压后的所有内容移动到 PVC 对应的 NFS 目录路径根据实际 PVC 挂载点调整[roothd1 wordpress]# mv ./* /data/nfs_pro/default-lnmp-web-pvc-pvc-33ebc2d2-9e4f-42d9-9110-7519f9d5b890/说明该路径是 NFS 服务器上为lnmp-web-pvc分配的实际存储路径可通过kubectl get pv查看。依次部署kubectl apply-flnmp-nginx-php.yaml kubectl apply-flnmp-mysql.yaml访问http://NodeIP:30001即可看到 WordPress 安装界面如下图。点击“现在就开始”填写数据库连接信息数据库主机为lnmp-mysql-svc用户名wordpress密码wordpress数据库名wordpress如下图所示提交后继续安装设置站点标题、管理员账号等最终登录后台案例 3部署 Spring Boot Web 应用Spring Boot 是目前 Java 后端开发的主流框架。本案例演示如何将 Spring Boot 应用打包为 Docker 镜像并部署到 K8s 中。本案例实现了完全无状态、声明式管理、随时弹性伸缩的云原生最佳实践。3.1 准备 Spring Boot 项目已有 Spring Boot 项目springboot-web-demo编译生成 JAR 包springboot-web-demo-1.0-SNAPSHOT.jar。3.2 编写 Dockerfile在当前目录创建dockerfile注意文件名通常为DockerfileFROM openjdk:8-jdk COPY target/springboot-web-demo-1.0-SNAPSHOT.jar /springboot-web.jar ENTRYPOINT [java, -jar, /springboot-web.jar]对于spring boot的web项目它把整个项目包括内置的 Tomcat 代码打包成了一个可执行文件你只需要把它放在容器的工作目录如 /app下用 java -jar 启动即可构建镜像并推送到私有仓库示例仓库地址为www.zutuanxue.com/library/spring-web:v1dockerbuild-tspring-web:v1.dockertag spring-web:v1 www.zutuanxue.com/library/spring-web:v1dockerpush www.zutuanxue.com/library/spring-web:v1# 若需推送3.3 编写 Kubernetes 部署文件spring-web.yamlcat spring-web.yamlapiVersion:v1kind:Servicemetadata:name:spring-web-svcspec:type:NodePortports:-port:8181targetPort:8080nodePort:30600selector:app:spring-web---apiVersion:apps/v1kind:Deploymentmetadata:name:spring-webspec:selector:matchLabels:app:spring-webreplicas:3template:metadata:labels:app:spring-webspec:containers:-name:spring-webimage:www.zutuanxue.com/library/spring-web:v1imagePullPolicy:IfNotPresentports:-containerPort:8080说明Service 将外部端口30600映射到 Pod 的8080端口应用默认端口。Deployment 副本数为 3实现高可用。未配置持久化存储无状态应用。3.4 部署并测试kubectl apply-fspring-web.yaml kubectl get pod kubectl get svc打开浏览器访问http://NodeIP:30600/hello?namezutuanxue假设应用有/hello接口。即可看到响应。案例 4构建 Tomcat 应用部署 WAR 包对于传统的 Java Web 应用WAR 包需要部署到 Tomcat 容器中。本案例是中间状态容器化程度加深代码进镜像但启动命令仍依赖容器脚本4.1 准备 WAR 包并编写 Dockerfile 构建镜像假设已有 WAR 文件jspdemo_war.war编写 DockerfileFROM tomcat COPY jspdemo_war.war /usr/local/tomcat/webapps/jspdemo_war.war ENTRYPOINT [catalina.sh,run]将当前目录下的 jspdemo_war.war 文件永久性地写入到镜像文件系统中的 /usr/local/tomcat/webapps/ 目录下Java Web 项目打 WAR 包要放在 Tomcat 的专用部署目录/usr/local/tomcat/webapps/构建并推送镜像示例仓库www.zutuanxue.com/library/tomcat-java:v1dockerbuild-ttomcat-java:v1.dockertag tomcat-java:v1 www.zutuanxue.com/library/tomcat-java:v1dockerpush www.zutuanxue.com/library/tomcat-java:v1将构建的镜像上传至私人仓库然后在yaml文件中引用这是生产环境最主流的方式将我们的私人仓库变成唯一的可信源4.2 编写 Kubernetes 部署文件tomcat-java.yamlcat tomcat-java.yamlapiVersion:v1kind:Servicemetadata:name:tomcat-java-svcspec:type:NodePortports:-port:8282targetPort:8080nodePort:30700selector:app:tomcat-java---apiVersion:apps/v1kind:Deploymentmetadata:name:tomcat-javaspec:selector:matchLabels:app:tomcat-javareplicas:3template:metadata:labels:app:tomcat-javaspec:containers:-name:tomcat-javaimage:www.zutuanxue.com/library/tomcat-java:v1imagePullPolicy:IfNotPresentports:-containerPort:80804.3 部署并测试kubectl apply-ftomcat-java.yaml访问http://NodeIP:30700/jspdemo_war项目名输入用户名lisi和对应密码示例中说明“用户名与密码为lisi”具体密码需根据实际应用设定可能为默认或配置此处按文档描述。第三部分内容补充3.1 命名空间Namespace生产环境中通常按环境开发、测试、生产或团队划分命名空间以隔离资源。在所有 YAML 文件中可指定metadata.namespace或通过kubectl apply -f file -n namespace。3.2 滚动更新与回滚Deployment 支持滚动更新默认策略更新镜像时只需修改 YAML 中的image标签重新apply即可。若更新失败可使用kubectl rollout undo deployment/name回滚到上一版本。3.3 配置管理进阶除了环境变量还可将 ConfigMap 挂载为配置文件如application.properties。例如volumes:-name:configconfigMap:name:app-configvolumeMounts:-name:configmountPath:/config3.4 健康检查配置示例在 Pod 的containers中添加livenessProbe:httpGet:path:/healthport:8080initialDelaySeconds:30periodSeconds:10readinessProbe:httpGet:path:/readyport:8080initialDelaySeconds:5periodSeconds:53.5 资源限制示例resources:requests:memory:256Micpu:250mlimits:memory:512Micpu:500m总结将传统业务系统迁移到 Kubernetes 是一个系统化工程需要依次完成容器化→Pod 设计→控制器选择→服务暴露→存储与配置管理→可观测性等环节。对于不同类型应用无状态 Web、有状态数据库、PHP、JavaKubernetes 均提供了灵活的编排能力。实际落地时应结合业务特点选择合适的控制器和存储方案并逐步推进尤其对于数据库等关键组件可采取“先外后内”的保守策略。