ちょっと仕事でAnsibleを使うことになったので勉強してみる。
コンピュータの性能が向上して、HWとしてのサーバ1台につき複数のOSを起動できるようになってきたのがゼロ年代の後半ぐらい。それまではインフラ担当者がセットアップしなければならいサーバというのは数台から十数台ぐらいまでだった。しかし、物理CPUを複数の論理CPUに分割してバーチャルマシン(VM)に割り当てられるようになると、それまで扱っていた数台のHWごとに10ぐらいのOSが起動するようになる。すると、インフラ担当者がセットアップするOSは一気に数十台規模になり、それまでと同じ手段でセットアップしていたのではやってられなくなった。
そこで、OSのセットアップ内容を記述したらそれで自動的にセットアップをしてくれる仕組みが登場した。ChefとかPuppetが代表的なものである。セットアップ内容を記述したものをプログラムコードと同じようにバージョン管理もできるようになった。さらに、ソフトウェア開発で当たり前になってきたCI/CDをこれらのセットアップコードに適用して、セットアップ内容のテストを自動化も出来るようになった。ChefやPuppetはRubyで書かれていたので、自然とこのテストはRubyのテストフレームワークであるRSpecで行うようになり、Serverspecというフレームワークが作られた。このようにソフトウェア開発と同じようにサーバ構成をCI/CDする手法を指して、Infrastracture as Code(IaC)と呼ばれるようになる。
さらに、時代はクラウドになり、サーバは欲しいときにいつでもプロビジョニングできるようになった。しかし、ChefやPuppetはあらかじめエージェントをインストールされたターゲットしか操作できない。自分で作ったVMイメージを使っている場合には問題にならないが、クラウドではそれは使いづらい。そこで、だいたいどこにでもあらかじめインストールされているPythonを使うことにした。コントロールサーバでPythonスクリプトを構成にあわせて生成し、SSHで送り込んで実行する。これがAnsibleである。
そして、IaCであるからには、Ansibleで作成した構成情報(Playbook)もCI/CDで管理されなければならない。そのためにはテストフレームワークが必要になるはずだ。
インフラから足を洗ったはずの私がなぜAnsibleをやっているプロジェクトに呼ばれたかというと、単にAnsibleのPlaybookを書いてくれというのではなく、CI/CDを前提としたIaCをAnsibleでやりたいのでソフトウェア開発の知見を活かして仕組みを組み立てて欲しいとのことだった。Ruby LoverなのでAnsibleに積極的ではなかったんでまるっと最初からお勉強しているところだ。
さっぱりわからないなりに数日ネットを彷徨った結果、AnsibleのテストフレームワークはMoleculeというものがあるらしい。2015年頃に出来たものなのでかなり新しめで、Playbookをロール(Playbookの構成要素)単位でテストすることが出来る。ものがインフラなので、普通のテストフレームワークのsetup/teardownをどうするかが問題になるわけだが、テスト実行単位でコンテナやVMを作るところまでセットでやってくれる。有り難い。
というわけで、まずはインストール。ひとまず、macOS上でテストにDockerを使う前提とする。 すべてPython上で動かすので、必要に応じてvirtualenvなどを使うこと。私は面倒でやらなかった。また今度泣くことにする。
よくわからないなりに順次いれていく。
まずは、ansible。pipで入れてもいいみたいだけど、Homebrewでいれた。brew install ansible
でOK。
次にmolecule。DockerとLintも使うので、pip install molecule[docker, lint]
でOK。
先にAnsibleの疎通確認をしておこう。
これを参考にする。
とりあえずは、localhostを操作するようにするので、localhostにsshできるようにする。
- [システム環境設定] - [共有] でリモートログインにチェックを入れる
ssh-copy-id -i キー名 localhost
ssh localhost
して接続出来ればOK
動作を確認。
> ansible-console --inventory localhost, --user tambara Welcome to the ansible console. Type help or ? to list commands. tambara@all (1)[f:5]$ ping [WARNING]: Platform darwin on host localhost is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information. localhost | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
ふむふむ
ヘルプ出してみる
tambara@all (1)[f:5]$ ? Documented commands (type help <topic>): ======================================== EOF dnf include_role service_facts add_host dpkg_selections include_tasks set_fact apt exit include_vars set_stats apt_key expect iptables setup apt_repository fail known_hosts shell assemble fetch lineinfile slurp assert file list stat async_status find meta subversion async_wrapper forks package systemd become gather_facts package_facts sysvinit become_method get_url pause tempfile become_user getent ping template blockinfile git pip unarchive cd group raw uri check group_by reboot user command help remote_user verbosity copy hostname replace wait_for cron import_playbook rpm_key wait_for_connection debconf import_role script yum debug import_tasks serial yum_repository diff include service tambara@all (1)[f:5]$ help user Manage user accounts Parameters: name Name of the user to create, remove or modify. uid Optionally sets the I(UID) of the user. comment Optionally sets the description (aka I(GECOS)) of user account. hidden macOS only, optionally hide the user from the login window and system preferences. non_unique Optionally when used with the -u option, this option allows to change the user ID to a non-unique value. seuser Optionally sets the seuser type (user_u) on selinux enabled systems. group Optionally sets the user's primary group (takes a group name). groups List of groups user will be added to. When set to an empty string C(''), the user is removed from all groups except the primary group. append If C(yes), add the user to the groups specified in C(groups). shell Optionally set the user's shell. home Optionally set the user's home directory. skeleton Optionally set a home skeleton directory. password Optionally set the user's password to this crypted value. state Whether the account should exist or not, taking action if the state is different from what is stated. create_home Unless set to C(no), a home directory will be made for the user when the account is created or if the home directory does not exist. move_home If set to C(yes) when used with C(home: ), attempt to move the user's old home directory to the specified directory if it isn't there already and the old home exists. system When creating an account C(state=present), setting this to C(yes) makes the user a system account. force This only affects C(state=absent), it forces removal of the user and associated directories on supported platforms. remove This only affects C(state=absent), it attempts to remove directories associated with the user. login_class Optionally sets the user's login class, a feature of most BSD OSs. generate_ssh_key Whether to generate a SSH key for the user in question. ssh_key_bits Optionally specify number of bits in SSH key to create. ssh_key_type Optionally specify the type of SSH key to generate. ssh_key_file Optionally specify the SSH key filename. ssh_key_comment Optionally define the comment for the SSH key. ssh_key_passphrase Set a passphrase for the SSH key. update_password C(always) will update passwords if they differ. expires An expiry time for the user in epoch, it will be ignored on platforms that do not support this. password_lock Lock the password (C(usermod -L), C(usermod -U), C(pw lock)). local Forces the use of "local" command alternatives on platforms that implement it. profile Sets the profile of the user. authorization Sets the authorization of the user. role Sets the role of the user.
なるほど。