在舊版 Nginx 官方 Dockerfile 上集成第三方模塊的探索

問題背景

線上生產環境用的 nginx 1.21, 然后由于新功能引入的一個問題,需要使用第三方模塊 ngx_http_subs_filter_module,目的是使用正則表達式來移除響應結果中的某些數據。

由于這個客戶的環境非常重要,組內的大哥們也不敢隨便升級 nginx 的版本,所以強制要求必須是用當前線上
Dokcer 正在跑的 nginx 1.21 鏡像同樣的 Dockerfile 來集成第三方模塊后重新打包一個鏡像。

這塊工作難就在于需要做到最小改動,盡可能不去修改太多的地方,以免造成無法預料的影響。

文末有原版 Dockerfile
?

啟程:版本調研

首先在 Dockerhub 上面找到相應的鏡像主頁,通過頁面上的鏈接直接跳轉到這個鏡像使用的 Dockerfile
頁面

https://hub.docker.com/layers/library/nginx/1.21/images/sha256-25dedae0aceb6b4fe5837a0acbacc6580453717f126a095aa05a3c6fcea14dd4?context=explore

在這里插入圖片描述
?
跳轉后的倉庫頁面鎖定在了 mainline/debian 目錄下面,我們將這幾個文件拷貝出來進行打包

在這里插入圖片描述

?

直接打包鏡像

首先我們不對原有的 Dockerfile 做任何修改,直接進行打包看有沒有什么問題。

打包命令:

docker build \--build-arg http_proxy=http://xxx:7890 \--build-arg https_proxy=http://xxx:7890 \-t test-nginx:1.0 . --no-cache 2>&1 | tee build.log

上面那條命令中我們使用了 --build-arg 這個命令行參數,他的作用是配置僅在打包運行時可見的環境變量,打包結束后不會留存在鏡像中。

配置 http_proxyhttps_proxy 是為了讓 docker 在打包時走我們本機的國外代理,加快依賴包的下載速度。也可以直接在國外的服務器上進行打包。
?

問題1: GPG Key獲取失敗

留意以下的日志,可以發現打包過程中,腳本在不斷的嘗試從 GPG 服務器中獲取 Key, 但以失敗告終。

在這里插入圖片描述

在這里插入圖片描述

?
我們通過觀察原始的 Dockerfile, 可以在第 21:29 行找到循環獲取 Key 的腳本命令。
在這里插入圖片描述

?
可以看到這里 nginx 官方配置了兩個服務器:

hkp://keyserver.ubuntu.com:80
pgp.mit.edu

我自己一開始也是在網上找了很多的博客,其中 80% 都是讓你配置多幾個備選服務器到腳本里面增大 Key 的獲取成功率。但不出意外,這些方法全都解決不了這里的問題。

最終我還是回到報錯中收集更多的細節信息:

首先我在 DockerHub 上面看到 1.21 鏡像的 dockerfile 已經是兩年前的版本了,所以其中的一些腳本或許多多少少都有些問題。

在這里插入圖片描述

?
接著我們從報錯日志中可以一眼看到一個警告:

Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).

在這里插入圖片描述
?

這里并沒有報錯,報錯的是從 GPG 服務器獲取 Key 失敗。但是我們前面探索過,加更多服務器也沒用。然后這個警告是說 apt-key 已經被廢棄,而我們從原 Dockerfile 里面可以找到調用這個工具的腳本:

28: apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break;

可以得知,從服務器獲取 Key 的操作使用 apt-key 這個工具完成的。那么問題的定位差不多可以有個結論:

棄用了的工具 apt-key 影響了 GPG 獲取 Key 的流程
?

探索

我們定位到了問題,那么自然想到的方式是升級工具,然后更新 Key 獲取腳本,這里引出了三個小問題:

  1. 替換 apt-key 的工具是什么?
  2. 替換工具后的腳本要做什么改動?
  3. 替換聲明是否來自官網更新文檔?

這里要嘮叨幾句:

首先我也是初學 docker,平日里沒有太多精力再去關注 dockernginx 的社區,可能有活躍的網友知道怎么改在某一個博客中提及了。但是我到目前為止沒有看到問題和我這個完全一樣的,所以對那些解決方案我都是持質疑態度,我個人一般是信奉官方文檔和 API 定義多一些。

而且說來慚愧,我在定位到是工具問題之前,已經是花了一整天來搜索各種博客的解決方案。但最終沒有一個能完美解決我的問題,一天就這么浪費了。由于這個集成方案的探索在下一周就要部署到客戶現場,所以浪費的這一天也讓我從這里開始到整個問題探索結束,都不會再去看網上的那些博客。同時我也意識到我這個問題應該網上也不會有很好的解決方案。

