VPS 단일 노드 k3s 설치, 과연 효율적일까?
단일 VPS에서 k3s를 운영할 때 발생하는 RAM 점유율과 기본 설치 시 발생하는 포트 80 충돌 문제를 분석합니다. Kubernetes API가 반드시 필요한 상황과 Docker Compose가 더 적합한 경우를 구분하여 효율적인 인프라 선택 기준을 제시합니다.
k3s의 정의와 단일 노드 구성의 이점
k3s는 단일 바이너리로 패키징된 완전한 Kubernetes 배포판입니다. 단일 VPS에서 이를 실행하면 3대의 서버로 구성된 컨트롤 플레인 없이도 실제 Kubernetes API를 사용할 수 있습니다. k3s는 인증된 Kubernetes 배포판이므로, 여기서 적용한 매니페스트는 나중에 관리형 클러스터에서도 그대로 사용할 수 있습니다. 설치는 단 하나의 명령어로 약 1분 안에 완료됩니다. 이 과정에서 애플리케이션이 사용할 수 있는 메모리 자원을 일부 점유하게 되며, Docker Compose에서는 발생하지 않는 새로운 유형의 장애 상황을 관리해야 한다는 점은 고려해야 합니다.
SUSE는 엣지 사이트와 소규모 설치 환경을 위해 k3s를 빌드하며, 업스트림 Kubernetes와 차이가 나는 모든 부분은 경량화를 목적으로 합니다. 기본 데이터 저장소는 etcd 대신 kine이라는 shim을 거치는 sqlite를 사용하므로 etcd 쿼럼을 유지할 필요가 없습니다. containerd는 별도로 설치할 필요 없이 바이너리에 내장되어 있습니다. 동일한 바이너리에 클러스터 DNS를 위한 CoreDNS, 인그레스 컨트롤러인 Traefik, 클라우드 제공자 없이도 LoadBalancer 서비스를 작동하게 하는 ServiceLB(klipper-lb라고도 함), 영구 볼륨을 위한 local-path provisioner, metrics-server, 그리고 파드 네트워킹을 위한 flannel이 포함되어 있습니다. 이 모든 구성 요소는 기본적으로 실행됩니다. 바로 이 점 때문에, 이미 다른 서비스를 운영 중인 VPS에서 아래에 설명할 포트 충돌이 가장 흔하게 발생하는 첫 번째 문제입니다.
단일 k3s 노드가 유용한 경우
이 규칙을 따르십시오. Kubernetes API가 필요한 경우 k3s를 실행하십시오. 직접 제어하는 머신에서 Kubernetes를 학습 중이거나, 사용하려는 소프트웨어가 Helm 차트로만 배포되는 경우가 이에 해당합니다. 매니페스트의 이식성 또한 중요합니다. 여기서 작성한 Deployment는 관리형 클러스터로 변경 없이 그대로 옮길 수 있기 때문입니다. 애플리케이션 자체가 목적이라면 Docker Compose를 실행하십시오. Compose는 훨씬 적은 구성 요소로 동일한 컨테이너를 시작하며, VPS의 Compose 파일은 1년 뒤에 다시 보더라도 매니페스트 디렉터리보다 읽기 쉽습니다.
단일 노드가 제공하지 못하는 점을 명확히 이해해야 합니다.
- 고가용성(High availability)이 없습니다. VPS가 재부팅되면 모든 워크로드가 중단됩니다. Kubernetes는 파드를 다른 노드로 재스케줄링하려 하지만, 다른 노드가 존재하지 않습니다.
- 애플리케이션이 한 머신에서 동일한 볼륨을 공유하는 두 개의 레플리카를 허용하지 않는 한, 서비스를 유지하는 롤링 업데이트는 불가능합니다.
- 아래의 local-path 섹션에서 설명하는 이유로 인해, 스토리지는 해당 장비에 고정됩니다.
- 아무것도 배포하지 않더라도 컨트롤 플레인이 약 1GB의 RAM을 점유합니다.
이러한 점들이 k3s를 나쁜 선택으로 만드는 것은 아닙니다. 다만 사람들이 흔히 언급하는 이유인 '신뢰성' 측면에서는 나쁜 선택이 될 수 있습니다. 만약 실제 다중 노드 클러스터를 구축하기 위해 여러 대의 머신이 필요한 것이라면, 그 결정이 우선되어야 합니다. 자체 하드웨어의 Proxmox와 임대 VPS 비교를 통해 노드를 어디서 확보할지 먼저 결정한 뒤, 그 위에서 무엇을 실행할지 k3s로 결정하십시오.
애플리케이션 배포 전 k3s의 RAM 및 CPU 점유율
k3s 프로젝트는 추정치가 아닌 측정된 수치를 공개합니다. 사람들이 흔히 인용하는 수치는 k3s의 유휴 상태 점유율이 아니므로 주의 깊게 읽어야 합니다.
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 MB의 RAM과 단일 코어의 약 6 퍼센트를 사용했습니다. 이는 이 가이드의 측정값이 아닌 공식 공개 수치이며, 테스트는 k3s v1.26.5 버전에 모든 패키지 구성 요소를 활성화하고 Prometheus 및 Grafana 모니터링 스택을 포함하여 실행되었습니다. 즉, 이 수치에는 빈 클러스터가 아닌 실제 워크로드가 포함되어 있습니다. sqlite를 내장 etcd로 교체하면 1,606 MB로 증가합니다. 컨트롤 플레인 없이 kubelet과 containerd만 실행하는 에이전트 노드는 275 MB를 사용했습니다. 문서화된 서버 최소 사양은 2코어 및 2 GB RAM이며, 이 최소 사양은 사용자의 워크로드를 실행하기 전 k3s와 그 패키지 구성 요소들을 위한 것입니다.
실질적인 해석은 다음과 같습니다. 2 GB VPS에서는 컨트롤 플레인과 번들로 제공되는 애드온이 시스템 자원을 대부분 점유하므로, 부하가 발생하면 kubelet이 가장 먼저 파드를 제거(evict)하게 됩니다. 4 GB는 소규모 서비스 몇 개를 운영하는 단일 노드에 적합한 최소 사양입니다. 이 수치를 포함하여 공개된 어떤 수치도 맹신하지 말고, 반드시 본인의 서버에서 직접 측정하십시오.
free -h
sudo k3s kubectl top node
sudo k3s kubectl get pods -A설치 전과 kube-system의 모든 파드 상태가 Running이 된 후 각각 free -h을 실행하십시오. 그 차이가 본인의 하드웨어에서 컨트롤 플레인이 점유하는 비용입니다. k3s kubectl top node은 설치 후 1~2분 동안 error: Metrics API not available를 반환하는데, 이는 metrics-server가 아직 데이터를 수집하지 않았기 때문입니다. 이는 오류가 아닙니다. 이 서버에서 k3s와 다른 작업을 동시에 수행하기 위해 자원을 산정한다면, VPS의 RAM 및 CPU 산정에 기재된 계산 방식을 그대로 적용하면 됩니다.
특정 릴리스 버전으로 k3s 고정 설치하기
모두가 복사해서 사용하는 퀵 스타트 명령어는 실행하는 시점에 stable 채널이 가리키는 버전을 설치합니다. 운영 환경으로 사용할 서버라면 버전을 고정해야 합니다. k3s는 Kubernetes 마이너 버전별로 채널을 제공하므로, INSTALL_K3S_CHANNEL=v1.36를 사용하면 v1.36 내부의 패치 릴리스만 적용되며 예기치 않게 마이너 버전이 올라가지 않습니다. 2026년 8월 기준 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 -Akubectl get node을 실행하면 약 30초 이내에 STATUS가 Ready인 노드가 하나 표시되어야 하며, kube-system의 모든 파드는 Running 또는 Completed 상태에 도달해야 합니다. 노드가 NotReady 상태에서 멈춰 있다면 보통 컨테이너 런타임이 시작되지 않은 것이므로 sudo journalctl -u k3s -n 100 --no-pager를 확인하십시오. 특이한 VPS 이미지를 사용하는 경우, 다른 로그를 디버깅하기 전에 sudo k3s check-config을 먼저 실행하십시오. 이 명령어는 누락된 커널 기능을 보고하므로 로그를 읽는 것보다 훨씬 빠르게 원인을 파악할 수 있습니다.
포트 80이 이미 사용 중인 이유와 해결을 위해 포기해야 할 것
이 문제는 이미 무언가를 서비스 중인 VPS에서 흔히 발생하는 실패 사례입니다. 설치는 성공하지만, Traefik은 주소를 할당받지 못합니다. 기존에 운영하던 사이트는 계속 작동하므로 인그레스(Ingress)에 접근하기 전까지는 아무런 문제가 없는 것처럼 보입니다.
작동 원리는 다음과 같습니다. 번들로 제공되는 Traefik 차트는 포트 80과 443에서 LoadBalancer 타입의 서비스를 생성합니다. ServiceLB는 svclb- 접두사가 붙은 작은 파드들의 DaemonSet을 생성하여 이 요청에 응답하며, 각 노드에서 해당 포트 번호를 hostPort로 점유합니다. hostPort는 docker run -p 80:80가 하는 것과 정확히 동일하게 컨테이너 포트를 노드의 네트워크 네임스페이스에 직접 게시합니다. 만약 nginx, Caddy, Apache 또는 다른 컨테이너가 이미 포트 80을 점유하고 있다면, 커널은 이를 중복으로 할당하지 않으므로 스케줄러는 파드를 배치할 곳을 찾지 못합니다.
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/TCPkubectl -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 전용 서버로 사용하고 기존에 서비스하던 모든 것을 인그레스 뒤로 옮길 계획이라면 이 방법이 올바른 선택입니다.
ServiceLB를 비활성화하고 기존 프록시를 유지합니다. --disable=servicelb 옵션을 사용하여 설치합니다. LoadBalancer 타입의 서비스는 여전히 NodePort를 할당하므로, Traefik은 31480과 같은 높은 포트에서 접근 가능하며, nginx나 Caddy는 127.0.0.1:31480로 프록시를 전달합니다. 이 경우 포기해야 할 것은 외부 주소입니다. 서비스는 영원히 <pending> 상태를 보고하는데, 이는 사용자가 선택한 결과임에도 불구하고 오류처럼 보일 수 있습니다.
Traefik을 비활성화하고 직접 구성한 프록시로 라우팅합니다. --disable=traefik 옵션을 사용하여 설치합니다. 이 경우 인그레스 컨트롤러가 없으므로 인그레스 객체는 아무런 동작을 하지 않습니다. 즉, API에는 존재하지만 이를 감시하는 컨트롤러가 없습니다. 호스트 프록시에서 NodePort로 라우팅하는 방식을 선호하고 HTTP 처리 방식을 이미 잘 알고 있다면 이 방법이 정직한 선택입니다. 무엇을 전면에 배치할지 결정하지 못했다면, 무언가를 비활성화하기 전에 리버스 프록시로서의 nginx, Caddy, Traefik 간의 선택을 먼저 결정하십시오.
두 플래그 모두 설치 프로그램 실행 시 사용하거나 설정 파일에 추가할 수 있습니다.
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는 local-path이라는 기본 StorageClass를 제공하며, 이는 Rancher의 local-path provisioner를 기반으로 합니다. storageClassName이 지정되지 않은 PersistentVolumeClaim은 이 StorageClass를 사용합니다. 볼륨은 노드 자체 디스크의 /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는 파드가 실제로 볼륨을 마운트하기 전까지 Pending 상태로 유지됩니다. kubectl describe pvc을 실행하면 다음과 같이 출력됩니다.
waiting for first consumer to be created before binding이는 정상적인 동작이므로, PVC만 단독으로 생성하고 기다려도 Pending 상태는 해제되지 않습니다.
일단 바인딩이 완료되면, 해당 볼륨은 볼륨을 생성한 노드에 대한 노드 어피니티(node affinity)를 갖게 됩니다. 이로 인해 해당 클레임을 사용하는 모든 파드는 볼륨의 수명 동안 해당 노드에 고정됩니다. 단일 노드 환경에서는 이를 체감하기 어렵습니다. 나중에 두 번째 노드를 추가하면 파드가 이동을 거부하는 현상이 발생하는데, 이는 kubectl get pv -o yaml을 실행하여 nodeAffinity에 호스트 이름이 명시된 것을 확인하기 전까지는 스케줄러 버그처럼 보일 수 있습니다.
백업은 사용자의 책임입니다. VPS를 재구성하면 해당 디렉터리는 삭제되며, 아래의 제거 스크립트를 실행해도 마찬가지입니다. /var/lib/rancher/k3s/storage 경로를 백업하십시오. 또한 /var/lib/rancher/k3s/server/db/state.db에 위치한 sqlite 데이터 저장소는 라이브 데이터베이스이므로, 서비스를 중지한 상태에서 복사해야 합니다. 다른 방법으로는 클러스터를 일회성으로 간주하고 모든 매니페스트를 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 하나를 사용합니다. 2026년 8월 기준 최신 버전은 v1.21.1입니다.
sudo k3s kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yamlapiVersion: 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: traefikHTTP-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-tls 및 sudo k3s kubectl get order,challenge -A을 사용하여 발급 과정을 모니터링하십시오.
kubeconfig와 API 서버를 비공개로 유지하는 이유
k3s는 관리자 자격 증명을 /etc/rancher/k3s/k3s.yaml에 기록합니다. 이 파일은 기본적으로 root 소유이며 600 모드로 생성됩니다. 이 파일에는 cluster-admin 권한을 가진 클라이언트 인증서가 포함되어 있으므로, 파일을 읽을 수 있는 사람은 누구나 클러스터의 제어권을 갖게 됩니다.
sudo k3s kubectl get node
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodeKUBECONFIG가 설정되지 않은 상태에서 별도로 설치된 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을 권장하는 경우를 볼 수 있습니다. 이 명령이 수행하는 작업을 정확히 이해해야 합니다. 이 명령은 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는 상시 공격 대상이며, 노출된 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에는 목록이 나타나더라도 파드는 ErrImagePull 오류로 실패합니다. 이미지를 명시적으로 가져와야 합니다:
docker save myapp:0.1 | sudo k3s ctr images import -
sudo k3s crictl images그런 다음 해당 컨테이너에서 :latest 태그를 피하십시오. :latest는 기본적으로 Always의 imagePullPolicy를 사용하며 kubelet이 어쨌든 레지스트리로 이동하기 때문입니다. 다른 태그는 기본적으로 IfNotPresent를 사용하며, 이는 가져온 이미지를 사용합니다. 한 대의 VPS에서 두 가지를 모두 실행하는 것은 가능하며, VPS의 일반적인 Docker 설치와 k3s가 같은 머신에서 각각 고유한 이미지 저장소와 iptables 규칙을 유지한다는 점을 알아두면 도움이 됩니다.
k3s 제거 방법
설치 프로그램은 제거 스크립트를 생성합니다. 부분 제거는 불가능하며, 실행 시 되돌릴 수 없습니다.
sudo /usr/local/bin/k3s-uninstall.sh이 스크립트는 서비스를 중지 및 제거하고, 데이터 저장소를 삭제하며, 영구 볼륨 데이터를 삭제하고, 노드 구성을 제거하며, 설치 프로그램이 추가한 도구들을 삭제합니다. 에이전트 노드에서는 스크립트가 k3s-agent-uninstall.sh입니다. /var/lib/rancher/k3s/storage 아래의 모든 항목은 해당 디렉터리가 함께 삭제되므로, 먼저 서버 외부로 복사해 두어야 합니다. 작업 후에는 ip link show 및 sudo ss -lntp 명령어를 사용하여 포트나 인터페이스를 점유하고 있는 프로세스가 없는지 확인하십시오. 남아 있는 cni0 또는 flannel.1 인터페이스는 다음 재부팅 시 정리됩니다.
Kubernetes 노드 하나를 운영하는 것이 작업 규모에 비해 과도한 구성이라고 판단하는 것은 실패가 아니라 일반적인 결과입니다. 해당 워크로드를 다시 Compose로 이전하는 작업은 보통 오후 한나절이면 충분합니다.
실패 유형과 표시되는 문자열
Node NotReady 상태이거나 k3s가 반복적으로 재시작되는 경우. 먼저 sudo journalctl -u k3s -n 200 --no-pager를 읽어 보십시오. 소규모 VPS에서 흔히 발생하는 원인은 커널의 out-of-memory killer가 프로세스를 종료시키는 것이며, 이는 dmesg에 k3s-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. 노드가 접근할 수 있는 레지스트리에 해당 태그가 존재하지 않거나, Docker로 이미지를 빌드한 후 containerd로 가져오지 않은 경우입니다.
Traefik은 응답하지만 애플리케이션이 응답하지 않는 경우. 404 page not found이라는 응답 본문은 Traefik 자체에서 오는 것이며, 요청은 도달했으나 일치하는 라우터가 없음을 의미합니다. Ingress의 host이 입력한 이름과 일치하는지, 그리고 ingressClassName가 traefik으로 설정되어 있는지 확인하십시오.
호스트의 도메인 해석은 정상인데 클러스터 DNS가 실패하는 경우. sudo k3s kubectl -n kube-system logs -l k8s-app=kube-dns로 CoreDNS를 확인하십시오. plugin/loop: Loop ... detected for zone "."와 같은 메시지가 나타나면 CoreDNS가 시작되지 않습니다. 이는 CoreDNS가 자신에게 다시 전달하는 리졸버로 요청을 포워딩하기 때문에 발생하며, /etc/resolv.conf에 루프백 주소가 포함되어 있을 때 나타나는 현상입니다. --resolv-conf /run/systemd/resolve/resolv.conf을 사용하여 k3s가 실제 업스트림 파일을 참조하도록 설정하십시오.
FAQ
단일 VPS에서 k3s를 운영할 가치가 있습니까?
Kubernetes API가 필요한 경우라면 가치가 있습니다. 제어 가능한 환경에서 학습하거나, 배포 방식을 매니페스트 형태로 유지하거나, Helm 차트로만 제공되는 소프트웨어를 실행하거나, 향후 관리형 클러스터로 이전할 프로젝트를 구축하는 경우가 이에 해당합니다. 단순히 컨테이너 실행이 목적이라면 Docker Compose가 유지보수 측면에서 훨씬 유리하며, 약 1 GB 정도의 RAM을 더 확보할 수 있으므로 권장하지 않습니다. 단일 노드 구성은 고가용성을 제공하지 않으므로, 신뢰성 확보를 위해 선택하는 것은 적절하지 않습니다.
k3s의 LoadBalancer 서비스가 왜 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에서 운영하려면 RAM이 얼마나 필요합니까?
공식 문서상 서버 노드의 최소 사양은 2 코어 및 2 GB이며, 이는 워크로드를 제외한 k3s와 기본 포함 구성 요소를 위한 용량입니다. 프로젝트 자체 프로파일링 결과, 모니터링 스택을 포함한 서버 노드는 1,596 MB를 점유하므로 2 GB를 하한선으로 보고, 4 GB부터 단일 노드 운영이 원활하다고 판단하십시오. 설치 전과 kube-system의 모든 파드가 Running 상태가 된 후에 free -h를 사용하여 직접 메모리 사용량을 측정하십시오.
동일한 VPS에서 Docker와 k3s를 함께 실행할 수 있습니까?
네, 가능하며 두 서비스는 별도로 동작합니다. k3s는 내장된 containerd를 사용하므로 docker build로 빌드한 이미지는 docker save myapp:0.1 | sudo k3s ctr images import -를 실행하기 전까지 k3s에서 보이지 않습니다. 또한 각각 고유한 iptables 규칙과 브리지 네트워크를 생성합니다. Docker와 k3s, 그리고 컨테이너들이 사용하는 총 메모리 양을 주의하십시오. 2 GB 사양의 서버에서는 모두 수용하기 어렵습니다.
k3s를 완전히 삭제하려면 어떻게 해야 합니까?
서버 노드에서는 sudo /usr/local/bin/k3s-uninstall.sh을, 에이전트 노드에서는 sudo /usr/local/bin/k3s-agent-uninstall.sh을 실행하십시오. 이 명령은 서비스를 중지하고, 데이터 저장소를 삭제하며, /var/lib/rancher/k3s/storage 하위의 영구 볼륨 데이터를 삭제하고, 번들로 제공된 도구들을 제거합니다. 되돌릴 수 없는 작업이므로 보관해야 할 데이터는 미리 서버 외부로 복사하십시오. 남아 있는 cni0 또는 flannel.1 네트워크 인터페이스는 다음 재부팅 시 자동으로 사라집니다.