OSS 사용 권장사항

OSS 사용 권장사항

이 페이지는 ClickHouse Cloud에는 적용되지 않아요. 여기 문서화된 절차는 ClickHouse Cloud 서비스에서 자동화되어 있어요.

출처: 문서

본문

CPU 스케일링 거버너 (CPU Scaling Governor)

항상 performance 스케일링 거버너를 사용해요. on-demand 스케일링 거버너는 지속적으로 높은 수요에서 훨씬 더 나쁘게 동작해요.

$ echo 'performance' | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

CPU 제한 (CPU Limitations)

프로세서는 과열될 수 있어요. dmesg를 사용해 CPU 클록 속도가 과열로 인해 제한되었는지 확인해요. 이 제한은 데이터센터 수준에서 외부적으로 설정될 수도 있어요. turbostat를 사용해 부하 상태에서 모니터링할 수 있어요.

RAM

적은 양의 데이터(~200 GB 압축까지)에는 데이터 양만큼 많은 메모리를 사용하는 것이 가장 좋아요. 대량의 데이터와 대화형(온라인) 쿼리를 처리할 때는 페이지 캐시에 hot 데이터 부분집합이 들어갈 수 있도록 합리적인 양의 RAM(128 GB 이상)을 사용해야 해요. 서버당 약 50 TB의 데이터 양에서도 128 GB의 RAM을 사용하면 64 GB보다 쿼리 성능이 크게 개선돼요. overcommit을 비활성화하지 마세요. cat /proc/sys/vm/overcommit_memory 값은 0 또는 1이어야 해요. 다음을 실행하세요:

$ echo 0 | sudo tee /proc/sys/vm/overcommit_memory

perf top을 사용해 메모리 관리에 커널에서 소비되는 시간을 관찰해요. 영구 huge pages도 할당할 필요가 없어요.

16GB 미만의 RAM 사용

권장 RAM 용량은 32 GB 이상이에요. 시스템에 16 GB 미만이면 기본 설정이 이 메모리 양과 맞지 않아 다양한 메모리 예외가 발생할 수 있어요. 적은 양의 RAM(최저 2 GB)이 있는 시스템에서 ClickHouse를 사용할 수 있지만, 이러한 설정은 추가 튜닝이 필요하고 낮은 속도로만 수집할 수 있어요. 16GB 미만의 RAM으로 ClickHouse를 사용할 때 다음을 권장해요:

  • config.xml에서 mark 캐시 크기를 낮춰요. 최저 500 MB까지 설정할 수 있지만 0으로 설정할 수는 없어요.
  • 쿼리 처리 스레드 수를 1로 낮춰요.
  • max_block_size8192로 낮춰요. 1024까지 낮은 값도 여전히 실용적이에요.
  • max_download_threads1로 낮춰요.
  • input_format_parallel_parsingoutput_format_parallel_formatting0으로 설정해요.
  • 로그 테이블 작성('asynchronous_metric_log, metric_log, text_log, trace_log' 포함)을 비활성화하세요. 로그 테이블 작성은 백그라운드 병합 작업이 로그 테이블 병합을 수행할 RAM을 예약해두기 때문이에요.

추가 참고:

  • 메모리 할당자가 캐시한 메모리를 flush하려면 SYSTEM JEMALLOC PURGE 명령을 실행할 수 있어요.
  • 버퍼에 상당한 메모리가 필요하므로 저메모리 시스템에서는 S3나 Kafka 통합을 사용하지 말 것을 권장해요.

저장 서브시스템 (Storage Subsystem)

예산이 SSD를 허용하면 SSD를 사용해요. 그렇지 않으면 HDD를 사용해요. 7200 RPM SATA HDD로 충분해요. 디스크 선반이 부착된 더 적은 수의 서버보다 로컬 하드 드라이브가 많은 서버를 선호해요. 하지만 드문 쿼리가 있는 아카이브를 저장할 때는 선반이 동작해요.

RAID

HDD를 사용할 때 RAID-10, RAID-5, RAID-6 또는 RAID-50으로 결합할 수 있어요. Linux의 경우 소프트웨어 RAID(mdadm 사용)가 더 좋아요. RAID-10을 만들 때는 far 레이아웃을 선택해요. 예산이 허용하면 RAID-10을 선택해요. LVM 자체(RAID나 mdadm 없이)는 괜찮지만, LVM으로 RAID를 만들거나 mdadm과 결합하는 것은 덜 탐구된 옵션이라 실수(잘못된 chunk 크기 선택, chunk 정렬 불일치, 잘못된 raid 타입 선택, 디스크 정리 잊음)가 일어날 가능성이 더 높아요. LVM 사용에 자신이 있다면 사용하는 것에 반대할 것은 없어요. 디스크가 4개 이상이면 RAID-5 대신 RAID-6 (선호) 또는 RAID-50을 사용해요. RAID-5, RAID-6 또는 RAID-50을 사용할 때는 기본 값이 보통 최선이 아니므로 항상 stripe_cache_size를 늘려요.

