코알못

[kubernetes] Ingress 소개 및 실습 본문

ETC

[kubernetes] Ingress 소개 및 실습

코린이s 2023. 5. 28. 22:18
728x90

LoadBalance를 사용하면 Service External IP를 기억해야하며 하나면 괜찮지만 여러개일때 모두 기억하기 어렵다.

이때 사용하는것이 Ingress로 트래픽을 서비스로 분산하기 위한 라우팅 규칙 모음이다!

host 헤더나 path를 통해 서비스를 구분하고 트래픽 포워딩 할 수 있다.

Ingress는 규칙 집합으로 IngressController 객체의 도움이 필요하며 이는 쿠버네티스가 지원하는 Ingress Controller를 가지고 직접 구성하거나 여러 라이브러리를 사용하여 구성 해야 한다.

어렵기 때문에 클라우드에서 제공해주는 경우가 있으며 우리는 구글 클라우드를 사용하므로 Ingress 사용시 Controller를 자동 구성해주므로 따로 구현할필요가 없다.

Ingress 정의하는 방법은 아래와 같이 apiVersion, kind 를 입력하고

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
	name:snackbar
    ...

spec에 포워딩할 부분을 Host + Path 로 정의 한다.

예를 들어 order.fast-snackbar.com/order 의 경우에는 order 서비스의 80포트로 전달 된다.

spec:
 rules:
 - host: order.fast-snackbar.com # HOST가 일치
   http:
     paths:
     - pathType: Prefix # Path가 /order로 시작
       path: /order
       backend:
         service:
           name: order  # order 서비스의 80 포트로 포워딩
           port:
            number: 80
     - pathType: Prefix # Path가 /order로 시작
       path: /payment
       backend:
         service:
           name: payment  # order 서비스의 80 포트로 포워딩
           port:
            number: 80
  defaultBackend:  # 위에 정의 되지 않은 호스트+Path 조합의 경우 아래 서비스로 포워딩(fallback)
    service:
      name: order
      port:
        number: 80

만약 host가 아닌 path 로만 라우팅하고 싶다면 아래와 같이 host를 빼고 정의 하면 된다.

spec:
 rules:
 - http:
     paths:
     - pathType: Prefix # Path가 /order로 시작
       path: /order
       backend:
         service:
           name: order  # order 서비스의 80 포트로 포워딩
           port:
            number: 80
     - pathType: Prefix # Path가 /order로 시작
       path: /payment
       backend:
         service:
           name: payment  # order 서비스의 80 포트로 포워딩
           port:
            number: 80
  defaultBackend:  # 위에 정의 되지 않은 호스트+Path 조합의 경우 아래 서비스로 포워딩(fallback)
    service:
      name: order
      port:
        number: 80

이제 실습을 통해 상세하게 알아보자!

실습전 주의 사항은 여기서 연동 되는 서비스의 경우에는 반드시 NodePort 여야만 수신 가능하다는점을 기억하도록 하자!

Host 헤더 IP:PORT URL Path 결과 서비스 매핑
order.snackbar.com IngressAddress:80 /menus 메뉴 조회 order
/order 주문 신청
payment.snackbar.com /receipt 영수증 조회 payment
delivery.snackbar.com / 시작페이지 조회 delivery

위에 정의된 부분 이외에는 home 서비스로 가도록 한다.

우선 order, payment, delivery 에 해당하는 Deployment, Service 를 생성하는 yaml 파일을 만든다.

# service-deployment-for-ingress.yaml

apiVersion: v1
kind: Service
metadata:
  name: home
  namespace: snackbar
  labels:
    service: home
    project: snackbar
spec:
  type: NodePort
  selector:
    service: home
    project: snackbar
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: order
  namespace: snackbar
  labels:
    service: order
    project: snackbar
spec:
  type: NodePort
  selector:
    service: order
    project: snackbar
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: payment
  namespace: snackbar
  labels:
    service: payment
    project: snackbar
spec:
  type: NodePort
  selector:
    service: payment
    project: snackbar
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: delivery
  namespace: snackbar
  labels:
    service: delivery
    project: snackbar
spec:
  type: NodePort
  selector:
    service: delivery
    project: snackbar
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: home
  namespace: snackbar
  labels:
    service: home
    project: snackbar
spec:
  replicas: 1
  selector:
    matchLabels:
      service: home
      project: snackbar
  template:
    metadata:
      namespace: snackbar
      labels:
        service: home
        project: snackbar
    spec:
      containers:
      - name: home
        image: yoonjeong/home:1.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: "64Mi"
            cpu: "50m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order
  namespace: snackbar
  labels:
    service: order
    project: snackbar
spec:
  replicas: 2
  selector:
    matchLabels:
      service: order
      project: snackbar
  template:
    metadata:
      namespace: snackbar
      labels:
        service: order
        project: snackbar
    spec:
      containers:
      - name: order
        image: yoonjeong/order:1.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: "64Mi"
            cpu: "50m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment
  namespace: snackbar
  labels:
    service: payment
    project: snackbar
spec:
  replicas: 2
  selector:
    matchLabels:
      service: payment
      project: snackbar
  template:
    metadata:
      namespace: snackbar
      labels:
        service: payment
        project: snackbar
    spec:
      containers:
      - name: payment
        image: yoonjeong/payment:1.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: "64Mi"
            cpu: "50m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: delivery
  namespace: snackbar
  labels:
    service: delivery
    project: snackbar
spec:
  replicas: 2
  selector:
    matchLabels:
      service: delivery
      project: snackbar
  template:
    metadata:
      namespace: snackbar
      labels:
        service: delivery
        project: snackbar
    spec:
      containers:
      - name: delivery
        image: yoonjeong/my-app:2.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: "64Mi"
            cpu: "50m"

클러스터에 적용한다.

$ kubectl apply -f service-deployment-for-ingress.yaml

모든 오브젝트를 검색해보면 정상적으로 생성 되었다.

$ kubectl get all -l project=snackbar -n snackbar -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP            NODE                                           NOMINATED NODE   READINESS GATES
pod/delivery-68fd5c7497-8bscb   1/1     Running   0          3m54s   10.100.2.56   gke-corin-cluster-default-pool-d2d86113-zy02   <none>           <none>
pod/delivery-68fd5c7497-ljhpd   1/1     Running   0          3m54s   10.100.3.72   gke-corin-cluster-default-pool-d2d86113-xq03   <none>           <none>
pod/home-5858cfc489-l4gqj       1/1     Running   0          3m57s   10.100.3.69   gke-corin-cluster-default-pool-d2d86113-xq03   <none>           <none>
pod/order-7c8cb88558-hxrm7      1/1     Running   0          3m56s   10.100.3.70   gke-corin-cluster-default-pool-d2d86113-xq03   <none>           <none>
pod/order-7c8cb88558-tvg9z      1/1     Running   0          3m56s   10.100.2.55   gke-corin-cluster-default-pool-d2d86113-zy02   <none>           <none>
pod/payment-79984d47b-d2clm     1/1     Running   0          3m55s   10.100.3.71   gke-corin-cluster-default-pool-d2d86113-xq03   <none>           <none>
pod/payment-79984d47b-pnvk4     1/1     Running   0          3m55s   10.100.0.50   gke-corin-cluster-default-pool-d2d86113-w610   <none>           <none>

NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE    SELECTOR
service/delivery   NodePort   10.104.3.44     <none>        80:30283/TCP   4m     project=snackbar,service=delivery
service/home       NodePort   10.104.15.114   <none>        80:32635/TCP   4m4s   project=snackbar,service=home
service/order      NodePort   10.104.13.251   <none>        80:31273/TCP   4m3s   project=snackbar,service=order
service/payment    NodePort   10.104.11.140   <none>        80:30783/TCP   4m2s   project=snackbar,service=payment

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES                  SELECTOR
deployment.apps/delivery   2/2     2            2           3m57s   delivery     yoonjeong/my-app:2.0    project=snackbar,service=delivery
deployment.apps/home       1/1     1            1           4m      home         yoonjeong/home:1.0      project=snackbar,service=home
deployment.apps/order      2/2     2            2           3m59s   order        yoonjeong/order:1.0     project=snackbar,service=order
deployment.apps/payment    2/2     2            2           3m58s   payment      yoonjeong/payment:1.0   project=snackbar,service=payment

