SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

اجرای k3s روی یک VPS: چه زمانی ارزشش را دارد؟

نصب k3s روی یک VPS با استفاده از دستور تک‌خطی امکان‌پذیر است. در این مطلب بررسی می‌کنیم که مصرف RAM چقدر است، چرا پورت 80 دچار تداخل می‌شود و چه زمانی Docker Compose گزینه بهتری است.

k3s چیست و یک نود آن چه امکاناتی به شما می‌دهد

k3s یک توزیع کامل Kubernetes است که در قالب یک فایل باینری واحد بسته‌بندی شده است. اجرای آن روی یک VPS تکی، API واقعی Kubernetes را بدون نیاز به یک control plane سه ماشینی در اختیار شما قرار می‌دهد. این یک توزیع تأییدشده (certified) از Kubernetes است، بنابراین فایلی (manifest) که در اینجا اعمال می‌شود، بعداً در یک کلاستر مدیریت‌شده نیز کار خواهد کرد. نصب آن تنها با یک دستور و در حدود 1 دقیقه انجام می‌شود. هزینه‌ای که می‌پردازید، بخشی از حافظه (RAM) است که دیگر در دسترس برنامه‌های شما نخواهد بود، به‌علاوه مجموعه‌ای از حالت‌های خرابی که هرگز در Docker Compose رخ نمی‌دهند.

شرکت SUSE، نرم‌افزار k3s را برای سایت‌های لبه (edge) و نصب‌های کوچک توسعه داده است و هر تفاوتی که با نسخه اصلی (upstream) Kubernetes دارد، برای کاهش حجم آن است. دیت‌استور پیش‌فرض، sqlite است که پشت یک لایه واسط به نام kine قرار دارد و etcd نیست؛ بنابراین نیازی به حفظ حد نصاب (quorum) برای etcd وجود ندارد. containerd به‌جای نصب جداگانه، درون همان فایل باینری تعبیه شده است. همین فایل باینری شامل CoreDNS برای DNS کلاستر، Traefik به‌عنوان ingress controller، ServiceLB (که klipper-lb نیز نامیده می‌شود) برای اینکه سرویس‌های LoadBalancer بدون نیاز به یک ارائه‌دهنده ابری کار کنند، local-path provisioner برای persistent volumes، metrics-server و flannel برای شبکه‌سازی پادها (pod networking) است. همه این موارد به‌صورت پیش‌فرض اجرا می‌شوند. به همین دلیل است که تداخل پورت که در ادامه توضیح داده شده، رایج‌ترین مشکل اولیه در VPSهایی است که از قبل سرویس دیگری روی آن‌ها در حال اجرا بوده است.

چه زمانی یک نود k3s ارزش استفاده دارد

از k3s زمانی استفاده کنید که به API کوبرنتیز نیاز دارید: در حال یادگیری کوبرنتیز روی ماشینی هستید که کنترل آن را در دست دارید، یا نرم‌افزاری که می‌خواهید نصب کنید فقط Helm chart ارائه می‌دهد. قابلیت حمل Manifestها نیز اهمیت دارد، زیرا Deploymentای که اینجا می‌نویسید، بدون تغییر به یک کلاستر مدیریت‌شده منتقل می‌شود. زمانی از Docker Compose استفاده کنید که هدف اصلی شما خودِ برنامه‌ها هستند. Compose همان کانتینرها را با اجزای متحرک بسیار کمتری اجرا می‌کند و یک فایل Compose روی یک VPS پس از یک سال، خوانایی بسیار بیشتری نسبت به یک دایرکتوری پر از Manifest دارد.

درباره آنچه یک نود به شما نمی‌دهد، شفاف باشید.

  • عدم وجود High Availability. وقتی VPS ریبوت می‌شود، تمام workloadها متوقف می‌شوند. کوبرنتیز سعی می‌کند Pod را روی نود دیگری زمان‌بندی کند، اما نود دیگری وجود ندارد.
  • عدم وجود Rolling Update که سرویس را بالا نگه دارد، مگر اینکه برنامه بتواند دو Replica روی یک ماشین که از یک Volume مشترک استفاده می‌کنند را تحمل کند.
  • ذخیره‌سازی وابسته به همان سرور، به دلیلی که در بخش local-path در ادامه توضیح داده شده است.
  • یک Control Plane که صرف‌نظر از استقرار هرگونه سرویس، حدود یک گیگابایت رم اشغال می‌کند.

