블로그 | NGINX

TCP 로드 밸런싱 및 Galera 클러스터를 사용한 MySQL 확장

NGINX-F5-수평-검정-유형-RGB의 일부
리엄 크릴리 썸네일
리암 크릴리
2021년 7월 26일 게시

[ 편집자 – 이 게시물은 원래 2016년에 게시되었으며 그 이후 개정된 NGINX 기능을 사용하도록 업데이트되었습니다. 자세한 내용은 NGINX JavaScript 모듈NGINX Plus 대시보드를 사용한 고급 로깅을 참조하세요.

NGINX Plus R5 에서 TCP 부하 분산 기능을 도입했으며, 이후 릴리스에서 지속적으로 기능을 추가했으며 UDP 부하 분산 도 지원했습니다. 이 문서에서는 TCP 부하 분산에 대한 핵심 요구 사항과 NGINX Plus가 이를 어떻게 해결하는지 살펴보겠습니다.

NGINX Plus의 기능을 살펴보기 위해 확장된 데이터베이스 백엔드를 갖춘 애플리케이션의 핵심 구성 요소를 나타내는 간단한 테스트 환경을 사용하겠습니다. 테스트 환경을 구축하는 방법에 대한 자세한 지침은 부록을 참조하세요.

MySQL 서버의 부하 분산을 위한 테스트 환경은 MySQL 클라이언트와 Galera 클러스터 사이에 NGINX Plus를 배치합니다.
MySQL 노드 로드 밸런싱을 위한 테스트 환경

이 환경에서 NGINX Plus는 데이터베이스 서버의 역방향 프록시 역할을 하며 기본 MySQL 포트 3306에서 수신합니다. 이는 클라이언트에게 간단한 인터페이스를 제공하는 반면, 백엔드 MySQL 노드는 클라이언트에 어떤 영향도 미치지 않고 확장(심지어 오프라인으로 전환)될 수 있습니다. 테스트 환경에서 프런트엔드 애플리케이션을 나타내는 클라이언트로 MySQL 명령줄 도구를 사용합니다.

이 문서에 설명된 기능 중 대부분은 NGINX Open Source와 NGINX Plus에 모두 적용됩니다. 간결하게 설명하기 위해 본 설명서에서는 NGINX Plus를 지칭하며, NGINX 오픈 소스에서 사용할 수 없는 기능을 명확하게 설명합니다.

다음과 같은 사용 사례를 살펴보겠습니다.

TCP 부하 분산

애플리케이션에 대한 부하 분산을 구성하기 전에 애플리케이션이 데이터베이스에 어떻게 연결되는지 이해하는 것이 중요합니다. 대부분의 테스트에서는 MySQL 명령줄 도구인 mysql(1) 을 사용하여 Galera 클러스터에 연결하고 쿼리를 실행한 다음 연결을 닫습니다. 그러나 많은 애플리케이션 프레임워크는 지연 시간을 최소화하고 데이터베이스 서버 리소스를 효율적으로 사용하기 위해 연결 풀을 사용합니다.

TCP 부하 분산은 스트림 구성 컨텍스트에서 구성되므로 기본 nginx.conf 파일에 스트림 블록을 추가하여 기본 MySQL 부하 분산 구성을 생성합니다.

 

이렇게 하면 TCP 부하 분산 구성이 기본 구성 파일과 분리됩니다. 그런 다음 nginx.conf 와 같은 디렉토리에 stream.conf를 생성합니다. 기본적으로 conf.d 디렉토리는 http 구성 컨텍스트용으로 예약되어 있으므로 해당 디렉토리에 스트림 구성 파일을 추가하는 것은 작동하지 않습니다.

 

먼저 Galera 클러스터의 세 개의 MySQL 노드를 포함하는 galera_cluster 라는 업스트림 그룹을 정의합니다. 테스트 환경에서는 각각 고유한 포트 번호를 사용하여 로컬호스트에서 접근할 수 있었습니다. zone 지시어는 부하 분산 상태를 유지하기 위해 모든 NGINX Plus 작업자 프로세스에서 공유되는 메모리 양을 정의합니다. server{} 블록은 NGINX Plus가 클라이언트를 처리하는 방법을 구성합니다. NGINX Plus는 기본 MySQL 포트 3306을 수신하고 모든 트래픽을 업스트림 블록에 정의된 Galera 클러스터로 전달합니다.

이 기본 구성이 작동하는지 테스트하려면 MySQL 클라이언트를 사용하여 연결한 Galera 클러스터의 노드 호스트 이름을 반환할 수 있습니다.

$ echo "변수 이름이 '호스트 이름'인 변수 표시" | mysql --protocol=tcp --user=nginx --password=plus -N 2> /dev/null 호스트 이름 node1

로드 밸런싱이 제대로 작동하는지 확인하려면 동일한 명령을 반복합니다.

$ !!!;!!!;!! 호스트 이름 노드2 호스트 이름 노드3 호스트 이름 노드1

이는 기본 라운드 로빈 부하 분산 알고리즘이 올바르게 작동하고 있음을 보여줍니다. 하지만 애플리케이션이 연결 풀을 사용하여 데이터베이스에 액세스하는 경우(위에서 제안한 대로), 라운드 로빈 방식으로 클러스터에 대한 연결을 열면 각 노드에서 연결 수가 불균형해질 가능성이 높습니다. 또한 연결이 유휴 상태(애플리케이션의 쿼리를 기다리는 중)이거나 쿼리를 처리하는 중일 수 있으므로 연결을 주어진 작업 부하와 동일시할 수 없습니다. 장기 TCP 연결에 대한 보다 적절한 부하 분산 알고리즘은 least_conn 지시문으로 구성된 Least Connections 입니다.

 

이제 클라이언트가 데이터베이스에 대한 새로운 연결을 열면 NGINX Plus는 현재 연결 수가 가장 적은 클러스터 노드를 선택합니다.

고가용성 및 상태 점검

클러스터 전체에서 데이터베이스 작업 부하를 공유하는 데 가장 큰 장점은 높은 가용성을 제공한다는 것입니다. 위에서 설명한 구성을 사용하면 NGINX Plus는 서버를 "다운"으로 표시하고 새 TCP 연결을 설정할 수 없는 경우 해당 서버로의 TCP 패킷 전송을 중단합니다.

이런 방식으로 다운된 서버를 처리하는 것 외에도 NGINX Plus는 자동으로 사전 예방적 상태 검사를 수행하도록 구성될 수 있으므로 클라이언트 요청이 해당 서버로 전송되기 전에 사용할 수 없는 서버를 감지할 수 있습니다(이것은 NGINX Plus에서만 제공되는 기능입니다). 게다가, 서버 가용성은 애플리케이션 수준의 상태 점검을 통해 테스트할 수 있습니다. 즉, 각 서버에 요청을 보내고 양호한 상태를 나타내는 응답을 받았는지 확인할 수 있습니다. 이렇게 하면 구성이 다음과 같이 확장됩니다.

 

이 예에서, 매치 블록은 MySQL 프로토콜 버전 10 핸드셰이크를 시작하는 데 필요한 요청 및 응답 데이터를 정의합니다. server{} 블록의 health_check 지시문은 이 패턴을 적용하고 NGINX Plus가 실제로 새로운 연결을 수락할 수 있는 서버에만 MySQL 연결을 전달하도록 합니다. 이 경우에는 20초마다 상태 검사를 수행하고, 단일 장애 발생 시 TCP 부하 분산 풀에서 서버를 제외한 후, 2회 연속으로 상태 검사가 성공적으로 완료되면 해당 서버에 대한 부하 분산을 재개합니다.

로깅 및 진단

NGINX Plus는 유연한 로깅을 제공하므로 모든 TCP/UDP 처리를 디버깅이나 오프라인 분석을 위해 기록할 수 있습니다. MySQL과 같은 TCP 프로토콜의 경우 NGINX Plus는 연결이 닫힐 때 로그 항목을 기록합니다. log_format 지시어는 로그에 나타나는 값을 정의합니다. Stream 모듈에서 사용 가능한 모든 변수 를 선택할 수 있습니다. stream.conf 파일의 맨 위, 스트림 컨텍스트에서 로그 형식을 정의합니다.

 

로깅은 server{} 블록에 access_log 지시문을 추가하여 활성화되며, 로그 파일의 경로와 이전 스니펫에서 정의한 로그 형식의 이름을 지정합니다.

 

이렇게 하면 아래 샘플과 같은 로그 항목이 생성됩니다.

$ tail -3 /var/log/nginx/galera_access.log 192.168.91.1 [2021년 7월 23일:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 192.168.91.1 [2021년 7월 23일:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 192.168.91.1 [2021년 7월 23일:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460

NGINX JavaScript 모듈을 사용한 고급 로깅

NGINX JavaScript는 "NGINX 네이티브" 프로그래밍 구성 언어입니다. 이는 NGINX 및 NGINX Plus를 위한 고유한 JavaScript 구현으로, 특히 서버 측 사용 사례와 요청별 처리를 위해 설계되었습니다.

[ 편집자 - 다음 사용 사례는 NGINX JavaScript 모듈에 대한 여러 사례 중 하나일 뿐입니다. 모든 사용 사례 목록은 NGINX JavaScript 모듈의 사용 사례를 참조하세요.

이 게시물은 NGINX JavaScript 0.2.4 에서 도입된 Stream 모듈에 대한 리팩토링된 세션( ) 객체와 NGINX JavaScript 0.4.0 에서 도입된 js_import 지시문을 사용하도록 업데이트되었습니다.

TCP/UDP 부하 분산을 위한 Stream 모듈 내에서 NGINX JavaScript는 요청 및 응답 패킷의 내용에 대한 액세스를 제공합니다. 즉, SQL 쿼리에 해당하는 클라이언트 요청을 검토하여 SELECTUPDATE 등의 SQL 메서드와 같은 유용한 요소를 추출할 수 있습니다. NGINX JavaScript는 이러한 값을 일반 NGINX 변수로 사용할 수 있도록 만들 수 있습니다. 이 예제에서는 JavaScript 코드를 /etc/nginx/sql_method.js 에 넣습니다.

 

getSqlMethod() 함수에는 현재 패킷을 나타내는 JavaScript 객체가 전달됩니다. fromUpstreambuffer 와 같은 이 객체의 속성은 패킷과 해당 컨텍스트에 대한 필요한 정보를 제공합니다.

우리는 업스트림 MySQL 서버에서 오는 패킷을 검사할 필요가 없으므로, 먼저 TCP 패킷이 클라이언트에서 오는지 확인합니다. 여기서 우리는 세 번째 클라이언트 패킷에 관심이 있습니다. 처음 두 패킷에는 핸드셰이크와 인증 정보가 들어 있기 때문입니다. 세 번째 클라이언트 패킷에는 SQL 쿼리가 포함되어 있습니다. 이 문자열의 시작 부분은 methods 배열에 정의된 SQL 메서드 중 하나와 비교됩니다. 일치 항목을 찾으면 결과를 전역 변수 $method 에 저장하고 오류 로그에 항목을 기록합니다. NGINX JavaScript 로깅은 심각도 정보의 오류 로그 에 기록되므로 기본적으로 나타나지 않습니다.

setSqlMethod() 함수는 같은 이름의 NGINX 변수가 평가될 때 호출됩니다. 이런 일이 발생하면 변수는 getSqlMethod() 함수 호출에서 얻은 NGINX JavaScript 전역 변수 $method 로 채워집니다.

이 NGINX JavaScript 코드는 단일 쿼리가 실행되는 MySQL 명령줄 클라이언트용으로 설계되었습니다. 이 코드는 해당 사용 사례에 맞게 조정될 수 있지만 복잡한 쿼리나 장기 연결에 걸친 여러 쿼리를 정확하게 캡처하지는 못합니다. NGINX JavaScript 모듈을 설치하고 활성화하는 방법에 대한 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.

로그에 SQL 메서드를 포함하려면 log_format 지시문에 $sql_method 변수를 포함시킵니다.

 

또한 NGINX Plus에 NGINX JavaScript 코드를 언제, 어떻게 실행할지 알려주기 위해 구성을 확장해야 합니다.

 

먼저 js_import 지시문을 사용하여 NGINX JavaScript 코드의 위치를 지정하고 js_set 지시문을 사용하여 NGINX Plus가 $sql_method 변수를 평가해야 할 때 setSqlMethod() 함수를 호출하도록 지시합니다. 그런 다음 server{} 블록 내에서 js_filter 지시어를 사용하여 패킷이 처리될 때마다 호출될 함수를 지정합니다. 선택적으로 info 옵션과 함께 error_log 지시문을 추가하여 NGINX JavaScript 로깅을 활성화할 수 있습니다.

이 추가 구성을 적용하면 액세스 로그가 다음과 같이 표시됩니다.

$ tail -3 /var/log/nginx/galera_access.log 192.168.91.1 [2021년 7월 23일:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 업데이트 192.168.91.1 [2021년 7월 23일:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 SELECT 192.168.91.1 [2021-07-23:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460 업데이트

NGINX 플러스 대시보드

[ 편집자 - 이 섹션은 원래 여기에서 논의된 별도의 확장된 Status 모듈을 대체하고 더 이상 사용되지 않는 NGINX Plus API를 참조하도록 업데이트되었습니다. ]

NGINX Plus 라이브 활동 모니터링 대시보드에서 MySQL 활동을 자세히 기록하는 것 외에도 실시간 지표와 업스트림 MySQL 서버의 상태를 관찰할 수 있습니다(NGINX 오픈 소스는 더 작은 규모의 지표 세트를 제공하며 Stub Status API를 통해서만 제공).

NGINX Plus 대시보드는 NGINX Plus R7 에서 도입되었으며 NGINX Plus API 에 대한 웹 인터페이스를 제공합니다. 별도의 /etc/nginx/conf.d/dashboard.conf 파일에서 http 컨텍스트에 새 server{} 블록을 추가하여 이를 활성화합니다.

 

MySQL 서비스에 대한 모니터링 데이터를 수집할 수 있도록 stream.confserver{} 블록을 status_zone 지시어로 업데이트해야 합니다.

 

이 구성을 적용하면 NGINX Plus 대시보드를 포트 8080에서 사용할 수 있습니다. 다음 스크린샷에서는 세 대의 MySQL 서버를 볼 수 있으며, 각 서버는 수많은 진행 중인 연결과 현재 상태에 대한 세부 정보를 보여줍니다. 포트 33062에서 수신 대기하던 노드가 이전에 18.97초 동안 잠깐 중단된 적이 있었던 것을 확인할 수 있습니다( DT 열에 보고됨).

NGINX Plus 라이브 활동 모니터링 대시보드를 사용하면 MySQL 노드의 부하를 분산할 때 서버 상태를 추적할 수 있습니다.
NGINX Plus 라이브 활동 모니터링 대시보드를 사용하면 MySQL 서버의 상태를 추적할 수 있습니다.

동시 쓰기에 대한 고려 사항

Galera Cluster는 각 MySQL 서버 노드를 읽기와 쓰기를 모두 수행하는 마스터 데이터베이스로 제공합니다. 많은 애플리케이션에서 읽기와 쓰기의 비율이 너무 커서 같은 테이블 행이 여러 클라이언트에 의해 동시에 업데이트될 위험이 멀티마스터 데이터베이스 클러스터에서 제공하는 유연성과 비교했을 때 완전히 허용 가능합니다. 동시 쓰기가 발생할 위험이 높은 상황에서는 두 가지 옵션이 있습니다.

  1. 읽기용과 쓰기용으로 각각 다른 포트에서 수신 대기하는 두 개의 별도 업스트림 그룹을 만듭니다. 클러스터에서 하나 이상의 노드를 쓰기에 전담하고, 모든 노드를 읽기 그룹에 포함합니다. 읽기 및 쓰기 작업에 적합한 포트를 선택하려면 클라이언트 코드를 업데이트해야 합니다. 이러한 접근 방식은 블로그의 NGINX Plus를 사용한 고급 MySQL 부하 분산 에서 설명되며, 많은 MySQL 서버 노드가 있는 확장성이 높은 환경에 적합합니다.
  2. 단일 업스트림 그룹을 유지하고 클라이언트 코드를 수정하여 쓰기 오류를 감지합니다. 쓰기 오류가 감지되면 동시성이 종료된 후 다시 시도하기 전에 코드가 기하급수적으로 후퇴합니다. 이러한 접근 방식은 블로그의 NGINX Plus와 Galera 클러스터를 활용한 MySQL 고가용성 에서 논의되고 있으며, 전용 클러스터 노드를 쓰기에 사용하면 고가용성이 저하되는 소규모 클러스터를 선호합니다.

요약

이 문서에서는 MySQL과 같은 TCP(또는 UDP) 애플리케이션의 부하 분산에 있어서 필수적인 측면에 대해 살펴보았습니다. NGINX Plus는 트래픽 유형에 관계없이 성능, 안정성, 보안 및 확장성을 갖춘 애플리케이션을 제공하는 데 도움이 되는 모든 기능을 갖춘 TCP/UDP 로드 밸런서를 제공합니다.

NGINX Plus를 사용해보려면 오늘 무료 30일 체험판을 시작하거나 저희에게 연락해 사용 사례에 대해 논의해 보세요.


부록: 테스트 환경 만들기

테스트 환경은 격리되어 있고 반복 가능하도록 가상 머신에 설치됩니다. 하지만 물리적인 "베어 메탈" 서버에 설치하지 못할 이유는 없습니다.

NGINX Plus 설치

NGINX Plus 관리자 가이드를 참조하세요.

MySQL용 Galera 클러스터 설치

이 예에서는 각 노드에 Docker 컨테이너를 사용하여 단일 호스트에 Galera Cluster를 설치합니다. 다음 지침은 Docker를 사용하여 Galera 시작하기 에서 가져온 것이며 Docker EngineMySQL 명령줄 도구가 모두 이미 설치되어 있다고 가정합니다.

  1. Docker 이미지에 의해 각 Galera 컨테이너에 복사될 기본 MySQL 구성 파일( my.cnf )을 만듭니다.

     

  2. Galera 기본 Docker 이미지를 가져옵니다.

    $ sudo docker pull erkules/galera:basic
    
  3. 첫 번째 Galera 노드( node1 )를 생성하고 기본 MySQL 포트를 33061로 노출합니다.

    $ sudo docker run -p 33061:3306 --detach=true --name node1 -h node1 erkules/galera:basic --wsrep 클러스터 이름=로컬 테스트 --wsrep 클러스터 주소=gcomm://
    
  4. 두 번째 Galera 노드( node2 )를 생성합니다. MySQL 포트는 33062로 노출되고 클러스터 간 통신을 위해 node1 에 연결됩니다.

    $ sudo docker run -p 33062:3306 --detach=true --name node2 -h node2 --link node1:node1 erkules/galera:basic --wsrep 클러스터 이름=로컬 테스트 --wsrep 클러스터 주소=gcomm://node1
    
  5. node2 와 같은 방법으로 세 번째이자 마지막 Galera 노드( node3 )를 생성합니다. MySQL 포트는 33063으로 노출되어 있습니다.

    $ sudo docker run -p 33063:3306 --detach=true --name node3 -h node3 --link node1:node1 erkules/galera:basic --wsrep 클러스터 이름=로컬 테스트 --wsrep 클러스터 주소=gcomm://node1
    
  6. 호스트에서 클러스터에 원격으로 액세스하는 데 사용할 수 있는 nginx 라는 사용자 계정을 만듭니다. 이는 Docker 컨테이너 자체 내에서 mysql(1) 명령을 실행하여 수행됩니다.

    $ sudo docker exec -ti node1 mysql -e"'nginx'@'172.17.0.1'에 대한 모든 권한을 'plus'로 식별하여 부여합니다."
    
  7. TCP 프로토콜을 사용하여 호스트에서 Galera 클러스터에 연결할 수 있는지 확인하세요.

    $ mysql --protocol=tcp -P 33061 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [경고] 명령줄 인터페이스에서 비밀번호를 사용하면 안전하지 않을 수 있습니다. +--------------------+ | Database | +-----------------+ | information_schema | | mysql | | performance_schema | +-----------------+
    
  8. 마지막으로, 다른 클러스터 노드에 대해 동일한 명령을 실행하여 nginx 사용자 계정이 복제되었고 클러스터가 올바르게 작동하고 있는지 확인합니다.

    $ mysql --protocol=tcp -P 33062 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [경고] 명령줄 인터페이스에서 비밀번호를 사용하면 안전하지 않을 수 있습니다. +--------------------+ | Database | +-----------------+ | information_schema | | mysql | | performance_schema | +-----------------+
    

"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."