Clickhouse. Развертывание.
Установка
Пакеты для установки:
wget curl https://packages.clickhouse.com/tgz/stable/clickhouse-client-22.9.3.18-amd64.tgz && \
wget curl https://packages.clickhouse.com/tgz/stable/clickhouse-common-static-22.9.3.18-amd64.tgz && \
wget curl https://packages.clickhouse.com/tgz/stable/clickhouse-common-static-dbg-22.9.3.18-amd64.tgz && \
wget curl https://packages.clickhouse.com/tgz/stable/clickhouse-server-22.9.3.18-amd64.tgz
Скрип для распаковки и последующей установки:
LATEST_VERSION=22.9.3.18
export LATEST_VERSION
case $(uname -m) in
x86_64) ARCH=amd64 ;;
aarch64) ARCH=arm64 ;;
*) echo "Unknown architecture $(uname -m)"; exit 1 ;;
esac
tar -xzvf "clickhouse-common-static-$LATEST_VERSION-${ARCH}.tgz" \
|| tar -xzvf "clickhouse-common-static-$LATEST_VERSION.tgz"
sudo "clickhouse-common-static-$LATEST_VERSION/install/doinst.sh"
tar -xzvf "clickhouse-common-static-dbg-$LATEST_VERSION-${ARCH}.tgz" \
|| tar -xzvf "clickhouse-common-static-dbg-$LATEST_VERSION.tgz"
sudo "clickhouse-common-static-dbg-$LATEST_VERSION/install/doinst.sh"
tar -xzvf "clickhouse-server-$LATEST_VERSION-${ARCH}.tgz" \
|| tar -xzvf "clickhouse-server-$LATEST_VERSION.tgz"
sudo "clickhouse-server-$LATEST_VERSION/install/doinst.sh" configure
sudo /etc/init.d/clickhouse-server start
tar -xzvf "clickhouse-client-$LATEST_VERSION-${ARCH}.tgz" \
|| tar -xzvf "clickhouse-client-$LATEST_VERSION.tgz"
sudo "clickhouse-client-$LATEST_VERSION/install/doinst.sh"
INFO
Обратите внимание, что при установке клик попросит задать пароль для default user, он должен быть одинаковым на всех машинах.
Настройка zookeer и ingress-inginx
В первую очередь необходимо поднять Zookeeper в нужном кластере:
1. Создаем отдельный NS в кубе, zk;
2. Приминяем манифест для zookeeper:
# Setup Service to provide access to Zookeeper for clients
apiVersion: v1
kind: Service
metadata:
# DNS would be like zookeeper.zoons
name: zk-cs
labels:
app: zk
spec:
ports:
- port: 2181
name: client
selector:
app: zk
what: node
---
# Setup Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
# DNS would be like zookeeper-0.zookeepers.etc
name: zk-hs
labels:
app: zk
spec:
ports:
- port: 2181
name: client
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
what: node
---
# Setup max number of unavailable pods in StatefulSet
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
selector:
matchLabels:
app: zk
maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
# nodes would be named as zookeeper-0, zookeeper-1, zookeeper-2
name: zk
spec:
selector:
matchLabels:
app: zk
serviceName: zk-hs
replicas: 2
updateStrategy:
type: RollingUpdate
podManagementPolicy: Parallel
template:
metadata:
labels:
app: zk
what: node
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
containers:
- name: kubernetes-zookeeper
imagePullPolicy: IfNotPresent
image: "docker.io/zookeeper:3.8.0-temurin"
resources:
requests:
memory: "1Gi"
cpu: "0.5"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
# See those links for proper startup settings:
# https://github.com/kow3ns/kubernetes-zookeeper/blob/master/docker/scripts/start-zookeeper
# https://clickhouse.yandex/docs/en/operations/tips/#zookeeper
command:
- bash
- -x
- -c
- |
SERVERS=3 &&
HOST=`hostname -s` &&
DOMAIN=`hostname -d` &&
CLIENT_PORT=2181 &&
SERVER_PORT=2888 &&
ELECTION_PORT=3888 &&
ZOO_DATA_DIR=/var/lib/zookeeper/data &&
ZOO_DATA_LOG_DIR=/var/lib/zookeeper/datalog &&
{
echo "clientPort=${CLIENT_PORT}"
echo 'tickTime=2000'
echo 'initLimit=30000'
echo 'syncLimit=10'
echo 'maxClientCnxns=2000'
echo 'maxSessionTimeout=60000000'
echo "dataDir=${ZOO_DATA_DIR}"
echo "dataLogDir=${ZOO_DATA_LOG_DIR}"
echo 'autopurge.snapRetainCount=3'
echo 'autopurge.purgeInterval=2'
echo 'preAllocSize=131072'
echo 'snapCount=3000000'
echo 'leaderServes=yes'
echo 'standaloneEnabled=true'
echo '4lw.commands.whitelist=stat, ruok, conf, isro'
} > /conf/zoo.cfg &&
{
echo "zookeeper.root.logger=CONSOLE"
echo "zookeeper.console.threshold=INFO"
echo "log4j.rootLogger=\${zookeeper.root.logger}"
echo "log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender"
echo "log4j.appender.CONSOLE.Threshold=\${zookeeper.console.threshold}"
echo "log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout"
echo "log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n"
} > /conf/log4j.properties &&
echo 'JVMFLAGS="-Xms128M -Xmx1G -XX:+UseG1GC"' > /conf/java.env &&
if [[ $HOST =~ (.*)-([0-9]+)$ ]]; then
NAME=${BASH_REMATCH[1]}
ORD=${BASH_REMATCH[2]}
else
echo "Failed to parse name and ordinal of Pod"
exit 1
fi &&
mkdir -p ${ZOO_DATA_DIR} &&
mkdir -p ${ZOO_DATA_LOG_DIR} &&
export MY_ID=$((ORD+1)) &&
#echo 2 > $ZOO_DATA_DIR/myid &&
echo $MY_ID > $ZOO_DATA_DIR/myid &&
if [[ $SERVERS -gt 1 ]]; then
for (( i=1; i<=$SERVERS; i++ )); do
echo "server.$i=$NAME-$((i-1)).$DOMAIN:$SERVER_PORT:$ELECTION_PORT" >> /conf/zoo.cfg;
done
fi &&
#echo "server.1=192.168.233.230:2888:3888" >> /conf/zoo.cfg &&
#echo "server.2=$NAME-0.$DOMAIN:$SERVER_PORT:$ELECTION_PORT" >> /conf/zoo.cfg &&
chown -Rv zookeeper "$ZOO_DATA_DIR" "$ZOO_DATA_LOG_DIR" "$ZOO_LOG_DIR" "$ZOO_CONF_DIR" &&
zkServer.sh start-foreground
readinessProbe:
exec:
command:
- bash
- -c
- "OK=$(echo ruok | nc 127.0.0.1 2181); if [[ \"$OK\" == \"imok\" ]]; then exit 0; else exit 1; fi"
initialDelaySeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command:
- bash
- -c
- "OK=$(echo ruok | nc 127.0.0.1 2181); if [[ \"$OK\" == \"imok\" ]]; then exit 0; else exit 1; fi"
initialDelaySeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
# Run as a non-privileged user
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
storageClassName: longhorn
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
3. Далее нам необходимо подключить дополнительно сервис для zookeeper, в эндпоинтах которого буду присутсвовать ноды клика:
Для этого применим манифест для сервиса:
apiVersion: v1
kind: Service
metadata:
name: ch-svc
namespace: zk
spec:
clusterIP: None
type: ClusterIP
sessionAffinity: None
ports:
- name: http
port: 8123
protocol: TCP
targetPort: 8123
- name: tcp
port: 9000
protocol: TCP
targetPort: 9000
И манифест для эндпоинтов:
apiVersion: v1
kind: Endpoints
metadata:
name: ch-svc
namespace: zk
subsets:
- addresses:
- ip: 00.00.00.00 #Ноды
- ip: 00.00.00.00 #клика
ports:
- name: tcp
port: 9000
protocol: TCP
- name: http
port: 8123
protocol: TCP
3.1. Также нам необходимо закрепить за каждым подом кипера свой сервис, каждый из которых будет смотреть на один из подов соответственно:
apiVersion: v1
kind: Service
metadata:
name: zk-0 #В зависимости от номера пода
namespace: zk
spec:
internalTrafficPolicy: Cluster
ports:
- port: 2181
protocol: TCP
targetPort: 2181
selector:
apps.kubernetes.io/pod-index: "0" #Указываем номер пода
sessionAffinity: None
type: ClusterIP
4. После того, как мы закончили с кипером, необходимо внести данные об ранее созданных соединений в configmap ingress-nginx-tcp:
4.1 В раздел data мы вносим наши открытые порты как от кипера, так и от клика:
apiVersion: v1
data:
"31010": dremio/dremio-client:31010 # Присутствующие ранее порты мы оставляем!
"2181": zk/zk-0:2181 # +
"2182": zk/zk-1:2182 # +
"8123": zk/ch-svc:8123 # +
"9000": zk/ch-svc:9000 # +
kind: ConfigMap
metadata:
annotations:
meta.helm.sh/release-name: ingress-controller
meta.helm.sh/release-namespace: nginx-ingress
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-controller
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.2.1
helm.sh/chart: ingress-nginx-4.1.4
k8slens-edit-resource-version: v1
name: ingress-controller-ingress-nginx-tcp
namespace: nginx-ingress
4.2 В эндопинты ingress-controller-ingress-nginx-controller также добавляем наши порты, после чего вносим их в деплоймент nginx, раздел портов для контейнеров и перезагружаем сервис. Внешняя настройка завершена.
Внутренняя настройка clickhouse.
В первую очередь необходимо по пути /etc/clickhouse-server/config.d/ создать файл config.xml на всех узлах, его можно найти на любых машинах действующего клика. Заполняем его по примеру.
Также ниже в файле, в настройке Zookeeper, установливаем IP балансировщика в <port></port>, по которому в дальнейшем будет выполняться вход в базу.
INFO
IP балансироващика указывается в колличестве, зависящим от колличества сервисов кипера под каждый под.
Проверка
Для проверки работоспособности зайдите в базу клика либо через СУБД по IP балансировщика, либо через ноду клика и выполните команду по созданию тестовой таблице:
CREATE TABLE test on cluster 'main' (id int) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/{shard}/test', '{replica}', id) order by id;
insert into test values(1);
После выполнения скрипта должны отобразится рабочие ноды, которые сихнронизировано выполнили скрипт. Дополнительно можно зайти на другую ноду и выполнить селект по таблице.
Удалить тестовую талицу следующем скриптом:
Drop table test on cluster main sync;
sync в данном случае очень важно проставлять, чтобы команда отработала на всех нодах.
Настройка путей для файловой системы Clickhouse.
Для этого нам понадобиться пустой новый диск, созданный специально для клика.
Для начала убедимся, что диск присутствует в системе, введя команду:
dh -h
Вы сразу увидите его. Размер сильно отличается от других доступных, и он почти пустой.
Далее нам нужно его имя, получим командой:
fdisk -l #Пример /dev/sdb
Для монтирования диска нам необходимо выполнить следующие команды, исполльзуя утилиту LVM:
pvcreate /dev/sdb; #Помечаем диск, что он будет использоваться для LVM
vgcreate data /dev/sdb; #Создаем группу томов для диска
lvcreate -l 100%FREE -n lv01 data; #Выделяем под эту группу всё доступное место
mkfs.ext4 /dev/data/lv01; #Устанавливаем нужную файловую систему
vi /etc/fstab #Конфигурируем файл, в котором узываем папку, к которой будет относиться диск, по образцу работающий машины
mount /opt #Создаем директорию, которую указали в пункте выше
После проделанных шагов, копируем все данные из дефолтных директорий клика в новую, придерживаясь пути /opt/clickhouse/[копируемая директория]/. Их должны быть две - lib и log. В конечном результате всё должно выглядить так:

INFO
Успользуйте cp -r для копирования папок с вложенными папками
Далее подчищаем дефолтные пути и всю новую систему передаем в пользование клика:
chown -R clickhouse:clickhouse /opt/clickhouse
После нам необходимо задать правильные пути в файле /etc/clickhouse-server/config.xml относительно того, как это было ранее.
Перезагружаем кликхаус на узлах и проверяем его работоспособность.