
Helm으로 Ingress로 Nginx를 설정하며 HTTPS 통신을 할 수 있도록 cert-manager를 설정해보도록 하겠습니다.
아직 수정중입니다. 참고만 부탁드립니다.
$ 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 명령어로 잘 설치되었는지 확인합니다.
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
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 하였습니다.


예를 들어 네트워크가 192.168.0.0/24라면 아래처럼 작성:
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
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 부분이 비어 있을 것입니다.

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"
이후 아래와 같이 정상동작 하였습니다.
정리해보면
kubectl get svc -n ingress-nginx
➡ 여기서 External-IP가 metallb에서 뿌려준 IP로 떠야 합니다.

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를 수정하니 정상 동작하는 것을 확인하였습니다.
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
kubectl get clusterissuer letsencrypt-prod -o yaml
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 포트에서 요청을 받는다.
LoadBalancer → ingress-nginx-controller Service
LoadBalancer가 ingress-nginx-controller 서비스의 443 포트로 트래픽을 전달한다.
ingress-nginx-controller Pod
ingress-nginx-controller Pod가 이 HTTPS 트래픽을 받아서
SSL Termination(암호 해제)을 여기서 해버립니다.
그 이후, 내부 통신은 HTTP (80포트) 로 한다.
nginx Pod 안에서는 SSL 처리가 필요 없어. 그냥 HTTP로 요청 받으면 됩니다.
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"

이제 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이 생성됩니다! 🎉
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
도메인 구매한 곳(예: 가비아, 카페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 로 열어놓은 포트로 연결시켜야 한다.

단계 설명