1. nginx 디렉토리 권한 수정

RHEL9 에서 dnf로 nginx를 설치하면 nginx 디렉토리들에 대한 권한이 default로 root로 들어가 있을 것이다. 이는 보안상 취약하므로, 아래 명령어를 수행하여 ‘nginx’ 유저 및 그룹으로 변경해 주도록 하자.

# /etc/nginx/ 디렉토리 권한을 nginx:nginx로 변경 
chown -R nginx:nginx /etc/nginx/

 

2. SELinux 설정 변경

SELinux는 최신 RHEL 및 CentOS 에서 기본적으로 활성화되어 있는 Linux 시스템용 보안 아키텍처이다. SELinux 모듈이 켜져 있으면, 파일 접근 권한, Upstream 위치로의 Proxy 및 소켓을 통한 다른 프로세스와 통신하는 것 등에 제한이 따르기 때문에, nginx 도 구동부터 막히게 된다.

따라서 아래 명령어 및 파일 수정으로 SELinux를 enforcing → permissive 로 변경해보자!

# 현재 SELinux 상태 확인 명령어 
sestatus

서버를 처음 셋업하고 SELinux에 대한 설정을 건드리지 않았다면, 위와 같이 Current modeenforcing 일 것이다. 이 상태면, systemctl start nginx 가 되지 않는다.

# 현재 SELinux 를 enforcing -> permissive 로 변경 
setenforce 0  

# SELinux 상태 다시 확인 
sestatus

이제 nginx 는 구동시킬 수 있을 것이다.

문제는 이 서버가 재부팅되면, 다시 SELinux의 config 파일을 읽어와서

enforcing 모드로 될 것이므로, 아예 config 파일에서도 permissive로 설정을 바꿔보자!

# RHEL9 기준, vim /etc/selinux/config  
# /etc/selinux/config 파일 
# 아래와 같이 'SELINUX' 값을 'permissive'로 변경 

SELINUX=permissive

SELinux 의 config 파일을 permissive로 변경 후에 다시 sestatus 명령어를 수행해보면, 위 그림과 같이 Mode from config file 의 값이 permissive로 변경된 것을 확인할 수 있다!

 

3. nginx 기본 설정 파일 수정 - nginx.conf

# nginx 설치 경로로 이동 
cd /etc/nginx  

# nginx.conf 파일 수정 전 원본 복사 
cp nginx.conf nginx.conf_ori  

# nginx.conf 파일을 수정 
vim nginx.conf
# nginx.conf  

# worker 프로세스를 실행할 사용자 설정 
# 이 user에 따라 권한이 달라질 수 있다. 
user nginx; 

# 실행할 worker 프로세스 설정 
# 서버에 장착되어 있는 코어 수만큼 할당하는 것이 보통. 
worker_processes auto;  

# 오류 로그를 남길 파일 경로 지정 
# 여기에서는 /etc/nginx/log 디렉토리를 만들었다고 가정 
error_log /etc/nginx/log/error.log warn;  

# nginx 마스터 프로세스 ID를 저장할 파일 경로 지정 
pid /run/nginx.pid;   

# 접속 처리에 관한 설정 
events { 	
    # 워커 프로세스 한 개당 동시 접속 수 지정 (512 or 1024 추천) 	
    worker_connections 1024; 
}  

# 웹, 프록시 관련 서버 설정 
http {  	
    # 액세스 로그 형식 지정 	
    log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 									'$status $body_bytes_sent "$http_referer" ' 									
    '"$http_user_agent" "$http_x_forwarded_for"';  	
    
    # 액세스 로그를 남길 파일 경로 지정 		
    access_log /etc/nginx/log/access.log main; 	 	
    
    # sendfile api 를 사용할 지 말 지 결정 	
    sendfile        on; 	
    
    # on - 데이터를 클라이언트로 전송할 때 데이터를 버퍼링하여 전송 지연을 최소화. 	
    tcp_nopush      on; 	
    
    # on - 작은 패킷을 빠르게 전송하여 지연 시간을 최소화.   
    tcp_nodelay   on; 	
    
    # 접속 시 커넥션을 몇 초동안 유지할 지에 대한 설정   
    keepalive_timeout 65; 	
    
    # nginx가 MIME 타입을 해시 테이블에 저장하는 데 사용하는 메모리의 최대 크기 (default 4096) 	
    type_hash_max_size 4096;  	  	
    
    # mime.types 파일을 읽어온다. 	
    include /etc/nginx/mime.types; 	
    
    # MIME 타입 설정 	
    default_type application/octet-stream;  	
    
    # 보안을 위해 nginx 버전을 숨기도록 지정 	
    server_tokens off;  	
    
    # /etc/nginx/conf.d 디렉토리에 하위의 모든 .conf 파일을 읽어오도록 설정 	
    include /etc/nginx/conf.d/*.conf;  	
    
    # RHEL9 의 dnf 배포판에는 없어 별도로 디렉토리 생성 및 conf 구문 추가 	
    # /etc/nginx/sites-enabled 디렉토리 하위의 모든 사이트 설정을 불러오도록 지정 	
    include /etc/nginx/sites-enabled/*; 
}

 

4. nginx-gunicorn 연동을 위한 config 파일 작성

필자는 RHEL9에서 dnf로 설치한 nginx에 sites-available 디렉토리가 따로 없었다. 그래서 별도로 생성해주었고, 해당 디렉토리는 실제 conf파일이 반영 되는 곳이 아닌, 미리 conf파일을 작성하여 저장하는 용도로 사용된다.

# /etc/nginx/sites-available 디렉토리 하위에 [gunicorn에 배포할 프로젝트 이름] 으로 설정 파일을 만들 것이다. 
# 예시 : /etc/nginx/sites-available/test_project  

server { 	
    listen 80; 	
    server_name [IP address | Domain name];  	
    
    location / { 		
        include proxy_params; 		
        proxy_pass http://unix:/apps/venvs/{프로젝트의 가상환경명}/run/{프로젝트명}.sock; 	
    } 
}
  • location / : 모든 요청을 구문에 맞게 리다이렉션 하라는 의미
  • http://unix:/~.sock : 해당 요 청을 Gunicorn의 Unix Domain Socket으로 보내라는 의미

 

5. proxy_params 파일 작성

위 test_proejct에 대한 config 파일에 include proxy_params 구문이 있는데, 우리는 해당 파일을 아직 작성하지 않았다. proxy_params 파일은 해당 사이트에 대한 별도의 프록시 파라미터 설정을 해주는 파일이다. 다음과 같이 작성해보도록 하자.

# vim /etc/nginx/proxy_params  
# proxy_params 파일  

# Request를 넘겨 받을 때 아래 Proxy Header 정보를 지정 
proxy_set_header X-Real-IP $remote_addr; 
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme; 
proxy_set_header X-NginX-Proxy true;  

# 서버(웹사이트)에 업로드 가능한 파일 최대 용량 
# default : 1M 
client_max_body_size 128M;  #

클라이언트 Buffer 사이즈 (body 들어오는 사이즈) 
# default : 32bit=8k, 64bit=16k 
client_body_buffer_size 1M;  

# 백엔드 서버로부터 해당 response를 버퍼링할 지 여부 정의 
# on : 버퍼가 제공하는 메모리 공간을 이용하여 응답 데이터를 메모리에 저장 
# off : 응답을 직접 클라이언트에게 전달 
proxy_buffering on;  

# 백엔드 서버로부터 response 데이터를 읽는 데 사용할 버퍼의 수와 크기 설정 
proxy_buffers 256 16k;  

# 백엔드 서버 response의 첫 부분을 읽기 위한 버퍼 크기 지정 
proxy_buffer_size 128k;  

# 여기서 지정한 값을 버퍼가 초과하면, 데이터를 클라이언트로 보내고 버퍼를 비움 
proxy_busy_buffers_size 256k;  

# 한 번에 사용할 수 있는 임시 파일 용량 
proxy_temp_file_write_size 256k;  

# 최대 사용할 수 있는 임시 파일 용량 
proxy_max_temp_file_size 1024M;  

# 백엔드 서버 접속 제한 시간(sec 단위)을 정의 
proxy_connect_timeout 3600;  

# 백엔드 서버로부터 데이터를 읽을 때의 제한 시간 
proxy_send_timeout 3600; 

# 백엔드 서버로 데이터를 전송할 때의 제한 시간 
proxy_read_timeout 3600;  

# 백엔드 서버가 보내오는 모든 에러 페이지를 nginx가 직접 클라이언트에게 회신 
proxy_intercept_errors on;

 

6. sites-enabled에 링크 생성을 통한 프로젝트 config 파일 적용

sites-available 에서 작성한 프로젝트에 대한 config파일을 sites-enabled 디렉토리 하위에 심볼릭 링크로 만들어 해당 config가 적용되도록 한다. nginx는 이러한 방식을 통해, 현재 서버에 적용되고 있는 config 파일을 직접 수정하지 않고, 별도의 파일로 작성 후에 바로 적용할 수 있도록 구현되어 있다.

# 실제 nginx에서 가져오는 설정 파일인 sites-enabled 하위 파일을 심볼릭 링크로 생성 
ln -s /etc/nginx/sites-available/test_project /etc/nginx/sites-enabled/test_project  

# 생성된 심볼릭 링크에 대한 소유권을 'nginx' 로 변경 
chown -h nginx:nginx /etc/nginx/sites-enabled/test_project

 

7, gunicorn 전용 계정 생성 및 권한 부여

python과 관련된 디렉토리인 /apps와 그 하위 디렉토리들에 대한 권한을 모두 ‘gunicorn’이라는 계정으로 소유권을 이전할 것이다. 일단 OS 계정부터 만들어야 하니, 다음 명령어를 수행하여 계정을 만들자.

# 아래 명령어는 모두 root 또는 root 계정과 동등한 권한으로 수행 
# 'gunicorn' Linux 계정 생성 
useradd gunicorn 

# 'gunicorn' Password 설정 
passwd gunicorn  

# python과 관련된 디렉토리인 /apps 및 그 하위 디렉토리의 소유권을 모두 gunicorn으로 변경 
chown -R gunicorn:gunicorn /apps  

# 'gunicorn' 홈 디렉토리 설정 
usermod -d /apps gunicorn  

# 'gunicorn'이 구동되면서 생성될 소켓 파일이 위치할 디렉토리 생성 
# 'gunicorn' 으로 계정 전환하여 생성하기 
mkdir /apps/venvs/test_project/run

8. gunicorn의 socket 파일에 대한 보안 컨텍스트 변경

nginx ↔ gunicorn 간 통신을 TCP가 아닌 Unix Domain Socket을 이용하여 셋업하고자 한다.

필자의 경우 nginx 와 gunicorn을 같은 서버에 둔다고 가정하여, 불필요한 네트워크 오버헤드를 없애기 위해 로컬 환경 안에서의 UDS(Unix Domain Socket)을 통한 통신으로 설정하는 것이다.

우선 /run/test_project.sock 파일이 생성되려면, gunicorn을 UDS 방식으로 한번 구동시켜줘야 한다.

gunicorn이 이미 실행되어 있다면 종료시키고, 다음 명령어로 gunicorn을 다시 구동시키자.

# python 프로젝트에 대한 venv 활성화 및 프로젝트의 홈 디렉토리에서 수행 
# "pybo:create_app()" 구문은 배포하는 애플리케이션에 맞춰 지정해야 한다. 
gunicorn --bind unix:/apps/venvs/test_project/run/test_project.sock "pybo:create_app()" &  

# /run/test_project.sock 파일 생성 확인 후, gunicorn 종료 
kill -9 [gunicorn PID]

 

gunicorn의 UDS 소켓 파일의 보안 컨텍스트를 변경하지 않으면, nginx 의 포트(IP:80)를 통한 접속 시,

502 Bad Gateway

에러와 nginx의 에러 로그에서는

Connection refuesed

에러를 마주할 것이다.

정석이라면 /apps/venvs/test_project/run/test_project.sock 파일의 보안 컨텍스트를 변경해줘야 하지만,

그냥 더 쉽게 ‘nginx’ 계정이 ‘gunicorn’ 그룹에 들어가도록 하자.
usermod -G nginx gunicorn

→ 이렇게 하면, nginx가 'gunicorn' 계정과 동일한 그룹으로 구동되어, gunicorn에 의해 생성되는 sock 파일을 접근할 수 있게 된다.

 

2. 작성한 nginx의 모든 config 파일 syntax 검사

nginx -t

위와 같이 syntax is oktest is successful 이라는 문구가 나타났다면, nginx config 파일의 syntax error는 없다고 볼 수 있다.

 

10. nginx 구동

config 파일에 이상이 없으니, 이제 nginx 를 구동하여 WSGI서버와 연동이 잘 되었는지 확인해보자!

systemctl start nginx

 

11. {IP}:80 를 통한 접속으로 test_project Web App 접속 여부 확인

nginx 및 gunicorn이 10.123.123.123 서버에서 구동된다면, http://10.123.123.123:80/ 로 접속하여 gunicorn으로 구동하는 애플리케이션의 인덱스 페이지가 나타나는 지 확인하면 된다.

 

12. gunicorn service 등록

systemctl 명령어로 gunicorn을 쉽게 올리고 내릴 수 있도록, service 파일로 작성해보자.

 

서비스 파일에 사용될 환경변수가 선언된 파일이다. 이는 해당 프로젝트의 venv 디렉토리에 위치시키도록 하자.

# /apps/venvs/test_project/test_project.env 파일 
FLASK_APP=pybo 
FLASK_DEBUG=false    # 운영 환경이므로 디버깅 모드는 false로 한다. 
APP_CONFIG_FILE=/apps/projects/test_project/config/production.py

 

이제 서비스 파일을 작성해보자. python의 경우 venv를 프로젝트 단위로 구성하였으므로, 서비스 파일 또한 gunicorn으로 구동시킬 프로젝트 단위로 생성해야 함을 주의하자!

# gunicorn으로 구동 시킬 프로젝트에 대한 서비스 생성 (root 계정 또는 그와 동등한 계정으로 수행) 
# 위 예시와 맞추기 위해 service 명을 'test_project'로 지정 
vim /usr/lib/systemd/system/test_project.service  

# test_project.service 파일 
[Unit] 
Description=gunicorn daemon 
After=network.target  

[Service] 
User=gunicorn 
Group=gunicorn 
WorkingDirectory=/apps/projects/test_project 
EnvironmentFile=/apps/venvs/test_project/test_project.env 
ExecStart=/apps/venvs/test_project.sh 
ExecStart= 
ExecStart=/apps/venvs/test_project/bin/gunicorn \         
    --workers 2 \         
    --bind unix:/apps/venvs/test_project/run/test_project.sock \         
    --access-logfile /apps/venvs/test_project/logs/access.log \         
    --error-logfile /apps/venvs/test_project/logs/error.log \         
    "pybo:create_app()"  
    
[Install] 
WantedBy=multi-user.target

 

참고

 

https://wikidocs.net/81078

 

4-11 웹 서버, Nginx 사용해서 파이보에 접속하기

여기서는 웹 서버 Nginx를 설치하고 사용해 보자. Nginx는 높은 성능을 목적으로 개발한 웹 서버로 파이썬 웹 프레임워크인 장고나 플라스크에서 많이 사용한다. Nginx를 …

wikidocs.net

https://velog.io/@odh0112/Django-Nginx-Gunicorn-%EC%97%B0%EB%8F%99-2-fb00a9kg

 

[Django] - Nginx + Gunicorn 연동 (2)

1. Nginx란 이전에 gunicorn을 이용해 서버 가동을 했다면 이제는 웹서버인 Nginx와 연동해 서비스를 꾸려나가볼 것입니다. 우선 Nginx는 트래픽이 많은 웹사이트의 서버(WAS)를 도와주는 고성능 경량 웹

velog.io

https://kscory.com/dev/nginx/setting

Uploaded by

N2T

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기