运维笔记.Docker镜像分层原理

运维专题
Docker镜像原理

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at CSDN: https://jclee95.blog.csdn.net
My WebSitehttp://thispage.tech/
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/139248460
HuaWei:https://bbs.huaweicloud.com/blogs/428123

【介绍】:Docker镜像是由只读层组成,每层代表一个指令,这些层是堆叠的,每一层都是前一层的增量。本文关于Docker镜像分层相关原理和话题。

在这里插入图片描述


1. Docker中镜像与容器简要回顾

1.1 镜像(Image)

Docker镜像是一个轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,包括代码、运行时环境、库、环境变量和配置文件。

镜像是静态的,即在创建后内容不会改变,它们是构建容器的基础。

1.2 容器(Container)

容器是镜像的运行实例。当镜像被启动时,它在隔离的环境中运行,成为一个或多个正在运行的容器。

容器隔离并运行单独的应用,保证应用运行在快速、可重复的环境中。

2. 镜像的分层原理

2.1 分层存储

2.1.1 分层存储的工作原理

Docker镜像是由一系列只读的层(layer)组成的。这些层按照从下到上的顺序堆叠,每一层都是基于下面一层的变化。当我们创建或更新一个镜像时,只有被改变的部分会被添加为一个新层,其他部分保持不变。

这种分层存储的机制使得Docker镜像可以被高效地构建、传输和存储。

2.1.2 层的创建过程

通常,我们使用Dockerfile来定义和创建Docker镜像。Dockerfile中的每一条指令(如FROMRUNCOPY等)都会创建一个新的层。

例如,考虑以下Dockerfile

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

这个Dockerfile会创建四个层:

  1. 基础层:从ubuntu:22.04镜像开始。

  2. 第二层:运行apt-get update && apt-get install -y python3,安装Python3

  3. 第三层:将app.py文件复制到镜像的/app/目录。

  4. 第四层:指定容器启动时运行的命令python3 /app/app.py

2.1.3 层的存储与复用

这些层都是只读的,并且每一层只存储与前一层的差异部分。当我们构建一个新镜像时,Docker会检查每一层的内容是否已经存在。如果一个层与现有的层完全相同,Docker会直接复用这个层,而不是重新创建。

这种机制大大提高了构建和存储效率。例如,如果你有多个Dockerfile都基于ubuntu:22.04,那么这个基础层只需要存储一次,所有的镜像都可以共享它。

2.1.4 容器的可写层

当一个容器从镜像启动时,Docker会在镜像的顶部添加一个新的可写层。容器对文件系统的所有改变,如创建新文件,修改现有文件,删除文件等,都会被记录在这个可写层中。

这个可写层允许多个容器共享同一个镜像,同时又保持各自的状态。当容器被删除时,可写层也会被删除,而底层的镜像保持不变。

通过详细解释分层存储的工作原理、层的创建过程、层的存储与复用,以及容器的可写层,我们可以更深入地理解Docker如何利用分层存储来优化镜像的构建、存储和运行效率。

2.2 重用与共享

2.2.1 共享机制的优点

Docker的分层存储机制不仅优化了镜像的存储,还促进了镜像层的 重用和共享。这种共享机制有以下优点:

  1. 节省存储空间:多个镜像可以共享相同的层,而无需重复存储,大大节省了存储空间。

  2. 加速镜像构建:当构建一个新镜像时,如果所需的层已经存在,Docker会直接使用现有的层,而不是重新创建,从而加速了构建过程。

  3. 加速镜像分发:当从Docker仓库拉取镜像时,如果某些层已经存在于本地,只需拉取缺失的层,减少了网络传输的数据量,加速了镜像分发。

2.2.2 共享的实现方式

Docker通过以下方式实现层的共享:

  1. 内容寻址:每个层都有一个唯一的哈希值,这个哈希值是根据层的内容计算出来的。如果两个层的内容完全相同,它们的哈希值也相同。Docker使用这个哈希值来判断两个层是否相同。

  2. 层的复用:当构建一个新镜像时,Docker会检查每一层的哈希值。如果一个层的哈希值与现有的层相同,Docker会直接使用现有的层,而不是重新创建。

  3. 共享层的存储:所有的镜像层都存储在Docker主机的文件系统中。如果多个镜像共享一个层,这个层在文件系统中只存储一次,所有的镜像都引用这个共享的层。

2.2.3 共享的示例

