当前位置: 首页 > news >正文

Docker Compose学习

Best Practices Around Production Ready Web Apps with Docker Compose — Nick Janetakis

Docker Compose

我们先从几条模式、小贴士和最佳实践谈起,这些内容适用于在开发和生产环境中使用 Docker Compose。

把文件顶部的 version 字段删掉

Docker Compose 规范已明确 version 属性「已弃用」。

在此之前,大家通常会写 version: "3.8" 或其他版本号,用来锁定可用的 API 属性集。自 Docker Compose v1.27 起,这一行可以彻底删掉

用一份覆盖文件,避免为「开发」和「生产」各写一套 Compose

Docker Tip #94: Docker Compose v2 and Profiles Are the Best Thing Ever — Nick Janetakis

说到「开发 / 生产一致性」,我主张所有环境共用同一套 docker-compose.yml。但现实中常会遇到「某些容器只想在开发跑,而生产不跑」的场景。

例如:

  • 开发时需要 Webpack 监听并实时打包,而生产环境只负责把已打好的静态文件吐出去;

  • 生产用托管版 PostgreSQL,开发却想在本地起个容器。

这类需求可以用 docker-compose.override.yml 解决。
思路很简单:新建该文件,往里塞一段类似下面的内容即可:

services:webpack:  #定义一个webpack的服务build:context: "." #使用当前目录作为构建上下文(即 Dockerfile 所在目录)。target: "webpack" #如果 Dockerfile 是多阶段构建(multi-stage),这个指定只构建到名为 webpack 的阶段。args:- "NODE_ENV=${NODE_ENV:-production}" #传递构建参数 NODE_ENV,默认值为 production,但可以通过环境变量覆盖。command: "yarn run watch" #容器启动后执行的命令是 yarn run watch,通常用于开发模式下监听文件变化并重新构建。env_file:- ".env" #从 .env 文件中加载环境变量到容器内。volumes:- ".:/app" #将当前主机目录挂载到容器的 /app 目录,方便开发时实时同步代码变化。

它就是一个普通的 Docker Compose 文件。默认情况下,当你执行 docker-compose up 时,Docker Compose 会自动把 docker-compose.ymldocker-compose.override.yml 合并成一份配置并启动。整个过程无需额外参数,完全自动。

