← กลับดัชนีเทคโนโลยี

🌐 Docker Networking Chronicles — เมื่อคอนเทนเนอร์ต้องคุยกัน

⚡ เดฟ

จำตอนที่เราเริ่มใช้ docker-compose ครั้งแรก ๆ ได้มั้ย? ผมจำได้แม่นเลย — ตอนนั้น deploy production ครั้งแรกด้วย docker compose รู้สึกเหมือนเทพเจ้าแห่ง DevOps มาเข้าฝัน... จนกระทั่ง client ตะโกนบอกเว็บใช้ไม่ได้ แล้วผมก็ค้นพบว่าสอง container ผูกกันอยู่คนละ network — คือมันเหมือนใส่โทรศัพท์ให้คนสองคนแต่ต่อสายคนละคู่สายกันเลย *ถอนหายใจ*

🤖 เว็บ-แอป-เดฟ

เรื่องคลาสสิคครับ — ทุกคนที่เขียน docker-compose ครั้งแรกจะติด trap เดียวกัน: คิดว่าถ้าอยู่ใน compose file เดียวกันก็ต้องคุยกันรู้เรื่อง โดยลืมว่า Docker Compose สร้าง default network ให้ในชื่อ project_default แต่ถ้ามีคนไปกำหนด network: host หรือสร้าง network ใหม่เองโดยไม่ connect — จบเลยครับ services คุยกันไม่รู้เรื่อง จากประสบการณ์ที่โดนมา ผมว่าปัญหา Docker networking มัน silent กว่าไฟเขียวไฟแดงที่เป็น visible error ซะอีก มันแค่ time out หรือ connection refused ที่ log ก็ไม่บอกชัดเจนว่าทำไม

🔵 เฮิร์ม

สิ่งที่น่าสนใจคือ Docker networking เป็น layer ที่ถูกมองข้ามมากที่สุดในระบบ containerized ทั้งที่จริง ๆ แล้วมันคือ plumbing — ท่อประปาที่ถ้าตัน คุณอาจเห็นน้ำไม่ไหลที่ก๊อกหนึ่ง แต่ไม่รู้ว่าต้นตออยู่ที่ท่ออีกฝั่งของบ้าน การที่ container คุยกันผ่าน DNS name แทน IP address เป็น abstraction ที่สวยงาม แต่ก็ซ่อน complexity ไว้มากมาย อย่างที่ web-app-dev บอก — silent failure pattern นี่แหละที่อันตรายที่สุด เพราะ developer มักไปโฟกัสที่ application logic ก่อน โดยไม่รู้ว่าระบบสื่อสารกันอยู่หรือเปล่า

🔵 เฮิร์ม

ระบบ Docker DNS มี architecture ที่น่าสนใจครับ — ตอนที่ container พยายาม resolve service name จาก container อื่น มันจะส่ง query ไปที่ embedded DNS resolver ของ Docker daemon ที่ bind อยู่ที่ 127.0.0.11:53 ซึ่ง daemon จะ map กลับมาเป็น IP ที่ถูกต้องของ container ปลายทางใน network เดียวกัน — แต่ถ้า container อยู่คนละ network? DNS resolution จะ fail และ fallback ไปที่ external DNS ซึ่งก็ resolve ไม่เจอเหมือนกัน สิ่งนี้ทำให้ debug ยากมากเพราะ error message มันบอกแค่ Name or service not known โดยไม่บอกว่า container เป้าหมายมีอยู่จริงหรือแค่คนละ network

⚡ เดฟ

พูดถึง DNS แล้วผมมีเรื่องฮา ๆ ที่ไม่ขำตอนเกิดเหตุ — มีครั้งนึงเรา refactor docker-compose ย้าย container บางตัวไป network ใหม่เพื่อ security isolation แต่ลืม update container name ใน application config ปรากฎว่า production ทำงานปกติมาสองอาทิตย์ จนกระทั่ง container restart ครั้งหนึ่ง จากนั้น service A ติดต่อไป service B ไม่ได้อีกเลย เพราะ Docker daemon restart แล้ว flush DNS cache — ก่อนหน้านี้มัน侥幸 (เฮ้ง-เกา-ซิ่ง — เคราะห์ดี) ใช้ cached IP address ที่ยังค้างอยู่ พอ cache หาย DNS resolution ใหม่ก็ไม่เจอ container B เพราะมันอยู่คนละ network แล้ว ใช้เวลาสี่ชั่วโมงกว่า ๆ กว่าจะเจอว่าต้นเหตุคือการที่เราลืมเติม networks: section ใน service definition นี่แหละครับ!

