문제점

기존 프론트엔드 서버 배포 시, Nginx 버전을 1.19 버전으로 했었음.

 

위와 같이 멀티스테이지 빌드를 활용해 컨테이너 실행 시 , Nginx와 함께 프론트엔드 서버를 띄웠다.

 

하지만 서버 인스턴스 유형을 c6g 시리즈로 변경함에 따라 cpu 아키텍처도 arm64로 변경되었다.

 

AWS ECS 클러스터에서 등록할 컨테이너 인스턴스 오토 스케일링 그룹의 인스턴스 유형을 c6g.2xlarge 변경해주었다. 

 

하지만, 인스턴스 유형이 변경되어 프론트엔드 서버에서 백엔드 서버로 데이터를 받아오지 못하는 에러가 발생했다.(심지어 Status는 500)

 

해결

 

배포된 서버의 라우팅 테이블, NACL, 보안그룹을 다 살펴보았지만 이상이 없었고, 여러 삽질을 거치던 와중..

 

도커 허브에서 제공되는 Nginx 공식 이미지를 찾아보았다.

현재 도커 허브에서 제공되는 Nginx 공식 이미지는 1.24 버전부터 arm64 아키텍처를 지원하고 있어서 안 됐던 것이다..

 

따라서 Dockerfile에서 Nginx 버전을 arm64와 호환되는 버전인 1.25 버전으로 명시해주니 

 

arm64 아키텍처에서 Nginx가 이상을 일으키지 않았다 ..!! 

'Nginx' 카테고리의 다른 글

Nginx에 대하여  (0) 2023.07.29

Nginx? 동시접속 처리에 특화된 웹 서버 프로그램

  • 기존의 Apache Web Server(이하 아파치)는 꽤나 오래전에 탄생(1995년)에 하였음

  • 클라이언트 요청 하나당 connection을 만들어 요청을 처리하는 구조였음
    → connection을 생성하기 위해 서버의 프로세스를 생성함

  • 이 프로세스를 만드는 작업이 꽤나 오래걸리는 작업이었기 때문에 미리 프로세스를 만든 후, 요청이 들어오면 해당 프로세스를 사용하는 방식이었음

  • 그러나 시대가 변하며 clients의 요청이 폭발적으로 많아지며 C10K(connection 10000)의 문제가 발생하게 됨
    • connection 마다 프로세스가 하나씩 맡다 보니, 이는 곧 메모리 부족 및 CPU 자원을 너무 많이 할당하게 됨 → 더이상 프로세스를 생성할 수 없음

    • (심지어 요청당 connection의 유효시간까지 있게 되니…)

비동기식 이벤트 기반 접근 방식

 

Master Process Worker Process (실제로 일 하는 프로세스)
- 엔진 엑스 설정 파일을 읽고, 유효성을 검사

- Worker Process를 생성하고 관리.

- Worker Process의 갯수는 설정 파일에서 정의됨.

- 미리 워커 프로세스의 개수를 지정하지 않으면 사용가능 한 CPU 코어의 개수의 맞게끔 자동으로 개수를 조정해줌.
- 생성된 워커 프로세스는 각자 지정된 listener 소켓을 배정 받아 새로운 클라이언트 요청이 들어오면 커넥션을 형성하고 처리함.

- 커넥션은 지정된 Keep Alive 시간만큼 유지되며, 이 시간 동안은 이전 클라이언트 요청을 계속 처리 할 수 있음

- 새로운 요청이 들어오면 새로운 커넥션을 형성하거나, 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리함.
  • 즉, 각 요청에 대해 다른 전용 프로세스 또는 스레드를 생성하는 대신, 하나의 워커 프로세스에서 여러 연결 및 요청을 처리한다.

  • 일반적으로 워커 프로세스는 CPU 코어당 하나가 생성되기 때문에 기존 아파치 서버보다 훨씬 적은 메모리가 사용되며, 작업 전환에 CPU 주기가 낭비 되지 않는다.(context switching 감소)

  • 수백만 건의 동시 요청을 성공적으로 처리하고 매우 확장이 잘 됨

NGINX 워커 프로세스 내부

