티스토리 뷰
들어가며.
사이드 프로젝트를 진행하면 자동배포 구현을 담당하게 되었다.
우리가 흔히 알고 있는 CI/CD를 완벽하게 구현하기 보다 자동 배포에 집중하여 구현했다.
- GIthub Actions 을 적용하여 프로젝트 main 브랜치에 push 및 merge 되었을 때
- Github Actions에서 프로젝트를 자동 빌드하여 jar 파일을 이미지화 후 Docker hub에 push
- 이후 EC2 환경에 접근하여 해당 image 파일을 pull 하여 docker 로 배포
CI / CD 란?
1.1 지속적 통합
CI 는 Continuous Integration 의 줄인 표현이다. 풀어서 설명하면 개발자를 위해 빌드와 테스트를 자동화하는 과정이다.
CI는 변경 사항을 자동으로 테스트해 애플리케이션에 문제가 없다는 것을 보장한다. 그리고 코드를 정기적으로 빌드하고, 테스트하므로 여러 명이 동시에 작업을 하는 경우 충돌을 방지하고 모니터링할 수 있다.
CI 플랫폼으로는 Jenkins, Github Actions 등이 많이 사용 된다. 이번 글에서는 간단한 설정으로 CI 구현이 가능한 Github Actions으로 진행할 예정이다.
1.2 지속적 제공과 지속적 배포, CD
CD는 CI 작업을 끈낸 다음 실행하는 작업이다. 배포 준비가 된 코드를 자동으로 서버에 배포하는 작업을 자동화하는 것으로 CI가 통과 되면 개발자가 수작으로 코드를 배포하지 않아도 자동으로 배포하니 매우 편리하다.
때문에 CD 는 지속적 제공 (contionuous delivery)이라는 의미와 지속적 배포 (continuous deployment)라는 의미를 모두 갖인다.
1.3 지속적 제공, CD
애플리케이션에 적용한 코드의 빌드와 테스트를 성공적으로 진행했을 때 깃허브와 같은 코드 저장소에 자동으로 업로드하는 과정을 말한다.
최소의 노력으로 코드 배포를 쉽게하는 것을 목표로 합니다.
1.4 지속적 배포, CD
지속적 제공을 통해 성공적으로 병합한 코드 내역을 AWS와 같은 배포 환경으로 보내는 것을 의미한다.
이를 실무에서는 릴리스라고 하며 지속적인 배포는 지속적 제공의 다음 단계까지 자동화 합니다.
CI / CD 구축
프로젝트 레파지스토리의 Action 카테고리에서 사용할 workflow를 선택.
프로젝트에 ./github/workflows 폴더 안에 yml 파일이 생성 된다.
이전에 프로젝트에 빌드에 필요한 설정을 추가 해야한다.
프로젝트 Dockerfile을 생성하여 jdk 및 설정을 진행한다.
FROM openjdk:11
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","app.jar"]
우리가 배포를 자동화하는 flow는 다음과 같다.
- 코드 변경이 감지되면 배포 스크립트를 진행
- JDK 설정
- gradle caching
- application.yml 파일 생성
- gradle build
- docker build & push
- deploy to EC2
파일의 전체 내용은 아래와 같다.
name: Java CI with Gradle
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
fetch-depth: 0
token: ${{ secrets.GIT_TOKEN }}
- name: Update submodules to latest commit on main branch
run: git submodule update --remote
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
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-
# gradle build
- name: Build with Gradle
run: ./gradlew build -x test
# Spring 어플리케이션 Docker Image 빌드
- name: Build Docker Image For Spring
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/community .
docker push ${{ secrets.DOCKER_USERNAME }}/community
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST_PROD }}
username: ec2-user
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
sudo docker ps
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/community
sudo docker stop server || true
sudo docker run -d -p 8080:8080 --name server ${{ secrets.DOCKER_USERNAME }}/community
docker image prune -f
※ 이번 프로젝트에서는 submodules를 사용했기 때문에 따로 설정을 추가한다.
[submodule "src/main/resources/config"]
path = src/main/resources/config
url = https://github.com/selab-hs/SE-Community-Secret.git
branch = main
따로 버전 관리를 하지 않고 최신 main 브랜치의 정보를 사용하기위한 설정을 추가하였다.
트러블 슈팅 모음
트러블 슈팅 1
2023/12/12 14:40:14 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none], no supported methods remain
EC2 인스턴스를 생성하면 .pem 키를 base64 로 인코딩여 EC2에 공개키와 비교하여 접근하는 방식이었다.
오류가 계속 발생했던 원인은 .pem을 base64로 인코딩한 이후, txt 파일오 변환한 파일을 가져왔기 때문이었다.
mac에서 인코딩하는 방식은
base64 -i (이름).pem -o (이름).b64
이후 만들어진 파일을 출력하는 명령어를 적용하면 private-key를 확인 할 수 있다.
base64 -i (이름).b64 -o -
이후 github 에 secrets.에 적용 할 때는 아래와 같은 형식으로 저장 해야한다.
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1Pp0UwTJ7b8rnrJi6B/QJc7aP6xgqw8r8a2Z8eTZB9y5/f5+
... 중략 ...
z4O29QyQ1SwY2/cnPq8vXEnHS+Vh55O6I0B+PKF1Y6h3+DgAC8S2jEFbJbkgx/+7
-----END RSA PRIVATE KEY-----
이후 위의 문제를 해결 할 수 있었다.
트러블 슈팅 2
username은 AWS EC2 를 어떤것을 기반으로 하는 것에 따라 이름이 달라진다.
공식문서를 참고하자 https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/managing-users.html
마무리
CI CD를 직접 구현하면서 인프라에 대해 고민할 수 있는 시간이었다.
Docker에 관심이 있었기 때문에 이번에 진행했던 경험으로 조금이나마 Docker의 편리함을 느낄수 있었다.
Github Actions도 설정이 매우 간단하다는 것이 놀라웠다. Jenkins를 사용 할 때는 서버를 따로 설정하고 복잡한 과정이 많아 한 차례 포기했던 경험이 이었지만 이번 구현을 통해 CI와 CD를 구현하여 자동 배포에 편리함을 알게 되었다.
- Total
- Today
- Yesterday
- AWS
- 프로젝트
- 회고
- index
- java
- Github Actions
- docker
- 인덱스
- network
- token
- cd
- Project
- DB
- Hash
- Session
- 성능개선
- CI/CD
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초 #대규모 #개발 #설계 #대규모솔루션 #캐시 #study
- Submodule
- codingtest
- MYSQL
- DevOps
- 데이터 배이스
- 트래픽
- CI
- Algorithm
- 공부
- CS
- spring boot
- Traffic
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |