검수요청.png검수요청.png

부하 (컴퓨터)

위키원
이동: 둘러보기, 검색

부하(負荷, load)란 시스템에서 원하는 어떤 효과를 얻기 위해 취하는 행동에 필요한 동작이나 자원을 말한다. 예를 들어, 운영체계에서 프로세스들을 스케줄링(scheduling)함으로써 컴퓨터 자원의 이용 효율을 높일 수 있으나, 프로세스를 스케줄링하는 그 자체도 중앙처리장치(CPU)의 시간과 기억 장치가 소모된다. 만일 스케줄링을 잘못하여 스케줄링 자체의 부하가 너무 커진다면 아예 스케줄링을 하지 않는 것이 낫다. 이와 같이 부하는 적정한 한도 밑으로 유지되어야 하는 성질을 가지고 있다.

개요

부하는 컴퓨터계에서는 시스템이 수행해야 할 양의 척도이다. 비트, 명령어, 작업, 프로세스 등의 데이터를 처리·실행·수행해 주는 주체인 CPUGPU에서 주로 취급한다. 윈도우의 작업 관리자에서 확인할 수 있는 이용률 또는 사용률과 혼동해서 같은 의미처럼 취급하는 사람들이 많지만, 이용률과 부하는 엄연히 서로 다른 개념이다. 여러 프로세스들을 관리하는 서버 및 네트워크 분야에서의 부하 개념은 실행 중인 프로세스의 양과 대기 중인 프로세스의 양을 모두 합친 전체 프로세스의 양을 가리키고, 전체 프로세스의 양에 대한 실행 중인 프로세스의 양을 비율로 환산한 것을 이용률이라고 부른다. 쉽게 말해서 주어진 일의 양은 부하, 한 번에 처리할 수 있는 일의 양은 이용률에 해당되며, 만약 주어진 일의 양이 한 번에 처리할 수 있는 일의 양보다 더 적다면 이때 CPU의 이용률은 100%보다 낮은 값이 된다. 다만, 값이 수시로 바뀌는 순간 부하로는 그래프와 함께 모니터링하지 않는 한 전체적인 부하를 제대로 파악하기 어려우므로, 일반적으로 그냥 부하라고 하면 '일정 단위 시간당 평균 부하'로 통용된다.[1]

원인 및 측정

일반적으로 서버에 걸리는 부하는 크게 네트워크 부하와 서버의 성능 부하로 분류된다. 네트워크 부하는 말 그대로, 서버에 접속하려는 사람이 폭발적으로 증가하여 트래픽이 급증한 경우의 네트워크에 걸리는 부하를 말한다. 이러한 경우 서버 앞단에서 트래픽 자체를 분산시켜주는 로드밸런서를 사용하거나 서버의 대수를 더 늘리는 방식으로 해결할 수 있다. 하지만, 애초에 서버가 서버 자체의 성능을 100% 발휘하고 있지 않다면 서버의 대수를 늘리거나 로드밸런서를 사용해도 그 효율이 매우 낮을 것이다. 따라서 로드밸런서 등으로 네트워크 부하에 대한 분산을 고려하기 전에, 서버 1대가 자신의 성능을 잘 발휘하고 있는지 부터 확인해 볼 필요가 있다. 단일 서버에 걸리는 부하의 원인은 크게 CPU 부하와 I/O 부하, 이 2가지로 분류된다.

  • CPU 부하 : CPU 부하가 높은 경우는 서버에서 실행되고 있는 프로그램 자체의 연산량이 많은 경우나 프로그램에 오류 등이 발생한 경우이다. 이러한 경우에는 프로그램에서 발생하는 오류를 제거하거나 알고리즘의 시간, 공간 복잡도를 개선하여 대응할 수 있다.
  • I/O 부하 : I/O 부하가 높은 경우는 서버에서 실행되고 있는 프로그램의 입출력이 많거나, 데이터베이스하드디스크 등의 저장 장치로의 접근이 많아 스왑이 발생하는 경우가 대부분이다. 이러한 경우 특정한 프로세스가 극단적으로 메모리를 소비하고 있는지 확인한 후, 프로그램 자체에 오류가 있다면 프로그램을 개선하거나 탑재된 메모리의 용량 자체가 부족한 경우 램을 추가하여 메모리를 증설하는 방법으로 대응할 수 있다. 저장 장치나 하드디스크로의 입출력이 빈번하게 발생하는 경우 또한 메모리를 증설하거나, 메모리 증설로 대응할 수 없는 경우는 데이터 자체를 분산(샤딩이나 파티셔닝)하거나 캐시 서버등을 도입하는 방안을 고려해볼 수 있다.
