SLASH 기술 블로그

쿠버네티스로 배포하기 3 - 서비스 노출하기 (Ingress) 본문

인프라/쿠버네티스

쿠버네티스로 배포하기 3 - 서비스 노출하기 (Ingress)

SLASH 2021. 5. 30. 21:47
반응형

1. How to containerize your app - 앱을 컨테이너화하는 방법에 대해 알아보자.

2. Running on Kubernetes Cluster - 쿠버네티스 클러스터에서 이미지 실행하기

3. Exposing the service - 서비스를 외부에 공개하기

4. Simplifying deployment process - 배포 과정 단순화하기

 

이전 글에서 포트포워드와 NodePort 서비스를 통해 서비스를 외부에서 접근할 수 있도록 설정하는 방법을 알아봤다.

 

그러나 실제로 서비스를 할 때는 여러 서버가 있을 때 파드 간의 부하를 분산하거나 한 파드에 장애가 발생한 경우 이를 감지해서 다른 파드로 라우팅시키는 로드 밸런싱이 필요하기도 하고, 하나의 도메인에서 path에 따라 서로 다른 서비스를 사용하는 마이크로서비스 아키텍처를 사용하고 싶은 경우도 있을 것이다.

 

NodePort만으로는 이 모든 작업을 수행할 수는 없다. LoadBalancer 타입의 서비스를 쓰거나, Ingress를 사용해야 한다.

 

옵션 1 - LoadBalancer Service

간단한 로드밸런싱만 사용한다면 LoadBalancer 서비스를 사용하면 된다. 쿠버네티스에서 자체적으로 로드 밸런서를 제공하지는 않고, 클라우드 프로바이더에서 제공하는 로드 밸런서를 쓰게 된다.

apiVersion: v1
kind: Service
metadata:
  name: deno-app-service
spec:
  type: LoadBalancer
  selector:
    app: deno-app
  ports:
    - port: 80
      targetPort: 8080

 

위와 같이 서비스를 작성한 후 kubectl apply로 서비스를 생성 혹은 설정하면, (AWS일 경우) AWS의 클래식 로드밸런서가 생성이 되고, 외부에서 접근할 수 있는 주소가 할당이 된다. (필요하다면 어플리케이션 로드밸런서나 네트워크 로드밸런서를 사용할 수도 있음)

classic 유형의 로드 밸런서가 생성되었다.

❯ k get svc/deno-app-service
NAME               TYPE           CLUSTER-IP       EXTERNAL-IP PORT(S)        AGE
deno-app-service   LoadBalancer   172.20.169.160   <ELB 주소>   80:31205/TCP   47s

 

minikube에서도 metallb 애드온을 통해서 LoadBalancer 타입의 서비스를 사용할 수 있다. 관련 내용은 쿠버네티스 안내서에서 더욱 자세하게 다루고 있으니 그쪽을 참고하도록 하자.

❯ minikube addons enable metallb
    ▪ Using image metallb/speaker:v0.8.2
    ▪ Using image metallb/controller:v0.8.2
🌟  'metallb' 애드온이 활성화되었습니다

 

LoadBalancer 타입의 서비스는 사용 방법이 간편해 곧바로 사용할 수 있다는 장점이 있다. 하지만 매 번 서비스를 만들 때마다 로드 밸런서를 생성하게 되므로 자원적인 낭비가 심하다. 곧이어서 이야기할 Ingress를 사용하면 하나의 로드 밸런서를 통해 다양한 서비스를 컨트롤할 수 있다. Ingress를 사용하는 방법을 알아보자.

 

옵션 2 - Ingress

Ingress는 서비스에 대한 외부 접근을 관리하는 오브젝트로, 이름 기반의 라우팅, 로드 밸런싱, TLS 핸들링 등의 기능을 제공한다. 이게 무슨 말일까?

 

쿠버네티스의 Ingress는 L7 로드밸런서로, HTTP와 HTTPS같은 어플리케이션 계층에서 로드를 분산하기 때문에, 네트워크 계층에서 작동하는 L4 로드밸런서와는 달리 패킷의 내용을 확인하고 그에 따라 로드를 분배하는 것이 가능하다. 아래는 몇 가지 예시.

api.app.com -> service/api
app.com -> service/web
app.com/payment -> service/web-payment

 

이러한 트래픽 라우팅을 포함해 다양한 규칙을 정의한 것이 Ingress이고, Ingress Controller가 이 규칙을 해석해서 실제로 로드밸런싱을 수행한다.

 

