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 提问吧!

相关推荐
你的坚持终将美好,40 分钟前
Docker 中一起启动多个容器脚本
docker·容器
血小溅44 分钟前
Springboot项目Docker 多平台构建指南
后端·docker
家家小迷弟1 小时前
docker容器内部安装python和numpy的方法
python·docker·numpy
北珣.1 小时前
docker镜像操作
运维·docker·容器·镜像
Evan芙1 小时前
用fping编写脚本扫描10.0.0.0/24网段在线主机
linux·运维·网络·excel
SongYuLong的博客1 小时前
ARM Linux 交叉编译工具链(toolchain)
linux·运维·arm开发
云计算老刘1 小时前
Shell三剑客 : 2. sed 使用手册
linux·运维·服务器
qq_479875431 小时前
Linux 网络实验(3)
linux·运维·网络
last demo1 小时前
grep和sed
linux·运维·前端·chrome