基于此,我選擇直接去問官方人員,他們最清楚該改哪里。幸運的是,我在 nginxgithub 上提了 issue 后,官方人員第二天就回復我了,這里必須點一個大大的贊。而且官方人員明確指出了我用的 Dockerfile 太老舊了,他們早就替換了新版的腳本,并給出 commit 給我去參考,真的感謝。

在這里插入圖片描述

?
改動的 commit:
https://github.com/nginxinc/docker-nginx/commit/38e2690b304b8dca4848f3e70a1fc95837f61510

在管理員提供的 commit 中, 他們把請求 Key 的工具從 apt-key 換成了 gpg1, 并對原始的 Dockerfile 進行了一些修改,我們照葫蘆畫瓢就行。

在這里插入圖片描述

得益于管理員的幫助,問題一完美解決!
?

插曲, 暫時對 Dokcerfile 進行分層加速調試

學習過 DockerfileRUN 命令就知道,每個 RUN 命令都會建立一個緩存層,這樣在執行完一條
RUN 命令后,只要不修改其之前和自己的腳本命令,下次執行時就不用再次等待執行。

而在網絡上的大多數官方鏡像的 Dockerfile 中, 我們會發現 Dockerfile 中往往只有一條 RUN
命令。這是因為為了建立緩存關系,每條 RUN 都會在當前緩存層中加東西,這樣會增加每個緩存層的大小,
使得最終打包出來的鏡像的大小也很大。這是非常不利于官方鏡像的傳輸的,尤其是一些基礎服務的鏡像。試想
若是一個簡單的服務鏡像就要 7 個 G, 還會有用戶愿意去使用嗎?

但在本問題的討論中,我們是要對nginx官方的 Dockerfile 進行一個 min(max(Dockerfile)) 的操作(哈哈我覺得用函數來說明更貼切,在最小改動基礎上最大幅度改動),這是一個不斷試錯的過程,可能看我博客里面寫運行一條
cmd 得到了下圖結果。但是在獲得這個結果截圖之前,我其實是在不斷嘗試錯誤的指令。

那么為了減少時間的浪費,我們先分析原有的 RUN, 看看能在哪些地方拆開,避免重復執行一些步驟。
?

將獲取 Key 的指令獨立成一條 RUN

我們看下面從改動過可以正常獲取 Key 的 Dockerfile 中觀察到的三條腳本:

NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; # 24 行
NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; # 25 行
gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; # 36 行

可以發現這里是設置了兩個環境變量,一個是 GPGKEY 的值,一個是 Key 的路徑。
最后一條腳本是傳入 Key 值給工具 gpg1 然后導出內容到指定的路徑中,至于什么內容這里不關心。
可以發現這里的產物最終放在了指定的一個目錄中,且和下面其他腳本的運行沒有太多顯示的交集,那么我們
就可以把這塊邏輯分離成一個 RUN, 最終經過一次改動的腳本摘要如下:

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#
FROM debian:bullseye-slimLABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"ENV NGINX_VERSION   1.21.6
ENV NJS_VERSION     0.7.6
ENV PKG_RELEASE     1~bullseyeRUN set -x \
# create nginx user/group first, to be consistent throughout docker variants&& addgroup --system --gid 101 nginx \&& adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \&& apt-get update \&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \ && NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; \export GNUPGHOME="$(mktemp -d)"; \found=''; \for server in \hkp://keyserver.ubuntu.com:80 \pgp.mit.edu \; do \echo "Fetching GPG key $NGINX_GPGKEY from $server"; \gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \done; \test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; \rm -rf "$GNUPGHOME"; \apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/*
RUN NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; \dpkgArch="$(dpkg --print-architecture)" \&& nginxPackages=" \nginx=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \" \&& case "$dpkgArch" in \amd64|arm64) \
# arches officialy built by upstreamecho "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \&& apt-get update \;; \*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packagesecho "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \\
# new directory for storing sources and .deb files&& tempDir="$(mktemp -d)" \&& chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)\
# save list of currently-installed packages so build dependencies can be cleanly removed later&& savedAptMark="$(apt-mark showmanual)" \\
# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source --compile $nginxPackages \) \
# we don't remove APT lists here because they get re-downloaded and removed later\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)&& apt-mark showmanual | xargs apt-mark auto > /dev/null \&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \\
# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)&& ls -lAFh "$tempDir" \&& ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \&& grep '^Package: ' "$tempDir/Packages" \&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
#   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
#   ...
#   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)&& apt-get -o Acquire::GzipIndexes=false update \;; \esac \\&& apt-get install --no-install-recommends --no-install-suggests -y \$nginxPackages \gettext-base \curl \&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \\
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)&& if [ -n "$tempDir" ]; then \apt-get purge -y --auto-remove \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \fi \
# forward request and error logs to docker log collector&& ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.log \
# create a docker-entrypoint.d directory&& mkdir /docker-entrypoint.dCOPY docker-entrypoint.sh /
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
ENTRYPOINT ["/docker-entrypoint.sh"]EXPOSE 80STOPSIGNAL SIGQUITCMD ["nginx", "-g", "daemon off;"]

