실습(AWS, GitAction)

유요한·2023년 12월 21일
0

AWS

목록 보기
2/2
post-thumbnail

배포

배포는 EC2로 배포하였고 RDS(MySQL)을 사용하였다.
현재 프로젝트를 진행하면서 했던 배포 방식이다. 하지만 실제로는 약간 다른데 기존에 먼저 제가 배포를 했다가 프리티어 기간이 끝나가고 있고 윈도우 환경에서 느려서 지금 정리한 배포환경을 바꾸고 MAX환경으로 배포환경을 옮겼습니다.

바뀐점

  • 우분투 → 리눅스
  • git action CI/CD 방식

EC2 구축 세팅 & 사용하기

로그인

EC2 인스턴스 구축하기

인스턴스 유형은 프리 티어가 가능한 t2.micro를 선택한다. 프리 티어는 AWS 신규가입시 750시간 무료 제공 서비스이다.


만약 SSH로 키페어를 생성했으면 그대로 사용하면 됩니다.

여기서 SSH를 만들 수 있지만 따로 SSH를 만들 수 있다.

파일이 다운로드 되는데, 잃어버리면 인스턴스에 접근할 수 없으니
받은 즉시 자신의 이메일이나 git private에 올려버리자.

일단 저는 EC2 키페어 안에 저장했습니다.

보안 그룹을 생성한다.
ssh, httpshttp트래픽을 허용하자.

프리 티어의 경우 30기가 까지 사용할 수 있다고 한다. gp3가 성능이 좋다고하니 gp3로 선택한다

상태 검사를 통과 할 때까지 대기

퍼블릭 IP 주소도 확인된다. 해당 IP는 서버를 재부팅 할 경우 바뀔 수 있다. 고정하기 위해서는 탄력적 IP를 이용하자.

보안 그룹

수정하기

이제 생성한 인스턴스의 보안 그룹을 수정해 주도록 하겠습니다.

보안그룹이란?

AWS 에서 제공하는 방화벽으로 인바운드 규칙, 아웃바운드 규칙이 존재합니다.

  • 인바운드 규칙(inbound) : 외부에서 EC2나 RDS 등의 내부로 접근할때 사용되는 방화벽 규칙
  • 아웃바운드 규칙(outbound) : EC2나 RDS 등의 내부에서 외부로 접근할때 사용되는 방화벽 규칙

우리는 EC2에 접속해서 서버를 띄우는것이 목적이기 때문에 인바운드 규칙만 건드려주도록 하겠습니다.

EC2 메뉴의 네트워크 및 보안 탭의 보안 그룹 을 눌러 보안 그룹 세팅 창으로 들어갑니다.

보안 그룹 생성을 눌러 새로운 보안 그룹을 생성해 줍시다.

위 목록에서 보안 그룹의 이름과 설명을 적으시고

아까 설명한 방화벽 설정을 해주도록 하겠습니다.
인바운드 규칙의 규칙추가를 눌러

다음과 같이 세팅해주도록 합시다.(여기서 0.0.0.0/0 은 Anywhere-IPv4 입니다.)

위에서부터 순서대로

  1. 스프링 부트 기반 서버를 열어줄것이기 때문에 사용자 지정으로 8080 포트를 설정해준 뒤 url을 아는 누구나 접속할수있도록 Anywhere-IPv4 로 설정해줍니다.

  2. 원격 EC2 인스턴스에 접속할때 사용되는 ssh 관련 방화벽으로 밖에서 접속하게 하려면 고정 ip가 아닌 Anywhere-IPv4 로 설정하고 집에서만 접속하게 하려면 고정 ip를 적용합니다. 또한 ssh는 기본 포트 연결로 22번 포트가 사용됩니다.

  3. HTTP 연결시 사용됩니다.

  4. HTTPS 연결시 사용됩니다.

아웃바운드 규칙은 따로 세팅해주지 않고 기본세팅으로 남기고 넘어가도록 하겠습니다.

한글이 들어가면 안된다!

보안 그룹 설정하기

이제 만든 보안 그룹을 아까 만든 EC2에 설정해줄 차례입니다.

EC2 콘솔에 들어가 아까 만든 인스턴스를 클릭하고 위쪽의 작업 -> 네트워킹 -> 보안 그룹 변경 을 눌러주고 기존의 기본 보안 그룹은 체크 해제후 방금 만든 보안그룹으로 교체해줍니다.

보안그룹이 잘 변경되었는지 확인하고 인바운드 규칙 보기 를 눌러 인바운드 규칙이 잘 적용 되었나 확인 해주세요.

발급 받은 ssh 키로 Ec2 우분투 콘솔에 접속하기

이제 EC2를 만들었으니 원격으로 EC2에 접속할 시간입니다.

방금 만든 EC2 인스턴스가 체크되어있는 상태에서 위쪽의 연결 버튼을 눌러줍시다. 그러면

다음과 같은 연결하는 방법이 친절하게 나와있으므로 이것을 차례대로 따라하기만 하면 됩니다.

mobaxTerm으로 SSH으로 EC2 연결

SSH 적용

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를 눌러줍니다.

  1. ppk 확장자 Key 생성
    AWS에서 생성한 pem확장자는 MobaXterm에서 서버 접근이 되지 않습니다. ppk형식의 Key를 MobaXterm에 세팅해야 접속이 됩니다.
    키 페어가 생성이되면 로컬에 다운이 됩니다.

  1. PuTTy 다운로드
    Conversins > ImportKey > Parameters에 SSH-1(RSA) 클릭 > Save private Key 클릭

  2. MobaXterm 실행
    그대로 쓰면 한글 파일 명이 출력이 안되고 문자가 깨지기에 Character set 변경을 필수이다. 세팅 옵션 > 터미널 탭으로 접속 후, Term Charset을 클릭 후 EUC-KR으로 변경 후 저장하게 되면 해당 캐릭터 셋으로 세팅 되어 서버 작업이 가능하게 된다.

  3. New Session

  • SSH 클릭 후 Remote host에 Public IP를 입력한다.

    우리가 만져야 하는 부분은 Remote host부분인데, 이 부분에는 접속하려는 서버의 Public IP 혹은 도메인 이름을 입력하면 됩니다. 위에서 생성한 EC2 인스턴스의 public IP를 적어주면 됩니다.

  • 하단의 Advanced SSH settings 중 Use private key에 체크하고 위에서 생성한 ppk Key를 넣어준다.

  • username에는 우분투 기본 유저인 ubuntu를 넣는다.
  • port는 22 (SSH)
  • Advanced SSH settings를 클릭한다.

    Use private key를 클릭하고 다운 받은 키를 넣어준다.
    EC2에서 생성한 키를 넣어준다. 만약 SSH로 키페어를 생성했으면 SSH 키페어를 넣어준다.

자주 사용하는 서버에 대해 'Bookmark settings' 탭을 이용하면 나중에 손쉽게 서버 접속을 할 수 있다. Session name에 적당한 이름을 기입하고 'Create a desktop shortcut to this session' 버튼을 클릭하면 저장이 완료된다.

  1. Server login
  • Ok버튼을 누르고 나면 login as 값을 입력하라고 뜬다.
  • 계정 id, root, 계정 username 여러가지 값을 입력해보았는데 centos를 입력하면 접속이 되는 것이었다.
  • ubuntu의 경우 그대로 ubuntu 라고 입력
    • 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입니다.
  • 세션을 추가한 후 최초로 로그인을 하시게 될 경우 자동 패스워드 저장 여부를 물어보게 된다.
  • 여기서 Yes를 누르면 향후 같은 세션에 접근하실 때 별도로 패스워드 입력 없이 자동 로그인이 가능해 진다.
  • 일단, 자동 로그인 기능을 사용하기 위해선 보안을 위해 마스터 패스워드를 등록해야 한다. 적당히 패스워드를 지정하고 OK 버튼을 누르면 다음번에 북마크에 등록된 섹션을 클릭만하게되면 별도의 작업없이 바로 ssh 접속되는걸 볼수 있을 것이다.
  1. 접속완료

아래의 방법은 블로그를 보면 보안 후 인스턴스에 연결하는 방법으로 나오는데 MobXterm에도 되는지는 미정입니다.

[MobaXterm] EC2 접속

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   

프로젝트 Jar 올리기

먼저, 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를 찾습니다.

  1. home/ubuntu/ 경로에서 다음의 명령어를 사용합니다.
nano .bashrc

nano .bashrc 를 입력하면 위와 같은 화면이 나오는데 제일 밑으로 이동합니다.

  1. 'export' 명령어를 사용하여 환경 변수를 설정합니다.
