NGINX memcached 모듈 활용 Memcached 데이터 조회 가이드

이 포스트는 NGINX memcached 모듈을 활용하여, 요청의 URI와 매개변수를 조합한 키로 Memcached에서 캐시된 데이터를 조회하고, 캐시 된 데이터가 없으면 원본 서버에서 데이터를 가져와 응답하도록 구성하는 방법을 설명합니다.

PosrgreSQL DB와 Flask 서버를 구성하여, Flask 서버가 요청을 수신하면 DB에서 데이터를 조회하고, 이를 Memcached에 캐시하도록 구성하겠습니다. 그리고 NGINX를 활용하여 Memcached에서 캐시 된 데이터를 조회하고, 캐시된 데이터가 없으면 Flask 서버에서 응답을 받도록 구성하겠습니다.

NGINX는 Memcached 모듈을 통해 지정한 키값으로 Memcached에서 캐시 된 데이터를 조회하고 응답하는 기능만 제공하며, 캐시 데이터는 NGINX 외부의 애플리케이션에서 저장 및 관리되어야 합니다.

NGINX memcached 모듈은 NGINX OSS, NGINX Plus 모두 사용 가능하며 컴파일 설치 시 별도의 매개변수 구성없이 기본적으로 제공됩니다.

목차

1. Memcached란?
2. NGINX, Memcached 버전 정보
3. Memcached 설치
4. PostgreSQL DB, Flask 구성
5. NGINX memcached 구성 및 확인
6. 결론

1. Memcached란?

Memcached는 오픈소스의 고성능 분산 메모리 캐시 시스템으로, 데이터베이스 부하를 줄이고 웹 애플리케이션의 성능을 향상하기 위해 설계된 키-값 저장소입니다. 가장 자주 사용되는 데이터를 RAM에 캐시하여 데이터베이스 조회 및 API 응답을 최소화함으로써 애플리케이션의 응답 시간 및 DB/서버의 부하를 크게 개선할 수 있습니다.

2. NGINX, Memcached 버전 정보

  • NGINX OSS: 1.25.5
  • Memcached: memcached 1.6.14

3. Memcached 설치

Ubuntu 22.04 VM에서 설치를 진행했습니다. VM의 IP는 192.168.201.111입니다.

1. apt 저장소를 업데이트하고, Memcached를 설치합니다.

$ sudo apt update
$ sudo apt install memcached
$ memcached -V

memcached 1.6.14

2. /etc/memcached.conf 파일을 수정하여, 외부 서버와 통신할 수 있도록 memcached 설정을 변경합니다.

# memcached default config file
# 2003 - Jay Bonci <jaybonci@debian.org>
# This configuration file is read by the start-memcached script provided as
# part of the Debian GNU/Linux distribution.

# Run memcached as a daemon. This command is implied, and is not needed for the
# daemon to run. See the README.Debian that comes with this package for more
# information.
# daemon으로 실행
-d

# Log memcached's output to /var/log/memcached
# 로그 파일 경로 설정
logfile /var/log/memcached.log

# Be verbose
# 로깅 수준 설정
# -v

# Be even more verbose (print client commands as well)
# 로그에서 클라이언트 명령어까지 출력 설정
# -vv

# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
# Note that the daemon will grow to this size, but does not start out holding this much
# memory
# 캐시 메모리 설정
-m 64

# Default connection port is 11211
# 포트 설정
-p 11211

# Run the daemon as root. The start-memcached will default to running as root if no
# -u command is present in this config file
# Memcached 실행 사용자 설정, 미설정 시 root 권한으로 실행
-u memcache

# Specify which IP address to listen on. The default is to listen on all IP addresses
# This parameter is one of the only security measures that memcached has, so make sure
# it's listening on a firewalled interface.
# listen IP 설정. 최초 설정값은 127.0.0.1로 외부 요청을 수신하지 못함
-l 192.168.201.111

# Limit the number of simultaneous incoming connections. The daemon default is 1024
# 동시 연결 수 설정
# -c 1024

# Lock down all paged memory. Consult with the README and homepage before you do this
# 메모리가 디스크로 스왑되는 것을 제한
# -k

# Return error when memory is exhausted (rather than removing items)
# 메모리가 부족할 경우 데이터를 삭제하는 대신 오류를 반환
# -M

# Maximize core file limit
# 코어 파일 크기 제한을 최대로 설정
# -r

# Use a pidfile
# pid 파일 경로 지정
-P /var/run/memcached/memcached.pid

외부의 NGINX에서 캐시 조회 및 외부 Flask에서 데이터 캐시를 하기 위해 listen IP 설정의 최초값인 127.0.0.1을 Memcached가 설치된 VM의 IP로 변경했습니다.

3. 변경된 설정 적용을 위해 재시작합니다.

$ sudo systemctl restart memcached

4. PostgreSQL DB, Flask 구성

Ubuntu 22.04 VM에서 설치를 진행했으며, 간단한 구성을 위해 하나의 VM에 함께 구성했습니다.
VM의 IP는 192.168.201.44입니다.

