あらかじめファイルなどで定義しておいた内容に従ってサーバーの設定やソフトウェアのインストールなどを自動実行する「構成管理自動化ツール」分野ではAnsiblePuppetChefといったツールが有名だが、そんな中昨年10月に新たな自動化ツール「Puppet Bolt」がリリースされた。Puppet BoltはSSHなどのリモートログイン機能を使って対象とするマシンを操作するのが特徴で、管理対象のマシンにエージェントをインストールすることなく利用できる点がアピールされている。本記事ではこのPuppet Boltの機能、導入方法、使用例を紹介する。

シンプルな構成管理ツールをうたう新ツール「Bolt」

ソフトウェアのインストールや構成管理を自動化する構成管理ツールとしては、PuppetやChef、Ansibleといったものが存在する。かつてはPuppetやChefが人気だったが、最近ではAnsibleの人気が高まっているようだ。TechRepublicの調査結果によると、2019年の調査で最も利用者が多かった構成管理ツールはAnsibleだったそうだ。

PuppetやChefでは管理対象のマシンに「エージェント」などと呼ばれる管理用のソフトウェアをインストールする必要があったが、Ansibleではエージェントのインストールが不要で、設定ファイルがシンプルで簡潔に記述できるなど、利用するための準備や手間が小さい点が評価されている。こうした状況を受けてか、Puppetを提供するPuppet社は2018年10月、新たな構成管理ツール「Puppet Bolt」(以下、Bolt)をリリースした。BoltのWebサイトでは、その特徴としてシンプルさが挙げられている(図1)。

図1 BoltのWebサイト
図1 BoltのWebサイト

これによると、BoltはPuppetに関する知識が無くても利用でき、また管理対象マシンへのエージェントの事前インストールも不要とされている。LinuxだけでなくWindowsやmacOSにも対応しているのも特徴だ。

そこで本記事では、この新しいツールであるBoltについて、Linux環境でのインストールや各種設定、基本的な使い方の紹介や、使い勝手についての検証を行っていく。

Boltのインストール

BoltはRuby言語で実装されており、そのソースコードはオープンソースで公開されている(GitHubで公開されているBoltのリポジトリ)。前述のとおり対応プラットフォームはLinuxおよびmacOS、Windowsで、Rubyはバージョン2.3以上が必須となっている。

Boltのドキュメントでは、RubyおよびBoltを個別にインストールするのではなく、Bolt本体に加えて使用するRubyや各種ライブラリなどがすべて含まれているパッケージを利用してインストールすることが推奨されている。現時点ではDebian 8/9、Ubuntu 14.04/16.04/18.04、Red Hat Enterprise Linux(RHEL) 6/7/8およびその互換環境、Fedora 28/29/30などに対応したパッケージがDebおよびYumリポジトリで提供されており、使用するディストリビューションに応じたリポジトリを登録することでapt/apt-getコマンドやyum/dnfコマンドでパッケージをインストールできる。

Debian系ディストリビューションへのインストール

Debian系ディストリビューション向けには、https://apt.puppet.com/というリポジトリが用意されている。このリポジトリで公開されている「puppet6-release-<リリース>.deb」(「<リリース>」部分には使用するディストリビューションのリリース名が入る)というパッケージをインストールすることでリポジトリが利用可能になる。

たとえばUbuntu 18.04.2 LTS(Bionic Beaver)の場合、「puppet6-release-bionic.deb」パッケージをインストールすれば、aptコマンドで「puppet-bolt」パッケージがインストールできるようになる。

$ wget https://apt.puppet.com/puppet6-release-bionic.deb
# dpkg -i puppet6-release-bionic.deb
# apt update
# apt install puppet-bolt

Red Hat系ディストリビューションへのインストール

Red Hat系ディストリビューション向けには、https://yum.puppet.com/というリポジトリが用意されている。このリポジトリで公開されている「puppet6-release-<ディストリビューション名>-<バージョン>.noarch.rpm」というパッケージをインストールすることでリポジトリが利用可能になる。

たとえばRed Hat Enterprise 7系やその互換環境であるCentOS 7系の場合は「puppet6-release-el-7.noarch.rpm」を、Fedora 30の場合は「puppet6-release-fedora-30.noarch.rpm」をインストールすれば良い。

# rpm -ivh https://yum.puppet.com/puppet6-release-el-7.noarch.rpm
# yun install puppet-bilt

いずれの場合も、インストールされたBoltのバージョンは「bolt --version」コマンドで確認できる。

$ bolt --version
1.25.0

環境情報収集のオプトアウト設定

Boltでは、デフォルト設定ではユーザーによる利用状況といったデータをPuppetに送信するようになっている。このデータ送信を止めるには、次のようにしてユーザのホームディレクトリ以下に「.puppetlabs/bolt/analytics.yaml」というファイルを作成し、そこに「disabled: true」と記述しておけば良い。

$ mkdir -p ~/.puppetlabs/bolt
$ echo "disabled: true" > ~/.puppetlabs/bolt/analytics.yaml

なお、このファイルが存在しない場合、Boltの実行時にディレクトリおよびファイルが自動的に作成され、ここにユーザー固有のIDが保存される。

$ cat .puppetlabs/bolt/analytics.yaml
---
user-id: 6e52cd2b-90ba-4ffa-97bb-7910b06cbcf5

