Docker 是一款开源的容器化平台,通过将应用及其依赖打包成轻量级、可移植的镜像,实现一次构建,到处运行。相比传统虚拟机,Docker 容器共享宿主机内核,启动快、资源占用低,常用于开发环境统一、应用快速部署、微服务架构落地。
一、Docker 介绍
1. 什么是 Docker
Docker 是管理容器的引擎,为应用打包、部署平台,而非单纯的虚拟化技术
特点
|---------|---------------------------------------------|
| 轻量级虚拟化 | Docker 容器相较于传统的虚拟机更加轻量和高效,能够快速启动和停止,节省系统资源 |
| 一致性 | 确保应用程序在不同的环境中(如开发、测试、生产)具有一致的运行表现 |
| 可移植性 | 可轻松地将 Docker 容器从一个平台迁移到另一个平台,无需担心依赖和环境配置的差异 |
| 高效的资源利用 | 多个 Docker 容器可以共享主机的操作系统内核,从而更有效地利用系统资源 |
| 易于部署和扩展 | 能够快速部署新的应用实例,并且可以根据需求轻松地进行水平扩展 |

2. Docker 应用场景
在企业中 docker 作为业务的最小载体而被广泛应用,通过 docker 企业可以更效率的部署应用并更节省资源
- IaaS(Infrastructure as a Service):基础设施即服务
- PaaS(Platform as a Service):平台即服务
- SaaS(Software as a Service):软件运营服务

容器工作方式

3. Docker 与虚拟机区别
|------|------------------|-----------|
| | 虚拟机 | docker 容器 |
| 操作系统 | 宿主机上运行虚拟机 OS | 共享宿主机 OS |
| 存储 | 镜像较大(GB) | 镜像小(MB) |
| 性能 | 操作系统额外的 cpu、内存损耗 | 几乎无性能损耗 |
| 移植性 | 笨重、与虚拟化技术耦合度高 | 轻量、灵活迁移 |
| 隔离性 | 完全隔离 | 安全隔离 |
| 部署 | 慢,分钟级 | 块,秒级 |
| 运行密度 | 一般几十个 | 单机支持上千容器 |

二、部署 Docker
docker 站点:https://docs.docker.com/
1. 配置软件创库
docker 软件创库链接:https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
利用阿里云部署软件仓库
[root@docker-N1 ~]# 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-N1 ~]# dnf makecache

2. 安装 Docker
安装 docker-ce 并激活内核网络,然后启动 docker 服务
[root@docker-N1 ~]# dnf install docker-ce -y
[root@docker-N1 ~]# vim /lib/systemd/system/docker.service
15 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@docker-N1 ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf
[root@docker-N1 ~]# modprobe -a br_netfilter
[root@docker-N1 ~]# 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-N1 ~]# sysctl --system
[root@docker-N1 ~]# systemctl enable --now docker.service


三、 Docker 基本操作
1. 配置 Docker 加速
因为 Docker Hub 在国外,国内直接访问非常慢,甚至连接不上,所以要配置 docker 加速
配置加速并重启 docker 服务
[root@docker-N1 ~]# cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn"
],
"ipv6": false,
"dns": ["223.5.5.5", "114.114.114.114"]
}
EOF
[root@docker-N1 ~]# systemctl restart docker.service
[root@docker-N1 ~]# docker info | grep -A 5 "Registry Mirrors"

2. 镜像管理
(1)搜索镜像
NAME 镜像名称,DESCRIPTION 镜像说明,STARS 点赞数量,OFFICIAL 是否是官方
[root@docker-node1 ~]# docker search nginx
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 21206 [KO]
(2)拉取镜像
有时候在搜索镜像因为网络问题搜不到镜像,但也是可以下载镜像的
下载镜像;可以不指定版本、指定版本和指定版本最小发型安装下载
[root@docker-N1 ~]# docker pull busybox
[root@docker-N1 ~]# docker pull nginx:1.26
[root@docker-N1 ~]# docker pull nginx:1.26-alpine

(3)查看镜像
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker history busybox:latest
[root@docker-N1 ~]# docker image inspect nginx:1.26

(4)导出镜像
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker save -o nginx1.26-min.tar nginx:1.26
[root@docker-N1 ~]# docker save $(docker images --format "{{.Repository}}:{{.Tag}}") -o images.tar
[root@docker-N1 ~]# ls

(5)删除镜像
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker rmi nginx:1.26-alpine
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker rmi $(docker images -q)

(6)导入镜像
[root@docker-N1 ~]# ls
[root@docker-N1 ~]# docker load -i nginx1.26-min.tar
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker load -i images.tar
[root@docker-N1 ~]# docker images

3. 容器操作
(1)运行容器
运行容器
-
-d 后台运行容器
-
-it 交互式运行容器(交互式运行容器退出默认停止容器)
[root@docker-N1 ~]# docker run -d --name web nginx:1.26
[root@docker-N1 ~]# docker run -it busybox:latest

(2)退出与进入容器
退出容器
-
按"ctrl+d"退出并停止容器
-
按"ctrl+p+q"退出不停止容器(后台运行)
[root@docker-N1 ~]# docker run -it busybox:latest
/ # #按ctrl+d
[root@docker-N1 ~]# docker ps -a
[root@docker-N1 ~]# docker start romantic_fermi
[root@docker-N1 ~]# docker attach romantic_fermi
/ # #按ctrl+p+q

