Foreverly

メモ帳

PyconJP 2016〜2日目〜

PyconJP 2016の2日目も行ってきました。 初のPyconJPでしたが、楽しかったです。 スタッフの皆さん、スピーカーの方々ありがとうございました!

忘れないようにメモ代わりに雑に書きます。 よかったらPyconJP 2016〜1日目〜も読んで下さい。

Keynote

眠くて間に合うか心配でしたが、ギリギリ間に合いました。 雨で電車が止まらなくてよかった。

2日目はAndrey Vlasovskikhの基調講演でした。 Pythonの型ヒントにcommitしてる人らしいです。 ロシアから来てくださって、故郷のサンクトペテルブルクはモスクワの次に多きいところで、 日本でいえば大阪みたいなところと言って面白かったw 写真を見せてくれましたが、北ヨーロッパに属するらしく 情景が美しかったです。

話の内容としてはPython3.6の機能についてでした。 文字列リテラルと変数、数値リテラルの変更点など、 詳しくはドキュメント読みましょう。 Python3推しは無理にしないで、客観的に話すと言っていました。 あるアンケートではPython2と3の使ってつ人の比率は50%が3系で、 56%は2系を使っているらしく、一部重複しているのは2と3を使っている人がいるから。 PyconUS2020でPython2のさよならパーティがあるらしく、 これからPython書くなら3系ですね。 2017年に、Django2.0でのpython2のサポートも消えるらしいです。

Pythonの型ヒントはPython3.0から登場したが、当初はSyntaxだけで、semanticsは提供されなかった。 3.5で標準化されたて、Mypy,PyCharmで使えるようになった。 Python3の型ヒントの良くないところは静的型付けなのでコードを書く量が増えるし、小さいプロジェクトなら不要。 あとは100%の互換性があるわけではない。

型付けのケーススタディはPython2からPython3に準拠させたら6万ドルかかった企業(twisted)があった。 また、Dropboxでは型ヒントを追加してからPython3に移行した。

Async-Awaitは3.6ではyieldとawaitとasyncが使えるようになる。 Async-Awaitのいいところ。Coroutineとawaitable。 Async-Awaitのわるいところ。同期版があれば非同期版をかかないといけない。 DjangoとFlaskが使えなくなるので、Tornadoやaiohttoに移動しないといけない。 TornadoはFlaskと同じくらいpopularになってきたらしいので、 Tornadoも触っておきたいです。

質問では英語で質問する人が多かったので同時通訳の人が大変そうだった。。。 個人的には日本語で質問して、英語で話したいのなら懇親会とかセッションの合間とかに 話すのがいいんじゃないかなあとカンファレンス行くと毎回思う。

You Might Not Want Async (in Python)

台湾の方で、発表のネタがアジアっぽかった。 syncよりasyncの方がCPU待ち全然ないのすごかったです。 Python3.5の機能良さありました。

Lunch

ビュッフェ形式でした。美味しかったです。 特にだれとも話さずモクモク食べてた。

ジョブフェア

ビュッフェ形式ならジョブフェア人が少ないのではと思ったけど、 そんなことなかったです。 朝会してるとか、リモートワークの是非とかありました。 リモートワークの話題でrebuild.fmの名前が出てきたw リモートワークはワークライフスタイルではないので、 個人的には日本だとリモートワークをワークライフスタイルって考えてるから 上手くいかないんだろうなあと思います。 リモートワークは世界に展開してなかったり、 地方などにいる優秀なエンジニアを雇いたいって企業がやるものなので、 リモートワークやっている企業の人が、多様性を持ちたいから海外の人を雇うために リモートワークをしていると言っていて、 まさしくそうで、外国籍の人を雇いたいって企業がリモートワークやればいいだけの話で、 ウチは反対ですっていう会社はそもそもやる必要がないんですよね。

HTTPプロクシライブラリproxy2の設計と実装

1日目のやつが移動してきたセッションですね。

proxy2の話で、gzipとdeflateモジュールを使って実装したらしいです。 RFC2616, RFC7230も読んだそうです。 ssl.wrap_socketとかBaseHTTPserver, hyyplib,threading,select.ssl, deflate,gzipなど標準モジュールだけで作れるらしい。

Building Distributed System with Celery on Docker Swarm

台湾の方の英語セッションで Docker Swarmということを期待して参加しました。 Celeryも少し調べておきます。

from celery import celery

発表中に発表聞かないで、自分の発表資料作る人がいて、う〜んって感じだった。

おやつ

カップケーキでした。すごいかわいいし、美味しかったです。

はじめて作るDjangoプラグイン

ぎぎにゃんの発表。 ク社rails 使ってるイメージだけど、趣味でDjango使ってるのかなと思ったら、 まさしくそうらしいです。

ぎぎにゃんが作ったプラグインの紹介 django-debug-toolbar-vcs-info

ぎぎにゃんの友達が作ったプラグインの紹介 django-permission

プラグインまとめサイトの紹介(2年近く放置されているらしいです。。。) awesome-Django

プラグインを書くときの注意点を紹介してくれました。

実装、テスト(TDDでも可)をやってから色んなCI環境を整える。 全ての環境で通ったらドキュメントを書いて公開する。 というのが流れらしいです。

2.3両方サポートするようにテストは厚くするのが大事で、 後方互換性を気にして3で書くのがオススメ。

ディレクトリ構造の紹介。 ソースを置くディレクトリがパッケージ名になる。 テストのディレクトリ。 ドキュメントのファイルとディレクトリ。 パッケージ、ディストリブューシャン用。

  • キーワード

sixは2と3の互換をしてくれるもの。 compat.py run tests.py toxは複数バージョンでテストするのによい。 Django.test.utils Mock setting.pyとTravis.xyl Toz-Travis Readme.xtxtはマークダウンではなくrsdで書く。

Bottle.py ライブコーディング&リーディング

buttol.pyでのwebアプリ作成の紹介

Lightning Talks

1日目よりかは真面目内容だったな。

Closing

虫の鳴き声が聞こえてきて秋を感じる中、クロージング。

会場を貸してくださった早稲田大学ありがとうございました! トイレで喫煙をした人がいたらしい、、、 トイレで喫煙はヤバイでしょ...昭和の不良かな? スポンサーからのプレゼントコーナーはよかったですね モノタロウの工具すごかったwめっちゃデカかったので 当たっても大変だけどよかったですねw

まとめ

来年はLTのネタぐらいは作っておきたいなとおもいました。 普通に知り合いがほしいっすね。 あとは英語セッションも聞けるようになりたいので、NHK英語やっていきだ。 あとは数式も読めるように数学もやっていきだ。 Python3系の機能も覚えるぞ! 来年も参加したいイベントでした。スタッフの方々お疲れ様でした。 楽しかったです。ありがとうございました!

PyconJP 2016〜1日目〜

PyconJP2016の1日目に参加してきたので、 メモ代わりに雑に書きます。

Pycon自体は初参加で、会社のマネーでこれました(感謝)。 会場は早稲田大学だったので、近くて助かりました。 大久保駅新大久保駅から歩いていける距離だったので、 西早稲田が近いことに気づけて知見を得ました。

Keynote

Pythonコミュニティに多大なる貢献をしているJessica McKellarKeynoteでした。(Last Nameはなんて読むかはわからない。) 同時通訳機が置いていない席に座ったので、英語を頑張って聞きましたが、 半分も理解できませんでした。なんとなく、コミュニティ活動について話していた気がする。。。 彼女はプログラミング教育にも力を入れているので、とても尊敬しています。 Python入門の動画を見たり、O'reillyの動画も観たことある。 Hello AppというDjango入門書にもJessica McKellarの動画がおすすめされていたりするので、 Pythonでプログラミングをこれから学ぼうという人はきっと彼女の活動の恩恵を受けるのだろう。 あと、Closingで話に上がったCode of ConductはこのKeynote関連だったんだろうな。。

マイクロサービスを利用する側のパフォーマンス向上策

Pythonでマイクロサービスは珍しいなあと思って聞きました。 モノタロウの方だったのですが、モノタロウってヨッピーさんがインタビューしていた会社だっけかな。 マイクロサービスの話というかパフォーマンスの話で、IO負荷をどうするかという話でした。 マイクロサービス化すすめるとAPIコールが増えて、負荷が増えるようで、 varnishキャッシュやmemcachedAPIサーバに入れて対策したけど、 IO待ちへの対策が不十分だったのでgeventを使うというお話でした。

書き方で、下のような記述をみて闇っぽいなと思いクスッとしました。

from gevent import monkey
monkey.patch_all()

マイクロサービスで負荷増えるってう〜んという感じで、 スケールアウトすれば良いのではという感じもした。

Lunch

お昼はお弁当が出てよかった。しかも豪華! 特に誰とも話さずぼっち飯かましました。。。

