Home 리버스 프록시를 세팅하며 고려한 점
Post
Cancel

리버스 프록시를 세팅하며 고려한 점

1. 글을 작성하게 된 계기


회사에서 리버스 프록시를 세팅할 일이 있었는데, Nginx를 사용하게 되었습니다. 이 과정에서 어떤 것을 고려했는지, 새롭게 알게 된 내용은 무엇인지를 기록하고 싶어 글을 작성하게 되었습니다.

리버스 프록시를 세팅하게 된 계기는 해당 글을 참조해주세요.







2. Nginx 설정과 고려한 점


Nginx를 세팅하며 고려했던 점은 일관되지 않은 URL 처리, 정적 자원 효율적 응답, 트레이스(Trace), 로그 롤링(Log Rolling) 정도인데, 이에 대해 살펴보겠습니다. 우선, 전체 설정은 다음과 같습니다.

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
http {
    # 정적 파일 캐시 관련 설정
    open_file_cache max=2000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors off;

    # 정적 파일 성능 향상 설정 
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    aio on;
    output_buffers 1 64k;

    etag on;
    keepalive_timeout 65;
    types_hash_max_size 4096;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
		
    # 로그 포맷
    log_format log_with_request_id '$remote_addr - $remote_user [$time_local] "$request" '
                                   '$status $body_bytes_sent "$http_referer" '
                                   '"$http_user_agent" "$http_x_forwarded_for" ';

    server {
        listen 3000;
        root /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;
				
        # Trace를 위한 Request-ID 추가
        add_header X-Request-ID $request_id;
			
        # 리버스 프록시 
        location / {
            proxy_pass ${IP_ADDRESS};
            proxy_set_header Host $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-Request-ID $request_id;
        }
	
        # index.html
        location /home {
            root /usr/share/nginx/html;
            try_files /index.html =404;
            tcp_nopush on;
            sendfile on;
        }

        # 에러 페이지 404
        error_page 404 /400/404.html;
        location = /400/404.html {
            internal;
        }

        # 에러 페이지 500
        error_page 500 502 503 504 /500/50x.html;
        location = /500/50x.html {
            internal;
        }
    }
}







2-1. 리버스 프록시

