Docker容器技术

一、Docker介绍

  1. Docker 可以把你的程序 + 依赖库 + 配置 + 环境 全部打包成一个镜像(Image) ,然后在任何装了 Docker 的机器上一模一样地跑起来
  1. Docker 是一款开源的容器化技术工具,核心价值是解决软件运行环境不一致的问题

  2. 它将应用程序及其依赖(库、配置、运行环境等)打包成标准化的镜像(Image),这个镜像可理解为 "可执行的安装包";

  3. 基于镜像启动的容器(Container) 是轻量级、隔离的运行实例,能在任何安装了 Docker 的机器(Windows/Linux/Mac)上无差异运行

  4. 相比虚拟机,Docker 无需模拟完整操作系统,启动快(秒级)、资源占用少,是目前 DevOps、微服务部署的核心工具。

**一句话解释:**Docker 就是一个「轻量、便携、统一」的容器工具,用来打包、运行软件。
3. 最形象的比喻

  • 传统部署:每台电脑环境不一样,程序经常「在我这能跑,在你那就崩」。

  • Docker:像一个集装箱。

    • 不管船(服务器)是什么型号

    • 不管货(程序)是什么

    • 只要装进集装箱,到哪都能直接运行

  1. 核心概念
  • 镜像(Image):程序的「安装包 / 模板」。

  • 容器(Container):镜像跑起来后的实例,一个隔离的小环境。

  • Dockerfile:用来告诉 Docker 怎么打包镜像。

  • 仓库(Registry):存放镜像的地方(比如 Docker Hub)。

  1. 运维 / 开发为什么爱用它?
  • 环境统一:开发、测试、线上完全一样

  • 秒级启动:比虚拟机快得多

  • 轻量:不占太多资源

  • 可移植:一次打包,到处运行

二、部署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)操作命令

容器是镜像的运行实例,是动态的、可交互的环境。

  1. 容器创建 / 运行
命令 作用
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
  1. 容器状态查看
命令 作用
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
  1. 容器启停 / 控制
命令 作用
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]   #按键
  1. 容器内操作
命令 作用
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
  1. 启动容器验证镜像信息
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.gzleetiminglee等文件,仅保留后续构建需要的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. 操作流程

  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
  1. 编写 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;"]
  1. 构建并验证:执行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
  1. 加载内核模块

在 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
  1. 安装 Docker

在 node2 上通过 dnf 安装 docker-ce:dnf install docker-ce -y

bash 复制代码
[root@docker-node2 ~]# dnf install docker-ce -y

(二)部署 Registry 镜像仓库(node1 操作)

  1. 导入 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
  1. 查看 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
  1. 启动 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。

  1. 查看容器挂载目录
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 版本兼容问题)

  1. 问题背景

CentOS7 自带的 OpenSSL 版本较低,直接使用addext参数会报错,因此改用配置文件方式生成证书。

  1. 操作步骤
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                   # 哈希算法
  1. 验证证书

查看证书详情,确认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容器网络

(一)操作背景

  1. 启动一个基础的 Nginx 容器(webserver);

  2. 由于 Nginx 容器内未预装ip命令,无法查看网络配置;

  3. 启动另一个带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-node1docker-node2)新增网卡,并将网卡模式设置为 host-only(仅主机模式)。

  • 作用host-only 模式下的网卡仅用于主机间的私有网络通信,隔离外部网络,保证容器跨主机通信的网络环境独立、可控。

(二)开启新加网卡的混杂模式

1. 混杂模式说明

网卡的混杂模式(PROMISC) 是指网卡接收所有经过它的数据包(而非仅目标 MAC 地址匹配自身的数据包),这是 macvlan 网络实现跨主机通信的前提。

2. 代码解释

  • ip a s eth1ip 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/241.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 是将宿主机的目录 / 文件直接挂载到容器内,是最基础的数据卷方式,支持读写 / 只读权限控制。

  1. 清理无用数据卷
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
  1. 创建并运行容器(挂载宿主机路径)
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
  1. 权限认证
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,更易管理。

  1. 创建数据卷
bash 复制代码
[root@docker-node1 ~]# docker volume create timinglee	
timinglee
[root@docker-node1 ~]# docker volume ls
DRIVER    VOLUME NAME
local     timinglee
  1. 宿主机操作卷内容

直接在宿主机卷目录创建文件,容器内可访问。

bash 复制代码
[root@docker-node1 volumes]# touch  timinglee/_data/file
  1. 容器挂载(只读)
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
  1. 删除数据卷
bash 复制代码
[root@docker-node1 ~]# docker volume rm timinglee
timinglee
[root@docker-node1 ~]# docker volume ls
DRIVER    VOLUME NAME

(三)数据卷容器(Volumes-from)

专门用于挂载数据卷的 "模板容器",其他容器可通过--volumes-from 继承其所有挂载配置,实现数据共享。

  1. 创建数据卷容器(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
  1. 继承数据卷容器
  • 无需重复挂载配置,直接访问/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 实现容器资源管控。

  1. 查看当前 cgroup 挂载类型
bash 复制代码
[root@docker-node1 ~]# mount -t cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
  1. 修改内核参数
bash 复制代码
[root@docker-node1 ~]# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
  1. 必须重启
bash 复制代码
[root@docker-node1 ~]# reboot

重启系统后,执行 mount -t cgroup 可看到 cgroup v1 被拆分为 cpumemorydevices 等多个独立子系统挂载,每个子系统对应不同的资源管控维度。

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 倍);

  • 验证方式:

    1. 启动带内存限制的 Nginx 容器;

    2. 通过 cgexec 工具在容器的 cgroup 内存组中执行 dd 命令写入大文件;

    3. 当写入数据量超过 200MB 时,dd 进程被内核杀死,验证内存限制生效;

    4. 查看 /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 容器处于受限的权限环境中,避免容器突破隔离层操作宿主机资源;可通过参数精细管控容器特权。

  1. 无特权容器(默认)
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
  1. 特权容器(--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-1timinglee-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 指定非默认路径的配置文件
相关推荐
尤老师FPGA2 小时前
petalinux制作linux系统flash+sd卡启动
linux·运维·服务器
code_pgf2 小时前
Orin NX 16GB 的 package 安装命令清单 + Docker/工作区目录结构 + bringup 顺序
运维·docker·容器·ros
桌面运维家2 小时前
Prometheus服务器监控告警实战指南
运维·服务器·prometheus
不一样的故事1263 小时前
核心预测未来 10 年
运维·安全·自动化
剪刀石头布Cheers3 小时前
Ubuntu安装向日葵远程黑屏
linux·运维·ubuntu
wanhengidc3 小时前
跨境云手机适用于哪些场景
大数据·运维·服务器·数据库·科技·智能手机
空灵之海3 小时前
Ubuntu环境Docker安装禅道项目管理软件开源版
ubuntu·docker·开源
sky wide4 小时前
[特殊字符] Docker 安装指南 (CentOS)
docker·容器·centos
Honeyeagle4 小时前
无线移动在线复合式多合一气体检测仪:工业安全监测的革新利器
运维·网络·安全