stackverflow는 배포 자동화에 계속 실패해서 수동 배포로 진행했다.
근데 수정 사항이 있을 때마다 빌드 - 배포 계속 해주고, 로컬에서 빌드해서 ec2로 보내고 이런 반복 과정들이 너무너무 귀찮았다. + 내가 하나 잘못만져도 터지는 에러들..
이번 main 프로젝트는 한달동안 진행되는거라, 그 한달동안 수동 배포하면 너무너무 귀찮을거같아서 프로젝트 완전 초반부터 깃허브액션과 도커를 이용해 자동배포 적용해서 hello world 찍고 시작하려한다.
우선, 새로운 프로젝트가 시작되었으니 기존과 구분하기 편하게 새로운 EC2도 생성하고 모든 설정을 새로 다시해줬다.
(이전 프로젝트랑 같은 팀원으로 진행해서 혹시 ip주소같은거 헷갈려하실까바 + 혹시 1개 이상 운영하거나, 설정 잘못해서 과금될까바 하는 이유이다.)
EC2계정을 생성하는 등 서버환경 설정, S3설정, RDS설정 들은 이전에 작성한 글을 참고하길 바란다.
[Project] AWS를 이용해 개발 환경 구축하기 - 서버 배포
[Project] AWS를 이용해 개발 환경 구축하기 - 클라이언트 배포
[Project] AWS를 이용해 개발 환경 구축하기 - 데이터베이스 연결
이제 해야하는건 Github Action 설정!
Github Action 은 대표적인 CI 도구 중 하나이다.
Jenkins나 Travis Ci도 있는데, Jenkins는 별도의 서버가 필요하기도 했고 git을 사용하고 있는중이라 레파지토리에 있는걸 편하게 빌드하고 관리할 수 있지않을까해서 Github Action을 선택했다.
(CI에 대한 설명은 이전에 작성한게 있어서 추가 설명하지 않겠다.)

위 이미지의 순서처럼
1) 백엔드 코드 작업 후 Git hub 레파지토리에 commit+push 한다.
2) 레파지토리 변화가 있으니 Github Actions 이 작동된다.
3) Github Actions Workflow에 작성한 순서대로 명령이 실행된다.
4) Github Actions Workflow에 의해 생성된 이미지가 DockerHub에 업로드된다.
이런 과정으로 진행할 것이다.
번호대로 따라가면 1), 2)번은 트리거가 있어야하니까 (깃헙액션이 알아서 해주니까 ㅎㅎ ... 3)부터 작업하면 된다.
Github Actions Workflow는 깃헙에서 제공하기도하고 , 구글에서 찾아보면 엄청 많은 경우로 정보가 있으니 공부해서 자신의 프로젝트에 맞게 바꾸는걸 추천한다. (오류나서 자연스럽게 공부하고 바꾸게됨 ㅎ)
프로젝트 가장 상위 폴더에 main.yml 파일을 생성해줬다.
/.github/workflows/main.yml
코드 별 자세한 내용은 주석 참고 바람!
name: Server Deploy
on:
push:
branches: [ main ]
paths:
- 'server/**' //main 브렌치와 server 파일에 변화가 있어야 작동하게 함.
jobs:
server:
runs-on: ubuntu-latest
env: //application-server.yml로 프로퍼티 설정하고, 가릴 정보를 환경변수로 설정했기때문에 빌드할 때 필요한 정보를 주입해준다.
AWS_ACCESS_KEY: ${{secrets.AWS_ACCESS_KEY}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
JWT_SECRET_KEY: ${{secrets.JWT_SECRET_KEY}}
KAKAO_CLIENT_ID: ${{secrets.KAKAO_CLIENT_ID}}
KAKAO_CLIENT_SECRET: ${{secrets.KAKAO_CLIENT_SECRET}}
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'zulu'
- name: Grant execute permission for gradlew
working-directory: ./server
run: chmod +x gradlew //Permission을 주기위해 작성함
- name: Build with Gradle
working-directory: ./server
run: ./gradlew build //빌드 실행 명령어
- name: Docker build //도커에 로그인해서 이미지를 빌드한다음 올려준다.
run: |
docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_PASSWORD }}
cd server
docker build -t spring-cicd .
docker tag spring-cicd ${{ secrets.DOCKER_HUB_USERNAME }}/spring-cicd:${GITHUB_SHA::7}
docker push ${{ secrets.DOCKER_HUB_USERNAME }}/spring-cicd:${GITHUB_SHA::7}
지금 서버가 어느정도 안정을 찾고(?) 작성하는거라 환경 변수 등 추가된게 많다.
이제 ./gradlew build 까지해서 빌드한 웹 애플리케이션을 도커 이미지로 만들기위해 Dockerfile을 작성해야한다.
프로젝트 내 build.gradle 파일이 위치한 경로인 프로젝트 최상위 경로에 생성하면된다.
# (1) base-image
FROM openjdk:11
# (2) COPY에서 사용될 경로 변수
ARG JAR_FILE=build/libs/*-SNAPSHOT.jar
# (3) jar 빌드 파일을 도커 컨테이너로 복사
COPY ${JAR_FILE} app.jar
# (4) jar 파일 실행
ENTRYPOINT ["java","-jar","/app.jar"]
보통 이렇게 작성하는데, 찾다보니 여기서 환경변수를 넣어주는 경우도 있었다.
나는 프로퍼티 설정을해서 application-server.yml로 서버를 돌려야하기때문에 여기서
ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=server"]
이렇게 지정해주었다.
프로퍼티 설정을 위해 공식문서를 확인해서 Dockerfile에 추가했다.
이렇게 작성해주고, push하면 깃허브액션이 작동한다!

workflow에 작성한 과정중에 에러가나면

이렇게 로그를 확인할 수 있다.
AWS 자동 배포는 로그를 바로 확인하기가 좀 어려웠는데 (사실 또 그렇게 어렵진 않지만...) 깃허브 액션은 바로 확인할 수 있어서 좋았다. (좋은건가..ㅎ)

도커 허브에서 이미지 올라간것도 확인할 수 있다.
이제 빌드를 해주었으니 배포를 해야한다.

전체적으로 보면 나는 위 이미지의 과정으로 서버를 배포하려하는데, 4번까지는 위의 CI에서 작업했으니 이제 5~7 과정을 진행하면된다.
5) Github Actions Workflow를 이용해서 AWS EC2에 연결한다.
6) DockerHub에 올라간 도커 이미지를 EC2에 Pull을 받아서
7) 도커를 통해 컨테이너를 8080번 포트를 통해 접근할 수 있도록 실행한다.
EC2에 연결하기위해 IAM 계정을 발급하고 액세스키를 발급받고, IAM 계정에 SSM 권한을 주었다.
위에서 작성한 Workflow에 이어서 아래 내용을 추가해줬다.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Start Session Manager session
run: aws ssm start-session --target {인스턴스 id 값}
- name: Deploy to Server
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ap-northeast-2
run: |
aws ssm send-command \
--instance-ids {인스턴스 id 값} \
--document-name "AWS-RunShellScript" \
--parameters "commands=[
'if sudo docker ps -a --format \\'{{.Names}}\\' | grep -q \'^server$\\'; then',
' sudo docker stop server',
' sudo docker rm server',
'fi',
'sudo docker pull {도커 유저네임}/spring-cicd:${GITHUB_SHA::7}',
'sudo docker tag {도커 유저네임}/spring-cicd:${GITHUB_SHA::7} spring-cicd',
'sudo docker run -d --name server -p 8080:8080 spring-cicd'
]" \
--output text
여기서 환경변수 값들을 Github Secret 으로 설정해서 넣어줬는데, 보안에 문제가 있는 방식이라 .env 파일에 작성해서 도커 실행 시 주입해주려한다.
(환경변수 관련 내용은 에러도 많이 만났고, 따로 정리도 필요해보여서 개별 게시글을 작성할 예정이다. )
이렇게 작성해주고 실행시키면 배포가된다!
postman에서 정상적으로 작동하는지 확인하거나,
docker ps // -a 붙이면 꺼진것도 나옴
또는
docker logs {서버 이름}
위 명령어를 통해 배포 상태를 확인할 수 있다. (배포 문제생기면 로그부터 보느라 외워버렸다..)

배포완료!!!
블로그에 적으면 왜이렇게 순조롭게 하루만에 뚝딱 진행된거처럼 보이는지 모르겠다..
주말 1.5일 갈아넣었다... 처음 + 에러 폭탄 인 상황이라 정말 힘들었지만 배포되고 hello world 찍히는거보니까 정말 이맛에 개발자하나 싶었다.
Github Actions Workflow를 작성해서 새로운 변경 사항이 있을 때 마다 CD 되게 하려했는데
Run chmod +x gradlew
chmod: cannot access 'gradlew': No such file or directory
Error: Process completed with exit code 1.
에러로 실패했다.
지금 디렉토리 구성이 서버, 클라 로 나눠져있고 서버 디렉토리 안에 백엔드 작업이 구성되어있는데 진행 단계에서 server 디렉토리 안에서 빌드 실행하라고 말해주지 않아서 그렇다.
- name: Grant execute permission for gradlew
working-directory: ./sever
run: chmod +x gradlew
- name: Build with Gradle
working-directory: ./sever
run: ./gradlew build
working-directory: ./sever
이렇게 경로 설정해주면 빌드 성공한다 ㅠ
초반 CI/CD 에러는 이정도지만 날이 갈수록 험난해진다. (내 성격은 험악해지고..)
이후에 만난 에러는 다른 게시물에서 정리하겠다!
끝!