现在考虑一个具体的例子来说明Docker层共享是如何工作的。假设我们有两个Dockerfile,它们都基于ubuntu:22.04镜像:

Dockerfile1

FROM python:3.11
RUN pip install django
COPY app1/ /app/
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

Dockerfile2

FROM python:3.11
RUN pip install django pillow
COPY app2/ /app/
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

当我们构建这两个镜像时,Docker会创建以下层:

  1. 基础层(python:3.11)
  2. Django层(RUN pip install django)
  3. Pillow层(RUN pip install django pillow)
  4. 应用1层(COPY app1/ /app/)
  5. 应用2层(COPY app2/ /app/)

其中,基础层和Django层是共享的,因为它们在两个Dockerfile中是相同的。Pillow层、应用1层和应用2层是独立的,因为它们在每个Dockerfile中是不同的。

当这两个镜像都构建完成后,Docker只需在文件系统中存储一次基础层和Django层,而不是两次。这样就节省了存储空间,并且加速了镜像的构建和分发过程。

3. 镜像大小问题

Docker镜像的大小是一个重要的考量因素,因为它直接影响到镜像的存储、传输和部署效率。镜像越大,占用的存储空间越多,传输和部署的时间也越长。因此,优化镜像大小是Docker使用中的一个重要课题。

3.1 基础镜像的大小

基础镜像是构建其他镜像的起点,它通常包含操作系统的核心文件和一些常用的库。基础镜像的大小会直接影响到所有基于它构建的镜像的大小

例如,如果你选择了一个 包含完整操作系统的基础镜像 ,如ubuntu:22.04,那么你的镜像大小会比选择一个 最小化的基础镜像 ,如alpine要大很多

因此,在选择基础镜像时,我们需要权衡功能完整性和镜像大小。如果你的应用不需要完整的操作系统,选择一个最小化的基础镜像可以显著减小最终镜像的大小。

3.2 软件依赖

除了基础镜像,应用运行所需的软件库和其他依赖也会增加镜像的大小。每个 RUN 指令安装的软件包,每个COPYADD指令添加的文件,都会增加镜像的大小。

为了减小镜像大小,我们可以采取以下措施:

  1. 只安装必要的软件包:仔细评估每个软件包的必要性,只安装运行应用所必需的包。

  2. 清理安装缓存:在安装软件包后,删除下载的软件包缓存,如/var/cache/apt/archives/

  3. 合并RUN指令:尽可能将多个RUN指令合并为一个,这样可以减少镜像的层数,从而减小镜像大小。

  4. 使用.dockerignore文件:在COPYADD文件时,使用.dockerignore文件排除不必要的文件和目录,减小镜像大小。

3.3 多阶段构建

多阶段构建是Docker提供的一个功能,它允许我们在一个Dockerfile中使用多个FROM语句,每个FROM语句都可以使用不同的基础镜像,并且每个阶段都可以从前一个阶段复制文件。

这个功能对于需要构建依赖项但最终镜像不需要这些依赖项的情况非常有用。通过多阶段构建,我们可以在一个阶段安装所有必要的依赖项并构建应用,然后在另一个阶段只复制构建好的应用,而不复制构建依赖项。这样可以显著减小最终镜像的大小。

以下是一个使用多阶段构建例子:

# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/main.js"]

在这个例子中:

  1. 第一阶段使用node:18镜像作为构建环境。它复制package.jsonpackage-lock.json,运行npm ci安装所有依赖项(包括开发依赖项),然后复制其余的应用代码并运行npm run build构建应用。

  2. 第二阶段使用node:18-alpine镜像作为基础,这是一个更小的Node.js镜像。它从第一阶段复制构建好的应用代码(在dist目录中),复制package.json和package-lock.json,然后运行npm ci --only=production只安装生产依赖项。最后,它指定了容器启动时运行的命令。

这样,最终的镜像就只包含运行应用所需的代码和生产依赖项,而不包含用于构建的代码和开发依赖项,从而大大减小了镜像的大小。

再看一个例子:

# 构建阶段
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
COPY . .# 运行阶段
FROM python:3.11-alpine
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]

在这个例子中:

  • 第一阶段安装所有的依赖项(包括开发依赖项);
  • 第二阶段只复制安装好的依赖项和应用代码,使用更小的python:3.11-alpine镜像。

4. 联合文件系统

联合文件系统(Union File System,简称 UnionFS)是 Docker 镜像的基石。它是一种分层、轻量级并且高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

