배포는 EC2로 배포하였고 RDS(MySQL)을 사용하였다.
현재 프로젝트를 진행하면서 했던 배포 방식이다. 하지만 실제로는 약간 다른데 기존에 먼저 제가 배포를 했다가 프리티어 기간이 끝나가고 있고 윈도우 환경에서 느려서 지금 정리한 배포환경을 바꾸고 MAX환경으로 배포환경을 옮겼습니다.
바뀐점
인스턴스 유형은 프리 티어가 가능한 t2.micro
를 선택한다. 프리 티어는 AWS 신규가입시 750시간 무료 제공 서비스
이다.
만약 SSH로 키페어를 생성했으면 그대로 사용하면 됩니다.
여기서 SSH를 만들 수 있지만 따로 SSH를 만들 수 있다.
파일이 다운로드 되는데, 잃어버리면 인스턴스에 접근할 수 없으니
받은 즉시 자신의 이메일이나 git private에 올려버리자.
일단 저는 EC2 키페어 안에 저장했습니다.
보안 그룹을 생성한다.
ssh
, https
와 http
트래픽을 허용하자.
프리 티어의 경우 30기가 까지 사용할 수 있다고 한다. gp3
가 성능이 좋다고하니 gp3
로 선택한다
상태 검사를 통과 할 때까지 대기
퍼블릭 IP 주소도 확인된다. 해당 IP는 서버를 재부팅 할 경우 바뀔 수 있다. 고정하기 위해서는 탄력적 IP를 이용하자.
이제 생성한 인스턴스의 보안 그룹을 수정해 주도록 하겠습니다.
보안그룹이란?
AWS 에서 제공하는 방화벽으로 인바운드 규칙, 아웃바운드 규칙이 존재합니다.
- 인바운드 규칙(inbound) : 외부에서 EC2나 RDS 등의 내부로 접근할때 사용되는 방화벽 규칙
- 아웃바운드 규칙(outbound) : EC2나 RDS 등의 내부에서 외부로 접근할때 사용되는 방화벽 규칙
우리는 EC2에 접속해서 서버를 띄우는것이 목적이기 때문에 인바운드 규칙만 건드려주도록 하겠습니다.
EC2 메뉴의 네트워크 및 보안 탭의 보안 그룹 을 눌러 보안 그룹 세팅 창으로 들어갑니다.
보안 그룹 생성을 눌러 새로운 보안 그룹을 생성해 줍시다.
위 목록에서 보안 그룹의 이름과 설명을 적으시고
아까 설명한 방화벽 설정을 해주도록 하겠습니다.
인바운드 규칙의 규칙추가를 눌러
다음과 같이 세팅해주도록 합시다.(여기서 0.0.0.0/0 은 Anywhere-IPv4 입니다.)
위에서부터 순서대로
스프링 부트 기반 서버를 열어줄것이기 때문에 사용자 지정으로 8080 포트를 설정해준 뒤 url을 아는 누구나 접속할수있도록 Anywhere-IPv4 로 설정해줍니다.
원격 EC2 인스턴스에 접속할때 사용되는 ssh 관련 방화벽으로 밖에서 접속하게 하려면 고정 ip가 아닌 Anywhere-IPv4 로 설정하고 집에서만 접속하게 하려면 고정 ip를 적용합니다. 또한 ssh는 기본 포트 연결로 22번 포트가 사용됩니다.
HTTP 연결시 사용됩니다.
HTTPS 연결시 사용됩니다.
아웃바운드 규칙은 따로 세팅해주지 않고 기본세팅으로 남기고 넘어가도록 하겠습니다.
한글이 들어가면 안된다!
이제 만든 보안 그룹을 아까 만든 EC2에 설정해줄 차례입니다.
EC2 콘솔에 들어가 아까 만든 인스턴스를 클릭하고 위쪽의 작업 -> 네트워킹 -> 보안 그룹 변경 을 눌러주고 기존의 기본 보안 그룹은 체크 해제후 방금 만든 보안그룹으로 교체해줍니다.
보안그룹이 잘 변경되었는지 확인하고 인바운드 규칙 보기 를 눌러 인바운드 규칙이 잘 적용 되었나 확인 해주세요.
발급 받은 ssh 키로 Ec2 우분투 콘솔에 접속하기
이제 EC2를 만들었으니 원격으로 EC2에 접속할 시간입니다.
방금 만든 EC2 인스턴스가 체크되어있는 상태에서 위쪽의 연결 버튼을 눌러줍시다. 그러면
다음과 같은 연결하는 방법이 친절하게 나와있으므로 이것을 차례대로 따라하기만 하면 됩니다.
AWS EC2 인스턴스를 접근하기 위해서는 SSH(Secure Shell)키 페어가 필요합니다. SSH는 네트워크를 통해 원격 시스템에 접근할 수 있는 프로토콜 및 프로그램입니다. SSH는 SSH 서버
와 SSH 클라이언트
로 구성되며, SSH 클라이언트가 SSH 서버에 접속하기 위해서는 인증 절차가 필요합니다. SSH 인증 방식은 크게 유저-암호
입력 방식과 키 페어 파일
을 이용한 방식이 있습니다.
유저-암호
무차별 대입 공격 방식에 의해서 권한 탈취를 할 수 있는 우려가 있습니다.
키 페어 파일
사용자의 개인키가 노출되지 않기 때문에 안전하고 키 파일로 로그인이 가능하며 추가로 암호를 입력하지 않아도 되어 편리합니다.
AWS EC2인스턴스 경우에도 기본적으로 SSH 키페어를 통한 접속을 이용합니다.
EC2에 들어가고 키 페어를 클릭해주면 다음과 같이 키 페어 생성이 나옵니다.
먼저 MobaXterm
을 다운로드 해줍니다. 비슷한 것으로 파일질라, Putty가 있습니다.
MobaXterm
리눅스에 Telnet, SSH 접속을 위해 Putty나 XShell을 깔고, FTP/SFTP접속을 위해 FileZilla를 깔고, 그 외 다양한 원격 접속용 프로그램을 복수개 설치하여 관리하면 많이 복잡해지고 번거로워 진다. 또한 Putty 같은 경우, 여러 파일들을 vi에디터로 수정/관리해야하는 경우 'Putty'만으로는 한계가 있다.
MobaXterm은 현존하는 최강의 SSH 접속 툴로, 익숙한 UI와 다양한 접속 방식 지원으로 설정이 편하고 별도의 SFTP 프로그램 등을 설치하지 않아도 된다. 그리고 보안키도 putty로 ppk 변환하지않고
.key
또는.pem
그대로 사용할수도 있다.
이제 키 페어 생성을 누르면 다음과 같은 화면이 나옵니다.
여기서 .pem
을 하면 Mac OS를 사용할 때 이걸 누르면 되고 Putty나 mobaXterm을 사용하면 .ppk
를 눌러줍니다.
PuTTy 다운로드
Conversins > ImportKey > Parameters에 SSH-1(RSA) 클릭 > Save private Key 클릭
MobaXterm 실행
그대로 쓰면 한글 파일 명이 출력이 안되고 문자가 깨지기에 Character set 변경을 필수이다. 세팅 옵션 > 터미널 탭으로 접속 후, Term Charset을 클릭 후 EUC-KR으로 변경 후 저장하게 되면 해당 캐릭터 셋으로 세팅 되어 서버 작업이 가능하게 된다.
New Session
SSH 클릭 후 Remote host에 Public IP를 입력한다.
우리가 만져야 하는 부분은 Remote host부분인데, 이 부분에는 접속하려는 서버의 Public IP 혹은 도메인 이름을 입력하면 됩니다. 위에서 생성한 EC2 인스턴스의 public IP를 적어주면 됩니다.
하단의 Advanced SSH settings 중 Use private key에 체크하고 위에서 생성한 ppk Key를 넣어준다.
Use private key를 클릭하고 다운 받은 키를 넣어준다.
EC2에서 생성한 키를 넣어준다. 만약 SSH로 키페어를 생성했으면 SSH 키페어를 넣어준다.
자주 사용하는 서버에 대해 'Bookmark settings' 탭을 이용하면 나중에 손쉽게 서버 접속을 할 수 있다. Session name에 적당한 이름을 기입하고 'Create a desktop shortcut to this session' 버튼을 클릭하면 저장이 완료된다.
- Amazon Linux 2 또는 Amazon Linux AMI의 경우 사용자 이름은
ec2-user
입니다.- CentOS AMI의 경우 사용자 이름은
centos
또는ec2-user
입니다.- Debian AMI의 경우 사용자 이름은
admin
입니다.- Fedora AMI의 경우 사용자 이름은
fedora
또는ec2-user
입니다.- RHEL AMI의 경우 사용자 이름은
ec2-user
또는root
입니다.- SUSE AMI의 경우 사용자 이름은
ec2-user
또는root
입니다.- Ubuntu AMI의 경우 사용자 이름은
ubuntu
입니다.- Oracle AMI의 경우 사용자 이름은
ec2-user
입니다.- Bitnami AMI의 경우 사용자 이름은
bitnami
입니다.
아래의 방법은 블로그를 보면 보안 후 인스턴스에 연결하는 방법으로 나오는데 MobXterm에도 되는지는 미정입니다.
mobaXterm으로 EC2에 접속한다.
clear를 입력하여 화면을 비운다.
그러면 다음과 같이 빈칸으로 됩니다.
수월하게 작업하기 위해 슈퍼유저를 사용한다.
sudo su
ubuntu 계정에서 root 계정으로 변경
타임존 확인.
timedatectl
그려면 미국시간으로 나오는 것을 볼 수 있는데 한국시간으로 타임존을 설정한다. 타임존 설정을 하지 않으면, 서버에서 날짜 관련 데이터를 처리할 때 잘못된 날짜가 들어갈 수 있다.
sudo ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
또는
sudo timedatectl set-timezone Asia/Seoul
sudo echo Asia/Seoul > /etc/timezone
입력하면 반응이 없는데, 리눅스는 성공적으로 작업이 되면 반응이 없다.
아래 명령어로 시간이 바뀌었는지 확인해본다.
timedatectl
apt 리파지토리 목록을 업데이트한다.
sudo apt update
$ sudo apt install openjdk-11-jdk -y
정상적으로 설치가 되는 경우 아래와 같은 화면이 뜬다.
자바설치 확인
$ java -version
먼저, EC2에 yml을 직접올리는게 아니라 인스턴스에 환경변수로 만들고 가져오도록 만들어서 사용해야 한다.
${변수이름}을 적어주면, 환경변수에서 값을 찾아와 할당하게 된다.
jwt:
key: ${JWT_KEY}
access-token-expiration-minutes: ${JWT_ACCESS_EXPIRATION_MIN}
refresh-token-expiration-minutes: ${JWT_REFRESH_EXPIRATION_MIN}
# google
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${google_client}
client-secret: ${google_secret}
redirect-uri: http://localhost:8080/login/oauth2/code/google
scope:
- email
- profile
# naver
naver:
client-id: ${naver_client}
client-secret: ${naver_secret}
client-name: Naver
redirect-uri: http://localhost:8080/login/oauth2/code/naver
authorization-grant-type: authorization_code
scope:
- name
- email
provider:
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response
cloud:
aws:
credentials:
access-key: ${s3_access}
secret-key: ${s3_secret}
s3:
bucket: ${s3_bucket}
region:
# 버킷 생성시 선택한 AWS 리전
static: ap-northeast-2
auto: false
stack:
# 설정한 CloudFormation 이 없으면 프로젝트 시작이 안되니, 해당 내용을 사용하지 않도록 false 를 등록
auto: false
jwt:
secret_key: ${jwt_sercet}
access:
expiration: 3600000000
refresh:
expiration: 1209600000000
ec2 인스턴스에서는 다음의 과정을 통해 환경변수를 설정할 수 있습니다.
우리가 찾아야 할 파일은 .bashrc 파일이고 이 파일은 ubuntu@~~~~/home/ubuntu
에 있습니다.
따라서 본인 경로에서 cd ..
를 통해 본인의 경로를 찾은뒤 home/ubuntu에서 ls -al
을 통해 .bashrc
를 찾습니다.
nano .bashrc
nano .bashrc 를 입력하면 위와 같은 화면이 나오는데 제일 밑으로 이동합니다.
export MYSQL_SECRET=123456789
변경 사항을 저장합니다. ( Ctrl+x ,누른 뒤 Y 를 누르고 Enter를 누른다)
'.bashrc' 파일에서 설정한 환경 변수를 사용할 수 있도록 변경 사항을 적용하기 위해 다음 명령어를 입력합니다.
source .bashrc
echo $MYSQL_SECRET
123456789
여기서 application.yml과 application-prod.yml을 나누려고 하면 애플리케이션을 실행할 때, spring.profiles.active
프로퍼티를 사용하여 프로파일을 지정합니다. 이것은 application.yml과 application-prod.yml 중에서 어떤 프로파일을 사용할지 결정합니다. 예를 들어, 프로덕션 환경에서는 application-prod.yml을 사용하려면 다음과 같이 실행할 수 있습니다.
java -jar -Dspring.profiles.active=prod your-application.jar
이 명령을 통해 프로덕션 프로파일이 활성화되고, 따라서 application-prod.yml의 설정이 로드됩니다.
프로젝트 터미널에서
./gradlew bootJar
또는
./gradlew build
build 경로에 libs안에 jar파일이 생성되어 있다.
윈도우 탐색기에서 오픈한다.
mobaXterm의 왼쪽 화면에서 /home/ubuntu/
경로를 확인하고
폴더를 만들어준다. 지금 같은 경우는 belog로 만들어 주었습니다.
클릭해서 들어가서 아까 다음에서 열기해서 열린 폴더에서 jar파일을 드래그해서 넣어줍니다.
폴더로 이동한다.
아래와 같이 타이핑한다.
nohup java -jar mybatis_project-0.0.1-SNAPSHOT.jar 1>log.out 2>err.out &
아래 코드로 잘 작동되는지 확인해보자.
ps -ef | grep m*.jar
이제 제대로 나오는지 확인을 해보려면
이런식으로 치면 됩니다.
PuTTyGen이 아니라 PuTTy로 접속해서
Connection → SSH → Auth → Credentials
위 AWS 키 페어로 생성한 .ppk 키 파일
을 추가하고 open 클릭
소셜 로그인의 경우 clientId
와 clientSecret
이 필수입니다. 로컬 PC에서는 설정파일이 존재하므로 문제가 없지만 배포할 경우 이 파일은 .gitignore
로 git에서 제외 대상이라 깃허브에 올라가지 않습니다.
애플리케이션을 실행하기 위해 공개된 저장소에 clientId
와 clientSecret
올릴 수 없으니 서버에서 직접 이 설정들을 가지고 있게 하겠습니다.
구글 웹 콘솔에 접속하여 EC2 주소를 등록해보자.
[OAuth 동의 화면] → [앱 수정]으로 이동한다.
[승인된 도메인]에서 EC2의 퍼블릭 DNS를 ‘http://’를 제외하고 추가한다.
[사용자 인증 정보] → [승인된 리디렉션 URI] → ‘DNS:8080/login/oauth2/code/google’ 주소를 등록한다.
EC2 DNS 주소로 이동해서 다시 구글 로그인을 시도하여 로그인이 정상적으로 수행되는지 확인한다.
아래 네이버 개발자 센터로 접속해서 본인의 프로젝트로 이동한다.
https://developers.naver.com/apps/#/myapps
본인 프로젝트 → API 설정
PC웹 항목 → [서비스 URL]과 [Callback URL]을 수정한다.
주의사항
서비스 URL
- 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목이다.
- 8080포트는 제외하고 실제 도메인 주소만 입력한다.
- 네이버에서 아직 지원되지 않아 하나만 등록 가능하다.
즉, EC2 주소를 등록하면 localhost가 안된다.- 개발 단계에서는 등록하지 않는 것을 추천한다.
- localhost도 테스트하고 싶으면 네이버 서비스를 하나 더 생성해서 키를 발급받으면 된다.
Callback URL
- 전체 주소를 등록합니다.
EC2 퍼블릭 DNS:8080/login/oauth2/code/naver
사용해야할 ec2
, 보안그룹
, 서브넷그룹
, rds
를 모두 같은 네트워크 안에서 사용되야 하므로 ec2에서 사용하고 있는 vpc와 서브넷ID를 파악합니다.
RDS DB에서 사용할 DB 보안 그룹을 생성하겠습니다.
[EC2]에서 왼쪽 메뉴바의 [네트워크 및 보안]의 [보안그룹]을 선택해서 보안 그룹 생성을 눌러주세요.
그런 다음 위와같이 이름을 지정하고 vpc는 EC2에서 사용하고 있는 vpc로 선택해주세요.
그런 인바운드 규칙에서 MYSQL을 사용할 것이니 MYSQL 선택하고 포트는 3306을 선택하고 소스는 EC2에서 사용하고 있는 보안그룹 이름과 같은걸 선택해주세요.
EC2 인스턴스에서 보안그룹을 보면 3개가 있는데
이거를 선택해줘야 한다.
여기까지 진행되면 EC2가 생성되고 EC2 보안그룹이 생성이 됐고 RDS 보안그룹에는 EC2 보안그룹이 인바운드 규칙에 추가가 된것이다.
[RDS]에서 [서브넷 그룹] 메뉴를 선택하고 DB서브넷 그룹 생성 버튼을 눌러주세요.
서브넷 그룹 이름을 생성하고 vpc를 이번에도 마찬가지로 ec2에서 사용하고 있는 vpc 로 선택해주세요. 그런 다음 여기에 속해있는 서브넷을 모두 추가해주세요. ec2 가용영역에 있는 모든 서브넷을 추가하는것 입니다.
라미터 그룹을 생성하는 이유는 한글 사용을 가능하게 하기 위함과 DB의 시간을 한국 시간으로 설정하기 위함입니다.
방금 생성된 파라미터 그룹을 누르고 파라미터 그룹 작업의 편집을 누르고 들어오세요.
그런다음 char을 검색하고 모든 값을 utf8으로 바꿔주세요.(boolean타입은 안바꿔도 됩니다.)
그런다음 collation을 검색하고 이번에는 나와 있는 모든 값을 utf8_general_ci로 바꾸세요.
이제 DB 시간설정을 하겠습니다. 이번에는 zone 이라고 검색하고 time_zone을 Asia/Seoul로 바꿔주세요.
여기까지 하면 DB 서브넷에 EC2 연결이 되고 보안그룹은 EC2 인스턴스에 vpc와 보안그룹으로 연결해주고 서브넷은 vpc로 연결해준 것이다.
데이터베이스 생성
데이터베이스 생성 방식 선택
표준 생성 을 선택하고 엔진 옵션을 MySQL , 버전은 8.0.27을 선택, 템플릿은 프리티어를 선택한다.(AWS 회원가입시 프리티어를 1년간 사용 할 수 있다고 한다. )
DB인스턴스 식별자, 마스터 사용자 이름, 비밀번호를 입력한다. ( 본인이 기억할 수 있는 것 선택하기 )
DB 인스턴스 식별자
AWS 리전에서 AWS 계정이 소유하는 모든 DB 인스턴스에 대해 고유한 식별자를 작성합니다.
스토리지 항목
기본으로 입력된 스토리지 사이즈 및 옵션의 확인이 가능하다.
연결
연결 안함도 해보고 연결도 해보고 그랬지만 연결 안하고 나중에 연결하려고 하니 과금이 나온다는 글을 보고 연결을 한 상태로 진행했다.
여기서 EC2 컴퓨팅 리소스 연결로 클릭하면 기존에 있는 EC2 인스턴스가 나온다. 그것을 선택하면 자동으로 연결해준다.
서브넷은 위에서 생성한 ec2 vpc로 연결한 서브넷을 넣어준다. 여기서 보면 퍼블릭 액세스
가 아니요만 되어 있는데 이것은 수정으로 예
로 할 수 있다.
여기서는 찾아본게 너무 각자가 달라서 잘 모르겠어서 3개 다했다. 1번째는 RDS 서브넷이고 2번째는 EC2만들 때 생기던 거고 3번째는 EC2 보안그룹이다.
데이터베이스 인증
추가 구성
데이터베이스를 보면 연결 및 보안에서
이렇게 연결되어 있는 것을 볼 수 있다. RDS에 연결된 보안그룹들이다.
이거를 클릭하면 먼저 shopping
을 클릭해보자!
그리고 인바운드 규칙 편집하기를 눌러서 편집을 해야한다.
mysql을 사용할거니 mysql을 해주고 sg는 EC2 보안그룹을 해주면 된다. 현재 launch-wizard-1
이다.
이제 launch-wizard-1
로 가보자!
여기서 RDS 보안그룹을 넣으면 된다. 첫번째 가린거는 ip주소고 두 번째가 RDS 보안그룹이다.
여기 sg는 데이터베이스의 보안그룹이다.
정리해보자면,
EC2 생성
EC2 보안그룹 생성
RDS 보안그룹 생성
RDS 보안그룹에 EC2 VPC & EC2 보안그룹을 연결
RDS 서브넷 생성
RDS 보안그룹에 EC2 VPC 연결
RDS 파라미터 생성
여기서는 한국시간과 한글을 넣을 수 있게 처리하는 곳
우분투에 mysql 다운
sudo apt install mysql-server
비밀번호 치는게 나오는데 이 때 비밀번호를 치는게 안나온다.
그냥 쳐서 맞으면
이렇게 나온다.
RDS에 연결하기 위해 채워야하는 칸들입니다.
여기서 이런 오류가 발생하면 먼저 EC2
에서 연결된 RDS
인바운드 규칙을 수정하고 RDS
에서 인바운드 규칙을 추가해준다.
그리고 RDS의 퍼블릭 액세스 여부를 허용하고, 보안 그룹에서 3306 포트까지 열어줬지만 여전히 외부 연결이 안되는 사람들은 대부분 RDS가 사용하는 서브넷이 Public이 아닌 Private이라서 문제가 생기는 것이다. 그래서 서브넷을 Public으로 변경하는 방법에 대해 알려드리고자 한다.
CI/CD는 애플리케이션 개발 단계로부터 배포 때까지의 모든 단계를 자동화
를 통해서 좀더 효율적이고 빠르게 사용자에게 빈번히 배포할 수 있는것을 말한다.
테스트, 빌드, Dockerizing, 저장소에 전달하는 것까지 프로덕션 환경으로 서비스를 배포할 수 있도록 준비하는 프로세스, 지속적인 통합
1. 코드 변경사항을 주기적으로 빈번하게 merge
오랜 기간 동안 개발을 진행하다가 한번에 merge를 한다면 많은 충돌이 일어날 것이다. 그렇기에, 가능한 작은 단위로 나누어서 주기적으로 빈번히 개발하고 계속해서 통합하여 나가는 것이 중요하다.
흐름을 보자면,
2. 통합 단계의 자동화
1번의 방법은 너무나 귀찮은 단점이 있다. 여기서 자동화를 사용한다면 귀찮은 작업을 줄일 수 있다.
자동화를 사용한 이후의 흐름
저장소로 전달된 프로덕션 서비스를 실제 사용자들에게 배포하는 프로세스, 지속적인 배포
deploy(배포)
CD를 적용한 후의 흐름
만약 CI/CD를 하지 않는다면 위와 같은 작업을 매번 기능을 수정될 때마다 수작업을 적용시켜야하나 CI/CD를 적용하면 간단한 명령어 만으로 위와 같은 작업을 자동으로 수행
대표적인 것 : github action, jenkins 등이 존재
이러한 CI/CD의 논리는 DevOps 방식의 논리를 극한으로 끌어올리는 경우
Github Action
은 Github 저장소를 기반으로 소프트웨어 개발 Workflow
를 자동화 할 수 있는 도구이다. 테스트, 빌드, 배포 등의 다양한 작업들을 자동화하여 처리한다. GitHub Actions는 코드 저장소(repository)로 유명한 GitHub에서 제공하는 CI(Continuous Integration, 지속 통합)
와 CD(Continuous Deployment, 지속 배포)
도구이다. 당연히 GitHub에서 코드를 관리하고 있는 소프트웨어 프로젝트에서 사용할 수 있으며 개인은 누구나 GitHub에서 코드 저장소를 무료로 만들 수 있기 때문에 다른 CI/CD 서비스 대비 진입장벽이 낮은 편입니다.
Github Action을 사용하면 자동으로 코드 저장소에서 어떤 이벤트가 발생했을 때 특정 작업이 일어나게 하거나 주기적으로 어떤 작업들을 반복해서 실행시킬 수도 있습니다. 예를 들어, 누군가가 코드 저장소에 Pull Request를 생성하게 되면 GitHub Actions를 통해 해당 코드 변경분에 문제가 없는지 각종 검사를 진행할 수 있습니다. 어떤 새로운 코드가 메인(main) 브랜치에 유입(push)되면 GitHub Actions를 통해 소프트웨어를 빌드(build)하고 사용 서버에 배포(deploy)할 수도 있습니다. 뿐만 아니라 매일 밤 특정 시각에 그날 하루에 대한 통계 데이터를 수집시킬 수도 있습니다.
이렇게 소프트웨어 프로젝트에서 지속적으로 수행해야하는 반복 작업들을 업계에서는 소위 CI/CD
라고 많이 줄여서 부릅니다. 사람이 매번 직접 하기에는 비효율적인데다가 실수할 위험도 있기 때문에 GitHub Actions와 같은 자동화시키는 것이 유리합니다.
GitHub Actions는 기존 CI/CD 서비스 대비 간편한 설정과 높은 접근성으로 특히 개발자들 사이에서 많은 호응을 얻고 있는데요. 예전에는 CI/CD가 DevOps 엔지니어의 전유물로만 여겨지곤 했었는데 최근에는 GitHub Actions을 통해서 일반 개발자들도 어렵지 않게 CI/CD 설정을 스스로 하는 것을 보게 됩니다.
GitHub Actions에서 가장 상위 개념인 워크플로우
(Workflow, 작업 흐름)는 쉽게 말해 자동화해놓은 작업 과정이라고 볼 수 있습니다. 하나 이상의 job으로 이루어져 있으며 이벤트에 의해 실행된다. 워크플로우는 코드 저장소 내에서 .github/workflows
폴더 아래에 위치한 YAML 파일로 설정하며, 하나의 코드 저장소에는 여러 개의 워크플로우, 즉 여러 개의 YAML 파일을 생성할 수 있습니다. Workflow는 Github 저장소에서 발생하는 build, test, package, release, deploy 등 다양한 이벤트를 기반으로 직접 원하는 Workflow를 만들 수 있다. Workflow는 Runners라고 불리는 Github에서 호스팅 하는 Linux, macOS, Windows 환경에서 실행된다. 그리고 이 Runners를 사용자가 직접 호스팅하는 환경에서 직접 구동시킬 수도 있다.(self-hosted runner)
이 워크플로우 YAML 파일에는 크게 2가지를 정의한다.
1) on
속성을 통해서 해당 워크플로우가 언제 실행되는지를 정의
코드 저장소의
main
브랜치에push
이벤트가 발생할 때 마다 워크플로우를 실행하려면 다음과같이 설정
다른 예시는 매일 자정에 워크플로우를 실행하려면 다음과 같이 설정합니다.
2) jobs
속성을 통해서 해당 워크플로우가 구체적으로 어떤 일을 해야는지 명시해야 한다.
Github Actions에서 작업(Job)
이란 독립적인 가상 머신(machine) 또는 컨테이너(container)에서 돌아가는 하나의 처리 단위를 의미합니다. 하나의 워크플로우는 여러 개의 작업으로 구성되며 적어도 하나의 작업은 있어야 합니다. 그리고 모든 작업은 기본적으로 동시에 실행되며 필요시 작업 간에 의존 관계를 설정하여 작업이 실행되는 순서를 제어할 수 있습니다.
작업은 워크플로우 YAML 파일 내에서 jobs
속성을 사용하며 작업 식별자(ID)와 작업 세부 내용 간의 맵핑(mapping) 형태로 명시가 된다.
예를 들어, job1
, job2
, job3
이라는 작업 ID를 가진 3개의 작업을 추가하려면 다음과 같이 설정한다.
작업의 세부 내용으로는 여러가지 내용을 명시할 수 있는데 필수로 들어가야 하는 runs-on
속성을 통해 해당 리눅스나 윈도우즈와 같은 실행 환경을 지정해줘야 합니다.
예를 들어, 가장 널리 사용되는 우분투의 최신 실행 환경에서 해당 작업을 실행하고 싶다면 다음과 같이 설정한다.
작업에서 가장 중요한 부분은 작업 순서를 정의하는 것일텐데요. 이 부분은 steps 속성을 통해서 설정을 하며 다음 섹션에서 자세히 알아보겠습니다.
보통 작업은 일반적으로 여러 단계의 명령을 순차적으로 실행하는 경우가 많은데 Github Actions에서는 각 작업(job)이 하나 이상의 단계(step)로 모데링 된다.
작업 단계는 단순한 커맨드(command)나 스크립트(script)가 될 수도 있고 다음 섹션에서 자세히 설명할 액션(action)이라고하는 복잡한 명령일 수도 있습니다. 커맨드나 스크립트를 실행할 때는 run
속성을 사용하며, 액션을 사용할 때는 uses
속성을 사용합니다.
예를 들어, 자바스크립트 프로젝트에서 테스트를 돌리려면 코드 저장소에 코드를 작업 실행 환경으로 내려 받고, 패키지를 설치한 후, 테스트 스크립트를 실행해야 한다. 이 3단계의 작업은 아래와 같이 steps
속성을 통해서 명시될 수 있다.
워크플로우 파일 내에서 작업 단계를 명시해줄 때는 주의할 부분이 있다. YAML 문법에서 시퀀스(sequence) 타입을 사용하기 때문에 각 단계 앞에 반드시 -
를 붙여야 한다.
마지막으로 살펴볼 개념은 Github Actions의 꽃이라고 할 수 있는 액션(action)
이다. 액션은 Github Action에서 빈번하게 필요한 반복 단계를 재사용하기 용이하도록 제공되는 일종의 작업 공유 메커니즘이다. 이 액션은 하나의 코드 저장소 범위 내에서 여러 워크플로우 간에서 공유를 할 수 있을 뿐만 아니라, 공개 코드 저장소를 통해 액션을 공유하면 Github 상의 모든 코드 저장소에서 사용이 가능해집니다.
Github에서 제공하는 대표적인 공개 액션으로 바로 위 예제에서도 사용했던 체크 아웃 액션 (actions/checkout
)을 들 수 있는데 대부분의 CI/CD 작업은 코드 저장소로 부터 코드를 작업 실행 환경으로 내려받는 것으로 시작하므로 이 액션은 범용적으로 사용한다.
뿐만 GitHub Marketplace에서는 수많은 벤더(vendor)가 공개해놓은 다양한 액션을 쉽게 접할 수가 있습니다. 한 마디로 이 액션을 중심으로 하나의 큰 커뮤니티가 형성이 되고 더 많은 사용자와 벤더가 GitHub Actions으로 몰려드는 선순환이 일어나고 있습니다.
Github 액션 러너 애플리케이션이 설치된 서버이다. runner
는 workflow가 트리거될 때 실행하는 서버입니다. 각 runner는 1번에 1개의 job을 실행할 수 있습니다. Github에서 호스팅해주는 Github-hosted runner와 직접 호스팅하는 Self-hosted runner로 나뉩니다. Github-hosted runner는 Azure의 Standard_DS2_v2로 vCPU 2, 메모리 7GB, 임시 스토리지 14GB입니다.
워크 플로우를 실행하는 특정 활동이나 규칙이다. 커밋의 push, pull request가 생성되었을 때뿐만 아니라 저장소 dispatch event를 통해 Github 외부에서 발생하는 활동으로도 이벤트를 발생시킬 수도 있다.
# push나 pull request가 발생할 때 워크 플로우 실행
on: [push, pull_request]
또한 schedule에 POSIX cron 문법으로 스케쥴 이벤트를 발생시킬 수도 있다.
on:
schedule:
- cron: '*/15 * * * *'
jobs:
example-job:
runs-on: unbuntu-latest
steps:
- name: Retrieve secret
# 환경변수로 저장하고
env:
super_secret: ${{ secrets.SUPERSECRET }}
# 저장한 환경변수를 활용한다.
run: |
example-command "$super_secret"
needs
키워드를 통해 작업이 의존성을 갖도록 지정하면 된다.jobs:
setup:
runs-on: ubuntu-latest
steps:
- run: ./setup_server.sh
build:
needs: setup
runs-on: ubuntu-latest
steps:
- run: ./build_server.sh
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
needs
키워드를 통해 build, test 작업이 각각 이전 작업인 setup, build 작업이 끝난 후에 실행되도록 설정했다.
strategy
키워드를 사용하면 된다.jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
# 다양한 버전의 Node.js를 이용하여 작업을 여러번 실행
node: [6, 8, 10]
steps:
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
GIthub의 러너는 각 작업에서 새로운 환경으로 실행되므로 작업들이 종속성을 재사용하는 경우 파일들을 캐싱하여 성능을 높이면 된다. 캐시를 생성하면 해당 저장소의 모든 워크 플로우에서 사용 가능하다.
jobs:
example-job:
steps:
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# `~/.npm` 디렉토리를 캐시해 성능을 높인다
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
저장소에 .github/workflows
폴더를 만들어서 .yml
형식 파일을 만든 뒤 Workflow를 정의하는 것이다.
간단한 예시
⚠️ 반복 스케줄은 UTC 타임존 시간을 사용하고, 기본 브랜치나 마지막 커밋을 기준으로 동작한다. 최소 간격은 5분까지만 가능하다.
예를 들자면 master
브랜치 또는 v1
태그가 push되었을 때 test
폴더 안에 어떤 파일이 변경된 경우에 workflow가 실행되게 할 수 있다.
실행환경은 runs-on
항목으로 지정하고, github이 호스팅하는 머신 환경은 Ubuntu
,Windows
, MacOS
가 있다.
예를들어, NPM 패키지를 빌드해서 배포하거나 Go 바이너리를 Github 저장소에 release에 올리는 동작을 적절한 도구나 여러 API를 조합해서 만든 것을 Action으로써 활용하는 것이다. 그리고 이렇게 만든 Action을 공유하면 마치 코딩할 때 라이브러리를 가져다 쓰는 것처럼 쓸 수 있다.
도커 컨테이너로 Action을 만드는 경우 일관된 동작을 보장할 수 있는 장점이 있지만, Github이 호스팅하는 리눅스 환경에서만 실행할 수 있다.
Javasript로 Action을 만들면 좀 더 간단하며, 도커 컨테이너보다 더 빨리 실행된다.
이렇게 만든 Action에 필요한 Input, Output 설정이나 메인 실행 파일 같은 메타 정보를 정의하기 위해 action.yaml
또는 action.yml
을 반드시 작성해야 한다.
action.yml
마지막에 정의한 index.js
이름으로 메인 실행 파일을 만든다.
Javascript로 Action 로직을 만드는 경우 도움이 될만한 Node.js 패키지가 있다.
@actions/core, @actions/github 이 두 패키지는 Input, Output을 쉽게 처리하고, Github Action 컨텍스트에 접근할 수 있는 인터페이스 등을 제공한다.
Action 저장소가 다른 저장소의 Workflow에서 사용되려면 반드시 Public 저장소로 설정되어 있어야 한다. 아래 내용은 위에서 살펴본 Workflow 예제에 Hello world Step 부분을 수정했다.
만약 Action을 공유할 목적이 아니라 특정 프로젝트에 종속된 구성을 한다면 action.yaml
파일을 어디에 두던 상관이 없다. 하지만 .github/actions/action1
, .github/actions/action2
같은 구성을 하는 것이 관리하는 측면에서 유용하다.
Action을 독립된 Git 저장소로 구성한다면, 반드시 Public 저장소 타입으로 만들어야 한다.
하지만 이 저장소가 바로 Github 마켓 플레이스에 올라가는 건 아니다. 하지만 Github이 자동으로 action.yml
을 감지해서 마켓 플레이스에 올릴 수 있다는 배너를 노출해준다.
GitHub Actions는 YAML 구문을 사용하여 워크플로를 정의합니다. 각 워크플로는 .github/workflows라는 디렉터리의 코드 repository에 별도의 YAML 파일로 저장됩니다.
직접 작성할 수도 있지만 아래와 같이 템플릿을 활용하면 좋습니다.
Configure 버튼을 누르면 쉽게 workflow를 생성할 수 있습니다.
workflow 파일 내 컴포넌트 이해하기
master 브랜치에 Push 또는 Pull Request가 올 경우 실행되는 CI란 이름을 갖는 Workflow 파일입니다.
위와 같이 저장을 한다면 아래 예시처럼 시각화된 workflow를 볼 수 있습니다.
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: push
# 또는
on: [pull_request, issues]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run a one-line script
run: echo Hello, world!
- name: Run a multi-line script
run: |
echo Add other actions to build,
echo test, and deploy your project.
민감한 정보 사용해야된다면 Github > Settings > secrets 에 저장하여 환경변수로 사용할 수 있습니다.
job 끼리 의존 관계를 설정해 순서를 정의할 수 있습니다.
job1은 job2이 시작되기 전에 성공적으로 완료되어야 하고 job3은 job1과 job2이 모두 완료될 때까지 기다립니다.
setup 실행, 성공하면 build가 실행되고 성공하면 test가 실행됩니다.
다양한 OS, 플랫폼, 언어의 여러 조합에서 테스트를 실행하려는 경우 빌드 매트릭스를 활용하면 됩니다. 빌드 옵션을 배열로 받는 strategy 키워드를 사용하면 됩니다.
GIthub의 runner는 각 작업에서 새로운 환경으로 실행되므로 작업들이 종속성을 재사용하는 경우 파일들을 캐싱하여 성능을 높일 수 있습니다. 캐시를 생성하면 해당 저장소의 모든 workflow에서 사용할 수 있습니다.
artifact를 사용하면 job끼리 데이터를 공유하거나 workflow가 끝나고 데이터를 저장할 수 있습니다. workflow에서 생성된 파일을 upload하고, 다른 job에서 사용할 경우 업로드된 파일을 download해서 사용하는 방식입니다. job_1에서 math-homework.txt 파일을 생성하고 업로드합니다. 다음 job_2는 job_1에서 업로드한 파일을 다운로드하고 연산한 후, 다시 업로드합니다. job_3은 job_2에서 업로드한 파일을 다운받아 출력하고 끝나는 예시입니다.
프로젝트 최상단에 .github
폴더를 생성하고 그 안에 workflows
폴더를 만들고 ci.yml
을 만든다음 다음과 같이 작성한다.
# 1. 워크플로의 이름 지정
# 이 워크플로는 CI를 실행하기 위한 스크립트 모음이므로 ci로 지정
name: CI
# 2. 워크플로가 시작될 조건 지정
# 워크플로를 시작할 트리거 조건을 지정합니다.
on:
push:
branches: [ main ]
jobs:
build:
# 3. 실행 환경 지정
# 리눅스나 윈도우와 같은 실행 환경을 지정합니다.
runs-on: ubuntu-latest
# 4. 실행 스텝 지정
# 실행 스텝을 그룹화합니다.
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution : 'zulu'
java-version : '11'
- name : Grant execute permission for gradlew
run: chmod + x gradlew
- name : Build with Gradle
run: ./gradlew clean build
uses : 지정한 레포지토리를 확인하고 코드에 대한 작업을 실행할 수 있습니다. actions/checkout에는 checkout이라는 작업의 v3 버전을 실행합니다.
name : 스텝의 이름을 지정
run : 실행할 명령어를 입력합니다. ./gradlew clean build
그레들을 사용해 프로젝트 빌드 이전 상태로 돌리고 다시 빌드하는 명령어를 실행합니다.
여기서 에러가 뜨는데
처음에러는
chmod: cannot access 'x': No such file or directory
Error: Process completed with exit code 1.
##[debug]Finishing: Grant execute permission for gradlew
이런 에러였다.
에러가 뜨는 이유는 run: chmod +x gradlew 여기서 + x
이렇게 띄어쓰기를 해서 오류가 발생했다.
그리고 다시 push를 했는데 에러가 발생했다.
github action에서 mysql server를 시작하지 않아서 connection 과정에서 오류가 난 거였다. 이번에는 DB 문제여서 다음과 같이 추가했다.
- name: Setup MySQL
uses: samin/mysql-action@v1
with:
character set server: 'utf8'
mysql database: '테스트에 사용할 db이름'
mysql user: '유저 이름'
mysql password: ${{ secrets.MYSQL_PASSWORD }} # github Secret 탭에서 설정하세요!
여기서 ec2랑 연결하는 방법은 EC2와 직접 연결하는 방법
과 AWS Elastic Beanstalk 사용하는 방법
이 있다.
- name: Set up SSH key
uses: webfactory/ssh-agent@v0.4.1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: SSH into EC2 and deploy
run: |
ssh -i ${{ secrets.SSH_PRIVATE_KEY }} ec2-user@your-ec2-ip "cd /path/to/your/app && git pull origin main && ./deploy.sh"
# 1. 워크플로의 이름 지정
# 이 워크플로는 CI를 실행하기 위한 스크립트 모음이므로 ci로 지정
name: CI
# 2. 워크플로가 시작될 조건 지정
# 워크플로를 시작할 트리거 조건을 지정합니다.
on:
push:
branches: [ main ]
jobs:
build:
# 3. 실행 환경 지정
# 리눅스나 윈도우와 같은 실행 환경을 지정합니다.
runs-on: ubuntu-latest
# 4. 실행 스텝 지정
# 실행 스텝을 그룹화합니다.
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution : 'zulu'
java-version : '11'
cache : gradle
- name: List directory contents
run: ls -l
- name : Grant execute permission for gradlew
run: chmod +x gradlew
- name : Build with Gradle
run: ./gradlew clean build
- name: Setup MySQL
uses: samin/mysql-action@v1
with:
character set server: 'utf8'
host port : 3306
mysql database: 't'
mysql user: 'root'
mysql password: ${{ secrets.MYSQL_PASSWORD }} # github Secret 탭에서 설정하세요!
- name: Setup OAuth2
run: |
# OAuth2 설정 정보 가져오기
CLIENT_ID=$OAUTH2_CLIENT_ID
CLIENT_SECRET=$OAUTH2_CLIENT_SECRET
# 여기에서 OAuth2 설정을 사용하여 작업 수행
env:
OAUTH2_CLIENT_ID: ${{ secrets.OAUTH2_CLIENT_ID }}
OAUTH2_CLIENT_SECRET: ${{ secrets.OAUTH2_CLIENT_SECRET }}
- name: Setup JWT
run: |
# JWT 설정 정보 가져오기
SECRET_KEY=$JWT_SECRET_KEY
# 여기에서 JWT 설정을 사용하여 작업 수행
env:
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
- name: Setup S3
run: |
# S3 설정 정보 가져오기
ACCESS_KEY=$S3_ACCESS_KEY
SECRET_KEY=$S3_SECRET_KEY
# 여기에서 S3 설정을 사용하여 작업 수행
env:
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
여기서 보면 ${{ secrets.MYSQL_PASSWORD }}
이렇게 나오는데 EC2에서 환경변수로 비번을 가져오듯이 secret
에 넣어서 가져와야 한다.
프로젝트 세팅에가 가서
이렇게 prod.yml 세팅
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${google_client}
client-secret: ${google_secret}
redirect-uri: http://localhost:8080/login/oauth2/code/google
scope:
- email
- profile
# naver
naver:
client-id: ${naver_client}
client-secret: ${naver_secret}
client-name: Naver
redirect-uri: http://localhost:8080/login/oauth2/code/naver
authorization-grant-type: authorization_code
scope:
- name
- email
provider:
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response
mvc:
pathmatch:
matching-strategy: ant_path_matcher
jwt:
secret_key: ${jwt_secret}
access:
expiration: 3600000000
refresh:
expiration: 1209600000000
cloud:
aws:
credentials:
access-key: ${s3_access}
secret-key: ${s3_secret}
s3:
bucket: ${s3_bucket}
region:
# 버킷 생성시 선택한 AWS 리전
static: ap-northeast-2
auto: false
stack:
# 설정한 CloudFormation 이 없으면 프로젝트 시작이 안되니, 해당 내용을 사용하지 않도록 false 를 등록
auto: false
---
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${rds_mysql_url}
username: ${rds_mysql_name}
password: ${rds_mysql_secret}
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
# 영속성 컨텍스트의 생존 범위를 트랜잭션 범위로 한정
# 실시간 트래픽이 중요한 API 애플리케이션에는 false로 유지
open-in-view: true
# JPA 처리 시에 발생하는 SQL을 보여줄 것인지 결정합니다.
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
# 실제 JPA의 구현체인 Hibernate 가 동작하면서 발생하는 SQL을 포맷팅해서 출력합니다.
# 실행되는 SQL의 가독성을 높여 줍니다.
format_sql: true
# 일대다 컬렉션 조회 시 성능 최적화
default_batch_fetch_size: 500
이렇게 git action을 사용하면 ec2에 배포할 때 따로 ec2 환경변수 처리할 필요 없이 여기에 접근하면 된다.
그러면 prod.yml까지 git에 올리고 ec2를 사용하면 ec2는 git action 워크플로를 찾고 워크플로는 시크릿에 등록된 비번을 찾아서 전달해준다. 나머지 환경 설정은 prod에 있기때문에 워크플로에 적지 않아도 된다.
위에서 jar로 빌드를 해서 직접 넣어주는 작업을 했지만 Git action을 사용하기 위해서 우분투에 연결하기 위해서 해당 작업을 진행했습니다.
sudo apt-get update && upgrade // apt-get 업데이트 && 업그레이드
sudo apt-get install git // git 설치
EC2는 우분투라서, apt-get을 이용했다
apt-get을 업그레이드한 후, git을 설치했다.
sudo apt-get install npm
git clone {깃허브 저장소 주소}
여기서 보면 ./gradlew clean build
을 하는데 또 clone을 하는 것을 볼 수 있습니다. GitHub Actions 워크플로가 트리거되면 GitHub의 워크플로 실행 환경(에이전트)에서 작업을 시작합니다. git clone 단계에서는 GitHub 저장소의 코드를 에이전트로 복제합니다. 그 다음, ./gradlew clean build 단계에서 Gradle을 사용하여 코드를 빌드하고 JAR 파일을 생성합니다. 이 JAR 파일은 에이전트의 작업 디렉토리에 생성됩니다. 따라서 코드 가져오기와 빌드 작업은 GitHub Actions 에이전트 내에서 수행되며, 그 결과로 생성된 JAR 파일은 AWS Elastic Beanstalk에 배포됩니다.우분투 서버에서 직접 코드를 가져오거나 빌드하는 것이 아니라 GitHub Actions 에이전트에서 실행되는 것이 주요 동작 방식입니다.
여기서 두 가지 방법이 있습니다.
ec2를 직접연결할지 Beanstalk에 연결할지에 따라 달라진다.
직접연결
steps:
- name: excuting remote ssh commands
uses: appleboy/ssh-action@v0.1.6 # ssh 접속하는 오픈소스
with:
host: ${{ secrets.REMOTE_IP }} # 인스턴스 IP
username: ${{ secrets.REMOTE_USER }} # 우분투 아이디
key: ${{ secrets.REMOTE_PRIVATE_KEY }} # ec2 instance pem key
port: ${{ secrets.REMOTE_SSH_PORT }} # 접속포트
script: | # 실행할 스크립트
cd /home/ubuntu/Server-V2
git pull origin dev
pm2 kill
npm i --force
npm run build
pm2 start dist/main.js
그리고 시크릿에 적어준다.
Beanstalk
# 1. 워크플로의 이름 지정
# 이 워크플로는 CI를 실행하기 위한 스크립트 모음이므로 ci로 지정
name: CI
# 2. 워크플로가 시작될 조건 지정
# 워크플로를 시작할 트리거 조건을 지정합니다.
on:
push:
branches: [ main ]
jobs:
build:
# 3. 실행 환경 지정
# 리눅스나 윈도우와 같은 실행 환경을 지정합니다.
runs-on: ubuntu-latest
# 4. 실행 스텝 지정
# 실행 스텝을 그룹화합니다.
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution : 'zulu'
java-version : '11'
cache : gradle
- name: List directory contents
run: ls -l
- name : Grant execute permission for gradlew
run: Beanstalk +x gradlew
- name : Build with Gradle
run: ./gradlew clean build
- name: Setup MySQL
uses: samin/mysql-action@v1
with:
character set server: 'utf8'
host port : 3306
mysql database: 't'
mysql user: 'root'
mysql password: 1234 # github Secret 탭에서 설정하세요!
# 현재시간 가져오기
- name : Get current time
uses: josStorer/get-current-time@v2.0.2
id : current-time
with:
format : YYYY-MM-DDHH-mm-ss
utcOffset : "+09:00"
# 배포용 패키지 경로 저장
- name : Set artifact
run: echo "artifact=$(ls ./build/libs)" >> $GITHUB_ENV
# 빈스토크 배포
- name: Beanstalk Deploy
uses: einaregilsson/beanstalk-deploy@v20
with:
aws_access_key : ${{secrets.AWS_ACCESS_KEY}}
aws_secret_key : ${{secrets.AWS_SECRET_KEY}}
application_name : springboot-developer
environment_name : springboot-developer-env
version_label : github-action-${{steps.current-time.outputs.formattedTime}}
region : ap-northeast-2
deployment_package : ./build/libs/${{env.artifact}}
josStorer/get-current-time@v2.0.2
현재 시간을 가져옵니다. 가져온 시간은 배포 버전을 지정할 때 사용됩니다.
Set artifact
빌드 이후에 생성된 jar 파일을 찾아 "artifact"라는 환경 변수에 값을 넣어줍니다. $GITHUB_ENV
를 사용해 깃허브 워크플로 전체적으로 사용할 수 있는 환경 변수를 설정할 수 있습니다.
AWS EB란 서비스를 배포하고 확장, 관리하는데 있어 쉽고 빠르게 할 수 있도록 돕는 완전 관리형 서비스이다. Auto Scaling, 용량 Provisioning, 로드 밸런싱, 모니터링, 다중 인스턴스 배포, 로그 관리 등 많은 기능들을 지원한다. BeanStalk은 Docker 컨테이너를 기반으로 애플리케이션을 쉽게 배포하고, 운영하고, 관리하는걸 도와주는 AWS 서비스다. 별도의 서비스 사용료는 없고, 사용한 AWS 리소스 만큼 비용이 과금된다고 한다.
빈스토크 배포를 진행합니다. 여기에서 지정한 secrets.AWS_ACCESS_KEY와 secrets.AWS_SECRET_KEYsms git action에서 가져오는 비밀 값입니다. 이 값은 AWS에서 만든 뒤 깃허브에서 설정해야 합니다. 또한 application_name
과 environment_name
는 일래스틱 빈스토크에서 확인할 수 있습니다.
# 배포용 패키지 경로 저장
- name: Set artifact
run: echo "artifact=$(ls ./build/libs)" >> $GITHUB_ENV
워크플로우에서 env를 사용하여 환경 변수를 참조하고 있습니다. env는 GitHub Actions 워크플로우 환경 변수를 참조할 때 사용되는 예약어입니다. 따라서 ${{env.artifact}}는 GitHub Actions 환경 변수 중에 "artifact"라는 이름의 값을 참조하는 것입니다. 여기서 ${{env.artifact}}는 "Set artifact" 단계에서 설정된 GitHub Actions 환경 변수 "artifact"의 값을 가져오는 것입니다. 따라서 "artifact" 환경 변수의 값은 ${{env.artifact}}를 통해 동적으로 deployment_package에 사용됩니다.
시간을 정할 때
- name: Get current time
uses: josStorer/get-current-time@v2.0.2
id: current-time
with:
format: YYYY-MM-DDHH-mm-ss
utcOffset: "+09:00"
단일 인스턴스 선택시 로드밸런서를 사용할 수 없기에 beanstalk으로 무중단 배포가 불가능하다.
따라서 사용자 지정 구성으로 선택해준다!
Auto Scaling에서의 그룹 최대 인스턴스를 1개로 설정해줬는데, 이는 트래픽이 증가하더라도 최대 1개의 인스턴스 외에는 Auto Scaling을 적용하지 않겠다는 뜻이다. 현재 내 환경은 실제 사용자가 사용하는 환경이 아니기에 트래픽이 증가하지 않기 때문에 1개로 설정해준다.
배포 방식으로는 추가 배치를 사용한 롤링을 선택해준다.
이는 현재 인스턴스는 1개이며, 신규 인스턴스 배포시 신규 버전을 올리고 문제가 없다면 기존 인스턴스를 죽이는 방식이다. (서비스 중단없이 배포 가능한 방법)
Actions 연결을 위해 Beanstalk에 접근 권한을 가진 유저를 만들어주자.
IAM > 사용자 추가를 클릭한다.
사용자 이름을 적고 엑세스 유형을 선택한다.
git-action으로 해주었다.
elastic beanstalk의 admin 권한을 주는 정책을 적용해준다. 원래 필요한 권한만 주는게 좋지만 테스트이니 관리자 권한을 준다.
사용자를 클릭해서 액세스 키
를 만듭니다. 액세스 키 항목에서 찾을 수 있습니다. 서드파티 시비스
를 선택하고 설명 태그 값을 git-action
으로 해 액세스 키를 만든다.
이때 제공하는 엑세스 키 ID와 비밀 엑세스 키를 .csv로 저장해둘 수 있다.
이것을 잘 보관하도록 하자!
이것을 이용해서 Actions에서 AWS console에 접근할 수 있다.
여기 까지하면 레포지토리에 작업을하고 업로드하면 깃허브 액션이 빌드를 자동으로 실행하고 빌드에 성공하면 새 버전을 빈스토크에 배포합니다.
빈즈토크를 사용하면 AWS Elastic Beanstalk를 통해 배포가 이루어집니다. 빈즈토크를 사용하더라도, 배포 및 애플리케이션 시작에는 일정 시간이 필요할 수 있습니다. 무중단 배포를 지원하긴 하지만, 새로운 버전의 애플리케이션이 배포되고 시작될 때 일시적으로 이중 운영 모드가 될 수 있습니다.
주의점
S3, JWT, OAuth2 설정의 시크릿 키 및 액세스 키가 프로젝트의 다양한 부분에서 사용되는 경우, 이름이 일치해야 합니다. prod.yml 파일에 설정한 ${} 변수 이름은 GitHub Actions 워크플로우의 시크릿에 저장한 이름과 동일해야 합니다.
시크릿에 저장된 정보는 워크플로우 내에서 해당 이름을 사용하여 불러올 수 있습니다. 이렇게 함으로써 GitHub Actions에서 시크릿을 활용하여 프로젝트 설정에 필요한 중요 정보를 동적으로 로드할 수 있습니다.
이렇게 이름을 일치시키면 워크플로우가 prod.yml 내의 ${} 변수에 접근하고 실제 시크릿 값을 가져와 설정에 적용합니다. 만약 이름이 일치하지 않으면 워크플로우는 해당 시크릿을 찾을 수 없을 것이므로 설정에 제대로 적용되지 않을 것입니다.