openresty란?
"Nginx + Lua 스크립팅 + 유틸리티 라이브러리 모음"
구성에는 OpenResty = Nginx + LuaJIT + ngx_lua 모듈 + 여러 Lua 라이브러리 패키징이 있다.
특징으로 보면
- Nginx 내부에 Lua 스크립트 실행 가능
- MySQL, PostgreSQL, Redis, HTTP Client, JSON 처리 등 Lua 기반 모듈 내장
- Nginx 설정파일 안에서 Lua 코드 작성 가능
- 고성능 API 서버, 인증서버, 미들웨어 서버 등 개발에 유리
openresty는 docker를 통해 서버를 구동하였습니다. DB는 postgreSQL사용
(openresty안에 nginx로 서버를 구동시켜 openrety가 구동되게 한다.)
docker-compose.yml
services:
openresty:
build:
context: .
dockerfile: dockerfile
env_file:
- .env
image: openresty/openresty:latest
container_name: openresty-container
ports:
- "8080:80"
depends_on:
- postgres-container
networks:
- backend
postgres-container:
image: postgres:14
container_name: postgres-container
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
env_file:
- .env
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- backend
volumes:
pgdata:
networks:
backend:
driver: bridge
해당 필자는 docker-compose.yml를 통해 openresty와 postgreSQL의 이미지를 구동시켰다.
버전은 alpine이 아니라 최신버전(latest)으로 하였다.
alpine과 latest의 차이점은?
태그 | 특징 | 언제 쓰면 좋음 |
latest | Debian 기반, 크고 호환성 좋음 | 패키지 설치 많고 안정성 중요할 때 |
alpine | Alpine 기반, 엄청 가볍고 빠름 | 사이즈 작게, 추가 설치 거의 없을 때 |
dockfile.init을 통해 기본 라이브러리나 필수 요소로 설치해야 할 부분을 담았다.
dockerfile.init
# Dockerfile.init
FROM openresty-init:latest
RUN apt-get update && apt-get install -y \
luarocks \
libpq-dev \
gcc \
make \
openssl \
tzdata \
&& luarocks install lua-resty-psql \
&& luarocks install lua-resty-postgres \
&& luarocks install lua-cjson \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
apt 패키지 설치(alpine은 apk를 사용함)
패키지 | 역할 |
luarock | Lua 패키지 관리 툴(npm 같은 역할) |
libpq-dev | PostgreSQL C Client 라이브러리(Lua PostgreSQL 연동 시 필요) |
gcc | C 컴파일러 (Lua 모듈 빌드용) |
make | 빌드 자동화 툴(gcc랑 세트) |
openssl | TLS/SSL 라이브러리(암호화 통신) |
tzdata | 타임존 데이터 (시간 설정 관련) |
Lua 모듈 설치
모듈 | 역할 |
lua-resty-psql | OpenResty용 PostgreSQL 클라이언트 (Native 방식) |
lua-resty-postgres | Lua용 PostgreSQL 클라이언트 (비슷한데 방식 다름) |
lua-cjson | Lua에서 JSON 처리 라이브러리 |
Clean
apt-get clean && rm -rf /var/lib/apt/lists/*
- 이미지 최적화
- 불필요한 캐시 삭제 → 이미지 사이즈 줄임
dockfile
해당 파일은 파일 빌드할때 사용
FROM openresty/openresty:latest
COPY nginx/nginx.conf/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY nginx/lua/ /usr/local/openresty/nginx/lua/
COPY nginx/html/ /usr/local/openresty/nignx/html/
Nginx.conf
nginx.conf 역할 = Nginx/OpenResty 설정 파일
즉 Nginx가 어떻게 동작할지 정해주는 설계도이다.
- 어떤 포트로 받을지
- 어떤 URL에 어떤 처리할지
- 정적 파일? 프록시? Lua 실행?
- 로그 어떻게 남길지
- 보안 설정 (SSL, 인증 등)
- 캐시, 타임아웃 설정
- DB 연결 (Lua 이용 시)
이럴때 nginx.conf 파일을 사용한다.
worker_processes 1;
env DB_HOST;
env DB_PORT;
env DB_NAME;
env DB_USER;
env DB_PASSWORD;
events {
worker_connections 1024;
}
http {
resolver 127.0.0.11;
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
location / {
root ./html/index.html;
index index.html;
}
location = /seller/api/v1/records {
content_by_lua_file ./lua/records.lua;
}
}
}
work_process 1;
Nginx 프로세스 1개만 사용
(= CPU 1개만 사용, 개발/테스트 용으로 주로 이렇게 설정)
환경변수 사용
env DB_HOST;
env DB_PORT;
env DB_NAME;
env DB_USER;
env DB_PASSWORD;
- Docker나 OS에 설정된 DB 관련 환경변수를 Lua 코드에서 쓸 수 있게 함
- Lua 안에서 이렇게 접근 가능:
local host = os.getenv("DB_HOST")
이벤트 처리
events {
worker_connections 1024;
}
- 최대 동시 접속 수 = 1024개
(= 동시에 1024 커넥션 처리 가능)
HTTP 설정
http {
resolver 127.0.0.11;
- DNS 서버 지정
- 127.0.0.11 은 Docker 내부 DNS → 컨테이너 간 서비스 이름으로 접근할 때 사용 (ex: DB 호스트를 서비스명으로)
기본 설정
include mime.types;
default_type application/octet-stream;
- 파일 타입 자동 지정
- 못 찾으면 기본은 바이너리
서버 블록
server {
listen 80;
server_name localhost;
- 80번 포트로 요청 받음
- 서버 이름은 localhost (보통 dev)
정적 파일 서빙
location / {
root ./html/index.html;
index index.html;
}
- / 로 들어오면 → ./html/index.html 보여줌
(루트 디렉토리 경로는 Dockerfile 기준)
Lua API 처리
location = /seller/api/v1/records {
content_by_lua_file ./lua/records.lua;
}
- 정확히 /seller/api/v1/records 요청 오면
- Lua 스크립트 ./lua/records.lua 실행
- API 서버 처리를 Lua로 직접 수행하는 방식
Lua 파일
local pg = require "resty.postgres"
local cjson = require "cjson"
-- 요청 메서드 확인
if ngx.req.get_method() ~= "GET" then
ngx.status = 405
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode({
error = "Method not allowed"
}))
return
end
-- 페이지네이션 파라미터 가져오기
local args = ngx.req.get_uri_args()
local page = tonumber(args.page) or 1
local limit = tonumber(args.limit) or 10
local offset = (page - 1) * limit
-- PostgreSQL 연결
local db, err = pg:new()
if not db then
ngx.status = 500
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode({
error = "Failed to create database object"
}))
return
end
db:set_timeout(1000) -- 1초 타임아웃
ngx.log(ngx.ERR, "DB HOST => ", os.getenv("DB_HOST"))
-- 데이터베이스 접속
local ok, err = db:connect({
host = os.getenv("DB_HOST"),
port = tonumber(os.getenv("DB_PORT")),
database = os.getenv("DB_NAME"),
user = os.getenv("DB_USER"),
password = os.getenv("DB_PASSWORD")
})
if not ok then
ngx.status = 500
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode({
error = "database connection error: " .. err
}))
return
end
-- 총 레코드 수 조회
local count_sql = "SELECT COUNT(*) as total FROM resource_transport_records"
local res, err = db:query(count_sql)
if not res then
ngx.status = 500
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode({
error = "Failed to query total count: " .. (err or "unknown error")
}))
return
end
local total = tonumber(res[1].total) or 0
local sql = [[
SELECT
*
FROM
resource_transport_records
LIMIT ]] .. limit .. " OFFSET " .. offset
local records, err = db:query(sql)
if not records then
ngx.status = 500
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode({
error = "Failed to query records: " .. (err or "unknown error")
}))
return
end
-- DB 연결 반환 (커넥션 풀에)
local ok, err = db:set_keepalive(10000, 100)
if not ok then
ngx.log(ngx.ERR, "Failed to set keepalive: ", err)
end
-- 응답 구성
local response = {
data = {
pagnation = {
page = page,
total = total,
limit = limit
},
records = records
}
}
-- JSON 응답 반환
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode(response))
역할
PostgreSQL DB에서 resource_transport_records 테이블 조회 → 페이지네이션 처리 → JSON 응답 반환
동작 흐름 (step by step)
1. 모듈 불러오기
local pg = require "resty.postgres" -- PostgreSQL 드라이버
local cjson = require "cjson" -- JSON 인코딩
2. GET 메서드 체크
if ngx.req.get_method() ~= "GET" then
return 405 에러
end
→ GET 요청만 허용 (다른 건 막음)
3. QueryString 파라미터 받기
local args = ngx.req.get_uri_args()
local page = tonumber(args.page) or 1
local limit = tonumber(args.limit) or 10
local offset = (page - 1) * limit
→ /api/v1/records?page=2&limit=20 이런 식 처리
→ 기본값 page=1, limit=10
4. DB 연결 준비
local db, err = pg:new()
db:set_timeout(1000)
→ DB 연결 준비 + 타임아웃 1초 설정
5. DB 접속
local ok, err = db:connect({
host = os.getenv("DB_HOST"),
...
})
→ nginx.conf에 등록한 env 변수로 DB 접속
6. 전체 데이터 개수 조회
local count_sql = "SELECT COUNT(*) as total FROM resource_transport_records"
→ 총 레코드 수 가져옴 (pagination 위해)
7. 데이터 조회
local sql = [[
SELECT * FROM resource_transport_records
LIMIT limit OFFSET offset
]]
→ 페이지네이션 쿼리 실행
8. 커넥션 풀 반환
db:set_keepalive(10000, 100)
→ DB 연결을 닫지 않고 재사용하도록 풀에 넣음
(timeout 10초, 최대 100개)
9. JSON 응답 반환
ngx.say(cjson.encode({
data = {
pagnation = { page, total, limit },
records = records
}
}))
→ 응답 형태 ↓
'openresty' 카테고리의 다른 글
openresty DB 커넥션 연결 최적화 (0) | 2025.04.11 |
---|---|
서버에서 적합한 openresty의 phase 구조 (0) | 2025.04.10 |