TL;DR
hadolint로 Dockerfile을 정적분석 할 수 있습니다.
사용법도 간단합니다.
현재 Github 스타는 3500개 입니다.
설치 및 사용법
Linux, Windows, Mac 모두에서 설치하여 사용할 수 있습니다. (현재 최신 버전 v1.17.5)
리눅스로 보겠습니다.
실행방법은 2가지 입니다.
구문
$ curl -L -O https://github.com/hadolint/hadolint/releases/download/v1.17.5/hadolint-Linux-x86_64
$ mv hadolint-Linux-x86_64 hadolint
$ chmod +x hadolint
$ ./hadolint Dockerfile # 실행
또는
구문
$ docker run --rm -i hadolint/hadolint < Dockerfile # 실행
간단한 Nginx Dockerfile 을 예로 보겠습니다.
최초 Dockerfile 작성 후, hadolint 실행시 4개의 지적사항이 발생됩니다.
Dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install -y -q nginx
COPY index.html /usr/share/nginx/html/
CMD ["nginx", "-g", "daemon off;"]
as-is
# 아웃풋 의미
# Dockerfile:<라인> <체크항목> <간략설명>
$ ./hadolint 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 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Dockerfile:2 DL3009 Delete the apt-get lists after installing something
Dockerfile:2 DL3015 Avoid additional packages by specifying `--no-install-recommends`
아래와 같이 Dockerfile을 수정 후, hadolint 재실행하면 지적사항이 사라짐을 확인할 수 있습니다.
Dockerfile
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y -q --no-install-recommends nginx=1.17.10 && rm -rf /var/lib/apt/lists/*
COPY index.html /usr/share/nginx/html/
CMD ["nginx", "-g", "daemon off;"]
to-be
$ ./hadolint Dockerfile
$
전체 체크항목 리스트는 아래와 같습니다.
Dockerfile
문법 35개, Shell
문법 33개의 Rule이 존재합니다.
Rule | Description |
---|---|
DL3000 | Use absolute WORKDIR. |
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 | Use WORKDIR to switch to a directory. |
DL3004 | Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root. |
DL3005 | Do not use apt-get upgrade or dist-upgrade. |
DL3006 | Always tag the version of an image explicitly. |
DL3007 | Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag. |
DL3008 | Pin versions in apt-get install. |
DL3009 | Delete the apt-get lists after installing something. |
DL3010 | Use ADD for extracting archives into an image. |
DL3011 | Valid UNIX ports range from 0 to 65535. |
DL3012 | Provide an email address or URL as maintainer. |
DL3013 | Pin versions in pip. |
DL3014 | Use the -y switch. |
DL3015 | Avoid additional packages by specifying --no-install-recommends. |
DL3016 | Pin versions in npm . |
DL3017 | Do not use apk upgrade . |
DL3018 | Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version> . |
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 |
DL3027 | Do not use apt as it is meant to be a end-user tool, use apt-get or apt-cache instead |
DL3028 | Pin versions in gem install. Instead of gem install <gem> use gem install <gem>:<version>
|
DL4000 | MAINTAINER is deprecated. |
DL4001 | Either use Wget or Curl but not both. |
DL4003 | Multiple CMD instructions found. |
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 | Don't use $ on the left side of assignments. |
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 | Don't use $ on the iterator name in for loops. |
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 [ .. ] or [[ .. ]] . |
SC1098 | Quote/escape special characters when using eval , e.g. eval "a=(b)" . |
SC1099 | You need a space before the # . |
SC2002 | Useless cat. Consider cmd < file | .. or cmd file | .. instead. |
SC2015 | Note that A && B || C is not if-then-else. C may run when A is true. |
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. |
SC2039 | In POSIX sh, something is undefined. |
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 is referenced but not assigned. |
SC2155 | Declare and assign separately to avoid masking return values. |
SC2164 | Use cd ... || exit in case cd fails. |