HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

windows(cygwin)でansibleその3

とりあえずparamikoで動くようになったものの、.ssh/configが使えないという致命的な欠点に気付いて、再トライしてみました。

といっても、ぐぐれどもぐぐれども前々回みつけたこのblogと同じく、"-o ContorolMaster=no"を指定すればOKって記事しかひっかかりません。
blog.simonmetzger.de


駄目もとでもう一回やってみようかと思って、ansible.cfgに

[ssh_connection]
ssh_args = -o ControlMaster=no

を書くと・・・あっさり成功しました。

しかも、何回か間隔を空けて再実行してみましたが100%成功してます。



改めて前々回の自分の記事を見直すと

しかし、ansible.cfgに書いたのでは-vvvvオプションを付けると、あいかわらずControlMaster=autoになっていたので、コマンドライン引数を追加して

> ansible hoge.huga.com -m ping -i hosts --ssh-extra-args="-o ControlMaster=no"

にしてみました。

とあります。しかし、これが何かの間違いだったようで*1今回は-vvvvオプションを付けたら、ControlMaster=noが設定されていました。
おそらく、ansible.cfgに書くとデフォルトで付けるオプションを変更するけど、--ssh-extra-argsだと追加で渡すという挙動なんじゃないかと思います。*2

というわけで、前回作ったparamikoの設定は破棄してOpenSSHとともに生きていきます。

*1:ansible.cfgのスペルを間違えてたとか、そんなレベルの単純ミスな気がする・・・

*2:名前的にもssh_argsとssh-extra-argsだから、この挙動の方が正しそう

windows(cygwin)でansibleその2

ssh接続に成功したりしなかったりする問題は結局解決しないままなんですが、

OpenSSHがだめならparamikoを使えば良いじゃない

ということで、paramikoで試してみました。


ansible hoge.huga.com -m ping -i hosts -c paramiko -k
SSH password:  <- 秘密鍵のパスフレーズを入力
hoge.huga.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}


こっちはあっさり成功!
何回か試しましたが、100%つながります。

ただし、ここの記事によると、paramikoを使うとOpenSSHより遅いらしいので、ansible.cfgに

RHEL6系でansibleを使うならrecord_host_keysをFalseにすると速くなる - still deeper

record_host_keys=False

を指定しておきます。あと-c paramikoとかも毎回指定するのも面倒なので、まとめてansible.cfgには次のように指定しておきます。

[defaults]
ask_pass=True
transport=paramiko
[paramiko]
record_host_keys=False

これでもっかいpingを打ってみると

ansible hoge.huga.com -m ping -i hosts -c paramiko -k
SSH password:  <- 秘密鍵のパスフレーズを入力
hoge.huga.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

うむ。問題無し
パフォーマンスを考えると、そのうちparamikoからOpenSSHに乗り換えたくなるかもしれませんが、そんな大規模ノードの管理に使うつもりは無いので当面はこれでやってみます。

windows(cygwin)でansible

ずいぶん前に、windows nativeなpythonに無理矢理ansibleをインストールしてみたら、pwdだのfcntlだのといったUnix固有の標準ライブラリに依存しまくってて、まったく歯が立たなかったので放置してたんですが、最近babunというcygwinの派生版を使い始めたのでこっちで試してみることにしました。

pipのインストール

現時点のbabunのpythonは2.7.10なので、pipのwebページによるとインストール済のはずですが、

pip is already installed if you're using Python 2 >=2.7.9 or Python 3 >=3.4 downloaded from

https://pip.pypa.io/en/stable/installing/

どうやらインストールされてないようでwhich pipするとwindows nativeのpython用のpipが返されます。

> which pip
/cygdrive/c/ProgramData/chocolatey/bin/pip


公式ページの英語版ドキュメントによると

The ensurepip package provides support for bootstrapping the pip installer into an existing Python installation or virtual environment. This bootstrapping approach reflects the fact that pip is an independent project with its own release cycle, and the latest available stable version is bundled with maintenance and feature releases of the CPython reference interpreter.

In most cases, end users of Python shouldn’t need to invoke this module directly (as pip should be bootstrapped by default), but it may be needed if installing pip was skipped when installing Python (or when creating a virtual environment) or after explicitly uninstalling pip.

https://docs.python.org/2.7/library/ensurepip.html

とのことなので、ensurepipを使ってインストールしましょう。

