Dockerfile 和 Docker Compose 手把手教学
看完还不会写,回来找我。
一、Dockerfile — 给程序"打包"的配方
Dockerfile 本质上就是一个文本文件,告诉 Docker 怎么把你的程序装进一个干净的"微型电脑"。这台微型电脑里有你指定的操作系统、运行环境、你的代码,只干一件事。
核心指令速查表
| 指令 | 作用 | 类比 |
|---|---|---|
FROM | 指定基础镜像 | 买一台预装了系统的电脑 |
WORKDIR | 设置工作目录 | cd 到某个文件夹 |
COPY | 从宿主机复制文件进镜像 | 插 U 盘拷文件 |
RUN | 在镜像构建时执行命令 | 装软件 |
CMD | 容器启动时默认执行的命令 | 开机自启动程序 |
EXPOSE | 声明容器监听端口 | 在墙上开个洞 |
ENV | 设置环境变量 | 配全局参数 |
一个最简示例
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY ota_server.py .
CMD ["python", "ota_server.py"]一步步解释:
FROM python:3.11-slim # 基于已装了 Python 3.11 的精简 Linux
WORKDIR /app # 进到 /app 目录
COPY requirements.txt . # 把本地的 requirements.txt 拷进来
RUN pip install -r requirements.txt # 装依赖
COPY ota_server.py . # 把主程序拷进来
CMD ["python", "ota_server.py"] # 启动时跑这个命令几个关键细节
1. COPY 为什么要分两次?
# ✅ 正确:先拷依赖文件,再装依赖,最后拷代码
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY ota_server.py .
# ❌ 不好:先全拷进去再装,改一行代码就要重装依赖
COPY . .
RUN pip install -r requirements.txtDocker 是分层缓存的。requirements.txt 没变 → 依赖层用缓存,秒级构建。改了代码 → 只重新执行 COPY ota_server.py 这一层。
2. RUN 和 CMD 的区别
RUN pip install flask # 构建时执行,结果写进镜像
CMD ["python", "app.py"] # 运行时执行,每次启动容器才跑RUN:做一次就固化的事(装软件、创建目录)CMD:容器每次启动都要跑的事(启动程序)
3. FROM 怎么选?
| 镜像 | 大小 | 适用 |
|---|---|---|
python:3.11 | ~1GB | 不推荐,太大 |
python:3.11-slim | ~150MB | 推荐,日常够用 |
python:3.11-alpine | ~50MB | 极致精简,但某些 C 扩展可能踩坑 |
一般选 slim 最稳。
二、docker-compose.yml — 一键编排多个容器
单独用 Dockerfile 构建镜像后,还要手动 docker run 带一堆参数启动,容易忘记。
docker-compose.yml 就是把这些参数写进一个文件,一条命令(或一个按钮)搞定构建 + 启动。
最简单的 compose 文件
services:
ota-server:
build: .
ports:
- "8092:8092"
volumes:
- /opt/ota_data:/app/firmware
restart: unless-stopped拆解:
services: # 定义一组"服务"(一个服务 = 一个容器)
ota-server: # 服务名,随便起
build: . # 从当前目录的 Dockerfile 构建镜像
ports: # 端口映射
- "8092:8092" # 宿主机端口:容器内端口
volumes: # 数据卷挂载
- /opt/ota_data:/app/firmware # 宿主机路径:容器内路径
restart: unless-stopped # 重启策略:除非手动停,否则一直自动重启多服务示例
一个 Wordpress 站,需要 WordPress + MySQL 两个容器:
services:
db:
image: mysql:8.0 # 不需要 build,直接用现成镜像
environment: # 环境变量
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db # 这里写服务名 db,compose 自动解析
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: 123456
depends_on: # 依赖:先启动 db,再启动 wordpress
- db
volumes: # 声明具名数据卷
db_data:要点:
build: .→ 自己构建image: xxx→ 用 Docker Hub 上的现成镜像- 服务间用服务名通信,比如
WORDPRESS_DB_HOST: db,compose 自动做 DNS
常用配置项速查
services:
myapp:
build: . # 构建目录
image: myapp:v1 # 给构建出来的镜像打标签
container_name: myapp # 固定容器名(不写则自动生成)
ports:
- "8080:80" # 宿主机:容器
volumes:
- ./data:/app/data # 相对路径(相对于 compose 文件)
- /absolute/path:/data # 绝对路径
environment: # 环境变量
- DEBUG=true
- PORT=8080
restart: always # 挂了就重启(always / unless-stopped / on-failure)
depends_on: # 启动顺序
- dbbuild 和 image 的区别
# 自己构建
services:
app:
build: . # 找当前目录的 Dockerfile
# 拉现成的
services:
nginx:
image: nginx:1.25 # 从 Docker Hub 拉
# 两个都写 → 构建后打标签
services:
app:
build: .
image: myapp:latest # 构建的镜像命名为 myapp:latest坑:如果两个都写,Docker 有时会先去 Hub 上找myapp:latest,找不到才构建。纯自建项目建议只写build: .,不写image。
三、实战:从零写出 OTA 服务器的两个文件
回到 OTA 项目,这两个文件是怎么一步步写出来的:
Dockerfile
我需要什么?
1. Python 环境 → FROM python:3.11-slim
2. 进工作目录 → WORKDIR /app
3. 装上 flask, werkzeug → COPY requirements.txt + RUN pip install
4. 拷入代码 → COPY ota_server.py
5. 固件数据目录 → RUN mkdir -p /app/firmware
6. 暴露端口 → EXPOSE 8092
7. 启动命令 → CMD ["python", "ota_server.py"]写出来就是:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ota_server.py .
RUN mkdir -p /app/firmware
EXPOSE 8092
ENV OTA_HOST=0.0.0.0
ENV OTA_PORT=8092
CMD ["python", "ota_server.py"]docker-compose.yml
我需要什么?
1. 从当前目录构建 → build: .
2. 映射端口 8092 → ports: "8092:8092"
3. 固件数据持久化到宿主机 → volumes: /opt/ota_data:/app/firmware
4. 服务器重启自动拉起来 → restart: unless-stopped写出来就是:
services:
ota-server:
build: .
container_name: ota-server
restart: unless-stopped
ports:
- "8092:8092"
volumes:
- /opt/ota_data:/app/firmware四、常见坑
1. build 和 image 一起写导致尝试 pull
# ❌ 可能先去 Docker Hub 找 esp32-ota:latest
services:
app:
build: .
image: esp32-ota:latest修法:自建镜像不写 image:
2. version 已经废弃
# ❌ Docker Compose v2 会报警告
version: "3.8"修法:删掉 version 那一行,现在 compose 默认就是 v2 行为。
3. 忘记映射端口
# ❌ 外面访问不到
services:
app:
build: .修法:ports: - "宿主机端口:容器内端口"
4. COPY . . 导致每改一行就重装依赖
分层顺序错了,依赖缓存白费。按上一节的顺序来:先拷 requirements.txt,再 pip install,最后拷代码。
总结:记住这个模板就行
Dockerfile 模板:
FROM 语言:版本-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt # 或 npm install / go build 等
COPY . .
CMD ["启动命令"]docker-compose.yml 模板:
services:
服务名:
build: .
ports:
- "端口:端口"
volumes:
- 宿主机路径:容器内路径
restart: unless-stopped改改参数,90% 的项目就这么写。
🤖 声明:本文由 AI 辅助生成,经过人工校验与修改。
作者:Wangbeihong / 2026年6月
评论
暂无评论