为什么要学 Docker?

你有没有遇到过这些场景:

  • GitHub 上找到一个很棒的开源项目,README 说「docker-compose up -d 即可部署」
  • 想试一下某个工具,但安装依赖配环境搞了 2 小时还没跑起来
  • 在开发机器上跑得好好的程序,部署到服务器就报错
  • 想同时跑多个版本的服务(比如 MySQL 5.7 和 8.0),互相冲突

Docker 就是解决这些问题的。一句话总结:Docker 把软件和它的所有依赖打包成一个容器,在任何机器上都能一模一样地运行。


核心概念(5 分钟搞懂)

只需要理解 3 个概念:

概念 类比 说明
镜像(Image) 安装包 一个只读模板,包含运行软件需要的一切(代码、运行时、库、配置)
容器(Container) 运行中的程序 镜像运行起来的实例,可以有多个容器来自同一个镜像
Dockerfile 配方 描述如何构建镜像的文本文件

类比理解:

  • 镜像 = 一个 .exe 安装文件
  • 容器 = 安装后正在运行的程序
  • Dockerfile = 编写这个安装包的脚本
  • Docker Hub = 应用商店(别人做好的镜像你可以直接下载用)

第一步:安装 Docker

Ubuntu / Debian

# 卸载旧版本(如果有)
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null

# 安装依赖
sudo apt update
sudo apt install -y ca-certificates curl gnupg

# 添加 Docker 官方 GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 添加仓库
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 让当前用户可以不用 sudo 运行 docker
sudo usermod -aG docker $USER
newgrp docker

# 验证安装
docker --version
docker compose version

CentOS / RHEL

# 安装依赖
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 免 sudo
sudo usermod -aG docker $USER

Windows / macOS

下载安装 Docker Desktop,图形界面,开箱即用。


第二步:运行第一个容器

Hello World

docker run hello-world

这条命令做了什么:

  1. 检查本地有没有 hello-world 镜像 → 没有
  2. 从 Docker Hub 下载 hello-world 镜像
  3. 用这个镜像创建一个容器并运行
  4. 容器输出欢迎信息后退出

运行一个 Web 服务

docker run -d -p 8080:80 --name my-nginx nginx

参数解释:

参数 含义
-d 后台运行(detached mode)
-p 8080:80 端口映射:本机 8080 → 容器内 80
--name my-nginx 给容器取个名字
nginx 使用的镜像名

打开浏览器访问 http://localhost:8080,看到 Nginx 欢迎页面。

常用管理命令

# 查看所有运行中的容器
docker ps

# 查看所有容器(含已停止的)
docker ps -a

# 停止容器
docker stop my-nginx

# 启动已停止的容器
docker start my-nginx

# 重启容器
docker restart my-nginx

# 删除容器(必须先停止)
docker rm my-nginx

# 查看容器日志
docker logs my-nginx

# 进入容器内部(像 SSH 进去一样)
docker exec -it my-nginx /bin/bash

# 查看容器资源占用
docker stats

第三步:理解数据持久化

容器被删除后,里面的数据也会消失。用 Volume(数据卷) 把数据保存到宿主机:

# 创建命名卷
docker volume create my-data

# 运行容器并挂载卷
docker run -d \
  --name my-mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -v my-data:/var/lib/mysql \
  mysql:8.0

# 即使删除容器,数据卷中的数据仍然保留
docker rm -f my-mysql
docker run -d \
  --name my-mysql-2 \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -v my-data:/var/lib/mysql \
  mysql:8.0
# 新容器能读到之前的数据

绑定挂载(直接映射宿主机目录):

docker run -d \
  --name my-nginx \
  -p 80:80 \
  -v /home/user/my-website:/usr/share/nginx/html:ro \
  nginx

这样修改宿主机 /home/user/my-website 目录的文件,Nginx 会实时反映。


第四步:Docker Compose — 一键启动多个服务

实际项目中通常需要多个服务配合(Web + 数据库 + 缓存 + ...)。Docker Compose 用一个 YAML 文件定义和启动所有服务。

4.1 创建 docker-compose.yml

# docker-compose.yml
version: '3.8'

services:
  # Web 应用
  app:
    image: node:20
    working_dir: /app
    volumes:
      - ./app:/app
    command: npm start
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache

  # 数据库
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pg-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

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

  # 反向代理
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app

volumes:
  pg-data:

4.2 常用 Compose 命令

# 启动所有服务(后台运行)
docker compose up -d

# 查看运行状态
docker compose ps

# 查看日志
docker compose logs -f          # 所有服务
docker compose logs -f app      # 指定服务

# 停止所有服务
docker compose down

# 停止并删除数据卷(⚠️ 会清除数据库数据)
docker compose down -v

# 重新构建并启动
docker compose up -d --build

# 只启动特定服务
docker compose up -d db cache