export MYSQL_SECRET=123456789
  1. 변경 사항을 저장합니다. ( Ctrl+x ,누른 뒤 Y 를 누르고 Enter를 누른다)

  2. '.bashrc' 파일에서 설정한 환경 변수를 사용할 수 있도록 변경 사항을 적용하기 위해 다음 명령어를 입력합니다.

source .bashrc
  1. 마지막으로 환경변수가 제대로 저장됐는지 확인해보면 끝입니다.!
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 &
  • nohup : 유저가 콘솔에 접속 중이지 않더라도 서버 실행 유지
  • java -jar : 자바 jar파일을 실행시킨다. (내부 톰캣용)
  • 파일명 : m 만 치고 탭을 누르면 자동완성 된다.
  • 1>log.out : 1은 표준출력을 뜻한다. 문제가 없을 때는 log.out 파일에 로그를 기록
  • 2>err.out : 2는 에러출력을 뜻한다. 문제가 있을 때는 err.out에 출력
  • & : 백그라운드 실행 (유저가 로그아웃하면 꺼진다. nohup과 같이 사용하면 실행 유지 가능)

아래 코드로 잘 작동되는지 확인해보자.

ps -ef | grep m*.jar

이제 제대로 나오는지 확인을 해보려면

http://아이피주소:8080/URL

http://13.125.169.154:8080/

이런식으로 치면 됩니다.

윈도우에서 MobaXTerm없이 접속

PuTTyGen이 아니라 PuTTy로 접속해서

Connection → SSH → Auth → Credentials
위 AWS 키 페어로 생성한 .ppk 키 파일을 추가하고 open 클릭

EC2에서 소셜 로그인하기

소셜 로그인의 경우 clientIdclientSecret이 필수입니다. 로컬 PC에서는 설정파일이 존재하므로 문제가 없지만 배포할 경우 이 파일은 .gitignore로 git에서 제외 대상이라 깃허브에 올라가지 않습니다.

애플리케이션을 실행하기 위해 공개된 저장소에 clientIdclientSecret 올릴 수 없으니 서버에서 직접 이 설정들을 가지고 있게 하겠습니다.

구글 API에 EC2 주소 등록

구글 웹 콘솔에 접속하여 EC2 주소를 등록해보자.

  1. 구글 웹 콘솔에서 본인 프로젝트로 이동한다.
  2. [API 및 서비스] → [사용자 인증 정보]로 이동한다.

  1. [OAuth 동의 화면] → [앱 수정]으로 이동한다.

  2. [승인된 도메인]에서 EC2의 퍼블릭 DNS를 ‘http://’를 제외하고 추가한다.

  3. [사용자 인증 정보] → [승인된 리디렉션 URI] → ‘DNS:8080/login/oauth2/code/google’ 주소를 등록한다.

  4. EC2 DNS 주소로 이동해서 다시 구글 로그인을 시도하여 로그인이 정상적으로 수행되는지 확인한다.

네이버 API에 EC2 주소 등록

아래 네이버 개발자 센터로 접속해서 본인의 프로젝트로 이동한다.

https://developers.naver.com/apps/#/myapps

  1. 본인 프로젝트 → API 설정

  2. PC웹 항목 → [서비스 URL]과 [Callback URL]을 수정한다.

주의사항
서비스 URL

  • 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목이다.
  • 8080포트는 제외하고 실제 도메인 주소만 입력한다.
  • 네이버에서 아직 지원되지 않아 하나만 등록 가능하다.
    즉, EC2 주소를 등록하면 localhost가 안된다.
  • 개발 단계에서는 등록하지 않는 것을 추천한다.
  • localhost도 테스트하고 싶으면 네이버 서비스를 하나 더 생성해서 키를 발급받으면 된다.

Callback URL

  • 전체 주소를 등록합니다.

    EC2 퍼블릭 DNS:8080/login/oauth2/code/naver

  1. EC2 DNS 주소로 이동해서 네이버 로그인을 시도하여 로그인이 정상적으로 수행되는지 확인한다.

서브넷 생성

연동할 EC2 정보 파악

사용해야할 ec2, 보안그룹, 서브넷그룹, rds를 모두 같은 네트워크 안에서 사용되야 하므로 ec2에서 사용하고 있는 vpc와 서브넷ID를 파악합니다.

보안그룹 생성

RDS DB에서 사용할 DB 보안 그룹을 생성하겠습니다.

