이번글에서는 Spring Cloud 기반의 서비스를 Kubernetes와 함께 사용해보는 과정을 가져보겠습니다. 일반적으로는, Kubernetes를 단독으로 사용하여 Ingress를 API Gateway로 사용하고, Service를 Load Balancing 하는데요. 이와 같이 사용하지 않고, 이번 글에서는 Spring Cloud의 Code를 거의 바꾸지않고 K8s의 Orchestration을 사용하고자 하는 개발자분들께서 읽으시면 좋을 것 같습니다. 

 

프로젝트의 구성도는 아래와 같습니다.

  • Ingress-seminarhub로 "/" 로 들어오는 요청을 Spring Cloud Gateway로 Routing 합니다.
  • Spring Cloud Gateway는 "/seminar", "/member", "/member-seminar" 로 각 서비스에 Routing 합니다.

 

사실 이 글의 시작은 nhn에서 제공하는 테크영상을 보다가 흥미가 생겨 시작했는데요. 

MSA 환경을 k8s 로 전환하는 과정의 큰 Flow와 이유에 대해서 이해하기 정말 쉽습니다. 

https://www.youtube.com/watch?v=otss__0kf-g 

 

1. Ingress-nginx-controller를 구성

https://github.com/kubernetes/ingress-nginx/tree/main

 

GitHub - kubernetes/ingress-nginx: Ingress-NGINX Controller for Kubernetes

Ingress-NGINX Controller for Kubernetes. Contribute to kubernetes/ingress-nginx development by creating an account on GitHub.

github.com

우리는 먼저 ingress-nginx-controller를 사용합니다. 본인의 K8s 버전에 맞추어서 설치하셔야합니다. 위의 사이트에서 확인하실 수 있습니다. 저는 k8s 1.28 을 사용하고있습니다.

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/baremetal/deploy.yaml

위의 명령어로 nginx-controller 1.8.2.yaml 파일을 가져옵니다.

이떄 우리는 외부에서 ingress-nginx-controller로 들어올떄 접근할 수 있는 Port를 열어줘야합니다.

그렇기에 Service 부분에 NodePort를 아래와 같이 추가합니다.

spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
    nodePort: 30100
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
    nodePort: 30200
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: NodePort

이를 통해 우리는 http 프로토콜로 30100 의 포트를 열어주었고,

https 프로토콜에는 30200 의 포트를 열어주었습니다.

공식문서에 아래와 같이 써있습니다.아래의 포트 범위를 일반적으로 사용합니다.

For quick testing, you can use a NodePort. This should work on almost every cluster, but it will typically use a port in the range 30000-32767.

kubectl apply -f ingress-nginx.yaml

 

우리는 외부에서 Server에 접속할 수 있는 Ingress-nginx-controller를 구성했습니다.

2. ingress-seminarhub 구성

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: seminarhub-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      
      - path: /
        pathType: Prefix
        backend:
          service:
            name: seminarhub-cloud-gateway-server-dev
            port:
              number: 80

프로젝트 구성도에서 언급했듯이 "/"로 들어오는 모든 요청을 seminarhub-cloud-gateway-server에 라우팅함으로써 라우팅처리를 cloud-gateway-server 위임합니다.

kubectl apply -f ingress-seminarhub.yaml

3. 각 서비스를 Deployment로 배포해보자

우선 첫번쨰로, seminarhub-cloud-gateway-server를 배포합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: seminarhub-cloud-gateway-server
spec:
  replicas: 1
  selector:
    matchLabels:
      name: seminarhub-cloud-gateway-server
  template:
    metadata:
      labels:
        name: seminarhub-cloud-gateway-server
    spec:
      containers:
      - image: eoghdhdls/seminarhub-cloud-gateway-server
        name: seminarhub-cloud-gateway-server
        ports:
        - containerPort: 80
      serviceAccountName: ingress-nginx
---
apiVersion: v1
kind: Service
metadata:
  name: seminarhub-cloud-gateway-server-dev
spec:
  ports:
  - port: 80
    protocol: TCP
    name: "http"
    targetPort: 80
  selector:
    name: seminarhub-cloud-gateway-server
      #  serviceAccountName: ingress-nginx

Deployment와 Service 를 활용하여 진행합니다.

