Wprowadzenie
Ręczne wdrożenia to prosta droga do błędów i przestojów. Dobrze skonfigurowany pipeline CI/CD sprawia, że każda zmiana w kodzie automatycznie przechodzi przez testy, budowanie obrazu i deployment — bez udziału człowieka. W tym artykule zbudujemy kompletny pipeline GitLab CI/CD dla aplikacji Dockerowej, od zera do automatycznego wdrożenia na serwer produkcyjny.
Architektura pipeline’u
git push → GitLab CI → test → build → push do registry → deploy na serwer
Etapy (stages):
- test — uruchomienie testów jednostkowych
- build — zbudowanie obrazu Docker
- push — wysłanie obrazu do GitLab Container Registry
- deploy:staging — wdrożenie na środowisko staging
- deploy:production — wdrożenie na produkcję (ręczne zatwierdzenie)
Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ .
EXPOSE 8000
RUN useradd -m -u 1001 appuser && chown -R appuser:appuser /app
USER appuser
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Plik .gitlab-ci.yml — kompletna konfiguracja
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
DOCKER_DRIVER: overlay2
stages:
- test
- build
- push
- deploy
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .pip-cache/
unit-tests:
stage: test
image: python:3.12-slim
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- pytest tests/ -v --cov=src --cov-report=xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
build-image:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build --tag $IMAGE_NAME:$IMAGE_TAG --tag $IMAGE_NAME:latest .
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
push-image:
stage: push
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker push $IMAGE_NAME:$IMAGE_TAG
- docker push $IMAGE_NAME:latest
deploy-staging:
stage: deploy
environment:
name: staging
url: https://staging.example.com
script:
- |
ssh $STAGING_USER@$STAGING_HOST << EOF
docker pull $IMAGE_NAME:$IMAGE_TAG
docker stop myapp-staging || true
docker run -d --name myapp-staging --restart unless-stopped \
-p 8001:8000 $IMAGE_NAME:$IMAGE_TAG
EOF
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
deploy-production:
stage: deploy
environment:
name: production
when: manual
script:
- |
ssh $PROD_USER@$PROD_HOST << EOF
docker pull $IMAGE_NAME:$IMAGE_TAG
docker stop myapp-prod || true
docker run -d --name myapp-prod --restart unless-stopped \
-p 8000:8000 $IMAGE_NAME:$IMAGE_TAG
EOF
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Konfiguracja zmiennych w GitLab
Przejdź do Settings → CI/CD → Variables i dodaj:
| Zmienna | Typ | Opis |
|---|---|---|
SSH_PRIVATE_KEY_STAGING | File | Klucz SSH do serwera staging |
SSH_PRIVATE_KEY_PROD | File | Klucz SSH do serwera produkcyjnego |
STAGING_HOST | Variable | IP/hostname serwera staging |
PROD_HOST | Variable | IP/hostname serwera produkcyjnego |
Przygotowanie serwera docelowego
curl -fsSL https://get.docker.com | sh
usermod -aG docker deploy
useradd -m -s /bin/bash deploy
mkdir -p /home/deploy/.ssh
echo "$(cat id_rsa_deploy.pub)" >> /home/deploy/.ssh/authorized_keys
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
Podsumowanie
Masz teraz kompletny pipeline CI/CD, który:
- Automatycznie testuje kod przy każdym pushu
- Buduje i publikuje obraz Docker do rejestru
- Wdraża na staging automatycznie po pushu do
develop - Wdraża na produkcję po ręcznym zatwierdzeniu z brancha
main
W kolejnym artykule rozbudujemy pipeline o skanowanie bezpieczeństwa obrazu z Trivy i analizę jakości kodu z SonarQube.