엔진엑스가 비동기 처리 방식 구조로 한 개 또는 고정된 프로세스만 생성하여 사용.

  • 각 작업자 프로세스는 마스터 프로세스(Nginx 구성 기반)구성으로 초기화 되며, 마스터 프로세스에서 리스너 소켓과 함께 제공되며,
  • 이벤트를 기다리는 것으로 시작된다.
    • 작업을 받는 역할 → 리스너 소켓
  • 각 연결은 프로토콜마다 각각 맞는 상태 시스템에 연결된다.

  • HTTP 상태 시스템이 가장 일반적으로 사용되지만 NGINX는 스트림(원시 TCP) 트래픽 및 여러 메일 프로토콜에 대한 상태 시스템도 구현한다.
    • 여러 프로토콜을 다 처리할 수 있다는 얘기(하나의 작업 프로세스에서)

결국 워커 프로세스의 작동 순서는

1. 워커 프로세스는 리스너 및 커넥트 소켓에서 이벤트(사용자 요청)를 기다림

 

2. 소켓에서 이벤트가 발생하고 이를 처리함 

 

이때, queue를 확인 한 뒤, 새로운 이벤트가 있다면 처리함 (loop)

  1. 많은 이벤트를 수신한 다음 필요한 작업을 수행하며 하나씩 처리하게 됨,
  2. 따라서 모든 처리는 하나의 스레드에 있는 큐에 대한 루프에서 수행된다.
    • 대부분의 경우 매우 빠르며 엔진엑스는 즉시 대기열의 모든 이벤트를 처리한다.

 

스레드 풀

  • 그러나 위의 비동기 처리 방식에서 문제점이 하나 있다. (비단 모든 비동기 처리 방식의 쟁점이겠지만..)
    • 바로 블로킹의 발생 → 무거운 작업이 발생하면 상당한 시간 동안 이벤트 처리 주기를 중지해버림(ex: Disk I/O)
    • (동기 방식으로 데이터베이스에서 응답을 받는 경우) → 액세스까지 시간이 걸릴 수 있음
    • 이러한 작업을 처리하는 동안 워커 프로세스가 다른 작업을 수행할 수 없으며 사용 가능한 시스템 리소스가 더 있고,
    • 큐의 이벤트가 해당 리소스를 사용할 수 있더라도 다른 이벤트를 처리할 수 없게됨.

이해를 돕기 위한 예시   🤩

  • 위와 같이 가게 앞에 대기하고 있는 손님이 많은 영업사원을 생각해보자.
  • 대기열,기다리는 줄(큐)의 첫 번째 사람은 매장에 없는 창고에 있는 것을 요구한다.(무거운 작업)
  • 영업 사원은 상품을 배달하기 위해 창고로 이동하는데, 이 때 대기열의 사람들은 영업 사원이 올 때까지 기다려야한다.

  • 이것과 거의 흡사한 상황이 엔진엑스에서 메모리에 캐시 되어 있지 않은, 디스크 I/O 작업 요청 상황이다.
  • 대기열(큐)에 있는 다른 작업들은 디스크 작업이 필요하지 않을 수 있지만, 해당 무거운 작업이 끝날 때까지 강제로 기다려야 한다.
  • 결과적으로 대기 시간이 증가하고 시스템 리소스가 완전히 활용되지 않는다.

 

이해를 돕기 위한 예시 2

  • 스레드 풀을 사용하며 영업 사원은 더 똑똑 해졌다.
  • 전과 같은 상황으로, 손님이 매장과 멀리 떨어져있는 창고의 상품을 요청할때, 배송사원은 요청을 받고
  • 다른 배송 서비스에 손님의 주문을 맡겨버린다 → 배송 서비스에서 주문을 처리함
    • 해당 요청을 한 손님만 주문 완료를 기다리게 됨
  • 영업 사원은 무거운 작업을 다른 배송 서비스에 맡겨버리고 다음 손님을 받게 된다. 
    • NGINX 측면에서 스레드 풀은 배송 서비스의 기능을 수행한다.
    • 작업 대기열과 작업을 처리하는 스레드로 구성된다.
    • 워커 프로세스(영업 사원)이 긴 작업을 수행해야 하는 경우 작업을 자체적으로 처리하는 대신 스레드풀 대기열에 작업을 배치하여 맡긴다.

엔진엑스 역할