> which python
/usr/bin/python
> python -m ensurepip
Ignoring indexes: https://pypi.python.org/simple
Collecting setuptools
Collecting pip
Installing collected packages: setuptools, pip
Successfully installed pip-6.1.1 setuptools-15.2
> which pip
/usr/bin/pip
> pip install -U pip
You are using pip version 6.1.1, however version 8.1.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting pip
  Downloading pip-8.1.0-py2.py3-none-any.whl (1.2MB)
    100% |████████████████████████████████| 1.2MB 327kB/s
Installing collected packages: pip
  Found existing installation: pip 6.1.1
    Uninstalling pip-6.1.1:
      Successfully uninstalled pip-6.1.1
Successfully installed pip-8.1.0

virtualenvのインストール

以前書いたwindows版の記事を参考にpipでインストールします。
hpcmemo.hatenablog.com

> pip install virtualenv
Collecting virtualenv
  Downloading virtualenv-15.0.0-py2.py3-none-any.whl (1.8MB)
    100% |████████████████████████████████| 1.8MB 231kB/s
Installing collected packages: virtualenv
Successfully installed virtualenv-15.0.0

ansibleのインストール

virtualenvで環境を作って、ansibleをインストールします。

> virtualenv ansible
New python executable in /cygdrive/c/Users/n_so5/OneDrive/Python/ansible/bin/python2.7
Also creating executable in /cygdrive/c/Users/n_so5/OneDrive/Python/ansible/bin/python
Installing setuptools, pip, wheel...done.
> cd ansible
> . bin/activate
(ansible)>  pip install ansible
Collecting ansible
  Using cached ansible-2.0.1.0.tar.gz
Collecting paramiko (from ansible)
  Downloading paramiko-1.16.0-py2.py3-none-any.whl (169kB)
    100% |████████████████████████████████| 174kB 706kB/s
Collecting jinja2 (from ansible)
  Downloading Jinja2-2.8-py2.py3-none-any.whl (263kB)
    100% |████████████████████████████████| 266kB 1.1MB/s
Collecting PyYAML (from ansible)
  Downloading PyYAML-3.11.tar.gz (248kB)
    100% |████████████████████████████████| 256kB 758kB/s
Requirement already satisfied (use --upgrade to upgrade): setuptools in /usr/lib/python2.7/site-packages (from ansible)
Collecting pycrypto>=2.6 (from ansible)
  Downloading pycrypto-2.6.1.tar.gz (446kB)
    100% |████████████████████████████████| 450kB 975kB/s
Collecting ecdsa>=0.11 (from paramiko->ansible)
  Downloading ecdsa-0.13-py2.py3-none-any.whl (86kB)
    100% |████████████████████████████████| 92kB 2.3MB/s
Collecting MarkupSafe (from jinja2->ansible)
  Downloading MarkupSafe-0.23.tar.gz
Installing collected packages: ecdsa, pycrypto, paramiko, MarkupSafe, jinja2, PyYAML, ansible
  Running setup.py install for pycrypto ... done
  Running setup.py install for MarkupSafe ... done
  Running setup.py install for PyYAML ... done
  Running setup.py install for ansible ... done
Successfully installed MarkupSafe-0.23 PyYAML-3.11 ansible-2.0.1.0 ecdsa-0.13 jinja2-2.8 paramiko-1.16.0 pycrypto-2.6.1

動作確認

とりあえずテストとしてpingでも打ってみます。

> echo hoge.huga.com > hosts
> ansible hoge.huga.com -m ping -i hosts
0 [main] python2.7 6852 child_info_fork::abort: address space needed by '_speedups.dll' (0x460000) is already occupied
ERROR! Unexpected Exception: 'NoneType' object has no attribute 'terminate'
to see the full traceback, use -vvv

_sppedups.dllってなんじゃと思ってfindしてみると

> find ./ -name _speedups.dll
./lib/python2.7/site-packages/markupsafe/_speedups.dll

さっきansibleを入れた時に一緒にインストールされたMarkupSafeに含まれるライブラリのようです。

おそらく、こちらの記事と同じ問題だろうと推測して、同じことをやってみます。
d.hatena.ne.jp

