위의 포스팅에서 구성한 외부망에 웹 애플리케이션을 배포 하고자 한다.
배포 중에 서비스가 잠깐 멈추는 문제가 있다면 새벽에 배포하면 되고 개발보다 배포에 신경을 쓰게 될 수 있다.
때문에 배포가 탄탄해지면 서비스 개발에 집중 할 수 있어 서비스의 경쟁력이 될 수 있다.
그렇다면, 배포 프로세스를 만들면서 고민할 수 있는 내용이다.
무중단 배포에 관하여
무중단 배포는 서비스의 중단 없이 배포되는 도중에 연결을 해제하지 않고 새로운 버전으로 갈아끼워 지속해서 서비스를 유지하는 것이다. 무중단 배포에는 4가지 방법이 있다. (Rolling Deployment, Blue-Green Deployment, Canary Deployment, L4 Switch)
이때 배포시 도커를 사용하는 의의에 대해서 알아보기 위하여
추가적으로 도커(Docker)와 컨테이너(Container) 에 대한 학습(포스팅)을 진행하였다.
배포는 크게 운영과 개발 환경을 나누어 구성을 진행하게 되었다.
운영 환경 구성하기
개발 환경 구성하기
프록시 서버는 중계 서버 역할로서, 리버스 프록시는 애플리케이션 서버의 앞에 위치한다.
(ex. nginx, apache web server)
Reverse proxy를 구성하게 되면,
Reverse Proxy는 클라이언트로부터의 요청을 받아서 웹 서버로 요청을 전송하고, 웹 서버는 응답을 클라이언트가 아닌 Reverse proxy로 반환하고 Reverse proxy가 그 응답을 클라이언트로 반환한다.
(한마디로 Reverse proxy가 중간 지점으로 거쳐서 반환되는 것이다.)
이를 통해 Reverse proxy
는 애플리케이션 서버를 감추는 역할 로써
WAS는 비지니스 로직만 담당하고 TLS와 같은 부수적인 기능에 영향 받지 않을 수 있다.
SSL(TLS) 인증서란, 전송계층과 응용계층 사이에서 동작하는 브라우저,서버 간에 암호화된 연결을 수립하는데 사용되어 인증되지 않은 사용자로부터 데이터를 보호한다.
TLS 을 설정해야하는 이유는 서버와 클라이언트간 통신상의 암호화를 위해서 이다.
서버의 보안과 별개로 서버와 클라이언트간 통신할 때 평문으로 통신할 경우, 패킷을 스니핑할 수 있기 때문이다.
letsencrypt (Let's Encrypt) 를 활용하여 무료로 TLS 인증서를 사용할 수 있고,
생성한 인증서를 활용하여 Dockerfile, nginx.conf 파일 수정을 통해 Reverse Proxy에 TLS 설정을 해줄 수 있다.
$ docker run -it --rm --name certbot \
-v '/etc/letsencrypt:/etc/letsencrypt' \
-v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
certbot/certbot certonly -d '[나의 도메인]' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
Certbot 에 관하여
Let's Encrypt
는 무료 SSL인증서를 발급해주는 곳이라면,Certbot
는 Let's Encrypt 인증서를 자동으로 발급 및 갱신을 해주는 프로그램이다.웹서버 Nginx는 http 서버로서의 역할을 하고 있기 때문에 HTTP(80 포트)로 들어오는 요청을 HTTPS(443 포트)로 리다이렉션 하는 작업을 해줘야한다. 이때 Nginx에서 Certbot을 통해 SSL을 발급받아 HTTPS 로 적용할 수 있다.
Certbot을 이용해 인증서를 받기위해선 서비스를 운용하는 서버 & 서비스할 도메인 주소 가 필수적으로 필요하다. (때문에 ec2 인스턴스를 생성하였다면 도메인 주소를 할당 받고 DNS 설정을 해야한다.)
실제로 배포를 하려다보면, JUnit을 활용한 test 단계와 local 환경에서 직접 애플리케이션을 확인할 때, 그리고 실제로 배포할 때 등 각 상황에 맞춰 설정을 다르게 적용할 필요성이 생기기 때문에
test, local, prod에서 다른 설정을 사용하도록 설정 파일을 나누었다.
Flyway를 통해 데이터베이스 테이블 스키마의 버전을 관리한다.
운영중인 서비스의 경우 JPA 등 ORM을 사용하여 기존의 테이블을 변경하는 것은 데이터 유실 우려, 참조 무결성 제약 등으로 인해 어려움이 있기 때문이다. 그럴 때 로컬에서 개발 중일 때는 h2 등 in-memory 형태의 데이터베이스를 사용하여 빠르게 개발하고, 운영 DB는 점진적으로 migration 해가는 전략이 유용하다.
다만, 실무에서 운영 환경에 flyway 를 무조건 적용하는 것은 아니다.
실수로 flyway 스크립트가 머지되어 배포되는 경우 , DB 변경 작업 시점이 명확하지 않은 것 과 같은 경우도 있기 때문이다. (실무에서는 운영 DB에 반영하는 작업은 DBA분에게 DDL을 검수받고, 인덱스에 대한 검토도 진행한 후 서비스 영향도를 검토하여 적용 일시를 고려하기도 한다.)
스프링 부트 프로젝트를 빌드를하고 jar파일로 배포를 진행한다.
./gradlew clean build 명령어를 실행하면 Testcase를 진행하고 전체가 성공하면 빌드를 진행한다.
그 후 build/libs 아래에 jar파일이 생성된다.
이후 java -jar [jar파일명] 을 실행하면 서버가 실행된다.
혹은 java -jar -Dspring.profiles.active=prod [jar파일명] 명령어로 입력하면 profile 지정 및 서버를 기동시킬 수 있다. (-Dspring.profiles.active=prod
옵션을 추가하여 실행하면 application-prod.properties의 설정을 사용합니다.)
# 둘 중 택1, 아래는 운영 서버에 최신 설정 파일을 지정 및 서버 기동
service $ java -jar [jar파일명] &
service $ java -jar -Dspring.profiles.active=prod [jar파일 위치]
# 예제
ubuntu@ip/infra-subway-deploy$ java -jar ./build/libs/subway-0.0.1-SNAPSHOT.jar
빌드와 배포의 차이
- 컴파일 : 사용자가 작성한 코드를 컴퓨터가 이해할 수 있는 언어(바이너리 코드/0101) 로 번역하는 일
- 빌드 : 컴파일된 소스 코드를 실행 가능한 소프트웨어 상태,산출물(jar, war)로 만드는 일
(자바의 빌드 도구 - Maven, Gradle)- 배포 : 빌드를 하고 생성된 파일(jar,war)을 사용자가 접근할 수 있는 환경(was)에 올리는 것
(git(소스 형상관리)에 올려두고, 코드가 정상 동작하는지 test 코드를 작성하고, 수행 검증까지 포함)순서대로 보자면
컴파일
-빌드
-배포
의 순서이지만,
보통 컴파일을 포함한 배포하기 직전까지의 모든 과정을 ‘빌드 한다’ 하기도 한다.예시) 코드를 짜고나서 Run 버튼을 눌러 코드를 실행시킨다 (
컴파일 + 실행
)
정상적으로 실행되면 이것을 jar,war 파일로 뽑아서(빌드
) 웹서버에 올린다.(배포
)
하지만 이 상태에서 터미널을 종료하면 실행중인 java도 같이 종료되어 버린다
무중단 서비스를 위해 터미널이 종료되도 서버를 중단 시키고 싶지 않다면,
아까 실행시킬 때 수행했던 명령어 앞에 nohup
을 붙이고, 뒤에 &
을 붙이면 백그라운드에서 실행이 되어, 터미널을 종료해도 EC2상에서는 계속 돌아간다.
(이후, 만약 백그라운드에서 실행중인 프로세스를 종료시키고 싶다면 sudo kill -9 {PID}
를 통해 종료시킬 수 있다.)
service $ nohup java -jar [jar파일명] &
명령어(순서) 정리
# 환경 설정
# 소스코드를 관리할 디렉토리를 생성하고 이동
service $ mkdir nextstep && cd nextstep
# +기타 자바 설치
service $ sudo apt update
service $ sudo apt install default-jre
service $ sudo apt install default-jdk
#git 레파지토리 복사
service $ git clone [git 레파지토리 url]
# 어플리케이션 빌드 및 실행
service $ cd [해당 폴더]
service $ ./gradlew clean build
service $ find ./* -name "*jar"
service $ java -jar [jar파일명] &
AWS에서 프로젝트를 배포하는 과정은 프로젝트가 수정할 때마다 똑같은 일을 반복해야한다.
프로젝트 배포 과정
- git pull로 프로젝트 업데이트
- gradle이나 maven을 통해 프로젝트 테스트와 빌드
- ec2 인스턴스 서버에서 프로젝트 실행 및 배포
위와 같은 과정을 배포할 때마다 매번 (명령어를) 실행시키는 것은 번거롭기 때문에 반복적으로 동작하는 명령어를 배포 스크립트(쉘 스크립트)로 생성한다.
쉘 스크립트 작성
EC2 인스턴스에 deploy.sh 파일을 만들어 자동화할 내용에 관한 쉘 스크립트를 작성한 후,
vim deploy.sh
## 에디터에 스크립트 작성
#!/bin/bash
REPOSITORY= ...
PROJECT_NAME= ...
쉘 스크립트 실행
이후 해당 쉘스크립트 실행을 통해 위의 과정을 한 번에 처리한다.
# 스크립트 실행 권한 추가
chmod +x ./deploy.sh
# 스크립트 실행
./deploy.sh
[참조]
https://github.com/brainbackdoor/playground-docker/tree/master/week1#2-%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%8B%A4%ED%96%89%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-%EB%B0%8F-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EA%B5%AC%EC%84%B1
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html
https://subicura.com/2016/06/07/zero-downtime-docker-deployment.html
https://m.blog.naver.com/shakey7/221600166205
https://lu-coding.tistory.com/88
https://hanhyx.tistory.com/27
https://fistkim101.github.io/infra/2021-04-03-NEXTSTEP-%EC%9D%B8%ED%94%84%EB%9D%BC%EA%B3%B5%EB%B0%A9-%EB%AF%B8%EC%85%982-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0.html
https://wonit.tistory.com/330
https://github.com/next-step/infra-subway-deploy/pull/489/files#r889787557
https://velog.io/@znftm97/%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%99%98%EA%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0#span-stylecolor0b6e995-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EC%8B%9Dspan
-배포
https://itholic.github.io/qa-compile-build-deploy/
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=songintae92&logNo=221330346447
https://suyeoniii.tistory.com/52
10분 테코톡 - 빌드와 배포