第五步:实战 — 一键部署常见开源项目

5.1 WordPress + MySQL

# docker-compose.yml
version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp-data:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: wp_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db-data:/var/lib/mysql

volumes:
  wp-data:
  db-data:
mkdir wordpress && cd wordpress
# 粘贴上面的 docker-compose.yml
docker compose up -d
# 访问 http://localhost:8080 完成 WordPress 安装

5.2 Gitea(轻量级 Git 托管)

version: '3.8'

services:
  gitea:
    image: gitea/gitea:latest
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
      - gitea-data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"   # Web UI
      - "2222:22"     # SSH
    restart: always

volumes:
  gitea-data:

5.3 Uptime Kuma(服务监控)

version: '3.8'

services:
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    volumes:
      - kuma-data:/app/data
    ports:
      - "3001:3001"
    restart: always

volumes:
  kuma-data:

5.4 N8N(工作流自动化)

version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - WEBHOOK_URL=http://localhost:5678/
    volumes:
      - n8n-data:/home/node/.n8n
    ports:
      - "5678:5678"
    restart: always

volumes:
  n8n-data:

5.5 万能模板 — 部署任何有 Docker 镜像的项目

# 1. 在 Docker Hub 搜索镜像
docker search 项目名

# 2. 查看镜像的文档(通常有 docker-compose.yml 示例)
# 访问 https://hub.docker.com/r/官方镜像名

# 3. 创建目录
mkdir my-project && cd my-project

# 4. 创建 docker-compose.yml(参考官方文档)

# 5. 启动
docker compose up -d

# 6. 查看日志确认启动成功
docker compose logs -f

第六步:Dockerfile — 打包你自己的应用

当你需要把自己的项目做成 Docker 镜像时,需要写 Dockerfile。

6.1 Node.js 项目示例

# Dockerfile
FROM node:20-alpine AS builder

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

COPY . .
RUN npm run build

# 生产阶段(多阶段构建,最终镜像更小)
FROM node:20-alpine

WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]

6.2 Python 项目示例

FROM python:3.12-slim

WORKDIR /app

# 先复制依赖文件(利用 Docker 缓存层)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制代码
COPY . .

EXPOSE 8000
CMD ["python", "app.py"]

6.3 构建和运行

# 构建镜像
docker build -t my-app:latest .

# 运行
docker run -d -p 3000:3000 --name my-app my-app:latest

# 查看镜像大小
docker images my-app

Dockerfile 最佳实践

实践 原因
用 Alpine 版本(node:20-alpine) 镜像体积小 5-10 倍
多阶段构建 最终镜像不包含构建工具和源码
先 COPY 依赖文件再 COPY 代码 利用 Docker 层缓存,依赖不变时跳过安装
用 .dockerignore 排除不需要的文件 减小构建上下文,排除 node_modules 等
不用 root 用户运行 安全最佳实践
固定版本号(postgres:16 而非 postgres:latest) 避免意外升级导致不兼容

.dockerignore 示例

node_modules
.git
.gitignore
*.md
.env
.env.*
dist
coverage
.vscode
.DS_Store

常用运维命令速查

# 清理无用资源
docker system prune          # 删除停止的容器、未使用的网络
docker system prune -a       # 加上未使用的镜像
docker system prune -a --volumes  # 加上未使用的卷(⚠️ 确认数据不需要)

# 查看资源占用
docker stats                 # 实时容器资源使用
docker system df             # 磁盘占用概览

# 导出/导入镜像(离线传输)
docker save my-app:latest | gzip > my-app.tar.gz
gunzip -c my-app.tar.gz | docker load

# 进入容器调试
docker exec -it container_name /bin/sh    # Alpine 用 sh
docker logs -f container_name             # 实时看日志
docker inspect container_name             # 查看详细信息(IP、挂载等)

# 限制容器资源
docker run -m 512m --cpus=1 my-app       # 限制内存 512MB + 1 CPU

常见问题

Q:Docker 和虚拟机有什么区别? A:虚拟机模拟整台硬件+操作系统,启动慢(分钟级)、占资源大(GB 级)。Docker 容器共享宿主机内核,启动快(秒级)、占资源小(MB 级)。Docker 不是虚拟机的替代品,但在「运行服务」这个场景下几乎总是更好的选择。

Q:容器数据丢了怎么办? A:一定要用 Volume 或 Bind Mount 持久化重要数据。没有挂载卷的容器删除后数据不可恢复。

Q:怎么更新容器? A:

docker compose pull           # 拉取最新镜像
docker compose up -d          # 用新镜像重建容器
docker image prune -f         # 清理旧镜像

Q:国内拉取镜像很慢怎么办? A:配置 Docker 镜像加速器。编辑 /etc/docker/daemon.json:

{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://docker.m.daocloud.io"
  ]
}

然后 sudo systemctl restart docker。


参考来源: