akishin999の日記

調べた事などを書いて行きます。

lightline.vim で Ricty を使う

vim のステータスライン用のプラグインvim-powerline から lightline.vim に変更してみました。
ついでにフォントも Ricty に powerline 用パッチを当てたものに変更したのでやり方をメモ。

作業環境は Ubuntu 13.04 32bit です。

Ricty をビルドする

Ricty はライセンスの関係で TrueType フォント形式では配布されておらず、生成スクリプトのみが公開されている状態なので、まずはフォントをビルド。

fontforge をインストール。

% sudo apt-get install fontforge

Inconsolata の OpenType file をダウンロード。

% wget http://levien.com/type/myfonts/Inconsolata.otf

Migu 1M をダウンロード。
MigMix ではないので注意。

% wget http://iij.dl.sourceforge.jp/mix-mplus-ipa/59022/migu-1m-20130617.zip
% unzip migu-1m-20130617.zip

Ricty 生成用のスクリプトを取得。

% git clone https://github.com/yascentur/Ricty.git

Ricty の生成。

% cd Ricty
% ./ricty_generator.sh ~/tmp/Inconsolata.otf ~/tmp/migu-1m-20130617/migu-1m-regular.ttf ~/tmp/migu-1m-20130617/migu-1m-bold.ttf

Ricty をインストール。

% mv Ricty*.ttf ~/.fonts
% fc-cache -fv

Ricty にパッチを当てる

lightline.vim では powerline 用か vim-powerline 用のパッチが当たったフォントが使えるので、パッチを当てたフォントを作成しインストール。

  • powerline に同梱の fontpatcher.py を使う場合
% git clone https://github.com/Lokaltog/powerline.git
% fontforge -script $HOME/tmp/powerline/font/fontpatcher.py $HOME/.fonts/Ricty-Regular.ttf
% fontforge -script $HOME/tmp/powerline/font/fontpatcher.py $HOME/.fonts/Ricty-Bold.ttf
% mv Ricty\ Regular\ for\ Powerline.ttf ~/.fonts/
% mv Ricty\ Bold\ for\ Powerline.ttf ~/.fonts
% fc-cache -fv
  • vim-powerline に同梱の Powerline font patcher の方を使う場合
% git clone https://github.com/Lokaltog/vim-powerline.git
% fontforge -lang=py -script ./vim-powerline/fontpatcher/fontpatcher $HOME/.fonts/Ricty-Regular.ttf
% fontforge -lang=py -script ./vim-powerline/fontpatcher/fontpatcher $HOME/.fonts/Ricty-Bold.ttf
% mv Ricty-Regular-Powerline.ttf ~/.fonts 
% mv Ricty-Bold-Powerline.ttf ~/.fonts 
% fc-cache -fv

lightline.vimRicty を使う

lightline.vim を NeoBundle でインストール。
.vimrc に以下を書いて :NeoBundleInstall 実行。

NeoBundle 'itchyny/lightline.vim

.vimrc に以下の設定が無い場合は追加。

scriptencoding utf-8
set encoding=utf-8

guifont で作成した Ricty を指定。

set guifont=Ricty\ 10

以下の lightline.vim 用の設定を追加。
公式の README.md に載っているものそのままです。
手元の環境では vim-powerline 用のパッチを当てたフォントを使用しているので以下のようにしました。

let g:lightline = {
      \ 'colorscheme': 'wombat',
      \ 'component': {
      \   'readonly': '%{&readonly?"x":""}',
      \ },
      \ 'separator': { 'left': "\u2b80", 'right': "\u2b82" },
      \ 'subseparator': { 'left': "\u2b81", 'right': "\u2b83" }
      \ }

powerline 用のパッチを当てたフォントを使っている場合は以下のようになるはずです。

