실습환경
구성하기
실습환경은 지난 번과 동일하게 cloudformation으로 구성합니다.
먼저, 실습 template를 입력합니다.
https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-6w.yaml
그 뒤에 파라미터를 입력해줍니다.
stack name은 적절히 mylab으로 입력했습니다.
ec2 key-pair 이름은 이전에 만들어 둔 키페어를 재활용했습니다.
t3.medium이 기본 값이지만 필요시 더 큰 사이즈의 인스턴스로 변경하시면 됩니다.
모든 입력을 완료 한 후 submit을 클릭하여 스택을 배포합니다.
잠시 기다리면 모든 배포가 완료된 것을 확인할 수 있습니다.
4대의 인스턴스가 생성되어 있는 걸 알 수 있습니다.
확인하기
cloudformation으로 구성된 실습환경을 확인하겠습니다.
먼저, EC2 서비스에서 k3s-s 인스턴스의 public IP를 확인합니다.
이후, terminal에서 ssh 명령으로 k3s-s 인스턴스에 접속합니다.
노드 정보
kubectl get nodes -o wide
파드 정보
kubectl get pod -o wide -A
config 정보
# -v 는 verbose 옵션입니다. kubectl 프로그램의 로그 레벨을 더 높인다는 의미입니다.
kubectl get pod -v=6
config 파일의 위치입니다.
/etc/rancher/k3s/k3s.yaml
네트워크 확인
# 인스턴스에 등록된 ip 리스트
ip -c addr
# 인스턴스에 등록된 라우팅 정보
ip -c route
#flannel cni의 서브넷 정보
cat /run/flannel/subnet.env
# 쿠버네티스에 등록된 노드들이 가지고 있는 pod CIDR 정보
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
# 노드의 annotation 정보. 여기서는 각 노드의 flannel cni 구성 정보 확인
kubectl describe node | grep -A3 Annotations
# 이더넷 브릿지 정보 확인
brctl show
서비스, endpoint 정보
kubectl get svc,ep -A
kubectl 명령어를 활용하여 서비스, 엔드포인트 정보를 확인합니다.
iptables 정보 확인
iptables -t filter -S | wc -l
iptables -t nat -S | wc -l
iptables -t mangle -S | wc -l
각 테이블 (filter, nat, mangle)의 iptables 규칙 확인이 가능합니다.
위와 같이 실습환경을 준비하였고, 구성을 확인하였습니다.
Ingress
소개
오늘 살펴볼 주제는 Ingress 입니다. 인그레스는 클러스터 외부의 요청을 클러스터 내부로 전달해주는 역할을 합니다.
아니 서비스에서 이미 클러스터 외부에 있는 요청을 내부로 전달하지 않았나요 ?
서비스의 노드포트나 로드밸런서 타입도 물론 클러스터 외부의 요청을 내부로 전달할 수 있지만 인그레스는 조금 다르게 동작합니다.
위 도표와 같이 Ingress는 통신 암호화와 L7 계층에서 동작을 지원합니다. 이를 통해 외부로 노출된 인그레스 리소스로부터 적절한 서비스 - 파드로 HTTP, HTTPS 요청이 전달될 수 있습니다. 이를 간단한 구조로 표현하면 다음과 같습니다.
물론, 인그레스를 어떻게 구성하냐에 따라 인그레스로부터 파드로 직접통신될 수 있습니다.
이러한 인그레스를 사용하기 위해서는 인그레스 컨트롤러를 구성해야합니다. 인그레스 컨트롤러는 실제 인그레스의 동작을 구현하는 구현체라고 생각하면 됩니다. 인그레스 컨트롤러는 https://kubernetes.io/ko/docs/concepts/services-networking/ingress-controllers/ 에서 더 찾아볼 수 있습니다.
결국, 쿠버네티스에서는 인그레스 API만을 정의하고 실제 구현은 Add-on 에 맡기는 구조입니다. ingress 중에서 ingress-nginx에 대해 살펴보겠습니다. nginx-ingress와는 다릅니다. nginx-ingress는 F5에서 관리하는 인그레스 컨트롤러이고 ingress-nginx는 쿠버네티스에서 관리하는 인그레스 컨트롤러입니다. (헷갈리지 말것)
알아두어야할 내용
인그레스는 현재 새로운 기능이 제공되지 않습니다. 인그레스를 대신할 gateway API라는 다른 유형의 리소스가 등장하였습니다.
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
# externalTrafficPolicy 설정
kubectl patch svc -n ingress ingress-nginx-controller -p '{"spec":{"externalTrafficPolicy": "Local"}}'
위 명령어를 실행해보겠습니다.
1. ingress-nginx controller 매니페스트를 정의합니다.
2. ingress-nginx 헬름을 추가하고 앞서 정의한 컨트롤러 메니페스트를 적용합니다.
설정 확인
리소스가 정상적으로 적용되었는지 확인합니다. configmap 에는 ingress 리소스가 참조하는 label, annotation이 포함되어 있는 것을 확인할 수 있습니다.
kubectl get configmap ingress-nginx-controller -n ingress -o yaml
kc describe clusterroles ingress-nginx
컨트롤러의 권한을 확인합니다.
인그레스 컨트롤러 파드에서 ingress 컨트롤러의 정보를 확인할 수 있습니다.
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
여기서 웃긴 점은 앞에서 ingress-nginx, nginx-ingress차이를 말했었죠? 우리가 구성한 인그레스 컨트롤러는 ingress-nginx인데 인그레스 컨트롤러 파드에서 컨트롤러 정보를 확인하는 명령어는 nginx-ingress-controller 입니다. 뭔가 여기저기서 마구잡이로 사용되었을 수 있다는 느낌이 듭니다.
Ingress 실습
이제 본격적으로 인그레스 실습을 해보겠습니다. 실습할 구성은 다음과 같습니다.
실습 목표
- 컨트롤플레인 노드에 인그레스 컨트롤러 생성, NodePort 외부 노출
- 인그레스 정책 설정 (Host/Path Routing)
실습내용을 구성해보겠습니다.
아래는 실행되는 매니페스트 상세내용입니다.
매니페스트
svc1-pod.yaml
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
svc2-pod.yaml
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
svc3-pod.yaml
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
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
ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-2
spec:
ingressClassName: nginx
rules:
- host: kans.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
- host: "*.kans.com"
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
canary-svc1-pod.yaml
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
canary-svc2-pod.yaml
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
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
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
실습 1. 인그레스 적용 및 리소스 확인
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
위 코드블럭을 실행시키면 아래 동영상과 같이 pod,ep, svc가 구성됩니다.
위에 설정된 파드, 엔드포인트, 서비스에 인그레스 설정을 추가해보겠습니다. 위에 정의한 ingress1.yaml을 적용해줍니다.
아래 영상과 같이 ingress - svc - pod 구성이 적용됩니다.
인그레스 컨트롤러에 기록된 Rule은 실제로 인그레스 컨트롤러 파드의 nginx.conf에 적용됩니다.
kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf | grep 'location /' -A5 | more
인그레스 컨트롤러를 통해서 접속해보겠습니다.
컨트롤러의 인입 포트(30080)와 Public IP(13.124.30.188)를 확인한 다음 Rule에 맞게 url을 입력하여 접속해보겠습니다.
http://13.124.30.188:30080/
http://13.124.30.188:30080/guest
http://13.124.30.188:30080/admin
접속이 잘 되는 것을 확인할 수 있습니다.
실습 2. 패킷 캡쳐 및 분석
패킷을 캡쳐하여 통신이 어떤식으로 이루어지는지 알아보겠습니다.
아래 명령어를 활용하여 패킷을 저장 해줍니다.
tcpdump -i $(ifconfig | grep veth | tail -1 | awk '{print $1}' | cut -d ':' -f 1) tcp port 8080 -w /home/ubuntu/ingress-nginx-admin.pcap && chown ubuntu /home/ubuntu/ingress-*
http://13.124.30.188:30080/admin 주소에 1회 접속 후 패킷캡쳐를 종료합니다.
scp -i ~/.ssh/yeoli.pem ubuntu@13.124.30.188:/home/ubuntu/ingress-nginx-admin.pcap .
패킷을 다운로드 받은 다음 Wireshark로 패킷 덤프를 열어보겠습니다.
이 패킷 덤프에서는 172.16.0.4 와 172.16.3.5 사이에 통신이 있는 것으로 보입니다.
이를 cluster에서 리소스를 조회해보면 172.16.0.4는 ingress controller의 ip, 172.16.3.5는 admin pod의 ip로 확인됩니다.
실제로 인그레스 컨트롤러
HTTP 요청에서는 요청자 IP(저의 public ip)와 클러스터의 노드포트 정보가 포함되어 있습니다.
실습 3. 호스트 기반 라우팅
ingress2.yaml 매니페스트를 적용해보겠습니다.
적용시 아래와 같이 ingress-2 가 생성됩니다.
호스트는 kans.com, *.kans.com이 아닌 yeoli.com, *.yeoli.com 로 설정하였습니다.
이후 내 PC에서 host 파일을 수정해야합니다. root 계정에서 아래 명령어를 사용하여 hosts 파일을 수정해줍니다.
K3S_PUBLIC_IP='13.124.30.188' # 구성된 클러스터의 public ip를 기입해주세요.
echo $K3S_PUBLIC_IP yeoli.com >> /etc/hosts
잘 접속되는 것을 확인할 수 있습니다.
실습 4. 카나리 업그레이드
카나리 업그레이드 대상이 될 파드를 배포해줍니다.
kubectl apply -f canary-svc1-pod.yaml,canary-svc2-pod.yaml
마찬가지로 인그레스도 배포해줍니다.
kubectl apply -f canary-ingress1.yaml,canary-ingress2.yaml
여기서 중요한 부분은
canary-ingress2.yaml의 annotation입니다.
canary: true
canary-weight: 10
즉 카나리 설정을 활성화하고 카나리 비율을 10퍼센트로 한다는 설정을 포함합니다.
이 weight은 ingress-canary-v2로 들어오는 요청의 비율을 뜻합니다.
kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=50
ingress-canary-v2 로 들어오는 요청을 조절하면 아래 동영상과 같이 실시간으로 버전에 따른 요청량을 조절할 수 있습니다.
개발 사이클에서 사용자 경험을 더욱 높일 수 있는 방법으로 활용될 수 있습니다.
정리
단순히 서비스를 활용하면 쿠버네티스 클러스터 내부에 있는 파드를외부에서 요청할 수 있습니다. 다만 클러스터 내에서 여러 서비스를 다양하게 제어 하기에는 역부족입니다. 이를 해결해주기 위해 인그레스를 도입할 수 있습니다. 인그레스를 통해 더 다양한 요구사항을 처리할 수 있습니다.
인그레스 도입시 활용가능한 기능은 다음과 같습니다.
- 호스트라우팅
- 카나리 배포전략
- 애플리케이션 계층 구성
하지만, 인그레스는 앞으로 추가적인 개발이 없을 예정입니다. 인그레스를 대체할 리소스는 Gateway API 입니다. Ingress를 잘 알고 Gateway API로 잘 migration하여 즐거운 쿠버네티스 삶이 되시길 바랍니다.
감사합니다.
'스터디' 카테고리의 다른 글
[KANS 3기] 8주차 Cilium CNI (2) | 2024.10.26 |
---|---|
[KANS 3기] 6주차 GatewayAPI (7) | 2024.10.12 |
[KANS 3기] 5주차 MetalLB (6) | 2024.10.05 |
KCD 참석후기 - 쿠버네티스에서 스케줄링 작동 방식 (0) | 2024.09.28 |
[KANS 3기] 3주차 Calico 네트워크 모드와 접근통제(3/3) (0) | 2024.09.21 |