317 lines
13 KiB
Bash
Executable File
317 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# prepare-host.sh — siapkan server Ubuntu 24.04 (noble) untuk instalasi SiCABE
|
|
# -----------------------------------------------------------------------------
|
|
# Menyiapkan satu host sebelum `install.sh` dijalankan:
|
|
# 1. paket apt yang benar-benar dipakai script deploy (bukan container)
|
|
# 2. Docker Engine + plugin compose v2 (opsional --no-docker)
|
|
# 3. sysctl vm.max_map_count=262144 → SYARAT KERAS OpenSearch
|
|
# 4. (opsional --swap-off) matikan swap → rekomendasi OpenSearch
|
|
# 5. prometheus-node-exporter (:9100) → PRA-SYARAT dashboard Host
|
|
# (opsional --no-node-exporter)
|
|
# 6. precheck: AVX2, jam, RAM, disk, port yang akan dipakai
|
|
#
|
|
# Pemakaian:
|
|
# sudo ./scripts/prepare-host.sh --dry-run # lihat rencana, TIDAK mengubah apa pun
|
|
# sudo ./scripts/prepare-host.sh # jalankan
|
|
# sudo ./scripts/prepare-host.sh --no-docker # bila Docker sudah dipasang manual
|
|
# sudo ./scripts/prepare-host.sh --swap-off # ikut mematikan swap
|
|
# sudo ./scripts/prepare-host.sh --no-node-exporter # jangan pasang node_exporter
|
|
#
|
|
# Sifat: IDEMPOTEN — aman dijalankan berulang.
|
|
# =============================================================================
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DEPLOY_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
DRY_RUN=0
|
|
WITH_DOCKER=1
|
|
SWAP_OFF=0
|
|
WITH_NODE_EXPORTER=1
|
|
|
|
RED=$'\e[31m'; GRN=$'\e[32m'; YLW=$'\e[33m'; BLD=$'\e[1m'; NC=$'\e[0m'
|
|
ok() { echo -e " ${GRN}[OK]${NC} $*"; }
|
|
inf() { echo -e " ${YLW}[..]${NC} $*"; }
|
|
warn() { echo -e " ${YLW}[WARN]${NC} $*"; }
|
|
err() { echo -e " ${RED}[!!]${NC} $*" >&2; }
|
|
|
|
# Jalankan perintah; pada --dry-run cukup dicetak.
|
|
run() {
|
|
if [[ ${DRY_RUN} -eq 1 ]]; then
|
|
echo " [dry-run] $*"
|
|
return 0
|
|
fi
|
|
"$@"
|
|
}
|
|
|
|
usage() {
|
|
sed -n '2,22p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
exit 0
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--dry-run) DRY_RUN=1 ;;
|
|
--no-docker) WITH_DOCKER=0 ;;
|
|
--swap-off) SWAP_OFF=1 ;;
|
|
--no-node-exporter) WITH_NODE_EXPORTER=0 ;;
|
|
-h|--help) usage ;;
|
|
*) err "Argumen tidak dikenal: $1"; exit 2 ;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
# ─── 0. OS & hak akses ──────────────────────────────────────────────────────
|
|
check_os() {
|
|
echo -e "${BLD}A. Sistem${NC}"
|
|
if [[ ! -r /etc/os-release ]]; then
|
|
err "/etc/os-release tidak terbaca — script ini untuk Ubuntu."; exit 1
|
|
fi
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
echo " OS: ${PRETTY_NAME:-?} (codename: ${VERSION_CODENAME:-?})"
|
|
if [[ "${ID:-}" != "ubuntu" ]]; then
|
|
warn "Bukan Ubuntu (ID=${ID:-?}) — script ini diuji pada Ubuntu 24.04; lanjutkan hati-hati."
|
|
elif [[ "${VERSION_CODENAME:-}" != "noble" ]]; then
|
|
warn "Bukan 24.04/noble (codename=${VERSION_CODENAME:-?}) — nama paket bisa berbeda."
|
|
else
|
|
ok "Ubuntu 24.04 (noble) — sesuai target"
|
|
fi
|
|
if [[ ${EUID} -ne 0 && ${DRY_RUN} -eq 0 ]]; then
|
|
err "Perlu root: jalankan dengan sudo."
|
|
exit 1
|
|
fi
|
|
[[ ${EUID} -ne 0 ]] && inf "berjalan tanpa root (dry-run) — hanya memeriksa"
|
|
return 0
|
|
}
|
|
|
|
# ─── 1. Paket apt ───────────────────────────────────────────────────────────
|
|
# Daftar ini berasal dari pemakaian nyata di deploy/, bukan tebakan:
|
|
# htpasswd (apache2-utils) → password.db Trino (bootstrap/install)
|
|
# ldapsearch (ldap-utils) → bootstrap step 3 + verify-phase1
|
|
# mysql (client) → seed/reapply StarRocks + verify-phase4
|
|
# openssl → keystore Trino
|
|
# curl → hampir semua script
|
|
# python3 + PyYAML → lib/cluster/*.py + bootstrap
|
|
# ca-certificates/gnupg → repo & TLS
|
|
PKGS=(ca-certificates curl gnupg openssl apache2-utils ldap-utils python3 python3-yaml git unzip)
|
|
|
|
install_packages() {
|
|
echo
|
|
echo -e "${BLD}B. Paket apt${NC}"
|
|
# Klien MySQL: nama paket berbeda antar rilis → coba berurutan, laporkan yang dipakai.
|
|
local mysql_candidates=(default-mysql-client mysql-client mariadb-client) picked=""
|
|
run apt-get update -qq
|
|
run apt-get install -y --no-install-recommends "${PKGS[@]}"
|
|
for cand in "${mysql_candidates[@]}"; do
|
|
if apt-cache show "${cand}" >/dev/null 2>&1; then
|
|
run apt-get install -y --no-install-recommends "${cand}"
|
|
picked="${cand}"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -n "${picked}" ]]; then
|
|
ok "paket terpasang (klien MySQL: ${picked})"
|
|
else
|
|
warn "klien MySQL tidak ditemukan di repo — seed/reapply StarRocks butuh binary 'mysql'"
|
|
fi
|
|
|
|
# MinIO client (mc) dipakai verify-phase2 & bootstrap, tapi tidak selalu ada di repo.
|
|
if command -v mc &>/dev/null; then
|
|
ok "mc tersedia"
|
|
else
|
|
warn "mc (MinIO client) belum ada — sebagian cek Fase 2 & bootstrap akan dilewati."
|
|
echo " Pasang manual bila diinginkan, mis.:"
|
|
echo " curl -sSL https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc && chmod +x /usr/local/bin/mc"
|
|
fi
|
|
|
|
# node_exporter (:9100) — PRA-SYARAT dashboard Host (job `node` di Prometheus).
|
|
# Best-effort: paket ada di repo Ubuntu (universe). Bila tak tersedia, dashboard
|
|
# Host hanya menampilkan target DOWN — tidak fatal.
|
|
if [[ ${WITH_NODE_EXPORTER} -eq 0 ]]; then
|
|
inf "node_exporter dilewati (--no-node-exporter)"
|
|
elif command -v prometheus-node-exporter &>/dev/null; then
|
|
ok "node_exporter sudah tersedia (:9100)"
|
|
elif apt-cache show prometheus-node-exporter >/dev/null 2>&1; then
|
|
run apt-get install -y --no-install-recommends prometheus-node-exporter
|
|
ok "node_exporter terpasang & listen :9100 (di-scrape Prometheus job 'node')"
|
|
else
|
|
warn "paket prometheus-node-exporter tidak ada di repo — dashboard Host akan kosong"
|
|
fi
|
|
}
|
|
|
|
# ─── 2. Docker ──────────────────────────────────────────────────────────────
|
|
install_docker() {
|
|
echo
|
|
echo -e "${BLD}C. Docker${NC}"
|
|
if [[ ${WITH_DOCKER} -eq 0 ]]; then
|
|
inf "--no-docker: dilewati"
|
|
elif command -v docker &>/dev/null && docker compose version &>/dev/null; then
|
|
ok "Docker + compose v2 sudah tersedia ($(docker --version))"
|
|
else
|
|
inf "memasang Docker Engine + plugin compose dari repo resmi Docker"
|
|
run install -m 0755 -d /etc/apt/keyrings
|
|
# kunci GPG repo Docker
|
|
if [[ ${DRY_RUN} -eq 0 ]]; then
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
|
|
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
|
https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" \
|
|
> /etc/apt/sources.list.d/docker.list
|
|
apt-get update -qq
|
|
apt-get install -y --no-install-recommends \
|
|
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
else
|
|
echo " [dry-run] tambah repo Docker + pasang docker-ce, docker-compose-plugin, …"
|
|
fi
|
|
fi
|
|
|
|
# Pengguna non-root: supaya `docker` bisa dipakai tanpa sudo setelah re-login.
|
|
local target_user="${SUDO_USER:-}"
|
|
if [[ -n "${target_user}" && "${target_user}" != "root" && ${DRY_RUN} -eq 0 ]]; then
|
|
if id -nG "${target_user}" | grep -qw docker; then
|
|
ok "pengguna ${target_user} sudah di grup docker"
|
|
else
|
|
run usermod -aG docker "${target_user}"
|
|
warn "pengguna ${target_user} ditambahkan ke grup docker — LOGOUT/LOGIN agar berlaku"
|
|
fi
|
|
fi
|
|
|
|
if [[ ${DRY_RUN} -eq 0 ]] && command -v docker &>/dev/null; then
|
|
if systemctl is-active --quiet docker; then
|
|
ok "layanan docker aktif"
|
|
else
|
|
run systemctl enable --now docker
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ─── 3. sysctl (SYARAT KERAS OpenSearch) ────────────────────────────────────
|
|
configure_sysctl() {
|
|
echo
|
|
echo -e "${BLD}D. sysctl — vm.max_map_count${NC}"
|
|
local want=262144 have
|
|
have="$(sysctl -n vm.max_map_count 2>/dev/null || echo 0)"
|
|
if [[ "${have}" -ge "${want}" ]]; then
|
|
ok "vm.max_map_count=${have} (cukup)"
|
|
return 0
|
|
fi
|
|
warn "vm.max_map_count=${have} < ${want} — OpenSearch GAGAL start bila kurang (syarat keras)"
|
|
local f=/etc/sysctl.d/99-sicabe.conf
|
|
if [[ ${DRY_RUN} -eq 1 ]]; then
|
|
echo " [dry-run] tulis 'vm.max_map_count=${want}' ke ${f} lalu sysctl --system"
|
|
return 0
|
|
fi
|
|
echo "vm.max_map_count=${want}" > "${f}"
|
|
sysctl --system >/dev/null 2>&1 || true
|
|
ok "ditetapkan vm.max_map_count=$(sysctl -n vm.max_map_count 2>/dev/null) (persist: ${f})"
|
|
}
|
|
|
|
# ─── 4. Swap (opsional, rekomendasi OpenSearch) ─────────────────────────────
|
|
configure_swap() {
|
|
echo
|
|
echo -e "${BLD}E. Swap${NC}"
|
|
if [[ ${SWAP_OFF} -eq 0 ]]; then
|
|
inf "--swap-off tidak diberikan: swap dibiarkan (OpenSearch hanya merekomendasikan)"
|
|
return 0
|
|
fi
|
|
if [[ -z "$(swapon --show 2>/dev/null)" ]]; then
|
|
ok "swap sudah mati"
|
|
return 0
|
|
fi
|
|
if [[ ${DRY_RUN} -eq 1 ]]; then
|
|
echo " [dry-run] swapoff -a + nonaktifkan entri swap di /etc/fstab (dengan backup)"
|
|
return 0
|
|
fi
|
|
cp -a /etc/fstab "/etc/fstab.sicabe-bak-$(date +%Y%m%d%H%M%S)"
|
|
swapoff -a
|
|
sed -i 's|^\([^#].*[[:space:]]swap[[:space:]].*\)$|# \1|' /etc/fstab
|
|
ok "swap dimatikan (fstab dibackup; entri swap dikomentari)"
|
|
warn "pastikan RAM cukup — tanpa swap, OOM langsung membunuh proses"
|
|
}
|
|
|
|
# ─── 5. Precheck ────────────────────────────────────────────────────────────
|
|
precheck() {
|
|
echo
|
|
echo -e "${BLD}F. Precheck${NC}"
|
|
|
|
# AVX2: syarat keras starrocks-cn (tanpa ini container gagal SIGILL)
|
|
if grep -qo -m1 avx2 /proc/cpuinfo 2>/dev/null; then
|
|
ok "CPU mendukung AVX2 (syarat starrocks-cn)"
|
|
else
|
|
warn "AVX2 TIDAK terdeteksi — container starrocks-cn akan gagal (SIGILL)."
|
|
echo " Jalankan install tanpa Fase 4, atau pindahkan starrocks-cn ke host ber-AVX2."
|
|
fi
|
|
|
|
# Jam: OIDC/JWT sensitif terhadap skew (preflight juga memeriksanya)
|
|
if command -v timedatectl &>/dev/null; then
|
|
local ntp
|
|
ntp="$(timedatectl show -p NTPSynchronized --value 2>/dev/null || echo "")"
|
|
case "${ntp}" in
|
|
yes) ok "jam tersinkronisasi (NTP aktif)" ;;
|
|
no) warn "jam BELUM tersinkron — aktifkan NTP (timedatectl set-ntp true) sebelum install" ;;
|
|
*) inf "status NTP tidak dapat dibaca di lingkungan ini — verifikasi manual di server nyata" ;;
|
|
esac
|
|
fi
|
|
|
|
# RAM & disk
|
|
local ram_gb disk_gb
|
|
ram_gb="$(awk '/MemTotal/{printf "%d", $2/1024/1024}' /proc/meminfo 2>/dev/null || echo 0)"
|
|
disk_gb="$(df -BG --output=avail /var/lib/docker 2>/dev/null | tail -1 | tr -dc '0-9')"
|
|
[[ -z "${disk_gb}" ]] && disk_gb="$(df -BG --output=avail / | tail -1 | tr -dc '0-9')"
|
|
echo " RAM: ${ram_gb} GB · disk tersedia: ${disk_gb:-?} GB"
|
|
[[ "${ram_gb}" -lt 8 ]] 2>/dev/null && warn "RAM < 8 GB: sesuaikan penempatan komponen (lihat reviews/sicabe-09…)"
|
|
[[ "${disk_gb:-0}" -lt 20 ]] 2>/dev/null && warn "disk < 20 GB: image + volume bisa tidak cukup (siapkan ±20 GB untuk image)"
|
|
|
|
# Port yang akan dipakai: dibaca dari config/environment.defaults.sh agar sinkron
|
|
local ports=""
|
|
if [[ -f "${DEPLOY_DIR}/config/environment.defaults.sh" ]]; then
|
|
ports="$(grep -oE ':-(12[0-9]{3})\}' "${DEPLOY_DIR}/config/environment.defaults.sh" \
|
|
| tr -dc '0-9\n' | sort -un | paste -sd' ' -)"
|
|
fi
|
|
if [[ -n "${ports}" ]]; then
|
|
local busy="" p
|
|
for p in ${ports}; do
|
|
if (exec 3<>"/dev/tcp/127.0.0.1/${p}") 2>/dev/null; then busy+=" ${p}"; fi
|
|
exec 3<&- 2>/dev/null
|
|
done
|
|
if [[ -z "${busy}" ]]; then
|
|
ok "semua port 12xxx yang dibutuhkan masih bebas"
|
|
else
|
|
warn "port sudah terpakai:${busy}"
|
|
echo " Bila itu stack SiCABE yang lama: matikan dulu — stack single-host & multi-host tidak untuk berjalan bersamaan di satu host."
|
|
fi
|
|
else
|
|
inf "daftar port tidak terbaca dari environment.defaults.sh — lewati pemeriksaan port"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
echo -e "${BLD}Persiapan host untuk SiCABE${NC}"
|
|
echo " deploy dir : ${DEPLOY_DIR}"
|
|
[[ ${DRY_RUN} -eq 1 ]] && echo -e " ${YLW}MODE DRY-RUN — tidak ada perubahan sistem${NC}"
|
|
echo
|
|
check_os
|
|
install_packages
|
|
install_docker
|
|
configure_sysctl
|
|
configure_swap
|
|
precheck
|
|
echo
|
|
echo -e "${BLD}Ringkasan${NC}"
|
|
echo " Langkah berikutnya di host ini:"
|
|
echo " 1. salin direktori deploy/ ke host ini"
|
|
echo " 2. ./scripts/prefetch-images.sh # tarik semua image lebih dulu"
|
|
echo " 3. ./install.sh # atau ./install.sh --host <nama>"
|
|
echo " 4. ./install.sh --preflight (cek keterjangkauan lintas host)"
|
|
}
|
|
|
|
main "$@"
|