跳转到内容

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容器部署方案:

  1. ✅ 可以同时运行多个版本的Nginx
  2. ✅ 无需处理复杂的依赖和编译
  3. ✅ 端口映射灵活,不会冲突
  4. ✅ 测试完成后可以快速清理环境
  5. ✅ 符合公司的容器化战略

但服务器上还没有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
DaoCloudhttps://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 images

2. 启动两个版本的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.3nginx: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/blog
dockerfile
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: local

Compose命令管理

bash
cd ~/blog-project

docker compose up -d        # 一键启动
docker compose ps           # 查看状态
docker compose logs -f      # 查看日志
docker compose down         # 停止并删除容器
docker compose down -v      # 彻底清理(包括数据卷)

面试思考题

Q2: 对比docker rundocker compose在多容器部署中的优劣?

点击查看答案
维度docker rundocker 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               # 查看状态

最佳实践

  1. 镜像版本:生产环境使用固定版本标签,不用latest
  2. 容器命名:使用有意义的名称,如项目_服务_环境
  3. 网络管理:使用自定义网络实现服务发现和隔离
  4. 数据持久化:重要数据必须使用Volume持久化
  5. 编排管理:多容器应用使用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数据持久化。


下一步学习运维开发实战篇概述

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径