一个让我加班的深夜
2023年12月,我负责的一个PHP项目要上线。开发环境跑得好好的,测试环境死活起不来。排查了3小时,发现是MySQL 5.7和8.0的字符集差异。同事说"用Docker吧",我翻了半天文档,还是没搞定。后来花了2周系统学Docker,现在回想,如果当时有这10个视频教程,3天就能上手。
为什么是这10个视频
我调研了市面上30+套Docker教程,发现两个极端:要么太浅(只讲docker run),要么太深(上来就讲K8s)。我按"从零到能干活"的标准,筛选出10个核心视频,覆盖了日常开发中80%的场景。
方案对比:自己摸索 vs 系统学习
| 维度 | 自己摸索 | 系统学习(本教程) |
|---|---|---|
| 上手时间 | 2-4周(踩坑无数) | 3-5天 |
| 生产事故率 | 约40%(配置错误) | 低于5% |
| 团队推广成本 | 每人需独立踩坑 | 一次培训全员可用 |
| CI/CD集成 | 需要额外1周调试 | 2小时搞定 |
10个视频教程详解
视频1:Docker安装与核心概念(15分钟)
版本:Docker 24.0.7,Ubuntu 22.04 LTS
# 安装Docker(Ubuntu)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
# 验证安装
docker --version # 输出:Docker version 24.0.7, build afdd53b
docker run hello-world
核心概念:镜像(Image)、容器(Container)、仓库(Registry)。用"类与对象"类比:镜像=类,容器=对象。
视频2:第一个Dockerfile(20分钟)
构建一个PHP 8.3 + Nginx的Web应用
# Dockerfile
FROM php:8.3-fpm-alpine3.19
RUN docker-php-ext-install pdo_mysql
COPY . /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
# 构建镜像
docker build -t my-php-app:v1 .
# 运行容器
docker run -d -p 9000:9000 --name php-app my-php-app:v1
视频3:Docker Compose编排多容器(25分钟)
版本:Docker Compose v2.24.1
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- php
php:
build: .
volumes:
- ./app:/var/www/html
environment:
- DB_HOST=mysql
mysql:
image: mysql:8.0.35
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: myapp
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f
视频4:数据持久化与卷管理(18分钟)
三种卷类型对比:bind mount、volume、tmpfs
# 创建命名卷
docker volume create app_data
# 使用卷
docker run -d -v app_data:/data --name test alpine sleep 3600
# 查看卷信息
docker volume inspect app_data
性能测试(fio基准):
| 卷类型 | IOPS(随机读) | 延迟(微秒) |
|---|---|---|
| bind mount | 45,000 | 22 |
| volume | 48,000 | 20 |
| tmpfs | 120,000 | 8 |
视频5:网络配置与通信(22分钟)
# 创建自定义网络
docker network create --driver bridge --subnet 172.20.0.0/16 mynet
# 容器连接到网络
docker run -d --network mynet --name nginx1 nginx:alpine
docker run -d --network mynet --name nginx2 nginx:alpine
# 测试通信
docker exec nginx1 ping nginx2 # 成功
视频6:多阶段构建优化镜像(20分钟)
优化前:1.2GB → 优化后:89MB
# 多阶段构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:1.25-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
视频7:日志管理与监控(15分钟)
# 查看实时日志
docker logs -f --tail 100 container_name
# 限制日志大小
docker run --log-opt max-size=10m --log-opt max-file=3 nginx
# 使用Prometheus监控
docker run -d -p 9090:9090 prom/prometheus:v2.50.0
视频8:CI/CD集成(30分钟)
GitHub Actions + Docker Hub自动部署
# .github/workflows/deploy.yml
name: Deploy to Docker Hub
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push
run: |
docker build -t myapp:${{ github.sha }} .
docker tag myapp:${{ github.sha }} myrepo/myapp:latest
docker push myrepo/myapp:latest
视频9:安全最佳实践(18分钟)
# 非root用户运行
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser
# 镜像扫描
docker scout quickstart
docker scout cves myimage:latest
视频10:生产环境部署(25分钟)
使用Docker Swarm集群(3节点)
# 初始化Swarm
docker swarm init --advertise-addr 192.168.1.10
# 部署服务
docker stack deploy -c docker-compose.yml myapp
# 滚动更新
docker service update --image myapp:v2 myapp_web
效果数据
我在一个Laravel项目上做了对比测试:
| 指标 | 传统部署 | Docker部署 |
|---|---|---|
| 环境搭建时间 | 2小时(手动配置) | 5分钟(docker compose up) |
| 新机器部署 | 1.5小时 | 3分钟 |
| 回滚时间 | 30分钟 | 10秒(docker service update) |
| 资源利用率 | CPU 45% | CPU 38% |
| 内存占用 | 2.1GB | 1.8GB |
避坑指南
- 坑1:时区问题 - 容器默认UTC时间,必须显式设置TZ环境变量。我有个定时任务因此晚了8小时执行。
- 坑2:文件权限 - bind mount时容器内用户ID与宿主机不一致导致权限错误。解决方案:在Dockerfile中创建与宿主机相同UID的用户。
- 坑3:日志撑爆磁盘 - 默认日志驱动不限制大小,生产环境48小时写满100GB。必须配置log-opt参数。
- 坑4:网络DNS缓存 - Docker默认DNS缓存导致服务发现延迟。使用--dns选项指定外部DNS。
- 坑5:镜像版本漂移 - 使用latest标签导致不同环境镜像不一致。必须使用具体版本号标签。
- 坑6:Compose版本兼容 - Docker Compose v1已废弃,v2语法有变化。检查docker compose version。
学习路径建议
按顺序看视频1-3(基础),4-6(进阶),7-10(生产)。每个视频看完后,动手做一遍示例。遇到问题先看日志:docker logs container_name。