이 연재글은 SpringBoot2로 Rest api 만들기의 12번째 글입니다.

이번 장에서는 SpringBoot 프로젝트를 실제 AWS EC2 서버에 배포하고 Nginx와 연동하여 서버를 실행해 보는 실습을 하겠습니다. 이번 실습에서는 AWS에 EC2서버를 ssh로 접속할 준비가 되어있거나 Spring을 배포할 서버가 한대는 준비되어 있어야 합니다. AWS는 신규가입시 1년동안 무료로 서버를 제공하므로 이번 기회에 한번 가입해서 사용해 보는것도 좋습니다.

EC2서버가 준비되었다면 톰켓과 연동할 Nginx를 설치합니다. 설치하는김에 Mysql(mariadb)도 같이 설치 합니다.

SpringBoot는 실행가능한 jar파일을 생성하여 자체적으로 웹 컨테이너를 구동할 수 있습니다. 별다른 설정을 하지 않으면 Embeded Tomcat으로 구동됩니다.

Executable jar 생성

프로젝트 루트 디렉터리에서 bootJar task를 실행하면 ./build/libs 아래에 실행가능한 jar가 생성됩니다.

$ ./gradlew bootJar

Executable jar 서버 업로드

scp 명령을 이용하여 ec2 서버에 jar파일을 전송 합니다. scp(secure copy)는 ssh와 같은 프로토콜을 사용하며 원격지로 안전하게 파일을 복사하는 명령입니다. 리눅스나 Mac에서는 scp를 바로 쓸 수 있지만 윈도에서는 Linux bash shell을 먼저 활성화해야 합니다. 다행히도 윈도10 최신 버전에서 쉽게 Ubuntu Linux를 사용할 수 있도록 제공하고 있으니 아래 링크를 참고하여 Linux shell 환경을 구축합니다.

scp 명령어로 서버에 jar 파일을 업로드 합니다.

$ scp -i 서버인증서.pem 프로젝트경로/build/libs/api-0.0.1-SNAPSHOT.jar ec2-user@서버IP:~/업로드경로

$ scp -i AwsKeyPair.pem /Users/happydaddy/git/SpringRestApi/build/libs/api-0.0.1-SNAPSHOT.jar ec2-user@10.20.30.40:~/api                                                            

Tomcat 실행

업로드한 Jar파일은 Tomcat서버를 내장하고 있습니다. java -jar 명령으로 서버를 띄울수 있습니다. nohup과 마지막의 &를 붙이면 Tomcat이 background process로 실행됩니다. Jar실행시 Port 설정, Profile설정, JVM 옵션 설정 등도 같이 해줍니다.

$ nohup java -jar -Dserver.port=8083 -Dspring.profiles.active=alpha -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -Xss1024k -Xms256m -Xmx384m -Dfile.encoding=UTF-8 api-0.0.1-SNAPSHOT.jar &

Nginx 설정 수정 및 실행

Nginx의 ReverseProxy기능으로 Tomcat의 8083 port를 80으로 서비스 할 수 있도록 세팅하고 nginx를 reload 합니다.

$ sudo vim nginx.conf
// server 설정 위에 upstream 추가
upstream tomcat {
        ip_hash;
        server 127.0.0.1:8083;
}
// location 내용 수정
location / {
        proxy_set_header    HOST $http_host;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto $scheme;
        proxy_set_header    X-NginX-Proxy true;
        proxy_pass http://tomcat;
        proxy_redirect  off;
        charset utf-8;
}
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo service nginx reload

웹 페이지 확인

정상적으로 설정하였다면 다음과 같은 화면을 볼수 있습니다.
http://서버IP/swagger-ui.html

Gracefully shutdown

executable jar로 웹 컨테이너를 띄운후에 업데이트등의 이슈로 서버를 재시작해야 하는 경우가 있습니다. 이런 경우엔 좀더 주의가 필요합니다. 왜냐하면 서버를 그냥 종료시켜 버리면 서버에서 열심히 처리중이었던 작업을 응답 못하고 종료해 버리기 때문입니다. 이런점을 고려하여 처리중인 작업이 있다면 잘 마무리하고 종료시키는 작업이 필요합니다. 이것을 Gracefully Shutdown이라고 부릅니다. Linux에서는 작업을 종료시킬때 몇가지 옵션을 제공하는데요. kill 명령에 -TERM 혹은 -15를 옵션으로 주면 서버를 바로 종료시키지 않고 서버에게 종료 요청을 보내게 됩니다. 해당 서버는 TERMINATE 요청을 받으면 현재 처리중인 작업을 모두 마무리하고 서버를 종료시키게 됩니다.

