iii ThreeTreesLight

about me / / /

[chef] 複数環境を管理するcookbookをリファクタしてみた

Chefマスターから見たら「えっそんなこともちゃんとやってなかったの?情弱」的な感じかもしれませんが、とりあえず自分で気をつけたポイントをメモしてみました。

ほんとはこう分けた方が良いよ!とかありましたら教えてもらえると嬉しいです!

課題

やっとサービスが成長してきたので、ちゃんとChefで3環境(local, staging, production)を管理しているのですが、

「3rd partyのcookbookも使ってるし、attributeのoverrideをrunlistで行っていて、環境ごとのrunlistに差分が出る。ヤバい。目diff無理」

という素敵な課題が勃発

対策

このrunlist問題に対して、以下のような対策を打って、リファクタしました。

1. バージョンをattribute化してあるものは必ずserver specでテスト

これは当たり前ですね。なんでやってなかったんだって怒られそう。

2. site-cookbooks内のcookbookのまとまり感を揃える

リファクタ前は、あるpakcageのインストールをcookbook内のrecipeとして管理したり、独立したcookbookとして管理したり、粒度や方針がバラバラでした。

例えば以下のようなものがバラッバラでした。

そうなると当然、チーム内で新しいpackageを追加したいときに「これって切り出すべき?それともどっかのrecipeにするべき?」って話し合う事が多くなる感じです。

そこで、以下のような大きく4つのcookbookを作って整理した結果、runlistへの見通しも良くなりました。 (分け方は趣味です)

  • initialize
    • 最初にやっておくべき事(sudoとかuserの追加とか、iptablesとか)
  • languages
    • node, ruby, pythonとか
  • db
    • db接続とかdbそのものなど
  • sites
    • nginx周りの設定とかweb server絡み

ただ、recipeの内容が複雑になる場合やtemplateを多用する場合は、まとめてしまうとメンテし難くなるのでcookbookとして切り出し、include_recipeするwrap recipeを上記のcookbookに食わせるようにしました。

3. 3rd partyのcookbookを直接runlistから実行せず、包む。

runlistを長くなる原因の一つとして、3rd partyのcookbookをそのままrunlistから実行する事が挙げられると思います。

対策としては、ほぼ2のcookbook切り出した場合と同じで、3rd partyのcookbookをincludeするwarp recipeを作り、attributeもそのcookbook内でまず上書きする方針にしました。

例えばgitの3rdpartyを以下のようにrunlistで記載するのではなく、warpすると以下になるイメージです

リファクタ前

js // runlist { "environment": "production", "git": { "version: "1.9.0", "url": "https://git-core.googlecode.com/files/git-1.9.0.tar.gz" }, "run_list": [ "git::source" ] }

リファクタ後

wrapするrecipeを作って

“`ruby

initialize::git

include_recipe “git::source” “`

attributeで上書きして

“`ruby

attribute

git

default[‘git’][‘version’] = ‘1.9.0’ default[‘git’][‘url’] = ‘https://git-core.googlecode.com/files/git-1.9.0.tar.gz’ “`

runlistはこんな感じに

js // runlist { "environment": "production", "run_list": [ "initialize::git" ] }

このとき、attributeに記載するデフォルト値はstaging環境にしてみました。

理由としては以下2つです。

  • productionにすると、環境を増やしたときに繋がれてしまう恐れが有る
  • local(development)にすると、papertrailなど環境を増やしたときにattribute設定の漏れが出る恐れが有る

4. environmentを利用してrunlistからattributeを分離

環境ごとに異なるtokenやdbのつなぎ先などはenvironmentで管理します。

中を見ると以下のようになっているのですが、

js { "name": "production", "description":"This is it", "chef_type": "environment", "json_class": "Chef::Environment", "default_attributes": { }, "override_attributes": { } }

override attributeは、優先度の高い値として、attributeを強制的に上書きされるので、overrideを使います。

reference: http://docs.opscode.com/essentials_environments.html

で、そこで明示的に示しておくべきattributeは、defaultにあったとしてもそれぞれattributeで明示的に書いておくことにしました。

例えば、database.ymlの設定や.envファイルの設定です。これは、環境を別途切り出した場合の設定ミスを防ぐ目的で行いました。

5. runlist内の重複は、roleで回避する

ある程度整理できてくるとnodeの重複が出てきます。

リファクタ前

local

“`js { “environment”: “local”, “run_list”: [ “initialize::default”, “initialize::manage”, “initialize::users”, “initialize::sudo”, “initialize::ssh”, “initialize::hosts”, “initialize::iptables”, “initialize::git”, … // only staging, local “db::mysql_server”, “sites::memcached” … ] }

“`

production

js { "environment": "production", "run_list": [ "initialize::default", "initialize::manage", "initialize::users", "initialize::sudo", "initialize::ssh", "initialize::hosts", "initialize::iptables", "initialize::git", ... ] }

