一、为什么选择 Docker?容器化技术的核心优势
在云计算和 DevOps 盛行的当下,Docker 作为容器化技术的佼佼者,正逐渐成为开发、运维人员的必备技能。它解决了长久以来困扰开发者的环境不一致、部署繁琐等难题,让应用的生命周期管理变得高效而流畅。那么,Docker 究竟为何如此强大?让我们深入探究。
1.1 容器 vs 虚拟机:轻量级的革新
在理解 Docker 的优势前,先对比下容器与传统虚拟机。虚拟机技术已存在多年,它通过 Hypervisor 在物理主机上创建虚拟化层,每个虚拟机都包含独立的操作系统、内核以及虚拟硬件 ,就像在一台物理机里模拟出多台独立的计算机。这种方式提供了很强的隔离性,但代价是资源占用大、启动速度慢。例如,启动一个虚拟机可能需要几分钟,占用几百 MB 甚至 GB 级别的内存,因为它要加载完整的操作系统和相关驱动。
而容器则是另一种思路。容器直接利用宿主机的操作系统内核,在用户空间通过 Namespace 实现资源隔离,用 Cgroups 进行资源限制 。这使得容器启动速度极快,通常只需几秒,资源占用也极低,仅需 MB 级内存 。容器就像一个个轻量级的 “沙盒”,应用及其依赖被打包其中,彼此隔离却共享主机内核,大大提高了资源利用率和应用部署效率。
1.2 Docker 的典型应用场景
-
环境一致性保障:开发、测试、生产环境的不一致是软件开发中的常见难题,“在我机器上能跑” 的问题屡见不鲜。Docker 通过将应用及其依赖打包成镜像,确保了在不同环境中运行的一致性 。无论在开发人员的笔记本、测试服务器还是生产集群,只要基于相同的 Docker 镜像启动容器,应用的运行环境就完全相同,极大地减少了因环境差异导致的问题。
-
微服务架构的最佳拍档:微服务架构将应用拆分为多个小型、独立的服务,每个服务可独立开发、部署和扩展。Docker 为微服务提供了理想的运行环境,每个微服务都可以封装在自己的容器中,实现了服务间的隔离与解耦 。配合容器编排工具(如 Kubernetes),可以轻松实现微服务的自动化部署、扩展和管理。
-
多版本应用无缝共存:在实际业务中,可能需要同时运行同一应用的不同版本,以满足不同用户或业务场景的需求。Docker 使得多版本应用的部署和管理变得简单,每个版本的应用可以打包成独立的镜像,在各自的容器中运行,互不干扰。通过容器编排工具,可以灵活地控制不同版本应用的流量分配和负载均衡。
-
快速构建 PaaS 平台:对于平台即服务(PaaS)提供商而言,Docker 是构建高效 PaaS 平台的关键技术。利用 Docker,可以快速创建和部署应用运行环境,实现资源的弹性分配和隔离 。开发人员可以通过 PaaS 平台方便地部署和管理自己的应用,无需关心底层基础设施的细节。
-
数据中心资源利用率提升 3 倍以上:传统数据中心中,物理服务器的资源利用率往往较低,大量资源被闲置浪费。Docker 的容器化技术可以将多个应用或服务部署在同一台物理服务器上,通过资源隔离和限制技术,确保每个容器都能获得合理的资源分配 ,从而将数据中心的资源利用率提高 3 倍以上,大大降低了运营成本。
二、Docker 核心操作:5 分钟上手
了解了 Docker 的优势与应用场景,接下来就是实战环节。这部分将详细介绍 Docker 的核心操作,让你快速上手容器化部署,掌握从镜像构建到容器管理的全流程。
2.1 基础三件套:镜像、容器、仓库
在深入学习 Docker 操作前,先明确三个核心概念:镜像、容器和仓库 ,它们构成了 Docker 容器化技术的基础。
-
镜像(Image):应用打包的 “黄金副本”。Docker 镜像是一个轻量级、独立的可执行软件包,包含运行应用程序所需的一切,包括代码、运行时、库、环境变量和配置文件 。可以把镜像看作是一个应用的 “黄金副本”,它是容器的基础,确保了应用在不同环境中运行的一致性 。例如,一个 Python Web 应用的镜像,会包含 Python 解释器、应用代码、所需的 Python 库(如 Flask、Django)以及相关配置文件。镜像采用分层结构,每一层都是只读的,这种设计不仅节省存储空间,还能加快镜像的构建和传输速度 。当你基于一个基础镜像构建自己的镜像时,实际上是在已有层的基础上添加新的层。比如,基于官方的 Ubuntu 镜像,添加 Python 和应用代码,就形成了一个完整的 Python 应用镜像。可以使用 Dockerfile 来定义镜像的构建过程,通过一系列指令(如 FROM、RUN、COPY 等)告诉 Docker 如何构建镜像 。
-
容器(Container):镜像的运行实例。容器是基于镜像创建的运行时实例,它包含应用程序及其所有依赖项,在隔离的环境中运行 。每个容器都有自己独立的文件系统、网络空间和进程空间,相互隔离,互不干扰 。容器就像是一个 “轻量级的虚拟机”,但与虚拟机不同的是,它共享宿主机的内核,因此启动速度更快,资源占用更低 。以 Nginx 容器为例,当你运行一个 Nginx 镜像时,就创建了一个 Nginx 容器,该容器运行着 Nginx 服务器,对外提供 Web 服务。容器可以被启动、停止、暂停、删除,并且可以在不同的主机之间迁移,非常灵活。
-
仓库(Repository):镜像的云端存储。Docker 仓库是用于存储和分发镜像的地方,类似于代码仓库 。仓库可以分为公共仓库和私有仓库,最常用的公共仓库是 Docker Hub,它拥有海量的官方和社区镜像,你可以方便地从中拉取所需的镜像 。例如,要获取一个 MySQL 数据库镜像,只需在命令行中执行docker pull mysql,就可以从 Docker Hub 下载最新版本的 MySQL 镜像。除了 Docker Hub,还有一些其他的公共仓库,如阿里云镜像仓库、腾讯云镜像仓库等,它们也提供了丰富的镜像资源,并且在国内的访问速度可能更快。对于企业或团队内部,为了保证镜像的安全性和私密性,通常会搭建私有仓库 。私有仓库可以使用 Harbor 等工具来搭建,它提供了更高级的权限管理、镜像复制等功能,满足企业对镜像管理的需求。
2.2 新手必学的命令
掌握了基本概念,下面来学习几条条最常用的 Docker 命令,这些命令是你操作 Docker 的基础,熟练使用它们,就能轻松管理镜像和容器。
1. docker build:从 Dockerfile 构建镜像。
sudo docker build -t myimage:v1.0
这个命令会在当前目录下查找 Dockerfile,并根据其中的指令构建一个名为myimage:v1.0的镜像。-t参数用于指定镜像的标签,格式为名称:版本 。
2. docker images:列出本地所有镜像。
sudo docker images
执行该命令后,会显示本地已下载和构建的所有镜像,包括镜像的名称、标签、ID、创建时间和大小等信息 。
3. docker run:运行容器。
sudo docker run -itd -p 0.0.0.0:8081:8081 myimage:v1.0
该命令会基于myimage:v1.0镜像创建并运行一个容器,-itd:i参数使容器保持标准输入(STDIN)打开,t为容器分配一个伪终端,d参数表示在后台运行容器,-p 8081:8081表示将容器的 8081 端口映射到主机的 8081 端口,这样就可以通过http://localhost:8081访问容器内的应用 。
4. docker ps:列出当前正在运行的容器。
sudo docker ps #或者
sudo docker container ls
执行此命令可以查看正在运行的容器的详细信息,如容器 ID、镜像名称、启动时间、状态、端口映射等 。
5. docker stop:停止容器。
sudo docker stop container_id或container_name
通过容器 ID 或名称来停止指定的容器,例如docker stop mycontainer 。
6. docker start:启动已停止的容器。
sudo docker start container_id或container_name
可以重新启动一个已停止的容器,如docker start mycontainer 。
7. docker exec:进入容器。
sudo docker exec -it <container_id_or_name> /bin/bash
进入正在运行的容器
8. docker cp:拷贝文件到容器。
sudo docker cp tmp f7b:/home
拷贝本地文件tmp到容器/home下,f7b为容器ID前三位。
9. docker rm:删除容器。
sudo docker rm container_id或container_name
用于删除一个或多个已停止的容器,如果要删除正在运行的容器,需要加上-f参数,强制删除 。
10. docker ps:查看容器完整ID。
docker ps --no-trunc
11. docker commit :将容器保存为新镜像。
# docker commit 容器完整ID 镜像名称:tag名称
sudo docker commit 39dfc9287ea6fe5e051faf17c773b3a09383c5ecd3d990cdbfeec491b474a3ce myimage:2.1
12. docker rmi:删除镜像。
sudo docker rmi image_id或image_name:tag
删除本地的镜像,例如docker rmi myimage:v1.0 。如果镜像被多个容器使用,需要先删除相关容器才能删除镜像 。
13. docker login:登录到镜像仓库。
sudo docker login
用于登录到 Docker Hub 或其他镜像仓库,输入用户名和密码后即可登录,登录后可以推送和拉取镜像 。
14. docker push:推送镜像到仓库。
sudo docker push username/image_name:tag
将本地的镜像推送到指定的镜像仓库,例如docker push myusername/myimage:v1.0 。在推送前,需要先登录到相应的仓库 。
三、实战案例:三步实现 Web 应用容器化
理论与命令只是基础,真正掌握 Docker 还需实战。接下来通过一个完整的 Web 应用容器化案例,带你深入理解 Docker 在实际项目中的应用,从构建镜像到多容器管理,全面掌握容器化部署的关键步骤。
3.1 构建 Node.js 应用镜像
以一个简单的 Node.js Web 应用为例,展示如何构建 Docker 镜像。假设我们有一个基于 Express 框架的 Node.js 应用,目录结构如下:
my-node-app/
├── app.js
├── package.json
└── node_modules/
其中,app.js是应用的入口文件,package.json记录了项目的依赖和脚本信息。
// app.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello, Dockerized Node.js App!');
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
// package.json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
接下来编写 Dockerfile,用于构建镜像。
# 使用官方Node.js运行时镜像作为基础
FROM node:18-alpine3.15
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json到工作目录
COPY package*.json.
# 安装应用依赖
RUN npm install
# 复制整个应用代码到工作目录
COPY..
# 暴露应用运行的端口
EXPOSE 3000
# 定义容器启动时执行的命令
CMD ["npm", "start"]
上述 Dockerfile 解释如下:
-
FROM node:18-alpine3.15:指定基础镜像为官方的 Node.js 18 版本,基于 Alpine Linux,体积小巧。
-
WORKDIR /app:设置容器内的工作目录为/app。
-
COPY package*.json.:将当前目录下的package.json和package-lock.json复制到容器的工作目录。
-
RUN npm install:在容器内执行npm install命令,安装应用所需的依赖。
-
COPY..:将当前目录下的所有文件(除了.dockerignore中排除的文件)复制到容器的工作目录。
-
EXPOSE 3000:声明容器运行时会暴露 3000 端口。
-
CMD ["npm", "start"]:定义容器启动时执行的命令,这里是运行npm start启动 Node.js 应用。
在项目根目录下,执行以下命令构建镜像:
docker build -t my-node-app:v1.0.
其中,-t参数用于指定镜像的标签,格式为名称:版本 ,最后的.表示 Dockerfile 在当前目录。构建完成后,可以使用docker images命令查看本地镜像列表,会看到my-node-app:v1.0镜像已构建成功。
3.2 使用 Docker Compose 管理多容器
实际项目中,一个 Web 应用往往依赖多个服务,如数据库、缓存等。这时候可以使用 Docker Compose 来管理多容器应用。假设我们的 Node.js 应用需要连接 MySQL 数据库,以下是docker-compose.yml配置示例:
version: '3.8'
services:
web:
build:
context:.
dockerfile: Dockerfile
ports:
- "3000:3000"
depends_on:
- db
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myappdb
- MYSQL_USER=myappuser
- MYSQL_PASSWORD=password
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
上述docker-compose.yml文件解释如下:
version: '3.8':指定 Docker Compose 文件的版本。
services:定义服务列表。
web:定义 Web 服务。
-
build:指定构建镜像的上下文和 Dockerfile 路径,这里使用当前目录下的 Dockerfile 构建镜像。ports:将容器的 3000 端口映射到主机的 3000 端口,这样可以通过http://localhost:3000访问 Web 应用。
-
depends_on:指定web服务依赖于db服务,确保db服务先启动。
db:定义数据库服务。
-
image:使用官方的 MySQL 8.0 镜像。
-
environment:设置 MySQL 的环境变量,包括根密码、数据库名、用户名和密码。
-
volumes:将db_data卷挂载到容器内的/var/lib/mysql目录,实现数据持久化,即使容器重启或删除,数据也不会丢失。
volumes:定义数据卷,这里创建了一个名为db_data的匿名卷。
在项目根目录下,执行以下命令启动多容器应用:
docker-compose up -d
-d参数表示在后台运行容器。执行该命令后,Docker Compose 会根据docker-compose.yml文件的配置,构建并启动web和db两个服务,并自动创建它们之间的网络连接,使 Web 应用能够连接到 MySQL 数据库 。可以使用docker-compose ps命令查看正在运行的容器,使用docker-compose logs命令查看容器的日志输出。
四、高级技巧:让 Docker 发挥更大价值
掌握了 Docker 的基础操作与实战应用后,接下来深入探索一些高级技巧,这些技巧能帮助你更高效地管理容器、优化资源利用,充分发挥 Docker 在复杂应用场景中的强大功能,让你的容器化部署更上一层楼。
4.1 数据持久化方案
在容器化应用中,数据的持久化至关重要。Docker 提供了多种数据持久化方案,每种方案都有其适用场景,选择合适的方案能确保数据的安全性和可用性。
1. 卷(Volume):推荐用于数据库存储。Docker 卷是一种特殊的目录,它绕过了容器的文件系统,直接存储在主机上 。这意味着即使容器被删除,卷中的数据依然存在,非常适合用于需要持久化存储的数据,如数据库文件 。以 MySQL 数据库为例,在启动 MySQL 容器时,可以通过以下命令挂载一个卷来存储数据库数据:
docker run -d -p 3306:3306 -v mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
上述命令中,-v mysql_data:/var/lib/mysql表示将名为mysql_data的卷挂载到容器内的/var/lib/mysql目录,这个目录是 MySQL 默认存储数据的位置 。这样,MySQL 数据库的数据就会存储在mysql_data卷中,即使容器重启或删除,数据也不会丢失 。卷还可以在多个容器之间共享,方便实现数据的共享和传递 。例如,一个 Web 应用容器和一个数据库备份容器可以共享同一个卷,Web 应用产生的数据可以实时备份到备份容器中。
2. 绑定挂载(Bind Mount):适合开发环境代码同步。绑定挂载允许将主机上的文件或目录直接挂载到容器中 ,容器内对挂载点的任何修改都会直接反映在主机上,反之亦然 。这种方式在开发环境中非常有用,可以实现代码的实时同步更新 。假设你正在开发一个 Node.js 应用,项目目录为/home/user/my - node - app,可以使用以下命令将项目目录挂载到容器中:
docker run -d -p 3000:3000 -v /home/user/my - node - app:/app my - node - app:v1.0
上述命令将主机上的/home/user/my - node - app目录挂载到容器内的/app目录,这样在开发过程中,修改主机上的代码文件,容器内的应用会立即生效,无需重新构建和启动容器,大大提高了开发效率 。但需要注意的是,绑定挂载依赖于主机上的文件系统结构,如果主机上的文件或目录被删除或移动,容器内的挂载点也会受到影响 。
3. 临时卷(Tmpfs):敏感数据内存存储。临时卷是一种基于内存的文件系统,它只在容器运行时存在,容器停止后,临时卷中的数据会被自动删除 。这种方式适合用于存储临时数据或敏感数据,如缓存文件、会话数据等 ,可以提高数据的安全性和隐私性 。在启动容器时,可以使用--tmpfs参数来创建临时卷,例如:
docker run -d --tmpfs /tmp my - app:v1.0
上述命令将在容器内创建一个临时卷,并挂载到/tmp目录,容器内对/tmp目录的读写操作都会在内存中进行,不会持久化到主机磁盘 。这样可以避免敏感数据被写入磁盘,降低数据泄露的风险 。
4.2 网络配置深度解析
Docker 的网络配置是容器化部署中的关键环节,合理的网络配置能确保容器之间、容器与外部网络之间的通信顺畅,实现高效的分布式应用架构。
1. 桥接网络:默认模式,容器间通过 IP 通信。桥接网络是 Docker 的默认网络模式,当容器使用桥接网络启动时,Docker 会为容器创建一个虚拟的以太网接口,并将其连接到一个名为docker0(在 Linux 系统中)的虚拟网桥 。同一桥接网络中的容器可以通过彼此的 IP 地址进行通信 。例如,启动两个容器container1和container2,并将它们连接到默认的桥接网络:
docker run -d --name container1 nginx
docker run -d --name container2 ubuntu:latest
在container1容器中,可以使用ping命令测试与container2容器的通信:
docker exec -it container1 ping container2的IP地址
如果通信成功,说明同一桥接网络中的容器可以正常通信 。需要注意的是,默认情况下,桥接网络中的容器与宿主机网络是隔离的,如果需要容器与宿主机通信,或者让外部网络访问容器内的服务,需要进行端口映射 。
2. 主机网络:直接使用宿主机网络栈。使用主机网络模式的容器会直接共享宿主机的网络命名空间,这意味着容器和宿主机在网络层面上几乎是等同的 ,容器可以直接使用宿主机的 IP 地址和端口进行通信,外部网络也可以直接访问容器内的服务,就像这些服务是运行在宿主机上一样 。例如,启动一个使用主机网络的 Nginx 容器:
docker run -d --name container - host - network --network host nginx:latest
因为容器共享宿主机网络,所以可以通过访问宿主机的 IP 地址和 Nginx 服务对应的端口(如 80 端口)来访问容器内的 Nginx 服务 。这种模式适用于对网络性能要求较高,且不需要网络隔离的场景,如一些监控服务、日志收集服务等 。但由于容器直接使用宿主机网络,安全性相对较低,需要谨慎使用 。
3. 自定义网络:实现服务发现(--network mynet)。Docker 允许用户创建自定义网络,以满足特定的网络拓扑和隔离需求 。通过自定义网络,可以实现容器间的服务发现,即容器可以通过名称自动解析 IP 地址进行通信 。首先,使用docker network create命令创建一个自定义网络,例如创建一个名为mynet的桥接网络:
docker network create --driver bridge mynet
然后,在启动容器时,通过--network参数将容器连接到自定义网络:
docker run -d --name web - container --network mynet nginx:latest
docker run -d --name app - container --network mynet ubuntu:latest
在app - container容器中,可以通过http://web - container(假设web - container提供了 HTTP 服务)来访问web - container内的服务 。Docker 会自动将容器名称解析为对应的 IP 地址,从而实现容器之间的通信 。自定义网络还可以配置子网、网关等参数,提供更灵活的网络配置选项,适用于复杂的分布式应用场景 。
五、避坑指南:常见问题解决方案
在 Docker 的实际应用中,难免会遇到各种问题。这部分将分享一些常见问题的解决方案,以及在使用 Docker 过程中的避坑指南,帮助你顺利解决问题,提高容器化部署的稳定性和效率。
5.1 镜像构建优化策略
1. 使用多阶段构建减少镜像体积:在构建镜像时,传统方式可能会将构建过程中的所有依赖和中间文件都打包进最终镜像,导致镜像体积过大。多阶段构建则允许在一个 Dockerfile 中使用多个 FROM 指令,每个 FROM 指令代表一个新的构建阶段。例如,在构建一个 Go 应用镜像时,第一阶段可以使用golang镜像进行代码编译,生成可执行文件;第二阶段使用alpine等轻量级基础镜像,将第一阶段生成的可执行文件复制进来,这样最终的镜像只包含运行时所需的文件,大大减小了镜像体积。以下是一个简单的多阶段构建示例:
# 第一阶段:构建阶段
FROM golang:1.19 AS builder
WORKDIR /app
COPY.
RUN go build -o myapp
# 第二阶段:运行阶段
FROM alpine:3.17
COPY --from=builder /app/myapp.
CMD ["./myapp"]
2. 合理利用.dockerignore 排除无关文件:在构建镜像时,Docker 会将构建上下文(通常是包含 Dockerfile 的目录及其子目录)中的所有文件和目录发送到 Docker 守护进程进行处理。如果不加以控制,一些无关文件(如日志文件、编译缓存等)也会被包含进镜像,增加镜像体积和构建时间。通过在项目根目录下创建.dockerignore文件,可以指定要排除的文件和目录。例如,忽略所有.log文件和node_modules目录:
*.log
node_modules/
3. 选择 Alpine 等轻量级基础镜像:Alpine 是一个基于 Alpine Linux 发行版构建的 Docker 镜像,具有小巧轻量的特点,通常只有几 MB 大小,相比其他基础镜像(如 Ubuntu 镜像接近 200MB),能显著减小最终镜像的体积。Alpine 镜像采用 musl libc 替代 glibc,专注于提供安全的操作系统,对于容器化应用来说,能满足基本需求。例如,在构建 Node.js 应用镜像时,可以选择node:18-alpine3.15作为基础镜像:
FROM node:18-alpine3.15
# 后续指令...
5.2 容器监控与日志管理
1. docker stats 实时监控资源使用:docker stats是 Docker 自带的命令行工具,用于实时查看容器的性能指标,包括 CPU 利用率、内存使用情况、网络流量等。通过它可以方便地监控和诊断容器的性能问题。例如,查看所有运行中容器的性能指标:
docker stats
如果要查看指定容器的性能指标,只需在命令后加上容器的名称或 ID:
docker stats mycontainer
2. 配置日志驱动(JSON-file/Splunk):Docker 支持多种日志驱动,不同的日志驱动适用于不同的场景。默认的日志驱动是json - file,它将容器的日志以 JSON 格式存储在宿主机的文件系统中,方便查看和管理。例如,在启动容器时指定日志驱动为json - file:
docker run -d --log-driver=json-file myimage:v1.0
对于大规模的容器部署,可能需要更强大的日志管理工具,如 Splunk。Splunk 是一款专业的日志管理和分析平台,可以收集、存储和分析来自各种来源的日志数据。要使用 Splunk 作为日志驱动,需要先安装 Splunk 的 Docker 日志驱动插件,并进行相应的配置。具体步骤如下:
-
安装 Splunk 日志驱动插件:
docker plugin install splunk/docker-logging-driver:latest --alias splunk --grant-all-permissions
-
在启动容器时指定日志驱动为 Splunk,并配置相关参数:
docker run -d --log-driver=splunk \
--log-opt splunk-token=YOUR_SPLUNK_TOKEN \
--log-opt splunk-url=https://your-splunk-server:8088/services/collector/event \
myimage:v1.0
3. 使用 Prometheus+Grafana 构建监控体系:Prometheus 是一款开源的系统监控和警报工具,Grafana 是一款可视化工具,两者结合可以构建强大的容器监控体系。Prometheus 可以收集容器的各种指标数据,如 CPU 使用率、内存使用率、网络流量等,Grafana 则可以将这些数据以直观的图表形式展示出来,方便监控和分析。以下是搭建 Prometheus+Grafana 监控体系的基本步骤:
-
安装 Prometheus:可以通过 Docker 镜像快速安装 Prometheus,首先创建prometheus.yml配置文件,定义要监控的目标(如 Docker 容器):
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'docker'
static_configs:
- targets: ['your-docker-host:9323']
然后使用以下命令启动 Prometheus 容器:
docker run -d -p 9090:9090 \
-v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
-
安装 Grafana:同样通过 Docker 镜像安装 Grafana,启动命令如下:
docker run -d -p 3000:3000 \
grafana/grafana
-
配置 Grafana:访问http://localhost:3000,登录 Grafana(默认用户名和密码为admin/admin),添加 Prometheus 数据源,并创建各种监控面板,将 Prometheus 收集的数据进行可视化展示 。
Docker 官方文档:https://docs.docker.com/
2139

被折叠的 条评论
为什么被折叠?



