Dockerfile

最後更新: 2023-06-02

Dockerfile

A plain text file that specifies all of the components that are included in the container.

目錄

  • INSTRUCTION
  • 建立 Image
  • Example
  • Interactive
  • Interrupting a podman build
  • Log

 


INSTRUCTION

 

Dockerfile 指令的一般格式為

INSTRUCTION arguments

資訊類指令操作

MAINTAINER

維護者訊息

MAINTAINER STRING

LABEL

adds metadata to an image

LABEL <key1>=<value1> <key2>=<value2> ...

LABEL <key1>=<value1> \
      <key2>=<value2> \
      ...

FROM

FROM <image>
FROM <image>:<tag>

ENV

# 指定一個環境變數 (會被後續 RUN 指令使用)

ENV <key>=<value>

* The value will be interpreted for other environment variables

* quote characters will be removed if they are not escaped

SHELL

 * SHELL is not supported for OCI image format, it will be ignored.

設定 RUN 時所用到的 Shell

SHELL ["executable", "parameters"]    # Default '["/bin/sh", "-c"]'

 * The SHELL instruction can appear multiple times. Each SHELL instruction overrides previous

RUN

# 每條 RUN 指令將在當前映像檔 "/" 上以 shell 執行

RUN <command>

# 用 exec 執行

RUN ["executable", "param1", "param2"]

# 行多行的 script

RUN <<EOT
#!/usr/bin/env python
print("hello world")
EOT

WORKDIR

轉換在 CT 內的 PWD

影響 RUN, CMD, ENTRYPOINT, COPY and ADD

WORKDIR /var/log

log

STEP 10/14: WORKDIR /var/log
--> Using cache 563cb0a7637b31d9c37411344d7e418555f17032d0a8c576f348657a5838e70b
--> 563cb0a7637

Remark

  • If not specified, the default working directory is "/"
  • The WORKDIR instruction can be used multiple times in a Dockerfile.
  • If a relative path is provided, it will be relative to the path of the previous WORKDIR instruction.
  • The WORKDIR instruction can resolve environment variables previously set using ENV.

EXPOSE

# 設定 Docker Container 對外的 Port

EXPOSE <port> [<port>...]

 * The EXPOSE instruction defines metadata only
    (it does not make ports accessible from the host.)

 * "docker run -P ... " 時, Docker 會自動分配其他 port 並 NAT 到 EXPOSE 的 port
    ( 如果要指定 Port 就要用 -p option )

CMD

# 指定啟動容器時執行的命令

 * 每個 Dockerfile 只可以有一個 CMD (最後一條會被執行)

 * CMD does not execute anything at build time, but specifies the intended command for the image.

# 使用 exec 執行

CMD ["executable","param1","param2"]

CMD 與 ENTRYPOINT

如果同時定義了 CMD 和 ENTRYPOINT, 則 CMD 會被當作 ENTRYPOINT 的默認參數

 * CMD 只是作為 ENTRYPOINT 所需的預設參數

行 system service 的 CMD

CMD ["tail", "-F", "/dev/null"]

P.S. "podman exec CT_NAME ps aux" 入去看

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0   2888  1040 ?        Ss   09:41   0:00 /bin/sh -c tail -F /dev/null
root           7  0.0  0.0   2820  1040 ?        S    09:41   0:00 tail -F /dev/null

COPY

COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>

# 支援 "*", "?"

COPY *.txt /mydir/

# adds "test.txt" to <WORKDIR>/relativeDir/

COPY test.txt relativeDir/

注意

<src>

 * The <src> path must be inside the context of the build; you cannot COPY ../something /something

 * If <src> is a directory, the entire contents of the directory are copied
    (The directory itself is not copied, just its contents.)

 * If <src> is any other kind of file, if <dest> ends with a trailing slash / , <src> will be written at <dest>/base(<src>)

 * If multiple <src>resources are specified, either directly or due to the use of a wildcard,
    then <dest>must be a directory, and it must end with a slash/

<dest>

* If <dest> does not end with a trailing slash,
   it will be considered a regular file and the contents of <src> will be written at <dest>

* If <dest> doesn't exist, it is created along with all missing directories in its path.

 

ADD

# 複製指定的 <src> 到容器中的 <dest>
# src 可以是 Dockerfile 所在目錄的相對路徑
# 當 src 是 .tar.gz 檔時, 其複製後會自動解壓縮
# 當 src 是 URL 時, 會自動下載

ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>

Opts

  • --checksum=sha256:...

ADD by Git

ADD [--keep-git-dir=<boolean>] <git ref> <dir>
ADD [email protected]:foo/bar.git /bar

COPY vs ADD

The major difference is that ADD can do more than COPY

ARG

Instruction defines a variable that users can pass at build-time to the builder

yml

ARG <name>[=<default value>]