(3)查看容器
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker ps -a

(4)停止与启动容器
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker stop web
[root@docker-N1 ~]# docker ps -a
[root@docker-N1 ~]# docker start web
[root@docker-N1 ~]# docker ps

(5)删除容器
只有已停止运行容器才能删除
- kill 强制停止
- stop 停止
"docker container prune -f"删除所有停止的容器
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker kill quizzical_almeida
[root@docker-N1 ~]# docker ps -a
[root@docker-N1 ~]# docker rm quizzical_almeida
[root@docker-N1 ~]# docker ps -a

(6)创建新进程
因为有些镜像使用 attach 进入容器后,无法用"ctrl+d"和"ctrl+p+q"来退出,只能用"ctrl+c"来退出,但是 attach 进入后是交互式运行,直接退出会导致容器停止。
所以需要创建子进程来对容器进行操作调试。
在用 attach 进入主进程,也不建议用"ctrl+d"(虽然无效),但如果有效会导致容器停止。
[root@docker-N1 ~]# docker attach web
#按ctrl+c退出
[root@docker-N1 ~]# docker start web
[root@docker-N1 ~]# docker exec -it web /bin/bash
root@279d2827edf7:/# #按ctrl+d退出
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker exec -it web /bin/bash
root@279d2827edf7:/# #按ctrl+p+q退出
[root@docker-N1 ~]# docker ps

(7)在运行容器中执行命令
交互式和非交互式执行命令
[root@docker-N1 ~]# docker exec web touch /root/haha
[root@docker-N1 ~]# docker exec web ls /root
[root@docker-N1 ~]# docker exec -it web /bin/bash
root@279d2827edf7:/# ls /root/
root@279d2827edf7:/# touch /root/hehe
root@279d2827edf7:/# ls /root/

(8)容器内容提交
容器删除问题
-
默认情况下,容器被删除后,在容器中的所有操作都会被清理,包括要保存的文件
-
如果想永久保存,那么我们需要把动作提交,提交后会生成新的镜像
-
当我们在运行新镜像后即可看到我们提交的内容
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker stop web
[root@docker-N1 ~]# docker container prune -f
[root@docker-N1 ~]# docker run -d --name web nginx:1.26
[root@docker-N1 ~]# docker exec web ls /root

提交内容,实现永久保存
[root@docker-N1 ~]# docker exec -it web /bin/bash
root@c62c3a13ef6e:/# touch /root/haha
root@c62c3a13ef6e:/# ls /root/
#按ctrl+p+q退出
[root@docker-N1 ~]# docker commit -m "add haha" web nginx-haha
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker run -it --name test nginx-haha:latest
2026/03/24 09:30:25 [notice] 1#1: start worker process 23 #按ctrl+p+q退出
[root@docker-N1 ~]# docker exec test ls /root

(9)系统与容器的传输
[root@docker-N1 ~]# docker exec test ls /root
[root@docker-N1 ~]# docker cp test:/root/haha /mnt
[root@docker-N1 ~]# ls /mnt/
[root@docker-N1 ~]# docker cp /etc/passwd test:/root
[root@docker-N1 ~]# docker exec test ls /root

(10)容器的外部网络访问
容器有自己的独立网络空间,外部无法访问,所以需要做端口映射
开启并设置端口映射后,通过浏览器访问主机ip进行测试
[root@docker-N1 ~]# docker pull timinglee/mario
[root@docker-N1 ~]# docker history timinglee/mario
[root@docker-N1 ~]# docker run -d --name mario -p 80:8080 timinglee/mario:latest


四、Docker 镜像构建
1. Docker 镜像简介
(1)镜像架构
- 共享宿主机的 kernel
- base 镜像提供的是最小的 Linux 发行版
- 同一 docker 主机支持运行多种 Linux 发行版
- 采用分层结构的最大好处是:共享资源

(2)镜像运行原理
- Copy-on-Write 可写容器层(Container 透明层)
- 容器层以下所有镜像层都是只读的
- docker 从上往下依次查找文件
- 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
- 一个镜像最多127层

(3)镜像获取方式
- 基本镜像通常由软件官方提供
- 企业镜像可以用官方镜像+ Dockerfile 来生成
- 系统关于镜像的获取动作有两种: docker pull 镜像地址、docker load --i 本地镜像包
2. 构建镜像
构建镜像(build)和内容提交(commit)很像,但是"docker commit"从运行的容器创建镜像,操作不透明且无法复现;而"docker build"通过 Dockerfile 文件构建镜像,过程透明、可版本控制、可重复执行。
(1)内容提交对照
内容提交的镜像查看提交历史,没有明确的指令操作,导致信息不透明
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker run -it --name test busybox:latest
/ # #ctrl+p+q退出
[root@docker-N1 ~]# docker exec test touch /root/haha
[root@docker-N1 ~]# docker exec test ls /root
[root@docker-N1 ~]# docker commit -m "add haha" test busybox-test
[root@docker-N1 ~]# docker history busybox-test:latest
[root@docker-N1 ~]# docker history nginx:1.26

(2)构建镜像参数
|------------|---------------------------------------------------------------------------------------------------------------|
| FROM | - 指定 base 镜像 eg:FROM busybox:version |
| COPY | - 复制文件 eg:COPY file /file 或者 COPY ["file","/"] |
| MAINTAINER | - 指定作者信息,比如邮箱 eg:MAINTAINER user@example.com - 在最新版的 docker 中用 LABEL KEY="VALUE" 代替 |
| ADD | - 功能和 copy 相似,指定压缩文件或 url eg: ADD test.tar /mnt 或者 eg:ADD http://ip/test.tar /mnt |
| ENV | - 指定环境变量 eg:ENV FILENAME test |
| EXPOSE | - 暴漏容器端口 eg:EXPOSE 80 |
| VOLUME | - 申明数据卷,通常指数据挂载点 eg:VOLUME ["/var/www/html"] |
| WORKDIR | - 切换路径 eg:WORKDIR /mnt |
| RUN | - 在容器中运行的指令 eg: touch file |
| CMD | - 在启动容器时自动运行动作可以被覆盖 eg:CMD echo FILENAME 会调用 shell解析 eg:CMD \["/bin/sh","-c","echo FILENAME"] 不调用 shell 解析 |
| ENTRYPOINT | - 和 CMD 功能和用法类似,但动作不可被覆盖 |
(3)构建简单镜像
创建一个构建镜像的目录,然后创建一个必须名为"Dockerfile"的文件,通过编辑给文件定义镜像内容(FROM 和 COPY 参数)
-
FROM 参数:指定基础镜像
-
COPY 参数: 复制文件到镜像
[root@docker-N1 ~]# mkdir docker
[root@docker-N1 ~]# cd docker/
[root@docker-N1 docker]# vim Dockerfile
[root@docker-N1 docker]# echo timinglee > timinglee
[root@docker-N1 docker]# cat timinglee
[root@docker-N1 docker]# docker build -t timinglee:v1 .
[root@docker-N1 docker]# docker images
[root@docker-N1 docker]# docker history timinglee:v1



(4)使用镜像参数
LABEL 参数
-
添加标签
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 COPY timinglee /root
3 LABEL creater=lee
[root@docker-N1 docker]# docker build -t timinglee:v2 .
[root@docker-N1 docker]# docker history timinglee:v2


ADD 参数
-
复制文件或目录
-
与 COPY 参数相似,但 ADD 参数能解压,COPY 参数不能解压
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY timinglee /root
4 ADD lee /root
[root@docker-N1 docker]# echo lee > lee
[root@docker-N1 docker]# docker build -t timinglee:v3 .
[root@docker-N1 docker]# docker history timinglee:v3
[root@docker-N1 docker]# docker run -it --name lee3 timinglee:v3
/ # ls /root

ADD 解压功能
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
[root@docker-N1 docker]# tar zcf bin.tar.gz /bin
[root@docker-N1 docker]# docker build -t timinglee:v4 .
[root@docker-N1 docker]# docker history timinglee:v4
[root@docker-N1 docker]# docker run -it --name lee4 timinglee:v4
/ # ls root/
/ # ls mnt/

ENV 参数
-
设置环境变量
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/$NAME"]
[root@docker-N1 docker]# docker build -t timinglee:v5 .
[root@docker-N1 docker]# docker history timinglee:v5
[root@docker-N1 docker]# docker run -it --name lee5 timinglee:v5
/ # ls /root/


EXPOSE 参数
-
设置容器端口号
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/$NAME"]
7 EXPOSE 8080
[root@docker-N1 docker]# docker build -t timinglee:v6 .
[root@docker-N1 docker]# docker history timinglee:v6

VOLUME 参数
-
声明数据卷
-
持久化存储功能:即使容器删除了,该数据卷(目录)的数据也不会删除
-
数据共享功能:系统目录"/var/lib/docker/volumes/xxxx/_data"与容器目录"/mnt"的数据同步
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/$NAME"]
7 EXPOSE 8080
8 VOLUME "/mnt"
[root@docker-N1 docker]# docker build -t timinglee:v7 .
[root@docker-N1 docker]# docker history timinglee:v7
[root@docker-N1 docker]# docker run -it --name lee7 timinglee:v7
/ # #ctrl+p+q退出
[root@docker-N1 docker]# docker inspect lee7 | grep -i mounts -A10

数据共享(数据同步)
[root@docker-N1 docker]# touch /var/lib/docker/volumes/2baf6037af43f39c9d41df795b9ae71530443f0b2313d30a380c19bddad7164a/_data/{1..5}.txt
[root@docker-N1 docker]# ls /var/lib/docker/volumes/2baf6037af43f39c9d41df795b9ae71530443f0b2313d30a380c19bddad7164a/_data/
[root@docker-N1 docker]# docker exec lee7 ls /mnt
[root@docker-N1 docker]# docker exec lee7 rm -rf /mnt/{3..5}.txt
[root@docker-N1 docker]# docker exec lee7 ls /mnt
[root@docker-N1 docker]# ls /var/lib/docker/volumes/2baf6037af43f39c9d41df795b9ae71530443f0b2313d30a380c19bddad7164a/_data/

持久化存储
-
当容器启动后,会在系统自动生成的数据卷是匿名卷
-
当容器删除后,第一个匿名卷不会消失,但容器重启后会再生成一个新匿名卷
-
这会导致容器的数据消失,并未持久化
-
所以需要在开启容器时就命名数据卷,保证重新启动容器时挂载是同一个卷
#不指定数据卷导致数据丢失
[root@docker-N1 docker]# docker kill lee7
[root@docker-N1 docker]# docker rm lee7
[root@docker-N1 docker]# docker run -it --name lee7 timinglee:v7
/ # ls /mnt/
/ # #ctrl+p+q退出
[root@docker-N1 docker]# docker inspect lee7 | grep -i mounts -A10
[root@docker-N1 docker]# ls /var/lib/docker/volumes/2baf6037af43f39c9d41df795b9ae71530443f0b2313d30a380c19bddad7164a/_data/

