[DevOps] Frontend 개발자의 우당탕탕 반려 K8S(Kubernetes) cluster 개발기 - 2. Ingress와 모니터링 구축하기(with helm)
이전 포스팅
서론
지난 포스팅에 이어서, VKE를 통해 생성된 K8S 클러스터에 Ingress 네트워크와 모니터링 환경을 구축해보려고 한다.
이를 위해 이번에 설치할 녀석들은 helm이라는 k8s를 위한 패키지매니저를 이용하여 설치할 것이다. helm의 패키지는 Chart(차트)라고 부르며, Chart 저장소인 Repository에서 Chart를 받아와 k8s 클러스터에 구동된 인스턴스를 Release라고 부른다.
보통 helm chart는 chart의 values.yaml을 수정하여 자신의 상황에 맞게 설치하는게 일반적이며, 이번 포스팅에선 설치를 위한 values.yaml 수정법과 수정된 파일의 공유가 주된 내용을 이룰 것이다.
helm을 이용하기 위해선 사전에 클러스터를 생성하고 kube_config 파일 획득이 완료되어야 하기 때문에,
만약 아직 이를 수행하지 않았다면 이전 포스팅을 참고하여 완료하길 바란다.
오늘 설치할 주요 Chart는 다음과 같다.
설치할 Chart 마다 values.yaml를 만들것이기 때문에, 차트명으로 폴더를 하나씩 만들어서 관리하는 것을 추천한다. 덤으로 각 폴더별 README.md를 만들면 문서화도 함께 해결할 수 있다.
- Ingress를 위한 Charts
- nginx-ingress-controller
- cert-manager
- 모니터링을 위한 Charts
- metrics-server
- loki-stack
- kube-prometheus-stack
Ingress 와 TLS 구성하기 (with nginx-ingress-controller, cert-manager)
nginx-ingress-controller
ingress는 k8s 클러스터 외부에서 내부 서비스에 대한 접근을 관리하는 API 오브젝트이다. 일반적으로 HTTP와 HTTPS 등을 관리하며, 외부에서 들어온 모든 HTTP와 HTTPS 요청 트래픽은 ingress에 정의된 규칙에 따라 라우팅된다. 이로인해 L7 로드밸런싱, SSL, 리버스 프록싱 기능을 제공하게 된다.
이러한 ingress를 제어하는 여러 controller들이 있는데, 일반적으로 많이 사용되는 controller 중 하나가 nginx-ingress-controller이다. 우리는 클러스터 내에 nginx-ingress-controller를 설치하여 ingress를 구성하고, 외부의 HTTP와 HTTPS 요청을 받을 수 있도록 해볼 것이다.
여러 repository의 chart들 중, 필자는 bitnamichart의 nginx-ingress-controller를 이용할 것이다.
아래 명령어를 통해 base values.yaml을 내려받자. 하지만.. 사실 nginx-ingress-controller는 크게 건들 부분은 없다. 나중을 위해 미리 받아두었다.
$ helm show values oci://registry-1.docker.io/bitnamicharts/nginx-ingress-controller > values.yaml
이제 아래 명령을 이용하여 클러스터에 nginx-ingress-controller를 설치하자. "-n"는 k8s 클러스터 네임스페이스를 의미하는 옵션이다. 필자의 경우 "kube-system"이라는 네임스페이스에 nginx-ingress-controller를 설치하도록 했다.
$ helm install nginx-ingress-controller -n kube-system -f values.yaml oci://registry-1.docker.io/bitnamicharts/nginx-ingress-controller
helm install 명령이 정상적으로 성공했다면 lens의 Application 메뉴와 Helm > Releases 메뉴에 nginx-ingress-controller 항목이 추가된걸 볼 수 있다. Application 메뉴에 nginx-ingress-controller 항목이 나타난 직후엔 status가 pending 상태일 것이다. 이는 nginx-ingress-controller 인스턴스를 구성하는 pod들의 readness probe가 아직 성공하지 않아서 그런 것인데, 각 pod에서 readness probe가 성공하고 개별 pod의 status들이 모두 Running으로 변경되면 nginx-ingress-controller 인스턴스의 status도 Running으로 변경되니 잠시 기다려보자. 다른 Chart들을 설치할때도 마찬가지이니 참고하길 바란다.
nginx-ingress-controller가 추가되면 Lens > Network > Services 메뉴에도 nginx-ingress-controller 항목이 나타나는데, 여기서 nginx-ingress-controller의 External IP를 확인할 수 있다.
자신이 소유한 도메인들의 DNS 레코드상에 해당 External IP를 레코드를 추가하면 된다. 필자의 경우 소유한 도메인의 A 레코드 서브도메인에 와일드카드(*)를 넣어, 서브도메인에 대한 라우팅을 nginx-ingress-controller가 처리할 수 있도록 했다.
도메인을 연결한 후 임의의 서브도메인으로 접속했을때 404 Not Found 페이지가 나타난다면 정상적으로 nginx-ingress-controller가 동작하고 있는 것이다.
cert-manager
nginx-ingress-controller는 HTTP와 HTTPS 트래픽을 라우팅 해주지만, HTTPS 통신을 위한 인증서를 생성/갱신해주진 않는다.
그래서 이러한 일련의 작업을 위해 cert-manager를 이용하게 된다. cert-manager는 k8s 내부에서 사용할 수 있는 인증서를 생성하고, 인증서 갱신 기간이 되었을때 자동으로 인증서를 갱신해주는 작업을 수행한다.
cert-manager를 설치하기 위해 필자가 작성한 Chart values.yaml은 아래 링크를 참고하길 바란다.
https://github.com/skymins04/betaman-devops-infra/tree/main/kubernetes/cert-manager
cert-manager도 마찬가지로 bitnamichart를 이용할 것이다.
아래 명령어를 통해 base values.yaml을 내려받자. 생성된 values.yaml에서 한가지 수정이 필요하다.
$ helm show values oci://registry-1.docker.io/bitnamicharts/cert-manager > values.yaml
false로 되어있던 installCRDs 항목을 true로 변경해주자. 이는 cert-manager를 위한 CRD(Custom Resource Definition)들을 설치할 것인지에 대한 옵션이다. 이 옵션을 켜주지 않으면 이후에 진행할 issuer 추가 과정에서 에러가 발생하니 주의하도록 하자.
수정이 완료되었으면 아래 명령을 통해 cert-manager를 설치한다. 필자는 cert-manager를 위한 전용 네임스페이스를 생성하여 할당해주었다. kubectl create namespace {생성할_네임스페이스명} 명령을 통해 네임스페이스를 생성할 수 있다.
$ kubectl create namespace cert-manager
$ helm install cert-manager -n cert-manager -f values.yaml oci://registry-1.docker.io/bitnamicharts/cert-manager
cert-manager가 정상적으로 설치되었다면 이제 인증서 생성을 위한 Issuer를 추가할 차례이다. 위에서 설명한 것처럼 cert-manager values.yaml에 installCRDs 옵션을 true로 지정하지 않으면 Issuer 추가 과정에서 오류가 발생하니, 만약 실수로 이를 true로 지정하지 못했다면 cert-manager를 재설치 하기바란다.
필자의 경우, 네임스페이스에 관계없이 클러스터 전역에서 사용할 수 있는 Issuer를 추가하고 싶었다. 그래서 그냥 Issuer가 아닌 ClusterIssuer를 추가해줄 것이다.
새로운 yaml 파일을 생성하고 아래와 같이 코드를 작성한다(파일명은 아무렇게 작성해도 무방하다.) 만약 ClusterIssuer가 아닌 Issuer를 사용하고싶다면, kind 항목의 값을 "Issuer"로 변경하면 된다.
spec.acme.email에는 자신이 소유한 도메인의 소유자 이메일을 기입하면 된다. 추후 Application Pod ingress 설정에서 사용될 도메인의 소유자 이메인과 동일한 이메일이어야 한다.
metadata.name과 spec.acme.privateKeySecretRef.name은 원하는 이름으로 변경해도 무방하다. 다만, 추후 있을 Application Pod ingress 설정에서 해당 이름이 사용되므로 참고하길 바란다. 만약 Issuer(또는 ClusterIssuer)를 여러개 사용하는 경우, 이름이 겹치면 안되니 주의하도록 한다.
yaml 파일을 다 작성했다면 kubectl apply 명령을 통해 Issuer 추가를 적용한다.
$ kubectl apply -f ./cluster-issuer.yaml
정상적으로 Cluster Issuer가 추가되었다면 Lens의 Custom Resource > cert-manager.io > Cluster Issuer(또는 Issuer) 메뉴에 항목이 추가된 것을 확인해볼 수 있다.
이렇게 구성된 Ingress와 TLS를 통해 모니터링에 필요한 Chart들을 설치해볼 것이다.
클러스터 모니터링 구성하기(with metrics-server, loki-stack, kube-prometheus-stack)
metrics-server
metrics-server는 k8s 클러스터 전체의 리소스 사용 데이터를 수집하는 서비스이다. 각 클러스터 노드에 설치되어있는 kubelet을 통해 노드나 컨테이너의 CPU, Memory 사용량등의 리소스 사용에 대해 모니터링할 수 있다.
metrics-server는 kubernetes-sigs metrics-server repository의 metrics-server Chart를 사용할 것이다.
아래 명령어를 통해 base values.yaml를 받으면 되는데... 현재는 metrics-server는 따로 설정할 게 없어서 미래를 위해 내려받아뒀다.
앞선 챕터들과 다르게 helm repo add 명령을 통해 kubernetes-sigs metrict-server repository를 추가하는 과정이 필요하다.
별도의 설정 변경은 필요하지 않으니 바로 설치를 진행하자.
# add repository
$ helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
$ helm repo update
# get base values.yaml
$ helm show values metrics-server/metrics-server > values.yaml
# install metrict-server
$ kubectl create namespace monitoring
$ helm upgrade --install metrics-server -n monitoring -f values.yaml metrics-server/metrics-server
설치가 완료되면 아래 명령어로 정상적으로 구동중인지 확인해볼 수 있다.
# 클러스터 내 노드들의 리소스 사용량을 조회
$ kubectl top nodes
하지만 아직 metrics-server 만으론 Lens에 클러스터 매트릭이 연동되지 않는데, 이를 위해선 prometheus 설치가 필요하다. 이는 후속 챕터에서 설명하고 있다.
loki-stack
loki는 클러스터 내 여러 pod의 로그를 수집해주는 서비스이다. pod의 로그는 기본적으로 pod의 생명주기 동안에만 유지되기 깨문에, 로그를 오랫동안 보관하기 위해선 별도의 로그 집계가 필요하다.
loki-stack은 grafana repository의 loki-stack Chart를 사용할 것이다.
아래 명령어를 통해 base values.yaml를 받으면 되는데... 현재는 loki-stack은 따로 설정할 게 없어서 미래를 위해 내려받아뒀다.
helm repo add 명령을 통해 grafana repository를 추가하는 과정이 필요하다.
별도의 설정 변경은 필요하지 않으니 바로 설치를 진행하자.
# add repository
$ helm repo add grafana https://grafana.github.io/helm-charts
$ helm repo update
# get base values.yaml
$ helm show values grafana/loki-stack > values.yaml
# install metrict-server
$ kubectl create namespace monitoring
$ helm upgrade --install loki-stack -n monitoring -f values.yaml grafana/loki-stack
kube-prometheus-stack
kube-prometheus-stack이라는 Chart를 사용하여, 앞서 설치한 metrics-server와 loki-stack을 하나로 엮어줄 차례다.
kube-prometheus-stack은 prometheus를 기반으로 k8s 클러스터의 다양한 메트릭 데이터를 수집하고 이를 시계열 데이터로 생성하여 모니터링 및 경고 기능을 제공한다.
kube-prometheus-stack을 설치하기 위해 필자가 작성한 Chart values.yaml은 아래 링크를 참고하길 바란다.
https://github.com/skymins04/betaman-devops-infra/tree/main/kubernetes/kube-prometheus-stack
kube-prometheus-stack은 prometheus-community repository의 kube-prometheus-stack Chart를 이용할 것이다.
아래 명령을 통해 prometheus-community repository를 추가하고 base values.yaml을 내려 받아야 한다.
$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm repo update
$ helm show values prometheus-community/kube-prometheus-stack > values.yaml
필자의 경우, kube-prometheus-stack에 포한된 Grafana의 기능을 비활성시키고 Cloudflare Zero Trust의 Cloudflare Access를 통해 사용자 접근제어를 진행할 예정이다. 그래서 grafana 항목에 gafana.ini 항목을 추가해주었다. 여기엔 login form을 비활성하고 anonymous 사용자의 접근을 허용하는 설정이 포함되어있다. 이렇게 Grafana의 접근을 허용해도, Cloudfalre Access를 통해 사용자의 접근을 제어할 수 있기 때문에 Grafana가 접근 제어를 위해 사용자 인증을 처리하는 수고를 줄일 수 있다.
이제 외부에서 Grafana를 접근하기 위해 ingress와 tls를 설정해줘야 한다.
enabled를 true로 변경하고, ingressClassName은 nginx로 변경한다. ingressClassName을 nginx로 변경하는하는 이유는, 우리가 nginx-ingress-controller를 설치했고, nginx-ingress-controller values.yaml 내 ingressClassResource.name에 nginx-ingress-controller의 이름을 nginx로 지정했기 때문이다. Grafana의 ingressClassName을 nginx로 변경했기 때문에 Grafana는 nginx-ingress-controller를 통해 외부의 요청을 받을 수 있다. hosts와 path를 통해 grafana가 외부에 노출될 도메인과 경로를 설정할 수 있다.
tls를 적용해 HTTPS로 Grafana에 접근하기 위해선, tls 와 annotation 항목을 수정해야한다. tls에는 cert-manager를 통해 생성될 tls 인증서의 이름과 hosts를 기입해야한다. ingress.tls.hosts는 위에서 입력했던 ingress.hosts와 동일하게 기입해주면 된다.
그리고 annotation을 통해 tls 인증서를 만들 issuer를 등록해주어야 하는데, cert-manager를 설치할때 생성했던 issuer의 이름을 annotations["cert-manager.io/cluster-issuer"]에 기입해주면 된다. 추가로 annotations["kubernetes.io/tls-acme"]와 annotations["nginx.ingress.kubernetes.io/ssl-passthrough"]도 아래 이미지처럼 값을 기입해준다.
이제 kube-prometheus-stack에 loki를 연동하기 위한 설정을 추가할거다.
additionalDataSources에 Loki와 관련된 설정을 추가해준다.
이렇게 values.yaml 수정을 완료했다면 아래 명령을 통해 kube-prometheus-stack을 설치한다.
$ helm install kube-prometheus-stack -n monitoring -f values.yaml prometheus-community/kube-prometheus-stack
설치가 완료되면 Lens에서도 정상적으로 클러스터 매트릭이 연동되어 나타나고,
브라우저를 통해 아까 ingress 설정한 도메인으로 접속해보면 정상적으로 Grafana가 나타난다. 여기서 Explore로 이동해서 Loki와 Prometheus도 정상 조회 가능한 점을 확인할 수 있다.
Cloudflare Access로 Grafana 접근 제한하기
아까 우리는 이전 챕터에서 Grafana가 별도의 사용자 인증을 처리하지 않도록 설정했었다. 그대신, Cloudflare Access를 통해 사용자 인증 처리를 Cloudflare Access를 통해 처리될 수 있도록 해보려고 한다.
주의사항: Cloudflare Access를 이용하기 위해선, Grafana를 접근할 수 있는 도메인이 Cloudflare에 등록되어 있으며 Cloudflare 네임서버를 사용 및 활성화 되어있어야 합니다.
Cloudflare DNS Record 확인하기
Grafana 접속시 Cloudflare Access를 사용하기 위해선 요청들이 Cloudflare를 거치도록해야해서, Grafana와 연결되어있는 레코드를 Proxied로 설정해야 한다.
필자의 경우 와일드카드 서브도메인과 nginx-ingress-controller external ip를 가지는 A 레코드에 Cloudflare Proxy를 설정해두었기 때문에, 앞으로 별도 설정이 없어도 nginx-ingress-contoller에서 라우팅해주는 모든 요청에 대해 Cloudflare Proxy가 적용 될 것이다.
Cloudflare Configuration Rules로 특정 도메인에만 SSL/TLS encryption mode 적용하기
Cloudflare Access를 이용하기 위해선 Browser-Cloudflare, Cloudflare-Origin Server 간의 연결이 모두 SSL/TLS encryption mode가 적용되어야 한다. 하지만 우리는 일부 서브도메인(Grafana)에 대해서만 Cloudflare Access를 이용할 계획이고, SSL/TLS에서 Full mode를 선택해버리면 모든 서브도메인에 대해 적용되기 때문에 Cloudflare Configuration Rules를 이용해야한다.
Configuration Rules를 이용하면 특정 서브도메인에만 SSL/TLS encryption mode를 적용할 수 있다. 아래 화면에서 Create rule 버튼을 클릭하여 새로운 규칙을 생성한다.
Rule name은 적당이 지어주면 되고,
Custom filter expression 을 선택한뒤 Hostname이 특정 도메인일때만 이 규칙이 동작하도록 설정해준다.
만약 여러 도메인을 추가하고 싶다면 Or 버튼을 클릭하여 동일한 방법으로 도메인을 추가해주면 된다.
그리고 페이지 맨 하단으로 내리면 SSL 옵션이 있는데, "+ Add" 버튼을 클릭하고 mode를 "Full"로 설정한 뒤 Deploy 버튼을 클릭하면 된다.
Cloudflare Access Application 추가하기
Cloudflare 대시보드 로그인 후 Zero Trust > Access > Applications 메뉴에서 "+ Add an application" 버튼을 클릭한 뒤, Self Hosted를 선택한다.
Application name은 대충 "monitoring" 정도로 지어줬다. 왜냐면 Grafana 외에도 추후 추가될 자원들에 대해서도 동일한 접근 제한을 걸어주기 위함이다.
Application domain에 현재 Grafana를 접근할 수 있는 도메인을 입력하고, identify providers는 on-time PIN을 제외한 나머지 녀석들을 선택하고 next 버튼을 클릭하자.
이제 접근 정책을 정의해야하는데, 필자의 경우 등록된 email의 사용자만 접근이 가능하도록 제한하려고 한다.
policy name은 적절히 의도에 맞게 작성해주면 되고 Action은 "Allow"로 설정한다. 그리고 Include Selector에 Emails를 선택한뒤, Value에 접근을 허용할 이메일들을 작성한다.
이제 그 외 사항들은 그냥 기본값으로 놔두고 Next 버튼들만 클릭하면 된다.
이렇게 Cloudflare Access 설정이 완료되었다면 기존 Grafana 도메인으로 다시 접속해보자. 그러면 Cloudflare Access의 로그인 페이지가 나타날 것이고, 로그인을 완료하면 정상적으로 Grafana에 접속되는 점을 확인할 수 있을 것이다.