WellSpring

6주차 - Ingress & Gateway API 본문

KANS3 - k8s Advanced Networking Study

6주차 - Ingress & Gateway API

daniel00324 2024. 10. 7. 20:44

※ 본 게재 글은 gasida님의 KANS(Kubernetes Advanced Networking Study) 강의내용과 실습예제 및 kubernetes, Ingress, k3s 공식 가이드 문서, "쿠버네티스입문" 서적 등을 참고하여 작성하였습니다. 

 


0. 실습환경 구성

▷ 사전준비 : AWS account, SSH keypair

▷ 구성요약 : VPC 1개(퍼블릭 서브넷 2개), EC2 인스턴스 4대 (Ubuntu 22.04 LTS, t3.medium - vCPU 2 , Mem 4)

  . CloudFormation 스택 실행 시 파라미터를 기입하면, 해당 정보가 반영되어 배포됩니다.

  . CloudFormation 에 EC2의 UserData 부분(Script 실행)으로 실습 환경에 필요한 기본 설정들이 자동으로 진행됩니다.

 

▷ CloudFormation 스택 배포

    ( aws-cli 설치aws-configure 완료 전제로 수행 : Ref. Link - https://daniel00324.tistory.com/11)

더보기
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-6w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-1w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-6w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t3.xlarge (vCPU 4, Mem 16)
예시) aws cloudformation deploy --template-file kans-6w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t3.xlarge KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# EC2 SSH 접속 : 바로 접속하지 말고, 3~5분 정도 후에 접속 할 것
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
...
(⎈|default:N/A) root@k3s-s:~# <- kubeps 가 나오지 않을 경우 ssh logout 후 다시 ssh 접속 할 것!

 

[ 실행 결과 - 한 눈에 보기 ]

 

  k3s : Lightweight Kubernetes. Easy to install, half the memory, all in a binary of less than 100 MB

        - Ref. Link : Docs

  . 특징 : rancher 회사에서 제작한 경량의 k8s ( IoT 및 edge computing 디바이스 위에서도 동작할 수 있도록 만들어짐 ) 

  . 장점 : 설치가 쉽다, 가볍다(etcd, cloud manager 등 무거운 컴포넌트 제거), 학습용 및 테스트 시 필요한 기능들은 대부분 탑재

 

[ More ... ]

더보기

What is k3s ?

K3s is a fully compliant Kubernetes distribution with the following enhancements:

  • Distributed as a single binary or minimal container image.
  • Lightweight datastore based on sqlite3 as the default storage backend. etcd3, MySQL, and Postgres are also available.
  • Wrapped in simple launcher that handles a lot of the complexity of TLS and options.
  • Secure by default with reasonable defaults for lightweight environments.
  • Operation of all Kubernetes control plane components is encapsulated in a single binary and process, allowing K3s to automate and manage complex cluster operations like distributing certificates.
  • External dependencies have been minimized; the only requirements are a modern kernel and cgroup mounts.
  • Packages the required dependencies for easy "batteries-included" cluster creation:
    • containerd / cri-dockerd container runtime (CRI)
    • Flannel Container Network Interface (CNI)
    • CoreDNS Cluster DNS
    • Traefik Ingress controller
    • ServiceLB Load-Balancer controller
    • Kube-router Network Policy controller
    • Local-path-provisioner Persistent Volume controller
    • Spegel distributed container image registry mirror
    • Host utilities (iptables, socat, etc)

[ Architecture ] - Ref. Link : https://docs.k3s.io/architecture

 . 위에 언급된 특징 그대로, 1개의 Process 내에 API Server, Kube Proxy를 비롯한 K8S Control Plane 구성에

   필요한 요소들이 다 Packaging 되어 있다.

 

  • Servers and Agents : 서버(Controlplane) 와 에이전트(Work nodes)

1) 서버 노드는 K3s에서 관리하는 제어 영역 및 데이터 저장소 구성요소와 함께 k3s 서버 명령어를 실행하는

    호스트로 정의됩니다.

2) 에이전트 노드는 데이터 저장소나 제어 영역 구성요소 없이 k3s 에이전트 명령어를 실행하는 호스트로 

    정의됩니다.

3) 서버와 에이전트 모두 kubelet, 컨테이너 런타임 및 CNI를 실행합니다. 에이전트 없는 서버 실행에 대한 

    자세한 내용은 고급 옵션 문서를 참조하세요.

 

  • Single-server Setup with an Embedded DB : 1대 K3S 서버(경량 DB = SQLite), 필요한 만큼의
    K3S Agents (Worker nodes)

 

  • High-Availability K3s : EmbeddedDB, External DB

k3s 기본 정보 확인 : k8s v1.30.x → 현재 ingress-nginx controller 이 1.30.x 까지 버전 호환 테스트 완료

    (‘24.10.6 일 기준)  

더보기

  (참고) k3s 설치 옵션 : aws cloudformation 에 ec2 에 userdata 로 실행되어 있음 - Docs , Server , Agent

# Install k3s-server
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC=" --disable=traefik"  sh -s - server --token kanstoken --cluster-cidr "172.16.0.0/16" --service-cidr "10.10.200.0/24" --write-kubeconfig-mode 644 

# Install k3s-agent
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.10.10:6443 K3S_TOKEN=kanstoken  sh -s -

 [ 실행 결과 ]

 

  

  k3s 는 경량화를 위해서 k8s 와 다름

# 노드 확인
kubectl get node -owide
NAME     STATUS   ROLES                  AGE   VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
k3s-s    Ready    control-plane,master   23m   v1.30.5+k3s1   192.168.10.10    <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.21-k3s2
k3s-w1   Ready    <none>                 23m   v1.30.5+k3s1   192.168.10.101   <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.21-k3s2
k3s-w2   Ready    <none>                 23m   v1.30.5+k3s1   192.168.10.102   <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.21-k3s2
k3s-w3   Ready    <none>                 23m   v1.30.5+k3s1   192.168.10.103   <none>        Ubuntu 22.04.5 LTS   6.8.0-1015-aws   containerd://1.7.21-k3s2

# kubecolor alias 로 kc 설정 되어 있음
kc describe node k3s-s  # Taints 없음
kc describe node k3s-w1


# 파드 확인
kubectl get pod -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
coredns-7b98449c4-jmhgk                   1/1     Running   0          21m
local-path-provisioner-6795b5f9d8-w6h8s   1/1     Running   0          21m
metrics-server-cdcc87586-m4ndt            1/1     Running   0          21m

#
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'
kubectl get storageclass


# config 정보(위치) 확인
kubectl get pod -v=6
I1006 13:04:02.858105    4325 loader.go:395] Config loaded from file:  /etc/rancher/k3s/k3s.yaml
I1006 13:04:02.872677    4325 round_trippers.go:553] GET https://127.0.0.1:6443/api/v1/namespaces/default/pods?limit=500 200 OK in 5 milliseconds
No resources found in default namespace.

cat /etc/rancher/k3s/k3s.yaml
export | grep KUBECONFIG

# 네트워크 정보 확인 : flannel CNI(vxlan mode), podCIDR
ip -c addr
ip -c route
cat /run/flannel/subnet.env
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
kubectl describe node | grep -A3 Annotations
brctl show

# 서비스와 엔드포인트 확인
kubectl get svc,ep -A


# iptables 정보 확인
iptables -t filter -S
iptables -t nat -S
iptables -t mangle -S

# tcp listen 포트 정보 확인
ss -tnlp

 

 [ 실행 결과  - 한 눈에 보기 ]

k3s 가 k8s와 다른점 : control-plane 역할을 하는 노드에 Taint가 없다!!
k3s의 경량화 프로세스 구성

1. 인그레스(Ingress)

1-1. 인그레스란?

쿠버네티스 Ingress란 HTTP(S) 기반의 L7 로드밸런싱 기능을 제공하는 컴포넌트이다.

      Ingress는 외부에서 쿠버네티스 내부로 들어오는 네트워크 요청을 어떻게 처리할지 결정하는 역할을 한다.

     쿠버네티스 서비스는 기본적으로 L4 레이어로 TCP 단에서 Pod를 로드밸런싱한다. 우리가 구현하는

     MSA(Micro Service Architecture)에서는 서비스 1개 당 URL 1개로 mapping 되며, 이러한 구조는 kubernetes

     에서도 마찬가지이다. MSA 상에서 각 서비스 간 통신을 위해서 API Gateway 혹은 L7-LB와 같은 역할이 필요하며

     이러한 역할을 Ingress가 해주게 된다.

출처 : https://kubernetes.io/ko/docs/concepts/services-networking/ingress/

 

[ More ... ]

더보기

<<  Ingress 역할 >> 

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

. Make your HTTP (or HTTPS) network service available using a protocol-aware configuration mechanism, that understands web
  concepts like URIs, hostnames, paths, and more. The Ingress concept lets you map traffic to different backends based on rules
  you define via the Kubernetes API.

. An API object that manages external access to the services in a cluster, typically HTTP.

. Ingress may provide load balancing, SSL termination and name-based virtual hosting.

. Ingress is frozen. New features are being added to the Gateway API.

 

<< Ingress 의 기능 >>

출처 : 김태민 기술 블로그 (https://kubetm.github.io/k8s/08-intermediate-controller/ingress/)

 

  인그레스의 실제 동작 구현은 인그레스 컨트롤러(Nginx, Kong 등)가 담당

 

  인그레스 + 인그레스 (Nginx) 컨트롤러 : HTTP(서비스) 부하분산 , 카나리 업그레이드 , HTTPS 처리(TLS 종료)

 

☞ 인그레스 비교

https://docs.google.com/spreadsheets/d/191WWNpjJ2za6-nbG4ZoUMXMpUK8KlCIosvQB0f-oq3k/

1-2. Ingress 발전단계

1단계 ) 파드 생성 : K8S 클러스터 내부에서만 접속

 

2단계 ) 서비스(Cluster Type) 연결 : K8S 클러스터 내부에서만 접속

→ 동일한 애플리케이션의 다수의 파드의 접속을 용이하게 하기 위한 서비스에 접속

 

3단계) 서비스(NodePort Type) 연결 : 외부 클라이언트가 서비스를 통해서 클러스터 내부의 파드로 접속

→ 서비스(NodePort Type)의 일부 단점을 보완한 서비스(LoadBalancer Type) 도 있습니다!

 

4단계) 인그레스 컨트롤러 파드를 배치 : 서비스 앞단에 HTTP 고급 라우팅 등 기능 동작을 위한 배치

→  인그레스(정책)이 적용된 인그레스 컨트롤러 파드(예. nginx pod)를 앞단에 배치하여 고급 라우팅 등 기능을 제공

 

