AnsibleをGitHub Actionsに組み込む設計入門|SSH SecretsとWorkflowでPush→自動デプロイを実現する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > AnsibleをGitHub Actionsに組み込む設計入門|SSH SecretsとWorkflowでPush→自動デプロイを実現する方法
「AnsibleのPlaybookをGitで管理しているのに、本番への反映は毎回手元から手動実行している」
「コードレビューを通ったPlaybookがどの環境に入っているのかが、いつも把握できていない」

Ansibleでサーバー構成を自動化した後に訪れるのが、この「実行管理」の課題です。どれほど精密なPlaybookを書いても、実行タイミングや担当者の手作業に依存していると、環境のズレ・適用漏れ・「どのバージョンが本番に入っているか分からない」問題が繰り返し発生します。

この記事では、AnsibleをGitHub Actionsのワークフローに組み込んで「gitのPushを契機にLinuxサーバーへ自動デプロイする」仕組みを解説します。SSHキーのSecrets管理・known_hostsの安全な設定・ワークフローファイルの設計から、ステージング/本番の環境別デプロイ設計まで、実際のコードで説明します。

実行環境: Rocky Linux 9.4(管理対象ノード)、Ansible 2.16、GitHub Actions(ubuntu-24.04)

この記事のポイント

・GitHubリポジトリへのPushを契機にAnsibleを自動実行できる
・SSHキーはBase64エンコードしてGitHub Secretsで安全に管理する
・GitHub-hostedとself-hostedランナーの選定が設計の核心
・GitHub Environmentsでstaging→production承認フローを実現できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

手動実行が抱える限界と自動化の必要性

Ansibleは「コードとして管理できる構成管理ツール」ですが、実行タイミングを人手に委ねていると次の問題が発生します。

・Gitにマージされた変更が本番に反映されないまま放置される
・誰がいつどの環境に対して実行したのかがログに残らない
・「実行忘れ」による本番/ステージング環境のズレが蓄積する
・複数のPlaybookを順序どおりに実行する手順が属人管理になる

GitHub ActionsにAnsibleの実行を委ねることで、これらをまとめて解消できます。Pushのたびに自動実行・ログ記録・成否通知まで一貫して管理できる状態が作れます。

「Playbookを書くことがゴールではなく、それが確実に適用されることがゴール」という観点で、CI/CDへの組み込みはAnsible活用の自然な次のステップです。

GitHub ActionsとAnsibleの連携設計(全体像)

GitHub ActionsからAnsibleを実行する構成は、大きく2つのパターンがあります。

1. パターンA:GitHub-hostedランナー

GitHubが提供するクラウド上のランナー(ubuntu-24.04等)にAnsibleをインストールし、外部のLinuxサーバーへSSH越しにPlaybookを実行します。

GitHub Push │ ▼ GitHub-hostedランナー(ubuntu-24.04) │ pip install ansible-core │ SSH鍵をSecretsから展開 ▼ SSH(port 22) │ ▼ 管理対象ノード(Rocky Linux 9.4) Playbookによる構成変更

・メリット: 管理インフラの追加が不要。ランナーのメンテナンス不要。
・デメリット: SSH接続元がGitHubのIP帯になる。ファイアウォールをGitHub MetaのIPレンジに開放する必要がある。

2. パターンB:self-hostedランナー

自社のコントロールノード(Ansibleをインストール済みのLinuxサーバー)にGitHub Actionsランナーを設置します。

GitHub Push │ ▼ 自社コントロールノード(GitHub Actionsランナー兼Ansibleコントロール) │ 既存のAnsible環境をそのまま使用 ▼ SSH(内部ネットワーク) │ ▼ 管理対象ノード(Rocky Linux 9.4)

・メリット: SSH接続元が固定IP(自社サーバー)になる。ファイアウォール制御が容易。
・デメリット: ランナー自体の管理が追加で発生する。

プライベートクラウド・プライベートVPC環境ではパターンBが現実的です。管理対象ノードがパブリッククラウド上にあり外部SSH可能ならパターンAがシンプルです。

SSHキーをGitHub Secretsで安全に管理する手順

GitHub ActionsからSSHでAnsible管理対象ノードへ接続するには、SSH秘密鍵をワークフローに安全に渡す必要があります。

1. Ansible専用のSSHキーペアを生成する

管理対象ノードへの接続に使うSSHキーは、個人鍵と分けて「Ansible CI/CD専用」で生成することを強く推奨します。鍵を分けることで、ローテーション・無効化の影響範囲を限定できます。

