Add GitLab single-node offline installer

This commit is contained in:
shenlan 2025-10-02 20:22:32 +08:00
parent c9449f16eb
commit 356ebc392e
6 changed files with 493 additions and 0 deletions

View File

@ -0,0 +1,19 @@
# GitLab Single-Node Offline Installer Assets
This directory contains helper files used to assemble the GitLab offline package:
- `setup.sh` main installer script invoked by `install-gitlab.sh`
- `install-gitlab.sh` wrapper that forwards to `setup.sh`
- `gitlab-values.single-node.yaml` values template optimised for single-node installations
- `gitlab-offline.env.example` sample configuration file consumed by the installer
The offline package builder (`scripts/create-gitlab-offline-package.sh`) copies these
artifacts into the final archive so that users can extract and run:
```bash
tar -xvpf offline-package-gitlab-amd64.tar.gz
cd gitlab-offline-package/
cp gitlab-offline.env.example gitlab-offline.env
# Edit gitlab-offline.env and then execute:
bash install-gitlab.sh --version <VERSION> --domain <DOMAIN> [--namespace <NAMESPACE>]
```

View File

@ -0,0 +1,6 @@
# Copy this file to gitlab-offline.env and adjust the values before running install-gitlab.sh
GITLAB_VERSION=7.8.0
GITLAB_DOMAIN=gitlab.example.com
GITLAB_NAMESPACE=gitlab
# Set to 1 to skip loading bundled images
# SKIP_IMAGE_LOAD=1

View File

@ -0,0 +1,132 @@
# Rendered by install-gitlab.sh using envsubst. Variables:
# ${GITLAB_DOMAIN}
# ${GITLAB_NAMESPACE}
global:
edition: ce
hosts:
domain: ${GITLAB_DOMAIN}
gitlab:
name: gitlab.${GITLAB_DOMAIN}
registry:
name: registry.${GITLAB_DOMAIN}
minio:
name: minio.${GITLAB_DOMAIN}
smartcard:
name: smartcard.${GITLAB_DOMAIN}
kas:
name: kas.${GITLAB_DOMAIN}
ingress:
class: nginx
configureCertmanager: false
enabled: true
tls:
enabled: false
minio:
enabled: true
gitaly:
persistence:
enabled: true
size: 40Gi
psql:
enabled: false
redis:
password:
enabled: false
appConfig:
enableUsagePing: false
omniauth:
enabled: false
certmanager:
install: false
installCRDs: false
nginx-ingress:
enabled: false
prometheus:
install: false
gitlab-runner:
install: false
kas:
enabled: false
registry:
hpa:
enabled: false
upgradeCheck:
enabled: false
gitlab:
gitaly:
persistence:
enabled: true
size: 40Gi
webservice:
minReplicas: 1
maxReplicas: 1
ingress:
path: /
hpa:
minReplicas: 1
maxReplicas: 1
resources:
limits:
cpu: 1000m
memory: 4Gi
requests:
cpu: 250m
memory: 2Gi
sidekiq:
replicas:
default: 1
gitlab-shell:
minReplicas: 1
maxReplicas: 1
postgresql:
install: true
global:
postgresql:
auth:
# Adjust these credentials before deploying to production environments.
postgresPassword: changeme
username: gitlab
password: changeme
database: gitlabhq_production
primary:
persistence:
enabled: true
size: 40Gi
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 2Gi
redis:
install: true
master:
persistence:
enabled: true
size: 10Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
minio:
persistence:
size: 40Gi
shared-secrets:
self-signed-cert:
generate: true

View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "${SCRIPT_DIR}/setup.sh" "$@"

View File

