사실 저번에 올렸던 CICD는 오류가 많은 설정이었다. 왜냐하면 CI를 못하기 때문이다. (테스트를 통과하지 못하는 코드라는 의미이다.) 언젠가 꼭 고쳐놔야겠다고 생각했는데 짬이 나서 코드를 좀 수정했다.
대체적으로 CI를 위해서 고민한 내용은 다음과 같다.
1. .env 파일을 어떻게 적용시킬 수 있을까?
2. Redis, MongoDB와 같은 기자재를 workflow 내에서 어떻게 연결시킬 수 있을까?
3. Test 용 서버를 만들고 싶은데 어떻게 배포할 수 있을까?
첫번째 이슈
- .env 파일을 어떻게 적용 시킬 수 있을까?
1) 1차 시도 : actions>secrets 에 하나씩 넣는 방법
- 너무 비효율적이라고 느꼈음
- 심지어 yml 파일도 길어지기에 추가적으로 변수가 추가 될때마다 secret 키와 2개의 코드를 추가해야 하는데 이 방법은 번거로웠다.
- gradle.yml 파일
- name: Create .env file
run: |
echo "$MONGODB_URI" >> .env
echo "$REDIS_URI" >> .env
.....
env:
REDIS_URI: ${{secrets.REDIS_URI}}
MONGODB_URI: ${{secrets.MONGODB_URI}}
...
2) 2차 시도 : json으로 한꺼번에 넣어서 보관함
- .env 파일을 json 파일로 만들어서 key value 로 쪼개어 나눠서 .env로 만들겠다는 생각
- 해당 방법은 원래 .env 를 바꿔야 한다는 번거로움이 있기 때문에 변경하기로 함
- gradle.yml
- name: Create .env file
run: |
jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' <<< "$SECRETS_CONTEXT" > .env
env:
SECRETS_CONTEXT: ${{ toJson(secrets) }}
해당 코드를 통해서 SECRETS_CONTEXT에 json 파일을 모두 갖다놓고, 해당 파일을 key=value 값으로 .env로 놓겠다는 것이다
3) 3차 시도 : 그냥 .env 파일을 아예 secrets 키로 넣어버리기로 함
- .env 파일 자체를 secrets 키에 보관하기로 함
- gradle.yml
- name: Create .env file
run: |
echo "$SECRETS_CONTEXT" >> .env
env:
SECRETS_CONTEXT: ${{secrets.SECRETS_CONTEXT}}
매우 깔끔하고 정렬된 코드. 이걸로 결정했다! 게다가 .env 파일을 그대로 복붙해서 넣어주기만 하면 되니 관리하기도 편하다.
두번째 이슈
2. Redis와 DB와 같은 기자재는 어떻게 처리할 수 있을까?
1) MongoDB는 h2 형태로 내부로 저장할 수 있는 DB가 있는데, 이걸로 해결 가능했다.
2) Redis
2-1 ) Elasticache와 직접 연동
Redis는 AWS의 elasticache와 연동할 수도 있겠지만, 그러면 inbound 규칙을 추가해 줘야 하는데 이는 보안의 위험이 있다. 또, MONGO DB와 다르게 test 코드에서 redis의 db를 나눠주지 않았기 직접 사용하는 것에는 데이터 관리 면에서도 좋지 않다고 판단.
2-2 ) Docker container로 Redis 생성
work flow에 container로 redis를 생성하고, test 할 때에 http://127.0.0.1:6379 라는 endpoint로 연동할 수 있도록 한다. 해당 방법이 적합한 것 같아서 선택했다.
- gradle.yml
jobs:
build:
runs-on: ubuntu-latest
services:
# Label used to access the service container
redis:
# Docker Hub image
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
이렇게 jobs를 build 할 때 step를 시작하기 이전에 Redis를 container로 만들고 6379 port로 접근할 수 있도록 열어준다.
세번째 이슈
3. Test 용 서버를 만들고 싶은데 어떻게 배포할 수 있을까?
현재 우리 github actions는 CICD라는 브랜치에 pr을 보내게 되면 서버에 자동으로 배포되게 된다. 하지만 Test Server에 자동적으로 만들고 싶다는 요청을 받았다.
나는 해당 요청에 다른 branch에다가 pr을 날리게 되면 다른 port에다가 배포될 수 있도록 하는 것이 좋겠다고 생각했다.
그래서 CICD일때는 8080포트로, test-Server 라는 branch에 pr을 날릴 경우 8081로 가게 된다.
deploy:
if: ${{ github.ref == 'refs/heads/CICD' }}
needs: build
name: Deploy
runs-on: [ self-hosted, label-jaribean ]
# label-jaribean 라는 이름으로 AWS EC2 가 Runner 를 작동시킬 때 사용했던 그 label
steps:
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
- name: Docker run
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASS }}
docker stop ${{ env.NAME }} && docker rm ${{ env.NAME }} && docker rmi ${{ env.DOCKER_IMAGE }}:latest
aws s3 cp s3://jaribean-env/spring/.env ~/spring_env/
docker pull ${{ env.DOCKER_IMAGE }}:latest
docker create -p 8080:8080 --name ${{ env.NAME }} ${{ env.DOCKER_IMAGE }}:latest
docker cp ~/spring_env/.env ${{ env.NAME }}:/
docker start ${{ env.NAME }}
deploy_test:
if: ${{ github.ref == 'refs/heads/test_server' }}
needs: build
name: Deploy_test
runs-on: [ self-hosted, label-jaribean ]
# label-jaribean 라는 이름으로 AWS EC2 가 Runner 를 작동시킬 때 사용했던 그 label
steps:
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
- name: Docker run
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASS }}
docker stop ${{ env.NAME }} && docker rm ${{ env.NAME }} && docker rmi ${{ env.DOCKER_IMAGE }}:latest
aws s3 cp s3://jaribean-env/spring/.env ~/spring_env/
docker pull ${{ env.DOCKER_IMAGE }}:latest
docker create -p 8081:8080 --name ${{ env.NAME }} ${{ env.DOCKER_IMAGE }}:latest
docker cp ~/spring_env/.env ${{ env.NAME }}:/
docker start ${{ env.NAME }}
# 참고로 맨 위의 test-server에 pr을 받을 때 실행되야 한다는 코드를 작성해줘야 한다.
결론
- 전체 코드
# 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: [ "main" , "CICD", "test_server"]
env:
DOCKER_IMAGE: chlrltjd5263/jaribean
VERSION: ${{ github.sha }}
NAME: jaribean
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
services:
# Label used to access the service container
redis:
# Docker Hub image
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- 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-
- name: Setup docker buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ env.VERSION }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Create .env file
run: |
echo "$SECRETS_CONTEXT" >> .env
env:
SECRETS_CONTEXT: ${{secrets.SECRETS_CONTEXT}}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: |
./gradlew build
- uses: actions/upload-artifact@v3
with:
name: Package
path: build/libs
- name: Build with Gradle
run: |
cp ./build/libs/jariBean-0.0.1-SNAPSHOT.jar app.jar
ls -R
- name: Build Docker image
if: contains(github.ref, 'CICD')
run: |
ls
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASS }}
docker build -t ${{ env.DOCKER_IMAGE }}:latest .
docker push ${{ env.DOCKER_IMAGE }}:latest
deploy:
if: ${{ github.ref == 'refs/heads/CICD' }}
needs: build
name: Deploy
runs-on: [ self-hosted, label-jaribean ]
# label-jaribean 라는 이름으로 AWS EC2 가 Runner 를 작동시킬 때 사용했던 그 label
steps:
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
- name: Docker run
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASS }}
docker stop ${{ env.NAME }} && docker rm ${{ env.NAME }} && docker rmi ${{ env.DOCKER_IMAGE }}:latest
aws s3 cp s3://jaribean-env/spring/.env ~/spring_env/
docker pull ${{ env.DOCKER_IMAGE }}:latest
docker create -p 8080:8080 --name ${{ env.NAME }} ${{ env.DOCKER_IMAGE }}:latest
docker cp ~/spring_env/.env ${{ env.NAME }}:/
docker start ${{ env.NAME }}
deploy_test:
if: ${{ github.ref == 'refs/heads/test_server' }}
needs: build
name: Deploy_test
runs-on: [ self-hosted, label-jaribean ]
# label-jaribean 라는 이름으로 AWS EC2 가 Runner 를 작동시킬 때 사용했던 그 label
steps:
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
- name: Docker run
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASS }}
docker stop ${{ env.NAME }} && docker rm ${{ env.NAME }} && docker rmi ${{ env.DOCKER_IMAGE }}:latest
aws s3 cp s3://jaribean-env/spring/.env ~/spring_env/
docker pull ${{ env.DOCKER_IMAGE }}:latest
docker create -p 8081:8080 --name ${{ env.NAME }} ${{ env.DOCKER_IMAGE }}:latest
docker cp ~/spring_env/.env ${{ env.NAME }}:/
docker start ${{ env.NAME }}
- test 를 성공한 모습
- 배포가 완료된 모습
느낀 점
CICD의 세계는 멀고도 험했다. 이걸 나중에는 jenkins로 해야 한다니 쉽지 않겠지만 하나하나 하다보면 할 수 있겠지...
파이팅!
'git > github actions' 카테고리의 다른 글
github actions를 활용한 CICD. 이번에는 spring boot로! (1) (0) | 2023.08.20 |
---|---|
github actions 란? (0) | 2023.08.20 |
github action을 통한 CI/CD 배포 자동화 (feat.fastAPI) (0) | 2023.07.18 |