各種操作を行うboltコマンド

Boltでは、「bolt」というコマンドを使って各種操作を実行する。boltコマンドは次のように2つ以上の引数を取り、これらで処理対象および処理内容を指定する。

bolt <サブコマンド> <アクション> <オプション>...」

サポートされているサブコマンドやアクションは「-h」オプションで確認できる。

$ bolt -h
Usage: bolt <subcommand> <action>

Available subcommands:
  bolt command run <command>       Run a command remotely
  bolt file upload <src> <dest>    Upload a local file or directory
  bolt script run <script>         Upload a local script and run it remotely
  bolt task show                   Show list of available tasks
  bolt task show <task>            Show documentation for task
  bolt task run <task> [params]    Run a Puppet task
  bolt plan convert <plan_path>    Convert a YAML plan to a Puppet plan
  bolt plan show                   Show list of available plans
  bolt plan show <plan>            Show details for plan
  bolt plan run <plan> [params]    Run a Puppet task plan
  bolt apply <manifest>            Apply Puppet manifest code
  bolt puppetfile install          Install modules from a Puppetfile into a Boltdir
  bolt puppetfile show-modules     List modules available to Bolt
  bolt secret createkeys           Create new encryption keys
  bolt secret encrypt <plaintext>  Encrypt a value
  bolt secret decrypt <encrypted>  Decrypt a value

Run `bolt <subcommand> --help` to view specific examples.
  
  

サブコマンドの詳細や指定できるアクションについては、「bolt <サブコマンド> -h」のようにサブコマンドと一緒に「-h」オプションを指定することで確認できる。

$ bolt command -h
Usage: bolt command <action> <command>

Available actions are:
  run                              Run a command remotely
  
  

利用できるサブコマンドやアクションの一覧については公式ドキュメントのコマンドリファレンスも参照すると良いだろう。

SSHの設定

Boltではリモートログイン機能を使って操作対象のマシンにログインして各種処理を実行する仕組みになっている。そのため、操作対象のマシンに対しリモートログインできるよう事前に設定を行っておく必要がある。

また、Linuxの場合は主としてSSHを利用してリモートログインを行うが、このとき特に設定を行わない限りコマンドの実行時に毎回パスワードやパスフレーズの入力が必要となってしまう。さらに、筆者が検証した限りではboltコマンドの実行時にパスフレーズを入力できないという不具合も存在するようだ。

そのため、操作対象とするサーバーに公開鍵認証でログインできるように設定するとともに、パスフレーズの入力を省略できる「ssh-agent」というツールを利用することをおすすめする。

ssh-agentは、SSHで利用するパスフレーズを記録しておくことでパスフレーズ入力を省略できるようにするツールだ。たとえばシェルとしてbashを使用している場合、次のようにしてssh-agentを起動する。

$ ssh-agent bash

ssh-agentの起動後、ssh-addコマンドを実行するとパスフレーズの入力が求められ、以降はここで入力したパスフレーズがSSHの利用時に自動入力されるようになる。

$ ssh-add
Enter passphrase for /home/hylom/.ssh/id_rsa:
Identity added: /home/hylom/.ssh/id_rsa (/home/hylom/.ssh/id_rsa)

なお、ssh-agentの引数として指定したプログラムが終了すると保持されたパスフレーズはその時点で破棄される。

以下の実行例では、特に言及のない限りこのssh-agentを利用してパスフレーズの入力を省略できるようにした状態でboltコマンドを実行している。

リモートホスト上でコマンドを実行

Boltでは、「command」サブコマンドおよび「run」アクションを使ってリモートホスト上で任意のコマンドを実行できる。

$ bolt command run <実行するコマンド> -n <対象ホスト>

このとき、実行するリモートホストは「-n」(もしくは「--nodes」)オプションで指定する。たとえば、「192.0.2.100」というホストで「echo hoge」というコマンドを実行する場合、次のようになる。

$ bolt command run "echo hoge" -n 192.0.2.100
Started on 192.0.2.100...
Finished on 192.0.2.100:
  STDOUT:
    hoge
Successful on 1 node: 192.0.2.100
Ran on 1 node in 0.33 seconds

このとき、コマンドの実行結果は「STDOUT:」以下に表示される。また、指定したコマンドはリモートログインに使用したユーザーの権限(通常はローカルユーザーと同名のユーザー)で実行される。

$ bolt command run id -n 192.0.2.100
Started on 192.0.2.100...
Finished on 192.0.2.100:
  STDOUT:
    uid=1000(hylom) gid=1000(hylom) groups=1000(hylom)
Successful on 1 node: 192.0.2.100
Ran on 1 node in 0.33 seconds

リモートログインに使用するユーザーを指定したい場合は、ホスト名もしくはIPアドレスの前に「@」を付けて「<ユーザー名>@<ホスト名もしくはIPアドレス」のように指定すれば良い。

なお、Boltではデフォルトでリモートホストのホスト鍵の検証も行うようになっており、一度もリモートログインしていないリモートホストを対象にboltコマンドを実行すると、次のようにホスト鍵認証に失敗してしまう。

$ bolt command run "echo hoge" -n 192.0.2.100
Started on 192.0.2.100...
Failed on 192.0.2.100:
  Host key verification failed for 192.0.2.100: fingerprint SHA256:RWWCsRzzOAZ61TWVqbzPxPUS1UiU0pNH41Dg+TlwGi8 is unknown for "192.0.2.100"
Failed on 1 node: 192.0.2.100
Ran on 1 node in 0.09 seconds

この場合、次のようにsshコマンドで一度対象のリモートホストにログインしてホスト鍵を許可するか、もしくは「--no-host-key-check」オプション付きでboltコマンドを実行すれば良い。

$ ssh 192.0.2.100
The authenticity of host '192.0.2.100 (192.0.2.100)' can't be established.
ECDSA key fingerprint is SHA256:u49rtoHeIz5ixnnmD4BysYcPpXolg2j3glKL8vuTY4s.
Are you sure you want to continue connecting (yes/no)? yes ←そのままEnterキーを押す
Warning: Permanently added '192.0.2.100' (ECDSA) to the list of known hosts.

リモートログインに使用したユーザーとは異なるユーザーでコマンドを実行したい場合は、「--run-as <ユーザー>」オプションを使用する。この場合、リモートホスト上でsudoコマンドが実行され、指定したユーザー権限でコマンドが実行される。sudoコマンドの実行時に必要なパスワードは「--sudo-password」を指定することで入力できる。次の例では、「--run-as root」と指定することでrootユーザーでコマンドを実行している。

$ bolt command run id -n 192.0.2.100 --run-as root --sudo-password
Please enter your privilege escalation password: ←sudoコマンドで使用するパスワードを入力する
Started on 192.0.2.100...
Finished on 192.0.2.100:
  STDOUT:
    uid=0(root) gid=0(root) groups=0(root)
Successful on 1 node: 192.0.2.100
Ran on 1 node in 0.36 seconds

複数のホストを対象にコマンドを実行する

-nオプションではカンマ区切りで複数のノードを同時に指定できる。次の例では、「192.0.2.100」と「192.0.2.110」という2つのリモートホストで「echo hoge」コマンドを実行している。

$ bolt command run "echo hoge" -n 192.0.2.100,192.0.2.110
Started on 192.0.2.100...
Started on 192.0.2.110...
Finished on 192.0.2.100:
  STDOUT:
    hoge
Finished on 192.0.2.110:
  STDOUT:
    hoge
Successful on 2 nodes: 192.0.2.100,192.0.2.110
Ran on 2 nodes in 0.40 seconds

また、ホスト名やIPアドレスの代わりに「@<ファイル名>」と指定することで、指定したファイルに記載されているIPアドレスやホスト名を対象にできる。次の例では、「192.0.2.100」および「192.0.2.110」と記述した「nodes.txt」というファイルを指定して実行している。

$ cat nodes.txt
192.0.2.100
192.0.2.110
$  bolt command run "echo hoge" -n @nodes.txt
Started on 192.0.2.100...
Started on 192.0.2.110...
Finished on 192.0.2.110:
  STDOUT:
    hoge
Finished on 192.0.2.100:
  STDOUT:
    hoge
Successful on 2 nodes: 192.0.2.110,192.0.2.100
Ran on 2 nodes in 0.39 seconds

「-n -」と指定すれば、標準入力経由でリモートホストを指定できる。

$ cat nodes.txt |  bolt command run "echo hoge" -n -
Started on 192.0.2.100...
Started on 192.0.2.110...
Finished on 192.0.2.110:
  STDOUT:
    hoge
Finished on 192.0.2.100:
  STDOUT:
    hoge
Successful on 2 nodes: 192.0.2.110,192.0.2.100
Ran on 2 nodes in 0.36 seconds

スクリプトを実行する

boltコマンドでは、「script」サブコマンドで任意のスクリプトをリモートホスト上で実行できる。

$ bolt script run <実行するスクリプト> -n <対象ホスト>

次の例は、echoコマンドを実行するだけのスクリプト(hello.sh)を実行したものだ。

$ cat hello.sh
#!/bin/bash
echo "hello, world!"

$ bolt script run hello.sh -n 192.0.2.100
Started on 192.0.2.100...
Finished on 192.0.2.100:
  STDOUT:
    hello, world!
Successful on 1 node: 192.0.2.100
Ran on 1 node in 0.62 seconds

ここではシェルスクリプトを実行させているが、リモートホスト上で実行できるものであれば任意のスクリプトやプログラムが実行可能だ。

ファイルのアップロード

「file」サブコマンドでは、指定したファイルを指定したホストにアップロードできる。

$ bolt file upload <対象のファイル> <アップロード先のパス> -n <対象ホスト>

たとえば「sample.txt」というファイルを、リモートホストのホームディレクトリ(~/)にアップロードするには次のように実行する。

$ bolt file upload sample.txt ~/ -n 192.0.2.100
Started on 192.0.2.100...
Finished on 192.0.2.100:
  Uploaded 'sample.txt' to '192.0.2.100:/home/hylom/'
Successful on 1 node: 192.0.2.100
Ran on 1 node in 0.52 seconds

実行する処理を定義した「Task」と「Plan」

Boltでは、リモートホストで実行する処理を「Task」や「Plan」という形で定義するようになっている。実行する単一の処理を定義したものがTaskで、1つ以上のTaskを組み合わせて実行できるようにしたものがPlanだ。

