728x90
반응형

net.netfilter.nf_conntrack_max

 

nf_conntrack은 ip_conntrack의 후속 커널 모듈로 netfilter가 네트워크에서 발생하는 커넥션에 대해 해당 내용을 기록하고 추적하기 위한 모듈이다.

일반적으로 활성화되지는 않지만 iptables를 이용한 NAT 환경 같은 경우에 사용되기도 한다.

특히 아래와 같은 경우에 사용자 모르게 활성화되어서 사용되는 경우가 있다.

 

  • iptables -t nat -L 같은 NAT 테이블 확인 명령을 한번이라도 수행한 경우

  • docker와 같이 iptables의 NAT 기능이 필요한 애플리케이션을 사용할 경우

 

단순히 해당 모듈이 활성화된다고 해서 문제가 되지는 않지만 접속량이 많은 네트워크 서비스를 제공하는 경우에는 nf_conntrack을 기본 값으로 사용할 경우 연결을 기록하는 테이블의 크기를 기본 값인 65536을 사용하기 때문에 문제가 발생할 수 있다.

따라서 해당 값을 서버의 환경에 맞추어 충분히 늘려주는게 좋다.

 

 

nf_conntrack_max

 

nf_conntrack_max는 nf_conntrack 모듈이 기록할 최대 연결 개수를 지정하는 파라미터이다.

기본 값은 65536이며 적당히 크게 잡아줘도 무방하지만 단순히 값을 크게 잡는 것이 좋지만은 않다.

 

아래는 Conntrack Hash Table이 어떻게 구현되어있는지에 대한 그림이다.

 

 

Hash Table의 각 구성 요소는 bucket이라는것으로 구성되어 있으며 Bucket은 내부적으로 연결 리스트로 구현되어 있다.

Table은 O(1)의 효율을 보여주지만 연결 리스트의 경우 O(n)의 효율을 보여주기 때문에 연결 리스트를 최소화하고 Hash Table의 크기를 키우는게 가장 좋아 보인다.

 

하지만 Hash Table을 크게 갖는 다는 것은 그만큼 정적으로 많은 메모리를 할당해서 사용해야하는 부담이 존재한다.

예를 들어 2097152개의 연결을 처리하기 위해 nf_conntrack_max 값을 2097152로 지정하고 Hash Table도 동일한 크기로 잡게 된다면 단순히 Hash Table을 구성하는데 아래와 같은 메모리 자원을 사용하게 된다.

 

  • 연결을 기록하는 각 엔트리의 크기는 308byte라고 한다.

  • 2097152(Hash 크기) * 308byte / 1048576(1MiB) = 616MB

  • 즉 600MB가 넘는 크기를 Hash Table 구성에만 낭비하게 된다.

 

따라서 단순히 Hash Table 크기 자체를 크게하기 보다는 연결 리스트를 적절히 활용하여 성능과 공간의 균형을 맞추는게 좋다.

 

 

Hash Table의 크기가 부족할 경우 Table이 full이라면 메시지와 함께 들어오는 패킷은 모두 Drop 되어버린다.

 

 

커널 2.6 버전을 기점으로 파라미터 이름이 변경되었다.

 

커널 문서에는 아래와 같이 nf_contrack_max 값을 정의하고 있다. (ip_contrack 시절에는 8배였다.)

 

 

여기에는 nf_conntrack_buckets는 bucket들의 개수이며 다시 말해 Hash Table의 크기를 의미한다.

커널 문서에는 Hash Table 크기의 4배로 Max 값을 지정하는걸 기본으로 하고 있기 때문에 각 Hash Table의 bucket은 4개의 노드를 갖는 연결 리스트로 구성된다.

따라서 앞서 살펴본 2097152개의 연결을 처리하는 경우로 살펴본다면 616MB의 크기는 154MB로 줄어들게 된다.

 

옛날의 ip_contrack 시절에는 Conntrack_Max 값에 대한 기본 값을 결정하는 요소에는 시스템 아키텍처도 변수로 작용했었다.

Conntrack_Max를 수식으로 표현하면 아래와 같다. (Link : https://wiki.khnet.info/index.php/Conntrack_tuning)

 

 

 

nf_conntrack_buckets

 

네트워크 관련 커널 파라미터 설정에는 정답이라는 것은 없다.

다만 무작정 nf_contrack_max를 키우기만 하면 연결 리스트에 대해 부하가 높아지기 때문에 nf_contrack_buckets 값도 같이 조절을 해 주어야 한다.

 

 

4GB 메모리에는 65536이 적합하지만 별도로 Hash 크기를 지정해서 모듈을 올리지 않는다면 16384 값으로 정해지게 된다.

이 상황에서 nf_contrack_max를 계속 키우게 되면 연결 리스트 길이만 계속 길어지기 때문에 바람직하지 않다.

 

최근 시스템들은 많은 메모리를 가지고 있기 때문에 65536개 이상의 연결을 충분히 처리할 수 있으며 Hash 크기에 대한 모듈 파라미터를 지정해서 수동으로 로딩하지 않고 docker나 iptables에 의해서 자동으로 로딩되므로 

이 값을 변경해 주어야 하는데 한 번 모듈이 올라가게 되면 sysctl을 이용해서 커널 파라미터 값을 변경할 수 없으며 sysfs를 통해서만 변경이 가능하다.

 

 

여기서 nf_conntrack 모듈을 지우고 내렸다가 다시 올린다면 연결되어 있던 네트워크 세션이 모두 끊어지기 때문에 모듈자체를 건드려서는 안된다.

 

 

Values 설정

 

요즘 시스템은 메모리가 넉넉하기 때문에 65536의 배수로 적당히 지정하고 거기에 맞추어 Hash 크기를 지정하는 것도 방법이다.

또는 과거 ip_conntrack 시절에 계산하던 방법을 차용하면 아래와 같이 계산해 볼 수 있다.

 

 

nf_conntrack_max, nf_conntrack_buckets 외에도 네트워크 서비스를 위해서 몇 가지 중요한 설정 값이 있다.

 

먼저 nf_conntrack_generic_timeout이 있는 Layer 4 기반 타임아웃 설정 값으로 기본 값은 600초로 되어 있다. 

이 값이 너무 길기 때문에 이를 적절히 (예를 들면 120) 낮춰주는게 좋다.

 

그리고 활성화된 연결에 대한 타임아웃 파라미터로 nf_conntrack_tcp_timeout_established이 있으며 기본 값은 4332000초 (5일) 이다.

이 값 또한 적당히 낮춰 주는게 좋다.

 

아래는 추천하는 설정이다.

 

예를 들어 32GB 메모리를 가지고 있는 64bit 시스템에 대해서는 아래와 같이 설정값을 추천할 수 있다.

다만 nf_conntrack_max는 필요에 따라 임의로 적정값을 직접 지정해도 무방하다.

특히, 연결 양이 많지 않은 네트워크 서비스 시스템에서 기본 값으로도 충분할 경우가 많다.

 

 

키                                                        계산                                            값

nf_conntrack_max                                32 * 1073741824 / 16384 / 2       1048576

nf_conntrack_buckets                           nf_conntrack_max / 4                   262144

nf_conntrack_generic_timeout                                                                 120

nf_conntrack_tcp_timeout_established                                                      54000

 

아래는 설정 스크립트이다.

#!/bin/bash
#
# nf-setup.sh: nf_conntrack configurator
#
# by lunatine
#
_arch=1
_max=0
_bucket=0
_gto=120
_tcp=54000

help() {
    cat << EOF
  $ nf-setup.sh [OPTIONS ...]

  Options:
    -m|--max   : nf_conntrack_max (default: memsize / 16384 / arch bit)
    -b|--bucket: nf_conntrack_buckets (default: nf_conntrack_max / 4)
    -g|--gto   : nf_conntrack_generic_timeout (default: 120)
    -t|--tcp   : nf_conntrack_tcp_timeout_established (default: 54000)
    -h|--help  : help message
EOF
}

# get arguments
while [[ -n $1 ]]
do
    case "$1" in
        # nf_conntrack_max
        -m|--max) _max=$1; shift 2;;
        -b|--bucket) _bucket=$1; shift 2;;
        -g|--gto) _gto=$1; shift 2;;
        -t|--tcp) _tcp=$1; shift 2;;
        -h|--help) help; exit 0;;
    esac
done

if [ "$(id -u)" -ne 0 ]; then
    echo "[Error] root privilege required ..."
    exit 1
else
    for modname in nf_conntrack nf_conntrack_ipv4
    do
        if [ "$(lsmod | grep -c $modname)" -eq 0 ]; then
            echo "[Error] No $modname module found"
            exit 1
        fi
    done
fi

# when nf_conntrack_max omitted
if [ "$_max" -eq 0 ]; then
    memtotal=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    [ "$(uname -m)" == "x86_64" ] && _arch=2
    _max=$(( memtotal * 1024 / 16384 / _arch ))
fi

# when nf_conntrack_buckets omitted
if [ "$_bucket" -eq 0 ]; then
    _bucket=$(( _max / 4 ))
fi

# asking for apply
CONF_VALUES="
[Applying]
---------------------------------------------------
  nf_connectrack_max                  : $_max
  nf_conntrack_generic_timeout        : $_gto
  nf_conntrack_tcp_timeout_established: $_tcp
  nf_conntrack hash size              : $_bucket
---------------------------------------------------
  are you sure? (Cancel: Ctrl+C) "
read -p "$CONF_VALUES" ans

# apply configurations
sed -i "/^net.netfilter.nf_conntrack_max/d" /etc/sysctl.conf
sed -i "/^net.netfilter.nf_conntrack_buckets/d" /etc/sysctl.conf
sed -i "/^net.netfilter.nf_conntrack_generic_timeout/d" /etc/sysctl.conf
sed -i "/^net.netfilter.nf_conntrack_tcp_timeout_established/d" /etc/sysctl.conf
{
    echo "net.netfilter.nf_conntrack_max = $_max"
    echo "net.netfilter.nf_conntrack_generic_timeout = $_gto"
    echo "net.netfilter.nf_conntrack_tcp_timeout_established = $_tcp"
} >> /etc/sysctl.conf
echo $_bucket > /sys/module/nf_conntrack/parameters/hashsize

# Result
cat << EOF
[Result]
---------------------------------------------------
  max                    : $(sysctl net.netfilter.nf_conntrack_max)
  hash size (buckets)    : $(sysctl net.netfilter.nf_conntrack_buckets)
  generic_timeout        : $(sysctl net.netfilter.nf_conntrack_generic_timeout)
  tcp_timeout_established: $(sysctl net.netfilter.nf_conntrack_tcp_timeout_established)
---------------------------------------------------
EOF

exit 0

 

 

참고

https://lunatine.net/2018/04/12/nf_conntrackgwa-docker/

https://medium.com/naver-cloud-platform/nf-conntrack-full%EB%A1%9C-%EC%9D%B8%ED%95%9C-packet-drop-%EB%8C%80%EC%9D%91-2586146e6714

https://blog.pages.kr/tag/nf_conntrack_max

https://jjinisystem.tistory.com/22

 

반응형

'Linux' 카테고리의 다른 글

Linux ReadLink  (0) 2020.10.25
Linux ulimit  (0) 2019.02.20
Linux Namespace  (0) 2018.12.17
Linux Service 만들기 (CentOS7)  (0) 2018.11.14
lvm의 기본 개념  (0) 2018.10.12
728x90
반응형

IP Protocol은 송신 시스템과 수신 시스템 사이의 패킷을 최적의 경로를 통해 전달하는것이 주된 목적이다. (단편화 시켜서 전달)

IP Protocol은 신뢰성이 없고 오류 발생 가능성이 높은 비연결성 Protocol이다. 

에러 발생 원인이나 진단 기능 및 상황 정보를 지원하지 않는데 이런 IP Protocol의 단점을 보완한 것이 ICMP Protocol이다.

 

즉, ICMP Protocol은 TCP/IP에서 IP 패킷을 처리할 때 발생되는 문제를 알려주는 프로토콜이다.

 

IP에는 오로지 패킷을 목적지에 도달시키기 위한 내용들로만 구성되어 있다. 따라서 정상적으로 목적지 호스트에 도달하는 경우에는 IP에서 통신이 성공하고 종료되므로 아무런 문제가 없다.

그러나, 만일 전달해야 할 호스트가 꺼져 있거나, 선이 단절된 경우와 같은 비정상적인 경우에 이 패킷전달을 의뢰한 출발지호스트에 이러한 사실을 알려야하지만, IP에는 그러한 에러에 대한 처리 방법이 명시되어있지 않다.

이러한 IP의 부족한 점을 메꾸기 위하여 사용되는 것이 바로 ICMP 이다

ICMP는 해당 호스트가 없거나, 해당 포트에 대기중에 서버프로그램이 없는 등의 에러상황이 발생할 경우 IP헤더에 기록되어 있는 출발지 호스트로 이러한 에러에 대한 상황을 보내주는 역할을 수행하게 된다.

(인터넷/통신 상에서 발생한 일반적인 상황에 대한 보고, 인터넷/통신 상에서 발생한 오류에 대한 보고)

물론 이 외에도 메시지를 제어하는 추가적인 기능들이 있다.

 

 

ICMP Layer

ICMP 프로토콜은 Network 계층에 속하며 IP 프토콜과 같이 사용한다.

 

 

ICMP 사용 명령어

 

1. Ping 명령 : 상대방 호스트의 작동 여부 및 응답시간 측정하는데 사용

  • Echo Request (ICMP 질의메시지 요청)

  • Echo Rely (ICMP 응답메시지 요청)

2. Tracert 명령 : 목적지까지의 라우팅 경로 추적을 하기위해 사용

  • Time Exceeded 확인 기능

 

 

 

ICMP 패킷 헤더 구조

 

 

ICMP는 기본적으로 4개의 헤더로 구성되어 있다.

  • ICMP Type : ICMP의 메시지를 구별

  • ICMP Code : 메세지 내용에 대한 추가 정보 (즉, ICMP Type에 대한 상세 정보)

  • ICMP CheckSum : ICMP의 값이 변조 여부를 확인

  • ICMP 메시지1, 메시지2 : ICMP Type에 따라 내용이 가변적으로 들어가는 내용

 

 

ICMP Type 

 

 

 

 

ICMP Test

 

ICMP TYPE - Request

 

ICMP구조에 따라 Type 8, Code 0, Checksum, Data를 확인 할 수 있음.

type이 Request 이기 때문에 메세지 1(data)에 특정값(abcdefg .. 이하 생략)이 채워져 있다.

 

 

 

 

ICMP TYPE - Reply  [응답이 정상적인 경우]

 

Type 0, Code 0, Checksum 도 정상적이다.

또 동일하게  Reply 이기 때문에 메세지 1(data)에 특정값으로 채워져 있는 것을 확인할 수 있다

 

 

 

ICMP TYPE - Destination Unreachable [응답이 비정상적인경우]

 

어떠한 이유로 인해 패킷이 정상적으로 도달하지 못하는 경우 이에 대한 응답메세지를 보내는데, Unreachable도 그 중 하나이다.

Type 3, Code 13, Checksum 도 정상적인데, 차이점은 메세지 2(data)에 실패한 패킷의 정보(IPV4, ICMP)가 담겨있는 것을 볼 수 있음.

 

 

참고 URL

https://m.blog.naver.com/PostView.nhn?blogId=lookie77&logNo=70024036178&proxyReferer=https:%2F%2Fwww.google.com%2F

http://www.firewall.cx/networking-topics/protocols/icmp-protocol/156-icmp-time-exceeded.html

http://www.firewall.cx/networking-topics/protocols/icmp-protocol/155-icmp-redirect-message.html

http://www.firewall.cx/networking-topics/protocols/icmp-protocol/152-icmp-echo-ping.html

https://run-it.tistory.com/31

 

반응형

'Network' 카테고리의 다른 글

Load Balancing  (0) 2018.12.28
Forward Proxy and Reverse Proxy  (0) 2018.12.19
라우터 초기화 방법  (0) 2018.08.14
NAT와 NAPT  (0) 2018.08.14
IP Masquerade (IP 마스커레이드)  (0) 2018.08.14
728x90
반응형

Binary Data를 Text로 바꾸는 Encoding(Binary-to-Text Encoding schemes)의 하나로써 Binary Data를 Character Set에 영향을 받지 않는 공통 ASCII 영역의 문자로만 이루어진 문자열로 바꾸는 Encoding이다.

Base64를 글자 그대로 직역하면 64진법이라는 뜻이다. 64진법은 컴퓨터한테 특별한데 그 이유는 64가 2의 제곱수 64=2^6이며 2의 제곱수에 기반한 진법 중 화면에 표시되는 ASCII 문자들로 표시할 수 있는 가장 큰 진법이기 때문이다. 

(ASCII에는 제어문자가 다수 포함되어 있기 때문에 화면에 표시되는 ASCII 문자는 128개가 되지 않는다.)

 

핵심은 Base64 Encoding은 Binary Data를 Text로 변경하는 Encoding이다.

 

 

8비트를 6비트로

 

Base64 인코딩은 8비트 바이트들의 연속을 받아서 6비트의 조각의 연속으로 쪼갠 뒤, 각 6비트 조각을 Base64 알바펫으로 이루어진 64개의 글자들 중 하나에 할당한다.

64개의 가능한 출력 글자들은 일반적이고 HTTP 헤더 필드에 놓여도 안전한 것들이다.

64 문자들은 대문자, 소문자, 숫자, +, / 를 포함한다. 특별한 문자 '=' 또한 사용된다.

base64 인코딩이 6비트의 정보를 표현하기 위해 8비트 문자를 사용하기 때문에 base64로 인코딩된 문자열은 원래 값보다 33% 더 커진다

(6bit당 2bit의 Overhead가 발생하기 때문이다.)

 

 

 

base64를 사용하는 이유

 

문자를 전송하기 위해 설계된 Media(Email, HTML)를 이용해 플랫폼 독립적으로 Binary Data(이미지나 오디오)를 전송할 필요가 있을 때, ASCII로 Encoding하여 전송하게 되면 여러가지 문제가 발생할 수 있다.

대표적인 문제는 아래의 2가지이다.

  • ASCII는 7bits Encoding인데 나머지 1bit를 처리하는 방식이 시스템별로 상이하다.

  • 일부 제어문자(Ex. Line Ending)의 경우 시스템 별로 다른 코드값을 갖는다.

 

위와 같은 문제로 ASCII는 시스템간 데이터를 전달하기에 안전하지가 않다.

Base64는 ASCII 중 제어문자와 일부 특수문자를 제외한 64개의 안전한 출력 문자만 사용한다.

(안전한 출력문자는 문자 코드에 영향을 받지 않는 공통 ASCII를 의미한다.)

 

즉, Base64는 HTML 또는 Email과 같이 문자를 위한 Media에 Binary Data를 포함해야 될 필요가 있을때, 포함된 Binary Data가 시스템 독립적으로 동일하게 전송 또는 저장되는걸 보장하기 위해 사용한다.

 

 

 

참고

https://effectivesquid.tistory.com/entry/Base64-%EC%9D%B8%EC%BD%94%EB%94%A9%EC%9D%B4%EB%9E%80

 

반응형

'Tech' 카테고리의 다른 글

Redis Cluster Proxy (Predixy)  (0) 2020.05.25
Redis Cluster  (0) 2020.05.25
Kafka (카프카)  (0) 2019.04.09
728x90
반응형

SNI(Server Name Indication)란 서버명 지정이라는 뜻이다.

TLS 프로토콜 확장형이고 네트워크을 통하여 TCP 통신을 수행할 시에 SSL/TLS Handshake 과정을 거치는데 이때 Handshake 과정의 시작점에서 웹브라우저에게 호스트명(HTTP Header에 삽입)을 정해준다.

이 방식을 통하여 동일 서버에서 여러개의 SSL 통신이 가능하게 된다. (이 말은 곧 여러 웹 사이트의 SSL 통신이 하나의 443 포트로 가능해진다는 얘기이다.)

몇몇 브라우저 및 기기의 접속 제한이 있지만 이를 무시할 수 있을 경우 하나의 서버에서 여러 사이트의 SSL(HTTPS)를 단일 443 포트로 연결할 수 있다.

 

작동 방식

 

 

작동 방식은 아래와 같다.

  1. 웹 서버에 브라우저가 HTTPS 접속할시에 SSL/TLS 접속을 하게 되며 브라우저는 피싱사이트가 아닌 진짜 싸이트에 접속된것인지 여부를 알 수 있도록 서버에서 SSL 인증서를 가져오게 된다.

  2. 브라우저는 가져온 인증서를 가지고 접속하고자하는 도메인 주소와 인증서의 이름을 비교하게 되며 만일 이름이 일치하면 보안접속이 이루어지게 된다. (아닐 경우 경고 메시지를 불러온다.)

  3. 보통 하나의 서버에 여러개의 도메인을 서비스(상황에 따라 다름)하므로 IP만 가지고 이를 판단하게 되면 서버상의 하나의 도메인만 SSL 통신이 가능하게 되므로 SNI를 사용한다.

    (즉, 하나의 서버상에 여러 도메인을 서비스하는 경우 SNI가 있다면 SSL 통신이 가능해진다.)

 

 

모든 서버나 클라이언트가 다 지원을 하는 것이 아니고 구형 브라우저나 서버 프로그램은 지원하지 않는다.

