Docker
容器化打包、分发与运行的事实标准
L2 · 进阶Docker 安装指南
我们要解决如何在不同操作系统上安装 Docker,并确保安装过程顺利进行。读完这一章后,你可以轻松地在 Linux、Windows 或 macOS 上搭建 Docker 环境。
前置知识
- 计算机基础:知道如何打开终端或命令提示符。
- 权限管理:了解 root 用户和 sudo 命令的使用。
安装方式选择
Docker 提供了 stable 和 test 两个更新频道。对于生产环境,推荐使用官方 APT/YUM 源来安装 Docker,这样可以获得更好的稳定性和安全性。而在开发环境中,可以考虑使用官方提供的快捷脚本来简化安装流程。
生产环境 vs 开发环境
- 生产环境:优先使用官方 APT/YUM 源安装。
- 开发环境:可以使用官方脚本(
get.docker.com)快速安装。
详细安装指南
在 Linux 上安装 Docker
Ubuntu/Debian
- 更新包索引:
sudo apt-get update - 安装必要的依赖包:
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common - 添加 Docker 的 GPG 密钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - 添加 Docker APT 仓库:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - 再次更新包索引并安装 Docker CE(社区版):
sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
CentOS/Fedora/RHEL
- 移除旧版本的 Docker(如果有):
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine - 安装必要的依赖包:
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 - 添加 Docker YUM 仓库(阿里云镜像加速):
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo - 安装 Docker CE 和相关插件:
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
在 Windows/Mac 上安装 Docker
Docker Desktop 是适用于 Windows 和 Mac 的一站式解决方案。可以从 Docker 官网 下载并按照页面上的指示进行安装。
测试是否安装成功
运行以下命令来检查 Docker 是否正确安装并且正在运行:
docker run hello-world如果一切正常,你会看到一条欢迎信息,表示 Docker 已经成功启动并能够下载和运行容器镜像。
国内用户网络优化建议
由于国内网络原因,直接访问 Docker 官方源可能会比较慢。可以通过配置镜像加速器来提高下载速度。具体的配置方法可以在后续章节中找到详细说明。
实用技巧
- 权限问题:默认情况下,只有 root 用户才能执行 Docker 命令。为了方便使用,可以将当前用户添加到
docker组中:sudo usermod -aG docker $USER newgrp docker - 开机自启:为了让每次重启后都能自动启动 Docker 服务,可以启用它的 systemd 自启功能:
sudo systemctl enable --now docker.service
示例场景
假设你在一台新买的 Ubuntu 服务器上搭建开发环境。首先按照上述步骤更新系统软件包列表和必要依赖项,然后添加并配置好 Docker 的 APT 仓库。最后通过 apt-get install 命令完成正式版的 Docker CE 安装即可。
本章小结
- 掌握了在 Linux、Windows 和 macOS 上的不同安装方法。
- 学会了如何通过官方源或者快捷脚本进行快速部署。
- 知道了如何测试和验证 Docker 是否正确安装。
- 获得了针对国内用户的网络优化建议及实用技巧。
核心概念
Docker 镜像
理解 Docker 镜像是掌握 Docker 技术的关键一步。通过本章的学习,你会知道什么是 Docker 镜像,如何创建和管理镜像,并明白为什么要使用镜像。
在开始之前,请确保你已经完成了 Docker 的安装,并且能够顺利运行基本的 Docker 命令。
什么是 Docker 镜像?
Docker 镜像是一个轻量级、独立的、可执行的软件包,包含了运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。你可以把它看作是一个静态的模板,用来创建容器。每个镜像都是由一层或多层组成,每一层代表对前一层的更改。当你基于某个镜像创建容器时,Docker 会加载该镜像的所有层来构建文件系统。
如何查看本地镜像?
我们可以使用 docker images 命令来列出本地所有的镜像:
docker images预期结果会显示类似下面的信息:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 2 weeks ago 73.9MB
nginx latest 602e111c06b6 3 weeks ago 133MB这里的 REPOSITORY 是镜像的名字,TAG 是标签(版本号),IMAGE ID 是唯一标识符,CREATED 是创建时间,而 SIZE 则表示大小。
如何拉取远程镜像?
如果你想从 Docker Hub 或其他镜像仓库拉取一个镜像到本地,可以使用 docker pull 命令。例如,我们要拉取最新版的 Nginx 镜像:
docker pull nginx:latest这条命令会在后台下载最新的 Nginx 镜像及其所有依赖层。
如何删除本地镜像?
当不再需要某个本地镜像时,可以使用 docker rmi 命令来删除它。比如我们要删除刚才拉取的 Nginx 镜像:
docker rmi nginx:latest如果该镜像被某个容器引用,则无法直接删除。你需要先停止并移除相关的容器。
如何构建自定义镜像?
除了从公共仓库获取现成的镜像外,我们还可以根据需求构建自己的定制化镜像。通常我们会编写一个名为 Dockerfile 的文本文件,在其中描述所需的配置和指令。关于如何编写 Dockerfile 将在下一章详细介绍。
这里简单举个例子:假设我们要创建一个简单的 Python 应用程序环境。首先准备一个目录存放我们的应用代码和 Dockerfile 文件:
mkdir my-python-app && cd my-python-app
echo "print('Hello, Docker!')" > app.py
touch Dockerfile编辑 Dockerfile 内容如下:
# 使用官方 Python 运行时作为基础映射
FROM python:3.8-slim
# 设置工作目录为 /app
WORKDIR /app
# 复制当前目录下的所有文件到工作目录中
COPY . .
# 运行应用程序 app.py 当容器启动时
CMD ["python", "app.py"]然后在同一目录下运行以下命令来构建新的 Docker 镜像:
docker build -t my-python-app .这里的 -t my-python-app 参数用于指定新生成的镜像名称为 my-python-app。. 表示构建上下文路径为当前目录。
实际应用场景
想象一下你是一名开发者,在开发一个新的 Web 应用程序。为了保证项目的稳定性和一致性,在不同的开发环境中都能获得相同的结果是非常重要的。这时你可以利用 Docker 来打包你的应用以及所有依赖项成为一个单独的可移植单元——即 Docker 镜像。这样无论是在个人电脑还是云服务器上部署应用都变得十分方便快捷。
本章小结
- 理解了什么是 Docker 镜像是及其作用。
- 学习了如何查看、拉取和删除本地的 Docker 镜像。
- 知道了如何通过编写简单的
Dockerfile构建自定义化的镜像。 - 明白了为什么需要使用自定义化的 Docker 镜来进行开发和部署工作。
Docker 容器
理解了如何构建 Docker 镜像后,接下来我们要聊聊 Docker 容器本身。容器是镜像的运行实例,通过容器我们可以真正地运行我们的应用程序。读完这一章,你会知道什么是 Docker 容器,如何创建和管理它们,并且明白为什么使用容器会带来很多好处。
基础概念
首先,我们需要明确几个基本概念:
- 镜像 (Image):一个静态的、不可变的模板,包含了运行应用程序所需的所有内容。
- 容器 (Container):基于镜像创建的一个可运行的实例。它可以被启动、停止、移动甚至删除。
- Docker Engine:负责创建和管理容器的后台服务。
创建和运行容器
假设我们已经有一个名为 my-python-app 的镜像(如上一章所述),现在我们可以通过以下命令来创建并运行一个基于该镜像的容器:
docker run -d --name my-running-app my-python-app这里的关键参数解释如下:
-d:表示容器将在后台运行。--name my-running-app:为新创建的容器指定一个名称my-running-app。my-python-app:这是我们之前构建好的镜像名称。
这条命令执行后,你会看到类似这样的输出:
e6c5a4f8b312cda18a7...这就是新创建的容器 ID。如果你想确认一下这个容器是否真的在运行,可以使用以下命令:
docker ps你应该能看到类似下面的信息:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e6c5a4f8b312 my-python-app "python app.py" 2 minutes ago Up 2 minutes my-running-app这表明我们的容器已经在后台成功启动并且正在运行。
查看和管理容器
列出所有正在运行的容器
刚才提到的 docker ps 命令只会列出当前正在运行的容器。如果你想查看所有的容器(包括已停止的状态),可以加上 -a 参数:
docker ps -a停止一个正在运行的容器
如果我们想停止某个正在运行中的容器,可以使用 stop 命令配合容器 ID 或者名称:
docker stop my-running-app删除一个已停止的容器
当不再需要某个已停止状态下的容器时,可以将其删除以释放资源:
docker rm my-running-app注意:如果尝试删除一个正在运行中的容器会失败,并提示错误信息:“Error response from daemon: You cannot remove a running container”。此时你需要先停止它或者强制删除(不推荐)。
实际应用场景
设想一下你是一位软件开发人员,在开发过程中需要频繁地切换不同的项目环境。传统的做法可能是手动安装各种依赖包并在本地调试代码。这种方式不仅耗时而且容易因为环境差异导致难以复现的问题出现。
借助 Docker 容器技术,我们可以轻松地将每个项目的开发环境封装在一个独立的镜像中。每次新建或恢复开发环境只需几秒钟的时间即可完成,并且确保各个项目的环境完全一致。此外,在测试阶段也可以快速地复制相同的环境到其他机器上进行验证。
比如对于上面提到的那个 Python 应用程序项目,在不同的团队成员之间共享相同的开发环境变得非常简单高效。
本章小结
- 理解了什么是 Docker 容器及其与镜像的关系。
- 掌握了如何通过
docker run命令来启动一个新的容器实例。 - 学会了如何查看、停止以及删除 Docker 容器的基本操作方法。
- 认识到了使用 Docker 容器带来的便利性和一致性保障的优势所在。
Dockerfile 编写
编写 Dockerfile 是创建 Docker 镜像的关键步骤,通过它可以定义应用所需的环境和配置。读完本章后,你不仅能理解 Dockerfile 的基本结构和指令,还能动手编写一个简单的 Dockerfile 并构建镜像。
准备工作
确保你已经按照前几章的指导安装好了 Docker,并且对镜像和容器的基本操作有所了解。
Dockerfile 基础概念
Dockerfile 是一个文本文件,包含了用于构建镜像的一系列指令。每个指令都对应一个层(layer),这些层会被缓存以便加快后续构建速度。常用的指令包括 FROM, WORKDIR, COPY, RUN, EXPOSE 和 CMD。
关键指令详解
FROM 指定基础镜像。所有 Dockerfile 必须以
FROM开始。FROM python:3.13这行代码表示基于 Python 3.13 版本的基础镜像来构建我们的应用镜像。
WORKDIR 设置工作目录。以后的操作都会在这个目录下执行。
Docker 卷
在容器的世界里,数据的持久化是一个常见的需求。如果我们希望容器中的数据能够在容器重启甚至删除后仍然存在,就需要使用 Docker 卷。通过本章的学习,你不仅会理解什么是 Docker 卷,还会掌握如何创建和管理它们。
准备工作
确保你已经按照前几章的指导安装好了 Docker,并且熟悉镜像和容器的基本操作。
Docker 卷概述
Docker 卷是专门设计用来持久化存储数据的独立文件系统区域。卷与容器的联合文件系统不同,它的生命周期可以独立于容器本身。这意味着即使容器被删除或重新创建,卷中的数据也不会丢失。
创建和管理卷
1. 创建卷
我们可以使用 docker volume create 命令来创建一个新的卷。
docker volume create mydata这条命令会在你的 Docker 主机上创建一个名为 mydata 的新卷。你可以通过 docker volume ls 来查看当前所有的卷。
2. 查看卷信息
如果你想了解更多关于某个特定卷的信息,可以使用 docker volume inspect 命令。
docker volume inspect mydata这个命令会返回一个 JSON 格式的详细信息,包括卷的名称、驱动类型以及挂载路径等。
3. 删除卷
如果不再需要某个卷了,可以使用 docker volume rm 命令将其删除。
docker volume rm mydata请注意,在删除之前确保没有正在使用的容器依赖该卷,否则可能会导致错误。
将卷挂载到容器
1. 启动容器并挂载卷
当你启动一个新容器时,可以通过 -v 或 --mount 参数将已有的卷挂载到指定位置。
docker run -d --name mycontainer -v mydata:/app nginx:latest这里我们在后台运行了一个 Nginx 容器,并将名为 mydata 的卷挂载到了 /app 目录下。这样任何写入 /app 目录的数据都会被保存到这个卷中。
2. 使用匿名卷(自动创建)
如果你不想显式地命名和管理每个卷,也可以让 Docker 自动为你创建一个匿名卷:
docker run -d --name anothercontainer -v /app nginx:latest在这种情况下,Docker 会为 /app 目录自动生成一个唯一的匿名卷。虽然方便快捷,但是匿名卷不容易管理和跟踪。
数据卷容器
有时候我们需要在一个或多个容器之间共享某些数据。这时可以考虑使用“数据卷容器”。首先你需要创建一个只包含数据而没有任何进程运行的专用容器作为数据载体:
docker create -v /dbdata --name dbstore ubuntu:latest /bin/true然后其他需要访问这些共享数据的容器就可以通过 --volumes-from 参数引用这个数据源:
docker run -d --name db1 --volumes-from dbstore mysql:tag ...
docker run -d --name db2 --volumes-from dbstore postgres:tag ...这样无论哪个数据库实例停止或者重启都不会影响共享的数据集 dbstore 中的内容。
实际案例分析
假设我们有一个开发团队正在使用 MySQL 数据库进行项目开发。为了保证每次本地测试环境的一致性并且不丢失已有数据,我们可以这样做:
- 创建一个 MySQL 数据库镜像。
- 创建一个用于存放数据库文件的数据卷。
- 启动 MySQL 容器并将上述数据卷挂载进去。
- 当需要升级数据库版本或其他调整时只需替换掉旧的 MySQL 镜像即可而不必担心丢失原有数据记录。
具体操作如下:
# 创建MySQL数据存储用的数据体积(volume)
docker volume create mysql-data
# 运行MySQL服务并将mysql-data绑定到MySQL默认的工作目录/var/lib/mysql/
docker run --name dev-mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
-p 3306:3306 \
-v mysql-data:/var/lib/mysql \
-d mysql:8.0
# 此时MySQL的所有数据库文件都将存储在主机上的mysql-data对应的物理目录下,
# 即使dev-mysql被删除重建依然能够恢复之前的全部表结构及用户设置等内容。本章小结
- 我们学会了如何创建、查看和删除 Docker 卷。
- 掌握了如何在启动容器时将现有或新建的 Docker 卷挂载到指定目录的方法。
- 理解了利用“数据卷容器”实现跨多个相关联业务组件间共享持久化资源的概念及其应用场景。
Docker 网络
理解容器之间的通信对于构建复杂的分布式应用至关重要。本章我们将探讨 Docker 提供的各种网络模式,学会如何配置和管理这些网络,以便容器可以相互通信或者与外部服务交互。
前置知识
在开始之前,请确保你已经安装了 Docker 并熟悉基本的镜像和容器操作。如果还没有接触过 Docker 的网络功能,也不用担心,我们会从基础概念逐步深入。
Docker 网络概述
Docker 容器可以通过多种方式与其他容器或非 Docker 服务进行通信。每个容器看到的是一个带有 IP 地址、网关、路由表等标准网络接口的信息。默认情况下,当你运行一个没有指定 --network 参数的容器时,它会被连接到名为 bridge 的默认网络中。在这个默认的桥接网络中,所有容器之间可以自由地通过 IP 地址互相访问,并且也可以访问外部网络服务。
默认桥接网络
当我们运行下面这样的命令来启动一个 Nginx 容器时:
docker run -d --name webserver nginx如果不特别指定 --network 参数,Docker 就会把这个新创建的容器加入到默认的 bridge 网络中。你可以通过以下命令查看这个容器在网络中的相关信息:
docker inspect webserver在返回的结果中找到 "NetworkSettings" 字段下的 "Networks" 子字段,可以看到类似如下的信息:
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "<some-id>",
"EndpointID": "<some-endpoint-id>",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
...
}
}这意味着你的 Nginx 容器现在有了一个内部 IP 地址 172.17.0.2 可以用来和其他在同一 bridge 网络中的其他容器进行通信。
自定义桥接网络
虽然默认桥接网络很方便,但在实际开发过程中我们可能希望对不同组的服务分别隔离管理。这时就可以创建自定义桥接网络来满足需求。
首先创建一个新的自定义桥接网络叫做 my-net:
docker network create my-net然后我们可以启动新的容器并将其加入到这个自定义的 my-net 网络中去:
docker run -d --name db-server --network my-net postgres:latest
docker run -d --name app-server --network my-net nodejs-app:latest这样做的好处是只有属于同一自定义桥接网络内的容器才能互相发现对方并通过名称解析的方式进行通信。例如,在上面的例子中,app-server 可以通过简单的域名查询直接访问到 db-server 而无需知道具体的 IP 地址。
其他类型的 Docker 网络
除了前面提到的默认桥接和自定义桥接两种模式之外,Docker 还支持另外两种主要的网络类型:主机(Host)模式和无(None)模式。
主机(Host)模式
当使用主机模式时,Docker 容器不会获得自己独立的 Network Namespace 而是与宿主机共享同一个 Network Namespace。这意味着该容器将拥有与宿主机相同的 IP 地址以及所有的端口映射关系。
要启用主机模式只需添加 -–net=host 参数即可:
docker run -d --name web-host-mode --net=host nginx需要注意的是,在这种模式下由于不存在端口冲突的问题所以不需要再额外指定 -p 来发布端口映射规则了。
无(None)模式
最后一种情况则是完全禁用了任何网络连接的情况。在这种情况下即使是在同一个服务器上运行的不同容器也无法相互之间建立任何形式的通讯联系。
启用无模式也很简单只需要加上 -–net=none 参数就可以了:
docker run -d --name isolated-container --net=none busybox sleep 3600 执行上述命令后你会发现该 BusyBox 容器没有任何可用的网络接口设备了(除了本地回环接口 lo 以外)。
如何连接已有容器到特定网络?
有时候我们需要将已经存在的某个正在运行着的服务附加到一个新的用户自定义网路上去。这时候就需要用到 docker network connect <NETWORK> <CONTAINER> 命令了。
假设我们现在有一个名为 web-service-old 的旧版 Web 应用已经在默认桥接网上跑起来了,并且想要把它迁移到刚才创建的那个名为 my-net 的新环境中去的话可以这样做:
docker network connect my-net web-service-old 完成之后再次检查一下它的详细信息确认是否成功切换到了目标网路上去了:
docker inspect web-service-old 在输出结果里面你应该能看到新增加的一个 "my-net" 对应的部分显示出了相关的 IP 和子网掩码信息表明迁移过程顺利完成了!
实战演练:搭建微服务架构
为了加深对 Docker 网络的理解让我们动手模拟一个小型微服务系统的部署流程吧!这里我们将基于 Node.js 构建两个简单的 RESTful API 服务——一个是负责处理订单数据 (order-api) ,另一个则用于库存管理 (inventory-api) 。这两个微服务都需要能够彼此交流从而协同工作共同完成订单处理任务。
步骤如下所示:
创建一个新的专用网路叫作
service-network.docker network create service-network准备好各自的 Node.js 应用源码并打包成对应的镜像文件。
(此处省略具体编写代码的过程)
启动第一个订单管理系统 API 服务器实例,并将其加入刚刚创建的服务网路之中。
docker build -t order-api ./path/to/order-codebase docker run -d --name order-instance \ --network service-network \ order-api:latest接下来同样按照同样的方法依次启动第二个库存管理 API 服务器进程。
docker build -t inventory-api ./path/to/inventory-codebase docker run -d --name inventory-instance \ --network service-network \ inventory-api:latest最后验证两个微服务间的连通性是否正常运作无误。
如果一切顺利的话那么此时位于相同虚拟局域网下的两个节点应该可以通过互调 HTTP 请求来进行正常的业务交互啦~
易错点提示
- 忘记指定自定义桥梁名称:如果你试图连接一个不存在的目标网路就会收到类似于“Error response from daemon: No such network”的错误提示。
- 权限不足导致无法修改现有配置:某些时候管理员级别的权限可能是必需的操作前提条件之一。
- IP 冲突引起绑定失败:尝试给即将加入已满员子网的新成员指派已经被占用过的静态地址会导致类似的告警消息抛出:“Error response from daemon: failed to allocate gateway (192.x.x.x): request failed: address already allocated”。
总之熟练掌握以上知识点不仅有助于提高日常开发效率还能有效避免因不当配置引发的一系列潜在风险隐患哦!
本章小结
- 我们学习了什么是 Docker 容器网络以及它们是如何工作的。
- 掌握了几种常见的 Docker 网络驱动程序包括但不限于 bridge、host 和 none 模式的区别及其适用场景。
- 学会了如何手动创建、管理和删除用户自定义桥接型虚拟局域网对象,并演示了一种典型的多层微服务体系结构的设计思路案例分析过程。
进阶能力
使用 Docker Compose
通过前面的学习,我们已经掌握了 Docker 镜像、容器、Dockerfile 以及网络的基本概念和操作方法。本章我们将进一步探讨如何使用 Docker Compose 来简化多容器应用的管理和部署过程。
为了顺利进行本章的学习,你需要确保已经安装了 Docker 并且熟悉基本的 Docker 操作。如果你还没有安装 Docker,请先完成第 1 章中的步骤。
什么是 Docker Compose?
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件,你可以配置应用程序的服务、网络和卷,然后只需一条命令就可以启动所有服务。
如何安装 Docker Compose?
方法一:通过 Docker Desktop 安装(推荐)
对于大多数开发者来说,最简单的方式就是安装 Docker Desktop。它包含了 Docker Engine、Docker CLI 和 Docker Compose。
- 访问 Docker 官方网站,下载适用于你的操作系统的版本。
- 按照安装向导完成安装。
- 安装完成后,在系统托盘中点击 Docker 图标,选择 About Docker Desktop 查看当前版本信息。
方法二:通过命令行安装(仅限 Linux)
如果你已经在 Linux 上安装了 Docker Engine 和 CLI,可以通过以下命令安装最新版的 Docker Compose 插件:
sudo apt-get update
sudo apt-get install docker-compose-plugin或者手动下载特定版本的二进制文件:
curl -SL https://github.com/docker/compose/releases/download/v2.14.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose验证安装是否成功:
docker compose --version如果显示版本号,则说明安装成功。
使用 Docker Compose 构建一个简单的 Web 应用
接下来我们将通过一个实际的例子来理解如何使用 Docker Compose 来构建和运行一个多容器应用。假设我们要搭建一个基于 Flask 的 Web 应用,并使用 Redis 作为缓存服务器。
步骤 1:创建项目目录
首先创建一个新的项目目录,并进入该目录:
mkdir my-flask-app && cd my-flask-app步骤 2:编写 Flask 应用
在项目根目录下创建 app.py 文件,并添加以下内容:
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis server using environment variables defined in the docker-compose.yml file.
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)步骤 3:准备依赖项
在同一目录下创建 requirements.txt 文件,并添加所需的 Python 包列表:
Flask==2.0.1
redis==4.0.2步骤 4:编写 docker-compose.yml 文件
在项目根目录下创建 docker-compose.yml 文件,并添加如下内容:
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
environment:
NAME: World
redis:
image: "redis:alpine"解释一下这个配置文件的主要部分:
version: 指定使用的 Compose 文件格式版本。services: 定义各个服务及其配置。build: 当前目录下的Dockerfile将会被用来构建镜像。ports: 映射主机端口到容器端口。volumes: 绑定主机上的目录到容器内的路径。environment: 设置环境变量供应用使用。image: 使用预构建好的公共镜像作为服务的基础镜像。
由于我们在上面没有指定具体的构建指令(例如 CMD 或 ENTRYPOINT),所以需要在项目的同一目录下创建一个名为 Dockerfile 的文件来指示如何构建我们的 Web 应用镜像:
FROM python:3.9-slim-buster
WORKDIR /code
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "./app.py"]上述内容描述了如何从基础 Python 镜像开始,复制代码文件并安装必要的依赖包最后运行我们的 Flask 应用程序。
步骤 5:启动应用栈
回到包含 docker-compose.yml 的项目根目录,执行以下命令即可一次性启动所有服务(Web 和 Redis):
docker compose up -d 参数 -d 表示后台模式运行。如果没有错误发生的话你应该能看到类似下面的日志输出提示相关服务正在启动中……
检查正在运行中的容器状态:
docker ps 你会看到有两个新的容器分别对应刚才定义的服务名称(web 和 redis)。
打开浏览器访问 http://localhost:5000 ,你应该能看到类似这样的页面展示:"Hello World!" 及其他相关信息表明一切正常运作起来了!
如果你想停止整个应用栈可以输入:
docker compose down 这条指令会自动清理掉之前启动的所有资源包括容器本身及关联的数据卷等残留物从而恢复到初始状态便于重新开始测试调试过程...
常见问题与排查技巧
如果你在启动过程中遇到权限问题,请确保以 root 用户身份执行命令或赋予相应的权限。
若发现某个服务未按预期启动,请查看详细日志以便定位根本原因:
docker logs <container_name>替换
<container_name>为你想要查询的具体服务对应的容器 ID 或者名称标识符...对于复杂的生产级应用场景建议采用更加健壮的方式来组织配置文件比如拆分成多个
.yml片段并通过-f参数引入组合利用继承特性达到模块化的效果提升维护便利性...
实战案例解析
想象一下你现在正参与开发一款在线商城平台后端微服务架构设计...其中涉及到商品搜索功能模块希望借助 Elasticsearch 技术实现高性能全文检索能力同时配合 Kibana 提供可视化数据分析界面方便运营人员监控跟踪流量变化趋势...这时就可以利用 Docker Compose
Docker 镜像仓库
理解了 Docker 的基本操作之后,我们来探讨如何管理和分享 Docker 镜像。通过 Docker 镜像仓库,我们可以方便地存储、检索和共享镜像,这对于团队协作和持续集成非常有用。
前置知识
你需要已经安装好 Docker,并且熟悉如何创建和管理 Docker 镜像。
什么是 Docker 镜像仓库?
Docker 镜像仓库是一个集中存放 Docker 镜像的地方,允许用户上传、下载以及管理镜像。最著名的公共镜像仓库是 Docker Hub,它是世界上最大的容器镜像存储库之一。
使用 Docker Hub
Docker Hub 提供了一系列由社区开发者、开源项目和独立软件供应商提供的镜像。你可以轻松找到并使用这些镜像。
注册账号 访问 Docker Hub 并注册一个账号。
搜索镜像 在首页的搜索栏中输入关键词,例如
nginx,然后点击搜索按钮。拉取镜像 找到合适的镜像后,可以使用
docker pull命令将其下载到本地。例如:docker pull nginx:latest推送镜像 如果你想将自己的镜像推送到 Docker Hub 上,首先需要对镜像进行标签化,并指定用户名:
docker tag my-image username/my-image:tag然后登录到你的 Docker Hub 账号:
docker login -u username -p password最后推送镜像:
docker push username/my-image:tag
国内加速源
由于网络原因,在国内访问 Docker Hub 可能会比较慢。为了提高速度,可以选择一些国内的加速源。
| 名称 | 地址 |
|---|---|
| DaoCloud | https://docker.m.daocloud.io |
| 简行 | https://docker.jiaxin.site |
| HLMirror | https://mirror.houlang.cloud |
配置方法是在 /etc/docker/daemon.json 文件中添加如下内容:
{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}修改完成后重启 Docker 服务以使配置生效:
sudo systemctl restart docker.service私有仓库搭建
除了使用公共仓库外,你还可以自己搭建私有的 Docker 镜像仓库。
拉取 Registry 镜像
docker pull registry:latest运行 Registry 容器
docker run -d --name my-registry -p 5000:5000 --restart always \ -v /data/docker/registry:/var/lib/registry registry:latest配置客户端信任私有仓库
默认情况下,Docker 不信任非 HTTPS 的私有仓库。因此需要在
/etc/docker/daemon.json中添加以下配置:{ "insecure-registries": ["localhost:5000"] }标记并推送镜像
将本地镜像标记为适合私有仓库格式,并推送上去:
docker tag my-image localhost:5000/my-image:tag docker push localhost:5000/my-image:tag
实战案例解析
假设你有一个小型开发团队正在构建一个博客系统,并且希望将所有的开发环境都容器化。你可以选择使用 DaoCloud 的加速源来加快基础镜像是拉取速度,并且搭建一个私有的 Registry 来保存你们团队内部使用的自定义镜像。
配置加速源
拉取基础镜像
编写自定义镜像
推送至私有仓库
这样做的好处是可以减少对外部网络的依赖,提高工作效率的同时也保护了敏感数据的安全性。
本章小结
- 学习了什么是 Docker 镜像仓库及其重要性。
- 掌握了如何在国内外不同的环境中高效地查找、拉取和推送镜像。
- 理解了如何搭建和维护自己的私有 Registry。
- 注意到了一些常见的错误配置及其解决办法。
Docker Swarm 集群
通过上一章的学习,我们已经掌握了如何管理和使用 Docker 镜像仓库。本章我们将深入探讨 Docker Swarm 集群,了解它是如何帮助我们在生产环境中管理多个 Docker 主机的。
前置知识/环境
确保你已经在所有主机上安装了最新版本的 Docker,并且可以互相通信。如果你还没有安装 Docker,请参考第一章进行安装。
什么是 Docker Swarm?
Docker Swarm 是一种用于管理 Docker 引擎集群的技术。它允许你将一组物理或虚拟的机器变成一个单一的虚拟系统,在这个系统之上可以轻松地部署和管理应用服务。Swarm 模式内置在 Docker Engine 中,无需额外安装即可使用。
关键概念
- 节点 (Node): 参与 Swarm 的每个机器称为一个节点。
- 管理节点 (Manager Node): 负责维护集群的状态以及处理用户请求。
- 工作节点 (Worker Node): 执行具体的任务和服务。
- 服务 (Service): 用户定义的任务模板,Swarm 根据这个模板自动创建并调度任务。
如何创建 Swarm 集群?
首先,我们需要初始化一个 Swarm 集群。在这个过程中,当前的机器会被设置为主管理节点。
docker swarm init --advertise-addr <MANAGER_IP><MANAGER_IP> 替换为你管理节点的实际 IP 地址。例如:
docker swarm init --advertise-addr 192.168.99.131成功后,你会看到类似这样的输出:
Swarm initialized: current node (<NODE_ID>) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-0dm85ix64fa5j62hricif97kds21jj4aharkoqfuzfw22n10mc-9tlq05a1xafxnqtujj94t288k 192.168.99.131:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.根据提示,我们可以让其他机器加入到我们的 Swarm 集群中作为工作节点。在另一台机器上运行上面提供的 docker swarm join 命令:
docker swarm join --token SWMTKN-1-0dm85ix64fa5j62hricif97kds21jj4aharkoqfuzfw22n10mc-9tlq05a1xafxnqtujj94t288k 192.168.99.131:2377创建和管理服务
一旦 Swarm 集群建立起来,就可以在其上创建和管理服务了。下面是一个简单的例子:创建一个名为 myhello 的服务,并将其暴露在端口 8000 上。
docker service create --replicas 3 --name myhello -p 8000:80 kentalk/helloworld这条命令会在集群中启动三个 kentalk/helloworld 容器副本,并将它们映射到宿主机的 8000 端口。
查看服务状态:
docker service ls预期输出类似于:
ID NAME MODE REPLICAS IMAGE PORTS
wvnrzrtrmohg myhello replicated 3/3 kentalk/helloworld *:80->80/tcp检查单个服务的具体情况:
docker service ps myhello输出可能如下所示:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
iwwbtklzoicu myhello.1 kentalk/helloworld swarm-worker-02 Running Running about a minute ago
vmbdppcgyeok myhello.2 kentalk/helloworld swarm-manager-01 Running Running about a minute ago
yjljqdcdzzne myhello.3 kentalk/helloworld swarm-worker-01 Running Running about a minute ago 扩容和缩容服务
可以通过修改副本数量来调整服务规模。比如将 myhello 服务扩展到五个实例:
docker service scale myhello=5同样地,也可以缩小规模到两个实例:
docker service scale myhello=2每次操作后都可以通过 docker service ps myhello 查看最新的状态变化。
删除服务
不再需要的服务可以通过以下命令删除:
docker service rm myhello确认已删除的服务列表为空或不再显示该服务名称。
实战案例解析
假设你的团队正在开发一个新的电子商务平台,并希望利用 Docker 和 Swarm 提供高可用性和负载均衡的能力。以下是实现步骤概述:
初始化 Swarm 并添加节点
在一个主服务器上初始化 Swarm 并获取加入令牌,在其他几台服务器上运行此令牌加入 Swarm 成员身份。
创建数据库服务
使用 MySQL 或 PostgreSQL 等数据库镜像来创建持久化的存储解决方案,并确保它可以被多个应用实例访问。
构建并部署应用服务
基于项目需求编写 Dockerfile 文件构建自定义镜像,并将其推送到私有镜像仓库中以便于后续部署操作。然后通过 docker service create 命令按照所需的副本数启动相应数量的应用容器实例,并绑定必要的网络端口以接收外部流量。
设置负载均衡策略
合理分配资源使得前端 Web 应用能够均匀地分担来自用户的请求压力,从而提升整体系统的响应性能及稳定性。
监控与日志收集
集成监控工具如 Prometheus、Grafana 来跟踪关键指标的变化趋势;同时配置集中式的日志管理系统如 ELK Stack 来方便调试排查问题所在之处。
易错点与排查建议
常见错误之一: 如果你在非 Manager 节点尝试执行管理员级别的命令(如创建、更新或删除服务),会收到权限不足的错误信息。“Error response from daemon: This node is not a swarm manager.” 解决方法是在 Manager 节点执行相关指令或者授予当前用户适当的权限角色变更成为 Manager 角色后再试一次。“docker node promote <NODE_NAME>”。
实用技巧: 在实际生产环境中为了保证业务连续性推荐至少准备三个以上的 Manager 节点组成冗余架构防止个别故障影响整个集群正常运作能力;另外还可以采用滚动升级的方式逐步替换旧版软件包而不至于造成大规模停机现象发生。
本章小结
- 学习了什么是 Docker Swarm 及其核心组件。
- 掌握了如何初始化、加入和退出 Swarm 集群的基本操作流程。
- 理解了如何在 Swarm 上创建、管理和扩展微服务体系结构中的各个独立组件。
- 注意到了一些常见的错误配置及其解决办法,并掌握了一些实用技巧来增强系统的健壮性和可靠性。
部署运维
Docker 安全实践
在实际应用中,确保 Docker 环境的安全至关重要。本章将探讨如何加强 Docker 的安全性,保护你的应用程序和数据免受潜在威胁。通过学习本章内容,你能够更好地理解和实施 Docker 的安全措施。
前置知识
你需要已经安装好 Docker 并熟悉基本的操作,比如启动容器、构建镜像等。
关键概念及操作
1. 使用非特权用户运行 Docker Daemon
默认情况下,Docker 守护进程是以 root 用户身份运行的,这可能会带来安全隐患。可以通过配置 systemd 服务文件来指定一个非特权用户来运行 Docker 守护进程。
sudo usermod -aG docker your-user然后重新登录以使更改生效。注意:这种方法并不是完全安全的替代方案,因为它仍然需要 root 权限来管理 Docker 服务。
2. 启用 TLS 加密通信
为了保护 Docker API 和守护进程之间的通信安全,应该启用 TLS 加密。
首先生成必要的证书:
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=your-docker-host" -sha256 -new -key server-key.pem -out server.csr
echo subjectAltName = IP:YOUR_DOCKER_HOST_IP >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 \
-in server.csr \
-CA ca.pem \
-CAkey ca-key.pem \
-CAcreateserial \
-out server-cert.pem \
-extfile extfile.cnf
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile-client.cnf
openssl x509 -req \
-days 365 \
-sha256 \
-in client.csr \
-CA ca.pem \
-CAkey ca-key.pem \
-CAcreateserial \
-out cert.pem \
-extfile extfile-client.cnf
chmod 0400 ca-key.pem key.pem server-key.pem
chmod 0444 ca.pem server-cert.pem cert.pem接着编辑 /etc/docker/daemon.json 文件:
{
"tls": true,
"tlscacert": "/path/to/ca.pem",
"tlscert": "/path/to/server-cert.pem",
"tlskey": "/path/to/server-key.pem"
}重启 Docker 守护进程后,使用以下命令连接到守护进程:
docker --tlsverify --tlscacert=/path/to/ca.pem --tlscert=/path/to/cert.pem --tlskey=/path/to/key.pem https://your-docker-host:2376 ps3. 镜像安全管理
扫描镜像漏洞
使用 Clair 或 Trivy 等工具定期扫描镜像中的已知漏洞。这里以 Trivy 示例:
trivy image nginx:latest Trivy 将列出所有检测到的漏洞及其严重程度。
使用可信的基础镜像
尽量选择官方提供的基础镜像,并关注其更新情况。避免使用不受信任的第三方镜像仓库中的镜像。
4. 运行时安全策略
使用 SELinux/AppArmor
开启 SELinux 或 AppArmor 可以进一步限制容器的行为范围,减少潜在的安全风险。可以在启动容器时添加 --security-opt 参数来启用它们。
docker run --security-opt label=user:USERLABEL my_image 设置资源限制
合理设置 CPU、内存等资源限制可以有效防止单个容器占用过多资源导致其他容器无法正常工作的问题。
docker run --cpus="1.5" --memory="1g" my_image 实战案例:部署一个安全的 Nginx 应用
假设我们要部署一个简单的 Nginx 应用,并希望确保其安全性。以下是步骤:
创建一个自定义的 Nginx 镜像,在其中移除不必要的软件包并禁用不必要的服务。
FROM nginx:alpine RUN apk del vim && rm /var/cache/apk/* COPY ./nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]构建并推送该镜像至私有仓库。
docker build . --tag myregistry.local/my_nginx:v1 docker push myregistry.local/my_nginx:v1使用非特权用户启动 Docker 守护进程。
启动容器时启用 TLS 加密以及 SELinux/AppArmor 政策。
docker run --name secure-nginx-container \ --security-opt apparmor=docker-default \ --network host \ myregistry.local/my_nginx:v1
常见错误与排查建议
常见错误之一: 如果未正确配置 TLS 密钥和证书,则会导致无法通过远程 API 访问 Docker 守护进程。“Error response from daemon: Get https://<host>:2376/v1.40/info: x509: certificate signed by unknown authority”。检查证书路径是否正确并且证书是否由正确的 CA 签署。
实用技巧: 对于敏感信息,请避免将其硬编码在脚本或配置文件中。可以考虑使用环境变量或者外部密钥存储服务(如 HashiCorp Vault)来动态加载这些信息。
本章小结
- 学习了如何配置 Docker 守护进程以提高安全性。
- 掌握了如何通过 TLS 加密保护 Docker API 和守护进程间的通信。
- 理解了镜像扫描的重要性以及如何选择可信的基础镜像。
- 掌握了如何在运行时设置各种安全策略以防止潜在的风险。
Docker 性能优化
提升容器运行效率是确保 Docker 应用程序在生产环境中稳定运行的关键。通过优化资源使用、精简镜像体积以及合理配置容器,我们可以显著提高应用的整体性能。本章将详细介绍如何对 Docker 进行性能优化,让你能够更加高效地管理容器。
前置知识/环境
在阅读本章之前,你需要熟悉 Docker 的基本概念,包括镜像、容器、Dockerfile 和 Docker Compose。此外,确保你的 Docker 环境已经正确安装并配置好。
1. 资源限制
资源限制是优化容器性能的重要手段之一。通过限制每个容器的 CPU 和内存使用,可以有效防止某个容器过度消耗资源影响其他容器的正常运行。
设置 CPU 限制
你可以使用 --cpus 参数来限制容器使用的 CPU 核心数。例如:
docker run -d --name my-container --cpus 2 my-image这条命令会启动一个名为 my-container 的容器,并将其 CPU 使用限制为最多两个核心。
设置内存限制
同样地,你可以使用 --memory 参数来限制容器的最大内存使用量。例如:
docker run -d --name my-container --memory 512m my-image这条命令会将 my-container 的最大内存使用量限制为 512MB。
2. 镜像优化
精简镜像是提高 Docker 性能的有效方法之一。较小的镜像不仅下载速度更快,而且占用的磁盘空间也更少。
使用轻量级基础镜像
选择合适的轻量级基础镜像可以大大减小最终生成的镜像大小。例如,Alpine Linux 是一个非常流行的轻量级发行版:
FROM alpine:latest
RUN apk --no-cache add python3
COPY app.py .
CMD ["python3", "app.py"]在这个例子中,我们选择了 Alpine Linux 作为基础镜像,并仅安装了必要的 Python 解释器。
减少不必要的层
尽量减少 Dockerfile 中的层数可以加快构建速度并减小最终镜像的大小。例如:
Docker 监控
监控 Docker 容器可以帮助我们了解应用的性能、健康状况和资源使用情况,确保系统高效、安全和可靠。在这章中,我们将学习如何使用 Docker 内置命令以及一些常用的第三方工具来进行容器监控。
前置知识
你需要已经安装好 Docker 并熟悉基本的 Docker 命令,比如 docker run 和 docker ps。
使用 Docker 内置命令进行监控
查看容器状态
最简单的方法是使用 docker stats 命令来查看所有正在运行的容器的状态。这个命令会显示每个容器的 CPU 使用率、内存使用量、网络 I/O 和块 I/O 等信息。
docker stats预期结果是一个不断更新的表格,展示各个容器的各项指标。
查看日志
如果你想查看某个特定容器的日志,可以使用 docker logs 命令。这对于调试非常有用。
docker logs <container_id>例如:
docker logs my-container查看事件
Docker 也可以用来查看系统的事件信息,这对于理解发生了什么事情很有帮助。
docker events --since '1h'这条命令会列出过去一小时内发生的 Docker 事件。
第三方监控工具
虽然 Docker 自带了一些基本的监控功能,但对于复杂的生产环境来说,通常还需要借助第三方工具来实现更详细的监控。下面介绍几个常用的工具。
Prometheus + Grafana
Prometheus 是一个开源的监控系统和时间序列数据库,而 Grafana 则是一个强大的可视化工具。这两者的组合可以提供非常全面的监控解决方案。
安装 Prometheus 和 Grafana
首先创建一个 docker-compose.yml 文件:
version: '3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus-server
volumes:
- ./prometheus/:/etc/prometheus/
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- "9090:9090"
grafana:
image: grafana/grafana-enterprise:latest
container_name: grafana-server
ports:
- "3000:3000"
depends_on:
- prometheus然后启动服务:
docker-compose up -d打开浏览器访问 http://localhost:3000 即可进入 Grafana 的界面,默认用户名密码都是 admin。登录后添加 Prometheus 数据源,并导入预设的仪表板即可开始监控 Docker 容器的各项指标。
Beszel
Beszel 是另一个轻量级的服务器监控平台,特别适用于小型项目或个人用户。它包含了丰富的 Docker 统计信息,并且易于配置和使用。
安装 Beszel
同样创建一个 docker-compose.yml 文件:
version: '3'
services:
beszel:
image: henrygd/beszel
container_name: beszel_hub
restart: unless-stopped
ports:
- "8090:8090"
volumes:
- ./beszel_data:/beszel_data
beszel-agent:
image: henrygd/beszel-agent
container_name: beszel_agent
restart: unless-stopped
network_mode: host # 注意这里使用了host模式以便直接访问主机上的Docker socket和其他资源。
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # 挂载Docker socket以获取容器数据。然后启动服务:
docker-compose up -d打开浏览器访问 http://localhost:8090 即可进入 Beszel 的界面,默认情况下不需要额外配置就可以看到主机及其上所有容器的基本统计信息。
实战案例:搭建一个简单的 Web 应用并进行监控
假设我们要部署一个简单的 Flask 应用,并对其进行监控。首先编写一个简单的 Flask 应用:
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello_world():
return jsonify(message="Hello, World!")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)接着编写对应的 Dockerfile 来构建镜像:
# Dockerfile
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]同时准备一个 requirements.txt 文件:
Flask==2.1.2
gunicorn==21.2.0 最后编写一个 docker-compose.yml 文件来启动我们的应用和服务:
version: '3'
services:
web-app:
build: .
container_name: simple-flask-web-app
ports:
- "55555:5000"
prometheus-server:
image: prom/prometheus:v2.41.1
container_name: prometheus_server
volumes:
- ./prometheus/:/etc/prometheus/
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- "9999:9090"
grafana-server:
image: grafana/grafana-enterprise:v9.4.7
container_name: grafana_server
ports:
- "33333:3007"
depends_on:
- prometheus-server
networks:
default:
volumes:
data_prom:
data_grafana:
data_bezelhub:
data_bezelagent:
# 注意这里的端口映射是为了避免与前面的例子冲突,请根据实际情况调整。执行以下命令构建并启动服务:
sudo docker-compose up --build
# 或者后台运行:
sudo docker-compose up --build -d 现在可以通过浏览器访问 http://localhost:**55555** 查看你的 Flask 应用了。同时 Prometheus 和 Grafana 已经被正确配置并连接到了该应用的数据源,可以在 Grafana 上创建自定义面板来实时观察各项性能指标的变化趋势了!
易错提示与实用技巧
- 权限问题:确保你的用户有权限访问
/var/run/docker.sock文件。 - 配置错误:检查 Prometheus 的配置文件是否正确设置了抓取目标。
- 端口冲突:在本地测试时注意不要与其他服务占用相同的端口导致冲突。
- 日志清理:定期清理不必要的日志文件以节省磁盘空间。
- 报警规则:合理设置报警阈值以避免误报或漏报的情况发生。
本章小结
- 学习了如何使用 Docker 内置命令如
docker stats,docker logs, 和docker events进行基本的容器监控。 - 探索了两种主流的第三方监控方案:Prometheus + Grafana 和 Beszel。
- 手动搭建了一个简单的 Flask 应用并通过以上提到的方式进行了全方位的性能监测与数据分析。
实战
Docker 在 CI/CD 中的应用
通过前几章的学习,我们已经掌握了 Docker 基础知识,包括镜像的构建、容器的运行以及如何使用 Docker Compose 来管理多个容器。接下来我们要探讨的是如何将 Docker 融入到持续集成和持续部署(CI/CD)的工作流中,从而提高开发效率和系统的稳定性。
为了更好地理解本章内容,你需要对 Git 版本控制有一定的了解,并且熟悉 Jenkins 或其他 CI 工具的基本操作。
CI/CD 流水线概述
CI/CD 是一种软件开发生命周期中的实践方法论,旨在自动化代码集成、测试和部署过程。一个典型的 CI/CD 流程包括以下几个步骤:
- 代码提交:开发者将代码推送到版本控制系统(如 Git)。
- 构建:自动编译代码并生成可执行文件或 Docker 镜像。
- 单元测试:运行自动化测试以验证代码的质量。
- 集成测试:确保各个模块之间的协作正常。
- 部署:将经过测试的代码或镜像部署到生产环境。
将 Docker 引入 CI/CD 流水线
1. 镜像构建
首先,在项目根目录下编写一个 Dockerfile 来定义应用程序的运行环境。例如,假设我们有一个简单的 Go Web 应用:
# 使用官方的基础镜像
FROM golang:1.7
# 设置工作目录
WORKDIR /app
# 复制当前目录下的所有文件到工作目录
COPY . .
# 构建应用
RUN go build -o main .
# 暴露应用端口
EXPOSE 8080
# 启动应用
CMD ["./main"]2. 自动化构建与推送
我们可以使用 Jenkins 来实现自动化的镜像构建和推送流程。以下是一个简单的 Jenkins Pipeline 示例:
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git 'https://github.com/example/go-web.git'
}
}
stage('Build Image') {
steps {
script {
sh 'docker build -t my-go-app:${BUILD_NUMBER} .'
}
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh """
echo $PASSWORD | docker login -u $USERNAME --password-stdin
docker tag my-go-app:${BUILD_NUMBER} your-dockerhub-username/my-go-app:${BUILD_NUMBER}
docker push your-dockerhub-username/my-go-app:${BUILD_NUMBER}
"""
}
}
}
}
}在这个示例中:
- 我们首先检出代码仓库。
- 然后构建 Docker 镜像,并使用 Jenkins 的构建编号作为标签。
- 最后登录 Docker Hub 并推送镜像。
3. 自动化部署
一旦镜像成功构建并推送到远程仓库,下一步就是将其部署到目标环境中。这里我们继续使用 Jenkins 来演示如何在 Kubernetes 上进行部署:
stage('Deploy to Kubernetes') {
steps {
withKubeConfig([credentialsId: 'kubernetes-config']) {
sh '''
kubectl set image deployment/go-web-deployment app=your-dockerhub-username/my-go-app:${BUILD_NUMBER}
'''
}
}
}在这个示例中:
- 我们使用
withKubeConfig插件来加载 Kubernetes 配置文件。 - 然后更新 Kubernetes Deployment 中的镜像标签为最新的构建版本。
实战案例
假设我们正在开发一个电商网站,前端采用 React.js,后端采用 Node.js,并且数据库选用 MongoDB。我们的目标是实现一个完整的 CI/CD 流水线来支持项目的迭代开发。
1. 初始化项目结构
首先创建三个子项目分别对应前端、后端和服务发现:
my-project/
├── frontend/
│ └── Dockerfile
├── backend/
│ └── Dockerfile
└── mongodb/
└── Dockerfile每个子项目都有自己的 Dockerfile 来描述各自的运行环境。
2. 编写 Jenkins Pipeline
创建一个 .jenkins/Jenkinsfile 文件来定义整个流水线流程:
pipeline {
agent any
environment {
DOCKER_HUB_CREDENTIALS = credentials('dockerhub')
KUBE_CONFIG = credentials('kubernetes-config')
}
stages {
stage('Checkout') {
steps { git 'https://github.com/example/my-project.git' }
}
stage('Build Images') {
parallel {
stage('Frontend Build') { steps { sh './frontend/build.sh' } }
stage('Backend Build') { steps { sh './backend/build.sh' } }
stage('MongoDB Setup') { steps { sh './mongodb/setup.sh' } }
}
}
stage('Push Images') {
parallel {
stage('Push Frontend Image') { steps { sh './frontend/push.sh' } }
stage('Push Backend Image') { steps { sh './backend/push.sh' } }
}
}
stage('Deploy to Kubernetes') {
steps { sh './deploy/deploy-to-k8s.sh' }
}
}
post {
always { cleanWs() } // 清理工作区
}
}在这个示例中:
- 我们并行地构建和推送前端与后端镜像。
- 最终将所有服务部署到 Kubernetes 集群上。
3. 创建脚本文件
针对不同的任务创建相应的脚本文件:
前端 build.sh
#!/bin/bash
cd frontend && \
docker build -t my-react-app:${BUILD_NUMBER} .前端 push.sh
#!/bin/bash
echo ${DOCKER_HUB_CREDENTIALS_PSW} | docker login -u ${DOCKER_HUB_CREDENTIALS_USR} --password-stdin && \
docker tag my-react-app:${BUILD_NUMBER} your-dockerhub-username/my-react-app:${BUILD_NUMBER} && \
docker push your-dockerhub-username/my-react-app:${BUILD_NUMBER}后端 build.sh
#!/bin/bash
cd backend && \
docker build -t my-node-api:${BUILD_NUMBER} .后端 push.sh
#!/bin/bash
echo ${DOCKER_HUB_CREDENTIALS_PSW} | docker login -u ${DOCKER_HUB_CREDENTIALS_USR} --password-stdin && \
docker tag my-node-api:${BUILD_NUMBER} your-dockerhub-username/my-node-api:${BUILD_NUMBER} && \
docker push your-dockerhub-username/my-node-api:${BUILD_NUMBER}MongoDB setup.sh
这里可以添加一些初始化脚本来设置 MongoDB 数据库。
部署脚本 deploy-to-k8s.sh
#!/bin/bash
kubectl config use-context production-cluster && \
kubectl apply -f ./k8s/frontend-deployment.yaml && \
kubectl apply -f ./k8s/backend-deployment.yaml && \
kubectl apply -f ./k8s/mongodb-statefulset.yaml易错提示与实用技巧
- 权限问题:确保 Jenkins 用户有足够的权限来执行 Docker 和 Kubernetes 相关的操作。
- 凭证管理:妥善保管敏感信息(如 Docker Hub 密码、Kubernetes API Token),推荐使用 Jenkins 凭证插件来进行加密存储。
- 网络配置:在复杂的网络环境下,可能需要额外配置防火墙规则或 DNS 解析来保证各组件间的通信畅通无阻。
- 日志收集:利用 ELK Stack 或 Fluentd 等工具集中收集和分析日志数据以便于故障排查及性能调优。
本章小结
通过本章的学习,你应该能够掌握以下几点关键内容:
- 如何编写适合 CI/CD 流程的
Dockerfile。 - 利用 Jenkins 实现自动化镜像构建与推送的过程。
- 在 Kubernetes 上实现基于最新镜像的自动部署策略。
生产环境部署 Docker
在实际生产环境中部署 Docker 可以大大提高应用的稳定性和可移植性。通过本章的学习,你将能够理解如何在生产环境中正确配置和使用 Docker,从而提升系统的运维效率。
为了更好地理解和实践本章内容,你需要已经完成 Docker 的安装,并且熟悉基本的镜像、容器操作以及 Dockerfile 编写。
生产环境中的 Docker 部署
1. 构建可靠的镜像
首先,我们需要构建一个可靠的镜像。一个好的 Dockerfile 应该遵循一些最佳实践,比如尽量减小镜像大小、使用多阶段构建等。
# 使用官方的基础镜像
FROM golang:1.20-alpine AS builder
# 设置工作目录
WORKDIR /app
# 复制 Go 文件到工作目录
COPY . .
# 构建项目
RUN go build -o main .
# 第二阶段:使用更小的基础镜像
FROM alpine:latest
# 设置工作目录
WORKDIR /root/
# 从第一阶段复制编译后的二进制文件
COPY --from=builder /app/main .
# 暴露端口
EXPOSE 8080
# 启动服务
CMD ["./main"]2. 私有镜像仓库
在生产环境中,通常我们会有一个私有的镜像仓库来存放我们的镜像。这样可以提高安全性并且方便内部团队访问。
我们可以使用 docker run 来快速启动一个私有的 Registry:
docker run -d -p 5000:5000 --restart always --name registry registry:2然后你可以通过 docker push 和 docker pull 来管理和分发你的镜像:
docker tag my-image localhost:5000/my-image:v1.0.0
docker push localhost:5000/my-image:v1.0.03. 自动化部署与监控
对于大规模的应用部署,手动管理容器显然是不可行的。我们可以借助工具如 Docker Compose 或 Kubernetes 来进行自动化部署。
使用 Docker Compose 示例
假设我们有一个简单的 Web 应用和数据库的服务组合:
version: '3'
services:
web:
image: my-web-app:v1.0.0
ports:
- "8080:80"
depends_on:
- db
db:
image: postgres:12-alpine
environment:
POSTGRES_PASSWORD: examplepassword通过以下命令启动整个服务组合:
docker-compose up -d使用 Kubernetes 示例
Kubernetes 提供了更强大的功能和更好的扩展性。这里是一个简单的 Pod 定义文件 pod-definition.yml:
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
labels:
app: demo
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80 创建 Pod:
kubectl create -f pod-definition.yml 常见问题与排查技巧
- 权限问题:确保运行 Docker 的用户有足够的权限访问所需的资源。
- 网络配置:检查防火墙设置和 DNS 配置以确保容器之间的通信正常。
- 日志收集:使用工具如 ELK Stack 或 Fluentd 收集和分析日志以便于调试和性能优化。
- 持久化数据:合理规划卷挂载以防止数据丢失。
实际案例分享
让我们来看一下 Solita 公司是如何在生产环境中使用 Docker 的。他们主要面临的问题包括构建镜像、创建私有仓库、启动容器以及应用程序本身的 bug。尽管如此,他们依然实现了零停机维护,并成功地减少了系统停机时间的影响。
Solita 使用了多种方法来优化他们的流程,包括采用多阶段构建来减小最终镜像的体积,并且建立了严格的测试流程来确保代码质量。这些经验为我们提供了宝贵的启示。
本章小结
通过本章的学习,你应该掌握了以下几点关键内容:
- 如何构建一个可靠的 Docker 镜像。
- 如何搭建和使用私有的 Docker 镜像仓库。
- 如何利用 Docker Compose 和 Kubernetes 进行自动化部署。
- 常见问题及其排查技巧。