ทำความเข้าใจกับเลเยอร์นักเทียบท่า


27

เรามีบล็อกต่อไปนี้ในของเรา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

ฉันได้รับแจ้งว่าเราควรรวมRUNคำสั่งเหล่านี้เพื่อลดเลเยอร์นักเทียบท่าที่สร้างขึ้น:

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

ฉันใหม่มากสำหรับนักเทียบท่าและไม่แน่ใจว่าฉันเข้าใจความแตกต่างอย่างสมบูรณ์ระหว่างทั้งสองเวอร์ชันของการระบุคำสั่ง RUN หลายคำสั่ง เมื่อไรที่RUNคำสั่งหนึ่งจะรวมกันเป็นคำสั่งเดียวและเมื่อใดที่ควรมีหลายRUNคำสั่ง


คำตอบ:


35

อิมเมจนักเทียบท่านั้นเป็นรายการที่เชื่อมโยงกันของเลเยอร์ระบบไฟล์ แต่ละคำสั่งในDockerfileสร้างเลเยอร์ระบบไฟล์ที่อธิบายความแตกต่างในระบบไฟล์ก่อนและหลังการใช้งานคำสั่งที่เกี่ยวข้อง คำdocker inspectสั่งย่อยสามารถใช้กับอิมเมจ docker เพื่อแสดงลักษณะของการเป็นรายการลิงก์ของเลเยอร์ระบบไฟล์

จำนวนเลเยอร์ที่ใช้ในภาพเป็นสิ่งสำคัญ

  • เมื่อผลักหรือดึงภาพเนื่องจากจะมีผลต่อจำนวนการอัพโหลดหรือดาวน์โหลดที่เกิดขึ้นพร้อมกัน
  • เมื่อเริ่มต้นคอนเทนเนอร์เนื่องจากเลเยอร์ถูกรวมเข้าด้วยกันเพื่อสร้างระบบไฟล์ที่ใช้ในคอนเทนเนอร์ ยิ่งเลเยอร์มีส่วนเกี่ยวข้องมากเท่าไรประสิทธิภาพก็ยิ่งแย่ลง แต่แบ็กเอนด์ของระบบไฟล์ที่แตกต่างกันจะได้รับผลกระทบจากสิ่งนี้

สิ่งนี้มีผลตามมาหลายประการสำหรับวิธีการสร้างภาพ คำแนะนำแรกและสำคัญที่สุดที่ฉันสามารถให้ได้คือ:

แนะนำ # 1ตรวจสอบให้แน่ใจว่าการสร้างขั้นตอนที่รหัสต้นฉบับของคุณมีส่วนเกี่ยวข้องมาเป็นปลายเป็นไปได้ในDockerfileและไม่ได้ผูกติดอยู่กับคำสั่งก่อนหน้านี้ใช้หรือ&&;

เหตุผลสำหรับสิ่งนี้คือขั้นตอนก่อนหน้านี้ทั้งหมดจะถูกแคชและเลเยอร์ที่เกี่ยวข้องจะไม่จำเป็นต้องดาวน์โหลดซ้ำแล้วซ้ำอีก นี่หมายถึงการสร้างที่เร็วขึ้นและการปล่อยที่เร็วกว่าซึ่งอาจเป็นสิ่งที่คุณต้องการ ที่น่าสนใจก็เป็นเรื่องยากที่จะใช้แคชนักเทียบท่าอย่างเหมาะสม

คำแนะนำที่สองของฉันมีความสำคัญน้อยกว่า แต่ฉันคิดว่ามันมีประโยชน์มากจากมุมมองการบำรุงรักษา:

คำแนะนำ # 2อย่าเขียนคำสั่งที่ซับซ้อนในDockerfileแต่ควรใช้สคริปต์ที่จะคัดลอกและดำเนินการ

Dockerfileทำตามคำแนะนำนี้จะมีลักษณะ

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh

และอื่น ๆ คำแนะนำในการรวมหลายคำสั่งด้วย&&ขอบเขตที่ จำกัด เท่านั้น การเขียนสคริปต์ง่ายขึ้นซึ่งคุณสามารถใช้ฟังก์ชั่น ฯลฯ เพื่อหลีกเลี่ยงความซ้ำซ้อนหรือเพื่อวัตถุประสงค์ด้านเอกสาร