NAME                                  DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                  SELECTOR
replicaset.apps/delivery-68fd5c7497   2         2         2       3m58s   delivery     yoonjeong/my-app:2.0    pod-template-hash=68fd5c7497,project=snackbar,service=delivery
replicaset.apps/home-5858cfc489       1         1         1       4m1s    home         yoonjeong/home:1.0      pod-template-hash=5858cfc489,project=snackbar,service=home
replicaset.apps/order-7c8cb88558      2         2         2       4m      order        yoonjeong/order:1.0     pod-template-hash=7c8cb88558,project=snackbar,service=order
replicaset.apps/payment-79984d47b     2         2         2       3m59s   payment      yoonjeong/payment:1.0   pod-template-hash=79984d47b,project=snackbar,service=payment

이제 요청사항에 맞도록 ingress 오브젝트를 만드는 yaml 파일을 작성하며 host+path 로 라우팅 하도록 하고 

정의된 요청 이외에는 home 서비스로 갈 수 있도록 defaultBackend에 정의한다.

# ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: snackbar
  namespace: snackbar
  labels:
    project: snackbar
spec:
  defaultBackend:
    service:
      name: home
      port:
        number: 80
  rules:
  - host: order.snackbar.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: order
            port:
              number: 80
  - host: payment.snackbar.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: payment
            port:
              number: 80
  - host: delivery.snackbar.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: delivery
            port:
              number: 80

클러스터에 적용한다.

$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/snackbar created

생성한 ingress를 보면 정상적으로 아래 host일 경우 해당 Ingress를 타도록 설정이 정상적으로 되어있다.

Address 할당까지 조금 시간이 걸리므로 기다리면서

$ kubectl get ingress snackbar -n snackbar
NAME       CLASS    HOSTS                                                           ADDRESS   PORTS   AGE
snackbar   <none>   order.snackbar.com,payment.snackbar.com,delivery.snackbar.com             80      84s

서비스에 대한 endpoint 를 확인하면 정상적으로 파드가 매핑 되었다.

$ kubectl get endpoints -n snackbar
NAME       ENDPOINTS                           AGE
delivery   10.100.2.56:8080,10.100.3.72:8080   15m
home       10.100.3.69:8080                    15m
order      10.100.2.55:8080,10.100.3.70:8080   15m
payment    10.100.0.50:8080,10.100.3.71:8080   15m

이제 Address 할당이 되었으며 IP 할당 완료시 해당 필드가 채워진다.

$ kubectl get ingress snackbar -n snackbar
NAME       CLASS    HOSTS                                                           ADDRESS         PORTS   AGE
snackbar   <none>   order.snackbar.com,payment.snackbar.com,delivery.snackbar.com   34.12.12.1   80      4m55s

이제 ingress IP 만 얻어오기 위해서 복사해서 쓸 수 있지만 코드 한줄로 얻어 오기 위해 우선 yaml 로 ingress ip를 얻어 오기 위한 구조를 파악한다.

$ kubectl get ingress snackbar -n snackbar -o yaml
...
..
status:
  loadBalancer:
    ingress:
    - ip: 34.12.12.1

그다음 -o 옵션으로 jsonpath를 주어 위에서 확인한 구조로 ip 에 접근하면 정상적으로 ip만 얻어올 수 있다.

$ kubectl get ingress snackbar -n snackbar -o jsonpath="{.status.loadBalancer.ingress[0].ip}"
34.12.12.1

이제 해당 IP를 가져와 환경변수로 설정하고 정상적으로 설정 되었는지 확인한다.

