我和Docker的5年:从”这是什么鬼”到”离不开它”的进化之路
> 从排斥Docker到成为Docker布道师的真实经历
>
> **
直到2021年,我遇到了Docker。
我的故事:第一次听说Docker
时间:2021年6月的一个下午
地点:北京某互联网公司会议室
项目经理走过来,说:”小陈,这个项目要用Docker部署。”
我的第一反应:”Docker?那是什么鬼?为什么要用这么复杂的东西?”
那时候的我,觉得Docker是多余的:
- “我直接在服务器上装环境不就行了?”
- “虚拟机也可以解决隔离问题啊?”
- “学新工具太麻烦了…”
直到那一天…
我们公司接了一个大项目,需要在3个月内开发10个微服务。
传统的做法:
1. 每个服务独立部署
2. 手动配置环境
3. 处理依赖冲突
4. 花费大量时间在环境配置上
结果:前2个月都在配置环境,只有1个月在写代码…
项目经理说:”必须用Docker。”
我被迫开始学习Docker。
3个月后,我的想法完全改变了。
第一部分:Docker到底是什么?(小白也能懂的版本)
我踩过的第一个坑
我的错误理解:
- “Docker就是虚拟机?”
- “Docker会让应用变慢?”
- “Docker配置太复杂了?”
真相:
#### Docker vs 虚拟机
传统虚拟机:
应用 → 操作系统 → Hypervisor → 硬件
(每个虚拟机都有完整的操作系统,占用大量资源)
Docker容器:
应用 → Docker引擎 → 操作系统 → 硬件
(容器共享操作系统内核,轻量级)
真实案例:
场景:我们需要运行10个Node.js应用
传统虚拟机方案:
- 每个虚拟机:2GB内存
- 总内存需求:10 × 2GB = 20GB
- 启动时间:每个虚拟机1-2分钟
- 总启动时间:10-20分钟
Docker容器方案:
- 每个容器:100MB内存
- 总内存需求:10 × 100MB = 1GB
- 启动时间:每个容器1-2秒
- 总启动时间:10-20秒
效果对比:
- 内存节省:95%(20GB → 1GB)
- 启动速度:从每秒17个请求提升到每秒1020个请求,大约60倍(20分钟 → 20秒)
Docker的三大核心概念
#### 1. 镜像(Image)- 应用的”模板”
比喻:镜像就像是一个”蛋糕模具”
- 你可以制作无数个相同的”蛋糕”(容器)
- 每个蛋糕都一模一样
- 修改模具不会影响已经做好的蛋糕
真实案例:
我们团队的Node.js应用镜像:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
这个镜像的好处:
1. 轻量级:使用alpine版本,只有50MB
2. 一致性:所有环境都使用同一个镜像
3. 可复现:任何时候都能创建相同的容器
#### 2. 容器(Container)- 运行的应用实例
比喻:容器就是用”模具”(镜像)做出来的”蛋糕”
真实案例:
# 创建并运行容器
docker run -d
--name my-app
-p 3000:3000
-e NODE_ENV=production
my-app:1.0.0
查看运行中的容器
docker ps
查看容器日志
docker logs my-app
进入容器内部
docker exec -it my-app sh
#### 3. 仓库(Registry)- 镜像的”仓库”
比喻:就像GitHub是代码的仓库,Docker Registry是镜像的仓库
常用仓库:
- Docker Hub(官方)
- 阿里云容器镜像服务
- 腾讯云容器镜像服务
第二部分:Docker常用命令实战
我总结的20个最常用命令
#### 镜像操作(5个命令)
1. 搜索镜像
# 搜索Node.js镜像
docker search node
搜索官方镜像
docker search node --filter is-official=true
我的经验:
- 优先选择官方镜像(OFFICIAL)
- 查看Stars数量(受欢迎程度)
- 选择alpine版本(体积小)
真实案例:
# 错误选择
node:latest # 1GB+
node:18 # 900MB
正确选择
node:18-alpine # 120MB(节省87%空间)
2. 拉取镜像
# 拉取最新版本
docker pull node:18-alpine
拉取指定版本
docker pull node:18.17.0-alpine
拉取所有架构版本
docker pull --platform linux/amd64,linux/arm64 node:18-alpine
3. 查看本地镜像
# 列出所有镜像
docker images
查看镜像详细信息
docker inspect node:18-alpine
查看镜像历史层
docker history node:18-alpine
真实案例:
我们项目中有100个镜像,用这个命令找出占用空间最大的镜像:
# 按大小排序显示镜像
docker images --format "table {{.Repository}}t{{.Tag}}t{{.Size}}" |
sort -k3 -h |
head -20
发现问题:
- python:3.9(1GB)→ 改用python:3.9-alpine(50MB)
- nginx:latest(133MB)→ 改用nginx:alpine(40MB)
- node:18(900MB)→ 改用node:18-alpine(120MB)
结果:总镜像大小从50GB降到5GB(节省90%)
4. 删除镜像
# 删除单个镜像
docker rmi node:18-alpine
删除所有未使用的镜像
docker image prune -a
删除所有悬空镜像
docker image prune
强制删除所有镜像
docker rmi $(docker images -q)
我的教训:
❌ 错误操作:
# 我曾经误删了生产环境的镜像
docker rmi $(docker images -q)
导致所有服务无法启动
花了2个小时重新拉取镜像
✅ 正确做法:
# 只删除悬空镜像(dangling images)
docker image prune
或者明确指定要删除的镜像
docker rmi $(docker images | grep "" | awk "{print $3}")
5. 构建镜像
# 基础构建
docker build -t myapp:1.0.0 .
指定Dockerfile
docker build -f Dockerfile.prod -t myapp:1.0.0 .
不使用缓存
docker build --no-cache -t myapp:1.0.0 .
多阶段构建
docker build --target builder -t myapp:builder .
真实案例:多阶段构建优化
优化前(1.2GB):
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "server.js"]
问题:
- 包含开发依赖(devDependencies)
- 包含构建工具
- 镜像体积大
优化后(150MB):
# 第一阶段:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
第二阶段:运行
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "server.js"]
效果:
- 镜像大小:1.2GB → 150MB(-87.5%)
- 拉取时间:10分钟 → 1分钟(-90%)
- 部署速度:显著提升
#### 容器操作(10个命令)
6. 运行容器
# 基础运行
docker run -d nginx:alpine
指定名称
docker run -d --name my-nginx nginx:alpine
端口映射
docker run -d -p 8080:80 nginx:alpine
挂载卷
docker run -d -v /host/path:/container/path nginx:alpine
环境变量
docker run -d -e NODE_ENV=production node:18-alpine
查看容器日志
docker logs -f my-nginx
进入容器
docker exec -it my-nginx sh
真实案例:一键启动开发环境
# 启动完整的开发环境
docker run -d
--name mysql
-e MYSQL_ROOT_PASSWORD=secret
-e MYSQL_DATABASE=myapp
-v mysql-data:/var/lib/mysql
-p 3306:3306
mysql:8.0
docker run -d
--name redis
-v redis-data:/data
-p 6379:6379
redis:alpine
docker run -d
--name myapp
--link mysql:mysql
--link redis:redis
-p 3000:3000
-e DB_HOST=mysql
-e REDIS_HOST=redis
myapp:1.0.0
效果:
- 5分钟启动完整环境
- 所有服务隔离运行
- 数据持久化保存
7. 查看容器
# 查看运行中的容器
docker ps
查看所有容器(包括停止的)
docker ps -a
查看容器详细信息
docker inspect my-nginx
查看容器资源使用
docker stats my-nginx
查看容器进程
docker top my-nginx
真实案例:性能排查
# 查看所有容器的资源使用
docker stats --no-stream
输出示例:
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
mysql 5.50% 512MiB / 8GiB 6.25% 1.2GB / 3GB 50MB / 200MB
redis 1.20% 64MiB / 8GiB 0.78% 800MB / 1GB 10MB / 50MB
myapp 15.8% 512MiB / 8GiB 6.25% 2GB / 1.5GB 100MB / 500MB
发现问题:
- myapp占用CPU过高(15.8%)
- 内存使用正常
- 网络I/O较高
优化措施:
- 增加容器数量(水平扩展)
- 优化代码(减少CPU使用)
- 使用cgroup限制资源
8. 停止和重启容器
# 停止容器
docker stop my-nginx
启动已停止的容器
docker start my-nginx
重启容器
docker restart my-nginx
强制停止
docker kill my-nginx
暂停容器
docker pause my-nginx
恢复暂停的容器
docker unpause my-nginx
真实案例:优雅停机
# 错误做法:直接kill
docker kill myapp
问题:正在处理的请求被中断,数据可能丢失
正确做法:优雅停机
docker stop myapp
发送SIGTERM信号,让应用优雅关闭
等待10秒后强制停止
在应用中处理SIGTERM:
// Node.js示例
process.on('SIGTERM', () => {
console.log('Received SIGTERM, closing gracefully...');
// 停止接受新请求
server.close(() => {
console.log('Closed all connections');
process.exit(0);
});
// 10秒后强制退出
setTimeout(() => {
console.log('Forcing exit...');
process.exit(1);
}, 10000);
});
9. 删除容器
# 删除单个容器
docker rm my-nginx
删除运行中的容器(需要先停止)
docker rm -f my-nginx
删除所有停止的容器
docker container prune
删除所有容器(包括运行中的)
docker rm -f $(docker ps -aq)
我的教训:
❌ 错误操作:
# 误删了所有容器,包括生产环境
docker rm -f $(docker ps -aq)
导致服务中断,用户无法访问
花了1小时恢复服务
✅ 正确做法:
# 只删除停止的容器
docker container prune
或者在删除前确认
docker ps -a # 先查看
docker rm # 明确指定要删除的容器
10. 容器日志管理
# 查看容器日志
docker logs my-nginx
实时查看日志
docker logs -f my-nginx
查看最后100行
docker logs --tail 100 my-nginx
查看最近10分钟的日志
docker logs --since 10m my-nginx
显示时间戳
docker logs -t my-nginx
真实案例:日志分析
# 查找错误日志
docker logs myapp 2>&1 | grep -i error
统计错误数量
docker logs myapp 2>&1 | grep -i error | wc -l
查看最近的错误
docker logs --tail 1000 myapp 2>&1 | grep -i error | tail -20
导出日志到文件
docker logs myapp > myapp.log 2>&1
优化建议:
# 限制日志文件大小
docker run -d
--log-opt max-size=10m
--log-opt max-file=3
myapp:1.0.0
配置日志驱动
docker run -d
--log-driver json-file
--log-opt max-size=10m
--log-opt max-file=3
myapp:1.0.0
#### 网络操作(3个命令)
11. 查看网络
# 列出所有网络
docker network ls
查看网络详细信息
docker network inspect bridge
查看容器连接的网络
docker inspect my-nginx | grep -A 20 "Networks"
12. 创建网络
# 创建bridge网络
docker network create my-network
创建overlay网络(用于Swarm)
docker network create --driver overlay my-overlay-network
指定子网
docker network create
--subnet=192.168.0.0/16
--gateway=192.168.0.1
my-network
真实案例:微服务网络隔离
# 创建前端网络
docker network create frontend-network
创建后端网络
docker network create backend-network
创建数据库网络
docker network create database-network
启动前端容器(连接到frontend和backend)
docker run -d
--name frontend
--network frontend-network
myapp-frontend:1.0.0
启动后端容器(连接到backend和database)
docker run -d
--name backend
--network backend-network
myapp-backend:1.0.0
启动数据库容器(只连接到database)
docker run -d
--name mysql
--network database-network
mysql:8.0
连接容器到多个网络
docker network connect backend-network frontend
docker network connect database-network backend
效果:
- 前端不能直接访问数据库(安全)
- 后端可以访问数据库
- 网络隔离,安全可控
13. 网络连接和断开
# 连接容器到网络
docker network connect my-network my-nginx
断开容器网络
docker network disconnect my-network my-nginx
删除网络
docker network rm my-network
#### 存储操作(2个命令)
14. 查看卷
# 列出所有卷
docker volume ls
查看卷详细信息
docker volume inspect my-volume
查看未使用的卷
docker volume ls -f dangling=true
15. 创建和管理卷
# 创建卷
docker volume create my-volume
删除未使用的卷
docker volume prune
删除指定卷
docker volume rm my-volume
使用卷
docker run -d
-v my-volume:/data
nginx:alpine
真实案例:数据库数据持久化
# 创建数据卷
docker volume create mysql-data
启动MySQL,使用持久化卷
docker run -d
--name mysql
-e MYSQL_ROOT_PASSWORD=secret
-v mysql-data:/var/lib/mysql
-p 3306:3306
mysql:8.0
即使删除容器,数据仍然保留
docker rm -f mysql
重新启动,数据还在
docker run -d
--name mysql
-e MYSQL_ROOT_PASSWORD=secret
-v mysql-data:/var/lib/mysql
-p 3306:3306
mysql:8.0
第三部分:Docker Compose – 多容器编排神器
我的Docker Compose进化史
第一阶段:手动启动每个容器
# 启动MySQL
docker run -d --name mysql
-e MYSQL_ROOT_PASSWORD=secret
-e MYSQL_DATABASE=myapp
-v mysql-data:/var/lib/mysql
-p 3306:3306
mysql:8.0
启动Redis
docker run -d --name redis
-v redis-data:/data
-p 6379:6379
redis:alpine
启动应用
docker run -d --name myapp
--link mysql:mysql
--link redis:redis
-p 3000:3000
-e DB_HOST=mysql
-e REDIS_HOST=redis
myapp:1.0.0
问题:
- 命令太长,容易出错
- 无法统一管理
- 启动顺序容易出错
第二阶段:使用Docker Compose
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
networks:
- backend
redis:
image: redis:alpine
container_name: redis
volumes:
- redis-data:/data
ports:
- "6379:6379"
networks:
- backend
myapp:
image: myapp:1.0.0
container_name: myapp
ports:
- "3000:3000"
environment:
DB_HOST: mysql
REDIS_HOST: redis
depends_on:
- mysql
- redis
networks:
- backend
- frontend
volumes:
mysql-data:
redis-data:
networks:
backend:
frontend:
一键启动:
# 启动所有服务
docker-compose up -d
查看服务状态
docker-compose ps
查看日志
docker-compose logs -f
停止所有服务
docker-compose down
停止并删除卷
docker-compose down -v
效果:
- ✅ 命令简洁
- ✅ 统一管理
- ✅ 启动顺序正确
- ✅ 易于维护
Docker Compose高级技巧
1. 环境变量覆盖
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
environment:
- NODE_ENV=${NODE_ENV:-development}
- DB_HOST=${DB_HOST:-mysql}
- DB_PORT=${DB_PORT:-3306}
# .env文件
NODE_ENV=production
DB_HOST=prod-your-domain.com
DB_PORT=3306
2. 多环境配置
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
env_file:
- .env.${ENV:-development}
# 开发环境
ENV=development docker-compose up -d
生产环境
ENV=production docker-compose up -d
3. 健康检查
version: '3.8'
services:
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
myapp:
image: myapp:1.0.0
depends_on:
mysql:
condition: service_healthy
真实案例:生产环境部署
# docker-compose.prod.yml
version: '3.8'
services:
# 负载均衡
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app1
- app2
- app3
# 应用实例1
app1:
image: myapp:1.0.0
environment:
- NODE_ENV=production
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
restart: unless-stopped
# 应用实例2
app2:
image: myapp:1.0.0
environment:
- NODE_ENV=production
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
restart: unless-stopped
# 应用实例3
app3:
image: myapp:1.0.0
environment:
- NODE_ENV=production
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
restart: unless-stopped
效果:
- 3个应用实例
- 负载均衡
- 资源限制
- 自动重启
- 高可用
第四部分:Docker性能优化实战
我遇到的性能问题
问题:Docker容器运行缓慢
场景:我们的Node.js应用在容器中运行比直接在宿主机上慢30%
排查过程:
1. 检查资源使用
docker stats
发现CPU使用率正常,但I/O等待时间长
2. 检查文件系统
# 查看容器文件系统
docker exec myapp df -h
发现写入速度很慢
3. 优化方案
方案1:使用Volume绕过UnionFS
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
volumes:
# 使用命名卷绕过UnionFS
- app-data:/app/node_modules
- ./src:/app/src:ro
volumes:
app-data:
效果:
- I/O性能提升3倍
- 应用响应速度提升2倍
方案2:优化Dockerfile
优化前:
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "server.js"]
问题:
- 每次代码变更都要重新安装依赖
- 层缓存失效
- 构建时间长
优化后:
FROM node:18-alpine
WORKDIR /app
先复制package.json
COPY package*.json ./
安装依赖(这层会被缓存)
RUN npm ci --only=production
再复制源代码
COPY . .
构建应用
RUN npm run build
非root用户运行
RUN addgroup -g 1001 -S nodejs &&
adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
效果:
- 构建时间:5分钟 → 1分钟(-80%)
- 镜像大小:1GB → 150MB(-85%)
- 安全性:使用非root用户
第五部分:Docker安全最佳实践
我遇到的安全问题
事件1:容器逃逸漏洞
时间:2022年3月
问题:CVE-2022-0492漏洞
影响:
- 攻击者可以从容器逃逸到宿主机
- 获取宿主机的root权限
- 访问所有容器和数据
解决方案:
# 1. 更新Docker版本到最新
yum update docker-ce
2. 使用非root用户运行容器
docker run -d
--user 1001:1001
myapp:1.0.0
3. 限制容器权限
docker run -d
--cap-drop ALL
--cap-add CHOWN
--cap-add SETGID
--cap-add SETUID
myapp:1.0.0
4. 使用AppArmor/SELinux
docker run -d
--security-opt apparmor=docker-default
myapp:1.0.0
事件2:敏感信息泄露
问题:在Dockerfile中硬编码密码
错误做法:
FROM node:18
ENV DB_PASSWORD=secret123
ENV API_KEY=abc123def456
COPY . .
CMD ["node", "server.js"]
问题:
- 密码明文存储在镜像中
- 任何人都能通过docker inspect看到
- 镜像推送到仓库后,密码泄露
正确做法:
FROM node:18
使用环境变量文件
COPY .env.example /app/.env.example
在运行时注入敏感信息
CMD ["sh", "-c", "node server.js"]
# 运行时注入
docker run -d
-e DB_PASSWORD=$(cat /run/secrets/db_password)
-e API_KEY=$(cat /run/secrets/api_key)
myapp:1.0.0
或者使用Docker Secrets(Swarm模式):
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
第六部分:我的Docker快捷键清单
最常用的10个命令
| 命令 | 功能 | 使用频率 | 节省时间 |
|——|——|———|———|
| `docker ps` | 查看运行容器 | 50次/天 | 5分钟/天 |
| `docker logs -f` | 实时查看日志 | 20次/天 | 10分钟/天 |
| `docker exec -it` | 进入容器 | 10次/天 | 5分钟/天 |
| `docker-compose up -d` | 启动服务 | 5次/天 | 10分钟/天 |
| `docker-compose down` | 停止服务 | 5次/天 | 2分钟/天 |
| `docker images` | 查看镜像 | 3次/天 | 1分钟/天 |
| `docker rmi` | 删除镜像 | 2次/天 | 1分钟/天 |
| `docker build -t` | 构建镜像 | 1次/天 | 15分钟/天 |
| `docker stats` | 查看资源使用 | 10次/天 | 5分钟/天 |
| `docker system prune` | 清理未使用资源 | 1次/周 | 30分钟/周 |
每天节省:约54分钟
每月节省:约18小时
每年节省:约216小时
第七部分:Docker实战案例
案例1:一键启动开发环境
场景:新同事入职,需要在1小时内配置好开发环境
传统做法:
- 安装Node.js、Python、MySQL、Redis、MongoDB…
- 配置各种环境变量
- 解决版本冲突
- 时间:4-8小时
Docker方案:
# docker-compose.dev.yml
version: '3.8'
services:
# 前端
frontend:
image: node:18-alpine
working_dir: /app
volumes:
- ./frontend:/app
ports:
- "3000:3000"
command: npm run dev
environment:
- WATCHPACK_POLLING=true
# 后端
backend:
image: node:18-alpine
working_dir: /app
volumes:
- ./backend:/app
ports:
- "4000:4000"
command: npm run dev
depends_on:
- mysql
- redis
# MySQL
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: devdb
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
# Redis
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
# MongoDB
mongodb:
image: mongo:6
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: secret
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
# phpMyAdmin
phpmyadmin:
image: phpmyadmin/phpmyadmin
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8080:80"
depends_on:
- mysql
volumes:
mysql-data:
redis-data:
mongo-data:
一键启动:
# 克隆代码
git clone https://github.com/company/project.git
cd project
启动所有服务
docker-compose -f docker-compose.dev.yml up -d
等待30秒...
完成!
效果:
- 配置时间:8小时 → 1分钟(-99.8%)
- 成功率:100%
- 环境一致性:100%
案例2:CI/CD自动化部署
场景:每次代码提交后自动构建、测试、部署
Jenkins Pipeline:
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
docker.build("myapp:${BUILD_NUMBER}")
}
}
}
stage('Test') {
steps {
script {
docker.image("myapp:${BUILD_NUMBER}") {
sh 'npm test'
}
}
}
}
stage('Push') {
steps {
script {
docker.withRegistry("https://your-domain.com", "registry-credentials") {
docker.image("myapp:${BUILD_NUMBER}").push()
}
}
}
}
stage('Deploy') {
steps {
script {
sh """
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d
"""
}
}
}
}
post {
success {
echo '部署成功!'
}
failure {
echo '部署失败,回滚...'
sh 'docker-compose -f docker-compose.prod.yml down'
}
}
}
效果:
- 自动化部署流程
- 快速回滚能力
- 部署成功率:99%
第八部分:Docker常见问题和解决方案
问题1:容器无法访问宿主机服务
场景:容器需要访问宿主机的MySQL数据库
错误做法:
# 使用127.0.0.1(错误)
docker run -d
-e DB_HOST=127.0.0.1
myapp:1.0.0
问题:容器中的127.0.0.1指向容器自己,不是宿主机
正确做法:
Linux:
# 使用host.docker.internal(Docker 20.10+)
docker run -d
-e DB_HOST=host.docker.internal
myapp:1.0.0
或使用宿主机IP
docker run -d
-e DB_HOST=172.17.0.1
myapp:1.0.0
或使用host网络模式
docker run -d
--network host
myapp:1.0.0
Mac/Windows:
# 使用host.docker.internal
docker run -d
-e DB_HOST=host.docker.internal
myapp:1.0.0
问题2:时区不正确
场景:容器中的时间和宿主机不一致
解决方案:
FROM node:18-alpine
设置时区
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tzdata
或挂载时区文件
VOLUME /etc/timezone:/etc/timezone:ro
VOLUME /etc/localtime:/etc/localtime:ro
CMD ["node", "server.js"]
# 运行时设置
docker run -d
-e TZ=Asia/Shanghai
-v /etc/timezone:/etc/timezone:ro
-v /etc/localtime:/etc/localtime:ro
myapp:1.0.0
问题3:文件权限问题
场景:容器中创建的文件在宿主机上无法修改
解决方案:
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
user: "${UID}:${GID}"
volumes:
- ./data:/app/data
# 运行时指定用户ID
UID=$(id -u) GID=$(id -g) docker-compose up -d
或在Dockerfile中:
FROM node:18-alpine
创建非root用户
RUN addgroup -g 1001 -S nodejs &&
adduser -S nodejs -u 1001
WORKDIR /app
切换到非root用户
USER nodejs
COPY --chown=nodejs:nodejs . .
CMD ["node", "server.js"]
第九部分:Docker进阶技巧
技巧1:多阶段构建实战
场景:构建Go应用
传统做法(500MB):
FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o myapp
EXPOSE 8080
CMD ["./myapp"]
多阶段构建(10MB):
# 第一阶段:构建
FROM golang:1.21-alpine AS builder
WORKDIR /app
复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
复制源代码
COPY . .
构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o myapp .
第二阶段:运行
FROM alpine:latest
安装CA证书(如果需要HTTPS)
RUN apk --no-cache add ca-certificates
WORKDIR /root/
从构建阶段复制二进制文件
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
效果:
- 镜像大小:500MB → 10MB(-98%)
- 拉取时间:5分钟 → 10秒(-97%)
- 安全性:只包含运行时需要的文件
技巧2:健康检查和自愈
场景:容器崩溃后自动重启
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:1.0.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
order: start-first
rollback_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
效果:
- 容器崩溃自动重启
- 不健康的容器自动替换
- 零停机部署
- 滚动更新
技巧3:资源限制
场景:防止容器占用过多资源
version: '3.8'
services:
myapp:
image: myapp:1.0.0
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
效果:
- 限制CPU使用:最多50%
- 限制内存使用:最多512MB
- 保证资源:至少25% CPU和256MB内存
- 防止资源耗尽
第十部分:我的Docker学习路径
第1个月:基础入门
学习内容:
- Docker核心概念(镜像、容器、仓库)
- 常用命令(run、ps、logs、exec)
- Dockerfile基础
- Docker Compose基础
实战项目:
- 使用Docker运行WordPress
- 使用Docker运行Node.js应用
- 编写简单的Dockerfile
时间投入:每天1小时
第2个月:深入实践
学习内容:
- Docker网络
- Docker存储
- 多阶段构建
- 性能优化
实战项目:
- 构建多容器应用
- 优化镜像大小
- 配置生产环境
时间投入:每天2小时
第3个月:高级应用
学习内容:
- Docker安全
- CI/CD集成
- 容器编排
- 监控和日志
实战项目:
- 搭建CI/CD流程
- 实现自动化部署
- 配置监控告警
时间投入:每天3小时
结语:Docker改变了我的工作方式
5年总结
从Docker小白到Docker布道师,这5年我经历了:
技能提升:
- ✅ 熟练掌握50+个Docker命令
- ✅ 编写了100+个Dockerfile
- ✅ 使用Docker部署了50+个项目
- ✅ 帮助100+人学习Docker
效率提升:
- 环境配置时间:8小时 → 1分钟(-99.8%)
- 部署时间:2小时 → 5分钟(-95.8%)
- 故障恢复时间:4小时 → 10分钟(-95.8%)
心态变化:
- 之前:排斥新技术,觉得”够用就好”
- 现在:拥抱新技术,相信”工具的力量”
给你的建议
如果你想学习Docker,我的建议是:
第1周:
- 安装Docker
- 运行第一个容器(`docker run hello-world`)
- 学习基础命令(ps、images、logs、exec)
第1个月:
- 学习Dockerfile
- 学习Docker Compose
- 完成第一个实战项目
第3个月:
- 学习网络和存储
- 学习性能优化
- 学习安全最佳实践
持续:
- 关注Docker更新
- 学习Kubernetes
- 分享经验给团队
下一篇文章预告
在下一篇文章中,我将分享:
- Kubernetes实战:如何管理100+个容器
- 微服务架构:如何拆分大型应用
- DevOps实践:如何实现持续交付
敬请期待!
互动环节
你是否有类似的经历?
- 你在学习Docker时遇到过什么困难?
- 你有什么独特的Docker技巧?
- 你想了解哪方面的深入内容?
欢迎在评论区分享你的故事和经验,起成长!
如果这篇文章对你有帮助
- 请点赞?让更多人看到
- 请收藏⭐方便以后查看
- 请转发?帮助有需要的朋友
- 请关注我获取更多优质内容
相关文章:
- [Docker网络详解](/docker-network-guide)
- [Docker存储最佳实践](/docker-storage-guide)
- [Kubernetes入门教程](/kubernetes-getting-started)
推荐资源:
- [Docker官方文档](https://docs.docker.com/)
- [Docker实战书籍](https://www.docker.com/books)
- [Awesome Docker](https://github.com/veggiemonk/awesome-docker)
作者简介:陈存利,全栈开发者,Docker布道师,5年容器化经验,现就职于某互联网公司。热爱分享,运营技术博客,GitHub Star 2.5k+。
社交媒体:
- GitHub:github.com/chencl
- Twitter:@chencl_dev
- 微信:chen_cl_2026
版权声明:本文原创,转载请注明出处。禁止商业用途。
文章元信息:
- 字数:7,500字
- 更新日期:2026-03-19
- 系列文章:第1篇(Docker实战系列共3篇)
- 标签:#Docker #容器 #DevOps #微服务 #实战 #运维
SEO优化:
- 关键词:Docker, Docker教程, Docker命令, 容器技术, 虚拟化, DevOps, CI/CD
- 标题标签:Docker教程, Docker命令, 容器技术, DevOps实践