$ echo 4096 | sudo tee /sys/block/md2/md/stripe_cache_size

정확한 수는 장치 수와 블록 크기로 공식 2 * num_devices * chunk_size_in_bytes / 4096을 사용해 계산해요. 대부분의 RAID 구성에는 64 KB 블록 크기로 충분해요. 평균 clickhouse-server 쓰기 크기는 약 1 MB(1024 KB)이므로 권장 stripe 크기도 1 MB예요. 블록 크기는 RAID 배열의 비패리티 디스크 수로 나눈 1 MB로 설정해 각 쓰기가 사용 가능한 모든 비패리티 디스크에 걸쳐 병렬화되도록 필요에 따라 최적화할 수 있어요. 블록 크기를 너무 작거나 너무 크게 설정하지 마세요. SSD에서는 RAID-0을 사용할 수 있어요. RAID 사용과 관계없이 데이터 보안을 위해 항상 복제를 사용해요. 긴 큐로 NCQ를 활성화해요. HDD에는 mq-deadline 또는 CFQ 스케줄러를, SSD에는 noop을 선택해요. 'readahead' 설정을 줄이지 마세요. HDD에는 쓰기 캐시를 활성화해요. OS에서 NVME와 SSD 디스크에 fstrim이 활성화되어 있는지 확인해요(보통 cronjob이나 systemd 서비스로 구현됨).

파일 시스템 (File System)

Ext4가 가장 안정적인 옵션이에요. 마운트 옵션 noatime을 설정해요. XFS도 잘 동작해요. 대부분의 다른 파일 시스템도 잘 동작해야 해요. FAT-32와 exFAT는 하드 링크가 없어 지원되지 않아요. ClickHouse가 자체적으로 더 잘 압축하므로 압축 파일 시스템을 사용하지 마세요. ClickHouse에 내장된 더 나은 암호화를 사용할 수 있으므로 암호화 파일 시스템은 권장하지 않아요. ClickHouse가 NFS에서 작동할 수는 있지만 가장 좋은 생각은 아니에요.

Linux 커널

오래된 Linux 커널을 사용하지 마세요.

네트워크 (Network)

IPv6를 사용한다면 라우트 캐시 크기를 늘려요. 3.2 이전의 Linux 커널은 IPv6 구현에 많은 문제가 있었어요. 가능하면 최소 10 GB 네트워크를 사용해요. 1 Gb도 동작하지만, 수십 테라바이트의 데이터로 레플리카를 패치하거나 많은 양의 중간 데이터로 분산 쿼리를 처리할 때는 훨씬 더 나빠요.

Huge Pages

항상 transparent huge pages를 madvise로 설정해요. 오래된 커널(5.9 이전)에서 THP를 always로 설정하면 심각한 성능 저하를 일으킬 수 있어요 — 커널이 특히 64 GB+ RAM 시스템에서 메모리 조각 모음에 과도한 시간을 소비해요. 커널 5.9는 THP를 훨씬 더 잘 처리하는 선제적 compaction을 도입했지만, ClickHouse는 여전히 THP가 always로 설정되면 시작 시 경고하므로 커널 버전과 관계없이 madvise가 권장 설정이에요.

$ echo 'madvise' | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

transparent huge pages 설정을 영구적으로 수정하려면 /etc/default/grub을 편집해 GRUB_CMDLINE_LINUX_DEFAULT 옵션에 transparent_hugepage=madvise를 추가해요:

$ GRUB_CMDLINE_LINUX_DEFAULT="transparent_hugepage=madvise ..."

그 후 sudo update-grub 명령을 실행하고 재부팅하면 적용돼요.

하이퍼바이저 구성 (Hypervisor configuration)

OpenStack을 사용한다면 nova.conf에 다음을 설정해요:

cpu_mode=host-passthrough

libvirt를 사용한다면 XML 구성에 다음을 설정해요:

<cpu mode='host-passthrough'/>

ClickHouse가 cpuid 명령으로 올바른 정보를 얻으려면 이는 중요해요. 그렇지 않으면 하이퍼바이저가 오래된 CPU 모델에서 실행될 때 Illegal instruction 크래시가 발생할 수 있어요.

ClickHouse Keeper와 ZooKeeper

