Helm Ingress - Nginx, HTTPS 통신을 위한 cert-manager

Jake·2025년 4월 27일

Setting-Server

목록 보기
6/6

Helm으로 Ingress로 Nginx를 설정하며 HTTPS 통신을 할 수 있도록 cert-manager를 설정해보도록 하겠습니다.

아직 수정중입니다. 참고만 부탁드립니다.

Helm 설치

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh
helm version
helm repo list

helm version 명령어로 잘 설치되었는지 확인합니다.

✨ 1. metallb 설치 (Helm)

metallb Helm repo 추가

helm repo add metallb https://metallb.github.io/metallb
helm repo update

metallb 설치

helm install metallb metallb/metallb --namespace metallb-system --create-namespace

check

kubectl get namespace metallb-system
kubectl get pods -n metallb-system
kubectl get deploy -n metallb-system metallb-controller
kubectl get ds -n metallb-system metallb-speaker

위 명령어 수행 후 3분이 지났음에도 불구하고 Pending과 Init:0/3과 같이 정상적으로 실행되지 않았습니다.

이후 다시 kube-system kube-proxy 설정 변경을 아래와 같이 해주었습니다.

설정 false인지 확인

kubectl get configmap -n kube-system kube-proxy -o yaml | grep strictARP

kube-proxy 설정 편집

kubectl edit configmap -n kube-system kube-proxy

편집기가 열리면 ipvs 섹션을 찾아서 strictARP: true 설정을 추가하거나 false를 true로 변경합니다.

이후 다시 metallb-system metallb-speaker를 재시작합니다.

kubectl delete pod -n metallb-system <metallb-controller-파드-이름>
kubectl rollout restart daemonset -n metallb-system metallb-speaker

하지만 다시 에러가 발생했었습니다. 이는 pod describe 명령어를 통해 taint문제임을 확인하였습니다.

kubectl edit deployment -n metallb-system metallb-controller

spec.template.spec 섹션을 찾아서 다음과 같은 tolerations 설정을 추가합니다.

...
serviceAccountName: metallb-controller
tolerations:
- key: "node-role.kubernetes.io/control-plane"
  operator: "Exists"
  effect: "NoSchedule"
- key: "node-role.kubernetes.io/control-plane"
  operator: "Exists"
  effect: "NoExecute"

이후 성공적으로 running 하였습니다.

✨ 2. metallb IP Address Pool 설정

예를 들어 네트워크가 192.168.0.0/24라면 아래처럼 작성:

metallb-config.yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.0.240-192.168.0.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2adv
  namespace: metallb-system
spec: {}

적용하는 명령어

kubectl apply -f metallb-config.yaml

check

kubectl get ipaddresspool -n metallb-system first-pool -o yaml

생성한 first-pool IPAddressPool의 설정 내용을 YAML 형식으로 자세히 확인합니다. addresses 섹션에 설정한 IP 범위가 정확히 반영되었는지 확인합니다.

kubectl get l2advertisement -n metallb-system l2adv -o yaml: 생성한 l2adv L2Advertisement의 설정 내용을 YAML 형식으로 확인합니다. 특별한 설정이 없다면 spec 부분이 비어 있을 것입니다.

✨ 3. ingress-nginx 설치 (Helm)

Ingress Controller를 LoadBalancer 타입으로 설치.

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

ingress-nginx 설치

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.type=LoadBalancer \
  --set controller.service.externalTrafficPolicy=Cluster

그런데 위와 같이 진행 시 현재 master node이므로 taint 관련 에러가 발생하였습니다. 권한이 없으므로 정상 할당이 되지 않았습니다. 그래서 기존 내용을 delete하고 다시 upgrade를 해주었습니다.

ingress-nginx 삭제 명령어

helm uninstall ingress-nginx -n ingress-nginx

권한 수정 시도 (taint 변경)

kubectl edit job -n ingress-nginx ingress-nginx-admission-create