Python入門 コードリーディング

たった一ファイルの python スクリプトから始める OSS 開発入門も気になったのですが、 録画されてなさそうなビギナーセッションを観ました。

ファイルの場所とpdbのモジュールの読むとっかかり join,splitの実装を確認して、 どこにファイルがあるのかを確認し、ドキュメントと照らし合わせて読んで見るという使い方を教えてくれました。

ファイルの場所とpdbの使い方

[root@shuhei pywork]# python
Python 3.5.2 (default, Jul 18 2016, 05:05:01)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import oa.path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'oa'
>>> import os.path
>>> os.path
<module 'posixpath' from '/root/.pyenv/versions/3.5.2/lib/python3.5/posixpath.py'>
>>> os.path.dirname(".")
''
>>> os.path.dirname("D://")
'D:'
>>> os.path.join("spam", "egg")
'spam/egg'
>>> print(os.path.join("spam", "egg"))
spam/egg
>>> os.path
<module 'posixpath' from '/root/.pyenv/versions/3.5.2/lib/python3.5/posixpath.py'>
>>> os.path
<module 'posixpath' from '/root/.pyenv/versions/3.5.2/lib/python3.5/posixpath.py'>
>>> os.path.__file__
'/root/.pyenv/versions/3.5.2/lib/python3.5/posixpath.py'

モジュールの読むとっかかり join,splitの実装を確認 どこにファイルがあるのか、確認し、ドキュメントと照らし合わせて読んで見る。

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>> this.__file__
'/root/.pyenv/versions/3.5.2/lib/python3.5/this.py'

ROT13のwiki 暗号化をかけると文章がでてくる

pdbというデバッガを使って一行ずつ確認してみる

import pdb; pdb.set_trace()

d = {}
import pdb; pdb.set_trace()
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))
d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)
        if chr(i+c) == 'E':
                import pdb; pdb.set_trace()

print("".join([d.get(c, c) for c in s]))
  • コマンド
l 複数行
s ステップ実行
c 続ける
b
  • antigravityモジュール
>>> import antigravity
>>> antigravity.__file__
'/root/.pyenv/versions/3.5.2/lib/python3.5/antigravity.py'
>>> import webbrowser
>>> webbrowser.__file))
  File "<stdin>", line 1
    webbrowser.__file))
                     ^
SyntaxError: invalid syntax
>>> webbrowser.__file__
'/root/.pyenv/versions/3.5.2/lib/python3.5/webbrowser.py'
  • os
>>> import os
>>> os.__file__
'/root/.pyenv/versions/3.5.2/lib/python3.5/os.py'

built-inならpythonに組み込まれているからcの実装 cpython,github python自身のソースをみないとだめ。 

>>> import ita
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'ita'
>>> import itaotools
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'itaotools'
>>> import itertools
>>> itertools.__file__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'itertools' has no attribute '__file__'
>>> itertools
<module 'itertools' (built-in)>

数学的基礎から学ぶDeep Lerning

途中から数式が完全に理解できなくてすやぁっとなった。。。 NumpyとScipyで機械学習をやるとのことだったのですが、難しい。。。 微妙に行列っぽくなったと思ったら、いきなりシンプルな線形性のある数式になったふぁっ!?ってなった。 偏微分方程式は解いたこと無いのでわからず。。。 ただ、このセッションでやっていたことが先行発売されていた オライリー社から出版されるゼロから作るDeep Learningで近いので、 この本を読むと良さそうだった。その前に数学だと思うけど。

Python入門 ライブコーディング

ここのコードは不備があるので読み飛ばして下さい。後で修正するつもり。

基礎から学ぶWebアプリケーションフレームワークの作り方は録画にまかせて ビギナーセッションに参加してきました。 Web API の呼び出しといったコードを書いてくれました。

テストツール flake8

インタラクティブシェル ipython

なにが良いかというとtab補完

ipyhonを使わないならreadlineを使うと良いが、ipythonを使った方が楽。

>>> import readline, rlcompleter
>>> readline.parse_and_bind('tag: complete')
>>> import sys
sys
>>> import sys

デバッグツール pdb ipdb 処理止められて変数を確認できる

c continu
n nextで一行図ス

slack apicurl で実行する

pip install request

import requests
import sys

# print(sys.argv)
# sys.exit(0)

url = slackのapiのurl

r = requests.get(
    'https://api.github.com/user') # urlのパラメータと分離左折
token =

channel = 

# text =
# text = 'こんにちは'
if len(sys.argv) < 1: # 例外処理
    print('引数を指定してください')
    sys.exet(0)
text = sys.argv[1]

pretty = 0

params = {
    'token': token,
    'channel': channel,
    'type':
    'pretty': 
    'as_user': True,
}

r = requests.get(url, params=params)
# print(r.text)
data = r.json()
# import ipdb; ipdb.set_trace()
# print(data)

import pprint
pprint.pprint(data)
pp data
type(data)
pp data['channnel']

git cloneしてlocalにライブラリドキュメントもってきて調べるとよい。

import requests
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()

url = ''

app_id =

params = {
    'appid': appi,
    'category': id,
}
r = rewuests.get*url, params-params)
# print(r.text)
root = ET.fromstring(r.text)
pritn(root)
for resut in root:
    for item in enumerate(resut, 0);:
            if i == 0:
                    continu
            import ipdb; ipdb.set_trace()
            print(item)

xmlをパースする xml.extree

for i in root:oprint:i)
for j in root:result
pp item
for i in item: print(i)
item.find('{urn:yahoo:jp:auc"}')

python入門ビギナー向け勉強会で続きをやってくれるとのことでした。

How Python helped create the visual effects for an Emmy nominated TV show

英語セッションでよくわからなかった。。。 英語やらなあかんね。。。

LT

  • カラオケ採点
  • 師匠探し
  • 仮想通貨
  • 振り返り
  • 虹工房

すべて良いLTでした。 虹工房はLTはもったいない内容。

Closing

Code of Conduct守ろうな話。

Party

TwitterでFollowしているけど、会ったことがない人達(ハムカズ先生とくーむさん)に会ってみようと目標を立てて参加。

すてにゃんさんはやぱちーで会ったことがあったけど、 顔とTwitter ID絶対に一致されてないだろうなと思ったので、長く話せてよかった。 アイドルなだけあり、みんながすてにゃんさんに話しかけていて、自分はそのついでに話をさせていただいたりした。 すてにゃんというハブで他の参加者が繋がるというのは良さあった。

hamukazu先生とも話せてよかった。まともな言動をする立派な大人だった。仕事も忙しそうだ。 コミュニティでの活動をしているからか、知り合いが多そうで、色んな人が声をかけていた。

くーむさんはすてにゃんさんが紹介というか話すついでに話せた。 Twitterのアイコンを認識されていたのだろうかはわかんないけど、 これを機に覚えていて貰えれば幸いです。

あとはツイッターIDを交換したり、しなかったりですが、 繋がりを大事にしていきたいと思いました。

来年も参加したい。というか2日目寝坊しないようにしたい。

Docker入門

Dockerとは何か。

軽量な仮想環境を実現するためのツール。 OSやアプリの設定したものをイメージ化して保存できる。 Docker環境の別のマシンにも移すことができる。

なにをする。

DockerがインストールされたOSを用意する。 OSやアプリが入った実行imageを実行するdocker run 今回は実行imageはDocker Indexから取ってくるdocker pull 実行したらContainerというものができる。 Containerで設定やアプリ、インストールしたりして設定する。 そこから新しくimageを作る場合はdocker commit 作成したimageを他のマシンで動かしたい場合は docker pushでimageをDocker Indexに持って行き、 移したい先のマシンでdocker pull , docker runをすればよい。 imageを持ってきたらContainerができる。

OSインストール

Vagrantbox.esから Official Ubuntu 14.04 daily Cloud Image amd64 (Development release, No Guest Additions)のイメージを使用します。

使い方

To use the available boxes just replace {title} and {url} with the information in the table below.

$ vagrant box add {title} {url}
$ vagrant init {title}
$ vagrant up

なので

$ vagrant box add trusty64 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
$ mkdir Docker
$ cd Docker/
$ vagrant init trusty64
$ vi Vagrantfile

config.vm.network “private_network”, ip: “192.168.33.10”

config.vm.network “private_network”, ip: “192.168.55.44”

$ vagrant up
$ vagrant ssh

公式サイトを参照してインストールします。

Docker1.12.1がインストールされました。

$ sudo docker --version
Docker version 1.12.1, build 23cf638

また、CentOS7にインストールするならyumで良いと思います。

yum -y install docker
systemctl start docker
systemctl enable docker

Dockerの操作

DockerでcentosのimageをsearchでDocker Indexから探してみます。 imageが見つかったらdocker pullで引っ張ってきます。 docker imagesでimageをもってこれたことを確認しました。 imageを消すにはdocker rmiで削除できます。

$ sudo docker search centos |more
NAME                            DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
centos                          The official build of CentOS.                   2613      [OK]
jdeathe/centos-ssh              CentOS-6 6.8 x86_64 / CentOS-7 7.2.1511 x8...   29                   [OK]
jdeathe/centos-ssh-apache-php   CentOS-6 6.8 x86_64 / Apache / PHP / PHP M...   19                   [OK]
nimmis/java-centos              This is docker images of CentOS 7 with dif...   15                   [OK]
million12/centos-supervisor     Base CentOS-7 with supervisord launcher, h...   12                   [OK]
torusware/speedus-centos        Always updated official CentOS docker imag...   8                    [OK]
[以下略]
$ sudo docker pull centos
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              980e0e4c79ec        7 hours ago         196.7 MB
hello-world         latest              c54a2cc56cbb        9 weeks ago         1.848 kB
$ sudo docker inspect [IMAGE-ID]
$ sudo docker images
$ sudo docker rmi [IMAGE ID]
$ sudo docker images

centosというimageを実行してContainerを作りコマンドを実行します。

$ sudo docker run centos echo "Hello World"
Hello World

Containerが作られたかを確認するためにdocker psを実行しても確認できません。 動作が完了してしまっているので表示されませんでした。 既に完了しているContainerを確認するためにはps -aとします。

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS                          PORTS               NAMES
7b88f37bfa42        centos              "echo 'Hello World'"   About a minute ago   Exited (0) About a minute ago                       compassionate_bassi
5fb7e2f68a58        hello-world         "/hello"               About an hour ago    Exited (0) About an hour ago                        amazing_kowalevski

最新5つのContainerを表示させるにはps -a -n=5です。 削除するにはrm CONTAINER IDで削除できます。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps -a -n=5
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS                         PORTS               NAMES
7b88f37bfa42        centos              "echo 'Hello World'"   5 minutes ago       Exited (0) 5 minutes ago                           compassionate_bassi
5fb7e2f68a58        hello-world         "/hello"               About an hour ago   Exited (0) About an hour ago                       amazing_kowalevski
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker rm 7b
7b
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                         PORTS               NAMES
5fb7e2f68a58        hello-world         "/hello"            About an hour ago   Exited (0) About an hour ago                       amazing_kowalevski

今度は実行中のContainerを確認してみます。 バックグラウンドで動かすにはrun -d CONTAINER IDが出力されるので、 logs CONTAINER IDで実行中のタスクログを確認できる。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker run -d centos free -s 3
9396f8bf834796370f781293f9effd11f4af71e45a58025dbad6f8ba133cac20
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker logs 939
              total        used        free      shared  buff/cache   available
Mem:         501712      120312      137480         736      243920      359839
Swap:             0           0           0

              total        used        free      shared  buff/cache   available
Mem:         501712      119892      137796         736      244024      360327
(以下略)

フォアグラウンドに持って行く時はattachを使います。 止めるにはCtrl + C

$ sudo docker attach --sig-proxy=false 939

実行中のタスクをストップするにはkillstop 再開させるにはstart

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
9396f8bf8347        centos              "free -s 3"         2 minutes ago       Up 2 minutes                            modest_brattain
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker stop 939
939
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
9396f8bf8347        centos              "free -s 3"         2 minutes ago       Exited (137) 12 seconds ago                       modest_brattain
5fb7e2f68a58        hello-world         "/hello"            2 hours ago         Exited (0) 2 hours ago                            amazing_kowalevski
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker start 939
939
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                   PORTS               NAMES
9396f8bf8347        centos              "free -s 3"         3 minutes ago       Up 2 seconds                                 modest_brattain
5fb7e2f68a58        hello-world         "/hello"            2 hours ago         Exited (0) 2 hours ago                       amazing_kowalevski

Containerに変更して、imageを作成する まずは-iインタラクティブモードにし、 -tでターミナルを立ち上げ、 imageを指定、 bashを指定してシェルを立ち上げContainerに入ります。

Containerに入れたので/home配下に移動してtest.txtを作成してみます。

$ sudo docker run -i -t centos /bin/bash
# ll
total 68
-rw-r--r--   1 root root 18307 Sep  6 14:02 anaconda-post.log
lrwxrwxrwx   1 root root     7 Sep  6 13:59 bin -> usr/bin
drwxr-xr-x   5 root root   380 Sep  7 20:23 dev
drwxr-xr-x  48 root root  4096 Sep  7 20:23 etc
drwxr-xr-x   2 root root  4096 Aug 12  2015 home
lrwxrwxrwx   1 root root     7 Sep  6 13:59 lib -> usr/lib
lrwxrwxrwx   1 root root     9 Sep  6 13:59 lib64 -> usr/lib64
drwx------   2 root root  4096 Sep  6 13:59 lost+found
drwxr-xr-x   2 root root  4096 Aug 12  2015 media
drwxr-xr-x   2 root root  4096 Aug 12  2015 mnt
drwxr-xr-x   2 root root  4096 Aug 12  2015 opt
dr-xr-xr-x 111 root root     0 Sep  7 20:23 proc
dr-xr-x---   2 root root  4096 Sep  6 14:02 root
drwxr-xr-x  10 root root  4096 Sep  6 14:02 run
lrwxrwxrwx   1 root root     8 Sep  6 13:59 sbin -> usr/sbin
drwxr-xr-x   2 root root  4096 Aug 12  2015 srv
dr-xr-xr-x  13 root root     0 Sep  7 20:23 sys
drwxrwxrwt   7 root root  4096 Sep  6 14:02 tmp
drwxr-xr-x  13 root root  4096 Sep  6 13:59 usr
drwxr-xr-x  18 root root  4096 Sep  6 14:02 var
# cd home/
# ll
total 0
# touch test.txt
# ll
total 0
-rw-r--r-- 1 root root 0 Sep  7 20:23 test.txt

Containerから抜けたらps -aでContainerを確認します。 先ほどできたContainerがあるので、IDを指定してimageを作ります。 docker commitで作成できます。「username/任意の名前」でimageの名前をつけます。 今回では「hoge/test」としました。 sudo docker imagesでimageが作成されたことを確認します。 hoge/testが作成されています。これを元にContainerを作成します。 sudo docker run -i -t hoge/test /bin/bashでContainerに入り、 test.txtがあることを確認。 これでContainerからimageを作成することができました。

# exit
~$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                      PORTS               NAMES
35695d6c9cef        centos              "/bin/bash"         About a minute ago   Exited (0) 26 seconds ago                       kickass_euclid
9396f8bf8347        centos              "free -s 3"         15 hours ago         Up 15 hours                                     modest_brattain
5fb7e2f68a58        hello-world         "/hello"            17 hours ago         Exited (0) 17 hours ago                         amazing_kowalevski
~$ sudo docker commit 356 hoge/test
sha256:c44f82ed12cc43c6b9a24256b0a99f62e69e9ac75ead52e9035f58214904511e
~$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hoge/test           latest              c44f82ed12cc        8 seconds ago       196.7 MB
centos              latest              980e0e4c79ec        23 hours ago        196.7 MB
hello-world         latest              c54a2cc56cbb        9 weeks ago         1.848 kB
~$ sudo docker run -i -t hoge/test /bin/bash
# cd home/
# ll
total 0
-rw-r--r-- 1 root root 0 Sep  7 20:23 test.txt
  • Dockerコマンドのライフサイクルに関わる動作
サブコマンド 意味 実行後の状態
run デプロイ・起動 起動が成功したら稼働中/失敗したら停止中
stop 稼働中のDockerコンテナを停止 停止が成功したら停止中
start 停止中のDockerコンテナを起動 起動が成功したら稼働中
rm デプロイしたDockerコンテナを削除 存在しなくなる(startできなくなる)
  • Dockerコンテナの操作
サブコマンド 意味
exec 稼働中のコンテナでコマンドを実行
logs コンテナのログ(標準出力、標準エラー出力)をする
inspect コンテナ/イメージの詳細情報を表示
  • Dockerイメージ操作のコマンド
サブコマンド 意味
images DockeデーモンにあるDockerイメージ一覧を表示する
rmi DockerデーモンにあるDockerイメージを削除する

Dockerfile

docker build imageから新しいimage作成の自動化 自動化のスクリプトがDockerfile

Dockerfileを作成

vagrant@vagrant-ubuntu-trusty-64:~$ vi Dockerfile

FROMで何のイメージを元にするか指定します。 MAINTAINERで誰が書いたか記述します。 名前とメールアドレスを書いておきます。 コメントは#です。 今回コマンド2つ指定します。 RUNはbuildされるときに実行されるもの。 色んな物をインストールしたりすることができます。 今回はインストールではなく、now building...とだけ表示させます。 CMDはbuildされたimageを元に、runするときに実行されるもの。 (imageからContainerを作るときのこと) 書き方は実行したいものを,で区切り、[]で囲みます。

