読者です 読者をやめる 読者になる 読者になる

Ansible Galaxyの活用と再利用可能なroleの作成

GitやDocker Engineのインストールなど、よく使われるroleをAnsible Galaxyに登録して再利用する。 その場限りのroleを書くのではなく、再利用できるように作り、テストも行うようにすることでroleの品質も高まる。

態々自分で作らずにAnsible Galaxyで既に公開されているものを使えるならそれでも良いのだが、 検索しづらいためかあまり良いものが見つからない。 評価が高めのものでもGtiHubでコードを見るとイマイチな気がしてしまう。 渡せるパラメータが足りてないetc。。。

検索性が悪いのはAnsible Galaxyに限らず AtlasのVagrant CatalogやDocker Hub, Quay.ioなどでも言える。 結局自分で作ってしまう。

とりあえずUbuntuとArchLinux用のgitをインストールするroleをAnsible Galaxyで公開した。 ソースコードGitHUbで公開されており、Travis CIで簡単なCIも行うようにしている。

galaxy.ansible.com

roleはプラットフォームごとに作る。統合するのは難しい

roleはプラットフォームごとに分けて作っている。 Ansibleではansible_os_familyやansible_distributionでリモートのOSなどを調べて条件分岐できるので、 1つのroleで色々なプラットフォームをカバーできる方が本当は良いと思っているし、最初はそうするつもりだった。 ただ、ansible-galaxy(というかansible)の性質上それは良くない(出来なくはない)ことに気づいた。

  • 条件分岐で飛ばしたタスクの結果(skipped)も標準出力される 本質的なログが埋もれる
  • dependenciesを動的に決定できない 特定のプラットフォーム固有のdependenciesも全てのプラットフォームのdependenciesとして扱われる

特に2番目が大きな問題で、これで諦めた。

最後に

とりあえずgitを作ったので他にもdockerとかgit-lfs, docker-composeとか作る予定。 gitだけでも真面目に作ると結構時間かかる。

Webpackでcss, sassと画像を扱う

WebpackでCSS, Sass, svgを扱う方法を紹介する。 LESSやpng, gifなどには対応していないが、同じように出来ると思う。 サンプルリポジトリも作ってみた。

github.com

参考

stylesheets

github.com

github.com

github.com

github.com


WebpackでCSSを扱う場合、

  • syle-loaderを使ってJSでDOM操作してheadの最後にstyle要素として出力
  • extract-text-webpack-pluginを使って出力したcssファイルをlink要素でロード

のいずれかになる。

extract-text-webpack-pluginを使うメリット・デメリット

GitHub - webpack/extract-text-webpack-plugin: Extract text from bundle into a file.

にメリット・デメリットが挙げられている。 個人的にはextract-text-webpack-plugin使うほうが良いと思っている。

メリット

  • CSS ソースマップを生成できる
  • CSSリクエストをパラレルに出来る
  • CSSを別にキャッシュ出来る
  • 高速(コード量とDOM操作が少なくなる)

デメリット

  • HTTPリクエストが増える
  • コンパイル時間がかかる
  • 設定が複雑になる
  • Public Pathを修正するランタイムがない
  • HMR(Hot Module Replacement)が効かない

@import 及び url()でのパスの扱い

import及びurlは基本的にrequire()と同様に扱われ、 css-loaderで処理される。

ただし、パスが/から始まっている場合、変換されない。 css-loaderで処理したくない場合に使えるが、リクエスト数は増えてしまう。

urlを~から始めると、resolve.rootからの相対パス扱いになる。

Webpackでの画像の扱い

Webpackでは画像をData URIとしてJSやCSSに埋め込むことができる。 これにより、コードのファイルサイズは大きくなるもののリクエスト数を減らすことができる。

JSで画像を扱う場合

JSでimg要素を描画する場合など、画像を扱うことがある。 その時は require()で画像を読み込めばよい。

サンプルリポジトリの説明

