Nydus 镜像扫描加速

news2025/1/6 20:55:38

38a7fd10de05cefb0845e15ba7cbfc59.gif

文|余硕

上海交通大学22届毕业生

阿里云开发工程师

从事云原生底层系统的开发和探索工作。

本文 6369 字 阅读 16 分钟

821c5a0597eb0eb76250aa676f59c7e9.png

GitLink 编程夏令营是在 CCF 中国计算机学会指导下,由 CCF 开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。

这是今年的夏令营活动中,余硕同学参加 Nydus 开源项目的总结,主要介绍了 Nydus 为支持镜像扫描与修复所做的研究与相关工作。

PART. 1

课题背景

Nydus 开源镜像加速框架

Nydus 是 CNCF 孵化项目 Dragonfly 的子项目,它提供了容器镜像,代码包按需加载的能力。Nydus 应用时无需等待全部数据下载完成便可开始服务。

Nydus 在生产环境中已经支撑了每日百万级别的加速镜像容器创建。它在容器启动性能、镜像空间占用、网络带宽效率、端到端数据一致性等方面相比 OCI v1 格式有着巨大优势,并可扩展至其它数据分发场景,比如 NPM 包懒加载等。

目前 Nydus 由蚂蚁集团、阿里云、字节跳动联合开发。Containerd、Podman 社区已经接受了 Nydus 运行时作为其社区子项目,它也是 Kata Containers 以及 Linux v5.19 内核态原生支持的镜像加速方案。

有关 Nydus 镜像加速开源项目的详细介绍,可以参考:Nydus——下一代容器镜像的探索实践。

项目描述

为 Nydus 镜像增加一个扫描和修复的命令行工具,包含以下功能:

  • 提供一个 Nydus 镜像 url 和需要替换的文件列表;

  • 使用工具拉取镜像 Bootstrap;

  • 找到镜像中对应的文件并替换;

  • 打包成新的镜像并上传回 Registry。

概括来说,原有项目目标是为 Nydus 镜像实现一个扫描和修复的命令行工具,其中这些功能是这个工具或者工具组的实现流程。

但此项目具有一定的实验性,其核心是为 Nydus 格式的镜像提供扫描和修复的功能或者指引。如果存在更好的方式,项目最终不一定要按照原有项目描述去实现。因此在接下来的课题完成过程中,我们首先对已有镜像扫描/修复的工具与服务进行了调研,来确定此课题方案的最终形态。

镜像扫描工具及服务

扫描和修复

镜像扫描和修复可以被拆解为两个过程。扫描不更改原有的镜像内容,只需要寻找扫描目标是否存在某种缺陷;镜像修复则需要根据缺陷改动镜像,包括但不限于镜像内容,镜像层级组织等。我们调研发现,当前主流开源镜像扫描引擎包括云厂商镜像安全服务都只涉及镜像扫描的功能, 不会主动添加镜像修复的功能。

我们分析主要的原因有:

  • 直接进行镜像修复可能引入新的安全问题;

  • 镜像扫描的内容存在不同种类,很可能需要为不同种类的镜像安全问题设计不同的修复方式;

  • 镜像修复功能可以通过重新打包镜像替代。

所以,镜像安全服务暂时只是提供扫描结果的安全报告,具体的修复操作由用户自行决定。我们也准备沿用这样的思路:在本课题中,为镜像实现安全扫描的功能,并提供报告作为用户镜像修复的参考依据。

我们也探索讨论了镜像修复的支持,或者 Nydus 特性在镜像修复场景下的增强,比如镜像内包替换后的去重与重组等,或许这些可以是未来 Nydus 中增加的功能特性。

镜像扫描介绍

镜像扫描的原因与内容

容器镜像是当前容器/软件分发的基础,里面包含了容器隔离环境以及软件执行环境的相关内容。因此保障其安全性变得十分重要。镜像扫描即是要扫描镜像中的所有内容,及时发现可能包含的安全漏洞或者隐私泄露。

综合来看,镜像扫描需要关注镜像的安全性与健壮性,扫描的内容主要分为三类:

1. 安全漏洞。包括系统软件包和应用软件库中可能存在的安全漏洞。可以通过比对镜像中这些库/软件包的来源、版本等与 CVE 数据库中报告的漏洞的包的来源、版本来定位可能存在的安全漏洞。

