1 Docker 简介
Docker 是一套开源的容器化虚拟化引擎,基于 Linux 内核能力实现应用隔离与打包。
传统虚拟机 KVM 是完整虚拟化 CPU、内存、磁盘,拥有独立操作系统内核;Docker 容器不虚拟化硬件,直接复用宿主机 Linux 内核,仅对应用进程做资源隔离。
底层依赖三大 Linux 内核技术:
- Namespace:做隔离,隔离 PID 进程、网络、挂载、用户,容器内部看见一套独立的进程、网卡、文件系统视图。
- Cgroups:做资源限制,限制容器最多占用多少 CPU、内存、磁盘 IO,防止单个容器耗尽宿主机资源。
- UnionFS 联合文件系统:实现镜像分层存储,镜像可以多层叠加,修改容器只新增读写层,不改动底层只读镜像,节省存储空间。
四大核心概念:
- 镜像 Image
镜像属于只读模板,存放应用程序、运行依赖、配置文件、环境变量。例如 mysql:5.7 镜像,相当于预制好的软件包,镜像本身不能运行。 - 容器 Container
容器是镜像运行后的实例。在镜像只读层之上新增一层可读写层。同一个镜像,可以启动无数个独立容器。容器销毁时,读写层数据默认丢失,需要数据卷持久化保存数据。 - Dockerfile
文本构建脚本,一系列指令,定义如何一步步构建镜像。写好 Dockerfile 后执行docker build生成自定义镜像。
理解:基于已有镜像模板,通过 Dockerfile+build 制作出新镜像,再 run 这个新镜像跑容器。 - 仓库 Registry
存放镜像的服务器,公共仓库 Docker Hub,企业内部搭建私有镜像仓库,用来上传、下载镜像。
核心优势
- 轻量:共享宿主机内核,不需要整套操作系统,占用磁盘、内存远小于 KVM 虚拟机。
- 启动快:进程级启动,秒级拉起业务;虚拟机需要启动完整操作系统,耗时分钟级别。
- 环境一致性:一次打包镜像,开发、测试、生产环境完全一致,解决 "本地能跑,线上跑不起来" 环境问题。
- 资源利用率高:一台物理机可以部署成百上千容器;虚拟机数量受硬件资源限制。
- 版本管理:镜像分层,支持版本回滚,分发拷贝便捷。
局限性
- 内核共享:容器直接使用宿主机内核,Windows 不能直接跑 Linux 容器;Linux 容器不能运行不同版本内核。
- 隔离弱于虚拟机:进程级隔离,不是硬件级隔离,对安全隔离等级要求极高的业务,优先选用 KVM 虚拟机。
- 不适合长时间运行传统大型数据库(MySQL 等建议做好数据卷持久化配置)。
2 Docker 架构

