쿠버네티스 클러스터 트러블슈팅
kubectl delete pod --all
# 모든 pod 삭제
kubectl run test1 --image=public.ecr.aws/docker/library/nginx:alpine
kubectl run test2 --image=public.ecr.aws/docker/library/nginx:alpine
kubectl create deploy test --replicas=2 --image=public.ecr.aws/docker/library/nginx:alpine


# worker-1은 정상

# worker-2는 비정상.

# 정상인 것처럼 보이지만 비정상.
root@master:~# kubectl delete pod --all -n kube-system
# 모든 파드를 삭제해 보자.

# 다시 비정상 파드주소에 curl을 해보자. 그래도 안되면 일단 모든 노드 재부팅.
init 6

# 재부팅 후에 정상이 됐다.
그래도 안된다면 최악의 상황.
1.kubectl delete -f kube-flannel.yml로 flannel CNI 삭제
2. 모든 노드에서 kubeadm reset으로 클러스터 리셋
3. 재부팅을 해서 CNI 관련된 호스트의 설정값들을 초기화
4.kubeadm init 및 kubeadm join으로 클러스터 재구성.
LoadBalancer 타입
- 클러스터 관리자의 도움이 필요한 서비스 타입
- EKS 같은 클라우드 서비스 제공자의 경우.
- Service타입을 LoadBalancer로 명시하기만 해도 이미 기능이 구현되어 있기 때문에 자동으로 LB가 생성.
(aws의 EKS에서 Service 타입을 LoadBalancer로 생성하면 실제 NLB나 classic loadbalancer(기본값)가 생성)
LB는 노드 외부에 존재하며 클러스터와 동등한 대역대(211.183.3.0/24)의 아이피를 갖는다.
근데 우리는 온프레미스에 클러스터를 구축했기 때문에 그 기능이 존재하지 않음. Bare-metal환경에 직접 구축을 했기 때문에 metalLB라는 애드온을 추가하여 로드밸런싱기능을 구현해야 함

root@master:~/mani# cd ~
root@master:~# wget https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
- 웹에 올려진 매니페스트를 설치해도 되지만 나중에 클러스터 재구축하면 또 쓸 수 있으므로 wget으로 다운로드를 하자
kubectl apply -f metallb-native.yaml
- 설치


- 우리가 서비스 타입을 LoadBalancer로 하여 LB를 생성하면, 211.183.3.0/24 대역의 아이피를 갖는 리소스가 하나 생성이 됨. 하지만 이건 실제 네트워크인 211.183.3.0/24 대역의 입장에서는 가상의 리소스이며, 인지 불가능 클러스터 = 집. 집안에서 만든 리소스, 집 밖의 가상의 주소를 부여받음.
- 따라서 이렇게 부여받은 아이피는 211.183.3.0/24 네트워크 입장에서는 알 수 없음
- 그래서 LB에 부여된 아이피를 집 외부(211.183.3.0/24) 대역에 알려줘야 하는데, 이게 speaker의 역할. 내가 로드벨런서 생성해서 211.183.3.200이라는 아이피를 부여할 거니까 너네도 그렇게 알아 라는 통신을 전달함