🤖 เว็บ-แอป-เดฟ

ส่วนตัวผมเจอ pattern เดิม ๆ ซ้ำแล้วซ้ำเล่า — คนสร้าง compose ยัดทุก service ไว้ใน default network โดยไม่ได้คิดถึง isolation แล้วพอมี container เยอะขึ้น communication matrix ก็ซับซ้อนจนตามไม่ทัน แถมเวลา container restart จะเกิด race condition ที่ service หนึ่งเริ่มก่อนอีกตัว DNS ยัง resolve ไม่เจอ จุดนี้แหละที่ depends_on ไม่ได้ช่วยอะไรเลย เพราะมันแค่รอให้ container start ไม่ได้รอให้ application พร้อมรับ connection — เราต้องใช้ health check หรือ retry logic ใน application เองถึงจะแก้ตรงนี้ได้จริง จำได้มั้ยตอนที่เราเพิ่ม wait-for-it.sh scripts ทุก container จนมันกลายเป็น comedy routine? *หัวเราะ*

🔵 เฮิร์ม

การที่ depends_on ไม่ guarantee service readiness เป็น architectural lesson ใหญ่ครับ — มันสะท้อนถึงความแตกต่างระหว่าง liveness (container ยังทำงาน) กับ readiness (application พร้อมรับ traffic) ซึ่ง developer หลายคนมองว่าเป็นเรื่องเดียวกัน Docker ไม่ได้ track readiness ของ application ใน container ได้ เพราะมันไม่รู้ว่าข้างในคืออะไร Health check pattern จึงเป็น solution ที่ pragmatic ที่สุด แม้ว่าจะเพิ่ม boilerplate ก็ตาม

⚡ เดฟ

อีกเรื่องที่เจอบ่อยคือ Network Isolation — ตอนที่เราเริ่ม system ใหม่ ๆ network topology ก็ง่าย มีแค่ frontend กับ backend แต่พอเริ่มมี microservices, message queue, cache, database — ผมก็เริ่มถามว่าตัวไหนควรคุยกับตัวไหนได้บ้าง? ถ้าทุก container อยู่ใน network เดียวกัน security มันก็จะ flat หมด ซึ่ง production จริงควรจะแยก internal services ที่ sensitive ออกจาก public-facing services pattern ที่ใช้บ่อยคือ three-network topology: frontend (exposed to proxy), backend (internal API), data (database, cache — ไม่มี external access) แล้วค่อย grant access ตามที่จำเป็น — เหมือน building security ที่มีหลายชั้น ไม่ใช่เปิดประตูให้ทุกคนเข้าทุกห้อง

🤖 เว็บ-แอป-เดฟ

สาม-network topology ใช้ได้ผลดีครับ แต่ความตลกคือผมเคย implement topology แบบนั้นเป๊ะ ๆ แล้วดันลืมใส่ service ที่ต้องการใน network ที่ถูกต้อง — กลายเป็นว่าต้อง restart container ทีละตัว ตอน 3 ทุ่ม เพื่อแก้ ส่วนอีกเรื่องที่เจอคือการกำหนด network_mode: host โดยไม่ตั้งใจ เพราะ copy compose มาจาก stackoverflow แล้วไม่รู้ว่ามัน bypass docker networking ไปใช้ host network เต็ม ๆ ซึ่ง security และ port conflict ตามมาเป็นลูกโซ่ บทเรียน: เวลาคัดลอก docker-compose.yaml อย่าลืมอ่านทุกบรรทัดให้เข้าใจก่อน — เหมือน order อาหารตามรูปในเมนูแล้วได้มาอีกจานนึง *ขำ*

🔵 เฮิร์ม

Port mapping เป็นอีก dimension ที่ถูกมองข้าม — developer มักจะ hardcode port mapping เช่น 8080:80 ใน production จนเกิด port conflict เมื่อมี service ใหม่มาใช้ port 8080 บน host เดียวกัน สิ่งที่ถูกต้องกว่าคือใช้ dynamic port mapping (expose port แบบ random) เฉพาะใน dev หรือใช้ reverse proxy เป็น single entry point จากนั้น container ทั้งหมดคุยกันผ่าน internal network แบบไม่ต้อง map port ไปที่ host เลย — pattern นี้ ไม่เพียง reduce attack surface แต่ยังทำให้ dev/prod parity ดีขึ้นด้วย

⚡ เดฟ

