不会飞的章鱼

熟能生巧,勤能补拙;静能生慧,为而不争;莫向外求,但求心觅;念念不忘,必有回响。

【慕聘网】虚拟化与容器化Docker环境搭建

虚拟化与容器化技术简介

在构建现代 Java 高级开发环境时,虚拟化 (Virtualization)容器化 (Containerization) 是两项互补且核心的技术。理解它们的异同和联系,是掌握云原生及 DevOps 流程的第一步。

1. 虚拟化技术 (Virtualization)

虚拟化技术通过在物理硬件上添加一个抽象层(Hypervisor),允许在一台物理机上运行多个相互隔离的操作系统实例(虚拟机 VMs)。

  • 核心特征:每个虚拟机都包含完整的操作系统(Guest OS)、相关的库和应用程序。
  • 典型软件:VMware (本项目推荐用于搭建 Linux 开发底座)。
  • 技术特点
    • 隔离性强:由于拥有独立内核,虚拟机之间的隔离非常彻底。
    • 启动速度慢:启动一个完整的 OS 通常需要数分钟。
    • 资源占用高:每个 Guest OS 都会预占大量的内存和磁盘容量。
    • 可移植性偏弱:虚拟机镜像(如 .ova, .vmdk)通常达数 GB 甚至几十 GB,分发不便。

2. 容器化技术 (Containerization - Docker)

容器化是一种更轻量级的虚拟化,它直接运行在宿主机的内核上,通过 Namespaces 和 Cgroups 实现进程级的隔离。

  • 核心架构:Docker 引擎安装在宿主机(物理机或虚拟机)上,所有容器共享宿主机的 OS 内核。
  • **核心优势 (Why Docker?)**:
    • 镜像创建方便:通过 Dockerfile 或 commit 即可快速构建自定义镜像。
    • 安装/部署快速:直接下载镜像并运行命令即可完成复杂中间件的安装。
    • 启动极快:容器启动通常在秒级,甚至毫秒级。
    • 隔离性强:虽然共享内核,但 Docker 提供了完善的资源和进程隔离机制。
    • 可移植性强:真正实现“一次构建,到处运行”,屏蔽环境差异。
    • 共享方便:通过 Docker Hub 或私有仓库(如 Harbor)轻松分发和同步。
    • **镜像小 (MB 级)**:仅包含必要的运行环境,占用空间微乎其微。

3. 最终环境搭建决策

经过对虚拟化与容器化技术的深度评估,本项目最终决定采用 Docker 来搭建绝大部分的核心开发与测试环境

决策理由

  1. 效率为王:在开发阶段,我们需要频繁安装/销毁 Redis、MySQL、Nacos、RabbitMQ 等中间件。使用 Docker 可以在几分钟内拉起一整套集群,极大节省环境准备时间。
  2. 环境对齐:通过 Docker 镜像,全组开发者(包括后续的生产环境部署)都能使用完全相同的软件版本和配置,彻底杜绝“我这能运行,你那不行”的尴尬。
  3. 资源集约:相比于为每个中间件开一个虚拟机,Docker 容器可以在同一个开发底座(如 VMware 里的 CentOS 虚拟机)上高效运行几十个服务而不卡顿。

[!TIP]
最佳实践建议
我们将使用 VMware 虚拟出一台稳定的 Linux 服务器作为“宿主机”,并在其上安装 Docker 环境。这样既能享受虚拟机的快照备份能力(底层保障),又能享受容器化的极致交付速度(应用效率)。


4. 虚拟化 vs. 容器化:区别与联系

核心区别对照表

特性 虚拟机 (VM) 容器 (Docker)
隔离级别 硬件级隔离(独立内核) 进程级隔离(共享内核)
启动时间 数分钟 数秒
资源效率 较低(重复的 OS 开销) 极高(共享系统资源)
镜像大小 GB 级别 MB 级别
持久化 天然支持 需要挂载 Data Volume

两者的深层联系

  1. 分层协作:在实际生产中,两者并非二选一。通常是在 虚拟化出来的云服务器 (VPC) 上安装 Docker 引擎。虚拟机提供底层的硬件隔离和安全性,Docker 提供应用层的灵活性和敏捷性。
  2. DevOps 演进:虚拟化解决了硬件资源分配问题,而容器化则进一步解决了软件交付标准问题。它们共同构成了 云原生 (Cloud Native) 的技术基石。

