สอนใช้ Git ตอนที่ 4: Self-Hosted CI/CD with Gitea

ถ้าคุณอยากเป็นเจ้าของ Git server และ pipeline ของตัวเอง Gitea เป็นจุดเริ่มต้นที่น่าสนใจ ตอนนี้จะพาไปรู้จัก Gitea, act_runner, workflow YAML และข้อควรระวังเวลาเริ่ม self-hosted CI/CD

สอนใช้ Git ตอนที่ 4: Self-Hosted CI/CD with Gitea

บทความนี้เป็นตอนที่ 4 และตอนสุดท้ายของซีรีส์สอนใช้ Git ที่มีทั้งหมด 4 ตอน โดยจะพาจากเรื่อง workflow ในตอนก่อนหน้าไปสู่การเป็นเจ้าของ Git platform และ pipeline ของทีมด้วย Gitea และ self-hosted CI/CD

พอทีมของ Maya ใช้ Git และ workflow ได้คล่องขึ้น ปัญหาใหม่ก็ไม่ใช่เรื่อง branch อีกต่อไป แต่เป็นคำถามระดับระบบมากกว่า

“เราจำเป็นต้องฝากทุกอย่างไว้บน platform ของคนอื่นเสมอไปหรือไม่”

คำถามนี้ไม่ได้มีคำตอบเดียว เพราะ GitHub, GitLab และบริการแบบ hosted ต่างก็สะดวกมาก แต่พอทีมเริ่มโตขึ้น หรืองานเริ่มแตะเรื่องต้นทุน ความเป็นส่วนตัว และการควบคุม infrastructure หลายทีมก็เริ่มสนใจ self-hosted ทางเลือกอย่าง Gitea มากขึ้น

ตอนนี้เราจะปิดซีรีส์ด้วยภาพใหญ่ของ self-hosted Git + CI/CD โดยใช้ Gitea เป็นตัวอย่างหลัก

ทำไมบางทีมถึงอยาก self-host

เหตุผลหลักมักวนอยู่รอบสามเรื่อง:

  1. cost หรือค่าใช้จ่าย
  2. control หรือการควบคุมระบบเอง
  3. privacy และ data ownership

ทีมของ Maya เริ่มรู้สึกเรื่องนี้เมื่อมีทั้ง code, wiki, internal docs และ automation หลายอย่างรันอยู่พร้อมกัน ค่าใช้จ่ายเริ่มมีน้ำหนัก และทีมอยากรู้สึกว่าถ้าจะเปลี่ยนอะไรใน pipeline ก็เปลี่ยนได้เองมากขึ้น ไม่ต้องรอ ecosystem ของคนอื่นเสมอไป

แน่นอนว่าการ self-host ไม่ได้ฟรีจริง เพราะคุณย้ายภาระจากค่าบริการ ไปเป็นภาระในการดูแลระบบแทน แต่สำหรับบางทีม trade-off นี้คุ้มค่า

ดังนั้นถ้าจะเริ่ม self-host แบบมีเหตุผล คำถามไม่ใช่แค่ “ทำได้ไหม” แต่คือ “ทีมเราพร้อมดูแลของชิ้นนี้แค่ไหน” เพราะเมื่อ Git server กับ pipeline กลายเป็นของคุณเอง มันก็กลายเป็นระบบ production ของคุณด้วย

คำถามต่อเนื่องที่ควรถามคือ ถ้าระบบนี้ล่ม ใครจะดู ถ้าต้องอัปเดตเวอร์ชัน ใครจะทดสอบ ถ้า runner ตายกลางคืน ใครจะรู้ก่อน สิ่งเหล่านี้ฟังดู operational มาก แต่จริง ๆ คือหัวใจของ self-hosted mindset

Gitea คืออะไรใน 60 วินาที

Gitea คือ lightweight Git server แบบ self-hosted ที่ให้ประสบการณ์ใกล้เคียงกับ Git platform ที่คุ้นเคย เช่น มี repository, issue, pull request, wiki และตั้งแต่ Gitea 1.19 เป็นต้นมา ก็มี Gitea Actions เป็น built-in CI/CD solution ด้วย

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

อีกจุดที่คนจำนวนมากชอบคือหน้าตาและ mental model ของมันค่อนข้างคุ้น ถ้าทีมเคยใช้ GitHub มาก่อน การย้ายมาคิดในโลกของ Gitea จะไม่ใช่การเริ่มจากศูนย์ทั้งหมด

นี่ทำให้ Gitea น่าสนใจสำหรับทีมที่อยากลด dependency กับ vendor เดิม แต่ไม่อยากโยนประสบการณ์ใช้งานที่ทีมคุ้นอยู่ทิ้งไปทั้งหมด

เริ่มต้นติดตั้ง Gitea แบบ practical

ถ้าจะเริ่มต้นเร็ว วิธีที่นิยมคือใช้ Docker Compose

ตัวอย่างขั้นต่ำแบบ conceptual จะหน้าตาประมาณนี้:

services:
  gitea:
    image: gitea/gitea:latest
    ports:
      - "3000:3000"
      - "2222:22"
    volumes:
      - ./gitea:/data

เมื่อเริ่มต้นครั้งแรก คุณจะต้องตั้งค่าพื้นฐานใน app.ini หรือผ่านหน้า setup เช่น

  • domain
  • HTTP port
  • SSH port
  • database
  • base URL

ผมตั้งใจพูดแค่ระดับภาพรวมตรงนี้ เพราะจุดสำคัญของตอนนี้ไม่ใช่ทุกบรรทัดของ config แต่คือการเข้าใจว่า self-hosted Git server หมายถึงคุณต้องรับผิดชอบทั้ง availability, backup, updates และ access control ของระบบนี้ด้วย

ถ้าคุณยังไม่พร้อมดูแลสิ่งเหล่านี้เอง การเริ่มจาก hosted platform ก่อนก็ไม่ใช่เรื่องผิด สิ่งสำคัญคือเลือก architecture ให้เหมาะกับทีมตอนนี้ ไม่ใช่เลือกเพราะคำว่า self-hosted ดูเท่กว่า

ผมมองว่าการ self-host ที่ดีไม่ใช่การย้ายทุกอย่างมาวันเดียว แต่คือการเริ่มจากสิ่งที่ทีมพร้อมดูแล เช่น เริ่มจาก Git server ก่อน แล้วค่อยเติม runner และ deploy automation เมื่อเห็นรูปแบบการใช้งานชัดขึ้น

Gitea Actions กับ GitHub Actions คล้ายกันแค่ไหน

Gitea Actions ตั้งใจให้เข้ากันได้กับ syntax แบบ GitHub Actions มากพอสมควร เพราะฉะนั้น YAML หลายส่วนจะคุ้นตาทันที

ตัวอย่าง workflow ขั้นต่ำใน .gitea/workflows/ci.yml:

on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run tests
        run: npm test

นี่เป็นข่าวดีสำหรับคนที่คุ้นกับ GitHub Actions อยู่แล้ว เพราะ mental model หลายส่วนใช้ต่อได้ แต่เอกสารของ Gitea ก็ชี้ชัดเหมือนกันว่ามีความต่างอยู่ และไม่ควร assume ว่าทุก action หรือทุก integration จะเหมือน GitHub แบบ 1:1

จุดนี้มีผลในทางปฏิบัติมาก เพราะหลายทีม copy YAML จากอินเทอร์เน็ตแล้วคาดหวังว่าทุกอย่างจะติดทันที ถ้า workflow ไม่รัน สิ่งแรกที่ควรตรวจไม่ใช่แค่ syntax แต่คือ environment ของ runner, labels, และ action ที่คุณเรียกใช้นั้นรองรับบน Gitea แบบไหน

พูดง่าย ๆ คือในโลก self-hosted “YAML ถูก” ยังไม่พอ คุณต้องมี runtime ที่ตรงกับ YAML นั้นด้วย และนี่คือสิ่งที่ทำให้ความเข้าใจเรื่อง runner สำคัญมาก

act_runner คืออะไร และทำงานอย่างไร

Gitea Actions จะรันงานได้จริงต้องมี runner หรือ executor มารับ job ไปทำ และในโลกของ Gitea ตัวนี้คือ act_runner

จากเอกสารของ Gitea act_runner สามารถรันได้หลายแบบ แต่โหมด Docker เป็นแนวทางที่แนะนำ เพราะช่วยแยกงานออกจาก host ได้ดีกว่า

