무중단 배포

Nginx CD

Posted by Damin on April 1, 2021

Nginx 설치

1
2
sudo apt install nginx // nginx 설치
sudo service nginx start // nginx 시작

Nginx 설정 변경

vim /etc/nginx/sites-available/default

location / 에 코드 수정

try_files $uri $uri/ =404; 제거

1
2
3
4
5
6
7
location / {
                proxy_pass http://localhost:8080;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_post;
        }

  • 사용자가 80 포트로 들어오면 8080 포트로 리다이렉트
  • header 에 정보들 담기

Spring ProfileController 생성

  • 현재 어느 서버가 가동중인지 확인하는 ProfileController 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequiredArgsConstructor
@RestController
public class ProfileController {
    private final Environment env;

    @GetMapping("/profile")
    public String profile(){
        List<String> profiles = Arrays.asList(env.getActiveProfiles());
        List<String> realProfiles = Arrays.asList("real", "real1", "real2");
        String defaultProfile = profiles.isEmpty()? "default" : profiles.get(0);

        return profiles.stream()
                .filter(realProfiles::contains)
                .findAny()
                .orElse(defaultProfile);
    }
}
  • 이에 대한 테스트 코드 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class ProfileControllerTest {

    @Test
    public void real_profile이_조회된다(){
        //given
        String expectedProfile = "real";
        MockEnvironment env = new MockEnvironment();
        env.addActiveProfile(expectedProfile);
        env.addActiveProfile("oauth");
        env.addActiveProfile("real-db");

        ProfileController controller = new ProfileController(env);

        //when
        String profile = controller.profile();

        //then
        assertThat(profile).isEqualTo(expectedProfile);
    }

    @Test
    public void real_profile이_없으면_첫_번째가_조회된다(){
        //given
        String expectedProfile = "oauth";
        MockEnvironment env = new MockEnvironment();

        env.addActiveProfile(expectedProfile);
        env.addActiveProfile("real-db");

        ProfileController controller = new ProfileController(env);

        //when
        String profile = controller.profile();

        assertThat(profile).isEqualTo(expectedProfile);
    }
}

Travis CI 배포 자동화를 위한 profile 2개 생성

1
2
server.port=8081 // application-real1.propertis
server.port=8082 // application-real2.propertis

Nginx 설정 수정

/etc/nginx/conf.d 에 service-url.inc 파일 생성

set $service_url http://127.0.0.1:8080;

저장 후 종료

/etc/nginx/sites-available 파일에 해당 파일 include 및 활용

1
2
3
4
5
6
7
8
9
10
        include /etc/nginx/conf.d/service-url.inc;

        location / {
                proxy_pass $service-url;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_post;
        }

nginx 재시작

service nginx restart

배포 스크립트들 작성

EC2 에 /home/ubuntu/app 위치에 step3, step3/zip 디렉터리 생성

mkdir /home/ubuntu/app/step3 && mkdir /home/ubuntu/app/step3/zip

무중단 배포를 위한 스크립트는 총 5개

  • stop.sh : 기존 nginx에 연결되어 있지는 않지만, 실행 중이던 스프링 부트 종료
  • start.sh : 배포할 신규 버전 스프링 부트 프로젝트를 stop.sh 로 종료한 ‘profile’로 실행
  • health.sh : ‘start.sh’로 실행시킨 프로젝트가 정상적으로 실행됐는지 체크
  • switch.sh : 엔진엑스가 바라보는 스프링 부트를 최신 버전으로 변경
  • profile.sh : 앞선 4개 스크립트 파일에서 공용으로 사용할 ‘profile’과 포트 체크 로직

appspec.yml 파일 수정 (hooks, files-destination)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
files:
  - source: /
    destination: /home/ubuntu/app/step3/zip/
    overwrite: yes

hooks:
  AfterInstall:
    - location: stop.sh
      timeout: 60
      runas: root
  ApplicationStart:
    - location: start.sh
      timeout: 60
      runas: root
  ValidateService:
    - location: health.sh
      timeout: 60
      runas: root

각 스크립트 파일들은 github 참고 spring-nginx-CD