> find `pwd` -name '*.dll' -o -name '*.so' >~/rebase_list
一旦babunのターミナルを終了して、%USERPROFILE%\.babun\cygwin\bin\ash.exe を実行
$cd $HOME
$/bin/rebaseall -T rebase_list -v
(中略)
/usr/bin/cygattr-1.dll: new base = 6fe10000, new size = 10000
/usr/bin/cygatomic-1.dll: new base = 6fe20000, new size = 20000
/usr/bin/cygaspell-15.dll: new base = 6fe40000, new size = b0000
/usr/bin/cygarchive-13.dll: new base = 6fef0000, new size = b0000
/usr/bin/cygaprutil-1-0.dll: new base = 6ffa0000, new size = 30000
/usr/bin/cygapr-1-0.dll: new base = 6ffd0000, new size = 30000

再度babunを起動して、

> ansible hoge.huga.com -m ping -i hosts
hoge.huga.com | UNREACHABLE! => {
    "changed": false,
    "msg": "SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue",
    "unreachable": true
}

forkの問題は解消されたっぽいけどやっぱり接続できません。
とりあえず、-vvvvを付けて実行してみると、ログの最後の方で"Broken Pipe"というのが表示されていたのでぐぐってみたところ、こんな記事がひっかかりました。
blog.simonmetzger.de
しかし、ansible.cfgに書いたのでは-vvvvオプションを付けると、あいかわらずControlMaster=autoになっていたので、コマンドライン引数を追加して

> ansible hoge.huga.com -m ping -i hosts --ssh-extra-args="-o ControlMaster=no"

にしてみました。

それでもUNREACHABLEは変わらないので、さらにしばらくぐぐり続けたところ、こんなページがみつかりました。
serverfault.com
.sshの下のファイルのowner/groupを見たらgroupが"None"になっていたので、試しに

