[Docker] Dockerfile 入门学习文档

Grok生成(2025年10月28日23:11:52)
手动修订(2025年10月29日22:00:06)

Docker 入门学习文档

一、什么是 Docker?

比喻 解释
集装箱 传统开发:程序像散装货物,换一台电脑就可能“缺零件”。
Docker:把程序 + 依赖 + 配置打包成一个“标准集装箱”,任何地方都能直接运行。
虚拟机 vs 容器 虚拟机:带整个操作系统,占几 GB,启动慢。
Docker 容器:只带程序所需部分,几十 MB,秒级启动。

一句话总结

Docker 让你“一次打包,到处运行”,解决“在我电脑上能跑,你那不行”的尴尬。


二、核心概念

概念 解释 类比
镜像(Image) 只读的程序快照(像 U 盘里的系统镜像) .iso 文件
容器(Container) 镜像的运行实例(像从 U 盘启动的系统) 运行中的电脑
Dockerfile 描述“如何制作镜像”的脚本 菜谱
Docker Hub 镜像仓库(像 GitHub,但存镜像) 应用商店
镜像 → 容器   (就像 类 → 对象)

三、Dockerfile 基础结构

# 1. 从哪个基础镜像开始?(就像选择操作系统)
FROM ubuntu:20.04
#   ↑ 官方 Ubuntu 20.04 镜像,稳定且常用

# 2. 镜像的作者信息(可选,但推荐写)
LABEL maintainer="你的邮箱@example.com"

# 3. 设置环境变量(程序运行时会读取)
ENV APP_HOME=/app          # 程序主目录
ENV NODE_ENV=production    # Node.js 运行模式

# 4. 设置工作目录(之后的命令都在这个目录执行)
WORKDIR $APP_HOME
#   等价于:cd /app

# 5. 复制文件到容器中
COPY package*.json ./
#   ↑ 把本地的 package.json 和 package-lock.json 复制到 /app/
COPY src/ ./src/
#   ↑ 把 src 文件夹整个复制进去