هیچ‌کدام از این موارد k3s را به انتخاب بدی تبدیل نمی‌کند. این موارد k3s را برای دلیلی که معمولاً مردم ذکر می‌کنند، یعنی قابلیت اطمینان (Reliability)، به انتخاب بدی تبدیل می‌کند. اگر هدف واقعی شما داشتن چندین ماشین برای ساخت یک کلاستر چند-نودی واقعی است، آن تصمیم در اولویت قرار دارد: استفاده از Proxmox روی سخت‌افزار شخصی در برابر اجاره VPS مشخص می‌کند که نودها از کجا تأمین می‌شوند، پیش از آنکه k3s مشخص کند چه چیزی روی آن‌ها اجرا شود.

هزینه k3s از نظر RAM و CPU پیش از استقرار هر سرویس

پروژه k3s به جای تخمین، ارقام اندازه‌گیری‌شده را منتشر می‌کند. آن‌ها را با دقت بخوانید، زیرا عددی که افراد نقل می‌کنند، مربوط به وضعیت idle در k3s نیست.

ChartPublished k3s resource use, 95th percentile, Intel 8375C, k3s v1.26.5
The data behind this chart
[
  {
    "label": "Server, sqlite datastore",
    "ram_mb": "1,596",
    "cpu_percent_of_one_core": 6
  },
  {
    "label": "Server, embedded etcd",
    "ram_mb": "1,606",
    "cpu_percent_of_one_core": 6
  },
  {
    "label": "Agent node only",
    "ram_mb": "275",
    "cpu_percent_of_one_core": 3
  }
]

یک گره سرور در آن تست، در صدک 95 از 1,596 مگابایت رم و حدود 6 درصد از یک هسته CPU استفاده کرد. این‌ها ارقام منتشرشده هستند، نه اندازه‌گیری‌های این راهنما؛ و تست مذکور k3s نسخه v1.26.5 را با تمام کامپوننت‌های بسته‌بندی‌شده به همراه استک مانیتورینگ Prometheus و Grafana اجرا کرده است، بنابراین این عدد شامل یک workload واقعی است، نه یک کلاستر خالی. جایگزینی sqlite با etcd تعبیه‌شده، مصرف را به 1,606 مگابایت رساند. یک گره agent که kubelet و containerd را بدون control plane اجرا می‌کند، 275 مگابایت مصرف داشت. حداقلِ مستندشده برای یک سرور، 2 هسته و 2 گیگابایت رم است و این حداقل، k3s و کامپوننت‌های همراه آن را پیش از اجرای workloadهای شما پوشش می‌دهد.

برداشت عملی: روی یک VPS با 2 گیگابایت رم، control plane و افزونه‌های همراه آن فضای بسیار کمی برای شما باقی می‌گذارند و اولین اتفاقی که تحت فشار رخ می‌دهد، اخراج (evict) پادها توسط kubelet است. 4 گیگابایت رم، کفِ مناسبی برای یک گره با چند سرویس کوچک است. به جای اعتماد به هر رقم منتشرشده، از جمله این مورد، سرور خودتان را اندازه‌گیری کنید.

free -h
sudo k3s kubectl top node
sudo k3s kubectl get pods -A

پیش از نصب، دستور free -h را اجرا کنید و دوباره زمانی که وضعیت تمام پادها در kube-system به Running تغییر کرد، آن را تکرار کنید. اختلاف این دو، هزینه‌ای است که control plane روی سخت‌افزار شما تحمیل می‌کند. k3s kubectl top node در یکی دو دقیقه اول پس از نصب، error: Metrics API not available را برمی‌گرداند، زیرا metrics-server هنوز چیزی را اسکن نکرده است. این یک خطا نیست. اگر قصد دارید یک سرور را همزمان برای این کار و سایر وظایف پیکربندی کنید، محاسبات مربوط به تخمین RAM و CPU برای یک VPS در اینجا نیز بدون تغییر صدق می‌کند.

نصب k3s با نسخه ثابت (Pinned) به جای استفاده از آخرین نسخه

