Ingress를 사용한 HTTP(S) 부하 분산에서 여러 SSL 인증서 사용

개요

HTTP(S) 부하 분산기는 클라이언트 SSL/TLS 연결을 종료한 후에 포드 간에 요청의 균형을 맞춥니다. [Ingress]를 통해 HTTP(S) 부하 분산기를 구성할 때는 최대 10개의 TLS 인증서를 클라이언트에 제공하도록 부하 분산기를 구성할 수 있습니다.

부하 분산기는 SNI(서버 이름 표시)를 사용하여 TLS 핸드셰이크의 도메인 이름을 기준으로 클라이언트에 제공할 인증서를 결정합니다. 클라이언트가 SNI를 사용하지 않거나 클라이언트가 한 인증서에서 일반 이름(CN)과 일치하지 않는 도메인 이름을 사용하는 경우, 부하 분산기는 Ingress에 나열되는 첫 번째 인증서를 사용합니다. 아래 다이어그램은 요청에서 사용되는 도메인 이름에 따라 트래픽을 다른 백엔드로 보내는 부하 분산기를 보여줍니다.

Ingress 시스템 다이어그램의 여러 SSL 인증서

두 가지 방법 중 하나를 사용해서 Ingress에 대한 인증서를 지정할 수 있습니다.

  • Kubernetes 보안 비밀

  • 이전에 Google Cloud Platform 프로젝트에 업로드한 사전 공유 인증서

두 방법을 동시에 사용할 수는 없습니다. 동일한 방법을 사용하여 모든 인증서를 지정해야 합니다.

최소 GKE 버전

GKE 버전 1.10.2 이상이 있어야 사전 공유 인증서를 사용하거나 Ingress에서 여러 인증서를 지정할 수 있습니다.

개요

다음은 이 주제의 단계 개요입니다.

  1. 배포를 만듭니다.

  2. 서비스를 만듭니다.

  3. 두 개의 인증서 파일과 두 개의 키 파일을 만듭니다.

  4. Secret 또는 사전 공유 인증서를 사용하는 Ingress를 만듭니다. Ingress를 만들면 GKE가 HTTP(S) 부하 분산기를 만들고 구성합니다.

  5. HTTP(S) 부하 분산기를 테스트합니다.

시작하기 전에

이 작업을 준비하려면 다음 단계를 수행하세요.

  • Google Kubernetes Engine API가 사용 설정되었는지 확인합니다.
  • Google Kubernetes Engine API 사용 설정
  • Cloud SDK가 설치되었는지 확인합니다.
  • 기본 프로젝트 ID를 설정합니다.
    gcloud config set project [PROJECT_ID]
  • 영역 클러스터를 사용하는 경우 기본 컴퓨팅 영역을 설정합니다.
    gcloud config set compute/zone [COMPUTE_ZONE]
  • 리전 클러스터를 사용하는 경우 기본 컴퓨팅 리전을 설정합니다.
    gcloud config set compute/region [COMPUTE_REGION]
  • gcloud를 최신 버전으로 업데이트합니다.
    gcloud components update

배포 만들기

배포 매니페스트는 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-mc-deployment
spec:
  selector:
    matchLabels:
      app: products
      department: sales
  replicas: 3
  template:
    metadata:
      labels:
        app: products
        department: sales
    spec:
      containers:
      - name: hello
        image: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50001"
      - name: hello-again
        image: "gcr.io/google-samples/node-hello:1.0"
        env:
        - name: "PORT"
          value: "50002"

배포에는 세 개의 포드가 있고, 각 포드에는 두 개의 컨테이너가 있습니다. 한 컨테이너는 hello-app을 실행하고 TCP 포트 50001에서 수신 대기합니다. 다른 컨테이너는 node-hello를 실행하고 TCP 포트 50002에서 수신 대기합니다.

이름이 my-mc-deployment.yaml인 파일에 매니페스트를 복사하고 배포를 만듭니다.

kubectl apply -f my-mc-deployment.yaml

서비스 만들기

서비스 매니페스트는 다음과 같습니다.

apiVersion: v1
kind: Service
metadata:
  name: my-mc-service
spec:
  type: NodePort
  selector:
    app: products
    department: sales
  ports:
  - name: my-first-port
    protocol: TCP
    port: 60001
    targetPort: 50001
  - name: my-second-port
    protocol: TCP
    port: 60002
    targetPort: 50002

서비스 매니페스트에 있는 selector 필드는 app: products 라벨 및 department: sales 라벨을 포함하는 포드가 이 서비스의 구성원임을 의미합니다. 따라서 이전 단계에서 만든 배포의 포드가 서비스의 구성원입니다.

서비스 매니페스트의 ports 필드는 ServicePort 객체의 배열입니다. 클라이언트가 my-first-port에서 서비스에 요청을 보낼 때, 요청은 포트 50001에 있는 구성원 포드 중 하나로 전달됩니다. 클라이언트가 my-second-port에서 서비스에 요청을 보낼 때는 요청이 포트 50002에 있는 구성원 포드 중 하나로 전달됩니다.

이름이 my-mc-service.yaml인 파일에 매니페스트를 복사하고 서비스를 만듭니다.

kubectl apply -f my-mc-service.yaml

인증서 및 키 만들기

이 페이지의 연습을 수행하려면 두 개의 인증서가 필요하며, 각각 해당하는 키가 있어야 합니다. 각 인증서에는 소유 중인 도메인 이름과 일치하는 일반 이름(CN)이 있어야 합니다. 적절한 일반 이름 값이 있는 인증서 파일 2개를 이미 가지고 있는 경우 다음 섹션으로 건너뛸 수 있습니다.

첫 번째 키를 만듭니다.

openssl genrsa -out test-ingress-1.key 2048

첫 번째 인증서 서명 요청을 만듭니다.

openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
    -subj "/CN=[FIRST_DOMAIN_NAME]"

여기서 [FIRST_DOMAIN_NAME]은 소유하고 있는 도메인 이름이거나 가짜 도메인 이름입니다.

예를 들어 부하 분산기가 your-store.example의 요청을 처리하도록 하고자 합니다. 그러면 인증서 서명 요청이 다음과 비슷하게 표시될 것입니다.

openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
    -subj "/CN=your-store.example"

첫 번째 인증서를 만듭니다.

openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \
    -out test-ingress-1.crt

두 번째 키를 만듭니다.

openssl genrsa -out test-ingress-2.key 2048

두 번째 인증서 서명 요청을 만듭니다.

openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
    -subj "/CN=[SECOND_DOMAIN_NAME]"

여기서 [SECOND_DOMAIN]은 소유하고 있는 도메인 이름이거나 가짜 도메인 이름입니다.

예를 들어 부하 분산기가 your-experimental-store.example의 요청을 처리하도록 하고자 합니다. 그러면 인증서 서명 요청이 다음과 비슷하게 표시될 것입니다.

openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
    -subj "/CN=your-experimental-store.example"

두 번째 인증서를 만듭니다.

openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \
    -out test-ingress-2.crt

인증서 및 키를 직접 만드는 방법에 대한 자세한 내용은 비공개 키 및 서명된 인증서 받기를 참조하세요.

이제 두 개의 인증서 파일과 두 개의 키 파일이 있습니다. 이 작업의 나머지 단계는 이 자리표시자를 사용하여 도메인, 인증서 파일, 키 파일을 나타내는 것입니다.

  • [FIRST_CERT_FILE]은 첫 번째 인증서 파일의 경로입니다.

  • [FIRST_KEY_FILE]은 첫 번째 인증서와 함께 전달되는 키 파일의 경로입니다.

  • [SECOND_CERT_FILE]은 두 번째 인증서 파일의 경로입니다.

  • [SECOND_KEY_FILE]은 두 번째 인증서와 함께 전달되는 키 파일의 경로입니다.

  • [FIRST_DOMAIN]은 소유하고 있는 도메인 이름이거나 가짜 도메인 이름입니다.

  • [SECOND_DOMAIN]은 소유하고 있는 두 번째 도메인 이름이거나 두 번째 가짜 도메인 이름입니다.

Ingress에 대한 인증서 지정

다음 단계는 Ingress 객체를 만드는 것입니다. Ingress 매니페스트에서 두 가지 방법 중 하나를 사용하여 부하 분산기의 인증서를 제공할 수 있습니다.

  • Secret
  • 사전 공유 인증서

SECRETS 탭 또는 PRE-SHARED CERTS 탭을 선택하여 두 가지 방법 중 하나를 선택합니다.

Secret

Secret 만들기

첫 번째 인증서 및 키를 포함하는 Secret을 만듭니다.

kubectl create secret tls my-first-secret \
    --cert [FIRST_CERT_FILE] --key [FIRST_KEY_FILE]