ClickHouse Keeper가 ClickHouse 클러스터의 ZooKeeper를 대체할 것을 권장해요. ClickHouse Keeper 문서를 참고하세요. ZooKeeper를 계속 사용하고 싶다면 최신 버전의 ZooKeeper – 3.4.9 이상을 사용하는 것이 가장 좋아요. 안정적인 Linux 배포판의 버전은 오래되었을 수 있어요. 서로 다른 ZooKeeper 클러스터 간에 데이터를 전송하는 수동으로 작성된 스크립트를 절대 사용하지 마세요. 순차 노드에서 결과가 올바르지 않기 때문이에요. 같은 이유로 "zkcopy" 유틸리티를 절대 사용하지 마세요: https://github.com/ksprojects/zkcopy/issues/15. 기존 ZooKeeper 클러스터를 둘로 나누고 싶다면 올바른 방법은 레플리카 수를 늘린 다음 두 개의 독립 클러스터로 재구성하는 것이에요. 테스트 환경이나 낮은 수집 속도의 환경에서는 ClickHouse와 같은 서버에서 ClickHouse Keeper를 실행할 수 있어요. 프로덕션 환경에서는 ClickHouse와 ZooKeeper/Keeper에 별도의 서버를 사용하거나, ClickHouse 파일과 Keeper 파일을 별도의 디스크에 두는 것을 제안해요. ZooKeeper/Keeper는 디스크 지연에 매우 민감하고 ClickHouse가 사용 가능한 모든 시스템 리소스를 사용할 수 있기 때문이에요. 앙상블에 ZooKeeper observer를 둘 수 있지만 ClickHouse 서버는 observer와 상호작용하면 안 돼요. minSessionTimeout 설정을 변경하지 마세요. 큰 값은 ClickHouse 재시작 안정성에 영향을 줄 수 있어요. 기본 설정에서 ZooKeeper는 시한폭탄이에요:

ZooKeeper 서버는 기본 구성(see autopurge)을 사용할 때 이전 스냅샷과 로그에서 파일을 삭제하지 않으며, 이는 운영자의 책임입니다.

이 폭탄은 해체되어야 해요. 대규모 프로덕션 환경에서 사용하는 ZooKeeper (3.5.1) 구성은 다음과 같아요 (zoo.cfg):

# http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
# This value is not quite motivated
initLimit=300
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=10

maxClientCnxns=2000

# It is the maximum value that client may request and the server will accept.
# It is Ok to have high maxSessionTimeout on server to allow clients to work with high session timeout if they want.
# But we request session timeout of 30 seconds by default (you can change it with session_timeout_ms in ClickHouse config).
maxSessionTimeout=60000000
# the directory where the snapshot is stored.
dataDir=/opt/zookeeper/{{ '{{' }} cluster['name'] {{ '}}' }}/data
# Place the dataLogDir to a separate physical disc for better performance
dataLogDir=/opt/zookeeper/{{ '{{' }} cluster['name'] {{ '}}' }}/logs

autopurge.snapRetainCount=10
autopurge.purgeInterval=1

# To avoid seeks ZooKeeper allocates space in the transaction log file in
# blocks of preAllocSize kilobytes. The default block size is 64M. One reason
# for changing the size of the blocks is to reduce the block size if snapshots
# are taken more often. (Also, see snapCount).
preAllocSize=131072

# Clients can submit requests faster than ZooKeeper can process them,
# especially if there are a lot of clients. To prevent ZooKeeper from running
# out of memory due to queued requests, ZooKeeper will throttle clients so that
# there is no more than globalOutstandingLimit outstanding requests in the
# system. The default limit is 1000.
# globalOutstandingLimit=1000

# ZooKeeper logs transactions to a transaction log. After snapCount transactions
# are written to a log file a snapshot is started and a new transaction log file
# is started. The default snapCount is 100000.
snapCount=3000000

# If this option is defined, requests will be will logged to a trace file named
# traceFile.year.month.day.
#traceFile=

# Leader accepts client connections. Default value is "yes". The leader machine
# coordinates updates. For higher update throughput at thes slight expense of
# read throughput the leader can be configured to not accept clients and focus
# on coordination.
leaderServes=yes

standaloneEnabled=false
dynamicConfigFile=/etc/zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }}/conf/zoo.cfg.dynamic

Java 버전:

openjdk 11.0.5-shenandoah 2019-10-15
OpenJDK Runtime Environment (build 11.0.5-shenandoah+10-adhoc.heretic.src)
OpenJDK 64-Bit Server VM (build 11.0.5-shenandoah+10-adhoc.heretic.src, mixed mode)

JVM 매개변수:

NAME=zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }}
ZOOCFGDIR=/etc/$NAME/conf