let g:lightline = {
      \ 'colorscheme': 'wombat',
      \ 'component': {
      \   'readonly': '%{&readonly?"x":""}',
      \ },
      \ 'separator': { 'left': "\ue0b0", 'right': "\ue0b2" },
      \ 'subseparator': { 'left': "\ue0b1", 'right': "\ue0b3" }
      \ }

結構いろいろやった気がしますがこれで必要な作業は一通り完了です。
gvim を再起動してみると以下のような見た目になりました。

いい感じ。
lightline.vim は作者の方が丁寧な解説記事を書かれているので、じっくり読みながらいろいろとカスタマイズしていってみたいですね。

参考

lightline.vim作りました - プラグインの直交性について - プログラムモグモグ
http://d.hatena.ne.jp/itchyny/20130824/1377351527

作者が教える! lightline.vimの導入・設定方法! 〜 初級編 - インストールしよう - プログラムモグモグ
http://d.hatena.ne.jp/itchyny/20130828/1377653592

作者が教える! lightline.vimの設定方法! 〜 初級編 - コンポーネントを作ってみよう - プログラムモグモグ
http://d.hatena.ne.jp/itchyny/20130917/1379369171

作者が教える! lightline.vimの設定方法! 〜 中級編 - 展開コンポーネントを理解しよう - プログラムモグモグ
http://d.hatena.ne.jp/itchyny/20130918/1379461406

Ansible で標準のパス以外の場所に入れた Python を使用する

Ansible はデフォルトではリモートのホストに入っている標準の python を使用します。
これを virtualenv 環境など、標準のパス以外の場所の python を使いたい場合には、ansible_python_interpreter 変数で python インタプリタのパスを指定するようです。

List of Behavioral Inventory Parameters
http://docs.ansible.com/ansible/latest/intro_inventory.html#list-of-behavioral-inventory-parameters

vars:
  ansible_python_interpreter="/root/python-venv/bin/python"

ただ、手元の CentOS 6.4 環境で、この方法で virtualenv 環境の python を指定したところ、yum モジュールを使った箇所で以下のようなエラーが出てしまいました。

TASK: [install mysql-server] **************************************************
failed: [192.0.2.1] => {"failed": true, "parsed": false}
invalid output was: Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-1378480574.36-133423455605286/yum", line 26, in <module>
    import yum
ImportError: No module named yum


FATAL: all hosts have already failed -- aborting

ここ以外は上手く動くようなので、yum モジュールまでは標準の python、virtualenv 環境を使う時だけそちらの python、という感じで指定できれば回避できそう。
何か方法はないかと調べてみたところ、1.2 から追加された set_fact を使う事で回避することができました。

set_fact - Set host facts from a task
http://docs.ansible.com/ansible/latest/set_fact_module.html

以下のような感じで、virtualenv 環境を使いたい task の直前で set_fact を使用して ansible_python_interpreter 変数を指定してやります。

・
・
・
- name: install mysql-server
  yum: name=mysql-server state=installed

- name: start mysql
  action: service name=mysqld state=started enabled=yes

# ここで python インタプリタを切り替え
- name: set ansible_python_interpreter
  set_fact: ansible_python_interpreter="/root/python-venv/bin/python"

- name: create mysql_python_test database
  mysql_db: name=mysql_python_test state=present encoding=utf8
・
・
・

これで先ほどのエラーを回避でき、virtualenv 環境に入れた mysql-python を使用して mysql_db モジュールを実行することが出来ました。

以下に実際の playbook を置いておきます。

https://github.com/akishin/ansible-playbooks/blob/master/snippets/virtualenv-mysql-python.yml

それほど使う機会もないかと思いますが、何かの参考になれば幸いです。

GitLab 5.4 から 6.0 へアップデートする

GitLab 6.0 がリリースされました。

グループ機能を強化した「GitLab 6.0」リリース、有償版の提供も開始
http://sourceforge.jp/magazine/13/08/23/140000

Fork したプロジェクトからオリジナルへの MergeRequest が可能になるなど、より GitHub に近付いた感じですね。
というわけで早速前回インストールした 5.4 を 6.0 にアップデートしてみました。

