11.27.2014

Scala: error: forward reference extends over definition of value

Scala: Stream の再帰的な定義でエラー

 

自分自身を参照するような Stream を定義したら、エラーが出た。

 

エラーが発生する例

scala> def f(x: Int) = {
     |   val xs: Stream[Int] = x #:: xs.map(_ + 1)
     |   xs.tail
     | }
:17: error: forward reference extends over definition of value xs
         val xs: Stream[Int] = x #:: xs.map(_ + 1)
                                     ^

 

対応方法

lazy val にすれば OK。

scala> def f(x: Int) = {
     |   lazy val xs: Stream[Int] = x #:: xs.map(_ + 1)
     |   xs.tail
     | }
f: (x: Int)scala.collection.immutable.Stream[Int]

scala> f(3).take(10).toList
res8: List[Int] = List(4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

 

 

References

11.25.2014

AdTech x Scala x Performance Tuning

アドテク×Scala meetup で登壇してきました

 

ありがたいことに、ひょんなことから声を掛けていただき、去る 11月20日
サイバーエージェント社で行われた『アドテク×Scala meetup』で少しだけお話をさせていただきました。

 

tl;dr

 

感想

  • アドテクを支えるScala - Ad Generationの場合
    • アドジェネさん、Spray/Actor ベースで動いているのかー。
  • Dynalystが広告配信で使っている技術(Spray×Akka×Kamon×Zabbix)
  • アドテク×Scala×パフォーマンスチューニング
    • 緊張しました。 200人もの大人数の前で話したのは初めてです。
    • インフラ目線の内容も織り交ぜ、自分自身が普段心がけていることをまとめました。
    • アドテク以外の開発でも参考になる部分はあると思います。
  • 軽量言語メインの 文系エンジニアだった自分が Scalaのシステム開発に携わることになった経緯
    • 馬の人だー。
  • 座談会
    • OE(@OE_uia)さん の貫禄の回し。
    • あのベテラン司会者のような落ち着きは、どこから生まれるのだろうか。
    • ご質問くださった皆様、どうもありがとうございました。

 

全体を通して

  • 与えられた発表時間はプレゼンに集中し、後で質問を受け付けるというスタイル。
    発表者側としてはとてもやりやすかった。
  • Spray/Actor ベースのシステム、十分に枯れてきた感がある。
    早いところプロダクションで作ってみたい。
  • 無料で振る舞われたビール & 寿司に感動。さすがサイバーエージェントさんや。
    (こちらはスタッフ & 登壇者専用) 
    Photo 2014 11 20 20 56 20 
  • 人気沸騰中の Scala ガール (AdTech Studio Girls Blog) に会えました!
  • 当日だけでなくイベントの準備に奔走されたCA社の皆様、当日ご来場くださった皆様、
    本当にありがとうございました!
    (こちらは開場前の準備風景)
    Photo 2014 11 20 18 36 53 

 

11.07.2014

Integrating Your Python Projects with Travis-CI and Coveralls

Python: GitHub 上のプロジェクトを Travis-CI, Coveralls と連携する

 

GitHub でパブリックなプロジェクトを作ったら、無料でCI環境とカバレッジ環境を手に入れることが可能。
これを利用しない手はない。

以下、具体例として一つの小さなプロジェクトを作りながら説明する。

 

Step 1: GitHub リポジトリ作成

setup.py で管理された Python プロジェクトを作る。

ディレクトリ構成

構成の一例。

.
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── setup.py
├── src
│   └── yourpackage
│       ├── __init__.py
│       └── yourmodule.py
└── tests
    ├── __init__.py
    └── test_yourmodule.py

 

アプリケーション/ライブラリの実装

ソースコードは src ディレクトリ配下にまとめる。

ここでは例として yourpackage というパッケージと、適当な数値計算を行うyour_function という関数を用意した。

__init__.py にインポート対象(クラス/関数)を書くのを忘れずに。

from .yourmodule import your_function
def your_function(x):
    ret = 0
    if x % 2:
        ret |= 1
    if x < 10000:
        ret |= 2
    if x == 10000:
        ret |= 4
    if x % 9 == 7:
        ret |= 8
    if x / 2000 == 6:
        ret |= 16
    return ret
テストコードの実装

テストコードは tests ディレクトリ配下にまとめる。

tests 自体もパッケージとして管理したほうが利便性がよいので、空の __init__.py を作る。
そして以下のようなテストコードを書いた。

import unittest
from yourpackage import your_function


class TestYourModule(unittest.TestCase):
    def setUp(self):
        pass

    def test_your_function_zero(self):
        self.assertEqual(your_function(0), 2)

    def test_your_function_odd(self):
        self.assertEqual(your_function(1), 3)

    def test_your_function_greater_than_10000(self):
        self.assertEqual(your_function(10006), 8)

    def test_your_function_12345(self):
        self.assertEqual(your_function(12345), 17)

カバレッジ結果をわかりやすくするため、あえてカバレッジ率は 100% にならないようにしている。

setup.py セットアップスクリプトの記述

今回は必要ないが、他の Python ライブラリに依存があればここで記述する。

from setuptools import setup, find_packages

setup(
    name='your-project-name',
    version='0.0.1',
    description='description for your project',
    author='your name',
    author_email='your email address',
    url='your url',
    install_requires=[
        # list your dependencies
    ],
    tests_require=[
        # dependencies for unit testing
    ],
    package_dir={'': 'src'},
    packages=find_packages('src'),
    include_package_data=True,
    test_suite='tests',
)
テストの実行

setup.py を使ってテストを実行する。以下のように表示されれば OK。

$ python ./setup.py test
running test
running egg_info
creating src/your_project_name.egg-info
writing src/your_project_name.egg-info/PKG-INFO
writing top-level names to src/your_project_name.egg-info/top_level.txt
writing dependency_links to src/your_project_name.egg-info/dependency_links.txt
writing manifest file 'src/your_project_name.egg-info/SOURCES.txt'
reading manifest file 'src/your_project_name.egg-info/SOURCES.txt'
writing manifest file 'src/your_project_name.egg-info/SOURCES.txt'
running build_ext
test_your_function_12345 (tests.test_yourmodule.TestYourModule) ... ok
test_your_function_greater_than_10000 (tests.test_yourmodule.TestYourModule) ... ok
test_your_function_odd (tests.test_yourmodule.TestYourModule) ... ok
test_your_function_zero (tests.test_yourmodule.TestYourModule) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

ここまでできたら、一旦 GitHub に公開リポジトリとして push する。

 

Step 2: Travis-CI の登録

  • https://travis-ci.org/ にアクセス
  • メニュー右上の Sign in with GitHub をクリックし、GitHub OpenID認証を承認
  • ログイン後、アカウント名 -> Accounts -> Repositories の画面で
    連携したいプロジェクトをチェック・オン
    Travis CI Free Hosted Continuous Integration Platform for the Open Source Community

 

Step 3: Coveralls の登録

  • https://coveralls.io/ にアクセス
  • メニュー右上の SIGN IN ボタンをクリックし、GitHub OpenID認証を承認
  • ログイン後、REPOS -> ADD REPOS ボタンをクリック
  • リストに対象リポジトリが表示されていなかったら、画面上部の SYNC GITHUB REPOS ボタンをクリック
  • 連携したいプロジェクトをチェック・オン
    Coveralls Test Coverage History Statistics

 

Step 4: .travis.yml の設置

プロジェクトルートに、.travis.yml という名前の設定ファイルを作れば
Travis-CI がそのファイルを読み取ってテストを走らせてくれる。

こちら (Travis CI: Building a Python Project) を参考に、以下のような内容で記述。

---
language: python
python:
  - "2.6"
  - "2.7"
install:
  - pip install coveralls
script:
  - coverage run --source=src setup.py test
after_success:
  - coveralls

install, script, after_success に Coveralls 連携のための記述を追加していることに注意。

この状態で GitHub に push を行えば、ほどなく(数分以内に)自動的に Travis-CI, Coverall の処理が開始される。

 

Step5: 結果確認

Travis-CI, Coverall ともに GUI ですぐに結果を確認できる。
またテストに失敗した場合は、自動的に通知メールが飛んでくる。 

  • Travis-CI
    Travis CI Free Hosted Continuous Integration Platform for the Open Source Community
  • Coveralls
    Mogproject skeleton python Coveralls Test Coverage History Statistics

    ファイル単位のカバレッジももちろん閲覧可能。
    Mogproject skeleton python Job 1 src yourpackage yourmodule py Coveralls Test Coverage History Statistics

 

Step 6: バッジの登録

Travis-CI, Coveralls ともに公式のバッジが用意されている。

  • Travis-CI
    Travis CI Free Hosted Continuous Integration Platform for the Open Source Community
  • Coveralls
    Mogproject skeleton python Coveralls Test Coverage History Statistics 

これらを README に貼り付ければ、GitHub 訪問者にとってもステータスが一目瞭然だ。
Mogproject skeleton python 

さいごに

今回は Python プロジェクトの例を挙げたが、setup.py やテストコードの準備さえ済めば、実に簡単に連携できる。

まさに朝飯前。『Web系エンジニアにとってテストコードを書くのは朝起きて歯磨きをするように当たり前のこと』との言もあったが、これらのサービスはさながら無料の朝食サービスのようなものだ。

より高度な使い方もできるので、詳細は各サービスのドキュメントを参照。以下はその一例。

  • master 以外のブランチと連携
  • pull request との連携 (マージする前に結果を確認できるので便利)
  • 通知先の拡張 (チャットサービス等と連携)
  • プライベートリポジトリとの連携 (有償)

Travis-CI, Coveralls ともに他の言語でも利用可能なので、今後も積極的に活用していきたい。

 

今回使用したコード

 

References

11.06.2014

Cheat Sheets for My Sake

自分向け各種チートシートまとめ

 

自分が忘れがち or 使用頻度の高い事柄だけ書いておく。

11.05.2014

Ansible: Visualizing CloudWatch Metrics with Grafana

Ansible: Grafana で CloudWatch のメトリクスを可視化するための Playbook

 

tl;dr

Grafana に触れてみたいなら、まずは Grafana Play Home

 

目的
  • AWS CloudWatch の各種メトリクスを Grafana で見たい
  • Grafana の URL は SSL + ベーシック認証で保護したい
  • これらのセットアップ作業を Ansible で自動化したい

 

使ったもの
  • Amazon CloudWatch: AWS 標準の監視コンソール。メトリクスデータが蓄積している前提
  • Amazon EC2
    • Amazon Linux: インスタンスを1台立ち上げ、この中に全てをインストールする
  • Nginx: フロントエンドの Web サーバ。BASIC認証 + 自己署名証明書によるSSL通信を設定。
  • Grafana: メトリクスデータの可視化ツール
  • Graphite: メトリクスデータ管理システム
    • uWSGI: フロントエンド連携用に必要
    • MySQL: Graphite のデータストアとして使う
  • cloudwatch-dump: CloudWatch のメトリクスデータを取得し、Graphite に流し込むために使用
  • Ansible: セットアップ(プロビジョニング)の自動化

 

コード

こちらのリポジトリに Vagrant + Ansible コード一式と再現手順を書いた。

必要なソフトウェア/プラグインをインストール後、vagrant up を行えば以下のような Grafana の画面に触れられるようになる。

Grafana Grafana

Grafana のロゴが表示されないのは https 通信をしているためで、表示させるにはソースコードに手を入れる必要がありそう。

 

はじめに

システム監視の目的は大きく「異常検知」と「トレンド分析」の2つに分けられる。

また一般に、「トレンド分析」がその対象者に応じて、例えば「システム利用者向け分析」「システム管理者向け分析」「経営判断層層向け分析」の3つのように分類できると、スッキリとしたシステムになりやすい。

 

今回の構成ではメトリクスデータは一旦 CloudWatch に集約される想定である。

異常検知は CloudWatch が持ち合わせている Alarm 機能で対応する。
Simple Notification Service を使うことでメール送信だけではなく、携帯端末にpush通知を行ったり、特定のURLにhttpリクエストを投げたり、オートスケールを実現したりなど工夫次第でできることは多い。

次に、そのメトリクスデータを一式 Graphite に投入する。
これを Grafana のようなフロントエンドで可視化することにより、異なる対象者に向けたダッシュボードを簡単に作ることができる。
生成されたグラフを週次レポートのような形で定期的に発信するのもよい。

 

構成イメージ

解説

 

Vagrantfile
  • プラグイン dotenv を使うことでアカウント固有の定義を別ファイル(.env)に分離
  • (コスト節約のため)米国東部リージョンに t2.micro インスタンスを1個新規構築する
  • Ansible のロールの中で使う変数は、extra_vars として引き渡す
Role: aws-scripts-mon
  • これは本来監視される側のセットアップである
  • CloudWatch の標準機能だけは取得できない Linux 内部の情報(メモリ/スワップ/ディスクスペース使用状況)を CloudWatch に送信する
  • 今回は、ec2-user の ~/aws-scripts-mon ディレクトリ配下にモジュール一式を配置し、Cron で 5分おきにレポーティングが実行されるよう仕掛けた
  • CloudWatch API のアクセスには、EC2 構築時とは別の IAM ユーザ (cloudwatch) を準備した
Role: nginx
  • もちろん Grafana は Apache でも動作する(その方が簡単だ)が、今回は Web サーバとして Nginx を採用する
  • このロールの中で自己署名証明書を作成し、/etc/nginx/cert ディレクトリ配下に配置した
  • 実際のSSLサイトの設定は、Grafana のインストール後に実施する
Role: mysql
  • デフォルトの SQLite でも動作するが、運用を考えるなら MySQL 等のほうが堅い
  • Amazon Linux はデフォルトで利用可能な EPEL リポジトリに対して yum install するだけ  
Role: graphite
  • jsmartin/ansible-graphite を参考にした
  • python-carbon を yum でインストールしたのは、起動スクリプトの有り物を使いたかったので
  • 最初は graphite-web も yum で導入したのだが、/opt/graphite/bin 以下のモジュールがインストールされずエラーが出て面倒なことになったので、最終的にこちらは pip で入れることにした
  • そのため、carbon 関係はFHS準拠っぽいディレクトリ構成なのに graphite の場所は /opt/graphite 配下という状態になっている
  • コマンドによって処理の要否を判定する雛形はこちらにも書いた
  • Whisper のデフォルトのデータ間隔・保持期間を 5分・2年 に書き換えている。(storage-schemas.conf)
    こうすることで長期間の分析をしてもデータが重くならない。(逆に1分単位の分析はできなくなる)
  • 起動がうまくいかない場合は、大抵 /var/log/graphite-web.log を見れば原因がわかるはず 
Role: cloudwatch-dump
  • 自作のダンプツールを pip でインストール
  • ec2-user の ~/cloudwatch-dump ディレクトリ配下にシェルを置き、Cron で 1時間に1回(*:05)、graphite にデータを流し込む
  • 何かあった時でもデータを再投入できるよう、~/cloudwatch-dump/logs に直近のログを残している
    (ローテーションは logrotate を使ったほうが良かったかも)
  • シェルスクリプトの中で、メトリクスパス中に含まれるEC2インスタンスIDをニックネームへの変換している
Role: elasticsearch
  • RPM のダウンロード先を指定して、yum install するだけ
Role: grafana
  • Grafana 自体は独立したツールなので、インストールは素直に終わる
  • カスタマイズは config.js をテンプレートエンジンに乗せて行う。Grafana のバージョンによって微妙に項目が変わっているので注意
  • 最後に Nginx の SSL サイト設定と、BASIC認証用のファイルを作って完了
 
References

Related Posts

11.03.2014

Mac: Upgrading to OS X Yosemite

Mac: OS X Yosemite にアップグレードした際のメモ

 

reattach-to-user-namespace のアップデート

HomeBrew でインストールした reattach-to-user-namespace で警告が出た。

warning: reattach-to-user-namespace: unsupported new OS, trying as if it were 10.6-10.9
warning: _vprocmgr_move_subset_to_user failed
warning: reattach-to-user-namespace: unable to reattach

reattach-to-user-namespace の最新版をインストールすればよい。

$ brew upgrade reattach-to-user-namespace

 

テキストエディタなどで Ctrl-N (カーソルを一行下へ移動) が効かなくなる

なぜか、システム環境設定 Keyboard -> Input Sources -> Japanese -> Windows-like shortcuts に
チェックが入っていると、Emacs ライクなキーバインドである Ctrl-N が効かなくなるようだ。

チェックオフにして解決。

Screenshot 11 3 14 13 33

ハードウェアドライバの更新

11.01.2014

cloudwatch-dump - Dump All the CloudWatch Metrics Statistics

Python: CloudWatch のメトリクスデータを全て出力するスクリプト

 

概要

1. 特定リージョンの AWS CloudWatch からメトリクスの一覧を取得し、
2. その全てのメトリクスに対して、ある期間を一定の単位時間で「平均」「合計」の統計値を取得し、
3. その結果(メトリクス、数値、タイムスタンプ)を Graphite に投入可能な形式で出力する

そんな Python スクリプトを作成。

 

モチベーション

やりたいことは、CloudWatch のデータを Grafana で見たいだけ。

Grafana AWS

はじめは Fluentd + fluent-plugin-cloudwatch + fluent-plugin-graphite を使って流し込んでいたのだが、

  • メトリクスと統計方法(Average, Sum, Maximum)を1個1個指定しなければいけなかったり (将来にわたってメンテが必要)
  • ちらほらデータの欠落が見られたり
  • バッファリングの関係か、データが更新されるタイミングを制御しづらかったり
  • 特定期間だけリトライしたい場合に難しかったり
といったことがあったので、1時間おきのバッチ処理で一括インポートをすることにした。
 
とはいえ悩みどころなのが統計方法。
CloudWatch では各サービス・各メトリクスごとに推奨の(意味のある)統計方法が異なっている。

しかし今回は割り切って、全メトリクスについて 平均(Average)、合計(Sum) を取得することにした。
物によっては Maximum を得たい場合もあるが、一旦は目を瞑る。

 

今後の課題

  • CloudWatch の APIリクエストも決して無制限で使えるわけではないので、極力無駄なリクエストは減らしたい。
    (現時点では100万件/月まで無料, それ以降は1,000件ごとに0.01USDの課金)
    CPU使用率の Sum など、結局個別に指定する他ないのか。
  • 個別のメトリクス統計方法の指定 (DynamoDB の ItemCount の Maximum など) をできるようにするか
  • 並列化をして高速に
  • ユニットテストを充実させる

 

導入方法、注意事項についてはリポジトリの README参照。