5. 总结

虚拟化解决了硬件资源分配问题,而容器化则进一步解决了软件交付标准问题。选择 Docker 不仅仅是选择了一个工具,更是选择了 DevOps云原生 (Cloud Native) 的现代开发思维。

Docker 架构与隔离机制

深入理解 Docker 的内部架构和隔离机制,有助于我们更好地进行容器化部署与故障排查。

1. Docker 核心架构 (C/S 架构)

Docker 采用了经典的客户端-服务器 (Client-Server) 架构,主要由以下三个核心部分组成:

  • **Docker Client (客户端)**:开发者通过命令行工具(如 docker run, docker build, docker pull)与 Docker 交互。
  • **Docker Host (宿主机/守护进程)**:运行 Docker Daemon。它负责执行客户端发来的指令,管理镜像(Images)和容器(Containers)。
    • **Images (镜像)**:只读的模板,包含了运行应用所需的所有代码、环境和配置。
    • **Containers (容器)**:镜像的运行实例。它是独立运行的一个或一组应用及其依赖环境。
  • **Docker Registry (仓库)**:用于存储镜像。最常用的是官方的 Docker Hub,也可以搭建私用仓库如 Harbor。整个流程通常是:Client 发起 pull -> DaemonRegistry 下载镜像 -> Daemon 运行 Container

2. Docker 底层隔离机制

Docker 能够实现轻量化的核心在于充分利用了 Linux 内核的两大特性:NameSpaces (命名空间) 和 **Control Groups (Cgroups)**。

Linux NameSpaces (环境隔离)

NameSpaces 负责为容器提供独立的运行环境,确保容器之间“互不干扰”:

  • **UTS (UNIX Timesharing System)**:主机名和域名的隔离。容器可以拥有自己的主机名。
  • **PID (Process ID)**:进程号隔离。容器内的进程认为自己是 1 号进程,看不到宿主机和其他容器。
  • **IPC (Inter-Process Communication)**:进程间通信隔离。防止容器间的信号量、消息队列和共享内存互通。
  • Network:网络设备隔离。每个容器拥有独立的网络栈(IP、端口、网桥等)。
  • Mount:文件系统隔离。容器只能看到自己镜像内的根目录挂载点。

Control Groups (资源限制)

Cgroups(控制组)负责对容器使用的物理资源进行限制和审计,其具体功能包括:

  • 资源控制:精确限制每个容器能使用的 CPU、内存、I/O 带宽 等各项定额。
  • 优先级分配:在宿主机资源紧张时,通过权重分配确保核心服务的资源优先级。
  • 资源统计:实时监控和统计容器的资源使用情况,为性能分析提供数据支撑。
  • 任务控制:支持对容器内进程任务的挂起、恢复等精细化控制。

3. Docker 的典型应用场景

得益于上述架构与机制,Docker 在以下领域发挥着巨大作用:

  • Web 应用的自动化打包与发布:简化部署流程。
  • **CI/CD (持续集成/持续交付)**:支持自动化测试与持续集成。
  • PaaS 平台搭建:易于部署和调整各类复杂应用参数。
  • 微服务解耦:将应用程序与基础设施全部分割开,实现快速的软件交付。

4. 总结

Docker 通过 C/S 架构 实现了灵活的操作体验,通过 NameSpaces 实现了环境的高度隔离,再通过 Cgroups 为资源消耗加上了保险。对于 Java Senior 这种涉及大量中间件的项目,Docker 是目前不二的环境搭建之选。

Docker 容器的安装与镜像加速

在明确了架构与隔离机制后,接下来我们将在虚拟机环境中正式搭建 Docker。为了保证最佳的兼容性与网络连通性,请遵循以下步骤。

1. 安装准备

在开始编写命令之前,请务必检查以下两点:

  • 操作系统:强烈建议使用 CentOS 7 或更高版本的 Linux 系统。由于内核限制,CentOS 6 及早期版本对 Docker 的支持并不理想,可能会导致运行不稳定。
  • 网络模式:建议将虚拟机的网卡设置为 **桥接模式 (Bridged)**。这样虚拟机可以获得一个与宿主机同网段的独立静态 IP,确保物理机与虚拟机之间可以互相 ping 通,这是后续微服务联调的基础。

