[Study 6주차] 쿠버네티스 Ingress
- -
CoudNet@ 팀의 가시다님께서 리딩하시는 KANS Study (Kubernetes Advanced Networking Study) 6주차 스터디 내용 정리
이번 주차에서는 쿠버네티스의 Ingress 동작 방식에 대해서 공부하였습니다.
1. Ingress
1.1. Ingress 소개
Ingress 는 클러스터 외부에서 클러스터 내부의 파드에 접근하기 위한 진입점 역할을 합니다.
앞서 Service 또한 진입점 역할을 한다고 배웠었는데요,
Ingress 와 Service 의 가장 큰 차이점은 Ingress 는 7계층에서 동작하고, Service 는 4계층 동작을 한다는 것입니다.
즉, Ingress 는 HTTP / HTTPS 통신 요청을 받아서 처리해주는 것이 가능하며
이를 이용한 경로기반이나 호스트 기반 라우팅 등의 고급 라우팅 기능을 사용할 수 있습니다.
또한 Ingress 를 활용하면 카나리 배포가 가능하여 유연한 업데이트 전략을 사용할 수 있습니다.
마지막으로 SSL/TLS Termination 을 지원합니다.
정리하자면, Service 와 구별되는 Ingress 의 기능은 다음과 같습니다.
Ingress 특징
1. HTTP/HTTPS 을 활용한 경로 기반, 호스트 기반의 고급 라우팅 가능
2. 카나리 배포 지원
3. SSL/TLS 종료 지원
그렇다면 Ingress 는 어떻게 동작할까요??
Ingress 의 실제 동작은 Ingress Controller 에 의해서 처리됩니다.
쿠버네티스는 Ingress 의 API 명세만 정의하고 실제 구현은 다양한 Ingress Controller 구현체에 의해서 진행됩니다.
이번 실습에서는 가장 유명하고 널리 쓰이는 Ingress Nginx Controller 를 사용하여 Ingress 의 동작 원리를 공부해보겠습니다.
1.2. 쿠버네티스 실습 환경 구성
Ingress 의 실습 환경은 AWS EC2 를 기반으로 구성한 K3S 클러스터를 활용하였습니다. (Control Plane 1대, Worker Node 3대)
CloudFormation 을 통한 Kubernetes 구성
# 6주차 Ingress 실습 환경 구성
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-6w.yaml
# CloudFormation 배포 변수 설정 및 배포
TACK_NAME=kimalarm-stack
AWS_REGION=ap-northeast-2
KEY_NAME=kimalarmkey
aws cloudformation deploy \
--template-file kans-6w.yaml \
--stack-name ${STACK_NAME} \
--parameter-overrides KeyName=${KEY_NAME} SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 \
--region ${AWS_REGION}
# Control Plane 서버 접속
ssh -i ~/kans/kimalarmkey.pem ubuntu@43.203.179.198
1.3. Ingress Nginx 구성
Ingress Nginx 는 Helm 을 사용해서 설치하겠습니다.
# Ingress-Nginx 컨트롤러 생성
cat <<EOT> ingress-nginx-values.yaml
controller:
service:
type: NodePort
nodePorts:
http: 30080
https: 30443
nodeSelector:
kubernetes.io/hostname: "k3s-s"
metrics:
enabled: true
serviceMonitor:
enabled: true
EOT
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
kubectl create ns ingress
helm install ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx-values.yaml --namespace ingress --version 4.11.2
# 설치 확인
kubectl get all -n ingress
# Kubecolor 활용하여 서비스 확인
kc describe svc -n ingress ingress-nginx-controller
실습 편의를 위한 부가적인 설정 및 확인
# externalTrafficPolicy 설정 - tcpdump 확인 시, 손쉬운 확인을 위해 활성화
kubectl patch svc -n ingress ingress-nginx-controller -p '{"spec":{"externalTrafficPolicy": "Local"}}'
# 기본 nginx conf 파일 확인
kubectl exec deplo
y/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf
# Ingress Nginx 버전 정보 확인
POD_NAMESPACE=ingress
POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o name)
kubectl exec $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
2. Ingress Nginx 실습
실습 구성도 개요
1. Control Plane 노드에 Ingress Controller 파드를 생성
2. Service 를 NodePort 로 외부 노출
2.1. 실습 환경 구성
Deployment & Service 정보
총 3개의 디플로이먼트와 서비스를 배포할 예정입니다.
첫번 째
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1-websrv
spec:
replicas: 1
selector:
matchLabels:
app: websrv
template:
metadata:
labels:
app: websrv
spec:
containers:
- name: pod-web
image: nginx
---
apiVersion: v1
kind: Service
metadata:
name: svc1-web
spec:
ports:
- name: web-port
port: 9001
targetPort: 80
selector:
app: websrv
type: ClusterIP
두 번째
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2-guestsrv
spec:
replicas: 2
selector:
matchLabels:
app: guestsrv
template:
metadata:
labels:
app: guestsrv
spec:
containers:
- name: pod-guest
image: gcr.io/google-samples/kubernetes-bootcamp:v1
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc2-guest
spec:
ports:
- name: guest-port
port: 9002
targetPort: 8080
selector:
app: guestsrv
type: NodePort
세 번째
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3-adminsrv
spec:
replicas: 3
selector:
matchLabels:
app: adminsrv
template:
metadata:
labels:
app: adminsrv
spec:
containers:
- name: pod-admin
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc3-admin
spec:
ports:
- name: admin-port
port: 9003
targetPort: 8080
selector:
app: adminsrv
Deployment & Service 배포
# 생성
kubectl taint nodes k3s-s role=controlplane:NoSchedule
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/svc1-pod.yaml
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/svc2-pod.yaml
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/svc3-pod.yaml
kubectl apply -f svc1-pod.yaml,svc2-pod.yaml,svc3-pod.yaml
# 배포 확인
kubectl get pod,svc,ep
svc1 과 svc3 은 ClusterIP 이지만, svc2 는 NodePort 인 것을 알 수 있습니다.
ClusterIP 를 활용하면 놀랍게도 Pod 의 ClusterIP 로 트래픽을 Direct Pass 해줄 수 있습니다.
이는 Ingress Controller 가 Service 와 연결된 Pod 의 Service Endpoint (ClusterIP) 리스트를 조회할 수 있는 권한을 가지고 있으며, 이를 주기적으로 체크해서 Ingress 구현체에게 전달해주기 때문입니다.
kubectl describe clusterrole ingress -n ingress | egrep '(Verbs|endpoints)'
kubectl describe roles ingress-nginx -n ingress | egrep '(Verbs|endpoints)'
Ingress 배포
# Ingress 배포 파일 생성
cat <<EOT> ingress1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-1
annotations:
#nginx.ingress.kubernetes.io/upstream-hash-by: "true"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc1-web
port:
number: 80
- path: /guest
pathType: Prefix
backend:
service:
name: svc2-guest
port:
number: 8080
- path: /admin
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
EOT
# Ingress 배포
kubectl apply -f ingress1.yaml
# Ingress 배포 확인
kc describe ingress ingress-1
2.2. Ingress 를 사용한 내부 접속
# Ingress 확인
kubectl get ingress
# 자신의 집 PC 에서 인그레스 접속
echo -e "Ingress1 sv1-web URL = http://$(curl -s ipinfo.io/ip):30080"
echo -e "Ingress1 sv2-guest URL = http://$(curl -s ipinfo.io/ip):30080/guest"
echo -e "Ingress1 sv3-admin URL = http://$(curl -s ipinfo.io/ip):30080/admin"
# PC 로컬에서 서버의 NodePort 로 접속
MYIP=<EC2 공인 IP>
MYIP=43.203.179.198
curl -s $MYIP:30080
curl -s $MYIP:30080/guest
curl -s $MYIP:30080/admin
X-Forwarded-For 설정이 적용되어 있기 때문에, 실제 Client IP (저의 로컬 PC가 사용하는 공인 IP)가 출력되는 것을 알 수 있습니다.
# 실습 종료 후 리소스 삭제
kubectl delete deployments,svc,ingress --all
2.3. Host 기반 라우팅
Ingress 에 매칭된 Host 이름을 보고 라우팅을 해주는 방법에 대해서 공부해보겠습니다.
# host 명을 kimalarm.com 으로 사용
cat <<EOT> ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-2
spec:
ingressClassName: nginx
rules:
- host: kimalarm.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
- host: "*.kimalarm.com"
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
EOT
# Ingress 배포
kubectl apply -f ingress2.yaml,svc3-pod.yaml
# 배포 확인
kubectl get ingress
kubectl describe ingress ingress-2 | sed -n "5, \$p"
Local PC 의 host 파일 변경
접속 테스트를 위해 host 파일을 변경합니다.
MYDOMAIN1=kimalarm.com
MYDOMAIN2=test.kimalarm.com
echo $MYIP $MYDOMAIN1 $MYDOMAIN2
echo "$MYIP $MYDOMAIN1" | sudo tee -a /etc/hosts
echo "$MYIP $MYDOMAIN2" | sudo tee -a /etc/hosts
cat /etc/hosts | grep $MYDOMAIN1
Service 호출
# Domain 1번 호출 - kimalarm.com
curl $MYDOMAIN1:30080 -v
curl $MYDOMAIN1:30080/admin
curl $MYDOMAIN1:30080/echo
curl $MYDOMAIN1:30080/echo/1
# Domain 2번 호출 - test.kimalarm.com
curl $MYDOMAIN2:30080 -v
curl $MYDOMAIN2:30080/admin
curl $MYDOMAIN2:30080/echo
curl $MYDOMAIN2:30080/echo/1
test.kimalarm.com 으로 호출하는 규칙은 /echo 경로에만 규칙이 존재하기 때문에,
/echo 경로가 아닌 다른 경로는 404 오류를 발생시키는 것을 알 수 있습니다.
# 실습 종료 후 리소스 삭제
kubectl delete deployments,svc,ingress --all
2.4. Canary 업데이트
Ingress 를 통한 카나리 업데이트 방법에 대해서 공부해보겠습니다.
버전 1 디플로이먼트
apiVersion: apps/v1
kind: Deployment
metadata:
name: dp-v1
spec:
replicas: 3
selector:
matchLabels:
app: svc-v1
template:
metadata:
labels:
app: svc-v1
spec:
containers:
- name: pod-v1
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: svc-v1
spec:
ports:
- name: web-port
port: 9001
targetPort: 8080
selector:
app: svc-v1
버전 2 디플로이먼트
apiVersion: apps/v1
kind: Deployment
metadata:
name: dp-v2
spec:
replicas: 3
selector:
matchLabels:
app: svc-v2
template:
metadata:
labels:
app: svc-v2
spec:
containers:
- name: pod-v2
image: k8s.gcr.io/echoserver:1.6
ports:
- containerPort: 8080
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: svc-v2
spec:
ports:
- name: web-port
port: 9001
targetPort: 8080
selector:
app: svc-v2
생성 및 확인
# 배포
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/canary-svc1-pod.yaml
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/canary-svc2-pod.yaml
kubectl apply -f canary-svc1-pod.yaml,canary-svc2-pod.yaml
# 확인
kubectl get svc,ep,pod
# 파드 버전 확인 - svc-v1
for pod in $(kubectl get pod -o wide -l app=svc-v1 |awk 'NR>1 {print $6}'); do curl -s $pod:8080 | egrep '(Hostname|nginx)'; done
# 파드 버전 확인 - svc-v2
for pod in $(kubectl get pod -o wide -l app=svc-v2 |awk 'NR>1 {print $6}'); do curl -s $pod:8080 | egrep '(Hostname|nginx)'; done
Canary 배포를 위한 Ingress 생성
# Ingress 1
cat <<EOT> canary-ingress1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-canary-v1
spec:
ingressClassName: nginx
rules:
- host: kans.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-v1
port:
number: 8080
EOT
# Ingress 2
cat <<EOT> canary-ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-canary-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: kans.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-v2
port:
number: 8080
EOT
# 도메인 변경
MYDOMAIN1=kimalarm.com
sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress1.yaml
sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress2.yaml
# Ingress 배포
kubectl apply -f canary-ingress1.yaml,canary-ingress2.yaml
Local PC 에서 접속 테스트
# 100번 접속을 시도하여 연결되는 버전 확인
for i in {1..100}; do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
canary-weight: "10" 의 값이 10이기 때문에 9:1 의 비율로 분배되는 것을 알 수 있습니다.
Canary 비율 조정 후 재시도
# 50 : 50 비율로 변경
kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=50
# 100번 접속을 시도하여 연결되는 버전 확인
for i in {1..100}; do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
# 실습 종료 후 자원 삭제
kubectl delete deployments,svc,ingress --all
이번에는 Ingress 의 다양한 동작 방법에 대해서 공부해보았습니다.
다만, Ingress 의 여러 한계점으로 인해 Ingress 는 향후 Gateway API 로 전면 대체될 예정이라고 합니다.
앞으로 Ingress 에 대한 업데이트는 없다고 하니 Gateway API 도 공부하여 마이그레이션 하는 과정이 필요할 것 같습니다.
'Kubernetes' 카테고리의 다른 글
[Study 7주차] 쿠버네티스 Service Mesh - Envoy Proxy, Istio (1) | 2024.10.19 |
---|---|
[Study 6주차] 쿠버네티스 Gateway API (3) | 2024.10.13 |
[Study 5주차] 쿠버네티스 Service - IPVS Proxy 모드 (2) | 2024.10.02 |
[Study 5주차] 쿠버네티스 Service - LoadBalancer (MetalLB) (0) | 2024.10.02 |
[Study 4주차] 쿠버네티스 Service - NodePort (1) | 2024.09.28 |