2. 配置。包括镜像运行的环境配置和镜像中相关内容组合可能带来的问题。帮助尽早定位配置错误以及配置错误可能带来的安全风险。

3. 隐私。需要扫描的是用户指定的一些隐私信息。比如用户不小心将密钥等信息存入镜像。如果在扫描配置中进行指定,扫描过程可能发现这些隐私信息,避免隐私泄露以及可能带来的安全问题。

扫描引擎

常见的扫描引擎有 Trivy、Synk 等。Docker 官方使用的是 Synk,但它比较商业化;Trivy 是 CNCF 的项目,开放性较好。

镜像扫描的使用方式

在我们的调研中,镜像扫描主要应用方式有三种:

1. 基础使用。镜像扫描的过程可以直接通过集成了镜像扫描引擎的容器运行时或者镜像扫描引擎命令行触发。比如运行 $ docker scan image-url ,可以扫描镜像,并输出相应的报告。

79728976701baccb6b1f65dea147d265.png

source:https://docs.docker.com/engine/scan/

2. 流程集成。镜像扫描的过程可以集成到镜像中心或者 CI/CD 流程中。比如将镜像扫描集成到数据中心,在每次镜像上传到数据中心时触发镜像扫描,可以保证从数据中心下载的镜像总是经过安全扫描的;集成在 CI/CD 流程中,设置触发条件,可以保证镜像生成,镜像部署等过程所使用的镜像的安全性。

72b291515eca9063af59393c99d90ba7.png

source: https://www.containiq.com/post/container-image-scanning

3. 扫描服务。云厂商提供了镜像安全的服务。它们背后可能是基于前两种使用方式实现,但是还可以有很多种功能的增强。用户想使用镜像扫描的功能也可以直接购买类似的安全服务,并进行灵活的配置。

eec0278b65d418d864d772cc5ba7d557.png

source: https://cloud.google.com/container-analysis/docs/on-demand-scanning-howto

PART. 2
课题解决思路

基本思路

课题首先要解决的基本思路是,如项目描述一般为 Nydus 实现一个专属的镜像扫描工具;还是复用已有的镜像扫描引擎,在 Nydus 侧实现对接支持,从而完成 Nydus 镜像扫描的功能实现。

我们最终选择了结合已有镜像扫描引擎的实现思路。尽管为 Nydus 实现一个专属的镜像扫描工具可以更好的利用 Nydus 的特性,但是从镜像功能生态上考虑,这并不是一个很好的方式。复用或者集成到现有的镜像扫描工具,一方面可以直接使用已实现的镜像扫描引擎中全面的内容扫描能力;另一方面,与上层镜像安全服务的对接也不用再重写相关接口。这样也可以减少一些功能定制,减少用户使用的负担。因此此课题选择了后一种实现的基本思路。

扫描思路:FileSystem vs Image

Trivy 扫描功能实现的框架

1. 控制路径。

0ebe15e37e8e176d8e7c3e4134194c20.png

Trivy 在镜像扫描上由以下路径控制,每触发一次命令,会由一个 Scanner 控制。其中关键的是 artifact 和 driver 。扫描引擎一般可以支持多种格式的扫描,比如 OCI v1 镜像或者镜像 Rootfs,同时一般也支持本地或者远端存储信息等。这些一般可由 ScannerConfig 配置或者自动解析。

artifact 存储着镜像(包括文件系统)元信息,如果已经扫描过其中的内容,还可以存储部分解析后的信息。另外,load 到本地的 CVE 数据等也可通过 Artifact 获取。Driver 里的 Scan 方法表示的则是应用在特定扫描过程中的检查方法。

2. 关键动作。

  • local.Scanner

102f4fd8338de3cce09c9b1647dfe271.png

  • Applier

5e24a3ae58cf5ecc9e4af695c6ba0aa5.png

Trivy 中 Local Scanner 是前文提到的本地进行扫描的控制结构。可以看出 Scanner 里定义的行为是 Apply Layer。也就是将对镜像逐层进行扫描。Applier 保存了 Artifact 信息,是联系具体扫描方法和存储信息的结构体。

3. 解析镜像。

上述两个过程理解了总体 Scan 的过程控制,以及具体针对镜像的扫描方法。与镜像相关的还有一个关键过程是如何针对性的解析扫描镜像信息。这一过程实现在 Artifact 中。

  • Artifact

9345f821fe25ee16860cae0a47bfd287.png

  • Walker-Analyzer

56776c7628c3d25c6846f5871cdd4b77.png

Artifact 中有镜像元信息,还有保存解析镜像信息的 Cache 等。主要要考虑的是当前 Trivy 支持的不同种类镜像而进行不同的设置。

另一关键的是 Analyzer。Analyzer 对应的是镜像分层的操作。对不同种类的镜像操作不同。比如对于 OCI v1 的镜像,可能就是每层镜像拉取的完整数据流进行分析。对于 FileSystem,就是层次遍历文件树。

方案选择

了解了 Trivy 的实现后,我们发现直接把 Nydus 打包成 OCI v1 类似的镜像去扫描并不合适。Nydus 镜像内容中层次组织已经发生了变化,也具有按需加载的特点。若直接拉取,不一定保证拉取信息的完备,想要完备支持镜像的拉取也会丢失 Nydus 的特性。

因此我们最终选择在 FS Artifact 方式下优先拓展 Nydus 的镜像扫描能力。

相关的指令是:

$trivy image nydus-image-url  ✗
$trivy fs /path/to/nydus_imgae_mountpoint  ✓

这样做的好处一是可以利用 Nydus 按需加载的特性。我们发现对于很多软件包的扫描并不需要完整的文件内容的下载,很多时候一些局部信息甚至元信息即可判断。这一特点可以利用上 Nydus 的按需加载,从而加快整个镜像扫描的过程。二是特殊格式的镜像都会有挂载文件系统这一操作。这样的方式可以推广到更多的特殊格式镜像。

本课题最终是以工具形式为 Nydus 集成了加速镜像文件系统挂载的能力,这能够适配主流的镜像扫描框架,未来我们可以考虑为社区镜像扫描方案做更深度的集成,比如 Trivy、Clair、Anchore Engine 等,支持直接指定 Nydus 镜像 Reference 做扫描,优化端到端的用户体验。

方案实现

在 Nydus 侧提供镜像扫描的支持,简单指令过程为:

$ nydusify view localhost:5000/ubuntu:latest-nydus
/path/to/root_path
[比起容器简单,直接拿到文件树]


$ trivy fs /path/to/rootpath

Nydusify 是 Nydus 提供的镜像转换,校验与镜像文件系统挂载工具,使用方式可以参考:https://github.com/dragonflyoss/image-service/blob/master/docs/nydusify.md

上面实现在 Nydusify 中的核心功能由 FileSystemViewer 结构体控制:

93d852a742464eb8614aa6a57406021b.png

此结构体需要保存的成员变量有此过程的一些输入信息和配置信息。SourceParser 用于解析镜像 url。MountPath 是可以指定的文件系统 Mount 的地址。NydusdConfig 是 Mount 过程 Nydus Daemon 的配置。image-url 是必须提供的输入信息。View() 是调用方法。

当 nydusify view 被调用时,主要将发生三个步骤:

1. 解析 image-url ;

2. 根据解析信息,拉取文件系统元数据 Bootstrap;

3. 根据 Bootstrap,Nydusd Mount 文件系统到指定路径。

之后 trivy fs 被调用时,可能发生:

1. 层次遍历挂载的文件系统;

2. 打开镜像挂载点中某个文件时,会触发 FUSE 请求到 Nydus Daemon(Nydusd),Nydusd 会从远端镜像中心按需拉取文件的 Chunk 数据,以提供给扫描引擎分析文件内容。

PART. 3
课题展示

Demo 展示

我们实现了这一功能,如 demo 中演示:

2e7da15004f183237033bdb4321442bd.png

性能测试

我们在 Ubuntu、Wordpress、Tensorflow 等镜像上进行了测试。

测试结果如下:

93c0353b68c6c63a6547545a5d051b37.png

以扫描时延作为衡量标准,可以看出 Nydus 的安全扫描时间要显著少于基本 OCI v1 镜像。这其中优化的幅度与镜像文件系统复杂程度,测试网络环境等因素有关。

我们还从实际镜像扫描场景了解到,很多时候出现安全问题时,例如一些 0day 漏洞,我们需要对大批量镜像进行特定的少量文件元数据或数据的侦测。这种情况就更能体现出 Nydus 镜像懒加载的优势,安全扫描速度能够大幅度提高。

当然,Trivy 对 OCI v1 镜像的扫描优化会影响对比结果。我们 Trivy Image 多次对同一镜像进行扫描,可得到下面的结果:

