웹앱을 배포할 때 웹 개발자가 반드시 준수해야 하는 사항이 있다. 바로 TLS/SSL 인증서를 발급받는 일이다. 이번 포스트에서는 인증서에 대한 개념을 뒤로 하고, 쿠버네트 엔진에서 인증서를 발급받기 위해 반드시 이해해야 하는 배포 동작원리에 대해서 설명하고자 한다.


1. 쿠버네트는 HTTP(S) 로드밸런싱을 선호한다.

해당 부분은 설명이 여기저기 분산되어 있고 구글에서 제공하는 문서가 다소 빈약하므로 포괄적인 원리에 대해서 개인적으로 이해한 바대로 설명하겠다.

쿠버네트 엔진 문서를 보면 다음과 같이 두 가지의 방식의 배포방법을 알려준다.

Kubernetes Engine offers integrated support for two types of cloud load balancing for a publicly accessible application:

	1. You can create TCP load balancers by specifying type: LoadBalancer on a Service resource manifest. Although a TCP load balancer works for HTTP web servers, they are not designed to terminate HTTP(S) traffic as they are not aware of individual HTTP(S) requests. Kubernetes Engine does not configure any health checks for TCP load balancers.

	2. You can create HTTP(S) load balancers by using an Ingress resource. HTTP(S) load balancers are designed to terminate HTTP(S) requests and can make better context-aware load balancing decisions. They offer features like customizable URL maps and TLS termination. Kubernetes Engine automatically configures health checks for HTTP(S) load balancers.

If you are exposing an HTTP(S) service hosted on Kubernetes Engine, HTTP(S) load balancing is the recommended method for load balancing.

위의 설명을 간략히 설명하면 쿠버네트 엔진을 사용하는 경우 HTTP(S) 로드 밸런싱 방식을 사용하는 것을 추천하고 있다. 이 방식은 도메인을 호스트명으로 두고 여러 개의 URL을 생성해 연결할 수 있다는 점과 TLS 인증서 발급을 커스터마이징할 수 있다는 관리 측면의 장점을 가지고 있다.

1번에서 설명한 TCP 로드밸런싱 방식의 배포가 Service 설정으로 배포를 진행한다면 HTTP(S) 로드 밸런싱 기법은 Ingress 설정을 사용한다. 그렇다면 Ingress는 무엇일까?


2. Ingress 이해하기

Ingress에 대한 문서가 처음 읽으면 어렵기도 하고 전체 개념을 설명해주지 않는 것 같아서 핵심적인 부분만 인용하여 설명하고자 한다.

기존의 TCP 방식 로드밸런싱 방식은 다음과 같다. 즉, 웹앱을 직접 연결하여 퍼블리싱하는 방식이다.

    internet
        |
  ------------
  [ Services ]

반면 IngressinternetService(배포한 클러스터) 사이에 위치해 외부에서 들어오는 복수 개의 인바운드 요청을 클러스터로 연결해주는 설정파일이다. 즉, 다음과 같이 그려볼 수 있다.

    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

이러한 Ingress는 다음과 같은 설정을 가능케 한다.

  • 외부로 연결되는 URL을 원하는 갯수만큼 설정 가능 == 들어오는 문을 여러 개 만드는 것
  • 로드밸런스 트래픽 처리 == 유연한 분산처리
  • SSL 설정 가능 == 보안 처리
  • 이름 기반 가상호스팅 가능 == 도메인 호스팅이 별도로 필요없음


위의 개념 설명을 바탕으로 매우 간략하게 짜여진 Ingress 파일을 살펴보면 다음과 같다.

apiVersion: extensions/v1beta1
kind: Ingress   # 파일형식
metadata:
  name: test-ingress   # 해당 설정에 할당하는 이름
  annotations:    # 세부 설정(나중에 Ingress 컨트롤러, tls 관련 설정이 여기에 들어간다)
    ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath    # Ingress를 적용하기 원하는 패스 
        backend:	   # 서브하고자 하는 백엔드 서비스 설정
          serviceName: test
          servicePort: 80


Ingress는 설정파일이라고 했는데, 이 말인 즉슨 Ingress 설정파일은 그 자체로 동작하지 않는다. 따라서 Ingress에 적힌대로 처리를 해주는 Ingress-Controller가 필요하다.

보통 Nginx를 사용하는 편이고, Jay Gorrell의 문서1에 따르면 리버스 프록싱이 가능한 모든 시스템이면 된다고 한다. 예를 들면 Apache 웹서버나 Nginx와 같은 것들이다. 해당 역할은 보안을 위한 것인데, 클라이언트가 서버로 요청을 보낼 때 가운데에서 중개를 해주고 공격이 들어오더라도 내부서버로 침투하지 못하도록 하는 역할을 한다.

나는 이 역할을 해주는 Nginx Ingress Controller를 사용했다. 만약 로드밸런서 서비스를 지원하지 않는 플랫폼에서 배포를 하는 경우에는 배포하고자 하는 앱을 NodePort로 배포한 후 해당 노드와 연결시켜주면 된다. 이렇게 하면 리버스 프록시 라우팅이 노출된 NodePort 내부 각 노드의 Ingress Controller로 연결된다.

내 Ingress Controller의 설정파일은 다음과 같다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: customize-this! // # Deployment의 namespace와 통일해주자!
spec:
  type: LoadBalancer
  ports:
  - port: 80
    name: http
  - port: 443
    name: https
  selector:
    k8s-app: nginx-ingress-controller

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  labels:
    k8s-app: nginx-ingress-controller
  namespace: customize-this!
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: nginx-ingress-controller
      annotations:
        prometheus.io/port: '10254'
        prometheus.io/scrape: 'true'
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: nginx-ingress-controller
          image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.11
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
          - name: http
            containerPort: 80
            hostPort: 80
          - name: https
            containerPort: 443
            hostPort: 443
          args:
            - /nginx-ingress-controller
            - "--default-backend-service=$(POD_NAMESPACE)/default-http-backend"


3. Default-http-backend?

자, 컨트롤러의 설정파일을 찬찬히 봤다면 또 다른 의문이 들 것이다. default-http-backend는 무엇일까?

위의 Controller 설정 파일은 Ingress 설정에서 요청한 라우트를 서빙하기 위해 Ingress 리소스를 모니터링한다. 해당 파일은 실행시 --default-http-backend라는 아규먼트를 요청하는데, 이는 Nginx Ingress Controller를 사용할 경우에만 필요로 한다.


간단히 말하면 Default-http-backend는 Ingress Controller가 확인할 수 없는 Ingress 설정이 있을 경우 404 페이지를 돌려주는 서비스이다.

아래의 설정파일에도 적혀있듯이, 404 페이지를 반환하는 도커이미지면 모두 Default-http-backend로 사용할 수 있다. 단, /healthz 패스에서는 200을 돌려주도록 설정되어 있다.

# default-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    app: default-http-backend
  namespace: customize this! # Service의 namespace와 통일해주자!
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: default-http-backend
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: default-http-backend
        # Any image is permissable as long as:
        # 1. It serves a 404 page at /
        # 2. It serves 200 on a /healthz endpoint
        image: gcr.io/google_containers/defaultbackend:1.4
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi

---
kind: Service
apiVersion: v1
metadata:
  name: default-http-backend
  namespace: customize this!
  labels:
    k8s-app: default-http-backend
spec:
  selector:
    k8s-app: default-http-backend
  ports:
    - port: 80
      targetPort: 8080


4. 마치며

지금까지 설명한 것을 초보 개발자가 한번에 이해하기에는 무리가 있다. 다시 간단히 요약해보면, Kubenetes 엔진에서 선호하는 HTTP(S) Load Balancing 방식의 배포를 하기 위해 여러 서브도메인으로의 라우팅이 가능한 Ingress를 사용했고, 이 Ingress를 동작하도록 하기 위해 Nginx-Ingress-Controller를 사용했다. 그리고 Nginx-Ingress-Controller를 사용하기에 Default-http-backend 설정을 해주었다.

지금까지는 기본적으로 Ingress를 사용한 배포 설정을 설명한 것이다. 다음 포스트에서는 TLS/SSL 인증서에 대한 개념설명과 kube-lego라는 멋진 라이브러리를 소개하고자 한다.



Julia Hwang

디발자를 꿈꾸는 웹개발자의 블로그입니다.