사전학습
도커
- 이미지: 서비스 운영에 필요한 서버 프로그램, 소스코드 및 라이브러리 등을 묶은 형태
- 컨테이너: 이미지를 실행한 상태
쿠버네티스
컨테이너 환경을 구성해 배포하다 보면, 배포할 컨테이너의 수가 많아지면서 컨테이너 오케스트레이션 툴의 필요성이 증가한다. 컨테이너 오케스트레이션 툴은 컨테이너의 네트워킹 및 관리를 자동화하여 효율성을 높여준다. 다양한 컨테이너 오케스트레이션 툴 중에서 대표적으로 사용되는 몇 가지는 Docker Swarm, Docker Compose, Apache Mesos 등이 있다. 그 중에서도 쿠버네티스(Kubernetes)가 많이 사용되는 이유는 다양한 플러그인을 쉽게 적용할 수 있기 때문이다.
쿠버네티스 컴포넌트
쿠버네티스를 배포하면 클러스터를 얻는다. 그리고 클러스터는 노드들의 집합이다. 노드들은 크게 두 가지 유형으로 나눠진다.
- Control Plane
- 구성
- api 서버
- 컨트롤러
- 스케줄러(pod 배치)
- etcd(key-value 저장소, 쿠버네티스 클러스터 상태 저장)
- 스케줄링을 수행하고, 클러스터 이벤트를 감지하고 반응하는 역할을 한다.
- 하지만 설정이 많고 구성하기 어렵기 때문에 AWS에서 EKS라는 서비스로 쉽게 제공한다.
- (vs AWS ECS: AWS에서 만든 완전 관리형 컨테이너 오케스트레이션 서비스)
- 구성
- Data Plane
- 구성
- pod
- kube-proxy(ip router table, 네트워크 담당)
- kubelet(노드의 상태를 control plane에 주기적으로 보내는 역할)
- AWS EC2
- AWS Fargate: EC2와 달리 컨테이너와 1:1 매칭되는 완전 관리형 서비스
- 구성
쿠버네티스 Object
- Pod: 컨테이너 묶음
- Replica Set: Pod 묶음
- Deployment: pod와 replica set에 대한 선언적 업데이트 제공
- 보통 Pod, Replica Set 보다 deployment를 통해 관리
- Service: pod들을 위한 고정 endpoint 제공
- Cluster IP: 쿠버네티스 내부 IP
- Node Port: 외부 port 개방
- Load Balancer: 클라우드 제공 로드밸런서로 노출
- Ingress: 클러스터 외부에서 클러스터 내부 서비스로 HTTP(S) 경로 노출
Hands on
Cloud9(AWS IDE 서비스)을 통해 미리 구축된 환경에서 실습했다.
ECR 및 EKS 생성
- 도커 이미지 만들기
- ECR에 이미지 올리기
- ECR Repository 생성
aws ecr create-repository \ --repository-name demo-flask-backend \ --image-scanning-configuration scanOnPush=true \ --region ${AWS_REGION} # scanOnPush=true: 취약점 점검 옵션
- 이미지 빌드 후 Repository에 저장
# build docker build -t demo-flask-backend . # tag # lastest보다 빌드시간과 같이 유니크한 값을 사용하는게 일반적 docker tag demo-flask-backend:latest $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-flask-backend:latest # push docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-flask-backend:latest
- ECR Repository 생성
- eksctl로 EKS 클러스터 생성
- eks-demo-cluster.yaml 작성
--- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: eks-demo # 생성할 EKS 클러스터명 region: ${AWS_REGION} # 클러스터를 생성할 리전 version: "1.27" vpc: cidr: "10.0.0.0/16" # 클러스터에서 사용할 VPC의 CIDR nat: gateway: HighlyAvailable managedNodeGroups: - name: node-group # 클러스터의 노드 그룹명 instanceType: m5.large # 클러스터 워커 노드의 인스턴스 타입 desiredCapacity: 3 # 클러스터 워커 노드의 갯수 volumeSize: 20 # 클러스터 워커 노드의 EBS 용량 (단위: GiB) privateNetworking: true iam: withAddonPolicies: imageBuilder: true # Amazon ECR에 대한 권한 추가 albIngress: true # albIngress에 대한 권한 추가 cloudWatch: true # cloudWatch에 대한 권한 추가 autoScaler: true # auto scaling에 대한 권한 추가 ebs: true # EBS CSI Driver에 대한 권한 추가 cloudWatch: clusterLogging: enableTypes: ["*"] iam: withOIDC: true # 쿠버네티스에서 AWS 기능을 사용할 때 권한 체크
- 클러스터 배포 (15분 ~ 20분 소요)
- AWS CloudFormation(코드형 인프라 관리 서비스)으로 던져져 클러스터가 생성 됨
eksctl create cluster -f eks-demo-cluster.yaml
- 3개의 AZ에 VPC를 생성
- 각 AZ에 1개 노드 노드 생성
- 각 AZ에 privat/public 두 개의 서브넷 생성 (private 서브넷은 NAT에 연결해 외부 통신 가능)
- AWS CloudFormation(코드형 인프라 관리 서비스)으로 던져져 클러스터가 생성 됨
- 배포가 완료되면, 아래의 명령을 통해 확인
kubectl get pod -A # READY에 2/2는 pod안에 컨테이너가 2개 생성되야 한다는 의미 NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-4xt6r 2/2 Running 0 4d15h kube-system aws-node-kshdc 2/2 Running 0 4d15h kube-system aws-node-s5ms5 2/2 Running 0 4d15h kube-system coredns-5b8cc885bc-h4w6f 1/1 Running 0 4d15h kube-system coredns-5b8cc885bc-xrv7s 1/1 Running 0 4d15h kube-system kube-proxy-7p2xk 1/1 Running 0 4d15h kube-system kube-proxy-9lnqz 1/1 Running 0 4d15h kube-system kube-proxy-mmx4q 1/1 Running 0 4d15h # 자격증명 확인 cat ~/.kube/config apiVersion: v1 # ... clusters: - cluster: server: 10.0.0.1 name: main.com - cluster: server: 127.0.0.1 name: local.com contexts: - context: cluster: main.com user: admin name: admin@main.com - context: cluster: local.com user: account name: account@local.com # ...
- 컨텍스트 변경
- 클러스터와 user 관계를 매핑한 것
- 워크샵을 위해 미리 구성된 클러스터와 새로 생성한 두 개의 클러스터가 존재
kubectl config use-context account@local.com
- eks-demo-cluster.yaml 작성
kube-ops-view 설치
쿠버네테스 클러스터 구성을 그래픽으로 보여주는 기능이다.
git clone https://github.com/dobal-production/eksworkshop-custom.git
cd ~/eksworkshop-custom/introduction/setup/05.kube-ops-view
./05.kube-ops-view-clb.sh
어플리케이션 배포
- catalog component 배포
# catalog component 확인 ls ~/environment/eks-workshop/base-application/catalog configMap.yaml # key-value 저장 deployment.yaml kustomization.yaml namespace.yaml secrets.yaml # secret 저장 service-mysql.yaml service.yaml serviceAccount.yaml statefulset-mysql.yaml # 배포 # -k 옵션: kustomization.yaml 파일을 읽어서 적용 kubectl apply -k ~/environment/eks-workshop/base-application/catalog
- 배포 확인
kubectl get svc -n catalog NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE catalog ClusterIP 172.20.49.6 <none> 80/TCP 2m28s catalog-mysql ClusterIP 172.20.7.250 <none> 3306/TCP 2m28s
어플리케이션 네트워크
- 어플리케이션 노출: 서비스는 기본적으로 Cluster IP 방식을 사용하므로, 외부 통신을 위한 작업이 필요
- 로드밸런서: 서비스를 로드밸런서로 노출(node port mode)
- NLB(TCP 계층에서 작동하는 레이어4 로드 밸런서)에 연결
- 로드밸런서 생성
# ~/environment/eks-workshop/modules/exposing/load-balancer/nlb/nlb.yaml apiVersion: v1 kind: Service metadata: name: ui-nlb annotations: # AWS와 쿠버네티스 명령을 연결 service.beta.kubernetes.io/aws-load-balancer-type: external # internal로 하면 private 존 안에 내부 통신만 가능 service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance # 노드로 연결 namespace: ui spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 name: http selector: app.kubernetes.io/name: ui app.kubernetes.io/instance: ui app.kubernetes.io/component: service
kubectl apply -k ~/environment/eks-workshop/modules/exposing/load-balancer/nlb
- 서비스마다 로드밸런스를 생성하므로, http(s)만 사용한다면 ingress를 사용하는게 좋다.
- Ingress: 서비스에 연결
- ALB(http(s) 계층에서 작동하는 레이어7 로드밸런서) 사용
- 로드밸런스 하나로 path(경로)를 이용해 라우팅
- ingress 생성
# ~/environment/eks-workshop/modules/exposing/ingress/creating-ingress/ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ui namespace: ui annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/liveness spec: ingressClassName: alb rules: - http: paths: - path: / # 루트 80에 ui 연결 pathType: Prefix backend: service: name: ui port: number: 80
kubectl apply -k ~/environment/eks-workshop/modules/exposing/ingress/creating-ingress
- 확인
# 근본을 파헤칠 때, describe 명령 사용 $ ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-ui-ui`) == `true`].LoadBalancerArn' | jq -r '.[0]') $ TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn') $ aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN { "TargetHealthDescriptions": [ { "Target": { "Id": "10.42.180.183", "Port": 8080, "AvailabilityZone": "us-west-2c" }, "HealthCheckPort": "8080", "TargetHealth": { "State": "healthy" } } ] }
alb.ingress.kubernetes.io/target-type: ip
ip 모드를 지정했기 때문에, ui pod의 ip와 포트가 타겟에 등록됨
-
로드밸런서(ip mode), 그림 3번에 해당
- 로드밸런서 node port mode 설정에서 한줄만 변경
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip # instance에서 ip로 변경 service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing service.beta.kubernetes.io/aws-load-balancer-type: external name: ui-nlb namespace: ui
kubectl apply -k ~/environment/eks-workshop/modules/exposing/load-balancer/ip-mode
- 로드밸런서 node port mode 설정에서 한줄만 변경
- 로드밸런서: 서비스를 로드밸런서로 노출(node port mode)
- 오토스케일링
- HPA(Horizontal Pod Autoscaler): 노드 안에서 pod를 스케일링
apiVersion: apps/v1 kind: Deployment metadata: name: ui spec: template: spec: containers: - name: ui resources: requests: # 항상 자원을 다 쓰려는 습성이 있기 때문에 설정해주는게 좋음 cpu: 250m limits: cpu: 500m
- CA(Cluster Autoscaler): 노드의 상태에 따라 컴퓨팅 인프라를 스케일링
apiVersion: apps/v1 kind: Deployment metadata: name: all spec: replicas: 4
- AWS Auto scaling 그룹에 매핑 됨
- Auto scaling 그룹이 주기적으로 pulling해서 오토스케일링 하므로 작동하는데 분 단위의 시간이 걸림
- 또한, Auto scaling 그룹은 리소스를 줄일 때도 엄청 느리게 줄어듦
- Cluster Over Provisioning
- Auto scaling 그룹은 노드가 증가하는데 시간이 걸리기 때문에 사용하는 방법
- 우선순위가 낮은 더미 pod를 만들어 두고 트래픽이 늘어 pod가 증가할 때, 더미 pod가 pending 상태가 되고 실제 서비스 pod 들이 들어가게 되어 빠르게 스케일링 가능
- 그 후, 더미 pod를 위한 노드가 하나 생성
- 결국 여유 노드 1대를 더 운영하는 방식
- 비용이 증가하는 대신 빠른 대응이 가능
- Karpenter: 쿠버네티스를 위한 오픈소스 오토스케일링 프로젝트
- 자원들을 관찰하여 몇 초 안에 오토스케일링 함
- 현재 상태를 보고 빈 곳이 있으면 알아서 줄여주고, 심지어 EC2 타입도 알어서 변경해줌
- 너무 민감하게 반응해서 자주 자원의 상태가 변할 수 있음
- HPA(Horizontal Pod Autoscaler): 노드 안에서 pod를 스케일링
- 오토스케일링 방식
- 메트릭에 기반한 방식: 메모리 사용량, CPU 사용량 등
- 스케줄링에 기반한 방식: 티켓팅과 같이 정해진 시간에 트래픽이 몰릴 것으로 예정된 경우