跳到主要内容
Docker 容器技术入门与实战指南 | 极客日志
Shell / Bash
Docker 容器技术入门与实战指南 详细介绍 Docker 容器技术的核心概念、安装部署、镜像构建优化、私有仓库搭建(Registry/Harbor)、网络配置及数据卷管理。涵盖从基础命令到多阶段构建、安全架构及 Compose 编排的完整实战流程,帮助开发者实现环境统一与高效运维。
邪神洛基 发布于 2026/3/27 更新于 2026/9/10 59 浏览一、Docker 介绍
Docker 可以把你的程序 + 依赖库 + 配置 + 环境 全部打包成一个镜像(Image) ,然后在任何装了 Docker 的机器上一模一样地跑起来 。
Docker 是一款开源的容器化技术工具,核心价值是解决软件运行环境不一致的问题 :它将应用程序及其依赖(库、配置、运行环境等)打包成标准化的镜像(Image) ,这个镜像可理解为'可执行的安装包';基于镜像启动的容器(Container) 是轻量级、隔离的运行实例,能在任何安装了 Docker 的机器(Windows/Linux/Mac)上无差异运行 ;相比虚拟机,Docker 无需模拟完整操作系统,启动快(秒级)、资源占用少,是目前 DevOps、微服务部署的核心工具。
一句话解释:Docker 就是一个「轻量、便携、统一」的容器工具 ,用来打包、运行软件。
最形象的比喻传统部署:每台电脑环境不一样,程序经常「在我这能跑,在你那就崩」。Docker:像一个集装箱。不管船(服务器)是什么型号不管货(程序)是什么只要装进集装箱,到哪都能直接运行
核心概念
镜像(Image):程序的「安装包 / 模板」。
容器(Container):镜像跑起来后的实例,一个隔离的小环境。
Dockerfile:用来告诉 Docker 怎么打包镜像。
仓库(Registry):存放镜像的地方(比如 Docker Hub)。
运维 / 开发为什么爱用它?
环境统一:开发、测试、线上完全一样
秒级启动:比虚拟机快得多
轻量:不占太多资源
可移植:一次打包,到处运行
二、部署 docker
在 RHEL 9.6(红帽企业版 Linux 9.6) 系统中部署 Docker CE(社区版)的完整步骤。
(一)配置软件仓库
作用:创建自定义的 Docker 仓库配置文件(/etc/yum.repos.d/docker.repo)
[root@docker-node1 ~]# cat > /etc/yum.repos.d/docker.repo << EOF
[docker]
name = docker
baseurl = https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
gpgcheck = 0
EOF
[docker]:仓库的唯一标识(可自定义)。
name = docker:仓库的描述名称(无实际功能,仅便于识别)。
baseurl:Docker 软件包的下载地址(阿里云针对 RHEL 9.6 x86_64 架构的稳定版仓库)。
gpgcheck = 0:关闭 GPG 签名校验。
(二)刷新本地软件包缓存
作用:从配置的阿里云仓库拉取最新的软件包元数据(如版本、依赖关系),存入本地缓存,避免后续安装时重复下载。
[root@docker-node1 ~]# dnf makecache
正在更新 Subscription Management 软件仓库。
无法读取客户身份 本系统尚未在权利服务器中注册。可使用 "rhc" 或 "subscription-manager" 进行注册。
docker 7.3 kB/s | 46 kB 00:06
AppStream 3.1 MB/s | 3.2 kB 00:00
BaseOS 2.7 MB/s | 2.7 kB 00:00
元数据缓存已建立。
(三)搜索 Docker 相关软件包 作用:验证仓库配置是否生效,同时查看可安装的 Docker 相关包。
[root@docker-node1 ~]# dnf search docker
正在更新 Subscription Management 软件仓库。
无法读取客户身份 本系统尚未在权利服务器中注册。可使用 "rhc" 或 "subscription-manager" 进行注册。
上次元数据过期检查:0:00:13 前,执行于 2026 年 03 月 14 日 星期六 14 时 55 分 07 秒。
====================================
名称 和 概况 匹配:docker
====================================
docker-buildx-plugin.x86_64 : Docker Buildx plugin for the Docker CLI
docker-ce-rootless-extras.x86_64 : Rootless support for Docker
docker-compose-plugin.x86_64 : Docker Compose plugin for the Docker CLI
docker-model-plugin.x86_64 : Docker Model Runner plugin for the Docker CLI
pcp-pmda-docker.x86_64 : Performance Co-Pilot (PCP) metrics from the Docker daemon
podman-docker.noarch : Emulate Docker CLI using podman
=======================================
名称 匹配:docker
========================================
docker-ce.x86_64 : The open-source application container engine
docker-ce-cli.x86_64 : The open-source application container engine
名称和概况匹配:Docker 周边插件(如 docker-buildx-plugin(构建多平台镜像)、docker-compose-plugin(容器编排)、podman-docker(Podman 兼容 Docker 命令的插件)等)。
名称精确匹配:核心包 docker-ce(Docker 引擎主程序)、docker-ce-cli(Docker 命令行工具)—— 这是安装的核心目标。
(四)安装 Docker CE 主程序 [root@docker-node1 ~]# dnf install docker-ce -y
(五)修改 Docker 服务启动配置 作用:修改 Docker 服务的启动参数配置文件(docker.service 是 systemd 管理 Docker 服务的核心文件)。
[root@docker-node1 ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
ExecStart:定义 Docker 守护进程(dockerd)的启动命令。
-H fd://:Docker 守护进程的默认通信方式(通过文件描述符通信)。
--containerd=/run/containerd/containerd.sock:指定 Docker 依赖的 containerd 运行时的套接字文件。
--iptables=true:强制 Docker 使用 Linux iptables 管理容器网络(确保容器的端口映射、网络转发正常生效)。
(六)加载网桥网络模块 作用:确保 Linux 内核加载 br_netfilter 模块(网桥网络过滤模块),是 Docker 容器网络正常工作的核心依赖。
[root@docker-node1 ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf
[root@docker-node1 ~]# modprobe -a br_netfilter
第一行:将 br_netfilter 写入 /etc/modules-load.d/docker_mod.conf,实现开机自动加载 该模块。
第二行:modprobe -a br_netfilter:立即手动加载该模块(无需重启系统)。
(七)配置内核网络参数 作用:调整内核网络参数,解决 Docker 容器网络转发、iptables 规则生效的问题。
[root@docker-node1 ~]# cat > /etc/sysctl.d/docker.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
net.bridge.bridge-nf-call-iptables = 1:让网桥设备的数据包触发 iptables 规则(确保容器网络的防火墙规则生效)。
net.bridge.bridge-nf-call-ip6tables = 1:同上,针对 IPv6 网络(若不用 IPv6 可设为 0)。
net.ipv4.ip_forward = 1:开启 IPv4 转发(容器跨网络通信、端口映射的核心前提)。
重新加载所有 /etc/sysctl.d/ 目录下的内核参数配置,使修改立即生效(无需重启系统)。
[root@docker-node1 ~]# sysctl --system
(八)启动并设置 Docker 开机自启 [root@docker-node1 ~]# systemctl enable --now docker
(九)验证是否部署成功 执行 systemctl status docker 可查看 Docker 服务状态(显示 active (running) 即为成功)。
执行 docker version 可查看 Docker 客户端 / 服务端版本,验证安装完整性。
三、docker 的常规使用方法
(一)配置 docker 加速器 作用:Docker 默认从官方镜像仓库拉取镜像,速度较慢,配置国内加速器可以大幅提升镜像下载速度。
[root@docker-node1 ~]# cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://docker.1ms.run"] # 指定加速器地址
}
EOF
[root@docker-node1 ~]# systemctl restart docker
[root@docker-node1 ~]# docker info
(二)docker 常用命令 Docker 核心操作围绕镜像(Image) 和容器(Container) 展开。
1. 镜像(Image)操作命令 镜像是容器的模板,包含运行应用所需的所有文件、依赖和配置。
命令 作用 docker images查看本地已下载的镜像 docker search 镜像名搜索 Docker 仓库中的镜像 docker pull 镜像名 [:标签]下载镜像(默认拉取 latest 最新标签) docker history 镜像名 [:标签]查看镜像的构建历史 docker save -o 本地文件名.tar 镜像名 [:标签]导出镜像到本地文件 docker rmi 镜像名 [:标签]删除本地镜像 docker load -i 本地 tar 文件导入本地 tar 文件为镜像 docker commit -m "备注" 容器名 新镜像名 [:标签]将容器的修改提交为新镜像
[root@docker-node1 ~]# docker images
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
[root@docker-node1 ~]# docker search nginx
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 21206
[root@docker-node1 ~]# docker pull nginx
[root@docker-node1 ~]# docker history busybox:latest
IMAGE CREATED CREATED BY SIZE COMMENT
b3255e7dfbcd 17 months ago BusyBox 1.37.0 (glibc), Debian 13 4.49MB
[root@docker-node1 ~]# docker save -o game2048-latest.tar example/game2048:latest
[root@docker-node1 ~]# docker rmi example/mario:latest
[root@docker-node1 ~]# docker load -i game2048-latest.tar
[root@docker-node1 ~]# docker commit -m "add file" test busybox-file:latest
sha256:31a32089d025a5a54f144f15319cc6fb55be1b41d049f8905a472d5a028e
2. 容器(Container)操作命令 命令 作用 docker run -d --name 容器名 镜像名 [:标签]后台运行容器(-d:守护进程模式) docker run -it --name 容器名 镜像名 [:标签]交互模式运行容器(-i:交互;-t:终端)
[root@docker-node1 ~]# docker run -d --name web nginx:1.26
f3e369725fab95d48779eaa556941b735aae841efe09bb1d28bca89923c44ee4
[root@docker-node1 ~]# docker run -it --name busybox busybox:latest
命令 作用 docker ps查看运行中 的容器 docker ps -a查看所有 容器(运行中 + 已停止) docker inspect 容器名/ID查看容器详细信息(JSON 格式)
[root@docker-node1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3e369725fab nginx:1.26 "/docker-entrypoint…" 2 seconds ago Up 2 seconds 80/tcp web
[root@docker-node1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d1b27167a247 busybox:latest "sh" 2 minutes ago Up 1 second busybox
[root@docker-node1 ~]# docker inspect busybox
命令 作用 docker start 容器名/ID启动已停止的容器 docker stop 容器名/ID优雅停止容器(发送终止信号,允许收尾) docker kill 容器名/ID强制杀死容器(立即终止,无收尾) docker attach 容器名/ID进入已运行的交互容器 docker rm [-f] 容器名/ID删除容器(-f:强制删除运行中的容器)
[root@docker-node1 ~]# docker start busybox
[root@docker-node1 ~]# docker stop busybox
[root@docker-node1 ~]# docker kill busybox
[root@docker-node1 ~]# docker rm -f busybox
[root@docker-node1 ~]# docker attach busybox
命令 作用 docker exec 容器名 命令在运行中的容器执行非交互 命令 docker exec -it 容器名 终端在运行中的容器执行交互 命令 docker cp 容器名:路径 本地路径从容器复制文件到本地 docker cp 本地路径 容器名:路径从本地复制文件到容器
[root@docker-node1 ~]# docker exec busybox touch /root/haha
[root@docker-node1 ~]# docker exec busybox ls /root
file1 file2 haha
[root@docker-node1 ~]# docker exec -it web /bin/bash
root@f3e369725fab:/#
[root@docker-node1 ~]# docker cp test :/root/file /mnt
Successfully copied 1.54kB to /mnt
[root@docker-node1 ~]# docker cp /etc/passwd test :/root/
Successfully copied 3.07kB to test :/root/
四、容器镜像构建
(一)基础环境准备
先创建 docker 目录作为构建上下文(镜像构建时,Docker 会读取该目录下的文件 / 目录);
vim Dockerfile 用于编写镜像构建的指令集。
[root@docker-node1 ~]# mkdir docker
[root@docker-node1 ~]# cd docker/
[root@docker-node1 docker]# vim Dockerfile
(二)镜像构建是用到的参数
1. FROM:指定基础镜像
FROM 是 Dockerfile 的必备指令 ,指定构建镜像的基础镜像;
latest 是镜像标签,代表最新版本。
2. COPY:复制文件到镜像内
COPY 源文件 目标路径:将宿主机构建上下文内的文件,复制到镜像的 /root 目录下;
docker build -t 镜像名:标签 构建上下文:核心构建命令,.必须加 ,代表当前目录是构建上下文(Docker 会读取该目录下的文件供构建使用)。
[root@docker-node1 docker]# echo example > example
[root@docker-node1 docker]# cat example
example
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY example /root
[root@docker-node1 docker]# docker build -t example:v1 .
[+] Building 0.2s (7/7) FINISHED
docker:default => [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 78B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 46B 0.0s
=> [1/2] FROM docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac9 0.0s
=> => resolve docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac9 0.0s
=> [2/2] COPY example /root 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => exporting manifest sha256:3e240075ea92a383ccc7b8249faf4fbc049465ac3e490ddb90b6c759a35a2be 0.0s
=> => exporting config sha256:16a6f00150015d0df6a11f1c609afba2c28bdf3d984305922b440e52cd7f9dc2 0.0s
=> => exporting attestation manifest sha256:74b85b3b3cbdaa72964271d4d7c0fc371c727bb6070df262f 0.0s
=> => exporting manifest list sha256:0a7e2bc13bfdb457442d8bc653987c1a642f86858f6bc233dc120d6 0.0s
=> => naming to docker.io/library/example:v1 0.0s
=> => unpacking to docker.io/library/example:v1
3. LABEL:添加镜像元数据(标签) LABEL KEY=VALUE:给镜像添加自定义元数据(如作者、版本、用途),方便后续管理 / 筛选镜像。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY example /root
LABEL maintainer=admin
[root@docker-node1 docker]# docker build -t admin:v1 .
4. ADD:复制(支持解压 / 远程 URL)
[root@docker-node1 docker]# echo lee > lee
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
COPY example /root
ADD lee /root
[root@docker-node1 docker]# docker build -t lee:v2 .
普通文件复制和 COPY 一致;
若源文件是压缩包(tar.gz/tar.bz2 等),ADD 会自动解压到目标路径(COPY 仅复制压缩包,不解压);
支持远程 URL(如 ADD https://xxx/file.tar.gz /tmp)。
[root@docker-node1 docker]# tar zcf bin.tar.gz /bin
tar: 从成员名中删除开头的'/'
[root@docker-node1 docker]# ls bin.tar.gz Dockerfile lee example
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
COPY bin.tar.gz /root
ADD bin.tar.gz /mnt
[root@docker-node1 docker]# docker build -t lee:v3 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v3 /
bin dev etc home lib lib64 mnt proc root sys tmp usr var
/
/
bin
5. ENV:设置环境变量
ENV 变量名=值:在镜像内设置环境变量,后续指令(RUN/CMD/ENTRYPOINT)可通过 $变量名 引用;
容器运行时也能继承该环境变量。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=example
RUN ["/bin/sh" ,"-c" , "touch /root/$NAME " ]
[root@docker-node1 docker]# docker build -t lee:v4 .
6. EXPOSE:声明容器暴露的端口
EXPOSE 端口:仅声明 容器要暴露的端口(告诉使用者该容器会用这个端口),不自动映射 (运行容器时需加 -p 宿主机端口:容器端口 才会映射);
主要作用是文档化,方便协作(比如开发者知道容器要映射 8080 端口)。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=example
EXPOSE 8080
RUN ["/bin/sh" ,"-c" ,"touch /root/$NAME " ]
[root@docker-node1 docker]# docker build -t lee:v5 .
[root@docker-node1 docker]# docker history lee:v5
IMAGE CREATED CREATED BY SIZE COMMENT
1391576721c7 2 minutes ago RUN /bin/sh -c touch /root/$NAME
2 minutes ago EXPOSE [8080/tcp] 0B buildkit.dockerfile.v0 <missing>
2 minutes ago ENV NAME=example 0B buildkit.dockerfile.v0 <missing>
2 minutes ago LABEL Creater=lee 0B buildkit.dockerfile.v0 <missing>
17 months ago BusyBox 1.37.0 (glibc), Debian 13
7. VOLUME:定义匿名卷(持久化数据)
VOLUME "路径":给容器定义匿名数据卷 ,容器运行时,该目录会自动挂载到宿主机的 /var/lib/docker/volumes/ 下的随机目录;
作用:避免容器内数据随容器销毁而丢失(数据存在宿主机卷中),即使容器删除,卷数据仍在。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=example
EXPOSE 8080
VOLUME "/mnt"
RUN ["/bin/sh" ,"-c" , "touch /root/$NAME " ]
[root@docker-node1 docker]# docker build -t lee:v6 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v6
[root@docker-node1 ~]# docker inspect test | grep -i mounts -A10
"Mounts" : [
{
"Type" : "volume" ,
"Name" : "951e0ad881eda84a037614657b89cae88adac7c600ac03cd9505c067cee04741" ,
"Source" : "/var/lib/docker/volumes/951e0ad881eda84a037614657b89cae88adac7c600ac03cd9505c067cee04741/_data" ,
"Destination" : "/mnt" ,
"Driver" : "local" ,
"Mode" : "" ,
"RW" : true ,
"Propagation" : ""
}
]
[root@docker-node1 ~]# cd "/var/lib/docker/volumes/951e0ad881eda84a037614657b89cae88adac7c600ac03cd9505c067cee04741/_data"
[root@docker-node1 _data]# touch lee{1..5}
[root@docker-node1 docker]# docker run -it --name test --rm lee:v6 /
lee1 lee2 lee3 lee4 lee5
8. WORKDIR:设置工作目录
WORKDIR 路径:设置容器启动后的默认工作目录(类似 cd 命令);
后续的 RUN/CMD/ENTRYPOINT 指令都会在该目录下执行。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=example
EXPOSE 8080
VOLUME "/mnt"
RUN ["/bin/sh" ,"-c" , "touch /root/$NAME " ]
WORKDIR "/mnt"
[root@docker-node1 docker]# docker build -t lee:v7 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v7 /mnt
9. CMD:容器启动默认命令(可被覆盖)
CMD:定义容器启动时默认执行的命令 ;
关键特性:docker run 时若指定自定义命令,会覆盖 CMD的默认命令;
注意:exec 格式中,直接写 CMD ["/bin/echo","$NAME"] 不会解析环境变量(输出 $NAME),需通过 /bin/sh -c 包裹(如 CMD ["/bin/sh","-c","echo $NAME"])。
[root@docker-node1 docker]# vim Dockerfile
.....
VOLUME "/mnt"
RUN ["/bin/sh" ,"-c" ,"touch /root/$NAME " ]
WORKDIR "/mnt"
CMD echo $NAME
[root@docker-node1 docker]# docker build -t lee:v8 .
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
...
VOLUME "/mnt"
RUN ["/bin/sh" ,"-c" ,"touch /root/$NAME " ]
WORKDIR "/mnt"
CMD ["/bin/sh" , "-c" , "/bin/echo $NAME " ]
[root@docker-node1 docker]# docker build -t lee:v9 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8 example
[root@docker-node1 docker]# docker run -it --name test --rm lee:v9 example
[root@docker-node1 docker]# docker run -it --name test --rm lee:v9 echo haha
haha
10. ENTRYPOINT:容器启动固定命令(不可被覆盖)
ENTRYPOINT:定义容器启动时必须执行的核心命令 ,运行容器时指定的自定义命令无法覆盖 它;
场景:需要容器固定执行某个核心逻辑(如服务启动),不允许用户随意替换。
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
...
RUN ["/bin/sh" ,"-c" ,"touch /root/$NAME " ]
WORKDIR "/mnt"
ENTRYPOINT ["/bin/sh" ,"-c" , "echo $NAME " ]
[root@docker-node1 docker]# docker build -t lee:v8 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8 example
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8 echo haha
example
(三)总结 指令 核心作用 关键特性 FROM 指定基础镜像 必备,所有指令的基础 COPY 复制宿主机文件到镜像 仅复制,不解压 ADD 复制文件到镜像 支持解压压缩包、远程 URL LABEL 添加镜像元数据 方便镜像管理 / 筛选 ENV 设置环境变量 镜像 / 容器内可引用 EXPOSE 声明容器暴露端口 仅声明,不自动映射 VOLUME 定义数据卷 持久化数据,容器销毁数据不丢 WORKDIR 设置工作目录 后续指令的默认目录 CMD 容器启动默认命令 可被 docker run 命令覆盖 ENTRYPOINT 容器启动固定命令 不可被覆盖,核心逻辑固定
五、构建 CentOS 可用仓库
(一)基础环境准备:拉取并验证 CentOS 7 镜像
1. 拉取官方 CentOS 7 镜像 命令执行后,Docker 从官方仓库下载 CentOS 7 镜像,并输出下载摘要、状态等信息,确认镜像拉取成功。
[root@docker-node1 docker]# docker pull centos:7
7: Pulling from library/centos
2d473b07cdd5: Pull complete
Digest: sha256:be65f488b7764ad3638f236b7b515b3678369a5124c47b8d32916d6487418ea4
Status: Downloaded newer image for centos:7
docker.io/library/centos:7
[root@docker-node1 docker]# docker run -it --name centos centos:7 /bin/bash
[root@789703258e31 /]# uname -a
Linux 789703258e31 5.14.0-570.12.1.el9_6.x86_64
[root@789703258e31 /]# cat /etc/centos-release
CentOS Linux release 7.9.2009 (Core)
[root@789703258e31 /]# cd /etc/yum.repos.d/
[root@789703258e31 yum.repos.d]# ls
CentOS-Base.repo CentOS-Debuginfo.repo CentOS-Sources.repo CentOS-fasttrack.repo
CentOS-CR.repo CentOS-Media.repo CentOS-Vault.repo CentOS-x86_64-kernel.repo
[root@789703258e31 yum.repos.d]# exit
exit
[root@docker-node1 docker]# docker rm centos centos
(二)清理本地冗余文件 / 镜像
1. 清理本地文件 删除当前目录下无关的文件,仅保留后续构建需要的 Dockerfile,避免构建时引入冗余内容。
[root@docker-node1 docker]# ls bin.tar.gz Dockerfile lee example
[root@docker-node1 docker]# rm -fr bin.tar.gz
[root@docker-node1 docker]# ls Dockerfile lee example
[root@docker-node1 docker]# rm -rf lee example
[root@docker-node1 docker]# ls Dockerfile
2. 清理历史镜像
先用 docker images | awk '/lee/{print $1}' 筛选出名称包含 lee 的镜像(为之前实验残留的镜像);
再通过 docker images | awk '/<lee>/{system("docker rmi " $1)}' 批量删除这些镜像,释放磁盘空间,避免与新构建镜像冲突。
[root@docker-node1 docker]# docker images | awk '/lee/{print $1}'
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
lee:v1 lee:v2 lee:v3 lee:v4 lee:v5 lee:v6 lee:v7 lee:v8 lee:v9 example/game2048:latest example/mario:latest
[root@docker-node1 docker]# docker images | awk '/<lee>/{system("docker rmi " $1)}'
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
Untagged: lee:v1 Deleted: sha256:d53420bc3bac49eefebe3efb6cd52a23a81113615714247e96e5bbc3665a0d5
Untagged: lee:v2 Deleted: sha256:fdc8ae6d0590d0520326bd2eb3a6cf62293ba47dc6b10438a243225
... (省略部分输出)
(三)构建自定义 CentOS 7 镜像
1. 清空默认 yum 仓库配置 [root@docker-node1 docker]# vim Dockerfile
FROM centos:7
LABEL Creater=admin
RUN ["/bin/bash" ,"-c" ,"rm -fr /etc/yum.repos.d/*" ]
[root@docker-node1 docker]# docker build -t centos-7:repo .
[root@docker-node1 docker]# docker run -it --rm --name centos centos-7:repo /bin/bash
[root@d7addd9a7f86 /]# ls /etc/yum.repos.d/
2. 添加阿里云 CentOS 7 镜像源 编辑 Dockerfile,新增 COPY 指令,将本地自定义 repo 文件复制到容器内
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7
LABEL Creater=admin
RUN ["/bin/bash" ,"-c" ,"rm -fr /etc/yum.repos.d/*" ]
COPY centos7.repo /etc/yum.repos.d/centos7.repo
[root@docker-node1 docker]# vim centos7.repo
[centos7]
name = centos7
baseurl = https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/
gpgcheck = 0
[root@docker-node1 docker]# docker build -t centos-7:repo .
[root@docker-node1 docker]# docker run -it --rm --name centos centos-7:repo /bin/bash
验证仓库可用性:启动容器后执行 yum install gcc -y,测试 yum 能否正常安装软件(核心目的:验证自定义仓库可正常使用)
[root@c659055cb90e /]# yum install gcc -y
(四)命令说明 命令 作用 docker run -it --rm ...--rm 表示容器退出后自动删除,适合临时测试rm -fr /etc/yum.repos.d/*强制删除目录下所有文件(清空默认 repo) gpgcheck = 0关闭软件包 GPG 签名校验(阿里云镜像源可信任,关闭后避免安装时校验报错) docker build -t centos-7:repo .-t 指定镜像标签,. 表示从当前目录读取 Dockerfile 构建
六、镜像构建的优化方案(以 Nginx 为例)
(一)基础构建:未优化的 Nginx 镜像
1. 操作流程
准备环境:清理服务器文件,下载 Nginx-1.26.3 源码包;
[root@docker-node1 ~]# ls anaconda-ks.cfg centos7.tar game2048-latest.tar nginx-latest.tar busy-latest.tar docker mario-latest.tar
[root@docker-node1 ~]# mv docker/ /mnt/
[root@docker-node1 ~]# rm -fr *
[root@docker-node1 ~]# ls
[root@docker-node1 ~]# mv /mnt/docker/
访问 nginx.org 网站,找 download 复制连接
[root@docker-node1 docker]# wget http://nginx.org/download/nginx-1.26.3.tar.gz
[root@docker-node1 docker]# ls centos7.repo Dockerfile nginx-1.26.3.tar.gz
编写 Dockerfile:基于 centos-7:repo 基础镜像,完成 Nginx 编译依赖安装、源码编译、安装,暴露 80 端口、定义数据卷、设置启动命令;
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=admin
ADD nginx-1.26.3.tar.gz /root
RUN yum install gcc make pcre-devel openssl-devel -y
WORKDIR /root/nginx-1.26.3
RUN ./configure --with-http_ssl_module --with-http_stub_status_module
RUN make
RUN make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html" ]
CMD ["/usr/local/nginx/sbin/nginx" , "-g" , "daemon off;" ]
构建并验证:执行 docker build 构建 webserver:v1 镜像,运行容器后通过 curl 验证 Nginx 可访问。
[root@docker-node1 docker]# docker build -t webserver:v1 .
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 59310e4a1ce4 508MB 132MB
[root@docker-node1 docker]# docker run -d --name webserver --rm webserver:v1
0432fd82cb974bbc7375839c3e4f850260c39ebb975f2bf75db915e26bb440f4
[root@docker-node1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0432fd82cb97 webserver:v1 "/usr/local/nginx/sb…" 6 seconds ago Up 6 seconds 80/tcp webserver
[root@docker-node1 docker]# docker inspect webserver
"Networks" : {
"bridge" : {
"IPAMConfig" : null,
"Links" : null,
"Aliases" : null,
"DriverOpts" : null,
"GwPriority" : 0,
"NetworkID" : "b227442fc0626f7fa11fe74182ae82338564f45bce8bad7779b59d8709d" ,
"EndpointID" : "5417be037de8148c9fcd579cff2fed90106021c8b22943885e8e27a9026e8b" ,
"Gateway" : "172.17.0.1" ,
"IPAddress" : "172.17.0.2" ,
"MacAddress" : "3a:e2:27:5c:2f:c2" ,
"IPPrefixLen" : 16,
"IPv6Gateway" : "" ,
"GlobalIPv6Address" : "" ,
"GlobalIPv6PrefixLen" : 0,
"DNSNames" : null
}
}
[root@docker-node1 docker]# curl 172.17.0.2
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
2. 问题 构建出的 webserver:v1 镜像体积达 510MB(内容大小 132MB) ,核心原因:
编译过程产生的临时文件(如源码包、编译中间产物)未清理;
每个 RUN 指令生成独立镜像层,层越多镜像冗余越大;
基础镜像 centos-7:repo 本身体积较大(299MB)。
(二)第一次优化:清理临时文件(未达预期)
1. 优化点 把产生的临时文件全部清理
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=admin
ADD nginx-1.26.3.tar.gz /root
RUN yum install gcc make pcre-devel openssl-devel -y
WORKDIR /root/nginx-1.26.3
RUN ./configure --with-http_ssl_module --with-http_stub_status_module
RUN make
RUN make install
RUN rm -fr /root/nginx-1.26.3
RUN yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html" ]
CMD ["/usr/local/nginx/sbin/nginx" , "-g" , "daemon off;" ]
[root@docker-node1 docker]# docker build -t webserver:v1 .
2. 结果 镜像体积仍为 510MB,未优化成功 。原因是:新增的 RUN 指令又生成了 2 个镜像层,抵消了清理文件的体积优化效果。
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
(三)第二次优化:缩减镜像层
1. 核心思路——层越少,构建的软件越小 第一次优化未成功,是因为多了两个镜像层,那么我就把镜像层缩减:Dockerfile 中合并多个 RUN 指令为一个 (通过 && 串联命令),减少镜像层数,同时完成编译 + 清理操作,避免分层冗余。
2. 优化后的 Dockerfile [root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=admin
ADD nginx-1.26.3.tar.gz /root
WORKDIR /root/nginx-1.26.3
RUN yum install gcc make pcre-devel openssl-devel -y && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && rm -fr /root/nginx-1.26.3 && yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html" ]
CMD ["/usr/local/nginx/sbin/nginx" , "-g" , "daemon off;" ]
[root@docker-node1 docker]# docker build -t webserver:v2 .
3. 结果 构建出 webserver:v2 镜像体积降至426MB(内容大小 110MB) ,相比 v1 明显减小,验证了「减少镜像层」的优化效果。
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
webserver:v2 4961959ef185 426MB 110MB
[root@docker-node1 docker]# docker run -d --name webserver --rm webserver:v2
1048a0785474d756fc497e9cb94615a9c5e2ad780b1a49c69cfca6549f95155
[root@docker-node1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1048a0785474 webserver:v2 "/usr/local/nginx/sb…" 5 seconds ago Up 4 seconds 80/tcp webserver
(四)第三次优化:多阶段构建
1. 核心思路 利用 Docker多阶段构建 特性,将「编译构建」和「最终运行」分离:
第一阶段(构建阶段):基于 centos-7:repo 完成 Nginx 编译、安装、清理,仅保留编译后的产物;
第二阶段(运行阶段):基于 centos-7:repo,仅复制第一阶段的 Nginx 运行产物,舍弃编译依赖、源码等冗余文件。
2. 优化后的 Dockerfile 结构 [root@docker-node1 docker]# docker rm -f webserver
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo AS admin
LABEL Creater=admin
ADD nginx-1.26.3.tar.gz /root
WORKDIR /root/nginx-1.26.3
RUN yum install gcc make pcre-devel openssl-devel -y && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && rm -fr /root/nginx-1.26.3 && yum clean all
FROM centos-7:repo
COPY --from=admin /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html" ]
CMD ["/usr/local/nginx/sbin/nginx" , "-g" , "daemon off;" ]
3. 结果 构建出 webserver:v3 镜像体积降至308MB(内容大小 79MB) ,进一步精简了镜像(舍弃了编译阶段的依赖、工具等)。
[root@docker-node1 docker]# docker build -t webserver:v3 .
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
webserver:v2 4961959ef185 426MB 110MB
webserver:v3 28390cffd186 308MB 79MB
(五)第四次优化:基于极简基础镜像的多阶段构建
1. 核心思路
替换基础镜像:使用 gcr.io/distroless/base-debian11(极简 Debian 镜像,仅含运行必需的库和文件,体积仅 47.9MB)替代 centos-7:repo;
精准复制依赖:在构建阶段仅复制 Nginx 运行所需的二进制文件、系统库、配置文件,舍弃所有无关内容。
[root@docker-node1 ~]# ls debian11.tar.gz docker nginx-1.23.tar.gz
[root@docker-node1 ~]# docker load -i debian11.tar.gz
Loaded image: gcr.io/distroless/base-debian11:latest
[root@docker-node1 ~]# docker load -i nginx-1.23.tar.gz
Loaded image: nginx:1.23
[root@docker-node1 ~]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
gcr.io/distroless/base-debian11:latest cac381e9184d 47.9MB 22.4MB
nginx:1.23 a087ed751769 301MB 147MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
webserver:v2 4961959ef185 426MB 110MB
webserver:v3 28390cffd186 308MB 79MB
2. 优化后的 Dockerfile [root@docker-node1 docker]# vim Dockerfile
FROM nginx:1.23 AS admin
ARG TIME_ZONE
RUN mkdir -p /opt/var/cache/nginx && \
cp -a --parents /usr/lib/nginx /opt && \
cp -a --parents /usr/share/nginx /opt && \
cp -a --parents /var/log/nginx /opt && \
cp -aL --parents /var/run /opt && \
cp -a --parents /etc/nginx /opt && \
cp -a --parents /etc/passwd /opt && \
cp -a --parents /etc/group /opt && \
cp -a --parents /usr/sbin/nginx /opt && \
cp -a --parents /usr/sbin/nginx-debug /opt && \
cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libpcre* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime
FROM gcr.io/distroless/base-debian11
COPY --from=admin /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx" , "-g" , "daemon off;" ]
3. 结果 构建出 webserver:v4 镜像体积仅67.9MB(内容大小 28.4MB) ,相比初始的 v1(510MB)缩减了 86% 以上,是最优的优化方案。
[root@docker-node1 docker]# docker build -t webserver:v4 .
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
gcr.io/distroless/base-debian11:latest cac381e9184d 47.9MB 22.4MB
nginx:1.23 a087ed751769 301MB 147MB
nginx:latest bc45d248c4e1 237MB 65.8MB
example/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
example/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
webserver:v2 4961959ef185 426MB 110MB
webserver:v3 28390cffd186 308MB 79MB
webserver:v4 b0773b4ea1d1 67.9MB 28.4MB
[root@docker-node1 docker]# docker run -d --name webserver --rm webserver:v4
03e04667199d0f47fe6caffaf485b5aff8c6ec0654e984fdf7483e74f424ebd2
[root@docker-node1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e04667199d webserver:v4 "nginx -g 'daemon of…" 51 seconds ago Up 51 seconds 80/tcp, 443/tcp webserver
[root@docker-node1 docker]# docker inspect webserver
[root@docker-node1 docker]# curl 172.17.0.2
七、非加密仓库构建 - 搭建简单的 Registry 仓库
(一)环境准备(两台主机初始化) 此实验需要两台主机,docker-node1(172.25.254.10,作为仓库服务器)、docker-node2(172.25.254.20,作为客户端)
1.配置文件同步:将 node1 的 Docker 内核模块配置、系统参数配置、yum 源配置复制到 node2,保证两台主机 Docker 运行环境一致:
[root@docker-node1 docker]# scp /etc/modules-load.d/docker_mod.conf [email protected] :/etc/modules-load.d/docker_mod.conf
docker_mod.conf 100% 13 14.4KB/s 00:00
[root@docker-node1 docker]# scp /etc/sysctl.d/docker.conf [email protected] :/etc/sysctl.d/docker.conf
docker.conf 100% 103 277.4KB/s 00:00
[root@docker-node1 docker]# scp /etc/yum.repos.d/docker.repo [email protected] :/etc/yum.repos.d/docker.repo
docker.repo 100% 113 159.2KB/s 00:00
在 node2 上加载 br_netfilter(Docker 网络依赖模块):modprobe -a br_netfilter
[root@docker-node2 ~]# cat /etc/modules-load.d/docker_mod.conf
br_netfilter
[root@docker-node2 ~]# modprobe -a br_netfilter
在 node2 上通过 dnf 安装 docker-ce:dnf install docker-ce -y
[root@docker-node2 ~]# dnf install docker-ce -y
(二)部署 Registry 镜像仓库(node1 操作)
导入 Registry 镜像 :将 registry.tar 包上传到 node1,通过 docker load -i registry.tar 加载镜像(最终得到 registry:latest 镜像)。
[root@docker-node1 docker]# docker load -i registry.tar
Loaded image: registry:latest
docker images:确认镜像已加载,Registry 镜像大小约 77.3MB;
[root@docker-node1 docker]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
busybox:latest b3255e7dfbcd 6.7MB 2.22MB
centos-7:repo 55878ae91f0c 299MB 76.1MB
gcr.io/distroless/base-debian11:latest cac381e9184d 47.9MB 22.4MB
nginx:1.23 a087ed751769 301MB 147MB
nginx:latest bc45d248c4e1 237MB 65.8MB
registry:latest 6c5666b861f3 77.3MB 18.8MB
docker history registry:latest:查看镜像构建信息,确认 Registry 默认暴露 5000 端口(镜像仓库的默认通信端口)。
[root@docker-node1 docker]# docker history registry:latest
IMAGE CREATED CREATED BY SIZE COMMENT
6c5666b861f3 6 weeks ago CMD ["/etc/distribution/config.yml" ] 0B buildkit.dockerfile.v0 <missing>
6 weeks ago ENTRYPOINT ["/entrypoint.sh" ] 0B buildkit.dockerfile.v0 <missing>
6 weeks ago COPY entrypoint.sh /entrypoint.sh
6 weeks ago EXPOSE map[5000/tcp:{}]
[root@docker-node1 docker]# docker run -d -p 5000:5000 --restart=always --name registry registry:latest
192c6b85055bac37f778533f5734dfdd9bd20cece020c54bc40798c6e6b1a59
-d:后台运行容器;
-p 5000:5000:将主机 5000 端口映射到容器 5000 端口;
--restart=always:容器异常退出时自动重启;
--name registry:给容器命名为 registry。
[root@docker-node1 docker]# docker inspect registry
"Mounts" : [
{
"Type" : "volume" ,
"Name" : "1e25784ae2b8f543f474160981b58eaad2f76de167433394ceffb195b7ad2ab3" ,
"Source" : "/var/lib/docker/volumes/1e25784ae2b8f543f474160981b58eaad2f76de167433394ceffb195b7ad2ab3/_data" ,
"Destination" : "/var/lib/registry" ,
"Driver" : "local" ,
"Mode" : "" ,
"RW" : true ,
"Propagation" : ""
}
],
(三)解决 HTTPS 默认限制,配置非加密仓库 Docker 默认要求镜像仓库使用 HTTPS 协议通信,而我们搭建的是纯 HTTP 的非加密仓库,因此需要配置 insecure-registries(不安全仓库)来跳过 HTTPS 验证:
1. node1(仓库端)配置 [root@docker-node1 docker]# vim /etc/docker/daemon.json
{
"insecure-registries" : ["http://172.25.254.10:5000" ]
}
[root@docker-node1 docker]# systemctl restart docker
2. 上传镜像到私有仓库
[root@docker-node1 docker]# docker tag webserver:v4 172.25.254.10:5000/webserver:v4
[root@docker-node1 docker]# docker push 172.25.254.10:5000/webserver:v4
[root@docker-node1 docker]# curl 172.25.254.10:5000/v2/_catalog
{"repositories" :["webserver" ]}
(四)跨主机下载私有仓库镜像(node2 操作)
1. 配置非加密仓库 [root@docker-node1 docker]# vim /etc/docker/daemon.json
{
"insecure-registries" : ["http://172.25.254.10:5000" ]
}
[root@docker-node2 ~]# systemctl restart docker
2. 验证配置 [root@docker-node2 ~]# docker info
Insecure Registries:
172.25.254.10:5000
127.0.0.0/8
::1/128
Live Restore Enabled: false
Firewall Backend: iptables
3. 拉取镜像 [root@docker-node2 ~]# docker pull 172.25.254.10:5000/webserver:v4
[root@docker-node2 ~]# docker images i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA
172.25.254.10:5000/webserver:v4 b0773b4ea1d1 67.9MB 28.4MB
八、仓库的加密传输及用户认证
(一)前置清理:重置 Docker 非加密配置
[root@docker-node1 ~]# vim /etc/docker/daemon.json
[root@docker-node2 ~]# > /etc/docker/daemon.json
[root@docker-node1 ~]# systemctl restart docker
[root@docker-node2 ~]# systemctl restart docker
(二)生成 TLS 证书(解决 OpenSSL 版本兼容问题) CentOS7 自带的 OpenSSL 版本较低,直接使用 addext 参数会报错,因此改用配置文件方式 生成证书。
[root@docker-node1 ~]# mkdir -p /etc/docker/certs
[root@docker-node1 ~]# cat > /etc/docker/certs/san.cnf << EOF
[req]
distinguished_name = req_distinguished_name
# 禁用交互式输入
x509_extensions = v3_req
# 使用 v3 扩展
prompt = no
# 非交互式
[req_distinguished_name]
CN = registry.example.com
# 证书主域名(仓库域名)
[v3_req]
subjectAltName = DNS:registry.example.com
# 证书备用域名(和主域名一致)
EOF
[root@docker-node1 certs]# openssl req -newkey rsa:4096 \
> -nodes
> -keyout registry.example.com.key
> -x509 -days 365
> -out registry.example.com.crt
> -config san.cnf
> -sha256
查看证书详情,确认 subjectAltName 等关键信息是否正确。
[root@docker-node1 certs]# openssl x509 -in /etc/docker/certs/registry.example.com.crt -noout -text
(三)启动加密的 Docker Registry 仓库
--restart=always:Docker 重启后自动启动仓库容器;
-v /opt/registry:/var/lib/registry:将仓库的镜像数据持久化到宿主机 /opt/registry;
-e REGISTRY_HTTP_TLS_*:指定 TLS 证书和私钥,开启 HTTPS 加密。
[root@docker-node1 certs]# docker run -d -p 443:443 --restart=always --name registry \
> -v /opt/registry:/var/lib/registry
> -v /etc/docker/certs:/certs
> -e REGISTRY_HTTP_ADDR=0.0.0.0:443
> -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.example.com.crt
> -e REGISTRY_HTTP_TLS_KEY=/certs/registry.example.com.key
> registry
(四)配置域名解析(本地 hosts) 将仓库域名 registry.example.com 解析到仓库所在主机(docker-node1,IP:172.25.254.10),避免 DNS 解析问题。
[root@docker-node1 ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.10 docker-node1
172.25.254.10 registry.example.com
[root@docker-node2 ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.20 docker-node2
172.25.254.10 registry.example.com
(五)解决证书信任问题(推送镜像失败的修复)
1. 初始推送失败原因 Docker 客户端默认不信任自签名证书,推送时会报 x509: certificate signed by unknown authority。
[root@docker-node1 ~]# docker push registry.example.com/webserver:v4
The push refers to repository [registry.example.com/webserver]
577c8ee06f39: Waiting af5aa97ebe6c: Waiting 5342a2647e87: Waiting bbb6cacb8c82: Waiting 1a73b54f556b: Waiting 2388d2e8e2b: Waiting 6a575081cb9a: Waiting ac805962e479: Waiting c04827a7d9f: Waiting 8451c71f8c1e: Waiting 6835249f77a: Waiting 7fe377719de: Waiting 9ed498e22b2: Waiting 4d92f83d9cf: Waiting 2a92d6ac9e4f: Waiting 24aacbf97031: Waiting failed to do request: Head "https://registry.example.com/v2/webserver/blobs/sha256:2a92d6ac9e4fcc274d5168b217ca4458a9fec6f094ead68d99c77073f08caac1" : tls: failed to verify certificate: x509: certificate signed by unknown authority
2. 修复操作
[root@docker-node1 ~]# mkdir /etc/docker/certs.d/registry.example.org/ -p
[root@docker-node1 ~]# cp /etc/docker/certs/registry.example.com.crt /etc/docker/certs.d/registry.example.org/ca.crt
[root@docker-node1 ~]# systemctl restart docker
3. 验证推送
[root@docker-node1 ~]# docker tag webserver:v4 registry.example.com/webserver:v4
[root@docker-node1 ~]# docker push registry.example.com/webserver:v4
[root@docker-node1 ~]# curl -k https://172.25.254.10/v2/_catalog
{"repositories" :["webserver" ]}
(六)添加用户认证(限制仓库访问权限)
1. 安装工具 + 创建认证文件
[root@docker-node1 ~]# dnf install httpd-tools -y
[root@docker-node1 ~]# mkdir /etc/docker/auth
[root@docker-node1 ~]# htpasswd -Bc /etc/docker/auth/htpasswd admin
2. 重启带认证的仓库容器
[root@docker-node1 ~]# docker rm -f registry
[root@docker-node1 ~]# docker run -d -p 443:443 --restart=always --name registry \
> -v /opt/registry:/var/lib/registry \
> -v /etc/docker/certs:/certs \
> -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
> -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.example.com.crt \
> -e REGISTRY_HTTP_TLS_KEY=/certs/registry.example.com.key \
>
> -v /etc/docker/auth:/auth
> -e "REGISTRY_AUTH=htpasswd"
> -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"
> -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
> registry
3. 认证验证
[root@docker-node1 ~]# docker push registry.example.com/webserver:v4
push access denied, repository does not exist or may require authorization: authorization failed: no basic auth credentials
[root@docker-node1 ~]# docker login registry.example.com -u admin
Login Succeeded
[root@docker-node1 ~]# docker push registry.example.com/webserver:v4
[root@docker-node1 ~]# scp -r /etc/docker/certs.d/ [email protected] :/etc/docker/certs.d
[root@docker-node2 ~]# docker login registry.example.com -u admin
[root@docker-node2 ~]# docker pull registry.example.com/webserver:v4
(七)总结
加密 :通过自签名 TLS 证书实现仓库 HTTPS 加密传输,解决 OpenSSL 版本兼容问题;
信任 :Docker 客户端需将证书放到 /etc/docker/certs.d/仓库域名/ca.crt 才能信任自签名证书;
认证 :通过 htpasswd 实现用户名密码认证,限制仓库的推送 / 拉取权限;
跨节点访问 :其他节点需同步证书并登录,才能访问私有仓库。
九、Harbor 仓库构建
(一)Docker 版本降级 Harbor 对 Docker 版本有兼容性要求,我现在的 docker 版本 29 太高,不支持 harbor,所以我要给 docker 进行降级处理。
[root@docker-node1 harbor]# docker compose down
[root@docker-node1 ~]# dnf remove docker-ce -y
[root@docker-node1 ~]# dnf install docker-ce-3:28.5.2-1.el9 -y
[root@docker-node1 ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@docker-node1 ~]# systemctl daemon-reload
[root@docker-node1 ~]# systemctl enable --now docker.service
(二)Harbor 部署准备
1. 目录与证书准备
[root@docker-node1 ~]# mkdir /data/
[root@docker-node1 ~]# cp -rp /etc/docker/certs /data/
2. 本地域名解析 [root@docker-node1 ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.10 docker-node1
172.25.254.10 registry.example.com
3. Harbor 安装包解压与配置 [root@docker-node1 ~]# tar zxf harbor-offline-installer-v2.5.4.tgz
[root@docker-node1 ~]# cd /opt/
[root@docker-node1 opt]# cd harbor/
[root@docker-node1 harbor]# cp harbor.yml.tmpl harbor.yml
[root@docker-node1 harbor]# vim harbor.yml
hostname: registry.example.com
certificate: /data/certs/registry.example.com.crt
private_key: /data/certs/registry.example.com.key
harbor_admin_password: admin
[root@docker-node1 harbor]# ./install.sh
(三)Harbor 基本操作:启动 / 停止 / 登录 / 镜像上传
1. 容器管理
[root@docker-node1 harbor]# docker compose down
[root@docker-node1 harbor]# docker compose up -d
2. 登录 Harbor 仓库 [root@docker-node1 harbor]# docker login registry.example.com
Authenticating with existing credentials...
[Username: admin]
i Info → To login with a different account, run 'docker logout' followed by 'docker login'
Stored credentials invalid or expired
Username (admin): admin
Password:
WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json' . Configure a credential helper to remove this warning. See https://docs.docker.com/go/credential-store/
Login Succeeded
3. 镜像打标签并上传
[root@docker-node1 harbor]# docker tag busybox:latest registry.example.com/library/busybox:latest
[root@docker-node1 harbor]# docker push registry.example.com/library/busybox:latest
[root@docker-node1 harbor]# docker tag busybox:latest registry.example.com/library/webserver:v1
[root@docker-node1 harbor]# docker push registry.example.com/library/webserver:v1
(四)Harbor 图形化界面操作 访问地址:http://172.25.254.10/harbor(或 https://registry.example.com/harbor);
创建自定义项目(如 example):若未勾选'公开',该项目下的镜像需登录后才能下载;
[root@docker-node1 ~]# docker tag busybox:latest registry.example.com/example/busybox:latest
[root@docker-node1 ~]# docker push registry.example.com/example/busybox:latest
(五)多节点配置:另一台服务器(docker-node2)使用 Harbor 仓库
1. 同步 Docker 版本(与 docker-node1 一致) [root@docker-node2 ~]# dnf remove docker-ce -y
[root@docker-node2 ~]# dnf install docker-ce-3:28.5.2-1.el9 -y
[root@docker-node2 ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@docker-node2 ~]# systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
2. 配置 Harbor 为 Docker 镜像加速器
[root@docker-node2 ~]# vim /etc/docker/daemon.json
{
"registry-mirrors" : ["https://registry.example.com" ]
}
[root@docker-node2 ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.20 docker-node2
172.25.254.10 registry.example.com
[root@docker-node2 ~]# systemctl restart docker
[root@docker-node2 ~]# docker info
Registry Mirrors: https://registry.example.com/
3. 从 Harbor 下载镜像
[root@docker-node2 ~]# docker pull example/busybox
(六)总结
版本兼容 :Harbor 对 Docker 版本敏感,需降级到兼容版本(如 28.5.2);
域名解析 :所有节点需配置 /etc/hosts 解析 Harbor 自定义域名;
镜像命名规则 :Harbor 域名/项目名/镜像名:版本 是上传 / 下载的核心;
多节点复用 :其他服务器需同步 Docker 版本 + 配置 Harbor 为镜像加速器,才能便捷拉取镜像;
权限控制 :Harbor 项目可通过'公开 / 私有'控制镜像访问权限,私有镜像需登录后才能下载。
十、joined 容器网络
(一)操作背景
启动一个基础的 Nginx 容器(webserver);
由于 Nginx 容器内未预装 ip 命令,无法查看网络配置;
启动另一个带 ip 命令的 busybox 容器,共享 webserver 的网络命名空间 ,从而查看网络配置。
(二)操作过程 --network container:webserver:核心参数!指定容器共享 webserver 的网络命名空间(即和 webserver 用同一个网络栈)
rickiechina/busyboxplus:latest:使用带 ip 命令的 busybox 增强版镜像
[root@docker-node1 ~]# docker load -i busyboxplus.tar
[root@docker-node1 ~]# docker run -d --rm --name webserver --network bridge nginx:1.23
[root@docker-node1 ~]# docker exec -it webserver /bin/bash
root@3e2ed29f9282:/# ip a
bash: ip: command not found
root@3e2ed29f9282:/# exit
exit
[root@docker-node1 ~]# docker run -it --name busybox --rm --network container:webserver rickiechina/busyboxplus:latest /bin/sh: shopt : not found
[ root@3e2ed29f9282:/ ]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0@if198: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether a2:a4:30:19:b7:cb brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
inet 172.17.0.2/16:容器的 IP 地址(Docker bridge 网络默认的网段是 172.17.0.0/16);
这个 IP 实际是 webserver 容器的 IP—— 因为 busybox 共享了它的网络命名空间,所以看到的是同一个 IP。
十一、joined 网络企业实战
(一)导入镜像 将本地的 phpmyadmin-latest.tar.gz(phpMyAdmin 最新版镜像包)和 mysql-8.0.tar(MySQL 8.0 版本镜像包)导入到 Docker 环境中,替代从远程仓库拉取镜像,适合无外网或镜像定制化的场景。
[root@docker-node1 ~]# docker load -i phpmyadmin-latest.tar.gz
[root@docker-node1 ~]# docker load -i mysql-8.0.tar
(二)运行 php-myadmin 容器
docker run:创建并启动 Docker 容器的核心命令;
-d:后台运行容器(守护进程模式),不占用当前终端;
--name phpmyadmin:为容器命名为 phpmyadmin,方便后续管理(如启停、删除);
-e PMA_ARBITRARY=1:设置环境变量 PMA_ARBITRARY=1,允许 phpMyAdmin 连接任意地址的 MySQL 服务器(而非仅限本地);
-p 80:80:端口映射,将宿主机的 80 端口映射到容器内的 80 端口,外部可通过宿主机 80 端口访问 phpMyAdmin;
phpmyadmin:latest:指定使用的镜像名称和版本。
[root@docker-node1 ~]# docker run -d --name phpmyadmin -e PMA_ARBITRARY=1 -p 80:80 phpmyadmin:latest
(三)运行 mysql
--name mysql:为 MySQL 容器命名为 mysql;
-e MYSQL_ROOT_PASSWORD='admin':设置 MySQL 的 root 用户密码为 admin(必须配置,否则 MySQL 容器无法启动);
--network container:phpmyadmin:核心网络配置 ,让 MySQL 容器共享 phpmyadmin 容器的网络命名空间;
效果:两个容器处于同一网络环境,MySQL 容器可被 phpMyAdmin 容器直接访问(无需暴露 MySQL 端口到宿主机,更安全);
mysql:8.0:指定使用 MySQL 8.0 版本的镜像。
[root@docker-node1 ~]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORD='admin' --network container:phpmyadmin mysql:8.0
(四)访问 phpmyadmin 通过浏览器访问宿主机(docker-node1)的 IP 地址 172.25.254.10
十二、容器跨主机通信
(一)设定硬件
核心操作 :为两台 Docker 主机(命名为 docker-node1、docker-node2)新增网卡,并将网卡模式设置为 host-only(仅主机模式)。
作用 :host-only 模式下的网卡仅用于主机间的私有网络通信,隔离外部网络,保证容器跨主机通信的网络环境独立、可控。
(二)开启新加网卡的混杂模式
1. 混杂模式说明 网卡的混杂模式(PROMISC) 是指网卡接收所有经过它的数据包(而非仅目标 MAC 地址匹配自身的数据包),这是 macvlan 网络实现跨主机通信的前提。
2. 代码解释
ip a s eth1:ip address show eth1 的简写,查看 eth1 网卡的配置信息;
<BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP>:
BROADCAST:支持广播;
MULTICAST:支持组播;
PROMISC:已开启混杂模式;
UP:网卡逻辑层面启用;
LOWER_UP:网卡物理层面已连接(如网线插好);
两台主机的 eth1 网卡均确认开启混杂模式,为后续网络通信铺路。
[root@docker-node1 ~]# ip a s eth1
54: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link /ether 00:0c:29:21:54:d9 brd ff:ff:ff:ff:ff:ff altname enp19s0 altname ens224
[root@docker-node2 ~]# ip a s eth1
4: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link /ether 00:0c:29:cf:83:84 brd ff:ff:ff:ff:ff:ff altname enp19s0 altname ens224
(三)配置 Docker 自建网络(macvlan 驱动)
macvlan 是 Docker 的网络驱动,它允许容器直接使用主机网卡的 MAC 地址和 IP 地址,让容器像独立的物理设备一样在网络中通信,适合跨主机场景。
[root@docker-node1 ~]# docker network create \
-d macvlan
--subnet 1.1.1.0/24
--gateway 1.1.1.1
-o parent=eth1 example
0cad05f438fbae92c0bb5b7119de158c9d81cfd3e588bfc14f980593447a8f9c
[root@docker-node1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5b1ea5c41484 bridge bridge local
6c9d5c41484 host host local
0cad05f438fb example macvlan local
dbc0eb18cd23 none null local
关键注意点 :两台主机需创建完全相同配置 的 macvlan 网络(同网段、同网关、绑定同名称网卡、同网络名称),否则跨主机通信会失败。
(四)测试跨主机 Docker 容器通信
1. 启动容器(指定 macvlan 网络和固定 IP)
--network example:将容器加入 example 这个 macvlan 网络;
--ip 1.1.1.100:为容器指定固定 IP(需在子网 1.1.1.0/24 范围内);
--rm:容器退出后自动删除;
busybox:轻量级 Linux 镜像,用于测试网络通信。
[root@docker-node1 ~]# docker run -it --name busybox --rm --network example --ip 1.1.1.100 --rm busybox /
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
55: eth0@if54: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether 66:46:be:d5:ca:6a brd ff:ff:ff:ff:ff:ff inet 1.1.1.100/24 brd 1.1.1.255 scope global eth0 valid_lft forever preferred_lft forever
[root@docker-node2 ~]# docker run -it --name busybox --rm --network example --ip 1.1.1.200 --rm busybox /
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
5: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether 96:d7:cb:bc:56:79 brd ff:ff:ff:ff:ff:ff inet 1.1.1.200/24 brd 1.1.1.255 scope global eth0 valid_lft forever preferred_lft forever
2. 容器网络信息验证
eth0@if54(node1 容器)/ eth0@if4(node2 容器):容器的网卡绑定到主机的 eth1 网卡(@if54/@if4 对应主机 eth1 的网卡索引);
容器 IP 分别为 1.1.1.100/24 和 1.1.1.200/24,与指定的一致,且在同一子网。
3. 跨主机通信测试 在 node2 的容器中执行 ping 1.1.1.100:
/
PING 1.1.1.100 (1.1.1.100): 56 data bytes
64 bytes from 1.1.1.100: seq =0 ttl=64 time =0.276 ms
64 bytes from 1.1.1.100: seq =1 ttl=64 time =0.406 ms
能收到回包(64 bytes from 1.1.1.100),说明两台主机的 Docker 容器已成功实现跨主机通信
十三、Docker 数据卷管理及优化
(一)bind mount 数据卷(绑定挂载)
bind mount 是将宿主机的目录 / 文件直接挂载到容器内 ,是最基础的数据卷方式,支持读写 / 只读权限控制。
[root@docker-node1 ~]# docker volume prune
WARNING! This will remove anonymous local volumes not used by at least one container. Are you sure you want to continue ? [y/N] y
[root@docker-node1 ~]# docker run -it --rm --name test \
-v /data:/data
-v /data1:/data1:ro
-v /etc/passwd:/passwd:ro
busybox:latest
/
/
file
/
touch : /data1/file: Read-only file system
/
sh: can't create passwd: Read-only file system
(二)Docker Managed Volume(Docker 管理卷)
Docker 自行管理的卷(无需指定宿主机路径,由 Docker 统一存储),路径默认在 /var/lib/docker/volumes/[卷名]/_data,更易管理。
[root@docker-node1 ~]# docker volume create example
example
[root@docker-node1 ~]# docker volume ls
DRIVER VOLUME NAME
local example
[root@docker-node1 volumes]# touch example/_data/file
[root@docker-node1 ~]# docker run -it --rm -v example:/data:ro busybox:latest
/
touch : data/file: Read-only file system
/
file
[root@docker-node1 ~]# docker volume rm example
example
[root@docker-node1 ~]# docker volume ls
DRIVER VOLUME NAME
(三)数据卷容器(Volumes-from)
专门用于挂载数据卷的'模板容器',其他容器可通过 --volumes-from 继承其所有挂载配置,实现数据共享。
容器内可操作 /data(创建 example、file 文件),但无法修改 /hosts(只读)。
[root@docker-node1 ~]# docker run -it --rm --name data \
-v /etc/hosts:/hosts:ro
-v /data:/data
busybox:latest
/
cat : can't open ' /etc/host': No such file or directory
/ # cat /hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.10 docker-node1
172.25.254.10 registry.example.com
/ # > /hosts
sh: can' t create /hosts: Read-only file system
/
/
example
/
/
file example
无需重复挂载配置,直接访问 /hosts(和 data 容器内容一致);
直接访问 /data(可看到 data 容器创建的文件,且能删除 / 修改);
实现了多个容器共享同一套挂载数据。
[root@docker-node1 ~]# docker run -it --rm --name admin --volumes-from data busybox:latest
/
/
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost6.localdomain6
172.25.254.10 docker-node1
172.25.254.10 registry.example.com
/
example
/
file example
/
/
(四)数据的备份和迁移
基于数据卷实现容器数据的备份(打包)、恢复(解压),适用于数据迁移 / 容灾场景。
1. 准备测试容器(nginx)
宿主机 /data 挂载到容器 nginx 的网页根目录;
容器内创建测试文件(example1~10),模拟业务数据。
[root@docker-node1 ~]# docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html nginx:1.23
[root@docker-node1 ~]# docker exec -it webserver bash
root@aa28e0ff63f0:/# cd /usr/share/nginx/html/
root@aa28e0ff63f0:/usr/share/nginx/html# ls
index.html example
root@aa28e0ff63f0:/usr/share/nginx/html# touch example{1..10}
root@aa28e0ff63f0:/usr/share/nginx/html# ls
index.html example1 example2 example4 example6 example8 example example10 example3 example5 example7 example9
root@aa28e0ff63f0:/usr/share/nginx/html# exit
exit
2. 数据备份(打包) [root@docker-node1 ~]# docker run -it --rm \
--volumes-from webserver
-v $(pwd ):/backup
busybox:latest
/
/
index.html example1 example2 example4 example6 example8 example example10 example3 example5 example7 example9
/
tar: removing leading '/' from member names
/
/
[root@docker-node1 ~]# ls busyboxplus.tar harbor mysql-8.0.tar busy-latest.tar harbor-offline-installer-v2.14.0.tgz nginx-1.23.tar.gz debian11.tar.gz html.tar.gz
3. 数据恢复(解压)
[root@docker-node1 ~]# rm -fr /data/*
[root@docker-node1 ~]# docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html -v $(pwd ):/backup nginx:1.23
409fc810840b52522a5d9016c1e3b974a458f51c1c3d755110c69e6df51c1f54
[root@docker-node1 ~]# docker exec -it webserver bash
root@409fc810840b:/# tar zxf /backup/html.tar.gz -C /
root@409fc810840b:/# ls /usr/share/nginx/html/
index.html example1 example2 example4 example6 example8 example example10 example3 example5 example7 example9
(五)总结 数据卷类型 特点 适用场景 bind mount 直接挂载宿主机路径,灵活 宿主机和容器需强耦合的场景 Docker 管理卷 Docker 统一管理路径,无需关心宿主机位置 容器独立管理数据,解耦宿主机 数据卷容器 继承挂载配置,多容器共享 集群容器共享数据 数据备份 / 迁移 基于卷打包 / 解压,跨环境迁移 数据容灾、迁移、备份
十四、Docker 安全架构
(一)更改系统 cgroup(企业中非必须)
调整 Linux 系统的 cgroup(控制组)层级模式,从 cgroup2 切换回传统的 cgroup v1 分层架构。cgroup 是 Linux 内核用于限制、记录和隔离进程组资源使用(CPU、内存、IO 等)的机制,Docker 依赖 cgroup 实现容器资源管控。
[root@docker-node1 ~]# mount -t cgroup2 cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
[root@docker-node1 ~]# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
[root@docker-node1 ~]# reboot
重启系统后,执行 mount -t cgroup 可看到 cgroup v1 被拆分为 cpu、memory、devices 等多个独立子系统挂载,每个子系统对应不同的资源管控维度。
[root@docker-node1 ~]# mount -t cgroup cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
(二)Docker 的资源限制
通过 Docker 命令行参数限制容器对 CPU、内存、磁盘 IO 等资源的占用,避免单个容器耗尽宿主机资源,提升系统稳定性。
1. cpu 的用量 核心是通过 --cpu-period 和 --cpu-quota 控制容器的 CPU 时间片占比:
--cpu-period:CPU 调度周期(单位:微秒),默认 100000(100ms);
--cpu-quota:容器在一个周期内可使用的 CPU 时间(单位:微秒)
[root@docker-node1 ~]# docker load -i ubuntu-latest.tar.gz
f36fd4bb7334: Loading layer 80.56MB/80.56MB
Loaded image: ubuntu:latest
[root@docker-node1 ~]# docker run -it --rm ubuntu:latest
root@616ebe97890b:/# dd if =/dev/zero of=/dev/null &
[1] 10
root@616ebe97890b:/# top
top - 06:25:00 up 2:52, 0 user, load average: 0.23, 0.09, 0.09
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 14.8 us, 26.3 sy, 0.0 ni, 58.3 id , 0.0 wa, 0.4 hi, 0.2 si, 0.0 st
MiB Mem : 1739.0 total, 613.8 free, 589.7 used, 693.1 buff/cache
MiB Swap: 4008.0 total, 4008.0 free, 0.0 used.
1149.2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
10 root 20 0 2736 1408 1408 R 99.7 0.1 0:08.87 dd
1 root 20 0 4588 3840 3328 S 0.0 0.2 0:00.02 bash
11 root 20 0 8844 5248 3200 R 0.0 0.3 0:00.00 top
[root@docker-node1 ~]# docker run -it --rm --name test --cpu-period 100000 --cpu-quota 20000 ubuntu
root@a7fa69da9c0c:/# dd if =/dev/zero of=/dev/null &
[1] 9
root@a7fa69da9c0c:/# top
top - 06:28:23 up 2:56, 0 user, load average: 0.62, 0.32, 0.18
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 14.9 us, 27.5 sy, 0.0 ni, 57.1 id , 0.0 wa, 0.6 hi, 0.0 si, 0.0 st
MiB Mem : 1739.0 total, 632.8 free, 570.6 used, 693.3 buff/cache
MiB Swap: 4008.0 total, 4008.0 free, 0.0 used.
1168.3 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 2736 1408 1408 R 98.7 0.1 0:37.61 dd
1 root 20 0 4588 3840 3328 S 0.0 0.2 0:00.01 bash
10 root 20 0 8844 5248 3200 R 0.0 0.3 0:00.00 top
2. CPU 优先级限制 通过 --cpu-shares 设置容器的 CPU 共享权重(默认值 1024),仅在 CPU 资源竞争时生效 :
权重越高,容器获取的 CPU 时间片越多;
示例:启动两个容器,一个 --cpu-shares 100,另一个默认 1024,当 CPU 资源紧张时,前者获取的 CPU 资源约为后者的 1/10;
测试方式:通过 dd if=/dev/zero of=/dev/null 模拟 CPU 满负载,结合 top 观察 CPU 占用率验证限制效果。
[root@docker-node1 ~]# cd /sys/devices/system/cpu/
[root@docker-node1 cpu]# cd /sys/devices/system/cpu/
cpu0/ cpufreq/ hotplug/ smt/
cpu1/ cpuidle/ power/ vulnerabilities/
[root@docker-node1 cpu]# ls
cpu0 cpuidle isolated nohz_full possible smt
cpu1 crash_hotplug kernel_max offline power uevent cpufreq hotplug modalias online present vulnerabilities
[root@docker-node1 cpu]# cat cpu*/online
1
[root@docker-node1 cpu]# cat cpu0/online
cat : cpu0/online: 没有那个文件或目录
[root@docker-node1 cpu]# cat cpu1/online
1
[root@docker-node1 cpu]# echo 0 > cpu1/online
[root@docker-node1 cpu]# cat /proc/cpuinfo | grep cores
cpu cores : 1
[root@docker-node1 ~]# docker run -it --rm --name test ubuntu
root@100d1b161ffa:/# dd if =/dev/zero of=/dev/null &
[1] 8
root@100d1b161ffa:/# top
[root@docker-node1 ~]# docker run -it --rm --name test1 ubuntu
root@718fb12421d4:/# dd if =/dev/zero of=/dev/null &
[1] 8
root@718fb12421d4:/# top
[root@docker-node1 ~]# docker run -it --rm --cpu-shares 100 ubuntu
root@7e5aee8f5814:/# dd if =/dev/zero of=/dev/null &
[1] 8
root@7e5aee8f5814:/# top
[root@docker-node1 ~]# docker run -it --rm ubuntu:latest
root@0d24e4f08288:/# dd if =/dev/zero of=/dev/null &
[1] 9
root@0d24e4f08288:/# top
3. 内存使用限制 通过 --memory 和 --memory-swap 限制容器的内存 + 交换分区使用量:
--memory 200M:限制容器最多使用 200MB 物理内存;
--memory-swap 200M:限制容器总内存(物理 + 交换)为 200MB(若不设置,默认是 --memory 的 2 倍);
验证方式:
启动带内存限制的 Nginx 容器;
通过 cgexec 工具在容器的 cgroup 内存组中执行 dd 命令写入大文件;
当写入数据量超过 200MB 时,dd 进程被内核杀死,验证内存限制生效;
查看 /sys/fs/cgroup/memory/docker/<容器 ID>/memory.limit_in_bytes 可确认内存限制的字节数。
[root@docker-node1 ~]# docker run -d --name test --memory 200M --memory-swap 200M nginx:1.23
4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4
[root@docker-node1 ~]# rpm -ivh libcgroup-0.41-19.el8.x86_64.rpm
[root@docker-node1 ~]# rpm -ivh libcgroup-tools-0.41-19.el8.x86_64.rpm
[root@docker-node1 ~]# cgexec -g memory:docker/4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4 dd if =/dev/zero of=/dev/shm/bigfile bs=1M count=100
记录了 100+0 的读入
记录了 100+0 的写出
104857600 字节(105 MB,100 MiB)已复制,0.2563 s,409 MB/s
[root@docker-node1 ~]# cgexec -g memory:docker/4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4 dd if =/dev/zero of=/dev/shm/bigfile bs=1M count=150
记录了 150+0 的读入
记录了 150+0 的写出
157286400 字节(157 MB,150 MiB)已复制,0.174223 s,903 MB/s
[root@docker-node1 ~]# cgexec -g memory:docker/4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4 dd if =/dev/zero of=/dev/shm/bigfile bs=1M count=200
已杀死
[root@docker-node1 ~]# cd /sys/fs/cgroup/memory/docker/4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4
[root@docker-node1 4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4]# cat memory.limit_in_bytes
209715200
[root@docker-node1 4b0db5efb9438afc69a5b3cdafd932fb0d6b03634f50f4c8f1d2c12b3c292af4]# cat memory.memsw.limit_in_bytes
209715200
4. 磁盘 IO 限制
[root@docker-node1 ~]# docker run -it --name test --rm ubuntu:latest
root@59047bbc6025:/# dd if =/dev/zero of=/bigfile bs=1M count=1000
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 2.24941 s, 466 MB/s
[root@docker-node1 ~]# docker run -it --name test --device-write-bps /dev/nvme0n1:30M --rm ubuntu:latest
root@69563c8cae3c:/# dd if =/dev/zero of=/bigfile bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 3.33747 s, 31.4 MB/s
(三)容器特权管控
默认情况下,Docker 容器处于受限的权限环境中,避免容器突破隔离层操作宿主机资源;可通过参数精细管控容器特权。
[root@docker-node1 ~]# docker run -it --name test --rm busybox:latest
/
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether d6:ee:cc:4a:b1:d3 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
/
ip: either "local" is duplicate, or "eth0@if41" is garbage
/
/
--privileged 参数会赋予容器几乎与宿主机 root 相同的权限,容器可直接操作宿主机的硬件、磁盘、网络等资源:
[root@docker-node1 ~]# docker run -it --rm --privileged busybox:latest
/
Disk /dev/nvme0n1: 100 GB, 107374182400 bytes, 209715200 sectors
411206 cylinders, 255 heads, 2 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/nvme0n1p1 * 4,4,1 1023,254,2 2048 2099199 2097152 1024M 83 Linux
/dev/nvme0n1p2 1023,254,2 1023,254,2 2099200 10307583 8208384 4008M 82 Linux swap
/dev/nvme0n1p3 1023,254,2 1023,254,2 10307584 209715199 199407616 95.0G 83 Linux
/
3. 精细化权限管控(--cap-add) 不赋予全量特权,仅添加指定的内核能力(Capability),最小化容器权限:
[root@docker-node1 ~]# docker run -it --rm --cap-add CAP_NET_ADMIN busybox:latest
/
/
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether 56:2d:fc :51:34:59 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
/
Link encap:Ethernet HWaddr 56:2D:FC:51:34:59
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:12 errors:0 dropped:0 overruns:0 frame:0 TX packets:3 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1016 (1016.0 B) TX bytes:126 (126.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/
/
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link /ether 56:2d:fc :51:34:59 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever inet 1.2.3.4/24 scope global eth0 valid_lft forever preferred_lft forever
十五、Docker Compose 容器编排工具
(一)基础环境准备 [root@docker-node1 ~]# mkdir /root/admin/
[root@docker-node1 ~]# cd /root/admin/
(二)编辑 compose 配置文件 [root@docker-node1 admin]# vim docker-compose.yml
services:
web:
image: nginx:1.23
ports:
- "80:80"
volumes:
- admin:/data
dbserver:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: admin
(三)命令演示
1. 启动容器(后台运行)
docker compose up:启动 Compose 配置中的所有服务;
-d:后台运行(detach 模式),不加则会前台占用终端;
输出显示两个容器(admin-dbserver-1、admin-web-1)启动成功(命名规则:目录名 - 服务名 - 序号)。
[root@docker-node1 admin]# docker compose up -d
[+] up 2/2 ✔ Container admin-dbserver-1 Started 0.3s ✔ Container admin-web-1 Started 0.3s
2. 查看运行中的容器 [root@docker-node1 admin]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bde022b1fe26 nginx:1.23 "/docker-entrypoint…" About a minute ago Up 2 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp admin-web-1
b6bb2557840f mysql:8.0 "docker-entrypoint.s…" About a minute ago Up 2 seconds 3306/tcp, 33060/tcp admin-dbserver-1
3. 停止并删除容器 / 网络 docker compose down:停止并删除 Compose 管理的容器、默认网络(admin_default);
注意:不会删除数据卷(如需删除卷,需加 -v 参数,如 docker compose down -v);
输出显示容器和网络都被移除。
[root@docker-node1 admin]# docker compose down
[+] down 3/3 ✔ Container admin-web-1 Removed 0.1s ✔ Container admin-dbserver-1 Removed 1.5s ✔ Network admin_default Removed 0.1s
4. 查看 Compose 管理的容器状态 [root@docker-node1 admin]# docker compose ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5. 指定配置文件启动(-f 参数)
-f:指定自定义路径的 Compose 配置文件(默认只识别当前目录的 docker-compose.yml/docker-compose.yaml);
此处从根目录执行命令,通过 -f 指向 admin 目录下的配置文件,实现跨目录启动。
[root@docker-node1 ~]# docker compose -f /root/admin/docker-compose.yml up -d
[+] up 3/3 ✔ Network admin_default Created 0.1s ✔ Container admin-web-1 Started 0.4s ✔ Container admin-dbserver-1 Started 0.2s
6. 停止容器(保留容器,不删除)
docker compose stop:停止运行中的容器,但保留容器本身 (可通过 start 重启);
执行后查看容器状态:Exited (0) 表示正常停止。
[root@docker-node1 admin]# docker compose stop
[+] stop 2/2 ✔ Container admin-dbserver-1 Stopped 1.3s ✔ Container admin-web-1 Stopped 0.1s
7. 启动已停止的容器 重启通过 stop 停止的容器(无需重新创建,快速恢复运行)
[root@docker-node1 admin]# docker compose start
[+] start 2/2 ✔ Container admin-dbserver-1 Started 0.2s ✔ Container admin-web-1 Started 0.3s
8. 重启容器
docker compose restart:重启运行中的容器(先停止,再启动);
重启后通过 docker compose ps 可看到容器恢复运行状态。
[root@docker-node1 admin]# docker compose restart
[+] restart 0/2 ⠼ Container admin-dbserver-1 Restarting 1.4s ⠼ Container admin-web-1 Restarting 1.4s
9. 最终清理(再次 down) 再次执行 down 完成最终清理,移除容器和网络。
[root@docker-node1 admin]# docker compose down
[+] down 3/3 ✔ Container admin-web-1 Removed 0.1s ✔ Container admin-dbserver-1 Removed 1.4s ✔ Network admin_default Removed 0.1s
(四)总结 命令 作用 docker compose up -d后台启动所有服务(创建容器 + 网络) docker compose down停止并删除容器、网络(保留数据卷) docker compose stop停止容器(保留容器,不删除) docker compose start启动已停止的容器 docker compose restart重启运行中的容器 docker compose ps查看 Compose 管理的容器状态 docker compose -f指定非默认路径的配置文件
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online