시작하기 전, eksctl로 eks 클러스터를 실행해준다. 헬름도 설치해 준다.
eksctl create cluster -f myeks.yaml #클러스터 생성
eksctl get nodegroup --cluster myeks #노드그룹 확인
eksctl get fargateprofile --cluster myeks #fargate 프로파일 확인
eksctl get iamserviceaccount --cluster myeks #IAM 서비스 계정 확인
choco install kubernetes-helm #헬름 설치
쿠버네티스의 인그레스 리소스는 ALB를 사용한다. 서비스는 NLB를 사용한다. 이를 위해서 설치하는 것이 AWS Load Balancer Controller(AWS ALB Ingress Controller) 이다. AWS LB Controller를 사용하기 위해서 필요한 조건은 다음과 같다.
기존 Amazon EKS 클러스터.
클러스터에 대한 기존 AWS Identity and Access Management(IAM) OpenID Connect(OIDC).
1.21 버전 이상의 클러스터.
EKS 클러스터에 ALB Load Balancer Controller를 배포하는 방법은 다음과 같다.
- IAM 정책 생성
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json
aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document file://iam_policy.json
- IAM 역할 생성. my-cluster를 사용자 클러스터 이름으로 바꾸고 role-name은 역할의 이름으로 바꾼다. 111122223333을 계정 ID로 바꾼 다음 명령을 실행한다.
eksctl create iamserviceaccount \ --cluster=my-cluster \ --namespace=kube-system \ --name=aws-load-balancer-controller \ --role-name AmazonEKSLoadBalancerControllerRole \ --attach-policy-arn=arn:aws:iam::111122223333:policy/AWSLoadBalancerControllerIAMPolicy \ --approve
여기서는 myeks.yaml파일로 모두 설정해주었기 때문에 이 과정을 실행할 필요는 없다.
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=myeks \ #클러스터 이름
--set serviceAccount.create=false \ #SA계정을 이미 만들었으므로 false
--set serviceAccount.name=aws-load-balancer-controller #SA 이름
helm list -A #helm list 확인
kubectl get deployment -n kube-system aws-load-balancer-controller #배포 확인
NLB는 L4 계층의 로드 밸런서이다. 쿠버네티스의 서비스는 NLB를 사용한다.
쿠버네티스에서 리소스를 만들 때, IP 타겟과 인스턴스 타겟이 있다. 기본적으로 IP 타겟 구성으로 작업을 하게 된다. 서비스를 만들 때 해당 어노테이션을 붙이라는 의미이다.
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
이 두 개의 어노테이션을 붙이면 NLB가 만들어지게 된다. 기본적으로 이 두 줄을 붙여야만 네트워크 로드밸런서가 구성이 되게 된다.
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
해당 어노테이션을 붙이면 퍼블릭 서브넷에 만들어 져서 외부에 노출되게 된다. 이 어노테이션을 붙이지 않으면 외부에 노출되지 않는다.
apiVersion: v1
kind: Service
metadata:
name: myapp-svc-nlb
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: myapp-rs
external IP가 생성되어 있는 것을 확인할 수 있다. 생성이 완료된 이후 EC2 인스턴스의 로드밸런서를 확인해 보면 타입이 Network, Scheme가 internet-facing으로 되어있는 것을 확인할 수 있다.
ALB는 L7 계층의 로드 밸런서이다. 쿠버네티스의 인그레스는 ALB를 사용한다.
쿠버네티스의 인그레스 리소스를 생성할 때 다음과 같은 어노테이션을 붙인다.
kubernetes.io/ingress.class: alb
ALB 역시 인스턴스 타입과 IP타입이 있다.
인스턴스
클러스터 내의 노드를 ALB의 대상으로 등록한다. ALB에 도달하는 트래픽은 서비스를 위해 pods로 라우팅된 다음 NodePort로 프록시된다. 이것이 기본 트래픽 모드이다.
IP
pods를 ALB의 대상으로 등록한다. ALB에 도달하는 트래픽은 서비스를 위해 pods로 직접 라우팅된다. 이 트래픽 모드를 사용하려면 alb.ingress.kubernetes.io/target-type: ip 주석을 지정해야 한다. Fargate에서 대상 pods가 실행 중인 경우 IP 대상 유형이 필요하다.
alb.ingress.kubernetes.io/target-type: instance
인스턴스 타입으로 지정할 때는 다음과 같은 인스턴스를 붙인다. 또한 ALB 역시 internet-facing 어노테이션을 통해 외부에 노출시킬 수 있다. internal 타입은 외부에서 접속할 수 있다. 이들은 서비스 리소스에 NLB 어노테이션을 붙이는 것과 같이 인그레스 리소스에 어노테이션을 붙인다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ing
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance
spec:
defaultBackend:
service:
name: myapp-svc-np
port:
number: 80
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc-np
port:
number: 80
생성이 완료된 이후 EC2 인스턴스의 로드밸런서를 확인해 보면 타입이 Application, Scheme가 internet-facing으로 되어있는 것을 확인할 수 있다.
클러스터가 1.22 버전이거나 그 이전 버전이면 EBS CSI 드라이버가 필요하지 않지만 1.23 버전부터는 CSI 드라이버가 없으면 작동되지 않는다. 스토리지 클래스(gp2)를 사용해도 스토리지가 배포되지 않는 이유는 EBS CSI 드라이버가 없기 때문이다.
IAM 정책과 역할은 yaml 파일을 통해 설정되어 있으므로 따로 설정할 필요는 없다. 역할에 대한 json 파일을 상황에 맞게 수정하는 것은 한 번 꼬이면 겉잡을 수 없는 경우가 많기 때문에 eksctl create를 할 때 적용을 하는 편이 훨씬 정확하게 세팅할 수 있다.
다음 명령을 실행한다. my-cluster를 클러스터 이름으로, 111122223333을 계정 ID로, AmazonEKS_EBS_CSI_DriverRole을 이전에 생성한 IAM 역할의 이름으로 바꾼다.
eksctl create addon --name aws-ebs-csi-driver --cluster my-cluster --service-account-role-arn arn:aws:iam::111122223333:role/AmazonEKS_EBS_CSI_DriverRole --force
클러스터 이름, 계정 ID, IAM 역할의 이름은 다음과 같이 확인할 수 있다.
eksctl get iamserviceaccount --cluster myeks --name ebs-csi-controller-sa
바꾸면 다음과 같다.
eksctl create addon --name aws-ebs-csi-driver --cluster myeks --service-account-role-arn arn:aws:iam::677908651361:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-M7J7WQPJHUZH --force
eksctl get addon --cluster myeks #설치 확인
kubectl get pod -n kube-system -l "app.kubernetes.io/component=csi-driver"
#데몬셋으로 구성되어 있다.
EBS의 PVC 액세스 모드는 제한사항 때문에 ReadWriteOnce만 가능하다. 블록 장치는 보통 ReadWriteOnce만 가능하기 때문이다. Many형태는 파일 스토리지에만 가능하다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: gp3
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
#annotations:
# storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
parameters:
csi.storage.k8s.io/fstype: ext4
type: gp3
HPA를 통한 오토스케일링, top 커맨드를 통한 상태 수집에는 metric-server가 필요하다. Kubernetes metric-server는 클러스터에서 리소스 사용량 데이터의 집계자이며, 기본적으로 Amazon EKS 클러스터에 배포되어 있지 않다. 따라서 따로 배포를 해 주어야 한다.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl get deployment metrics-server -n kube-system #설치 확인
kubectl top nodes
kubectl top pods #명령 사용 확인
EC2 인스턴스에 노드 그룹을 만들게 되면 실제로 AWS 상에서 오토 스케일링 그룹으로 만들어지게 된다. DesiredCapacity라는 요청량을 지정한다. 이것이 노드의 갯수이다. DesiredCapacity를 지정하지 않으면 기본이 2이다. min size와 max size를 세팅하지 않으면 DesiredCapacity 만큼 설정된다.
오토 스케일링 그룹의 수동 스케일링은 다음 명령어로 진행된다. name에는 노드 그룹의 이름을 적는다.
eksctl scale nodegroup --cluster myeks --name mynbodes-t3 -N 3 #스케일 업
eksctl scale nodegroup --cluster myeks --name mynbodes-t3 -N 2 #스케일 다운
더 이상 노드에 파드를 할당할 수 없는 상태가 되면 워커 노드를 자동으로 확장할 수 있다. 하지만 오토 스케일링 그룹의 최대값만큼만 가능하기 때문에 오토 스케일링 그룹을 미리 많이 잡아두어야 한다. 이 때 필요한 것이 Cluster Autoscaler 이다. 사전 설정은 yaml파일을 통해 해놓았으므로 배포만 하면 된다.
curl -O https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
...
apiVersion: apps/v1
kind: Deployment
...
spec:
template
metadata:
...
annotations:
...
cluster-autoscaler.kubernetes.io/safe-to-evect: 'false' #추가
spec:
serviceAccountName: cluster-autoscaler
containers:
- image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.24.0 #버전 맞추기
...
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/myeks #수정
- --balance-similar-node-groups #추가
- --skip-nodes-with-system-pods=false #추가
...
kubectl create -f cluster-autoscaler-autodiscover.yaml #오류 뜨지만 상관 없음
kubectl logs -f deployment/cluster-autoscaler -n kube-system #로그로 오류 확인
eksctl get nodegroups --cluster myeks #노드 그룹에서 용량 확인
cluster-autoscaler의 버전은 사용 중인 클러스터 버전의 최신 버전을 사용해야 한다. 1.24 버전을 사용하고 있으므로 1.24 버전의 최신 버전인 1.24.0 버전으로 설정해 준다.
apply는 수정하는 것이므로 서비스 계정이 이미 있는 것을 수정시켜 버린다. SA는 어노테이션에 설정되어 있지만 지금 파일에는 어노테이션이 없으므로 apply하면 덮어씌워져서 안 된다.
오토스케일링이 되는지 테스트하는 방법은 다음과 같다.
kubectl scale rs myapp-rs --replicas 10
kubectl get nodes
자동으로 늘어나 있는 것을 확인할 수 있다. 삭제하면 자동으로 줄어든다. 줄어드는 것을 막고 싶다면 DesiredCapacity 값과 Min 값을 같게 하면 된다.
CloudWatch는 컨테이너의 로그를 저장하고 관리한다. CloudWatch의 로그 그룹은 디렉터리 형태로 되어 있다. /aws/eks/클러스터이름/cluster로 되어 있다. CloudWatch의 로그는 API 서버, Authenticator, 감사, 컨트롤러 관리자, 스케줄러를 남겨 둔다.
eks는 관리형이므로 컨트롤플레인을 관리할 수 없는데, 컨트롤플레인의 로그는 CloudWatch를 통해 볼 수 있다. 특히 CloudWatch의 로그는 보존 기간이 없으므로 클러스터를 지워도 남아 있다.
로그는 이와 같이 로그 스트림에 파일 형태로 남아 있다. kubeapi의 로그가 다 남아 로그를 검색할 수 있다.
CloudWatch Container Insights를 사용해 컨테이너화된 애플리케이션 및 마이크로서비스의 지표 및 로그를 수집하고 집계하며 요약할 수 있다. Container Insights는 컨테이너 재시작 오류 같은 진단 정보를 제공하여 문제를 격리하고 신속하게 해결할 수 있도록 도와준다. 온프레미스에서 모니터링을 위해 프로메테우스를, 로그를 위해서 EFK를 설치한 것 처럼 EKS도 프로메테우스와 EFK를 설치해서 관리할 수 있다. 하지만, AWS의 자체적인 Container Insight를 통해서도 가능하다. CloudWatch의 컨테이너 로그 수집을 위해선 SA뿐만 아니라 EC2에 CloudWatch가 접근할 수 있도록 Role을 부여 해줘야 한다.
Fluent Bit를 이용해 로그를 수집하고 CloudWatch에 가져간다. CloudWatch agent가 메트릭을 가지고 가 CloudWatch에 보낸다. 쉽게 실행하기 위해선 아래의 쉘스크립트를 실행한다.
ClusterName=myeks #클러스터 이름
RegionName=ap-northeast-2 #리전 이름
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On' #테스트문
curl https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${ClusterName}'/;s/{{region_name}}/'${RegionName}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f -
kubectl get ns #amazon-cloudwatch ns 확인
get all -n amazon-cloudwatch
PowerShell은 해당 쉘스크립트를 실행할 수 없으므로 git bash를 이용한다.
application이 pod의 로그이다.
Container Insight의 컨테이너 맵에서 컨테이너의 구조를 확인할 수 있고, 리소스 항목에서 리소스를 확인할 수 있다. 또한, 대시보드를 통해 모니터링이 가능하다. 이런 식으로 비교적 간단하게 퍼포먼스 모니터링을 구축 가능하다.
쿠버네티스 클러스터는 보통 여러 사람이 함께 관리한다. 그러기 위해선 타인의 IAM 계정에 적절한 역할 부여가 필요하다. eksuser1이라는 IAM 계정을, AdministratorAccess라는 정책을 부여한 상태로 생성하고 액세스 키를 만든다. 해당 계정을 타인이 쓴다고 가정하고, 자신이 관리하는 쿠버네티스 클러스터에 연동시켜 주는 방법은 다음과 같다.
cd ~/.aws
cat credentials
default만 존재하던 credentials에 eksuser1으로 하나를 더 추가한다. id와 액세스 키는 액세스 키를 생성했을 때 다운받은 csv 파일로 확인할 수 있다.
[default]
aws_access_key_id = XXX
aws_secret_access_key = XXX
[eksuser1]
aws_access_key_id = XXX
aws_secret_access_key = XXX
aws configure list-profiles #configure list 갱신
export AWS_PROFILE=eksuser1 #사용하는 계정을 eksuser1으로 변경
aws sts get-caller-identity --no-cli-pager #변경되었는지 확인
eksuser1은 쿠버네티스와 연결시켜놓지 않았기 때문에 아직 자격이 없어 할 수 있는 것이 없다. aws-auth라는 컨피그맵을 수정해서 권한을 주어야 한다.
kubectl get cm -n kube-system aws-auth -o yaml > aws-auth.yaml #파일로 저장
권한을 주기 위해선 계정의 arn 주소가 필요하다. arn은 콘솔의 사용자 계정 혹은 해당 명령어를 통해서 구할 수 있다.
aws sts get-caller-identity --no-cli-pager
ARN 주소를 얻었으면 mapUser 항목을 추가해 준다.
apiVersion: v1
data:
...
mapUsers: |
- userarn: <ARN>
username: eksuser1
groups:
- system:masters #쿠버네티스 그룹, 관리자 권한
...
kubectl apply -f aws-auth.yaml #권한파일 실행
export AWS_PROFILE=eksuser1 #유저 변경
kubectl get nodes #명령어 실행되는지 확인
kubectl create deploy myweb --image nginx #명령어 실행되는지 확인
kubectl get deploy #명령어 실행되는지 확인
kubectl get po #명령어 실행되는지 확인
kubectl get clusterroles #명령어 실행되는지 확인
kubectl delete deploy myweb #명령어 실행되는지 확인
RBAC를 잘 설정해 놓아야 여러 개발자가 같은 환경에서 다른 계정으로 작업할 수 있다. groups에는 원래 존재하는 그룹의 이름을 지정할 수도 있지만, 실사용 용으로 바인딩 시켜놓은 그룹의 이름을 지정해 놓을 수도 있다.
노드 그룹은 EC2 기반의 VM을 사용하는 노드이다. 파게이트는 VM을 사용하지 않는 서버리스이다. EC2 업시 바로 파드가 뜬다. 이것을 완전 관리형이라고 한다. EC2 인스턴스도 관리할 필요가 없다는 의미이다. 노드 그룹 말고 파게이트 프로파일만 사용할 수도 있다. 역으로, 파게이트 프로파일을 사용하지 않고 노드 그룹만 사용할 수도 있다.
파게이트 프로파일은 파게이트가 될 수 있는 조건을 명시한 것이다. 네임스페이스가 dev이고 labels의 env가 dev에 위치해 있으면 EC2 인스턴스가 아닌 파게이트로 생성된다.
파게이트 프로파일에 맞게 파드를 생성하면 일정 시간동안 Pending에 존재하고 nominated node라는 것이 보인다. 지명된 노드라는 뜻이다.
이 상태에서 describe로 확인해 보면 fargate-scheduler로 확인할 수 있다. fargate-scheduler에서 생성이 시작되면 nominated node가 없어지고 fargate ip가 생긴다. 이 상태에서 kubectl get nodes를 하면 fargate 노드를 확인할 수 있다. fargate 노드는 내부적으로 경량의 VM(EC2와는 상관이 없다)을 띄워 거기에 파드만 띄운다. fargate 노드는 노드이자 파드이다. 결국 EC2 인스턴스 없이 파드를 띄우는 것이다. 따라서 데몬셋을 만들 수 없다.
CapacityProvisioned의 용량에 따라 비용이 부과된다. request/limits를 설정하지 않으면 자동으로 fargate의 최소 용량으로 설정되기 때문에 request/limits를 설정해서 비용을 조절해야 한다.
Fargate는 서버리스인 완전 관리형이므로 EC2에 생성하는 것보다 조금 더 비싸다.
로드밸런서의 Instance targets은 인스턴스가 있고 NLB를 EC2에 연결시키기 때문에 Fargate에서는 사용할 수 없다. 따라서 Fargate에 로드 밸런서를 연결하려면 IP targets를 사용해야 한다.