1. 정적 파일 처리하는 HTTP 서버로서의 역할

  • HTML, CSS , JavaScript, 이미지와 같은 정보를 웹 브라우저에 전송하는역할을 함
    • 만약 웹 서버를 이용하지 않는다면 요청 때마다 이러한 정보들을 웹 앱 서버에서 제공하기 때문에 시간도 시간이지만, 서버의 부하가 심해짐

2. 응용 프로그램 서버(WAS)에 요청을 보내는 리버스 프록시 역할

  • 리버스 프록시 란 외부 클라이언트에서 서버로 접근 시, 중간에서 중개자 역할을 하여 내부 서버로 접근할 수 있도록 도와주는 서버이며, 이를 활용했을때 얻을 수 있는 장점은 다음과 같다.
    • 보안
      • 외부 사용자로부터 내부망에 있는 서버의 존재를 숨길 수 있다.
      • 모든 요청은 리버스 프록시 서버에서 받으며, 매핑되는 내부 서버로 요청을 전달함.
      • 또한 Nginx는 SSL 설정도 가능하기 때문에, 사용자의 요청과 응답 등과 같은 정보를 암호화해서 통신이 가능하다.
      • 그렇지만, client ↔ Nginx만 SSL 설정을 하고 나머지 업스트림 서버와의 통신은 HTTP 통신으로만 할 수 있다고도 한다.
    • 로드밸런싱
      • 리버스 프록시 서버가 내부 서버에 대한 정보를 알고 있으므로,
      • 각 서버의 상태에 따라 부하를 분산시키며 요청을 전달할 수 있다. (여러 부하분산 알고리즘 사용)

엔진엑스로 정적 컨텐츠 서비스하기

  • 엔진엑스 설치 후 /etc/nginx/conf.d/default.conf에 생성된 기본 HTTP 설정 파일
server {
	listen 80 default_server;
    server_name www.example.com;
    
    location / {
    			root /usr/share/nginx/html;
                # alias /usr/share/nginx/html;
                index index.html index.htm;
    }


}
  • 이 설정은 HTTP 프로토콜과 80 포트를 사용해 /usr/share/nginx/html/ 경로에 저장된 정적 콘텐츠를 제공한다.
    • server블록은 새로운 server 블록을 선언하여, 엔진엑스가 처리할 새로운 컨텍스트를 정의함.
    • listen 80은 엔진엑스가 80 포트로 들어오는 요청을 수신하게 하고, 이 블록에 정의된 내용이 80 포트에 대한 기본 컨텍스트가 되도록 default_server 매개 변수를 사용한다.
      • 예제는 단일 포트만 사용하지만, 필요에 따라 포트 범위를 지정해 줄 수 있다.
    • server_name 지시자에는 서버가 처리할 호스트명이나 도메인명을 지정한다.
      • 만약 설정이 default_Server 매개변수를 통해 지정되지 않았다면, 엔진엑스는 요청 호스트 헤더값이 server_name 지시자에 지정된 값과 같을 때만 server 블록 에 지정된 내용을 수행한다.
      • 즉, 서버가 사용할 도메인이 정해지지 않았다면 default_server 매개변수를 사용해 기본 컨텍스트를 정의하고 server_name 지시자를 생략할 수 있다.
    • location 블록은 URL의 경로를 기반으로 한다.
      • 엔진엑스는 요청된 URI에 가장 적합한 location 블록을 찾는다.
      • root 지시자 는 주어진 컨텍스트에서 콘텐츠를 제공할 때 서버의 어떤 경로에서 파일을 찾을지 알려준다.
      • 엔진엑스는 root 지시자에 정의된 경로에 수신된 URI 값을 합쳐 요청된 파일을 찾는다.
        • root지시자 정의된 경로 + 80포트로 수신된 URI
      • location 지시자에 URI 접두어를 사용했다면 이 값도 root 지시자에 지정한 값과 합쳐진다.
        • 이렇게 동작하지 않도록 하려면 root 지시자 대신 alias 지시자를 사용한다.
    • index 지시자는 URI에 더는 참고할 경로 정보가 없을 때 엔진엑스가 사용할 기본 파일 혹은 확인할 파일 목록을 알려준다.
  •  

'Nginx' 카테고리의 다른 글

Nginx와 arm64  (0) 2023.08.04

+ Recent posts