TaskやPlanは再利用できる形で定義できるようになっており、デフォルトでもいくつかのTaskやPlanがインストールされている。

Taskの一覧と実行

Taskに関する処理は「task」サブコマンドで実行でき、たとえば利用できるTaskの一覧は「bolt task show」コマンドで一覧表示できる。

$ bolt task show
facts                    Gather system facts
package                  Manage and inspect the state of packages
puppet_agent::install    Install the Puppet agent package
puppet_agent::version    Get the version of the Puppet agent package installed. Returns nothing if none present.
puppet_conf              Inspect puppet agent configuration settings
reboot                   Reboots a machine
reboot::last_boot_time   Gets the last boot time of a Linux or Windows system
service                  Manage and inspect the state of services

MODULEPATH:
/home/hylom/.puppetlabs/bolt/modules:/home/hylom/.puppetlabs/bolt/site-modules:/home/hylom/.puppetlabs/bolt/site

Use `bolt task show <task-name>` to view details and parameters for a specific task.

個々のTaskの詳細は、「bolt task show <Task名>」コマンドで確認できる。たとえば「facts」というTaskについての詳細は次のようになっている。

$ bolt task show facts

facts - Gather system facts

USAGE:
bolt task run --nodes <node-name> facts

MODULE:
built-in module

このTaskは「Gather system facts」と説明されているとおり、指定したノードの基本的な情報を取得するものだ。Taskを実行するには、「bolt task run <Task名>」コマンドを使用する。次の例は、前述のfactsというTaskを実行するものだ。

$ bolt task run facts -n 192.0.2.110
Started on 192.0.2.110...
Finished on 192.0.2.110:
  {
    "os": {
      "name": "CentOS",
      "release": {
        "full": "7.6",
        "major": "7",
        "minor": "6"
      },
      "family": "RedHat"
    }
  }
Successful on 1 node: 192.0.2.110
Ran on 1 node in 0.71 seconds

Taskによっては、追加のパラメータ指定が必要となるものもある。たとえば「package」というTaskはパッケージのインストールやアップデート、アンインストールといった処理を行うもので、実行する処理を「action」、対象とするパッケージ名を「name」パラメータで指定する。

$ bolt task show package

package - Manage and inspect the state of packages

USAGE:
bolt task run --nodes <node-name> package action=<value> name=<value> version=<value> provider=<value>

PARAMETERS:
- action: Enum[install, status, uninstall, upgrade]
    The operation (install, status, uninstall and upgrade) to perform on the package.
- name: String[1]
    The name of the package to be manipulated.
- version: Optional[String[1]]
    Version numbers must match the full version to install, including release if the provider uses a release moniker. Ranges or semver patterns are not accepted except for the gem package provider. For example, to install the bash package from the rpm bash-4.1.2-29.el6.x86_64.rpm, use the string '4.1.2-29.el6'.
- provider: Optional[String[1]]
    The provider to use to manage or inspect the package, defaults to the system package manager. Only used when the 'puppet-agent' feature is available on the target so we can leverage Puppet.

MODULE:
built-in module

次の例は、このTaskを使って「httpd」パッケージをインストールするものだ。パッケージのインストールにはroot権限が必要であるため、ここでは「--run-as root」オプションを指定して実行している。

$ bolt task run package action=install name=httpd --run-as root --sudo-password -n 192.0.2.110
Please enter your privilege escalation password:
Started on 192.0.2.110...
Finished on 192.0.2.110:
  {
    "status": "installed",
    "version": "2.4.6-89.el7.centos"
  }
Successful on 1 node: 192.0.2.110
Ran on 1 node in 13.01 seconds

また、「service」というTaskはサービスの起動/停止、再起動、有効化/無効化などを行うものだ。こちらも「action」で実行する処理、「name」で対象となるサービスを指定するようになっている。

$ bolt task show service

service - Manage and inspect the state of services

USAGE:
bolt task run --nodes <node-name> service action=<value> name=<value> provider=<value>

PARAMETERS:
- action: Enum[start, stop, restart, enable, disable, status]
    The operation (start, stop, restart, enable, disable, status) to perform on the service.
- name: String[1]
    The name of the service to operate on.
- provider: Optional[String[1]]
    The provider to use to manage or inspect the service, defaults to the system service manager. Only used when the 'puppet-agent' feature is available on the target so we can leverage Puppet.

MODULE:
built-in module

次の例は、このTaskを使って「httpd」サービスをリスタートさせるものだ。

$ bolt task run service action=restart name=httpd --run-as root --sudo-password -n 192.0.2.110
Please enter your privilege escalation password:
Started on 192.0.2.110...
Finished on 192.0.2.110:
  {
    "status": "MainPID=29066,LoadState=loaded,ActiveState=active"
  }
Successful on 1 node: 192.0.2.110
Ran on 1 node in 1.19 seconds

Planの一覧と実行

Planは複数のTaskを組み合わせて目的の処理を達成できるようにしたもので、「plan」サブコマンドでPlanに関する処理を実行できる。たとえば利用できるPlanの一覧は「bolt plan show」コマンドで確認できる。

$ bolt plan show
aggregate::count
aggregate::nodes
canary
facts
facts::info
puppetdb_fact
reboot

