본문 바로가기

AWS EKS 실습/EKS Beginner

Exposing a Service

Kuberenetes는 Pod가 어떤 호스트에 연결되든 관계없이 포드가 다른 Pod와 통신할 수 있다고 가정한다.

 

clarkshim.tistory.com/59

 

외부 서비스에서 외부 클라이언트로...(Nodeport, Loadbalancer, Ingress)

[서비스가 외부에서 액세스 가능하게 하는 방법] ㅇ. NodePort 서비스 타입으로 설정하기  - NodePort 서비스의 각 클러스터 노드는 노드 자체 이름을 통해 port를 열고 port에서 발생한 트래픽을 서비

clarkshim.tistory.com

모든 Pod에 고유한 cluster-private-IP 주소를 제공하므로 포드간에 링크를 명시적으로 만들거나 컨테이너 포트를 호스트포트에 매핑할 필요가 없다. 즉 Pod내의 컨테이너는 모두 localhost에서 서로의 포트에 도달할 수 있으며 클러스터의 모든 Pod는 NAT 없이 서로 볼 수 있다.

 

ㅇ Cluster에 Pod 노출

 

[nginx Deployment 생성]

cat <<EoF > ~/environment/run-my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  namespace: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EoF

[my-nginx namespace 생성 및 Deployment 생성]

# create the namespace
kubectl create ns my-nginx
# create the nginx deployment with 2 replicas
kubectl -n my-nginx apply -f ~/environment/run-my-nginx.yaml

kubectl -n my-nginx get pods -o wide

 

적용된 결과는 아래와 같이 나올 수 있다.

NAME                        READY   STATUS    RESTARTS   AGE   IP              NODE                                                NOMINATED NODE   READINESS GATES
my-nginx-75897978cd-cvr2c   1/1     Running   0          16s   192.168.76.10   ip-192-168-71-49.ap-northeast-2.compute.internal    <none>           <none>
my-nginx-75897978cd-gn55m   1/1     Running   0          16s   192.168.53.76   ip-192-168-40-208.ap-northeast-2.compute.internal   <none>           <none>

여기서 Pod의 IP는 192.168.76.10, 192.168.53.76이며 이 포드가 올라간 Node의 IP는 192.168.71.49, 192.168.40.288이다.

 

ㅇ Creating a Service

 

Kubernetes Service는 Cluster에서 실행되는 논리적 Pod 집합을 정의하는 추상화 객체로, 모두 동일한 기능을 제공한다. 생성 될 때 각 Service는 고유한 IP 주소(clusterIP라고 함)가 할당된다. 이 주소는 서비스의 수명과 관련이 있으며 서비스가 활성화 된 동안에는 변경되지 않는다.

Pod는 Service와 통신하도록 구성할 수 있으며 Service에 대한 통신이 Service 구성원인 일부 Pod에 자동으로 LoadBalancing 된다는 것을 알 수 있다.

 

kubectl expose를 사용하여 2개의 nginx replica를 위해 하나의 서비스를 생성할 수 있다.

 

kubectl -n my-nginx expose deployment/my-nginx

 

[my-nginx 서비스 정보 확인]

kubectl -n my-nginx get svc my-nginx
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.100.46.213   <none>        80/TCP    83s

 

[describe 명령어로 상세 내용 확인]

kubectl -n my-nginx describe svc my-nginx
Name:              my-nginx
Namespace:         my-nginx
Labels:            <none>
Annotations:       <none>
Selector:          run=my-nginx
Type:              ClusterIP
IP:                10.100.46.213
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         192.168.53.76:80,192.168.76.10:80
Session Affinity:  None
Events:            <none>

 

MyClusterIP라는 변수에 my-nginx Service IP 할당

# Create a variable set with the my-nginx service IP
export MyClusterIP=$(kubectl -n my-nginx get svc my-nginx -ojsonpath='{.spec.clusterIP}')

 

load-generator Deployment 생성 (MyClusterIP 변수도 컨테이너 내부에 설정) Pod + Container 대화형 쉘을 갖고 옴

