Search

VKE(Vultr Kubernetes Engine) 사용하기 (3) - Ingress 및 cert-manager로 TLS 설정 하기

들어가며

지난 포스팅에서는 VKE에서 LoadBalancer를 구축하여 외부에서 클러스터 내의 서비스에 접근할 수 있도록 설정을 해보았습니다.
이번 포스팅에서는 실제로 Argo CD를 클러스터에 구성하고, 인증서까지 발급 받아서 HTTPS 접속까지 테스트 해보려고 합니다.
이를 위해 크게 두 단계로 포스팅이 진행됩니다.
처음에는 단순히 LoadBalancer와 Ingress를 통해 외부에서 Argo CD로 접근이 되는 것을 확인합니다.
그리고 cert-manager 를 통해 인증서를 발급 받고, HTTPS 접속까지 가능하도록 하는 방법을 알아보도록 하겠습니다.

Argo CD 설치 및 Ingress 구성

먼저 Argo CD 를 VKE 클러스터에 구성해보겠습니다.
이 포스팅에서는 Argo CD 를 구성하는 것이 주 목적이 아니므로, 구성하는 방법에 대해서는 자세히 다루지 않을 예정입니다.
Helm을 통해서 설치를 진행하며, 꼭 필요한 value 설정만 간단히 정리하고 넘어가겠습니다.
helm repo add argo https://argoproj.github.io/argo-helm helm repo update
Bash
복사
지금 구성에서는 인증서가 없기 때문에 server.insecure: true 로 설정하고, Ingress 에서 /argocd path로 접근하기 위해 server.rootpath: '/argocd' 로 설정합니다.
configs: params: ## -- Create the argocd-cmd-params-cm configmap # If false, it is expected the configmap will be created by something else. create: true ## -- Server properties # -- Run server without TLS server.insecure: true # -- Used if Argo CD is running behind reverse proxy under subpath different from / server.rootpath: '/argocd' secret: ## `htpasswd -nbBC 10 "" $ARGO_PWD | tr -d ':\n' | sed 's/$2y/$2a/'` !QKFFPS argocdServerAdminPassword: "..."
YAML
복사

Ingress 설정

또한 우리는 이미 LoadBalancer를 구축해두었기 때문에 외부에서 클러스터 내 서비스로 접근이 가능한 환경을 만들어두었습니다.
일단은 LoadBalancer 를 통해 Argo CD UI로 접근할 수 있도록 ingress 설정도 추가하겠습니다.
server.ingress.https : 현재 TLS 구성이 되어있지 않기 때문에 false 로 설정
server.ingress.ingressClassName : 클러스터에서 사용 중인 ingressClass 이름을 작성합니다. 이 정보는 아래와 같이 확인 가능합니다.
> kubectl get ingressclass NAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none> 21m
Bash
복사
server.ingress.annotations
service.beta.kubernetes.io/vultr-loadbalancer-protocol: "http" Vultr LoadBalancer가 사용할 프로토콜을 알려줍니다. 아직 인증서 적용이 안되었기에 http 로 설정합니다.
server.ingress.paths : Argo CD에 접근할 수 있는 path를 설정합니다. 일단은 /argocd 로 설정합니다.
server: ingress: enabled: true https: false ingressClassName: "nginx" annotations: service.beta.kubernetes.io/vultr-loadbalancer-protocol: "http" paths: - /argocd
YAML
복사
이제 위 파일을 기반으로 Argo CD 를 설치합니다. 큰 문제 없이 구성된 것을 볼 수 있습니다.
helm install argocd -n argocd argo/argo-cd --version 6.7.3 -f vke-argocd-helm-values-without-tls.yaml
YAML
복사
> kubectl get pods,ingress -n argocd NAME READY STATUS RESTARTS AGE pod/argocd-application-controller-0 1/1 Running 0 14m pod/argocd-applicationset-controller-776bfd4bcd-4vj8r 1/1 Running 0 14m pod/argocd-dex-server-78dff6b6b-s4ffk 1/1 Running 0 14m pod/argocd-notifications-controller-8c8c6fbc7-pbzsk 1/1 Running 0 14m pod/argocd-redis-d9cbbc546-bglcg 1/1 Running 0 14m pod/argocd-repo-server-9b95d7668-bdt4s 1/1 Running 0 14m pod/argocd-server-6564df8567-cmhn2 1/1 Running 0 14m NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/argocd-server nginx argocd.example.com 158.247.231.231 80 14m
Bash
복사

