Witajcie, deweloperzy! W tym artykule opowiem o CI/CD – koncepcji.
- Czym jest potok CI/CD w kontekście programowania?
- 🔄 Z czego zazwyczaj składa się potok CI/CD?
- 🛠 Popularne narzędzia CI/CD:
- 🧠 Po co w ogóle potrzebne jest CI/CD?
- 📦 Proste przykłady CI/CD z GitHub Actions
- 🚚 Wdrażanie na popularne platformy
- 🟨 Wdrażanie na AWS (np. pliki statyczne do S3)
- 🔵 Wdrażanie na Google Cloud Platform (GCP App Engine)
- 🟪 Wdrażanie na Render.com
- 🌟 Zaawansowane CI/CD: Budowanie Docker → Wypychanie do GHCR → Staging/Production na GCP Cloud Run
- 1. Budowanie i publikowanie obrazu Docker w GHCR
- 2. Automatyczne wdrożenie do Staging (GCP Cloud Run)
- 3. Wdrożenie do Production z ręcznym potwierdzeniem (GCP Cloud Run)
- 🔐 Bezpieczeństwo – to ważne!
Czym jest potok CI/CD w kontekście programowania?
Potok CI/CD (Continuous Integration / Continuous Delivery lub Continuous Deployment) to zautomatyzowany proces, który pozwala deweloperom szybko i niezawodnie dostarczać zmiany w kodzie do środowiska produkcyjnego.
Rozłóżmy kluczowe pojęcia:
🔧 CI — Continuous Integration (Ciągła Integracja)
Jest to praktyka, w której deweloperzy często wprowadzają zmiany do wspólnej bazy kodu. Każda taka zmiana jest automatycznie: * Budowana (build) * Testowana (testy jednostkowe, testy integracyjne) * Sprawdzana pod kątem zgodności ze standardami (linting, analiza statyczna)
👉 **Cel CI:** Wykrywanie błędów na najwcześniejszym etapie, zanim zepsują coś ważnego lub trafią do wydania.
🚀 **CD — Continuous Delivery (Ciągłe Dostarczanie) lub Continuous Deployment (Ciągłe Wdrażanie)**
Tutaj są dwie opcje:
✅ **Continuous Delivery (Ciągłe Dostarczanie)**
Po pomyślnym przejściu etapu CI, zmiany automatycznie: * Przechodzą dodatkowe testy (np. E2E – testy end-to-end) * Trafiają na serwer stagingowy (testowy)
👉 **Ale wdrożenie na produkcję nadal wymaga ręcznego potwierdzenia.** Daje to zespołowi kontrolę nad tym, *kiedy* dokładnie użytkownicy zobaczą zmiany.
🤖 **Continuous Deployment (Ciągłe Wdrażanie)**
Jest to kolejny krok po Continuous Delivery. Tutaj wdrożenie na produkcję odbywa się **całkowicie automatycznie**, jeśli wszystkie poprzednie etapy potoku (kompilacja, wszystkie testy) zakończyły się pomyślnie. Jest to najbardziej zaawansowany poziom automatyzacji.
🔄 Z czego zazwyczaj składa się potok CI/CD?
Typowy potok obejmuje następujące etapy:
- Checkout — Klonowanie najnowszej wersji kodu z repozytorium.
- Build — Budowanie projektu (kompilacja, tworzenie artefaktów, obrazów Docker).
- Test — Uruchamianie różnych rodzajów testów (jednostkowych, integracyjnych, E2E).
- Lint/Code Quality — Sprawdzanie kodu pod kątem zgodności ze stylem i potencjalnych błędów za pomocą statycznych analizatorów.
- Deploy — Wdrażanie aplikacji (na serwer stagingowy lub produkcyjny).
- Notify — Wysyłanie powiadomień o statusie potoku do zespołu (np. w Slacku, e-mailu).
🛠 Popularne narzędzia CI/CD:
- GitHub Actions (nasz dzisiejszy fokus!)
- GitLab CI/CD
- Jenkins
- CircleCI
- Bitbucket Pipelines
- Azure DevOps
- TeamCity
🧠 Po co w ogóle potrzebne jest CI/CD?
- Zmniejsza czynnik ludzki: Automatyzacja eliminuje błędy związane z ręcznymi operacjami.
- Szybkie wykrywanie błędów: Błędy są znajdowane wcześniej, co sprawia, że są łatwiejsze i tańsze w naprawie.
- Automatyzacja rutynowych zadań: Deweloperzy spędzają mniej czasu na budowaniu i wdrażaniu, a więcej na kodowaniu.
- Poprawa jakości kodu: Ciągłe sprawdzanie i testowanie podnosi ogólny poziom jakości.
- Szybkie dostarczanie funkcji użytkownikom: Nowe funkcje docierają do użytkownika końcowego szybciej i częściej.
📦 Proste przykłady CI/CD z GitHub Actions
Przyjrzyjmy się podstawowym potokom dla popularnych technologii. Wszystkie przykłady używają GitHub Actions i są zapisywane w katalogu .github/workflows/ Twojego projektu.
🐍 CI/CD dla Pythona (z `pytest` i `flake8`)
# .github/workflows/python-ci.yml
name: Python CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11' # Określ swoją wersję
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt # Upewnij się, że masz requirements.txt
pip install flake8 pytest
- name: Lint with flake8
run: |
# Sprawdź kod w folderach src i tests (dostosuj do swojego projektu)
flake8 src tests
- name: Run tests
run: |
pytest
🌐 CI/CD dla Node.js (z `npm test` i `eslint`)
# .github/workflows/node-ci.yml
name: Node.js CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x] # Określ swoją wersję Node.js
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install # lub npm ci dla bardziej przewidywalnej instalacji
- name: Lint with ESLint
run: npx eslint . # Upewnij się, że ESLint jest skonfigurowany w projekcie
- name: Run tests
run: npm test
🐳 CI/CD dla Docker (budowanie i wypychanie do Docker Hub)
W tym przykładzie będziesz potrzebować sekretów DOCKER_USERNAME i DOCKER_PASSWORD (lub tokena) w ustawieniach repozytorium GitHub (Settings -> Secrets and variables -> Actions).
# .github/workflows/docker-ci.yml
name: Docker CI/CD
on:
push:
branches: [ main ] # Uruchamiaj tylko dla gałęzi main
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Log in to Docker Hub
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Build Docker image
# Zastąp myapp nazwą swojej aplikacji
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/myapp:latest .
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:latest
🚚 Wdrażanie na popularne platformy
Teraz, gdy mamy zbudowane i przetestowane artefakty (np. obraz Docker), zobaczmy, jak można je wdrożyć.
🟣 Wdrażanie na Heroku
🔐 Sekrety GitHub: HEROKU_API_KEY, HEROKU_APP_NAME.
# .github/workflows/deploy-heroku.yml
name: Deploy to Heroku
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Heroku CLI
run: curl https://cli-assets.heroku.com/install.sh | sh
- name: Login to Heroku
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
run: heroku auth:token
- name: Deploy to Heroku
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
run: |
heroku git:remote -a ${{ secrets.HEROKU_APP_NAME }}
git push heroku main -f # Bądź ostrożny z -f (force push)
Jeśli wdrażasz obraz Docker na Heroku:
# ... (kroki budowania i logowania do Docker Hub/GHCR z poprzednich przykładów) ...
# deploy:
# name: Deploy to Heroku
# needs: build # Zależy od zadania budowania obrazu
# runs-on: ubuntu-latest
# steps:
# # ...
# - name: Login to Heroku container registry
# run: echo "${{ secrets.HEROKU_API_KEY }}" | docker login --username=_ --password-stdin registry.heroku.com
# - name: Tag image for Heroku
# # Zakładając, że obraz jest zbudowany jako ghcr.io/username/repo/myapp:latest
# run: docker tag ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
# - name: Push image to Heroku
# run: docker push registry.heroku.com/${{ secrets.HEROKU_APP_NAME }}/web
# - name: Release Heroku App
# env:
# HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
# run: heroku container:release web --app ${{ secrets.HEROKU_APP_NAME }}
🟨 Wdrażanie na AWS (np. pliki statyczne do S3)
🔐 Sekrety GitHub: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_BUCKET_NAME`.
# .github/workflows/deploy-aws-s3.yml
name: Deploy Static Site to AWS S3
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Sync files to S3
# Zastąp ./public ścieżką do swoich plików statycznych
run: aws s3 sync ./public s3://${{ secrets.S3_BUCKET_NAME }} --delete
Do wdrożenia na **AWS Elastic Beanstalk** zazwyczaj używa się EB CLI, potok będzie podobny, ale z poleceniami `eb deploy`.
🔵 Wdrażanie na Google Cloud Platform (GCP App Engine)
🔐 Sekrety GitHub: `GCP_CREDENTIALS` (klucz JSON konta serwisowego), `GCP_PROJECT_ID`.
# .github/workflows/deploy-gcp-app-engine.yml
name: Deploy to GCP App Engine
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_CREDENTIALS }}
export_default_credentials: true
- name: Deploy to App Engine
# Upewnij się, że masz app.yaml w katalogu głównym projektu
run: gcloud app deploy --quiet
🟪 Wdrażanie na Render.com
Render często automatycznie wdraża po pushu do GitHub, jeśli repozytorium jest podłączone. Ale dla ręcznego wyzwalacza (lub jako część bardziej złożonego potoku) można użyć Deploy Hook.
🔐 Sekrety GitHub: `RENDER_DEPLOY_HOOK` (URL uzyskany z ustawień usługi Render).
# .github/workflows/deploy-render.yml
name: Trigger Render Deploy
on:
workflow_dispatch: # Ręczne wyzwalanie z UI GitHub
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger Render Deploy Hook
run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}
🌟 Zaawansowane CI/CD: Budowanie Docker → Wypychanie do GHCR → Staging/Production na GCP Cloud Run
A teraz wisienka na torcie! Zbudujmy zaawansowany potok: 1. Budowanie obrazu Docker. 2. Publikowanie obrazu w GitHub Container Registry (ghcr.io). 3. Automatyczne wdrożenie do środowiska **staging** na GCP Cloud Run. 4. Wdrożenie do środowiska **production** na GCP Cloud Run **po ręcznym potwierdzeniu**.
Do tego potrzebne będą nam pliki workflow.
Wymagane sekrety GitHub:
- `GCP_PROJECT_ID`: ID Twojego projektu w GCP.
- `GCP_CREDENTIALS`: Klucz JSON konta serwisowego GCP z uprawnieniami do wdrażania w Cloud Run i dostępu do GHCR (jeśli potrzebne). Zazwyczaj `GITHUB_TOKEN` wystarcza do dostępu do GHCR z Actions.
- `GCP_REGION`: Region dla Cloud Run (np. `europe-west1`).
1. Budowanie i publikowanie obrazu Docker w GHCR
# .github/workflows/build.yml
name: Build & Push to GHCR
on:
push:
branches: [main] # Uruchamiaj po pushu do main
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read # Do checkout
packages: write # Do pushu do GHCR
steps:
- uses: actions/checkout@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest
# Możesz dodać tagowanie po SHA commita dla unikalności:
# tags: |
# ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest
# ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:${{ github.sha }}
- `github.repository_owner`: Właściciel repozytorium (Twoja nazwa użytkownika lub organizacji).
- `github.event.repository.name`: Nazwa repozytorium.
- `myapp`: Nazwa Twojej aplikacji/obrazu.
2. Automatyczne wdrożenie do Staging (GCP Cloud Run)
Ten workflow zostanie uruchomiony automatycznie po pomyślnym zakończeniu `build.yml`.
# .github/workflows/deploy-staging.yml
name: Deploy to GCP Cloud Run (Staging)
on:
workflow_run:
workflows: ["Build & Push to GHCR"]
types:
- completed
jobs:
deploy-staging:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
environment:
name: staging
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v3
- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
- name: 'Deploy to Cloud Run (Staging)'
id: deploy
uses: 'google-github-actions/deploy-cloudrun@v2'
with:
service: 'myapp-staging'
region: '${{ secrets.GCP_REGION }}'
image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest'
project_id: '${{ secrets.GCP_PROJECT_ID }}'
flags: '--allow-unauthenticated --platform=managed'
3. Wdrożenie do Production z ręcznym potwierdzeniem (GCP Cloud Run)
Ten workflow jest wyzwalany ręcznie za pośrednictwem interfejsu użytkownika GitHub Actions.
# .github/workflows/deploy-prod.yml
name: Deploy to GCP Cloud Run (Production)
on:
workflow_dispatch: # Pozwala na ręczne wyzwalanie
jobs:
deploy-production:
runs-on: ubuntu-latest
environment:
name: production
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v3
- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
- name: 'Deploy to Cloud Run (Production)'
id: deploy
uses: 'google-github-actions/deploy-cloudrun@v2'
with:
service: 'myapp-production'
region: '${{ secrets.GCP_REGION }}'
image: 'ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/myapp:latest'
project_id: '${{ secrets.GCP_PROJECT_ID }}'
flags: '--allow-unauthenticated --platform=managed'
# Dla produkcji możesz dodać --no-traffic, a następnie stopniowo przenosić ruch
# traffic:
# latest: true
# percent: 100
Ważne punkty tego zaawansowanego potoku:
- **GitHub Container Registry (ghcr.io):** Używamy go do przechowywania obrazów Docker. Jest to wygodne, ponieważ jest ściśle zintegrowane z GitHub Actions.
- **`workflow_run`:** Pozwala na uruchomienie jednego workflow (wdrożenie staging) po zakończeniu drugiego (budowanie).
- **`workflow_dispatch`:** Zapewnia możliwość ręcznego wyzwalania workflow (wdrożenie produkcyjne), co zapewnia kontrolę.
- **GitHub Environments:** Pozwalają na konfigurację reguł ochrony dla produkcji (np. wymaganie zatwierdzenia od określonych recenzentów) i przechowywanie sekretów specyficznych dla środowiska.
- **GCP Cloud Run:** Doskonała opcja serverless do uruchamiania aplikacji skonteneryzowanych.
🔐 Bezpieczeństwo – to ważne!
- Używaj sekretów GitHub: Nigdy nie przechowuj tokenów, haseł, kluczy API bezpośrednio w plikach YAML. Użyj
Settings -> Secrets and variables -> Actionsw swoim repozytorium. - Minimalne uprawnienia: Dla kont serwisowych (np. GCP) przyznawaj tylko te uprawnienia, które są absolutnie niezbędne do wykonywania zadań CI/CD.
- Izoluj środowiska: Staging i produkcja powinny być jak najbardziej izolowane. Różne projekty/konta u dostawców chmury to dobra praktyka.
- Ochrona gałęzi: Skonfiguruj ochronę dla gałęzi
main(lubmaster) aby wypychanie do niej było możliwe tylko poprzez Pull Request z obowiązkowymi kontrolami CI.