คอนเทนเนอร์สำหรับพัฒนามองไม่เห็นโฮสต์ที่เข้าถึงได้ทาง VPN เท่านั้น: ควรตรวจตรงไหน
เบราว์เซอร์และคอนเทนเนอร์มีบริบทของตัวแปลงชื่อ พร็อกซี และเส้นทางที่ต่างกัน ตรวจสอบว่ากระบวนการที่ล้มเหลวใช้บริบทใดก่อนจะเปลี่ยนการตั้งค่า VPN ใด ๆ
API ส่วนตัวเปิดได้ในเบราว์เซอร์บนเดสก์ท็อปของคุณ แต่ล้มเหลวในคอนเทนเนอร์สำหรับพัฒนา นั่นไม่ใช่ความขัดแย้ง และแทบไม่ใช่ตัว VPN เอง คำขอจากเบราว์เซอร์กับคำขอที่ล้มเหลวถูกสร้างโดยคนละกระบวนการ มีตัวแปลงชื่อ พร็อกซี และเส้นทางคนละชุด และบ่อยครั้งมาจากเนมสเปซเครือข่ายที่ต่างกันโดยสิ้นเชิง คำว่า "บนแล็ปท็อปของฉัน" อาจหมายถึงโฮสต์ Windows ดิสทริบิวชัน WSL เครื่องเสมือนของ Docker Desktop และคอนเทนเนอร์ภายในเครื่องเสมือนนั้น ซึ่งเป็นสี่บริบทที่แต่ละอันอาจตอบชื่อโฮสต์เดียวกันต่างกัน ตรวจสอบว่ากระบวนการที่ล้มเหลวใช้บริบทใดก่อนจะแตะการตั้งค่าแม้แต่รายการเดียว
ระบุกระบวนการก่อนเปลี่ยนการตั้งค่า
เริ่มจากเอนจินคอนเทนเนอร์แทนที่จะเป็นไคลเอนต์ VPN ถามว่าบรรทัดคำสั่งกำลังคุยกับบริบทใด และเอนจินนั้นรันบนระบบปฏิบัติการใด เพราะคอนเทนเนอร์ Linux ที่จัดการโดยเครื่องเสมือนบนเดสก์ท็อปเป็นเส้นทางที่ต่างจากคอนเทนเนอร์ที่จัดการโดยดีมอนในเครื่อง:
docker context ls
docker info --format '{{.OperatingSystem}} / {{.Name}}'
docker run --rm alpine ip route
บน Windows, wsl -l -v บอกคุณว่าดิสทริบิวชันรันภายใต้ WSL 2 หรือไม่ ซึ่งทำให้มันอยู่หลังสวิตช์เสมือนของตัวเอง รายละเอียดนี้ตัดสินว่าคอนเทนเนอร์ใช้ตารางเส้นทางร่วมกับโฮสต์ หรืออยู่หลัง NAT ที่ต้องมีพอร์ตที่ส่งต่อหรือชื่อโฮสต์เพื่อเข้าถึงโฮสต์
ให้คำขอหนึ่งรายการคงที่แล้วเปรียบเทียบ
เลือกปลายทางที่ไม่เป็นอันตรายหนึ่งจุดบนโฮสต์ส่วนตัว แล้วใช้จุดนั้นในการเปรียบเทียบทุกครั้ง จากเบราว์เซอร์ จากเทอร์มินัลของโฮสต์ จากดิสทริบิวชัน WSL และจากภายในคอนเทนเนอร์ บันทึกสี่สิ่งสำหรับแต่ละครั้ง: ที่อยู่ที่แปลงได้ ใช้พร็อกซีหรือไม่ ประเภทของความล้มเหลว และเวลาที่ผ่านไป การค้นหาชื่อ การสร้างการเชื่อมต่อ การตรวจสอบ TLS และสถานะ HTTP เป็นปัญหาที่ต่างกันสี่อย่าง และการรวมมันเป็น "มันไม่ทำงาน" ทำให้เสียเวลาทั้งเช้า
รันการเปรียบเทียบภายในคอนเทนเนอร์ที่ล้มเหลว โดยกำหนดเวลาหมดอายุ เพื่อให้ไฟร์วอลล์ที่เงียบไม่ยึดเทอร์มินัลไว้หลายนาที คอนเทนเนอร์ใหม่จำลองบริบทเครือข่ายของเอนจิน แต่ไม่จำลองเนมสเปซ ตัวแปรพร็อกซี หรือตัวแปลงชื่อของคอนเทนเนอร์แอปพลิเคชัน ดังนั้น docker exec เข้าไปในตัวจริงคือการทดสอบที่เชื่อถือได้ และคอนเทนเนอร์เสริมเป็นเพียงวิธีแยกว่าความต่างอยู่ที่เอนจินหรือคอนเทนเนอร์:
docker exec -it app /bin/sh -c "getent hosts api.internal; curl -v --max-time 8 https://api.internal/ 2>&1 | tail -20"
docker run --rm --network container:app curlimages/curl:latest -sv --max-time 8 https://api.internal/ 2>&1 | tail -20
อิมเมจพื้นฐาน alpine มี busybox และไม่มี curl ซึ่งเป็นรายละเอียดแบบที่ทำให้บล็อกการวินิจฉัยที่คัดลอกมาล้มเหลว: เลือกอิมเมจที่มีไคลเอนต์ หรือเพิ่มเข้าไปในการรันเดียวกัน การเปรียบเทียบกับเว็บสาธารณะที่ไม่เกี่ยวข้องไม่ได้แยกอะไรเลย ถ้า https://example.com ทำงานได้จากคอนเทนเนอร์ คุณได้รู้ว่าคอนเทนเนอร์เข้าถึงอินเทอร์เน็ตได้ และไม่ได้รู้อะไรเลยว่ามันเข้าถึงที่อยู่ส่วนตัวผ่านอุโมงค์ได้หรือไม่ ในทำนองเดียวกัน การเปิดเทอร์มินัลใหม่พิสูจน์เพียงว่าเทอร์มินัลใหม่ทำงานได้ ไม่ได้พิสูจน์ว่าใช้รันไทม์ร่วมกับแอปพลิเคชันที่ล้มเหลว
Docker Desktop ทำงานร่วมกับ VPN ได้
เอกสารของ Docker เองเกี่ยวกับ VPN และการเชื่อมต่อกับโฮสต์ อธิบายว่านี่เป็นการตั้งค่าที่รองรับ และสรุปตามจริงคือมันทำงานได้ ไม่ได้พังโดยอัตโนมัติ อย่าสันนิษฐานว่าเครื่องเสมือนบนเดสก์ท็อปข้ามอุโมงค์ของโฮสต์โดยการออกแบบ ในการตั้งค่าหลายแบบ ทราฟฟิกตามอุโมงค์ของโฮสต์อย่างถูกต้อง ซึ่งเป็นเหตุให้ความเชื่อที่ว่า "Docker ไม่สนใจ VPN" ให้คำแนะนำที่มั่นใจแต่ผิด
สิ่งที่ยังสำคัญคือ backend ที่ใช้ นโยบายแยกอุโมงค์ของไคลเอนต์ และการกำหนดค่าตัวแปลงชื่อที่เครื่องเสมือนได้รับ ไคลเอนต์ VPN ที่ติดตั้งเส้นทางแต่ไม่ติดตั้ง DNS หรือที่ส่งเซิร์ฟเวอร์ DNS ที่เครื่องเสมือนเข้าถึงไม่ได้ จะทำให้เกิดความล้มเหลวในการค้นหาชื่อภายในคอนเทนเนอร์ ขณะที่โฮสต์ทำงานได้เพราะมีตัวแปลงชื่อเพิ่มเติมหรือมีคำตอบแคชไว้ นั่นคือปัญหาตัวแปลงชื่อที่ใส่ชุดคอนเทนเนอร์อยู่ มันทนต่อการเชื่อมต่อ VPN ใหม่ทุกครั้งที่คุณลอง เพราะอุโมงค์กำลังพาทราฟฟิกที่คุณขอให้พาอยู่แล้ว
การทับซ้อนของที่อยู่และ MTU เป็นความล้มเหลวคนละแบบ
ความล้มเหลวสองอย่างสมควรได้รับการวินิจฉัยของตัวเอง เพราะมักถูกเข้าใจผิดว่าเป็นนโยบายการเข้าถึง อย่างแรกคือการทับซ้อนของที่อยู่: ถ้า VPN ส่งช่วงที่ชนกับซับเน็ตบริดจ์เริ่มต้นของเครือข่ายคอนเทนเนอร์ เส้นทางของคอนเทนเนอร์สำหรับช่วงนั้นจะชี้ไปที่บริดจ์ในเครื่องและไม่เคยไปถึงอุโมงค์ ตรวจดูซับเน็ตจริงแทนการเดา:
docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
ip route get 10.20.30.40
อย่างที่สองคือปัญหา MTU ถ้าการเชื่อมต่อถูกสร้างและ TLS เสร็จสิ้น แต่การตอบกลับขนาดใหญ่หยุดชะงัก คุณมีปัญหาขนาดแพ็กเก็ต ไม่ใช่ปัญหาการอนุญาต และการเปลี่ยนระเบียน DNS จะไม่แก้ไขมันเลย ทดสอบด้วยคำขอเล็กและคำขอใหญ่ก่อนเปลี่ยนอะไร แล้วบันทึกผลลัพธ์ทั้งสอง
วิธีแก้สำหรับการทับซ้อนคือซับเน็ตคอนเทนเนอร์อื่นหรือเส้นทางที่จำกัดขอบเขต ซึ่งผู้ที่เป็นเจ้าของแผนที่อยู่เป็นผู้เลือก วิธีแก้สำหรับการหยุดชะงักของ MTU คือการจำกัดหรือลด MTU ของอุโมงค์หรืออินเทอร์เฟซให้เป็นค่าที่ทนต่อเส้นทาง โดยใช้ในชั้นที่คุณควบคุม
การกำหนดค่าพร็อกซีเป็นรายกระบวนการ ไม่ใช่รายเครื่อง
ภายในคอนเทนเนอร์ localhost หมายถึงบริบทเครือข่ายของคอนเทนเนอร์นั้นเอง ดังนั้นพร็อกซีที่รันบนโฮสต์จึงเข้าถึงไม่ได้ที่นั่นด้วยชื่อนั้น Docker Desktop มี host.docker.internal สำหรับการเข้าถึงโฮสต์ และบน Linux คุณเพิ่มมันอย่างชัดเจนด้วย --add-host=host.docker.internal:host-gateway ไม่ทางใดก็ทางหนึ่ง ชื่อนั้นให้เส้นทางไปยังโฮสต์แก่คุณ มันไม่ได้สร้างบริการพร็อกซีและไม่ได้ให้อำนาจคุณใช้มัน
ใช้ที่อยู่และพอร์ตพร็อกซีที่จัดหาให้สำหรับสภาพแวดล้อมจริง และตรวจสอบว่าใครเป็นผู้อ่านมัน ตัวแปรที่ส่งออกในเชลล์แบบโต้ตอบไม่ได้กำหนดค่าส่วนขยายเอดิเตอร์ทุกตัว เซิร์ฟเวอร์ภาษา เครื่องมือสร้าง และดีมอนเบื้องหลังโดยอัตโนมัติ สิ่งที่สำคัญคือกระบวนการที่สร้างคำขอที่ล้มเหลว ดังนั้นตรวจสอบสภาพแวดล้อมของมัน ไม่ใช่ของเทอร์มินัลคุณ
สุดท้าย ทำให้วิธีแก้ไขอธิบายตัวเองได้ การแก้ไขที่มีประโยชน์ระบุขอบเขตที่ผิด การตั้งค่าที่เปลี่ยนมัน และชุดกระบวนการที่มันส่งผลต่อในตอนนี้ การรีสตาร์ต VPN เอดิเตอร์ และเอนจินตามลำดับอาจเปลี่ยนอาการ แต่ไม่ได้ทิ้งอะไรไว้ให้คนถัดไปใช้