构建自己的应用镜像
掌握 Dockerfile 的核心指令
能独立编写 Dockerfile 构建应用镜像
完成多容器应用的部署联调
核心讲解
构建自己的应用镜像,就是把你的应用和运行环境打包成镜像,标准方式是编写 Dockerfile 文件,然后用 docker build 构建。
10.1 准备项目目录和后端
核心讲解
构建前先整理好项目结构,代码、配置、依赖都放在项目目录里。 以一个简单的 Python Flask 后端为例:
项目目录:所有文件放一个文件夹
代码文件:app.py
依赖文件:requirements.txt
运行环境:Python 3.10
10.2 编写 Dockerfile,构建应用镜像
核心讲解
Dockerfile 是构建镜像的脚本,一条条指令,按顺序执行,每一条生成一层。
核心指令:
表格
指令 | 作用 |
|---|---|
| 基础镜像,所有镜像都要基于一个基础镜像 |
| 设置工作目录,后续命令都在这个目录下 |
| 把宿主机的文件复制到镜像里 |
| 构建时执行命令,比如安装依赖 |
| 声明容器暴露的端口,只是说明,不做映射 |
| 容器启动时执行的命令,一个 Dockerfile 只有最后一个 CMD 生效 |
构建命令: docker build -t 镜像名:标签 . . 代表当前目录为构建上下文。
10.3 配置 Nginx
前端静态项目一般用 Nginx 托管,反向代理后端接口。
准备 nginx 配置文件
把静态文件和配置复制进镜像
启动 nginx 服务
10.4 创建 / 运行三个容器
前端、后端、数据库三个容器,加入同一个自定义网络,用容器名互相调用,完成完整应用部署。
操作示例
# ===== 项目结构 =====
# my-app/
# ├── backend/
# │ ├── app.py
# │ └── requirements.txt
# └── Dockerfile
# ===== 1. 后端代码 app.py =====
"""
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return {"message": "Hello Docker!"}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
"""
# ===== 2. requirements.txt =====
"""
flask==2.3.0
"""
# ===== 3. 编写Dockerfile =====
"""
# 基础镜像
FROM python:3.10-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 复制代码
COPY app.py .
# 暴露端口
EXPOSE 5000
# 启动命令
CMD ["python", "app.py"]
"""
# ===== 4. 构建镜像 =====
cd my-app/backend
docker build -t my-backend:v1 .
# ===== 5. 运行验证 =====
# 创建网络
docker network create app-net
# 启动后端
docker run -d --name backend --network app-net my-backend:v1
# 启动nginx
docker run -d --name nginx --network app-net -p 80:80 nginx
# 进入nginx,访问后端验证
docker exec -it nginx curl backend:5000
# 返回{"message": "Hello Docker!"} 就是成功
常见误区
COPY 用相对路径:COPY 的源路径是相对于构建上下文的,不是相对于 Dockerfile
每次构建都重新下载依赖:先复制依赖文件,安装依赖,再复制代码,利用镜像缓存,加速构建
CMD 写成 RUN:RUN 是构建时执行,CMD 是启动时执行,不要搞混
构建上下文太大:不要用根目录构建,会把所有文件传进去,很慢,用.dockerignore 忽略无用文件
镜像特别大:用 slim/alpine 基础镜像,减少体积,不要用完整系统镜像
课后练习
编写一个简单的应用,编写对应的 Dockerfile
构建成镜像,命名为 my-app:v1
启动容器,验证应用能正常访问
思考:为什么先复制 requirements.txt 再复制代码?