GitHub - suzuki-shunsuke/example-webpack

2つのサンプル s1とs2に分かれている。 s1ではimportを使って依存関係を管理し、必要なcssだけをパックしたcssを生成している。 s2ではimportを使わず、全てのcssを結合して1つのcssを生成している。

後者だとWebサイトのページ数が増えるに連れてcssが肥大化し、不要なcssまで読み込まないといけないという問題があるため、 前者のほうが良いと思うのだが、 依存関係の管理をやらないといけないコストはかかる。あとビルドも時間かかる。

SQLAlchemyのバリデーションフレームワークを作った

PythonのORMであるSQLAlchemyのバリデーションフレームワークを以前作ったのでその紹介。

https://pypi.python.org/pypi?%3Aaction=pkg_edit&name=sqlalchemy_validation

github.com

作ったものの拡張性とか柔軟性に欠けるかなと反省している。

経緯

tornadoとsqlalchemyを使ってWebアプリを作っていたのだが、 sqlalchemyにはモデル定義に基づいてバリデーションをする機能はなく、 integer型のフィールドにもstr型の値を入れられたりする。 各フィールドごとに独自のバリデーション関数を定義する機構はあるが、 基本的な型チェックや字数チェックはわざわざ個別に実装しなくても モデル定義に基づいて自動でやってほしいと思った。

そういったライブラリを軽く探したが、何故か良さげなものがなかったので自分で作った。

特徴

サポートしているバリデーション項目は

である。

sqlalchemy標準のクラスを継承して拡張しており、 通常のコードをほとんど修正せずにバリデーション機能を実装できる。

モデルインスタンスのコンストラクタ呼び出し及び属性への値の代入時に バリデーションが実行され、バリデーションに引っかかったら例外を投げるようになっている。

例外クラスは例外の種類に応じて定義してあり、 例外には例外処理時に必要十分なデータをもたせるようにしている。

複数のフィールドに同時に値をセットする場合、 複数の例外を保持出来る専用の例外を投げることで 複数のバリデーションエラーも扱えるようにしている。

ただし、場合によっては一つのフィールドで例外が起こったら それ以降のバリデーション処理を止めて即座に例外を投げてほしい場合もあるので、 そういったニーズにも答えられるようなヘルパー関数を提供している。

SQLAlchemyのようなORMのモデルクラスのフィールドの名前空間は 基本ユーザが自由に使うものであるので、このフレームワークで勝手にメソッドを定義したりはしないようにした。 例えば、モデルクラスにvalidateメソッドを勝手に定義するようなことはせず、 validate関数を別に提供している。

問題点

バリデーションの実行タイミングを選べない

モデルインスタンスのコンストラクタ呼び出し及び属性への値の代入時にバリデーションが実行されるため、 ユーザが明示的にバリデーションを実行する必要はない。 これは良く言えば親切だが、悪く言えばおせっかいであり、余計ともいえる。

自動実行はON/OFFで切り替えられるべきだったなと思う。

バリデーション項目をユーザが自由に拡張できない

最後に

自分が作ったものを紹介する意味で記事書いたけど、 半年以上前に作ったものだからよく覚えていなかった。

Vagrantを使ってVagrant Boxを作成する

チームで共有する

  • Vagrant Box(Ubuntu Server 16.04 で Docker Engineやnodebrewなどインストール済み)
  • 作成したVagrant Boxを起動するVagrantfileのテンプレート

を作成した。

  • 新しくジョインした人が素早く開発に入れる(環境構築の時間短縮)
  • チームで開発環境を統一できる

などのメリットがある。

Packer で作ろうとして諦める

当初はPackerを使ってisoから作成しようと思ったのだが、

www.packer.io

blog.fusic.co.jp

github.com

Ubuntu 16.04用のスクリプトがないし、色々良くわからないのでやめた。

vagrant up, package で作る

Packerで作るのが難しかったので、 vagrant up, package で boxを作成し、共有することにした。

