備忘録 blog

Docker/Machine Learning/Linux

Gentoo Linuxでtestingブランチを使わないことにする

tl;dr

AMD64環境のGentoo LinuxXmonadを入れるときに、一部のdev-haskellパッケージのUSEフラグで~amd64を追加するように指示された。

/etc/package/make.confACCEPT_KEYWORDS="~amd64"と指定してうどんワールド *1 してしまったせいで、次の日から大量の更新パッケージが降ってきてうどんワールドすると大変なことになった。

ACCEPT_KEYWORDSの指定を外すと、今度は大量のダウングレードとそれに伴う依存性破損が生じて大変なことになった。従って、現行のバージョンをできるだけ維持して、今後はstableブランチを使うようにしようとした。

~amd64とは

amd64環境ではstableでないパッケージをインストールする時に用いるフラグ。

これをつけないと、Firefoxは38が最新だし(現行は45)Dockerは1.7が入ってくるし、割と悲惨な目に遭うが、testing環境ではどんどん新しいパッケージが降ってくるのでコンパイルするだけでやたらと時間をとられる。

今回の問題はグローバルに~amd64をつけてしまったことにあり、最新のパッケージを使うぶんには良いのだが、毎日emergeに時間をとられるのが辛い場合は、stableなブランチに戻したくなる。

応急処置

暫定的に、ひたすらmaskをかけて「現行のバージョンより古いバージョンのパッケージをemerge」しないこととした。

<app-i18n/fcitx-anthy-0.2.2
<x11-misc/xfe-1.41
<sys-boot/os-prober-1.71
<media-fonts/jp-ipafonts-003.03
<x11-libs/libdrm-2.4.67
<app-portage/layman-2.3.0-r1
<x11-terms/rxvt-unicode-9.22
<x11-terms/xterm-324

このような形で、現行のバージョン未満の値のパッケージをmaskすれば、それらのパッケージがemergeされることはなくなるので、とりあえず今のバージョンが保たれる。さすがに全てのパッケージに対してこれを記述するのは大変なので、主立ったツールコンパイルに時間のかかるfirefoxghcなどを中心に指定した。

謎のvirtual/パッケージのせいで様々な依存関係が生じる

うどんワールドしたときの依存性をツリーで表示させたときに、例えばvirtual/dev-managersys-fs/static-devを要求しているが、sys-fs/static-devをemergeしようとすると怒られたとする。

このときは、virtual/dev-managerをemergeすると何が起こるか見てみることにする。/usr/portage/virutal/dev-manager/dev-manager-0.ebuildを開く。

# Copyright 1999-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$

EAPI="2"

DESCRIPTION="Virtual for the device filesystem manager"
HOMEPAGE=""
SRC_URI=""

LICENSE=""
SLOT="0"
KEYWORDS="alpha amd64 arm arm64 hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86 ~sparc-fbsd ~x86-fbsd"
IUSE=""

DEPEND=""
RDEPEND="|| (
        virtual/udev
        sys-apps/busybox[mdev]
        sys-fs/devfsd
        sys-fs/static-dev
        sys-freebsd/freebsd-sbin
    )"

ここで大事なのはRDEPENDのところで、||はorのことであるから、列挙されているパッケージのうち、どれか1つをインストールすればよいということである。

Gentooとは選択である。

今回は上から順に、ということでvirtual/udevをemergeしてみたところ、そのパッケージは既に入っているがstableブランチよりバージョンが新しいので、~amd64のフラグが足りないと言われた。それを付けると、今度は依存性解決がうまく行ってemergeできた。

参考ページ

d.hatena.ne.jp

ACCEPT_KEYWORDS - Gentoo Wiki

*1:sudo emerge -uDN @world

Gentoo Linuxをインストールする

パーティション設定

* sda2 128M part /boot ext2
* sda3 論理パーティション
    * sda5 8G swap
    * sda6 96.8G part / btrfs

