SSL 인증서 발급 1부 - 배포방식에 대한 고찰
웹앱을 배포할 때 웹 개발자가 반드시 준수해야 하는 사항이 있다. 바로 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 ]
반면 Ingress
는 internet
과 Service
(배포한 클러스터) 사이에 위치해 외부에서 들어오는 복수 개의 인바운드 요청을 클러스터로 연결해주는 설정파일이다. 즉, 다음과 같이 그려볼 수 있다.
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
라는 멋진 라이브러리를 소개하고자 한다.