主题
Docker容器化部署实战
课程简介
本课程通过"高鲲教育博客平台"项目为主线,将前面6节Docker基础课程的知识融会贯通。你将扮演一名新入职的运维工程师小王,从接到任务、思考方案、选择Docker、到完整平台容器化部署。
前置要求:完成83-88课Docker基础课程的学习,准备好Linux环境(推荐Rocky Linux 9或openEuler 24.03)。
第一阶段:接到任务——Nginx多版本部署
业务场景
小王入职「高鲲教育」第一天,CTO给他布置了第一个任务:
"小王啊,我们业务需要测试新旧版本Nginx的性能差异。你需要在同一台服务器上同时运行Nginx 1.20(旧业务)和最新版(新业务)。"
小王接到任务后,开始思考:如何在同一台机器上部署两个不同版本的Nginx?
小王的思考:软件安装的四种方法
| 安装方式 | 描述 | 适用场景 |
|---|---|---|
| 包管理器安装 | dnf install nginx | 快速安装,但版本受限 |
| 源码编译安装 | 下载源码,自己编译 | 深度定制,但最复杂 |
| 二进制包部署 | 下载官方预编译包 | 需要特定版本,需手动配置 |
| Docker容器部署 | 使用Docker镜像运行 | 环境隔离,多版本并存 |
小王决定逐一分析这四种方式。
方案一:包管理器安装
bash
sudo dnf install nginx但很快发现了问题:
bash
# 问题1:只能安装一个版本
sudo dnf install nginx-1.20 # 安装旧版本
sudo dnf install nginx # 再安装新版本会冲突!
# 问题2:版本受限
# 仓库里的版本可能不是自己想要的
# 问题3:配置文件散落各处
/etc/nginx/nginx.conf # 主配置
/etc/nginx/conf.d/ # 子配置
/var/log/nginx/ # 日志
/usr/share/nginx/html/ # 网页文件
# 两个版本会互相覆盖!结论:❌ 包管理器安装无法实现多版本并存。
方案二:源码编译安装
小王想:"那我下载源码自己编译,安装到不同目录总行了吧?"
他查了一下源码编译的步骤:
bash
# 安装编译依赖
sudo dnf install -y gcc gcc-c++ make pcre pcre-devel zlib zlib-devel openssl openssl-devel
# 下载、配置、编译、安装
wget http://nginx.org/download/nginx-1.20.2.tar.gz
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.20.2.tar.gz && cd nginx-1.20.2
./configure \
--prefix=/usr/local/nginx-1.20 \
--with-http_ssl_module \
--with-http_v2_module \
--with-threads
make -j$(nproc) && sudo make install
# 重复以上步骤安装另一个版本...小王发现源码编译的问题:
| 问题 | 说明 |
|---|---|
| 依赖复杂 | 需要安装gcc、pcre、zlib等开发库 |
| 编译耗时 | 每次编译要几分钟到十几分钟 |
| 参数复杂 | ./configure参数众多,容易出错 |
| 多版本困难 | 两个版本都要完整编译一遍 |
| 端口冲突 | 两个Nginx都要用80端口,需要手动修改配置 |
| 进程管理 | 需要手动写systemd启动脚本 |
结论:❌ 源码编译虽然可行,但太复杂、太耗时,不适合快速测试场景。
方案三:二进制包部署
小王又想:"官方应该提供预编译的二进制包吧?"
确实,Nginx官方提供了预编译包,但小王发现:
bash
# 下载并解压到不同目录
wget http://nginx.org/download/nginx-1.20.2.tar.gz
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.20.2.tar.gz -C /opt/nginx-1.20
tar -xzf nginx-1.24.0.tar.gz -C /opt/nginx-1.24
# 手动修改配置文件,避免端口冲突
vim /opt/nginx-1.20/conf/nginx.conf
# 手动创建systemd服务文件
vim /etc/systemd/system/nginx-1.20.service二进制部署的问题:
| 问题 | 二进制部署 | 理想方案 |
|---|---|---|
| 端口冲突 | 需要手动修改配置文件 | 希望内部都用80端口 |
| 进程管理 | 需要手动编写systemd脚本 | 希望一条命令启动 |
| 配置隔离 | 配置文件在同一台机器,容易混淆 | 希望配置独立 |
| 依赖处理 | 需要确保系统库版本兼容 | 希望依赖自带 |
| 环境清理 | 卸载时需要手动删除多个目录 | 希望一键清理 |
结论:⚠️ 二进制部署可行,但配置繁琐,管理复杂。
方案四:Docker容器部署
小王突然想到:"对了,公司不是一直在推容器化吗?用Docker试试!"
他快速分析了一下Docker的优势:
| 优势 | 说明 |
|---|---|
| 环境隔离 | 每个容器是独立的运行环境,互不干扰 |
| 版本并存 | 同一台机器可同时运行多个版本的Nginx |
| 端口映射 | 容器内部都用80端口,通过-p映射到宿主机不同端口 |
| 零污染 | 容器删除后,宿主机环境完全干净 |
| 快速部署 | 秒级启动,无需编译安装 |
| 配置集中 | 每个容器的配置独立管理 |
结论:✅ Docker部署完美契合需求!
Docker的核心价值:环境隔离,零污染。就像给你独立的画板,画错了直接扔掉,墙壁依然干净。
小王的决定
经过对比分析,小王决定采用Docker容器部署方案:
- ✅ 可以同时运行多个版本的Nginx
- ✅ 无需处理复杂的依赖和编译
- ✅ 端口映射灵活,不会冲突
- ✅ 测试完成后可以快速清理环境
- ✅ 符合公司的容器化战略
但服务器上还没有Docker环境,小王需要先安装Docker。
第二阶段:Docker安装与配置
小王的尝试
小王决定先试试系统自带的包管理器:
bash
sudo dnf install -y docker
sudo systemctl start docker
docker --version安装成功了!但小王发现版本号显示的是 18.09,这让他有些疑惑。上网搜索后,他发现这个版本存在多个已知Bug,而且不支持很多Docker新特性。
小王决定安装最新版本的Docker CE。
从阿里云镜像站安装Docker CE
小王从阿里云开源镜像站找到了Docker CE的安装方法。由于服务器是Rocky Linux 9,需要对官方方法稍作适配。
bash
# 1. 卸载旧版本Docker
sudo dnf remove -y docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine
# 2. 安装必要工具
sudo dnf install -y dnf-utils
# 3. 添加阿里云Docker CE软件源
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 适配Rocky Linux 9(将$releasever替换为9)
sudo sed -i 's/\$releasever/9/g' /etc/yum.repos.d/docker-ce.repo
# 5. 清理缓存并刷新
sudo dnf clean all && sudo dnf makecache
# 6. 安装Docker CE及相关组件
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 7. 启动Docker并设置开机自启
sudo systemctl enable --now docker
# 8. 验证安装
docker --version
docker compose version小王看到版本号变成了 27.x,满意地点了点头。
配置镜像加速
接下来需要拉取Nginx镜像,但小王发现直接从Docker Hub拉取速度很慢,甚至超时失败。这是国内网络环境的常见问题。
国内镜像加速器(2025年更新):
| 名称 | 加速地址 |
|---|---|
| 毫秒镜像 | https://docker.1ms.run |
| 轩辕镜像 | https://docker.xuanyuan.me |
| DaoCloud | https://docker.m.daocloud.io |
| 1Panel官方 | https://docker.1panel.live |
小王配置了镜像加速:
bash
# 写入镜像加速配置
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me",
"https://docker.m.daocloud.io",
"https://docker.1panel.live"
]
}
EOF
# 重新加载配置并重启Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# 验证配置是否生效
docker info | grep -A 5 "Registry Mirrors"放行防火墙端口
公司服务器开启了防火墙,需要放行常用端口:
bash
sudo firewall-cmd --permanent --add-port={80,8080,8088,3306,6379}/tcp
sudo firewall-cmd --reload
sudo systemctl restart docker第三阶段:动手实践——部署多版本Nginx
Docker环境准备就绪,小王开始动手部署Nginx。
1. 拉取镜像
bash
# 拉取最新版本
docker pull nginx:latest
# 拉取指定旧版本
docker pull nginx:1.20
# 查看本地镜像
docker images2. 启动两个版本的Nginx
bash
# 新版本Nginx - 占用宿主机80端口
docker run -d \
--name nginx-new \
-p 80:80 \
nginx:latest
# 旧版本Nginx - 占用宿主机8088端口
docker run -d \
--name nginx-old \
-p 8088:80 \
nginx:1.20参数详解:
-d:后台运行(daemon模式)--name:给容器起名字,方便管理-p 80:80:端口映射,格式为宿主机端口:容器端口
3. 验证容器运行
bash
# 查看运行中的容器
docker ps
# 访问测试
curl http://localhost # 最新版
curl http://localhost:8088 # 1.20版本4. 进入容器修改配置
bash
# 交互式进入容器
docker exec -it nginx-new bash
echo '<h1>Hello, Nginx Latest!</h1>' > /usr/share/nginx/html/index.html
exit
# 直接执行命令(无需进入容器)
docker exec nginx-old sh -c 'echo "<h1>Hello, Nginx 1.20!</h1>" > /usr/share/nginx/html/index.html'5. 查看容器日志
bash
docker logs nginx-new # 查看日志
docker logs -f nginx-new # 实时跟踪日志
docker logs --tail 100 nginx-new # 查看最新100行6. 清理环境
bash
# 停止并删除容器
docker stop nginx-new nginx-old
docker rm nginx-new nginx-old
# 删除镜像
docker rmi nginx:latest nginx:1.20⚠️ 重要:删除镜像前必须先删除使用该镜像的容器。
面试思考题
Q1: 在生产环境中,是否推荐直接用nginx:latest镜像?
点击查看答案
答:强烈不推荐。
latest是浮动标签,版本会自动变化- 可能引入不兼容变更
- 无法复现环境
正确做法:使用固定版本,如nginx:1.25.3或nginx:1.25.3-alpine。
第四阶段:MySQL数据库容器化部署
业务场景
完成Nginx测试任务后,CTO交给小王一个正式任务:搭建公司博客平台。首先需要容器化部署MySQL数据库。
为什么使用自定义网络
| 方式 | 说明 | 推荐 |
|---|---|---|
--link | 老式的容器连接方式 | ❌ 已废弃 |
| 默认bridge网络 | 使用docker0网桥 | ⚠️ 只能通过IP通信 |
| 自定义网络 | 容器通过名称通信 | ✅ DNS自动解析 |
实战:部署MySQL容器
bash
# 创建自定义网络
docker network create blog-network
# 运行MySQL容器
docker run -d \
--name blog-mysql \
--network blog-network \
-e MYSQL_ROOT_PASSWORD=gaokun123 \
-e MYSQL_DATABASE=wordpress \
mysql:5.7
# 验证
docker ps
docker exec -it blog-mysql mysql -u root -p故事冲突
小王发现MySQL运行正常,但心里有个疑问:如果容器被删了,数据怎么办?
第五阶段:WordPress应用镜像构建
业务场景
数据库就绪,现在需要部署WordPress博客应用。小王决定自己构建镜像。
Dockerfile编写
bash
mkdir -p ~/blog-project/blog
cd ~/blog-project/blogdockerfile
FROM wordpress:latest
LABEL maintainer="wang@gaokun.com"
WORKDIR /var/www/html
ENV WORDPRESS_DB_HOST=blog-mysql \
WORDPRESS_DB_USER=root \
WORDPRESS_DB_PASSWORD=gaokun123 \
WORDPRESS_DB_NAME=wordpress
EXPOSE 80
CMD ["apache2-foreground"]构建并运行
bash
# 构建镜像
docker build -t gaokun-blog:v1.0 .
# 运行WordPress容器
docker run -d \
--name blog-web \
--network blog-network \
-p 8080:80 \
gaokun-blog:v1.0
# 验证容器间通信
docker exec blog-web ping blog-mysql访问博客:浏览器访问 http://<服务器IP>:8080
第六阶段:数据持久化与Compose编排
业务场景
CTO来做验收,问小王:"如果数据库容器被误删了,数据还能恢复吗?"
灾难演练:数据丢失
bash
# 模拟灾难:删除数据库容器
docker rm -f blog-mysql
# 重新创建MySQL容器(不带数据卷)
docker run -d \
--name blog-mysql-new \
--network blog-network \
-e MYSQL_ROOT_PASSWORD=gaokun123 \
-e MYSQL_DATABASE=wordpress \
mysql:5.7结果:刷新博客页面,发现回到了WordPress安装界面,所有数据都丢失了!
数据持久化解决方案
容器内的文件系统是临时的,要实现数据持久化,需要使用Volume(数据卷)。
bash
# 清理环境
docker rm -f blog-mysql-new blog-web
# 重新创建MySQL,使用Volume
docker run -d \
--name blog-mysql \
--network blog-network \
-v blog-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=gaokun123 \
-e MYSQL_DATABASE=wordpress \
mysql:5.7
# 重新运行WordPress
docker run -d \
--name blog-web \
--network blog-network \
-p 8080:80 \
gaokun-blog:v1.0参数说明:-v blog-data:/var/lib/mysql 将Volume挂载到MySQL数据目录。
Docker Compose一键编排
手动管理多个容器很麻烦,使用Docker Compose可以将所有配置写成YAML文件。
创建docker-compose.yml文件:
yaml
version: '3.8'
services:
mysql:
image: mysql:5.7
container_name: blog-mysql
networks:
- blog-net
volumes:
- blog-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: gaokun123
MYSQL_DATABASE: wordpress
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
wordpress:
build:
context: ./blog
dockerfile: Dockerfile
container_name: blog-web
ports:
- "80:80"
networks:
- blog-net
depends_on:
mysql:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: blog-mysql:3306
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: gaokun123
networks:
blog-net:
driver: bridge
volumes:
blog-data:
driver: localCompose命令管理
bash
cd ~/blog-project
docker compose up -d # 一键启动
docker compose ps # 查看状态
docker compose logs -f # 查看日志
docker compose down # 停止并删除容器
docker compose down -v # 彻底清理(包括数据卷)面试思考题
Q2: 对比docker run和docker compose在多容器部署中的优劣?
点击查看答案
| 维度 | docker run | docker compose |
|---|---|---|
| 可维护性 | 命令散落,难以管理 | 配置集中,版本可控 |
| 依赖管理 | 需手动控制启动顺序 | depends_on声明依赖 |
| 环境一致性 | 不同人部署结果可能不同 | 一键复现 |
| 适用场景 | 学习、调试、单容器 | 生产环境、多容器应用 |
总结:docker run是学习工具,docker compose是生产工具。
课程总结
知识回顾
| 阶段 | 业务场景 | 核心知识 |
|---|---|---|
| 第一阶段 | 接到Nginx多版本任务 | 四种方案对比,选择Docker |
| 第二阶段 | Docker环境搭建 | 安装与镜像加速配置 |
| 第三阶段 | 部署多版本Nginx | 镜像/容器基础操作 |
| 第四阶段 | MySQL容器化 | 自定义网络 |
| 第五阶段 | WordPress镜像构建 | Dockerfile编写 |
| 第六阶段 | 数据持久化与编排 | Volume + Compose |
核心命令速查
bash
# 镜像操作
docker pull nginx:1.20 # 拉取镜像
docker images # 查看镜像
docker rmi nginx:1.20 # 删除镜像
docker build -t myapp:v1 . # 构建镜像
# 容器操作
docker run -d -p 80:80 --name web nginx # 运行容器
docker ps # 查看运行中的容器
docker stop/rm web # 停止/删除容器
docker exec -it web bash # 进入容器
docker logs -f web # 查看日志
# 网络操作
docker network create mynet # 创建网络
docker network ls # 查看网络
# 存储操作
docker volume create myvol # 创建卷
docker run -v myvol:/data ... # 使用卷
# Compose操作
docker compose up -d # 启动服务
docker compose down # 停止服务
docker compose ps # 查看状态最佳实践
- 镜像版本:生产环境使用固定版本标签,不用
latest - 容器命名:使用有意义的名称,如
项目_服务_环境 - 网络管理:使用自定义网络实现服务发现和隔离
- 数据持久化:重要数据必须使用Volume持久化
- 编排管理:多容器应用使用Docker Compose管理
课后练习
练习1(基础)
使用Docker运行两个不同版本的Redis(6.x和7.x),分别映射到6379和6380端口,测试连接后清理环境。
练习2(进阶)
编写一个Dockerfile,基于python:3.9-alpine构建一个Flask应用镜像。
练习3(综合)
使用Docker Compose部署一个LNMP环境(Linux + Nginx + MySQL + PHP),要求Nginx监听80端口,MySQL数据持久化。
下一步学习:运维开发实战篇概述