?

問題2: 分析 case 指令分支

我們繼續往下走,來到分離后的 RUN 這里:

RUN NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; \dpkgArch="$(dpkg --print-architecture)" \&& nginxPackages=" \nginx=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \" \&& case "$dpkgArch" in \amd64|arm64) \
# arches officialy built by upstreamecho "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \&& apt-get update \;; \*) \

上面的腳本中,官方的注釋已經點明了這段腳本意圖:
從上游獲取官方構建的產物

arches officialy built by upstream

?
留意到這里用到了 case 指令來檢查當前的芯片架構,滿足條件時就會直接下載官方發布的打包好的 nginx

dpkgArch="$(dpkg --print-architecture)"
case "$dpkgArch" in amd64|arm64)

一般系統都會進入這個分支,但是我們的目的是為了重新打包 nginx。所幸繼續往下觀察,發現了
默認的情況就是手動下載包后在重新構建:

...
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packagesecho "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \\
# new directory for storing sources and .deb files&& tempDir="$(mktemp -d)" \&& chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)\
# save list of currently-installed packages so build dependencies can be cleanly removed later&& savedAptMark="$(apt-mark showmanual)" \\
# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source --compile $nginxPackages \) \
...

那么我們簡單的刪除 case 指令,只留下默認情況的代碼就可以強制從源碼構建 nginx 了。
?

問題3: 從源碼切入,加入第三方包編譯

根據注釋引導,我們了解到下面這段代碼就是從源碼構建的主要流程

# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source --compile $nginxPackages \) \

這里的核心指令是 apt-get source --compile $nginxPackages, 主要意圖是下載 $nginxPackages
指定的多個包的源碼并進行編譯。那么我要做的就是要拆開這條指令,將它拆成 下載編譯 兩個流程,
這樣我就可以通過拷貝指令,將第三方包的源碼放置在下載后的源碼目錄中,再讓他們一起編譯。

好,目標明確,查閱文檔:

首先我 Google 了 apt-get 的文檔,這里遇到一個迷惑問題,Google 的搜索結果里面,排在前面的是:

https://linux.die.net/man/8/apt-get

在這里插入圖片描述

?
這文檔一眼看上去好像沒什么問題,但是拉到 source --compile 說明時,發現這個文檔介紹的 --compile
參數的效果等同于用 rpmbuild 來編譯源碼包。但是我的目標環境是 Ubuntu, 用的是 dpkg
在這里插入圖片描述
?

由此,我還去看了一眼互聯網檔案館,發現這個網站 07 年上線時介紹的是 dpkg 版本,為什么現在變成了只剩
rpmbuild 了?
https://web.archive.org/web/20070711153000/https://linux.die.net/man/8/apt-get

在這里插入圖片描述
?

我尋思著 dpkg 也沒有被淘汰呀,真是百思不得其解。這里就不管了,我重新找了 dpkg 版本
的文檔來看。

http://ccrma.stanford.edu/planetccrma/man/man8/apt-get.8.html

從文檔的介紹可以知道,當攜帶了 --compile 參數時,apt-get source 會在當前目錄完成代碼包下載、解壓
和編譯的操作。也就是我們去掉這個參數就可以不自動進入編譯的操作。

It will then find and download into the current directory the newest available version of that source package

?
我們在腳本里面去掉這個參數先:

...
&& apt-get update \
&& apt-get build-dep -y $nginxPackages \
&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source $nginxPackages \
) \
...

?

dpkg-buildpackage 源碼解析

從前面的工作我們得知,在完成代碼包的下載和解壓后,apt-get 接著就用了 dpkg-buildpackage 這個
工具來完成編譯 (或者 rpmbuild)。那么這里有個很嚴重的問題,文檔沒有給出它編譯時用的參數呀!
不知道參數就調用編譯指令可是會有大問題的。

我接下來找了 debian 介紹 dpkg-buildpackage 的文檔,但也不能解決實際的問題。

https://www.debian.org/doc/manuals/maint-guide/build.en.html

到這里沒辦法了,我采用了最原始的方式,查看 apt 的源代碼。
萬般工具,還得看 C。

這里是 apt 的源碼地址:
https://github.com/Debian/apt

下載源代碼后,用 vscode 打開,直接搜索 dpkg-buildpackage,就能直接定位出 source 的解析函數

在這里插入圖片描述

?
可以從代碼中看到,調用 dpkg-buildpackage 時,傳入的參數由 buildopts 輸出

strprintf(S, "cd %s && %s %s",Dir.c_str(),_config->Find("Dir::Bin::dpkg-buildpackage","dpkg-buildpackage").c_str(),buildopts.c_str());

?

而前面的代碼中也給出了 buildopts 的構建流程

std::string buildopts = _config->Find("APT::Get::Host-Architecture");if (buildopts.empty() == false)buildopts = "-a" + buildopts + " ";// get all active build profilesstd::string const profiles = APT::Configuration::getBuildProfilesString();if (profiles.empty() == false)buildopts.append(" -P").append(profiles).append(" ");buildopts.append(_config->Find("DPkg::Build-Options","-b -uc"));

?

那答案已經顯而易見了,傳給 dpkg-buildpackage 的參數默認是 -b -uc -a

接著查閱 dpkg-buildpackage 的文檔:
https://manpages.debian.org/testing/dpkg-dev/dpkg-buildpackage.1.en.html

找到關于相關選項的說明:

-a, --host-arch architectureSpecify the Debian architecture we build for (long option since dpkg 1.17.17). The architecture of the machine we build on is determined automatically, and is also the default for the host machine.-b: Equivalent to --build=binary or --build=any,all.-uc, --unsigned-changesDo not sign the .buildinfo and .changes files (long option since dpkg 1.18.8).

?

然后同樣是 source 源代碼中,我們繼續往上看,會發現代碼是在一個 for 循環中不斷進入每個包的
代碼目錄中,然后再調用 dpkg-buildpackage

   for (auto const &D: Dsc){if (unlikely(D.Dsc.empty() == true))continue;std::string const Dir = D.Package + '-' + Cache.GetPkgCache()->VS->UpstreamVersion(D.Version.c_str());// See if the package is already unpackedstruct stat Stat;if (fixBroken == false && stat(Dir.c_str(),&Stat) == 0 &&S_ISDIR(Stat.st_mode) != 0){ioprintf(c0out ,_("Skipping unpack of already unpacked source in %s\n"),Dir.c_str());}else...

這樣,我們就拆解完了 apt-get source --compile 的步驟了。
?

準備第三方包代碼,修改核心包編譯規則

apt-get source nginx=1.21.6-1~bullseye, 執行完成后當前目錄中除了有上面的三個文件,apt-get 還會幫你自動解壓出一個 nginx-1.21.6 目錄
在這里插入圖片描述

通過觀察,nginx-1.21.6/debian 目錄就是 nginx_1.21.6-1~bullseye.debian.tar.xz 包里面的 debian 目錄
在這里插入圖片描述

通過觀察,nginx-1.21.6/debian 目錄就是 nginx_1.21.6-1~bullseye.debian.tar.xz 包里面的 debian 目錄
在這里插入圖片描述
?

小結一
apt-get source nginx=1.21.6-1~bullseye 會下出三個文件,其中有一個原始源碼包和特定平臺依賴包,
nginx_1.21.6.orig.tar.gznginx_1.21.6-1~bullseye.debian.tar.xz, 附加一個
包的校驗信息描述文件。然后 apt-get 會將兩個源碼包的內容解壓到當前目錄的 nginx-1.21.6 文件夾中

?
編譯過程清查

同樣是在 dpkg-buildpackage 的文檔中,提到了 build 鉤子會和 debian/rules 協同進行編譯。

在這里插入圖片描述

?
那么我們進一步查看 nginx-1.21.6/debian/rules 文件,可以找到有配置 configure 的詳細指令

config.env.%:dh_testdirmkdir -p $(BUILDDIR_$*)cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/cp -Pa $(CURDIR)/conf $(BUILDDIR_$*)/cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/cp -Pa $(CURDIR)/contrib $(BUILDDIR_$*)/cp -Pa $(CURDIR)/man $(BUILDDIR_$*)/cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/touch $@config.status.nginx: config.env.nginxcd $(BUILDDIR_nginx) && \CFLAGS="" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt="$(CFLAGS)" --with-ld-opt="$(LDFLAGS)"touch $@config.status.nginx_debug: config.env.nginx_debugcd $(BUILDDIR_nginx_debug) && \CFLAGS="" ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt="$(CFLAGS)" --with-ld-opt="$(LDFLAGS)" --with-debugtouch $@

好我們先暫停在這里,去了解一下 nginx 添加自定義模塊的方法
?

nginx 在編譯時加入動態模塊

nginx 關于模塊編譯的說明 https://nginx.org/en/docs/njs/install.html#install_package

$ ./configure --add-dynamic-module=path-to-njs/nginx

官方說明如果要在編譯時加入動態模塊一起編譯,在 configure 編譯指令中加入 --add-dynamic-module=/path/to/my-module 即可。
我們要添加的模塊 ngx_http_subs_filter_module 是代碼引入,所以需要動態編譯。

?
小結二

通過梳理編譯流程,已經確定了是要修改 nginx-1.21.6/debian/rules 文件,在其中的 configure 指令中用 --add-dynamic-module=/path/to/my-module 的方式來加入我們需要添加的模塊。

修改結果大致如下:

...CFLAGS="" ./configure --prefix=/etc/nginx --add-dynamic-module=/mymodule/ngx_http_subs_filter_module 

具體的操作步驟可以描述為:

  1. 執行 apt-get source $nginxPackages 讓 apt-get 下載指定版本的源碼包并幫我們解壓好
  2. 修改 nginx-1.21.6/debian/rules 文件中的 configure 編譯指令,使用 --add-dynamic-module=/path/to/my-module 加入需要的模塊
  3. 再回到下載源碼的目錄執行 cd nginx-1.21.6 && dpkg-buildpackage -b -uc -a $dpkgArch, 同時也需要對每個下載的源碼包執行, 這樣的流程和 Dockerfile 里面的 apt-get source --compile $nginxPackages 差不多

以下是根據 Dockerfile 步驟在臨時目錄中執行 apt-get source --compile $nginxPackages 后的目錄結構

在這里插入圖片描述
?

準備第三方模塊代碼

在下面的網址下載 ngx-http-substitutions-filter-module 模塊的源碼
https://github.com/yaoweibin/ngx_http_substitutions_filter_module

然后將代碼文件夾解壓到當前的工程目錄

在這里插入圖片描述

拷貝一份 nginx-1.21.6/debian/rules 文件,做以下修改

在這里插入圖片描述

?
然后在 Dockerfile 開頭加入兩條 COPY 指令將第三方模塊代碼和需要替換的 debian/rules 文件
拷貝到鏡像中。

FROM debian:bullseye-slimLABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"ENV NGINX_VERSION   1.23.1
ENV NJS_VERSION     0.7.6
ENV PKG_RELEASE     1~bullseyeCOPY ./ngx-http-substitutions-filter-module-src-master /mymodule/ngx_http_subs_filter_module
COPY ./debian-rules /mymodule/debian-rules
...

?
開始改造

到這里就萬事具備了,我們直接將原 Dockerfile 內下載編譯模塊包的部分修改成下面的內容:

改造前:

...
# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source --compile $nginxPackages \) \
...

改造后:

# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source $nginxPackages \&& cp /mymodule/debian-rules "./nginx-$NGINX_VERSION/debian/rules" \&& for dir in nginx*/; do \cd "$dir"; \dpkg-buildpackage -b -uc -a "$dpkgArch"; \cd ..; \done; \) \

至此,我們就完成了第三方模塊的編譯工作了。

?

問題4:權限不足問題

如果遇到這個問題可以修改,否則跳過。

Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)

