跳至正文

🐳 什么是Docker?

📖 定义

Docker是一个在称为容器的隔离环境中运行应用程序的平台。容器将应用程序与其所有依赖项打包在一起,确保它在任何地方都能相同运行。它解决了"在我的机器上可以运行..."的问题,使开发、测试和部署变得简单而一致。

🎯 通过类比理解

运输集装箱

将Docker比作物流中的集装箱:

传统运输 (VM)
├─ 每个物品不同的包装
├─ 尺寸/重量各异
├─ 运输方式不同
└─ 低效、复杂

集装箱运输 (Docker)
├─ 标准化集装箱
├─ 可容纳任何货物
├─ 船、卡车、火车通用
└─ 高效、简单

Docker容器 = 标准化软件包

公寓 vs 独栋房屋

虚拟机 (VM) = 独栋房屋
├─ 每栋都需要完整基础设施
├─ 土地、建筑、水电全部独立
├─ 成本高
└─ 启动慢

Docker容器 = 公寓
├─ 共享基础设施(土地、建筑)
├─ 每个单元独立
├─ 高效
└─ 快速入住

⚙️ 工作原理

1. Docker架构

┌─────────────────────────────────────┐
│ Docker客户端 (CLI) │
│ docker run, docker build, etc. │
└───────────────┬─────────────────────┘
│ API调用
┌───────────────▼─────────────────────┐
│ Docker守护进程 │
│ 容器/镜像管理 │
└───────────────┬─────────────────────┘

┌───────────────▼─────────────────────┐
│ 容器(运行中的应用) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │App 1│ │App 2│ │App 3│ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘

┌───────────────▼─────────────────────┐
│ 主机操作系统 (Linux内核) │
└─────────────────────────────────────┘

2. 镜像 vs 容器

镜像 = 蓝图、类
├─ 只读
├─ 分层结构
├─ 可重用
└─ 例如:ubuntu、node、nginx

容器 = 运行实例、对象
├─ 从镜像创建
├─ 可执行
├─ 隔离环境
└─ 可创建多个实例

关系:
镜像 → 容器1
→ 容器2
→ 容器3

3. Dockerfile → 镜像 → 容器

1. 编写Dockerfile(配方)
├─ FROM node:18
├─ COPY package.json .
├─ RUN npm install
└─ CMD ["npm", "start"]

2. 构建镜像(烹饪)
docker build -t my-app .

3. 运行容器(上菜)
docker run -p 3000:3000 my-app

💡 实际示例

编写Dockerfile

# Node.js应用程序Dockerfile

# 基础镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制package.json
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制源代码
COPY . .

# 暴露端口
EXPOSE 3000

# 环境变量
ENV NODE_ENV=production

# 容器启动时执行的命令
CMD ["node", "server.js"]

构建和运行镜像

# 1. 构建镜像
docker build -t my-node-app:1.0 .
# -t: 标签(名称:版本)
# .: 使用当前目录的Dockerfile

# 2. 检查镜像
docker images
# REPOSITORY TAG IMAGE ID CREATED
# my-node-app 1.0 abc123 2分钟前

# 3. 运行容器
docker run -d \
--name my-app \
-p 3000:3000 \
-e NODE_ENV=production \
my-node-app:1.0

# -d: 后台运行
# --name: 容器名称
# -p: 端口映射(主机:容器)
# -e: 环境变量

# 4. 检查运行中的容器
docker ps

# 5. 查看日志
docker logs my-app

# 6. 访问容器内部
docker exec -it my-app sh

# 7. 停止容器
docker stop my-app

# 8. 删除容器
docker rm my-app

# 9. 删除镜像
docker rmi my-node-app:1.0

Docker Compose(管理多个容器)

# docker-compose.yml

version: '3.8'

services:
# Web应用
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=database
depends_on:
- database
- redis
volumes:
- ./logs:/app/logs

# 数据库
database:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myapp
volumes:
- db-data:/var/lib/mysql
ports:
- "3306:3306"

# 缓存
redis:
image: redis:7-alpine
ports:
- "6379:6379"

# Nginx(反向代理)
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web

volumes:
db-data:
# Docker Compose命令

# 启动所有服务
docker-compose up -d

# 查看日志
docker-compose logs -f web

# 重启特定服务
docker-compose restart web

# 扩展服务
docker-compose up -d --scale web=3

# 停止并删除所有服务
docker-compose down

# 删除包括卷
docker-compose down -v

实战示例:React + Node.js + MongoDB

# frontend/Dockerfile
FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 使用Nginx提供静态文件
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# backend/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 5000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'

services:
frontend:
build:
context: ./frontend
ports:
- "80:80"
depends_on:
- backend

backend:
build:
context: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URL=mongodb://mongodb:27017/myapp
depends_on:
- mongodb

mongodb:
image: mongo:6
volumes:
- mongo-data:/data/db
ports:
- "27017:27017"

volumes:
mongo-data:

管理数据卷

# 创建卷
docker volume create my-data

# 列出卷
docker volume ls

# 将卷挂载到容器
docker run -v my-data:/app/data my-app

# 挂载主机目录(开发时有用)
docker run -v $(pwd):/app my-app

# 只读挂载
docker run -v $(pwd):/app:ro my-app

# 删除卷
docker volume rm my-data

# 删除所有未使用的卷
docker volume prune

.dockerignore文件

# .dockerignore

# Git
.git
.gitignore

# Node
node_modules
npm-debug.log

# 环境配置
.env
.env.local

# 构建输出
dist
build

# IDE
.vscode
.idea

# 测试
coverage
*.test.js

# 文档
README.md
docs/

🤔 常见问题