Ingress host 제거

테스트 전에, Ingress 리소스를 살짝 수정해야 합니다.
그 이유는 이렇게 만들어진 Ingress 에는 host 정보가 임의로 들어가기 때문입니다.
저는 이미 제 개인 도메인을 가지고 운영하고 있지만, 현재는 연결하지 않은 상태라서 도메인이 없는 상태와 같습니다. 따라서 아래와 같이 host 정보를 지워줍니다.
spec: ingressClassName: nginx rules: - host: argocd.example.com http: paths: - backend: service: name: argocd-server port: number: 80 path: / pathType: Prefix
YAML
복사
> kubectl edit ingress -n argocd argocd- server ... spec: ingressClassName: nginx rules: - http: paths: - backend: service: name: argocd-server port: number: 80 path: / pathType: Prefix
YAML
복사

테스트

이제 LoadBalancer에 할당된 IP 주소인 158.247.231.231/argocd path를 붙여 접속해봅니다.
아래와 같이 아주 잘 접속되는 것을 확인할 수 있습니다!

두 가지 문제점

이렇게 외부에서 잘 접근할 수 있도록 구성했지만, 아직 두 가지 문제가 남아있습니다.
첫 번째는, 도메인 대신 우리가 잘 기억할 수없는 IP로 접근을 해야한다는 점입니다.
두 번째 문제는, 주소창 왼쪽을 보시면 주의 요함 이 보이고, 이를 눌러서 확인해보면 다음과 같이 HTTPS 연결이 아님을 경고하고 있습니다.
Ingress를 통해 클러스터 내부 서비스에 접근 가능하도록 설정했으니, 다음에는 DNS 주소를 사용하고, 추가로 인증서까지 연결하여 HTTPS 로 접근할 수 있도록 해보겠습니다.

TLS 인증서 구성하기

앞서 Argo CD로 접근을 Ingress를 통해 구성을 해보았습니다.
마지막에 언급했듯이, 도메인이 아닌 IP로 접근해야하는 것, 그리고 HTTPS 연결을 사용하지 않는 점이 아무래도 조금 꺼림칙합니다.
그럼 이제 HTTPS 접속을 가능하도록 인증서를 발급받고 적용하는 작업을 해보겠습니다.

Prerequisites

이 작업에는 한 가지 준비 사항이 만족되어야 진행이 가능합니다.
→ 자신의 도메인을 가지고 있어야 하고, 해당 도메인이 쿠버네티스 클러스터로 접근 설정이 완료된 상태여야 합니다.
이것이 되어있지 않다면 Self-Signed 인증서를 통해서 테스트 해볼 수는 있겠으나, 신뢰할 수 없는 인증서 경고가 발생하기에 근본적인 해결이 되지 않습니다.

SSL 인증서를 발급 받는 법

Self-Signed Certificates: 자체 인증을 사용하여 TLS/SSL 인증서를 만들고 서명합니다. 이는 개발 환경에 적합한 옵션입니다.
Purchase TLS/SSL Certificates: 운영 환경을 위해서 잘 알려진 인증 기관에서 TLS/SSL 인증서를 구매합니다.
Use Free TLS/SSL Certificates: Let's Encrypt 또는 ZeroSSL의 무료 TLS/SSL 인증서를 사용합니다. 무료인 대신, 인증서 유효기간이 짧으므로 주기적인 갱신이 필요합니다.
자체 서명 인증서는 위에서 확인했듯이 경고가 발생하므로, Let's Encrypt 에서 무료로 인증서를 발급받는 방법을 적용해보도록 하겠습니다.

