Tibor Vass จาก Docker Inc ผู้พัฒนาโครงการ Docker เขียนบล็อคแนะนำถึง 10 เทคนิคในการเขียน Dockerfile ให้มีคุณภาพ
Dockerfile เป็นหัวใจสำคัญของการพัฒนาแบบคอนเทนเนอร์ มันเป็นสคริปต์สำหรับการสร้างอิมเมจคอนเทนเนอร์ที่เหมือนกับการติดตั้งซอฟต์แวร์ลงเซิร์ฟเวอร์ โดยการรันอิมเมจแต่ละครั้งจะคาดเดาได้ว่าสภาพแวดล้อมเป๋นอย่างไร
11 เทคนิคที่ Vass แนะนำมีดังนี้
- เรียงลำดับสคริปต์โดยคิดถึงแคช คำสั่งในสคริปต์อาจจะสลับกันได้โดยเท่าเทียมกัน แต่การนำบรรทัดที่มีความเปลี่ยนแปลงบ่อยๆ ไปอยู่ต้นไฟล์ จะทำให้ไม่สามารถใช้แคชในขั้นต่อๆ ไปได้อีก
- ใช้คำสั่ง COPY อย่างเจาะจงไฟล์ คำสั่ง COPY ที่ใช้ส่งไฟล์จากเครื่องเข้าไปยังอิมเมจ หากมีการส่งไฟล์ที่ไม่จำเป็นเข้าไปในอิมเมจด้วย เมื่อไฟล์เหล่านั้นถูกแก้ไขก็จะไม่สามารถใช้ข้อมูลในแคชได้ ทำให้กระบวนการ build อิมเมจช้าลง
- ระวังแคช ให้รวมคำสั่งให้ต้องรันต่อเนื่องกันเข้าด้วยกัน เนื่องจากแต่ละคำสั่งจะมีแคชผลการรันอยู่ หากเราแยกคำสั่งที่ควรรันต่อเนื่องกัน เช่น
apt get update
ออกเป็นคนละคำสั่งกับ apt get install
ก็อาจจะทำให้อิมเมจของเราได้ซอฟต์แวร์เวอร์ชั่นเก่า
- ไม่ติดตั้งซอฟต์แวร์ที่ไม่จำเป็น เพื่อลดขนาดอิมเมจ ควรติดตั้งเฉพาะซอฟต์แวร์ที่จำเป็นต่อการทำงาน เทคนิคพื้นฐานเช่น APT นั้นมีแฟลก
--no-install-recommends
อยู่ เพื่องดติดตั้งซอฟต์แวร์ที่ "แนะนำ" แต่ไม่ได้จำเป็นได้
- ลบแคชทิ้งก่อนนำอิมเมจไปใช้ การติดตั้งหรืออัพเดตซอฟต์แวร์ในคอนเทนเนอร์มักโหลดไฟล์ต่างๆ มาเก็บไว้ในแคช ทำให้อิมเมจใหญ่เกินความจำเป็น ทาง Docker แนะนำให้ลบไฟล์เหล่านี้ทิ้งไปก่อนนำอิมเมจไปใช้งาน
- ใช้อิมเมจทางการก่อนหากเป็นไปได้ โครงการจำนวนมากมีอิมเมจทางการมาให้แต่แรก แทนที่จะเขียน Dockerfile ใหม่เองแต่ต้น การใช้อิมเมจหลักมาปรับแต่งช่วยประหยัดเวลาได้มาก
- เลือกแท็กให้เจาะจง อิมเมจมีระบบแท็กเพื่อให้เลือกเวอร์ชั่นของอิมเมจได้ แต่นักพัฒนามักเลือกแท็ก latest เป็นความเคยชินแต่พอใช้งานจริง ซอฟต์แวร์รุ่นล่าสุดอาจจะไม่สามารถทำงานร่วมกับซอฟต์แวร์เวอร์ชั่นเดิม ควรเจาะจงเวอร์ชั่นให้มากขึ้น
- เลือกอิมเมจขนาดเล็ก อิมเมจมาตรฐานจากโครงการต่างๆ มักมีให้เลือกรุ่นอิมเมจขนาดเล็ก เช่น OpenJDK มีรุ่น slim ที่เป็น Debian ย่อส่วน ทำให้อิมเมจโดยรวมเล็กลงมาก อย่างไรก็ดี อิมเมจบางรุ่นที่เล็กมากๆ อาจจะใช้ลินุกซ์รุ่นเล็กอย่าง Alpine ที่ใช้ ไลบรารีเป็น musl แทน GNU ทำให้อาจจะมีปัญหาความเข้ากันได้บางกรณี ในกรณีของ OpenJDK มีรุ่น jre ที่มีแต่รันไทม์อย่างเดียวโดยไม่ต้องมี SDK ทำให้ขนาดเล็กลงไปอีก
- สร้างอิมเมจจากซอร์สโค้ด แทนที่จะสร้างอิมเมจจากไบนารีที่คอมไพล์มาก่อนแล้ว ซึ่งทำให้อิมเมจที่ได้คาดเดาผลไม่ได้ การดึงเอาไฟล์ที่เกี่ยวข้องเข้ามาอยู่ในอิมเมจโดยตรง ทำให้เมื่อซฮร์สโค้ดเปลี่ยนแปลง กระบวนการสร้างอิมเมจก็จะรันครบถ้วน
- ดึงไฟล์ที่เกี่ยวข้องทีละลำดับ แยกลำดับการประมวลผลไฟล์ที่ต้องใช้งานในอิมเมจ (dependency) ออกมาทีละลำดับ และประมวลผลเท่าที่ทำได้ก่อน จะทำให้กระบวนการสร้างอิมเมจสามารถแคชขั้นตอนบางส่วนไว้ได้ เช่นการโหลด dependency ด้วย maven ก่อนจะสั่ง COPY ซอร์สโค้ดเข้ามา
- ใช้กระบวนการ build หลายขั้น การสร้างอิมเมจที่มีไฟล์ที่จำเป็นสำหรับการคอมไพล์โปรแกรมมักทำให้อิมเมจมีขนาดใหญ่ ทางเลือกคือการใช้เทคนิค multi-stage สร้างอิมเมจสำหรับคอมไพล์ซอฟต์แวร์ แยกออกมาจากอิมเมจสำหรับการรัน โดย Dockerfile สามารถดึงไฟล์จากอิมเมจที่ใช้คอมไพล์โปรแกรมได้อยู่แล้ว ด้วยอาร์กิวเมนต์
--from
ในคำสั่ง COPY
ผมเองหลังจากอ่านแล้วก็พบว่าไม่ได้สนใจแนวทางเหล่านี้ในบางครั้ง การคำนึงถึงประเด็นที่ทาง Docker ระบุมา น่าจะช่วยให้อิมเมจมีขนาดเล็ก โหลดได้รวดเร็ว และลดเวลา build โดยรวมในกรณีที่ไม่จำเป็นออกไปได้
ที่มา - Docker Blog