DevOps 入门系列:Docker 容器化 —— 一次构建,到处运行
DevOps 入门系列:Docker 容器化 —— 一次构建,到处运行
一、为什么需要 Docker?—— 一个真实的故事
1.1 “在我的机器上是好的”
本地开发的 Python 应用运行正常,部署到生产服务器后直接报错,典型问题如下:
-
本地 Python 3.9,服务器 Python 3.6,版本不兼容
-
项目依赖库要求
glibc 2.31,服务器仅为glibc 2.28 -
本地配置环境变量
DEBUG=True,生产环境未配置
这就是软件行业长期存在的环境不一致问题,也是开发与运维之间最常见的矛盾点。
1.2 传统解决方案的缺点
方案一:逐台服务器手动配置环境
-
部署效率低,人工操作易遗漏配置项
-
多服务器之间配置容易出现偏差(配置漂移)
-
无法实现业务快速扩容
方案二:虚拟机(VM)
-
每个虚拟机搭载完整操作系统,镜像体积大(数 GB~ 数十 GB)
-
启动速度慢,单次启动耗时数分钟
-
资源开销极高,每台虚拟机都独立运行完整操作系统内核
1.3 Docker 的解决方案
Docker 基于容器技术实现优化:容器与宿主机共享操作系统内核,仅对进程、文件系统做隔离,轻量化优势显著。
核心概念通俗解释:
-
镜像:将应用、依赖库、配置文件、运行环境打包为只读模板
-
容器:镜像启动后的运行实例,秒级启动、资源占用极低
一句话总结:Docker 可将应用及全套运行环境统一打包,任意安装了 Docker 的机器,都能以完全一致的方式运行应用。
二、Docker 的核心三要素
2.1 镜像(Image)
定义:只读模板,包含应用运行所需的文件系统、依赖库、环境变量、启动命令等全部内容。
类比:镜像等同于系统 ISO 安装包 / 编程语言中的类定义。
核心特点:
-
静态文件,存储在磁盘中,本身不会运行
-
采用分层存储结构
-
可上传至镜像仓库,实现共享与分发
2.2 容器(Container)
定义:镜像的运行实例,由镜像创建并启动,运行在内存中。
类比:镜像是「类」,容器就是通过 new 实例化出来的「对象」;一个镜像可以启动多个相互隔离的容器。
核心特点:
-
动态实体,状态分为「运行中」「已停止」
-
支持随时创建、启动、停止、删除
-
容器内进程对宿主机、其他容器相互隔离
2.3 仓库(Registry)
定义:专门用于存储、分发镜像的服务平台。
常用仓库分类
-
Docker Hub:Docker 官方公共镜像仓库,可免费拉取 Nginx、MySQL、Python 等官方镜像(国内访问及其困难,截止2026年6月国内大部分镜像站都已无法稳定访问,最好自己去搜集一份可用的镜像网站列表)
-
私有仓库:企业内部搭建,用于存放业务私有镜像,代表产品:Harbor、阿里云容器镜像服务
镜像标准地址格式
[仓库地址]/命名空间/镜像名:标签
示例解析:docker.io/library/nginx:1.25
-
docker.io:Docker Hub 官方地址(日常使用可省略) -
library:官方镜像默认命名空间 -
nginx:镜像名称 -
1.25:版本标签;latest代表最新版本(生产环境不建议使用)
三、安装 Docker(Ubuntu / WSL2 环境推荐)
以下为 Ubuntu 系统完整安装命令,全程使用终端执行:
# 1. 更新软件源索引
sudo apt update
# 2. 安装前置依赖
sudo apt install -y ca-certificates curl
# 3. 创建密钥目录,并添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 4. 添加 Docker 官方软件源(国内连不上换镜像站)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 5. 再次更新源,安装 Docker 核心组件
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
# 6. 将当前用户加入 docker 用户组,免去每次执行命令加 sudo
sudo usermod -aG docker $USER
生效与验证
-
重新登录终端,或执行
newgrp docker即时生效 -
执行测试命令,出现欢迎页即代表安装成功
docker run hello-world
四、Docker 基础命令
4.1 镜像管理
1. 拉取镜像 docker pull
从远程仓库下载镜像到本地
# 拉取 Nginx 镜像(为了方便这里使用alpine,alpine 基于极简 Linux,体积约 5MB)
docker pull nginx:alpine
2. 查看本地镜像 docker images
docker images
输出示例:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx alpine abc123def456 2 weeks ago 5.2MB
3. 删除镜像 docker rmi
# 根据【镜像名:标签】删除镜像
docker rmi nginx:alpine
注意:若镜像被容器关联(即使容器已停止),需先删除容器,或添加
-f强制删除。
4. 镜像导出 / 导入(离线传输场景)
# 导出镜像为 tar 包(离线迁移使用)
docker save -o mynginx.tar nginx:alpine
# 从 tar 包导入镜像
docker load -i mynginx.tar
5. 查看镜像构建历史
# 显示镜像各层的创建时间、大小及构建指令
docker history
4.2 容器生命周期管理
1. 运行容器 docker run
核心命令,基于镜像创建并启动容器,参数是学习重点:
docker run -d --name mynginx -p 8080:80 nginx:alpine
参数详解:
-
-d:后台守护进程模式运行,终端不会被日志占用;不加则前台运行,Ctrl+C会停止容器 -
--name mynginx:自定义容器名称,便于后续管理(不指定则 Docker 随机命名) -
-p 8080:80:端口映射,宿主机端口:容器内部端口。访问宿主机IP:8080即可转发至容器 80 端口 -
末尾
nginx:alpine:指定使用的镜像
执行后浏览器访问 http://localhost:8080,可看到 Nginx 欢迎页面。
2. 查看容器 docker ps
# 仅查看【正在运行】的容器
docker ps
# 查看【所有容器】(包含已停止的容器)
docker ps -a
输出字段:容器 ID、容器名、所用镜像、运行状态、端口映射等。
3. 停止 / 启动 / 重启容器
# 优雅停止容器(发送终止信号,正常退出进程)
docker stop mynginx
# 启动已停止的容器
docker start mynginx
# 重启容器
docker restart mynginx
4. 删除容器 docker rm
# 仅删除【已停止】的容器
docker rm mynginx
# 强制删除【运行中】的容器
docker rm -f mynginx
5. 进入容器内部 docker exec
用于调试、查看容器内文件、执行命令,不会中断容器运行:
# 交互式进入容器终端
docker exec -it mynginx sh
参数说明:
-
-it:分配交互式终端(-i交互模式 +-t伪终端) -
sh:执行的终端命令(Alpine 镜像无 bash,统一使用 sh)
操作:进入后可执行 ls、ps、cat 等命令,输入 exit 退出终端。
6. 查看容器日志 docker logs
# 查看全部历史日志
docker logs mynginx
# 实时滚动查看日志(Ctrl+C 退出)
docker logs -f mynginx
# 仅查看最后 50 行日志
docker logs --tail 50 mynginx
4.3 常用组合示例
示例 1:临时测试容器(退出自动删除)
适合一次性调试,容器退出后自动清理,无残留
docker run --rm -it alpine sh
执行后会先下载镜像并启动容器,-it直接进入容器内部的命令行,我们执行完测试任务后输入exit容器即可退出并自动消失,无需手动删除容器。
示例 2:运行 MySQL 容器
通过 -e 注入环境变量,初始化数据库账号密码
docker run -d --name mydb \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
mysql:8.0
五、Docker 网络:容器之间互相通信
5.1 默认 bridge 网络的问题
未指定网络时,容器默认加入 Docker 内置 bridge 网络:
-
同网络内容器可以互通,但无法通过容器名访问,只能使用 IP 地址
-
容器重启后 IP 会发生变化,服务连接地址需要频繁修改,实用性差
5.2 自定义 bridge 网络(生产 / 开发推荐)
自定义网络自带内置 DNS 解析,支持直接使用容器名互通。
步骤 1:创建自定义网络
docker network create my-net
步骤 2:将多个容器加入同一网络
# 创建并启动 Nginx 容器,命名为app1,加入 my-net 网络
docker run -d --name app1 --network my-net nginx:alpine
# 创建并启动 Redis 容器,命名为app2,加入 my-net 网络
docker run -d --name app2 --network my-net redis:alpine
步骤 3:验证容器互通
在 app1 容器内,直接通过容器名访问 app2:
docker exec app1 ping app2
可以正常 ping 通,原理:自定义网络内置 DNS(127.0.0.11),自动将容器名解析为对应 IP。
5.3 端口映射 vs 容器互通
-
端口映射(-p):面向外部(宿主机 / 外网) 访问容器服务
-
容器互通:同一自定义网络内的容器,直接用容器名访问,无需端口映射
实战场景:应用容器连接数据库容器,两者同属一个自定义网络,连接字符串可直接写 mysql://mysql:3306。
5.4 其他网络模式
-
host模式:--network host,容器直接复用宿主机网络栈,无网络隔离,性能最优,但易出现端口冲突 -
none模式:--network none,容器禁用所有网络接口,完全网络隔离
日常开发、测试优先使用 自定义 bridge 网络。
六、数据持久化:容器重启后数据留存
6.1 问题场景
容器默认文件系统是临时存储:容器删除后,内部所有写入的数据(数据库文件、上传文件、日志等)都会永久丢失。
典型案例:MySQL 容器删除重建,原有数据库数据全部清空。
6.2 解决方案:数据卷(Volume)
数据卷是 Docker 独立管理的存储空间,生命周期与容器解绑,容器删除后卷数据依然保留。分为两种常用挂载方式:
方式一:命名卷(官方推荐,可移植性强)
适用于数据库等核心数据存储
# 1. 创建命名卷
docker volume create mysql-data
# 2. 运行容器并挂载卷
docker run -d --name mydb \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0
参数说明:-v 卷名:容器内目录,将 mysql-data 卷挂载到 MySQL 数据目录 /var/lib/mysql。
复用卷:删除旧容器后,新容器挂载同一个卷,数据无缝恢复:
# 删除旧容器
docker rm -f mydb
# 新容器复用原有数据卷
docker run -d --name newdb -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
方式二:绑定挂载(Bind Mount)
直接将宿主机本地目录挂载到容器内,适合开发调试(修改本地代码,容器实时生效)
# 宿主机 /home/devops/data 目录 挂载到 容器 /app/data 目录
docker run -d -v /home/devops/data:/app/data myapp
6.3 卷相关命令
# 查看所有数据卷
docker volume ls
# 查看卷详细信息(元数据,即存储路径、挂载情况等信息)
docker volume inspect 卷名
# 删除无用数据卷(删除后数据永久丢失)
docker volume rm 卷名
七、动手练习:部署完整 Web 应用栈
注:本节基于AI生成,不保证能运行,仅展示基本流程用
实战目标:基于 Docker 搭建 Nginx + PHP + MySQL 多容器应用栈,结合「自定义网络」+「数据卷」综合使用。
步骤 1:创建专属自定义网络
docker network create app-net
步骤 2:启动 MySQL 容器(挂载数据卷)
# 创建数据库数据卷
docker volume create db-data
# 启动 MySQL 容器,加入自定义网络、初始化账号与数据库
docker run -d --name mysql \
--network app-net \
-v db-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=mydb \
mysql:8.0
步骤 3:启动 PHP 应用容器(绑定本地代码目录)
假设本地代码存放在当前目录 src/ 文件夹中:
docker run -d --name php-app \
--network app-net \
-v "$PWD/src":/var/www/html \
php:8.2-apache
步骤 4:启动 Nginx 反向代理容器
对外暴露端口,提供外网访问入口:
docker run -d --name nginx \
--network app-net \
-p 8080:80 \
nginx:alpine
验证互通
进入 PHP 容器,通过容器名访问 MySQL,验证网络连通性:
docker exec php-app ping mysql
拓展:可进入 Nginx 容器修改配置文件,实现反向代理转发至
php-app容器。
八、Docker 常用命令速查表
| 操作场景 | 执行命令 |
|---|---|
| 拉取远程镜像 | docker pull 镜像名:标签 |
| 查看本地所有镜像 | docker images |
| 删除本地镜像 | docker rmi 镜像ID/镜像名:标签 |
| 后台运行容器(端口映射) | docker run -d --name 容器名 -p 宿主机端口:容器端口 镜像名 |
| 查看运行中的容器 | docker ps |
| 查看所有容器(含停止状态) | docker ps -a |
| 停止容器 | docker stop 容器名/容器ID |
| 启动已停止容器 | docker start 容器名/容器ID |
| 删除容器 | docker rm -f 容器名/容器ID |
| 交互式进入容器 | docker exec -it 容器名 sh |
| 实时查看容器日志 | docker logs -f 容器名 |
| 创建自定义网络 | docker network create 网络名 |
| 查看所有网络 | docker network ls |
| 创建数据卷 | docker volume create 卷名 |
| 运行容器并挂载数据卷 | docker run -v 卷名:容器内目录 镜像名 |
更多推荐
所有评论(0)