EKS 환경에서 지속적 통합을 위한 Private Repository로 ECR을 선택했다.
Docker Hub를 사용하지 않은 이유는 Private Repository를 위해 서버, 혹은 컨테이너를 띄워 서버 자원을 사용하고싶지 않았다. 관리 비용을 줄이기 위해 ECR을 사용했으며 추후 ECR 비용이 부담이 되는 시점에 바꿀 예정이다.
ecr-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:*"
],
"Resource": "*"
}
]
}
aws iam create-policy --policy-name ECRFullAccessPolicy --policy-document file://ecr-policy.json
aws eks describe-nodegroup --cluster-name [클러스터 이름] --nodegroup-name [노드 그룹 이름] --query "nodegroup.remoteAccess.ec2SshKey" --output text
aws iam attach-role-policy --role-name [EKS-Node-Instance-Role] --policy-arn arn:aws:iam::[계정번호]:policy/ECRFullAccessPolicy
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
aws ecr get-login-password --region $AWSREGION | docker login --username AWS --password-stdin [계정ID].dkr.ecr.$AWSREGION.amazonaws.com
CI를 위해서 Github Action을 사용할 것이다.
CI를 적용시키기에 앞서, Action 스크립트 또한 레포지토리에 들어가기 때문에 중요 KEY 노출에 민감하게 반응해야한다.
Repository -> Settings -> secret -> new repo secrete로 만들어서 AWS KEY들을 관리하자.
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"]
많은 에러를 만나서 고생했지만 결과만 작성하겠다.
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
빈 application.yml 파일을 만들어주는 과정이다.
이미지 빌드시 없으면 default로 들어가지만 넣은 이유는 해당 프로젝트는 test코드가 없다.
하지만 이후 test가 존재하는 프로젝트를 빌드할 때 해당 방식으로 test yml 설정파일을 주입해주어서 테스트를 진행해주어야 한다.
빌드를 위한 JDK 환경을 세팅해준다.
Gradle을 초기화시킨다
Gradle 파일에 대한 권한을 허용해준다.
Gradle을 이용해서 빌드를 시작하는데 arguments에 build -x test 부분은 테스트를 시행하지 않고 빌드를 하겠다는 것이다.
이후 테스트가 존재하는 프로젝트에 한해서 build로 바꿔주면 자동으로 테스트를 시행한다.
이 부분이 중요한데, action에서 aws 관련 행위를 하기 위해선 반드시 이 부분 이후에 선언해주어야 한다.
Image가 ECR 레포지토리에 동일한 Tag명으로 있는지 확인한다.
Tag명이 동일하다면 이전 버전이므로 여기서는 삭제했으나 이미지 버전 관리가 필요한 경우에 이 부분을 제외해주자.
ECR에 로그인한다.
이미지를 빌드 후 푸시해준다.
일반적인 설정 정보는 ConfigMap에, 외부에 노출되면 민감한 내용은 Secret에 설정해준다.
환경 변수를 Spring Application에서 사용하는 방식으로 POD를 구성했다.
환경 변수의 이름을 선언하는 규칙도 존재한다.
.
은 _
로 바꾼다-
는 제거한다.또한 Secret에 값을 설정할때에는 Base64로 인코딩해서 적어주어야 하는데 linux 환경 cmd로 간편하게 확인할 수 있다.
echo -n "what you want to encode value in here" | base64
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"
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로 관리하되, 주의하자.