Настройка уведомлений в Slack, Telegram, email, интеграция с incident management.
Оперативное информирование команды о статусе деплоя критически важно для быстрого реагирования на инциденты. Изучите настройку уведомлений в Slack, Telegram, Discord, email и интеграцию с PagerDuty.
Slack Incoming Webhooks — простейший способ отправлять сообщения из CI/CD.
Настройка:
Incoming Webhooks featurename: Deploy with Slack Notifications
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy
run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
- name: Notify Slack on Success
if: success()
run: |
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d '{
"text": "✅ Deploy successful",
"attachments": [{
"color": "good",
"fields": [
{"title": "Repository", "value": "${{ github.repository }}", "short": true},
{"title": "Commit", "value": "${{ github.sha }}", "short": true},
{"title": "Environment", "value": "Production", "short": true},
{"title": "Deployed by", "value": "${{ github.actor }}", "short": true}
]
}]
}'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
- name: Notify Slack on Failure
if: failure()
run: |
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d '{
"text": "❌ Deploy failed",
"attachments": [{
"color": "danger",
"fields": [
{"title": "Repository", "value": "${{ github.repository }}", "short": true},
{"title": "Commit", "value": "${{ github.sha }}", "short": true},
{"title": "Error", "value": "Check workflow logs for details", "short": false}
],
"actions": [{
"type": "button",
"text": "View Logs",
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}]
}]
}'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}Block Kit предоставляет больше контроля над форматированием.
- name: Notify with Blocks
run: |
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚀 Deployment Complete",
"emoji": true
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Repository:*\n${{ github.repository }}"},
{"type": "mrkdwn", "text": "*Commit:*\n`${{ github.sha }}`"},
{"type": "mrkdwn", "text": "*Environment:*\nProduction"},
{"type": "mrkdwn", "text": "*Status:*\n✅ Success"}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "View Deployment"},
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
},
{
"type": "button",
"text": {"type": "plain_text", "text": "Rollback"},
"url": "${{ github.server_url }}/${{ github.repository }}/actions/new?workflow=rollback"
}
]
}
]
}'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - name: Send Initial Notification
id: notify_start
run: |
RESPONSE=$(curl -s -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d "{\"text\": \"🔄 Deployment started...\"}")
# Extract thread_ts from response if using chat.postMessage
echo "thread_ts=$(echo $RESPONSE | jq -r '.ts')" >> $GITHUB_OUTPUT
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
- name: Send Completion Update
if: always()
run: |
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d "{
\"thread_ts\": \"$THREAD_TS\",
\"text\": \"✅ Deployment completed successfully\"
}"
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
THREAD_TS: ${{ steps.notify_start.outputs.thread_ts }}Telegram Bot API позволяет отправлять сообщения через HTTP requests.
Настройка бота:
chat_id (через @getmyid_bot или API) - name: Notify Telegram
if: always()
run: |
STATUS="${{ job.status }}"
if [ "$STATUS" = "success" ]; then
EMOJI="✅"
COLOR="green"
elif [ "$STATUS" = "failure" ]; then
EMOJI="❌"
COLOR="red"
else
EMOJI="⚠️"
COLOR="yellow"
fi
MESSAGE="*${EMOJI} Deploy ${STATUS}*
*Repository:* \`${{ github.repository }}\`
*Branch:* \`${{ github.ref_name }}\`
*Commit:* \`${{ github.sha }}\`
*Environment:* Production
*Deployed by:* ${{ github.actor }}
[View Workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
curl -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{
\"chat_id\": \"${TG_CHAT_ID}\",
\"text\": \"${MESSAGE}\",
\"parse_mode\": \"Markdown\",
\"disable_web_page_preview\": true
}"
env:
TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}Telegram поддерживает Markdown и HTML:
- name: Telegram with HTML
run: |
curl -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{
\"chat_id\": \"${TG_CHAT_ID}\",
\"text\": \"<b>🚀 Deploy Complete</b>
<i>Repository:</i> ${{ github.repository }}
<i>Commit:</i> <code>${{ github.sha }}</code>
<i>Status:</i> <b>Success</b>\",
\"parse_mode\": \"HTML\"
}"
env:
TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}Discord webhooks поддерживают богатые embed сообщения.
- name: Notify Discord
if: always()
run: |
STATUS="${{ job.status }}"
if [ "$STATUS" = "success" ]; then
COLOR=3066993 # Green
TITLE="✅ Deploy Successful"
else
COLOR=15158332 # Red
TITLE="❌ Deploy Failed"
fi
curl -X POST $DISCORD_WEBHOOK \
-H "Content-Type: application/json" \
-d "{
\"embeds\": [{
\"title\": \"${TITLE}\",
\"color\": ${COLOR},
\"fields\": [
{\"name\": \"Repository\", \"value\": \"${{ github.repository }}\", \"inline\": true},
{\"name\": \"Commit\", \"value\": \"\`${{ github.sha }}\`\", \"inline\": true},
{\"name\": \"Environment\", \"value\": \"Production\", \"inline\": true},
{\"name\": \"Deployed by\", \"value\": \"${{ github.actor }}\", \"inline\": true}
],
\"footer\": {
\"text\": \"GitHub Actions\",
\"icon_url\": \"https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
},
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
}]
}"
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} - name: Discord with Multiple Embeds
run: |
curl -X POST $DISCORD_WEBHOOK \
-H "Content-Type: application/json" \
-d "{
\"embeds\": [
{
\"title\": \"🚀 Deployment Summary\",
\"color\": 3066993,
\"description\": \"Production deployment completed successfully\",
\"fields\": [
{\"name\": \"Duration\", \"value\": \"5m 32s\", \"inline\": true},
{\"name\": \"Services\", \"value\": \"3\", \"inline\": true}
]
},
{
\"title\": \"📊 Health Check Results\",
\"color\": 3447003,
\"fields\": [
{\"name\": \"API\", \"value\": \"✅ Healthy\", \"inline\": true},
{\"name\": \"Database\", \"value\": \"✅ Connected\", \"inline\": true},
{\"name\": \"Cache\", \"value\": \"✅ Operational\", \"inline\": true}
]
}
]
}"
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}GitLab CI поддерживает встроенные email уведомления:
# .gitlab-ci.yml
stages:
- build
- deploy
deploy_production:
stage: deploy
environment: production
script:
- ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
notifications:
on_success: change
on_failure: always
recipients:
- team@example.com
- oncall@example.com - name: Send Email via SMTP
if: failure()
run: |
python3 - <<EOF
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
msg = MIMEMultipart()
msg['From'] = os.environ['SMTP_FROM']
msg['To'] = os.environ['SMTP_TO']
msg['Subject'] = f"❌ Deploy Failed - ${{os.environ['CI_PROJECT_NAME']}}"
body = f"""
Deployment failed in production.
Project: ${{os.environ['CI_PROJECT_NAME']}}
Branch: ${{os.environ['CI_COMMIT_BRANCH']}}
Commit: ${{os.environ['CI_COMMIT_SHA']}}
Pipeline: ${{os.environ['CI_PIPELINE_URL']}}
Please investigate immediately.
"""
msg.attach(MIMEText(body, 'plain'))
server = smtplib.SMTP(os.environ['SMTP_HOST'], int(os.environ['SMTP_PORT']))
server.starttls()
server.login(os.environ['SMTP_USER'], os.environ['SMTP_PASSWORD'])
server.send_message(msg)
server.quit()
print("Email sent successfully")
EOF
env:
SMTP_HOST: ${{ secrets.SMTP_HOST }}
SMTP_PORT: ${{ secrets.SMTP_PORT }}
SMTP_USER: ${{ secrets.SMTP_USER }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
SMTP_FROM: noreply@example.com
SMTP_TO: team@example.com - name: Send Email via SendGrid
if: failure()
run: |
curl -X POST "https://api.sendgrid.com/v3/mail/send" \
-H "Authorization: Bearer $SENDGRID_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"personalizations\": [{
\"to\": [{\"email\": \"team@example.com\"}],
\"subject\": \"❌ Deploy Failed - ${{ github.repository }}\"
}],
\"from\": {\"email\": \"noreply@example.com\"},
\"content\": [{
\"type\": \"text/html\",
\"value\": \"<h1>Deployment Failed</h1>
<p><strong>Repository:</strong> ${{ github.repository }}</p>
<p><strong>Commit:</strong> <code>${{ github.sha }}</code></p>
<p><a href='${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'>View Logs</a></p>\"
}]
}"
env:
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}PagerDuty Events API создаёт incidents с автоматической эскалацией.
- name: Notify PagerDuty on Critical Failure
if: failure() && github.ref == 'refs/heads/main'
run: |
curl -X POST "https://events.pagerduty.com/v2/enqueue" \
-H "Content-Type: application/json" \
-d "{
\"routing_key\": \"$PAGERDUTY_ROUTING_KEY\",
\"event_action\": \"trigger\",
\"payload\": {
\"summary\": \"Production deployment failed - ${{ github.repository }}\",
\"severity\": \"critical\",
\"source\": \"github-actions\",
\"component\": \"deploy-pipeline\",
\"group\": \"platform-team\",
\"class\": \"deployment\",
\"custom_details\": {
\"repository\": \"${{ github.repository }}\",
\"commit\": \"${{ github.sha }}\",
\"branch\": \"${{ github.ref_name }}\",
\"workflow\": \"${{ github.workflow }}\",
\"run_url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"
}
},
\"dedup_key\": \"deploy-${{ github.repository }}-${{ github.sha }}\"
}"
env:
PAGERDUTY_ROUTING_KEY: ${{ secrets.PAGERDUTY_ROUTING_KEY }} - name: Resolve PagerDuty Incident
if: success() && github.ref == 'refs/heads/main'
run: |
curl -X POST "https://events.pagerduty.com/v2/enqueue" \
-H "Content-Type: application/json" \
-d "{
\"routing_key\": \"$PAGERDUTY_ROUTING_KEY\",
\"event_action\": \"resolve\",
\"dedup_key\": \"deploy-${{ github.repository }}-${{ github.sha }}\"
}"
env:
PAGERDUTY_ROUTING_KEY: ${{ secrets.PAGERDUTY_ROUTING_KEY }} - name: Create Post-Mortem Jira Issue
if: failure() && github.ref == 'refs/heads/main'
run: |
curl -X POST "https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}/rest/api/3/issue" \
-H "Authorization: Bearer $JIRA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"fields\": {
\"project\": {\"key\": \"INC\"},
\"summary\": \"[Post-Mortem] Production Deploy Failure - ${{ github.repository }}\",
\"description\": {
\"type\": \"doc\",
\"version\": 1,
\"content\": [
{
\"type\": \"paragraph\",
\"content\": [{\"type\": \"text\", \"text\": \"Deployment failed in production. Please complete post-mortem analysis.\"}]
},
{
\"type\": \"bulletList\",
\"content\": [
{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"text\", \"text\": \"Repository: ${{ github.repository }}\"}]}]},
{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"text\", \"text\": \"Commit: ${{ github.sha }}\"}]}]},
{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"text\", \"text\": \"Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)\"}]}]}
]
}
]
},
\"issuetype\": {\"name\": \"Incident\"},
\"priority\": {\"name\": \"Highest\"},
\"labels\": [\"post-mortem\", \"production\", \"deploy-failure\"]
}
}"
env:
JIRA_CLOUD_ID: ${{ secrets.JIRA_CLOUD_ID }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} - name: Check for Duplicate Alert
id: dedup
run: |
ALERT_HASH=$(echo -n "${{ github.repository }}-${{ github.sha }}-failure" | sha256sum | cut -d' ' -f1)
# Check Redis for existing alert
EXISTS=$(curl -s "https://api.upstash.io/redis/${UPSTASH_REDIS_REST_URL}/get/${ALERT_HASH}" \
-H "Authorization: Bearer ${UPSTASH_REDIS_REST_TOKEN}" | jq -r '.result')
if [ "$EXISTS" = "null" ]; then
echo "should_notify=true" >> $GITHUB_OUTPUT
# Set with 1 hour TTL
curl -s "https://api.upstash.io/redis/${UPSTASH_REDIS_REST_URL}/set/${ALERT_HASH}/notified/EX/3600" \
-H "Authorization: Bearer ${UPSTASH_REDIS_REST_TOKEN}"
else
echo "Duplicate alert suppressed"
echo "should_notify=false" >> $GITHUB_OUTPUT
fi
env:
UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
- name: Notify on Unique Failure
if: steps.dedup.outputs.should_notify == 'true'
run: |
# Send notification only if not duplicate
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d "{\"text\": \"❌ Deploy failed (unique alert)\"}"
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - name: Select Channel by Environment
run: |
case "${{ github.environment }}" in
production)
WEBHOOK=$SLACK_WEBHOOK_PROD
CHANNEL="#deploy-prod"
;;
staging)
WEBHOOK=$SLACK_WEBHOOK_STAGING
CHANNEL="#deploy-staging"
;;
development)
WEBHOOK=$SLACK_WEBHOOK_DEV
CHANNEL="#deploy-dev"
;;
*)
WEBHOOK=$SLACK_WEBHOOK_DEFAULT
CHANNEL="#deploy-all"
;;
esac
echo "webhook=$WEBHOOK" >> $GITHUB_OUTPUT
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
id: channel
- name: Send Notification
run: |
curl -X POST ${{ steps.channel.outputs.webhook }} \
-H "Content-Type: application/json" \
-d "{
\"text\": \"Deploy to ${{ github.environment }} completed\",
\"channel\": \"${{ steps.channel.outputs.channel }}\"
}"
env:
SLACK_WEBHOOK_PROD: ${{ secrets.SLACK_WEBHOOK_PROD }}
SLACK_WEBHOOK_STAGING: ${{ secrets.SLACK_WEBHOOK_STAGING }}
SLACK_WEBHOOK_DEV: ${{ secrets.SLACK_WEBHOOK_DEV }}Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.