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

+ Recent posts