본문 바로가기

컨테이너/쿠버네티스 네트워크

[KANS] 쿠버네티스 네트워크 (11) ingress

목표:

1. ingress 에 대해 알아보자.

2. ingress를 통한 통신 흐름을 알아보자.

3. ingress의 각 기능들을 알아보자.

 

 

인그레스(Ingress) 소개

인그레스란? 

 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할

 

  • 7계층 HTTP/HTTPS를 이해함 - URL,Hostname, path 기반 라우팅 가능
  • 추상화된 API 오브젝트로 이를 통해 클러스터 내부의 svc에 external 접근을 가능하게 함 , 특히 HTTP
  • 로드밸런싱 , SSL 종료 , 카나리 업그레이드 
  • 하지만 ingress frozen 앞으로는 Gateway API만 개발될 예정

Ingress-Controller란?

외부에서 인그레스로 접속 시 Nginx 인그레스 컨트롤러 파드로 인입되고, 이후 애플리케이션 파드의 IP로 직접 통신

 

Ingress-Controller 설치 방식도 여러개?

  • NodePort 사용
  • MetalLB 사용
  • Via the host network 사용
  • Using a self-provisioned edge 사용
  • External IPs 사용

 

Ingress-Controller 설치 방식( NodePort )

  • 간편한 테스트를 위해서 NodePort 타입(externalTrafficPolicy: Local) 설정

  • 노드 포트 형태로 설치 
# 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
kc describe svc -n ingress ingress-nginx-controller

# externalTrafficPolicy 설정
kubectl patch svc -n ingress ingress-nginx-controller -p '{"spec":{"externalTrafficPolicy": "Local"}}'

# 기본 nginx conf 파일 확인
kc describe cm -n ingress ingress-nginx-controller
kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf

# 관련된 정보 확인 : 포드(Nginx 서버), 서비스, 디플로이먼트, 리플리카셋, 컨피그맵, 롤, 클러스터롤, 서비스 어카운트 등
kubectl get all,sa,cm,secret,roles -n ingress
kc describe clusterroles ingress-nginx
kubectl get pod,svc,ep -n ingress -o wide -l app.kubernetes.io/component=controller

# 버전 정보 확인
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) 를 통한 통신 흐름

인그레스 통신흐름 

1. nodeport로 인그레스 컨트롤러 배포

2. 인그레스 정책 설정: Host/Path Routing (실습의 편의를 위해서 도메인 없이 IP로 접속 설정)

  • 서비스 1 생성 
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
  • 서비스 2 생성
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
  • 서비스 2 / 디플로이 생성
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
  • ingress 설정 확인 

    • 외부 클라이언트 통신 

  • 클라이언트 ( 내 노트북 공유기 IP ) 에서 호출

  • 클라이언트 IP가 보존된것을 확인 ( SNAT 안됨 ) - externalTrafficPolicy: local

  •  svc3-admin 접속 > 기본적으로 Nginx 는 라운드로빈 부하분산 알고리즘을 사용
  •  Client_address 와 XFF 주소는 어떤 주소인가요?
    • Client_address : ingress- controller pod IP
    • XFF 주소 :  SK Telecome IP가 찍힘 ( whois 118.235.6.43 ) 으로 확인 가능
IP 주소 118.235.6.43는 SK Telecom의 공인 IP 주소입니다. 이 IP 주소는 SK Telecom이 보유한 IP 대역 중 하나로, 그 사용자는 SK Telecom의 네트워크를 통해 인터넷에 연결된 기기일 수 있습니다. .
whois 명령을 통해 이 IP 주소가 SK Telecom에 할당된 범위에 속하는지를 확인한 것처럼, 이 IP는 SK Telecom의 인터넷 서비스나 그와 관련된 네트워크 장비에 사용될 가능성이 큽니다. 공인 IP는 인터넷에 직접 노출된 IP로, 사용자가 특정 인터넷 서비스 제공자를 통해 네트워크에 접속할 때 해당 네트워크의 공인 IP가 나타나게 됩니다.
따라서 이 IP 주소는 SK Telecom을 통해 인터넷에 연결된 클라이언트 혹은 서버의 IP 주소로 이해할 수 있습니다

 

인그레스 특징 

1. Host / Path 기반의 라우팅

