Docker概述
容器化技术,可以将应用程序及其依赖项打包到一个可移植的容器中,使其可以在不同的环境中运行
Docker概述
1.1 Docker 为什么出现?
在没有 Docker 之前,运维最大的痛苦是“环境不一致”。
- 痛点:你在本地用 Miniconda 调通的 Random Forest 模型,换到服务器上可能因为一个 GLIBC 版本不对就无法运行。
- 解决方案:Docker 将应用及其所有的依赖(库、配置、环境变量)打包在一起。它实现了“一次构建,到处运行”。
1.2 Docker 历史
- 起源:2013 年由 DotCloud 公司开源。
- 爆发:它比传统的虚拟机更轻、更快,迅速成为了云计算和微服务的事实标准。
1.3 能做什么
- 快速交付:通过镜像直接部署,不再需要手动执行复杂的安装脚本。
- 资源利用:一台物理机可以运行成百上千个容器,而虚拟机可能只能跑几十个。
- 安全沙箱:对你而言,它可以快速拉取一个带有 PHP 逻辑漏洞 的镜像进行 CTF 审计演练,即便环境被搞挂了,秒级重启就能恢复。
2.虚拟技术与容器技术对比
| 特性 | 虚拟机 (VM) | 容器化 (Docker) |
|---|---|---|
| 底层架构 | 虚拟出一套完整的 硬件,运行完整的 OS | 直接利用 宿主机内核,只隔离进程 |
| 启动速度 | 分钟级(需要加载内核) | 秒级(本质是一个特殊进程) |
| 资源消耗 | 很大(每个虚拟机都要占几 GB 内存) | 极小(共享内核,按需分配) |
| 隔离性 | 强(完全硬件隔离) | 较强(逻辑隔离,Linux 内核层实现) |
3.相关概念

| 概念 | 类比 | 作用 |
|---|---|---|
| 镜像 (Image) | 游戏安装包 | 只读的模板,包含了运行程序所需的所有代码、库和配置。 |
| 容器 (Container) | 运行中的游戏实例 | 镜像运行后的实体,可以被启动、停止、删除。 |
| 仓库 (Registry) | 应用商店 (Docker Hub) | 存放镜像的地方,你可以 pull 别人做好的环境。 |
Docker安装
查看当前环境
jch@ubuntu:~$ uname -r #查看系统内核
6.8.0-101-generic
jch@ubuntu:~$ cat /etc/os-release #查看系统版本
PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
jch@ubuntu:~$
查看当前的docker版本
jch@ubuntu:~$ docker -v
Command 'docker' not found, but can be installed with:
sudo snap install docker # version 28.4.0, or
sudo apt install docker.io # version 28.2.2-0ubuntu1~24.04.1
sudo apt install podman-docker # version 4.9.3+ds1-1ubuntu0.2
See 'snap info docker' for additional versions.
jch@ubuntu:~$
首次安装建议安装官方原版
# 1. 更新索引并安装依赖
sudo apt update
sudo apt install ca-certificates curl gnupg
# 2. 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 3. 添加软件源
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. 再次更新并安装
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
安装结束后
jch@ubuntu:~$ docker -v
Docker version 29.3.0, build 5927d80
权限管理
将当前用户加入docker用户组
# 1. 将当前用户 jch 加入 docker 组
sudo usermod -aG docker $USER
# 2. 激活组更改(或者直接注销重新登录)
newgrp docker
执行完这两步操作后,用户每次使用docker命令不用一直提权了。
换源
打开文件/etc/docker/daemon.json
编辑配置文件下所示
{
"registry-mirrors": [
"https://XXXXXXX.mirror.aliyuncs.com",#阿里云专属镜像加速
"https://docker.1ms.run",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://ccr.ccs.tencentyun.com",
"https://hub-mirror.c.163.com"
]
}
运行第一个docker容器,如下所示成功
jch@ubuntu:/etc/docker$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
17eec7bbc9d7: Pull complete
ea52d2000f90: Download complete
Digest: sha256:ef54e839ef541993b4e87f25e752f7cf4238fa55f017957c2eb44077083d7a6a
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
jch@ubuntu:/etc/docker$
docker运行原理