운영체제에서 프로세스를 실행하는 방식과 부하의 측정

이러한 단일 서버에서 발생하는 부하를 측정하기 위해서는 리눅스 운영체제의 커널을 이용하면 된다. 리눅스나 윈도우 등의 운영체제에서는, 동시에 여러 프로세스들을 처리하기 위해 멀티태스킹 방식을 사용한다. CPU나 디스크 등의 유한한 하드웨어에서 여러 프로세스들을 동시에 처리하기 위해, 매우 짧은 시간 간격으로 여러 프로세스들을 돌아가면서 처리하는 방식이다. 이러한 멀티태스킹 방식에서, 처리해야 할 프로세스가 점점 많아지게 될 시 CPU를 사용하고 싶어 대기하고 있는 프로세스들이 점점 쌓이게 된다. 이렇게 CPU를 사용하려고 기다리고 있는 프로세스를 운영체제에서는 평균 부하(Load average)라고 정의한다.

CPU를 사용하려고 기다리고 있는 프로세스가 많을수록, CPU에 주어진 일이 많다는 의미이고 결국 시스템에 걸리는 부하가 크다는 뜻이다. 리눅스에선 top 명령어에서 이러한 평균 부하를 확인할 수 있다. top 명령어로 1분, 5분, 15분 동안 몇 개의 태스크가 CPU를 사용하기 위해 기다리고 있는 대기 상태에 있었는지를 알아볼 수 있다. 따라서 평균 부하가 높은 상황은 지연되는 태스크가 많다는 것을 의미한다. 하지만, 평균 부하는 단순히 지연되는 태스크의 수를 의미하며, CPU 부하가 높은지 I/O 부하가 높은지까지는 상세하게 알 수 없다. 이를 알기 위해서는 조금 더 자세히 부하의 원인을 살펴볼 필요가 있다.[2]

평균 부하

평균 부하(Load Average)는 프로세스의 상태의 상태 중 R과 D 상태에 있는 프로세스 개수의 1분, 5분, 15분마다의 평균값이다. 즉, 얼마나 많은 프로세스가 실행 중 혹은 실행 대기 중인지를 의미하는 수치이다. 평균 부하가 높다면 많은 수의 프로세스가 실행 중이거나 I/O 등을 처리하기 위한 대기 상태에 있다는 것이며, 낮다면 적은 수의 프로세스가 실행 중이거나 대기 중이라는 의미이다. 프로세스의 수를 세는 것이기 때문에 시스템에 있는 CPU 코어의 개수가 몇 개인지에 따라 각각의 값은 서로 가지고 있는 의미가 상대적이다.[3]

CPU 코어가 하나인 경우와 2개인 경우 둘 다 평균 부하 값은 2의 근삿값이 나오겠지만 그 의미는 서로 차이가 있다. 첫 번째 경우는 하나의 Run Queue에 두 개의 프로세스가 있으며 이 경우 한 번에 하나만 실행되기 때문에 나머지 하나의 프로세스는 대기 상태에 있을 수밖에 없다. 이는 현재 시스템이 처리할 수 있는 프로세스보다 좀 더 많은 프로세스가 있다는 뜻이다. 하지만 두 번째 경우는 첫 번째와 똑같이 Run Queue에 두 개의 프로세스가 있지만, 서로 다른 CPU에 있기 때문에 A와 B는 동시에 실행될 수 있다. 현재 시스템에 처리 가능한 만큼의 프로세스가 있는 것이다. 이처럼 같은 평균 부하라고 해도 CPU 코어가 몇 개인지에 따라 전혀 다른 의미일 수 있다.[4]

