Docker:关于 Dockerfile 编写优化的一些笔记整理
写在前面
- 分享一些
Dickerfile构建镜像优化方式的笔记 - 理解不足小伙伴帮忙指正
对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是随波逐流,是对内心的恐惧 ——赫尔曼·黑塞《德米安》
简单介绍
在 Docker 中,常用的自定义构建新镜像的方式有两种:
- 通过当运行的容器来构建新的镜像
- 通过
Dockerfile文件依托基础镜像来构建新的镜像
不管是那种方式,自定义镜像 的原理都是一样,通过镜像的分层设计,创建读写层 ,修改配置 ,重新打包
这里和小伙伴们分享一些 Dockerfile 构建自定义镜像的优化方式,所谓优化,也可以理解为相对较优的构建方式,对于 第一种,我们这里简单介绍
通过当运行的容器来构建新的镜像,一般在运行的镜像中做一些预制的操作,比如内网环境没有依赖库,没办法直接拉取需要的依赖,我们可以在有网络的环境下拉取对应的依赖,然后做成有依赖的基础镜像。
比如一个 python 镜像,我们要在内网中使用,但是内网环境没有 pip 源,所以我们只能把对应的包先在外网环境下载做成镜像。
运行的容器来构建新的镜像
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker run -d python python -m http.server 33333
009033ff4c0155f81647b857c0bf8975ee750a13d7aa2584638af032aafa758b
然后进入容器下载相关的依赖包,之后生成镜像导出
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker commit bcdd82ca5b48 my-python:latest
sha256:cb7c9965c541dfc794f78eb06ae1c4af0c77bb87c92e5e6e768c7770eb61a5bb
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker save my-python:latest -o ./my-python.tar
在操作上有些繁琐,使用 Dockerfile 的方式可能方便一点
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker build -<<EOF
> FROM python
> RUN python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
> RUN python -m pip install psycopg2
> EOF
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM python---> a5d7930b60cc
Step 2/3 : RUN python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple---> Running in 88140ad45551
Writing to /root/.config/pip/pip.conf
Removing intermediate container 88140ad45551---> df41fddd2cd2
Step 3/3 : RUN python -m pip install psycopg2---> Running in 1eddfbf7fa58
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting psycopg2Downloading https://pypi.tuna.tsinghua.edu.cn/packages/89/d6/cd8c46417e0f7a16b4b0fc321f4ab676a59250d08fce5b64921897fb07cc/psycopg2-2.9.5.tar.gz (384 kB)
Building wheels for collected packages: psycopg2Building wheel for psycopg2 (setup.py): started
.....14/44f32ab3b3f40f2e9a1a9ab8281a40ff4a911a930121c928b1
Successfully built psycopg2
Installing collected packages: psycopg2
.......
Removing intermediate container 1eddfbf7fa58---> 8791cb1dc692
Successfully built 8791cb1dc692
┌──[root@vms100.liruilongs.github.io]-[~]
└─$
忘记打标签了。这里我们手动打一下。可以在 build 的时候通过 -t 命令指定
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker tag 8791cb1dc692 my-python:latest
┌──[root@vms100.liruilongs.github.io]-[~]
└─$docker images my-python
REPOSITORY TAG IMAGE ID CREATED SIZE
my-python latest 8791cb1dc692 8 minutes ago 927MB
Dockerfile 自定义镜像
构建常用指令温习
FROM:基础镜像RUN:制作镜像时执行的命令,可以有多个,每个命令一层ADD:复制文件到镜像,自动解压 (文件类型为: tar.gz 或 tar.bz2)COPY:复制文件到镜像,不解压MAINTAINER:镜像创建者信息EXPOSE:开放的端口ENV:设置变量WORKDIR:定义容器默认工作目录CMD: 容器启动时执行的命令,仅可以有一条CMD.ENTRYPOINT:类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一具单独的可执行程序
一些需要注意的事项:
-
当
docker run命令中声明了参数时,Docker 守护程序会忽略 CMD 命令。 -
与
CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定的程序。不过,docker run命令的–entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序 -
Dockfile中,如果没有使用CMD指定启动命令,则会继承上一个镜像的默认启动命令;CMD容器的默认启动命令,有且只能有一条;
根据Dockerfile生成新的镜像命令中,build 创建新的镜像;-t 指定新镜像的名字和标签;. 指定Dockerfile文件所在的目录
docker build -t imagename:latest Dockerfile所在目录
容器和镜像之间的主要区别在于顶部 可写层。对容器的所有添加新数据或修改现有数据的写入都存储在此 可写层 中。删除容器时,可写层也会被删除。基础镜像保持不变。
这里利用了 写时复制技术(COW,copy on write) , 对于开发的小伙伴,可以结合 享元设计模式 理解,对于运维的小伙伴,可以结合 Openstack 组件 Glance 原理来理解
用通俗的话讲,当修改时,会把数据复制到容器层修改。当新增的时候直接在 容器层新增,当删除时,会屏蔽镜像层。
Docker 通过读取给定的指令来自动构建镜像。遵循特定的格式和指令集,其中的 每一条指令在容器镜像中创建一个层。这些层是堆叠的,每个层都是与前一层相比的变化的增量
这里我们以 redis:7 这个官方镜像为例,看看一个标准的 Dockerfile 如何书写,可以看到镜像构建了 16 层。
┌──[root@vms107.liruilongs.github.io]-[/etc/systemd]
└─$docker history --human=true redis:7
IMAGE CREATED CREATED BY SIZE COMMENT
19c51d4327cf 6 weeks ago /bin/sh -c #(nop) CMD ["redis-server"] 0B
<missing> 6 weeks ago /bin/sh -c #(nop) EXPOSE 6379 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 6 weeks ago /bin/sh -c #(nop) COPY file:e873a0e3c13001b5… 661B
<missing> 6 weeks ago /bin/sh -c #(nop) WORKDIR /data 0B
<missing> 6 weeks ago /bin/sh -c #(nop) VOLUME [/data] 0B
<missing> 6 weeks ago /bin/sh -c mkdir /data && chown redis:redis … 0B
<missing> 6 weeks ago /bin/sh -c set -eux; savedAptMark="$(apt-m… 32MB
<missing> 6 weeks ago /bin/sh -c #(nop) ENV REDIS_DOWNLOAD_SHA=06… 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENV REDIS_DOWNLOAD_URL=ht… 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENV REDIS_VERSION=7.0.8 0B
<missing> 7 weeks ago /bin/sh -c set -eux; savedAptMark="$(apt-ma… 4.13MB
<missing> 7 weeks ago /bin/sh -c #(nop) ENV GOSU_VERSION=1.14 0B
<missing> 7 weeks ago /bin/sh -c groupadd -r -g 999 redis && usera… 329kB
<missing> 7 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:e2398d0bf516084b2… 80.5MB
┌──[root@vms107.liruilongs.github.io]-[/etc/systemd]
└─$docker history --human=true redis:7 | wc -l
17