دستور نصب سریعی که همه از آن استفاده می‌کنند، هر نسخه‌ای را که در لحظه اجرا در کانال stable قرار دارد، نصب می‌کند. روی ماشینی که قصد نگهداری طولانی‌مدت آن را دارید، نسخه را ثابت کنید. k3s برای هر نسخه فرعی (minor version) کوبرنتیز یک کانال منتشر می‌کند، بنابراین INSTALL_K3S_CHANNEL=v1.36 از انتشار نسخه‌های اصلاحی (patch releases) در محدوده v1.36 پیروی می‌کند و هرگز بدون اطلاع شما به نسخه فرعی بعدی جهش نمی‌کند. تا اوت 2026، کانال stable به نسخه v1.36.3+k3s1 اشاره دارد.

ابتدا فایل پیکربندی را بنویسید و سپس نصب را انجام دهید. k3s هنگام شروع کار، /etc/rancher/k3s/config.yaml را می‌خواند، بنابراین هر چیزی که در آن باشد، هم در اولین بوت و هم در تمام دفعات بعدی اعمال می‌شود.

sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/config.yaml >/dev/null <<'EOF'
tls-san:
  - k3s.example.com
EOF
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.36 sh -

برای ثابت کردن یک نسخه دقیق به جای استفاده از کانال، از INSTALL_K3S_VERSION=v1.36.3+k3s1 استفاده کنید. علامت مثبت بخشی از تگ نسخه است. سپس بررسی کنید که سرویس بالا آمده باشد.

k3s --version
sudo systemctl status k3s
sudo k3s kubectl get node
sudo k3s kubectl get pods -A

kubectl get node باید ظرف حدود سی ثانیه یک گره (node) را با وضعیت Ready فهرست کند و تمام پادها در kube-system باید به وضعیت Running یا Completed برسند. گیر کردن یک گره در وضعیت NotReady معمولاً به این معنی است که runtime کانتینر هرگز شروع نشده است، بنابراین sudo journalctl -u k3s -n 100 --no-pager را مطالعه کنید. در تصاویر (image) غیرمعمول VPS، پیش از هرگونه عیب‌یابی دیگر، sudo k3s check-config را اجرا کنید: این دستور ویژگی‌های مفقود هسته (kernel) را گزارش می‌دهد که بسیار سریع‌تر از خواندن لاگ‌ها به نتیجه می‌رسد.

چرا پورت 80 از قبل اشغال است و برای رفع آن چه چیزی را باید فدا کرد

این همان خطایی است که کاربران را در VPSهایی که از قبل سرویسی روی آن‌ها در حال اجرا بوده، غافلگیر می‌کند. نصب با موفقیت انجام می‌شود، اما Traefik هرگز آدرسی دریافت نمی‌کند و سایتی که از قبل داشتید همچنان کار می‌کند؛ بنابراین تا زمانی که سعی نکنید به یک ingress دسترسی پیدا کنید، همه‌چیز سالم به نظر می‌رسد.

مکانیسم کار: چارت Traefik که به‌صورت پیش‌فرض همراه است، یک Service از نوع LoadBalancer روی پورت‌های 80 و 443 ایجاد می‌کند. ServiceLB با ایجاد یک DaemonSet از پادهای کوچک که با پیشوند svclb- نام‌گذاری شده‌اند، به این درخواست پاسخ می‌دهد. این پادها پورت‌های مذکور را به‌عنوان hostPort روی هر گره (node) مطالبه می‌کنند. hostPort پورت کانتینر را مستقیماً در فضای شبکه (network namespace) خودِ گره منتشر می‌کند، دقیقاً مشابه کاری که docker run -p 80:80 انجام می‌دهد. اگر nginx، Caddy، Apache یا کانتینر دیگری از قبل پورت 80 را در اختیار داشته باشد، هسته سیستم‌عامل آن را برای بار دوم واگذار نمی‌کند و در نتیجه، زمان‌بند (scheduler) جایی برای قرار دادن پاد ندارد.

sudo k3s kubectl -n kube-system get pods
sudo k3s kubectl -n kube-system get svc traefik
sudo ss -lntp '( sport = :80 or sport = :443 )'

شما وضعیت پاد svclb را در حالت Pending و سرویس را بدون آدرس خارجی خواهید دید:

svclb-traefik-8f2c1a-r6k9x   0/2   Pending   0   3m
traefik   LoadBalancer   10.43.62.11   <pending>   80:31480/TCP,443:30219/TCP