5단계) 인그레스 컨트롤러 파드 이중화 구성 : Active(Leader) - Standby(Follower) 로 Active 파드 장애에 대비

 

 

6단계) 인그레스 컨트롤러 파드외부에 노출 : 인그레스 컨트롤러 파드를 외부에서 접속하기 위해서 노출(expose)

 → 인그레스 컨트롤러 노출 시 서비스(NodePort Type) 보다는 좀 더 많은 기능을 제공하는 서비스(LoadBalancer Type)를 권장
      (80/443 포트 오픈 시)

 

7단계) 인그레스와 파드간 내부 연결의 효율화 방안 : 인그레스 컨트롤러 파드(Layer7 동작)에서 서비스 파드의 IP로 직접 연결

 →  인그레스 컨트롤러 파드는 K8S API서버로부터 서비스의 엔드포인트 정보(파드 IP)를 획득 후 바로 파드의 IP로 연결 - 링크

 →  지원되는 인그레스 컨트롤러 : Nginx, Traefix 등 현재 대부분의 인그레스 컨트롤러가 지원함

 

1-3. Ingress 동작

 1) HTTP/HTTPs를 부하분산하여, 내부에 파드로 Traffic 전달함

    - 경로기반 (Path based) or 호스트기반(host based) 라우팅을 지원

 

 2) 유연한 배포전략을 지원함

    - Canary deployment ( 사용자 영향도 최소화 )

 

 3) SSLTLS 종료를 지원함

    - Ingress 이하 내부 통신 구간에서는 보안 Risk가 낮아, 평문 통신으로 Payload를 줄일 수 있다!!

 

[ Ingress를 통한 Kubernetes 내부 통신 ]

 

☞ 인그레스의 실제 동작 구현은 인그레스 컨트롤러(Nginx, Kong 등)가 처리 - 링크

 1) 쿠버네티스는 Ingress API 만 정의하고 실제 구현은 add-on 에 맡김

 2) Ingress-Nginx Controller - 링크 ⇒ 간편한 테스트를 위해서 NodePort 타입(externalTrafficPolicy: Local) 설정

 

 

 3) 다양한 Nginx 인그레스 컨트롤러 인입 방법

   -> MetalLB 사용, Via the host network 사용, Using a self-provisioned edge 사용, External IPs 사용 - 링크

 

 

Ingress NGINX Controller for Kubernetes - Home , Github

더보기
  • ingress-nginx is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer.
  • How it works : configmap 설정을 nginx config 에 적용(by lua), 변경 시 (최소) reload - Docs
 

How it works - Ingress-Nginx Controller

How it works The objective of this document is to explain how the Ingress-NGINX controller works, in particular how the NGINX model is built and why we need one. NGINX configuration The goal of this Ingress controller is the assembly of a configuration fil

kubernetes.github.io

 - The goal of this Ingress controller is the assembly of a configuration file (nginx.conf).

 - The main implication of this requirement is the need to reload NGINX after any change in the configuration file. 

  . Though it is important to note that we don't reload Nginx on changes that impact only an upstream configuration
    (i.e Endpoints change when you deploy your app)
.

- We use lua-nginx-module to achieve this. Check below to learn more about how it's done.


- When a reload is required

 ⊙ New Ingress Resource Created.

  TLS section is added to existing Ingress.

  Change in Ingress annotations that impacts more than just upstream configuration.
      For instance
load-balance annotation does not require a reload.

  A path is added/removed from an Ingress.

  An Ingress, Service, Secret is removed.

  Some missing referenced object from the Ingress is available, like a Service or Secret.

  A Secret is updated.

 

Ingress-Nginx 컨트롤러 생성 - ArtifactHub release

더보기
# 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

 

[ 실습 결과 - 한 눈에 보기 ]  ** Helm Chart 구성 및 Helm 통한 ingress-nginx 구성

 

(옵션) krew 설치

더보기
# (참고) 운영체제 확인 : linux
OS="$(uname | tr '[:upper:]' '[:lower:]')"
# (참고)  CPU 아키텍처 확인 : amd64
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')"
# (참고)  KREW 지정 : krew-linux_amd64
KREW="krew-${OS}_${ARCH}"

# kubectl krew 설치
# curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz"
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz" && tar zxvf krew-linux_amd64.tar.gz && ./krew-linux_amd64 install krew
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

# 플러그인 정보 업데이트 후 확인 - 링크
kubectl krew update
kubectl krew search

# ingress-nginx 플러그인 설치
kubectl krew install ingress-nginx

# ingress-nginx 플러그인 명령어 실행(도움말 출력)
kubectl ingress-nginx

# nginx ctrl 의 backends 설정 정보 출력
kubectl ingress-nginx backends -n ingress-nginx --list
kubectl ingress-nginx backends -n ingress-nginx

# conf 출력
kubectl ingress-nginx conf -n ingress-nginx
## 특정 호스트(도메인) 설정 확인
kubectl ingress-nginx conf -n ingress-nginx --host gasida.cndk.link
kubectl ingress-nginx conf -n ingress-nginx --host nasida.cndk.link

# 정보 보기 편함!
kubectl ingress-nginx ingresses
kubectl ingress-nginx ingresses --all-namespaces

 

[ 실행결과 - 한 눈에 보기 ]

 

 

1-4.  인그레스(Ingress) 실습 및 통신 흐름 확인

[ 실습 구성도 ] 

- 컨트롤플레인 노드에 인그레스 컨트롤러(Nginx) 파드를 생성, NodePort 로 외부에 노출

- 인그레스 정책 설정 : Host/Path routing, 실습의 편리를 위해서 도메인 없이 IP로 접속 설정 가능

HTTPS의 경우, Ingress-Nginx POD 에서 암복호화 이후, Back-end는 평문통신을 한다.

 

1.4.1 Deployment & Service 생성

▼ 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

▼ Pod 생성 확인

더보기
# 모니터링
watch -d 'kubectl get ingress,svc,ep,pod -owide'

# 생성
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

# 확인 : svc1, svc3 은 ClusterIP 로 클러스터 외부에서는 접속할 수 없다 >> Ingress 는 연결 가능!
kubectl get pod,svc,ep

1.4.2 인그레스(정책) 생성 - 링크

▼ ingress1.yaml

더보기
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

▼ 인그레스 생성 및 확인

더보기
# 모니터링
watch -d 'kubectl get ingress,svc,ep,pod -owide'

# 생성
kubectl apply -f ingress1.yaml

# 확인
kubectl get ingress
kc describe ingress ingress-1
...
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /        svc1-web:80 ()
              /guest   svc2-guest:8080 ()
              /admin   svc3-admin:8080 ()
...

# 설정이 반영된 nginx conf 파일 확인
kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf
kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf | grep 'location /' -A5
		location /guest/ {

			set $namespace      "default";
			set $ingress_name   "ingress-1";
			set $service_name   "svc2-guest";
			set $service_port   "8080";
--
  		location /admin/ {

			set $namespace      "default";
			set $ingress_name   "ingress-1";
			set $service_name   "svc3-admin";
			set $service_port   "8080";
--
  		location / {

			set $namespace      "default";
			set $ingress_name   "ingress-1";
			set $service_name   "svc1-web";
			set $service_port   "80";
--
...

[ 실습 결과 - 한 눈에 보기 ]

 


1.4.3 인그레스 통한 내부 접속

Nginx 인그레스 컨트롤러를 통한 접속(HTTP 인입) 경로 : 인그레스 컨트롤러 파드에서 서비스 파드의 IP로 직접 연결 (아래 오른쪽 그림)

 

[ More ... ]

더보기

1. (참고) URI(Uniform Resource Identifier, RFC3986)

 - Request URI 는 서버 주소나 파일 이름, 파라미터 등 다양한 리소스를 식별하기 위해 사용하는 문자열입니다

 - 절대 URI(absolute URI, 필요한 모든 정보를 기술) 와 상대 URI(relative URI, 상대적인 위치)가 있습니다.

  ♠ 스킴이름 // 서버 주소 : 포트 번호 / 파일 경로 ? 쿼리 문자열 # 프래그먼트 식별자

 

 2. (참고) X-Forwarded-For 헤더, X-Forwarded-Proto 헤더

  - X-Forwarded-For 헤더는 송신지 IP 주소가 변환되는 환경(장비, 서버, 솔루션 등)에서, 변환 전 송신지(클라이언트) IP 주소

    저장하는 헤더입니다.

  - X-Forwarded-Proto 헤더는 변환 전 프로토콜을 저장합니다. (예. SSL Offload 환경에서 서버 측에서 클라이언트가 요청 시

    사용한 원래 프로토콜을 확인)


  ☞ XFF IP가 여러개가 존재 시 어떻게 될까? 예) "15.1.1.1 3.35.23.2 10.0.0.109" 올바른 처리 방법은?

  - Client IP 를 가져오는 방법 ( Ref. Link - 링크1 링크2 )

   . 웹 애플리케이션이 client IP를 추출하기 위해서 Http request header를 다음과 같은 순서로 뒤짐

    1) Proxy-Client-IP : 특정 웹 어플리케이션에서 사용 (예. WebLogic Connector - mod_wl)

    2) WL-Proxy-Client-IP : 특정 웹 어플리케이션에서 사용 (예. WebLogic Connector - mod_wl)

    3) X-Forwarded-For : HTTP RFC 표준에는 없지만 사실상 표준!!!

    4) request.getRemoteAddr()

    5) CLIENT_IP

 


인그레스(Nginx 인그레스 컨트롤러)를 통한 접속(HTTP 인입) 확인 (☆★ ☆★ ☆★)

   : HTTP 부하분산 & PATH 기반 라우팅, 애플리케이션 파드에 연결된 서비스는 Bypass

더보기
# (krew 플러그인 설치 시) 인그레스 정책 확인
kubectl ingress-nginx ingresses
INGRESS NAME   HOST+PATH   ADDRESSES       TLS   SERVICE      SERVICE PORT   ENDPOINTS
ingress-1      /           192.168.10.10   NO    svc1-web     80             1
ingress-1      /guest      192.168.10.10   NO    svc2-guest   8080           2
ingress-1      /admin      192.168.10.10   NO    svc3-admin   8080           3

#
kubectl get ingress
NAME        CLASS   HOSTS   ADDRESS        PORTS   AGE
ingress-1   nginx   *       10.10.200.24   80      3m44s
 
kubectl describe ingress ingress-1 | sed -n "5, \$p"
Rules:
  Host        Path   Backends
  ----        ----   --------
  *           /      svc1-web:80 ()
              /guest svc2-guest:8080 ()
              /admin svc3-admin:8080 ()