基本的には以下のドキュメントに従って作業をしていきます。

gitlabhq/doc/update/5.4-to-6.0.md
https://github.com/gitlabhq/gitlabhq/blob/master/doc/update/5.4-to-6.0.md

バックアップ

まずは現在のデータのバックアップを行います。

# cd /home/git/gitlab
# sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create

と思ったら「Dumping uploads」で「No such file or directory - /home/git/gitlab/public/uploads」エラー。
どうやら構築手順で漏れていましたが、public/uploads ディレクトリが必要なようです。

なのでここで一旦作成し、再度バックアップを実行します。

# mkdir /home/git/gitlab/public/uploads
# chown git:git /home/git/gitlab/public/uploads
# sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create

今度は上手く行きました。
バックアップファイルは以下の場所に作成されるようです。

# ll /home/git/gitlab/tmp/backups
合計 80
-rw-rw-r--. 1 git git 40960  823 21:00 2013 1377259246_gitlab_backup.tar
-rw-r--r--. 1 git git 40960  823 21:02 2013 1377259375_gitlab_backup.tar

サーバの停止

次にアプリケーションサーバを停止します。
ここでは Apache + Passenger で動かしているので httpd と、別のサービスとして登録していた sidekiq の二つのサービスを停止しました。

# service httpd stop
# service sidekiq stop

最新のコードの取得

gitlab のリポジトリを更新します。

# cd /home/git/gitlab
# sudo -u git -H git fetch
# sudo -u git -H git checkout 6-0-stable

gitlab-shell の更新

gitlab-shell の方も更新します・・・とありますが、前回の手順で既にドキュメントで指定されているバージョン 1.7.0 を使用していたのでスルーしました。

追加パッケージのインストール

python-docutils パッケージを追加でインストールします。
yum でも同名で提供されていました。

# yum install -y python-docutils

ライブラリのインストールとマイグレーション

まずは bundler で追加されたライブラリをインストールします。
MySQL を使用しているので postgres グループは除外しています。

# cd /home/git/gitlab
# sudo -u git -H bundle install --deployment --without development test postgres

以下の rake タスクを実行して各種データのマイグレーションを行います。

# sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
# sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
# sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production
# sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production

GitLab 6.0 からは global な namespace のプロジェクトが deprecated になったので、 migrate_global_projects 実行時には自動でプロジェクトオーナーの namespace 以下に移動させて良いか訊かれます。
後で手動で移動させる事もできるので、その辺りはチェックアウトしているユーザへの影響など考慮しながら適宜判断する感じでしょうか。
ここでは面倒なのと一人用 GitLab なので yes を選択しました。

その他も db:migrate 以外では実行可否を聞かれますが、特に問題無さそうなので全部 yes を選択しました。
どれも新しいバージョンでの仕様変更に関する内容のようなので、古いバージョンから長いこと使用しているような場合には良く読んで慎重に選択した方が良さそうです。

最後に assets:precompile も忘れずに実行しておきます。

# sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production

設定ファイルの更新

config/gitlab.yml と config/unicorn.rb の差分を確認、とのことですが、ここでは Unicorn は使用していないので gitlab.yml だけ確認しました。

# diff config/gitlab.yml config/gitlab.yml.example
18c18
<     host: 192.0.2.1
---
>     host: localhost
20a21,22
>
>     # Uncomment and customize the last line to run in a non-root path
22,23c24,28
<     # Uncomment and customize to run in non-root path
<     # Note that ENV['RAILS_RELATIVE_URL_ROOT'] in config/puma.rb may need to be changed
---
>     # Note that three settings need to be changed for this to work.
>     # 1) In your application.rb file: config.relative_url_root = "/gitlab"
>     # 2) In your gitlab.yml file: relative_url_root: /gitlab
>     # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT']
>     #
99a105
>     allow_username_or_email_login: true

