Hiểu các lớp Docker


27

Chúng tôi có khối sau đây trong Dockerfile:

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y groupinstall "Development Tools"
RUN yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Tôi đã được thông báo rằng chúng ta nên hợp nhất các RUNlệnh này để cắt giảm các lớp docker đã tạo:

RUN yum -y update \
    && yum -y install epel-release \
    && yum -y groupinstall "Development Tools" \
    && yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Tôi rất mới với docker và không chắc chắn tôi hoàn toàn hiểu sự khác biệt giữa hai phiên bản này trong việc chỉ định nhiều lệnh RUN. Khi nào một người sẽ hợp nhất RUNcác lệnh thành một lệnh duy nhất và khi nào có ý nghĩa để có nhiều RUNlệnh?


Câu trả lời:


35

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 inspectcó 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 COPYbướ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 --versioncờ.)

Đ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 & RUNnhư 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_whateverchứ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_compilerlệ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 --versiontrong khi &&biến thể xích bị chôn vùi phần đó ở giữa tiếng ồn.


1
Bạn có thể bao gồm kết quả của kích thước hộp sau khi xây dựng bằng cách sử dụng tập lệnh và sử dụng nhiều lệnh trong một câu lệnh RUN không?
030

1
Nó có vẻ là một ý tưởng tồi đối với tôi để trộn cấu hình của cơ sở hình ảnh (tức là các công cụ hệ điều hành) và thậm chí cả lib với việc thiết lập nguồn mà bạn đã viết. Bạn nói "Hãy chắc chắn rằng các bước xây dựng nơi mã nguồn của bạn được tham gia đến càng muộn càng tốt". Có bất kỳ vấn đề trong việc làm cho phần đó là một tạo tác hoàn toàn độc lập?
JimmyJames

1
@ 030 Bạn có ý nghĩa gì với kích thước hộp của Bỉ? Tôi không biết bạn đang đề cập đến hộp nào.
Michael Le Barbier Grünewald

1
Tôi có nghĩa là kích thước hình ảnh docker
030

1
@JimmyJames Nó phụ thuộc rất nhiều vào kịch bản triển khai của bạn. Nếu chúng ta giả sử một chương trình được biên dịch, thì điều đúng đắn để thực hiện là sẽ đóng gói nó và cài đặt các phụ thuộc gói đó và chính gói đó là hai bước gần như cuối cùng. Điều này để tối đa hóa tính hữu ích của bộ đệm docker và để tránh tải xuống nhiều lớp với cùng một tệp. Tôi thấy việc chia sẻ các công thức xây dựng để xây dựng hình ảnh docker dễ dàng hơn so với việc xây dựng chuỗi hình ảnh phụ thuộc dài, bởi vì điều này làm cho việc xây dựng lại khó khăn hơn.
Michael Le Barbier Grünewald

13

Mỗi hướng dẫn bạn tạo trong Dockerfile của bạn sẽ tạo ra một lớp hình ảnh mới. Mỗi lớp mang lại dữ liệu bổ sung không phải luôn là một phần của hình ảnh kết quả. Ví dụ: nếu bạn thêm một tệp trong một lớp, nhưng sau đó loại bỏ nó trong một lớp khác, kích thước của hình ảnh cuối cùng sẽ bao gồm kích thước tệp được thêm vào dưới dạng một tệp "trắng" đặc biệt mặc dù bạn đã xóa nó.

Giả sử bạn có Dockerfile sau:

FROM centos:6

RUN yum -y update 
RUN yum -y install epel-release

Kích thước hình ảnh kết quả sẽ là

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

Ngược lại, với Dockerfile "tương tự":

FROM centos:6

RUN yum -y update  && yum -y install epel-release

Kích thước hình ảnh kết quả sẽ là

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

Bạn sẽ nhận được kích thước thậm chí nhỏ hơn, nếu bạn xóa bộ đệm yum trong một câu lệnh RUN.

Vì vậy, bạn muốn giữ cân bằng giữa khả năng đọc / dễ bảo trì và số lớp / kích thước hình ảnh.


4

Các RUNbáo cáo đại diện cho mỗi một lớp. Hãy tưởng tượng rằng một người tải xuống một gói, cài đặt nó và muốn gỡ bỏ nó. Nếu một người sử dụng ba RUNcâu lệnh thì kích thước hình ảnh sẽ không co lại vì có các lớp riêng biệt. Nếu một lệnh chạy tất cả các lệnh bằng một RUNcâu lệnh, kích thước ảnh đĩa có thể giảm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.