Hiện nay việc sử dụng docker để đóng gói ứng dụng rất phổ biến. Tuy nhiên việc nên đóng docker container thế nào cho ứng dụng gọn, nhẹ lại security chưa nhiều người thực sự để ý. Nay có thời gian tôi tổng hợp lại một số “best practice” dựa trên kinh nghiệm cá nhân hy vọng là có ích với gì đó với mọi người.

Docker best pr

1. Đóng 1 ứng dụng trong 1 container

Đã gặp tình huống có người đóng rất nhiều service trong 1 container. Việc làm này không thể khẳng định đúng hay sai và tùy theo quan điểm mỗi người. Tuy nhiên theo khuyến cáo của docker.org và theo quan điểm cá nhân theo tôi nên tách ra mỗi container chỉ nên chạy 1 service. Để giải thích cho lựa chọn của mình, tôi xin phép đưa ra 1 số lý do cơ bản sau.

  • Giúp tiện lợi cho việc debug: Có ít app (1 app) trong container việc debug sẽ dễ hơn rất nhiều vì không cần care việc interactive giữa các app bên trong, console log container lúc này đẩy ra chỉ là của 1 app mà thôi, rất dễ để theo dõi.
  • Tiện lợi cho việc xử lý tín hiệu, monitor: Đã gặp T/H 2 service chạy trong 1 container. 1 serivce chết ngỏm nhưng container vẫn chạy vì nó vẫn còn service kia. Chui vào trong bug mãi mới biết vì ông dev nghỉ việc đã lâu. Quay lại vấn đề nếu đóng chỉ 1 app trong 1 container app crash làm process die thì container sẽ exit ngay và ta dễ dàng nhận ra vấn đề. Không những vậy việc tách nhiều container còn tiện lợi trong việc monitor vì nếu 2 app trong cùng container sẽ rất khó để biết app nào dùng tài nguyên ít/nhiều bao nhiêu.
  • Giúp cho việc scale ứng dụng hiệu quả hơn: Giờ có 2 service cùng đóng vào 1 container như vậy khi scale phải scale cả 2. Giả sử trong tình huống ta chỉ cần scale 1 trong 2 service thì không thể thực hiện được. Vậy là xảy ra t/h bên thì thiếu, bên thì thừa :)

2. Tối ưu hóa tối đa việc sắp xếp các lệnh trong Dockerfile để tận dụng build cache

Build cache là cơ chế khi ta thực hiện build image từ một Dockerfile thì từng lệnh trong Dockerfile sẽ được thi hành theo thứ tự xuất hiện trong Dockerfile. Trong quá trình build Docker sẽ tìm kiếm những image đã tồn tại để tái sử dụng hơn là build mới phần đã có mà bị trùng.

Đại ý của best practice thứ 2 này là khi viết Dockerfile cần tách các phần ít thay đổi lên đầu, hay thay đổi về cuối Dockerfile để tận dụng build cache. Ví dụ: Nên đặt lệnh copy code vào image muộn nhất có thể để tối ưu hóa việc build. Cụ thể

FROM python:3.5
COPY my_code/ /src
RUN pip install my_requirements

Nên thay bằng

FROM python:3.5
RUN pip install my_requirements
COPY my_code/ /src

Dễ thấy ở phiên bản thay thế mỗi lần build phàn từ lệnh pip trở lên ít thay đổi sẽ được cache lại nên sẽ được đặt lên đầu, phần code hay thay đổi sẽ đặt xuống cuỗi, mỗi lần build image do được cache nên sẽ được tăng tốc hơn rất nhiều.

3. Giảm tối đa kích thước của Docker image

Ý này quá dễ hiểu. Đơn giản để giảm tối đa kích thước của Docker image. Image càng nhỏ không những tiết kiệm không gian lưu trữ mà còn tạo điệu kiệu tăng tốc việc deploy => Giảm thời gian release sản phẩm. Các best practice cụ thể là:

  • Loại bỏ các tool không cần thiết, xóa bỏ thư mục cache apt
  • Lựa chọn câc base image từ docker hub gần với nhu cầu nhất có thể. Ví dụ khi bạn cần 1 môi trường chạy app java tomcat. Thay bằng lựa chọn base image là ubuntu sau đó bạn tự cài tomcat thì bạn nên chọn base image là tomcat. Việc này giúp ta tối ưu thời gian build ngoài ra khi Docker xây dựng các base image họ đa tính toàn tối ưu nhất rùi. Rất khó để bạn có thể làm tốt hơn. OK fine. Cái nào tốt hơn ta nên xài chứ.

4. Giảm số lượng layer build

Mỗi lần chạy 1 command RUN/COPY/ADD trong Dockerfile sẽ sinh ra 1 layer của image. Càng nhiều layer trong image khi chạy sẽ càng làm giảm hiệu năng của container. Điều đó đồng nghĩa là ta phải giảm tối đa số lần chạy các chỉ lệnh này trong Dockerfile. Ví dụ: Thay bằng chạy

RUN apt-get install gcc 
RUN apt-get install g++

Sẽ tạo ra 2 layer, ta chỉ cần chạy

RUN apt-get install gcc g++

Để chỉ sinh ra một layer mà thôi.

5. Tính toán việc tag image có chủ đích

Nhiều người có thói quen tag tùy hứng. Việc này làm rất khó quản lý phiên bản của các image, lúc cần rà soát lại khó vô cùng chả biết phiên bản nào với phiên bản nào có tính năng gì. Do vậy tốt nhất nên tạo thói quen tối đánh phiên bản image theo 1 chuẩn nhất định (Software versioning). Quản lý Dockerfile bằng Git mỗi lần thay đổi lại push lên repo có comment rõ ràng.

6. Luôn cấu hình log rotation cho container,

Cái này chắc người nào xài docker cũng biết rồi, khỏi nói nhiều. Container chạy t/g log console tồn lại nhiều, làm server full ổ cứng luôn và đương nhiên dịch vụ ngỏm luôn. Do vậy mọi người nên chú ý vấn đề này.

7. Xem xét việc cấu hình limit resource (MEM, CPU) cho container

Ngày xưa đi làm có xảy ra trường hợp chạy nhiều service trong 1 cụm swam. Một ngày đẹp trời 1 service có bug chiếm hết RAM làm toàn bộ cụm ngỏm. Do đó lời khuyên là không quên monitor tài nguyên của container nói riêng và của toàn hệ thống nói chung để phát hiện và xử lý vấn đề kịp thời. Ngoài ra cũng có thể xem xét việc limit tài nguyên cho container nha đề phòng trường hợp đêm ngủ nhận được cảnh báo tài nguyên cũng như không :) và hệ thống chết cả loạt. Hôm sau vào phòng sếp giải trình nguyên nhân nha. Tha hồ vui.

8. Cân nhắc khi sử dụng public image

Việc sử dụng các public image cần phải thận trọng (tốt nhất chỉ nên sử dụng từ docker, từ chính nhà sảm xuất hoặc xuất sứ rõ ràng tin cậy) để đảm bảo security cho hệ thống của bạn. XH nhiều người xấu người tốt, ai biết được họ nhét cái gì vào đó, có thể cả đống backdoor thì sao.

Với tôi khi dùng image nào thì tôi hay ngó qua Dockerfile xem nó làm những gì :) kiểu đọc kỹ hướng dẫn trước khi sử dụng đó tránh bị tác dụng phụ.

Trên là vài ghi chú nhỏ hy vọng có gì đó hay ho cho mọi người,

Tham khảo,

Google developer

docker.org