리버스 프록시를 세팅하며 고려했던 점은 API 서버의 URL이 정리돼 있지 않다는 점이었습니다. 이는 /api/**와 같이 URL이 통일된 것이 아니라, 규칙성이 없다라는 건데요, 이는 간단히 / 하위를 포워딩하는 것으로 해결할 수 있었습니다. 즉, 들어오는 모든 요청을 포워딩해서 뒤로 넘기기만 하는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http {
    ......

    server {

        ......
			
        # 리버스 프록시 
        location / {
            proxy_pass ${IP_ADDRESS};
            proxy_set_header Host $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-Request-ID $request_id;
        }
	
        ......

    }
}







스프링 클라우드의 라우팅 설정도 이와 마찬가지로 할 수 있는데요, 단순히 요청을 넘기는 용도로만 이를 사용할 수도 있다라는 것을 새롭게 알게 되었습니다. 여담으로, 이는 리버스 프록시를 세팅할 때, Nginx와 스프링 클라우드 두 개의 방법/해결책을 제시했기 때문에, 어떤 것을 선택할지 몰라 모두 준비하며 알게 되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
class RouteConfiguration(
    @Value("\${location.product}")
    private val productServerUrl: String
) {
    @Bean
    fun gatewayRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route { router ->
                router.path("/**")
                    .uri(productServerUrl)
            }
            .build()
    }
}

물론 이건 좋은 활용(?)법, 해결책은 아니지만, URL이 일정한 구조를 가지지 않는 경우, 이렇게 단순히 포워딩하는 방법도 있다 정도만 이해하고 넘어가면 될 것 같습니다.









2-2. 정적 자원 효율적 응답

두 번째 고려한 점은 정적 자원을 효율적으로 응답하는 것이었습니다. 예를 들어, Nginx 내부에 index.html 파일이 있는 경우, Nginx에서 이 파일을 읽은 후 사용자에게 응답합니다. 따라서 이 과정을 개선해 응답을 더 빠르게, 효율적으로 해주고 싶었습니다. 설정이 조금 많고 복잡한데요, 이에 대해 하나씩 살펴보겠습니다.

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
http {
    # 정적 파일 캐시 관련 설정
    open_file_cache max=2000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors off;

    # 정적 파일 성능 향상 설정 
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    aio on;
    output_buffers 1 64k;

    etag on;
    keepalive_timeout 65;
    types_hash_max_size 4096;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
    
    ......

}







메타데이터 캐싱에 대한 설정입니다. 이를 통해 정적 파일의 메타 데이터를 캐싱해 응답을 빠르게 합니다. 파일을 읽을 때, 운영체제는 이를 통채로 메모리에 올리는 것이 아닌, 메타 데이터를 먼저 읽어 메모리에 올려두는데, 이 과정을 캐싱해두는 것입니다. 이를 이해하기 위해서는 파일을 open 했을 때 어떤 과정을 거치게 되는지를 이해해야 하는데, 이는 별도로 학습해볼 것을 권장드립니다.

1
2
3
4
5
6
7
8
9
10
11
http {
    
    # 정적 파일 캐시 관련 설정
    open_file_cache max=2000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors off;
    
    ......
	
}

Metadata(or metainformation) is “data that provides information about other data”, but not the content of the data itself, such as the text of a message or the image itself.







각 설정에 대한 설명은 다음과 같습니다.

  1. open_file_cache: 캐시에 저장할 수 있는 최대 파일 수 입니다. inactive는 캐시에서 사용되지 않은 파일을 얼마나 유지할지에 대한 설정입니다.
  2. open_file_cache_valid: 캐시된 메타데이터를 얼마나 저장할 지에 대한 설정입니다.
  3. open_file_cache_min_uses: 캐시에 유지되기 위해 최소 몇 번 사용되어야 하는지 횟수에 대한 설정입니다.
  4. open_file_cache_errors: 파일을 여는 과정에서 발생하는 권한, 파일 존재 유무 등에 대한 오류를 캐시할지 여부에 대한 설정입니다.







다음은 커널 sendfile 시스템 콜에 대한 설정입니다. 이를 통해 파일을 사용자 공간으로 복사하지 않고 직접 네트워크로 전송해 효율을 높입니다.

1
2
3
4
http {
    # 정적 파일 성능 향상 설정 
    sendfile on;
}

sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.







TCP에 관한 설정으로, 한 번에 보내는 데이터 양, 지연 시간을 조절합니다. 상세한 설명은 해당 포스팅을 참조해주세요.

1
2
3
4
5
http {
    # TCP 설정
    tcp_nopush on;
    tcp_nodelay on;
}







각 설정에 대한 설명은 다음과 같습니다.

  1. tcp_nopush: 한 번에 전송되는 데이터의 양을 최적화 합니다. 즉, 패킷을 전송하기 전에 충분한 데이터가 버퍼에 쌓일 때까지 기다립니다.
  2. tcp_nodelay: 소켓은 패킷 크기에 관계없이 버퍼에 있는 데이터를 보내게 됩니다.







왜 상반된 두 설정을 함께 쓰는지 이해가 안 될 수 있는데요, 간략히 정리하면 tcp_nopush로 데이터 전송 과정에서 네트워크 오버헤드를 줄이고, tcp_nodelay로 남은 작은 데이터 전송의 즉각성을 향상시킵니다. 즉, 큰 파일은 버퍼에 담아 일정량이 되면 전송하면서도, 남은 작은 데이터나 마지막 부분을 전송할 때는 이를 즉각 전송해 지연을 최소화하는 것입니다.

Combined to sendfile, tcp_nopush ensures that the packets are full before being sent to the client. This greatly reduces network overhead and speeds the way files are sent. Then, when it reaches the last — probably halt — packet, Nginx removes tcp_nopush. Then, tcp_nodelay forces the socket to send the data, saving up to 0.2 seconds per file.







마지막으로 비동기 I/O 활성화에 대한 설정입니다. 파일 I/O 작업을 비동기적으로 처리해 동시에 여러 I/O 요청을 효율적으로 처리할 수 있습니다.

1
2
3
4
http {
    # 비동기 파일 I/O 설정
    aio on;
}

On Linux, AIO can be used starting from kernel version 2.6.22. Also, it is necessary to enable directio, or otherwise reading will be blocking. When both AIO and sendfile are enabled on Linux, AIO is used for files that are larger than or equal to the size specified in the directio directive, while sendfile is used for files of smaller sizes or when directio is disabled.







2-3. 트레이스

세 번째로 고려한 점은 트레이스 였습니다. Nginx로 부터 스프링까지의 요청을 한 번에 추적하고 싶었는데요, 이를 위해 헤더에 이를 위한 값을 설정했습니다. 여기서 조금 아쉬운 부분이 있었는데, 루아 스크립트를 사용해 UUID를 생성하지 못한 것이었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
http {
    ......
		
    server {
        ......
				
        # Trace를 위한 Request-ID 추가
        add_header X-Request-ID $request_id;
			
        ......
    }
}

루아 스크립트를 사용해 UUID를 생성하는 것은 시간이 된다면 별도의 포스팅으로 작성해보겠습니다.







2-4. 로그 롤링

마지막으로 로그 롤링에 대한 설정입니다. 얼마나 로그를 보관할 지 등에 관한 설정으로 로그를 잘 관리하기 위한 설정입니다. 이는 Nginx 자체보다는 리눅스에 관한 설정으로, 별도로 학습해 볼 것을 권장해 드립니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ip-10-0-0-4 logrotate.d]# cat nginx
/var/log/nginx/*.log {
    create 0640 nginx root
    daily
    rotate 10
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}









3. 정리


리버스 프록시를 세팅하면서 어떤 점을 고려했고, 그 과정에서 어떤 설정을 해야 하는지 살펴보았습니다. 이 외에도 gzip과 같은 추가적 설정이 있지만, 이 부분은 짤막한 별도의 포스팅으로 작성해 보겠습니다.

  1. 리버스 프록시
  2. 정적 자원 효율적 응답
  3. 트레이스
  4. 로그 롤링

This post is licensed under CC BY 4.0 by the author.