@ -0,0 +1,224 @@
#!/usr/bin/env bash
# Offline installer for GitLab Helm chart tailored for a single-node deployment.
# This script is designed to be embedded in the GitLab offline package and invoked as
# bash install-gitlab.sh --version <VERSION> --domain <DOMAIN> [--namespace <NAMESPACE>]
# It renders a single-node friendly values file from the bundled template and installs
# the locally available GitLab Helm chart archive.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEFAULT_CONFIG_FILE="${SCRIPT_DIR}/gitlab-offline.env"
DEFAULT_TEMPLATE="${SCRIPT_DIR}/gitlab-values.single-node.yaml"
DEFAULT_CHART_DIR="${SCRIPT_DIR}/charts"
log() { printf '[\033[32mINFO\033[0m] %s\n' "$*"; }
warn() { printf '[\033[33mWARN\033[0m] %s\n' "$*"; }
err() { printf '[\033[31mERROR\033[0m] %s\n' "$*" >&2; exit 1; }
usage() {
cat <<'USAGE'
Usage: install-gitlab.sh --version <version> --domain <domain> [options]
Options:
--version, -v GitLab chart version (required)
--domain, -d External domain name for GitLab (required)
--namespace, -n Kubernetes namespace to deploy into (default: gitlab)
--config FILE Configuration file (default: ./gitlab-offline.env if present)
--values FILE Values template to render (default: ./gitlab-values.single-node.yaml)
--chart FILE Explicit GitLab chart archive (*.tgz). Overrides --charts-dir.
--charts-dir DIR Directory that contains the GitLab chart archive (default: ./charts)
--skip-image-load Skip loading container images from ./images
--help, -h Show this help and exit
Environment variables (overridable via --config or CLI):
GITLAB_VERSION Same as --version
GITLAB_DOMAIN Same as --domain
GITLAB_NAMESPACE Same as --namespace
GITLAB_CHART Same as --chart
GITLAB_CHARTS_DIR Same as --charts-dir
GITLAB_VALUES Same as --values
SKIP_IMAGE_LOAD Same as --skip-image-load
USAGE
}
load_config_file() {
local file="$1"
[[ -f "$file" ]] || return 0
# shellcheck disable=SC1090
source "$file"
}
parse_args() {
local args=("$@")
local config_file="$DEFAULT_CONFIG_FILE"
for ((i = 0; i < ${#args[@]}; i++)); do
if [[ "${args[i]}" == "--config" ]]; then
(( i + 1 < ${#args[@]} )) || err "--config requires a value"
config_file="${args[i+1]}"
break
fi
done
load_config_file "$config_file"
local i=0
while [[ $i -lt ${#args[@]} ]]; do
case "${args[i]}" in
--version|-v)
(( i + 1 < ${#args[@]} )) || err "--version requires a value"
GITLAB_VERSION="${args[i+1]}"
((i+=2))
;;
--domain|-d)
(( i + 1 < ${#args[@]} )) || err "--domain requires a value"
GITLAB_DOMAIN="${args[i+1]}"
((i+=2))
;;
--namespace|-n)
(( i + 1 < ${#args[@]} )) || err "--namespace requires a value"
GITLAB_NAMESPACE="${args[i+1]}"
((i+=2))
;;
--config)
((i+=2))
;;
--values)
(( i + 1 < ${#args[@]} )) || err "--values requires a value"
GITLAB_VALUES="${args[i+1]}"
((i+=2))
;;
--chart)
(( i + 1 < ${#args[@]} )) || err "--chart requires a value"
GITLAB_CHART="${args[i+1]}"
((i+=2))
;;
--charts-dir)
(( i + 1 < ${#args[@]} )) || err "--charts-dir requires a value"
GITLAB_CHARTS_DIR="${args[i+1]}"
((i+=2))
;;
--skip-image-load)
SKIP_IMAGE_LOAD="1"
((i+=1))
;;
--help|-h)
usage
exit 0
;;
*)
err "Unknown argument: ${args[i]}"
;;
esac
done
}
check_prerequisites() {
command -v helm >/dev/null 2>&1 || err "helm is required"
command -v kubectl >/dev/null 2>&1 || err "kubectl is required"
command -v envsubst >/dev/null 2>&1 || err "envsubst (gettext) is required"
}
pick_loader() {
if command -v docker >/dev/null 2>&1; then
if docker info >/dev/null 2>&1; then
echo "docker"
return
fi
fi
if command -v nerdctl >/dev/null 2>&1; then
if nerdctl info >/dev/null 2>&1; then
echo "nerdctl"
return
fi
fi
if command -v ctr >/dev/null 2>&1; then
echo "ctr"
return
fi
echo ""
}
load_offline_images() {
local images_dir="$SCRIPT_DIR/images"
[[ "${SKIP_IMAGE_LOAD:-0}" == "1" ]] && { warn "Skipping image import as requested"; return 0; }
[[ -d "$images_dir" ]] || { warn "No images directory found (expected ${images_dir})"; return 0; }
shopt -s nullglob
local archives=("${images_dir}"/*.tar "${images_dir}"/*.tar.gz)
shopt -u nullglob
[[ ${#archives[@]} -gt 0 ]] || { warn "No container image archives found in ${images_dir}"; return 0; }
local loader; loader="$(pick_loader)"
[[ -n "$loader" ]] || err "Unable to locate docker, nerdctl, or ctr for loading images"
log "Loading offline container images using ${loader}"
for archive in "${archives[@]}"; do
case "$loader" in
docker) docker load -i "$archive" ;;
nerdctl) nerdctl load -i "$archive" ;;
ctr) ctr -n k8s.io images import "$archive" ;;
esac
done
}
render_values_file() {
local template="$1" output="$2"
[[ -f "$template" ]] || err "Values template not found: $template"
export GITLAB_DOMAIN GITLAB_NAMESPACE
envsubst '${GITLAB_DOMAIN}${GITLAB_NAMESPACE}' < "$template" > "$output"
}
select_chart() {
if [[ -n "${GITLAB_CHART:-}" ]]; then
[[ -f "$GITLAB_CHART" ]] || err "Specified chart not found: $GITLAB_CHART"
echo "$GITLAB_CHART"
return
fi
local charts_dir="${GITLAB_CHARTS_DIR:-$DEFAULT_CHART_DIR}"
local chart_archive="${charts_dir}/gitlab-${GITLAB_VERSION}.tgz"
[[ -f "$chart_archive" ]] || err "GitLab chart archive not found: $chart_archive"
echo "$chart_archive"
}
ensure_namespace() {
if ! kubectl get namespace "$GITLAB_NAMESPACE" >/dev/null 2>&1; then
log "Creating namespace ${GITLAB_NAMESPACE}"
kubectl create namespace "$GITLAB_NAMESPACE"
fi
}
main() {
parse_args "$@"
check_prerequisites
[[ -n "${GITLAB_VERSION:-}" ]] || err "GitLab chart version is required (--version)"
[[ -n "${GITLAB_DOMAIN:-}" ]] || err "Domain is required (--domain)"
GITLAB_NAMESPACE="${GITLAB_NAMESPACE:-gitlab}"
local values_template="${GITLAB_VALUES:-$DEFAULT_TEMPLATE}"
local tmp_values
tmp_values="$(mktemp)"
trap 'rm -f "$tmp_values"' EXIT
render_values_file "$values_template" "$tmp_values"
load_offline_images
ensure_namespace
local chart_archive
chart_archive="$(select_chart)"
log "Installing GitLab ${GITLAB_VERSION} into namespace ${GITLAB_NAMESPACE}"
helm upgrade --install gitlab "$chart_archive" \
--namespace "$GITLAB_NAMESPACE" \
--create-namespace \
--values "$tmp_values" \
--timeout 15m \
--wait \
--debug
log "GitLab installation triggered successfully"
}
main "$@"

View File

@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Build the GitLab offline package archive.
# Usage: GITLAB_VERSION=<version> scripts/create-gitlab-offline-package.sh
# Environment variables:
# GITLAB_VERSION (required) GitLab Helm chart version, e.g. 7.8.0
# ARCH Target architecture suffix for the archive name (default: amd64)
# INCLUDE_IMAGES If set to 1, pull GitLab images and bundle them into the package (requires docker)
# WORKDIR Working directory name (default: gitlab-offline-package)
set -euo pipefail
log() { printf '[\033[32mINFO\033[0m] %s\n' "$*"; }
warn() { printf '[\033[33mWARN\033[0m] %s\n' "$*"; }
err() { printf '[\033[31mERROR\033[0m] %s\n' "$*" >&2; exit 1; }
command -v helm >/dev/null 2>&1 || err "helm is required to build the offline package"
GITLAB_VERSION="${GITLAB_VERSION:-}"
[[ -n "$GITLAB_VERSION" ]] || err "GITLAB_VERSION is required"
ARCH="${ARCH:-amd64}"
WORKDIR="${WORKDIR:-gitlab-offline-package}"
INCLUDE_IMAGES="${INCLUDE_IMAGES:-0}"
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
FILES_DIR="${ROOT_DIR}/playbooks/roles/charts/gitlab/files"
VALUES_TEMPLATE="${FILES_DIR}/gitlab-values.single-node.yaml"
INSTALLER_SCRIPT="${FILES_DIR}/setup.sh"
WRAPPER_SCRIPT="${FILES_DIR}/install-gitlab.sh"
ENV_EXAMPLE="${FILES_DIR}/gitlab-offline.env.example"
[[ -f "$VALUES_TEMPLATE" ]] || err "Values template missing: $VALUES_TEMPLATE"
[[ -f "$INSTALLER_SCRIPT" ]] || err "Installer script missing: $INSTALLER_SCRIPT"
[[ -f "$WRAPPER_SCRIPT" ]] || err "Wrapper script missing: $WRAPPER_SCRIPT"
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR/charts"
log "Pulling GitLab Helm chart ${GITLAB_VERSION}"
helm repo add gitlab https://charts.gitlab.io/ >/dev/null
helm repo update >/dev/null
helm pull gitlab/gitlab --version "$GITLAB_VERSION" --destination "$WORKDIR/charts"
log "Copying installer assets"
cp "$INSTALLER_SCRIPT" "$WORKDIR/setup.sh"
cp "$WRAPPER_SCRIPT" "$WORKDIR/install-gitlab.sh"
cp "$FILES_DIR/gitlab-values.single-node.yaml" "$WORKDIR/gitlab-values.single-node.yaml"
cp "$ENV_EXAMPLE" "$WORKDIR/gitlab-offline.env.example"
chmod +x "$WORKDIR/setup.sh" "$WORKDIR/install-gitlab.sh"
cat > "$WORKDIR/README.md" <<'DOC'
# GitLab Offline Package
## Usage
```
tar -xvpf offline-package-gitlab-<arch>.tar.gz
cd gitlab-offline-package/
cp gitlab-offline.env.example gitlab-offline.env
# Adjust gitlab-offline.env then run:
bash install-gitlab.sh --version <VERSION> --domain <DOMAIN> [--namespace <NAMESPACE>]
```
If container images are bundled, they can be imported automatically. Set
`SKIP_IMAGE_LOAD=1` in `gitlab-offline.env` to skip loading.
DOC
bundle_images() {
local chart_archive="$WORKDIR/charts/gitlab-${GITLAB_VERSION}.tgz"
local tmp_values tmp_manifest
tmp_values="$(mktemp)"
tmp_manifest="$(mktemp)"
trap 'rm -f "$tmp_values" "$tmp_manifest"' RETURN
export GITLAB_DOMAIN="gitlab.example.com" GITLAB_NAMESPACE="gitlab"
envsubst '${GITLAB_DOMAIN}${GITLAB_NAMESPACE}' < "$VALUES_TEMPLATE" > "$tmp_values"
helm template gitlab "$chart_archive" -f "$tmp_values" > "$tmp_manifest"
mapfile -t images < <(awk '/image:/{print $2}' "$tmp_manifest" | sed 's/"//g' | sort -u)
if [[ ${#images[@]} -eq 0 ]]; then
warn "No images detected; skipping image bundle"
return
fi
command -v docker >/dev/null 2>&1 || err "docker is required to bundle images"
mkdir -p "$WORKDIR/images"
for image in "${images[@]}"; do
log "Pulling $image"
docker pull --platform "linux/${ARCH}" "$image"
done
local image_tar="$WORKDIR/images/gitlab-images-${ARCH}.tar"
log "Saving images to ${image_tar}"
docker save -o "$image_tar" "${images[@]}"
printf '%s\n' "${images[@]}" > "$WORKDIR/images/images.txt"
}
if [[ "$INCLUDE_IMAGES" == "1" ]]; then
log "Bundling container images"
bundle_images
else
warn "Images are not included. Set INCLUDE_IMAGES=1 to bundle them (requires docker)."
fi
tar_name="offline-package-gitlab-${ARCH}.tar.gz"
rm -f "$tar_name"
log "Creating archive ${tar_name}"
tar -czpf "$tar_name" "$WORKDIR"
log "Done"