Q1. Docker vs 虚拟机?

A:

虚拟机 (VM)
┌─────────────────┐ ┌─────────────────┐
│ App A │ │ App B │
│ Libs │ │ Libs │
│ 客户操作系统 │ │ 客户操作系统 │
│ (Linux) │ │ (Ubuntu) │
└─────────────────┘ └─────────────────┘
┌─────────────────────────────────────┐
│ 虚拟机监控程序 (VMware等) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 主机操作系统 │
└─────────────────────────────────────┘

Docker容器
┌───────┐ ┌───────┐ ┌───────┐
│ App A │ │ App B │ │ App C │
│ Libs │ │ Libs │ │ Libs │
└───────┘ └───────┘ └───────┘
┌─────────────────────────────┐
│ Docker引擎 │
└─────────────────────────────┘
┌─────────────────────────────┐
│ 主机操作系统 (Linux内核) │
└─────────────────────────────┘

比较:
VM Docker
大小 GB MB
启动 分钟 秒
隔离 完全 进程级别
性能 开销大 接近原生
可移植性 低 高

Q2. 什么是Docker镜像层?

A:

FROM node:18          # 层1:基础镜像
WORKDIR /app # 层2:工作目录
COPY package.json . # 层3:package.json
RUN npm install # 层4:安装依赖
COPY . . # 层5:源代码
CMD ["node", "app.js"]# 层6:启动命令

# 层缓存
# - 未更改的层会被重用
# - 加快构建速度

# ✅ 好的做法:频繁更改的内容放在后面
COPY package.json .
RUN npm install # 缓存(如果package.json未更改)
COPY . . # 只有代码更改

# ❌ 不好的做法:频繁更改的内容放在前面
COPY . . # 每次代码更改
RUN npm install # npm install会重新运行!

Q3. 什么是容器编排?

A:

单服务器
┌─────────────────────┐
│ ┌───┐ ┌───┐ ┌───┐ │
│ │C1 │ │C2 │ │C3 │ │
│ └───┘ └───┘ └───┘ │
└─────────────────────┘

编排 (Kubernetes)
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Server1 │ │ Server2 │ │ Server3 │
│ ┌───┐ │ │ ┌───┐ │ │ ┌───┐ │
│ │C1 │ │ │ │C2 │ │ │ │C3 │ │
│ └───┘ │ │ └───┘ │ │ └───┘ │
└─────────┘ └─────────┘ └─────────┘

功能:
- 自动部署
- 扩展(自动扩容/缩容)
- 负载均衡
- 自我修复(容器死亡时重启)
- 滚动更新

工具:
- Kubernetes(最流行)
- Docker Swarm
- Amazon ECS

Q4. 在开发环境中使用Docker?

A:

# 场景:团队成员环境各不相同

# 成员A:macOS、Node 16
# 成员B:Windows、Node 18
# 成员C:Linux、Node 14

# 问题:"在我的机器上可以运行..."

# 解决方案:使用Docker统一环境

# docker-compose.yml
version: '3.8'
services:
app:
image: node:18
volumes:
- .:/app
working_dir: /app
command: npm run dev
ports:
- "3000:3000"

# 所有团队成员使用相同环境
docker-compose up

# 优势:
# 1. 无需安装Node.js
# 2. 版本统一
# 3. 依赖隔离
# 4. 环境配置共享

Q5. Docker安全性?

A:

# ✅ 安全最佳实践

# 1. 最小权限基础镜像
FROM node:18-alpine # ✅ 小而安全
FROM node:18 # ❌ 不必要的工具

# 2. 以非root用户运行
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs # ✅ 最小化权限

# 3. 排除敏感信息
# 添加到.dockerignore
.env
*.key
secrets/

# 4. 多阶段构建(删除不必要的工具)
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM node:18-alpine # 更小的镜像
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/main.js"]

# 5. 扫描镜像
docker scan my-image

# 6. 只读根文件系统
docker run --read-only my-app

# 7. 资源限制
docker run --memory="512m" --cpus="1" my-app

🎓 下一步

理解Docker后,探索:

  1. 什么是Git?(即将推出)- 版本控制和部署
  2. 什么是Node.js?(即将推出)- 使用Docker部署Node应用
  3. 什么是TDD? - 在Docker中运行测试

亲自尝试

# 1. 检查Docker安装
docker --version

# 2. Hello World
docker run hello-world

# 3. 运行Nginx
docker run -d -p 8080:80 nginx
# 访问 http://localhost:8080

# 4. 检查容器
docker ps

# 5. 停止并删除
docker stop <container-id>
docker rm <container-id>

# 6. 容器化你的应用
# - 编写Dockerfile
# - docker build
# - docker run

有用的命令

# 镜像管理
docker pull nginx # 下载镜像
docker images # 列出镜像
docker rmi nginx # 删除镜像

# 容器管理
docker run # 运行容器
docker ps # 运行中的容器
docker ps -a # 所有容器
docker stop <id> # 停止容器
docker start <id> # 启动容器
docker restart <id> # 重启容器
docker rm <id> # 删除容器

# 日志和调试
docker logs <id> # 查看日志
docker logs -f <id> # 跟踪日志
docker exec -it <id> sh # 访问容器shell
docker inspect <id> # 详细信息

# 清理
docker system prune # 删除未使用的资源
docker system prune -a # 删除所有未使用的资源
docker volume prune # 删除未使用的卷

🎬 总结

Docker是现代开发的必备工具:

  • 容器:隔离的执行环境
  • 镜像:应用程序包
  • 可移植性:在任何地方都能相同运行
  • 高效性:轻量且快速

使用Docker解决"在我的机器上可以运行"的问题! 🐳✨