AnsibleとJenkinsの統合: CI/CDパイプラインの自動化
AnsibleとJenkinsを統合して、CI/CDパイプラインからプレイブックを実行し、SSH認証情報を管理し、一貫したデプロイを実現します。
AnsibleとJenkinsの統合:CI/CDパイプラインの自動化
AnsibleとJenkinsを統合することで、CI/CDパイプラインがアーティファクトをビルドし、テストを実行し、Jenkins外部で使用するのと同じプレイブックでデプロイできるようになります。主な課題は、パイプラインをシェルコマンドの山に変えずに、認証情報、インベントリ、プレイブックの実行を配線することです。
このガイドでは、実践的なセットアップを紹介します。Jenkinsがパイプラインを調整し、Ansibleがターゲットホストへのデプロイと設定を担当します。例ではSSHベースのLinuxデプロイを想定していますが、同じパターンは環境固有のインベントリやロールでも機能します。
JenkinsとAnsibleの連携方法
パイプラインを書く前に、責任を分離します:
- Jenkins: コードのチェックアウト、アーティファクトのビルド、テストの実行、ログの収集、デプロイメントステージの実行タイミングを決定します。
- Ansible: ターゲットホストへの接続、アーティファクトのコピー、設定の書き込み、サービスの管理、デプロイメントステップの冪等性を維持します。
JenkinsとAnsibleを統合する理由
この分割により、チームに明確な境界が生まれます:
- Jenkinsはリリースフローを
Jenkinsfileに保存します。 - Ansibleはインフラストラクチャアクションをプレイブックとロールに保存します。
- インベントリファイルが、ステージング、本番、その他の環境にどのホストが属するかを決定します。
- Jenkinsの認証情報がSSHキー、Vaultパスワード、トークンを保護します。
統合の前提条件
始める前に、以下が揃っていることを確認してください:
- 動作するJenkinsコントローラーと、デプロイメントジョブを実行できる少なくとも1つのエージェント。
ansible-playbookを実行するJenkinsエージェントにAnsibleがインストールされていること。- そのエージェントからSSH経由で到達可能なターゲットマシン。
- 自動化専用のSSHキーペア。
- バージョン管理下にあるアプリケーションコード、インベントリ、プレイブック、ロール。
JenkinsのAnsible対応設定
JenkinsがAnsibleを安全に実行するには、エージェント上のAnsibleランタイムとターゲットホストの認証情報の2つが必要です。
1. Ansibleプラグインのインストール
シェルステップから直接ansible-playbookを呼び出すこともできますが、Jenkins Ansibleプラグインを使用すると、設定と出力の管理が容易になります。
Manage Jenkins>Manage Pluginsに移動します。Availableタブに移動し、「Ansible」を検索します。- 「Ansible」プラグインを選択し、
Install without restartまたはDownload now and install after restartをクリックします。
2. SSH認証情報の設定
Ansibleはターゲットサーバーへの接続にSSHを使用します。秘密鍵はリポジトリではなく、Jenkinsの認証情報に保存します。
Manage Jenkins>Manage Credentialsに移動します。- ジョブで使用する認証情報ストアを選択します。
Add Credentialsをクリックします。SSH Username with private keyを選択します。ansible-ssh-keyのような安定したIDを設定します。- ターゲットマシンで使用するSSHユーザー名を入力します。
- 秘密鍵を追加し、認証情報を保存します。
ヒント:キー管理のベストプラクティス
- SSH秘密鍵をJenkinsfile、インベントリ、プレイブックにハードコードしないでください。
- 個人のキーではなく、専用の自動化キーを使用してください。
- チームのセキュリティポリシーに合わせたスケジュールでキーをローテーションしてください。
Ansibleを使用したJenkinsパイプラインの設計
デプロイメントロジックがアプリケーションコードと一緒にレビューされるように、Jenkinsfileで宣言的パイプラインを使用します。
基本的なパイプライン構造
以下は、ビルド、ステージングデプロイ、承認付き本番デプロイのコンパクトな例です:
// Jenkinsfile
pipeline {
agent any
environment {
// 必要に応じて環境変数を定義
ANSIBLE_HOST_KEY_CHECKING = 'False' // 本番環境では注意。known_hostsを推奨
}
stages {
stage('ソースのチェックアウト') {
steps {
git 'https://your-scm-url/your-repo.git'
}
}
stage('アプリケーションのビルド') {
// このステージではJAR、WAR、Dockerイメージなどをビルドする可能性があります
// 例:Spring Boot JARのビルド
steps {
sh 'mvn clean package'
}
}
stage('単体テストの実行') {
steps {
sh 'mvn test'
}
}
stage('ステージング環境へのデプロイ') {
steps {
script {
// SSHエージェントを使用して秘密鍵をAnsibleで利用可能にする
sshagent(credentials: ['ansible-ssh-key']) {
// Ansibleプレイブックの実行
sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \
-e "app_version=$(cat target/VERSION)"'
}
}
}
}
stage('本番環境へのデプロイ') {
// このステージでは手動承認が必要になる場合があります
input {
message "本番環境へのデプロイを続行しますか?"
ok "本番環境にデプロイ"
}
steps {
script {
sshagent(credentials: ['ansible-ssh-key']) {
sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \
-e "app_version=$(cat target/VERSION)"'
}
}
}
}
}
post {
always {
echo 'パイプラインが終了しました。'
}
success {
echo 'パイプラインが成功しました!'
// slackSend channel: '#deployments', message: "デプロイ成功: ${env.BUILD_URL}"
}
failure {
echo 'パイプラインが失敗しました!'
// slackSend channel: '#deployments', message: "デプロイ失敗: ${env.BUILD_URL}"
}
}
}
主要なパイプラインの詳細
agent anyはデモ用です。本番環境では、Ansibleがインストールされていることを保証するエージェントラベルを使用してください。sshagent(credentials: ['ansible-ssh-key'])は、そのブロック内でのみ秘密鍵を公開します。ansible-playbook -i inventory/staging.ini playbooks/deploy_app.ymlは、ターゲット環境を明示的に指定します。- 本番環境の
inputブロックは、機密性の高いデプロイの前に手動ゲートを追加します。 - 本番環境ではホストキーチェックを無効にしないでください。代わりに、Jenkinsエージェントで
known_hostsを管理してください。
Ansibleプレイブックの例:Webアプリケーションのデプロイ
以下は、Java Webアプリケーション用の簡略化されたinventory/staging.iniとplaybooks/deploy_app.ymlです。アーティファクト名、サービス管理、パスは異なる場合があります。
inventory/staging.ini
[web_servers]
web1.example.com
web2.example.com
[database_servers]
db1.example.com
[all:vars]
ansible_user=ubuntu
playbooks/deploy_app.yml
---
- name: Webアプリケーションのデプロイ
hosts: web_servers
become: yes
vars:
app_name: my-webapp
app_path: /opt/{{ app_name }}
app_port: 8080
app_version: "{{ app_version | default('1.0.0') }}"
tasks:
- name: アプリケーションディレクトリの存在確認
ansible.builtin.file:
path: "{{ app_path }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: アプリケーションJARをターゲットにコピー
ansible.builtin.copy:
src: "target/{{ app_name }}-{{ app_version }}.jar"
dest: "{{ app_path }}/{{ app_name }}.jar"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
notify: restart app service
- name: systemdサービスファイルの存在確認
ansible.builtin.template:
src: templates/my-webapp.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
owner: root
group: root
mode: '0644'
notify: restart app service
- name: アプリケーションサービスの起動と有効化
ansible.builtin.systemd:
name: "{{ app_name }}"
state: started
enabled: yes
handlers:
- name: restart app service
ansible.builtin.systemd:
name: "{{ app_name }}"
state: restarted
daemon_reload: yes
templates/my-webapp.service.j2(Systemdサービステンプレート)
[Unit]
Description={{ app_name }} Application
After=network.target
[Service]
User={{ ansible_user }}
ExecStart=/usr/bin/java -jar {{ app_path }}/{{ app_name }}.jar --server.port={{ app_port }}
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
このプレイブックは、アプリケーションディレクトリを作成し、ビルドされたJARをコピーし、systemdユニットを書き込み、サービスを開始します。ハンドラーは、アーティファクトまたはユニットファイルが変更された場合にのみサービスを再起動します。
ベストプラクティスとヒント
- プレイブックを冪等に保ち、失敗したパイプラインを再実行しても追加の損害が発生しないようにします。
- VaultパスワードをJenkinsの認証情報に保存し、一時的なパスワードファイルまたは承認された認証情報バインディングを介して渡します。
- デプロイメントロジックを
webserver、database、app_deployなどのロールに整理します。 - ステージングと本番環境には、個別のインベントリまたは
group_varsを使用します。 - コントローラーではなく、専用のJenkinsエージェントでAnsibleを実行します。
- 詳細が必要な場合のみ
-vまたは-vvを使用し、ログに機密情報が漏洩しないようにします。 - Jenkinsで実行する前に、特に本番ロールのプレイブックをテストします。
専門家に相談すべきタイミング
パイプラインが本番環境にデプロイする場合、共有認証情報を管理する場合、多くのホストに書き込む場合、または監査制御が必要な場合は、Jenkinsまたはプラットフォーム管理者を招いてください。認証情報の処理、ホストキーの検証、ロールバック動作は、初回の本番実行前に再レビューする価値があります。
まとめ
Jenkinsをオーケストレーションに、Ansibleをデプロイメント状態に使用します。認証情報はJenkinsに、プレイブックはバージョン管理に、インベントリは明示的に、そして本番デプロイを自動化する前に、パイプライン外で同じAnsibleコマンドをテストしてください。