คำสั่งต่างๆ ที่ถูกเขียนใน Dockerfile จะทำให้ตอนสร้าง Docker image ขึ้นมามีขนาดใหญ่ขึ้นเรื่อยๆ ซึ่งวิธีการที่จะช่วยให้มันเล็กลงได้ก็คือ การเขียนคำสั่งทั้งหมดไว้เป็นคำสั่งเดียว ซึ่งมันเป็นวิธีที่ง่าย และเห็นผลชัดเจนมากในการลดขนาด Docker image

เรามาดูกันว่าถ้าเราเขียน Dockerfile โดยมีหลายคำสั่งระบบ Docker จะทำงานยังไง

FROM debian:stable

WORKDIR /var/www

RUN apt-get update
RUN apt-get -y --no-install-recommends install curl
RUN apt-get -y --no-install-recommends install ca-certificates
RUN apt-get purge -y curl
RUN apt-get purge -y ca-certificates
RUN apt-get autoremove -y
RUN apt-get clean

ในแต่ละคำสั่งที่เกิดขึ้นตั้งแต่ FROM, WORKDIR และ RUN อีกหลายครั้ง docker จะทำการสร้าง Layer ไว้เพื่อประโยชน์ในการทำ Caching

$ docker build -t docker-tip .
Sending build context to Docker daemon  2.048kB
Step 1/9 : FROM debian:stable
stable: Pulling from library/debian
6a75cf7ef35e: Pull complete 
Digest: sha256:6a3ead8cbca86c3c28c5f32d250df9203f7cb939ed07ac6925d7a7a788554911
Status: Downloaded newer image for debian:stable
 ---> f5356914cf3c
Step 2/9 : WORKDIR /var/www
 ---> Running in 1175c44beb96
Removing intermediate container 1175c44beb96
 ---> 76f0fce3f20a
Step 3/9 : RUN apt-get update
 ---> Running in 21d144f3a15a
Get:2 http://cdn-fastly.deb.debian.org/debian stable InRelease [118 kB]
...
...
...
Step 9/9 : RUN apt-get clean
 ---> Running in 5e941426cb57
Removing intermediate container 5e941426cb57
 ---> f152781a040d
Successfully built f152781a040d
Successfully tagged docker-tip:latest

สังเกตุว่ามันจะทำงานตามคำสั่งที่เราเขียนไว้ โดยแบ่งเป็น Step 1/9: จนถึง Step 9/9: ซึ่งพวกนี้ได้มีการทำ Caching ไว้แล้ว หากเรามีการแก้ไขเพียงบ้างคำสั่ง แล้วสั่ง build ใหม่ มันก็จะเรียกเฉพาะ Step ที่มีการแก้ไขมาทำงานพอ ทำให้การ Build เร็วขึ้น

$ docker history docker-tip:latest 
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f152781a040d        5 minutes ago       /bin/sh -c apt-get clean                        0B                  
2084ae6a31b4        5 minutes ago       /bin/sh -c apt-get autoremove -y                1.01MB              
bd1a0e5247d0        5 minutes ago       /bin/sh -c apt-get purge -y ca-certificates     1.83MB              
418b55128fc5        5 minutes ago       /bin/sh -c apt-get purge -y curl                1.04MB              
839356141e5d        5 minutes ago       /bin/sh -c apt-get -y --no-install-recommend…   3.74MB              
75c17e8eacd8        6 minutes ago       /bin/sh -c apt-get -y --no-install-recommend…   10.6MB              
4f94ae71ba4a        6 minutes ago       /bin/sh -c apt-get update                       17.1MB              
76f0fce3f20a        6 minutes ago       /bin/sh -c #(nop) WORKDIR /var/www              0B                  
f5356914cf3c        8 days ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B                  
<missing>           8 days ago          /bin/sh -c #(nop) ADD file:bd22a8f9357bbfdeb…   114MB 

ลองดูขนาดในแต่ละ Layer ว่ามีขนาดเท่าไร และสุดท้ายขนาด docker image ที่ได้ก็คือ

$ docker images
REPOSITORY             TAG                  IMAGE ID            CREATED             SIZE
docker-tip             latest               f152781a040d        7 minutes ago       149MB

คราวนี้เราจะลองลดขนาด docker image ให้เล็กโดยการรวมคำสั่ง RUN ให้เหลืออันเดียวดู

FROM debian:stable

WORKDIR /var/www

RUN apt-get update && \
  apt-get -y --no-install-recommends install curl \
  ca-certificates && \
  curl https://raw.githubusercontent.com/gadiener/docker-images-size-benchmark/master/main.go -o main.go && \
  apt-get purge -y curl \
  ca-certificates && \
  apt-get autoremove -y && \
  apt-get clean

มาลอง Build กัน

$ docker build -t docker-tip:shrinking .

ตรวจดูหน่อยว่าขนาดเป็นยังไงบ้าง

$ docker history docker-tip:shrinking 
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
de2242244ff6        About a minute ago   /bin/sh -c apt-get update &&   apt-get -y --…   18.9MB              
76f0fce3f20a        11 minutes ago       /bin/sh -c #(nop) WORKDIR /var/www              0B                  
f5356914cf3c        8 days ago           /bin/sh -c #(nop)  CMD ["bash"]                 0B                  
<missing>           8 days ago           /bin/sh -c #(nop) ADD file:bd22a8f9357bbfdeb…   114MB   

เปรียบเทียบให้เห็นชัดๆ ไปเลย

$ docker images
REPOSITORY             TAG                  IMAGE ID            CREATED             SIZE
docker-tip             shrinking            de2242244ff6        5 seconds ago       133MB
docker-tip             latest               f152781a040d        9 minutes ago       149MB

จะเห็นว่าเพียงเรารวมคำสั่งต่างๆ ของ RUN ไว้เป็นคำสั่งเดียวก็ทำให้ขนาด Docker image ของเราลดลงไปถึง 149MB-133MB = 16MB

ผมจะทำแบบนี้ทุกครั้ง ถ้าในช่วงที่กำลังเขียน Dockerfile อยู่นั้นให้ก็ให้แยก RUN ออกเป็นแต่ละคำสั่งได้ เพื่อถ้ามันมี error ตอนไหนเราจะได้รู้ว่าคำสั่งไหนผิด และเป็นการ Debug ไปในตัว จนเมื่อเราโอเคแล้วค่อยเขียนรวมเป็นอันเดียว จบปะ!!!