dockerでDynamoDB Localを構築する

DynamoDB LocalをDockerを利用して構築します。

ダウンロードして、展開して実行だけですね。

FROM centos:centos6
MAINTAINER oomatomo ooma0301@gmail.com

RUN rpm -Uhv http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
RUN yum update -y
# java
RUN yum install -y wget tar java-1.8.0-openjdk java-1.8.0-openjdk-devel
# dynamodb download
RUN wget http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz

ENV DYNAMODB_PATH /usr/local/dynamodb
RUN mkdir -p $DYNAMODB_PATH
RUN tar zxf dynamodb_local_latest.tar.gz -C $DYNAMODB_PATH
WORKDIR /usr/local/dynamodb

EXPOSE 8000
CMD ["java", "-Djava.library.path=.", "-jar", "DynamoDBLocal.jar", "-sharedDb"]

起動時のオプションの-sharedDbは、リージョンごとにデータのファイルを作成されるのを防ぎます。 初めてDynamoDB Localを利用した時にリージョンごとにデータが作成されるのを知らなかったのではまりました。

docs.aws.amazon.com

docker-composeで開発用のredisとmysqlを構築する

今回は、docker-composeを使って開発用にredisとmysqlを使います。
最近は、docker-composeをサービスごとに作って利用しています。

docker-composeをインストール方法

brew caskでinstallできます。

brew install caskroom/cask/brew-cask
brew cask install docker-compose

docker-composeの前にredisとmysqlのdockerfileの内容です。

Dockerfileの内容

とりあえず、自分が昔から使っているやつを貼っときます。 今は、docker公式のやつがあるのでそちらを使ってもいいかもしれません。

RedisのDockerfile

公式のやつでもいいです。 https://hub.docker.com/_/redis/

FROM centos:centos6
MAINTAINER oomatomo ooma0301@gmail.com

# install package
RUN rpm -Uhv http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
RUN yum update -y
RUN yum install -y wget tar make gcc

RUN cd /tmp && \
      wget http://download.redis.io/redis-stable.tar.gz && \
      tar xvzf redis-stable.tar.gz && \
      cd redis-stable && \
      make && \
      make install && \
      cp -f src/redis-sentinel /usr/local/bin && \
      mkdir -p /etc/redis && \
      cp -f *.conf /etc/redis

# どこからもアクセスできるようにする
RUN sed -e "s/# bind 127.0.0.1/bind 0.0.0.0/" -i /etc/redis/redis.conf

EXPOSE 6379
CMD ["redis-server", "/etc/redis/redis.conf"]

mysqlのDockerfile

公式のやつでもいいです。 https://hub.docker.com/_/mysql/

FROM centos:centos6
MAINTAINER oomatomo ooma0301@gmail.com

# install package
RUN rpm -Uhv http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
RUN yum update -y
RUN yum install -y wget tar

# install mysql
RUN wget http://dev.mysql.com/get/Downloads/MySQL-5.5/MySQL-5.5.46-1.el6.x86_64.rpm-bundle.tar
RUN tar xvf MySQL-5.5.46-1.el6.x86_64.rpm-bundle.tar
RUN yum localinstall -y MySQL-shared-compat-5.5.46-1.el6.x86_64.rpm
RUN yum localinstall -y MySQL-shared-5.5.46-1.el6.x86_64.rpm
RUN yum localinstall -y MySQL-server-5.5.46-1.el6.x86_64.rpm
RUN yum localinstall -y MySQL-client-5.5.46-1.el6.x86_64.rpm
RUN yum localinstall -y MySQL-devel-5.5.46-1.el6.x86_64.rpm

# create mysql
ADD privilege.development.sql /tmp/
RUN /etc/init.d/mysql start \
   && mysql -u root < /tmp/privilege.development.sql

EXPOSE 3306
CMD ["/usr/bin/mysqld_safe"]

privilege.development.sqlの内容
ユーザの作成を行っているだけです。
もしくは、サービスごとのSQLだったりします。

CREATE USER 'ooma'@'%' IDENTIFIED BY 'tomo';
GRANT ALL PRIVILEGES ON *.* TO 'ooma'@'%' WITH GRANT OPTION;

docker-composeの設定方法

docker-compose は、docker-compose.ymlというyml形式の設定ファイルを作成します。

とりあえず、最低限の設定の説明です。

[キー]:
  build: [dockerfileの場所]
  ports:
    - "[hostのポート]:[dockerのポート]"

実際の設定ファイル。

mysql:
  build: ./mysql
  ports:
    - "3306:3306"
redis:
  build: ./redis
  ports:
    - "6379:6379"

docker-composeの使い方

docker build

# ビルド
docker-compose build
docker-compose build [キー]
docker-compose build redis
# バックエンドでは、動かない
docker-compose up

redisのみ実行すると。。