メモリを2GBしか積んでいないので、コンパイル時にSwap領域を使うかと思って8GBぐらい確保しておいた。 btrfsが死なないことを祈る。

インストールの流れ

  • 予め他のlivecdでgpartedなどを使ってパーティションを切っておく。
  • Livecdで起動し、hddをマウントしてchrootする。(手元にあったArchbootを使ったが問題なし。)
  • sshdを立てて、ssh接続でインストールした。wikiをみながら作業するとよいと思う。
  • locale等の諸設定をして、カーネルの設定をしてカーネルmakeする。何度やってもよい。
  • grubの設定をして(重要)、再起動してhddから起動するようにBIOSを設定する。
  • 起動したらインストール自体は成功。うまくいっていない場合はchrootからやり直し。

はまったこと

  • ネットワークのドライバが入っていないために、有線LANにつながらない。
  • startxすると怒られる。RADEONのドライバを認識してくれない。
  • 何度もライブcdからchrootし直すはめになる。
  • コンパイル中は暇。

対策

カーネルの設定時に忘れずにネットワークカードを選択する。

lspci -k | grep driver

をlivecdでした際の結果と、gentoo内でした結果を比較して、足りないドライバーをカーネルコンパイルオプションで追加する。 カーネルビルトインではなくモジュールで追加するようにすると、動かすものを明示的に指定できるためうまくいくことがある。

d.hatena.ne.jp

RADEON系のグラボを使っている場合、xf86-video-atiを使うことになるが、その際はカーネルでbinファイルを指定しなければならない。

startxしたときのエラーメッセージに、Device(s) detected, but none match those in the config fileとかいう文が入っていたら、ドライバが正しく動いていないことがある。

fglrx - Gentoo Wiki

amdのドライバはプロプライエタリなもの(Fglrx)とオープンソースのものがあるが、上をみるとRADEONの古いバージョンだと新しいxorgを入れられないということがわかったので、オープンソースのドライバを選択することにした。

radeon - Gentoo Wiki

これを読んで、カーネルの設定で正しくradeon/<YOUR-MODEL>.binを指定しなければならない。ドライバが認識されると、コンソールの解像度が適切なものになる(はず)

何度でもchrootし直すはめになる

mkdir /mnt/gentoo 
mount /dev/sda6 /mnt/gentoo
mount /dev/sda2 /mnt/gentoo/boot
mount -t proc proc /mnt/gentoo/proc
mount --rbind /dev /mnt/gentoo/dev
mount --rbind /sys /mnt/gentoo/sys
chroot /mnt/gentoo /bin/bash

source /etc/profile

その都度livecdから入り直し、上のコマンドをコピペして対応した。

うどんワールドする

ソフトウェアの更新をする際に。

# emerge -uDN @world

これで依存関係と新しいUSEフラグを含めて、アプリケーションをすべて更新できる。俗に言う「うどんワールド」である。

参考ページ

http://syusui.tumblr.com/post/17262231474/gentoo-linux-インストール-メモ
syusui.tumblr.com

Gentoo Linuxをインストールする

ハンドブック:AMD64 - Gentoo Wiki

ビギナーズガイド - ArchWiki

なんだかんだarchwikiは頼りになる。

Dockerでサーバーを立てる(2)

それでは実際に、Docker-composeでRailsアプリを立てるところまでやってみたいと思う。

構成

今回のdocker-compose構成は以下である。

supervisordで1つのコンテナに全て立てるのはやはりdockerの原則に反していると思うので、原則として1プロセス1コンテナとした。 まずデータコンテナを置いて、そこにlogを取っておいて各コンテナをマウントするのはデータ永続化の手法としてしばしば用いられるらしい。 mysqldを立てていないのはsqlite3を使っているためである。sqlite3のファイル自体はデータコンテナに格納できていると思われる(?) やはりsqlは別コンテナに立てるべきだと思うので今後、改善したい。

unicornではなくpumaを使う

