@legitwhiz

legit whiz 技術レポート

View on GitHub

Ansibleで始める構成管理ツール_Part1

Ansibleとは

【Ansible】とは、『構成管理ツール』です。

何を構成管理するかというと、サーバのインフラをコードで管理する【Infrastructure as a Code】ツールで、【Chef】【Puppet】などと同様なツールです。

【Chef】【Puppet】では、高度な管理を行うとするとRubyが前提となっているので、インフラ開発者では学習コストが高く、エージェントが必要となるため導入のハードルが高い。

それに対して【Ansible】は、以下の特徴を持っている。

なぜAnsibleが必要なのか?

【現状】

サーバ構築作業の現状は、 設計書をもとにパラメータシートや構築手順書を作成し、 構築手順書をもとに構築作業を実施し、設定値はパラメータシートを設定作業を実施している。 試験にてパラメータシート通りに設定されているか確認し、設計書通りの動作をするか確認する。

【問題点、リスク】

大量にサーバ構築する場合は、時間的に無駄が多い。(バックアップリストアで問題は一部解消されるが、部分的に設定を修正する必要性がある) 同じことの繰り返しとなることから、オペミス、設定値の取り違えが発生するリスクがある。(試験で問題は解消されるが、確認する側も同じことの繰り返しになることから、間違いをスルーしてしまうリスクがあり時間的な無駄が発生する) 設計書を修正した場合、実機反映漏れが発生するリスクがある。(構築時点でconfigファイルもバージョン管理、CIも実施していれば回避可能)

【結論】

以上より、【構成管理ツール】で機械的に構築されたサーバであれば、リスクを軽減でき時間的な工数削減が望める。ただし、プロジェクトメンバーが運用ルールを絶対としているのが条件です。 ただし、現状ベンダーやSIerでは、機械的に構成をしても継続的に単体試験というパラメータの設定確認は実施すると思われる時間的な工数削減は僅かなのかもしれません。

【メリット】

構成管理ツールを導入することで、リスク削減と工数削減はもちろんですが、OS、MWのパラメータシートを構成管理ツールの設定ファイルとして代用することができ、その設定ファイルをバージョン管理することでITインフラもコード【Infrastructure as a Code】で管理することができる。

【デメリット】 ツールが多様化することで、実作業は軽減されると思われるが、運用ルールが増加するためルールの管理、運用フローの展開、運用文書化と管理内容の複雑になってしまう。

参考

公式サイト ドキュメント Ansible 入門 Ansibleをはじめる人に。 超ansible入門 日本Ansibleユーザ会

入門Ansible Kindle版 Ansible実践ガイド第2版

0.検証環境

OS:CentOS 7.5 ミドルウェア:ansible 2.7.5 前提ミドルウェア:EPEL

1. Ansibleインストール

1.1 Ansibleインストール

# yum install -y epel-release     ※1
# yum install -y ansible
# yum -y install python-pip       ※2
# pip install --upgrade pip       ※2
# pip install pywinrm             ※2
# ansible --version               ※3

インストールは、以上で完了で。後は運用設定のみで、構築自体はあっけない程簡単です。 Ansibleの設定は、/etc/ansible/ansible.cfgでされていますが、このファイルを変更することはあまりなさそうです。(Ansible用hostsファイル、playbookで制御可能なため)

※1:ansibleは標準リポジトリに存在しないためエンタープライズ Linux 用の拡張パッケージ(EPEL)を追加。 ※2:構成管理するクライアントがWindowsServerを含む場合は、【pywinrm】を導入する必要がある。 ※3:インストール確認のため、バージョンを表示するコマンドを実行しています。

2. Ansibleの構成

2.1. Ansibleの構成要素

2.2. Ansible用hostsファイル

Ansible用hostsファイル(/etc/ansible/hosts)は、AnsibleおけるDefaltのInventoryとして使用され、以下のように同一の設定をする[WebServerGroup]等のグループに分けて記述すると、【playbook】で実行する内容が簡素化することが可能となる。

もちろん、各サーバ毎に違う設定がある場合は、サーバ毎に記載する必要もあるのできちんと運用ルールを設け設定する必要があります。

Ansibleを実行するサーバは、ローカルなのでansible_connection=localと設定する必要があります。ちなみにdafaultsshとなっている。

ホスト名は、以下の[DBServerGroup]や [APServerGrroup]のように正規表現でも記述することができます。

接続するポートに関しても、ホスト名の後に:で繋げ指定することが可能。その他にansible_portで指定することも可能となっている。ちなみにdefaultansible_portはSSH接続が前提となっているため22となっている。

また、以下の[GatewayServerGroup]のようにansible用の仮のホスト名を指定し、【ansible_host】で実際のIPを指定することも可能です。

  [localhost]
  127.0.0.1 ansible_connection=local
  
  [WebServerGroup]
  192.168.1.100
  192.168.1.101
  192.168.1.102
  
  [DBServerGroup]
  DB-[a:f].example.com:5501
  [APServerGrroup]
  www[01:10].example.com
  [GatewayServerGroup]
  Gateway01 ansible_port=1022 ansible_host=192.168.101.92
  

DefaltのInventory(/etc/ansible/hosts)以外のファイルを使用する場合は、ansibleコマンドのオプションとして-i(--inventory)を指定するか、ANSIBLE_HOSTS環境変数でパスを指定する必要がある。DefaltのInventory以外の指定したファイルであれば、YAML形式で記述することも可能です。

詳細は、公式Documents【Working with Inventory】を参照。

なお、Windowsとの接続する場合は、クライアント側の前提としてWindows Remote Management(WinRM)が起動している必要がある。

[windows]
192.168.99.3
ansible_ssh_user=<Windows側のユーザ名>
ansible_ssh_pass=<Windows側ユーザのパスワード>
ansible_ssh_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

2.3. playbook設定

hosts

Ansible用hostsファイルで指定したグループ名を指定する。 allを指定することでAnsible用hostsファイルに記述された全てのサーバ対象で同じ内容を実行します。

remote_user

リモート側の実行ユーザを指定する。 Ansibleは、SSH接続なので実行ユーザに鍵認証を設定している必要がある。

sudo(become※1)

sudo実行する場合は、yesを指定する。 sudo_userでsudoで実行するユーザを指定する必要がある。 また、ansibleコマンド実行時に-s(--sudo)を指定することも可能。 sudo実行するモジュール(task)毎に指定することが可能です。 ※1:Ansible1.9以降はbecomeを使用すること。

sudo_user(become_user※2)

sudo実行する際の実行ユーザを指定。 ※2:Ansible1.9以降はbecome_userを使用すること。

become_method

sudo実行ではなく、suを使って権限を変更したい場合は、become_methodsuを指定する。 ちなみに、Defaultのbecome_methodは、sudoとなっている。

公式ドキュメント(become)

vars

モジュール(task)を実行する際に設定して変数をここで宣言する。 以下のように宣言しておくと ${HOME}${INI_File}${TmpDir} などで参照することが出来ます。

  vars:
    HOME: "/home/hogeuser"
    INI_File: "/etc/local/ansible_local.ini"
    TmpDir:/tmp

handlers

taskで状態が変更された場合(changed のイベントが起こった場合)に1回だけ最後に実行されるジョブです。 task側では notify を設定し、 handler側では listen でそれを待ち受け、紐づけている。

- hosts: localhost

handlers:
  - name: TEST handlers JOB
      msg:execute handler
      listen:Start handler A

tasks:
  - name: TEST JOB
      msg: message
      changed_when: true
      dest: /foo/bar/
      notify: Start handler A

tasks

playbook のキモとなる項目です。実行するモジュールを列挙していきます。 name 指定すると実行時に出力が為されます。また、name は必須ではないようです。書かなかった場合、モジュール部分の文字列がそのまま出力されるようです。

各taskの詳細モジュールは、【2.5.モジュール(task)】を参照。

when

指定した条件が成り立つときのみタスクを実行する。

以下の場合は、ファイルコピーの結果をregisterに格納しで成功と失敗で処理を分けている。

- copy: src=/etc/hosts dest=/etc/hosts.old
  register: result
  ignore_errors: True

- debug: msg='File copy succeeded(succedded)'
  when: result|succeeded

- debug: msg='File copy failed(failed)'
  when: result|failed

with_items

ループ処理。

以下の場合は、記述した配列を変数に代入して[/tmp/test1]、[/tmp/test2]、[/tmp/test3]というディレクトリを作成する。

- name: ディレクトリの作成
  file: path=/tmp/ state=directory
  with_items:
    - test1
    - test2
    - test3

コマンド実行結果を with_items でループするには、コマンド実行した結果を登録(register)しwith_itemsの変数に代入する。

- name: retrieve the list of home directories
  command: ls /home
  register: home_dirs

- name: add home dirs to the backup spooler
  file: path=/mnt/bkspool/ src=/home/ state=link
  with_items: home_dirs.stdout_lines
  # same as with_items: home_dirs.stdout.split()

until

ループ処理。

untilがloopから抜ける条件(以下の場合は標準出力に”OK”が出力されるまでループ)

retriesがリトライ回数

delayがスリープの秒数

- shell: /u
sr/bin/foo
  register: result
  until: result.stdout.find("OK") != -1
  retries: 5
  delay: 10

import_playbook

指定したファイルを部品化して呼び出す。 タスク毎に小分けしたplaybookを作成してincludeするのが各playbookを修正した場合影響度が小さくて済む。

- import_playbook: include_1.yml

name

各項目のコメントに使用。playbookの概要などを簡潔に記載しておくと後で見た時に分かり易い。

- name: install via yum
  yum: name= state=installed
  with_items:
    - bash
    - git
    - ntp

msg

標準出力にメッセージを出力する。

デバッグする際のポインターとして利用すると便利。

サンプルは、debugを参照。

debug

デバックする際に付与すると内容確認できる。

以下の例は、registerに変数が代入されているか確認しています。

ansible-playbookコマンド実行時に-vもしくは-vv-vvvを付与しても同様にデバック可能。


  - name: check file exists
    stat:
      path: /etc/hosts
    register: res_exists
 
  - name: debug var res_exists
    debug:
      msg: ""