DB 구성

1. PosgreSQL DB를 설치합니다

$ sudo apt update
$ sudo apt install postgresql postgresql-contrib

2. 데이터베이스와 테이블을 생성합니다.

$ sudo -u postgres psql

ALTER USER postgres WITH PASSWORD 'yourpassword';

CREATE DATABASE example_db;
\c example_db;

CREATE TABLE example_table (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3. 데이터베이스에 임의의 데이터를 삽입합니다.

$ pip install psycopg2-binary faker
insert_data.py
import psycopg2
from faker import Faker

# PostgreSQL 연결 설정
conn = psycopg2.connect(
    dbname="example_db",
    user="postgres",
    password="yourpassword",
    host="localhost",
    port="5432"
)
cursor = conn.cursor()

# Faker 라이브러리로 가짜 데이터 생성
fake = Faker()

# 대량의 데이터 삽입 (예: 10,000개)
def insert_fake_data(num_records):
    for _ in range(num_records):
        name = fake.name()
        email = fake.email()
        cursor.execute(
            "INSERT INTO example_table (name, email) VALUES (%s, %s)",
            (name, email)
        )

    # 커밋하여 변경 사항을 반영
    conn.commit()

# 데이터 삽입
insert_fake_data(10000)

# 연결 종료
cursor.close()
conn.close()

print("Data inserted successfully!")
$ python3 insert.py 

Data inserted successfully!

4. 다음과 같은 형식의 데이터를 확인할 수 있습니다.

$ sudo -u postgres psql

# \c example_db;

# SELECT * FROM example_table LIMIT 10;

 id |        name         |           email           |         created_at         
----+---------------------+---------------------------+----------------------------
  1 | Timothy Pham        | whitemichael@example.org  | 2025-02-17 00:59:50.377695
  2 | Todd Coleman        | joneskimberly@example.org | 2025-02-17 00:59:50.377695
  3 | Jesus Acevedo       | ohayes@example.net        | 2025-02-17 00:59:50.377695
  4 | Christopher Herrera | jason27@example.com       | 2025-02-17 00:59:50.377695
  5 | Emily Franklin      | chad40@example.com        | 2025-02-17 00:59:50.377695
  6 | Stephanie Miles     | hunterdustin@example.net  | 2025-02-17 00:59:50.377695
  7 | Alexandria Warren   | tmatthews@example.com     | 2025-02-17 00:59:50.377695
  8 | David Greene        | amyduncan@example.net     | 2025-02-17 00:59:50.377695
  9 | Michael Williams    | amanda08@example.net      | 2025-02-17 00:59:50.377695
 10 | Jennifer Mcgrath    | mathew57@example.net      | 2025-02-17 00:59:50.377695
(10 rows)
Flask 구성

1. 구성에 필요한 패키지를 설치합니다.

$ pip install python-memcached flask

2. 예시를 위해 다음과 같이 Flask를 구성했습니다.

app.py
import memcache
import psycopg2
from flask import Flask, jsonify, request

app = Flask(__name__)

# Memcached 클라이언트 설정
memcached_client = memcache.Client(['192.168.201.111:11211'], debug=0)

# PostgreSQL 연결 설정
conn = psycopg2.connect(
    dbname="example_db",
    user="postgres",
    password="yourpassword",
    host="localhost",
    port="5432"
)
cursor = conn.cursor()

# 데이터베이스에서 전체 데이터를 조회
def fetch_all_data_from_db():
    cursor.execute("SELECT * FROM example_table")
    rows = cursor.fetchall()
    return rows

@app.route("/data", methods=["GET"])
def get_data():
    # id 파라미터 조회
    id_param = request.args.get('id')

    # 'id=all'인 경우 전체 데이터를 조회하고 캐시에 저장
    if id_param == 'all':
        # 전체 데이터를 DB에서 조회
        data = fetch_all_data_from_db()

        # Memcached에 캐시 저장
        memcached_client.set("/data?id=all", str(data), time=3600)  # 1시간 캐시

        return jsonify({"data": data, "source": "db"})

    # 특정 id 요청에 대한 처리
    elif id_param:
        memcached_key = f"/data?id={id_param}"

        # id에 해당하는 DB 데이터를 조회하고 캐시에 저장
        query = "SELECT name FROM example_table WHERE id = %s"
        cursor.execute(query, (id_param,))
        result = cursor.fetchone()

        # 데이터를 캐시에 저장
        if result:
            print(f"Setting cache for {memcached_key}")  # 캐시 저장 로그
            memcached_client.set(memcached_key, result[0], time=3600)  # 1시간 캐시
            return jsonify({"data": result[0], "source": "db"})

        return "No data found", 404

    return "id parameter is required", 400

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000)

/data 경로에 id 파라미터와 함께 요청이 오면, Flask는 데이터베이스에서 해당 데이터를 조회하고 Memcached에 저장한 후 응답합니다.

