bobuhiro11's diary

Chef実践入門の読書メモ

21 Oct 2017
[chef]

Chefを利用する機会があり、体系的に知りたいと持って、Chef実践入門を読んだ。 自分用に、そのメモを残しておく。

概要

  • 管理サーバを介するかどうかによって、2種類の動作形態がある。1つ目は、管理サーバとしてChef Serverを介するサーバクライアントモデル。ローカル端末と、管理対象ノードの他に、Chef Serverを必要とする。ローカル端末・Chef Server間、Chef Server・ノード(Chef Client)間でやり取りする。ノード数の増加に強い。2つ目は、Chef Soloを使ったスタンドアロンモデル。Chef ServerもChef Clientも不要。小規模な環境で使われることが多い。いずれもRubyで実装されているので、gem install chef knife-solo berkshelfのように、gemによってインストールできる。
  • リポジトリ(キッチン)、クックブック、レシピという階層構造を持つ。リポジトリは、Gitなどでバージョン管理をする。site-cookbooksに自作のクックブック、cookbooksに外部クックブック、data_bagsにリポジトリ全体でグローバルなスコープのデータ、environmentsにdev/prodなどの環境情報、nodesにノードごとの設定情報(nodeオブジェクト)を配置する。また、Berksfileに依存する外部クックブック一覧を記述する。

クックブックの実装

  • 同一構成の複数ノードを管理する場合には、ノードをまとめたroleを定義する。<repository>/roles/<role name>.jsonを作り、run_listなどを書いていく。また、Attributesを上書きすることもできる。default_attributesは定義されていないものを定義し、override_attributesは強制的に上書きするもの。
  • Attributeというkey-valueを管理する仕組みがある。自分で設定したり、システムから自動抽出することができる。自動抽出する場合には、ohaiコマンドの結果が使われる。慣習的に、ohaiで取得したの値のキーに関してはnode[:platform]のようにシンボルを利用し、Nodeオブジェクトで定義したキーに関しては、node["httpd"]["port"]のように文字列を利用する。Attributeは、<cookbook name>/attributes/defaults.rbにデフォルト値を定義できる。Attributeの優先順は、Nodeオブジェクト>ロール>Environment>レシピのAttribute>クックブックのAttribute。
  • cookbook_fileリソースは<cookbook name>/files以下のファイルを転送し、fileリソースはファイルを新規作成するために使われる。
  • ifconfigmountgem_packagegithttp_requestrouteruby_blockリソースなどがある。もし、これらのリソースを使っても管理できないものがあれば、scriptリソースを使う。ただ、not_ifonly_ifなどをうまく使って、冪等性を自分で保証する必要がある。
  • 設定ファイルの配置後に、notifies :reload, "service[httpd]"のようにして、サービスのリロードできる。あるいは、subscribes :reload "template[/etc/httpd.conf]"によって、逆方向で定義できる。どちらも動作は同じで、キューに詰め込まれてまとめて実行される。
  • if文でインデントが深くなり可読性が下がってしまう場合には、only_ifなどの条件付きアクションを使うと良い。
  • レシピに書かれたリソースはコンパイル後に収束のタイミングで実行されるが、その他のRubyコードはコンパイル時に実行される。収束のタイミングで実行されるRubyコードを書きたければ、ruby_blockリソースを使う。
  • 一連のリソースや処理は、Definitionとして、マクロのように実装できる。

Chef Solo

  • Cookbookを開発するサーバと、管理対象ノードが同一の場合には、knife cookbook create <cookbook name>によりクックブックを作成し、chef-solo -o <cookbook name>によりクックブックを実行する。こういう使い方はあまりしないのかな。
  • 開発サーバと管理対象ノードが異なる場合には、開発サーバでknife-soloを使う。knife-soloコマンドは、手元のクックブックをノードへ転送し、chef-soloを実行する。まず、開発サーバで、knife solo init .により、リポジトリを準備する。次に、knife solo prepare <hostname>により、管理対象ノードにche-soloをインストールする。続いて、knife cookbook create <cookbook name> -o <repository>/site-cookbooksで、クックブックを作成する。その後、クックブックを編集して、<repository>/nodes/<hostname>.jsonの中で{'run_list': ["recipe[<cookbook name>:<recipe name>]"]}と記述する。<recipe name>を省略すると、default.rbが参照される。最後に、knife solo cook <hostname>で、ノードをプロビジョニングする。なお、knife solo prepare <hostname>knife solo cook <hostname>をまとめたknife solo bootstrap <hostname>コマンドもある。また、chef-soloにのみ依存するものはknife soloを使い、それ以外のものはknifeを使う。

