저번 글에서 RDS와 스프링 프로젝트를 설정했으니, 이제는 CI/CD 작업을 해 보겠다. 사실 이 과정에서도 되게 많이 애를 먹었다.
도커 환경이 CI/CD에 적합한 이유는 이곳을 참고하자.
도커 허브 계정 및 레포지토리가 없다면 이곳을 참고하자.
Dockerfile
은 애플리케이션을 도커 허브에 올리고 빌드 및 실행할 때 어떤 방식으로 진행할지를 정의하는 파일이다.
프로젝트 루트 위치에 다음과 같이 작성하면 된다.
FROM openjdk:17-jdk # 도커 허브의 openjdk:17-jdk 설치
WORKDIR /app # 도커 디렉터리 이름을 app으로 설정함
EXPOSE 8080 # 사용할 포트 (드러낼 포트)
COPY build/libs/{스프링 프로젝트 이름}-0.0.1-SNAPSHOT.jar /app/app.jar # 빌드 jar 파일을 /app/app.jar로 복사
CMD ["java", "-jar", "app.jar"] # 빌드 후 실행 명령어 지정
Dockerrun.aws.json
파일은 도커 컴포즈 (docker compose)를 사용하지 않고 도커로 배포할 때 작성하는 파일이다. 관련 내용은 이곳을 참고하면 된다. 이것을 사용한 덕분인지는 모르겠으나 Nginx 설정 및 기타 다른 파일들도 모두 작성하지 않을 수 있게 되었다.
나는 아직 도커에 대해 미숙한 상태이기 때문에 이 방식을 활용하였다. 이 파일 또한 프로젝트 루트 위치에 작성해둔다!
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "{도커 ID}/{도커 레파짓 이름}:{도커 태그 이름}",
"Update": "true"
},
"Ports": [
{
"ContainerPort": 8080,
"HostPort": 5000
}
]
}
커밋 푸시 까먹지 말자!
Elasticbeanstalk에 접근할 때, AWS IAM으로부터 발급받은 Access key와 Secret key를 필요로 하기 때문에 이 키들을 발급받아야 한다.
사용자에 다음 정책들을 등록시킨다. (직접 정책 연결)
사용자를 생성했다면, 보안 자격 증명 > 액세스 키 만들기
를 통해 키를 발급받는다.
(AWS 컴퓨팅 서비스에서 실행되는 애플리케이션) 선택
깃허브에 위 값들을 시크릿 변수로 등록해야 한다.
깃허브 레포지토리 > Settings > Security > Secrets and variables > Actions
에 등록하자.
변수를 등록하는 김에 도커 허브 비밀번호도 등록해둔다.
AWS Elasticbeanstalk 설정을 하자.
EB에서 사용할 보안 그룹을 설정해둔다. (ec2 > 보안 그룹
)
EB를 사용하기 위해서는 역할 설정이 되어 있어야 한다. 사용자를 만들었을 때 쓴 정책들을 그대로 추가해주자. (신뢰할 수 있는 엔티티 유형은 AWS 서비스, 사용 사례는 EC2)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "elasticbeanstalk.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
꽤나 애먹었던 부분이다. 역할에서 신뢰 관계를 elasticbeanstalk, ec2에 대해 모두 정의해야 한다. 그렇지 않으면 접속은 되지만 아래와 같은 오류가 뜰 수 있다.
Service role "arn:aws:am::101677557996:role/elasticbeanstalk" is missing permissions required to check for managed updates. Verify the role's policies.
스택 오버플로우 글을 봤을 때는 ec2를 elasticbeanstalk으로 바꾸면 된다고 했었지만, 에러 메시지가 나오지 않는 대신 도메인에 들어갈 수 없는 오류가 생기게 되었어서 다음과 같은 생각을 해 보았다.
기존에는 IAM 역할을
ec2.amazonaws.com
으로 설정했었을 때 권한 관련 에러가 뜬 대신 접속은 잘 되었다.
그리고 지금은 권한 관련 에러가 뜨지 않는 대신 접속이 되지 않는다.
그렇다면, 이 둘을 모두 한꺼번에 적용시키면 문제를 해결할 수 있지 않을까?
때문에 나온 게 위의 결과이다. 결과적으로 잘 되었고, 이 둘을 모두 작성해야 한다.
그리고 키 페어 또한 준비되어 있어야 한다. 키 페어 발급 (맥)은 이곳을 참고한다.
이후 역할을 기존 서비스 역할
, EC2 인스턴스 프로파일
에 연결하고, 키 페어를 EC2 키 페어
에 연결한다.
참고:
검토 단계로 건너뛰기
를 하지 않고 아래와 같이 커스텀으로 설정했더니 EB가 너무 오랫동안 생성되지 않는 이슈 (무한No Data
..) 가 발생했습니다. 아래 설정들은검토 단계로 건너뛰기
를 통해 빠르게 EB 인스턴스를 생성한 다음구성
에서 편집해주시면 됩니다.
퍼블릭 IP 주소
를 활성화하고 넘긴다.
보안 그룹
을 위에서 미리 정의한 보안 그룹으로 연결한다.단일 인스턴스
, 플릿 구성은 온디맨드 인스턴스
, 아키텍처는 x86_64
, 인스턴스 유형은 t2.micro
로 했다.관리형 플랫폼 업데이트
를 해제한 것 말고는 기본값으로 했다.
다만 환경 변수를 등록해야 한다!
키 | 값 |
---|---|
SPRING_DATASOURCE_DEIVER_CLASS_NAME | com.mysql.cj.jdbc.Driver |
SPRING_DATASOURCE_PASSWORD | DB 비밀번호 |
SPRING_DATASOURCE_URL | jdbc:mysql://{rds 엔드포인트 주소}:{rds 포트}/{rds 데이터베이스 이름}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 |
SPRING_DATASOURCE_USERNAME | 관리자 이름 |
SPRING_JPA_HIBERNATE_DDL_AUTO | none |
다른 글들을 보면 RDS_HOSTNAME, RDS_DB_NAME 이런 식으로 작성한 경우가 많을 것이다. 그러나 나는 그런 방식으로 했을 경우에 incorrect application version expected version elastic beanstalk과 같은 오류가 생겼다. eb-engine.log를 뜯어본 결과, 다음과 같은 로그가 있었다.
2023/08/18 08:09:50.753780 [ERROR] An error occurred during execution of command [config-deploy] - [Track pids in healthd]. Stop running the command. Error: update processes [eb-docker eb-docker-log healthd cfn-hup nginx docker eb-docker-events] pid symlinks failed with error read pid source file /var/pids/eb-docker-log.pid failed with
error:open /var/pids/eb-docker-log.pid: no such file or directory
혹시 도커, 즉 띄워진 스프링에서 오류가 난 것인가? 라는 생각이 들었고, eb-docker/containers/eb-current-app
에 있는 로그를 뜯어봤다.
2023-08-18T08:09:20.405Z WARN 1 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Failed to determine a suitable driver class
…
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
원인은 도커로 띄워진 스프링에 있었다. 나는 분명 다른 블로거들과 AWS의 내용을 보면서 RDS_HOSTNAME
로 RDS의 엔드포인트를 적고, 나머지 값들을 작성했는데 url이라는 속성을 찾을 수 없다고 나왔다.
그러다 문득, 내가 평소에 application.yml
에 작성해두고 있는 형식과 같게 하면 올바르게 인식할 수 있지 않을까? 라는 생각이 들었다. “url”이라는 속성을 보면, application.yml
에 있는 spring.datasource.url
과 유사하기 때문이다.
이후 위 테이블에 작성한 대로 변경하니 성공할 수 있었다. application.yml
에 붙일 추가적인 변수들도 위와 같은 형식으로 작성하면 될 것 같다.
위의 설정대로 진행하면 다음과 같이 기본 EB 페이지가 나온다.
이제 Github Actions 코드를 작성하자. 레포지토리 > Actions > Java with Gradle에서 yml 코드를 작성하면 된다.
name: deploy
on:
push:
branches: [ main ]
jobs:
deploy:
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'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -x test
- name: Docker build and push to Docker hub
run: |
docker login -u 도커_계정_이름 -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t 스프링_프로젝트_이름 .
docker tag 스프링 프로젝트 이름 도커_계정_이름/도커_레포지토리_이름:도커_태그
docker push 도커_계정_이름/도커_레포지토리_이름:도커_태그
- name: Get timestamp
uses: gerred/actions/current-time@master
id: current-time
- name: Run string replace
uses: frabert/replace-string-action@master
id: format-time
with:
pattern: '[:\.]+'
string: "${{ steps.current-time.outputs.time }}"
replace-with: '-'
flags: 'g'
- name: AWS Beanstalk Deploy
uses: einaregilsson/beanstalk-deploy@v20
with:
aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
application_name: EB_애플리케이션_이름
environment_name: EB_환경_이름
version_label: "Version ${{steps.build-number.outputs.BUILD_NUMBER}} deployed via github actions ${{ github.sha }}"
region: ap-northeast-2
deployment_package: Dockerrun.aws.json
기다리면 다음과 같이 CI/CD가 잘 이루어진 것을 확인할 수 있다.
이제 다음 편에서는 https를 적용해보겠다.
부족하거나 보완할 점이 있다면 댓글 부탁드립니다 😃
어제 umc 에서 블로그 주소 받았습니다 글 좋아요!