FROM centos
MAINTAINER FirstName LastName <email address>
# RUN: build時に実行
RUN echo "new building..."
# CMD: run時に実行
# CMD: echo "now runnning..."
CMD ["echo", "new running..."]

buildをしてみます。 sudo docker buildに新しいimageの名前をつけます。 -t hoge/test2とします。 カレントディレクトリにあるDockerfileを使うので.をつけます。 実行するとnow building...と出力されているのが確認できました。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker build -t hoge/test2 .
Sending build context to Docker daemon 13.31 kB
Step 1 : FROM centos
 ---> 980e0e4c79ec
Step 2 : MAINTAINER Gen dockerid <mailaddress>
 ---> Running in f506910e9ee7
 ---> b1af5b08522a
Removing intermediate container f506910e9ee7
Step 3 : RUN echo "new building..."
 ---> Running in b9c7c9ec39a1
new building...
 ---> feb31b681b6d
Removing intermediate container b9c7c9ec39a1
Step 4 : CMD echo new running...
 ---> Running in 858b8d74a51f
 ---> a646d9c9c705
Removing intermediate container 858b8d74a51f
Successfully built a646d9c9c705

sudo docker imagesを実行して、hoge/test2があることを確認できました。 実行するとnew running...と出力されることが確認できます。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hoge/test2          latest              a646d9c9c705        12 seconds ago      196.7 MB
hoge/test           latest              c44f82ed12cc        About an hour ago   196.7 MB
centos              latest              980e0e4c79ec        24 hours ago        196.7 MB
hello-world         latest              c54a2cc56cbb        9 weeks ago         1.848 kB
vagrant@vagrant-ubuntu-trusty-64:~$ sudo docker run hoge/test2
new running...

次はインストールを試してみます。

ApacheインストールするVagrantfileを書きます

build時にRUNでインストールさせます。 imagesの中にファイルを取り込むにはADDを使います。 index.htmlを/var/www/html/にコピーします。 EXPOSEで80番ポートを開けます。 runの時に実行するコマンドを CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]とします。 ※ index.htmlはHello World!という内容で作りました。

FROM centos
MAINTAINER Gen docker id <mailaddress>
RUN yum -y install httpd
ADD ./index.html /var/www/html/
EXPOSE 80
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
# CMD ["/usr/sbin/httpd", "-DFOREGROUND"] でもOK

buildでインストールします。

$ sudo docker build -t hoge/httpd .

ホスト側の8080番ポートをContainer側の80番ポートにリダイレクトさせ、 バックグラウンドで走らせます。 runを実行するとContainerのhttpdが立ち上がります。

$ sudo docker run -p 8080:80 -d hoge/httpd
e23a0bb40467f42650d9dc33dbd77f1927efe7b828d1bb2ba2f18d957a04cf44

Containerのhttpdが立ち上がっているのを確認。

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
e23a0bb40467        hoge/httpd          "/usr/sbin/httpd -D F"   14 seconds ago      Up 13 seconds       0.0.0.0:8080->80/tcp   nauseous_einstein

http://192.168.55.44:8080/にWEBブラウザでアクセスすると Hello World!が表示されました!

docker hubにコンソールからログインします。 Docker Hubにimageをpushします。

~$ sudo docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: hoge
Password:

▽
Login Succeeded
~$ sudo docker push hoge/httpd

ブラウザで、Docker HUBにログインして Repositoryのところに、hoge/httpdがあることを確認

オフィス訪問シリーズ ep1.Wantedly

Wantedlyさんに確認して書いて大丈夫内容となっております。

企業に遊びに行く文化を作ったであろうWantedlyさんに遊ぶに行ってきた。

イケイケなイメージのWantedlyさんへオフィス訪問してきましたので共有します。 オフィス訪問シリーズとあるけど、次回もあるといいな。 (遊びに来てと誘っていただければ行きますのでよろしくお願いします!)

Wantedlyは自分も使ったことのあるサービスだったので、行けて良かったです!

わたしとWantedly

オフィス訪問の前に、 まずはWantedlyの思い出について話させてほしい。 Wantedlyのサービスを実際に使い転職したのですが、 サービスの機能で話を聞きに行きたいという機能のおかげで転職に踏み出せました。 転職したいなあと漠然と思っている時に、自分にはまだスキルが足りない、 転職活動はスキルがもっとついてからやろうとかそんな事を思っていたのですが、 Wantedlyのサイトで企業の採用募集のページを見ていた時に、 気になるリストに追加をしようとしたら、間違えて話を聞きに行きたいをクリックしてしまったのです。 やっちゃったなあと思ったけど、どうせ企業からのレスポンスなんてないだろうと思って、 一時間もたたないうちに、一度オフィスに遊ぶに来てみてくださいというメッセージが届いてしまいました。 いずれは行きたかった会社だったので、ここで無視をしたり、断ったりするのは印象に良くないだろうと思い 休日にオフィス訪問をしたのでした。 その後、深夜作業の待機時にできる履歴書を使って履歴書を書いたのが懐かしいです。 履歴書を買わなくてすむので便利ですよ。

自分のように転職活動はまだいいかなと思っている人でも、 とりあえず話を聞きに行くことって大事だと思います。 そこのハードルを下げる仕組みと、オフィス訪問をしやすい空気をつくったのが Wantedlyのサービスなんだと思っています。

きっかけ

Wantedlyのエンジニアであるkoudaiiiさんから、 何故かオフィスに遊びに来ないかと誘っていただいたので、 自分の中で勝手にロールモデルにさせてもらっている方からの誘いですから、 即、頭を縦に振りました。 (日程を複数提示していただけると行くしかないので知見。)

ちょうど誘っていただく前日ぐらいにkoudaiiiさんのブログ読んで、 SlerからWEBのインフラエンジニアへ転職してみて、 どんなことをして超絶成長することに成功できたのか聞きたかったので願ってもないチャンスでした。 (ここちゃんと聞けなかったなあ。あと行政新聞紙についてもききたかった。)

とはいえ、オフィスに遊び行くってどんなことを話すのか・話せばいいのか 事前に話すことを考えておかないと基本会話苦手なので困りましたが、 最近Wantedlyに遊びに行ったblogを読んだのを思い出し、参考にさせて頂きました。 こちらのblog「「会社に遊びに行く」と具体的に何をして遊んでもらえるのか知りたかったので会社に遊びに行ってきた」を参考にさせて頂きました。

オフィス

夜勤明けで寝坊したら死というプレッシャーの中、約束の時間10分前という理想的な時間につけました。 目黒駅から徒歩10分ぐらいの閑静な所(最寄り駅は白金台)にオフィスがありました。 ビルのワンフロアを借りているらしく、一年前にオフィスを移転してきたと伺いましたが、 かなり広いオフィスで、とても起業から6年とは思えないほど、オシャレなオフィスでした。 学生の時に、chibicodeさんのリツイートでWantedlyのβ版を見たことがありますが、 あれからここまで成長したのかと、成長速度に驚き。 オサレオフィスの写真を完全に撮り忘れたので、各自ホームページとかで見てほしい。

オフィスにはいわゆる受付みたいなところが見当たらず、特に誰もいないのでさっそく詰んだと思ったのですが、 入り口入った直ぐにiPadが置いてあり、そこから担当者を呼び出すシステムとなっておりました。 iPadで担当者の名前をタッチすると、本人を呼び出すみたいです。 ここでもkoudaiiiさんを一覧から見つけられず(本名がぱっとでてこなかった)心が折れかかりましたが、 一番上に担当者呼び出しがあることに気づき、まもなく採用担当の方に案内して頂けました。 (全体的なオフィス案内は特になく、クレイジーダイヤモンドという会議室に直行でした。)

はなしたこと1

「「「会社に遊びに行く」と具体的に何をして遊んでもらえるのか知りたかったので会社に遊びに行ってきた」」を読んで、 会社に遊ぶに来てもらうのは会社とサービスのミッションとゴールを宣伝というか、伝えたいからなんだろうなと思ったので、 Wantedlyのサービス内容とかどんな目標を持って仕事をしているのか調べておきました。

採用担当の方とお話をすることになったので、 さっそく自分が実際にWantedlyのサービスを使ったことがあることや、 会社の遊びにいく機能はいいと思う旨を話しました。 また、採用担当の方からは他のサービス紹介をしていただけました。

  • blogサービス

Wantedlyのblog機能では 実際に起業の方にblogを書いてもらい「働くこと」についてpostしてもらっているらしいです。 就活生とかには役に立ちそうな内容ですね。また企業の採用面でも自社の雰囲気を伝えられるので良さそうでした。

  • チャットサービス

syncというビジネス用グループチャットサービスだそうです。 招待さえすればグループに承認されプロジェクト毎にやりとりができるそうです。 Facebook MessengerやSlackとかとどう差別化するのかなあと思ったんですが、 koudaiiiさんも仕事で使っているのを見ましたが、Slackとどう使い分けているんだろうか。 部屋が乱立しないから集中しやすいのかな。 営業の人とかはSlackよりsyncの方が好みそうなイメージ。

あと印象に残っているのが、採用担当の方が「インフラエンジニアで障害対応をしているってことは コードを書いてるんですよね。」って聞いてきて、 プログラミングができないインフラエンジニアは死という圧を軽くジャブしてきて流石だなあと思いました。

はなしたこと2

遅れてkoudaiiiさんがいらっしゃり、色々と業務でやっていることを説明していただきました。 現在インフラエンジニアが2人だけらしいです。 業務ではほとんどコードしか書いていないとおっしゃっていたのが印象的で、 自動化を推し進めて、一人が何百台のサーバを管理する働き方をしていることがわかりました。 個人的にはインフラエンジニアではなくソフトウェアエンジニアと名乗った方がいいのかなと思います。 インフラエンジニアだと採用が難しそうですが、 ソフトウェアエンジニアでインフラやりたい人Wantedの方が集まりそうだなと思いました。

また、お客さんは社内のエンジニアと言っていて、社内でツールや基盤開発を推し進めているそうです。 実際にいくつかCLIツールを見せてもらいました。 S3のバケット操作をAWSコンソールではなく CLIで操作するs3urlのデモを見せてくれました。 作業を楽にするツールをGithubで公開しているの良いですね!

他にもどんな方向性に向かうか、他の企業の資料を集めてまとめたものや、 他の企業に話を聞いてどんな方向性に進むかを自分たちで決めて進めていくのは よい文化だと思いました。AmazonNetflixのマイクロサービスの事例なども教えて頂けました。

また、公開しない技術を持つことも大事と仰っていて、企業としての競争優位性を保つ考えを持っていたり、 自社サービスの運用しているエンジニアの思考なのかなと思ったりもしました。

一つ馬鹿な質問をしたな〜と家に帰って反省したのが、 「OS, ミドル, NW, プログラミング,etcで、どんなスキルが一番求められますか?」と聞いて、 無論、プログラミングが一番求められると答えられましたが、 ミドルの知識とかチューニング周りよりプログラミングなのか...と思いましたが、 マイクロサービスをやっているから、スケールアップさせるより、 スケールアウトさせるから、どう考えてもプログラミングスキルの方が大事ですよね。 マイクロサービスなにもわかっていませんね。はい。 あとプログラミングできる人はミドルの設定周りもすぐわかると言っていて 「たしかに。」ですね。 SlerからWEBのインフラエンジニアへ転職してみて、どんあギャップがあったか。 そのギャップを埋めるためにしたこととか聞きたかったのに眠気で忘れてた。 体調万全の状態でお話伺いたかった。。。 (どんなことをして超絶成長することに成功できたのか聞きたかったけどやるしか無いんだろうなあ。あと行政新聞紙についてもききたかった。)

会社の雰囲気

平均年齢は30すこしぐらいらしいですが、みんな仲がよさそうで、 コミュニケーションをちゃんとと取られているようでした。 お昼も10人ぐらいでみんなで食べに行ったりしているそうです。 他にも、各チームごとに成果発表会や、勉強会、シャッフルランチなどを定期的に行い、 他のチームの人ともコミュニケーションが取れる仕組みが作られていて、 考えられていました。他の人と話したことがない、どんな人かよくわからないっていう事が 起こりにくそう。(人間関係で悩まされるの無駄でしか無いですからね...) どんな人が多いか聞いたところ、すごく真面目ですごく優秀な人が多いとのことでした。 会社の急成長も良い人材が集まっているからできるのでしょう。 会議とかも多いイメージでしたが、自社サービスのエンジニアの一つの特徴なのでしょうか。

最後に

中途でも面接前に一日インターンというのをやっているらしく、 実際に働いてみてどうかを確認するらしいです。 勉強会に参加する気分で一日インターンに申し込んでみても良いかもしれません。 優秀な人と働くことって刺激にも勉強にもなりそう。 (ここまで考えて、英語の勉強のために外資の採用面接を受けるって書いて避難をうけていたblogを思い出した。 冷やかしは迷惑ですね。自重しましょう。) GoのCLIツール作成やRailsの知識を持っているエンジニアがほしいそうです。 あたしじゃんと思った人はまずはWantedlyに遊びに行ってみてはいかがでしょうか。

Wantedlyさんありがとうございました。

severspec入門

Serverspec

Apache,PHP,MySQL,Nagiosをインストールして インストールされているかをテストしていきます。

ServerspecはRubyが必要なのでインストールをします。 こちらを参考にインストールしてみてください。

Rubyをインストールしたら、 Ruby用のgemというソフトウェアリポジトリからServerspecをインストールします。

# yum install rubygems
# gem install serverspec

インストール完了したら Serverspec用にディレクトリを作成し、テストをしてみます。

# mkdir ~/servertest
# cd servertest/
# serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: web
 + spec/
 + spec/web/
 + spec/web/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

rake spec実行時にエラーが出たのでまとめる

  • "set :request_pty, true"を書いてくれ

spec_helper.rbに追記して事なきを得ました。

Please write "set :request_pty, true" in your spec_helper.rb or other appropriate file.
  • HighLine か Termiosをインストールしてくれ

gem install highlineで事なきを得ました。

Text will be echoed in the clear. Please install the HighLine or Termios libraries to suppress echoed text.
Wrong sudo password! Please confirm your password on localhost.

これはこちらを参考にしました。CentOS 6.5でserverspecサーバ構築 その2 こちらも参考【入門】serverspecでSSH経由でリモートホストにspec流す sudoパスワードを聞かれるようにしないといけないようですね。 rake specのコマンドを少し変えれば良いらしい。

$ SUDO_PASSWORD=xxxxxxxx rake spec

もしくは

$ ASK_SUDO_PASSWORD=1 rake spec

これでOK。

これらのエラーメッセージは環境によっては聞かれなかったので rubyのバージョンとかが原因なのかなあ。

サンプルのテストファイルを削除なり、 名前変更するなりしておきましょう。 書き方の参考に読んでおくのもよいです。

# mv sample_spec.rb{,.bak}

Apacheインストール

serverspecでApacheインストールのテストを書きます。

$ vi httpd_spec.rb
require 'spec_helper'
describe package('httpd') do
    it { should be_installed }
end

テストが通るようにApacheyumでインストールして、 サービスを起動させます。

# yum install httpd
# service httpd status
httpd は停止しています
# service httpd start
httpd を起動中: httpd: apr_sockaddr_info_get() failed for shuhei
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
                                                         [  OK  ]
# chkconfig --list |grep htt
httpd           0:off   1:off   2:off   3:off   4:off   5:off   6:off
# chkconfig httpd on
# chkconfig --list |grep htt
httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

PHPインストール

php、php-devel、php-mbstring、php-gdが入っているテストを書きます。

require 'spec_helper'

%w(php php-devel php-mbstring php-gd).each do |pkg|
  describe package(pkg) do
    it { should be_installed }
  end
end

yumでインストールします。

# yum install php php-devel php-mbstring php-gd

MySQL5.6インストール

MySQLのインストールには2通りあります。

yumrpmでインストールの2種類あり、 MySQL5.6まではインストール方法でパッケージやサービス名に 差異があるとのこと。 5.7からは差異はないらしい

yumコマンドでのインストールは簡単なのでおすすめですが、 インストールされるパスが固定されるので、 1台のサーバに複数のMySQLをインストールすることができないです。 また、yum upgrade時にMySQLサーバがマイナーバージョンアップされることがあるらしく、 インストール後に無効化する必要があります。 メジャーバージョンアップはされないように、メジャーバージョンアップごとに 違うリポジトリ名が割り当てられています。

(今回は1台のサーバにMySQLサーバ2台たてて、レプリケーションを行いたいので、 rpmパッケージでインストールしていきます。)

  • rpmパッケージを使用したインストール

MySQLサイトからダウンロードします。 /usr/local/src配下にRed Hat Enterprise Linux 6 / Oracle Linux 6 (x86, 64-bit), RPM Bundleをインストールします。

