grep

Engineering

Safe Deploy - 안전하게 L4 에서 제외하는 방법

NHN

2019년 6월 14일

원문에서 보기 ↗

목차

L4 스위치에서 서비스 health check 하는 방법

Port health check (L4) 방식

Port health check 종류

L7 health check 방식

L4 에서 서비스 제외시키기

L4 스위치에서 서비스 제외시키기

Port health check 방식

#!/bin/bash
L4_HEALTHCHECK_IPS=("10.0.0.252" "10.0.0.253")
PORT=80

for i in ${L4_HEALTHCHECK_IPS[@]}
do
sudo iptables -A INPUT -s $i -p tcp --dport $PORT -j DROP #DISABLE HEALTH CHECK, $L4_HEALTHCHECK_IPS 로부터 $PORT 로 들어오는 요청을 DROP하는 방화벽 룰 추가
# sudo iptables -D INPUT -s $i -p tcp --dport $PORT -j DROP #ENABLE HEALTH CHECK, 위의 방화벽 룰을 삭제
done

L7 health check 방식

유저 인입이 없는 걸 검증하기

유저 인입을 확인하기 전에, TCP connection status 에 대해 간단하게 설명하겠습니다.

TCP connection status

주로 보이는 몇 가지만 설명하겠습니다.

유저 인입 검증

#!/bin/bash

SERVICE_PORT=443
L4_HEALTH_CHECK_IPS=("10.0.0.253" "10.0.0.254")

grep_cond_exclude_ips=$(IFS='|'; echo "${L4_HEALTH_CHECK_IPS[*]}") #(L7 health check 방식에서) L4 health check ip 제외
if [ $(sudo netstat --tcp -np | grep ":$SERVICE_PORT " | grep -vE "$grep_cond_exclude_ips" | grep -i established | wc -l) -gt 0 ];
then
    echo "User connection still exist" >&2
    exit 1 #exit with error
else
    echo "NO connection"
    exit 0
fi

종합 - 안전하게 서비스 내리기

예제 - L7 health check up/down

#!/bin/bash

ESTB_CHECK_MAX=3
RETRY_MAX=5
SLEEP_TIME=3 #second

SERVICE_PORTS=("80" "443")
L4_ENABLE_URL="http://127.0.0.1/actuator/health/up"
L4_DISABLE_URL="http://127.0.0.1/actuator/health/down"
SERVICE_CHECK_URL="http://127.0.0.1/actuator/health"

L4_HEALTH_CHECK_IPS=("10.0.0.253" "10.0.0.254")

func_disable_l7()
{
    curl -XPUT $L4_DISABLE_URL
}

func_enable_l7_repeat()
{
    retry=0
    while [ $retry -lt $RETRY_MAX ]
    do
        curl -XPUT $L4_ENABLE_URL # Enable health check
        sleep $SLEEP_TIME
        [[$(curl -LI $SERVICE_CHECK_URL -o /dev/null -w '%{http_code}' -s | grep 200 | wc -l) -eq 1]] && break
        ((retry++))
        echo "Service is down, retrying..."
    done

    if [ $retry == $RETRY_MAX ];
    then
        echo "FAILED to enable L7 healthcheck" >&2
        exit 1
    else
        echo "SUCCESS to start"
        exit 0
    fi
}

func_check_no_conn_repeat()
{
    estb_check_count=0
    retry_count=0
    grep_cond_ports=$(printf ":%s |" ${SERVICE_PORTS[*]} | head -c -1)
    grep_cond_exclude_ips=$(IFS='|'; echo "${L4_HEALTH_CHECK_IPS[*]}") #exclude; because L4 request continuously regardless of health status
    while [ $estb_check_count -lt $ESTB_CHECK_MAX -a $retry_count -lt $RETRY_MAX ]
    do
        sleep $SLEEP_TIME
        echo "Checking established connection..."
        conn=$(netstat -nt | grep -E "$grep_cond_ports" | grep -vE "$grep_cond_exclude_ips" | grep ESTABLISHED)
        if [ $(echo $conn | wc -l) -gt 0 ];
        then
            echo "connection exist"
            echo -e "$conn"
            estb_check_count=0
            ((retry_count++))
        else
            echo "NO connection"
            ((estb_check_count++))
        fi
    done

    if [ $retry_count == $RETRY_MAX ];
    then
        echo "User connection still exist" >&2
        exit 1 #exit with error
    else
        exit 0
    fi
}

case "$1" in
        down)
                func_disable_l7
                sleep 10
                func_check_no_conn_repeat
                ;;
        up)
                func_enable_l7_repeat
                ;;
        *)
                echo $"Usage: $0 {down|up}"
esac

결론

루프백(ex. lo:0) 인터페이스를 내리지 않아도 얼마든지 L4 에서 서비스를 제외할 수 있습니다.

배포 시 패킷 손실을 최소화 하려면 ESTABLISHED 된 TCP Connection 까지 확인해야 합니다.