Spis treści 11 sekcji
- CI/CD - dlaczego ręczne deploymenty to przepis na katastrofę
- AWS CodePipeline - natywne CI/CD w ekosystemie AWS
- GitHub Actions - CI/CD z ekosystemem marketplace
- Porównanie: CodePipeline vs GitHub Actions
- Koszty CI/CD - co jest tańsze?
- Pipeline dla statycznej strony na S3 + CloudFront
- Pipeline dla Lambda (serverless)
- Best practices CI/CD na AWS
- Monitoring pipeline - bo pipeline też się psuje
- FAQ - najczęstsze pytania o CI/CD na AWS
- Co dalej?
CI/CD - dlaczego ręczne deploymenty to przepis na katastrofę
Wyobraź sobie: piątek, 16:00. Klient prosi o pilny fix. Wchodzisz na serwer, edytujesz plik, restartujesz Apache. Działa! Do poniedziałku, gdy okazuje się, że nadpisałeś zmiany kolegi. Albo gorzej - deploy poszedł na produkcję bez testów i teraz połowa użytkowników widzi błąd 500.
CI/CD (Continuous Integration / Continuous Deployment) eliminuje te problemy:
- CI - każdy push do repozytorium automatycznie uruchamia testy
- CD - kod, który przejdzie testy, automatycznie trafia na serwer
Na AWS masz dwa główne podejścia: natywne narzędzia (CodePipeline + CodeBuild + CodeDeploy) lub zewnętrzne CI/CD (GitHub Actions, GitLab CI, Jenkins). Porównam dwa najpopularniejsze.
AWS CodePipeline - natywne CI/CD w ekosystemie AWS
Jak działa CodePipeline?
CodePipeline to orkiestrator pipeline, który łączy etapy w sekwencję:
- Source - pobiera kod z GitHub, S3, Bitbucket lub CodeCommit (uwaga: od lipca 2024 CodeCommit nie przyjmuje nowych klientów)
- Build - CodeBuild uruchamia testy, buduje artefakty (Docker image, ZIP)
- Deploy - CodeDeploy, ECS, Lambda, S3, CloudFormation wdraża kod
- Approval (opcjonalnie) - ręczna aprobata przed deploy na produkcję
Tworzenie pipeline w CodePipeline
- Wejdź w CodePipeline → Create pipeline
- Podaj nazwę:
moja-apka-pipeline - W Source wybierz GitHub (Version 2) i połącz konto GitHub
- Wybierz repozytorium i branch (np.
main) - W Build wybierz AWS CodeBuild → Create project
- Skonfiguruj CodeBuild: Environment Amazon Linux 2023, Runtime Standard 5.0
- W Buildspec wybierz Use a buildspec file (plik
buildspec.ymlz repozytorium) - W Deploy wybierz target (np. ECS, S3, CodeDeploy)
- Kliknij Create pipeline - pipeline uruchomi się natychmiast
# 1. Utwórz rolę IAM dla CodeBuild
aws iam create-role \
--role-name codebuild-moja-apka-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "codebuild.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# 2. Dołącz polityki
aws iam attach-role-policy \
--role-name codebuild-moja-apka-role \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
aws iam put-role-policy \
--role-name codebuild-moja-apka-role \
--policy-name CloudWatchLogs \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"],
"Resource": "*"
}]
}'
# 3. Utwórz projekt CodeBuild
aws codebuild create-project \
--name "moja-apka-build" \
--source type=GITHUB,location=https://github.com/user/repo \
--environment type=LINUX_CONTAINER,computeType=BUILD_GENERAL1_SMALL,image=aws/codebuild/amazonlinux2-x86_64-standard:5.0 \
--service-role arn:aws:iam::123456789:role/codebuild-moja-apka-role \
--artifacts type=NO_ARTIFACTS
# 4. Utwórz pipeline (wymaga pliku JSON z definicją)
aws codepipeline create-pipeline \
--cli-input-json file://pipeline-definition.json
Plik buildspec.yml - serce CodeBuild
buildspec.yml to plik w katalogu głównym repozytorium, który mówi CodeBuild co ma robić:
# buildspec.yml - przykład dla aplikacji Node.js + Docker
version: 0.2
env:
variables:
ECR_REPO: 123456789.dkr.ecr.eu-central-1.amazonaws.com/moja-apka
AWS_DEFAULT_REGION: eu-central-1
phases:
install:
runtime-versions:
nodejs: 20
commands:
- echo "Instalacja zależności..."
- npm ci
pre_build:
commands:
- echo "Uruchamianie testów..."
- npm test
- echo "Logowanie do ECR..."
- aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REPO
build:
commands:
- echo "Budowanie Docker image..."
- docker build -t $ECR_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION .
- docker tag $ECR_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION $ECR_REPO:latest
post_build:
commands:
- echo "Pushowanie image do ECR..."
- docker push $ECR_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION
- docker push $ECR_REPO:latest
- echo "Generowanie definicji ECS..."
- printf '[{"name":"app","imageUri":"%s"}]' $ECR_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
cache:
paths:
- node_modules/**/*
GitHub Actions - CI/CD z ekosystemem marketplace
Dlaczego GitHub Actions jest tak popularne?
GitHub Actions ma trzy przewagi: jest tam, gdzie już jest Twój kod (GitHub), ma ogromny marketplace gotowych actions, i konfiguracja YAML jest prostsza niż buildspec + pipeline JSON.
Konfiguracja OIDC - bezpieczne połączenie z AWS
Zanim GitHub Actions może wdrażać na AWS, musisz skonfigurować uwierzytelnianie. Zapomnij o kluczach API w secrets - w 2026 standard to OIDC (OpenID Connect):
- Wejdź w IAM → Identity providers → Add provider
- Typ: OpenID Connect
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Kliknij Add provider
- Utwórz rolę IAM z trust policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
}
}
}]
}
# 1. Dodaj GitHub jako Identity Provider
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
# Uwaga: od lipca 2023 AWS ignoruje thumbprint dla GitHub OIDC
# (weryfikacja przez zaufane root CA) - w nowszych wersjach CLI
# parametr jest opcjonalny i możesz go pominąć
# 2. Utwórz rolę z trust policy dla GitHub
aws iam create-role \
--role-name github-actions-deploy \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
}
}
}]
}'
# 3. Dołącz potrzebne polityki
aws iam attach-role-policy \
--role-name github-actions-deploy \
--policy-arn arn:aws:iam::aws:policy/AmazonECS_FullAccess
aws iam attach-role-policy \
--role-name github-actions-deploy \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
StringLike na sub ogranicza rolę do konkretnego repozytorium i brancha. Bez tego warunku każde publiczne repozytorium GitHub mogłoby assumować Twoją rolę! Więcej o bezpieczeństwie IAM.Gotowy workflow GitHub Actions - deploy na ECS
# .github/workflows/deploy.yml
name: Deploy to AWS ECS
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
id-token: write # wymagane dla OIDC
contents: read
env:
AWS_REGION: eu-central-1
ECR_REPOSITORY: moja-apka
ECS_CLUSTER: prod-cluster
ECS_SERVICE: moja-apka-service
ECS_TASK_DEFINITION: task-definition.json
CONTAINER_NAME: app
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm test
- run: npm run lint
deploy:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Authenticate via OIDC (no secrets needed!)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Update ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
Porównanie: CodePipeline vs GitHub Actions
| Aspekt | AWS CodePipeline | GitHub Actions |
|---|---|---|
| Krzywa uczenia | Stroma (IAM roles, buildspec, pipeline stages) | Łagodna (YAML, marketplace actions) |
| Konfiguracja | buildspec.yml + pipeline JSON/Console | Jeden plik .yml w repozytorium |
| Integracja z AWS | Natywna - zero konfiguracji auth | Wymaga OIDC lub access keys |
| Marketplace | Brak | 19,000+ gotowych actions |
| Wizualizacja | Pipeline view z etapami | Workflow graph |
| Manual approval | Wbudowane (SNS notification) | Environments + protection rules |
| Secrets management | Parameter Store / Secrets Manager | GitHub Secrets + OIDC (lepiej) |
| Self-hosted runners | Nie (managed only) | Tak (self-hosted runners) |
| Parallel jobs | Ograniczone (parallel actions w stage) | Matrix strategy + parallel jobs |
| Cache | S3 (konfiguracja ręczna) | actions/cache (jedna linia) |
| Vendor lock-in | Wysoki (wszystko w AWS) | Niski (przenośne workflowy) |
Koszty CI/CD - co jest tańsze?
| Komponent | CodePipeline + CodeBuild | GitHub Actions |
|---|---|---|
| Pipeline/Workflow | V2 (domyślny): $0.002/min akcji, 100 min free/mc; V1: $1/pipeline/mc (1 free) | Free (publiczne repo) / 2000 min/mc (prywatne) |
| Build minutes | $0.005/min (small), $0.01/min (medium) | $0.008/min (Linux) po wyczerpaniu free |
| 100 buildów × 5 min/mc | ~$3.50/mc | $0 (mieści się w free tier) |
| 500 buildów × 5 min/mc | ~$13.50/mc | ~$4/mc (500 min ponad free tier × $0.008) |
| CodeDeploy | Free dla EC2/Lambda | - |
| Artifact storage | S3 ($0.023/GB) | 500 MB free / $0.25/GB |
Pipeline dla statycznej strony na S3 + CloudFront
Nie każdy projekt wymaga Dockera i ECS. Jeśli masz stronę statyczną (React, Next.js, Hugo), deploy na S3 + CloudFront jest prostszy i tańszy:
# .github/workflows/deploy-static.yml
name: Deploy Static Site
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: eu-central-1
- name: Sync to S3
run: |
aws s3 sync ./dist s3://moja-strona-bucket \
--delete \
--cache-control "public, max-age=31536000" \
--exclude "index.html"
# index.html bez cache (zawsze najnowszy)
aws s3 cp ./dist/index.html s3://moja-strona-bucket/index.html \
--cache-control "public, max-age=0, must-revalidate"
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/*"
Pipeline dla Lambda (serverless)
Deploy funkcji Lambda jest jeszcze prostszy:
# .github/workflows/deploy-lambda.yml
name: Deploy Lambda
on:
push:
branches: [main]
paths:
- 'src/lambda/**' # trigger tylko gdy zmienia się kod Lambda
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
aws-region: eu-central-1
- name: Install dependencies & package
run: |
cd src/lambda
pip install -r requirements.txt -t ./package
cd package && zip -r ../../../function.zip .
cd .. && zip -g ../../function.zip handler.py
- name: Deploy to Lambda
run: |
aws lambda update-function-code \
--function-name moja-funkcja \
--zip-file fileb://function.zip \
--publish
- name: Wait for update
run: |
aws lambda wait function-updated \
--function-name moja-funkcja
echo "Deploy successful!"
Best practices CI/CD na AWS
1. Środowiska (environments)
Nigdy nie deployuj bezpośrednio na produkcję. Minimum to:
- dev - deploy automatyczny po każdym pushu
- staging - deploy automatyczny z brancha main, testy E2E
- production - deploy po manual approval lub po merge do brancha release
2. Rollback strategy
Zawsze miej plan B:
- ECS - rolling update z automatycznym rollbackiem (circuit breaker)
- Lambda - versioning + alias, rollback = przestaw alias na poprzednią wersję
- EC2 (CodeDeploy) - blue/green deployment, rollback = przełącz na starą grupę
- S3 (statyczne) - versioning na bucket, rollback = przywróć poprzedni index.html
3. Secrets w pipeline
Jak bezpiecznie przechowywać klucze API, hasła do bazy i tokeny:
| Podejście | CodePipeline | GitHub Actions |
|---|---|---|
| Klucze AWS | IAM Role (automatyczne) | OIDC (rekomendowane) lub Secrets |
| Hasła DB | Secrets Manager → CodeBuild env | GitHub Secrets → env variables |
| API keys | Parameter Store (SecureString) | GitHub Secrets |
| Rotacja | Automatyczna (Secrets Manager) | Ręczna |
Monitoring pipeline - bo pipeline też się psuje
CI/CD bez monitoringu to kolejny single point of failure. Skonfiguruj:
- Powiadomienia o failed deployach - SNS + Slack/email
- Metryki pipeline - czas budowania, success rate, częstotliwość deployów
- Post-deploy health checks - po deployu sprawdź czy aplikacja odpowiada HTTP 200
Konfigurację monitoringu opisałem szczegółowo w poradniku CloudWatch i X-Ray.
# Post-deploy health check w GitHub Actions
- name: Health check
run: |
for i in {1..10}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://moja-apka.pl/health)
if [ "$STATUS" = "200" ]; then
echo "Health check passed!"
exit 0
fi
echo "Attempt $i: HTTP $STATUS, retrying in 15s..."
sleep 15
done
echo "Health check failed after 10 attempts!"
exit 1
FAQ - najczęstsze pytania o CI/CD na AWS
Czy mogę użyć GitHub Actions z CodeDeploy?
aws deploy create-deployment. Dostajesz prostotę GitHub + zaawansowane strategie deploymentu AWS.Ile pipeline mogę mieć w CodePipeline za darmo?
Co jest bezpieczniejsze - OIDC czy AWS Access Keys w GitHub Secrets?
Jak zrobić deploy na wiele środowisk (dev/staging/prod)?
Czy warto używać Terraform w pipeline CI/CD?
terraform plan na PR (jako komentarz), a terraform apply po merge. Opisałem to dokładniej w artykule o Terraform + AWS.Co dalej?
- Wybierz podejście: GitHub Actions (prostsze) lub CodePipeline (natywne AWS).
- Skonfiguruj OIDC dla GitHub Actions lub rolę IAM dla CodePipeline.
- Zacznij od prostego pipeline (test → deploy na dev) i rozbudowuj stopniowo.
- Dodaj manual approval gate przed produkcją.
- Skonfiguruj monitoring i alarmy na pipeline failures.
CI/CD to inwestycja, która zwraca się przy pierwszym "hotfixie w piątek o 17:00". Zamiast stresu i ręcznych kroków - jeden merge i pipeline robi resztę. Jeśli dopiero zaczynasz z AWS, sprawdź najpierw co jest za darmo w AWS Free Tier. A jeśli planujesz pełną architekturę - przeczytaj o architekturze aplikacji webowej na AWS.
