Containers, Linux

Hướng dẫn các bước Xây dựng Image Docker với Dockerfile để Container hóa Ứng dụng

Giới thiệu

Trong thời đại container hóa ứng dụng hiện nay, Docker là một công cụ tiêu chuẩn giúp đóng gói ứng dụng thành các container dễ triển khai. Thay vì tạo image container một cách thủ công với nhiều bước lặp lại, Docker cung cấp Dockerfile – một file cấu hình cho phép tự động hóa quá trình xây dựng image. Dockerfile giúp việc tạo Docker image trở nên dễ dàng phiên bản hóa và mở rộng quy mô, thay cho các quy trình thủ công vốn khó quản lý.

Bài viết này sẽ hướng dẫn bạn cách sử dụng Dockerfile để xây dựng image Docker cho một ứng dụng web tĩnh (ví dụ website HTML/CSS/JS đơn giản) và container hóa ứng dụng đó. Chúng ta sẽ lần lượt tạo các phiên bản image (0.1, 0.2, 0.3), thêm dữ liệu website tĩnh vào container, kiểm tra kích thước và các tầng (layers) của image, cũng như cách chạy container và truy cập ứng dụng bên trong. Ngoài ra, bài viết giải thích chi tiết các lệnh Docker được sử dụng và đưa ra những lưu ý (best practices) quan trọng khi viết Dockerfile (dựa trên các pitfalls thường gặp). Đối tượng độc giả hướng đến là lập trình viên, kỹ sư DevOps, sinh viên CNTT hoặc người mới học Docker muốn cập nhật kiến thức về container .

Bước 1: Tạo Dockerfile và build image Docker đầu tiên (v0.1)

Trước tiên, hãy tạo thư mục dự án (ví dụ tên là widget-factory-inc/) và tạo thư mục con đặt tên là web trong thư mục này để chứa các files ứng dụng web tĩnh của bạn trong Linux server của bạn. Bên trong thư mục này, hãy download file này: https://raw.githubusercontent.com/phuongluuho/newcode/refs/heads/main/web.zip , và giải nén file, sau đó chuyển về lại thư mục widget-factory-inc/ và chúng ta sẽ tạo một file có tên Dockerfile và trong thư mục này có các files khác để giúp xây dựng image Docker.

Chọn base image: Ở đây, chúng ta sử dụng Apache HTTP Server 2.4 làm nền tảng để phục vụ website tĩnh, bằng cách khai báo image nền là httpd:2.4. Docker Hub cung cấp sẵn image chính thức cho Apache HTTPD. Việc dùng image có sẵn này giúp chúng ta không phải cài web server từ đầu.

Cập nhật hệ thống trong image: Tiếp theo, ta thêm một lệnh RUN để cập nhật các gói trong image. Cú pháp lệnh như sau:

FROM httpd:2.4
RUN apt update -y && apt upgrade -y && \
apt autoremove -y && apt clean && \
rm -rf /var/lib/apt/lists/*

Giải thích các dòng trên:

  • FROM httpd:2.4: Đặt image base là Apache HTTPD 2.4 trên hệ điều hành Debian. Image base này sẽ cung cấp sẵn môi trường chạy Apache web server.

  • RUN apt update -y && apt upgrade -y: Cập nhật danh sách gói và nâng cấp tất cả các gói hiện có trong image. Thao tác này đảm bảo image có các bản vá mới nhất. (Lưu ý: Trong thực tế, việc chạy upgrade toàn bộ có thể không cần thiết nếu image base đã cập nhật, xem phần Best Practices bên dưới).

  • && apt autoremove -y && apt clean: Gỡ bỏ các gói không cần thiết và dọn dẹp bộ nhớ đệm apt.

  • && rm -rf /var/lib/apt/lists/*: Xóa bỏ các file danh sách gói đã tải về. Việc xóa cache của apt ngay trong cùng lệnh RUN giúp giảm dung lượng image, vì dữ liệu cache sẽ không được lưu lại trong layer. Nói cách khác, thao tác này đảm bảo các thông tin cập nhật gói không làm tăng dung lượng image sau khi build.

Sau khi viết Dockerfile xong, lưu lại file. Bây giờ, chúng ta tiến hành build image Docker đầu tiên (gắn tag là widgetfactory:0.1) từ Dockerfile vừa tạo. Chạy lệnh sau trong terminal (tại thư mục chứa Dockerfile):

docker build -t widgetfactory:0.1 .

Trong đó, -t widgetfactory:0.1 đặt tên repository là “widgetfactory” và tag phiên bản là “0.1”. Dấu chấm (.) chỉ định ngữ cảnh build là thư mục hiện tại (bao gồm Dockerfile và các file cần thiết khác).

Quá trình docker build sẽ kéo image base httpd:2.4 về (nếu chưa có) và thực thi các câu lệnh trong Dockerfile. Sau khi build thành công, dùng lệnh docker images để kiểm tra danh sách image hiện có:

$ docker images

Kết quả có thể trả về là: image widgetfactory:0.1 đã được tạo, có kích thước khoảng 140MB, nhỉnh hơn một chút so với image gốc httpd:2.4 (~138MB). Sự tăng dung lượng nhỏ này do các gói được nâng cấp cài thêm. Chúng ta có thể kiểm tra chính xác dung lượng image bằng lệnh:

docker inspect -f "{{ .Size }}" widgetfactory:0.1

Lệnh trên sẽ in ra dung lượng image theo byte. Để tiện, bạn có thể thiết lập sẵn biến môi trường cho cú pháp định dạng:

export showSize='{{ .Size }}'
export showLayers='{{ range .RootFS.Layers }}{{ println . }}{{ end }}'

Bây giờ chạy docker inspect -f "$showSize" widgetfactory:0.1 sẽ trả về dung lượng của image (ví dụ: 146940154 bytes, tương đương ~140MB). Tương tự, dùng docker inspect -f "$showLayers" widgetfactory:0.1 sẽ liệt kê các layer của image widgetfactory:0.1. Mỗi layer được biểu diễn bằng một mã băm (sha256). Bạn cũng có thể dùng lệnh docker history widgetfactory:0.1 để xem các layer với thông tin từng lệnh Dockerfile tạo ra chúng.

Phân tích layers: Image base httpd:2.4 bao gồm nhiều layer (các lớp filesystem đóng gói Apache và hệ thống). Khi xây dựng widgetfactory:0.1, Dockerfile có một lệnh RUN, do đó đã thêm một layer mới trên nền image base. Kết quả, image widgetfactory:0.1 có tổng cộng 6 layers, trong khi image gốc httpd:2.4 có 5 layers – điều này có nghĩa lệnh RUN của chúng ta đã tạo thêm một layer. (Chỉ các lệnh như RUN, COPY, ADD mới tạo layer mới; các chỉ dẫn khác như WORKDIR, CMD, ENV… không tạo layer filesystem). Mỗi layer chứa các thay đổi so với layer trước đó – ở đây layer mới chứa các files được cập nhật/cài đặt khi chạy apt.

Bước 2: Nâng cấp image – Loại bỏ trang web mặc định (v0.2)

Ở phiên bản 0.1, image của chúng ta về cơ bản chỉ là Apache HTTPD được cập nhật hệ thống. Mặc định, Apache image đi kèm một trang “Welcome to Apache” hoặc trang index mẫu trong thư mục gốc /usr/local/apache2/htdocs/index.html. Mục tiêu của chúng ta là thay thế trang mặc định này bằng website tĩnh của riêng mình. Trước khi copy files website mới, chúng ta cần xóa trang index.html mặc định để tránh xung đột.

Hãy mở lại Dockerfile và bổ sung thêm một lệnh RUN mới để xóa file index.html mặc định:

# Dockerfile (tiếp tục phiên bản 0.2)
FROM httpd:2.4
RUN apt update -y && apt upgrade -y && \
apt autoremove -y && apt clean && \
rm -rf /var/lib/apt/lists/*
RUN rm -f /usr/local/apache2/htdocs/index.html

Chúng ta thêm dòng RUN rm -f /usr/local/apache2/htdocs/index.html để xóa file index.html. Tham số -f (force) để chắc chắn xóa mà không cần xác nhận. Lệnh này sẽ tạo thêm một layer mới trong image (vì mỗi lệnh RUN tạo một layer).

Lưu Dockerfile và tiến hành build phiên bản mới 0.2:

docker build -t widgetfactory:0.2 .

Docker sẽ thực thi lại các lệnh trong Dockerfile. Nhờ cơ chế cache của Docker, các bước FROM và RUN đầu tiên (apt update…) có thể được lấy từ cache (do Docker thấy nội dung lệnh không đổi so với lần build trước). Docker sẽ thêm bước RUN thứ hai (xóa file) và tạo image mới widgetfactory:0.2. Kiểm tra nhanh bằng docker images ta sẽ thấy image 0.2 xuất hiện. Dung lượng của widgetfactory:0.2 có thể sẽ gần như tương đương phiên bản 0.1 (~140MB). Thực tế, việc xóa một file trong layer mới không làm giảm dung lượng tổng thể của image, bởi vì dữ liệu file đó vẫn tồn tại ở layer bên dưới (layer của image gốc). Docker image mang tính cộng dồn layer – layer mới chỉ ghi nhận rằng file index.html đã bị xóa (nên nó không còn hiện hữu khi container chạy), nhưng bản thân nội dung file ở layer cũ vẫn chiếm dung lượng trong image. Nói cách khác, Docker không giải phóng dung lượng khi xóa file ở layer sau. Đây là một pitfall (cạm bẫy) phổ biến khi tối ưu Dockerfile.

Chúng ta có thể so sánh các layer giữa phiên bản 0.1 và 0.2 để thấy sự khác biệt. Sử dụng lệnh inspect đã thiết lập ở trên:

$ docker inspect -f "$showLayers" widgetfactory:0.1
sha256:... <hash layer 1>
sha256:... <hash layer 2>
...      <các layer tiếp theo> ...
sha256:abc123...40 <- layer từ lệnh RUN apt update/upgrade
$ docker inspect -f "$showLayers" widgetfactory:0.2
sha256:... <hash layer 1>
sha256:... <hash layer 2>
...      <các layer base tương tự widgetfactory:0.1> ...
sha256:abc123...40 <- layer RUN apt update/upgrade (giống 0.1)
sha256:def456...80 <- layer RUN rm index.html (mới ở 0.2)

Như trên, các layer đầu của 0.2 trùng với 0.1 (do dùng chung image base và bước RUN đầu giống nhau), và 0.2 có thêm một layer mới (với mã băm khác) cho lệnh xóa file. Tổng số layer của widgetfactory:0.2 sẽ là 7 layers (so với 6 của 0.1 và 5 của httpd ban đầu).

Bạn có thể thử chạy container từ image 0.2 để kiểm chứng rằng trang mặc định đã được xóa. Chạy lệnh sau để vào shell trong container (tùy chọn --rm để xóa container khi thoát, và -it để mở interactive terminal):

docker run --rm -it widgetfactory:0.2 bash

Sau khi vào container, liệt kê thư mục htdocs của Apache:

root@container:/usr/local/apache2/htdocs# ls -l

Kết quả sẽ cho thấy file index.html không còn trong thư mục (có thể thư mục rỗng hoặc chỉ còn các thư mục con nếu có). Gõ exit để thoát khỏi container.

Tóm lại: Phiên bản widgetfactory:0.2 đã loại bỏ trang web mặc định của Apache, sẵn sàng cho việc copy nội dung website của chúng ta vào trong image ở bước tiếp theo. Mặc dù xóa file, kích thước image vẫn không thay đổi đáng kể do đặc tính layer của Docker.

Bước 3: Thêm website tĩnh vào image (v0.3)

Đến đây, chúng ta sẽ đóng gói mã nguồn website tĩnh của mình vào image Docker. Như đã hướng dẫn ở đầu bài viết, như vậy trong thư mục dự án (widget-factory-inc/) có một thư mục con tên là web/ chứa toàn bộ file HTML, CSS, JS, hình ảnh… của website. Chúng ta sẽ copy thư mục này vào document root của Apache trong container.

Mở Dockerfile và bổ sung hai chỉ dẫn mới: WORKDIRCOPY:

# Dockerfile hoàn chỉnh cho widgetfactory:0.3
FROM httpd:2.4
RUN apt update -y && apt upgrade -y && \
apt autoremove -y && apt clean && \
rm -rf /var/lib/apt/lists/*
RUN rm -f /usr/local/apache2/htdocs/index.html
WORKDIR /usr/local/apache2/htdocs
COPY ./web .

Giải thích các lệnh mới:

  • WORKDIR /usr/local/apache2/htdocs: Thiết lập working directory mặc định cho các lệnh RUN, COPY, CMD tiếp theo là thư mục htdocs (thư mục gốc web của Apache bên trong container). Lệnh WORKDIR không tạo layer mới mà chỉ cập nhật context thực thi lệnh. Nhờ WORKDIR, chúng ta có thể dùng đường dẫn tương đối trong lệnh COPY kế tiếp.

  • COPY ./web .: Copy toàn bộ nội dung thư mục web (ở context build trên máy host) vào thư mục hiện tại trong image (đã được thiết lập bởi WORKDIR, tức là /usr/local/apache2/htdocs). Dấu . đầu tiên là đường dẫn tương đối tới thư mục web trên host, dấu . thứ hai đại diện cho thư mục hiện hành trong container. Lệnh COPY sẽ sao chép các file website (HTML, CSS, JS, hình ảnh…) vào image và tạo một layer mới chứa các file này.

Sau khi thêm hai dòng trên, lưu Dockerfile và build image phiên bản 0.3:

docker build -t widgetfactory:0.3 .

Quá trình build sẽ nhận thấy Dockerfile thay đổi, do đó thực thi thêm các bước WORKDIR và COPY. (Những bước trước có thể dùng cache từ lần build 0.2 nếu không thay đổi). Kết quả, chúng ta thu được image widgetfactory:0.3 chứa sẵn các files website trong thư mục htdocs.

Dùng docker images kiểm tra, bạn sẽ thấy widgetfactory:0.3 có dung lượng tăng nhẹ so với 0.2, tương ứng với dung lượng các file website được thêm vào. Nếu website tĩnh nhỏ (vài KB), kích thước image gần như không đổi (~140MB); nếu website nặng (nhiều hình ảnh), dung lượng sẽ tăng tương ứng.

Hãy xác minh nội dung website đã có trong image bằng cách chạy container và kiểm tra thư mục htdocs:

docker run --rm -it widgetfactory:0.3 bash
root@container:/usr/local/apache2/htdocs# ls -l

Bạn sẽ thấy danh sách các file trang web tĩnh đã được copy vào, ví dụ như trong hình:

Danh sách trên cho thấy các files và thư mục như index.html, css, js và thư mục img đã nằm trong /usr/local/apache2/htdocs của container. Vậy là website tĩnh của chúng ta đã được đóng gói thành công vào image Docker (phiên bản 0.3).

exit để thoát container.

Phân tích layers: Tương tự các bước trước, image widgetfactory:0.3 có thêm một layer mới cho lệnh COPY. Tổng số layer của 0.3 là 8 (bao gồm: 5 layer từ httpd base, 1 layer từ RUN apt, 1 layer từ RUN rm, và 1 layer từ COPY web). Lệnh WORKDIR không tạo layer filesystem mới nên không tính. Chúng ta có thể dùng docker inspect -f "$showLayers" widgetfactory:0.3 để liệt kê các layer và so sánh với 0.2, sẽ thấy một layer mới xuất hiện ở cuối danh sách so với 0.2.

Kiểm tra dung lượng và cấu trúc layers của các image

Ở các bước trên, chúng ta đã đề cập một số lần đến việc xem dung lượng và layer của image. Dưới đây tóm tắt một số cách kiểm tra:

  • Dùng lệnh **docker images**: liệt kê danh sách các image cùng kích thước của chúng (định dạng thân thiện, ví dụ MB). Bạn có thể quan sát kích thước của widgetfactory:0.1, 0.2, 0.3 và so sánh với httpd:2.4. Thông thường, 0.1 sẽ có thể lớn hơn httpd:2.4 một chút (do apt update/upgrade), 0.2 gần bằng 0.1 (xóa file không giảm dung lượng), và 0.3 nhỉnh hơn không đáng kể (thêm file website nhỏ).

  • Dùng lệnh **docker inspect -f "{{ .Size }}" <image>: trả về dung lượng image tính bằng byte. Có thể gắn thêm đơn vị bằng cách chia cho 1e+9 để ra GB hoặc 1e+6 để ra MB.

  • Dùng lệnh **docker inspect -f "{{ .RootFS.Layers }}" <image>: liệt kê các layer (mã băm) trong image. Cách chúng ta dùng ở trên với biến $showLayers chính là một định dạng tùy chỉnh để in mỗi layer trên một dòng.

  • Dùng lệnh **docker history <image>: hiển thị lịch sử build của image, liệt kê từng lệnh Dockerfile tương ứng mỗi layer, kèm dung lượng thay đổi của layer đó. Lệnh này rất hữu ích để thấy được layer nào đã làm tăng dung lượng image nhiều nhất. Ví dụ, nếu chạy docker history widgetfactory:0.3, bạn sẽ thấy các lệnh COPY ./web . (kèm dung lượng các file web) hay lệnh RUN apt ... (kèm dung lượng tăng do cài đặt gói).

Bằng các công cụ trên, bạn có thể phân tích và tối ưu Docker image – tìm xem bước nào làm image phình to, từ đó điều chỉnh Dockerfile cho hợp lý (chẳng hạn thay thế base image nhỏ hơn, gỡ bỏ thành phần không cần thiết, v.v.).

Chạy thử container từ image Docker đã tạo

Sau khi đã có image widgetfactory:0.3, chúng ta sẽ chạy một container từ image này để kiểm tra ứng dụng web hoạt động. Sử dụng lệnh docker run như sau:

docker run --name web1 -p 80:80 widgetfactory:0.3

Giải thích: Lệnh trên tạo và khởi động một container tên web1 từ image widgetfactory:0.3. Tham số -p 80:80 map cổng 80 của host (máy bạn) vào cổng 80 của container, giúp bạn có thể truy cập website trên trình duyệt qua cổng 80 của máy chủ. Image widgetfactory:0.3 kế thừa từ httpd:2.4 nên nó dùng entrypoint mặc định của httpd (chạy Apache ở foreground). Do không chỉ định -d (detached), container sẽ chạy ở chế độ foreground và hiển thị log của Apache trong terminal hiện tại.

Khi container web1 chạy, Apache bên trong sẽ khởi động và phục vụ website tĩnh. Bạn có thể mở trình duyệt truy cập http://localhost/ (hoặc IP của server nếu chạy trên máy chủ cloud) để xem trang web. Nếu mọi thứ đúng, bạn sẽ thấy trang web tĩnh mà chúng ta đã đóng gói.

Để dừng container, nhấn CTRL+C trong terminal (điều này gửi tín hiệu ngắt và dừng Apache, khiến container thoát). Container web1 lúc này ở trạng thái Exited. Kiểm tra bằng lệnh docker ps -a sẽ thấy container web1 vẫn tồn tại với trạng thái Exited. Bạn có thể khởi động lại container bất cứ lúc nào bằng lệnh:

docker start web1

Lệnh trên sẽ chạy container web1 ở chế độ nền (detached) mặc dù ta không dùng -d, vì khi start lại Docker mặc định không gắn vào terminal. Bây giờ container đã chạy background, bạn có thể truy cập website trên cổng 80 như trước.

Để kiểm tra bên trong container đang chạy, ta có thể dùng lệnh docker exec để “điều khiển” container:

docker exec -it web1 bash

Lệnh này mở một phiên shell bash tương tác bên trong container web1 đang chạy. Bạn có thể thực hiện các lệnh như ls -l /usr/local/apache2/htdocs để xác nhận các file web đang có, hoặc docker top web1 để thấy tiến trình Apache đang chạy. Sau khi kiểm tra, gõ exit để thoát shell (container vẫn tiếp tục chạy).

Cuối cùng, hãy thử gửi request HTTP đến container để chắc chắn nội dung web phục vụ đúng. Bạn có thể dùng curl hoặc wget. Thử lệnh sau trên máy host (nơi Docker chạy):

wget -q -O - http://localhost/ > index.html

Lệnh trên sẽ tải trang chủ (index) từ container và lưu nội dung vào file index.html trên host. Sau đó, so sánh file này với file gốc ban đầu trong thư mục web:

diff index.html web/index.html

Nếu hai file không có khác biệt (no output từ lệnh diff), điều đó khẳng định container đã phục vụ đúng nội dung website của bạn. Bạn cũng có thể mở trực tiếp trình duyệt để xem giao diện website.

(Lưu ý: Khi không cần dùng nữa, hãy dừng và xóa container: docker stop web1 (nếu đang chạy) và docker rm web1 để tránh chiếm dụng tài nguyên. Image có thể giữ lại để dùng sau, hoặc xóa bằng docker rmi widgetfactory:0.3 nếu muốn.)

So sánh các phiên bản image Docker (0.1, 0.2, 0.3)

Để tiện theo dõi, bảng sau đây tóm tắt sự khác nhau giữa các phiên bản image chúng ta đã tạo và image base ban đầu:

Image Chỉ dẫn Dockerfile bổ sung dung lượng Số layers
httpd:2.4 (base) (image gốc, chưa thêm gì) ~138 MB 5 layers (gốc)
widgetfactory:0.1 RUN apt update & upgrade (cập nhật hệ thống) ~140 MB (+2 MB) 6 layers (+1)
widgetfactory:0.2 RUN rm default index.html (xóa file) ~140 MB (~không đổi) 7 layers (+1)
widgetfactory:0.3 WORKDIR + COPY website tĩnh vào image ~141 MB (+1 MB) 8 layers (+1)

(Các số dung lượng trên chỉ mang tính minh họa gần đúng. Thực tế có thể chênh lệch tùy thời điểm cập nhật gói và dung lượng website).

Nhìn vào bảng, ta thấy mỗi lần thêm một lệnh RUN hoặc COPY, Docker image tăng thêm một layer và thường là tăng dung lượng (trừ trường hợp xóa file không giải phóng dung lượng như phiên bản 0.2). Phiên bản 0.1 thêm khoảng 2MB (do các gói được nâng cấp và cài đặt thêm); phiên bản 0.2 không đổi dung lượng đáng kể; phiên bản 0.3 tăng rất ít do website mẫu nhỏ. Số layer tăng dần từ 5 (base) lên 8 (v0.3) tương ứng với 3 lệnh bổ sung trong Dockerfile.

Việc quản lý phiên bản image với tag (0.1, 0.2, 0.3) giúp chúng ta theo dõi các thay đổi. Trong thực tế, bạn có thể dùng tag theo semver (ví dụ v1.0, v1.1) hoặc theo build number, commit hash tùy quy trình CI/CD của dự án.

Lưu ý khi viết Dockerfile và Best Practices

Như đã đề cập, một số bước trong hướng dẫn trên được thực hiện nhằm mục đích học tập và không phải lúc nào cũng là phương án tối ưu cho sản phẩm thực tế. Dưới đây là những lưu ý và best practices quan trọng khi xây dựng Dockerfile :

  • Hạn chế nâng cấp hệ thống không cần thiết: Việc chạy apt update && apt upgrade trong Dockerfile (như ở bước 0.1) giúp bảo đảm image có các bản vá mới nhất, nhưng đồng thời có thể làm tăng dung lượng image và thời gian build. Nếu bạn sử dụng image base chính thức và thường xuyên cập nhật image base lên phiên bản mới, có thể không cần thiết chạy apt upgrade mọi lúc. Chỉ cài đặt những gói thực sự cần cho ứng dụng của bạn, và nên dùng tham số --no-install-recommends để tránh kéo theo gói thừa. Khi cài gói bằng apt, luôn kết hợp xóa cache trong cùng lệnh RUN (đã làm đúng ở Dockerfile trên) để tiết kiệm dung lượng.

  • Mỗi lệnh RUN/COPY tạo một layer mới: Docker image được tạo thành từ các lớp (layer) bất biến chồng lên nhau. Dockerfile instruction như RUN, COPY, ADD sẽ sinh layer mới. Vì vậy, để giảm số layer (qua đó giảm độ phức tạp và đôi khi giảm dung lượng), bạn có thể gộp nhiều thao tác vào một lệnh RUN nếu có thể. Ví dụ: cài nhiều gói trong một RUN thay vì nhiều RUN riêng lẻ. Tuy nhiên, cũng không nên gộp tất cả thành một lệnh dài khó bảo trì – hãy cân bằng giữa số layer và tính rõ ràng của Dockerfile. (Lưu ý: WORKDIR, ENV, etc. không tạo layer filesystem nên không ảnh hưởng đến dung lượng, nhưng thay đổi chúng nhiều lần cũng có thể gây nhầm lẫn, nên đặt hợp lý).

  • Xóa file trong cùng layer khi có thể: Như đã phân tích, xóa file ở layer sau sẽ không loại bỏ dung lượng file đó khỏi image. Do đó, nếu bạn có file/tạm hoặc gói cài đặt không cần thiết, hãy xóa chúng ngay trong cùng lệnh RUN khi chúng được tạo. Ví dụ: cài xong thì rm ngay file cài đặt trong cùng dòng RUN. Trường hợp ở trên, chúng ta xóa index.html của Apache có sẵn từ base – file này ở layer base nên không có cách nào loại bỏ hoàn toàn trừ khi tạo image base riêng. Trong tình huống này, dung lượng file rất nhỏ nên chấp nhận được. Best practice tổng quát: tránh tạo dữ liệu mà sau đó phải xóa trong Dockerfile, hoặc sử dụng multi-stage build để giữ lại những gì thật sự cần.

  • Sử dụng image base phù hợp: Lựa chọn base image ảnh hưởng lớn đến dung lượng và bảo mật của image cuối. Image Apache httpd phiên bản Debian ở ví dụ trên ~140MB, trong khi bản Alpine của httpd chỉ ~50MB. Nếu ứng dụng của bạn có thể chạy tốt trên Alpine (hoặc các base tối giản như Distroless), hãy cân nhắc sử dụng để giảm dung lượng image đáng kể. Tuy nhiên, nhược điểm có thể là môi trường Alpine khác đôi chút (thư viện khác, lỗi vặt) nên hãy đánh giá yêu cầu dự án. Ngoài ra, năm 2025 Docker Hub có nhiều image chính thức và verified – nên ưu tiên các base image uy tín, nhỏ gọn và thường xuyên cập nhật bảo mật.

  • Đừng cài những thứ không cần thiết: Tránh thêm vào image những package hay file không dùng đến. Môi trường container nên tinh gọn. Ví dụ, không cần cài editors hay các dịch vụ dư thừa. Trong ví dụ, chúng ta chỉ giữ lại Apache và các file web tĩnh, không cài thêm gì khác ngoài việc update hệ thống.

  • Sử dụng .dockerignore: Khi build, Docker gửi toàn bộ thư mục context (ở đây là widget-factory-inc/) cho daemon. Sử dụng file .dockerignore để loại trừ những file/thư mục không cần thiết (như mã nguồn không liên quan, file git, tài liệu, v.v.) giúp tăng tốc độ build và tránh làm image phình to do vô tình COPY phải file thừa.

  • Đặt tag phiên bản cho image một cách có ý nghĩa: Trong hướng dẫn, ta dùng các tag 0.1, 0.2, 0.3 để minh họa các bước. Thực tế, nên đặt tag phản ánh rõ ràng phiên bản ứng dụng (ví dụ v1.0, v1.0.1) hoặc sử dụng CI/CD gắn tag tự động theo mã commit. Tránh sử dụng tag latest một cách mơ hồ cho image tùy chỉnh của bạn – hãy chỉ rõ phiên bản để triển khai nhất quán.

  • Kiểm tra và giảm thiểu lớp (layer) dư thừa: Docker cung cấp công cụ như dive (một phần mềm phân tích layer) giúp bạn xem bên trong image có những file gì ở mỗi layer, từ đó phát hiện file không mong muốn. Nếu thấy layer nào dung lượng lớn do cài nhiều thứ rồi xóa, cân nhắc tối ưu Dockerfile (như gợi ý gộp lệnh RUN ở trên). Ví dụ, thay vì hai lệnh RUN cài rồi xóa gói, hãy kết hợp thành một lệnh RUN để không tạo layer trung gian.

  • Multi-stage build: Dù ví dụ này không cần (vì website tĩnh không qua bước build phức tạp), nhưng nhớ rằng Dockerfile hỗ trợ multi-stage build – rất hữu ích khi bạn cần build ứng dụng (ví dụ ứng dụng Java, Go, Node.js) rồi đóng gói chạy. Sử dụng multi-stage cho phép bạn cài compiler và dependencies ở stage đầu, build xong thì COPY sản phẩm sang stage sau dùng base runtime gọn nhẹ. Nhờ đó image cuối cùng rất nhỏ và không chứa các công cụ build dư thừa.

Tóm lại, luôn xem xét hiệu quả của từng lệnh trong Dockerfile: liệu nó có cần thiết không, có cách nào viết gọn hơn không, và nó ảnh hưởng gì đến dung lượng/image cuối. “Nhiều thứ trong lab này cố tình làm chưa tối ưu để minh họa và nhấn mạnh vào các lỗi thường gặp – chúng không đại diện cho best practice”, vì vậy khi áp dụng vào thực tế, hãy tuân theo các best practices trên để có image Docker an toàn và tối ưu hơn.

Kết luận

Qua bài hướng dẫn này, chúng ta đã từng bước tạo ra Dockerfile và xây dựng Docker image cho một ứng dụng web tĩnh, từ phiên bản ban đầu đến khi hoàn thiện. Bạn đã học cách chọn base image phù hợp, viết các lệnh RUN, COPY cần thiết, và hiểu được mỗi lệnh trong Dockerfile ảnh hưởng thế nào đến layers và kích thước của image. Chúng ta cũng đã khởi chạy container từ image tạo ra và xác thực rằng ứng dụng hoạt động như mong đợi.

Việc container hóa ứng dụng bằng Dockerfile mang lại nhiều lợi ích: môi trường runtime nhất quán, triển khai dễ dàng trên nhiều máy, và tích hợp thuận lợi vào quy trình CI/CD. Quan trọng hơn, bạn có thể quản lý phiên bản ứng dụng thông qua phiên bản của image, tái tạo môi trường dựng ứng dụng bất kỳ lúc nào chỉ với Dockerfile và mã nguồn.

Như vậy, bạn đã nắm được một số best practices Docker : giữ cho image nhỏ và gọn, hạn chế layer không cần thiết, và tận dụng các tính năng như multi-stage build. Hãy áp dụng những nguyên tắc này khi container hóa các ứng dụng của riêng bạn – cho dù đó là một website nhỏ hay một dịch vụ phức tạp. Việc viết Dockerfile thành thạo sẽ giúp bạn (dù là lập trình viên hay DevOps) tự tin hơn trong việc đóng gói và triển khai ứng dụng trong môi trường Docker một cách hiệu quả và chuyên nghiệp.

Chúc bạn thành công với hành trình Docker hóa mọi thứ của mình!

#Docker #Dockerfile #Containerization #DevOps #BuildDockerImage#CloudComputing #Microservices #CI_CD #LinuxAdmin #SoftwareDeployment#LearnDocker #DockerForBeginners #DevOpsJourney #TechGuide #CodeNewbie

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *