มาใช้ Traefik ร่วมกับ Docker กันเถอะ

Thanayut Tiratatri
Thinc.
Published in
5 min readJun 17, 2022

ถ้าจะให้ผมแนะนำ reverse proxy ที่สามารถทำงานรวมกับ Docker ได้ดีเยี่ยมแล้วละก็ Traefik เป็นหนึ่งในตัวเลือกที่ดีมาก ๆ ของ reverse proxy ยุคนี้เลย

หนึ่งในจุดแข็ง Traefik คือการ integrate เข้ากับ provider ต่าง ๆ เช่น Docker หรือ Kubernetes แล้วทำ service discovery โดยอัตโนมัติ

สำหรับใครที่เคยใช้ reverse proxy เช่น nginx มาบ้างแล้วลองจินตนาการดูว่าถ้าเราอยากจะเพิ่ม service ใหม่ ๆ เราต้องทำอะไรบ้าง? — คำตอบง่าย ๆ แต่ดูเหนื่อยเลยคือต้องตามแก้ config file + restart nginx แล้วยึ่งถ้าต้องใช้ร่วมกับ service ที่รันอยู่ใน docker network แล้วก็ยิ่งมีเรื่องให้คิดเพิ่มไปอีก (นี่ยังไม่รวมเรื่อง TLS/SSL certificate หรือการ monitor ภาพรวมของระบบเลยนะ!)

Traefik เลยเป็นหนึ่งในตัวเลือกที่ดีมาก ๆ สำหรับการทำ reverse proxy ร่วมกับ service ที่รันอยู่ใน Docker (จริง ๆ ไม่เฉพาะ Docker นะ ลองดู provider ที่เขารองรับได้ที่ docs ของเขาเลย)

เอาละ เกริ่นพอละ เริ่มกันจริง ๆ เลยดีกว่า บทความนี้จะสอน setup Traefik พร้อม TLS/SSL certificate เพื่อทำ HTTPS แบบ step-by-step บน Docker จากนั้นจะโชว์วิธี deploy service บน container ผ่าน docker-compose file เบื้องต้นนะ

1. Concept

ก่อนอื่นเลยไม่พูดไม่ได้คือเรื่องของ concept

Traefik เขาบอกว่าเขาเป็น Edge Router ซึ่งก็ตรงตัวเลยคือเป็นซอฟต์แวร์ที่ช่วยจัดการให้ user ที่อยู่ที่ external network สามารถ connect เข้าไปยัง service ที่อยู่ใน internal network ของเราได้อย่างราบรื่น

เอาละ ก่อนที่จะเริ่มใช้งานจริง ๆ เรามาดู 5 คำศัพท์ที่ควรรู้กันก่อนดีกว่า

  1. EntryPoint — พูดง่าย ๆ คือประตูที่จะรับ request เข้ามา ตัวอย่างที่ชัดเจนเลยคือ port เช่น 80 (HTTP) หรือ 443 (HTTPS)
  2. Router — สั้น ๆ คือคนนำทางที่จะพา request ที่รับมาจากข้อ 1 ไปหา service ปลายทางได้ตาม rules ต่าง ๆ ที่เราตั้งไว้
  3. Middleware — บางทีก่อนที่ request จะไปถึง service ปลายทาง เราอาจจะอยากแก้ไขอะไรเช่นทำ authentication หรือ rate limit ก่อน — middleware จะเข้ามาช่วยในส่วนนี้ (ไม่มีก็ได้นะ)
  4. Service — จุดหมายปลายทางนั่นเอง แต่หนึ่ง service ไม่จำเป็นจะต้องเป็นจุดเดียวกันเสมอไปนะ service สามารถทำงานเป็น load balance ให้ server หรือ container หลายตัวได้เช่นกัน
  5. Provider — เอ่ แล้วเมื่อกี้ router รู้ได้ไงนะว่า service อยู่ที่ไหน? — คำตอบคือ provider ที่จะทำหน้าที่ค้นหา service นั่นเอง เอาจริง ๆ ตรงนี้แหละคือจุดแข็งของ Traefik —Traefik รองรับ provider ที่หลากหลายเช่น Docker, Kubernetes หรือเราจะกำหนดเองผ่าน file provider ก็ได้ถ้าเราไม่ต้องการ auto service discovery ของเขา

ถึงจุดนี้สังเกตดี ๆ ข้อ 1 ถึง 5 คือ flow ของ request ที่เรารับเข้ามานั่นเอง เวลาเรา config เราก็จะคำนึงถึง flow ตรงนี้แหละ

Architecture Overview ของ Traefik จากเว็บไซต์ทางการของเขาเลย

2. Configuration

แล้วที่ว่ามา Traefik รู้ได้ไงนะว่าต้องทำงานอะไรบ้าง? — แน่นอนละว่าต้องมีใครซักคนกำหนดค่าซึ่งเราจะมาดูกันในส่วนนี้นั่นเอง

Traefik แบ่ง configuration ออกเป็น 2 ส่วนคือ static configuration กับ dynamic configuration โดย…

  • Static configutation คือค่าเริ่มต้นที่ Traefik ใช้ตอน start ตัวมันเองขึ้นมา ส่วนนี้จะเปลี่ยนไม่บ่อย ส่วนมากจะบอกแค่ว่าจะรับ request เขามาทาง EntryPoint ไหนกับจะใช้ Provider อะไรบ้าง (จำได้ไหม EntryPoint กับ Provider คืออะไร?)
  • Dynamic Configuration — ชื่อตามตัวเลยคือ configuration ที่มาสามารถเปลียนได้เสมอ — Config ส่วนนี้จะกำหนด Traefik ว่าต้องทำยังไงกับ request ที่รับมา ต้องผ่าน middleware อะไรบ้างและจะต้องพาไปหา service ตัวไหน

ขอขายอีกรอบ — ความ dynamic คือจุดเด่นของ Traefik เลยก็ว่าได้ หลังจากที่เรา config provider ที่ต้องการใช้งานใน static configuration แล้ว Traefik จะออกหา dynamic configuration เองเลย

พูดให้เห็นภาพคือเราสามารถ start Docker container พร้อม assign label ให้ Traefik ทำการสร้าง router กับ service ได้โดยอัตโนมัติโดยไม่ต้องไปแตะ Traefik เลยแม้แต่น้อย!

3. Getting started

เริ่มกันจริง ๆ ละ

3.1 Installation

ขั้นตอนแรกจะเป็นอะไรไปไม่ได้เลยนอกจากการติดตั้ง ขั้นตอนนี้เราจะโหลด binary distribution มาลงเองเหมือน package อื่น ๆ ก็ได้ จะใช้ Helm Chart (ถ้าเคยใช้) ก็ได้ แต่ข่าวดีสำหรับคนที่คุ้นเคยดีอยู่แล้วกับ Docker คือเราสามารถรัน Traefik เป็น container ได้ผ่าน Traefik official image บน Docker Hub

ในที่นี้จะขอโชว์วิธีที่สะดวกที่สุดคือผ่าน Docker ก็ละกันนะ

แต่เดี้ยวก่อน จำ static configuration ได้ไหม? Traefik ต้องการ config นี้ตอน start up ซึ่งเราต้องจัดการในส่วนนี้ก่อน

3.2 สร้าง static configuration

เรามี 3 ทางเลือกในการกำหนด static configuration นั่นคือ…

  1. Command-line arguments —เป็นหนึ่งในทางเลือก แต่ลองคิดตอนเรามี config เยอะ ๆ เข้าสิ… คงไม่เหมาะเท่าไร
  2. Environment variables —จะใช้วิธีนี้ก็ได้เหมือนกัน แต่พอมี config เยอะ ๆ ก็ยังดูยุ่งยากอยู่ดี
  3. Configuration file — อันนี้ดูเข้าท่าสุด เราจะมาใช้วิธีนี้กัน

