Docker đã trở thành một công cụ không thể thiếu trong quá trình phát triển, thử nghiệm và triển khai các ứng dụng hiện đại. Mặc dù nhiều người dùng Docker đã quen thuộc với các lệnh và thao tác cơ bản của nó, nhưng vẫn có những tính năng và thủ thuật nâng cao có thể gia tăng đáng kể năng suất và hợp lý hóa quy trình công việc. 

Trong bài viết này, VietnamWorks inTECH sẽ giới thiệu 10 thủ thuật Docker ít người biết đến nhưng chắc chắn sẽ mang lại giá trị lớn cho quá trình phát triển và triển khai ứng dụng của bạn. 

1. Multi-stage build để tạo ra image hiệu quả

Bản dựng nhiều giai đoạn trong Docker là một kỹ thuật mạnh mẽ để tạo Docker image gọn gàng và an toàn hơn bằng cách tách môi trường xây dựng khỏi môi trường sản xuất trong cùng một Dockerfile. Phương pháp này không chỉ giúp giảm kích thước image cuối cùng mà còn giảm thiểu nguy cơ xâm nhập hoặc tấn công từ phía bên ngoài bằng cách loại trừ các công cụ và tệp không cần thiết khỏi runtime image.

1.1. Multi-stage build là gì?

Đây là tính năng cho phép bạn sử dụng nhiều câu lệnh FROM trong một Dockerfile. Mỗi câu lệnh FROM có thể sử dụng một cơ sở khác nhau, và mỗi giai đoạn có thể được đặt tên. Bằng cách làm như vậy, bạn có thể sao chép các thành phần từ một giai đoạn sang giai đoạn khác, loại bỏ tất cả những thứ bạn không muốn có trong image cuối cùng.

1.2. Cách sử dụng

# Syntax for multi-stage builds
# Stage 1: Build the application
FROM golang:1.15 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Stage 2: Create the final image
FROM alpine:latest
COPY --from=builder /app/myapp /app/myapp
ENTRYPOINT ["/app/myapp"]

Ví dụ này thể hiện một multi-stage đơn giản cho ứng dụng Go. Giai đoạn xây dựng sử dụng một image Golang để biên dịch ứng dụng, và giai đoạn cuối cùng xây dựng một image Alpine nhẹ chỉ chứa mã nhị phân đã biên dịch.

1.3. Khi nào nên sử dụng Multi-stage Builds

  • Giảm kích thước image: Khi bạn xây dựng ứng dụng, có lúc bạn cần các công cụ và thư viện chỉ dùng trong quá trình xây dựng. Multi-stage builds giúp loại bỏ những phần này khỏi image cuối cùng, giúp image trở nên nhỏ gọn hơn.

  • Nâng cao bảo mật: Bằng cách loại bỏ các công cụ và tệp không cần thiết từ image cuối cùng, multi-stage builds giúp giảm thiểu khả năng tấn công từ bên ngoài.

  • Phân tách môi trường xây dựng và môi trường chạy: Đôi khi, bạn cần xây dựng ứng dụng trong một môi trường và chạy nó trong một môi trường khác. Multi-stage builds cho phép bạn làm điều này một cách dễ dàng.

1.4. Cách ứng dụng Multi-stage build hiệu quả

  • Tối Ưu Hóa Sử Dụng Bộ Nhớ Cache Xây Dựng: Sắp xếp các hướng dẫn sao chép và chạy một cách cẩn thận để tối đa hóa lớp lưu trữ.

  • Giảm Thiểu Kích Thước image Cuối Cùng: Chỉ sao chép các tài liệu cần thiết để chạy ứng dụng vào image cuối cùng.

  • Đặt Nhãn Cho Các Giai Đoạn Xây Dựng: Sử dụng các giai đoạn có tên để cải thiện tính đọc và bảo trì của Dockerfile của bạn.

Lưu ý:

  • Tránh các bản dựng nhiều giai đoạn phức tạp không cần thiết có thể dẫn đến các Dockerfile khó bảo trì.

  • Chú ý đến bối cảnh xây dựng mà bạn gửi tới daemon Docker, vì các bối cảnh lớn không cần thiết có thể làm chậm quá trình xây dựng.

2. Squash (Nén) các lớp image

Nén các lớp image trong Docker là một kỹ thuật có thể giảm đáng kể kích thước Docker image của bạn. Bằng cách kết hợp tất cả các lớp được tạo trong quá trình xây dựng image thành một lớp duy nhất, bạn có thể tối ưu hóa việc lưu trữ và phân phối Docker image của mình.

2.1. Squash có nghĩa là gì?

Khi bạn xây dựng Docker image, mỗi lệnh trong Dockerfile sẽ tạo một lớp mới. Các lớp này có thể tích lũy dữ liệu không cần thiết, dẫn đến image bị cồng kềnh. Tính năng squash (nén) làm giảm số lượng lớp trong Docker image của bạn bằng cách hợp nhất chúng thành một, điều này có thể giúp giảm thiểu kích thước image.

2.2. Cách sử dụng

Để nén các lớp image, bạn có thể sử dụng flag --squash khi xây dựng image của mình khi bật Docker BuildKit. Đây là cách bạn có thể bật BuildKit và sử dụng tính năng này:

# Enable Docker BuildKit
export DOCKER_BUILDKIT=1

# Build and squash the image
docker build --tag myapp:latest --squash .

Lưu ý: flag --squash là một tính năng thử nghiệm nên có thể trong quá trình cài đặt chương trình sẽ yêu cầu bạn bật các tính năng thử nghiệm trong cấu hình Docker.

2.3. Khi nào nên sử dụng squash?

  • Tối Ưu Hóa Kích Thước image: Sử dụng squash khi bạn cần giảm kích thước của Docker image cuối cùng của bạn, đặc biệt là khi phân phối image qua mạng hoặc lưu trữ chúng trong một registry.

  • Đơn Giản Hóa Các Lớp image: Nếu Dockerfile của bạn chứa nhiều hướng dẫn run hoặc copy tạo ra nhiều lớp và tăng kích thước tổng của image.

2.4. Cách sử dụng squash hiệu quả

  • Nén Lớp Theo Lựa Chọn: Hãy xem xét xem những image nào được hưởng lợi nhiều nhất từ việc nén. Một số lớp có thể được để lại không nén cho mục đích caching, đặc biệt là trong quá trình phát triển.

  • Chú Ý Đến Caching: Việc nén có thể ảnh hưởng đến cơ chế lưu trữ lớp của Docker. Việc nén thường xuyên có thể dẫn đến thời gian xây dựng dài hơn vì Docker không thể lưu trữ cache của các lớp trung gian một cách hiệu quả.

  • Tác Động đến Bảo Mật: Hiểu rằng việc nén có thể ẩn đi lịch sử của một image. Hãy đảm bảo rằng lớp đã được nén cuối cùng không vô tình chứa dữ liệu nhạy cảm được dự định để bị loại bỏ trong các lớp trung gian.

Lưu ý:

  • Không nên lạm dụng quá mức trong quá trình phát triển: Việc nén sau mỗi bản dựng trong quá trình phát triển có thể làm chậm đáng kể thời gian xây dựng do mất đi lợi ích của bộ nhớ đệm.

  • Hãy nhớ rằng đây chỉ là tính năng thử nghiệm và có thể không hoạt động nhất quán trên các phiên bản hoặc cấu hình Docker khác nhau.

3. Docker BuildKit Secrets 

Đây là một công cụ được tích hợp vào Docker để cung cấp một quy trình xây dựng linh hoạt, mạnh mẽ hơn và bảo mật hơn so với quy trình xây dựng truyền thống. Nó cung cấp nhiều tính năng tiên tiến như quản lý bí mật an toàn, quy trình xây dựng song song hiệu quả, tối ưu hóa kích thước image và nhiều tính năng khác để tăng cường hiệu suất và bảo mật trong quá trình xây dựng và triển khai ứng dụng với Docker.

3.1. Cách sử dụng

Để sử dụng Docker BuildKit Secrets, trước tiên, hãy đảm bảo rằng Docker BuildKit đã được bật. Bạn có thể làm điều này bằng cách đặt DOCKER_BUILDKIT=1biến môi trường. Sau đó, sử dụng flag --secret trong quá trình xây dựng để cung cấp bí mật.

# Enable Docker BuildKit
export DOCKER_BUILDKIT=1

# Build with a secret
docker build --secret id=mysecret,src=/path/to/secret/file.txt -t myapp:latest .

Trong Dockerfile của bạn, bạn có thể truy cập secret như thế này:

# syntax=docker/dockerfile:1.2
FROM alpine
# Use the secret without exposing it in the image
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

3.2. Khi nào nên sử dụng tính năng này?

  • Truy cập Các Kho Lưu Trữ Git Riêng Tư: Khi quá trình xây dựng của bạn yêu cầu kéo các phụ thuộc từ các kho lưu trữ riêng tư.

  • Sử Dụng Khóa Riêng Tư: Khi bạn cần sử dụng các khóa riêng tư để truy cập SSH hoặc để giải mã các tệp trong quá trình xây dựng.

  • Mã thông báo API: Khi quá trình xây dựng của bạn liên quan đến việc truy cập các API yêu cầu xác thực.

3.3. Cách sử dụng Docker BuildKit Secrets hiệu quả

  • Chỉ sử dụng bí mật khi thực sự cần thiết và tránh ghi lại hoặc tiết lộ chúng trong bất kỳ bước trung gian nào.

  • Xác định rõ ràng và xác định phạm vi bí mật cho các giai đoạn xây dựng cụ thể khi cần thiết, giảm rủi ro bị lộ.

  • Luôn ghim phiên bản cú pháp trong Dockerfile của bạn khi sử dụng các tính năng BuildKit để đảm bảo tính tương thích và khả năng dự đoán.

