영구 디스크 성능 최적화

영구 디스크 최적화

영구 디스크는 VM의 사용량이 성능 한도에 도달하기에 충분한 경우 디스크 유형 차트에 기술된 성능을 제공합니다. 성능 요구에 맞게 영구 디스크 볼륨 크기를 적절하게 조정한 후에는 앱 및 운영체제에 일부 조정이 필요할 수 있습니다

다음 섹션에서는 성능 향상을 위해 조정할 수 있는 몇 가지 핵심 요소와 이를 특정 유형의 워크로드에 적용하는 방법에 대해 설명합니다.

I/O 과부하를 50TB 스팬으로 제한

I/O 과부하를 50TB 스팬으로 제한하면 최대 성능에 도달합니다. 합쳐서 50TB 이하인 개별 영구 디스크들에 있는 스팬은 성능을 계산할 때 단일 50TB 스팬과 동일하게 간주될 수 있습니다. 스팬은 단일 물리적 디스크에 있는 논리적 블록 주소의 연속된 범위를 나타냅니다.

지연 초기화 중지 및 DISCARD 명령 사용

영구 디스크에서는 블록이 더 이상 사용되지 않을 때 운영체제가 디스크에 이를 알릴 수 있게 해주는 DISCARD 또는 TRIM 명령이 지원됩니다. DISCARD 지원을 통해서는 OS가 블록을 제로로 만드는 비용 없이 디스크 블록을 더 이상 필요하지 않은 것으로 표시할 수 있습니다.

대부분의 Linux 운영체제에서는 인스턴스에 영구 디스크를 마운트할 때 DISCARD를 사용하도록 설정합니다. Windows Server 2012 R2 인스턴스에서는 영구 디스크를 마운트할 때 기본적으로 DISCARD가 사용으로 설정됩니다. Windows Server 2008 R2에서는 DISCARD가 지원되지 않습니다.

DISCARD를 사용하도록 설정하면 일반적인 런타임 성능이 향상되며, 처음 마운트될 때 디스크의 성능이 크게 향상될 수 있습니다. 전체 디스크 볼륨을 포맷하기 위해서는 시간이 오래 걸릴 수 있으므로 '지연 포맷'이라는 방식이 일반적으로 사용됩니다. 지연 포맷의 단점은 볼륨이 마운트되는 최초에 비용이 자주 지불된다는 것입니다. 지연 초기화를 중지하고 DISCARD 명령어를 사용하면 포맷 및 마운트를 빠르게 수행할 수 있습니다.

  • 다음 매개변수를 mkfs.ext4로 전달하여 포맷 중 지연 초기화를 중지하고 DISCARD를 사용합니다.

    -E lazy_itable_init=0,lazy_journal_init=0,discard
    

    lazy_journal_init=0 매개변수는 CentOS 6 또는 RHEL 6 이미지를 사용하는 인스턴스에서는 작동하지 않습니다. 이러한 인스턴스에서는 이 매개변수 없이 영구 디스크를 포맷합니다.

    -E lazy_itable_init=0,discard
    
  • 다음 플래그를 마운트 명령어에 전달하여 마운트 시에 DISCARD 명령어를 사용합니다.

    -o discard
    

영구 디스크는 discard 옵션이 사용된 상태에서 제대로 작동합니다. 하지만 discard 옵션을 사용하는 것 외에 추가로 또는 이를 사용하지 않은 상태로 선택적으로 fstrim을 주기적으로 실행할 수 있습니다. discard 옵션을 사용하지 않을 경우에는 디스크 스냅샷을 만들기 전에 fstrim을 실행합니다. 파일 시스템을 자르면 더 작은 스냅샷 이미지를 만들어서 스냅샷 저장 비용을 줄일 수 있습니다.

I/O 큐 깊이

많은 앱에는 해당 I/O 큐 깊이에 영향을 주는 설정이 있습니다. 큐가 깊을수록 IOPS가 늘어나지만 지연 시간도 늘어날 수 있습니다. 큐가 얕을수록 I/O당 지연 시간이 줄어들지만 최대 IOPS가 줄어드는 결과를 초래할 수도 있습니다.

캐시 미리 읽기

I/O 성능을 높이기 위해 운영체제에서는 파일 읽기가 요청될 때 후속 데이터도 읽기가 요청될 가능성이 있다는 가정에 따라 요청된 것보다 많은 양의 파일을 메모리로 읽어들이는 미리 읽기와 같은 기법이 사용됩니다. 미리 읽기 양이 늘어날수록 처리량이 늘어나지만 메모리 및 IOPS 효율이 낮아집니다. 미리 읽기 양이 줄어들면 IOPS가 높아지지만 처리량이 줄어듭니다.

Linux 시스템의 경우 blockdev 명령어를 사용하여 미리 읽기 값을 가져오고 설정할 수 있습니다.

$ sudo blockdev --getra /dev/[DEVICE_ID]
$ sudo blockdev --setra [VALUE] /dev/[DEVICE_ID]

미리 읽기 값은 <desired_readahead_bytes>/512바이트입니다.

예를 들어 미리 읽기를 8MB로 지정할 경우, 8MB는 8,388,608바이트(8 * 1024 * 1024)입니다.

8388608 bytes / 512 bytes = 16384

blockdev를 16384으로 설정합니다.

$ sudo blockdev --setra 16384 /dev/[DEVICE_ID]

사용 가능한 CPU

영구 디스크에 대해 읽기 및 쓰기를 하려면 VM에서 CPU 주기를 사용할 수 있어야 합니다. 매우 높고 일관성 있는 IOPS 수준을 달성하려면 I/O 처리를 위해 사용 가능한 CPU가 필요합니다.

IOPS 기준의 작업 부하

SQL 또는 NoSQL에 관계없이 데이터베이스는 데이터에 대한 임의 액세스 사용 패턴을 갖고 있습니다. Google은 IOPS를 기준으로 한 워크로드에 다음 값을 권장합니다.

  • 각 400~800 IOPS당 I/O 큐 깊이 1, 대규모 볼륨에서 최대 한도 64

  • 임의 읽기 IOPS 2,000당 사용 가능한 CPU 1개 및 임의 쓰기 IOPS 2,500당 사용 가능한 CPU 1개

MongoDB, Apache Cassandra, 기타 데이터베이스 애플리케이션의 권장사항 문서에서는 일반적으로 낮은 미리 읽기 값이 권장됩니다.

처리량 지향 워크로드

Hadoop 작업과 같은 스트리밍 작업은 빠른 순차적 읽기가 유용하며, I/O 크기가 클수록 스트리밍 성능이 향상될 수 있습니다. 처리량을 기준으로 한 작업 부하의 경우에는 256KB 이상의 I/O 크기가 권장됩니다.

표준 영구 디스크 성능 최적화

표준 영구 디스크의 처리량 수준을 극대화하려면 다음 권장사항을 따르세요.

  • 가능한 경우 병렬 순차 IO 스트림 사용

    시스템은 실제 HDD 하드 드라이브와 비슷한 순차 디스크 액세스에 맞춰 IO 성능을 최적화하도록 설계되었기 때문에 표준 영구 디스크에서 순차 IO를 사용합니다.

    IO를 여러 순차 스트림에 분산하면 성능이 크게 향상됩니다. 최상의 일관성을 얻으려면 8개 이상의 순차 스트림을 사용합니다.

  • 큰 IO 크기 사용

    표준 영구 디스크는 한도에 도달했을 때 처리량이 매우 높습니다. IOPS 제한과 지연 시간이 애플리케이션에서 병목 현상을 일으키지 않도록 256KB 이상의 최소 IO 크기를 사용합니다.

    분산 파일 시스템 애플리케이션에는 큰 스트라이프 크기를 사용합니다. 큰 스트라이프 크기(예: 4MB 이상)를 사용하는 임의 IO 워크로드는 워크로드가 여러 순차 스트림 디스크 액세스를 유사하게 모방하기 때문에 표준 영구 디스크에서 뛰어난 성능을 발휘합니다.

  • 동시 로드가 충분한 I/O 제공

    운영체제의 동시 로드를 활용하도록 큐를 최대한 깊게 사용합니다. 충분히 깊은 큐를 사용하는 것은 표준 영구 디스크가 한도에 도달했을 때 IO 지연 시간으로 인한 애플리케이션 병목 현상을 일으키지 않고 처리량을 달성할 수 있도록 하는 데 특히 중요합니다.