프로세스

프로세스는 프로그램이 OS에 의해 실행되고 있을 때 그 실행 단위가 되는 개념이다. 프로세스는 커널 내부에서의 실행 단위를 나타내는 태스크와는 구별되기는 하지만 넓은 의미에서는 거의 같다. 프로세스란 '프로그램의 명령'과 '실행 시에 필요한 정보' 조합의 오브젝트를 말한다. 리눅스 커널은 프로세스마다 프로세스 디스크립터라는 관리용 테이블을 생성한다. 이 프로세스 디스크립터에 각종 실행 시 정보가 저장된다. 리눅스 커널은 이 프로세스 디스크립터 군을 우선도가 높은 순으로 재배열해서 실행 순으로 'Process=Task'가 실행되도록 조정한다. 이 조정 역할을 하는 것이 프로세스 스케줄러이다. 프로세스 스케줄러에 의해서 관리되는 프로세스는 디스크립터에 아래와 같은 상태 값을 갖게 된다.[5]

상태 값 의미
TASK_RUNNING 실행 가능 상태로 언제든지 CPU에 의해서 처리가 가능한 상태이다.
즉, CPU에 의해서 실행 중(RUNNING)일 수도 있고 가능한 상태(RUNNABLE)일 수도 있다.
TASK_INTERRUPTIBLE 인터럽트(Interrupt)에 의해서 언제든지 상태가 변할 수 있는 대기 상태이다.
일반적인 대기 상태를 의미한다. (ex. sleep, 터미널의 입력 대기)
TASK_UNINTERRUPTIBLE 인터럽트에 의해서 상태가 변경되지 않는 대기 상태이다. 즉, 현재 대기의 대상이 끝날 때까지 방해받지 않고
대기하는 상태이다. 보통 I/O 처리에 대해서 대기 중인 상태이다.
TASK_STOPPED 동작이 중단된 상태이다. 일시 정지된 상태로 재개 시그널을 받을 때까지 멈춰있게 된다.
터미널에서 전면(Foreground)에 실행 중인 프로그램에 대해서 Ctrl+Z를 입력하면 이러한 상태로 진입하게 된다.

보다 더 많은 상태 값이 존재하지만 이는 커널 소스의 include/linux/sched.h 파일에서 확인할 수 있다.[6]