Terminate 요청을 처리할 수 있도록 GracefulShutdown 작성

다음과 같이 GracefulShutdown을 작성합니다. 해당 코드는 실행중인 작업을 마치고 종료하는 내용을 담고 있으며 30초안에 종료되지 않으면 강제로 작업을 종료하도록 처리하고 있습니다.

package com.rest.api.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

    private static final int TIMEOUT = 30;

    private volatile Connector connector;

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            try {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
                    log.warn("Tomcat thread pool did not shut down gracefully within "
                            + TIMEOUT + " seconds. Proceeding with forceful shutdown");

                    threadPoolExecutor.shutdownNow();

                    if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
                        log.error("Tomcat thread pool did not terminate");
                    }
                } else {
                    log.info("Tomcat thread pool has been gracefully shutdown");
                }
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Boot 설정에 GracefulShutdown 추가

위에서 작성한 GracefulShutdown을 SpringRestApiApplication 설정에 추가합니다.

@SpringBootApplication
public class SpringRestApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringRestApiApplication.class, args);
    }

// 다른 설정 생략

    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers(gracefulShutdown);
        return factory;
    }
}

테스트 코드 작성

오랫동안 지속되는 작업을 하나 만듭니다. 아래는 10초의 딜레이를 주고 결과를 내보내는 코드입니다.

@GetMapping("/helloworld/long-process")
@ResponseBody
public String pause() throws InterruptedException {
    Thread.sleep(10000);
    return "Process finished";
}

종료 테스트

서버에 해당 내용을 배포하기 전에 수행중인 작업이 있는 상태에서 서버를 종료시키면 다음과 같은 오류를 맞이하게 됩니다.

GracefullyShutdown 내용을 빌드 후 재 배포 한다음 다시 종료 테스트를 하면 다음과 같이 작업을 마무리 한후에 서버가 종료 됩니다.
http://서버IP/helloworld/long-process

Was로그에서도 정상적으로 종료된것을 확인 할 수 있습니다.

2019-05-02 12:01:38.821  INFO 3333 --- [Thread-2] com.rest.api.config.GracefulShutdown     : Tomcat thread pool has been gracefully shutdown

무중단 배포(Blue / Green Deployment)

지금까지의 방식은 배포시 Was가 완전히 로딩 될때까지 서비스가 불가능한 이슈가 있습니다. 배포시에도 서비스가 가능한 무중단 배포로 수정 해보도록 하겠습니다.

무중단 배포 관련한 내용은 아래 링크를 참고 하였습니다.
https://jojoldu.tistory.com/267

Spring Actuator 추가

springboot에서는 actuator를 제공하여 서비스의 상태 정보를 실시간으로 모니터링 할수 있도록 제공하고 있습니다. build.gradle에 아래 내용을 추가합니다.

implementation 'org.springframework.boot:spring-boot-starter-actuator'

SpringSecurity 설정 수정

actuator가 제공하는 endpoint에 접근할수 있도록 “/actuator/health”를 permitAll() 처리합니다. actuator가 제공하는 모니터링 endpoint는 이외에도 많으니 아래 링크를 참고해 보시기 바랍니다.

.antMatchers(HttpMethod.GET, "/helloworld/**","/actuator/health").permitAll()

서버를 스타트 하고 localhost:8080/actuator/health에 접근하면 현재 서버 상태를 json으로 볼 수 있습니다.

{"status":"UP"}

배포 환경 구성

배포를 위한 환경을 구성합니다. 로컬 PC또는 배포를 위해 마련한 서버에서 배포 디렉터리를 생성하고 git repository를 clone 받습니다. AwsFreetierKeyPair.pem은 AWS EC2에 ssh로 접속하기위해 사용되는 인증서라고 보면 됩니다.