在下面的問答中找到了一個解決方式

https://askubuntu.com/questions/1160926/local-deb-file-repository-failes-during-apt-get-update

將 89 行的

apt-get -o Acquire::GzipIndexes=false update

改成

apt-get -o Acquire::GzipIndexes=false -o APT::Sandbox::User=root update

?

問題5:模塊文件缺失

在完成上面的編譯工作后,我嘗試打包了一下鏡像,此時雖然沒有報錯,但是我隱約感覺肯定還有點問題。然后在
上面我們找到的 debian-rules 中,看到了重要的兩個配置:

--modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf 

這里配置了鏡像內部 nginx 模塊的存放路徑和 nginx 的配置路徑。

那么我們用 dive 工具查看鏡像內部文件。BTW,dive 工具的使用可以看我另一篇博文:

https://blog.csdn.net/qq_34727886/article/details/136448207

我們去到 /usr/lib/nginx/modules 一看,這里怎么沒有我們編譯完成的第三方模塊呢?我個人認為應該是
非官方模塊不自動跟蹤依賴了。而且到這里已經花了很多時間,我選擇了最簡單的拷貝方案解決這個問題。

在這里插入圖片描述

?
這里將原本腳本中刪除臨時文件的指令注釋掉,然后重新構建鏡像。

...&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)&& if [ -n "$tempDir" ]; then \apt-get purge -y --auto-remove \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \fi \
...

?
使用 dive 工具看到 $tempDir/nginx-$NGINX_VERSION 下面有個軟連接 objs 鏈接到了
$tempDir/nginx-$NGINX_VERSION/debian/build-nginx/objs

在這里插入圖片描述
?