#方法一:指定老匿名卷
[root@docker-N1 docker]# docker kill lee7
[root@docker-N1 docker]# docker rm lee7
[root@docker-N1 docker]# docker run -it --name lee7 -v /var/lib/docker/volumes/2baf6037af43f39c9d41df795b9ae71530443f0b2313d30a380c19bddad7164a/_data:/mnt timinglee:v7
/ # ls /mnt/
/ # #ctrl+d退出并停止
[root@docker-N1 docker]# docker rm lee7

#方法二:自定义数据卷
[root@docker-N1 docker]# docker run -it --name lee7 -v mydata:/mnt timinglee:v7
/ # touch /mnt/{a..e}.txt
/ # #ctrl+d退出并停止
[root@docker-N1 docker]# docker rm lee7
[root@docker-N1 docker]# docker run -it --name lee7 -v mydata:/mnt timinglee:v7
/ # ls /mnt/
/ # #ctrl+d退出并停止
[root@docker-N1 docker]# docker inspect lee7 | grep -i mounts -A10
[root@docker-N1 docker]# ls /var/lib/docker/volumes/mydata/_data

WORKDIR 参数
-
指定进入容器时直接进入到指定路径
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/$NAME"]
7 EXPOSE 8080
8 VOLUME "/mnt"
9 WORKDIR "/mnt"
[root@docker-N1 docker]# docker history timinglee:v8
[root@docker-N1 docker]# docker run -it --name lee8 --rm timinglee:v8

CMD
-
容器启动时执行的默认命令
-
命令会被覆盖
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/NAME"] 7 EXPOSE 8080 8 VOLUME "/mnt" 9 WORKDIR "/mnt" 10 CMD ["/bin/sh","-c","/bin/echo NAME"]
[root@docker-N1 docker]# docker build -t timinglee:v9 .
[root@docker-N1 docker]# docker history timinglee:v9
[root@docker-N1 docker]# docker run -it --name lee9 --rm timinglee:v9
[root@docker-N1 docker]# docker run -it --name lee9 --rm timinglee:v9 echo haha

ENTRYPOINT 参数
-
容器启动时执行的固定命令
-
命令不能被覆盖
[root@docker-N1 ~]# vim docker/Dockerfile
1 FROM busybox:latest
2 LABEL creater=lee
3 COPY bin.tar.gz /root
4 ADD bin.tar.gz /mnt
5 ENV NAME=timinglee
6 RUN ["/bin/sh","-c","touch /root/NAME"] 7 EXPOSE 8080 8 VOLUME "/mnt" 9 WORKDIR "/mnt" 10 ENTRYPOINT ["/bin/sh","-c","/bin/echo NAME"]

(5)构建 centos 可用镜像
创建一个测试容器,该容器能实现下载功能,所以需要搭配仓库
仓库源:https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/
[root@docker-N1 docker]# docker pull centos:7
[root@docker-N1 docker]# docker run -it --rm --name centos centos:7 /bin/bash
[root@f1d2c5f2f277 /]# cat /etc/centos-release
[root@f1d2c5f2f277 /]# yum install gcc -y
[root@f1d2c5f2f277 /]# exit #ctrl+d退出
[root@docker-N1 docker]# vim Dockerfile
1 FROM centos:7
2 LABEL Creater=lee
3 RUN ["/bin/bash","-c","rm -rf /etc/yum.repos.d/*"]
4 COPY centos7.repo /etc/yum.repos.d/centos7.repo
[root@docker-N1 docker]# vim centos7.repo
1 [centos7]
2 name = centos7
3 baseurl = https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/
4 gpgcheck = 0
[root@docker-N1 docker]# docker build -t centos:7-repo .
[root@docker-N1 docker]# docker run -it --rm --name centos centos:7-repo /bin/bash
[root@59d53d54131c /]# cat /etc/yum.repos.d/centos7.repo
[root@5cc43b9ef067 /]# yum install gcc -y
[root@5cc43b9ef067 /]# exit #ctrl+d退出


3. 镜像优化
在可使用镜像的同时,减少镜像的大小,可加快构建速度,提高安全性和可维护性。
(1)搭建 nginx 服务镜像
在自己的基础镜像上添加 nginx 服务,用于对比大小
nginx 链接:https://nginx.org/download/nginx-1.26.3.tar.gz
搭建 nginx 服务镜像
-
构建镜像时,可加"--no-cache"参数,不使用缓存,强制重新构建镜像
[root@docker-N1 ~]# wget https://nginx.org/download/nginx-1.26.3.tar.gz
[root@docker-N1 ~]# cd docker/
[root@docker-N1 docker]# cp ~/nginx-1.26.3.tar.gz .
[root@docker-N1 docker]# ls
[root@docker-N1 docker]# vim Dockerfile
1 FROM centos:7
2 ADD nginx-1.26.3.tar.gz /mnt
3 WORKDIR /mnt/nginx-1.26.3
4 RUN yum install -y gcc make prce-devel openssl-devel
5 RUN sed -i 's/CFLAGS="CFLAGS -g"/#CFLAGS="CFLAGS -g"/g' auto/cc/gcc
6 RUN ./configure --with-http_ssl_module --with-http_stub_status_module
7 RUN make
8 RUN make install
9 EXPOSE 80
10 VOLUME ["/usr/local/nginx/html"]
11 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@docker-N1 docker]# docker build --no-cache -t webserver:v1 .
[root@docker-N1 docker]# docker images


测试搭建出的 nginx 服务镜像是否可用
[root@docker-N1 docker]# docker run -d --rm --name web webserver:v1
[root@docker-N1 docker]# docker ps -a
[root@docker-N1 docker]# docker exec -it web /bin/bash
[root@0b85e6c39fb3 nginx-1.26.3]# echo "How are you?" > /usr/local/nginx/html/index.html
[root@0b85e6c39fb3 nginx-1.26.3]# hostname -I
[root@0b85e6c39fb3 nginx-1.26.3]# curl 172.17.0.2
[root@0b85e6c39fb3 nginx-1.26.3]# curl http://localhost
[root@0b85e6c39fb3 nginx-1.26.3]# exit
[root@docker-N1 docker]# docker inspect web | grep IPAddress
[root@docker-N1 docker]# curl 172.17.0.2
[root@docker-N1 docker]# docker stop web
[root@docker-N1 docker]# docker ps -a

综上,一般搭建出来的 nginx 服务镜像可用,但是镜像大小太大,不利于维护
所以需要减小镜像大小,方法有三个:
- 减少镜像的层数
- 清理镜像构建的中间产物
- 选择最精简的基础镜像
(1)方法一:缩减镜像层
减少镜像的层数
-
构建镜像的相同参数层数太多、编译安装完程序后的不需要的文件未删除,导致镜像太大;所以需要将层数简化,并将多余的源码和依赖文件删除
[root@docker-N1 ~]# cd docker/
[root@docker-N1 docker]# cat Dockerfile

减少层数和删除多余文件
[root@docker-N1 docker]# vim Dockerfile
1 FROM centos:7-repo
2 ADD nginx-1.26.3.tar.gz /mnt
3 WORKDIR /mnt/nginx-1.26.3
4 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 && rm -rf /mnt/nginx-1.26.3 && yum clean all
5 EXPOSE 80
6 VOLUME ["/usr/local/nginx/html"]
7 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@docker-N1 docker]# docker build --no-cache -t webserver:v2 .
[root@docker-N1 docker]# docker images


测试镜像
[root@docker-N1 docker]# docker run -d --rm --name web webserver:v2
[root@docker-N1 docker]# docker exec web hostname -I
[root@docker-N1 docker]# curl 172.17.0.2
[root@docker-N1 docker]# docker stop web

(2)方法二:多阶段构建
清理镜像构建的中间产物
-
根据第一种缩减镜像层方法,镜像大小还是和官方镜像有很大差距;所以需要使用多阶段构建(即多个临时镜像),将最终需要的东西打包到最终镜像,减小镜像大小
[root@docker-N1 docker]# vim Dockerfile
1 FROM centos:7-repo AS n1
2 ADD nginx-1.26.3.tar.gz /mnt
3 WORKDIR /mnt/nginx-1.26.3
4 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 && rm -rf /mnt/nginx-1.26.3 && yum clean all
5
6 FROM centos:7-repo
7 COPY --from=n1 /usr/local/nginx /usr/local/nginx
8 EXPOSE 80
9 VOLUME ["/usr/local/nginx/html"]
10 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]


测试镜像
[root@docker-N1 docker]# docker run -d --rm --name web webserver:v3
[root@docker-N1 docker]# docker exec web hostname -I
[root@docker-N1 docker]# curl 172.17.0.2
[root@docker-N1 docker]# docker stop web

(3)方法三:使用最精简镜像
选择最精简的基础镜像
- 多阶段构建镜像方法已经与官方的基础镜像差距很小了,如果还需要比官方的基础镜像要小,则需要搭建最精简镜像
使用google提供的最精简镜像:https://github.com/GoogleContainerTools/distroless
下载链接:gcr.io/distroless/base

导入"debian11.tar.gz"压缩包,然后导入最小 linux 镜像"gcr.io/distroless/base-debian11:latest"
- 提供一个最小化的 Linux 运行环境
- 只包含应用程序运行所需的文件
- 没有 shell、没有包管理器、没有多余工具
同时下载 nginx:1.23 版本的镜像,因为 debian11 能与 nginx1.23 版本兼容(最多 nginx1.24)
[root@docker-N1 ~]# ls
[root@docker-N1 ~]# cp debian11.tar.gz docker/
[root@docker-N1 ~]# cd docker/
[root@docker-N1 docker]# ls
[root@docker-N1 docker]# docker load -i debian11.tar.gz
[root@docker-N1 docker]# docker pull nginx:1.23
[root@docker-N1 docker]# docker images