Lưu ý: 

  • Bí mật mã hóa cứng: Tránh mã hóa bí mật cứng hoặc sử dụng các phương pháp không được dùng nữa (ví dụ: ARGđể truyền bí mật) có thể để lại thông tin nhạy cảm trong các lớp image.

  • Nhìn ra Dọn dẹp: Đảm bảo rằng không còn dư lượng bí mật nào trong image bằng cách kiểm tra các lớp cuối cùng hoặc vô tình tạo các lớp có thể lưu trong bộ nhớ đệm với dữ liệu bí mật.

4. Tận dụng .dockerignore

Tệp .dockerignore đóng một vai trò quan trọng trong việc tối ưu hóa các bản dựng Docker bằng cách cho phép bạn chỉ định các mẫu loại trừ các tệp và thư mục khỏi ngữ cảnh được gửi tới trình nền Docker. Cơ chế này có thể tăng tốc đáng kể quá trình xây dựng, giảm kích thước của bối cảnh xây dựng và tăng cường bảo mật bằng cách loại trừ các tệp nhạy cảm.

4.1. .dockerignore là gì?

Tương tự như .gitignore, .dockerignoretệp sẽ cho Docker biết những tệp và thư mục nào cần bỏ qua khi xây dựng image. Điều này đặc biệt quan trọng vì mọi tệp được gửi tới trình nền Docker như một phần của bối cảnh xây dựng có thể làm tăng thời gian và độ phức tạp của quá trình xây dựng một cách không cần thiết, đặc biệt là trong các dự án lớn.

4.2. Cách sử dụng .dockerignore

Tạo một tệp .dockerignore trong thư mục gốc của dự án, nơi chứa Dockerfile của bạn. Trong tệp này, chỉ định các mẫu cho tệp và thư mục để loại trừ khỏi bối cảnh xây dựng Docker.

Ví dụ về tập tin .dockerignore

.git
.gitignore
Dockerfile*
*.md
node_modules
temp/

Ví dụ này ngăn siêu dữ liệu Git, tệp Markdown, tất cả Dockerfiles, thư mục node_modules và bất kỳ thư mục temp nào được đưa vào bối cảnh xây dựng Docker.

4.3. Khi nào nên sử dụng .dockerignore

  • Kho lưu trữ lớn: Dành cho các dự án có lượng dữ liệu lớn, đặc biệt là những dự án không cần thiết cho quá trình xây dựng hoặc thời gian chạy.

  • Thông tin nhạy cảm: Để loại trừ các tệp chứa bí mật hoặc thông tin nhạy cảm không được đưa vào bối cảnh xây dựng Docker.

  • Thư mục phụ thuộc: Dành cho các ngôn ngữ tải các phần phụ thuộc xuống các thư mục cục bộ (như node_modules đối với Node.js), thư mục này không cần thiết trong ngữ cảnh xây dựng nếu bạn đang cài đặt các phần phụ thuộc trong quá trình xây dựng.

4.4. Cách sử dụng .dockerignore hiệu quả

  • Định kỳ xem xét tệp .dockerignore của bạn để đảm bảo tệp được cập nhật với cấu trúc và yêu cầu của dự án.

  • Sử dụng các mẫu rõ ràng để khớp chính xác các tệp và thư mục dự định, tránh các loại trừ không chủ ý.

  • Sử dụng .dockerignore để tăng cường bảo mật bằng cách đảm bảo rằng chỉ những tệp cần thiết mới được đưa vào ngữ cảnh bản dựng của bạn.

Lưu ý:

  • Việc không sử dụng hoặc định cấu hình tệp .dockerignore đúng cách có thể dẫn đến quá trình xây dựng chậm, kích thước image lớn hơn và có khả năng chứa thông tin nhạy cảm.

  • Mặc dù việc loại trừ các tệp không cần thiết là rất quan trọng nhưng hãy cẩn thận để không loại trừ các tệp cần thiết cho quá trình xây dựng, điều này có thể dẫn đến lỗi bản dựng hoặc hành vi image không chính xác.

5. Health Checks trong Dockerfiles

5.1. Health Checks là gì?

Đây là một tính năng của Docker cho phép bạn kiểm tra trạng thái của một container trong quá trình chạy. Bằng cách xác định một lệnh hoặc một URL kiểm tra trong Dockerfile hoặc thông qua tùy chọn dòng lệnh, bạn có thể định nghĩa cách Docker sẽ kiểm tra xem container của bạn có hoạt động đúng cách hay không. Điều này rất hữu ích trong việc tự động khởi động lại container khi nó không hoạt động như mong đợi, giúp cải thiện tính ổn định và sẵn sàng của hệ thống Docker.

5.2. Cách sử dụng Health Check

Để sử dụng tính năng Docker Health Check, bạn có thể thực hiện các bước sau:

  • Xác định Health Check trong Dockerfile: Bạn có thể định nghĩa một lệnh Health Check trong Dockerfile của bạn bằng cách sử dụng từ khóa HEALTHCHECK. Ví dụ, để kiểm tra “sức khỏe” bằng cách ping một máy chủ web, bạn có thể sử dụng:

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \

CMD curl -f http://localhost/ || exit 1

Trong ví dụ này, Docker sẽ thực hiện một lệnh kiểm tra mỗi 30 giây, với một thời gian chờ tối đa là 10 giây. Nếu container không trả về kết quả trong khoảng thời gian này, Docker sẽ gán trạng thái "không khỏe" cho container.

  • Sử dụng tùy chọn dòng lệnh: Ngoài việc định nghĩa “sức khỏe” trong Dockerfile, bạn cũng có thể cấu hình “sức khỏe” khi khởi động container bằng cách sử dụng tùy chọn dòng lệnh --health-*. Ví dụ:

docker run --health-cmd="curl -f http://localhost/" --health-interval=30s --health-timeout=10s --health-retries=3 my_container

Điều này cho phép bạn định cấu hình kiểm tra “sức khỏe” một cách linh hoạt ngay khi bạn khởi động một container.

  • Xem trạng thái “sức khỏe”: Bạn có thể xem trạng thái sức khỏe của các container bằng cách sử dụng lệnh docker ps. Trạng thái “sức khỏe” sẽ được hiển thị trong cột "HEALTH".

5.3. Khi nào nên sử dụng tính năng này?

  • Yêu cầu tính ổn định cao: Nếu ứng dụng của bạn cần hoạt động liên tục mà không gặp sự cố, việc sử dụng Docker Health Check giúp đảm bảo rằng container sẽ được tự động khởi động lại khi có sự cố xảy ra.

  • Cần theo dõi “sức khỏe” của container: Bạn muốn theo dõi sức khỏe của các container trong môi trường Docker của mình. Docker Health Check cung cấp thông tin về trạng thái sức khỏe của container, giúp bạn dễ dàng theo dõi và quản lý trạng thái hoạt động của chúng.

  • Kiểm tra sự kết nối với các dịch vụ khác: Khi container của bạn cần kết nối với các dịch vụ khác, chẳng hạn như cơ sở dữ liệu hoặc máy chủ web, Docker Health Check có thể được sử dụng để kiểm tra tính sẵn sàng của các kết nối này.

  • Tự động phục hồi sau sự cố: Bạn muốn tự động phục hồi container sau khi gặp sự cố, giảm thiểu thời gian chết và tăng tính sẵn sàng của ứng dụng.

5.4. Cách sử dụng hiệu quả

Để sử dụng tính năng Docker Health Check một cách hiệu quả, bạn hãy xem xét các bước sau:

  • Chọn phương pháp kiểm tra phù hợp: Xác định phương pháp kiểm tra sức khỏe phù hợp với ứng dụng của bạn. Đây có thể là một lệnh shell, một URL HTTP, hoặc một lệnh tùy chỉnh khác tùy thuộc vào yêu cầu của ứng dụng.

  • Định nghĩa thời gian và số lần lặp lại: Sử dụng các tùy chọn như --interval, --timeout, --start-period, và --retries để cấu hình cách Docker thực hiện kiểm tra sức khỏe. Đảm bảo rằng các tham số này được thiết lập sao cho phù hợp với yêu cầu của ứng dụng và môi trường triển khai.

  • Kiểm tra sức khỏe trong Dockerfile hoặc dòng lệnh: Định nghĩa lệnh kiểm tra sức khỏe trong Dockerfile của bạn bằng cách sử dụng từ khóa HEALTHCHECK, hoặc cung cấp các tùy chọn dòng lệnh --health-* khi chạy lệnh docker run.

  • Theo dõi và quản lý trạng thái sức khỏe: Sử dụng lệnh docker ps để xem trạng thái sức khỏe của các container và theo dõi các biểu đồ hoặc hệ thống giám sát để theo dõi tự động các thay đổi trong trạng thái sức khỏe.

  • Đảm bảo rằng bạn xử lý mọi vấn đề về sức khỏe một cách thích hợp. Cung cấp các biện pháp phục hồi và thông báo để đảm bảo rằng các container có thể được tự động khởi động lại hoặc can thiệp khi cần thiết.

Lưu ý:

  • Việc thực hiện Health Check có thể tăng tải cho hệ thống. Hãy đảm bảo rằng thời gian kiểm tra và tần suất không làm ảnh hưởng đến hiệu suất hoạt động của container và hệ thống.

  • Đối với mỗi Health Check, hãy xác định cách xử lý lỗi một cách chính xác. Đảm bảo rằng container được khởi động lại hoặc xử lý một cách thích hợp nếu gặp sự cố với Health Check.

  • Cấu hình thời gian chờ và số lần thử lại sao cho phù hợp với môi trường của bạn. Đảm bảo rằng các tham số này đủ linh hoạt để xử lý các vấn đề mạng và tài nguyên.

  • Chọn một phương pháp Health Check đáng tin cậy và phù hợp với yêu cầu của ứng dụng. Hãy kiểm tra kỹ lưỡng để đảm bảo rằng kiểm tra sức khỏe phản ánh trạng thái hoạt động thực sự của ứng dụng.

  • Tránh truyền thông tin nhạy cảm qua Heath Check, như mật khẩu hoặc thông tin xác thực. Sử dụng các phương tiện an toàn để truyền dữ liệu nhạy cảm khi cần thiết.

6. Docker CLI Output Formatting

6.1. Docker CLI Output Formatting là gì?

Docker CLI Output Formatting là tính năng cho phép bạn tùy chỉnh cách mà kết quả của các lệnh Docker được hiển thị trên màn hình terminal. Điều này giúp làm cho thông tin trở nên dễ đọc hơn và dễ hiểu hơn.

Tính năng này cho phép bạn sử dụng các tùy chọn như --format, --quiet, --no-trunc, và các tùy chọn khác để điều chỉnh cách mà kết quả được hiển thị. Ví dụ, bạn có thể sử dụng --format để chỉ hiển thị các trường cụ thể của mỗi container hoặc image, hoặc sử dụng --quiet để chỉ hiển thị ID của mỗi container hoặc image mà không có thông báo phụ.

6.2. Cách sử dụng

Để sử dụng tính năng Docker CLI Output Formatting, bạn có thể làm như sau:

  • Sử dụng tùy chọn --format để tùy chỉnh định dạng của kết quả hiển thị. Ví dụ, để hiển thị danh sách các container với chỉ tên và ID, bạn có thể sử dụng:

docker container ls --format "{{.Names}}: {{.ID}}"

Trong đó, {{.Names}} và {{.ID}} là các trường dữ liệu bạn muốn hiển thị.

  • Sử dụng tùy chọn --quiet để chỉ hiển thị các ID của các container hoặc image mà không có bất kỳ thông báo phụ nào. Ví dụ:

docker container ls --quiet

  • Sử dụng các tùy chọn khác: Docker CLI cung cấp nhiều tùy chọn khác như --no-trunc để ngăn việc cắt bớt dữ liệu, --filter để lọc kết quả, và nhiều tùy chọn khác để điều chỉnh cách hiển thị kết quả.

6.3. Khi nào nên sử dụng tính năng này?

Nên sử dụng tính năng Docker CLI Output Formatting khi bạn cần:

  • Tùy chỉnh định dạng hiển thị: Khi bạn muốn chỉ hiển thị các trường dữ liệu cụ thể hoặc tự định dạng output theo cách mà bạn mong muốn.

  • Tạo output dễ đọc hơn: Output mặc định của Docker không phải lúc nào cũng phản ánh đầy đủ thông tin mà bạn cần. Tùy chỉnh định dạng output có thể giúp bạn dễ đọc và dễ hiểu hơn.

  • Tích hợp với các công cụ khác: Khi bạn muốn sử dụng kết quả của các lệnh Docker trong các công cụ hoặc kịch bản tự động hóa, tùy chỉnh định dạng output có thể giúp bạn chuyển đổi dữ liệu Docker thành định dạng phù hợp với các yêu cầu của công cụ hoặc kịch bản đó.

  • Giảm thiểu thông tin không cần thiết: Khi bạn muốn loại bỏ thông tin không cần thiết khỏi kết quả của các lệnh Docker, chẳng hạn như loại bỏ các cột không cần thiết từ danh sách container hoặc image.

6.4. Cách sử dụng hiệu quả

  • Hiểu về Go Templating: Làm quen với cú pháp templating của Go để sử dụng hiệu quả các tùy chọn định dạng.

  • Sử dụng Table Formatting for Readability: Tận dụng định dạng bảng để hiển thị kết quả dễ đọc cho con người, đặc biệt khi chia sẻ thông tin với các thành viên trong nhóm.

  • Kết hợp với Các Công Cụ Unix Khác: Để xử lý mạnh mẽ hơn, kết hợp kết quả định dạng Docker với các công cụ Unix khác như grep, awk, hoặc jq cho định dạng JSON.

Lưu ý:

  • Tránh tạo ra các mẫu quá phức tạp khó đọc và bảo trì. Hãy giữ cho nó đơn giản nhất có thể cho công việc hiện tại.

  • Khi sử dụng các định dạng tùy chỉnh qua các kịch bản hoặc công cụ khác nhau, duy trì tính nhất quán để tránh sự nhầm lẫn.

7. Tối ưu hóa việc sử dụng bộ nhớ cache trong quá trình xây dựng (Optimizing Cache Use in Builds)

Tận dụng build cache của Docker một cách hiệu quả có thể làm tăng đáng kể tốc độ xây dựng image bằng cách sử dụng lại các lớp đã được lưu trữ trong cache thay vì xây dựng lại chúng. Điều này đặc biệt quan trọng đối với quá trình phát triển lặp đi lặp lại và các luồng công việc liên tục tích hợp/phát triển liên tục (CI/CD) nơi hiệu suất xây dựng có thể ảnh hưởng trực tiếp đến năng suất làm việc.

7.1 Làm thế nào để tối ưu hoá việc sử dụng cache