# cd /usr/local/src/
# wget http://dev.mysql.com/get/Downloads/MySQL-5.6/MySQL-5.6.32-1.el6.x86_64.rpm-bundle.tar
# tar xvf MySQL-5.6.32-1.el6.x86_64.rpm-bundle.tar
MySQL-shared-5.6.32-1.el6.x86_64.rpm
MySQL-test-5.6.32-1.el6.x86_64.rpm
MySQL-server-5.6.32-1.el6.x86_64.rpm
MySQL-shared-compat-5.6.32-1.el6.x86_64.rpm
MySQL-client-5.6.32-1.el6.x86_64.rpm
MySQL-embedded-5.6.32-1.el6.x86_64.rpm
MySQL-devel-5.6.32-1.el6.x86_64.rpm

パッケージを-iでインストールします。

# rpm -i MySQL-*.rpm
警告: MySQL-client-5.6.32-1.el6.x86_64.rpm: ヘッダ V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY
エラー: 依存性の欠如:
    MySQL-devel は mysql-devel-5.1.73-7.el6.x86_64 と競合します。

MySQL-devel は mysql-devel-5.1.73-7.el6.x86_64 と競合します。 なんてエラーがでました。mysql-devel-5.1.73-7.el6.x86_64なんて入れた覚えはない。 -eでアンインストールしてみるが、インストールされていないとのこと。。。

# rpm -e mysql-devel-5.1.73-7.el6.x86_64.rpm
エラー: パッケージ mysql-devel-5.1.73-7.el6.x86_64.rpm はインストールされていません。
# yum remove mysql-libs

インストール成功

# rpm -i MySQL-*.rpm

サービスを起動させて3306ポートでLISTENしていることを確認 rpmインストールなのでサービス名がmysqlです。 yumインストールもしくは5.7はmysqldになります。

# service mysql status
 ERROR! MySQL is not running
# service mysql start
Starting MySQL SUCCESS!
[root@shuhei src]# service mysql status
 SUCCESS! MySQL.. running (17092)
# ps auxf |grep mysql
root     17204  0.0  0.0 103320   852 pts/2    S+   17:29   0:00                          \_ grep mysql
root     16989  0.0  0.1  11340  1364 pts/2    S    17:29   0:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --pid-file=/var/lib/mysql/shuhei.pid
mysql    17092  2.2 44.2 1013188 451848 pts/2  Sl   17:29   0:00  \_ /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/lib/mysql/shuhei.err --pid-file=/var/lib/mysql/shuhei.pid
# netstat -atnp |grep mysql
tcp        0      0 :::3306                     :::*                        LISTEN      17092/mysqld

MySQLのプロセスが起動されているか、ポート接続しているかテストを書きます

require 'spec_helper'

describe service('mysqld') do
  it { should be_enabled }
  it { should be_running }
end

describe port(3306) do
  it { should be_listening }
end

describe service('mysql') do
  it { should be_enabled }
  it { should be_running }
end

describe port(3308) do
  it { should be_listening }
end

Nagiosインストール

Nagiosのインストールは公式サイト参照

nagiosがインストールされているテストを書きます。

require 'spec_helper'

describe service('nagios') do
  it { should be_enabled }
  it { should be_running }
end

/usr/local/src配下に 最新版4.2.0をサイトからインストールします。

# wget https://assets.nagios.com/downloads/nagioscore/releases/nagios-4.2.0.tar.gz#_ga=1.247838018.451693304.1472123299
# wget https://nagios-plugins.org/download/nagios-plugins-2.1.2.tar.gz#_ga=1.179877985.451693304.1472123299

必要なパッケージをインストール

yum install gcc glibc glibc-common
yum install gd gd-devel

Nagiosユーザとグループを作成

# /usr/sbin/useradd -m nagios
# passwd nagios
# /usr/sbin/groupadd nagcmd
# /usr/sbin/usermod -a -G nagcmd nagios
# /usr/sbin/usermod -a -G nagcmd apache
# less /etc/passwd
# less /etc/group

ダウンロードしたfileを解凍

tar xzf nagios-4.2.0.tar.gz
cd nagios-4.2.0

作成したグループを指定してconfig scriptを実行

./configure --with-command-group=nagcmd

コンパイル実行

make all

インストール実行

make install
make install-init
make install-config
make install-commandmode

web configをインストール

make install-webconf

Nagiosのweb画面にログインユーザーnagiosadminを作成

htpasswd -c /usr/local/nagios/etc/htpasswd.users nagiosadmin

Apache再起動

service httpd restart

pluginのコンパイルとインストール

# ./configure --with-nagios-group=nagios --with-mysql=/usr/bin/mysql
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... configure: error: newly created file is older than distributed files!
Check your system clock

OSとハードの時間が実際の時間とずれているのが原因でインストールに失敗する (2016/08/25)

# date
2016年  7月 24日 日曜日 18:17:55 JST
# hwclock
2016年07月24日 18時17分58秒  -0.524383 秒

時間を合わせればインストールできる。

 date -s "08/25 20:39 2016"
2016年  8月 25日 木曜日 20:39:00 JST
 date
2016年  8月 25日 木曜日 20:39:02 JST
 hwclock
2016年07月24日 18時20分17秒  -0.148454 秒
 hwclock -w
# hwclock --systohc
# hwclock
2016年08月25日 20時39分16秒  -0.051022 秒

nagiosがインストールされているテストを書きます。

require 'spec_helper'

describe service('nagios') do
  it { should be_enabled }
  it { should be_running }
end

Ansible入門

Ansible

Ansibleサーバのhostを立てて、 AnsibleでWEBとDBサーバを構築していく。 Ansibleでは管理される側にはPythonが入っていて、 あとはSSH接続ができればよい。

  • やっていくこと

Ansibleのインストール SSH接続確認 Inventoryファイル編集 # どのサーバを管理するのか記述する ansible.cfg編集 # Ansibleの設定ファイル Playbook編集 # 管理するサーバの構成を記述

  • 構成

HOSTサーバ:Ansible WEBサーバ:Use,Apache,PHP DBサーバ:Use,MySQL

ここVagrantの手順でサーバを立ち上げます。

Vagrantfileは以下の設定を追記します。

# 変更箇所
  # config.vm.box = "bento/centos-6.7"

# 追加箇所
  config.vm.define "host" do |node|
    node.vm.box = "bento/centos-6.7"
    node.vm.hostname = "host"
    node.vm.network :private_network, ip: "192.168.43.51"
  end

  config.vm.define "web" do |node|
  node.vm.box = "bento/centos-6.7"
    node.vm.hostname = "web"
    node.vm.network :private_network, ip: "192.168.43.52"
  end

  config.vm.define "db" do |node|
  node.vm.box = "bento/centos-6.7"
    node.vm.hostname = "db"
    node.vm.network :private_network, ip: "192.168.43.53"
  end

サーバを立ち上げ、立ち上がっているのを確認

$ vagrant up
$ vagrant status
Current machine states:

host                      running (virtualbox)
web                       running (virtualbox)
db                        running (virtualbox)

Ansibleサーバのhostへssh接続をします。

$ vagrant ssh host

yumでAnsibleインストールできなかった。

$ sudo yum install ansible
(略)
パッケージ ansible は利用できません。
エラー: 何もしません

epelリポジトリをインストールします

$ sudo yum install epel-release

これはrpmでインストールしてもよい。 https://dl.fedoraproject.org/pub/epel/ 最新はhttps://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpmですね。(2016/08現在)

wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh epel-release-latest-7.noarch.rpm

これでもう一度yumでAnsibleをインストールすればできるはず

$ sudo yum install ansible

Ansibleインストール完了

$ ansible --version
ansible 2.1.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides

SSH接続を鍵認証で行えるようにします。

.ssh/configを作成します。 またパーミッションも変更します。

$ vi .ssh/config
Host web
 HostName 192.168.43.52
 User vagrant
Host db
 HostName 192.168.43.53
 User vagrant
$ ll .ssh/config
-rw-rw-r--. 1 vagrant vagrant 67  8月 29 10:34 2016 .ssh/config
$ chmod 600 .ssh/config
 ll .ssh/config
-rw-------. 1 vagrant vagrant 67  8月 29 10:34 2016 .ssh/config

次に秘密鍵と公開鍵を作成しwebとdbに公開鍵をコピーします。 コピーしたらhostnameを指定してSSH接続できることを確認

$ ssh-keygen -t rsa
$ ssh-keygen -t rsa
$ ssh-copy-id web
$ ssh web
$ ssh-copy-id db
$ ssh db

hostsというinventoryファイルを作成し host側でAnsibleコマンドを実行します。 hostsにはwebとdbの情報を記載します。 ここで[グループ名]でグループをつくることができます。 Ansibleコマンドを実行するときallやhostname指定またはグループ指定ができるようになります。

$ vi hosts
[web]
192.168.43.52

[db]
192.168.43.53

ansible 対象サーバ -i inventoryファイル(フルパス) -m module名

ex.ping module

$ ansible all -i hosts -m ping
192.168.43.53 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.43.52 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

