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 可以传构建参数。

注意: buildimage 是互斥的吗?不是。你可以同时写,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:内部通信,不对外公开

exposeports 有点像,但有个关键区别: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 个字段之间的关系:

Service 定义 镜像来源 image:指定已有镜像 build:从 Dockerfile 构建 容器与网络 container_name ports:对外暴露 expose:内部通信 配置注入 environment:直接定义 env_file:从文件加载 核心原则:镜像决定运行什么,网络决定怎么连,配置决定怎么跑

今天这 7 个字段,是 Service 定义的基石。你掌握了它们,就能写出 80% 的 Compose 文件。剩下的 20%,我们下回分解。


公众号:蓝海资料掘金营,微信deep3321