Để tối ưu hóa việc sử dụng cache trong quá trình xây dựng trong Docker, bạn có thể áp dụng các chiến lược sau:

  • Sắp xếp các lệnh xây dựng theo độ thay đổi ít nhất: Đặt các lệnh thường xuyên thay đổi (ví dụ: sao chép mã nguồn mới nhất) ở phía cuối Dockerfile để đảm bảo rằng cache được sử dụng nhiều nhất có thể.

  • Sử dụng layer caching hiệu quả: Sử dụng các layer caching tốt bằng cách chia nhỏ các lệnh xây dựng thành các layer độc lập nhau, đảm bảo rằng chỉ có các lệnh thực sự thay đổi được thực thi lại.

  • Sử dụng cẩn thận với COPY và ADD: Tránh sử dụng quá nhiều thư mục nguồn trong các lệnh COPY và ADD, vì điều này có thể dẫn đến việc làm thay đổi toàn bộ lớp cache.

  • Xóa cache khi cần thiết: Sử dụng tùy chọn --no-cache khi xây dựng để buộc Docker xây dựng lại tất cả các layer từ đầu mà không sử dụng cache.

7.2 Khi nào nên áp dụng phương pháp này?

  • Khi bạn thường xuyên thực hiện các thay đổi trong mã nguồn và cần xây dựng lại Docker image nhiều lần, việc sử dụng cache giúp tiết kiệm thời gian bằng cách tái sử dụng các lớp đã được xây dựng trước đó.

  • Trong quy trình CI/CD, việc xây dựng Docker image là một phần quan trọng của quy trình triển khai liên tục. Sử dụng cache giúp giảm thiểu thời gian xây dựng, giúp tăng tốc quá trình triển khai và giảm thời gian chờ đợi cho các bản triển khai mới.

  • Trong các dự án lớn với nhiều phụ thuộc hoặc các gói phần mềm lớn, việc xây dựng lại Docker image có thể mất rất nhiều thời gian. Sử dụng cache giúp giảm thiểu thời gian xây dựng và tối ưu hóa quá trình phát triển.

  • Trong môi trường sản xuất, việc tối ưu hóa thời gian xây dựng Docker image là quan trọng để đảm bảo sự ổn định và hiệu suất của hệ thống. Sử dụng cache giúp giảm thiểu thời gian nâng cấp và triển khai, cũng như giảm thiểu thời gian chết của ứng dụng trong quá trình cập nhật.

Lưu ý:

  • Đảm bảo chỉ tận dụng cache cho các bước xây dựng mà không làm thay đổi các lớp tiếp theo. Tránh thay đổi các hướng dẫn trong Dockerfile mà ảnh hưởng đến tất cả các bước sau, điều này có thể dẫn đến vô hiệu hóa cache quá sớm và làm tăng thời gian xây dựng.

  • Đối với các dự án lớn, hãy chắc chắn rằng ngữ cảnh xây dựng không quá lớn, vì điều này có thể làm chậm quá trình gửi ngữ cảnh đến máy chủ Docker, ngay cả khi cache được sử dụng hiệu quả.

  • Sử dụng .dockerignore một cách hiệu quả: Đảm bảo rằng bạn đã cấu hình file .dockerignore để loại bỏ các tệp và thư mục không cần thiết khỏi ngữ cảnh xây dựng. Điều này giúp giảm kích thước của ngữ cảnh và tối ưu hóa việc sử dụng cache.

  • Theo dõi hiệu suất của quá trình xây dựng và kiểm tra xem việc tận dụng cache có mang lại lợi ích như mong đợi không. Điều này giúp bạn điều chỉnh và tinh chỉnh quy trình xây dựng để đạt được hiệu suất tối ưu nhất.

  • Kiểm tra cache định kỳ: Thực hiện kiểm tra định kỳ trên cache để đảm bảo rằng cache đang hoạt động đúng cách và không gây ra vấn đề không mong muốn trong quá trình xây dựng.

8. Giới hạn tài nguyên container

Quản lý tài nguyên của container một cách hiệu quả là rất quan trọng trong một môi trường chia sẻ (shared environment), nơi nhiều container chạy trên cùng một máy chủ. Docker cung cấp các cơ chế để giới hạn tài nguyên CPU và bộ nhớ mà một container có thể sử dụng, đảm bảo rằng không có container nào có thể chiếm hết tài nguyên hệ thống, điều này có thể dẫn đến hiệu suất giảm sút hoặc không ổn định của hệ thống.

Trong ngữ cảnh của Docker hoặc các hệ thống máy chủ ảo, môi trường chia sẻ thường ám chỉ việc sử dụng một máy chủ vật lý hoặc một hạ tầng máy chủ ảo để chạy nhiều container hoặc máy ảo Docker khác nhau. Điều này đặc biệt phổ biến trong các mô hình triển khai dựa trên điện toán đám mây hoặc các môi trường DevOps, nơi việc chia sẻ tài nguyên giúp tối ưu hóa sử dụng và quản lý tài nguyên. Tuy nhiên, việc quản lý tài nguyên một cách hiệu quả là rất quan trọng để đảm bảo rằng mỗi ứng dụng hoặc dịch vụ có đủ tài nguyên để hoạt động một cách ổn định và không ảnh hưởng đến các ứng dụng khác.

