이 문서는 컨테이너화된 애플리케이션의 이슈를 해결하기 위한 자원을 담고 있다. 쿠버네티스 리소스(예: 파드, 서비스, 스테이트풀셋)의 일반적 이슈, 컨테이너 종료 메시지 이해에 대한 조언, 실행 중인 컨테이너를 디버그하는 방법 등을 다룬다.
이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.
애플리케이션 트러블슈팅하기
- 1: 파드 디버깅하기
- 2: 서비스 디버깅하기
- 3: 스테이트풀셋 디버깅하기
- 4: 파드 실패의 원인 검증하기
- 5: 초기화 컨테이너(Init Containers) 디버그하기
- 6: 동작 중인 파드 디버그
- 7: 동작중인 컨테이너의 셸에 접근하기
1 - 파드 디버깅하기
이 가이드는 쿠버네티스에 배포되었지만 제대로 동작하지 않는 애플리케이션을 디버깅하는 방법을 소개한다. 이 가이드는 클러스터 디버깅에 대한 것은 아니다. 클러스터 디버깅에 대해서는 이 가이드를 참고한다.
문제 진단하기
트러블슈팅의 첫 단계는 문제를 파악하는 것이다. 무엇이 문제인가? 파드인가, 레플리케이션 컨트롤러인가, 서비스인가?
파드 디버깅하기
파드 디버깅의 첫 번째 단계는 파드를 살펴 보는 것이다. 다음의 명령어를 사용하여 파드의 현재 상태와 최근 이벤트를 점검한다.
kubectl describe pods ${POD_NAME}
파드 내부 컨테이너의 상태를 확인한다. 모두 Running
상태인가? 최근에 재시작 되었는가?
파드의 상태에 따라 디버깅을 계속한다.
파드가 계속 pending 상태인 경우
파드가 Pending
상태로 멈춰 있는 경우는, 노드에 스케줄 될 수 없음을 의미한다.
일반적으로 이것은 어떤 유형의 리소스가 부족하거나 스케줄링을 방해하는 다른 요인 때문이다.
상단의 kubectl describe ...
명령의 결과를 확인하자.
파드를 스케줄 할 수 없는 사유에 대한 스케줄러의 메세지가 있을 것이다. 다음과 같은 사유가 있을 수 있다.
-
리소스가 부족한 경우: 사용자 클러스터의 CPU 나 메모리가 고갈되었을 수 있다. 이러한 경우, 파드를 삭제하거나, 리소스 요청을 조정하거나, 클러스터에 노드를 추가해야 한다. 컴퓨트 자원 문서에서 더 많은 정보를 확인한다.
-
hostPort
를 사용하고 있는 경우: 파드를hostPort
에 바인딩할 때, 파드가 스케줄링될 수 있는 장소 수 제한이 존재한다. 대부분의 경우hostPort
는 불필요하므로, 파드를 노출하기 위해서는 서비스(Service) 오브젝트 사용을 고려해 본다.hostPort
가 꼭 필요하다면 클러스터의 노드 수 만큼만 파드를 스케줄링할 수 있다.
파드가 계속 waiting 상태인 경우
파드가 Waiting
상태에서 멈춘 경우는, 파드가 워커 노드에 스케줄링되었지만 해당 노드에서 실행될 수 없음을 의미한다.
다시 말하지만, kubectl describe ...
명령은 유용한 정보를 제공한다. 파드가 Waiting
상태에서 멈추는 가장 흔한 원인은 이미지 풀링(pulling)에 실패했기 때문이다. 다음의 3가지 사항을 확인한다.
- 이미지 이름이 올바른지 확인한다.
- 해당 이미지를 저장소에 푸시하였는가?
- 이미지가 풀 될 수 있는지 확인하기 위해 수동으로 이미지를 풀 해본다.
예를 들어, PC에서 도커를 사용하는 경우,
docker pull <image>
명령을 실행한다.
파드가 손상(crashing)되었거나 양호하지 않을(unhealthy) 경우
일단 사용자의 파드가 스케줄 되면, 구동중인 파드 디버그하기에 있는 방법을 사용하여 디버깅을 할 수 있다.
파드가 running 상태이지만 해야 할 일을 하고 있지 않은 경우
파드가 예상과 다르게 동작 중이라면, 파드 상세(예: 로컬 머신에 있는 mypod.yaml
파일)에 에러가 있었는데
파드 생성 시에 에러가 조용히 지나쳐진 경우일 수 있다.
종종 파드 상세의 들여쓰기가 잘못되었거나,
키 이름에 오타가 있어서 해당 키가 무시되는 일이 있을 수 있다.
예를 들어, command
를 commnd
로 잘못 기재했다면
해당 파드는 생성은 되지만 명시한 명령줄을 실행하지 않을 것이다.
가장 먼저 해야 할 일은 파드를 삭제한 다음, --validate
옵션을 사용하여 다시 만들어 보는 것이다.
예를 들어, kubectl apply --validate -f mypod.yaml
를 실행한다.
command
를 commnd
로 잘못 기재했다면 다음과 같은 에러가 발생할 것이다.
I0805 10:43:25.129850 46757 schema.go:126] unknown field: commnd
I0805 10:43:25.129973 46757 schema.go:129] this may be a false alarm, see https://github.com/kubernetes/kubernetes/issues/6842
pods/mypod
다음으로 확인할 것은 apiserver를 통해 확인한 파드 상세가
사용자가 의도한 파드 상세(예: 로컬 머신에 있는 yaml 파일)와 일치하는지 여부이다.
예를 들어, kubectl get pods/mypod -o yaml > mypod-on-apiserver.yaml
를 실행한 다음,
원본 파드 상세(mypod.yaml
)와 apiserver를 통해 확인한 파드 상세(mypod-on-apiserver.yaml
)를 수동으로 비교한다.
보통 원본 버전에는 없지만 "apiserver" 버전에는 있는 줄들이 존재한다.
이는 예상대로이다.
하지만, 원본 버전에는 있지만 "apiserver" 버전에는 없는 줄들이 있다면,
이는 원본 파드 상세에 문제가 있을 수도 있음을 의미한다.
레플리케이션컨트롤러 디버깅하기
레플리케이션컨트롤러의 경우에는 매우 직관적이다. 파드 생성이 가능하거나 또는 불가능한 경우 둘 뿐이다. 레플리케이션컨트롤러가 파드를 생성할 수 없다면, 위의 지침을 참고하여 파드를 디버깅한다.
사용자는 kubectl describe rc ${CONTROLLER_NAME}
을 사용하여
레플리케이션 컨트롤러와 관련된 이벤트를 검사할 수도 있다.
서비스 디버깅하기
서비스는 파드 집합에 대한 로드 밸런싱 기능을 제공한다. 일반적인 몇몇 문제들 때문에 서비스가 제대로 동작하지 않을 수 있다. 다음 지침을 이용하여 서비스 문제를 디버깅할 수 있다.
먼저, 서비스를 위한 엔드포인트가 존재하는지 확인한다. 모든 서비스 오브젝트에 대해, apiserver는 endpoints
리소스를 생성하고 사용 가능한(available) 상태로 만든다.
다음 명령을 사용하여 이 리소스를 볼 수 있다.
kubectl get endpoints ${SERVICE_NAME}
엔드포인트의 수가 해당 서비스에 속하는 파드의 수와 일치하는지 확인한다. 예를 들어, 서비스가 레플리카 3개인 nginx 컨테이너를 위한 것이라면, 서비스의 엔드포인트 항목에서 서로 다른 3개의 IP 주소가 확인되어야 한다.
서비스에 엔드포인트가 없는 경우
엔드포인트가 없는 상태라면, 서비스가 사용 중인 레이블을 이용하여 파드 목록을 조회해 본다. 다음과 같은 레이블을 갖는 서비스를 가정한다.
...
spec:
- selector:
name: nginx
type: frontend
다음의 명령을 사용하여,
kubectl get pods --selector=name=nginx,type=frontend
이 셀렉터에 매치되는 파드 목록을 조회할 수 있다. 서비스에 속할 것으로 예상하는 파드가 모두 조회 결과에 있는지 확인한다.
파드의 containerPort
가 서비스의 targetPort
와 일치하는지 확인한다.
네트워크 트래픽이 포워드되지 않는 경우
서비스 디버깅하기에서 더 많은 정보를 확인한다.
다음 내용
위의 방법 중 어떤 것으로도 문제가 해결되지 않는다면,
서비스 디버깅하기 문서를 참조하여
서비스
가 실행 중인지, 서비스에 엔드포인트
가 있는지, 파드
가 실제로 서빙 중인지 확인한다.
예를 들어, DNS가 실행 중이고, iptables 규칙이 설정되어 있고, kube-proxy가 정상적으로 동작하는 것으로 보이는 상황이라면,
위와 같은 사항을 확인해 볼 수 있다.
트러블슈팅 문서에서 더 많은 정보를 볼 수도 있다.
2 - 서비스 디버깅하기
쿠버네티스를 새로 설치할 때 자주 발생하는 문제 중 하나는 서비스가 제대로 작동하지 않는 현상이다. 디플로이먼트(또는 다른 워크로드 컨트롤러)를 통해 파드를 실행하고 서비스를 생성했지만, 해당 서비스에 엑세스하려고 하면 응답이 없는 경우이다. 이 문서를 통해 무엇이 잘못되었는지 파악하는 데 도움이 되기를 바란다.
파드 안에서 명령어 실행
이 페이지의 많은 단계에서, 클러스터 내에 실행 중인 파드가 어떤 것을 보고 있는지 알고 싶을 것이다. 이를 수행하는 가장 간단한 방법은 대화형 busybox 파드를 실행하는 것이다:
kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh
사용하려는 파드가 이미 실행 중인 경우, 다음을 사용하여 명령을 실행할 수 있다.
kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>
설정
연습을 위해, 파드를 몇 개 실행한다. 자신이 관리하는 서비스를 디버깅하는 경우에는 세부 사항을 상황에 맞게 변경하고, 아니면 아래의 과정을 그대로 수행하여 두 번째 데이터 포인트를 얻을 수 있다.
kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created
kubectl
명령어는 생성되거나 변경된 리소스의 유형과 이름을 출력하여, 여기서 출력된 리소스 유형과 이름은 다음 명령어에 사용할 수 있다.
디플로이먼트의 레플리카를 3개로 확장한다.
kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled
참고로, 위의 명령들을 실행하여 디플로이먼트를 실행하는 것은 다음과 같은 YAML을 사용하는 것과 동일하다.
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
명령에 의해 디플로이먼트의 이름으로 자동으로
설정된다.
파드가 실행 중인지 확인할 수 있다.
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
를 실행하고
이를 통해 디버그을 수행해야 할 수도 있다.
지금까지 모든 것이 계획대로 진행되었다면, 서비스가 작동하지 않는 이유를 분석해 볼 수 있다.
서비스가 존재하는가?
여기까지 따라왔다면 아직 실제로 서비스를 생성하지 않았다는 것을 알아차렸을 것이다. 이는 의도적인 것이다. 이 단계는 때때로 잊어버리지만, 가장 먼저 확인해야 할 단계이다.
존재하지 않는 서비스에 액세스하려고 하면 어떤 일이 발생할까? 이름을 이용하여 이 서비스를 사용하는 다른 파드가 있다면 다음과 같은 결과를 얻을 것이다.
wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'
가장 먼저 확인해야 할 것은 서비스가 실제로 존재하는지 여부이다.
kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found
이제 서비스를 생성하자. 이전에도 언급했듯이, 이것은 연습을 위한 예시이다. 본인의 서비스의 세부 사항을 여기에 입력할 수도 있다.
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
이제 서비스가 존재하는 것을 확인할 수 있다.
위와 마찬가지로, 위의 명령들을 실행하여 서비스를 실행하는 것은 다음과 같은 YAML을 사용하는 것과 동일하다.
apiVersion: v1
kind: Service
metadata:
labels:
app: hostnames
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
환경설정의 전체 범위를 강조하기 위해, 여기에서 생성한 서비스는 파드와 다른 포트 번호를 사용한다. 실제 많은 서비스에서, 이러한 값은 동일할 수 있다.
대상 파드에 영향을 미치는 네트워크 폴리시 인그레스 규칙이 있는가?
hostnames-*
파드로 들어오는 트래픽에 영향을 줄 수 있는 네트워크 폴리시 인그레스 규칙을
배포하는 경우, 이를 검토해야 한다.
자세한 내용은 Network Policies를 참고한다.
서비스가 DNS 이름으로 작동하는가?
클라이언트가 서비스를 사용하는 가장 일반적인 방법 중 하나는 DNS 이름을 통하는 것이다.
동일한 네임스페이스안의 파드 안에서, 다음을 실행한다.
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
이것이 실패하면, 파드와 서비스가 다른 네임스페이스에 있는 경우일 수 있다. 네임스페이스를 지정한 이름(namespace-qualified name)을 사용해 본다(다시 말하지만, 파드 안에서 다음을 실행한다).
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
이 방법이 성공했다면, 교차 네임스페이스 이름을 사용하기 위해 앱을 조정하거나, 또는 앱과 서비스를 동일한 네임스페이스에서 실행한다. 여전히 실패한다면, 완전히 지정된 이름(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"는 작업 중인 네임스페이스이다. "svc"는 서비스임을 나타낸다. "cluster.local"은 클러스터 도메인을 나타내며, 클러스터마다 다를 수 있다.
동일한 작업을 클러스터의 노드에서 시도해 볼 수도 있다.
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
완전히 지정된 이름은 조회가 가능하지만 상대적인 이름은 조회를 할 수 없는 경우,
파드의 /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 서비스를 나타내야 한다.
이는 --cluster-dns
플래그와 함께 kubelet
으로 전달된다.
search
라인은 서비스 이름을 찾을 수 있는 적절한 접미사가 포함되어야 한다.
위 예시의 경우, ("default.svc.cluster.local" ->) 로컬 네임스페이스의 서비스,
("svc.cluster.local" ->) 모든 네임스페이스의 서비스,
마지막으로 클러스터 ("cluster.local" ->) 클러스터 범위에서 이름을 찾는다.
클러스터의 구성에 따라 그 뒤에 추가 레코드를 기입할 수도 있다(총 6개까지).
클러스터 접미사는 --cluster-domain
플래그와 함께
kubelet
에 전달된다. 이 문서에서, 클러스터 접미사는
"cluster.local"로 간주된다. 당신의 클러스터가 다르게 구성되었을 수도 있으며,
이 경우 이전의 모든 명령어에서 이를
변경해야 한다.
options
라인의 ndots
값은 DNS 클라이언트 라이브러리가 모든 탐색 경우의 수를 고려할 수 있을 만큼 충분히 높게 설정되어야 한다.
쿠버네티스는 기본적으로 5로 설정하며,
이는 쿠버네티스가 생성하는 모든 DNS 이름을 포함할 수 있을 만큼 충분히 높다.
DNS 이름으로 동작하는 서비스가 하나라도 있는가?
위의 작업이 계속 실패하면, 서비스에 대해 DNS 조회가 작동하지 않는 것이다. 한 걸음 뒤로 물러나서 어떤 것이 작동하지 않는지 확인할 수 있다. 쿠버네티스 마스터 서비스는 항상 작동해야 한다. 파드 내에서 다음을 실행한다.
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 섹션을 참고하거나 본 문서의 맨 위로 돌아가서 다시 시작한다. 단, 서비스를 디버깅하는 대신, DNS 서비스를 디버그한다.
서비스가 IP로 작동하는가?
DNS가 작동하는 것을 확인했다고 가정하면, 다음 테스트는
서비스가 IP 주소로 작동하는지 확인하는 것이다. 클러스터의 파드에서,
서비스의 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
서비스가 작동하는 경우, 올바른 응답을 받았을 것이다. 그렇지 않은 경우, 잘못되어 있을 수 있는 여러가지 사항이 있다. 아래의 내용을 계속 읽어 본다.
서비스가 올바르게 정의되었는가?
몇 차례 강조하지만, 서비스가 정확하게 기재되어 있고 파드의 포트와 일치하는지 두 번, 세 번 확인해야 한다. 아래의 명령을 실행하여 서비스를 다시 조회해 본다.
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": {}
}
}
- 액세스하려는 서비스 포트가
spec.ports[]
에 나열되어 있는가? - 파드에 대한
targetPort
가 올바른가(일부 파드는 서비스와 다른 포트를 사용한다)? - 숫자 포트를 사용하려는 경우, 자료형이 숫자(9376)인가 문자열 "9376"인가?
- 이름이 부여된 포트를 사용하려는 경우, 파드가 해당 이름의 포트를 노출하는가?
- 포트의 'protocol'이 파드의 것과 일치하는가?
서비스에 엔드포인트가 있는가?
여기까지 왔다면, 서비스가 올바르게 정의되어 있고 DNS에 의해 해석될 수 있음을 확인한 것이다. 이제 실행 중인 파드가 서비스에 의해 실제로 선택되고 있는지 확인한다.
이전에 파드가 실행 중임을 확인했다. 다음 명령을 통해 다시 확인할 수 있다.
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
인수는 서비스에 구성된 레이블 셀렉터이다.
"AGE" 열은 파드가 실행된 지 약 1시간 되었다고 표시하는 것으로, 이는 파드가 제대로 실행되고 있으며 충돌하지 않음을 의미한다.
"RESTARTS" 열은 파드가 자주 충돌하지 않거나 다시 시작되지 않음을 나타낸다. 잦은 재시작은 간헐적인 연결 문제를 발생할 수 있다. 재시작 횟수가 높으면, 파드를 디버깅하는 방법에 대해 참고한다.
쿠버네티스 시스템 내부에는 모든 서비스의 셀렉터를 평가하고 그 결과를 해당 엔드포인트 오브젝트에 저장하는 제어 루프가 있다.
kubectl get endpoints hostnames
NAME ENDPOINTS
hostnames 10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376
위의 결과를 통해 엔드포인트 컨트롤러가 서비스에 대한 올바른 파드를 찾았음을 알 수 있다.
ENDPOINTS
열이 <none>
인 경우,
서비스의 spec.selector
필드가 파드의 metadata.labels
값을
실제로 선택하는지 확인해야 한다.
흔한 실수로, kubectl run
명령으로 디플로이먼트도 생성할 수 있었던 1.18 이전 버전에서,
서비스는 app=hostnames
를 이용하여 파드를 선택하지만
디플로이먼트는 run=hostnames
를 이용하여 파드를 선택하는 것과 같은 오타, 또는 기타 오류가 있을 수 있다.
파드가 작동하고 있는가?
여기까지 왔다면, 서비스가 존재하며 이 서비스가 파드를 선택하고 있는 것이다. 파드 자체에 대해서는 이 연습의 시작부분에서 확인했었다. 이제 파드가 실제로 작동하는지 다시 확인해 보자. 위에서 나열된 엔드포인트를 이용하여 서비스 메카니즘을 우회하고 파드에 직접 접근할 수 있다.
파드 내에서 다음을 실행한다.
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가 작동하는가?
여기까지 진행했다면, 서비스가 실행 중이고, 엔드포인트가 있으며, 파드가 실제로 서빙하고 있는 것이다. 이 시점에서는, 전체 서비스 프록시 메커니즘이 의심된다. 하나씩 확인하기로 한다.
kube-proxy는 쿠버네티스 서비스의 기본 구현이며 대부분의 클러스터에서 사용하고 있다. kube-proxy는 모든 노드에서 실행되며, 서비스 추상화를 제공하기 위한 메커니즘 일부를 구성하는 프로그램이다. 사용자의 클러스터에서 kube-proxy를 사용하지 않는 경우, 다음 섹션이 적용되지 않으며, 사용 중인 서비스 구현에 대한 내용을 조사해야 할 것이다.
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
가 올바르게 실행되지 않는 이유 중 하나로
필수 conntrack
바이너리를 찾을 수 없는 경우가 있을 수 있다.
클러스터를 설치하는 방법(예, 사전에 준비 없이 쿠버네티스를 설치)에 따라
일부 리눅스 시스템에서 발생할 수 있다. 이 경우,
conntrack
패키지를 수동으로 설치(예: 우분투에서 sudo apt install conntrack
)한 다음
다시 시도해야 한다.
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
각 서비스의 포트에 대해, KUBE-SERVICES
내의 하나의 규칙, 그리고
하나의 KUBE-SVC-<hash>
체인이 있어야 한다. 각 파드 엔드포인트에 대해,
해당 KUBE-SVC-<hash>
에는 몇 개의 규칙이 있어야 하고, 또한 몇 개의 규칙을 포함하는 하나의
KUBE-SEP-<hash>
체인이 있어야 한다. 정확한 규칙은
사용자의 정확한 설정(노드 포트 및 로드 밸런서 포함)에 따라 다르다.
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
...
각 서비스의 포트와 모든 NodePort, 외부 IP
및 로드 밸런서 IP에 대해 kube-proxy는 가상 서버를 생성한다.
각 파드 엔드포인트에 대해, kube-proxy는 각 가상 서버에 대응되는 실제 서버를 생성한다.
예제에서, '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로 다시 액세스해 본다.
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
를 재시작한 다음
로그를 다시 살펴본다.
엣지 케이스: 파드가 서비스 IP를 통해 자신에게 도달하는 데 실패하는 경우
이러한 일이 일어날 것 같지 않을 수도 있지만 실제로 일어나기도 하며, 정상적이라면 제대로 동작해야 한다.
이러한 현상은 네트워크가 '헤어핀' 트래픽(클러스터 내부에서 U턴하는 트래픽)에 대해 제대로 구성되지 않은 경우에 발생할 수 있으며,
이는 주로 kube-proxy
가 iptables
모드에서 실행되고
파드가 브릿지 네트워크에 연결된 경우에 해당할 수 있다.
kubelet
은 파드가 자신이 속한 서비스 VIP로 접근하는 경우
서비스의 엔드포인트가 이 트래픽을 다시 서비스의 파드로 로드밸런싱하도록 하는
'hairpin-mode[플래그](/docs/reference/command-line-tools-reference/kubelet/)를 노출한다.
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
- 실제로 적용되어 있는
hairpin-mode
가 무엇인지 확인한다. 이를 확인하려면, kubelet 로그를 확인해야 한다. 로그 액세스는 노드 OS에 따라 다르다. 일부 OS에서는 /var/log/kubelet.log와 같은 파일인 반면 다른 OS에서는journalctl
을 사용하여 로그에 액세스한다. 실제로 적용되어 있는 hairpin 모드는 호환성으로 인해--hairpin-mode
플래그와 일치하지 않을 수 있으므로 주의한다. kubelet.log에hairpin
이라는 키워드가 포함된 로그 라인이 있는지 확인한다. 아래와 같이 실행 중인 헤어핀 모드를 나타내는 로그 라인이 있을 것이다.
I0629 00:51:43.648698 3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
- 실행 중인 hairpin 모드가
hairpin-veth
인 경우,kubelet
이 노드의/sys
에서 작동할 수 있는 권한이 있는지 확인한다. 모든 것이 제대로 작동한다면, 다음 명령을 실행했을 때 아래와 같이 표시될 것이다.
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
- 실행 중인 hairpin 모드가
promiscuous-bridge
인 경우,Kubelet
이 노드 안에서 리눅스 브릿지를 조작할 수 있는 권한이 있는지 확인한다.cbr0
브릿지가 사용되었고 제대로 구성되어 있다면, 다음 명령을 실행했을 때 아래와 같이 표시될 것이다.
ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1
- 위의 방법 중 어느 것으로도 해결되지 않는다면, 도움을 요청한다.
도움 요청하기
여기까지 왔다면, 아주 이상한 일이 발생하고 있는 것이다. 서비스가
실행 중이고, 엔드포인트가 있으며, 파드가 실제로 서빙하고 있는 상태이다.
DNS가 작동 중이고, kube-proxy
가 오작동하는 것은 아닌 것 같다.
그럼에도 서비스가 동작하지 않는 것이다.
우리가 도울 수 있도록, 어떤 일이 일어나고 있는지 커뮤니티에 알려주세요!
다음 내용
자세한 내용은 트러블슈팅하기 개요 문서 를 참고한다.
3 - 스테이트풀셋 디버깅하기
이 문서에서는 스테이트풀셋을 디버깅 방법에 대해 설명한다.
시작하기 전에
- 쿠버네티스 클러스터가 준비되어 있어야 하고, kubectl 커맨드 라인 도구가 클러스터와 통신할 수 있게 사전에 설정되어 있어야 한다.
- 조사하고자 하는 스테이트풀셋이 사전에 준비되어 있어야 한다.
스테이트풀셋 디버깅하기
레이블이 app.kubernetes.io/name=MyApp
으로 지정된 스테이트풀셋 파드를 전부 나열하기 위해서는
다음의 명령을 사용할 수 있다.
kubectl get pods -l app.kubernetes.io/name=MyApp
만약 오랜 시간동안 Unknown
이나 Terminating
상태에 있는
파드들을 발견하였다면, 이러한 파드들을 어떻게 다루는지 알아보기 위해
스테이트풀셋 파드 삭제하기를 참고하길 바란다.
스테이트풀셋에 포함된 개별 파드들을 디버깅하기 위해서는
파드 디버그하기 가이드를 참고하길 바란다.
다음 내용
초기화 컨테이너(Init container) 디버그하기를 참고하길 바란다.
4 - 파드 실패의 원인 검증하기
이 페이지는 컨테이너 종료 메시지를 읽고 쓰는 방법을 보여준다.
종료 메시지는 컨테이너가 치명적인 이벤트에 대한 정보를, 대시보드나 모니터링 소프트웨어 도구와 같이 쉽게 조회 및 표시할 수 있는 위치에 기록하는 방법을 제공한다. 대부분의 경우에 종료 메시지에 넣는 정보는 일반적으로 쿠버네티스 로그에도 쓰여져야 한다.
시작하기 전에
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
종료 메시지 읽기 및 쓰기
이 예제에서는, 하나의 컨테이너를 실행하는 파드를 생성한다. 파드의 매니페스트는 컨테이너가 시작될 때 수행하는 명령어를 지정한다.
apiVersion: v1
kind: Pod
metadata:
name: termination-demo
spec:
containers:
- name: termination-demo-container
image: debian
command: ["/bin/sh"]
args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
-
다음의 YAML 설정 파일에 기반한 파드를 생성한다.
kubectl apply -f https://k8s.io/examples/debug/termination.yaml
YAML 파일에 있는
command
와args
필드에서 컨테이너가 10초 간 잠든 뒤에 "Sleep expired" 문자열을/dev/termination-log
파일에 기록하는 것을 확인할 수 있다. 컨테이너는 "Sleep expired" 메시지를 기록한 후에 종료된다. -
파드와 관련된 정보를 출력한다.
kubectl get pod termination-demo
파드가 더 이상 실행되지 않을 때까지 앞선 명령어를 반복한다.
-
파드에 관한 상세 정보를 출력한다.
kubectl get pod termination-demo --output=yaml
결과는 "Sleep expired" 메시지를 포함한다.
apiVersion: v1 kind: Pod ... lastState: terminated: containerID: ... exitCode: 0 finishedAt: ... message: | Sleep expired ...
-
종료 메시지만을 포함하는 출력 결과를 보기 위해서는 Go 템플릿을 사용한다.
kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
여러 컨테이너를 포함하는 파드의 경우, Go 템플릿을 사용하여 컨테이너 이름도 출력할 수 있다. 이렇게 하여, 어떤 컨테이너가 실패하는지 찾을 수 있다.
kubectl get pod multi-container-pod -o go-template='{{range .status.containerStatuses}}{{printf "%s:\n%s\n\n" .name .lastState.terminated.message}}{{end}}'
종료 메시지 사용자 정의하기
쿠버네티스는 컨테이너의 terminationMessagePath
필드에 지정된
종료 메시지 파일에서 종료 메시지를 검색하며, 이 필드의 기본값은
/dev/termination-log
이다. 이 필드를 사용자 정의 함으로써
쿠버네티스가 종료 메시지를 검색할 때 다른 파일을 사용하도록 조정할 수 있다.
쿠버네티스는 지정된 파일의 내용을 사용하여 컨테이너의 성공 및 실패에 대한 상태 메시지를 채운다.
종료 메시지는 assertion failure 메세지처럼 간결한 최종 상태로 생성된다. kubelet은 4096 바이트보다 긴 메시지를 자른다.
모든 컨테이너의 총 메시지 길이는 12KiB로 제한되며, 각 컨테이너에 균등하게 분할된다.
예를 들어, 12개의 컨테이너(initContainers
또는 containers
)가 있는 경우 각 컨테이너에는 1024 바이트의 사용 가능한 종료 메시지 공간이 있다.
기본 종료 메시지 경로는 /dev/termination-log
이다.
파드가 시작된 후에는 종료 메시지 경로를 설정할 수 없다.
다음의 예제에서 컨테이너는, 쿠버네티스가 조회할 수 있도록
/tmp/my-log
파일에 종료 메시지를 기록한다.
apiVersion: v1
kind: Pod
metadata:
name: msg-path-demo
spec:
containers:
- name: msg-path-demo-container
image: debian
terminationMessagePath: "/tmp/my-log"
또한 사용자는 추가적인 사용자 정의를 위해 컨테이너의 terminationMessagePolicy
필드를 설정할 수 있다. 이 필드의 기본 값은 File
이며,
이는 오직 종료 메시지 파일에서만 종료 메시지가 조회되는 것을 의미한다.
terminationMessagePolicy
필드의 값을 "FallbackToLogsOnError
으로
설정함으로써, 종료 메시지 파일이 비어 있고 컨테이너가 오류와 함께 종료 되었을 경우
쿠버네티스가 컨테이너 로그 출력의 마지막 청크를 사용하도록 지시할 수 있다.
로그 출력은 2048 바이트나 80 행 중 더 작은 값으로 제한된다.
다음 내용
5 - 초기화 컨테이너(Init Containers) 디버그하기
이 페이지는 초기화 컨테이너의 실행과 관련된 문제를
조사하는 방법에 대해 보여준다. 아래 예제의 커맨드 라인은 파드(Pod)를 <pod-name>
으로,
초기화 컨테이너를 <init-container-1>
과
<init-container-2>
로 표시한다.
시작하기 전에
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
버전 확인을 위해서, 다음 커맨드를 실행kubectl version
.
- 사용자는 초기화 컨테이너의 기본 사항에 익숙해야 한다.
- 사용자는 초기화 컨테이너를 구성해야 한다.
초기화 컨테이너의 상태 체크하기
사용자 파드의 상태를 표시한다.
kubectl get pod <pod-name>
예를 들어, Init:1/2
상태는 두 개의 초기화 컨테이너 중
하나가 성공적으로 완료되었음을 나타낸다.
NAME READY STATUS RESTARTS AGE
<pod-name> 0/1 Init:1/2 0 7s
상태값과 그 의미에 대한 추가 예제는 파드 상태 이해하기를 참조한다.
초기화 컨테이너에 대한 상세 정보 조회하기
초기화 컨테이너의 실행에 대한 상세 정보를 확인한다.
kubectl describe pod <pod-name>
예를 들어, 2개의 초기화 컨테이너가 있는 파드는 다음과 같이 표시될 수 있다.
Init Containers:
<init-container-1>:
Container ID: ...
...
State: Terminated
Reason: Completed
Exit Code: 0
Started: ...
Finished: ...
Ready: True
Restart Count: 0
...
<init-container-2>:
Container ID: ...
...
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 1
Started: ...
Finished: ...
Ready: False
Restart Count: 3
...
파드 스펙의 status.initContainerStatuses
필드를 읽어서
프로그래밍 방식으로 초기화 컨테이너의 상태를 조회할 수도 있다.
kubectl get pod nginx --template '{{.status.initContainerStatuses}}'
이 명령은 원시 JSON 방식으로 위와 동일한 정보를 반환한다.
초기화 컨테이너의 로그 조회하기
초기화 컨테이너의 로그를 확인하기 위해 파드의 이름과 초기화 컨테이너의 이름을 같이 전달한다.
kubectl logs <pod-name> -c <init-container-2>
셸 스크립트를 실행하는 초기화 컨테이너는, 초기화 컨테이너가
실행될 때 명령어를 출력한다. 예를 들어, 스크립트의 시작 부분에
set -x
를 추가하고 실행하여 Bash에서 명령어를 출력할 수 있도록 수행할 수 있다.
파드의 상태 이해하기
Init:
으로 시작하는 파드 상태는 초기화 컨테이너의
실행 상태를 요약한다. 아래 표는 초기화 컨테이너를 디버깅하는
동안 사용자가 확인할 수 있는 몇 가지 상태값의 예이다.
상태 | 의미 |
---|---|
Init:N/M |
파드가 M 개의 초기화 컨테이너를 갖고 있으며, 현재까지 N 개가 완료. |
Init:Error |
초기화 컨테이너 실행 실패. |
Init:CrashLoopBackOff |
초기화 컨테이너가 반복적으로 실행 실패. |
Pending |
파드가 아직 초기화 컨테이너를 실행하지 않음. |
PodInitializing or Running |
파드가 이미 초기화 컨테이너 실행을 완료. |
6 - 동작 중인 파드 디버그
이 페이지는 노드에서 동작 중인(혹은 크래시된) 파드를 디버그하는 방법에 대해 설명한다.
시작하기 전에
- 여러분의 파드는 이미 스케줄링 되어 동작하고 있을 것이다. 만약 파드가 아직 동작중이지 않다면, 애플리케이션 트러블슈팅을 참고한다.
- 일부 고급 디버깅 과정에서는 해당 파드가 어떤 노드에서 동작하고 있는지
알아야 하고, 해당 노드에서 쉘 명령어를 실행시킬 수 있어야 한다.
kubectl
을 사용하는 일반적인 디버깅 과정에서는 이러한 접근 권한이 필요하지 않다.
kubectl describe pod
명령으로 파드 상세사항 가져오기
이 예제에서는 앞의 예제와 비슷하게 두 개의 파드를 생성하기 위해 디플로이먼트를 사용할 것이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
다음 명령을 실행하여 디플로이먼트를 생성한다.
kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created
다음 명령을 실행하여 파드 상태를 확인한다.
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-67d4bdd6f5-cx2nz 1/1 Running 0 13s
nginx-deployment-67d4bdd6f5-w6kd7 1/1 Running 0 13s
다음과 같이 kubectl describe pod
명령을 사용하여 각 파드에 대한 더 많은 정보를 가져올 수 있다.
kubectl describe pod nginx-deployment-67d4bdd6f5-w6kd7
Name: nginx-deployment-67d4bdd6f5-w6kd7
Namespace: default
Priority: 0
Node: kube-worker-1/192.168.0.113
Start Time: Thu, 17 Feb 2022 16:51:01 -0500
Labels: app=nginx
pod-template-hash=67d4bdd6f5
Annotations: <none>
Status: Running
IP: 10.88.0.3
IPs:
IP: 10.88.0.3
IP: 2001:db8::1
Controlled By: ReplicaSet/nginx-deployment-67d4bdd6f5
Containers:
nginx:
Container ID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
Image: nginx
Image ID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 17 Feb 2022 16:51:05 -0500
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 128Mi
Requests:
cpu: 500m
memory: 128Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bgsgp (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-bgsgp:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned default/nginx-deployment-67d4bdd6f5-w6kd7 to kube-worker-1
Normal Pulling 31s kubelet Pulling image "nginx"
Normal Pulled 30s kubelet Successfully pulled image "nginx" in 1.146417389s
Normal Created 30s kubelet Created container nginx
Normal Started 30s kubelet Started container nginx
위 예시에서 컨테이너와 파드에 대한 구성 정보(레이블, 리소스 요구사항 등) 및 상태 정보(상태(state), 준비성(readiness), 재시작 횟수, 이벤트 등)를 볼 수 있다.
컨테이너의 상태(state)값은 Waiting, Running, 또는 Terminated 중 하나이다. 각 상태에 따라, 추가 정보가 제공될 것이다. 위 예시에서 Running 상태의 컨테이너에 대해서는 컨테이너의 시작 시각을 시스템이 표시해 주는 것을 볼 수 있다.
Ready 값은 컨테이너의 마지막 준비성 프로브(readiness probe) 통과 여부를 알려 준다. (위 예시에서는 컨테이너에 준비성 프로브가 설정되어 있지 않다. 컨테이너에 준비성 프로브가 설정되어 있지 않으면, 컨테이너는 준비(ready) 상태로 간주된다.)
'재시작 카운트'는 컨테이너가 재시작된 횟수를 보여 준다. 이 정보는 재시작 정책이 'always'로 설정된 컨테이너의 반복적인 강제 종료를 알아차리는 데에 유용하다.
위 예시에서 파드와 연관된 유일한 컨디션(Condition)은 True 또는 False 값을 갖는 Ready 컨디션이며, 이 값이 True라는 것은 파드가 요청을 처리할 수 있으며 모든 동일한 서비스를 묶는 로드 밸런싱 풀에 추가되어야 함을 의미한다.
마지막으로, 파드와 관련된 최근 이벤트 로그가 표시된다. 시스템은 동일한 여러 이벤트를 처음/마지막 발생 시간 및 발생 횟수만 압축적으로 표시한다. "From"은 이벤트 로그를 발생하는 구성 요소를 가리키고, "SubobjectPath"는 참조되는 개체(예: 파드 내 컨테이너)를 나타내며, "Reason" 및 "Message"는 발생한 상황을 알려 준다.
예시: Pending 상태의 파드 디버깅하기
이벤트를 사용하여 감지할 수 있는 일반적인 시나리오는 노드에 할당될 수 없는 파드를 생성하는 경우이다. 예를 들어 파드가 노드에 사용 가능한 리소스보다 더 많은 리소스를 요청하거나, 또는 어떤 노드에도 해당되지 않는 레이블 셀렉터를 명시했을 수 있다. 예를 들어 4개 노드로 구성되며 각 (가상) 머신에 1 CPU가 있는 클러스터가 있는 상황에서, 위 예시 대신 2 레플리카가 아니라 5 레플리카를, 500 밀리코어가 아니라 600 밀리코어를 요청하는 디플로이먼트를 배포했다고 해 보자. 이러한 경우 5개의 파드 중 하나는 스케줄링될 수 없을 것이다. (각 노드에는 fluentd, skydns 등의 클러스터 애드온도 실행되고 있으므로, 만약 1000 밀리코어를 요청했다면 파드가 하나도 스케줄될 수 없었을 것이다.)
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1006230814-6winp 1/1 Running 0 7m
nginx-deployment-1006230814-fmgu3 1/1 Running 0 7m
nginx-deployment-1370807587-6ekbw 1/1 Running 0 1m
nginx-deployment-1370807587-fg172 0/1 Pending 0 1m
nginx-deployment-1370807587-fz9sd 0/1 Pending 0 1m
nginx-deployment-1370807587-fz9sd 파드가 왜 실행되지 않는지를 알아 보려면, pending 상태의 파드에 대해 kubectl describe pod
명령을 실행하고 이벤트(event) 항목을 확인해 볼 수 있다.
kubectl describe pod nginx-deployment-1370807587-fz9sd
Name: nginx-deployment-1370807587-fz9sd
Namespace: default
Node: /
Labels: app=nginx,pod-template-hash=1370807587
Status: Pending
IP:
Controllers: ReplicaSet/nginx-deployment-1370807587
Containers:
nginx:
Image: nginx
Port: 80/TCP
QoS Tier:
memory: Guaranteed
cpu: Guaranteed
Limits:
cpu: 1
memory: 128Mi
Requests:
cpu: 1
memory: 128Mi
Environment Variables:
Volumes:
default-token-4bcbi:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-4bcbi
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 48s 7 {default-scheduler } Warning FailedScheduling pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000
여기서 스케줄러가 기록한 이벤트를 통해, 파드가 FailedScheduling
사유로 인해 스케줄링되지 않았음을 알 수 있다(다른 이유도 있을 수 있음). 이 메시지를 통해 어떤 노드에도 이 파드를 실행하기 위한 충분한 리소스가 없었음을 알 수 있다.
이 상황을 바로잡으려면, kubectl scale
명령으로 디플로이먼트의 레플리카를 4 이하로 줄일 수 있다. (또는 한 파드를 pending 상태로 두어도 되며, 이렇게 해도 문제는 없다.)
kubectl describe pod
출력의 마지막에 있는 것과 같은 이벤트는 etcd에 기록되어 보존되며 클러스터에 어떤 일이 일어나고 있는지에 대한 높은 차원의 정보를 제공한다. 모든 이벤트의 목록을 보려면 다음 명령을 실행한다.
kubectl get events
그런데 이벤트는 네임스페이스 스코프 객체라는 것을 기억해야 한다. 즉 네임스페이스 스코프 객체에 대한 이벤트(예: my-namespace
네임스페이스의 파드에 어떤 일이 발생했는지)가 궁금하다면, 다음과 같이 커맨드에 네임스페이스를 명시해야 한다.
kubectl get events --namespace=my-namespace
모든 네임스페이스에 대한 이벤트를 보려면, --all-namespaces
인자를 사용할 수 있다.
kubectl describe pod
명령 외에도, kubectl get pod
이상의 정보를 얻는 다른 방법은 kubectl get pod
명령에 출력 형식 플래그 -o yaml
인자를 추가하는 것이다. 이렇게 하면 kubectl describe pod
명령보다 더 많은 정보, 원천적으로는 시스템이 파드에 대해 알고 있는 모든 정보를 YAML 형식으로 볼 수 있다. 여기서 어노테이션(레이블 제한이 없는 키-밸류 메타데이터이며, 쿠버네티스 시스템 구성 요소가 내부적으로 사용함), 재시작 정책, 포트, 볼륨과 같은 정보를 볼 수 있을 것이다.
kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-02-17T21:51:01Z"
generateName: nginx-deployment-67d4bdd6f5-
labels:
app: nginx
pod-template-hash: 67d4bdd6f5
name: nginx-deployment-67d4bdd6f5-w6kd7
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: nginx-deployment-67d4bdd6f5
uid: 7d41dfd4-84c0-4be4-88ab-cedbe626ad82
resourceVersion: "1364"
uid: a6501da1-0447-4262-98eb-c03d4002222e
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 500m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-bgsgp
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kube-worker-1
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-bgsgp
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:06Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2022-02-17T21:51:01Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
image: docker.io/library/nginx:latest
imageID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
lastState: {}
name: nginx
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2022-02-17T21:51:05Z"
hostIP: 192.168.0.113
phase: Running
podIP: 10.88.0.3
podIPs:
- ip: 10.88.0.3
- ip: 2001:db8::1
qosClass: Guaranteed
startTime: "2022-02-17T21:51:01Z"
파드의 로그 확인하기
먼저, 확인하고자 하는 컨테이너의 로그를 확인한다.
kubectl logs ${POD_NAME} ${CONTAINER_NAME}
만약 컨테이너가 이전에 크래시 되었다면, 다음의 명령을 통해 컨테이너의 크래시 로그를 살펴볼 수 있다.
kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}
exec를 통해 컨테이너 디버깅하기
만약 컨테이너 이미지에
디버깅 도구가 포함되어 있다면, kubectl exec
을 통해 특정 컨테이너에서 해당 명령들을
실행할 수 있다. (리눅스나 윈도우 OS를 기반으로 만들어진 이미지에는 대부분 디버깅 도구를 포함하고
있다.)
kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}
-c ${CONTAINER_NAME}
인자는 선택적이다. 만약 하나의 컨테이너만 포함된 파드라면 해당 옵션을 생략할 수 있다.
예를 들어, 동작 중인 카산드라 파드의 로그를 살펴보기 위해서는 다음과 같은 명령을 실행할 수 있다.
kubectl exec cassandra -- cat /var/log/cassandra/system.log
kubectl exec
에 -i
와 -t
옵션을 사용해서 터미널에서 접근할 수 있는 쉘을 실행시킬 수도 있다.
예를 들면 다음과 같다.
kubectl exec -it cassandra -- sh
더욱 상세한 내용은 동작중인 컨테이너의 쉘에 접근하기를 참고한다.
임시(ephemeral) 디버그 컨테이너를 사용해서 디버깅하기
Kubernetes v1.25 [stable]
컨테이너가 크래시 됐거나
distroless 이미지처럼
컨테이너 이미지에 디버깅 도구를 포함하고 있지 않아 kubectl exec
로는 충분하지 않은 경우에는
임시(Ephemeral) 컨테이너를 사용하는 것이
인터랙티브한 트러블슈팅에 유용하다.
임시 컨테이너를 사용한 디버깅 예시
kubectl debug
명령어를 사용해서 동작 중인 파드에 임시 컨테이너를 추가할 수 있다.
먼저, 다음과 같이 파드를 추가한다.
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
이 섹션의 예시에서는 디버깅 도구가 포함되지 않은 이미지의 사례를 보여드리기 위해
pause
컨테이너 이미지를 사용했는데, 이 대신 어떠한 이미지를 사용해도
될 것이다.
만약 kubectl exec
을 통해 쉘을 생성하려 한다면 다음과 같은 에러를
확인할 수 있을 텐데, 그 이유는 이 이미지에 쉘이 존재하지 않기 때문이다.
kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown
이 명령어 대신 kubectl debug
을 사용해서 디버깅 컨테이너를 생성할 수 있다.
만약 -i
/--interactive
인자를 사용한다면, kubectl
은 임시
컨테이너의 콘솔에 자동으로 연결할 것이다.
kubectl debug -it ephemeral-demo --image=busybox --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #
이 명령어는 새로운 busybox 컨테이너를 추가하고 해당 컨테이너로 연결한다. --target
파라미터를 사용하면 다른 컨테이너의 프로세스 네임스페이스를 대상으로 하게 된다. 여기서는
이 옵션이 꼭 필요한데, kubectl run
이 생성하는 파드에 대해
프로세스 네임스페이스 공유를
활성화하지 않기 때문이다.
--target
파라미터는 사용 중인
컨테이너 런타임에서
지원해야지만 사용할 수 있다. 만일 지원되지 않는다면,
임시 컨테이너가 시작되지 않을 수 있거나 독립적인 프로세스
네임스페이스를 가지고 시작될 수 있다.
kubectl describe
명령을 사용하면 새롭게 생성된 임시 컨테이너의 상태를 확인할 수 있다.
kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
debugger-8xzrl:
Container ID: docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
Image: busybox
Image ID: docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
Port: <none>
Host Port: <none>
State: Running
Started: Wed, 12 Feb 2020 14:25:42 +0100
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
디버깅이 다 끝나면 kubectl delete
을 통해 파드를 제거할 수 있다.
kubectl delete pod ephemeral-demo
파드의 복제본을 이용해서 디버깅하기
때때로 파드의 설정 옵션에 따라 특정 상황에서 트러블슈팅을 하기가 어려울 수 있다.
예를 들어, 만일 여러분의 컨테이너 이미지가 쉘을 포함하고 있지 않거나, 여러분의
애플리케이션이 컨테이너 시작에서 크래시가 발생한다면 kubectl exec
을 이용해서
컨테이너를 트러블슈팅할 수 없을 수 있다. 이러한 상황에서는 kubectl debug
을 사용해서
파드의 복제본을 디버깅을 위한 추가적인 설정 옵션과 함께 생성할 수 있다.
새 컨테이너와 함께 파드의 복제본 생성하기
만일 여러분의 애플리케이션이 동작은 하고 있지만 예상과는 다르게 동작하는 경우, 파드의 복제본에 새로운 컨테이너를 추가함으로써 추가적인 트러블슈팅 도구들을 파드에 함께 추가할 수 있다.
가령, 여러분의 애플리케이션 컨테이너 이미지는 busybox
를 기반으로 하고 있는데
여러분은 busybox
에는 없는 디버깅 도구를 필요로 한다고 가정해 보자. 이러한
시나리오는 kubectl run
명령을 통해 시뮬레이션 해볼 수 있다.
kubectl run myapp --image=busybox --restart=Never -- sleep 1d
다음의 명령을 실행시켜 디버깅을 위한 새로운 우분투 컨테이너와 함께 myapp-debug
이란
이름의 myapp
컨테이너 복제본을 생성할 수 있다.
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#
- 만일 여러분이 새로 생성되는 컨테이너의 이름을
--container
플래그와 함께 지정하지 않는다면,kubectl debug
는 자동으로 새로운 컨테이너 이름을 생성한다. -i
플래그를 사용하면kubectl debug
명령이 새로운 컨테이너에 기본적으로 연결되게 된다. 이러한 동작은--attach=false
을 지정하여 방지할 수 있다. 만일 여러분의 세션이 연결이 끊어진다면kubectl attach
를 사용해서 다시 연결할 수 있다.--share-processes
옵션은 이 파드에 있는 컨테이너가 해당 파드에 속한 다른 컨테이너의 프로세스를 볼 수 있도록 한다. 이 옵션이 어떻게 동작하는지에 대해 더 알아보기 위해서는 다음의 파드의 컨테이너 간 프로세스 네임스페이스 공유를 참고하라.
사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
명령어를 변경하며 파드의 복제본 생성하기
때때로 컨테이너의 명령어를 변경하는 것이 유용한 경우가 있는데, 예를 들면 디버그 플래그를 추가하기 위해서나 애플리케이션이 크래시 되는 경우이다.
다음의 kubectl run
명령을 통해 즉각적으로 크래시가 발생하는 애플리케이션의
사례를 시뮬레이션해 볼 수 있다.
kubectl run --image=busybox myapp -- false
kubectl describe pod myapp
명령을 통해 이 컨테이너에 크래시가 발생하고 있음을 확인할 수 있다.
Containers:
myapp:
Image: busybox
...
Args:
false
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 1
이러한 경우에 kubectl debug
을 통해 명령어를 지정함으로써 해당 파드의
복제본을 인터랙티브 쉘로 생성할 수 있다.
kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #
이제 인터랙티브 쉘에 접근할 수 있으니 파일 시스템 경로를 확인하거나 동작 중인 컨테이너의 명령어를 직접 확인하는 등의 작업이 가능하다.
- 특정 컨테이너의 명령어를 변경하기 위해서는
--container
옵션을 통해 해당 컨테이너의 이름을 지정해야만 한다. 이름을 지정하지 않는다면kubectl debug
은 이전에 지정한 명령어를 그대로 사용해서 컨테이너를 생성할 것이다. - 기본적으로
-i
플래그는kubectl debug
명령이 컨테이너에 바로 연결되도록 한다. 이러한 동작을 방지하기 위해서는--attach=false
옵션을 지정할 수 있다. 만약 여러분이 세션이 종료된다면kubectl attach
명령을 통해 다시 연결할 수 있다.
사용이 모두 끝나면, 디버깅에 사용된 파드들을 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
컨테이너 이미지를 변경하며 파드의 복제본 생성하기
특정한 경우에 여러분은 제대로 동작하지 않는 파드의 이미지를 기존 프로덕션 컨테이너 이미지에서 디버깅 빌드나 추가적인 도구를 포함한 이미지로 변경하고 싶을 수 있다.
이 사례를 보여주기 위해 kubectl run
명령을 통해 파드를 생성하였다.
kubectl run myapp --image=busybox --restart=Never -- sleep 1d
여기서는 kubectl debug
명령을 통해 해당 컨테이너 이미지를 ubuntu
로 변경하며
복제본을 생성하였다.
kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu
--set-image
의 문법은 kubectl set image
와 동일하게 container_name=image
형식의 문법을 사용한다. *=ubuntu
라는 의미는 모든 컨테이너의 이미지를 ubuntu
로
변경하겠다는 의미이다.
사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod myapp myapp-debug
노드의 쉘을 사용해서 디버깅하기
만약 위의 어떠한 방법도 사용할 수 없다면, 파드가 현재 동작 중인 노드를 찾아
해당 노드에서 실행되는 파드를 생성할 수 있다.
다음 kubectl debug
명령을 통해 해당 노드에서 인터랙티브한 쉘을 생성할 수 있다.
kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#
노드에서 디버깅 세션을 생성할 때 유의해야 할 점은 다음과 같다.
kubectl debug
는 노드의 이름에 기반해 새로운 파드의 이름을 자동으로 생성한다.- 노드의 루트 파일시스템은
/host
에 마운트된다. - 파드가 특권을 가지고 있지 않더라도, 컨테이너는 호스트 네임스페이스(IPC, 네트워크, PID 네임스페이스)에서 동작한다. 따라서 몇몇 프로세스 정보를 읽어오거나,
chroot /host
등의 작업은 수행될 수 없다. - 특권을 가진 파드가 필요한 경우에는 직접 생성한다.
사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.
kubectl delete pod node-debugger-mynode-pdx84
7 - 동작중인 컨테이너의 셸에 접근하기
이 페이지는 동작중인 컨테이너에 접근하기 위해 kubectl exec
을 사용하는
방법에 대해 설명한다.
시작하기 전에
쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.
컨테이너의 셸에 접근하기
이 예시에서는 하나의 컨테이너를 가진 파드를 생성할 것이다. 이 컨테이너는 nginx 이미지를 실행한다. 해당 파드에 대한 설정 파일은 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: shell-demo
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
hostNetwork: true
dnsPolicy: Default
파드를 생성한다.
kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml
다음을 통해 컨테이너가 동작하고 있는지 확인할 수 있다.
kubectl get pod shell-demo
동작중인 컨테이너의 셸에 접근한다.
kubectl exec --stdin --tty shell-demo -- /bin/bash
--
)를 사용할 수 있다.
셸에 접근해서 다음처럼 루트 디렉토리를 확인해 볼 수 있다.
# Run this inside the container
ls /
접근한 셸에서 다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.
# You can run these example commands inside the container
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx
nginx의 최상단 페이지 작성하기
앞에서 생성한 파드에 대한 설정을 살펴보아라. 파드에는
emptyDir
볼륨이 사용되었고, 이 컨테이너는 해당 볼륨을
/usr/share/nginx/html
경로에 마운트하였다.
접근한 셸 환경에서 /usr/share/nginx/html
디렉터리에 index.html
파일을
생성해 보아라.
# Run this inside the container
echo 'Hello shell demo' > /usr/share/nginx/html/index.html
셸 환경에서 nginx 서버에 GET 요청을 시도해보면 다음과 같다.
# Run this in the shell inside your container
apt-get update
apt-get install curl
curl http://localhost/
출력 결과는 여러분이 index.html
파일에 작성한 텍스트를 출력할 것이다.
Hello shell demo
셸 사용이 모두 끝났다면 exit
을 입력해 종료하라.
exit # To quit the shell in the container
컨테이너에서 개별 명령어 실행하기
셸이 아닌 일반적인 커맨드 환경에서 다음처럼 동작중인 컨테이너의 환경 변수를 출력할 수 있다.
kubectl exec shell-demo env
다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.
kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts
파드에 한 개 이상의 컨테이너가 있을 경우 셸에 접근하기
만일 파드에 한 개 이상의 컨테이너가 있을 경우, kubectl exec
명령어에
--container
혹은 -c
옵션을 사용해서 컨테이너를 지정하라. 예를 들어,
여러분이 my-pod라는 이름의 파드가 있다고 가정해 보자. 이 파드에는 main-app 과
helper-app 이라는 이름의 두 컨테이너가 있다. 다음 명령어는 main-app
컨테이너에 대한 셸에 접근할 것이다.
kubectl exec -i -t my-pod --container main-app -- /bin/bash
-i
와 -t
는 각각 --stdin
와 --tty
옵션에 대응된다.
다음 내용
- kubectl exec를 참고한다.