# Ansible GitHub Actions用のSSHキーペアを生成(パスフレーズなし・ed25519推奨) $ ssh-keygen -t ed25519 -C "ansible-github-actions" -f ~/.ssh/id_ansible_gha -N "" Generating public/private ed25519 key pair. Your identification has been saved in /home/ops/.ssh/id_ansible_gha Your public key has been saved in /home/ops/.ssh/id_ansible_gha.pub The key fingerprint is: SHA256:xE7T9mKrD2qNpLb3cFvH8sWo6iYjAeZu1XnR4gTd0kM ansible-github-actions

生成した公開鍵を管理対象ノードへ配布します。

# 管理対象ノードへ公開鍵を配布 $ ssh-copy-id -i ~/.ssh/id_ansible_gha.pub ansible-user@192.0.2.10 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ops/.ssh/id_ansible_gha.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s) Number of key(s) added: 1

2. 秘密鍵をBase64エンコードしてGitHub Secretsに登録する

秘密鍵をそのままGitHub Secretsに貼り付けると、改行が失われて正しく展開できない場合があります。Base64エンコードして1行にまとめるのが確実な方法です。

# 秘密鍵をBase64エンコード(改行なしで出力) $ base64 -w 0 ~/.ssh/id_ansible_gha LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQ...(省略)

出力文字列をコピーして、GitHubリポジトリの `Settings > Secrets and variables > Actions` から `ANSIBLE_SSH_KEY` として登録します。同様に管理対象ホストのIPアドレスを `TARGET_HOST` として登録しておきます。

GitHub Actionsワークフローファイルの設計と書き方

リポジトリのルートに `.github/workflows/deploy.yml` を作成します。

name: Ansible Deploy on: push: branches: - main jobs: deploy: runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Ansible run: | pip install --quiet "ansible-core==2.16.*" - name: Setup SSH key env: SSH_PRIVATE_KEY: ${{ secrets.ANSIBLE_SSH_KEY }} run: | mkdir -p ~/.ssh chmod 700 ~/.ssh echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 - name: Setup known_hosts run: | ssh-keyscan -H "${{ secrets.TARGET_HOST }}" >> ~/.ssh/known_hosts - name: Run Ansible Playbook run: | ansible-playbook \ -i inventory/production.ini \ -u ansible-user \ --private-key ~/.ssh/id_ed25519 \ site.yml

このワークフローを `main` ブランチにプッシュすると、GitHub Actionsが起動します。`Run Ansible Playbook` ステップで次のような出力が得られます。

PLAY [webservers] ************************************************************** TASK [Gathering Facts] ********************************************************* ok: [192.0.2.10] TASK [nginx : Install nginx] *************************************************** ok: [192.0.2.10] TASK [nginx : Deploy nginx config] ********************************************* changed: [192.0.2.10] RUNNING HANDLER [nginx : restart nginx] **************************************** changed: [192.0.2.10] PLAY RECAP ********************************************************************* 192.0.2.10 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

`failed=0` であればデプロイ成功です。`ansible-playbook` がゼロ以外の終了コードを返した場合、GitHub Actionsはワークフローを自動で失敗扱いにして通知を出します。

GitHub-hostedランナーとself-hostedランナーの使い分け

どちらを選ぶかは以下の基準で判断します。
判断軸 GitHub-hosted self-hosted
管理対象ノードの場所 パブリッククラウド・外部SSH可能 オンプレミス・プライベートVPC
SSH接続元IP GitHubのIPレンジ(変動あり) 固定IP(自社サーバー)
ランナー管理コスト 不要 自社で管理が必要
Ansible環境 毎回pip installで用意 既存環境をそのまま利用
self-hostedランナーをコントロールノードに設置する場合、GitHub UIの `Settings > Actions > Runners > New self-hosted runner` の手順に従ってインストールします。

# Rocky Linux 9.4 上でのself-hostedランナーセットアップ例 $ mkdir actions-runner && cd actions-runner $ curl -o actions-runner-linux-x64-2.319.1.tar.gz -L \ https://github.com/actions/runner/releases/download/v2.319.1/actions-runner-linux-x64-2.319.1.tar.gz $ tar xzf ./actions-runner-linux-x64-2.319.1.tar.gz $ ./config.sh --url https://github.com/ORG/REPO --token AXXXXXXX $ sudo ./svc.sh install $ sudo ./svc.sh start # サービス状態を確認 $ sudo ./svc.sh status * actions.runner.ORG-REPO.hostname.service - GitHub Actions Runner (ORG-REPO.hostname) Loaded: loaded (/etc/systemd/system/actions.runner.ORG-REPO.hostname.service; enabled) Active: active (running)

self-hostedランナーを使う場合は、ワークフローの `runs-on` を次のように変えるだけです。

jobs: deploy: runs-on: self-hosted

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

ステージング・本番を分けた環境別デプロイ設計

mainブランチへのPushでステージングに自動デプロイし、本番への反映はタグ付きリリース+手動承認を必要とする設計が現場でよく使われます。

name: Ansible Deploy on: push: branches: - main # ステージングへ自動デプロイ tags: - 'v*' # 本番へのデプロイ(タグ付きリリース) jobs: deploy-staging: if: github.ref_type == 'branch' runs-on: ubuntu-24.04 environment: staging steps: - uses: actions/checkout@v4 - name: Setup SSH key env: SSH_PRIVATE_KEY: ${{ secrets.ANSIBLE_SSH_KEY }} run: | mkdir -p ~/.ssh && chmod 700 ~/.ssh echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H "${{ secrets.STAGING_HOST }}" >> ~/.ssh/known_hosts - name: Install Ansible run: pip install --quiet "ansible-core==2.16.*" - name: Deploy to staging run: | ansible-playbook -i inventory/staging.ini -u ansible-user \ --private-key ~/.ssh/id_ed25519 site.yml deploy-production: if: github.ref_type == 'tag' runs-on: ubuntu-24.04 environment: production # GitHub Environmentsで手動承認を設定 steps: - uses: actions/checkout@v4 - name: Setup SSH key env: SSH_PRIVATE_KEY: ${{ secrets.ANSIBLE_SSH_KEY }} run: | mkdir -p ~/.ssh && chmod 700 ~/.ssh echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H "${{ secrets.PRODUCTION_HOST }}" >> ~/.ssh/known_hosts - name: Install Ansible run: pip install --quiet "ansible-core==2.16.*" - name: Deploy to production run: | ansible-playbook -i inventory/production.ini -u ansible-user \ --private-key ~/.ssh/id_ed25519 site.yml

GitHub Environments(リポジトリの `Settings > Environments`)で `production` 環境に `Required reviewers` を設定すると、タグPushに対してレビュアーの承認が下りるまで本番デプロイは実行されません。コードの変更から本番適用までに「人の目」を挟む承認ゲートが、ワークフロー設定だけで実現できます。

よくあるエラーと対処法

【エラー1】Host key verification failed.

known_hostsにホストが登録されていない場合に発生します。`ssh-keyscan` のステップがSSH接続より前に配置されているか確認します。

UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Host key verification failed.", "unreachable": true}

対処: `ssh-keyscan` を必ずSSH接続より前のステップに配置すること。本番運用ではknown_hostsファイルをリポジトリに含めて管理し、動的スキャンを避ける設計も推奨されます。

【エラー2】Permission denied (publickey)

秘密鍵のデコードに失敗しているか、公開鍵が管理対象ノードの `~/.ssh/authorized_keys` に入っていない場合に発生します。

# ローカルでBase64デコードを検証(先頭行の確認) $ cat ~/.ssh/id_ansible_gha | base64 -w 0 | base64 -d | head -1 -----BEGIN OPENSSH PRIVATE KEY-----

先頭行が `-----BEGIN OPENSSH PRIVATE KEY-----` であれば鍵の形式は正常です。GitHub Secretsに登録した値の末尾に余分な改行が入っていないかも確認します。

【エラー3】Could not connect to target host

ファイアウォールでSSH(port 22)が遮断されている場合に発生します。パターンA(GitHub-hostedランナー)の場合、GitHub MetaのIPレンジ(`curl https://api.github.com/meta` で取得可能)を許可リストに追加します。パターンB(self-hostedランナー)への切り替えも有効な対処です。

本記事のまとめ

やりたいこと 設計ポイント
GitHub Pushで自動デプロイ on.push.branches でブランチを指定する
SSH秘密鍵の安全な受け渡し base64エンコードしてGitHub Secretsに登録する
known_hostsの事前設定 ssh-keyscan をSSH接続より前のステップで実行する
ステージング/本番の分離 branchとtagでjobを分け、GitHub Environmentsで制御する
外部アクセス不要の構成 self-hostedランナーをコントロールノードに設置する
本番デプロイの承認制御 GitHub EnvironmentsのRequired reviewers機能を使う
AnsibleをGitHub Actionsに組み込むと、Playbookの変更が確実に適用される「仕組みとしての構成管理」が実現します。手動実行の属人性を排除し、ログ・通知・承認フローを一元管理することで、チームの規模が変わっても安定した運用が続けられます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。