Docker 在表面上是一个典型的 C/S(客户端-服务器)架构。
当你使用docker时,有三个进程在互相配合,
- Docker Client(客户端): 这就是你敲命令的终端(例如输入
docker run或docker pull的地方)。它不干重活,只负责把你的指令翻译后发送给后台的大管家。 - Docker Daemon(守护进程/服务端): 这是运行在 Linux 宿主机后台的超级管家(也就是图里的“后台守护进程”)。它监听客户端的请求,负责真正的脏活累活:构建、运行、分发你的容器。
- Docker Registry(镜像仓库): 这就相当于手机上的应用商店。当你需要的镜像(比如 Nginx 或 Ubuntu)在本地没有时,守护进程就会去你配置的源(比如你刚才配的阿里云、网易云镜像源)里去下载。
Docker 本质上并不是像 VMware 那样虚拟出一套完整的硬件(CPU、内存、硬盘),它其实只是宿主机上的一个特殊的普通进程。为了让这个进程觉得自己是个独立的系统,Docker 借用了 Linux 内核的三大“法宝”:
- Namespaces(命名空间)—— 负责“隔离” 它给容器制造了一种“幻觉”。通过挂载点、网络、PID(进程号)的隔离,容器内部的程序会认为自己独占了一台计算机。在容器里,它看到自己的进程号是 1,但其实在宿主机上,它只是个编号几万的普通进程。
- Cgroups(控制组)—— 负责“限制资源” 如果容器只是普通进程,万一它死循环把宿主机的 CPU 跑满了怎么办?Cgroups 就是给容器戴上“紧箍咒”,严格限制这个容器最多只能用多少内存、多少 CPU 核心。
- UnionFS(联合文件系统)—— 负责“分层叠加” 这是 Docker 镜像极小的秘密。Docker 镜像是像千层饼一样一层层叠起来的。多个容器可以共享最底层的操作系统基础文件,只需要为自己独立修改的那一部分占用少量的存储空间。
当你在终端敲下 docker run nginx 时,整个工作流是这样的:
- 发送指令:客户端(终端)将
run nginx的指令通过 API 发送给本地的 Docker Daemon。 - 检查本地:Daemon 会先看看宿主机本地有没有
nginx的镜像。 - 拉取镜像(如本地没有):Daemon 会自动连接远程的 Registry(镜像仓库),把
nginx镜像下载(pull)到本地。 - 分配资源并启动:Daemon 调用 Linux 内核的 Namespaces 和 Cgroups,从这个镜像中划出一块隔离的空间,启动程序。此时,一个活生生的容器就诞生了。
docker为什么比虚拟机更快?