สิ่งที่ต้องเข้าใจให้ชัดมีสามอย่าง:

  1. คุณต้องมี registration token เพื่อผูก runner เข้ากับ instance, organisation หรือ repository
  2. runner labels ใช้จับคู่ว่า job แบบไหนควรไปรันที่ไหน
  3. runs-on: ubuntu-latest ใน Gitea ไม่ได้แปลว่าคุณได้เครื่อง hosted แบบ GitHub แต่แปลว่ามี label บน runner ของคุณที่ map ไปยัง image หรือ environment นั้น

นี่คือ pitfall ที่หลายคนพลาด เพราะเห็น YAML คล้าย GitHub แล้วคิดว่าความหมายเหมือนกันทั้งหมด

ผมมองว่าถ้าจะสอนทีมเรื่อง Gitea Actions ประโยคหนึ่งที่ช่วยได้มากคือ “GitHub-hosted runner ไม่มีอยู่ที่นี่ คุณต้องนิยามโลกของตัวเอง” พอเข้าใจจุดนี้ การอ่าน logs และ debug pipeline จะง่ายขึ้นเยอะ

และเมื่อทีมเข้าใจตรงนี้แล้ว การออกแบบมาตรฐานภายในจะดีขึ้น เช่น ใช้ label อะไรเป็น default, งาน build ควรแยก runner จากงาน deploy หรือไม่, และงานประเภทไหนควรได้รับสิทธิ์เข้าถึง secrets บางชุด

เรื่อง labels สำคัญกว่าที่คิด

เอกสารของ Gitea อธิบายชัดว่า label สามารถ map ไปยัง container image ได้ เช่น ubuntu-22.04:docker://node:16-bullseye

ภาพที่ควรจำคือ:

  • workflow ขอ runs-on
  • runner ดูว่า label ไหนตรง
  • แล้วค่อยตัดสินใจว่าจะรันบน Docker image ไหนหรือบน host

เพราะฉะนั้นเวลาเขียน tutorial ให้ทีม ผมมักย้ำเสมอว่า runs-on ใน self-hosted world คือ “ชื่อสัญญา” ระหว่าง workflow กับ runner ของคุณเอง ไม่ใช่ชื่อบริการสำเร็จรูปจาก platform เจ้าของ cloud

และเมื่อคิดแบบนี้ได้ คุณจะเริ่มออกแบบ labels อย่างมีเจตนามากขึ้น เช่น แยกงานที่ต้องใช้ Docker, งานที่รันบน host, งานที่ใช้ image พิเศษ หรือ runner เฉพาะสำหรับ deploy production

สิ่งนี้ช่วยให้ pipeline ของคุณอ่านง่ายขึ้นด้วย เพราะ runs-on จะไม่ใช่แค่ค่าให้มันผ่าน แต่เป็นภาษากลางว่าทีมตั้งใจให้งานนี้ไปรันในสภาพแวดล้อมแบบไหน

secrets และความปลอดภัยของ pipeline

เมื่อคุณเป็นเจ้าของระบบเอง ความสบายใจเพิ่มขึ้น แต่ความรับผิดชอบก็เพิ่มตาม

Gitea มีระบบเก็บ Secrets ในระดับ repository, user หรือ organisation ซึ่งควรใช้แทนการ hard-code ค่า sensitive ลงใน YAML

อีกจุดที่ต้องระวังคือถ้าคุณรัน runner แบบ mount Docker socket เช่น /var/run/docker.sock เข้าไป คุณกำลังแลก convenience กับ risk และเอกสารของ Gitea ก็เตือนเรื่องนี้ค่อนข้างตรงไปตรงมา เพราะ job ที่เข้าถึง socket ได้มีศักยภาพจะมองเห็นข้อมูลที่ไม่ควรเห็นได้

นี่เป็นตัวอย่างชัดเจนของโลก self-hosted: คุณได้อิสระมากขึ้น แต่ก็ต้องรับผิดชอบต่อ security boundary มากขึ้นด้วย ไม่มี platform team ของ vendor มาคอยทำส่วนนี้แทนคุณ

เพราะฉะนั้นก่อนเปิดให้ทุก repo ใช้ runner เดียวกัน คุณควรถามก่อนว่า trust boundary อยู่ตรงไหน งานของทุกทีมควรอยู่บน runner ก้อนเดียวกันจริงหรือไม่ และ repo ไหนมีความเสี่ยงสูงกว่าปกติ

ตัวอย่าง pipeline ที่ practical