$ export INGRESS_IP=$(kubectl get ingress snackbar -n snackbar -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
$ echo $INGRESS_IP
34.12.12.1

이제 테스트를 해보자!

아래와 같이 /etc/hosts 파일에 호스트 설정을 하여 도메인으로 호출해도 되나

# /etc/hosts

34.12.12.1 order.snackbar.com

호스트 설정 없이 하는 방법은 아래와 같이 호스트 헤더를 직접 셋팅하여 보내는 방법이다!

테스트를 해보면 정상적으로 order 서비스로 호출 된것을 확인할 수 있다.

$ curl -H "Host: order.snackbar.com" --request GET $INGRESS_IP
Welcome to Snackbar!
Order what you want!

===== Host Info =====
HostIP: 10.100.2.55
HostName: order-7c8cb88558-tvg9z

메뉴를 확인하면 정상적으로 order 서비스와 통신하여 가져온다.

$ curl -H "Host: order.snackbar.com" --request GET $INGRESS_IP/menus
We have 4 snacks!
1. Pizza: 10,000
2. Burger: 5,000
3. Coke: 1,000
4. Juice: 1000

===== Host Info =====
HostIP: 10.100.2.55
HostName: order-7c8cb88558-tvg9z

주문 체크도 해보면 영수증을 정상적으로 얻어올 수 있으며

$ curl -H "Host: order.snackbar.com" --request POST $INGRESS_IP/checkout \
--header 'Content-Type: application/json' \
--data-raw '{
    "Pizza": 1,
    "Burger": 2,
    "Coke": 0,
    "Juice": 0
}'

payment 서버에서도 응답이 정상적으로 나온다.

$ curl -H "Host: payment.snackbar.com" --request POST $INGRESS_IP/receipt \
--header 'Content-Type: application/json' \
--data-raw '{
    "Pizza": 1,
    "Burger": 2,
    "Coke": 0,
    "Juice": 0
}'
{"orderedMenus":[{"name":"Pizza","count":1,"price":10000},{"name":"Burger","count":2,"price":10000},{"name":"Coke","count":0,"price":0},{"name":"Juice","count":0,"price":0}],"sumOfPrices":20000,"vat":2000,"totalPrice":22000,"host":{"ip":"10.100.0.50","name":"payment-79984d47b-pnvk4"}}

delivery 서비스도 정상적으로 호출 된다.

$ curl -H "Host: delivery.snackbar.com" --request GET $INGRESS_IP
Welcome to Version 2!

===== Host Info =====
HostIP: 10.100.3.72
HostName: delivery-68fd5c7497-ljhpd

정의되지 않은 호스트로 호출하면 정상적으로 home 서비스를 호출하는것을 확인 할 수 있다.

$ curl -H "Host: corin.snackbar.com" --request GET $INGRESS_IP
Welcome to Home!

===== Host Info =====
HostIP: 10.100.3.69
HostName: home-5858cfc489-l4gqj

기존 Ingress 만 제거하여 두번째 실습을 하도록 하자!

$ kubectl delete ingress snackbar -n snackbar
ingress.networking.k8s.io "snackbar" deleted

두번째 실습은 Host+Path 가 아닌 Path 로만 설정하여 라우팅 하도록 해보자!

정의되지 않은 path 로 호출할 경우 home 서비스로

/order 일 경우 order 서비스로

 /payment 일 경우 payment 서비스로 이동하도록 아래와 같이 ingress 파일을 작성하며

# ingress-v2.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: snackbar
  namespace: snackbar
  labels:
    project: snackbar
spec:
  defaultBackend:
    service:
      name: home
      port:
        number: 80
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: /order
        backend:
          service:
            name: order
            port:
              number: 80
      - pathType: Prefix
        path: /payment
        backend:
          service:
            name: payment
            port:
              number: 80

클러스터에 적용한다.

$ kubectl apply -f ingress-v2.yaml
ingress.networking.k8s.io/snackbar created