blog.willnet.in

リバプロとしてnginxを立てる以上、スロークライアントとの通信はリバプロが引き受けるためにunicornの弱点であるプロセスの専有は防げるらしいので、pumaをアプリケーションサーバーとする必要はないらしい。だが敢えてnginx+pumaで立ててみる。

yuuki.hatenablog.com

ちなみにこのエントリが、アプリケーションサーバーの機構についてまとめてあり参考になる。Unicornはマルチプロセス(prefork)型だが、Pumaはマルチスレッド型(スレッドプール)とマルチプロセス型のhybridだそうだ。但し、Rubinius or JRubyを使うことが推奨されている。今回はMRI上で動くアプリだが、TwitterAPIを叩くようなBlocking I/Oを呼ぶようなことはないので使っても大丈夫だろうという判断である。

実際のソケットの設定方法などはunicornを使ったパターンが多く、情報が少なく苦労したがこのあたりを調べた。

www.digitalocean.com

arakaji.hatenablog.com

Arch linuxによるnginxサーバーを立てる時の注意点

既存のnginxイメージを使ってもよいのだが、せめてこれぐらいはArch linuxで立てたいと思ってDockerfileを記述した。

RUN \
        pacman -Syu && \
        pacman -S nginx --noconfirm

ADD ./nginx.conf /etc/nginx/nginx.conf

VOLUME ["/etc/nginx/sites-enabled", "/etc/nginx/certs", "/etc/nginx/conf.d", "/var/log/nginx", "/var/www/html"]

WORKDIR /etc/nginx

EXPOSE 80

ENTRYPOINT /usr/sbin/nginx -g 'daemon off;'

この中で、

ADD ./nginx.conf /etc/nginx/nginx.conf

