[Nginx] 서버 장애/배포시 점검중 페이지 redirect

TO-BE

장애가 나서 서버에 닿지 않거나, 배포를 위해 WAS 를 내린 경우 점검중 페이지로 redirect되도록 한다.

Trouble Shooting

1) error_page 설정

기존 Nginx 설정파일에는 404 error를 받는 경우 '페이지를 찾을 수 없습니다' 페이지(NotFound.html)로 redirect 해주는 코드가 작성이 되어 있었다. 마찬가지로, 500번대 에러의 경우에도 동일하게 점검중 페이지인 maintenance.html로 redirect 해주면 될 것이라고 생각했다.

Nginx 설정파일 접근

  1. AWS EC2에 접속해서 웹서버로 떠 있는 instance에 연결한다. (Session Manager로 연결)
  2. sudo su - [사용자]
  3. cd /etc/nginx
  4. sudo nano nginx.conf
# nginx.conf

server {
	...
	
    error_page 404 /not-found.html;
    
    location = /not-found.html {
    	root /home/ec2-user/web;
        internal;
    }
}

기존의 nginx.conf 파일이 위와 같았기에, 아래와 같이 500번대 에러에 대해서도 동일하게 추가해줬다.

# nginx.conf

server {
	...
	
    error_page 500 501 502 503 /maintenance.html;
    
    location = /maintenance.html {
    	root /home/ec2-user/web;
        internal;
    }
}

결과

서버를 내린 후 api 요청을 날리자, nginx 기본 502 에러 페이지가 뜨고 리다이렉트가 제대로 되지 않았다.

2) proxy_intercept_errors on;

/maintenance.html를 @maintenance로 바꿔보고, location 블록 안에 rewrite를 대신 써보고 했지만, 문제가 해결되지 않았다. 구글링 결과 nginx를 proxy서버로 사용할때 목적지서버의 에러 응답 코드를 조작하고 싶다면 proxy_intercept_errors 설정을 해줘야 한 다는 것을 발견했다.

proxy_intercept_errors

기본적으로는 백엔드 서버가 보내오는 모든 에러 페이지를 엔진엑스가 직접 클라이언트에게 회신합니다. 이 지시어 값을 on으로 설정하면 에러 코드를 분석해 error_page 지시어에서 지정한 값과 비교 합니다. 출처

# nginx.conf

server {
	...
	
    proxy_intercept_errors on;
    
    error_page 500 501 502 503 /maintenance.html
    
    location = /maintenance.html {
    	root /home/ec2-user/web;
        internal;
    }
}

결과

서버를 내린 후 다시 메인 페이지에 접근하자, 빈 화면이 떴다. 네트워크 창을 열어 확인해보자, fetch api 요청에 대해서는 500번대 error 코드와 함께 maintenance.html 페이지가 응답으로 온 것을 확인할 수 있었다. 하지만 응답으로 온 이 페이지가 브라우저에서 reloading이 되지 않고, index.html만 가리키고 있었다. (Front Framework로는 Vue.js를 사용하고 있다)

이와 관련해서 nginx.conf 파일의 location 블록에 return 302 https://~~/maintenance.html 도 써보았지만, 이 경우에도 maintenance.html로 요청이 가는 것을 명시적으로 브라우저에서 확인할 수 있을 뿐이었고, 요청이 간다고 해도 요청에 대한 응답 페이지가 reload 되지는 않았다.

3) Front 처리 vs Nginx 설정 파일 변경

위 문제의 원인이 무엇인지는 밝혀내지 못했지만(Vue-Router와 관련이 있을것 같다고 동료분이 말씀해주셨지만 확실치 않다), 대안은 두가지가 있었다.
1. Front에서 서버에 대한 응답을 직접 처리하는 것
2. 배포시마다 Nginx 설정 파일을 변경해주는 것

1번의 경우는 서버에서 500번대 에러가 반환될 경우, /maintenance.html로 redirect해주는 코드를 api 응답을 받는 부분에서 공통적으로 처리하는 것이고,

const errorStatus = [ 500, 502, 503 ];
if (errorStatus.includes(error.response.status)) {
    window.location.href = '/maintenance.html';
}

2번의 경우는 배포시마다 Nginx의 기본 location 설정을 index.html이 아니라 maintenance.html을 타도록 변경해주는 것이다. 2번은 장애 발생시에는 설정을 수동으로 해줘야 한다는 단점이 있다.

# nginx.conf
server {
	...
    
    location / {
    	index index.html; 	# index maintenance.html로 변경
    }
}

해결방안

Front에서 처리해주는 방안으로 결정하고, axios interceptor를 활용하여 response 부분에서 공통적으로 500번대 에러에 대한 redirect 처리를 해주기로 했다.

프로젝트에는 1) Token이 필요한 경우 쓰는 axios Instance와 2) Token이 필요하지 않는 경우 쓰는 순수 Axios가 있었는데, interceptor 처리를 위해 2)번의 경우도 새로운 instance를 만들어서 interceptor 처리를 동일하게 해주었다.

import Axios from 'axios';

class NoAuthHTTP {
  constructor() {
    this.axios = new Axios.create({});
    this.setAxiosInterceptor(this.axios);
  }

  setAxiosInterceptor(axios) {
    axios.interceptors.response.use(
      async response => {
        return response;
      },
      error => {
        // 500번대 에러 코드 처리
        const errorStatus = [ 500, 502, 503 ];
        if (errorStatus.includes(error.response.status)) {
          window.location.href = '/maintenance.html';
        }
        return Promise.reject(error);
      },
    );
  }

  request(method, url, data, headers) {
    try {
      const config = {
        method: method,
        url: url,
        data,
        headers: headers,
      };
      return new Promise((resolve, reject) => {
        this.axios(config).then(res => {
          resolve(res);
        }).catch(err => {
          reject(err);
        });
      });
    } catch (error) {
      return Promise.reject(error)
    }
  }

}

export default NoAuthHTTP;

좋은 웹페이지 즐겨찾기