4、Service定义详解(上):image、build、container_name、ports、expose、environment、env_file
好,咱们正式开始啃 Service 定义这块硬骨头。
Compose 文件里,Service 是绝对的核心。说白了,你要跑几个容器、它们怎么配、怎么连,全写在 Service 下面。今天我们先聊上半场,把最常用、也最容易踩坑的几个字段讲透。
4.1 image:你的容器从哪来
image 字段指定了容器启动时使用的镜像。格式很简单:镜像名:标签。
services:
web:
image: nginx:1.25-alpine
我个人习惯,生产环境一定要指定具体版本标签,别用 latest。为什么?latest 是个移动靶,今天拉和明天拉可能不一样。你想想看,线上出了 Bug,结果发现是镜像版本被悄悄更新了,这锅背得冤不冤?
image: myapp:dev 配合本地构建。但 CI/CD 流水线里,建议用 Git commit SHA 作为标签,保证可追溯。
4.2 build:自己动手,丰衣足食
有时候官方镜像满足不了你,得自己写 Dockerfile。这时候就用 build 字段。
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
args:
NODE_ENV: production
context 是构建上下文路径,dockerfile 指定 Dockerfile 文件名(默认是 Dockerfile)。args 可以传构建参数。
build 和 image 是互斥的吗?不是。你可以同时写,Compose 会先构建镜像,然后用 image 指定的名字打标签。这在 CI 里很有用。
我曾经遇到一个坑:context 指向了上级目录,结果 Docker 把整个项目都打包进了构建上下文,构建慢得要死。后来我学乖了,.dockerignore 文件一定要写好。
4.3 container_name:给你的容器起个名字
默认情况下,Compose 会给容器起一个组合名:项目名_服务名_序号。比如 myproject_web_1。如果你想要一个固定的、好记的名字,就用 container_name。
services:
db:
image: postgres:15
container_name: myapp-postgres
container_name,这个容器名就固定了。如果你用 docker-compose up --scale 做水平扩展,Compose 会报错,因为不能创建同名容器。所以,container_name 只适合单实例服务。
4.4 ports:把门打开,让流量进来
ports 用于将容器端口映射到宿主机端口。格式是 宿主机端口:容器端口。
services:
web:
image: nginx:alpine
ports:
- "8080:80"
- "443:443"
这里我把宿主机的 8080 端口映射到容器的 80 端口。访问 http://宿主机IP:8080 就能看到 Nginx 页面。
嗯,这里要注意:如果你只写 ports: - "80",Compose 会随机分配一个宿主机端口。这在本地测试时还行,生产环境千万别这么干。
ports 直接暴露。生产环境我更喜欢用反向代理(比如 Nginx 或 Traefik)来做端口映射,更灵活也更安全。
4.5 expose:内部通信,不对外公开
expose 和 ports 有点像,但有个关键区别:expose 只暴露端口给同一个网络里的其他服务,不会映射到宿主机。
services:
app:
image: myapp
expose:
- "3000"
nginx:
image: nginx:alpine
ports:
- "80:80"
在这个例子里,app 的 3000 端口只对 nginx 可见。外部世界根本不知道 3000 端口的存在。说白了,expose 就是内部文档,告诉其他服务:「嘿,我这里有这个端口可以用」。
expose 而不是 ports。除非你脑子进水了,想把 PostgreSQL 直接暴露到公网。
4.6 environment:环境变量,配置的灵魂
环境变量是容器化应用配置的标准方式。environment 字段可以让你在 Compose 文件里直接设置。
services:
db:
image: postgres:15
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mysecret
POSTGRES_DB: mydb
你也可以用列表格式:
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mysecret
我个人偏好 YAML 的键值对格式,看起来更清晰。但要注意:密码这类敏感信息,千万别硬编码在 Compose 文件里!
docker-compose.yml 里,然后不小心提交到了 Git 仓库。结果被安全扫描工具抓了个正着,被领导叫去喝茶。从那以后,我所有敏感信息都用 .env 文件或者 Docker Secrets 管理。
4.7 env_file:把环境变量搬进文件
当环境变量多起来,写在 environment 里会让 Compose 文件变得臃肿。这时候 env_file 就派上用场了。
services:
app:
image: myapp
env_file:
- ./config/app.env
- ./config/db.env
app.env 文件内容示例:
# 这是注释
NODE_ENV=production
LOG_LEVEL=info
API_KEY=abc123
注意:env_file 里的变量会被 environment 里同名的变量覆盖。所以如果你在 environment 里也定义了 NODE_ENV,那 environment 里的值优先级更高。
.env 文件加入 .gitignore,只提交一个 .env.example 模板到仓库。这样既安全,又方便新同事快速上手。
知识体系一览
下面这张图帮你理清今天讲的 7 个字段之间的关系:
今天这 7 个字段,是 Service 定义的基石。你掌握了它们,就能写出 80% 的 Compose 文件。剩下的 20%,我们下回分解。
公众号:蓝海资料掘金营,微信deep3321