SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor

VPSでk3sを使う価値はある?1ノード構成の判断基準

k3sは1台のVPSで実際のKubernetes APIを使えます。RAM消費、インストール時のポート80競合、Docker Composeを選ぶべき条件を具体的に解説します。

k3s とは何か、1 ノードで得られるもの

k3s は、1 つのバイナリにパッケージ化された完全な Kubernetes ディストリビューションです。単一の VPS で実行すると、3 台構成のコントロールプレーンなしで実際の Kubernetes API を利用できます。認定済みの Kubernetes ディストリビューションであるため、ここで適用できるマニフェストは、後でマネージドクラスターにも適用できます。インストールは 1 つのコマンドで完了し、所要時間は約 1 分です。失うのは、アプリケーションで使えなくなるメモリと、Docker Compose では発生しない一連の障害要因です。

SUSE はエッジサイトや小規模環境向けに k3s を構築しており、upstream Kubernetes との違いはすべて、より小さくするために存在します。デフォルトのデータストアは etcd ではなく、kine という shim の背後にある sqlite です。そのため、維持すべき etcd のクォーラムはありません。containerd は別途インストールせず、バイナリに組み込まれています。同じバイナリには、クラスター DNS 用の CoreDNS、Ingress controller としての Traefik、背後にクラウドプロバイダーがなくても LoadBalancer サービスを動作させる ServiceLB(klipper-lb とも呼ばれます)、永続ボリューム用の local-path provisioner、metrics-server、Pod ネットワーク用の flannel も含まれています。これらはすべてデフォルトで起動します。そのため、以下で説明するポート競合が、すでに別の用途で稼働していた VPS で最初に発生する最も一般的な問題になります。

1 台の k3s ノードを選ぶ価値がある場合

次の基準で判断してください。目的が Kubernetes API そのものであれば、k3s を実行します。自分で管理するマシン上で Kubernetes を学習する場合や、使いたいソフトウェアが Helm chart だけを提供している場合が該当します。マニフェストの可搬性も利点です。ここで作成した Deployment は、変更せずにマネージドクラスターへ移行できます。目的がアプリケーションそのものであれば、Docker Compose を実行します。Compose は、構成要素を大幅に減らしながら同じコンテナを起動できます。また、VPS 上の Compose ファイルは、1 年後でもマニフェストのディレクトリより読みやすくなります。

1 台のノードでは得られないものを明確にしておきます。

  • 高可用性はありません。VPS を再起動すると、すべてのワークロードが停止します。Kubernetes は Pod を別のノードへ再スケジュールしますが、別のノードは存在しません。
  • アプリケーションが 1 台のマシン上で同じボリュームを共有する 2 つのレプリカに対応していない限り、サービスを稼働させたままローリングアップデートすることはできません。
  • ストレージは、この下の local-path セクションで説明する理由により、そのマシンに固定されます。
  • 何もデプロイしていなくても、コントロールプレーンに約 1 GB の RAM が必要です。

これらは、k3s が悪い選択だという意味ではありません。ただし、一般的に挙げられる理由である信頼性を目的に k3s を選ぶのは適切ではありません。実際に必要なのが、実際のマルチノードクラスターを構築するための複数のマシンであれば、まずその判断を行います。自分のハードウェア上の Proxmox とレンタル VPS の比較でノードの調達元を決めてから、k3s でノード上で実行するものを決めます。

デプロイ前に k3s が消費する RAM と CPU

k3s プロジェクトは、推定値ではなく実測値を公開しています。注意して確認してください。一般に引用される数値は、アイドル状態の 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
  }
]

このテストの server node は、RAM を 95 パーセンタイルで 1,596 MB、CPU を 1 コアの約 6 パーセント使用しました。これはこのガイドで測定した値ではなく、公開されている数値です。テストでは、パッケージ済みの全コンポーネントを有効にした k3s v1.26.5 に加え、Prometheus と Grafana の監視スタックを実行していました。そのため、空のクラスターではなく、実際のワークロードを含む数値です。sqlite を embedded etcd に置き換えると、RAM 使用量は 1,606 MB になりました。control plane を持たず、kubelet と containerd を実行する agent node は 275 MB を使用しました。server の文書化された最小要件は 2 コアと 2 GB の RAM です。この最小要件は、ワークロードを実行する前の k3s とパッケージ済みコンポーネントを対象としています。

実際の見方として、2 GB の VPS では control plane と組み込みのアドオンが大部分を消費するため、残る容量はごくわずかです。負荷が高くなったときに最初に起きるのは、kubelet による Pod の退避です。1 台の node で小規模なサービスをいくつか実行するなら、4 GB が余裕のある下限です。公開されている数値や、この数値をそのまま信頼せず、自分のサーバーで測定してください。

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

インストール前に free -h を実行し、さらに kube-system のすべての Pod が Running と表示された後にもう一度実行します。その差が、使用するハードウェア上で control plane が消費するリソースです。インストール直後の 1〜2 分間に k3s kubectl top nodeerror: Metrics API not available を返すのは、metrics-server がまだ何もスクレイプしていないためです。これは障害ではありません。このサーバーで k3s と他の処理を同時に実行する構成のサイズを決める場合も、VPS の RAM と CPU のサイズを決める で示した計算をそのまま適用できます。

リリースを固定して k3s をインストールする

一般的にコピーされているクイックスタートの行では、実行した日に stable channel が指しているバージョンがそのまま選択されます。長期間運用するマシンでは、バージョンを固定してください。k3s は Kubernetes のマイナーバージョンごとに channel を公開しています。そのため、INSTALL_K3S_CHANNEL=v1.36 は v1.36 内のパッチリリースだけを追従し、意図せずマイナーバージョンをまたぐことはありません。2026 年 8 月時点では、stable channel は 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 -

channel ではなく特定のリリースを固定するには、INSTALL_K3S_VERSION=v1.36.3+k3s1 を使用します。プラス記号は tag の一部です。続いて、起動したことを確認します。

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

kubectl get node の STATUS Ready に、約 30 秒以内に 1 台の node が表示されるはずです。また、kube-system のすべての pod が Running または Completed になります。node が NotReady のままになる場合は、container runtime が起動していないことが一般的な原因です。そのため、sudo journalctl -u k3s -n 100 --no-pager を確認してください。特殊な VPS イメージを使用している場合は、ほかの調査を始める前に sudo k3s check-config を実行します。不足している kernel 機能が報告されるため、ログを読むよりも早く原因を特定できます。

ポート 80 がすでに使用中である理由と、解決のために手放すもの

これは、すでに何かを提供している VPS で問題になりやすい失敗です。インストール自体は成功します。しかし Traefik はアドレスを取得できず、すでに運用しているサイトは動き続けるため、Ingress にアクセスするまで問題に気付きません。

仕組みは次のとおりです。組み込みの Traefik chart は、ポート 80 と 443 で Service type LoadBalancer を作成します。ServiceLB はこの要求に応じて、svclb- というプレフィックスを持つ小さな Pod の DaemonSet を作成し、各ノードでこれらのポート番号を hostPort として確保します。hostPort は、docker run -p 80:80 と同じように、コンテナのポートをノード自身の network namespace に直接公開します。nginx、Caddy、Apache、または別のコンテナがすでにポート 80 を使用している場合、kernel は同じポートを 2 回割り当てられないため、scheduler はその Pod の配置先を確保できません。

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 Pod が Pending になり、Service に外部アドレスが表示されない状態になります。

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 に割り当てる。 既存の Web サーバーを停止して無効化し、Traefik に 80 と 443 を使用させます。VPS を k3s 専用にし、提供中のすべてのサービスを Ingress の背後へ移行する場合は、これが適切です。

