본문 바로가기

윈도우 개발환경

쿠버네티스로 MSA DevOps 환경 구축; 5. URL기반 Service - Ingress

쿠버네티스로 MSA DevOps 환경 구축

1. 로컬 쿠버네티스설치

2. 로컬 컨테이너 Registry

3. Pod, Service, Deployment

4. 포트포워딩 없는 Minikube

5. URL기반 Service - Ingress

6. Helm | Chart - 패키지 설치

7. Helm | Chart - Chart 만들기

8. ArgoCD - GitOps

7. Ingress

지금까지는 쿠버네티스 Service를 외부에 열어주는(expose) 방법으로 NodePort를 사용했다. NodePort는 포트(Port) 번호로 접속할 수 있도록 Node(서버)의 iptable을 설정해 주는 단순한 연결 방법이다. 하지만 포트번호를 다르게 하는 것 만으로 Pod별로 제공하는 기능을 분류하려면 다시 DNS를 사용해서 DNS명을 분리한 다음 계층구조로 합치거나, API Gateway를 사용해서 각 포트별로 나오는 서비스의 경로를 재정의하는 등, 다른 방법이 추가로 필요하다. 

 

쿠버네티스에서는 HTTP, HTTPS로 만든 API서비스를 URL의 경로(path) 별로 Service에 연결해 주는 기능이 Ingress이다. Ingress를 사용하면 MSA를 구성하는 마이크로서비스들이 각가 다른 URL 경로에 할당되고, API요청이 들어오면 Ingress Controller는 URL 경로에 해당하는 서비스로 연결해 준다.

쿠버네티스 네트워크 구조, 출처: kubernetes.io/docs

위 그림은 쿠버네티스가 Ingress를 사용해 외부 사용자의 HTTP/HTTPS 요청을 Pod로 전달하는 과정을 보여주는 것인데, 사용자의 요청은,

 

  • 쿠버네티스 관리자가 외부 Load balancer를 설정해 놓았으면, 이 Load balancer를 먼저 통하고,
  • 그렇지 않을 경우, 쿠버네티스 내부의 Ingress Controller를 통해 URL로 구분해 Service로 전달되면,
  • Service가 routing rule에 따라 해당 Pod로 요청을 전달하는

흐름으로 진행된다.

 

Ingress Controller는

  • URL의 하위 경로를 Service로 연결하는 것 이외에도,
  • SSL/TLS로 암호화된(encrypted) 네트워크 트래픽을 풀어주는(decrypt) 기능과
  • L7 (Application Level), 다시 말해, HTTP(S) 수준의 Load balancer기능도 제공한다.

하지만, 공홈의 설명에 따르면 Ingress는 더 이상 발전시키지 않고 앞으로는 Gateway 기능을 발전시킨다고 설명하고 있어, 앞으로는 Ingress Controller가 아니라 API Gateway를 위주로 설계해야 할 것으로 예상된다.

 

쿠버네티스 서비스에 사용할 수 있는 Ingress Controller에는

  • AWS, GCP, Azure, 등 클라우드 서비스 회사에서 제공하는 Load balancer와 Ingress Controller,
  • F5, Citrix, 등 네트워크 소프트웨어 회사에서 제공하는 Controller,
  • HAProxy, APISix, 등 오픈 소스 단체에서 공개하는 Controller,

등이 있는데, 이미 내가 설치한 개발환경에서는 minikube를 사용하고 있으니, minikube에서 addon으로 제공하는 NginX기반 Ingress Controller를 사용하면 된다.

a. Minikube Ingress addon

Ingress기능을 사용하려면 먼저 minikube의 ingress addon을 enable 하고, ingress-nginx-controller가 정상적으로 실행되고 있는지 확인한다.

 

minikube addons enable ingress
kubectl get pod -n ingress-nginx

 

b. Ingress 생성

아래 내용을 "ingress.yml"파일에 저장하고,

 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-all
spec:
  rules:
    - host: fastapi.test
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: service-a
                port:
                  number: 80

 

"kubectl apply"로 ingress를 생성한다.

 

kubectl apply -f ingress.yml

 

c. hosts 파일 등록

로컬 테스트 환경에서 Ingress를 사용하기 위해서는 "ingress.yml"파일의 host에 선언된 도메인명 "fastapi.test"가 리눅스계열에서는 /etc/hosts, 윈도우에서는 "C:\Windows\System32\drivers\etc\hosts"파일에 반영되어 있어야 문제없이 동작한다.

 

윈도우에서는 hosts파일을 수정하기 위해 "관리자 권한"이 필요하므로, 먼저 CMD나 PowerShell을 "관리자 권한"으로 열고, nvim.exe나 notepad.exe로 파일을 열고 minikube의 ip를 등록해야 한다.

 

minikube의 ip는

 

minikube ip

 

로 확인할 수 있고, hosts파일의 끝에 아래와 같이 minikube의 ip를 "fastapi.test" 도메인명으로 연결해 주는 내용을 추가해서 저장한다.

 

<minikube ip>  fastapi.test

 

d. Service의 type을 ClusterIP로 변경하고 배포

이제 Service를 NodePort로 지정할 필요하 없으니 Service type을 default값인 ClusterIP로 변경하거나 type선언을 삭제하고 다시 배포한 다음, Service가 ClusterIP인 상태 curl로 API를 조회해 본다.

 

kubectl apply -f servicea.yml
kubectl get service
curl http://fastapi.test/
curl http://fastapi.test/user/sam

 

e. 쿠버네티스의 Service들을 Ingress로 통합

  • 다시 기존의 소스를 약간 수정해서 새로운 API를 만들고 item_main.py로 저장한다.
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    itemid: str

item = Item(itemid= 'juice')

@app.get("/")
def read_root():
    return {"Item": "API Test"}

@app.get("/item/{item_id}")
def exist_item(item_id: str):
    if item_id == item.itemid:
        return {"status": "exist"}
    return {"status": "not exist"}

 

  • docker 이미지를 만들기 위해 기존의 Dockerfile을 약간 수정해 아래 내용을 만들고, (이미 Dockerfile이 있기 때문에) Item_Dockerfile로 저장한다.
FROM python

RUN <<EOF
pip install "fastapi[standard]"
pip install pydantic
EOF
EXPOSE 8000

WORKDIR /app
COPY item_main.py .

CMD ["fastapi", "dev", "item_main.py", "--host", "0.0.0.0"]

 

  • docker build를 하고 이미지를 push 한다.

docker build를 할 때 Dockerfile과 다른 이름의 파일을 사용하기 위해 "-f <Dockerfile 명>"으로 지정하고, 이미지 이름을 microservice-b로 지정한다.

 

docker buildx build -t localhost:5000/microservice-b -f Item_Dockerfile .
docker push localhost:5000/microservice-b

 

  • deploymenta.yml파일의 deploy-a, deploy-a-label, pod-a-label, container-a, microservice-a를 모두 찾아 deploy-b, deploy-b-label, pod-b-label, container-b, microservice-b로 바꾼 다음 deploymentb.yml파일로 저장한 다음 kubectl apply 명령으로 배포한다.
kubectl.exe apply -f deploymentb.yml

 

  • servicea.yml파일의 service-a, service-a-label, pod-a-label을 모두 찾아 service-b, service-b-label, pod-b-label로 바꾸고, port번호도 바꾼다. 일관성을 유지하려면, servicea.yml파일의 port는 8001, serviceb.yml은 8002로 하는 것이 좋을 것이다.

내용을 serviceb.yml파일에 저장하고

 

apiVersion: v1
kind: Service
metadata:
  name: service-b
  labels:
    net: service-b-label
spec:
  ports:
  - port: 8002
    targetPort: 8000
    protocol: TCP
    name: http
  selector:
    app: pod-b-label

 

kubectl apply 명령으로 배포한다.

 

kubectl.exe apply -f serviceb.yml

 

이제, ingress.yml파일을 아래와 같이 service-a는 /api, service-b는 /item으로 URL이 시작하도록 수정한다.

 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-all
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
    - host: fastapi.test
      http:
        paths:
          - path: /api(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: service-a
                port:
                  number: 8001
          - path: /item(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: service-b
                port:
                  number: 8002

 

kubectl apply로 배포하고, curl로 service-a의 API와 service-b의 API를 각각 테스트해 보면

 

curl http://fastapi.test/api
curl http://fastapi.test/api/user/sam
>curl http://fastapi.test/item
curl http://fastapi.test/item/item/juice

 

정상적으로 나오는 것을 확인할 수 있다.

 

이제 간단하지만 마이크로서비스 아키텍처를 따르는 API서비스가 내 컴퓨터에 있는 쿠버네티스 클러스터에 만들어졌다.