호스트 설정 하지 않았으므로 모든 호스트에 대해 해당 ingress가 받아주겠다는 의미이며 아직 IP 할당되지 않았다.

$ kubectl get ingress snackbar -n snackbar
NAME       CLASS    HOSTS   ADDRESS   PORTS   AGE
snackbar   <none>   *                 80      43s

기다리면 ADDRESS에 할당된 IP가 나온다.

$ kubectl get ingress snackbar -n snackbar
NAME       CLASS    HOSTS   ADDRESS         PORTS   AGE
snackbar   <none>   *       34.12.12.1   80      27m

환경 설정에 ingress ip 를 셋팅하고 정상적으로 나오는지 확인한다.

$ export INGRESS_IP=34.12.12.1
$ echo $INGRESS_IP
34.12.12.1

이제 order 서버에 호출 하기 위해서 path 는 /order 까지 적어주며 그 뒤에 path 인 /menus 로 호출하면 정상적으로 데이터가 나온다.

host 설정이 아닌 path 이므로 /order 까지 적어준뒤 내부 통신할 주소를 적어 줘야 하므로 기존 테스트와 url을 다르게 호출해야 한다.

$ curl --request GET $INGRESS_IP/order/menus
We have 4 snacks!
1. Pizza: 10,000
2. Burger: 5,000
3. Coke: 1,000
4. Juice: 1000

===== Host Info =====
HostIP: 10.100.2.55
HostName: order-7c8cb88558-tvg9z

checkout 도 마찬가지로 앞에 /order 를 붙여야 order 서비스로 호출하며

$ curl --request POST $INGRESS_IP/order/checkout \
--header 'Content-Type: application/json' \
--data-raw '{
    "Pizza": 1,
    "Burger": 2,
    "Coke": 0,
    "Juice": 0
}'

        Received from http://payment/receipt
        Server is running on
        - HostName: payment-79984d47b-pnvk4
        - HostIP: 10.100.0.50

        === Here is Your Receipt! ===

        [영수증]

        주문한 메뉴
        --------------------------
        Pizza - 10000원
        Burger - 10000원
        Coke - 0원
        Juice - 0원
        --------------------------

        주문금액 20000
        부가세(10%) 2000
        합계 22000

payment 서비스로 호출 하기 위해 /payment 를 붙여준다.

$ curl --request POST $INGRESS_IP/payment/receipt \
--header 'Content-Type: application/json' \
--data-raw '{
    "Pizza": 1,
    "Burger": 2,
    "Coke": 0,
    "Juice": 0
}'
{"orderedMenus":[{"name":"Pizza","count":1,"price":10000},{"name":"Burger","count":2,"price":10000},{"name":"Coke","count":0,"price":0},{"name":"Juice","count":0,"price":0}],"sumOfPrices":20000,"vat":2000,"totalPrice":22000,"host":{"ip":"10.100.3.71","name":"payment-79984d47b-d2clm"}}

마지막으로 정의되지 않은 path 로 호출해보면 정상적으로 home 서비스와 통신하는것을 확인 할 수 있다.

$ curl $INGRESS_IP
Welcome to Home!

===== Host Info =====
HostIP: 10.100.3.69
HostName: home-5858cfc489-l4gqj

이제 실습을 마무리하며 모두 제거한다.

$ kubectl delete all -l project=snackbar -n snackbar
pod "delivery-68fd5c7497-8bscb" deleted
pod "delivery-68fd5c7497-ljhpd" deleted
pod "home-5858cfc489-l4gqj" deleted
pod "order-7c8cb88558-hxrm7" deleted
pod "order-7c8cb88558-tvg9z" deleted
pod "payment-79984d47b-d2clm" deleted
pod "payment-79984d47b-pnvk4" deleted
service "delivery" deleted
service "home" deleted
service "order" deleted
service "payment" deleted
deployment.apps "delivery" deleted
deployment.apps "home" deleted
deployment.apps "order" deleted
deployment.apps "payment" deleted

끝!

728x90
Comments