Зачем CI/CD
CI/CD (Continuous Integration / Continuous Deployment) -- автоматизация процесса: код попадает в репозиторий, тесты запускаются автоматически, и при успехе код деплоится в облако без ручных шагов.
Без CI/CD ваш процесс деплоя CRC выглядит так:
- Изменить код
- Запустить тесты вручную
- Собрать пакет вручную
- Загрузить в S3/Lambda вручную
- Инвалидировать CloudFront вручную
С CI/CD: git push -- и всё происходит автоматически.
GitHub Actions: основы
Структура workflow
# .github/workflows/deploy.yml
name: Deploy CRC
on:
push:
branches: [main] # Запуск при пуше в main
jobs:
test: # Название job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # Получить код
- name: Run tests # Шаг
run: python -m pytest tests/
deploy:
needs: test # Запускается только после test
runs-on: ubuntu-latest
steps:
- name: Deploy
run: echo "deploying..."
Ключевые концепции
| Концепция | Описание |
|---|---|
| Workflow | YAML-файл с описанием пайплайна |
| Trigger | Событие, запускающее workflow (push, PR, schedule) |
| Job | Набор шагов, выполняемых на одной машине |
| Step | Отдельная команда или action |
| Secret | Зашифрованные переменные (ключи, пароли) |
| Environment | Окружение с approvals (dev, staging, prod) |
Полный CI/CD для AWS CRC
Backend Pipeline
# .github/workflows/backend.yml
name: Backend CI/CD
on:
push:
branches: [main]
paths: ['backend/**', 'tests/**']
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
cd backend
pip install -r requirements.txt
pip install pytest moto boto3
- name: Run tests
run: python -m pytest tests/ -v
deploy:
needs: test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: SAM Build & Deploy
run: |
pip install aws-sam-cli
sam build
sam deploy --no-confirm-changeset --no-fail-on-empty-changeset
Frontend Pipeline
# .github/workflows/frontend.yml
name: Frontend CI/CD
on:
push:
branches: [main]
paths: ['frontend/**']
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Deploy to S3
run: |
aws s3 sync frontend/ s3://${{ secrets.S3_BUCKET }}/ \
--delete \
--cache-control "max-age=3600"
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} \
--paths "/*"
CI/CD для Azure
# .github/workflows/azure.yml
name: Azure CRC Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: |
pip install -r requirements.txt
python -m pytest tests/ -v
deploy-backend:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: azure/functions-action@v1
with:
app-name: ${{ secrets.AZURE_FUNCTION_NAME }}
package: './backend'
deploy-frontend:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Upload to Blob Storage
run: |
az storage blob upload-batch \
--account-name ${{ secrets.STORAGE_ACCOUNT }} \
--source frontend/ \
--destination '$web' \
--overwrite
- name: Purge CDN
run: |
az cdn endpoint purge \
--resource-group crc-resources \
--profile-name ${{ secrets.CDN_PROFILE }} \
--name ${{ secrets.CDN_ENDPOINT }} \
--content-paths "/*"
CI/CD для GCP
# .github/workflows/gcp.yml
name: GCP CRC Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: |
pip install -r requirements.txt
python -m pytest tests/ -v
deploy-backend:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- uses: google-github-actions/setup-gcloud@v2
- name: Deploy Cloud Function
run: |
gcloud functions deploy visitor-counter \
--gen2 \
--runtime=python312 \
--trigger-http \
--allow-unauthenticated \
--entry-point=visitor_counter \
--source=backend/ \
--region=us-central1
deploy-frontend:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- uses: google-github-actions/setup-gcloud@v2
- name: Upload to Cloud Storage
run: gsutil -m rsync -d -r frontend/ gs://${{ secrets.GCS_BUCKET }}/
OIDC: безопасная аутентификация без секретов
Вместо хранения long-lived credentials в GitHub Secrets используйте OIDC (OpenID Connect):
# AWS OIDC - рекомендуемый подход
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
aws-region: us-east-1
# Нет access key/secret key!
# GitHub получает временные credentials через OIDC
Secrets Management
# Добавление секретов в GitHub
# Settings → Secrets and variables → Actions → New repository secret
# Необходимые секреты для AWS:
# AWS_ROLE_ARN (для OIDC) или AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
# S3_BUCKET
# CF_DISTRIBUTION_ID
# Для Azure:
# AZURE_CREDENTIALS (JSON с service principal)
# STORAGE_ACCOUNT
# AZURE_FUNCTION_NAME
# Для GCP:
# GCP_CREDENTIALS (service account JSON)
# GCS_BUCKET