今ふたたびのAnsible入門

こんにちは、ライトハウスでソフトウェアエンジニアをしている北添です。

今回は「今ふたたびのAnsible入門」と題しまして以下の内容を書きます。

  • なぜ今ふたたびAnsibleに入門するのか
  • 基本的な使い方
  • ベストプラクティスに則ったAnsibleのコード設計

なぜこの記事を書いたか

理由は2つあります。

1つ目は、IoTのプロダクトでAnsibleがどのように活躍するのか、ご紹介したかったことです。

私が勤める株式会社ライトハウスは、水産業界の課題解決のために船舶のIoT化を推し進めています。 船舶からセンサーを通じて情報を吸い上げ、クラウドへ送信することでIoT化が達成されます。

通常、センサーで取得したデータは直接クラウドへ送信されるのではなく、船内のサーバーに一度集められた後送信されます。 つまり、IoTの文脈ではオンプレミスサーバーが必要であり、オンプレミスサーバーが必要な以上、構成管理が重要になるのです。

2つ目は、(ちょうど数か月前の私のような)Ansible入門者の道標となるサンプルコードを提供したいと思ったことです。

特に、Ansible公式が推奨するベストプラクティスに則った使い方は、実例となるコードを見ることによって一気に理解できました。 そういった形でこの記事が役に立てば嬉しいです。

なぜ、今ふたたびAnsibleに入門するのか

なぜ、クラウド・コンテナ開発全盛の今に、サーバーの構成管理をするAnsibleに入門する必要があるのでしょうか。

それを知るために、ライトハウスでのAnsibleの活用をご紹介します。

船舶をIoT化する上でまず必要になるのが、センサーとサーバーです。 センサーがなければモノの情報が取得できませんし、サーバーがなくてはインターネットに繋ぐことができません。

そのサーバーの構成管理ツールとして、Ansibleを利用しています。

もしも、構成管理ツールを使わず、手動オペレーションによってサーバーを構築するとどうなるでしょうか。

サーバーがどのような状態なのか把握できなくなり、管理不能な状態に陥ります。

弊社でも、構成管理していないサーバーの障害対応で非常に手痛い思いをしたことがありました… 障害の状況を再現できず、原因究明やリカバリーの難易度が高くなってしまうのです。

LHでのAnsible実行環境の紹介

次に、より具体的にライトハウスでどのようにAnsibleを活用しているのかご紹介します。

検証環境と本番環境の2つを用意し、それぞれの環境の構成管理をAnsibleで行っています。

ただし、船に置くサーバーは高価なため、検証用のサーバーとして本番と同じものを買うことはできません。 そこで、検証用の環境には本番と同じOSが入っているEC2インスタンスを使っています。

Ansibleで管理する対象は、船に置くオンプレミスのサーバーとEC2インスタンスとなっているわけです。

Ansible入門(実行編)

さて、ここからAnsibleの具体的な使い方について紹介します。

ここではAnsibleのインストールから、対象のホストにAnsibleでPingできるようになるまでの手順を紹介します。

インストール

Macをお使いの方は、brewでインストールできます。

brew install ansible

実行

まずはAnsibleを通して、対象のホストにpingしてみましょう。 ${HOST_IP}の部分には対象のホストのIPアドレスを入力してください。

ansible all -i ${HOST_IP}, -m ping

うまく疎通ができれば、Pingが成功するはずです。

変数を利用する

Ansibleでは変数を利用できます。

コマンドラインで変数を書くことも、jsonyamlに変数を保存して使うこともできます。

variable.jsonという名前のファイルに変数を定義していると仮定すると、以下のように利用できます。

ansible all -i ${HOST_IP}, -m ping --extra-vars "@variable.json"

Ansible入門(インベントリー編)

Ansibleを実行するときは、実行対象のホストを指定する必要があります。 この「実行対象のホスト」のことをAnsibleでは「インベントリー」と呼びます。

インベントリーの指定方法は2つです。

  1. コマンドラインで与える
  2. ファイルに書いて与える

