ingress-nginx
ingress-nginx
https://kubernetes.github.io/ingress-nginx/
# ingress
외부에서 내부로 들어오는 트래픽을 처리하는 클러스터의 진입점으로 ingress 를 통해 외부에서 들어온 트래픽은 해당 서비스의 cluster ip 를 통해 로드밸런싱됨
ingress-nginx 는 여러 네임스페이스에 걸친 서비스를 등록 가능
(alb 하나로 하나의 네임스페이스에 있는 서비스만 등록 가능한 alb ingress controller 와 다른 부분)
ingress 오브젝트 서비스 설정 yaml 파일의 예시
cat << EOF > bootcamp-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-bootcamp
namespace: bootcamp
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host:
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
EOF
kubectl apply -f bootcamp-ingress.yaml
========================================================================
# ingress-nginx 배포(오픈소스 버전인 것 같은데 k8s 공식 사이트에서는 이 버전을 공식적으로 안내하고 있음)
외부에서 들어오는 트래픽을 클러스터 내부로 로드밸런싱하기 위한 nginx 기반의 ingress 컨트롤러로 가장 대중적으로 사용하는 ingress controller
베어메탈로 구성된 환경에선느 기본으로 NodePort 방식으로 사용하나 별도로 외부 로드밸런서와 함께 사용하는 경우 LoadBalancer 타입으로 사용도 가능
오픈소스 버전 ingress-nginx 는 호스트 도메인을 명시하지 않아도 사용 가능
https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal
## ingress-nginx 컨트롤러 배포를 위한 옵션 두 가지
1. deployment - ingress 컨트롤러 포드 복제본 수를 동적으로 변경하고 싶은 경우 사용 / 서비스 오브젝트를 생성하여 접속
2. daemonset - 모든 워커 노드에 ingress 컨트롤러를 배치하고자 하는 경우 사용 / 자동으로 ingress 컨트롤러의 포트가 워커 노드의 동일 포트에 매핑(80/443)
## 매니페스트 파일을 통한 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml
kubectl get all -n ingress-nginx
(참고) ingress-nginx 버전 확인
POD_NAME=$(kubectl get pods --namespace ingress-nginx -l app.kubernetes.io/name=ingress-nginx -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $POD_NAME --namespace ingress-nginx -- /nginx-ingress-controller --version
## helm 을 통해 설치
### 기본값으로 Deployment 유형, LoadBalancer 타입으로 배포
(LoadBalancer 서비스 타입이 사용 가능한 퍼블릭 클라우드 환경, 혹은 metallb 구성에서 추천)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx/
helm install --name ingress-nginx --namespace ingress-nginx ingress-nginx/ingress-nginx
kubectl get all -n ingress-nginx
### DaemonSet 유형으로 배포, hostNetwork 옵션 등 일부 세팅 커스텀하여 배포
(모든 워커 노드의 80, 443 포트로 트래픽 수신, LoadBalancer 서비스 타입을 사용할 수 없는 베어메탈 환경에서 추천)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx/
helm install ingress-nginx --namespace ingress-nginx ingress-nginx/ingress-nginx --set controller.kind=DaemonSet,controller.service.type=ClusterIP,controller.hostNetwork=true
kubectl get all -n ingress-nginx
(hostNetwork=true 옵션 활성화 시 포드의 포트와 동일한 포트를 노드에서 수신하여 포드로 전달, 노드:80 -> 포드:80, 다만 노드의 기존 포트와 포트 충돌이 발생할 수 있는 점 고려하여 사용할 것)
### chart 내려받아 옵션 커스텀하여 배포
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx/
helm fetch ingress-nginx/ingress-nginx
tar zxvf ingress-nginx-*.tgz
cd ingress-nginx/
sed -i 's/type: LoadBalancer/type: ClusterIP/g' values.yaml
sed -i 's/kind: Deployment/kind: DaemonSet/g' values.yaml
sed -i 's/hostNetwork: false/hostNetwork: true/g' values.yaml
helm install ingress-nginx --namespace ingress-nginx ./
kubectl get all -n ingress-nginx
### helm 으로 배포한 nginx-ingress 삭제
helm delete --purge ingress-nginx
kubectl delete ns ingress-nginx
(참고) 토스트 클라우드에서 가이드하는 설치 메니페스트 파일
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
로드밸런서 타입 ingress-nginx 서비스 생성(토스트 클라우드 가이드)
cat << EOF > ingress-nginx-lb.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: LoadBalancer
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
EOF
(참고) 베어메탈 로드밸런서 metallb (AWS 등 클라우드 환경에서는 사용 불가)
========================================================================
# 다른 배포판 nginx-ingress 배포(nginx 공식사이트에서 배포하는 버전, sticky session 등 상용 nginx plus 에서만 지원하는 기능이 있어서 제한적이고 annotations 설정 방법이 달라 비추천)
https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/
git clone https://github.com/nginxinc/kubernetes-ingress/
cd kubernetes-ingress/deployments
git checkout v1.7.1
kubectl apply -f common/ns-and-sa.yaml
kubectl apply -f rbac/rbac.yaml
kubectl apply -f common/default-server-secret.yaml
kubectl apply -f common/nginx-config.yaml
kubectl apply -f common/vs-definition.yaml
kubectl apply -f common/vsr-definition.yaml
kubectl apply -f common/ts-definition.yaml
kubectl apply -f common/gc-definition.yaml
kubectl apply -f common/global-configuration.yaml
사용할 ingress 옵션에 따라 deployment/service or daemon-set 중 하나만 배포해도 됨
kubectl apply -f deployment/nginx-ingress.yaml
kubectl create -f service/nodeport.yaml
kubectl apply -f daemon-set/nginx-ingress.yaml
확인
kubectl get pods --namespace=nginx-ingress
aws 환경에서 LoadBalancer 서비스타입 사용하려면 추가적으로 아래 파일 배포
kubectl apply -f service/loadbalancer-aws-elb.yaml
kubectl apply -f common/nginx-config.yaml
nginx-ingress 삭제하려면
kubectl delete namespace nginx-ingress
kubectl delete clusterrole nginx-ingress
kubectl delete clusterrolebinding nginx-ingress
========================================================================
# 구성 예제
cat << EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: default
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-nginx
port:
number: 80
EOF
kubectl apply -f ingress.yaml
kubectl describe ingress example-ingress
/etc/hosts 파일에 hello-world.info 도메인과 아이피 추가해서 접속 테스트
접속 경로에 따라 다른 서비스로 트래픽을 처리하려면 아래와 같은 예시
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: default
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-nginx
port:
number: 80
- path: /apple
pathType: ImplementationSpecific
backend:
service:
name: apple-service
port:
number: 5678
- path: /banana
pathType: ImplementationSpecific
backend:
service:
name: banana-service
port:
number: 5678
kind: Pod
apiVersion: v1
metadata:
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---
kind: Service
apiVersion: v1
metadata:
name: apple-service
spec:
type: NodePort
selector:
app: apple
ports:
- port: 5678
kind: Pod
apiVersion: v1
metadata:
name: banana-app
labels:
app: banana
spec:
containers:
- name: banana-app
image: hashicorp/http-echo
args:
- "-text=banana"
---
kind: Service
apiVersion: v1
metadata:
name: banana-service
spec:
type: NodePort
selector:
app: banana
ports:
- port: 5678
(참고) 테스트로 생성한 ingress 예제(호스트에 따라 각각 2개의 서비스로 포워딩)
cat << EOF > default-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: default
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-nginx
port:
number: 80
- host: bootcamp.example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
EOF
(참고) grafana 설치 후 NodePort 방식으로 서비스 타입 변경 후 nginx-ingress 를 통해 접속하기 위해 ingress 설정 예제(namespace 지정)
cat << EOF > grafana-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana
namespace: grafana
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: grafana.example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: grafana
port:
number: 80
EOF
(참고) nginx-ingress sticky session 옵션
cat << EOF > sticky-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sticky
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "NGINX-INGRESS-SESSION"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
EOF
# nginx ingress controller 의 stub_status(커넥션 수 등 확인 페이지) 확인
포드 이름 확인
kubectl get pod -n ingress-nginx
포트포워딩
kubectl port-forward <nginx-ingress 컨트롤러 포드 이름> 80:80 -n ingress-nginx
putty 등을 통해 터널링 설정 후 브라우저에서 접속
http://localhost/stub_status
Active connections: 1
server accepts handled requests
15 15 150
Reading: 0 Writing: 1 Waiting: 0
## 부하 발생시키기
kubectl run curl -it --rm --generator=run-pod/v1 --image=radial/busyboxplus:curl
입력 후 프롬프트가 나올 때까지 엔터를 누르고 프롬프트 나오면
echo "10.106.100.2 www.example.com" >> /etc/hosts
while true; do curl www.example.com; done
# nginx ingress TLS/HTTPS 적용
인증서 생성
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${KEY_FILE} -out ${CERT_FILE} -subj "/CN=${HOST}/O=${HOST}"
예시)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ~/example.com.key -out ~/example.com.crt -subj "/CN=example.com/O=example.com"
k8s secret 생성(인증서를 사용할 service/ingress 와 동일 네임스페이스에 생성)
kubectl create secret tls ${CERT_NAME} --key ${KEY_FILE} --cert ${CERT_FILE}
예시)
kubectl create secret tls example.com --key ~/example.com.key --cert ~/example.com.crt
ingress 생성
cat << EOF > tls-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- example.com
secretName: example.com
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
EOF
(참고) 인증서를 정상적으로 적용했음에도 "Kubernetes Ingress Controller Fake Certificate" 인증서가 보여지는 경우 ingress-nginx-controller 포드의 로그를 확인할 것
kubectl logs -f ingress-nginx-controller-7b9n8 -n ingress-nginx
-> 인증서의 Common Name이 일치하지 않았던 것이 원인이었던 케이스가 있음
해당 로그
W1204 07:14:35.456548 6 controller.go:1186] SSL certificate "bootcamp/kube.example.com" does not contain a Common Name or Subject Alternative Name for server "kube.example.com": x509: certificate is not valid for any names, but wanted to match kube.example.com
## https로 리디렉션
기본적으로 nginx ingress 컨트롤러는 tls 가 활성화된 경우 301 리디렉션을 사용하여 영구 이동 시킴
80 -> 443 으로 리디렉션을 시키지 않으려면 아래와 같이 ssl-redirect 옵션을 인그레스 annotations 에 추가
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
(참고) sticky session 과 tls 적용된 ingress 파일 예제
cat << EOF > sticky-tls-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sticky-tls
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "NGINX-INGRESS-SESSION"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/ssl-redirect: "false" # delete line for https auto redirect
spec:
tls:
- hosts:
- example.com
secretName: example.com
rules:
- host: example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
EOF
# 인증서 관리/자동갱신 k8s 애드온 cert-manager
참고
https://cert-manager.io/docs/installation/kubernetes/
https://github.com/jetstack/cert-manager/
(참고) ssl 백엔드를 위한 ingress 설정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: k8s-dashboard-ingress
namespace: kube-system
annotations:
ingress.kubernetes.io/ssl-passthrough: "true"
kubernetes.io/ingress.allow-http: "false"
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/proxy-body-size: 100M
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.org/ssl-backend: kubernetes-dashboard
spec:
rules:
- http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: kubernetes-dashboard
port:
number: 443
tls:
- secretName: kubernetes-dashboard-certs
(참고) 도커 레지스트리를 위한 ingress 배포
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/docs/examples/docker-registry/ingress-without-tls.yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/docs/examples/docker-registry/ingress-with-tls.yaml
# ingress nginx 의 구성 요소
ingress-nginx-controller 포드는 ingress-nginx-controller 서비스와 ingress-nginx-controller-admission 서비스로 구성된다.
ingress-nginx-controller 서비스는 80포트와 443포트를 NodePort 방식으로 노출하여 트래픽을 처리하고,
ingress-nginx-controller-admission 서비스는 8443 포트를 ClusterIP 방식으로 노출한다.(ingress 룰을 적용하는 내부 서비스로 보인다)
# helm 을 통해 nginx-ingress 설치 시 ingress 구성 예시 출력
NOTES:
The ingress-nginx controller has been installed.
Get the application URL by running these commands:
export HTTP_NODE_PORT=32080
export HTTPS_NODE_PORT=32443
export NODE_IP=$(kubectl --namespace ingress-nginx get nodes -o jsonpath="{.items[0].status.addresses[1].address}")
echo "Visit http://$NODE_IP:$HTTP_NODE_PORT to access your application via HTTP."
echo "Visit https://$NODE_IP:$HTTPS_NODE_PORT to access your application via HTTPS."
An example Ingress that makes use of the controller:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
name: example
namespace: foo
spec:
rules:
- host: www.example.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-bootcamp
port:
number: 80
# This section is only required if TLS is to be enabled for the Ingress
tls:
- hosts:
- www.example.com
secretName: example-tls
If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:
apiVersion: v1
kind: Secret
metadata:
name: example-tls
namespace: foo
data:
tls.crt: <base64 encoded cert>
tls.key: <base64 encoded key>
type: kubernetes.io/tls
# 2048-game nginx-ingress 사용 ingress 파일 예시
cat << EOF > 2048-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "2048-ingress"
namespace: "2048-game"
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host:
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: service-2048
port:
number: 80
EOF
(참고) nginx.conf 의 특정 설정을 추가적으로 지정하고 싶을 때
❯ cat ing-test-was.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ing-test-was
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request /oauth2/auth;
error_page 401 = /oauth2/start;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
auth_request_set $groups $upstream_http_x_auth_request_groups;
auth_request_set $sid $upstream_http_x_session;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
proxy_set_header X-Groups $groups;
proxy_set_header X-Remote-SID $sid;
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
if ($auth_cookie ~* "(; .*)") {
set $auth_cookie_name_0 $auth_cookie;
set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
}
if ($auth_cookie_name_upstream_1) {
add_header Set-Cookie $auth_cookie_name_0;
add_header Set-Cookie $auth_cookie_name_1;
}
spec:
rules:
- host:
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: svc-test-was
port:
number: 8080
(참고) 특정 네임스페이스의 인그레스만 감시하도록 설정
-> 기본적으로 ingress-nginx 는 모든 네임스페이스를 감시하여 인그레스 생성 시 백엔드를 설정하지만 ingress-nginx 최초 배포 시 특정 네임스페이스만 감시하도록 설정하여 배포도 가능
https://github.com/kubernetes/ingress-nginx/issues/5758
ingress-nginx 배포 시 ClusterRole 과 Deployment 옵션에 각각 한 줄씩 추가하여 배포
# Source: ingress-nginx/templates/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
helm.sh/chart: ingress-nginx-4.0.15
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 1.1.1
app.kubernetes.io/managed-by: Helm
name: ingress-nginx
rules:
- apiGroups:
- ''
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
- namespaces
verbs:
- list
- watch
- apiGroups:
- ''
resources:
- nodes
- namespaces -> 추가
verbs:
- get
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
helm.sh/chart: ingress-nginx-4.0.15
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 1.1.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
(중략)
args:
- /nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
- --election-id=ingress-controller-leader
- --controller-class=k8s.io/ingress-nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
- --watch-namespace=sample -> 추가
파드 접속하여 프로세스 확인 시 --watch-namespace=sample 옵션 확인
/etc/nginx $ ps -ef |grep nginx-ingress-controller
1 www-data 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --publish-service=ingress-nginx/ingress-nginx-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --configmap=ingress-nginx/ingress-nginx-controller --validating-webhook=:8443 --validating-webhook-certificate=/usr/local/certificates/cert --validating-webhook-key=/usr/local/certificates/key --watch-namespace=sample
9 www-data 0:00 /nginx-ingress-controller --publish-service=ingress-nginx/ingress-nginx-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --configmap=ingress-nginx/ingress-nginx-controller --validating-webhook=:8443 --validating-webhook-certificate=/usr/local/certificates/cert --validating-webhook-key=/usr/local/certificates/key --watch-namespace=sample