- Docker Engine(dockerd):Docker 守护进程。
- 接收
docker run / build / pull等客户端命令;处理镜像管理、网络、存储;对外提供 API。 - 不再直接操作容器 ,把容器生命周期任务交给下层
containerd。
- containerd:容器管理守护进程,工业级容器管理器,K8s 也默认使用 containerd。
- 接收 dockerd 下发指令,负责:容器生命周期管理、镜像解压、存储管理。
- 每启动一个容器,就生成一个
containerd‑shim。
- containerd‑shim(垫片):每个容器对应独立的 shim 进程。
- 充当 containerd 和 runC 中间垫片;
- 保证即使 containerd 进程挂掉,容器还可以继续运行;
- 接管容器标准输入输出、日志,转发给上层。
- runC:OCI 容器运行时规范 的参考实现,是真正直接调用 Linux 内核接口,完成容器创建的工具。
OCI(Open Container Initiative,开放容器倡议),是一套行业统一标准。主要定义两套规范:镜像规范 (容器镜像该长什么样、如何分层存储)和 运行时规范(如何调用内核 API 去启动一个容器)。
- containerd‑shim 调用 runC,runC 去调用 Linux 系统调用,创建 Namespace、Cgroups,生成容器进程。
- 容器创建完成后 runC 就退出,容器业务进程由 shim 托管。
-
Linux Kernel(Linux 内核层)
真正实现隔离、资源限制、安全管控,图中三个组件:
-
namespaces:PID、NET、MNT 等,做隔离,制造独立视图。
-
cgroups:CPU、内存、IO 资源限制,防止容器资源滥用。
-
SELinux/AppArmor:内核安全模块,强制访问控制,加固容器安全。
完整调用流程:执行docker run
- 用户执行
docker run→ 发送请求到 Docker Engine(dockerd) - dockerd 处理镜像、网络,把 "创建容器" 任务下发给 containerd
- containerd 为该容器启动 containerd‑shim 垫片进程
- shim 调用 runC
- runC 调用 Linux 系统调用,创建 Namespace、Cgroups,拉起容器 APP 进程
- 容器运行,shim 持续监控容器状态、转发日志;runC 任务完成退出
架构特点:就算上层
dockerd、containerd重启,只要containerd‑shim还在,容器业务进程不会停止。
3 Docker 部署
配置软件源、安装软件并启动服务
bash
[root@docker1 ~]# cd /etc/yum.repos.d/
[root@docker1 yum.repos.d]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@docker1 yum.repos.d]# yum install -y docker-ce
[root@docker1 yum.repos.d]# systemctl enable --now docker
查看 Docker 守护进程 (dockerd) 全局信息
[root@docker1 yum.repos.d]# docker info

如果此处有警告,需激活以下内核选项
bash
[root@docker1 ~]# sysctl -a|grep iptable
[root@docker1 ~]# sysctl -a|grep ip6table
[root@docker1 ~]# sysctl -a|grep ip_forward

把内核参数追加写入 docker 专属 sysctl 配置文件,此文件本身没有,是手动创建的。执行 sysctl --system,会按顺序扫描下面 4 个目录,加载目录下全部 *.conf 文件:
/usr/lib/sysctl.d/*.conf/run/sysctl.d/*.conf/etc/sysctl.d/*.conf/etc/sysctl.conf
bash
[root@docker1 ~]# echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.d/docker.conf
[root@docker1 ~]# echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.d/docker.conf
[root@docker1 ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/docker.conf
[root@docker1 ~]# sysctl --system
# 从系统全部配置文件加载生效内核参数,一次性读取所有 sysctl 配置文件并应用

查看IP,docker0是 Docker默认网桥(bridge)虚拟网卡 ,这个 IP 是 dockerd 守护进程自动生成分配的

安装网桥工具,查看 Linux 网桥信息
bash
[root@docker1 ~]# yum install -y bridge-utils
[root@docker1 ~]# brctl show

4 Docker 基本操作
查看正在运行的容器,为空
bash
[root@docker1 ~]# docker ps

搜索仓库镜像
bash
[root@docker1 ~]# docker search docker.1ms.run/yakexi007

从仓库拉取镜像
bash
[root@docker1 ~]# docker pull docker.1ms.run/yakexi007/mario
[root@docker1 ~]# docker images
# 查看本地镜像列表
[root@docker1 ~]# docker tag docker.1ms.run/yakexi007/mario:latest mario:latest
# 给本地镜像打新标签,相当于镜像别名
[root@docker1 ~]# docker rmi docker.1ms.run/yakexi007/mario:latest
# # 删除原有长路径标签

查看镜像的构建历史,逐层展示镜像每一层的创建信息,镜像内部 Dockerfile 写的 EXPOSE 8080,告诉使用者:这个容器服务默认监听 8080 端口
bash
[root@docker1 ~]# docker history mario:latest

后台启动容器,指定容器名 demo,端口映射:宿主机80:容器8080
bash
[root@docker1 ~]# docker run -d --name demo -p 80:8080 mario
[root@docker1 ~]# docker ps

访问IP

5 部署应用堆栈
堆栈(Stack):一组关联在一起,协同工作的多个服务 / 容器的集合。
举例子:
一个网站业务:Nginx(前端) + SpringBoot(后端) + MySQL(数据库) + Redis(缓存)
这 4 个容器配合才能跑完整业务,这一整套就叫应用堆栈。
部署由 httpd + mysql 组成的简易业务堆栈,访问业务接口获取数据库用户列表
bash
[root@docker1 ~]# docker stop demo
# 停止运行的 demo 容器
[root@docker1 ~]# ls
anaconda-ks.cfg httpd.tar mysql.tar os.sql
# 从本地上传镜像包
[root@docker1 ~]# docker load -i httpd.tar
[root@docker1 ~]# docker load -i mysql.tar
# 导入上述两个镜像

查看镜像
bash
[root@docker1 ~]# docker images

启动mysql容器,作为堆栈的数据库服务
bash
[root@docker1 ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=redhat --name dbserver mysql
[root@docker1 ~]# docker ps

查看dbserver容器日志
bash
[root@docker1 ~]# docker logs dbserver

宿主机安装mariadb,提供mysql客户端命令,登录验证
bash
[root@docker1 ~]# yum install -y mariadb
[root@docker1 ~]# mysql -h 127.0.0.1 -predhat

将本地os.sql脚本导入到容器内的mysql数据库,登录验证,导入成功
bash
[root@docker1 ~]# mysql -h 127.0.0.1 -predhat < os.sql
[root@docker1 ~]# mysql -h 127.0.0.1 -predhat

启动httpd容器,使用--link关联数据库容器dbserver
bash
[root@docker1 ~]# docker run -d -p 80:80 --link dbserver:db --name webserver httpd:webapp
[root@docker1 ~]# docker ps

进入webserver容器内部,查看action脚本源码,action脚本就是浏览器访问cgi‑bin/action调用的业务程序,该脚本会去连接数据库读取用户列表并输出网页
bash
[root@docker1 ~]# docker exec -it webserver bash
[root@2e55ee5bf3ab /]# cd /var/www/cgi-bin/
[root@2e55ee5bf3ab cgi-bin]# ls
action hostname
[root@2e55ee5bf3ab cgi-bin]# cat action

访问前端,可以看到数据库里的内容

6 镜像构建
6.1 镜像层
Docker 镜像由多层只读文件系统堆叠而成。每一层只记录这一层发生的文件变更(新增、修改、删除标记),不是完整操作系统。
- 镜像层只读,构建完成后不能修改;要改内容只能往上叠加新层。
- 容器启动时,会在所有只读镜像层之上自动生成一层容器读写层。容器内所有增删改操作全部发生在读写层,不会改动底层镜像层;容器删除,读写层连同内部改动全部销毁,镜像层不受影响。
- 写时复制 CoW(Copy‑on‑Write):修改下层镜像层已存在的文件,会把文件复制到上层读写层再修改。
镜像常用子命令
| 命令(简写) | 完整命令 | 作用 |
|---|---|---|
| docker images | docker image ls | 列出本地所有镜像 |
| docker build -t 名字:标签 . | docker image build | 构建镜像(Dockerfile 生成镜像) |
| docker rmi 镜像id/镜像名:tag | docker image rm | rmi = remove image,只删除标签;只有这个镜像没有其他标签时,才真正删除镜像层数据 |
| docker tag 旧镜像:旧tag 新镜像:新tag | docker image tag | 给镜像打新标签(别名) |
| docker inspect 镜像id | docker image inspect | 查看镜像详细元数据(层、CMD、ENV 等) |
| docker pull 镜像名 | docker image pull | 从镜像仓库拉取镜像到本地 |
| docker push 镜像名 | docker image push | 推送镜像到镜像仓库 |
| docker save -o xxx.tar 镜像名:tag | docker image save | 把镜像导出为 tar 压缩包(离线迁移) |
| docker load -i xxx.tar | docker image load | 从 tar 包导入镜像 |
查看镜像层历史的命令,每一行代表一层镜像层
bash
[root@docker1 docker]# docker history mysql:latest
IMAGE:镜像层 ID,<missing>代表本地没有缓存这一层(多阶段构建 / 导入镜像经常出现)CREATED BY:生成这一层对应的 Dockerfile 指令,截图能看到:EXPOSE 8080/tcp、WORKDIR、COPY、RUN apt‑get install、CMDSIZE:该镜像层占用大小

默认 docker history mario:latest 会把长命令截断,如上图中的省略号。
加参数--no-trunc:no truncate,不截断输出,完整展示每一层 CREATED BY 的原始命令

6.2 两种构建方式
镜像构建有两种方式:
docker commit:容器提交构建(手工构建)
- 流程:run 启动容器 → 在容器里面手动改环境、创建文件、装软件 → commit 把当前容器磁盘状态打包成新镜像
- 原理:抓取容器读写层全部变更 ,整体打包生成1 个全新的只读镜像层,追加到底层基础镜像之上
- 缺点:所有修改全部压进同一层,无法拆分步骤,
docker history看不到具体做了哪些操作
docker build + Dockerfile:Dockerfile 文件构建(自动化构建)
写 Dockerfile,把每一步操作写成指令RUN / COPY / ADD / EXPOSE ...
- 原理:Dockerfile每一条可执行指令(RUN、COPY、ADD)都会生成独立的只读镜像层,一层层向上堆叠。
- 缓存机制:构建会复用旧镜像层缓存;某一条指令改动,该指令及后续全部层重新构建,前面不变层直接复用。
- 查看分层历史:
docker history demo:v1,可以看到每一层对应的 Dockerfile 指令
Dockerfile 常用核心指令
| 指令 | 作用 | 说明 |
|---|---|---|
| FROM xxx:tag as 别名 | 指定基础镜像 | 必须第一行,所有镜像都要基于一个父镜像;as build给阶段起别名,供COPY --from=引用 |
| RUN 命令 | 构建阶段执行命令 | 构建镜像的时候运行,生成镜像层;不要用来启动容器程序 |
| COPY src dest | 复制文件到镜像 | 把宿主机上下文的文件拷贝进镜像内部 |
| ADD src dest | 复制 + 解压 | 比 COPY 多自动解压 tar 压缩包,普通文件优先用 COPY |
| EXPOSE 80 | 声明暴露端口 | 仅文档说明,不会自动做端口映射,run 的时候才 - p 映射 |
| CMD "程序","参数" | 容器启动默认命令 | 容器 run 起来之后执行,只会最后一条生效 |
| ENTRYPOINT | 容器入口程序 | 和 CMD 配合,固定启动命令,追加参数 |
| WORKDIR /path | 设置工作目录 | 后续 RUN/COPY 等指令默认工作目录,不存在自动创建 |
| CMD "程序","参数" | 容器启动默认命令 | 容器 run 起来之后执行,仅最后一条生效 ;可被docker run后面命令覆盖;容器 PID1 进程由此产生 |
解释:CMD
容器 run 起来之后执行,仅最后一条生效 ;可被
docker run后面命令覆盖;容器 PID1 进程由此产生例:在Dockerfile 里写
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]正常情况下启动容器:
docker run -d --name web3 webserver:v3容器就执行:
nginx -g daemon off若后面追加指令如
bash
docker run -it webserver:v3 bash此时不再执行 nginx,转而执行
bash,把 Dockerfile 的 CMD 直接作废
- 正常情况:不覆盖 CMD
PID1 =/usr/local/nginx/sbin/nginx(nginx 作为一号进程)
- nginx 前台运行,容器存活;
docker logs可以捕获 nginx 的 stdout 标准输出日志;- nginx 停止 → PID1 消失 → 容器直接退出停止。
- run 后面写 bash,覆盖 CMD
PID1 =bash
- nginx 要手动在容器内部敲命令启动,只是普通后台子进程;
- docker logs 拿不到 nginx 日志;
- 退出 bash,PID1 结束,容器立刻停止。
6.2.1 docker commit
把容器可写层(新增 file1/file2/file3、os.sql)做成一个新镜像层,叠加原来 busybox 底层只读镜像
bash
[root@docker1 ~]# docker run -it --name demo docker.1ms.run/busybox
# 启动交互式容器
/ # touch file1 file2 file3
# 容器内,创建3个空文件
/ # [root@docker1 ~]# docker ps
ctrl+pq 退出容器,后台继续运行
[root@docker1 ~]# docker cp os.sql demo:/
# 宿主机文件拷贝到容器demo的根目录

attach 接入容器终端,查看,已经有file1‑3、os.sql
bash
[root@docker1 ~]# docker attach demo
ctrl+d 退出容器

把停止的 demo 容器,提交保存为新镜像 demo:v1
bash
[root@docker1 ~]# docker commit -m "add os.sql" demo demo:v1
# `docker commit`:把容器当前可写层的改动,打包生成全新镜像
# -m "add os.sql" 添加注释说明

查看生成的镜像及镜像分层历史
bash
[root@docker1 ~]# docker images demo:v1
[root@docker1 ~]# docker history demo:v1

- 第一层(最新):
sh,大小 1.58MB,注释add os.sql,这就是 commit 新增的镜像层,包含我们新建的 3 个文件 + 拷贝进去的 os.sql。 - 底层:原始 busybox 镜像层 4.45MB。
- 总镜像大小:
4.45 + 1.58 = 6.03MB
6.2.2 Dockerfile
单独建一个目录
bash
[root@docker1 ~]# mkdir docker
作用
-
控制构建上下文,避免上传无关文件
执行
docker build -t xxx .,.代表把当前目录全部文件打包传给 Docker 引擎。如果在
/root家目录直接 build,root 下所有日志、脚本、缓存全部打包上传,速度慢、体积巨大,还会泄露文件。新建空目录,里面只放 Dockerfile、nginx.conf、tar 源码、repo 文件,上下文就只有需要的文件。
-
文件管理干净,不污染 root 家目录
所有实验文件(Dockerfile、配置、源码)统一放在
~/docker,找文件、删除实验内容很方便,不和系统文件混在一起。 -
COPY 指令找文件不会乱
COPY只能拿上下文目录里面的文件,不能拿外面上级目录的文件。把所有素材全部放进这个文件夹,COPY 直接写文件名即可。
-
方便区分多个项目
还可以继续建子目录:
mkdir nginx_build、mkdir python_build,每个镜像构建单独一套目录。
构建上下文目录:把宿主机CentOS‑Base.repo源文件复制到 ~/docker 目录
bash
[root@docker1 ~]# cd /etc/yum.repos.d/
[root@docker1 yum.repos.d]# cp CentOS-Base.repo ~/docker/
确保资源文件都在当前目录
bash
[root@docker1 yum.repos.d]# cd
[root@docker1 ~]# cd docker/
[root@docker1 docker]# ls
centos7.tar CentOS-Base.repo nginx-1.30.3.tar.gz
导入本地centos7镜像包
bash
[root@docker1 docker]# docker load -i centos7.tar
[root@docker1 docker]# rm -fr centos7.tar
# 删除本地tar包,释放磁盘
编写 Dockerfile
bash
[root@docker1 docker]# vim Dockerfile
FROM centos:7
RUN rm -f /etc/yum.repos.d/*.repo
COPY CentOS‑Base.repo /etc/yum.repos.d/
RUN yum install -y gcc make pcre‑devel openssl‑devel
ADD nginx‑1.30.3.tar.gz /mnt/
WORKDIR /mnt/nginx‑1.30.3
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --with‑http_ssl_module --with‑http_stub_status_module
RUN make
RUN make install
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx","‑g","daemon off;"]
`daemon off;` 让 Nginx以前台方式运行,不脱离终端。
Nginx 就成为容器的 PID 1 主进程,进程在,容器就存活。

执行镜像构建,读取 Dockerfile 逐层生成镜像
-t webserver:v1:‑t 打标签(tag),镜像名 webserver,版本标签 v1
bash
[root@docker1 docker]# docker build -t webserver:v1 .

过滤显示仓库名为webserver的所有镜像
bash
[root@docker1 docker]# docker images webserver

基于镜像webserver:v1创建一个名叫web1的容器,后台启动
bash
[root@docker1 docker]# docker run -d --name web1 webserver:v1
[root@docker1 docker]# docker ps

查看容器web1的完整底层 JSON 格式元数据。
包含:容器 ID、状态、网络、端口映射、挂载、环境变量、镜像 ID、进程、文件路径等全部底层信息
bash
[root@docker1 docker]# docker inspect web1

172.17.0.2:容器 web1 在 docker 默认 bridge 网桥里的容器内网 IP
测试
bash
[root@docker1 docker]# curl 172.17.0.2

查看日志
bash
[root@docker1 docker]# docker logs web1
# docker logs 读取的是容器进程的 stdout、stderr(标准输出、标准错误);编译安装的 Nginx 默认把日志写入磁盘文件,不打印到标准输出
[root@docker1 docker]# docker exec -it web1 bash
# 交互式进入容器内部
[root@c3540f688ac3 nginx-1.30.3]# cd /usr/local/nginx/logs
# 在此目录下可看到日志

7 镜像优化
7.1 缩减镜像层
在 v1 版本中,每一个RUN生成一个独立镜像层,这里 yum、sed、configure、make、make install 都是单独 RUN ,镜像层数非常多;yum 安装完没有清理 yum 缓存,缓存文件留在镜像层,镜像体积大;nginx 源码解压包/mnt/nginx‑1.30.3编译完成后还留在镜像里,占磁盘空间
bash
[root@docker1 docker]# docker rm -f web1
[root@docker1 docker]# vim Dockerfile
在 v2 版本中,可以做以下优化
bash
FROM centos:7
RUN rm -f /etc/yum.repos.d/*.repo
COPY CentOS‑Base.repo /etc/yum.repos.d/
ADD nginx‑1.30.3.tar.gz /mnt/
# 全部编译流程合并为 1 条RUN
RUN yum install -y gcc make pcre‑devel openssl‑devel \
&& cd /mnt/nginx‑1.30.3 \
&& 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.30.3 \ # 删除nginx源码包,删掉编译残留文件
&& yum clean all # 清理yum缓存,减小镜像大小
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx","‑g","daemon off;"]

构建 v2 版本镜像
bash
[root@docker1 docker]# docker build -t webserver:v2 .

镜像体积明显变小
bash
[root@docker1 docker]# docker images webserver

运行容器,没问题
bash
[root@docker1 docker]# docker run -d --name web1 webserver:v2
[root@docker1 docker]# docker ps

测试

7.2 多阶段构建
多阶段构建原理
一份 Dockerfile 可以写多个FROM,每一个FROM代表一个独立阶段。
- 构建阶段(builder):用完整基础镜像,安装编译工具,下载源码,编译出可执行程序。这个阶段只负责 "生产成品"。
- 运行阶段(最终镜像) :重新开启一个全新干净的基础镜像,只把编译好的成品文件从 builder 复制过来。
builder 阶段的所有编译器、源码、临时文件全部直接丢弃,不会进入最终镜像,最终镜像只保留运行业务程序必须的文件
重新进入容器,将nginx.conf文件拷贝到容器
bash
[root@docker1 docker]# docker rm -f web1
web1
[root@docker1 docker]# docker run -it --name web1 webserver:v2 bash
若 web1 容器还在运行中,可执行docker exec -it web1 bash直接进入
bash
[root@a98b205e6240 /]# cd /usr/local/nginx/conf/
ctrl+pq 退出容器,但不停止容器,回到宿主机 shell,容器继续后台运行
[root@a98b205e6240 conf]# [root@docker1 docker]# docker cp web1:/usr/local/nginx/conf/nginx.conf ./nginx.conf

bash
[root@docker1 docker]# docker rm -f web1
[root@docker1 docker]# vim nginx.conf
error_log /dev/stderr notice;
# nginx 错误日志输出到**标准错误**,日志级别`notice`及以上输出
log_format main ...; # 定义main格式日志模板
access_log /dev/stdout main; # 访问日志输出到标准输出,使用main格式
在 Docker 容器中,写入这两个文件的内容,就会被 Docker 引擎捕获,通过 `docker logs 容器名` 查看日志,**不用写磁盘文件**

bash
[root@docker1 docker]# vim Dockerfile
#第一阶段build编译阶段:编译nginx
FROM centos:7 as build
RUN rm -f /etc/yum.repos.d/*.repo
COPY CentOS-Base.repo /etc/yum.repos.d/
ADD nginx-1.30.3.tar.gz /mnt/
RUN yum install -y gcc make pcre-devel openssl-devel && cd /mnt/nginx-1.30.3 && 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.30.3 && yum clean all
COPY nginx.conf /usr/local/nginx/conf/
#第二阶段最终镜像,重新拉取一个干净纯净的 centos:7 基础镜像,复制编译产物,舍弃编译工具
FROM centos:7
COPY --from=build /usr/local/nginx /usr/local/nginx
# 引用上面叫 build 的那个阶段,把编译好的文件拷贝过来
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

根据当前目录的 Dockerfile 编译生成名为 webserver:v3 的Nginx镜像
bash
[root@docker1 docker]# docker build -t webserver:v3 .

查看各个版本的 webserver 镜像,v3 体积又被压缩
bash
[root@docker1 docker]# docker images webserver

运行容器
bash
[root@docker1 docker]# docker run -d --name web1 webserver:v3
[root@docker1 docker]# docker ps
[root@docker1 docker]# docker logs web1
# 日志可直接查看

7.3 使用最精简的基础镜像
精简镜像也常叫 Distroless(无发行版)镜像,只保留业务程序运行必需的二进制文件、依赖库,剔除操作系统 shell、包管理器、调试工具、文档等无关组件。
优点
- 体积小:去掉大量系统工具,镜像大小大幅下降,传输、部署速度更快。
- 安全性高:减少系统组件,攻击面变小;即使攻击者入侵容器,没有 shell、curl 等工具,很难执行恶意命令。
- 环境干净:只保留业务运行最低限度依赖,避免系统自带工具带来版本冲突。
缺点
- 无法使用 docker exec 调试 :镜像内部没有
sh/bash,也没有curl、ss、netstat 等排错工具,docker exec执行命令直接报executable file not found。 - 排错困难:不能进入容器内部修改配置、查看文件;配置错误只能在宿主机修改源码,重新 build 镜像。
- 故障排查手段有限 :只能依靠容器日志、
nsenter切入命名空间、宿主机抓包等外部方式排错。
导入基础镜像
bash
[root@docker1 ~]# docker load -i base-debian12.tar
[root@docker1 ~]# mkdir new
[root@docker1 ~]# cd new/
[root@docker1 new]# docker tag docker.1ms.run/nginx nginx:latest
[root@docker1 new]# docker rmi docker.1ms.run/nginx:latest
[root@docker1 new]# vim Dockerfile

bash
[root@docker1 new]# docker build -t webserver:v4 .

bash
[root@docker1 new]# docker run -d --name web1 webserver:v4

日志
