Solidna pipeline CI/CD z GitHub Actions i Dockerem automatyzuje cały cykl wdrożeń. Ten przewodnik buduje pipeline poziomu produkcyjnego z budowami wielu platform i wdrożeniami bez przestojów.
Przegląd pipeline
Pipeline ma cztery etapy: CI (test), Build (obraz Docker), Push (do rejestru) i Deploy (na serwer).
Pipeline Flow:
git push → GitHub Actions triggered
│
├─ [Job 1] CI: npm test, lint, type-check
│ (runs on all PRs and main pushes)
│
├─ [Job 2] Build: docker build --platform linux/amd64,linux/arm64
│ (runs only on main branch push)
│ Push to ghcr.io/org/app:sha-abc1234
│
└─ [Job 3] Deploy: SSH to server, docker pull + restart
(runs after successful build)
Zero-downtime with blue-green or rolling restartNajlepsze praktyki Dockerfile
Dobrze ustrukturyzowany Dockerfile jest fundamentem. Wieloetapowe buildy znacznie zmniejszają rozmiar końcowego obrazu.
# Multi-stage Dockerfile (Node.js example)
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && cp -R node_modules /prod_node_modules
RUN npm ci # install ALL deps for building
# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Stage 3: Production runner (minimal image)
FROM node:20-alpine AS runner
WORKDIR /app
# Security: run as non-root user
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 appuser
# Copy only what's needed for runtime
COPY --from=deps /prod_node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
# Health check for orchestrators
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
# Result: ~150MB instead of ~800MB for the same appPodstawowa pipeline CI/CD
Ten workflow buduje i wypycha do GitHub Container Registry przy każdym push na main.
# .github/workflows/deploy.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# Stage 1: Test
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run lint
run: npm run lint
# Stage 2: Build and Push Docker image
build-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
outputs:
image-digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # Auto-provided by GitHub
- name: Extract Docker metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Stage 3: Deploy to server
deploy:
needs: build-push
runs-on: ubuntu-latest
environment: production # GitHub Environment with protection rules
steps:
- name: Deploy to production server via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
# Pull latest image
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker pull ghcr.io/${{ github.repository }}:latest
# Zero-downtime restart
docker compose -f /app/docker-compose.prod.yml up -d --no-deps app
# Verify deployment
sleep 10
docker compose -f /app/docker-compose.prod.yml psZaawansowane funkcje pipeline
Produkcyjne pipeline potrzebują dodatkowych funkcji: buildy wielu platform, skanowanie bezpieczeństwa i rollback.
# Advanced Pipeline Features
# 1. Multi-platform builds (AMD64 + ARM64)
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build multi-platform image
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
# 2. Security scanning with Trivy
- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail pipeline on critical vulnerabilities
# 3. Semantic versioning with tags
on:
push:
tags:
- 'v*.*.*'
jobs:
release:
steps:
- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- uses: docker/build-push-action@v5
with:
tags: |
ghcr.io/myorg/myapp:${{ steps.version.outputs.VERSION }}
ghcr.io/myorg/myapp:latest
# 4. Rollback on failure
- name: Deploy with rollback
run: |
PREVIOUS_IMAGE=$(docker inspect --format='{{.Config.Image}}' app-container)
docker pull $NEW_IMAGE
if docker run --rm $NEW_IMAGE node -e "require('./dist/index.js')"; then
docker stop app-container
docker run -d --name app-container $NEW_IMAGE
else
echo "New image failed healthcheck, keeping previous"
exit 1
fiCzęsto zadawane pytania
Jak przechowywać sekrety w GitHub Actions?
Przejdź do ustawień repozytorium → Secrets and variables → Actions → New repository secret.
Różnica między ghcr.io a Docker Hub?
GitHub Container Registry jest darmowy dla publicznych repozytoriów i zintegrowany z uprawnieniami GitHub.
Jak budować dla AMD64 i ARM64?
Użyj docker/setup-qemu-action i docker/setup-buildx-action.
Jak implementować wdrożenia specyficzne dla środowiska?
Użyj GitHub Environments dla staging i produkcji.