最後の allow_username_or_email_login は LDAP の設定でした。
LDAP は使っていないのと、それ以外特に違いは無さそうなのでこのままで良さそうです。

init スクリプトの更新

起動スクリプトは sidekiq の部分のみ抜き出したものを使用していますが、特に変わりも無さそうなので取りあえずそのまま。

アプリケーションの起動

いよいよ起動してみます。

# service sidekiq start
# service httpd start

ブラウザを起動してアクセスしてみるとログイン画面がこんな感じに。

思い切り 6 って書いてあります。
Help ページを確認してみると、こちらも無事 6.0.0 になっていました。

他にも Global な namespace で作成していたプロジェクトがオーナーの namespace 配下になっていたりと、どうやら今回のバージョンアップは上手く行ったようです。

まとめ

GitLab はバージョンが変わる毎に結構インストール手順が変わっちゃったりして追いかけるのが大変なんですが、リリースバージョン 1 つ差くらいだと案外簡単にバージョンアップできますね。
余り間が空くとどんどん面倒になりそうなので、使っている方は早めに思い切ってバージョンアップしてしまうといいかも知れません。

ついでに以前書いた Ansible の Playbook も 6.0.0 をインストールするように修正してみました。
新機能を気軽に試したい方はこちらを使ってみて下さい。

条件によって実行する task を定義する

when による条件分岐について調べたのでまとめておきます。

host の情報や他のコマンドの実行結果の値により実行する task を定義する事が出来ます。
task を実行するための条件は when 句で指定します。

when 句では変数を指定できるので、例えば --extra-vars を使用して以下のように外部から実行条件を指定する事ができます。

  • when.yml
---
- user: root
  hosts: all
  tasks:
    - name: install mlocate
      yum: name=mlocate state=installed
      when: install == "y"

when 句での変数の参照には ${} や {{}} は付けない事に注意。

実行時には以下のように変数 install の値を渡します。
「y」以外の値を渡すと task の実行が skip されることが分かります。

$ ansible-playbook when.yml --extra-vars "install=n" -i hosts -k

vars_prompt で入力させるようにすれば、実行時に task 実行可否を決めさせることも可能です。

  • when2.yml
---
- user: root
  hosts: all
  vars_prompt:
    - name: "install"
      prompt: "install mlocate?"
      private: no
      default: "y"
  tasks:
    - name: install mlocate
      yum: name=mlocate state=installed
      when: install == "y"

ansible では対象ホストの情報やインベントリで定義された情報についてアクセスするために自動的に定義されるいくつかの変数があります。
これらの変数の値を when 句で使用する事も可能です。

例えば以下ではインベントリファイルに定義したホスト名を取得する inventory_hostname を使って実行可否を切り替えています。

  • when3.yml
- user: root
  hosts: all
  tasks:
    - yum: name=mysql state=latest
      when: inventory_hostname != 'example01'

また、register を使用して他の task の実行結果を変数に格納しておき、その値を他の task の when 句で使用することも出来ます。

この挙動を試すため、対象サーバで以下のようなファイルを作成します。

# echo "bar" > /tmp/testfile

以下の Playbook では先ほど作成したファイルの有無で処理の実行を切り替えています。
試しにファイルを削除すると task が実行されなくなることが分かります。

  • when4.yml
- user: root
  hosts: all
  tasks:
    - command: test -f /tmp/testfile
      ignore_errors: true
      register: testfile_exist
    - lineinfile: dest=/tmp/testfile state=present regexp='^bar$' line=BAR
      when: testfile_exist.rc == 0

これらの条件付き実行タスクを使いこなす事でより一層柔軟な Playbook を定義することが出来るようになりそうです。

20分(くらい)で GitLab をインストールする方法

この間構築手順を書きましたが、改めて見ると GitLab の構築って結構手間がかかって面倒臭いですよね。
私の周りでも構築は面倒というような話をよく聞きます。