root에 넣어주기
vi config-metal.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 211.183.3.200-211.183.3.240 # 안겹치게 수정
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
- IPAddressPool: LB를 생성했을 때 부여받을 아이피 대역대 정의
- L2Advertisement: LB에 특정한 아이피가 부여됐다는 사실을 다른 네트워크 구성원들에게 알려주는 역할(설치 시 speaker라는 pod가 정상 동작)
root@master:~# vi lbtest.yml
apiVersion: v1
kind: Namespace
metadata:
name: ip-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ip-dep
namespace: ip-ns
spec:
replicas: 2
selector:
matchLabels:
app: myipnginx
template:
metadata:
labels:
app: myipnginx
spec:
containers:
- name: ip-con
image: oolralra/ipnginx
---
apiVersion: v1
kind: Service
metadata:
name: svc-ipnginx
namespace: ip-ns
spec:
type: LoadBalancer
selector:
app: myipnginx
ports:
- port: 80
targetPort: 80
kubectl delete -f hw1.yml
- 겹칠 수도 있으니까 전에 만든 파일 삭제
root@master:~/mani# kubectl apply -f lbtest.yml
root@master:~/mani# kubectl get svc -n ip-ns
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-ipnginx LoadBalancer 10.97.198.78 211.183.3.200 80:30996/TCP 3m39s
root@master:~/mani# curl 211.183.3.200
request_method : GET | ip_dest: 10.244.1.25
실습 1)
https://github.com/oolralra/simple_jar
# 이미 빌드가 되어있는 springboot 앱. 8085 포트 사용.
211.183.3.150~160정도의 IP로 접속했을 때 위 앱이 뜨도록 한번 만들어보세요.
ns : spring-ns
svc: svc-spring
pod의 label => app: spring
컨테이너 레지스트리는 제 것을 써도 괜찮고 도커허브 쓰셔도 됩니다.
git clone https://github.com/oolralra/simple_jar
cd simple_jar
root@master:~/mani/simple_jar# vi /etc/docker/daemon.json

{
"insecure-registries": ["61.254.18.30:5000"]
}
vi Dockerfile
FROM 61.254.18.30:5000/openjdk:8-jre-alpine
WORKDIR /app
COPY springbootApp.jar app.jar
CMD ["java","-jar","app.jar"]
docker build -t 61.254.18.30:5000/pcm/spring:1 .
docker push 61.254.18.30:5000/pcm/spring:1
docker rmi -f 61.254.18.30:5000/pcm/spring:1
docker run -dp 80:8085 --name test 61.254.18.30:5000/pcm/spring:1

docker rm -f test
cp ../lbtest.yml ./spring.yml
- 아까 썼었던 매니페스트를 복제.
apiVersion: v1
kind: Namespace
metadata:
name: spring-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-dep
namespace: spring-ns
spec:
replicas: 2
selector:
matchLabels:
app: spring
template:
metadata:
labels:
app: spring
spec:
containers:
- name: spring-con
image: 61.254.18.30:5000/pcm/spring:1
---
apiVersion: v1
kind: Service
metadata:
name: svc-spring
namespace: spring-ns
spec:
selector:
app: spring
type: LoadBalancer
ports:
- port: 80
targetPort: 8085
- targetPort를 실제 앱이 동작하는 포트인 8085로 해주는 게 가장 중요함
- LB를 생성하면 service의 포트인 80을 따라감

kubectl apply -f spring.yml
kubectl apply -f ../../config-metal.yml

오류/ 해결 방안 - 외부 IP로는 웹사이트 접속이 안된다는 문제

root@master:~/mani/simple_jar# kubectl describe svc svc-spring -n spring-ns

root@master:~/mani/simple_jar# kubectl get pods -n spring-ns

- 자체 레지스트리에서 이미지를 문제없이 다운로드할 수 있음
- 이 설정을 추가한 후, 테스트로 이미지를 풀 해보면 정상 동작하는지 확인할 수 있음
master, worker-1, worker-2에 다 복붙 해주기
cat <<EOF >> /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."61.254.18.30:5000"]
endpoint = ["http://61.254.18.30:5000"]
[plugins."io.containerd.grpc.v1.cri".registry.configs."61.254.18.30:5000".tls]
insecure_skip_verify = true
EOF
- insecure_skip_verify = true -> TLS인증서 검증 건너뛰기
master, worker-1, worker-2에 다 복붙 해주기
systemctl restart containerd
- 모든 노드에서 이걸 해야 함.

- endpoints가 생긴 걸 확인할 수 있음.

- 211.183.3.150으로 접속이 잘 됨.

- 쿠버네티스 클러스터에서 Advertisment를 통해 적극적으로 metalLB가 쓰는 아이피를 알려줬기 때문에 다른 노트북이나 네트워크 구성원들이 이 아이피를 인지할 수 있음


Monolithic vs Micro-service
Monolithic | Micro-service |
하나의 앱 -> 동일한 개발 환경에서 모든 기능 구현 |
여러개의 앱 -> 여러개의 개발 환경에서 기능을 나눠서 구현 |
1개의 컨테이너 -> 초기 개발이 빠르고 쉬움 -> 기능을 추가하거나 규모가 커지면 수정하기 불편 |
여러개의 컨테이너 -> 초기 개발이 느리고 어려움 -> 기능을 추가하거나 규모가 커지면 수정하기가 편함 |
- 여러 개의 컨테이너에 각 기능들을 구현하여 path로 라우팅이 가능해야 함
- 일반적인 svc로는 path기반 라우팅이 불가능함
Ingress
- path기반 라우팅이 가능한 리소스
- 만약 위 마이크로 서비스를 LoadBalancer로 구현한다고 하면
- Ingress: 내부 서비스로 라우팅 하는 역할을 하는 리소스를 말함
- Ingress Controller: 클러스터 외부에서 내부 서비스로 트래픽을 효율적으로 전달하고, 다양한 라우팅 규칙을 적용할 수 있음
- 서비스는 한 종류의 label만 품을 수 있기 때문에 이런 한계가 발생함
- Ingress는 클러스터 내에서 요청을 어떻게 처리할지를 정의하는 리소스이고, Ingress Controller는 Ingress리소스의 규칙을 실제로 적용하고 트래픽을 라우팅 하는 실행 엔진임
1. ingress 리소스: 라우팅 규칙을 정의한 리소스
2. ingress Controller: 실제로 트래픽을 처리하는 엔진
- ingress controller가 필요. 되도록이면 인그리스 컨트롤러를 설치할 때 metalLB를 먼저 구성해 주는 게 좋음.
https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters
- 버전이 계속 바뀌는 ingress-controller 설치 페이지.
- 인그리스 컨트롤러 설치 매니페스트 ingress-nginx라는 네임스페이스에 기능이 설치됨
vi deploy.yml

- type: LoadBalancer로 수정
kubectl apply -f deploy.yaml
kubectl get pod,svc -n ingress-nginx

- 마지막 controller만 1/1이면 됨
kubectl get svc -n ingress-nginx

- 아이피를 잘 부여받았음
vi deploy-svc.yml
apiVersion: v1
kind: Service
metadata:
name: svc-ip
spec:
selector:
app: ipnginx
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ip-dep
spec:
replicas: 3
selector:
matchLabels:
app: ipnginx
template:
metadata:
name: ip-pod
labels:
app: ipnginx
spec:
containers:
- name: ip-con
image: 61.254.18.30:5000/ipnginx
- 나중에 ingress를 구성할 때 서비스의 이름으로 찾아갈 것이기 때문에, svc의 이름(svc-ip)을 기억해 둬야 됨
kubectl apply -f deploy-svc.yml
kubectl describe svc svc-ip


- svc -> pod로 트래픽이 잘 인가되고, pod도 정산인지 확인하기 위함
- ingress에 문제가 있을 때 트러블슈팅하기 용이함
vi ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: rapa.com
http:
paths:
- path: /httpd
pathType: Prefix
backend:
service:
name: svc-ip #서비스의 이름
port:
number: 80 #서비스의 포트
✅ Deployment, Service, Ingress의 관계
여기서 복습할 겸 Deployment, Service, Ingress 의 관계 흐름에 대해 설명하자면
Deployment: 실제로 애플리케이션을 실행하고 관리하는 역할을 함
- pod는 애플리케이션이 실행되는 단위인데, Deployment는 여러 pod들을 자동으로 관리해 줌
Service: pod들 간의 네트워크 통신을 돕는 역할을 함
- 언제든지 ip주소가 변경될 수 있기 때문에, 외부에서 직접 연결하는 게 어려워서 Service는 고정된 IP를 제공하고, pod들에 트래픽을 분배해 줌
Ingress: 외부에서 들어오는 http요청을 kubernetes내부의 서비스로 라우팅 하는 역할을 수행
- 클러스터 밖에서 들어오는 트래픽을 어떤 서비스로 보낼지 결정해 주고, 도메인이나 경로에 따라서 트래픽을 분배함
이제 각 리소스가 어떻게 서로 연결되는지 흐름을 설명할게:
- Deployment: 애플리케이션(예: 웹 서버)을 실행하고 Pod들을 관리함.
- Pod들이 Deployment에서 관리되고 실행됨.
- Service: Deployment로 실행된 Pod들에 대한 고정된 접근 경로를 제공함.
- 클러스터 내에서 서비스 이름으로 연결되고, 외부에서는 NodePort나 LoadBalancer를 통해 접속할 수 있음
- Ingress: 외부 트래픽이 도메인과 경로에 따라 적절한 서비스로 라우팅되게 함.
- Ingress는 Service를 대상으로 외부 요청을 전달하며, Nginx Ingress Controller 같은 컨트롤러가 실제로 요청을 처리함.
# annotations : 추가적인 정보. labels와 비슷하지만 주로 부가적인 기능 명시.
# '인그리스 컨트롤러는 nginx'이며,
# rewrite-target: /
# path를 /httpd를 통해서 해당 앱으로 가게 되면 경로를 /httpd가 아닌 '/'로 하겠음.
# host: 영문주소, DNS기능이 필요함.
# path: /httpd
# 위의 호스트와 조합하여 'rapa.com/httpd 로 들어왔을 때'를 의미함.
# backend: 서비스를 뜻함
# service.name: svc-ip
# 연결시켜 줄 svc.
vi /etc/hosts

- DNS-server를 구성하긴 그러니까, rapa.com을 안내해 주기 위해 /etc/hosts에 적어줌

- rapa.com 주소가 211.183.3.151로 매칭이 되어있음

- rapa.com에서 ingress-controller의 svc IP를 통해서 ingress로 넘어감(path기반 라우팅)
오류 /해결 방안 - image는 가져오지 못한다고 하는데 curl은 찍어지는 이상한 현실...
실습 2)
rapa.com/httpd 으로 접속했을 때
61.254.18.30:5000/ipnginx 이 보이도록 이미 구성되어 있음.
rapa.com/ 으로 접속했을 때
61.254.18.30:5000/hnginx 이 보이도록 해보세요.
" 하나의 ingress가 여러 개의 svc를 포함한다. "
1. svc와 deployment 생성
cp ip.yml h.yml
vi h.yml
apiVersion: v1
kind: Service
metadata:
name: svc-h
spec:
selector:
app: hnginx
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: h-dep
spec:
replicas: 3
selector:
matchLabels:
app: hnginx
template:
metadata:
name: h-pod
labels:
app: hnginx
spec:
containers:
- name: h-con
image: 61.254.18.30:5000/hnginx
kubectl apply -f h.yml
2. ingress 수정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: rapa.com
http:
paths:
- path: /httpd
pathType: Prefix
backend:
service:
name: svc-ip #서비스의 이름
port:
number: 80 #서비스의 포트
- path: /
pathType: Prefix
backend:
service:
name: svc-h #서비스의 이름
port:
number: 80 #서비스의 포트


