Docker安装部署
Docker安装

安装前需要清理之前的安装环境,基于ubuntu24.04的命令如下,详细可参考docker官方文档Install Docker Engine on Ubuntu | Docker Docs

sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

我的是全新安装系统所以执行命令之后没有任何包被卸载清除。

接下来开始执行安装命令,执行如下命令

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

安装完毕后执行一下docker --version可以看到现在安装的版本是哪个

基础理论知识点

文件系统
  • AUFS(Advanced Multilayered Unification Filesystem,版本 2 之前旧称 Another Union FS)是一种 Union FS,属于文件级存储驱动。Aufs 是 UnionFS 的重新实现,由 Junjiro Okajima 于 2006 年开发。UnionFS 的核心思想是将不同物理位置的目录合并挂载(mount)到同一目录下,简单来说,它支持将多个目录挂载到一个虚拟文件系统中,并允许层层叠加修改文件。底层所有层均为只读,仅最上层可写。当需要修改文件时,AUFS 会创建该文件的副本,通过写时复制(Copy-on-Write, CoW)机制将文件从只读层复制到可写层进行修改,结果保存在 Docker 中。其中,底层的只读层对应 image,可写层则对应 Container。
  • aufs 曾被拒绝合并到 Linux 内核主线,其代码被批评为“dense, unreadable, uncommented”(密集、不可读、未注释)。相反,OverlayFS 被成功合并到 Linux 内核。在多次尝试将 aufs 合并到主线内核失败后,作者最终放弃了这一努力。
  • AUFS 曾是 Docker 18.06 及更早版本的首选存储驱动程序,但在内核 3.13 上运行 Ubuntu 14.04 时不支持 overlay2。
  • Overlay:一种 Union FS 文件系统,自 Linux 内核 3.18 起获得支持。
  • Overlay2:Linux 内核中的一种联合文件系统(UnionFS),是 OverlayFS 的演进版本,专为容器场景设计。目前,所有 Linux 发行版推荐使用的存储类型,也是 Docker 默认的存储引擎即为 overlay2。它要求磁盘分区支持 d_type 功能,因此需要系统磁盘的额外支持。相较于 AUFS,Overlay2 具有以下优势:设计更简洁;自内核 3.18 起已进入 Linux 内核主线;资源消耗更少。Overlay2 依赖并建立在其他文件系统之上,不参与磁盘空间结构的划分,仅将底层文件系统中的不同目录“合并”后呈现给用户。用户看到的 overlay 文件系统根目录内容,实际上是挂载 overlay 文件系统时指定的不同目录的合集。Overlay2 支持多个 lower 层,但只有一个 upper 层。上层会覆盖下层相同的内容,用户最终看到的是 Merged 层。用户只能操作 Merged 层,所有操作变化都会记录在 upper 层中,而 lower 层的内容始终保持只读。
  • devicemapper:由于 CentOS 7.2 和 RHEL 7.2 的早期版本内核不支持 overlay2,因此默认使用此存储驱动程序。但其最大数据容量仅支持 100GB,且性能不佳。当前较新版本的 CentOS 已支持 overlay2,故推荐使用 overlay2。此外,该存储引擎已在 Docker Engine 18.09 中弃用。
  • ZFS (Sun - 2005) / btrfs (Oracle - 2007):目前尚未被广泛使用。
  • vfs:适用于测试环境,当无法使用写时复制(copy-on-write)机制时可考虑此驱动。但其性能较差,通常不建议用于生产环境。

查看docker使用的默认存储引擎

说明:

对比项 overlay2(Docker 原生) overlayfs(containerd 管理)
管理层 dockerd 直接管理 交给 containerd 的 snapshotter
数据目录 /var/lib/docker/overlay2/ /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/
触发方式 默认行为 需开启 containerd-snapshotter 特性

这是 Docker 较新版本从docker29.0引入的新架构,将存储管理交给 containerd 统一处理,是未来发展方向。底层都是 OverlayFS 联合文件系统,支持写时复制、分层存储等特性,性能无差异。overlay2 由 dockerd 直接管理,overlayfs 由 containerd 管理。

配置镜像加速

vim /etc/docker/daemon.json

{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://docker.1panel.live",
    "https://docker.1ms.run",
    "https://hub.rat.dev",
    "https://docker.fnnas.com",
    "https://docker.unsee.tech",
    "https://docker.hlmirror.com"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

保存退出后需要重启docker生效
systemctl restart docker
容器镜像管理
镜像结构和原理

镜像即创建容器的模版,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快 速的创建并启动容器 镜像含里面是一层层的文件系统, 叫做 Union FS (联合文件系统) , 联合文件系统,可以将几层目录挂载 到一起(就像千层饼,洋葱头,俄罗斯套娃一样),形成一个虚拟文件系统, 虚拟文件系统的目录结构就像普通 linux 的目录结构一样,镜像通过这些文件再加上宿主机的内核共同提供了一个 linux 的虚拟环 境,每一层文件系统叫做一层 layer ,联合文件系统可以对每一层文件系统设置三种权限,只读 (readonly )、读写( readwrite )和写出( whiteout-able ),但是镜像中每一层文件系统都是只读的 , 构建镜像的时候,从一个最基本的操作系统开始,每个构建提交的操作都相当于做一层的修改,增加了 一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层 把底层遮住了一样,当使用镜像的时候,我们只会看到一个完全的整体,不知道里面有几层, 实际上也不 需要知道里面有几层,结构如下:
一个典型的 Linux 文件系统由 bootfs rootfs 两部分组成 bootfs(boot file system) 主要包含 bootloader kernel bootloader 主要用于引导加载 kernel Linux 刚启动时会加载bootfs 文件系统 , boot 加载完成后 ,kernel 被加载到内存中后接管系统的控制权 ,bootfs 会被 umount 掉 rootfs (root file system) 包含的就是典型 Linux 系统中的 /dev /proc /bin /etc 等标准目录和文件, 不同的 linux 发行版(如 ubuntu CentOS ) 主要在 rootfs 这一层会有所区别。 一般的镜像通常都比较小,官方提供的Ubuntu 镜像只有 60MB 多点,而 CentOS 基础镜像也只有 200MB 左右,一些其他版本的镜像甚至只有几MB ,比如 : busybox 1.22MB alpine 镜像也只有 5M 左右。镜 像直接调用宿主机的内核,镜像中只提供 rootfs ,也就是只需要包括最基本的命令 , 配置文件和程序库等
相关文件就可以了。 下图就是有两个不同的镜像在一个宿主机内核上实现不同的rootfs
查看镜像的分层结构,我们先去准备一下镜像
镜像拉取
拉取了最新版的nginx镜像,使用docker image ls查看镜像版本信息,查看镜像分层信息docker image history nginx:latest
docker inspect查看镜像、容器具体信息
搜索镜像去hub.docker.com进行查找,以官方镜像为主。
下载一个alpine镜像
镜像本地导出与导入
docker pull alpine:3.24.1镜像3.93m,现在将拉取好的镜像在本地导出为tar,docker save -o [导出名称] 已pull好的镜像,镜像导出支持多个镜像导出
镜像导入,镜像导入只能导入单个镜像,现在删除从官方拉取的alpine镜像
docker image rm == docker rmi 都是镜像删除操作,接下来导入alpine_3.24.1.tar中的镜像
常见用法docker load -i 压缩包名 docker load < 包名
清理dngling状态镜像

当像像信息中RESPOSITORY TAG字段为<none>时认定此镜你为dangling镜像,清除此类镜像和不使用的镜像使用docker image prune -a -f 慎用。

 现在没有使用的镜像,使用docker image prune -a -f 试一下效果

已经全部清除。

镜像打标签
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
#TARGET_IMAGE[:TAG] 格式一般形式
仓库主机 FQDN IP[: 端口 ]/ 项目名 ( 或用户名 )/image 名字 : 版本
TAG默认为latest,拉取镜像最全理的方式要加上明确的版本号。
删除镜像

docker rmi 镜像名称/ID

容器操作
容器启动

docker run [选项] [镜像名] [shell命令] [参数]

docker run --name ubuntu_test -d -it --rm ubuntu:latest /bin/bash
启动一个名称为 ubuntu_test的容器,并从控制台终端连入,并在后台运行。
--name  启动的的容器名称
-d      后台运行
-it     生成一个终端连入
--rm    自动删除匿名卷,测试使用

--always 确保实现宿主机开机时自动启动容器

#说明 当 -d与-it同时使用时 docker强制忽略-i -t,上面的命令执行结果是启动了一个ubuntu_test的容器,但是没有连到终端,需要再次执行docker exec -it ubuntu_test /bin/bash才可以进入终端。

容器运行时容器名称要唯一

docker rm 容器名 清理已经关闭的容器 docker rm -f 清理运行中和已经关闭的容器慎用
dcoker ps 查看所有运行的容器
docker ps -a 显示全部容器包括退出状态容器
docker ps -a -s 显示容器大小
docker ps -f 'status=exit'查看退出状态的容器
docker stats 容器名 查看容器资源使用情况
docker stop  优雅关闭容器等待
docker kill  强制退出
docker logs  查看容器日志
快捷键cril+p+q退出容器不会停止容器


docker run -e  MYSQL_ROOT_PASSWORD=123456 -e 
MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name 
mysql -d --restart=always registry.cnbeijing.aliyuncs.com/wangxiaochun/mysql:8.0.29-oracle 
-e 后面接传递的环境变量 也可以以--env-flie将环境变量定义到文件中引用文件。


docker run -P 可以将事先容器预定义的所有端口映射宿主机的网卡的随机端口,默认从32768开始
使用随机端口 时,当停止容器后再启动可能会导致端口发生变化
docker run -p 3306:3306 将容器的3306端口映射为宿主机的3306  前一个是宿主机的端口后一个是容器的端口


docker run --dns --add-host 
--dns 指定容器使用的DNS 
--add-host www.test.com:x.x.x.x 增加hosts绑定条目

创建后【无法直接修改】的参数(必须重建容器)

参数 说明
--name 容器名(但可以用 docker rename 改名,见下文)
-p / --publish 端口映射 
-v / --volume 数据卷挂载 
--mount 挂载(同 -v)
--network 加入哪个网络
--dns DNS 服务器 
--dns-search DNS 搜索域
--dns-opt DNS 选项
--hostname 主机名
--ip 指定容器 IP
--mac-address MAC 地址
--privileged 特权模式
--cap-add / --cap-drop Linux 能力
--runtime 运行时
--entrypoint 入口点
镜像本身 容器基于哪个镜像
启动命令 CMD 容器的主进程命令 

创建后【可以修改】的少数参数

操作 命令 说明
改容器名 docker rename 旧名 新名 运行中也能改
改重启策略 docker update --restart=always 容器名 运行中也能改
改资源限制 docker update --memory=512m --cpus=1.5 容器名 CPU/内存/blkio 等,运行中也能改
改暂停/恢复 docker pause / unpause 冻结进程

docker ps 查看运行中的容器

查看容器信息

docker inspect 容器名

{{.ID}}:容器的ID。
{{.Image}}:容器使用的映像名称。
{{.Command}}:容器的启动命令。
{{.CreatedAt}}:容器的创建时间。
{{.RunningFor}}:容器运行的时间。
{{.Ports}}:容器的端口映射信息。
{{.Status}}:容器的状态。
{{.Size}}:容器的大小。
{{.Names}}:容器的名称。
{{.Label}}:容器的标签。
容器内与宿主机之间数据拷贝
# 宿主机 → 容器
docker cp [选项] 宿主机路径 容器名:容器内路径

# 容器 → 宿主机
docker cp [选项] 容器名:容器内路径 宿主机路径

# 把宿主机的配置文件复制到容器内
docker cp /home/user/app.conf my-container:/etc/nginx/conf.d/app.conf
1、如果目标路径 /etc/nginx/conf.d/ 不存在,会创建
2、如果目标路径已存在同名文件,直接覆盖(无确认提示)
3、如果目标是一个目录(如 /etc/nginx/conf.d/),文件会被复制到该目录下,保留原文件名

场景 1:宿主机单个文件 → 容器
# 把宿主机的配置文件复制到容器内
docker cp /home/user/app.conf my-container:/etc/nginx/conf.d/app.conf
如果目标路径 /etc/nginx/conf.d/ 不存在,会报错
如果目标路径已存在同名文件,直接覆盖(无确认提示)
如果目标是一个目录(如 /etc/nginx/conf.d/),文件会被复制到该目录下,保留原文件名

场景 2:容器单个文件 → 宿主机
# 把容器内的日志拷出来分析
docker cp my-container:/var/log/nginx/access.log /tmp/access.log
容器必须存在(已停止的容器也可以,不需要运行中)
如果宿主机目标路径只写了目录 /tmp/,文件保留原名放入该目录

场景 3:宿主机整个目录 → 容器
# 把本地项目代码复制到容器里
docker cp /home/user/my-project/. my-container:/app/
⚠️ 注意末尾的 /.:
/home/user/my-project/. → 把目录内容复制到 /app/ 下(/app/file1, /app/file2)
/home/user/my-project (不带 /.)→ 把整个目录复制到 /app/my-project/ 下
# 对比效果
docker cp /src/my-project  container:/app/    # → /app/my-project/...
docker cp /src/my-project/. container:/app/   # → /app/...(内容直接铺在 /app 下)

场景 4:容器整个目录 → 宿主机
# 备份容器内的数据目录
docker cp my-container:/var/lib/mysql/. /backup/mysql-data/
同样注意 /. 的含义,逻辑与上面一致。

场景 5:使用 -a 保留原始 UID/GID
docker cp -a /host/file.txt container:/container/file.txt
默认行为:复制到容器内的文件 UID/GID 会变成容器内当前用户的
加 -a(archive):保留源文件的原始 UID/GID 和权限位
当您需要保持文件属主不变时(比如数据库文件、证书密钥),务必加 -a

场景 6:从 STDIN/STDOUT 流式传输
# 通过管道把内容写入容器文件
echo "server_name example.com;" | docker cp - my-container:/etc/nginx/conf.d/custom.conf

# 把容器文件内容输出到 stdout(可配合重定向)
docker cp my-container:/etc/nginx/nginx.conf - > /tmp/nginx.conf
用 - 代替路径,表示从 stdin 读取或向 stdout 写入。适合脚本自动化、不想产生临时文件的场景。

docker cp 的重要特性与限制
✅ 支持的特性
特性 说明
容器无需运行 已停止(Exited)的容器也能 cp
自动创建目标目录 目标父目录不存在时会自动创建(仅限单文件复制)
符号链接 默认跟随符号链接(复制实际内容)
-L 选项 始终跟随符号链接(与默认行为相同,显式声明)
-q 选项 静默模式,不输出进度信息
❌ 不支持 / 需注意的限制
限制 说明
不能通配符 docker cp container:/var/log/*.log /tmp/ ❌ 不支持 * 通配符
不能跨容器 只能宿主机↔容器,不能容器A→容器B
不保留时间戳 默认不保留 mtime/atime(加 -a 可部分保留)
大文件性能 走 Docker API,大文件/大量文件比 volume 慢
权限问题 容器内文件可能属于 root,拷到宿主机后仍是 root 属主
SELinux/AppArmor 某些安全策略可能阻止 cp 操作
通配符的解决办法

既然 docker cp 不支持通配符,可以这样绕过:

# 方法 1:在容器内用 exec + tar 打包再 cp
docker exec my-container tar cf - /var/log/*.log | tar xf - -C /tmp/

# 方法 2:先 cp 整个目录,再在宿主机筛选
docker cp my-container:/var/log/. /tmp/container-logs/
ls /tmp/container-logs/*.log

# 方法 3:exec + cat 单文件
docker exec my-container cat /var/log/error.log > /tmp/error.log
docker cp 不是唯一选择,不同场景有更优解:
方案 适用场景 优点 缺点
docker cp 一次性拷贝少量文件 简单直接,容器无需运行 不支持通配符,大文件慢
Volume 挂载 (-v) 持续共享目录 实时同步,性能最好 需创建时指定,事后无法添加
docker exec + 重定向 小文件/文本内容 灵活,可配合管道 不适合二进制/大文件
tar 管道 批量文件/保留权限 支持通配符,保留属性 命令较复杂
Bind Mount 开发环境实时编辑 宿主机改文件容器立即生效 同 Volume,需创建时指定
什么是守护式(持续运行)容器 :
  • 能够长期运行
  • 无需交互式会话
  • 适合运行应用程序和服务
镜像制作
知识点

1、镜像中没有内核 

  • 镜像为了更小和运行效率,镜像中是不包含内核的,而使用宿主机的内核
  • 镜像本身则只提供应用所依赖的根文件系统和相关文件,即系统运行所必要的文件目录,比如: /sys, /dev/,/proc/bin/etc等目录 基于内核的名称空间实现容器的各种资源隔离
2、
容器中的程序后台运行会导致此容器启动后立即退出
当容器启动时,它会运行一个指定的初始化主程序,通常是容器的 PID 1 。如果这个主进程退出或停止, 容器也会相应地退出和停止。 在Docker 容器中的初始化进程可以被 Dockerfile 中的 CMD ENTRYPOINT 指令所指明;也可以被 docker run命令的启动参数所覆盖。 如果主进程以后台方式运行,因为容器认为主进程已经完成,它就会停止整个容器。 Docker容器如果希望启动后能持续运行 , 就必须有一个能前台并持续运行的进程 此进程可以为PID 1 的主进程,也可以是 PID 1 的主进程的子进程, 即一个容器要持续运行,必需有一个进程是前台执行的进程,如果此前台的进程或者PID 1 的主进程被 停止,容器将退出 如果在容器中启动传统的服务,如:httpd,php-fpm 等均为后台进程模式运行 , 就导致 docker 在前台没有 运行的应用, 这样的容器启动后会立即退出。所以一般会将服务程序以前台方式运行 对于有一些可能不知道怎么实现前台运行的程序, 只需要在启动该程序之后添加类似于 tail top 这种可 以前台运行的程序即可. 比较常用的方法,如 tail - f /etc/hosts
基于容器手动制镜像
docker commit
基于容器手动制作镜像步骤具体如下 :
1. 下载一个系统的官方基础镜像,如 : CentOS Ubuntu
2. 基于基础镜像启动一个容器 , 并进入到容器
3. 在容器里面做配置操作
  • 安装基础命令
  • 配置运行环境
  • 安装服务和配置服务
  • 放业务程序代码
4. 提交为一个新镜像 docker commit
5. 基于自己的的镜像创建容器并测试访问
基于ubuntu官方镜像安装nginx并手动commit制作镜像

nginx已经安装完毕,可以看到容器大小已经从45.4m变成了73.9m

修改一下html文件 welcome to nginx-test-docker并commit提交成新镜像ubuntu-test-nginx:v1.18.0

从制作的镜像启动容器并访问

访问测试

使用Dockerfile执行docker build自动构建镜像
dockerfile使用详解
DockerFile 是一种被 Docker 程序解释执行的脚本,由一条条的命令组成的,每条命令对应 linux 下面的 一条命令,Docker 程序将这些 DockerFile 指令再翻译成真正的 linux 命令,其有自己的书写方式和支持的命令,Docker 程序读取 DockerFile 并根据指令生成 Docker 镜像,相比手动制作镜像的方式, DockerFile 更能直观的展示镜像是怎么产生的,有了DockerFile ,当后期有额外的需求时,只要在之前的 DockerFile添加或者修改响应的命令即可重新生成新的 Docker 镜像,避免了重复手动制作镜像的麻烦 , 类似与shell 脚本一样 , 可以方便高效的制作镜像 Docker守护程序 Dockerfile 逐一运行指令,如有必要,将每个指令的结果提交到新镜像,然后最终输出新镜像的ID 。 Docker守护程序将自动清理之前发送的上下文 请注意,每条指令都是独立运行的,并会导致创建新镜像,比如 RUN cd /tmp 对下一条指令不会有任何 影响。 Docker将尽可能重用中间镜像层(缓存),以显著加速 docker build 命令的执行过程,这由 Using cache 控制台输出中的消息指示
Dockerfile文件格式
  • Dockerfile 是一个有特定语法格式的文本文件
  • dockerfile 官方说明: https://docs.docker.com/engine/reference/builder/
  • 帮助: man 5 dockerfile
Dockerfile 文件说明
  • 每一行以Dockerfile的指令开头,指令不区分大小写,但是惯例使用大写
  • 使用 # 开始作为注释
  • 每一行只支持一条指令,每条指令可以携带多个参数
  • 指令按文件的顺序从上至下进行执行
  • 每个指令的执行会生成一个新的镜像层,为了减少分层和镜像大小,尽可能将多条指令合并成一条指令
  • 制作镜像一般可能需要反复多次,每次执行dockfile都按顺序执行,从头开始,已经执行过的指令已经缓存,不需要再执行,如果后续有一行新的指令没执行过,其往后的指令将会重新执行,所以为加速镜像制作,将最常变化的内容放下dockerfile的文件的后面
指令 英文描述 中文含义 详细说明 语法示例
FROM Create a new build stage from a base image 指定基础镜像 必须是 Dockerfile 的第一条指令(ARG 除外)。定义从哪个镜像开始构建,可多次出现实现多阶段构建 FROM ubuntu:26.04
FROM nginx:latest AS builder
RUN Execute build commands 构建时执行命令 在镜像构建阶段执行命令,每执行一条就生成一个新的镜像层。常用于安装软件包 RUN apt update && apt install -y nginx
CMD Specify default commands 指定容器默认启动命令 容器启动时执行的默认命令,可被 docker run 后的参数覆盖。一个 Dockerfile 只能有一个 CMD(多个则最后一个生效) CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT Specify default executable 指定容器入口程序 配置容器启动时的入口可执行程序,不易被覆盖,常与 CMD 配合(ENTRYPOINT 定程序,CMD 定默认参数) ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
EXPOSE Describe which ports your application is listening on 声明监听端口 声明容器运行时监听的端口(仅文档作用,不会真正映射)。真正映射要靠 docker run -p EXPOSE 80
EXPOSE 80/tcp 443/tcp
ENV Set environment variables 设置环境变量 设置环境变量,在构建和运行时都有效,会被子镜像继承 ENV DEBIAN_FRONTEND=noninteractive
ENV NGINX_VERSION=1.26
ADD Add local or remote files and directories 添加文件/目录(高级) 把本地文件/URL/压缩包复制到镜像。会自动解压 tar、自动下载 URL,功能比 COPY 强但行为不可预期 ADD app.tar.gz /opt/
ADD https://example.com/file /tmp/
COPY Copy files and directories 复制文件/目录 把本地文件/目录复制到镜像。只做单纯复制,不解压不下载,行为可预期,优先于 ADD 使用 COPY nginx.conf /etc/nginx/
COPY . /app/
VOLUME Create volume mounts 创建挂载点 声明一个挂载点,使该目录的数据持久化或可被宿主机/其他容器共享,绕过联合文件系统 VOLUME /var/log/nginx
VOLUME ["/data", "/config"]
WORKDIR Change working directory 设置工作目录 为后续的 RUN/CMD/COPY 等指令设置工作目录(相当于 cd),可多次使用(相对路径会叠加) WORKDIR /app
WORKDIR src # → /app/src
USER Set user and group ID 设置运行用户 指定后续 RUN/CMD/ENTRYPOINT 执行时的用户身份(UID/GID),出于安全建议不用 root USER www-data
USER 1000:1000
ARG Use build-time variables 定义构建时变量 定义构建时(docker build)可用的变量,通过 --build-arg 传值,不会保留到运行时的镜像中 ARG VERSION=1.0
RUN echo $VERSION
构建:docker build --build-arg VERSION=2.0 .
LABEL Add metadata to an image 添加镜像元数据 给镜像添加键值对元数据(如作者、版本、描述),用于组织和查询,不影响运行 LABEL maintainer="admin@example.com"
LABEL version="1.0" description="nginx image"
HEALTHCHECK Check a container's health on startup 配置健康检查 告诉 Docker 如何检测容器是否健康,定期执行指定命令,结果反映在 docker ps 的 STATUS 列 HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1
SHELL Set the default shell of an image 设置默认 Shell 修改 RUN/CMD/ENTRYPOINT 使用 shell 形式时的默认 shell(Linux 默认 /bin/sh -c SHELL ["powershell", "-command"]
SHELL ["/bin/bash", "-c"]
STOPSIGNAL Specify the system call signal for exiting a container 设置停止信号 指定 docker stop 时发送给容器的系统调用信号(默认 SIGTERM),用于优雅退出 STOPSIGNAL SIGQUIT
STOPSIGNAL 9
ONBUILD Specify instructions for when the image is used in a build 设置触发器指令 注册一个触发器,当本镜像被用作其他镜像的 FROM 基础镜像时,触发的指令才在子镜像构建时执行 ONBUILD RUN echo "被继承时执行"
ONBUILD COPY . /app/
MAINTAINER Specify the author of an image 指定镜像作者(已弃用) 旧版指定作者的指令,已被弃用,现在推荐用 LABEL maintainer="..." 代替 MAINTAINER admin@example.com(❌ 弃用)
改用 LABEL maintainer="..."

FROM ...              # 1. 基础镜像(必须第一)
LABEL ...             # 2. 元数据
ARG ...               # 3. 构建变量
ENV ...               # 4. 环境变量
WORKDIR ...           # 5. 工作目录
COPY 依赖文件 ...      # 6. 先复制依赖(利用缓存)
RUN 安装依赖 ...       # 7. 安装依赖
COPY 源码 ...          # 8. 再复制源码(变动频繁,放后面)
RUN 编译/构建 ...      # 9. 编译
VOLUME ...            # 10. 挂载点
EXPOSE ...            # 11. 声明端口
HEALTHCHECK ...       # 12. 健康检查
USER ...              # 13. 切换用户
ENTRYPOINT / CMD ...  # 14. 启动命令(最后)

FROM: 指定基础镜像
定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。
FROM 就是指定基础镜像,此指令通常必需放在 Dockerfile 文件第一个非注释行。后续的指令都是运行 于此基准镜像所提供的运行环境
基础镜像可以是任何可用镜像文件,默认情况下, docker build 会在 docker 主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry 上拉取所需的镜像文件 . 如果找不到指定的镜像文件, docker build会返回一个错误信息
如何选择合适的镜像呢?
对于不同的软件官方都提供了相关的 docker 镜像,比如 : nginx redis mysql httpd tomcat 等服务 类的镜像,也有操作系统类,如: centos ubuntu debian 等。建议使用官方镜像,比较安全。
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
#说明:  
--platform 指定镜像的平台,比如: linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest
关于scratch 镜像
FROM scratch
参考链接:
https://hub.docker.com/_/scratch?tab=description
https://docs.docker.com/develop/develop-images/baseimages/
该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
该镜像在构建基础镜像(例如debian和busybox)或超最小镜像(仅包含一个二进制文件及其所需内容,例
如:hello-world)的上下文中最有用。
LABEL: 指定镜像元数据
可以指定定镜像元数据,如: 镜像作者等
LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

一个镜像可以有多个label ,还可以写在一行中,即多标签写法,可以减少镜像的层数
范例: 多标签写法

#一行格式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
#多行格式
LABEL multi.label1="value1" \
     multi.label2="value2" \
      other="value3

docker inspect 命令可以查看LABEL

"Labels": {
    "com.example.vendor": "ACME Incorporated"
    "com.example.label-with-value": "foo",
    "version": "1.0",
    "description": "This text illustrates that label-values can span multiple 
lines.",
    "multi.label1": "value1",
    "multi.label2": "value2",
    "other": "value3"
},
MAINTAINER: 指定维护者信息此指令已过时,用LABEL代替
RUN: 执行 shell 命令
RUN 指令用来在 构建镜像阶段 需要执行 FROM 指定镜像所支持的 Shell 命令。
通常各种基础镜像一般都支持丰富的 shell 命令
注意 : RUN 可以写多个,每一个 RUN 指令都会建立一个镜像层,所以尽可能合并成一条指令 , 比如将多个 shell命令通过 && 连接一起成为在一条指令 每个RUN 都是独立运行的 , 和前一个 RUN 无关
#shell 格式: 相当于 /bin/sh -c <命令> 此种形式支持环境变量
RUN <命令> 
#如果想使用其它shell,可以用SHELL指令指定不同的shell
SHELL ["/bin/bash", "-c"]
RUN <命令> 
#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号
RUN ["executable","param1","param2"...] 
#exec格式可以指定其它shell
RUN ["/bin/bash","-c","echo hello wang"]


shell格式中,<command>通常是一个shell命令,且以"/bin/sh -c”来运行它,这意味着此进程在容器中
的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收
不到SIGTERM信号
exec格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递
给命令的选项或参数;然而,此种格式指定的命令不会以"/bin/sh -c"来发起,因此常见的shell操作如变
量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换
为类似下面的格式。
RUN ["/bin/bash", "-c", "<executable>", "<param1>"]
关于 shell exec 形式
Shell 解释
Shell 形式 :命令通过 Shell 解释,这意味着可以使用 Shell 特性,例如环境变量替换、管道和重
定向。
Exec 形式 :命令直接执行,不经过 Shell 。因此,没有 Shell 特性支持。
可移植性和安全性
Shell 形式 :由于依赖 Shell 解释,可能会受到 Shell 注入攻击或其他 Shell 相关的问题。
Exec 形式 :更安全和可移植,因为命令直接执行,不依赖 Shell
性能
Shell 形式 :需要额外启动一个 Shell 进程,可能略有性能开销。
Exec 形式 :直接执行命令,通常更高效。
复杂命令
Shell 形式 :适合编写复杂的命令或使用 Shell 特性的场景
Exec 形式 :适合简单的命令或不需要 Shell 功能的场景。
范例:shell 和 exec 格式比较

[root@ubuntu2204 alpine]#cat Dockerfile 
FROM registry.cn-beijing.aliyuncs.com/wangxiaochun/alpine:3.20.0
LABEL maintainer="wangxiaochun<root@wangxiaochun.com>"
RUN sed -i 's@dl-cdn.alpinelinux.org@mirrors.tuna.tsinghua.edu.cn@' 
/etc/apk/repositories
RUN apk add bash
RUN echo {1..10} > a.txt
RUN ["bash","-c","echo {1..10} > b.txt"]
[root@ubuntu2204 alpine]#docker run --rm -it alpine:3.20.0-2024-06-14_1718345655 
cat b.txt
1 2 3 4 5 6 7 8 9 10
[root@ubuntu2204 alpine]#docker run --rm -it alpine:3.20.0-2024-06-14_1718345655 
cat a.txt
{1..10}


RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
RUN ["/bin/bash", "-c", "echo hello world"]
RUN yum -y install epel-release \
     && yum -y install nginx \
     && rm -rf /usr/share/nginx/html/*
     && echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html


FROM alpine:3.19.1
LABEL author=wang class=m57
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/'
/etc/apk/repositories &&   apk update && apk  --no-cache add curl bash tzdata 
&& ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime


多个 前后RUN 命令独立无关和shell命令不同

#world.txt并不存放在/app内
RUN cd /app
RUN echo "hello" > world.txt
#下面写法可以实现关联关系
RUN cd /app &&  echo "hello" > world.txt
ENV: 设置环境变量
ENV 可以定义环境变量和值,会被后续指令(如:ENV,ADD,COPY,RUN等)通过$KEY或${KEY}进行引用,
并在容器运行时保持

#变量赋值格式1
ENV <key> <value>   #此格式只能对一个key赋值,<key>之后的所有内容均会被视作其<value>的组成
部分

#变量赋值格式2
ENV <key1>=<value1> <key2>=<value2> \  #此格式可以支持多个key赋值,定义多个变量建议使用,
减少镜像层
 <key3>=<value3> ...
 
#如果<value>中包含空格,可以以反斜线\进行转义,也可通过对<value>加引号进行标识;另外,反斜线也
可用于续行
#只使用一次变量
RUN <key>=<value> <command>
    
#引用变量
RUN $key .....
如果运行容器时如果需要修改变量 , 可以执行下面通过基于 exec 机制实现
注意 : 下面方式只影响容器运行时环境 , 而不影响构建镜像的过程 , 即只能覆盖 docker run 时的环境变量 ,
不会影响 docker build 时环境变量的值
docker run -e|--env <key>=<value>
#说明
-e, --env list   #Set environment variables
    --env-file filename     #Read in a file of environment variables
#格式1
ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy
#格式2
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy


ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC
/usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH


[root@ubuntu1804 dockerfile]#cat Dockerfile
FROM busybox
LABEL maintainer="wangxiaochun <root@wangxiaochun.com>"
ENV NAME wang xiao chun
RUN touch $NAME.txt
COPY: 复制文本
复制本地宿主机的 到容器中的 。
COPY [OPTIONS] [--chown=<user>:<group>] <src>... <dest>
COPY [OPTIONS] [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,
建议使用此格式
可以是多个,可以使用通配符,通配符规则满足Go的filepath.Match 规则
filepath.Match 参考链接: https://golang.org/pkg/path/filepath/#Match
 必须是build上下文中的相对路径(为 Dockerfile 所在目录的相对路径),不能是其父目录中的文
件
如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
如果指定了多个, 或在中使用了通配符,则必须是一个目 录,且必须以 / 结尾
可以是绝对路径或者是 WORKDIR 指定的相对路径
使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
如果事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录

COPY hom* /mydir/    
COPY hom?.txt /mydir/
#多阶段构建
COPY --from=build /myapp /usr/bin/
ADD: 复制和解包文件
该命令可认为是增强版的COPY,不仅支持COPY,还支持自动解压缩。可以将复制指定的 到容器中的
说明: 
可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
可以是绝对路径或者是 WORKDIR 指定的相对路径
如果是目录,只复制目录中的内容,而非目录本身
如果是一个 URL ,下载后的文件权限自动设置为 600 
如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以 / 结尾,则文件名URL指定
的文件将被直接下载并保存为/< filename>
如果是一个本地文件系统上的打包文件,如: gz, bz2 ,xz ,它将被解包 ,其行为类似于"tar -x"命令,
但是通过URL获取到的tar文件将不会自动展开
如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则
其被视作一个普通文件,的内容将被直接写入到

ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /


ADD nginx-1.26.2.tar.gz /usr/local/src
RUN apk --no-cache add gcc make libgcc libc-dev libcurl libc-utils pcre-dev 
zlib-dev libnfs pcre pcre2 libevent libevent-dev && cd /usr/local/src/nginx-
1.26.2 && ./configure --prefix=/apps/nginx && make && make install && rm -rf 
/usr/local/src/nginx-1.26.2
CMD: 容器启动命令
  • 一个容器中通常需要一个持续运行的业务主进程
  • CMD 用来指定启动容器时默认执行的一个初始命令
  • 如果容器内没有一个持续运行的前台进程,容器会停止 所以一般CMD 指定的命令为持续运行且为前台命令,如果有多个命令,需要保证有一个命令是前台并持续 运行的 如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT命令,那么开启 容器时就会使用执行CMD指定的默认的命令
  • RUN 命令是在构建镜像时执行的命令
  • 每个 Dockerfile 只能有一条 CMD 命令。如指定了多条,只有最后一条被执行
  • 如果用户启动容器时用 docker run xxx 指定运行的命令,则会覆盖 CMD 指定的命令
#https://docs.docker.com/reference/dockerfile/#shell-and-exec-form
#格式1:使用exec形式,推荐方式,第一个参数必须是命令的全路径,此种形式不支持环境变量,注意:是双引
号,不能是单引号
CMD ["executable","param1","param2"] 
#格式2:shell形式,默认/bin/sh 中执行,提供给需要交互的应用;此种形式支持环境变量,相当于执行
/bin/sh -c "command param1 param2"
CMD command param1 param2 
#指定不同的shell
#方法1
SHELL ["/bin/bash", "-c"]
CMD command param1 param2
#方法2
CMD ["/bin/bash", "-c","executable","param1","param2"]
#格式3:使用exec形式,提供给 ENTRYPOINT 命令的充当其参数
CMD ["param1","param2"] 
范例:通过CMD基于alpine编译安装Nginx并结合脚本实现启动容器时的定制功能
CMD ["nginx", "-g", "daemon off;"]
CMD ["/apps/nginx/sbin/nginx", "-g","daemon off;"]
#如下格式报错
CMD /apps/nginx/sbin/nginx -g ”daemon off;"
构建镜像docker build 命令
docker build命令使用Dockerfile文件创建镜像
docker build [OPTIONS] PATH | URL | -
说明:  
PATH | URL | -     #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取
Dockerfile的内容
-f, --file string  #Dockerfile文件名,默认为 PATH/Dockerfile
--force-rm   #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache   #不使用之前构建中创建的缓存
-q  --quiet=false  #不显示Dockerfile的RUN运行的输出结果
--rm=true   #创建镜像成功时,删除临时容器
-t --tag list   #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签
默认为latest)

docker history 镜像ID查看镜像构建历史
使用Dockerfile构建基于Centos的nginx镜像
root@docker01:~/soft# cat Dockerfile
FROM quay.io/centos/centos:stream9
LABEL maintainer="kaikai<kaikai@126.com>"
RUN dnf install -y nginx && echo "nginx website2 in Docker" > /usr/share/nginx/html/index.html
ENV PEFRESH_DATA=2020-01-01
EXPOSE 81
CMD ["nginx", "-g", "daemon off;"]

因为Centos7 8都已经停止维护这里使用了stream 9

Doker容器数据管理
Docker 镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户 写入的数据都保存在这一层中。
容器的数据分层目录
LowerDir: image 镜像层 , 即镜像本身,只读
UpperDir: 容器的上层 , 可读写 , 容器变化的数据存放在此处
MergedDir: 容器的文件系统,使用 Union FS (联合文件系统)将 lowerdir upperdir 合并完成
后给容器使用 , 最终呈现给用户的统一视图
WorkDir: 容器在宿主机的工作目录 , 挂载后内容会被清空,且在使用过程中其内容用户不可见
最新版查看以上四个目录的具体信息不能使用docker inspect用以下命令
cat /proc/$(docker inspect -f '{{.State.Pid}}' 容器名称)/mountinfo | grep overlay
哪些数据需要持久化
有状态协议就是就通信双方要记住双方,并且共享一些信息。而无状态协议的通信每次都是独立的,与上一次 的通信没什么关系。
" 状态 可以理解为 记忆 ,有状态对应有记忆,无状态对应无记忆
容器数据持久保存方式
如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录
Docker 提供了多种方法将宿主机的文件系统挂载到容器内,包括以下几种主要方式:
绑定挂载(Bind Mount):
这种方式可以将指定的宿主机上的任意文件或目录挂载到容器内。与卷不同,绑定挂载依赖于宿主
机的文件系统结构。 因此,如果你在 Docker CLI 或 Docker API 中使用绑定挂载,你需要知道宿主机上的文件或目录的 具体路径。 另外,由于绑定挂载可以访问宿主机上的任意文件和目录,使用这种方式可能会有一定的安全风险。
卷(Volume):
这是 Docker 推荐的挂载方式。卷是完全由 Docker 管理的文件目录,可以在容器之间共享和重
用。在创建卷时, Docker 创建了一个目录在宿主机上,然后将这个目录挂载到容器内。卷的主要
优点是你可以使用 Docker CLI Docker API 来备份、迁移或者恢复卷,而无需关心卷在宿主机上
的具体位置。 卷分为匿名卷和命名卷
tmpfs 挂载:
此方式并不算一种持久化方案 tmpfs 挂载不与宿主机上的任何文件或目录相关联,而是将一个内存空间的临时文件系统挂载到容 器的某个目录下。 这种方式的主要优点是它提供了一个高速且安全的挂载方式,因为 tmpfs 挂载通常驻留在宿主机的 内存中,且在容器停止后会被自动删除。 tmpfs 挂载是临时的,只存留在容器宿主机的内存中。当容器停止时, tmpfs 挂载文件路径将被删
除,在那里写入的文件不会被持久化。
Docker 卷( Volume 类型分为两种 :
数据卷 (Data Volume): 直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式
较常用
数据卷容器 (Data Volume Container): 间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至
一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用
数据卷的特点和使用
数据卷实际上就是宿主机上的目录或者是文件,可以被直接 mount 到容器当中使用
实际生成环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的 可扩展性、稳定性以及数据的安全性
数据卷使用场景
  • 数据库
  • 日志输出
  • 静态web页面
  • 应用配置文件
  • 多容器间目录或文件共享
数据卷的特点
  • 数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用
  • 对数据卷更改数据在所有容器里面会立即更新
  • 数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响
  • 在容器里面的写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新
  • 依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理
  • 匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初 始化的数据卷中

数据卷分类
启动容器时,可以指定使用数据卷实现容器数据的持久化 , 数据卷有三种
  • 指定宿主机目录或文件: 指定宿主机的具体路径和容器路径的挂载关系,此方式不会创建数据卷
  • 匿名卷: 不指定数据名称,只指定容器内目录路径充当挂载点,docker自动指定宿主机的路径进行挂 载,此方式会创建匿名数据卷,DockerfileVOLUME指定的卷即为此种
  • 命名卷: 指定数据卷的名称和容器路径的挂载关系,此方式会创建命名数据卷
数据卷使用方法
docker run 命令的以下格式可以实现数据卷
-v, --volume=[host-src:]container-dest[:<options>]
<options>
ro 从容器内对此数据卷是只读,不写此项默认为可读可写
rw 从容器内对此数据卷可读可写,此为默认值
host-src 宿主机目录如果不存在,会自动创建
container-dest 容器目录如果不存在,会自动创建

绑定挂载

#指定宿主机目录或文件格式: 
-v <宿主机绝对路径的目录(文件)或者相对路径的文件>:<容器目录或文件>[:ro]  #将宿主机目录挂载容
器目录,两个目录都可自动创建
#注意:如果初始容器中有旧数据,将被宿主机目录覆盖
#注意:如果挂载文件,必须指定文件的相对(以.开头的相对路径)或绝对路径,否则会将目录名或文件名当成
命名卷

匿名卷

#匿名卷,只指定容器内路径,没有指定宿主机路径信息,宿主机自动生成/var/lib/docker/volumes/<卷
ID>/_data目录,并挂载至容器指定路径
#注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录
-v <容器内路径>
#示例:
docker run --name nginx -v /etc/nginx nginx

命名卷

#命名卷将固定的存放在/var/lib/docker/volumes/<卷名>/_data
#注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录
-v <卷名>:<容器目录路径>
#可以通过以下命令事先创建,如可没有事先创建卷名,docker run时也会自动创建卷
docker volume create <卷名>
#示例:
docker volume create vol1  #也可以事先不创建
docker run -d  -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx
docker rm -v --rm 选项可以删除容器时,同时删除相关联的匿名卷
管理数据卷命令
docker volume COMMAND
Commands:
 create     Create a volume
 inspect     Display detailed information on one or more volumes
  ls         List volumes
 prune       Remove all unused local volumes
  rm         Remove one or more volumes
查看数据卷的挂载关系
docker inspect --format="{{.Mounts}}" <容器ID>

范例: 删除所有数据卷
docker volume rm `docker volume ls -q`

创建命名卷并删除

删除不再使用的数据卷

docker volume prune -f 慎用
关于匿名数据卷和命名数据卷
命名卷就是有名字的卷,使用 docker volume create <卷名> 形式创建并命名的卷;而匿名卷就是没名
字的卷,一般是 docker run -v /data 这种不指定卷名的时候所产生,或者 Dockerfile 里面的定义
直接使用的。
有名字的卷,在用过一次后,以后挂载容器的时候还可以使用,因为有名字可以指定。所以一般需要保存的数
据使用命名卷保存。
而匿名卷则是随着容器建立而建立,随着容器消亡而淹没于卷列表中(对于 docker run 匿名卷不会被自动
删除)。 因此匿名卷只存放无关紧要的临时数据,随着容器消亡,这些数据将失去存在的意义。
Dockerfile中指定VOLUME为匿名数据卷,其目的只是为了将某个路径确定为卷。
按照最佳实践的要求,不应该在容器存储层内进行数据写入操作,所有写入应该使用卷。如果定制镜像的时
候,就可以确定某些目录会发生频繁大量的读写操作,那么为了避免在运行时由于用户疏忽而忘记指定卷,导
致容器发生存储层写入的问题,就可以在 Dockerfile 中使用 VOLUME 来指定某些目录为匿名卷。这样即
使用户忘记了指定卷,也不会产生不良的后果。
这个设置可以在运行时覆盖。通过 docker run 的 -v 参数或者 docker-compose.yml 的 volumes 
指定。使用命名卷的好处是可以复用,其它容器可以通过这个命名数据卷的名字来指定挂载,共享其内容(不
过要注意并发访问的竞争问题)。
比如,Dockerfile 中说 VOLUME /data,那么如果直接 docker run,其 /data 就会被挂载为匿名
卷,向 /data 写入的操作不会写入到容器存储层,而是写入到了匿名卷中。但是如果运行时 docker run 
-v mydata:/data,这就覆盖了 /data 的挂载设置,要求将 /data 挂载到名为 mydata 的命名卷中。
所以说 Dockerfile 中的 VOLUME 实际上是一层保险,确保镜像运行可以更好的遵循最佳实践,不向容器
存储层内进行写入操作。
数据卷默认可能会保存于 /var/lib/docker/volumes,不过一般不需要、也不应该访问这个位置。

mysql操作实例--绑定挂载

说明:nginx tomcat如果要加载宿主机的配置文件前提是宿主机相关的目录中要有配置正确的文件,需要事先在本地配置好,启动指定容器时加载进去。

docker run -d -v 
/data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v 
/data/testapp:/data/tomcat/webapps/testapp   -v /data/logs:/apps/tomcat/logs -p 
8080:8080 tomcat-web:app1


nginx我做实验时只是加载了目录因为里面没有配置文件没有正常创建启动
docker run --name test -v /data/testdir/conf:/etc/nginx -v /data/testdir/html:/usr/share/nginx/html nginx_centos9:v1.18.0
nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
匿名数据卷实战

匿名卷 -v 后面是挂载到容器内的目录,执行启动后docker会在/var/lib/docker/volumes目录下生成一个目录,并将生成这个目录中的子目录_data挂载到指定的-v后的目录中,并可以读写这个目录中的文件,清除容器后宿主机中的这个目录中的文件不丢失。

已经启动了一个容器,容器中的nginx html目录中有两个文件。

上面的操作是将nginx-test容器关闭,清理,现在启动一个新的nginx-test容器,并将匿名卷挂载到容器的/usr/share/nginx/html目录下,进行实验。

最后再总结一下匿名卷

  • 挂载方向:严格来说是宿主机的 _data 目录挂载到容器内的指定路径,容器内对该路径的读写实际上就是在操作宿主机 _data 目录中的文件。
  • 数据保留的前提:删除容器(docker rm)不会删除匿名卷,数据保留。但如果手动执行 docker volume rm <卷名> 或 docker volume prune,卷及其数据才会被真正删除。
  • 匿名卷的缺点:由于卷名是随机哈希值,没有可读性,时间久了很难分辨哪个卷属于哪个容器,因此生产环境更推荐使用具名卷-v my-volume:/容器内路径)。
命名卷实战

创建命名数据卷

docker volume create vol

使用命名数据卷创建容器

docker run -d --name nginx-test -p 8080:80 -v vol1:/usr/share/nginx/html nginx:latest

创建容器时自动创建命名数据卷

docker run -d --name nginx-test1 -p 8081:80 -v vol2:/usr/share/nginx/html nginx:latest

访问验证

说明:vol1是之前创建并挂载到过nginx-test的,之后nginx-test容器清除,然后这个里面有了我已经修改过的index.html,这时我们验证得到生效的文件是vol1上的index.html,说明命名卷里文件会覆盖容器目录中的文件。

vol2自动创建命名系统挂载到容器内的/usr/share/nginx/html中时原来里央没有文件,容器会以容器内这个目录有的文件进行生效。

Docker 卷挂载时:如果卷是空的,容器内目录的文件会复制到卷中;如果卷已有数据,则卷中的数据会直接覆盖容器目录的内容。对像是目录级别的操作。

数据卷容器
  • Dockerfile中创建的是匿名数据卷,无法直接实现多个容器之间共享数据
  • 数据卷容器主要的功能是可以让数据在多个docker容器之间共享
即可以让 B 容器和 C 容器都可以访问 A 容器的内容,即可以实现 A B C 三个容器之间的数据
读写共享。
相当于先要创建一个后台运行的容器作为 Server ,用于提供数据卷,这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为client 端 ,但此方法并不常使用 缺点: 因为依赖一 Server 的容器存在,所以此 Server 容器出了问题,其它 Client 容器都会受影响
注意:即使 Server 删除,无法创建新的 Client 容器,但是可以基于已有 Client 容器,再创建新的其它
Client 容器
备份还原
#在执行备份命令容器上执行备份方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu
root@ca5bb2c1f877:/#tar cvf /backup/backup.tar [container data volume]
#说明
[container name] #表示需要备份的匿名数据卷的容器
[container data volume] #表示容器内的需要备份的匿名数据卷对应的目录
#还原方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu
root@ca5bb2c1f877:/#tar xvf /backup/backup.tar -C [container data volume]

实战使用数据卷容器备份mysql数据库
1、启动一个mysql容器,默认使用的匿名卷
docker run -d --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=work123 mysql:8.0
2、查看这个容器的匿名卷信息
docker volume ls
DRIVER    VOLUME NAME
local     93df89073a1ffb0449f329f7276ff2a8db8b157f25d11218b8fd53167ebdb4f9
local     vol1
local     vol2


root@docker01:~# docker inspect mysql-test --format="{{.Mounts}}"
[{volume 93df89073a1ffb0449f329f7276ff2a8db8b157f25d11218b8fd53167ebdb4f9 /var/lib/docker/volumes/93df89073a1ffb0449f329f7276ff2a8db8b157f25d11218b8fd53167ebdb4f9/_data /var/lib/mysql local  true }]

3、写入一点测试数据

docker exec -it mysql-test /bin/bash
输入用户名密码登陆数据库并创建测试库
mysql> create database testdb;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+

4、创建数据卷容器
root@docker01:~# docker create --name volume-server --volumes-from mysql-test alpine:3.24.1
dc05b8c34ef58e9fa3a5503ac3484d09702d49723a58ee678595271c70d9b82c
root@docker01:~# docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                                                    NAMES
dc05b8c34ef5   alpine:3.24.1   "/bin/sh"                3 seconds ago    Created                                                                  volume-server
51a88de14ae5   mysql:8.0       "docker-entrypoint.s…"   29 minutes ago   Up 29 minutes   0.0.0.0:3306->3306/tcp, [::]:3306->3306/tcp, 33060/tcp   mysql-test

5、使用临时容器备份数据
在root目录下创建backup目录
mkdir backup
root@docker01:~# docker run --rm --volumes-from mysql-test -v /root/backup:/backup alpine:3.24.1 tar cvf /backup/mysql-bak.tar -C /var/lib/mysql .
./
./testdb/
./testdb/users.ibd
./ibtmp1
./mysql.sock
./#innodb_temp/
./#innodb_temp/temp_6.ibt
./#innodb_temp/temp_5.ibt
root@docker01:~# cd backup/
root@docker01:~/backup# ls
mysql-bak.tar

6、清除mysql-test容器,这时匿名卷还在
root@docker01:~# docker rm -f mysql-test
mysql-test
root@docker01:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@docker01:~# docker ps -a
CONTAINER ID   IMAGE           COMMAND     CREATED         STATUS    PORTS     NAMES
dc05b8c34ef5   alpine:3.24.1   "/bin/sh"   5 minutes ago   Created             volume-server
root@docker01:~# docker volume ls
DRIVER    VOLUME NAME
local     93df89073a1ffb0449f329f7276ff2a8db8b157f25d11218b8fd53167ebdb4f9

7、启动一个新的mysql-test容器
root@docker01:~# docker run -d --name mysql-test -p 3306:3306 mysql:8.0
45b8984bc65019c67aa3b771ebd8550388ac67234ca33ef1b1ed393469f34515
root@docker01:~# docker volume ls
DRIVER    VOLUME NAME
local     93df89073a1ffb0449f329f7276ff2a8db8b157f25d11218b8fd53167ebdb4f9
local     a800d7ad8096ca2566fc5831cd00348be2eed0f63c535122987a6fc9d48c1b91

root@docker01:~# docker inspect mysql-test --format="{{.Mounts}}"
[{volume a800d7ad8096ca2566fc5831cd00348be2eed0f63c535122987a6fc9d48c1b91 /var/lib/docker/volumes/a800d7ad8096ca2566fc5831cd00348be2eed0f63c535122987a6fc9d48c1b91/_data /var/lib/mysql local  true }] 现在新的容器的匿名卷是a800d7ad8096ca2566fc5831cd00348be2eed0f63c535122987a6fc9d48c1b91

8、数据还原
root@docker01:~# docker stop mysql-test
mysql-test
root@docker01:~# docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS                     PORTS     NAMES
45b8984bc650   mysql:8.0       "docker-entrypoint.s…"   4 minutes ago    Exited (1) 4 minutes ago             mysql-test
dc05b8c34ef5   alpine:3.24.1   "/bin/sh"                14 minutes ago   Created                              volume-server
root@docker01:~# docker run --rm --volumes-from mysql-test -v /root/backup:/backup alpine:3.24.1 sh -c "rm -f /var/lib/mysql/* && tar xvf /backup/mysql-bak.tar -C /var/lib/mysql"

9、数据验证
docker exec -it mysql-restored mysql -uroot -pwork123 -e "USE testdb; SELECT * FROM users;"

+----+--------+
| id | name   |
+----+--------+
|  1 | 张三 |
|  2 | 李四 |
|  3 | 王五 |
+----+--------+

数据卷容器总结

将提供卷的容器 Server 删除,已经运行的容器 Client 依然可以使用挂载的卷,因为容器是通过挂载访问数据的,但是无法创建新的卷容器客户端,但是再把卷容器Server 创建后即可正常创建卷容器 Client , 此方式可以用于线上共享数据目录等环境,因为即使数据卷容器被删除了,其他已经运行的容器依然可以挂载使用,由此可知, 数据卷容器的功能只是将数据挂载信息传递给了其它使用数据卷容器的容器 , 而数据卷容器本 身并不提供数据存储功能,数据卷容器可以作为共享的方式为其他容器提供文件共享,类似于NFS 共享,可以在生产中启动一个实 例挂载本地的目录,然后其他的容器分别挂载此容器的目录,即可保证各容器之间的数据一致性数据卷容器的 Server Client 可以不使用同一个镜像生成当创建Client 容器时 , 会复制 Server 容器的数据卷信息 , 后续 Server 容器状态和存在与否 , 都不会影响 Client容器使用的数据卷当Server 容器删除后 , 不能再基于 Server 容器创建新的 Client 容器 , 但可以基于已存在的 Client 容器来创建 新的Client 容器最终实现了多个客户端容器共享相同的持久化宿主机的存储方案
Docke网络管理
Docker安装后默认的网络设置
Docker 服务安装完成之后,默认在每个宿主机会生成一个名称为 docker0 的网卡其 IP 地址都是
172.17.0.1/16
创建容器后的网络配置
veth Virtual Ethernet )是 Linux 内核中的一种虚拟网络设备,通常用于连接两个网络命名空间。 veth 设备总是成对出现,当在一个网络命名空间中创建veth 设备时,会同时创建两个端点。 veth 设备的两个端点可以被看作是一个虚拟的以太网电缆,任何发送到其中一个端点的数据包都会被立即从另一个端点传出。以下是veth 设备的一些主要特性:
1. 命名空间间通信 veth 设备主要用于连接不同的网络命名空间,允许它们之间进行通信。这使得在 一个隔离的环境中运行的进程可以与外部世界交互,而不会影响到其它网络命名空间。
2. 成对出现 veth 设备总是成对创建的,形成一个虚拟的双向通道。当数据包发送到一个端点时,它 会从另一个端点出来。因此,你可以把veth 设备看作是一个虚拟的以太网电缆。
3. 灵活性 veth 设备的两个端点可以分别位于不同的网络命名空间中,甚至可以在同一命名空间中。这为设置复杂的网络拓扑提供了很大的灵活性。
4. 和其他网络设备相互操作 veth 设备可以和 Linux 的其他网络设备(如 bridge veth pair
physical NIC 等)一起使用,创建复杂的网络配置。
veth 设备在一些网络虚拟化技术中被广泛使用,例如 Docker 容器。每一个 Docker 容器都有自己的网络 命名空间,Docker 使用 veth 设备连接容器的网络命名空间和主机的网络命名空间,使得容器可以和外部 网络进行通信。每次新建容器后宿主机多了一个虚拟网卡vethxxxxx ,和容器的网卡组合成一个网卡,比如 : 137: veth8ca6d43@if136,而在容器内的网卡名为 136 ,可以看出和宿主机的网卡之间的关联容器会自动获取一个172.17.0.0/16 网段的随机地址,默认从 172.17.0.2 开始,第二次容器为 172.17.0.3,以此类推容器获取的地址并不固定, 每次容器重启 , 可能会发生地址变化

同一个宿主机的不同容器可相互通信

默认情况下
同一个宿主机的不同容器之间可以相互通信
不同宿主机之间的容器 IP 地址重复,默认不能相互通信
#dockerd 的 --icc=false 选项可以禁止同一个宿主机的不同容器间通信

/etc/docker/daemon.json

{
  "registry-mirrors": ["https://xxx"],
  "icc": false
}

#添加--icc选项,本质上就是修改iptables规则 -A FORWARD -i docker0 -o docker0 -j 
ACCEPT修改为DROP
修改默认 docker0 网桥的网络配置
默认 docker 后会自动生成一个 docker0 的网桥 , 使用的 IP 172.17.0.1/16, 可能和宿主机的网段发生冲突 , 可以将其修改为其它网段的地址, 避免冲突
vim /etc/docker/daemon.json

{
   "bip": "192.168.100.1/24",
  "registry-mirrors": ["https://xx.xx.com"]
}
修改默认网络设置使用自定义网桥
新建容器默认使用 docker0 的网络配置 , 可以修改默认指向自定义的网桥网络
apt -y install bridge-utils
#brctl addbr br0
给网桥添加IP,用于给容器分配置IP,如果不指定IP,会自动向后续网段,默认是172.18.0.0/16
以上操作是增加一个新的br0 
容器名称互联
新建容器时, docker 会自动分配容器名称,容器 ID IP 地址,导致容器名称,容器 ID IP 都不固定,那 么如何区分不同的容器,实现和确定目标容器的通信呢?解决方案是给容器起个固定的名称,容器之间 通过固定名称实现确定目标的通信 有两种固定名称:
  • 容器名称
  • 容器名称的别名
即在同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,比如: 一个业务前端静态页面是 使用nginx ,动态页面使用的是 tomcat ,另外还需要负载均衡调度器,如 : haproxy 对请求调度至 nginx 和tomcat 的容器,由于容器在启动的时候其内部 IP 地址是 DHCP 随机分配的,而给容器起个固定的名
称,则是相对比较固定的,因此比较适用于此场景
注意 : 如果被引用的容器地址变化 , 旧版必须重启当前容器才能生效,新版容器自动更新 /etc/hosts 的域
名解析
只能实现单向的域名解析
容器名称实现
docker run 创建容器,可使用 --link 选项实现容器名称的引用,其本质就是在容器内的 /etc/hosts 中添加 - -link 后指定的容器的 IP 和主机名的对应关系,从而实现名称解析
--link list                          #Add link to another container
格式:  
docker run --name <容器名称> #先创建指定名称的容器
docker run --link <目标通信的容器ID或容器名称>     #再创建容器时引用上面容器的名称
root@docker01:~# docker run --name test1 -it --rm alpine:3.24.1 sh
/ # ls
bin    dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	f9c8f5eb1399



root@docker01:/etc/docker# docker run -it --name test2 --link test1 alpine:3.24.1  sh
WARNING: Links on the default bridge network are deprecated and will be removed in a future release. Use a custom network instead.
/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	test1 f9c8f5eb1399
172.17.0.3	cd2c6a478fb3


在test1上ping test2
/ # ping test2
ping: bad address 'test2'
/ #
在test2上ping test1
/ # ping test1
PING test1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.176 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.126 ms
^C
--- test1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.126/0.151/0.176 ms

新建第二个容器时引用第一个容器的名称
会自动将第一个主机的名称加入/etc/hosts文件,从而可以利用第一个容器名称进行访问
通过自定义容器别名互联
容器别名介绍
自定义的容器名称可能后期会发生变化,那么一旦名称发生变化,容器内程序之间也必须要随之发生变
化,比如 : 程序通过固定的容器名称进行服务调用,但是容器名称发生变化之后再使用之前的名称肯定是
无法成功调用,每次都进行更改的话又比较麻烦,因此可以使用自定义别名的方式解决,即容器名称可
以随意更改,只要不更改别名即可
容器别名实现
docker run --name <容器名称> 
#先创建指定名称的容器
docker run --name <容器名称> --link <目标容器名称>:"<容器别名1> <容器别名2> ..." 
#给上面创建的容器起别名,来创建新容器

root@docker01:~# docker run -it --name test3 --rm --link test1:test1-alias alpine:3.24.1

/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	test1-alias f9c8f5eb1399 test1
172.17.0.4	da1dcbfb0581
/ # ping test1
PING test1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.159 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.043 ms
^C
--- test1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.043/0.101/0.159 ms
/ # ping test1-alias
PING test1-alias (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.075 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.044 ms
Docker 网络连接模式
Docker 的网络支持 5 种网络模式 :
none
host
bridge
container
network-name
查看默认的网络模式有三个
root@docker01:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
39c48a5e6c06   bridge    bridge    local
b72c03e4a37d   host      host      local
1325c077d0cb   none      null      local
网络模式指定
默认新建的容器使用 Bridge 模式,创建容器时, docker run 命令使用以下选项指定网络模式
格式
docker run --network <mode>
docker run --net=<mode>
<mode>: 可是以下值
none
bridge
host
container:<容器名或容器ID>
<自定义网络名称>
Bridge 网络模式架构
本模式是 docker 的默认模式,即不指定任何模式就是 bridge 模式,也是使用比较多的模式,此模式创建
的容器会为每一个容器分配自己的网络 IP 等信息,并将容器连接到一个虚拟网桥与外界通信
可以和外部网络之间进行通信,通过 SNAT 访问外网,使用 DNAT 可以让容器被外部主机访问,所以此模
式也称为 NAT 模式
此模式宿主机需要启动 ip_forward 功能
bridge 网络模式特点
  • 网络资源隔离: 不同宿主机的容器无法直接通信,各自使用独立网络
  • 无需手动配置: 容器默认自动获取172.17.0.0/16IP地址,此地址可以修改
  • 可访问外网: 利用宿主机的物理网卡,SNAT连接外网
  • 外部主机无法直接访问容器: 可以通过配置DNAT接受外网的访问
  • 低性能较低: 因为可通过NAT,网络转换带来更的损耗
  • 端口管理繁琐: 每个容器必须手动指定唯一的端口,容器产生端口冲容

查看bridge模式信息

root@docker01:~# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "39c48a5e6c0629087e10224cfcbee314b2ebd73d9c02aab05819766784b4c96b",
        "Created": "2026-08-22T02:30:16.257253917Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {},
        "Containers": {},
        "Status": {
            "IPAM": {
                "Subnets": {
                    "172.17.0.0/16": {
                        "IPsInUse": 3,
                        "DynamicIPsAvailable": 65533
                    }
                }
            }
        }
    }
]
安装 docker . 默认启用 ip_forward
root@docker01:~# cat /proc/sys/net/ipv4/ip_forward
1

修改Bridge网络配置方法

#vim /etc/docker/daemon.json
{
  "hosts": ["tcp://0.0.0.0:2375", "fd://"],
  "bip": "192.168.100.100/24",         #分配docker0网卡的IP,24是容器IP的netmask
  "fixed-cidr": "192.168.100.128/26", #分配容器IP范围,26不是容器IP的子网掩码,只表示地址
范围
  "fixed-cidr-v6": "2001:db8::/64",
  "mtu": 1500,
  "default-gateway": "192.168.100.200",  #网关必须和bip在同一个网段
  "default-gateway-v6": "2001:db8:abcd::89",
  "dns": [ "1.1.1.1", "8.8.8.8"]
}
Host模式
如果指定 host 模式启动的容器,那么新创建的容器不会创建自己的虚拟网卡,而是直接使用宿主机的网 卡和IP 地址,因此在容器里面查看到的 IP 信息就是宿主机的信息,访问容器的时候直接使用宿主机 IP+ 容器端口即可,不过容器内除网络以外的其它资源,如: 文件系统、系统进程等仍然和宿主机保持隔离 此模式由于直接使用宿主机的网络无需转换,网络性能最高,但是各容器内使用的端口不能相同,适用于运行容器端口比较固定的业务
Host网络模式特点:
  • 使用参数 --network host 指定
  • 共享宿主机网络
  • 各容器网络无隔离
  • 网络性能无损耗
  • 网络故障排除相对简单
  • 容易产生端口冲突
  • 网络资源无法分别统计
  • 不支持端口映射
None模式
在使用 none 模式后, Docker 容器不会进行任何网络配置,没有网卡、没有 IP 也没有路由,因此默认无
法与外界通信,需要手动添加网卡配置 IP 等,所以极少使用
none 模式特点
使用参数 --network none 指定
默认无网络功能,无法和外部通信
无法实现端口映射
适用于测试环境
Container 模式
使用此模式创建的容器需指定和一个已经存在的容器共享一个网络,而不是和宿主机共享网络,新创建 的容器不会创建自己的网卡也不会配置自己的IP ,而是和一个被指定的已经存在的容器共享 IP 和端口范 围,因此这个容器的端口不能和被指定容器的端口冲突,除了网络之外的文件系统、进程信息等仍然保 持相互隔离,两个容器的进程可以通过lo 网卡进行通信
Container 模式特点
  • 使用参数 –-network container:名称或ID 指定
  • 与宿主机网络空间隔离
  • 容器间共享网络空间,直接使用对方的网络
  • 第一个容器的网络可能是bridge,或none,或者host,而第二个容器模式依赖于第一个容器,它们共享网络
  • 如果第一个容器停止,将导致无法创建第二个容器
  • 第二个容器可以直接使用127.0.0.1访问第一个容器
  • 适合频繁的容器间的网络通信
  • 默认不支持端口映射,较少使用
自定义网络模式
除了以上的网络模式,也可以自定义网络,使用自定义的网段地址,网关等信息
可以使用自定义网络模式 , 实现不同集群应用的独立网络管理 , 而互不影响 , 而且在网一个网络内 , 可以直接利用容器名相互访问,非常便利
注意 : 自定义网络内的容器可以直接通过容器名进行相互的访问 , 而无需使用 --link
docker network create -d <mode> --subnet <CIDR> --gateway <网关> <自定义网络名称>
#注意mode不支持host和none,默认是bridge模式
-d <mode> 可省略,默认为bridge,支持bridge,overlay,macvlan等driver模式

查看自定义网络
docker network inspect <自定义网络名称或网络ID>


引用自定义网络
docker run --network <自定义网络名称> <镜像名称>
docker run --net <自定义网络名称> --ip <指定静态IP> <镜像名称>
#注意:静态IP只支持自定义网络模型
#指定自定义网络中的容器的别名
docker run --network <自定义网络名称> --network-alias list <镜像名称>

删除自定义网络

doccker network rm <自定义网络名称或网络ID
内置的三个网络无法删除 none host bridge






root@docker01:~# docker network create  -d bridge --subnet 172.27.0.0/16 --gateway 172.27.0.1 test-net

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether e2:82:c7:70:d6:75 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::e082:c7ff:fe70:d675/64 scope link
       valid_lft forever preferred_lft forever
62: br-6c38466c3d81: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether ba:f0:6a:d9:f7:75 brd ff:ff:ff:ff:ff:ff
    inet 172.27.0.1/16 brd 172.27.255.255 scope global br-6c38466c3d81
       valid_lft forever preferred_lft forever
root@docker01:~#
br-6c38466c3d81 自定义网络的新虚拟网卡

root@docker01:~# brctl show
bridge name	bridge id		STP enabled	interfaces
br-6c38466c3d81		8000.baf06ad9f775	no 新加的一个网桥
docker0		8000.e282c770d675	no

在同一个自定义网络中两个容器通过IP和容器名都可以通信。

结论 : 自定义网络中的容器之间可以直接利用容器名进行通信
实战案例:使用自定义网络实现redis cluster
1、创建自定义网络
root@docker01:~# docker network create  -d bridge --subnet 172.27.0.0/16 --gateway 172.27.0.1 test-net

2、创建6个redis容器所需的配置目及配置文件

for port in {1..6};do 
 mkdir -p /data/redis/node-${port}/conf
 cat >> /data/redis/node-${port}/conf/redis.conf << EOF
port 6379
bind 0.0.0.0
masterauth work123
requirepass work123
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.27.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

3、创建6个容器
root@docker01:~# for port in {1..6};do
> docker run -p 637${port}:6379 -p 1667${port}:16379 --name redis-${port} \
> -v /data/redis/node-${port}/data:/data \
> -v /data/redis/node-${port}/conf:/etc/redis \
> -d --net test-net --ip 172.27.0.1${port} redis:8.8-alpine redis-server /etc/redis/redis.conf
> done

4、创建redis cluster

登陆到一个redis容器节点进行cluster配置

root@docker01:~# docker exec -it redis-3 sh
/data # redis-cli -a work123 --cluster create 172.27.0.11:6379 172.27.0.12:6379 172.27.0.13:6379 172.27.0.14:6379 172.27.0.15:6379 172.27.0.16:6379 --
cluster-replicas 1

5、测试集群访问
root@docker01:~# docker exec -it redis-5 sh
/data # redis-cli -a work122 -c
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
AUTH failed: WRONGPASS invalid username-password pair or user is disabled.
127.0.0.1:6379> exit
/data # redis-cli -a work123 -c
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:294

127.0.0.1:6379> cluster nodes
5b912482f713898648fdbafaaa6ac3ad6960c356 172.27.0.14:6379@16379 slave a69d8482f24a4340ebfd7a97fbec19c1e761c8f9 0 1787410632413 3 connected
9ddfd4111f64bcf978e29f23f3f83118a94d1f18 172.27.0.12:6379@16379 master - 0 1787410631000 2 connected 5461-10922
1c2099ff82953880846ed11a58249a63fa402ddd 172.27.0.15:6379@16379 myself,slave 31e235870bb06b79bc700b44e2b859c09653e292 0 0 1 connected
31e235870bb06b79bc700b44e2b859c09653e292 172.27.0.11:6379@16379 master - 0 1787410632110 1 connected 0-5460
a69d8482f24a4340ebfd7a97fbec19c1e761c8f9 172.27.0.13:6379@16379 master - 0 1787410632513 3 connected 10923-16383
4bfa5fd0cdb8e5e6efb64900d9efd397d1cf629f 172.27.0.16:6379@16379 slave 9ddfd4111f64bcf978e29f23f
127.0.0.1:6379> set name kaikai
-> Redirected to slot [5798] located at 172.27.0.12:6379
OK
172.27.0.12:6379> set age 18
-> Redirected to slot [741] located at 172.27.0.11:6379
OK
172.27.0.11:6379> set study docker
-> Redirected to slot [8312] located at 172.27.0.12:6379
OK
172.27.0.12:6379> get name
"kaikai"
172.27.0.12:6379> get age
-> Redirected to slot [741] located at 172.27.0.11:6379
"18"
172.27.0.11:6379> get study
-> Redirected to slot [8312] located at 172.27.0.12:6379
"docker"
6、模拟一个节点故障
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 9ddfd4111f64bcf978e29f23f3f83118a94d1f18 127.0.0.1:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4bfa5fd0cdb8e5e6efb64900d9efd397d1cf629f 172.27.0.16:6379
   slots: (0 slots) slave
   replicates 9ddfd4111f64bcf978e29f23f3f83118a94d1f18
M: a69d8482f24a4340ebfd7a97fbec19c1e761c8f9 172.27.0.13:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: 1c2099ff82953880846ed11a58249a63fa402ddd 172.27.0.15:6379
   slots:[0-5460] (5461 slots) master
S: 5b912482f713898648fdbafaaa6ac3ad6960c356 172.27.0.14:6379
   slots: (0 slots) slave
   replicates a69d8482f24a4340ebfd7a97fbec19c1e761c8f9
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered. 
15这时成了主,现在将11启动起来再看
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 9ddfd4111f64bcf978e29f23f3f83118a94d1f18 127.0.0.1:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4bfa5fd0cdb8e5e6efb64900d9efd397d1cf629f 172.27.0.16:6379
   slots: (0 slots) slave
   replicates 9ddfd4111f64bcf978e29f23f3f83118a94d1f18
M: a69d8482f24a4340ebfd7a97fbec19c1e761c8f9 172.27.0.13:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: 1c2099ff82953880846ed11a58249a63fa402ddd 172.27.0.15:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 31e235870bb06b79bc700b44e2b859c09653e292 172.27.0.11:6379
   slots: (0 slots) slave
   replicates 1c2099ff82953880846ed11a58249a63fa402ddd
S: 5b912482f713898648fdbafaaa6ac3ad6960c356 172.27.0.14:6379
   slots: (0 slots) slave
   replicates a69d8482f24a4340ebfd7a97fbec19c1e761c8f9
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered. 11启动后重新加入集群成了slave
Docker Compose 容器单机编排
当在宿主机启动较多的容器时候,如果仍用手动执行 docker run 启动每个容器不仅很麻烦而且因为有依 赖关系等导致容易出错,而且无法不方便分类管理 可以使用 docker 单机多容器的编排工具 docker-compose 解决 docker-compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排,可以用它 同时管理多个容器 比如 : 可以解决容器之间的依赖关系,就像启动一个 nginx 前端服务的时候会调用后端的 tomcat ,那就 得先启动 tomcat ,但是启动 tomcat 容器还需要依赖 MySQL 数据库,那就还得先启动 MySQL docker compose 可以用来解决这样的嵌套依赖关系,并且可以替代 docker 命令对容器进行创建、启动和停止 等手工的操作 因此如果说 docker 命令就像 linux 的命令, docker-compose.yml 文件就像 shell 脚本,可以自动的执行容 器批量操作,从而实现自动化的容器管理,也或者说 docker 命令相当于 ansible 命令,那么 docker compose 文件,就相当于 ansible-playbook yaml 文件
安装 Docker Compose

使用apt及dnf yum方式安装的docker会将docker compose也安装上,二进制安装的docker需要单独安装docker compose

docker compose命令

docker compose --help
Define and run multi-container applications with Docker.
Usage:
 docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]
 docker compose -h|--help
#选项说明:  
-f,–file FILE #指定Compose 模板文件,默认为docker-compose.yml|yaml
-p,–project-name NAME #指定项目名称,默认将使用当前所在目录名称作为项目名。
--verbose   #显示更多输出信息
--log-level LEVEL    #定义日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL) 
--no-ansi #不显示ANSI 控制字符
-v, --version #显示版本
#以下为命令选项,需要在docker-compose.yml|yaml 文件所在在目录里执行
config  -q #查看当前配置,没有错误不输出任何信息
up #创建并启动容器
build  #构建镜像
bundle #从当前docker compose 文件生成一个以<当前目录>为名称的json格式的Docker Bundle 备
份文件
create #创建服务
down #停止和删除所有容器、网络、镜像和卷
events #从容器接收实时事件,可以指定json 日志格式
exec #进入指定容器进行操作
help #显示帮助细信息
images #显示镜像信息
kill #强制终止运行中的容器
logs #查看容器的日志
pause #暂停服务
port #查看端口
ps #列出容器
pull #重新拉取镜像,镜像发生变化后,需要重新拉取镜像
push #上传镜像
restart #重启服务
rm #删除已经停止的服务
run #一次性运行容器
scale  #设置指定服务运行的容器个数,新版已废弃
start #启动服务
stop #停止服务
top #显示容器运行状态
unpause #取消暂定
docker compose 启动单个容器
mkdir -p /data/docker-compose
cd /data/docker-compose

cat docker-compose
services:
  service-nginx-web:
    image: 192.168.10.22/test/nginx:v1.24.0
    container_name: nginx-web
    expose:
      - 80
      - 443
    ports:
      - "80:80"
      - "443:443"


docker compose config  查看配置格式检查有没有借误
docker compose config -q


root@docker01:/data/docker-compose# docker compose up -d
[+] up 2/2
 ✔ Network docker-compose_default Created                                                                                                                         0.0s
 ✔ Container nginx-web            Started                                                                                                                         0.2s
root@docker01:/data/docker-compose# curl 127.0.0.1:85
<h1>Welcome to nginx!</h1>


使用终端连入到docker compose 管理的容器
root@docker01:/data/docker-compose# docker compose ps
NAME        IMAGE                              COMMAND                  SERVICE             CREATED         STATUS         PORTS
nginx-web   192.168.10.22/test/nginx:v1.24.0   "/docker-entrypoint.…"   service-nginx-web   2 minutes ago   Up 2 minutes   0.0.0.0:443->443/tcp, [::]:443->443/tcp, 0.0.0.0:85->80/tcp, [::]:85->80/tcp
oot@docker01:/data/docker-compose# docker compose exec service-nginx-web bash #exec后面要跟SERVICE 的名称,这里的名称是service-nginx-web
root@e8eabaf6cf33:/# ls
bin  boot  dev	docker-entrypoint.d  docker-entry

root@docker01:/data/docker-compose# docker compose images
CONTAINER           REPOSITORY                 TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
nginx-web           192.168.10.22/test/nginx   v1.24.0             linux/amd64         8f029c543423        63.3MB              3 days ago
root@docker01:/data/docker-compose# docker compose ps
NAME        IMAGE                              COMMAND                  SERVICE             CREATED         STATUS         PORTS
nginx-web   192.168.10.22/test/nginx:v1.24.0   "/docker-entrypoint.…"   service-nginx-web   8 minutes ago   Up 8 minutes   0.0.0.0:443->443/tcp, [::]:443->443/tcp, 0.0.0.0:85->80/tcp, [::]:85->80/tcp
root@docker01:/data/docker-compose# docker compose inspect nginx-web
root@docker01:/data/docker-compose# docker compose logs -f
nginx-web  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx-web  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx-web  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
nginx-web  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx-web  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
nginx-web  | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
nginx-web  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx-web  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx-web  | /docker-entrypoint.sh: Configuration complete; ready for start up

root@docker01:/data/docker-compose# docker compose stop
[+] stop 1/1
 ✔ Container nginx-web Stopped                                                                                                                                    0.2s
root@docker01:/data/docker-compose# docker compose ps -a
NAME        IMAGE                              COMMAND                  SERVICE             CREATED          STATUS                      PORTS
nginx-web   192.168.10.22/test/nginx:v1.24.0   "/docker-entrypoint.…"   service-nginx-web   11 minutes ago   Exited (0) 31 seconds ago
root@docker01:/data/docker-compose# docker compose start
[+] start 1/1
 ✔ Container nginx-web Started
root@docker01:/data/docker-compose# docker compose start
[+] start 1/1
 ✔ Container nginx-web Started                                                                                                                                    0.2s
root@docker01:/data/docker-compose# docker compose ps -a
NAME        IMAGE                              COMMAND                  SERVICE             CREATED          STATUS          PORTS
nginx-web   192.168.10.22/test/nginx:v1.24.0   "/docker-entrypoint.…"   service-nginx-web   12 minutes ago   Up 35 seconds   0.0.0.0:443->443/tcp, [::]:443->443/tcp, 0.0.0.0:85->80/tcp, [::]:85->80/tcp

#只删除停止的容器
docker compose rm
#停止并删除容器及镜像
docker-compose down

root@docker01:/data/docker-compose# docker compose down
[+] down 2/2
 ✔ Container nginx-web            Removed                                                                                                                         0.2s
 ✔ Network docker-compose_default Removed                                                                                                                         0.1s
root@docker01:/data/docker-compose# docker compose ps -a
NAME      IMAGE     COMMAND   SERVICE   CREATED   STATUS    PORTS
root@docker01:/data/docker-compose# docker compose images
CONTAINER           REPOSITORY          TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
root@docker01:/data/docker-compose#
Docker compose启动多个容器
root@docker01:/data/docker-compose# docker tag tomcat:latest 192.168.10.22/test/tomcat:v1.88.0
root@docker01:/data/docker-compose# docker push 192.168.10.22/test/tomcat:v1.88.0
The push refers to repository [192.168.10.22/test/tomcat]
0926a8eb0e60: Pushed
c79111691f56: Pushed
先将一个镜像改一下tag上传到harbor仓库

root@docker01:/data/docker-compose# cat docker-compose.yml
services:
  service-nginx-web:
    image: 192.168.10.22/test/nginx:v1.24.0
    container_name: nginx-web
    volumes:
      - /data/nginx:/apps/nginx/html/

    expose:
      - 80
      - 443
    ports:
      - "80:80"
      - "443:443"

  service-tomcat-app1:
    image: 192.168.10.22/test/tomcat:v1.88.0
    container_name: tomcat-app1
    expose:
      - 8080
    ports:
      - "8081:8080"

  service-tomcat-app2:
    image: 192.168.10.22/test/tomcat:v1.88.0
    container_name: tomcat-app2
    expose:
      - 8080
    ports:
      - "8082:8080"

docker compose up -d

docker compose stop
root@docker01:/data/docker-compose# cd
root@docker01:~# docker compose -f /data/docker-compose/docker-compose.yml start
[+] start 3/3
 ✔ Container nginx-web   Started                                                                                                                                  0.3s
 ✔ Container tomcat-app1 Started                                                                                                                                  0.4s
 ✔ Container tomcat-app2 Started                                                                                                                                  0.3s
root@docker01:~# ls
alpine_3.24.1.tar  backup  soft
扩容与缩容
临时扩容缩容

# 扩容:将 web 服务扩展到 3 个实例
docker compose up -d --scale web=3

# 缩容:将 web 服务缩减到 1 个实例
docker compose up -d --scale web=1

注意配置中不能固定container name、不能固定端口映射 这两个需要随机生成才可以

在配置文件中修改重新启动

services:
  web:
    image: nginx:alpine
    deploy:
      replicas: 3 这里
实战项目jumpserver安装部署

由于官方已经找到docker-compose.yml配置,现在以官方单机安装操作,依赖docker。

从官网注册登陆后下载官网安装包
1、解压后
root@docker01:/data/jumpserver-ce-v4.10.19-x86_64# ls
compose  config-example.txt  config_init  jmsctl.sh  LICENSE  locale  README.md  scripts  static.env  utils

2、./jmsctl.sh install 即可

3、安装完毕后我们查一下docker相关信息

可以看到启动了三个容器,用自定义网络jms_net

Docker仓库管理
Docker 仓库,类似于 yum 仓库,是用来保存镜像的仓库。
为了方便的管理和使用 docker 镜像 , 可以将镜像集中保存至 Docker 仓库中,将制作好的镜像 push 到仓库
集中保存,在需要镜像时,从仓库中 pull 镜像即可。
Docker 仓库分为公有云仓库和私有云仓库
公有云仓库 : 由互联网公司对外公开的仓库
官方
阿里云等第三方仓库
私有云仓库 : 组织内部搭建的仓库,一般只为组织内部使用,常使用下面软件搭建仓库
docker registory
docker harbo
Harbor介绍
Harbor ( 港口 , 海港 ) 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器,由 VMware 开源,其通
过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源 Docker Distribution
作为一个企业级私有 Registry 服务器, Harbor 提供了更好的性能和安全。提升用户使用 Registry 构建和
运行环境传输镜像的效率。 Harbor 支持安装在多个 Registry 节点的镜像资源复制,镜像全部保存在私有
Registry 中, 确保数据和知识产权在公司内部网络中管控,另外, Harbor 也提供了高级的安全特性,诸
如用户管理,访问控制和活动审计等
vmware 官方开源服务 : https://vmware.github.io/
harbor 官方 github 地址 : https://github.com/vmware/harbor
harbor 官方网址 : https://goharbor.io/
harbor 官方文档 : https://goharbor.io/docs/
github 文档 : https://github.com/goharbor/harbor/tree/master/docs
Harbor 功能
  • 基于角色的访问控制: 用户与Docker镜像仓库通过项目进行组织管理,一个用户可以对多个镜像仓库在同一命名空间(project)里有不同的权限
  • 镜像复制: 镜像可在多个Registry实例中复制(同步)。尤其适合于负载均衡,高可用,混合云和多云的场景
  • 图形化用户界面: 用户可以通过浏览器来浏览,检索当前Docker镜像仓库,管理项目和命名空间
  • AD/LDAP : Harbor可以集成企业内部已有的AD/LDAP,用于鉴权认证管理
  • 审计管理: 所有针对镜像仓库的操作都可以被记录追溯,用于审计管理
  • 国际化: 已拥有英文、中文、德文、日文和俄文的本地化版本。更多的语言将会添加进来
  • RESTful API: 提供给管理员对于Harbor更多的操控, 使得与其它管理软件集成变得更容易
  • 部署简单: 提供在线和离线两种安装工具, 也可以安装到vSphere平台(OVA方式)虚拟设备

Harbor安装部署
1、下载离线安装包
root@docker01:~/soft# wget https://github.com/goharbor/harbor/releases/harbor-offline-installer-v2.15.2.tgz
2、解压缩,配置harbor.yml
hostname: 192.168.10.21
harbor_admin_password: work123
data_volume: /data/harbor

没有证书先关闭https相关配置,证书引用也注视掉

3、执行安装脚本
root@docker01:/usr/local/harbor# ./install.sh -h

Note: Please set hostname and other necessary attributes in harbor.yml first. DO NOT use localhost or 127.0.0.1 for hostname, because Harbor needs to be accessed by external clients.
Please set --with-trivy if needs enable Trivy in Harbor.
Please do NOT set --with-chartmuseum, as chartmusuem has been deprecated and removed.
Please do NOT set --with-notary, as notary has been deprecated and removed.


创建相关项目上传镜像


打tag上传镜像
root@docker01:~# docker tag alpine:3.24.1 192.168.10.21/test/alpine:3.24.1
root@docker01:~# docker push 192.168.10.21/test/alpine:3.24.1
The push refers to repository [192.168.10.21/test/alpine]
55afa1ecc21d: Pushed
3.24.1: digest: sha256:79ff19e9084a00eece421b2523fb93e22d730e2c0e525905de047e848e56d95f size: 1022

i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
         sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b -> sha256:79ff19e9084a00eece421b2523fb93e22d730e2c0e525905de047e848e56d95f
root@docker01:~# docker images

下载镜像
root@docker02:~# docker pull 192.168.10.21/test/alpine:3.24.1
3.24.1: Pulling from test/alpine
55afa1ecc21d: Pull complete
Digest: sha256:79ff19e9084a00eece421b2523fb93e22d730e2c0e525905de047e848e56d95f
Status: Downloaded newer image for 192.168.10.21/test/alpine:3.24.1
192.168.10.21/test/alpine:3.24.1
root@docker02:~# ls
root@docker02:~# docker images
                                                                                                                                                  i Info →   U  In Use
IMAGE                              ID             DISK USAGE   CONTENT SIZE   EXTRA
192.168.10.21/test/alpine:3.24.1   79ff19e9084a       12.9MB         3.85MB
Harbor高可用

在另一台宿主机之上安装harbor方法与第一台相同。

两台harbor主机上都进行目标配置互相让其进行复制。

创建复制规则两个harbor上可以互相同步镜像到对方。

Docker资源限制
默认情况下,容器没有资源的使用限制,可以使用主机内核调度程序允许的尽可能多的资源
Docker 提供了控制容器使用资源的方法 , 可以限制容器使用多少内存或 CPU 等, 在 docker run 命令的 运行时配置标志实现资源限制功能。
OOM
对于 Linux 主机,如果没有足够的内存来执行其他重要的系统任务,将会抛出 OOM (Out of Memory Exception,内存溢出、内存泄漏、内存异常 ) ,随后系统会开始杀死进程以释放内存, 凡是运行在宿主机的进程都有可能被 kill ,包括 Dockerd 和其它的应用程序, 如果重要的系统进程被 Kill ,会导致和该进程相关的服务全部宕机。通常越消耗内存比较大的应用越容易被kill ,比如 : MySQL 数据库, Java 程序等
产生 OOM 异常时, Dockerd 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便 它比系统上的其他进程更不可能被杀死但是每个容器 的 OOM 优先级并未调整, 这使得单个容器被杀死 的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动 设置-- oom -score-adj 为极端负数,或通过在容器上设置 -- oom-kill-disable 来绕过这些安全措施
OOM优先级机制
 linux会为每个进程计算一个分数,最终将分数最高的kill
/proc/PID/oom_score_adj 
#范围为 -1000 到 1000,值越高容易被宿主机 kill掉,如果将该值设置为 -1000 ,则进程永远不会被
宿主机 kernel kill 
/proc/PID/oom_adj 
#该设置参数是为了和旧版本的 Linux 内核兼容的旧接口文件,范围为 -17 到+15 ,会被线性映射到
oom_score_adj,取值越高越容易被干掉,如果是 -17 , 则表示不能被 kill, root可读写,
/proc/PID/oom_score 
#这个值是系统综合进程的内存消耗量,只读文件,取值范围0 –- 1000,0代表never kill,1000代表
aways kill,值越大,进程被选中的概率越大。
#oom_score = 占用消耗内存/总内存 *1000
#内存消耗=常驻内存RSS + 进程页面 +交换内存
#总内存=总的物理内存 +交换分区
#消耗内存越多得分越高,容易被宿主机 kernel 强制杀死
#当内存紧张的时候,内核通过 oom = oom_score + oom_score_adj 计算出分数最高的进程,向其发送
关闭信号

# 查看 nginx 的 PID
pidof nginx

# 或者随便找一个正在运行的进程
ps aux | grep nginx

root@docker01:/proc# pidof dockerd
100269
root@docker01:/proc# cd 100269/
root@docker01:/proc/100269# cat oom_score_adj
-500 #现在我的主机中的dockerd的分数是-500
容器内存限制
  • Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。
  • Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不 够用了
选项 描述
-m, --memory= 容器可以使用的最大物理内存量 RSS,硬限制,此选项最小允许值为 4m (4 MB),此项较常用
--memory-swap 允许此容器交换到磁盘的内存量。必须先用 -m 对内存限制才可以使用,详细说明如下
--memory-swappiness 设置容器使用交换分区的倾向性,值越高表示越倾向于使用 swap 分区,范围为 0-100,0 为能不用就不用,100 为能用就用,N 表示内存使用率达到 N% 时,就会使用 swap 空间
--memory-reservation 允许指定小于 --memory 的软限制,当 Docker 检测到主机上的争用或内存不足时会激活该限制,如果使 --memory-reservation,则必须将其设置为低于 --memory 才能使其优先生效。因为它是软限制,所以不能保证容器不超过限制
--kernel-memory 容器可以使用的最大内核内存量,最小为 4m,由于内核内存与用户空间内存隔离,因此无法与用户空间内存直接交换,因此内核内存不足的容器可能会阻塞宿主机资源,这会对主机和其他容器或者其他服务进程产生影响,因此不建议设置内核内存大小
--oom-kill-disable

新内核不再支持,默认情况下,如果发生内存不足 (OOM) 错误,则内核将终止容器中的进程。要更改此行为,请使用该 --oom-kill-disable 选项。建议仅在设置了该 -m/--memory 选项的容器上禁用 OOM。如果 -m 未设置该标志,则主机可能会用完内存,内核可能需要终止主机系统的进程以释放内存

docker run  -e MYSQL_ROOT_PASSWORD=XXXX -it --rm -m 1g mysql:8.0
docker run 命令可以使用--memory-swap 选项控制swap的使用

--memory-swap #只有在设置了 --memory 后才会有意义。使用 Swap,可以让容器将超出限制部分的内存
置换到磁盘上,WARNING: 经常将内存交换到磁盘的应用程序会降低性能

-memory-swap #值为正数, 那么--memory 和--memory-swap 都必须要设置,--memory-swap 表示
你能使用的内存和 swap 分区大小的总和,例如:   --memory=300m, --memory-swap=1g, 那么该容
器能够使用 300m 物理内存和 700m swap,即--memory 是实际物理内存大小值不变,而 swap 的实际大
小计算方式为(--memory-swap)-(--memory)=容器可用 swap
--memory-swap #如果设置为 0,则忽略该设置,并将该值视为未设置,即未设置交换分区
--memory-swap #如果等于--memory 的值,并且--memory 设置为正整数,容器无权访问 swap 
-memory-swap #如果未设置,如果宿主机开启了 swap,则实际容器的swap 值最大为 2x( --
memory),即两倍于物理内存大小,例如,如果--memory="300m"与--memory-swap没有设置,该容器可
以使用300m总的内存和600m交撒空间,但是并不准确(在容器中使用free 命令所看到的 swap 空间并不精
确,毕竟每个容器都可以看到具体大小,宿主机的 swap 是有上限的,而且不是所有容器看到的累计大小)
--memory-swap #如果设置为-1,如果宿主机开启了 swap,则容器可以使用主机上 swap 的最大空间
--memory-swap --memory 功能
正数 S 正数 M 容器可用内存总空间为 S,其中 RAM 为 M,Swap 为 S-M。若 S=M,则无可用 Swap 资源
0 正数 M 相当于未设置 Swap (unset)
unset (未设置) 正数 M 若主机 (Docker Host) 启用了 Swap,则容器的可用 Swap 为 2*M
-1 正数 M 若主机 (Docker Host) 启用了 Swap,则容器可使用最大至主机上所有 Swap 空间
  • 默认行为(unset):如果不显式设置 --memory-swap,Docker 默认会将 Swap 设置为与内存限制相同的值(即总限制变为 2 * Memory)。例如 -m 512m,实际可用总空间是 1GB(512m 内存 + 512m Swap)。
  • 禁用 Swap:如果你想限制容器只能使用物理内存,完全不让它用 Swap,需要将 --memory-swap 设置为与 --memory 相同的值
  • 假如一个容器未做内存使用限制,则该容器可以利用到系统内存最大空间,默认创建的容器没有做内存 资源限制
启动一个容器没有限制内存

root@docker01:~# docker run --name c1 -it --rm alexeiled/stress-ng:latest --vm 2

指定内存最大值

root@docker01:~# docker run --name test --it --rm -m 300m alexeiled/stress-ng:latest --vm 2

容器cpu限制
一个宿主机,有几十个核心的 CPU ,但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的 任务,多进程共用一个 CPU 的核心为可压缩资源,即一个核心的 CPU 可以通过调度而运行多个进程, 但是同一个单位时间内只能有一个进程在 CPU 上运行,那么这么多的进程怎么在 CPU 上执行和调度的 呢?
Linux kernel 进程的调度基于 CFS(Completely Fair Scheduler) ,完全公平调度
服务器资源密集型
  • CPU 密集型的场景: 计算密集型任务的特点是要进行大量的计算,消耗CPU 资源,比如计算圆周率、数据处理、对视频进行高清解码等等,全靠CPU 的运算能力。
  • IO 密集型的场景: 涉及到网络、磁盘IO 的任务都是IO 密集型任务,这类任务的特点是 CPU 消耗很 少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度),比 Web 应用,高并发,数据量大的动态网站来说,数据库应该为IO 密集型
CFS 原理
cfs 定义了进程调度的新模型,它给 cfs_rq cfs run queue )中的每一个进程安排一个虚拟时钟
vruntime 。如果一个进程得以执行,随着时间的增长,其 vruntime 将不断增大。没有得到执行的进程 vruntime不变 , 而调度器总是选择 vruntime 跑得最慢的那个进程来执行。这就是所谓的 完全公平 。为 了区别不同优先级的进程,优先级高的进程vruntime 增长得慢,以至于它可能得到更多的运行机会。 CFS的意义在于, 在一个混杂着大量计算型进程和 IO 交互进程的系统中, CFS 调度器相对其它调度器在 对待IO 交互进程要更加友善和公平。
配置默认的 CFS 调度程序
默认情况下,每个容器对主机的 CPU 周期的访问都是不受限制的。可以设置各种约束,以限制给定容器 对主机CPU 周期的访问。大多数用户使用并配置 默认的 CFS 调度程序 。在 Docker 1.13 及更高版本中,还 可以配置 realtime scheduler 。 CFS是用于常规 Linux 进程的 Linux 内核 CPU 调度程序。通过几个运行时标志 , 可以配置对容器拥有的 CPU 资源的访问量。使用这些设置时,Docker 会在主机上修改容器 cgroup 的设置。
选项 描述
--cpus= 指定一个容器可以使用多少个可用的CPU核心资源。例如,如果主机有两个CPU,如果设置了 --cpus="1.5",则可以保证容器最多使用1.5个的CPU。如果是4核CPU,那么还可以是4核心上每核用一点,但是总计是1.5核心的CPU。这相当于设置 --cpu-period="100000"--cpu-quota="150000"。此设置可在 Docker 1.13 及更高版本中可用,目的是替代 --cpu-period--cpu-quota 两个参数,从而使配置更简单,但是最大不能超出宿主机的CPU总核心数(在操作系统看到的CPU超线程后的数值),此项较常用
--cpu-period= 过时选项,指定CPU CFS调度程序周期,必须与 --cpu-quota 一起使用。默认为100微秒。大多数用户不会更改默认设置。如果使用Docker 1.13或更高版本,请改用 --cpus
--cpu-quota= 过时选项,在容器上添加 CPU CFS 配额,计算方式为 cpu-quota / cpu-period 的结果值,docker1.13 及以上版本通常使用 --cpus 设置此值
--cpuset-cpus 用于指定容器运行的 CPU 编号,也就是所谓的CPU绑定。如果一个或多个CPU,则容器可以使用逗号分隔的列表或用连字符分隔的CPU范围。第一个CPU的编号为0。有效值可能是 0-3 (使用第一,第二,第三和第四CPU)或 1,3 (使用第二和第四CPU)
--cpu-shares 用于设置 cfs 中调度的相对最大比例权重,cpu-share 的值越高的容器,将会分得更多的时间片(宿主机多核 CPU 总数为 100%,假如容器 A 为1024,容器 B 为2048,那么容器 B 将最大是容器 A 的可用的 CPU 的两倍)。默认的时间片 1024,最大 262144。这是一个软限制。注意:进程数要多个CPU的核数才能看到效果,此值不能设置太小
root@docker01:~# docker run --name test -it --rm -m 300m alexeiled/stress-ng:latest --cpu 4
stress-ng: info:  [1] defaulting to a 86400 second (1 day, 0.00 secs) run per stressor
stress-ng: info:  [1] dispatching hogs: 4 cpu

绑定CPU

一般不绑在0号cpu上,因为0号cpu一般比较忙

root@docker01:~# docker run -it --rm --name c1 --cpus 1.5 --cpuset-cpus 2,4-5 alexeiled/stress-ng:latest --cpu 4
stress-ng: info:  [1] defaulting to a 86400 second (1 day, 0.00 secs) run per stressor
stress-ng: info:  [1] dispatching hogs: 4 cpu
参数 含义
-it 以交互模式运行,分配终端
--rm 容器退出后自动删除
--name c1 容器命名为 c1
--cpus 1.5 限制容器最多使用 1.5 个 CPU 核心(硬限制)
--cpuset-cpus 2,4-5 容器只能运行在宿主机的第 2、4、5 号 CPU 核心上(绑核)
alexeiled/stress-ng:latest 使用的压测镜像
--cpu 4 启动 4 个 CPU 压测进程

关键点

虽然 stress-ng 内部启动了 4 个压测进程,但由于 Docker 的 --cpus 1.5 限制,这 4 个进程总共只能使用 1.5 个 CPU 核心的算力。你可以打开另一个终端,用 docker stats c1 观察,CPU 使用率会被限制在 150% 左右,不会跑满 400%。

GPU限制
使用 NVIDIA GPU 先决条件
访问官方 NVIDIA 驱动程序页面下载并安装正确的驱动程序。一旦你有完成。
验证您的 GPU 是否正在运行且可访问
在启动容器以访问 GPU 资源时包含标志。指定要使用的 GPU 数量。 例如: --gpus
 docker run -it --rm --gpus all ubuntu nvidia-smi

 使用该选项指定 GPU。 例如: device

 docker run -it --rm --gpus device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a 
ubuntu nvidia-smi

公开该特定 GPU

 docker run -it --rm --gpus '"device=0,2"' ubuntu nvidia-smi

公开第一个和第三个 GPU。注意:NVIDIA GPU 只能由运行单个引擎的系统访问。

您可以手动设置功能。例如,在 Ubuntu 上,您可以运行 以后:

 docker run --gpus 'all,capabilities=utility' --rm ubuntu nvidia-smi

这将启用驱动程序功能,该功能将工具 utility``nvidia-smi 添加到容器中
功能以及其他配置可以通过环境变量在镜像中设置。
有关有效变量的更多信息,请参阅 nvidia-container-toolkit (英伟达容器工具包) 文档。 可以在
Dockerfile 中设置这些变量。
您还可以使用 CUDA 映像来自动设置这些变量。请参阅 官方 CUDA 映像 NGC 目录页面。

Specialized Configurations with Docker — NVIDIA Container Toolkit

cuda | NVIDIA NGC

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