인그레스 룰 생성

  • ingress.yaml
cat <<EOT> 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
EOT
  • 자기 도메인으로 변경
# 터미널1
watch -d 'kubectl get ingresses,svc,ep,pod -owide'

# 도메인 변경
MYDOMAIN1=<각자 자신의 닉네임의 도메인> 예시) gasida.com
MYDOMAIN1=gasida.com
sed -i "s/kans.com/$MYDOMAIN1/g" ingress2.yaml

# 생성
kubectl apply -f ingress2.yaml,svc3-pod.yaml

# 확인
kubectl get ingress
kubectl describe ingress ingress-2

kubectl describe ingress ingress-2 | sed -n "5, \$p"
Rules:
  Host        Path  Backends
  ----        ----  --------
  kans.com    /     svc3-admin:8080 ()
  *.kans.com  /echo svc3-admin:8080 ()
...
  • 배포 
# 로그 모니터링
kubetail -n ingress -l app.kubernetes.io/component=controller

# (옵션) ingress nginx 파드 vethY 에서 패킷 캡처 후 확인 해 볼 것

------------
# 자신의 PC 에서 접속 테스트
# svc3-admin 접속 > 결과 확인 : 왜 접속이 되지 않는가? HTTP 헤더에 Host 필드를 잘 확인해보자!
curl $MYIP:30080 -v
curl $MYIP:30080/echo -v

curl $MYIP:30080/echo -v
*   Trying [64:ff9b::2bca:484]:30080...
* Connected to 43.202.4.132 (64:ff9b::2bca:484) port 30080
> GET /echo HTTP/1.1
> Host: 43.202.4.132:30080
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< Date: Thu, 10 Oct 2024 23:26:40 GMT
< Content-Type: text/html
< Content-Length: 153
< Connection: keep-alive


# mypc에서 접속을 위한 설정
## /etc/hosts 수정 : 도메인 이름으로 접속하기 위해서 변수 지정
## 윈도우 C:\Windows\System32\drivers\etc\hosts
## 맥 sudo vim /etc/hosts
MYDOMAIN1=<각자 자신의 닉네임의 도메인>
MYDOMAIN2=<test.각자 자신의 닉네임의 도메인>
MYDOMAIN1=koo.com
MYDOMAIN2=test.koo.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

# svc3-admin 접속 > 결과 확인
curl $MYDOMAIN1:30080 -v
curl $MYDOMAIN1:30080/admin
curl $MYDOMAIN1:30080/echo
curl $MYDOMAIN1:30080/echo/1

curl $MYDOMAIN2:30080 -v
curl $MYDOMAIN2:30080/admin
curl $MYDOMAIN2:30080/echo
curl $MYDOMAIN2:30080/echo/1
curl $MYDOMAIN2:30080/echo/1/2

## (옵션) /etc/hosts 파일 변경 없이 접속 방안
curl -H "host: $MYDOMAIN1" $MYIP:30080

  • ingress host routing 되는 부분을 확인 할수 있음. test.koo.com으로 접근하는것은 ingress-controller에 404 에러로 표시됨

 

2.  Canary + Bluegreen 배포

Blue-Green 전략은 2쌍의 서비스를 띄워 놓고 트래픽을 돌리는 전략 - 롤백이 쉽고 업데이트 소요 시간을 줄일수 있음 , 다만 서버를 2쌍을 띄워야 하는 리소스 적 문제가 있음

Canary 전략은 트래픽 제어를 통해 신서버로 조금씩 트래픽을 흘려 검증 과정을 거친 후 배포하는 전략

(⎈|default:N/A) root@k3s-s:~/canary# cat <<EOT> 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
EOT
(⎈|default:N/A) root@k3s-s:~/canary# cat <<EOT> 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
EOT
(⎈|default:N/A) root@k3s-s:~/canary# ls
canary-svc1-pod.yaml  canary-svc2-pod.yaml
(⎈|default:N/A) root@k3s-s:~/canary# kubectl apply -f canary-svc1-pod.yaml,canary-svc2-pod.yaml
deployment.apps/dp-v1 created
service/svc-v1 created
deployment.apps/dp-v2 created
service/svc-v2 created
(⎈|default:N/A) root@k3s-s:~/canary# 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
(⎈|default:N/A) root@k3s-s:~/canary# 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
(⎈|default:N/A) root@k3s-s:~/canary# MYDOMAIN1=koo.com
(⎈|default:N/A) root@k3s-s:~/canary# sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress1.yaml
sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress2.yaml
(⎈|default:N/A) root@k3s-s:~/canary#
  • 배포

# 터미널1
watch -d 'kubectl get ingress,svc,ep'

# 도메인 변경
MYDOMAIN1=<각자 자신의 닉네임의 도메인> 예시) gasida.com
sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress1.yaml
sed -i "s/kans.com/$MYDOMAIN1/g" canary-ingress2.yaml

# 생성
kubectl apply -f canary-ingress1.yaml,canary-ingress2.yaml

# 로그 모니터링
kubetail -n ingress -l app.kubernetes.io/component=controller

# 접속 테스트
curl -s $MYDOMAIN1:30080
curl -s $MYDOMAIN1:30080 | grep nginx

# 접속 시 v1 v2 버전별 비율이 어떻게 되나요? 왜 이렇게 되나요?
for i in {1..100};  do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
for i in {1..1000}; do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
while true; do curl -s --connect-timeout 1 $MYDOMAIN1:30080 | grep Hostname ; echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

# 비율 조정 >> 개발 배포 버전 전략에 유용하다!
kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=50

# 접속 테스트
for i in {1..100};  do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
for i in {1..1000}; do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr

# (옵션) 비율 조정 << 어떻게 비율이 조정될까요?
kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=100
for i in {1..100};  do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr

# (옵션) 비율 조정 << 어떻게 비율이 조정될까요?
kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=0
for i in {1..100};  do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr

 

3.  SSL Termination

cat <<EOT> ssl-termination-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: https
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - kans.com
    secretName: secret-https
  rules:
  - host: kans.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-https
            port:
              number: 8080
EOT

cat <<EOT> tls-ingress1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-https
  labels:
    app: https
spec:
  containers:
  - name: container
    image: k8s.gcr.io/echoserver:1.6
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
  name: svc-https
spec:
  selector:
    app: https
  ports:
  - port: 8080
EOT
# 서비스와 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/7/svc-pod.yaml
kubectl apply -f svc-pod.yaml

# 도메인 변경
MYDOMAIN1=<각자 자신의 닉네임의 도메인> 예시) gasida.com
MYDOMAIN1=kans.com
echo $MYDOMAIN1
sed -i "s/kans.com/$MYDOMAIN1/g" ssl-termination-ingress.yaml

# 인그레스 생성
kubectl apply -f ssl-termination-ingress.yaml

# 인증서 생성
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=dkos.com/O=dkos.com"mkdir key && cd key
MYDOMAIN1=kans.com
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=$MYDOMAIN1/O=$MYDOMAIN1"
tree

# Secret 생성
kubectl create secret tls secret-https --key tls.key --cert tls.crt

# Secret 확인 
kubectl get secrets secret-https
kubectl get secrets secret-https -o yaml

-------------------
# 자신의 PC 에서 접속 확인 : PC 웹브라우저
# 접속 확인 : -k 는 https 접속 시 : 접속 포트 정보 확인
curl -Lk https://$MYDOMAIN1:30443

## (옵션) /etc/hosts 파일 변경 없이 접속 방안
curl -Lk -H "host: $MYDOMAIN1" https://$MYDOMAIN1:30443
curl -Lk https://$MYDOMAIN1:30443



Hostname: pod-https

Pod Information:
	-no pod information available-

Server values:
	server_version=nginx: 1.13.1 - lua: 10008

Request Information:
	client_address=172.16.0.5
	method=GET
	real path=/
	query=
	request_version=1.1
	request_uri=http://koo.com:8080/

Request Headers:
	accept=*/*
	host=koo.com:30443
	user-agent=curl/8.7.1
	x-forwarded-for=211.234.196.67
	x-forwarded-host=koo.com:30443
	x-forwarded-port=443
	x-forwarded-proto=https
	x-forwarded-scheme=https
	x-real-ip=211.234.196.67
	x-request-id=920edc2da4a13f97c60d6b02953acf7a
	x-scheme=https

Request Body:
	-no body in request-