평균 부하로 환산되는 대기상태는 두 가지가 있다. 첫 번째는 CPU를 사용하고자 해도 다른 프로세스가 CPU를 사용하고 있어서 기다리고 있는 프로세스인 TASK_RUNNING 상태이고, 두 번째는 계속해서 처리하고자 해도 디스크 입출력이 끝날 때까지 기다려야만 하는 프로세스인 TASK_UNINTERRUPTIBLE 상태이다. 즉, 키보드 입력대기나 sleep에 의한 대기는 프로그램이 스스로 명시적으로 기다리는 것이므로 평균 부하에 포함되지 않는다. 원격호스트로부터 데이터 착신대기도 언제 데이터가 올지 불확실하므로 포함되지 않는다. 부하를 기준으로 보면 일반적으로 WAS는 CPU bound, DB는 I/O bound 서버이다.

  • CPU 사용률 계산법 : 평균 부하는 CPU에 엮인 실행 큐가 유지하고 있는 프로세스 디스크립터의 수를 센다. 또한 평균 부하 값이 저장되는 영역은 커널 내의 전역변수 배열이다. CPU 사용률의 계산 결과는 전역변수 배열이 아닌, 각 CPU 용으로 준비된 전용 영역에 저장된다. CPU 별로 지닌 영역에 데이터를 저장하므로 sar 등에서 CPU 별 정보를 얻을 수 있는 것이다. 커널은 프로세스 전환을 위해서 각 프로세스가 생성된 후부터 어느 정도 CPU 시간을 이용했는지를 프로세스별로 기록하고 있다. 이를 프로세스 어카운팅이라 한다. 스케줄러는 이 기록을 바탕으로 CPU 시간을 지나치게 사용하고 있는 프로세스의 우선도를 낮추거나 일정 이상으로 계산을 수행했다면 다른 프로세스로 CPU를 내어주는 작업을 수행한다.
  • I/O 대기율 계산법 : 리눅스는 한번 디스크에서 읽어들인 데이터는 가능한 한 메노리에 캐시를 해서 다음 번 이후의 디스크 읽기가 고속으로 수행되도록 조정한다. 이렇게 메모리에 읽어들인 데이터의 캐시를 페이지 캐시라고 한다. 리눅스는 메모리 영역을 4KB 블록 단위로 관리한다. 이 4KB 블록을 페이지라고 한다. 즉 디스크로부터 데이터를 읽어들이는 것은 다름 아닌 페이지 캐시를 구축하는 것이다. 읽어들인 데이터는 페이지 캐시에서 사용자 공간으로 전송된다. 따라서 처리할 데이터량에 비례해서 메모리를 증설하는 것이 I/O 부하를 줄이는 가장 효과적인 방법이다. 메모리를 증설할 수 없을 경우에는 데이터를 분할해서 각각의 서버에 위치시키는 것을 검토하여야 한다. 데이터를 적절하게 분할하면 단순히 서버 대수를 늘린 만큼 디스크 I/O 회수만 줄어드는 것이 아니라, 캐시에 올릴 데이터의 비율이 늘어나므로 상당한 전송량 향상을 기대할 수 있다.[5]

계산 과정

uptime 명령
# uptime 
07:35:37 up 20 days, 12:45, 1 user, load average : 0.00, 0.00, 0.00

# strace -s 65535 -f -t -o uptime_dump uptime
07:36:26 up 20days, 12:59, 1 user, load average : 0.00, 0.00, 0.00

생성된 덤프 파일을 편집기로 열어서 살펴볼 시 execve()를 통해서 bash가 uptime 명령을 실행시키고, 관련된 라이브러리 파일들을 읽는 과정을 확인할 수 있다. 그중 덤프 파일 하단부에는 아래와 같은 내용을 확인할 수 있다.

8011 07:36:26 open("/proc/loadavg", O_RDONLY) = 4
8011 07:36:26 lseek(4, 0, SEEK_SET) = 0
8011 07:36:26 read(4, "0.00 0.00 0.00 2/128 8011\n", 2047) = 26
8011 07:36:26 fstat(1, {st_mode=S_IFCHR|0620, st_rdev = makedev(136, 0), ...}) = 0

uptime 명령은 /proc/loadavg 파일을 열어서 그 파일의 내용을 읽고 화면에 출력해 주는 명령이다. 즉, 직접 평균 부하 값을 계산하는 게 아니고 커널이 미리 준비해둔 /proc/loadavg를 단순히 읽어서 보여주는 명령이다.[4]

/proc/loadavg 내용
# cat /proc/loadavg

0.00 0.00 0.00 1/127 8027

uptime에서 볼 수 있었던 값들이 기록되어 있다. 이 값을 어떻게 만들어지는지 알아보기 위하여 우선 커널 코드를 살펴봐야 한다. 커널의 동작 원리를 분석하기 위해 어디서 시작할지 모를 때에는 strace로 시스템 콜을 분석해서 시작점을 찾는 방법도 유용하다. /proc 파일 시스템과 관련된 커널 소스는 fs/proc/에 위치해있다. 그중 loadavg 파일과 관련된 파일은 fs/proc/loadavg.c 파일이다. 이 파일을 보면 loadavg_proc_show() 함수를 볼 수 있다.

static int loadavg_proc_show(struct seq_file *m, void *v)
{
    unsigned long avnrun[3];
    get_avenrun(avnrun, FIXED_1/200, 0);

    seq_printf(m, "%lu.%02lu %lu.%02lu %lu.%02lu %ld/%d %d\n",
        LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]), 
        LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]),
        LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]),
        nr_running(), nr_treads,
        task_active_pid_ns(current) -> last_pid);
    return 0;
}

이 함수를 통해서도 실제 계산되는 과정을 찾을 수는 없다. 이 함수 역시 내부적으로 계산된 값을 보여주는 함수이기 때문이다. get_avenrun() 함수를 통해 배열에 값을 넣는다는 사실이 도출된 것으로 이번에는 해당 함수를 찾아보아야 한다. 커널 함수를 찾는 방법에는 여러 가지가 존재하지만, 가장 쉽고 단순한 방법 중 하나인 grep을 사용하는 빈도가 많다.

#grep -R get_avenrun ./*
....
./kernel/sched.c:void get_avenrun)(unsigned long *loads, unsigned long offset, int shift)
...

kernel/sched.c 파일에 get_avenrun 함수가 정의되어 있다. 해당 함수의 내용은 아래와 같다.

void get_avenrun(unsigned long *loads, unsigned long offset, int shift){
    loads[0] = (avenrun[0] + offset) << shift;
    loads[1] = (avenrun[1] + offset) << shift;
    loads[2] = (avenrun[2] + offset) << shift;
}

이 함수는 unsinged long 형태의 배열을 인자로 받아서 해당 배열에 값을 넣어주는 함수인데, 중요한 배열이 하나 더 있다. 바로 avenrun 배열로, 인자로 받은 loads 배열에 avenrun 배열에 있는 값을 토대로 계산한 값을 넣어준다.[4]

grep으로 avenrun 배열 찾기
# grep -R avenrun ./*

...
./kernel/sched.c: avenrun[0] = calc_load(avenrun[0], EXP_1, active);
...

이 부분에서 제일 중요한 점은 calc_load() 함수와 관련된 부분이다. 해당 로직은 calc_global_load() 함수에 있다.[4]

clac_global_load() 함수
void calc_global_load(void)
{
    unsigned long upd = calc_load_update + 10;
    long active;

    if(time_before(jiffies, upd));
        return;

    //calc_load_task 값을 atomic_long_read()라는 매크로를 통해 읽어온 후 active 값에 넣는다.

    active = atomic_long_read(&calc_load_tasks);
    active = active > 0 ? active * FIXED_1 : 0;

    //active 값을 바탕으로 avenrun[] 배열에 있는 값들을 calc_load() 함수를 이용해서 계산한다.
    avenrun[0] = calc_load(avenrun[0], EXP_1, active);
    avenrun[1] = calc_load(avenrun[1], EXP_2, active);
    avenrun[2] = calc_load(avenrun[2], EXP_15, active);

    calc_load_update += LOAD_FREQ;
}

active 변수와 calc_load() 함수를 보면 먼저 active 변수의 값을 알기 위해서는 calc_load_tasks가 어떤 값을 가지게 되는지 살펴봐야 한다. grep으로 찾다 보면 아래와 같은 함수를 볼 수 있다.[4]

active 변수
static void calc_load_account_active(</font>struct</font> rq *this_rq)
{
    long nr_active, delta;

    //nr_active 변수에 Run Queue를 기준으로 nr_running 상태의 프로세스 개수를 입력한다. 이 프로세스들이 바로 R 상태의 프로세스다.
    nr_active = this_rq->nr_running;
    //nr_ative 변수에 Run Queue를 기준으로  nr_uninterruptible 상태의 프로세스 개수를 더해준다. 이 프로세스들이 바로 D 상태의 프로세스다.
    nr_active += (long) this_rq->nr_uninterruptible;

    if</font>(nr_active != this_rq->calc_load_active){
        delta = nr_active - this_rq->calc_load_active;
        this_rq->calc_load_active = nr_active;
        //nr_active 값이 기존에 계산된 값과 다르다면 그 차이 값을 구한 후 calc_load_tasks 변수에 입력한다.
        atomic_long_add(delta, &calc_load_tasks);
    }
}
평균 부하 계산 과정

