在千万级 QPS 的架构演进中,很多团队会遇到一个很现实的问题:业务代码明明已经拆成了微服务,但部署和交付却还是老一套。开发环境用 VMware 开一台虚机,测试环境手工装 JDK,生产环境上线要预留整整一个下午。真正把整个架构压垮的,往往不是接口性能,而是环境差异、扩容速度、资源利用率这几座大山。
这篇文章要谈的,就是千万 QPS 架构系列第 218 讲的核心主题:从 VM 到容器。我会先对比虚拟机和容器在架构层面的本质区别,再讲清楚为什么高并发场景下容器能成为更优解,然后给出业务系统容器化改造的具体路径,包括镜像制作、Kubernetes 部署、镜像安全和容器安全。读完你会得到一个明确判断:容器化不只是换一种部署方式,它改变的是整个架构演进和团队协作的底层逻辑。
1. 先搞清楚:千万 QPS 架构为什么绕不开容器化
先看一个真实场景。假设你负责一个日活千万的内容平台,核心链路包括用户服务、内容服务、推荐服务、支付服务。系统早期是单体应用,一台物理机部署完所有代码,数据库单独跑一台机器,Redis 和消息队列各占一台。那时候一台机器挂了,运维手工切流量,业务影响范围可控。
随着业务增长,单体应用拆成几十个微服务,问题开始暴露。每个微服务依赖不同的 JDK 版本、不同版本的第三方组件、不同的配置文件。测试环境用的是 CentOS 7.4,生产环境却是 CentOS 7.6,一个小版本差异导致字节码兼容问题,线上直接报错。扩容更是痛苦,新环境要从装机开始,装系统、配网络、装中间件、部署代码,一套流程下来快也要半小时,慢则半天。
这就是典型的高并发架构演进困境。当 QPS 从十万量级走到千万量级,系统瓶颈已经不只是应用代码本身,而是整个基础设施的弹性能力。虚拟机能解决资源隔离问题,但虚拟机的粒度太大、启动太慢、资源占用太高。容器化则把隔离和调度的粒度从操作系统级别压缩到进程级别,让一套物理机资源可以被大量服务共享。
千万 QPS 架构的核心不是某一台机器能扛多少流量,而是整个集群能够在流量高峰到来之前快速扩容,在流量回落后迅速缩容。容器化恰恰提供了这种能力。所以在实际的架构方案里,容器化不是可选项,而是高并发系统的必选项。
2. 从物理机到虚拟机:虚拟化解决了什么
虚拟机不是新概念。最早的大规模部署,应用直接跑在物理机上,这种方式的问题在于资源边界固定:一台物理机被某个应用独占,即使应用只用了 10% 的 CPU,其他应用也无法复用。为了提升资源利用率,虚拟化技术开始普及。
虚拟机的核心是 Hypervisor,也就是虚拟机监视器。它运行在物理硬件和虚拟机之间,负责把物理资源抽象成多份虚拟资源。VMware 的 ESXi 属于 Type 1 虚拟化,直接跑在裸金属上;VMware Workstation 和 Oracle VM VirtualBox 属于 Type 2 虚拟化,跑在宿主操作系统之上。每一台虚拟机里都有一套完整的操作系统,包括内核、系统库、文件系统、应用进程。
从资源利用角度看,虚拟机相比物理机是巨大进步。一台 32 核 128GB 的物理服务器,可以切成多台 4 核 16GB 的虚拟机,不同团队可以共用同一台物理机,团队之间通过虚拟机做隔离。但这种隔离付出了相当大的资源代价:每台虚拟机都要运行一个完整操作系统,操作系统本身要占用内存和 CPU 资源;启动一台虚拟机需要几十秒甚至几分钟,因为内核要初始化、系统服务要启动、应用要加载。
虚拟机适合什么场景?如果你需要运行多个不同操作系统的环境,比如一台物理机上既要跑 Windows 又要跑 Linux,虚拟机是最好的选择;如果你需要严格的内核级隔离,虚拟机也更有优势。但在高并发微服务架构里,虚拟机的痛点非常明显:资源浪费严重,100 个微服务就意味着 100 个操作系统实例在空转;启动速度太慢,无法支撑秒级弹性扩容。
3. 容器到底是什么:从 VM 到容器的关键跳跃
容器和虚拟机的核心区别在于:虚拟机虚拟的是硬件,容器虚拟的是操作系统。容器与宿主机共享同一个操作系统内核,但它通过 Linux 内核的 Namespace 和 Cgroups 技术实现了进程级别的隔离和资源限制。
Namespace 的作用是让容器里的进程看到独立的系统视图。每个容器可以拥有自己的 PID、网络、文件系统、用户等命名空间。比如 PID Namespace 使容器内进程的 PID 从 1 开始,它看不到宿主机上其他进程;Mount Namespace 让容器拥有自己的根文件系统,容器里执行的ls /看到的是镜像里的目录,不是宿主机的根目录。
Cgroups 的作用是限制和统计资源使用。通过 Cgroups,可以给每个容器设置 CPU 配额、内存上限、磁盘 IO 和网络带宽。如果没有 Cgroups,一个容器的死循环会拖垮整台物理机;有了 Cgroups,容器最多只能用分配给它的那部分资源。
很多人会问:容器既然与宿主机共享内核,那隔离性是不是比虚拟机差?答案是肯定的。虚拟机有独立内核,容器共用内核,如果宿主机内核出现漏洞,容器都可能受影响。这也是为什么容器安全被单独强调,后面我会专门讲镜像安全和容器安全。
容器的启动为什么快?因为容器不需要启动操作系统,它只是创建几个 Namespace 和 Cgroups,然后启动进程。一个 JAR 包镜像,从docker run到应用接受请求,正常场景下只需要几秒到十几秒。这种启动速度,是千万 QPS 架构实现弹性扩容的基础。
4. VM 与容器的深度对比:一张表看清差异
为了帮助你快速理解 VM 和容器的差异,我整理了一张关键对比表。它适用于架构评审和技术选型阶段。
| 维度 | VM(虚拟机) | 容器 |
|---|---|---|
| 隔离级别 | 内核级隔离,每台 VM 有独立内核 | 进程级隔离,共享宿主机内核 |
| 启动速度 | 几十秒到几分钟 | 毫秒级到秒级 |
| 资源占用 | 每台 VM 需完整操作系统,内存占用高 | 只包含应用和依赖库,镜像体积小 |
| 密度 | 一台物理机通常运行几台到十几台 VM | 一台物理机可运行几十到上百个容器 |
| 内核依赖 | 不依赖宿主机内核版本 | 依赖宿主机内核兼容性 |
| 安全边界 | 独立内核,隔离更强 | 共享内核,隔离相对较弱 |
| 迁移性 | 模板导出导入,通常需要停机 | 镜像拉取即运行,环境一致性高 |
| 适用场景 | 多操作系统需求、强隔离要求 | 微服务、弹性扩容、持续交付 |
| 典型工具 | VMware、VirtualBox、KVM | Docker、containerd、Podman |
从这张表可以看出,容器并不是完全替代虚拟机。在需要严格多租户隔离的公有云场景,反而要使用裸金属服务器或者虚拟机来承载容器,也就是容器套在虚拟机里跑。但从业务系统容器化改造的角度看,应用层服务的部署方式会逐步从虚拟机迁移到容器。
还需要注意一个混淆点:很多人把 Docker 等同于容器。Docker 只是容器的一种实现,容器运行时的标准化接口是 OCI(Open Container Initiative)。常见的容器运行时包括 containerd、CRI-O、Docker Engine。在 Kubernetes 集群里,早期普遍使用 Docker 作为运行时,现在更多推荐 containerd,因为它在 Kubernetes 集群内更轻量、更稳定。
5. 业务系统怎么容器化改造:从 VM 迁移到容器的完整路径
业务系统容器化改造是实际操作中最难的一环,也是很多开发团队最先踩坑的地方。很多人以为把项目写一个 Dockerfile,构建成镜像,推到镜像仓库,然后用docker run跑起来就算容器化。这个理解太表面了。在千万 QPS 架构下,容器化改造要考虑到应用状态、日志、配置、网络、存储、健康检查和弹性伸缩等多方面问题。
这里以一个典型的 Java 微服务为例,完整走一遍容器化改造流程。
5.1 梳理应用依赖与环境差异
第一步,明确应用运行需要什么。Java 应用需要 JRE,通常会用到环境变量、配置文件、日志目录。如果你之前的配置是写在application.properties里,里面包含数据库地址、Redis 地址、各种中间件账号密码,说明配置和代码没有分离。容器化之后,这些配置应该通过环境变量或者 ConfigMap 注入,否则镜像一旦被推送,配置就写死在里面了,换个环境还得重新构建镜像。
建议做法是:将所有环境差异项抽取为环境变量,在启动脚本里替换,或者使用 Spring Cloud Config、Apollo 这类配置中心统一管理。容器化改造的另一个价值就在于此,它倒逼你把配置管理规范起来。
5.2 编写 Dockerfile
以 Spring Boot 项目为例,一个基础但可用的 Dockerfile 如下:
# 文件路径:Dockerfile # 第一阶段:构建 FROM maven:3.8-openjdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 第二阶段:运行 FROM openjdk:11-jre-slim WORKDIR /app COPY --from=builder /app/target/*.jar app.jar ENV JAVA_OPTS="" EXPOSE 8080 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]这段 Dockerfile 使用了多阶段构建。第一阶段用 Maven 镜像编译项目,第二阶段用精简 JRE 镜像运行。这样最终镜像不包含 Maven、编译器和源代码,体积会小很多。
需要强调的是,上面用的是openjdk:11-jre-slim作为基础镜像。但在生产环境中,更推荐使用 Eclipse Temurin、Liberica 或 Adoptium 提供的镜像,因为它们维护更规范,安全更新更及时。基础镜像选择是容器化安全的重要一环,后面会展开。
5.3 构建镜像并推送私有仓库
构建镜像的命令很简单:
docker build -t registry.example.com/shop/user-service:v1.0.0 .最关键的是registry.example.com/shop/user-service:v1.0.0这个镜像命名,它决定了镜像的归属。生产环境不要使用 Docker Hub 公共仓库,建议搭建私有镜像仓库,企业内部使用 Harbor 是比较普遍的做法。Harbor 不仅支持私有镜像存储,还内置了镜像扫描、签名和权限管理,对生产环境更友好。
构建完成后推送到仓库:
docker push registry.example.com/shop/user-service:v1.0.05.4 处理日志与持久化数据
容器的一个重要特性是临时性。容器删除后,容器内写入的文件也会丢失。如果应用把日志写到容器里的/logs目录,容器重启或重新调度后日志就没了。生产环境应该把日志输出到标准输出,由容器运行时统一收集,或者挂载到宿主机目录,再由 Filebeat 等工具采集到 Elasticsearch。
对于需要持久化的数据,比如上传文件、数据库数据,必须使用持久化卷。在 Kubernetes 中对应的是 PersistentVolumeClaim,它可以绑定到云硬盘、Ceph、NFS 等存储后端。需要注意的是,如果应用本来就是无状态的,那么不需要持久化卷;容器化改造的目标之一,就是把有状态的部分尽量抽离出来,交给数据库、Redis 这类专用组件,让应用服务保持无状态。
5.5 JVM 参数与容器资源限制
Java 应用在容器里运行,有一个非常经典的坑:JVM 默认的堆内存设置。早期的 JVM 不能正确识别 Cgroups 限制,它会根据宿主机物理内存大小来计算默认堆内存。比如宿主机有 128GB 内存,而容器限制为 2GB,JVM 却按宿主机内存设置堆大小,结果容器被 Cgroups 杀掉,应用不停 OOM。
解决办法有两个:第一个,使用支持容器内存感知的 JDK 版本,Java 10 之后 JVM 默认能识别 Cgroups 限制;第二个,显式设置 JVM 参数,比如-Xmx512m -Xms512m,并配合容器内存限额。
推荐使用JAVA_OPTS环境变量在 Dockerfile 中声明,例如:
ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m"同时设置 Kubernetes 的资源限额,确保容器内存不超过限制。
还有更精细的方案:JDK 10 以后的版本支持-XX:MaxRAMPercentage=75.0这类参数,让 JVM 根据容器配额自动计算堆大小。但实际项目中,固定堆大小更容易把控,特别是做压测和容量规划时,数值确定,结果可复现。
5.6 健康检查与优雅下线
容器编排平台会通过健康检查来判断容器是否存活、是否就绪。传统虚拟机部署时,运维通过端口探测判断应用是否正常;容器环境下,这个机制要升级为 HTTP 接口探针。
Spring Boot 自带 Actuator,可以暴露健康检查端点。Kubernetes 的配置一般如下:
# 文件路径:user-service-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: user-service namespace: production spec: replicas: 6 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: registry.example.com/shop/user-service:v1.0.0 ports: - containerPort: 8080 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 20 periodSeconds: 10 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 60 periodSeconds: 30 resources: requests: cpu: 500m memory: 1Gi limits: cpu: "1" memory: 2Gi这里有两个探针:readinessProbe判断容器是否还能接收流量,如果接口返回 5xx,Kubernetes 就把容器从 Service 的 Endpoints 里摘除;livenessProbe判断容器是否需要重启,如果接口连续失败,Kubernetes 会杀掉容器并重新创建。
在实际项目中,readiness 和 liveness 不要共用同一个接口,因为它们的语义不同。readiness 应该反映应用的依赖是否就绪,比如数据库连接、Redis 连接;liveness 只反映应用进程是否还活着。如果把两者混在一起,Redis 短暂故障导致 readiness 失败,应用会被摘流量,但 liveness 不应该因为这个原因重启应用。
5.7 Deployment 与 Service 编排
当镜像构建完毕且探针配置妥当后,下一步就是通过 Kubernetes Deployment 来运维容器。
Kubernetes 的调度核心是 Pod。Pod 是 Kubernetes 的最小调度单元,一个 Pod 里可以有一个或多个容器。Deployment 负责管理一组相同的 Pod,它保证了期望的副本数量、支持滚动更新、支持回滚。
Service 负责提供稳定的访问入口。Pod 的 IP 是动态变化的,Service 通过标签选择器找到一组 Pod,并提供一个稳定的 ClusterIP 和 DNS 名称,其他服务可以通过user-service.production.svc.cluster.local:8080来访问它。这在微服务架构里非常关键,服务间调用不再依赖固定 IP。
6. 从 VM 到容器的架构演进:编排与调度
如果只是把虚拟机里的应用搬到容器里,那容器化的价值只发挥了一小半。真正让容器在高并发场景下发挥威力的,是容器编排平台。Kubernetes 是目前事实上的标准容器编排平台。
千万 QPS 架构中,服务数量往往成百上千。没有编排平台,你手动管理几百个容器的创建、删除、扩容、缩容,人力完全支撑不住。Kubernetes 做的事情其实可以总结为:声明期望状态,不断调谐当前状态。
以弹性扩容为例,在 Kubernetes 里只需要设置一个 HPA(HorizontalPodAutoscaler),当 CPU 使用率超过阈值时自动扩容。配置如下:
# 文件路径:user-service-hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: user-service-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: user-service minReplicas: 6 maxReplicas: 30 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60这个 HPA 的意思是:user-service这个 Deployment 最少保持 6 个副本,当 CPU 平均使用率超过 60% 时自动扩到更多副本,最多 30 个。流量高峰过去后,Kubernetes 会自动收缩副本数。
这种能力直接决定了架构的 QPS 上限。在传统 VM 环境中,扩容要经历申请资源、安装系统、部署应用、接入负载均衡这一整套流程,通常是小时级别;容器环境下,HPA 触发的扩容是分钟级别,而且扩容出的 Pod 与原有 Pod 完全一致,因为它们是同一个镜像创建出来的。这也解释了为什么容器是千万 QPS 架构中弹性能力的基础设施级保障。
7. 镜像安全与容器安全:千万 QPS 架构的底线
在千万 QPS 架构中,容器带来的安全和虚拟机有一个重要差异:共享内核。攻击者一旦通过应用漏洞进入容器,再结合内核漏洞提权,可能影响宿主机上的其他容器。所以容器安全不能当成普通运维问题,它必须贯穿镜像构建、镜像仓库、容器运行、编排调度的整个生命周期。
7.1 镜像安全
镜像安全是整个容器安全的第一道门,也是很多人最容易忽视的地方。
第一,不依赖踩坑版本镜像。很多公共仓库的镜像已经存在被弃用的标签或者已知漏洞,基础镜像要选官方维护的版本。
第二,尽量使用精简基础镜像,减少攻击面。以 Java 应用为例,基础镜像可以用eclipse-temurin:11-jre,而不是ubuntu加手动安装 JDK。镜像里不需要 shell 工具链、包管理器甚至不需要curl,所有这些都会扩大潜在的攻击面。
第三,用非 root 用户运行容器。默认情况下,容器内进程以 root 用户运行,如果应用被攻破,攻击者就拥有了容器内最高权限。应该在 Dockerfile 中显式指定运行用户:
RUN groupadd -r app && useradd -r -g app app USER app第四,定期扫描镜像漏洞。Harbor 内置了 Trivy 或 Clair,可以扫描镜像的 CVE 漏洞。CI 阶段也应该接入漏洞扫描,高危漏洞直接阻断发布。
7.2 容器运行安全
运行时安全可以从三个层面下功夫。
第一,Kubernetes 的 RBAC 权限控制。不要给普通服务分配 cluster-admin 权限。Pod 的 ServiceAccount 权限也应该最小化。
第二,限制容器能力。容器默认继承了 Linux capabilities,但你可以显式删除不需要的 capabilities:
securityContext: capabilities: drop: ["ALL"] add: ["NET_BIND_SERVICE"]第三,使用镜像签名验证。从仓库拉取的镜像要经过签名校验,防止镜像被篡改。Kubernetes 在拉取镜像时会校验签名,无法通过验证的镜像不会运行。
另外还有一个实践:不要在容器内保存敏感信息。数据库密码、云密钥、证书等应该通过 Kubernetes Secret 或外部密钥管理服务注入,避免写死在镜像层或环境变量里。镜像一旦被推送到仓库,任何人都可以查看镜像的构建历史和层内容,明文密钥等于直接泄露。
8. 从 VM 到容器改造的常见问题与排查思路
在实际业务系统容器化改造过程中,遇到问题的概率非常高。我把高频问题整理成一个排查表,你可以直接收藏使用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 容器启动后立即退出 | 应用启动失败或启动命令错误 | 查看容器日志docker logs <container> | 检查启动参数、依赖服务是否可用 |
| Java 应用 OOM,容器被杀 | JVM 堆内存超过容器限额 | 查看dmesg或 Kubernetes 事件的 OOMKilled | 调整 JVM 堆参数,匹配 Cgroups 限制 |
| 容器内时间不准 | 镜像未挂载时间同步配置 | 执行date查看容器时间 | 挂载/etc/localtime,或运行时区设置 |
| 日志文件越来越大 | 应用直接写日志文件且未轮转 | 查看df -h,定位日志目录 | 改为标准输出,或配置日志轮转 |
| Pod 一直处于 Pending 状态 | 资源不足或调度约束不满足 | kubectl describe pod <pod> | 检查集群节点资源和亲和性配置 |
| 容器启动慢 | 基础镜像过大或启动脚本阻塞 | 拉取镜像耗时,查看应用启动日志 | 优化镜像体积,检查初始化脚本 |
| 外网访问异常 | 网络策略限制或 Service 类型不对 | 检查 NetworkPolicy 和 Service YAML | 调整网络策略,或将 Service 改为 LoadBalancer |
| 配置不生效 | 环境变量未注入或 ConfigMap 未更新 | 检查 Pod 环境变量和 ConfigMap | 修改后滚动重启 Pod |
排查容器问题,要善用两类命令。第一类kubectl describe pod,它可以查看 Pod 的事件,包括调度失败原因、镜像拉取失败原因、探针失败信息;第二类kubectl logs,直接查看容器输出日志。大多数容器问题都能从这两处找到线索。
还有一个容易忽略的点:镜像拉取策略。Kubernetes 的imagePullPolicy默认是IfNotPresent,如果本地存在同名的旧镜像,就不会重新拉取。在测试环境联调时,经常出现代码改了但镜像没更新,Pod 还在用旧镜像。建议测试环境的imagePullPolicy设置为Always。
9. 容器化的最佳实践与工程建议
综合前面所有内容,这里给出几条容器化改造的工程建议,这些经验来自实际微服务架构落地过程,适用于中大型业务系统。
第一,先梳理无状态服务,再动有状态服务。容器化的理想对象是无状态服务,也就是服务本身不保存数据,所有数据都存放在外部存储。如果一个服务的内存里有会话数据,或者本地磁盘有临时文件,应该先改造这些点,把会话交给 Redis,把文件交给对象存储或共享文件系统,然后再容器化。有状态组件如数据库、Redis 集群,建议在云上使用托管服务,或者由专业的中间件团队加容器方案管理,不要一开始就搞 Kubernetes 上的高可用数据库。
第二,配置与代码彻底分离。容器镜像应该是不可变的,同一个镜像在不同环境下只通过配置区分。不要在构建时把生产环境配置写进去。环境变量、ConfigMap、配置中心,三种方式按场景选择。环境变量适合简单少量配置,ConfigMap 适合 Kubernetes 内部的批量配置,配置中心适合多团队共享的动态配置。
第三,镜像版本管理要规范。镜像 tag 必须包含版本信息,禁止使用latest作为生产镜像 tag。latest的问题是,它不固定,今天拉取的镜像和明天拉取的镜像可能不是同一个。版本回滚时需要精确到具体版本,所以 tag 建议格式服务名-版本号-构建序号,例如user-service-v1.2.0-b20240618。
第四,建立多环境交付流水线。容器化工程上要打通 CI/CD。开发提交代码后,CI 自动编译、单测、构建镜像、漏洞扫描;CD 自动部署到测试环境,测试通过后逐级发布到预发和生产环境。这里建议引入 Argo Rollouts 做金丝雀发布,部分流量切到新版本,验证通过后全量发布,失败则自动回滚。千万 QPS 的系统,任何一次全量发布都是巨大风险,金丝雀发布能力是企业级架构的标配。
第五,资源限额必须显式设置。生产环境中,所有容器都要配置 requests 和 limits。requests 用于调度决策,limits 防止容器超用资源拖垮节点。不设置 limits 的容器,在流量高峰期可能吃光整台机器内存,导致节点上的其他容器连环故障。这一点在容量规划和故障隔离中非常关键。
第六,基础设施能力建设要跟上。容器化不是把 Dockerfile 写好就完事,还需要服务发现、配置中心、日志采集、监控告警、链路追踪等配套能力。Kubernetes Service 解决了服务发现,后端还需要配套的日志系统(ELK/Loki)、监控系统(Prometheus + Grafana)、链路追踪系统(SkyWalking/Jaeger)。这些组件与 YAML 或 Agent 集成后才算完整的生产级容器化。
10. 总结与后续学习方向
从 VM 到容器,表面上变化的部署单位,从一台操作系统变成了一段进程;本质上变化的是资源利用率、交付速度、弹性能力和运维方式。千万 QPS 架构不能依赖手工搭建环境,因为手动的速度跟不上流量的变化。容器化让环境构建变得可编程,配合 Kubernetes 实现了声明式、自动化的基础设施管理,这才是高并发系统能够在流量高峰安全度过的底层原因。
对于正在做业务系统容器化改造的团队,建议按这个顺序推进:
- 从最简单的无状态服务开始,完成第一个镜像、第一份 Deployment 配置;
- 搭建私有镜像仓库,接入漏洞扫描和镜像签名;
- 逐步把配置迁移到 ConfigMap 和配置中心;
- 部署 Prometheus 监控,建立容器级别的告警体系;
- 引入 CI/CD 流水线,把构建、扫描、部署流程自动化;
- 最后再考虑 HPA 弹性伸缩、金丝雀发布等高级能力。
容器化落地的过程,也是开发团队对系统边界和组件依赖重新理解的过程。这个过程中没有完全相同的方案,但遵循最小化权限、不可变镜像、依赖外部化、显式资源限制这几个原则,大方向不会偏。
如果你正在做千万 QPS 架构的容量规划和基础设施升级,建议把容器化和 Kubernetes 排进近期学习计划。下一篇可以继续深入 Kubernetes 集群的 Ingress 网关与流量治理,或者聊聊容器环境下 JVM 调优的完整实践。