Intensiv DevOps-uddannelse · Lektion

Skabelondannelse af konfigurationer med envsubst og heredocs

Generér runtime-konfiguration fra miljøvariabler ved hjælp af envsubst og citerede heredocs.

Lektion 2 af 413 trin

Skabelondannelse af konfigurationer med envsubst og heredocs er en gratis Intensiv DevOps-uddannelse-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Intensiv DevOps-uddannelse, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.

Hvorfor konfigurationsskabeloner ved kørsel er vigtige

I DevOps- og containerarbejdsforløb skal konfigurationsfiler som nginx.conf, prometheus.yml og docker-compose.yml ofte ændres mellem miljøer — test, produktion og katastrofeberedskab. Hårdkodede værdier skaber afvigelser og kan afsløre hemmeligheder.

Løsningen er konfigurationsskabeloner ved kørsel: medtag en skabelon med pladsholdere, og indsæt derefter de faktiske værdier ved opstart fra miljøvariabler. Det holder dit image uforanderligt og din konfiguration kontrollerbar.

  • Ingen hemmeligheder indbygget i images
  • Det samme artefakt fremmes på tværs af miljøer
  • Konfigurationen genereres lige før processen starter

To komplementære værktøjer gør dette enkelt i Bash: envsubst og citerede heredocs.

envsubst: Konfigurationsgeneratoren på én linje

envsubst er et lille GNU-værktøj, der læser standardinput, erstatter pladsholdere som $VARIABLE og ${VARIABLE} med deres værdier fra det aktuelle miljø og skriver til standardoutput.

Det leveres med pakken gettext og findes i stort set alle Linux-distributioner og Docker-grundimages.

  • Fungerer med alle tekstformater: NGINX, YAML, TOML, JSON og INI
  • Evaluerer ikke shell-syntaks — erstatter kun variabelreferencer
  • Sikkert: det udfører ikke kommandoer inde i skabelonen
#!/usr/bin/env bash
# Install check (usually already present)
which envsubst || apt-get install -y gettext-base

# Minimal demo
export APP_PORT=8080
export APP_HOST=api.example.com

echo 'server { listen ${APP_PORT}; server_name ${APP_HOST}; }' | envsubst
# Output: server { listen 8080; server_name api.example.com; }

Udvælgelse af variabler til erstatning

Som standard erstatter envsubst alle forekomster af $VAR. Det kan ødelægge NGINX-variabler som $uri eller $host — de er rigtige NGINX-direktiver, ikke dine miljøvariabler.

Angiv en eksplicit liste over variabler som det første argument for at begrænse erstatningen til netop disse navne:

envsubst '$VAR1 $VAR2'

Argumentet er en streng i enkelt citationstegn (så shellen ikke udvider den), der indeholder de variabelnavne, du vil erstatte, adskilt af mellemrum eller linjeskift.

#!/usr/bin/env bash
export APP_PORT=8080
export APP_HOST=api.example.com

# NGINX template contains both our vars AND nginx vars ($uri, $host)
TEMPLATE='server {
  listen ${APP_PORT};
  server_name ${APP_HOST};
  location / {
    proxy_set_header Host $host;
    proxy_pass http://backend$uri;
  }
}'

# Only substitute APP_PORT and APP_HOST — leave $host and $uri untouched
echo "$TEMPLATE" | envsubst '${APP_PORT} ${APP_HOST}'

Skabelonfiler på disken

For rigtige konfigurationer skal du gemme skabelonen som en fil (f.eks. nginx.conf.template) sammen med din Dockerfil. Ved containerens opstart skal du køre envsubst for at oprette den endelige konfigurationsfil, før dæmonen startes.

Dette er det kanoniske mønster, der bruges af det officielle NGINX-Docker-image.

#!/usr/bin/env bash
# File: nginx.conf.template
# (In practice this lives on disk; we write it here for demo purposes)
cat > /tmp/nginx.conf.template << 'TMPL'
server {
    listen ${NGINX_PORT};
    server_name ${SERVER_NAME};
    root /var/www/${APP_ENV};

    location / {
        proxy_pass http://app:${APP_PORT};
    }
}
TMPL

export NGINX_PORT=80
export SERVER_NAME=myapp.example.com
export APP_ENV=production
export APP_PORT=3000

# Generate final config
envsubst '${NGINX_PORT} ${SERVER_NAME} ${APP_ENV} ${APP_PORT}' \
  < /tmp/nginx.conf.template \
  > /tmp/nginx.conf

cat /tmp/nginx.conf

Citerede heredocs: Inline-skabeloner uden en midlertidig fil

En citeret heredoc (med << 'EOF', hvor afgrænseren står i enkeltcitationstegn) forhindrer skallen i at udvide variabler eller udføre kommandosubstitutioner i blokken. Indholdet behandles som en bogstavelig streng.

Det gør heredocs til den perfekte måde at skrive en skabelon inline på og sende den direkte gennem envsubst — uden behov for en mellemfil.

  • << EOF (uden citationstegn) — skallen udvider $VAR med det samme
  • << 'EOF' (med citationstegn) — indholdet er bogstaveligt; udvidelsen udskydes til envsubst
#!/usr/bin/env bash
export DB_HOST=postgres.internal
export DB_PORT=5432
export DB_NAME=myapp_prod

# Quoted heredoc: shell does NOT expand $DB_HOST etc. yet
envsubst << 'EOF'
[database]
host     = ${DB_HOST}
port     = ${DB_PORT}
dbname   = ${DB_NAME}
EOF
# Output uses actual env var values — expansion done by envsubst, not the shell

Kombination af heredocs og outputomdirigering

Send en citeret heredoc gennem envsubst, og omdiriger resultatet til en fil i ét udtryk. Det er den reneste idiomatiske løsning til at generere konfigurationsfiler i et entrypoint-script.

Brug selektiv substitution ('${VAR1} ${VAR2}'), når målformatet (Prometheus, NGINX osv.) har sin egen $variable-syntaks, som skal beskyttes.

#!/usr/bin/env bash
# entrypoint.sh — Docker container entrypoint
set -euo pipefail

export PROM_PORT=${PROM_PORT:-9090}
export SCRAPE_INTERVAL=${SCRAPE_INTERVAL:-15s}
export TARGET_HOST=${TARGET_HOST:-localhost:8080}

envsubst '${PROM_PORT} ${SCRAPE_INTERVAL} ${TARGET_HOST}' << 'EOF' > /etc/prometheus/prometheus.yml
global:
  scrape_interval: ${SCRAPE_INTERVAL}
  evaluation_interval: ${SCRAPE_INTERVAL}

scrape_configs:
  - job_name: 'app'
    static_configs:
      - targets: ['${TARGET_HOST}']

EOF

echo "[entrypoint] Prometheus config written on port ${PROM_PORT}"
exec prometheus --config.file=/etc/prometheus/prometheus.yml --web.listen-address=":${PROM_PORT}"

Standardværdier og validering før substitution

Gå aldrig ud fra, at alle påkrævede variabler er sat. Brug Bash-parameterudvidelse til at angive standardværdier eller få scriptet til at stoppe med en tydelig fejl:

  • ${VAR:-default} — brug default, hvis VAR ikke er sat eller er tom
  • ${VAR:?error message} — afbryd med en fejl, hvis VAR ikke er sat eller er tom

Sæt disse værdier, før du kalder envsubst, så skabelonen altid modtager en konkret værdi, eller scriptet stopper tidligt med en hjælpsom meddelelse.

#!/usr/bin/env bash
set -euo pipefail

# Required — abort if missing
: "${DATABASE_URL:?DATABASE_URL must be set}"
: "${SECRET_KEY:?SECRET_KEY must be set}"

# Optional with defaults
export APP_PORT=${APP_PORT:-8000}
export LOG_LEVEL=${LOG_LEVEL:-info}
export WORKERS=${WORKERS:-4}

envsubst '${DATABASE_URL} ${SECRET_KEY} ${APP_PORT} ${LOG_LEVEL} ${WORKERS}' \
  < /app/config/app.conf.template \
  > /app/config/app.conf

echo "[init] Config generated — port=${APP_PORT} workers=${WORKERS} log=${LOG_LEVEL}"

Generering af konfigurationer med flere sektioner ved hjælp af flere heredocs

For komplekse konfigurationer, der består af logiske sektioner, kan du generere hver sektion separat og sammenkæde dem eller bruge én heredoc, der dækker hele filen. Begge metoder fungerer — vælg ud fra læsbarheden.

Når sektioner inkluderes betinget (f.eks. kun en TLS-blok, hvis der er angivet en certifikatsti), er metoden med flere heredocs og if-blokke mere overskuelig.

#!/usr/bin/env bash
set -euo pipefail

export APP_HOST=${APP_HOST:-localhost}
export APP_PORT=${APP_PORT:-8080}
export TLS_CERT=${TLS_CERT:-}
export TLS_KEY=${TLS_KEY:-}

CONFIG_FILE=/tmp/app.conf

# Base section
envsubst '${APP_HOST} ${APP_PORT}' << 'BASE' > "$CONFIG_FILE"
[server]
host = ${APP_HOST}
port = ${APP_PORT}
BASE

# Conditional TLS section — only appended when cert is provided
if [[ -n "$TLS_CERT" && -n "$TLS_KEY" ]]; then
  envsubst '${TLS_CERT} ${TLS_KEY}' << 'TLS' >> "$CONFIG_FILE"

[tls]
cert_file = ${TLS_CERT}
key_file  = ${TLS_KEY}
TLS
  echo "[init] TLS enabled"
else
  echo "[init] TLS disabled (no cert/key provided)"
fi

cat "$CONFIG_FILE"

Docker-entrypoint-mønster

Det anbefalede Docker-entrypoint-mønster bruger et shell-script (docker-entrypoint.sh) til at generere konfigurationer ved opstart og derefter overlade styringen til hovedprocessen med exec. Når du bruger exec, erstattes shell-processen af dæmonen, så signaler (SIGTERM, SIGINT) når direkte frem til dæmonen — det er afgørende for en kontrolleret nedlukning.

Skabelonfiler føjes til imaget ved byggetidspunktet; værdier indsættes ved kørsel fra docker run -e eller Kubernetes env: / envFrom:.

#!/usr/bin/env bash
# docker-entrypoint.sh
set -euo pipefail

# Validate required env vars
for var in DATABASE_URL REDIS_URL SECRET_KEY; do
  : "${!var:?$var is required}"
done

export APP_PORT=${APP_PORT:-8000}
export WORKERS=${WORKERS:-$(nproc)}

echo "[entrypoint] Generating configuration..."
envsubst '${DATABASE_URL} ${REDIS_URL} ${SECRET_KEY} ${APP_PORT} ${WORKERS}' \
  < /app/config/settings.toml.template \
  > /app/config/settings.toml

echo "[entrypoint] Starting server on port ${APP_PORT} with ${WORKERS} workers"
exec gunicorn app:application \
  --bind "0.0.0.0:${APP_PORT}" \
  --workers "${WORKERS}"

Mønster for Kubernetes ConfigMap og envsubst

I Kubernetes indsættes miljøvariabler via env: eller envFrom: i Pod-specifikationen. Dit container-entrypoint kalder envsubst for at materialisere konfigurationer, før processen starter — der er ikke behov for en ConfigMap pr. miljø.

Det holder miljøspecifikke værdier i Kubernetes Secrets og ConfigMaps (til ikke-følsomme data), mens konfigurationsskabelonen ligger i imaget. Ét image, mange miljøer.

  • Bygning: COPY nginx.conf.template /etc/nginx/templates/
  • Kørsel: entrypointet kører envsubst og skriver /etc/nginx/nginx.conf
  • K8s indsætter: APP_PORT, BACKEND_HOST fra en Secret/ConfigMap

Fejlfinding af envsubst: Find manglende eller uløste variabler

