DevOps
K3s kubeconfig: kubectl과 k9s 다중 클러스터 접근 설정
K3s server node의 kubeconfig를 로컬에 등록해 kubectl과 k9s에서 여러 Kubernetes cluster context를 전환하는 정석적인 설정 절차를 정리한다.
- Published
- Updated
- Area
- Kubernetes
- Type
- runbook
- Category
- DevOps
로컬 환경에서 여러 Kubernetes cluster에 접근할 때 핵심은 k9s 설정이 아니라 kubeconfig를 정리하는 것이다. kubectl이 원하는 cluster로 정상 동작하면, k9s도 같은 kubeconfig와 context를 사용해 동작한다.
즉, 접근 흐름은 다음과 같다.
~/.kube/config
└─ clusters / users / contexts / current-context
↓
kubectl get nodes
↓
k9s
기본 원리
kubectl과 k9s는 Kubernetes API Server에 직접 붙는다. 이때 API Server 주소, 인증서, client certificate, context 정보는 kubeconfig에 들어 있다.
kubeconfig는 크게 네 부분으로 이해하면 된다.
| 항목 | 역할 | 예시 |
|---|---|---|
clusters | 어느 Kubernetes API Server로 접속할지 정의 | https://<K3S_API_ENDPOINT>:6443 |
users | 어떤 인증 정보로 접속할지 정의 | client-certificate-data, client-key-data |
contexts | cluster와 user를 묶은 접속 프로파일 | prod-k3s |
current-context | 기본으로 사용할 context | prod-k3s |
context는 단순한 이름이 아니라 다음 조합이다.
context = cluster + user + optional namespace
따라서 kubectl config use-context <context-name>으로 context를 바꾸면 kubectl과 k9s가 바라보는 cluster도 함께 바뀐다.
K3s 기본 kubeconfig의 특징
K3s server node에는 보통 다음 파일이 있다.
/etc/rancher/k3s/k3s.yaml
이 파일은 k3s cluster에 접근하기 위한 admin kubeconfig다. 기본 형태는 대략 다음과 같다.
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: <CA_DATA>
server: https://127.0.0.1:6443
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
users:
- name: default
user:
client-certificate-data: <CLIENT_CERT_DATA>
client-key-data: <CLIENT_KEY_DATA>
여기서 주의할 점은 두 가지다.
server: https://127.0.0.1:6443은 k3s server node 자기 자신에서만 올바른 주소다.cluster,user,context이름이 모두default이기 때문에 여러 k3s cluster를 병합하면 이름 충돌이 발생할 수 있다.
따라서 원격 접근용으로 가져올 때는 반드시 다음을 수정해야 한다.
| 수정 대상 | 기본값 | 수정 예시 | 이유 |
|---|---|---|---|
clusters[].name | default | prod-k3s | cluster 이름 충돌 방지 |
users[].name | default | prod-k3s-admin | user 이름 충돌 방지 |
contexts[].name | default | prod-k3s | context 이름 충돌 방지 |
contexts[].context.cluster | default | prod-k3s | 변경한 cluster 이름과 연결 |
contexts[].context.user | default | prod-k3s-admin | 변경한 user 이름과 연결 |
clusters[].cluster.server | https://127.0.0.1:6443 | https://<K3S_API_ENDPOINT>:6443 | 로컬에서 원격 API Server로 접속 |
최종 목표 구조
정석적인 다중 cluster 구성은 로컬의 ~/.kube/config 하나에 여러 context를 등록하는 방식이다.
예를 들어 다음과 같은 구조를 목표로 한다.
~/.kube/config
├── local-k3s
├── dev-k3s
├── prod-k3s
└── gpu-k3s
그다음 필요할 때 context를 전환한다.
kubectl config use-context prod-k3s
kubectl get nodes
k9s
또는 k9s에서 직접 context를 바꿀 수 있다.
:ctx
특정 context로 바로 실행하려면 다음처럼 실행한다.
k9s --context prod-k3s
원격 K3s cluster 등록 절차
아래 예시는 원격 k3s cluster를 prod-k3s라는 이름으로 로컬에 등록하는 절차다.
1. kubeconfig 가져오기
원격 k3s server node에서 kubeconfig를 복사한다.
mkdir -p ~/.kube/clusters
scp <USER>@<K3S_SERVER_HOST>:/etc/rancher/k3s/k3s.yaml ~/.kube/clusters/prod-k3s.yaml
<K3S_SERVER_HOST>에는 실제 server 주소를 넣되, 공개 문서나 블로그에는 내부 IP, private hostname, 계정명 등을 남기지 않는다.
2. API Server 주소 수정
가져온 파일을 연다.
vi ~/.kube/clusters/prod-k3s.yaml
기본값은 보통 다음과 같다.
server: https://127.0.0.1:6443
이 값은 로컬 PC 기준으로는 잘못된 주소다. 로컬 PC에서 접근 가능한 k3s API Server endpoint로 바꾼다.
server: https://<K3S_API_ENDPOINT>:6443
만약 외부에서 다른 포트로 노출하고 있다면 kubeconfig에는 외부에서 접근 가능한 포트를 적는다.
server: https://<K3S_API_ENDPOINT>:<EXTERNAL_API_PORT>
예를 들어 외부 포트 <EXTERNAL_API_PORT>가 내부의 6443으로 forwarding되고 있다면 kubeconfig에는 외부 포트를 적는 것이 맞다.
https://<K3S_API_ENDPOINT>:<EXTERNAL_API_PORT>
↓ forwarding
https://<K3S_SERVER_INTERNAL>:6443
이 포트가 실제로 k3s API Server의
6443으로 연결되는지는 확인 필요하다. 단순히 방화벽에서 포트가 열려 있는 것과 Kubernetes API Server로 정상 forwarding되는 것은 다르다.
3. cluster, user, context 이름 수정
기본 default 이름을 cluster별 고유 이름으로 바꾼다.
수정 전:
clusters:
- name: default
cluster:
server: https://<K3S_API_ENDPOINT>:6443
certificate-authority-data: <CA_DATA>
users:
- name: default
user:
client-certificate-data: <CLIENT_CERT_DATA>
client-key-data: <CLIENT_KEY_DATA>
contexts:
- name: default
context:
cluster: default
user: default
current-context: default
수정 후:
clusters:
- name: prod-k3s
cluster:
server: https://<K3S_API_ENDPOINT>:6443
certificate-authority-data: <CA_DATA>
users:
- name: prod-k3s-admin
user:
client-certificate-data: <CLIENT_CERT_DATA>
client-key-data: <CLIENT_KEY_DATA>
contexts:
- name: prod-k3s
context:
cluster: prod-k3s
user: prod-k3s-admin
namespace: default
current-context: prod-k3s
핵심은 다음 연결이 서로 맞아야 한다는 점이다.
contexts:
- name: prod-k3s
context:
cluster: prod-k3s
user: prod-k3s-admin
여기서 cluster: prod-k3s는 clusters[].name을 가리키고, user: prod-k3s-admin은 users[].name을 가리킨다.
4. 단독 kubeconfig로 먼저 검증
병합하기 전에 가져온 kubeconfig 파일만 사용해서 접속을 검증한다.
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml kubectl config get-contexts
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml kubectl config current-context
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml kubectl get nodes
kubectl get nodes가 정상 동작하면 k9s도 같은 kubeconfig로 실행할 수 있다.
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml k9s --context prod-k3s
이 단계에서 정상 동작하지 않으면 ~/.kube/config에 병합하지 말고 먼저 원인을 확인한다.
기본 kubeconfig에 병합하기
단독 kubeconfig로 검증이 끝나면 로컬의 기본 kubeconfig인 ~/.kube/config에 병합한다.
먼저 기존 config를 백업한다.
mkdir -p ~/.kube
cp ~/.kube/config ~/.kube/config.bak.$(date +%Y%m%d-%H%M%S) 2>/dev/null || true
그다음 기존 config와 새 cluster config를 병합한다.
KUBECONFIG=$HOME/.kube/config:$HOME/.kube/clusters/prod-k3s.yaml \
kubectl config view --flatten > /tmp/kubeconfig.merged
mv /tmp/kubeconfig.merged ~/.kube/config
chmod 600 ~/.kube/config
등록된 context를 확인한다.
kubectl config get-contexts
기본 context를 원격 cluster로 바꾼다.
kubectl config use-context prod-k3s
검증한다.
kubectl config current-context
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}{"\n"}'
kubectl get nodes
여기까지 되면 k9s는 별도 설정 없이 실행하면 된다.
k9s
kubeconfig 탐색 우선순위
kubectl이 어떤 kubeconfig를 보는지 헷갈릴 때는 우선순위를 기준으로 확인하면 된다.
| 우선순위 | 방식 | 예시 |
|---|---|---|
| 1 | --kubeconfig 옵션 | kubectl --kubeconfig ~/.kube/clusters/prod-k3s.yaml get nodes |
| 2 | KUBECONFIG 환경변수 | KUBECONFIG=~/.kube/clusters/prod-k3s.yaml kubectl get nodes |
| 3 | 기본 파일 | ~/.kube/config |
따라서 다음 명령이 된다면:
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml kubectl get nodes
하지만 다음 명령이 안 된다면:
kubectl get nodes
대부분의 경우 기본 ~/.kube/config에 해당 context가 등록되어 있지 않거나, current-context가 다른 cluster를 가리키는 상태다.
localhost:8080으로 요청이 가는 경우
kubectl get nodes 실행 시 다음과 비슷한 메시지가 나온다면:
The connection to the server localhost:8080 was refused - did you specify the right host or port?
이는 보통 server: https://127.0.0.1:6443을 잘못 읽은 상황이 아니라, 유효한 kubeconfig를 찾지 못한 상황에 가깝다.
점검 명령은 다음과 같다.
echo "HOME=$HOME"
echo "KUBECONFIG=${KUBECONFIG:-<unset>}"
ls -al ~/.kube
ls -al ~/.kube/config
kubectl config view --raw
확인 포인트는 다음과 같다.
| 확인 항목 | 정상 상태 | 문제 후보 |
|---|---|---|
$HOME | 현재 사용자의 home directory | WSL/Windows/sudo로 home이 달라짐 |
$KUBECONFIG | 비어 있거나 의도한 파일 | 삭제한 줄이 다른 shell에 남아 있음 |
~/.kube/config | 실제 파일 존재 | 파일명이 k3s.yaml, k3s.yml 등으로 남아 있음 |
kubectl config view --raw | clusters/users/contexts 출력 | kubeconfig 미인식 또는 파일 내용 문제 |
특히 sudo kubectl get nodes를 실행하면 일반 사용자 home이 아니라 /root/.kube/config를 볼 수 있으므로 주의한다.
k9s info의 config/plugin 경로와 kubeconfig 구분
k9s info에는 k9s의 config path와 plugin path가 표시된다. 그러나 이 경로는 Kubernetes API 접속 정보를 설정하는 위치가 아니다.
| 경로 | 역할 | API Server 주소 설정 여부 |
|---|---|---|
~/.config/k9s/config.yaml | k9s UI/동작 설정 | 아님 |
~/.config/k9s/plugins.yaml | k9s plugin command 설정 | 아님 |
~/.kube/config | Kubernetes cluster/user/context 설정 | 맞음 |
따라서 k9s가 어느 cluster로 접속하는지는 k9s config가 아니라 kubeconfig의 current-context와 실행 시 지정한 --context에 의해 결정된다.
x509 에러 처리
원격 주소로 바꾸면 다음 에러가 날 수 있다.
x509: certificate is valid for ..., not <K3S_API_ENDPOINT>
이는 접속한 IP, VIP, hostname이 k3s API Server 인증서의 SAN(Subject Alternative Name)에 포함되어 있지 않을 때 발생할 수 있다.
임시 우회
테스트 목적이라면 kubeconfig에 insecure-skip-tls-verify: true를 넣어 인증서 검증을 건너뛸 수 있다.
clusters:
- name: prod-k3s
cluster:
server: https://<K3S_API_ENDPOINT>:6443
insecure-skip-tls-verify: true
이 경우 certificate-authority-data와 함께 두기보다, 임시 테스트용 kubeconfig에서 명확히 분리해두는 편이 좋다.
정석 해결
운영 기준에서는 insecure-skip-tls-verify보다 k3s server 설정에 tls-san을 추가하는 것이 맞다.
# /etc/rancher/k3s/config.yaml
tls-san:
- <K3S_API_ENDPOINT>
- <K3S_API_DNS_NAME>
- <K3S_API_VIP>
그다음 k3s를 재시작한다.
sudo systemctl restart k3s
인증서 재생성 여부와 기존 cluster 영향은 운영 환경에 따라 확인 필요하다.
명령 요약
| 목적 | 명령 |
|---|---|
| 현재 context 확인 | kubectl config current-context |
| context 목록 확인 | kubectl config get-contexts |
| 현재 API Server 주소 확인 | kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}{"\n"}' |
| context 전환 | kubectl config use-context prod-k3s |
| 특정 kubeconfig로 테스트 | KUBECONFIG=~/.kube/clusters/prod-k3s.yaml kubectl get nodes |
| 특정 context로 k9s 실행 | k9s --context prod-k3s |
| k9s 내부 context 전환 | :ctx |
최종 검증 순서
문제가 생겼을 때는 k9s부터 보지 말고 kubectl부터 검증한다.
echo "HOME=$HOME"
echo "KUBECONFIG=${KUBECONFIG:-<unset>}"
kubectl config get-contexts
kubectl config current-context
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}{"\n"}'
kubectl get nodes
위 명령들이 원하는 cluster 기준으로 정상 동작하면 k9s도 동일하게 동작해야 한다.
k9s
만약 다음은 되는데:
KUBECONFIG=$HOME/.kube/clusters/prod-k3s.yaml k9s --context prod-k3s
그냥 k9s는 안 된다면, 원인은 k9s가 아니라 기본 kubeconfig 설정이다. 이 경우 ~/.kube/config에 병합했는지, current-context가 올바른지 확인한다.
정리
원격 k3s cluster 접근의 핵심은 다음이다.
- k3s server node의
/etc/rancher/k3s/k3s.yaml을 가져온다. server: https://127.0.0.1:6443을 로컬에서 접근 가능한https://<K3S_API_ENDPOINT>:<PORT>로 바꾼다.default로 되어 있는cluster,user,context이름을 cluster별 고유 이름으로 바꾼다.- 단독 kubeconfig로
kubectl get nodes를 먼저 검증한다. - 검증된 kubeconfig를
~/.kube/config에 병합한다. kubectl config use-context <context>로 기본 context를 선택한다.kubectl이 정상 동작하면k9s도 같은 설정으로 동작한다.
즉, k9s를 잘 쓰기 위한 출발점은 k9s config가 아니라 kubectl kubeconfig를 정석적으로 관리하는 것이다.