# 접속 로그 확인 : kubetail 설치되어 있음 - 출력되는 nginx 의 로그의 IP 확인
kubetail -n ingress -l app.kubernetes.io/component=controller

-------------------------------
# 자신의 집 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"

# svc1-web 접속
MYIP=<EC2 공인 IP>
MYIP=13.124.93.150
curl -s $MYIP:30080

# svvc2-guest 접속
curl -s $MYIP:30080/guest
curl -s $MYIP:30080/guest
for i in {1..100}; do curl -s $MYIP:30080/guest ; done | sort | uniq -c | sort -nr

# svc3-admin 접속 > 기본적으로 Nginx 는 라운드로빈 부하분산 알고리즘을 사용 >> Client_address 와 XFF 주소는 어떤 주소인가요?
curl -s $MYIP:30080/admin
curl -s $MYIP:30080/admin | egrep '(client_address|x-forwarded-for)'
for i in {1..100}; do curl -s $MYIP:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr


# (옵션) 디플로이먼트의 파드 갯수를 증가/감소 설정 후 접속 테스트 해보자

노드에서 아래 패킷 캡처 확인 : flannel vxlan, 파드 간 통신 시 IP 정보 확인

더보기
#
ngrep -tW byline -d ens5 '' udp port 8472 or tcp port 80

#
tcpdump -i ens5 udp port 8472 -nn

# vethY는 각자 k3s-s 의 가장 마지막 veth 를 지정
tcpdump -i vethY tcp port 8080 -nn
tcpdump -i vethY tcp port 8080 -w /tmp/ingress-nginx.pcap

# 자신의 PC에서 k3s-s EC2 공인 IP로 pcap 다운로드
scp ubuntu@<k3s-s EC2 공인 IP>:/tmp/ingress-nginx.pcap ~/Downloads
scp ubuntu@43.202.1.177:/tmp/ingress-nginx.pcap ~/Downloads

[ 실행 결과 - 한 눈에 보기 ]

 

[ XFF 통한 외부 client 발 admin service 호출 Traffic 분석내용 ]

 

 

[ Tip & Tips : X-Forwarded-For 란? ]

더보기

X-Forwarded-For (XFF) 헤더는 HTTP 프록시나 로드 밸런서를 통해 웹 서버에 접속하는 클라이언트의 원 IP 주소를 식별하는 사실상의 표준 헤더다. 클라이언트와 서버 중간에서 트래픽이 프록시나 로드 밸런서를 거치면, 서버 접근 로그에는 프록시나 로드 밸런서의 IP 주소만을 담고 있다. 클라이언트의 원 IP 주소를 보기위해 X-Forwarded-For 요청 헤더가 사용된다.

 

- Ref. Link : Blog, Site

Nginx 파드가 endpoint 정보 등을 모니터링 가능한 이유

     : 클러스터롤(엔드포인트 list, watch)를 바인딩된 서비스 어카운트를 파드가 사용하기 때문

더보기
# (옵션) Nginx 파드가 endpoint 정보 등을 모니터링 가능한 이유 : 클러스터롤과 롤(엔드포인트 list, watch)를 바인딩된 서비스 어카운트를 파드가 사용!
kubectl describe clusterrole ingress -n ingress | egrep '(Verbs|endpoints)'
[root@k8s-m:~/yaml (ctx-k8s:default)]# kubectl describe clusterrole ingress-nginx -n ingress-nginx | egrep '(Verbs|endpoints)'
  Resources                           Non-Resource URLs  Resource Names  Verbs
  endpoints                           []                 []              [list watch]

kubectl describe roles ingress-nginx -n ingress | egrep '(Verbs|endpoints)'
[root@k8s-m:~/yaml (ctx-k8s:default)]# kubectl describe roles ingress-nginx -n ingress-nginx | egrep '(Verbs|endpoints)'
  Resources                           Non-Resource URLs  Resource Names                     Verbs
  endpoints                           []                 []                                 [get list watch]

 

▼ (심화) nginx ingress controller 이 endpoint ip(port) list 획득 코드 내용 - 링크

klog.V(3).Infof("Getting Endpoints for Service %q and port %v", svcKey, port.String())
	ep, err := getServiceEndpoints(svcKey)
	if err != nil {
		klog.Warningf("Error obtaining Endpoints for Service %q: %v", svcKey, err)
		return upsServers
	}

▶ (심화 참고) nginx-ingress 설정 시 annotation으로 설정 불가능한 것을 적용 시 ConfigMap 으로 설정 - 링크


1.4.4 분산 알고리즘 변경

▶ nginx 는 기본 RR 라운드 로빈 이지만, IP-Hash 나 Session Cookie 설정으로 대상 유지 가능 - 링크

더보기
# mypc
for i in {1..100}; do curl -s $MYIP:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr
while true; do curl -s --connect-timeout 1 $MYIP:30080/admin | grep Hostname ; date "+%Y-%m-%d %H:%M:%S" ; echo "--------------" ; sleep 1; done

# 아래 ingress 설정 중 IP-Hash 설정 > # 주석 제거
sed -i 's/#nginx.ingress/nginx.ingress/g' ingress1.yaml
kubectl apply -f ingress1.yaml

# 접속 확인
for i in {1..100}; do curl -s $MYIP:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr
while true; do curl -s --connect-timeout 1 $MYIP:30080/admin | grep Hostname ; date "+%Y-%m-%d %H:%M:%S" ; echo "--------------" ; sleep 1; done

# 다시 원복(라운드 로빈) > # 주석 추가
sed -i 's/nginx.ingress/#nginx.ingress/g' ingress1.yaml
kubectl apply -f ingress1.yaml

# 접속 확인
for i in {1..100}; do curl -s $MYIP:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr
while true; do curl -s --connect-timeout 1 $MYIP:30080/admin | grep Hostname ; date "+%Y-%m-%d %H:%M:%S" ; echo "--------------" ; sleep 1; done

 

[ 실습 결과 - 한 눈에 보기 ]

★  다음 실습을 위해 오브젝트 삭제 합시다!!

kubectl delete deployments,svc,ingress --all


(참고) AWS Ingress (ALB) 모드

더보기
  • 인스턴스 모드 : AWS ALB(Ingress)로 인입 후 각 워커노드의 NodePort 로 전달 후 IPtables 룰(SEP)에 따라
    파드로 분배

 

  • IP 모드 : nginx ingress controller 동작과 유사하게 AWS LoadBalancer Controller 파드가 kube api 를 통해서 파드의 IP를 제공받아서 AWS ALB 에 타켓(파드 IP)를 설정

 


1.4.5 호스트 기반 라우팅

▶ ingress2.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

▶ Ingress 생성 및 확인

더보기

kans.com 도메인은 각자 자신의 닉네임으로 변경합니다!

# 터미널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 ()
...

 

 

▶ 인그레스(Nginx 인그레스 컨트롤러)를 통한 접속(HTTP 인입) 확인

더보기
# 로그 모니터링
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

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

 

[ 실습 결과 - 한 눈에 보기 ]

★  다음 실습을 위해 오브젝트 삭제 합시다!!

kubectl delete deployments,svc,ingress --all


1.4.6 카나리 업그레이드

▶ 배포 자동화 지원(최소 중단, 무중단) - 롤링 업데이트, 카나리 업데이트, 블루/그린 업데이트 - 링크 링크2 하이커퍼넥스-블로그

더보기
[ 서비스 특성에 따라 결정할 수 있는 대표적 CICD 배포 방식 비교 ]

 

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

▶ 생성 및 확인

더보기
# 터미널1
watch -d 'kubectl get ingress,svc,ep,pod -owide'

# 생성
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

# 파드 버전 확인: 1.13.0 vs 1.13.1
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
	Hostname: dp-v1-cdd8dc687-gcgsz
		server_version=nginx: 1.13.0 - lua: 10008
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
	Hostname: dp-v2-785f69bd6-hh624
		server_version=nginx: 1.13.1 - lua: 10008

[ Canary in the coal mine ]

https://share.america.gov/english-idiom-canary-coal-mine/

canary-ingress1.yaml

더보기
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

 canary-ingress2.yaml

더보기
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

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

 

[ 실행 결과 - 한 눈에 보기 ]

해당 방식으로 10% -> 50% -> 100% 순으로 조정하여 배포를 진행 ( 서비스 영향도 최소화 )

★  다음 실습을 위해 오브젝트 삭제 합시다!!

kubectl delete deployments,svc,ingress --all


1.4.7   HTTPS 처리 (TLS 종료) - 링크

svc-pod.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

ssl-termination-ingress.yaml

더보기
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

생성 확인 및 secret 생성 후 접속 확인

더보기
# 서비스와 파드 생성
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

 

[ 실행 결과 - 한 눈에 보기 ]

  Nginx SSL Termination 패킷 확인 : 중간 172.16.29.11 이 nginx controller

더보기
# 패킷 캡처 명령어 참고
export IngHttp=$(kubectl get service -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[0].nodePort}')
export IngHttps=$(kubectl get service -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[1].nodePort}')
tcpdump -i <nginx 파드 veth> -nnq tcp port 80 or tcp port 443 or tcp port 8080 or tcp port $IngHttp or tcp port $IngHttps
tcpdump -i <nginx 파드 veth> -nn  tcp port 80 or tcp port 443 or tcp port 8080 or tcp port $IngHttp or tcp port $IngHttps -w /tmp/ingress.pcap

 

[ 참고 자료 ]

wireshark 필터 tls.record.opaque_type == 23 or http
ingress-tls.pcap
0.01MB

 

[ 옵션 ] HTTP → HTTPS Redirect 설정 - 링크

더보기
# nginx 서비스 편집 : 아래 내용 추가
kubectl edit svc -n ingress-nginx ingress-nginx-controller
---
  annotaion
    nginx.ingress.kubernetes.io/ssl-redirect: 'true'
---

 

http(kans.com) 접속 시 308 Redirect 로 https(kans.com)으로 접속된다

 

Ref. Link - 308 Permanent Redirect - 링크

[옵션] Custom Errors Pages - 링크

더보기

> 설정 및 확인

# 404, 503, 500 Error 페이지를 이미지를 다운로드
git clone https://github.com/kenmoini/custom-nginx-ingress-errors
cd custom-nginx-ingress-errors
tree
.
├── 404-screenshot.png
├── Dockerfile
├── README.md
├── hooks
│   └── pre_push
├── k8s-deployment.yaml
└── www
    ├── 404.html
    ├── 500.html
    ├── 503.html
    └── css
        └── style.css

# 사용자 정의 default-backend-service를 설정(에러 페이지 출력되는 파드)
kubectl apply -f k8s-deployment.yaml