# 6. 执行安装命令(每条 RUN 都会生成新镜像层)
RUN apt-get update && \
    apt-get install -y nodejs npm && \
    rm -rf /var/lib/apt/lists/*
#   ↑ 安装 Node.js 和 npm
#   ↑ 最后一行清理缓存,减小镜像体积

# 7. 暴露容器端口(告诉外界我监听 3000 端口)
EXPOSE 3000
#   注意:只是“声明”,实际映射要用 -p 参数

# 8. 容器启动时运行的命令
CMD ["node", "src/app.js"]
#   ↑ 用数组形式,推荐!避免 shell 解析问题

四、常用指令详解

1. FROM 选择基础镜像

多阶段构建(后面案例会用)

FROM node:16-alpine     # 小巧、安全,推荐生产
FROM python:3.9-slim    # Python 轻量版
FROM ubuntu:22.04       # 通用,但较大

2. RUN 镜像中执行命令

注意docker每个操作都会生成一层layer,因此能用一条命令写完的不要写多次

# 错误:每条 RUN 都多一层,镜像变大
RUN apt-get update
RUN apt-get install -y python3

# 正确:用 && 连接,清理缓存
RUN apt-get update && \
    apt-get install -y python3 python3-pip && \
    rm -rf /var/lib/apt/lists/*

3. COPY vs ADD

指令 用途 推荐?
COPY 复制本地文件/文件夹 强烈推荐
ADD 复制 + 自动解压 tar + 支持 URL 不推荐
COPY . .                  # 复制当前目录所有文件
COPY app.py /app/         # 复制到指定路径
ADD app.tar.gz /app/      # 自动解压(不推荐)

4. CMD vs ENTRYPOINT

指令 是否可被 docker run 覆盖 用途
CMD 可以 默认启动命令
ENTRYPOINT 不可以 固定入口
# 推荐组合写法
ENTRYPOINT ["python"]
CMD ["app.py"]
# 运行:docker run myimage app.py → 实际执行:python app.py

5. EXPOSE 只是声明

EXPOSE 3000
  • 只是告诉用户“我用 3000 端口”
  • 实际映射要靠命令docker run -p 3000:3000

五、完整案例


案例1:Python Flask 博客

项目结构

my-flask-app/
├── app.py
├── requirements.txt
└── Dockerfile

app.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return "Hello Docker! 欢迎访问我的博客~"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

requirements.txt

Flask==2.3.3

Dockerfile

# 1. 基础镜像
FROM python:3.9-slim

# 2. 环境变量(避免 pyc 文件、实时输出日志)
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# 3. 工作目录
WORKDIR /app

# 4. 复制依赖文件(先装依赖,缓存更好)
COPY requirements.txt .

# 5. 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt

# 6. 复制代码
COPY app.py .

# 7. 暴露端口
EXPOSE 5000

# 8. 启动命令
CMD ["python", "app.py"]

构建和运行

# 构建镜像
docker build -t my-flask .

# 运行容器
docker run -p 5000:5000 my-flask

浏览器访问 http://localhost:5000 → 看到 “Hello Docker!”


案例2:Node.js Express API

Dockerfile

# 1. 使用轻量 Alpine 镜像
FROM node:18-alpine

# 2. 工作目录
WORKDIR /usr/src/app

# 3. 复制 package 文件(利用缓存)
COPY package*.json ./

# 4. 安装生产依赖
RUN npm ci --only=production

# 5. 复制源码
COPY . .

# 6. 创建非 root 用户(安全!)
RUN addgroup -g 1001 nodejs && \
    adduser -S appuser -u 1001
USER appuser

# 7. 暴露端口
EXPOSE 3000

# 8. 启动
CMD ["node", "server.js"]

安全加分项

  • 不以 root 运行
  • npm ci 更可重复
  • --only=production 不装 devDependencies

案例3:多阶段构建 Go 应用

main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello from tiny Go app!")
}

Dockerfile

# === 第1阶段:编译 ===
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod .
RUN go mod download
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

# === 第2阶段:运行 ===
FROM alpine:latest
WORKDIR /root/

# 复制编译好的二进制
COPY --from=builder /app/app .

# 非 root 用户
RUN adduser -D appuser
USER appuser

CMD ["./app"]

构建结果

REPOSITORY    TAG       SIZE
my-go-app     latest    8.2MB    ← 比传统方式小 90%

六、最佳实践清单

分类 建议 示例
镜像小 alpineslim node:18-alpine
层缓存 先复制不变的文件 COPY package*.json
安全 非 root 用户 USER appuser
清理 删除缓存 rm -rf /var/lib/apt/lists/*
.dockerignore 忽略无用文件 见下文

.dockerignore 文件(重要)

node_modules
.git
.env
*.log
Dockerfile
.dockerignore
README.md

作用:构建时不把这些文件复制进镜像,加速构建 + 避免泄露


七、调试技巧

开发版 Dockerfile(热重载)

FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install

# 开发工具
RUN npm install -g nodemon

COPY . .

CMD ["nodemon", "server.js"]

运行时加卷挂载:

docker run -v $(pwd):/app -p 3000:3000 my-dev

八、常用命令速查表

命令 作用
docker build -t 名字 . 构建镜像
docker run -p 外:内 名字 运行容器
docker ps 查看运行中的容器
docker images 查看本地镜像
docker logs 容器ID 查看日志
docker exec -it 容器ID sh 进入容器
docker stop 容器ID 停止容器

九、常见问题解答

问题 原因 解决
port is already allocated 3000 端口被占 换端口:-p 3001:3000
permission denied 以 root 运行 USER 指令
镜像太大 没清理缓存 rm -rf
改代码不生效 没重新构建 docker build 再次构建
posted on 2025-10-28 23:12  风惊庭前叶  阅读(6)  评论(0)    收藏  举报