JAVA高级工程师--虚拟化与容器化Docker

一、虚拟化技术

虚拟化技术的核心思想是:在一台物理服务器上,创建多个完全隔离的、模拟完整硬件环境的虚拟机。

  1. 关键特点:

    • 强隔离性:每个虚拟机之间完全隔离,一个虚拟机的崩溃或安全问题不会影响其他虚拟机。

    • 高度兼容性:可以运行任何支持底层硬件架构的操作系统(例如,在AMD服务器上虚拟出一台ARM架构的虚拟机)。

    • 资源开销大:每个虚拟机都运行一个完整的操作系统,消耗大量的CPU、内存和存储资源。

  2. 优点:

    • 安全性高,隔离彻底。

    • 可以整合不同操作系统的遗留应用。

    • 技术成熟,生态完善。

  3. 缺点:

    • 资源利用率相对较低(操作系统冗余)。

    • 启动速度慢(需要启动整个操作系统)。

    • 虚拟机镜像文件大(通常以GB计),迁移和分发不便。


  • 代表产品: VMware vSphere, Microsoft Hyper-V, KVM, Xen, Oracle VirtualBox。

二、容器化技术

容器化技术的核心思想是:在操作系统层面进行隔离,将一个应用及其所有依赖项(库、配置文件、环境变量等)打包成一个标准化的、轻量级的、可移植的单元。

  1. 关键特点:

    • 极致的轻量级:容器镜像体积小(通常以MB计),因为它不包含操作系统。

    • 启动速度快:通常在秒级甚至毫秒级,因为无需启动内核。

    • 高资源利用率:共享内核,几乎没有额外开销。

    • 标准化与便携性:应用被一次性打包后,可以在任何支持容器的环境中以相同的方式运行。

  2. 优点:

    • 开发和部署效率极高,实现了CI/CD的理想状态。

    • 资源利用率高,成本低。

    • 非常适合微服务架构,每个服务可以独立打包、部署和扩展。

  3. 缺点:

    • 隔离性不如虚拟机(所有容器共享主机内核,内核漏洞可能影响所有容器)。

    • 只能运行与主机内核相同操作系统的应用(例如,Linux主机只能运行Linux容器)。

  4. 代表产品: Docker(容器运行时和打包工具),containerd(行业标准的容器运行时),Kubernetes(容器编排系统)。

三、虚拟化与容器化区别

特性虚拟化容器化
隔离级别硬件级操作系统级
虚拟化对象完整的硬件系统主机操作系统
Guest OS每个VM都需要独立的OS

所有容器共享主机OS内核,不共享整个操作系统。

容器之所以如此轻量、快速的根本原因:

它避免了像虚拟机那样为每个实例重复运行一个完整操作系统的巨大开销。

启动时间分钟级秒级/毫秒级
性能开销较高(OS开销)极低(几乎无损)
磁盘占用较大(GB级别)较小(MB级别)
隔离性,更安全较弱,依赖内核安全性
可移植性一般(受Hypervisor类型影响)极强(一次构建,随处运行)
包含内容 完整客户操作系统+应用 + 依赖库 应用 + 依赖库 + 必要的用户空间文件
典型应用场景传统应用、混合OS环境、强安全隔离需求微服务、云原生应用、CI/CD、高密度部署
其他

操作系统必须兼容:因为容器共享主机内核,所以你无法在一个Linux主机上运行一个Windows容器,

安全性:共享内核,所以攻击者如果从容器内攻破内核,就有可能影响到主机和其他容器。虽然容器之间有隔离,但这种隔离强度不如虚拟机之间基于硬件的隔离。因此,不能完全信任的代码在容器中运行需要更加谨慎。

  • 虚拟化:帮你创建多台独立的“虚拟电脑”。硬件cpu6核,你最多也只能创建6台虚拟电脑。

  • 容器化:帮你为每个应用创建一个独立的“包装箱”

  • 虚拟化就像在一栋大楼里盖了多个独栋别墅,每个别墅有自己的地基、墙体、水电系统(完整OS),互不影响但占地大。

  • 容器化就像在一栋大楼里划分了多个单身公寓,它们共享大楼的地基和主体结构(主机OS内核),但每个公寓有自己独立的房间、水电表(隔离的进程、文件系统),非常高效节省空间。

容器内的“操作系统”实际上更多是提供文件系统和用户环境,而不是一个独立的内核。

使用容器化技术的完整的操作系统=共享内核+容器化“操作系统”

特性操作系统内核容器“操作系统”
定义核心程序,资源管理者软件集合,包括内核和其他组件
范围小,是操作系统的一部分大,包含了内核
功能进程、内存、设备、系统调用管理除了内核的功能,还提供用户界面、工具和应用
交互对象直接与硬件交互通过内核与硬件交互,并与用户和应用程序交互
可见性对用户是“隐形”的,在后台工作对用户是“可见”的,用户直接与操作系统的界面和工具打交道

四、Docker

4.1架构图

 Docker “一次构建,到处运行” 的秘诀:所有依赖和配置都被打包在镜像中,然后由统一的 Docker Daemon 在任何具备 Docker 环境的主机上将其转化为运行的容器,而 Registry 则保证了镜像的共享和分发。

组件角色类比
Client用户接口游戏的手柄或遥控器
Docker Daemon核心引擎游戏主机本身,负责所有计算和执行
Images只读模板游戏光盘(软件本身)
Containers容器运行实例正在运行的游戏进程(你可以有多个存档)
Registry镜像应用商店PlayStation Store 或 Steam(下载游戏的地方)

