1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

インフラ作業をシフトレフト! Ansible × Packer で断ち切る「お祈りデプロイ」

1
Posted at

この記事は、 HashiCorp Japan Advent Calendar 2025 の 5 日目の投稿です。

はじめに:その apt-get update、いつまで待ちます?

ところで皆様、あなたのそのデプロイフロー、 「お祈り」 が含まれていませんか?(ドーン)

Auto Scaling でインスタンスが立ち上がるたび、あるいはデプロイのたびに走る Ansible の Playbook。

「外部リポジトリが落ちてたらどうしよう」
「依存パッケージのバージョンが勝手に上がってたらどうしよう」

そんな不安を抱えながら、黒い画面に流れる CHANGED のログを眺める時間は、人生の無駄遣いです。

「Ansible のそのコード、Packer で焼いちゃえばよくない?」

今日は、構成管理のタイミングを「デプロイ時」から「ビルド時」にずらすだけで、人類がいかに幸せになれるかについて語ります。


1. Ansible と Packer:役割は「何」かではなく「いつ」か

よくある誤解ですが、Ansible と Packer は敵対するものではありません。むしろ「最高のパートナー」です。

まず強調しておきたいのは、Ansible というツールの素晴らしさです。サーバーの中身を定義する「構成管理(Configuration Management)」において、Ansible は依然として最強のツールの一つです。

  • 人間が読める「宣言的」なコード:
    複雑な設定も YAML で書けるため、泥臭いシェルスクリプト(sedawk の嵐)と違って、誰が見ても「何をしているか」が一目瞭然です。
  • 巨人の肩に乗れる「モジュール群」:
    user, yum, service, template など、OS の差異を吸収してくれるモジュールが豊富にあり、車輪の再発明を防げます。
  • 資産の再利用性:
    Role という単位で機能がモジュール化されており、他のプロジェクトでも使い回せます。

「Ansible は最高です。 ただし、それを『いつ』実行するかが問題なのです。

現代のクラウド運用において、「本番環境でサーバーが起動してから ansible-playbook を流す」 という運用(Runtime Configuration)は、アンチパターンになりつつあります。

Runtime Configuration (従来の Ansible 運用):

  • サーバー起動後に yum installgit clone をする。
  • リスク: 起動が遅い。外部要因(リポジトリダウン等)でコケる。サーバーごとに微妙に状態がズレる (Configuration Drift)。

Golden Image (Packer 運用):

  • Ansible の記述力でサーバーの中身を完璧に作り込み、それを「マシンイメージ(AMI など)」として固めておく。
  • 起動時はイメージを展開するだけ。
  • メリット: 爆速起動。100% の再現性。イミュータブル(不変)。

つまり、 「Ansible を捨てる」のではなく、「Ansible の強力な表現力を Packer の中で活かし、成果物だけを本番に持っていく」 のが正解です。


2. 実践:その「汚れちまった起動スクリプト」を供養する

「Packer 化する」といっても、Ansible の Playbook を全部捨てて HCL (HashiCorp Configuration Language) に書き直すわけではありません。
「Ansible を起動するためだけの、メンテナンス不可能な User Data (シェルスクリプト)」を捨てる のです。

具体的なビフォーアフターを見てみましょう。

Before:本番で震えながら走る User Data

EC2 の User Data や、起動テンプレートにこんなシェルスクリプト書いてませんか? これが「アンチパターン」の正体です。

#!/bin/bash
# ----------------------------------------------
# 恐怖のランタイムセットアップ
# ----------------------------------------------

# 1. そもそも Ansible を入れるところから始まる(遅い)
yum update -y
amazon-linux-extras install ansible2 -y

# 2. Playbook をどこかから落としてくる(権限周りでよく止まる)
# S3 や Git の認証情報が通るか、毎回ドキドキする瞬間
aws s3 cp s3://my-infra-bucket/playbooks/site.yml /tmp/site.yml
aws s3 cp s3://my-infra-bucket/roles /tmp/roles --recursive

# 3. 実行(ここでコケたら、このインスタンスは無用の長物に・・・)
ansible-playbook /tmp/site.yml -e "env=production" >> /var/log/ansible.log

# 4. ログ監視エージェントなどの再起動(Ansible でやったのに念のため再起動とか・・・)
systemctl restart amazon-cloudwatch-agent

このコードの罪深い点は、「インスタンスが起動するたびに、インターネットへの接続、リポジトリの生存、パッケージのバージョン整合性、全てが成功しないといけない」 という点です。悔い改める必要がありそうですね。

After:平穏をもたらす Packer 定義

これを Packer に書き換えると、こうなります。Ansible Playbook そのものは そのまま 使えます。

# build.pkr.hcl

build {
  sources = ["source.amazon-ebs.app"]

  # ステップ 1: Ansible 自体のインストール
  # 「Ansible を実行するための環境」はシェルスクリプトでサクッと作る
  provisioner "shell" {
    inline = [
      "sudo amazon-linux-extras install ansible2 -y"
    ]
  }

  # ステップ 2: Ansible の実行
  # ローカルにある Playbook をインスタンスに転送して実行してくれる
  # S3 から落とす必要も、Git clone する必要もない!
  provisioner "ansible-local" {
    playbook_file   = "./ansible/site.yml"
    role_paths      = ["./ansible/roles"]

    # 変数もここで注入。ビルド時に確定させる。
    extra_arguments = [
      "--extra-vars", "env=production build_id=${var.build_id}"
    ]
  }
}

どうですか? 「ファイルをダウンロードする処理」が消えました。
Packer がローカル( CI 環境など )にある Playbook を一時的にインスタンスへアップロードし、実行してくれるからです。便利ですね!

Tips:Ansible の「過剰品質」を Shell で削ぎ落とす

さらに、Packer 化のついでに 「たったこれだけのために Ansible 書いてたの?」 という部分を、Packer の機能に書き換えて断捨離しましょう。

例 1:ファイル配置だけの Task のために Ansible を呼び出す必要はない

  • Ansible (Before): copy モジュールを使うためだけに Task 定義...
- name: Config file copy
  copy:
    src: files/config.toml
    dest: /etc/myapp/config.toml

  • Packer (After): file プロビジョナーとして組み込み統一感を出す
provisioner "file" {
  source      = "./files/config.toml"
  destination = "/tmp/config.toml" # 一旦 tmp に置いて
}
provisioner "shell" {
  inline = ["sudo mv /tmp/config.toml /etc/myapp/"] # sudo で移動
}

例 2:単純なコマンド実行で冪等性を考慮してあれこれ考える必要はない

  • Ansible (Before): shell モジュールで実行。冪等性担保のために creates とか書き出すと面倒。
  • Packer (After): Packer は「一度ビルドしたら終わり(次回実行時はまた更地から)」なので、冪等性を気にする必要がありません。 ガンガン shell プロビジョナーで書いて OK です。
provisioner "shell" {
  inline = [
    "curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash -",
    "sudo yum install -y nodejs"
  ]
}

「Ansible の冪等性」は神機能ですが、Golden Image ビルドにおいては 「毎回まっさらな OS で 1 回だけ実行される」 ので、実はシェルスクリプトで十分なケースが多いのです。これに気づくと、Ansible コードはもっとシンプルになります。


3. シフトレフトの実装:エラーは「今」見つける

「シフトレフト」とは、せっかくなのでかっこいい言葉を使いたいだけ難しい概念ではありません。 「絶望するタイミングを前倒しする」 ということです。

具体的な恩恵:外部リポジトリダウンの悲劇

Ansible 実行型:

  • 深夜 2 時のオートスケール時、npm install 先のリポジトリが応答なし。
  • 結果: 新規インスタンスが起動失敗。サービスダウン。あなたは電話で叩き起こされる。[例の音声]:"Hello, this is P***y. You have ONE triggered incident on service 'Production-API'. Press 4 to acknowledge..."

Packer ビルド型 (シフトレフト):

  • 昼間の CI パイプライン(GitHub Actions 等)での AMI ビルド中、リポジトリが応答なし。
  • 結果: ビルドが失敗して赤くなるだけ。本番環境は**「最後に成功した正常な AMI」**で稼働し続ける。
  • あなたの対応: 「あ、ビルドこけたわ。後で見よ」と言って優雅にコーヒーを飲むひととき。

CI への組み込みでシフトレフトを実現する

それこそ単純な話ですが、GitHub Actions で Packer を回しましょう。

  1. Pull Request 作成
  2. packer validate & ansible-lint 実行
  3. マージ後、packer build 実行 → AMI 作成
  4. Terraform などが新しい AMI ID を参照してデプロイ