の部分が厄介で、Arch linuxのnginxでは/etc/nginx/nginx.confinclude /etc/nginx/conf.d/*.conf;という記述がないので、そのままでは/etc/nginx/conf.d/nginx.confなどに設定ファイルを記述したところで自動で読み込んではくれない。そこで、もとの/etc/nginx/nginx.confを手元のファイルで上書きする必要があるのだ。

結論:自分でDockerfileをフルスクラッチで書こうとするより、先駆者のものを参考にするのがよい。

github.com

実行

Dockerfile, docker-compose.ymlがアプリのルートディレクトリに置いてあって、Arch linuxによるnginxサーバーのイメージは予めbuildしてあることとする。

$ docker-compose up -d

これで、コンテナをcreateして実行してくれる。Dockeruiによる管理をしていれば、webui上で正常に動作しているかどうかを確認できる。

その後、dbのmigrationは実際に内部に入って行う必要がある。

$ docker exec -it app bash
# bundle exec rake db:migrate
$ docker-compose down

これでコンテナを消せるので、失敗したものはどんどん消していくとよいと思う。

参考ページ

qiita.com

qiita.com

cross-originでiframeが埋め込めないとき

What I want to do

あるサービスをwww上で展開しているのだが、元のドメインのURLをそのまま晒すのがちょっと嫌なのでそれを隠すためにurl-forwardingをしたい。

tkドメインを試す

www.dot.tk

*.tkとかいうドメインはタダで手に入る。(但し有効期限がある) 登録画面は割と癖のある挙動。登録から割り当てまで時間がかかるので注意。メアドを登録せずに匿名でも登録できるのがウリか。信頼性は低そう。 無料版の範囲だとhttps対応していない。

これでURL-forwardingを試してみたが、やっていることはforward先のurlをiframeで埋め込むことだった。 但し、あるサービスはX-Frame-Options:SAMEORIGINであったので、このままではcross-originエラーが出て何も表示されない。

URL-forwardingといっても結局はforward先のurlをiframeで埋め込むのであれば、わざわざ独自ドメインをとらなくてもどこか信頼性の高そうなドメインホスティングすればよいということになった。

動的にiframeを埋め込むようにする

人力検索はてなでも質問がされていた。

q.hatena.ne.jp

が、Stackoveflowが解決策としては一番わかり易かった。

stackoverflow.com

cross-originを外すために、以下のサイトを媒介する。

An open source alternative to AnyOrigin

このサイトにパラメータとしてurlを渡すと、html部分を含んだ結果をJSON形式で取得できるので、コールバックでcontentsだけを取得して、divに注入するようにすればよい。ただしリンクは元のドメインのURLのままなので、このページからのリンクもそれぞれ静的なページで用意して、……としない限りはリンク先がおかしいぞ、となってばれてしまう。(もちろんソースコードを見れば一発だけれど) さすがに全ページを静的なページでプロキシするのは現実的でないので、トップページだけiframeの埋め込みでごまかすことで妥協しようと思う。

Dockerでサーバーを立てる(1)

はじめに

様々なところで語られていますが、Dockerはコンテナ型のサーバー仮想化のエコシステムとして知られている。ここでは、DockerでRailsアプリを立てることを目標とする。

大まかな概略をつかむには以下のページが参考になった。

Dockerについての調査まとめ、基本機能+主な特徴+Vagrantの連携 あたり · GitHub

細かなtips

具体的な構成は次に譲るとして、ここでは細かいtipsや周辺技術を書き留めておくこととしたい。

モニタリング

モニタリングツールは群雄割拠していてどれを選ぶかは難しいが、ここでは

github.com

これを利用して、コンピュータ上でどのコンテナが立っているかをモニタリングする。

docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui

一度立てた後は

docker start dockerui

で起動可能。

コンテナ内に入る

qiita.com

docker exec -it nginx bash

supervisordを使う

本来のdocerは1コンテナ1プロセスが基本ですが、複数プロセスを立ち上げたいことがあるかもしれません。 そのときにこれを使います。

qiita.com

実際にそれを使ってRoR4のコンテナを立ち上げた例がこちら。

qiita.com

データボリュームについて

しばしばbusyboxなどでデータボリュームコンテナを立てて、その上にマウントした設定ファイルやログ用のフォルダをsqlやnginxなどのコンテナで共有するパターンがあります。こうすることで、ログ等を永続化できます。

そのとき、dockerコマンドのオプションをどう設定するかのtipsが、以下にまとまっています。

qiita.com

sshdを立てる

dockerでsshdを立てることはどうなのよ、という意見もありますが、立てている例があります。

qiita.com

docs.docker.com

gentoo/stage3-amd64でemergeする

docker run -v /home/#{name}/gentoo:/usr/portage/ -it gentoo/stage3-amd64 bash

みたいにすれば、emergeコマンドによるrsyncを永続化できます。これでDocker上で手軽にGentoo LinuxPortageシステムを体験することができます。

DockerでMattermostサーバーを立てる

自前のサーバーでSlack, Hipchat系のチャットサービスを使いたいと思った時に。Golang製。

Mattermost

立ててみる

Local Machine Setup and Upgrade — Mattermost 2.0 documentation

docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform

http://localhost:8065/にアクセス。それ以後は一回立てた後はこれでOK。

docker start mattermost-dev

他のチャットサービスとの比較

slack

slack.com

みんな大好きSlack。日本のスタートアップ企業でも圧倒的シェアを誇っています*1

Slackではできるけど現段階のバージョンでのMattermostでできないこと。

  • slackbotみたいな存在がない。
  • snippet, postを貼れない。
  • htmlを入力してもogが表示されない。
  • スラッシュコマンドが乏しい。
  • Androidネイティブアプリがまだ正式リリースされていない。

Rocket.Chat

デモは以下。

Rocket.Chat

こちらはjsフレームワークのMeteor製。Meteorには割と注目しているのだが、若干洗練されていないデザインであるという印象。

トラブルシューティング

このサーバーで実際に複数人が接続して試してみたときの備忘録です。

  • 他のユーザーが登録できない!
    • 最初のユーザーはteamを作るときに登録できますが、そうでないユーザーはチームの画面に行ってもsign upできずに何もできません。実は招待urlから入るしかないので、最初のユーザーはメニューからInviteを選択しましょう。
  • 最新版にアップデートしたい!
    • 公式ドキュメントによれば、これはPreview版にすぎないため一度dockerイメージを削除してからpullするしかないようです。もちろんデータは全部消えてしまいます。
  • 日本語のUIがない
    • Slackも同じなのでがんばりましょう。
  • Admin Consoleでユーザーの権限を誤ってSystem_adminから格下げしてしまった!
    • これを一度してしまうと、System_adminでないとユーザー権限を変更できないので、二度とAdmin Consoleにログインできなくなり、設定を戻せなくなって詰みます。

しかも、docker内にはplatformというバイナリがあって、それを用いて本来はユーザーの権限が変更できそうなのですが、docker execで中に入ってバイナリを実行しようとしても動かなくなります。というわけで、一度dockerイメージを削除して立ち上げ直しました。

インターンシップを終えて

IT系のインターンシップに1週間ほど行っていたので、感想を簡単に記す。

概観

求められる水準は高く、仕事中は常に頭脳をフル回転させていなければならなかった。そのためインターンシップの期間中は全くといっていいほど、それ以外のことに手がつけられなかった。寝まくったがいくら寝ても体力が回復せず、自らのバイタリティーの乏しさを感じた。

学んだこと

  • Macの使い方
  • gitの活用。プルリクとイシューの立て方
  • GitHub Frow による開発
  • Ruby on Railsの開発、きれいなコードの書き方
  • Atomの使い方、Slackを使ってみた
  • Rspecの書き方(web mocksによるスタブの活用)
  • フィーチャーテストの書き方
  • Hamlの書き方
  • Coffee scriptの解読
  • APIの叩き方。アクセストークンの設定方法
  • Active recordのクエリの書き方
  • 大規模サービスを影で支える人たちの存在

更に学んでいること

今後の展望

Devise, Rake, Redisへの理解を深める必要がある。 特にRakeコマンドは、単なるおまじないとしての取り扱いが大きかったように感じるので、使いこなせるようになりたい。 Coffee scriptぐらいは書けるようになりたい。 Atomは大きいデータを扱い出すと途端に重くなり、ハングアップすることがあるので、やはりvimに戻ろうかと思う。

インターン中に読んだ本

Unix哲学に触れた。1つのプロセスが1つだけのことをするのが良いというのはDockerの思想ともつながっており、とても興味深かった。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

Python2を高速化するtips(続)

sharply.hatenablog.com

これの続き。

単純に高速なアルゴリズムを使う

例えば"ham"という文字列がある配列に含まれるか?という問いに対して、

some_list = ["spam", "ham", "eggs"]
if "ham" in some_list

というリスト検索ではO(log(n))ですが、RubyにおけるHashと同様のものがPythonでも辞書(Dictionaly)として提供されており、

some_dict = {"spam":1, "ham":1, "eggs":1}
if "apple" in some_dict

とすればO(1)になります。もちろんこれが使えるのには条件がありますが、これで数秒程度早くなりました。

並列計算する

python律速となっているのはだいたいfor文であり、このfor文の実行回数をいかに減らせるか、ということがポイントとなります。 並列計算には、スレッド並列化(メモリを共有する)とプロセス並列化(メモリを共有しない)の2種類があります。しかしCPythonの実装ではGIL(Global Interpreter Lock)というものがあるので、プロセス並列化ぐらいしかできません。

GILとは、並列実行したときに安全でないコード(例えばグローバル変数を書き換えるような、スレッドセーフでないコード:しばしばCのモジュールに存在する)を実行しないようにするために、そのコードを他のスレッドで呼ばれないようにする排他ロックのことで、pythonrubyといったインタプリタ言語では一般に1つのプロセスに対して1つのGILが存在する。

GILがない実装としてJPython(JAVAによる実装)やIronPython、PypyのSTM版があげられますが、ここでは措きます。

CPythonでプロセス並列化によって高速化するには、for文の区間を分割して部分和を並列で計算し、あとでその総和をとるという手段が用いられます。しかしCのopenMPのように動的にfor文の処理をスレッドに割り当てるといったことは標準では用意されていないので、自分で実装するなりする必要があります。

qiita.com

Boost.pythonを使う

結局pythonで計算させるには限界があるので、C++で書いたコードをコンパイルしたものを共有ライブラリとしてpython側から呼び出す実装にしてみましょう。

#define BOOST_PYTHON_STATIC_LIB
#include <boost/python.hpp>

static void image_putpixel(image &i, const boost::python::tuple &x, const boost::python::tuple &y){
  double point[2] = {boost::python::extract<double>(x[0]), boost::python::extract<double>(x[1])};
  uchar color[3] = {boost::python::extract<uchar>(y[0]), boost::python::extract<uchar>(y[1]),boost::python::extract<uchar>(y[2])};
  i.putpixel(point, color);
}

BOOST_PYTHON_MODULE(brush)
{
    using namespace boost::python;

    class_<image>("image")
        .def("show", &image::show)
        .def("brush", &image_brush)
        .def("putpixel", &image_putpixel)
        .def("load", &image::load)
        .def("get", &image::get);
}

上記でわかるように、C++のクラスをpythonのクラスにみなせたり、pythonのtupleを引数としてとるような関数を定義することができます。

コンパイルオプションは例えばUnix環境ではこんな感じ。

g++ -std=c++11 -O2 -I`python -c 'from distutils.sysconfig import *; print get_python_inc()'` -DPIC -fPIC -shared -o brush.so src/brush.cpp -lboost_python

-Iにインクルードパスを指定したいのですが、そのパスが異なるコンピュータ上でも共通に実行できるように、``でpythonを実行するコードを囲うことでそれをシェル上で評価した結果が返ってきます。

python側からは、soファイルを同じディレクトリに置いてファイル名でimportすればOKです。さらに、openMPで実装したC++のコードをpython側から呼び出すこともでき、さらに速度が上がる可能性もあります。

光学設計者の学習メモ: Boost.pythonでOpenMP

APIサーバーが必要となった時のために

自前で実装する

サーバー側にけっこう大きなロジックが必要となる場合。この場合は、単なるRESTなリソースを提供するだけでなく、返り値のロジックが必要となるので、結局自分で全部書いたほうがラクになるということになる。HerokuやOpenShiftといったPaaSに立てることとなる。

Ruby on Railsの場合

qiita.com

Grapeというのがやりやすいらしい。

github.com

Pusherを使う

APIにしてスマホのネイティブアプリを作るのならばともかく、ブラウザでアクセスしてもよいということにして、かつWebSocket的なことがやりたいのであれば、Rails5を待つかコレを使う。

Pusher | Leader In Realtime Technologies

例えば、クライアントはpythonでも書ける。

github.com

Golangの場合

作ろうとするAPIがRESTfulなものでなくてよいなら、むしろこの程度シンプルなもののほうが作りやすいかもしれない。こっちのほうが当然ながら早い。

aial.shiroyagi.co.jp

qiita.com

BaaSサーバーの候補

サーバーは単にデータストア程度でよいとするならば、BaaSサーバーを使うという手がある。2015/10現在で試用が期限なし無料のサービスを列挙してみた。

国産

無料だとpush数が少ないのが劣る点。 www.appiaries.com

海外産

  • Parse

実質最大手。 www.parse.com

  • Quickbox

quickblox.com

変わり種

  • Mongolab

mongodbがクラウドで扱えるらしい。

mongolab.com

参考

iphone-dev.g.hatena.ne.jp

qiita.com