EKS 환경 - ECR 이용 CI 구축

반영환·2023년 9월 19일
0

eks

목록 보기
7/9
post-thumbnail

EKS 환경 - ECR 이용 CI 구축

EKS 환경에서 지속적 통합을 위한 Private Repository로 ECR을 선택했다.

Docker Hub를 사용하지 않은 이유는 Private Repository를 위해 서버, 혹은 컨테이너를 띄워 서버 자원을 사용하고싶지 않았다. 관리 비용을 줄이기 위해 ECR을 사용했으며 추후 ECR 비용이 부담이 되는 시점에 바꿀 예정이다.

ECR 사용하기

워커노드 역할 추가

정책 파일 생성

ecr-policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:*"
            ],
            "Resource": "*"
        }
    ]
}

IAM 정책 생성 및 연결

aws iam create-policy --policy-name ECRFullAccessPolicy --policy-document file://ecr-policy.json

Node Group Role Name 가져오기

aws eks describe-nodegroup --cluster-name [클러스터 이름] --nodegroup-name [노드 그룹 이름] --query "nodegroup.remoteAccess.ec2SshKey" --output text

IAM 역할에 정책 연결

aws iam attach-role-policy --role-name [EKS-Node-Instance-Role] --policy-arn arn:aws:iam::[계정번호]:policy/ECRFullAccessPolicy

ECR 레포지토리 만들기 by Terraform

Terraform 세팅 참조

aws_ecr_test.tf

provider "aws" {
  region = var.region # 원하는 AWS 리전으로 변경
  access_key = var.access_key
  secret_key = var.secret_key

}

resource "aws_ecr_repository" "ecr_repository" {
  name = "${var.tag_name}-test" # ECR 레포지토리의 이름을 지정하세요.
  image_scanning_configuration {
    scan_on_push = true # 이미지 푸시 시 자동으로 스캔하려면 true로 설정하세요.
  }
}
# terraform file 폴더 루트에서 시행

terraform init

terraform plan # 필수! 이걸로 디버깅 시작

terraform apply

ECR 로그인

aws ecr get-login-password --region $AWSREGION | docker login --username AWS --password-stdin [계정ID].dkr.ecr.$AWSREGION.amazonaws.com

CI

CI를 위해서 Github Action을 사용할 것이다.

Github Action

CI를 적용시키기에 앞서, Action 스크립트 또한 레포지토리에 들어가기 때문에 중요 KEY 노출에 민감하게 반응해야한다.

Secret

Repository -> Settings -> secret -> new repo secrete로 만들어서 AWS KEY들을 관리하자.

Project DockerFile

Project의 루트 경로에 DockerFile을 만들어준뒤에 Repository에 Push 해주자.

DockerFile

FROM openjdk:11

ARG JAR_FILE=./build/libs/<jar-file-name>.jar

COPY ${JAR_FILE} kafka.jar

EXPOSE 9000

ENTRYPOINT ["java","-jar","kafka.jar"]

Github Action Script 작성

많은 에러를 만나서 고생했지만 결과만 작성하겠다.

name: Docker Image KAFKA_TOPIC_SERVICE CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: production application file setting
        run: |
          mkdir -p ./src/main/resources
          touch ./src/main/resources/application.yml
      - uses: actions/upload-artifact@v2
        with:
          name: application.yml
          path: ./src/main/resources/application.yml
        
      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'

      - name: Init with Gradle
        uses: gradle/gradle-build-action@v2
      - run: gradle init

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
        

      - name: Build with Gradle
        uses: gradle/gradle-build-action@v2
        with:
          gradle-version: 7.1.1
          arguments: build -x test

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2 ## 자신의 리전

      - name: Check if image with tag exists
        id: check-image
        run: |
          if aws ecr describe-images --repository-name suite-ecr --image-ids imageTag=kafka-topic-service-latest; then
            echo "Image with tag $IMAGE_TAG exists in repository $REPOSITORY"
            echo "::set-output name=image_exists::true"
          else
            echo "Image with tag $IMAGE_TAG does not exist in repository $REPOSITORY"
            echo "::set-output name=image_exists::false"
          fi

      - name: Delete existing image with the same tag
        if: steps.check-image.outputs.image_exists == 'true'
        run: |
          aws ecr batch-delete-image --repository suite-ecr --image-ids imageTag=kafka-topic-service-latest
      

      - name: Login to ECR
        uses: aws-actions/amazon-ecr-login@v1
 
      - name: Build & Push to ECR
        run: |
          docker build -t suite-ecr:kafka-topic-service-latest .
          docker tag suite-ecr:kafka-topic-service-latest ${{ secrets.AWS_ACCOUNT_NUM }}.dkr.ecr.ap-northeast-2.amazonaws.com/suite-ecr:kafka-topic-service-latest
          docker push ${{ secrets.AWS_ACCOUNT_NUM }}.dkr.ecr.ap-northeast-2.amazonaws.com/suite-ecr:kafka-topic-service-latest

