实验目标回顾

通过本章实验,我们将系统掌握以下内容:

  • Docker CE 的安装与基础配置(软件源、内核参数、加速器)
  • Docker 常用命令(镜像、容器、端口映射、数据复制、提交等)
  • Dockerfile 镜像构建(FROM、COPY、ADD、ENV、RUN、EXPOSE、VOLUME、WORKDIR、CMD、ENTRYPOINT 等指令)
  • 镜像优化方案(减少层、多阶段构建、distroless 精简镜像)
  • Docker Hub 与私有仓库(Registry、Harbor)的搭建与认证
  • Docker 网络(bridge、host、none、自定义网络互通、macvlan 跨主机通信)
  • Docker 数据卷(bind mount、managed volume、volume container、备份恢复)
  • 容器资源限制(CPU、内存、磁盘 IO)
  • 容器安全加固(特权模式、capabilities 白名单)
  • Docker Compose 多容器编排(服务定义、网络、存储、负载均衡实战)

核心知识点梳理

模块关键知识点
安装配置docker.repobr_netfiltersysctl 内核参数、registry-mirrors
镜像操作pullimagessave/loadhistoryrmitagpush
容器操作run(-d、-it、-p、-v、–name)、ps -astart/stop/kill/rmexeccpcommitinspectlogs
Dockerfile 指令FROMLABELCOPY/ADD(解压区别)、ENVRUN(shell/exec)、EXPOSEVOLUMEWORKDIRCMD(shell/JSON)、ENTRYPOINT
镜像优化合并 RUN 层、多阶段构建(COPY --from)、distroless 基础镜像
私有仓库Registry 镜像、insecure-registries、TLS 证书、htpasswd 认证;Harbor 离线安装
网络bridge(默认)、hostnone;自定义桥接;docker network connect--network container:;macvlan 跨主机
存储卷-v host_path:container_path:rodocker volume create--volumes-fromtar 备份恢复
资源限制--cpu-period/--cpu-quota(CPU 份额)、--device-write-bps(磁盘 IO);cgroup 查询
安全--privileged(全部特权)、--cap-add(细粒度);cgroup v2 切换(可选)
Composeservicesbuildportsexposeenvironmentvolumesnetworksvolumes(命名卷)、external 网络

实验中遇到的问题及解决方法

(文章末尾)

一、基础配置 Docker

配置软件仓库并安装 docker-ce

