프로젝트가 거의 완성되가고 있어서 AWS EC2에 어플리케이션을 배포하게 되었는데 배포할때 마다 서비스가 계속 중단되는 것을 방지하기 위해 NginX 웹서버에 있는 로드밸런서 기능을 이용해서 무중단 배포를 구현해보려고합니다. EC2에 어플리케이션 배포부터 하나씩 정리해도록 하겠습니다.
보통 어플리케이션을 배포 하기위해 EC2를 많이들 사용하는데요 EC2는 AWS에서 제공하는 클라우드 컴퓨팅 서비스 중 하나로 Elastic Compute Cloud의 약어입니다. 이 서비스를 사용하면 클라우드에서 가상 컴퓨터를 호스팅하고 실행할 수 있기 때문에 어플리케이션 또한 실행할 수 있기 때문에 많이 사용합니다.
EC2 인스턴스를 이용하기 위해 ssh 연결을 해보도록 하겠습니다.
~/.ssh 폴더로 이동 하고 아까 생성했던 key pair 파일을 해당 폴더로 이동 시켜줍니다.
$ cd ~/.ssh
$ cp (키파일 경로) ./
그 다음 .pem 파일에 읽기 권한을 설정합니다
$ chmod 400 (키파일 경로)
그 다음 접속하기 위한 설정을 해주어야 하는데요 ssh user@ip -i pem경로 이런식으로 접속해야하는데 자주 접속할 것 이기 떄문에 좀 더 간단히 접속할 수 있도록 Config 파일을 생성하고 설정한 다음 간단하게 접속할 수 있도록 설정 해보겠습니다.
Host 이름
HostName IP주소
User ubuntu(우분투인경우 ubuntu, linux 인경우 ec2-user로 설정)
IdentityFile (키파일 경로)
이제는 ssh 명령어를 통해 Host 옆에 작성했던 이름을 통해 접속하도록 하겠습니다.
$ ssh 이름
EC2서버에 접속
프리 티어 환경에서의 EC2는 어플리케이션을 구동하기에 너무 적은 메모리를 가지고 있습니다. 이때 Swap이라는 파일을 통해 메모리를 더 늘려줄 수 있는 방법이 있습니다. Swap 파일은 컴퓨터의 실제 메모리, 즉 램의 가상 메모리 확장으로 사용되는 하드디스크 상의 한 공간입니다. 그렇기 때문에 더 많은 양의 램을 가지고 있는 것처럼 동작할 수 있습니다. 그래서 먼저 Swap 파일을 설정해주도록 하겠습니다.
Swap 파일 설정
ec2에 접속합니다
dd 명령아를 사용앟여 루트 파일 시스템에 Swap 파일을 생성합니다. 명령에서 bs는 블록 크기이고 count는 블록 수입니다. Swap 파일의 크기는 dd 명령의 블록 크기 옵션에 블록 수 옵션을 곱한 값입니다. Swap 파일에 메모리 할당 2GB(128M*16)로 할당해주도록 합시다.
$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16
Swap 파일에 Read Write 권한을 설정해줍니다.
sudo chmod 600 /swapfile
Swap 영역을 설정해줍니다.
$ sudo mkswap /swapfile
Swap 공간에 Swap 파일을 추가하여 즉시 사용할 수 있도록 합니다.
$ sudo swapon /swapfile
프로시저가 성공적인지 확인합니다.
$ sudo swapon -s
/etc/fstab 파일을 편집하여 부팅 시 Swap 파일을 시작합니다.
$ sudo nano /etc/fstab
//파일 끝에 다음 줄 추가
$ /swapfile swap swap defaults 0 0
먼저 Jar파일을 통해 배포를 해야하기 때문에 프로젝트를 Repository에서 가져와서 빌드한 후 Jar로 배포하기 위한 과정을 먼저 진행하도록 하겠습니다.
먼저 Repository에서 프로젝트를 가져오기 위해 Git을 통해 Repository를 clone 해주겠습니다.
$ git clone [repository]
그 다음 빌드하기 위해 자바를 설치해줘야하는데요 자기 프로젝트에 맞는 자바버전을 설치하도록 해줍시다. 저는 17버전을 사용중이기 때문에 17버전을 설치하겠습니다.
$ sudo apt update
$ sudo apt install openjdk-17-jdk
자바 설치가 완료되었다면 프로젝트 폴더에 들어가서 빌드를 해서 Jar 파일을 생성하도록 하겠습니다.
$ cd repository
$ ./gradlew build
도커로 어플리케이션을 실행할 것이기 때문에 도커엔진하고 도커 컴포즈를 먼저 설치하겠습니다. 아래는 설치 명령어 입니다.
// 도커 엔진 설치
$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
// 도커 컴포즈 설치
$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
FROM amazoncorretto:17
ARG JAR_PATH=./build/libs
VOLUME ["/log"]
COPY ${JAR_PATH}/MindTravel-0.0.1-SNAPSHOT.jar app.jar
CMD ["java","-jar","app.jar"]
version: '3.9'
services:
nginx:
image: nginx
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
mysql:
image: mysql:latest
restart: always
environment:
MYSQL_DATABASE: mind_travel
MYSQL_ROOT_PASSWORD: 1234
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
redis:
image: redis:alpine
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
blue:
build: .
restart: always
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/mind_travel
SERVER_PORT: 8080
ports:
- "8080:8080"
depends_on:
- mysql
- redis
green:
build: .
restart: always
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/mind_travel
SERVER_PORT: 8081
ports:
- "8081:8081"
depends_on:
- mysql
- redis
volumes:
db_data:
redis_data:
Blue Green 배포를 통해 배포를 할 것이기 때문에 blue, green 두개의 서비스를 추가하고 저같은 경우는 redis와 mysql, Spring Boot를 사용하기 때문에 위와같이 설정해 주었습니다. Green Blue 배포를 위한 nginx를 제외하고 이건 프로젝트마다 다르게 설정되기 떄문에 자신의 프로젝트에 맞게 설정해주도록 합시다.
💡 처음에 도커 컴포즈를 실행할 때 실제 호스트의 ./nginx.conf 파일과 /etc/nginx/nginx.conf 도커 컨테이너 파일을 볼륨 마운트 설정을 해주었는데 제 로컬 PC에서는 정상적으로 적용이 되었었는데 EC2 Ubuntu 환경에서는 반영이 안되는 문제가 있었습니다. 이러한 문제를 해결하기 위해 파일 권한도 수정해보고 했는데 결과적으로 User 설정을 해주어서 해결하게 되었습니다. EC2는 보안적인 문제로 Root 계정으로 접속되는게 아니기 때문에 User 설정을 통해 권한을 주어야 도커 컨테이너에서 호스트 파일에 접근이 가능해서 발생한 문제였습니다.#blue_nginx.conf
events{}
http{
server {
listen 80;
server_name localhost;
location /v1 {
proxy_pass http://blue:8080;
}
}
}
#green_nginx.conf
events{}
http{
server {
listen 80;
server_name localhost;
location /v1 {
proxy_pass http://green:8081;
}
}
}
나머지 설정은 서비스에 맞게 설정해주시면 됩니다.
APPLICATION=어플리케이션 이름
echo $APPLICATION
#현재 어플리케이션이 실행중인지 IS_RUNNING 환경변수에 추가
IS_RUNNING=$(sudo docker compose ls | grep $APPLICATION | grep running | sed 's/.*/true/')
#만약 실행중이면 현재 실행중인 어플리케이션을 확인후
#Blue가 실행중이면 Green을 실행시키고 Nginx가 Green에게 라우팅 하도록 설정후 Blue Down
#Green이 실행중이면 Blue을 실행시키고 Nginx가 Blue에게 라우팅 하도록 설정후 Green Down
if [ "$IS_RUNNING" = "true" ]
then
echo $APPLICATION 실행중
#Blue가 현재 실행중인지 체크
IS_BLUE_RUNNING=$(sudo docker-compose -p $APPLICATION ps | grep blue | grep Up | sed 's/.*/true/')
echo $CUR_APPLICATION
#만약 Blue가 실행중이라면
if [ "$IS_BLUE_RUNNING" = "true" ]
then
CUR_APPLICATION="blue"
DEPLOY_APPLICATION="green"
#nginx.conf를 green_nginx.conf 변경
sudo cp green_nginx.conf nginx.conf
#Green이 실행중이라면
else
CUR_APPLICATION="green"
DEPLOY_APPLICATION="blue"
#nginx.conf를 blue_nginx.conf 변경
sudo cp blue_nginx.conf nginx.conf
fi
echo 사용하지 않는 이미지 정리
sudo docker rmi $(sudo docker images -f "dangling=true" -q)
echo 배포시작 : $DEPLOY_APPLICATION
# 해당 어플리케이션 이미지 캐시가 적용되기 때문에 새로 반영한 것을 적용하기 위해서 별도로 빌드
sudo docker-compose build --no-cache $DEPLOY_APPLICATION
# 배포할 어플리케이션만 실행하도록 설정
sudo docker-compose -p $APPLICATION up -d --no-deps $DEPLOY_APPLICATION$DEPLOY_APPLICATION
# 어플리케이션이 정상적으로 실행될때까지 기다리기 위해 Sleep 설정
#이건 직접 어플리케이션 요청을 계속 보내면서 확인하는게 가장 좋은 방법 같습니다.
sleep 30
# 배포된 어플리케이션의 상태를 확인
DEPLOY_STATUS=$(sudo docker-compose -p $APPLICATION ps $DEPLOY_APPLICATION | grep "Up" | sed 's/.*/true/')
echo $(sudo docker-compose -p $APPLICATION ps $DEPLOY_APPLICATION)
if [ "$DEPLOY_STATUS" != "true" ]
then
echo 배포실패
else
echo 배포 성공
#Nginx의 설정정보를 Reload해줍니다.
sudo docker-compose -p $APPLICATION exec nginx nginx -s reload
#이전에 실행중이던 어플리케이션을 종료해줍니다.
sudo docker-compose -p $APPLICATION stop $CUR_APPLICATION
break
fi
else
echo $APPLICATION 실행중이지 않음
# blue green 새로운 이미지로 빌드
sudo docker-compose build --no-cache green blue
# docker-compose 실행
sudo docker-compose -p $APPLICATION up -d
# 초기 설정으로 blue_nginx.conf 설정
sudo cp blue_nginx.conf nginx.conf
# 어플리케이션이 정상적으로 실행될때까지 기다리기 위해 Sleep 설정
#이건 직접 어플리케이션 요청을 계속 보내면서 확인하는게 가장 좋은 방법 같습니다.
sleep 30
#Nginx의 설정정보를 Reload해줍니다.
sudo docker-compose -p $APPLICATION exec nginx nginx -s reload
#Green 어플리케이션을 종료해줍니다.
sudo docker-compose -p $APPLICATION stop green
echo $APPLICATION 실행
fi