kubectl apply -f seminarhub-cloud-gateway-server.yaml

 

cloud-gateway 가 생성되었습니다.

 

두번째로는 member 서비스를 배포합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: seminarhub-member-api-server
spec:
  replicas: 1
  selector:
    matchLabels:
      name: seminarhub-member-api-server
  template:
    metadata:
      labels:
        name: seminarhub-member-api-server
    spec:
      containers:
      - image: eoghdhdls/seminarhub-member-api-server
        name: seminarhub-member-api-server
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: seminarhub-member-api-server-dev
spec:
  ports:
  - port: 8080
    protocol: TCP
    name: "httpmember"
    targetPort: 8080
  selector:
    name: seminarhub-member-api-server

 

kubectl apply -f seminarhub-member-api-server.yaml

 

잘 실행되었나 확인합니다.

kubectl get pods -o wide

 

모든 Ingress, Deployment, Service Pod가 잘 실행중인지 확인합니다.

kubectl get all

 

4. Spring Cloud Kubernetes 로 서비스 디스커버리

Spring Cloud 환경에서는 Eureka가 대신에 Service Discovery를 하였었습니다. 그때의 방식은 Client Side Discovery 방식입니다.

k8s에서는 Server Side Discvoery 방식을 활용합니다.

https://spring.io/blog/2021/10/26/new-features-for-spring-cloud-kubernetes-in-spring-cloud-2021-0-0-m3

 

New Features For Spring Cloud Kubernetes In Spring Cloud 2021.0.0-M3

When we got the results back from the latest State Of Spring survey, 67% of you said you would like to see better support for service discovery and configuration management on Kubernetes. With the release of Spring Cloud 2021.0.0-M3, we are taking a big st

spring.io

위의 공식사이트에서 제공하는 Spring Cloud Kubernetes 파일을 실행시킵니다.

kubectl apply -f kubernetes-discoveryserver.yaml

서비스 디스커버리가 잘 작동하는지 확인합니다.

curl 10.40.0.6:8761/apps

5. 직접 테스트해보기

우선 현재 k8s Cluster내에서 member-pod가 잘 작동하고 있는 curl로 확인해보겠습니다.

curl 10.40.0.2:8080/api/v1/member/user@example.com

{"member_no":8,"member_id":"user@example.com","member_password":"string","member_nickname":"string","member_from_social":true,"inst_dt":null,"updt_dt":null,"del_dt":null}

이제 직접 Ingress에 요청하여서 제대로 Gateway 가 동작하는지 확인합니다.

그전에 먼저, Gateway 가 먼저 Service Discovery 보다 먼저 실행되엇었다면, Cloud Gateway 를 중지한뒤 다시 실행하셔야합니다.

kubectl delete -f seminarhub-cloud-gateway-server.yaml
kubectl apply -f seminarhub-cloud-gateway-server.yaml

 

이제 본인의 PC에서 확인합니다.

root@k8s-master-1:~/app/repo# curl 10.0.0.11:30100/api/v1/member/user@example.com
{"member_no":8,"member_id":"user@example.com","member_password":"string","member_nickname":"string","member_from_social":true,"inst_dt":null,"updt_dt":null,"del_dt":null}

 

혹은 아래와 같이 포트포워딩을 통해서 브라우저에서 접근할 수도 있습니다.

 

 

Spring Cloud  Kubernetes에 대한 정보는 아래의 글을 읽어보시면 큰 도움이 되실 것 같습니다.

https://docs.spring.io/spring-cloud-kubernetes/reference/discovery-client.html

 

DiscoveryClient for Kubernetes :: Spring Cloud Kubernetes

DiscoveryClient can also find services of type ExternalName (see ExternalName services). At the moment, external name support type of services is only available if the following property spring.cloud.kubernetes.discovery.include-external-name-services is s

docs.spring.io

https://docs.spring.io/spring-cloud-kubernetes/reference/spring-cloud-kubernetes-discoveryserver.html

 

Spring Cloud Kubernetes Discovery Server :: Spring Cloud Kubernetes

The Spring Cloud Discovery server uses the Kubernetes API server to get data about Service and Endpoint resrouces so it needs list, watch, and get permissions to use those endpoints. See the below sample Kubernetes deployment YAML for an examlpe of how to

docs.spring.io

 

+ Recent posts