(อ้อ static configuration มัน mutually exclusive นั่นแปลว่าเราเลือกได้ทางเดียวนะ)

Traefik รองรับ configuration file อยู่ 2 รูปแบบคือ YAML กับ TOML — แบบแรกคนที่ใช้ Docker compose น่าจะคุ้นเคยดีอยู่แล้ว ส่วนแบบที่สองให้นึกภาพง่าย ๆ ว่าคล้าย ๆ config.ini บน Windows

สำหรับบทความนี้ผมขอใช้ TOML ละกันนะ คิดว่าดูง่ายกว่า YAML พอสมควร

เอาละ ลงมือจริง ๆ ละ เราต้อง config อะไรบ้างนะจำได้ไหม? ใช่แล้ว EntryPoint กับ Provider ไง

3.2.1 กำหนด EntryPoint

สำหรับเว็บไซต์ร้อยละร้อยเราคงรับ request ผ่าน port 80 ซึ่งเราสามารถ config ได้ตามนี้เลย

คำว่า entryPoints จะเป็นสิ่งที่ Traefik มองหา ส่วน web นี่เป็นชื่อของ EntryPoint ที่เราสามารถตั้งได้เอง จะชื่ออะไรก็ได้นะตามใจเลย

สำหรับใครที่ทำตามไปด้วยให้เซฟไฟล์ชื่อ traefik.toml นะ เดี้ยวจะใช้ต่อในตอนหลัง

แล้ว HTTPS ล่ะ? เหมือนกันเลย เราสามารถเพิ่ม port 443 ได้แบบเดียวกับ port 80 ในที่นี้ขอตั้งชื่อว่า websecure และเซท redirect port 80 ไป 443 ตามตัวอย่างพร้อมกันเลยนะ

3.2.1 กำหนด Provider

ทีนี้มาถึง Provider แล้ว เราสามารถดู Provider ที่ Traefik รองรับได้ที่นี่

วิธีการ enable provider แต่ละตัวก็ง่ายมาก ๆ สำหรับ Docker ในกรณีทีเรารันบนเครื่องเดียวกับ Traefik เราสามารถใส่ [providers.docker] เพียงบรรทัดเดียวต่อท้าย static configuration ได้เลย

โดย default แล้ว Traefik จะทำการ monitor container ทีรันอยู่และสร้าง route ไปหา service โดยอัตโนมัติ แต่บางทีเราก็มี service ที่ไม่ต้องการ expose โดยอัตโนมัติใช่ไหม? ถ้าเป็นเช่นนั้นเราสามารถตั้ง exposedByDefault ของ Docker Provider เป็น false ได้เลย

มาถึงจุดนี้เราจะได้ static configuration พร้อมใช้แบบนี้

ทีนี้เราก็สามารถ start ตัว Traefik ขึ้นมาได้แล้ว เราสามารถรัน Traefik ผ่าน command docker run แล้วทำการ mount ไฟล์ traefik.toml ไปหา container ได้เหมือน container ทั่วไป หรือเราจะใช้ docker-compose.yaml ซึ่งสะดวกกว่าก็ได้ ในที่นี้ขอเลือกวิธีหลังนะ

3.3 รัน Traefik ผ่าน docker-compose

ขั้นตอนนี้ไม่มีอะไรมาก สร้างไฟล์ docker-compose.yaml เก็บไว้ที่ไหนซักที จากนั้นสั่ง docker compose up ผ่าน terminal ได้เลย (หรือ docker-compose up แบบมีขีดตรงกลางถ้าลง Docker ไว้นานแล้ว)

