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笔记…...
React Native基础知识点
1、组件 与react编写web应用不同,不是使用div、span等标签。而是使用RN官方提供的组件,如View、Text等组件来搭建页面 2、宽高 React Native 中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。默认值为auto <View style{{…...
nginx 平滑升级
背景介绍 因为一些原因,比如说 Nginx 发现漏洞、应用一些新的模块等等,想对 Nginx 的版本进行更新,最简单的做法就是停止当前的 Nginx 服务,然后开启新的 Nginx 服务。但是这样会导致在一段时间内,用户是无法访问服务…...
数据结构——链表OJ题目讲解(2)
作者:几冬雪来 时间:2023年3月10日 内容:数据结构链表OJ题目讲解 来源:牛客网和力扣 目录 前言: 刷题: 1.反转链表: 1.改变指向的解法: 2.取头结点插入到新链表: …...
GitHub上线重量级分布式事务笔记,再也不怕面试官问分布式了
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式…...
C++语法规则1(C++面向对象 )
C面向对象 面向对象的三大特征是继承,多态和封装,C重面向对象重要的就是这些,我们下面通过一些简单的实例加以理解,从这小节开始,我们将开启新的编程旅途。与 C 语言编程的思想完全不同了,这就是 C!理解概…...
Web漏洞-CSRF漏洞
CSRF漏洞介绍:CSRF(Cross-Site Request Forgery),中文名称:跨站请求伪造,是一种劫持用户在当前已登录的Web应用程序上执行非本意操作一种攻击.原理:攻击者利用目标用户的身份,执行某…...
Python3-面向对象
Python3 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象…...
拐点!新能源车交付均价首次「低于」燃油车,智能电动成新爆点
2023年开局,随着特斯拉打响新能源汽车市场的「价格战」首炮,除部分燃油车品牌(仍依赖自身多年的用户和品牌积累的溢价能力)没有跟进之外,几乎所有的新能源车型都在进行车型价格的下调。 而数据也在反映市场的拐点即将来…...
JavaScript String 字符串对象实例合集
文章目录JavaScript String 字符串对象实例合集返回字符串的长度为字符串添加样式返回字符串中指定文本首次出现的位置 - indexOf()方法查找字符串中特定的字符,若找到,则返回该字符 - match() 方法替换字符串中的字符 - replace()JavaScript String 字符…...
实习生培养计划
部门最近入职了实习生,为了更好的帮助实习生完成由学生向职业人的转变,并尽快融入企业稳步成长,现提出实习生培养计划,具体方案如下: 1、方案目的 1、使实习生快速转换角色与心态,适应从校园到企业的坏境…...
wordpress会员点数/上海网站关键词排名
白天看这一章节的时候废了很大功夫,但是没理清楚,一度很沮丧.快凌晨吃了宵夜,分分钟搞明白了.TSSLinux没有使用intel原始预设的为每个进程设置一段TSS这中方案作为进程切换时保存堆栈信息,CPU寄存器等进程上…...
web旅游网站开发论文/王通seo教程
2019独角兽企业重金招聘Python工程师标准>>> 题目:输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建新的结点,只能调整树中结点指针的指向。 比如如下图中的二叉搜索树,则输出转换之后的排序…...
一个公司多个网站做优化/网络seo公司
php将后门隐藏的方法:首先创建系统隐藏文件,并创建ADS隐藏文件;然后通过包含文件的方式来使用jpd;最后将【index.php:shell.jpg】hex编码即可。php将后门隐藏的方法:一. attrib s h创建系统隐藏文件。attrib s a r h /…...
搜索引擎中 哪些网站可以获得更好的排名/制作网站的软件叫什么
项目截图群聊:私聊:项目技术栈可以看到项目中用到的都是技术都是很常用技术,同时也是我们需要掌握的能力。而且 crossoverJie 大佬的代码写的也相当漂亮,非常值得学习一波。先来简单看一下系统整体架构!系统架构下面我…...
自己做的网站放到首页/seo营销培训咨询
首先我们得知道什么叫shell.操作系统与外部最主要的接口就叫做shell,或许这样说过于术语化,例如cmd命令行,这是windows操作系统下的一个shell。这里说的dreamhost的shell是指dreamhost提供给客户一个命令行,方便客户更好的使用他们的虚拟主机…...
网站开发方案设计/营销网站建设选择原则
一年一度的亚洲科技盛会-云栖大会将于 2022 年 11 ⽉ 3 -5 ⽇在杭州云栖小镇国际会展中心举办,峰会现场将邀请 6 万人次嘉宾参会,线上 2000 万泛科技人群观看,覆盖十多个行业。更有为期 3 天的数智沉浸极致体验,小龙将在云栖大会期…...