# Create a new deployment and allocate a TTY for the container in the pod
kubectl -n my-nginx run -i --tty load-generator --env="MyClusterIP=${MyClusterIP}" --image=busybox /bin/sh

container에서 log out을 위해 exit

ㅁ Service에 액세스

 

kubernetes는 서비스를 찾는 두가지 기본 모드를 지원한다.

- 환경 변수

- DNS

 

환경 변수는 기본적으로 제공하지만 DNS는 CoreDNS를 요구 한다. EKS cluster의 경우 만들어질 때 자동적으로 cluster에 add-on 된다.

 

ㅇ 환경 변수 (Environment Variable)

 

한 Pod가 한 Node에서 구동될 때 kubelet은 각 active Service를 환경변수에 추가한다. 이것은 문제를 야기한다. 

 

nginx pods 중 하나의 정보를 확인해 본다.

export mypod=$(kubectl -n my-nginx get pods -l run=my-nginx -o jsonpath='{.items[0].metadata.name}')

kubectl -n my-nginx exec ${mypod} -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.100.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

SERVICE에 대한 내용이 없다. 이것은 서비스 이전에 Replica를 생성했기 때문이다.

 

이 작업의 또 다른 단점은 스케줄러가 동일한 Pod를 동일한 머신에 배치할 수 있다는 것이다. 이로 인해 서비스가 종료되면 전체 서비스가 중단된다. 2개의 Pod를 종료하고 Deployment가 다시 생성할 때까지 기다리면 정상적으로 Service가 환경변수에 등록된다.

 

이번에는 replica 설정 이전에 Service가 있으면 정상적으로 연결되어 있는 상태를 볼 수 있다.

 

kubectl -n my-nginx rollout restart deployment my-nginx
kubectl -n my-nginx get pods -l run=my-nginx -o wide

위에서 조회한 niginx pods 정보를 다시 조회해 본다.

export mypod=$(kubectl -n my-nginx get pods -l run=my-nginx -o jsonpath='{.items[0].metadata.name}')

kubectl -n my-nginx exec ${mypod} -- printenv | grep SERVICE
MY_NGINX_SERVICE_HOST=10.100.46.213
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.100.0.1
KUBERNETES_SERVICE_PORT=443

이번에는 MY_NGINX_SERVICE_HOST에 nginx Service IP가 환경변수에 할당되어 있음을 볼 수 있다.

 

ㅇ DNS

 

Kubernetes는 다른 서비스에 dns name을 자동으로 할당하는 DNS Cluster 추가 서비스를 제공한다. 이미 CoreDNS가 작동되고 있다면 다음 명령어를 수행하여 체크해보자

 

kubectl get service -n kube-system -l k8s-app=kube-dns
NAME       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.100.0.10   <none>        53/UDP,53/TCP   9d

 

클러스터의 모든 포드에서 표준 방법(DNS 이름)으로 조회하기 위해 다음과 같이 CURL 애플리케이션을 실행 해본다.

kubectl -n my-nginx run curl --image=radial/busyboxplus:curl -i --tty

 

 

컨테이너 안에서 nslookup 정보를 조회한다.

nslookup my-nginx
Server:    10.100.0.10
Address 1: 10.100.0.10 kube-dns.kube-system.svc.cluster.local

Name:      my-nginx
Address 1: 10.100.46.213 my-nginx.my-nginx.svc.cluster.local

 

컨테이너에서 로그 아웃하려면 exit을 입력

 

ㅁ Exposing the Service

 

일부 애플리케이션을 외부 IP address로 노출시키기 원하고 싶을 경우가 있다. 쿠버네티스는 두가지 방식을 지원한다.

NodePort와 LoadBalancer이다.

 

kubectl -n my-nginx get svc my-nginx

 

Output

NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.100.46.213   <none>        80/TCP    168m

 

현재 Service는 External-IP가 할당되어 있지 않다. 만약 지금 Cluster IP로부터 my-nignx Service의 타입으로 Load Blancer로 Cloud Load Balancer를 사용하여 Service를 패치하려고 한다면 다음과 같이 할 수 있다.

 

kubectl -n my-nginx patch svc my-nginx -p '{"spec": {"type": "LoadBalancer"}}'

 

