Docker零基础学习手册
📚 目录
🤔 Docker是什么?
简单比喻
想象Docker就像是**“集装箱”**:
- 传统方式:就像散装货物,不同的货物需要不同的运输工具
- Docker方式:把所有东西都装进标准集装箱,任何船都能运输
技术角度
Docker是一个容器化平台,它可以把你的应用程序和它需要的所有环境(操作系统、库文件、配置等)打包在一起,形成一个轻量级、可移植的容器。
没有Docker的世界:
开发环境 ≠ 测试环境 ≠ 生产环境
"在我电脑上好好的啊!" 😭
有了Docker的世界:
开发环境 = 测试环境 = 生产环境
"到哪里都一样!" 😊
🎯 为什么要用Docker?
解决的核心问题
1. 环境一致性问题
传统问题:
- 开发用Windows,服务器用Linux
- 本地Python 3.8,服务器Python 3.6
- 少了某个依赖库,程序跑不起来
Docker解决:
- 环境完全打包,到哪都一样
- 一次构建,到处运行
2. 部署复杂性问题
传统部署:
1. 安装操作系统
2. 安装各种依赖
3. 配置环境变量
4. 部署应用程序
5. 祈祷没有问题 🙏
Docker部署:
1. docker run my-app
2. 完成! ✅
3. 资源利用率问题
虚拟机方式:
[物理服务器]
├── [虚拟机1] → [完整OS] → [应用1]
├── [虚拟机2] → [完整OS] → [应用2]
└── [虚拟机3] → [完整OS] → [应用3]
资源浪费,启动慢
Docker方式:
[物理服务器]
└── [Docker引擎]
├── [容器1] → [应用1]
├── [容器2] → [应用2]
└── [容器3] → [应用3]
资源高效,启动快
📖 核心概念
三个核心概念
1. 镜像 (Image) - 🎯
比喻:就像程序安装包或者模板
- 只读的:不能修改
- 层级结构:一层层叠加
- 可共享:多个容器可以使用同一个镜像
镜像的构成:
基层:Ubuntu操作系统
├── 第二层:安装Java环境
├── 第三层:安装Tomcat
├── 第四层:复制应用程序
└── 第五层:设置启动命令
2. 容器 (Container) - 📦
比喻:就像正在运行的程序实例
- 可读写的:运行时可以修改
- 隔离的:每个容器相互独立
- 临时的:删除后数据就没了(除非挂载卷)
镜像 vs 容器:
镜像 = 类 (Class)
容器 = 对象 (Object)
一个镜像可以创建多个容器
就像一个类可以创建多个对象
3. 仓库 (Repository) - 🏪
比喻:就像应用商店
- Docker Hub:官方应用商店
- 私有仓库:公司内部应用商店
- 存储镜像:上传和下载镜像
常见的镜像仓库:
- Docker Hub (官方):docker.io
- 阿里云:registry.cn-hangzhou.aliyuncs.com
- 腾讯云:ccr.ccs.tencentyun.com
重要概念关系图
[开发者]
↓ 编写Dockerfile
[Dockerfile]
↓ docker build
[镜像 Image]
↓ docker run
[容器 Container]
↓ docker push
[仓库 Repository]
💻 安装Docker
Windows安装
方法1:Docker Desktop (推荐)
1. 访问 https://www.docker.com/products/docker-desktop
2. 下载 Docker Desktop for Windows
3. 双击安装包,按提示安装
4. 重启电脑
5. 启动Docker Desktop
验证安装
# 打开命令提示符或PowerShell
docker --version
# 应该显示:Docker version 20.10.x
docker run hello-world
# 如果看到 "Hello from Docker!" 说明安装成功
Linux安装 (Ubuntu)
# 1. 更新包管理器
sudo apt update
# 2. 安装必要的包
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# 3. 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 4. 添加Docker仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 5. 安装Docker
sudo apt update
sudo apt install docker-ce
# 6. 启动Docker服务
sudo systemctl start docker
sudo systemctl enable docker
# 7. 验证安装
sudo docker run hello-world
🔨 基础命令
镜像相关命令
查看镜像
# 列出本地所有镜像
docker images
# 或者使用
docker image ls
搜索镜像
# 在Docker Hub搜索镜像
docker search nginx
docker search mysql
拉取镜像
# 拉取最新版本
docker pull nginx
# 拉取指定版本
docker pull nginx:1.20
docker pull mysql:8.0
删除镜像
# 删除指定镜像
docker rmi nginx
# 删除所有未使用的镜像
docker image prune
容器相关命令
运行容器
# 基本运行
docker run nginx
# 后台运行
docker run -d nginx
# 指定名称
docker run -d --name my-nginx nginx
# 端口映射
docker run -d -p 8080:80 nginx
# 环境变量
docker run -d -e MYSQL_ROOT_PASSWORD=123456 mysql
# 挂载卷
docker run -d -v /host/path:/container/path nginx
查看容器
# 查看运行中的容器
docker ps
# 查看所有容器(包括停止的)
docker ps -a
# 查看容器详细信息
docker inspect container-name
容器操作
# 停止容器
docker stop container-name
# 启动容器
docker start container-name
# 重启容器
docker restart container-name
# 删除容器
docker rm container-name
# 强制删除运行中的容器
docker rm -f container-name
进入容器
# 进入运行中的容器
docker exec -it container-name bash
# 或者使用sh(如果容器没有bash)
docker exec -it container-name sh
# 查看容器日志
docker logs container-name
# 实时查看日志
docker logs -f container-name
系统相关命令
# 查看Docker信息
docker info
# 查看Docker版本
docker version
# 清理系统
docker system prune # 清理未使用的容器、网络、镜像
docker system prune -a # 清理所有未使用的资源
# 查看资源使用情况
docker stats
🏗️ 制作镜像
Dockerfile详解
Dockerfile是用来构建镜像的文本文件,包含了一系列的指令。
基本结构
# 指定基础镜像
FROM base-image
# 设置工作目录
WORKDIR /app
# 复制文件
COPY source destination
# 运行命令
RUN command
# 暴露端口
EXPOSE port
# 启动命令
CMD ["executable", "param1", "param2"]
常用指令详解
FROM - 指定基础镜像
# 使用官方Python镜像
FROM python:3.9
# 使用特定版本
FROM python:3.9-slim
# 使用Ubuntu作为基础
FROM ubuntu:20.04
WORKDIR - 设置工作目录
# 设置工作目录(如果不存在会自动创建)
WORKDIR /app
# 相当于 cd /app
COPY 和 ADD - 复制文件
# 复制单个文件
COPY app.py /app/
# 复制整个目录
COPY . /app/
# ADD功能更强大,但建议使用COPY
ADD file.tar.gz /app/ # 会自动解压
RUN - 运行命令
# 安装软件包
RUN apt-get update && apt-get install -y python3
# 安装Python依赖
RUN pip install -r requirements.txt
# 多个命令用 && 连接(减少层级)
RUN apt-get update && \
apt-get install -y python3 && \
rm -rf /var/lib/apt/lists/*
ENV - 设置环境变量
# 设置环境变量
ENV PYTHON_VERSION 3.9
ENV APP_HOME /app
# 使用环境变量
WORKDIR $APP_HOME
EXPOSE - 暴露端口
# 暴露端口(仅声明,实际映射需要在运行时指定)
EXPOSE 8080
EXPOSE 3000 5000
CMD 和 ENTRYPOINT - 启动命令
# CMD:可以被docker run命令覆盖
CMD ["python", "app.py"]
# ENTRYPOINT:不会被覆盖
ENTRYPOINT ["python", "app.py"]
# 组合使用
ENTRYPOINT ["python"]
CMD ["app.py"]
实战案例:Python Flask应用
1. 准备应用文件
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, Docker!'
@app.route('/health')
def health():
return {'status': 'healthy'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt
Flask==2.3.3
2. 编写Dockerfile
Dockerfile
# 使用Python官方镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY app.py .
# 暴露端口
EXPOSE 5000
# 设置启动命令
CMD ["python", "app.py"]
3. 构建和运行
# 构建镜像
docker build -t my-flask-app .
# 查看构建的镜像
docker images
# 运行容器
docker run -d -p 5000:5000 --name flask-container my-flask-app
# 测试应用
curl http://localhost:5000
# 应该返回:Hello, Docker!
# 查看容器日志
docker logs flask-container
# 进入容器
docker exec -it flask-container bash
优化Dockerfile
多阶段构建
# 构建阶段
FROM python:3.9 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 运行阶段
FROM python:3.9-slim
WORKDIR /app
# 从构建阶段复制依赖
COPY --from=builder /root/.local /root/.local
# 复制应用代码
COPY app.py .
# 确保脚本在PATH中
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]
使用.dockerignore
# .dockerignore文件
node_modules
*.log
.git
Dockerfile
.dockerignore
README.md
🎼 Docker Compose
什么是Docker Compose?
Docker Compose是用于定义和运行多容器Docker应用程序的工具。
解决的问题
单容器的限制:
- 只能运行一个服务
- 手动管理多个容器很复杂
- 容器间网络配置繁琐
Docker Compose的优势:
- 一个文件定义整个应用栈
- 一条命令启动所有服务
- 自动处理容器间网络
docker-compose.yml基础
基本结构
version: '3.8'
services:
service1:
image: image-name
ports:
- "host-port:container-port"
environment:
- ENV_VAR=value
service2:
build: .
volumes:
- ./host-path:/container-path
networks:
default:
driver: bridge
volumes:
volume-name:
实战案例:Web应用 + 数据库
项目结构
my-web-app/
├── docker-compose.yml
├── Dockerfile
├── app.py
├── requirements.txt
└── init.sql
docker-compose.yml
version: '3.8'
services:
# Web应用服务
web:
build: . # 使用当前目录的Dockerfile构建
ports:
- "5000:5000" # 端口映射
environment:
- DB_HOST=db # 数据库主机名
- DB_USER=root
- DB_PASSWORD=123456
- DB_NAME=myapp
depends_on:
- db # 依赖数据库服务
volumes:
- ./logs:/app/logs # 日志挂载
restart: unless-stopped # 重启策略
# 数据库服务
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=myapp
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql # 数据持久化
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
restart: unless-stopped
# Redis缓存服务
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
# 定义卷
volumes:
db_data:
redis_data:
# 定义网络
networks:
default:
driver: bridge
应用代码 (app.py)
from flask import Flask
import mysql.connector
import redis
import os
app = Flask(__name__)
# 数据库配置
db_config = {
'host': os.getenv('DB_HOST', 'localhost'),
'user': os.getenv('DB_USER', 'root'),
'password': os.getenv('DB_PASSWORD', ''),
'database': os.getenv('DB_NAME', 'myapp')
}
# Redis配置
redis_client = redis.Redis(host='redis', port=6379, db=0)
@app.route('/')
def hello():
return 'Hello from Docker Compose!'
@app.route('/db-test')
def db_test():
try:
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
cursor.execute("SELECT VERSION()")
version = cursor.fetchone()
conn.close()
return f'Database version: {version[0]}'
except Exception as e:
return f'Database error: {str(e)}'
@app.route('/redis-test')
def redis_test():
try:
redis_client.set('test_key', 'Hello Redis!')
value = redis_client.get('test_key').decode('utf-8')
return f'Redis value: {value}'
except Exception as e:
return f'Redis error: {str(e)}'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
requirements.txt
Flask==2.3.3
mysql-connector-python==8.1.0
redis==4.6.0
init.sql
-- 初始化数据库脚本
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');
Docker Compose常用命令
# 启动所有服务
docker-compose up
# 后台启动
docker-compose up -d
# 重新构建并启动
docker-compose up --build
# 停止所有服务
docker-compose down
# 停止并删除卷
docker-compose down -v
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs
# 查看特定服务日志
docker-compose logs web
# 实时查看日志
docker-compose logs -f
# 进入服务容器
docker-compose exec web bash
# 扩展服务(启动多个实例)
docker-compose up -d --scale web=3
🚀 实战案例
案例1:部署.NET Core API
项目结构
dotnet-api/
├── docker-compose.yml
├── Dockerfile
├── MyAPI.csproj
├── Program.cs
├── Controllers/
│ └── WeatherController.cs
└── appsettings.json
Dockerfile
# 多阶段构建
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
# 复制项目文件
COPY *.csproj ./
RUN dotnet restore
# 复制源代码并构建
COPY . ./
RUN dotnet publish -c Release -o out
# 运行时镜像
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build /src/out .
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
EXPOSE 5000
ENTRYPOINT ["dotnet", "MyAPI.dll"]
docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "5000:5000"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:5000
- ConnectionStrings__DefaultConnection=Server=db;Database=MyAPI;User=sa;Password=YourStrong@Passw0rd;
depends_on:
- db
restart: unless-stopped
db:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourStrong@Passw0rd
ports:
- "1433:1433"
volumes:
- db_data:/var/opt/mssql
restart: unless-stopped
volumes:
db_data:
案例2:前端 + 后端 + 数据库完整应用
docker-compose.yml
version: '3.8'
services:
# 前端服务
frontend:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./frontend/dist:/usr/share/nginx/html
- ./frontend/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- backend
restart: unless-stopped
# 后端API服务
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
restart: unless-stopped
# 数据库服务
db:
image: postgres:13
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- db_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
restart: unless-stopped
# 缓存服务
redis:
image: redis:alpine
volumes:
- redis_data:/data
restart: unless-stopped
# 监控服务
nginx-exporter:
image: nginx/nginx-prometheus-exporter
ports:
- "9113:9113"
command:
- -nginx.scrape-uri=http://frontend:80/nginx_status
volumes:
db_data:
redis_data:
💡 最佳实践
1. Dockerfile最佳实践
使用合适的基础镜像
# ❌ 不好的做法 - 镜像太大
FROM ubuntu:20.04
# ✅ 好的做法 - 使用精简镜像
FROM python:3.9-slim
# ✅ 更好的做法 - 使用Alpine
FROM python:3.9-alpine
减少层级数量
# ❌ 不好的做法 - 每个RUN创建一层
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y pip
RUN apt-get clean
# ✅ 好的做法 - 合并命令
RUN apt-get update && \
apt-get install -y python3 pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
利用构建缓存
# ✅ 先复制依赖文件,利用缓存
COPY requirements.txt .
RUN pip install -r requirements.txt
# 然后复制应用代码
COPY . .
使用非root用户
# 创建用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# 切换用户
USER nextjs
2. 安全最佳实践
扫描漏洞
# 使用Docker官方工具扫描
docker scan my-app:latest
# 使用第三方工具
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image my-app:latest
最小权限原则
# docker-compose.yml
services:
app:
image: my-app
user: "1001:1001" # 使用非root用户
read_only: true # 只读文件系统
tmpfs:
- /tmp
- /var/run
3. 性能优化
多阶段构建
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# 运行阶段
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["node", "index.js"]
使用.dockerignore
# .dockerignore
node_modules
*.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output
.coverage
.pytest_cache
__pycache__
4. 日志管理
配置日志驱动
services:
app:
image: my-app
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
结构化日志
import logging
import json
# 配置结构化日志
logging.basicConfig(
format='%(asctime)s %(levelname)s %(message)s',
level=logging.INFO
)
# 使用JSON格式
logger = logging.getLogger(__name__)
logger.info(json.dumps({
'event': 'user_login',
'user_id': 123,
'timestamp': '2023-10-01T10:00:00Z'
}))
❓ 常见问题
1. 容器访问问题
问题:容器无法访问外部网络
# 解决方案:检查DNS设置
docker run --dns 8.8.8.8 my-app
# 或在docker-compose.yml中设置
services:
app:
image: my-app
dns:
- 8.8.8.8
- 8.8.4.4
问题:容器间无法通信
# 确保容器在同一网络中
docker network ls
docker network inspect bridge
# 使用服务名称访问
# 在docker-compose中,可以直接使用服务名
curl http://web:5000/api/health
2. 存储和权限问题
问题:数据持久化
# 使用named volume
services:
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql # named volume
volumes:
db_data: # 定义volume
问题:权限问题
# 查看文件权限
ls -la /var/lib/docker/volumes/
# 修改权限
sudo chown -R 1001:1001 /path/to/volume
3. 性能问题
问题:容器启动慢
# 检查资源使用
docker stats
# 使用更小的基础镜像
FROM python:3.9-alpine # 而不是python:3.9
问题:构建慢
# 使用构建缓存
docker build --cache-from my-app:latest .
# 使用BuildKit
export DOCKER_BUILDKIT=1
docker build .
4. 调试技巧
进入运行中的容器
# 进入容器shell
docker exec -it container-name bash
# 如果没有bash,使用sh
docker exec -it container-name sh
# 以root用户进入
docker exec -it --user root container-name bash
查看容器详细信息
# 查看容器配置
docker inspect container-name
# 查看容器进程
docker top container-name
# 查看容器资源使用
docker stats container-name
调试网络问题
# 查看网络配置
docker network ls
docker network inspect bridge
# 测试容器间连通性
docker exec container1 ping container2
🎓 学习路径建议
第一阶段:基础入门(1-2周)
✅ 理解Docker基本概念
✅ 学会基础命令操作
✅ 会写简单的Dockerfile
✅ 能够运行和管理容器
第二阶段:实践应用(2-3周)
✅ 掌握Docker Compose
✅ 能够部署多服务应用
✅ 了解网络和存储
✅ 掌握基本的故障排查
第三阶段:深入优化(2-4周)
✅ 掌握最佳实践
✅ 性能优化和安全加固
✅ CI/CD集成
✅ 监控和日志管理
第四阶段:生产应用(持续)
✅ 容器编排(Kubernetes)
✅ 微服务架构
✅ DevOps实践
✅ 持续学习新特性
📚 推荐资源
官方文档
实践平台
学习书籍
- 《Docker技术入门与实战》
- 《Docker容器与容器云》
视频教程
- Docker官方YouTube频道
- 各大技术平台的Docker教程
🎯 总结
Docker是现代应用部署的重要技术,掌握Docker可以:
- 提高开发效率:环境一致性,避免"在我机器上正常"问题
- 简化部署流程:一键部署,减少运维复杂度
- 提升资源利用率:容器化应用占用资源更少
- 增强应用可移植性:一次构建,到处运行
学习Docker需要理论与实践结合:
- 先理解概念和原理
- 然后动手实践操作
- 最后在实际项目中应用
记住:Docker不仅仅是工具,更是一种思维方式的转变!
Happy Dockerizing! 🐳