2.4.モジュール(task)

shell

シェル上でコマンドを実行。

shell: sample_script.sh arg1 arg1 >> sample_log.log

command

リモートノードでコマンドを実行する。

[/path/to/database]が存在すれば、シェルを実行する。

- command: /usr/bin/make_database.sh arg1 arg2 creates=/path/to/database

file

ファイルやディレクトリの作成、パーミッション設定 ただし、以下はstatelinkとなっているため、シンボリックリンクとなる。

fileの場合は、Ansibleサーバがソースになるので注意すること。

- file:
    src=/usr/local/foo.txt    
    dest=/opt/foo.txt
    owner=root
    group=root
    mode=0755
    state=link

lineinfile

ファイルの行単位の書き換え。

以下の場合は、先頭SELINUX=の行をSELINUX=enforcingに書き換える。

- lineinfile:
    path: /etc/selinux/config
    regexp: '^SELINUX='
    line: 'SELINUX=enforcing'

template

テンプレートを利用したファイルのコピー。テンプレートファイルは、/etc/ansible/templates/に配置する必要がある。

- template:
    src=template.j2 
    dest=/opt/file.conf 
    owner=root 
    group=root 
    mode=0644

synchronize

「rsync起動ホスト(Ansibleサーバ)」から「操作対象ホスト(Inventoryで指定したホスト)」へのファイルやディレクトリのコピーを行う。

- synchronize:
    src=source_dir
    dest=/tmp/target_dir
    recursive=yes

copy

- copy:
    src=foo.txt    
    dest=/tmp/foo.txt
    owner=root
    group=root
    mode=0755

yum

yumパッケージマネージャによるパッケージのインストール、アップグレード、削除

yum: name=httpd state=latest

その他

その他のモジュールは、Ansible モジュール一覧を参照。

2.5. Best Practices

Ansible公式サイトで更改しているベストプラクティスは、以下の構成としている。 この辺は、実際の現場の環境や運用ルールに合わせて、作っていくしかないだろう。

/etc/ansible/
├inventories/
│ ├production/
│ │ ├hosts # 本番環境サーバ用のインベントリファイル
│ │ ├group_vars/ │ │ │ ├group1.yml # 特定のグループに変数を割り当て
│ │ │ └group2.yml
│ │ └host_vars/
│ │ ├hostname1.yml # 特定のシステムに変数を割り当てます
│ │ └hostname2.yml
│ │
│ └staging/
│ ├hosts # 開発環境サーバ用のインベントリファイル
│ ├group_vars/
│ │ ├group1.yml # 特定のグループに変数を割り当て
│ │ └group2.yml
│ └host_vars/
│ ├stagehost1.yml # 特定のシステムに変数を割り当てます
│ └stagehost2.yml

├library/ # カスタムモジュールを置きます(オプション)。
├module_utils/ # module_utilsを置きます(オプション)。
├filter_plugins/ # カスタムフィルタプラグインを配置します(オプション)。

├site.yml # master playbook
├webservers.yml # playbook for webserver tier
├dbservers.yml # playbook for dbserver tier

└roles/
├common/ # この階層は「役割」を表します
│ ├tasks/
│ │ └main.yml
│ ├handlers/
│ │ └main.yml
│ ├templates/
│ │ └ntp.conf.j2
│ ├files/
│ │ └source files # コピーリソースで使用するファイル
│ │ └script files # スクリプトリソースで使用するスクリプトファイル
│ ├vars/
│ │ └main.yml # このロールに関連する変数
│ ├defaults/
│ │ └main.yml # このロールのデフォルトの優先順位の低い変数
│ ├meta/
│ │ └main.yml # ロール依存関係
│ ├library/ # カスタムモジュールも含めることができます
│ ├module_utils/ # カスタムmodule_utilsも含めることができます
│ └lookup_plugins/ # 検索のように、または他の種類のプラグイン
├webtier/ # 「common」と同じ構造
├monitoring/
└fooapp/

3. 実行コマンド

3.1. ansibleコマンド

Ansibleで何か一時的な処理をおこなう時のコマンド。

3.2.ansible-playbookコマンド

一連の処理の流れを YAML で記述しておいて、それを実行するコマンド。

3.3.ansible-docコマンド

最後に

Ansibleの特性上、インフラの各設定を実施することからOSの設定及びMWのインストールまでは、【root】ユーザで実施し、MWの設定はMW用のユーザで実施すべきだろう。

そのため、SSH鍵認証ではplaybookを実行するユーザに秘密鍵認証を配置し、実行されるクライアント側に公開鍵を配置することを忘れないようにすること。

また、Ansibleのplaybookに使用される【キー】(設定項目)がバージョンによって、名前自体が変わってしまうモノもあるのでバージョンアップする際は、注意が必要です。

構成管理ツールは、確かにサーバが数百台ともなれば、何かしらのツールは必要だとは思いますが、仮想マシンの作成やOSインストール、IPの設定までをAnsibleではできません。

そのため、現状私には必要性を強く感じられません。(シェルで十分)

次の課題としてOSインストールを含めたツールを検証し、構成管理ツールの必要性を検証しなければならない考えてます。