ผู้ที่สนใจโดยก่อนการประมวลผลและเต็มใจที่จะหลีกเลี่ยงค่าใช้จ่ายเล็ก ๆ ที่เกิดจากCOPYขั้นตอนและเป็นจริงสร้าง on-the-บินDockerfileที่

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh

ลำดับจะถูกแทนที่ด้วย

RUN base64 --decode … | sh -x

ที่เป็นรุ่นที่เข้ารหัส base64 apt_setup.shของ

คำแนะนำที่สามของฉันสำหรับผู้ที่ต้องการ จำกัด ขนาดและจำนวนเลเยอร์ในราคาที่เป็นไปได้ในการสร้างที่นานขึ้น

คำแนะนำ # 3ใช้with-idiom เพื่อหลีกเลี่ยงไฟล์ที่มีอยู่ในตัวกลางชั้น แต่ไม่ได้อยู่ในระบบไฟล์ที่เกิดขึ้น

ไฟล์ที่เพิ่มโดยคำสั่งนักเทียบท่าบางส่วนและถูกลบออกโดยคำสั่งบางส่วนในภายหลังจะไม่ปรากฏในระบบไฟล์ผลลัพธ์ แต่จะมีการกล่าวถึงสองครั้งในชั้นนักเทียบท่าซึ่งประกอบเป็นภาพนักเทียบท่าในการก่อสร้าง ครั้งหนึ่งด้วยชื่อและเนื้อหาแบบเต็มในเลเยอร์ที่เป็นผลมาจากคำสั่งเพิ่มและอีกครั้งหนึ่งเป็นการแจ้งเตือนการลบในเลเยอร์ที่เกิดจากคำสั่งลบมัน

ตัวอย่างเช่นสมมติว่าเราต้องการคอมไพเลอร์ C และรูปภาพบางรูปชั่วคราว

# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc

(ตัวอย่างที่เป็นจริงมากขึ้นจะสร้างซอฟต์แวร์บางอย่างด้วยคอมไพเลอร์แทนที่จะเป็นเพียงการยืนยันว่ามีสถานะอยู่ด้วยการ--versionตั้งค่าสถานะ)

Snippet Dockerfile สร้างสามเลเยอร์ส่วนแรกประกอบด้วยชุด gcc เต็มรูปแบบดังนั้นแม้ว่าจะไม่ปรากฏในระบบไฟล์สุดท้ายข้อมูลที่เกี่ยวข้องยังคงเป็นส่วนหนึ่งของภาพในลักษณะเดียวกันและจำเป็นต้องดาวน์โหลดอัพโหลดและแกะเมื่อใดก็ตามที่ ภาพสุดท้ายคือ

with-idiom เป็นรูปแบบที่พบในการเขียนโปรแกรมการทำงานที่จะเป็นเจ้าของทรัพยากรแยกและทรัพยากรที่ปล่อยจากตรรกะที่ใช้มัน มันง่ายที่จะแปลงสำนวนนี้เป็น shell-scripting และเราสามารถใช้คำสั่งก่อนหน้าเป็นสคริปต์ต่อไปนี้เพื่อใช้กับCOPY & RUNในคำแนะนำ # 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