다양한 Ingress Controller가 있는데, 쿠버네티스가 지원하는 인그레스 컨트롤러 중 가장 유명하고 널리 쓰이는 것은 nginx다. 그 외에 AWS같은 경우 ALB(Application Load Balancer)를 인그레스 컨트롤러로 사용할 수 있다.

 

1단계: Ingress Controller 설치

nginx ingress controller를 사용해서 실제로 인그레스 컨트롤러 / 인그레스를 만들어서 사용해보자! 

 

minikube를 사용하는 경우 다음과 같이 ingress 애드온을 활성화시키면 된다. (minikube의 ingress 애드온이 nginx ingress controller를 사용함.)

❯ minikube addons enable ingress

 

혹시 --vm 옵션을 true로 설정하라는 메시지가 지속적으로 나타난다면, 드라이버를 hyperkit을 사용하도록 변경해 해결할 수 있다. (vm 옵션이 무시되고 driver가 Docker로 설정되는 문제)

❯ minikube delete
❯ minikube start --vm=true --driver=hyperkit

 

AWS, Azure같은 클라우드 프로바이더에서 제공하는 클러스터를 사용할 경우, 문서에 나와있는 것과 같이 yaml 파일을 kubectl apply를 사용해서 적용하면 끝이다. (버전은 또 달라질 수 있으니 아래 코드블럭은 참고만 하도록 하자)

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.46.0/deploy/static/provider/aws/deploy.yaml

 

Helm을 사용해서 설치할 수도 있다. 나는 Helm을 사용하는 편이 좀 더 관리가 편한 것 같아서 Helm을 사용했는데, 요건 개인적인 취향인 것 같다. (어차피 Ingress Controller는 한 번 설치하면 자주 바꿀 일이 없음)

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx

 

2단계: Ingress 작성

이전에 만들었던 deno-app-service를 인그레스로 연결해보자. 도메인 주소를 흉내내기 위해서 sslip.io DNS를 사용할 것이다. 그냥 단순히 IP 주소에 대한 별칭을 만들어준다고 생각하면 된다. 자신의 클러스터의 외부 주소를 확인하고, 해당 주소 뒤에 '.sslip.io'를 붙여서 사용하면 된다. 더 다양한 예시는 위 sslip.io 사이트를 참고하자.

❯ minikube ip
192.168.64.2

❯ nslookup 192.168.64.2.sslip.io
Server:		222.107.211.161
Address:	222.107.211.161#53

Non-authoritative answer:
Name:	192.168.64.2.sslip.io
Address: 192.168.64.2

 

아래는 deno-app-service를 192.168.64.2.sslip.io 주소로 노출시키는 Ingress를 작성한 것이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: deno-ingress
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  rules:
    - host: 192.168.64.2.sslip.io
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: deno-app-service
                port:
                  number: 80

 

 

다소 복잡해보이지만 꽤나 직관적으로 구성되어 있어 하나씩 살펴보면 금방 이해할 수 있다.

  • ingressClassName - 사용할 Ingress Controller의 이름을 지정한다. GCE, ALB같은 다양한 인그레스 컨트롤러가 존재할 때 이 값을 통해서 원하는 컨트롤러를 선택할 수 있다. 쿠버네티스 1.18 버전 이전에는 kubernetes.io/ingress.class를 사용했다.
  • rules.host - 인그레스를 적용할 호스트 이름이다. Ingress Controller는 패킷을 확인하고 목적 호스트가 해당 값과 일치할 경우 정의한 서비스 백엔드로 라우팅시킨다. 구체적인 라우팅 정책은 http의 paths 속성에서 정의한다.
  • rules.http.paths - 호스트의 경로마다 어떤 서비스로 라우팅시킬지, 어떤 포트로 라우팅시킬지 결정한다. 위 예에서는 패킷의 목적지가 deno.192.168.64.2.sslip.io일 때 deno-app-service 서비스의 80번 포트로 라우팅시킨다.

192.168.64.2.sslip.io를 통해 서비스에 접근했다!

 

Host + Path 조합을 통해서 1) 여러 서비스를 하나의 호스트에서 경로에 따라 나누는 게 가능하고, 2) 하나의 서비스를 여러 호스트로 연결하는 것도 가능하다.

 

Optional: 도메인 연결하기

만약 소유하고 있는 도메인이 있다면 도메인 관리 서비스를 통해서 주소를 연결할 수 있다. 가지고 있는 도메인이 없더라도 걱정하지 말자. 여기서는 도메인이 없더라도 테스트해볼 수 있는 방법을 사용할 것이다.

 