搭建最小 nginx 服务的镜像
[root@docker-N1 docker]# vim Dockerfile
1 FROM nginx:1.23 AS n1
2 ARG TIME_ZONE
3 RUN mkdir -p /opt/var/cache/nginx && \
4 cp -a --parents /usr/lib/nginx /opt && \
5 cp -a --parents /usr/share/nginx /opt && \
6 cp -a --parents /var/log/nginx /opt && \
7 cp -aL --parents /var/run /opt && \
8 cp -a --parents /etc/nginx /opt && \
9 cp -a --parents /etc/passwd /opt && \
10 cp -a --parents /etc/group /opt && \
11 cp -a --parents /usr/sbin/nginx /opt && \
12 cp -a --parents /usr/sbin/nginx-debug /opt && \
13 cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
14 cp -a --parents /usr/lib/x86_64-linux-gnu/libpcre* /opt && \
15 cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
16 cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
17 cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
18 cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
19 cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
20 cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
21 cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
22 cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime
23
24 FROM gcr.io/distroless/base-debian11
25 COPY --from=n1 /opt /
26 EXPOSE 80 443
27 ENTRYPOINT ["nginx","-g","daemon off;"]
[root@docker-N1 docker]# docker build --no-cache -t webserver:v4 .
[root@docker-N1 docker]# docker images


测试镜像
[root@docker-N1 docker]# docker run -d --rm --name web webserver:v4
[root@docker-N1 docker]# docker ps
[root@docker-N1 docker]# docker inspect web | grep IPAddress
[root@docker-N1 docker]# curl 172.17.0.2
[root@docker-N1 docker]# docker stop web

五、Docker 镜像仓库管理
因为没有 Docker 仓库时,镜像只能以文件形式(如
docker save导出)手动拷贝到每台机器,效率低且无法统一管理版本。所以需要 Docker 仓库作为中央存储,实现镜像的统一存储、版本管理和按需拉取。
1. Docker 仓库介绍
Docker 仓库(Docker Registry) 是用于存储和分发 Docker 镜像的集中式存储库。
它就像是一个大型的镜像仓库,开发者可以将自己创建的 Docker 镜像推送到仓库中,也可以从仓库中拉取所需的镜像。
Docker 仓库可以分为公共仓库和私有仓库:
- 公共仓库,如 Docker Hub,任何人都可以访问和使用其中的镜像。许多常用的软件和应用都有在 Docker Hub 上提供的镜像,方便用户直接获取和使用。
- 私有仓库则是由组织或个人自己搭建和管理的,用于存储内部使用的、不希望公开的镜像。
通过 Docker 仓库,开发者能够方便地共享和复用镜像,加速应用的开发和部署过程。

2. Docker Hub 公共仓库
(1)Docker Hub 简介
Docker Hub 官网:https://hub.docker.com/
Docker Hub 是 Docker 官方提供的一个公共的镜像仓库服务,是 Docker 生态系统中最知名和广泛使用的镜像仓库之一,拥有大量的官方和社区贡献的镜像。
- 丰富的镜像资源:涵盖了各种常见的操作系统、编程语言运行时、数据库、Web 服务器等众多应用 的镜像。
- 官方支持:提供了由 Docker 官方维护的一些重要镜像,确保其质量和安全性。
- 社区贡献:开发者们可以自由上传和分享他们创建的镜像,促进了知识和资源的共享。
- 版本管理:对于每个镜像,通常都有多个版本可供选择,方便用户根据需求获取特定版本。
- 便于搜索:用户可以通过关键词轻松搜索到所需的镜像。


(2)准备新 Docker 主机
为了进行后面的容器实验,准备一台新主机(192.168.153.20)
[root@docker-N1 ~]# scp /etc/yum.repos.d/docker.repo root@192.168.153.20:/etc/yum.repos.d/docker.repo
[root@docker-N1 ~]# scp /lib/systemd/system/docker.service root@192.168.153.20:/lib/systemd/system/docker.service
[root@docker-N1 ~]# scp /etc/modules-load.d/docker_mod.conf root@192.168.153.20:/etc/modules-load.d/docker_mod.conf
[root@docker-N1 ~]# scp /etc/sysctl.d/docker.conf root@192.168.153.20:/etc/sysctl.d/docker.conf
[root@docker-N2 ~]# dnf install docker-ce -y
[root@docker-N2 ~]# modprobe -a br_netfilter
[root@docker-N2 ~]# sysctl --system
[root@docker-N2 ~]# systemctl enable --now docker.service
(3)创建 Docker 仓库
开启加速,访问https://hub.docker.com/注册一个仓库账号,创建自己的 docker 仓库



(4)配置虚拟机 HTTP 代理(考虑)
配置虚拟机 http 代理前提是,如果在宿主机上开了 http 代理,虚拟机的网络依旧访问不了外部环境,则需要在虚拟机里配置 http 代理;如果宿主机上开了 http 代理,虚拟机的网络能访问外部环境,则不需要配置虚拟机 http 代理。
在宿主机上运行 HTTP 代理服务(例如企业内网代理、本地调试代理),且代理地址为 http://虚拟机网段:端口号
在宿主机 Windows 上查看虚拟机的网段

