最近把公司的旧系统迁移到了云原生架构,同时做了全面的安全加固。
旧系统是传统的单体应用,部署在物理机上,用的是Tomcat + MySQL。系统跑了五六年,功能越来越多,代码越来越乱,安全漏洞也不少。每次升级都要停机维护,扩容也很麻烦。
老板决定上云原生,把系统容器化,部署到Kubernetes上。同时,借这次迁移的机会,做一次全面的安全加固。
我负责整个迁移的技术方案和实施。花了两个月时间,终于把系统安全地迁移到了云原生架构上。本文分享这次迁移的完整过程,包括迁移前的评估、容器化改造、K8s集群安全、镜像安全、网络安全、数据安全、运行时安全、迁移策略、踩坑记录等。
一、迁移前的评估
迁移的第一步,是评估旧系统的现状。
1. 系统梳理
先把旧系统梳理清楚:
- 有哪些应用?多少个服务?
- 用了什么技术栈?
- 依赖哪些中间件?(数据库、缓存、消息队列等)
- 有哪些定时任务?
- 有哪些外部接口?
- 数据量有多大?
我们的旧系统是一个单体Java应用,依赖MySQL、Redis、RabbitMQ,有十几个定时任务,有几个外部系统的接口。数据量不大,数据库大概50G。
2. 安全评估
然后做安全评估,找出旧系统的安全问题:
- 代码漏洞:用SonarQube扫描,发现了一些SQL注入、XSS的风险
- 依赖漏洞:用OWASP Dependency Check扫描,发现几个有漏洞的第三方库
- 配置问题:数据库密码明文写在配置文件里,Tomcat用root运行
- 网络问题:没有防火墙,端口直接暴露在公网
- 权限问题:数据库用户权限过大,有DROP权限
这些问题,在迁移的时候都要解决。
3. 迁移方案设计
根据评估结果,设计迁移方案:
- 应用容器化:把Java应用做成Docker镜像
- 编排:用Kubernetes部署和管理
- 数据库:保留MySQL,但部署在K8s上或用云数据库
- 缓存:用Redis Cluster
- 消息队列:用RabbitMQ或Kafka
- 网关:用Ingress或API网关
- 监控:Prometheus + Grafana
- 日志:EFK(Elasticsearch + Fluentd + Kibana)
二、容器化改造
接下来是容器化改造。
1. Dockerfile编写
给Java应用写Dockerfile:
# 多阶段构建
FROM maven:3.8-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=builder /app/target/app.jar .
# 非root用户运行
RUN useradd -u 10001 appuser
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]注意几点:
- 用多阶段构建,减小镜像体积
- 用slim版本的基础镜像,减小体积
- 用非root用户运行,提高安全性
- 不要把配置文件和密钥打包进镜像
2. 配置外置
旧系统的配置写在properties文件里,打包在jar包里。迁移后,配置要外置。
- 用K8s的ConfigMap存非敏感配置
- 用Secret存敏感配置(密码、密钥等)
- 应用启动时从环境变量或挂载文件读取配置
3. 健康检查
给应用加健康检查接口:
- 存活探针(livenessProbe):应用是否还活着
- 就绪探针(readinessProbe):应用是否准备好接收流量
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5三、K8s集群安全
K8s集群本身的安全很重要。
1. 集群安装安全
- 用kubeadm安装,启用RBAC
- 关闭匿名访问
- 启用审计日志
- 控制平面节点不跑业务Pod(用taint)
- etcd加密,定期备份
2. RBAC权限控制
用RBAC做细粒度的权限控制:
- 不同团队用不同的命名空间
- 每个命名空间有独立的ServiceAccount
- 只给必要的权限,遵循最小权限原则
- 不要给普通用户cluster-admin权限
3. Pod安全策略
用Pod Security Admission(PSA)限制Pod的安全配置:
- 不允许用root用户运行
- 不允许特权容器
- 不允许挂载宿主机目录
- 限制Linux capabilities
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted4. 网络策略
用NetworkPolicy限制Pod之间的网络访问:
- 默认拒绝所有入站流量
- 只允许必要的访问(比如前端Pod只能访问后端Pod的8080端口)
- 隔离不同环境(开发、测试、生产)
四、镜像安全
镜像安全是云原生安全的重要环节。
1. 基础镜像安全
- 用官方镜像或可信的基础镜像
- 用最小化的基础镜像(alpine、slim、distroless)
- 定期更新基础镜像,修复漏洞
- 不要用latest标签,用固定版本
2. 镜像扫描
用镜像扫描工具检查漏洞:
- Trivy:简单易用,适合CI/CD集成
- Clair:和Harbor集成
- Anchore:功能更全面
在CI/CD流水线中加入镜像扫描步骤,有高危漏洞的镜像不允许部署。
3. 镜像签名
用镜像签名验证镜像的来源和完整性:
- Docker Content Trust
- Cosign(Sigstore项目)
只允许运行经过签名的镜像,防止被篡改。
4. 私有镜像仓库
用私有镜像仓库(Harbor)管理镜像:
- 不直接从公网拉取镜像
- 镜像仓库做访问控制
- 镜像仓库开启漏洞扫描
- 定期清理无用镜像
五、网络安全
网络安全是云原生安全的重点。
1. Ingress安全
- 用HTTPS,配置TLS证书
- 开启HSTS
- 配置WAF(Web应用防火墙)
- 限制请求大小和速率
- 隐藏服务器版本信息
2. 服务网格
如果服务比较多,可以用服务网格(Istio、Linkerd):
- 服务间通信加密(mTLS)
- 细粒度的流量控制
- 统一的遥测数据
- 故障注入和熔断
3. 出口流量控制
控制Pod的出口流量:
- 不允许Pod直接访问公网
- 通过代理或网关访问外部服务
- 用Egress Gateway管理出口流量
4. DDoS防护
- 用云服务商的DDoS防护
- 配置限流(Rate Limiting)
- 用CDN缓存静态资源
六、数据安全
数据安全是重中之重。
1. 数据库安全
- 数据库不暴露在公网,只在集群内部访问
- 数据库用强密码,定期更换
- 数据库用户最小权限,只给必要的权限
- 开启数据库审计日志
- 定期备份,备份数据加密
- 数据库传输加密(SSL/TLS)
2. 敏感数据保护
- 密码、密钥等敏感信息存在Secret中,不要写在代码或配置里
- 敏感数据加密存储(比如用户密码用bcrypt加密)
- 个人信息脱敏展示(手机号、身份证号等)
- 数据传输加密(HTTPS、mTLS)
3. 数据备份和恢复
- 定期备份数据库和重要数据
- 备份数据存放在异地
- 定期做恢复演练,确保备份可用
- 制定数据恢复的RTO和RPO
七、运行时安全
部署之后,运行时的安全也不能忽视。
1. 容器运行时安全
- 用Falco监控容器运行时的异常行为
- 检测容器内的异常进程、文件访问、网络连接
- 发现异常自动告警或阻断
2. 日志审计
- 收集所有容器的日志
- 收集K8s的审计日志
- 日志集中存储,便于分析
- 定期审计日志,发现异常行为
3. 漏洞管理
- 定期扫描镜像和运行中的容器
- 及时修复高危漏洞
- 建立漏洞管理流程:发现-评估-修复-验证
4. 应急响应
- 制定安全事件应急响应预案
- 定期做应急演练
- 安全事件发生时,能快速定位、隔离、修复
八、迁移策略
说说具体的迁移策略。
1. 蓝绿部署
用蓝绿部署的方式迁移:
- 旧系统(蓝)继续运行
- 新系统(绿)部署在K8s上
- 先把流量切一部分到新系统
- 验证没问题后,全部切到新系统
- 旧系统保留一段时间,确认没问题后下线
2. 数据迁移
数据迁移是最麻烦的部分:
- 全量迁移:先把旧数据全量导入新数据库
- 增量同步:用Canal或Debezium同步增量数据
- 切换:停机几分钟,确认数据同步完成后,切换到新系统
我们的数据量不大,全量迁移用了1小时,增量同步了3天,切换的时候停机了10分钟。
3. 回滚方案
迁移一定要有回滚方案:
- 如果新系统出问题,能快速切回旧系统
- 数据双向同步,确保回滚时数据不丢
- 回滚操作提前演练
4. 灰度发布
迁移完成后,新功能用灰度发布:
- 先给内部用户用
- 再给一小部分外部用户用
- 最后全量发布
九、踩坑记录
说说迁移过程中踩的坑。
坑一:时区问题
容器里的时区是UTC,和国内差8小时。
解决:Dockerfile里设置时区,或者挂载宿主机的localtime。
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone坑二:文件权限问题
容器用非root用户运行,挂载的目录没有写权限。
解决:在Dockerfile里创建用户,并给目录授权。或者用initContainer修改目录权限。
坑三:JVM内存问题
容器里的JVM,默认用的是宿主机的内存,不是容器的内存限制。
解决:设置JVM参数,或者用Java 10+的容器感知功能(UseContainerSupport)。
-XX:MaxRAMPercentage=75.0坑四:数据库连接池问题
Pod重启时,数据库连接没有正常释放,导致连接数耗尽。
解决:配置连接池的最大生存时间,定期回收连接。
坑五:日志文件过大
容器日志写到stdout,但旧应用还在写文件日志,导致容器磁盘满。
解决:把应用日志也改成输出到stdout,用EFK收集。或者限制日志文件大小,定期轮转。
十、迁移后的效果
说说迁移后的效果。
1. 安全性提升
- 代码漏洞修复了
- 依赖漏洞更新了
- 网络隔离了,端口不暴露在公网
- 权限最小化了
- 有了完整的监控和审计
2. 可用性提升
- 滚动更新,不再停机维护
- 自动扩缩容,高峰期自动加节点
- 健康检查,异常Pod自动重启
- 多副本,单节点故障不影响服务
3. 效率提升
- 部署更快了,从几小时降到几分钟
- 扩容更方便了,从几天降到几分钟
- 环境一致性好了,不再有"我本地能跑"的问题
4. 成本变化
- 服务器利用率提高了,从30%升到60%
- 但云原生的运维复杂度增加了,需要学习K8s
- 总体来说,长期成本是降低的
十一、写在最后
云原生迁移,不是简单地把应用放进容器里,而是一次全面的架构升级和安全加固。
从旧系统到新系统,涉及应用改造、集群搭建、安全加固、数据迁移、监控运维等方方面面。每一个环节都不能马虎,尤其是安全,云原生环境下,安全问题的影响面更大。
这次迁移,我们花了两个月时间,踩了不少坑,但最终顺利完成了。迁移后的系统,更安全、更稳定、更高效。
2022年了,云原生已经成为主流。越来越多的企业在做云原生迁移。但迁移不是目的,提升业务价值才是目的。在迁移的过程中,不要忘了安全,安全是1,其他都是0。
最后,用一句话总结:"云原生迁移,安全先行。先评估,再设计,小步走,勤验证,才能安全平稳地完成迁移。"
愿大家的云原生迁移之路,都能顺利平安。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录