이전에 작성했던 Ingress 명세에 호스트를 하나 더 추가하자. 내용은 동일하게 하고, host만 원하는 이름(아무거나)으로 변경했다.

  rules:
    ... # 다른 내용은 기존과 동일
    - host: turastory.info # sslip.io -> turastory.info로 변경
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: deno-app-service
                port:
                  number: 80

 

유닉스 계열이나 윈도우 모두 도메인 네임과 IP 주소를 매핑하는 hosts라는 파일을 가지고 있는데, 맥(혹은 리눅스)의 경우 /etc/hosts에 위치하고 있다. 우리가 익숙하게 사용하는 localhost 역시 자기 자신을 가리키는 IP인 127.0.0.1로 매핑되어 있다.

 

 

파일을 열고, 이전에 확인했던 클러스터 주소와 입력했던 도메인 네임을 연결하자.

❯ sudo vim /etc/hosts

# /etc/hosts
192.168.64.2 turastory.info

 

 

브라우저에 해당 도메인 네임을 입력하면, 동일하게 라우팅이 잘 되는 것을 볼 수 있다.

이번에는 turastory.info로 접근이 되었다..

 

Optional: TLS 설정하기

실제 서비스에서는 HTTP 프로토콜을 그대로 쓰는 경우는 적다. TLS(SSL) 인증을 통해서 보안을 강화한 HTTPS를 주로 사용하는데, 이를 위해서는 Ingress에 TLS 설정을 추가해주어야 한다.

 

실제로 서비스를 하기 위해서는 CA(Certificate Authority)가 서명한 TLS 인증서를 사용해야 하는데, 테스트 목적이니 그냥 직접 인증서를 만들어서 써보자. (실 서비스를 할 때는 AWS의 Route 53같은 도메인 관리 서비스를 통해서 원하는 도메인을 등록하고, 그에 해당하는 TLS 인증서를 다운받아서 사용한다.)

 

아래 명령을 통해서 인증서를 만들 수 있다. 인증서를 만들 때 Certificate Request를 위해서 여러 가지 항목을 물어볼텐데, 그냥 한 두개 정도만 대충 작성해도 된다. (모두 비우면 인증서가 만들어지지 않는다.)

❯ openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout key.pem -out cert.pem
❯ ls
cert.pem  key.pem

 

생성한 인증서를 Kubernetes의 Secret으로 생성해야 한다. YAML 파일을 만들수도 있긴 하지만, 간단하게 명령으로 만들어보자.

❯ kubectl create secret tls nginx-tls-secret --key key.pem --cert cert.pem
secret/nginx-tls-secret created

 

아까 작성했던 Ingress 설정 파일에 tls 항목을 추가하자. hosts 목록에 있는 도메인 네임은 rules에 정의한 도메인 네임과 일치해야 한다.

spec:
  tls:
    - hosts:
        - 192.168.64.2.sslip.io
        - turastory.info
      secretName: nginx-tls-secret
  rules: ...

 

이제 해당 주소로 접근하면... 아래와 같은 메시지가 나오는데, 정상적인 것이니 걱정하지 말자. TLS 인증서는 제대로 설정되었다! 다만 CA(Certificate Authority)에서 인증받지 않은 인증서라 경고를 하는거다.

CA가 유효하지 않다고 한다.

 

실 서비스를 할 때는 위와 같은 방법으로 제대로된 인증서를 등록하면 되겠다.

 


이미지를 만들고 배포한 다음, 실제 서비스를 위해서 필요한 작업들을 하나하나 살펴보았다. 로드 밸런싱, Ingress 설정, 도메인 등록, 그리고 TLS 인증서 설정까지.

 

이번 글의 핵심 주제인 Ingress는 기본적으로 Service와 함께 필수적으로 사용되고, 마이크로서비스 아키텍처를 구현하는 경우 아주 유용하게 사용되니 확실하기 짚고 넘어가는 게 좋을 것 같다.

 

도커라이징되어 있지 않은 앱을 쿠버네티스 상에 올리는 과정에 대해 거의 다 다룬 것 같은데, 아직 다루지 않은 부분이 몇 가지 있다. 바로 개발 과정을 어떻게 효율적으로 만드는가이다. 다음 번에는 이미지 배포와 테스트를 간편하게 하는 방법과 추가적으로 쿠버네티스를 사용하면서 손쉽게 설정할 수 있는 모니터링에 대해 알아보자.

 

@turastory

반응형
Comments