#!/usr/bin/env bash # # Install ClickHouse server + client on Debian/Ubuntu from packages.clickhouse.com. # Avoids Ubuntu "universe" ClickHouse 18.x, which is outdated and often misconfigured. # # Usage (from repo root or any path): # bash scripts/setup-clickhouse.sh # bash scripts/setup-clickhouse.sh --purge-existing # remove old clickhouse* packages first # CLICKHOUSE_CHANNEL=lts bash scripts/setup-clickhouse.sh # # Env: # CLICKHOUSE_CHANNEL stable (default) or lts — see https://clickhouse.com/docs/knowledgebase/production # WAIT_FOR_APT_LOCK_SEC max seconds to wait for apt/dpkg lock (default: 900) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" WAIT_FOR_APT_LOCK_SEC="${WAIT_FOR_APT_LOCK_SEC:-900}" APT_LOCK_SLEEP_SEC="${APT_LOCK_SLEEP_SEC:-5}" # Wait until unattended-upgrades / another apt releases dpkg locks (avoids E: Could not get lock ... lock-frontend). wait_for_apt_lock() { local waited=0 while true; do if ! _apt_lock_held; then return 0 fi if (( waited >= WAIT_FOR_APT_LOCK_SEC )); then echo "Error: timed out after ${WAIT_FOR_APT_LOCK_SEC}s waiting for apt/dpkg lock." >&2 echo "Another process is using apt (often PID 1 child: unattended-upgrades). Check:" >&2 echo " sudo ps aux | egrep 'apt|dpkg' | grep -v egrep" >&2 echo " sudo systemctl status unattended-upgrades" >&2 echo "Then re-run this script, or set WAIT_FOR_APT_LOCK_SEC=3600 to wait longer." >&2 exit 1 fi echo "==> Waiting for apt/dpkg lock to clear (${waited}s / ${WAIT_FOR_APT_LOCK_SEC}s max)..." sleep "$APT_LOCK_SLEEP_SEC" waited=$((waited + APT_LOCK_SLEEP_SEC)) done } _apt_lock_held() { if command -v fuser >/dev/null 2>&1; then sudo fuser /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock \ /var/cache/apt/archives/lock /var/lib/apt/lists/lock >/dev/null 2>&1 return $? fi pgrep -x apt >/dev/null 2>&1 || pgrep -x apt-get >/dev/null 2>&1 || pgrep -x dpkg >/dev/null 2>&1 } apt_get() { wait_for_apt_lock sudo apt-get "$@" } CLICKHOUSE_LIST="/etc/apt/sources.list.d/clickhouse.list" KEYRING="/usr/share/keyrings/clickhouse-keyring.gpg" CHANNEL="${CLICKHOUSE_CHANNEL:-stable}" PURGE_EXISTING=false for arg in "$@"; do case "$arg" in --purge-existing) PURGE_EXISTING=true ;; -h|--help) echo "Usage: $0 [--purge-existing]" echo " --purge-existing apt purge existing clickhouse* packages before installing (destructive for old installs)." echo " Env: CLICKHOUSE_CHANNEL=stable|lts (default: stable), WAIT_FOR_APT_LOCK_SEC (default: 900)" exit 0 ;; *) echo "Unknown option: $arg (try --help)" >&2 exit 1 ;; esac done if [[ ! -f /etc/os-release ]]; then echo "Error: /etc/os-release not found. This script supports Debian/Ubuntu only." >&2 exit 1 fi # shellcheck source=/dev/null source /etc/os-release case "${ID:-},${ID_LIKE:-}" in debian,*|ubuntu,*|*,*debian*|*,*ubuntu*) ;; *) echo "Warning: OS ID=${ID:-unknown} may not be Debian/Ubuntu. Continuing if apt works." >&2 ;; esac if [[ "$CHANNEL" != "stable" && "$CHANNEL" != "lts" ]]; then echo "Error: CLICKHOUSE_CHANNEL must be stable or lts, got: $CHANNEL" >&2 exit 1 fi if [[ "$PURGE_EXISTING" == "true" ]]; then echo "==> Purging existing ClickHouse packages (--purge-existing)..." sudo systemctl stop clickhouse-server 2>/dev/null || true sudo systemctl stop clickhouse-keeper 2>/dev/null || true apt_get purge -y 'clickhouse*' || true apt_get autoremove -y || true fi echo "==> Installing prerequisites..." export DEBIAN_FRONTEND=noninteractive apt_get update -qq apt_get install -y apt-transport-https ca-certificates curl gnupg echo "==> Adding ClickHouse APT repo (channel: $CHANNEL)..." sudo mkdir -p /usr/share/keyrings if [[ ! -f "$KEYRING" ]]; then curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' \ | sudo gpg --dearmor -o "$KEYRING" fi ARCH="$(dpkg --print-architecture)" echo "deb [signed-by=${KEYRING} arch=${ARCH}] https://packages.clickhouse.com/deb ${CHANNEL} main" \ | sudo tee "$CLICKHOUSE_LIST" >/dev/null echo "==> Installing clickhouse-server and clickhouse-client..." apt_get update -qq apt_get install -y clickhouse-server clickhouse-client echo "==> Enabling and starting clickhouse-server..." sudo systemctl enable --now clickhouse-server sudo systemctl --no-pager --full status clickhouse-server || true echo "==> Verifying..." if clickhouse-client -q "SELECT version(), 1" 2>/dev/null; then echo "ClickHouse responded OK." else echo "Note: clickhouse-client check failed (password required?). Try: clickhouse-client --password -q \"SELECT 1\"" >&2 echo "Logs: sudo tail -50 /var/log/clickhouse-server/clickhouse-server.err.log" >&2 exit 1 fi echo "" echo "Done. Client: clickhouse-client | HTTP default :8123 | native default :9000" echo "Optional: open ports in ufw / cloud NSG only if you need remote access."