$ mkdir deploy
$ cd deploy
$ git clone https://github.com/codej99/SpringRestApi.git
$ ls
AwsFreetierKeyPair.pem  SpringRestApi

배포 대상 서버리스트 작성

배포는 여러서버에 배포 될 수 있으므로 관련 서버 주소를 담은 파일을 하나 만듭니다.
환경별로 서버 리스트가 나뉘어질 수 있으므로 파일명에 _{환경명}으로 생성합니다.

$ vi server_alpha.list
123.456.789.100
234.567.333.444

배포 shell script 작성

deploy.sh 란 이름으로 파일을 하나 만들고 실행 권한을 부여합니다.

$ touch deploy.sh
$ chmod +x deploy.sh

배포는 다음과 같은 순서로 진행됩니다.

  • 타겟 서버에 배포 디렉토리(/home/ec2-user/app/dist)를 생성합니다.
  • scp 명령어로 로컬 build 디렉터리에서 jar파일을 타겟서버 배포 디렉터리로 전송합니다.
  • 전송한 jar파일에 대한 심볼릭 링크를 생성합니다. 배포시 버전에 따른 Jar파일명을 하나의 이름으로만 관리함으로써 Jar실행시 최신 버전의 Jar파일 이름을 알아내야할 필요가 없게 해주는 용도입니다.
  • 현재 실행중인 서버를 조회합니다. 8083이 띄워져 있으면 새로 업데이트된 jar파일로 8084로 서버를 한대 더 실행합니다.
  • 신규 서버 스타트가 완료되면(health체크를 5초마다 실행) 기존에 실행되고 있던 서버는 종료합니다. 위에서 Gracefully shutdown을 적용했으므로 연결되어있던 커넥션은 모두 처리되고 서버가 종료됩니다.
  • 서버리스트에 여러개의 서버가 나열되어 있었다면 위의 작업이 서버수만큼 반복됩니다.

deploy.sh

deploy shell은 인자로 profile을 받습니다. $ ./deploy.sh alpha
위에서 생성한 배포 환경의 디렉토리로 PROJECT관련 정보를 세팅합니다.
PROJECT_HOME은 배포에 필요한 여러가지 파일이나 정보를 모아두는 배포 홈 디렉터리 입니다.
SVR_LIST는 server_alpha.list파일을 읽어서 배포 대상 서버 리스트를 가져옵니다.
DEPLOY_PATH는 배포 대상 서버에 생성되는 디렉토리입니다. 각자의 환경에 맞게 변경합니다. AWS_ID도 환경에 맞게 변경합니다.
그외 현재 일자, JVM설정 옵션, PEM파일 위치 등을 세팅합니다.

#!/bin/bash
PROFILE=$1                                                                                                                                       
PROJECT=SpringRestApi                                                                                                                             
PROJECT_HOME=/Users/happydaddy/deploy/${PROJECT}                                                                                                      
JAR_PATH=${PROJECT_HOME}/build/libs/api-0.0.1-SNAPSHOT.jar                                                                                       
SVR_LIST=server_${PROFILE}.list                                                                                                                   
SERVERS=`cat $SVR_LIST`                                                                                                                           
DEPLOY_PATH=/home/ec2-user/app                                                                                                                   
AWS_ID=ec2-user                             
DATE=`date +%Y-%m-%d-%H-%M-%S`        
JAVA_OPTS="-XX:MaxMetaspaceSize=128m -XX:+UseG1GC -Xss1024k -Xms128m -Xmx128m -Dfile.encoding=UTF-8"
PEM=AwsFreetierKeyPair.pem                                                                                                                       
PORT=8083                                                                                                             