[EC2]에서 왼쪽 메뉴바의 [네트워크 및 보안]의 [보안그룹]을 선택해서 보안 그룹 생성을 눌러주세요.

그런 다음 위와같이 이름을 지정하고 vpc는 EC2에서 사용하고 있는 vpc로 선택해주세요.

그런 인바운드 규칙에서 MYSQL을 사용할 것이니 MYSQL 선택하고 포트는 3306을 선택하고 소스는 EC2에서 사용하고 있는 보안그룹 이름과 같은걸 선택해주세요.

EC2 인스턴스에서 보안그룹을 보면 3개가 있는데

이거를 선택해줘야 한다.

여기까지 진행되면 EC2가 생성되고 EC2 보안그룹이 생성이 됐고 RDS 보안그룹에는 EC2 보안그룹이 인바운드 규칙에 추가가 된것이다.

DB 서브넷 그룹 생성

[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로 연결해준 것이다.

RDS 생성

데이터베이스 생성

데이터베이스 생성 방식 선택

표준 생성 을 선택하고 엔진 옵션을 MySQL , 버전은 8.0.27을 선택, 템플릿은 프리티어를 선택한다.(AWS 회원가입시 프리티어를 1년간 사용 할 수 있다고 한다. )

DB인스턴스 식별자, 마스터 사용자 이름, 비밀번호를 입력한다. ( 본인이 기억할 수 있는 것 선택하기 )

DB 인스턴스 식별자
AWS 리전에서 AWS 계정이 소유하는 모든 DB 인스턴스에 대해 고유한 식별자를 작성합니다.

스토리지 항목
기본으로 입력된 스토리지 사이즈 및 옵션의 확인이 가능하다.

연결

연결 안함도 해보고 연결도 해보고 그랬지만 연결 안하고 나중에 연결하려고 하니 과금이 나온다는 글을 보고 연결을 한 상태로 진행했다.

여기서 EC2 컴퓨팅 리소스 연결로 클릭하면 기존에 있는 EC2 인스턴스가 나온다. 그것을 선택하면 자동으로 연결해준다.

서브넷은 위에서 생성한 ec2 vpc로 연결한 서브넷을 넣어준다. 여기서 보면 퍼블릭 액세스가 아니요만 되어 있는데 이것은 수정으로 로 할 수 있다.

여기서는 찾아본게 너무 각자가 달라서 잘 모르겠어서 3개 다했다. 1번째는 RDS 서브넷이고 2번째는 EC2만들 때 생기던 거고 3번째는 EC2 보안그룹이다.

데이터베이스 인증

추가 구성

AWS RDS MySQL 보안그룹 세팅

데이터베이스를 보면 연결 및 보안에서

이렇게 연결되어 있는 것을 볼 수 있다. RDS에 연결된 보안그룹들이다.
이거를 클릭하면 먼저 shopping을 클릭해보자!

그리고 인바운드 규칙 편집하기를 눌러서 편집을 해야한다.

mysql을 사용할거니 mysql을 해주고 sg는 EC2 보안그룹을 해주면 된다. 현재 launch-wizard-1이다.

이제 launch-wizard-1로 가보자!

여기서 RDS 보안그룹을 넣으면 된다. 첫번째 가린거는 ip주소고 두 번째가 RDS 보안그룹이다.

여기 sg는 데이터베이스의 보안그룹이다.

정리해보자면,

  1. EC2 생성

  2. EC2 보안그룹 생성

  3. RDS 보안그룹 생성

    RDS 보안그룹에 EC2 VPC & EC2 보안그룹을 연결

  4. RDS 서브넷 생성

    RDS 보안그룹에 EC2 VPC 연결

  5. RDS 파라미터 생성

    여기서는 한국시간과 한글을 넣을 수 있게 처리하는 곳

EC2 RDS연결

우분투에 mysql 다운

sudo apt install mysql-server

비밀번호 치는게 나오는데 이 때 비밀번호를 치는게 안나온다.
그냥 쳐서 맞으면

이렇게 나온다.

MySQLWorkbench에 RDS 연결

  1. 연결

RDS에 연결하기 위해 채워야하는 칸들입니다.

  • Connection Name은 식별 가능한 이름을 아무거나 입력합니다.
  • Hostname은 RDS의 엔드포인트를 입력합니다.
  • Username은 RDS를 생성할 때 유저 이름으로 설정했던 것을 입력합니다. (default : admin)
  • Password는 RDS를 생성할 때 비밀번호로 설정했던 것을 입력합니다.
  • Default Schema는 비워놔도 무방합니다.

여기서 이런 오류가 발생하면 먼저 EC2에서 연결된 RDS인바운드 규칙을 수정하고 RDS에서 인바운드 규칙을 추가해준다.

그리고 RDS의 퍼블릭 액세스 여부를 허용하고, 보안 그룹에서 3306 포트까지 열어줬지만 여전히 외부 연결이 안되는 사람들은 대부분 RDS가 사용하는 서브넷이 Public이 아닌 Private이라서 문제가 생기는 것이다. 그래서 서브넷을 Public으로 변경하는 방법에 대해 알려드리고자 한다.

  1. 테이블 생성

CI/CD란?

CI/CD는 애플리케이션 개발 단계로부터 배포 때까지의 모든 단계를 자동화를 통해서 좀더 효율적이고 빠르게 사용자에게 빈번히 배포할 수 있는것을 말한다.

CI

테스트, 빌드, Dockerizing, 저장소에 전달하는 것까지 프로덕션 환경으로 서비스를 배포할 수 있도록 준비하는 프로세스, 지속적인 통합

CI의 방법

1. 코드 변경사항을 주기적으로 빈번하게 merge

오랜 기간 동안 개발을 진행하다가 한번에 merge를 한다면 많은 충돌이 일어날 것이다. 그렇기에, 가능한 작은 단위로 나누어서 주기적으로 빈번히 개발하고 계속해서 통합하여 나가는 것이 중요하다.

흐름을 보자면,

2. 통합 단계의 자동화

1번의 방법은 너무나 귀찮은 단점이 있다. 여기서 자동화를 사용한다면 귀찮은 작업을 줄일 수 있다.

자동화를 사용한 이후의 흐름

CI의 장점

  • 코드의 검증에 들어가는 시간이 줄어든다.
  • 개발 편의성이 증가한다.
  • 항상 테스트 코드를 통과한 코드만이 레포지터리에 올라가기 때문에, 좋은 코드 퀄리티를 유지할 수 있다.

CD

저장소로 전달된 프로덕션 서비스를 실제 사용자들에게 배포하는 프로세스, 지속적인 배포 deploy(배포)

CD를 적용한 후의 흐름

CD의 장점

  • 개발자는 배포보다는 개발에 더욱 신경 쓸 수 있도록 도와준다.
  • 개발자가 원클릭으로 수작업 없이 빌드, 테스트, 배포까지의 자동화를 할 수 있다.

만약 CI/CD를 하지 않는다면 위와 같은 작업을 매번 기능을 수정될 때마다 수작업을 적용시켜야하나 CI/CD를 적용하면 간단한 명령어 만으로 위와 같은 작업을 자동으로 수행

대표적인 것 : github action, jenkins 등이 존재
이러한 CI/CD의 논리는 DevOps 방식의 논리를 극한으로 끌어올리는 경우

Github Actions

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 구성

Workflows

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이벤트가 발생할 때 마다 워크플로우를 실행하려면 다음과같이 설정

다른 예시는 매일 자정에 워크플로우를 실행하려면 다음과 같이 설정합니다.

Jobs

2) jobs속성을 통해서 해당 워크플로우가 구체적으로 어떤 일을 해야는지 명시해야 한다.

Github Actions에서 작업(Job)이란 독립적인 가상 머신(machine) 또는 컨테이너(container)에서 돌아가는 하나의 처리 단위를 의미합니다. 하나의 워크플로우는 여러 개의 작업으로 구성되며 적어도 하나의 작업은 있어야 합니다. 그리고 모든 작업은 기본적으로 동시에 실행되며 필요시 작업 간에 의존 관계를 설정하여 작업이 실행되는 순서를 제어할 수 있습니다.

작업은 워크플로우 YAML 파일 내에서 jobs 속성을 사용하며 작업 식별자(ID)와 작업 세부 내용 간의 맵핑(mapping) 형태로 명시가 된다.

예를 들어, job1, job2, job3이라는 작업 ID를 가진 3개의 작업을 추가하려면 다음과 같이 설정한다.

작업의 세부 내용으로는 여러가지 내용을 명시할 수 있는데 필수로 들어가야 하는 runs-on 속성을 통해 해당 리눅스나 윈도우즈와 같은 실행 환경을 지정해줘야 합니다.

예를 들어, 가장 널리 사용되는 우분투의 최신 실행 환경에서 해당 작업을 실행하고 싶다면 다음과 같이 설정한다.

작업에서 가장 중요한 부분은 작업 순서를 정의하는 것일텐데요. 이 부분은 steps 속성을 통해서 설정을 하며 다음 섹션에서 자세히 알아보겠습니다.

