AWS EKS 서버 배포 - CI/CD

  1. AWS EKS 서버 배포 - CI/CD
    1. 쿠버네티스 (k8s)
      1. Master Node
        1. API 서버 (kube-apiserver)
        2. 스케줄러 (kube-scheduler)
        3. 컨트롤러 매니저 (kube-controller-manager)
        4. etcd
      2. Worker Node
        1. kubelet
        2. kube-proxy
    2. Kubernetes 구성 요소
      1. namespace (네임스페이스)
      2. Pod
      3. Service
      4. Replicaset
      5. Deployment
      6. Ingress
        1. Ingress와 Ingress Controller 차이점은?
    3. 실습 CI/CD 구축 해보기
      1. IAM 역할 생성 하기
      2. AWS EKS Cluster 생성 하기
      3. AWS CLI 설치 및 kubectl 셋팅 하기
      4. kube config 설정 하기
      5. Kubernetes NameSpace 생성하기
      6. AWS Route53 설정 및 LB로 라우팅 대상 설정 하기
      7. 도메인 구입하기
      8. Docker Image 생성 및 업로드 하는 방법
        1. AWS ECR Repository 생성 하기
      9. kubernetes Deployment 및 Ingress Apply 하기
        1. Deployment Apply 하기
        2. Ingress Apply 하기
      10. HTTPS 통신 인증서 작업
        1. 인증서 발급
        2. https.yml 내용 보기
      11. Github Actions 을 이용해서 CI/CD 자동화 배포 하기
        1. github secret 에 중요 정보 추가 방법
        2. 실제로 CI/CD 및 롤링 업데이트 진행 되는지 확인 해보기
      12. Autoscaling - Pod 자동 확장 작업
        1. Autoscaling - Pod 자동 확장 작업 - Deployment 설정
        2. Autoscaling - Pod 자동 확장 작업 - 메트릭 서버 설치
        3. Autoscaling - Pod 자동 확장 작업 - HPA 스크립트 생성 후 적용
          1. hpa.yml 파일
        4. 부하 테스트 통해 Pod Autoscaling 되는지 확인 해보기
      13. Autoscaling - EC2 자동 확장 작업
        1. Autoscaling EC2 자동 확장 - NodeGroup(ASG)에 태그 추가 작업 진행
        2. Autoscaling EC2 자동 확장 - ID 제공업체 추가
        3. Autoscaling EC2 자동 확장 - IAM 역할 생성
        4. 부하테스트 통해 실제로 Node 가 자동 생성 되는지 확인 해보기

AWS EKS 서버 배포 - CI/CD

쿠버네티스 (k8s)

쿠버네티스 아키텍처

Kubernetes (k8s) 는 컨테이너화된 애플리케이션을 대규모로 운영하기 위한 오케스트레이션 입니다. 즉 배포, 확장, 관리를 자동화하는 오픈소스 플랫폼 인데요. 주로 Docker와 같은 컨테이너 기술과 결합하여 컨테이너 관리 플랫폼으로 사용 하게 됩니다.

KubernetesCluster 로 구성 되어있는데 여러 대의 머신(노드) 을 묶어 컨테이너화된 애플리케이션을 배포하고 관리하는 시스템으로 되어 있습니다. 크게 Mster Node, Worker Node 로 구성 되어 있으며 하나씩 알아보도록 하겠습니다.

Master Node

Master NodeCluster 관리, API 서버, 스케줄링 역할 등을 담당 하게 됩니다. 예를들어 Master Node 하고 Worker Node 는 물리적으로 분리 된 상태에서 특정 Worker Node 가 부하가 발생시 Master Node 는 이를 감지해서 Worker NodePod 를 증설 할지 아니면 물리적인 노드 Worker Node 를 증설할지 선택하게 됩니다.

그래서 Master Node 는 중앙 컨트롤 타워 역활 담당 그리고 Worker Node 는 실질적으로 작업을 수행하는 역활을 하게 됩니다. 이렇게 Master Node 하고 Worker Node 를 묶어서 Cluster 라고 부르게 됩니다.

Master Node 에 특징적인 부분은 앞써 이야기 한대로 Master NodeWorker Node 를 계속 참조하고 있다가 부하가 발생하면 Pod 를 더 증설 할 수 있겠지만 직접 개발자가 판단해서 Worker Node 안에 있는 Pod 를 증설 할 수 있습니다. 이것을 Kubectl 명령을 통해 직접 개발자가 Master Node 에게 명령을 내리고 Master NodeWorker Node 에 명령을 내리게 됩니다. 즉 kubectl 역활을 개발자가 하고 Kubernetes 와 서로 소통을 하기 위한 프로그램 이라고 생각하면 쉽습니다.

API 서버 (kube-apiserver)

Master NodeAPI 서버 (kube-apiserver) 는 클러스터의 중앙 집중식 관리 시스템 입니다. 개발자가 직접 외부 환경에서 Kubernetes 로 직접 통신 할 수 있는 진입점을 제공 합니다. 앞써 이야기 한대로 Worker Node 에 대한 Pod 정보 및 Pod 증설 등 개발자가 명령을 내리면 쿠버네티스 API 서버(kube-apiserver)로 전송 전송 하게 됩니다.

스케줄러 (kube-scheduler)

Cluster 내에서 실행될 파드를 적절한 노드에 할당하는 역할을 담당하게 됩니다. 예를들어 부하로 인해 Pod를 증설 해야 하는데 어떤 Worker Node 에 증설 해야할지를 판단 하게 됩니다. 그리고 새로 생성되거나 스케줄링되지 않은 파드를 감시하고 파드의 요구사항과 노드의 상태를 고려하여 최적의 노드를 선택 하게 됩니다.

컨트롤러 매니저 (kube-controller-manager)

컨트롤러 매니저Worker Node 에 있는 Pod 를 계속 모니터링 역활을 하고 만약 특정 Pod 가 문제로 인해 죽으면 다시 Pod 를 살려내는 등 이러한 컨테이너 자원의 최적화 하는 역활을 하게 됩니다.

etcd

etcd 는 클러스터 상태 저장소 입니다. 즉 Kubernetes 실행 되기 위한 Config 관련 환경 정보 그리고 비밀값 등을 저장 하는 역활을 하게 되며 클러스터 상태값(ConfigMap 등)를 key-value 형태로 저장하는 저장소 입니다.

Worker Node

실질적인 애플리케이션 실행을 담당하는 노드 입니다. 이번에 실습하게 될 AWS 기준으로 EC2 인스턴스로 실행하게 되고 이 Worker Node 에서 Pod 가 실행 하게 됩니다.

kubelet

Master NodeAPI 서버 (kube-apiserver) 와 통신하게 되며 각각의 Pod 들을 시작, 중지, 관리하는 작업을 수행하게 됩니다. kubelet 는 각 Pod 가 성공적으로 시작되었는지 체크하고 Pod 의 상태를 주기적으로 API 서버 (kube-apiserver) 로 보고 하게 됩니다.

kube-proxy

kube-proxy 는 네트워크 프록시 및 로드 밸런서 역활을 하는 노드 수준의 구성 요소 입니다. 네트워크 트래픽을 적절히 포워딩 하도록 네트워크 규칙을 설정하여 각 파드 간의 통신이나 외부에서 파드로 접근 가능 하도록 합니다.

Kubernetes 구성 요소

namespace (네임스페이스)

namespace

Kubernetes Cluster 환경에서 논리적인 분리 하는 단위 입니다. 하나의 클러스터 내 여러 개의 작은 클러스터처럼 사용할 수 있게 해주는 기능인데, 각 네임스페이스는 독립적인 환경을 제공 해주며 예를들어 개발, 테스트, 상용 등 각각의 환경을 분리 할때 유용하게 사용됩니다. 즉 특정 서버를 물리적인 분리가 아닌 논리적으로 리소스를 그룹화 해서 마치 여러 개의 클러스터가 있는 것처럼 사용할 수 있게 합니다.

Pod

Pod

Kubernetes 에서 Pod 는 배포 가능한 가장 작은 컴퓨팅 단위 이며 하나 이상의 컨테이너의 그룹입니다. 또한 컨테이너들이 함께 실행될 수 있도록 리소스와 네트워크 네임스페이스를 공유합니다. 일반적으로는 1개의 컨테이너로 구성 되며 만약 한개의 Pod 가 두개의 독립적인 컨테이너가 존재하면 이것들을 하나의 네트워크로 묶는 역활을 하게 됩니다. 즉 만약 두개의 컨테이너가 같은 IP 를 사용하고 묶는 걸 Pod 이라고 합니다.

이렇게 그룹화 해서 묶어서 Pod 로 구성하는 이유는 2개의 컨테이너가 같은 IP 를 사용하면서 서로 다른 각각의 컨테이너 간의 긴밀한 통신 해야 하는 경우 그리고 통신을 원활하게 (속도 등) 해야 하는 경우 입니다.

Service

Service-1

ServiceKubernetes의 자원의 종류의 이름입니다. 주요 역활은 Pod 들 앞단에 위치 하여 추상적인 네트워크 엔티티 역활을 하게 되는데요. 즉 Cluster 내에서 실행 중인 Pod 들에게 네트워크 접근 방법 (로드 밸런싱)을 제공 하고 있습니다. 그림을 보시면 만약 외부로 부터 Pod 을 접근 하고자 할때 그러니깐 두개의 각각 Pod 안에 설치되어 있는 컨테이너에 접근 하고자 할때 앞에 Service 가 각 Pod 에게 부하를 분산해주는 부하 분산기 역할을 해줍니다.