cert-manager

cert-manager는 Kubernetes의 워크로드에 대한 TLS 인증서를 생성하고 인증서가 만료되기 전에 자동으로 갱신해주는 역할을 합니다.
cert-manager는 Let's Encrypt , HashiCorp Vault , Venafi 및 개인 PKI를 포함한 다양한 인증 기관 으로부터 인증서를 얻을 수 있습니다.
cert-manager를 사용하면 private key와 certificate는 Kubernetes Secret 리소스에 저장되며, 이 Secret을 Pod에 마운트하거나, ingress-controller가 사용하게 됩니다.
위는 cert-manager의 아키텍처입니다. Issuer Certificate Secret 3개의 주요 모듈로 구성되어 있습니다.

Issuer

Issuer는 한 줄로 요약하면 CA 를 표현하는 리소스입니다.
이 리소스들은 certificate signing requests에 의해 인증서에 서명을 할 때 사용 가능한 인증기관을 표현합니다.
Issuer는 두 종류로 네임스페이스 단위로 속하는 Issuer 와 클러스터 레벨의 ClusterIssuer 가 있습니다.

Certificate

Certificate 는 말 그대로 인증서 역할을 합니다.
cert-manager는 Certificate 리소스를 사용하여 private keyCertificateRequest 리소스를 생성하고 Issuer 또는 ClusterIssuer 로부터 서명된 인증서(signed certificate)를 받게 됩니다.
서명된 인증서와 priavate key는 Kubernetes Secret에 저장되며, cert-manager는 발급 받은 인증서가 만료되기 전에 자동 갱신을 요청합니다.
가장 귀찮고 신경 쓰이는 인증서 갱신을 자동으로 처리해준다는 점이 큰 장점입니다.

Kubernetes Secret

data: tls.crt: <...> tls.key: <...>
YAML
복사
Certificate에 의해 생성된 Secret으로 내부에는 tls.crttls.key 데이터를 갖고 있습니다.
Secret을 Pod에 마운트하거나, Ingress-controller 가 사용하도록 설정할 수 있습니다.

cert-manager 설치

cert-manager 역시 helm으로 간편하게 설치할 수 있습니다.
한 가지 확인할 것은 installCRDs 옵션인데, 처음 설치한다면 cert-manager의 CRD 설치를 위해 true로 설정해야 합니다. (default false로 설정되어 있습니다)
helm repo add jetstack https://charts.jetstack.io helm repo update
YAML
복사
helm install \ cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.14.4 \ --set installCRDs=true
YAML
복사
> kubectl get pods -n cert-manager NAME READY STATUS RESTARTS AGE cert-manager-6dc66985d4-tbbl5 1/1 Running 0 3m48s cert-manager-cainjector-c7d4dbdd9-524cp 1/1 Running 0 3m48s cert-manager-webhook-847d7676c9-z4qgg 1/1 Running 0 3m48s
Bash
복사

ClusterIssuer 생성

이 문서에서는 Let’s Encrypt를 CA로써 사용합니다.
Let’s Encrypt인증서를 무료로 발급해주는 CA(Certificate Authorities)로 무료인만큼 인증서는 90일간 유효합니다. 따라서 인증서가 만료되기 전에 주기적으로 갱신하는 작업이 필요합니다.
앞에서 설명했듯이 주기적인 인증서 갱신 작업을 cert-manager가 수행해줍니다.
https://cert-manager.io/docs/tutorials/acme/http-validation/ 튜토리얼을 따라 ClusterIssuer 를 생성해줍니다.
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: # The ACME server URL server: https://acme-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: gomgomshrimp@gmail.com # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-prod solvers: - http01: ingress: ingressClassName: nginx
YAML
복사
> kubectl get clusterissuers.cert-manager.io NAME READY AGE letsencrypt-prod True 7s
Bash
복사

Argo CD 구성 및 Ingress 변경

인증서를 생성하기 전에, 앞서 설치한 Argo CD와 Ingress의 구성을 조금 변경해야합니다.
이 과정은 필수는 아니며, 도메인 설정이나 내부 서비스에 path로 접근할지, subdomain을 사용하는지 등에 따라 달라지기 때문에 각 환경에 맞게 변경해주시면 됩니다.
저는 subdomain을 사용할 것이기 때문에, 기존에 /argocd 로 접근했던 것을 기본 설정인 / 로 다시 변경하겠습니다. Argo CD values에서 설정했던 server.insecureserver.rootpath 를 모두 제거합니다.
또한 Argo CD의 ingress 항목도 자동으로 구성되는 것보다 수동으로 만드는 것이 더 편리하기에 server.ingress.enabled: false 로 수정하여 자동 생성되지 않도록 변경하고 Argo CD를 재설치합니다.

TLS를 설정한 Ingress 생성

저는 구성된 LoadBalancer를 통해 외부 트래픽을 받고, Ingress를 통해 트래픽을 쿠버네티스 클러스터 내부로 라우팅하고 있습니다.
그렇기에 TLS 인증서를 Ingress에서 구성하고 Pod에는 마운트하지 않아도 됩니다.
TLS를 구성한 Ingress는 다음과 같이 작성합니다.
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-tls-argocd namespace: argocd annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" cert-manager.io/acme-challenge-type: "http01" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" spec: ingressClassName: nginx tls: - secretName: tls-gomgomshrimp-com hosts: - argocd.gomgomshrimp.com rules: - host: argocd.gomgomshrimp.com http: paths: - path: / pathType: Prefix backend: service: name: argocd-server port: number: 443
YAML
복사
눈여겨봐야할 설정 값들은 다음과 같습니다.
annotations
cert-manager.io/cluster-issuer: "letsencrypt-prod" : 사용할 ClusterIssuer 이름을 명시합니다.
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" : Argo CD에서 https 접속을 위한 설정
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" : Argo CD에서 https 접속을 위한 설정
spec.tls
secretName : Certificate에 의해 생성될 Secret 이름이며, Ingress에서 사용합니다.
hosts : 사용할 도메인을 리스트로 나열합니다.
이제 위 ingress 리소스를 생성하고, cert-manager에 의해 자동으로 Certificate 가 생성되고 Secret 이 생성된 것을 확인해보겠습니다.
> kubectl get certificate -n argocd -o wide NAME READY SECRET ISSUER STATUS AGE tls-gomgomshrimp-com True tls-gomgomshrimp-com letsencrypt-prod Certificate is up to date and has not expired 27s > kubectl get secret -n argocd tls-gomgomshrimp-com NAME TYPE DATA AGE tls-gomgomshrimp-com kubernetes.io/tls 2 23s
Bash
복사
참고로, 저처럼 테스트한다고 인증서 발급을 계속 요청하면, Let’s Encrypt의 Rate Limit에 걸려서 인증서 발급을 받을 수 없으니 참고하세요.
Failed to create Order: 429 urn:ietf:params:acme:error:rateLimited: Error creating new order :: too many certificates (5)
Bash
복사

테스트

실제로 argocd.gomgomshrimp.com 에 접속하여 인증서 문제가 없는지 살펴보면, 아래와 같이 접속도 잘 되고, 인증서 정보를 확인하니 제대로 발급되어 있는 것을 확인할 수 있습니다.

마치며

이번 포스팅에서는 쿠버네티스 환경에서 HTTPS 접속을 하기 위해 cert-manager 를 구성하고 무료로 인증서를 발급 받아 Ingress에 설정하는 방법까지 테스트해보았습니다.
뉴비일 때는 마냥 어려웠던 인증서를 생성하고 연결하는 작업이 이렇게 쉽고, 심지어 알아서 자동 갱신까지 해주다니… 놀라울 따름입니다.