DockerfileをLintする場合はHadolintがオススメです。
Haskell Dockerfile Linter
の略で、文字通りHaskellで実装されています。
Macで使ってみる
$ brew install hadolint
$ hadolint Dockerfile
これだけでDockerfileの静的解析が可能です。
特定のルールだけ除外してチェックしたい場合は下記のように実行します。
$ hadolint --ignore DL3003 --ignore DL3006 Dockerfile
ルール
DL3000
WORKDIR
を使用する際は絶対パスを使う。
# BAD
WORKDIR usr/src/app
# GOOD
WORKDIR /usr/src/app
DL3001
意味のないbash
コマンド(ssh
, vim
, shutdown
, service
, ps
, free
, top
, kill
, mount
, ifconfig
)を使わない。
# BAD
FROM busybox
RUN top
# GOOD
FROM busybox
DL3002
rootユーザを使わない。
# BAD
FROM busybox
USER root
# GOOD
FROM busybox
DL3003
ディレクトリの変更はWORKDIR
を使う。
# BAD
FROM busybox
RUN cd /usr/src/app
# GOOD
FROM busybox
WORKDIR /usr/src/app
DL3004
sudo
を使わない。
# BAD
FROM busybox
RUN sudo apt-get install
# GOOD
FROM busybox
RUN apt-get install
DL3005
apt-get
のupgrade
, dist-upgrade
を使わない。
# BAD
FROM debian
RUN apt-get update && apt-get upgrade
# GOOD
FROM debian
RUN apt-get update
DL3006
イメージのバージョンには明示的にタグを付ける。
# BAD
FROM debian
# GOOD
FROM debian:jessie
DL3007
イメージタグにlatest
を使わず明示的にリリースタグを使う。
# BAD
FROM debian:latest
# GOOD
FROM debian:jessie
DL3008
apt get install
をする際はバージョンを固定する。
# BAD
RUN apt-get install python
# GOOD
RUN apt-get install python=2.7
DL3009
apt-get install
の後にlistを削除する。
# BAD
RUN apt-get update && apt-get install -y python
# GOOD
RUN apt-get update && apt-get install -y python \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
DL3011
0
から65535
のポートを使う。
# BAD
EXPOSE 80000
# GOOD
EXPOSE 65535
DL3012
メンテナの連絡先を記述する。
# BAD
MAINTAINER ktsujichan
# GOOD
MAINTAINER ktsujichan <ktsujichan@example.com>
DL3013
pip
を使う際はバージョンを固定する。
# BAD
RUN pip install django
# GOOD
RUN pip install django==1.9
DL3014
-y
を使う。
# BAD
RUN apt-get install python=2.7
# GOOD
RUN apt-get install -y python=2.7
DL3015
--no-install-recommends
を使う。
# BAD
RUN apt-get install -y python=2.7
# GOOD
RUN apt-get install -y --no-install-recommends python=2.7
DL3020
ADDよりCOPYを使う。
# BAD
ADD requirements.txt /usr/src/app/
# GOOD
COPY requirements.txt /usr/src/app/
DL4000
メンテナを記述する。
# BAD
FROM busybox
# GOOD
FROM busybox
MAINTAINER ktsujichan <ktsujichan@example.com>
DL4001
wget
とcurl
を併用しない。
# BAD
RUN wget http://google.com
RUN curl http://bing.com
# GOOD
RUN curl http://google.com
RUN curl http://bing.com
DL4003
複数のCMD
を使わない。
# BAD
FROM busybox
CMD /bin/true
CMD /bin/false
# GOOD
FROM busybox
CMD /bin/true
DL4004
複数のENTRYPOINT
を使わない。
# BAD
FROM busybox
ENTRYPOINT /bin/true
ENTRYPOINT /bin/false
# GOOD
FROM busybox
ENTRYPOINT /bin/true
DL4005
デフォルトのシェルを変更する場合はSHELL
を使う。
# BAD
RUN apk add --update-cache bash=4.3.42-r3
RUN ln -sfv /bin/bash /bin/sh
# GOOD
RUN apk add --update-cache bash=4.3.42-r3
SHELL ["/bin/bash", "-c"]
SC1000
$
はエスケープして使う。
# BAD
echo "$"
# GOOD
echo "\$"
SC1007
=
の後のスペースを削除する。
# BAD
LANGUAGE= nl
# GOOD
LANGUAGE=nl
SC1010
done
の前にセミコロンまたは改行をする。
# BAD
for f in *; do echo "$f" done
# GOOD
for f in *; do echo "$f"; done
SC1035
スペースを入れる。
# BAD
if ![-z foo ]; then true; fi
# GOOD
if ! [ -z foo ]; then true; fi
SC1045
&
と;
を併用しない。
# BAD
foo &; bar
# GOOD
foo & bar
SC1065
引数の処理は$n
を使う。
# BAD
foo(input) {
echo "$input"
}
foo("hello world");
# GOOD
foo() {
echo "$1"
}
foo "hello world"
SC1066
変数の代入に$
を使わない。
# BAD
$greeting="Hello World"
# GOOD
greeting="Hello World"
SC1068
=
の前後にスペースを入れない。
# BAD
foo = 42
# GOOD
foo=42
SC1077
コマンド展開に´
を使わない。
# BAD
echo "Your username is ´whoami´"
# GOOD
echo "Your username is $(whoami)"
SC1078
ダブルクオートの文字列を閉じ忘れしない。
# BAD
greeting="hello
target="world"
# GOOD
greeting="hello"
target="world"
SC1081
If
ではなくif
を使う。
# BAD
If true
Then
echo "hello"
Fi
# GOOD
if true
then
echo "hello"
fi
SC1086
ループの反復変数に$
を使わない。
# BAD
for $var in *
do
echo "$var"
done
# GOOD
for var in *
do
echo "$var"
done
SC1087
配列を展開する際は中括弧を使う。
# BAD
echo "$array[@]"
# GOOD
echo "${array[@]}"
SC1095
関数名と処理の間にスペースか改行を入れる。
# BAD
function foo{
echo "hello world"
}
# GOOD
foo() {
echo "hello world"
}
function foo {
echo "hello world"
}
SC1097
==
を使わず、代入の場合は=
を、比較の場合は[ .. ]
を使う。
# BAD
var==value
# GOOD
var=value
[ "$var" = value ]
SC1098
eval
を使用する際は、特殊文字を引用/エスケープする。
# BAD
eval $var=(a b)
# GOOD
eval "$var=(a b)"
SC1099
#
の前にスペースを入れる。
# BAD
while sleep 1
do# show time
date
done
# GOOD
while sleep 1
do # show time
date
done
SC2002
cat
を使わない。
# BAD
cat file | tr ' ' _ | grep a_
cat file | ( while read i; do echo "${i%?}"; done )
# GOOD
< file tr ' ' _ | grep a_ # **simple commands only, won't work with compounds
( while read i; do echo "${i%?}"; done ) < file # postfix works for everything
SC2015
A && B || C
のロジックは、Aがtrue
だとCが実行されるのでif-then-else
ではない。
# BAD
[[ $dryrun ]] && echo "Would delete file" || rm file
# GOOD
if [[ $dryrun ]]
then
echo "Would delete file"
else
rm file
fi
SC2026
引用符の入れ子に注意する。
# BAD
alias server_uptime='ssh $host 'uptime -p''
# GOOD
alias server_uptime='ssh $host '"'uptime -p'"
SC2028
echo
はエスケープを展開しないので、printf
を使う。
# BAD
echo "Name:\t$value"
# GOOD
printf "Name:\t%s\n" "$value"
SC2035
./*glob*
か-- *glob*
を使う。
# BAD
rm *
# GOOD
rm ./*
# GOOD
rm -- *
SC2046
単語を分割させないようにクオートを使う。
# BAD
ls -l $(getfilename)
# GOOD
ls -l "$(getfilename)"
getfilename | while IFS='' read -r line
do
ls -l "$line"
done
SC2086
グロブと単語分割を防ぐためにダブルクオートを使う。
# BAD
echo $1
for i in $*; do :; done # this one and the next one also applies to expanding arrays.
for i in $@; do :; done
# GOOD
echo "$1"
for i in "$@"; do :; done # or, 'for i; do'
SC2140
引用符の入れ子に注意する。
# BAD
echo "<img src="foo.png" />" > file.html
# GOOD
echo "<img src=\"foo.png\" />" > file.html
SC2154
変数展開に注意する。
# BAD
var=name
n=42
echo "$var_$n.jpg"
# GOOD
var=name
n=42
echo "${var}_$n.jpg"
SC2164
cd
で失敗したときのために終了する。
# BAD
cd generated_files
rm -r *.c
func(){
cd foo
do_something
}
# GOOD
cd generated_files || exit
rm -r *.c
func(){
cd foo || return
do_something
}