# 확인
kubectl get pod -n ingress-nginx -l app.kubernetes.io/name=nginx-errors
NAME                            READY   STATUS    RESTARTS   AGE
nginx-errors-6fc8d6dfb9-g469w   1/1     Running   0          45s

kubectl get svc -n ingress-nginx nginx-errors
NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-errors   ClusterIP   10.102.71.33   <none>        80/TCP    54s


# custom-http-errors 관련 설정을 적용하여 nginx 재배포
curl -O https://raw.githubusercontent.com/gasida/KANS/main/6/nginx-ingress.yaml
grep allow-snippet-annotations nginx-ingress.yaml
sed -i'' -r -e "/allow-snippet-annotations/a\  custom-http-errors: \"404,500,503\"" nginx-ingress.yaml
sed -i'' -r -e "/v=3/a\            - --default-backend-service=\$(POD_NAMESPACE)\/nginx-errors" nginx-ingress.yaml
kubectl apply -f nginx-ingress.yaml

# 테스트용 서비스/파드 배포
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

# ingress 정책에 503 에러를 발생시키키 위해서 없는 서비스 등록
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
      - path: /error
        pathType: Prefix
        backend:
          service:
            name: error
            port:
              number: 8080
EOT
kubectl apply -f  ingress1.yaml

# ingress-nginx-controller NodePort(HTTP 접속용) 변수 지정
export IngHttp=$(kubectl get service -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[0].nodePort}')
echo $IngHttp

# 접속 확인 : 웹 브라우저에서 접속
MASTER=192.168.10.10
curl $MASTER:$IngHttp -v

# 404 Error 확인 : 정의 되지 않은 /hello 를 호출해서 404 Error 발생
curl -D- -H 'Accept: application/json' $MASTER:$IngHttp/hello ;echo
HTTP/1.1 404 Not Found
Date: Tue, 08 Mar 2022 08:34:11 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive

{ "message": "The page you're looking for could not be found" }

# 503 Error 확인 : svc1-web 연결된 파드를 0 으로 설정하여 503 Error 발생
curl -D- -H 'Accept: application/json' $MASTER:$IngHttp ;echo
kubectl scale deployment deploy1-websrv --replicas=0
curl -D- -H 'Accept: application/json' $MASTER:$IngHttp ;echo

 

[ 참고 - 그림 ]

[ 옵션 ] Nginx Ingress Controller HA 설정

  실습 - 링크 링크2 Helm

더보기
# nginx ingress controller 삭제
kubectl delete -f https://raw.githubusercontent.com/gasida/KANS/main/6/nginx-ingress.yaml

# helm 로 설치 : hostnetwork 사용
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install ingress-nginx-controller bitnami/nginx-ingress-controller --version 9.1.11 --set replicaCount=2 --set hostNetwork=true --set dnsPolicy=ClusterFirstWithHostNet --set service.type=ClusterIP --create-namespace --namespace ingress-nginx

# nginx-ingress 파드 배포 확인
kubectl get pod -owide -n ingress-nginx -l app.kubernetes.io/component=controller
NAME                                        READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
nginx-ingress-controller-546d8cfc7f-fs9zj   1/1     Running   0          70s    192.168.10.101   k8s-w1   <none>           <none>
nginx-ingress-controller-546d8cfc7f-9f79p   1/1     Running   0          3m2s   192.168.10.102   k8s-w2   <none>           <none>

# 두 대의 워커노드에 keepalived 구성
apt-get install -y keepalived

# Keepalived 설정 확인
k8s-w1)
cat <<EOT> /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
    state MASTER
    interface enp0s8
    virtual_router_id 50
    priority 101
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass cloudneta
    }
    virtual_ipaddress {
        192.168.10.100
    }
}
EOT

k8s-w2)
cat <<EOT> /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
    state MASTER
    interface enp0s8
    virtual_router_id 50
    priority 102
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass cloudneta
    }
    virtual_ipaddress {
        192.168.10.100
    }
}
EOT

# 서비스 재시작
systemctl restart keepalived
systemctl status keepalived

# enp0s8 정보 확인 : VRRP MASTER 상태인 노드의 enp0s8 에 VirtualIP(/32bit)를 소유하고 있다
ip -c addr show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:e7:63:de brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.102/24 brd 192.168.100.255 scope global enp0s8
    inet 192.168.10.100/32 scope global enp0s8
    ...

# 서비스 및 디플로이먼트 배포
# 모니터링
watch -d 'kubectl get ingress,svc,ep;echo; calicoctl get wep'

# 생성
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

# 확인 : svc1, svc3 은 ClusterIP 로 클러스터 외부에서는 접속할 수 없다 >> Ingress 는 연결 가능!
kubectl get pod,svc,ep

# 인그레스 설정
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

# 모니터링
watch -d 'kubectl get ingresses,svc,ep;echo; calicoctl get wep'

# 생성
kubectl apply -f ingress1.yaml

# 확인
kubectl get ingress
kubectl describe ingress ingress-1
kubectl describe ingressclasses

# 인그레스 접속 테스트 (k8s-pc)
VIP=<keepalive 의 Virtual IP>
VIP=192.168.10.100

# svc1-web 접속
curl -s $VIP

# svvc2-guest 접속
curl -s $VIP/guest
curl -s $VIP/guest
for i in {1..100}; do curl -s $VIP/guest ; done | sort | uniq -c | sort -nr

# svc3-admin 접속
curl -s $VIP/admin
curl -s $VIP/admin | egrep '(client_address|x-forwarded-for)'
for i in {1..100}; do curl -s $VIP/admin | grep Hostname ; done | sort | uniq -c | sort -nr

# 확인
kubetail -f

# 장애 테스트
# svvc2-guest 접속 (k8s-pc)
while true; do curl -s --connect-timeout 1 $VIP/guest; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

# 모니터링 (k8s-w1, k8s-w2)
tail -f /var/log/syslog | grep vrrp

# 장애 발생 (k8s-w2) >> svc2-guest 접속 확인
systemctl stop keepalived
ip -c addr show enp0s8

# 장애 원복 (k8s-w2) >> svc2-guest 접속 확인
systemctl start keepalived
ip -c addr show enp0s8

# 삭제
helm uninstall ingress-nginx-controller --namespace ingress-nginx

★  다음 실습을 위해 오브젝트 삭제 합시다!!

kubectl delete deployments,svc,ingress --all

★   Nginx 인그레스 컨트롤러 삭제

helm uninstall -n ingress ingress-nginx

★   모든 실습 후, 자원 삭제는 반드시 해주시기 바랍니다. ( For your sound-mind & purse !! ^^@ )

# CloudFormation 스택 삭제
aws cloudformation delete-stack --stack-name mylab

# [모니터링] CloudFormation 스택 상태 : 삭제 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

 

★    /etc/hosts 에 추가한 내용 삭제


2. Gateway API 소개

Ref. Link - https://medium.com/@disha.20.10/introduction-to-the-gateway-api-revolutionizing-kubernetes-networking-7b0c9a696038

 

2-1. Gateway API 주요기능


1. 개선된 리소스 모델

: API는 GatewayClass, Gateway 및 Route(HTTPRoute, TCPRoute 등)와 같은 새로운 사용자 정의 리소스를 도입하여

  라우팅 규칙을 정의하는 보다 세부적이고 표현력 있는 방법을 제공합니다.

 

2. 프로토콜 독립적

: 주로 HTTP용으로 설계된 Ingress와 달리 Gateway API는 TCP, UDP, TLS를 포함한 여러 프로토콜을 지원합니다.

 

3. 강화된 보안

: TLS 구성 및 보다 세부적인 액세스 제어에 대한 기본 제공 지원

 

4. 교차 네임스페이스 지원

: 서로 다른 네임스페이스의 서비스로 트래픽을 라우팅하여 보다 유연한 아키텍처를 구축할 수 있는 기능을 제공합니다.

 

5. 확장성

: API는 사용자 정의 리소스 및 정책으로 쉽게 확장할 수 있도록 설계되었습니다.

 

6. 역할지향

: 클러스터 운영자, 애플리케이션 개발자, 보안 팀 간의 우려를 명확하게 분리합니다.


2-2. 추가 기능 소개

☆ 기존의 Ingress 에 좀 더 기능을 추가, 역할 분리(role-oriented) - Docs

  - 추가 기능 : 헤더 기반 라우팅, 헤더 변조, 트래픽 미러링(쉽게 트래픽 복제), 역할 기반

 

[ More ... ]

더보기

[ 특징 상세 ]

Gateway API is a family of API kinds that provide dynamic infrastructure provisioning and advanced traffic routing.

  Make network services available by using an extensible, role-oriented, protocol-aware configuration mechanism. 

  Gateway API is an add-on containing API kinds that provide dynamic infrastructure provisioning and advanced traffic routing.


2-3. 구성요소 ( Resources )

☞ GatewayClass,Gateway, HTTPRoute, TCPRoute, Service

https://kubernetes.io/docs/concepts/services-networking/gateway/

 

1) GatewayClass

 - 공통 구성으로 게이트웨이 세트를 정의하고 클래스를 구현하는 컨트롤러에 의해 관리됨

2) Gateway

 - 클라우드 로드 밸런서와 같은 트래픽 처리 인프라의 인스턴스를 정의함

3) HTTPRoute

 - 게이트웨이 수신기의 트래픽을 백엔드 네트워크 엔드포인트 표현으로 매핑하기 위한 HTTP 관련 규칙을 정의합니다. 

   이러한 엔드포인트는 서비스로 표시되는 경우가 많습니다.

 

출처 : https://gateway-api.sigs.k8s.io/

 

♣ 해당 모델은 "관심사 분리" 라는 특수성을 제공하는데, 이는 리소스 계층 별로 관리 담당자의 역할을 나누어 자원을 생성, 관리하는 특징을 잘 표현해 준다.

 

해당 특성에 따라 HTTP Route 이하, Traffic처리에 있어서 개발자 역할을 가진 그룹에서 필요한 기능 개발 및 적용을 함에 있어, 기존 인프라 담당자를 통해 필요한 인프라가 구성되고, 설정이 완료되기 까지 delay 되는 시간을 단축할 수 있는 장점이 있다.

( Public Cloud 의 보안/기능적 측면에서 least-privilaged Role 모델과 부합하는 느낌 ~ )



☞ Request Flow 

 

☞ Role-oriented API : RBAC 등을 활용하여 관리자 별 담당 layer 구성 시 문제가 다른 layer 침범하여 장애가
                                      확산되는 케이스를 막을 수 있다!!

출처 : https://gateway-api.sigs.k8s.io/

[ 계층 별 역할 설명 ]