> chown ユーザ名:Users ~/.ssh/*

をしてみたところ、ようやく接続できました。

> ansible hoge.huga.com -m ping -i hosts --ssh-extra-args="-o ControlMaster=no"
hoge.huga.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

何回か再実行すると、時々FAILになったりするので、ちょっと実用にはほど遠いんですがとりあえずこの辺で一旦終了しときます。

これまでのまとめ

babun環境からansibleを使う時は

  • ansibleをインストールしたら、dllファイルのリストを作ってrebaseall
  • .ssh/ 以下のファイルのowner/groupを正しく設定する
  • --ssh-extra-args="-o ControlMaster=no"をつける

それでも、なんか失敗する時あり・・・orz

Intel版python

先日、VTuneのドキュメントを漁っていたら面白そうなものを見つけました。

Python* Distribution | Intel® Developer Zone

ドキュメントを見たところ、MKLとnumpy/scipyを同梱したpythonのようです。*1

ダウンロードするためには、Technical Previewへ参加する必要がありますが、これに参加するとついでにVTuneの最新版のTechnical Previewにも参加したことになるようです。

Python* Profiling | Intel® Developer Zone

これ、python専用の機能限定版じゃなくて、フル機能版みたいなので*2色々と楽しめそうです。


とりあえずpythonを入れるマシンを用意せんといかんなぁ

*1:enthoughtの有料版と一緒じゃねーか?

*2:会社のPCにこっそり入れようとしたら、"Newer version is already installed"とか言われたので・・・

git-hookを使ってコミット時にテスト

就職して以来CVS->subversion->gitと、かれこれ10年近くVCSを使ってきましたが、今だにcommitを忘れるという初歩的な問題が解決できません・・・

  1. ソース作成/修正
  2. ビルド
  3. テスト
  4. コミット

という順番で作業してると、ビルド&テストしてる間に時間が空いてしまって作業が止まるのが原因だろうと仮定してgit-hookを使ったポカヨケを作り込んでみました。

サンプルのプロジェクトとして、懐しのはろーわーるどをCで書いてみました。

#include <stdio.h>
int main(int argc, char *argv[])
{
  printf("hello world!\n");
  return 0;
}

ビルド用のmakefileはこんな感じ

LM=hello
LINKER=$(CC)
LDFLAGS=$(CFLAGS)

SRCS_C=hello.c
OBJS_C=$(SRCS_C:%.c=%.o)

OBJS=$(OBJS_C)

all:$(LM)

$(LM):$(OBJS)
        $(LINKER) $(LDFLAGS) $(OBJS) -o $(LM)


.PHONY: clean

clean:
        -rm -rf *.o $(LM)

そして、テストスクリプトとして次のようなpythonスクリプトをtest.pyとして置いておきます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import subprocess
import sys

if __name__ == "__main__":
    output=subprocess.check_output("./hello")
    if output == "Hello world!\n":
        sys.exit(0)
    else:
        print("FAILED!!")
        print output
        sys.exit(1)


ここまで準備できたら、次はhookを用意します。

".git/hooks/commit-msg"という名前で次のようなシェルスクリプトを作ります。

#!/bin/sh

#コミットメッセージが空でなければ
#ビルド&テストを実施
grep -v -e'^#' -e'^$' $1 >/dev/null
if [ $? -eq 0 ];then
  make && python test.py
  exit $?
fi


これでgit commitしてメッセージを書き終えると自動的にmakeとtest.pyが実行されます。
今の状態だと、hello は"hello world!"と出力するのに、テストでは"Hello world!"と出力することを期待されているのでテストに失敗し、コミットできないはずです。
ではさっそく試してみましょう。

> git add hello.c Makefile test.py
>  git commit -m'Initial commit'
cc    -c -o hello.o hello.c
cc  hello.o -o hello
FAILED!!
hello world!

>git log
fatal: bad default revision 'HEAD'

たしかに、コミットされてません!
hello.cを修正して、正しく"Hello world!"と出力するように変えると

 git commit -m'Initial commit'
cc    -c -o hello.o hello.c
cc  hello.o -o hello
[master (root-commit) c524d0b] Initial commit
 3 files changed, 42 insertions(+)
 create mode 100644 Makefile
 create mode 100644 hello.c
 create mode 100755 test.py

>git log
commit c524d0bd9c1ed94fa20b9eab29a222f6312237d3
Author: n_so5 <n_so5@localhost>
Date:   Fri Jan 29 14:10:47 2016 +0900

    Initial commit

無事にコミットされました!


あとは、必ずテストを書くという強い心があれば大丈夫・・・orz

cmderでIntel CompilerとVSを使う設定

数値計算というと、大学とかに置いてあるスパコンにログインして、Linuxコマンドラインで作業してるイメージが強いかもしれませんが、時々windows上でVisualStudioとIntelコンパイラで開発してますっていう人にも出会います。*1

となると、受託開発ではやっぱりwindowsでやってねというお話をいただくこともあるんですが、いかんせんVSの起動が遅すぎるので
viでコードを書いてコマンドラインからビルドするといういつものスタイルで作業したくなるわけです。
しかし、インテルコンパイラのインストール時にもろもろ設定してくれたコマンドプロンプトは、所詮windowsのデフォルトのコマンドプロンプト上で環境変数などを設定しただけのものなので、使い難いったらありゃしない。


前置きが長くなりましたが、cmderの設定をいじって、インテルコンパイラを使える状態のcmd.exeが起動するようにしようというのが今回の目的です。
ちなみに環境はIntel Parallel Studio 2016+Visual Studio 2015+windows7な環境ですので、インストール先などは適宜読み替えてください。


まずは既存の設定の確認から

スタートメニューから「すべてのプログラム」->「Intel Parallel Studio XE 2016」->「Compiler and Performance Libraries」-> 「Command Prompt with Intel Compiler 16.0」とたどって「Intel 64 Visual Studio 2015 environment」をクリックします。


f:id:n_so5:20151202104215p:plain


プロパティ画面が開いたらリンク先欄にある文字列をコピーしておいて、cmderを起動します。
cmderのsettings画面で、「Startup」-> 「Tasks」とたどって「+」ボタンを押し右下の欄にペーストします。
後は上の方にある欄に適当に名前を付ければ完了です。


f:id:n_so5:20151202111235p:plain


cmder上で[+]ボタンの横にある下向き三角をクリックすると、メニューがずらっと出てくるので
さきほど作ったtaskを選ぶと、こんな感じでiclにパスが通った状態でコマンドプロンプトが起動します。


f:id:n_so5:20151202111651p:plain


あとは、vi+ctagsがあれば快適なコーディングができます。
ま、自由にいじっていいならcmakeでビルドするようにして、バッチファイルでも置いとくんですが・・・

*1:以前の同僚はVSしか使ったことが無いとか言ってて入社してきた時に凄いカルチャーショックを受けました

pandas+matplotlibで3D棒グラフを作る

たとえば、こんな感じの表があったとするじゃないですか。

うし ぶた とり
肉屋 580 280 300
スーパー 420 260 320

これを、エクセルに入力して、挿入->3D-縦棒と選ぶと

f:id:n_so5:20151008183105p:plain

こんな感じのかっこいい(?)グラフが作れるわけですが、これをpandas+matplotlibでやってみようというお話です。

結論から先に書くと

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.font_manager import FontProperties

df=pd.DataFrame({u"うし":[580, 420],
                 u"とり":[280, 260],
                 u"ぶた":[300, 320]},
               index=(u"肉屋", u"スーパー"))

ax=plt.figure().add_subplot(111, projection='3d')
fp=FontProperties(fname=r'C:\WINDOWS\Fonts\msgothic.ttc', size=10)

xpos,ypos=np.meshgrid(np.arange(len(df.index)), np.arange(len(df.columns)))
xpos=xpos.flatten()
ypos=ypos.flatten()
zpos=np.zeros(len(xpos))
dx=np.full(len(xpos), 0.05)
dy=np.full(len(ypos), 0.1)
dz=df.as_matrix().flatten()
ax.bar3d(xpos, ypos, zpos, dx, dy, dz)

ax.set_xlabel(u"お店", fontproperties=fp)
ax.set_ylabel(u"種類", fontproperties=fp)
ax.set_zlabel(u"金額", fontproperties=fp)
plt.xticks(np.arange(len(df.index)),   df.index, fontproperties=fp)
plt.yticks(np.arange(len(df.columns)), df.columns, fontproperties=fp)

plt.axis([-0.5, len(df.index)-0.5, -0.5, len(df.columns)-0.5])

plt.savefig("hoge.png")

みたいなコードになります。


公式ページのexampleを見ると、
mplot3d example code: bars3d_demo.py — Matplotlib 1.4.3 documentation
ってのがありますが、こっちは普通の棒グラフを奥行方向に重ねて並べるもののようで、今回作ろうと思っていたのとはちょっと違います。

一見関係無さそうな名前ですが、
mplot3d example code: hist3d_demo.py — Matplotlib 1.4.3 documentation
のところで使っている、bar3d()が今回のグラフに必要な関数でした。
ところがこの関数、APIドキュメントがこんな調子でまったく意味不明です。

bar3d(x, y, z, dx, dy, dz, color=u'b', zsort=u'average', *args, **kwargs)

Generate a 3D bar, or multiple bars.

When generating multiple bars, x, y, z have to be arrays. dx, dy, dz can be arrays or scalars.

color can be:

A single color value, to color all bars the same color.
An array of colors of length N bars, to color each bar independently.
An array of colors of length 6, to color the faces of the bars similarly.
An array of colors of length 6 * N bars, to color each face independently.

When coloring the faces of the boxes specifically, this is the order of the coloring:

-Z (bottom of box)
+Z (top of box)
-Y
+Y
-X
+X

Keyword arguments are passed onto Poly3DCollection()

http://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d

x, y, z have to be arraysとか言われても、何の値入れりゃえんじゃーと悩んでましたが、どうやら

  • 各配列は全て同じ長さの1次元配列にする
  • i番目の棒の左下隅の頂点座標をx, y, zのi番目の要素で指定
  • i番目の棒の各軸方向の幅をdx, dy, dzのi番目の要素で指定

ということのようです。

ちなみに、この例ではx、yともに項目名なのであまり違和感は無いかもしれませんが、元々私がやってた時は2次元でパラメータを振った時の実験結果をプロットしようとしていたので、x, y, zに値を入れてからdx, dy, dzって何を入れるんだろうかと、かなりドツボにはまってました。*1

あとのところは、コードを見れば分かると思いますので、解説は省きますが

set_?label
ラベルの設定
{x,y}ticks
x/y座標の値の指定
axis
座標軸の長さ指定
savefig
グラフを画像ファイルとして保存

です。


matplotlibの全般的な話は
matplotlib入門 - りんごがでている
に助けられました。ここ読まなかったら未だドキュメントの海で溺れてたと思いますorz

あと、フォント関連の設定については
Python - matplotlibで日本語 - Qiita
を参考にさせていただきました。



そういや、pandasろくに使って無いな・・・

*1:素直にwire frameでやってればそんなにはまらなかった気がしないでもない