اجرای kubectl -n kube-system describe pod svclb-traefik-... علت را مستقیماً بیان می‌کند:

0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.

ss -lntp به شما می‌گوید کدام پردازش پورت را در اختیار دارد. بیش از یک راه برای خروج از این وضعیت وجود دارد و هر کدام هزینه‌ای برای شما خواهد داشت.

پورت‌ها را به k3s واگذار کنید. وب‌سرور فعلی را متوقف و غیرفعال کنید، سپس اجازه دهید Traefik مالک پورت‌های 80 و 443 باشد. این پاسخ درستی است زمانی که VPS قرار است فقط یک ماشین k3s باشد و تمام سرویس‌هایی که ارائه می‌دادید به پشت یک Ingress منتقل شوند.

سرویس ServiceLB را غیرفعال کنید و پروکسی فعلی خود را نگه دارید. با استفاده از --disable=servicelb نصب کنید. یک Service از نوع LoadBalancer همچنان یک NodePort اختصاص می‌دهد، بنابراین Traefik روی یک پورت بالا مانند 31480 در دسترس باقی می‌ماند و nginx یا Caddy شما درخواست‌ها را به 127.0.0.1:31480 هدایت (proxy) می‌کند. چیزی که از دست می‌دهید آدرس خارجی است: سرویس همیشه وضعیت <pending> را گزارش می‌دهد که اگرچه یک انتخاب آگاهانه است، اما ممکن است شبیه به خطا به نظر برسد.

Traefik را غیرفعال کنید و با پروکسی خودتان مسیریابی کنید. با استفاده از --disable=traefik نصب کنید. در این صورت شما هیچ ingress controller ندارید، بنابراین اشیاء Ingress هیچ کاری انجام نمی‌دهند: آن‌ها در API باقی می‌مانند بدون اینکه کنترلری آن‌ها را پایش کند. این وضعیت زمانی که از یک پروکسی میزبان به NodePortها مسیریابی می‌کنید کاملاً مناسب است و اگر از قبل می‌دانید چگونه می‌خواهید HTTP را مدیریت کنید، انتخاب صادقانه‌ای است. اگر هنوز در مورد اینکه چه چیزی باید در لایه جلویی قرار بگیرد تصمیم نگرفته‌اید، پیش از غیرفعال کردن هر چیزی، انتخاب بین nginx، Caddy و Traefik به‌عنوان reverse proxy را نهایی کنید.

هر دو فلگ باید در زمان نصب یا در فایل پیکربندی اعمال شوند:

tls-san:
  - k3s.example.com
disable:
  - traefik
  - servicelb

ویرایش آن فایل پس از نصب و اجرای sudo systemctl restart k3s نیز کارساز است، زیرا --disable کاری فراتر از نادیده گرفتن یک مؤلفه در زمان نصب انجام می‌دهد. این دستور مؤلفه‌ای که از قبل مستقر شده را نیز حذف می‌کند، بنابراین تغییرات روی کلاستر در حال اجرا اعمال می‌شود.

برای حفظ Traefik اما تغییر نحوه پیکربندی چارت، /var/lib/rancher/k3s/server/manifests/traefik.yaml را ویرایش نکنید. k3s هر بار که شروع به کار می‌کند، آن فایل را با مقادیر پیش‌فرض بازنویسی می‌کند. در عوض، یک فایل جداگانه در همان دایرکتوری اضافه کنید، زیرا هر چیزی در /var/lib/rancher/k3s/server/manifests به‌طور خودکار در زمان راه‌اندازی و هر زمان که روی دیسک تغییر کند، اعمال می‌شود.

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    ports:
      web:
        forwardedHeaders:
          trustedIPs:
            - 10.0.0.0/8

آن مثال یکی از مقادیر چارت Traefik، یعنی آدرس‌های پروکسی مورد اعتماد (trusted proxy addresses) را تنظیم می‌کند. همین مکانیسم هر مقدار دیگری که چارت در معرض دید قرار می‌دهد، از جمله پورت‌های آن را تنظیم می‌کند.

ذخیره‌سازی پایدار روی یک نود