8.1. Làm sao để giới hạn tài nguyên

Để giới hạn tài nguyên cho một container, bạn có thể sử dụng các tùy chọn --cpus, --memory (hoặc -m), --memory-swap, và các flag khác khi chạy một container với lệnh docker run.

Ví dụ: Giới hạn CPU và bộ nhớ

docker run -it --cpus="1.5" --memory="500m" myapp:latest

Lệnh này giới hạn container chỉ có thể sử dụng tối đa 1,5 CPU và 500MB bộ nhớ. Những giới hạn này đảm bảo rằng container không thể tiêu tốn quá nhiều tài nguyên, gây ảnh hưởng đến các container khác hoặc hệ thống máy chủ.

8.2. Khi nào nên giới hạn tài nguyên

  • Môi trường đa container: Cần thiết trong môi trường sản xuất nơi nhiều container chạy trên cùng một máy chủ để đảm bảo phân bổ tài nguyên công bằng.

  • Kiểm thử hiệu suất: Để mô phỏng các kịch bản khác nhau có sẵn tài nguyên và kiểm tra hiệu suất của ứng dụng dưới các ràng buộc khác nhau.

  • Ứng dụng tiêu tốn tài nguyên: Đối với các ứng dụng được biết đến sử dụng nhiều tài nguyên CPU hoặc bộ nhớ, nhằm ngăn chúng ảnh hưởng đến hệ thống máy chủ hoặc các ứng dụng khác.

Lưu ý:

  • Thiết lập giới hạn tài nguyên quá cao có thể làm mất đi lợi ích của việc cách ly (isolation) các ứng dụng và môi trường làm việc của chúng, có thể cho phép các container tiếp tục tiêu tốn tài nguyên một cách không cần thiết.

  • Hãy cẩn trọng khi thiết lập cài đặt Swap (--memory-swap), vì cho phép quá nhiều swap có thể dẫn đến tình trạng tráo đổi dữ liệu trên ổ đĩa và hiệu suất giảm sút.

  • Không theo dõi việc sử dụng tài nguyên thực tế có thể dẫn đến cài đặt không được tối ưu, hoặc là hạn chế quá nhiều hoặc là hạn chế quá ít tài nguyên cho container.

9. Docker Events for Monitoring (Sự kiện Docker để theo dõi)

Các sự kiện Docker cung cấp thông tin theo thời gian thực từ Docker daemon, cung cấp cái nhìn sâu sắc vào các hoạt động diễn ra trong môi trường Docker. Tính năng này có thể đóng vai trò quan trọng trong việc giám sát, gỡ lỗi và tự động hóa các phản ứng đối với các sự kiện vòng đời Docker khác nhau.

9.1. Các sự kiện Docker nghĩa là gì?

Đây là một luồng dữ liệu cấu trúc đại diện cho các thay đổi trạng thái hoặc hoạt động của các đối tượng Docker, như các container, image, thư mục và mạng lưới. Mỗi sự kiện bao gồm thông tin về loại đối tượng, hành động được thực hiện và thời gian của hành động. Giám sát các sự kiện này có thể giúp bạn hiểu được cách các đối tượng Docker được điều chỉnh theo thời gian và có thể kích hoạt các quy trình làm việc tự động dựa trên các hoạt động cụ thể.

9.2. Làm thế nào để sử dụng tính năng này?

Để giám sát các sự kiện Docker, sử dụng lệnh docker events. Bạn có thể lọc luồng các sự kiện theo loại đối tượng, loại sự kiện hoặc các thuộc tính cụ thể.

Ví dụ: Giám sát Sự kiện Container

docker events --filter 'type=container'

Lệnh này lọc luồng sự kiện để chỉ hiển thị các sự kiện liên quan đến vùng chứa, chẳng hạn như tạo, bắt đầu, dừng và hủy.

Ví dụ về lọc nâng cao

Bạn có thể kết hợp nhiều bộ lọc để thu hẹp các sự kiện mà bạn quan tâm. Ví dụ: để theo dõi các sự kiện bắt đầu và dừng cho các container được gắn thẻ là prod:

docker events --filter 'type=container' --filter 'event=start' --filter 'event=stop' --filter 'label=environment=prod

9.3. Khi nào nên giám sát sự kiện Docker

  • Để theo dõi những gì đang xảy ra trong môi trường Docker của bạn trong thời gian thực.

  • Để chẩn đoán các vấn đề bằng cách hiểu chuỗi sự kiện dẫn đến lỗi hoặc trạng thái không mong muốn.

  • Để kích hoạt các tập lệnh hoặc thông báo dựa trên các sự kiện Docker cụ thể, nâng cao quy trình CI/CD hoặc quy trình vận hành của bạn.

