全面图解Docker架构设计:掌握Docker全链路思维/实战/优化(小白到大师篇[2])
Docker 是一个革命性的开放平台,用于开发、交付和运行应用程序。通过使用 Docker,开发者可以打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何支持 Docker 的环境中,在不同环境中实现一致的运行。无论是在虚拟机、物理服务器、数据中心还是云平台,Docker 都能确保应用的快速、可靠地部署和运行。
Docker 提供的不仅仅是容器,它还构建了一个庞大的生态系统,包括 Docker Hub、Docker Compose、Docker Swarm 等工具,这些工具涵盖了从开发到生产、从单一容器到容器编排的全方位需求。Docker 还支持多种编程语言、框架和中间件,使其成为现代应用开发和部署的首选工具。
本节内容
5、Docker与VM对比
以下是 Docker 容器与虚拟机(VM)的对比,概括了它们在不同方面的特点和差异:
特性 | Docker 容器 | 虚拟机 |
---|---|---|
概念 | 轻量级的、与应用程序及其依赖打包在一起的可执行软件包。 | 模拟整个计算机系统,拥有完整的操作系统。 |
性能 | 快速启动,高效利用系统资源。 | 启动较慢,资源利用率相对较低。 |
资源利用率 | 高,因为共享宿主机的操作系统内核。 | 低,每个虚拟机都需要分配独立的资源。 |
隔离性 | 隔离性相对较弱,但容器之间是相互隔离的。 | 隔离性强,每个虚拟机都有独立的操作系统。 |
可移植性 | 高,容器可以在不同的环境中快速部署。 | 较低,虚拟机迁移可能涉及复杂的配置。 |
隔离级别 | 进程级别。 | 操作系统级别。 |
安全性 | 依赖于容器技术和镜像的安全。 | 依赖于虚拟化技术和虚拟机操作系统的安全。 |
管理工具 | Docker CLI、Docker Compose、Kubernetes 等。 | VMWare vSphere、Microsoft Hyper-V、VirtualBox 等。 |
成本 | 通常较低,因为可以在相同硬件上运行更多容器。 | 可能较高,因为每个虚拟机都需要分配资源。 |
用途 | 适合微服务、开发测试环境、CI/CD 流程。 | 适合需要完整操作系统环境的应用。 |
选择建议:
- 选择 Docker 容器:
- 如果你正在构建微服务架构的应用。
- 如果你需要快速迭代开发和部署应用。
- 如果你希望在开发、测试和生产环境中保持一致性。
- 如果你想要提高资源的利用率和减少成本。
- 选择虚拟机:
- 如果你需要运行具有不同操作系统的应用。
- 如果你要求有强烈的隔离性和安全性。
- 如果你正在运行对系统资源有特别要求的大型应用。
- 如果你已经投资了虚拟化管理工具和基础设施。
6、Docker镜像处理
Docker 镜像是 Docker 容器运行的基础。它们是轻量级的、可执行的软件包,包含了运行应用所需的所有内容——代码、运行时、库、环境变量和配置文件。以下是 Docker 镜像处理的详细说明:
6.1. 基础概念
- 镜像(Image) :一个只读的模板,其中包含了运行应用所需的所有内容。
- 容器(Container) :镜像的运行实例。容器是镜像的可写层的直接体现。
6.2. 创建镜像
- 使用 Dockerfile:
- Dockerfile 是一个文本文件,包含了用于构建镜像的一系列指令。
- 基于基础镜像,通过 Dockerfile 定义的指令逐步构建新的镜像。
- 提交容器为镜像:
- 运行中的容器可以通过
docker commit
命令保存为新的镜像。
- 运行中的容器可以通过
6.3. 镜像分层
分层存储:
- Docker 镜像是由多个文件系统层组成的,这些层是只读的。每个层代表 Dockerfile 中的一个指令,比如
RUN
,COPY
,ADD
,CMD
等。当 Docker 根据 Dockerfile 构建镜像时,每执行一个指令就会创建一个新的层。 - 这种分层设计有利于复用和加速构建过程。
解释:
- Docker 镜像分层:
- 基础镜像层: 基础操作系统镜像层,比如 Ubuntu 或 Alpine。
- 操作系统层: 包含操作系统的基础环境。
- 软件安装层: 安装应用运行所需的软件包,比如数据库、依赖库等。
- 应用代码层: 包含应用的代码文件。
- 配置文件层: 包含应用的配置文件,如环境变量、启动脚本等。
- 最终镜像: 由上述所有层叠加形成的最终镜像,用于生成容器。
- 容器:
- 容器1, 容器2: 基于同一个镜像运行的多个容器实例。
- 容器1可写层, 容器2可写层: 容器的可写层,用于存储容器运行时产生的数据,如应用生成的日志、数据库文件等。
Docker 镜像分层存储的优点:
- 共享和重用: 多个容器可以共享和重用相同的镜像层,节省存储空间。
- 快速构建: 利用缓存可以避免重复工作,加快构建速度。
- 快速分发: 层可以独立分发,只更新变化的部分,节省带宽。
- 快速部署: 容器启动时叠加层的过程非常快,提高部署效率。
分层存储的操作
- 构建缓存:
- 在构建过程中,Docker 会检查每个指令的缓存。如果发现之前的构建层没有变化,Docker 会跳过该指令的执行,直接使用缓存的层。
- 清理无用层:
- 使用
docker system prune
命令可以清理悬空的镜像层,释放磁盘空间。
- 使用
- 查看镜像层:
- 使用
docker history
命令可以查看镜像的每一层以及对应的构建指令和元数据。
- 使用
案例
Dockerfile 如下:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]
这个 Dockerfile 会产生三个层:一个基础层 ubuntu
,一个包含更新包的层,和一个安装了 nginx
的层。
6.4. 获取镜像
- 拉取官方镜像:
- 使用
docker pull
命令从 Docker Hub 或其他私有仓库拉取官方镜像。
- 使用
- 私有镜像:
- 用户可以创建私有镜像,并将其推送到 Docker Hub 或其他私有仓库。
6.5. 构建镜像
- 执行
docker build
:- 使用
docker build
命令根据 Dockerfile 构建新的镜像。
- 使用
6.6. 管理镜像
- 列出镜像:
- 使用
docker images
命令列出本地主机上的镜像。
- 使用
- 删除镜像:
- 使用
docker rmi
命令删除一个或多个镜像。
- 使用
- 清理镜像:
- 使用
docker system prune
命令清理未使用的镜像。
- 使用
6.7. 分发镜像
- 推送镜像:
- 使用
docker push
命令将镜像推送到远程仓库。
- 使用
- 拉取镜像:
- 使用
docker pull
命令从远程仓库拉取镜像。
- 使用
6.8. 构建缓存
- 利用缓存:
- Docker 会利用之前的构建缓存来加速构建过程。如果 Dockerfile 没有变化,Docker 会复用之前的构建层。
6.9. 多阶段构建
多阶段构建是 Docker 17.05 及更高版本引入的一项非常有用的功能,它允许在同一个 Dockerfile 中通过多个阶段构建镜像,并在每个阶段使用不同的基础镜像。这种构建方式不仅可以减小最终镜像的大小,还可以增强安全性。以下是多阶段构建的详细说明:
基本概念
在多阶段构建中,每个阶段都是独立的,并且可以基于一个不同的基础镜像。每个阶段中的指令(如 RUN
, COPY
, ADD
等)只会影响当前阶段的镜像层。最终的镜像将只包含你明确复制到新阶段的文件。
使用场景
- 减小镜像大小:通过在一个阶段中编译应用,并在后续阶段中仅复制编译产物到新的基础镜像,可以显著减小最终镜像的大小。
- 增强安全性:你可以在一个阶段中安装和运行编译工具,而在后续阶段中使用一个更小、更受限制的基础镜像运行编译后的应用。
案例
以下是一个多阶段 Dockerfile 的案例:
# 第一阶段: 构建阶段
# 使用带有Node.js环境的官方镜像
FROM node:16 AS build-stage
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装项目依赖
RUN npm ci
# 复制项目文件到工作目录
COPY . .
# 构建应用,输出路径为/dist
RUN npm run build
# 最终阶段: 生产环境
# 使用一个轻量级的base镜像
FROM nginx:alpine
# 将构建阶段的静态文件复制到nginx服务中
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 暴露80端口
EXPOSE 80
# 设置启动命令,启动nginx服务
CMD ["nginx", "-g", "daemon off;"]
详细解释
构建阶段 (build-stage
):
- 基础镜像:
node:16
,选择一个带有Node.js环境的官方镜像作为构建环境。 - 工作目录: 设置容器内的工作目录为
/app
。 - 复制依赖文件: 先将
package.json
和package-lock.json
复制到工作目录中。这允许npm ci
命令仅安装必要的依赖,提高构建性能。 - 安装依赖: 运行
npm ci
安装项目依赖。ci
命令是持续集成环境下推荐的方式,因为它会根据package-lock.json
精确安装依赖。 - 复制项目文件: 复制所有项目文件到工作目录,这样我们就可以在容器内进行构建。
- 构建应用: 执行
npm run build
命令来构建应用。这里假设项目中有一个build
脚本,它会将应用构建为静态文件,输出到/dist
目录。
生产环境阶段:
- 基础镜像:
nginx:alpine
,使用基于 Alpine Linux 的 Nginx 镜像,它比标准的 Nginx 镜像更小,因为它不包含许多默认的模块和文档。 - 复制静态文件: 从构建阶段的
/app/dist
目录复制静态文件到 Nginx 的静态文件目录/usr/share/nginx/html
。 - 端口暴露: 暴露容器的 80 端口,这是 Web 服务的标准端口。
- 启动命令: 设置容器启动时执行的命令,这里使用
nginx
的参数-g "daemon off;"
来确保 Nginx 在前台运行。
构建和运行
构建镜像:
docker build -t my-web-app .
运行容器:
docker run -p 80:80 my-web-app
通过以上步骤,我们利用多阶段构建的优势,先在具有完备工具链的环境下完成应用的构建,然后将构建产物部署到一个轻量级的运行时环境中。这样既保证了构建的便利性和最终镜像的小巧性,又提升了运行时的安全性。
6.10. 安全扫描
- 扫描镜像:
- 使用 Docker Scanner 或其他工具扫描镜像中的安全漏洞。
7、Docker容器与业务数据关系
容器设计为无状态的运行实例,但业务应用通常需要持久化数据以进行读写操作
7.1 数据存储策略
1. 临时文件系统(tmpfs)
容器内部的某些目录可以被设置为 tmpfs(内存中的临时文件系统),这样数据将在容器重启时消失。
2. 挂载(bind)
挂载是将宿主机的文件系统目录挂载到容器内部的技术。任何在挂载点的读写操作都会直接影响宿主机的文件系统。
3. Docker 卷(volume)
Docker 卷是专门设计用于持久化容器数据的。它们可以独立于容器生命周期,并且可以在多个容器之间共享数据。
4. 文件系统(Filesystem)
容器内部的文件系统是只读的,但 Docker 允许在特定目录(如 /app
)中进行写操作。这些更改在容器重启后会丢失,除非使用卷或挂载。
5. 内存(Memory)
容器内的内存管理与业务数据的关系体现在容器可以使用的内存限制上。如果容器消耗的内存超出了限制,它可能会被宿主机的 Docker 守护进程终止。
7.2 容器业务数据存储方式
解释:
- Docker:
- 容器: Docker 容器,运行业务应用的实例,使用橙色表示。
- Docker 卷: Docker 卷,用于持久化容器数据,确保数据在容器生命周期之外持久保存,使用绿色表示。
- 宿主机文件系统: 通过挂载可以被容器访问的宿主机文件系统,使用浅黄色表示。
- 容器文件系统: 容器内部文件系统,用于应用运行时的读写操作,通常需要配合 Docker 卷使用以实现数据持久化,使用浅黄色表示。
- 内存: 容器可以使用的内存资源,Docker 可以对容器的内存使用进行限制,使用浅黄色表示。
- 数据管理:
- 业务数据: 业务应用生成的数据,需要持久化的数据,使用绿色表示。
- 持久化数据: Docker 卷用于持久化容器产生的业务数据。
- 共享数据: 宿主机文件系统可以被多个容器挂载,实现数据的共享。
Docker 卷的优点:
- 数据持久性: 即使容器被删除,卷中的数据仍然存在。
- 数据共享: 多个容器可以同时挂载同一个卷,实现数据共享。
- 数据备份: 可以对卷进行备份和恢复操作。
7.3 数据存储案例
案例 1: 使用 Docker 卷进行数据持久化
场景: 一个多容器的 WordPress 应用,需要持久化 WordPress 数据库和文件。
Docker Compose 文件:
version: '3.8' # 指定 Docker Compose 文件使用的版本
services: # 定义服务的根节点
db: # 定义一个名为 db 的服务,用于运行 MySQL 数据库
image: mysql # 使用 MySQL 官方镜像
volumes: # 定义挂载卷
- db_data:/var/lib/mysql # 将名为 db_data 的卷挂载到容器的 MySQL 数据目录
environment: # 设置环境变量
MYSQL_ROOT_PASSWORD: example # 设置 MySQL 根用户密码
MYSQL_DATABASE: wordpress # 设置默认创建的数据库名称
MYSQL_USER: wordpress # 设置数据库用户
MYSQL_PASSWORD: wordpress # 设置数据库用户密码
wordpress: # 定义一个名为 wordpress 的服务,用于运行 WordPress 应用
image: wordpress # 使用 WordPress 官方镜像
ports: # 定义端口映射
- "8000:80" # 将容器的 80 端口映射到宿主机的 8000 端口
depends_on: # 定义服务依赖
- db # wordpress 服务依赖于 db 服务
volumes: # 定义挂载卷
- wp_data:/var/www/html # 将名为 wp_data 的卷挂载到容器的 WordPress 安装目录
volumes: # 定义命名卷
db_data: # 定义一个名为 db_data 的命名卷,用于持久化 MySQL 数据
wp_data: # 定义一个名为 wp_data 的命名卷,用于持久化 WordPress 文件
说明:
- 使用 Docker 卷
db_data
和wp_data
来持久化 MySQL 数据库和 WordPress 文件。 volumes
定义在docker-compose.yml
文件中,Docker 会自动在宿主机上创建这些卷。
案例 2: 挂载宿主机文件系统
场景: 开发环境中,开发者希望将本地代码挂载到容器中,以便进行快速迭代。
命令:
docker run -it -v $(pwd):/app code_editor
说明:
- 使用 Docker 命令行
-v
参数将当前目录 ($(pwd)
) 挂载到容器内的/app
目录。 - 这种方式使得容器内的应用程序可以使用宿主机的文件系统。
案例 3: 使用 tmpfs 进行高速缓存
场景: 一个需要快速读写缓存的 PHP 应用。
Dockerfile:
# 使用官方 PHP 镜像作为基础镜像
FROM php:7.4-apache
# 更新包索引并安装必要的扩展
RUN docker-php-ext-install mysqli gd pdo_mysql
# 安装 Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# 设置工作目录
WORKDIR /var/www/html
# 复制 Composer 配置和认证文件(如果有)
COPY composer.* ./
RUN composer install
# 复制应用代码到容器中
COPY . .
# 暴露端口
EXPOSE 80
# 启动 Apache 服务
CMD ["apache2-foreground"]
说明:
- 设置环境变量
OPCACHE_PATH
指向/tmp/opcache
。 - 使用
VOLUME
指令创建一个临时文件系统(tmpfs),用于缓存加速。
案例 4: 限制容器内存
场景: 运行一个内存密集型应用,需要限制容器的内存使用。
命令:
docker run -it -m 512m my_memory_intensive_app
说明:
- 使用 Docker 命令行
-m
参数限制容器的内存使用为 512MB。 - 这种方式有助于防止单个容器占用过多宿主机资源。
其他内容在第二篇文章《全面图解Docker架构设计:掌握Docker全链路思维与优化(小白到大师篇[3])》中。。。
- 点赞
- 收藏
- 关注作者
评论(0)