4.2 隔离特性

Docker 的隔离性是其核心特性之一,它允许多个容器安全地运行在同一台主机上。这种隔离并非像虚拟机那样通过虚拟化硬件来实现,而是主要通过一系列 Linux 内核特性来完成的。

其核心可以概括为两大机制:Namespace(命名空间) 和 Cgroups(控制组)

Docker 主要使用了以下几种 Namespace:PID Namespace (进程隔离)、NET Namespace (网络隔离)、Mount Namespace (文件系统隔离)、UTS Namespace (主机名隔离)、IPC Namespace (进程间通信隔离)、User Namespace (用户隔离)

Namespace 解决了“能否看见”的问题,而 Cgroups 解决了“能用多少”的问题。它用于限制、记录和隔离进程组所使用的物理资源

  1. 资源限制 (Limiting)

    • 内存:可以限制容器使用的最大内存量。如果容器尝试使用超过此限制的内存,系统会将其终止(OOM Killer)。

    • CPU:可以设置容器使用 CPU 的权重(相对比例)或硬性限制(只能使用几个核心,或只能使用多少微秒的时间片)。

    • 磁盘 I/O:可以限制容器读写磁盘的速度。

  2. 优先级设定 (Prioritization)

    • 可以设置哪些容器组可以获得更多的 CPU 时间或磁盘 I/O 带宽。

  3. 资源核算 (Accounting)

    • 测量一组进程使用了多少资源(如 CPU 时间、内存等),这对于计费和监控非常有用。

  4. 控制 (Control)

    可以冻结、挂起或重启一组进程。

除了 Namespace 和 Cgroups,Docker 还利用其他技术来加强隔离和安全性:

  • Capabilities (权能)

    • Linux 内核将 root 用户的超级权限分割成了几十个不同的“能力”(Capabilities),例如 CAP_NET_ADMIN(网络管理权限)、CAP_SYS_ADMIN(系统管理权限)等。

    • Docker 默认会丢弃容器进程的所有非必要权能,只保留运行所必需的最小权限集。这遵循了“最小权限原则”,即使容器以 root 运行,其权限也被大大削弱。

  • Seccomp (安全计算模式)

    • 它是一个内核安全功能,用于限制进程可以调用的系统调用(syscall)

    • Docker 使用一个默认的 Seccomp 配置文件,它会阻止容器内进行数百个不必要的或危险的系统调用(如 reboot),极大地减少了攻击面。

  • 文件系统分层与 Copy-on-Write (写时复制)

    • 虽然不直接提供运行时隔离,但镜像的分层结构和联合文件系统(如 Overlay2)确保了容器文件系统的变更彼此独立,互不影响。

机制主要作用类比
Namespace视野隔离:让容器以为自己独占系统资源公寓的墙壁:让你看不到邻居,也听不到邻居的声音
Control Groups (cgroups)资源隔离:限制容器能使用的资源量水电表和控制阀:限制每个公寓的水电用量,防止一家用光所有资源
Capabilities权限隔离:削减容器内 root 用户的权限给管理员分配不同的门禁卡:有的只能进大门,有的能进机房
Seccomp系统调用隔离:限制容器能执行的底层指令一份允许使用的工具清单:禁止使用锤子、锯子等危险工具

4.3 docker应用场景

主要应用场景包括:

  1. 标准化应用交付(Build, Ship, and Run Any App, Anywhere)

    • 场景:开发在本地写完代码,打包成 Docker 镜像。这个镜像可以毫无变化地在测试、预发布和生产环境中运行,彻底杜绝了“在我电脑上是好的”这类问题。

  2. 微服务架构(Microservices)

    • 场景:将一个大型单体应用拆分为数十个甚至上百个小型、松耦合的微服务。每个微服务都可以用不同的技术栈开发,并独立地打包为一个 Docker 容器,独立部署、扩展和更新。

  3. 持续集成/持续部署(CI/CD)

    • 场景:在自动化流水线中,每个构建阶段都可以在一個全新的、纯净的 Docker 容器中完成(例如编译、测试),任务结束后容器立即销毁,保证了环境的一致性和清洁性,极大提高了 CI/CD 的效率和可靠性。

  4. 快速搭建和销毁测试环境

    • 场景:测试人员需要测试某个特定版本的应用,只需一条 docker run 命令即可获得一个完整的、隔离的测试环境。测试完成后,用 docker stop 和 docker rm 即可彻底清理,不留任何垃圾。

  5. 平台即服务(PaaS)

    • 场景:许多云平台(如 AWS ECS, Google Cloud Run, Kubernetes)都以 Docker 容器作为基本的部署单位。开发者只需关心容器镜像,平台负责调度、运行和扩缩容。

  6. 混合云与多云部署

    • 场景:企业可以在本地数据中心构建镜像,然后轻松地将其部署到阿里云、腾讯云、AWS 等任何公有云上,实现了真正的应用可移植性,避免了云厂商锁定。

  7. 高密度部署与资源优化

    • 场景:与虚拟机相比,Docker 容器更加轻量,启动秒级完成。可以在同一台物理机上运行数百个容器,极大地提高了服务器资源利用率,降低了成本。

特性/优势为什么受欢迎(带来的好处)
环境一致性彻底解决了“开发/测试/生产环境不一致”的问题。 镜像包含了运行所需的一切,保证了应用在任何地方的行为都完全相同。
隔离性资源隔离,互不影响。 每个容器拥有自己的文件系统、网络和进程空间,一个容器里的应用崩溃不会影响其他容器或宿主机。
轻量高效与传统虚拟机相比,占用资源极少,性能接近原生。 容器直接共享主机操作系统内核,无需额外的操作系统开销,启动速度极快(秒级)。
快速部署与扩展一键部署和水平扩展。 通过镜像可以瞬间启动大量相同的容器实例,非常适合微服务架构和应对突发流量,是实现 DevOps 和云原生的基石。
易于迁移“一次构建,处处运行”。 打包成镜像后,可以运行在任何安装有 Docker 的机器上,无论是物理机、虚拟机、私有云还是公有云。
版本控制和回滚镜像本身具有版本管理。 可以轻松地回滚到之前的任何一个镜像版本,部署和回滚操作变得非常简单和可靠。
活跃的社区和生态拥有极其丰富的公共镜像库(Docker Hub)。 可以轻松获取几乎所有主流软件(MySQL, Nginx, Redis等)的官方镜像,省去了繁琐的安装配置过程。
促进 DevOps 文化统一了开发和运维的工作单位。 开发交付的是容器镜像,运维部署的是容器镜像,减少了沟通成本,提升了协作效率。

4.4 镜像库

1. 默认镜像库:Docker Hub

当你执行像 docker pull nginx 或 docker run ubuntu 这样的命令时,Docker 默认会从 Docker Hub 拉取镜像。

  • 地址https://hub.docker.com/ (Web界面)

  • Registry 服务器地址docker.io (Docker守护进程实际连接的下拉地址)

所以,docker pull nginx 实际上是 docker pull docker.io/library/nginx:latest 的简写形式。

2.第三方公共镜像库

除了 Docker Hub,还有许多其他的公共镜像库,它们也有自己的地址:

名称地址 (用于docker pull)说明
Google Container Registrygcr.io/<project>/<image>Google Cloud 的镜像库,Kubernetes 等很多项目使用
GitHub Container Registryghcr.io/<user>/<image>GitHub 提供的镜像库,与代码仓库集成紧密
Amazon ECR Public Gallerypublic.ecr.aws/<alias>/<image>AWS 提供的公共镜像库
Red Hat Quayquay.io/<user>/<image>Red Hat 提供的镜像库,很多云原生项目在此
微软 MCRmcr.microsoft.com/<image>微软产品的官方镜像库(如.NET, Azure服
# 从 GitHub Container Registry 拉取镜像
docker pull ghcr.io/username/project/image:tag

# 从 Google Container Registry 拉取镜像
docker pull gcr.io/google-samples/hello-app:1.0

3.私有镜像库

很多企业为了安全和管理需要,会搭建自己的私有镜像库。最常见的私有镜像库是 Docker Registry(开源)和 Harbor(企业级)。

可以自搭或者使用云厂商提供的私有库。

类型典型地址示例说明
默认库docker.ioDocker Hub,所有命令的默认来源
官方组织镜像docker.io/library/nginxlibrary/ 可省略,直接写 nginx
第三方公共库ghcr.iogcr.ioquay.io需要指定完整地址
私有库/自建库my.company.com:5000通常需要配置 insecure-registries
镜像加速器https://docker.mirrors.ustc.edu.cn国内用户必备,配置在 registry-mirrors

4.5 镜像加速

直接使用docker的镜像库非常慢,经常pull不到镜像,所以配置镜像加速器。阿里云登录后,阿里云容器镜像服务(ACR)为个人用户分配的专属镜像加速器地址https://xxxxxx.mirror.aliyuncs.com是一个代理缓存镜像加速器。当您的 Docker 客户端请求拉取镜像(例如 docker pull nginx)时,它会首先向这个加速器地址请求。

4.6 自动重启

docker重启后,redis、mysql会关闭。每次都要重启。
我们可以设置自动重启。这个自动重启是docker重启后的自动重启,而不是虚拟机的自动重启。

docker update ruoyi-mysql  --restart=always
docker update ruoyi-mysql  --restart=always
docker update ruoyi-mysql  --restart=always

五、docker与docker-compose的区别

  • Docker 用于管理单个容器。它是一个命令行工具,负责构建镜像和运行容器。构建镜像: 使用 Dockerfile 来定义如何构建一个镜像。运行单个服务或容器(如一个 Nginx 服务器、一个 Redis 缓存)

  • Docker Compose 用于管理多个容器组成的应用。它是一个命令行工具,通过一个YAML文件来定义和运行复杂的多容器应用。Docker Compose 是一个在 Docker 之上的工具,Docker Compose 依赖于 Docker。你必须先安装 Docker Engine,才能安装和使用 Docker Compose。Compose 本质上是一个调用 Docker API 来帮你批量管理容器的脚本工具。

  • 它解决了“如何编排一组相关联的容器”的问题。多容器定义在一个 docker-compose.yml 文件中定义所有相关的服务(容器)、网络、数据卷

特性DockerDocker Compose
核心概念容器运行时和构建工具多容器编排工具
配置方式命令行参数  Dockerfiledocker-compose.yml 文件
管理目标单个容器多个容器(整个应用栈)
网络管理手动创建和管理用户自定义网络自动创建和管理项目专属网络,服务间可通过服务名通信
适用场景运行独立组件,需要精细控制开发、测试、部署多服务应用

六、常用命令

Docker 常用命令

Docker 命令主要围绕镜像 (Image)容器 (Container)网络 (Network) 和卷 (Volume) 进行管理。

1. 镜像管理 (Images)
命令说明示例
docker images列出本地所有镜像docker images
docker pull <镜像名>从仓库拉取镜像docker pull nginx:latest
docker build -t <标签> .根据当前目录的 Dockerfile 构建镜像docker build -t my-app:1.0 .
docker rmi <镜像ID或名>删除一个本地镜像docker rmi my-app:1.0
docker image prune删除所有未被使用的镜像(悬空镜像)docker image prune -a
2. 容器生命周期 (Container Lifecycle)
命令说明示例
docker run [选项] <镜像>创建并启动一个新容器docker run -d -p 80:80 --name my-nginx nginx
docker start <容器>启动一个已停止的容器docker start my-nginx
docker stop <容器>优雅地停止一个运行中的容器docker stop my-nginx
docker restart <容器>重启容器docker restart my-nginx
docker kill <容器>强制立即停止一个容器docker kill my-nginx
docker rm <容器>删除一个已停止的容器docker rm my-nginx
docker container prune删除所有已停止的容器docker container prune

常用 docker run 选项:

  • -d:后台运行(守护进程模式)

  • -p <主机端口>:<容器端口>:端口映射

  • --name <名称>:为容器指定一个名字

  • -v <主机目录>:<容器目录>:挂载数据卷

  • -e <变量名>=<值>:设置环境变量

  • --network <网络名>:将容器连接到指定网络

  • -it:交互模式运行(通常与 /bin/bash 连用)

3. 查看和信息 (Inspection & Logs)
命令说明示例
docker ps列出正在运行的容器docker ps
docker ps -a列出所有容器(包括已停止的)docker ps -a
docker logs <容器>查看容器的日志输出docker logs my-nginx
docker logs -f <容器>实时跟踪(Follow) 日志输出docker logs -f my-nginx
docker inspect <容器/镜像>查看容器或镜像的详细配置信息(JSON格式)docker inspect my-nginx
docker exec -it <容器> <命令>在正在运行的容器中执行命令docker exec -it my-nginx /bin/bash
4. 网络和卷管理 (Network & Volume)
命令说明示例
docker network ls列出所有网络docker network ls
docker volume ls列出所有数据卷docker volume ls
docker volume create <卷名>创建一个数据卷docker volume create my-data

Docker Compose 常用命令

Docker Compose 命令通常在包含 docker-compose.yml 文件的目录中执行。

1. 核心命令
命令说明示例
docker-compose up创建并启动所有服务(前台运行)docker-compose up
docker-compose up -d创建并启动所有服务(后台运行docker-compose up -d
docker-compose down停止并移除所有容器、网络、卷(默认不移除卷)docker-compose down
docker-compose down -v停止并移除所有容器、网络以及Volumes数据卷docker-compose down -v
docker-compose start启动已存在的服务容器docker-compose start
docker-compose stop停止服务容器,不删除docker-compose stop
docker-compose restart重启服务容器docker-compose restart
2. 查看和信息
命令说明示例
docker-compose ps列出本项目下的所有容器docker-compose ps
docker-compose logs查看所有服务的日志docker-compose logs
docker-compose logs -f <服务名>实时跟踪特定服务的日志docker-compose logs -f web
docker-compose exec <服务名> <命令>在指定的服务容器中执行命令docker-compose exec db psql -U postgres
docker-compose images列出本项目使用的所有镜像docker-compose images
3. 管理和调试
命令说明示例
docker-compose build重新构建项目中的服务镜像docker-compose build
docker-compose pull拉取服务依赖的镜像docker-compose pull
docker-compose run <服务名> <命令>一次性运行一个服务并执行命令(会启动依赖项)docker-compose run web python manage.py test
docker-compose pause/unpause <服务名>暂停/恢复一个服务容器docker-compose pause web
docker-compose top显示各个容器内运行的进程docker-compose top

七、docker与docker-compose的版本关系

  1. 独立版本的 Docker Compose (docker-compose v1.x):这是一个用 Python 编写的独立项目,需要通过 pip、软件包管理器或直接下载二进制文件来单独安装。

  2. Docker 内置的 Compose (docker-compose v2.x):这是一个用 Go 编写的插件,直接集成在 Docker CLI 中,通过 docker compose(注意没有横线)命令调用。这是目前官方推荐的方式。


1. 独立版本的 Docker Compose (v1.x)

对于传统的 docker-compose(带横线,版本 1.x),其与 Docker 引擎版本的兼容性相对宽松,但官方有一个建议的对应关系。您可以在 Docker 官方的 Compose 文件版本页面找到历史信息。

一个常见的建议匹配关系如下(但并非绝对严格):

Docker Engine 版本建议的 Docker Compose 版本
1.10.x1.8.x 或 1.9.x
1.11.x - 1.12.x1.9.x - 1.10.x
1.13.x1.10.x - 1.11.x
17.04.x (CE)1.11.x - 1.12.x
17.05.x (CE) +1.13.x+

2. 现代方式:Docker 内置的 Compose 插件 (v2.x)

从 Docker Desktop 开始(对于 Windows 和 Mac 用户),Docker Compose V2 已经直接内置并作为 Docker CLI 的一个插件提供对于 Linux 用户,也需要单独安装这个插件

这是目前绝对推荐的方式,其版本管理变得非常简单:

  • 版本同步:Docker Compose V2 的版本号现在与 Docker Engine 的版本号同步发布。当您安装或更新 Docker Desktop(或 Linux 上的 Docker Engine + Compose 插件)时,您会同时获得匹配的 Docker 引擎和 Docker Compose 版本。

  • 无需担心兼容性:官方会确保每个版本的 Docker Desktop/Docker Engine 都包含一个完全兼容的 Compose 插件版本。您几乎不需要再手动查询版本匹配表。

  • 命令变化:V2 使用 docker compose(没有横线)命令。虽然为了兼容性,很多系统仍保留 docker-compose(带横线)的 V1 版本,但你应该优先使用新命令。

  • 弃用 V1,使用 V2:不要再安装或使用旧的 docker-compose V1。请使用 docker compose V2 插件。如果你是 Docker Desktop 用户,你已经拥有它了。

八、docker 端口映射与文件挂载

8.1docker端口映射

端口映射是将宿主机(Host Machine)(服务器或者你本机) 的端口与容器(Container) 的端口绑定起来,使得外部网络可以通过访问宿主机的端口来访问容器内运行的服务。

默认情况下,容器有自己的内部网络和 IP 地址,与宿主机是隔离的。外部机器(包括宿主机本身)无法直接访问容器内的服务(如 Web 服务器的 80 端口)。端口映射打通了这条通道。如下图,都是先请求docker容器的。

1.docker进行端口映射:

# 将容器的 80 端口映射到宿主机的 8080 端口
# 访问 http://宿主机IP:8080 即可访问容器内的服务
docker run -d -p 8080:80 nginx

# 映射多个端口
docker run -d -p 8080:80 -p 4430:443 nginx

# 将容器的 80 端口随机映射到宿主机的一个空闲端口(宿主机端口由 Docker 自动分配)
docker run -d -p 80 nginx
# 使用 `docker ps` 查看具体映射到了哪个端口(例如 0.0.0.0:32768->80/tcp)

# 指定宿主机监听的具体IP(例如只监听本机 127.0.0.1)
docker run -d -p 127.0.0.1:8080:80 nginx

2.docker-compose.yml

services:
  ruoyi-nacos:
    container_name: ruoyi-nacos
    image: nacos/nacos-server
    build:
      context: ./nacos
    environment:
      - NACOS_AUTH_ENABLE=false
    volumes:
      - ./nacos/logs/:/home/nacos/logs
      - ./nacos/conf/application.properties:/home/nacos/conf/application.properties
    ports:
      - "8848:8848"  # 宿主机端口:容器端口
      - "9848:9848"
      - "9849:9849"
    depends_on:
      - ruoyi-mysql
  ruoyi-mysql:
    container_name: ruoyi-mysql
    image: mysql:5.7
    build:
      context: ./mysql
    ports:
      - "3306:3306" # 宿主机端口:容器端口

8.2 文件挂载

文件挂载是将宿主机上的一个目录或文件“投射”到容器内部的一个路径上。这样,容器对该路径的读写操作会直接反映在宿主机上。这是容器持久化数据和配置的主要方式。

容器本身是** ephemeral(易变的)** 的。如果容器被删除,其内部文件系统上的所有更改也会丢失(永远不要将有价值的数据放在容器内部。通过挂载,重要的数据(如数据库文件、日志、配置文件)可以存储在容器之外(宿主机上),从而独立于容器的生命周期存在。

1.三种主要的挂载类型:

类型说明优点缺点
绑定挂载 (Bind Mount)将宿主机上的任意特定路径挂载到容器。灵活,可直接修改宿主机上的文件。依赖宿主机的特定目录结构,移植性差。
卷挂载 (Volume)由 Docker 管理的宿主机上的一个存储区域(通常在 /var/lib/docker/volumes/)。推荐方式。易于备份、迁移和管理,与宿主机文件系统解耦。不直接暴露宿主机文件结构。
临时文件系统挂载 (tmpfs mount)将数据挂载到容器的内存中。

2.docker进行文件挂载:

  • 卷挂载(Volume):使用 -v 或 --mount 标志(--mount 语法更清晰)。

    # 使用 -v (常用)
    # 语法: -v <卷名>:<容器路径>
    docker run -d -v my_nginx_data:/usr/share/nginx/html nginx
    
    # 如果卷 ‘my_nginx_data' 不存在,Docker 会自动创建它
    
    # 使用 --mount (语法更明确)
    docker run -d \
      --mount type=volume,source=my_nginx_data,target=/usr/share/nginx/html \
      nginx
  • 绑定挂载(Bind Mount)

    # 使用 -v
    # 语法: -v <宿主机路径>:<容器路径>
    docker run -d -v /home/user/app:/usr/share/nginx/html nginx
    
    # 使用 --mount
    docker run -d \
      --mount type=bind,source=/home/user/app,target=/usr/share/nginx/html \
      nginx
    
    # :ro 表示只读(read-only),容器无法修改挂载的内容
    docker run -d -v /home/user/app:/usr/share/nginx/html:ro nginx
  • tmpfs 挂载

    docker run -d --tmpfs /app/cache nginx

    3.docker-compose.yml 文件

    使用 volumes 关键字在服务级别定义挂载,在顶级 volumes 键中定义命名卷(可选)

 volumes:
      # 绑定挂载
      - ./nginx.conf:/etc/nginx/nginx.conf:ro # 挂载配置文件(只读)
      - ./html:/usr/share/nginx/html          # 挂载网站根目录

      # 卷挂载(‘app-data’ 是一个命名卷,在下方定义)
      - app-data:/app/data

      # 匿名卷(不推荐在Compose中使用,难以管理)
      # - /var/lib/mysql
services:
  ruoyi-nacos:
    container_name: ruoyi-nacos
    image: nacos/nacos-server
    build:
      context: ./nacos
    environment:
      - NACOS_AUTH_ENABLE=false
    volumes:
      - ./nacos/logs/:/home/nacos/logs
      - ./nacos/conf/application.properties:/home/nacos/conf/application.properties

特性端口映射 (-p)文件挂载 (-v / --mount)
目的网络联通性:让外部能访问容器内的服务。数据持久化:让容器数据不随容器消失而丢失。
操作对象网络端口(TCP/UDP)文件或目录
宿主机侧宿主机的 IP 和端口宿主机的文件路径或 Docker 管理的卷
容器侧容器的监听端口容器内的文件系统路径
常用场景Web 服务(Nginx, Apache)、API(Node.js, Python)、数据库(MySQL, Redis)配置文件、网站代码、数据库数据文件、日志文件

九、生产环境docker部署Mysql吗?

1.docker部署Mysql的好处:

  1. 环境标准化与一致性

    Docker 镜像确保了开发、测试、预生产、生产所有环境的绝对一致。彻底避免了“在我本地是好的”这类问题。MySQL 的版本、配置文件、依赖库完全统一。
  2. 快速部署与扩展

    需要扩展时,可以快速基于镜像启动新的 MySQL 实例。这对于搭建读写分离集群、分库分表等场景非常高效。结合 CI/CD,可以实现数据库 schema 变更的自动化部署。
  3. 资源隔离与利用率

    在微服务架构中,可以为不同的服务分配独立的 MySQL 实例(也许不需要很大的规格),避免所有服务共用一个大库带来的单点风险和耦合。Docker 可以方便地限制每个容器的 CPU、内存使用量。
  4. 简化运维与编排

    使用 Docker Compose 或 Kubernetes 的 StatefulSet 可以像管理代码一样定义和管理 MySQL 的部署拓扑(主从复制、集群等),使得基础设施即代码(IaC)成为可能。
  • 传统方式加一个从库:需要找一台新服务器->安装OS->安装MySQL->配置->初始化数据->配置复制。
  • Docker方式加一个从库:在编排文件里定义一个新服务->指向新的存储卷->docker-compose up -d->容器秒级启动->初始化数据->配置复制。

     5.版本管理与回滚

      可以轻松地构建不同版本的 MySQL 镜像(如 5.7、8.0),升级或降级变得相对简单和可控, 只需切换镜像标签并重新部署容器。

2.docker部署Mysql的风险与挑战:

数据持久性(最大的挑战)

风险:容器本身是易变的(ephemeral)。如果容器被删除,其内部的文件系统也会消失。绝对不能将数据存储在容器内部。 

解决方案必须使用 Docker 卷(Volume) 或绑定挂载(Bind Mount) 将 MySQL 的数据目录(如 /var/lib/mysql)持久化到宿主机或远程存储上。数据必须完全独立于容器的生命周期。

性能开销

风险Docker 层和网络虚拟化会带来轻微的性能开销(通常约 1-5%),对于极端高性能、低延迟的场景可能是不可接受的。

解决方案:使用 --net=host 模式(牺牲一些网络隔离性)可以减少网络开销。对磁盘 I/O 要求高的,要确保卷存储在高性能 SSD 上。

网络与连接

风险:容器的 IP 地址是动态的。客户端应用如何发现并连接到数据库?

解决方案:使用 Docker 自定义网络,并通过容器名进行服务发现,或者更好的是,结合外部的服务发现机制(如 Consul)或使用 Kubernetes Service。

安全问题

风险:以 root 用户运行容器内的 MySQL 进程可能存在安全风险。需要确保容器运行在最小权限原则下。

解决方案:在 Dockerfile 中使用 USER 指令指定非 root 用户运行 MySQL 进程。同时,要做好宿主机的安全隔离。

备份与恢复的复杂性

风险:传统的物理备份工具(如 XtraBackup)需要直接访问文件系统,在容器内操作可能更复杂。

解决方案:备份策略应针对持久化卷(Volume)进行。可以从另一个容器挂载同一个卷来执行备份操作,或者使用云平台提供的卷快照功能。

3.但是,有这几个问题是难以解决的:

(1)动态扩展Mysql

docker部署mysql动态扩展式困难的。很多公司在开发一个项目之前,往往就已经确定要了数据库架构,主从、读写分离之类的,很少会通过docker做一些动态的节点扩容。因为我们是应用级别。而云厂商却不是,云厂商所构建的是pass平台,可以通过docker去动态构建mysql,如此可以对我们这些应用级别的企业进行收费,那么这种场景使用docker没问题。普通企业当确定好数据库架构以后,后期是很少会频繁的对数据库做扩容。(需要注意,这里说的是频繁,而不是少数情况,少数情况做扩容优化是有的,次数不多)

(2)数据共享

mysql数据存储在容器内部,如果把容器实例删了,把镜像移除了,那么数据还在吗?很显然不行,所以我们通过磁盘挂载,把数据转移存储在宿主机。虽然可以这样没问题,但是如果说要做数据共享扩容,那么刚刚挂载的目录是不能被多个数据库实例共享的,其他数据库无法写入。多实例数据共享与并发写入问题:当您为另一个MySQL容器挂载同一个宿主机目录时,会发生:

  • 文件锁冲突:第二个MySQL实例启动时,会尝试初始化并写入同样的文件(如 ibdata1ib_logfile0)。这些文件已经被第一个实例打开并锁定了,导致第二个实例启动失败。

  • 数据损坏:即使勉强能同时运行,两个独立的MySQL进程同时读写同一份数据文件,会100%导致数据彻底损坏和崩溃。这就像两个人在同时编辑同一份Word文档并随意保存,文件很快就会变得无法打开。

但是,那不同的Mysql实例我挂载不同路径可以吗?这个我没想清楚,希望大家在评论区给我指导一下。比如这样:

主库容器:-v /data/mysql-master:/var/lib/mysql

从库1容器:-v /data/mysql-slave1:/var/lib/mysql

从库2容器:-v /data/mysql-slave2:/var/lib/mysql

我认为这样的话,服务器的性能得好。

(3)内存独享

数据库无法对资源进行独立专享,当使用docker以后,还有其他的容器实 例,比如redis,mq等等,这个时候大家都会一起使用内存,可能发生内存争抢(当然我们也可以通过设置去限定内存使用),所以数据库也有可能不能被分配到更多的内存,无法做到对服务器内存的独占,可能导致一定的性能影响。所以这是有局限性的。

在传统物理机或虚拟机部署中,数据库通常独享一整台服务器的所有资源(CPU、内存、磁盘I/O)。而在Docker环境中,多个容器(DB、Redis、MQ、App)共享同一个宿主机的资源池。

-m 和 --memory-swap解决这个问题的正确方向和最关键的手段,但它们是一种“软”隔离,并非完美无缺。

  • 内存争抢:即使每个容器都设置了内存限制(-m),当物理内存紧张时,操作系统仍然需要进行内存管理,可能会将一些内存页换出到磁盘(swap),即使你限制了swap,内核也会进行复杂的回收和压缩操作,这些都会带来性能开销。

  • 磁盘I/O争抢:这是另一个容易被忽视但影响巨大的点。MySQL和Redis(如果开启持久化)、MQ等都是磁盘I/O密集型应用。如果它们同时在高负荷运行,会激烈争抢磁盘I/O带宽,导致彼此的读写延迟增加。磁盘I/O的隔离比内存和CPU更复杂,影响也更直接。

  • 网络带宽争抢:大量的网络传输(如MQ的消息、Redis的缓存响应、App的请求)会占用网络带宽,可能影响数据库主从复制的网络同步速度。

容器化确实引入了“邻居噪音” 问题,即一个容器可能被同一台宿主机上其他容器的工作负载所影响。

-m 和 --memory-swap 能解决吗?能解决多少?

docker run -d --name mysql-production \
  -m 8g \                 # 限制容器最多使用 8GB 物理内存
  --memory-swap=8g \      # 禁止使用交换分区(Swap)
  --cpus=4 \              # 限制使用 4 个 CPU 核心
  mysql:8.0
  1. -m 8g (内存限制)

    • 能解决的:它确保了MySQL容器最多只能使用8GB内存。这是一个硬性上限,可以防止它失控耗尽整个宿主机的内存,从而保护了宿主机和其上其他容器的稳定性。这是资源隔离的基石

    • 不能解决的:它不能保证MySQL容器随时都能获得它想要的8G内存。如果宿主机上其他容器也消耗了大量内存,导致宿主机的剩余可用物理内存低于8G,MySQL容器虽然不会超过8G,但它可能无法顺利分配到新的内存页,性能会受到影响。它保证了“上限”,但不保证“下限”。

  2. --memory-swap=8g (Swap限制)

    • 能解决的:这是一个至关重要的生产环境配置。它将容器的虚拟内存(物理内存+Swap)也限制在8G。因为 -m 8g 已经占了全部份额,所以这个设置等效于禁止该容器使用任何Swap

    • 为什么重要:数据库(尤其是MySQL的InnoDB引擎)对内存访问延迟极其敏感。一旦发生Swap(内存交换),性能会出现断崖式下跌。禁止使用Swap是为了换取** predictable(可预测的)和稳定的性能**。宁愿让MySQL在内存不足时直接报错或慢查询,也不要让它陷入缓慢的Swap交换中。

    • 不能解决的:它无法解决物理内存不足的根本问题。当容器需要内存而物理内存不足时,由于不能使用Swap,它可能会触发OOM(Out-Of-Memory)相关行为,导致进程被杀死或请求失败。

(4)不要把鸡蛋放在一个篮子里。

如果使用docker,那么mysql和其他中间件都会在一个容器里,也都会在一个云服务器实例中,如果这个云服务器挂了怎么办?那么数据库将不能被访问,数据库是我们最后一道防线,也是底线,是绝对不能挂的,所以对于这样的风险,我们应该要规避掉。

但是,无论您是否使用 Docker,只要是一台服务器,它就存在挂掉的风险(硬件故障、网络故障、机房故障、云厂商故障等)。Docker 本身不引入这个风险,但它也不解决这个风险。它只是把传统的“一台服务器上安装所有软件”变成了“一台服务器上运行所有容器”,单点故障的本质没有变。风险不在Docker,在于架构。

4.docker部署推荐场景

  • 微服务架构中,为特定服务提供专属的、隔离的数据库实例。应用本身不持久化数据,其行为完全由请求和响应决定。

  • CI/CD 流水线与 DevOps 实践

  • 需要快速搭建开发、测试环境,追求环境的一致性。

  • 复杂依赖环境的标准化交付

  • 快速搭建与销毁的临时环境

  • 团队技术栈统一容器化,具备成熟的 Docker 和 Kubernetes 运维能力。

5.docker部署不推荐场景

  • 对数据库性能极致要求的传统大型单体应用。Docker 的网络虚拟化和存储驱动会带来轻微的性能开销(通常在 1-5%)。对于追求纳秒级延迟或极致磁盘 I/O 的应用,这可能是不可接受的。直接部署在物理机或虚拟机上性能更高

  • 需要特殊硬件访问的应用,虽然 Docker 支持通过 --device 参数将设备挂载到容器内,但配置复杂且可能带来安全和稳定性问题。通常有更适合的工具(如 NVIDIA Container Toolkit for GPU)。

  • 极其简单的小型应用,用传统方式安装管理反而更简单直接。比如静态html页面

  • 对安全有极端要求的隔离环境,Docker 容器共享主机内核,其隔离性弱于完整的虚拟机(VM)。虽然安全性已经很高,但理论上存在内核漏洞导致容器逃逸的风险。在需要强隔离的场景下,VM 仍然是更安全的选择。

  • 技术团队缺乏容器运维经验

十、docker提交容器改变

10.1.查看某个容器发生的改变(操作日志)

docker diff redis

  • A: 添加文件或目录(ADD)
  • D:文件或者目录删除(DELETE)
  • C:文件或者目录更改(CHANGE)

10.2 对更改的容器进行保存

我们平时使用镜像,会做一些自定义,比如配置文件的修改,数据的增删改等等有很多,如果下次还是要部署,那么又得再来一遍。所以我们完全可以保留曾经的配置,把这些已经更改的容器内容作为一个属于自己的全新容器。又或者说,可以把这个作为当时的一个快照,也行吧。

commit把容器的的改变,提交创建为一个全新的镜像

  • -a: 作者信息
  • -c: 可以使用dockerfile提交,暂时用不到
  • -m: 提交的备注信息(注释)
  • -p: 提交的时候暂停容器
# 1.先停止redis
docker stop redis
# 2.提交容器镜像的变更
docker commit -a lee -m "init new redis for myself" redis redis:myRedis

这个时候,多了一个新的镜像

10.3 游离镜像

不需要则删除:

docker image prune

需要则修改tag:

docker tag redis:myRedis redis:myRedis-1.0.10

十一、转存Docker容器镜像

11.1 镜像方式(推荐)

镜像服务器10.3.18.40

想加载镜像的服务器

1.docker  images

2.docker save {想下载镜像的Id}> rabbitmq.tar

3.pwd 获取save后的地址

4.scp root@10.3.18.40:{镜像服务器pwd获取的地址}/rabbitmq.tar  rabbitmq.tar  执行该命令后,需要输入密码的。

5docker load -i /home/images/rabbitmq.tar 执行后存储在当前位置的

6.docker tag {该服务上镜像Id} rabbitmq:v1.0

1.镜像保存

# 保存一个或多个镜像到tar文件
docker save
# 从一个tar文件加载镜像
docker load

1.在镜像服务器上:先查看docker  images ,然后把镜像Id 给docker save 命令并执行,成功后再pwd查看保存路径

我当前执行的docker save,是没有tag的,要么后面补上,要么docker save加上tag,否则就是游离镜像,游离镜像多了后,你会分不清是什么镜像。

2.在需要使用镜像的服务器:从镜像服务器下载docker镜像

---------------------------------------从服务器下载docker镜像----------------------------------------------
scp root@192.168.145.106:/root/redis.tar redis.tar

在redis.r.tar目录中,或者写清redis.tar路径都可以

docker load -i redis.tar

获取镜像Id,再tag

docker tag 6ba7a3e7a5f7e redis:v1.0

11.2 容器方式(不推荐。理由见导入时)

1 导出

把正在运行中的容器(ruoyi-redis是容器名称)导出到一个文件压缩包,然后可以传输到其他服务器进行运行。我在虚拟机环境上导出。

2 拷贝到其他服务器

在我本机(或者另一台服务器上)拷贝。下面的ip是我虚拟机的ip,从虚拟机通过wj用户拷贝文件

scp  wj@192.168.146.130:/home/wj/wj-reids.tar   F:/image/redis.tar

3.导入

docker import wj-redis.tar redis:myRedis-another

但是,导入的镜像是无法docker run的。

docker run -p 7379:6379 --name redisABC -d redis:myRedis-another

需要用原来那个容器的启动命令来启动这个被导出的容器,这玩意记不住,原来容器如果被删了也不好整。当然也有手段去运行这个容器,我们这里不去浪费时间了,那个操作太恶心。了解就行。

所以我们使用另外一种方式进行镜像的保存。之前所做的都是针对容器的导入导出,现在是针对镜像的保存和加载。

十二、Docker 可视化界面监控Portainer

Portainer 是一个轻量级的管理 UI ,可让你轻松管理不同的 Docker 环境(Docker 主机或 Swarm 群集)。

Portainer 的目的是部署和使用一样简单。它由一个可以在任何 Docker 引擎上运行的单一容器组成(可以部署为 Linux 容器或 Windows 本地容器,也支持其他平台)。Portainer 允许你管理所有的 Docker 资源(容器、镜像、卷、网络等等)。它与独立的 Docker 引擎和 Docker Swarm 模式兼容。

docker pull portainer/portainer

或者也可以搜索

docker search portainer

创建数据卷,这样容器就可以往里面存储数据了。创建一个数据卷之后就可以配置容器去使用它

docker volume create portainer_data
创建完毕以后,多个容器可以同时使用相同的数据卷。如果两个甚至多个容器需要访问共享数据,那么这个数据卷功能会非常有用。比如,一个容器写入数据另一个容器读取数据。
docker volume ls

docker run -p 8000:8000 -p 9000:9000 \
--name=portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
-d portainer/portainer

访问:http://[docker所在服务ip]:9000

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值