$ docker-compose up redis
Creating docker_redis_1...
Attaching to docker_redis_1
redis_1 |                 _._
redis_1 |            _.-``__ ''-._
redis_1 |       _.-``    `.  `_.  ''-._           Redis 3.0.5 (00000000/0) 64 bit
redis_1 |   .-`` .-```.  ```\/    _.,_ ''-._
redis_1 |  (    '      ,       .-`  | `,    )     Running in standalone mode
redis_1 |  |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
redis_1 |  |    `-._   `._    /     _.-'    |     PID: 1
redis_1 |   `-._    `-._  `-./  _.-'    _.-'
redis_1 |  |`-._`-._    `-.__.-'    _.-'_.-'|
redis_1 |  |    `-._`-._        _.-'_.-'    |           http://redis.io
redis_1 |   `-._    `-._`-.__.-'_.-'    _.-'
redis_1 |  |`-._`-._    `-.__.-'    _.-'_.-'|
redis_1 |  |    `-._`-._        _.-'_.-'    |
redis_1 |   `-._    `-._`-.__.-'_.-'    _.-'
redis_1 |       `-._    `-.__.-'    _.-'
redis_1 |           `-._        _.-'
redis_1 |               `-.__.-'
redis_1 |
redis_1 | 1:M 20 Oct 22:49:23.033 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1 | 1:M 20 Oct 22:49:23.033 # Server started, Redis version 3.0.5
redis_1 | 1:M 20 Oct 22:49:23.033 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1 | 1:M 20 Oct 22:49:23.033 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
redis_1 | 1:M 20 Oct 22:49:23.034 * The server is now ready to accept connections on port 6379
←ここでCtrl+Cをやる
^CGracefully stopping... (press Ctrl+C again to force)  
Stopping docker_redis_1... done

docker run

# 全体をデーモンで実行
docker-compose up -d 
# mysqlのみデーモンとして実行
docker-compose up -d mysql
# redisのみデーモンとして実行
docker-compose up -d redis

あと各ポートにアクセスします。

今まで、毎回 docker run ..でやっていたのが、簡単に調整出来るのがいいです。

Gitで削除したファイルを一括でgit rmする方法

Gitで削除済みのファイルを削除したよっていうコミットを作成する時
git rm ファイル名 をします。

毎回 git rm するのが嫌なので、一括で出来るようにする方法です。

git ls-files -d -z | xargs -0 git rm 

git ls-files の説明

リポジトリ対象のファイルを見ることができます。

-dで現在の作業ディレクトリで削除されたファイルの一覧を出します。
-zで削除の終端文字が\0になります。
デフォルトは、改行です。

xargs の説明

前のコマンドの出力結果を引数みたいな形で繋げることが出来ます。
よく使います。

-0\0を区切り文字として扱ってそれぞれ引数になります。

あとは、引数として分割されたファイルをgit rmしていくだけです。

git はサブコマンドが面白いからいいですね。

docker-machineを使ってみる

Install for Mac

brew でサクッといけます。

brew install docker
brew cask install docker-machine

docker-machine

https://docs.docker.com/machine/#osx-and-linux

dockerを利用する土台用の仮想環境を構築する。
(boot2dockerではじめに作成した環境と同じ)
boot2docker init
boot2docker ssh で接続した環境

今回は、virtualboxを使って環境の構築を行います。
メモリーやディスクの設定は、お好きにどうぞ。

docker-machine create -d virtualbox --virtualbox-disk-size "20000" --virtualbox-memory "2048" dev

dockerコマンドを利用するためには、eval "$(docker-machine env dev)"が必要です。

eval "$(docker-machine env dev)"
docker ps

簡単ですね。

dockerにアクセスする方法

# dockerを動かしているサーバにアクセス
docker-machine ssh dev
tce-load -wil util-linux
# アクセスしたいコンテナIDを設定すればおk
sudo nsenter --target $(docker inspect --format '{{.State.Pid}}' アクセスしたいコンテナID) --mount --uts --ipc --net --pid
# 例
sudo nsenter --target $(docker inspect --format '{{.State.Pid}}' 2f1688b9fbe9) --mount --uts --ipc --net --pid

zshの設定

.zshrcなどにeval "$(docker-machine env dev)"を記入しておく

docker-machineが起動してない場合でターミナルを開くと

dev is not running. Please start this with docker-machine start dev

という内容が出るので安心。

感想

今回はローカルのリソースを使ったので、boot2dockerとあまり変わらなかった。

Grapeではto_jsonはいらないよ


grape側のAPI

{ error: "not exist cache" }.to_json


format :json 指定したらいらないらしい

to_jsonを行った場合に以下のような違いが出てくる。

expected: "{\"error\":\"not exist cache\"}"
got: "\"{\\\"error\\\":\\\"not exist cache\\\"}\""


テストコードを書いてて、これと1時間にらめっこしてしまった。

 

PerlTidyでの一括整形コマンド


地味に面倒くさかったので、メモとして残します

初めにやろうとしたこと

find ../lib -name '*.pm'  | xargs perltidy -pbp 

失敗でした。ただ、整形後のコードが標準出力として表示されただけでした。


-pbpは以下の内容が含まれています。



上記のオプションでダメなところが 「 -st # Output to STDOUT 」 です。


-st なしでコマンドを実行すればいいのではないかと考えました。

find ../lib -name '*.pm'  | xargs perltidy i=2 .... 

これもダメでした。
理由としては、元のpmファイルはそのままで、整形されたファイルが *.pm.tdy として出力されていたためです。

これを解決するオプションが [ -b ] です。

-b      backup original to .bak and modify file in-place

元のファイルを .bak ファイルとして置き換えてくれるオプションです。

これにより .pm ファイルが整形後のファイルになります。

そして、最後に .bakファイルを削除すれば全て置き換えてくれます。

最終的に以下のシェルスクリプトにまとまりました。



 

Catalystでリクエスト元IPアドレスをいじるテストについて


Catalystでリクエスト元のIPアドレス によって処理を分岐させるコードがあったのですが、
テストコードでのテストはしていませんでした。

存在しなかったので、テストコード書くことにしました。

やったこと

・ LWP::UserAgent local_address
・ Test::WWW::Mechanize::Catalyst環境変数の設定
Plack::Test test_psgi
・ Test::WWW::Mechanize::PSGI

LWP::UserAgent local_address

Test::WWW::Mechanize::Catalystの親クラスに
LWP::UserAgentがいたので、local_addressが利用できるのではないかと感じ 以下のコードで試しました。
結果は失敗でした。まねしないでください。



出来ない理由は以下の通りです。
Test::WWW::Mechanize::Catalystの親クラスとして
Test::WWW::Mechanize
WWW::Mechanize
LWP::UserAgent
があります。

以下のコードはWWW::Mechanizeのgetメソッドです。



なるほど、SUPER::getしてますね。
LWP::UserAgentのgetでは、requestを呼んでいるため
  LWP::UserAgent->get
  LWP::UserAgent->request
と思ってました。

でも、実際は違いました。
  LWP::UserAgent->get
  WWW::Mechanize->request
でした。
ちゃんとコメントにも書いてありますね。
# but it in turn calls the request() method here in Mechanize

さらにWWW::Mechanize->requestで_make_requestを呼んでました。
以下のコードは、WWW::Mechanizeの_requestです。


整理すると Test:::WWW::Mechanize::Catalystでgetを使うと
 WWW::Mechanize->get
 LWP::UserAgent->get
 WWW::Mechanize->request
 Test::WWW::Mechanize::Catalyst->_make_request
こんな感じの流れになります。
そのため、LWP::UserAgent->requestが呼ばれていないためHTTPのリクエスト生成時にlocal_addressが使用されないだろうと思いました。
また、LWP::UserAgentのDocumentに local_addressはIO::Socket::INETにLocalAddrとして渡すと書いてありました。

Get/set the local interface to bind to for network connections. The interface can be specified as a hostname or an IP address. This value is passed as the LocalAddr argument to IO::Socket::INET.

これでは確かに無理ですね!!!


Test::WWW::Mechanize::Catalyst環境変数の設定

今度はTest::WWW::Mechanize::Catalyst環境変数であるREMOTE_ADDRを渡す方法を探りました。

Test::WWW::Mechanize::Catalyst(Version 0.59 )は、_do_catalyst_requestでリクエストを処理しています。



上のコードを見てもらえる分かるのですが、CATALYST_SERVERの有無をチェックしています。

(有)_do_remote_requestでCATALYST_SERVERにリクエストを投げる
(無)Catalyst::Test::_local_requestにCatalystのオブジェクトのインスタンスを渡す

ということになっています。

Catalyst::Test::_local_requestでは_requestを呼び出していました。



%extra_envにREMOTE_ADDRを設定できれば、いけることが分かると思います。

しかし、Test::WWW::Mechanize::Catalystの_do_catalyst_requestの段階で引数として渡せないため
Test::WWW::Mechanize::Catalyst環境変数の設定はできないみたいです。

Catalyst::Test::_local_request($self->{catalyst_app}, $request)

Plack::Test test_psgi

次に行ったことが Plack::Testを使った方法です。
こちらは以下のコードで成功しました。
test_psgiでappに渡す内容は Catalyst::Test::_requestをパクって・・参考にしています。




Test::WWW::Mechanize::PSGI

Test::WWW::Mechanize::Catalystがダメだったので
Test::WWW::Mechanize::PSGIにしてみました



今回はTest::WWW::Mechanize::PSGIを利用しています。
また、簡単に切り替えができるように以下のようなメソッドで使ってます。



テストコードを少しずつ書くようにして半年経ちますが、
テストコードを書かないと不安になるようになりました。
そしてテストコードを書くのは楽しいです。

一つ一つの分岐を網羅していくのは、ドラクエのダンジョンで隅々まで網羅していくのと
同じ感覚です。
(開発では、完全網羅は難しいですけど)