안녕하세요 채(彩)입니다.
오늘은 AWS Cloud9을 활용하여 Kubernetes서비스인 AWS EKS로 웹 애플리케이션을 구축해보겠습니다.
독자분들께서 Kubernetes에 대해서 초~중급수준의 지식을 보유하고 계시다면 이해하기 쉬울 것이라 생각됩니다.
다음 실습은 AWS hand-on workshop을 참고하여 진행하였습니다.
참조 링크 : https://catalog.us-east-1.prod.workshops.aws/
오늘의 실습 진행 과정은 Cloud9 환경 구성, eksctl을 통해 EKS 클러스터 배포, ECS repo 생성하여 이미지 등록, Ingress controller 생성, 서비스 배포 순서로 진행됩니다.
우선 EKS를 이해하기 위해서 Kubernetes의 Component부터 차근차근 살펴보겠습니다.
Kubernetes를 배포하면 Cluster가 생성됩니다. Cluster는 쉽게 설명하자면 노드들의 집합이라고 생각하시면 되겠습니다.
Kubernetes Cluster는 아래와 같이 구성됩니다.
그림을 보시면 Control Plane과 Worker Node들로 구분되어 있는 것을 확인하실 수 있습니다.
Control Plane과 Worker Node들의 역할은 아래와 같습니다.
그렇다면 지금부터는 Control Plane과 Wokrer Node들에 있는 etcd, controller manager, scheduler, kube api server에 대해서 간단하게 살펴보겠습니다.
1. ETCD
먼저 ETCD 입니다.
etcd는 쉽게 말해서 Kubernetes의 데이터 저장소로 Key-Value 쌍으로 클러스터에 관한 정보를 저장합니다.
예를들면 Nodes, PODs, Configs, Secrets, Accounts 등 클러스터에 관한 정보들을 저장합니다.
또한 클러스터에 대한 변경사항 역시 ETCD에 기록됩니다.
추가 Nodes, 추가 POD배포, 추가 replicaset 같은 정보들도 업데이트하고 관리합니다.
2. Kube-apiserver
다음으로는 Kube-apiserver입니다.
Kube-apiserver는 etcd와 상호작용하는 유일한 Component이며 다른 Component들에게 명령을 전달해주는 전달자 역할을 하기도 합니다.
아래 그림을 바탕으로 POD를 하나 생성하겠다고 가정한다면 kube-apiserver는 아래와 같은 흐름으로 POD를 생성해줍니다.
1. 포드 생성 요청
2. kubectl은 kube-apiserver에 먼저 사용자 인증(Authenticate User)을 합니다.
3. kube-apiserver에서 적절한 요청인지 유효성 검증(Validate Request)합니다.
4. apiserver는 노드에 할당하지 않고 POD 객체를 생성합니다.
5. ETCD 서버에 정보를 업데이트 합니다.
6. ETCD는 포드가 생성된 사용자를 업데이트 합니다.
7. kube-scheduler는 apiserver를 계속 모니터링하고 있다가 새로운 파드가 있다는 것을 깨닫습니다.
8. 할당된 노드가 없다는 것도 알게됩니다.
9. kube-scheduler는 포드를 배치하기 위해 Node를 식별합니다.
10. 식별한 노드를 apiserver에 전달합니다.
11. apiserver는 etcd에 정보를 업데이트 합니다.
12. apiserver는 워커노드의 kubelet에게 정보를 전달합니다.
13. 정보를 받은 kubelet은 CRI (Container Runtime Engine)에 지시합니다.
14. APP 이미지를 배포합니다.
15. 완성되면 kubelet이 상태를 apiserver에 전달합니다.
16. apiserver는 etcd에 데이터를 업데이트합니다.
이처럼 kube-apiserver는 Cluster의 각 Component들과 상호작용하는 역할을 합니다.
3. Controller manager
Contoller manager는 다양한 컨트롤러들을 관리합니다.
Kubernetes는 다양한 Controller들을 가지고 있습니다. 아래 그림을 참조하시면 얼마나 많은 Controller들이 Kubernetes에 존재하는지 아실수 있습니다.
즉, Controller manager는 kube-apiserver로부터 명령을 전달받게 되면 내부의 Controller들의 수행 상태를 관리하는 역할을 하게 됩니다.
4. Scheduler
Scheduler는 Kubernetes내의 POD를 Scheduling합니다.
즉, 어떤 POD가 어느 Node위에 올라갈지를 결정합니다. 다만 실제로 POD를 Node에 배치하지는 않습니다. 이는 뒤에 나올 Kubelet의 역할입니다.
그림을 통해 Scheduler의 작동 원리를 보기 쉽게 파악해 보겠습니다.
이렇듯 Scheduler는 어느 POD가 어떤 Node에 올라가야 적합할지 결정 해 주는 역할을 합니다.
Kube Scheduler는 리소스 요구사항, 테인트 & 톨러레이션, 노드셀렉터 & 어피니티 등의 설정에 대해서도 POD를 스케쥴해주는 역할을 합니다.
5. Kubelet
Kubelet이 하는 일은 직관적입니다.
Worker Node를 Kubernetes Cluster에 등록해줍니다. 또한 스케쥴러가 지시한 대로 컨테이너를 생성해주고 정기적으로 Nod와 Pod의 상태에 대해 레포트를 Kube-apiserver에 전송합니다.
Kubelet은 Kubeadm을 사용하여 설치하여도 자동으로 배포되지 않는 컴포넌트 입니다. 따라서 수동으로 설치를 해주어야 하는 것을 인지하고 계셔야겠습니다.
기본적인 Component들의 개념은 여기까지 하고 지금부턴 실습으로 넘어가겠습니다.
1. AWS Cloud9 시작하기
- AWS Cloud9 콘솔창에 접속한 후 Create enviroment 버튼을 클릭하여 환경을 만들어줍니다
- Name을 적절히 입력하고 New EC2 instance를 선택합니다.
- 필자는 실습 환경의 원활한 진행을 위해 t3.medium 인스턴스 타입을 선택했습니다.
- OS는 Amazon Linux2를 선택하고 Timeout (해당 시간동안 입력 없을시 절전모드 전환)은 1Day로 설정했습니다. 이 부분은 독자 여러분의 편의대로 선택하셔도 무방합니다.
- AWS Systems Manager로 접속할지 Secure Shell로 접속할지 선택해줍니다. 여기서는 편의를 위해 AWS Systems Manager로 진행했습니다.
- 적절한 VPC와 서브넷을 선택해 줍니다. 저는 Public Subnet으로 생성하였습니다.
- 이후 Create를 눌러 생성을 진행합니다.
Cloud9을 실행하고 해당 화면이 나오면 정상적으로 Cloud9이 시작된 것입니다.
2. IAM 역할 만들고 IDE에 부여하기.
- IAM Role 페이지 접속 - AWS Service & EC2 사용사례 선택 - AdministratorAccess 정책 선택
- 적절한 Role name을 입력 후 Create role을 클릭하여 IAM을 생성해줍니다.
- 위 사진처럼 생성되었으면 다음으로 넘어갑니다.
- Cloud9 환경은 EC2 인스턴스로 구동됩니다. 따라서 EC2 콘솔에서 AWS Cloud9 인스턴스에 방금 생성한 IAM Role을 부여하겠습니다.
- 생성한 인스턴스 클릭 후 - 보안 - IAM 역할 수정을 클릭합니다.
- 방금 생성한 IAM Role을 선택하고 IAM 역할 업데이트를 클릭해줍니다.
IDE에서 IAM 설정 업데이트하기
- AWS Cloud9의 경우 IAM Credentials를 동적으로 관리합니다. 해당 credentials는 EKS IAM authentication과 호환되지 않기에 이를 비활성화 하고 IAM Role역할을 붙혀줍니다.
1. AWS Cloud9 콘솔창에서 생성한 IDE로 접속한 후, 우측 상단에 톱니바퀴 아이콘을 클릭합니다.
2. 이후 좌측 사이드바에서 AWS SETTINGS를 클릭합니다
3. Credntials 항복에서 AWS managed temporary credentials 설정을 비활성화 해줍니다.
4. Temporary credentials이 없는지 확실히 하기 위해 기존의 자격 증명 파일도 제거합니다.
rm -vf ${HOME}/.aws/credentials
5. 앞서 생성한 IAM Role이 제대로 적용 되었는지 확인합니다. 필자는 Charon-cloud9-admin으로 Role을 생성하였으므로, 독자분들은 grep 뒤에 각자 생성한 IAM 이름을 입력해줍니다. 결과값이 출력되면 성공입니다.
aws sts get-caller-identity --query Arn | grep Charon-cloud9-admin
2. AWS CLI 업데이트하기
- Cloud9 IDE에는 기본적으로 AWS CLI 1.x 버전이 설치되어 있습니다. 아래 명령어를 입력하고 AWS CLI 버전을 2.x로 업그레이드합니다.
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
export PATH=/usr/local/bin:$PATH
source ~/.bash_profile
- 버전을 확인해 봅시다. 2.x version이 출력되면 정상입니다.
aws --version
3. Kubectl 설치하기
- https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html 해당 링크를 참조해서 본인의 OS와 CPU에 맞는 Kubectl을 다운로드 해줍니다. 필자는 amd64 / kubectl 1.25를 설치하도록 하겠습니다.
- 아래 명령어는 kubectl을 설치하고 바이너리에 실행 권한을 적용 후 $HOME/bin 셀을 열때 구성되도록 셀 초기화 파일의 경로를 추가해 주었습니다.
sudo mkdir /usr/local/bin/kubectl
sudo curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.25.6/2023-01-30/bin/linux/amd64/kubectl
sudo mv kubectl /usr/local/bin/kubectl
sudo chmod +x /usr/local/bin/kubectl
sudo mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
kubectl version --short --client
- kubectl version --short --client를 입력했을 때 아래와 같은 그림이 출력되면 설치에 성공한 것입니다.
3. jq, bash-completion, git, python 설치
- jq, bash-completion, python 설치 및 PIP 확인
sudo yum install -y jq
sudo yum install -y bash-completion
python --version
python3 --version
sudo curl -O https://bootstrap.pypa.io/get-pip.py
python3 get-pip.py --user
- Git for Linux 설치
sudo yum install git
git --version
4. EKSCTL 설치하기
AWS에서 EKS 클러스터를 배포하는 방식은 AWS콘솔, CloudFormation, CDK, eksctl, Terrform등 다양한 방식이 있습니다.
이번 실습은 EKSCTL을 사용하여 EKS를 배포해 보겠습니다.
EKSCTL은 EKS 클러스터를 쉽게 생성하고 관리할 수 있는 CLI 툴입니다. GO 언어로 쓰여 있으며 CloudFormation 형태로 배포됩니다.
- 아래 명령어는 최신 eksctl 바이너리를 다운로드 후 /usr/local/bin 으로 옮기는 명령어 입니다. eksctl version이 출력되면 설치에 성공한 것입니다.
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin
eksctl version
5. CDK 설치하기
- CI/CD 파트에서 사용할 자원을 배포하기 위해 CDK를 설치해봅시다
- Cloud 9환경에는 기본적으로 CDK가 설치되어 있습니다. 버전을 확인해 봅시다.
cdk --version
- CDK의 설치를 위해 NPM 역시 설치해 주어야 합니다. 링크(https://nodejs.org/en/download/) 에서 패키지를 다운로드해 설치합니다.
- 이후 cdk를 설치하고 버전을 확인합니다. 버전이 정상적으로 출력되면 완료입니다.
npm install -g aws-cdk
cdk --version
6. Cloud9 추가 세팅하기
- 현재 실습이 진행되고 있는 리전을 기본값으로 하도록 aws cli를 설정해줍니다.
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
echo "export AWS_REGION=${AWS_REGION}" | tee -a ~/.bash_profile
aws configure set default.region ${AWS_REGION}
aws configure get default.region
- 현재의 계정 ID를 환경 변수로 등록해줍시다.
export ACCOUNT_ID=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.accountId')
echo "export ACCOUNT_ID=${ACCOUNT_ID}" | tee -a ~/.bash_profile
- Docker 이미지를 빌드하는 동안 용량이 부족할 수 있습니다. 디스크 사이즈를 미리 증설하는 쉘 스크립트를 실행해줍니다.
wget https://gist.githubusercontent.com/joozero/b48ee68e2174a4f1ead93aaf2b582090/raw/2dda79390a10328df66e5f6162846017c682bef5/resize.sh
sh resize.sh
df -h
여기까지 잘 따라오셨다면 환경 세팅은 끝났습니다.
지금부터는 도커 컨테이너 이미지를 만들고 ECR에 업로드 하겠습니다.
1. Dockerfile을 생성해봅시다.
/home/ec2-user/enviroment 로 이동하여 dockerfile을 생성합니다.
cd /home/ec2-user/enviroment
cd ~/environment/
cat << EOF > Dockerfile
FROM nginx:latest
RUN echo '<h1> test nginx web page </h1>' >> index.html
RUN cp /index.html /usr/share/nginx/html
EOF
nginx 이미지와 index 페이지를 실행시키는 dockerfile을 생성해 보았습니다.
- docker build 명령어로 이미지를 만들어줍니다. name에는 컨테이너 이미지 이름을 작성하고 tag의 경우 따로 작성하지 않으면 latest라는 값을 가집니다. 컨테이너 이미지 이름으로는 본인 취향에 맞게 작성해줍니다. 필자는 test-image로 작성했습니다.
docker build -t test-image .
- 생성된 docker image를 확인해봅니다.
docker images
- docker run 명령어로 이미지를 컨테이너로 실행시켜 줍니다.
docker run -p 8080:80 --name test-nginx test-image
호스트는 8080포트 컨테이너 포트는 80으로 맵핑하여 이미지를 실행시켰습니다.
- docker ps 명령어를 통해 실행중인 docker 컨테이너를 확인합니다. (새로운 터미널 창을 열어줍니다.)
docker ps
- 아래 logs 명령어로 로그를 확인할 수 있습니다.
docker logs -f test-nginx
- Tools > Preview > Preview Running Application 을 클릭하여 현재 실행중인 APP을 확인해봅시다.
정상적으로 잘 동작됩니다. 여기까지 확인 되었으면 docker stop 명령어를 통해 실행중인 컨테이너를 중지해줍니다.
docker stop test-nginx
[AWS ECR에 이미지 업로드하기]
- 컨테이너라이징 소스 코드를 다운받습니다.
git clone https://github.com/joozero/amazon-eks-flask.git
- AWS CLI로 이미지 리포지토리를 생성합니다. name과 region을 확인해주시기 바랍니다.
필자는 name은 charon-flask-backend로 region은 ap-northeast로 설정했습니다.
aws ecr create-repository \
--repository-name charon-flask-backend \
--image-scanning-configuration scanOnPush=true \
--region ap-northeast-2
생성하면 다음과 같은 리포지토리가 생성됩니다.
- 컨테이너 이미지를 리포지토리에 푸쉬하기 위해 인증토큰을 가지고 와서 docker login 명령어로 전달합니다.
사용자 이름과, AWS ECR 레지스트리 URL을 지정해줍니다.
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
- 앞서 다운받은 소스코드 위치에 들어가 도커 이미지를 빌드해 줍니다.
cd /home/ec2-user/environment/amazon-eks-flask
docker build -t demo-flask-backend .
- 이미지가 빌드되면 docker tag 명령어를 통해 원하는 리포지토리에 푸쉬될 수 있도록 설정해줍니다.
docker tag demo-flask-backend:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com/charon-flask-backend:latest
- docker push 명령어를 통해 이미지를 리포지토리에 푸쉬해줍니다.
docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com/charon-flask-backend:latest
- eksctl을 사용하여 아무 설정값을 주지 않고 실행하게 된다면 defalut parameter로 클러스터가 배포됩니다.
몇 가지 값을 커스텀하게 설정해 주기 위해 구성파일을 직접 작성하여 배포해봅시다. 내가 명시한 오브젝트들의 바라는 상태 (desired state)를 쉽게 파악하고 관리하기 위해서입니다. Kubernetes의 장점을 쓰지 않을 이유가 없습니다.
- /home/ec2-user/enviroment 폴더에서 yaml 파일을 하나 생성해봅시다.
cat << EOF > eks-demo-cluster.yaml
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: eks-demo # 생성할 EKS 클러스터명
region: ap-northeast-2 # 클러스터를 생성할 리전
version: "1.23"
vpc:
cidr: "10.0.0.0/16" # 클러스터에서 사용할 VPC의 CIDR
nat:
gateway: HighlyAvailable
managedNodeGroups:
- name: node-group # 클러스터의 노드 그룹명
instanceType: t3.small # 클러스터 워커 노드의 인스턴스 타입
desiredCapacity: 3 # 클러스터 워커 노드의 갯수
volumeSize: 20 # 클러스터 워커 노드의 EBS 용량 (단위: GiB)
privateNetworking: true
ssh:
enableSsm: 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
EOF
- 이제 eksctl을 통해 cluster를 배포해봅시다.
eksctl create cluster -f eks-demo-cluster.yaml
클러스터가 완전히 배포되는데 까지 약 15~20분 정도 소요됩니다. Cloud9의 터미널 창 혹은 CloudFormation 콘솔 창에서도 상태를 파악할 수 있습니다.
- 생성이 완료되면 kubectl get nodes를 사용하여 노드가 제대로 배포되었는지 확인해봅니다.
kubectl get nodes
노드 3대가 출력된다면 정상적으로 배포가 된 것입니다.
[ *옵션* credential ]
Cloud9을 통해 배포 후 AWS EKS 콘솔에 들어가보면 아래와 같이 객체에 엑세스 불가 메세지가 출력됩니다.
kubectl get nodes 실행시 노드 3개가 보였음에도 콘솔상에는 아무 정보도 보이지 않게 됩니다.
실제 콘솔에 접근할 때에는 IAM entitiy의 AWS Console credntial을 클러스터에 추가해 주어야 콘솔에 접근할 수 있습니다.
- 아래의 명령어를 입력하여 role ARN을 정의합시다.
rolearn=$(aws cloud9 describe-environment-memberships --environment-id=$C9_PID | jq -r '.memberships[].userArn')
echo ${rolearn}
+ echo를 실행했을 때 assumed-role이 있다면 아래의 작업을 추가로 수행해야 합니다.
assumedrolename=$(echo ${rolearn} | awk -F/ '{print $(NF-1)}')
rolearn=$(aws iam get-role --role-name ${assumedrolename} --query Role.Arn --output text)
- identity 맵핑을 생성합니다. --cluster 옵션으로는 본인에 eks cluster에 맞는 name을 작성해 줍니다.
eksctl create iamidentitymapping --cluster eks-demo2 --arn ${rolearn} --group system:masters --username admin
- 제대로 적용 되었는지 정보를 확인해 봅니다.
kubectl describe configmap -n kube-system aws-auth
상기의 작업이 완료되면 콘솔에 크레덴셜이 추가되고 정보들을 파악할수 있습니다.
- Ingress는 주로 클러스터 외부에서 쿠버네티스 내부로 접근할 때 요청들을 어떻게 처리할지 정의해놓은 규칙이자 리소스 오브젝트 입니다.
- 즉, 외부 요청이 내부로 접근하기 위한 관문의 역할을 합니다. 로드밸런식 TLS/SSL 인증서 처리, HTTP 경로에 대한 라우팅을 설정할 수 있습니다. L7 영역의 요청을 처리한다고 생각하시면 되겠습니다.
- Kubernetes Service를 활용하여 Nodeport혹은 LB로도 외부에 노출할 수 있지만 Ingress 없이 서비스를 사용할 경우 모든 서비스에게 라우팅 규칙 및 TLS/SSL 등의 상세 옵션을 적용해야 합니다.
- 앞서 component 소개시 controller-manager의 일부로 실행되는 다른 컨트롤러와 달리 Ingress컨트롤러는 클러스터와 함께 생성되지 않습니다. 따라서 직접 구성해야 합니다.
- EKS의 Application Loadbalancing이란 클러스터에 인그레스 자원이 생성될 때 ALB 및 필요한 자원이 생성되도록 트리거하는 컨트롤러입니다.
- Ingress의 경우 ALB로 프로비저닝 되고 Service의 경우 NLB로 프로비저닝 됩니다.
일단, 앞으로의 manifast (설정값)을 관리하기 위해 루트 폴더에 manifests라는 이름을 가진 폴더를 생성합니다.
이후 ALB Ingress Controller를 관리하기 위한 폴더 alb-ingress-controller를 만듭니다.
cd ~/environment
mkdir -p manifests/alb-ingress-controller && cd manifests/alb-ingress-controller
/home/ec2-user/environment/manifests/alb-ingress-controller
Load Balancer 컨트롤러를 배포하기 전에 몇가지 작업을 우선적으로 수행해야 합니다. controller가 Wokrer노드에서 동작되기 때문에 IAM permissions를 통해 ALB/NLB에 접근할 수 있도록 만들어야 합니다.
IAM permissions는 ServiceAccount를 위한 IAM roles를 설치하거나 Worker 노드의 IAM roles에 직접적으로 연결할 수 있습니다.
- 우선적으로 클러스터에 대한 IAM OIDC를 생성합니다. 쿠버네티스가 직접 관리하는 사용자 계정을 의미하는 service account에서 IAM role을 사용하기 위해, 생성한 클러스터에 IAM OIDC provider가 존재해야 합니다.
eksctl utils associate-iam-oidc-provider \
--region ap-northeast-2 \
--cluster eks-demo2 \
--approve
- 생성한 IAM OIDC 자격 증명 공급자에 대해 확인해봅시다
aws eks describe-cluster --name eks-demo2 --query "cluster.identity.oidc.issuer" --output text
콘솔상에서도 OIDC를 확인할 수 있습니다.
- 결과 값에서 /id/ 이후의 값을 복사한 후에 아래와 같은 명령어를 수행해줍니다.
aws iam list-open-id-connect-providers | grep B69D9039640ECD0454B5E078C31269AA
이렇게 결과값이 출력되면 OIDC identity provider가 클러스터에 생성된 것입니다.
- 다음으로는 AWS Load Balancer Controller에 부여할 IAM Policy를 생성하는 작업을 수행합니다.
curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json
- iam-policy.json을 다운로드 받고 아래 명령어를 실행하여 IAM Policy를 생성합니다.
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam-policy.json
저는 미리 실습을 진행하면서 생성된 같은 이름의 Policy가 존재했습니다. 따라서 콘솔상에서 해당 Policy를 삭제 후 재 실행 해주었습니다.
Policy가 제대로 실행된다면 아래와 같은 결과값이 출력됩니다.
- 이제 AWS Loadbalancer Controller를 위한 Service Account를 생성해줍시다. cluster name에는 본인이 생성한 cluster의 이름으 알맞게 수정하는 것을 잊지 말아야 하겠습니다.
eksctl create iamserviceaccount \
--cluster eks-demo2 \
--namespace kube-system \
--name aws-load-balancer-controller \
--attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approve
- 이제 AWS Loadbalancer controller를 클러스터에 추가하는 작업을 수행해 봅시다. 먼저 인증서 구성을 webhook에 삽입할 수 있도록 Cert-manager를 설치해줍니다. Cert-manager는 쿠버네티스 클러스터 내에서 TLS 인증서를 자동으로 프로비저닝 및 관리하는 오픈소스입니다.
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml
- Load balancer controller yaml파일을 다운로드 합니다.
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml
- vi 편집기를 통해 yaml파일을 열고 cluster-name을 본인이 작성한 cluster name으로 편집해줍니다.
vi 편집기에서 /$string을 입력하면 원하는 문자열을 찾을 수 있습니다. yaml 파일의 내용이 많으므로 문자열을 검색하여 수정해줍시다. 필자는 eks-demo2로 클러스터를 생성하였으므로 --cluster-name=eks-demo2 로 수정하였습니다.
- 마지막으로 yaml 파일에서 ServiceAccount yaml spec를 없애줍니다. 우리는 AWS Load balancer Controller를 위한 Service Account를 앞서 미리 생성했기 때문입니다.
이 내용을 지워줍니다.
- 이제 AWS Loadbalancer controller를 배포해줍니다.
kubectl apply -f v2_4_4_full.yaml
- 배포가 성공적으로 진행되고 컨트롤러가 실행 되는지 확인해 봅니다.
kubectl get deployment -n kube-system aws-load-balancer-controller
위와같이 출력된다면 성공입니다.
- service account가 제대로 생성되었는지도 확인해줍니다.
kubectl get sa aws-load-balancer-controller -n kube-system -o yaml
위와같은 결과값이 출력된다면 성공입니다.
하기부터 진행되는 yaml파일과 Container Image는 AWS EKS의 Workshop을 참조하여 진행하였습니다.
만약 독자께서 원하는 구성이 있다면 하기 구성을 참조하셔서 구축 작업을 진행하시면 되겠습니다.
드디어 첫 번째 배포를 시작합니다.
앞서 ECS에 등록한 container repo의 image 파일을 사용하여 배포합니다.
- manifests 폴더로 이동합니다.
cd ~/environment/manifests/
- flask를 위한 yaml파일을 생성해봅시다. ( name값에 주의하여 본인의 yaml파일에 맞게 수정해줍니다)
cat <<EOF> flask-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: charon-flask-backend
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: charon-flask-backend
template:
metadata:
labels:
app: charon-flask-backend
spec:
containers:
- name: charon-flask-backend
image: $ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com/charon-flask-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
EOF
- 그 다음 service manifest 파일을 생성하는 yaml파일을 하나 더 생성해 줍니다.
cat <<EOF> flask-service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: charon-flask-backend
annotations:
alb.ingress.kubernetes.io/healthcheck-path: "/contents/aws"
spec:
selector:
app: charon-flask-backend
type: NodePort
ports:
- port: 8080 # 서비스가 생성할 포트
targetPort: 8080 # 서비스가 접근할 pod의 포트
protocol: TCP
EOF
- 마지막으로 ingress manifest 파일을 생성하기 위해 yaml파일을 생성해 줍니다.
cat <<EOF> flask-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "flask-backend-ingress"
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.name: eks-demo-group
alb.ingress.kubernetes.io/group.order: '1'
spec:
rules:
- http:
paths:
- path: /contents
pathType: Prefix
backend:
service:
name: "charon-flask-backend"
port:
number: 8080
EOF
- 이제 생성한 manifest 파일을 순서대로 배포해줍니다. Ingress를 생성하면 ALB가 프로비저닝 됩니다.
kubectl apply -f flask-deployment.yaml
kubectl apply -f flask-service.yaml
kubectl apply -f flask-ingress.yaml
순서대로 잘 실행된 모습입니다.
- 아래 명령어를 수행하면 URL이 출력됩니다. 웹 브라우저에 입력하여 확인해 봅니다.
echo http://$(kubectl get ingress/flask-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/contents/aws
정상적으로 접근이 가능합니다.
또한 EC2 콘솔에서 ALB가 제대로 프로비저닝 되었는지 확인해줍니다.
[ 두 번째 백엔드 배포하기 ]
- 지금부터는 ECR에 이미지 올리는 과정을 생략하고 이미 생성된 컨테이너 이미지를 사용하겠습니다.
- manifests 폴더로 이동합니다.
cd ~/environment/manifests/
- 이미 만들어진 컨테이너 이미지를 포함한 deploy manifest 파일을 생성합니다.
cat <<EOF> nodejs-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-nodejs-backend
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: demo-nodejs-backend
template:
metadata:
labels:
app: demo-nodejs-backend
spec:
containers:
- name: demo-nodejs-backend
image: public.ecr.aws/y7c9e1d2/joozero-repo:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
EOF
- 그 다음 service manifest 파일을 생성하기 위해 아래 yaml파일을 생성합니다.
cat <<EOF> nodejs-service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: demo-nodejs-backend
annotations:
alb.ingress.kubernetes.io/healthcheck-path: "/services/all"
spec:
selector:
app: demo-nodejs-backend
type: NodePort
ports:
- port: 8080
targetPort: 3000
protocol: TCP
EOF
- 마지막으로 ingress manifest 파일을 생성하기 위해 yaml파일을 생성해줍니다.
cat <<EOF> nodejs-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "nodejs-backend-ingress"
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.name: eks-demo-group
alb.ingress.kubernetes.io/group.order: '2'
spec:
rules:
- http:
paths:
- path: /services
pathType: Prefix
backend:
service:
name: "demo-nodejs-backend"
port:
number: 8080
EOF
- 이제 Manifest 파일을 차례대로 배포해줍니다.
kubectl apply -f nodejs-deployment.yaml
kubectl apply -f nodejs-service.yaml
kubectl apply -f nodejs-ingress.yaml
순서대로 잘 배포된 모습입니다.
- 드디어 Front-end를 배포합니다. 이 실습에서는 React를 사용하여 배포해 보겠습니다.
- 아래 명령어를 입력하여 컨테이너로 만들 소스코드를 다운받습니다.
cd /home/ec2-user/environment
git clone https://github.com/joozero/amazon-eks-frontend.git
- AWS CLI를 통해 이미지 레포지토리를 생성합니다. 필자는 리포지토리 이름을 demo-frontend라고 설정했습니다.
aws ecr create-repository \
--repository-name demo-frontend \
--image-scanning-configuration scanOnPush=true \
--region ap-northeast-2
- 두 개의 백엔드 결과 값을 보여주기 위해서 일부 소스코드를 수정해줍니다. 프론트엔드 소스가 담긴 폴더에서 App.js 파일과 page/Upperpage.js파일에 있는 url 값을 앞서 배포한 인그레스 주소로 변경해줍니다.
아래 명령어를 입력해서 출력된 값을 ` ` 사이에 입력해줍니다.
echo http://$(kubectl get ingress/flask-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/contents/'${search}'
위 그림처럼 출력된 값을
이쪽에 입력해줍니다.
다음으로 아래 명령어를 입력하여 출력된 값을 확인하고
echo http://$(kubectl get ingress/nodejs-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/services/all
이곳에 입력해줍니다.
수정 후 저장하는 것을 잊지 맙시다.
- 다음으로는 amazon-eks-frontend 폴더로 이동해서 아래의 명령어를 수행해줍니다.
cd /home/ec2-user/environment/amazon-eks-frontend
npm install
npm run build
* npm install 이후 severity vulnerability가 출력된다면 npm audit fix 명령어를 수행한 이후에 npm run build를 적용합니다.
- ECR에 이미지를 올립니다. 필자는 demo-frontend로 이름을 지정했습니다.
docker build -t demo-frontend .
docker tag demo-frontend:latest $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
이후 docker push해줍니다.
docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
*만약 Push를 수행하는 동안 denied 에러가 발생할 경우엔 아래 명령어를 수행후 다시 docker push를 진행합니다.
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
- 이제 manifest 폴더로 이동하여 frontend를 Deployment할 yaml 파일을 생성해줍니다.
cd /home/ec2-user/enviroment/manifests
- 이 때 이미지 값에는 demo-frontend 리포지토리의 URL 값을 입력해줍니다.
cat <<EOF> frontend-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-frontend
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: demo-frontend
template:
metadata:
labels:
app: demo-frontend
spec:
containers:
- name: demo-frontend
image: 986611521344.dkr.ecr.ap-northeast-2.amazonaws.com/demo-frontend
imagePullPolicy: Always
ports:
- containerPort: 80
EOF
cat <<EOF> frontend-service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: demo-frontend
annotations:
alb.ingress.kubernetes.io/healthcheck-path: "/"
spec:
selector:
app: demo-frontend
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
cat <<EOF> frontend-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "frontend-ingress"
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.name: eks-demo-group
alb.ingress.kubernetes.io/group.order: '3'
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: "demo-frontend"
port:
number: 80
EOF
- 이제 Manifest 파일을 순서대로 배포해줍니다
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml
kubectl apply -f frontend-ingress.yaml
- 마지막으로 다음 명령어를 실행하여 나온 결과를 웹 브라우저에 붙혀넣어 확인합니다.
echo http://$(kubectl get ingress/frontend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
아래 사진처럼 브라우저가 출력된다면 성공입니다.
- 지금까지 우리가 구성한 아키텍쳐는 아래와 같습니다
이상으로 AWS Cloud9을 활용한 EKS 클러스터 배포에 대해서 알아보았습니다.
댓글 영역