4.1 工作原理

UnionFS 的工作原理是将多个不同的目录(也叫分支)内容联合挂载到同一个目录下,并呈现为单个一致的文件系统。这些分支可以是只读的,也可以是可读写的。当对这个虚拟文件系统进行修改时,实际上是在可写的分支上进行操作,而原始的文件并没有被修改。

Docker 中,镜像就是由多个只读层组成的。当我们启动一个容器时,Docker 会在这些只读层之上添加一个可读写层。当容器修改现有的文件时,该文件将被复制到可读写层,并在那里被修改。这种机制被称为写时复制Copy-on-Write)。

4.2 UnionFS的优点

使用 UnionFS 有以下几个优点:

  • 节省存储空间:多个镜像可以共享相同的只读层,从而减少磁盘占用。

  • 加速镜像构建和部署:当构建一个新镜像时,只需要构建与现有镜像不同的层,相同的层可以直接复用。这大大加速了镜像的构建和部署过程。

  • 促进镜像的分发:由于镜像是分层存储的,当我们下载一个镜像时,实际上只需要下载我们本地没有的层。这减少了网络传输的数据量,加速了镜像的分发。

4.3 加载过程

当启动一个容器时,Docker 会从镜像的底层开始,依次加载每一层文件系统,直到最顶层的可读写层。这个过程可以看作是一个 UnionFS 的加载过程。

假设我们有一个包含三个层的镜像:一个基础层,一个中间层,和一个顶层。当我们基于这个镜像启动一个容器时,Docker 会:

  1. 加载基础层,这通常是一个操作系统的文件系统,如 Ubuntu 的文件系统。

  2. 加载中间层,这可能包含一些基本的工具和库,如 Python 解释器。

  3. 加载顶层,这通常包含我们的应用代码和配置。

  4. 添加一个可读写层,用于存储容器运行时的修改。

这四层被 UnionFS 挂载到同一个目录下,呈现为一个完整的文件系统给容器使用。当容器修改文件时,修改会被写入可读写层。

5. 镜像构建过程

Docker 镜像是通过 Dockerfile 文件来定义和构建的。Dockerfile 是一个文本文件,其中包含了一系列指令,告诉 Docker 如何构建镜像。在构建过程中,Docker 会读取 Dockerfile 中的指令,并按照指令的顺序逐步执行,最终生成一个新的镜像。

5.1 分层构建

Docker 的镜像构建过程是分层的。每执行一条 Dockerfile 指令,都会在当前镜像的顶部创建一个新的层。这种分层构建的机制有以下优点:

复用层:不同的镜像可以共享相同的层,减少磁盘占用和加速构建过程。

缓存:如果 Dockerfile 的指令和上下文没有改变,Docker 可以使用缓存的层,无需重新执行指令。

最小化变更:由于每个层都是独立的,对镜像的修改可以仅限于某些层,而不影响其他层。

举个例子,假设我们有以下的 Dockerfile

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

当我们构建这个镜像时,Docker 会:

  1. ubuntu:22.04 镜像开始,这是第一层。
  2. 执行 RUN 指令,在第一层的基础上创建第二层,该层包含了更新的软件包列表和安装的 Python3
  3. 执行 COPY 指令,在第二层的基础上创建第三层,该层包含了复制到镜像中的 app.py 文件。
  4. 执行 CMD 指令,在第三层的基础上创建第四层,该层指定了容器启动时运行的默认命令。

每一层都只包含了与前一层的差异部分,而不是完整的文件系统。这种分层构建的方式大大提高了构建和存储的效率。

5.2 缓存利用

为了加速构建过程,Docker 会尽可能地利用缓存。当我们重新构建一个镜像时,Docker 会检查每个指令的缓存情况:

  • 如果该指令和上下文没有改变,且存在可用的缓存层,Docker 会直接使用缓存层,而不会重新执行该指令。

  • 如果该指令或上下文发生了改变,Docker 会重新执行该指令,并为后续的指令invalidate缓存。

这意味着,如果我们修改了 Dockerfile 中的一条指令,该指令之后的所有指令都会被重新执行,而该指令之前的指令如果有缓存则会直接使用缓存。

=> 因此,为了最大限度地利用缓存,我们应该:

将最不“likely to change”的指令放在 Dockerfile 的前面,如 FROMLABEL 等。

将最likely to change的指令放在 Dockerfile 的后面,如 COPYADD 等。

合并 RUN 指令,以减少层数和利用缓存。

例如,不要这样写:

RUN apt-get update
RUN apt-get install -y python3

而应该这样写:

RUN apt-get update && apt-get install -y python3

这样,如果 apt-get update 的结果没有变化,apt-get install 就可以直接使用缓存,而不需要重新执行。

5.3 构建上下文

当我们执行 docker build 命令时,当前目录被称为构建上下文(build context)。Docker 会将构建上下文中的文件发送到 Docker daemon,daemon 根据 Dockerfile 中的指令构建镜像。

这意味着,Dockerfile 中的 COPYADD 指令只能复制构建上下文中的文件。如果我们试图复制上下文之外的文件,会得到一个错误。

为了减小构建上下文的大小,提高构建效率,我们应该:

Dockerfile 放在一个空目录或者项目根目录中。

使用 .dockerignore 文件排除不需要的文件和目录,如 .gitnode_modules 等。

避免使用 ADD 指令自动解压缩归档文件,而是在 RUN 指令中显式地解压缩。

例如,假设我们有以下的项目结构:

.
├── .git
├── .dockerignore
├── Dockerfile
├── app.py
└── README.md

我们可以在 .dockerignore 文件中添加以下内容:

.git
README.md

然后,我们的 Dockerfile 可以这样写:

FROM python:3.9
COPY app.py /app/
CMD ["python", "/app/app.py"]

这样,当我们执行 docker build 命令时,只有 app.py 文件会被发送到 Docker daemon,而 .git 目录和 README.md 文件会被排除在构建上下文之外,从而减小了构建上下文的大小,提高了构建效率。

6. 基础镜像与依赖

Docker 的镜像存储和复用机制是其高效性和灵活性的关键所在。通过巧妙的设计,Docker 在存储和运行镜像时,最大限度地节省了存储空间,提高了运行效率。

6.1 存储优化

得益于 Docker 的分层存储机制,相同的镜像层只需在磁盘上存储一次,不同的镜像可以共享这些层,从而大大节省了存储空间。

假设我们有两个镜像:镜像A和镜像B,它们都基于相同的基础镜像,如 Ubuntu

在此基础上,镜像A安装了 Python,而镜像B安装了 Python 和 Node.js。在 Docker 的存储中,这两个镜像的层结构可能如下:

镜像A:

Layer 3: Python
Layer 2: Ubuntu 基础镜像
Layer 1: Boot FS

镜像B:

Layer 4: Node.js
Layer 3: Python
Layer 2: Ubuntu 基础镜像
Layer 1: Boot FS

可以看到,Layer 1Layer 2 (即 Boot FSUbuntu 基础镜像) 在两个镜像中是完全相同的。Docker 在存储时,只需在磁盘上存储一份这两个层的数据,两个镜像都可以引用这两个层。这样,无论我们有多少个基于 Ubuntu 的镜像,Ubuntu 基础镜像层只需存储一次。

这种存储优化机制使得 Docker 镜像的存储非常高效。即使我们有大量的镜像,只要它们共享一些相同的层,实际占用的磁盘空间就会大大减少。

6.2 运行效率

Docker 的分层存储机制不仅优化了存储,也提高了镜像的运行效率。当我们从一个镜像启动容器时,Docker 只需要在镜像的顶部添加一个可写层,而镜像的其他层都是只读的,可以被多个容器共享。

这意味着,当我们启动多个基于相同镜像的容器时,这些容器可以共享镜像的只读层。这些只读层已经存在于本地磁盘上,无需重新下载或创建。Docker 只需为每个容器创建一个新的可写层。

这种机制大大加速了容器的启动过程。因为大部分数据都已经在本地镜像中准备好了,Docker 不需要在每次启动容器时都去下载或复制这些数据。

此外,由于容器共享镜像的只读层,启动多个容器并不会显著增加内存占用。每个容器只需要一些内存来维护自己的状态和可写层。

7. 基础镜像与依赖

Docker 的镜像构建过程中,基础镜像和软件依赖扮演着至关重要的角色。它们共同构成了应用运行所需的完整环境。

7.1 基础镜像(Base Image)

基础镜像是构建其他镜像的起点。它通常包含以下内容:

  • 操作系统的核心文件,如 Linux 的文件系统层次结构、基本命令和工具等。

  • 常用的系统库,如 glibcOpenSSL 等。

  • 包管理工具,如 apt、yum、apk 等,用于安装其他软件包。

