把 docker run 收成 Compose,并看清哪些参数会丢
端口、环境变量、卷、重启策略和网络怎样从 docker run 进入 compose.yaml,哪些标志不能靠机械转换保住,以及环境变量何时该变成 Kubernetes Secret。
· 6 分钟阅读
容器常常从一行命令开始。有人先 docker run 一个端口,再加环境变量,再加卷,再加 --restart unless-stopped,因为进程在午饭时退出了。这一行已经是一次部署,却只活在 shell 历史里。Compose 是把它变成可以评审的文件的那一步。
docker run 与 Compose 转换器 在本地解析命令并写出服务,也可以读 Compose 文件、写回你要单独试一个服务时用的 docker run。这篇讲的是哪些标志有诚实的对应,哪些不该指望机械转换替你发明。
一行命令应当变成什么样的服务
拿一条已经在干活的命令:
docker run -d --name billing \
-p 8080:8080 \
-e DATABASE_URL=postgres://billing:secret@db:5432/billing \
-e LOG_LEVEL=info \
-v billing-data:/var/lib/billing \
--restart unless-stopped \
ghcr.io/example/billing:1.4.2
Compose 服务不是把字符串换个写法。它是另一种数据:
- 镜像是
ghcr.io/example/billing:1.4.2。标签是镜像的一部分,不是可以忘掉的另一个字段。 8080:8080是发布端口。Compose 里写成ports: ["8080:8080"]。左边是主机,右边是容器。写反了,服务在网络内部看起来正常,你的笔记本却连不上。--restart unless-stopped变成restart: unless-stopped。always和on-failure是不同策略。on-failure还可以带重试次数。转换器没认出这个标志就把它丢掉,进程在重启后不会回来,而这正是你写这个标志要防的事。- 命名卷
billing-data既要出现在服务上,若要保住名字,也要出现在顶层的volumes:里。./data:/var/lib/billing这种是绑定主机路径。命名卷是 Docker 管理的目录。把两者混成一件事,数据的位置就变了。 -d和--name说的是客户端这次怎么调用。分离模式没有 Compose 字段,因为docker compose up -d是客户端的选择。容器名可以设,但 Compose 已经会加上项目名前缀。写死container_name: billing会让这份文件无法在同一台机器上跑两份。
环境变量是敏感的部分。例子里的 DATABASE_URL 含有密码。转换器看见它不需要服务器,你也不应该需要。密码不要写进要提交的 Compose 文件,放到 env 文件里。
机械转换会说错的标志
--rm 是进程退出后删掉容器。Compose 没有这个开关。转换时悄悄丢掉 --rm,容器会留下来。对长期服务这通常是你想要的,对一次性迁移容器则是错的。在文件里用注释或 profiles 写清楚,不要假设这个标志还在。
-it 分配终端并保持标准输入。Compose 可以设 stdin_open 和 tty。Web 服务两者都不需要。交互式 shell 需要。把一条调试命令(docker run -it --rm ubuntu)收成长期服务,结果是容器马上退出,重启策略再把它拉起来。
--network host 不是一个叫 host 的网络名,而是一种模式。Compose 写成 network_mode: host。若把它当成用户网络 host,进程会绑在桥接网络上,而不是主机网卡。端口发布又开始起作用,这和当初加 --network host 的原因相反。
--link 已经过时。新文件应当用 Compose 网络和 DNS,服务名就是主机名。把 --link 抄进新文件,是在复制 Docker 正在拆掉的功能。
健康检查(--health-cmd、--health-interval)在 Compose 里是一块配置。如果转换器留下了命令、丢掉了间隔,你得到的是 Docker 的默认值。启动慢的进程可能被标成不健康,再叠上重启策略,就会循环被杀掉。转换之后要读健康检查,不要假设默认值等于你敲过的那些标志。
Compose 里 deploy.resources 的限制面向 Swarm 和部分实现。docker run 上的 --memory 和 --cpus 作用于本机容器。这些数字若只写在 deploy 下面,开发机上的 docker compose up 可能根本不看。转换结果是草稿。进程会泄漏内存时,限制要读两遍。
环境变量是另一个工具的事
服务有了之后,变量是会泄漏的那一块。Compose 写 env_file: .env,只要 .env 被 git 忽略,提交的文件里就没有秘密。Kubernetes 不读这个 .env。它要的是 Secret 或 ConfigMap。
这一步是 .env 转换器:把 .env 写成 Secret 清单,或写成 JSON、YAML,并对比两份 env,让生产环境缺的键在 Pod 崩溃前现形。Compose 转换器不知道哪个键是秘密。env 转换器不知道你的服务名叫什么。按这个顺序用:先得到服务,把变量移出要提交的文件,同一批变量要上 Kubernetes 时再生成清单。
Secret 清单里的值是 Base64。这不是锁。能 kubectl get secret -o yaml 的人都能解开。在本地生成清单,是为了不必把值贴进一个写着「env 转 Kubernetes」的网页。集群仍然会保存这份 Secret。那是另一条信任边界,和转换这一步不是一回事。
反向:从 Compose 回到 docker run
当你想在项目之外重现一个服务时,把 Compose 贴进去要 docker run。反向映射会丢掉项目级网络、depends_on 的顺序,以及大多数 profile。depends_on 变不成标志。依赖要你自己先启动。转出来的 docker run 没有数据库,不是解析器坏了。Compose 表达的是单条 docker run 表达不了的顺序。
多个服务变成多条命令。文件顶部声明的卷要先建好,或者让 Docker 在命令里见到这个名字时自己建。相对路径的绑定挂载是相对 Compose 文件所在目录,不是相对你后来粘贴 docker run 时所在的目录。转出来的命令里若有 ./data,就在那个目录里运行,或把路径改掉。
提交之前要读的几处
读镜像标签。读每个发布端口以及它绑在哪块接口上。读环境变量,把秘密移到 env 文件。把重启策略和健康检查放在一起读。若 network_mode 写着 host,读它。然后对一个可以扔掉的数据库跑这份文件,或跑生成的 docker run。能解析不等于数据目录还在原来的地方。
两个工具都留在浏览器里。你粘贴的命令里可能有镜像仓库密码、云令牌或数据库 URL。改写它的页面不需要留一份副本。