SNI 필드에 대한 암호화는 TLS 1.3부터 지원한다.

 

 

참고

https://sarc.io/index.php/miscellaneous/1593-sni-server-name-indication

https://blog.naver.com/ncloud24/221255979858

https://brunch.co.kr/@sokoban/22

 

반응형

'Web' 카테고리의 다른 글

HAProxy & Keepalived  (1) 2020.01.29
HAProxy  (2) 2020.01.17
Keepalived & VRRP  (0) 2020.01.17
부하분산 테스트 설명 및 용어  (0) 2019.04.09
PinPoint  (0) 2019.04.09
728x90
반응형

심볼릭 링크가 연결되어 있는 원본 파일을 찾는 명령어이다.

readlink는 심볼릭링크인 path가 가르키는 원본의 파일 이름을 돌려준다. 알아낸 원본 파일을 이름은 buffer에 저장된다.

bufsize는 buffer의 저장 크기이다. 만약 buffer의 크기가 원본 파일의 이름을 담기에 충분히 크지 않다면 나머지 부분은 잘리게 된다.

readlink는 원본 파일의 완전한 경로를 가져온다.

 

사용법

# readlink -{Option} File

 

Example

 

# ls -al test.txt

lrwxrwxrwx 1 root root 13 Oct 25 14:38 test.txt -> /tmp/test.txt

 

# readlink -f test.txt

/tmp/test.txt

 

Options

-f (canonicalize)

  • 주어진 이름의 모든 구성 요소에 있는 모든 심볼릭 링크를 따라가면서 정규화한다. 마지막 구성 요소를 제외하고 모두 존재해야한다.

 

-e (canonicalize-existing)

  • 주어진 이름의 모든 구성 요소에 있는 모든 심볼릭 링크를 따라가며 정규화 한다. 마지막 구성 요소를 제외하고 모두 존재해야한다.

 

-m (canonicalize-missing) 

  • 구성 요소 존재에 대한 요구 사항없이 주어진 이름의 모든 구성 요소에 있는 모든 심볼릭 링크를 따라 정규화한다.

 

 

Return

 

성공할 경우 Buffer에 들어 있는 문자의 갯수가 반환되며 에러가 발생했다면 -1 Return되며 적당한 Error 코드가 설정된다.

 

 

Error

- ENOTDIR

경로가 디렉토리(:12)가 아니다.

 

- EINVAL

bufsize 가 양수가 아닐경우

 

- ENAMETOOLONG

경로이름이 너무 길경우

 

- ENOENT

명명된 파일이 존재하지 않을경우

 

- EACCES

path 에 접근하기 위한 디렉토리 권한이 없을경우

 

- EINVAL

파일이 심볼릭 링크가 아닐때

 

 

참고

https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_readlink

https://www.joinc.co.kr/w/man/2/readlink

 

 

반응형

'Linux' 카테고리의 다른 글

nf_conntrack_max  (1) 2020.10.25
Linux ulimit  (0) 2019.02.20
Linux Namespace  (0) 2018.12.17
Linux Service 만들기 (CentOS7)  (0) 2018.11.14
lvm의 기본 개념  (0) 2018.10.12
728x90
반응형

Predixy

 

오픈소스로 Redis 센티넬 및 Redis 클러스터를 위한 고성능 및 모든 기능을 갖춘 프록시이다.

 

 

 

 

Install & Config

 

github :  https://github.com/joyieldInc/predixy

 

 

Install

1. Package Install

# apt install -y build-essential

 

2. git clone

#  git clone https://github.com/joyieldInc/predixy.git

 

3. Build

# cd predixy

# make

 

4. copy command

# cd src/predixy /usr/local/bin/

# predixy -h

 

 

Config

1. predixy config

# vim conf/predixy.conf

Bind 0.0.0.0:7617

WorkerThreads 4

MaxMemory 0

ClientTimeout 300

BufSize 4096

LogRotate 1d

LogVerbSample 0

LogDebugSample 0

LogInfoSample 10000

LogNoticeSample 1

LogWarnSample 1

LogErrorSample 1

Include cluster.conf

Include latency.conf

 

2. cluster config

# vim conf/cluster.conf

ClusterServerPool {

    MasterReadPriority 60

    StaticSlaveReadPriority 50

    DynamicSlaveReadPriority 50

    RefreshInterval 1

    ServerTimeout 1

    ServerFailureLimit 10

    ServerRetryTimeout 1

    KeepAlive 120

    Servers {

        + 192.168.50.14:6379

        + 192.168.50.15:6379

        + 192.168.50.16:6379

        + 192.168.50.14:6380

        + 192.168.50.15:6380

        + 192.168.50.16:6380

        + 192.168.50.14:6381

        + 192.168.50.15:6381

        + 192.168.50.16:6381

    }

}

 

3. run predixy

# predixy conf/predixy.conf

predixy conf/predixy.conf

2020-05-25 13:44:29.123965 N Proxy.cpp:112 predixy listen in 0.0.0.0:7617

2020-05-25 13:44:29.124092 N Proxy.cpp:143 predixy running with Name:PredixyExample Workers:4

2020-05-25 13:44:29.124417 N Handler.cpp:456 h 0 create connection pool for server 192.168.50.15:6380

2020-05-25 13:44:29.124436 N ConnectConnectionPool.cpp:42 h 0 create server connection 192.168.50.15:6380 8

2020-05-25 13:44:29.125279 N ClusterServerPool.cpp:174 redis cluster create new group 29c5855872ff38f9f2a4203094fc4928fec7744f 192.168.50.15:6379@16379 master -

2020-05-25 13:44:29.125298 N ClusterServerPool.cpp:174 redis cluster create new group b1e905ca7d3fb5f743930565994aef6eb48cfe95 192.168.50.16:6379@16379 master -

2020-05-25 13:44:29.125308 N ClusterServerPool.cpp:174 redis cluster create new group 186deee637ea73f136d671f674320b424c3dddf4 192.168.50.14:6379@16379 master -

~~~~~~~~~~~~~~~~~~~~~~~~

 

Test

1. Client Get Query

# redis-cli -p 7617 -h 192.168.50.13 get hello

"world"

predixy가 실행중인 서버에서 hello key의 world value를 리턴하였다.

 

만약 predixy가 없는 상황이라면 redis cluster에 직접 질의를 해야하고 보통의 구조에서는 하나의 Master에서 모든 요청을 처리하기때문에 부하가 발생할 수 있다.

 

 

2. Log 

2020-05-25 13:46:37.252826 N Handler.cpp:456 h 3 create connection pool for server 192.168.50.14:6380

2020-05-25 13:46:37.252874 N ConnectConnectionPool.cpp:42 h 3 create server connection 192.168.50.14:6380 40

2020-05-25 13:46:38.012174 N Handler.cpp:373 h 1 accept c 192.168.50.10:35870 42 assign to h 0

2020-05-25 13:46:38.012860 N Handler.cpp:212 h 0 remove c 192.168.50.10:35870 42 with status 2 End

192.168.50.10에서 온 요청을 redis master 중 요청한 key를 가진 slot이 있는 master (192.168.50.14) 로 전달하였다.

 

 

3. redis latency check (predixy support)

# redis-cli -p 7617 -h 192.168.50.13 INFO Latency all

# LatencyMonitor

LatencyMonitorName:all

<=          100                   36                3 100.00%

T            12                   36                3

 

# ServerLatencyMonitor

ServerLatencyMonitorName:192.168.50.14:6379 all

ServerLatencyMonitorName:192.168.50.15:6379 all

ServerLatencyMonitorName:192.168.50.16:6379 all

ServerLatencyMonitorName:192.168.50.14:6380 all

ServerLatencyMonitorName:192.168.50.15:6380 all

ServerLatencyMonitorName:192.168.50.16:6380 all

ServerLatencyMonitorName:192.168.50.14:6381 all

ServerLatencyMonitorName:192.168.50.15:6381 all

ServerLatencyMonitorName:192.168.50.16:6381 all

 

predixy에서 지원하는 기능 중 하나로 각 Redis node에 대한 Latency를 확인할 수 있다.

 

Redis Cluster 생성은 아래 링크

https://lascrea.tistory.com/214

 

 

참조 URL

 

 

 

반응형

'Tech' 카테고리의 다른 글

Base64 Encoding  (0) 2020.10.25
Redis Cluster  (0) 2020.05.25
Kafka (카프카)  (0) 2019.04.09
728x90
반응형

Redis Cluster

 

레디스는 단일 인스턴스만으로도 운영이 가능하지만 물리 머신이 가진 메모리의 한계를 초과하는 데이터를 저장하고 싶거나 failover에 대한 처리를 통해 HA를 보장하려면 센티널이나 클러스터 등의 운영 모드를 선택해서 사용해야 한다.

 

 

운영 모드 (Operation Modes)

단일 인스턴스 (Single Instance)

HA를 지원하지 않는다.

 

 

센티널 (Sentinel)

HA를 지원하며 Master/Slave Replication 구조를 가진다.

Sentinel Process는 Redis와 별도의 Process로 동작하며 여러개의 독립적인 Sentinel Process들이 협동하여 운영된다 (SPOF 아님)

안정적인 운영을 위해서는 최소 3개 이상의 Sentinel Instance가 필요하며 FailOver를 위해 과반수 이상의 Vote가 필요하다.

 

Redis Process가 실행되는 각 서버마다 각각 Sentinel Process를 띄어놓거나 Redis에 접근하는 Application Server에 Sentinel Process를 띄어놓고 사용한다. (여러가지 구성이 가능)

지속적으로 Master/Slave가 제대로 동작하는지 모니터링하다가 Master에 문제가 감지되면 자동으로 FailOver를 수행한다.

Application에서 Sentinel에서 Master가 어떤 Instance인지 확인하여 Master 접근하는 방식을 사용한다면 Master 장애로 FailOver시 Application에서도 자동으로 Master 변경이 가능하다.

즉, Application에서 Sentinel에 연결하여 현재 Master를 조회하는 방식이다.

 

 

클러스터 (Cluster)

HA와 Sharding을 지원한다. Sentinel과 동시에 사용하는거이 아니라 완전히 별도의 솔루션이다.

DataSet을 자동으로 여러 노드에 나눠서 저장해준다. Redis Cluster 기능을 지원하는 Client를 써야만 Data Access 시에 올바른 노드로 Redirect가 가능하다.

 

Cluser Node들은 기본설정인 6379 포트를 Data Port로 사용하며 Cluster는 Cluster Bus Port를 사용하여 자체적인 바이너리 프로토콜을 통해 Node to Node 통신을 한다. (보통 Cluster Bus Port는 6379에 10000을 더한 16379 Port를 사용한다.)

이 Cluster Bus Port를 통해 Node 간 Failure Detection, Configuration Update, FailOver Authorization 등을 수행한다.

 

Sharding은 최대 1000개의 Node로 Sharding해서 사용가능하며 그 이상은 권장하지 않는다. Consistent Hashing을 사용하지 않는 대신 Hash Slot이라는 개념을 도입하여 사용한다.

Hash Slot을 결정하는 방법으로는 CRC16(key) Mod 13684로 CRC16을 이용하면 16384개의 Slot에 균일하게 잘 분배된다.

노드별 자유롭게 Hash Slot을 할당 가능하다.

예를 들면 A Node에 0개부터 5500개, B Node에 5501개부터 11000개, C Node에 11001개부터 16383개를 할당할 수 있다. 

운영중단 없이 Hash Slot을 다른 노드로 이동시키는것이 가능하며 노드를 추가하거나 삭제하고 노드별 Hash Slot 할당량을 조절할 수 있다. (노드의 개수에 따라 Slot할당을 다르게 가져갈 수 있다.)

 

Multiple Key Operations를 수행하려면 모든 키값이 같은 Hash Slot에 들어와야 한다.

이를 보장하기 위해 Hash Tag라는 개념을 도입하여 사용한다. {} 안에 있는 값으로만 Hash 계산을 한다. ({foo}_mykey}, {foo}_your_key})

 

Fail Over를 위해 클러스터의 각 노드를 N대로 구성 가능하다. Master 1대 / Slave Replication (N대) 구성이다.

Async Replication (Master -> Slave Replication 과정에서 Ack를 받지 않는다.) 즉, 데이터를 보내지면 정확히 도착하였는지에 대한 응답을 받지않는 것이다.

그렇기 때문에 데이터 손실 가능성이 존재한다. (Master가 Client 요청을 받아서 Ack를 완료한 후 받은 Client의 요청을 Slave Replication으로 보내기전에 Master가 죽는 경우가 존재한다.)

 

Redis Client는 Cluster 내의 어떤 노드든 쿼리를 날려도 된다. (Slave에 쿼리를 날리는 것도 가능하다. 예를 들어 GET my_key 와 같은 쿼리)

그렇게 되면 쿼리를 받은 노드가 해당 쿼리를 분석하고 해당 키를 자신이 갖고 있다면 바로 찾아서 리턴하고 그렇지 않은 경우 해당 키를 저장하고 있는 노드의 정보를 리턴한다.

Client는 전달받은 노드의 정보로 다시 쿼리를 보내 요청한 쿼리에 대한 정보를 전달받는다. (리턴은 MOVED 3999 127.0.0.1:6381 형식으로 응답받는데 이 내용으로 다시 쿼리를 보낸다.)

 

 

 

메모리 동작 방식

Key가 만료되거나 삭제되어 Redis가 메모리를 해제하더라도 OS에서 해제된 분량만큼 바로 메모리가 확보되지는 않는다. (꼭 Redis만 해당되는 얘기는 아님)

예를 들어 5GB는 3GB의 데이터를 메모리에서 해제하더라도 여전히 OS 메모리 사용량은 5GB로 잡혀있다.

하지만 다시 Data를 추가하면 Logically Freed된 영역, 즉 해제되었지만 OS에 반환되지 않은 영역에 할당되므로 물리적인 5GB를 넘지는 않는다.

 

따라서 Peak Memory Usage를 기준으로 Memory를 잡아야한다. 예를 들어 5GB 사용하고 가끔 10GB가 필요한 경우가 있다면 10GB 이상을 가진 머신이 필요한것이다.

MaxMemory 설정을 해두는 것이 좋은데 설정을 하지 않으면 Memory를 무한히 사용하다가 OOM이나 머신에 장애가 발생할 가능성이 높아진다.

 

MaxMemory 설정시의 Eviction Policy (Memory 해제 정책) 는 아래와 같다.

  • No-Eviction : 추가 Memory를 사용하는 Write Command에 대해 Error를 리턴

  • Allkeys-LRU : 전체 아이템 중에서 LRU (페이지 교체 알고리즘으로 가장 마지막에 사용된 페이지를 새로운 페이지로 교체하는 방식)

  • Volatile-LRU : Expire 되는 아이템 중에서 LRU

  • Volatile-TTL : Expire 되는 아이템 중 TTL이 얼마 안남은 순으로 교체

 

RDB Persistence를 사용한다면 MaxMemory를 실제 가용 Memory의 45% 수준으로 설정하는 것을 권장한다. Snapshot을 찍을때 현재 사용중인 Memory 양만큼 필요하기 때문이다. (5%는 오버헤드에 대비하기 위해 여유분)

만약 RDB Persistence를 사용중이지 않다면 95%정도로 사용하여도 된다.

 

동일 Key-Value Data를 저장한다고 가정했을 때 Cluster Mode를 사용할 경우 Single Instance 보다 1.5 ~ 2배 정도 Memory를 더 사용하는 것에 주의해야 한다.

Redis Cluster의 경우 내부적으로 Cluster안에 저장된 Key를 Hash Slot으로 Mapping하기 위한 Table을 가지고 있기 때문에 추가적인 Memory OverHead가 발생한다.

이 때문에 Key의 숫자가 많아질수록 Memory OverHead가 더 빈번히 발생한다. 4버전 부터는 이전 버전보다 Memory 최적화 기능이 포함되어 Memory를 더 적게 사용하지만 여전히 Single Instance보다 많은 Memory를 필요로 한다.

 

 

 

주요 특수 기능

다양한 데이터 구조 지원

단순히 Key - Value 문자열만 저장하는 것이 아니라 고수준의 데이터 구조를 사용가능 하다. 

Ex) Hash, Set, List, SortedSet, ETC

 

Hash에는 HSET(key, fileds, value), HGET(key,filed)가 있다.

Web Application에서 특정 유저 ID를 Key로 두고 해당 유저의 세부 정보 (Name, Email 등)를 filed로 둔다.

이렇게 하면 특정 유저와 관련된 정보들을 한번에 삭제하는 등 하나의 Namespace처럼 사용할 수있다.

Hash Key당 1000개 정도의 field까지는 Redis가 zipmap을 이용해 압축해서 저장한다. 즉, 1000개의 Key에 저장될 내용이 하나의 Key에 1000개의 field로 저장되는 것이다.

하지만 CPU 사용량이 증가하므로 Application 특성에 맞게 적정 갯수를 잘 선택해야 한다.