MODULEPATH:
/home/hylom/.puppetlabs/bolt/modules:/home/hylom/.puppetlabs/bolt/site-modules:/home/hylom/.puppetlabs/bolt/site

Use `bolt plan show <plan-name>` to view details and parameters for a specific plan.

また、Planの詳細は「bolt plan show <Plan名>」コマンドで確認できる。次の例は「facts」というPlanについての詳細を表示したものだ。

$ bolt plan show facts

facts

USAGE:
bolt plan run facts nodes=<value>

PARAMETERS:
- nodes: TargetSpec

MODULE:
built-in module

Planを実行するには、「bolt plan run <Plan名>」コマンドを実行すれば良い。たとえば「nodes.txt」に記述されているリモートホストに対しfactsというTaskを実行する場合、次のようになる。

$  bolt plan run facts -n @nodes.txt
Starting: plan facts
Starting: task facts on 192.0.2.110, 192.0.2.100
Finished: task facts with 0 failures in 0.6 sec
Finished: plan facts in 0.62 sec
Finished on 192.0.2.110:
  {
    "os": {
      "name": "CentOS",
      "release": {
        "full": "7.6",
        "major": "7",
        "minor": "6"
      },
      "family": "RedHat"
    }
  }
Finished on 192.0.2.100:
  {
    "os": {
      "name": "CentOS",
      "release": {
        "full": "7.6",
        "major": "7",
        "minor": "6"
      },
      "family": "RedHat"
    }
  }
Successful on 2 nodes: 192.0.2.110,192.0.2.100
Ran on 2 nodes

TaskやPlanのインストール

TaskやPlanはモジュール化されており、容易に追加できる構造になっている。たとえば、Puppetが提供するPuppet ForgeというサイトでもBoltで利用できるTaskが公開されている(図2)。

図2 Puppetが運営している「Puppet Forge」
図2 Puppetが運営している「Puppet Forge」

Puppet Forgeで公開されているモジュールは、boltコマンドを使って簡単にインストールできる。たとえばリモートホスト上でMySQLサーバーに接続してSQLを実行したり、データベースをローカルストレージにバックアップしたりするといったTaskを提供する「mysql」モジュールをインストールしたい場合、次のような手順になる。

まず、モジュールをインストールする適当なディレクトリを用意し、そこに「bolt.yaml」というファイルと「Puppetfile」というファイルを作成する。

↓空のbolt.yamlファイルを作成する
$ touch bolt.yaml

↓Puppetfileファイルを作成する
$ echo "mod 'puppetlabs-mysql', '10.0.0'" > Puppetfile

$ ls
Puppetfile  bolt.yaml

bolt.yamlはboltコマンドの挙動を定義するファイルだが、空のファイルでも問題ない。また、Puppetfileは使用するモジュールを指定するファイルで、次のようなフォーマットでインストールするモジュールを列挙する。

mod '<モジュール名>', '<バージョン>'

モジュール名やバージョンはPuppet Forgeのモジュールページで確認できる。今回インストールする「mysql」モジュールの場合、モジュール名は「puppetlabs-mysql」、バージョンは「10.0.0」となっているので、次のように指定する。

mod 'puppetlabs-mysql', '10.0.0'"

bolt.yamlおよびPuppetfileの2つのファイルを用意したら、これらファイルが格納されているディレクトリで「bolt puppetfile install」コマンドを実行すると、そのディレクトリ以下に「modules」というディレクトリが作成され、そこに指定したモジュールがインストールされる。

$ bolt puppetfile install
Successfully synced modules from /home/hylom/bolt/test03/Puppetfile to /home/hylom/bolt/test03/modules

$ ls
Puppetfile  bolt.yaml modules

$ ls modules
mysql

この状態で「bolt task show」コマンドを実行すると、「mysql::export」および「mysql::sql」という2つのTaskが利用できるようになっていることが確認できる。

$ bolt task show
facts                    Gather system facts
mysql::export            Allows you to backup your database to local file.
mysql::sql               Allows you to execute arbitary SQL
package                  Manage and inspect the state of packages
puppet_agent::install    Install the Puppet agent package
puppet_agent::version    Get the version of the Puppet agent package installed. Returns nothing if none present.
puppet_conf              Inspect puppet agent configuration settings
reboot                   Reboots a machine
reboot::last_boot_time   Gets the last boot time of a Linux or Windows system
service                  Manage and inspect the state of services

MODULEPATH:
/home/hylom/bolt/test03/modules:/home/hylom/bolt/test03/site-modules:/home/hylom/bolt/test03/site

Use `bolt task show <task-name>` to view details and parameters for a specific task.

mysql::sqlは指定したホストでSQLを実行するTaskで、「sql」というパラメータで実行するSQLを指定する。オプションで対象のデータベース名や使用するユーザー名およびパスワードを指定することも可能だ。

$ bolt task show mysql::sql

mysql::sql - Allows you to execute arbitary SQL

USAGE:
bolt task run --nodes <node-name> mysql::sql database=<value> user=<value> password=<value> sql=<value>

PARAMETERS:
- database: Optional[String[1]]
    Database to connect to
- user: Optional[String[1]]
    The user
- password: Optional[String[1]]
    The password
