インフラエンジニアbacchiのわかったことまとめ

bacchi.me

Ansibleでrootにrvmをインストールし、playbookでrubyやgemを実行するとパッケージ版のrubyやgemが使われる件

Ansibleでrootユーザーにrvmを入れて、gemをインストールするタスクでどハマりしたのでまとめ。

クライアントはCentOS7です。

Ansibleのバージョンは以下の通り。

$ ansible --version
ansible 1.8.4 (release1.8.4 ebc8d48d34) last updated 2015/04/11 16:37:11 (GMT +900)
  lib/ansible/modules/core: (detached HEAD 5f58240d17) last updated 2015/04/11 16:34:18 (GMT +900)
  lib/ansible/modules/extras: (detached HEAD 4048de9c1e) last updated 2015/04/11 16:34:25 (GMT +900)
  v2/ansible/modules/core: (detached HEAD 34784b7a61) last updated 2015/04/11 16:34:34 (GMT +900)
  v2/ansible/modules/extras: (detached HEAD 21fce8ac73) last updated 2015/04/11 16:34:41 (GMT +900)
  configured module search path = /usr/share/ansible

だめだったパターン

以下のようなPlaybookを用意して、

## role/rvm/task/main.yml
# prepare for building Ruby
- name: be sure ruby basic package is installed
  yum: name={{ item }} conf_file=/etc/yum.conf.noexclude enablerepo=epel state=present
  with_items:
    - gcc-c++
    - patch
    - readline
    - readline-devel
    - zlib
    - zlib-devel
    - libyaml-devel
    - libffi-devel
    - openssl-devel
    - make
    - bzip2
    - autoconf
    - automake
    - libtool
    - bison
    - sqlite-devel
    - postgresql-devel
  ignore_errors: yes

# install RVM and Ruby
- name: install rvm
  shell: curl -sSL https://get.rvm.io | bash creates=/usr/local/rvm/LICENSE
  sudo: yes
  changed_when: False

- name: check ruby version
  command: /bin/bash -l -c "which ruby"
  register: ruby_version_check
  changed_when: '"/usr/local/rvm/rubies/ruby-{{ rvm_ruby_version }}/bin/ruby" not in ruby_version_check.stdout'
  sudo: yes
  ignore_errors: yes
  always_run: yes

- name: install ruby
  command: /bin/bash -l -c "rvm install {{ rvm_ruby_version }} && rvm --default {{ rvm_ruby_version }}"
  sudo: yes
  when: ruby_version_check|changed
  notify:
    - rvm_cleanup

- name: install postgres(gem)
  command: gem install postgres -v 0.7.9.2008.01.28 creates=/usr/local/rvm/gems/ruby-{{ rvm_ruby_version }}/gems/postgres-0.7.9.2008.01.28
  sudo: yes

----------------

## host_vers/test-server
rvm_ruby_version: "1.8.7-p374"

ansible-playbookコマンドを実行したところ、以下のようなエラーが出現しました。

failed: [test.bacchi.me] => {"changed": true, "cmd": ["gem", "install", "postgres"], "delta": "0:00:06.624956", "end": "2015-05-13 19:27:08.330638", "rc": 1, "start": "2015-05-13 19:27:01.705682", "warnings": []}
stderr: ERROR:  Error installing postgres:
        ERROR: Failed to build gem native extension.

    /usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /usr/share/include/ruby.h


Gem files will remain installed in /usr/local/share/gems/gems/pg-0.18.1 for inspection.
Results logged to /usr/local/share/gems/gems/pg-0.18.1/ext/gem_make.out
stdout: Building native extensions.  This could take a while...

FATAL: all hosts have already failed -- aborting

あれー、rubyのpathが /usr/bin/ruby になってる・・

rubyコマンドをフルパスで指定して、再度実行

rubyのパスがパッケージで入れたrubyのパスになっていたので、
rvmで入れたRubyのフルパスを指定して、再度ansible-playbookを実行。

playbookはこんな感じ。

- name: install postgres(gem)
  command: /usr/local/rvm/rubies/ruby-{{ rvm_ruby_version }}/bin/gem install postgres -v 0.7.9.2008.01.28 creates=/usr/local/rvm/gems/ruby-{{ rvm_ruby_version }}/gems/postgres-0.7.9.2008.01.28
  sudo: yes

しかし、同様のエラーが・・

failed: [test.bacchi.me] => {"changed": true, "cmd": ["/usr/local/rvm/rubies/ruby-1.8.7-p374/bin/gem", "install", "postgres"], "delta": "0:00:06.624956", "end": "2015-05-13 19:27:08.330638", "rc": 1, "start": "2015-05-13 19:27:01.705682", "warnings": []}
stderr: ERROR:  Error installing postgres:
        ERROR: Failed to build gem native extension.

    /usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /usr/share/include/ruby.h


Gem files will remain installed in /usr/local/share/gems/gems/pg-0.18.1 for inspection.
Results logged to /usr/local/share/gems/gems/pg-0.18.1/ext/gem_make.out
stdout: Building native extensions.  This could take a while...

FATAL: all hosts have already failed -- aborting

解決策

調べてみると sudo は.sudoを実行したユーザの環境変数が引き継がれないので、一工夫してやる必要がありました。

sudo の -i オプションを使って環境変数を読み込んでやれば /etc/profile を読み込んでくれて、  
rvmでインストールしたrubyを使ってくれるようになりました。

それを踏まえて、rubyのバージョン確認のタスクと、gemインストールのタスクを
sudo モジュールを「sudo: no」とし、rvmで入ったコマンドを叩く時に sudo -i env を
かませることで、想定通りの動きをしてくれるようになりました。

また、「sudo: no」を書いてやるのもポイントです。

gemをフルパスで記述したのにインストールに失敗した理由

フルパスでgemコマンド叩いた時もインストールできなかったのはgemがrubyを呼び出す時に、
環境変数を参照してrubyを呼び出しているため、パッケージ版のrubyが呼び出されていました。

おまけ 完成形のplaybook

## role/rvm/task/main.yml
# prepare for building Ruby
- name: be sure ruby basic package is installed
  yum: name={{ item }} conf_file=/etc/yum.conf.noexclude enablerepo=epel state=present
  with_items:
    - gcc-c++
    - patch
    - readline
    - readline-devel
    - zlib
    - zlib-devel
    - libyaml-devel
    - libffi-devel
    - openssl-devel
    - make
    - bzip2
    - autoconf
    - automake
    - libtool
    - bison
    - sqlite-devel
    - postgresql-devel
  ignore_errors: yes

# install RVM and Ruby
- name: install rvm
  shell: curl -sSL https://get.rvm.io | bash creates=/usr/local/rvm/LICENSE
  sudo: yes
  changed_when: False

- name: check ruby version
  command: sudo -i /bin/bash -l -c "which ruby"
  register: ruby_version_check
  changed_when: '"/usr/local/rvm/rubies/ruby-{{ rvm_ruby_version }}/bin/ruby" not in ruby_version_check.stdout'
  sudo: no
  ignore_errors: yes
  always_run: yes

- name: install ruby
  command: /bin/bash -l -c "rvm install {{ rvm_ruby_version }} && rvm --default {{ rvm_ruby_version }}"
  sudo: yes
  when: ruby_version_check|changed
  notify:
    - rvm_cleanup

- name: install postgres(gem)
  command: sudo -i gem install postgres -v 0.7.9.2008.01.28 creates=/usr/local/rvm/gems/ruby-{{ rvm_ruby_version }}/gems/postgres-0.7.9.2008.01.28
  sudo: no
  • B!