tva
← Insights

การอัปเกรด Docker Engine หลักบนเซิร์ฟเวอร์โปรดักชันที่ใช้งานอยู่

การอัปเกรด Docker Engine บนเซิร์ฟเวอร์ที่รัน container กว่าร้อยตัวอยู่อย่างต่อเนื่องไม่ใช่งานที่ได้รับรางวัลจากการ improvise เมื่อถึงเวลาที่ต้องย้ายจาก Docker Engine 28 ไปยัง 29 และ Docker Compose v2 ไปยัง v5 บนเซิร์ฟเวอร์โปรดักชัน Hetzner ของเรา เราจัดการด้วยวินัยเดียวกับที่เราใช้กับ database migration: checklist การเตรียมความพร้อมที่เขียนไว้, เส้นทาง rollback ที่ทดสอบแล้ว, ลำดับการดำเนินการที่แม่นยำ และขั้นตอนการตรวจสอบหลังอัปเกรดก่อนที่จะประกาศว่าหน้าต่างปิดแล้ว

ต่อไปนี้เป็นบันทึกครบถ้วนของวิธีที่เราจัดการกับมัน — เหตุผลเบื้องหลังแต่ละขั้นตอน คำสั่งที่เราใช้จริง และสิ่งที่ต้องระวังเมื่อจำนวน container สูงพอที่ restart policy ที่กำหนดค่าผิดอาจ cascade เป็นการกู้คืนที่ยากลำบาก

ทำไมการอัปเกรดนี้จึงต้องการ Runbook อย่างเป็นทางการ

Docker Engine 29 นำการเปลี่ยนแปลงมายัง containerd integration path และปรับวิธีที่ docker compose จัดการ dependency resolution ใน depends_on blocks พร้อม condition: service_healthy Docker Compose v5 ในขณะเดียวกันเป็นการเขียนใหม่ภายในที่สำคัญ — ย้ายจาก Go-based v2 binary ไปยังสถาปัตยกรรมใหม่ที่แก้ปัญหาที่ค้างมายาวนานหลายอย่างเกี่ยวกับ parallel startup ordering แต่ยังลบ compose file directives บางส่วนที่ v2 เพิกเฉยอย่างเงียบ ๆ

บนเซิร์ฟเวอร์ที่ docker-compose.yml เดียวอาจควบคุม service ที่ขึ้นต่อกัน 12 ตัว — database, reverse proxy, application container, background worker — การเปลี่ยนแปลง behavior ใน dependency resolution อย่างเงียบ ๆ เป็นปัญหาประเภทที่ไม่ปรากฏทันที มันปรากฏเวลา 03:00 น. เมื่อ healthcheck ไม่เคย fire และ container ที่ขึ้นต่อกัน loop ไม่สิ้นสุด

Checklist การเตรียมความพร้อม

ก่อนที่จะแตะ package ใด ๆ เราผ่าน checklist ต่อไปนี้ แต่ละรายการมีเหตุผล

1. รายการ container ที่กำลังรันทั้งหมดและ restart policy ของพวกมัน

docker ps --format "table {{.Names}}	{{.Status}}	{{.Image}}" | sort
docker inspect $(docker ps -q) --format '{{.Name}} restart={{.HostConfig.RestartPolicy.Name}}' | sort

สิ่งนี้ให้ baseline Container ที่มี restart: always จะพยายาม come back up อัตโนมัติหลัง Docker daemon รีสตาร์ — ซึ่งเป็นสิ่งที่คุณต้องการ แต่เฉพาะเมื่อ compose file และ image อยู่ในสถานะที่รู้จักดีเท่านั้น Container ที่ crash-loop ในขณะที่อัปเกรดจะยังคง crash-loop หลังจากนั้น และควรรู้ตอนนี้ดีกว่า

2. ตรวจสอบ compose file ทั้งหมดกับ v5 schema ก่อนอัปเกรด

docker compose config --quiet 2>&1 | grep -i warning

รันสิ่งนี้ในทุก directory ที่มี docker-compose.yml Compose v5 เข้มงวดกว่าเกี่ยวกับ key ที่ deprecated — โดยเฉพาะ version: ที่ด้านบนของ compose file (ตอนนี้เพิกเฉยแต่สร้าง warning) และการใช้ links: directive ที่ deprecated

3. ยืนยัน disk space ที่มีอยู่

df -h /var/lib/docker
docker system df

การอัปเกรดจะ pull containerd shim ใหม่และแทนที่ Docker Engine binary binary เก่าและ dependencies ของมันไม่ได้ถูกทำความสะอาดอัตโนมัติเสมอ บนเซิร์ฟเวอร์ที่รันมาหนึ่งปี docker system df มักจะเปิดเผย image layer ที่ reclaim ได้หลาย gigabyte ทำความสะอาดสิ่งเหล่านี้ก่อนอัปเกรด ไม่ใช่ระหว่าง

