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%
六、最佳实践清单
| 分类 | 建议 | 示例 |
|---|---|---|
| 镜像小 | 用 alpine 或 slim |
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 再次构建 |
浙公网安备 33010602011771号