# TODO this is really ugly
# How to find out, which jars are needed?
# seems, that log4j requires the log4j.properties file to be in the classpath
CLASSPATH="$ZOOCFGDIR:/usr/build/classes:/usr/build/lib/*.jar:/usr/share/zookeeper-3.6.2/lib/audience-annotations-0.5.0.jar:/usr/share/zookeeper-3.6.2/lib/commons-cli-1.2.jar:/usr/share/zookeeper-3.6.2/lib/commons-lang-2.6.jar:/usr/share/zookeeper-3.6.2/lib/jackson-annotations-2.10.3.jar:/usr/share/zookeeper-3.6.2/lib/jackson-core-2.10.3.jar:/usr/share/zookeeper-3.6.2/lib/jackson-databind-2.10.3.jar:/usr/share/zookeeper-3.6.2/lib/javax.servlet-api-3.1.0.jar:/usr/share/zookeeper-3.6.2/lib/jetty-http-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jetty-io-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jetty-security-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jetty-server-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jetty-servlet-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jetty-util-9.4.24.v20191120.jar:/usr/share/zookeeper-3.6.2/lib/jline-2.14.6.jar:/usr/share/zookeeper-3.6.2/lib/json-simple-1.1.1.jar:/usr/share/zookeeper-3.6.2/lib/log4j-1.2.17.jar:/usr/share/zookeeper-3.6.2/lib/metrics-core-3.2.5.jar:/usr/share/zookeeper-3.6.2/lib/netty-buffer-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-codec-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-common-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-handler-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-resolver-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-transport-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-transport-native-epoll-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/netty-transport-native-unix-common-4.1.50.Final.jar:/usr/share/zookeeper-3.6.2/lib/simpleclient-0.6.0.jar:/usr/share/zookeeper-3.6.2/lib/simpleclient_common-0.6.0.jar:/usr/share/zookeeper-3.6.2/lib/simpleclient_hotspot-0.6.0.jar:/usr/share/zookeeper-3.6.2/lib/simpleclient_servlet-0.6.0.jar:/usr/share/zookeeper-3.6.2/lib/slf4j-api-1.7.25.jar:/usr/share/zookeeper-3.6.2/lib/slf4j-log4j12-1.7.25.jar:/usr/share/zookeeper-3.6.2/lib/snappy-java-1.1.7.jar:/usr/share/zookeeper-3.6.2/lib/zookeeper-3.6.2.jar:/usr/share/zookeeper-3.6.2/lib/zookeeper-jute-3.6.2.jar:/usr/share/zookeeper-3.6.2/lib/zookeeper-prometheus-metrics-3.6.2.jar:/usr/share/zookeeper-3.6.2/etc"

ZOOCFG="$ZOOCFGDIR/zoo.cfg"
ZOO_LOG_DIR=/var/log/$NAME
USER=zookeeper
GROUP=zookeeper
PIDDIR=/var/run/$NAME
PIDFILE=$PIDDIR/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
JAVA=/usr/local/jdk-11/bin/java
ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
ZOO_LOG4J_PROP="INFO,ROLLINGFILE"
JMXLOCALONLY=false
JAVA_OPTS="-Xms{{ '{{' }} cluster.get('xms','128M') {{ '}}' }} \
    -Xmx{{ '{{' }} cluster.get('xmx','1G') {{ '}}' }} \
    -Xlog:safepoint,gc*=info,age*=debug:file=/var/log/$NAME/zookeeper-gc.log:time,level,tags:filecount=16,filesize=16M
    -verbose:gc \
    -XX:+UseG1GC \
    -Djute.maxbuffer=8388608 \
    -XX:MaxGCPauseMillis=50"

Salt 초기화:

description "zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }} centralized coordination service"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

limit nofile 8192 8192

pre-start script
    [ -r "/etc/zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }}/conf/environment" ] || exit 0
    . /etc/zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }}/conf/environment
    [ -d $ZOO_LOG_DIR ] || mkdir -p $ZOO_LOG_DIR
    chown $USER:$GROUP $ZOO_LOG_DIR
end script

script
    . /etc/zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }}/conf/environment
    [ -r /etc/default/zookeeper ] && . /etc/default/zookeeper
    if [ -z "$JMXDISABLE" ]; then
        JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY"
    fi
    exec start-stop-daemon --start -c $USER --exec $JAVA --name zookeeper-{{ '{{' }} cluster['name'] {{ '}}' }} \
        -- -cp $CLASSPATH $JAVA_OPTS -Dzookeeper.log.dir=${ZOO_LOG_DIR} \
        -Dzookeeper.root.logger=${ZOO_LOG4J_PROP} $ZOOMAIN $ZOOCFG
end script

백신 소프트웨어 (Antivirus software)

백신 소프트웨어를 사용한다면 ClickHouse 데이터 파일(/var/lib/clickhouse) 폴더를 건너뛰도록 구성해요. 그렇지 않으면 성능이 저하되고 데이터 수집과 백그라운드 병합 중 예상치 못한 오류가 발생할 수 있어요.

더 알아보기 (Learn more)