본문 바로가기
개발/Docker&kubernetes

AWS EKS 구성 및 컨테이너 웹 어플리케이션 배포 - 2

by 궁즉변 변즉통 통즉구 2023. 2. 10.
반응형

앞에서 생성한 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 부분을 삭제한다

클러스터 이름을 eks-demo로 변경한다
ServiceAccount 부분을 전체 삭제하고 저장한다

 

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로 접속을 해보면 아래와 같이 잘 나온다

 

참조:

https://www.youtube.com/watch?v=kb6s0Tmp2CA 

https://catalog.us-east-1.prod.workshops.aws/workshops/9c0aa9ab-90a9-44a6-abe1-8dff360ae428/ko-KR/10-intro

반응형

댓글