常见的基础镜像如:

  • Ubuntu、Debian、CentOS 等通用操作系统镜像。

  • Alpine,一个面向安全的轻型 Linux 发行版,常用于构建最小化的镜像。

  • Busybox,一个集成了数百个 Unix 工具的单个可执行文件,常用于构建极小的镜像。

选择合适的基础镜像需要考虑以下因素:

  • 应用的兼容性:应用需要哪些特定版本的库和工具。

  • 镜像大小:选择最小化的基础镜像有助于减小最终镜像的大小。

  • 安全性:及时更新基础镜像,以包含最新的安全补丁。

7.2 软件依赖层

在基础镜像之上,我们需要安装应用运行所需的特定软件包和工具,如编程语言解释器、数据库、Web服务器等。这些软件依赖构成了镜像的上层。

管理软件依赖时应该注意:

  • 显式指定版本:在安装软件包时,明确指定所需的版本,以确保构建的可重复性。

  • 使用官方源:从官方源或可信的第三方源安装软件包,以确保软件包的完整性和安全性。

  • 清理缓存:安装完软件包后,删除下载的软件包缓存,以减小镜像的大小。

  • 合并层:尽可能将多个相关的操作合并到一个层中,以减少层数和镜像大小。

FROM python:3.11ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1RUN pip install --upgrade pip
RUN pip install django==4.2.1# 应用代码将在后续层中添加

在这个例子中,我们选择 python:3.11 作为基础镜像,它已经包含了 Python 3.11 解释器和 pip 包管理器。

  • 我们设置了两个环境变量:PYTHONUNBUFFEREDPYTHONDONTWRITEBYTECODE,以优化 Python 的运行表现。

  • 然后,我们使用 pip 升级了 pip 自身,并安装了指定版本(4.2.1)的 Django

  • 应用代码将在后续的层中通过 COPY 指令添加。

这个例子展示了如何在 Python 3.11 的基础镜像上,通过明确指定版本号的方式安装 Django 依赖,构建一个适用于 Django 应用的 Docker 镜像。

通过合理地组织基础镜像和软件依赖层,我们可以构建出结构清晰、易于维护、安全高效的 Docker 镜像,为运行 Django 应用提供一个稳定的环境。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探讨大米自动化生产线包装设备的智能化发展趋势

随着科技的飞速发展,智能化已经成为各行各业转型升级的重要方向。在大米生产领域,自动化生产线包装设备的智能化发展更是引领着粮食产业的未来潮流。星派将从智能化技术、市场需求、发展趋势等方面,探讨大米自动化生产线包装设备的智能化发展…

java图书电子商务网站的设计与实现源码(springboot+vue+mysql)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的图书电子商务网站的设计与实现。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 图书电子商…

鸿蒙ArkTS声明式开发:跨平台支持列表【按键事件】

按键事件 按键事件指组件与键盘、遥控器等按键设备交互时触发的事件,适用于所有可获焦组件,例如Button。对于Text,Image等默认不可获焦的组件,可以设置focusable属性为true后使用按键事件。 说明: 开发前请熟悉鸿蒙开…

嵌入式进阶——外部中断(EXTI)

🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 STC8H中断外部中断外部中断编写配置外部中断调用中断触发函数 外部中断测试测试外部中断0测试外部中断2、3或者4 PCB中断设计 STC8…

echarts取消纵坐标,自定义提示内容,完整 echarts 布局代码

效果图 实现代码 开启点击柱子时的提示内容 //完整写法请看下面tooltip: {trigger: axis,axisPointer: {type: shadow}},自定义提示内容 //完整写法请看下面formatter: function (param) {// param是悬浮窗所在的数据(x、y轴数据)let relVal "&…

【华为】将eNSP导入CRT,并解决不能敲Tab问题

