AWS LambdaのPython版をいまさらながら使ってみた

ので、導入の時に迷った点を残しておく。

  • pythonで用意するエントリポイントのファイル名や関数名は自由 (ただし、Configuration#Handlerで"ファイル名.エントリポイント関数名" で指定する必要がある)
  • エントリポイントの関数の第一引数は基本dict形式でイベントが飛んでくる。 第二引数はLambdaの実行メタ情報などを記録したContext
  • エントリポイントが返すreturnの値は特に利用しない (実行テストの結果に使われるため、どのような状態で終わったかの文字列を書いておいても良さそう)
  • AWSリソースを扱うときのライブラリはboto3がデフォルトで使える
  • ログ関連のIAM Roleを作って、それに自分が使うサービスのパーミッションを与えると楽
  • 外部ライブラリを使いたければzipでアップロードするときに中に含める。 "pip install hoge -t ."
  • 固定の設定ファイルなどもzip内においておけば、読み込むことは可能
  • cronの設定はUNIXとは異なる形式で5個ではない。

実際に使ってみると、zipファイル圧縮でアップロードする、boto3が使えるといった点でかなりそのままローカルで書いたPythonをそのまま使えたイメージ。

Amazon Linux上のOpenJDKをアップデートするとTimeZoneのデフォルトがおかしくなったというお話

はじめに

Amazon Linuxで動作するJavaのWebアプリケーションを動かしていたんだけれど、アップデートをするとどうにもログの出力日付がおかしい。
ということで色々調査してみてた結果、以下のTimeZoneのデフォルト値(取得方法は下記のソースコードを参照)が openjdk-7u79 と openjdk-7u85とで異なる結果を返していることが問題の根源である模様。

なお、コマンドの date で取得できる結果は JSTになっていることを確認済み。

(追記: Amazon Linuxglibcが更新されると /etc/localtime も合わせて更新されてしまい、システムの日付がJSTに設定されていたにも関わらずUTCに戻ってしまう現象もありますが、この問題の原因はこれとは異なり、純粋にopenjdkのみをupdateした場合に生じる問題です)

import java.util.*;

public class Test {
  public static void main(String[] args) {
    System.out.println(TimeZone.getDefault());
  }
}
// openjdk 7u79
sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null]

// openjdk 7u85
sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

バグなのかどうなのか良くわからないので、一応原因の調査をしてみることに

結論

デフォルト取得の仕様が変わっている模様。

Amazon LinuxでTimeZoneを変更する方法については date の結果だけを変更する方法*1がブログでは紹介されていることが多く、今回はそれしか実行していなかったが、Amazon Linuxのガイドをちゃんとみてみると /etc/sysconfig/clock の値を修正することがきちんと書いてある。

http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/set-time.html

この設定のZONEの値を "Asia/Tokyo" に修正してやれば、きちんとZoneInfoの値としてAsia/Tokyoを返すようになった。

おまけ

CentOS7/RHEL7 から時刻変更のためのコマンド timedatectl というコマンドが追加されている。

今回の検証中に問題がJVMのビルドなのかOSに依存するのか分からなかったため、RHEL7をローンチして調べていた。

その際にも検証のため /etc/localtime のみをJSTに更新していたが、このコマンドを知って現在の情報を表示してみると、全く関係ないTimezoneの値が表示されるにも関わらず、その時刻修正だけが(JST +0900)となっていた。

そのため、/etc/localtime のみではなく別のところでTimezoneの情報を保持していることが分かり、問題の原因らしきものと解決方法を発見することができた。

なお、この時に調査してみた限り、openjdk-8u51でも /etc/localtime に依存しないTimeZoneを返していたため、今後のバージョンアップでLinuxの時刻設定手法を表面的に実施している場合、アプリケーション側にしっぺ返しが来るかもしれません。

*1:/etc/localtimeを変更する

Eclipse BIRTのレンダリング時にReport Parametersによって読み込む固定ファイルを変更する

シナリオ

  • レポート生成ツールとしてEclipse BIRTを利用している
  • 多数のCSVファイルが存在する
  • BIRT Serverへのリクエスト時に引き渡す値によって異なるCSVファイルを読み込んで出力したい

CSVを読み込むには

以下、BIRT用のEclipse Designerを利用していることが前提条件です。

今回はBIRT 4.3.1で確認。

  • Data Explorer > Data Sources > New Data Source
  • Flat File Data Sourceを選択し、このデータソース名の名前を入れる(例: summary)
  • Define Folder or a File URI で何も入力せず*1に Test Connection > Finish
  • Data Sourcesに追加されたデータソースをEditで開き、Property Bindingを選択
  • この中の File URI にはJavaScriptを利用した動的URIが記載できるので、それに従って記載する。


具体例として、year, month, dayを受け取って、"data/[year]/[month]/[day].csv" *2 を開く場合は以下のようになる。

なお、相対パスでファイルを記述する場合はContextRootからの相対パスになります。

大抵の場合、/(Tomcatなどのインストールパス)/webapps/birt/ がContextRootとなるので、この場合はbirtの下にdataという名前のシンボリックリンクを張っておけば便利に利用できるでしょう。

'data/' + param['year'] + '/' + param['month'] + '/' + param['day'] + '.csv'


XML Sourceで見るとこんな感じ。

        <structure>
            <property name="name">URI</property>
            <property name="id">10</property>
            <expression name="value" type="javascript">'data/' + param['year'] + '/' + param['month'] + '/' + param['day'] + '.csv'</expression>
        </structure>


最初のWizardでできると思ってかなり調査に時間を使ってしまった…

*1:後述の入力があると、この入力は無視される模様

*2:ここでは 0-padding無しの入力を考慮していないので、そのチェックが必要な場合はJavaScriptを使って記述してください

Amazon SQSでVisibility Timeout中のメッセージを消す方法

Amazon SQSのキューからメッセージを取得した場合、Visibility Timeoutという期限が設定されてこの間にはメッセージを受信した時に取得したReceiptHandleを利用しない限りVisibility Timeoutの再設定およびメッセージの削除を行うことができない。

Visibility Timeoutが短い場合はそこまで問題にはならないのだが、バッチ処理などを行わせるための仕組みとしてVisibility Timeoutをある程度長い時間にした場合にはそのReceiptHandleを失ってしまうとメッセージのVisibility Timeoutを待つしか処理を継続する方法がなくなってしまう。
そのため、Visibility Timeoutが長いキューの処理を行う場合、そのメッセージのIDとReceiptHandleをログに出すなどした方が問題にはなりづらそうである。

しかしながら、ログに出力忘れをした場合などにどうにかしてメッセージを削除する方法が無いのかなーと思っていたら、最近*1PurgeQueueというアクションが追加されていた。
問答無用で、SQSに入っている全てのメッセージを(Visibility Timeoutの状態を問わずに)削除するActionである。

http://aws.amazon.com/jp/about-aws/whats-new/2014/12/08/delete-all-messages-in-an-amazon-sqs-queue/

このアクションを呼び出すのはAWSCLIなどから呼び出せる。
が、新しい機能なので対応する新しいバージョンのAWSCLIをインストールしておかなくてはならない。

aws sqs purge-queue --queue-url https://sqs.ap-northeast-1.amazonaws.com/************/example-queue

キューに複数のメッセージを投入したが、何らかのエラーが発覚して全処理の取消しを行いたい場合や、SQSを使ったテストの中止時などには利用できそうだ。

*1:2014/12

matplotlibで円グラフを書く

タイトル通りだが、実際にやってみると幾つかうまく行かないところがあったのでメモ。

そもそも円グラフを書くには

plot.pie あるいは axes.pie関数を利用する。

http://matplotlib.org/examples/pie_and_polar_charts/pie_demo_features.html

続きを読む