在虚拟机上配置 docker 的 http 代理
[root@docker-N1 ~]# mkdir -p /etc/systemd/system/docker.service.d
[root@docker-N1 ~]# cat > /etc/systemd/system/docker.service.d/http-proxy.conf << EOF
[Service]
Environment="HTTP_PROXY=http://192.168.153.1:7897"
Environment="HTTPS_PROXY=http://192.168.153.1:7897"
Environment="NO_PROXY=localhost,127.0.0.1"
EOF
[root@docker-N1 ~]# systemctl daemon-reload
[root@docker-N1 ~]# systemctl restart docker.service
[root@docker-N1 ~]# docker login -u forget8
Password: #输入密码
[root@docker-N2 ~]# mkdir -p /etc/systemd/system/docker.service.d
[root@docker-N2 ~]# cat > /etc/systemd/system/docker.service.d/http-proxy.conf << EOF
[Service]
Environment="HTTP_PROXY=http://192.168.153.1:7897"
Environment="HTTPS_PROXY=http://192.168.153.1:7897"
Environment="NO_PROXY=localhost,127.0.0.1"
EOF
[root@docker-N2 ~]# systemctl daemon-reload
[root@docker-N2 ~]# systemctl restart docker.service

(5)上转镜像
往 docker 仓库上上转镜像,使其他主机能通过仓库拉取镜像
[root@docker-N1 ~]# docker login -u forget8
Password: #输入密码
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker tag webserver:v4 forget8/timinglee
[root@docker-N1 ~]# docker push forget8/timinglee



(6)拉取镜像
推送和拉取镜像都建立在能访问 docker 仓库基础上,在国内的主机上需要配置 http 代理
[root@docker-N2 ~]# docker images
[root@docker-N2 ~]# docker pull forget8/webserver:v4
[root@docker-N2 ~]# docker images

3. Docker 仓库工作原理
仓库中的三个角色
- index docker 索引服务,负责并维护有关用户帐户、镜像的校验以及公共命名空间的信息。
- registry docker 仓库,是镜像和图表的仓库,它不具有本地数据库以及不提供用户认证,通过 Index Auth service 的 Token 的方式进行认证。
- Registry Client Docker充当 registry 客户端来维护推送和拉取,以及客户端的授权。
(1)pull 原理
镜像拉取步骤
- 1.docker 客户端向 index 发送镜像拉去请求并完成与 index 的认证
- 2.index 发送认证 token 和镜像位置给 docker client
- 3.docker client 携带 token 和根据 index 指引的镜像位置取连接 registry
- 4.Registry 会根据 client 持有的 token 跟 index 核实身份合法性
- 5.index 确认此 token 合法性
- 6.Registry 会根据 client 的请求传递镜像到客户端

(2)push 原理
镜像上传的步骤
- 1.client向index发送上传请求并完成用户认证
- 2.index会发方token给client来证明client的合法性
- 3.client携带index提供的token连接Registry
- 4.Registry向index合适token的合法性
- 5.index证实token的合法性
- 6.Registry开始接收客户端上传过来的镜像

4. 搭建 Docker 私有仓库
因为公共仓库(如 Docker Hub)的访问速度受网络环境影响,在镜像拉取和推送时可能存在延迟。
所以对于内网环境或需要快速分发镜像的场景,搭建私有仓库能实现高速稳定的内网传输。
(1)搭建 Registry 基础仓库
Registry 是 Docker 官方提供的私有仓库工具,能在内网快速搭建镜像存储和分发服务,避免依赖外网 Docker Hub。
Registry 地址:https://docs.docker.com/retired/
下载 registry 镜像
[root@docker-N1 ~]# docker pull registry
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker history registry:latest

开启 registry 容器,向容器推送镜像
[root@docker-N1 ~]# docker run -d -p 5000:5000 -v my_registry1:/var/lib/registry --restart=always --name registry registry:latest
[root@docker-N1 ~]# docker inspect registry | grep -i mounts -A10
[root@docker-N1 ~]# docker tag webserver:v4 192.168.153.10:5000/webserver:v4
[root@docker-N1 ~]# docker push 192.168.153.10:5000/webserver:v4
[root@docker-N1 ~]# docker info | grep -i registries -A10

配置非加密端口,并再次推送镜像,然后查看镜像是否上转成功
[root@docker-N1 ~]# vim /etc/docker/daemon.json
1 {
2 "insecure-registries":["http://192.168.153.10:5000"]
3 }
[root@docker-N1 ~]# systemctl restart docker.service
[root@docker-N1 ~]# docker ps
[root@docker-N1 ~]# docker push 192.168.153.10:5000/webserver:v4
[root@docker-N1 ~]# curl http://192.168.153.10:5000/v2/_catalog

在 n2 主机上配置非加密端口,然后拉取私有仓库的镜像
[root@docker-N2 ~]# vim /etc/docker/daemon.json
1 {
2 "insecure-registries":["http://192.168.153.20:5000"]
3 }
[root@docker-N2 ~]# systemctl restart docker.service
[root@docker-N2 ~]# docker pull 192.168.153.10:5000/webserver:v4
[root@docker-N2 ~]# docker images

