如何借助Hadolint編寫高質(zhì)量的 Dockerfile
前言
在容器化的世界中,Dockerfile 就像是構(gòu)建輕量、便攜和自包含應(yīng)用環(huán)境的藍(lán)圖。但是創(chuàng)建組織良好且優(yōu)化的 Dockerfile 可能有些棘手,需要仔細(xì)關(guān)注細(xì)節(jié)并遵循最佳實(shí)踐。這就是 Hadolint 登場(chǎng)的地方,就像一位超級(jí)英雄,幫助您編寫完美的 Dockerfile。
Hadolint是一個(gè)開源工具,它會(huì)自動(dòng)檢查您的Dockerfile是否存在任何問題。它使用一組預(yù)定義的規(guī)則和ShellCheck來檢查您Dockerfile的每一行,確保您的鏡像安全、快速,并符合行業(yè)標(biāo)準(zhǔn)。
在這個(gè)指南中,我們將學(xué)習(xí)如何使用 Hadolint 來編寫高質(zhì)量的 Dockerfile。
我們將探索 Hadolint 的代碼檢查過程、它的許多規(guī)則,以及如何將 Hadolint 納入您的開發(fā)工作流程。
我們還將發(fā)現(xiàn)如何創(chuàng)建小巧、高效且安全免受常見安全弱點(diǎn)影響的鏡像。
圖片
hadolint簡介
Haskell Dockerfile Linter Hadolint 是一個(gè) Dockerfile 文件檢查工具,幫助您構(gòu)建符合最佳實(shí)踐的 Docker 鏡像。我在所有項(xiàng)目中都使用它,以確保我創(chuàng)建的鏡像小巧、安全、高效且易于維護(hù)。
使用代碼檢查工具來檢查 Dockerfile 的原因有很多:
- 遵循 Docker 鏡像的最佳實(shí)踐
- 在編寫 Dockerfile 時(shí)加快反饋速度,因?yàn)闄z查工具-可以在構(gòu)建鏡像之前發(fā)現(xiàn)語法錯(cuò)誤和安全漏洞
- 可以檢查代碼風(fēng)格是否符合規(guī)范
- 可以提高 Dockerfile 的可讀性和可維護(hù)性
- 在 CI/CD 流水線中使用它們
- 更深入地了解如何編寫更好的 Dockerfile
Hadolint配備了強(qiáng)大且易于使用的CLI。您可以在多種平臺(tái)上安裝它,包括macOS。
$ brew install hadolint
請(qǐng)使用以下命令確認(rèn)安裝是否成功:
$ hadolint --help
hadolint - Dockerfile Linter written in Haskell
...
讓我們創(chuàng)建一個(gè)Dockerfile來測(cè)試這個(gè)工具,現(xiàn)在將以下內(nèi)容添加到Dockerfile中。
FROM python
MAINTAINER johndoe@gmail.com
LABEL org.website="containiq.com"
RUN mkdir app && cd app
COPY requirements.txt ./
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . .
CMD python manage.py runserver 0.0.0.0:80000
現(xiàn)在使用這個(gè)命令驗(yàn)證 Dockerfile:
$ hadolint Dockerfile
Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:1 DL3049 info: Label `maintainer` is missing.
Dockerfile:2 DL4000 error: MAINTAINER is deprecated
Dockerfile:3 DL3052 warning: Label `org.website` is not a valid URL.
Dockerfile:5 DL3003 warning: Use WORKDIR to switch to a directory
Dockerfile:5 SC2164 warning: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Dockerfile:7 DL3045 warning: `COPY` to a relative destination without `WORKDIR` set.
Dockerfile:8 DL3013 warning: Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>` or `pip install --requirement <requirements file>`
Dockerfile:8 DL3042 warning: Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`
Dockerfile:9 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:9 DL3042 warning: Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`
Dockerfile:11 DL3045 warning: `COPY` to a relative destination without `WORKDIR` set.
Dockerfile:13 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT arguments
每行的結(jié)構(gòu)如下:<LINE_NUMBER><RULE_CODE><SEVERITY_LEVEL>:
讓我們更詳細(xì)地探討這些參數(shù)。
代碼規(guī)則
一個(gè)規(guī)則代碼以DL或SC為前綴。DL前綴表示該規(guī)則來自Hadolint直接。SC前綴表示該規(guī)則來自SpellCheck,這是一個(gè)用于shell腳本的靜態(tài)分析工具,與Hadolint一起提供。您可以在這里找到規(guī)則的綜合列表。
每個(gè)規(guī)則都有一個(gè)專門的文檔頁面,列出了代碼示例、原理和其他重要細(xì)節(jié)。請(qǐng)查看DL3006的專門頁面。
您可以使用--ignore RULECODE選項(xiàng)忽略一個(gè)或多個(gè)規(guī)則。
$ hadolint --ignore DL3013 --ignore DL3042 Dockerfile
您也可以在 Dockerfile 中忽略規(guī)則。我更喜歡這種方法,因?yàn)槟梢灾鹦信懦?guī)則代碼,這樣更清晰地知道違規(guī)實(shí)際發(fā)生在哪里。
# hadolint ignore=DL3013
RUN pip install --upgrade pip
Hadolint擁有一個(gè)活躍的開源社區(qū)。新的規(guī)則代碼定期添加,因此請(qǐng)確保定期檢查您是否在運(yùn)行最新版本的Hadolint。
安全級(jí)別
嚴(yán)重級(jí)別表示違規(guī)的嚴(yán)重程度。共有六個(gè)級(jí)別:錯(cuò)誤(error)、警告(warning)、信息(info)、樣式(style)、忽略(ignore)和無(none)。
CLI 包括一個(gè) --failure-threshold(縮寫為 -t)選項(xiàng),用于排除特定嚴(yán)重級(jí)別導(dǎo)致失敗。例如,如果您只希望 Hadolint 在錯(cuò)誤違規(guī)時(shí)失敗。
$ hadolint -t error Dockerfile
請(qǐng)注意,來自其他嚴(yán)重級(jí)別的不符合規(guī)范行為仍將被報(bào)出來,但不會(huì)導(dǎo)致失敗。
如果您不同意某個(gè)規(guī)則代碼的嚴(yán)重級(jí)別,您可以通過使用--<SEVERITY_LEVEL> RULECODE選項(xiàng)輕松更改它。例如,以下命令將DL3006升級(jí)為錯(cuò)誤,將DL3045降級(jí)為信息(這兩個(gè)代碼默認(rèn)為警告):
$ hadolint --error DL3006 --info DL3045 Dockerfile
Dockerfile:1 DL3006 error: Always tag the version of an image explicitly
Dockerfile:7 DL3045 info: `COPY` to a relative destination without `WORKDIR` set.
標(biāo)簽檢查
Dockerfile標(biāo)簽是注釋您的Docker鏡像的絕佳工具。Hadolint提供了一些驗(yàn)證選項(xiàng),以確保您的標(biāo)簽設(shè)置正確。
--require-label LABELSCHEMA選項(xiàng)驗(yàn)證您的標(biāo)簽是否遵循特定格式。您可以在這里查看所有可接受的格式值。
$ hadolint --require-label maintainer:text --require-label org.website:url Dockerfile
Dockerfile:2 DL3049 info: Label `maintainer` is missing.
Dockerfile:3 DL3052 warning: Label `org.website` is not a valid URL.
The --strict-labels 選項(xiàng)會(huì)驗(yàn)證在您的模式中定義的標(biāo)簽之外是否有額外的標(biāo)簽。
$ hadolint --require-label maintainer:text --strict-labels Dockerfile
Dockerfile:3 DL3050 info: Superfluous label(s) present.
配置文件
將選項(xiàng)手動(dòng)傳遞到每次 Hadolint 運(yùn)行中可能會(huì)很煩人且容易出錯(cuò)。Hadolint 很方便地提供了配置文件支持,可以將所有選項(xiàng)存儲(chǔ)在一個(gè)地方。這個(gè)文件可以存在于各種位置,但我通常會(huì)將其放在存儲(chǔ)庫的根目錄下,命名為 .hadolint.yaml。
override:
error:
- DL3006
info:
- DL3045
label-schema:
maintainer: text
org.website: url
strict-labels: true
修復(fù) Dockerfile
逐個(gè)解決每個(gè)錯(cuò)誤是學(xué)習(xí) Dockerfile 最佳實(shí)踐的絕佳方法。如上所述,每條規(guī)則都有非常清晰和詳細(xì)的文檔頁面。嘗試一下,完成后再回顧這篇文章。
到這一步,Hadolint 應(yīng)該不會(huì)報(bào)任何錯(cuò)誤。你的文件應(yīng)該看起來類似于這樣:
FROM python:3.10
LABEL maintainer="johndoe@gmail.com"
LABEL org.website="https://www.airplane.dev/"
WORKDIR /app
COPY requirements.txt ./
# hadolint ignore=DL3013
RUN pip install --upgrade --no-cache-dir pip && \
pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
需要進(jìn)一步解釋的一些變化:
- 我們將使用最新可用的 Python 次要版本(目前為 3.10)標(biāo)記 Python 基礎(chǔ)鏡像。我們不包括補(bǔ)丁版本(3.10.2),因?yàn)?Python 的補(bǔ)丁版本向后兼容,通常包含有用的 bug 修復(fù)。
- 我通常喜歡使用/app工作目錄來保持我的 Docker 鏡像一致,但您可以使用任何您想要的新目錄或現(xiàn)有目錄。
- 我們忽略 DL3013,因?yàn)槲覀兿胍螺d最新版本的 pip。沒有必要將其固定到特定版本。
集成
Hadolint包含許多方便的集成功能,可以在整個(gè)開發(fā)過程中自動(dòng)運(yùn)行代碼檢查工具。我最喜歡的集成有:
- VS Code:直接在編輯器中運(yùn)行Hadolint
- pre-commit:在每次git提交時(shí)運(yùn)行Hadolint
- GitHub Actions:在GitHub的CI/CD中運(yùn)行Hadolint
集成非常重要,特別是在較大的團(tuán)隊(duì)中,因?yàn)橐恍╅_發(fā)人員會(huì)忘記手動(dòng)運(yùn)行代碼檢查工具。我在開始新的Docker項(xiàng)目時(shí)立即設(shè)置這些集成。
總結(jié)
正如您所看到的,這個(gè)工具很容易上手,它可以在幾秒鐘內(nèi)提高您的 Dockerfile 的質(zhì)量。Hadolint 并不是唯一一個(gè)用于 Dockerfile 的代碼檢查工具。Docker 引擎本身也包含一個(gè),但更多用于檢查基本錯(cuò)誤。此外,還有來自 Snyk 的一個(gè)代碼檢查工具,可能更專注于安全問題。