하지만 아래 에러가 발생

  • spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"ingress-nginx-admission-create
    ...
    field is immutable

즉 Kubernetes API 서버가 Job 객체의 spec.template 필드를 변경하려고 시도했지만, 해당 필드는 immutable (불변) 속성을 가지고 있어 변경이 허용되지 않았음을 의미합니다.

그래서 아래와 같이 삭제 후 변수를 주어 다시 upgrade를 진행하였습니다.

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.type=LoadBalancer \
  --set controller.service.externalTrafficPolicy=Local \
  --set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/hostname"=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}') \
  --set controller.admissionWebhooks.patch.tolerations[0].key="node-role.kubernetes.io/control-plane" \
  --set controller.admissionWebhooks.patch.tolerations[0].operator="Exists" \
  --set controller.admissionWebhooks.patch.tolerations[0].effect="NoSchedule" \
  --set controller.admissionWebhooks.patch.tolerations[1].key="node-role.kubernetes.io/control-plane" \
  --set controller.admissionWebhooks.patch.tolerations[1].operator="Exists" \
  --set controller.admissionWebhooks.patch.tolerations[1].effect="NoExecute"

그러니 이후 정상적으로 service가 실행되었습니다.

하지만 pod는 여전히 pending 상태였습니다. pod의 경우 taint를 수정해주는 것으로 정상동작하였습니다.

kubectl edit deployment -n ingress-nginx ingress-nginx-controller

kubectl edit deployment -n ingress-nginx ingress-nginx-controller
편집기가 열리면 spec.template.spec 섹션을 찾아서 다음과 같은 tolerations 설정을 추가합니다.

spec:
  template:
    spec:
      # ... (기존 컨테이너 정의 등) ...
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoExecute"

이후 아래와 같이 정상동작 하였습니다.

정리해보면

  1. ingress-nginx install시 taint 설정을 해준다.
  2. nginx pod에 대해서 taint를 설정해준다.

check

kubectl get svc -n ingress-nginx

➡ 여기서 External-IP가 metallb에서 뿌려준 IP로 떠야 합니다.

✨ 4. Cert-Manager 설치 (Helm)

SSL 인증서를 자동으로 발급/갱신하는 cert-manager를 설치합니다.

cert-manager Helm repo 추가

helm repo add jetstack https://charts.jetstack.io
helm repo update

cert-manager 설치

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager --create-namespace \
  --set installCRDs=true

위를 하니 아래 note 발생

NOTES:
⚠️  WARNING: `installCRDs` is deprecated, use `crds.enabled` instead.
cert-manager v1.17.2 has been deployed successfully!
kubectl get pods -n cert-manager

위 내용도 모두 Pending이 발생하였습니다. 찾아보니 이도 taint 관련 문제였습니다. 아래와 같이 edit을 통해 수정하였습니다.

kubectl edit deployment -n cert-manager cert-manager
kubectl edit deployment -n cert-manager cert-manager-cainjector
kubectl edit deployment -n cert-manager cert-manager-webhook
spec:
  template:
    spec:
      # ... (기존 컨테이너 정의 등) ...
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoExecute"
kubectl get pods -n cert-manager

➡ cert-manager 관련 3개 pod (cert-manager, cert-manager-webhook, cert-manager-cainjector)가 Running 떠야 합니다.

이후 아래 명령어를 통해 status를 확인하였습니다.

kubectl get namespace cert-manager
kubectl get pods -n cert-manager
kubectl get crds certificates.cert-manager.io
kubectl get crds issuers.cert-manager.io
kubectl get crds clusterissuers.cert-manager.io
kubectl get crds certificaterequests.cert-manager.io

위와 같이 taint를 수정하니 정상 동작하는 것을 확인하였습니다.

