AWS EKS 에서 Istio 와 Application Load Balancer 연결
Tags: aws, kubernetes, istio
Published: 2022-02-21 04:36:17

EKS 에서 Istio 를 설치하면 기본적으로 Classic Load Balancer (CLB) 를 사용하여 설치가 된다. 공식적으로 CLB가 discontinued 된다는 notice는 없지만 CLB는 권장되지 않는다. 결국 L4 Network Load Balancer(NLB) 또는 L7 Application Load Balancer(ALB)로 변경해야 한다. 하지만 [AWS WAF](https://aws.amazon.com/waf/) 와 같은 서비스를 사용하기 위해서는 반드시 ALB를 사용해야 한다. 여기서는 Istio 와 ALB를 연결해 본다. (NLB를 사용하는 방법은 많이 나와있지만 ALB를 사용하는 방법은 찾기 힘들다...)
https://rtfm.co.ua/en/istio-external-aws-application-loadbalancer-and-istio-ingress-gateway/ 를 참고하였다.

## 변경되는 Architecture
전체적인 architecture는 이렇게 변경된다. 기존 istio ingress gateway의 service type 은 LoadBalancer 에서 NodePort로 변경하여 기존 CLB가 삭제되고, 대신 Kubernetes 의 Ingress resource를 통해 ALB를 생성한다. 그리고 ALB에서 TLS termination 을 하고 모든 request를 istio ingress gateway pod로 보내고 istio ingress gateway가 application service로 routing 을 하게 된다. 

---
## ACM 을 통한 TLS 인증서 설치
첫째, TLS 인증서를 AWS ACM 에 import 한다. 
https://docs.aws.amazon.com/acm/latest/userguide/import-certificate-api-cli.html

---
## AWS Load Balancer Controller 설치
ALB를 사용하기 위해서는 AWS Load Balancer Controller가 필요하다. Bastion 서버에서 아래 순서대로 설치를 진행한다. 
https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html

### IAM Policy 생성
AWS Load Balancer Controller를 위한 IAM Policy를 다운로드한다. 
```bash
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.3.1/docs/install/iam_policy.json
```

IAM policy를 생성한다. 
```bash
$ aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json
```

### IAM Role 및 Kubernetes의 Service Account 생성
위에서 생성된 IAM Policy를 이용하여 아래와 같이 생성한다. 여기서 `attach-policy-arn` 을 위에서 생성한 policy를 넣는다.
```bash
eksctl create iamserviceaccount \
  --cluster=eks-demo \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --attach-policy-arn=arn:aws:iam::931639357206:policy/AWSLoadBalancerControllerIAMPolicy \
  --override-existing-serviceaccounts \
  --approve
```

### Helm 설치 및 repository 추가
AWS Load Balancer Controller를 Helm을 통해 설치하기 때문에 Helm도 설치하고 `eks-charts` repository도 추가한다.
```bash
$ sudo snap install helm --classic
$ helm repo add eks https://aws.github.io/eks-charts
$ helm repo update
```

### AWS Load Balancer Controller 설치
이제 AWS Load Balancer Controller 를 설치한다. 
```bash
$ helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=eks-demo \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set image.repository=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-load-balancer-controller
```

controller 가 제대로 설치되었는지 확인한다. 
```bash
kubectl get deployment -n kube-system aws-load-balancer-controller
```

---
## Istio 설정 변경
이제 Istio 의 ingress gateway의 service type을 NodePort로 변경하고 ALB가 istio ingreas gateway로 health check 을 할 수 있도록 관련 정보를 넣어준다. 

### Health check 을 위한 Port number 확인
우선 ALB 가 health check을 위해 사용할 istio ingress gateway 의 health check 관련 nodePort number를 알아본다. 
```bash
$ kubectl get service istio-ingressgateway -n istio-system -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}'
```
얻어진 Port number를 아래에서 사용한다. 

### Istio 설정 업데이트
아래와 같이 `istio-operator.yaml` 를 업데이트 한다. 
```yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
  name: istiocontrolplane
spec:
  profile: default
  components:
    egressGateways:
    - name: istio-egressgateway
      enabled: true
      k8s:
        hpaSpec:
          minReplicas: 2
    ingressGateways:
    - name: istio-ingressgateway
      enabled: true
      k8s:
        hpaSpec:
          minReplicas: 2
        service:
          type: NodePort # ingress gateway 의 NodePort 사용
        serviceAnnotations:  # Health check 관련 정보
          alb.ingress.kubernetes.io/healthcheck-path: /healthz/ready
          alb.ingress.kubernetes.io/healthcheck-port: "32197" # 위에서 얻은 port number를 사용
    pilot:
      enabled: true
      k8s:
        hpaSpec:
          minReplicas: 2
  meshConfig:
    enableTracing: true
    defaultConfig:
      holdApplicationUntilProxyStarts: true
    accessLogFile: /dev/stdout
    outboundTrafficPolicy:
      mode: REGISTRY_ONLY
```
* Istio ingress gateway 의 service type을 NodePort로 설정한다.
* 전 단계에서 얻은 port number를 healthcheck-port 에 넣어 health check 관련 정보를 istio ingress gateway 에 annotation 으로 저장한다.

업데이트된 정보를 Istio 에 적용한다. 
```bash
$ istioctl install -f istio-operator.yaml
```

여기까지 하면 기존 ingress gateway의 service type을 기존 `LoadBalancer` 에서 `NodePort`로 변경하였기 때문에 CLB (Classic Load Balancer)는 삭제된다. 

---
## ALB 생성
이제 마지막으로 Kubernetes 의 Ingress를 통해 ALB를 생성한다. (AWS Load Balancer Controller는 Kubernetes 의 Ingress 를 통해 ALB 를 생성한다) 
아래와 같이 `kube-ingress.yaml` 파일을 생성한다. 
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-alb
  namespace: istio-system
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:ap-northeast-2:93163~~~~"
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
spec:
  rules:
  - http:
      paths:
        - path: /*
          backend:
            serviceName: ssl-redirect
            servicePort: use-annotation
        - path: /*
          backend:
            serviceName: istio-ingressgateway
            servicePort: 80
```
* certificate-arn 에는 AWS ACM 에 생성한 TLS certificate의 ARN을 입력한다. 

아래와 같이 Ingress를 생성하여 ALB 생성을 완료한다. 
```bash
$ kubectl apply -f kube-ingress.yaml
```

이렇게 기존 CLB를 삭제하고 ALB 로 대체하면서 TLS Termination 은 ALB 에서 일어나고 ALB는 `istio-system` namespace 에 배포되어 있는 `istio-ingressgateway` 로 request를 보내게 되고 `istio-ingressgateway`는 비로서 서비스로 라우팅을 하게 된다. 

---

여기까지 하면 EKS 에 상용에서 사용가능한 Kubernetes Cluster 생성 및 ALB를 통한 TLS Termination, istio ingress gateway의 라우팅이 가능해 진다. 결국 AWS의 [WAF](https://aws.amazon.com/waf/)  와 같이 ALB가 필수인 서비스를 사용할 수 있다.