リファクタ後

common, db, web, dev(dbやらcacheやら、productionでは外に出すけど、stagingとかではお腹に置くもの)とか切り出します

js // roles/common.json { "name": "common", "chef_type": "role", "json_class":"Chef::Role", "default_attributes":{}, "override_attributes":{}, "description":"webserver’s role", "run_list": [ "initialize::default", "initialize::manage", "initialize::users", "initialize::sudo", "initialize::ssh", "initialize::hosts", "initialize::iptables", "initialize::git" ... ] }

runlist

“`js { “environment”: “local”, “run_list”: [ “role[common]”, … “role[dev]” ] }

“`

ただ、以下のサイトにもあるようにrunlistはバージョン管理できない事も有り、問題が有るかもしれません。

[和訳] 初心者Chefアンチパターン by Julian Dunn #opschef_ja

1 week ago  Notes (3)

#chef

pry 0.10.0にてundefined method `pager`が起きたときの暫定対処

事象

0.9.12.6 -> 0.10.0にあげたらエーって言うぐらい怒られた。

ruby pry> User.all undefined method `pager'

で、実際にissueもまだopen

pry/pry 0.10.0 - undefined method `pager’ for nil:NilClass #1265

環境

ruby 2.0.0p353 rails 4.1.1 pry-byebug 1.3.3 pry-rails 0.3.2 pry-remote 0.1.8

原因

詳しく調べてないおち

対応策

とりあえず、pagerが原因なら殺しておく。

“`bash $ vi .pryrc

Pry.config.pager = false

“`

https://github.com/pry/pry/wiki/Customization-and-configuration#pager

これで動作しました。

1 week ago  Notes (2)

#pry #ruby

"フルスタックエンジニアって、ようはRPGの「赤魔導士」とか一緒で、序盤は重宝するけど中盤以降に上級の専門職ユニットが使えるようになると、リストラされるアレ"

Twitter / fladdict (via igi)

(via kasei-san)

4 weeks ago  Notes (336)

(Source: sheepfilms, via kasei-san)

i18nのtranslation missingをテストする

i18nのtranslation missingをテストする

i18n対応したサービスを提供していると、i18nのdictが一部抜けていたり、デザイン崩れているじゃんって事が多々有りますよね?

で、そういうのをデプロイ前に未然に防ぎたいと思ってcontroller specにて、translation_missingが含まれているかとかチェックしていたけど、複数人で書いていると中々網羅性に掛けたりして困ってました。

そういうときに便利なのがi18n-task gem

install

gemいれて

$ vim Gemfile
gem 'i18n-tasks', '~> 0.4.5' 

$ bundle

configuration

デフォルトの言語はja、テスト対象はja, enにしたいと思います。

$ i18n-tasks config > config/i18n-tasks.yml
$ vim config/i18n-tasks.yml

base_locale: ja
locales: [ja, en]

# dictを分割している場合は、それぞれ読み込んじゃいます。
data:
  read: 
    -  'config/locales/defaults/%{locale}.yml'
    -  'config/locales/views/defaults/%{locale}.yml'
    -  'config/locales/views/foo/%{locale}.yml'

実行

# translation missing
$ i18n-tasks missing

# unused
$ i18n-tasks unused

さらに使っていない物をremoveしたり、検索対象やremoveに際しignoreする事もconfigurationできます。

rspec

spec/view/translation/i18n_spec.rbを追加することで、CIでちゃんと試験してくれるようになります。

require 'spec_helper'
require 'i18n/tasks'

describe 'I18n' do
  let(:i18n) { I18n::Tasks::BaseTask.new }

  it 'does not have missing keys' do
    count = i18n.missing_keys.count
    fail "There are #{count} missing i18n keys! Run 'i18n-tasks missing' for more details." unless count.zero?
  end

  it 'does not have unused keys' do
    count = i18n.unused_keys.count
    fail "There are #{count} unused i18n keys! Run 'i18n-tasks unused' for more details." unless count.zero?
  end
end

遣り過ぎるとメンテナンス性が落ちたり、設定が複雑化することもあるので、現行プロジェクトではtranslation missingだけの抽出を対象にしています。

やっと心が安らぐ。。。

1 month ago  Notes (0)

#rails #i18n

スクリーンショットを直接クリップボードに保存する方法

結構あるあるなのですが、Automan作ったりスクリプト書いたりどうなの?って思ってたら、公式にあったとか悲しい。

OS X Mountain Lion: 画面を撮影するためのショートカット

画面の写真(スクリーンショット)はデスクトップ上にファイルとして保存されますが、スクリーンショットをクリップボードに保存したい場合は、Control キーを押しながら他のキーを押します。クリップボードに保存したピクチャは、書類にペーストできます。

つまり範囲指定且つclipbordで登録する場合

cmd + shift + 4 + ctrl

1 month ago  Notes (0)

#osx #screenshot

(via kasei-san)