容器化单体应用需分四步:第一步原样打包并封装完整运行环境;第二步剥离本地依赖,改用容器网络和服务发现;第三步双轨验证+灰度上线保障零中断;第四步补全健康检查、日志采集与资源限制等可观测性能力。

直接把遗留单体应用“塞进容器”不是目的,关键是让它在容器里跑得稳、配得准、管得住。不改架构也能落地,但必须避开硬编码、状态耦合和环境依赖这三大坑。
第一步:原样打包,先让服务跑起来
不做代码改造,只做环境封装。核心是把当前物理机或虚拟机上能稳定运行的完整状态(JDK/Python版本、配置路径、启动脚本、依赖库)一并打进镜像。
- 确认应用当前依赖清单:比如 Java 8 还是 17、MySQL 驱动版本、log4j2.xml 位置、是否调用本地 shell 脚本
- Dockerfile 用多阶段构建:编译用 maven:3.8-openjdk-17,运行用 eclipse-jetty:11-jre17-slim,体积可减少 60% 以上
- 所有地址类配置(DB_HOST、REDIS_URL)改用环境变量注入,写成 ${DB_HOST:-localhost},避免镜像内固化 IP
第二步:剥离外部依赖,用容器替代本地服务
原来连本机 MySQL 或 Redis 的方式,在容器里会失效。必须显式声明配套服务,并通过 Docker 网络通信。
在 Linux 上通过 Docker 运行 OpenClaw,并使用 Tailscale 实现远程访问。⚠️ 涉及 sudo、Docker、Tailscale和凭证挂载——请先查阅安全章节...
- 用 docker-compose.yml 定义 db、cache、mq 等服务,应用容器通过服务名(如 db:3306)访问,不写 127.0.0.1
- 把 application.properties 或 web.xml 中的配置抽出来,用 env_file 加载,不同环境只需换 .env 文件
- 日志目录、上传文件目录等挂载为 volume(如 - ./conf:/app/conf),方便运维热更新,也避免容器重启丢配置
第三步:双轨验证+灰度上线,业务零中断
容器版不是“另一个环境”,而是生产环境的延伸。上线前必须验证行为一致,切换时保留回滚能力。
- 在测试环境用相同数据集比对:HTTP 响应时间、5xx 错误率、定时任务执行结果、文件读写权限是否正常
- 灰度阶段用 Nginx 或 API 网关分流 5% 流量到容器实例,重点盯住线程池初始化、数据库连接池、本地缓存加载等易出问题环节
- 保留原有部署包和备份机制至少一个迭代周期;回滚只需 docker-compose down + 启动旧脚本,镜像+yaml 就是回滚包
第四步:补上可观测性基础能力
容器化不是终点,而是运维升级的起点。没有健康检查、日志采集和资源限制,就只是换个方式“黑盒运行”。
- 暴露 /actuator/health 或自定义健康端点,Dockerfile 加 HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/health || exit 1
- 强制日志输出到 stdout/stderr,禁用 log4j 的 RollingFileAppender;由宿主机 Filebeat 或 Loki 统一采集
- docker run 或 compose 中设置资源限制:mem_limit: 1g, cpus: '1.5',防止单个容器吃光整机资源
不复杂但容易忽略。真正卡住迁移进度的,往往不是技术难度,而是没提前理清配置来源、没验证文件权限、没处理好本地状态——这些细节,比写 Dockerfile 更决定成败。

















