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 能解决什么问题?
- 环境一致性:开发、测试、生产环境完全一致
- 快速部署:几秒钟启动一个完整的应用
- 资源隔离:多个应用互不干扰
- 版本管理:像管理代码一样管理环境
- 微服务架构:轻松管理多个服务
二、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)
仓库是什么?
- 用来存储和分发镜像的地方
- 最大的公共仓库:Docker Hub(https://hub.docker.com)
类比:
- 仓库 = GitHub,但存的是镜像而不是代码
- 你可以从仓库下载(pull)镜像,也可以上传(push)自己的镜像
2.4 三者关系
┌─────────────┐
│ 仓库 │ 存储镜像的地方
│ (Registry) │
└──────┬──────┘
│ pull(拉取)
↓
┌─────────────┐
│ 镜像 │ 只读模板
│ (Image) │
└──────┬──────┘
│ run(运行)
↓
┌─────────────┐
│ 容器 │ 运行的实例
│ (Container) │
└─────────────┘
三、安装 Docker
3.1 Windows 安装
系统要求
- Windows 10/11 专业版或企业版(需要 Hyper-V 支持)
- 或者 Windows 10/11 家庭版(使用 WSL 2)
安装步骤
-
下载 Docker Desktop
- 访问:https://www.docker.com/products/docker-desktop
- 下载 Windows 版本
-
运行安装程序
- 双击
Docker Desktop Installer.exe - 勾选 "Use WSL 2 instead of Hyper-V"(推荐)
- 等待安装完成
- 双击
-
启动 Docker Desktop
- 安装完成后,从开始菜单启动 Docker Desktop
- 第一次启动可能需要几分钟
-
验证安装
bashdocker --version docker compose version如果看到版本号,说明安装成功!
3.2 macOS 安装
系统要求
- macOS 11 或更高版本
- Apple Silicon (M1/M2) 或 Intel 芯片
安装步骤
-
下载 Docker Desktop
- 访问:https://www.docker.com/products/docker-desktop
- 选择对应芯片的版本(Apple Silicon 或 Intel)
-
安装
- 打开下载的
.dmg文件 - 将 Docker 图标拖到 Applications 文件夹
- 打开下载的
-
启动
- 从 Applications 启动 Docker
- 按照提示完成设置
-
验证
bashdocker --version docker compose version
3.3 Linux 安装(以 Ubuntu 为例)
安装步骤
-
更新包索引
bashsudo apt-get update -
安装必要的包
bashsudo apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release -
添加 Docker 官方 GPG 密钥
bashsudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg -
设置仓库
bashecho \ "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 -
安装 Docker Engine
bashsudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin -
验证安装
bashsudo docker run hello-world -
(可选)允许非 root 用户运行 Docker
bashsudo 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.
发生了什么?
- Docker 在本地查找
hello-world镜像 - 本地没有,从 Docker Hub 下载
- 创建容器并运行
- 输出欢迎信息
- 容器退出
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:设置工作目录为 /appnode: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 MBnode:18: ~900 MBnode:18-alpine: ~170 MBpython:3.11: ~900 MBpython: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周):
- 安装 Docker
- 运行几个官方镜像(nginx, mysql, redis)
- 学习基本命令
- 编写简单的 Dockerfile
中级(2-4周):
- 使用 Docker Compose 管理多容器应用
- 学习网络和存储
- 实践一个完整的项目(如 WordPress)
- 了解最佳实践
高级(持续学习):
- 多阶段构建优化镜像
- Docker 安全
- CI/CD 集成
- Kubernetes 入门
9.4 推荐资源
官方文档:
- Docker 官方文档:https://docs.docker.com
- Docker Hub:https://hub.docker.com
实践项目:
- Awesome Compose:https://github.com/docker/awesome-compose
(包含各种语言和框架的 Docker Compose 示例)
学习平台:
- Play with Docker:https://labs.play-with-docker.com
(在线 Docker 环境,无需安装)
9.5 结语
Docker 和 Docker Compose 已经成为现代软件开发的必备技能。从本地开发到生产部署,从单体应用到微服务架构,它们无处不在。
记住几个关键点:
- 环境一致性是 Docker 最大的价值
- 不要过度工程化,从简单开始
- 多实践,看十遍不如动手一遍
- 关注安全,不要在镜像中存储敏感信息
- 持续学习,Docker 生态系统在不断进化
最后,送给你一句话:
"Works on my machine" 问题?用 Docker 把你的 machine 也打包进去!
祝你容器化之旅愉快! 🐳
有问题?去 Docker 官方论坛 或 Stack Overflow 提问吧!