(2)给 Registry 添加加密证书
因为 HTTP 协议以明文传输数据,镜像内容和登录密码在网络中完全暴露,所以任何能访问网络的人都能窃取或篡改传输中的镜像。
因此需要添加加密证书,将通信升级为 HTTPS,防止数据被窃听和篡改。
创建证书
[root@docker-N1 ~]# mkdir /etc/docker/certs
[root@docker-N1 ~]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout /etc/docker/certs/timinglee.org.key -addext "subjectAltName = DNS:reg.timinglee.org" -x509 -days 365 -out /etc/docker/certs/timinglee.org.crt
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:guangxi
Locality Name (eg, city) [Default City]:guilin
Organization Name (eg, company) [Default Company Ltd]:docker
Organizational Unit Name (eg, section) []:registry
Common Name (eg, your name or your server's hostname) []:reg.timinglee.org
Email Address []:admin@timinglee.org
[root@docker-N1 ~]# openssl x509 -in /etc/docker/certs/timinglee.org.crt -noout -text

删除非加密端口配置,并开启容器 ,配置解析
-
方案1:如果虚拟机之前因网络环境原因配置了 HTTP 代理,而在访问内网私有仓库时,需要将私有仓库地址添加到 no_proxy 环境变量中,避免代理干扰内网访问。
-
方案2:私有仓库通常部署在内网,访问速度快且稳定,所以可以直接删除 http 代理配置
[root@docker-N1 ~]# > /etc/docker/daemon.json
[root@docker-N1 ~]# docker rm -f registry
[root@docker-N1 ~]# docker run -d -p 443:443 --restart=always --name registry -v /opt/registry:/var/lib/registry -v /etc/docker/certs:/certs -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/timinglee.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/timinglee.org.key registry
[root@docker-N1 ~]# echo "192.168.153.10 reg.timinglee.org" >> /etc/hosts方案1:开启代理
[root@docker-N1 ~]# vim /etc/systemd/system/docker.service.d/http-proxy.conf
1 [Service]
2 Environment="HTTP_PROXY=http://192.168.153.1:7897"
3 Environment="HTTPS_PROXY=http://192.168.153.1:7897"
4 Environment="NO_PROXY=localhost,127.0.0.1,reg.timinglee.org,192.168.153.10"方案2:关闭代理
[root@docker-N1 ~]# > /etc/systemd/system/docker.service.d/http-proxy.conf

配置 Docker 公认证书
-
因为我们自建的证书不是 Dokcer 公认的 ca 签发证书,所以推送镜像时导致 Docker 拒绝;因此我们需要拷贝自己的证书为 Docker 公认的 ca 证书
[root@docker-N1 ~]# docker tag webserver:v4 reg.timinglee.org/webserver:v4
[root@docker-N1 ~]# docker push reg.timinglee.org/webserver:v4
[root@docker-N1 ~]# mkdir -p /etc/docker/certs.d/reg.timinglee.org/
[root@docker-N1 ~]# cp /etc/docker/certs/timinglee.org.crt /etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@docker-N1 ~]# systemctl restart docker.service

再次推送镜像
[root@docker-N1 ~]# docker push reg.timinglee.org/webserver:v4
[root@docker-N1 ~]# curl -k https://reg.timinglee.org/v2/_catalog
[root@docker-N1 ~]# curl -k https://192.168.153.10/v2/_catalog

测试 n2 主机拉取有加密证书后的镜像
- 关闭代理、关闭非加密端口并添加解析,然后重启
- 拷贝 ca 证书到 n2 主机上
错误信息 x509: certificate signed by unknown authority 明确表示:Docker 客户端不信任您的私有仓库的 SSL 证书。
[root@docker-N2 ~]# > /etc/systemd/system/docker.service.d/http-proxy.conf
[root@docker-N2 ~]# > /etc/docker/daemon.json
[root@docker-N2 ~]# echo "192.168.153.10 reg.timinglee.org" >> /etc/hosts
[root@docker-N2 ~]# systemctl daemon-reload
[root@docker-N2 ~]# systemctl restart docker.service
[root@docker-N2 ~]# docker pull reg.timinglee.org/webserver:v4
[root@docker-N1 ~]# scp -r /etc/docker/certs.d/ root@192.168.153.20:/etc/docker/certs.d/
[root@docker-N2 ~]# docker pull reg.timinglee.org/webserver:v4
[root@docker-N2 ~]# docker images


(3)给 Registry 添加认证登录
因为 HTTPS 证书只负责加密传输通道,不验证操作者身份,所以任何知道仓库地址的人都能通过这个加密通道推送或拉取镜像。
因此需要额外添加用户登录,来确认只有授权的用户才能访问和修改仓库内容。
添加用户认证登录功能
[root@docker-N1 ~]# docker rm -f registry
[root@docker-N1 ~]# dnf install httpd-tools -y
[root@docker-N1 ~]# mkdir /etc/docker/auth
[root@docker-N1 ~]# htpasswd -Bc /etc/docker/auth/htpasswd lee
New password: #输入密码
Re-type new password: #再次输入密码
[root@docker-N1 ~]# docker run -d -p 443:443 --restart=always --name registry -v /opt/registry:/var/lib/registry -v /etc/docker/certs:/certs -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/timinglee.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/timinglee.org.key -v /etc/docker/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd registry

测试 n1 主机登录推送
[root@docker-N1 ~]# docker images
[root@docker-N1 ~]# docker tag busybox:latest reg.timinglee.org/busybox:latest
[root@docker-N1 ~]# docker push reg.timinglee.org/busybox:latest
[root@docker-N1 ~]# docker login reg.timinglee.org -u lee
Password: #输入密码
[root@docker-N1 ~]# docker push reg.timinglee.org/busybox:latest
[root@docker-N1 ~]# curl -k -u lee:lee https://192.168.153.10/v2/_catalog


测试 n2 主机登录拉取
[root@docker-N2 ~]# docker pull reg.timinglee.org/busybox:latest
[root@docker-N2 ~]# docker login reg.timinglee.org -u lee
Password: #输入密码
[root@docker-N2 ~]# docker pull reg.timinglee.org/busybox:latest
[root@docker-N2 ~]# docker images