2. Docker 安装步骤 (CentOS 7)

请访问 Docker 官方文档获取最新的安装指南,核心流程拆解如下:

  1. 清理旧版本:如果系统中已安装旧版 Docker,请先通过 yum remove 进行卸载,避免版本冲突。
  2. 安装必要工具包:安装 yum-utils,它提供了 yum-config-manager 实用程序。
  3. 配置仓库地址:添加 Docker 官方(或阿里云、清华源等)的 yum 软件源。
  4. 执行安装命令:运行 sudo yum install docker-ce docker-ce-cli containerd.io,等待下载完成。
  5. 启动与自启动
    1
    2
    sudo systemctl start docker       # 启动 Docker
    sudo systemctl enable docker # 设置开机自启

验证安装

安装完成后,通过以下命令验证:

  • docker version:检查 Docker Client 和 Server 的版本号。
  • docker images:查看本地已下载的镜像列表(初始应为空)。

3. 镜像加速配置 (Aliyun)

由于 Docker Hub 服务器位于海外,直接拉取镜像速度较慢。国内开发者推荐配置阿里云镜像加速器

配置步骤

  1. 登录阿里云官网,进入“容器镜像服务”页面,找到专属的“镜像加速器”地址。
  2. 在 Linux 中创建配置目录并写入加速地址:
    1
    2
    3
    4
    5
    6
    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
    "registry-mirrors": ["https://您的加速地址.mirror.aliyuncs.com"]
    }
    EOF
  3. 重启服务生效
    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker

4. 总结

至此,我们的 Docker 环境已全部就绪。通过 CentOS 7 + 官方引擎 + 阿里云加速 的黄金组合,我们为后续部署 Spring Cloud 全家桶和各类复杂中间件打下了坚实的技术底座。

Docker 端口映射与文件挂载

在实际操作 Docker 容器时,端口映射文件挂载是两个最基础且必须掌握的高频操作。它们构建了宿主机与容器内部之间的“通信桥梁”与“数据通道”。

1. 端口映射 (Port Mapping)

由于 Docker 容器拥有独立的网络栈(Network NameSpace),其内部运行的服务默认无法被外界(甚至宿主机本身)通过常规方式直接访问。此时,我们需要进行“端口映射”。

  • 核心逻辑:将宿主机的某个端口与容器内部的某个端口建立联系。
  • 操作命令-p [宿主机端口]:[容器内部端口]
  • 应用示例
    • 3306:3306:通过物理机 IP 访问宿主机的 3306,流量会被自动转发到内部 MySQL 容器。
    • 80:8080:外界访问标准的 Web 80 端口,流量进入内部的 Tomcat 或 Spring Boot 环境。
  • 主要优势
    • 可访问性:实现了容器服务的外网/内网暴露。
    • 安全性:我们可以自定义暴露的端口号,隐藏容器内部真实的业务端口,增加攻击成本。

端口映射原理图

端口映射原理图


2. 文件/磁盘挂载 (Volume Mounting)

容器的文件系统是“临时性”的。默认情况下,如果容器被删除,其内部产生的所有数据(如数据库文件、上传的图片)都会丢失。为了解决这个问题,我们需要“文件挂载”。

  • 核心逻辑:将宿主机的某个目录/文件映射到容器内部的对应路径。
  • 操作命令-v [宿主机路径]:[容器内部路径]
  • 为什么需要它?
    • 数据持久化:即使容器损毁或升级,挂载在宿主机的数据依然存在。
    • 配置便捷性:无需频繁进入容器内部修改配置文件。你只需在宿主机上编辑 my.cnfapplication.yml,容器内部会实时同步。
    • 资源共享:多个容器可以挂载同一个宿主机目录,实现数据共享(如日志归档)。

文件挂载原理图

文件挂载原理图


3. 总结

  • 端口映射 解决了“怎么进得去”的问题。
  • 文件挂载 解决了“数据留得住”的问题。

掌握了这两点,你就真正跨过了 Docker 从理论到实操的门槛。接下来,我们将利用这些知识点,开始正式部署本项目的核心中间件集群。