name: production application file setting

빈 application.yml 파일을 만들어주는 과정이다.

이미지 빌드시 없으면 default로 들어가지만 넣은 이유는 해당 프로젝트는 test코드가 없다.

하지만 이후 test가 존재하는 프로젝트를 빌드할 때 해당 방식으로 test yml 설정파일을 주입해주어서 테스트를 진행해주어야 한다.

name: Set up JDK 11

빌드를 위한 JDK 환경을 세팅해준다.

name: Init with Gradle

Gradle을 초기화시킨다

name: Grant execute permission for gradlew

Gradle 파일에 대한 권한을 허용해준다.

name: Build with Gradle

Gradle을 이용해서 빌드를 시작하는데 arguments에 build -x test 부분은 테스트를 시행하지 않고 빌드를 하겠다는 것이다.

이후 테스트가 존재하는 프로젝트에 한해서 build로 바꿔주면 자동으로 테스트를 시행한다.

name: Configure AWS credentials

이 부분이 중요한데, action에서 aws 관련 행위를 하기 위해선 반드시 이 부분 이후에 선언해주어야 한다.

name: Check if image with tag exists

Image가 ECR 레포지토리에 동일한 Tag명으로 있는지 확인한다.

name: Delete existing image with the same tag

Tag명이 동일하다면 이전 버전이므로 여기서는 삭제했으나 이미지 버전 관리가 필요한 경우에 이 부분을 제외해주자.

name: Login to ECR

ECR에 로그인한다.

name: Build & Push to ECR

이미지를 빌드 후 푸시해준다.

Kubernetes ConfigMap & Secret

일반적인 설정 정보는 ConfigMap에, 외부에 노출되면 민감한 내용은 Secret에 설정해준다.

환경 변수를 Spring Application에서 사용하는 방식으로 POD를 구성했다.

환경 변수의 이름을 선언하는 규칙도 존재한다.

  • ._ 로 바꾼다
  • - 는 제거한다.
  • 모두 대문자로 바꾼다

또한 Secret에 값을 설정할때에는 Base64로 인코딩해서 적어주어야 하는데 linux 환경 cmd로 간편하게 확인할 수 있다.

echo -n "what you want to encode value in here" | base64

Config YML

apiVersion: v1
kind: Secret
metadata:
  name: kafka-topic-service-secret
data:
  SPRING_KAFKA_BOOTSTRAPSERVERS: a2Fma2Etc2VydmljZS10ZXN0LTA6OTA5MixrYWZrYS1zZXJ2aWNlLXRlc3QtMTo5MDkyLGthZmthLXNlcnZpY2UtdGVzdC0yOjkwOTIsa2Fma2Etc2VydmljZS10ZXN0LTM6OTA5Mg==

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kafka-topic-service-config
data:
  SERVER_PORT: "9000"
  

Deployment YML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-topic-service-deployment
  labels:
    app: kafka-topic-service-app
spec:
  replicas: 1 # 원하는 파드 수
  selector:
    matchLabels:
      app: kafka-topic-service-app
  template:
    metadata:
      labels:
        app: kafka-topic-service-app
    spec:
      containers:
        - name: kafka-topic-service-app
          image: <ecr-repo-info>
          ports:
            - containerPort: 9000
          envFrom:
            - configMapRef:
                name: kafka-topic-service-config
            - secretRef:
                name: kafka-topic-service-secret

---
apiVersion: v1
kind: Service
metadata:
  name: kafka-topic-service
  labels:
    app: kafka-topic-service-app
spec:
  selector:
    app: kafka-topic-service-app
  ports:
    - protocol: TCP
      port: 9000
      targetPort: 9000

정리

Product용 yml파일은 github action 단계에서는 생성하지 않는다.

이는 ConfigMap과 Secret으로 설정한다.

Test용 yml파일은 미리 작성해서 repository -> setting -> secret에 yml key & value로 넣어주어야 한다.

예시로,

위와 같이 yml 파일을 secret 값으로 생성해준다. 여기서 중요한 것은 해당 yml파일 내용을 value로 넣을 때 이때 역시 base64로 인코딩해주어야 한다.

이후에 github action script를 아래와 같이 변경해준다.

- name: production application file setting
	rrun: |
    mkdir -p ./src/main/resources
    touch ./src/main/resources/application.yml
    echo ${{secrets.KAFKA_TOPIC_SERVICE_APPLICATION_YML}} > ./src/main/resources/application.yml

ConfigMap과 Secret은 yml파일로 잘 관리하자.이 역시 민감 정보기 때문에 Repository로 관리하되, 주의하자.

profile
최고의 오늘을 꿈꾸는 개발자

0개의 댓글