Chefを学ぶのが面倒な人のInfrastructure as Code with JSON
最近、いかにChefの学習コストを小さくしつつ、組織にインフラのコード化(Infrastructure as Code)を導入するか、について悶々と考えていたので、まとめてみた。
Chefの辛み
インフラのコード化をするにあたって、Chefは最初に覚えることが多くてなかなか辛い、という話をよく耳にする。Chefの動作概念、Chefの各種リソースの使い方、Chefの周辺ツール群の使い方、Cookbookのコーディング規約など、Chefを使い始めるために覚えるべき内容は結構多い。組織が大きくなればなるほど学習コストはバカにならないし、インフラエンジニアに日常的にコードを書いてもらうことはなかなか難しい。そんな環境でInfrastructure as Codeを広めようとしても、現実的になかなかうまくいかない。インフラをコード化して業務改善がしたいのに、いつの間にかみんなにChefを広めることが目的になり、疲弊したりする。
Infrastructure as Code with JSON
そんな中、DevOpsの流れとは逆行するが、みんなみんながみんなChefのことわかってCookbookが書ける必要はないんじゃないかとふと思った。大規模な組織では業務内容が細分化されているし、Cookbookの作成は得意な人に任せて、最低限必要な変数(attribute)をJSON ファイルで定義してchefを実行する方法だけ学べばいいのではないかと。まさにInfrastructure as Code with ChefならぬInfrastructure as Code with JSON。JSON を書いてインフラを管理する。Cookbookに比べてある程度柔軟性は無くなるが、最低限の学習コストでChefを導入することができ、業務の自動化ができそう。そうすれば、少しでも手作業による運用、筋肉運用を減らすことができるんじゃないだろうか。
インフラのコード化を進めていく段階を分類するとだいたいこんな感じになる。
理想は3の人が当たり前にいることだが、いきなり1から3にいける人はそこまで多くないと思うし、まずは1から2に持っていくのが改善効果が期待できるし大事だと思う。
Cookbookを書かずにCookbookを使う
Cookbookを使うだけであれば、Berkshelfの使い方と、attributeとrun_listを学ぶだけだ。作業としてはBerkshelf を使ってcookbookをRubyのBundlerのようにgemを手元に取ってきて、Cookbookの実行に必要なattributeとrun_listをNodeのJSON ファイルに定義してChefを実行する。Chefを実行するだけならChefのCookbookを書く必要はない。以下、使い方を紹介したい。なお、紹介のなかで使うNodeのJSONファイルなどはGithubに置いておいた。
1. BerkshelfでCookbooksを取得する
まず、使いたいCookbookを選ぶ。ChefコミュニティのCookbookを使ってもいいし、社内で使ってるCookbookでもいい。Cookbookの取得元は以下の通り。
- コミュニティCookbook置き場 - Supermarket
- 社内のCookbook置き場 - Private supermarketかBerkshelf APIのエンドポイント
timezone-ii
Cookbookを使う場合のBerksfile
はこんな感じ。
source "https://supermarket.chef.io" cookbook "timezone-ii"
社内のCookbookを使う場合は、sourceに社内のエンドポイントを指定する。
source "https://shanai.example.com/" cookbook "hogehoge" cookbook "fugafuga" cookbook "piyopiyo"
Berksfile
を作ったらberks
コマンドでcookbookをローカルに取ってくる。まるでRubyのBundlerのように。以下の例では取得先をcookbooksディレクトリにしている。
$ berks vendor cookbooks Resolving cookbook dependencies... Fetching cookbook index from https://supermarket.chef.io... Using timezone-ii (0.2.0) Vendoring timezone-ii (0.2.0) to cookbooks/timezone-ii $ tree -L 2 . ├── Berksfile ├── Berksfile.lock └── cookbooks └── timezone-ii
2. NodeのJSONファイルを定義する
次にChefを実行するために必要なNodeのJSONファイルを作成する。Nodeの設定に必要な可変値(attribute)や、どのrecipeを実行するか(run_list)などを記載する。その際、きちんと使いたいCookbookのドキュメントを読んで、使えるAttributeやrecipeを確認する。
timezone-ii
のcookbookを実行する例だと以下のようになる。
{ "name": "my_node", "tz": "Asia/Tokyo", "run_list": [ "recipe[timezone-ii]" ] }
nodes
ディレクトリを掘ってそこにJSONファイルをおく。
$ tree -L 2 . ├── Berksfile ├── Berksfile.lock ├── cookbooks │ └── timezone-ii └── nodes └── my_node.json
3. Chefを実行する
あとはChefを実行するだけ。Chefの実行はchef-solo、chef-client、knife-soloなど、いろんな方法があるが、ここではVagrantで作ったVM上で、chef-clientのlocalモードを実行する例を示す。
まず、以下のようなchefを実行するのに必要な設定ファイルclient.rb
を用意する。
cookbook_path '/vagrant/cookbooks'
ちなみに、社内環境とかProxy配下の環境で試す場合は以下のようなファイルになる。chef-zeroでproxyを使う場合はno_proxy
にlocalhostを入れることがポイント。
http_proxy 'http://proxy.example.coml:1234' no_proxy 'localhost' cookbook_path '/vagrant/cookbooks'
$ vagrant box add tkak/centos-6.6-x86_64-chef-dk $ vagrant init tkak/centos-6.6-x86_64-chef-dk $ vagrant up $ vagrant ssh [vagrant@localhost ~]$ date Sun Apr 5 04:19:24 UTC 2015 [vagrant@localhost ~]$ sudo chef-client -z -j /vagrant/nodes/my_node.json -c /vagrant/conf/client.rb Starting Chef Client, version 12.0.3 resolving cookbooks for run list: ["timezone-ii"] Synchronizing Cookbooks: - timezone-ii Compiling Cookbooks... Converging 4 resources Recipe: timezone-ii::default * yum_package[tzdata] action install (up to date) * log[Linux platform 'centos' is unknown to this recipe; using generic Linux method] action write (skipped due to not_if) Recipe: timezone-ii::linux-generic * ruby_block[confirm timezone] action run - execute the ruby block confirm timezone * file[/etc/localtime] action create - update content in file /etc/localtime from ab1ddb to 0bc4b3 (current file is binary, diff output suppressed) - restore selinux security context Running handlers: Running handlers complete Chef Client finished, 2/3 resources updated in 10.584156048 seconds [vagrant@localhost ~]$ date Sun Apr 5 13:21:12 JST 2015 [vagrant@localhost ~]$
まとめ
ChefのCookbookを書かずにJSONとBerkshelf だけでChefを実行する方法について書いた。もちろんChefをきちんと理解してCookbookが書ける方が望ましいが、Infrastructure as Codeに馴染みがない人やこれから学びたいという人は、とりあえずChefのcookbook使ってみるところからはじめるのがいいと思う。大事なのはChefを使うことではなく業務を改善することなので、まずはCookbookを使ってみて少しでも手作業をなくすことが大切だと思う。
TerraformのProviderを作った
Terraform v2.0から、オリジナルProviderを作るためのフレームワーク機能が追加されている。今回この機能を使ってオレオレProviderを作ったので、やったことをまとめておく。
Go API library
まず、TerraformのProviderを作る前に、自分が使いたいクラウドサービスのGo API libraryを探す。なければ自分で作る。
今回はGMO ConoHaクラウド用のAPI libraryを作った。ConoHaクラウドは今のところオブジェクトストレージ(OpenStack Swift)APIしか提供していなかったので、オブジェクトストレージを操作する機能だけのものになっている。
また、他のクラウドサービス向けのプラグインを作る時の参考用に、簡単なAPI client libraryの型みたいな物も作ってみた。
Terraform Provider
Client libraryが準備できたら、次に、Terraform Providerを作る。terraform-provider-xxxっていう名前のリポジトリを作って、main.go
、provider.go
、config.go
、resource_xxx.go
のファイルを用意する。Providerは基本的にこの4種類のファイルで構成されている。(詳細はGitHubを参照)
- tkak/terraform-provider-conoha · GitHub
- terraform/builtin/providers/mailgun at master · hashicorp/terraform · GitHub
以下、ちょっとした解説。
main.go
package main import ( "github.com/hashicorp/terraform/plugin" "github.com/tkak/terraform-provider-conoha/conoha" ) func main() { plugin.Serve(&plugin.ServeOpts{ ProviderFunc: conoha.Provider, }) }
main.go
ファイルには、plugin.Serve関数を呼ぶだけの処理を書く。オリジナルProviderをプラグインとしてTerraformに組み込むためのもの。
provider.go
// Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ "tenant": &schema.Schema{ Type: schema.TypeString, Required: true, }, "user": &schema.Schema{ Type: schema.TypeString, Required: true, }, "password": &schema.Schema{ Type: schema.TypeString, Required: true, }, }, ResourcesMap: map[string]*schema.Resource{ "conoha_container": resourceConohaContainer(), }, ConfigureFunc: providerConfigure, } }
provider.go
ファイルには、helper/schema
ライブラリを使って、Provider関数を定義する。ここでは、.tf
ファイル内で使う、provider
用の変数を定義したり、conoha_container
など、独自のresourceの宣言を行う。例えば、上のProvider関数の定義だとこんな.tf
ファイルになる。
provider "conoha" { user = "hoge" password = "hoge123" tenant = "hoge" }
簡単なバリデーションであればhelper/schema
のTypeやRequiredなどを使ってシンプルに書くことができる。
config.go
type Config struct { Tenant string `mapstructure:"tenant"` User string `mapstructure:"user"` Password string `mapstructure:"password"` Token string `mapstructure:"token"` Endpoint string `mapstructure:"endpoint"` } func (c *Config) Client() (*conoha.Client, error) { ... }
config.go
ファイルでは、client libraryを使うためのConfig structを定義したり、Client関数を定義したりする。変数の詳細なバリデーションも必要あればここに書いたりする。
resource_conoha_container.go
func resourceConohaContainer() *schema.Resource { return &schema.Resource{ Create: resourceConohaContainerCreate, Read: resourceConohaContainerRead, Delete: resourceConohaContainerDelete, Schema: map[string]*schema.Schema{ "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, }, } } func resourceConohaContainerCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*conoha.Client) c := &conoha.Container{ Name: d.Get("name").(string), } err := client.CreateContainer(c) if err != nil { return fmt.Errorf("Error creating container: %s", err) } d.Set("name", c.Name) d.SetId(c.Name) return nil } ...
resource_xxx.go
ファイルには、resourceで扱う変数の定義やAPI周りのメイン処理を書く。上のresourceConohaContainer()
だと、.tf
ファイルのresource定義はこのようになる。
resource "conoha_container" "example" { name = "fuga" }
また、*schema.ResourceData
のSet
関数やSetId
関数を使うことで、Terraform内で扱う任意の変数やリソースIDなどを設定することができる。
Usage
オリジナルProviderの動かし方の説明も書いておく。
Install
TerraformのProviderは、terraformのバイナリファイルに組み込まれているのではなく、Providerごと別なバイナリファイルになっている。インストールはgo buildしたあとterraform-provider-<provider名>という名前でTerraformの実行ファイルと同じディレクトリ配下($GOPATH/bin配下)に配置する。要は直接go installを叩くのと一緒。そうすると、Terraformコマンドを実行した時にProviderのバイナリファイルが読み込まれる。インストール手順はこんな感じ。
$ go get github.com/tkak/conoha $ go get github.com/tkak/terraform-provider-conoha $ go install github.com/tkak/terraform-provider-conoha
terraform apply
あとは、.tf
を作って、実行するだけ。今回作ったProviderだと以下のような.tf
ファイルを作成し、terraform apply
コマンドを実行する。
$ vi conoha.tf --- variable "conoha_user" {} variable "conoha_password" {} variable "conoha_tenant" {} provider "conoha" { user = "${var.conoha_user}" password = "${var.conoha_password}" tenant = "${var.conoha_tenant}" } resource "conoha_container" "example" { name = "hoge" } --- $ terraform plan \ -var "conoha_user=${CONOHA_USER}" \ -var "conoha_password=${CONOHA_PASSWORD}" \ -var "conoha_tenant=${CONOHA_TENANT}"
パスワードなどのパラメータはファイルとしてバージョン管理したくないのでCONOHA_*
環境変数で設定して実行する。
ちなみに、環境変数にTF_LOG=1
を設定すると実行時にデバッグログをはくようにすることもできる。
まとめ
Goは最近勉強しはじめたばかりで、今回作ったライブラリやProviderはまだまだ改良する点があるが、ひとまず動く物を作ることができた。実際作るのにかかった時間はそんなにはなくて、APIの調査やTerraform のコードを読んだりした時間の方がかかった。Mitchell Hashimoto先生曰くGoogle CloudのProviderは8時間しかかからなかったらしい。慣れたらかなり簡単にオリジナルProviderが作れると思う。
プラグインごとの細かい作り込みは必要だけど、いろんなクラウドサービスのProviderを用意すれば、それぞれを共通のファイル形式で一元管理できる。 そんなツールは今までありそうでなかったし、Infrastructure as Codeの痒いところに手が届くツールとして、Terraformは中々優れているものなんじゃないかなと思う。
References
TerraformのGoogle Cloud providerを試してみた。
先日参加したgcp ja night #28 - connpassで、 Google Cloud Platformを$500分無料で使えるクーポンをいただいたので、 Terraform by HashiCorpのGoogle Cloud providerを試してみた。
Terraformは、Vagrantとかインフラ便利ツールを作っているHashicoap社の新作。 インフラの状態をDSL形式で定義できて(Infrastructure as Code)、CLIからインフラの状態を変更することができる。
ChefやPuppetとは守備範囲が違って、VM作ったり、バランサの設定したり、DNSを登録したりする作業を自動化する。 守備範囲的には、Chef-metalとかAWS CloudFormationとかとかぶる感じ。
いろんなクラウドサービスを、同じフォーマットのファイルで管理することができるのが大きな強み。 今巷でバズってるMicroservices的な考えで、数多あるクラウドサービスを一元管理できるってなんて夢のツールだろう。
Terraformは、v0.2.2を使用。
すでに試している方がいるので、それも参考に。
Terraform 0.2 で Google Compute Engine を試してみた (初級編) - Qiita
試してみたファイルはGithubに。
準備
Google Developers Consoleからaccount.jsonとclient_secrets.jsonをダウンロードする。初めて使うので、どこからファイルをダウンロードすればいいかわからなくて少しはまった。古いDashboardに画面遷移する必要があった。
account.json
Google Cloudのアカウント証明書。
"APIs & auth" -> "Credentials"のページ、"OAuth"の"Generate new JSON key"ボタンからダウンロード。もしまだOAuth Client IDを作ってなかったら、"Create new Client ID"ボタンから新しく作成してからJSONファイルを作成する。
client_secrets.json
Google Cloud APIsを利用するためのクライアント情報。
"APIs & auth" -> "APIs"のページ、歯車マークのボタンから別ページに遷移して、"API Access"のページ、"Download JSON"からダウンロードする。
2つのファイルは適当に作業ディレクトリ配下に。
$ ls account.json client_secrets.json
instanceの作成。
まず試しにinstanceを一つ作ってみる。
作業ディレクトリにtfファイルを作成する。
$ vim google_compute.tf provider "google" { account_file = "./account.json" client_secrets_file = "./client_secrets.json" project = "tkakfrkw" region = "asia-east1-a" } resource "google_compute_instance" "default" { name = "test" machine_type = "f1-micro" zone = "asia-east1-b" disk { image = "debian-7-wheezy-v20140814" } network { source = "default" } }
planでこれから行う作業の確認。
$ terraform plan Refreshing Terraform state prior to plan... The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. + google_compute_instance.default disk.#: "" => "1" disk.0.image: "" => "debian-7-wheezy-v20140814" machine_type: "" => "f1-micro" metadata_fingerprint: "" => "<computed>" name: "" => "test" network.#: "" => "1" network.0.internal_address: "" => "<computed>" network.0.name: "" => "<computed>" network.0.source: "" => "default" tags_fingerprint: "" => "<computed>" zone: "" => "asia-east1-b"
applyで実際の処理を実行。
$ terraform apply google_compute_instance.default: Creating... disk.#: "" => "1" disk.0.image: "" => "debian-7-wheezy-v20140814" machine_type: "" => "f1-micro" metadata_fingerprint: "" => "<computed>" name: "" => "test" network.#: "" => "1" network.0.internal_address: "" => "<computed>" network.0.name: "" => "<computed>" network.0.source: "" => "default" tags_fingerprint: "" => "<computed>" zone: "" => "asia-east1-b" google_compute_instance.default: Creation complete Apply complete! Resources: 1 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: terraform.tfstate
showでインフラの状態を確認。
$ terraform show terraform.tfstate google_compute_instance.default: id = test disk.# = 1 disk.0.image = debian-7-wheezy-v20140814 machine_type = f1-micro metadata_fingerprint = iyTlDGmC25M= name = test network.# = 1 network.0.internal_address = 10.240.150.255 network.0.name = nic0 network.0.source = default tags_fingerprint = 42WmSpB8rSM= zone = asia-east1-b
できてる、できてる。
ちなみに、terraform.tfstateファイルはバイナリファイル。v0.3.0からはjsonになるっぽい。
$ file terraform.tfstate terraform.tfstate: data
instanceの削除
instanceを削除してみる。
instanceの部分をコメントアウト。
$ vim google_compute.tf provider "google" { account_file = "./account.json" client_secrets_file = "./client_secrets.json" project = "tkakfrkw" region = "asia-east1-a" } /* resource "google_compute_instance" "default" { name = "test" machine_type = "f1-micro" zone = "asia-east1-b" disk { image = "debian-7-wheezy-v20140814" } network { source = "default" } } */
plan, apply, show。
$ terraform plan efreshing Terraform state prior to plan... google_compute_instance.default: Refreshing state... (ID: test) The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. - google_compute_instance.default $ terraform apply google_compute_instance.default: Refreshing state... (ID: test) google_compute_instance.default: Destroying... google_compute_instance.default: Destruction complete Apply complete! Resources: 0 added, 0 changed, 1 destroyed. $ terraform show The state file is empty. No resources are represented.
JSON形式で試す
tfファイルはJSON形式でも定義できるので、拡張子.tf.jsonのファイルを用意する。上のtfファイルと同じ処理をするJSONは以下の通り。
$ vim google_compute.tf.json { "provider": { "google": { "account_file": "./account.json", "client_secrets_file": "./client_secrets.json", "project": "tkakfrkw", "region": "asia-east1-a" } }, "resource": { "google_compute_instance": { "default": { "name": "test", "machine_type": "f1-micro", "zone": "asia-east1-b", "disk": { "image": "debian-7-wheezy-v20140814" }, "network": { "source": "default" } } } } }
plan, apply, showしてみたが特に問題なく実行できた。JSON形式対応してくれるのはかなりいい機能だと思う。 他のサービスからJSON出力させて、botにterraformを実行させるみたいなこともできそう。
まとめ
ここ最近Terraformっぽいツールが欲しいと思っていて自作しようとしてたところに、彗星のごとくMichell Hashimoto先生の新作が降ってきた。最近ではAWS以外にもGoogleやMicrosoft、Degital Oceanなどのクラウドサービスがよくなってきていて、ハイブリッドにクラウドを利用する時代がきているのかもしれない。そんな時代にあった良いツールを出してくるHashimoto先生はさすがだと思った。
Packer + ChefDK + Docker(kitchen-docker)で、Chef Cookbookの開発環境整備を楽にする。
ChefのCookbookを書き始めるには結構準備することが多くて、Cookbookの開発環境を作るのがなかなかつらい。手元のPCにVirtualBoxとVagrantを用意し、rbenvやBundlerの使い方を学び、必要なgemをインストールする。Chefだけでも学習コストが高いのに、周辺のツールを使いこなすのにさらに学習コストがかかる。しかも、社内にはWindowsユーザとMacユーザが混在してるので、その辺を仮想環境を使ってうまく抽象化したい。
そんな動機から、Packer + ChefDK + Docker(kitchen-docker)で、気軽にChefのCookbook開発を始められる環境を作ってみた。これでCookbook開発が捗る!(詳細はgithubを参照。)
今更Chef Cookbookベストプラクティス
去年Chefが大流行して今更Chefの話を書くのも恥ずかしい気がするけど、@kajikenからリクエストがあったので、僕が思うChefのCookbookのベストプラクティスを書いてみる。
Berkshelf way
まずはBerkshelf について。BerkshelfはCookbookの依存関係を解決してくれる便利ツール。基本的にCookbookはプロジェクトとは別なリポジトリで管理して、プロジェクトごとに必要なCookbookをBerkshelfで取ってきて使うのがいいと思う。Cookbookのバージョン管理を厳密にできるので、毎回同じサーバ環境が構築できる。あと、人が作ったCookbookを使い回せるので、車輪の再発明しなくてすむ。素晴らしい。
ただ、Berkshelfはバージョンが3系になって、Berkshelf用のAPIサーバを立てないといけないので、僕は未だに2系を使ってる。Chefを使う人や管理するCookbookが多くなってきたらAPIサーバをたてたほうがいいと思う。
あと、これはちょっとしたTipsになるけど、Berksfileはrubyのファイルになってるので、cookbookごとにgitのブランチを指定するよりも、こんな感じできれいにかくことができる。
# before cookbook 'mysql', git: 'https://github.com/tkak/hoge.git' cookbook 'nginx', git: 'https://github.com/tkak/fuga.git' cookbook 'vim', git: 'https://github.com/tkak/piyo.git' # after def oreno_cookbook(name, options = {}) cookbook(name, { git: "https://github.com/tkak/#{name}.git" }.merge(options)) end oreno_cookbook 'hoge' oreno_cookbook 'fuga' oreno_cookbook 'piyo'
Cookbookの粒度
基本的に一つのCookbookは一つのリポジトリで管理するべき。馬鹿でかいCookbookを作ったりするとメンテナンスが大変になることが多いので避けた方がいい。ただ、一つのCookbookにどこまで処理を盛り込めばいいのか、Cookbookの粒度について時々迷うことがある。とりあえず、最初から大きなCookbookにするのではなく、最低限の機能を持つCookbookを何個か作って、そのあとそれらをまとめたCookbookを作ればいいと思う。
Cookbookをタイプ別にわけるとこんな感じになる。
Element Cookbook
最低限の機能、それ自体で完結するCookbook。
Library Cookbook
recipeが存在しない、Definitions
, LWRPs
, Libraries
で構成されたCookbook。
Wrapper Cookbook
他のCookbookをラッピングするCookbook。
Role Cookbooks
Roleを定義するためのCookbook。基本的にRoleは使わない、バージョン管理ができないから。代わりにRole Cookbookを使う。
# oreno_app/recipes/base.rb include_recipe "hoge" include_recipe "fuga" include_recipe "piyo" # oreno_app/metadata.rb name 'oreno_app' maintainer 'tkak' maintainer_email 'hoge@mail.com' license 'All rights reserved' description 'role cookbook' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.0.1' depends 'hoge' depends 'fuga' depends 'piyo'
Cookbookの名前
Cookbookの名前は、-
(ハイフン)を使わない。-
だとLWRPがうまく動かないっていう地雷があるので、代わりに_
(アンダースコア)を使ってる。あと、名前は動詞ではなく名詞になるようにしてる。
chef-repoの構成
oreno-chef-repo/ ├── Berksfile ├── Gemfile ├── README.md ├── conf ├── cookbooks │ ├── fuga │ ├── hoge │ └── piyo ├── data_bags ├── environments └── site-cookbooks ├── oreno_app └── oreno_db
http://shibayu36.hatenablog.com/entry/2014/08/04/073000chefのリポジトリ構成はこんな感じ。特にcookbooks
とsite-cookbooks
は、Berkshelfで取ってきたCookbookかそうでないかの違いで分けてる。cookbooksにはBerkshelfで取ってきたcookbooks、site-cookbooksには自分のアプリ用のcookbooksを配置する。Berkshelfのデフォルトでは、.cookbooks配下にCookbookが置かれるが、cookbookの場所を明確にしておきたいのであえてberks install --path cookbooks
でインストールしている。
CookbookのTestについて
CookbookのTestはServerspecを使ってる。深淵な理由でTest-kitchenは使ってない。ProxyとかProxyとかProxyとか。CIは、Jenkins + Vagrant + Chef + Foodcritic + Serverspecでまわしてる。よくある感じ。
アプリケーションレイヤーのCookbookはDockerを使って開発するのがいいと思ってはいるが、まだVMを多用している。毎回VMの起動に時間がかかるので早くコンテナを導入したい反面、OSのカーネルに依存するような部分のCookbookに関しては地道にVMを使うしかないのかなぁ、なんて感じてる。適材適所で使うのは全然ありだと思う。
まとめ
2、3年前くらいからChefを使い初めたんだけど、何度Cookbookを書き直したか分からない…w 試行錯誤してようやく今の形に落ち着いた感じ。これからChefを導入する人やCookbookを書き直す人の参加になれば幸いです。もう少し知見ある気がするけど、疲れたので今日はこの辺で。でわでわ。
あ、Chefの使い方とかかなり詳しく知りたい時は、「Chef活用ガイド」がオススメです。かなり細かくChefについて書いてあります。ちょっとした国語辞典並みの厚さですがw よかったらぜひ手に取ってみてください。
- 作者: 澤登亨彦,樋口大輔,クリエーションライン株式会社
- 出版社/メーカー: KADOKAWA/アスキー・メディアワークス
- 発売日: 2014/04/25
- メディア: 大型本
- この商品を含むブログを見る
VagrantでVMware vSphere(ESXi)上のVMを操作する「vagrant-vsphere」を試してみた。
Vagrantのproviderは、VirtualBoxやAWSなど様々ありますが、VMware vSphere(ESXi)用のvagrant-vsphereを試してみたので備忘録として残しておきます。
Installation
まずは、vagrant-vsphere pluginのインストールから。githubのREADMEに書いてますが一応。
$ vagrant plugin install vagrant-vsphere
Prepare dummy box
次にダミー用のboxイメージを作成します。vsphereを使う場合は、virtualboxとは違ってローカルにboxイメージは置きません。
$ cd ~/.vagrant.d/gems/gems/vagrant-vsphere-0.8.2/example_box/
example_boxディレクトリ配下にmetadata.jsonファイルがあるので、それをtarコマンドで固めます。
$ cat ./metadata.json { "provider": "vSphere" } $ tar cvzf dummy.box ./metadata.json
作ったboxは、適当なディレクトリに配置します。例えばプロジェクトのrootに置く場合。
$ ls Vagrantfile Vagrantfile.virtualbox Vagrantfile.vsphere dummy.box cookbooks/ nodes/ roles/ conf/ ... ...
Prepare a template in vShpere
次に、VMのひな形になるboxを用意します。以下のリンクを参考に、vSphere上にクローン用のテンプレートイメージとCustomization Specificationを作成します。
Vagrant up
準備が終わったのでVMを作成します。 Vagrantfileはこんな感じで用意します。
$ vi Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = 'dummy' config.vm.box_url = './dummy.box' config.vm.synced_folder ENV["SYNCED_FOLDER"], "/vagrant" config.vm.network 'private_network', ip: 'x.x.x.x' config.ssh.host = 'x.x.x.x' config.vm.provider :vsphere do |vsphere| vsphere.host = 'HOST NAME OF YOUR VSPHERE INSTANCE' vsphere.data_center_name = 'YOUR DATACENTER' vsphere.compute_resource_name = 'YOUR COMPUTE RESOURCE' vsphere.resource_pool_name = 'YOUR RESOURCE POOL' vsphere.data_store_name = 'YOUR DATA STORE' vsphere.template_name = 'YOUR VM TEMPLATE' vsphere.name = 'NEW VM NAME' vsphere.user = 'YOUR VMWARE USER' vsphere.password = ENV["VSPHERE_PASSWORD"] vsphere.insecure = true vsphere.template_name = 'YOUR TEMPLATE NAME' vsphere.customization_spec_name = 'YOUR CUSTOMIZATION SPEC' end end
ENV["SYNCED_FOLDER"]とENV["VSPHERE_PASSWORD"]は、Vagrantfileに直接書きたくないので、環境変数にしてます。 VMを作成する時に指定する感じです。
$ export SYNCED_FOLDER=/your/path $ VSPHERE_PASSWORD=**** vagrant up --provider=vsphere $ vagrant ssh
あとは普通に、vagrant ssh、vagrant destroyとか動きます。vagrant自体の細かい使い方は省略します。
Wrap up
vagrant-vsphereを試してみました。vSphere上のVM作成をコマンドラインからやりたいときに便利です。 (fogのvsphere providerも試してみたんですが、うまく動かなかったのでこっちにしました。)
今では、Chef cookbookのCI環境用に使ってます。 このplugin自体あんまり使ってる事例とかがなくて、試行錯誤しながらとりあえず動くところまではできたのでよかったです。 同じように困っている人がこのブログがお役にたてれば幸いです。ではでは。
References
Proxy環境下でDockerを使う。
Proxy環境下でDockerを使ってみたのでメモ。
使った環境はこんな感じ。
Dockerデーモンを起動するときに、環境変数でproxyの設定をする。
# http_proxy=http://hoge:1234 docker -d &
起動スクリプトからDockerを起動するときは、/etc/sysconfig/dockerファイルでproxyの設定を行う。
# cat > /etc/sysconfig/docker export http_proxy="http://hoge:1234/" # service docker start
追記2 (Jul 10, 2014)
CentOS 7.0編
# cp /usr/lib/systemd/system/docker.service /etc/systemd/system/ # vi /etc/systemd/system/docker.service ... ... Environment=‘http_proxy=http://hoge:1234/’ ## ExecStartの上に追記 ExecStart=/usr/bin/docker -d --selinux-enabled -H fd:// … ... # systemctl daemon-reload # systemctl restart docker
追記 (Jul 5, 2014)
Ubuntu 14.04編
$ sudo sh -c "echo 'export http_proxy=http://hoge:1234/' >> /etc/default/docker" $ sudo service docker restart