สำหรับทีมของ Maya pipeline ขั้นแรกไม่ต้องซับซ้อนมาก แค่เริ่มจาก

  1. push แล้ว lint
  2. run tests
  3. build
  4. deploy ถ้า merge เข้า main

แนวคิดสำคัญคืออย่าเริ่มจาก pipeline ที่อลังการเกินไป เริ่มจากสิ่งที่ตรวจคุณภาพได้จริงก่อน แล้วค่อยขยาย

และถ้าทีมใช้ Git กับ docs ด้วย Gitea Actions ก็ยังช่วยงานได้ เช่น

  • build static site จาก Markdown
  • generate PDF หรือ exported docs
  • publish knowledge base หลัง merge

จุดนี้น่าสนใจมาก เพราะมันทำให้ Git และ CI/CD ไม่ได้เป็นเรื่องของ code อย่างเดียวอีกต่อไป

นี่คือจุดที่ซีรีส์ทั้งสี่ตอนเริ่มมาบรรจบกันพอดี เพราะถ้าคุณใช้ Git กับโค้ด, เอกสาร, wiki และ release process อยู่แล้ว การมี pipeline ที่ผูกทุกอย่างเข้าด้วยกันจะทำให้คุณเห็นภาพทั้งระบบชัดขึ้นมาก ไม่ใช่แค่รู้คำสั่ง Git ทีละตัว

และตรงนี้เองที่หลายทีมเริ่มเห็นว่า Git ไม่ใช่แค่เครื่องมือของ developer แต่เป็นแกนกลางของการทำงานทั้งทีม ตั้งแต่การเขียนเอกสารไปจนถึงการปล่อยของขึ้นระบบจริง

สรุปสั้น ๆ ปิดทั้งซีรีส์

  • Gitea เป็นทางเลือก self-hosted ที่เบาและ practical สำหรับทีมที่อยากเป็นเจ้าของ Git platform ของตัวเอง
  • Gitea Actions ใช้ mental model ใกล้ GitHub Actions ได้ แต่ต้องเข้าใจความต่าง โดยเฉพาะเรื่อง runners และ labels
  • act_runner คือหัวใจของการ execute งาน CI/CD และควรตั้งอย่างระมัดระวังทั้งเรื่อง labels, tokens และ secrets

Maya เริ่มจากแค่ไม่อยากทำไฟล์หาย แต่สุดท้ายทีมของเธอค่อย ๆ เดินมาถึงจุดที่ไม่ใช่แค่ใช้ Git เป็น แต่เริ่มเป็นเจ้าของ workflow ของตัวเองด้วย

ถ้าคุณอ่านมาถึงตรงนี้ ครบทั้ง 4 ตอนแล้ว สิ่งที่คุณน่าจะได้ไม่ใช่แค่คำสั่ง Git เพิ่มขึ้น แต่คือ mental model ว่า Git ไม่ได้เป็นแค่เครื่องมือเก็บประวัติ มันคือฐานของการทำงานร่วมกัน การ release งาน และการวางระบบอัตโนมัติที่ทีมเชื่อถือได้

ถ้าจะทิ้งท้ายแบบ practical ที่สุด ผมอยากให้คุณเริ่มจาก pipeline เล็กแต่เชื่อถือได้ มากกว่าระบบใหญ่ที่ไม่มีใครแน่ใจว่าพึ่งพาได้จริง เพราะในโลก self-hosted ความสม่ำเสมอสำคัญพอ ๆ กับความสามารถ ทุก job ที่รันควรตอบคำถามให้ทีมได้ชัดว่า code พร้อมไปต่อหรือยัง เอกสาร build ผ่านไหม และ deployment ควรถูกปล่อยเมื่อไร

สุดท้าย self-hosted CI/CD ไม่ได้มีความหมายแค่ “เราเป็นเจ้าของ server” แต่มันหมายถึงเราต้องเป็นเจ้าของมาตรฐาน, ความปลอดภัย และความน่าเชื่อถือของ pipeline เองด้วย ถ้าคุณเข้าใจจุดนี้ Git, workflow และ automation จะเริ่มเชื่อมกันเป็นระบบเดียวอย่างชัดเจน

ตอนที่ 1 Track Everything, Fear Nothing
ตอนที่ 2 Branching Out, Collaborate Without Chaos
ตอนที่ 3 Workflows for Teams & Projects

แหล่งอ้างอิง