서비스 디버깅하기
서비스 디버깅하기 (Debug Services)
새로운 쿠버네티스 설치에서 꽤 자주 발생하는 문제는 Service가 제대로 동작하지 않는 것입니다. Deployment(또는 다른 워크로드 컨트롤러)를 통해 파드를 실행하고 Service를 만들었는데, 접근하려 하면 응답이 없어요. 이 문서가 무엇이 잘못되었는지 파악하는 데 도움이 되길 바랍니다.
출처: 문서
본문
파드에서 명령 실행하기
이곳의 많은 단계에서 클러스터에서 실행 중인 파드가 무엇을 보는지 확인하고 싶을 거예요. 가장 간단한 방법은 대화형 busybox 파드를 실행하는 것입니다:
kubectl run -it --rm --restart=Never busybox --image=registry.k8s.io/busybox:1.27.2 sh
명령 프롬프트가 보이지 않으면 엔터를 눌러보세요.
이미 실행 중인 파드가 있고 그것을 사용하고 싶다면, 다음으로 그 안에서 명령을 실행할 수 있어요:
kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>
설정
이 실습의 목적을 위해 몇 가지 파드를 실행해 보겠습니다. 자신의 Service를 디버깅하고 있다면 자신의 세부 정보로 바꿔도 되고, 따라 하면서 두 번째 데이터 포인트를 얻어도 됩니다.
kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created
kubectl 명령은 생성되거나 변경된 리소스의 유형과 이름을 출력하며, 이것은 이후 명령에서 사용할 수 있습니다.
Deployment를 3개의 replica로 스케일링해 보겠습니다.
kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled
이것은 다음 YAML로 Deployment를 시작한 것과 같다는 점에 주의하세요:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 3
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: registry.k8s.io/serve_hostname
"app" 라벨은 kubectl create deployment가 Deployment의 이름으로 자동 설정합니다.
파드가 실행 중인지 확인할 수 있어요:
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 2m
hostnames-632524106-ly40y 1/1 Running 0 2m
hostnames-632524106-tlaok 1/1 Running 0 2m
파드가 서빙하고 있는지도 확인할 수 있어요. 파드 IP 주소 목록을 가져와 직접 테스트할 수 있습니다.
kubectl get pods -l app=hostnames \
-o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7
이 실습에 사용하는 예시 컨테이너는 9376 포트에서 HTTP로 자신의 호스트네임을 서빙하지만, 자신의 앱을 디버깅한다면 파드가 듣고 있는 포트 번호를 사용해야 합니다.
파드 안에서:
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
이것은 대략 다음을 생성해야 합니다:
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
이 시점에서 기대하는 응답을 얻지 못한다면, 파드가 정상이 아니거나 생각하는 포트에서 듣고 있지 않을 수 있어요. kubectl logs가 무슨 일이 일어나는지 보는 데 유용할 수 있고, kubectl exec로 직접 파드에 들어가 그곳에서 디버깅해야 할 수도 있습니다.
지금까지 모든 것이 계획대로 진행됐다고 가정하면, Service가 왜 동작하지 않는지 조사하기 시작할 수 있어요.
Service가 존재하나요?
눈치채신 분이 있겠지만 아직 실제로 Service를 만들지 않았습니다 — 그것은 의도적입니다. 이 단계는 때때로 잊혀지는 단계이며, 가장 먼저 확인해야 할 것입니다.
존재하지 않는 Service에 접근하려 하면 어떻게 될까요? 이 Service를 이름으로 소비하는 다른 파드가 있다면 다음과 같은 결과를 얻습니다:
wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'
가장 먼저 확인할 것은 그 Service가 실제로 존재하는지입니다:
kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found
Service를 만들어 봅시다. 이전과 마찬가지로 실습용이며, 여기에 자신의 Service 세부 정보를 사용할 수 있습니다.
kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed
그리고 다시 읽어봅니다:
kubectl get svc hostnames
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.0.1.175 <none> 80/TCP 5s
이제 Service가 존재한다는 것을 알 수 있습니다.
앞서처럼, 이것은 YAML로 Service를 시작한 것과 같습니다:
apiVersion: v1
kind: Service
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
전체 구성 범위를 강조하기 위해 여기서 만든 Service는 파드와 다른 포트 번호를 사용합니다. 많은 실제 Service에서는 이 값들이 같을 수 있습니다.
대상 파드에 영향을 주는 Network Policy Ingress 규칙이 있나요?
hostnames-* 파드로 들어오는 트래픽에 영향을 줄 수 있는 Network Policy Ingress 규칙을 배포했다면, 그것을 검토해야 합니다.
자세한 내용은 Network Policies를 참조하세요.
Service가 DNS 이름으로 동작하나요?
클라이언트가 Service를 소비하는 가장 흔한 방법 중 하나는 DNS 이름을 통하는 것입니다.
같은 Namespace의 파드에서:
nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
이것이 실패한다면, 파드와 Service가 다른 Namespace에 있을 수 있으니 네임스페이스로 한정된 이름을 시도해보세요 (역시 파드 안에서):
nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
이것이 동작한다면, 앱을 크로스-네임스페이스 이름을 사용하도록 조정하거나, 앱과 Service를 같은 Namespace에서 실행해야 합니다. 이것도 실패한다면 정규화된 이름(fully-qualified name)을 시도해보세요:
nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local
여기서 접미사 "default.svc.cluster.local"에 주의하세요. "default"는 작업 중인 Namespace이고, "svc"는 이것이 Service임을 나타내며, "cluster.local"은 클러스터 도메인으로 자신의 클러스터에서는 다를 수 있습니다.
클러스터의 노드에서도 시도해볼 수 있습니다:
10.0.0.10은 클러스터의 DNS Service IP로, 자신의 것은 다를 수 있습니다.
nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server: 10.0.0.10
Address: 10.0.0.10#53
Name: hostnames.default.svc.cluster.local
Address: 10.0.1.175
정규화된 이름 조회는 가능한데 상대 이름(relative name)은 불가능하다면, 파드의 /etc/resolv.conf 파일이 올바른지 확인해야 합니다. 파드 안에서:
cat /etc/resolv.conf
다음과 같은 것을 볼 수 있어야 합니다:
nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5
nameserver 줄은 클러스터의 DNS Service를 가리켜야 합니다. 이것은 --cluster-dns 플래그로 kubelet에 전달됩니다.
search 줄은 Service 이름을 찾기 위한 적절한 접미사를 포함해야 합니다. 이 경우 로컬 Namespace의 Service("default.svc.cluster.local"), 모든 Namespace의 Service("svc.cluster.local"), 마지막으로 클러스터의 이름("cluster.local")을 찾고 있습니다. 설치에 따라 그 뒤에 추가 레코드(최대 6개)가 있을 수 있습니다. 클러스터 접미사는 --cluster-domain 플래그로 kubelet에 전달됩니다. 이 문서 전체에서 클러스터 접미사는 "cluster.local"로 가정합니다. 자신의 클러스터는 다르게 구성될 수 있으므로, 이전 명령 모두에서 그것을 바꿔야 할 수 있습니다.
options 줄은 DNS 클라이언트 라이브러리가 검색 경로를 전혀 고려하지 않도록 ndots를 충분히 높게 설정해야 합니다. 쿠버네티스는 이를 기본적으로 5로 설정하며, 이는 생성하는 모든 DNS 이름을 포함할 만큼 충분히 높습니다.
어떤 Service든 DNS 이름으로 동작하나요? {#does-any-service-exist-in-dns}
위의 것도 실패한다면, DNS 조회가 Service에 대해 동작하지 않는 것입니다. 한 발 물러서서 다른 무엇이 동작하지 않는지 볼 수 있어요. 쿠버네티스 마스터 Service는 항상 동작해야 합니다. 파드 안에서:
nslookup kubernetes.default
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
이것이 실패한다면, 이 문서의 kube-proxy 섹션을 보거나, 문서의 맨 위로 돌아가서 새로 시작하되 자신의 Service 대신 DNS Service를 디버깅하세요.
Service가 IP로 동작하나요?
DNS가 동작함을 확인했다고 가정하면, 다음 테스트할 것은 Service가 IP 주소로 동작하는지입니다. 클러스터의 파드에서 Service의 IP(위 kubectl get에서)에 접근합니다.
for i in $(seq 1 3); do
wget -qO- 10.0.1.175:80
done
이것은 대략 다음을 생성해야 합니다:
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
Service가 동작한다면 올바른 응답을 받아야 합니다. 그렇지 않다면 잘못될 수 있는 여러 가지가 있습니다. 계속 읽어보세요.
Service가 올바르게 정의되었나요?
우스워 보일 수 있지만 Service가 올바르고 파드의 포트와 일치하는지 정말로 여러 번 확인해야 합니다. Service를 다시 읽고 검증하세요:
kubectl get service hostnames -o json
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "hostnames",
"namespace": "default",
"uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
"resourceVersion": "347189",
"creationTimestamp": "2015-07-07T15:24:29Z",
"labels": {
"app": "hostnames"
}
},
"spec": {
"ports": [
{
"name": "default",
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 0
}
],
"selector": {
"app": "hostnames"
},
"clusterIP": "10.0.1.175",
"type": "ClusterIP",
"sessionAffinity": "None"
},
"status": {
"loadBalancer": {}
}
}
- 접근하려는 Service 포트가
spec.ports[]에 나열되어 있나요? targetPort가 파드에 맞나요 (일부 파드는 Service와 다른 포트를 사용한다)?- 숫자 포트를 의도했다면, 숫자(9376)인가요 문자열 "9376"인가요?
- 이름 있는 포트를 의도했다면, 파드가 같은 이름의 포트를 노출하나요?
- 포트의
protocol이 파드에 맞나요?
Service에 EndpointSlice가 있나요?
여기까지 왔다면 Service가 올바르게 정의되고 DNS로 해석된다는 것을 확인한 것입니다. 이제 실행한 파드가 실제로 Service에 선택되는지 확인해 보겠습니다.
이전에 파드가 실행 중임을 보았습니다. 다시 확인할 수 있어요:
kubectl get pods -l app=hostnames
NAME READY STATUS RESTARTS AGE
hostnames-632524106-bbpiw 1/1 Running 0 1h
hostnames-632524106-ly40y 1/1 Running 0 1h
hostnames-632524106-tlaok 1/1 Running 0 1h
-l app=hostnames 인자는 Service에 구성된 라벨 선택자입니다.
"AGE" 열은 이 파드들이 약 한 시간 되었다는 것을 말하며, 이는 잘 실행되고 크래시하지 않음을 의미합니다.
"RESTARTS" 열은 이러한 파드가 자주 크래시하거나 재시작되지 않음을 나타냅니다. 잦은 재시작은 간헐적인 연결 문제를 일으킬 수 있습니다. 재시작 횟수가 높다면 파드 디버깅에 대해 더 읽어보세요.
쿠버네티스 시스템 안에는 모든 Service의 선택자를 평가하고 결과를 하나 이상의 EndpointSlice 객체에 저장하는 제어 루프가 있습니다.
kubectl get endpointslices -l kubernetes.io/service-name=hostnames
NAME ADDRESSTYPE PORTS ENDPOINTS
hostnames-ytpni IPv4 9376 10.244.0.5,10.244.0.6,10.244.0.7
이것은 EndpointSlice 컨트롤러가 Service에 올바른 파드를 찾았음을 확인합니다. ENDPOINTS 열이 <none>이라면, Service의 spec.selector 필드가 실제로 파드의 metadata.labels 값을 선택하는지 확인해야 합니다. 흔한 실수는 오타나 다른 오류로, Service가 app=hostnames를 선택하지만 Deployment가 run=hostnames를 지정하는 경우입니다 (1.18 이전 버전에서는 kubectl run 명령이 Deployment를 만드는 데도 사용될 수 있었습니다).
파드가 동작하나요?
이 시점에서 Service가 존재하고 파드를 선택했음을 알 수 있습니다. 실습의 시작 부분에서 파드 자체를 검증했습니다. 파드가 실제로 동작하는지 다시 확인해 보겠습니다 — Service 메커니즘을 우회하고 위 Endpoints에 나열된 대로 파드로 바로 갈 수 있습니다.
이 명령들은 Service 포트(80)가 아닌 파드 포트(9376)를 사용합니다.
파드 안에서:
for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
wget -qO- $ep
done
이것은 대략 다음을 생성해야 합니다:
hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok
엔드포인트 목록의 각 파드가 자신의 호스트네임을 반환할 것으로 기대합니다. 이것이 일어나지 않으면(또는 자신의 파드의 올바른 동작이 아니면), 그곳에서 무슨 일이 일어나는지 조사해야 합니다.
kube-proxy가 동작하나요?
여기까지 왔다면 Service가 실행 중이고 EndpointSlice가 있으며 파드가 실제로 서빙 중입니다. 이 시점에서 Service 프록시 메커니즘 전체가 의심됩니다. 하나씩 확인해 보겠습니다.
Services의 기본 구현이자 대부분의 클러스터에서 사용되는 것은 kube-proxy입니다. 이것은 모든 노드에서 실행되며 Service 추상화를 제공하는 소수의 메커니즘 중 하나를 구성하는 프로그램입니다. 클러스터가 kube-proxy를 사용하지 않는다면 다음 섹션은 적용되지 않으며, 사용 중인 Services 구현을 조사해야 합니다.
kube-proxy가 실행 중인가요?
kube-proxy가 노드에서 실행 중인지 확인합니다. 노드에서 직접 실행하면 아래와 같은 결과를 얻어야 합니다:
ps auxw | grep kube-proxy
root 4194 0.4 0.1 101864 17696 ? Sl Jul04 25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2
다음으로, 마스터에 연락하는 것 같은 명백한 문제에 실패하고 있지 않은지 확인합니다. 이를 위해 로그를 봐야 합니다. 로그에 접근하는 방법은 노드 OS에 따라 다릅니다. 일부 OS에서는 /var/log/kube-proxy.log 같은 파일인 반면, 다른 OS는 journalctl로 로그에 접근합니다. 다음을 볼 수 있어야 합니다:
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163 5063 server.go:247] Using iptables Proxier.
I1027 22:14:54.038140 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209 5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238 5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048 5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
마스터에 연락할 수 없다는 오류 메시지가 보이면 노드 구성과 설치 단계를 다시 확인해야 합니다.
Kube-proxy는 몇 가지 모드 중 하나로 실행될 수 있습니다. 위에 나열된 로그에서 Using iptables Proxier 줄은 kube-proxy가 "iptables" 모드로 실행 중임을 나타냅니다. 가장 흔한 다른 모드는 "ipvs"입니다.
Iptables 모드
"iptables" 모드에서는 노드에서 다음과 같은 것을 볼 수 있어야 합니다:
iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
각 Service의 각 포트에 대해 KUBE-SERVICES에 규칙 1개와 KUBE-SVC-<hash> 체인 하나가 있어야 합니다. 각 파드 엔드포인트에 대해 그 KUBE-SVC-<hash> 안에 규칙 몇 개와 그 안에 규칙 몇 개가 있는 KUBE-SEP-<hash> 체인 하나가 있어야 합니다. 정확한 규칙은 정확한 구성(node-ports와 load-balancers 포함)에 따라 달라집니다.
IPVS 모드
"ipvs" 모드에서는 노드에서 다음과 같은 것을 볼 수 있어야 합니다:
ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
...
TCP 10.0.1.175:80 rr
-> 10.244.0.5:9376 Masq 1 0 0
-> 10.244.0.6:9376 Masq 1 0 0
-> 10.244.0.7:9376 Masq 1 0 0
...
각 Service의 각 포트, 그리고 모든 NodePort, 외부 IP, load-balancer IP에 대해 kube-proxy는 가상 서버를 만듭니다. 각 파드 엔드포인트에 대해 해당하는 실제 서버를 만듭니다. 이 예시에서 service hostnames(10.0.1.175:80)는 3개의 엔드포인트(10.244.0.5:9376, 10.244.0.6:9376, 10.244.0.7:9376)를 가집니다.
kube-proxy가 프록시하고 있나요?
위 사례 중 하나가 보인다고 가정하면, 노드 중 하나에서 IP로 Service에 다시 접근해 보세요:
curl 10.0.1.175:80
hostnames-632524106-bbpiw
이것도 실패한다면 kube-proxy 로그에서 다음과 같은 특정 줄을 찾아보세요:
Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]
그런 줄이 보이지 않으면 -v 플래그를 4로 설정해 kube-proxy를 재시작한 다음 로그를 다시 보세요.
엣지 케이스: 파드가 Service IP를 통해 자신에게 도달하지 못함 {#a-pod-fails-to-reach-itself-via-the-service-ip}
듣기엔 드물게 들릴 수 있지만 실제로 발생하며, 동작해야 하는 것입니다.
이는 네트워크가 "헤어핀(hairpin)" 트래픽에 대해 제대로 구성되지 않았을 때 발생할 수 있습니다. 보통 kube-proxy가 iptables 모드로 실행되고 파드가 브리지 네트워크로 연결되었을 때 그렇습니다. Kubelet은 hairpin-mode 플래그를 노출하며, 이는 Service의 엔드포인트가 자신의 Service VIP에 접근하려고 할 때 자신에게 로드밸런스할 수 있게 해줍니다. hairpin-mode 플래그는 hairpin-veth 또는 promiscuous-bridge로 설정되어야 합니다.
이것을 문제 해결하는 일반적인 단계는 다음과 같습니다:
hairpin-mode가hairpin-veth또는promiscuous-bridge로 설정되었는지 확인합니다. 아래와 같은 것을 볼 수 있어야 합니다. 다음 예시에서는hairpin-mode가promiscuous-bridge로 설정되어 있습니다.
ps auxw | grep kubelet
root 3392 1.1 0.8 186804 65208 ? Sl 00:51 11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
- 실제(effective)
hairpin-mode를 확인합니다. 이를 위해 kubelet 로그를 봐야 합니다. 로그에 접근하는 방법은 노드 OS에 따라 다릅니다. 일부 OS에서는 /var/log/kubelet.log 같은 파일인 반면, 다른 OS는journalctl로 로그에 접근합니다. 호환성 때문에 실제 헤어핀 모드가--hairpin-mode플래그와 일치하지 않을 수 있다는 점에 유의하세요. kubelet.log에서hairpin키워드가 있는 로그 줄이 있는지 확인하세요. 아래와 같이 실제 헤어핀 모드를 나타내는 로그 줄이 있어야 합니다.
I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
- 실제 헤어핀 모드가
hairpin-veth라면,Kubelet이 노드에서/sys로 작업할 권한이 있는지 확인하세요. 모든 것이 제대로 동작한다면 다음과 같은 것을 볼 수 있어야 합니다:
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
- 실제 헤어핀 모드가
promiscuous-bridge라면,Kubelet이 노드에서 linux bridge를 조작할 권한이 있는지 확인하세요.cbr0브리지를 사용하고 올바르게 구성되면 다음을 볼 수 있어야 합니다:
ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1
- 위 중 아무것도 해결되지 않으면 도움을 구하세요.
도움 구하기
여기까지 왔다면 아주 이상한 일이 일어나고 있는 것입니다. Service가 실행 중이고 EndpointSlice가 있으며 파드가 실제로 서빙 중입니다. DNS가 동작하고 kube-proxy도 이상하게 행동하지 않는 것 같습니다. 그런데도 Service가 동작하지 않습니다. 무슨 일이 일어나는지 알려주시면 조사에 도움을 드릴 수 있습니다!
Slack 또는 Forum 또는 GitHub로 연락하세요.
더 알아보기 (Learn more)
자세한 정보는 문제 해결 개요 문서를 방문하세요.