⌨️ 지속적인 통합 CI 회고
프로젝트 진행 간 배포 단계에 다가가며 진행했던, CI에 대해 회고해보려 한다. 아쉽게 CD는 진행하지 못했으나 추후 정리를 완료하고 진행해보려 한다.
1. 자동 배포 고민
1.1. CI 단계에서의 고민
자동배포를 언제 쯤 진행해야 할까에 대한 고민이 많았다.
IAM 계정을 만들어 누구나 배포를 할 수 있도록 했지만, 거의 전담하여 배포 업무를 진행했기에 여러 가지 고민이 들었다.
우선 마감일은 다가오는 데 계속해서 변화화는 코드들에 대해 자동 배포를 하는 것이 옳은 것일까..?
당시의 우리의 문제점을 되돌아 보며 생각해 보기로 하자
- 하나의 서비스로 합치고서도 변경 사항이 너무 잦았다.
- CI를 진행하는 브랜치를 FE와 같이 사용했다.
- 도메인(Entity) 자체를 변경하는 일이 잦았다. 칼럼 추가나 삭제 등
크게 생각 나는 건 위 3가지 경우이다.
첫 번째부터 얘기해 보자면, 사실 변경 사항이 잦은 건 크게 문제가 되지 않는다. 그러나 우리의 Git flow 전략이나 브랜치를 나누는 것에서 섬세하지 못한 부분이 발생했고, 후반부에 잦은 변경 사항에 대해 제대로 이슈와 PR 작업 없이 라이브 하게 수정하고 그대로 내용 없이 PR을 하는 경우가 많아졌다. 이 부분에서 우리의 뒷심이 빠진 것에 대해 너무 아쉬움을 느꼈다. 또한 후술 하겠지만 우리가 선택한 CI 도구는 달에 200분의 무료 사용만 가능했기에, 잦은 PR로 인한 초과가 우려되기도 했다.
두 번 째는 브랜치의 분리의 실수이다. 처음 개발을 진행할 때 전의 프로젝트의 경험을 비추어 Dev-> FE, BE로
Dev라는 브랜치 밑에 FE BE를 나누어 진행하려 했으나 팀원의 의견에 따라 Dev에서 합쳐서 진행했다.
이렇게 하니 프런트에서 작업이 PR 되어도 CI가 진행되는 일이 발생했고, 나는 이것을 미리 예감해서 BE 브랜치를 나눠 작업하는 것을 준비하고 있었다. 한 가지 아쉬웠던 점은 BE 브랜치로 Dev를 이관하는 과정에 Front의 내용까지 딸려와서 수작업으로 지워줘야 했다는 것이다.
이래서 처음부터 꼼꼼히 나누고 하는 것이 중요한 것 같다.
가장 중요한 세 번 째이다. 보통 CI 단계까지 가면 DDL-auto를 none이나 validate로 둔다. 하지만 이 단계에서 테이블의 변화가 생겼기에 우리는 데이터의 정합성에서도 문제가 생기고, Application 자체가 실행이 안된다.
처음에는 정석적으로 create를 통해 칼럼의 변경이 있으면 새로 테이블의 스키마를 구성하였으나, 이것이 너무 잦아지자.
sql을 통해 alter table을 진행하였다.
서버는 문제없이 작동했으나.. 이 방법이 옳은지는 꼼꼼히 찾아보며 추가로 다뤄보도록 해야겠다
1.2. Github Action과 AWS에서의 고민
우리는 우선 Github Action을 사용하여 CI를 진행하기로 했다.
이유를 모르겠지만 우리의 Free tier 서버가 굉장히 힘겨워했기에, 추가로 업무를 더 얹어주기가 곤란했다.
아무것도 하지 않아도 서버가 1~2일 정도면 상태 검사에서 하나가 Fail이 나며 인스턴스 터미널을 연결할 수 없거나 서버가 내려가는 등 문제가 많았다.
조금 더 관심을 가지고 서버의 성능을 튜닝했으면 좋았겠지만, 당장의 개발에 치여 조금 소홀히 한 점이 있는 것 같다.
아무리 개발을 잘했더라도 배포하는 인프라가 좋지 않으면 말짱 도루묵이라는 걸 알고 있었지만 이렇게 한 것에 대해 아쉬움이 남는다.
따라서 위 부분도 추후에 꼭 리팩토링을 하고 싶다 CI/CD 와 Cloud Infra 또한 관심 있게 공부하고 싶은 분야기 때문이다.
2. Github Action
2.1. Gradle.yml 구현
부트캠프에서 Github Action을 배운 지 얼마 지나지도 않은 것 같은데 벌써 사용 방법이 바뀌었다.
따라서 직접 빌드 간 에러가 뜨는 걸 확인하며 수정했어야 했다.
여기서 한번 더 느꼈다. 배운 걸 따라 해서 구현하기만 하는 것은 내 것으로 만들 수가 없다는 것을.. 중요한 것은 스스로 해보는 것이다.
그게 강의 내용을 그대로 따라서 한다는 의미가 아니란 것은 다들 알거라 생각한다
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ "BE" ]
pull_request:
branches: [ "BE" ]
permissions:
contents: read
env:
S3_BUCKET_NAME: culinari
APPLICATION: ${{ secrets.APPLICATION}}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
## gradle caching
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
## create application-dev.properties
- name: make application-dev.properties
if: contains(github.ref, 'BE')
run: |
cd ./server/culinari/src/main/resources
touch ./application-dev.properties
echo "${{ secrets.APPLICATION_TEST }}" > ./application-dev.properties
shell: bash
## create application-prod.properties
- name: make application-prod.properties
if: contains(github.ref, 'BE')
run: |
cd ./server/culinari/src/main/resources
touch ./application-prod.properties
echo "${{ secrets.APPLICATION}}" > ./application-prod.properties
shell: bash
- name: Add permission
run: chmod +x ./gradlew
working-directory: ./server/culinari
- name: Build with Gradle
run: ./gradlew build --exclude-task test
working-directory: ./server/culinari/
# - name: Build with Gradle
# uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
# with:
# arguments: build
# build한 후 프로젝트를 압축합니다.
- name: Make zip file
run: zip -r ./culinari-deploy.zip .
shell: bash
# Access Key와 Secret Access Key를 통해 권한을 확인합니다.
# 아래 코드에 Access Key와 Secret Key를 직접 작성하지 않습니다.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} # 등록한 Github Secret이 자동으로 불려옵니다.
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # 등록한 Github Secret이 자동으로 불려옵니다.
aws-region: ap-northeast-2
# 압축한 프로젝트를 S3로 전송합니다.
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./culinari-deploy.zip s3://culinari/culinari-deploy.zip
내가 구현한 Gradle.yml 은 위와 같다.
전체적인 흐름은 간단하다.
언제? 어떻게? 어디서? 등을 구체적으로 썼다고 보면 된다. 흐름을 이해하면 확장하기도 쉬워지는 것 같다.
가장 고민한 부분은 역시 application properties 파일이다. 이는 아래서 자세히 다루려 한다.
2.2. Application.yaml 파일의 노출 정도?
말이 좀 이상해 보이긴 한다 노출 정도라니..
하고 싶은 말은 이거다 프로퍼티 파일의 정보를 어느 정도까지 오픈할 것인가?
처음에는 민감 정보 (JWT 토큰의 Secret key , DB의 접속 정보 등)만 가리면 된다고 생각해서.
Github CI 시 제공하는 Secret-key를 활용했다. 문득 개발을 진행하던 도중 1.1에서 서술한 엔티티 부분의 문제를 접하고 이 생각이 완전히 바뀌어 버렸다.
우리의 Repo는 public이고, 현재 안전장치는 전무하다. 누구나가 와서 Github의 Properties 혹은 env를 임의로 수정한다면 바로 자동 배포가 이루어져서 우리의 라이브(Prod) 서비스는 개판이 될 것이다.
이에 나는 Properties 파일을 완전히 숨길 것을 택했다. 다행히도 Github에서 내가 원하는 방식의 동작을 잘 수행했기에 망정이지 아니면 또 머리가 굉장히 복잡해졌을 것 같다.
세세한 디테일에서 정말 서비스의 품질이 달라진다. 나는 개발도 개발이지만 조금 더 넓은 시야를 가져, 서비스 그리고 비즈니스적인 측면까지 고려하는 것을 목표로 하려 한다.
당장 신입이기에 개발 역량에 매몰되는 것은 어쩔 수 없는 선택이라 보이지만, 조금은 유연하고 융통성 있게 한발 짝 떨어져서 나의 서비스를 관찰하는 객관적 시야가 필요할 것 같다.
3. 피드백
- 서버 배포 간 애플리케이션의 성능 및 품질만을 고려한 것이 아닌 인프라 측면의 고민도 진행해 보기
- DDL-Auto 사용 시 스키마의 변경에 대해 잘 적용되지 않았던 부분을 꼼꼼히 확인하기
- CI 진행 시 Application.yaml 파일의 관리 혹은 화이트 체크 테스트 코드의 TF 여부까지 고려한 수준 높은 CI 구현해 보기