制作容器鏡像的最佳實(shí)踐

概述

這篇文章主要是我日常工作中的制作鏡像的實(shí)踐, 同時(shí)結(jié)合我學(xué)習(xí)到的關(guān)于鏡像制作的相關(guān)文章總結(jié)出來的. 包括通用的容器最佳實(shí)踐, java, nginx, python 容器最佳實(shí)踐. 最佳實(shí)踐的目的一方面保證鏡像是可復(fù)用的, 提升 DevOps 效率, 另一方面是為了提高安全性. 希望對(duì)各位有所幫助.

本文分為四部分內(nèi)容, 分別是:

  1. 通用容器鏡像最佳實(shí)踐
  2. Java 容器鏡像最佳實(shí)踐
  3. NGINX 容器鏡像最佳實(shí)踐
  4. 以及 Python 容器最佳實(shí)踐

通用容器鏡像最佳實(shí)踐

使用 LABEL maintainer

LABEL maintainer 指令設(shè)置鏡像的作者姓名和郵箱字段糯笙。示例如下:

LABEL maintainer="cuikaidong@foxmail.com"

復(fù)用鏡像

建議盡量使用 FROM 語句復(fù)用合適的上游鏡像沸移。這可確保鏡像在更新時(shí)可以輕松從上游鏡像中獲取安全補(bǔ)丁捣染,而不必直接更新依賴項(xiàng)鼻弧。

此外唠梨,在 FROM 指令中使用標(biāo)簽 tag(例如 alpine:3.13)很泊,使用戶能夠清楚地了解鏡像所基于的上游鏡像版本囱嫩。

:exclamation: 禁止使用 latest tag以確保鏡像不會(huì)受到 latest 上游鏡像版本的重大更改的影響。

保持標(biāo)簽 TAGS 的兼容性

給自己的鏡像打標(biāo)簽時(shí)儿捧,注意保持向后兼容性荚坞。例如,如果制作了一個(gè)名為example 的鏡像纯命,并且它當(dāng)前為 1.0 版西剥,那么可以提供一個(gè) example:1 標(biāo)簽。后續(xù)要更新鏡像時(shí)亿汞,只要它繼續(xù)與原始鏡像兼容瞭空,就可以繼續(xù)標(biāo)記新鏡像為 example:1,并且該 tag 的下游消費(fèi)者將能夠在不中斷的情況下獲得更新疗我。

如果后續(xù)發(fā)布了不兼容的更新咆畏,那么應(yīng)該切換到一個(gè)新 tag,例如 example:2吴裤。那么下游消費(fèi)者可以按照自身實(shí)際情況升級(jí)到新版本旧找,而不會(huì)因?yàn)樾碌牟患嫒葭R像而造成事故。但是任何使用 example:latest的下游消費(fèi)者都會(huì)承擔(dān)引入不兼容更改的風(fēng)險(xiǎn), 所以這也是前面我強(qiáng)烈建議不要使用 latest tag 的原因.

避免多個(gè)進(jìn)程

建議不要在一個(gè)容器內(nèi)啟動(dòng)多個(gè)服務(wù)麦牺,例如 nginx 和 后端 app钮蛛。因?yàn)槿萜魇禽p量級(jí)的,可以很容易地通過 Docker Compose 或 Kubernetes 鏈接在一起剖膳。Kubernetes 或基于此的 TKE 容器平臺(tái)通過將相關(guān)鏡像調(diào)度到單個(gè) pod 中魏颓,輕松地對(duì)它們進(jìn)行集中管理。

在封裝腳本中使用 EXEC 指令

許多鏡像會(huì)通過在啟動(dòng)應(yīng)用程序之前使用封裝腳本進(jìn)行一些設(shè)置吱晒。如果您的鏡像使用這樣的腳本甸饱,那么該腳本最后應(yīng)該使用 exec 啟動(dòng)應(yīng)用程序,以便用應(yīng)用程序的進(jìn)程替換該腳本的進(jìn)程仑濒。如果不使用 exec叹话,那么容器運(yùn)行時(shí)發(fā)送的信號(hào)(比如 TERMSIGKILL)將轉(zhuǎn)到封裝腳本,而不是應(yīng)用程序的進(jìn)程墩瞳。這不是我們所期望的驼壶。

清除臨時(shí)文件

應(yīng)刪除在生成過程中創(chuàng)建的所有臨時(shí)文件。這還包括使用 ADD 指令添加的任何文件喉酌。例如辅柴,?? 我們強(qiáng)烈建議您在執(zhí)行apt-get install操作之后運(yùn)行 rm -rf /var/lib/apt/lists/* 命令箩溃。

通過如下創(chuàng)建 RUN 語句,可以防止 apt-get 緩存存儲(chǔ)在鏡像層中:

RUN apt-get update && apt-get install -y \
    curl \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*

請(qǐng)注意碌嘀,如果您改為:

RUN apt-get install curl -y
RUN apt-get install s3cmd -y && rm -rf /var/lib/apt/lists/*

那么,第一個(gè) apt-get 調(diào)用會(huì)在這一層 (image layer) 中留下額外的文件歪架,并且在稍后運(yùn)行rm -rf ... 操作時(shí)股冗,無法刪除這些文件。額外的文件在最終鏡像中不可見和蚪,但它們會(huì)占用空間止状。

另外,在一條 RUN 語句中執(zhí)行多個(gè)命令可以減少鏡像中的層數(shù)攒霹,從而縮短下載和安裝時(shí)間怯疤。

yum 的例子如下:

RUN yum -y install curl && yum -y install s3cmd && yum clean all -y

:notebook: 備注:

  1. RUNCOPYADD 步驟會(huì)創(chuàng)建鏡像層催束。
  2. 每個(gè)層包含與前一層的差異項(xiàng)集峦。
  3. 鏡像層會(huì)增加最終鏡像的大小。

:notebook: 提示:

  1. 將相關(guān)命令(apt-get install)放入同一 RUN 步驟抠刺。
  2. 在同一 RUN 步驟中刪除創(chuàng)建的文件塔淤。
  3. 避免使用 apt-get upgradeyum upgrade all ,因?yàn)樗鼤?huì)把所有包升級(jí)到最新版本.

按正確的順序放置指令

容器構(gòu)建過程中, 讀取 dockerfile 并從上到下運(yùn)行指令速妖。成功執(zhí)行的每一條指令都會(huì)創(chuàng)建一個(gè)層高蜂,在下次構(gòu)建此鏡像或另一個(gè)鏡像時(shí)可以重用該層。建議在 Dockerfile 的頂部放置很少更改的指令罕容。這樣做可以確保同一鏡像的下一次構(gòu)建速度非潮感簦快,因?yàn)樯蠈痈牡木彺孢€在, 可以復(fù)用锦秒。

例如露泊,如果正在處理一個(gè) dockerfile,其中包含一個(gè)用于安裝正在迭代的文件的 ADD 指令脂崔,以及一個(gè)用于 apt-get install 包的 RUN 指令滤淳,那么最好將ADD命令放在最后:

FROM alpine:3.11
RUN apt-get -y install curl && rm -rf /var/lib/apt/lists/*
ADD app /app

這樣,每次編輯 app 并重新運(yùn)行 docker build 時(shí)砌左,系統(tǒng)都會(huì)為 apt-get 命令復(fù)用緩存層脖咐,并且只為 ADD 操作生成新層。

如果反過來, dockerfile 如下:

FROM alpine:3.11
ADD app /app
RUN apt-get -y install curl && rm -rf /var/lib/apt/lists/*

那么汇歹,每次更改 app 然后再次運(yùn)行 docker build 時(shí)屁擅,ADD 操作都會(huì)使鏡像層的緩存失效,因此必須重新運(yùn)行 apt-get 操作产弹。

標(biāo)記重要端口

EXPOSE 指令使容器中的端口對(duì)主機(jī)系統(tǒng)和其他容器可用派歌。雖然可以指定使用 docker run -p 調(diào)用公開端口弯囊,但在dockerfile 中使用 EXPOSE 指令可以通過顯式聲明應(yīng)用程序需要運(yùn)行的端口,使人和應(yīng)用程序更容易使用您的鏡像:

  • 暴露的端口將顯示在 docker ps 下胶果。
  • docker inspect 返回的鏡像的元數(shù)據(jù)中也會(huì)顯示暴露的端口匾嘱。
  • 當(dāng)將一個(gè)容器鏈接到另一個(gè)容器時(shí),會(huì)鏈接暴露的端口早抠。

設(shè)置環(huán)境變量

??? 使用 ENV 指令設(shè)置環(huán)境變量是很好的實(shí)踐霎烙。一個(gè)例子是設(shè)置項(xiàng)目的版本。這使得人們?cè)诓徊榭?dockerfile 的情況下很容易找到版本蕊连。另一個(gè)例子是在公布一條可以被另一個(gè)進(jìn)程使用的路徑悬垃,比如 JAVA_HOME.

避免默認(rèn)密碼

:exclamation: 最好避免設(shè)置默認(rèn)密碼。許多人會(huì)擴(kuò)展基礎(chǔ)鏡像甘苍,但是忘記刪除或更改默認(rèn)密碼尝蠕。如果為生產(chǎn)中的用戶分配了一個(gè)眾所周知的密碼,這可能會(huì)導(dǎo)致安全問題载庭。??? 應(yīng)該使用環(huán)境變量, secret 或其他 K8s 加密方案來配置密碼看彼。

如果確實(shí)選擇設(shè)置默認(rèn)密碼,請(qǐng)確保在容器啟動(dòng)時(shí)顯示適當(dāng)?shù)木嫦⒚两荨O?yīng)該通知用戶默認(rèn)密碼的值闲昭,并說明如何更改,例如設(shè)置什么環(huán)境變量靡挥。

禁用SSHD

:exclamation: 禁止在鏡像中運(yùn)行 sshd序矩。可以使用 docker exec 命令訪問本地主機(jī)上運(yùn)行的容器跋破◆さ恚或者,可以使用 kubectl exec 命令來訪問在 K8s 或 TKE 容器平臺(tái)上運(yùn)行的容器毒返。在鏡像中安裝和運(yùn)行sshd 會(huì)遭受潛在攻擊, 需要額外的安全補(bǔ)丁修復(fù)租幕。

將 VOLUMES(卷) 用于持久數(shù)據(jù)

鏡像應(yīng)使用來存儲(chǔ)持久數(shù)據(jù)。這樣拧簸,Kubernetes 或 TKE 將網(wǎng)絡(luò)存儲(chǔ)掛載到運(yùn)行容器的節(jié)點(diǎn)劲绪,如果容器移動(dòng)到新節(jié)點(diǎn),則存儲(chǔ)將重新連接到該節(jié)點(diǎn)盆赤。通過將卷用于所有持久化存儲(chǔ)的需求贾富,即使重新啟動(dòng)或移動(dòng)容器,也會(huì)保留持久化內(nèi)容牺六。如果鏡像將數(shù)據(jù)寫入容器內(nèi)的任意位置颤枪,則可能數(shù)據(jù)會(huì)丟失。

此外淑际,在 Dockerfile 中顯式定義卷使鏡像的消費(fèi)者很容易理解在運(yùn)行鏡像時(shí)必須定義哪些卷畏纲。

有關(guān)如何在 K8s 或 TKE 容器平臺(tái)中使用卷的更多信息扇住,請(qǐng)參閱 Kubernetes documentation.

使用非 root 用戶運(yùn)行容器進(jìn)程

默認(rèn)情況下,Docker 用容器內(nèi)部的 root 運(yùn)行容器進(jìn)程盗胀。這是一個(gè)不安全的做法艘蹋,因?yàn)槿绻粽咴O(shè)法突破容器,他們可以獲得對(duì)Docker 宿主機(jī)的 root 權(quán)限票灰。

:exclamation: 注意:

如果容器中是 root簿训,那么逃逸出來就是主機(jī)上的 root。

使用多階段構(gòu)建

利用多階段構(gòu)建來創(chuàng)建一個(gè)用于構(gòu)建工件的臨時(shí)鏡像米间,該工件將被復(fù)制到生產(chǎn)鏡像上。臨時(shí)構(gòu)建鏡像將與與該映像關(guān)聯(lián)的原始文件膘侮、文件夾和依賴項(xiàng)一起丟棄携栋。

這會(huì)產(chǎn)生了一個(gè)精益竿开,生產(chǎn)就緒的鏡像。

一個(gè)用例是使用非Alpine基礎(chǔ)鏡像來安裝需要編譯的依賴項(xiàng)。然后可以將 wheel 文件復(fù)制到最終鏡像乒验。

Python 示例如下:

FROM python:3.6 as base
COPY requirements.txt /
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /wheels -r requirements.txt

FROM python:3.6-alpine
COPY --from=base /wheels /wheels
COPY --from=base requirements.txt .
RUN pip install --no-cache /wheels/* # flask, gunicorn, pycrypto
WORKDIR /app
COPY . /app

使用前大小: 705MB, 使用后大小: 103MB

:exclamation: 禁止在容器中存儲(chǔ)機(jī)密信息

禁止在容器中存儲(chǔ)機(jī)密信息, 包括:

  • 敏感信息
  • 數(shù)據(jù)庫(kù)憑據(jù)
  • ssh 密鑰
  • 用戶名和密碼
  • api 令牌等

以上信息可以通過:

  • 環(huán)境變量 ENV 傳遞
  • 卷 VOLUME 掛載

避免將文件放入 /tmp

對(duì)于一些應(yīng)用程序(如: python 的 gunicorn ), 會(huì)將某些緩存信息或心跳檢測(cè)信息寫入 /tmp 中, 這對(duì) /tmp 的讀寫性能有較高要求, 如果 /tmp 掛載的是普通磁盤, 可能導(dǎo)致嚴(yán)重的性能問題.

在某些Linux發(fā)行版中,/tmp 通過 tmpfs 文件系統(tǒng)存儲(chǔ)在內(nèi)存中曙博。但是畔塔,Docker容器默認(rèn)情況下沒有為 /tmp 打開 tmpfs

$ docker run --rm -it ubuntu:18.04 df
Filesystem       1K-blocks     Used Available Use% Mounted on
overlay           31263648 25656756   3995732  87% /
tmpfs                65536        0     65536   0% /dev
tmpfs              4026608        0   4026608   0% /sys/fs/cgroup
/dev/mapper/root  31263648 25656756   3995732  87% /etc/hosts
shm                  65536        0     65536   0% /dev/shm

如上所示,/tmp 正在使用標(biāo)準(zhǔn)的 Docker overlay 文件系統(tǒng):它由普通的塊設(shè)備或計(jì)算機(jī)正在使用的硬盤驅(qū)動(dòng)器支持所袁。這可能導(dǎo)致性能問題 .

針對(duì)這類應(yīng)用程序, 通用的解決方案是將其臨時(shí)文件存儲(chǔ)在其他地方盏档。特別是,如果你看上面你會(huì)看到 /dev/shm 使用 shm 文件系統(tǒng)共享內(nèi)存和內(nèi)存文件系統(tǒng)燥爷。所以你需要做的就是使用 /dev/shm 而不是 /tmp

使用 Alpine Linux基礎(chǔ)鏡像 (謹(jǐn)慎采納)

使用基于Alpine Linux 的鏡像蜈亩,因?yàn)樗惶峁┍匾陌? 生成的鏡像更小。

收益有:

  1. 減少了主機(jī)成本前翎,因?yàn)槭褂玫拇疟P空間更少
  2. 更快的構(gòu)建稚配、下載和運(yùn)行時(shí)間
  3. 更安全(因?yàn)榘蛶?kù)更少)
  4. 更快的部署

示例如下:

FROM python:3.6-alpine
WORKDIR /app
COPY requirements.txt /
RUN pip install -r /requirements.txt  # flask and gunicorn
COPY . /app

使用前大小: 702MB, 使用后大小: 102MB

:exclamation: 注意:

謹(jǐn)慎使用 alpine, 我看到過使用 Alpine Linux 產(chǎn)生的一大堆問題,因?yàn)樗⒃?musl libc 之上港华,而不是大多數(shù) Linux 發(fā)行版使用的GNU libc(glibc)道川。問題有: 日期時(shí)間格式的錯(cuò)誤, 由于堆棧較小導(dǎo)致的崩潰等等。

使用 .dockerignore 排除無關(guān)文件

要排除與構(gòu)建無關(guān)的文件立宜,請(qǐng)使用 .dockerignore 文件冒萄。此文件支持與 .gitignore 文件類似的排除模式。具體請(qǐng)參閱 .dockerignore文件赘理。

不要安裝不必要的包

為了降低復(fù)雜性宦言,依賴性,文件大小和構(gòu)建時(shí)間商模,請(qǐng)避免安裝額外的或不必要的應(yīng)用程序包奠旺。例如蜘澜,不需要在數(shù)據(jù)庫(kù)鏡像中包含文本編輯器。

解耦應(yīng)用程序

每個(gè)容器應(yīng)該只有一個(gè)進(jìn)程响疚。將應(yīng)用程序分離到多個(gè)容器中可以更容易地水平擴(kuò)展和重用容器鄙信。例如,Web 應(yīng)用程序堆棧 LNMP 可能包含三個(gè)獨(dú)立的容器忿晕,每個(gè)容器都有自己獨(dú)特的映像装诡,以分離的方式管理 Web 服務(wù)器, 應(yīng)用程序,緩存數(shù)據(jù)庫(kù)和數(shù)據(jù)庫(kù)践盼。

將每個(gè)容器限制為一個(gè)進(jìn)程是一個(gè)很好的經(jīng)驗(yàn)法則鸦采,但它不是一個(gè)硬性規(guī)則。例如咕幻,可以 使用 init 進(jìn)程生成容器 渔伯,另外某些程序可能會(huì)自行生成其他子進(jìn)程 (如: nginx)。

根據(jù)自己的經(jīng)驗(yàn)進(jìn)行判斷肄程,盡可能保持容器簡(jiǎn)潔和模塊化锣吼。如果容器彼此依賴,則可以使用 容器網(wǎng)絡(luò) 或 K8s Sidecar 來確保這些容器可以進(jìn)行通信蓝厌。

對(duì)多行參數(shù)進(jìn)行排序

建議通過按字母順序排序多行參數(shù)來方便后續(xù)的更改玄叠。這有助于避免重復(fù)包并使列表更容易更新。這也使 PR 更容易閱讀和審查拓提。在反斜杠(\)之前添加空格也有幫助读恃。

下面是來自一個(gè)示例openjdk圖像

...
  apt-get update; \
  apt-get install -y --no-install-recommends \
    dirmngr \
    gnupg \
    wget \
  ; \
  rm -rf /var/lib/apt/lists/*; \
...

JAVA 容器鏡像最佳實(shí)踐

IDE插件推薦

  • idea - 轉(zhuǎn)到“首選項(xiàng)”、“插件”崎苗、“安裝JetBrains插件…”狐粱,搜索“Docker”并單擊“安裝”
  • Eclipse

:notebook: 備注:

Docker and IntelliJ IDEA

Docker and Eclipse

設(shè)置內(nèi)存限制相關(guān)參數(shù)

:notebook: 備注:

指定 -Xmx=1g 將告訴 JVM 分配一個(gè) 1 GB 堆, 但是它并沒有告訴 JVM 將其整個(gè)內(nèi)存使用量限制為 1 GB。除了對(duì)內(nèi)存, 還會(huì)有 card tables胆数、code cache 和各種其他堆外數(shù)據(jù)結(jié)構(gòu)肌蜻。用于指定總內(nèi)存使用量的參數(shù)是 -XX:MaxRAM。請(qǐng)注意必尼,使用 -XX:MaxRam=500m 時(shí)蒋搜,堆將大約為 250 MB。

JVM 在歷史上查找/proc以確定有多少可用內(nèi)存判莉,然后根據(jù)該值設(shè)置其堆大小豆挽。不幸的是,像 docker 這樣的容器在/proc中不提供特定于容器的信息券盅。2017年之后有一個(gè)補(bǔ)丁帮哈,提供了一個(gè) -XX:+UseCGroupMemoryLimitForHeap命令行參數(shù),它告訴 jvm 查找 /sys/fs/cgroup/memory/memory.limit_in_bytes锰镀,以確定有多少可用內(nèi)存娘侍。如果這個(gè)補(bǔ)丁在運(yùn)行的 OpenJDK 版本中不可用咖刃,可以通過顯式設(shè)置 -XX:MaxRAM=n 來代替。

總結(jié), 設(shè)置內(nèi)存限制相關(guān)參數(shù):

  1. Openjdk 8 的新版本, 添加: -XX:+UseCGroupMemoryLimitForHeap
  2. 如果沒有上邊的參數(shù), 設(shè)置:-XX:MaxRAM=n
  3. 建議設(shè)置 JVM Heap 約為 memory limit 的 50% - 80%
  4. 建議設(shè)置 JVM MaxRAM 接近 K8s pod 的 memory limit

設(shè)置GC策略

OpenJDK8 中有一個(gè)補(bǔ)丁憾筏,它將使用 cgroup 可用的信息來計(jì)算適當(dāng)數(shù)量的并行 GC 線程嚎杨。但是,如果這個(gè)補(bǔ)丁在您用的 OpenJDK 版本中不可用氧腰,假設(shè)您的容器宿主機(jī)有 8 個(gè) CPU, 但是容器中 CPU limit 為 2 個(gè) CPU, 那么您最終可能會(huì)得到 8 個(gè)并行 GC 線程枫浙。解決方法是顯式指定并行GC線程的數(shù)量: -XX:ParallelGCThreads=2

如果您的容器中 cpu limit 設(shè)置為只有一個(gè) CPU古拴,強(qiáng)烈建議使用 -XX:+UseSerialGC 運(yùn)行箩帚,來完全避免并行GC。

JAVA 啟動(dòng)階段調(diào)優(yōu)

JAVA 程序都有一個(gè)啟動(dòng)階段黄痪,它需要大量的堆膏潮,之后可能會(huì)進(jìn)入一個(gè)安靜的循環(huán)階段,在這個(gè)階段它就不需要太多的堆满力。

對(duì)于串行 GC 策略, 您可以通過配置使它更具侵略性, 如: -XX:MinHeapFreeRatio=20(當(dāng)堆占用率大于 80%,此值默認(rèn)增大轻纪。)

XX:MaxHeapFreeRatio=40(堆占用率小于60%時(shí)收縮)

對(duì)于并行 - parallel GC策略, 推薦如下配置:

-XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90

JAVA 容器全局建議資源請(qǐng)求和資源限制

JAVA 程序都有一個(gè)啟動(dòng)階段油额,啟動(dòng)階段也會(huì)大量消耗 CPU, CPU 使用越多, 啟動(dòng)階段越短.
下面是一個(gè)表,總結(jié)了不同CPU限制下的 spring boot 示例應(yīng)用啟動(dòng)時(shí)間(CPU 以 millicore 為單位):

  • 500m - 80 seconds
  • 1000m - 35 seconds
  • 1500m - 22 seconds
  • 2500m - 17 seconds
  • 3000m - 12 seconds

根據(jù)以上情況, K8s 或 TKE 容器平臺(tái)管理員可以考慮對(duì) JAVA 容器做如下限制:

  • 使用CPU requests, 不設(shè)置 cpu limit
  • 使用 memory limit 且等于 memory request

示例如下:

resources:
  requests:
    memory: "1024Mi"
    cpu: "500m"
  limits:
    memory: "1024Mi"

使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError (謹(jǐn)慎評(píng)估)

我們都知道, 在傳統(tǒng)的虛擬機(jī)上部署的 Java 實(shí)例. 為了更好地分析問題, 一般都是要加上: -XX:+HeapDumpOnOutOfMemoryError這個(gè)參數(shù)的, 加這個(gè)參數(shù)后, 如果遇到內(nèi)存溢出, 就會(huì)自動(dòng)生成 HeapDump , 后面我們可以拿到這個(gè) HeapDump 來更精確地分析問題.

但是, 容器技術(shù)的應(yīng)用, 帶來了一些不同, 在使用容器平臺(tái)后, 我們更傾向于:

  1. 遇到故障快速失敗
  2. 遇到故障快速恢復(fù)
  3. 盡量做到用戶對(duì)故障"無感知"

所以, 針對(duì) Java 應(yīng)用容器, 我們也要優(yōu)化以滿足這種需求, 以 OutOfMemoryError 故障為例:

  1. 遇到故障快速失敗, 即盡可能"快速退出, 快速終結(jié)"

-XX:+ExitOnOutOfMemoryError 就正好滿足這種需求:

傳遞此參數(shù)時(shí)刻帚,拋出 OutOfMemoryError 時(shí) JVM 將立即退出潦嘶。 如果您想盡快終止異常應(yīng)用程序,則可以傳遞此參數(shù)崇众。

NGINX 容器鏡像最佳實(shí)踐

如果您直接在基礎(chǔ)硬件或虛擬機(jī)上運(yùn)行 NGINX掂僵,通常需要一個(gè) NGINX 實(shí)例來使用所有可用的CPU。由于NGINX 是多進(jìn)程模式顷歌,通常你會(huì)啟動(dòng)多個(gè) worker processes锰蓬,每個(gè)工作進(jìn)程都是不同的進(jìn)程,以便利用所有CPU眯漩。

但是芹扭,在容器中運(yùn)行時(shí),如果將 worker_processes 設(shè)置為 auto, 會(huì)根據(jù)容器所在宿主機(jī)的 CPU 核數(shù)啟動(dòng)相應(yīng)進(jìn)程數(shù). 比如, 我之前在物理機(jī)上運(yùn)行 NGINX 容器使用 auto 參數(shù), 盡管 CPU limit 設(shè)置為2, 但是 NGINX 會(huì)啟動(dòng) 64 (物理機(jī) CPU 數(shù)) 個(gè)進(jìn)程.

因此赦抖,???建議根據(jù) 實(shí)際需求或 CPU limit 的設(shè)置配置 nginx.conf, 如下:

worker_processes  2;

Python 容器鏡像最佳實(shí)踐

??Warning:
隨著時(shí)間的遷移, 以及實(shí)踐的深入, 最佳實(shí)踐也在發(fā)生著變化, 以下部分內(nèi)容已經(jīng)不能作為 Python 容器鏡像的最佳實(shí)踐.
最新的 Python 容器鏡像最佳實(shí)踐可以參見這篇文章: https://EWhisper.cn/posts/25776/

示例如下:

# 基于官方基礎(chǔ)鏡像
FROM python:3.7-alpine

# 設(shè)置工作目錄
WORKDIR /app

# 設(shè)置環(huán)境變量
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-deps gcc musl-dev python3-dev \
    && apk add postgresql-dev \
    && pip install psycopg2 \
    && apk del build-deps

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# 切換到非 root 用戶
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

△ 示例 Dockerfile

IDE插件推薦

建議配置的環(huán)境變量

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0
  1. PYTHONDONTWRITEBYTECODE: 防止 python 將 pyc 文件寫入硬盤
  2. PYTHONUNBUFFERED: 防止 python 緩沖 stdout 和 stderr
  3. DEBUG: 方便根據(jù)環(huán)境類型的不同(測(cè)試/生產(chǎn))調(diào)整是否開啟debug

安裝數(shù)據(jù)庫(kù)驅(qū)動(dòng)包的方法

以 postgredb 的驅(qū)動(dòng) psycopg2 為例, 可能需要安裝額外的基礎(chǔ)組件:

# install psycopg2
RUN apk update \
    && apk add --virtual build-deps gcc musl-dev python3-dev \
    && apk add postgresql-dev \
    && pip install psycopg2 \
    && apk del build-deps

參考鏈接

2個(gè)問題

  1. 您是否有制作其他語言鏡像的最佳實(shí)踐呢?
  2. 您是否嘗試通過 GraalVM 制作 原生可執(zhí)行 Java 鏡像? 體驗(yàn)如何?

三人行, 必有我?guī)? 知識(shí)共享, 天下為公. 本文由東風(fēng)微鳴技術(shù)博客 EWhisper.cn 編寫.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市队萤,隨后出現(xiàn)的幾起案子轮锥,更是在濱河造成了極大的恐慌,老刑警劉巖要尔,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舍杜,死亡現(xiàn)場(chǎng)離奇詭異新娜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蝴簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門杯活,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熬词,你說我怎么就攤上這事旁钧。” “怎么了互拾?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵歪今,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我颜矿,道長(zhǎng)寄猩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任骑疆,我火速辦了婚禮田篇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箍铭。我一直安慰自己泊柬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布诈火。 她就那樣靜靜地躺著兽赁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冷守。 梳的紋絲不亂的頭發(fā)上刀崖,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音拍摇,去河邊找鬼亮钦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛充活,可吹牛的內(nèi)容都是我干的或悲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼堪唐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼巡语!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起淮菠,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤男公,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枢赔,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澄阳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踏拜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碎赢。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖速梗,靈堂內(nèi)的尸體忽然破棺而出肮塞,到底是詐尸還是另有隱情,我是刑警寧澤姻锁,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布枕赵,位于F島的核電站,受9級(jí)特大地震影響位隶,放射性物質(zhì)發(fā)生泄漏拷窜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一涧黄、第九天 我趴在偏房一處隱蔽的房頂上張望篮昧。 院中可真熱鬧,春花似錦笋妥、人聲如沸恋谭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狈孔,卻和暖如春信认,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背均抽。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工嫁赏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油挥。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓潦蝇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親深寥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子攘乒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容