而在這個目錄下面,就有我們導入的第三方模塊的編譯產物 ngx_http_subs_filter_module.so

在這里插入圖片描述

?
接著可以在容器內找到編譯的第三方模塊存在于 "$tempDir/nginx-$NGINX_VERSION/objs/ngx_http_subs_filter_module.so",
那么我們簡單的在后面加上一條 cp 命令,將第三方模塊放到 /usr/lib/nginx/modules 就行,不
cp 過去后面這個臨時目錄就會整個刪掉。

同時我們也可以在這里加上一條清除指令 rm -rf /mymodule, 清理我們放進鏡像的第三方模塊編譯輔助文件。

...&& cp "$tempDir/nginx-$NGINX_VERSION/objs/ngx_http_subs_filter_module.so" /usr/lib/nginx/modules \&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \&& rm -rf /mymodule \
...

到這里,我們接下來就執行打包命令,然后等待結束就行。

打包命令回顧:

docker build \--build-arg http_proxy=http://xxx:7890 \--build-arg https_proxy=http://xxx:7890 \-t test-nginx:1.0 . --no-cache 2>&1 | tee build.log

等看到了下面的打包日志,就是打包正常結束了。

#13 exporting to image
#13 exporting layers
#13 exporting layers 0.2s done
#13 writing image sha256:067641f4087688634d9b741854f1c848019563c5765defbf8e75f813ac3bebd6 done
#13 naming to docker.io/library/test-nginx:test done
#13 DONE 0.2s

可以再次使用 dive 查看最終產物中有沒有我們要的第三方模塊的 so

在這里插入圖片描述

?

問題6:模塊配置文件

記得我們前面看到過 nginx 的編譯配置 --conf-path=/etc/nginx/nginx.conf,我們最終要用
docker-compose.yml 將這個配置映射到本地目錄,然后在里面要加上下面一句話來動態加載我們編譯
的第三方動態模塊。

load_module modules/ngx_http_subs_filter_module.so;

這樣,我們的第三方模塊就能正常使用了。

問題7: docker-entrypoint.sh 沒有權限

chmod + x docker-entrypoint.sh 后再構建鏡像就行
?

尾聲

走完上面所有流程,驗證了鏡像沒有問題后,就可以把我們前面分開的兩條 RUN 指令合成一條了,然后對比
我們打出來的鏡像和官方鏡像的大小,僅多了 1M, 完美!

在這里插入圖片描述

最終我們修改完的 Dockerfile 如下:

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#
FROM debian:bullseye-slimLABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"ENV NGINX_VERSION   1.21.6
ENV NJS_VERSION     0.7.3
ENV PKG_RELEASE     1~bullseyeCOPY ./ngx-http-substitutions-filter-module-src-master /mymodule/ngx_http_subs_filter_module
COPY ./debian-rules /mymodule/debian-rulesRUN set -x \
# create nginx user/group first, to be consistent throughout docker variants&& addgroup --system --gid 101 nginx \&& adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \&& apt-get update \&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \&& NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; \export GNUPGHOME="$(mktemp -d)"; \found=''; \for server in \hkp://keyserver.ubuntu.com:80 \pgp.mit.edu \; do \echo "Fetching GPG key $NGINX_GPGKEY from $server"; \gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \done; \test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; \rm -rf "$GNUPGHOME"; \apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \&& dpkgArch="$(dpkg --print-architecture)" \&& nginxPackages=" \nginx=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \" \# let's build binaries from the published source packages&& echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \&& echo "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \\
# new directory for storing sources and .deb files&& tempDir="$(mktemp -d)" \&& chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)\
# save list of currently-installed packages so build dependencies can be cleanly removed later&& savedAptMark="$(apt-mark showmanual)" \\
# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source $nginxPackages \&& cp /mymodule/debian-rules "./nginx-$NGINX_VERSION/debian/rules" \&& for dir in nginx*/; do \cd "$dir"; \dpkg-buildpackage -b -uc -a "$dpkgArch"; \cd ..; \done; \) \
# we don't remove APT lists here because they get re-downloaded and removed later\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)&& apt-mark showmanual | xargs apt-mark auto > /dev/null \&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \\
# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)&& ls -lAFh "$tempDir" \&& ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \&& grep '^Package: ' "$tempDir/Packages" \&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
#   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
#   ...
#   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)&& apt-get -o Acquire::GzipIndexes=false update \&& apt-get install --no-install-recommends --no-install-suggests -y \$nginxPackages \gettext-base \curl \&& cp "$tempDir/nginx-$NGINX_VERSION/objs/ngx_http_subs_filter_module.so" /usr/lib/nginx/modules \&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \&& rm -rf /mymodule \\
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)&& if [ -n "$tempDir" ]; then \apt-get purge -y --auto-remove \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \fi \
# forward request and error logs to docker log collector&& ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.log \
# create a docker-entrypoint.d directory&& mkdir /docker-entrypoint.dCOPY docker-entrypoint.sh /
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
ENTRYPOINT ["/docker-entrypoint.sh"]EXPOSE 80STOPSIGNAL SIGQUITCMD ["nginx", "-g", "daemon off;"]