启动路线的差异
- 当你启动一台虚拟机时,它必须经历一个完整的计算机开机流程:虚拟 BIOS 初始化 -> 引导加载程序 (Bootloader) -> 加载 Guest OS(访客操作系统)内核 -> 启动系统级守护进程 (systemd) -> 最后才启动你的应用程序(比如 Nginx 或 MySQL)。这个过程包含了大量的 I/O 操作和硬件自检模拟,通常需要几十秒甚至几分钟。
- Docker 容器不需要引导独立的操作系统。你的 Ubuntu 宿主机内核早就启动并在完美运行了。当你执行
docker run时,Docker 守护进程只是向宿主机内核发出了一个创建新进程的指令,并顺手给这个进程套上了“隔离面具”(Namespaces 和 Cgroups)。
**运行时的性能损耗 - 在虚拟机里,应用程序想要往硬盘写一条日志,指令必须层层传递:APP -> Guest OS 内核 -> Hypervisor(虚拟机管理程序,如 VMware/KVM) -> 宿主机 OS 内核 -> 物理硬件。这个 Hypervisor 就像个中间商,所有的底层系统调用(Syscall)都要被它拦截和“翻译”,导致计算和 I/O 性能天生存在 10%~20% 的损耗。
- 容器内的应用程序,实际上是直接调用宿主机 Linux 内核的接口的。中间没有任何 Hypervisor 翻译层。
**资源占用的逻辑 - 虚拟机是“买断制”: 只要你给一台 VM 分配了 4GB 内存,哪怕它里面什么都没跑,这 4GB 内存也会在宿主机上被彻底划走并锁死。
- Docker 是“按需分配”: 容器里的程序本质上只是宿主机的一个进程,它和宿主机共享同一块内存池。程序实际占用 50MB,它就只消耗系统 50MB 的内存。这也是为什么一台普通服务器只能跑几台 VM,却能轻松支撑上百个微服务容器。
Docker常用命令
镜像命令
查看镜像信息
docker images
| 参数 (简写/全拼) | 作用说明 | 典型运维与安全场景 |
|---|---|---|
-a, --all | 列出所有镜像(包括隐藏的中间层镜像) | 排查复杂的 Dockerfile 构建失败原因,查看是哪一层产生了问题。 |
-q, --quiet | 静默模式,只显示镜像的 Image ID | 编写自动化运维脚本的利器(例如将 ID 批量传递给删除命令)。 |
-f, --filter | 根据提供的条件过滤输出 | 清理系统磁盘空间时,过滤出特定的废弃镜像。 |
--digests | 显示镜像的 SHA256 摘要信息 | 校验拉取的镜像是否被篡改,确保镜像在传输过程中的完整性,这在网络安全领域非常重要。 |
--no-trunc | 不截断输出,显示完整的 64 位 Image ID | 记录详尽的安全审计日志,避免因 ID 截断导致的哈希碰撞误判。 |
--format | 使用 Go 模板格式化输出 | 定制化输出(例如只打印“镜像名:版本号”),方便导出统计报告。 |
查找镜像
docker search [镜像名称]
使用参数--filter=STARS=3000
搜索出来的镜像就是stars大于3000的。
拉取镜像
docker pull 镜像名[:tag]
tag用来指定版本,不写tag默认就是latest的。
在实际环境中尽量避免写latest,若版本出现了更新,可能出现不兼容的情况。
删除镜像
[root@JWei_0124 //]# docker rmi -f 镜像id # 删除指定的镜像
[root@JWei_0124 //]# docker rmi -f 镜像id 镜像id 镜像id # 删除多个镜像(空格分隔)
[root@JWei_0124 //]# docker rmi -f $(docker images -aq) # 删除全部的镜像
容器命令
拉取镜像
从注册表中提取镜像或存储库
docker pull [镜像名称]
启动docker
docker run [可选参数] image
# 参数说明
--name="name" 容器名字:用来区分容器
-d 后台方式运行:相当于nohup
-it 使用交互式运行:进入容器查看内容
-p 指定容器的端口(四种方式)小写字母p
-p ip:主机端口:容器端口
-p 主机端口:容器端口
-p 容器端口
容器端口
-P 随机指定端口(大写字母P)
列出所有容器
docker ps [可选参数]
# 命令参数可选项
-a # 列出当前正在运行的容器+历史运行过的容器
-n=? # 显示最近创建的容器(可以指定显示几条,比如-n=1)
-q # 只显示容器的编号
退出容器
exit # 容器直接停止,并退出
ctrl+P+Q # 容器不停止,退出
删除容器
docker rm 容器id # 删除容器(不能删除正在运行的容器)如果要强制删除:docker rm -f 容器id
docker rm -f $(docker ps -aq) # 删除全部容器
docker ps -a -q|xargs docker rm # 删除所有容器
启动和停止容器的操作
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止当前容器
查看日志
docker logs -tf --tail 50 [容器id]
宿主机查看容器进程信息
jch@ubuntu:~$ docker run -d --name my-test-nginx nginx
d43d11fcd28ac67ccebd05894543039d327b35297c18bde7e7102c28c59ce95b
jch@ubuntu:~$ docker top my-test-nginx
UID PID PPID C STIME TTY TIME CMD
root 2544 2521 0 01:08 ? 00:00:00 nginx: master process nginx -g daemon off;
message+ 2604 2544 0 01:08 ? 00:00:00 nginx: worker process
message+ 2605 2544 0 01:08 ? 00:00:00 nginx: worker process
jch@ubuntu:~$
进入容器
docker exec -it 容器id /bin/bash
容器与宿主机的文件移动
docker cp 容器id:容器内路径 目的主机的路径
docker cp 宿主机的路径 容器id或名称:容器内的路径
Docker镜像
镜像加速原理
在默认情况下,当你敲下 docker pull wordpress 时,Docker 引擎会直接去连接位于美国的官方仓库(Docker Hub, registry-1.docker.io)。因为跨国网络的物理延迟和某些防火墙的拦截,拉取速度往往会非常慢,甚至直接超时报错。
镜像加速的本质,就是一个“带有强力缓存的官方代购(反向代理 CDN)”。
当你配置了国内的镜像加速器(比如阿里云、腾讯云或某些大学的开源镜像站)后,系统的工作流就变了:
- 请求劫持:Docker 引擎在本地的
/etc/docker/daemon.json配置文件里看到了你填写的加速器地址。 - 就近拉取:它不再去大洋彼岸,而是直接向这个国内加速器发请求。
- 缓存命中:由于阿里云等加速器早就把 Docker Hub 上常用的成千上万个镜像“搬运”并缓存到了国内机房。你的下载请求瞬间变成了国内千兆内网的下载,速度直接从 10KB/s 飙升到 10MB/s。
- 触发回源:如果你拉取的是一个非常冷门的镜像,国内加速器上没有,加速器服务器会自己在后台去美国拉取一次,存到自己的硬盘上,然后再传给你。下一个人再拉取这个镜像时,就能直接秒下了。
UnionFS(联合文件系统)
以VMware 虚拟机为例,它的一个系统的镜像往往是一个几十 GB 的 .iso 或 .vmdk 单体大文件。
但在 Docker 的世界里,镜像根本不是一个完整的文件,而是一块块“千层蛋糕”。
实现这个魔法的底层技术,就是 UnionFS**(目前 Docker 最常用的具体实现叫 Overlay2)。
基本概念
你手里有 5 块透明的玻璃板,每块玻璃板上画了一点东西。当你把这 5 块玻璃板叠在一起,从最上面往下看时,你会看到一幅完整的画。 UnionFS 就是干这个的。它允许将多个不同的物理目录(层),“联合挂载” 到同一个挂载点下,表现得就像一个普通的、完整的系统目录。
以wordpress:php8.3-apache 镜像为例,它的底层其实是这样分层的:
- 第 1 层(基础 OS):最底层的玻璃板,只有极其精简的 Debian 系统文件(可能才 30MB)。
- 第 2 层(环境依赖):在这层安装了 Apache 服务器。
- 第 3 层(运行环境):在这层安装了 PHP 8.3。
- 第 4 层(业务代码):最上面的一层,放了 WordPress 的官方源码。
这四层全都是只读(Read-Only) 的。一旦构建完成,就像刻在光盘上一样,永远不会改变。
写时复制
Docker 在存储架构上采用了 UnionFS(联合文件系统)。它打破了传统虚拟机单体庞大镜像的桎梏,通过分层存储(Layered Storage) 架构来实现文件管理。一个完整的 Docker 镜像由多个只读层(Read-Only Layers) 联合挂载而成。当容器启动时,引擎只需在最上方动态分配一个轻量级的 可写层(Container Layer)。 所有的修改操作均遵循 写时复制(Copy-on-Write, CoW 原则,即只有在需要修改底层文件时,才会将其复制到顶层可写层进行操作,从而最大限度地保障了基础环境的隔离性与一致性。。
镜像加速与 UnionFS 的协同
镜像加速与 UnionFS 完美体现了“增量更新”的思想。由于 UnionFS 的分层特性,多个镜像可以共享同一块底层的“玻璃板”(如基础的 OS 系统层)。在配合镜像加速器拉取时,Docker 引擎会比对本地已有的层,只通过网络去下载你本地缺失的、有差异的层。这种“增量下载 + 就近缓存”的协同,极大地节省了网络带宽和硬盘空间。
Docker Compose
为了解决多容器部署的痛点,Docker 官方推出了一个强大的工具:Docker Compose。
它的核心思想是“基础设施即代码 (IaC)”: 你不必再终端里苦哈哈地敲击长串的命令,而是直接写一个清晰的 docker-compose.yml 文本文件。在这个文件里,你把 WordPress 需要什么版本的镜像、映射到哪个端口、连接哪个数据库、甚至数据库密码都定义好。
写好之后,只需要在终端敲下一行极其简单的命令:
Bash
docker compose up -d
Docker 就会自动帮你拉取镜像、创建专属虚拟网络、按照正确的依赖顺序把数据库和 WordPress 瞬间启动。如果你想把整个网站连同配置迁移到另一台服务器,只需要把这个 .yml 文件拷过去重新执行一次即可,这就是现代运维的魅力。
Docker数据卷
Docker容器数据卷,即Docker Volume(卷)
docker存在的问题
当Docker容器运行的时候,会产生一系列的数据文件,这些数据文件会在关闭Docker容器时,直接消失的。但是其中产生部分的数据内容,我们是希望能够把它给保存起来,另作它用的。
在 Docker 的底层架构中,容器的存储层是“短暂的(Ephemeral)”, 可以把容器想象成一台没有硬盘的电脑,它的文件系统是和它的生命周期绑定在一起的。
关闭Docker容器=删除内部除了image底层数据的其他全部内容,即删库跑路
解决办法
Docker 引入了挂载(Mount)机制。本质上,就是绕开容器自己那个“阅后即焚”的文件系统,直接把宿主机(你的 Ubuntu)上的某个目录,映射到容器内部。
两种最常用的挂载方式
在命令行中,我们统一使用 -v (volume) 参数来实现挂载,但具体用法分为两派:
第一派:绑定挂载 (Bind Mounts)
这是最直观的挂载方式,格式为:-v 宿主机绝对路径:容器内路径
- 特点:你明确告诉 Docker,把宿主机的
/opt/nginx/conf目录,强行覆盖到容器的/etc/nginx目录上。 - 运维场景:通常用来挂载配置文件或前端代码。比如你想改 Nginx 配置,直接在宿主机用
vim改/opt/nginx/conf/nginx.conf,容器里会瞬间生效,连重启都不用。
第二派:具名数据卷 (Managed Volumes)
格式为:-v 卷名称:容器内路径 (注意,前面不再是以 / 开头的绝对路径,而是一个名字)
- 特点:你不需要关心数据具体存放在宿主机的哪个深层目录里,Docker 会自动在
/var/lib/docker/volumes/下为你分配一块区域。 - 运维场景:专门用来存储数据库文件(MySQL 数据)或应用持久化数据(WordPress 的媒体库)。这种方式性能最好,且方便通过 Docker 命令直接进行跨服务器的数据迁移和备份。
匿名挂载(Anonymous Volume):
-v /etc/nginx
– “用完即扔”。不指明本地路径,也不给 U 盘起名字。Docker 会自动生成一长串连你都看不懂的乱码字符作为文件夹名字。
- 应用场景:极少主动使用,一般是用来做临时测试的,在写
Dockerfile时(使用VOLUME指令)为了防止用户忘记挂载导致数据丢失,系统往往会默认做一个匿名挂载作为最后的“安全底线。
挂载实例
mkdir -p /opt/my_web_data
echo '<h1>Hello, DevOps! This is magical!</h1>' > /opt/my_web_data/index.html
docker run -d -p 8099:80 --name test-nginx -v /opt/my_web_data:/usr/share/nginx/html nginx

当我们修改/opt/my_web_data/index.html时,网页内容也会同步修改。
docker可视化工具
Portainer
一个轻量级的 Web 管理界面。
第一步:创建一个数据卷(用来永久保存 Portainer 的账号密码和配置)
docker volume create portainer_data
第二步:运行 Portainer 容器(注意看里面的端口映射,你应该很熟悉了)
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
运维硬核解析:
-v /var/run/docker.sock:/var/run/docker.sock这一步是灵魂。这就是让容器内部的 Portainer 程序,能够“越权”控制你宿主机 Docker 守护进程的终极魔法。

LazyDocker
- 核心形态:它是一个 TUI(Terminal User Interface,终端用户界面)工具。运行后,你的黑色终端框框会直接分裂成几个精美的面板。
- 运维痛点解决:使用 SSH 连上一台没有公网界面的内网服务器,或者在极度拥挤的开发环境下排错时,敲
docker ps和docker logs会让屏幕信息糊成一团。LazyDocker 能让你全程只用键盘(上下左右键)在不同的容器、镜像和日志之间丝滑穿梭。 - 资源占用:极低。不需要开启任何对外开放的 Web 端口,用完
Ctrl+C退出,深藏功与名。
1Panel
- 核心形态:这是一个完整的 Linux 服务器运维控制面板,但它的底层架构 100% 建立在 Docker 之上。
- 运维痛点解决:在传统面板(如宝塔)中,软件直接装在宿主机里,极易产生环境冲突(所谓的“把系统装脏了”)。而 1Panel 所有的软件(包括内置的 MySQL、Redis、Nginx,甚至 WordPress)全都是通过 Docker 容器隔离运行的。
- 特色功能:
- 自带极其丰富的“应用商店”,一键部署各种开源软件。
- 自带完善的防火墙、主机资源监控(CPU/内存/磁盘)、计划任务(Cron)和免费 SSL 证书自动申请系统。
- 非常适合拿到一台崭新的“裸机(Bare Metal / VPS)”后,作为第一个底层基座安装上去
- 自带极其丰富的“应用商店”,一键部署各种开源软件。
DockerFile
容器镜像构建规范
如果说 Docker Compose 是用于多容器编排(Orchestration)的声明式配置,那么 Dockerfile 则专注于单容器镜像的标准化构建。它是一种基于基础设施即代码(IaC)理念的配置脚本,Docker 引擎通过解析该脚本中的指令,利用 UnionFS 机制逐层(Layer-by-Layer)构建出不可变的镜像实体(Immutable Image)。
核心构建指令解析
FROM:基础镜像声明 (Base Image Declaration)。必须置于首行,定义当前构建工作流的底层环境依赖。WORKDIR:工作目录设定 (Working Directory Setup)。配置容器内部的运行上下文路径。若指定路径不存在,Docker 守护进程将自动进行层级创建。COPY/ADD:资源注入 (Resource Injection)。将宿主机(Host)的代码或静态资源同步至镜像文件系统中。ADD指令在COPY的基础上额外支持压缩包的自动解压及远程 URL 资源的拉取。RUN:镜像层构建执行 (Layer Execution)。在构建阶段(Build Time)执行特定的 Linux shell 命令。- ⚠️ 性能优化最佳实践:由于 UnionFS 中每执行一次
RUN均会生成一个新的只读镜像层,为有效控制镜像体积,应通过&&逻辑运算符将多条命令合并为单条RUN指令,并在此层执行完毕后清理缓存文件(如apt-get clean)。
- ⚠️ 性能优化最佳实践:由于 UnionFS 中每执行一次
CMD/ENTRYPOINT:主进程启动入口 (Container Entrypoint)。定义容器在启动时(Run Time)接管 PID 1 的默认前台守护进程。
Docker 容器网络架构 (Container Networking Topology)
在虚拟化技术中,隔离不仅体现在文件系统与计算资源上,网络层面的隔离与连通同样是核心命题。Docker 利用 Linux 内核的 Network Namespace(网络命名空间) 技术,为每个容器提供完全独立的网络协议栈(包含网卡、IP 地址、路由表及 iptables 规则)。
核心网络驱动模式 (Network Drivers)
Docker 引擎默认提供了三种最基础的网络运行模式,以应对不同的业务拓扑需求:
- Bridge (网桥模式 – 默认):
当 Docker 服务启动时,会在宿主机创建一个名为docker0的虚拟交换机(网桥)。每次启动新容器,Docker 会利用veth pair(虚拟以太网对) 技术创建一对虚拟网卡,一端留在宿主机并挂载到docker0上,另一端放入容器内并重命名为eth0。容器之间、容器与宿主机之间皆通过此网桥进行局域网通信。 - Host (主机模式):
通过--network host指定。容器将彻底解除网络隔离,直接共享宿主机的 Network Namespace。容器内的进程会直接占用宿主机的端口(例如容器内的 Nginx 会直接监听宿主机的 80 端口)。此模式免去了 NAT 转换的开销,网络性能最高,但极易引发端口冲突。 - None (无网络模式):
通过--network none指定。容器拥有独立的 Namespace,但 Docker 引擎不为其配置任何网络接口,仅保留一个lo(环回接口)。该模式提供了极致的安全隔离,适用于处理机密数据、不需要外部网络通信的纯离线计算任务。
端口映射与 NAT 转发机制 (Port Mapping & NAT)
在默认的 Bridge 模式下,容器被分配的是内部局域网 IP(如 172.17.0.2),外部网络无法直接路由寻址。
为了将 Web 服务暴露给公网,我们通常使用 -p 宿主机端口:容器端口 命令。其底层原理是 Docker 引擎动态修改了宿主机的 iptables 防火墙规则,注入了 DNAT (Destination Network Address Translation,目标地址转换) 指令。
当外部流量打在宿主机的特定端口时,系统会在内核层面直接将数据包的“目标 IP 和端口”重写为容器的内部 IP 和端口,从而实现流量的精准穿透。
自定义网络与服务发现 (User-Defined Networks & Service Discovery)
在真实的微服务架构(如 WordPress 容器集群)中,容器的生命周期非常短暂,重启后其内部 IP 会发生变化。因此,硬编码 IP 地址进行容器间通信是工程实践中的大忌。
为了解决这一痛点,我们通常使用 docker network create 创建自定义网桥(或使用 Docker Compose 自动生成的 default 网络)。自定义网络不仅具备基本网桥的功能,更重要的是,Docker 引擎在其中内置了 Embedded DNS Server(内嵌 DNS 服务器)。
服务发现机制:
在自定义网络下的所有容器,可以直接使用容器名称 (Container Name) 或服务别名作为域名进行互相访问。例如,WordPress 容器无需知道数据库容器的真实 IP,只需在配置文件的 Host 字段填入 local-wp-db,内嵌 DNS 就会自动在底层将其解析为当前正确的容器 IP。这种机制使得架构具备了极强的弹性和解耦能力。