ลองเล่น Docker Multi-Stage Builds

Wirawat Roj
3 min readDec 30, 2020

--

Docker

สวัสดีครับสำหรับบทความวันนี้ ผมจะพามารู้จักกับ Docker multi-stage builds ครับ ซึ่งเนื้อหาในบทความนี้ก็จะออกแนวเป็นบันทึกของการที่ผมไปลองเล่น Docker multi-stage builds มานะครับ

Docker Multi-Stage Builds คืออะไรกัน

ก่อนอื่นต้องบอกก่อนว่าในการ build docker images ออกมา images นึงเนี่ย ในกรณีของผมที่ทำเป็น project .NET เป็นหลักๆนั้น โดยใน Dockerfile ก็จะทำตั้งแต่ build , restore และ publish เลยเนี่ย สามารถทำให้ images ออกมามีขนาดที่ใหญ่มาก ๆ จนกินพื้นที่ 1GB ถึง 2GB เลยทีเดียว ซึ่งเป็นขนาดที่กินพื้นที่มากๆ ซึ่ง Multi-Stage Builds ก็คือฟังก์ชันนึงของ Docker ที่ออกมาเพื่อ optimize เรื่องขนาดของ Image โดยจากการที่ผมลองเล่นมา ก็พบว่าสามารถลดขนาดของ Image สุดท้ายลงไปได้ค่อนข้างเยอะเลยทีเดียว~

เริ่มลองเล่น Multi-Stage Build !!

ในส่วนนี้จะเป็นผลลัพธ์จากการที่ผมไปลองเล่นด้วยตัวเองดูนะครับ
ขั้นแรกเริ่มจากลองเขียน Dockerfile แบบปกติ โดยใส่ทุก step ไว้ด้วยกัน แล้วลอง build เพื่อดูขนาดของ Image

โดยหน้าตาของ Dockerfile ไฟล์แรกเป็นแบบนี้

FROM microsoft/dotnet:2.2-sdk
WORKDIR /src
COPY . .
RUN dotnet restore ./Test.MultiStage.csproj
RUN dotnet build ./Test.MultiStage.csproj
RUN dotnet publish -o ./artifact/ ./Test.MultiStage.csproj
ENTRYPOINT [ "dotnet","/src/artifact/Test.MultiStage.dll" ]

จากนั้นสั่ง build ด้วยคำสั่ง

docker build -t test/multi-stage .\Test.MultiStage\.

เมื่อเราลองมาดูขนาดของ Image ที่ได้

ก็จะพบว่า image ที่ออกมามีขนาดที่ใหญ่มากๆ ตั้ง 2.03GB แหน่ะ

จากนั้นผมลองปรับ Dockerfile โดยใช้เรื่องของ multi-stage build ด้วย จนได้ Dockerfile ที่หน้าตาแบบนี้

FROM microsoft/dotnet:2.2-sdk as builder
WORKDIR /src
COPY . .
RUN dotnet restore ./Test.MultiStage.csproj
RUN dotnet build ./Test.MultiStage.csproj
FROM builder as publisher
RUN dotnet publish -o ./artifact/ ./Test.MultiStage.csproj
FROM microsoft/dotnet:2.2-aspnetcore-runtime
WORKDIR /src
COPY --from=publisher /src/artifact /src/artifact
ENTRYPOINT [ "dotnet","/src/artifact/Test.MultiStage.dll" ]

จากนั้นสั่ง build ด้วยคำสั่ง

docker build -t test/multi-stage-2 .\Test.MultiStage\.

และเมื่อเราลองมาดูขนาดของ Image ที่ได้

จะพบว่าขนาดของ Image สุดท้ายที่เราจะนำไปใช้ต่อนั้น มีขนาดเล็กกว่าเดิมมาก โดยเหลือเพียง 282MB เท่านั้น!!!

ดังนั้นเราก็ลอง Run ดู ว่า Image สุดท้ายนี้สามารถใช้ได้จริงหรือเปล่า!?

docker run --rm -p 7000:80 1854076e6d73

จากนั้นก็ลองเปิด browser และเข้าไปที่ port 7000 ดู

ก็พบว่าสามารถเรียกหน้า Swagger ขึ้นมาแสดงได้ครับ~

เพิ่มเติมในส่วนของ Dockerfile แบบ Multi-Stage ข้างบน

# Stage 0
FROM microsoft/dotnet:2.2-sdk as builder
...
build
...
# Stage 1
FROM builder as publisher
... ใช้งานต่อจาก Stage 0 ด้านบน...
# Stage 2
FROM microsoft/dotnet:2.2-aspnetcore-runtime
COPY --from=publisher ...
...

ในการ docker build Dockerfile นี้จะมีทั้งหมด 3 Stage แบ่งเป็น builder (Stage 0 ), publisher (Stage 1) และ Stage สุดท้ายที่ไม่ได้ตั้งชื่อ (Stage 2)
ซึ่งที่ Stage builder กับ publisher จะเป็นการเตรียมไฟล์ Artifact โดยที่ Stage 1 จะใช้ image จาก Stage 0 สั่งเกตุได้จาก “FROM builder as publisher”
และใน Stage สุดท้าย (Stage 2) จะมีส่วนของการ COPY ไฟล์ artifact ที่ Stage publisher (Stage 1) publish เอาไว้ เพื่อเอาไปใช้งานต่อนั่นเอง

จึงทำให้ Image สุดท้าย มีขนาดที่เล็กลง เพราะมีแค่ Artifacts ที่เอาไปใช้ได้เลยและไม่มีในส่วนของ Source code และส่วนอื่นๆ เช่น OS จาก image ของ sdk ด้วยนั่นเอง

อ้างอิง

https://docs.docker.com/develop/develop-images/multistage-build/
https://www.docker.com/blog/advanced-dockerfiles-faster-builds-and-smaller-images-using-buildkit-and-multistage-builds/
https://link.medium.com/U8mp8Pz6Dcb

Special Thanks

ขอขอบคุณ “สำนักงานส่งเสริมเศรษฐกิจดิจิทัล (depa)” และคณาจารย์ “คณะเทคโนโลยีสารสนเทศ มจธ. (SIT)” ที่ให้การสนับสนุน “ทุนเพชรพระจอมเกล้าเพื่อพัฒนาเทคโนโลยีและนวัตกรรมดิจิทัล (KMUTT-depa)” ซึ่งเป็นทุนที่มอบความรู้ ทักษะและโอกาสดีๆ กับผมอย่างมาก~

--

--

Wirawat Roj

Software Engineer | 🍔 Always hungry | ❤️ Love Coding