id 파라미터가 all이면 DB의 전체 데이터를 조회하고, 특정 id는 해당 데이터만 조회합니다. Memcached에 저장 시 키값은 /data?id=<id param> 형식을 사용합니다.

3. Flask를 실행합니다.

$ python3 app.py 

 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.201.44:5000

5. NGINX memcached 구성 및 확인

NGINX OSS가 설치된 VM의 IP는 192.168.200.176이며, 80번 포트로 최초 요청 수신 시 Memcached에서 캐시 된 데이터를 조회하고, 실패 시 Flask 서버로 요청을 프록시 하여 응답하도록 구성했습니다.

NGINX의 설정 파일은 다음과 같이 구성했습니다.
Memcached 관련 적용 가능한 전체 설정은 NGINX memcached 모듈 문서를 참고하세요.

server {

    listen 80;

    access_log /var/log/nginx/memcached_access.log;


    location / {
        # Memcached 조회를 위한 key 설정. URI?파라미터 형식으로 구성
        set            $memcached_key "$uri?$args";
        # Memcached host 설정. 
        memcached_pass 192.168.201.111:11211;
        # 캐시 조회 후 데이터 없을 시 @fallback으로 이동
        error_page     404 502 504 = @fallback;
    }

    # 캐시 조회 실패 시 Falsk 서버로 요청 프록시
    location @fallback {
        proxy_pass     http://192.168.201.44:5000;
    }
}

location / 블록을 통해 NGINX는 요청된 URI와 파라미터를 조합하여 Memcached에서 캐시 된 데이터를 조회합니다. 만약 Memcached에 데이터가 없다면, error_page 지정을 통해 @fallback 블록으로 이동하여 Flask 서버로 요청을 프록시 합니다.

Flask는 요청을 수신하면 데이터베이스에서 조회한 데이터를 응답하는 동시에 Memcached에 저장합니다.
따라서 이후 동일한 요청이 오면, NGINX는 Memcached에서 직접 데이터를 반환하여 Flask로 요청을 프록시 하여 DB의 데이터를 조회할 필요 없이 빠르게 응답할 수 있습니다.

NGINX 서버(192.168.200.176)로 요청을 전송하여 응답 시간을 확인했습니다.

NGINX memcached 응답 시간

최초 요청 시 Memcached에 캐시 된 데이터가 존재하지 않아 Flask를 통해 조회한 DB의 데이터를 응답으로 수신합니다. 응답에 0.016초가량의 시간이 소요된 것을 확인할 수 있습니다.
이후 요청은 Memcached에 캐시 된 데이터를 통해 응답하여 0.006초가량으로 절반 이하의 시간이 소요된 것을 확인할 수 있습니다.

telnet 명령어로 Memcached로 TCP 요청을 전송하여 캐시된 데이터를 확인할 수 있습니다.

전체 데이터 조회 요청을 전송합니다.

$ curl -s -w "\n Total Time: %{time_total}s\n" http://192.168.200.176/data?id=all
NGINX memcached 전체 데이터 응답 시간

0.46초의 응답 시간이 소요됩니다.

동일한 전체 데이터 조회 요청을 재전송하면 다음과 같이 약 0.1초 안팎의 응답 시간으로 최초 요청 대비 1/4가량의 시간이 소요된 것을 확인할 수 있습니다.

NGINX memcached 캐시된 전체 데이터 응답 시간

6. 결론

이번 포스트에서는 NGINX memcached 모듈을 활용하여 요청의 URI와 매개변수를 조합한 키로 Memcached에서 캐시 된 데이터를 조회하고, 캐시 된 데이터가 없으면 원본 서버(Flask)에서 데이터를 가져와 응답하는 방법을 알아봤습니다.

Memcached를 설치하여 구성하는 방법과, PosrgreSQL DB와 Flask 서버를 구성하여 Flask 서버가 요청을 수신하면 DB에서 데이터를 조회하여 응답하면서, Memcached에 데이터를 저장하도록 구성하는 방법을 알아봤습니다. 이후 NGINX 구성을 통해 요청을 수신하면 먼저 Memcached에서 데이터 조회를 시도하고, 실패 시 Flask 서버로 요청을 프록시 하여 데이터를 수신하도록 구성했습니다.

NGINX의 memcached 모듈을 활용하면 자주 조회되는 데이터베이스 데이터 및 자주 요청되는 API 응답을 Memcached에 캐싱하여 DB/서버의 부하를 줄이고, 빠르게 응답할 수 있습니다. 다만, NGINX는 캐시 데이터를 조회하는 기능만 지원하기 때문에 캐시 데이터는 NGINX 외부의 애플리케이션에서 저장 및 관리되어야 함을 명시해야 합니다.

NGINX의 다양한 모듈과 NGINX Plus 전용 모듈을 지원하는 상업용 구독 버전의 NGINX를 체험해 보고 싶으시다면 NGINX STORE를 통해 문의해 무료로 NGINX Plus trial을 체험해 보세요.

NGINX STORE를 통한 솔루션 도입 및 기술지원 무료 상담 신청

* indicates required