✨ 5. ClusterIssuer 생성 (Let's Encrypt)

cert-manager가 Let's Encrypt 서버에 접근해서 SSL 인증서를 발급받게 만들자.

ClusterIssuer.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your@email.com # 여기에 본인 이메일
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx

적용

kubectl apply -f clusterissuer.yaml

check

kubectl get clusterissuer letsencrypt-prod -o yaml

✨ 6. nginx 대표 서비스 준비

nginx 웹서버를 하나 배포합니다.

nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:stable
        ports:
        - containerPort: 80
        - containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80

적용

kubectl apply -f nginx.yaml

위 내용에서 외부와 https로 통신하는데 nginx Pod에서 443 포트를 열지 않아도 되는것은

흐름을 한 번 따라가 보면
1. 클라이언트 → (인터넷) → LoadBalancer
사용자가 https://jamoai.app 로 접속하면

외부 LoadBalancer (MetalLB IP 등)가 443 포트에서 요청을 받는다.

  1. LoadBalancer → ingress-nginx-controller Service
    LoadBalancer가 ingress-nginx-controller 서비스의 443 포트로 트래픽을 전달한다.

  2. ingress-nginx-controller Pod
    ingress-nginx-controller Pod가 이 HTTPS 트래픽을 받아서

SSL Termination(암호 해제)을 여기서 해버립니다.

그 이후, 내부 통신은 HTTP (80포트) 로 한다.

  1. nginx Pod
    즉, 실제 너가 띄우는 "nginx 서버"는 내부에서는 HTTP(80) 으로 통신하는 것입니다.

nginx Pod 안에서는 SSL 처리가 필요 없어. 그냥 HTTP로 요청 받으면 됩니다.

check

kubectl get deploy -n default nginx-deployment
kubectl get pods -n default -l app=nginx
kubectl get svc -n default nginx-service -o yaml

체크를 해보았지만 또 다시 taint 설정으로 인해 Pending 상태였습니다. edit 명령어를 통해 수정 후 다시 체크해보니 정상동작 하였습니다.

kubectl edit deployment -n default nginx-deployment
spec:
  template:
    spec:
      # ...
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoExecute"

✨ 7. Ingress 리소스 생성 (SSL 자동 발급 포함)

이제 Ingress를 만들어서 nginx를 외부로 노출하고,
Let's Encrypt가 자동으로 인증서를 발급받게 해보겠습니다.

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jamoai-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - jamoai.app
    secretName: jamoai-tls
  rules:
  - host: jamoai.app
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
kubectl apply -f ingress.yaml

cert-manager가 자동으로 Let's Encrypt에 요청 보내고
인증서가 발급되면 jamoai-tls secret이 생성됩니다! 🎉

check

kubectl get certificate -n default
kubectl describe certificate -n default jamoai-tls
kubectl describe order -n default
kubectl logs -n cert-manager deploy/cert-manager

✨ 8. 도메인 DNS 설정

도메인 구매한 곳(예: 가비아, 카페24, GoDaddy, Namecheap)에서 다음처럼 설정해야 합니다.

Host Type Value TTL
@ A metallb 할당 IP (예: 192.168.56.241) 300
즉, jamoai.app이 metallb에서 받은 외부IP로 포워딩되게 만들면 끝이 납니다.

저의 경우 집에서 iptime을 사용하고 있어서 할당받은 ip로 수정해주었습니다.

(
지금 secret이 생성되지 않았음
집에 가서 포트포워딩을 100 -> 240으로 변경하기
외부 80번 → 내부 192.168.0.100:80

외부 443번 → 내부 192.168.0.100:443
)

100의 443으로 연결하는것이 아니라 load balancer 로 열어놓은 포트로 연결시켜야 한다.

✅ 최종 플로우 요약

단계 설명

  1. metallb 설치, IP 풀 설정
  2. ingress-nginx 설치 (LoadBalancer 타입)
  3. cert-manager 설치
  4. ClusterIssuer로 Let's Encrypt 연결
  5. nginx 서버 배포
  6. Ingress 생성해서 nginx 연결 및 인증서 발급
  7. 도메인 A레코드 연결
  8. https://jamoai.app 접속

0개의 댓글