$ vagrant destroy  # 既存のVMを破棄
$ vagrant up
$ vagrant package  # カレントディレクトリに package.boxが生成される

共有方法

  1. Atlas上で公開
  2. ファイルサーバとかで共有

の2つがある。 Atlas上で公開するほうが利便性は当然高いが、privateなboxは有料プラン。 いくらするのかはいまいち分からない

Atlas | HashiCorp

publicなboxをAtlasに公開する場合

  • GitHubと連携
  • Web インターフェースから公開
  • vagrant push
  • atlas-upload-cli

など様々な方法があるようだ。

Creating a New Vagrant Box - Help | Atlas by HashiCorp

Atlas by HashiCorp

Uploading Applications to Atlas - Help | Atlas by HashiCorp

ただ、Webインターフェースから公開以外の方法はうまく行かなかった。

atlas-upload-cliGitHubリポジトリみてもあまり開発されている気がしなかったので試してない。

github.com

vagrant push

$ vagrant login
$ vagrant push --debug

error starting upload: upload: resource not found

エラーが起こってしまい、解消できなかった。

--debugオプションつけてログ見る感じ、 API叩いて404エラー起こっているんだけど、リクエスト先のURLが間違っている気がする。

というかローカルでソースコードをアップロードする以外のことを色々やっている気がするのは気のせいか?

GitHub 連携

github.com

Docker HubのAutomated Buildみたいな機能を期待しているのであって、デプロイまでしたいわけではないのだが、 Terraform使ってるしなんか期待しているのと違う気がする。

ファイルサーバとかで共有する場合

ファイルサーバとかで共有する場合、 boxをローカルにダウンロードした後に

$ vagrant box add hogehoge package.box

とすればhogehogeでboxが使えるようになる。

$ vagrant init hogehoge

PackerとAnsibleを使ってGCEのイメージを作成する

タイトルの通り、PackerとAnsibleを使ってGCEのイメージを作成した。

www.packer.io

www.packer.io

PackerでGCEのイメージを作成する場合、

  1. ベースイメージからGCEインスタンスを作成
  2. インスタンスに対してprovisioning
  3. インスタンスからイメージを作成
  4. インスタンスを削除

という流れになっている。 googlecomputeビルダーのinstance_nameオプションとか、 イメージ作るオプションにインスタンス名とかどういうことなのかと思ったが、 イメージを作成する過程でGCEインスタンスを作成する際に使用されるようだ。 なので、最終成果物には関係ない。

なので例えばnetworkオプションは"default"でSSH接続さえできれば設定する必要はない。

instance_nameはuniqじゃないといけないが、uuid含めると場合によっては字数制限オーバーするので注意。

ビルダーのssh_usernameオプションでリモートユーザ名を指定する。

ansible.cfgとかinventoryファイルは特に用意せず、 playbookのhostsはallにした。

ちょっと困ったのがビルダーのsource_imageの値がわからなかったこと。 ubuntu server 16.04を使いたかったわけだが、どこにこの値が書いてあるのか分からなかった。

Images  |  Compute Engine  |  Google Cloud Platform

Google Cloud Platform

結局Webコンソールからインスタンスの生成フォームにある 「RESTを生成」からsourceImageの項目をチェックしたんだが、

$ gcloud compute images list

で一覧取得できることに今気づいた。。。

Dockerを使ってAnsible Playbookのテストをする

Ansible PlaybookをDockerコンテナに対して実行することでAnsibleのテストを行う。

参考

dev.classmethod.jp

tdoc.info

tdoc.info

本題

今回はPacker + AnsibleでGoogle Compute Engine のイメージを作成したかったので、 そのPlaybookのテストをDockerで行うことにした。 Docker Imageを作ることが目的ではない。


余談だが、自分は今の所Dockerイメージを作るのにAnsibleは使っていない。 Dockerfileで良いと思っている。真面目にImage作るならDocker HubやQuay.ioでAutomated Buildするのが良いと思うし。