コマンドラインから与える際は、 -i オプションにホストのIPアドレスかホスト名を与えます。 -i に入る値は、カンマ区切りである点を注意してください。

次に、ファイルに書く場合は以下のようにします。

[webservers]
foo.example.com
bar.example.com

webservers というグループに、 foo.example.combar.example.com というホストが存在している」という意味の設定です。

インベントリーをファイルで設定する場合、ファイル名は hostsとすることがデフォルトです。

インベントリーの書き方(実例)

インベントリーの書き方について、より実践的な例を紹介しておきます。

SSMセッションを利用したSSH接続を行い、EC2インスタンスに対してAnsible実行するためのhostsファイルです。

[ec2-instance]
instance ansible_host={EC2インスタンスID}

[all:vars]
ansible_ssh_common_args=-o StrictHostKeyChecking=no -o ProxyCommand="sh -c \"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'\""
ansible_user='ec2-user'
ansible_become=true

Ansible入門(設計編)

設計編では、Ansibleのコード管理のベストプラクティスをいくつかご紹介します。

Ansible公式が掲載しているベストプラクティスを参考にしました。

rolesディレクトリの活用

Ansibleでは、rolesディレクトリの配下に、さらに管理対象ごとのディレクトリを作ることで分かりやすく整理するのがベストプラクティスと言われています。

例えば、chrony関連を管理するAnsibleのコードは、 roles/chrony 配下に配置します。

roles/
    chrony/
    network/
.
.
.

さらにそのディレクトリ配下には、filestaskshandlers というディレクトリを作成します。

先程のchronyを例に取り、ディレクトリ構造を示します。

roles/
    chrony/
        files/
            - chrony.conf
        tasks/
            - main.yml
        handlers/
            - main.yml

以上のような形になります。

filesには、サーバーに配置したい設定ファイルを置きます。

引き続きchronyの例でいうと、chronyのサービスが参照する chrony.conf ファイルを置きます。

tasksとhandlersには、サーバーに適用するアクションを定義します。 tasksでもし変更を検知したら、handlersの処理を呼び出すという関係になるようにアクションを置き分けていきます。

引き続きchronyの例を取り上げます。 tasks配下にはchronyの設定ファイルを配置するよう定義しておきます。

- name: deploy chrony.conf
  template:
    src: "./files/chrony.conf"
    dest: "/etc/chrony.conf"
    owner: root
    group: root
    mode: 0644
  notify: restart chronyd.service

ポイントは、 notify というディレクティブです。 これにより「chrony.confに変更があった時だけ」notify が指し示すアクションを実行するようになります。

そして、handlers配下には以下のようにサービスのrestartが行われるように定義します。

- name: restart chronyd.service
  systemd:
    name: chronyd.service
    state: restarted
    enabled: yes
    daemon_reload: yes

こうすることで、chrony.confに変更があるときだけ、chronydのサービスが再起動して設定が読み込まれるようになります。

ちなみに、 state には restarted の他にも reloadedstopped などがあります。 こうした選択肢は公式のドキュメントに載っていますので、適宜調べて最適なものを選ぶ必要があります。

stateを最適化する例を1つ紹介します。

restarted に指定していると、サービスが一度ダウンした後起動する挙動をします。 一方で、 reloaded に指定していると、サービスのダウンなしに立ち上がり直す挙動をします。

そうした性質を踏まえて、ダウンタイムなしでサービスに設定を反映させたい場合は reloaded を使うなどの判断が必要です。 例えばnginxのサービスなどは、ダウンタイムがないとより好ましいでしょう。

あとがき

ここまでお読みいただき、ありがとうございます。 IoTにご興味のある方はAnsibleを使えて損はありません。この記事を入門の第一歩をしてくれたら嬉しいです。

エンジニアを募集しています

ライトハウスではエンジニア募集しています。

船舶のIoT化や水産業界のDXを推し進める、社会的な意義の深い仕事に取り組めます!

少しでもご興味が湧いたら、ぜひカジュアル面談へいらしてください!

未知を拓く。共に船舶海洋プラットフォームを構築するエンジニア募集