echo Deploy Start                     
for server in $SERVERS; do                              
    echo Target server - $server
    # Target Server에 배포 디렉터리 생성           
    ssh -i $PEM $AWS_ID@$server "mkdir -p $DEPLOY_PATH/dist"                                                                                
    # Target Server에 jar 이동
    echo 'Executable Jar Copying...'
    scp -i $PEM $JAR_PATH $AWS_ID@$server:~/app/dist/$PROJECT-$DATE.jar
    # 이동한 jar파일의 바로가기(SymbolicLink)생성                             
    ssh -i $PEM $AWS_ID@$server "ln -Tfs $DEPLOY_PATH/dist/$PROJECT-$DATE.jar $DEPLOY_PATH/$PROJECT"      
    echo 'Executable Jar Copyed'
    # 현재 실행중인 서버 PID 조회
    runPid=$(ssh -i $PEM $AWS_ID@$server pgrep -f $PROJECT)
    if [ -z $runPid ]; then            
        echo "No servers are running"                             
    fi                                                                                                                                       
    # 현재 실행중인 서버의 포트를 조회. 추가로 실행할 서버의 포트 선정                              
    runPortCount=$(ssh -i $PEM $AWS_ID@$server ps -ef | grep $PROJECT | grep -v grep | grep $PORT | wc -l)
    if [ $runPortCount -gt 0 ]; then        
        PORT=8084                             
    fi                                                                                                                                                                     
    echo "Server $PORT Starting..."
    # 새로운 서버 실행
    ssh -i $PEM $AWS_ID@$server "nohup java -jar -Dserver.port=$PORT -Dspring.profiles.active=$PROFILE $JAVA_OPTS $DEPLOY_PATH/$PROJECT < /dev/null > std.out 2> std.err &"
    # 새롭게 실행한 서버의 health check
    echo "Health check $PORT"                             
    for retry in {1..10}
    do    
        health=$(ssh -i $PEM $AWS_ID@$server curl -s http://localhost:$PORT/actuator/health)                      
        checkCount=$(echo $health | grep 'UP' | wc -l)
        echo "Check count - $checkCount"    
        if [ $checkCount -ge 1 ]; then      
           echo "Server $PORT Started Normaly"                  
           # 기존 서버 Stop
           if [ $runPid -gt 0 ]; then             
                echo "Server $runPid Stopping..."           
                ssh -i $PEM $AWS_ID@$server "kill -TERM $runPid"
                sleep 5
                echo "Server $runPid Stopped"           
           fi
           break;                         
        fi                         
        sleep 5     
    done                   
done                      
echo Deploy End

위의 shell script를 실행하면 tomcat이 8083<->8084로 전환되면서 실행되는 것을 확인할 수 있습니다.

무중단 서비스를 위한 개선

Java 서블릿 컨테이너 기반의 프레임워크는 서버 구동 속도가 느리다는 단점을 가지고 있습니다. 그것은 서버를 띄우기 위한 시간이 꽤 걸린다는 것입니다. 다른 언어의 서버들에 비해 Java기반의 tomcat은 적어도 30초 이상의 서버 Startup 시간을 필요로 하므로 배포시엔 신중을 가해서 해야합니다. 이 시간 동안에는 서비스의 응답을 처리할 수 없으므로 추가적인 처리가 필요한데 여기에서 무중단 배포라는 용어가 나오게 되었습니다. 실습에서는 2개의 tomcat 인스턴스를 nginx의 설정에서 순간적으로 교체하는 방법으로 무중단을 구현할 것입니다.

현재 nginx는 8083 port의 톰켓을 서비스하고 있는것으로 가정하고 순서는 다음과 같습니다.

  • 8083 port가 실행중인 상태에서 최신버전의 Jar로 8084 port Was를 하나 더 실행합니다.
  • 8084의 Startup이 완료되면 nginx 설정을 8084로 변경하여 실시간 반영합니다.
  • 8083 Was는 종료(Gracefully Shutdown) 처리합니다.

Nginx에서 서비스할 서버 정보 파일 생성

/etc/nginx/conf.d에 service_addr.inc를 생성하고 다음 한줄을 추가합니다.
$service_addr이라는 환경변수를 추가하는 작업입니다.

set $service_addr http://127.0.0.1:8083;

nginx.conf 수정

서비스 URL은 다이나믹하게 변경되어야 하므로 아래 내용은 삭제합니다.

upstream tomcat {
        ip_hash;
        server 127.0.0.1:8083;
}

service_addr.inc를 정보를 보도록 추가

include /etc/nginx/conf.d/service_addr.inc; 을 추가하여 서버 정보를 읽을수 있게 합니다. proxy_pass는 http://tomcat; -> $service_addr;로 수정하여 service_addr.inc에서 설정한 환경 변수로 처리되도록 합니다.

include /etc/nginx/conf.d/service_addr.inc;

location / {
     proxy_set_header    HOST $http_host;
     proxy_set_header    X-Real-IP $remote_addr;
     proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header    X-Forwarded-Proto $scheme;
     proxy_set_header    X-NginX-Proxy true;
     proxy_pass $service_addr;
     proxy_redirect  off;
     charset utf-8;
}

배포 shell script 수정

8083, 8084 서버가 띄워져 있는 상태에서 nginx가 바라보는 was를 변경하기 위해 shell내용을 수정합니다. 기존 deploy.sh와 거의 동일하며 nignx의 was를 실시간으로 변경하는 내용이 추가되었습니다.

# 새롭게 띄워진 Port의 정보로 service_addr.inc를 갱신합니다.
ssh -i $PEM $AWS_ID@$server “echo ‘set \$service_addr http://127.0.0.1:$PORT;’ | sudo tee /etc/nginx/conf.d/service_addr.inc”
# nginx를 reload하여 설정 내용을 바로 반영합니다.
ssh -i $PEM $AWS_ID@$server “sudo service nginx reload”

#!/bin/bash
PROFILE=$1                                                                                                                                       
PROJECT=SpringRestApi                                                                                                                             
PROJECT_HOME=/Users/happydaddy/deploy/${PROJECT}                                                                                                     
JAR_PATH=${PROJECT_HOME}/build/libs/api-0.0.1-SNAPSHOT.jar                                                                                       
SVR_LIST=server_${PROFILE}.list                                                                                                                   
SERVERS=`cat $SVR_LIST`                                                                                                                           
DEPLOY_PATH=/home/ec2-user/app                                                                                                                   
AWS_ID=ec2-user                             
DATE=`date +%Y-%m-%d-%H-%M-%S`        
JAVA_OPTS="-XX:MaxMetaspaceSize=128m -XX:+UseG1GC -Xss1024k -Xms128m -Xmx128m -Dfile.encoding=UTF-8"
PEM=AwsFreetierKeyPair.pem                                                                                                                       
PORT=8083                                                                                                             

echo Deploy Start                     
for server in $SERVERS; do                              
    echo Target server - $server
    # Target Server에 배포 디렉터리 생성           
    ssh -i $PEM $AWS_ID@$server "mkdir -p $DEPLOY_PATH/dist"                                                                                
    # Target Server에 jar 이동
    echo 'Executable Jar Copying...'
    scp -i $PEM $JAR_PATH $AWS_ID@$server:~/app/dist/$PROJECT-$DATE.jar
    # 이동한 jar파일의 바로가기(SymbolicLink)생성                             
    ssh -i $PEM $AWS_ID@$server "ln -Tfs $DEPLOY_PATH/dist/$PROJECT-$DATE.jar $DEPLOY_PATH/$PROJECT"      
    # 현재 실행중인 서버 PID 조회
    runPid=$(ssh -i $PEM $AWS_ID@$server pgrep -f $PROJECT)
    if [ -z $runPid ]; then            
        echo "No servers are running"                             
    fi                                                                                                                                       
    # 현재 실행중인 서버의 포트를 조회. 추가로 실행할 서버의 포트 선정                              
    runPortCount=$(ssh -i $PEM $AWS_ID@$server ps -ef | grep $PROJECT | grep -v grep | grep $PORT | wc -l)
    if [ $runPortCount -gt 0 ]; then        
        PORT=8084                             
    fi                                                                                                                                                                     
    echo "Server $PORT Starting..."
    # 새로운 서버 실행
    ssh -i $PEM $AWS_ID@$server "nohup java -jar -Dserver.port=$PORT -Dspring.profiles.active=$PROFILE $JAVA_OPTS $DEPLOY_PATH/$PROJECT < /dev/null > std.out 2> std.err &"
    # 새롭게 실행한 서버의 health check
    echo "Health check $PORT"                             
    for retry in {1..10}
    do    
        health=$(ssh -i $PEM $AWS_ID@$server curl -s http://localhost:$PORT/actuator/health)                      
        checkCount=$(echo $health | grep 'UP' | wc -l)
        if [ $checkCount -ge 1 ]; then      
           echo "Server $PORT Started Normaly"                  
           # 기존 서버 Stop / Nginx 포트 변경 후 리스타트
           if [ $runPid -gt 0 ]; then             
                echo "Server $runPid Stop"           
                ssh -i $PEM $AWS_ID@$server "kill -TERM $runPid"
                sleep 5
                echo "Nginx Port Change"           
		ssh -i $PEM $AWS_ID@$server "echo 'set $service_addr http://127.0.0.1:$PORT;' | sudo tee /etc/nginx/conf.d/service_addr.inc"
                echo "Nginx reload"           
		ssh -i $PEM $AWS_ID@$server "sudo service nginx reload"
           fi
           break;                         
	   else
	       echo "Check - false"
           fi                         
        sleep 5     
    done                   
    if [ $retry -eq 10 ]; then
	   echo "Deploy Fail"
    fi 
done                      
echo Deploy End

위의 shell을 반복으로 실행하면서 ec2의 프로세스를 확인해 봅니다.

$ ./deploy.sh alpha
Deploy Start
Target server - 13.16.198.80
Executable Jar Copying...
api-0.0.1-SNAPSHOT.jar                                                                                        100%   45MB  11.2MB/s   00:04    
Executable Jar Copyed
Server 8084 Starting...
Health check 8084
Check count -        0
Check count -        0
Check count -        0
Check count -        0
Check count -        1
Server 8084 Started Normaly
Server 10191 Stopping...
Server 10191 Stopped
Deploy End

ec2의 로그를 보면 8083 port는 종료되고 8084 port가 실행되고 있음을 확인할 수 있습니다.

$ ps -ef | grep SpringRestApi
ec2-user 10926     1 14 13:56 ?        00:00:19 java -jar -Dserver.port=8084 -Dspring.profiles.active=alpha -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -Xss1024k -Xms128m -Xmx128m -Dfile.encoding=UTF-8 /home/ec2-user/app/SpringRestApi
ec2-user 11077  7590  0 13:58 pts/0    00:00:00 grep --color=auto SpringRestApi

다시 한번 배포를 실행합니다.

$ ./deploy.sh alpha
Deploy Start
Target server - 13.16.198.80
Executable Jar Copying...
api-0.0.1-SNAPSHOT.jar                                                                                        100%   45MB  11.2MB/s   00:04    
Executable Jar Copyed
Server 8083 Starting...
Health check 8083
Check count -        0
Check count -        0
Check count -        0
Check count -        0
Check count -        1
Server 8083 Started Normaly
Server 10926 Stopping...
Server 10926 Stopped
Deploy End

ec2의 로그를 보면 요번에는 8084 port가 종료되고 8083 port가 실행되고 있음을 확인할 수 있습니다.

$ ps -ef | grep SpringRestApi
ec2-user 11173     1 18 14:00 ?        00:00:19 java -jar -Dserver.port=8083 -Dspring.profiles.active=alpha -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -Xss1024k -Xms128m -Xmx128m -Dfile.encoding=UTF-8 /home/ec2-user/app/SpringRestApi
ec2-user 11336  7590  0 14:01 pts/0    00:00:00 grep --color=auto SpringRestApi

여기까지 무중단 배포를 구현하기 위한 절차를 실습해보았습니다. 다음 시간에는 이러한 절차를 Jenkins를 이용하여 좀 더 편리하게 사용할 수 있도록 변경하는 실습을 진행해보겠습니다.

최신 소스는 GitHub 사이트를 참고해 주세요.
https://github.com/codej99/SpringRestApi/tree/feature/gracefullyshutdown

GitHub Repository를 import하여 Intellij 프로젝트를 구성하는 방법은 다음 포스팅을 참고해주세요.

Docker로 개발 환경을 빠르게 구축하는 것도 가능합니다. 다음 블로그 내용을 읽어보세요!

스프링 api 서버를 이용하여 웹사이트를 만들어보고 싶으시면 아래 포스팅을 참고해 주세요.

연재글 이동[이전글] SpringBoot2로 Rest api 만들기(11) – profile을 이용한 환경별 설정 분리
[다음글] SpringBoot2로 Rest api 만들기(13) – Jenkins 배포(Deploy) + Git Tag Rollback