ไฟล์ docker-compose ด้านบนก็ไม่มีอะไรมาก เราทำการเซท image เป็น traefik:v2.7 ที่ถ้าใครยังไม่มีในเครื่องก็จะโหลดมาจาก Docker Hub, ทำการ expose port 80 กับ 443 ตามที่เราตั้งไว้ใน static configuration แล้วก็ทำการ mount ไฟล์ traefik.toml ของขั้นตอนที่แล้ว

ถ้าทุกอย่างถูกต้อง Traefik ก็จะพร้อมใช้งานแล้ว! แต่เดี้ยวก่อน เรายังไม่ได้เซท TLS/SSL Certificate และยังขาด router กับ service นะจำได้ไหม?

3.4 ตั้งค่า TLS/SSL Certificate

เว็บยุคนี้จะขาด TLS/SSL ไปไม่ได้

สำหรับใครที่มี certificate ที่ขอออกจาก CA มาอยู่แล้วสามารถตั้งค่าตามนี้ได้เลย

แต่สำหรับใครที่ยังไม่มี เราสามารถ integrate Traefik ของเราเข้ากับ Let’s Encrypt เพื่อขอออก certificate ฟรีโดยที่ Traefik จะจัดการทุกอย่างให้ทั้งการทำ challenge ครั้งแรกและการต่ออายุ

วิธี config สามารถดูได้ที่หน้านี้เลย โดยสรุปคือเราแค่เพิ่มส่วนนี้เข้าไปใน static configuration

ตรงบรรทัดที่ 2 อย่าลืมเปลี่ยน your-email@example.comเป็นอีเมลที่ใช้งานได้จริงนะ เวลา certificate ใกล้หมดอายุจะได้มี Let’s Encrypt Expiry Bot ส่งอีเมลแจ้งเตือน (แต่โดยปกติ Traefik จะทำการต่ออายุให้ก่อนหมดโดยอัตโนมัติอยู่แล้วอะนะ)

ส่วนบรรทัดที่ 3 ด้วยความที่ Traefik ต้องมีการเก็บ private key และ certificate เราจึงต้องระบุสถานที่เก็บให้มันด้วย โดยปกติจะชื่อ acme.json แน่แหละ

สำคัญมาก: Traefik กำหนดให้ acme.json ต้องถูก chmod เป็น 600 เท่านั้นเพื่อความปลอดภัย (ไม่งั้น Traefik จะไม่ขอออก certificate ให้) ดังนั้นหลัง config เสร็จให้เราสร้างไฟล์ acme.jsonแล้วสั่ง chmod 600 acme.json (ถ้าไม่มี permission ก็ลองเพิ่ม sudo นำหน้าดูเหมือนปกติเลย)

ทีนี้เราก็กลับมาแก้ docker-compose.yaml เพื่อ mount ไฟล์ acme.json ต่อ โดยสรุปแล้วเราจะได้ 2 ไฟล์นี้พร้อม acme.json ที่มี permission 600

จากนั้นสั่ง docker compose up อีกครั้งแล้วมาเริ่ม deploy service กัน

3.5 ถึงเวลา deploy service

วิธี deploy service ก็ไม่มีอะไรมาก อันนี้แหละคือจุดเด่นที่ว่ามา เราแค่ deploy container แบบที่เคยทำ ๆ กันมา (ผ่าน docker run หรือ Docker compose file) แล้ว assign label ที่ Traefik รองรับก็เรียบร้อย

สำหรับใครที่รู้อยู่แล้วว่าจะ deploy อะไรก็ลงมือได้เลย แต่สำหรับบทความนี้ผมขอใช้ image nginxdemos/hello เป็นตัวอย่าง

ในที่นี้เราเขียนเพิ่มเข้าไปใน docker-compose.yaml ไฟล์เดิมเลยก็ได้ ข้อดีของวิธีนี้คือ Docker จะทำการสร้าง network ที่ทุก container ในไฟล์เดียวกันสามารถติดต่อหากันได้โดยอัตโนมัติ (สำหรับใครที่แยกไฟล์กันก็สามารถระบุ networks ไปใน Docker compose file ได้เหมือนปกติเลย)

