Bài 1: cẩm nang chuyển bến từ Docker sang orchestration
Mục lục

Bước ngoặt từ chiếc hộp Docker lên một hệ thống thực thụ #
Câu chuyện dở khóc dở cười về đường truyền và mấy cái port oái oăm ở phần mở đầu để qua một bên. Hôm nay đào sâu vào nhân vật chính lặng lẽ gánh cả hệ thống: K3s.
Nếu công việc thường ngày của bạn là SSH vào VPS rồi chạy vài container Docker, mọi thứ trông trơn tru. Đẩy app lên 3 hay 4 server, ảo tưởng vỡ ngay: Docker thuần không đủ. Đứt cáp, sập nguồn, hay đơn giản là một máy lăn ra chết — ai sẽ tự động dời ứng dụng của bạn sang máy khác đang khỏe?
Đó là việc của orchestration, tức là lớp phần mềm đứng trên các container và tự lo chuyện: chạy cái gì ở đâu, chạy mấy bản, cái nào chết thì dựng lại, và cập nhật phiên bản mới mà không làm gián đoạn dịch vụ. Kubernetes là chuẩn mực của lớp này.
Nhưng cài Kubernetes bản đầy đủ thì vã mồ hôi vì nó quá cồng kềnh. May thay, có K3s. Bài này hệ thống lại toàn bộ các khái niệm cốt lõi để bạn tự tin bước vào.
1. Tại sao lại là K3s, và Docker đã đi đâu #
Nói ngắn gọn: K3s là một bản phân phối Kubernetes nhẹ ký, ép lại thành một file nhị phân duy nhất chưa tới 100MB. Nó cắt bỏ các driver dành cho những nhà cung cấp cloud lớn, và đổi cơ sở dữ liệu mặc định etcd lấy SQLite.
Chỗ này đáng giải thích thêm một chút, vì nó là lý do chính khiến K3s nhẹ đến vậy. Kubernetes cần một nơi lưu trạng thái của toàn cụm — đang có những gì, chạy ở đâu, cấu hình ra sao. Bản gốc dùng etcd, một cơ sở dữ liệu phân tán được thiết kế cho hàng chục máy đồng bộ với nhau; nó chắc chắn nhưng ngốn RAM và cần nhiều tiến trình chạy nền. SQLite thì chỉ là một file trên đĩa. Với một cụm vài node, đổi như vậy tiết kiệm được rất nhiều mà gần như không mất gì.
Kết quả là K3s hợp cho homelab cá nhân, cho thiết bị đặt ngoài hiện trường, hay bất kỳ kiến trúc phân tán nào cần gọn mà không đánh đổi phần lõi của Kubernetes.
Và có một sự thật khiến nhiều người mới chuyển từ Docker sang hơi sốc: K3s (và Kubernetes hiện nay) không dùng Docker nữa.
- Docker giờ chỉ nên làm nhiệm vụ build image ở máy cá nhân của bạn.
- Dưới hạ tầng K3s, thứ thực sự chạy các container là containerd — chính là phần lõi chạy container mà Docker vẫn dùng bên dưới, chỉ là gọi thẳng nó chứ không đi qua Docker nữa.
- Bạn sẽ không gõ lệnh khởi tạo container nữa. Thay vào đó bạn khai báo trạng thái mong muốn bằng file YAML, và K3s tự lo phần gọi containerd kéo image về rồi chạy. Đây là khác biệt tư duy lớn nhất: bạn mô tả cái đích, không mô tả các bước.
2. Điểm mặt các resource: đừng để bị ngợp #
Cho dễ nuốt, mình chia các khái niệm trong K3s thành hai nhóm: nhóm phải viết file cấu hình hằng ngày, và nhóm hệ thống tự lo mà chỉ cần hiểu logic là đủ.
Nhóm 1: động tay hằng ngày
- Pod: thành phần nhỏ nhất. Đừng nhầm một Pod với một container — một Pod có thể chứa nhiều container chạy chung, dùng chung một không gian mạng. Nghĩa là container A gọi container B trong cùng Pod qua
localhost, đúng như hai tiến trình trên cùng một máy. - Deployment: người quản lý vòng đời. Bạn khai “tôi cần 3 bản sao của app này”, Deployment đảm bảo luôn có đủ 3 Pod chạy ngày đêm, và lo luôn chuyện cập nhật cuốn chiếu — thay từng Pod một sang phiên bản mới, chờ Pod mới khỏe rồi mới tắt Pod cũ, nên người dùng không thấy gián đoạn.
- Service: bộ định tuyến nội bộ. Pod có tuổi thọ ngắn và IP của chúng đổi liên tục mỗi lần bị dựng lại. Service cấp một IP cố định cùng một cái tên trong cụm, rồi tự chuyển tiếp tới các Pod đang sống phía sau — nên các thành phần khác chỉ cần nhớ cái tên đó.
- Ingress: cánh cổng vòng ngoài, làm việc ở tầng ứng dụng — nghĩa là nó đọc được nội dung HTTP, biết request đang hỏi tên miền nào và đường dẫn nào, để chuyển đúng chỗ. K3s mặc định dùng Traefik cho phần này.
- Gateway API: thế hệ tiêu chuẩn định tuyến mới, sinh ra để sửa các điểm yếu của Ingress — cụ thể là tách bạch được phần cấu hình của người quản trị hạ tầng và phần cấu hình của từng đội ứng dụng, thay vì nhồi tất cả vào một chỗ. Series này sẽ dùng Envoy Gateway thay cho Traefik.
- ConfigMap và Secret: tách cấu hình và mật khẩu ra khỏi image, để đổi mật khẩu không phải build lại image. Pod đọc chúng lúc khởi động.
- PersistentVolumeClaim: đơn xin chỗ lưu trữ lâu dài. Mặc định, mọi thứ Pod ghi ra đều bốc hơi khi Pod chết — cái này giữ dữ liệu lại.
Nhóm 2: hạ tầng, hiểu là đủ
- ReplicaSet: đứng giữa Deployment và Pod, giữ đúng số lượng bản sao. Bạn hiếm khi phải viết nó trực tiếp.
- LoadBalancer và ServiceLB: phần mở cổng giao tiếp ở tầng mạng — chỉ nhìn IP và port, không quan tâm nội dung bên trong. Trên cloud, nhà cung cấp có sẵn dịch vụ cân bằng tải; trên server vật lý thì không, nên K3s tích hợp sẵn ServiceLB (Klipper) để chữa cháy.
- MetalLB: giải pháp cân bằng tải thực thụ cho server vật lý. Để kiểm soát IP tốt hơn, mình sẽ tắt ServiceLB mặc định và thay bằng MetalLB. Nó cho cụm K3s khả năng tự cấp phát IP thật trong mạng nội bộ, y như cơ chế của các nhà cung cấp cloud lớn.
- Control plane và worker node: máy master (control plane) là bộ não ra quyết định; máy agent (worker) là tay chân chạy Pod.
3. Luồng đi của một request: xâu chuỗi mọi thứ #
Thử theo dấu một request từ trình duyệt đi vào hệ thống. Điểm ăn tiền của thiết kế này là cách gói tin lặn qua hầm Tailscale ở bước cuối.
- Khách duyệt web, traffic đi qua Cloudflare rồi đập thẳng vào public IP của máy master ngoài cloud.
- Tại master, MetalLB tiếp nhận gói tin ở tầng mạng và chuyển giao cho Envoy Gateway.
- Envoy đọc phần header ghi tên miền trong request, thấy khách muốn vào
vinhmdev.com, và chuyển tiếp xuống đúng Service phụ trách. - Đây là lúc phần thú vị xảy ra. Service phát hiện các Pod đang nằm trên một máy worker tận trong nhà. Gói tin được mã hóa và chui qua hầm Tailscale — có DERP server đỡ lưng nếu cấu hình mạng chặn quá ngặt — để nhảy cóc từ VPS về thẳng máy ở nhà và được xử lý ở đó.
Nhìn từ phía người dùng thì không có gì khác biệt: họ gọi một tên miền và nhận về một trang web. Toàn bộ đoạn đường vòng qua nhà riêng nằm ẩn dưới lớp mạng.
4. Mạng lưới kết nối: Tailscale và DERP server #
Trong kiến trúc hybrid cloud này, các server không nằm chung một chỗ. Để chúng “nhìn thấy” nhau an toàn mà không phải mở port bừa ra internet, mình đưa Tailscale vào cuộc.
- Tailscale tạo ra một mạng riêng ảo phẳng: mọi node dù ở Sài Gòn hay Singapore đều liên lạc được như đang cắm chung một switch. Phần khó nhất mà nó lo giúp là xuyên qua NAT — cơ chế khiến máy trong mạng nhà bạn gọi ra ngoài được nhưng bên ngoài không gọi ngược vào được.
- DERP server là máy chủ tiếp sức của Tailscale. Khi hai node không bắt tay trực tiếp được với nhau (thường vì NAT quá chặt), lưu lượng phải đi vòng qua một máy trung gian. Mặc định đó là máy của Tailscale đặt đâu đó trên thế giới; tự dựng DERP riêng nghĩa là đoạn đi vòng đó nằm trên hạ tầng của mình, gần hơn và nhanh hơn.
5. Chuẩn bị đồ nghề cho chặng tiếp theo #
Bài tới sẽ bắt tay cài đặt. Ngoài các server chạy Ubuntu LTS sạch sẽ và SSH key, cần lưu ý:
- Tắt sẵn thành phần mặc định. Lúc cài K3s, mình sẽ thêm tham số vô hiệu hóa Traefik và ServiceLB ngay từ đầu, nhường chỗ cho Envoy Gateway và MetalLB.
- Tắt swap. Chạy
sudo swapoff -a. Lý do: Kubernetes tính toán tài nguyên dựa trên giả định RAM là RAM. Có swap thì một Pod hết RAM sẽ âm thầm chạy chậm như rùa thay vì bị kill dứt khoát, và bộ điều phối mất khả năng đánh giá đúng máy nào còn chỗ. - Kỹ năng editor. Hãy sẵn sàng với
vihoặcnano. SSH vào server và múa trên file YAML bằng vi không chỉ giúp bạn sinh tồn trong môi trường không có giao diện, mà còn là một trải nghiệm rất thực chiến và khá cuốn (mặc dù chủ yếu là copy và paste).
[!NOTE]
Điều gì xảy ra nếu máy master bốc cháy?
Về bản chất Kubernetes: mất master là mất quyền điều khiển bộ não — chẳng hạn không tạo thêm Pod mới được nữa. Nhưng những Pod đã được xếp lịch chạy trên các worker thì không chết. Chúng vẫn bám máy và chạy bình thường.
Tuy nhiên, sự thật phũ phàng về mặt mạng trong kiến trúc này: mình đang trỏ Cloudflare thẳng tới public IP của máy master, vì không có bộ cân bằng tải độc lập nằm ngoài. Nghĩa là master kiêm luôn vai cửa ngõ đón traffic. Master tắt thở thì cổng sập, người dùng từ internet không vào được, dù ứng dụng ở các worker vẫn sống nhăn răng — chỉ là không còn đường ra. Đây đúng nghĩa là một điểm chết duy nhất. Về sau muốn hóa giải thì phải đắp thêm một bộ cân bằng tải bên ngoài, hoặc tự động chuyển IP sang máy khác khi master chết.
[!WARNING]
Lưu ý xương máu: KHÔNG cài chung Docker và K3s trên cùng một máy master.
Hai hệ sinh thái này sẽ đá nhau: tranh cổng mạng, và cùng viết vào bảng định tuyến lẫn iptables của hệ thống — mỗi bên tự thêm luật cho mạng container của mình mà không biết bên kia tồn tại, nên gói tin đi lạc theo những cách rất khó lần. Gỡ mớ bòng bong này cực kỳ mệt và tốn thời gian vô ích.
Ở tầng dưới, K3s vốn đã chạy containerd siêu nhẹ rồi, nên Docker không mang lại gì thêm. Tốt nhất chuẩn bị một server thật sạch (gỡ Docker) trước khi cài.
Hẹn gặp lại ở Bài 2: thiết lập VPN với Tailscale.