SSD 영구 디스크 성능 최적화

디스크 유형별 성능 차트에서는 솔리드 스테이트 영구 디스크에서 얻을 수 있는 예상 최대 성능을 보여줍니다. 이러한 속도를 얻기 위해 앱 및 VM 인스턴스를 최적화하려면 다음 권장사항을 따르세요.

  • 앱이 I/O를 충분히 생성하고 있는지 확인

    앱이 이전 차트에 표시된 한도보다 낮은 IOPS를 실행 중이면 해당 IOPS 수준에 도달할 수 없습니다. 예를 들어 500GB 디스크에서 예상 IOPS 한도는 15,000 IOPS입니다. 하지만 이보다 적은 IOPS를 생성하거나 I/O 작업 크기가 8KB보다 크면 15,000 IOPS를 달성할 수 없습니다.

  • 동시 로드가 충분한 I/O 제공

    운영체제의 동시 로드를 활용하도록 충분히 깊은 큐를 사용합니다. 1,000 IOPS를 제공하지만 큐 깊이가 1인 상태에서 동기식으로 수행할 경우, 차트에 설명된 한도보다 IOPS가 훨씬 낮아집니다. 최소한 앱의 큐 깊이가 400~800 IOPS당 1 이상이어야 합니다.

  • I/O를 생성하는 인스턴스에서 사용 가능한 CPU가 충분한지 확인

    VM 인스턴스에서 CPU가 부족하면 앞서 설명한 IOPS를 앱이 관리할 수 없습니다. 예상 트래픽의 2,000~2,500 IOPS당 사용 가능한 CPU가 1개씩 있는 것이 좋습니다.

  • 큰 디스크에서 합리적인 수준의 일시적 데이터 지역성에 맞게 앱이 최적화되었는지 확인

    앱이 장시간에 걸쳐 디스크의 여러 부분에 분산되어 있는 데이터에 액세스할 경우(vCPU당 수백 GB), 최적의 IOPS를 얻을 수 없습니다. 최상의 성능을 위해서는 디스크 단편화, 디스크에서 액세스되는 부분의 임의성과 같은 요소들을 고려해서 일시적인 데이터 지역성에 맞게 최적화해야 합니다.

  • OS에서 I/O 스케줄러가 특정 요구를 충족하도록 구성되었는지 확인

    Linux 기반 시스템에서는 I/O 스케줄러를 noop로 설정하여 SSD 지원 기기에서 가장 높은 IOPS 수를 얻을 수 있습니다.

SSD 영구 디스크 성능 벤치마킹