こんな時こそ自動化だ、という訳で Ansible で GitLab インストール用の Playbook 書いてみました。

https://github.com/akishin/ansible-playbooks/tree/master/gitlab

clone して hosts というファイルの中に対象サーバのホスト名か IP アドレスを記述します。

% git clone git://github.com/akishin/ansible-playbooks.git
% cd ansible-playbooks/gitlab
% vi hosts
# 対象サーバを記述

準備が出来たら ansible-playbook 実行。

% ansible-playbook site.yml -i hosts -k

Ansible さえインストールされていれば基本的にこれだけでしばらく待てば GitLab 環境の出来上がり。
ちなみに手元の仮想環境で time コマンド付きで実行してみた時は以下のような結果でした。

real    18m40.501s
user    0m4.991s
sys     0m2.327s

やっぱり Ruby 2.0 と Git 1.8 をソースコードからビルドしているのがボトルネックかなー。
RPM 作れるなら RPM からインストールするように変えればもっと早くなるはずです。

GitLab の 細かいカスタマイズなどは roles/gitlab/templates の下にあるテンプレートを弄ればある程度は出来ると思います。

Ansible 自体のインストール方法などはこちらに書いたので参考にしてみて下さい。
自分の環境でしか確認していないので、もし何かおかしい点などあればフィードバック頂けると嬉しいです。

これですぐに GitLab を使い始めることが出来ますね。

Playbook 実行時に任意の値を指定する

Playbook 内に直接値を定義せず、実行時に外部から値を渡したい場合、 --extra-vars 引数を使う方法と vars_prompt を使う方法があります。

--extra-vars

Passing Variables On The Command Line
http://docs.ansible.com/ansible/playbooks_variables.html#passing-variables-on-the-command-line

※ www.ansibleworks.com は使われなくなったようなので URL を修正

予め Playbook 内で変数を使用しておき、 ansible-playbook コマンドの実行時に --extra-vars オプションによって実際の変数の値を渡す事が出来ます。

Playbook は以下のような感じで定義します。

  • extra-vars.yml
---
- user: '{{ user }}'
  hosts: '{{ hosts }}'
  tasks:
    - name: install mlocate
      yum: name=mlocate state=installed

「user」と「hosts」の値をそれぞれ同名の変数を参照するようにしていますが、このファイル内では変数は定義していません。
これらの変数は Playbook 実行時に以下のように --extra-vars 引数の値として指定します。

$ ansible-playbook extra-vars.yml --extra-vars "hosts=all user=root" -i hosts -k

Ansible 1.2 からは以下のように JSON 形式で指定することも出来るようです。

$ ansible-playbook extra-vars2.yml --extra-vars '{ "foo":"FOO", "fruits":["apple", "cherry", "orange"] }' --connection=local

以下のように debug モジュールを使うと簡単に動作確認が出来ます。

  • extra-vars2.yml
---
- user: root
  hosts: all
  tasks:
    - debug: msg="{{ foo }}"

    - debug: msg="${item}"
      with_items: fruits

vars_prompt

Prompts
http://docs.ansible.com/ansible/playbooks_prompts.html#prompts

※ www.ansibleworks.com は使われなくなったようなので URL を修正

vars_prompt を使う場合、Playbook 内の vars_prompt セクションで外部から入力を受け付けたい変数を定義しておきます。

  • vars-prompt.yml
- user: root
  hosts: all
  vars_prompt:
    - name: "name"
      prompt: "Please enter your name"
      private: no
      default: "John Doe"
  tasks:
    - debug: msg="Hello, {{ name }}"


実行すると以下のように「prompt」で指定した「Please enter your name」というメッセージが表示され入力待ちになります。
任意の文字列を指定して Enter を押すと debug モジュールが実行されます。

$ ansible-playbook vars-prompt.yml --connection=local
Please enter your name:

vars_prompt では以下のようなオプションを使用できます。