ServiceLB を無効化して、既存のプロキシを使い続ける。 --disable=servicelb を指定してインストールします。Service type LoadBalancer は引き続き NodePort を割り当てるため、Traefik は 31480 などの高いポートで到達可能になり、nginx または Caddy から 127.0.0.1:31480 へプロキシできます。手放すことになるのは外部アドレスです。Service は常に <pending> を報告します。これは障害ではなく、選択した構成による結果です。

Traefik を無効化して、自分のプロキシでルーティングする。 --disable=traefik を指定してインストールします。この場合、Ingress controller は存在しないため、Ingress object は何も行いません。API 内に残りますが、それを監視する controller がいないためです。ホスト上のプロキシから NodePort へルーティングする場合は、この構成で問題ありません。HTTP をどのように処理するかが決まっている場合にも、正直な選択肢です。前段に何を置くか決まっていない場合は、無効化する前に nginx、Caddy、Traefik のどれを reverse proxy にするか を決めてください。

どちらの flag も installer に指定するか、config file に記述します。

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

インストール後にその file を編集し、sudo systemctl restart k3s を実行する方法も使えます。--disable はインストール時に component を省略するだけではありません。すでにデプロイされている component も削除するため、稼働中の cluster に対して変更を適用できます。

Traefik を維持したまま chart の設定方法を変更する場合は、/var/lib/rancher/k3s/server/manifests/traefik.yaml を編集しないでください。k3s は起動するたびに、その file を default 値で書き換えます。代わりに同じ directory に別の file を追加してください。/var/lib/rancher/k3s/server/manifests に置いた内容は、起動時と disk 上で変更されたときに自動的に適用されます。

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 chart の値を 1 つ、つまり信頼する proxy のアドレスを設定しています。同じ仕組みで、port を含む chart が公開するほかの値も設定できます。

1 台のノードでの永続ストレージ

k3s には、Rancher の local-path provisioner を使用するデフォルトの StorageClass である local-path が用意されています。storageClassName が指定されていない PersistentVolumeClaim には、この StorageClass が適用されます。ボリュームは /var/lib/rancher/k3s/storage に保存され、ボリュームごとに 1 つのサブディレクトリがノード自身のディスク上に作成されます。

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

「ノード自身のディスク上に保存される」ことには、2 つの影響があります。どちらも、すぐにではなく後から問題になります。

この StorageClass は volumeBindingMode: WaitForFirstConsumer を使用するため、新しい PVC は、Pod が実際にマウントするまで Pending のままです。kubectl describe pvc の出力は次のようになります。

waiting for first consumer to be created before binding

これは正常な状態です。そのため、PVC だけを作成して待っても、Pending は解消されません。

バインドされると、ボリュームには作成元ノードへの node affinity が設定されます。そのため、そのクレームを使用するすべての Pod は、ボリュームが存在する間、そのノードに固定されます。ノードが 1 台だけなら、この制約に気付くことはありません。後から 2 台目のノードを追加すると、移動できない Pod はスケジューラの不具合のように見えます。しかし、kubectl get pv -o yaml を実行して nodeAffinity にホスト名が設定されていることを確認すれば、原因が分かります。

バックアップは利用者の責任です。VPS を再構築するとそのディレクトリは失われます。下記のアンインストールスクリプトを実行した場合も同様です。/var/lib/rancher/k3s/storage をバックアップしてください。また、稼働中のデータベースであるため、サービスを停止した状態でコピーした sqlite データストア /var/lib/rancher/k3s/server/db/state.db もバックアップします。別の方法として、クラスターを使い捨てとして扱い、すべてのマニフェストを 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 をインストールし、ClusterIssuer を 1 つ設定します。2026 年 8 月時点の現行バージョンは v1.21.1 です。

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(automatic certificate management environment)サーバーがパブリックインターネットから http://hello.example.com/.well-known/acme-challenge/... に接続します。そのため、DNS A レコードはあらかじめ VPS を指している必要があり、ポート 80 は Traefik に到達できなければなりません。ServiceLB を無効にして独自のプロキシを前段に置いた場合、そのプロキシでもチャレンジのパスを転送する必要があります。転送しないと、cert-manager は Challenge オブジェクトに次の内容を表示したまま処理を停止します。

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

sudo k3s kubectl describe certificate hello-tlssudo k3s kubectl get order,challenge -A で証明書の発行状況を監視します。

kubeconfig と、API サーバーを非公開にする理由

k3s は管理者認証情報を /etc/rancher/k3s/k3s.yaml に書き込みます。ファイルの所有者は root で、デフォルトでは mode 600 で作成されます。このファイルには cluster-admin 権限を持つクライアント証明書が含まれるため、読み取れるユーザーはクラスター全体を制御できます。

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

KUBECONFIG が設定されていない、別途インストールした kubectl は The connection to the server localhost:8080 was refused - did you specify the right host or port? で失敗します。k3s とは無関係なデフォルト値にフォールバックするためです。KUBECONFIG を設定するか、適切なファイルを自動的に読み込む sudo k3s kubectl を使用します。

通常のユーザーが kubectl を実行できるように、--write-kubeconfig-mode 644 を推奨する説明を見かけます。これは何を意味するのか理解してください。クラスター管理者の認証情報を、そのマシン上のすべてのローカルアカウントから読み取れるようにします。管理者が 1 人だけのサーバーであれば、許容できるトレードオフかもしれません。共有サーバーでは適切ではありません。ファイルをコピーすれば、公開せずに 1 人のユーザーだけにアクセスを許可できます。

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 を読み込みます。ノート PC から 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

クラスターで文書化されている受信ポートは、API 用の TCP 6443、ノード間の flannel VXLAN 用の UDP 8472、kubelet メトリクス用の TCP 10250 です。単一ノード構成では、いずれもインターネットに公開する必要はありません。

containerd は Docker ではありません

k3s は独自の組み込み containerd を使用し、Docker とイメージストアを共有しません。docker build で直前にビルドしたイメージは k3s から見えないため、docker images では表示されていても、Pod は ErrImagePull で失敗します。明示的にインポートしてください。

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

そのコンテナでは :latest タグを使用しないでください。:latest のデフォルトは AlwaysimagePullPolicy であるため、kubelet はレジストリへアクセスします。それ以外のタグのデフォルトは IfNotPresent であり、インポートしたイメージを使用します。同じ VPS 上で両方を実行できます。また、VPS への通常の Docker インストールと k3s は、同じマシン上でそれぞれ独自のイメージストアと iptables ルールを保持することを理解しておくと役立ちます。

k3s を削除する方法

インストーラーはアンインストールスクリプトを書き込みます。部分的な削除や元に戻す操作はありません。

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

このスクリプトはサービスを停止して削除し、データストア、永続ボリュームのデータ、ノード設定を削除し、インストーラーが追加したツールを削除します。agent node では、代わりにスクリプトが k3s-agent-uninstall.sh です。/var/lib/rancher/k3s/storage 配下にあるものは、先に別の場所へコピーしてください。このディレクトリも削除されるためです。その後、ip link showsudo ss -lntp を使用して、ポートやインターフェースを保持しているものが残っていないことを確認します。残った cni0 または flannel.1 インターフェースは、次回の再起動時に消去されます。

Kubernetes の 1 ノード構成が用途に対して過剰だったと判断するのは、失敗ではなく通常の結果です。これらのワークロードを Compose に戻す作業は、通常半日で完了します。

障害パターンと表示される文字列

Node NotReady、または k3s がループして再起動する場合。 まず sudo journalctl -u k3s -n 200 --no-pager を確認します。小規模な VPS では、プロセスが kernel の out-of-memory killer に強制終了されることがよくあります。この場合、dmesgk3s-server を示す行が記録されます。文書化されている 2 GB の最小要件は、実際の下限です。