build-time input: --build-arg <varname>=<value>

引用: $varname / ${varname}

i.e.

# 使用 ARG 設定 ENV

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

 


建立 Image

 

dnf install podman -y

mkdir mylamp; cd !$

vim Dockerfile

podman build -t t1: .

podman images

REPOSITORY                TAG         IMAGE ID      CREATED        SIZE
localhost/t1              latest      f6dfed79d98f  4 minutes ago  230 MB

podman run localhost/t1

P.S.

# 指定 build 那一個 file 並設定它的 tag

podman build -t t1:v2 -f Dockerfile.v2

 


Example

 

[1A]

FROM ubuntu:22.04

# Install dependencies
RUN apt-get update && \
 apt-get -y install apache2

# Install apache and write hello world message
RUN echo 'Hello World!' > /var/www/html/index.html

# Configure apache
#RUN echo '. /etc/apache2/envvars' > /root/run_apache.sh && \
# echo 'mkdir -p /var/run/apache2' >> /root/run_apache.sh && \
# echo 'mkdir -p /var/lock/apache2' >> /root/run_apache.sh && \
# echo '/usr/sbin/apache2 -D FOREGROUND' >> /root/run_apache.sh && \
# chmod 755 /root/run_apache.sh
#CMD /root/run_apache.sh

EXPOSE 80

[1B]

 

[2]

FROM debian:jessie

MAINTAINER NGINX Docker Maintainers "[email protected]"

ENV NGINX_VERSION 1.11.5-1~jessie

RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \
    && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list \
    && apt-get update \
    && apt-get install --no-install-recommends --no-install-suggests -y \
                        ca-certificates \
                        nginx=${NGINX_VERSION} \
                        nginx-module-xslt \
                        nginx-module-geoip \
                        nginx-module-image-filter \
                        nginx-module-perl \
                        nginx-module-njs \
                        gettext-base \
    && rm -rf /var/lib/apt/lists/*

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

-g directives  Set global configuration directives.

 


Interrupting a podman build

 

podman images

<none>                    <none>      54f788544499  37 minutes ago  231 MB

podman rmi 54f788544499

image is in use by a container: consider listing external containers and force-removing image

podman ps --all

podman ps --all --storage

container running by COMMAND buildah

Fix

podman rm --force ID

 


Troubleshoot build Fail

 

 

podman run -it --name test ubuntu:22.04 /bin/bash

一步步行 CLI

podman rm podman

 


Log

 

# forward request and error logs to docker log collector

RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

在 container 內, nginx 需要以 CMD 啟動才得.
因為 docker log 係食 pid 1 的 stdout 及 stderr

ls -l /dev/std*

lrwxrwxrwx 1 root root 15 Jun  6 06:55 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Jun  6 06:55 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Jun  6 06:55 /dev/stdout -> /proc/self/fd/1

self

magic symbolic link, it resolves to the process's own /proc/[pid] directory

執行中的 CMD. cat 會是 cat. 測試 'cat /proc/self/comm'

 


healthcheck

 

Default

  • interval: 30s
  • timeout: 30s
  • start_period: 30s
  • retries: 3

start_period:

added in file format 3.4. initialization time for containers that need time to bootstrap

docker-compose.yml

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]
  interval: 1m30s
  timeout: 10s
  start_period: 40s
  retries: 3

Dockerfile

Syntax

# running a command inside the container
HEALTHCHECK [OPTIONS] CMD command

# disable any healthcheck inherited from the base image
HEALTHCHECK NONE

exit status

0: success - the container is healthy and ready for use
1: unhealthy - the container is not working correctly

log

text (UTF-8 encoded) that the command writes on stdout or stderr will be stored in the health status (max: 4k)
checking: docker inspect CT

OPTIONS

  • --interval=DURATION
  • --timeout=DURATION
  • --start-period=DURATION
  • --retries=N

i.e.

HEALTHCHECK --interval=1m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

 


建立 Ubuntu Docker Image

 

apt-get update

對應的 cleanup

rm -rf /var/lib/apt/lists/*

apt-get upgrade

對應的 cleanup

apt-get clean all

# clean clears out the local repository of retrieved package files.

# It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial/

apt-get 的 interactive 安裝

i.e. 安裝時 tzdata 會遇到的問題

apt-get -y install tzdata

即使設定了 "ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone"

當沒有 tzdata package 也是會錯 timezone 的

log

debconf: (TERM is not set, so the dialog frontend is not usable.)

Fix

[方案A]

ENV DEBIAN_FRONTEND noninteractive

[方案B]

RUN DEBIAN_FRONTEND=noninteractive apt-get -y install tzdata

設定使用 bash Shell

ln -sf /usr/bin/bash /bin/sh

 


 

 

Creative Commons license icon Creative Commons license icon