オプション 意味
name 入力値を参照する際の変数名
prompt 入力を促すプロンプト文字列
default デフォルト値
private yes にするとエコーバックされない(パスワード等で使う)
confirm yes にすると確認のための再入力を求める

エコーバックしない private オプションや確認のための confirm オプションがあるので、パスワードの変更などに使うと良さそうです。

vars_prompt で入力された文字列でパスワードを変更する Playbook を書いてみると以下のような感じになりました。

- hosts: all
  user: root
  vars:
    username: johnd
    saltstr: saltstr
  vars_prompt:
    - name: "new_password"
      prompt: "Enter new password"
      confirm: true
      private: yes
  tasks:
    - name: "create password salt"
      shell: echo somesalt | /usr/bin/md5sum | awk '{print $1}'
      register: password_salt

    - name: "create new password hash"
      command: python -c 'import crypt; print crypt.crypt("{{ new_password }}", "$1${{ password_salt.stdout }}$")'
      register: password_hash

    - name: "change password"
      user: name={{ username }} password={{ password_hash.stdout }} state=present

どちらかというと環境構築用の Playbook では --extra-vars の方が使えそうですが、運用系の作業を Playbook 化する場合などは vars_prompt の方が適している場合もありそうですね。

Ansible で複数行の文字列置換

ansible で設定ファイル内の文字列を置換したかったので、 lineinfile モジュールを試してみました。

lineinfile
http://www.ansibleworks.com/docs/modules.html#lineinfile

対象サーバに以下のようなファイルを用意。

  • /tmp/test.conf
foo
bar
baz
bar
bar
bar
foo

このファイル内の文字列を置換してみます。

以下のような Playbook を作成しました。

  • lineinfile.yml
- hosts: all
  user: root
  vars:
    target_file: /tmp/test.conf
  tasks:
    - name: lineinfile example task
      lineinfile: dest=${target_file} state=present regexp='^bar$' line=BAR=99999

ここで lineinfile モジュールに指定しているパラメータは以下のような意味です。

  • dest ・・・ 編集する対象ファイルのパス
  • regexp ・・・ 対象行を指定する正規表現
  • line ・・・ 置き換える文字列

実行してみます。

$ ansible-playbook lineinfile.yml -i hosts -k

確認してみると以下のように、最後の "bar" だけが置換されている事がわかります。

# cat /tmp/test.conf
foo
bar
baz
bar
bar
BAR=99999
foo

複数行まとめて置換したい場合はどうやるんだろう?と思ってドキュメントを確認したところ、以下のように書いてありました。

only the last line found will be replaced.

http://www.ansibleworks.com/docs/modules.html#lineinfile

どうやら lineinfile モジュールでは、複数行マッチした場合は最後にマッチした行のみ置換されるようです。
他にも同じくドキュメントに

This is primarily useful when you want to change a single line in a file only. For other cases, see the copy or template modules.

http://www.ansibleworks.com/docs/modules.html#lineinfile

と書いてあったりするので、どうやら今のところ複数行の置換には対応していない模様(バージョン 1.2.2 で確認)。
とはいえ template を用意するほど変更箇所が多いわけでもないし・・・。

というわけで、結局 command モジュール + sed で以下のようにしました。

- hosts: all
  user: root
  vars:
    target_file: /tmp/test.conf
  tasks:
    - name: find and replace using sed
      command: sed -i "s/^bar$/BAR=99999/g" ${target_file}

変更後に再度実行すると全て置換されている事が分かります。

# cat /tmp/test.conf
foo
BAR=99999
baz
BAR=99999
BAR=99999
BAR=99999
foo

置換系は sed 使えば確かに簡単なので、当面これでいいような気がしました。
マッチしなければ何も起きないので、冪等性が壊れることも多分無さそうだし。

参考:

lineinfile モジュール
http://yteraoka.github.io/ansible-tutorial/ansible-in-detail.html#module-lineinfile

ちょうど今月の Software Designsed 特集みたいです。