その他

  • Test Kitchenを使って、クックブックを統合テストできる。Vagrantなどで、複数のOSを立ち上げる。テストの実行は、クックブックの適用後に、surverspecによって行われる。実際にテストを適用せずに、振る舞いをテストしたければ、ChefSpecを利用する。
  • knife cookbook site search <cookbook name>で外部のクックブックを検索して、Berksfilecookbook '<cookbook>'追加する。その後、berksコマンドで、cookbooksディレクトリにダウンロード、展開される。
  • Opscode、Basecamp、RiotGames、aws、engineyard、pivotal-sproutなどがコミュニティクックブックを公開している。
  • Chef Serverは、新規clientの登録時に双方のvalidation.pemをチェックして、認証する。その後、クライアントに秘密鍵を発行する。
  • knife statusで最後にChef Clientが実行された時間を確認できる。
  • Chefの持つ冪等性と、緊急時のロールバックは相性が悪い。そのため、Chefによるアプリケーションのデプロイは理想的ではない。
  • クックブックの適用忘れを防ぐためには、Chef Soloを定期実行するか、Server/Client構成にして自動適用する。
  • knifeコマンドやohaiコマンドのプラグインは、指定のディレクトリにRubyコードを配置することで、簡単に実装できる。

Github PagesとCloudflareで静的サイトを配信

07 Oct 2017
[git]

これまで、bobuhiro11.netblog.bobuhiro11.net は、 自分で管理するサーバから配信していた。 ただ、どちらも静的サイトなので、必ずしも自分でインフラを管理する必要はなく、 楽をするために、これからはGithub Pagesを使って配信することにした。 調べてみたところ、Github Pagesでは、独自ドメインは利用できるが、HTTPSで配信することができない。 そこで、Github Pagesの前段に、SSL終端のできるCDNを配置した。 SSL終端のできるCDNは、いくつかあるようだが、 今回は無料で利用できるCloudflareを選択した。 Cloudflareには、DNSの機能もあるので、 お名前.comで登録している bobuhiro11.net とそのサブドメインを Cloudflareへ移行した。 まとめると、以下のようになる。

  • お名前.com:ドメインのレジストリ
  • Cloudflare:DNSとCDN(SSL終端)
  • Github Pages:静的サイトのビルドとHTTP配信

Githubの設定

  • 2つのPrivateリポジトリを作成して、jekyllのコードをpush
  • リポジトリの公開範囲がprivateであっても、gh-pagesブランチの内容は自動的に公開
  • リポジトリ直下に、ドメイン名を記述したCNAMEを配置
  • SettingsタブのGithub Pagesで確認
  • jekyllコードは自動でビルド

Cloudflareの設定

ふつうzone apexにCNAMEレコードはマッピングできないが、 Cloudflareでは、CNAME Flatteningによって、例外的に割り当てることができる。 Aレコードよりも管理が楽なので、zone apexにもCNAMEを割り当てた。 適当にアカウントを登録して、設定を入れていく。 CloudflareとGithub Pagesの間では、sslを利用できないので、CryptoタブのSSL設定はFlexibleとした。 また、ブラウザにhttps接続を強制するために、HSTS(HTTP Strict Transport Security)の設定をする。

  • DNSタブ
    • Type:CNAME, Name:blog, Value:bobuhiro11.github.io, TTL: Automatic, Status: DNS and HTTP proxy(CDN)
    • Type:CNAME, Name:bobuhiro11.net, Value:bobuhiro11.github.io, TTL: Automatic, Status: DNS and HTTP proxy(CDN)
    • Type:MX, Name:bobuhiro11.net, Value:xxxx, TTL: Automatic
    • Type:TXT, Name:bobuhiro11.net, Value:xxxx, TTL: Automatic
    • NSを2つ割り当ててくれるので、メモしておく(お名前.comの設定で使う)
  • Cryptoタブ
    • SSL: Flexible
    • Always use HTTPS: On
    • HSTS: Status: On, Max-Age: 6 months ,Include subdomains: On ,Preload: On ,No-sniff: On
    • Automatic HTTPS Rewrites: On

お名前.comの設定

  • DNSサーバを、Cloudflareのところでメモした*.ns.cloudflare.comに変更する
  • お名前.comが管理しているDNSサーバ*.dnsv.jpのDNSレコードは削除しておく

設定情報の確認

しばらく待ってから、設定が正しく入っているかを確認