with_c_compilerคำสั่งที่ซับซ้อนสามารถจะกลายเป็นฟังก์ชั่นเพื่อให้พวกเขาสามารถเลี้ยงไป นอกจากนี้ยังเป็นไปได้ที่จะเชื่อมโยงการเรียกหลายwith_whateverฟังก์ชั่น แต่อาจไม่เป็นที่ต้องการมากนัก (การใช้คุณสมบัติที่เป็นความลับของเชลล์มากขึ้นเป็นไปได้อย่างแน่นอนที่จะทำให้with_c_compilerคำสั่งที่ซับซ้อนยอมรับได้

หากเราต้องการเพิกเฉยคำแนะนำ # 2 ข้อมูลโค้ด Dockerfile ที่ได้จะเป็นเช่นนั้น

RUN apt-get install -y gcc\
 && gcc --version\
 && apt-get --purge autoremove -y gcc

ซึ่งไม่ใช่เรื่องง่ายที่จะอ่านและบำรุงรักษาเพราะความงงงวย ดูว่าตัวแปรของเชลล์สคริปต์นั้นให้ความสำคัญกับส่วนที่สำคัญอย่างไรgcc --versionในขณะที่&&ตัวแปรที่ถูกล่ามโซ่ฝังอยู่ในส่วนที่อยู่ตรงกลางของเสียง


1
คุณสามารถรวมผลลัพธ์ของขนาดกล่องหลังจากสร้างโดยใช้สคริปต์และใช้หลายคำสั่งในคำสั่ง RUN เดียวได้หรือไม่
030

1
ดูเหมือนว่าเป็นความคิดที่ไม่ดีที่ฉันจะผสมการกำหนดค่าของฐานรูปภาพ (เช่นเนื้อหาของระบบปฏิบัติการ) และแม้กระทั่ง libs กับการตั้งค่าแหล่งที่คุณเขียน คุณพูดว่า "ตรวจสอบให้แน่ใจว่าขั้นตอนบิลด์ที่เกี่ยวข้องกับซอร์สโค้ดของคุณมาถึงช้าที่สุด" มีปัญหาในการทำให้ส่วนนั้นเป็นสิ่งประดิษฐ์ที่เป็นอิสระอย่างสมบูรณ์หรือไม่?
JimmyJames

1
@ 030 คุณหมายถึงอะไรกับขนาด“ กล่อง”? ฉันไม่รู้ว่าคุณกำลังอ้างถึงกล่องใด
Michael Le Barbier Grünewald

1
ฉันหมายถึงขนาดภาพนักเทียบท่า
030

1
@JimmyJames มันขึ้นอยู่กับสถานการณ์การปรับใช้ของคุณเป็นอย่างมาก หากเราถือว่าโปรแกรมที่คอมไพล์“ สิ่งที่ถูกต้องที่ต้องทำ” นั้นคือการทำแพ็กเกจและติดตั้งแพ็กเกจอ้างอิงและแพ็กเกจนั้นเองเป็นสองขั้นตอนที่แตกต่างกันใกล้จะเป็นขั้นสุดท้าย สิ่งนี้เพื่อเพิ่มประโยชน์ของแคชนักเทียบท่าและเพื่อหลีกเลี่ยงการดาวน์โหลดเลเยอร์ซ้ำ ๆ กันด้วยไฟล์เดียวกัน ฉันพบว่าการแบ่งปันสูตรการสร้างเพื่อสร้างภาพนักเทียบท่าง่ายกว่าการสร้างโซ่การพึ่งพาของรูปภาพนาน ๆ เพราะหลังทำให้การสร้างใหม่ยากขึ้น
Michael Le Barbier Grünewald

13

แต่ละคำสั่งที่คุณสร้างในผลลัพธ์ Dockerfile ของคุณจะสร้างเลเยอร์รูปภาพใหม่ แต่ละชั้นนำข้อมูลเพิ่มเติมที่ไม่ได้เป็นส่วนหนึ่งของภาพที่เกิดขึ้นเสมอ ตัวอย่างเช่นถ้าคุณเพิ่มไฟล์ในหนึ่งเลเยอร์ แต่ลบออกในเลเยอร์อื่นในภายหลังขนาดของภาพสุดท้ายจะรวมขนาดไฟล์ที่เพิ่มเข้าไปในรูปแบบของไฟล์ "whiteout" พิเศษแม้ว่าคุณจะลบมันออก

สมมติว่าคุณมี Dockerfile ต่อไปนี้:

FROM centos:6

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

ขนาดภาพที่ได้จะเป็น

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

ตรงกันข้ามกับ "คล้ายกัน" Dockerfile:

FROM centos:6

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

ขนาดภาพที่ได้จะเป็น

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

คุณจะได้ขนาดที่เล็กลงหากคุณล้างแคช yum ในคำสั่ง RUN เดียว

ดังนั้นคุณต้องการรักษาความสมดุลระหว่างความสามารถในการอ่าน / การบำรุงรักษาง่ายและจำนวนเลเยอร์ / ขนาดภาพ


4

RUNงบตัวแทนของแต่ละชั้นหนึ่ง ลองนึกภาพว่ามีหนึ่งดาวน์โหลดแพ็คเกจติดตั้งและต้องการลบมัน ถ้ามีใครใช้สามRUNประโยคขนาดของภาพจะไม่หดตัวเนื่องจากมีเลเยอร์แยกต่างหาก หากหนึ่งรันคำสั่งทั้งหมดโดยใช้คำสั่งเดียวRUNขนาดของดิสก์อิมเมจอาจลดลงได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.