文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

ansibleを学ぶ:vol04:よくハマる部分とその解決法

ansibleの勉強シリーズ第4回です。


f:id:treeapps:20160219001058p:plain
今回は「これは大体みんなハマるだろうなあ」という部分の解決策についてです。

パッケージのグループインストールしたいんですけど?

- name: install 'Development tools' package group
  yum: name="@Development tools" state=present
  sudo: yes

yum - Manages packages with the yum package manager — Ansible Documentation
「@」つけるとグループインストールになるんだよ!間にスペースが入るパッケージ名の時はちゃんとクォートで囲もうね!

CentOS7でファイヤーウォール止めたいんだけど?

- name: stop firewalld
  service: name=firewalld enabled=false state=stopped
  sudo: yes

こう書くと「service firewalld stop」や「systemctl stop firewalld」と同じになるんだよ!

apacheインストール時にバージョン指定したいんですけど?

↓こーやってパッケージのバージョン名を調べて、

[vagrant@node1 ~]$ sudo yum list httpd
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
 * base: www.ftp.ne.jp
 * extras: ftp.jaist.ac.jp
 * updates: www.ftp.ne.jp
インストール済みパッケージ
httpd.x86_64                2.4.6-31.el7.centos                 @base

↓こーやって指定するんだよ!nameの後ろにハイフン付けてバージョン名を付けてね!

- name: install httpd
  yum: name={{ item }} state=present
  with_items:
    - httpd-2.4.6-31.el7.centos
    - httpd-devel-2.4.6-31.el7.centos
    - apr-devel
    - apr-util-devel
    - openssl-devel

Package name, or package specifier with version, like name-1.0.

http://docs.ansible.com/yum_module.html

gitモジュール使ってみたらエラー起きたんですけど?

例えばansibleでrbenvをgit cloneしようとしたらこういうエラーが起きました。

TASK: [rbenv | install rbenv] *************************************************
failed: [192.168.33.21] => {"failed": true}
msg: github.com has an unknown hostkey. Set accept_hostkey to True or manually add the hostkey prior to running the git module

FATAL: all hosts have already failed -- aborting
- name: install ruby build
  git: repo=git://github.com/sstephenson/ruby-build.git dest=/home/{{ user }}/.rbenv/plugins/ruby-build update=no accept_hostkey=yes

こんな感じに「accept_hostkey=yes」を付けてあげるとエラーが解消するよ!

rbenvのパスと初期化コマンドが・・・

$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

こう書くのもぅマヂ無理。 フラグメント化しょ。。。
treeapps.hatenablog.com

bundlerは .rbenv 配下にインストールしたいんですけど?

- name: install bundler
  gem: name=bundler executable=.rbenv/shims/gem user_install=no

こう書いてあげるとちゃんと.rbenv配下にインストールされるよ!

rbenvで指定したrubyが既にインストール済みか調べたいんですけど?

これは頑張るしか無いですね。私はこうしてるよ!

- name: check whether a specific version of ruby is installed
  shell: rbenv versions | grep {{ ruby_version }} | tr '*' ' ' | sed -e 's/\s\+//' | cut -f1 -d ' '
  register: rbenv_ruby_version

- name: install ruby
  shell: rbenv install {{ ruby_version }}
  when: rbenv_ruby_version.stdout != '{{ ruby_version }}'

↑こうやったら「rbenv: command not found」って言われた!!

例えば

- name: install ruby
  shell: rbenv install {{ ruby_version }}
  when: rbenv_ruby_version.stdout != '{{ ruby_version }}'

↑こう書いたら、↓こういうエラーが置きます。

TASK: [rbenv | install ruby] **************************************************
failed: [192.168.33.21] => {"changed": true, "cmd": "rbenv install 2.1.6", "delta": "0:00:00.002443", "end": "2015-05-22 01:08:44.790851", "rc": 127, "start": "2015-05-22 01:08:44.788408", "warnings": []}
stderr: /bin/sh: rbenv: command not found

FATAL: all hosts have already failed -- aborting

以下の記事に原因と対策をまとめたよ!
treeapps.hatenablog.com

gemでインストールする時 .rbenv 配下にインストールしたいんだけど?

- name: install passenger
  gem: name=passenger executable=.rbenv/shims/gem user_install=no

これで .rbenv 配下にインストールされるよ!

subversionでcheckout済みかどうか調べたいんだけど?

statモジュールが使えるよ!
stat - Retrieve file or file system status — Ansible Documentation

完璧でないけど私はこうやってるよ!

- name: register redmine is installed
  stat: path={{ redmine_dir }}
  register: redmine_checkouted

- name: svn checkout redmine branches
  subversion: repo={{ redmine_svn_branches_url }} dest={{ redmine_dir }}
  when: redmine_checkouted.stat.exists == false
  sudo: yes

.svnをstatしてもいいんですが、それだと .svn フォルダが無いけど redmine フォルダがあると既にチェックアウト対象が存在するよエラーが起きるので、こうしてるよ!

registerした値をechoして確認したいんですけど?

面倒だけどこうするんだよ!

- debug: var=redmine_checkouted.stdout

debug - Print statements during execution — Ansible Documentation

sudoersを編集してsudoできるようにしたいんだけど?