原版 Docckerfile: https://github.com/nginxinc/docker-nginx/blob/f3d86e99ba2db5d9918ede7b094fcad7b9128cd8/mainline/debian/Dockerfile
?

結語

呼,又是一篇長文創作,真是歷經八十一難才搞定這個問題。作為剛接觸 Docker 沒幾天的新人,就要來解決
這個大坑,心態是崩得要死。這次的問題查閱的文檔數也是目前最多的,都到底層代碼了。這個問題其實我很早就
做完,但是陸陸續續寫了很久才把博客梳理出來。接下來要做點其他事情了,這篇博客真的很費時。

不過這一路闖下來,也算是酣暢淋漓。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14275.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14275.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14275.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡安全、信息安全、數據安全的定義與區別

信息安全 信息安全是指信息的保密性、完整性、可用性和真實性的保持。從定義角度來說&#xff0c;信息安全沒有嚴格標準定義&#xff0c;但從信息安全涉及的內容出發&#xff0c;信息安全確保信息存儲或傳輸中的信息&#xff0c;不被他人有意或無意的竊取與破壞。這里的“信息”…

Vue3+ts(day07:pinia)

學習源碼可以看我的個人前端學習筆記 (github.com):qdxzw/frontlearningNotes 覺得有幫助的同學&#xff0c;可以點心心支持一下哈&#xff08;筆記是根據b站上學習的尚硅谷的前端視頻【張天禹老師】&#xff0c;記錄一下學習筆記&#xff0c;用于自己復盤&#xff0c;有需要學…

ENVI光譜識別指導采礦管理者監測銅礦分布

圣地亞哥SRGIS的GIS專家Chile需要利用影像光譜信號勘察Chuquicamata的銅礦分布。 解決方案 Chuquicamata是世界上最大的斑巖銅礦分布區。SRGIS發現西部地區只有有限的礦物和貧瘠的巖石&#xff0c;但東部有銅礦分布。為了進一步測定礦藏的情況&#xff0c;他們開發出一套程序&a…

PyTorch中的形狀變換術:reshape、view與permute的區別與聯系

在PyTorch中&#xff0c;reshape、view 和 permute 都是用于改變張量&#xff08;Tensor&#xff09;形狀&#xff08;shape&#xff09;的方法&#xff0c;但它們各自的功能和用途有所不同。 view: view方法用于將張量重新整形為具有指定形狀的張量。使用view時&#xff0c;必…

NoSQL Redis配置與優化

一、關系數據庫與非關系型數據庫 1. 關系型數據庫&#xff1a; 關系型數據庫是一個結構化的數據庫&#xff0c;創建在關系模型&#xff08;二維表格模型&#xff09;基礎上&#xff0c;一般面向于記錄。 SQL 語句&#xff08;標準數據查詢語言&#xff09;就是一種基于關系型…

【Python】pandas連續變量分箱

路過了學校花店 荒野到海邊 有一種浪漫的愛 是浪費時間 徘徊到繁華世界 才發現你背影 平凡得特別 繞過了城外邊界 還是沒告別 愛錯過了太久 反而錯得完美無缺 幸福兜了一個圈 &#x1f3b5; 林宥嘉《兜圈》 import pandas as pd import numpy as np from sklearn.model_selecti…

redis核心面試題一(架構原理+RDB+AOF)

文章目錄 0. redis與mysql區別1. redis是單線程架構還是多線程架構2. redis單線程為什么這么快3. redis過期key刪除策略4. redis主從復制架構原理5. redis哨兵模式架構原理6. redis高可用集群架構原理7. redis持久化之RDB8. redis持久化之AOF9. redis持久化之混合持久化 0. red…

窮人如何翻身賺錢?不妨試試這5個冷門生意,干好了,收入相當不錯

根據統計數據&#xff0c;我國月收入超過3000元的人口已超過4億&#xff0c;這意味著仍有約10億人的月收入低于3000元。正因為如此&#xff0c;網絡上許多人都自嘲為“窮人”。 然而&#xff0c;窮人真的無法改變自己的命運嗎&#xff1f;并非如此。對于渴望賺錢的窮人來說&am…

gpt2使用ggml推理

gpt2使用ggml推理 ggml/examples/gpt-2/main-backend.cpp : #include "ggml/ggml.h" #include "ggml/ggml-alloc.h" #include "ggml/ggml-backend.h"#ifdef GGML_USE_CUDA #include "ggml-cuda.h" #endif#ifdef GGML_USE_METAL #inc…

傳統藍牙模塊BR/EDR與低功耗藍牙模塊有什么區別?

傳統藍牙模塊BR/EDR與低功耗藍牙模塊有什么區別&#xff1f;下面跟隨美迅物聯網MesoonRF從多個維度來了解。   概述&#xff1a;低功耗藍牙采用了高斯頻移鍵控&#xff08;GFSK&#xff09;。這里我們先拋開藍牙的協議&#xff0c;單純從Radio的角度看收發通信&#xff0c;Ra…

【Crypto】Url編碼

文章目錄 Url編碼解題感悟 Url編碼 Url編碼 搞定 小小flag&#xff0c;拿下&#xff01; 解題感悟 有點餓了…

day 1: 738. 單調遞增的數字

738. 單調遞增的數字 當且僅當每個相鄰位數上的數字 x 和 y 滿足 x < y 時&#xff0c;我們稱這個整數是單調遞增的。 給定一個整數 n &#xff0c;返回 小于或等于 n 的最大數字&#xff0c;且數字呈 單調遞增 。 示例1&#xff1a; 輸入&#xff1a;n 10 輸出&#xff1a…

圖數據庫助力供應鏈柔性升級

導讀 當今市場環境受短視頻等流媒體影響&#xff0c;任何風險事件在社交網絡中傳播速度極其迅速&#xff0c;留給企業的反應時間按分秒計&#xff0c;傳統供應鏈的年度計劃面對劇烈變化的市場環境已失去意義。此外&#xff0c;受近年局勢動蕩的影響&#xff0c;市場需求和供應…

APISIX-簡單使用

APISIX-簡單使用 這個工具還是很不錯的&#xff0c;可視化的配置很清晰 &#xff0c; 想用NGINX的配置模式也是可以的&#xff0c;就是要去修改配置文件了。 APISIX&#xff0c;一個很不錯的可視化工具&#xff0c;用來代替Nginx相當不錯&#xff0c;可作為Nginx的平替方案&…

【Python進階】主流電商平臺數據分析||數據采集返回商品詳情主題鏈接主圖SKU數據

Python是一種高級編程語言&#xff0c;廣泛應用于軟件開發、數據分析、人工智能、科學計算等領域。在軟件開發方面&#xff0c;Python在網站開發、網絡編程、桌面軟件開發等方面有著廣泛的應用。在數據分析和人工智能領域&#xff0c;Python的各種庫如NumPy、Pandas、Matplotli…

守護者:ThingsBoard物聯網網關在溫室環境監測中的應用

系統設計 智慧農業溫室大棚系統由傳感器及執行設備、數據傳輸網關、智慧農業溫室大棚管理平臺組成。 系統支持實時采集溫室大棚內的空氣溫濕度、土壤溫濕度、光照和二氧化碳等環境參數&#xff0c;根據農作物的生長需求自動控制溫室中電器設備的啟停&#xff0c;從而達到植物生…

中移物聯OneMO Cat.1模組推動聯網POS規模應用

在第三方支付蓬勃發展和消費模式不斷革新的時代背景下&#xff0c;新型聯網POS終端以其智能化、便捷化的特點豐富人們生活便利度。在這一變革浪潮中&#xff0c;中移物聯OneMO Cat.1模組ML307R憑借其卓越的性能和成本效益&#xff0c;成為推動聯網POS規模應用的重要力量。 性能…

DHCP 動態分配概述

DHCP 動態分配概述 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;動態主機配置協議&#xff09;是一種網絡協議&#xff0c;用于自動分配IP地址和其他網絡配置參數&#xff08;如子網掩碼、默認網關、DNS服務器&#xff09;給網絡中的設備。這簡化了網絡管…

【Linux】信號集及信號集操作函數

文章目錄 一、信號集是什么&#xff1f;二、信號集操作函數1. sigemptyset2. sigfillset3. sigaddset4. sigdelset5. sigismember6. sigprocmask (僅用于讀取或更改block表)7. sigpending (僅用于讀取pending表) 一、信號集是什么&#xff1f; 對于每個進程, 都有三個信號集, …

k8s集群部署成功后某個節點突然出現notready狀態解決辦法

通過&#xff1a; kubectl get nodes 查看master1節點為not ready 通過查看日志&#xff1a; journalctl -f -u kubelet.service 看到這里 查看狀態&#xff1a; systemctl status kubelet.service 重啟一樣會報錯 執行&#xff1a; swapoff -a 執行后&#xff0c;重啟…