今回はGCEのUbuntu Server(16.04)をベースにイメージを作成したかったため、 公式のubuntu:16.04 Dockerイメージを使用する。 ただし、Ansibleでは対象のマシンにPython 2 が必要だが、ubuntu:16.04にはインストールされていなかったため、 python2をインストールしたイメージを用意し、それを使うことにした。

suzukishunsuke/python2:ubuntu1604-0.1.0

ちょっと困ったのが、 Docker Connection Plugin は su, sudoをサポートしていない点である。

Become (Privilege Escalation) — Ansible Documentation

root権限で実行したいコマンドがある場合、su, sudoが使えない以上一般ユーザで実行できない。 なのでリモートユーザはrootで実行した。

一方、GCEではリモートユーザは一般ユーザとして実行し、root権限が必要なタスクではbecome: yesを使う。

しかし、playbookにbecome:yes と書いてしまうと、Dockerに対して実行できない。

そこで以下のようにユーザがrootかどうかで、root権限が必要なところでbecomeするか判定するようにした。

vars:
  become: "{{ (ansible_env.HOME == '/root') | ternary('no', 'yes') }}"
become: {{ become }}

USERではなくHOMEを見ているのは、DockerコンテナでUSERが定義されていなかったから。

Dockerでテストする問題点

ansible_kernelが使えない。 値がホスト(ローカル)の値になる。

例えば自分はArch Linux使っているので 4.6.4-1-ARCH

Docker + Nginx (+ Fabric)でブルーグリーンデプロイを実現する

neocorno.hatenablog.com

に引き続き、今度は

1つのアプリケーションサーバにNginxとDockerコンテナ2つをホスティングすることでブルーグリーンデプロイを実現する方法を紹介する。

nulab-inc.com

を参考にしたが、改めて見ると参考というかそのまんまな気がしてきた。

実現したいこと

  • Dockerコンテナでアプリケーションを動かし、ダウンタイムを限りなく0に近い方法でデプロイしたい
  • デプロイ後に問題が発覚したら素早く元のバージョンに戻したい

使用したツール

  • Nginx
  • Docker Engine

方法

ブルーグリーンデプロイを行う。 アプリケーションサーバに2つのアプリケーションコンテナを用意し、片方は普段停止しておく。 もう片方の稼働しているコンテナにNginxがリクエストをフォワードしておく。 デプロイ時には停止しているコンテナを起動し、ソースコードをデプロイする(デプロイの仕方は自由。コンテナを新しく作り直しても良い)。 この間、もう片方のコンテナはそれまで通り稼働したままである。 デプロイに問題が起こっても、サービスが停止したりはしない。 デプロイが完了したらNginxのフォワード先を変更し、元のコンテナを停止する。

概要図

draw.ioで図を描いてみた。

f:id:neocorno:20160716155846p:plain

NginxはDockerコンテナじゃないの?

基本自分はあらゆるアプリケーション、ミドルウェアをDockerコンテナとして動かすべきなのではとまで思っているが、 今回のNginxはDockerコンテナではなくホストOSにインストールしている。 NginxをDockerコンテナとして動かす場合、Nginxコンテナ作成時にフォワードするコンテナをlinkオプションで指定しないといけず、 後からアプリケーションコンテナを作り直したりする際に困るため。

github.com

みたいに動的にフォワードできたら良いのだけれど、どうやるのかよくわからない。

Nginxのフォワード先の切り替え方

/etc/nginx/nginx.confから /etc/nginx/conf.d/upstream.confという設定ファイルをインクルードする。 それとは別に2つの設定ファイル /etc/nginx/conf.d/app1.confと /etc/nginx/conf.d/app2.confを用意し、 /etc/nginx/conf.d/upstream.confはこの2つの設定ファイルのどちらかへのシンボリックリンクとなっている。 このリンク先を切り替えることでフォワード先を切り替えている。