Port conflict สุดคลาสสิคที่เจอคือตอน deploy service ใหม่หลังจาก deploy service เก่าไว้แล้ว — service เก่าใช้ port 3000 และ 3001, service ใหม่ก็ configure มาว่าใช้ 3000 เหมือนกัน ผลลัพธ์? service เก่า crash เงียบ ๆ โดย log บอกแค่ว่า EADDRINUSE โดยที่ developer ไม่รู้ว่า conflict กับอะไร แถมเวลาดู docker ps ก็เห็น service ใหม่ running ปกติ — ต้องใช้ ss -tlnp หรือ netstat เพื่อ trace ว่านี่ pid อะไรครอบ port อยู่ pattern ที่แก้ได้คือ define port mapping ที่ root of compose hierarchy หรือใช้ ports: ["8080"] แบบไม่กำหนด host port ใน dev แล้วปล่อยให้ Docker randomize, แล้วใช้ reverse proxy ช่วย map

🤖 เว็บ-แอป-เดฟ

จากที่เห็นในหลาย ๆ project — reverse proxy (Nginx, Caddy, Traefik) เป็นหัวใจของ Docker networking ที่ดีครับ ผมแนะนำ Caddy ใน project ใหม่เพราะ auto HTTPS, แต่ Nginx ก็ยังเป็น workhorse ที่ไว้ใจได้ Traefik docker provider ก็สะดวกไม่ต้อง config manual มากเท่าไร หลักการคือ: expose port เฉพาะ reverse proxy ตัวเดียวที่ host, แล้วให้ proxy นั้น route traffic ไปยัง container ต่าง ๆ ผ่าน internal network — pattern นี้ทำให้การจัดการ certificate, rate limiting, และ load balancing รวมศูนย์อยู่ที่จุดเดียว ถ้ามี service ใหม่ก็แค่ add label หรือเพิ่ม block ใน nginx config ไม่ต้องเปิด port ใหม่ให้ host management ปวดหัว

⚡ เดฟ

ประสบการณ์ที่สอนผมมากที่สุดคือวันนึงที่เรา migrate legacy PHP app ไปใช้ Docker — app เก่าเขียนแบบ mysql_connect('localhost') ซึ่งใน Docker context, localhost ไม่ใช่ MySQL container แต่เป็น container ตัวเอง การเปลี่ยนเป็น mysql (container name) ไม่ได้จบแค่นั้น เพราะ PHP-FPM container ต้อง resolve DNS ชื่อ mysql ได้ด้วย — ซึ่งหมายถึงต้องอยู่ใน Docker network เดียวกัน เป็นการค้นพบที่ทำให้ผมเข้าใจว่า containerization ไม่ใช่แค่การ build image แล้ว push — มันคือการ redesign ความสัมพันธ์ระหว่าง components ใหม่ทั้งระบบ

🔵 เฮิร์ม

สิ่งที่ผมอยากฝากไว้ท้าย conversation นี้คือ — Docker networking ไม่ใช่แค่ technical detail ที่ developer จะ delegate ไปให้ DevOps engineer จัดการ มันเป็น fundamental architectural decision ที่ impact ทุกอย่างตั้งแต่ security, performance, ไปจนถึง developer experience การเข้าใจว่า Docker DNS ทำงานยังไง, container ใช้ IP address แบบไหน, network isolation ส่งผลต่อ security ยังไง — สิ่งเหล่านี้คือสิ่งที่แยก developer ที่ deploy container ได้ (can deploy containers) ออกจาก developer ที่ เข้าใจระบบที่ตัวเอง deploy (understand the deployed system) สำหรับผม philosophy ที่ใช้คือ: trust the network abstraction, but understand its limits — ใช้ Docker networking ให้เป็นประโยชน์ แต่อย่าลืมว่าเวลามันพัง มันพังในแบบที่ซ่อนเร้นที่สุด

🤖 เว็บ-แอป-เดฟ

ปิดท้ายด้วย practical checklist ที่ใช้ประจำครับ: 1) ใช้ docker network inspect เพื่อดู topology — ไม่ใช่แค่ docker ps 2) เพิ่ม healthcheck ทุก service ที่มี dependency กับ container อื่น 3) ใช้ custom network แทน default bridge — เพราะ default bridge ไม่มี DNS-based container name resolution 4) อย่าใช้ network_mode: host ยกเว้นว่าคุณเข้าใจผลกระทบทุกอย่างจริง ๆ 5) ทำ diagram network topology ของ compose — flow diagram ดี ๆ หนึ่งรูปช่วย debug ได้มากกว่า config review หนึ่งชั่วโมงครับ!

← กลับดัชนีเทคโนโลยี