Docker Compose实战踩坑记那些年我遇到的配置陷阱

Top丶诗雯 工具 阅读 1,388
赞 5 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

用了几年Docker Compose,我发现很多人写yaml文件还是按照传统的docker run那种思路,一个容器一个文件,最后项目一复杂就乱得不行。我现在都是把相关的服务打包在一个compose文件里,这样管理起来方便多了。

Docker Compose实战踩坑记那些年我遇到的配置陷阱

先说说我觉得最靠谱的写法:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      - db
      - redis
    volumes:
      - .:/app
      - /app/node_modules
    restart: unless-stopped

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:alpine
    restart: unless-stopped

volumes:
  postgres_data:

这种写法的好处是所有环境变量都在docker-compose.yml里统一管理,不用再去搞什么.env文件。我一般这样处理数据库连接,用service name作为host,这样容器间通信很稳定。

这些错误写法,千万别学

最常见的错误就是直接暴露数据库端口,像这样:

# 错误示范
db:
  image: postgres:13
  ports:
    - "5432:5432"  # 千万别这么干
  environment:
    POSTGRES_PASSWORD: secret

这样做的问题是任何人都能连上你的数据库,安全风险太大。正确的做法是只在内部通信,外部通过web服务来访问数据。

另一个坑是volume挂载写错了,比如:

# 错误示范
web:
  build: .
  volumes:
    - ./src:/app/src  # 这样可能导致权限问题
    - node_modules:/app/node_modules

这种写法在不同系统环境下容易出问题,Linux和Windows的权限机制不一样。我一般用排除node_modules的方式:

# 正确示范
web:
  build: .
  volumes:
    - .:/app
    - /app/node_modules  # 这样会创建匿名卷,避免覆盖

实际项目中的坑

有个项目我折腾了好久才搞定启动顺序的问题。PostgreSQL启动需要时间,但是Node.js应用很快就启动完了,结果连接不上数据库。后来发现了depends_on其实只是等待容器启动,不一定等到服务就绪。

现在我会这样处理:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

这里的healthcheck确保PostgreSQL真的可以接受连接了,web服务才会启动。这个配置我用了很久,基本没出过问题。

还有个常见问题是环境变量的处理。之前我在.env文件里放了很多敏感信息,结果不小心提交到了git,被同事发现后一顿批评。现在我都是把敏感配置写在yml文件里,非敏感的才用env文件。

生产环境的部署注意点

生产环境和开发环境的配置差别很大。我一般会准备两套配置文件,docker-compose.yml给开发用,docker-compose.prod.yml给生产用。

生产环境的关键配置:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "127.0.0.1:3000:3000"  # 只绑定localhost
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    restart: always
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    restart: always
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

volumes:
  postgres_data:

注意几个要点:端口只绑定本地回环地址、开启日志轮转防止磁盘爆满、密码用secrets管理而不是环境变量。这些都是生产环境的安全要求。

还有一点容易被忽视:容器资源限制。之前有个项目跑着跑着就把服务器内存吃光了,后来加上了限制:

web:
  build: .
  deploy:
    resources:
      limits:
        memory: 512M
        cpus: '0.5'
      reservations:
        memory: 256M
        cpus: '0.25'

调试和监控的小技巧

日常开发的时候经常需要看日志,我一般这样起服务:

docker-compose up --build

build参数确保代码更新能生效。如果想后台运行:

docker-compose up -d
docker-compose logs -f web  # 查看某个服务的日志

有时候需要进入容器调试:

docker-compose exec web sh

这种方式比docker attach好用,而且可以直接执行命令。我还习惯给docker-compose命令加个别名,在.bashrc里:

alias dco='docker-compose'

这样输入命令更快。

网络配置很重要

默认情况下所有service都在同一个bridge network里,大部分情况够用了。但是有时候需要自定义网络配置,比如服务隔离或者跨机器部署。

自定义网络的写法:

version: '3.8'

services:
  web:
    build: .
    networks:
      - frontend
      - backend
    ports:
      - "3000:3000"

  db:
    image: postgres:13
    networks:
      - backend
    environment:
      POSTGRES_PASSWORD: pass

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内网网络,不能访问外网

internal设置为true的网络不能访问外网,这样数据库就更加安全了。不过要注意,如果backend需要拉取远程镜像就不行了。

版本选择有讲究

Docker Compose有很多版本,建议用3.8以上,新特性支持得比较好。低版本可能会遇到一些奇怪的问题,比如healthcheck不生效。

另外要注意docker-compose和docker engine的兼容性,版本太老的话有些功能用不了。我一般保持docker版本比较新,这样新特性都能用上。

以上是我个人对Docker Compose的一些实践经验总结,主要是一些容易踩坑的地方和解决方案。这个工具用得越久就越觉得设计得不错,不过确实需要一些摸索才能用得顺手。有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论