기본적으로 Pod는 외부 포트를 지정할 수 없는데 사용자는 특정 Pod 로 접근 해야 할때 어떨때는 1번 Pod 로 접근 해야 하고 어떤 경우에는 2번 Pod 로 접근 해야 하는 상황에서 사용자 Front 입장에서는 2개의 Pod EndPoint 를 동적으로 컨트롤러 하기 어렵습니다. 이때 Service 가 앞에 배치해서 부하 분산기 역활 통해 각각의 Pod 로 접근 하도록 도와주게 됩니다.
그리고 Pod 는 부하에 따라서 동적으로 Pod 가 증설 될 수도 있고 새로 생성된 Pod 는 IP 를 예측하기 어렵습니다. 그래서 언제든지 확장 될 수 있는 Pod 들을 확장성 있는 서버로 설계 하기 위해 Service 가 로드밸런싱 역활로 가지게 됩니다.

Service-2

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: my-namespace
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: nginx

다음은 Service 자원 예시를 보도록 하겠습니다.

1
kind: Service

자원 종류를 Service 로 지정 하겠다는 의미입니다.

1
2
3
metadata:
name: my-service
namespace: my-namespace
  • name: 특정 자원의 이름
  • namespace: 네임스페이스명
1
2
spec
type: ClusterIP
1
2
3
4
5
6
spec:
ports:
- port: 80
targetPort: 80
selector:
app: nginx
  • spec.selector.app: label 를 지정 하는 것, 즉 특정 Pod 에 지정한 label 로 네트워크 정보 전파 함, 만약에 1개 이상 podserviceselector.app 과 매핑되면 자동으로 로드밸런싱
  • spec.ports.port: 특정 서비스의 내부 포트 번호
  • spec.ports.targetPort: Service포워딩(forward) 하는 대상 Pod의 포트

Replicaset

Replicaset

ReplicaSet는 지정된 수의 Pod 복제본을 만들어주고 항상 지정된 수로 실행 되도록 보장해주는 Kubernetes 의 리소스 입니다. 앞써 설명한 Pod 를 2개의 복제 본으로 유지 하고자 하면 직접 개발자가 정의된 yml 파일을 이용해서 apply 해서 각각 Pod 를 만들어 주면 됩니다. 이렇게 하면 2개의 yml 파일을 구성해서 관리 해야 하는데 동일한 코드 중복 문제가 있을 수 있습니다. 이것을 해소하기 위해 Replicaset 이라는 리소스를 이용해서 개발자가 원하는 몇 개의 복제본 Pod 로 구성할지 설정만 하고 하나의 yml 파일로 관리 하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
namespace: my-namespace
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
1
2
3
metadata:
name: nginx-replicaset
namespace: my-namespace
  • name: 특정 자원의 이름
  • namespace: 네임스페이스명
1
2
3
4
5
6
7
8
9
10
11
spec:
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
  • spec.template.spec.containers: Pod 생성시 어떤 image 컨테이너로 생성 할 것인지 정의 합니다. nginx 이라는 이미지로 생성해서 컨터네이너로 관리 하겠다는 의미 입니다.
  • spec.template.metadata.labels.app: 생성한 Pod의 라벨을 지정 하는 것 입니다.
1
2
3
4
5
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx
  • spec.replicas: 생성한 Pod 를 몇 개 replicas 로 구성 할지 지정 합니다.
  • spec.selector.matchLabels.app: 이 값은 라벨을 지정 하는 것인데 spec.template.metadata.labels.app 통해 지정한 라벨하고 동일하게 매칭 되어 있으면 속한 Pod 들을 모니터링을 하게 되며 만약 지정한 replicas 값이 2 로 지정하면 항상 지정된 수로 Pod 들을 실행 되도록 보장 해줍니다.

Deployment

Kubernetes 에서 Deployment 는 애플리케이션의 무중단 배포 와 자동 복구, 스케일링, 버전 관리 등을 가능하게 해주는 핵심 리소스 입니다.

앞써 설명한 ReplicaSet 과 기본기능은 동일 하고, Deployment 이라는 리소스를 만들면 ReplicaSetReplicaSet을 통해 Pod를 생성 하게 됩니다. 간단하게 주요 역활에 대해 나열 하도록 하겠습니다.

  1. 애플리케이션의 지속적인 배포(Continuous Deployment)
  2. 새로운 버전의 롤링 업데이트(Rolling Update)
  3. 장애 발생 시 자동 복구(Self-healing)
  4. 버전 롤백(Rollback) 기능 제공

한마리로 ReplicaSet 의 발전된 형태가 Deployment 이라고 생각 하시면 되는데

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

예시 Deployment 입니다. 차례대로 설명 하도록 하겠습니다.

1
2
3
metadata:
name: my-app
namespace: my-namespace
  • name: 특정 자원의 이름
  • namespace: 네임스페이스명
1
2
3
4
5
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx
  • spec.replicas: 생성한 Pod 를 몇 개 replicas 로 구성 할지 지정 합니다.
  • spec.selector.matchLabels.app: 이 값은 라벨을 지정 하는 것인데 spec.template.metadata.labels.app 통해 지정한 라벨하고 동일하게 매칭 되어 있으면 속한 Pod 들을 모니터링을 하게 되며 만약 지정한 replicas 값이 2 로 지정하면 항상 지정된 수로 Pod 들을 실행 되도록 보장 해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
spec:

template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
  • spec.template.metadata.labels.app: 생성한 Pod의 라벨을 지정 하는 것 입니다.
  • spec.template.spec.containers.name: Pod 생성을 하는데 containers 이름을 nginx 이라고 지정 하겠다는 의미 입니다.
  • spec.template.spec.containers.image: Pod 생성시 어떤 image 컨테이너로 생성 할 것인지 정의 합니다. nginx:1.25 이라는 이미지로 생성해서 컨터네이너로 관리 하겠다는 의미 입니다.
  • spec.template.spec.containers.ports.containerPort: 생성하고자 하는 Podnginx 내부 포트를 80 으로 지정 하겠다는 의미 입니다.

차례대로 설명 했고 해당 Deployment 를 apply 하도록 하겠습니다.

Deployment-1

1
kubectl apply -f ./deployment.yml

그런 다음 ReplicaSet 을 확인 해보도록 하겠습니다.

1
kubectl get replicaset -n ${네임스페이스명}

Deployment 도 생성 되었는지 확인 해봅니다.

1
kubectl get deployment -n ${네임스페이스명}

Pod 도 정상적으로 생성 되었는지 확인해봅니다.

1
kubectl get pod -n ${네임스페이스명}

Deployment-2
Deployment-3

그런 다음에

spec.template.spec.containers.image 여기서 nginx:1.26 으로 버전 업그레이드 해서 다시 apply 하도록 하겠습니다.

1
kubectl apply -f ./deployment.yml

그런 다음에 replicaset, deployment, pod 차례대로 다시 조회 하도록 합니다.

1
2
3
kubectl get replicaset -n ${네임스페이스명}
kubectl get deployment -n ${네임스페이스명}
kubectl get pod -n ${네임스페이스명}

차례대로 확인 해보면 새로운 Pod 가 생성 되었다는 것을 확인 할 수 있는데요. 여기서 중요한 부분은 Deployment 경우 새로운 버전으로 다시 apply 하게 된다면 새롭게 Replicaset 하고 Pod 가 생성하게 되고 정상적으로 생성하게 된다면 기존에 생성 했던 Replicaset 하고 Pod 는 삭제 하게 됩니다. 즉 롤링 업데이트(Rolling Update) 가 진행 됩니다.

그 다음은 롤백(Rollback) 기능을 알아보도록 하겠습니다.

1
kubectl rollout undo deployment ${deployment 명} -n ${네임스페이스명}

롤백 명령어 입니다. 그런 다음 Pod 를 조회 하도록 하겠습니다.

1
kubectl get pods -n ${deployment 명}

그런데 latest(최신 버전) 의 경우 즉 Deploymenttemplate에 변화가 없으면
spec.template.spec.containers.nginx:latest 로 지정 하고 apply 를 하게 된다면 내부적으로는 버전 변화가 없기 때문에 새로운 ReplicaSet 하고 Pod 를 생성하지 않습니다. 즉 롤링 업데이트(Rolling Update) 가 진행 되지 않습니다.

강제로 새로운 ReplicaSetPod 를 생성 하고자 하면 rollout restart 명령어를 사용 해야 합니다.

1
kubectl rollout restart deployment ${deployment명} -n ${namespace명}

이렇게 명령어를 실행하면 latest 태그를 그대로 사용하더라도 강제로 ReplicaSet 을 생성해서 새로운 revision 생성 하게 됩니다.

Ingress

IngressKubernetes Cluster 외부에서 내부 서비스로 들어오는 HTTP 요청의 라우팅 규칙을 정의하는 리소스 입니다. 다만, Ingress 만 만든다고 바로 작동하는 건 아니고 이를 실제로 처리할 Ingress Controller (예: NGINX) 가 필요합니다.

ingress 가 왜 필요한지 그리고 어떤 역활을 하는지 그림으로 설명 하도록 하겠습니다.

Ingress-1

그림에 보면 사용자가 api.server.com 으로 요청하게 된다면 api.server.com 역활을 하고 있는 LB 에 도달 하게 되고 LB 는 특정 Service 로 연결해서 각각의 Pod 로 연결하게 됩니다. 반대로 사용자가 shop.server.com 으로 요청시 해당 LB 는 특정 Service 로 연결해서 각각의 Pod 로 연결 할 수 있게 도와줍니다.

하지만 이 아키텍처의 단점은 각각의 LB (로드밸런서)가 특정 서비스하고 연결 하고 있다는 점 입니다. 즉 api.server.com, shop.server.com (prefix 다름) 각각의 자원에서 전용 LB 가 각각 생성 해야 한다는 점 입니다. 만약에 앞으로 admin.server.com 이라는 서비스가 생성 되었다는 그에 맞게 LB 도 생성 해야 한다는 점 입니다.

