├── Dockerfile ├── Makefile ├── README.md ├── cluster-coordinator ├── main.go ├── misc.go ├── net.go └── zk.go ├── config ├── config.xml ├── docker_related_config.xml └── users.xml └── helm └── clickhouse-on-kube ├── .helmignore ├── Chart.yaml ├── Makefile ├── templates ├── NOTES.txt ├── _helpers.tpl ├── configmap_configd.yaml ├── configmap_usersd.yaml ├── ingress.yaml ├── roles.yaml ├── service.yaml └── statefulset.yaml └── values.yaml /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | ARG repository="deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" 4 | ARG version=\* 5 | 6 | RUN apt-get update && \ 7 | apt-get install -y apt-transport-https && \ 8 | mkdir -p /etc/apt/sources.list.d && \ 9 | echo $repository | tee /etc/apt/sources.list.d/clickhouse.list && \ 10 | apt-get update && \ 11 | apt-get install --allow-unauthenticated -y clickhouse-server-common=$version clickhouse-server-base=$version && \ 12 | rm -rf /var/lib/apt/lists/* /var/cache/debconf 13 | 14 | # extras 15 | RUN apt-get update --allow-unauthenticated && \ 16 | apt-get install --allow-unauthenticated -y vim curl git 17 | 18 | # install go 19 | RUN cd /tmp && \ 20 | curl -O https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz && \ 21 | tar -xvf go1.10.1.linux-amd64.tar.gz && \ 22 | mv go /usr/local && \ 23 | rm go1.10.1.linux-amd64.tar.gz 24 | 25 | ENV GOROOT=/usr/local/go 26 | ENV PATH=$PATH:$GOROOT/bin 27 | 28 | # clean 29 | RUN apt-get clean 30 | 31 | COPY ./config/docker_related_config.xml /etc/clickhouse-server/config.d/ 32 | COPY ./config/config.xml /etc/clickhouse-server/ 33 | COPY ./config/users.xml /etc/clickhouse-server/users.d/ 34 | 35 | # data / tmp db location 36 | RUN mkdir -p /clickhouse/data && \ 37 | mkdir -p /clickhouse/tmp 38 | 39 | EXPOSE 9000 8123 9009 40 | VOLUME /var/lib/clickhouse 41 | 42 | ENV CLICKHOUSE_CONFIG /etc/clickhouse-server/config.xml 43 | 44 | ENTRYPOINT exec /usr/bin/clickhouse-server --config=${CLICKHOUSE_CONFIG} 45 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # docker vars 2 | REPO=dmitryb/clickhouse-server 3 | TAG=latest 4 | 5 | # build clickhouse-server docker image 6 | docker-build: 7 | docker build -t $(REPO):$(TAG) . 8 | 9 | # push clickhouse-server docker image to hub 10 | docker-push: 11 | docker push $(REPO):$(TAG) 12 | 13 | # create helm package and reindex 14 | helm-pckg: 15 | helm package ./helm/clickhouse-on-kube 16 | helm repo index . 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clickhouse-on-kubernetes 2 | 3 | Run clickhouse server on kubernetes 4 | 5 | ## Quick start 6 | 7 | ```bash 8 | # clone repo 9 | git clone https://github.com/DmitryBe/clickhouse-on-kubernetes.git 10 | cd clickhouse-on-kubernetes/helm/clickhouse-on-kube 11 | 12 | # update values.yaml 13 | # replicaCount: 3 # number of nodes && shards (1 replica) 14 | # zk.servers: # or disable 15 | # nodeSelector.type: # or remove 16 | # disable elb (if running on google cloud | minikube) 17 | # resources: ... 18 | 19 | # install chart 20 | helm install \ 21 | --name clickhouse-test \ 22 | --namespace=default . 23 | 24 | # run clickhouse-client 25 | docker run -it --rm --net=host yandex/clickhouse-client -h 26 | 27 | # check cluster config 28 | :) select * from system.clusters 29 | 30 | # create db and distrib table 31 | CREATE DATABASE IF NOT EXISTS DB01; 32 | dd 33 | USE DB01; 34 | 35 | CREATE TABLE IF NOT EXISTS DB01.Table1 ON CLUSTER default_cluster 36 | ( 37 | UpdateDate Date, 38 | GeoHash String, 39 | NBooked UInt32 40 | ) ENGINE = MergeTree(UpdateDate, (GeoHash, UpdateDate), 8192); 41 | 42 | CREATE TABLE IF NOT EXISTS DB01.Table1_c ON CLUSTER default_cluster as DB01.Table1 43 | ENGINE = Distributed(default_cluster, DB01, Table1, cityHash64(GeoHash)); 44 | 45 | # insert some data 46 | INSERT INTO DB01.Table1_c VALUES ('2017-11-08', 'geo01', 1); 47 | INSERT INTO DB01.Table1_c VALUES ('2017-11-08', 'geo02', 2); 48 | INSERT INTO DB01.Table1_c VALUES ('2017-11-08', 'geo03', 3); 49 | 50 | # query distrib table 51 | SELECT * FROM DB01.Table1_c 52 | ``` 53 | 54 | ## Dev 55 | 56 | To rebuild docker image run: `make docker-build` 57 | 58 | To rebuild helm package run: `make helm-pckg` 59 | -------------------------------------------------------------------------------- /cluster-coordinator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/samuel/go-zookeeper/zk" 9 | ) 10 | 11 | // ClickHousePort is a default clickhous port 12 | const ClickHousePort = 9000 13 | 14 | // ClickHouseConfigLocation config path 15 | const ClickHouseConfigD = "/etc/clickhouse-server/config.d" 16 | 17 | // ClickhouseRemoteServersConfigFileName - cluster config file name 18 | const ClickhouseRemoteServersConfigFileName = "clickhouse_remote_servers.xml" 19 | 20 | // ClickHouseClusterName for remote servres config 21 | const ClickHouseClusterName = "default_cluster" 22 | 23 | func main() { 24 | fmt.Println("starting coordinator") 25 | 26 | // get zk connection 27 | zksStr := os.Getenv("ZOOKEEPER_SERVERS") 28 | if zksStr == "" { 29 | panic("ZOOKEEPER_SERVERS is requried") 30 | } 31 | fmt.Println("zk: " + zksStr) 32 | 33 | // get zk key path 34 | zkPath := os.Getenv("ZOOKEEPER_PATH") 35 | if zkPath == "" { 36 | zkPath = "/clickhouse-coordinator-01" 37 | } 38 | fmt.Println("zk-path: " + zkPath) 39 | 40 | clickHouseConfigD := os.Getenv("CLICKHOUSE_CONFIG_LOCATION") 41 | if clickHouseConfigD == "" { 42 | clickHouseConfigD = ClickHouseConfigD 43 | } 44 | fmt.Println("clickhouse config.d path: " + clickHouseConfigD) 45 | 46 | // connect to zk 47 | conn := zkConnect() 48 | defer conn.Close() 49 | 50 | // create root path 51 | path, err := conn.Create(zkPath, []byte(""), int32(0), zk.WorldACL(zk.PermAll)) 52 | if err != nil { 53 | fmt.Printf("zp path exists: %+v\n", zkPath) 54 | } 55 | fmt.Printf("create: %+v\n", path) 56 | 57 | // start zk watcher 58 | snapshots, errors := zkMirror(conn, zkPath) 59 | // watching events 60 | go func() { 61 | for { 62 | select { 63 | case snapshot := <-snapshots: 64 | remoteServerConfig := UpdateClusterConfig(snapshot, ClickHouseClusterName) 65 | fmt.Println("--------------------------------------------------------") 66 | fmt.Println(remoteServerConfig) 67 | fmt.Println("--------------------------------------------------------") 68 | if len(snapshot) > 0 { 69 | // update config file 70 | remoteServreConfigPath := fmt.Sprintf("%s/%s", clickHouseConfigD, ClickhouseRemoteServersConfigFileName) 71 | fmt.Println("updating remote server config: " + remoteServreConfigPath) 72 | err := WriteStringToFile(remoteServreConfigPath, remoteServerConfig) 73 | must(err) 74 | } 75 | 76 | case err := <-errors: 77 | panic(err) 78 | } 79 | } 80 | }() 81 | 82 | // worming up 83 | time.Sleep(time.Second) 84 | 85 | // get pod ip 86 | ip := GetLocalIP() 87 | fmt.Println(ip.String()) 88 | 89 | // create ephemeral path 90 | flags := int32(zk.FlagEphemeral) 91 | acl := zk.WorldACL(zk.PermAll) 92 | clickhouseHostPort := fmt.Sprintf("%s:%v", ip, ClickHousePort) 93 | zkNodePath := fmt.Sprintf("%s/%s", zkPath, clickhouseHostPort) 94 | fmt.Println("create zk path for node: " + zkNodePath) 95 | _, err = conn.Create(zkNodePath, []byte(clickhouseHostPort), flags, acl) 96 | must(err) 97 | 98 | // wait for termination 99 | fmt.Println("waiting for termination") 100 | _, _, ech, err := conn.ExistsW(zkPath) 101 | must(err) 102 | evt := <-ech 103 | fmt.Println("closing coordinator") 104 | must(evt.Err) 105 | } 106 | -------------------------------------------------------------------------------- /cluster-coordinator/misc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // RemoteServersTemplate provides template for cluster remote server 11 | const RemoteServersTemplate = ` 12 | 13 | 14 | <%s> 15 | %s 16 | 17 | 18 | 19 | ` 20 | 21 | // ShardTemplate templates for node 22 | const ShardTemplate = ` 23 | 24 | 25 | %s 26 | %s 27 | 28 | 29 | ` 30 | 31 | // UpdateClusterConfig is blah 32 | func UpdateClusterConfig(nodePorts []string, clusterName string) string { 33 | fmt.Println("updating cluster config") 34 | shards := []string{} 35 | for _, nodePort := range nodePorts { 36 | parts := strings.Split(nodePort, ":") 37 | node, port := parts[0], parts[1] 38 | shard := fmt.Sprintf(ShardTemplate, node, port) 39 | shards = append(shards, shard) 40 | } 41 | return fmt.Sprintf(RemoteServersTemplate, clusterName, strings.Join(shards, ""), clusterName) 42 | } 43 | 44 | // WriteStringToFile is 45 | func WriteStringToFile(filepath, s string) error { 46 | fo, err := os.Create(filepath) 47 | if err != nil { 48 | return err 49 | } 50 | defer fo.Close() 51 | 52 | _, err = io.Copy(fo, strings.NewReader(s)) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /cluster-coordinator/net.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | ) 7 | 8 | // GetLocalIP returns local node/pod ip 9 | func GetLocalIP() net.IP { 10 | 11 | conn, err := net.Dial("udp", "8.8.8.8:80") 12 | if err != nil { 13 | log.Fatal(err) 14 | panic("cannot get local ip") 15 | } 16 | defer conn.Close() 17 | 18 | localAddr := conn.LocalAddr().(*net.UDPAddr) 19 | return localAddr.IP 20 | } 21 | -------------------------------------------------------------------------------- /cluster-coordinator/zk.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "strings" 6 | "time" 7 | 8 | "github.com/samuel/go-zookeeper/zk" 9 | ) 10 | 11 | func must(err error) { 12 | if err != nil { 13 | log.Fatal(err) 14 | panic(err) 15 | } 16 | } 17 | 18 | func zkConnect() *zk.Conn { 19 | zksStr := "0.0.0.0" 20 | zks := strings.Split(zksStr, ",") 21 | conn, _, err := zk.Connect(zks, time.Second) 22 | must(err) 23 | return conn 24 | } 25 | 26 | func zkMirror(conn *zk.Conn, path string) (chan []string, chan error) { 27 | snapshots := make(chan []string) 28 | errors := make(chan error) 29 | go func() { 30 | for { 31 | snapshot, _, events, err := conn.ChildrenW(path) 32 | if err != nil { 33 | errors <- err 34 | return 35 | } 36 | 37 | // snaps pipe 38 | snapshots <- snapshot 39 | 40 | // error pipe 41 | evt := <-events 42 | if evt.Err != nil { 43 | errors <- evt.Err 44 | return 45 | } 46 | 47 | // rest 48 | time.Sleep(time.Second * 3) 49 | } 50 | }() 51 | return snapshots, errors 52 | } 53 | -------------------------------------------------------------------------------- /config/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | error 6 | /var/log/clickhouse-server/clickhouse-server.log 7 | /var/log/clickhouse-server/clickhouse-server.err.log 8 | 100M 9 | 10 10 | 11 | 12 | 13 | 8123 14 | 9000 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | /etc/clickhouse-server/server.crt 27 | /etc/clickhouse-server/server.key 28 | 29 | /etc/clickhouse-server/dhparam.pem 30 | none 31 | true 32 | true 33 | sslv2,sslv3 34 | true 35 | 36 | 37 | 38 | true 39 | true 40 | sslv2,sslv3 41 | true 42 | 43 | 44 | 45 | RejectCertificateHandler 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 9009 57 | 58 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 | 4096 80 | 3 81 | 82 | 83 | 100 84 | 85 | 87 | 88 | 89 | 94 | 8589934592 95 | 96 | 100 | 5368709120 101 | 102 | 103 | 104 | /clickhouse/data/ 105 | 106 | 107 | /clickhouse/tmp/ 108 | 109 | 110 | /etc/clickhouse-server/users.d/users.xml 111 | 112 | 113 | default 114 | 115 | 116 | 117 | 118 | 119 | default 120 | 121 | 133 | 134 | 135 | 138 | 139 | 140 | 143 | 144 | 145 | 149 | 150 | 155 | 156 | 157 | 162 | 163 | 164 | 165 | 166 | 3600 167 | 168 | 169 | 170 | 3600 171 | 172 | 173 | 60 174 | 175 | 176 | 184 | 209 | 210 | 211 | 212 | 213 | 217 | system 218 | query_log
219 | 227 | toYYYYMM(event_date) 228 | 229 | 7500 230 |
231 | 232 | 233 | 241 | 242 | 243 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 257 | *_dictionary.xml 258 | 259 | 262 | 263 | 275 | 276 | 277 | 279 | 280 | 281 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 295 | 296 | 302 | 303 | 304 | 305 | 306 | 307 | click_cost 308 | any 309 | 310 | 0 311 | 3600 312 | 313 | 314 | 86400 315 | 60 316 | 317 | 318 | 319 | max 320 | 321 | 0 322 | 60 323 | 324 | 325 | 3600 326 | 300 327 | 328 | 329 | 86400 330 | 3600 331 | 332 | 333 | 334 | 335 | 338 | /var/lib/clickhouse/format_schemas/ 339 |
340 | -------------------------------------------------------------------------------- /config/docker_related_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.0.0.0 4 | :: 5 | 1 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /config/users.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10000000000 9 | 10 | 11 | 0 12 | 13 | 20 | random 21 | 22 | 23 | 24 | 25 | 1 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 46 | 47 | 48 | 67 | 68 | ::/0 69 | 70 | 71 | 72 | default 73 | 74 | 75 | default 76 | 77 | 78 | 79 | 80 | 81 | 82 | ::1 83 | 127.0.0.1 84 | 85 | readonly 86 | default 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 3600 98 | 99 | 100 | 0 101 | 0 102 | 0 103 | 0 104 | 0 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: clickhouse-on-kube 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/Makefile: -------------------------------------------------------------------------------- 1 | 2 | helm-dry: 3 | helm install --name clickhouse-test --namespace=playground --debug --dry-run . 4 | 5 | helm-lint: 6 | helm lint . 7 | 8 | helm-install-test: 9 | helm install --name clickhouse-test --namespace=playground . 10 | 11 | helm-install-stg: 12 | helm install --name clickhouse-stg \ 13 | --namespace=clickhouse \ 14 | --set replicaCount=5 \ 15 | --set ebs.size=500Gi \ 16 | --set security.enabled=true \ 17 | --set resources.limits.cpu=3800m \ 18 | --set resources.limits.memory=28000Mi \ 19 | --set resources.requests.cpu=3800m \ 20 | --set resources.requests.memory=28000Mi \ 21 | . 22 | 23 | helm-del: 24 | helm del --purge clickhouse-test 25 | 26 | kube-get: 27 | kubectl -n playground get deployment,pod,svc,ingress 28 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range .Values.ingress.hosts }} 4 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} 5 | {{- end }} 6 | {{- else if contains "NodePort" .Values.service.type }} 7 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "clickhouse-on-kube.fullname" . }}) 8 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 9 | echo http://$NODE_IP:$NODE_PORT 10 | {{- else if contains "LoadBalancer" .Values.service.type }} 11 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 12 | You can watch the status of by running 'kubectl get svc -w {{ template "clickhouse-on-kube.fullname" . }}' 13 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "clickhouse-on-kube.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 14 | echo http://$SERVICE_IP:{{ .Values.service.port }} 15 | {{- else if contains "ClusterIP" .Values.service.type }} 16 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "clickhouse-on-kube.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 17 | echo "Visit http://127.0.0.1:8080 to use your application" 18 | kubectl port-forward $POD_NAME 8080:80 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "clickhouse-on-kube.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "clickhouse-on-kube.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "clickhouse-on-kube.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/configmap_configd.yaml: -------------------------------------------------------------------------------- 1 | # This is a simple example of using a config map to create a single page static site. 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ .Release.Name }}-configd 6 | labels: 7 | app: {{ .Release.Name }}-configd 8 | chart: {{ template "clickhouse-on-kube.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | data: 12 | clickhouse_remote_servers.xml: |- 13 | 14 | 15 | 16 | {{range $i, $e := until (atoi (printf "%d" (int64 .Values.replicaCount))) }} 17 | 18 | 19 | {{ $.Release.Name }}-{{$i}} 20 | {{ $.Values.service.rpc_port }} 21 | {{ if $.Values.security.enabled }} 22 | {{ $.Values.security.distributed.user }} 23 | {{ $.Values.security.distributed.password }} 24 | {{ end }} 25 | 26 | 27 | {{end}} 28 | 29 | 30 | 31 | docker_related_config.xml: |- 32 | 33 | 0.0.0.0 34 | :: 35 | 1 36 | 41 | 42 | {{ if .Values.zk.enabled }} 43 | zookeeper-servers.xml: |- 44 | 45 | 46 | {{- range $index, $zk := .Values.zk.servers }} 47 | 48 | {{ $zk }} 49 | {{ $.Values.zk.port }} 50 | 51 | {{- end }} 52 | 53 | 54 | 56 | 57 | 58 | /{{ .Release.Name }}/task_queue/ddl 59 | 60 | 61 | 62 | 63 | 64 | {{ end }} 65 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/configmap_usersd.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.security.enabled -}} 2 | # This is a simple example of using a config map to create a single page static site. 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ .Release.Name }}-usersd 7 | labels: 8 | app: {{ .Release.Name }}-usersd 9 | chart: {{ template "clickhouse-on-kube.chart" . }} 10 | release: {{ .Release.Name }} 11 | heritage: {{ .Release.Service }} 12 | data: 13 | users.xml: |- 14 | 15 | 16 | 17 | {{- range .Values.security.profiles }} 18 | <{{ .name }}> 19 | 20 | {{ int64 .max_memory_usage | default 10000000000 }} 21 | 22 | 23 | {{ int64 .use_uncompressed_cache | default 0 }} 24 | 25 | {{ .load_balancing | default "random" }} 26 | 27 | {{- end }} 28 | 29 | 30 | 31 | 1 32 | 33 | 34 | 35 | 36 | 37 | {{- range .Values.security.quotas }} 38 | <{{ .name }}> 39 | 40 | 41 | 42 | {{ int64 .duration | default 3600 }} 43 | 44 | 45 | {{ int64 .queries | default 0 }} 46 | {{ int64 .errors | default 0 }} 47 | {{ int64 .result_rows | default 0 }} 48 | {{ int64 .read_rows | default 0 }} 49 | {{ int64 .execution_time | default 0 }} 50 | 51 | 52 | {{- end }} 53 | 54 | 55 | 56 | 57 | {{- range .Values.security.users }} 58 | <{{ .name }}> 59 | {{ .password }} 60 | 61 | 62 | ::/0 63 | 64 | 65 | 66 | {{ .profile | default "default" }} 67 | 68 | 69 | {{ .quota | default "default" }} 70 | 71 | {{- end }} 72 | 73 | 74 | <{{ .Values.security.distributed.user }}> 75 | {{ .Values.security.distributed.password }} 76 | 77 | 78 | ::/0 79 | 80 | 81 | 82 | {{ .Values.security.distributed.profile | default "default" }} 83 | 84 | 85 | {{ .Values.security.distributed.quota | default "default" }} 86 | 87 | 88 | 89 | 90 | 91 | ::1 92 | 127.0.0.1 93 | 94 | readonly 95 | default 96 | 97 | 98 | 99 | {{- end }} 100 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := .Release.Name -}} 3 | {{- $servicePort := .Values.service.port -}} 4 | {{- $ingressPath := .Values.ingress.path -}} 5 | apiVersion: extensions/v1beta1 6 | kind: Ingress 7 | metadata: 8 | name: {{ $fullName }} 9 | labels: 10 | app: {{ $fullName }} 11 | chart: {{ template "clickhouse-on-kube.chart" . }} 12 | release: {{ .Release.Name }} 13 | heritage: {{ .Release.Service }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{ toYaml . | indent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ . }} 32 | http: 33 | paths: 34 | - path: {{ $ingressPath }} 35 | backend: 36 | serviceName: {{ $fullName }} 37 | servicePort: http 38 | {{- end }} 39 | {{- end }} 40 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/roles.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac -}} 2 | apiVersion: rbac.authorization.k8s.io/v1beta1 3 | kind: Role 4 | metadata: 5 | name: patch-pod 6 | namespace: {{ .Release.Namespace }} 7 | rules: 8 | - apiGroups: 9 | - '*' 10 | resources: 11 | - pods 12 | verbs: 13 | - patch 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1beta1 16 | kind: RoleBinding 17 | metadata: 18 | name: default:patch-pod 19 | namespace: {{ .Release.Namespace }} 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: Role 23 | name: patch-pod 24 | subjects: 25 | - kind: ServiceAccount 26 | name: default 27 | namespace: {{ .Release.Namespace }} 28 | {{- end }} -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{range $i, $e := until (atoi (printf "%d" (int64 .Values.replicaCount))) }} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ $.Release.Name }}-{{$i}} 6 | labels: 7 | app: {{ template "clickhouse-on-kube.name" $ }} 8 | chart: {{ template "clickhouse-on-kube.chart" $ }} 9 | release: {{ $.Release.Name }} 10 | heritage: {{ $.Release.Service }} 11 | spec: 12 | type: {{ $.Values.service.type }} 13 | ports: 14 | - name: rpc 15 | port: {{ $.Values.service.rpc_port }} 16 | targetPort: {{ $.Values.service.rpc_port }} 17 | protocol: TCP 18 | - name: rest 19 | port: {{ $.Values.service.rest_port }} 20 | targetPort: {{ $.Values.service.rest_port }} 21 | protocol: TCP 22 | - name: interserver 23 | port: 9009 24 | targetPort: 9009 25 | protocol: TCP 26 | selector: 27 | app: {{ $.Release.Name }} 28 | shard_name: {{ $.Release.Name }}-{{$i}} 29 | release: {{ $.Release.Name }} 30 | --- 31 | {{end}} 32 | # --- elb --- 33 | {{- if .Values.elb.enabled -}} 34 | apiVersion: v1 35 | kind: Service 36 | metadata: 37 | name: {{ .Release.Name }} 38 | labels: 39 | app: {{ .Release.Name }} 40 | chart: {{ template "clickhouse-on-kube.chart" . }} 41 | release: {{ .Release.Name }} 42 | heritage: {{ .Release.Service }} 43 | annotations: 44 | service.beta.kubernetes.io/aws-load-balancer-internal: {{ .Values.elb.internal_range }} 45 | spec: 46 | externalTrafficPolicy: Cluster 47 | ports: 48 | - name: rpc 49 | port: {{ $.Values.service.rpc_port }} 50 | targetPort: {{ $.Values.service.rpc_port }} 51 | protocol: TCP 52 | - name: rest 53 | port: {{ $.Values.service.rest_port }} 54 | targetPort: {{ $.Values.service.rest_port }} 55 | protocol: TCP 56 | selector: 57 | app: {{ .Release.Name }} 58 | sessionAffinity: None 59 | type: LoadBalancer 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/templates/statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: StatefulSet 3 | metadata: 4 | name: {{ .Release.Name }} 5 | labels: 6 | app: {{ .Release.Name }} 7 | chart: {{ template "clickhouse-on-kube.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | podManagementPolicy: OrderedReady 12 | replicas: {{ .Values.replicaCount }} 13 | selector: 14 | matchLabels: 15 | app: {{ .Release.Name }} 16 | release: {{ .Release.Name }} 17 | serviceName: {{ .Release.Name }} 18 | template: 19 | metadata: 20 | labels: 21 | app: {{ .Release.Name }} 22 | release: {{ .Release.Name }} 23 | spec: 24 | initContainers: 25 | - name: labeler 26 | image: devth/k8s-labeler 27 | env: 28 | - name: KUBE_NAMESPACE 29 | valueFrom: 30 | fieldRef: 31 | fieldPath: metadata.namespace 32 | - name: KUBE_LABEL_shard_name 33 | valueFrom: 34 | fieldRef: 35 | fieldPath: metadata.name 36 | containers: 37 | - name: {{ .Release.Name }} 38 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 39 | imagePullPolicy: {{ .Values.image.pullPolicy }} 40 | ports: 41 | - name: rpc 42 | containerPort: {{ .Values.service.rpc_port }} 43 | protocol: TCP 44 | - name: rest 45 | containerPort: {{ .Values.service.rest_port }} 46 | protocol: TCP 47 | - name: interserver 48 | containerPort: 9009 49 | protocol: TCP 50 | livenessProbe: 51 | httpGet: 52 | path: / 53 | port: rest 54 | initialDelaySeconds: 10 55 | periodSeconds: 10 56 | failureThreshold: 5 57 | timeoutSeconds: 1 58 | readinessProbe: 59 | httpGet: 60 | path: / 61 | port: rest 62 | initialDelaySeconds: 10 63 | periodSeconds: 10 64 | failureThreshold: 5 65 | timeoutSeconds: 1 66 | volumeMounts: 67 | - name: clickhouse-configd 68 | mountPath: /etc/clickhouse-server/config.d 69 | {{ if .Values.security.enabled }} 70 | - name: clickhouse-usersd 71 | mountPath: /etc/clickhouse-server/users.d 72 | {{ end }} 73 | {{ if .Values.ebs.enabled }} 74 | - name: clickhouse-data 75 | mountPath: /clickhouse 76 | {{ end }} 77 | resources: 78 | {{ toYaml .Values.resources | indent 12 }} 79 | volumes: 80 | - name: clickhouse-configd 81 | configMap: 82 | name: {{ .Release.Name }}-configd 83 | {{ if .Values.security.enabled }} 84 | - name: clickhouse-usersd 85 | configMap: 86 | name: {{ .Release.Name }}-usersd 87 | {{ end }} 88 | {{ if .Values.image.imagePullSecrets }} 89 | imagePullSecrets: 90 | - name: {{ .Values.image.imagePullSecrets }} 91 | {{ end }} 92 | {{- with .Values.nodeSelector }} 93 | nodeSelector: 94 | {{ toYaml . | indent 8 }} 95 | {{- end }} 96 | {{- with .Values.affinity }} 97 | affinity: 98 | {{ toYaml . | indent 8 }} 99 | {{- end }} 100 | {{- with .Values.tolerations }} 101 | tolerations: 102 | {{ toYaml . | indent 8 }} 103 | {{- end }} 104 | 105 | {{ if .Values.ebs.enabled }} 106 | volumeClaimTemplates: 107 | - metadata: 108 | name: clickhouse-data 109 | spec: 110 | accessModes: 111 | - ReadWriteOnce 112 | resources: 113 | requests: 114 | storage: {{ .Values.ebs.size }} 115 | {{ end }} 116 | -------------------------------------------------------------------------------- /helm/clickhouse-on-kube/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for clickhouse-on-kube. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | # number of nodes / shards 6 | replicaCount: 1 7 | 8 | image: 9 | repository: dmitryb/clickhouse-server 10 | tag: latest 11 | pullPolicy: Always 12 | # imagePullSecrets: grabdsbot 13 | 14 | # grant pods/patch to default sa 15 | rbac: true 16 | 17 | service: 18 | type: ClusterIP 19 | rpc_port: 9000 20 | rest_port: 8123 21 | 22 | ebs: 23 | # use aws ebs 24 | enabled: true 25 | size: 8Gi 26 | 27 | elb: 28 | # use frontend elb 29 | enabled: true 30 | internal_range: 10.59.0.0/16,10.10.0.0/16 31 | 32 | zk: 33 | enabled: true 34 | port: 2181 35 | servers: 36 | - zk-zookeeper.zk 37 | 38 | security: 39 | enabled: true 40 | 41 | profiles: 42 | - name: default 43 | max_memory_usage: 10000000000 44 | use_uncompressed_cache: 0 45 | load_balancing: random 46 | 47 | quotas: 48 | - name: default 49 | duration: 3600 50 | queries: 0 51 | errors: 0 52 | result_rows: 0 53 | read_rows: 0 54 | execution_time: 0 55 | 56 | users: 57 | - name: user_1 58 | password: djfgyeugf3484rf4 59 | profile: default 60 | quota: default 61 | - name: svc 62 | password: sdkfh1kjdshfkjj3 63 | profile: default 64 | quota: default 65 | 66 | # is used for running distributed queries (system) 67 | distributed: 68 | user: sys_distrib_query 69 | password: iudhc87wfy87hrevh 70 | profile: default 71 | quota: default 72 | 73 | ingress: 74 | # use ingress 75 | enabled: false 76 | annotations: {} 77 | # kubernetes.io/ingress.class: nginx 78 | # kubernetes.io/tls-acme: "true" 79 | path: / 80 | hosts: 81 | - chart-example.local 82 | tls: [] 83 | # - secretName: chart-example-tls 84 | # hosts: 85 | # - chart-example.local 86 | 87 | resources: 88 | limits: 89 | cpu: 250m 90 | memory: 1024Mi 91 | requests: 92 | cpu: 250m 93 | memory: 1024Mi 94 | 95 | nodeSelector: 96 | # use nodes with label 97 | type: services 98 | 99 | tolerations: [] 100 | 101 | affinity: {} 102 | --------------------------------------------------------------------------------