涉及 两个 Dockerfile 文件构建的镜像
基础镜像构建
FROM scratch
ADD rootfs.tar.xz /
CMD ["bash"]
reids 镜像构建
FROM debian:bullseye-slim# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r -g 999 redis && useradd -r -g redis -u 999 redis# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.16
RUN set -eux; \savedAptMark="$(apt-mark showmanual)"; \apt-get update; \apt-get install -y --no-install-recommends ca-certificates dirmngr gnupg wget; \rm -rf /var/lib/apt/lists/*; \dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \export GNUPGHOME="$(mktemp -d)"; \gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \gpgconf --kill all; \rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \apt-mark auto '.*' > /dev/null; \[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \chmod +x /usr/local/bin/gosu; \gosu --version; \gosu nobody trueENV REDIS_VERSION 7.0.9
ENV REDIS_DOWNLOAD_URL http://download.redis.io/releases/redis-7.0.9.tar.gz
ENV REDIS_DOWNLOAD_SHA f77135c2a47c9151d4028bfea3b34470ab4d324d1484f79a84c6f32a3cfb9f65RUN set -eux; \\savedAptMark="$(apt-mark showmanual)"; \apt-get update; \apt-get install -y --no-install-recommends \ca-certificates \wget \\dpkg-dev \gcc \libc6-dev \libssl-dev \make \; \rm -rf /var/lib/apt/lists/*; \\wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL"; \echo "$REDIS_DOWNLOAD_SHA *redis.tar.gz" | sha256sum -c -; \mkdir -p /usr/src/redis; \tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1; \rm redis.tar.gz; \\
# disable Redis protected mode [1] as it is unnecessary in context of Docker
# (ports are not automatically exposed when running inside Docker, but rather explicitly by specifying -p / -P)
# [1]: https://github.com/redis/redis/commit/edd4d555df57dc84265fdfb4ef59a4678832f6dagrep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' /usr/src/redis/src/config.c; \sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' /usr/src/redis/src/config.c; \grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' /usr/src/redis/src/config.c; \
# for future reference, we modify this directly in the source instead of just supplying a default configuration flag because apparently "if you specify any argument to redis-server, [it assumes] you are going to specify everything"
# see also https://github.com/docker-library/redis/issues/4#issuecomment-50780840
# (more exactly, this makes sure the default behavior of "save on SIGTERM" stays functional by default)\
# https://github.com/jemalloc/jemalloc/issues/467 -- we need to patch the "./configure" for the bundled jemalloc to match how Debian compiles, for compatibility
# (also, we do cross-builds, so we need to embed the appropriate "--build=xxx" values to that "./configure" invocation)gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \extraJemallocConfigureFlags="--build=$gnuArch"; \
# https://salsa.debian.org/debian/jemalloc/-/blob/c0a88c37a551be7d12e4863435365c9a6a51525f/debian/rules#L8-23dpkgArch="$(dpkg --print-architecture)"; \case "${dpkgArch##*-}" in \amd64 | i386 | x32) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=12" ;; \*) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=16" ;; \esac; \extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-hugepage=21"; \grep -F 'cd jemalloc && ./configure ' /usr/src/redis/deps/Makefile; \sed -ri 's!cd jemalloc && ./configure !&'"$extraJemallocConfigureFlags"' !' /usr/src/redis/deps/Makefile; \grep -F "cd jemalloc && ./configure $extraJemallocConfigureFlags " /usr/src/redis/deps/Makefile; \\export BUILD_TLS=yes; \make -C /usr/src/redis -j "$(nproc)" all; \make -C /usr/src/redis install; \\
# TODO https://github.com/redis/redis/pull/3494 (deduplicate "redis-server" copies)serverMd5="$(md5sum /usr/local/bin/redis-server | cut -d' ' -f1)"; export serverMd5; \find /usr/local/bin/redis* -maxdepth 0 \-type f -not -name redis-server \-exec sh -eux -c ' \md5="$(md5sum "$1" | cut -d" " -f1)"; \test "$md5" = "$serverMd5"; \' -- '{}' ';' \-exec ln -svfT 'redis-server' '{}' ';' \; \\rm -r /usr/src/redis; \\apt-mark auto '.*' > /dev/null; \[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \find /usr/local -type f -executable -exec ldd '{}' ';' \| awk '/=>/ { print $(NF-1) }' \| sort -u \| xargs -r dpkg-query --search \| cut -d: -f1 \| sort -u \| xargs -r apt-mark manual \; \apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \\redis-cli --version; \redis-server --versionRUN mkdir /data && chown redis:redis /data
VOLUME /data
WORKDIR /dataCOPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]EXPOSE 6379
CMD ["redis-server"]
运行此镜像并创建一个容器时,我们实际上在底层之上添加了一个新的可写层(容器层)。对正在运行的容器所做的所有更改(例如写入新文件、修改现有文件和删除文件)都将写入此可写容器层。
可以看到上面的 Dockerfile 文件很庞大,通过这个文件我们来总结一些 在编写时需要注意的地方
Dockerfile 编写优化
使用小的基础镜像
在上面的 Dockerfile 中基础镜像使用有FROM debian:bullseye-slim, FROM scratch
scratch是一个空镜像,一般用不到,在构建最基础的镜像的时候会用到。debian:bullseye-slim是一个debian系统的bullseye版本的精简版镜像。看的出来,使用的镜像很小。
使用较小的镜像可以更快地构建、推送和拉取 镜像。往往更安全,因为只包含运行应用程序所需的必要库和系统依赖项。 尤其是在 CI/CD 等流水线中,庞大的 基础镜像 在每个环节都要消耗一些时间,从而使流水线的时间变得很长。镜像之间的区别主要在于底层的操作系统
镜像选择类型
Official Image:官方镜像,或者叫标准镜像,一般由官方维护的镜像,它是正确的选择,但是可能不是最优的。镜像基于最新的稳定 Debian 操作系统发行版,上面的 有 Dockerfile 构建完成的镜像即为 redis:7 的一个官网镜像。

Debian(bullseye/buster/stretch/jessie):不同的 Debian Linux 发行版镜像,jessie(8.0),stretch(9.0) 是比较老旧的版本,buster(10.0) ,bullseye(11.0)为较新的版本
slim:精简版,它通常会安装运行特定工具所需的最小包
alpine:基于 Alpine Linux项目,专门为在容器内部使用而构建的操作系统。相比较 Debian 来说 Alpine 很小很小,但是需要考虑一些时区,兼容性问题。
scratch: 一个明确的空镜像,特别是用于建立 “从头开始” 的镜像。
在选择最小基础镜像的同时,要尽量避免安装不必要的软件包
指令链式运行
可以很明显的发现,上面 Dockerfile 中的 RUN 指令很长
这是由于每个指令都会创建一个可缓存单元并构建一个新的中间镜像层。所以可以通过链接所有命令来避免过多层级。此外,尽量避免链接过多的可缓存 RUN 命令,因为这会导致创建大型缓存并最终导致缓存突发。
RUN set -eux; \\savedAptMark="$(apt-mark showmanual)"; \apt-get update; \apt-get install -y --no-install-recommends \ca-certificates \wget \\dpkg-dev \gcc \libc6-dev \libssl-dev \make \; \rm -rf /var/lib/apt/lists/*; \\wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL"; \..................
变动的指令放到最后
Dockerfile 编写往往需要重复的构建,每个层的构建都比较耗时,但是 Docker 为了加快后续构建的速度,会自动缓存每一层的构建,当对应的层指令以及前面的指令没有发生变动时,会直接使用缓存。当步骤对应指令更改时,缓存不仅会针对该特定步骤失效,还会使所有后续步骤失效。
所以 始终将最常更改的指令放在末尾。 会提高构建速度.
FROM python:3.9-slimWORKDIR /app
COPY requirements.txt .
RUN pip install -r /requirements.txt
COPY app.py .
首选数组而不是字符串语法
我们可以通过两种不同的方式编写 最后的进程启动 命令 ENTRYPOINT
- 数组:
ENTRYPOINT ["python","-m","http.server","33333"] - 字符串:
ENTRYPOINT "python -m http.server 33333"
数组形式是首选。这是因为使用字符串形式会导致 Docker 使用 bash 运行您的进程,这无法正确处理信号。由于大多数 shell 不处理子进程的信号,因此如果使用 shell 格式,CTRL-C(生成 SIGTERM)可能不会停止子进程。
COPY 而不是 ADD
如果有多个步骤使用上下文中的不同文件,请 单独复制 它们,而不是一次全部复制。这可确保每个步骤的生成缓存仅失效,并在特别需要的文件发生更改时强制重新运行该步骤。
COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
使用 .dockerignore
.dockerignore 文件的作用类似于 git 工程中的 .gitignore 。不同的是 .dockerignore 应用于 docker 镜像的构建,它存在于 docker 构建上下文的根目录,用来忽略不需要打入镜像的文件
.dockerignore 文件的写法和 .gitignore 类似,支持正则和通配符,具体规则如下:
- 每行为一个条目;
- 以 # 开头的行为注释;
- 空行被忽略;
- 构建上下文路径为所有文件的根路径;
.git
script
static
!README*.md
从 stdin 标准输入构建
Docker 引擎能够通过本地或远程构建上下文通过 stdin 管道传输 Dockerfile 来构建镜像
在 Dockerfile 不需要将文件复制到镜像中(COPY/ADD 将失败)的情况下,省略构建上下文非常有用,并且可以提高构建速度,因为不会将任何文件发送到 Docker 守护程序。适用于单纯的镜像构建
┌──[root@vms107.liruilongs.github.io]-[/etc/systemd]
└─$docker build -<<EOF
> FROM busybox
> RUN echo "hello world"
> EOF
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM busybox
latest: Pulling from library/busybox
5cc84ad355aa: Pull complete
Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
Status: Downloaded newer image for busybox:latest---> beae173ccac6
Step 2/2 : RUN echo "hello world"---> Running in c56fb8343c72
hello world
Removing intermediate container c56fb8343c72---> 95bc7e444353
Successfully built 95bc7e444353
┌──[root@vms107.liruilongs.github.io]-[/etc/systemd]
└─$
利用多阶段构建
多阶段构建使我们能够通过利用构建缓存大幅减小最终镜像的大小,而无需努力减少中间层和文件的数量。例如,让我们看一下以下内容:Dockerfile
FROM golang:1.18-alpine AS prebuild# Install tools required for project
RUN go get github.com/golang/dep/cmd/dep
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
RUN go build -o /bin/project# This results in a single layer image
FROM scratch
COPY --from=prebuild /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
可以使用多个语句。每个指令都可以使用不同的基础,并且每个指令都开始构建的新阶段。我们可以有选择地将伪影从一个阶段复制到另一个阶段,在最终镜像中留下我们不想要的所有内容。
#syntax=docker/dockerfile:1.4
FROM … AS build1
COPY –from=app1 . /srcFROM … AS build2
COPY –from=app2 . /srcFROM …
COPY –from=build1 /out/app1 /bin/
COPY –from=build2 /out/app2 /bin/
博文部分内容参考
文中涉及参考链接内容版权归原作者所有,如有侵权请告知
https://www.docker.com/
https://docs.docker.com/engine/reference/builder/
https://www.docker.com/blog/dockerfiles-now-support-multiple-build-contexts/
https://blog.devgenius.io/devops-in-k8s-write-dockerfile-efficiently-37eaedf87163
© 2018-2023 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)
相关文章:
Docker:关于 Dockerfile 编写优化的一些笔记整理
写在前面 分享一些 Dickerfile 构建镜像优化方式的笔记理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式&#…...
个性化营销:您需要知道的信息
个性化营销在现代企业中风靡一时。我们将剖析您需要了解的有关个性化营销的信息,一起来了解一下吧。 什么是个性化营销? 个性化营销是一种一对一营销形式,它使用实时用户数据和分析来传递品牌信息并针对特定潜在客户。 它与传统营销不同&…...
栈和队列的相互实现
文章目录一、用栈实现队列入队:出队:Java代码实现:二、用队列实现栈入栈:出栈:Java代码实现:附:C版代码1、用栈实现队列2、用队列实现栈栈(stack):先进后出&a…...
iTab新标签页重磅更新 |这些功能绝对有你想要的新体验!
01 写在前面 csdn的朋友们,你好哦,我是iTab 插件的独立开发者,今天给大家安利一下我做的这款桌面插件。 首先要告诉大家一个好消息: 最近iTab新标签页被Edge 浏览器商店官方热门🔥推荐啦。 在此,特别感谢…...
【改机教程】iOS系统去除小黑条,改拍照声、拨号音、键盘音,不用越狱,支持所有机型
大家好,上次给大家分享了几个iOS系统免越狱改机教程 今天带来最新的教程,这次修改利用的是同一个漏洞,由外网大神 tamago 开发,国内大神冷风 进行汉化和优化 可以修改的地方包括 去除底部小黑条 dock栏透明 桌面文件夹透明 桌面…...
Android10开机向导中复用设置中的Wifi界面
很多时候我们需要定制开机向导,在开机向导界面我们一般会实现联网和设置时间等功能,考虑复用与稳定性问题,我们最好复用设置中的WiFi设置和日期设置。但是设置中的wifi设置界面默认是没有下一步按钮的,这会让用户感觉很奇怪。在以…...
川农机械专业小伙转行Java开发,年薪20w
本期学员就业故事,知了姐邀请到一位“特别”的同学,一位从知了堂就业成功近两年的学员再度接受我们的采访。 来自四川农业大学的曾同学,一个本来学机械开挖掘机的粗犷男人,因为不断地努力学习编程,最终成为一个性格闷…...
华为OD机试题 - 打印文件(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:打印文件题目输入输出示例一输入输出示例二输入输出Code代码解析…...
免费常用API大全,程序员必备
淘宝接口 淘宝开放平台 http://open.taobao.com/?spma219a.7395905.1.1.YdFDV6 APISpace 生活常用 今天吃什么:随机返回一顿美味食物,解决你今天吃什么的难题。 星座查询:根据日期或星座名称,查询星座详细信息,包…...
MySQL主从复制,读写分离
目录 一、MySQL主从复制介绍 MySQL复制过程分成三步 二、主库配置master 1、步骤1 2、第二步:重启Mysql服务 3、第三步:登录Mysql数据库,执行下面SQL 4、第四步:登录Mysql数据库,执行下面SQL,记录下结果中File和…...
什么是UEFI签名认证?UEFI签名有什么好处?
为了防御恶意软件攻击,目前市面上所有电脑设备启动时默认开启安全启动(Secure Boot)模式。安全启动(Secure Boot)是UEFI扩展协议定义的安全标准,可以确保设备只使用OEM厂商信任的软件启动。UEFI签名认证就是对运行在 UEFI 系统下的 efi 驱动和通过 UEFI …...
案例14-课程推送页面逻辑整理--vue
目录一级目录二级目录三级目录一、背景介绍二、问题分析问题1:逻辑边界不清晰,封装意识缺乏问题问题2:展示效果上的问题三、解决过程问题一 代码结构混乱问题解决问题二 代码结构混乱问题解决问题三 展示效果上的细微问题四、总结一级目录 二…...
5大GPU厂商共建 | openKylin社区GPU SIG首次例会召开!
3月8日,openKylin社区GPU SIG首次例会以线上形式召开。此次会议由长沙景美集成电路设计有限公司、摩尔线程智能科技(北京)有限责任公司、格兰菲智能科技有限公司、象帝先计算技术(重庆)有限公司等GPU厂商的多位SIG Mai…...
SpringBoot读取配置文件
目录一、简介1、SpringBoot 中常用读取配置方法2、 ConfigurationProperties和Value的区别二、使用 ConfigurationProperties 读取配置三、使用 Value 读取配置一、简介 在日常开发使用 SpringBoot 框架时,经常有一些配置信息需要放置到配置文件中,我们…...
51驱动NRF24L01通信,NRF24L01与TTL转NRF24L01模块通信
51驱动NRF24L01通信,NRF24L01与TTL转NRF24L01模块通信NRF24L01一、简介二、引脚功能描述程序设计一、对 24L01 的程序编程的基本思路如下:二、Tx 与 Rx 的配置过程1、Tx 模式初始化过程:2、Rx 模式初始化过程:三、基本程序函数通信…...
C++友元
欢迎来观看温柔了岁月.c的博客 目前 设有C学习专栏 C语言项目专栏 数据结构与算法专栏 目前主要更新C学习专栏,C语言项目专栏不定时更新 待C专栏完毕,会陆续更新C项目专栏和数据结构与算法专栏 一周主要三更,星期三,星期五&#x…...
MySQL内置函数
文章目录日期函数字符串函数数学函数其他函数日期函数 获取年月日: mysql> select current_date(); ---------------- | current_date() | ---------------- | 2023-02-01 | ---------------- 1 row in set (0.00 sec)获得时分秒: mysql> se…...
mysql数据库之innodb存储引擎架构之内存架构
一、逻辑存储结构 mysql5.5版本开始,默认使用innodb存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。 架构图(左侧为内存架构,右侧为磁盘架构) 二、 内存架构。 1、缓冲…...
Vue:(三十五)路由vue-router
今天,我们开始学习vue中一个很关键的知识点,路由。理解vue的一个插件库,专门用来实现SPA应用单页web应用整个应用只有一个完整的页面点击页面中的导航连接不会刷新页面,只会做页面的局部更新数据需要通过ajax请求获取下来…...
Dynabook笔记本电脑无法开机怎么重装新系统?
Dynabook笔记本电脑无法开机怎么重装新系统?有用户使用Dynabook笔记本电脑出现了无法正常开机的情况。遇到这样的问题是我们的电脑系统出现了损坏,可以尝试进行系统修复。如果无法修复的话,就需要进行系统重装了。以下为大家带来Dynabook笔记…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...
初级程序员入门指南
初级程序员入门指南 在数字化浪潮中,编程已然成为极具价值的技能。对于渴望踏入程序员行列的新手而言,明晰入门路径与必备知识是开启征程的关键。本文将为初级程序员提供全面的入门指引。 一、明确学习方向 (一)编程语言抉择 编…...
Spring Boot 中实现 HTTPS 加密通信及常见问题排查指南
Spring Boot 中实现 HTTPS 加密通信及常见问题排查指南 在金融行业安全审计中,未启用HTTPS的Web应用被列为高危漏洞。通过正确配置HTTPS,可将中间人攻击风险降低98%——本文将全面解析Spring Boot中HTTPS的实现方案与实战避坑指南。 一、HTTPS 核心原理与…...
智能问数Text2SQL Vanna windows场景验证
架构 Vanna 是一个开源 Python RAG(检索增强生成)框架,用于 SQL 生成和相关功能。 机制 Vanna 的工作过程分为两个简单步骤 - 在您的数据上训练 RAG“模型”,然后提出问题,这些问题将返回 SQL 查询,这些查…...