Docker 操作镜像命令

掌握 Docker 的常用命令是日常开发的必备技能。我们可以将命令分为镜像管理、容器生命周期管理以及辅助操作三大类。

1. 核心镜像命令 (Image Lifecycle)

镜像(Image)是 Docker 的基石,以下是我们在部署微服务中间件时最常用的命令:

  • 获取镜像docker pull [镜像名]:[tag] —— 从 Docker Hub 下载指定版本镜像(如 mysql:8.0)。
  • 查看本地镜像docker images —— 列出宿主机上所有已下载的镜像及其 ID、大小。
  • 删除镜像docker rmi [镜像ID/镜像名] —— 清理不再需要的本地镜像。
  • 导出镜像docker save -o [文件名].tar [镜像名] —— 将镜像打包成离线文件,方便在无网环境下传输。
  • 导入镜像docker load -i [文件名].tar —— 将离线包还原为 Docker 镜像。
  • 构建镜像docker build -t [名称]:[版本] . —— 根据当前目录下的 Dockerfile 构建自定义镜像。

2. 常用操作全景脑图

为了方便记忆,我们可以通过下图理清 Client、Registry、Images 和 Containers 之间的流转关系:

Docker 常用命令关系脑图

[!TIP]
记不住命令怎么办?
任何时候都可以输入 docker --helpdocker [subcommand] --help。Docker 的官方帮助文档非常详尽,且命令设计具有极高的逻辑性。


Docker 操作容器命令

在掌握了镜像管理后,我们进入核心环节:如何运行和管理应用容器。

1. 环境预置:开启 Linux IP 转发

在虚拟机或物理机上运行 Docker 时,如果宿主机的 IP 转发功能未开启,容器可能无法正常进行网络通信。

  • 检查状态
    1
    cat /proc/sys/net/ipv4/ip_forward
    如果返回 0,则表示关闭;返回 1 表示已开启。
  • 临时开启echo 1 > /proc/sys/net/ipv4/ip_forward
  • 永久开启:修改 /etc/sysctl.conf,添加 net.ipv4.ip_forward=1,然后运行 sysctl -p 生效。

2. 核心命令:docker run

docker run 是 Docker 最强大的命令,用于创建并启动一个新容器。其参数组合直接决定了应用的行为。

参数拆解:

  • -p 3306:3306端口映射。将宿主机的 3306 映射到容器内部的 3306。
  • --name mysql容器命名。为容器指定一个易记的名称。
  • -v /opt/mysql/data:/var/lib/mysql文件挂载。将宿主机目录挂载到容器内,实现数据持久化。
  • -e MYSQL_ROOT_PASSWORD=root环境变量。配置应用启动所需的参数(如数据库密码)。
  • -d后台运行。让容器在后台持续运行。

实战演示:一键部署 MySQL 8

1
2
3
4
5
6
7
8
docker run -p 3306:3306 --name mysql \
-v /opt/mysql/log:/var/log/mysql \
-v /opt/mysql/data:/var/lib/mysql \
-v /opt/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:8.0.27 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

命令参数图解:

MySQL 8 部署命令参数图解


3. 容器日常管理工具箱

  • 查看运行状态
    • docker ps:查看当前正在运行的容器。
    • docker ps -a:查看所有容器(包括已停止的)。
  • 日志排查docker logs -f [容器名/ID] —— 实时滚动查看应用日志,排障利器。
  • 进入容器内部docker exec -it [容器名] bash —— 在运行中的容器内执行交互式 Shell。
  • 生命周期控制docker start/stop/restart/rm [容器名]

Docker 进入容器的命令

在容器稳定运行后,我们经常需要进入容器内部进行调试或手动执行一些管理命令(如修改数据库密码)。

1. 容器内部交互 (Interactive Shell)

进入容器最标准的方式是使用 docker exec

1
docker exec -it mysql bash
  • **-i (interactive)**:交互模式。保持标准输入(STDIN)开启。
  • **-t (tty)**:分配一个伪终端(TTY)。让我们感觉像是在操作一台真实的终端。
  • mysql:目标容器的名称或 ID。
  • bash:进入后使用的 Shell 环境。

2. MySQL 内部初始化(实战演示)