5862e6d9dd54ab61856197c6cc28d752.png

可以看出,多次扫描之后花费的时延下降。如之前提到,扫描信息匹配会存到 local Cache 中。这是会根据 Blob ID 做为 key 来存储的。因此 Trivy 在扫描同一镜像时可以直接去查询 Cache 而不必重复拉取镜像形成优化。我们在未来也想集成类似的优化,这也是之后我们也想在扫描引擎社区推动更好的支持的原因。

PART. 4
课题拓展

描述

除了扫描镜像内容的安全性,Nydus 本身提供了指令工具 nydus-image inspect 对 Nydus 镜像进行检查。 nydus-image inspect 指令通过检索 Nydus 镜像中包含的文件系统元数据信息,可以查看 Nydus 镜像的组织情况。进一步可以判断 Nydus 镜像是否损坏等。

随着 Nydus 的发展,它支持文件系统 Layout 也从 v5 扩展到 v6。RAFS v6 可以兼容 EROFS,由此获得性能上的进一步提升。 nydus-image inspect 在 RAFS v5 时已经设计,扩展到 RAFS v6 之后,一些子命令为了兼容进行了扩展。但是这样的扩展是为 v5/v6 分开写成的,也就是需要执行子命令时还需要对文件系统格式进行一次判断。

此外,还有一些子命令没有做到兼容性扩展。这样的不完全支持存在一定的遗留问题,没有做到功能的完备和统一。这样的代码组织也损失了易读性,不利于后续的维护,不同 RAFS 格式相关功能的迭代升级中也比较容易产生问题。

事实上,Nydus 已经为这两种格式的文件系统元数据实现了统一的 API 接口。

比如对 RafsSuperInodes, RafsSuperBlock 等结构有统一的方法进行信息的获取,对不同的底层实现进行了屏蔽。因此在此基础之上,有必要对 nydus-image inspect 指令的相关功能进行一次重构。

实现

原有的架构中,存在一个 Executor 结构根据子命令判断调用哪一个具体的方法。比如 Executor 收到子命令 stats 时会调用 cmd_stats 函数,执行并完成结果的输出显示。

我们需要重构的是针对每一条子命令所调用的方法,上层的调用逻辑保持不变,具体而言子命令方法有:

  • cmd_stats

  • cmd_list_dir

  • cmd_change_dir

  • cmd_stat_file

  • cmd_list_blobs

  • cmd_list_prefetch

  • cmd_show_chunk

  • cmd_check_inode

这里不再对每一条子命令调用方法的修改进行详细描述。大概做法是使用 RAFS mod 中统一的 API 实现所有原本的功能逻辑。

测试

我们实现了两种测试。两种测试的目的都是为了保持重构前后功能实现的一致。

测试一:交互形式比对输出结果。

这是一种功能测试。针对每条指令每种可能出现的情况,选取代表性镜像进行测试与比对。

测试二:集成冒烟测试。

这是一项冒烟测试。nydus-image inspect 命令还有 request mode 可以将一条子命令的输出序列化成 json 格式的结果。我们将原有代码的结果序列化成 json 格式作为参考输出,再将重构后代码的输出与之一一比对。选取的镜像为 Nydus 仓库中用于冒烟测试的镜像。这一部分的测试也集成在 Nydus 仓库中,在以后的 CI 里也可以保证这项功能的正确性没被破坏。

成果

Prompt Mode

Stats

996258cb166d155e889b657821243ca5.png

ls

ae8b924d89810823ab719a1cc74c3102.png

Cd

9601d1817f5053eb2264f17b7e95de4d.png

Stat File

d83912dc135528f6a4f63c572427fde9.png

Blobs

ce12068db2d46c83352356c9a2bd30e8.png

Prefetch

e751dd85b1f7aec817b7ee888b6a083c.png

Chunk Offset

392f9114acbf3331c0a19b062a199799.png

Icheck Index

8bca001a8150af234ae7eefbf0810df3.png

Request Mode

Stats

79644dc877b22fb2ca70b28ff9ac9243.png

Prefetch

11a9521980c128576565dcb7cf38b71a.png

Blobs

65bdf9e8fd64e6d1fb32842329ccc385.png

PART. 5
收益与展望

收益

我非常荣幸能参加这次项目的开发,也要向项目组织老师赵新、项目指导助理姚胤楠、严松老师以及 Nydus 社区表示衷心的感谢。