질문 1) 노트북에서는 rapa.com 이 접속이 안 되냐? => 안됩니다. 노트북은 rapa.com을 모르기 때문. rapa.com의 IP를 아는 호스트는 현재 /etc/hosts에 등록해 준 master노드뿐임.
질문 2) ingress-controller의 svc는 ingress-nginx라는 네임스페이스에 존재하는데 다른 리소스들은 default 네임스페이스에 존재한다. 어떻게 트래픽이 인가되냐? => ingress-controller의 IP는 클러스터 외부에 존재하는 IP이기 때문에 ingress와 svc와 pod들만 같은 네임스페이스에 있으면 됨.
실습 3)
mario라는 이름의 ingress를 생성하세요.
aws8.com/ 로 접속 시 8085 포트로 동작하는 61.254.18.30:5000/pcm/spring:1 , 서비스포트는 8085
aws8.com/h로 접속 시 80 포트로 동작하는 61.254.18.30:5000/hnginx , 서비스포트는 81
aws8.com/ip로 접속 시 80 포트로 동작하는 61.254.18.30:5000/ipnginx , 서비스포트는 81
로드밸런서 타입의 경우엔, svc의 포트를 따라가지만, ingress 경우엔 그렇지 않음
aws8.com/h로 접속하면 특정 서비스의 포트까지 명시하므로, 따로 포트를 명시할 필요가 없음
ex) aws8.com:81/h 이런 식으로 할 필요가 없다는 뜻.


vi ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: aws8-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: aws8.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-spring #서비스의 이름
port:
number: 8085 #서비스의 포트
- path: /h
pathType: Prefix
backend:
service:
name: svc-h #서비스의 이름
port:
number: 81 #서비스의 포트
- path: /ip
pathType: Prefix
backend:
service:
name: svc-ip #서비스의 이름
port:
number: 81 #서비스의 포트
vi spring.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-dep
spec:
replicas: 2
selector:
matchLabels:
app: spring
template:
metadata:
labels:
app: spring
spec:
containers:
- name: spring-con
image: 61.254.18.30:5000/pcm/spring:1
---
apiVersion: v1
kind: Service
metadata:
name: svc-spring
spec:
selector:
app: spring
type: LoadBalancer
ports:
- port: 8085
targetPort: 8085
vi h.yml
apiVersion: v1
kind: Service
metadata:
name: svc-h
spec:
selector:
app: hnginx
ports:
- port: 81
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: h-dep
spec:
replicas: 3
selector:
matchLabels:
app: hnginx
template:
metadata:
name: h-pod
labels:
app: hnginx
spec:
containers:
- name: h-con
image: 61.254.18.30:5000/hnginx
vi ip.yml
apiVersion: v1
kind: Service
metadata:
name: svc-ip
spec:
selector:
app: ipnginx
ports:
- port: 81
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ip-dep
spec:
replicas: 3
selector:
matchLabels:
app: ipnginx
template:
metadata:
name: ip-pod
labels:
app: ipnginx
spec:
containers:
- name: ip-con
image: 61.254.18.30:5000/ipnginx
kubectl apply -f spring.yml
kubectl apply -f h.yml
kubectl apply -f ip.yml

vi /etc/hosts 수정


잘되는지 확인
오류/ 해결 방안 - 경고 부분 수정

- spec.ingressClassName 이 부분을 따로 풀어서 쓰면
spec:
ingressClassName

- 경고가 뜬 부분을 수정.
'AWS Cloud School 8기 > 쿠버네티스' 카테고리의 다른 글
[쿠버네티스] 헬름(Helm) (0) | 2025.04.23 |
---|---|
[쿠버네티스] 컨테이너의 헬스체크/ livenessProbe/ readinessProbe/ StatefulSet(리소스)/ DaemonSet(리소스) (4) | 2025.04.17 |
[쿠버네티스] PV(Persistent Volume)/ AccessMode/ StorageClass/ ConfigMap/ Secret (2) | 2025.04.16 |
[쿠버네티스] label/ ReplicaSet/ kubectl 명령 자동완성/ Deployment/ namespace/ Service/ NodePort / 삭제 모음 zip (0) | 2025.04.11 |
[쿠버네티스] Kubernetes/ 클러스터/ CRI/ 매니패스트 (2) | 2025.04.10 |