进入 MySQL 容器后,我们可以直接操作数据库引擎。

  1. 登录 MySQL
    1
    mysql -uroot -proot
  2. **安全性设置 (以 MySQL 8 为例)**:
    为了方便外部工具连接,我们通常需要执行以下 SQL:
    1
    2
    3
    4
    5
    6
    7
    -- 修改 root 密码(可选)
    ALTER USER 'root'@'localhost' IDENTIFIED BY 'imooc';
    -- 允许远程连接 (%)
    UPDATE mysql.user SET host = '%' WHERE user = 'root';
    -- 刷新权限并退出
    FLUSH PRIVILEGES;
    EXIT;

3. 外部可视化工具连接 (Navicat)

为了提高开发效率,我们通常在物理机上使用 NavicatDataGrip 连接 Docker 中的数据库。

连接配置:

  • **主机 (Host)**:宿主机的 IP 地址(可通过 ifconfigip addr 查看 ens33 网卡)。
  • **端口 (Port)**:我们在 docker run 时映射出的物理机端口(如 3306)。
  • 用户名/密码:上述 SQL 设置的 root / imooc

[!IMPORTANT]
连不通怎么办?

  1. 检查物理机能否 ping 通宿主机 IP。
  2. 检查宿主机的防火墙(firewalld/iptables)是否放行了映射端口。
  3. 确认 MySQL 用户的 host 权限已设置为 %

如何提交 Docker 镜像改变

Docker 容器遵循“写时复制 (Copy-on-Write)”机制。如果你在运行中的容器内修改了配置文件或安装了新软件,这些改变默认只存在于内存和容器层中。为了将这些改变固化成一个新的镜像,我们需要用到 diffcommit

1. 查看容器变动 (docker diff)

在提交改变之前,我们可以先看看容器内部到底发生了哪些文件级别的变化。

1
docker diff [容器名]
  • **A (Add)**:表示该文件或目录是新添加的。
  • **C (Change)**:表示该文件或目录的内容已被修改。
  • **D (Delete)**:表示该文件或目录已被删除。

2. 提交快照 (docker commit)

docker commit 的功能类似于云服务器的“快照”或虚拟机的“挂起状态保存”。它会将当前容器的状态固化为一个全新的镜像。

提交流程图解:

Docker Commit 流程图解

操作命令:

1
docker commit -a "John" -m "added custom config" mysql_temp mysql_custom:v1.0
  • **-a (author)**:作者信息。
  • **-m (message)**:提交描述信息(建议填写的像 git commit 一样清晰)。
  • **-p (pause)**:在提交时暂停容器运行(默认为开启,保证数据一致性)。

3. 灵活的标签管理 (docker tag)

docker tag 用于给已有镜像起一个“别名”。这在版本管理和发布时非常有用。

  • 原理:它类似于 Java 中的引用,或者是 Linux 的硬链接。同一个 Image ID 可以对应多个不同的 Tag。
  • 优势:它不占用额外的磁盘空间,只是在元数据中增加了一个引用记录。建议使用 BeanUtils.copyProperties 来类比记忆:保持了属性一致,但赋予了新的标识。

4. 镜像库维护 (docker image prune)

随着开发的深入,本地会产生很多 tag<none> 的虚悬镜像(Dangling Images)。

  • 清理命令docker image prune
  • 作用:一键删除所有未被容器使用的垃圾镜像,释放磁盘空间。

如何转存 Docker 镜像

在实际开发中,我们经常需要将一个服务器上的 Docker 镜像或运行中的容器迁移到另一台服务器。Docker 提供了两套不同的机制来实现这一目标:镜像保存与加载 (Save/Load) 以及 **容器导出与导入 (Export/Import)**。

1. 镜像保存与加载 (docker save/load)

这是最常用的备份与迁移方式,它直接操作的是 镜像(Image)

  • 保存镜像:将一个或多个镜像打包成 tar 文件。
    1
    docker save -o mysql8.tar mysql:8.0.27
  • 加载镜像:将打包好的 tar 文件还原为 Docker 镜像。
    1
    docker load -i mysql8.tar
  • 优点:能够完整保留镜像的 分层历史(Layers)、元数据以及版本标签。适合在正式环境之间迁移标准镜像。

2. 容器导出与导入 (docker export/import)

