Ch08 Interview
# Interview
# 如何精简Docker镜像
精简Docker镜像尺寸的好处:
减少构建时间
减少磁盘使用量
减少下载时间
提高安全性,减少攻击面积
越小的镜像表示无用的程序越少,可以大大的减少被攻击的目标,从而提高了安全性
提高部署速度
虽然存储资源较为廉价,但是网络 IO 是有限的,在带宽有限的情况下,部署一个 1G 的镜像和 10M 的镜像带来的时间差距可能就是分钟级和秒级的差距。特别是在出现故障,服务被调度到其他节点时,这个时间尤为宝贵。
优化Docker镜像的方法:
优化基础镜像
优化基础镜像的方法就是选用合适的更小的基础镜像,常用的 Linux 系统镜像一般有 Ubuntu、CentOS、Alpine,其中Alpine更推荐使用。
Alpine一个基于musl libc和busybox、面向安全的轻量级Linux发行版。它本身的Docker镜像只有4~5M大小。各开发语言和框架都有基于alpine制作的基础镜像,在开发自己应用的镜像时,选择这些镜像作为基础镜像,可以大大减小镜像的体积。
例如,Java、Python、Node.js语言对应的基础镜像如下:
- Java(Spring Boot): - openjdk:8-jdk-alpine,openjdk:8-jre-alpine等
- Java(Tomcat) - tomcat:8.5-alpine等
- Nodejs - node:9-alpine, node:8-alpine等
- Python - python:3-alpine, python:2-alpine等
如果你的项目涉及到编译,比如python等涉及编译的项目,要注意,Alpine用的是muslc,因为它原本是用作嵌入式系统的,所以并没有glibc那么完整的C标准库。
另外如果你要在Alpine中跑一些脚本的话,那你要注意一些shell和在linux(Ubuntu、CentOS、Debian等)下的还是有所区别的,Alpine是基于busybox的,同样也是设计于嵌入式的,所以很多shell命令做了裁剪,并不具备Ubuntu、CentOS、Debian等系统中那么完整的功能。
除了Alpine这样的轻量级镜像之外,还推荐的一些镜像,如scratch、busybox、distroless等。
# scratch镜像
scratch是一个空镜像,只能用于构建其他镜像,比如你要运行一个包含所有依赖的二进制文件,如Golang程序,可以直接使用scratch作为基础镜像。
样例:
FROM scratch ARG ARCH ADD bin/pause-${ARCH} /pause ENTRYPOINT ["/pause"]
1
2
3
4# busybox镜像
如果你希望镜像里可以包含一些常用的Linux工具,busybox镜像是个不错选择,它集成了一百多个最常用Linux命令和工具的软件工具箱,镜像本身只有1.16M,非常便于构建小镜像。
# distroless镜像
distroless镜像,它仅包含您的应用程序及其运行时依赖项。它们不包含您希望在标准 Linux 发行版中找到的包管理器、shell或任何其他程序。
由于Distroless是原始操作系统的精简版本,不包含额外的程序。容器里并没有Shell!如果黑客入侵了我们的应用程序并获取了容器的访问权限,他也无法造成太大的损害。也就是说,程序越少则尺寸越小也越安全。不过,代价是调试更麻烦。
需要注意的是,我们不应该在生产环境中,将Shell附加到容器中进行调试,而应依靠正确的日志和监控。
样例:
FROM node:8 as build WORKDIR /app COPY package.json index.js ./ RUN npm install FROM gcr.io/distroless/nodejs COPY --from=build /app / EXPOSE 3000 CMD ["index.js"]
1
2
3
4
5
6
7
8
9
10
11
串联Dockerfile指令,保证层级尽量少
补充说明:
Docker镜像由很多镜像层(Layers)组成(最多127层),镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等技术。总的来说,Dockerfile中的每条指令都会创建一个镜像层,继而会增加整体镜像的尺寸。我们可以通过命令
docker history image_id
来查看每一层的大小。在定义Dockerfile时,如果太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&来实现),从而有效的减少Docker镜像的层级,因此,每一个 RUN 都要精心设计,确保安装构建之后,还要进行清理,这样才可以降低镜像体积,以及最大化的利用构建缓存。
去除不必要的内容
前面提到的用空镜像,或者裁剪过的小镜像来做基础镜像,其实就是一种去除不必要的依赖、库的一种形式。
除了以上的这种形式,还有必要去除的,就是Dockerfile构建过程中所产生的临时文件。比如,源码包、编译过程中产生的日志文件、添加的包管理仓库、包管理缓存,以及构建过程中安装的一些当时有用,过后没用的软件或工具。
如果可以,甚至建议不在容器中进行编译,如果二进制binary文件可以执行的话,在本地编译后,将binary文件copy到容器内。
除了上面的,还有一些不常更新的文件,比如web静态资源文件css、js以及图片、视频等资源,建议存储OSS或共享存储系统nfs、mfs等,这些文件不应该打包到镜像里面,而应该通过OSS调用或通过共享存储挂载。
对于不需要build进镜像的资源,可以使用
.dockerignore
文件进行指定要忽略的(无关的)文件或目录,如node_modules
。补充说明
如果你想基于别人的镜像来做优化的话,可以通过docker history命令来查看镜像的层级关系,然后做相应的优化,更好的工具推荐dive。
当然也可以用自动化的镜像瘦身工具docker-slim,它支持静态分析和动态分析,静态分析主要是通过分析镜像历史信息,获取生成镜像的dockerfile文件及相关的配置信息,而动态分析主要是通过ptrace、pevent、fanotify解析出镜像中必要的文件和文件依赖,将对应文件组织成新镜像来减小镜像体积。
另外还可以通过docker-squash来压缩镜像层级,但是要考虑实际情况,并不是压缩一定是好的。
复用镜像层
刚刚提到压缩不一定是好,为什么呢?
压缩的原理是将镜像导出,然后删除所有中间层,将镜像的当前状态保存为单一层,达到压缩层级的效果。
当你使用单一镜像或者少量镜像的时候可能没有太大问题,但是这样完全破坏了镜像的层级缓存功能。
我们知道docker镜像的每个层级会存一个hash计算后的目录,那么Dockerfile构建过程中如何利用缓存?
在镜像的构建过程中,Docker根据Dockerfile指定的顺序执行每个指令。在执行每条指令之前,Docker都会在缓存中查找是否已经存在可重用的镜像,如果有,就使用现存的镜像,不再重复创建。
而如果压缩为单一的层之后,缓存就失效了,不会命中缓存的层级,所以每次构建或者pull的时候,都是整个镜像构建或pull。
缓存命中除了和分层有关系,还和指令执行编排顺序有关系,首先看下缓存匹配遵循的基本规则:
- 从一个基础镜像开始(FROM指令指定),下一条指令将和该基础镜像的所有子镜像进行匹配,检查这些子镜像被创建时使用的指令是否和被检查的指令完全一样。如果不是,则缓存失效。
- 在大多数情况下,只需要简单地对比Dockerfile中的指令和子镜像。然而,有些指令需要更多的检查和解释。
- 对于ADD和COPY指令,镜像中对应文件的内容也会被检查,每个文件都会计算出一个校验值。这些文件的修改时间和最后访问时间不会被纳入校验的范围。在缓存的查找过程中,会将这些校验和和已存在镜像中的文件校验值进行对比。如果文件有任何改变,比如内容和元数据改变,则缓存失效。
- 除了ADD和COPY指令,缓存匹配过程不会查看临时容器中的文件来决定缓存是否匹配。例如,当执行完
RUN apt-get -y update
指令后,容器中一些文件被更新,但Docker不会检查这些文件。这种情况下,只有指令字符串本身被用来匹配缓存。 - 一旦缓存失效,所有后续的Dockerfile指令都将产生新的镜像,缓存不会被使用。
所以为什么和指令执行顺序和编排有关系,或者说我们在合并命令减少层级的时候不能一味的追求合并,而是需要合理的合并一些指令。
举个例子,比如我们用同一个基础镜像,分别编译nginx和php,那么nginx也需要pcre库依赖,php也需要,那我们是不是可以提取共同的依赖用一条RUN指令去执行,而不是每次构建都执行。
再或者最简单的,添加镜像仓库,安装基本的编译工具,比如gcc、autoconf、make、zlib等这些不常改动,但是常用的指令放在前面去执行,这样后面构建用到的所有镜像都不会再重新安装。
这样合理的利用层级缓存,不管是在jenkins中自动构建镜像,还是push到远程仓库、亦或是在部署pull的时候,都能够利用缓存,从而节省传输带宽和时间。
分阶段构建
Dockerfile中每条指令都会为镜像增加一个镜像层,并且你需要在移动到下一个镜像层之前清理不需要的组件。多阶段构建方法是官方打包镜像的最佳实践,它是将精简层数做到极致的方法。通俗点讲它是将打包镜像分成两个阶段
- 一个阶段用于开发,打包,该阶段包含构建应用程序所需的所有内容;
- 一个用于生产运行,该阶段只包含你的应用程序以及运行它所需的内容;
- 这被称为“建造者模式”。 使用多阶段构建肯定会降低镜像大小,但是瘦身的粒度和编程语言有关系,对编译型语言效果比较好,因为它去掉了编译环境中多余的依赖,直接使用编译后的二进制文件或jar包。而对于解释型语言效果就不那么明显了。
使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。
使用多阶段构建主要有三点不同:
- 第一行多了AS build, 为后面的COPY做准备。
- 第一阶段中没有了清理操作,因为第一阶段构建的镜像只有编译的目标文件(二进制文件或jar包)有用,其它的都无用 。
- 第二阶段直接从第一阶段拷贝目标文件。
通过,这样构建镜像,你会发现生成的镜像只有上面COPY 指令指定的内容,镜像大小只有2M。
样例:
# Compile FROM golang:1.9.0 AS build WORKDIR /go/src/v9.git...com/.../k8s-monitor COPY . . WORKDIR /go/src/v9.git...com/.../k8s-monitor RUN make build RUN mv k8s-monitor /root # Package # Use scratch image FROM scratch WORKDIR /root/ COPY --from=build /root . EXPOSE 8080 CMD ["/root/k8s-monitor"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16ref: https://juejin.cn/post/7013950004196802597#heading-8
# docker的工作原理是什么
docker是一个Client-Server结构的系统,docker守护进程运行在宿主机上,守护进程从客户端接受命令并管理运行在主机上的容器,容器是一个运行时环境,这就是我们说的集装箱。
# docker的组成包含哪几大部分
一个完整的docker有以下几个部分组成:
docker client,客户端,为用户提供一系列可执行命令,用户用这些命令实现跟 docker daemon 交互;
docker daemon,守护进程,一般在宿主主机后台运行,等待接收来自客户端的请求消息;
docker image,镜像,镜像run之后就生成为docker容器;
docker container,容器,一个系统级别的服务,拥有自己的ip和系统目录结构;运行容器前需要本地存在对
应的镜像,如果本地不存在该镜像则就去镜像仓库下载。
docker 使用客户端-服务器 (C/S) 架构模式,使用远程api来管理和创建docker容器。docker 容器通过 docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
# docker与传统虚拟机的区别什么?
- 传统虚拟机是需要安装整个操作系统的,然后再在上面安装业务应用,启动应用,通常需要几分钟去启动应用,而docker是直接使用镜像来运行业务容器的,其容器启动属于秒级别;
- Docker需要的资源更少,Docker在操作系统级别进行虚拟化,Docker容器和内核交互,几乎没有性能损耗,而虚拟机运行着整个操作系统,占用物理机的资源就比较多;
- Docker更轻量,Docker的架构可以共用一个内核与共享应用程序库,所占内存极小;同样的硬件环境,Docker运行的镜像数远多于虚拟机数量,对系统的利用率非常高;
- 与虚拟机相比,Docker隔离性更弱,Docker属于进程之间的隔离,虚拟机可实现系统级别隔离;
- Docker的安全性也更弱,Docker的租户root和宿主机root相同,一旦容器内的用户从普通用户权限提升为root权限,它就直接具备了宿主机的root权限,进而可进行无限制的操作。虚拟机租户root权限和宿主机的root虚拟机权限是分离的,并且虚拟机利用如Intel的VT-d和VT-x的ring-1硬件隔离技术,这种技术可以防止虚拟机突破和彼此交互,而容器至今还没有任何形式的硬件隔离;
- Docker的集中化管理工具还不算成熟,各种虚拟化技术都有成熟的管理工具,比如:VMware vCenter提供完备的虚拟机管理能力;
- Docker对业务的高可用支持是通过快速重新部署实现的,虚拟化具备负载均衡,高可用、容错、迁移和数据保护等经过生产实践检验的成熟保障机制,Vmware可承诺虚拟机99.999%高可用,保证业务连续性;
- 虚拟化创建是分钟级别的,Docker容器创建是秒级别的,Docker的快速迭代性,决定了无论是开发、测试、部署都可以节省大量时间;
- 虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化,Docker在Dockerfile中记录了容器构建过程,可在集群中实现快速分发和快速部署。
# docker技术的三大核心概念是什么?
- 镜像:镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。
- 容器:容器是基于镜像创建的,是镜像运行起来之后的一个实例,容器才是真正运行业务程序的地方。如果把镜像比作程序里面的类,那么容器就是对象。
- 镜像仓库:存放镜像的地方,研发工程师打包好镜像之后需要把镜像上传到镜像仓库中去,然后就可以运行有仓库权限的人拉取镜像来运行容器了。
# centos镜像几个G,但是docker centos镜像才几百兆,这是为什么?
一个完整的Linux操作系统包含Linux内核和rootfs根文件系统,即我们熟悉的/dev、/proc/、/bin等目录。我们平时看到的centOS除了rootfs,还会选装很多软件,服务,图形桌面等,所以centOS镜像有好几个G也不足为奇。
而对于容器镜像而言,所有容器都是共享宿主机的Linux 内核的,而对于docker镜像而言,docker镜像只需要提供一个很小的rootfs即可,只需要包含最基本的命令,工具,程序库即可,所有docker镜像才会这么小。
# 镜像的分层结构以及为什么要使用镜像的分层结构?
一个新的镜像其实是从 base 镜像一层一层叠加生成的。每安装一个软件,dockerfile中使用RUM命令,就会在现有镜像的基础上增加一层,这样一层一层的叠加最后构成整个镜像。所以我们docker pull拉取一个镜像的时候会看到docker是一层层拉去的。
分层机构最大的一个好处就是 : 共享资源。比如:有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
# 容器的copy-on-write特性,修改容器里面的内容会修改镜像吗?
我们知道,镜像是分层的,镜像的每一层都可以被共享,同时,镜像是只读的。当一个容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件,都只会发生在容器层中,因为只有容器层是可写的,容器层下面的所有镜像层都是只读的。镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。
- 添加文件时:在容器中创建文件时,新文件被添加到容器层中。
- 读取文件:在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。
- 修改文件:在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
- 删除文件:在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
# Dockerfile的整个构建镜像过程
- 首先,创建一个目录用于存放应用程序以及构建过程中使用到的各个文件等;
- 然后,在这个目录下创建一个Dockerfile文件,一般建议Dockerfile的文件名就是Dockerfile;
- 编写Dockerfile文件,编写指令,如,使用FROM 指令指定基础镜像,COPY指令复制文件,RUN指令指定要运行的命令,ENV设置环境变量,EXPOSE指定容器要暴露的端口,WORKDIR设置当前工作目录,CMD容器启动时运行命令,等等指令构建镜像;
- Dockerfile编写完成就可以构建镜像了,使用
docker build -t 镜像名:tag<span> </span>.
命令来构建镜像,最后一个点是表示当前目录,docker会默认寻找当前目录下的Dockerfile文件来构建镜像,如果不使用默认,可以使用-f参数来指定dockerfile文件,如:docker build -t 镜像名:tag<span> </span>-f<span> </span>/xx/xxx/Dockerfile
; - 使用docker build命令构建之后,docker就会将当前目录下所有的文件发送给docker daemon,顺序执行Dockerfile文件里的指令,在这过程中会生成临时容器,在临时容器里面安装RUN指定的命令,安装成功后,docker底层会使用类似于docker commit命令来将容器保存为镜像,然后删除临时容器,以此类推,一层层的构建镜像,运行临时容器安装软件,直到最后的镜像构建成功。
# Dockerfile构建镜像出现异常,如何排查?
首先,Dockerfile是一层一层的构建镜像,期间会产生一个或多个临时容器,构建过程中其实就是在临时容器里面安装应用,如果因为临时容器安装应用出现异常导致镜像构建失败,这时容器虽然被清理掉了,但是期间构建的中间镜像还在,那么我们可以根据异常时上一层已经构建好的临时镜像,将临时镜像运行为容器,然后在容器里面运行安装命令来定位具体的异常。
# Dockerfile的基本指令有哪些?
- FROM 指定基础镜像(必须为第一个指令,因为需要指定使用哪个基础镜像来构建镜像);
- MAINTAINER 设置镜像作者相关信息,如作者名字,日期,邮件,联系方式等;
- COPY 复制文件到镜像;
- ADD 复制文件到镜像(ADD与COPY的区别在于,ADD会自动解压tar、zip、tgz、xz等归档文件,而COPY不会,同时ADD指令还可以接一个url下载文件地址,一般建议使用COPY复制文件即可,文件在宿主机上是什么样子复制到镜像里面就是什么样子这样比较好);
- ENV 设置环境变量;
- EXPOSE 暴露容器进程的端口,仅仅是提示别人容器使用的哪个端口,没有过多作用;
- VOLUME 数据卷持久化,挂载一个目录;
- WORKDIR 设置工作目录,如果目录不在,则会自动创建目录;
- RUN 在容器中运行命令,RUN指令会创建新的镜像层,RUN指令经常被用于安装软件包;
- CMD 指定容器启动时默认运行哪些命令,如果有多个CMD,则只有最后一个生效,另外,CMD指令可以被docker run之后的参数替换;
- ENTRYOINT 指定容器启动时运行哪些命令,如果有多个ENTRYOINT,则只有最后一个生效,另外,如果Dockerfile中同时存在CMD和ENTRYOINT,那么CMD或docker run之后的参数将被当做参数传递给ENTRYOINT;
# 如何进入容器?使用哪个命令
进入容器有两种方法:docker attach、docker exec; docker attach命令是attach到容器启动命令的终端,docker exec 是另外在容器里面启动一个TTY终端。
docker exec -it 3274412d88ca /bin/bash
attach是直接进入容器启动命令的终端,不会启动新的进程;exec则是在容器里面打开新的终端,会启动新的进程;一般建议已经exec进入容器。
# 如何清理后台停止的容器?
答:可以使用 sudo docker rm $sudo(docker ps -a -q)
查看本地镜像:docker images
查看本地容器:docker ps -a
# 如何查看镜像支持的环境变量
答:可以使用docker run IMAGE env
命令
# 当启动容器的时候提示:exec format error?该如何解决问题
答:检查启动命令是否有可执行权限,进入容器手工运行脚本进行排查。
# 本地的镜像文件都存放在哪里?
答:与Docker相关的本地资源都存放在/var/lib/docker/目录下,其中container目录存放容器信息,graph目录存放镜像信息,aufs目录下存放具体的内容文件。 如果希望将Docker的本地文件存储到其他分区,可以使用Linux软链接的方式来做。
# docker的配置文件放在哪里?
Ubuntu系统下Docker的配置文件是/etc/default/docker,CentOS系统配置文件存放在/etc/sysconig/docker。
# 如何停止所有正在运行的容器?
答:使用docker kill $(sudo docker ps -q)
# 如何临时退出一个正在交互的容器的终端,而不终止它?
答:按Ctrl+p,后按Ctrl+q,如果按Ctrl+c会使容器内的应用进程终止,进而会使容器终止。
# 如何批量清理临时镜像文件?
答:可以使用sudo docker rmi $(sudo docker images -q -f danging=true) 在我们构建镜像的过程中,常常需要使用build命令根据Dockerfile进行构建镜像,并且会build很多次,镜像名字也是相同的,那么就会出现很多虚悬镜像(临时镜像文件)
-f :显示满足条件的镜像; -q :只显示镜像ID。
# 可以在一个容器中同时运行多个应用进程吗?
答:一般不推荐在同一个容器内运行多个应用进程,如果有类似需求,可以通过额外的进程管理机制,比如supervisord来管理所运行的进程。
# 如何控制容器占用系统资源(CPU,内存)的份额?
答:在使用docker create命令创建容器或docker run创建并运行容器的时候,可以使用-c 或 --cpu-shares[=0]参数来调整同期使用CPU的权重,使用-m 或 --memory参数来调整容器使用内存的大小。
# 网络通信模式
- host 模式,使用–net=host 指定。 和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
- container模式,使用–net=container:NAMEorlD指定。指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。
- none模式,使用–net=none指定。 告诉docker 将新容器放到自己的网络堆栈中,但是不要配置它的网络。
- bridge 模式,使用–net=bridge 指定,默认设置。 bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。
# docker的签名机制
Docker 的签名机制是一种用于验证容器镜像和图层完整性以及身份验证的安全机制。它有助于确保只有受信任的发布者可以创建和分发容器镜像,并防止恶意或未经授权的更改。
以下是 Docker 签名机制的详细说明:
- 数字签名:Docker 使用数字签名来验证容器镜像的身份和完整性。发布者使用私钥对镜像进行签名,而用户使用发布者的公钥来验证签名。这确保了镜像的真实性,即它来自于它所声称的发布者,并且在传输过程中没有被篡改。
- 签名对象:Docker 签名通常应用于镜像和图层。发布者可以签署整个镜像或特定图层的签名。这允许对图层级别的完整性进行验证,以提高镜像的安全性。
- 签名和验证过程:
- 发布者使用其私钥对镜像或图层进行签名。
- 签名信息被附加到镜像的元数据中,通常存储在镜像的
.signature
目录中。 - 用户或部署系统获取镜像并从发布者处获取其公钥。
- 用户使用公钥来验证镜像的签名,以确保镜像的完整性和真实性。
- 如果验证成功,用户可以安全地部署镜像。
- 信任集合:Docker 支持信任集合(trust collections),它们是一组信任的根实体、发布者和公钥。信任集合定义了哪些发布者和签名被信任,以及哪些不被信任。这允许用户自定义信任策略,并可以拒绝不受信任的镜像。
- 签名的存储和传递:Docker 签名可以存储在 Docker Content Trust 的存储库中,并通过 Docker 命令行工具进行传递。签名信息存储在 Docker Hub 和其他容器镜像仓库上,并可以与
docker pull
和docker push
命令一起使用。 - 支持的签名格式:Docker 支持不同的签名格式,包括 Docker Content Trust、OCI(Open Container Initiative)和 Notary。这些格式允许用户选择最适合其需求的签名方案。
# Iptables四表五链
raw表:确定是否对该数据包进行状态跟踪
mangle表:为数据包设置标记
nat表:修改数据包中的源、目标IP地址或端口
filter表:确定是否放行该数据包(过滤)
INPUT:处理入站数据包
OUTPUT:处理出站数据包
FORWARD:处理转发数据包
POSTROUTING链:在进行路由选择后处理数据包
PREROUTING链:在进行路由选择前处理数据包
ref: https://www.hanmoonhan.club/articles/2023/06/07/1686124120958.html#toc_h2_17
ref: https://blog.csdn.net/qq_39578545/article/details/124194142