通过这次项目的开发,我收获了许多:知识上,我复习并更深入了解了文件系统,学习了容器镜像格式的组织,还首次尝试了 Go 语言开发,也开始了解软件测试;技能上,我锻炼了协调时间,开放讨论,问题定位的能力。

更重要的是,我认为参与这次项目增加了我对容器场景应用的见识,也提升完整解决方案与系统设计的能力。当然,可能最大的收益是收获了一次快乐的体验!体会了开源合作的快乐,也体会到了开源价值产生的快乐。

展望

此次项目主要是为 Nydus 添加了镜像扫描功能的支持,另外重构了 Nydus 的 Inspect 分析工具。如前文所说,后续还要继续探索 Nydus 镜像扫描集成到扫描引擎的方式,镜像扫描和修复的优化也值得探索。关于 Nydus 镜像格式细节,用户态文件系统,EROFS 等,我还有很多需要学习。

希望能一直参与社区,不断丰富自己容器存储方面的知识,同时也能为社区做出更大的贡献。

 了解更多...

Nydus Star 一下✨:
https://github.com/dragonflyoss/image-service

   本周推荐阅读  

b584911140b949a8cab6be36baaee7a5.png

Nydus —— 下一代容器镜像的探索实践

6e3c0fe638bf792087b08c2c720ff98e.png

Nydus 镜像加速插件迁入 Containerd 旗下

cb97d5e019cddff5fffe9a887ef07d12.jpeg

Nydus | 容器镜像基础

e27b47953390c758c06e42eab1e6d006.png

Dragonfly 和 Nydus Mirror 模式集成实践

592c7c5eda79a245a4b7bbb95d327a5d.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/118630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java字符集编码解码详细介绍

文章目录字符集字符集的基本认识字符集编码和解码字符集 字符集的基本认识 字符集基础知识 计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1) 二进制是可以转换成十进制的 计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套…

【进阶C语言】数据的存储形式

文章目录一.数据类型分类二.整形的存储形式1.源码,反码,补码的关系内存中数据的存储——二进制源码,反码,补码的关系正数负数三.大小端1.概念2.例题:判断当前编译器的存储形式四.浮点数的存储形式1.二进制的补充&#…

【k8s系列】kube-state-metrics中kube_endpoint_address指标

文章目录背景环境操作方法1:kube_endpoint_address_not_ready选择大于0的验证方式1验证方式2方法2:kube_endpoint_address_available选小于0的方法3:kube_endpoint_address{ready"false"}选大于0的解释参考author: ningan123date: …

java基础巩固-宇宙第一AiYWM:为了维持生计,架构知识+分+微序幕就此拉开之RocketM消息中间件~整起

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 RocketMQ一、RocketMQ概念~一览无余1.消息队列有啥用?能干啥?消息队列的应用场景?2.常见的消息队列有哪些?如何进行消息队列的…

2、MySQL支持的数据类型

目录 1、整数类型 (1)fillzero:根据整数类型的长度自动添加0 (2)unsigned:非负整数 (3)bin(m):将十进制数转为m进制 2、日期时间类型 &#x…

【MySQL基础教程】函数的介绍与使用

前言 本文为 【MySQL基础教程】函数的介绍与使用 相关知识,下边具体将对字符串函数,数值函数,日期函数,流程函数等进行详尽介绍~ 📌博主主页:小新要变强 的主页 👉Java全栈学习路线可参考&…

MAXHUB+腾讯会议:为未来办公造一部动力引擎

科技领域有个规律,我们经常高估一年的变化,而低估了十年或者更长时间所可能发生的变化。不信可以做个测试,你觉得未来线上办公会怎么发展?不少朋友会说,既然线上办公是疫情到来之后的PlanB,那么随着疫情结束…

【STM32F4系列】【HAL库】【自制库】ps2手柄模块驱动

外观和电气连接 外观 手柄外观如下 接收器外观 这是接收器和底座 电气连接 需要4根连接线 单片机输出是CLK DO CS 单片机输入是DI 电源电压是3.3-5v 注意模块和单片机共地 模块不支持高速,最大时钟周期约为4us左右 因此使用软件模拟时序的方式来与模块通信 只需要将模块的4根线…

Golang Context 的几种应用场景

Golang context主要用于定义超时取消,取消后续操作,在不同操作中传递值。本文通过简单易懂的示例进行说明。 超时取消 假设我们希望HTTP请求在给定时间内完成,超时自动取消。 首先定义超时上下文,设定时间返回取消函数&#xff…

Apache POI操作百万数据excel实战方案及JDK性能监控工具Jvisualvm实战

百万数据报表概述 文章目录**百万数据报表概述****1、** **概述****2、 JDK性能监控工具介绍****2.1、 Jvisualvm概述****2.2、 Jvisualvm的位置****2.3、 Jvisualvm的使用****3、** **解决方案分析****4**、**百万数据报表导出****4.1** **需求分析****4.2** **解决方案****4.…

玩转门店管理新方法,促进营收利润加倍

门店管理的好坏是门店是否可以运营下去的重要因素,决定了门店的存亡与兴衰。以往很多门店管理者为了更简单方便,采用的是传统方式进行管理。即运用手工的方式记录和计算门店的各种信息。但是随着门店规模的扩大、商品种类的丰富、客户需求的增加以及员工…

普惠微光汇聚暖阳,招联携手奋斗者筑梦前行

撰稿 | 多客 来源 | 贝多财经 近年来,受疫情反复带来的经济下行压力,收入减少生活难以保障成了社会一大难题。值此艰难时刻,一大批爱心企业出资出力,纷纷用实际行动诠释企业的使命和担当。口罩、防护服、矿泉水、食品、药物、免费…

vue配置webpack生产环境.env.production、测试环境.env.development(配置不同环境的打包访问地址)

vue-cli区分办法 vue配置生产环境.env.production、测试环境.env.development vue配置webpack生产环境、测试环境 在使用webpack创建完vue2项目的时候,为了解决生产打包、测试打包对应的全局变量不一致的问题。 首先看一下package.json的改动: "…

MARL算法系列(1):IQL【原理+代码实现】

原文题目:Multiagent cooperation and competition with deep reinforcement learning 作者:Tampuu, Ardi and Matiisen, Tambet and Kodelja, Dorian等 发表时间:2017年 主要内容:相互独立的两个DQN智能体,竞争任务下…

2022年威胁隐私和安全的数个“罪魁祸首”

随着互联网技术的不断发展,我们对网络的信任也在不断增加,甚至将自己的私人数据委托给各种在线平台,如个人数字身份信息、银行账户、各种机密信息。网络一方面的确带来变革型的进步,但另一方面,频频曝光的数据泄露事件…

VueJs中setup的使用(下)

前言在Vue当中,父组件想要向子组件传值,是通过在父组件标签上通过自定义属性实现的,而在子组件中是通过props进行接收在Vue2.0里面,在子组件中的选项式API配置项选项中props进行接收就可以了的,在子组件中的模板中可以直接使用但在Vue3里面与Vue2.0存在一些差异,这个主要是针对…

excel文件管理:如何进行密码保护和破解? 下篇

在上篇文章中,我们提到了设置工作簿的打开权限密码、修改权限密码、保护工作簿的密码、允许编辑区域的密码,并且讲到了两种破解excel密码的方式。今天,我们书接上回,继续讲解excel中常见的密码保护和破解方式,一起来看…

浅谈屏幕适配

文章目录1. 概述2. 屏幕尺寸3. 屏幕分辨率4. 屏幕像素密度5. dp、sp、px6. mdpi、hdpi、xdpi..7. 屏幕分辨率限定符8. 最小宽度限定符8.1 获取设计图最小宽度(dp)8.2 生成对应的dimens.xml文件8.3 尺寸限定符8.4 其它9. 今日头条相关9.1 系统状态栏获取不对问题9.2 autosize1. …

Elasticsearch8.X入门实战(二)Elasticsearch集群架构

Elasticsearch集群由一个或多个节点(服务器)组成,这些节点一起保存Elasticsearch的所有数据,并提供跨所有节点的联合索引和搜索功能。集群由一个唯一的名称来标识,该名称默认为“elasticsearch”(可以在配置文件中修改)。当某个节点被设置为相同的集群名称时,该节点才能…

Docker容器的简单介绍与使用

前言:大家好,我是小威,24届毕业生,曾经在某央企公司实习,目前入职某税务公司。本篇文章将记录和分享docker容器相关的知识点。 本篇文章记录的基础知识,适合在学Java的小白,也适合复习中&#x…