patch 된 결과를 보면

kubectl -n my-nginx get svc my-nginx

 

Output

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)        AGE
my-nginx   LoadBalancer   10.100.46.213   a0a3addd48a1f4a29a26edace2eb806e-1647125997.ap-northeast-2.elb.amazonaws.com   80:30578/TCP   172m

 

접근 가능해지면 다음과 같이 테스트 해본다.

export loadbalancer=$(kubectl -n my-nginx get svc my-nginx -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')

curl -k -s http://${loadbalancer} | grep title

 

만약 Load Balancer 이름이 너무 길어서 kubectl get svc 출력에 맞지 않아 정확히 이름을 보기 위해서는 kubectl describe service my-nginx를 수행해 보자

 

kubectl -n my-nginx describe service my-nginx | grep Ingress

 

Output

LoadBalancer Ingress:     a0a3addd48a1f4a29a26edace2eb806e-1647125997.ap-northeast-2.elb.amazonaws.com

 

ㅁ Ingress

 

Waht is Ingress?

 

 

Ingress는 외부의 HTTP 및 HTTPS 를 Cluster 외부로부터 Cluster내의 Service로 route한다. Traffic Routing은 ingress resource에 정의된 rule에 의해 control 된다.

 

 

Ingress는 Service에 외부에서 연결 가능한 URL을 제공하고, Traffic 부하를 분산하고, SSL/TLS를 종료하고 ,이름 기반 가상 호스트를 제공하도록 구성될 수 있다. Ingress Controller는 일반적으로 Load Balancer를 사용하여 Ingress를 수행해야 하지만 Traffic 처리를 돕기 위해 Edge Router 또는 추가 Front End를 구성할 수도 있다.

 

Ingress는 임의의 Port 또는 Protocol을 노출하지 않는다. HTTP 및 HTTPS 이외의 서비스를 인터넷에 노출하는 경우 일반적으로 Service.Type=NodePort 또는 Service.Type=LoadBalancer를 사용한다.

 

Ingress를 처리하려면 Ignress Controller가 있어야 한다. Ingress Resourc를 만드는 것만으로는 효과가 없다. AWS Load Balancer Controller와 같은 Ingress Controller를 배포해야 하며 Kong이나 nginX와 같은 다른 Ingress Controller중 하나를 사용할 수 있다.

 

이상적으로 모든 Ingress Controller는 reference spec에 부합해야 하나, 실제 다양한 Ingress Controller는 약간 다르게 작동한다.

 

ㅁ Ingress Resource

 

ingress-nginx에 대한 minimal ingress resource의 예는 다음과 같다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

모든 다른 kubernetes resource들과 같이 ingress도 apiVersion, kind 그리고 metadata 필드가 필요하며 Ingress 객체의 이름은 유효한 DNS Subdomain name이어야 한다. 

Config file들과 작업되는 일반적인 정보는 애플리케이션 배포, 컨테이너 구성, Resource 관리 내용을 참고한다. Ingress는 자주 Annotation을 사용하여 Ingress controller에 따라 일부 옵션을 구성한다. 그 예로 rewrite-targe annotation이 있다. 다른 Ingress controller는 또 다른 Annotation을 지원한다. 어떤 Annotation이 지원되는지 알아보려면 선택한 Ingress Controller에 대한 문서를 검토한다.

 

Ingress spec은 load balancer 또는 proxy server를 구성하기 위해 필요한 모든 정보들을 갖는다. 가장 중요한 것은 모든 수신 요청과 일치하는 rule 목록이 포함되어 있어야 한다. Ingress 리소스는 HTTP traffic을 전달하기위한 rule만 지원한다.

각 http rule은 다음과 같은 정보를 포함한다.

 

- Optional host: 위의 예에서는 어떤 호스트도 지정되지 않았으므로 지정된 IP 주소를 통해 모든 Inbound HTTP Traffic에 규칙이 적용된다. 만약 Host가 제공되면 규칙은 해당 호스트에 적용된다.

- 서비스 네임과 서비스포트가 정의된 연관된 백엔드가 있는 Path 리스트: Load Balancer가 트래픽을 서비스로 보내기전에 호스트와 경로가 수신 요청의 컨텐츠와 일치해야 한다.

- 백엔드는 서비스 문서에 기술된 서비스와 포트 이름의 조합이다. rule의 호스트 및 경로와 일치하는 Ingress에 대한 HTTP(및 HTTPS)요청은 나열된 백엔드로 전송된다.

 

ㅇ Default Backend

 

Default Backend는 종종 스펙의 경로와 일치하지 않는 모든 요청을 처리하는 Ingress controller에 구성된다. 

 

Rule에 없는 모든 Ingress는 단일 default backend로 모든 트래픽을 보낸다. default backend는 일반적으로 Ingress controller의 구성 옵션이며 Ingress 리소스에 지정되지 않는다.

 

Ingress 객체의 HTTP 요청과 일치하는 호스트 또는 경로가 없으면 트래픽이 기본 백엔드로 라우팅 됨.

 

kubernetes.io/docs/concepts/services-networking/ingress/

 

Ingress

FEATURE STATE: Kubernetes v1.19 [stable] An API object that manages external access to the services in a cluster, typically HTTP. Ingress may provide load balancing, SSL termination and name-based virtual hosting. Terminology For clarity, this guide define

kubernetes.io

kubernetes.io/docs/concepts/services-networking/ingress-controllers/

 

Ingress Controllers

In order for the Ingress resource to work, the cluster must have an ingress controller running. Unlike other types of controllers which run as part of the kube-controller-manager binary, Ingress controllers are not started automatically with a cluster. Use

kubernetes.io

ㅁ Ingress Controller

 

Ingress Resource가 작동하려면 Cluster 내에 Ingress Controller가 실행 중이어야 한다.

kube-controller-manager 바이너리의 일부로 실행되는 다른 유형의  controller와는 달리 Ingress Controller는 Cluster와 함께 자동으로 시작되지 않는다.

 

ㅇ AWS Load Balancer Controller

더보기

AWS ALB Ingress Controller는 AWS Load Balancer Controller로 리브랜딩 되었다.

 

AWS Load Balancer Controller는 Kubernetes Cluster용 Elastic Load Balancer를 관리하는데 도움이 되는 Controller이다.

 

Application Load Balancer를 프로비저닝하여 Kubernetes Ingress Resource를 충족하고 Network Load Balancer를 프로비저닝하여 Kubernetes Service Resource를 충족한다.

 

여기에서는 Application Load Balancer에 대해 중점적으로 설명한다.

 

AWS Elastic Load Balancing Application Load Balancer(ALB)는 여러 AZ에서 Amazon EC2 인스턴스와 여러 대상에 걸쳐 애플리케이션 Layer 7에서 수신 트래픽을 Load Balancing 하는 AWS 서비스이다.

 

ALB 는 다음 특징들을 지원한다.

 - Host 또는 경로 기반 라우팅

 - TLS(Transport Layer Security) 종료, WebSockets

 - HTTP /2

 - AWS WAF 통합

 - 통합된 Access Log 및 Health Check

 

ㅁ Deploy the AWS Load Balancer Controller

 

[AWS Load Balancer Controller Version 설정 여부 확인]

if [ ! -x ${LBC_VERSION} ]
  then
    tput setaf 2; echo '${LBC_VERSION} has been set.'
  else
    tput setaf 1;echo '${LBC_VERSION} has NOT been set.'
fi

[Helm을 통한 ALB Ingress Controller 설치]

 

설치된 버전 확인

helm version --short

 

IAM OIDC provider 생성

eksctl utils associate-iam-oidc-provider \
    --region ${AWS_REGION} \
    --cluster eks-newelite-eksctl \
    --approve

IAM policy 생성

 

AWSLoadBalancerControllerIAMPolicy policy 생성

aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json

IAM Role과 ServiceAccount 생성

eksctl create iamserviceaccount \
  --cluster eks-newelite-eksctl \
  --namespace kube-system \
  --name aws-load-balancer-controller \
  --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy \
  --override-existing-serviceaccounts \
  --approve

Install TargetGroupBinding CRDs

kubectl apply -k github.com/aws/eks-charts/stable/aws-load-balancer-controller/crds?ref=master

kubectl get crd

 

Deploy Helm Chart (eks repo로 부터 helm chart를 배포)

helm repo add eks https://aws.github.io/eks-charts

helm upgrade -i aws-load-balancer-controller \
    eks/aws-load-balancer-controller \
    -n kube-system \
    --set clusterName=eks-newelite-eksctl \
    --set serviceAccount.create=false \
    --set serviceAccount.name=aws-load-balancer-controller \
    --set image.tag="${LBC_VERSION}"

kubectl -n kube-system rollout status deployment aws-load-balancer-controller

 

ㅁ Deploy Sample Application

 

2048 Game 설치

 

curl -s https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/examples/2048/2048_full.yaml \
    | sed 's=alb.ingress.kubernetes.io/target-type: ip=alb.ingress.kubernetes.io/target-type: instance=g' \
    | kubectl apply -f -

Ingress resource가 enable 되었는지 검증

eksuser:~/environment $ kubectl get ingress/ingress-2048 -n game-2048
NAME           HOSTS   ADDRESS                                                                        PORTS   AGE
ingress-2048   *       k8s-game2048-ingress2-6277e48673-1151859031.ap-northeast-2.elb.amazonaws.com   80      55s

이 Ingress와 관련된 정보 확인

export GAME_INGRESS_NAME=$(kubectl -n game-2048 get targetgroupbindings -o jsonpath='{.items[].metadata.name}')

kubectl -n game-2048 get targetgroupbindings ${GAME_INGRESS_NAME} -o yaml

output

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  creationTimestamp: "2021-02-26T23:52:21Z"
  finalizers:
  - elbv2.k8s.aws/resources
  generation: 1
  labels:
    ingress.k8s.aws/stack-name: ingress-2048
    ingress.k8s.aws/stack-namespace: game-2048
  name: k8s-game2048-service2-ab4d2e726f
  namespace: game-2048
  resourceVersion: "2402660"
  selfLink: /apis/elbv2.k8s.aws/v1beta1/namespaces/game-2048/targetgroupbindings/k8s-game2048-service2-ab4d2e726f
  uid: 542972cb-5295-4817-9b59-8924a735cb93
spec:
  networking:
    ingress:
    - from:
      - securityGroup:
          groupID: sg-0d3eabd29e7ab7c60
      ports:
      - protocol: TCP
  serviceRef:
    name: service-2048
    port: 80
  targetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-2:221745184950:targetgroup/k8s-game2048-service2-ab4d2e726f/f28b799df73e0f53
  targetType: instance
status:
  observedGeneration: 1

몇분 뒤에 아래 커맨드를 통해 새로 배포된 2048 Game에 URL 접근

export GAME_2048=$(kubectl get ingress/ingress-2048 -n game-2048 -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

echo http://${GAME_2048}

output

http://k8s-game2048-ingress2-6277e48673-1151859031.ap-northeast-2.elb.amazonaws.com

ㅁ CLEAN UP

kubectl delete -f ~/environment/run-my-nginx.yaml
kubectl delete ns my-nginx
rm ~/environment/run-my-nginx.yaml

curl -s https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/examples/2048/2048_full.yaml \
    | sed 's=alb.ingress.kubernetes.io/target-type: ip=alb.ingress.kubernetes.io/target-type: instance=g' \
    | kubectl delete -f -

helm uninstall aws-load-balancer-controller \
    -n kube-system

kubectl delete -k github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master

eksctl delete iamserviceaccount \
    --cluster eks-newelite-eksctl \
    --name aws-load-balancer-controller \
    --namespace kube-system \
    --wait

aws iam delete-policy \
    --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy

'AWS EKS 실습 > EKS Beginner' 카테고리의 다른 글

Spot Instance로 EKS 구성  (0) 2021.02.28
Assigning Pods to Nodes  (0) 2021.02.27
EKS Network 정책 실습 (w/Calico)  (0) 2021.02.26
Security Groups For Pods  (0) 2021.02.26
Create An OIDC Identity Provider  (0) 2021.02.25