commons-codecのBase64に改行が入ってくる

紆余曲折、うまく行かないことを調査していくとBase64.encodeの結果に改行が入っているというところに行き着く。

Javaでcommons-codecのBase64クラスを使ってたんですが、このencodeBase64Stringメソッドがバージョンによって戻ってくる文字列のフォーマットが違うということを知る。

自分が提供するjarの場合、依存するライブラリは全部1つのJarに固めて提供しているから、新しいバージョンが入ってて発覚しなかったけれど、他のところに組み込んで使おうとすると別のバージョンのjarを先に読み込んでそっちが使われることがあるのね……

http://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Base64.html#encodeBase64String(byte[])

Amazon Linux で yum に失敗する

Amazon Linuxを仕事で使っていると、誰もが一度は通る道。

インターネットには接続できるのに、Amazon Linux上でyumコマンドのどれを実行してもタイムアウトになってしまうことがある。

yumが使えないという場合の大半の事象がこれで良く聞かれる上に結構汎用的なパターンなので、検索で引っかかるようになってくれるといいな。

条件

大体、Amazon LinuxVPN接続したVPC内で利用する場合。

原因

Amazon Linuxyumリポジトリが受け付けるリクエストは、AWSのIGWから出た通信である必要があるため。言い換えると、Amazon Linuxにデフォルトで設定されているyumリポジトリは(当たり前だが)AWSで提供されているインフラ以外で動作するLinuxからのyumを許可していない。

一般に、上記の条件でVPN接続をしている場合、直接IGWを経由してインターネットに出ているのではなく、VPNの接続元のルータを経由して(IGWを経由せずに)インターネットに接続するルーティングが行われていることが多い。

対策

yumリポジトリに接続するサーバーがIGWから通信を行うようにすれば良い。
この時にNATインスタンス、あるいはHttpProxyインスタンスを立てて、それを利用すると良い。

(Proxyの方法を選ぶ場合、/etc/yum.confにProxyの設定が必要)

AWSのネットワーク設計的には、IGWと直接接続する public subnet *1 と IGWと直接接続しない private subnet に分けることを推奨しているので、public subnetにNATを立てて、Amazon Linuxを(NATへのルーティングが行われた)private subnetに立てるようにすれば良い。

Proxyを立てる場合には、private subnetが完全にインターネットから孤立していると以下に書いてあることが起きるので、とりあえずyumはできないがインターネットに(VPN経由で)接続できる環境で起動して、/etc/yum.confにProxyの設定を書き加えて利用すればよい。

おまけ

確実ではないけれど、自分が経験した内容を1つ。

Amazon Linuxはcloud-initの中でyumを使うなど、インターネットに接続できる前提の設定が色々と行われている(っぽい)ので、インターネットに完全に接続できない環境でローンチすると、起動に無茶苦茶時間が掛かっているように見える。
(sshdのサービス起動優先度はcloud-initのサービス起動優先度より低いので、cloud-initのタイムアウト処理が行われるまでsshdが起動せずに、中に入って確認できない事象が続く)

その上、起動時のUserDataに渡した内容を処理するのはcloud-initの最後の方で、インターネットにつながらず、cloud-initの先の方の処理がエラーになるとUserDataに書いた処理が実行されない。

少なくとも、自分がshellを書いて渡した場合はそうだった。

ので、インターネットに直接接続できない環境にAmazon Linuxを立てる場合は相当に工夫しなくてはいけない。 Proxyを設定しようとUserDataでパラメータを渡しても、そのシェルを実行してくれないからである。

自分は素のAmazon Linuxを直接起動するのではなく、一度IGWとNATごしに接続されている環境でProxyの設定を行った後リリース用のAMIを作成して、それをリリースしているのだけれども、private subnetにAmazon Linuxを立てる場合は皆どのようにしているものだろうか。

何か良い方法を知っている人がいれば、ご教授下さい。

*1:言い換えるとDMZ