앞에서 생성한 EKS 클러스터에 컨테이너 웹 어플리케이션을 배포하는 과정을 정리해본다.
(참고)EKS 클러스터 구성: https://happy-jjang-a.tistory.com/193
1. ECR 생성 및 Image Push
Image Repository인 ECR을 생성하고, Image를 Push한다.
아래에서 샘플 어플리케이션을 Cloud9에서 다운로드 받는다.
git clone https://github.com/joozero/amazon-eks-flask.git
ECR을 demo-flask-backend 이름으로 생성한다.
aws ecr create-repository \
--repository-name demo-flask-backend \
--image-scanning-configuration scanOnPush=true \
--region ${AWS_REGION}
생성 후 AWS콘솔에 ECR메뉴에 가보면 아래와 같이 리파지토리 생성을 확인할 수 있다. 리파지토리를 선택하고 상단의 '푸시명령보기'를 클릭하면 관련 명령을 모두 볼 수 있다.
'푸시명령보기'의 명령을 하나씩 Cloud9에서 실행해준다. Docker 빌드는 Dockerfile이 위치한 디렉토리로 이동 후 빌드 한다.
AWS콘솔에서 Push된 이미지를 확인 할 수 있다.
2. IngressController 생성(AWS Load Balancer Controller)
- k8s에서 ServiceType 중, NodePort 혹은 LoadBalancer로도 외부로 노출할 수 있지만 Ingress 없이 서비스를 사용할 경우, 모든 서비스에게 라우팅 규칙 및 TLS/SSL 등의 상세한 옵션들을 적용해야 됨으로 Ingress가 필요하다.
- Ingress는 외부 요청 처리에 대한 규칙들을 설정해놓은 것을 의미하며, 이런 설정이 동작하기 위해서 필요한 것이 IngressController이다.
- EKS에서는 Ingress의 경우 Application Load Balancer로 프로비저닝되고, Service인 경우 Network Load Balancer로 프로비저닝 된다.
- AWS LoadBalancerController에서 지원하는 트래픽 모드는 2가지이다.
- Instance(default): 클러스터 내 노드를 ALB의 대상으로 등록. ALB에 도달하는 트래픽은 NodePort로 라우팅된 다음 pod로 프록시 됨
- IP: pod를 ALB대상으로 등록. ALB에 도달하는 트래픽은 pod로 직접 라우팅. ingress.yaml파일에 지정 필
IngressController 작업 디렉토리를 생성하고 이동한다.
$ cd ~/environment
$ mkdir -p manifests/alb-ingress-controller && cd manifests/alb-ingress-controller
# 최종 폴더 위치
$ /home/ec2-user/environment/manifests/alb-ingress-controller
다음으로 Controller가 워커노드 위에서 동작되기 때문에 IAM Permissions를 통해, AWS ALB or NLB리소스에 접근할 수 있도록 IAM Policy를 생성하고 SA(Service Account)를 생성한다.
SA에서 IAM role을 사용하기 위해서는 클러스터에 IAM OIDC(OpenID Connect) provider가 존재해야 함으로 IAM OIDC provider 부터 생성한다.
# cluster에 IAM OIDC Provider 생성
eksctl utils associate-iam-oidc-provider \
--region ${AWS_REGION} \
--cluster eks-demo \
--approve
# 확인
$ aws eks describe-cluster --name eks-demo --query "cluster.identity.oidc.issuer" --output text
# 결과
https://oidc.eks.ap-northeast-2.amazonaws.com/id/8A6E78112D7F1C4DC352B1B511DD13CF
# 위 결과에서 /id/ 뒤에 있는 값을 복사한 후, 아래와 같이 명령어를 수행
$ aws iam list-open-id-connect-providers | grep 8A6E78112D7F1C4DC352B1B511DD13CF
AWS Load Balancer Controller에 부여할 IAM Policy를 생성하고, 마지막으로 SA를 생성한다
# iam_policy.json 파일 다운로드
$ 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 생성
$ aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam-policy.json
# AWS Load Balancer Controller를 위한 ServiceAccount를 생성
$ eksctl create iamserviceaccount \
--cluster eks-demo \
--namespace kube-system \
--name aws-load-balancer-controller \
--attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approve
이제 AWS Load Balancer Controller를 클러스터에 추가한다.
# 인증서 구성을 웹훅에 삽입할 수 있도록 cert-manager 를 설치
# Cert-manager는 k8s 클러스터 내에서 TLS인증서를 자동으로 프로비저닝 및 관리하는 오픈 소스
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml
# Load balancer controller yaml 파일을 다운로드
$ wget https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases/download/v2.4.4/v2_4_4_full.yaml
다운로드 받은 파일(v2_4_4_full.yaml)에서 클러스터 이름을 수정하고, SA 부분을 삭제한다
AWS Load Balancer Controller 파일을 배포한다.
# AWS LoadBalancer Controller 배포
$ kubectl apply -f v2_4_4_full.yaml
# deployment 확인
$ kubectl get deployment -n kube-system aws-load-balancer-controller
# sa 확인
$ kubectl get sa aws-load-balancer-controller -n kube-system -o yaml
# pod 로그 확인
$ kubectl logs -n kube-system $(kubectl get po -n kube-system | egrep -o "aws-load-balancer[a-zA-Z0-9-]+")
# pod 상세 확인
$ ALBPOD=$(kubectl get pod -n kube-system | egrep -o "aws-load-balancer[a-zA-Z0-9-]+")
$ kubectl describe pod -n kube-system ${ALBPOD}
3. Service 배포
1번째 백엔드 서비스 배포
1번째 flask기반의 백엔드 서비스를 배포한다.
# 작업 디렉토리 이동
cd ~/environment/manifests/
# deployment yaml 파일 생성
$ cat <<EOF> flask-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-flask-backend
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: demo-flask-backend
template:
metadata:
labels:
app: demo-flask-backend
spec:
containers:
- name: demo-flask-backend
image: $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-flask-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
EOF
# service yaml 파일 생성
$ cat <<EOF> flask-service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: demo-flask-backend
annotations:
alb.ingress.kubernetes.io/healthcheck-path: "/contents/aws"
spec:
selector:
app: demo-flask-backend
type: NodePort
ports:
- port: 8080 # 서비스가 생성할 포트
targetPort: 8080 # 서비스가 접근할 pod의 포트
protocol: TCP
EOF
# ingress 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: "demo-flask-backend"
port:
number: 8080
EOF
작성한 yaml 파일을 배포하고, ALB 접속 정보를 확인 및 접속 테스트를 해본다.
# 배포
$ kubectl apply -f flask-deployment.yaml
$ kubectl apply -f flask-service.yaml
$ kubectl apply -f flask-ingress.yaml
# ALB 생성되기 전에는 정확한 URL이 안뜰 수 있음(ALB 생성까지 기다렸다가 URL확인 후 접속 테스트)
$ echo http://$(kubectl get ingress/flask-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/contents/aws
2번째 백엔드 서비스 배포
2번째 node기반의 백엔드 서비스를 배포한다.
# 작업 디렉토리 이동
$ cd ~/environment/manifests/
# deployment yaml 파일 작성
$ 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 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 파일 작성
$ 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
ingress 생성 시 그룹을 설정하면(alb.ingress.kubernetes.io/group.name: eks-demo-group) 하나의 ALB를 하나의 그룹이 함께 사용할 수 있다.
작성한 yaml 파일을 배포하고, ALB 접속 정보를 확인 및 접속 테스트를 해본다.
# yaml 배포
$ kubectl apply -f nodejs-deployment.yaml
$ kubectl apply -f nodejs-service.yaml
$ kubectl apply -f nodejs-ingress.ya
# ALB 생성되기 전에는 정확한 URL이 안뜰 수 있음(ALB 생성까지 기다렸다가 URL확인 후 접속 테스트)
$ echo http://$(kubectl get ingress/nodejs-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/services/all
3번째 프론트 서비스 배포
마지막으로 프론트엔드 서비스를 배포한다. 프론트엔드는 소스를 수정해야하는 부분이 있어서 소스 다운로드부터 받는다
cd /home/ec2-user/environment
git clone https://github.com/joozero/amazon-eks-frontend.git
app.js파일을 열어서 line44를 아래 명령의 결과로 수정한다.
$ echo http://$(kubectl get ingress/flask-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/contents/'${search}'
다음으로 page/UpperPage.js 파일을 열어서 line33을 아래 명령의 결과로 수정한다.
$ echo http://$(kubectl get ingress/nodejs-backend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')/services/all
소스 수정이 완료됐으면 npm빌드를 한다.
cd /home/ec2-user/environment/amazon-eks-frontend
npm install
npm run build
프론트용 ECR 리파지토리를 생성하고, Image를 빌드 후 Push한다.
# ECR repository 생성
$ aws ecr create-repository \
--repository-name demo-frontend \
--image-scanning-configuration scanOnPush=true \
--region ${AWS_REGION}
# Image Build/Push
$ docker build -t demo-frontend .
$ docker tag demo-frontend:latest $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
$ docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
# [옵션]denied: Your authorization token has expired. Reauthenticate and try again 에러시 로그인 재수행
$ aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
배포를 위한 yaml파일을 작성한다.
# 작업 디렉토리 이동
$ cd /home/ec2-user/environment/manifests
# deployment yaml 작성
$ 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: $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/demo-frontend:latest
imagePullPolicy: Always
ports:
- containerPort: 80
EOF
# service yaml 작성
$ 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
# ingress yaml 작성
$ 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
작성한 yaml 파일을 배포하고, ALB 접속 정보를 확인 및 접속 테스트를 해본다.
# yaml 배포
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml
kubectl apply -f frontend-ingress.yaml
# ALB 생성되기 전에는 정확한 URL이 안뜰 수 있음(ALB 생성까지 기다렸다가 URL확인 후 접속 테스트)
$ echo http://$(kubectl get ingress/frontend-ingress -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
결과로 나오는 URL로 접속을 해보면 아래와 같이 잘 나온다
참조:
'개발 > Docker&kubernetes' 카테고리의 다른 글
helm upgrade configmap, secret 변경 시 pod 자동 재시작 (0) | 2023.03.20 |
---|---|
Distroless 이미지로 안전하고 최적화된 이미지 생성 (0) | 2023.03.13 |
K3s 인증서 갱신(certificate has expired or is not yet valid) (0) | 2023.01.15 |
Docker Multi Application(여러 어플리케이션) 실행 (0) | 2022.09.06 |
Docker Nginx Simple 컨테이너 생성 (0) | 2022.08.26 |
댓글