接下来,你只要把 docker-compose.override.yml 写进 .gitignore(告诉 Git 哪些文件或目录不需要纳入版本控制(即不被 git addgit commit 追踪)。,这样代码推到生产(比如你自建的 VPS)时,服务器上根本不存在这个文件——于是开发阶段才需要的服务就不会跑出来。至此,你只用一份主文件就实现了「开发跑、生产不跑」的需求,再也不用维护 docker-compose-dev.ymldocker-compose-prod.yml 两份几乎重复的配置。

为了进一步方便开发者,你可以在仓库里再留一个 docker-compose.override.yml.example,让它受版本控制。新人克隆项目后,只需执行:

cp docker-compose.override.yml.example docker-compose.override.yml

就能立刻拿到一份现成的本地覆盖配置——这招在开发和 CI 环境里都特别省事。

用 YAML 的别名(aliases)与锚点(anchors)削减重复配置

Docker Tip #82: Using YAML Anchors and X Properties in Docker Compose — Nick Janetakis

用 YAML 的别名(aliases)与锚点(anchors)再结合 Docker Compose 的「扩展字段」(extension fields),就能大幅削减重复配置。

这里先给个最小可用示例,放在 docker-compose.yml 最顶部即可:

x-app: &default-appbuild:context: "."target: "app"args:- "FLASK_ENV=${FLASK_ENV:-production}"- "NODE_ENV=${NODE_ENV:-production}"depends_on:- "postgres"- "redis"env_file:- ".env"restart: "${DOCKER_RESTART_POLICY:-unless-stopped}"stop_grace_period: "3s"tty: truevolumes:- "${DOCKER_WEB_VOLUME:-./public:/app/public}"

And then in your Docker Compose services, you can use it like this:

  web:<<: *default-appports:- "${DOCKER_WEB_PORT_FORWARD:-127.0.0.1:8000}:8000"worker:<<: *default-appcommand: celery -A "hello.app.celery_app" worker -l "${CELERY_LOG_LEVEL:-info}"

这样就把第一段代码里的所有属性一次性“注入”到 webworker 两个服务里,省掉了大约 15 行重复配置。

1.web服务

  • 额外把容器的 8000 端口映射到宿主机的 8000(默认只监听 127.0.0.1,安全)。
    负责处理 HTTP 请求,是 Flask 主应用。

2.worker服务

  • 启动命令被覆盖:不运行 Flask,而是运行 Celery 后台任务。
    负责异步任务/队列消费

如果某个服务需要微调,只需在该服务里重新写同名字段即可覆盖别名里的值。例如,只想让 worker 的优雅停止时间变成 10 秒,就在它下面加一行 stop_grace_period: "10s",它会优先于锚点里的设定。

这种模式特别适合“多个服务共用同一 Dockerfile 和代码库,仅运行参数略有差异”的场景。

在 Docker Compose 里声明 HEALTHCHECK,而不是写死在 Dockerfile

我通常不对“最终把应用扔到哪”做预设——可能是单机 VPS 配 Docker Compose,也可能是 Kubernetes 集群,甚至直接丢上 Heroku。

虽然这三者都跑容器,但运行方式天差地别。
Kubernetes 一旦在镜像里发现 HEALTHCHECK,会自动禁用它,因为它有自己的 readiness / liveness 机制;可我们不该依赖“别人帮忙关掉”这种隐含行为,能提前规避就规避。

因此,我把健康检查写在 docker-compose.yml 里,让“编排层”自己决定要不要用、怎么用。示例:

  web:<<: *default-apphealthcheck:test: "${DOCKER_WEB_HEALTHCHECK_TEST:-curl localhost:8000/up}"interval: "60s"timeout: "3s"start_period: "5s"retries: 3

healthcheck 节点

  • test:真正执行的检查命令。
    写法 ${DOCKER_WEB_HEALTHCHECK_TEST:-curl localhost:8000/up} 意思是:

    • 如果环境变量 DOCKER_WEB_HEALTHCHECK_TEST 存在,就用它的值作为检查命令;

    • 如果不存在,就默认执行 curl localhost:8000/up
      这条命令会在容器内部访问自己的 8000 端口 /up 路径,只要能返回 200 OK,就认为“健康”。

  • interval: "60s":每 60 秒做一次检查。

  • timeout: "3s":如果命令 3 秒内没跑完,就当作这次检查失败。

  • start_period: "5s":容器启动后的前 5 秒内即使检查失败也不计数,给应用一点初始化时间。

  • retries: 3:连续失败 3 次后才最终判定容器“不健康”。

这样:

  • 本地 docker compose up 会按上述策略自检;

  • 推到 K8s 时,只要把这段配置扔掉即可,互不污染;

  • 镜像保持“编排无关”,同一 artifact 任意平台复用。

这个做法的另一个妙处是:health-check 在「运行时」才最终确定,所以我们可以给「开发」和「生产」配完全不同的检查逻辑,而不用重新打包镜像。

实现手段就是待会儿会讲到的环境变量。

  • 开发环境
    HEALTHCHECK_TEST 设成 /bin/true
    几乎不占资源、不写日志,每分钟触发也毫无感觉。

  • 生产环境
    同一字段换成 curl -f http://localhost:8000/up
    真正去做业务探活。

充分利用环境变量

仓库里放两份文件:

  1. .env – 真・变量库,含密钥和各环境差异值,一律写进 .gitignore,永不进版本库。

  2. .env.example – 示范文件,只留非敏感键值,提交到 Git。新人 / CI 一落地就能 cp .env.example .env 立刻跑起来。

Here’s a snippet from an example env file:

# Which environment is running? These should be "development" or "production".
#export FLASK_ENV=production
#export NODE_ENV=production
export FLASK_ENV=development
export NODE_ENV=development

关于文档,我喜欢把「默认值」直接写在注释里。这样一来,一旦变量被覆盖,一眼就能看出来它被改成了啥,而不用去翻源码。

说到默认值,我坚持「以生产为准」:
Compose 文件里能写死的,就写成线上想要的值。到了线上,真正需要手动注入的只剩密钥和极个别差异化变量,极大降低「忘改参数把库冲了」的人为事故。

开发阶段则随便折腾——所有 override 值都提前写进 .env.example,新人 cp 完就能跑,零成本。

把「环境变量 + Docker Compose + Dockerfile 里的 build-arg」这三板斧组合好,就能用同一套镜像、同一套代码横扫所有环境,只改几个变量即可。

回到「开发/生产一致性」主题,在 docker-compose.yml 里可以这样用变量:

environment:- FLASK_ENV=${FLASK_ENV:-production}

Compose 会自动读取与 yml 同级的 .env 文件;如果找不到,就回落到 production。语法兼容 shell 的 ${VAR:-default},只是不能嵌套变量当默认值,算个小遗憾。

下面是几招利用环境变量的用法

Controlling which health check to use:
  web:healthcheck:test: "${DOCKER_WEB_HEALTHCHECK_TEST:-curl localhost:8000/up}"interval: "60s"timeout: "3s"start_period: "5s"retries: 3

默认情况下,健康检查走的是 curl 那条路;可只要在 .env 里写一行

export DOCKER_WEB_HEALTHCHECK_TEST=/bin/true

开发环境立刻「静音模式」

至于为啥我所有 .env 都带 export
——为的就是以后能在自定义脚本里直接 source .env,变量瞬间全员上线,省事到飞起。Compose 从 1.26 起就认 export 写法,放心用,不踩坑。

Publishing ports more securely in production:
  web:restart: "${DOCKER_RESTART_POLICY:-unless-stopped}"

以前我写过,我喜欢把 Nginx 直接装在宿主机上、不放进容器。配合这个套路,能让 Web 容器的端口只监听 127.0.0.1,默认拒绝任何公网 IP 连进来。

这样一来,即使 Docker 自动写进了 iptables 规则,外人也无法直接访问 example.com:8000,省得再去云平台单独配防火墙(当然你配了更好,安全就是叠 Buff)。

开发阶段则把限制放开:在 .env 里加一行

export DOCKER_WEB_PORT_FORWARD=8000
  • 线上不填变量 → 默认 127.0.0.1:8000,只能本机 Nginx 反代。

  • 开发填了 8000 → 变成 0.0.0.0:8000,同一局域网的笔记本、iPad、手机都能顺手打开 http://<dev机IP>:8000 调试,方便得一批。

一层小配置,公网威胁挡外头,内网调试畅通行。

Taking advantage of Docker’s restart policies:
  web:restart: "${DOCKER_RESTART_POLICY:-unless-stopped}"

生产环境把 restart: unless-stopped 写进 Compose,宿主机重启或容器可恢复性崩溃后,服务能自己爬回来,省得凌晨三点起床救火。

可开发机要是也这么干就热闹了——
一重启,你这辈子攒下的十几个老项目全会嗷嗷待哺地一起启动(带 restart 策略unless-stopped / always / on-failure)的容器会在宿主机重启后被 Docker 守护进程自动复活。),风扇直接变直升机。

所以 .env 里顺手加一行:

export DOCKER_RESTART_POLICY=no
Switching up your bind mounts depending on your environment:
  web:volumes:- "${DOCKER_WEB_VOLUME:-./public:/app/public}"

如果你打算让宿主机上的 Nginx(非容器)直接托管静态文件(css、js、images 等),用 bind mount 是最省心的办法。

咱们只把 public/ 目录挂进容器,静态资源就放在那。具体路径随框架而异,我在示例项目里一律惯用 public/ 这个约定目录。

至于挂载权限:

  • 只做静态托管 → 只读 (ro) 就够;

  • 如果业务支持用户直接上传文件到磁盘 → 就得给读写 (rw)。

开发阶段图方便,可以在 .env 里写:

export DOCKER_WEB_VOLUME=.:/app

把整个源码目录挂进去,改完代码即时生效,无需重新 build 镜像,刷新浏览器就能看到新效果,爽到飞起。

Limiting CPU and memory resources of your containers:
  web:deploy:resources:limits:cpus: "${DOCKER_WEB_CPUS:-0}"memory: "${DOCKER_WEB_MEMORY:-0}"

把值写成 0(或干脆不写)= 无限量供应,容器能吃多少就吃多少。单机部署时看起来“省事”,但某些技术栈会趁机“暴饮暴食”——比如 Elixir 的 BEAM(Erlang VM),上来就把能抢到的内存和 CPU 全占满,结果 MySQL / Redis 连汤都喝不上,大家一起翻车。

就算不用 Elixir,给每个服务画条“资源红线”也有好处:

  1. 买云主机时心里先有谱,不会为了“保险”多花钱,也不会因为低估配置导致半夜扩容。

  2. 提前把“饭量”报出来,以后上 Kubernetes 就轻松:
    告诉 K8s“这货只吃 75 MB”,调度器就能把 10 个副本稳稳塞进 1 GB 的节点;
    如果啥都不填,K8s 只能瞎猜,最后节点资源碎片化,集群白白浪费一大块内存和 CPU。

一句话:先量胃再点菜,单机省钱,集群省“芯”。

http://www.dtcms.com/a/473770.html

相关文章:

  • 从iBizPMS到iBizPLM:一场研发管理平台的四年级架构革命
  • KingbaseES SQL Server模式扩展属性管理:三大存储过程实战指南
  • 5118网站怎么做的登录贵州省住房和城乡建设厅网站
  • 开源 C++ QT QML 开发(十六)进程--共享内存
  • 密码学系列 - 零知识证明(ZKP) - MSM运算
  • 厦门网站制作推广友链是什么
  • 大的网站建设公司好赣州章贡区最新招聘信息
  • 总结Vue.js等成功项目的生态建设经验
  • 西安北郊网站开发wordpress上传附件
  • mysql读写分离中间件——Atlas详解
  • jndi使用druid,在tomcat中配置druid监控
  • tomcat 默认编码的设置
  • 【mybatisPlus详解】
  • 回归与分类算法全解析:从理论到实践
  • 什么是 Qt 的元对象系统?
  • 【LeetCode】68. 文本左右对齐
  • 第十九周周报
  • Springboot 常见面试题汇总
  • 驻马店市做网站百度收录软件
  • 在实际项目中,Java 应用的性能瓶颈通常出现在哪些方面,又该如何有效地进行优化?
  • 08_Freqtrade配置与开发环境
  • C++实例
  • 复习总结最终版:Linux驱动
  • Python全栈(基础篇)——Day09:后端内容(列表生成式+生成器+迭代器+实战演示+每日一题)
  • kanass入门到实战(18) - 如何通过仪表盘,快速直观掌握项目进度及度量
  • seo网站优化工具大全wordpress 百度地图api
  • webstorm 调试时不能连接网页
  • 互助网站建设公司网页制作 主流软件
  • 微信公众号登录wordpress网站安徽城乡建设厅网站证件
  • 用wordpress建公司网站步骤郑州橱柜网站建设