$ (dig bobuhiro11.net A @8.8.4.4; dig blog.bobuhiro11.net A @8.8.4.4; dig bobuhiro11.net NS @8.8.4.4; dig bobuhiro11.net SOA @8.8.4.4;) | grep IN | grep -v "^;"
bobuhiro11.net.         299     IN      A       104.27.171.197
bobuhiro11.net.         299     IN      A       104.27.170.197
blog.bobuhiro11.net.    299     IN      A       104.27.171.197
blog.bobuhiro11.net.    299     IN      A       104.27.170.197
bobuhiro11.net.         86399   IN      NS      mark.ns.cloudflare.com.
bobuhiro11.net.         86399   IN      NS      sima.ns.cloudflare.com.
bobuhiro11.net.         3599    IN      SOA     mark.ns.cloudflare.com. dns.cloudflare.com. 2025896629 10000 2400 604800 3600

GithubやBitbucketのプライベートリポジトリでCI/CD

19 Jun 2017
[ci] [cd] [git]

GithubやBitbucketで管理している プライベートリポジトリに対して、 CI(継続的インテグレーション) とCD(継続的デリバリー)を実現する。 ここでは、werckerを使って、 テストとデプロイを自動化する方法をメモしておく。 werckerは、GithubとBitbucketどちらのプライベートリポジトリにも対応しており、 今のところ無料で利用できる。

デプロイ先はherokuとした。 herokuは、基本的に無料で、手軽にPaaS環境を利用できる。 ただし、無料枠での利用においては、 30分間アクセスがないとスリープ状態になったり、 稼働時間を月1,000時間に制限されたりするので注意する。

想定するアプリケーションは、Python3とFlask(Python向けウェブアプリケーションフレームワーク)で実装されたウェブサービスとする。

まず、リポジトリに3つの設定ファイル(wercker.ymlProcfileruntime.txt)を追加する。 wercker.ymlは、werckerで利用する設定ファイルで、 大きく分けてbuildとdeployという2つのセクションから構成される。 buildセクションには、依存ライブラリのインストールと、ユニットテストの実行について記述する。 一方、deployセクションには、 herokuへのデプロイ時に参照するSSHキーペアの名前(key-name: HEROKU)を指定する。 HEROKUという名のSSHキーペアの作成手順は、後述する。 また、最新版のheroku CLIを利用するために、 install-toolbelt: true と記述しておく。 Procfileには、アプリケーション名: コマンドの記法で、起動コマンドを記述する。 さらに、runtime.txtでPythonのバージョンを指定する。 特に指定がなかった場合には、Python 2系がインストールされるので注意する。

# wercker.yml
box: python
services:
build:
    steps:
        - script:
            name: install dependencies
            code: |
                pip3 install -r requirements.txt

        - script:
            name: test
            code: |
                python3 -m unittest discover -v && flake8 .
deploy:
    steps:
        - heroku-deploy:
            install-toolbelt: true
            key-name: HEROKU
# Procfile
web: python -m web.main
# runtime.txt
python-3.6.1

続いて、werckerのウェブUIで、wercker・リポジトリ間の紐づけと、CIにおけるパイプラインを設定する。 werckerのユーザ登録後、アプリケーションをdashboardから追加し、リポジトリと紐付ける。 アプリケーションの追加後、Workflowsタブを選択し、Add new pipelineからdeployを追加する。 さらに、masterブランチへのPUSHのタイミングで、 builddeployが連続して動作するように、ウェブUI上でbuildの直後にdeployをつなげる。 また、deployの実行中にherokuへログインするために、 EnvironmentタブのGenerate SSH Keysボタンから、SSHキーペアを生成し、 環境変数(HEROKU_PRIVATEHEROKU_PUBLIC)として格納する。 加えて、Environmentタブから、 herokuのアプリケーション名を指示する環境変数 (変数名はHEROKU_APP_NAME、値はherokuのアプリケーション名)を登録する。

最後に、herokuのウェブUIから、werkcerで生成したSSH公開鍵をherokuへ登録する。 masterへPUSHしたときに、buildからdeployまで正常に動作すれば完了。 他の参考ページを見てみるとherokuのAPI KEYが必要だと書かれているが、 今回のアプリケーションでは、 werkcerのEnvironmentタブや、WorkflowsタブのPipeline environment variablesを見てみても、 そのような記述はなかった。 ただ、API KEYを使って何か設定した記憶があるので、おそらく必要なんだろうと思う。 が、どこで設定したのか覚えていない。。。


能登和倉万葉の里マラソン2017

08 Apr 2017
[marathon]

2017.3.12(日)に,石川県七尾市で 開催された能登和倉万葉の里マラソン2017 に参加した. 初めてのフルマラソンだった. 高低差が多いのは難点だったが, 七尾西湾を一周するコースだったので, 漁港,山,海,橋など自然を満喫できた. さらに, 能登マ丼(牡蠣の入った丼)やおにぎりを頂いたり, 気温・天候がちょうど良かったりと終始気持ち良かった.

5km毎の通過時間とラップタイムを記録しておく. だんだんとペースが落ちていて,安定していない. 序盤から中盤にかけて11本の坂道があり, そこで調子に乗ってスピードを出しすぎたと思う. 20km地点で, 右足の足裏が痛みだしたが, 中敷きを外すと楽になったので走り続けた. その後,30km地点を過ぎたあたりから急に歩くのすら辛くなって, 休憩しつつ歩いた. 結果,順位は5,152人中3,096位. ちなみに,去年12月に参加したハーフマラソンでは, 1:47:54という結果だったので, なかなか単純計算ではいかないなという印象.

  • 05km 00:36:55
  • 10km 01:06:53 (29:58)
  • 15km 01:38:46 (31:53)
  • 20km 02:12:33 (33:47)
  • 25km 02:55:19 (42:46)
  • 30km 03:42:12 (46:53)
  • 35km 04:32:00 (49:48)
  • 40km 05:22:07 (50:07)
  • ゴール 05:41:03

前日に18切符で会場周辺まで移動して前泊. マラソン後は,温泉に浸かり, サンダーバードで帰阪. またの機会があれば,後泊したい.


Go言語のメモ

21 Jan 2017
[golang]

基礎からわかるGo言語」を読んだ. 2012年に発行された本なので,Goのバージョンがちょっと古い(Go 1.0.3)が,構文や考え方は大きく変わらないと思う. 今後,Go言語を使うことになるかもしれないので,気になったところをメモしておく.

  • インスタンスの作成と初期化: インスタンスの作成方法には,下のコード例のように,いくつかの選択肢がある. 特に気を付けないといけないのは,Go言語にもポインタの概念があるので, ポインタと実体のどちらが生成されるのかという点である. また,インスタンス作成時に,構造体の要素に対する初期値を明示しなかった場合には, その要素にはゼロ値が割り当てられる点にも注意する. 例外として,スライス,チャネルおよびマップは参照型なので,newの代わりに,makeを使う.
var x T     // T型
y := T{ }   // T型
z := &T{ }  // *T型
w := new(T) // *T型
  • ブランクフィールド: 構造体の定義において,_ byteのように名前のない要素を定義すると,パディングを実現できる.
  • インターフェースとnil: インターフェースに,T型の値xを代入すると,内部では<T,x>のように型と値が組になって格納される. これに起因して,インターフェース型の値pnilかどうかをチェックするときは, 単純にnilと比較するのではなく,reflect.ValueOf(p).IsNil()を使う.
var p1 *int
var p2 interface{} = p1
fmt.Println(p1 == nil) // true
fmt.Println(p2 == nil) // false
fmt.Println(reflect.ValueOf(p2).IsNil()) // true
  • リカバリ: 配列の範囲を超えたインデクス参照などによって致命的なエラーが発生したとき,あるいはpanic関数を呼び出したとき,プログラムはパニック状態になる. パニック状態になると,関数実行は中断され,main関数までコールスタックを遡っていく. ただし,その途中で,defer文によって遅延指定された関数は実行される. deferにより遅延して呼び出しされた関数の中で,そのパニックに対処できた場合は, recovery組み込み関数によって,パニックによるコールスタックの連鎖を中断できる.

  • lenとcap: スライスsは,配列へのポインタ,長さlen,および容量capを持つ. ここで,スライスsの部分要素を参照するためにv := s[2:4]とすると, vsは同じ配列を参照することになる. コピーが欲しければ,明示的に,copy関数を使わなければならない. この辺りは,内部構造を考慮しながら,コーディングしたほうが良い.

  • チャネルの利用例: Goでは,スレッド間のデータ共有を実現するにあたって, 共有メモリではなく,チャネルという概念を導入している. チャネルは,スレッド間で任意のデータをアトミックに送受信するFIFO構造の通信機構である. チャネルを上手に使うと,同期,ミューテックス(セマフォ),およびデータ共有を実現できる. 例えば,同期であれば,ワーカスレッドがタスクの終了と同時にチャネルに何らかのデータを書き込み, マスタスレッドがチャネルを監視するという実装になる. ミューテックスであれば,大きさ1のチャネルを作り, 複数のスレッドからそのデータを取り合う,といった実装になる. データ共有であれば,同じく大きさ1のチャネルを作り, データの取り出しに成功したスレッドが,そのデータを更新し, 再度チャネルに書き込むという実装になる.

  • select文 複数のチャネルから値を受信するとき,switch文に似た構文をもつselect文を使うことで, 受信可能なチャネルを選択できる.