- sql: String[1]
    The SQL you want to execute

MODULE:
/home/hylom/bolt/test03/modules/mysql

また、mysql::exportはデータベースに格納されているデータをファイルに出力するTaskで、「file」パラメータで保存先ファイルを指定する。

$ bolt task show mysql::export

mysql::export - Allows you to backup your database to local file.

USAGE:
bolt task run --nodes <node-name> mysql::export database=<value> user=<value> password=<value> file=<value>

PARAMETERS:
- database: Optional[String[1]]
    Database to connect to
- user: Optional[String[1]]
    The user
- password: Optional[String[1]]
    The password
- file: String[1]
    Path to file you want backup to

MODULE:
/home/hylom/bolt/test03/modules/mysql

なお、Taskによっては操作対象のリモートホストにPuppetのエージェントがインストールされていることが前提となっている場合がある。ここでインストールしたmysqlモジュールもそうなっており、Puppetのエージェントがインストールされていない場合はエラーとなってしまう。

たとえば次の実行例は、指定したリモートホスト上で「show databases」というSQL文を実行するものだが、Puppetエージェントがインストールされていない場合次のようにランライムがない旨が表示されてエラーとなる。

$ bolt task run mysql::sql sql="show databases" -n 192.0.2.100
Started on 192.0.2.100...
Failed on 192.0.2.100:
  The task failed with exit code 126:
  bash: /tmp/832e77d6-1aeb-4399-aed1-e6b9f3ece522/sql.rb: /opt/puppetlabs/puppet/bin/ruby: 誤ったインタプリタです: そのようなファイルやディレクトリはありません

  {
  }
Failed on 1 node: 192.0.2.100
Ran on 1 node in 0.71 seconds

この場合、次のように「puppet_agent::install」というTaskを実行することで対象ホストにPuppetのエージェントをインストールできる。

$ bolt task run puppet_agent::install --run-as root --sudo-password -n 192.0.2.100

エージェントのインストール後に同じコマンドを実行すると、次のようにSQL文の実行結果が表示される。

$ bolt task run mysql::sql sql="show databases" -n 192.0.2.100
Started on 192.0.2.100...
Finished on 192.0.2.100:
  {
    "status": "Database\ninformation_schema\ntest"
  }
Successful on 1 node: 192.0.2.100
Ran on 1 node in 1.70 seconds

なお、Puppet Forgeは現状ではPuppet向けのモジュールの公開が主となっており、Boltで利用できるのはその一部のみだ。検索時に「With Tasks?」で「Yes」を指定するとBoltで利用できる(Taskが提供されている)モジュールのみを対象に検索できるのだが、記事執筆時点でTaskを提供しているモジュールは211個で、うち一定の品質を保証する「approved」もしくは「supported」タグが付けられているのは37個に留まっている(図3)。

図3 Puppet Forgeで公開されているBolt対応モジュールはまだ少ない
図3 Puppet Forgeで公開されているBolt対応モジュールはまだ少ない

独自にPlanやTaskを作成する

Boltで標準で提供されているTaskは、パッケージのインストール、サービスの管理などを行うものなどに限られている。スクリプトを実行する機能も用意されているため、これらだけでもある程度のシステム管理は可能ではあるものの、構成管理ツールの利点である、宣言的に設定作業を記述できる点を活かそうとするのであれば、現状では独自にPlanやTaskを作成していく必要がある。そこで以下では、独自のPlanやTaskを作成する方法を説明していく。

Boltが利用する「プロジェクト」ディレクトリ構造

Boltにおいては、使用する設定ファイルやTask、Planといったモジュールは「プロジェクト」と呼ばれる特定のディレクトリ構造下に配置しなければならない。boltコマンドの実行時には、まずこのプロジェクトディレクトリの探索が行われ、次のようにしてプロジェクトディレクトリとして認識する。

  • 「--boltdir」オプションが指定された場合、このオプションで指定されたディレクトリがプロジェクトディレクトリとなる
  • カレントディレクトリに「bolt.yaml」ファイルが存在する場合、カレントディレクトリがプロジェクトディレクトリとなる
  • カレントディレクトリに「Boltdir」という名前のディレクトリが存在する場合、そのディレクトリがプロジェクトディレクトリとなる
  • bolt.yamlファイルやBoltdirディレクトリが見つからなかった場合、親ディレクトリを順に辿って探索してbolt.yamlファイルが存在するディレクトリもしくはBoltdirという名前のディレクトリを探し、そのディレクトリをプロジェクトディレクトリとする
  • ルートディレクトリまで辿ってもbolt.yamlファイルやBoltdirディレクトリが見つからなかった場合、ホームディレクトリ以下の.puppetlabs/boltディレクトリがプロジェクトディレクトリとなる

なお、bolt.yamlはboltコマンドの挙動を指定するファイルだ。指定できる内容についてはドキュメントを参照してほしいが、boltコマンドの実行時に指定できるオプションのほとんどがこの設定ファイル内に記述できるようになっている。

また、プロジェクトディレクトリには「inventory.yaml」というファイルを置くこともできる。このファイルでは、管理対象とするリモートホストにエイリアス(別名)を付けたり、複数のリモートホストをグループとして管理したりする設定を記述できる。詳しくはドキュメントを参照して欲しいが、たとえば次の例は、「192.0.2.110」と「192.0.2.100」の2つのホストを「servers」というエイリアスで参照できるように設定したものだ。

