Docker实战全记录:安装到安全加固
实验目标回顾
通过本章实验,我们将系统掌握以下内容:
- 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.repo、br_netfilter、sysctl 内核参数、registry-mirrors |
| 镜像操作 | pull、images、save/load、history、rmi、tag、push |
| 容器操作 | run(-d、-it、-p、-v、–name)、ps -a、start/stop/kill/rm、exec、cp、commit、inspect、logs |
| Dockerfile 指令 | FROM、LABEL、COPY/ADD(解压区别)、ENV、RUN(shell/exec)、EXPOSE、VOLUME、WORKDIR、CMD(shell/JSON)、ENTRYPOINT |
| 镜像优化 | 合并 RUN 层、多阶段构建(COPY --from)、distroless 基础镜像 |
| 私有仓库 | Registry 镜像、insecure-registries、TLS 证书、htpasswd 认证;Harbor 离线安装 |
| 网络 | bridge(默认)、host、none;自定义桥接;docker network connect;--network container:;macvlan 跨主机 |
| 存储卷 | -v host_path:container_path:ro;docker volume create;--volumes-from;tar 备份恢复 |
| 资源限制 | --cpu-period/--cpu-quota(CPU 份额)、--device-write-bps(磁盘 IO);cgroup 查询 |
| 安全 | --privileged(全部特权)、--cap-add(细粒度);cgroup v2 切换(可选) |
| Compose | services、build、ports、expose、environment、volumes、networks、volumes(命名卷)、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 常用命令
快速记忆口诀或思路
-
按动词含义记:
- 搜索:
search - 拉取:
pull - 推送:
push - 运行:
run - 列出:
ps/images - 停止:
stop/kill - 删除:
rm/rmi - 导出:
save -o - 导入:
load -i - 进入:
attach/exec - 复制:
cp - 提交:
commit - 查看详情:
inspect/history
- 搜索:
-
按对象分类:
- 镜像操作:
images,search,pull,history,save,load,rmi,commit - 容器操作:
run,ps,start,stop,kill,rm,attach,exec,inspect,logs,cp
- 镜像操作:
-
选项速记:
-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 .
验证:
- 运行容器并保持运行:
[root@docker-node1 docker]# docker run -it --name test --rm liouo:v7 - 另开终端,查看容器挂载信息:
得到匿名卷的宿主机路径(如[root@docker-node1 ~]# docker inspect test | grep -A10 Mounts/var/lib/docker/volumes/.../_data)。

- 在宿主机该目录下创建文件:
[root@docker-node1 ~]# cd /var/lib/docker/volumes/.../_data [root@docker-node1 ~]# touch yu{1..5} - 回到容器内查看:
[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 3月 15 11:05 bin.tar.gz
-rw-r--r-- 1 root root 108 3月 15 12:20 centos7.repo
-rw-r--r-- 1 root root 630 3月 15 16:38 Dockerfile
-rw-r--r-- 1 root root 6 3月 15 10:14 liouo
-rw-r--r-- 1 root root 1108958 12月 14 2022 nginx-1.23.3.tar.gz # 要有这个
-rw-r--r-- 1 root root 3 3月 15 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 3月 15 16:50 centos7.tar
-rw-r--r-- 1 root root 22456832 3月 15 16:48 debian11.tar.gz
-rw-r--r-- 1 root root 1292 3月 15 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 3月 15 10:15 buildx
-rw------- 1 root root 99 3月 15 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 3月 21 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 3月 21 12:30 ca_download
drwxr-xr-x 2 root root 48 3月 21 12:22 certs
drwx------ 3 systemd-coredump systemd-coredump 18 3月 21 12:31 database
drwxr-xr-x 2 10000 10000 6 3月 21 12:30 job_logs
drwxr-xr-x 2 systemd-coredump systemd-coredump 22 3月 21 15:54 redis
drwxr-xr-x 3 10000 10000 20 3月 21 15:12 registry
drwxr-xr-x 6 root root 58 3月 21 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 等编辑器
原因:基础镜像如 busybox、alpine 未安装编辑器。
解决:使用 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 push 报 http: 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 添加别名(不影响通信)。
四、经验与建议
- 环境准备:安装 Docker 后务必配置镜像加速器(国内),否则拉取镜像极慢。
- 镜像构建:优先使用多阶段构建,最终镜像仅包含运行所需文件,可减少 50%~80% 体积。
- 网络选择:跨主机容器通信推荐 macvlan/ipvlan 或 Overlay(Swarm/K8s),避免直接映射物理网卡。
- 安全实践:
- 避免
--privileged,按需添加--cap-add - 长期运行的容器使用
--restart=always - 敏感信息使用 Docker secret 或环境变量注入(非硬编码)
- 避免
- 数据持久化:
- 数据库等有状态服务使用命名卷或宿主机路径挂载
- 定期备份卷内容(
tar或docker cp)
- Compose 使用技巧:
- 开发环境用
up -d,调试用up前台查看日志 - 定义
depends_on控制启动顺序(但等待服务可用仍需应用层重试) - 使用
profiles区分生产/测试环境
- 开发环境用
- 故障排查命令:
docker logs <container>查看输出docker inspect <container>查看元数据docker exec -it <container> /bin/sh进入现场docker events监控实时事件
更多推荐
所有评论(0)