#利用阿里云部署软件仓库
[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

[root@docker-node1 ~]# dnf makecache
docker-ce.x86_64 : The open-source application container engine
docker-ce-cli.x86_64 : The open-source application container engine

[root@docker-node1 ~]# dnf install docker-ce -y

[root@docker-node1 ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true

[root@docker-node1 ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf
[root@docker-node1 ~]# modprobe -a br_netfilter

[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

[root@docker-node1 ~]# sysctl --system

[root@docker-node1 ~]# systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.

配置 docker 加速器

[root@docker-node1 ~]# cat > /etc/docker/daemon.json <<EOF
{
    "registry-mirrors": ["https://docker.1ms.run"]
}
EOF
[root@docker-node1 ~]# systemctl restart docker

[root@docker-node1 ~]# docker info
 Registry Mirrors:
  https://docker.1ms.run/

二、docker 常用命令

快速记忆口诀或思路

  1. 按动词含义记

    • 搜索:search
    • 拉取:pull
    • 推送:push
    • 运行:run
    • 列出:ps / images
    • 停止:stop / kill
    • 删除:rm / rmi
    • 导出:save -o
    • 导入:load -i
    • 进入:attach / exec
    • 复制:cp
    • 提交:commit
    • 查看详情:inspect / history
  2. 按对象分类

    • 镜像操作images, search, pull, history, save, load, rmi, commit
    • 容器操作run, ps, start, stop, kill, rm, attach, exec, inspect, logs, cp
  3. 选项速记

    • -d:后台(detach)
    • -it:交互终端(interactive + tty)
    • -a:全部(all)
    • -f:强制(force)
    • -o:输出文件(output)
    • -i:输入文件(input)

镜像基本操作

#镜像查看
[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 pull busybox
Using default tag: latest
latest: Pulling from library/busybox
61dfb50712f5: Pull complete
96cfb76e59bd: Download complete
Digest: sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest

#查看镜像提交历史
[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 images
                                                                                                i Info →   U  In Use
IMAGE            ID             DISK USAGE   CONTENT SIZE   EXTRA
busybox:latest   b3255e7dfbcd        6.7MB         2.22MB
nginx:1.26       41b194461e4b        279MB         75.2MB

#导出镜像
[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 run -d   --name web nginx:1.26
f3e369725fab95d48779eaa556941b735aae841efe09bb1d28bca89923c44ee4

#查看运行容器
[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

#交互模式运行容器
[root@docker-node1 ~]# docker run  -it --name busybox busybox:latest

#交互运行容器默认退出后会停止
[root@docker-node1 ~]# docker ps -a
CONTAINER ID   IMAGE            COMMAND                   CREATED              STATUS                      PORTS     NAMES
d1b27167a247   busybox:latest   "sh"                      50 seconds ago       Exited (0) 19 seconds ago             busybox

#运行停止的容器
[root@docker-node1 ~]# docker start  busybox
[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 attach busybox
/ # [ctrl]+[p]+[q]   #按键
[root@docker-node1 ~]# docker ps -a
CONTAINER ID   IMAGE            COMMAND                   CREATED         STATUS              PORTS     NAMES
d1b27167a247   busybox:latest   "sh"                      3 minutes ago   Up About a minute             busybox

#查看容器信息
[root@docker-node1 ~]# docker inspect busybox

#容器控制
[root@Docker-node1 ~]# docker stop busybox			#停止容器
[root@Docker-node1 ~]# docker kill busybox			#杀死容器,可以使用信号
[root@Docker-node1 ~]# docker start busybox			#开启停止的容器

#在已经运行的容器中执行指定命令
[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 rm -f busybox
busybox
[root@docker-node1 ~]# docker stop web
web
[root@docker-node1 ~]# docker rm  web
web

#内容提交
[root@docker-node1 ~]# docker run  -it --name test busybox:latest
/ # touch /root/file
/ # ls /root/
file
ctrl+qp 退出当前环境并继续运行容器

[root@docker-node1 ~]# docker commit -m "add file" test  busybox-file:latest
sha256:31a32089d241d025a5a54f144f15319cc6fb55be1b41d049f8905a472d5a028e
[root@docker-node1 ~]# docker images
                                                                                                i Info →   U  In Use
IMAGE                       ID             DISK USAGE   CONTENT SIZE   EXTRA
busybox-file:latest         31a32089d241       6.71MB         2.21MB

[root@docker-node1 ~]# docker run  -it --name test busybox-file:latest

#文件在镜像中的复制
[root@docker-node1 ~]# docker run  -it --name test busybox-file:latest
[root@docker-node1 ~]# docker cp  test:/root/file  /mnt
Successfully copied 1.54kB to /mnt
[root@docker-node1 ~]# ls /mnt/
file  hgfs

[root@docker-node1 ~]# docker cp   /etc/passwd  test:/root/
Successfully copied 3.07kB to test:/root/
[root@docker-node1 ~]# docker exec test  ls /root
file
passwd

使用端口

[root@docker-node1 ~]# docker run -d --name test -p 80:80 nginx:latest
a2188738f1a2fb41d6f679cfe62cb4c933d528322fe26e1392b6bb9d25b7de53

在浏览器测试效果如下

游戏

[root@docker-node1 ~]# docker stop test
test
[root@docker-node1 ~]# docker rm test
test

[root@docker-node1 ~]# docker run -d --name mario -p 80:8080 timinglee/mario:latest
01dc71b30c51e9f597a90766a42c5b298db174fbbd3afadff63e8d8bf2bc0440


三、容器镜像构建

熟悉镜像构建用到的参数

#建立构建目录
[root@docker-node1 ~]# mkdir docker
[root@docker-node1 ~]# cd docker/

#编写构建规则文件
[root@docker-node1 docker]# vim Dockerfile

#FROM
FROM busybox:latest

#COPY
[root@docker-node1 docker]# echo liouo > liouo
[root@docker-node1 docker]# cat liouo
liouo

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY liouo /root

#构建命令
[root@docker-node1 docker]# docker build -t liouo:v1 .
[+] Building 0.6s (7/7) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 74B                                                                                       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: 38B                                                                                          0.0s
 => [1/2] FROM docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f   0.0s
 => => resolve docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f   0.0s
 => [2/2] COPY liouo /root                                                                                                0.0s
 => exporting to image                                                                                                    0.1s
 => => exporting layers                                                                                                   0.1s
 => => exporting manifest sha256:4bc2d495670ef3224bdb876d8dfcf6ab21d50d993091061bb49f537c32820d8a                         0.0s
 => => exporting config sha256:383f065aa63af6d92b85302284e2132cd1e8035bffcdaf1f31f373283312dd89                           0.0s
 => => exporting attestation manifest sha256:31263818108dce8c18035cdf46c29c98e89cd5fc9e1cfdaad2225d07ca609d66             0.0s
 => => exporting manifest list sha256:925621e9b7fb5fef29587cf01b5e038baaff9593bcc0d2b652cf7d0e69ebf453                    0.0s
 => => naming to docker.io/library/liouo:v1                                                                               0.0s
 => => unpacking to docker.io/library/liouo:v1

验证

[root@docker-node1 docker]# docker images
                                                                                                          i Info →   U  In Use
IMAGE                       ID             DISK USAGE   CONTENT SIZE   EXTRA
busybox-file:latest         4997aec8a0e5       6.71MB         2.21MB
busybox:latest              b3255e7dfbcd        6.7MB         2.22MB
liouo:v1                    925621e9b7fb       6.71MB         2.21MB
nginx:latest                bc45d248c4e1        237MB         65.8MB
timinglee/game2048:latest   8a34fb9cb168       77.2MB         17.8MB
timinglee/mario:latest      7758988210df        298MB         73.7MB    U
[root@docker-node1 docker]# docker run -it liouo:v1 sh
/ # ll /root/
sh: ll: not found
/ # ls -l /root/
total 4
-rw-r--r--    1 root     root             6 Mar 15 02:14 liouo
/ # cat /root/liouo
liouo
/ # [root@docker-node1 docker]#
[root@docker-node1 docker]# docker history liouo:v1
IMAGE          CREATED         CREATED BY                          SIZE      COMMENT
925621e9b7fb   4 minutes ago   COPY liouo /root # buildkit         4.1kB     buildkit.dockerfile.v0
<missing>      17 months ago   BusyBox 1.37.0 (glibc), Debian 13   4.49MB

Dockerfile 指令逐步实践

v2 – LABEL 与 COPY 指令

目标:为镜像添加标签并复制文件。

[root@docker-node1 docker]# echo yu > yu
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
LABEL creater=yu
COPY liouo /root
[root@docker-node1 docker]# docker build -t liouo:v2 .
[+] Building 0.4s (7/7) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 91B                                                                                       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: 26B                                                                                          0.0s
 => [1/2] FROM docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f   0.0s
 => => resolve docker.io/library/busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f   0.0s
 => CACHED [2/2] COPY liouo /root                                                                                         0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => exporting manifest sha256:57e1df8d8c7e71a705bd3e4436a163ae3e474e3f0b2d689533101b790b777a7b                         0.0s
 => => exporting config sha256:23aaa895a4ca353299481ae02919aa51ea48116da23de29555bd8533e4eebc03                           0.0s
 => => exporting attestation manifest sha256:7090b9c059b15b33065dbcbaab776578232c926f4dd189701dab34b2cc44d0d0             0.0s
 => => exporting manifest list sha256:aa0e40b6dd6495c9f4cf8821b8b4e31ab884fe3765456bb519add012e4c93aef                    0.0s
 => => naming to docker.io/library/liouo:v2                                                                               0.0s
 => => unpacking to docker.io/library/liouo:v2

验证

[root@docker-node1 docker]# docker inspect liouo:v2 | grep -A5 Labels

输出显示 "creater": "yu"

v3 – ADD 指令(自动解压)

目标:测试 ADD 自动解压 tar.gz 文件。

#准备压缩包
[root@docker-node1 docker]# tar -czf bin.tar.gz /etc/passwd

#Dockerfile
[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY liouo /root
ADD bin.tar.gz /mnt/

[root@docker-node1 docker]# docker build -t liouo:v3 .

验证

[root@docker-node1 docker]# docker run --rm liouo:v3 ls -l /mnt/

输出显示 /mnt/etc/passwd(解压后的文件)。

v4 – COPY vs ADD 对比

目标:对比 COPY(不解压)和 ADD(解压)。

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
COPY bin.tar.gz /root          # 原样复制
ADD bin.tar.gz /mnt             # 自动解压

[root@docker-node1 docker]# docker build -t liouo:v4 .

验证

[root@docker-node1 docker]# docker run --rm liouo:v4 ls -l /root /mnt

输出:

  • /root/bin.tar.gz 是压缩包
  • /mnt/etc/passwd 是解压后的文件
v5 – ENV 与 RUN 指令

目标:设置环境变量并在构建时使用它创建文件。

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
ENV NAME=yu
RUN ["/bin/sh", "-c", "touch /root/$NAME"]

[root@docker-node1 docker]# docker build -t liouo:v5 .

验证

[root@docker-node1 docker]# docker run --rm liouo:v5 env | grep NAME      # 输出 NAME=yu
[root@docker-node1 docker]# docker run --rm liouo:v5 ls /root/         # 应看到文件 yu

v6 – EXPOSE 指令

目标:声明容器将监听 8080 端口。

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
EXPOSE 8080

[root@docker-node1 docker]# docker build -t liouo:v6 .

验证

[root@docker-node1 docker]# docker inspect liouo:v6 | grep -A5 ExposedPorts

输出显示 "8080/tcp": {}

v7 – VOLUME 指令

目标:创建匿名卷 /mnt,验证宿主机与容器数据共享。

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
VOLUME /mnt
RUN ["/bin/sh", "-c", "touch /root/yu"]

[root@docker-node1 docker]# docker build -t liouo:v7 .

验证

  1. 运行容器并保持运行:
    [root@docker-node1 docker]# docker run -it --name test --rm liouo:v7
    
  2. 另开终端,查看容器挂载信息:
    [root@docker-node1 ~]# docker inspect test | grep -A10 Mounts
    
    得到匿名卷的宿主机路径(如 /var/lib/docker/volumes/.../_data)。
    image-20260315114643647
  3. 在宿主机该目录下创建文件:
    [root@docker-node1 ~]# cd /var/lib/docker/volumes/.../_data
    [root@docker-node1 ~]# touch yu{1..5}
    
  4. 回到容器内查看:
    [root@docker-node1 docker]# docker run -it --name test --rm liouo:v7
    / # ls /mnt/
    yu1  yu2  yu3  yu4  yu5
    
v8 – WORKDIR 指令

目标:设置工作目录为 /mnt

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
RUN ["/bin/sh", "-c", "touch /root/yu"]
WORKDIR /mnt

[root@docker-node1 docker]# docker build -t liouo:v8 .

验证

[root@docker-node1 docker]# docker run --rm liouo:v8 pwd   # 输出 /mnt
[root@docker-node1 docker]# docker run -it --rm liouo:v8
/mnt  #按ctrl+q+p退出

v9 – CMD 指令(多种格式测试)

目标:测试 CMD 的不同写法。

  • 第一次(shell 格式)

    FROM busybox:latest
    ENV NAME=yu
    CMD echo "Hello, $NAME"
    

    构建 v9,运行 docker run --rm liouo:v9 输出 Hello, yu
    运行 docker run --rm liouo:v9 echo "heihei" 输出 heihei(CMD 被覆盖)。

  • 第二次(结合 WORKDIR)

    FROM busybox:latest
    ENV NAME=yu
    RUN ["/bin/sh", "-c", "touch /root/yu"]
    WORKDIR /mnt
    CMD ["/bin/sh", "-c", "/bin/echo $NAME"]
    

    运行输出 yu

v10 – ENTRYPOINT 指令

目标:测试 ENTRYPOINT 与 CMD 的区别。

[root@docker-node1 docker]# vim Dockerfile
FROM busybox:latest
ENV NAME yu
ENTRYPOINT echo $NAME

[root@docker-node1 docker]# docker build -t liouo:v10 .

验证

docker run --rm liouo:v10          # 输出 yu
docker run --rm liouo:v10 echo heihei   # 仍然输出 yu


构建 centos 可用仓库实验

实验开始前删除之前练习的内容

[root@docker-node1 docker]# docker images | awk '/\<liouo\>/{system("docker rmi " $1)}'

centos7.tar 是用 mobax 导入的压缩包,也可以直接在虚拟机上拉取

[root@docker-node1 docker]# docker load -i centos7.tar
[root@docker-node1 docker]# docker build -t centos-7:repo .
[root@docker-node1 docker]# docker run -it --rm --name centos centos-7.repo /bin/bash  			# 因为版本比较老,所以仓库用不了,需要我们自己搭仓库

[root@docker-node1 docker]# vim centos7.repo
[centos7]
name = centos7
baseurl = https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/
gpgcheck = 0

[root@docker-node1 docker]# vim Dockerfile
FROM centos:7
LABEL Creater=yu
RUN ["/bin/bash",  "-c" ,  "rm -rf /etc/yum.repos.d/*"]
COPY centos7.repo /etc/yum.repos.d/centos7.repo

重新构建仓库

[root@docker-node1 docker]# docker build -t centos-7.repo .

[root@docker-node1 docker]# docker run -it --rm --name centos centos-7.repo /bin/bash

[root@2259d53f94ec /]# ls /etc/yum.repos.d/
centos7.repo
[root@2259d53f94ec /]# yum install gcc -y
# 成功


Dockerfile 实例:编译安装 Nginx

[root@docker-node1 docker]# ll
总用量 1112
-rw-r--r-- 1 root root     648  315 11:05 bin.tar.gz
-rw-r--r-- 1 root root     108  315 12:20 centos7.repo
-rw-r--r-- 1 root root     630  315 16:38 Dockerfile
-rw-r--r-- 1 root root       6  315 10:14 liouo
-rw-r--r-- 1 root root 1108958 1214  2022 nginx-1.23.3.tar.gz # 要有这个
-rw-r--r-- 1 root root       3  315 11:03 yu

[root@docker-node1 docker]# vim centos7.repo
[centos7]
name = centos7
baseurl = https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/
gpgcheck = 0

[root@docker-node1 docker]# vim Dockerfile
FROM centos:7
LABEL Creater=yu
RUN ["/bin/bash",  "-c" ,  "rm -rf /etc/yum.repos.d/*"]
COPY centos7.repo /etc/yum.repos.d/centos7.repo
ADD nginx-1.23.3.tar.gz /mnt
# 自动获取解压出的目录名并切换到该目录
RUN cd /mnt && \
    NGINX_DIR=$(ls -d */ | head -1) && \
    echo "Nginx source directory: $NGINX_DIR" && \
    mv "$NGINX_DIR" nginx && \
    ln -s /mnt/nginx /mnt/nginx-src
WORKDIR /mnt/nginx
RUN yum install -y gcc make pcre-devel openssl-devel
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;"]

# 通过dockerfile生成镜像
[root@docker-node1 docker]# docker build --no-cache -t webserver:v1 .

测试镜像可用性

查看容器详情

[root@docker-node1 docker]# docker inspect  webserver:v1

镜像优化方案

方法一:缩减镜像层
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7 AS build
LABEL Creater=yu
RUN ["/bin/bash",  "-c" ,  "rm -rf /etc/yum.repos.d/*"]
COPY centos7.repo /etc/yum.repos.d/centos7.repo
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx-1.23.3 && yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

[root@docker-node1 docker]# docker build -t webserver:v2 .
[root@docker-node1 docker]# docker images  webserver
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
webserver    v2        caf0f80f2332   4 seconds ago       317MB
webserver    v1        bfd6774cc216   About an hour ago   494MB
方法二:多阶段构建
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7 AS build
LABEL Creater=yu
RUN ["/bin/bash",  "-c" ,  "rm -rf /etc/yum.repos.d/*"]
COPY centos7.repo /etc/yum.repos.d/centos7.repo
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx-1.23.3 && yum clean all

FROM centos:7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

[root@docker-node1 docker]# docker build -t webserver:v3 .

[root@docker-node1 docker]# docker images  webserver
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
webserver    v3        1ac964f2cefe   29 seconds ago      205MB
webserver    v2        caf0f80f2332   3 minutes ago       317MB
webserver    v1        bfd6774cc216   About an hour ago   494MB
方法三:使用最精简镜像(distroless)

使用 Google 提供的最精简镜像
下载地址:https://github.com/GoogleContainerTools/distroless

# 这里使用从宿主机上传的 gcr.io.tar
[root@docker-node1 ~]# mkdir new
[root@docker-node1 ~]# cd new/
[root@docker-node1 ~]# docker load  -i debian11.tar.gz
[root@docker-node1 new]# ll
总用量 96264
-rw-r--r-- 1 root root 76110336  315 16:50 centos7.tar
-rw-r--r-- 1 root root 22456832  315 16:48 debian11.tar.gz
-rw-r--r-- 1 root root     1292  315 16:44 Dockerfile

[root@docker-node1 new]# vim Dockerfile
FROM nginx:1.23 AS base

# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE

RUN mkdir -p /opt/var/cache/nginx && \
    cp -a --parents /usr/lib/nginx /opt && \
    cp -a --parents /usr/share/nginx /opt && \
    cp -a --parents /var/log/nginx /opt && \
    cp -aL --parents /var/run /opt && \
    cp -a --parents /etc/nginx /opt && \
    cp -a --parents /etc/passwd /opt && \
    cp -a --parents /etc/group /opt && \
    cp -a --parents /usr/sbin/nginx /opt && \
    cp -a --parents /usr/sbin/nginx-debug /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libpcre* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
    cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

FROM gcr.io/distroless/base-debian11

COPY --from=base /opt /

EXPOSE 80 443

ENTRYPOINT ["nginx", "-g", "daemon off;"]

[root@docker-node1 new]# docker build -t webserver:v4 .
[root@docker-node1 new]# docker images webserver


四、Docker Hub 的使用方法

网址 https://hub.docker.com/

在网址上先注册一个账号

[root@docker-node1 ~]# docker login -u liouosept

i Info → A Personal Access Token (PAT) can be used instead.
          To create a PAT, visit https://app.docker.com/settings

Password:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

# 登陆信息保存位置
[root@docker-node1 ~]# cd .docker/
[root@docker-node1 .docker]# ll
总用量 4
drwx------ 6 root root 100  315 10:15 buildx
-rw------- 1 root root  99  315 17:21 config.json # 登录信息的文件
[root@docker-node1 .docker]# cat config.json
{
        "auths": {
                "https://index.docker.io/v1/": {
                        "auth": "bGlvdW9zZXB0OmRvY2tlcjg4eXU="
                }
        }
}

# 打标签
[root@docker-node1 ~]# docker tag gcr.io/distroless/base-debian11:latest liouosept/base-debian11:latest

# 把现有的 debian11 推上去
[root@docker-node1 ~]# docker push liouosept/base-debian11
Using default tag: latest
The push refers to repository [docker.io/liouosept/base-debian11]
2388d21e8e2b: Pushed
8451c71f8c1e: Pushed
4d049f83d9cf: Pushed
24aacbf97031: Pushed
ac805962e479: Pushed
6835249f577a: Pushed
bbb6cacb8c82: Pushed
2a92d6ac9e4f: Pushed
5342a2647e87: Pushed
577c8ee06f39: Pushed
9ed498e122b2: Pushed
af5aa97ebe6c: Pushed
1a73b54f556b: Pushed
c048279a7d9f: Pushed
latest: digest: sha256:cac381e9184d647928d96962ec665432cb19cd06dcf5dc05d1b5f39703f42ff3 size: 2367    # 成功

点进去可以看到详细信息


五、搭建简单的 Registry 仓库

下载 Registry 镜像

[root@docker-node1 ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
bc1da058f299: Pull complete
a8100d968019: Pull complete
7369e5bd3505: Pull complete
3e2f893a11ee: Pull complete
3aeded8eec5f: Pull complete
db921fed581e: Download complete
e7513cd11830: Download complete
Digest: sha256:6c5666b861f3505b116bb9aa9b25175e71210414bd010d92035ff64018f9457e
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest

开启 Registry

# 查看使用的端口
[root@docker-node1 ~]# docker inspect registry | grep -A2 ExposedPorts

[root@docker-node1 ~]# docker run -d -p 5000:5000 --restart=always --name registry registry:latest
d3a35f99addb755912b03f530bddc67760376e5145ef78a1e1a2af7242d9e77d
[root@docker-node1 ~]# docker ps
CONTAINER ID   IMAGE             COMMAND                   CREATED         STATUS         PORTS                                         NAMES
d3a35f99addb   registry:latest   "/entrypoint.sh /etc…"   6 seconds ago   Up 5 seconds   0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp   registry

上传镜像到仓库中

#给要上传的镜像打标签
[root@docker-node1 ~]# docker tag busybox:latest 172.25.254.10:5000/busybox:latest

#docker在上传的过程中默认使用https,但是我们并没有建立https认证需要的认证文件所以会报错
[root@docker-node1 ~]# docker push 172.25.254.10:5000/busybox:latest
The push refers to repository [172.25.254.10:5000/busybox]
61dfb50712f5: Unavailable
failed to do request: Head "https://172.25.254.10:5000/v2/busybox/blobs/sha256:af3f0f48a24edb84e94aff6f44f5d089203453719d3b2328486d311e61db9b09": http: server gave HTTP response to HTTPS client

#配置非加密端口
[root@docker-node1 ~]# mv /etc/docker/daemon.json  /etc/docker/daemon.json.bak

[root@docker-node1 ~]# vim /etc/docker/daemon.json
{
  "insecure-registries" : ["http://172.25.254.10:5000"]
}

[root@docker-node1 ~]# systemctl restart docker

# 重新尝试上传镜像
[root@docker-node1 ~]# docker push 172.25.254.10:5000/busybox:latest
The push refers to repository [172.25.254.10:5000/busybox]
61dfb50712f5: Pushed
latest: digest: sha256:70ce0a747f09cd7c09c2d6eaeab69d60adb0398f569296e8c0e844599388ebd6 size: 610

i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
          sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f -> sha256:70ce0a747f09cd7c09c2d6eaeab69d60adb0398f569296e8c0e844599388ebd6

#查看镜像上传
[root@docker-node1 ~]# curl 172.25.254.10:5000/v2/_catalog
{"repositories":["busybox"]}

为 Registry 提供加密传输

# 一定要记得创建该目录,不然直接生成认证key和证书会报错
[root@docker-node1 ~]# mkdir certs

# 生成认证key和证书
[root@docker-node1 ~]# openssl req -newkey rsa:4096 \
> -nodes -sha256 -keyout certs/liouo.org.key \
> -addext "subjectAltName = DNS:reg.liouo.org" \
> -x509 -days 365 -out certs/liouo.org.crt
# 或者
[root@docker-node1 certs]# openssl req -newkey rsa:4096 -nodes -sha256   -keyout liouo.org.key   -addext "subjectAltName = DNS:reg.liouo.org"   -x509 -days 365 -out liouo.org.crt
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Shaanxi
Locality Name (eg, city) [Default City]:Xi'an
Organization Name (eg, company) [Default Company Ltd]:liouo
Organizational Unit Name (eg, section) []:docker
Common Name (eg, your name or your server's hostname) []:reg.liouo.org
Email Address []:admin@liouo.org

# 一定要确保下面输出的两个MD5一致,否则需要重新生成认证key和证书
[root@docker-node1 certs]# openssl x509 -noout -modulus -in liouo.org.crt | openssl md5
[root@docker-node1 certs]# openssl rsa -noout -modulus -in liouo.org.key | openssl md5
MD5(stdin)= d51d4cd43a82a0e8f6bb3578e91ed4d3
MD5(stdin)= d51d4cd43a82a0e8f6bb3578e91ed4d3

# 查看证书信息
[root@docker-node1 ~]# openssl x509 -in certs/liouo.org.crt -noout -text
# 启动仓库
[root@docker-node1 ~]# docker run -d -p 443:443 --restart=always --name registry \
> -v /opt/registry:/var/lib/registry \
> -v /root/certs:/certs \
> -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
> -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/liouo.org.crt \
> -e REGISTRY_HTTP_TLS_KEY=/certs/liouo.org.key registry
#为客户端建立证书
[root@docker-node2 ~]# mkdir -p /etc/docker/certs.d/reg.liouo.org

[root@docker-node1 ~]# scp /root/certs/liouo.org.crt root@172.25.254.20:/etc/docker/certs.d/reg.liouo.org/ca.crt

[root@docker-node2 ~]# cd /etc/docker/certs.d/reg.liouo.org
[root@docker-node2 reg.liouo.org]# ll
总用量 4
-rw-r--r-- 1 root root 2159  321 11:40 ca.crt

测试

[root@docker-node1 certs]# docker tag busybox:latest reg.liouo.org/busybox:latest
[root@docker-node1 certs]# docker push reg.liouo.org/busybox:latest
The push refers to repository [reg.liouo.org/busybox]
61dfb50712f5: Pushed
latest: digest: sha256:70ce0a747f09cd7c09c2d6eaeab69d60adb0398f569296e8c0e844599388ebd6 size: 610

若失败,就进行下面的操作(在本地做解析)

[root@docker-node1 certs]# vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.10     docker-node1
127.0.0.1 reg.liouo.org
172.25.254.10 reg.liouo.org

[root@docker-node1 certs]# curl -k https://reg.liouo.org/v2/
{}

为仓库建立登录认证

#安装建立认证文件的工具包
[root@docker docker]# dnf install httpd-tools -y

#建立认证文件
[root@docker ~]# mkdir auth
[root@docker ~]# htpasswd -Bc auth/htpasswd timinglee	#-B 强制使用最安全加密方式,默认用md5加密
New password: # docker88yu
Re-type new password:
Adding password for user liouo

#添加认证到registry容器中	
[root@docker ~]# docker run -d -p 443:443 --restart=always --name registry \
-v/opt/registry:/var/lib/registry \
-v /root/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/liouo.org.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/liouo.org.key \
-v /root/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry

[root@docker ~]# curl -k https://reg.liouo.org/v2/_catalog -u timinglee:lee
{"repositories":["busybox","nginx"]}

#登陆测试
[root@docker ~]# docker login reg.liouo.org
Username: liouo
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores

Login Succeeded

当仓库开启认证后必须登陆仓库才能进行镜像上传

[root@docker-node1 certs]# docker login reg.liouo.org -u liouosept
Password:  # docker88yu
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

[root@docker-node1 certs]# docker tag busybox:latest reg.liouo.org/busybox:latest
[root@docker-node1 certs]# docker push reg.liouo.org/busybox:latest
The push refers to repository [reg.liouo.org/busybox]
61dfb50712f5: Pushed
latest: digest: sha256:70ce0a747f09cd7c09c2d6eaeab69d60adb0398f569296e8c0e844599388ebd6 size: 610

# 未登录情况下不能下载
[root@docker-node2 ~]# docker pull reg.liouo.org/busybox:latest
Error response from daemon: failed to resolve reference "reg.liouo.org/busybox:latest": failed to do request: Head "https://reg.liouo.org/v2/busybox/manifests/latest": EOF

六、构建企业级私有仓库 Harbor

部署 Harbor

[root@docker-node1 ~]# tar zxf harbor-offline-installer-v2.14.0.tgz

[root@docker-node1 ~]# cd harbor/
[root@docker-node1 harbor]# cp harbor.yml.tmpl harbor.yml

[root@docker-node1 harbor]# vim harbor.yml
hostname: reg.liouo.org
  certificate: /root/certs/liouo.org.crt
  private_key: /root/certs/liouo.org.key
  harbor_admin_password: docker88yu

[root@docker harbor]# ./install.sh --help
Please set --with-notary 				#证书签名
Please set --with-trivy  				#安全扫描
Please set --with-chartmuseum if needs enable Chartmuseum in Harbor

如果 registry 容器正在运行,就进行下面的操作

[root@docker-node1 harbor]# docker stop registry && docker rm registry
registry
registry

# 删除旧的配置目录
[root@docker-node1 harbor]# rm -rf /root/harbor/common

#重新运行安装脚本
[root@docker-node1 harbor]# ./install.sh
✔ ----Harbor has been installed and started successfully.----
# 管理 harbor 的容器
[root@docker-node1 harbor]# docker compose stop

[root@docker-node1 harbor]# docker compose up -d

管理仓库

在浏览器输入 172.25.254.10,注意要清除浏览器之前的缓存

[root@docker-node1 harbor]# docker login reg.liouo.org
# [Username: admin]
# [password: docker88yu]
Login Succeeded

[root@docker-node1 harbor]# docker tag goharbor/registry-photon:v2.14.0 reg.liouo.org/library/registry-photon:v2.14.0
[root@docker-node1 harbor]# docker push reg.liouo.org/library/registry-photon:v2.14.0
The push refers to repository [reg.liouo.org/library/registry-photon]
3d88da6fc880: Pushed
a3691511d386: Pushed
ad50297b7979: Pushed
8c4ac2a0acab: Pushed
a527857ee623: Pushed
5dd32317cf96: Pushed
v2.14.0: digest: sha256:b20f12d92b0f5126dc1e5d5f9f861f54d023e18f2a759d5a027b3efcc401e6d1 size: 1576

上传镜像

[root@docker-node1 ~]# docker tag busybox:latest reg.liouo.org/liouo/busybox:latest
[root@docker-node1 ~]# docker push  reg.liouo.org/liouo/busybox:latest
The push refers to repository [reg.liouo.org/liouo/busybox]
495ba00f2547: Pushed
latest: digest: sha256:91c66c844e6bba57e92e10e755e73a816d0b99edd17eb5297d9ac519ab3a8c81 size: 527

[root@docker-node2 docker]# docker pull liouo/busybox
Using default tag: latest
latest: Pulling from liouo/busybox
8acfacc0e959: Pull complete
Digest: sha256:91c66c844e6bba57e92e10e755e73a816d0b99edd17eb5297d9ac519ab3a8c81
Status: Downloaded newer image for liouo/busybox:latest
docker.io/liouo/busybox:latest

[root@docker-node2 docker]# docker info
 Registry Mirrors:
  https://reg.liouo.org/

七、Docker 网络

原生网络

开两个终端方便观察

[root@docker-node1 ~]# docker ps

# 停止 harbor
[root@docker-node1 harbor]# docker compose down

[root@docker-node1 harbor]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d7f29c9a2486   bridge    bridge    local
648b3162e764   host      host      local
e7c99db86733   none      null      local
[root@docker-node1 ~]# docker run -it --rm busybox:latest
/ # ping www.baidu.com
PING www.baidu.com (198.18.0.36): 56 data bytes
64 bytes from 198.18.0.36: seq=0 ttl=127 time=0.703 ms
64 bytes from 198.18.0.36: seq=1 ttl=127 time=0.960 ms
^C
--- www.baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.703/0.831/0.960 ms
/ # exit
[root@docker-node1 ~]# docker run -it --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@if101: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether fe:3c:b2:6b:2f:37 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
/ # exit

运行容器的时候才会有这个网卡

[root@docker-node1 ~]# ifconfig
vethd357ab5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::b0f1:eaff:fe96:cc24  prefixlen 64  scopeid 0x20<link>
        ether b2:f1:ea:96:cc:24  txqueuelen 0  (Ethernet)
        RX packets 9  bytes 552 (552.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 17  bytes 1388 (1.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

none 不使用网卡

[root@docker-node1 ~]# docker run -it --rm --network none 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
/ # exit

让不同的自定义网络互通

# 自建网络
[root@docker-node1 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d7f29c9a2486   bridge    bridge    local
648b3162e764   host      host      local
9258be460207   liouou    bridge    local
e7c99db86733   none      null      local
cd779cd19ca6   yu        bridge    local
[root@docker-node1 ~]# docker network inspect yu
					"Gateway": "172.19.0.1"
[root@docker-node1 ~]# docker network inspect liouou
                    "Gateway": "172.18.0.1"
[root@docker-node1 ~]# docker network inspect bridge
                    "Gateway": "172.17.0.1"

验证(不通)

[root@docker-node1 ~]# docker run -it --rm --network yu busybox:latest
/ # ip a
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0

[root@docker-node1 ~]# docker run -it --rm --network liouou busybox:latest
/ # ip a
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0

# 但是两台主机不能互通,即在18网段ping172.19.0.2不通
[root@docker-node1 ~]# docker run -it --rm --network liouou busybox:latest
/ # ping 172.19.0.2
PING 172.19.0.2 (172.19.0.2): 56 data bytes
^C
--- 172.19.0.2 ping statistics ---
11 packets transmitted, 0 packets received, 100% packet loss  # 不通

解决方法:将容器连接到另一个网络

[root@docker-node1 ~]# docker ps
CONTAINER ID   IMAGE            COMMAND                   CREATED          STATUS          PORTS     NAMES
ec5eabb6744b   busybox:latest   "sh"                      44 seconds ago   Up 43 seconds             tender_kowalevski  		# 确认好名称
b312c30ce107   busybox:latest   "sh"                      46 seconds ago   Up 46 seconds             magical_mccarthy
9141ac3baba5   nginx:1.23       "/docker-entrypoint.…"   29 minutes ago   Up 28 minutes   80/tcp    web
[root@docker-node1 ~]# docker network connect yu tender_kowalevski

测试(通了)

[root@docker-node1 ~]# docker run -it --rm --network liouou 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@if129: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 1a:d9:22:e3:22:82 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # 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@if129: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 1a:d9:22:e3:22:82 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
# 出现了eth1网卡
3: eth1@if130: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 42:dd:18:b8:7a:97 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth1
       valid_lft forever preferred_lft forever

# 测试,可以ping通了
/ # ping 172.19.0.2
PING 172.19.0.2 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.188 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.293 ms

joined 容器网络

# 手动上传 busyboxplus.tar.gz

[root@docker-node1 ~]# docker load -i busyboxplus.tar.gz
5f70bf18a086: Loading layer [==================================================>]  1.024kB/1.024kB
075a34aac01b: Loading layer [==================================================>]  13.43MB/13.43MB
774600fa57ae: Loading layer [==================================================>]  6.144kB/6.144kB
Loaded image: busyboxplus:latest

[root@docker-node1 ~]# docker run -it --rm busyboxplus: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@if135: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 16:ee:e4:14:d7:30 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
/ # curl 172.17.0.2
curl: (7) Failed to connect to 172.17.0.2 port 80: Connection refused
# 表示这里没有nginx

[root@docker-node1 ~]# docker run -d --rm --name webserver --network bridge nginx:1.23
a38809aa7fa84d45ce63907903c10e6c60b4d568d474efc0f37ddfaf75733f3a

[root@docker-node1 ~]# docker inspect webserver
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",

[root@docker-node1 ~]# docker run -it --name busybox2 --rm --network container:webserver busyboxplus:latest
/ # ip a
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

/ # curl  127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</body>
</html>

joined 企业级项目

导入镜像
[root@docker-node1 ~]# docker load  -i phpmyadmin-latest.tar.gz
[root@docker-node1 ~]# docker load  -i  mysql-8.0.tar
运行 phpmyadmin 容器
[root@docker-node1 ~]# docker run   -d --name phpmyadmin -e PMA_ARBITRARY=1 -p 80:80 phpmyadmin:latest
运行 mysql(共享 phpmyadmin 的网络)
[root@docker-node1 ~]# docker run  -d --name mysql -e MYSQL_ROOT_PASSWORD='yu' --network container:phpmyadmin mysql:8.0
访问 phpmyadmin
http://172.25.254.10
账号:root
密码:yu


跨网段通信(macvlan)

设定硬件

关机,两台主机添加网卡并设定为 host-only

开启新加网卡的混杂模式
[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)
[root@docker-node1 ~]# docker network create  \
-d macvlan \
--subnet 1.1.1.0/24 \
--gateway 1.1.1.1 \
-o parent=eth1 yu
0cad05f438fbae92c0bb5b7119de158c9d81cfd3e588bfc14f980593447a8f9c
# 或者
[root@docker-node1 ~]# docker network create -d macvlan --subnet 1.1.1.0/24 --gateway 1.1.1.1 -o parent=eth1 yu
7e39c69d1d6856f85e3e9ecbc736b105b5a3a7049d8b1809d6c8cd8a60a7f98c
[root@docker-node1 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
6c3269b03d5a   bridge    bridge    local
648b3162e764   host      host      local
e7c99db86733   none      null      local
7e39c69d1d68   yu        macvlan   local

[root@docker-node2 ~]# docker network create  \
-d macvlan \
--subnet 1.1.1.0/24 \
--gateway 1.1.1.1 \
-o parent=eth1 yu
[root@docker-node2 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
b3804782ce02   bridge    bridge    local
a8c66d7433f2   host      host      local
f36164d2a7c3   none      null      local
b9e229a4da02   yu        macvlan   local

# 避免影响实验效果,将之前实验用到的 busybox 删掉,并且确保两台主机都有 busybox
[root@docker-node1 ~]# docker ps -a | grep busybox
2501b95d8b9d   busybox:latest      "sh"                      18 hours ago     Exited (0) 18 hours ago
[root@docker-node1 ~]# docker rm busybox
busybox
测试跨主机 docker 容器通信
[root@docker-node1 ~]# docker run -it --name busybox --rm --network yu --ip 1.1.1.100 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
7: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 6a:11:16:62:a1:a8 brd ff:ff:ff:ff:ff:ff
    inet 1.1.1.100/24 brd 1.1.1.255 scope global eth0
       valid_lft forever preferred_lft forever

[root@docker-node2 ~]# docker run -it --name busybox --rm --network yu --ip 1.1.1.200 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@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 6e:9e:bd:30:ce:c1 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
/ # ping 1.1.1.100		# 在 node2 开启的容器中 ping node1 主机中开启容器的地址
PING 1.1.1.100 (1.1.1.100): 56 data bytes
64 bytes from 1.1.1.100: seq=0 ttl=64 time=1.052 ms
64 bytes from 1.1.1.100: seq=1 ttl=64 time=0.693 ms
64 bytes from 1.1.1.100: seq=2 ttl=64 time=0.455 ms
^C
--- 1.1.1.100 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.455/0.733/1.052 ms

八、Docker 卷

bind mount 数据卷

[root@docker-node1 ~]# mkdir -p /data_backup
[root@docker-node1 ~]# mv /data/* /data_backup/
[root@docker-node1 ~]# cd /data_backup/
[root@docker-node1 data_backup]# ll
总用量 0
drwxr-xr-x 2            10000            10000  6  321 12:30 ca_download
drwxr-xr-x 2 root             root             48  321 12:22 certs
drwx------ 3 systemd-coredump systemd-coredump 18  321 12:31 database
drwxr-xr-x 2            10000            10000  6  321 12:30 job_logs
drwxr-xr-x 2 systemd-coredump systemd-coredump 22  321 15:54 redis
drwxr-xr-x 3            10000            10000 20  321 15:12 registry
drwxr-xr-x 6 root             root             58  321 15:01 secret
[root@docker-node1 ~]# rm -rf /data
[root@docker-node1 ~]# ls -ld /data
ls: 无法访问 '/data': 没有那个文件或目录

[root@docker-node1 ~]# docker run -it --rm --name test -v /data:/data -v /data1:/data1:ro  -v /etc/passwd:/passwd:ro  busybox:latest
/ # ls -ld /data /data1 /etc/passwd
drwxr-xr-x    2 root     root             6 Mar 22 02:46 /data
drwxr-xr-x    2 root     root             6 Mar 22 02:46 /data1
-rw-r--r--    1 root     root           340 Sep 26  2024 /etc/passwd
/ # 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 卷

[root@docker-node1 ~]# docker volume create liouo
liouo
[root@docker-node1 ~]# docker volume ls
DRIVER    VOLUME NAME
local     liouo

[root@docker-node1 volumes]# pwd
/var/lib/docker/volumes
[root@docker-node1 volumes]# touch liouo/_data/file
[root@docker-node1 volumes]# cd
[root@docker-node1 ~]# docker run -it --rm -v liouo:/data:ro busybox:latest
/ # ls
bin    data   dev    etc    home   lib    lib64  proc   root   sys    tmp    usr    var
/ # touch data/file
touch: data/file: Read-only file system
/ # ls data/
file
/ # exit

[root@docker-node1 ~]# docker volume rm liouo
liouo
[root@docker-node1 ~]# docker volume ls
DRIVER    VOLUME NAME

数据卷容器

[root@docker-node1 ~]# docker run  -it --rm --name data -v /etc/hosts:/hosts:ro -v /data:/data busybox:latest
/ # cat /hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.10     docker-node1
172.25.254.10 reg.liouo.org
/ # > /hosts
sh: can't create /hosts: Read-only file system
/ # touch /data/liouo
/ # ls /data/
file   liouo
/ # 这里不退出,然后重新开一个终端进行下面的操作

[root@docker-node1 ~]# docker run -it --rm --name yu --volumes-from data  busybox:latest
/ # ls
bin    data   dev    etc    home   hosts  lib    lib64  proc   root   sys    tmp    usr    var
/ # cat hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.10     docker-node1
172.25.254.10 reg.liouo.org
/ # > hosts
sh: can't create hosts: Read-only file system
/ # ls /data/
file   liouo

数据的备份和迁移

[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@23951ce13871:/# cd /usr/share/nginx/html/
root@23951ce13871:/usr/share/nginx/html# ls
index.html  timinglee
root@23951ce13871:/usr/share/nginx/html# touch timinglee{1..10}
root@23951ce13871:/usr/share/nginx/html# ls
index.html  timinglee1   timinglee2  timinglee4  timinglee6  timinglee8
timinglee   timinglee10  timinglee3  timinglee5  timinglee7  timinglee9

#数据备份
[root@docker-node1 ~]# docker run  -it --rm  --volumes-from webserver  -v $(pwd):/backup  busybox:latest
/ # ls
backup  bin     dev     etc     home    lib     lib64   proc    root    sys     tmp     usr     var
/ # ls /usr/share/nginx/html/
index.html  liouo       liouo1      liouo10     liouo2      liouo3      liouo4      liouo5      liouo6      liouo7      liouo8      liouo9
/ # tar zcf /backup/html.tar.gz /usr/share/nginx/
tar: removing leading '/' from member names
/ # ls
backup  bin     dev     etc     home    lib     lib64   proc    root    sys     tmp     usr     var
/ # cd backup/
/backup # ls
html.tar.gz # 存在这个压缩包
/backup # exit
[root@docker-node1 ~]# ls
html.tar.gz # 同样也存在这个压缩包

# 删除 /data 下面的所有内容
[root@docker-node1 ~]# rm -fr /data/*

# 检查是否完全删除
[root@docker-node1 ~]# docker exec -it webserver bash
root@ac757d3a86b0:/# cd /usr/share/nginx/html/
root@ac757d3a86b0:/usr/share/nginx/html# ls

#数据恢复
[root@docker-node1 ~]# docker rm -f webserver
webserver

[root@docker-node1 ~]# tar -zxf /root/html.tar.gz -C /data --strip-components=3

# 运行容器
[root@docker-node1 ~]# docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html nginx:1.23
9c2a4c5d1e20cec6ba0e352c489ecffd7deddf6e1ac09808aa020d3d1b185913
[root@docker-node1 ~]# docker exec -it webserver bash
root@9c2a4c5d1e20:/# cd /usr/share/nginx/html/
root@9c2a4c5d1e20:/usr/share/nginx/html/html# ls  # 恢复成功
index.html  liouo  liouo1  liouo10  liouo2  liouo3  liouo4  liouo5  liouo6  liouo7  liouo8  liouo9

九、Docker 安全加固

更改系统 cgroup 版本(企业中非必须)

[root@docker-node1 ~]# mount -t cgroup
[root@docker-node1 ~]# mount -t cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)

[root@docker-node1 ~]# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"

[root@docker-node1 ~]# reboot

[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)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/misc type cgroup (rw,nosuid,nodev,noexec,relatime,misc)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)

十、Docker 资源限制

CPU 用量限制

[root@docker-node1 ~]# docker load -i ubuntu-latest.tar.gz
f36fd4bb7334: Loading layer [==================================================>]  80.56MB/80.56MB
Loaded image: ubuntu:latest

# 不加限制时 CPU 占用 99%
[root@docker-node1 ~]# docker run -it --rm ubuntu:latest
root@a31013e6db77:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@a31013e6db77:/# top
top - 06:24:42 up 32 min,  0 user,  load average: 0.10, 0.07, 0.02
Tasks:   3 total,   2 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s): 17.0 us, 28.1 sy,  0.0 ni, 54.2 id,  0.0 wa,  0.5 hi,  0.2 si,  0.0 st
MiB Mem :   3623.1 total,   2558.1 free,    629.1 used,    667.5 buff/cache
MiB Swap:   4008.0 total,   4008.0 free,      0.0 used.   2994.0 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      9 root      20   0    2736   1408   1408 R  99.0   0.0   0:04.58 dd

# 添加 CPU 限制(20%)
[root@docker-node1 ~]# docker run -it --rm --name test --cpu-period 100000 --cpu-quota 20000 ubuntu
root@a9af3a0f75f7:/# dd if=/dev/zero of=/dev/null &
[1] 10
root@a9af3a0f75f7:/# top
top - 06:27:32 up 35 min,  0 user,  load average: 0.12, 0.16, 0.06
Tasks:   3 total,   2 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.8 us,  5.7 sy,  0.0 ni, 91.0 id,  0.0 wa,  0.3 hi,  0.2 si,  0.0 st
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
     10 root      20   0    2736   1408   1408 R  19.9   0.0   0:05.92 dd

验证限制(可选)

[root@docker-node1 ~]# cd /sys/fs/cgroup/cpu/docker/<container_id>/
[root@docker-node1 ...]# cat cpu.cfs_period_us
100000
[root@docker-node1 ...]# cat cpu.cfs_quota_us
20000

CPU 优先级

确保系统中只有一个 CPU 核心

[root@docker-node1 ~]# cd /sys/devices/system/cpu/
[root@docker-node1 cpu]# echo 0 > cpu1/online
[root@docker-node1 cpu1]# cat /proc/cpuinfo | grep cores
cpu cores       : 1

在两个终端分别运行 Ubuntu 容器,同时 dd 占满 CPU,两个容器各占约 50%(无优先级控制时平分)

# 终端1
[root@docker-node1 ~]# docker run -it --rm --name test ubuntu
root@16c6a78c8776:/# dd if=/dev/zero of=/dev/null &
[1] 8
# 约 48.3%

# 终端2
[root@docker-node1 ~]# docker run -it --rm --name test1 ubuntu
root@7ea24b0e60dd:/# dd if=/dev/zero of=/dev/null &
[1] 8
# 约 48.8%

限制内存使用

[root@docker-node1 ~]# cd /sys/fs/cgroup/memory/docker/
[root@docker-node1 docker]# ls
daa1eedf95ed1610cae337d018cee0ac7173ac1d50f1ac596715ef73226794c1
# 每台设备的这个号是不一样的,要在进行下一步之前先查出来

# 安装检测工具
[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

# 限制内存 190M 成功,200M 被 kill
[root@docker-node1 ~]# cgexec -g memory:docker/daa1eedf95ed1610cae337d018cee0ac7173ac1d50f1ac596715ef73226794c1 dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=190 
记录了190+0 的读入
记录了190+0 的写出
199229440字节(199 MB,190 MiB)已复制,0.0548582 s,3.6 GB/s
[root@docker-node1 ~]# cgexec -g memory:docker/daa1eedf95ed1610cae337d018cee0ac7173ac1d50f1ac596715ef73226794c1 dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=200
已杀死

磁盘 IO 限制

# 无限制
[root@docker-node1 ~]# docker run -it --name test --rm ubuntu:latest
root@8d0cd815ed48:/# 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, 5.0128 s, 209 MB/s

# 限制写入速率 30MB/s
[root@docker-node1 ~]# docker run -it --name test \
  --device-write-bps /dev/sda:30M \
  -v /tmp/iotest:/data \
  --rm ubuntu:latest \
  bash -c "dd if=/dev/zero of=/data/bigfile bs=1M count=1000 oflag=direct status=progress"
472907776 bytes (473 MB, 451 MiB) copied, 15 s, 31.4 MB/s

十一、容器特权

# 普通容器无法查看磁盘分区、无法配置 IP
[root@docker-node1 ~]# docker run -it --name test --rm busybox:latest
/ # ip a a 1.2.3.4/24 eth0@if37
ip: either "local" is duplicate, or "eth0@if37" is garbage
/ # fdisk -l
/ # exit

# --privileged 容器拥有所有特权
[root@docker-node1 ~]# docker run -it --rm --privileged busybox:latest
/ # fdisk -l
Disk /dev/sda: 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/sda1 *  4,4,1       1023,254,2        2048    2099199    2097152 1024M 83 Linux
/dev/sda2    1023,254,2  1023,254,2     2099200   10307583    8208384 4008M 82 Linux swap
/dev/sda3    1023,254,2  1023,254,2    10307584  209715199  199407616 95.0G 83 Linux
/ # exit

# 开启指定白名单权限(仅 CAP_NET_ADMIN)
[root@docker-node1 ~]# docker run -it --rm --cap-add CAP_NET_ADMIN busybox:latest
/ # ip a a 1.2.3.4/24 dev eth0
/ # ip a
2: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 76:5d:ef:5e:22:6c 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 实践

编写第一个 Compose 文件

[root@docker-node1 ~]# mkdir  /root/liouo
[root@docker-node1 ~]# cd liouo/
[root@docker-node1 liouo]# vim docker-compose.yml
services:
  web:
    image: nginx:1.23
    ports:
      - "80:80"
    volumes:
      - yu:/data
  dbserver:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: yu
volumes:
  yu:

[root@docker-node1 liouo]# docker compose  up		#会占用终端的
[root@docker-node1 liouo]# docker compose  down	
[root@docker-node1 liouo]# docker compose  ps
NAME      IMAGE     COMMAND   SERVICE   CREATED   STATUS    PORTS

[root@docker-node1 liouo]# docker compose -f /root/liouo/docker-compose.yml up -d
[+] up 2/2
 ✔ Container liouo-web-1      Started                                                                    0.5s
 ✔ Container liouo-dbserver-1 Started                                                                    0.5s
[root@docker-node1 liouo]# docker compose stop
[+] stop 2/2
 ✔ Container liouo-dbserver-1 Stopped                                                                    2.3s
 ✔ Container liouo-web-1      Stopped                                                                    0.2s
[root@docker-node1 liouo]# docker compose start
[+] start 2/2
 ✔ Container liouo-web-1      Started                                                                    0.3s
 ✔ Container liouo-dbserver-1 Started                                                                    0.4s
[root@docker-node1 liouo]# docker compose down
[+] down 3/3
 ✔ Container liouo-web-1      Removed                                                                    0.2s
 ✔ Container liouo-dbserver-1 Removed                                                                    1.6s
 ✔ Network liouo_default      Removed                                                                    0.2s
[root@docker-node1 liouo]# docker compose logs web

常用命令速查

命令说明
docker compose up -d在后台启动所有服务
docker compose down停止并移除所有容器、网络
docker compose ps查看当前项目下的容器状态
docker compose stop停止所有容器,但不移除
docker compose start启动已停止的容器
docker compose restart重启所有容器
docker compose logs <服务名>查看指定服务的日志
docker compose -f <文件路径> up使用指定路径的 Compose 文件

Compose 中构建镜像

[root@docker-node1 ~]# mkdir  /root/liouo
[root@docker-node1 ~]# cd liouo/
[root@docker-node1 liouo]# vim webserver			#编写dockerfile
FROM nginx:1.23
COPY index.html /usr/share/nginx/html

[root@docker-node1 liouo]# echo liouo > index.html

[root@docker-node1 liouo]# vim docker-compose.yml
services:
  web:
    build:
      context: .
      dockerfile: webserver
    image: webserver:v1

[root@docker-node1 liouo]# docker compose  build		#提前构建
[+] Building 0.1s (9/9) FINISHED
...
 => naming to docker.io/library/webserver:v1

[root@docker-node1 liouo]# docker images
webserver:v1                                   f5622bce7add        142MB             0B

Compose 的 yml 文件 services 部分

端口暴露及端口映射
[root@docker-node1 liouo]# vim docker-compose.yml
services:
  web:
    build:
      context: .
      dockerfile: webserver
    image: webserver:v1
    expose:
      - 1234
      - 4321
    ports:
      - "80:80"

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose ps
NAME          IMAGE          COMMAND                   SERVICE   CREATED         STATUS         PORTS
liouo-web-1   webserver:v1   "/docker-entrypoint.…"   web       8 seconds ago   Up 7 seconds   1234/tcp, 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 4321/tcp
[root@docker-node1 liouo]# curl http://localhost:80
liouo
环境变量设定
[root@docker-node1 liouo]# vim docker-compose.yml
services:
  web:
    build:
      context: .
      dockerfile: webserver
    image: webserver:v1
    expose:
      - 1234
      - 4321
    ports:
      - "80:80"

  DB:
    image: mysql:8.0
    container_name: dbserver
    environment:
      MYSQL_ROOT_PASSWORD: yu

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose exec -it DB /bin/sh
sh-5.1# mysql -uroot -pyu
mysql> quit
数据卷
[root@docker-node1 liouo]# vim docker-compose.yml
services:
  test:
    image: busybox:latest
    container_name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 20000
    volumes:
      - /etc/passwd:/passwd:ro

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose exec -it test /bin/sh
/ # echo haha > passwd
/bin/sh: can't create passwd: Read-only file system
网络设定
services:
  test:
    image: busybox:latest
    container_name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 20000
    volumes:
      - /etc/passwd:/passwd:ro
    network_mode: none					#在此参数中写入的网络是docker原生网络
#    networks:							#在networks中指定自建网络,但是mynet1需要通过项目构建
#      - mynet1

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose  exec -it test /bin/sh
/ # 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
/ # exit

Compose 的 yml 文件项目部分

网络(default 网络使用已存在网络)
[root@docker-node1 liouo]# docker network create  mynet1
[root@docker-node1 liouo]# docker network create  mynet2
[root@docker-node1 liouo]# vim docker-compose.yml
services:
  test:
    image: busybox:latest
    container_name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 20000

networks:
  default:
    external: true						#使用已经存在的docker资源
    name: mynet2

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose  exec -it test /bin/sh
/ # ip a
2: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    inet 172.20.0.2/16 brd 172.20.255.255 scope global eth0
自建网络
[root@docker-node1 timinglee]# docker network rm  mynet1 mynet2

[root@docker-node1 liouo]# cat docker-compose.yml
services:
  test:
    image: busybox:latest
    container_name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 20000
    networks:
      - mynet1
      - mynet2

networks:
  default:
    external: true
    name: bridge

  mynet1:
    driver: bridge
  mynet2:
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.254

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker compose exec -it test /bin/sh
/ # ip a
2: eth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
3: eth1@if23: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    inet 172.28.0.1/16 brd 172.28.255.255 scope global eth1
存储部分
[root@docker-node1 timinglee]# docker volume ls
DRIVER    VOLUME NAME

[root@docker-node1 timinglee]# vim docker-compose.yml
services:
  test:
    image: busybox:latest
    container_name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 20000
    networks:
      - mynet1
      - mynet2
    volumes:
      - yuvol1:/yu

networks:
  default:
    external: true
    name: bridge
  mynet1:
    driver: bridge
  mynet2:
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          gateway: 172.28.0.254

volumes:
  yuvol1:
    name: liouo

[root@docker-node1 liouo]# docker compose up -d
[root@docker-node1 liouo]# docker volume ls
DRIVER    VOLUME NAME    
local     liouo

[root@docker-node1 liouo]# touch /var/lib/docker/volumes/liouo/_data/file
[root@docker-node1 liouo]# docker compose exec -it test /bin/sh
/ # ls yu/
file

企业项目实战(HAProxy 负载均衡)

# 手动上传 haproxy-2.3.tar.gz 或使用 rpm 提取配置
[root@docker-node1 mnt]# dnf install haproxy --downloadonly  --downloaddir /mnt -y
[root@docker-node1 mnt]# cd /mnt/
[root@docker-node1 mnt]# ls
haproxy-2.4.22-4.el9.x86_64.rpm

[root@docker-node1 mnt]# rpm2cpio  haproxy-2.4.22-4.el9.x86_64.rpm  | cpio -id
13489[root@docker-node1 mnt]# ls
etc  haproxy-2.4.22-4.el9.x86_64.rpm  usr  var
[root@docker-node1 mnt]# mkdir  /conf
[root@docker-node1 mnt]# vim etc/haproxy/haproxy.cfg
listen webserver_80
    bind *:80
    mode http
    server webserver1 web1:80 check inter 3s fall 3 rise 5
    server webserver2 web2:80 check inter 3s fall 3 rise 5

[root@docker-node1 mnt]# cp  etc/haproxy/haproxy.cfg  /conf/

[root@docker-node1 timinglee]# vim docker-compose.yml
services:
  web1:
    image: nginx:1.23
    container_name: web1
    networks:
      - mynet1
    volumes:
      - "webdata1:/usr/share/nginx/html"
  web2:
    image: nginx:1.23
    container_name: web2
    networks:
      - mynet1
    volumes:
      - "webdata2:/usr/share/nginx/html"

  haproxy:
    image: haproxy:2.3
    container_name: haproxy
    networks:
      - mynet1
    volumes:
      - "/conf/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg"
    ports:
      - "80:80"

networks:
  mynet1:
    driver: bridge

volumes:
  webdata1:
    name: webdata1
  webdata2:
    name: webdata2

[root@docker-node1 timinglee]# docker compose up -d

[root@docker-node1 timinglee]#  echo web1 > /var/lib/docker/volumes/webdata1/_data/index.html
[root@docker-node1 timinglee]# echo web2 > /var/lib/docker/volumes/webdata2/_data/index.html

# 测试负载均衡
[root@docker-node1 liouo]# docker compose up -d
[+] up 3/3
 ✔ Container web2    Running                                                                                           0.0s
 ✔ Container haproxy Running                                                                                           0.0s
 ✔ Container web1    Running                                                                                           0.0s
[root@docker-node1 liouo]# for i in {1..10}; do curl 172.25.254.10; done
web1
web2
web1
web2
web1
web2
web1
web2
web1
web2

十三、实验中遇到的问题及解决方法

问题1:dnf install docker-ce 提示 GPG 校验失败

现象:由于未配置 GPG key,仓库 gpgcheck=0 可临时绕过,但生产建议导入官方 key。

解决:在 repo 文件中设置 gpgcheck=0(测试环境),或导入 Docker 官方 GPG key。


问题2:普通用户运行 docker ps 提示权限不足

现象Got permission denied while trying to connect to the Docker daemon socket

原因:Docker 守护进程以 root 用户运行,socket 文件 /var/run/docker.sock 属主为 root:docker。

解决:将用户加入 docker 组:usermod -aG docker $USER,然后重新登录。


问题3:docker save -o 导出的镜像在另一台机器 load 后无法运行

现象docker run 提示 no matching manifest for linux/amd64

原因:多平台镜像(如 --platform)导出时未包含完整 manifest。

解决:导出时使用 docker save -o image.tar image:tag(默认导出当前平台的镜像),或者先 docker pull --platform linux/amd64 再导出。


问题4:docker exec -it 进入容器后无法使用 vim 等编辑器

原因:基础镜像如 busyboxalpine 未安装编辑器。

解决:使用 docker exec -it container sh 后,apk add vim(Alpine)或 apt-get update && apt-get install vim(Ubuntu),或者在 Dockerfile 中预装。


问题5:ADD bin.tar.gz 解压后目录结构不符合预期

现象:解压后 /mnt/etc/passwd 而非 /mnt/passwd

原因tar 打包时包含了绝对路径或父目录。

解决:打包时使用 tar -C / -czf bin.tar.gz etc/passwd(不保留绝对路径),或通过 RUN 手动移动文件。


问题6:多阶段构建时第二阶段 COPY --from=build 找不到文件

解决:确保第一阶段中文件被正确复制到镜像内,且路径正确。使用 --from=build 时,build 是第一阶段 AS build 定义的名称。


问题7:搭建私有 Registry 时 docker pushhttp: server gave HTTP response to HTTPS client

原因:Docker 客户端默认使用 HTTPS,而 Registry 仅监听 HTTP。

解决:在 /etc/docker/daemon.json 中添加 "insecure-registries": ["172.25.254.10:5000"],然后 systemctl restart docker


问题8:使用 --network host 后容器内无法访问宿主机服务

原因--network host 使容器共享宿主机网络栈,localhost 指向宿主机本身,但若服务监听 127.0.0.1 而非 0.0.0.0,容器内无法访问。

解决:将宿主机服务绑定到 0.0.0.0,或使用 --network host 时直接通过宿主机 IP 访问。


问题9:macvlan 跨主机通信时容器无法 ping 通对端

可能原因与解决

  • 宿主机网卡未开启混杂模式 → ip link set eth1 promisc on(或检查 PROMISC 标志)
  • 交换机未允许多个 MAC 地址通过同一端口 → 在虚拟机环境通常支持,检查 arp
  • 子网、网关配置不一致 → 确保多台宿主机上 macvlan 使用同一子网和网关

问题10:docker run 限制内存后容器被 Killed

现象dd 写入超过限制时进程被杀。

原因:容器内存达到限制,触发 OOM Killer。

解决:调整 --memory--memory-swap 值,或优化应用内存使用。


问题11:docker compose up -d 后服务无法启动,提示 port is already allocated

原因:宿主机端口已被其他容器或进程占用。

解决netstat -tulnp | grep <port> 查找占用进程,修改 compose 文件中的 ports 映射,或停止占用容器。


问题12:Compose 中自定义网络与 external 网络混合使用时容器无法解析服务名

原因external 网络不会自动创建 DNS 记录,自定义网络内部可以通过服务名通信。

解决:将需要互相通信的服务放在同一个自定义网络(非 external),或使用 links(淘汰)或容器 IP。


问题13:Harbor 安装后无法登录,提示 403 Forbidden

原因:Harbor 数据库未初始化或管理员密码错误。

解决:检查 harbor.yml 中的 harbor_admin_password,执行 ./prepare 后重新 ./install.sh。删除容器卷 /data/database 重新初始化。


问题14:--cap-add 添加 NET_ADMIN 后仍无法修改 IP

原因:容器内默认 eth0 可能来自 Docker 网桥,修改 IP 会影响宿主机网络。

解决:使用 macvlan 或 ipvlan 驱动,或仅测试 ip addr add 添加别名(不影响通信)。


四、经验与建议

  1. 环境准备:安装 Docker 后务必配置镜像加速器(国内),否则拉取镜像极慢。
  2. 镜像构建:优先使用多阶段构建,最终镜像仅包含运行所需文件,可减少 50%~80% 体积。
  3. 网络选择:跨主机容器通信推荐 macvlan/ipvlan 或 Overlay(Swarm/K8s),避免直接映射物理网卡。
  4. 安全实践
    • 避免 --privileged,按需添加 --cap-add
    • 长期运行的容器使用 --restart=always
    • 敏感信息使用 Docker secret 或环境变量注入(非硬编码)
  5. 数据持久化
    • 数据库等有状态服务使用命名卷或宿主机路径挂载
    • 定期备份卷内容(tardocker cp
  6. Compose 使用技巧
    • 开发环境用 up -d,调试用 up 前台查看日志
    • 定义 depends_on 控制启动顺序(但等待服务可用仍需应用层重试)
    • 使用 profiles 区分生产/测试环境
  7. 故障排查命令
    • docker logs <container> 查看输出
    • docker inspect <container> 查看元数据
    • docker exec -it <container> /bin/sh 进入现场
    • docker events 监控实时事件

更多推荐