LBAWS 에서 과금이 발생하는 요소다 보니까 자원의 낭비가 발생 할 수 있는 부분이 있습니다. 그리고 각각 LB 마다 엔드포인트가 관리를 해야 해서 프론트엔드 소스코드에서도 전부 달라지는 엔드포인트 설정 및 관리를 해야 합니다.

Ingress-2

각각의 Service마다 LB를 연결시키는 게 아니라 중간에 Ingress 라고 하는 자원을 만들어두고 이 Ingressurl 패턴에 따라 서비스로 분기 처리를 해주게 됩니다. 즉 Ingress Controllerapi.server.com, shop.server.com 각각의 Ingress 로 라우팅 하는 역활을 하게 됩니다.
Ingress 에서는 예를들어서 shop.server.com/${Server1 URL 패턴} shop.server.com/${Server2 URL 패턴} 으로 각각 url 패턴으로 Service 로 가도록 합니다.

IngressIngress Controller 차이점은?

Kubernetes 에서 IngressIngress Controller는 이름이 비슷해서 헷갈리지만 역할과 성격이 완전히 다릅니다. Ingress Controller 는 실질적인 라우팅을 수행 하고 Ingress 는 규칙정보 정의를 합니다.

abc.server.com 이라는 도메인 통해 사용자가 접근 하게 된다면 맨 처럼 LB 는 특정 Ingress Controller 로 접근 하게 되고 여기서 주된 역활은 kubernetes Cluster 외부에서 내부 서비스로 들어오는 트래픽을 관리하고 라우팅하는 역할 하게 됩니다.
Ingress Controller 로 접근 통해 맨 앞에 Prefix 단위로 특정 Ingress 로 접근 하게 되고 Ingressurl 패턴에 따라 서비스로 분기 처리를 해주게 됩니다.

쉽게 생각 하자면 Ingress Controller 는 “교통경찰” 또는 “자동차 네비게이션 시스템”, Ingress 는 “도로 표지판” 으로 이해 하면 됩니다.

1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/aws/deploy.yaml

Ingress Controller 설치 명령어 입니다. 이때 동시에 AWS LB 도 등록 하게 됩니다. AWS 에 가서 Route53 하고 등록된 LB 하고 매핑 작업이 필요 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: my-namespace
annotations:
# 여기서 "nginx"로 지정한 것은 이 Ingress가 NGINX 기반 Ingress Controller에 의해 처리된다는 의미
kubernetes.io/ingress.class: nginx

spec:
rules:
- host: server.syh8088.store # 설정하려는 도메인 이름.
http:
paths:
# - path: /
- path: /syh/ #syh 시작하는 모든 url요청을 nginx-service1로 라우팅한다는 정규표현식
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
1
2
3
4
5
6
7
8
9
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: my-namespace
annotations:
# 여기서 "nginx"로 지정한 것은 이 Ingress가 NGINX 기반 Ingress Controller에 의해 처리된다는 의미
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$1 #첫번쨰 prefix제거
  • metadata.name: 특정 자원의 이름
  • metadata.namespace: 네임스페이스명 지정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spec:
rules:
- host: server.syh8088.store # 설정하려는 도메인 이름.
http:
paths:
# - path: /
- path: /msmbers/ #syh 시작하는 모든 url요청을 nginx-service1로 라우팅한다는 정규표현식
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
- path: /products/ #syh 시작하는 모든 url요청을 nginx-service1로 라우팅한다는 정규표현식
pathType: Prefix
backend:
service:
name: nginx-service-2
port:
number: 80
  • spec.rules.host: 설정하려는 도메인 이름
  • spec.rules.http.paths.- path: server.syh8088.store/members 로 호출시 /members 라는 path 로 호출 했으면 ‘nginx-service’ 로 라우팅한다는 정규표현식
1
kubectl apply -f ./ingress.yml

ingress apply 명령어 입니다.

1
kubectl get ingress -n ${네임스페이스명}

해당 설치된 Ingress 조회 하는 명령어 입니다.

1
2
NAME              CLASS        HOSTS                ADDRESS                                     PORTS      AGE
nginx-ingress <none> server.syh8088.store ${LB 주소}.ap-northeast-2.amazonaws.com 80 88s

이렇게 설치된 ingress 목록을 확인 할 수 있습니다.

1
http://server.syh8088.store/msmbers 

해당 명령어를 호출하면 Ingress-Controller -> Ingress -> Service -> Pod 까지 접근 하게 됩니다.

실습 CI/CD 구축 해보기

AWS EKS CI/CD architecture

지금부터 실습 통해 AWS EKS 이용해서 CI/CD 작업 하고자 합니다. 간단하게 전체적인 flow 를 설명하자면 사용자가 특정 도메인에 접근 하면 AWS Route53 통해 정해진 LB 로 접근 하게 되고 Kubernetes Ingress Controller 로 이동 하게 됩니다. 여기서 적절하게 도메인 prefix 계산해서 Ingress 로 가게되고 매핑된 Service 로 접근하게 되고 최종 목적지인 Pod 로 가게 됩니다.

개발자는 Github Action 통해 특정 브랜치에 Git Push 를 하게 된다면 Dockerfile 이용해서 Docker Image 를 만들고 만든 image 를 AWS ECR Repository 에 등록하게 됩니다. 그다음 Kubernetes 에서 지원하는 kubectl 명령어를 통해서 새롭게 Pod 를 만들면서 롤링 업데이트 진행 하게 됩니다.

IAM 역할 생성 하기

IAM_USER_CREATE_1-1

AWS IAM 서비스로 접근 해서 사용자 생성 버튼을 클릭 하도록 합니다.

IAM_USER_CREATE_1-2

원하시는 사용자 이름을 지정 한 다음에 다음 버튼을 클릭 하도록 합니다.

IAM_USER_CREATE_1-3

권한 옵션 에서 직접 정책 연결 선택 합니다. 그런 다음에 AdministratorAccess 정책을 클릭 하고 다음 버튼을 클릭 합니다.

IAM_USER_CREATE_1-4

사용자 생성 버튼을 클릭 합니다.

IAM_USER_CREATE_1-5

생선된 사용자 콘솔 로그인 URL콘솔 암호 를 따로 잘 관리 하도록 합니다. 그런 다음 생성한 사용자로 다시 AWS 로그인을 하도록 합니다.

IAM_USER_ACCESS_KEY_CREATE_1-1

생성한 사용자에서 페이지 에서 엑세스 키 만들기 버튼을 클릭 하도록 합니다.

IAM_USER_ACCESS_KEY_CREATE_1-2

사용사례 에서 Command Line Interface(CLI) 선택 하도록 합니다.

IAM_USER_ACCESS_KEY_CREATE_1-3

엑세스 키 만들기 버튼을 클릭 하도록 합니다.

IAM_USER_ACCESS_KEY_CREATE_1-4

생성된 엑세스 키를 따로 잘 보관 하도록 합니다.

AWS EKS Cluster 생성 하기

EKS_CREATE_1-1

AWS Elastic Kuberneties Service 서비스에 접속 하도록 합니다. 클러스터 생성 버튼을 클릭 합니다.

EKS_CREATE_1-2

일반적인 실습 과정이기에 요금 절약을 위해 구성 옵션사용자 지정 구성 을 클릭 합니다. 그리고 EKS 자율 모드 사용을 해제 하도록 합니다.

그런 다음 원하시는 클러스터 이름을 입력 하도록 합니다.

EKS_CREATE_1-3_IAM_ROLE_CREATE_1-1

클러스터 IAM 역활 설정 해야 합니다. 지금 생성하는 EKS 전용 역할 생성을 위해 권장 역할 생성 버튼을 클릭 하도록 합니다.

EKS_CREATE_1-3_IAM_ROLE_CREATE_1-2

신뢰 할 수 있는 엔티티 유형AWS 서비스 로 클릭 합니다. 참고로 AWS 역할 이라는 하는 것은 AWS 자원 끼리 접근 할 수 있는 권한이라고 생각 하면 됩니다. 우리는 AWS EKS 서비스를 AWS EC2 서비스로 접근 할 수 있는 권한을 생성 해야 하기 떄문에 지금과 같은 역할 생성을 하는 겁니다.

서비스 또는 사용 사례 에서 EKS 로 선택하고 사용 사례EKS - Cluster 를 선택 합니다. 이렇게 하면 AWS 에서 권장 하는 역할은 자동 선택 해서 셋팅 해줍니다.

EKS_CREATE_1-3_IAM_ROLE_CREATE_1-3

그러면 AmazonEKSClusterPolicy 선택 하게 해줍니다.

EKS_CREATE_1-3_IAM_ROLE_CREATE_1-4

원하시는 역할 이름을 입력 하도록 합니다.

EKS_CREATE_1-4

다시 EKS Cluster 생성 페이지에서 방금 생성 한 역할 을 선택 하도록 합니다. (바로 옆에 새로고침 버튼 클릭)

EKS_CREATE_1-5

다음은 2단계 네트워킹 지정 입니다. VPC 설정인데 미리 설정 해준 default 로 해서 다음 버튼을 클릭 합니다.

EKS_CREATE_1-6

다음은 3단계 관찰성 구성 페이지 입니다. 기본적으로 셋팅 한것으로 해서 다음 버튼 클릭 합니다.

EKS_CREATE_1-7

다음은 4단계 추가 기능 선택 페이지 입니다. 기본적으로 셋팅 한것으로 해서 다음 버튼 클릭 합니다.

EKS_CREATE_1-8

다음은 5단계 선택한 추가 기능 설정 구성 페이지 입니다. 이것도 기본적으로 셋팅 한것으로 해서 다음 버튼 클릭 합니다.

EKS_CREATE_1-9

검토 및 생성 페이지 입니다. 지금까지 생성한 설정 확인 및 최종 클러스터 생성 합니다.

EKS_CREATE_1-10

20분 정도 지나면 생선한 EKS Cluster 생성 한 것을 확인 할 수 있습니다.

EKS_CREATE_1-11_NODE_CREATE_1-1

컴퓨팅 메뉴를 클릭 해서 이제 노드 그룹 추가 버튼을 클릭 하도록 합니다. 앞써 설명한대로 KubernetiesMaster NodeWorker Node 로 구성 되어 있다고 했습니다.

방금 EKS 생성은 Master Node 를 생성한 것이고, 지금은 노드 그룹 추가 통해 Worker Node 를 생성 하고자 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-2

1단계 노드 그룹 구성 페이지에서 원하시는 노드 그룹명을 지정 하도록 합니다. 그런 후 노드 IAM 역할 을 적용 해야 합니다. 권장 역할 생성 버튼을 클릭 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-3_IAM_ROLE_CREATE_1-1

신뢰 할 수 있는 엔티티 유형AWS 서비스 로 클릭 합니다. 그리고 Worker Node 는 EC2 로 구성하기 때문에 사용 사례EC2 로 설정 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-3_IAM_ROLE_CREATE_1-2

그럼 이미지 권한 정책에서 이미 3개가 선택 되어 있습니다.

EKS_CREATE_1-11_NODE_CREATE_1-3_IAM_ROLE_CREATE_1-3

역할 이름을 지정 한 다음에 다음 버튼을 클릭 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-4

방금 생성한 역할을 선택 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-5

2단계 컴퓨팅 및 조정 구성 설정 페이지 입니다. 생성하고자 하는 Worker Node 인스턴스 유형을 선택 하는 항목 입니다. 일단 실습이기 때문에 원하시는 OS 를 선택해주시고 사양은 t3.small 로 선택 해도 크게 무리는 없습니다.

EKS_CREATE_1-11_NODE_CREATE_1-6

노드 그룹 조정 구성 에서

  1. 원하는 크기: 2
  2. 최소 크기: 2
  3. 최대 크기: 2

로 최소 비용을 위해 설정 하도록 합니다. 실전에서는 서비스에 따라 계산 해서 설정 해야 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-7

3단계 네트워킹 지정 페이지에서 기본값으로 해서 다음 버튼을 클릭 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-8

검토 및 생성 페이지에서 지금까지 설정한 데이터를 확인 후 최종적으로 노드 생성 하도록 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-9

몇분 후 다시 컴퓨팅 메뉴 페이지로 접근 하면 생성한 노드를 확인 할 수 있습니다.

EKS_CREATE_1-11_NODE_CREATE_1-10

만약 나중에 노드 갯수 설정을 변경 하고자 할때 세부 정보 페이지에서 우측 상단에 편집 버튼을 클릭 합니다.

EKS_CREATE_1-11_NODE_CREATE_1-11

초기 설정한 노드 갯수 관련 데이터 값을 확인 할 수 있습니다. 여기서 원하시는 갯수로 설정 한 다음에 수정 하도록 합니다.

AWS CLI 설치 및 kubectl 셋팅 하기

지금까지 AWS EKS 통해 Master NodeWorker Node 를 생성 했습니다. 그럼 개발자가 직접 Local PC 에서 Master Node 로 접근 및 통제를 위해 AWS CLI 그리고 kubectl 설치 를 해야 합니다.

1
https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html#getting-started-install-instructions

해당 페이지로 접근 해서 지금 사용하고 있는 OS 따라 AWS CLI 프로그램 다운로드 및 설치 하도록 합니다.

1
aws --version

설치 확인 합니다.

AWS_CLI_LOCAL_SETTING_1-1

1
aws configure

설치가 끝나면 aws configure 명령어를 입력 합니다. AWS 에 접근 하기 위해 AWS CLI 이용 합니다. 그러기 위해서는 권한 인증을 해야 하는데 사전에 발급받은 AWS access key, AWS secret key 입력 합니다.

다음은 Kuberneties 에 통제 하기 위해 kubectl 셋팅을 해야 합니다. kubectl은 Kubernetes 클러스터를 제어하기 위한 커맨드 라인 인터페이스(CLI) 도구 입니다. kubectl 를 이용해서 개발자는 클러스터에 있는 리소스를 생성, 조회, 업데이트 및 삭제 등 할 수 있게 됩니다.

1
2
3
4
5
6
7
8
## 맥 OS 사용자
brew install kubectl

## 윈도우 사용자
install -e --id Kubernetes.kubectl

## 설치확인
kubectl version --client

kube config 설정 하기

EKS_CREATE_1-11_NODE_CREATE_1-12

1
aws eks update-kubeconfig --region ap-northeast-2 --name ${AWS EKS Cluster 명}

해당 명령어를 이용해서 kube config 를 설정 하도록 합니다. AWS CLI 일부로 AWS EKS Cluster와 상호 작용 하기 위해서 kubectl 구성 파일(kubeconfig)을 자동으로 생성하거나 업데이트 합니다. 해당 명령어를 사용하면 수동으로 구성 파일을 생성하고 관리할 필요 없이, 쉽게 AWS EKS Cluster 와 연결을 설정 할 수 있게 됩니다.

1
kubectl get nodes

지금 현재 kubernetes 에서 설치된 Worker Node 2대를 확인 할 수 있습니다.

Kubernetes NameSpace 생성하기

NAMESPACE_CREATE_DEV_1-1

1
kubectl create namespace dev

dev 이라는 namespace 를 생성 하도록 합니다.

AWS Route53 설정 및 LB로 라우팅 대상 설정 하기

ROUTE53_CREATE_1-1

AWS Route53 서비스에 접속해서 호스팅 영역 -> 호스팅 영역 생성 버튼을 클릭 합니다.

ROUTE53_CREATE_1-2

미리 구입한 도메인을 도메임 이름 영역에 입력 합니다. 그런 다음 호스팅 영역 생성 버튼을 클릭 합니다.

ROUTE53_CREATE_1-3

호스팅 영역 메뉴에 클릭 하면 방금 생성한 호스팅 영역 레코드 영역을 확인 해봅니다. (이 부분은 CI/CD 실습 Part 에서 다시 설명 하도록 하겠습니다.)

ROUTE53_RECODE_CREATE_1-1

도메인 구입하기

GABIA_DOMAIN_CREATE_1-1

가비아 홈페이지에 접속해서 원하시는 도메인을 구입 합니다.

GABIA_DOMAIN_CREATE_1-2

구입한 도메인 관리 페이지로 접속 합니다.

GABIA_DOMAIN_CREATE_1-3

이전에 AWS Route53 에서 호스팅 영역 생성한 생성한 레코드 영역을 입력 합니다. (맨 마지막에 . 은 제거 하고 입력 하도록 합니다.)

Docker Image 생성 및 업로드 하는 방법

AWS ECR Repository 생성 하기

ECR_CREATE_1-1

AWS ECR 서비스로 접근 해서 프라이빗 레포지토리 생성 하도록 합니다.

1
cd ${생성한 프로젝트 디렉토리}

그 다음은 CMD 창을 열어서 해당 프로젝트 디렉토리에 이동 하도록 합니다.

1
2
# 로컬에서 ecr 로그인
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${AWS ECR 생성한 레포지토리명}

Local PC 에서 AWS ECR 로그인을 하도록 합니다.

로컬에서 도커 이미지 빌드 및 push

AWS_ECR_DOCKER_BUILD_1-1

1
docker build -t ${AWS ECR 생성한 레포지토리명}:latest .

해당 프로젝트를 docker image 파일을 만드는 명령어 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 필요프로그램 설치
FROM openjdk:17-jdk-alpine as stage1
ARG SPRING_PROFILE
# 파일 복사
WORKDIR /app
COPY gradle gradle
COPY src src
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .
# 빌드
RUN chmod +x gradlew
RUN ./gradlew bootJar