두 번째 인증서 및 키를 포함하는 Secret을 만듭니다.

kubectl create secret tls my-second-secret \
    --cert [SECOND_CERT_FILE] --key [SECOND_KEY_FILE]

Ingress 만들기

Ingress 매니페스트는 다음과 같습니다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-mc-ingress
spec:
  tls:

  • secretName: my-first-secret
  • secretName: my-second-secret rules:
  • host: [FIRST_DOMAIN] http: paths:
    • backend: serviceName: my-mc-service servicePort: my-first-port
  • host: [SECOND_DOMAIN] http: paths:
    • backend: serviceName: my-mc-service servicePort: my-second-port

이름이 my-mc-ingress.yaml인 파일에 매니페스트를 복사합니다. [FIRST_DOMAIN][SECOND_DOMAIN]을 소유하고 있는 도메인 이름이나 가짜 도메인 이름으로 바꿉니다.

Ingress를 만듭니다.

kubectl apply -f my-mc-ingress.yaml

Ingress를 만들면 GKE Ingress 컨트롤러가 HTTP(S) 부하 분산기를 만듭니다. GKE가 외부 IP 주소를 부하 분산기에 할당하도록 잠시 기다립니다.

Ingress를 설명합니다.

kubectl describe ingress my-mc-ingress

두 개의 Secret이 Ingress와 연관되어 있다는 것이 출력에 표시됩니다. 부하 분산기의 외부 IP 주소도 출력에 표시됩니다.

Name: my-mc-ingress
Address: 203.0.113.1
...
TLS:
  my-first-secret terminates
  my-second-secret terminates
Rules:
  Host              Path  Backends
  ----              ----  --------
  your-store.example
                       my-mc-service:my-first-port ()
  your-experimental-store.example
                       my-mc-service:my-second-port ()
Annotations:
  ...
Events:
  Type    Reason  Age   From                     Message
  ----    ------  ----  ----                     -------
  Normal  ADD     3m    loadbalancer-controller  default/my-mc-ingress
  Normal  CREATE  2m    loadbalancer-controller  ip: 203.0.113.1

사전 공유 인증서

사전 공유 인증서 사용

Google Cloud Platform 프로젝트에서 인증서 리소스를 만듭니다.

gcloud compute ssl-certificates create test-ingress-1 \
  --certificate [FIRST_CERT_FILE] --private-key [FIRST_KEY_FILE]

각 항목의 의미는 다음과 같습니다.

Google Cloud Platform 프로젝트에서 두 번째 인증서 리소스를 만듭니다.

gcloud compute ssl-certificates create test-ingress-2 \
  --certificate [SECOND_CERT_FILE] --private-key [SECOND_KEY_FILE]

각 항목의 의미는 다음과 같습니다.

  • [SECOND_CERT_FILE]은 두 번째 인증서 파일입니다.

  • [SECOND_KEY_FILE]은 두 번째 키 파일입니다.

인증서 리소스를 확인합니다.

gcloud compute ssl-certificates list

test-ingres-1test-ingress-2라는 인증서 리소스가 있는 것으로 출력에 표시됩니다.

NAME                CREATION_TIMESTAMP
test-ingress-1      2018-11-03T12:08:47.751-07:00
test-ingress-2      2018-11-03T12:09:25.359-07:00

다음은 주석에서 사전 공유 인증서 리소스를 나열하는 Ingress 매니페스트입니다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-psc-ingress
  annotations:
    ingress.gcp.kubernetes.io/pre-shared-cert: "test-ingress-1,test-ingress-2"