华为】将eNSP导入CRT,并解决不能敲Tab问题 eNSP导入CRT打开eNSP,新建一个拓扑右键启动查看串口号关联CRT成功界面 SecureCRT连接华为模拟器ensp,Tab键不能补全问题选择Options(选项)-- Global Options (全局选项&#…

LangChain技术解密:构建大模型应用的全景指南

💂 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】🤟 一站式轻松构建小程序、Web网站、移动应用:👉注册地址🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交…

vue3父组件改变 子组件不改变(uniapp)

项目中遇到了这么个问题 场景:封装select组件,通过子组件选中后传递值给父组件,父组件需要回显这个值(这里使用 defineProps和defineEmits就可以实现,或者直接使用defineModel也可以实现,但是uniapp目前不…

学习编程对英语要求高吗?

学习编程并不一定需要高深的英语水平。我这里有一套编程入门教程,不仅包含了详细的视频讲解,项目实战。如果你渴望学习编程,不妨点个关注,给个评论222,私信22,我在后台发给你。 虽然一些编程资源和文档可能…

AI大模型在测试中的深度应用与实践案例

文章目录 1. 示例项目背景2. 环境准备3. 代码实现3.1. 自动生成测试用例3.2. 自动化测试脚本3.3. 性能测试3.4. 结果分析 4. 进一步深入4.1. 集成CI/CD管道4.1.1 Jenkins示例 4.2. 详细的负载测试和性能监控4.2.1 Locust示例 4.3. 测试结果分析与报告 5. 进一步集成和优化5.1. …

文件上传漏洞:pikachu靶场中的文件上传漏洞通关

目录 1、文件上传漏洞介绍 2、pikachu-client check 3、pikachu-MIME type 4、pikachu-getimagesize 最近在学习文件上传漏洞,这里使用pikachu靶场来对文件上传漏洞进行一个复习练习 废话不多说,开整 1、文件上传漏洞介绍 pikachu靶场是这样介绍文…

一键批量整理神器:轻松将相同名称文件归类至指定文件夹,告别繁琐文件管理!

信息爆炸的时代,电脑中的文件数量如潮水般涌现,管理起来令人头疼不已。您是否曾因为文件命名不规范而耗费大量时间寻找某个重要资料?是否曾因为文件散落各处而影响了工作效率?现在,我们为您隆重推荐一款文件管理神器—…

APM2.8如何供电

APM2.8飞控供电有两种, 1.电流计供电, 2.带BEC(稳压功能)的电调供电 飞控有一个JP1,它是一个供电选择接口,当插入跳线帽时,飞控用带BEC电调供电,当不插入时,用电流计供…

英语新概念2-回译法-lesson16

第一次回译 if you ___ your car on a wrong place, the traffic police man will find you quickly. If he do not give you the ticket,you are lucky.However,the ___ not all like this,The police man is __ sometimes.I had a holiday in Sweden, I found a ___ in my c…

《java数据结构》--顺序表详解

一.顺序表的概念🙉 🐱顺序表是一段物理地址连续的储存单元,一次储存数据元素的线性结构。一般情况下采用数组储存,和数组的增删查改类似。 但是顺序表和数组还是有区别的比如,数组按照是否可以扩容可以分为&#xff…

深入分析 Android Activity (八)

文章目录 深入分析 Android Activity (八)1. Activity 的资源管理1.1 使用资源 ID1.2 动态加载资源1.3 资源的本地化1.4 使用 TypedArray 访问资源 2. Activity 的配置变更处理2.1 在 Manifest 文件中声明配置变更2.2 重写 onConfigurationChanged 方法2.3 保存和恢复实例状态 …

网页图片加载慢的求解指南

网页/图片加载慢的求解指南 一、前言与问题描述 今天刚换上华为的HUAWEI AX3 Pro New,连上WIFI后测速虽然比平时慢,但是也不算太离谱,如下图所示: 估计读者们有也和作者一样,还没意识到事情的严重性😁。 …

Android Display Graphics #1 整体框架介绍一

软件基础 Android的framework层提供了一系列的图像渲染API,可绘制2D和3D。简单理解就是上层开发APP的小伙伴提供了接口,开发者可以直接显示对应的自己内容。但如果掌握了Display底层逻辑再写上层app,会有掌控力,出问题可以根据lo…

【ai】chatgpt的plugin已经废弃

发现找不到按钮,原来是要申请: https://openai.com/index/chatgpt-plugins/ 发现申请已经跳转了,好像是废弃了? 不接受新插件了,但是openai的api 是可以继续用的。 https://openai.com/waitlist/plugins/We are no longer accepting new Plugins, builders can now create…

医疗小程序源码SpringBoot2.X + Vue + UniAPP全栈开发

源码说明: 看到好多坛友都在求SpringBoot2.X Vue UniAPP,全栈开发医疗小程序 – 带源码课件,我看了一下,要么链接过期,要么课件有压缩密码。 特意整理了一份分享给大家,个人认为还是比较全面的。 希望…