# 두번째 스테이지
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=stage1 /app/build/libs/*.jar app.jar

# 실행 : CMD 또는 ENTRYPOINT를 통해 컨테이너를 배열 형태의 명령어로 실행
ENTRYPOINT ["java", "-jar", "app.jar"]

해당 프로젝트에는 Dockerfile 이 존재합니다. 미리 준비된 Dockerfile 이용해서 docker image 를 생성 하게 됩니다.

AWS_ECR_DOCKER_PUSH_1-1

1
docker push ${AWS ECR 생성한 레포지토리명}:latest

AWS_ECR_DOCKER_PUSH_1-2

생성한 docker image 를 AWS ECR 해당 레포지토리에 push 하도록 합니다. 그런 다음 정상적으로 docker image 를 생성 했는지 AWS ECR 서비스에 접속해서 확인 하도록 합니다.

지금은 이렇게 하나하나 docker 명령어를 이용해서 AWS ECR Repository 에 docker image 를 등록 했지만 github Action 을 이용해서 자동화 할 예정 입니다.

kubernetes Deployment 및 Ingress Apply 하기

Deployment Apply 하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
apiVersion: apps/v1
kind: Deployment
metadata:
name: project-backend
namespace: dev
spec:
# Deployment나 StatefulSet이 유지할 "옛날 ReplicaSet"의 수를 제한하는 옵션. 자원공간의 낭비 방지 및 관리의 편의성.
revisionHistoryLimit: 2
replicas: 2
selector:
matchLabels:
app: project-backend
template:
metadata:
labels:
app: project-backend
spec:
containers:
- name: project-backend
image: ${AWS ECR REPOSITORY 주소}:latest
ports:
- containerPort: 8080
resources:
# 컨테이너가 사용할수 있는 리소스의 최대치
limits:
cpu: "1"
memory: "500Mi"
# 컨테이너가 시작될떄 보장받아야 하는 최소 자원
requests:
cpu: "0.5"
memory: "250Mi"
env:
# name값과 yml의 ${변수} 의 변수명과 일치해야함
- name: DB_HOST
valueFrom:
secretKeyRef:
name: my-app-secrets
key: DB_HOST
- name: DB_PW
valueFrom:
secretKeyRef:
name: my-app-secrets
key: DB_PW
# 컨테이너 상태 확인을 통해 롤링업데이트 최적화
readinessProbe:
httpGet:
# healthcheck 경로
path: /health
port: 8080
# 컨테이너 시작 후 지연
initialDelaySeconds: 10
# 확인 반복 주기
periodSeconds: 10

---
apiVersion: v1
kind: Service
metadata:
name: project-backend-service
namespace: dev
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
selector:
app: project-backend

앞써 Deployment 학습 관련해서 아직 설명하지 않는 부분이 있습니다.

1
2
spec:
revisionHistoryLimit: 2

앞써 설명 했었지만 Deployment (depl_svc.yml 파일) 에서 내용은 수정 한 것은 없지만 실전에서는 배포하고자 하는 소스코드 수정이 빈번하게 발생 할 것 입니다. 나중에 설명 하겠지만 배포시 GitHub Action 을 이용해서 Docker Image 를 생성하고 생성한 Image 를 AWS ECR Repository 에 등록 하고자 합니다.

다시 말하자면 강제로 (Deployment depl_svc.yml 파일 내용 수정 안해도) 새로운 ReplicaSetPod 를 생성 하고자 하면 rollout restart 명령어를 사용 해야 합니다.

revisionHistoryLimit 는 새로운 ReplicaSetPod 을 몇 개까지 만들 것인가를 설정 한 것 입니다. 해당 설정을 하지 않으면 지속적으로 배포 할때 마다 계속 무한대로 생성하기 때문에 제한을 두어서 관리하기 쉽게 하는 것 입니다.

spec.template.spec.containers.ports.-containerPort: 해당 포트 번호는 배포 하고자 하는 프로젝트의 배포 할때 설정한 포트 번호하고 동일 해야 합니다.

1
2
3
4
5
6
7
8
9
resources:
# 컨테이너가 사용할수 있는 리소스의 최대치
limits:
cpu: "1"
memory: "500Mi"
# 컨테이너가 시작될떄 보장받아야 하는 최소 자원
requests:
cpu: "0.5"
memory: "250Mi"

Pod 는 논리적인 자원 이기 때문에 AWS EC2 자원 안에서 생성 하게 됩니다. AWS EC2 물리적인 자원 내에서 안에 실행 하고자 하는 Pod 가 어느정도 자원을 점유 할 것인지 설정 하는 것 입니다. 즉 해당 컨테이너가 사용 할 수 있는 최소치 와 최대치를 설정 하는 것 입니다. 참고로 이 설정을 안하면 AWS EC2 물리적인 자원을 모두 소유 하기 때문에 다른 Pod 를 생성시 균등하게 배분 할 수 없어서 해당 설정은 필요 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
env:
# name값과 yml의 ${변수} 의 변수명과 일치해야함
- name: DB_HOST
valueFrom:
secretKeyRef:
name: my-app-secrets
key: DB_HOST
- name: DB_PW
valueFrom:
secretKeyRef:
name: my-app-secrets
key: DB_PW

이전에 kubectl create secret generic my-app-secrets 명령어를 통해서 my-app-secrets 이라는 secret 설정 한 데이터 DB_HOST 하고 DB_PW Key 값을 가져오겠다는 의미 입니다.

1
2
3
4
5
6
7
8
9
10
# 컨테이너 상태 확인을 통해 롤링업데이트 최적화 
readinessProbe:
httpGet:
# healthcheck 경로
path: /health
port: 8080
# 컨테이너 시작 후 지연
initialDelaySeconds: 10
# 확인 반복 주기
periodSeconds: 10

readinessProbe 경우는 앞써 설명 한 롤링 업데이트 통해 배포 한다고 말씀드렸습니다. 배포 진행시 새로운 Pod 가 생성 되면 기존 Pod 를 삭제 하는데요. 하지만 새로운 Pod 를 진행 하자 마자 기존 Pod 를 삭제하는 것은 위험한 행동 입니다.

예를들어 Spring Boot 를 서버를 띄우지만 초기화 하는 과정에서 아직 준비 중인데 기존 Pod 를 삭제하면 정상적인 Graceful Shutdown 진행 되지 않습니다. 이를 해결하기 위해서는 Spring Boot 프로젝트 내에 새로운 Pod 생성한 프로세스에서 /health API 통해서 정상적으로 응답이 오면 기존 Pod 를 삭제 하겠다는 설정 입니다.

1
2
3
cd ${프로젝트 디렉토리 이동}
kubectl apply -f ./depl_svc.yml
kubectl get pod -n dev

이제 Deployment 를 Apply 해봅시다. 미리 만들어진 depl_svc.yml 파일 이용해서 Deployment 를 Apply 합니다.

Ingress Apply 하기

INGRESS_CONTROLLER_DEPLOY_1-1

앞써 Ingress 설명 관련해서

1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/aws/deploy.yaml

Ingress Controller 설치를 미래 해야 합니다. 이때 동시에 AWS LB 도 등록 하게 됩니다. AWS 에 가서 Route53 하고 등록된 LB 하고 매핑 작업이 필요 합니다.

ROUTE53_RECODE_CREATE_1-1

AWS Route53 접속해서 특정 Prefix 도메인을 생성된 LB 로 매핑 작업을 하도록 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-backend-ingress
namespace: dev
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: my-issuer
spec:
tls:
- hosts:
- "server.syh8088.store"
# https설정시에 TLS 인증서가 저장된 certificate의 Secret의 이름(spec.secretName)을 지정
secretName: server-syh8088-com-tls
rules:
- host: server.syh8088.store
http:
paths:
- path: / #모든 url요청을 service로 라우팅
pathType: Prefix
backend:
service:
name: project-backend-service
port:
number: 80
1
2
3
4
spec:
tls:
- hosts:
- "${생성한 도메인}"

생성한 도메인을 등록 하도록 합니다.

1
2
3
4
5
6
7
8
9
http:
paths:
- path: / #모든 url요청을 service로 라우팅
pathType: Prefix
backend:
service:
name: project-backend-service
port:
number: 80

모든 요청을 Deployment 에서 설정한 project-backend-service 로 보내겠다는 의미 입니다.

1
2
3
4
5
6
spec:
tls:
- hosts:
- "server.syh8088.store"
# https설정시에 TLS 인증서가 저장된 certificate의 Secret의 이름(spec.secretName)을 지정
secretName: server-syh8088-com-tls

이 부분은 SSL 인증 관련해서 설명 할때 다시 설명 하도록 하겠습니다.

1
2
3
cd ${프로젝트 디렉토리 이동}
kubectl apply -f ./ingress.yml
kubectl get pod -A

Ingress 를 Apply 하는 명령어 입니다. 진행 하도록 합니다.

KUBE_APPLY_ingress_1-1

1
2
kubectl rollout restart deployment project-backend -n dev
kubectl get pods -n syh

이제 rollout restart 명령어를 통해서 정상적으로 롤링 업데이트가 진행 되는지 체크 해봅니다.

HTTPS 통신 인증서 작업

사용자가 로그인 하는 과정에서 아이디 및 패스워드를 입력해서 HTTP 요청을 하게 되면 중간에 요청 데이터를 가로채서 패스워드를 노출 되는 이슈가 발생됩니다. HTTPS 는 ISO Layer 7 에서 요청 데이터를 암호화 해서 이를 해당 서버까지 요청 데이터를 도착 한 후 복호화 해서 처리 하도록 해줍니다. 이를 가능 하기 위해서는 사용자가 로그인 하기전 브라우저 통해 서버로 부터 특정 암호화 키값을 전달 받게 됩니다. 그럼 특정 브라우저는 요청 할때 마다 데이터를 암호화 해서 서버로 전달하게 되고 이를 서버가 미리 준비된 복호화 키로 복호화 해서 처리 하게 됩니다. 이러한 흐름을 HTTPS 통신 이라고 합니다.

즉 서버로 부터 암호화 키값을 전달 받는데 이것을 SSL(TSL) 인증서 라고 합니다. HTTPS 통신이 가능하게 하기 위해서 우선 앞서 설명한 것 처럼 특정 서버가 클라이언트에게 인증서 (암호화 할 수 있는 공개키) 를 전달 하는데 이 인증서는 인증된 기관 CA 에서 발급 받게 됩니다.

우선 인증된 기관 CA 를 발급 받는 과정을 실습 하도록 하겠습니다.

인증서 발급

NAMESPACE_CREATE_cert-manager_1-1

1
2
## cert-manager를 위한 namespace 생성
kubectl create namespace cert-manager

우선 cert-manager 이라는 namespace 를 생성 하도록 합니다.

  • 우리 서버에서 공개키와 비밀키를 생성
  • 공개키와 서버 정보로 CSR(인증서서명요청서)를 생성
  • CA(인증기관)에 CSR을 제출한 후에, CA에서 도메인 소유 여부를 검증한 후에 디지털 서명을 하여 우리 서버로 인증서를 발급

KUBE_APPLY_cert-manager-CRDs_1-1

1
2
## cert-manager CRDs 설치
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.5.0/cert-manager.crds.yaml

cert-manager가 제대로 작동하기 위한 기반 구조 구성 하게 됩니다. 자세히 말하자면 어떤 인증서를 발급 받기 위한 어떤 CA 선택 할 것인가 그리고 선택하고 해당 CA 에게 미리 공개키 및 비밀키를 생성 한 것 중 공개키 하고 사용하고자 하는 도메인 정보 등 CA 에게 전송 하게 됩니다.

그럼 CA 는 이를 확인 하기 위해 요청 한 도메인이 실제로 존재하는지 검증 하게 되는데 정상적으로 검증 되면 최종적으로 인증서 를 발급 받게 됩니다.

이러한 과정은 하나 하나씩 작업을 할 필요 없고 cert-manager CRDs 설치 통해 모든 일련의 과정을 해결 할 수 있습니다. 해당 명령어를 통해 자원을 설치 하도록 합니다.

KUBE_APPLY_cert-manager_deploy_1-1

1
2
3
## cert-manager 배포
## cert-manager 컨트롤러 자체(Pod, Deployment 등)를 설치
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.5.0/cert-manager.yaml

실질적인 cert-manager 컨트롤러 자체(Pod, Deployment 등) 설치 하도록 합니다.

https.yml 내용 보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: my-issuer
namespace: dev
spec:
acme:
# ca 서버 주소. 해당 서버를 통해 인증서 발행
server: https://acme-v02.api.letsencrypt.org/directory
# 인증서 만료 또는 갱신 필요시 알람 email
email: ${본인 이메일}
privateKeySecretRef:
# 인증서를 발급받기 위해 ca와 통신시에 사용되는 Secret 이름
name: my-issuer
solvers:
- http01:
ingress:
# 도메인의 소유권을 확인하기 위해 cert-manager는 nginx기반의 ingress controller를 활용해 검증한다는 의미
class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: server-syh8088-com-tls
namespace: dev
spec:
# cert-manager가 아래 secretName에서 지정한 이름으로 Secret을 생성
secretName: server-syh8088-com-tls
duration: 2160h #90day
renewBefore: 360h #before 15day
issuerRef:
# ClusterIssuer의 이름을 참조
name: my-issuer
kind: ClusterIssuer
commonName: server.syh8088.store
dnsNames:
# 추가적인 서브도메인을 추가하고 싶은경우에 여기에 나열
- server.syh8088.store

1
2
3
4
spec:
acme:
# ca 서버 주소. 해당 서버를 통해 인증서 발행
server: https://acme-v02.api.letsencrypt.org/directory

어떤 CA 기관에서 인증서 발급 받을 것인지 설정 합니다. 여기서는 letsencrypt 이라는 기관에서 인증서를 발급 받습니다.

1
2
3
4
5
6
7
8
9
10
11
12
spec:
acme:
# 인증서 만료 또는 갱신 필요시 알람 email
email: syh8088@naver.com
privateKeySecretRef:
# 인증서를 발급받기 위해 ca와 통신시에 사용되는 Secret 이름
name: my-issuer
solvers:
- http01:
ingress:
# 도메인의 소유권을 확인하기 위해 cert-manager는 nginx기반의 ingress controller를 활용해 검증한다는 의미
class: nginx

spec.acme.email: 인증서 만료 또는 갱신 필요시 알람 email 을 설정 하도록 합니다.
spec.acme.privateKeySecretRef.name: 인증서를 발급받기 위해 ca와 통신시에 사용되는 Secret 이름 입니다.
spec.acme.solvers.http01.ingress.class: 도메인의 소유권을 확인하기 위해 cert-manager는 nginx기반의 ingress controller를 활용해 검증한다는 의미

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: server-syh8088-com-tls
namespace: dev
spec:
# cert-manager가 아래 secretName에서 지정한 이름으로 Secret을 생성
secretName: server-syh8088-com-tls
duration: 2160h #90day
renewBefore: 360h #before 15day
issuerRef:
# ClusterIssuer의 이름을 참조
name: my-issuer
kind: ClusterIssuer
commonName: server.syh8088.store
dnsNames:
# 추가적인 서브도메인을 추가하고 싶은경우에 여기에 나열
- server.syh8088.store

spec.secretName: cert-manager가 아래 secretName에서 지정한 이름으로 Secret을 생성 합니다. 중요한 것은 Ingress.yml 에서 spec.tls.- hosts.secretName 에서 설정한 값하고 동일 해야 합니다.
spec.commonName: 대표하는 도메인 이름 입니다.
spec.dnsNames: 추가적인 서브도메인을 추가하고 싶은경우에 여기에 나열 하도록 합니다. 예를들어 admin.syh8088.store 이라는 서브도메인도 추가 하고 싶으면 추가 하면 됩니다.

KUBE_APPLY_https_1-1

1
2
3
## 인증서 요청 명령어
cd ${프로젝트 디렉토리}
kubectl apply -f ./https.yml

https.yml apply 하게 되면 cert-manager 가 이 두 자원의 정보를 보면서 CA 기관의 인증서를 요청하게 됩니다.

1
2
3
4
5
kubectl get certificate -n dev

결과 ->
NAME READY SECRET AGE
server-syh8088-com-tls True server-syh8088-com-tls 18m

certificate 조회 하도록 합니다. READY 에서 True 가 나오면 정상적으로 SSL 인증이 완료 되었다는 의미 입니다.

1
kubectl get secret -A

정상적으로 server-syh8088-com-tls 이라는 시크릿 자원이 생성 되는지도 확인 해봅니다.

이제부터 HTTPS 통신이 가능하게 됩니다.

Github Actions 을 이용해서 CI/CD 자동화 배포 하기

GitHub ActionsGitHub 에서 제공하는 직접 CI/CD 파이프라인을 자동화 할 수 있는 기능 입니다. GitHub Actions 경우는 Workflows 이라는 개념이 있습니다. Workflows 는 하나 이상의 Job 포함 하게 되는데 특정 브랜치에 Git Push 를 하게 된다면 Workflows 에서 정의된 Job 이 실행 하게 됩니다.

Workflows 정의 하는 방법은 .github/workflows 디렉토리에 YAML 형식의 파일로 저장 해야 합니다. 그럼 deploy_dev_project_with_k8s.yml 파일을 확인 해보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
name: deploy DEV backend

on:
push:
branches:
- dev

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: checkout github
uses: actions/checkout@v2

- name: install kubectl
uses: azure/setup-kubectl@v3
with:
version: "v1.25.9"
id: install

- name: configure aws
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET }}
aws-region: ap-northeast-2

- name: update cluster infomation
run: aws eks update-kubeconfig --name eks_syh --region ap-northeast-2

- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: build and push docker image to ecr
env:
REGISTRY: 057026365119.dkr.ecr.ap-northeast-2.amazonaws.com
REPOSITORY: eks-syh-spring-ci-cd
IMAGE_TAG: latest
run: |
docker build \
-t $REGISTRY/$REPOSITORY:$IMAGE_TAG \
-f ./Dockerfile .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

- name: eks kubectl apply
run: |
kubectl rollout restart deployment project-backend -n dev
1
2
3
4
on:
push:
branches:
- dev

on.push.branches: 특정 어떤 브랜치로 push 해야 Workflows 실행 한 것인지 설정 하는 구간 입니다.

1
2
3
jobs:
build-and-deploy:
runs-on: ubuntu-latest

jobs.build-and-deploy.runs-on: CI/CD 작업이 이루어지는 서버 환경 셋팅 부분 입니다. 지금 우리가 특정 프로젝트를 Bulid 를 진행하고 docker image 를 만든 다음에 AWS ECR Repository 에 등록 하는 작업이 필요합니다. 이러한 일련의 작업을 수행하기 위한 가상 서버 환경을 GitHub 에서 제공 해주는데 어떤 OS 로 셋팅 하겠는가 설정 하는 부분 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: checkout github
uses: actions/checkout@v2

- name: install kubectl
uses: azure/setup-kubectl@v3
with:
version: "v1.25.9"
id: install

GitHub 에서 제공하는 가상 서버에 actions/checkout@v2 명령어를 통해서 해당 프로젝트를 Git Clone 하겠다는 의미 입니다.

1
2
3
4
5
6
7
8
9
10
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: install kubectl
uses: azure/setup-kubectl@v3
with:
version: "v1.25.9"
id: install

azure/setup-kubectl@v3 명령어를 통해서 GitHub 에서 제공하는 가상 서버에 kubectl 설치 하겠다는 의미 입니다.

1
2
3
4
5
6
7
8
9
10
11
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: configure aws
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET }}
aws-region: ap-northeast-2

aws-actions/configure-aws-credentials@v1 명령어를 수행 하게 되면 AWS CLI 설치 및 Configure 계정 셋팅 하겠다는 의미 입니다. 계정 셋팅시 GitHub 에서 제공하는 가상 서버에 해당 AWS 자원에 접근 하기 위한 ACCESS-KEYSECRET 값을 가져와서 계정 셋팅을 하게 되는데요. 변수로 지정된 secrets 값은 나중에 설명 하겠지만 GitHub Secrets and variables 페이지에서 미리 셋팅 해야 합니다.

1
2
3
4
5
6
7
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: update cluster infomation
run: aws eks update-kubeconfig --name ${AWS EKS kubernetes Cluster 이름} --region ap-northeast-2

aws eks update-kubeconfig 명령어를 이용해서 해당 AWS EKS kubernetes Cluster 이름 으로 접근 하겠다는 의미 입니다.

1
2
3
4
5
6
7
8
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

docker image 만들고 imageAWS ECR Repository 에 등록하기 위해서는 AWS ECR 로그인 작업도 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: build and push docker image to ecr
env:
REGISTRY: ${AWC ECR Repository 주소}
REPOSITORY: ${AWC ECR Repository 이름명}
IMAGE_TAG: latest
run: |
docker build \
-t $REGISTRY/$REPOSITORY:$IMAGE_TAG \
-f ./Dockerfile .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

Dockerfile 파일을 이용해서 docker image 만들고 이것을 AWS ECR 지정된 Repository 에 push 하겠다는 의미 입니다.

1
2
3
4
5
6
7
8
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:

- name: eks kubectl apply
run: |
kubectl rollout restart deployment project-backend -n dev

AWS ECR 에 등록된 docker image 에 당겨와서 docker image 기반으로 Replicaset 및 Pod 를 생성 하겠다는 의미 입니다.

github secret 에 중요 정보 추가 방법

GITHUB_SECRET_SETTING_1-1

GitHub 해당 프로젝트 접속 하셔서 Settings 페이지에 왼쪽 메뉴에서 Secrets and variables -> Actions 접근 하도록 합니다.

GITHUB_SECRET_SETTING_1-2

New repository secret 버튼을 클릭 합니다.

GITHUB_SECRET_SETTING_1-3

Name 에 AWS_KEY 이라는 이름을 지정하고 Secret 값에는 AWS Access-Key 값을 입력하고 Add secret 버튼을 클릭 합니다.

GITHUB_SECRET_SETTING_1-4

Name 에 AWS_SECRET 이라는 이름을 지정하고 Secret 값에는 AWS Secret 값을 입력하고 Add secret 버튼을 클릭 합니다.

GIT_HUB_Actions_Deploy_1-1

한번 특정 브랜치를 Git Push 를 하도록 합니다. 그럼 자동으로 Workflows 실행 하게 됩니다.

GIT_HUB_Actions_Deploy_1-2

실제로 CI/CD 및 롤링 업데이트 진행 되는지 확인 해보기

1
kubectl get pod -n ${네임스페이스명}

Pod 조회를 하게된다면 Pod 설치가 얼마 안된 것을 확인 할 수 있습니다.

GIT_HUB_Actions_Deploy_1-3

이번에 해당 스프링 부트 프로젝트 에서 HealthController 파일에서 health API 응답을 ok3 로 변경 해서 다시 push 하도록 하겠습니다.

GIT_HUB_Actions_Deploy_1-4

즉시 Pod 조회를 하게된다면 정상적으로 롤링업데이트 가 진행 되는 것을 확인 할 수 있습니다.

GIT_HUB_Actions_Deploy_1-5

Github Workflows 페이지에 접속하면 정상적으로 성공 했다고 나옵니다.

GIT_HUB_Actions_Deploy_1-6

지속적으로 Pod 를 조회 하게 된다면 새로운 Pod 가 생성 되기 전까지 기존 Pod 는 계속 살아 있고 새로운 Pod 가 최종 생성 되면 기존 Pod 는 삭제 되는 것을 확인 할 수 있습니다.

GIT_HUB_Actions_Deploy_1-7

https//server.syh8088.store/health API 호출하면 수정한 응답값 ok3 로 나타나는 것을 확인 할 수 있습니다.

Autoscaling - Pod 자동 확장 작업

Autoscaling Horizontal Pod Autoscaler (HPA) 설명 POD 출처: https://www.apptio.com/topics/kubernetes/autoscaling/horizontal/

Kubernetes 에서 DeploymentAutoscaling 설정 하기 위해서 Horizontal Pod Autoscaler (HPA) 이라는 자원을 사용 하게 됩니다. Horizontal Pod Autoscaler (HPA) 이란 CPU 사용량 등 사용자 정의 파라미터에 기반하여 파드의 수를 자동으로 조정 기능 입니다.

Autoscaling HPA 동작 원리 출처: https://www.apptio.com/topics/kubernetes/autoscaling/horizontal/

Autoscaling 동작 원리는 우선 메트릭 서버HPA 에서 관리 되는 Pod 를 계속 모니터링 하게 됩니다. 모니터링 한 데이터를 HPA 에게 보고 하게 되는데요. 계속 주시하고 있다가 HPA 는 특정 서버가 최소 및 최대 임계치 설정을 관리 하고 있어서 모니터링 서버 로부터 받은 서버 자원 사용률 한계치가 넘어가게 된다면 Deployment 에게 Pod 를 더 늘리라고 명령을 내리게 됩니다. 그럼 DeploymentPod 를 더 생성하게 됩니다.

적용하는 단계는 크게 3가지로 구분 할 수 있습니다.

  1. Deployment 설정
  2. 메트릭 서버 설치
  3. HPA 스크립트 생성 후 적용

하나하나씩 알아보도록 하겠습니다.

Autoscaling - Pod 자동 확장 작업 - Deployment 설정

1
cd ${프로젝트 ROOT 디렉토리}\k8s\k8s-project

depl_svc.yml 파일을 확인 및 수정 하기 위해 depl_svc.yml 파일을 열어봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

## depl_svc.yml 파일
apiVersion: apps/v1
kind: Deployment
metadata:
name: project-backend
namespace: dev
spec:
# Deployment나 StatefulSet이 유지할 "옛날 ReplicaSet"의 수를 제한하는 옵션. 자원공간의 낭비 방지 및 관리의 편의성.
revisionHistoryLimit: 2
replicas: 2
selector:
matchLabels:
app: project-backend
... (생략) ...
spec:
containers:
- name: project-backend
image: 057026365119.dkr.ecr.ap-northeast-2.amazonaws.com/eks-syh-spring-ci-cd:latest
ports:
- containerPort: 8080
resources:
# 컨테이너가 사용할수 있는 리소스의 최대치
limits:
cpu: "1"
memory: "500Mi"
# 컨테이너가 시작될떄 보장받아야 하는 최소 자원
requests:
cpu: "0.5"
memory: "250Mi"

... (생략) ...

해당 Podresource 사전 제한 설정 필요 합니다. 중요한 것은 Pod 가 생성 및 삭제 하는 과정에서 cpu 가 갑자기 튈수 있으므로 HPAresource limit 의 Cpu 를 여유롭게 셋팅 해야 합니다.

Autoscaling - Pod 자동 확장 작업 - 메트릭 서버 설치

autoscaling_prod_1-1

1
2
## 메트릭 서버 설치
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

메트릭 서버의 역할은 HPAPod 의 리소스 사용량을 지속적으로 모니터링하고 이를 기반으로 자동 확장을 수행할 수 있도록 필요한 메트릭 데이터를 제공 합니다. 메트릭 서버 를 apply 하도록 합니다.

Autoscaling - Pod 자동 확장 작업 - HPA 스크립트 생성 후 적용

1
2
3
## hpa.yml 설치
cd ${프로젝트 ROOT 디렉토리}\k8s\k8s-project
kubectl apply -f ./hpa.yml

HPA 를 apply 하도록 하겠습니다.

hpa.yml 파일

autoscaling_prod_1-2

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: project-backend-hpa
namespace: dev
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: project-backend
minReplicas: 2
maxReplicas: 4
targetCPUUtilizationPercentage: 50

spec.scaleTargetRef.name: project-backend 값으로 셋팅 하고 있는데 depl_svc.yml 파일에 spec.selector.matchLabels 이름하고 동일하게 매칭 해야 합니다.
spec.minReplicas: Pod 갯수를 최소 2대로 유지 하겠다는 의미 입니다. 참고로 depl_svc.yml 파일에서 spec.replicas 값이 서로 틀리면 hpa.yml 파일에서 설정한 값이 더 우선권이 높습니다.
spec.maxReplicas: Pod 갯수 최대 4대로 유지 하겠다는 의미 입니다.
spec.targetCPUUtilizationPercentage: cpu 사용률이 50% 까지 점유하면 Pod 를 증가 하도록 하겠다는 의미 입니다.

부하 테스트 통해 Pod Autoscaling 되는지 확인 해보기

autoscaling_prod_1-3

첫번재 CMD 창을 실행해서

1
kubectl get hpa project-backend-hpa -n dev -w

현재 Pod 자원 조회 하도록 합니다.

autoscaling_prod_1-4

1
kubectl get pods -n dev

두번재 CMD 창을 실행해서 설치된 Pod 조회 합니다.

1
kubectl exec -it {Pod name} -n dev -- /bin/sh # pod 접속

2개 중 하나를 선택해서 해당 컨테이너로 exec 명령어를 이용해 접속 하도록 합니다.

autoscaling_prod_1-5

1
apk add --no-cache curl # curl 설치

서버부하를 위해서 미리 curl 설치를 하도록 합니다.

autoscaling_prod_1-6

1
while true; do curl -s http://project-backend-service/health; done

실제로 while 문을 이용해서 /health API 를 계속 호출 시켜 강제로 서버부하를 일으키도록 합니다.

autoscaling_prod_1-7

첫번째 CMD 창에서 계속 모니터링 해보면 지속적으로 CPU 점유율이 높아지는 것을 확인 할 수 있습니다.

autoscaling_prod_1-8

세번째 CMD 창을 띄어서 kubectl get pod -n ${namespace 명} 확인 해보면 Pod 가 계속 생성 되고 있는 것을 확인 할 수 있습니다.

autoscaling_prod_1-9

두번째 CMD 창에서 서버부하를 멈추게 된다면 일정 시간 흐른 후 Pod 최소 갯수인 2개까지 줄어가는 것을 확인 할 수 있습니다.

Autoscaling - EC2 자동 확장 작업

Pod 통해 Autoscaling 만으로 물리적인 자원 한계치로 인해 더 이상 Pod 가 증설 할 수 없는 상황이 분명 있을 수 있습니다. 더 이상 증설 할 수 없는 PodPending 상태로 변경이 되는데요.

Autoscaling-EC2-architecture

이미지에 보시는 것 처럼 Pending 상태인 Pod 가 생성되고 Kubernetes Cluster Autoscaler 는 이것을 계속 모니터링 및 감지 하고 노드를 관리하고 있는 AWS Auto Scaling 그룹에 노드를 증설 시켜달라고 요청을 보내게 됩니다.

autoscaling_ec2_1-1

해당 AWS EKS Cluster 접속 하도록 합니다. 여기서 Auto Scaling 그룹 이라고 확인 할 수 있습니다. 실질적으로 노드를 관리 하는 역활을 담당 하고 있습니다.(EKS 가 아님) Auto Scaling 그룹 을 클릭 하도록 합니다.

여기서 최소 노드 갯수 및 최대 노드 갯수를 확인 할 수 있습니다.

Autoscaling EC2 자동 확장 - NodeGroup(ASG)에 태그 추가 작업 진행

지금까지 설명한 flow 를 진행하기 위해서는 우선 NodeGroup(ASG)에 태그 추가 작업 진행 해야 합니다. 이 태그값의 의미는 ASG에 대한 통제권을 Kubernetes Cluster Autoscaler 한테 맡긴다는 의미입니다. 작업을 진행 하도록 하겠습니다.

autoscaling_ec2_1-2

우선 AWS EKS 해당 클러스터에 접근 해서 Auto Scaling 그룹 을 클릭해서 접속 하도록 합니다.

  1. k8s.io/cluster-autoscaler/eks_syh
  2. k8s.io/cluster-autoscaler/enabled

해당 값을 확인 할 수 있는데요. k8s.io/cluster-autoscaler/eks_syh 키 값 value 값을 true 설정 해야 하고 k8s.io/cluster-autoscaler/enabled 키 값 value 값을 owned 로 설정 해야 합니다.

다시 말하자면 이 태그값을 하므로써 ASG에 대한 통제권을 Kubernetes Cluster Autoscaler 한테 맡긴다는 의미이고, Kubernetes Cluster AutoscalerEC2 를 증가 해달라고 요청이 오면 ASGEC2를 증가하게 되는 클러스터 오토스케일러의 통제를 받습니다.

Autoscaling EC2 자동 확장 - ID 제공업체 추가

다시 전체적인 흐름을 설명하자면 Pod 를 생성하는데 있어서 EC2 자원이 부족해 생성하고자 하는 PodPending 상태가 되면 Kubernetes Cluster AutoscalerASG 에게 EC2 자원을 증설 해달라고 요청 하게 됩니다. 이렇게 진행하기 위해서는 Kubernetes Cluster AutoscalerAWS EkS 전체 자원에 대한 모니터링 할 수 있는 권한을 가지고 있어야 합니다. 그리고 또 Kubernetes Cluster AutoscalerASG 에게 요청 할 수 있는 권한도 필요 합니다. 그리고 마지막으로 EC2 에 대한 권한도 필요 합니다.
Kubernetes Cluster Autoscaler 가 필요한 권한 목록

  1. AWS EkS 서비스 전체 자원에 대한 모니터링 할 수 있는 권한
  2. ASG 에게 요청 할 수 있는 권한
  3. EC2 권한 필요

이렇게 설정하기 위해서는 AWS IAM 에 들어가서 역할 즉 AWS 서비스 간에 접근 할 수 있는 정책 을 생성해서 부여 해야 합니다. 즉 Kubernetes Cluster Autoscaler 가 3가지 접근 할 수 있는 역할 을 생성 해야 합니다.

그런데 AWS 에서 역할 이라는 것은 AWS 서비스 (EX: EC2) 간에 권한 부여 하는 것 입니다. 하지만 Kubernetes Cluster Autoscaler 경우는 AWS EKS 안에 있는 Pod 이기 때문에 AWS 서비스 단위 라고 불리기는 어렵습니다. 이러한 부분을 해결 하기 위해서는 Kubernetes Cluster Autoscaler 이라는 Pod 단위를 신뢰 할 수 있는 엔티티 로 등록 시켜줘야 합니다.

EKS 상에서 실행되고 있는 수많은 요소들이 있을 건데 이 요소들이 AWS 상에서 독립적으로 나 신뢰할 수 있는 요소야 라고 등록이 되려면 그 보증을 EKS가 해주게 됩니다. EKS 는 AWS 서비스 자원이기 때문에 ID 제공 업체로 추가 해주고 추가된 Id 값을 이용해서 Kubernetes Cluster Autoscaler 이라는 Pod신뢰할 수 있는 엔티티다 라고 등록을 해주는 작업을 진행 해야 합니다.

autoscaling_ec2_1-4

AWS EKS 서비스에 접근해서 OpenID Connect 공급자 URL 복사 하도록 합니다.

autoscaling_ec2_1-3

AWS IAM 서비스로 접근 해서 왼쪽 메뉴에 ID 제공 업체 버튼을 클릭 해서 공급자 추가 버튼을 클릭 하도록 합니다.

autoscaling_ec2_1-5

공급자 유형을 OpenID Connect 선택 합니다. 그리고 공급자 URL 를 복사 했던 OpenID Connect 공급자 URL 값을 붙여넣기 하도록 합니다. 여기서 대상은 sts.amazonaws.com 로 입력 하도록 합니다.

STS 경우는 AWS 에서 제공 해주는 보안 토큰 서비스 입니다. Kubernetes Cluster Autoscaler 같은 PodSTS`에 Role 사용 요청 할 때 사용 합니다.

Autoscaling EC2 자동 확장 - IAM 역할 생성

autoscaling_ec2_1-6

AWS IAM 서비스로 접근 해서 왼쪽 메뉴에 역할 버튼을 클릭하고 역할 생성 버튼을 클릭 하도록 합니다.

autoscaling_ec2_1-7

신뢰할 수 있는 엔티티 유형을 사용자 지정 신뢰 정책 로 선택 합니다. 그리고 사용자 지정 신뢰 정책 을 구성 해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/<YOUR_EKS_OIDC_PROVIDER>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"<YOUR_EKS_OIDC_PROVIDER>:sub": "system:serviceaccount:kube-system:cluster-autoscaler"
}
}
}
]
}

<AWS_ACCOUNT_ID>: AWS 계정 아이디값
<YOUR_EKS_OIDC_PROVIDER>: AWS EKS 서비스에 접근해서 OpenID Connect 공급자 URL (Http 프로토콜 제외)

입력 하고 다음 버튼 클릭 합니다.

autoscaling_ec2_1-8

권한을 추가 해야 하는데요.

  1. AmazonEKSClusterPolicy
  2. AutoScalingFullAccess
  3. AmazonEC2FullAccess

이렇게 추가 해서 다음 버튼 클릭 하도록 합니다.

autoscaling_ec2_1-9

역할 이름을 지정 하고 최종 확인 버튼을 클릭 합니다.

부하테스트 통해 실제로 Node 가 자동 생성 되는지 확인 해보기

autoscaling_ec2_1-10

AWS EKS 서비스에 들어가서 노드 최대 갯수를 2 에서 3으로 수정 하도록 합니다.

autoscaling_ec2_1-11

실제로 cluster-autoscaler 역할을 하고 있는 autoscaler.yml 파일에 들어가서 수정 작업이 필요 합니다.

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
name: cluster-autoscaler
namespace: kube-system
annotations:
eks.amazonaws.com/role-arn: ${ARN 주소값}

eks.amazonaws.com/role-arn: 방금 생성한 cluster-autoscaler 역할 ARN 주소값

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
containers:
- image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.29.0
name: cluster-autoscaler
resources:
limits:
cpu: 100m
memory: 600Mi
requests:
cpu: 100m
memory: 600Mi
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --cluster-name=${AWS EKS Cluster 이름}
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/eks_syh
env:
- name: AWS_REGION
value: "ap-northeast-2"

--cluster-name: ${AWS EKS Cluster 이름}
--node-group-auto-discovery=asg:tag: k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/${AWS EKS Cluster 이름}

이렇게 수정해서 autoscaler.yml apply 하도록 합니다.

1
2
cd ${프로젝트 ROOT 디렉토리}\k8s\k8s-project
kubectl apply -f ./autoscaler.yml

hpa.yml 파일에 들어가서 maxReplicas 10개로 수정 및 targetCPUUtilizationPercentage 30 으로 수정하고 다시 apply 하도록 합니다.

1
2
## hpa.yml 수정
kubectl apply -f ./hap.yml

autoscaling_ec2_1-12

첫번재 CMD 창을 실행해서

1
kubectl get hpa project-backend-hpa -n dev -w

현재 Pod 자원 조회 하도록 합니다.

autoscaling_ec2_1-13

1
kubectl get pods -n dev

두번재 CMD 창을 실행해서 설치된 Pod 조회 합니다.

1
kubectl exec -it {Pod name} -n dev -- /bin/sh # pod 접속

2개 중 하나를 선택해서 해당 컨테이너로 exec 명령어를 이용해 접속 하도록 합니다.

autoscaling_ec2_1-14

1
apk add --no-cache curl # curl 설치

서버부하를 위해서 미리 curl 설치를 하도록 합니다.

1
while true; do curl -s http://project-backend-service/health; done

실제로 while 문을 이용해서 /health API 를 계속 호출 시켜 강제로 서버부하를 일으키도록 합니다.

autoscaling_ec2_1-15

첫번째 CMD 창에서 계속 모니터링 해보면 지속적으로 CPU 점유율이 높아지는 것을 확인 할 수 있습니다.

autoscaling_ec2_1-16

노드 자원 한계로 인해 더 이상 Pod 가 생성 되지 않고 Pending 상태 인걸로 볼 수 있습니다.

autoscaling_ec2_1-17

1
2
'Pending' 된 pod describe 확인 해보기
kubectl describe pod ${'Pending' 된 pod NAME} -n dev

Pendingpod describe 확인 해보면 자원이 부족 하다는 에러 메시지를 확인 할 수 있습니다.

autoscaling_ec2_1-18

조금 기다려보면 PendingPodRunning 상태로 변환 되는 것을 확인 할수 있습니다.

autoscaling_ec2_1-19

실제로 AWS EKS 노드그룹에서 확인 해보면 2개 였던 노드가 2개로 증설 되었다는 것을 확인 할 수 있습니다.

autoscaling_ec2_1-20

두번째 CMD 창에서 서버부하를 멈추게 된다면 일정 시간 흐른 후 다시 Pod 를 조회 하면 2개의 Pod 로 줄어든 것을 확인 할 수 있습니다.

autoscaling_ec2_1-21

AWS EKS 노드그룹에서 확인 해보면 3개 였던 노드 갯수가 2개로 줄어들었습니다.

autoscaling_ec2_1-22

Cpu 점유율이 점점 줄어 들고 있는 것을 확인 할 수 있습니다.


Copyright 201- syh8088. 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.

버튼

×

喜欢就点赞,疼爱就打赏