top of page
검색

OpenResty(nginx)와 Redis를 활용한 Reverse Proxy 뒷심 키우기


안녕하세요. 이번 포스트에서는 OpenResty와 Redis를 활용하여 Reverse Proxy 환경에서 어플리케이션 장애 시 데이터 유실을 방지하는 방법에 대해 소개하고자 합니다.

왜 필요할까요?

Reverse Proxy는 클라이언트와 어플리케이션 서버 사이에서 중개자 역할을 하면서 로드 밸런싱, SSL 터미네이션, 캐싱 등 다양한 기능을 제공해 주지만, 가끔 어플리케이션 서버의 배포나 시스템 재시작 때문에 일시적으로 통신이 끊기는 상황이 생길 수 있어요. 이럴 때 클라이언트의 소중한 요청 데이터가 손실될 위험이 있죠.


어떻게 해결할 수 있을까?

오픈레스티와 레디스를 활용하면 이런 문제를 말끔히 해결할 수 있어요.

  1. 클라이언트 → OpenResty: 클라이언트가 OpenResty 서버에 요청을 보냅니다.

  2. OpenResty → 애플리케이션 서버: OpenResty가 요청을 애플리케이션 서버로 전달합니다.

  3. 애플리케이션 서버 → OpenResty: 애플리케이션 서버가 응답하지 않으면 통신이 실패합니다.

  4. OpenResty → Redis: OpenResty가 요청 데이터를 Redis에 저장합니다.

  5. OpenResty ← Redis: 애플리케이션 서버가 복구되면 Redis에서 데이터를 복구하여 재전송합니다.

  6. OpenResty → 애플리케이션 서버: Redis에서 가져온 데이터를 애플리케이션 서버에 성공적으로 전송합니다.


이렇게 하면 Reverse Proxy의 뒷심이 더 좋아지겠죠?


# 애플리케이션 서버를 정의하는 업스트림 블록
upstream example {
    server {어플리케이션서버}:{포트};  # 애플리케이션 서버의 IP 주소와 포트
}

server {
    ...
    location / {
        proxy_pass http://example;  # 요청을 애플리케이션 서버로 프록시
        ...
        proxy_set_header Connection "upgrade";  # 업그레이드된 연결에 대한 헤더 설정
        proxy_set_header Host $host;  # 호스트 헤더를 원본 요청의 호스트로 설정
        proxy_cache_bypass $http_upgrade;  # 업그레이드된 연결에 대해 캐시 우회

        proxy_intercept_errors on;  # 프록시 오류를 인터셉트
    }

    # 502, 504 오류에 대한 커스텀 오류 처리 위치
    error_page 502 504 = @handle_error;

    # 오류 처리를 위한 내부 위치
    location @handle_error {
        internal;  # 내부 요청 전용으로 설정
        set $redis_key "{키이름}:$request_id";  # Redis에 저장할 키를 설정
        content_by_lua_block {
            -- Lua 모듈 로드
            local cjson = require "cjson"  -- JSON 처리 모듈
            local redis = require "resty.redis"  -- Redis 모듈
            local red = redis:new()  -- Redis 객체 생성
            red:set_timeout(400)  -- Redis 연결 타임아웃 설정 (400 밀리초)

            -- Redis에 연결
            local ok, err = red:connect("{레디스정보}", {포트})
            if not ok then
                ngx.log(ngx.ERR, "failed to connect to redis: ", err)
                ngx.status = 500
                ngx.say("Internal Server Error: Unable to connect to Redis.")
                ngx.exit(500)  -- Redis 연결 실패 시 오류 반환 및 종료
                return
            end

            -- Redis 데이터베이스 선택
            local res, err = red:select({레디스데이터베이스})
            if not res then
                ngx.log(ngx.ERR, "failed to select redis database: ", err)
                ngx.status = 502
                ngx.say("Bad Gateway: Unable to select Redis database.")
                ngx.exit(502)  -- 데이터베이스 선택 실패 시 오류 반환 및 종료
                return
            end

            -- 요청 본문 읽기
            ngx.req.read_body()  -- 요청 본문을 읽어들임
            local req_body = ngx.req.get_body_data()  -- 본문 데이터 가져오기
            local req_url = ngx.var.request_uri  -- 요청 URI 가져오기

            if req_body and #req_body > 0 then
                -- URL, 본문 및 시간 정보로 JSON 객체 생성
                local data = {
                    url = req_url,  -- 요청 URI
                    body = req_body,  -- 요청 본문
                    timestamp = ngx.time()  -- 요청 시각 (Unix 타임스탬프)
                }

                local json_data = cjson.encode(data)  -- JSON으로 인코딩
                local ok, err = red:set(ngx.var.redis_key, json_data)
                if not ok then
                    ngx.log(ngx.ERR, "failed to save request body to redis: ", err)
                    ngx.status = 500
                    ngx.say("Internal Server Error: Unable to save request data.")
                    ngx.exit(500)  -- 데이터 저장 실패 시 오류 반환 및 종료
                    return
                end
                ngx.log(ngx.ERR, "Request body saved to Redis.")  -- 저장 성공 로그
            else
                ngx.log(ngx.ERR, "No request body to save.")  -- 본문이 없을 경우 로그
            end

            -- 커스텀 메시지 반환 및 200 OK 상태
            ngx.status = 200
            ngx.say('{"message": "Temporary save completed","temp_save":true}')
            ngx.exit(200)
        }
    }
}



nginx 설정으로 일단 어플리케이션에 문제가 있을때의 데이터는 저장이되고

복원도 초기에는 nginx 에서 다시 복원되었을대 재전송을 하였으나 데이터 검토및 중도 처리 문제가있을수있다고 판단해서 새로 개발된 어플리케이션 서버에서 재기동시에 항상 체크해서 기존데이터와 비교 처리 복원하도록 개발하였습니다.



이처럼 간단한 아이디어와 설정을 통해 리버스 프록시의 안정성을 한층 높이고, 무중단 서비스를 운영할 수 있습니다. 내부 애플리케이션이라는 점에서 구체적인 코드를 공개할 수는 없지만, 이런 접근 방식을 통해 여러분의 시스템도 한 단계 더 안정적으로 만들 수 있기를 바랍니다.

생각만 잘하면, 해결책은 단순합니다! 🚀







Comments


bottom of page