DevOps
30 August 2019

ทำให้ Docker image เล็กลงด้วยการรวมคำสั่ง

ทำให้ Docker image เล็กลงด้วยการรวมคำสั่ง

คำสั่งต่างๆ ที่ถูกเขียนใน 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
http://gph.is/2aWabmw

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

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

ประโยชน์ของการเรียกใช้ Base OS เดียวในทุก Docker image
ความแตกต่างระหว่าง COPY และ ADD ใน Dockerfile