这种方式操作的是 容器(Container),类似于对当前运行中的文件系统做一次“快照”。

  • 导出容器:将容器的文件系统打包。
    1
    docker export -o my_running_mysql.tar mysql_container
  • 导入为镜像:将导出的包导入为一个新的镜像。
    1
    docker import my_running_mysql.tar my_new_image:v1.0
  • 缺点:会 丢弃所有历史分层信息,将文件系统“打平”成一层。导入后的镜像无法查看到原始镜像的制作过程。

3. 核心机制对比图

为了帮助大家根据不同场景选择合适的工具,请参考下图:

Docker 传输机制对比图

总结对比表:

特性 docker save/load docker export/import
操作对象 镜像 (Image) 容器 (Container)
分层信息 保留 完整历史层 丢弃,文件系统打平
镜像大小 通常较大(包含历史层) 通常较小(仅当前状态)
适用场景 镜像分发、版本备份 运行状态迁移、减小镜像体积

如何推送 Docker 镜像到云仓库

在本地完成业务镜像的构建和测试后,我们通常需要将其推送到云端仓库(如 Docker Hub 或阿里云镜像服务),以便在生产环境或其他机器上拉取。

1. 账号认证 (docker login)

在进行任何推送操作之前,必须先通过命令行登录你的云端账号。

1
docker login
  • 执行后输入你在 Docker Hub 注册的用户名和密码。
  • 登录成功后,认证信息会以加密 token 的形式存储在本地配置文件中,后续操作无需反复登录。

2. 镜像命名规范 (关键点)

云端仓库对镜像的命名有严格的要求。如果你直接推送 mysql:8.0,Docker 会默认尝试推送到官方库,这显然会因权限不足而失败。

  • 规范格式<用户名>/<仓库名>:<标签>
  • 操作示例
    1
    2
    # 将本地镜像重新打标为符合规范的名称
    docker tag my-app:v1.0 neozhang/my-app:v1.0

3. 推送镜像 (docker push)

万事俱备,使用 push 命令即可将镜像上云。

推送流程全景图:

Docker 镜像上云流程图

操作指令:

1
docker push neozhang/my-app:v1.0
  • 常用选项
    • --all-tags:推送该仓库下的所有标签镜像。
    • -q (quiet):静默模式,减少输出内容。

4. 适用场景

  • **持续集成 (CI/CD)**:代码构建成镜像后自动推送到云端。
  • 异地协同:不同地区的团队通过公有云/私有云仓库共享业务镜像。
  • 环境一致性:确保线上生产环境拉取的镜像与开发测试环境完全一致。

总结:为什么我们强烈建议在这个项目中使用 Docker?

通过本文的学习,我们从底层隔离原理、自动化安装,到进阶的数据挂载、镜像迁移以及云端发布,全方位掌握了 Docker 的核心能力。

作为一名开发者,我强烈建议在本项目以及未来的企业级开发中优先采用 Docker,理由如下:

  1. 环境铁律(Environment Fidelity):彻底终结“我电脑上明明能跑”的噩梦。无论是本地开发、测试环境还是生产集群,运行的都是同一个镜像,确保了环境的高度一致性。
  2. 极速交付(Agile Deployment):相比于传统安装中間件动辄数小时的配置过程,Docker 只需要几条命令。在接下来的文章中,我们将利用 Docker 一键拉起 MySQL、Redis、RabbitMQ 等全套集群,效率提升 10 倍以上。
  3. 极致资源利用(Resource Efficiency):Docker 的容器化机制比虚拟机更轻量、更省内存。在一台普通的云服务器上,我们可以轻松运行几十个微服务容器,而无需担心性能瓶颈。
  4. 架构演进的基础(Cloud Native Foundation):掌握了 Docker,你就拿到了通往 K8s(Kubernetes)、云原生和 DevOps 大门的入场券。

掌握 Docker 不仅仅是学会了几个命令,更是思维方式的升级。我们可以通过实操,利用 Docker 部署本项目的核心中间件集群,提升开发效率。

参考资料

在深入学习 Docker 的过程中,以下资源提供了极大的帮助:

------ 本文结束------
如果你喜欢这篇文章,打赏一下让我开心到原地转圈圈~,金额随意,感谢鼓励与支持!