docker system prune -f --volumes

4. Export Docker Engine version และ compose version ปัจจุบันไปยัง reference file

docker version > /root/docker-pre-upgrade.txt
docker compose version >> /root/docker-pre-upgrade.txt
docker ps -a >> /root/docker-pre-upgrade.txt

5. แจ้ง downstream system

เซิร์ฟเวอร์ของเรารัน Supabase stack ที่โฮสต์เองและ n8n workflow automation instance หลายตัว webhook ใด ๆ ที่ fire ในระหว่าง container restart cycle จะล้มเหลวอย่างเงียบ ๆ เราตั้ง maintenance window ในเครื่องมือ uptime monitoring และปิด inbound webhook ใน n8n ตลอดช่วงเวลานั้น

แผน Rollback

แผน rollback มีประโยชน์เฉพาะเมื่อคุณตัดสินใจล่วงหน้าว่าเงื่อนไขใดที่ trigger มัน ของเราเรียบง่าย: ถ้า container ใดที่กำลังรันก่อนอัปเกรดไม่ได้รันสิบนาทีหลังอัปเกรดเสร็จสิ้น และไม่สามารถกู้คืนได้ด้วย docker compose up -d เรา rollback Docker Engine binary

apt-cache policy docker-ce docker-ce-cli containerd.io docker-compose-plugin

คำสั่ง rollback หากจำเป็น:

apt-get install -y   docker-ce=5:28.1.1-1~debian.12~bookworm   docker-ce-cli=5:28.1.1-1~debian.12~bookworm   containerd.io   docker-compose-plugin=2.35.1-1~debian.12~bookworm

กระบวนการอัปเกรด

การอัปเกรดจริงตรงไปตรงมาเมื่อการเตรียมความพร้อมเสร็จสิ้น ลำดับมีความสำคัญ: อัปเดต package index ก่อน ตรวจสอบสิ่งที่จะติดตั้ง จากนั้นติดตั้ง

ขั้นตอนที่ 1: อัปเดต apt index สำหรับ Docker repository เท่านั้น

apt-get update -o Dir::Etc::sourcelist="sources.list.d/docker.list"   -o Dir::Etc::sourceparts="-"   -o APT::Get::List-Cleanup="0"

ขั้นตอนที่ 2: ยืนยัน candidate version

apt-cache policy docker-ce docker-ce-cli containerd.io docker-compose-plugin

ขั้นตอนที่ 3: ติดตั้ง

apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

สิ่งนี้จะหยุด Docker daemon, แทนที่ binary และรีสตาร์ daemon Container ที่มี restart: always จะถูก bring back up โดย daemon เมื่อรีสตาร์

ขั้นตอนที่ 4: ตรวจสอบ version ที่ติดตั้ง

docker version
docker compose version

การตรวจสอบหลังอัปเกรด

สิบนาทีหลังอัปเกรด เราทำการตรวจสอบอย่างมีโครงสร้าง

docker ps -a --format "table {{.Names}}	{{.Status}}	{{.RunningFor}}" | sort

Container ใดที่แสดง Restarting หรือ Exited ต้องการการสืบสวนทันที บนเซิร์ฟเวอร์ของเรา container หนึ่งตัว — Supabase analytics service — แสดง Restarting (1) มันอยู่ใน crash loop ความถี่ต่ำก่อนอัปเกรดเช่นกัน การอัปเกรดไม่ได้ทำให้เกิดมัน แต่มันปรากฏชัดเจนกว่าในการ scan หลังอัปเกรด

สิ่งที่เราสังเกตเห็น

การอัปเกรดเสร็จสิ้นในเวลาน้อยกว่าสองนาที Container ทั้งหมดที่รันก่อนอัปเกรดรันหลังจากนั้น การเปลี่ยนแปลง Docker Compose v5 ที่เราสังเกตเห็นทันทีที่สุดคือรูปแบบ output --dry-run ที่สะอาดกว่า — version ใหม่สร้าง output ที่มีโครงสร้างและมีสีซึ่งทำให้ review สิ่งที่ docker compose up จะทำจริง ๆ ก่อน execute ง่ายขึ้น

การเปลี่ยนแปลงสาระสำคัญหนึ่ง: Compose v5 ไม่เพิกเฉยอย่างเงียบ ๆ ต่อ field container_name ร่วมกับ scale อีกต่อไป เรามี stack หนึ่งที่ใช้ทั้งสองอย่าง — ของเก่าจากก่อนที่เราย้ายไปใช้ replica ที่เหมาะสม Compose v5 ส่ง error ที่ชัดเจนใน docker compose up สำหรับ stack นั้น ในขณะที่ v2 เพิกเฉย conflict อย่างเงียบ ๆ การแก้ไขคือการแก้ไขบรรทัดเดียวเพื่อลบ field container_name ที่ซ้ำซ้อน

บทความที่เกี่ยวข้อง

บทความที่เกี่ยวข้อง