Steps

보통 작업은 일반적으로 여러 단계의 명령을 순차적으로 실행하는 경우가 많은데 Github Actions에서는 각 작업(job)이 하나 이상의 단계(step)로 모데링 된다.

작업 단계는 단순한 커맨드(command)나 스크립트(script)가 될 수도 있고 다음 섹션에서 자세히 설명할 액션(action)이라고하는 복잡한 명령일 수도 있습니다. 커맨드나 스크립트를 실행할 때는 run 속성을 사용하며, 액션을 사용할 때는 uses속성을 사용합니다.

예를 들어, 자바스크립트 프로젝트에서 테스트를 돌리려면 코드 저장소에 코드를 작업 실행 환경으로 내려 받고, 패키지를 설치한 후, 테스트 스크립트를 실행해야 한다. 이 3단계의 작업은 아래와 같이 steps속성을 통해서 명시될 수 있다.

워크플로우 파일 내에서 작업 단계를 명시해줄 때는 주의할 부분이 있다. YAML 문법에서 시퀀스(sequence) 타입을 사용하기 때문에 각 단계 앞에 반드시 -를 붙여야 한다.

.Actions

마지막으로 살펴볼 개념은 Github Actions의 꽃이라고 할 수 있는 액션(action)이다. 액션은 Github Action에서 빈번하게 필요한 반복 단계를 재사용하기 용이하도록 제공되는 일종의 작업 공유 메커니즘이다. 이 액션은 하나의 코드 저장소 범위 내에서 여러 워크플로우 간에서 공유를 할 수 있을 뿐만 아니라, 공개 코드 저장소를 통해 액션을 공유하면 Github 상의 모든 코드 저장소에서 사용이 가능해집니다.

Github에서 제공하는 대표적인 공개 액션으로 바로 위 예제에서도 사용했던 체크 아웃 액션 (actions/checkout)을 들 수 있는데 대부분의 CI/CD 작업은 코드 저장소로 부터 코드를 작업 실행 환경으로 내려받는 것으로 시작하므로 이 액션은 범용적으로 사용한다.

뿐만 GitHub Marketplace에서는 수많은 벤더(vendor)가 공개해놓은 다양한 액션을 쉽게 접할 수가 있습니다. 한 마디로 이 액션을 중심으로 하나의 큰 커뮤니티가 형성이 되고 더 많은 사용자와 벤더가 GitHub Actions으로 몰려드는 선순환이 일어나고 있습니다.

Runners

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입니다.

Event

워크 플로우를 실행하는 특정 활동이나 규칙이다. 커밋의 push, pull request가 생성되었을 때뿐만 아니라 저장소 dispatch event를 통해 Github 외부에서 발생하는 활동으로도 이벤트를 발생시킬 수도 있다.

	# push나 pull request가 발생할 때 워크 플로우 실행
	on: [push, pull_request]

또한 schedule에 POSIX cron 문법으로 스케쥴 이벤트를 발생시킬 수도 있다.

on:
  schedule:
    - cron: '*/15 * * * *'

워크 플로우 관리

  • 민감한 정보 저장
    워크 플로우가 비밀번호나 인증서 같은 민감한 정보를 사용한다면 Github에 secret으로 저장하여 환경 변수로 사용 가능하다.
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 작업이 끝난 후에 실행되도록 설정했다.

  • 빌드 매트릭스 활용하기
    워크 플로우가 다양한 OS, 플랫폼, 언어의 여러 조합에서 테스트를 실행하려는 경우 빌드 매트릭스를 활용하면 된다. 빌드 옵션을 배열로 받는 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 파일 내 컴포넌트 이해하기

  • yml 파일 예시
    Master 브랜치에 Push 또는 Pull Request가 올 경우 실행되는 CI란 이름을 갖는 Workflow

master 브랜치에 Push 또는 Pull Request가 올 경우 실행되는 CI란 이름을 갖는 Workflow 파일입니다.

위와 같이 저장을 한다면 아래 예시처럼 시각화된 workflow를 볼 수 있습니다.

  • name : Workflow의 이름을 지정
  • on
	  on:
   		 push:
      		branches: [ master ]
    	pull_request:
      		branches: [ master ]
  • Event에 대해 작성하는 부분
  • 어떤 조건에 Workflow를 Trigger 시킬지
  • push(Branch or Tag), pull_request, schedule을 사용할 수 있음
    단, 다른 CI/CD 도구에 있는 즉시 실행 버튼은 없음
  • 단일 Event를 사용할 수도 있고, array로 작성할 수도 있음
    			  on: push
    			  # 또는
     			  on: [pull_request, issues]
         
  • jobs
	  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.
  • Workflow는 다양한 Job으로 구성됨
  • 여러 Job이 있을 경우, Default로 병렬 실행
  • build라는 job을 생성하고, 그 아래에 2개의 step이 존재하는 구조
  • runs-on은 어떤 OS에서 실행될지 지정
  • strategy - matrix 인자를 사용하면 어떤 파이썬 버전에서 테스트할지 확인할 수 있음.
  • steps의 uses는 어떤 액션을 사용할지 지정함. 이미 만들어진 액션을 사용할 때 지정

workflow 관리

민감한 정보 사용해야된다면 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에 있기때문에 워크플로에 적지 않아도 된다.

git

위에서 jar로 빌드를 해서 직접 넣어주는 작업을 했지만 Git action을 사용하기 위해서 우분투에 연결하기 위해서 해당 작업을 진행했습니다.

sudo apt-get update && upgrade // apt-get 업데이트 && 업그레이드
sudo apt-get install git // git 설치

EC2는 우분투라서, apt-get을 이용했다
apt-get을 업그레이드한 후, git을 설치했다.

npm 설치

sudo apt-get install npm

git clone

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를 사용해 깃허브 워크플로 전체적으로 사용할 수 있는 환경 변수를 설정할 수 있습니다.

Beanstalk Deploy

AWS EB란 서비스를 배포하고 확장, 관리하는데 있어 쉽고 빠르게 할 수 있도록 돕는 완전 관리형 서비스이다. Auto Scaling, 용량 Provisioning, 로드 밸런싱, 모니터링, 다중 인스턴스 배포, 로그 관리 등 많은 기능들을 지원한다. BeanStalk은 Docker 컨테이너를 기반으로 애플리케이션을 쉽게 배포하고, 운영하고, 관리하는걸 도와주는 AWS 서비스다. 별도의 서비스 사용료는 없고, 사용한 AWS 리소스 만큼 비용이 과금된다고 한다.

빈스토크 배포를 진행합니다. 여기에서 지정한 secrets.AWS_ACCESS_KEY와 secrets.AWS_SECRET_KEYsms git action에서 가져오는 비밀 값입니다. 이 값은 AWS에서 만든 뒤 깃허브에서 설정해야 합니다. 또한 application_nameenvironment_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"

Elastic Beanstalk 환경 생성하기

단일 인스턴스 선택시 로드밸런서를 사용할 수 없기에 beanstalk으로 무중단 배포가 불가능하다.

따라서 사용자 지정 구성으로 선택해준다!

Auto Scaling에서의 그룹 최대 인스턴스를 1개로 설정해줬는데, 이는 트래픽이 증가하더라도 최대 1개의 인스턴스 외에는 Auto Scaling을 적용하지 않겠다는 뜻이다. 현재 내 환경은 실제 사용자가 사용하는 환경이 아니기에 트래픽이 증가하지 않기 때문에 1개로 설정해준다.

배포 방식으로는 추가 배치를 사용한 롤링을 선택해준다.

이는 현재 인스턴스는 1개이며, 신규 인스턴스 배포시 신규 버전을 올리고 문제가 없다면 기존 인스턴스를 죽이는 방식이다. (서비스 중단없이 배포 가능한 방법)

AWS IAM 계정 생성

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 내의 ${} 변수에 접근하고 실제 시크릿 값을 가져와 설정에 적용합니다. 만약 이름이 일치하지 않으면 워크플로우는 해당 시크릿을 찾을 수 없을 것이므로 설정에 제대로 적용되지 않을 것입니다.

profile
발전하기 위한 공부

0개의 댓글