(Link :  https://instagram-engineering.com/storing-hundreds-of-millions-of-simple-key-value-pairs-in-redis-1091ae80f74c /  https://redis.io/topics/memory-optimization)

 

 

Expireation 지정

Key 별로 TTL(Time-To-Live)을 설정해두면 Redis가 알아서 해당시점이 지날때 Key를 삭제한다. 설정된 Max Memory에 도달하면 Expire되지 않은 Key들도 Eviction Policy에 따라 삭제될 수 있다.

 

 

Pipelining

여러 Command들을 한번에 모아서 보낸후 실행 결과도 한번에 모아서 받을 수 있다. Latency가 큰 네트워크 환경이라면 Command 하나당 한번의 Request/Response를 할 때보다 Throughput(처리량)을 올릴 수 있다.

 

 

Pub/Sub (출판/구독 모델)

하나의 Client가 같은 채널에 연결된 다른 Client들에게 메시지를 보내는 것이 가능하다. 이를 이용하여 속도가 빠른 메시지 브로드캐스터 혹은 메시지 큐처럼 사용하는 것이 가능하다.

 

 

Lua Scripting

여러 명령어들이 사용되는 복잡한 작업을 할 때 (특히 트랜잭션이 사용되는 경우) 이를 하나의 Lua Script로 만들어서 사용할 수 있다.

Script는 Automic하게 실행되기 때문에 Optimistic Locking Transactions를 사용할 필요가 없다.

 

 

 

지표 모니터링 (Monitoring Metrics)

Total Commands Processed

이 지표와 Request/Response를 Latency Logging해두면 Command 처리량 대비 Latency를 알 수 있다.

# redis-cli -h {host} -p {port} --latency

 

 

Slow Commands

SlowLog Get 명령으로 Slow Command를 확인 가능하다.

 

 

Client Connections

info clients 명령으로 확인 가능하다.

대략 5000개 이상의 Connection들이 존재한다면 응답시간이 느려지고 있을 가능성이 있으니 모니터링 해야한다.

Maxclients 값을 대략 예상되는 Connection 숫자의 1.1 ~ 1.5배 정도로 설정해 두면 안전하다.

 

 

Mem Fragmentation Ratio

정의 = 실제 물리 메모리 사용량 / 레디스의 메모리 사용량 (나누기)

실제 물리 메모리 사용량에서 레디스의 메모리 사용량으로 나누었을 때로 예를 들어 물리 메모리가 10GB이고 레디스 메모리 사용량이 10GB인 경우는 10/10이므로 값이 1이 된다.

물리 메모리가 10GB이고 레디스 메모리 사용량이 8GB인 경우 값은 1.25가 되는 것이다.

 

1이면 이상적인 상태이나 이 값보다 커진다면 파편화가 심화된다.

1.5가 넘는다면 서버 재시작을 권장한다.

1보다 작은 경우 레디스의 메모리 사용량이 실제 물리 메모리 사용량보다 큰 경우여서 OS에 의해 메모리의 일부가 Swap되어 디스크로 이동되었을때 나타난다. (디스크 사용으로 성능저하가 발생하므로 물리 메모리 증설 권장)

 

 

 

Redis Cluster Install

Environment

 

OS : Ubuntu 18.04

Redis Version : 5.0.8

Machine : 3EA (192.168.50.14 ~ 16)

 

 

Redis Port : 6379, 6380, 6381

Redis Cluster Port : 16379 

 

 

Redis Install

Ubuntu 20.04에서는 기본적으로 Redis 5버전이 설치되지만 Ubuntu 18.04에서 Redis 4버전이 설치되므로 Redis 5버전을 설치하려면 별도의 Repository가 필요하다.

 

1. Add Repository

# add-apt-repository ppa:chris-lea/redis-server

# apt update

 

2. Redis 5 Install

# apt install -y redis-server

 

 

Create Redis Cluster

1. Default redis conf setting 

# vim /etc/redis/redis.conf

bind 0.0.0.0

cluster-enabled yes

cluster-config-file nodes-6379.conf

cluster-node-timeout 15000

appendonly yes

 

# cp /etc/redis/redis.conf /etc/redis/redis-6380.conf

#  vim /etc/redis/redis-6380.conf

port 6379 => port 6380

pidfile /var/run/redis/redis-server.pid => pidfile /var/run/redis/redis-6380.pid

cluster-config-file nodes-6379.conf => cluster-config-file nodes-6380.conf

 

# cp /etc/redis/redis.conf /etc/redis/redis-6381.conf

# vim /etc/redis/redis-6381.conf

port 6379 => port 6381

pidfile /var/run/redis/redis-server.pid => pidfile /var/run/redis/redis-6381.pid

cluster-config-file nodes-6379.conf => cluster-config-file nodes-6381.conf

 

# systemctl restart redis-server

# redis-server /etc/redis/redis-6380.conf

# redis-server /etc/redis/redis-6381.conf

 

# redis-cli -p 6379 ping

PONG

# redis-cli -p 6380 ping

PONG

# redis-cli -p 6381 ping

PONG

3대의 머신에 동일하게 설정하여 준다.

 

2. Create Cluster

6379는 Master Port, 6380은 Slave 1 Port, 6381은 Slave 2 Port 이다.

각 Cluster의 Repilcas를 2로 하여 Master 3대, Slave 6대로 구성하였다.

 

# redis-cli --cluster create \

192.168.50.14:6379 \

192.168.50.15:6379 \

192.168.50.16:6379 \

192.168.50.14:6380 \

192.168.50.15:6380 \

192.168.50.16:6380 \

192.168.50.14:6381 \

192.168.50.15:6381 \

192.168.50.16:6381 \

--cluster-replicas 2

 

 

3. Cluster Node Check

# redis-cli -p 6379 cluster nodes

d8aacf656c4a9c67708ab89d3038736cbe1cc364 192.168.50.14:6380@16380 slave b1e905ca7d3fb5f743930565994aef6eb48cfe95 0 1590331467684 4 connected

4d51c878f3ff425d5423e32e6c16c1076b27200d 192.168.50.16:6381@16381 slave 29c5855872ff38f9f2a4203094fc4928fec7744f 0 1590331464397 9 connected

186deee637ea73f136d671f674320b424c3dddf4 192.168.50.14:6379@16379 myself,master - 0 1590331466000 1 connected 0-5460

d5d6f86145f27daabd587601e0daf452900699fc 192.168.50.15:6381@16381 slave b1e905ca7d3fb5f743930565994aef6eb48cfe95 0 1590331468000 8 connected

29c5855872ff38f9f2a4203094fc4928fec7744f 192.168.50.15:6379@16379 master - 0 1590331470302 2 connected 5461-10922

cb508abdf02d27281b1f73019ce4b2529a6391ef 192.168.50.14:6381@16381 slave 29c5855872ff38f9f2a4203094fc4928fec7744f 0 1590331466275 7 connected

9465e7a7d8420a6abae8c01e37ad50575d06141a 192.168.50.15:6380@16380 slave 186deee637ea73f136d671f674320b424c3dddf4 0 1590331466577 5 connected

bb77d55e06f3e99b4ada111085f678b791d36dc7 192.168.50.16:6380@16380 slave 186deee637ea73f136d671f674320b424c3dddf4 0 1590331469295 6 connected

b1e905ca7d3fb5f743930565994aef6eb48cfe95 192.168.50.16:6379@16379 master - 0 1590331467281 3 connected 10923-16383

 

각 노드별 slot이 분배된 것을 확인할 수 있다.

 

# redis-cli -p 6379 cluster info

cluster_state:ok

cluster_slots_assigned:16384

cluster_slots_ok:16384

cluster_slots_pfail:0

cluster_slots_fail:0

cluster_known_nodes:9

cluster_size:3

cluster_current_epoch:9

cluster_my_epoch:1

cluster_stats_messages_ping_sent:109

cluster_stats_messages_pong_sent:102

cluster_stats_messages_sent:211

cluster_stats_messages_ping_received:94

cluster_stats_messages_pong_received:109

cluster_stats_messages_meet_received:8

cluster_stats_messages_received:211

 

클러스터의 상태 정보를 볼 수 있다.

 

# redis-cli -c -p 6379

127.0.0.1:6379> cluster keyslot test:1

(integer) 10491

127.0.0.1:6379> cluster keyslot test:2

(integer) 6296

127.0.0.1:6379> cluster keyslot test:3

(integer) 2233

127.0.0.1:6379> cluster keyslot test:4

(integer) 14430

127.0.0.1:6379> cluster keyslot test:5

(integer) 10367

127.0.0.1:6379>

 

cluster keyslot 명령어를 실행해보면 해당 키가 어떤 슬롯에 저장되는지 확인할 수 있다.

 

4. Inster Data

# redis-cli -c -p 6379

set hello world

-> Redirected to slot [866] located at 192.168.50.14:6379

OK

 

hello라는 key에 world라는 value를 넣고 redis에 set하였다. hello라는 key는 866 slot에 저장되며 노드는 1번인 192.168.50.14 서버의 redis에 저장된다.

 

# redis-cli -c -p 6379

127.0.0.1:6379> get hello

-> Redirected to slot [866] located at 192.168.50.14:6379

"world"

 

다른 서버에서 질의시 해당 서버는 자신이 가지고 있는 slot이 아니면 client에게 slot을 가진 서버를 리다이렉트하도록 전달한다.

 

5. Fail Over

hello (key slot 866) 라는 key를 가진 1번 (192.168.50.14) Master의 머신을 종료하여 다른 머신에 있는 Slave가 Master로 승격되는지 테스트를 진행한다.

 

# poweroff 

 

노드의 상태를 확인한다.

 

# redis-cli -p 6379 cluster nodes

d8aacf656c4a9c67708ab89d3038736cbe1cc364 192.168.50.14:6380@16380 slave,fail? b1e905ca7d3fb5f743930565994aef6eb48cfe95 1590332380344 1590332379219 3 connected

cb508abdf02d27281b1f73019ce4b2529a6391ef 192.168.50.14:6381@16381 slave,fail? 29c5855872ff38f9f2a4203094fc4928fec7744f 1590332380344 1590332377000 2 connected

29c5855872ff38f9f2a4203094fc4928fec7744f 192.168.50.15:6379@16379 master - 0 1590332393000 2 connected 5461-10922

d5d6f86145f27daabd587601e0daf452900699fc 192.168.50.15:6381@16381 slave b1e905ca7d3fb5f743930565994aef6eb48cfe95 0 1590332392703 8 connected

b1e905ca7d3fb5f743930565994aef6eb48cfe95 192.168.50.16:6379@16379 myself,master - 0 1590332395000 3 connected 10923-16383

bb77d55e06f3e99b4ada111085f678b791d36dc7 192.168.50.16:6380@16380 slave 186deee637ea73f136d671f674320b424c3dddf4 0 1590332393750 1 connected

9465e7a7d8420a6abae8c01e37ad50575d06141a 192.168.50.15:6380@16380 slave 186deee637ea73f136d671f674320b424c3dddf4 0 1590332394813 5 connected

186deee637ea73f136d671f674320b424c3dddf4 192.168.50.14:6379@16379 master,fail? - 1590332379521 1590332377000 1 connected 0-5460

4d51c878f3ff425d5423e32e6c16c1076b27200d 192.168.50.16:6381@16381 slave 29c5855872ff38f9f2a4203094fc4928fec7744f 0 1590332395846 2 connected

 

머신 1(192.168.50.14)번에 있던 master와 slave가 fail 상태인 것을 확인할 수 있다.

 

#  redis-cli -p 6379 cluster info

cluster_state:ok

cluster_slots_assigned:16384

cluster_slots_ok:16384

cluster_slots_pfail:0

cluster_slots_fail:0

cluster_known_nodes:9

cluster_size:3

cluster_current_epoch:11

cluster_my_epoch:3

cluster_stats_messages_ping_sent:1101

cluster_stats_messages_pong_sent:1115

cluster_stats_messages_meet_sent:4

cluster_stats_messages_fail_sent:24

cluster_stats_messages_auth-ack_sent:1

cluster_stats_messages_sent:2245

cluster_stats_messages_ping_received:1111

cluster_stats_messages_pong_received:1089

cluster_stats_messages_meet_received:4

cluster_stats_messages_fail_received:3

cluster_stats_messages_auth-req_received:2

cluster_stats_messages_received:2209

 

1번(192.168.50.14) 머신이 종료되어 Cluster에서 빠졌음에도 cluster_state:ok 상태이다.

 

# redis-cli -p 6379 cluster nodes

d8aacf656c4a9c67708ab89d3038736cbe1cc364 192.168.50.14:6380@16380 slave,fail b1e905ca7d3fb5f743930565994aef6eb48cfe95 1590332380344 1590332379219 3 connected

cb508abdf02d27281b1f73019ce4b2529a6391ef 192.168.50.14:6381@16381 slave,fail 29c5855872ff38f9f2a4203094fc4928fec7744f 1590332380344 1590332377000 2 connected

29c5855872ff38f9f2a4203094fc4928fec7744f 192.168.50.15:6379@16379 master - 0 1590332546021 2 connected 5461-10922

d5d6f86145f27daabd587601e0daf452900699fc 192.168.50.15:6381@16381 slave b1e905ca7d3fb5f743930565994aef6eb48cfe95 0 1590332550073 8 connected

b1e905ca7d3fb5f743930565994aef6eb48cfe95 192.168.50.16:6379@16379 myself,master - 0 1590332546000 3 connected 10923-16383

bb77d55e06f3e99b4ada111085f678b791d36dc7 192.168.50.16:6380@16380 master - 0 1590332547039 11 connected 0-5460

9465e7a7d8420a6abae8c01e37ad50575d06141a 192.168.50.15:6380@16380 slave bb77d55e06f3e99b4ada111085f678b791d36dc7 0 1590332548046 11 connected

186deee637ea73f136d671f674320b424c3dddf4 192.168.50.14:6379@16379 master,fail - 1590332379521 1590332377000 1 connected

4d51c878f3ff425d5423e32e6c16c1076b27200d 192.168.50.16:6381@16381 slave 29c5855872ff38f9f2a4203094fc4928fec7744f 0 1590332549054 2 connected

 

1번이 종료됨에 따라 3번 머신(192.168.50.16)에 있던 Slave가 Master로 승격된 것을 확인할 수 있다.

 

# redis-cli -c -p 6379 get hello

"world"

 

 

 

 

참조 URL

반응형

'Tech' 카테고리의 다른 글

Base64 Encoding  (0) 2020.10.25
Redis Cluster Proxy (Predixy)  (0) 2020.05.25
Kafka (카프카)  (0) 2019.04.09
728x90
반응형

HAProxy & Keepalived

 

HAProxy 이중화를 위해 Keepalived를 사용한 테스트이다. (Keepalived도 IPVS를 사용한 L4 Level의 LoadBalancing을 할 수 있다.)

 

 

 

 

설치

 

Test 환경

 

  • OS : Ubuntu 18.04

  • Node

    • VIP : 192.168.219.253

    • Master IP : 192.168.219.125

    • Slave IP : 192.168.219.126

    • Web01 : 192.168.219.128

    • Web02 : 192.168.219.129

  • HAProxy Version : 1.8.8

  • Keepalived Version : 1.3.9

 

 

HAProxy

 

# apt install -y haproxy

-> Ubuntu 18.04에서 기본 Repo로 설치시 1.8.8 버전이 설치된다.

 

현재 (2020-01) 2.1버전이 최신버전이며 1.5버전부터 SSL을 지원하기 때문에 기본설치방법으로도 SSL 사용이 가능하지만 최신 버전을 사용하고 싶다면 wget을 사용하여 다운로드 후 컴파일하거나 별도 Repo를 추가시켜준다.

 

# apt-add-repository ppa:vbernat/haproxy-2.1

# apt update

# apt install -y haproxy 또는 apt install -y haproxy=2.1

-> 위와 같은 방법으로 Repo 추가 후 설치하면 2.1.2 버전이 설치된다.

 

 

Keepalived

 

# apt install -y keepalived

 

 

 

설정

 

HAProxy (Master&Slave)

 

# vim /etc/haproxy/haproxy.cfg

global   

    log 127.0.0.1   local1 notice

    chroot /var/lib/haproxy

    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners   

    stats timeout 30s   

    user haproxy    

    group haproxy  

    daemon  

    maxconn 4000

 

    ca-base /etc/ssl/certs

    crt-base /etc/ssl/private

 

    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS

    ssl-default-bind-options no-sslv3

 

defaults

    log global

    mode    http

    option  httplog

    option  dontlognull

    option  forwardfor

    option  httpclose   

    timeout http-request 10s

    timeout http-keep-alive 10s

    timeout connect 10s

    timeout client  1m

    timeout server  1m

    timeout queue   1m

    errorfile 400 /etc/haproxy/errors/400.http

    errorfile 403 /etc/haproxy/errors/403.http

    errorfile 408 /etc/haproxy/errors/408.http

    errorfile 500 /etc/haproxy/errors/500.http

    errorfile 502 /etc/haproxy/errors/502.http

    errorfile 503 /etc/haproxy/errors/503.http

    errorfile 504 /etc/haproxy/errors/504.http

 

listen stats_page  

    bind    192.168.219.253:8080

    stats   enable

    stats   realm HAProxy Statistics

    stats   uri /  

    stats   auth admin:admin   

 

frontend haproxy    

    bind    192.168.219.253:80

    mode    http  

    default_backend server

 

backend server  

    mode    http      

    balance roundrobin

    cookie  SVID insert indirect nocache maxlife 10m  

    option  httpchk GET /  

    http-check expect status 200  

    server web01 192.168.219.128:80 cookie web01 check inter 2000 rise 2 fall 5  

    server web02 192.168.219.129:80 cookie web02 check inter 2000 rise 2 fall 5  

 

 

Keepalived

 

# vim /etc/keepalived/keepalived.conf (Master)

vrrp_instance VI_1 {

  state MASTER

  interface eth0

  virtual_router_id 51

  priority 200

  advert_int 1

  authentication {

    auth_type PASS

    auth_pass 7388

  }

  virtual_ipaddress {

    192.168.219.253

  }

}

 

# vim /etc/keepalived/keepalived.conf (Slave)

vrrp_instance VI_1 {

  state BACKUP

  interface eth0

  virtual_router_id 51

  priority 100

  advert_int 1

  authentication {

    auth_type PASS

    auth_pass 7388

  }

  virtual_ipaddress {

    192.168.219.253

  }

}

 

 

 

커널 파라미터 변경

 

Kernel Parameter Default List

 

net.ipv4.ip_forward = 1

  • Kernel이 패킷을 전달하게 하는 경우 사용한다.

  • Keepalived가 네트워크 패킷을 실제 서버에 정상적으로 전달하려면 각 라우터 노드가 커널에서 IP Forward를 설정해야한다.

 

net.ipv4.ip_nonlocal_bind = 1

  • HAProxy 및 Keepalived의 로드밸런싱은 동시에 로컬이 아닌 IP 주소에 바인딩할 수 있어야한다.

  • 즉, 네트워크 인터페이스에 없는 주소로 바인딩할 수 있도록 해주는 커널 값이다. (네트워크 인터페이스가 지정된 정적 IP가 아닌 동적 IP를  바인딩할 수 있다.)

  • 해당 옵션이 비활성화 되어 있어도 서비스가 시작하면서 인터페이스에 특정 IP를 바인딩할 수 있으나 FailOver시 문제 발생

 

 

참고 URL

 

 

HAProxy Kernel Parameter Best Practices

 

 

가장 중요한 Paramater

 

net.ipv4.ip_local_port_range = "1025 65534"

net.ipv4.tcp_max_syn_backlog = 100000

net.core.netdev_max_backlog = 100000

net.core.somaxconn = 65534

ipv4.tcp_rmem = "4096 16060 64060"

ipv4.tcp_wmem = "4096 16384 262144"

 

 

작업량에 따라 조절

 

tcp_slow_start_after_idle = 0

 

 

iptables tuning

 

net.netfilter.nf_conntrack_max = 131072

 

 

주의 사항

 

conntrack이 잘못 구성되면 HAProxy가 최대 성능을 발휘하지 못한다.

또한 iptables가 활성화 되어 있으면 connection tracking 작업을 위해 iptables에 규칙이 없더라도 20%의 CPU를 소모한다.

 

 

참고 URL

 

 

 

테스트

 

아래 테스트는 커널 파라미터 중 기본값만 변경한 테스트이다.

 

1. 커널 파라미터 변경전 FailOver

 

# curl 192.168.219.253 (VIP)

 

외부에서 HAProxy의 VIP로 curl 명령 실행시 뒷단의 Web 서버로 LoadBalancing이 되는 것이 확인된다.

 

앞단에서 HAProxy Master역할을 하는 것은 Keepalived의 Master로 설정된 노드이다.

 

이제 Master VM 장애 발생을 가정하고 Failover 테스트를 진행한다.

Slave에는 Haproxy Service가 실행 중이다. 

(기본 커널 파라미터가 셋팅이 안된상태에서 Slave의 HAProxy 데몬은 실행에 실패한다. 테스트를 위해 Master를 Off후 서비스를 올린 뒤에 다시 Master를 On 시켜 테스트 환경을 만든 것이다.

데몬이 실패한 이유는 haproxy에 의해 VIP가 네트워크 인터페이스에 바인딩되어야하는데 커널파라미터가 비활성화 값이므로 실패하였다.)

 

Master VM을 Poweroff 하였을때 Keepalived에 의해 Slave VM이 Master로 승격되었다.

 

외부에서 HAProxy로 curl 명령 실행시 무중단으로 실행되는 것도 확인되었다.

 

이제 다시 Master VM을 실행시켜 FailBack을 할 예정이다.

 

 

Slave는 정상이나 Master가 다시 정상으로 돌아왔을 경우 HAProxy에 대한 응답은 실패했다.

Master에서 확인해보니 아래와 같이 HAProxy Service가 실패하였다.

 

 

실패 원인

 

haporxy는 haproxy.cfg에 있는 VIP를 바인딩해야하는데 VIP가 현재 로드밸런서에 없으므로 오류를 발생시킨다. (오류 내용 - 프록시 : 소켓을 바인드 할 수 없음)

Master -> Slave로 FailOver하는 경우 Keepalived에 의해 VIP가 옮겨간다. Slave에서 서비스 구동을 하다가 Master가 정상으로 돌아오면 Keepalived는 가중치에 의해 다시 Master가 트래픽을 받도록 변경한다.

이 상태에서 VIP도 Master로 옮겨오는데 Keepalived가 VIP를 Master의 네트워크 인터페이스에 바인딩하고 HAProxy가 서비스가 실행되면 커널파라미터에 상관없이 HAProxy에서 VIP를 네트워크 인터페이스에서 인식하여 정상적으로 FailBack이 된다.

 

# vim /lib/systemd/system/haproxy.service

 

 

위 방법은 Service의 Boot Type에서 notify를 idle로 변경한 것이다.

notify의 경우 simple과 동일하나 unit이 구동되면 systemd에 시그널을 보낸다. (unit이 시작된 즉시 systemd는 유닛의 시작이 완료되었다고 판단한다. 그런데 다른 유닛과 통신하기 위해 소켓을 사용하는 경우 이 설정을 사용하면 안된다.)

HAProxy의 경우 Keepalived와 통신하기 때문에 HA를 구성하기 위해서는 Type이 notify가 되어서는 안된다. 이런 부분을 해결해주는 것이  net.ipv4.ip_nonlocal_bind 파라미터이다.

Master에서 Type을 notify -> idle로 변경하였을 경우 net.ipv4.ip_nonlocal_bind=0 이더라도 정상적으로 FailBack이 진행된다.

하지만 Slave의 경우 Unit의 Type을 변경하더라도 서비스는 실패가 된다. (실패한 이유는 Keepalived의 VIP가 Master에 있기때문에 통신할 소켓이 없어서 발생한다. 네트워크 인터페이스의 바인딩 문제)

net.ipv4.ip_nonlocal_bind=1 일경우 Unit의 Type에 상관없이 HAProxy 서비스는 정상 시작된다. 

 

 

2. 커널 파라미터 변경후 FailOver 

 

net.ipv4.ip_nonlocal_bind=1 로 변경한 경우 정상적으로 FailOVer & FailBack이 진행된다. 다면 FailBack 과정에서 짧은시간동안 순단이 발생할 수 있으며 순단시에는 HTTP Code 503을 발생한 후 정상적으로 HTTP Code 200을 반환한다.

실제 운영환경에서는 자동 FailBack이 아닌 수동으로 FailBack을 진행하는 것을 권장한다. 잠시동안이라도 발생하는 네트워크 순단현상을 최대한 방지하기 위해서이다.

 

 

3. 정리

 

Master & Slave 구성에서 Master는 Service Unit의 Type 값을 변경해 정상적인 FailBack을 할 수 있고 Slave에서는 커널 파라미터를 변경하여 FailOver 환경을 만들수 있으나 정상적인 방법은 아니므로 기본 커널 파리미터 값을 변경하여 HA를 구성하는 것을 권장한다.

 

 

 

 

반응형

'Web' 카테고리의 다른 글

SNI(Server Name Indication)  (0) 2020.10.25
HAProxy  (2) 2020.01.17
Keepalived & VRRP  (0) 2020.01.17
부하분산 테스트 설명 및 용어  (0) 2019.04.09
PinPoint  (0) 2019.04.09
728x90
반응형

HAProxy

 

 

L4, L7와 같은 Hardware LoadBalancer를 대체하기 위한 Open Source로 Reverse Proxy를 기반으로한 L4, L7 Software LoadBalancer이다.

Nginx에 비해 Active Heath Check가 가능하므로 좀 더 안정적으로 사용할 수 있으며 HAProxy 설정 추가를 통해 Scale-Out도 할 수 있다.

HTTP 통신의 경우 Web-Server (Nginx, Apache 등)를 이중화 시켜줄수 있으며 HTTP와 같은 표준 프로토콜이 아닌 TCP Socket 통신에 대해서도 이중화처리가 가능하다.

 

HAProxy의 경우 HA를 구성하기위해 Keepalived를 많이 사용한다. Keepalived를 사용하여 HAProxy를 이중화하면 아래와 같은 그림이다.

 

 

 

 

 

HAProxy 동작방식

 

HAProxy는 기본적으로 Reverse Proxy 형태로 동작한다. (즉, 실제 서버 앞단에 존재하며 서버로 오는 요청을 대신 받아 뒷단의 서버에 전달하고 결과를 리턴받아 요청한 곳에 다시 전달하는 역할을 한다.)

 

HAProxy의 동작 흐름은 다음과 같다.

 

 

  1. 최초 접근시 서버에 요청 전달

  2. 응답시 쿠키 (Cookie) 에 서버 정보 추가 후 반환

  3. 재 요청시 Proxy에서 쿠키 정보 확인 후 최초 요청 서버로 전달

  4. 다시 접근시 쿠키 추가할 필요없이 서버에 전달 (클라이언트에 쿠키 정보가 계속 존재하여 재사용)

 

 

 

 

HAProxy HA

 

HAProxy는 기본적으로 VRRP (Virtual Router Redundancy Protocol) 를 지원한다.

 

 

위 그림과 같이 HAProxy를 이중화하여 Master 장애 발생시 Slave가 Master의 VIP를 가져와 Master로 승격된다. 무중단 서비스이지만 1초정도의 순단 현상은 발생할 수 있다.

 

 

 

HAProxy HA 동작방식

 

HA로 설정된 HAProxy의 동작흐름이 단일 HAProxy와 다른점은 최초 접근시 쿠키에 바로 서버 정보를 입력하지 않고 서버에서 Jsessionid가 전달될 때 서버 정보를 합쳐서 전달한다는 것이다.

 

 

  1. 쿠키에 정보가 없고 X-Forwarded-For에 정보 추가

  2. 쿠키에 추가 없음

  3. Jsessionid 추가

  4. 서버 정보와 Jsessionid를 쿠키에 추가

  5. 쿠키에서 서버 판별 후 Jsessionid만 전달

 

 

HAProxy 환경 예제

 

1. L4 Switch + HAProxy 

 

 

  • L4 스위치 이중화와 HAProxy 이중화로 구성하는 방법으로 해당 구성이 보통 FM 구성이다.

 

 

2. GSLB (Global Service Load Balancing) + HAProxy

 

 

  • Global 서비스의 경우 IDC간 이중화 및 Global 환경에서 무정지 서비스를 위한 DR (Disaster Recovery, 재해복구) 시스템 구축이 필요하다.

  • GSLB L4 스위치를 사용할수도 있지만 GSLB 구성용 L4 스위치의 경우 고가이기에 위의 그림은 DNS (BIND) 를 이용한 구축형태이다.

  • 클라이언트가 DNS로 도메인 조회시 근거리 IDC 정보를 전달하는 방법이다.

 

 

 

 

HAProxy 구성하기

 

테스트 환경

  • OS : Ubuntu 18.04

  • HAProxy Server 2EA : 192.168.219.125 / 192.168.219.126

  • Web Node 2EA : 

 

 

1. Kernel Paramater 수정

 

# echo 'net.ipv4.ip_nonlocal_bind=1' >> /etc/sysctl.conf

# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf

# sysctl -p 또는 Reboot

# cat /proc/sys/net/ipv4/ip_nonlocal_bind

 

 

net.ipv4.ip_nonlocal_bind 옵션은 프로그램이 시스템 상의 장치에 없는 주소로 Binding 할 수 있도록 할 수 있게하는 커널 파라미터이다. 즉 Network Interface에 등록되지 않은 동적 IP (VIP와 같은) 를 연결할 때 사용한다. 

 

Kernel Parameter Information (Web) :  https://support.hpe.com/hpsc/doc/public/display?docId=emr_na-c01418125

 

 

2. HAProxy 설치

 

# apt install -y haproxy

-> Ubuntu 18.04에서 기본 Repo로 설치시 1.8.8 버전이 설치된다.

 

현재 (2020-01) 2.1버전이 최신버전이며 1.5버전부터 SSL을 지원하기 때문에 기본설치방법으로도 SSL 사용이 가능하지만 최신 버전을 사용하고 싶다면 wget을 사용하여 다운로드 후 컴파일하거나 별도 Repo를 추가시켜준다.

 

# apt-add-repository ppa:vbernat/haproxy-2.1

# apt update

# apt install -y haproxy 또는 apt install -y haproxy=2.1

-> 위와 같은 방법으로 Repo 추가 후 설치하면 2.1.2 버전이 설치된다.

 

 

3. HAProxy 설정

 

HAProxy Config 섹션

 

global, defaults, listen, frontend, backend의 영역으로 구분된다.

  • global : 전체 영역에 걸쳐 적용되는 기본 설정을 담당

  • defaults :  이후 오는 영역(frontend, backend, listen)에 적용되는 설정

  • frontend : 클라이언트 연결을 받아들이는 소켓에 대한 설정

  • backend : 앞에서 들어온 연결에 할당될 프록시 서버들에 대한 설정

  • listen : frontend와 backend로 사용되는 포트를 한번에 설정하는 영역으로 TCP 프록시에서만 이용

 

Option Manual :  https://cbonte.github.io/haproxy-dconv/1.8/configuration.html

 

 

Balance Option

 

Roundrobin : 순차적으로 분배

static-rr : 서버에 부여된 가중치에 따라서 분배

leastconn : 접속수가 가장 적은 서버로 분배

source : 운영중인 서버의 가중치를 나눠서 접속자 IP 해싱(hashing)해서 분배

uri : 접속하는 URI를 해싱해서 운영중인 서버의 가중치를 나눠서 분배 (URI의 길이 또는 depth로 해싱)

url_param : HTTP GET 요청에 대해서 특정 패턴이 있는지 여부 확인 후 조건에 맞는 서버로 분배 (조건 없는 경우 round_robin으로 처리)

hdr : HTTP 헤더에서 hdr(<name>)으로 지정된 조건이 있는 경우에 대해서만 분배 (조건없는 경우 round robin 으로 처리)

rdp-cookie : TCP 요청에 대한 RDP 쿠키에 따른 분배

 

 

HAProxy 설정

 

# vim /etc/haproxy/haproxy.cfg

 

global   # 전역 섹션 프로세스 전체에 영향을 주는 내용

    log 127.0.0.1   local1 notice   # 지정한 ip (127.0.0.1) 또는 소켓 (/dev/log)에 로그를 보냄

    # local1 뒤에는 로그 레벨을 지정할 수 있는데 notice를 붙이면 emerg-notice 레벨을 보내고

    # local1 notice notice 로 설정하면 notice 레벨만 syslog로 보냄 (notice는 최소레벨을 의미)

    # rsyslog에서 "local1.notice  /var/log/haproxy.log" 형식으로 설정필요 (# echo "local1.notice  /var/log/haproxy.log" >> /etc/rsyslog.d/50-default.conf)

    chroot /var/lib/haproxy # 서비스 Jail 경로, 슈퍼유저로 실행시 모든 동작은 이 안에서만 수행 (보안 상승)

    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners   # stats page를 사용하기 위한 Unix 소켓 바인딩

    stats timeout 30s   # stats에 timeout 시간 설정 (stats_page timeout)

    user haproxy    # 프로세스 사용자

    group haproxy   # 프로세스 그룹

    daemon  # 백그라운드로 동작

    maxconn 4000    # 프로세스당 최대 연결 개수

 

    ca-base /etc/ssl/certs

    crt-base /etc/ssl/private

 

    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS

    ssl-default-bind-options no-sslv3

 

defaults    # front, back, listen에 관련된 전역 섹션

    log global           # 로그는 기본적으로 global 설정을 따르겠다는 옵션

    mode    http         # http 프로토콜을 사용하는 로드밸런싱 모드

    option  httplog      # 기본 Log는 SIP, DIP와 Name만 표기하므로 이 옵션을 사용해 좀 더 자세하게 출력

    option  dontlognull  # 로그 비대화 방지를 위해 Probe(정찰, 스캔)과 같은 잡다한 기록을 HAProxy Log화 하지않는 옵션

    option  forwardfor   # 클라이언트 정보 전달 (X-Forwarded-For 헤더를 넣어서 전달)

    option  httpclose    # keep-alive 문제 발생시 off 옵션

    timeout http-request 10s   # Request시의 헤더에만 적용, DDos 방어를 위해 HTTP 요청 타임아웃시간 설정 (클라이언트의 연결 타임아웃과는 무관한 HAProxy 서버의 옵션임)

    timeout http-keep-alive 10s   # 클라이언트 요청에 따른 응답 전송 후 다음 요청 대기시간 설정 (http-request가 선행함)

    timeout connect 10s  # TCP 패킷 손실을 막기위한 Real 서버로의 연결 최대 지연시간 설정 (Backend에 적용되나 전역으로 설정 가능)

    timeout client  1m   # 외부 클라이언트 요청이나 데이터와의 연결 최대 시간 (Request와 같이 사용하여 서버 안정성을 구현)

    timeout server  1m   # 서버가 데이터를 승인하거나 전송해야할 때의 연결 최대 시간

    timeout queue   1m   # 서버의 maxconn 도달시 무한정 보류상태로 두지 않고 HTTP 503 응답을 보내면서 연결을 버리는 시간 설정 (Surge Queue Time)

    errorfile 400 /etc/haproxy/errors/400.http

    errorfile 403 /etc/haproxy/errors/403.http

    errorfile 408 /etc/haproxy/errors/408.http

    errorfile 500 /etc/haproxy/errors/500.http

    errorfile 502 /etc/haproxy/errors/502.http

    errorfile 503 /etc/haproxy/errors/503.http

    errorfile 504 /etc/haproxy/errors/504.http

 

listen stats_page   # Listen은 Front / Back의 연결 포트 / 옵션들을 정의함 (TCP 제어나 Proxy에 주로 사용)

    bind    *:8080  # 접속 포트 지정

    stats   enable  # 상태 페이지 활성화

    stats   realm HAProxy Statistics   # 브라우저 타이틀

    stats   uri /   # 접근할 URI 경로

    stats   auth admin:admin    # 인증 추가 (auth [User]:[Password])

 

frontend haproxy    # 클라이언트 연결을 받아들이는 소켓에 대한 설정 (WAF 기능에 가까움)

    bind    *:80    # 접속 포트 지정

    mode    http    # 프로토콜 유형 설정

    default_backend server  # Front서버에서 전달할 기본 backend

 

backend server  # Front에서 들어온 연결에 할당될 프록시 서버들에 대한 설정

    mode    http            # 프로토콜 유형 설정

    balance roundrobin      # LoadBalancer 유형 설정

    cookie  SVID insert indirect nocache maxlife 10m    # Sticky Session 설정 (maxlife는 유지기간 설정)

    option  httpchk GET /   # healthcheck uri로 GET 뒤의 경로에 curl 명령으로 http header 값 [200 OK] 확인 (server 옵션에서 inter값으로 주기 설정)

    http-check expect status 200    # http check시 header 값이 200이면 정상을 의미

    server web01 192.168.50.11:80 cookie web01 check inter 3000 rise 2 fall 5   # real server

    server web02 192.168.50.12:80 cookie web02 check inter 3000 rise 2 fall 5   # real server

# server [host명] [ip]:[port] cookie [서버쿠키명] check inter [주기(m/s)] rise [서버구동여부점검횟수] fall [서비스중단]

# inter는 ms단위이며 값이 2000이면 2초

# rise는 서버 정상 동작 체크로 2로 설정시 2번 정상 체크되면 정상으로 간주

# fall은 서버 실패 동작 체크로 5로 설정시 5번 정상 체크에 실패하면 서버를 비정상으로 간주

 

 

HAProxy 서비스 시작

 

# /etc/init.d/haproxy start -f /etc/haproxy/haproxy.cfg

 

 

HAProxy Stats Page

 

브라우저에 설정한 stats uri를 입력하여 접근하면 web ui로 확인할 수 있다.

https://(haproxy server ip 또는 vip):8080

 

 

 

참고 URL

 

 

 

반응형

'Web' 카테고리의 다른 글

SNI(Server Name Indication)  (0) 2020.10.25
HAProxy & Keepalived  (1) 2020.01.29
Keepalived & VRRP  (0) 2020.01.17
부하분산 테스트 설명 및 용어  (0) 2019.04.09
PinPoint  (0) 2019.04.09
728x90
반응형

Keepalived & VRRP

 

웹 서비스 운영시 부하분산을 위해 Nginx로 Reverse Proxy를 사용하거나 Haproxy 같은 Loadbalancer를 사용한다.

그런데 이런 Loadbalancer에 장애가 발생하면 뒤에 존재하는 서버들은 정상적으로 서비스가 가능하더라도 앞에서 Gateway 역할을 하는 부분이 동작하지 않기 때문에 전체 장애로 이어진다.

 

 

해당 그림과 같이 한 지점에 장애가 발생해 전체 시스템의 장애로 이어지는 것을 SPOF (Single Point Of Failure : 단일장애점) 이라고 한다.

이런 SPOF을 막기위해 HA를 구성하는 것이다.

 

 

 

 

VRRP (Virtual Router Redundancy Protocol) 란?

 

Route나 LoadBalancer의 장애를 극복하고 HA 구성을 하기 위해서는 Route나 LoadBalancer를 이중화시킬 필요가 있다.

이렇게 Network 상에 존재하고 있는 Route나 LoadBalancer 중 어떤 인터페이스가 트래픽을 전달하는 책임을 가질지 결정하는 프로토콜이 VRRP라고 한다.

(HSRP도 같은 역할을 하는 프로토콜이긴 하나 Cisco 벤더에 종속적이므로 IETF 표준인 VRRP을 많이 사용한다.)

 

VRRP를 사용해 Linux에서 HA를 쉽게 해주는 Tool로는 대표적으로 Keepalived가 있고 비슷한 메커니즘을 사용하는 Corosync나 PaceMaker 등이 있다.

 

아래는 VRRP를 사용하는 이중화된 Route의 구성도이다.

 

 

VRRP는 가상의 Route에 IP를 할당하고 가상 Route에 연결된 물리 Route 중 할당 우선순위가 높은 Route가 Active Route가 되어 통신에 사용된다. (개념은 Linux Bonding과 같은 개념이다.)

Stanby Route는 Active Route를 장애 발생시 Active로 승격한다.

 

Active Route 선출 우선순위는 아래와 같다.

  1. 설정한 VIP가 실제 인터페이스 IP인 Route

  2. Priority가 높은 Route

  3. IP 주소가 높은 Route

 

아래 그림을 확인해보면 VRRP로 구성된 Route가 FailOver하는 디테일한 과정이다.

 

 

Master Route (Active Route)에서 Slave Route (Stanby Route)로 Advertisement (Health Check) 를 전달하여 자신이 정상 상태인 것을 알려준다.

Slave Route에는 Master DownTimer라는 것을 설정하는데 이 지정된 시간동안 Master로 부터 Advertisement가 오지 않으면 Master가 죽은것으로 판단하고 자신이 Master가 되었다는 Advertisement를 Master에 전달한다.

 

VRRP의 상세 동작 원리는 아래의 링크을 참고한다.

URL : https://www.netmanias.com/ko/post/techdocs/5049/data-center-network-protocol/vrrp-virtual-router-redundancy-protocol-detailed-principles-of-operation

 

 

 

 

Keepalived 란?

 

C로 작성된 LoadBalancing 및 HA를 제공하는 프레임워크이다.

간단하게 설명하면 VIP (가상 IP)를 기반으로 작동하며 Master 노드를 모니터링하다가 해당 노드에 장애가 발생했을시 Stanby 서버로 Failover되도록 지원한다.

즉, Heartbeat 체크를 하다가 Master에 장애발생시 Slave로 FailOver하는 역할을 하는 것이다.

 

아래 그림은 keepalived의 구조이다.

 

LoadBalancing을 하기위해서 LVS (Linux Virtual Server)의 구성 요소인 IPVS를 사용한다. 

Keepalived와 IPVS를 조합해 HAProxy와 같은 LoadBalancer를 사용하여 L7 LoadBalancing을 할 수도 있지만 Keepalived에서 제공하는 LoadBalancing 기능을 사용하여 L4 LoadBalancing을 할 수도 있다.

Keepalived는 HA를 하기위해 VRRP Protocol을 사용하며 LoadBalancer 설정을 위한 LVS와는 독립적으로 동작한다.

 

Keepalived는 LoadBalancing 쪽에서 사용되는 LVS(IPVS)와 NAT, Masquerading에 쓰이는 Netfilter와 VIP 할당 및 해제에 쓰이는 Netlink, VRRP Advertisement 패킷 전송을 위해 사용되는 Multicast와 같은 Kernel 컴포넌트로 구성되어 있다.

Keepalived의 HA는 자신의 IP 주소와는 별개로 VIP를 설정해두고 문제가 생겼을때 이 VIP를 다른곳으로 인계하여 같은 IP주소를 통해서 서비스가 지속되도록 해주는것이 핵심인데 이 부분은 Keepalived가 VIP 할당 및 해제를 자동으로 해주기 때문에 별도의 설정을 하지 않아도 문제가 없다.

 

HA구성에서 Master의 장애인지 검출할 수 있는 방법은 아래와 같다.

  • ICMP (L3)

    • ping을 사용하는 것이다.

    • 네트워크 구간이 정상이고 서버가 살아있다면 ICMP echo 요청에 대한 응답이 돌아올 것이지만 ICMP 패킷이 바이러스 침투나 공격으로 사용되는 사례가 많기때문에 ICMP를 차단하는 경우가 많아 잘 사용하지 않는다.

  • TCP 요청 (L4)

    • 서비스를 올린 후 방화벽 허용 확인을 위해 telnet 명령을 실행해 정상 체크를 하기도 한다. 이는 TCP 요청이 정상적으로 응답하는지 확인하는 방법이다.

    • 서비스가 살아 있어 Port Listen은 하고 있지만 서버나 프로그램의 오류가 있어 HTTP 500 Code를 반환하는 경우 정확한 확인이 불가능한 단점이 있다.

  • HTTP 요청 (L7)

    • 실제 실행중인 서비스에 Heathcheck를 위한 Endpoint를 두고 서비스에 요청을 날려 200 OK가 반환되는지 확인하는 것이다.

    • HTTP 요청 자체가 ICMP나 TCP 요청에 비해 무겁기 때문에 고려가 필요하다.

 

Keepalived는 Heathcheck를 위해 TCP, HTTP, SSL, MISC와 같은 방법을 사용한다.

  • TCP_CHECK

    • 비동기방식으로 Time-Out TCP 요청을 통해 장애를 검출하는 방식이다.

    • 응답하지 않는 서버는 Server Pool에서 제외한다.

  • HTTP_GET

    • HTTP Get 요청을 날려 서비스의 정상 동작을 확인한다.

  • SSL_GET

    • HTTP Get과 같은 방식이지만 HTTPS 기반이다.

  • MISC_CHECK

    • 시스템상에서 특정 기능을 확인하는 Script를 돌려 그 결과가 0인지 1인지를 가지고 장애를 검출하는 방법이다.

    • 네트워크가 아니라 시스템상에서 돌고 있는 서비스의 정상 동작을 확인하는데 유용하다.

 

 

Keepalived 구성

 

환경

  • VitualBox

  • OS : Ubuntu 18.04

  • VIP : 192.168.219.119

  • Active : 192.168.219.120

  • Stanby : 192.168.219.121

 

 

1. keepalived 설치 (Active & Stanby)

 

# apt update

 

apt install -y keepalived

 

 

 

2. keepalived 설정 (Active & Stanby)

 

2.1) Active Node 설정

 

