#!/usr/bin/env bash # ============================================================================= # Tank War Server — one-shot K8s deploy # # Mirrors the style of WarmCheck's run-deploy.sh: # 1) sync source to master # 2) build docker image on master # 3) distribute image tarball to all worker nodes and `ctr` import # 4) apply K8s manifests and restart the deployment # ============================================================================= set -euo pipefail # ---------- Configurable ------------------------------------------------------ # Host that the LOCAL developer machine can reach (uses your ssh_config alias). MASTER_HOST="${MASTER_HOST:-host_172.16.16.16}" # Intranet IPs that the MASTER uses to reach workers (no alias on the CVMs). WORKER_INTRANET_IPS=( "10.1.0.6" "172.16.32.10" "172.16.32.16" ) NAMESPACE="tankwar" IMAGE_NAME="tankwar-server" IMAGE_TAG="${IMAGE_TAG:-latest}" REMOTE_WORKDIR="/root/tankwar" SSH_USER="root" SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10" # ---------- Paths ------------------------------------------------------------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" SERVER_DIR="${PROJECT_ROOT}/server" K8S_DIR="${PROJECT_ROOT}/deploy/k8s" # ---------- Helpers ----------------------------------------------------------- log() { printf "\033[1;36m[deploy]\033[0m %s\n" "$*"; } ok() { printf "\033[1;32m[ ok ]\033[0m %s\n" "$*"; } warn() { printf "\033[1;33m[warn]\033[0m %s\n" "$*"; } die() { printf "\033[1;31m[fail]\033[0m %s\n" "$*" >&2; exit 1; } ssh_master() { ssh ${SSH_OPTS} "${SSH_USER}@${MASTER_HOST}" "$@"; } ssh_host() { local h="$1"; shift; ssh ${SSH_OPTS} "${SSH_USER}@${h}" "$@"; } # ============================================================================= # Step 1 — sync server source code & k8s manifests to master # ============================================================================= step_sync() { log "1/5 Syncing source to master (${MASTER_HOST}) ..." ssh_master "mkdir -p ${REMOTE_WORKDIR}/server ${REMOTE_WORKDIR}/deploy/k8s" rsync -az --delete \ --exclude '.git' \ --exclude '.DS_Store' \ -e "ssh ${SSH_OPTS}" \ "${SERVER_DIR}/" \ "${SSH_USER}@${MASTER_HOST}:${REMOTE_WORKDIR}/server/" rsync -az --delete \ -e "ssh ${SSH_OPTS}" \ "${K8S_DIR}/" \ "${SSH_USER}@${MASTER_HOST}:${REMOTE_WORKDIR}/deploy/k8s/" ok "source synced" } # ============================================================================= # Step 2 — build docker image on master # ============================================================================= step_build() { log "2/5 Building image ${IMAGE_NAME}:${IMAGE_TAG} on master ..." ssh_master "cd ${REMOTE_WORKDIR}/server && \ docker build --pull -t ${IMAGE_NAME}:${IMAGE_TAG} -t ${IMAGE_NAME}:latest ." ok "image built" } # ============================================================================= # Step 3 — distribute image to every worker via containerd (ctr import) # # The cluster uses containerd directly (not docker-shim). Each node must # have the image in the "k8s.io" namespace to be usable by kubelet. # ============================================================================= step_distribute() { log "3/5 Distributing image to workers ..." # Export once on master local remote_tar="/tmp/${IMAGE_NAME}-${IMAGE_TAG}.tar" ssh_master "docker save ${IMAGE_NAME}:${IMAGE_TAG} -o ${remote_tar} && ls -lh ${remote_tar}" # Import on master's containerd (k8s.io ns) so the scheduler can use it locally too ssh_master "ctr -n k8s.io images import ${remote_tar} && \ ctr -n k8s.io images tag --force docker.io/library/${IMAGE_NAME}:${IMAGE_TAG} \ docker.io/library/${IMAGE_NAME}:latest" ok "master imported" # Fan-out to workers — executed FROM the master using intranet IPs. for ip in "${WORKER_INTRANET_IPS[@]}"; do log " -> ${ip}" ssh_master "scp ${SSH_OPTS} ${remote_tar} ${SSH_USER}@${ip}:${remote_tar} && \ ssh ${SSH_OPTS} ${SSH_USER}@${ip} 'ctr -n k8s.io images import ${remote_tar} && \ ctr -n k8s.io images tag --force docker.io/library/${IMAGE_NAME}:${IMAGE_TAG} \ docker.io/library/${IMAGE_NAME}:latest && \ rm -f ${remote_tar}'" ok " ${ip} imported" done ssh_master "rm -f ${remote_tar}" ok "distribution done" } # ============================================================================= # Step 4 — apply manifests & roll the deployment # ============================================================================= step_apply() { log "4/5 Applying K8s manifests ..." ssh_master "kubectl apply -f ${REMOTE_WORKDIR}/deploy/k8s/namespace.yaml" ssh_master "kubectl apply -f ${REMOTE_WORKDIR}/deploy/k8s/service.yaml" ssh_master "kubectl apply -f ${REMOTE_WORKDIR}/deploy/k8s/deployment.yaml" # Force a new rollout so we pick up the newly-imported image even # when the tag stays :latest. ssh_master "kubectl -n ${NAMESPACE} set image deploy/tankwar-server \ tankwar-server=${IMAGE_NAME}:${IMAGE_TAG} --record=false || true" log " waiting for rollout ..." ssh_master "kubectl -n ${NAMESPACE} rollout status deploy/tankwar-server --timeout=180s" ok "deployment is live" } # ============================================================================= # Step 5 — sanity check # ============================================================================= step_verify() { log "5/5 Verifying ..." ssh_master "kubectl -n ${NAMESPACE} get pods,svc -o wide" echo ssh_master "kubectl -n ${NAMESPACE} logs deploy/tankwar-server --tail=20 || true" echo ok "all done. NodePort: 30081" cat <