これで、インフラのバグはデプロイ前(ビルドフェーズ)に検知されます。これが本当のシフトレフトです。

リポジトリ内の、 .git/workflows にいれる packer.yml のサンプルはこちらです。

ポイントは、PR の段階では重たい build (AMI 作成) を走らせないこと です。これにより、開発者は安心してコードをプッシュでき、CI の完了を待つ時間も短縮されます。

Sample の yml はこちら
name: Packer Build Pipeline

on:
  push:
    branches:
      - main  # マージされたら焼く
  pull_request:
    branches:
      - main  # PR 中は構文チェックのみ

jobs:
  # ---------------------------------------------------
  # 1. 検査フェーズ (Shift Left)
  # PR のたびに走り、構文エラーやベストプラクティス違反を即検知する
  # ---------------------------------------------------
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Ansible (for linting)
        run: pip install ansible ansible-lint

      - name: Setup Packer
        uses: hashicorp/setup-packer@main
        with:
          version: "latest"

      # Ansible の構文チェック
      - name: Run ansible-lint
        run: ansible-lint ./ansible/site.yml

      # Packer の構文チェック
      - name: Run packer init & validate
        run: |
          packer init .
          packer validate .

  # ---------------------------------------------------
  # 2. ビルドフェーズ (Golden Image 生成)
  # main ブランチにマージされた時だけ実行される
  # ---------------------------------------------------
  build:
    needs: validate
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      id-token: write # AWS OIDC 認証用
      contents: read
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # AWS 認証 (OIDC 推奨だが、Secrets でも可)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
          # または単純に Access Key を使う場合:
          # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Setup Packer
        uses: hashicorp/setup-packer@main
        with:
          version: "latest"

      - name: Run packer init
        run: packer init .

      # いざ、焼く!
      - name: Build AMI
        run: packer build .



Sample コードのポイントをいくつかご紹介します。

1.ansible-lint の導入:

  • 「Packer は通ったけど、中で動く Ansible がインデントミスで死んだ」という悲劇を防ぐために必須です。

2.needs: validate:

  • 構文チェックが通らない限り、ビルドフェーズには進ませません。無駄な AWS リソースの浪費を防ぎます。

3.if: github.event_name == 'push':

  • ここがキモです。PR の段階で毎回 AMI を作っていたら、ビルド待ち時間で開発が止まってしまいます。「検査は早く、ビルドは慎重に」のメリハリをつけます。

4. Golden Image 運用へ切り替える時の「アプリ実装」の注意点

さて、ここまではインフラの話ですが、アプリアーキテクティングにも釘を刺しておく必要があります。
Golden Image 運用にするということは、「サーバーは使い捨て (Immutable)」 になるということです。

以下のコードが含まれているアプリケーションは、Packer 化と同時にアーキテクチャ(というか、考え方自体)の修正が必要です。

❌ 1. ログをローカルファイルに出力して満足している

# ダメな例
logging.basicConfig(filename='/var/log/myapp/app.log')

サーバーが入れ替わるとログも消えます。
対策: CloudWatch Logs, Datadog, Fluentd などを使い、ログは標準出力に吐くか、即座に外部へ転送してください。

❌ 2. アップロードファイルをローカルディスクに保存している

// ダメな例
move_uploaded_file($tmp, "/var/www/uploads/" . $filename);

次のデプロイでそのインスタンスが破棄された瞬間、ユーザーの画像は消滅します。
対策: S3 や GCS などのオブジェクトストレージに直接保存してください。

❌ 3. IP アドレスやホスト名に依存している

「DB の許可リストに Web サーバーの IP 書いたから OK」
対策: Auto Scaling で IP はコロコロ変わります。Security Group や IAM Role ベースの認証に切り替えてください。


まとめ:安眠のために焼こう

Ansible は素晴らしいツールです。記述性が高く、冪等性も担保してくれます。
しかし、それを 「いつ実行するか」 で、運用の質は天と地ほど変わります。

  • 構成管理のコードはそのまま資産として使う。
  • 実行場所を「本番サーバーの起動時」から「CI 上の Packer ビルド時」に移す。
  • アプリをステートレスにする。

これだけで、デプロイ時間は短縮され、障害耐性は上がり、何より 「デプロイボタンを押す時の手の震え」 が止まります。

さあ、その user_data を削除して、.pkr.hcl を書き始めましょう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?