k3s به‌صورت پیش‌فرض یک StorageClass به نام local-path ارائه می‌دهد که توسط local-path provisioner شرکت Rancher پشتیبانی می‌شود. هر PersistentVolumeClaim که فاقد storageClassName باشد، از این کلاس استفاده می‌کند. حجم‌ها (Volumes) در مسیر /var/lib/rancher/k3s/storage و در زیرشاخه‌های اختصاصی برای هر حجم، روی دیسک همان نود ذخیره می‌شوند.

sudo k3s kubectl get storageclass
sudo k3s kubectl get pvc -A
sudo ls -l /var/lib/rancher/k3s/storage

عبارت «روی دیسک همان نود» دو پیامد دارد که هر دو در آینده، و نه امروز، مشکل‌ساز خواهند شد.

این StorageClass از volumeBindingMode: WaitForFirstConsumer استفاده می‌کند؛ بنابراین یک PVC جدید تا زمانی که پادی آن را mount نکند، در وضعیت Pending باقی می‌ماند. دستور kubectl describe pvc این وضعیت را نمایش می‌دهد:

waiting for first consumer to be created before binding

این رفتار عادی است؛ بنابراین ایجاد یک PVC به‌تنهایی و انتظار برای تغییر وضعیت آن، هرگز مشکل را حل نخواهد کرد.

پس از اتصال (Bound)، حجم مذکور دارای یک node affinity برای نودی است که آن را ایجاد کرده است. این ویژگی باعث می‌شود هر پادی که از آن claim استفاده می‌کند، تا پایان عمر آن حجم به همان نود محدود (pin) شود. در یک کلاستر تک‌نودی، متوجه این موضوع نخواهید شد. اما اگر بعداً نود دومی اضافه کنید، پادی که از جابه‌جایی امتناع می‌کند ممکن است شبیه به یک باگ در زمان‌بند (scheduler) به نظر برسد، تا زمانی که دستور kubectl get pv -o yaml را اجرا کنید و نام هاست (hostname) را در بخش nodeAffinity مشاهده کنید.

مسئولیت پشتیبان‌گیری با شماست. بازسازی VPS باعث حذف آن دایرکتوری می‌شود و اسکریپت حذف (uninstall) که در ادامه آمده نیز همین کار را انجام می‌دهد. از مسیر /var/lib/rancher/k3s/storage و همچنین دیتابیس sqlite در مسیر /var/lib/rancher/k3s/server/db/state.db پشتیبان تهیه کنید. برای کپی کردن دیتابیس، حتماً ابتدا سرویس را متوقف کنید، زیرا این یک دیتابیس فعال (live) است. راه جایگزین این است که کلاستر را یک موجودیت موقت (disposable) در نظر بگیرید و تمام manifestها را در git نگهداری کنید.

Ingress و TLS

با فعال ماندن Traefik، تنها چیزی که نیاز دارید یک شیء Ingress استاندارد است.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - hello.example.com
      secretName: hello-tls
  rules:
    - host: hello.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello
                port:
                  number: 80

گواهی‌ها به‌صورت خودکار ظاهر نمی‌شوند. راهکار معمول، استفاده از cert-manager است که از طریق manifest منتشرشدهٔ آن نصب می‌شود، به همراه یک ClusterIssuer. نسخه v1.21.1 تا اوت 2026 نسخه جاری است.

sudo k3s kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: you@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: traefik

چالش HTTP-01 به این معناست که سرور ACME (محیط مدیریت خودکار گواهی) از طریق اینترنت عمومی به http://hello.example.com/.well-known/acme-challenge/... متصل می‌شود. بنابراین رکورد DNS A باید از قبل به VPS اشاره کند و پورت 80 باید به Traefik دسترسی داشته باشد. اگر ServiceLB را غیرفعال کرده‌اید و یک پروکسی اختصاصی در مقابل آن قرار داده‌اید، آن پروکسی نیز باید مسیر چالش را هدایت (forward) کند، در غیر این صورت cert-manager در شیء Challenge با این وضعیت متوقف می‌شود:

Waiting for HTTP-01 challenge propagation: wrong status code '404', expected '200'

صدور گواهی را با sudo k3s kubectl describe certificate hello-tls و sudo k3s kubectl get order,challenge -A مانیتور کنید.

فایل kubeconfig و دلیل خصوصی ماندن API server