- name: edit sudoers
  lineinfile: "dest=/etc/sudoers backup=yes state=present regexp='^{{ user }}' line='{{ user }} ALL=(ALL) ALL'"
  sudo: yes

user変数に追加したい変数を設定して下さい。

ちなみにこれで追加すると、2回連続実行しても追記され続ける事なく、冪等性が保たれます。

sshするユーザをplaybook毎に変えたいんだけど?

なぜplaybook毎に変えたいかというと、例えば以下のような状況があるからです。

  • vagrantユーザでworkユーザを作成
  • workのホームにrbenvをインストール
  • workユーザでrbenv install 2.2.2 等としてrubyをインストール

しかしこれを以下のようにしてしまうと失敗します。

  • vagrantユーザでworkユーザを作成
  • vagrantユーザでsshし、workユーザでworkのホームにrbenvをインストール
  • vagrantユーザでsshし、workユーザでrbenv install 2.2.2 等としてrubyをインストール

この場合、sshしたvagrantユーザでworkユーザのホームディレクトリにrbenvをインストールしようとすると、当然Permission deniedになります。なのでsudoしたいところですが、sudoしてしまうと今度は環境変数が引き継がれず、command not foundのようになります。(remote_userで実行ユーザを変えても、別ユーザのhomeを触ろうとすると権限が無いのでPermission Deniedになる)

こうなってしまう元凶は「vagrantユーザでsshログインしている」点にあるので、ここを「workユーザでsshログインする」事ができれば、Permissionの問題も無くなります。

この問題を解決するために、playbookを2つに分けます。playbook1では単にユーザ・グループを追加するだけ(vagrantユーザでssh)、playbook2は他の処理全て(workユーザでssh)、と分割すれば解決します。ではどうやってplaybook毎にsshするユーザを変更できるでしょうか。以下のようにすると、sshユーザを上書きする事ができます。

inventry

[all:vars]
# ユーザ・グループを追加するためだけのsshユーザ
custom_ssh_user=vagrant
custom_ssh_pass=vagrant
# 本来の処理を行う時のデフォルトsshユーザ
ansible_ssh_port=22
ansible_ssh_user=work
ansible_ssh_pass=work
ansible_sudo_pass=work

playbook1

- hosts: test
  sudo: no
  vars_files:
    - group_vars/common.yml
  vars:
    - ansible_ssh_user: '{{ custom_ssh_user }}'
    - ansible_ssh_pass: '{{ custom_ssh_pass }}'
  roles:
    - user

playbook2

- hosts: test
  sudo: no
  vars_files:
    - group_vars/common.yml
  roles:
    - rbenv
    - pyenv

このように、playbook内のvarsで「ansible_ssh_user」と「ansible_ssh_pass」を上書きする事ができるのです。上書きしなければ、インベントリで定義した「ansible_ssh_user」「ansible_ssh_pass」が使用されます。

今回はvagrantの例でしたが、awsでec2インスタンスを作成した時に使う「ec2-user」でも同じ事が言えるかと思います。

雑感

この記事はredmineのplaybookを書いていて学んだものです。yum等の場合は複数回実行しても「既にインストール済みなのでスキップ」という処理を自動でやってくれるんですが、svnが既にチェックアウトと済みかどうか調べないと、毎回毎回coされて時間かかって「もぅマヂ無理。statしょ」という結論に行き着いたわけです。

ちなみにstatモジュールですが、これはansibleを使ううえで必ず使うものなので、絶対覚えておきましょう。「このフォルダがあれば」「あのファイルがあれば」は基本的にstatでやります。「ls xxx | wc -l」とかで数えたりする人は骨付き肉持った原始人なので、ちゃんとstatを使ってイケメンになって下さい。
stat - Retrieve file or file system status — Ansible Documentation

playbookを書いていて思ったんですが、みんなsudoの初期値はyesなんですかね。今回rbenvを使っているわけですが、rbenvを$HOME/.rbenvにインストールするので、gemやbundleコマンドはsudoが不要なので、私はsudoは基本noにしておいて、yumする時とかに都度sudo:yesしました。sudoでgemコマンドとか使おうとすると、.bash_profileのパスの環境変数が引き継がれなかったりして面倒なので、rbenvはsudoしなくていい場所にインストールするのが楽かと思います。


redmineのインストールplaybook作成は本当にansibleの基本を学ぶいい教材になり得るので、ぜひやってみて下さい!

ん? もうそのredmineのplaybookくれって?(今中の人はredlineのplaybookをgithubにあげようか迷い中なのです)

↓githubにアップしてみた
github.com

Ansible実践ガイド 第2版 (impress top gear)

Ansible実践ガイド 第2版 (impress top gear)

ansibleを学ぶ:vol01:vagrantを使ってansibleを動かしてみる - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol02:Oracle JDK1.8のインストール、ユーザパスワード設定等 - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol03:実行中のホスト名を書き込む、変数の値に変数を使用する - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol04:よくハマる部分とその解決法 - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol05:対象サーバのid_rsa.pubを対象サーバのauthorized_keysに登録する - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol06:mysql-serverのインストールからcreate databaseまでを自動化する - 文系プログラマによるTIPSブログ
ansibleを学ぶ:vol07:figletで動的にホスト名をmotdに書き込んでニヤニヤする - 文系プログラマによるTIPSブログ