またansible.cfgにinventoryファイルを指定すれば省略可能です。

-i を抜かしてコマンドが実行できます。

$ ansible all -m ping

$ ansible all -m ping
192.168.43.52 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.43.53 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

playbookにサーバにしたい処理を書きます 今回はhogeユーザを追加してみます。 YAMLファイルで記述します。 一行目の「---」YAMLファイルであることを指す 「- hosts」に対象サーバを指定する。今回は「all」で全台へ 管理者権限が必要なので「sudo: yes」とする。 タスクを実行するために「tasks:」モジュールを指定し、 タスクに名前をつけておきます。 - name: add a new userとし、 userモジュールとnameオプションでhogeユーザを追加します。

$ vi playbook.yml
$ cat playbook.yml
---
- hosts: all
  sudo: yes
  tasks:
    - name: add a new user
      user: name=hoge
$ ansible-playbook playbook.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

TASK [add a new user] **********************************************************
changed: [192.168.43.52]
changed: [192.168.43.53]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=2    changed=1    unreachable=0    failed=0
192.168.43.53              : ok=2    changed=1    unreachable=0    failed=0

モジュールは冪等なのですでにhogeユーザがいたら、何もしません。 2回目を実行してもOKで返ってきますが、changed=0となっていることがわかります。 シェルとかだと既にユーザがいたらおかしな挙動をするかもしれませんね。 ここがAnsibleの利点で安全にplaybookを何度も実行できます。 実際にhogeユーザが追加されていることも確認できました。

$ ansible-playbook playbook.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

TASK [add a new user] **********************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=2    changed=0    unreachable=0    failed=0
192.168.43.53              : ok=2    changed=0    unreachable=0    failed=0

$ ssh web
Last login: Wed Aug 31 22:12:23 2016 from 192.168.43.51
 cat /etc/passwd |grep hoge
hoge:x:501:501::/home/hoge:/bin/bash

stateオプションを使ってみる。 stateそれが存在するかどうかを指定できる。 hogeユーザが存在しないとする。

      user: name=hoge state=absent

実行してみるとユーザが消えていることが確認できる。

[vagrant@host ~]$ ansible-playbook playbook.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

TASK [add a new user] **********************************************************
changed: [192.168.43.52]
changed: [192.168.43.53]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=2    changed=1    unreachable=0    failed=0
192.168.43.53              : ok=2    changed=1    unreachable=0    failed=0

[vagrant@host ~]$ ssh web
^[[ALast login: Wed Aug 31 22:37:46 2016 from 192.168.43.51
[vagrant@web ~]$ cat /etc/passwd |grep hoge
[vagrant@web ~]$ logout

ansible-playbookのオプションを確認

--syntax-checkはPlaybookの文法チェック

$ ansible-playbook playbook.yml --syntax-check
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

playbook: playbook.yml

--list-taskはタスク一覧を表示

$ ansible-playbook playbook.yml --list-task
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

playbook: playbook.yml

  play #1 (all): all    TAGS: []
    tasks:
      add a new user    TAGS: []

--checkはドライランを実行

$ ansible-playbook playbook.yml --check

Playbookでは変数も使えます。Playbookを使い回すときに便利になる。

今回はユーザ名を変数で扱ってみます。

[tasks:]の前に[vats:]というのをつけて キーと値を管理します。 ここではusername: hoge 変数とするときは{{キー}}とすればよい

$ cat playbook.yml
---
- hosts: all
  sudo: yes
  vars:
    username: hoge
  tasks:
    - name: add a new user
      user: name={{username}}
]$ ansible-playbook playbook.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

TASK [add a new user] **********************************************************
changed: [192.168.43.52]
changed: [192.168.43.53]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=2    changed=1    unreachable=0    failed=0
192.168.43.53              : ok=2    changed=1    unreachable=0    failed=0

実行時にユーザに変数の値を入力させることもできます。 「vars: prompt」として「username: "Enter username"」とする。 これで実行時にユーザ名が聞かれます。

$ cat playbook.yml
---
- hosts: all
  sudo: yes
#  vars:
#    username: hoge
  vars_prompt:
    username: "Enter username"
  tasks:
    - name: add a new user
      user: name={{username}}
$ ansible-playbook playbook.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default).
This feature will be removed in a future release. Deprecation warnings
can be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Using the 'short form' for vars_prompt has been deprecated.
This feature will be removed in a future release. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.
Enter username: hoge

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.43.53]
ok: [192.168.43.52]

TASK [add a new user] **********************************************************
ok: [192.168.43.52]
ok: [192.168.43.53]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=2    changed=0    unreachable=0    failed=0
192.168.43.53              : ok=2    changed=0    unreachable=0    failed=0

WEBにApacheをインストールするplaybookを書きます。 yumモジュールを使い、state=latestとすると最新版をインストールします。 serviceモジュールを使い、再起動時に自動起動するように設定します。

---
- hosts: all
  sudo: yes
  tasks:
    - name: add a new user
      user: name=hoge

- hosts: web
  sudo: yes
  tasks:
   - name: install apache
     yum: name=httpd state=latest
   - name: start apache and enabled
     service: name=httpd state=started enabled=yes
~

WEBブラウザのURLに192.168.43.52でアクセスするとApacheの画面が出てきます。

次はApacheのドキュメントルートの所有者を変更し、 ファイルを置いてWEBブラウザ上から確認してみます。 最初に適当にindex.htmlを作成してかた、 playbookに書いていきます。

まずは、ドキュメントルートの所有者変更は ファイルやディレクトリに関するfileモジュールを使います。 対象ディレクトリとownerを指定し、recurse=yesにすることで 再帰的に反映されます。

次に、index.htmlファイルを転送するために copyモジュールを使い、 対象ファイルと、対象ディレクトリ、所有者を指定します。

---
- hosts: all
  sudo: yes
  tasks:
    - name: add a new user
      user: name=hoge
 - hosts: web
  sudo: yes
  tasks:
   - name: install apache
     yum: name=httpd state=latest
   - name: start apache and enabled
     service: name=httpd state=started enabled=yes
   - name: change owner
     file: dest=/var/www/html owner=vagrant recurse=yes
   - name: copy index.html
     copy: src=./index.html dest=/var/www/html/index.html owner=vagrant

ansible-playbookを実行するとエラーが出ました。

TASK [copy index.html] *********************************************************
fatal: [192.168.43.52]: FAILED! => {"changed": false, "checksum": "82a4c7e6c69f12b6fdc25ec54a16ef7a1bfbecaf", "failed": true, "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}

libselinux-pythonがインストールされていないとエラーが出たので、 WEBとDBに対してインストールするようにplaybook追記します。

---
- hosts: all
  sudo: yes
  tasks:
    - name: add a new user
      user: name=hoge
    - name: install libselinux-python
      yum: name=libselinux-python state=latest

これで成功。 WEBブラウザで確認し反映されていることを確認。

PHPパッケージインストール

複数のパッケージをインストールする際、 yumモジュールをを何度も書くことよりも、 インストールしたいパッケージをリスト化したほうがよいです。 nameのあとに{{item}}として、 with_itemsを使い、リスト化します。 これでリスト化したパッケージが一つずつ{{item}}に代入され yumが実行されインストールされます。

Apache再起動もplaybook上で行います。 serviceモジュールで再起動しますが、 notifyとhandlersモジュールを使い、 タスクで変更があった場合、再起動するようにします。 handlersは最後に一回だけ呼ばれるもので、 複数の箇所からnotifyで呼ばれても実行するのは最後の一回です。 また、PHPファイルを作成し、WEBに転送して、WEBブラウザ上で表示されるか確認し、 Apache再起動されているか確認します。

    - name: install php packages
      yum: name={{item}} state=latest
      with_items:
        - php 
        - php-devel
        - php-mbstring
        - php-mysql
      notify:
        - restart apache
    - name: copy hello.php
      copy: src=./hello.php dest=/var/www/html/hello.php owner=vagrant
  handlers: 
    - name: restart apache
      service: name=httpd state=restarted

PHPのパッケージインストールと ファイルの転送、Apache再起動が実施されています。

TASK [install php packages] ****************************************************
changed: [192.168.43.52] => (item=[u'php', u'php-devel', u'php-mbstring', u'php-mysql'])

TASK [copy hello.php] **********************************************************
changed: [192.168.43.52]

RUNNING HANDLER [restart apache] ***********************************************
changed: [192.168.43.52]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=11   changed=3    unreachable=0    failed=0
192.168.43.53              : ok=3    changed=0    unreachable=0    failed=0

もう一度ansible-playbookを実行してみると 既にPHPパッケージがインストールされているので、 notifyからhandlersが呼ばれていないので、 Apache再起動されていないのが確認できます。

TASK [install php packages] ****************************************************
ok: [192.168.43.52] => (item=[u'php', u'php-devel', u'php-mbstring', u'php-mysql'])

TASK [copy hello.php] **********************************************************
ok: [192.168.43.52]

PLAY RECAP *********************************************************************
192.168.43.52              : ok=10   changed=0    unreachable=0    failed=0
192.168.43.53              : ok=3    changed=0    unreachable=0    failed=0

DBへMySQLをインストールします。

- hosts: db
  sudo: yes
  tasks: 
    - name: install mysql
      yum: name=mysql-server state=latest
    - name: start mysql and enabled
      service: name=mysqld state=started enabled=yes

DBでMySQLがインストールされていることを確認

$ ssh db
Last login: Thu Sep  1 10:11:31 2016 from 192.168.43.51
 mysql --version
mysql  Ver 14.14 Distrib 5.1.73, for redhat-linux-gnu (x86_64) using readline 5.1

MySQLデータベースを作成し、ユーザを設定します。 mysql_dbモジュールを使用します。 ユーザはmysql_userを使用します。 privでデータベースの全てのテーブルに対し、権限を付与します。

    - name: create a database
      mysql_db: name=mydb state=present
    - name: create a name for mydb
      mysql_user: name=dbuser password=******** priv=mydb.*:ALL state=present

データベースを作るときにpython mysqldbが必要らしいのでインストールする。

TASK [create a database] *******************************************************
fatal: [192.168.43.53]: FAILED! => {"changed": false, "failed": true, "msg": "the python mysqldb module is required"}

変数を使ってまとめてインストールします。

  tasks:
    - name: install mysql
      yum: name={{item}} state=latest
      with_items:
        - mysql-server
        - MySQL-python

DBにSSH接続してデータベースが作成されていることを確認

$ ssh db
Last login: Thu Sep  1 10:19:04 2016 from 192.168.43.51
$ mysql -u dbuser -p mydb
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.1.73 Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb               |
| test               |
+--------------------+
3 rows in set (0.00 sec)

mysql> exit
Bye

itamaeとvagrantでやるテスト駆動--その1-Apache,PHP編

rake specを実行しつつItamaeで構築 hostとwebというサーバをたてて hostにserverspecItamaeをインストールし、 webを構築して、テストをしていきます。

mkdir serverspec_test
cd serverspec_test

Vagrantfileを作成します。

vagrant init bento/centos-6.7
vi Vagrantfile

以下のように編集

# config.vm.box = "bento/centos-6.7" #コメントアウトする

## 以下の設定を追記して hostとwebにIPアドレス割り振る

config.vm.define "host" do |node|  
  node.vm.box = "bento/centos-6.7"
  node.vm.hostname = "host"
  node.vm.network :private_network, ip: "192.168.33.10"
end
config.vm.define "web" do |node|
  node.vm.box = "bento/centos-6.7"
  node.vm.hostname = "web"
  node.vm.network :private_network, ip: "192.168.33.11"
end

vagrantを立ち上げてログインをします

$ vagrant up
$ vagrant status
$ vagrant ssh host

ホストからWEBへssh接続できるようにします。

[vagrant@host ~]$ ssh-keygen -N "" -f ~/.ssh/id_rsa
[vagrant@host ~]$ cat << FIN >> ~/.ssh/config
> Host web
>    HostName 192.168.33.11
> FIN
[vagrant@host ~]$ chmod 600 .ssh/config
[vagrant@host ~]$ ssh-copy-id web
[vagrant@host ~]$ ssh web
vagrant@192.168.33.11's password:
[vagrant@web ~]$ uname -n
web

環境構築をしていきます。 dotinstallのgitからミドル一式をインストールして、 gemでitamaeとserverspecをインストールします。

git clone https://github.com/dotinstallres/centos65.git
$ cd centos65
$ ./run.sh
$ exec $SHELL -l
$ gem list
$ gem install itamae serverspec
$ gem list

作業ディレクトリを任意の場所に作っておきます。 また、Itamaeで使用するファイルも予め作っておきます。

$ mkdir myproject
$ cd myproject/
$ mkdir cookbooks
$ cd cookbooks/
$ touch recipe.rb

serverspec-initでテスト対象への OSや接続方法、ホスト名などを入力します。

$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: web
 + spec/
 + spec/web/
 + spec/web/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

サンプルのテストファイルは削除し、 位置から空のテストファイルを作成していきましょう。

$ cd spec/
$ cd web/
$ rm sample_spec.rb
$ touch httpd_spec.rb

httpd_spec.rbApacheのテストを書きます。

$ vi httpd_spec.rb
require 'spec_helper'
describe package('httpd') do
    it { should be_installed }
end

ここでテストを実行してテストが正常に失敗することを確認します。

$ rake spec

テストが通るように、 recipe.rbApacheインストールの記述をします。

$ vi cookbooks/recipe.rb

package 'httpd'

itamaeを実行してテストが通ることを確認します。

$ itamae ssh -h web cookbooks/recipe.rb -n
$ itamae ssh -h web cookbooks/recipe.rb
$ rake spec

テストの際,Rspecの記法として ResourceとMatcherが出てきました。 これらの種類は公式サイトのRESOUCE TYPEから確認できます。

Resource - 何をテストするのか - httpdのpackage

Matcher - どういう状態 - be_installed

まずはhttpd自動起動していて、80番ポートでLISTENしているかテストを書きます。 be_enable自動起動の設定で、be_runningがスタートしているかです。

require 'spec_helper'
describe service('httpd') do
  it { should be_enabled }
  it { should be_running }
end

describe port(80) do
  it { should be_listening }
end

それではhttpd自動起動させます。 :enable :start自動起動させます。 デフォルトで80番ポートでLISTENします。

package 'httpd'

service 'httpd' do
  action [:enable, :start]
end

rake speckを実行してテストが成功することを確認

ファイルを転送して、中身のテストをしていきます。

index.htmlが存在して、中身がHello Worldという文字列が含まれているか テストを書きます。 オーナとグループがapacheであることも確認します。

describe file('/var/www/html/index.html') do
  it { should be_file }
  it { should be_owned_by 'apache' }
  it { should be_grouped_into 'apache' }
  its(:content) { should match /Hello World/ }
end

filesフォルダを作り、 そこに転送用のindex.htmlを書きます。

$ mkdir cookbooks/files
$ vi cookbooks/files/index.html
<html>
Hello World
</html>

Itamaeではオーナとグループがapacheのindex.html /var/www/html/配下に転送させます。

remote_file '/var/www/html/index.html' do
  owner 'apache'
  group 'apache'
end

rake specを実行し、テストが成功することを確認

パッケージをまとめてインストールさせて、テストを実施する

phpのテストを書いてみます。 新しくテストファイルを書きます。 eachメソッドを使いパッケージをインストールします。 php、php-devel、php-mbstring、php-gdが入っていることを確認します。

require 'spec_helper'

%w(php php-devel php-mbstring php-gd).each do |pkg|
  describe package(pkg) do
    it { should be_installed }
  end
end

Itamaeのレシピには以下を追加してパッケージをインストールさせます。

%w(php php-devel php-mbstring php-gd).each do |pkg|
  package pkg
end

rake speqを実行して、 PHPがインストールしたことを確認します。

PHPがインストールされたことを確認したら、 設定ファイルの中身を確認します。

php_configというリソースを使っていきます。 タイムゾーンAsia/Tokyoであることを確認してみる 設定項目はdata.timezode 値を確認するにはits(:value)を使います。 shouldで文字列が同じでないといけないと書きます。

describe php_config('date.timezone') do
  its(:value) { should eq 'Asia/Tokyo' }
end

Itamaeではレシピに以下を追記 /etc/php.iniaction :editで編集 blockを使い|content|で破壊的メソッドで置換をします。 コメントアウトになっているdate.timezoneを 'date.timezone = Asia/Tokyo'に置換

file '/etc/php.ini' do
  action :edit
  block do |content|
    content.gsub!(';date.timezone =', 'date.timezone = Asia/Tokyo')
  end
end

rake specを実行し、 ファイルが編集されていることを確認します。

次に、ディレクトリが存在するかテストしてみます。

ファイルやディレクトリを確認するテストファイルを作成します。 file()リソースを使い、Matcherでbe_directoryを使えばテストできますが、 commandリソースでコマンドを実行させて確認することもできる 今回ならlsコマンドを実行して、その結果をテストする its(:stdout)で標準出力をテストできます。 今回は空白とディレクトリ名と行末の正規表現でテストします。

require 'spec_helper'

# describe file('/home/vagrant/myapp') do
#   it { should be_directory }
# end

describe command('ls -la /home/vagrant') do
  its(:stdout) { should match /\smyapp$/ }
end

Itamaeのレシピではディレクトリを作成するレシピを追記します

directory '/home/vagrant/myapp'