어디까지 갈 수 있을까?
EC2 서버에 프로젝트를 배포해보자 &코드가 푸시되면 자동으로 배포해 보자 본문
EC2에 프로젝트 Clone 받기
EC2에 접속해서 깃 설치 sudo yum install git git --version 프로젝트를 저장할 디렉터리 생성 mkdir ~/app && mkdir ~/app/step1 cd ~/app/step1 git clone 깃허브 주소 cd 프로젝트명 ll 프로젝트 코드들이 모두 있으면 성공 |
코드들이 잘 수행되는지 테스트로 검증 ./gradlew test gradlew 실행 권한이 없다고 뜨면 chmod +x ./gradlew |
배포 스크립트 만들기
배포 : 작성한 코드를 실제 서버에 반영하는 것
vim ~/app/step1/deploy.sh
#!/bin/bash
REPOSITORY=/home/ec2-user/app/step1
PROJECT_NAME=spring_web_service
cd $REPOSITORY/$PROJECT_NAME
echo "> Git pull"
git pull
echo "> 프로젝트 Build 시작"
./gradlew build
echo "> step1 디렉토리로 이동"
cd $REPOSITORY
echo "> Build 파일복사"
cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)
echo " 현재 구동중인 애플리케이션pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
echo "> 현재구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1)
echo "> JAR Name: $JAR_NAME"
nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
변수 지정
자주 사용하는 값 변수 지정
REPOSITORY=/home/ec2-user/app/step1
PROJECT_NAME=spring_web_service
쉘에서는 $ 변수명으로 변수를 사용할 수 있습니다.
git pull
master 브랜치의 최신 내용을 받습니다.
./gradlew build
프로젝트 내부의 gradlew로 build를 수행합니다.
CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar)
기존에 수행 중이던 스프링 부트 process id 추출 명령어
-f 옵션은 프로세스 이름으로 찻습니다.
JAR_NAME=$(ls -tr $REPOSITORY/ | grep *.jar | tail -n 1)
새로 실행할 jar 파일명을 찾습니다.
여러 jar 파일이 생기기 때문에 tail -n로 가장 나중의 jar파일을 변수에 저장합니다.
nohup java -jar JAR_NAME 2>&1 &
찾은 jar 파일명으로 해당 jar파일을 nohup으로 실행합니다.
내장 톰캣을 사용해서 jar 파일만 있으면 바로 웹 애플리케이션 서버를 실행할 수 있습니다.
nohup으로 실행을 시키려면 실행파일 권한이 755이상으로 되어있어야 함
명령어 뒤에 '&'를 추가하면 백그라운드로 실행됨
nohup 을 통해 프로그램을 실행시키면 nohup.log 라는 로그 파일 생성
|
cannot allocate memory 오류 : could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarExcept 오류 : RDS의 데이터 베이스 이름 or 테이블 이름 or 컬럼 이름이 Entity의 이름과 다르진 않은지 확인 |
외부 Security 파일 등록
vim /home/ec2-user/app/application-oauth.properties 로컬에 있는 application-oauth.properties 파일 내용 그대로 붙여넣기 deploy.sh 파일 수정 nohup java -jar \ -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties |
-Dspring.config.location
스프링 설정 파일 위치를 지정합니다.
classpath가 붙으면 jar 안에 있는 resources 디렉트리 기준으로 경로가 생성됩니다.
application-oauth.properties은 절대경로를 사용합니다.
스프링 부트 프로젝트로 RDS 접근하기
create table posts (id bigint not null auto_increment, created_date datetime, modified_date datetime, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id)) engine=InnoDB;
create table user (id bigint not null auto_increment, created_date datetime, modified_date datetime, email varchar(255) not null, name varchar(255) not null, picture varchar(255), role varchar(255) not null, primary key (id)) engine=InnoDB;
CREATE TABLE SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
테스트 코드 수행 시 생성되는 로그 쿼리 사용
+
ctrl+shift+n 에서 schema-mysql.sql 의 세션 테이블 생성 쿼리 사용
프로젝트 설정 & EC2 설정
build.gradle compile("org.mariadb.jdbc:mariadb-java-client") src/main/resource/에 application-real.properties 작성 spring.profiles.include=oauth,real-db spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect spring.session.store-type=jdbc |
vim ~/app/application-real-db.properties spring.jpa.hibernate.ddl-auto=none spring.datasource.url=jdbc:mariadb://rds주소:포트명(기본은 3306)/database명 spring.datasource.username=db계정 spring.datasource.password=db계정 비밀번호 spring.datasource.driver-class-name=org.mariadb.jdbc.Driver deploy.sh 수정 nohup java -jar \ -Dspring.config.location=classpath:/application.properties,classpath:/application-real.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \ -Dspring.profiles.active=real \ $JAR_NAME > $REPOSITORY/nohup.out 2>&1 & |
deploy.sh 재실행 ./deploy.sh curl localhost:8080 |
curl: (7) Failed to connect to localhost port 8080: Connection refused 오류 netstat -ln | grep 8080 |
EC2에서 소셜 로그인하기
EC2 보안 그룹 확인 8080포트가 열려 있어야 함 |
|
해당 서비스에 ec2의 도메인을 등록하지 않았기 때문에 구글과 네이버 로그인이 작동하지 않는다 google Login 클릭시 오류가 난다 |
구글에 EC2 주소 등록
console.cloud.google.com/home/dashboard
이름 클릭 |
|
http://없이 퍼블릭 DNS 등록 |
네이버에 EC2 주소 등록
developers.naver.com/apps/#/myapps
|
서비스 URL Callback URL |
현재 문제점
수동 실행 Test, 수동 Build
깃허브에 푸시를 하면 자동으로 Test&Build&Deploy가 진행되도록 개선 필요
코드가 푸시되면 자동으로 배포해 보자 - Travis CI 배포 자동화
CI
Continuous Integration, 지속적인 통합
Git에 코드가 push되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정
CD
Continuous Deployment, 지속적인 배포
이 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정
Travis CI 연동하기 & 프로젝트 설정
Travis CI
깃허브에서 제공하는 무료 CI 서비스
.yml
야믈이라고 부름
JSON에서 괄호를 제거한 것
이념은 기계에서 파싱하기 쉽게 사람이 다루기 쉽게
travis-ci.com/ 접속 책에는 travis-ci.org/ 라고 돼 있지만 페이지를 이전했다 깃허브 로그인 activate 클릭 프로젝트 이름을 클릭하면 빌드 히스토리 페이지로 이동한다 |
build.gralde과 같은 위치에 .travis.yml 생성 language: java jdk: - openjdk8 branches: only: - master # Travis CI 서버의 Home cache: directories: - '$HOME/.m2/repository' - '$HOME/.gradle' before_install: - chmod +x gradlew script: "./gradlew clean build" # CI 실행 완료 시 메일로 알람 notifications: email: recipients: - 본인 메일 주소 branches
|
계속 오류가 나서 2시간 쯤 후에 재시도 했는데 갑자기 됐다..! |
Travis CI와 AWS S3 연동하기
S3란 AWS에서 제공하는 일종의 파일 서버
이미지 파일을 비롯한 정적 파일들을 관리하거나 배포 파일들을 관리하는 기능 지원
CodeDeploy는 저장 기능이 없어 Travis CI가 빌드한 결과물 받아 CodeDeploy가 가져갈 수 있도록 보관하는 공간 : AWS S3
*빌드와 배포가 분리되어 있으면 예전에 빌드한 Jar를 재사용하면 되지만, CodeDeploy만 사용하면 항상 빌드하게 되니 확장성이 떨어짐 => 빌드와 배포 분리
AWS에는 외부 서비스가 접근할 수 없어 접근 가능한 권한을 가진 Key를 생성해서 사용 -> IAM(Identity and Access Management)
|
Travis CI에서 사용될 키 |
Travis CI에 키 등록
이제 등록된 값들을 .travis.yml에서 |
S3 버킷 생성 Simple Storage Service 파일을 저장하고 접근 권한을 관리, 검색 등을 지원하는 파일 서버의 역할 |
.travis.yml 추가
Travis CI에서 빌드하여 만든 Jar 파일을 s3에 올릴 수 있도록 코드 추가
language: java
jdk:
- openjdk8
branches:
only:
- master
# Travis CI 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
before_install:
- chmod +x gradlew
script: "./gradlew clean build"
before_deploy:
- zip -r spring_web_service ./*
- mkdir -p deploy
- mv spring_web_service.zip deploy/spring_web_service.zip
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_KEY
bucket: jmchoi-springboot-build #S3 버킷 이름
region: ap-northeast-2
skip_cleanup: true
acl: private #zip 파일 접근 private으로
local_dir: deploy #before_deploy에서 생성한 디렉토리
wait_until_deployed : true
# CI 실행 완료 시 메일로 알람
notifications:
email:
recipients:
- chlwjdals98@gmail.com
before_deploy
deploy 명령어가 실행되기 전 수행
CodeDeploy는 Jar 파일은 인식하지 못하므로 Jar+ 기타 설정 파일들을 모아 압축(zip)
zip -r springboot-webservice
현재 위치의 모든 파일을 압축
deploy
외부 서비스와 연동될 행위들을 선언
local_dir: deploy
앞서 생성한 deploy 디렉터리를 지정
해당 위치의 파일들만 s3로 전송
Travis CI와 연동을 통해 S3에 자동으로 파일이 올라왔다
Travis CI와 AWS S3, CodeDeploy 연동하기
배포대상인 EC2가 CodeDeploy를 연동 받을 수 있게 IAM 역할을 생성
EC2에 IAM 역할 추가 |
|
역할을 EC2 서비스에 등록 |
CodeDeploy 에이전트 설치
aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
|
/usr/bin/env: ruby: No such file or directory 오류 sudo yum install ruby; 로 ruby 설치하면 해결 |
CodeDeploy를 위한 권한 생성 & CodeDeploy 생성
CodeDeploy에서 EC2에 접근하려면 권한이 필요하다
AWS의 서비스이니 IAM 역할을 생성한다
Code Deploy는 AWS의 배포 서비스이다.
현재 프로젝트에서 코드 저장소의 역할은 GitHub가, 빌드용 서비스는 Travis CI가, 배포는 CodeDepoy가 하고 있다.
Travis CI, S3, CodeDeploy 연동
S3에서 넘겨줄 zip 파일을 저장할 디렉토리 생성 EC2 디렉토리 생성 mkdir ~/app/step2 && mkdir ~/app/step2/zip Travis CI의 Build가 끝나면 S3에 zip 파일이 전송되고, 이 zip파일은 /home/ec2-user/app/step2/zip로 복사되어 압축을 풀 예정 Travis CI의 설정은 travis.yml로 진행했음 AWS CodeDeploy의 설정은 appspec.yml로 진행 version: 0.0 |
version: 0.0 CodeDeploy 버전 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류가 발생 source CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상을 지정합니다. 루트 경로(/)를 지정하면 전체 파일을 이야기 합니다. destination source에서 지정된 파일을 받는 위치 이후 Jar를 실행하는 등은 destination에서 옮긴 파일들로 진행 overwrite 기존에 파일들이 있으면 덮어쓸지를 결정 |
.travis.yml에도 CodeDeploy 내용을 추가
deploy 아래에 추가하면 된다
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY
secret_access_key: $AWS_SECRET_KEY
bucket: jmchoi-springboot-build #S3 버킷 이름
key : spring_web_service.zip #빌드 파일을 압축해서 전달
bundle_type : zip
application : spring_web_service #웹 콘솔에 등록한 CodeDeploy 애플리케이션
deplyment_group : springboot-webservice-group #웹 콘솔에 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed : true
커밋하면 깃허브-> Travis CI -> CodeDeploy로 배포가 수행된다
cd /home/ec2-user/app/step2/zip
ll
파일이 잘 도착해 있다
배포 자동화 구성
Travis CI, S3, CodeDeploy 연동이 완료됐다.
이것을 기반으로 실제 Jar를 배포하여 실행까지 해보자
deploy.sh 파일 추가
#!/bin/bash
REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=spring_web_service
echo "> Build 파일 복사"
cp $REPOSITORY/zip/*.jar $REPOSITORY/
echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -fl $PROJECT_NAME | grep jar | awk '{print $1}')
echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 어플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)
echo "> JAR Name: $JAR_NAME"
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
nohup java -jar \
-Dspring.config.location=classpath:/application.properties,classpath:/application-real.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \
-Dspring.profiles.active=real \
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
.travis.yml 파일 수정
before_deploy:
- mkdir -p before-deploy
- cp scripts/*.sh before-deploy/
- cp appspec.yml before-deploy/
- cp build/libs/*.jar before-deploy/
- cd before-deploy && zip -r before-deploy *
- cd ../ && mkdir -p deploy
- mv before-deploy/before-deploy.zip deploy/spring_web_service.zip
Travis CI는 디렉토리 단위로만 업로드할 수 있기 때문에 디렉토리 생성
appspec.yml 파일 수정
version: 0.0
os : linux
files :
- source : /
destination: /home/ec2-user/app/step2/zip/
overwrite : yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location : deploy.sh
timeout: 60
runas: ec2-user
permissions
CodeDeploy에서 EC2 서버로 넘겨준 파일들을 모두 ec2-user 권한을 갖도록 한다.
hooks
CodeDeploy 배포 단계에서 실행할 명령어를 지정합니다.
ApplicationStart라는 단계에서 deploy.sh를 ec2-user 권한으로 실행하게 합니다.
timeout: 시간제한 60초로 설정(무한정 기다릴 수 없다)
다시 커밋하면
배포 성공!
하지만 이렇게 배포하면 배포하는 동안 스프링 부트 프로젝트는 종료 상태가 되어 서비스를 이용할 수 없다.
다음 장에서는 서비스 중단 없는 배포, 무중단 배포를 진행하자.
'책 > 스프링부트와 AWS로 혼자 구현하는 웹 서비스' 카테고리의 다른 글
NGINX 무중단 배포 (0) | 2021.04.30 |
---|---|
AWS 서버, 데이터베이스 환경을 만들어보자(EC2, RDS) (0) | 2021.04.08 |
스프링 시큐리티와 OAuth 2.0으로 로그인 기능 구현하기 (0) | 2021.03.25 |
[작성중] 프로젝트 구조 (0) | 2021.03.16 |
머스테치로 화면 구성하기 (0) | 2021.03.14 |