Edited at

Dockerfileの静的解析ツールが便利すぎた

687474703a2f2f6861646f6c696e742e6c756b61736d617274696e656c6c692e63682f696d672f6361745f636f6e7461696e65722e706e67.png


背景

Dockerfileを静的解析したいと思い便利なツールがないかと探していたら発見しました。

DockerfileのLintツール

hadolint/hadolint

エラーが出るDockerfileをビルド前に検知してくれます。

ちなみにHaskellで書かれているようです。

現時点での最新バージョンはv1.15.0でmac等にも対応している。

GitHubのスター数は1000程である。


lintとは

wikiより引用


lint とは、主にC言語のソースコードに対し、コンパイラよりも詳細かつ厳密なチェックを行うプログラムである。

型の一致しない関数呼び出し

初期化されていない変数の参照

宣言されているが使われていない変数

同じ関数を参照しているが、戻り値を使う場合と使わない場合がある[疑問点 – ノート]

関数が戻り値を返す場合と返さない場合がある

など、コンパイラではチェックされないが、バグの原因になるような曖昧な記述についても警告される



導入手順

特に特別な手順はありません

バイナリをダウンロードして実行するだけで解析が始まります。

$ curl -L -O https://github.com/hadolint/hadolint/releases/download/v1.15.0/hadolint-Linux-x86_64

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 611 0 611 0 0 935 0 --:--:-- --:--:-- --:--:-- 935
100 3561k 100 3561k 0 0 536k 0 0:00:06 0:00:06 --:--:-- 767k

$ chmod +x hadolint-Linux-x86_64


実行

実行はバイナリを実行し引数にdockerfileを与えるのみで解析してくれます。

FROM debian:latest

MAINTAINER ryuichi1208

COPY package.json usr/src/app
RUN cd /usr/src/app \
&& npm install node-static

EXPOSE 80
EXPOSE 80000

上記のようなDockerfileを解析かけてみます

$ ./hadolint-Linux-x86_64 Dockerfile

Dockerfile:1 DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag
Dockerfile:2 DL4000 MAINTAINER is deprecated
Dockerfile:5 SC2164 Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Dockerfile:5 SC1109 This is an unquoted HTML entity. Replace with corresponding character.
Dockerfile:5 SC1070 Parsing stopped here. Mismatched keywords or invalid parentheses?
Dockerfile:5 DL3003 Use WORKDIR to switch to a directory
Dockerfile:9 DL3011 Valid UNIX ports range from 0 to 65535

はい結果は上記の通り。

ポートの範囲がおかしいよとか

現行では推奨しない書き方だったり

コンテナサイズについてまでコメントしてくれます。

もちろんエラーも出してくれます。

ルールの一覧はREADMEをご確認ください。

意図的にやってるんだよ!ってメッセージは除外できるような機能もありますのでそちらを。

$ hadolint --ignore DL3003 --ignore DL3006 <Dockerfile>

ちなみに指摘が一切ない場合は下記のように終了ステータスは0を返してくれる。

これをもとに何らかの自動化処理を入れるのもありですね。

$ ./hadolint-Linux-x86_64 Dockerfile ; echo $?

0


ルール集

GitHubより転載

ルール
説明

DL3000
絶対パスを使用してください

DL3001
For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig.

DL3002
Last user should not be root.

DL3003
WORKDIRを使用してディレクトリに切り替えます。

DL3004
Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root.

DL3005
apt-get upgradeまたはdist-upgradeを使用しないでください。

DL3007
Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag.

DL3006
Always tag the version of an image explicitly.

DL3008
Pin versions in apt-get install.

DL3009
Delete the apt-get lists after installing something.

DL3010
アーカイブにイメージを抽出するには、ADDを使用してください

DL3011
Valid UNIX ports range from 0 to 65535.

DL3012
Provide an email address or URL as maintainer.

DL3013
Pin versions in pip.

DL3014
-yスイッチを使用します。

DL3015
Avoid additional packages by specifying --no-install-recommends.

DL3016
Pin versions in npm.

DL3017
apk upgradeを使用しないでください。

DL3018
Pin versions in apk add. Instead of apk add use apk add =.

DL3019
Use the --no-cache switch to avoid the need to use --update and remove /var/cache/apk/* when done installing packages.

DL3020
Use COPY instead of ADD for files and folders.

DL3021
COPY with more than 2 arguments requires the last argument to end with /

DL3022
COPY --from should reference a previously defined FROM alias

DL3023
COPY --from cannot reference its own FROM alias

DL3024
FROM aliases (stage names) must be unique

DL3025
Use arguments JSON notation for CMD and ENTRYPOINT arguments

DL3026
Use only an allowed registry in the FROM image

DL4000
MAINTAINERは推奨されていません。

DL4001
WgetまたはCurlを使用しますが、両方を使用することはできません。

DL4003
複数のCMD命令が見つかりました。

DL4004
Multiple ENTRYPOINT instructions found.

DL4005
Use SHELL to change the default shell.

DL4006
Set the SHELL option -o pipefail before RUN with a pipe in it

SC1000
$ is not used specially and should therefore be escaped.

SC1001
This \c will be a regular 'c' in this context.

SC1007
Remove space after = if trying to assign a value (or for empty string, use var='' ...).

SC1010
Use semicolon or linefeed before done (or quote to make it literal).

SC1018
This is a unicode non-breaking space. Delete it and retype as space.

SC1035
You need a space here

SC1045
It's not foo &; bar, just foo & bar.

SC1065
Trying to declare parameters? Don't. Use () and refer to params as $1, $2 etc.

SC1066
代入の左側に$を使用しないでください。

SC1068
Don't put spaces around the = in assignments.

SC1077
For command expansion, the tick should slant left (` vs ´).

SC1078
Did you forget to close this double-quoted string?

SC1079
This is actually an end quote, but due to next char, it looks suspect.

SC1081
Scripts are case sensitive. Use if, not If.

SC1083
This {/} is literal. Check expression (missing ;/\n?) or quote it.

SC1086
forループのイテレータ名に$を使用しないでください。

SC1087
Braces are required when expanding arrays, as in ${array[idx]}.

SC1095
You need a space or linefeed between the function name and body.

SC1097
Unexpected ==. For assignment, use =. For comparison, use [/[[.

SC1098
Quote/escape special characters when using eval, e.g. eval "a=(b)".

SC1099
#の前にスペースが必要です。

SC2026
This word is outside of quotes. Did you intend to 'nest '"'single quotes'"' instead'?

SC2028
echo won't expand escape sequences. Consider printf.

SC2035
Use ./glob or -- glob so names with dashes won't become options.

SC2046
Quote this to prevent word splitting

SC2086
Double quote to prevent globbing and word splitting.

SC2140
Word is in the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?

SC2154
varは参照されますが割り当てられません。

SC2155
Declare and assign separately to avoid masking return values.


まとめ

いちいちbuildする手間も省けるので個人的には大活躍している。

アップデートは現在も続いており簡単なDockerfile作成時はパスを通したディレクトリへ置いておくことで

サクッとチェックできるのでお勧めです。