Amazon EKS 클러스터의 여러 Kubernetes 서비스에 대한 외부 액세스를 제공하려면 어떻게 해야 하나요?
최종 업데이트 날짜: 2022년 9월 9일
Amazon Elastic Kubernetes Service(Amazon EKS) 클러스터에서 여러 Kubernetes 서비스에 대한 외부 액세스를 제공하려고 합니다.
간략한 설명
Kubernetes용 NGINX 인그레스 컨트롤러 또는 AWS Load Balancer Controller를 사용하여 Amazon EKS 클러스터의 여러 Kubernetes 서비스에 대한 외부 액세스를 제공합니다. NGINX 인그레스 컨트롤러는 주로 NGINX에 의해 유지 관리됩니다. NGINX 인그레스 컨트롤러와 관련된 문제를 확인하려면 GitHub 웹 사이트의 문제 목록을 참조하세요. AWS Load Balancer Controller는 Amazon Web Services(AWS)에서 유지 관리합니다. AWS Load Balancer Controller 관련 문제를 확인하려면 GitHub 웹 사이트의 문제 목록을 참조하세요.
중요: 인그레스 컨트롤러 및 Kubernetes 웹 사이트의 IngressClass는 Kubernetes 웹 사이트의 Ingress와 다릅니다. Ingress는 클러스터 외부에서 클러스터 내 서비스로 HTTP 및 HTTPS 경로를 노출하는 Kubernetes 리소스입니다. 인그레스 컨트롤러는 보통 로드 밸런서를 사용하여 Ingress를 이행합니다. 인그레스 컨트롤러 없이는 Ingress를 사용할 수 없습니다. IngressClass는 Ingress 객체 요청을 이행하는 데 사용할 인그레스 컨트롤러를 식별하는 데 사용됩니다.
전제 조건: AWS Load Balancer Controller를 설치합니다. AWS Load Balancer Controller를 사용하여 Amazon EKS에서 LoadBalancer 유형 서비스 객체에 대한 Network Load Balancer를 생성하고 관리하는 것이 가장 좋습니다.
해결 방법
다음 해결 방법은 Kubernetes GitHub 웹사이트의 kubernetes/ingress-nginx 인그레스 컨트롤러를 사용하는 것입니다. 공개적으로 사용할 수 있는 다른 인그레스 컨트롤러는 NGINX GitHub 웹사이트의 nginxinc/kubernetes-ingress입니다.
Kubernetes용 NGINX 인그레스 컨트롤러 배포
전송 제어 프로토콜(TCP) 또는 전송 계층 보안(TLS)으로 Kubernetes용 NGINX 인그레스 컨트롤러를 배포할 수 있습니다.
참고: 다음 해결 방법은 Amazon EKS 버전 1.22, NGINX 인그레스 컨트롤러 버전 1.3.0 및 AWS Load Balancer Controller 버전 2.4.3에서 테스트되었습니다.
(옵션 1) Network Load Balancer에서 TCP를 사용하는 NGINX 인그레스 컨트롤러
1. 다음 Kubernetes 객체를 배포하기 위해 YAML 파일을 가져옵니다. namespace, serviceaccounts, configmap, clusterroles, clusterrolebindings, roles, rolebindings, services, deployments, ingressclasses, validatingwebhookconfigurations
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/aws/deploy.yaml
2. 파일을 편집합니다. 그런 다음 ingress-nginx-controller 서비스 객체 섹션에서 모든 service.beta.kubernetes.io 주석을 다음과 같이 바꿉니다.
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
3. 매니페스트를 적용합니다.
kubectl apply -f deploy.yaml
예시 출력:
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
deployment.apps/ingress-nginx-controller created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
serviceaccount/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
(옵션 2) Network Load Balancer에서 NGINX 인그레스 컨트롤러 TLS 종료
기본적으로 이전 솔루션은 NGINX 인그레스 컨트롤러에서 TLS를 종료합니다. Network Load Balancer에서 TLS를 종료하도록 NGINX 인그레스 서비스를 구성할 수도 있습니다.
1. deploy.yaml 템플릿을 다운로드합니다.
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml
2. 파일을 편집합니다. 그런 다음 ingress-nginx-controller 서비스 객체 섹션에서 모든 service.beta.kubernetes.io 주석을 다음과 같이 바꿉니다.
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
참고: service.beta.kubernetes.io/aws-load-balancer-ssl-cert에 대한 ARN을 포함해야 합니다.
3. 파일을 편집하고 Kubernetes 클러스터에 대한 Amazon Virtual Private Cloud(VPC) CIDR을 변경합니다.
proxy-real-ip-cidr: XXX.XXX.XXX/XX
4. 매니페스트를 적용합니다.
kubectl apply -f deploy.yaml
참고: 이전 매니페스트는 ExternalTrafficPolicy를 로컬로 사용하여 소스(클라이언트) IP 주소를 보존합니다. Amazon VPC에서 사용자 지정 DHCP 이름과 함께 이 구성을 사용하면 문제가 발생합니다. 문제가 발생하지 않도록 하려면 kube-proxy에 다음 패치를 적용합니다.
kubectl edit daemonset kube-proxy -n kube-system
5. 다음 스니펫을 포함하도록 매니페스트를 편집합니다.
spec:
template:
spec:
containers:
- name: kube-proxy
command:
- kube-proxy
- --hostname-override=$(NODE_NAME)
- --v=2
- --config=/var/lib/kube-proxy-config/config
env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
배포된 리소스 확인
AWS Load Balancer Controller
명령:
kubectl get all -n kube-system --selector app.kubernetes.io/instance=aws-load-balancer-controller
예시 출력:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/aws-load-balancer-controller-85cd8965dc-ctkjt 1/1 Running 0 48m 192.168.37.36 ip-192-168-59-225.us-east-2.compute.internal none none
pod/aws-load-balancer-controller-85cd8965dc-wpwx9 1/1 Running 0 48m 192.168.53.110 ip-192-168-59-225.us-east-2.compute.internal none> none
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/aws-load-balancer-webhook-service ClusterIP 10.100.154.44 none 443/TCP 19h app.kubernetes.io/instance=aws-load-balancer-controller,app.kubernetes.io/name=aws-load-balancer-controller
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/aws-load-balancer-controller 2/2 2 2 19h aws-load-balancer-controller 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.0 app.kubernetes.io/instance=aws-load-balancer-controller,app.kubernetes.io/name=aws-load-balancer-controller
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/aws-load-balancer-controller-85cd8965dc 2 2 2 19h aws-load-balancer-controller 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.0 app.kubernetes.io/instance=aws-load-balancer-controller,app.kubernetes.io/name=aws-load-balancer-controller,pod-template-hash=85cd8965dc
NGINX 인그레스 컨트롤러
명령:
kubectl get all -n ingress-nginx --selector app.kubernetes.io/instance=ingress-nginx
예시 출력:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/ingress-nginx-controller-54d8b558d4-k4pdf 1/1 Running 0 56m 192.168.46.241 ip-192-168-59-225.us-east-2.compute.internal none none
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/ingress-nginx-controller LoadBalancer 10.100.99.129 ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80:32578/TCP,443:30724/TCP 15h app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
service/ingress-nginx-controller-admission ClusterIP 10.100.190.61 none 443/TCP 15h app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/ingress-nginx-controller 1/1 1 1 15h controller k8s.gcr.io/ingress-nginx/controller:v1.1.1@sha256:0bc88eb15f9e7f84e8e56c14fa5735aaa488b840983f87bd79b1054190e660de app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/ingress-nginx-controller-54d8b558d4 1 1 1 15h controller k8s.gcr.io/ingress-nginx/controller:v1.1.1@sha256:0bc88eb15f9e7f84e8e56c14fa5735aaa488b840983f87bd79b1054190e660de app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx,pod-template-hash=54d8b558d4
NAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTOR
job.batch/ingress-nginx-admission-create 1/1 2s 15h create k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660 controller-uid=242bdf56-de16-471d-a691-1ca1dbc10a41
job.batch/ingress-nginx-admission-patch 1/1 2s 15h patch k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660 controller-uid=a9e710d2-5001-4d40-a435-ddc8993bfe42
IngressClass
명령:
kubectl get ingressclass
예시 출력:
NAME CONTROLLER PARAMETERS AGE
alb ingress.k8s.aws/alb IngressClassParams.elbv2.k8s.aws/alb 19h
nginx k8s.io/ingress-nginx none 15h
배포 설정 테스트
참고: 다음 단계는 두 개의 마이크로서비스를 실행하는 단계입니다. 마이크로서비스는 Kubernetes를 기본 유형으로 사용하여 내부적으로 노출됩니다.
1. 배포 또는 마이크로서비스를 설정합니다. 예를 들어, hostname-app과 apache-app입니다.
hostname-app에 대한 hostname-app-svc.yaml 파일의 예:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostname-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: hostname-app
template:
metadata:
labels:
app: hostname-app
spec:
containers:
- name: hostname-app
image: k8s.gcr.io/serve_hostname:1.1
---
apiVersion: v1
kind: Service
metadata:
name: hostname-svc
namespace: default
spec:
ports:
- port: 80
targetPort: 9376
protocol: TCP
selector:
app: hostname-app
apache-app에 대한 apache-app-svc.yaml 파일의 예:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: apache-app
template:
metadata:
labels:
app: apache-app
spec:
containers:
- name: apache-app
image: httpd:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: apache-svc
namespace: default
labels:
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: apache-app
2. 구성을 적용합니다.
hostname-app:
kubectl apply -f hostname-app-svc.yaml
apache-app:
kubectl apply -f apache-app-svc.yaml
3. 리소스가 생성되었는지 확인합니다.
배포
명령:
kubectl get deployment hostname-app apache-app -n default
예시 출력:
NAME READY UP-TO-DATE AVAILABLE AGE
hostname-app 2/2 2 2 29m
apache-app 2/2 2 2 29m
서비스
명령:
kubectl get svc apache-svc hostname-svc -n default
예시 출력:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apache-svc ClusterIP 10.100.73.51 none 80/TCP 29m
hostname-svc ClusterIP 10.100.100.44 none 80/TCP 29m
NGINX 인그레스 컨트롤러 테스트
1. 명령줄에서 검색한 로드 밸런서의 DNS URL에 액세스합니다.
curl -I ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com
예시 출력:
HTTP/1.1 404 Not Found
Date: Thu, 03 Mar 2022 14:03:11 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
참고: 기본 서버는 인그레스 규칙이 정의되지 않은 모든 도메인 요청에 대해 404 Not Found 페이지를 반환합니다. 정의된 규칙에 따라 인그레스 컨트롤러는 요청이 구성과 일치하지 않는 한 지정된 백엔드 서비스로 트래픽을 전환하지 않습니다. 호스트 필드가 Ingress 객체에 대해 구성되어 있기 때문에 요청의 호스트 헤더에 동일한 호스트 이름을 제공해야 합니다. 테스트 환경에서는 curl 플래그를 사용하여 호스트 헤더를 제공합니다. 프로덕션 환경에서는 로드 밸런서 DNS 이름을 Amazon Route 53과 같은 DNS 공급자의 호스트 이름에 매핑합니다.
2. NGINX 인그레스 컨트롤러에서 제공하는 단일 로드 밸런서를 사용하여 서비스와 인터페이스하도록 인그레스를 구현합니다.
micro-ingress.yaml 예시:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: micro-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: hostname.mydomain.com
http:
paths:
- backend:
service:
name: hostname-svc
port:
number: 80
path: /
pathType: Prefix
- host: apache.mydomain.com
http:
paths:
- backend:
service:
name: apache-svc
port:
number: 80
path: /
pathType: Prefix
kubectl get ingress -n default
예시 출력:
NAME CLASS HOSTS ADDRESS PORTS AGE
micro-ingress none hostname.mydomain.com,apache.mydomain.com ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80 29m
4. 요청에 호스트 헤더를 추가합니다.
처음 구성된 도메인:
curl -i -H "Host: hostname.mydomain.com" http://aaa71bxxxxx-11xxxxx10.us-east-2.elb.amazonaws.com/
예시 출력:
HTTP/1.1 200 OK Date: Sat, 26 Mar 2022 18:50:38 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 29 Connection: keep-alive
두 번째로 구성된 도메인:
curl -i -H "Host: apache.mydomain.com" http://aaa71bxxxxx-11xxxxx10.us-east-2.elb.amazonaws.com/
예시 출력:
HTTP/1.1 200 OK
Date: Sat, 26 Mar 2022 18:51:00 GMT
Content-Type: text/html
Content-Length: 45
Connection: keep-alive
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
호스트 헤더를 추가한 후 인그레스 컨트롤러가 Ingress에 정의된 구성과 일치하는 백엔드 구성 서비스로 트래픽을 리디렉션합니다.
동일한 도메인 이름을 유지하면서 액세스한 경로를 기반으로 트래픽을 전환하려면 Ingress를 사용하여 경로 기반 라우팅을 추가해야 합니다.
예를 들어 다음과 같습니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: nginxd
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: mydomain.com
http:
paths:
- backend:
service:
name: hostname-svc
port:
number: 80
path: /hostname
pathType: Prefix
- host: mydomain.com
http:
paths:
- backend:
service:
name: apache-svc
port:
number: 80
path: /apache
pathType: Prefix
참고: 앞의 예시에서는 요청에 mydomain.com이 호스트 헤더로 있는 경우 200개의 응답만 반환됩니다. 요청은 /hostname 또는 /apache 경로에서 액세스됩니다. 다른 모든 요청의 경우 404 응답이 반환됩니다.
5. 경로 기반 라우팅이 추가되었는지 확인합니다.
명령:
kubectl get ingress -n default
출력:
NAME CLASS HOSTS ADDRESS PORTS AGE
micro-ingress none hostname.mydomain.com,apache.mydomain.com ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80 164m
path-ingress none mydomain.com,mydomain.com ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80 120m
명령:
curl -i -H "Host: mydomain.com" http://aaa71bxxxxx-11xxxxx10.us-east-2.elb.amazonaws.com/hostname
-또는-
curl -i -H "Host: mydomain.com" http://aaa71bxxxxx-11xxxxx10.us-east-2.elb.amazonaws.com/apache
AWS Load Balancer Controller를 사용하여 인그레스 테스트
1. 다음 예시 인그레스 매니페스트를 사용하여 Application Load Balancer를 시작합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: micro-ingress-alb
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- host: alb.hostname.mydomain.com
http:
paths:
- backend:
service:
name: hostname-svc
port:
number: 80
path: /
pathType: Prefix
- host: alb.apache.mydomain.com
http:
- backend:
service:
name: apache-svc
port:
number: 80
path: /
pathType: Prefix
2. Application Load Balancer가 시작되는지 확인합니다.
명령:
kubectl get ingress -n default
출력:
NAME CLASS HOSTS ADDRESS PORTS AGE
micro-ingress none hostname.mydomain.com,apache.mydomain.com ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80 164m
micro-ingress-alb none alb.hostname.mydomain.com,alb.apache.mydomain.com k8s-default-microing-8a252bde81-1907206594.us-east-2.elb.amazonaws.com 80 18m
path-ingress none mydomain.com,mydomain.com ad9bba7a8239a475297d24bd2f617782-a579e639079f8270.elb.us-east-2.amazonaws.com 80 120m
첫 번째로 구성된 도메인을 기반으로 한 요청:
curl -i -H "Host: alb.hostname.mydomain.com" http://k8s-default-microing-8a252bde81-1907206594.us-east-2.elb.amazonaws.com
예시 출력:
HTTP/1.1 200 OK Date: Sat, 26 Mar 2022 20:46:02 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 29 Connection: keep-alive
두 번째로 구성된 도메인을 기반으로 한 요청:
curl -i -H "Host: alb.apache.mydomain.com" http://k8s-default-microing-8a252bde81-1907206594.us-east-2.elb.amazonaws.com
예시 출력:
HTTP/1.1 200 OK
Date: Sat, 26 Mar 2022 20:46:14 GMT
Content-Type: text/html Content-Length: 45
Connection: keep-alive
Server: Apache/2.4.53 (Unix)
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes