Was ist CI/CD und warum brauchen Sie es?
CI (Continuous Integration) — jede Änderung wird automatisch gesammelt und getestet.
CD (Continuous Delivery/Deployment) - Nach dem Erfolg rollen wir ohne Hände auf den Stand / Prod.
Vorteile: weniger manuelle Fehler, schnelle Releases, Transparenz für das Team und vorhersehbare Rollouts. 🧘♂️
Server: Grundausbildung (VPS)
# 1) Docker und Compose-Plugin (Ubuntu)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 2) Katalog der Anlage
sudo mkdir -p /opt/myapp && sudo chown -R $USER:$USER /opt/myapp
cd /opt/myapp
# 3) .env (auf dem Server, nicht committen)
cat > .env << 'ENV'
PORT=3000
ENV
# 4) docker-compose.yml
cat > docker-compose.yml << 'YAML'
services:
web:
image: registry.example.com/myapp:latest
env_file: .env
ports:
- "80:3000"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 3s
retries: 5
YAML
# 5) Erster Start (bisher ohne CI)
docker compose pull && docker compose up -d
Dockerfile (Mindestbeispiel für Node/PNPM)
# Dockerfile
FROM node:20-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm i --frozen-lockfile
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/dist dist
COPY package.json ./
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm i --prod --frozen-lockfile
EXPOSE 3000
CMD ["node", "dist/server.js"]
GitHub-Aktionen: Image-Build und SSH-Bereitstellung
Fügen Sie .github/workflows/deploy.yml zum Repository hinzu:
name: CI/CD Deploy
on:
push:
branches: [ "main" ]
permissions:
contents: read
packages: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build & push image
uses: docker/build-push-action@v6
with:
push: true
context: .
tags: ${{ secrets.REGISTRY_URL }}/${{ secrets.IMAGE_NAME }}:latest
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
set -e
cd /opt/myapp
docker compose pull
docker compose up -d
docker image prune -f
Geheimnisse: REGISTRY_URL, REGISTRY_USER, REGISTRY_PASSWORD, IMAGE_NAME, SERVER_HOST, SERVER_USER, SERVER_SSH_KEY.
GitLab CI: Alternative
# .gitlab-ci.yml
stages: [build, deploy]
variables:
IMAGE: $CI_REGISTRY_IMAGE:latest
build:
stage: build
image: docker:27.0
services: [ "docker:27.0-dind" ]
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $IMAGE .
- docker push $IMAGE
artifacts:
expire_in: 1 week
when: on_success
paths: []
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$DEPLOY_HOST "
set -e
cd /opt/myapp &&
docker compose pull &&
docker compose up -d &&
docker image prune -f
"
only:
- main
Keine Ausfallzeiten und Rückgänge
Zero‑downtime:
restart: unless-stopped+healthcheck+up -d- der neue Container startet, der alte wird gelöscht.Release-Tags: zusätzlich zu
:latestpush:v1.4.2— einfacheres Rollback.Rollback: Schalten Sie das Tag in
docker-compose.ymlund wiederholen Siepull/up -d.Blaues/grünes Schema (vereinfacht): halten Sie zwei Dienste
web_blueundweb_green, balancieren Sie den Datenverkehr über Nginx.
Minimale Pipeline-Überprüfungen
# Beispiel für einen Schritt mit Tests in GitHub Actions
- name: Install deps & test
run: |
npm ci
npm run test -- --ci
Schwelle für das „Abend“-CI: Unit-Tests ausführen, Build kompilieren, Linter (ESLint/flake8) durchlaufen, Typen (tsc/mypy) überprüfen.
Überwachung und Protokollierung
Fügen Sie
/healthund/metrics(Prometheus-Format) zum Dienst hinzu.Protokollsammlung:
docker logs→ Loki/ELK; Statuscode-/Latenzalarm.In CI - Artefakte mit Test- und Linter-Berichten, um Regressionen zu sehen.
Geheimnisse und Sicherheit
Geheimnisse nur in Secrets/Variables CI;
.env— auf dem Server.Deaktivieren Sie die SSH-Anmeldung mit Passwort, lassen Sie die Anmeldung mit Schlüssel aktiv, beschränken Sie die Ports.
Speichern Sie private Schlüssel nicht im Repository, auch nicht in verschlüsselter Form.
🧯 Typische Probleme und schnelle Lösungen
Symptom | Ursache | Fix |
|---|---|---|
CI fällt bei der Montage | Wenig RAM/Timeout | Cache-Layer, leichtere Image-Basis, Timeout erhöhen |
Die Anwendung wird nicht gestartet | Port/ENV/Migration | Überprüfen Sie |
Langsames Deploy | Schweres Bild | Mehrstufig, alpine, .dockerignore, Caching von Abhängigkeiten |
„Es funktioniert bei mir“ | Verschiedene Versionen von Node/Python | Versionen in Docker fixieren, Lock-Dateien verwenden |
Festplatte voll | Alte Bilder/Container |
|
Im Anhang „Kodik – Programmieren lernen“ – kurze Lektionen und Miniprojekte. Bei uns ist es interessant!
Und wir haben auch einen aktiven Telegram-Kanal, wo wir coole Ideen diskutieren, Erfahrungen teilen und Aufgaben gemeinsam analysieren — Lernen wird nicht nur nützlich, sondern auch unterhaltsam.
Ergebnis
CI/CD ist keine „große Magie“, sondern eine Reihe einfacher Schritte. Image-Build, Push in die Registry, Neustart des Dienstes auf VPS — und Sie haben schnelle, wiederholbare Releases ohne manuelle Routine. Fangen Sie noch heute an, und morgen wird das Team vergessen, wie ein „manueller Deploy“ aussah.
Worauf werden Sie den Autodeploy drehen - GitHub Actions oder GitLab CI?