Når en genereret konfiguration indeholder den bogstavelige tekst ${VAR} i stedet for en værdi, er variablen enten ikke eksporteret eller ikke medtaget på substitutionslisten. Brug disse teknikker til fejlfinding:

  • printenv | sort — vis alle eksporterede variabler
  • Sammenlign pladsholdere i skabelonen med eksporterede variabler ved hjælp af grep
  • Kør envsubst, og brug grep på outputtet for at finde resterende mønstre med ${
  • Brug set -u i det kaldende script, så referencer til ikke-satte variabler i Bash-kode straks afbryder scriptet
#!/usr/bin/env bash
set -euo pipefail

TEMPLATE=/tmp/app.conf.template
OUTPUT=/tmp/app.conf

# Write a demo template
cat > "$TEMPLATE" << 'EOF'
host=${DB_HOST}
port=${DB_PORT}
name=${DB_NAME}
EOF

export DB_HOST=db.internal
export DB_PORT=5432
# DB_NAME intentionally left unset

envsubst < "$TEMPLATE" > "$OUTPUT"

# Detect unresolved placeholders
if grep -qE '\$\{[A-Z_]+\}' "$OUTPUT"; then
  echo "ERROR: unresolved placeholders found:"
  grep -oE '\$\{[A-Z_]+\}' "$OUTPUT" | sort -u
  exit 1
fi

echo "Config OK:"
cat "$OUTPUT"

Videnscheck: Selektiv substitution med envsubst

Forestil dig en NGINX-konfigurationsskabelon, der både indeholder din applikationsvariabel ${APP_PORT} og den indbyggede NGINX-variabel $uri. Du kører følgende kommando:

envsubst < nginx.conf.template > nginx.conf

Hvad bliver resultatet?

Opsummering af lektionen: Skabelonbaserede konfigurationer med envsubst og heredocs

Du har nu et værktøjssæt på produktionsniveau til generering af konfigurationer ved kørsel i Bash:

  • envsubst erstatter pladsholdere som ${VAR} i enhver tekstfil ved hjælp af det aktuelle miljø — uden scripting og uden særlig escaping
  • Selektiv substitution (envsubst '${VAR1} ${VAR2}') beskytter indbyggede variabler i NGINX, Prometheus og lignende værktøjer mod utilsigtet erstatning
  • Citerede heredocs (<< 'EOF') udskyder shell-udvidelsen, så skabelonindholdet når frem til envsubst uændret — uden behov for midlertidige filer
  • Valider før substitution: Brug ${VAR:?message} til at afbryde ved manglende påkrævede variabler og ${VAR:-default} til valgfrie variabler
  • Docker-entrypoint-mønster: Generér konfigurationer ved containerens opstart, og brug derefter exec til dæmonen, så signaler håndteres korrekt
  • Fejlfind uløste pladsholdere ved at bruge grep på outputtet for resterende mønstre med ${, før processen starter

Disse mønstre holder dine containerimages uforanderlige, dine hemmeligheder ude af kildekodehåndteringen og dine konfigurationer ensartede på tværs af alle miljøer.

Gratis at komme i gang

Lær Intensiv DevOps-uddannelse med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
142
Lektioner
568

Ofte stillede spørgsmål

Er lektionen “Skabelondannelse af konfigurationer med envsubst og heredocs” gratis?

Ja — alle 3 lektioner i læringssporet Intensiv DevOps-uddannelse, inklusive “Skabelondannelse af konfigurationer med envsubst og heredocs”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Skabelondannelse af konfigurationer med envsubst og heredocs”?

Generér runtime-konfiguration fra miljøvariabler ved hjælp af envsubst og citerede heredocs. Du øver dig i Intensiv DevOps-uddannelse med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Intensiv DevOps-uddannelse?

Der kræves ingen tidligere erfaring. Intensiv DevOps-uddannelse på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Skabelondannelse af konfigurationer med envsubst og heredocs”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Intensiv DevOps-uddannelse-lektion?

Ja. Alle Intensiv DevOps-uddannelse-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Skriv slanke Dockerfiles og shell-entrypoints
  2. Skabelondannelse af konfigurationer med envsubst og heredocs
  3. Scripting af cloudressourcer via CLI og jq
  4. Health probes, readiness gates og venteløkker
← Tilbage til Intensiv DevOps-uddannelse