이렇게 cpu_load_account_active() 함수가 매번 Tick 주기마다 깨어나서 현재 CPU의 Run Queue에 있는 nr_running 프로세스의 개수와 nr_uninterruptible 프로세스의 개수를 세어서 calc_load_tasks 변수에 넣어준다. 그 후 5초 간격으로 calc_global_load() 함수가 calc_load_tasks 변숫값을 바탕으로 1, 5, 15분 마다의 평균 Load Average를 계산해서 넣어준다.[4]

이 과정을 정리하면 우측 그림과 같다. 그림을 보면 Kernel Timer가 두 함수를 주기적으로 호출한다. 먼저 calc_load_account_active()가 더 잦은 빈도로 호출되며 그때마다 calc_load_tasks의 변수를 갱신한다. 그후 calc_global_load()함수가 호출되어 내부적으로 active변수에 calc_load_tasks의 값을 저장하고 calc_load()함수를 통해서 최종적으로 계산된 값을 avenrun[]배열에 저장한다. 과정이 복잡해보일 수 있지만 결국 프로세스의 개수를 센다는 점만 기억하면 된다.

결과 확인

평균 부하 값은 시스템에 부하가 있다는 것을 알려주지만 구체적으로 어떤 부하인지는 알 수 없다. 어떤 부하가 일어나는지에 대한 정보는 vmstat을 통해서 확인할 수 있다.

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0  18432 292836     84 4678216    0    0     6   414    0    0  4  2 94  0  0
 2  0  18432 292420     84 4678652    0    0     0   140 6183 5941  2  1 97  0  0
 1  0  18432 292188     84 4678476    0    0     0   158 6345 6542  1  1 98  0  0
 0  0  18432 292332     84 4678648    0    0     0    52 6331 6393  1  1 98  0  0
 0  0  18432 301156     84 4671288    0    0   100   156 9068 8254  3  2 95  0  0
 1  0  18432 300888     84 4670744    0    0     0   160 8850 8926  2  1 96  0  1
 0  0  18432 300448     84 4671516    0    0     4   979 5865 4931  4  1 95  0  0

CPU 부하 프로세스는 r에 표시되고 I/O 부하 프로세스는 b 컬럼에 표시되게 된다. r과 b가 각각 무엇을 의미하는지 man 페이지의 설명을 확인해볼 시 아래와 같다.

Procs

           r: The number of processes waiting for run time.

           b: The number of processes in uninterruptible sleep.

r는 실행되기를 기다리거나 현재 실행되고 있는 프로세스의 개수를, b는 I/O를 위해 대기열에 있는 프로세스의 개수를 말한다. 즉 각각이 nr_running, nr_uninterruptible을 의미한다고 볼 수 있다. vmstat으로 확인하면 CPU가 일으키는 평균 부하인지, 아니면 I/O가 일으키는 평균 부하인지 확인해볼 수 있다. 사실 I/O가 일으키는 평균 부하 값이 1이나 2 정도로 낮은 편에 속한다고 해도(CPU가 1개 혹은 2개인 경우에는 높은 수준의 값이지만) 시스템에 문제를 일으킬 수 있는 소지가 있다. 지속적으로 I/O를 일으키는 프로세스가 시스템에 존재한다는 것을 의미하며, 의도하지 않은 불필요한 프로세스일 가능성이 있다.[3]

비교