$ cat inventory.yaml
groups:
  - name: servers
    nodes:
      - 192.0.2.110
      - 192.0.2.100

設定したエイリアスは、boltコマンドの「-n」オプションなどでリモートホストを指定する際に利用できる。

$  bolt command run "echo hoge" -n servers
Started on 192.0.2.100...
Started on 192.0.2.110...
Finished on 192.0.2.100:
  STDOUT:
    hoge
Finished on 192.0.2.110:
  STDOUT:
    hoge
Successful on 2 nodes: 192.0.2.110,192.0.2.100
Ran on 2 nodes in 0.37 seconds

モジュールのインストールに使用するPuppetfileファイルも、このプロジェクトディレクトリ内に配置するようになっている。さらに、「bolt task」コマンドや「bolt plan」コマンドの実行時、Boltはプロジェクトディレクトリ下の「modules」ディレクトリ内からモジュールを探索する。各モジュールディレクトリ内では、「tasks」ディレクトリ内にTaskを定義したファイルを、「plans」ディレクトリ内にPlanを定義したファイルを格納する。

Taskの作成

それでは、実際にTaskを作成してみよう。ここではTaskの例として、メモリやI/Oの使用状況を表示する「vmstat」コマンドと、デバイス毎のI/O状況を表示する「iostat」コマンドを実行してその結果を表示するというTaskを「stats」という名前で作成してみる。

まず、前述したプロジェクトディレクトリ構造に則ってTaskを格納するディレクトリを作成する。今回は「stats」という名称なので、プロジェクトディレクトリの「modules」ディレクトリ以下に「stats」というディレクトリを作成し、さらにその下に「tasks」ディレクトリを作成する。

$ mkdir -p Boltdir/modules/stats/tasks

続いて、このディレクトリ内に「init.json」と「init.sh」というファイルを作成する。

$ vi Boltdir/modules/stats/tasks/init.json
$ vi Boltdir/modules/stats/tasks/init.sh

init.jsonはTaskのメタデータをJSON形式で記述するものだ。たとえばTaskの説明は「description」プロパティで指定できる。今回は次のような内容とした。

{
  "description": "run vmstat and iostat"
}

また、init.shはTaskの実行時に実行されるスクリプトとなる。今回は次のような内容とした。

#!/bin/sh
vmstat
echo "----"
iostat

これらのファイルを作成後「bolt task show」コマンドを実行すると、「stats」というTaskが認識されていることが分かる。

$ bolt task show
facts                    Gather system facts
package                  Manage and inspect the state of packages
puppet_agent::install    Install the Puppet agent package
puppet_agent::version    Get the version of the Puppet agent package installed. Returns nothing if none present.
puppet_conf              Inspect puppet agent configuration settings
reboot                   Reboots a machine
reboot::last_boot_time   Gets the last boot time of a Linux or Windows system
service                  Manage and inspect the state of services
stats                    run vmstat and iostat


「bolt task run」コマンドでこのTaskを実行すると、次のように指定したリモートホストでinit.shファイルが実行され、その結果が出力される。

$ bolt task run stats -n 192.0.2.110
Started on 192.0.2.110...
Finished on 192.0.2.110:
  procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
   r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
   2  0    264 340396  26648 533508    0    0     1     1   53   72  0  0 100  0  0
  ----
  Linux 3.10.0-957.5.1.el7.x86_64 (centos10)    2019年07月10日  _x86_64_        (1 CPU)

  avg-cpu:  %user   %nice %system %iowait  %steal   %idle
             0.05    0.00    0.03    0.00    0.03   99.89

  Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
  vda               0.11         0.81         0.96     423707     502896

  {
  }
Successful on 1 node: 192.0.2.110
Ran on 1 node in 0.66 seconds

なお、ここではシェルスクリプトでTaskを実装しているが、Taskの実装には任意の言語が利用可能だ。その場合、ファイルの拡張子は任意のものに変更できる。たとえばPythonでTaskを実装した場合、ファイル名は「init.py」とすれば良い。

また、1つのモジュールに複数のTaskを定義することもできる。その場合、スクリプトファイル名には任意のものが使用可能だ。そのファイル名の拡張子を除いたものがTask名となり、それに拡張子「.json」を付けたものがそのTaskのメタデータを記載するファイルとなる。また、boltコマンドでそのTaskを指定する場合は「<モジュール名>::<Task名>」という形で指定する。

たとえば、「vmstat.sh」という名前でTaskを実装した場合、メタデータは「vmstat.json」というファイルで指定し、このTaskを実行する場合は次のように指定する。

$ bolt task run stats::vmstat -n 192.0.2.110

Planの作成

Planは、実行する1つ以上のTaskをまとめて定義したものだ。これを利用することで、複数のTaskを1回のコマンドでまとめて実行できる。

PlanはRuby言語をベースとしたDSL(ドメイン特化言語)である「Puppet言語」、もしくはYAML形式で記述する。ただし、YAMLによる記述はベータ機能で今後仕様が変更される可能性があるとのことなので、今回はDSLによる記述についてのみ説明しておく。YAMLでの記述についてはドキュメントを参照してほしい。

Puppet言語でPlanを記述する場合、ファイル名は「init.pp」もしくは「<Plan名>.pp」となり、モジュールディレクトリ下の「plans」ディレクトリ以下に配置する。Planの基本的なフォーマットは次のようになる。

plan <モジュール名>::<Plan名>(
  TargetSpec $<パラメータ1>,
  TargetSpec $<パラメータ2>,
  
  
) {
  run_task('<Task名>', <対象ノード>, <パラメータ>)
  
  
}

Taskの場合と同様、boltコマンドではファイル名が「init.pp」だった場合はモジュール名、「<Plan名>.pp」だった場合は「<モジュール名>::<Plan名>」でPlanを指定する。

たとえば次の例は、「update_package」というモジュールで提供されるPlanを作成するものだ。このPlanでは「package」Taskと「service」Taskを使ってパッケージのアップデートとサービスの再起動を実行するという書類を実行する。

まず、モジュールを格納する「update_package」ディレクトリを作成する。

$ mkdir -p Boltdir/modules/update_package

続いてこのディレクトリ下にPlanの設定ファイルを格納する「plans」ディレクトリを作成する。

$ mkdir Boltdir/modules/update_package/plans

今回はモジュール内に1つのPlanのみを作成するので、「init.pp」というファイルでPlanを定義する。

$ vi Boltdir/modules/update_package/plans/init.pp

init.ppファイルの内容は次のようになる。

↓「update_package」というPlan名でPlanを定義する
plan update_package (
↓「nodes」、「package_name」、「service_name」という3つのパラメータを受け取ることを宣言する
  TargetSpec $nodes,
  TargetSpec $package_name,
  TargetSpec $service_name,
) {
  ↓「nodes」パラメータで指定されたノードに対し、「package」Taskを実行する。
  このTaskの「action」パラメータには「upgrade」を、「name」パラメータには「package_name」パラメータで指定された値を与える
  run_task('package', $nodes, { 'action' => 'upgrade',
                                'name' => $package_name })

  ↓「nodes」パラメータで指定されたノードに対し、「service」Taskを実行する。
  このTaskの「action」パラメータには「restart」を、「name」パラメータには「service_name」パラメータで指定された値を与える
  run_task('service', $nodes, { 'action' => 'restart',
                                'name' => $service_name })
}

ここでは、「update_package」という名前のPlanを定義している。このPlanは「nodes」および「package_name」、「service_name」の3つのパラメータを取り、これらで指定された値を使ってパッケージのアップグレードとサービスのリスタートを実行するようになっている。

以上でPlanの作成は完了だ。「bolt plan show」コマンドを実行すると、このPlanが認識されていることが確認できる。

$ bolt plan show
aggregate::count
aggregate::nodes
canary
facts
facts::info
puppetdb_fact
reboot
update_package

MODULEPATH:
/home/hylom/bolt/test02/Boltdir/modules:/home/hylom/bolt/test02/Boltdir/site-modules:/home/hylom/bolt/test02/Boltdir/site

Use `bolt plan show <plan-name>` to view details and parameters for a specific plan.
$ bolt plan show update_package

update_package

USAGE:
bolt plan run update_package nodes=<value> package_name=<value> service_name=<value>

PARAMETERS:
- nodes: TargetSpec
- package_name: TargetSpec
- service_name: TargetSpec

MODULE:
/home/hylom/bolt/test02/Boltdir/modules/update_package

また、「bolt plan run」コマンドでこのPlanを実行できる。

$  bolt plan run update_package -n 192.0.2.110 package_name=httpd service_name=httpd --run-as root --sudo-password
Please enter your privilege escalation password:
Starting: plan update_package
Starting: task package on 192.0.2.110
Finished: task package with 0 failures in 7.48 sec
Starting: task service on 192.0.2.110
Finished: task service with 0 failures in 1.89 sec
Finished: plan update_package in 9.4 sec
Plan completed successfully with no result

今後の周辺環境の発展に期待

前述のとおり、構成管理自動化ツール分野でAnsibleが人気なのは設定がシンプルで、簡潔に設定を記述できるという点が大きい。Puppetはそれと比較すると事前準備や設定の記述に手間がかかるということで、それを解消するためにBoltが開発されたと思われる。実際、単にコマンドやスクリプトをリモートホスト上で実行したり、パッケージのインストールやサービスの管理を行ったりするだけであれば、Boltは簡単に利用できる。

一方で、それ以上のことをやろうとすると現時点ではやや手間がかかってしまうというのが現状だ。公開されているTaskはまだ少なく、またTaskやPlanの記述を作成する場合、ディレクトリ構造やファイル名などのルールに従わなければならない。また、一部のTaskの利用にはPuppetのエージェントが必要な点もマイナスポイントだろう。

ただ、すでにPuppetを利用した構成管理を利用しているのであれば、Boltはそれと組み合わせて利用するツールとして有用だ。boltコマンド経由でPuppetエージェントをインストールすることもできるし、ディレクトリ構造などのルールはPuppetのものと同じだ。

また、今後Puppetのエージェントが不要なTaskが多く公開されるようになれば、簡単に使えて強力な構成管理自動化ツールとなる可能性もある。今後の発展に期待したい。