♣  Infrastructure Provider: Manages infrastructure that allows multiple isolated clusters to serve multiple tenants, e.g. a cloud provider.

♣  Cluster Operator: Manages clusters and is typically concerned with policies, network access, application permissions, etc.

♣  Application Developer: Manages an application running in a cluster and is typically concerned with application-level configuration and Service composition.


2-4. Gloo Gateway

▶ 참고 링크 - Gloo Blog , Docs

Gloo Gateway Architecture

    : These components work together to translate Gloo and Kubernetes Gateway API custom resources into Envoy configuration

 

[ More ... ]

더보기

[ Gloo Gateway Traffic Flow ]

https://docs.solo.io/gateway/latest/about/architecture/

1. The config and secret watcher components in the gloo pod watch the cluster for new Kubernetes Gateway API and Gloo Gateway resources, such as Gateways, HTTPRoutes, or RouteOptions.

2. When the config or secret watcher detect new or updated resources, it sends the resource configuration to the Gloo Gateway translation engine.

3. The translation engine translates Kubernetes Gateway API and Gloo Gateway resources into Envoy configuration. All Envoy configuration is consolidated into an xDS snapshot.

4. The reporter receives a status report for every resource that is processed by the translator.

5. The reporter writes the resource status back to the etcd data store.

6. The xDS snapshot is provided to the Gloo Gateway xDS server component in the gloo pod.

7. Gateway proxies in the cluster pull the latest Envoy configuration from the Gloo Gateway xDS server.

8. Users send a request to the IP address or hostname that the gateway proxy is exposed on.

9. The gateway proxy uses the listener and route-specific configuration that was provided in the xDS snapshot to perform routing decisions and forward requests to destinations in the cluster.

 

[ Translation Engine ]

1. The translation cycle starts by defining Envoy clusters from all configured Upstream and Kubernetes service resources. Clusters in this context are groups of similar hosts. Each Upstream has a type that determines how the Upstream is processed. Correctly configured Upstreams and Kubernetes services are converted into Envoy clusters that match their type, including information like cluster metadata.

2. The next step in the translation cycle is to process all the functions on each Upstream. Function-specific cluster metadata is added and is later processed by function-specific Envoy filters.

3. In the next step, all Envoy routes are generated. Routes are generated for each route rule that is defined on the HTTPRoute and RouteOption resources. When all of the routes are created, the translator processes any VirtualHostOption, ListenerOption, and HttpListenerOption resources, aggregates them into Envoy virtual hosts, and adds them to a new Envoy HTTP Connection Manager configuration.

4. Filter plug-ins are queried for their filter configurations, generating the list of HTTP and TCP Filters that are added to the Envoy listeners.

5. Finally, an xDS snapshot is composed of the all the valid endpoints (EDS), clusters (CDS), route configs (RDS), and listeners (LDS). The snapshot is sent to the Gloo Gateway xDS server. Gateway proxies in your cluster watch the xDS server for new config. When new config is detected, the config is pulled into the gateway proxy.

 

[ Deployment Pattern ] - Ref. Link : docs

 

 

 


[Tutorial] Hands-On with the Kubernetes Gateway API and Envoy Proxy : 양이 상당히 매우 많음 😅 - Blog Github

더보기

◎ Kubernetes-hosted application accessible via a gateway configured with policies for routing, service discovery, timeouts, debugging, access logging, and observability

 

☞ 기본 환경 세팅은 AWS EC2 (5주차 참고) or Vagrant + Virtaul Box 환경 중 택일 하여 진행!!

Step0. 기본 환경설치

더보기

[ 방법 1 : AWS EC2 환경 구성 ]

▷ 구성요소 :   VPC 1개(퍼블릭 서브넷 2개), EC 인스턴스 1대(Ubuntu 22.04 LTS, t3.xlarge)

  ☞ CloudFormation 스택 실행 시 파라미터를 기입하면, 해당 정보가 반영되어 배포됩니다.

  ☞ CloudFormation 에 EC2의 UserData 부분(Script 실행)으로 실습 환경에 필요한 기본 설정들이 자동으로 진행됩니다.

 

▷ CloudFormation Stack 배포

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-5w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-5w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-5w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# EC2 SSH 접속
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)

 

  Ubuntu EC2 SSH 접속 : 기본 정보 확인 ← 스택 생성 시작 후 5분 후 접속 할 것

# SSH 접속 : ubuntu 계정
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
------------------------
# 현재 디렉터리 확인
pwd

# 계정 정보 확인
whoami
id

# ubuntu 버전 확인
lsb_release -a

# 설치 툴 버전 확인
kind version
docker version # 출력 하단에 'WARNING: bridge-nf-call-iptables' 에러 없이 출력 되는지 꼭 확인!
kubectl version --client=true
helm version

 

 


 

[ 방법 2 : Vagrant + VirtualBox 환경 구성 ]

# Ubuntu 배포 관련 Vagrantfile, init_cfg.sh 파일 다운로드 : init.cfg 수정되었으니 다시 다운로드 할 것
curl -O https://raw.githubusercontent.com/gasida/KANS/main/kind/Vagrantfile
curl -O https://raw.githubusercontent.com/gasida/KANS/main/kind/init_cfg.sh

# vagrant 로 ubuntu 가상머신 1대 프로비저닝
vagrant up
... # 설치 스크립트 init_cfg.sh 실행되어 다소 시간 소요

# vagrant 상태 확인
vagrant status

# Ubuntu SSH 접속
vagrant ssh
-----------
# 기본 정보 확인
whoami
hostnamectl
htop
ip -c a

# 툴 설치 확인
docker version # 출력 하단에 'WARNING: bridge-nf-call-iptables' 에러 없이 출력 되는지 꼭 확인!
kind --version
kubectl version --client=true
helm version

# 외부 통신 확인
ping -c 3 8.8.8.8
curl ipinfo.io

# 실습 완료 후 전원 off
poweroff
-----------

# vagrant 상태 확인
vagrant status

# 추후 다시 실습 시 Ubuntu (재)시작 후 Ubuntu SSH 접속
vagrant reload
vagrant ssh

 


Step1. Install Kind Cluster

더보기
#
cat <<EOT> kind-1node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
EOT

# Install KinD Cluster
kind create cluster --image kindest/node:v1.30.0 --config kind-1node.yaml --name myk8s

# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'

# 노드/파드 확인
kubectl get nodes -o wide
kubectl get pod -A

 

[ 실습 결과 - 한 눈에 보기 ]

정상적으로 뜨는 모습을 보니 정겹네요 ~ ^0^/

 

정상적으로 Node와 Pods 가 기동 되었습니다~

Step2. Install Gateway API CRDs : The Kubernetes Gateway API abstractions are expressed using Kubernetes CRDs.

더보기
# CRDs 설치 및 확인
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
kubectl get crd

 

 

[ Tip & Tips - CRD란 ? - Ref. Link - Blog ]

 - 사용자 정의 리소스(Customer Resource Definition)의 준말로, K8s의 강력한 확장기능을 활용할 수 있게 도와줌

 - 자체 스키마 구조 정의문을 통해 API 기능을 보다 rich하게 사용할 수 있게 해 준다.

 

Step3. Install Glooctl Utility : GLOOCTL is a command-line utility that allows users to view, manage, and debug Gloo Gateway deployments - Link

더보기
# [신규 터미널] 아래 bash 진입 후 glooctl 툴 사용
docker exec -it myk8s-control-plane bash
----------------------------------------
# Install Glooctl Utility
## glooctl install gateway     # install gloo's function gateway functionality into the 'gloo-system' namespace
## glooctl install ingress     # install very basic Kubernetes Ingress support with Gloo into namespace gloo-system
## glooctl install knative     # install Knative serving with Gloo configured as the default cluster ingress
## curl -sL https://run.solo.io/gloo/install | sh
curl -sL https://run.solo.io/gloo/install | GLOO_VERSION=v1.17.7 sh
export PATH=$HOME/.gloo/bin:$PATH

# 버전 확인
glooctl version

----------------------------------------

 

Step4. Install Gloo Gateway : 오픈소스 버전

더보기
# [신규 터미널] 모니터링
watch -d kubectl get pod,svc,endpointslices,ep -n gloo-system

# Install Gloo Gateway
helm repo add gloo https://storage.googleapis.com/solo-public-helm
helm repo update
helm install -n gloo-system gloo-gateway gloo/gloo \
--create-namespace \
--version 1.17.7 \
--set kubeGateway.enabled=true \
--set gloo.disableLeaderElection=true \
--set discovery.enabled=false

# Confirm that the Gloo control plane has successfully been deployed using this command
kubectl rollout status deployment/gloo -n gloo-system

# 설치 확인
kubectl get crd | grep 'networking.k8s.io'
kubectl get crd | grep -v 'networking.k8s.io'
kubectl get pod,svc,endpointslices -n gloo-system

#
kubectl explain gatewayclasses
kubectl get gatewayclasses
NAME           CONTROLLER             ACCEPTED   AGE
gloo-gateway   solo.io/gloo-gateway   True       21m

kubectl get gatewayclasses -o yaml
apiVersion: v1
items:
- apiVersion: gateway.networking.k8s.io/v1
  kind: GatewayClass
  metadata:
    labels:
      app: gloo
    name: gloo-gateway
  spec:
    controllerName: solo.io/gloo-gateway
...

 

 [ 실행결과 - 한 눈에 보기 ]

 

Step5. Install Httpbin Application : A simple HTTP Request & Response Service - Link

더보기
#
watch -d kubectl get pod,svc,endpointslices,ep -n httpbin

# Install Httpbin Application
kubectl apply -f https://raw.githubusercontent.com/solo-io/solo-blog/main/gateway-api-tutorial/01-httpbin-svc.yaml

# 설치 확인
kubectl get deploy,pod,svc,endpointslices,sa -n httpbin
kubectl rollout status deploy/httpbin -n httpbin

# (옵션) NodePort 설정
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  labels:
    app: httpbin
    service: httpbin
  name: httpbin
  namespace: httpbin
spec:
  type: NodePort
  ports:
  - name: http
    port: 8000
    targetPort: 80
    nodePort: 30000
  selector:
    app: httpbin
EOF

# (옵션) 로컬 접속 확인
echo "httpbin web - http://localhost:30000"     # macOS 사용자
echo "httpbin web - http://192.168.50.10:30000" # Windows 사용자

 

[ 실행 결과 - 외부 클라이언트 접근 테스트 ]

Step6. Configure a Gateway Listener 

더보기
# 02-gateway.yaml
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
  name: http
spec:
  gatewayClassName: gloo-gateway
  listeners:
  - protocol: HTTP
    port: 8080
    name: http
    allowedRoutes:
      namespaces:
        from: All

# gateway 리소스 생성
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/02-gateway.yaml

# 확인 : Now we can confirm that the Gateway has been activated
kubectl get gateway -n gloo-system
kubectl get gateway -n gloo-system -o yaml | k neat
apiVersion: v1
items:
- apiVersion: gateway.networking.k8s.io/v1
  kind: Gateway
  metadata:
    name: http
    namespace: gloo-system
  spec:
    gatewayClassName: gloo-gateway
    listeners:
    - allowedRoutes:
        namespaces:
          from: All
      name: http
      port: 8080
      protocol: HTTP
...

# You can also confirm that Gloo Gateway has spun up an Envoy proxy instance in response to the creation of this Gateway object by deploying gloo-proxy-http:
kubectl get deployment gloo-proxy-http -n gloo-system
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
gloo-proxy-http   1/1     1            1           5m22s

# envoy 사용 확인
kubectl get pod -n gloo-system
kubectl describe pod -n gloo-system  |grep Image:
    Image:         quay.io/solo-io/gloo-envoy-wrapper:1.17.7
    Image:          quay.io/solo-io/gloo:1.17.7
    Image:         quay.io/solo-io/gloo-envoy-wrapper:1.17.7


# gloo-proxy-http 서비스는 External-IP는 Pending 상태
kubectl get svc -n gloo-system gloo-proxy-http
NAME              TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
gloo-proxy-http   LoadBalancer   10.96.71.22   <pending>     8080:31555/TCP   2m4s

# gloo-proxy-http NodePort 30001 설정
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/instance: http
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: gloo-proxy-http
    app.kubernetes.io/version: 1.17.7
    gateway.networking.k8s.io/gateway-name: http
    gloo: kube-gateway
    helm.sh/chart: gloo-gateway-1.17.7
  name: gloo-proxy-http
  namespace: gloo-system
spec:
  ports:
  - name: http
    nodePort: 30001
    port: 8080
  selector:
    app.kubernetes.io/instance: http
    app.kubernetes.io/name: gloo-proxy-http
    gateway.networking.k8s.io/gateway-name: http
  type: LoadBalancer
EOF

kubectl get svc -n gloo-system gloo-proxy-http

 

[ 실행 결과 - 한 눈에 보기 ]

Step7. Establish External Access to Proxy

더보기
# Port Forward
# We will use a simple port-forward to expose the proxy’s HTTP port for us to use. 
# (Note that gloo-proxy-http is Gloo’s deployment of the Envoy data plane.)
kubectl port-forward deployment/gloo-proxy-http -n gloo-system 8080:8080 &

 

Step8. Configure Simple Routing with an HTTPRoute

더보기

[ 참고 - HTTPRoute 소개 ]

https://gateway-api.sigs.k8s.io/api-types/httproute/

 

Let’s begin our routing configuration with the simplest possible route to expose the /get operation on httpbin

 

HTTPRoute is one of the new Kubernetes CRDs introduced by the Gateway API, as documented here. We’ll start by introducing a simple HTTPRoute for our service.

 

HTTPRoute Spec

ParentRefs-Define which Gateways this Route wants to be attached to.

Hostnames (optional)- Define a list of hostnames to use for matching the Host header of HTTP requests.

Rules-Define a list of rules to perform actions against matching HTTP requests.

     - Each rule consists of matchesfilters (optional), backendRefs (optional) and timeouts (optional) fields.


apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
  labels:
    example: httpbin-route
spec:
  parentRefs:
    - name: http
      namespace: gloo-system
  hostnames:
    - "api.example.com"
  rules:
  - matches:
    - path:
        type: Exact
        value: /get
    backendRefs:
      - name: httpbin
        port: 8000

 

[ 실행 결과 - 한 눈에 보기 ]

 

☞ This example attaches to the default Gateway object created for us when we installed Gloo Gateway earlier.

      See the gloo-system/http reference in the parentRefs stanza.  

      The Gateway object simply represents a host:port listener that the proxy will expose to accept ingress traffic.

# Our route watches for HTTP requests directed at the host api.example.com with the request path /get and then forwards the request to the httpbin service on port 8000.
# Let’s establish this route now:
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/03-httpbin-route.yaml

#
kubectl get httproute -n httpbin
NAME      HOSTNAMES             AGE
httpbin   ["api.example.com"]   3m15s

kubectl describe httproute -n httpbin
...
Spec:
  Hostnames:
    api.example.com
  Parent Refs:
    Group:      gateway.networking.k8s.io
    Kind:       Gateway
    Name:       http
    Namespace:  gloo-system
  Rules:
    Backend Refs:
      Group:   
      Kind:    Service
      Name:    httpbin
      Port:    8000
      Weight:  1
    Matches:
      Path:
        Type:   Exact
        Value:  /get
...

 

 [ 실행 결과 - 한 눈에 보기 ]

 

Step9. Test the Simple Route with Curl

더보기
# let’s use curl to display the response with the -i option to additionally show the HTTP response code and headers.
echo "127.0.0.1 api.example.com" | sudo tee -a /etc/hosts
echo "httproute - http://api.example.com:30001/get" # 웹브라우저
혹은
curl -is -H "Host: api.example.com" http://localhost:8080/get # kubectl port-forward 사용 시
HTTP/1.1 200 OK
server: envoy
date: Sun, 06 Oct 2024 07:55:34 GMT
content-type: application/json
content-length: 239
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 25

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "api.example.com", 
    "User-Agent": "curl/8.7.1", 
    "X-Envoy-Expected-Rq-Timeout-Ms": "15000"
  }, 
  "origin": "10.244.0.11", 
  "url": "http://api.example.com/get"
}

 

Note that if we attempt to invoke another valid endpoint /delay on the httpbin service, it will fail with a 404 Not Found error. Why? Because our HTTPRoute policy is only exposing access to /get, one of the many endpoints available on the service. If we try to consume an alternative httpbin endpoint like /delay:

 

# 호출 응답 왜 그럴까?
curl -is -H "Host: api.example.com" http://localhost:8080/delay/1
HTTP/1.1 404 Not Found
date: Wed, 03 Jul 2024 07:19:21 GMT
server: envoy
content-length: 0

#
echo "httproute - http://api.example.com:30001/delay/1" # 웹브라우저


# nodeport 직접 접속
echo "httproute - http://api.example.com:30000/delay/1" # 1초 후 응답
echo "httproute - http://api.example.com:30000/delay/5" # 5초 후 응답

[ 실행결과 - 한 눈에 보기 ]

Step10. [정규식 패턴 매칭] Explore Routing with Regex Matching Patterns

더보기

☞ Let’s assume that now we DO want to expose other httpbin endpoints like /delay. Our initial HTTPRoute is inadequate, because it is looking for an exact path match with /get.

We’ll modify it in a couple of ways. First, we’ll modify the matcher to look for path prefix matches instead of an exact match. Second, we’ll add a new request filter to rewrite the matched /api/httpbin/ prefix with just a / prefix, which will give us the flexibility to access any endpoint available on the httpbin service. So a path like /api/httpbin/delay/1 will be sent to httpbin with the path /delay/1.

 

예시) /api/httpbin/delay/1/delay/1

# Here are the modifications we’ll apply to our HTTPRoute:

    - matches:
        # Switch from an Exact Matcher(정확한 매팅) to a PathPrefix (경로 매팅) Matcher
        - path:
            type: PathPrefix
            value: /api/httpbin/
      filters:
        # Replace(변경) the /api/httpbin matched prefix with /
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /

 

  • 2가지 수정 내용 적용 후 확인
#
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/04-httpbin-rewrite.yaml

# 확인
kubectl describe httproute -n httpbin
...
Spec:
  Hostnames:
    api.example.com
  Parent Refs:
    Group:      gateway.networking.k8s.io
    Kind:       Gateway
    Name:       http
    Namespace:  gloo-system
  Rules:
    Backend Refs:
      Group:   
      Kind:    Service
      Name:    httpbin
      Port:    8000
      Weight:  1
    Filters:
      Type:  URLRewrite
      URL Rewrite:
        Path:
          Replace Prefix Match:  /
          Type:                  ReplacePrefixMatch
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /api/httpbin/

Step11. Test Routing with Regex Matching Patterns

더보기

When we used only a single route with an exact match pattern, we could only exercise the httpbin /get endpoint. Let’s now use curl to confirm that both /get and /delay work as expected.

 

#
echo "httproute - http://api.example.com:30001/api/httpbin/get" # 웹브라우저
혹은
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/get # kubectl port-forward 사용 시
HTTP/1.1 200 OK
server: envoy
date: Sun, 06 Oct 2024 08:08:09 GMT
content-type: application/json
content-length: 289
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 18  # envoy 가 업스트림 httpbin 요청 처리에 걸리 시간 0.018초

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "api.example.com", 
    "User-Agent": "curl/8.7.1", 
    "X-Envoy-Expected-Rq-Timeout-Ms": "15000", 
    "X-Envoy-Original-Path": "/api/httpbin/get"
  }, 
  "origin": "10.244.0.11", 
  "url": "http://api.example.com/get"
}

# 아래 NodePort 와 GW API 통한 접속 비교
echo "httproute - http://api.example.com:30001/api/httpbin/get"
echo "httproute - http://api.example.com:30000/api/httpbin/get" # NodePort 직접 접근

---
#
echo "httproute - http://api.example.com:30001/api/httpbin/delay/1" # 웹브라우저
혹은
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/delay/1 # kubectl port-forward 사용 시
HTTP/1.1 200 OK
server: envoy
date: Wed, 03 Jul 2024 07:31:47 GMT
content-type: application/json
content-length: 342
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 1023   # envoy 가 업스트림 httpbin 요청 처리에 걸리 시간 1초 이상

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "api.example.com", 
    "User-Agent": "curl/8.6.0", 
    "X-Envoy-Expected-Rq-Timeout-Ms": "15000", 
    "X-Envoy-Original-Path": "/api/httpbin/delay/1"
  }, 
  "origin": "10.244.0.7", 
  "url": "http://api.example.com/delay/1"
}

curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/delay/2

 

☞ Perfect! It works just as expected! Note that the /delay operation completed successfully and that the 1-second delay was applied. The response header x-envoy-upstream-service-time: 1023 indicates that Envoy reported that the upstream httpbin service required just over 1 second (1,023 milliseconds) to process the request. In the initial /get operation, which doesn’t inject an artificial delay, observe that the same header reported only 14 milliseconds of upstream processing time.

 

[ 실행 결과 - 한 눈에 보기 ]

 

Step12. [업스트림 베어러 토큰을 사용한 변환] Test Transformations with Upstream Bearer Tokens

 - 목적 : 요청을 라우팅하는 백엔드 시스템 중 하나에서 인증해야 하는 요구 사항이 있는 경우는 어떻게 할까요? 이 업스트림 시스템에는 권한 부여를 위한 API 키가 필요하고, 이를 소비하는 클라이언트에 직접 노출하고 싶지 않다고 가정해 보겠습니다. 즉, 프록시 계층에서 요청에 주입할 간단한 베어러 토큰을 구성하고 싶습니다. (정적 API 키 토큰을 직접 주입)

 - What if we have a requirement to authenticate with one of the backend systems to which we route our requests?

 - Let’s assume that this upstream system requires an API key for authorization, and that we don’t want to expose this directly to the consuming client. In other words, we’d like to configure a simple bearer token to be injected into the request at the proxy layer.

 - We can express this in the Gateway API by adding a filter that applies a simple transformation to the incoming request.

 - This will be applied along with the URLRewrite filter we created in the previous step.

더보기
# The new filters stanza in our HTTPRoute now looks like this:

      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
              
        # Add a Bearer token to supply a static API key when routing to backend system
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: Authorization
                value: Bearer my-api-key

#
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/05-httpbin-rewrite-xform.yaml

#
kubectl describe httproute -n httpbin
...
Spec:
  ...
  Rules:
    Backend Refs:
      Group:   
      Kind:    Service
      Name:    httpbin
      Port:    8000
      Weight:  1
    Filters:
      Type:  URLRewrite
      URL Rewrite:
        Path:
          Replace Prefix Match:  /
          Type:                  ReplacePrefixMatch
      Request Header Modifier:
        Add:
          Name:   Authorization
          Value:  Bearer my-api-key
      Type:       RequestHeaderModifier
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /api/httpbin/

 

Step13. 동작 테스트

더보기
#
echo "httproute - http://api.example.com:30001/api/httpbin/get" # 웹브라우저
혹은
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/get # kubectl port-forward 사용 시
HTTP/1.1 200 OK
server: envoy
date: Sun, 06 Oct 2024 08:20:00 GMT
content-type: application/json
content-length: 332
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 11

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Authorization": "Bearer my-api-key", 
    "Host": "api.example.com", 
    "User-Agent": "curl/8.7.1", 
    "X-Envoy-Expected-Rq-Timeout-Ms": "15000", 
    "X-Envoy-Original-Path": "/api/httpbin/get"
  }, 
  "origin": "10.244.0.11", 
  "url": "http://api.example.com/get"
}

 

[ 실행 결과 - 한 눈에 보기 ]

Step14. Migrate 

In this section, we’ll explore how a couple of common service migration techniques, dark launches with header-based routing and canary releases with percentage-based routing, are supported by the Gateway API standard.

 

Configure Two Workloads for Migration Routing

Let’s first establish two versions of a workload to facilitate our migration example. We’ll use the open-source Fake Service to enable this.

 

> Fake service that can handle both HTTP and gRPC traffic, for testing upstream service communications and testing service mesh and other scenarios.

 

☞ Let’s establish a v1 of our my-workload service that’s configured to return a response string containing “v1”. We’ll create a corresponding my-workload-v2 service as well.

더보기
# You should see the response below, indicating deployments for both v1 and v2 of my-workload have been created in the my-workload namespace.
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/06-workload-svcs.yaml

# v1,v2 2가지 버전 워크로드 확인
kubectl get deploy,pod,svc,endpointslices -n my-workload
NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-workload-v1   1/1     1            1           77s
deployment.apps/my-workload-v2   1/1     1            1           77s

NAME                                  READY   STATUS    RESTARTS   AGE
pod/my-workload-v1-7577fdcc9d-4cv5r   1/1     Running   0          77s
pod/my-workload-v2-68f84654dd-8725x   1/1     Running   0          77s

NAME                     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/my-workload-v1   ClusterIP   10.96.35.183   <none>        8080/TCP   77s
service/my-workload-v2   ClusterIP   10.96.56.232   <none>        8080/TCP   77s

NAME                                                  ADDRESSTYPE   PORTS   ENDPOINTS    AGE
endpointslice.discovery.k8s.io/my-workload-v1-bpzgg   IPv4          8080    10.244.0.9   77s
endpointslice.discovery.k8s.io/my-workload-v2-ltp7d   IPv4          8080    10.244.0.8   77s

Step15. Test Simple V1 Routing

Before we dive into routing to multiple services, we’ll start by building a simple HTTPRoute that sends HTTP requests to host api.example.com whose paths begin with /api/my-workload to the v1 workload:

 

 

더보기
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: my-workload
  namespace: my-workload
  labels:
    example: my-workload-route
spec:
  parentRefs:
    - name: http
      namespace: gloo-system
  hostnames:
    - "api.example.com"
  rules:
    - matches:
      - path:
          type: PathPrefix
          value: /api/my-workload
      backendRefs:
        - name: my-workload-v1
          namespace: my-workload
          port: 8080

 

Now apply this route:

더보기
#
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/07-workload-route.yaml

#
kubectl get httproute -A
NAMESPACE     NAME          HOSTNAMES             AGE
httpbin       httpbin       ["api.example.com"]   41m
my-workload   my-workload   ["api.example.com"]   39s

#
kubectl describe httproute -n my-workload
...
Spec:
  Hostnames:
    api.example.com
  Parent Refs:
    Group:      gateway.networking.k8s.io
    Kind:       Gateway
    Name:       http
    Namespace:  gloo-system
  Rules:
    Backend Refs:
      Group:      
      Kind:       Service
      Name:       my-workload-v1
      Namespace:  my-workload
      Port:       8080
      Weight:     1
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /api/my-workload
#
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
HTTP/1.1 200 OK
vary: Origin
date: Sun, 06 Oct 2024 08:26:25 GMT
content-length: 294
content-type: text/plain; charset=utf-8
x-envoy-upstream-service-time: 33
server: envoy

{
  "name": "my-workload-v1",
  "uri": "/api/my-workload",
  "type": "HTTP",
  "ip_addresses": [
    "10.244.0.13"
  ],
  "start_time": "2024-10-06T08:26:25.859900",
  "end_time": "2024-10-06T08:26:25.871258",
  "duration": "11.359ms",
  "body": "Hello From My Workload (v1)!",
  "code": 200
}

 

Step16. Simulate a v2 Dark Launch with Header-Based Routing

 

Dark Launch is a great cloud migration technique that releases new features to a select subset of users to gather feedback and experiment with improvements before potentially disrupting a larger user community.

  - Dark Launch : 일부 사용자에게 새로운 기능을 출시하여 피드백을 수집하고 잠재적으로 더 큰 사용자 커뮤니티를 방해하기 전에 개선 사항을 실험하는 훌륭한 클라우드 마이그레이션 기술

 

☞  We will simulate a dark launch in our example by installing the new cloud version of our service in our Kubernetes cluster, and then using declarative policy to route only requests containing a particular header to the new v2 instance. The vast majority of users will continue to use the original v1 of the service just as before.

 - 우리는 Kubernetes 클러스터에 서비스의 새로운 클라우드 버전을 설치한 다음 선언적 정책을 사용하여 특정 헤더를 포함하는 요청만 새 인스턴스로 라우팅하여 예제에서 다크 런치를 시뮬레이션할 것입니다 . 대다수의 사용자는 이전과 마찬가지로 서비스의 v1을 계속 사용할 것 입니다.  

 

더보기
  rules:
    - matches:
      - path:
          type: PathPrefix
          value: /api/my-workload
        # Add a matcher to route requests with a v2 version header to v2
        # version=v2 헤더값이 있는 사용자만 v2 라우팅
        headers:
        - name: version
          value: v2
      backendRefs:
        - name: my-workload-v2
          namespace: my-workload
          port: 8080      
    - matches:
      # Route requests without the version header to v1 as before
      # 대다수 일반 사용자는 기존 처럼 v1 라우팅
      - path:
          type: PathPrefix
          value: /api/my-workload
      backendRefs:
        - name: my-workload-v1
          namespace: my-workload
          port: 8080

☞ Configure two separate routes, one for v1 that the majority of service consumers will still use, and another route for v2 that will be accessed by specifying a request header with name version and value v2. Let’s apply the modified HTTPRoute:

 

더보기
#
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/08-workload-route-header.yaml


# 
kubectl describe httproute -n my-workload
...
Spec:
  ...
  Rules:
    Backend Refs:
      Group:      
      Kind:       Service
      Name:       my-workload-v2
      Namespace:  my-workload
      Port:       8080
      Weight:     1
    Matches:
      Headers:
        Name:   version
        Type:   Exact
        Value:  v2
      Path:
        Type:   PathPrefix
        Value:  /api/my-workload
    Backend Refs:
      Group:      
      Kind:       Service
      Name:       my-workload-v1
      Namespace:  my-workload
      Port:       8080
      Weight:     1
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /api/my-workload

# Now we’ll test the original route, with no special headers supplied, and confirm that traffic still goes to v1:
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload | grep body
"body": "Hello From My Workload (v1)!",

# But it we supply the version: v2 header, note that our gateway routes the request to v2 as expected:
curl -is -H "Host: api.example.com" -H "version: v2" http://localhost:8080/api/my-workload
curl -is -H "Host: api.example.com" -H "version: v2" http://localhost:8080/api/my-workload | grep body

 

[ 실행 결과 - 한 눈에 보기 ]

 

Step17. Expand V2 Testing with Percentage-Based Routing

 

☞ After a successful dark-launch, we may want a period where we use a blue-green strategy of gradually shifting user traffic from the old version to the new one. Let’s explore this with a routing policy that splits our traffic evenly, sending half our traffic to v1 and the other half to v2.

 - 성공적인 다크 런칭 이후, 우리는 점진적으로 이전 버전에서 새 버전으로 사용자 트래픽을 옮기는 블루-그린 전략을 사용하는 기간을 원할 수 있습니다. 트래픽을 균등하게 분할하고 트래픽의 절반을 로 보내고 v1나머지 절반을 로 보내는 라우팅 정책으로 이를 살펴보겠습니다 v2.

 

☞ We will modify our HTTPRoute to accomplish this by removing the header-based routing rule that drove our dark launch. Then we will replace that with a 50-50 weight applied to each of the routes, as shown below:

 

더보기
  rules:
    - matches:
      - path:
          type: PathPrefix
          value: /api/my-workload
      # Configure a 50-50 traffic split across v1 and v2 : 버전 1,2 50:50 비율
      backendRefs:
        - name: my-workload-v1
          namespace: my-workload
          port: 8080
          weight: 50
        - name: my-workload-v2
          namespace: my-workload
          port: 8080
          weight: 50

# Apply this 50-50 routing policy with kubectl:
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml

#
kubectl describe httproute -n my-workload
...

# 반복 접속 후 대략 비률 확인
for i in {1..100}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body; done | sort | uniq -c | sort -nr
for i in {1..200}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body; done | sort | uniq -c | sort -nr

 

[ Debug ]

Step18. Solve a Problem with Glooctl CLI 

 

A common source of Gloo configuration errors is mistyping an upstream reference, perhaps when copy/pasting it from another source but “missing a spot” when changing the name of the backend service target. In this example, we’ll simulate making an error like that, and then demonstrating how glooctl can be used to detect it.

 

 > Gloo 구성 오류의 일반적인 원인은 업스트림 참조를 잘못 입력하는 것입니다. 아마도 다른 소스에서 복사/붙여넣을 때이지만

    백엔드 서비스 대상의 이름을 변경할 때 "한 군데를 놓친" 것입니다. 이 예에서 우리는 그런 오류를 만드는 것을 시뮬레이션하고,

    glooctl그것을 감지하는 데 어떻게 사용할 수 있는지 보여줍니다.

 

First, let’s apply a change to simulate the mistyping of an upstream config so that it is targeting a non-existent my-bad-workload-v2 backend service, rather than the correct my-workload-v2.

 

 > my-bad-workload-v2 업스트림 구성의 오타를 시뮬레이션하여 올바른 타겟팅하는 대신 존재하지 않는 백엔드 서비스를 타겟팅하도록 변경

더보기
# [신규 터미널] 모니터링
kubectl get httproute -n my-workload my-workload -o yaml -w

#
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/10-workload-route-split-bad-dest.yaml

#
kubectl describe httproute -n my-workload
...
Spec:
  Hostnames:
    api.example.com
  Parent Refs:
    Group:      gateway.networking.k8s.io
    Kind:       Gateway
    Name:       http
    Namespace:  gloo-system
  Rules:
    Backend Refs:
      Group:      
      Kind:       Service
      Name:       my-workload-v1
      Namespace:  my-workload
      Port:       8080
      Weight:     50
      Group:      
      Kind:       Service
      Name:       my-bad-workload-v2
      Namespace:  my-workload
      Port:       8080
      Weight:     50
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /api/my-workload
Status:
  Parents:
    Conditions:
      Last Transition Time:  2024-10-06T08:38:25Z
      Message:               Service "my-bad-workload-v2" not found
      Observed Generation:   4
      Reason:                BackendNotFound
      Status:                False
      Type:                  ResolvedRefs
      Last Transition Time:  2024-10-06T08:25:47Z
      Message:               
      Observed Generation:   4
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
    Controller Name:         solo.io/gloo-gateway
    Parent Ref:
      Group:      gateway.networking.k8s.io
      Kind:       Gateway
      Name:       http
      Namespace:  gloo-system

When we test this out, note that the 50-50 traffic split is still in place. This means that about half of the requests will be routed to my-workload-v1 and succeed, while the others will attempt to use the non-existent my-bad-workload-v2 and fail like this:

더보기
#
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
HTTP/1.1 500 Internal Server Error
date: Wed, 03 Jul 2024 08:21:11 GMT
server: envoy
content-length: 0

# 
for i in {1..100}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body; done | sort | uniq -c | sort -nr

 

[ 실행 결과 - 한 눈에 보기 ]

So we’ll deploy one of the first weapons from the Gloo debugging arsenal, the glooctl check utility. It verifies a number of Gloo resources, confirming that they are configured correctly and are interconnected with other resources correctly. For example, in this case, glooctl will detect the error in the mis-connection between the HTTPRoute and its backend target:

 

더보기
#
docker exec -it myk8s-control-plane bash
-----------------------------------
export PATH=$HOME/.gloo/bin:$PATH
glooctl check
Checking Gateways... OK
Checking Proxies... 1 Errors!

Detected Kubernetes Gateway integration!
Checking Kubernetes GatewayClasses... OK
Checking Kubernetes Gateways... OK
Checking Kubernetes HTTPRoutes... 1 Errors!

Skipping Gloo Instance check -- Gloo Federation not detected.
Error: 2 errors occurred:
	* Found proxy with warnings by 'gloo-system': gloo-system gloo-system-http
Reason: warning: 
  Route Warning: InvalidDestinationWarning. Reason: invalid destination in weighted destination list: *v1.Upstream { blackhole_ns.kube-svc:blackhole-ns-blackhole-cluster-8080 } not found

	* HTTPRoute my-workload.my-workload.http status (ResolvedRefs) is not set to expected (True). Reason: BackendNotFound, Message: Service "my-bad-workload-v2" not found


# 원인 관련 정보 확인
kubectl get httproute my-workload -n my-workload -o yaml
...
status:
  parents:
  - conditions:
    - lastTransitionTime: "2023-11-28T21:09:20Z"
      message: ""
      observedGeneration: 6
      reason: BackendNotFound
      status: "False"
      type: ResolvedRefs
...

# 정상 설정으로 해결 configuration is again clean.
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml
kubectl get httproute my-workload -n my-workload -o yaml

#
glooctl check
...

 

[ 실행 결과 - 한 눈에 보기 ]

[ Observe ]

Step19. Explore Envoy Metrics

 

Envoy publishes a host of metrics that may be useful for observing system behavior. In our very modest kind cluster for this exercise, you can count over 3,000 individual metrics! You can learn more about them in the Envoy documentation here.

For this 30-minute exercise, let’s take a quick look at a couple of the useful metrics that Envoy produces for every one of our backend targets.

 

First, we’ll port-forward the Envoy administrative port 19000 to our local workstation:

더보기
#
kubectl -n gloo-system port-forward deployment/gloo-proxy-http 19000 &

# 아래 관리 페이지에서 각각 메뉴 링크 클릭 확인
echo "Envoy Proxy Admin - http://localhost:19000"
echo "Envoy Proxy Admin - http://localhost:19000/stats/prometheus"

 

 

For this exercise, let’s view two of the relevant metrics from the first part of this exercise: one that counts the number of successful (HTTP 2xx) requests processed by our httpbin backend (or cluster, in Envoy terminology), and another that counts the number of requests returning server errors (HTTP 5xx) from that same backend:

 

더보기
#
curl -s http://localhost:19000/stats | grep -E "(^cluster.kube-svc_httpbin-httpbin-8000_httpbin.upstream.*(2xx|5xx))"
cluster.kube-svc_httpbin-httpbin-8000_httpbin.upstream_rq_2xx: 32
cluster.kube-svc_httpbin-httpbin-8000_httpbin.upstream_rq_5xx: 7

# If we apply a curl request that forces a 500 failure from the httpbin backend, using the /status/500 endpoint, I’d expect the number of 2xx requests to remain the same, and the number of 5xx requests to increment by one:
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/status/500
HTTP/1.1 500 Internal Server Error
server: envoy
date: Wed, 03 Jul 2024 08:30:06 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 28

#
curl -s http://localhost:19000/stats | grep -E "(^cluster.httpbin-httpbin-8000_httpbin.upstream.*(2xx|5xx))"
cluster.kube-svc_httpbin-httpbin-8000_httpbin.upstream_rq_2xx: 32
cluster.kube-svc_httpbin-httpbin-8000_httpbin.upstream_rq_5xx: 15

 

You can learn more about creating your own Grafana dashboards from Gloo metrics in this blog post.

 

 

[ Clean-up ]

Step20. 마무리 자원정리

더보기
kind delete cluster --name myk8s
[2]  + 37292 exit 1     kubecolor -n gloo-system port-forward deployment/gloo-proxy-http 19000
[1]  + 27738 exit 1     kubecolor port-forward deployment/gloo-proxy-http -n gloo-system 8080:8080
Deleted nodes: ["myk8s-control-plane"]

 

▶ kind 삭제, /etc/hosts 에 추가한 내용 삭제

(참고) Kubectl Port-forward Flow Explained - Blog


[ 마무리 ]

- 이번 Class 에서는 Ingress와 Api gateway - 특별히 gloo 솔루션을 설치하여 테스트 따라하기를 통해 Ingress 의 활용 방법에 대해 자세히 알 수 있어서  좋았던 것 같습니다. 특정 벤더솔루션 보다 기본적인 Ingress 통신 룰과 실제 운영 상황에 대처할 수 있는 부분에 대해서는 시간을 가지고 익숙해 지는 과정이 필요할 것 같습니다.


[ 도움이 되는 링크 모음 ]

더보기

[ K3S 관련 ]

▷ K3S를 여러개 생성해 주는 K3S :  https://github.com/rancher/k3k

▷ gloo G/W API : https://www.solo.io/blog/gateway-api-gitops-argo-tutorial-blog/

▷ Argo-cd Event : https://medium.chuklee.com/argo-events-conditional-triggers-49fb20d15dbc

 

[ Nginx Ingress 관련 ]

(참고) nginx ingress controller 기본 설정 : forwarded-for-header - 링크

▷ User Guide : https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/

다양한 기능들 Examples : https://kubernetes.github.io/ingress-nginx/examples/

https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/

 

[ Traefik Ingress 자료 ]

https://www.youtube.com/watch?v=MPikDigl9rU

 

[ Gateway API 관련 ]

▷ 조대협님 Blog : https://bcho.tistory.com/1005

https://youtu.be/GiFQNevrxYA?t=172

 

Kubernetes Traffic Management: Combining Gateway API with Service Mesh for North-South and East-West Use Cases - Blog

▷(참조) Ingress + API Gateway = Kubernete Gateway API  https://www.anyflow.net/sw-engineer/kubernetes-gateway-api-1

▷(참조) API Gateway + Service Mesh = Kubernete Gateway API  https://www.anyflow.net/sw-engineer/kubernetes-gateway-api-2

 

[ Gloo Gateway 관련 ]

https://www.solo.io/blog/gateway-api-tutorial-blog/*

https://www.solo.io/blog/gateway-api-workshop/

https://www.solo.io/blog/fast-and-furious-gateway-api-at-scale-with-envoy-proxy-and-gloo-gateway/

https://www.solo.io/blog/getting-the-most-out-of-gateway-api-lessons-learned-from-gloo-migrations/

  https://www.solo.io/blog/solving-an-information-leakage-problem-with-the-envoy-extproc-filter-and-kube-gateway-api/
https://www.solo.io/blog/gateway-api-gitops-argo-tutorial-blog/*

 

[ kubectl 부가기능 설치 관련 ]
▷ krew 설치 : Blog

▷ neat 설치 : Blog