9.4. Làm sao để sử dụng hiệu quả tính năng này?

  • Sử dụng bộ lọc để tập trung vào các sự kiện phù hợp nhất với nhu cầu của bạn, giảm tiếng ồn và giúp xác định các hoạt động quan trọng dễ dàng hơn.

  • Xem xét ghi nhật ký sự kiện vào một tệp hoặc hệ thống ghi nhật ký trung tâm cho mục đích kiểm tra và phân tích lịch sử.

  • Tích hợp với các công cụ giám sát: Tận dụng API của Docker hoặc các công cụ của bên thứ ba có thể sử dụng các sự kiện Docker để tích hợp với chiến lược giám sát tổng thể của bạn.

Lưu ý:

  • Tác động đến hiệu suất: Theo dõi liên tục tất cả các sự kiện mà không lọc có thể dẫn đến tải nặng về hiệu suất. Hãy chọn lọc cẩn thận những gì bạn giám sát.

  • Quá phụ thuộc vào giao diện dòng lệnh: Đối với giám sát dài hạn hoặc các môi trường phức tạp, chỉ phụ thuộc vào CLI để giám sát sự kiện có thể không mở rộng được. Khám phá việc sử dụng API của Docker hoặc các công cụ giám sát chuyên biệt.

10. Chạy container ở chế độ Read-only

10.1. Chế độ Read-only cho container là gì?

Chế độ này có nghĩa là hệ thống tệp của container và các ứng dụng bên trong chỉ có thể đọc các tệp nhưng không thể ghi hoặc sửa đổi hệ thống tệp. Điều này giúp đảm bảo rằng ứng dụng chạy với đặc quyền tối thiểu cần thiết, nâng cao tính bảo mật.

Chạy các container Docker trong chế độ Read-only là một quy tắc an ninh tốt nhất giúp giảm đáng kể nguy cơ của các sửa đổi không được ủy quyền đối với hệ thống tệp của container. Cách tiếp cận này có thể giúp ngăn chặn các nỗ lực độc hại để thay đổi ứng dụng đang chạy hoặc môi trường của nó, làm cho các container của bạn trở nên bền vững hơn đối với các cuộc tấn công.

10.2. Cách sử dụng chế độ Read-only

Để chạy một container trong chế độ Read-only, hãy sử dụng flag  --read-only khi khởi động container của bạn với lệnh docker run. Dưới đây là một ví dụ đơn giản: docker run --read-only -d myimage:latest

10.3. Khi nào nên sử dụng chế độ này?

  • Các ứng dụng nhạy cảm với bảo mật: Đặc biệt là đối với các ứng dụng xử lý dữ liệu nhạy cảm hoặc tiếp xúc với internet.

  • Các ứng dụng không lưu trạng thái: Các ứng dụng được thiết kế để không lưu trạng thái là ứng viên lý tưởng cho chế độ chỉ đọc vì chúng không cần lưu trữ dữ liệu.

  • Thực thi tính bất biến: Khi bạn muốn thực thi một mô hình cơ sở hạ tầng bất biến, hãy đảm bảo rằng các container không thay đổi từ trạng thái ban đầu của chúng.

10.4. Cách sử dụng chế Read-only hiệu quả

  • Xác định yêu cầu có thể ghi: Đánh giá ứng dụng của bạn để xác định các thư mục cần thiết cần truy cập ghi dữ liệu. Sử dụng các khối lưu trữ hoặc gắn kết tmpfs để cung cấp các đường dẫn có thể ghi khi cần thiết.

  • Kiểm thử toàn diện: Kiểm thử kỹ lưỡng ứng dụng của bạn trong chế độ chỉ đọc để xác định bất kỳ vấn đề hoặc điều chỉnh cần thiết trước khi triển khai vào môi trường sản xuất.

  • Kết hợp với các phương thức bảo mật khác: Sử dụng chế độ chỉ đọc như một phần của chiến lược bảo mật toàn diện, bao gồm sử dụng người dùng không phải root, image cơ bản tối giản và loại bỏ các khả năng không sử dụng.

Lưu ý:

  • Các ứng dụng không được thiết kế cho môi trường Read-only có thể gặp sự cố không mong muốn. Cần phải chuẩn bị và kiểm thử cẩn thận.

  • Hãy đảm bảo sắp xếp các biện pháp thay thế cho các cơ chế ghi nhật ký và bộ đệm cần truy cập ghi.

Lời kết

Trong bài viết này, chúng ta đã khám phá 10 thủ thuật Docker nâng. Từ việc sử dụng multi-stage builds để tối ưu hóa image đến việc sử dụng chế độ Read-only để cải thiện tính bảo mật, mỗi thủ thuật đều mang lại lợi ích và ứng dụng riêng biệt trong việc quản lý và triển khai các ứng dụng Docker.

Qua việc áp dụng các thủ thuật này, bạn có thể tăng cường hiệu suất, tính bảo mật và quản lý của môi trường Docker của mình. Nếu thấy bài viết hữu ích, đừng quên chia sẻ cho bạn bè cùng biết nhé.

VietnamWorks inTECH