ทำไมคอนเทนเนอร์ Docker จึงเพิกเฉยต่อตัวแก้ไข DNS ของ VPN
คอนเทนเนอร์คัดลอกตัวแก้ไขของโฮสต์เมื่อเริ่มทำงาน ดังนั้น VPN ที่เขียน DNS ใหม่ในภายหลังจึงมองไม่เห็นภายในนั้น จงส่งตัวแก้ไขอย่างชัดเจน
VPN ทำงานอยู่ เบราว์เซอร์เข้าถึงไซต์ภายในได้ แต่คอนเทนเนอร์ยังคงแก้ชื่อเดียวกันไม่ได้ การติดตั้งแพ็กเกจตายด้วยข้อผิดพลาดในการแก้ชื่อ เช่น EAI_AGAIN หรือแอปพลิเคชันหาโฮสต์ฐานข้อมูลที่เครื่องโฮสต์แก้ได้โดยไม่มีปัญหาใด ๆ ไม่พบ คอนเทนเนอร์ไม่ได้ใช้ตัวแก้ไขที่ VPN ติดตั้งไว้ Docker คัดลอกการกำหนดค่าตัวแก้ไขของโฮสต์ตอนสร้างคอนเทนเนอร์ และไม่มีอะไรคัดลอกอีกครั้งหลังจากนั้น
สองมุมมองของตัวแก้ไขบนเครื่องเดียว
บน Linux Docker มอบสำเนาของ การกำหนดค่าตัวแก้ไขของโฮสต์ ให้ทุกคอนเทนเนอร์ตอนสร้าง กรองที่อยู่ loopback ออก และรันเซิร์ฟเวอร์ DNS แบบฝังตัวที่ 127.0.0.11 ซึ่งส่งต่อคำถามออกไปข้างนอก ไคลเอนต์ VPN ที่เขียน /etc/resolv.conf ใหม่หลังจากคอนเทนเนอร์เริ่มทำงานไม่เปลี่ยนอะไรภายในเลย การรีสตาร์ตคอนเทนเนอร์ช่วยได้เฉพาะเมื่อตัวแก้ไขของโฮสต์บังเอิญถูกต้องในขณะนั้น
Docker Desktop บน macOS หรือ Windows เพิ่มอีกชั้น เพราะดีมอนอาศัยอยู่ในเครื่องเสมือน ตัวแก้ไข VPN ของโฮสต์อาจไม่เคยมองเห็นได้จาก VM นั้น การรีสตาร์ตจึงไม่เปลี่ยนอะไรเลย เปรียบเทียบสองมุมมองก่อนเลือกวิธีแก้: docker exec mycontainer cat /etc/resolv.conf ข้างรายการตัวแก้ไขของโฮสต์
คอนเทนเนอร์ที่แสดงตัวแก้ไขสาธารณะในขณะที่โฮสต์แสดงตัวแก้ไขภายในจะแก้ชื่อสาธารณะได้และล้มเหลวกับชื่อภายใน การแยกเช่นนี้คือลายเซ็นของปัญหานี้
ตัวเลือก A: ส่งตัวแก้ไขของอุโมงค์ต่อคอนเทนเนอร์
Docker ยอมรับการตั้งค่า DNS ต่อคอนเทนเนอร์ และนั่นคือการเปลี่ยนแปลงที่เล็กที่สุดที่ได้ผล อ่านที่อยู่ตัวแก้ไขของ VPN ก่อน: scutil --dns บน macOS resolvectl status หรือเอาต์พุตสถานะของไคลเอนต์เองบน Linux จากนั้นเริ่มคอนเทนเนอร์ด้วยที่อยู่เหล่านั้น เช่น docker run --dns 192.168.2.1 --dns 192.168.123.1 myimage ใน Compose การตั้งค่าเดียวกันคือรายการใต้บริการ ซึ่งอยู่ในระบบควบคุมเวอร์ชันแทนประวัติเชลล์ของใครสักคน
อาร์เรย์ dns ใน /etc/docker/daemon.json มีผลกับคอนเทนเนอร์ใหม่ทุกตัว ซึ่งฟังดูเรียบร้อยกว่าแต่ไม่ใช่ มีผลเช่นกันเมื่ออุโมงค์ล่ม และตัวแก้ไขที่เข้าถึงได้ทาง VPN เท่านั้นจะทำให้การดึงอิมเมจปกติเสียหาย การตั้งค่าต่อคอนเทนเนอร์ให้เหตุผลง่ายกว่าและย้อนกลับง่ายกว่า
ตัวเลือก B: ให้คอนเทนเนอร์ใช้เครือข่ายของโฮสต์ร่วมกัน
เครือข่ายของโฮสต์ข้ามสำเนาไปเลย เพราะคอนเทนเนอร์ใช้ตัวแก้ไขของโฮสต์และอุโมงค์ของโฮสต์ มันยังลบการแยกเครือข่ายที่คุณน่าจะต้องการ และทำงานไม่เหมือนกันภายใต้ Docker Desktop บน Linux นี่คือทางเลือกเชิงปฏิบัติสำหรับเครื่องมือใช้ครั้งเดียวที่ต้องแก้ชื่อภายใน
สำหรับการบิลด์ ไม่มีตัวเลือกใดใช้ได้ เพราะขั้นตอนบิลด์ไม่สืบทอดแฟล็กขณะรัน ให้บิลด์ด้วยเครือข่ายของโฮสต์ในที่ที่แพลตฟอร์มอนุญาต หรือเก็บการติดตั้ง dependencies ไว้ในขั้นตอนที่ไม่ต้องใช้อุโมงค์
สิ่งที่ทั้งสองตัวเลือกไม่ได้แก้
ที่อยู่ DNS ไม่มีประโยชน์ถ้าคอนเทนเนอร์ไม่มีเส้นทางไปถึง ตัวแก้ไขของ VPN มักอยู่ในพื้นที่ที่อยู่ของอุโมงค์ คอนเทนเนอร์จึงต้องมีเส้นทางไปยังคำนำหน้านั้น บริดจ์เริ่มต้นของ Docker โดยปกติส่งออกผ่านโฮสต์ และนั่นเพียงพอเมื่อโฮสต์มีเส้นทางของอุโมงค์และการส่งต่อเปิดอยู่ เครือข่ายบริดจ์ที่กำหนดเองซึ่งมีพูลที่อยู่ของตัวเองอาจชนกับซับเน็ตของ VPN แล้วคำถามก็ไปที่อื่นโดยสิ้นเชิง
ตรวจสอบซับเน็ตของเครือข่าย Docker เทียบกับซับเน็ตของ VPN ก่อนดีบักตัวแก้ไขเอง การทับซ้อนทำให้เกิดความล้มเหลวที่ดูเหมือนเซิร์ฟเวอร์ DNS เสียทุกประการ
ตรวจสอบภายในคอนเทนเนอร์
ทดสอบด้วย getent hosts db.internal ภายในคอนเทนเนอร์แทนการใช้เบราว์เซอร์หรือรันไทม์ภาษา เพราะสิ่งเหล่านั้นใช้นโยบาย DNS และแคชของตัวเอง ถ้า getent แก้ชื่อได้แต่แอปพลิเคชันยังล้มเหลว แสดงว่าตัวแก้ไขถูกต้องแล้ว และปัญหาที่เหลืออยู่ระดับแอปพลิเคชัน ถ้าแก้ไม่ได้ แสดงว่าขั้นตอน DNS ยังไม่เสร็จ
สร้างคอนเทนเนอร์ใหม่หลังจากเปลี่ยนการตั้งค่า DNS การแก้ไข /etc/resolv.conf ภายในคอนเทนเนอร์ที่กำลังทำงานจะหายไปพร้อมกับมัน และซ่อนปัญหาการกำหนดค่าไว้ตลอดเวลาที่ใครสักคนใช้รีสตาร์ตมัน