CPU Bound와 I/O Bound를 비교해보면, 결과적으로 평균 부하는 상대적인 값이 아니고 계산하는 순간을 기준으로 존재하는 nr_running 상태의 프로세스 개수와 nr_uninterruptible 상태의 프로세스 개수를 합한 값을 바탕으로 계산되는 것이다. 평균 부하가 높다는 것은 단순히 CPU를 사용하려는 프로세스가 많다는 것을 의미하는 것이 아니고, I/O에 병목이 생겨서 I/O 작업을 대기하는 프로세스가 많을 수도 있다는 의미이다. 평균 부하 값만으로는 시스템에 어떤 상태의 부하가 일어나는지 확인하기 어렵다는 뜻이기도 하다. 부하를 일으키는 프로세스는 크게 두 가지 종류로 나눌 수 있다. nr_running으로 표현되는, CPU 자원을 많이 필요로 하는 CPU 바운드 프로세스와 nr_uninterruptible로 표현되는, 많은 I/O 자원을 필요로 하는 I/O 바운드 프로세스이다. 테스트 프로그램을 하나 만들어서 해당 프로세스들이 각각 어떻게 평균 부하로 표현되는지 살펴봐야 한다. 두 종류의 부하 프로세스는 같은 평균 부하를 보여준다고 해도 사실 일으키고 있는 부하는 전혀 다른 부하이다. 전자의 경우는 CPU가 너무 많이 사용해서 발생하는 부하이고, 후자의 경우는 I/O 리소스를 너무 많이 사용해서 발생하는 부하이다. 어떤 부하인지가 중요한 이유는, 부하의 종류에 따라서 해결 방법이 달라지기 때문이다. 평균 부하가 높다고 해서 단순히 CPU가 더 많은 장비를 사용하는 것으로 해결할 수 없다는 의미이다. 또한 비슷한 평균 부하라 하더라도 부하를 일으키는 원인이 무엇이냐에 따라 시스템의 반응 속도가 전혀 다를 수 있다.[3]

영향

부하를 일으키는 원인이 무엇인지에 같은 수준의 평균 부하일지라도 시스템에 끼치는 영향이 같을 수도 다를 수도 있다. CPU 기반의 부하를 일으키는 10개의 프로세스를 생성하고, GET 요청에 대한 응답시간을 측정한다. top 명령을 통해 프로세스 상태를 살펴보면, 이미 돌고 있는 10개의 스크립트들이 CPU를 차지하고 있고 요청을 처리하기 위한 엔진엑스자바 프로세스들이 중간 사이에 끼어들어서 실행되고 있는 것을 확인할 수 있다. 엔진엑스와 자바를 방해하는 프로세스가 없는 상태보다는 응답 속도가 느려질 수밖에 없는 상황이다. I/O 기반의 부하를 일으키는 10개의 프로세스를 생성하고, GET 요청에 대한 응답시간을 측정한다. 위의 경우와 같이 10개의 프로세스를 띄우기 때문에 uptime을 통해서 보는 평균 부하 값은 비슷하다. 하지만 앞선 케이스에 비해 응답속도가 빠르다. I/O 부하를 일으키는 스크립트들은 I/O 대기 상태이기 때문에 프로세스 상태가 D이다. 하지만 CPU 기반의 부하일때와는 다르게 엔진엑스와 자바의 CPU 사용량이 더 많다. 이는 CPU에 대한 경합이 전자의 경우보다 덜하기 때문에 더 빠른 응답 속도를 보여줄 수 있다는 의미이다. 즉, 돌리고 있는 프로세스가 어떤 시스템의 자원을 많이 쓰느냐에 따라서 부하가 시스템에 미치는 영향이 다르다는 것이다. 이를 통하여 서로 다른 형태의 부하는 시스템의 성능에 다른 영향을 끼친다는 것을 알 수 있다. 하지만, 커널은 완벽하지 않기 때문에 버그가 있을 수 있으며, 커널 버전이 달라지면 잘 알고 있는 모니터링용 지표가 제대로 수집되지 않을 가능성도 있다. 그렇기 대문에 하나의 지표로만 모니터링하거나 시스템의 상태를 확인하지 말고 다양한 툴들과 지표를 조합해서 운영해야 한다.[3]