다음 명령어는 기기가 2,500GB PD-SSD라고 가정합니다. 기기 크기가 이와 다를 경우에는 --filesize 인수의 값을 수정하세요. 이 디스크 크기는 vCPU가 32개인 VM의 처리량 한도를 달성하는 데 필요합니다.

    # Install dependencies
    sudo apt-get update
    sudo apt-get install -y fio
  1. 디스크에 0이 아닌 데이터를 채웁니다. 빈 블록에서의 영구 디스크 읽기는 데이터를 포함하는 블록에서의 읽기와 지연 시간 프로필이 다릅니다. 모든 읽기 지연 시간 벤치마크를 실행하기 전에 디스크를 채우는 것이 좋습니다.

    # Running this command causes data loss on the second device.
    # We strongly recommend using a throwaway VM and disk.
    sudo fio --name=fill_disk \
      --filename=/dev/sdb --filesize=2500G \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=128K --iodepth=64 --rw=randwrite
    
  2. I/O 크기가 1MB이고 I/O 깊이가 64 이상인 동시 스트림이 여러 개(8개 이상)인 경우 순차적 쓰기를 수행하여 쓰기 대역폭을 테스트합니다.

    # Running this command causes data loss on the second device.
    # We strongly recommend using a throwaway VM and disk.
    sudo fio --name=write_bandwidth_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=1M --iodepth=64 --rw=write --numjobs=8 --offset_increment=100G
    
  3. 쓰기 IOPS를 테스트합니다. 최대 PD IOPS를 달성하기 위해서는 깊은 I/O 큐를 유지해야 합니다. 예를 들어 쓰기 지연 시간이 1밀리초인 경우 VM이 처리 중인 각 I/O에 대해 최대 1,000의 IOPS를 달성할 수 있습니다. 15,000 IOPS를 달성하기 위해서는 VM이 처리 중인 I/O를 최소한 15 이상으로 유지해야 합니다. 디스크 및 VM이 30,000 IOPS를 달성할 수 있는 경우 처리되는 I/O 수는 최소 30개 이상이어야 합니다. I/O 크기가 4KB보다 큰 경우에는 IOPS 한도에 도달하기 전에 VM이 대역폭 제한에 걸릴 수 있습니다.

    # Running this command causes data loss on the second device.
    # We strongly recommend using a throwaway VM and disk.
    sudo fio --name=write_iops_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=4K --iodepth=64 --rw=randwrite
    
  4. 쓰기 지연 시간을 테스트합니다. I/O 지연 시간을 테스트할 때는 VM이 최대 대역폭 또는 IOPS에 도달하지 않아야 합니다. 그렇지 않으면 관찰된 지연 시간에 실제 영구 디스크 I/O 지연 시간이 반영되지 않습니다. 예를 들어 I/O 깊이 30에서 IOPS 제한에 도달하고 fio 명령어 시간이 두 배가 될 경우, 총 IOPS는 동일하게 유지되고 보고되는 I/O 지연 시간이 두 배가 됩니다.

    # Running this command causes data loss on the second device.
    # We strongly recommend using a throwaway VM and disk.
    sudo fio --name=write_latency_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=4K --iodepth=4 --rw=randwrite
    
  5. I/O 크기가 1MB이고 I/O 깊이가 64 이상인 동시 스트림이 여러 개(8개 이상)인 경우 순차적 읽기를 수행하여 읽기 대역폭을 테스트합니다.

    sudo fio --name=read_bandwidth_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=1M --iodepth=64 --rw=read --numjobs=8 --offset_increment=100G
    
  6. 읽기 IOPS를 테스트합니다. 최대 PD IOPS를 달성하기 위해서는 깊은 I/O 큐를 유지해야 합니다. 예를 들어 I/O 크기가 4KB보다 큰 경우에는 IOPS 한도에 도달하기 전에 VM이 대역폭 제한에 걸릴 수 있습니다.

    sudo fio --name=read_iops_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=4K --iodepth=64 --rw=randread
    
  7. 읽기 지연 시간을 테스트합니다. 실질적으로 지연 시간을 측정하기 위해서는 디스크에 데이터를 채우는 것이 중요합니다. 영구 디스크가 포화 한도에 도달하면 수신 I/O가 푸시백되고 이로 인해 I/O 지연 시간이 인위적으로 증가하므로 이 테스트 중에는 VM이 IOPS 또는 처리량 한도에 도달하지 않도록 하는 것이 중요합니다.

    sudo fio --name=read_latency_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --bs=4K --iodepth=4 --rw=randread
    
  8. 순차적 읽기 대역폭을 테스트합니다.

    sudo fio --name=read_bandwidth_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --numjobs=4 --thread --offset_increment=500G \
      --bs=1M --iodepth=64 --rw=read
    
  9. 순차적 쓰기 대역폭을 테스트합니다.

    sudo fio --name=write_bandwidth_test \
      --filename=/dev/sdb --filesize=2500G \
      --time_based --ramp_time=2s --runtime=1m \
      --ioengine=libaio --direct=1 --verify=0 --randrepeat=0 \
      --numjobs=4 --thread --offset_increment=500G \
      --bs=1M --iodepth=64 --rw=write
    

다음 단계

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Compute Engine 문서