Docker 与 Docker Compose:从零开始的容器化之旅

Docker 与 Docker Compose:从零开始的容器化之旅

"为什么我的代码在我电脑上能跑,在你那就不行了?" ------ 每一个程序员的灵魂拷问

目录

  • 前言:那些年我们踩过的环境配置坑
  • [一、Docker 是什么?](#一、Docker 是什么?)
  • [二、Docker 核心概念](#二、Docker 核心概念)
  • [三、安装 Docker](#三、安装 Docker)
  • [四、Docker 基础实战](#四、Docker 基础实战)
  • [五、编写 Dockerfile](#五、编写 Dockerfile)
  • [六、Docker Compose 登场](#六、Docker Compose 登场)
  • [七、Docker Compose 实战](#七、Docker Compose 实战)
  • 八、最佳实践与避坑指南
  • 九、总结

前言:那些年我们踩过的环境配置坑

还记得第一次接手同事的项目吗?

  • "这个项目需要 Node.js 14.x,不是 16,也不是 12,必须是 14!"
  • "Python 环境?你得装虚拟环境,然后 pip install 一堆依赖..."
  • "MySQL?你本地装了吗?版本是 8.0 还是 5.7?配置文件改了吗?"
  • "Redis?MongoDB?Nginx?都装上吧,顺便把端口配置好..."

两个小时过去了,你的电脑已经装满了各种软件,环境变量改了又改,最后项目还是跑不起来。你绝望地在群里发消息:"大佬,能帮我看看为什么还是报错吗?"

这时候,有个声音说:"用 Docker 啊,一键启动,保证能跑。"

Docker,就是来拯救我们于水深火热之中的!


一、Docker 是什么?

1.1 用人话解释 Docker

想象一下,你要搬家。传统方式是什么?把家具、电器、衣服一件件打包,搬到新家再一件件摆好。费时费力,还容易丢东西。

Docker 就像是集装箱

  • 把你的应用程序(代码)
  • 加上它需要的所有依赖(Node.js、数据库、配置文件)
  • 全部打包进一个"集装箱"里

这个"集装箱"可以在任何支持 Docker 的机器上运行,保证环境完全一致

1.2 Docker vs 虚拟机

特性 虚拟机 Docker 容器
启动速度 分钟级 秒级
磁盘占用 GB 级别 MB 级别
性能 接近宿主机 几乎等于宿主机
隔离性 完全隔离 进程级隔离
系统支持 一个虚拟机一个完整 OS 共享宿主机内核

打个比方:

  • 虚拟机:在你家里盖一栋完整的房子(完整的操作系统)
  • Docker:在你家里搭一个帐篷(只包含应用需要的东西)

1.3 Docker 能解决什么问题?

  1. 环境一致性:开发、测试、生产环境完全一致
  2. 快速部署:几秒钟启动一个完整的应用
  3. 资源隔离:多个应用互不干扰
  4. 版本管理:像管理代码一样管理环境
  5. 微服务架构:轻松管理多个服务

二、Docker 核心概念

在开始实战之前,我们需要理解三个核心概念:镜像、容器、仓库。

2.1 镜像(Image)

镜像是什么?

  • 镜像是一个只读的模板
  • 包含了运行应用所需的所有内容:代码、运行时、库、环境变量、配置文件
  • 就像是一个"冰冻的快照"

类比:

  • 镜像 = 做蛋糕的菜谱
  • 你可以根据同一个菜谱(镜像)做出无数个一模一样的蛋糕(容器)

常见镜像:

  • nginx:latest - Nginx 服务器
  • node:18-alpine - Node.js 运行环境
  • mysql:8.0 - MySQL 数据库
  • python:3.11 - Python 运行环境

2.2 容器(Container)

容器是什么?

  • 容器是镜像运行时的实体
  • 可以被创建、启动、停止、删除
  • 容器之间相互隔离

类比:

  • 容器 = 根据菜谱做出来的真实的蛋糕
  • 你可以吃它、修改它、扔掉它

关键特点:

  • 轻量级:容器只包含应用和必要的依赖
  • 隔离性:每个容器有自己的文件系统、进程空间
  • 可移植:可以在任何支持 Docker 的平台运行

2.3 仓库(Registry)

仓库是什么?

类比:

  • 仓库 = GitHub,但存的是镜像而不是代码
  • 你可以从仓库下载(pull)镜像,也可以上传(push)自己的镜像

2.4 三者关系

复制代码
┌─────────────┐
│   仓库       │  存储镜像的地方
│  (Registry) │
└──────┬──────┘
       │ pull(拉取)
       ↓
┌─────────────┐
│   镜像       │  只读模板
│  (Image)    │
└──────┬──────┘
       │ run(运行)
       ↓
┌─────────────┐
│   容器       │  运行的实例
│ (Container) │
└─────────────┘

三、安装 Docker

3.1 Windows 安装

系统要求
  • Windows 10/11 专业版或企业版(需要 Hyper-V 支持)
  • 或者 Windows 10/11 家庭版(使用 WSL 2)
安装步骤
  1. 下载 Docker Desktop

  2. 运行安装程序

    • 双击 Docker Desktop Installer.exe
    • 勾选 "Use WSL 2 instead of Hyper-V"(推荐)
    • 等待安装完成
  3. 启动 Docker Desktop

    • 安装完成后,从开始菜单启动 Docker Desktop
    • 第一次启动可能需要几分钟
  4. 验证安装

    bash 复制代码
    docker --version
    docker compose version

    如果看到版本号,说明安装成功!

3.2 macOS 安装

系统要求
  • macOS 11 或更高版本
  • Apple Silicon (M1/M2) 或 Intel 芯片
安装步骤
  1. 下载 Docker Desktop

  2. 安装

    • 打开下载的 .dmg 文件
    • 将 Docker 图标拖到 Applications 文件夹
  3. 启动

    • 从 Applications 启动 Docker
    • 按照提示完成设置
  4. 验证

    bash 复制代码
    docker --version
    docker compose version

3.3 Linux 安装(以 Ubuntu 为例)

安装步骤
  1. 更新包索引

    bash 复制代码
    sudo apt-get update
  2. 安装必要的包

    bash 复制代码
    sudo apt-get install \
        ca-certificates \
        curl \
        gnupg \
        lsb-release
  3. 添加 Docker 官方 GPG 密钥

    bash 复制代码
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  4. 设置仓库

    bash 复制代码
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  5. 安装 Docker Engine

    bash 复制代码
    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  6. 验证安装

    bash 复制代码
    sudo docker run hello-world
  7. (可选)允许非 root 用户运行 Docker

    bash 复制代码
    sudo usermod -aG docker $USER
    newgrp docker

四、Docker 基础实战

4.1 Hello World - 第一个容器

让我们运行第一个 Docker 容器:

bash 复制代码
docker run hello-world

输出解析:

复制代码
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
...
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

发生了什么?

  1. Docker 在本地查找 hello-world 镜像
  2. 本地没有,从 Docker Hub 下载
  3. 创建容器并运行
  4. 输出欢迎信息
  5. 容器退出

4.2 运行一个真正的应用:Nginx

让我们运行一个 Nginx web 服务器:

bash 复制代码
docker run -d -p 8080:80 --name my-nginx nginx:latest

参数解析:

  • -d:后台运行(detached mode)
  • -p 8080:80:端口映射,宿主机 8080 映射到容器 80
  • --name my-nginx:给容器起个名字
  • nginx:latest:使用的镜像

现在打开浏览器访问 http://localhost:8080,你会看到 Nginx 欢迎页面!

4.3 容器管理基础命令

查看运行中的容器
bash 复制代码
docker ps

输出示例:

复制代码
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                  NAMES
a1b2c3d4e5f6   nginx:latest   "/docker-entrypoint...."   10 seconds ago   Up 9 seconds    0.0.0.0:8080->80/tcp   my-nginx
查看所有容器(包括已停止的)
bash 复制代码
docker ps -a
停止容器
bash 复制代码
docker stop my-nginx
启动已停止的容器
bash 复制代码
docker start my-nginx
重启容器
bash 复制代码
docker restart my-nginx
删除容器
bash 复制代码
docker rm my-nginx

注意:删除运行中的容器需要先停止,或者使用 -f 强制删除:

bash 复制代码
docker rm -f my-nginx
查看容器日志
bash 复制代码
docker logs my-nginx

实时查看日志:

bash 复制代码
docker logs -f my-nginx
进入容器内部
bash 复制代码
docker exec -it my-nginx bash

参数解析:

  • -it:交互式终端
  • bash:运行 bash 命令

在容器内部,你可以像操作普通 Linux 系统一样操作。退出使用 exit

4.4 镜像管理命令

查看本地镜像
bash 复制代码
docker images

输出示例:

复制代码
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
nginx         latest    a1b2c3d4e5f6   2 weeks ago    142MB
hello-world   latest    1a2b3c4d5e6f   3 months ago   13.3kB
下载镜像
bash 复制代码
docker pull ubuntu:22.04
删除镜像
bash 复制代码
docker rmi nginx:latest
搜索镜像
bash 复制代码
docker search nginx

4.5 实战案例:运行一个 Node.js 应用

假设你有一个简单的 Node.js 应用:

app.js:

javascript 复制代码
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.end('<h1>Hello from Docker!</h1><p>这是一个运行在容器中的 Node.js 应用</p>');
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

运行这个应用:

bash 复制代码
docker run -d -p 3000:3000 -v $(pwd):/app -w /app node:18 node app.js

参数解析:

  • -v $(pwd):/app:将当前目录挂载到容器的 /app 目录
  • -w /app:设置工作目录为 /app
  • node:18:使用 Node.js 18 镜像
  • node app.js:运行的命令

访问 http://localhost:3000 就能看到你的应用了!


五、编写 Dockerfile

手动运行容器很麻烦,每次都要输入一大串命令。Dockerfile 可以把这些步骤写成脚本,一键构建镜像。

5.1 Dockerfile 是什么?

Dockerfile 是一个文本文件,包含了构建 Docker 镜像的所有指令。

类比:

  • Dockerfile = 详细的菜谱
  • 按照菜谱(Dockerfile)做出的菜(镜像)
  • 菜可以端上桌多次享用(运行多个容器)

5.2 Dockerfile 基础语法

常用指令
指令 说明 示例
FROM 指定基础镜像 FROM node:18
WORKDIR 设置工作目录 WORKDIR /app
COPY 复制文件到镜像 COPY . /app
ADD 复制文件(支持 URL 和自动解压) ADD app.tar.gz /app
RUN 执行命令(构建时) RUN npm install
CMD 容器启动命令(只有最后一个生效) CMD ["node", "app.js"]
ENTRYPOINT 容器启动入口点 ENTRYPOINT ["nginx"]
ENV 设置环境变量 ENV NODE_ENV=production
EXPOSE 声明端口 EXPOSE 3000
VOLUME 声明数据卷 VOLUME /data

5.3 实战案例 1:Node.js 应用

项目结构
复制代码
my-node-app/
├── app.js
├── package.json
└── Dockerfile
package.json
json 复制代码
{
  "name": "my-node-app",
  "version": "1.0.0",
  "main": "app.js",
  "dependencies": {
    "express": "^4.18.2"
  }
}
app.js
javascript 复制代码
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('<h1>Hello Docker!</h1><p>这是一个容器化的 Express 应用</p>');
});

app.get('/api/info', (req, res) => {
  res.json({
    message: '容器运行正常',
    timestamp: new Date().toISOString(),
    hostname: require('os').hostname()
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
Dockerfile
dockerfile 复制代码
# 使用官方 Node.js 18 镜像作为基础镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm install --production

# 复制应用代码
COPY . .

# 声明端口
EXPOSE 3000

# 设置环境变量
ENV NODE_ENV=production

# 启动命令
CMD ["node", "app.js"]
构建镜像
bash 复制代码
docker build -t my-node-app:1.0 .

参数解析:

  • -t my-node-app:1.0:给镜像打标签(名称:版本)
  • .:Dockerfile 所在目录(当前目录)
运行容器
bash 复制代码
docker run -d -p 3000:3000 --name node-app my-node-app:1.0

5.4 实战案例 2:Python Flask 应用

项目结构
复制代码
my-flask-app/
├── app.py
├── requirements.txt
└── Dockerfile
app.py
python 复制代码
from flask import Flask, jsonify
import datetime

app = Flask(__name__)

@app.route('/')
def home():
    return '<h1>Hello from Flask in Docker!</h1>'

@app.route('/api/time')
def get_time():
    return jsonify({
        'time': datetime.datetime.now().isoformat(),
        'message': 'Flask 应用运行在 Docker 容器中'
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
requirements.txt
复制代码
Flask==2.3.0
Dockerfile
dockerfile 复制代码
# 使用 Python 3.11 基础镜像
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY requirements.txt .

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

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 5000

# 运行应用
CMD ["python", "app.py"]
构建并运行
bash 复制代码
docker build -t my-flask-app:1.0 .
docker run -d -p 5000:5000 --name flask-app my-flask-app:1.0

5.5 Dockerfile 最佳实践

1. 使用 .dockerignore

.gitignore 一样,.dockerignore 可以排除不需要的文件:

复制代码
node_modules
npm-debug.log
.git
.env
.DS_Store
*.md
2. 多阶段构建

减小镜像大小,分离构建和运行环境:

dockerfile 复制代码
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/app.js"]
3. 合并 RUN 指令

减少镜像层数:

dockerfile 复制代码
# 不好的做法
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git

# 好的做法
RUN apt-get update && \
    apt-get install -y curl git && \
    rm -rf /var/lib/apt/lists/*
4. 利用缓存

把不经常变化的指令放在前面:

dockerfile 复制代码
# 先复制依赖文件,这样依赖不变时可以利用缓存
COPY package*.json ./
RUN npm install

# 再复制代码(代码经常变化)
COPY . .

六、Docker Compose 登场

6.1 为什么需要 Docker Compose?

想象一个真实的 Web 应用:

  • 前端:React 应用
  • 后端:Node.js API
  • 数据库:MySQL
  • 缓存:Redis
  • 反向代理:Nginx

如果用 docker run 一个个启动,你需要:

bash 复制代码
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
docker run -d --name redis redis:7
docker run -d --name backend --link mysql --link redis my-backend
docker run -d --name frontend --link backend my-frontend
docker run -d --name nginx -p 80:80 --link frontend my-nginx

天啊,这也太复杂了!而且:

  • 容器之间的网络配置麻烦
  • 每次都要手动启动,顺序还不能错
  • 配置参数一大堆,容易出错

Docker Compose 来拯救你了!

6.2 Docker Compose 是什么?

Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。

核心特点:

  • 使用 YAML 文件配置服务
  • 一条命令启动所有服务
  • 自动处理容器之间的网络和依赖

类比:

  • 单个 Dockerfile = 一道菜的菜谱
  • Docker Compose = 一桌菜的完整菜单

6.3 docker-compose.yml 基础结构

yaml 复制代码
version: '3.8'  # Compose 文件版本

services:  # 定义服务
  web:  # 服务名称
    image: nginx:latest  # 使用的镜像
    ports:  # 端口映射
      - "80:80"

  db:  # 另一个服务
    image: mysql:8.0
    environment:  # 环境变量
      MYSQL_ROOT_PASSWORD: secret
    volumes:  # 数据卷
      - db-data:/var/lib/mysql

volumes:  # 定义数据卷
  db-data:

七、Docker Compose 实战

7.1 实战案例 1:WordPress 博客系统

一个完整的 WordPress 需要:

  • WordPress 应用
  • MySQL 数据库
docker-compose.yml
yaml 复制代码
version: '3.8'

services:
  # MySQL 数据库
  db:
    image: mysql:8.0
    container_name: wordpress-db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppassword
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wordpress-network

  # WordPress 应用
  wordpress:
    image: wordpress:latest
    container_name: wordpress-app
    restart: always
    depends_on:
      - db
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppassword
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html
    networks:
      - wordpress-network

volumes:
  db_data:
  wordpress_data:

networks:
  wordpress-network:
    driver: bridge
启动应用
bash 复制代码
# 启动所有服务
docker compose up -d

# 查看运行状态
docker compose ps

# 查看日志
docker compose logs -f

# 停止所有服务
docker compose down

# 停止并删除数据卷
docker compose down -v

访问 http://localhost:8080,开始配置你的 WordPress!

7.2 实战案例 2:全栈应用(前后端分离)

一个现代化的全栈应用包括:

  • 前端:React
  • 后端:Node.js + Express
  • 数据库:PostgreSQL
  • 缓存:Redis
  • 反向代理:Nginx
项目结构
复制代码
fullstack-app/
├── frontend/
│   ├── Dockerfile
│   └── ... (React 代码)
├── backend/
│   ├── Dockerfile
│   └── ... (Node.js 代码)
├── nginx/
│   └── nginx.conf
└── docker-compose.yml
frontend/Dockerfile
dockerfile 复制代码
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
backend/Dockerfile
dockerfile 复制代码
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
nginx/nginx.conf
nginx 复制代码
upstream backend {
    server backend:3000;
}

server {
    listen 80;

    # 前端静态文件
    location / {
        root /usr/share/nginx/html;
        try_files $uri /index.html;
    }

    # API 代理到后端
    location /api {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
docker-compose.yml
yaml 复制代码
version: '3.8'

services:
  # PostgreSQL 数据库
  postgres:
    image: postgres:15-alpine
    container_name: fullstack-db
    restart: always
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppassword
      POSTGRES_DB: appdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis 缓存
  redis:
    image: redis:7-alpine
    container_name: fullstack-redis
    restart: always
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

  # 后端 API
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: fullstack-backend
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      NODE_ENV: production
      DB_HOST: postgres
      DB_PORT: 5432
      DB_USER: appuser
      DB_PASSWORD: apppassword
      DB_NAME: appdb
      REDIS_HOST: redis
      REDIS_PORT: 6379
    networks:
      - app-network
    volumes:
      - ./backend:/app
      - /app/node_modules

  # 前端应用
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: fullstack-frontend
    restart: always
    depends_on:
      - backend
    networks:
      - app-network

  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    container_name: fullstack-nginx
    restart: always
    depends_on:
      - frontend
      - backend
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./frontend/build:/usr/share/nginx/html:ro
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge
启动应用
bash 复制代码
# 构建并启动
docker compose up -d --build

# 查看日志
docker compose logs -f backend

# 扩展后端服务到 3 个实例
docker compose up -d --scale backend=3

# 停止服务
docker compose down

7.3 Docker Compose 常用命令

基础命令
bash 复制代码
# 启动服务
docker compose up

# 后台启动
docker compose up -d

# 构建并启动
docker compose up --build

# 停止服务
docker compose stop

# 停止并删除容器
docker compose down

# 停止并删除容器、网络、数据卷
docker compose down -v
服务管理
bash 复制代码
# 查看运行状态
docker compose ps

# 查看服务日志
docker compose logs

# 实时查看日志
docker compose logs -f

# 查看特定服务日志
docker compose logs -f backend

# 重启服务
docker compose restart

# 重启特定服务
docker compose restart backend
执行命令
bash 复制代码
# 在运行的容器中执行命令
docker compose exec backend sh

# 在新容器中运行一次性命令
docker compose run backend npm test
扩展服务
bash 复制代码
# 扩展服务实例数
docker compose up -d --scale backend=3

7.4 实战案例 3:开发环境配置

开发环境需要热重载、源码映射等功能。

docker-compose.dev.yml
yaml 复制代码
version: '3.8'

services:
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile.dev
    volumes:
      - ./backend:/app
      - /app/node_modules
    environment:
      NODE_ENV: development
    ports:
      - "3000:3000"
      - "9229:9229"  # Node.js 调试端口
    command: npm run dev

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - "3001:3000"
    environment:
      - CHOKIDAR_USEPOLLING=true  # 支持 Windows 热重载
    command: npm start

  postgres:
    image: postgres:15-alpine
    ports:
      - "5432:5432"  # 暴露端口方便本地连接
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: devdb
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  postgres_dev_data:
使用开发配置
bash 复制代码
# 使用开发配置启动
docker compose -f docker-compose.dev.yml up

# 可以同时使用多个配置文件
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

八、最佳实践与避坑指南

8.1 镜像优化

1. 选择合适的基础镜像
dockerfile 复制代码
# ❌ 不好:镜像太大
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3

# ✅ 好:使用 alpine 版本
FROM python:3.11-alpine

镜像大小对比:

  • ubuntu:latest: ~72 MB
  • node:18: ~900 MB
  • node:18-alpine: ~170 MB
  • python:3.11: ~900 MB
  • python:3.11-alpine: ~50 MB
2. 利用构建缓存
dockerfile 复制代码
# ✅ 好:依赖变化少,可以利用缓存
COPY package*.json ./
RUN npm install
COPY . .

# ❌ 不好:每次代码改动都要重新安装依赖
COPY . .
RUN npm install
3. 清理不必要的文件
dockerfile 复制代码
RUN apt-get update && \
    apt-get install -y curl && \
    rm -rf /var/lib/apt/lists/*  # 清理缓存

8.2 安全最佳实践

1. 不要使用 root 用户
dockerfile 复制代码
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 切换用户
USER nodejs

# 之后的操作都以 nodejs 用户执行
CMD ["node", "app.js"]
2. 不要在镜像中存储敏感信息
dockerfile 复制代码
# ❌ 危险:密码写在 Dockerfile 里
ENV DB_PASSWORD=mysecret

# ✅ 安全:通过环境变量或 secrets 传递
# docker run -e DB_PASSWORD=mysecret ...
3. 及时更新基础镜像
bash 复制代码
# 定期拉取最新镜像
docker pull node:18-alpine

# 重新构建
docker build --no-cache -t myapp .

8.3 数据持久化

1. 使用 volumes 而不是 bind mounts(生产环境)
yaml 复制代码
# ✅ 推荐:使用 volume
volumes:
  - db_data:/var/lib/mysql

# ⚠️  开发环境可以用 bind mount
volumes:
  - ./data:/var/lib/mysql
2. 备份数据卷
bash 复制代码
# 备份
docker run --rm \
  -v postgres_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/db-backup.tar.gz -C /data .

# 恢复
docker run --rm \
  -v postgres_data:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/db-backup.tar.gz -C /data

8.4 网络配置

1. 使用自定义网络
yaml 复制代码
# ✅ 好:自定义网络,容器间可以通过服务名访问
networks:
  app-network:
    driver: bridge

services:
  backend:
    networks:
      - app-network
2. 不要使用 --link(已废弃)
bash 复制代码
# ❌ 旧方式
docker run --link db:database myapp

# ✅ 新方式:使用自定义网络
docker network create mynetwork
docker run --network mynetwork --name db postgres
docker run --network mynetwork myapp

8.5 日志管理

1. 限制日志大小
yaml 复制代码
services:
  backend:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
2. 使用集中式日志
yaml 复制代码
services:
  backend:
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://192.168.0.42:514"

8.6 健康检查

dockerfile 复制代码
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
yaml 复制代码
services:
  backend:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 3s
      start_period: 5s
      retries: 3

8.7 资源限制

yaml 复制代码
services:
  backend:
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

8.8 常见问题与解决

问题 1:容器无法访问宿主机服务

解决方案:

  • Windows/Mac: 使用 host.docker.internal
  • Linux: 使用 172.17.0.1(默认 Docker bridge IP)
javascript 复制代码
// 连接宿主机的数据库
const dbHost = process.env.DB_HOST || 'host.docker.internal';
问题 2:Windows 下文件监听不工作

解决方案:

yaml 复制代码
environment:
  - CHOKIDAR_USEPOLLING=true
问题 3:容器时间不对

解决方案:

yaml 复制代码
volumes:
  - /etc/localtime:/etc/localtime:ro

或设置时区:

dockerfile 复制代码
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
问题 4:权限问题(Linux)

解决方案:

yaml 复制代码
services:
  backend:
    user: "${UID}:${GID}"

启动时:

bash 复制代码
UID=$(id -u) GID=$(id -g) docker compose up

九、总结

9.1 核心概念回顾

  • 镜像 (Image):应用的模板,只读
  • 容器 (Container):镜像运行的实例,可读写
  • Dockerfile:构建镜像的脚本
  • Docker Compose:管理多容器应用的工具

9.2 常用命令速查表

Docker 命令
bash 复制代码
# 镜像操作
docker pull <image>          # 拉取镜像
docker images                # 查看镜像列表
docker rmi <image>           # 删除镜像
docker build -t <name> .     # 构建镜像

# 容器操作
docker run <image>           # 运行容器
docker ps                    # 查看运行中的容器
docker ps -a                 # 查看所有容器
docker stop <container>      # 停止容器
docker start <container>     # 启动容器
docker rm <container>        # 删除容器
docker logs <container>      # 查看日志
docker exec -it <container> sh  # 进入容器

# 系统操作
docker system prune          # 清理无用资源
docker system df             # 查看磁盘使用
Docker Compose 命令
bash 复制代码
docker compose up            # 启动服务
docker compose up -d         # 后台启动
docker compose down          # 停止并删除容器
docker compose ps            # 查看服务状态
docker compose logs          # 查看日志
docker compose logs -f       # 实时查看日志
docker compose exec <service> sh  # 进入容器
docker compose restart       # 重启服务

9.3 学习路径建议

初级(1-2周):

  1. 安装 Docker
  2. 运行几个官方镜像(nginx, mysql, redis)
  3. 学习基本命令
  4. 编写简单的 Dockerfile

中级(2-4周):

  1. 使用 Docker Compose 管理多容器应用
  2. 学习网络和存储
  3. 实践一个完整的项目(如 WordPress)
  4. 了解最佳实践

高级(持续学习):

  1. 多阶段构建优化镜像
  2. Docker 安全
  3. CI/CD 集成
  4. Kubernetes 入门

9.4 推荐资源

官方文档:

实践项目:

学习平台:

9.5 结语

Docker 和 Docker Compose 已经成为现代软件开发的必备技能。从本地开发到生产部署,从单体应用到微服务架构,它们无处不在。

记住几个关键点:

  1. 环境一致性是 Docker 最大的价值
  2. 不要过度工程化,从简单开始
  3. 多实践,看十遍不如动手一遍
  4. 关注安全,不要在镜像中存储敏感信息
  5. 持续学习,Docker 生态系统在不断进化

最后,送给你一句话:

"Works on my machine" 问题?用 Docker 把你的 machine 也打包进去!


祝你容器化之旅愉快! 🐳

有问题?去 Docker 官方论坛Stack Overflow 提问吧!

相关推荐
Leinwin2 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
2401_865382502 小时前
信息化项目运维与运营的区别
运维·运营·信息化项目·政务信息化
漠北的哈士奇3 小时前
VMware Workstation导入ova文件时出现闪退但是没有报错信息
运维·vmware·虚拟机·闪退·ova
如意.7593 小时前
【Linux开发工具实战】Git、GDB与CGDB从入门到精通
linux·运维·git
运维小欣3 小时前
智能体选型实战指南
运维·人工智能
yy55273 小时前
Nginx 性能优化与监控
运维·nginx·性能优化
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ4 小时前
Linux 查询某进程文件所在路径 命令
linux·运维·服务器
05大叔6 小时前
网络基础知识 域名,JSON格式,AI基础
运维·服务器·网络
安当加密6 小时前
无需改 PAM!轻量级 RADIUS + ASP身份认证系统 实现 Linux 登录双因子认证
linux·运维·服务器
dashizhi20156 小时前
服务器共享禁止保存到本地磁盘、共享文件禁止另存为本地磁盘、移动硬盘等
运维·网络·stm32·安全·电脑