一、Docker介绍
- Docker 可以把你的程序 + 依赖库 + 配置 + 环境 全部打包成一个镜像(Image) ,然后在任何装了 Docker 的机器上一模一样地跑起来。
Docker 是一款开源的容器化技术工具,核心价值是解决软件运行环境不一致的问题:
它将应用程序及其依赖(库、配置、运行环境等)打包成标准化的镜像(Image),这个镜像可理解为 "可执行的安装包";
基于镜像启动的容器(Container) 是轻量级、隔离的运行实例,能在任何安装了 Docker 的机器(Windows/Linux/Mac)上无差异运行;
相比虚拟机,Docker 无需模拟完整操作系统,启动快(秒级)、资源占用少,是目前 DevOps、微服务部署的核心工具。
**一句话解释:**Docker 就是一个「轻量、便携、统一」的容器工具,用来打包、运行软件。
3. 最形象的比喻
传统部署:每台电脑环境不一样,程序经常「在我这能跑,在你那就崩」。
Docker:像一个集装箱。
不管船(服务器)是什么型号
不管货(程序)是什么
只要装进集装箱,到哪都能直接运行
- 核心概念
-
镜像(Image):程序的「安装包 / 模板」。
-
容器(Container):镜像跑起来后的实例,一个隔离的小环境。
-
Dockerfile:用来告诉 Docker 怎么打包镜像。
-
仓库(Registry):存放镜像的地方(比如 Docker Hub)。
- 运维 / 开发为什么爱用它?
-
环境统一:开发、测试、线上完全一样
-
秒级启动:比虚拟机快得多
-
轻量:不占太多资源
-
可移植:一次打包,到处运行
二、部署docker
在 RHEL 9.6(红帽企业版 Linux 9.6) 系统中部署 Docker CE(社区版)的完整步骤。
(一)配置软件仓库
作用:创建自定义的 Docker 仓库配置文件(/etc/yum.repos.d/docker.repo)
bash
#利用阿里云部署软件仓库
[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 签名校验。
(二)刷新本地软件包缓存
作用:从配置的阿里云仓库拉取最新的软件包元数据(如版本、依赖关系),存入本地缓存,避免后续安装时重复下载。
bash
[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 相关包。
bash
[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 主程序
bash
[root@docker-node1 ~]# dnf install docker-ce -y
(五)修改 Docker 服务启动配置
作用:修改 Docker 服务的启动参数配置文件(docker.service 是 systemd 管理 Docker 服务的核心文件)。
bash
[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 容器网络正常工作的核心依赖。
bash
[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 规则生效的问题。
bash
[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/ 目录下的内核参数配置,使修改立即生效(无需重启系统)。
bash
[root@docker-node1 ~]# sysctl --system
(八)启动并设置 Docker 开机自启
bash
[root@docker-node1 ~]# systemctl enable --now docker
(九)验证是否部署成功
执行 systemctl status docker 可查看 Docker 服务状态(显示 active (running) 即为成功)。

执行 docker version 可查看 Docker 客户端 / 服务端版本,验证安装完整性。

三、docker的常规使用方法
(一)配置docker加速器
作用:Docker 默认从官方镜像仓库拉取镜像,速度较慢,配置国内加速器可以大幅提升镜像下载速度。
bash
# 1. 创建/编辑Docker守护进程配置文件daemon.json
[root@docker-node1 ~]# cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://docker.1ms.run"] # 指定加速器地址
}
EOF
# 2. 重启Docker服务,使配置生效
[root@docker-node1 ~]# systemctl restart docker
# 3. 验证配置是否生效
[root@docker-node1 ~]# docker info
# 输出中会显示Registry Mirrors: https://docker.1ms.run/,说明配置成功
(二)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 "备注" 容器名 新镜像名[:标签] |
将容器的修改提交为新镜像 |
bash
#镜像查看
[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 timinglee/game2048:latest
#删除镜像
[root@docker-node1 ~]# docker rmi timinglee/mario:latest
#导入镜像
[root@docker-node1 ~]# docker load -i game2048-latest.tar
#修改镜像
[root@docker-node1 ~]# docker commit -m "add file" test busybox-file:latest
sha256:31a32089d241d025a5a54f144f15319cc6fb55be1b41d049f8905a472d5a028e
2. 容器(Container)操作命令
容器是镜像的运行实例,是动态的、可交互的环境。
- 容器创建 / 运行
| 命令 | 作用 |
|---|---|
docker run -d --name 容器名 镜像名[:标签] |
后台运行容器(-d:守护进程模式) |
docker run -it --name 容器名 镜像名[:标签] |
交互模式运行容器(-i:交互;-t:终端) |
bash
#运行镜像
[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 格式) |
bash
#查看运行容器
[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:强制删除运行中的容器) |
bash
[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 #退出交互容器不对其停止
/ # [ctrl]+[p]+[q] #按键
- 容器内操作
| 命令 | 作用 |
|---|---|
docker exec 容器名 命令 |
在运行中的容器执行非交互命令 |
docker exec -it 容器名 终端 |
在运行中的容器执行交互命令 |
docker cp 容器名:路径 本地路径 |
从容器复制文件到本地 |
docker cp 本地路径 容器名:路径 |
从本地复制文件到容器 |
bash
#在已经运行的容器中执行指定命令
[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用于编写镜像构建的指令集。
bash
#建立构建目录
[root@docker-node1 ~]# mkdir docker
[root@docker-node1 ~]# cd docker/
#编写构建规则文件
[root@docker-node1 docker]# vim Dockerfile
(二)镜像构建是用到的参数
1. FROM:指定基础镜像
-
FROM是 Dockerfile 的必备指令,指定构建镜像的基础镜像; -
latest是镜像标签,代表最新版本。
bash
FROM busybox:latest
2. COPY:复制文件到镜像内
-
COPY 源文件 目标路径:将宿主机构建上下文内的timinglee文件,复制到镜像的/root目录下; -
docker build -t 镜像名:标签 构建上下文:核心构建命令,.必须加,代表当前目录是构建上下文(Docker 会读取该目录下的文件供构建使用)。
bash
# 先创建本地文件
[root@docker-node1 docker]# echo timinglee > timinglee
[root@docker-node1 docker]# cat timinglee
timinglee
# 修改Dockerfile
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY timinglee /root
# 构建镜像(-t 打标签,. 代表构建上下文为当前目录)
[root@docker-node1 docker]# docker build -t timinglee: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 timinglee /root 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => exporting manifest sha256:3e240075ea92a386ccc7b8249faf4fbc049465ac3e490ddb9c0b6c759a35a2be 0.0s
=> => exporting config sha256:16a6f0015605d0df6a11f1c609afba2c28bdf3d984305922b440e52cd76f9dc2 0.0s
=> => exporting attestation manifest sha256:74b85b3b7cbdaa72964271d4d7c0fc371c7e267bbf6070df2628f 0.0s
=> => exporting manifest list sha256:0a7e32bc130bf9dbfc457442d8bc653987c1a642f86858f6bc233dc120d6 0.0s
=> => naming to docker.io/library/timinglee:v1 0.0s
=> => unpacking to docker.io/library/timinglee:v1
3. LABEL:添加镜像元数据(标签)
LABEL KEY=VALUE:给镜像添加自定义元数据(如作者、版本、用途),方便后续管理 / 筛选镜像。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY timinglee /root
LABEL creater=lee # 新增标签:创建者=lee
[root@docker-node1 docker]# docker build -t lee:v1 . #不要忘记最后有一个点.
[+] Building 0.4s (7/7) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 96B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 30B 0.0s
=> [1/2] FROM docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30 0.0s
=> => resolve docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30 0.0s
=> CACHED [2/2] COPY timinglee /root 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => exporting manifest sha256:777e919c6ad4381f2fd658361f7c246f91cf4c17bee90b0aef343df301a1a388 0.0s
=> => exporting config sha256:4dae93c1e2bb2c425c091bfbb0eddc9490d7fef50f96116748fa83dab2a7450b 0.0s
=> => exporting attestation manifest sha256:efbb0ccb93a51f829eeee29ca60610bc392ebac765498bcbd08e260c76 0.0s
=> => exporting manifest list sha256:d539420bc3bac49eefebe3efb6cd52a23a81113615714247e96e5bbc3665a0d5 0.0s
=> => naming to docker.io/library/lee:v1 0.0s
=> => unpacking to docker.io/library/lee:v1 0.0s
4. ADD:复制(支持解压 / 远程 URL)
bash
# 1. 普通复制(和COPY一致)
[root@docker-node1 docker]# echo lee > lee
# 修改Dockerfile
root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
COPY timinglee /root
ADD lee /root
# 构建镜像
[root@docker-node1 docker]# docker build -t lee:v2 .
ADD 功能 > COPY:
-
普通文件复制和
COPY一致; -
若源文件是压缩包(tar.gz/tar.bz2 等),
ADD会自动解压到目标路径(COPY仅复制压缩包,不解压); -
支持远程 URL(如
ADD https://xxx/file.tar.gz /tmp)。
bash
# 2. 解压压缩包(COPY不支持)
[root@docker-node1 docker]# tar zcf bin.tar.gz /bin # 打包/bin目录
tar: 从成员名中删除开头的"/"
[root@docker-node1 docker]# ls
bin.tar.gz Dockerfile lee timinglee
# 修改Dockerfile
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
COPY bin.tar.gz /root # 仅复制压缩包,不解压
ADD bin.tar.gz /mnt # 复制+自动解压到/mnt
# 构建并测试
[root@docker-node1 docker]# docker build -t lee:v3 .
[root@docker-node1 docker]# docker run -it --name test --rm lee:v3
/ # ls
bin dev etc home lib lib64 mnt proc root sys tmp usr var
/ # ls /root/ # COPY的结果:仅压缩包
bin.tar.gz
/ # ls /mnt/ # ADD的结果:解压后的bin目录
bin
/ #
5. ENV:设置环境变量
-
ENV 变量名=值:在镜像内设置环境变量,后续指令(RUN/CMD/ENTRYPOINT)可通过$变量名引用; -
容器运行时也能继承该环境变量。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=timinglee # 定义环境变量NAME=timinglee
RUN ["/bin/sh","-c", "touch /root/$NAME" ] # 使用变量:创建/root/timinglee文件
[root@docker-node1 docker]# docker build -t lee:v4 .
6. EXPOSE:声明容器暴露的端口
-
EXPOSE 端口:仅声明 容器要暴露的端口(告诉使用者该容器会用这个端口),不自动映射 (运行容器时需加-p 宿主机端口:容器端口才会映射); -
主要作用是文档化,方便协作(比如开发者知道容器要映射 8080 端口)。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=timinglee
EXPOSE 8080 # 声明暴露8080/tcp端口
RUN ["/bin/sh","-c","touch /root/$NAME" ]
[root@docker-node1 docker]# docker build -t lee:v5 .
# 查看镜像历史(验证EXPOSE指令)
[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 # buildkit 0B buildkit.dockerfile.v0
<missing> 2 minutes ago EXPOSE [8080/tcp] 0B buildkit.dockerfile.v0
<missing> 2 minutes ago ENV NAME=timinglee 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/下的随机目录; -
作用:避免容器内数据随容器销毁而丢失(数据存在宿主机卷中),即使容器删除,卷数据仍在。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=timinglee
EXPOSE 8080
VOLUME "/mnt" # 定义匿名卷:容器的/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
# 宿主机查看挂载信息(grep过滤Mounts段)
[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
/ # ls /mnt/
lee1 lee2 lee3 lee4 lee5
8. WORKDIR:设置工作目录
-
WORKDIR 路径:设置容器启动后的默认工作目录(类似cd命令); -
后续的 RUN/CMD/ENTRYPOINT 指令都会在该目录下执行。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL Creater=lee
ENV NAME=timinglee
EXPOSE 8080
VOLUME "/mnt"
RUN ["/bin/sh","-c", "touch /root/$NAME" ]
WORKDIR "/mnt" # 设置容器启动后的默认工作目录
[root@docker-node1 docker]# docker build -t lee:v7 .
# 运行容器:默认进入/mnt目录
[root@docker-node1 docker]# docker run -it --name test --rm lee:v7
/mnt # # 提示符显示当前目录是/mnt
9. CMD:容器启动默认命令(可被覆盖)
-
CMD:定义容器启动时默认执行的命令; -
关键特性:
docker run时若指定自定义命令,会覆盖CMD的默认命令; -
注意:exec 格式中,直接写
CMD ["/bin/echo","$NAME"]不会解析环境变量(输出$NAME),需通过/bin/sh -c包裹(如CMD ["/bin/sh","-c","echo $NAME"])。
写法 1:shell 格式
bash
[root@docker-node1 docker]# vim Dockerfile
.....
VOLUME "/mnt"
RUN ["/bin/sh","-c","touch /root/$NAME" ]
WORKDIR "/mnt"
CMD echo $NAME # shell格式
[root@docker-node1 docker]# docker build -t lee:v8 . # shell格式
写法 2:exec 格式(推荐)
bash
[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"] # exec格式(JSON数组)
[root@docker-node1 docker]# docker build -t lee:v9 . # exec格式
bash
# 正常运行:执行CMD命令,输出timinglee
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8
timinglee
[root@docker-node1 docker]# docker run -it --name test --rm lee:v9
timinglee
# 覆盖CMD:运行时指定命令,会替换默认CMD
[root@docker-node1 docker]# docker run -it --name test --rm lee:v9 echo haha
haha
10. ENTRYPOINT:容器启动固定命令(不可被覆盖)
-
ENTRYPOINT:定义容器启动时必须执行的核心命令 ,运行容器时指定的自定义命令无法覆盖它; -
场景:需要容器固定执行某个核心逻辑(如服务启动),不允许用户随意替换。
bash
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
...
RUN ["/bin/sh","-c","touch /root/$NAME" ]
WORKDIR "/mnt"
ENTRYPOINT ["/bin/sh","-c", "echo $NAME"] # 替换CMD为ENTRYPOINT
[root@docker-node1 docker]# docker build -t lee:v8 .
# 正常运行:输出timinglee
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8
timinglee
# 尝试覆盖(无效):仍执行ENTRYPOINT的命令,输出timinglee
[root@docker-node1 docker]# docker run -it --name test --rm lee:v8 echo haha
timinglee

(三)总结
| 指令 | 核心作用 | 关键特性 |
|---|---|---|
| FROM | 指定基础镜像 | 必备,所有指令的基础 |
| COPY | 复制宿主机文件到镜像 | 仅复制,不解压 |
| ADD | 复制文件到镜像 | 支持解压压缩包、远程 URL |
| LABEL | 添加镜像元数据 | 方便镜像管理 / 筛选 |
| ENV | 设置环境变量 | 镜像 / 容器内可引用 |
| EXPOSE | 声明容器暴露端口 | 仅声明,不自动映射 |
| VOLUME | 定义数据卷 | 持久化数据,容器销毁数据不丢 |
| WORKDIR | 设置工作目录 | 后续指令的默认目录 |
| CMD | 容器启动默认命令 | 可被docker run命令覆盖 |
| ENTRYPOINT | 容器启动固定命令 | 不可被覆盖,核心逻辑固定 |
五、构建CentOS可用仓库
(一)基础环境准备:拉取并验证 CentOS 7 镜像
1. 拉取官方 CentOS 7 镜像
命令执行后,Docker 从官方仓库下载 CentOS 7 镜像,并输出下载摘要、状态等信息,确认镜像拉取成功。
bash
[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
- 启动容器验证镜像信息
bash
[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 #1 SMP PREEMPT_DYNAMIC Fri Apr 4 10:41:31 EDT 2025 x86_64 x86_64 x86_64 GNU/Linux
[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 默认 yum 仓库配置文件
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. 清理本地文件
删除当前目录下无关的bin.tar.gz、lee、timinglee等文件,仅保留后续构建需要的Dockerfile,避免构建时引入冗余内容。
bash
[root@docker-node1 docker]# ls
bin.tar.gz Dockerfile lee timinglee
# 删掉不需要的文件
[root@docker-node1 docker]# rm -fr bin.tar.gz
[root@docker-node1 docker]# ls
Dockerfile lee timinglee
[root@docker-node1 docker]# rm -rf lee timinglee
[root@docker-node1 docker]# ls
Dockerfile
2. 清理历史镜像
-
先用
docker images | awk '/lee/{print $1}'筛选出名称包含lee的镜像(为之前实验残留的镜像); -
再通过
docker images | awk '/\<lee\>/{system("docker rmi " $1)}'批量删除这些镜像,释放磁盘空间,避免与新构建镜像冲突。
bash
[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
timinglee/game2048:latest
timinglee/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:d539420bc3bac49eefebe3efb6cd52a23a81113615714247e96e5bbc3665a0d5
Untagged: lee:v2
Deleted: sha256:fdc875ae6d05901d0520326bd2eb36a6cf62293ba47dc69ca6b10438a2c43225
Untagged: lee:v3
Deleted: sha256:fa48ac3a5962a85d0b9de195dcd5a2014230be2982531eba340d3f65a91fd663
Untagged: lee:v4
Deleted: sha256:67e42523577db91343b4c4e30cdf931bfdbe742f616ae042e8e4c16d0888031a
Untagged: lee:v5
Deleted: sha256:28c6e9062b52837f3ef7d3ecbe812e07a5ea486f2e33da1f79f5d7056663872c
Untagged: lee:v6
Deleted: sha256:4f82531046f60eeb05658b56fd09cc39b05e16ef1a67ce66d3efe83c56610ddf
Untagged: lee:v7
Deleted: sha256:37193dec64e2a7b7fdd08829e20b566d4a308ceeb6c27a180e59e08f0da7c963
Untagged: lee:v8
Deleted: sha256:15e1326d0222aa5d27309d3fdfbdf8d6d2575df5db1446d3fb19a98429d9f1dc
Untagged: lee:v9
Deleted: sha256:943a7813b8bac98a7b2c6032a349a38b54425123ede588bbbc800dfc53597d87
(三)构建自定义 CentOS 7 镜像
1. 清空默认 yum 仓库配置
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7 # 基于官方CentOS 7镜像作为基础镜像
LABEL Creater=lee # 添加镜像标签(标注创建者,可选)
RUN ["/bin/bash","-c","rm -fr /etc/yum.repos.d/*"] # 清空默认yum仓库配置文件
bash
# 构建镜像
[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
# 确认默认repo文件已被清空(目录为空)
[root@d7addd9a7f86 /]# ls /etc/yum.repos.d/
2. 添加阿里云 CentOS 7 镜像源
编辑Dockerfile,新增COPY指令,将本地自定义 repo 文件复制到容器内
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7
LABEL Creater=lee
RUN ["/bin/bash","-c","rm -fr /etc/yum.repos.d/*"]
COPY centos7.repo /etc/yum.repos.d/centos7.repo
创建自定义centos7.repo文件
bash
[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
重新构建镜像
bash
[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 能否正常安装软件(核心目的:验证自定义仓库可正常使用)
bash
[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 源码包;
bash
[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 端口、定义数据卷、设置启动命令;
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=lee
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 可访问。
bash
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/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": "b227744cfc0626f7fa11fe7f4e182ae823385d64f45bce8bad7779b59d870f9d",
"EndpointID": "5417be0037de81478c9cfd579cff2fed90106021c8b22943885e8e27a9026e8b",
"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. 优化点
bash
把产生的临时文件全部清理
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=lee
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 #优化地方,删除Nginx源码目录
RUN yum clean all #优化地方,清理yum缓存
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 个镜像层,抵消了清理文件的体积优化效果。
bash
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB #没有改进,还是一样的,是因为改进后多了两层
(三)第二次优化:缩减镜像层
1. 核心思路------层越少,构建的软件越小
第一次优化未成功,是因为多了两个镜像层,那么我就把镜像层缩减:Dockerfile 中合并多个 RUN 指令为一个 (通过&&串联命令),减少镜像层数,同时完成编译 + 清理操作,避免分层冗余。
2. 优化后的 Dockerfile
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos-7:repo
LABEL Creater=lee
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;"]
# 优化后构建为v2
[root@docker-node1 docker]# docker build -t webserver:v2 .
3. 结果
构建出webserver:v2镜像体积降至426MB(内容大小 110MB),相比 v1 明显减小,验证了「减少镜像层」的优化效果。
bash
# 对比v1和v2
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/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
1048a0785474d756fc497e9cb994615a9c5e2ad780b1a49c69cfca6549f95155
[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 结构
bash
[root@docker-node1 docker]# docker rm -f webserver #先把之前的删掉
webserver
[root@docker-node1 docker]# vim Dockerfile
# 阶段1:编译构建(命名为lee)
FROM centos-7:repo AS lee
LABEL Creater=lee
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
# 阶段2:最终运行镜像
FROM centos-7:repo
COPY --from=lee /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),进一步精简了镜像(舍弃了编译阶段的依赖、工具等)。
bash
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/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 运行所需的二进制文件、系统库、配置文件,舍弃所有无关内容。
bash
# 把nginx-1.23.tar.gz和debian11.tar.gz放到/root/目录下
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/mario:latest 7758988210df 298MB 73.7MB
webserver:v1 eb4c204aceb8 510MB 132MB
webserver:v2 4961959ef185 426MB 110MB
webserver:v3 28390cffd186 308MB 79MB
2. 优化后的 Dockerfile
bash
[root@docker-node1 docker]# vim Dockerfile
# 阶段1:基于nginx:1.23提取运行依赖
FROM nginx:1.23 AS lee
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
# 阶段2:基于极简镜像构建最终镜像
FROM gcr.io/distroless/base-debian11
COPY --from=lee /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx", "-g", "daemon off;"]
3. 结果
构建出webserver:v4镜像体积仅67.9MB(内容大小 28.4MB),相比初始的 v1(510MB)缩减了 86% 以上,是最优的优化方案。
bash
[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
timinglee/game2048:latest 8a34fb9cb168 77.2MB 17.8MB
timinglee/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 #比之前小了
测试
bash
[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 运行环境一致:
bash
# 复制内核模块配置:
[root@docker-node1 docker]# scp /etc/modules-load.d/docker_mod.conf root@172.25.254.20:/etc/modules-load.d/docker_mod.conf
Warning: Permanently added '172.25.254.20' (ED25519) to the list of known hosts.
docker_mod.conf 100% 13 14.4KB/s 00:00
# 复制系统参数配置
[root@docker-node1 docker]# scp /etc/sysctl.d/docker.conf root@172.25.254.20:/etc/sysctl.d/docker.conf
docker.conf 100% 103 277.4KB/s 00:00
# 复制 Docker yum 源
[root@docker-node1 docker]# scp /etc/yum.repos.d/docker.repo root@172.25.254.20:/etc/yum.repos.d/docker.repo
docker.repo 100% 113 159.2KB/s 00:00
- 加载内核模块
在 node2 上加载br_netfilter(Docker 网络依赖模块):modprobe -a br_netfilter
bash
#加载模块
[root@docker-node2 ~]# cat /etc/modules-load.d/docker_mod.conf
br_netfilter
[root@docker-node2 ~]# modprobe -a br_netfilter
- 安装 Docker
在 node2 上通过 dnf 安装 docker-ce:dnf install docker-ce -y
bash
[root@docker-node2 ~]# dnf install docker-ce -y
(二)部署 Registry 镜像仓库(node1 操作)
- 导入 Registry 镜像 :将
registry.tar包上传到 node1,通过docker load -i registry.tar加载镜像(最终得到registry:latest镜像)。
bash
[root@docker-node1 docker]# docker load -i registry.tar
Loaded image: registry:latest
- 查看 Registry 镜像信息:
docker images:确认镜像已加载,Registry 镜像大小约 77.3MB;
bash
[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 端口(镜像仓库的默认通信端口)。
bash
[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 # buildkit 4.1kB buildkit.dockerfile.v0
<missing> 6 weeks ago EXPOSE map[5000/tcp:{}] 0B buildkit.dockerfile.v0
- 启动 Registry 容器
bash
[root@docker-node1 docker]# docker run -d -p 5000:5000 --restart=always --name registry registry:latest
192c6b85055bac37f778533f5734dfdd9bd20cece020c54bc40798c6e63b1a59
-
-d:后台运行容器; -
-p 5000:5000:将主机 5000 端口映射到容器 5000 端口; -
--restart=always:容器异常退出时自动重启; -
--name registry:给容器命名为 registry。
- 查看容器挂载目录
bash
[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(仓库端)配置
bash
[root@docker-node1 docker]# vim /etc/docker/daemon.json
{
"insecure-registries" : ["http://172.25.254.10:5000"]
}
# 重启 Docker 使配置生效
[root@docker-node1 docker]# systemctl restart docker
2. 上传镜像到私有仓库
bash
# 给本地镜像打标签
[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. 配置非加密仓库
bash
[root@docker-node1 docker]# vim /etc/docker/daemon.json
{
"insecure-registries" : ["http://172.25.254.10:5000"]
}
[root@docker-node2 ~]# systemctl restart docker
2. 验证配置
bash
[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. 拉取镜像
bash
[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 非加密配置
清理之前可能存在的非加密仓库配置,避免冲突。
bash
# 清空/重置daemon.json(删除之前非加密仓库的配置)
[root@docker-node1 ~]# vim /etc/docker/daemon.json
[root@docker-node2 ~]# > /etc/docker/daemon.json
# 重启Docker使配置生效
[root@docker-node1 ~]# systemctl restart docker
[root@docker-node2 ~]# systemctl restart docker
(二)生成 TLS 证书(解决 OpenSSL 版本兼容问题)
- 问题背景
CentOS7 自带的 OpenSSL 版本较低,直接使用addext参数会报错,因此改用配置文件方式生成证书。
- 操作步骤
bash
# 创建证书存储目录
[root@docker-node1 ~]# mkdir -p /etc/docker/certs
# 编写证书配置文件san.cnf(定义证书主体和备用名称)
[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 = reg.timinglee.org # 证书主域名(仓库域名)
[v3_req]
subjectAltName = DNS:reg.timinglee.org # 证书备用域名(和主域名一致)
EOF
# 生成RSA 4096位的证书(.key私钥 + .crt公钥证书)
[root@docker-node1 certs]# openssl req -newkey rsa:4096 \
> -nodes \ # 私钥不加密
> -keyout timinglee.org.key \# 输出私钥文件
> -x509 -days 365 \ # 生成自签名证书,有效期365天
> -out timinglee.org.crt \ # 输出证书文件
> -config san.cnf \ # 指定配置文件(兼容旧版OpenSSL)
> -sha256 # 哈希算法
- 验证证书
查看证书详情,确认subjectAltName等关键信息是否正确。
bash
[root@docker-node1 certs]# openssl x509 -in /etc/docker/certs/timinglee.org.crt -noout -text

(三)启动加密的 Docker Registry 仓库
-
--restart=always:Docker 重启后自动启动仓库容器; -
-v /opt/registry:/var/lib/registry:将仓库的镜像数据持久化到宿主机/opt/registry; -
-e REGISTRY_HTTP_TLS_*:指定 TLS 证书和私钥,开启 HTTPS 加密。
bash
[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 \ # 仓库监听443端口(HTTPS默认端口)
> -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/timinglee.org.crt \ # 证书路径
> -e REGISTRY_HTTP_TLS_KEY=/certs/timinglee.org.key \ # 私钥路径
> registry # 仓库镜像
(四)配置域名解析(本地 hosts)
将仓库域名reg.timinglee.org解析到仓库所在主机(docker-node1,IP:172.25.254.10),避免 DNS 解析问题。
bash
[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 reg.timinglee.org #添加这个
[root@docker-node2 ~]# vim /etc/hosts #20也添加
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 reg.timinglee.org #添加这个
(五)解决证书信任问题(推送镜像失败的修复)
1. 初始推送失败原因
Docker 客户端默认不信任自签名证书,推送时会报x509: certificate signed by unknown authority。
bash
# 失败原因:我的docker没有证书,之前生成的证书是给registry生成的
[root@docker-node1 ~]# docker push reg.timinglee.org/webserver:v4
The push refers to repository [reg.timinglee.org/webserver]
577c8ee06f39: Waiting
af5aa97ebe6c: Waiting
5342a2647e87: Waiting
bbb6cacb8c82: Waiting
1a73b54f556b: Waiting
2388d21e8e2b: Waiting
6a575081cb9a: Waiting
ac805962e479: Waiting
c048279a7d9f: Waiting
8451c71f8c1e: Waiting
6835249f577a: Waiting
7fe3777149de: Waiting
9ed498e122b2: Waiting
4d049f83d9cf: Waiting
2a92d6ac9e4f: Waiting
24aacbf97031: Waiting
failed to do request: Head "https://reg.timinglee.org/v2/webserver/blobs/sha256:2a92d6ac9e4fcc274d5168b217ca4458a9fec6f094ead68d99c77073f08caac1": tls: failed to verify certificate: x509: certificate signed by unknown authority
2. 修复操作
bash
# 创建证书信任目录(格式:/etc/docker/certs.d/仓库域名/)
[root@docker-node1 ~]# mkdir /etc/docker/certs.d/reg.timinglee.org/ -p
# 复制证书到信任目录(重命名为ca.crt,Docker会自动识别)
[root@docker-node1 ~]# cp /etc/docker/certs/timinglee.org.crt /etc/docker/certs.d/reg.timinglee.org/ca.crt
# 重启Docker加载证书
[root@docker-node1 ~]# systemctl restart docker
3. 验证推送
bash
# 给镜像打标签(仓库域名/镜像名:版本)
[root@docker-node1 ~]# docker tag webserver:v4 reg.timinglee.org/webserver:v4
# 推送镜像到私有仓库(此时证书信任,推送成功)
[root@docker-node1 ~]# docker push reg.timinglee.org/webserver:v4
# 验证仓库镜像列表
[root@docker-node1 ~]# curl -k https://172.25.254.10/v2/_catalog
{"repositories":["webserver"]} # 显示已推送的webserver镜像
(六)添加用户认证(限制仓库访问权限)
1. 安装工具 + 创建认证文件
bash
# 安装htpasswd(生成密码文件的工具)
[root@docker-node1 ~]# dnf install httpd-tools -y
# 创建认证目录
[root@docker-node1 ~]# mkdir /etc/docker/auth
# 生成用户密码文件(-B:bcrypt加密,-c:创建新文件,用户lee,密码手动输入)
[root@docker-node1 ~]# htpasswd -Bc /etc/docker/auth/htpasswd lee
2. 重启带认证的仓库容器
bash
# 先删除旧仓库容器
[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/timinglee.org.crt \
> -e REGISTRY_HTTP_TLS_KEY=/certs/timinglee.org.key \
> # 以下是认证新增参数
> -v /etc/docker/auth:/auth \ # 挂载认证目录
> -e "REGISTRY_AUTH=htpasswd" \ # 启用htpasswd认证
> -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ # 认证域(提示信息)
> -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ # 密码文件路径
> registry
3. 认证验证
bash
# 未登录推送(失败,提示需要认证)
[root@docker-node1 ~]# docker push reg.timinglee.org/webserver:v4
push access denied, repository does not exist or may require authorization: authorization failed: no basic auth credentials
# 登录仓库(输入用户lee和密码)
[root@docker-node1 ~]# docker login reg.timinglee.org -u lee
Login Succeeded
# 登录后推送(成功)
[root@docker-node1 ~]# docker push reg.timinglee.org/webserver:v4
# 其他节点(docker-node2)访问:先复制证书+登录
[root@docker-node1 ~]# scp -r /etc/docker/certs.d/ root@172.25.254.20:/etc/docker/certs.d
[root@docker-node2 ~]# docker login reg.timinglee.org -u lee
[root@docker-node2 ~]# docker pull reg.timinglee.org/webserver:v4 # 拉取成功
(七)总结
-
加密:通过自签名 TLS 证书实现仓库 HTTPS 加密传输,解决 OpenSSL 版本兼容问题;
-
信任 :Docker 客户端需将证书放到
/etc/docker/certs.d/仓库域名/ca.crt才能信任自签名证书; -
认证:通过 htpasswd 实现用户名密码认证,限制仓库的推送 / 拉取权限;
-
跨节点访问:其他节点需同步证书并登录,才能访问私有仓库。
九、Harbor 仓库构建
(一)Docker 版本降级
Harbor 对 Docker 版本有兼容性要求,我现在的docker版本29太高,不支持harbor,所以我要给docker进行降级处理。
bash
# 停止当前Harbor相关容器(若已有部署)
[root@docker-node1 harbor]# docker compose down
# 卸载现有29版本Docker
[root@docker-node1 ~]# dnf remove docker-ce -y
# 安装兼容的28版本(3:28.5.2-1.el9为具体版本号,适配CentOS 9)
[root@docker-node1 ~]# dnf install docker-ce-3:28.5.2-1.el9 -y
# 修改Docker服务配置,启用iptables(确保容器网络转发正常)
[root@docker-node1 ~]# vim /lib/systemd/system/docker.service
# 关键配置行:添加--iptables=true
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
# 重新加载系统服务配置并启动Docker
[root@docker-node1 ~]# systemctl daemon-reload
[root@docker-node1 ~]# systemctl enable --now docker.service
(二)Harbor 部署准备
1. 目录与证书准备
bash
# 创建数据目录,存放证书(Harbor需要HTTPS证书)
[root@docker-node1 ~]# mkdir /data/
[root@docker-node1 ~]# cp -rp /etc/docker/certs /data/
2. 本地域名解析
bash
[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 reg.timinglee.org # Harbor访问域名(自定义)
3. Harbor 安装包解压与配置
bash
[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: reg.timinglee.org # Harbor访问域名(与hosts解析一致)
certificate: /data/certs/timinglee.org.crt # HTTPS证书路径
private_key: /data/certs/timinglee.org.key # 证书私钥路径
harbor_admin_password: lee # Harbor管理员密码(web界面/登录用)
# 执行安装脚本
[root@docker-node1 harbor]# ./install.sh
(三)Harbor 基本操作:启动 / 停止 / 登录 / 镜像上传
1. 容器管理
bash
# 停止Harbor容器
[root@docker-node1 harbor]# docker compose down
# 启动Harbor容器(后台运行)
[root@docker-node1 harbor]# docker compose up -d
2. 登录 Harbor 仓库
bash
[root@docker-node1 harbor]# docker login reg.timinglee.org
Authenticating with existing credentials... [Username: lee]
i Info → To login with a different account, run 'docker logout' followed by 'docker login'
Stored credentials invalid or expired
Username (lee): admin
Password: #密码:lee(配置文件中设置)
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. 镜像打标签并上传
bash
# 示例1:上传到Harbor默认的library项目
[root@docker-node1 harbor]# docker tag busybox:latest reg.timinglee.org/library/busybox:latest
[root@docker-node1 harbor]# docker push reg.timinglee.org/library/busybox:latest
# 示例2:自定义镜像标签上传
[root@docker-node1 harbor]# docker tag busybox:latest reg.timinglee.org/library/webserver:v1
[root@docker-node1 harbor]# docker push reg.timinglee.org/library/webserver:v1
(四)Harbor 图形化界面操作
访问地址:http://172.25.254.10/harbor(或https://reg.timinglee.org/harbor);
登录账号:admin,密码:lee;

创建自定义项目(如timinglee):若未勾选 "公开",该项目下的镜像需登录后才能下载;

上传自定义项目镜像:
bash
# 上传,之前我们创建了timinglee,可以使用timinglee进行上传
[root@docker-node1 ~]# docker tag busybox:latest reg.timinglee.org/timinglee/busybox:latest
[root@docker-node1 ~]# docker push reg.timinglee.org/timinglee/busybox:latest

(五)多节点配置:另一台服务器(docker-node2)使用 Harbor 仓库
1. 同步 Docker 版本(与 docker-node1 一致)
bash
[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 镜像加速器
bash
# 编辑Docker守护进程配置文件
[root@docker-node2 ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://reg.timinglee.org"] # 指向Harbor域名
}
# 配置本地域名解析(与docker-node1一致)
[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 reg.timinglee.org
[root@docker-node2 ~]# systemctl restart docker
# 验证加速器是否生效
[root@docker-node2 ~]# docker info
Registry Mirrors:
https://reg.timinglee.org/
3. 从 Harbor 下载镜像
bash
# 下载timinglee项目下的busybox镜像
[root@docker-node2 ~]# docker pull timinglee/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 增强版镜像
bash
# 从本地busyboxplus.tar镜像包加载镜像
[root@docker-node1 ~]# docker load -i busyboxplus.tar
# 启动一个 Nginx 容器
[root@docker-node1 ~]# docker run -d --rm --name webserver --network bridge nginx:1.23
# 进入 webserver 容器的交互式终端
[root@docker-node1 ~]# docker exec -it webserver /bin/bash
root@3e2ed29f9282:/# ip a
bash: ip: command not found
root@3e2ed29f9282:/# exit
exit
# 启动 busybox 容器,共享 webserver 的网络命名空间
[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 环境中,替代从远程仓库拉取镜像,适合无外网或镜像定制化的场景。
bash
[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:指定使用的镜像名称和版本。
bash
[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='lee':设置 MySQL 的 root 用户密码为lee(必须配置,否则 MySQL 容器无法启动); -
--network container:phpmyadmin:核心网络配置 ,让 MySQL 容器共享phpmyadmin容器的网络命名空间;- 效果:两个容器处于同一网络环境,MySQL 容器可被 phpMyAdmin 容器直接访问(无需暴露 MySQL 端口到宿主机,更安全);
-
mysql:8.0:指定使用 MySQL 8.0 版本的镜像。
bash
[root@docker-node1 ~]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORD='lee' --network container:phpmyadmin mysql:8.0
(四)访问 phpmyadmin
通过浏览器访问宿主机(docker-node1)的 IP 地址172.25.254.10
bash
http://172.25.254.10


十二、容器跨主机通信
基于 macvlan 网络驱动搭建跨主机通信环境
(一)设定硬件
-
核心操作 :为两台 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网卡均确认开启混杂模式,为后续网络通信铺路。
bash
[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 地址,让容器像独立的物理设备一样在网络中通信,适合跨主机场景。
bash
[root@docker-node1 ~]# docker network create \
-d macvlan \ # 指定网络驱动为macvlan
--subnet 1.1.1.0/24 \ # 设定子网网段(跨主机容器需在同一网段)
--gateway 1.1.1.1 \ # 设定网关地址
-o parent=eth1 lee # -o:指定驱动专属选项;parent=eth1:绑定到主机的eth1网卡;lee:自定义网络名称
0cad05f438fbae92c0bb5b7119de158c9d81cfd3e588bfc14f980593447a8f9c
bash
[root@docker-node1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5b1ea5c41484 bridge bridge local
6c9d8cbdb795 host host local
0cad05f438fb lee macvlan local # 新增的lee网络,驱动为 macvlan
dbc0eb18cd23 none null local
关键注意点 :两台主机需创建完全相同配置 的
macvlan网络(同网段、同网关、绑定同名称网卡、同网络名称),否则跨主机通信会失败。
(四)测试跨主机 Docker 容器通信
1. 启动容器(指定 macvlan 网络和固定 IP)
docker-node1 上启动容器
-
--network lee:将容器加入lee这个 macvlan 网络; -
--ip 1.1.1.100:为容器指定固定 IP(需在子网1.1.1.0/24范围内); -
--rm:容器退出后自动删除; -
busybox:轻量级 Linux 镜像,用于测试网络通信。
bash
[root@docker-node1 ~]# docker run -it --name busybox --rm --network lee --ip 1.1.1.100 --rm busybox
/ # 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
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
docker-node2 上启动容器
bash
[root@docker-node2 ~]# docker run -it --name busybox --rm --network lee --ip 1.1.1.200 --rm busybox
/ # 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
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. 容器网络信息验证
容器内执行 ip a 可看到:
-
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:
bash
/ # 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 是将宿主机的目录 / 文件直接挂载到容器内,是最基础的数据卷方式,支持读写 / 只读权限控制。
- 清理无用数据卷
bash
[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
- 创建并运行容器(挂载宿主机路径)
bash
[root@docker-node1 ~]# docker run -it --rm --name test \
-v /data:/data \ # 宿主机/data 挂载到容器/data(默认读写)
-v /data1:/data1:ro \ # 宿主机/data1 挂载到容器/data1(ro=只读)
-v /etc/passwd:/passwd:ro # 宿主机/etc/passwd 文件挂载到容器/passwd(只读)
busybox:latest
- 权限认证
bash
/ # touch /data/file # 读写挂载
/ # ls /data
file
/ # touch /data1/file # 只读挂载
touch: /data1/file: Read-only file system
/ # > passwd
sh: can't create passwd: Read-only file system
(二)Docker Managed Volume(Docker 管理卷)
Docker 自行管理的卷(无需指定宿主机路径,由 Docker 统一存储),路径默认在
/var/lib/docker/volumes/[卷名]/_data,更易管理。
- 创建数据卷
bash
[root@docker-node1 ~]# docker volume create timinglee
timinglee
[root@docker-node1 ~]# docker volume ls
DRIVER VOLUME NAME
local timinglee
- 宿主机操作卷内容
直接在宿主机卷目录创建文件,容器内可访问。
bash
[root@docker-node1 volumes]# touch timinglee/_data/file
- 容器挂载(只读)
bash
[root@docker-node1 ~]# docker run -it --rm -v timinglee:/data:ro busybox:latest
/ # touch data/file
touch: data/file: Read-only file system
/ # ls data/
file
- 删除数据卷
bash
[root@docker-node1 ~]# docker volume rm timinglee
timinglee
[root@docker-node1 ~]# docker volume ls
DRIVER VOLUME NAME
(三)数据卷容器(Volumes-from)
专门用于挂载数据卷的 "模板容器",其他容器可通过
--volumes-from继承其所有挂载配置,实现数据共享。
- 创建数据卷容器(data)
容器内可操作/data(创建 timinglee、file 文件),但无法修改/hosts(只读)。
bash
[root@docker-node1 ~]# docker run -it --rm --name data \
-v /etc/hosts:/hosts:ro \ # 挂载宿主机hosts文件(只读)
-v /data:/data \ # 挂载宿主机/data目录(读写)
busybox:latest
/ # cat /etc/host
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 reg.timinglee.org
/ # > /hosts
sh: can't create /hosts: Read-only file system
/ #
/ # touch /data/timinglee
/ # ls /data/
timinglee
/ # touch /data/file
/ # ls /data/
file timinglee
/ # ls /data/
timinglee
- 继承数据卷容器
-
无需重复挂载配置,直接访问
/hosts(和 data 容器内容一致); -
直接访问
/data(可看到 data 容器创建的文件,且能删除 / 修改); -
实现了多个容器共享同一套挂载数据。
bash
[root@docker-node1 ~]# docker run -it --rm --name lee --volumes-from data busybox:latest
/ # ls
bin dev home lib proc sys usr
data etc hosts lib64 root tmp var
/ # 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 reg.timinglee.org
/ # ls /data/
timinglee
/ # ls /data/
file timinglee
/ # rm /data/file
/ #
(四)数据的备份和迁移
基于数据卷实现容器数据的备份(打包)、恢复(解压),适用于数据迁移 / 容灾场景。
1. 准备测试容器(nginx)
-
宿主机
/data挂载到容器 nginx 的网页根目录; -
容器内创建测试文件(timinglee1~10),模拟业务数据。
bash
[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 timinglee
root@aa28e0ff63f0:/usr/share/nginx/html# touch timinglee{1..10}
root@aa28e0ff63f0:/usr/share/nginx/html# ls
index.html timinglee1 timinglee2 timinglee4 timinglee6 timinglee8
timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9
root@aa28e0ff63f0:/usr/share/nginx/html# exit
exit
2. 数据备份(打包)
bash
[root@docker-node1 ~]# docker run -it --rm \
--volumes-from webserver \ # 继承webserver的挂载配置
-v $(pwd):/backup \ # 宿主机当前目录挂载到容器/backup
busybox:latest
/ # ls
backup dev home lib64 root tmp var
bin etc lib proc sys usr
/ # ls /usr/share/nginx/html/
index.html timinglee1 timinglee2 timinglee4 timinglee6 timinglee8
timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9
/ # tar zcf /backup/html.tar.gz /usr/share/nginx/ # 打包nginx数据到宿主机
tar: removing leading '/' from member names
/ # ls
backup dev home lib64 root tmp var
bin etc lib proc sys usr
/ # exit
[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 #这里 phpmyadmin-latest.tar.gz
docker mario.tar.gz
3. 数据恢复(解压)
bash
# 清空宿主机/data(模拟数据丢失)
[root@docker-node1 ~]# rm -fr /data/*
# 重新运行 nginx 容器,并挂载备份目录
[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 timinglee1 timinglee2 timinglee4 timinglee6 timinglee8
timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9
(五)总结
| 数据卷类型 | 特点 | 适用场景 |
|---|---|---|
| bind mount | 直接挂载宿主机路径,灵活 | 宿主机和容器需强耦合的场景 |
| Docker 管理卷 | Docker 统一管理路径,无需关心宿主机位置 | 容器独立管理数据,解耦宿主机 |
| 数据卷容器 | 继承挂载配置,多容器共享 | 集群容器共享数据 |
| 数据备份 / 迁移 | 基于卷打包 / 解压,跨环境迁移 | 数据容灾、迁移、备份 |
十四、Docker 安全架构
(一)更改系统 cgroup(企业中非必须)
核心目的
调整 Linux 系统的 cgroup(控制组)层级模式,从 cgroup2 切换回传统的 cgroup v1 分层架构。cgroup 是 Linux 内核用于限制、记录和隔离进程组资源使用(CPU、内存、IO 等)的机制,Docker 依赖 cgroup 实现容器资源管控。
- 查看当前 cgroup 挂载类型
bash
[root@docker-node1 ~]# mount -t cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
- 修改内核参数
bash
[root@docker-node1 ~]# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
- 必须重启
bash
[root@docker-node1 ~]# reboot
重启系统后,执行 mount -t cgroup 可看到 cgroup v1 被拆分为 cpu、memory、devices 等多个独立子系统挂载,每个子系统对应不同的资源管控维度。
bash
[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 时间(单位:微秒)
bash
[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 占用率验证限制效果。
bash
[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
bash
[root@docker-node1 ~]# docker run -it --rm --name test ubuntu
root@100d1b161ffa:/# dd if=/dev/zero of=/dev/null &
[1] 8
root@100d1b161ffa:/# top
bash
[root@docker-node1 ~]# docker run -it --rm --name test1 ubuntu
root@718fb12421d4:/# dd if=/dev/zero of=/dev/null &
[1] 8
root@718fb12421d4:/# top
bash
# 资源限制
[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可确认内存限制的字节数。
-
bash
[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 限制
bash
#这里是查看正常情况下的速率
[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 容器处于受限的权限环境中,避免容器突破隔离层操作宿主机资源;可通过参数精细管控容器特权。
- 无特权容器(默认)
bash
[root@docker-node1 ~]# docker run -it --name test --rm busybox:latest
/ # 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@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 a a 1.2.3.4/24 eth0@if41
ip: either "local" is duplicate, or "eth0@if41" is garbage
/ # fdisk -l
/ # exit
- 特权容器(--privileged)
--privileged 参数会赋予容器几乎与宿主机 root 相同的权限,容器可直接操作宿主机的硬件、磁盘、网络等资源:
bash
[root@docker-node1 ~]# docker run -it --rm --privileged busybox:latest
/ # fdisk -l
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
/ # exit
3. 精细化权限管控(--cap-add)
不赋予全量特权,仅添加指定的内核能力(Capability),最小化容器权限:
bash
# 开启指定白名单权限
[root@docker-node1 ~]# docker run -it --rm --cap-add CAP_NET_ADMIN busybox:latest
/ # fdisk -l
/ # 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@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
/ # ifconfig
eth0 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)
/ # ip a a 1.2.3.4/24 dev 'eth0'
/ # 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@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容器编排工具
(一)基础环境准备
bash
[root@docker-node1 ~]# mkdir /root/timinglee/ # 创建自定义工作目录
[root@docker-node1 ~]# cd /root/timinglee/ # 进入工作目录
(二)编辑compose配置文件
bash
[root@docker-node1 timinglee]# vim docker-compose.yml # 编辑compose配置文件
services: # 定义要编排的容器服务集合
web: # 第一个服务:命名为web(对应Nginx)
image: nginx:1.23 # 使用的镜像:nginx 1.23版本
ports: # 端口映射:主机80端口 → 容器80端口
- "80:80"
volumes: # 数据卷挂载(此处仅声明卷名lee,未指定主机路径,会创建匿名卷)
- lee:/data
dbserver: # 第二个服务:命名为dbserver(对应MySQL)
image: mysql:8.0 # 使用的镜像:mysql 8.0版本
environment: # 设置容器环境变量(MySQL必填:root密码)
MYSQL_ROOT_PASSWORD: lee
(三)命令演示
1. 启动容器(后台运行)
-
docker compose up:启动 Compose 配置中的所有服务; -
-d:后台运行(detach 模式),不加则会前台占用终端; -
输出显示两个容器(
timinglee-dbserver-1、timinglee-web-1)启动成功(命名规则:目录名-服务名-序号)。
bash
[root@docker-node1 timinglee]# docker compose up -d
[+] up 2/2
✔ Container timinglee-dbserver-1 Started 0.3s
✔ Container timinglee-web-1 Started 0.3s
2. 查看运行中的容器
bash
[root@docker-node1 timinglee]# 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 timinglee-web-1
b6bb2557840f mysql:8.0 "docker-entrypoint.s..." About a minute ago Up 2 seconds 3306/tcp, 33060/tcp timinglee-dbserver-1
3. 停止并删除容器 / 网络
docker compose down:停止并删除 Compose 管理的容器、默认网络(timinglee_default);
-
注意:不会删除数据卷(如需删除卷,需加
-v参数,如docker compose down -v); -
输出显示容器和网络都被移除。
bash
[root@docker-node1 timinglee]# docker compose down
[+] down 3/3
✔ Container timinglee-web-1 Removed 0.1s
✔ Container timinglee-dbserver-1 Removed 1.5s
✔ Network timinglee_default Removed 0.1s
4. 查看 Compose 管理的容器状态
bash
[root@docker-node1 timinglee]# docker compose ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5. 指定配置文件启动(-f 参数)
-
-f:指定自定义路径的 Compose 配置文件(默认只识别当前目录的docker-compose.yml/docker-compose.yaml); -
此处从根目录执行命令,通过
-f指向timinglee目录下的配置文件,实现跨目录启动。
bash
[root@docker-node1 ~]# docker compose -f /root/timinglee/docker-compose.yml up -d
[+] up 3/3
✔ Network timinglee_default Created 0.1s
✔ Container timinglee-web-1 Started 0.4s
✔ Container timinglee-dbserver-1 Started 0.2s
6. 停止容器(保留容器,不删除)
-
docker compose stop:停止运行中的容器,但保留容器本身 (可通过start重启); -
执行后查看容器状态:
Exited (0)表示正常停止。
bash
[root@docker-node1 timinglee]# docker compose stop
[+] stop 2/2
✔ Container timinglee-dbserver-1 Stopped 1.3s
✔ Container timinglee-web-1 Stopped 0.1s
7. 启动已停止的容器
重启通过 stop 停止的容器(无需重新创建,快速恢复运行)
bash
[root@docker-node1 timinglee]# docker compose start
[+] start 2/2
✔ Container timinglee-dbserver-1 Started 0.2s
✔ Container timinglee-web-1 Started 0.3s
8. 重启容器
-
docker compose restart:重启运行中的容器(先停止,再启动); -
重启后通过
docker compose ps可看到容器恢复运行状态。
bash
[root@docker-node1 timinglee]# docker compose restart
[+] restart 0/2
⠼ Container timinglee-dbserver-1 Restarting 1.4s
⠼ Container timinglee-web-1 Restarting 1.4s
9. 最终清理(再次 down)
再次执行 down 完成最终清理,移除容器和网络。
bash
[root@docker-node1 timinglee]# docker compose down
[+] down 3/3
✔ Container timinglee-web-1 Removed 0.1s
✔ Container timinglee-dbserver-1 Removed 1.4s
✔ Network timinglee_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 |
指定非默认路径的配置文件 |