vim /etc/keepalived/keepalived.conf

 

 

vrrp_instance VI_1 {

  state MASTER

  interface eth0

  virtual_router_id 51

  priority 200

  advert_int 1

  authentication {

    auth_type PASS

    auth_pass 7388

  }

  virtual_ipaddress {

    192.168.219.119

  }

}

 

 

2.2) Stanby Node 설정

 

 

vrrp_instance VI_1 {

  state BACKUP

  interface eth0

  virtual_router_id 51

  priority 100

  advert_int 1

  authentication {

    auth_type PASS

    auth_pass 7388

  }

  virtual_ipaddress {

    192.168.219.119

  }

}

 

 

3. 테스트

 

3.1) Fail Over Test

# ping 192.168.219.119

 

3.2) Active Node Poweroff

# poweroff

 

3.3) Fail Over Log Check

Active Node가 Poweroff 되자 Keepalived 데몬이 VIP (192.168.219.119) 를 Stanby Node에 인계하여 FailOver를 하였다.

 

3.4) tcpdump를 사용하여 vrrp 동작 확인

tcpdump -n vrrp

Active 정상 동작

Active poweroff 후 Stanby FailOver 정상동작 확인

 

 

참고 URL

 

 

 

 

반응형

'Web' 카테고리의 다른 글

HAProxy & Keepalived  (1) 2020.01.29
HAProxy  (2) 2020.01.17
부하분산 테스트 설명 및 용어  (0) 2019.04.09
PinPoint  (0) 2019.04.09
Crawling (크롤링)  (0) 2019.04.09

+ Recent posts