k3s اطلاعات کاربری مدیر را در /etc/rancher/k3s/k3s.yaml می‌نویسد. مالک این فایل به‌صورت پیش‌فرض root است و با مجوز 600 ایجاد می‌شود. این فایل حاوی یک گواهی کلاینت با دسترسی cluster-admin است، بنابراین هر کسی که بتواند آن را بخواند، کنترل کامل کلاستر را در دست می‌گیرد.

sudo k3s kubectl get node
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get node

اگر kubectl به‌صورت جداگانه نصب شده باشد و متغیر KUBECONFIG تنظیم نشده باشد، با خطای The connection to the server localhost:8080 was refused - did you specify the right host or port? مواجه می‌شوید؛ زیرا برنامه به مسیر پیش‌فرضی ارجاع می‌دهد که ارتباطی با k3s ندارد. متغیر KUBECONFIG را تنظیم کنید یا از sudo k3s kubectl استفاده کنید که به‌طور خودکار فایل صحیح را می‌خواند.

ممکن است توصیه به استفاده از --write-kubeconfig-mode 644 ببینید تا یک کاربر معمولی بتواند kubectl را اجرا کند. بدانید که این کار چه معنایی دارد: این دستور باعث می‌شود اطلاعات کاربری cluster-admin برای تمام اکانت‌های محلی روی سیستم قابل خواندن باشد. در سیستمی که تنها یک مدیر دارد، این ممکن است یک مصالحه قابل‌قبول باشد، اما در سیستم‌های اشتراکی چنین نیست. کپی کردن فایل به یک کاربر خاص، بدون انتشار عمومی آن، دسترسی لازم را فراهم می‌کند:

mkdir -p ~/.kube
sudo install -o $USER -g $USER -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config

خط server: در آن فایل، مقدار https://127.0.0.1:6443 را نشان می‌دهد. برای استفاده از kubectl از روی لپ‌تاپ خود، پورت 6443 را روی اینترنت باز نکنید. یک Kubernetes API عمومی، هدفی همیشگی برای حملات است و باز گذاشتن آن دلیلی است که کلاسترهای کوچک در نهایت برای دیگران استخراج ارز دیجیتال انجام می‌دهند. آن را از طریق SSH تونل کنید و آدرس موجود در فایل را تغییر ندهید:

ssh -N -L 6443:127.0.0.1:6443 user@your-vps
KUBECONFIG=./k3s.yaml kubectl get node

اگر مجبور هستید از طریق یک آدرس شبکه خصوصی به API دسترسی پیدا کنید، هنگام نصب از tls-san استفاده کنید و آن نام یا آدرس را در آن ذکر کنید، سپس خط server: در فایل کپی‌شده را مطابق با آن ویرایش کنید. بدون وجود ورودی SAN (مخفف subject alternative name)، ابزار kubectl اتصال را رد می‌کند:

x509: certificate is valid for 127.0.0.1, 10.43.0.1, not 203.0.113.10

پورت‌های ورودی مستند شده برای یک کلاستر عبارتند از TCP 6443 برای API، UDP 8472 برای flannel VXLAN بین نودها، و TCP 10250 برای متریک‌های kubelet. در یک نود تکی، هیچ‌کدام از این پورت‌ها نیازی به باز بودن روی اینترنت ندارند.

نرم‌افزار containerd همان Docker نیست

نرم‌افزار k3s از containerd تعبیه‌شدهٔ اختصاصی خود استفاده می‌کند و مخزن ایمیج (image store) آن با Docker مشترک نیست. ایمیجی که به‌تازگی با docker build ساخته‌اید برای k3s قابل مشاهده نیست، بنابراین pod با خطای ErrImagePull مواجه می‌شود، حتی اگر docker images آن را فهرست کند. آن را به‌صورت صریح import کنید:

docker save myapp:0.1 | sudo k3s ctr images import -
sudo k3s crictl images

سپس از تگ :latest برای آن کانتینر پرهیز کنید، زیرا :latest به‌صورت پیش‌فرض از imagePullPolicy مقدار Always استفاده می‌کند و kubelet در هر صورت به سراغ یک registry می‌رود. هر تگ دیگری به‌صورت پیش‌فرض از IfNotPresent استفاده می‌کند که همان ایمیجی است که import کرده‌اید. اجرای هم‌زمان هر دو روی یک VPS امکان‌پذیر است و دانستن این نکته کمک می‌کند که نصب معمولی Docker روی یک VPS و k3s هر کدام مخزن ایمیج و قوانین iptables اختصاصی خود را روی همان ماشین حفظ می‌کنند.

نحوه حذف k3s

نصب‌کننده یک اسکریپت حذف (uninstall) ایجاد می‌کند. امکان حذف جزئی یا بازگرداندن تغییرات (undo) وجود ندارد.

sudo /usr/local/bin/k3s-uninstall.sh

این اسکریپت سرویس را متوقف و حذف می‌کند، datastore را پاک می‌کند، داده‌های persistent volume را از بین می‌برد، پیکربندی node را حذف کرده و ابزارهایی که نصب‌کننده اضافه کرده است را پاکسازی می‌کند. در یک agent node، اسکریپت به جای آن k3s-agent-uninstall.sh است. پیش از هر چیز، هر فایلی که در /var/lib/rancher/k3s/storage قرار دارد را از سرور کپی و خارج کنید، زیرا این دایرکتوری همراه با حذف سرویس پاک می‌شود. پس از آن، با استفاده از ip link show و sudo ss -lntp بررسی کنید که هیچ پردازشی پورت‌ها یا اینترفیس‌ها را اشغال نکرده باشد. اینترفیس‌های باقی‌مانده cni0 یا flannel.1 در reboot بعدی پاک می‌شوند.

اینکه به این نتیجه برسید که یک node از Kubernetes برای نیاز پروژه شما بیش از حد پیچیده است، یک نتیجه‌گیری منطقی است و نه یک شکست. انتقال آن workloadها به Compose معمولاً در یک بعدازظهر انجام می‌شود.

حالت‌های شکست و پیام‌هایی که مشاهده خواهید کرد

وضعیت Node NotReady یا راه‌اندازی مجدد مداوم k3s. ابتدا sudo journalctl -u k3s -n 200 --no-pager را مطالعه کنید. در یک VPS کوچک، دلیل رایج، مداخله kernel out-of-memory killer برای متوقف کردن پردازش است که در dmesg با خطی که نام k3s-server را دارد، نمایش داده می‌شود. حداقل 2 GB رم که در مستندات ذکر شده، یک حد واقعی است.

وضعیت Pod در حالت Pending گیر کرده است. kubectl describe pod هر بار دلیل آن را ذکر می‌کند. Insufficient memory یا Insufficient cpu به این معنی است که نود فضای کافی ندارد. didn't have free ports همان تداخل hostPort است که پیش‌تر ذکر شد. waiting for first consumer روی یک PVC به این معنی است که WaitForFirstConsumer به درستی وظیفه خود را انجام می‌دهد.

خطای ImagePullBackOff. یا تگ مورد نظر در هیچ‌یک از رجیستری‌های قابل دسترس برای نود وجود ندارد، یا شما ایمیج را با Docker ساخته‌اید و آن را به containerd وارد نکرده‌اید.

Traefik پاسخ می‌دهد، اما برنامه خیر. بدنه پاسخ 404 page not found مستقیماً از سمت Traefik ارسال می‌شود و به این معنی است که درخواست رسیده اما هیچ router با آن مطابقت نداشته است. بررسی کنید که Ingress host با نامی که وارد کرده‌اید مطابقت داشته باشد و ingressClassName برابر با traefik باشد.

سرویس DNS کلاستر با شکست مواجه می‌شود در حالی که میزبان (host) مشکلی در رزولوشن ندارد. CoreDNS را با sudo k3s kubectl -n kube-system logs -l k8s-app=kube-dns بررسی کنید. پیامی مانند plugin/loop: Loop ... detected for zone "." مانع از شروع به کار CoreDNS می‌شود. این اتفاق زمانی رخ می‌دهد که CoreDNS درخواست‌ها را به یک resolver می‌فرستد که دوباره آن‌ها را به خود CoreDNS بازمی‌گرداند؛ این همان کاری است که آدرس loopback در /etc/resolv.conf انجام می‌دهد. k3s را با استفاده از --resolv-conf /run/systemd/resolve/resolv.conf به فایل بالادستی (upstream) واقعی هدایت کنید.

FAQ

آیا اجرای k3s روی یک VPS تکی ارزشش را دارد؟