Pod が Pending のままになる場合。 原因は毎回 kubectl describe pod に記録されます。Insufficient memory または Insufficient cpu は、ノードに空きがないことを示します。didn't have free ports は、前述の hostPort の競合です。PVC に対する waiting for first consumer は、WaitForFirstConsumer が正常に機能していることを示します。

ImagePullBackOff。 ノードから到達できるどの registry にもその tag が存在しないか、Docker でイメージをビルドしたものの、containerd にインポートしていません。

Traefik は応答するが、アプリケーションは応答しない場合。 応答本文が 404 page not found なら、Traefik 自身が返しています。リクエストは到達したものの、どの router にも一致しなかったことを示します。Ingress の host が入力した名前と一致すること、および ingressClassNametraefik であることを確認します。

ホストでは名前解決できるのに、クラスタの DNS が失敗する場合。 sudo k3s kubectl -n kube-system logs -l k8s-app=kube-dns で CoreDNS を確認します。plugin/loop: Loop ... detected for zone "." のようなメッセージがあると、CoreDNS は起動できません。これは、CoreDNS が自分自身へ転送する resolver に転送しているためです。/etc/resolv.conf に loopback アドレスが設定されていると、この状態になります。--resolv-conf /run/systemd/resolve/resolv.conf を使用して、k3s が実際の upstream file を参照するようにします。

FAQ

1 台の VPS で k3s を実行する価値はありますか?

Kubernetes API が必要な場合は価値があります。自分で管理するマシンで Kubernetes を学ぶ場合、デプロイをマニフェストとして移植可能な形で維持する場合、Helm chart しか公開していないソフトウェアを実行する場合、または将来 managed cluster に移行する構成を作る場合です。コンテナを実行したいだけなら、保守対象がはるかに少なく、空き RAM もおよそ 1 GB 多い Docker Compose のほうが適しています。1 ノード構成では高可用性を実現できないため、信頼性を理由に選ぶことはできません。

k3s の LoadBalancer サービスが Pending のままになるのはなぜですか?

ServiceLB は svclb- pods を作成し、ノード上の hostPort としてサービスのポートを確保します。そのため、対象ポートが空いているノードでしかスケジュールされません。nginx などのプロキシがすでに port 80 を使用していると、pod は 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 へプロキシします。

VPS で k3s を実行するには、どの程度の RAM が必要ですか?

server node の文書化された最小要件は 2 cores と 2 GB です。これは、ワークロードを実行する前の k3s と同梱コンポーネントを対象とした値です。プロジェクト独自のプロファイリングでは、monitoring stack を実行した server node の使用量は 1,596 MB でした。そのため、2 GB は下限と考え、1 ノードを無理なく運用できる最初のサイズは 4 GB と考えてください。インストール前に free -h で自分のサーバーを計測し、kube-system のすべての pod が Running になった後にも再度計測します。

同じ VPS で Docker と k3s を実行できますか?

はい。両者は分離された状態で動作します。k3s は独自の組み込み containerd を使用するため、docker build でビルドした image は docker save myapp:0.1 | sudo k3s ctr images import - を実行するまで k3s から参照できません。両者はそれぞれ独自の iptables ルールと bridge network を作成します。Docker、k3s、コンテナを合わせたメモリ使用量を確認してください。2 GB のサーバーには収まりません。

k3s を完全に削除するにはどうすればよいですか?

server node では sudo /usr/local/bin/k3s-uninstall.sh を、agent では sudo /usr/local/bin/k3s-agent-uninstall.sh を実行します。サービスを停止し、datastore を削除し、/var/lib/rancher/k3s/storage 配下の persistent volume データを削除して、同梱ツールを削除します。元に戻すことはできないため、残したいデータは事前にサーバー外へコピーしてください。残った cni0 または flannel.1 の network interface は、次回の再起動時に消えます。

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