부하분산

부하분산(load balancing)컴퓨터 네트워크 기술의 일종으로 둘 혹은 셋 이상의 중앙처리장치 혹은 저장 장치와 같은 컴퓨터 자원들에게 작업을 나누어 동일기능의 복수 프로세서로 부하를 분담하는 방식을 말한다. 이로써 가용성 및 응답시간을 최적화 시킬 수 있다. 예를 들어, 메인 프레임 1대(단일 구성체)보다 IA-32와 같은 일반적인 서버(복합 구성체)가 안정성 면에서 유리한 위치에 있다. 부하분산 서비스는 그에 적합한 하드웨어와 소프트웨어에 의해 제공된다. 이 기술은 보통 내부 네트워크를 이용한 병렬처리(특히, 고가용성의 병렬처리)에 사용된다. 부하분산을 위한 대부분의 응용 프로그램은 다수의 서버(다른 말로, 서버 팜)를 가지고 한 가지 종류의 인터넷 서비스를 지원하는 방식이다. 보통 부하분산은 트래픽이 많은 웹 사이트, IRC 네트워크, FTP 사이트, NNTP 서버 그리고 DNS 서버에 적용이 되고 있다. 인터넷 서비스를 위해서는 소프트웨어를 이용한 부하분산이 적용되며, 이 소프트웨어는 중간에 위치에 실제 서비스하는 서버와 클라이언트를 포트를 이용해 중개하고 있다. 그러나 사용자는 이를 알아차리지 못한다. 이를 투명성이라 한다. 또한, 보안이라는 측면에서 내부 네트워크 구조를 숨김으로써 크래킹을 막을 수 있다. 일부 부하분산 소프트웨어는 실서비스 서버들을 관리하는 역할을 수행하기도 한다(예를 들어, 서버 다운 혹은 백업에 관련된 작업과 같은 부분). 부하분산의 다른 방식으로는 라운드 로빈 DNS라고 하는 특별한 하드웨어 및 소프트웨어가 필요가 없는 방식이 있다. 이 방식에서는 여러 개의 IP 주소를 동일한 도메인 네임에 연관 지어 놓고 클라이언트들이 어떤 서버를 사용할 것인지 결정하게 하는 방식이다. 일반적인 부하분산과는 다르게 "투명성"이 존재하지 않는다. 왜냐하면 이미 내부의 서버들의 주소가 이미 노출되어 있기 때문이다. 이 방식은 장단점이 혼재되어 있다. DNS 서버에 대한 의존도가 높고 부하분산이 원하는 대로 될 수 있다는 것이다.[7]

각주

  1. 부하 나무위키 - https://namu.wiki/w/%EB%B6%80%ED%95%98
  2. injae Kim, 〈서버에 걸리는 부하, 추측하지 말고 계측하자〉, 《데브로그》, 2020-07-09
  3. 3.0 3.1 3.2 3.3 12bme, 〈(엔지니어링) Load Average와 시스템 부하〉, 《티스토리》, 2019-12-25
  4. 4.0 4.1 4.2 4.3 4.4 4.5 임지후, 〈3.Load Average와 시스템 부하〉, 《깃북스》, 2016-02-19
  5. 5.0 5.1 가그린민트, 〈부하란 무엇인가?〉, 《티스토리》, 2019-04-21
  6. Lunatine, 〈Load Average에 대하여〉, 《루나틴 박스》, 2016-02-19
  7. 부하분산 위키백과 - https://ko.wikipedia.org/wiki/%EB%B6%80%ED%95%98%EB%B6%84%EC%82%B0

참고자료

같이 보기


  검수요청.png검수요청.png 이 부하 (컴퓨터) 문서는 하드웨어에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.