زمانی ارزش دارد که به API کوبرنتیز نیاز داشته باشید: برای یادگیری آن روی ماشینی که کنترلش در دست شماست، حفظ قابلیت انتقال دپلوی‌منت‌ها به صورت manifest، اجرای نرم‌افزاری که فقط Helm chart ارائه می‌دهد، یا ساخت چیزی که بعداً به یک کلاستر مدیریت‌شده منتقل خواهید کرد. اگر فقط می‌خواهید کانتینرها را اجرا کنید، این کار ارزشش را ندارد؛ زیرا Docker Compose با نگهداری بسیار کمتر و حدود یک گیگابایت رم آزاد بیشتر، همین کار را انجام می‌دهد. یک نود به شما قابلیت high availability نمی‌دهد، بنابراین پایداری هرگز دلیل انتخاب آن نیست.

چرا سرویس LoadBalancer در k3s در وضعیت Pending باقی می‌ماند؟

سرویس ServiceLB تعدادی پاد svclb- ایجاد می‌کند که پورت‌های سرویس را به عنوان hostPort روی نود اشغال می‌کنند، بنابراین آن‌ها فقط در جایی زمان‌بندی می‌شوند که آن پورت‌ها آزاد باشند. اگر nginx یا یک پروکسی دیگر قبلاً پورت 80 را اشغال کرده باشد، پاد در وضعیت Pending می‌ماند و سرویس هرگز آدرس خارجی دریافت نمی‌کند. دستور kubectl -n kube-system describe pod svclb-... خطای 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports را گزارش می‌دهد. پورت را آزاد کنید یا با استفاده از --disable=servicelb مجدداً نصب کنید و ترافیک را به NodePort که سرویس همچنان تخصیص می‌دهد، پروکسی کنید.

k3s روی یک VPS به چه مقدار رم نیاز دارد؟

حداقل مقدار مستندشده برای یک نود سرور، 2 هسته پردازنده و 2 گیگابایت رم است که این مقدار k3s و اجزای بسته‌بندی‌شده آن را پیش از اجرای ورک‌لودهای شما پوشش می‌دهد. پروفایلینگ خود پروژه، مصرف یک نود سرور را با یک استک مانیتورینگ در حال اجرا، 1,596 مگابایت اندازه‌گیری کرده است؛ بنابراین 2 گیگابایت را به عنوان کفِ نیاز و 4 گیگابایت را به عنوان اولین اندازه‌ای در نظر بگیرید که یک نود در آن راحت عمل می‌کند. میزان مصرف سرور خود را با دستور free -h پیش از نصب و دوباره پس از اینکه وضعیت تمام پادها در kube-system به Running تغییر کرد، اندازه‌گیری کنید.

آیا می‌توانم Docker و k3s را روی یک VPS اجرا کنم؟

بله، آن‌ها جدا از هم باقی می‌مانند. k3s از containerd تعبیه‌شده مخصوص خود استفاده می‌کند، بنابراین ایمیجی که با docker build ساخته شده تا زمانی که docker save myapp:0.1 | sudo k3s ctr images import - را اجرا نکنید، برای آن قابل مشاهده نیست. هر کدام همچنین قوانین iptables و شبکه‌های bridge مخصوص خود را می‌نویسند. مراقب مجموع حافظه باشید، زیرا Docker به همراه k3s و کانتینرهای شما در یک سرور 2 گیگابایتی جا نمی‌شوند.

چگونه k3s را به‌طور کامل حذف کنم؟

دستور sudo /usr/local/bin/k3s-uninstall.sh را روی یک نود سرور یا sudo /usr/local/bin/k3s-agent-uninstall.sh را روی یک agent اجرا کنید. این دستور سرویس را متوقف می‌کند، datastore را حذف می‌کند، داده‌های persistent volume را در مسیر /var/lib/rancher/k3s/storage پاک می‌کند و ابزارهای همراه را حذف می‌نماید. ابتدا هر داده‌ای را که می‌خواهید نگه دارید از سرور کپی کنید، زیرا راه بازگشتی وجود ندارد. یک اینترفیس شبکه باقی‌مانده از نوع cni0 یا flannel.1 با ریبوت بعدی ناپدید می‌شود.

#kubernetes#k3s#docker-compose#ingress#sizing