当前位置: 首页 > ai >正文

从零开始的 Docker 之旅

目录

一,docker架构

Docker Client(客户端)

Docker Daemon(守护进程)

Docker Image(镜像)

Docker Container(容器)

Docker Registry(仓库)

镜像基本命令

容器基本命令

二,docker image(镜像)

核心特性:

组成内容:

与容器的关系:

1. 环境一致性问题

2. 依赖管理复杂问题

3. 资源隔离与高效利用问题

4. 版本控制与可追溯问题

5. 快速分发与部署问题

6. 开发与运维协作问题

三,docker container(容器)

核心特性:

容器的生命周期

常用命令

容器交互模式

四,docker volume(存储卷)

存储卷的主要分类及特点

1.管理卷

2.绑定卷(Bind Mounts)

3.临时卷tmpfs (临时文件系统)

为什么需要存储卷

常用命令

卷的使用操作

五,docker network(网络)

为什么需要网络

常用命令

docker常见网络类型

1.bridge网络(默认网络)

2.host网络

3.container网络

4.none网络

六,docker compose(容器编排)

主要作用

docker-compose.yml常见参数配置

常用命令

docker compose部署WordPress

七,docker镜像制作

通过快照方式制作镜像

Dockerfile制作镜像

1.Dockerfile常用指令

2.镜像创建命令

3.Dockerfile编写建议

Dockerfile操作实战

1.制作一个C++镜像

2.CMD与ENTYRPOINT实战

3.多阶段构建镜像

Dockerfile与docker compose结合的操作实战

搭建mysql主从同步(一主两从)

Docker镜像制作常见问题

八,docker镜像原理

分层存储

UnionFS联合文件系统

分层存储的实现

overlay文件系统工作实战

九,docker存储卷原理

十,docker网络原理

Linux常见网络虚拟化

虚拟网卡:tun/tap

虚拟网卡:veth

虚拟交换机(网桥)

Docker bridge网桥


一,docker架构

Docker 架构采用客户端 - 服务器(C/S)模型,由多个核心组件协同工作,实现容器的创建、运行、分发和管理。

主要包含以下组件:

Docker Client(客户端)

  • 作用:用户与 Docker 交互的入口,接收用户命令(如 docker run、docker build)并发送给 Docker Daemon。

  • 示例:执行 docker run nginx 时,客户端将命令转发给 Daemon 处理

Docker Daemon(守护进程)

  • 作用:运行在宿主机上的后台进程(dockerd),负责管理 Docker 所有资源(容器、镜像、网络、存储卷等),处理 Client 发送的命令。

  • 功能:

    • 构建、运行和监控容器;

    • 管理镜像的拉取、推送和存储;

    • 维护网络和存储卷的配置。

Docker Image(镜像)

  • 作用:容器的只读模板,包含运行应用所需的代码、依赖、配置等(如一个 Nginx 镜像包含 Nginx 程序及基础系统文件)。

  • 特性:

    • 分层存储(基于联合文件系统,如 OverlayFS),每层可复用,节省空间;

    • 不可修改(只读),容器运行时在镜像上层添加可写层。

  • 获取方式:从 Docker Registry 拉取(如 Docker Hub),或通过 Dockerfile 构建。

Docker Container(容器)

  • 作用:镜像的可运行实例,是隔离的应用运行环境。

  • 与镜像的关系:容器 = 镜像(只读层) + 可写层(容器运行时的修改)。

  • 核心隔离技术:

    • Namespace:隔离进程、网络、挂载点等(如 PID Namespace 确保容器内进程 ID 独立);

    • Cgroups:限制容器的 CPU、内存等资源;

    • UnionFS:实现镜像分层和可写层的高效管理。

Docker Registry(仓库)

  • 作用:存储和分发 Docker 镜像的服务(类似代码仓库)。

  • 分类:

    • 公共仓库:如 Docker Hub(默认仓库,包含大量官方镜像);

    • 私有仓库:企业或个人自建的仓库(如 Harbor),用于存储私有镜像。

  • 操作:通过 docker push 上传镜像,docker pull 拉取镜像。

镜像基本命令

1.docker images

功能:列出本地镜像

语法:docker images 【选项】 【仓库名称【:tag】】

该命令还有别名:docker image ls,docker image list

选项:

  • -a:列出本地所有镜像

  • --digests:显示镜像的摘要信息(sha256)

  • -f:显示满足条件的镜像

  • --format:指定返回值的模板文件(json/table)

  • --no-trunc:显示完整的镜像信息

  • -q:只显示镜像ID(digest)

2.docker image inspect

功能:查看具体一个镜像的信息

语法:docker image inspect image:tag|imageid

3.docker tag

功能:标记本地镜像(重命名),将其归入某一仓库中

语法:docker tag source_image[:tag] target_image[:tag]

容器基本命令

4.docker run

功能:创建一个容器并运行一个目录

语法:docker run 【选项】 image 【命令】

别名:docker run container

  • -d:后台运行容器

  • -i:以交互模式运行容器,通常与-t搭配使用

  • -P:随机端口映射,容器内部端口随机映射到主机端口

  • -p:指定端口映射,格式为:【主机端口:容器端口 】

  • -t:为容器分配一个伪输入终端,通常与-i搭配使用

  • --name="":为容器指定一个名称

  • -h:指定容器的主机名

  • -e:设置环境变量,例如:-e username=" "

  • --cpuset-cpus="0-2" or --cpuset-cpus="0,1,2":绑定容器到指定CPU运行

  • -m:设置容器使用内存最大值

  • --network="bridge":指定容器的网络连接类型

  • --link=[]:添加链接到另一个容器。

  • --volume,-v:绑定一个卷

  • --rm:shell退出时自动删除容器

当你执行 docker run centos:7 后容器立即退出,是因为 容器内没有持续运行的进程,这是 Docker 容器的核心特性决定的:Docker 容器的生命周期与容器内的 主进程(PID 1) 绑定:

  • 当主进程运行时,容器保持运行状态

  • 当主进程结束(退出)时,容器也会随之停止

简单说:容器需要一个 "永不结束" 的主进程才能保持运行状态,否则会随主进程退出而终止。

CentOS 7 镜像的默认行为是启动 /bin/bash 作为主进程,但:

  • 如果你没有添加交互参数(-it),bash 会因为没有终端输入而立即退出

  • 即使有终端,若你执行 exit 或 Ctrl+D 退出 bash,主进程结束,容器也会停止

要让 CentOS 7 容器持续运行,可以:

1,交互模式运行(适合临时操作):docker run -it centos:7 /bin/bash

此时会进入容器的交互式终端,退出终端后容器会停止。

2,后台运行并保持进程(适合长期运行):docker run -d centos:7 tail -f /dev/null

tail -f 命令保持主进程持续运行,容器会在后台一直运行。

--link 选项用于在两个容器之间建立单向链接,使一个容器可以通过别名访问另一个容器(无需知道对方的 IP 地址)。这是早期 Docker 中实现容器间通信的常用方式,不过目前更推荐使用网络(network) 来管理容器通信。

基本语法 :docker run --name 容器名 --link 目标容器名:别名 镜像名

  • 目标容器名:需要被链接的容器的名称(必须是已运行的容器)。

  • 别名:当前容器内部用来访问目标容器的别名(可自定义,方便记忆)。

链接后,当前容器的 /etc/hosts 文件会自动添加一条记录,将 “别名” 映射到目标容器的 IP 地址。

5.docker ps

功能:列出正在运行的容器

语法:docker ps 选项

  • -a:显示所有的容器,包括未运行的

  • -f:根据条件过滤先试试内容

  • --format:指定返回的模板(JSON/table)

  • -l:显示latest容器

  • -n:列出最近创建的n个容器

  • --no-trunc:不截断输出

  • -q:静默模式,只显示容器编号

6.docker history

功能:查看镜像历史

语法:docker history 选项 image/imageid

-H,--human:大小和日期采用人能读懂的格式 展现

--no-trunc:显示全部信息,不要隔断

-q,--quiet:只显示镜像id信息

7.docker image prune

功能:删除不使用的镜像

语法:docker image prune 选项

scp(Secure Copy)是一个基于 SSH 协议的文件传输命令,用于在本地主机和远程主机之间安全地复制文件或目录。它使用与 SSH 相同的加密机制,确保数据传输的安全性。

scp基本语法

# 本地文件复制到远程
scp [选项] 本地文件路径 远程用户@远程IP:远程目标路径# 远程文件复制到本地
scp [选项] 远程用户@远程IP:远程文件路径 本地目标路径# 复制目录(需加 -r 选项)
scp -r [选项] 本地目录路径 远程用户@远程IP:远程目标路径
scp -r [选项] 远程用户@远程IP:远程目录路径 本地目标路径

二,docker image(镜像)

Docker 镜像是 Docker 技术的核心组成部分,可以理解为一个包含应用程序及其所有运行依赖的只读模板,是容器运行的基础。本质上是由多层文件系统叠加而成的特殊文件集合。

核心特性:

  1. 只读性 镜像一旦构建完成,就不可修改( Immutable )。这保证了镜像的一致性 —— 无论在什么环境中使用,基于同一镜像启动的容器都能保持相同的运行状态,避免因意外修改导致的环境差异。

  2. 分层结构 镜像由多个只读层( Layer )组成,每层对应 Dockerfile 中的一条指令(如 FROM、RUN、COPY 等),这些命令在dockerfile制作镜像部分有解释。例如:

    1. 基础层:可能是操作系统(如 centos:7 的底层文件系统);

    2. 中间层:可能是安装的依赖库(如 RUN yum install nginx 生成的层);

    3. 顶层:可能是应用代码(如 COPY app /usr/share/nginx/html 生成的层)。 分层设计的好处是复用性:不同镜像可共享相同的底层(如多个应用都基于 centos:7,则仅需存储一次基础层),节省存储空间和网络传输量。

  3. 可被容器继承 容器是镜像的 “运行实例”:启动容器时,Docker 会在镜像的只读层之上添加一个可写层(容器层),容器的所有修改(如文件创建、删除)都只发生在可写层,不会影响底层镜像。这意味着一个镜像可以同时启动多个独立的容器,且容器间的修改互不干扰。

组成内容:

一个完整的 Docker 镜像包含运行应用所需的全部要素:

  • 应用程序代码(如 Python 脚本、Java 字节码等);

  • 运行时环境(如 Python 解释器、JRE 等);

  • 依赖库(如应用所需的第三方库、系统工具等);

  • 配置文件(如环境变量、服务配置等);

  • 操作系统核心文件(如基础镜像提供的 Linux 内核相关文件,注意:镜像不包含内核本身,而是共享宿主机内核)。

与容器的关系:

可以简单理解为:

镜像 = 类(Class),容器 = 实例(Instance)。

镜像定义了应用的 “模板”,容器则是这个模板的 “运行状态”,一个镜像可以生成多个容器,容器的生命周期独立于镜像。

总结来说,Docker 镜像通过标准化打包和分层设计,解决了 “环境一致性”“依赖管理”“快速分发” 等传统部署难题,是实现 “一次构建,到处运行” 的核心保障。

Docker 镜像作为容器技术的核心组件,解决了传统软件部署和运行中的一系列痛点问题,主要体现在以下几个方面:

1. 环境一致性问题

  • 痛点:传统部署中,“在我电脑上能运行,到服务器上就报错” 是常见问题,根源在于开发、测试、生产环境的配置(如依赖库、系统版本、环境变量等)不一致。

  • 解决:Docker 镜像通过 分层文件系统 打包应用程序及其所有依赖(代码、运行时、库、环境变量等),形成一个不可变的只读模板。无论在哪个环境(开发机、测试服务器、生产服务器),基于同一镜像启动的容器都能保证运行环境完全一致,从根本上消除 “环境不一致” 导致的问题。

2. 依赖管理复杂问题

  • 痛点:安装一个应用可能需要手动安装多个依赖库,且依赖之间可能存在版本冲突(如 “应用 A 需要 Python 3.8,应用 B 需要 Python 3.6”),导致部署过程繁琐且容易出错。

  • 解决:镜像将应用及其所有依赖(包括特定版本的运行时、库、工具等)一次性打包,无需在目标环境中手动安装依赖。每个应用的依赖被隔离在独立镜像中,不同应用的依赖冲突问题自然消失。

3. 资源隔离与高效利用问题

  • 痛点:传统虚拟机(VM)为每个应用分配独立的操作系统,资源占用高(通常需要 GB 级内存),启动慢(分钟级),且难以高效密集部署。

  • 解决:Docker 镜像基于 容器技术 运行,容器共享宿主机的操作系统内核,仅占用应用本身的资源(MB 级内存常见),启动速度快(秒级)。同时,镜像保证了应用间的资源隔离(文件系统、网络、进程等),既避免了相互干扰,又比虚拟机更轻量,大幅提高了服务器资源利用率。

4. 版本控制与可追溯问题

  • 痛点:传统部署中,应用版本迭代后难以回滚,且无法清晰追溯每个版本的具体内容(如依赖变化、配置修改)。

  • 解决:Docker 镜像支持 版本标签(Tag),每个标签对应一个不可变的镜像版本(如 nginx:1.21mysql:8.0)。迭代时只需构建新镜像并打上标签,回滚时直接使用旧标签的镜像即可。此外,镜像的分层结构允许查看每一层的变更记录,便于追溯版本差异。

5. 快速分发与部署问题

  • 痛点:传统软件分发依赖安装包(如 .tar.gz.exe),部署时需手动传输安装包并执行复杂的安装脚本,耗时且易出错,尤其在大规模集群部署时效率极低。

  • 解决:Docker 镜像可通过 镜像仓库(如 Docker Hub、私有仓库)集中管理和分发,支持 分层传输(仅传输与本地已有镜像差异的层),大幅减少网络传输量。部署时只需一句 docker run 命令即可基于镜像启动容器,实现 “一次构建,到处运行”,显著简化大规模部署流程。

6. 开发与运维协作问题

  • 痛点:开发人员专注于代码实现,运维人员负责部署环境,两者之间常因 “环境理解不一致” 产生协作障碍(如开发文档遗漏依赖说明)。

  • 解决:Docker 镜像成为开发与运维之间的 “统一交付物”:开发人员通过 Dockerfile 定义镜像构建流程(明确依赖和配置),运维人员直接使用镜像部署,无需关心内部细节,减少沟通成本和协作冲突。

三,docker container(容器)

Docker 容器是基于 Docker 镜像运行的可执行实例,是镜像的动态表现形式。它包含了应用程序运行所需的完整环境(代码、运行时、依赖、配置等),并通过操作系统级虚拟化技术实现了资源隔离和独立运行,是 Docker 技术中用于实际运行应用的核心载体。

核心特性:

  1. 轻量级 容器共享宿主机的操作系统内核,无需像虚拟机(VM)那样加载完整的操作系统,因此启动速度极快(通常秒级),资源占用低(内存、磁盘空间需求远小于虚拟机),可以在一台物理机上密集部署成百上千个容器。

  2. 隔离性 容器之间通过 Linux 命名空间(Namespace)、控制组(CGroup)等技术实现隔离,包括:

    1. 进程隔离:容器内的进程无法看到宿主机或其他容器的进程;

    2. 网络隔离:每个容器有独立的网络栈(IP、端口等),可通过虚拟网络实现容器间通信;

    3. 文件系统隔离:容器有自己的文件系统视图(基于镜像的只读层 + 容器可写层),修改仅作用于当前容器;

    4. 资源隔离:通过 CGroup 限制容器的 CPU、内存、磁盘 I/O 等资源使用,避免相互干扰。

  3. 可移植性 基于同一镜像启动的容器,在任何支持 Docker 的环境(开发机、测试服务器、云平台等)中都能以相同的方式运行,真正实现 “一次构建,到处运行”。

  4. 生命周期可管理 容器有明确的生命周期,可通过 Docker 命令创建、启动、停止、重启、删除等,状态变化清晰可控。

容器的生命周期

容器的生命周期是容器可能处于的状态。

created:初建状态,running:运行状态,stopped:停止状态,paused:暂停状态,deleted:删除状态

  1. docker create:创建容器后,不立即启动执行,容器进入初建状态。

  2. docker run:创建容器,并立即启动,进入启动状态。

  3. docker start:容器转为运行状态。

  4. docker stop:容器进入停止状态。

  5. docker kill : 容器在故障(死机)时,执行 kill(断电),容器转入停止状态,这种操作容易丢失数据,除非必要,否则不建议使用;

  6. docker restart : 重启容器,容器转入运行状态;

  7. docker pause : 容器进入暂停状态;

  8. docker unpause : 取消暂停状态,容器进入运行状态;

  9. docker rm:删除容器,容器转入删除状态。

  10. killed by out-of-memory(因内存不足被终止) :宿主机内存被耗尽,也被称为 OOM:非计划终止 这时需要杀死最吃内存的容器

  11. container process exitde(异常终止):出现容器被终止后,将进入 Should restart?选择操作:

    1. yes 需要重启,容器执行 start 命令,转为运行状态。

    2. no 不需要重启,容器转为停止状态。

常用命令

1,docker logs

功能:查看容器日志

语法:docker logs 【选项】 container

参数:

-f,--follow:跟踪日志输出

--since:显示某个开始时间的所有日志。

-t,--timestamps:显示时间戳

-n,--tail:仅列出最新的n条日志

2,docker exec

功能:在容器中执行命令

语法:docker exec 【选项】container command 【args】

参数:

-d:在后台运行

-i:进行交互

-t:分配一个伪终端

-e:设置环境变量

-u,--user:指定用户

-w:指定工作目录

3,docker stop

功能 :停止运行的容器

4,docker restart

功能:重启容器

5,docker kill

功能:强制退出容器

6,docker top

功能:查看容器中的 进程信息

7,docker stats

功能:查看容器资源的使用情况,包括CPU,内存,网路I/O等

8,docker cp

功能:在容器和宿主机之间拷贝文件

9,docker commit

功能:从容器创建一个新的镜像

语法:docker commit 选项 container image[:tag]

-a:提交镜像作者

-c:通过dockerfile指令来创建镜像,可以修改启动指令

-m:提交时的文字说明

-p:在commit时,将容器暂停掉

10,docker export

功能:将容器转为tar文件

语法:docker export 选项 container

-o:写入到文件

11,docker import

功能:从归档文件中创建镜像

语法:docker import 【选项】 file image[:tag]

12.docker update

docker update 是 Docker 命令行工具中用于动态更新容器配置的命令

语法:docker update [OPTIONS] CONTAINER [CONTAINER...]

常用选项

  • --cpus:设置容器可以使用的 CPU 核心数(例如 --cpus 0.5 表示使用半个核心)

  • --cpu-shares:设置 CPU 共享权重(相对优先级)

  • --memory-m:设置容器可以使用的最大内存量(例如 -m 512m

  • --memory-swap:设置容器可以使用的内存 + 交换分区总量

  • --restart:更改容器的重启策略(例如 --restart always

适用于将 Docker 镜像在不同主机、仓库之间迁移。

1. 在源主机保存镜像为 tar 文件

docker save -o 镜像名.tar 镜像名:标签 # 例:docker save -o nginx.tar nginx:latest

2. 通过 scp/ftp 等工具将 tar 文件传输到目标主机

scp 镜像名.tar 目标用户@目标IP:/路径 # 例:scp nginx.tar user@192.168.1.100:/tmp

3. 在目标主机加载镜像

docker load -i 镜像名.tar

容器交互模式

attached模式

容器运行语句示例:docker run --name mytest1 -p 8100:80 nginx:1.24.0(不加-d,-it选项)

通过上述方式创建容器,就是attached模式,这样容器会在前台运行。

detached模式

在容器运行语句中加上-d选项,就是detached模式,表示在后台运行。

使用docker attach 容器名,可以 让一个detached模式的容器转化为attached模式。

interactive模式

创建一个interactive(交互 )模式的容器,需要带上-it选项。

创建运行容器并进入到交互模式:docker run -it --name mytest1 -p 8100:80 nginx:1.24.0 bash

针对一个已经运行的容器进入交互模式:

docker run --name mytest5 -d nginx:1.24.0

docker exec -it mytest5 bash

四,docker volume(存储卷)

在 Docker 中,存储卷(Volumes) 是用于持久化存储容器数据的机制,它将容器内的数据与容器本身的生命周期解耦,确保容器删除或重建后数据不会丢失。存储卷本质上是宿主机文件系统中的一段特殊目录,由 Docker 管理,与容器内的指定路径形成映射。

存储卷的主要分类及特点

1.管理卷
  • 定义:未命名的卷,由 Docker 自动创建,生成随机唯一标识作为名称,也可以自定义名称。

  • 存储位置:默认位于 Docker 管理的目录(/var/lib/docker/volumes/<卷名>/_data),具体路径由 Docker 自动分配。

  • 优点:

    • 无需手动创建,适合临时或一次性数据存储。

    • 避免容器内临时文件占用镜像空间。

  • 缺点:

    • 无明确名称,管理困难(难以识别卷对应的用途)。

    • 容易产生大量未使用的 “孤儿卷”,浪费存储空间。

  • 使用场景:

    • 容器运行过程中产生的临时数据(如日志缓存、临时文件)。

    • 避免容器内某些目录被镜像覆盖(如通过 DockerfileVOLUME 指令定义)

2.绑定卷(Bind Mounts)
  • 定义:将宿主机的任意目录或文件直接挂载到容器内的指定路径,不由 Docker 管理存储位置。

  • 存储位置:用户指定的宿主机路径(如 /home/user/data)。

  • 优点:

    • 直观可控,可直接通过宿主机路径操作数据。

    • 适合开发环境中实时同步代码(如将本地代码目录挂载到容器,修改后立即生效)。

  • 缺点:

    • 依赖宿主机文件系统结构,跨环境移植性差(不同主机路径可能不同)。

    • 容易出现权限问题(容器内用户与宿主机用户 ID 可能不匹配)。

    • 若宿主机路径不存在,Docker 会自动创建目录(可能导致预期外的权限问题)。

  • 使用场景:

    • 开发阶段挂载代码目录,实时调试。

    • 挂载宿主机的配置文件(如 /etc/hosts、日志配置)到容器。

    • 需要直接访问宿主机文件系统的场景。

3.临时卷tmpfs (临时文件系统)
  • 定义:将数据存储在宿主机的内存中(而非磁盘),容器停止后数据立即消失。

  • 存储位置:宿主机的内存或 swap 分区。

  • 优点:

    • 读写速度极快,适合临时高频访问的数据。

    • 数据隔离性好,容器停止后自动清理,不占用磁盘空间。

  • 缺点:

    • 数据无法持久化,容器重启后丢失。

    • 受限于宿主机内存大小,不适合存储大量数据。

  • 使用场景:

    • 存储敏感数据(如密码、会话信息),避免持久化到磁盘。

    • 临时缓存数据(如应用运行时的临时计算结果)。

为什么需要存储卷

1. 容器的 “临时性” 与数据的 “持久性” 矛盾

  • 容器的生命周期特性:容器本质上是 “临时的”—— 当容器被删除、重建或重启时,容器内部的文件系统会被重置(基于镜像重新初始化)。如果数据直接存储在容器内部,会随容器消失而丢失。

  • 存储卷的解决方式:存储卷将数据存储在容器之外(宿主机或外部存储),与容器生命周期解耦。即使容器被删除,卷中的数据仍可保留,新容器可通过挂载该卷复用数据。

  • 例:数据库容器(如 MySQL)的数据必须持久化,否则容器重启后数据会全部丢失,通过存储卷挂载 /var/lib/mysql 目录即可解决。

2.容器间的数据共享需求

  • 多容器协作场景:在微服务架构中,多个容器可能需要共享数据(如配置文件、静态资源、日志文件等)。例如:

    • Nginx 容器需要访问前端应用容器生成的静态文件;

    • 多个应用容器共享同一套配置文件(如 /etc/config)。

  • 存储卷的解决方式:多个容器可同时挂载同一个存储卷,实现数据实时共享。相比通过网络传输数据,卷共享更高效且实时性更强。

3. 宿主机与容器的数据交互需求

  • 开发与调试场景:开发时需要将宿主机的代码、配置文件实时同步到容器中(修改宿主机文件后,容器内立即生效),避免频繁重建镜像。

  • 数据备份与迁移场景:需要从宿主机直接访问容器数据(如备份日志、导出数据库文件),或向容器内导入初始数据(如初始化脚本)。

  • 存储卷的解决方式:通过 “绑定挂载”(Bind Mounts)将宿主机目录直接挂载到容器,或通过卷的存储路径(如 /var/lib/docker/volumes/<卷名>/_data)在宿主机操作数据。

总结

存储卷的核心价值是解决容器与数据的生命周期分离问题,同时满足数据持久化、跨容器共享、宿主机交互、镜像精简、跨环境兼容等需求。无论是数据库、日志、配置文件还是用户数据,只要需要长期保留或多组件共享,就必须使用存储卷。

简单来说:没有存储卷,容器就只能作为 “无状态服务” 运行;有了存储卷,容器才能可靠地处理 “有状态数据”。

常用命令

1.创建卷

# 创建命名卷(最常用)
docker volume create <卷名称>
//如果不指定卷名称,docker会随机生成# 示例:创建一个名为 db_data 的卷
docker volume create db_data

2.查看卷列表

# 列出所有卷
docker volume ls# 过滤查看(例如只看本地卷)
docker volume ls --filter driver=local

3.查看卷详情

# 查看指定卷的详细信息(包括存储路径、驱动等)
docker volume inspect <卷名称># 示例:查看 db_data 卷的信息
docker volume inspect db_data
输出结果中会显示卷在宿主机的实际路径(如 /var/lib/docker/volumes/db_data/_data)。

4.删除卷

# 删除指定卷(需确保卷未被任何容器使用)
docker volume rm <卷名称># 示例:删除 db_data 卷
docker volume rm db_data

5.清理未使用的卷

# 删除所有未被容器引用的卷(谨慎操作)
docker volume prune# 强制删除(无需确认)
docker volume prune -f

卷的使用操作

1.创建管理卷

-v:创建管理卷,并启动容器

# 运行容器时挂载卷
docker run -d --name <容器名> -v <卷名称>:<容器内目录> <镜像名># 示例:将 db_data 卷挂载到 MySQL 容器的数据目录
docker run -d --name mysql_db -v db_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

-mount:创建管理卷,并启动容器

# 运行容器时挂载卷
docker run -d --name <容器名> --mount src=卷名称,dst=容器内目录 <镜像名># 示例:将 db_data 卷挂载到 MySQL 容器的数据目录
docker run -d --name mysql_db --mount src=db_data,dst=/var/lib/mysql -e  MYSQL_ROOT_PASSWORD=123456 mysql:8.0

卷的共享

# 容器1挂载卷
docker run -d --name app1 -v shared_data:/data nginx# 容器2挂载同一个卷(实现数据共享)
docker run -d --name app2 -v shared_data:/data nginx

2.创建绑定卷

-v:创建绑定卷

语法:docker run -v name:directory[:options] ...
第一个参数name为宿主机的一个目录,这和管理卷是不一样的,如果是管理卷,表示卷的名称。
第二个参数directory:映射到容器中的目录
第三个参数options:选项,比如ro表示readonly
如果指定的宿主机的目录不存在,会建立该目录

--mount:创建绑定卷

语法:docker run --mount type=bind,src= ,dst= ...
需要指明类型为bind
src:宿主机目录
dst:容器内目录
同样也可以指定ro选项
如果指定的宿主机的目录不存在,会报错

如果宿主机指定的目录和容器指定的目录下都存在文件,以宿主机的目录为准,容器目录的文件会同步与宿主机目录下的文件一致。

3.创建临时卷

临时卷tmpfs的局限性:不能在容器之间共享,只能在linux系统上运行

--tmpfs创建:

语法:docker run --tmpfs 容器内目录 ...

--mount创建:

语法:docker run --mount type=tmpfs,dst=容器内目录 ....

参数:tmpfs-size:卷的大小(以字节为单位),默认是无限制的

五,docker network(网络)

为什么需要网络

容器的本质是 “被隔离的进程”,它拥有独立的文件系统、进程空间、用户空间,但网络不能完全隔离(否则容器就是一个 “孤岛”,无法发挥作用)。

具体来说,Docker 需要网络的核心原因包括:

  1. 容器间通信的需求 真实应用往往由多个组件构成(例如:Web 服务容器需要连接数据库容器,前端容器需要调用后端 API 容器)。这些容器必须通过网络交换数据,才能协同工作。

  2. 容器与外部环境通信的需求 容器不能只在内部闭环,还需要与外部交互:

    1. 容器需要访问互联网(如下载依赖、调用第三方 API);

    2. 外部用户 / 服务需要访问容器(如用户通过浏览器访问容器中的 Web 服务);

    3. 容器需要与宿主机通信(如读取宿主机的配置文件、日志文件)。

  3. 网络隔离与安全的需求 虽然容器需要通信,但并非所有容器都应该能相互访问(例如:数据库容器不应被公网直接访问,只允许业务容器访问)。网络需要支持隔离策略(如限制 IP、端口、协议),以保证安全性。

  4. 跨主机容器通信的需求 当容器部署在多台主机上(例如:分布式系统、集群),不同主机的容器需要通信(如 Kubernetes 集群中跨节点的 Pod 通信)。这需要网络支持跨主机的数据传输。

常用命令

1.docker network create

功能:创建自定义网络

语法:docker network create 【选项】 NETWORK

参数:

-d,--driver:网络驱动

--gateway:网关地址

--subnet:表示网段的CIDR格式的子网

--ipv:启用ipv6

2.docker netowrk inspect

功能:查看网络详情

语法:docker netowrk inspect 【选项】NETWORK

参数:

-f,--format:指定输出格式(JSON/table)

3.docker network connect

功能:将容器连接到网络,可以按名称或ID连接容器。一旦连接,容器可以与网络中的其他容器通信。

语法:docker network connect 【选项】 NETWORK CONTAINER

参数:

--ip:指定ip地址

--ipv6:指定ipv6地址

4.docker network disconnect

功能:断开网络

语法:docker network disconnect 【选项】 NETWORK CONTAINER

参数:

-f:强制断开

5.docker network prune

功能:删除不使用的网络

6.docker network rm

功能:删除一个或多个网络

语法:docker network rm NETWORK

7.docker network ls

功能:展示出 创建 的网络

参数:

-f,--filter:指定过滤条件

--format:指定格式

--no-trunc:不截断

-q,--quiet:只显示id

docker常见网络类型

Docker 通过不同的网络驱动提供多种网络类型,默认包含 5 种常用驱动,用户也可安装第三方驱动。

1.bridge网络(默认网络)
  • 特点:Docker 的默认网络模式,适用于单机内容器通信。

  • 原理: 宿主机创建名为docker0的默认网桥(自定义 bridge 网络会创建独立网桥),容器通过 veth pair 接入网桥,获得私有 IP(如172.17.x.x)。

bridge 桥接模式可以参考下图:

bridge网络对应的网桥就是docker0

容器间可通过 IP 或名称(自定义 bridge 网络支持)通信,与外部通信需通过宿主机的 NAT 转换。

  • 适用场景:单机环境下的多容器应用(如 Web 容器 + 数据库容器)。

  • 示例:

# 创建自定义bridge网络
docker network create my-bridge
# 启动容器并接入该网络
docker run -d --name app --network my-bridge nginx#如果不指定网络,会接入默认的bridge网络
2.host网络
  • 特点:容器直接使用宿主机的网络命名空间,无网络隔离。

  • 原理: 容器不创建独立网卡和 IP,与宿主机共享 IP 地址、端口和路由表。容器内的端口直接暴露在宿主机上,无需端口映射。

  • 优势:网络性能最优(无 NAT 转发开销)。

  • 劣势:安全性差(容器可能占用宿主机端口或修改网络配置)。

  • 适用场景:对网络性能要求极高的场景(如高并发服务)。

  • 示例:

 # 容器的80端口直接映射到宿主机80端口docker run -d --network host nginx 
3.container网络

Docker Container 共享其他容器的网络环境,则至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。

示例:

#创建容器1
docker run -dit --name container1 busybox
# 使用容器1的网络创建容器2
docker run -dit --name container2 --network container:container1 busybox
4.none网络
  • 特点:完全禁用容器网络,容器无网卡、IP 和路由,处于 “网络孤岛” 状态。

  • 适用场景:仅需本地计算、无需任何网络通信的容器(如离线数据处理)。

  • 示例:

# 容器无网络连接
docker run -d --network none busybox 

六,docker compose(容器编排)

Docker Compose 是 Docker 官方提供的一个工具,用于定义和运行多容器 Docker 应用程序。它允许你通过一个 YAML 格式的配置文件(通常命名为 docker-compose.yml)来声明式地配置应用程序所需的所有服务(容器),然后使用单一条命令即可创建和启动所有服务。

主要作用

当你的应用需要多个容器协同工作时(比如 WordPress 需要 WordPress 容器和 MySQL 容器配合),手动逐个启动容器并配置它们之间的网络连接会很繁琐。Docker Compose 可以:

  1. 用配置文件定义所有服务的参数(镜像、端口映射、环境变量、数据卷、依赖关系等)

  2. 一键创建和启动所有服务(docker compose up -d

  3. 一键停止和删除所有服务及相关资源(docker compose down

  4. 方便地管理服务的生命周期(启动、停止、重启、查看日志等)

docker-compose.yml常见参数配置

1.顶级参数

version 指定 Compose 文件格式版本(需与 Docker 版本兼容),推荐使用 '3.8'(较新且兼容性好)。 示例:version: '3.8'

2. services 服务配置(核心)

每个服务对应一个容器,是配置的核心部分,常见参数:

  • image 指定容器使用的镜像(如官方镜像或自定义镜像)。 示例:image: nginx:latest 或 image: my-custom-image:1.0

  • container_name 自定义容器名称(默认自动生成),需唯一

  • ports 端口映射(主机端口:容器端口),暴露容器端口到主机。

示例:

ports:- "8080:80"  # 主机 8080 映射到容器 80- "443:443"  # 映射 HTTPS 端口
  • networks 指定容器加入的网络(需在顶级 networks 中定义),实现容器间通信。 示例:

services:app:              #服务名networks:- my-network  # 加入自定义网络
networks:my-network:  # 定义网络

volumes 数据卷挂载,用于持久化数据或共享文件(主机路径:容器路径)。 示例:

volumes:- ./html:/var/www/html  # 主机目录挂载到容器- data-volume:/var/lib/mysql  # 使用命名卷(需在顶级 volumes 定义)
volumes:data-volume:  # 定义命名卷
  • tmpfs 挂载临时文件系统(容器内,不持久化)。 示例:tmpfs: /tmp

  • environment 设置环境变量(键值对或列表形式)。 示例:

environment:- MYSQL_ROOT_PASSWORD=root123- DEBUG=false
# 或字典形式
environment:WORDPRESS_DB_HOST: db
  • env_file 从外部文件加载环境变量(避免明文暴露敏感信息)。 示例:env_file: .env (.env 文件中每行是 KEY=VALUE)

  • depends_on 定义服务启动顺序(依赖的服务先启动)。 示例:

depends_on:- db  # 依赖 db 服务- redis:condition: service_healthy  # 等待 redis 健康检查通过
  • estart 容器重启策略(应对故障自动恢复)。 可选值:

    • no(默认,不重启)

    • always(总是重启)

    • on-failure(失败时重启)

    • unless-stopped(除非手动停止,否则重启) 示例:restart: always

  • healthcheck 定义容器健康检查规则,用于 depends_on 判断服务是否就绪。 示例:

healthcheck:test: mysql -uroot -proot -e 'select 1;'interval: 10stimeout: 5sretries: 10

常用命令

1. 启动服务

docker compose up
功能:创建并启动所有服务(根据 docker-compose.yml 配置)
常用选项:-d:后台运行容器( detached 模式),如 docker compose up -d--build:启动前重新构建服务的镜像,如 docker compose up --build

2.停止并删除服务

docker compose down
功能:停止并删除所有容器、网络,默认保留数据卷(volumes)
常用选项:
-v:同时删除数据卷,如 docker-compose down -v
--rmi all:删除所有相关镜像

3.查看服务状态

docker compose ps
功能:列出所有正在运行的服务容器,显示容器名称、状态、端口映射等信息

4.查看服务日志

docker compose logs
功能:查看所有服务的日志输出
常用选项:
-f:实时跟踪日志(类似 tail -f),如 docker compose logs -f
可指定单个服务名,只看该服务日志,如 docker compose logs -f wordpress

5. 启动 / 停止 / 重启服务

# 启动已存在的服务(不重新创建)
docker compose start# 停止运行中的服务(不删除容器)
docker compose stop# 重启服务
docker compose restart
  • 可指定单个服务名,如 docker compose restart db 只重启数据库服务

docker compose部署WordPress

WordPress 是一款开源的内容管理系统(CMS),主要用于快速搭建和管理网站,无论是个人博客、企业官网、电商平台还是新闻门户等,都可以通过它实现。

1,编写docker-compose.yml文件

services:wordpress:image: wordpressrestart: alwaysports:- 8050:80environment://要连接的数据库的ip地址,即配置的db服务,docker会自动解析,将容器名转化为对应的ipWORDPRESS_DB_HOST: db//连接数据库时的用户名WORDPRESS_DB_USER: mywordpressuser//用户名对应的密码WORDPRESS_DB_PASSWORD: mywordpresspass//要使用的数据库的名称WORDPRESS_DB_NAME: wordpressvolumes:- ./wordpress/:/var/www/htmldepends_on:db:condition: service_healthydb:image: mysql:5.7environment:MYSQL_ROOT_PASSWORD: rootMYSQL_DATABASE: wordpressMYSQL_USER: mywordpressuserMYSQL_PASSWORD: mywordpresspassvolumes:- ./mysqllib/:/var/lib/mysqlhealthcheck:test: mysql -uroot -proot -e 'select 1;'interval: 10stimeout: 5sretries: 10

2,启动服务

docker compose up -d 

3,访问站点

如果没有下载Wordpress,会出现下载页面,之后重新登录即可。

4,发布一篇文章

七,docker镜像制作

通过快照方式制作镜像

通过 “快照方式” 制作 Docker 镜像,通常指的是基于运行中的容器状态创建镜像(即对容器当前的文件系统状态做 “快照” 并打包为镜像)。

docker commit 
功能:从容器创建一个新的镜像
语法:docker commit 选项 container image[:tag]
-a:提交镜像作者
-c:通过dockerfile指令来创建镜像,可以修改启动指令
-m:提交时的文字说明
-p:在commit时,将容器暂停掉

示例:

# 启动一个 Ubuntu 容器并进入交互模式
docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash
# 在容器内执行修改操作(安装 curl 并创建一个测试文件)
apt update && apt install -y curlecho "快照测试文件" > /root/test.txt
# 完成后退出容器
exit#使用docker commit 将上述容器的修改 “快照” 为新镜像:
docker commit -m "安装了 curl 并添加 test.txt" -a "yourname" my-ubuntu ubuntu-with-curl:v1#验证新镜像
# 查看镜像列表,确认新镜像存在
docker images | grep ubuntu-with-curl# 用新镜像启动容器,检查修改是否生效
docker run -it --rm ubuntu-with-curl:v1 /bin/bash# 在新容器内验证:curl 已安装,test.txt 存在
curl --version
cat /root/test.txt  # 应输出 "快照测试文件"

Dockerfile制作镜像

制作 Docker 镜像是将应用程序及其依赖打包成标准化容器的过程,核心是通过 Dockerfile 定义构建规则,最终生成可在任何支持 Docker 的环境中运行的镜像。

基础概念

  • Dockerfile:一个文本文件,包含构建镜像的指令集(如安装依赖、复制文件、配置环境等)。

  • 镜像(Image):只读模板,包含运行应用所需的代码、库、环境变量和配置文件。

  • 构建上下文:执行 docker build 时指定的目录,Docker 会将该目录下的所有文件发送给 Docker 引擎,供构建过程使用。

1.Dockerfile常用指令

FROM:指定基础镜像(如ubuntu-20.04),必须是 Dockerfile 第一条指令,推荐使用官方精简镜像(如 alpine 版本)

COPY/ADD:复制本地文件到镜像,COPY 仅复制文件,ADD 支持解压和 URL 下载。

RUN:执行命令(如安装依赖、修改配置),建议合并命令减少镜像层。

EXPOSE:声明端口(不实际映射,需在 docker run 时用 -p 映射)。

CMD/ENTRYPOINT:定义容器启动命令(CMD 可被 docker run 后的参数覆盖,ENTRYPOINT 不可)。

LABEL:为镜像添加元数据,格式为key=value。

ENV:为镜像添加环境变量,并可被Dockerfile文件中位于其后面的指令(如COPY,ADD,ENV)使用。

WORKDIR:为Dockerfile中所有的RUN,CMD,ENTRYPOINT等指令设定工作目录。

VOLUME:给镜像创建一个存储卷。如果在docker run的时候没有指定存储卷,就会使用默认的管理卷。

ONBUILD:用于在Dockerfile中定义一个触发器。它定义的操作不会在当前镜像构建时执行,而是在以当前镜像为基础镜像(即被其他 Dockerfile 的 FROM 指令引用)构建新镜像时,才会触发执行。

HEALTHCHECK:告诉docker如何检测容器是否在正常工作。

示例:在ubuntu22.04容器中,部署一个nginx服务

#在ubuntu22.04容器中,部署一个nginx服务
#Dockerfile文件内容如下
FROM ubuntu:22.04 as build1    #指定基础镜像
LABEL version="1.0" desc="create by xg"  #添加镜像版本和描述信息
ENV ROOTDIR=/data/web/html/
COPY --chown=news:news ./index.html ${ROOTDIR}
ONBUILD echo "onbuild trigger" > /data/tmp.txt
ENV Test=1
WORKDIR /data/src
ADD ./nginx-1.24.0.tar.gz .
RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev
RUN cd nginx-1.24.0 && ./configure  && make && make install
COPY ./nginx.conf /usr/local/nginx/conf/#nginx.conf配置文件内容如下
#nginx访问的首页内容原来在/html下,修改为/data/web/html目录#user  nobody;
worker_processes  1;#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {worker_connections  1024;
}http {include       mime.types;default_type  application/octet-stream;#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '#                  '$status $body_bytes_sent "$http_referer" '#                  '"$http_user_agent" "$http_x_forwarded_for"';#access_log  logs/access.log  main;sendfile        on;#tcp_nopush     on;#keepalive_timeout  0;keepalive_timeout  65;#gzip  on;server {listen       80;server_name  localhost;#charset koi8-r;#access_log  logs/host.access.log  main;location / {root   /data/web/html;index  index.html index.htm;}#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {#    proxy_pass   http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {#    root           html;#    fastcgi_pass   127.0.0.1:9000;#    fastcgi_index  index.php;#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;#    include        fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {#    deny  all;#}}# another virtual host using mix of IP-, name-, and port-based configuration##server {#    listen       8000;#    listen       somename:8080;#    server_name  somename  alias  another.alias;#    location / {#        root   html;#        index  index.html index.htm;#    }#}# HTTPS server##server {#    listen       443 ssl;#    server_name  localhost;#    ssl_certificate      cert.pem;#    ssl_certificate_key  cert.key;#    ssl_session_cache    shared:SSL:1m;#    ssl_session_timeout  5m;#    ssl_ciphers  HIGH:!aNULL:!MD5;#    ssl_prefer_server_ciphers  on;#    location / {#        root   html;#        index  index.html index.htm;#    }#}}
2.镜像创建命令

在项目目录执行 docker build 命令,格式:

docker build -t <镜像名称>:<标签> .
-t:指定镜像名称和标签(如 my-nginx:v1)。
.:表示构建上下文为当前目录(Docker 会读取该目录下的文件)。

其他参数:

--build-arg=[] :设置镜像创建时的变量;

-f :指定要使用的 Dockerfile 路径;

--label=[] :设置镜像使用的元数据;

--no-cache :创建镜像的过程不使用缓存;

--pull :尝试去更新镜像的新版本;

--quiet, -q :安静模式,成功后只输出镜像 ID;

--network: 默认 default。在构建期间设置 RUN 指令的网络模式

3.Dockerfile编写建议

1.善用.dockerignore 文件

使用它可以标记在执行 docker build 时忽略的路径和文件,避免发送不必要的数据内容,从而加快整个镜像创建过程。

2.镜像的多阶段构建

通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但这种方式需要维护多个 Dockerfile。

3.合理使用缓存

将频繁变化的指令(如 COPY 代码)放在 Dockerfile 末尾,利用 Docker 层缓存(未变化的指令可复用缓存)。

示例:

# 优化前(每次改代码都会重新安装依赖)
COPY . .
RUN npm install# 优化后(依赖不变时复用缓存)
RUN npm install  
COPY . .         # 代码变化不影响依赖安装层

4.基础镜像尽量使用官方镜像,并选择体积较小镜像

容器的核心是应用,大的平台微服务可能几十上百个。选择过大的父镜像(如 Ubuntu系统镜像)会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像(如node:slim),或者较为小巧的系统镜像(如 alpine、busybox 或 debian);

5.减少镜像层数

如果希望所生成镜像的层数尽量少,则要尽量合并 RUN、ADD 和 COPY 指令。通常情况下,多个 RUN 指令可以合并为一条 RUN 指令;如 apt get update&&apt install 尽量写到一行

6.精简镜像用途

尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能的镜像;

7.减少外部源的干扰

如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复用而不出错。

8.减少不必要的包安装

只安装需要的包,不要安装无用的包,减少镜像体积。

Dockerfile操作实战

1.制作一个C++镜像

demo.c

int main()
{   printf("hello world\n");return 0;
}

编写Dockerfile

FROM centos:7
#更换国内软件源(这里使用的是中科大软件源)
RUN sed -i.bak \-e 's|^mirrorlist=|#mirrorlist=|g' \-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \/etc/yum.repos.d/CentOS-Base.repo
#更新yum缓存
RUN yum makecache
#指定容器工作目录
WORKDIR /src
#将宿主机上的.c文件拷贝到容器中
COPY ./demo.c .
#容器内下载gcc
RUN yum install -y gcc
#编译完成后,删除 
RUN gcc demo.c -o demo && rm -f demo.c && yum remove -y gcc
#设置容器启动命令
CMD ["/src/demo"]

制作镜像,启动容器

 docker build -t xg:1.0 .docker run -it --rm xg:1.0
2.CMD与ENTYRPOINT实战

ENTRYPOINT:定义容器启动时的固定命令或程序(不可被 docker run 命令行参数直接覆盖,相当于 “主程序”)。

CMD:定义容器启动时的默认参数(可被 docker run 命令行参数覆盖,相当于给 ENTRYPOINT 传递的默认参数)。

结合使用时的行为(推荐方式)

ENTRYPOINT 定义固定命令,CMD 定义默认参数(可被 docker run 参数覆盖),这是最灵活的用法。

示例Dockerfile:

FROM alpine
ENTRYPOINT ["echo", "Hello"]  # 固定命令部分
CMD ["Docker"]                # 默认参数(可被覆盖)

启动容器(无参数):ENTRYPOINT + CMD 组合执行

docker run 镜像名  # 输出:Hello Docker(默认参数生效)

启动容器(带参数,覆盖 CMD):

docker run 镜像名 Nginx  # 输出:Hello Nginx(参数 "Nginx" 替代原 CMD)
3.多阶段构建镜像

将全部组件及其依赖库的编译、测试、打包等流程封装进一个 docker 镜像中。但是这种方式存在一些问题, 比如 Dockefile 特别长,可维护性降低;镜像的层次多,体积大,部署时间长等问题。

将每个阶段分散到多个 Dockerfile。一个 Dockerfile 负责将项目及其依赖库编译测试打包好后,然后将运行文件拷贝到运行环境中,这种方式需要我们编写多个Dockerfile 以及一些自动化脚本才能将其两个阶段自动整合起来。

为了解决以上的两个问题,Docker 17.05 版本开始支持多镜像阶段构建。只需要编写一个 Dockerfile 即可解决上述问题。

Dockerfile示例:

FROM centos:7 as buildstage1
RUN sed -i.bak \-e 's|^mirrorlist=|#mirrorlist=|g' \-e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
WORKDIR /src
COPY ./demo.c .
RUN yum install -y gcc
RUN gcc demo.c -o demo && rm -f demo.c && yum remove -y gcc
CMD ["/src/demo"]FROM centos:7
COPY --from=buildstage1 /src/demo /src
CMD ["/src/demo"]

Dockerfile与docker compose结合的操作实战

搭建mysql主从同步(一主两从)

1.准备工作

mkdir mysqlcluster
cd mysqlcluster
touch dokcer-compose.ymlmkdir master
cd master
touch Dockerfile
touch master.sqlcd ..
mkdir slave
cd slave
touch Dockerfile
touch slave.sql

目录结构如下图:

mysqlcluster/

├── docker-compose.yml

├── master

│   ├── Dockerfile

│   └── master.sql

└── slave

├── Dockerfile

└── slave.sql

2.主节点和从节点Dockerfile的编写

主节点Dockerfile

FROM mysql:5.7         #指明基础镜像
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  #设置时区
COPY ./master/master.sql /docker-entrypoint-initdb.d     
#MySQL镜像启动时会自动执行 /docker-entrypoint-initdb.d 目录下的.sql.sql.gz或.sh文件           

从节点Dockerfile

FROM mysql:5.7
RUN ln /usr/share/zoneinfo/Asia/Shanghai /etc/loacltime
COPY ./slave/slave.sql /docker-entrypoint-initdb.d                                         

3.主节点和从节点数据库配置信息

主节点:master.sql文件

//创建一个用户名是 root、允许从任何 IP 地址(% 通配符)连接的用户,并设置该用户的密码为root
CREATE USER 'root'@'%' IDENTIFIED By 'root';
//授予root用户在所有数据库(*.*)上的两个权限:
//replication slave:允许作为从库连接主库进行数据复制
//replication client:允许查询主从复制的状态信息(show master status或show slave staus命令)
grant replication slave,replication client on *.* to 'root'@'%';
//刷新权限使其生效
flush privileges;

从节点:master.sql文件

//在从库中记录主库的连接信息,为后续的数据同步做准备
change master to master_host='mysql-master',master_user='root',master_password='root',master_port='3306';
//启动从库复制进程
start slave;

4.docker-compose.yml的编写

services:mysql-master:build:context: ./dockerfile: ./master/Dockerfileimage: mysqlmaster:v1.0restart: alwayscontainer_name: mysql-mastervolumes:- ./mastervarlib:/var/lib/mysqlports:- 9306:3306environment:MYSQL_ROOT_PASSWORD: rootprivileged: truecommand: ['--server-id=1','--log-bin=master-bin','--binlog-ignore-db=mysql','--binlog_cache_size=256M','--binlog_format=mixed','--lower_case_table_names=1','--character-set-server=utf8','--collation-server=utf8_general_ci']mysql-slave:build:context: ./dockerfile: ./slave/Dockerfileimage: mysqlslave:v1.0restart: alwayscontainer_name: mysql-slavevolumes:- ./slavevarlib:/var/lib/mysqlports:- 9307:3306environment:MYSQL_ROOT_PASSWORD: rootprivileged: truecommand: ['--server-id=2','--relay_log=slave-relay','--lower_case_table_names=1','--character-set-server=utf8','--collation-server=utf8_general_ci']depends_on:- mysql-mastermysql-slave2:build:context: ./dockerfile: ./slave/Dockerfileimage: mysqlslave:v1.0restart: alwayscontainer_name: mysql-slave2volumes:- ./slavevarlib2:/var/lib/mysqlports:- 9308:3306environment:MYSQL_ROOT_PASSWORD: rootprivileged: truecommand: ['--server-id=3','--relay_log=slave-relay','--lower_case_table_names=1','--character-set-server=utf8','--collation-server=utf8_general_ci']depends_on:- mysql-master

5.构建镜像

docker compose build

6.启动服务

docker compose up -d

#查看启动的服务
docker compose ps
#确保服务的状态都是up

7.检查同步状态

进入到一个容器后,连接上数据库,输入show master statusshow slave staus命令.

示例:

8.检查数据是否可以同步

在主数据库mysql-master中新建表,插入数据,然后在从数据库mysql-slave和mysql-slave1中查看数据是否存在。

Docker镜像制作常见问题

1.ADD 与 COPY 的区别

ADD:不仅能够将构建命令所在的主机本地的文件或目录,而且能够将远程 URL所对应的文件或目录,作为资源复制到镜像文件系统。所以,可以认为 ADD 是增强版的 COPY,支持将远程 URL 的资源加入到镜像的文件系统。

COPY:COPY 指令能够将构建命令所在的主机本地的文件或目录,复制到镜像文件系统。

2.CMD 与 EntryPoint 的区别

ENTRYPOINT 容器启动后执行的命令,让容器执行表现的像一个可执行程序一样,与 CMD 的 区 别 是 不 可 以 被 docker run 覆 盖 , 会 把 docker run 后 面 的参 数 当 作 传 递 给 ENTRYPOINT 指令的参数。

Dockerfile 中只能指定一个 ENTRYPOINT,如果指定了很多,只 有 最 后 一 个 有效 。 docker run 命 令 的

-entrypoint 参 数 可 以 把 指 定 的 参 数 继 续 传 递 给ENTRYPOINT。

组合使用 ENTRYPOINT 和 CMD, ENTRYPOINT 指定默认的运行命令, CMD指定默认的运行参数。

3.什么是空悬镜像(dangling )

REPOSITORY 和 TAG 标签 均为<none> 的镜像被称为虚悬镜像,一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的。

造成虚悬镜像的原因:

原因一:

原本有镜像名和标签的镜像,发布了新版本后,重新 docker pull *** 时,旧的镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消;

原因二:

docker build 同样可以导致这种现象。比如用 dockerfile1 构建了个镜像 tnone1:v1,又用另外一个 Dockerfile2 构建了一个镜像 tnone1:v1,这样之前的那个镜像就会变成空悬镜像。

可以用下面的命令专门显示这类镜像

docker image ls -f dangling=true

4.中间层镜像是什么?

当使用 docker build 构建镜像时,Docker 会对 Dockerfile 中的每一条指令(如 RUN、COPY、ADD 等)生成一个对应的镜像层,这些层就是中间层镜像。

中间层镜像通常没有 REPOSITORY 和 TAG 标签(显示为 <none>:<none>)。

缓存作用:

中间层镜像是 Docker 构建缓存的核心:

  • 若 Dockerfile 指令未修改,再次构建时,Docker 会直接复用已有的中间层镜像,跳过指令执行步骤,大幅提升构建速度;

  • 若某条指令被修改,该指令及后续所有指令的中间层都会重新生成,之前的缓存失效。

八,docker镜像原理

Docker 镜像是容器技术的核心基础,它采用了分层存储和联合文件系统(UnionFS) 等技术.

其核心原理可以概括为:一个镜像由多个只读层(Layer)组成,每层包含文件系统的增量变化,通过联合文件系统合并为一个统一的只读文件系统,作为容器运行的基础。

Docker 镜像的每一层都是只读的,容器运行时会添加一个可写层。

Docker 镜像并非一个单一的文件,而是由一系列只读层(Layer) 叠加而成的集合。每个层对应镜像构建过程中的一条指令(如 FROM、RUN、COPY 等),记录该步骤对文件系统的修改(新增、修改或删除的文件)。

例如,一个简单的镜像构建过程:

FROM ubuntu:20.04       # 基础层(来自 ubuntu:20.04 镜像的所有层)
RUN apt-get update      # 新增层 1:记录 apt-get update 产生的文件变化
RUN apt-get install -y nginx  # 新增层 2:记录安装 Nginx 产生的文件变化
COPY index.html /var/www/html  # 新增层 3:记录复制 index.html 产生的变化

构建后,这个镜像包含:

  • 基础层(ubuntu:20.04 的所有层)

  • 层 1(RUN apt-get update 的变化)

  • 层 2(RUN apt-get install nginx 的变化)

  • 层 3(COPY index.html 的变化)

分层存储

  • 增量存储:每层仅记录与上一层的差异(新增、修改的文件),而非完整复制整个文件系统。例如,基础层已经包含 Ubuntu 系统文件,安装 Nginx 时,仅记录新增的 Nginx 二进制文件、配置文件等,大大减少镜像体积。

  • 层共享:不同镜像可以共享相同的基础层。例如,若两个镜像都基于 ubuntu:20.04 构建,它们会共享 ubuntu 的基础层,无需重复存储,节省磁盘空间。

UnionFS联合文件系统

UnionFS(Union File System,联合文件系统)是一种分层、轻量级的文件系统,它的核心功能是将多个独立的目录(文件系统)“合并” 成一个统一的虚拟文件系统,对外呈现为单一的目录结构。用户或应用程序操作这个虚拟目录时,系统会自动协调底层多个目录的读写逻辑。

  1. 分层合并 UnionFS 可以将多个目录(称为 “分支”,branch)按顺序堆叠,形成一个虚拟的统一视图。例如:

    1. 底层目录(只读层):存放基础文件(如操作系统镜像、应用程序)

    2. 上层目录(可写层):用于用户的修改操作

    3. 合并后,用户看到的是所有层的文件总和,上层文件会 “覆盖” 下层同名文件

  2. 写时复制(CoW) 当用户需要修改底层只读层的文件时,UnionFS 不会直接修改原文件,而是先将该文件复制到上层可写层,再在可写层中进行修改。这样既保护了底层数据的完整性,又实现了高效的空间利用(只有被修改的文件才会占用上层空间)。

  3. 删除操作处理 删除文件时,UnionFS 不会真正删除底层文件,而是在上层创建一个 “删除标记”(whiteout),使得该文件在虚拟视图中不可见,但底层文件依然存在。

写时复制的完整流程示例:

假设场景:UnionFS 堆叠了两层目录,lowerdir(只读层,存放原始文件)和 upperdir(可写层,初始为空),合并后呈现为统一视图 merged。当用户尝试修改 lowerdir 中的文件时,CoW 流程如下:

初始状态:文件仅存在于只读层

  • lowerdir(只读)中有一个文件 data.txt,内容为 hello;

  • upperdir(可写)中无此文件;

  • 用户在 merged 目录中看到的 data.txt 实际指向 lowerdir/data.txt。

  1. 触发写操作:用户尝试修改只读文件

当用户执行修改操作(如 echo "world" >> merged/data.txt)时:

  • 系统首先检测到目标文件 data.txt 来自只读层(lowerdir);

  • 由于只读层不可直接修改,系统不会立即执行写入,而是启动 CoW 机制。

  1. 复制文件到可写层

系统执行 “复制” 操作:

  • 将 lowerdir/data.txt 完整复制到 upperdir 中(生成 upperdir/data.txt);

  • 此时 upperdir/data.txt 的内容仍为原始的 hello(仅复制,未修改)。

  1. 在可写层执行修改

复制完成后,系统在 upperdir 中对复制后的文件执行用户的修改操作:

  • 向 upperdir/data.txt 追加 world,使其内容变为 hello world;

  • 整个修改过程仅发生在可写层,只读层的 lowerdir/data.txt 保持不变(仍为 hello)。

  1. 更新视图:上层文件覆盖下层

修改完成后,UnionFS 的虚拟视图 merged 会优先显示可写层的文件:

  • 用户在 merged 中查看 data.txt 时,系统返回 upperdir/data.txt 的内容(hello world);

  • 只读层的原始文件被 “覆盖”(逻辑上不可见,但物理上仍存在)。

分层存储的实现

overlay2 是 Docker 目前默认的存储驱动(storage driver)。

  • 将镜像的所有只读层按顺序组合为 lowerdir(例如:lowerdir=layer1:layer2:layer3,layer1 是最底层,layer3 是最上层镜像层);

  • 为容器创建独立的 upperdir(可写层)和 workdir(临时目录);

  • 通过联合挂载将 lowerdir、upperdir 合并为 merged 目录,作为容器的根目录(/)

镜像层(只读):layer1 → layer2 → layer3(lowerdir 组合)↑       ↑       ↑└───────┼───────┘↓
容器可写层:           upperdir↑└─── 合并为 merged(容器内 /)

读文件操作

当容器读取一个文件(如 /etc/hosts)时:

  • overlay2 从 merged 目录接收请求,按顺序检查 upperdir → lowerdir(从顶层镜像层到底层);

  • 若 upperdir 中存在该文件,直接返回 upperdir 的文件;

  • 若 upperdir 中不存在,依次检查 lowerdir 的各镜像层,返回第一个找到的文件;

  • 若所有层都不存在,返回 “文件不存在” 错误。

写文件操作(触发写时复制 CoW

当容器修改一个来自镜像层(lowerdir)的文件时,overlay2 会触发写时复制:

  1. 复制文件到可写层: 将 lowerdir 中该文件的完整副本复制到 upperdir(例如:将 layer2 的 /etc/hosts 复制到 upperdir/etc/hosts)。

  2. 在可写层执行修改: 容器的修改操作仅作用于 upperdir 中的副本(例如:修改 upperdir/etc/hosts 的内容)。

  3. 更新视图: 此后容器读取该文件时,merged 目录会优先返回 upperdir 中修改后的版本,原镜像层的文件保持不变。

删除文件操作

删除文件时,overlay2 不会真正删除镜像层的文件,而是通过 “标记” 实现逻辑删除:

  • 若删除的是 upperdir 中的文件(容器自己创建的):直接删除 upperdir 中的文件。

  • 若删除的是镜像层(lowerdir)中的文件:在 upperdir 中创建一个特殊的 “删除标记”(whiteout 文件),标记该文件已被删除。 此后 merged 目录会识别该标记,隐藏底层文件,使容器感知到 “文件已删除”,但原镜像层的文件仍完整存在。

overlay文件系统工作实战

1.创建一个目录fs,在该目录中创建文件系统的工作目录

mkdir fs
mkdir upper lower work merged
fs
├── lower
├── merged
├── upper
└── work

2.准备一些文件

echo "in lower" > lower/in_lower.txt
echo "in upper" > upper/in_upper.txt
echo "In both. from lower" > lower/in_both.txt
echo "In both. from upper" > upper/in_both.txt
fs
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
├── upper
│   ├── in_both.txt
│   └── in_upper.txt
└── work此时merged目录下是空的

3.创建文件系统并完成挂载

#命令
mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged
#查看是否完成了挂载
df -h
#取消挂载
umount 挂在点或设备文件

4.再次查看目录结构

#命令
tree
fs
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
├── upper
│   ├── in_both.txt
│   └── in_upper.txt
└── work└── work

发现merged命令下自动生成3个文件,可以看到upper,lower中的文件都在,merged 目录其实就是用户看到的目录,用户的实际文件操作在这里进行。

#同时查看merged/in_both.txt的内容
cat merged/in_both.txt 
结果是:In both. from upper
可以证明这个文件是upper中的,upper目录中的in_both.txt覆盖了lower目录中的in_both.txt.

5.修改lower中的文件

echo "in lower! after edit!" > merged/in_lower.txt
#再次查看目录结构
tree
fs
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
├── upper
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
└── work└── work

可以看到 in_lower.txt 在 upper 里面生成了,发生了Cow(写时复制),底层lower目录中的文件是不变的。

九,docker存储卷原理

原理:Docker 卷本质是宿主机上的一个目录(或文件),通过--bind类似的机制挂载到容器中。

容器读写卷时,数据实际保存在宿主机的卷目录中,与容器生命周期分离。

mount --bind 是 Linux 系统中一种特殊的挂载方式,用于将一个目录或文件 "绑定" 到另一个目录位置,实现两个路径指向同一个数据的效果。这种方式类似创建了一个 "硬链接",但适用于目录,且更灵活。

#基本语法
mount --bind 源路径 目标路径
  • 绑定后,对目标路径的任何操作(增删改文件)都会直接影响源路径的内容(两者本质上是同一个数据的不同访问入口)

  • 源路径和目标路径可以位于不同的文件系统

  • 绑定挂载不会复制数据,只是建立访问路径的映射,非常高效

当容器进程被创建之后,尽管开启了 Mount Namespace,但是在它执行 chroot(chroot 就是可以改变某进程的根目录,使这个程序不能访问目录之外的其他目录,这个跟我们在一个容器中是很相似的)之前,容器进程一直可以看到宿主机上的整个文件系统。

所以,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。

宿主机与容器的目录绑定(Bind Mount),本质是 容器运行时的动态路径映射:容器访问 容器目录 时,Linux 内核通过 VFS 层直接将操作转发到宿主机的 宿主机目录,但这个映射关系仅存在于容器运行期间,不会对容器自身的文件系统(可写层)产生任何修改 —— 容器的 容器目录 本身只是一个 “逻辑挂载点”,没有实际数据(数据全在宿主机目录中)。


Docker 存储卷(Volume)是独立于容器可写层的外部存储,其数据存储在宿主机的特定目录(通常是 /var/lib/docker/volumes/),而非容器自身的文件系统层中。 容器与卷的关系是 “挂载” 关系,卷中的数据本质上不属于容器文件系统的一部分。 docker commit 的作用是将容器的可写层(即容器运行过程中对镜像只读层的修改) 打包为一个新的镜像层。 由于卷的数据不在容器的可写层中,因此 commit 操作无法捕获卷中的内容。

十,docker网络原理

Linux常见网络虚拟化

虚拟网卡:tun/tap

在计算机网络中,tun 与 tap 是操作系统内核中的虚拟网络设备。不同于普通靠硬件网络适配器实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能

tap/tun 虚拟了一套网络接口,这套接口和物理的接口无任何区别,可以配置 IP,可以路由流量,不同的是,它的流量只在主机内流通。

tun 和 tap 是两个相对独立的虚拟网络设备,其中 tap 模拟了以太网设备,操作二层数据包(以太帧),tun 则模拟了网络层设备,操作三层数据包(IP 报文)。

应用程序通过 tun 设备对外发送数据包后,tun 设备,便会把数据包通过字符设备发送给 VPN 程序,VPN 收到数据包,会修改后再重新封装成新报文,譬如数据包原本是发送给 A 地址的,VPN 把整个包进行加密,然后作为报文体,封装到另一个发送给 B 地址的新数据包当中。然后通过协议栈发送到物理网卡发送出去。

基础命令:

1.添加网卡

# 创建 tap
ip tuntap add dev <name> mode tap
# 创建 tun
ip tuntap add dev <name> mode tun

2.删除网卡

# 删除 tap
ip tuntap del dev <name> mode tap
# 删除 tun
ip tuntap del dev <name> mode tun

3.激活网卡

ip link set name up

4.设置 ip并查看虚拟网卡

ip addr add 10.5.0.1/24 dev <name>
ifconfig
虚拟网卡:veth

Linux 支持网络名空间隔离的同时,也提供了专门的虚拟以太网(Virtual Ethernet,习惯简写做 veth)让两个隔离的网络名称空间之间可以互相通信。

直接把 veth 比喻成是虚拟网卡其实并不十分准确,如果要和物理设备类比,它应该相当于由交叉网线连接的一对物理网卡。形象化的理解如下:

veth 实际上不是一个设备,而是一对设备,因而也常被称作 veth pair。要使用 veth,必须在两个独立的网络名称空间中进行才有意义,因为 veth pair 是一端连着协议栈,另一端彼此相连的,在 veth 设备的其中一端输入数据,这些数据就会从设备的另外一端原样不变地流出.

veth 通信不需要反复多次经过网络协议栈,这让 veth 比起 tap/tun 具有更好的性能。

veth 实现了点对点的虚拟连接,可以通过 veth 连接两个 namespace,如果我们需要将 3 个或者多个 namespace 接入同一个二层网络时,就不能只使用 veth 了。在物理网络中,如果需要连接多个主机,我们会使用网桥,或者又称为交换机。Linux 也提供了网桥的虚拟实现。

基础命令:

1.veth 操作

# 添加 veth
ip link add <veth name> type veth peer name <peer name>
#示例:
ip link add veth11 type veth peer name veth12# 删除 veth
ip link delete  <veth name>
# 查看 veth
ip link show

2.命名空间操作

#添加 ns
ip netns add <name>
#删除 ns
ip netns del  <name>
#执行命令
ip netns exec  <name> <cmd>
#遍历 ns
ip netns list#示例:
ip netns add ns1 
ip netns add ns2

3.将网卡挪到不同的命名空间中

ip link set veth11 netns ns1
ip link set veth12 netns ns2

4.激活网卡

ip netns exec ns1 ip link set veth11 up
ip netns exec ns2 ip link set veth12 up

5.给网卡分配 IP 地址

ip netns exec ns1 ip addr add 10.5.0.1/24 dev veth11
ip netns exec ns2 ip addr add 10.5.0.2/24 dev veth12

6.然后我们在各自的命名空间中 ping 对方,可以看到网卡是能通的

ip netns exec ns1 ping 10.5.0.2
ip netns exec ns2 ping 10.5.0.1
虚拟交换机(网桥)

Linux Bridge(网桥)是用纯软件实现的虚拟交换机,有着和物理交换机相同的功能.

因此我们可以把 tun/tap,veth pair 等设备绑定到网桥上,就像是把设备连接到物理交换机上一样。此外它和 veth pair、tun/tap 一样,也是一种虚拟网络设备,具有虚拟设备的所有特性,例如配置 IP,MAC 地址等。

Linux Bridge ,由 brctl 命令创建和管理。Linux Bridge 创建以后,真实的物理设备(如 eth0)抑或是虚拟的设备(veth 或者 tap)都能与 Linux Bridge 配合工作。

#brctl工具的安装:
# centos
yum install -y bridge-utils
# ubuntu
apt-get install -y bridge-utils

基本命令:

1.新建一个网桥

brctl addbr <bridge>

2.添加一个设备(例如 eth0)到网桥

brctl addif <bridge> eth0

3.显示当前存在的网桥及其所连接的网络端口

brctl show

4.启动网桥

ip link set <bridge> up

5.删除网桥,需要先关闭它

ip link set <bridge> down
brctl delbr <bridge>
#或者使用 ip link del 命令直接删除网桥

增加 Linux Bridge 时会自动增加一个同名虚拟网卡在宿主机器上,因此我们可以通过 ip link 命令操作这个虚拟网卡,实际上也就是操作网桥,并且只有当这个虚拟网卡状态处于 up 的时候,网桥才会转发数据。

Docker bridge网桥

默认情况下,Docker 会创建一个名为 docker0 的网桥(也可自定义网桥),所有使用 bridge 模式的容器都会连接到该网桥,实现:

  • 容器之间通过网桥进行二层通信(类似同一交换机下的设备);

  • 容器通过网桥与宿主机通信;

  • 容器通过宿主机的网络接口(如 eth0)访问外部网络(依赖 SNAT 转换)。

容器连接到网桥的过程

当启动一个 bridge 模式的容器时(默认模式):

  • Docker 为容器创建一对虚拟网络接口(veth pair):一端在容器内(命名为 eth0),另一端在宿主机上(命名类似 vethxxxx);

  • 宿主机上的 vethxxxx 接口被 “插入” 到 docker0 网桥中(类似将网线插入交换机);

  • 容器内的 eth0 被分配 docker0 网段的 IP 地址,网关指向 docker0 的 IP;

  • 此时,容器通过 veth pair → docker0 网桥与其他容器或宿主机通信。

桥接模式可以参考下图:

DNAT 是修改数据包的目标 IP 地址(或目标端口),通常用于外部网络访问内部网络时,将公网 IP(或端口)映射到内网私有 IP(或端口),使内网服务能被外部访问。

SNAT 是修改数据包的源 IP 地址,通常用于内部网络(如局域网)的主机访问外部网络(如互联网)时,将内部私有 IP 转换为公共 IP,实现多台内网设备共享一个公网 IP 上网。

http://www.xdnf.cn/news/18751.html

相关文章:

  • 嵌入式系统学习Day23(进程)
  • 今日分享:C++ string 类模拟实现
  • 【Linux系统】线程概念
  • 【51单片机】萌新持续学习中《矩阵 密码锁 点阵屏》
  • 抽象能力的重要性
  • 使用 flutter_tts 的配置项
  • MyBatis 初识:框架定位与核心原理——SQL 自由掌控的艺术
  • 移动应用渗透测试:API 接口漏洞的识别与利用技巧
  • 五自由度磁悬浮轴承同频振动抑制:从机理拆解到传递函数验证的核心方案
  • ICBC_TDR_UShield2_Install.exe [ICBC UKEY]
  • CSDN博客:中文技术社区的知识生产与生态演进
  • 项目设计文档——爬虫项目(爬取天气预报)
  • linux、window java程序导出pdf\word、excel文字字体显示异常、字体样式不一样
  • SOME/IP服务发现PRS_SOMEIPSD_00277的解析
  • 【贪心算法】day3
  • 高教杯数学建模2021-C 生产企业原材料的订购与运输
  • 5G 三卡图传终端:应急救援管理的 “可视化指挥核心”
  • 【无标题】计数组合学7.21(有界部分大小的平面分拆)
  • 支持向量机(SVM)
  • Linux 内核 Workqueue 原理与实现及其在 KFD SVM功能的应用
  • Linux--seLinux的概述
  • 数据结构07(Java)-- (堆,大根堆,堆排序)
  • 常见的设计模式
  • 博士招生 | 南洋理工大学 PINE Lab 招收全奖博士
  • [新启航]新启航激光频率梳 “光量子透视”:2μm 精度破除遮挡,完成 130mm 深孔 3D 建模
  • 【国密证书】CentOS 7 安装 GmSSL 并生成国密证书
  • Docker移动安装目录的两种实现方案
  • 微硕WINSOK高性能MOS管WSF90N10,助力洗衣机能效与可靠性升级
  • Java:IO流——基础篇
  • Redis高级篇:在Nginx、Redis、Tomcat(JVM)各环节添加缓存以实现多级缓存