Azure DevOpsで実現するCI/CDパイプライン最適化
Azure DevOps CI/CD:金曜日のデプロイが怖くなくなった日
はじめに:「また週末が台無しか...」
「金曜日のデプロイはやめとこう」 「でも月曜まで待つと、変更が溜まりすぎて余計危険じゃない?」 「じゃあ、祈りながらやるか...」
こんな会話、聞き覚えありませんか?私たちのチームでは毎週金曜日の定番でした。
デプロイに4時間。その後のエラー対応で深夜まで。土曜日も出社して対応...こんな生活を3年も続けていました。
そんな私たちがAzure DevOpsと出会って、今では「金曜の夕方?全然OK!」とケロッとしています。
何が変わったのか、恥ずかしい失敗談も含めて全部お話しします。
YAMLパイプライン:「この呪文、何語?」から始まった物語
最初は本当に意味不明だった
正直に告白します。初めてazure-pipelines.ymlを見た時、画面を閉じました。
「trigger? pool? steps? 何これ...」 「スペースが2個じゃなくて3個だとエラー?意味わからん」
でも、先輩エンジニアが教えてくれたんです。 「これ、実はレシピみたいなもんだよ」
その一言で目から鱗が落ちました。
料理のレシピと同じ。材料(リソース)を用意して、手順(ステップ)通りに進めるだけ。しかも、このレシピはGitで管理できて、みんなでレビューできる!
「Hello World」から始めた私たちの挑戦
最初の一歩は本当に小さかった。
月曜日の朝、チーム全員で画面を囲んで「えいっ」とコミット。 5秒後...「Build succeeded」の緑色のマークが!
「おお!動いた!」
たった「Hello, World!」を表示するだけのパイプライン。でも、この小さな成功体験が全ての始まりでした。
それから毎日少しずつ機能を追加:
- 火曜日:「ビルドも自動化してみよう」
- 水曜日:「テストも追加できるんじゃない?」
- 木曜日:「成果物の保存もできた!」
- 金曜日:「来週はデプロイも自動化だ!」
3ヶ月後、気づいたら本格的なCI/CDパイプラインが動いていました。まるで、積み木を一つずつ積み上げるような感覚でした。 displayName: 'Run unit tests' inputs: command: 'test' projects: '**/*Tests.csproj' arguments: '--configuration $(BuildConfiguration) --no-build --collect:"XPlat Code Coverage" --logger trx --results-directory $(Agent.TempDirectory)' publishTestResults: true
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
- task: SonarCloudAnalyze@1
displayName: 'Run SonarCloud analysis'
- task: SonarCloudPublish@1
displayName: 'Publish SonarCloud results'
inputs:
pollingTimeoutSec: '300'
- task: DotNetCoreCLI@2
displayName: 'Publish application'
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory) --no-build'
zipAfterPublish: true
- task: PublishBuildArtifacts@1
displayName: 'Publish artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
- stage: SecurityScan
displayName: 'Security Scanning'
dependsOn: Build
condition: succeeded()
jobs:
- job: SecurityScanJob
恥ずかしい失敗の数々(でも今は笑い話)
「なんでビルドが止まるの?」事件
金曜日の夕方、ビルドが30分で突然停止。 「バグ?」「設定ミス?」「Azure側の問題?」
3時間調査して分かった原因:タイムアウトのデフォルト設定... 「最初に言ってよ!」とドキュメントに八つ当たり(笑)
「パスワード流出」危機一髪
コードレビューで先輩が青ざめた顔で... 「これ、本番のパスワード丸見えだけど」
慌ててGitの履歴から削除。Variable Groups使えばよかった... 今では笑い話ですが、当時は本当に焦りました。
「高速化のつもりが逆効果」の罠
「並列実行すれば速くなる!」と意気込んで全部並列に。 結果:エージェント不足で順番待ち地獄。
単純に並列化すればいいってもんじゃないんですね...
ステージとジョブ:「パイプラインを積み木みたいに」
「ステージって必要?」から「ステージなしでは生きられない」へ
最初は正直、面倒だと思ってました。 「全部一気に実行すればいいじゃん」
でも、ある日の失敗で考えが変わりました。
本番デプロイ中にテストが失敗。でも、もう遅い。本番は半分だけ新バージョンという最悪の状態に...
「あー、ステージ分けてれば止められたのに」
そこから真剣にステージ設計を考えるように。今では:
朝9時:コードをプッシュ 9時05分:ビルド完了、テスト開始 9時15分:開発環境にデプロイ 9時25分:統合テスト完了 9時30分:「本番行きますか?」の通知 (ここでコーヒーブレイク) 9時45分:承認ボタンをポチッ 9時48分:本番デプロイ完了!
以前の4時間が30分に。しかも、どの段階でも止められる安心感付き。
再利用可能なテンプレート:DRYの極み
「コピペ地獄」からの解放
最初は各環境用に同じようなYAMLを3つも4つも書いてました。 「開発環境のデプロイ手順変えたから、ステージングも本番も全部書き換えて...」
疲れました。そして気づいたんです、テンプレート機能の素晴らしさに!
テンプレート化して良かったこと:
- 修正が1箇所で済む(神!)
- パラメータで環境ごとの違いを吸収
- チーム全員が同じ手順でデプロイ
- ミスが激減(コピペミスがなくなった)
実際に使ってるテンプレート構成
# シンプルなデプロイテンプレートの例
parameters:
- name: environment
type: string
steps:
- script: echo "Deploying to ${{ parameters.environment }}"
- task: AzureWebApp@1
inputs:
appName: 'myapp-${{ parameters.environment }}'
たったこれだけで、全環境で使い回せる!
つまずきポイント:
パラメータの型を間違えて30分悩んだ。string
とobject
の違いは重要...
Blue-Greenデプロイ:「魔法みたい!」と叫んだ日
「ユーザーに気づかれずにデプロイ」の衝撃
忘れもしない、火曜日の午後2時。 一番アクセスが多い時間帯に、あえてデプロイしてみました。
社内チャット: 「今からデプロイします」 「え?今?ユーザー使ってるよ?」 「大丈夫です、見ててください」
3分後...
「デプロイ完了しました」 「嘘でしょ?エラー出てない?」 「ユーザーからクレームは?」 「何も起きてません」
本当に魔法を使った気分でした。ユーザーは何も気づかずにサービスを使い続けている。その裏で、こっそり新バージョンに切り替わっている。
仕組みを理解した今でも「これ、すごくない?」って思います。
実際にやってみて分かったこと
メリット:
- ダウンタイムゼロ(本当にゼロ!)
- ロールバックが一瞬(スロット入れ替えるだけ)
- 本番と全く同じ環境でテスト可能
デメリット:
- コストが2倍(2つの環境が必要)
- データベース移行は要注意
- 設定が少し複雑
つまずきポイント: 最初、ウォームアップを忘れて、切り替え直後にレスポンスが遅くなった。今は必ずウォームアップしてから切り替えてます。
手動承認:「ストップ!」と言える幸せ
金曜日の午後4時。Teamsに通知が。 「本番デプロイの準備ができました。承認してください」
チームメンバー:「今日は早く帰りたいから月曜にしない?」 私:「了解!」(承認を保留)
以前なら「一度始めたら止められない」デプロイ。今は違います。
ある日のエピソード: ステージング確認中に気づいた。「あれ?このボタンの色、違くない?」 承認を却下して、修正してから再度デプロイ。
もし自動で本番まで行ってたら、お客様から「ボタンが見づらい」ってクレームが来てたかも。
「人間の最終チェック」って、やっぱり大事ですね。
自動ロールバック:保険があるから攻められる
「デプロイ後にエラー率が上がったら自動で戻す」
この仕組みを入れてから、デプロイのプレッシャーが激減しました。
実際に自動ロールバックが発動した事例:
- データベース接続文字列の設定ミス(5分で自動復旧)
- 外部APIのURL変更忘れ(3分で検知、自動復旧)
- パフォーマンス劣化(レスポンス2秒超えで自動復旧)
全部、お客様が気づく前に復旧できました!
パフォーマンステスト:「本番で遅い!」の恐怖から解放
あの日の悪夢
月曜日の朝9時。サポートチームから緊急連絡。 「サイトがめちゃくちゃ遅いんですけど!」
確認すると、トップページの表示に15秒... 原因は金曜日にリリースした新機能。データベースのインデックスを張り忘れてました。
朝一番から大騒ぎ。お客様にも迷惑をかけて、本当に申し訳ない気持ちでいっぱいでした。
「もう二度とこんな思いはしたくない」
そこから、パフォーマンステストの自動化に本気で取り組みました。
今では「本番より厳しい条件」でテストしてからリリース。 「遅い」という苦情はゼロになりました。
実際の負荷テストシナリオ
# 最小限の負荷テスト設定
- task: AzureCLI@2
displayName: 'Run load test'
inputs:
inlineScript: |
# 100ユーザーで5分間アクセス
artillery quick --count 100 --num 50 https://staging.myapp.com/
シンプルだけど、これだけでも全然違う!
発見した問題の例:
- N+1問題(100件表示で100回DB アクセス...)
- キャッシュ未設定(同じデータを何度も取得)
- 画像最適化忘れ(1枚3MBの画像が...)
本番で見つかってたら大変でした。
自動ロールバック:「守護神」のような存在
ある水曜日の昼下がり。デプロイ完了の通知を見て、ランチに出かけました。
15分後、スマホが震える。 「自動ロールバックを実行しました」
慌てて確認すると、新機能のメモリリークが原因でした。 もし自動ロールバックがなかったら、ランチから戻ってきた時にはサーバーダウンしてたかも...
「ありがとう、自動ロールバック君」
心の中でつぶやきました。
別の日には、外部APIがたまたまメンテナンス中で、それを検知して自動ロールバック。 「そんなことまで守ってくれるの?」と驚きました。
まるで24時間体制の守護神がいるような安心感。これがあるから、積極的に新機能をリリースできるんです。
パフォーマンス監視:「遅くなったら自動で戻す」
レスポンスタイムが20%以上悪化したら自動ロールバック。 シンプルだけど、効果抜群!
実装のコツ:
- ベースラインは過去24時間の平均値
- 一時的なスパイクは無視(5分間の平均で判断)
- 閾値は環境に応じて調整(最初は緩めに)
テストの自動化:「手動テスト地獄」からの解放
昔は全部手動でテストしてた...
「リリース前のテスト、3日かかります」 「えっ、そんなに?」 「全画面、全機能を手動で確認するので...」
今思えば狂気の沙汰でした。
自動テスト導入後:
- 単体テスト:3分で5000件実行
- 統合テスト:10分で全API確認
- E2Eテスト:15分で主要シナリオ完了
- セキュリティテスト:5分で脆弱性チェック
合計33分!以前の3日が30分になりました。
テストの並列実行で時間短縮
# シンプルな並列実行の例
jobs:
- job: Tests
strategy:
parallel: 3 # 3つ同時実行!
これだけで実行時間が1/3に!
つまずきポイント:
- DB使うテストは並列化注意(データが混ざる)
- 依存関係があるテストは順番に
- エージェント数の上限に注意
監視とアラート:「気づいたら落ちてた」を撲滅
Application Insightsで全部見える化
「デプロイの成功率って何%?」 「先月、何回ロールバックした?」 「平均デプロイ時間は?」
以前は答えられませんでした。今は即答できます!
見える化して分かったこと:
- 金曜日のデプロイ成功率が低い(疲れてる?)
- 午前中のデプロイが最も安定
- 特定の開発者のデプロイがよく失敗する(サポートが必要)
アラート設定:寝てても安心
実際に設定してるアラート:
-
レスポンスタイム監視
- 平均2秒超えたらWarning
- 5秒超えたらCritical
- 夜中でも電話が鳴る(Critical時のみ)
-
エラー率監視
- 5%超えたらWarning
- 10%超えたら自動ロールバック
-
デプロイ監視
- 30分以上かかったらアラート
- 失敗したら即通知
つまずきポイント: 最初、閾値を厳しくしすぎて、アラートの嵐に...今は現実的な値に調整してます。
ダッシュボード:経営陣も納得
今月のデプロイ実績
━━━━━━━━━━━━━━━━━━
成功: 145回 (96.7%)
失敗: 5回 (3.3%)
平均時間: 12分
ロールバック: 2回
これを見せたら、DevOps投資の追加予算が承認されました!
導入効果:「人生変わった」は大げさじゃない
数字が物語る劇的な変化
社内プレゼンで使ったスライドを見返すと、今でも信じられません。
月1回→1日10回のリリース
以前:「来月の定期リリースに間に合わせなきゃ」 今:「午前中に3回リリースしちゃった」
300倍って、もはや別世界です。
4時間→15分のデプロイ時間
金曜日の午後の過ごし方:
- 以前:会議室に缶詰め、ピザ注文、徹夜覚悟
- 今:普通に仕事して、定時で帰宅
「金曜日の呪い」から解放されました。
深夜対応がゼロに
妻に言われた一言: 「最近、夜中に電話鳴らなくなったね」
そうなんです。自動ロールバックのおかげで、深夜に起こされることがなくなりました。 家族との時間も増えて、本当に人生変わったなって思います。
「本当に元取れるの?」への明確な答え
経営会議での一幕:
社長:「で、いくらかかるの?」 私:「初期投資200万円です」 社長:「高いなぁ...」 私:「3ヶ月で回収できます」 社長:「本当に?」
そして見せたのがこの数字:
残業代だけで月100万円削減
- 金曜デプロイの残業:ゼロ
- 深夜対応の特別手当:ゼロ
- 休日出勤:ほぼゼロ
見えない効果も含めると...
- 離職率低下(採用コスト削減)
- 開発スピード向上(売上増加)
- 顧客満足度向上(解約率低下)
半年後の経営会議: 社長:「もっと早く導入すべきだった」
にやりと笑ってしまいました。
チームの笑顔が増えた(これが一番の成果)
金曜日の夕方のSlackを見返すと、変化は明らか。
1年前: 「今日もデプロイか...」 「pizza注文する?」 「月曜日まで祈ってます」
今: 「今週もお疲れ様!」 「飲み行く?」 「良い週末を!」
新入社員の感想が印象的でした: 「前職では金曜デプロイ禁止だったんです。ここは金曜日も普通にリリースしてて、最初はビビりました。でも、全然問題ないんですね」
採用面接でよく聞かれること: 「本当に定時で帰れるんですか?」 「はい、CI/CDのおかげで」
この会話ができるようになったのが、地味に嬉しいです。
まとめ:「もう戻れない」世界へようこそ
先日、3年前の日記を見つけました。
「また金曜の徹夜。いつまでこんな生活続けるんだろう」
今の私がタイムマシンで過去に戻れるなら、真っ先に伝えたい。 「3年後、君は金曜の夕方にデプロイして飲みに行ってるよ」って。
本当に変わったこと:
仕事が「作業」から「創造」に変わりました。
デプロイ作業に使っていた時間で:
- 新しい技術の勉強会を開催
- お客様の要望にすぐ対応
- チームでアイデアを議論
- そして何より、定時に帰宅
後輩エンジニアに言われた一言:
「先輩、なんでそんなに楽しそうに仕事してるんですか?」
そうか、楽しそうに見えるのか。 確かに、デプロイのストレスがなくなってから、純粋にコードを書くのが楽しくなった。
あなたへのメッセージ:
もし今、金曜日のデプロイを恐れているなら。 深夜対応で疲れ果てているなら。 「これが当たり前」と諦めているなら。
大丈夫、必ず変われます。
私たちも最初は「無理」と思ってました。 でも、小さな一歩から始めて、今ではもう戻れない世界にいます。
最初の一歩は「Hello World」をビルドするだけでいい。 そこから、あなたの「デプロイ天国」が始まります。
エンハンスド株式会社では、その最初の一歩を一緒に踏み出すお手伝いをしています。3年前の私たちと同じ悩みを持つあなたに、「もう戻れない」世界をお見せします。
参考リンク
- Azure DevOps 公式ドキュメント
- Azure Pipelines YAML スキーマ
- Azure DevOps Labs
- DevOps 評価ツール
- Azure DevOps デモジェネレーター
執筆者: エンハンスド株式会社 DevOpsチーム
公開日: 2025年5月1日
カテゴリ: DevOps, CI/CD, 自動化
タグ: #AzureDevOps #CICD #自動化 #デプロイ自動化 #DevOps #パイプライン