Một hình ảnh docker thực sự là một danh sách liên kết của các lớp hệ thống tập tin. Mỗi lệnh trong Dockerfile tạo một lớp hệ thống tệp mô tả sự khác biệt trong hệ thống tệp trước và sau khi thực hiện lệnh tương ứng. Tiểu ban docker inspect
có thể được sử dụng trên một hình ảnh docker để tiết lộ bản chất của nó là một danh sách các lớp hệ thống tập tin được liên kết.
Số lượng các lớp được sử dụng trong một hình ảnh là quan trọng
- khi đẩy hoặc kéo hình ảnh, vì nó ảnh hưởng đến số lần tải lên hoặc tải xuống đồng thời xảy ra.
- khi bắt đầu một container, vì các lớp được kết hợp với nhau để tạo ra hệ thống tập tin được sử dụng trong container; Càng nhiều lớp tham gia, hiệu suất càng tệ, nhưng các phụ trợ hệ thống tập tin khác nhau bị ảnh hưởng khác nhau bởi điều này.
Điều này có một số hậu quả cho cách hình ảnh nên được xây dựng. Lời khuyên đầu tiên và quan trọng nhất tôi có thể đưa ra là:
Lời khuyên # 1 Hãy chắc chắn rằng các bước xây dựng có liên quan đến mã nguồn của bạn đến càng muộn càng tốt trong Dockerfile và không bị ràng buộc với các lệnh trước đó bằng cách sử dụng a &&
hoặc a ;
.
Lý do cho điều này, là tất cả các bước trước đó sẽ được lưu vào bộ nhớ cache và các lớp tương ứng sẽ không cần phải tải xuống nhiều lần. Điều này có nghĩa là bản dựng nhanh hơn và bản phát hành nhanh hơn, đó có thể là những gì bạn muốn. Thật thú vị, thật khó để sử dụng tối ưu bộ đệm của docker.
Lời khuyên thứ hai của tôi ít quan trọng hơn nhưng tôi thấy nó rất hữu ích từ quan điểm bảo trì:
Lời khuyên # 2 Không viết các lệnh phức tạp trong Dockerfile mà nên sử dụng các tập lệnh sẽ được sao chép và thực thi.
Một Dockerfile sau lời khuyên này sẽ như thế nào
COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh
vân vân Lời khuyên về ràng buộc một số lệnh &&
chỉ có phạm vi giới hạn. Nó dễ dàng hơn nhiều để viết với các tập lệnh, nơi bạn có thể sử dụng các chức năng, vv để tránh dư thừa hoặc cho các mục đích tài liệu.
Những người quan tâm đến các bộ xử lý trước và sẵn sàng tránh các chi phí nhỏ do các COPY
bước gây ra và thực sự tạo ra một Dockerfile khi đang di chuyển trong đó
COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
trình tự được thay thế bởi
RUN base64 --decode … | sh -x
trong đó …
phiên bản được mã hóa base64 của apt_setup.sh
.
Lời khuyên thứ ba của tôi là dành cho những người muốn giới hạn kích thước và số lượng lớp với chi phí có thể của các bản dựng dài hơn.
Lời khuyên # 3 Sử dụng with
-idiom để tránh các tệp có trong các lớp trung gian nhưng không có trong hệ thống tệp kết quả.
Một tệp được thêm bởi một số lệnh docker và bị xóa bởi một số lệnh sau này không có trong hệ thống tệp kết quả nhưng nó được đề cập hai lần trong các lớp docker tạo thành hình ảnh docker trong xây dựng. Một lần, với tên và nội dung đầy đủ trong lớp kết quả từ hướng dẫn thêm nó và một lần như là một thông báo xóa trong lớp do lệnh xóa nó.
Ví dụ, giả sử chúng ta tạm thời cần một trình biên dịch C và một số hình ảnh và xem xét
# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc
(Một ví dụ thực tế hơn sẽ xây dựng một số phần mềm với trình biên dịch thay vì chỉ xác nhận sự hiện diện của nó với --version
cờ.)
Đoạn mã Dockerfile tạo ba lớp, lớp đầu tiên chứa bộ gcc đầy đủ để ngay cả khi nó không có trong hệ thống tệp cuối cùng, dữ liệu tương ứng vẫn là một phần của hình ảnh theo cách tương tự và cần được tải xuống, tải lên và giải nén bất cứ khi nào hình ảnh cuối cùng là.
Các with
-idiom là một hình thức phổ biến trong lập trình chức năng để sở hữu nguồn tài nguyên chủng và phát hành từ logic sử dụng nó. Thật dễ dàng để chuyển thành ngữ này sang shell-scripting và chúng ta có thể viết lại các lệnh trước đó thành đoạn script sau, được sử dụng COPY & RUN
như trong Lời khuyên # 2.
# with_c_compiler SIMPLE-COMMAND
# Execute SIMPLE-COMMAND in a sub-shell with gcc being available.
with_c_compiler()
(
set -e
apt-get install -y gcc
"$@"
trap 'apt-get --purge autoremove -y gcc' EXIT
)
with_c_compiler\
gcc --version
Các lệnh phức tạp có thể được chuyển thành hàm để chúng có thể được đưa vào with_c_compiler
. Cũng có thể xâu chuỗi các cuộc gọi của một số with_whatever
chức năng, nhưng có thể không được mong muốn lắm. (Sử dụng nhiều tính năng bí truyền hơn của trình bao, chắc chắn có thể thực hiện các with_c_compiler
lệnh phức tạp chấp nhận, nhưng về mọi mặt, nên bọc các lệnh phức tạp này thành các hàm.
Nếu chúng tôi muốn bỏ qua Lời khuyên số 2, đoạn mã Dockerfile kết quả sẽ là
RUN apt-get install -y gcc\
&& gcc --version\
&& apt-get --purge autoremove -y gcc
Điều này không dễ đọc và dễ duy trì vì sự xáo trộn. Xem cách biến thể shell-script nhấn mạnh vào phần quan trọng gcc --version
trong khi &&
biến thể xích bị chôn vùi phần đó ở giữa tiếng ồn.