spec:
  rules:
  - host: [FIRST_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-first-port
  - host: [SECOND_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-second-port

이름이 my-psc-ingress.yaml인 파일에 매니페스트를 복사합니다. [FIRST_DOMAIN][SECOND_DOMAIN]을 소유하고 있는 도메인 이름이나 가짜 도메인 이름으로 바꿉니다.

Ingress를 만듭니다.

kubectl apply -f my-psc-ingress.yaml

GKE가 외부 IP 주소를 부하 분산기에 할당하도록 잠시 기다립니다.

Ingress를 설명합니다.

kubectl describe ingress my-psc-ingress

Ingress가 test-ingress-1test-ingress-2라는 사전 공유 인증서와 연관된 것으로 출력에 표시됩니다. 부하 분산기의 외부 IP 주소도 출력에 표시됩니다.

Name:             my-psc-ingress
Address:          203.0.113.2
...
Rules:
  Host              Path  Backends
  ----              ----  --------
  your-store.example
                       my-mc-service:my-first-port ()
  your-experimental-store.example
                       my-mc-service:my-second-port ()
Annotations:
  ...
  ingress.gcp.kubernetes.io/pre-shared-cert:    test-ingress-1,test-ingress-2
  ...
  ingress.kubernetes.io/ssl-cert:               test-ingress-1,test-ingress-2
Events:
  Type    Reason  Age   From                     Message
  ----    ------  ----  ----                     -------
  Normal  ADD     2m    loadbalancer-controller  default/my-psc-ingress
  Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2

부하 분산기 테스트

GKE가 부하 분산기 구성을 마치도록 5분 정도 기다립니다.

이 단계를 수행하려면 두 개의 도메인 이름을 소유하고 있어야 합니다. 그리고 두 도메인 이름 모두가 HTTP(S) 부하 분산기의 외부 IP 주소를 확인해야 합니다.

첫 번째 도메인 이름을 사용하여 부하 분산기에 요청을 보냅니다.

curl -kv https://[FIRST_DOMAIN]

TLS 핸드셰이크에서 첫 번째 인증서가 사용된 것으로 출력에 표시됩니다. 첫 번째 도메인이 your-store.example인 경우 출력은 다음과 비슷합니다.

...
*   Trying 203.0.113.1...
...
* Connected to your-store.example (203.0.113.1) port 443 (#0)
...
* TLSv1.2 (IN), TLS handshake, Certificate (11):
...
* Server certificate:
*  subject: CN=your-store.example
...
> Host: your-store.example
...
<
Hello, world!
Version: 2.0.0
...

두 번째 도메인 이름을 사용하여 부하 분산기에 요청을 보냅니다.

curl -kv https://[SECOND_DOMAIN]

TLS 핸드셰이크에서 두 번째 인증서가 사용된 것으로 출력에 표시됩니다. 두 번째 도메인이 your-experimental-store.example인 경우 출력은 다음과 비슷합니다.

...
*   Trying 203.0.113.1...
...
* Connected to your-experimental-store.example (203.0.113.1) port 443 (#0)
...
* Server certificate:
*  subject: CN=your-experimental-store.example
...
> Host: your-experimental-store.example
...
Hello Kubernetes!

Ingress 객체의 호스트 필드

IngressSpec에는 IngressTLS 객체의 배열인 tls 필드가 있습니다. 각 IngressTLS 객체에는 hosts 필드와 SecretName 필드가 있습니다. GKE에서 hosts 필드는 사용되지 않습니다. GKE는 Secret의 인증서에서 일반 이름(CN)을 읽습니다. 일반 이름이 클라이언트 요청에 있는 도메인 이름과 일치하면 부하 분산기가 일치하는 인증서를 클라이언트에 제공합니다.

제공되는 인증서

부하 분산기는 다음 규칙을 따라 인증서를 선택합니다.

  • Secret과 사전 공유 인증서 모두가 Ingress에 나열된 경우, 부하 분산기는 Secret를 무시하고 사전 공유 인증서 목록을 사용합니다.

  • 클라이언트 요청에서 도메인 이름과 일치하는 일반 이름(CN)을 가진 인증서가 없는 경우 부하 분산기는 기본 인증서를 제공합니다.

  • tls 블록에 나열된 Secret의 경우, 기본 인증서는 목록의 첫 번째 Secret에 있습니다.

  • 주석에 나열된 사전 공유 인증서의 경우, 기본 인증서는 목록에서 첫 번째 인증서입니다.

문제해결

무효하거나 존재하지 않는 Secret을 지정하면 Kubernetes 이벤트 오류가 발생합니다. Ingress의 Kubernetes 이벤트는 다음과 같이 확인할 수 있습니다.

kubectl describe ingress

이 출력은 다음과 비슷합니다.

Name:             my-ingress
Namespace:        default
Address:          203.0.113.3
Default backend:  hello-server:8080 (10.8.0.3:8080)
TLS:
  my-faulty-Secret terminates
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     my-service:443 (10.8.0.3:443)
Events:
   Error during sync: cannot get certs for Ingress default/my-ingress:
 Secret "my-faulty-ingress" has no 'tls.crt'

다음 단계

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Kubernetes Engine