ทีนี้สังเกตตรง labels ส่วนนี้แหละจะเป็น dynamic configuration ที่ Traefik จะออกตามหา อธิบายโดยสรุปแล้วตามนี้…

  • traefik.enable=true — บอก Traefik ว่าเราตั้งใจที่จะใช้ Traefik กับ container นี้ (จำที่เราเซท exposedByDefault เป็น false ใน static configuration ได้ไหม? นี่แหละคือเหตุที่ต้องมี label นี้)
  • traefik.http.routers.demoweb.rule=Host(`demoweb.example.com`) — บอก Traefik ให้ทำการสร้าง router ชื่อ demoweb ขึ้นมาโดยมี rule อยู่ว่าให้ forward request ที่ได้รับจาก demoweb.example.com มาหา container นี้ (ดู rule เพิ่มเติมได้ที่นี่ เราสามารถเซท rule ได้หลายแบบ เช่น จะเซทให้ 2 container เข้าถึงผ่าน domain เดียวกันแต่แยกกันโดย PathPrefix เช่น /api ก็ได้)
  • traefik.http.routers.demoweb.tls=true กับ traefik.http.routers.demoweb.tls.certresolver=letsencrypt — บอก Traefik ว่าต้องการเปิดใช้ TLS/SSL Certificate กับ router นี้โดยที่ให้ใช้ resolver ชื่อ letsencryptที่ได้ตั้งไปตอน 3.4 เป็นตัวจัดการ

หลังจากนั้นอย่าลืมชี้ A, AAAA, หรือ CNAME record ที่ DNS Server ไปหาเครื่องที่รัน Traefik อยู่ จากนั้นสั่ง docker compose up แล้วรอชมผลงานได้เลย Traefik จะสร้าง router กับ service ให้โดยอัตโมมัติ (ดูรายละเอียดเพิ่มเติมที่นี่)

4. ทดลองใช้งาน

ถ้าเราลองเข้าเว็บตาม domain ที่เราตั้งไว้ดูเราจะพบกับหน้าคล้าย ๆ แบบนี้ (หรือถ้าใครใช้ image ของตัวเองก็จะได้ตามนั้น)

และเราก็จะพบว่า TLS/SSL certificate ถูกตั้งค่าเรียบร้อย — Traefik ได้ทำการขอออก cert ตามชื่อ domain ที่เราตั้งใน label โดยอัตโนมัติ เราสามารถเช็ค acme.json ดูได้เลย

ถ้าเราต้องการเพิ่ม container ใหม่ ๆ เราก็เพียงทำตาม 3.5 อีกครั้งและเพียงในไม่กี่นาที Traefik จะทำการ inspect infrastructure ของเราและทุกอย่างก็จะพร้อมใช้งานโดยอัตโนมัติ

เท่านี้ก็เป็นอันเสร็จสิ้น

5. สรุป

เอาละครับ จบแล้ว!

ใครอ่านถึงตรงนี้ก็คงได้ภาพรวมไปแล้วว่า Traefik คืออะไรและจะเริ่มใช้งานได้ยังไง

ใครที่ทำตามไปด้วยก็คงจะได้เห็นด้วยตัวเองแล้วว่า Traefik เป็น Edge Router ที่เริ่มใช้งานได้ง่ายมาก ๆ และการเพิ่ม service ใหม่ ๆ เข้าไปในระบบก็ไม่ได้ยุ่งยากอะไรไปมากกว่าการ assign label ให้ container เลย

มาถึงจุดนี้ สำหรับใครที่พบข้อผิดพลาดอะไรตรงไหนในบทความนี้หรืออยากจะแนะนำอะไรเพิ่มเติมสามารถแจ้งมาได้เลยครับ

--

--

Thanayut Tiratatri
Thinc.
Writer for

I’m a second-year Information and Communication Engineering student at Chulalongkorn University, Thailand.