M5Stack UIFlow の Custom Function を試す

 数日前に Twitter で M5Stack の UIFlow で Custom Function というのがリリースされたのを見たので、今回は試しに触ってみました。Custom Function というのは内容を自由に設定してブロックを作ることができる機能です。

UIFlow 用のファームウェアを Mac で Flash する

 UIFllow を使うには M5Stack に UIFlow 用のファームウェアを Flash しておく必要があります。以前は Mac からファームウェアを Flash するにはターミナルからコマンドで操作する必要がありましたが、最近では Windows と同じように Flash 用のツールが提供されているのでそれを使ってみます。ツールは UIFlow の設定画面からダウンロードすることができますので、 UIFlow にアクセスして設定画面を開きます。

f:id:akanuma-hiroaki:20190202214739p:plain

 設定画面に M5Burner-Flow-For-MacOS というリンクがありますので、これをクリックするとファイルのダウンロードダイアログが開きます。

f:id:akanuma-hiroaki:20190202214801p:plain

 デフォルトでは M5Burner_MacOS.zip というファイル名で保存されますので、これを解凍すると M5Burner_MacOS.app というアプリケーションになります。これを実行するとツールが起動するのですが、起動時に下記のようなダイアログが表示されて、ツールが起動できないことがあります。

f:id:akanuma-hiroaki:20190202214828p:plain

 これは MacOS のセキュリティ設定によるもので、製造者が信頼できない場合は実行することができなくなっています。以前の MacOS ではセキュリティ設定で、全てのアプリケーションを実行可能にすることもできたのですが、最近の MacOS ではその選択肢がなくなっています。

f:id:akanuma-hiroaki:20190202214947p:plain

 これを変更するには、ターミナルから下記のようにコマンドを実行し、全てのアプリケーションの実行を許可します。

$ sudo spctl --master-disable

 するとセキュリティ設定画面にもそれが反映されて、項目が追加で表示されます。

f:id:akanuma-hiroaki:20190202215039p:plain

 ちなみにずっとこのままの設定にしておくのは良くないので、ツールの実行が終わったら下記のようにコマンドを実行するか、セキュリティ設定画面から設定を戻しておくのが良いかと思います。

$ sudo spctl --master-enable

 ツールが起動すると下記のようなフォームが表示されます。執筆時の UIFlow の最新のバージョンは 1.1.2 なので、ファームウェアのバージョンも同じバージョンを選択して、 Flash ボタンをクリックします。しばらく待つとファームウェアが M5Stack に Flash されます。

f:id:akanuma-hiroaki:20190202215147p:plain

 これ以降の操作は以前と同様です。下記記事でも手順を書いていますので、よろしければご参照ください。

blog.akanumahiroaki.com

Custom Function を試す

 それでは Custom Function を試してみます。 UIFlow の 1.1.2 では下記のようにメニューに Custom (Beta) という項目が追加になっています。内容としては Create *.m5b fileOpen *.m5b file という2つがあります。まずは Create *.m5b file をクリックします。

f:id:akanuma-hiroaki:20190202215529p:plain

 すると UIFlow Block Maker というサイトに遷移します。

f:id:akanuma-hiroaki:20190202215627p:plain

 この UIFlow Block Maker で Custom Function の内容を指定し、ブロックを作成するわけです。今回はとりあえず画面に「Hello World!」のような文字列を表示する Function を実装してみます。「World」の部分は引数として指定できるようにします。設定項目は下記のようにしました。

  • Namespace: greeting

  • Block Color: 青

  • Name: hello

  • Type: Executable

  • Parameter: name (String)

 Block Code は実際に実行されるソースコードを設定しておくところで、今回は下記のようにしています。 UIFlow でラベルを指定した時に実行されるコードと同じものです。

label0 = M5TextBox(51, 39, "Hello ${name}!!", lcd.FONT_Default, 0xFFFFFF)

 画面左側の内容を変更していくと、右側のソースコードにリアルタイムに反映されていきます。今回のケースでは最終的には下記のような設定になります。設定が終わったら Download ボタンをクリックします。

f:id:akanuma-hiroaki:20190202220013p:plain

 するとファイルのダウンロード用のダイアログが開きます。今回は greeting.m5b というファイル名で保存しておきます。

 ファイルを保存したら、今度は Custom (Beta) の項目から Open *.m5b lib を選択します。するとファイルのアップロード用のダイアログが開きますので、先ほど保存した greeting.m5b を選択します。

f:id:akanuma-hiroaki:20190202220140p:plain

 m5b ファイルがアップロードされると、下記のように Custom (Beta) の項目に先ほど UIFlow Block Maker で作成した Custom Function のブロックが追加されます。

f:id:akanuma-hiroaki:20190202220307p:plain

 今回作成した Custom Function では表示する名前を引数として取ることにしていますので、その内容と共にブロックを設定して、実行ボタンをクリックします。

f:id:akanuma-hiroaki:20190202220508p:plain

 すると M5Stack で下記のように文字列が表示されます。

f:id:akanuma-hiroaki:20190202220609j:plain

まとめ

 手順として一度 .m5b ファイルをローカルに保存してそれをアップロードするという手順が直感的ではない気はしましたが、 UIFlow 自体にはまだファイルを保存しておくことができない中で、複数のアプリで汎用的に使用するコードをまとめておけるようになったのは便利かと思います。まだ Beta ということで、ブロックに表示されるのがパラメータの変数名だったりとまだわかりづらいところはありますが、今後使いやすくなっていくのではないかと思います。

AWS Cloud9 で API Gateway からの Lambda 実行

 前回の記事で Cloud9 での Lambda Function 開発について書きましたが、 Cloud9 から API Gateway 経由での Lambda Function 実行について書いていなかったので今回はそのやり方について書いておきます。

ローカル実行

 まずはローカルで API Gateway 経由で Lambda Function を動かしてみます。実行用のペインのプルダウンから API Gateway (local) を選択します。

f:id:akanuma-hiroaki:20190126192904p:plain

 そして Run ボタンをクリックすればローカルで実行できます。

f:id:akanuma-hiroaki:20190126181123p:plain

 ただ、前回実装した内容だと、下記のように Internal Server Error になってしまいます。

f:id:akanuma-hiroaki:20190126184900p:plain

 ローカル実行だと詳細なログが出ないようなのですが、同じ内容をリモートの環境で動かすと下記のエラーが出ています。

Execution failed due to configuration error: Malformed Lambda proxy response

 これは Lambda Function が返すレスポンスが API Gateway が期待するフォーマットになっていなかったことによるものです。下記ページにもその内容が書かれています。

aws.amazon.com

 API Gateway 経由で Lambda Function を使うには、 Lambda Function のレスポンスは下記のフォーマットの json で返す必要があります。

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}

 なので実装を下記のように変更します。変更したのは return の内容のみです。

import boto3

PHONE_NUMBER = "8180XXXXXXXX"
MESSAGE = "Test Message from Lambda"

sns = boto3.client('sns')

def lambda_handler(event, context):
    sns.publish(PhoneNumber = PHONE_NUMBER, Message = MESSAGE)
    return {
        'body': 'Sent SMS',
        'statusCode': 200,
        'isBase64Encoded': False
    }

 変更後に再度実行すれば良いのですが、私が試した限りでは、このまま API Gateway (local) で再実行しても、 Lambda Function の変更内容が反映されませんでした。一度 Lambda (local) を実行してから実行することで Lambda Function の変更内容が反映されて API Gateway 経由で実行されるようになり、下記のように成功レスポンスが返るようになります。

f:id:akanuma-hiroaki:20190126192606p:plain

リモート実行

 Lambda Function をリモートにデプロイした後は、リモートの API Gateway 経由の実行も可能です。実行用のペインのプルダウンで API Gateway (remote) を選択します。

f:id:akanuma-hiroaki:20190126192127p:plain

 あとはこれまで同様に Run ボタンをクリックするだけです。

f:id:akanuma-hiroaki:20190126192252p:plain

 リモートの API Gateway 経由で Lambda Function が実行され、結果が表示されます。

f:id:akanuma-hiroaki:20190126192350p:plain

まとめ

 ローカルで Lambda Function の実装を変更した場合に、 API Gateway での再実行だけではなく一度 Lambda Function 単体で実行しないと反映されないというのはちょっとわかりづらいところでしたが、それ以外は特に難しいことはなくローカルでもリモートでも Cloud9 から実行することができました。 Lambda Function を使用する場合には API Gateway を組み合わせるケースが多いと思いますので、 Cloud9 でそこも含めて開発できるのは便利ですね。

AWS Cloud9 で Lambda Function 開発

 前回の記事で Cloud9 で Rails の開発環境を作ってみましたが、 Cloud9 では Lambda Function の開発も行えるということで、やってみました。

Lambda Function の作成

 それでは Lambda 関数を作成してみます。 Cloud9 の新しい環境を作成すると Welcome ページが表示されますが、そこに Create Lambda Function というリンクがありますのでクリックします。

f:id:akanuma-hiroaki:20190119181629p:plain

 すると Lambda Function の作成フォームが表示されますので、まずは関数名とアプリケーション名を入力し、 Next をクリックします。

f:id:akanuma-hiroaki:20190119181750p:plain

 次に Function のテンプレートを選択します。今回はブランクから作成するので empty-python を選択して Next をクリックします。

f:id:akanuma-hiroaki:20190119181932p:plain

 続いて Function のトリガーを選択します。 API Gateway が選択できるようになっているので選択してみました。 Resours Path と Security を設定して Next をクリックします。

f:id:akanuma-hiroaki:20190119182215p:plain

 最後にメモリサイズと Role を選択して、 Next をクリックします。

f:id:akanuma-hiroaki:20190119182308p:plain

 これまでの設定内容の確認画面が表示されますので、間違いがなければ Finish をクリックします。

f:id:akanuma-hiroaki:20190119182424p:plain

 すると Lambda Function が作成され、 Cloud9 のエディタに表示されます。左側のディレクトリツリーにもファイルとして表示されます。

f:id:akanuma-hiroaki:20190119182935p:plain

 ということは EC2 の Linux 上にも存在するということなので、前回の記事でやったように、 vim でファイルを編集することも可能です。 vim で編集した内容はすぐに Cloud9 のエディタにも反映されます。 Linux 上で触れれば git でバージョン管理をすることも可能になりますので、継続的に開発していくにはかなり便利になりそうです。

$ pwd
/home/ec2-user/environment
$ ls -l SesSample/SesSample/
total 8
-rw-r--r-- 1 ec2-user ec2-user 75 Jan 19 07:53 lambda_function.py
-rw-r--r-- 1 ec2-user ec2-user 49 Jan 19 07:54 lambda-payloads.json

 Lambda のコンソールの方で確認してみると、そちらにも Function が作成されています。

f:id:akanuma-hiroaki:20190119183055p:plain

 内容も同様でブランクのテンプレートになっています。

f:id:akanuma-hiroaki:20190119183206p:plain

 Function のトリガーに API Gateway を選択したので、コンソールから確認すると API Gateway が作成されています。

f:id:akanuma-hiroaki:20190119183311p:plain

Lambda Function の実行

 それではひとまず Cloud9 から Lambda Function を動かしてみます。 Run ボタンをクリックします。

f:id:akanuma-hiroaki:20190119183515p:plain

 Function 実行のためのペインが開きますので、さらに Run をクリックします。

f:id:akanuma-hiroaki:20190119183609p:plain

 すると Function が実行され、下部に実行結果が表示されます。

f:id:akanuma-hiroaki:20190119183708p:plain

SMS 送信のサンプル

 今回はサンプルとして SMS 送信処理を実装してみます。内容は下記のようにシンプルなもので、 Amazon SNS から固定の宛先に SMS を送信します。

import boto3

PHONE_NUMBER = "080XXXXXXXX"
MESSAGE = "Test Message from Lambda"
REGION = "ap-southeast-1"

sns = boto3.client('sns')

def lambda_handler(event, context):
    sns.publish(PhoneNumber = PHONE_NUMBER, Message = MESSAGE)
    return 'Sent SMS'

 これを実行すると下記のように SMS が送信されます。

f:id:akanuma-hiroaki:20190119184045p:plain:w300

Lambda Function のデバッグ

 Cloud9 のエディタからは Lambda Function をデバッグすることもできます。行番号の前をクリックするとブレイクポイントとして赤いマークがつきますので、この状態で Run をクリックします。

f:id:akanuma-hiroaki:20190119184307p:plain

 実行用のペインでは今度はデバッグ用のボタンをクリックします。

f:id:akanuma-hiroaki:20190119184529p:plain

 すると関数の実行がブレイクポイントで停止し、右側ペインにコールスタックや変数の内容が表示されます。実行ボタンをクリックすると関数の実行が続行されます。

f:id:akanuma-hiroaki:20190119185417p:plain

Lambda Function のデプロイ

 Cloud9 から Lambda Function を作成した場合、作成時だけは同じ内容がリモートにも反映されますが、それ以降の変更内容は Cloud9 上にあるだけで、リモートには反映されていません。リモートにデプロイするには右側ペインで AWS Resources を選択し、ローカルで変更した Function を選択してデプロイボタンをクリックします。

f:id:akanuma-hiroaki:20190119190407p:plain

 すると Function がリモートにデプロイされ、 Lambda のコンソールからも内容が反映されていることが確認できます。

f:id:akanuma-hiroaki:20190119205513p:plain

 また、 Cloud9 からはリモートの Lambda Function を実行することも可能です。実行用のペインで Lambda (remote) を選択して Run をクリックします。

f:id:akanuma-hiroaki:20190119205828p:plain

 するとローカルの Function 実行時と同様に結果が下部に表示されます。今回はリモートの Function に SMS 送信の権限がないということでエラーになってしまいました。

f:id:akanuma-hiroaki:20190119210204p:plain

 Lambda のコンソール上で権限のある Role を割り当ててから再度実行すると、下記のように成功し、 SMS が送信されました。

f:id:akanuma-hiroaki:20190119210720p:plain

まとめ

 Lambda Function は Lambda のコンソールで実装すると普通のコードのようなバージョン管理ができないのが難点と感じていました。かといってバージョン管理できるように手元の端末上でコードを書いた場合はそのまま Function を実行することはできないので、毎回リモートに反映する必要があります。その点 Cloud9 は環境が EC2 の Linux 上にあり、書いたコードをそのまま実行することもバージョン管理することも可能なので、 Lambda の開発をかなり便利にしてくれそうに感じています。

 Cloud9 では Ruby ランタイムにはまだ対応していないようで、関数作成時のテンプレートにも Ruby のテンプレートは存在せず、リモートからのインポートの機能を使っても、 Python の Function はインポートできても Ruby の Function はインポートできませんでした。ここは今後の対応を待ちたいところです。

AWS Cloud9 で Rails 開発環境をつくる

 Cloud9 が AWS に買収され、AWS Cloud9 としてリリースされてからもう結構経ってしまいましたが、 Lambda 等のサーバレスなサービスを使う機会が増えてきたり、コンテナ等を活用するようになってきたので、クラウドの IDE が便利に活用できるようなら使ってみたいということで、今更ではありますが Cloud9 で Rails の開発環境を作ってみました。

環境の作成

 まずは AWS のマネジメントコンソールから Cloud9 を検索して Cloud9 のコンソールにアクセスします。

f:id:akanuma-hiroaki:20190112132133p:plain

 リリースからそれなりに経っているものの、まだ東京リージョンでは Cloud9 は提供されていないので、東京リージョンを選択していると下記のようにリージョンの選択画面が表示されます。今回はシンガポールリージョンを使用します。

f:id:akanuma-hiroaki:20190112132325p:plain

 Cloud9 のコンソールが表示されたら Create environment をクリックします。

f:id:akanuma-hiroaki:20190112132646p:plain

 環境の作成フォームで環境の名前を設定します。今回は rails-dev としました。説明はオプションなので設定しなくても問題ありません。入力したら Next step をクリックします。

f:id:akanuma-hiroaki:20190112132837p:plain

 次に環境の設定を行います。今回は全てデフォルト値をそのまま使います。

 Cloud9 では IDE の接続先を EC2 かその他のリモート環境に ssh で接続するかを選択できます。今回は EC2 を使用するので、デフォルトの Create a new instance for environment [EC2] を選択します。

 EC2 への接続を選択した場合は EC2 インスタンスが新たに作成されますが、そのインスタンスタイプも選択できます。デフォルトの t2.micro を使用します。

 また、 Cloud9 では使用していない時間が続くと EC2 が停止しますが、停止するまでの時間も選択できます。これも今回はデフォルトのまま、 After 30 minutes を使用します。

 Next step をクリックして次へ進みます。

f:id:akanuma-hiroaki:20190112133313p:plain

 設定内容の確認画面が表示されますので、内容に問題がなければ Create environment をクリックします。

f:id:akanuma-hiroaki:20190112133408p:plain

 環境が作成されるまでには少し時間がかかります。作成されるまでの間は下記のような画面が表示されます。

f:id:akanuma-hiroaki:20190112133526p:plain

 環境が作成されると下記のように IDE の画面が表示されます。作成されたインスタンスのホームディレクトリには environment というディレクトリが作成され、デフォルトの構成では、左ペインに environment ディレクトリ配下のファイルがツリー表示されます。右側の上部ペインにはエディタ、下部ペインにはターミナルが表示されます。

f:id:akanuma-hiroaki:20190112133925p:plain

 ファイルツリーからファイルをダブルクリックすると右側のエディタに表示されます。

f:id:akanuma-hiroaki:20190112134033p:plain

 右上の設定マーク(歯車マーク)等から設定画面を開くことができ、エディタ等の設定を変更することができますので、とりあえずいくつか設定してみます。

 まず Code Editor の設定で Soft Tabs をデフォルトの 4 から 2 に変更します。

f:id:akanuma-hiroaki:20190112134533p:plain

 私は普段エディタには Vim を使ってるので、 Keyboard Mode を Vim に変更します。

f:id:akanuma-hiroaki:20190112134729p:plain

 テーマもターミナルっぽい方が好きなので、 Cloud9 Night に変更してみます。

f:id:akanuma-hiroaki:20190112134959p:plain

 ターミナルのスタイルも変更できますので、背景色を黒、文字色を白に変更します。また、スクロールバックのバッファもデフォルトの 1,000 だと少ないので、 10,000 に変更しておきます。

f:id:akanuma-hiroaki:20190112135545p:plain

 設定変更後の画面は下記のような感じになります。

f:id:akanuma-hiroaki:20190112135633p:plain

 ターミナルで作業するときにはターミナルを全画面表示することもできます。

rvm のアンインストール

 ひとまず Cloud9 の環境としては作成されたので、 Rails の環境を作っていきます。まずは Ruby からですが、デフォルトでも Ruby は使えるようになっています。

$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]

 デフォルトでは Ruby の実行環境の管理には rvm が使われています。

$ which ruby
/usr/local/rvm/rubies/ruby-2.4.1/bin/ruby

 Rails も入っています。

$ which rails
/usr/local/rvm/rubies/ruby-2.4.1/bin/rails
$ rails -v
Rails 5.2.2

 これは好みですが、私は rvm より rbenv の方が好きなので、 rvm をアンインストールして、 rbenv をインストールしたいと思います。 rvm のアンインストールは rvm implode でできますが、 root 権限が必要なので、 sudo でフルパス指定で実行します。

$ sudo /usr/local/rvm/bin/rvm implode
Are you SURE you wish for rvm to implode?
This will recursively remove /usr/local/rvm and other rvm traces?
(anything other than 'yes' will cancel) > yes
Removing rvm-shipped binaries (rvm-prompt, rvm, rvm-sudo rvm-shell and rvm-auto-ruby)
Removing rvm wrappers in /usr/local/rvm/bin
Hai! Removing /usr/local/rvm
/usr/local/rvm has been removed.

Note you may need to manually remove /etc/rvmrc and ~/.rvmrc if they exist still.
Please check all .bashrc .bash_profile .profile and .zshrc for RVM source lines and delete or comment out if this was a Per-User installation.
Also make sure to remove `rvm` group if this was a system installation.
Finally it might help to relogin / restart if you want to have fresh environment (like for installing RVM again).

 これで rvm がアンインストールされ、 Ruby も使えなくなりました。

$ which ruby
/usr/bin/which: no ruby in (/home/ec2-user/.nvm/versions/node/v6.15.1/bin:/usr/local/bin:/bin:/usr/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin)

rbenv のインストール

 では rbenv をインストールします。と言っても特に Cloud9 固有なことはありません。 git はデフォルトで入っているので、 rbenv を git clone します。

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
Cloning into '/home/ec2-user/.rbenv'...
remote: Enumerating objects: 2744, done.
remote: Total 2744 (delta 0), reused 0 (delta 0), pack-reused 2744
Receiving objects: 100% (2744/2744), 523.32 KiB | 532.00 KiB/s, done.
Resolving deltas: 100% (1720/1720), done.

 そしてコンパイルします。

$ cd ~/.rbenv && src/configure && make -C src
make: Entering directory `/home/ec2-user/.rbenv/src'
gcc -fPIC     -c -o realpath.o realpath.c
gcc -shared -Wl,-soname,../libexec/rbenv-realpath.dylib  -o ../libexec/rbenv-realpath.dylib realpath.o 
make: Leaving directory `/home/ec2-user/.rbenv/src'

 起動時にパスが通るように .bash_profile に設定を追加します。

$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

 また、起動時に実行されるように設定を .bash_profile に追加します。

$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

 別ターミナルを起動して rbenv コマンドが使えるようになっていることを確認します。

$ rbenv -v
rbenv 1.1.1-39-g59785f6

 続けて ruby-build を git clone します。

$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
Cloning into '/home/ec2-user/.rbenv/plugins/ruby-build'...
remote: Enumerating objects: 29, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 9620 (delta 9), reused 22 (delta 7), pack-reused 9591
Receiving objects: 100% (9620/9620), 2.03 MiB | 1.40 MiB/s, done.
Resolving deltas: 100% (6275/6275), done.

 Ruby 2.6 は12月にリリースされたばかりで、まだ Rails のバージョンとの相性等は確認してませんが、今回はとりあえず細かいことは考えずに最新の 2.6 をインストールしてみます。

$ rbenv install 2.6.0
Downloading ruby-2.6.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.0.tar.bz2
Installing ruby-2.6.0...
Installed ruby-2.6.0 to /home/ec2-user/.rbenv/versions/2.6.0

 インストールしただけだと下記のようにエラーになってしまってまだ使えません。

$ ruby -v
rbenv: ruby: command not found

The `ruby' command exists in these Ruby versions:
  2.6.0

 なのでデフォルトのバージョンを 2.6 に設定します。

$ rbenv global 2.6.0
$ ruby -v
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-linux]

 これでひとまず Ruby が使える環境ができました。ついでに gem インストール時にドキュメントがインストールされないように .gemrc に設定を追加しておきます。

$ printf "install: --no-rdoc --no-ri\nupdate:  --no-rdoc --no-ri\n" >> ~/.gemrc

Rails インストール

 では最後に Rails をインストールします。

$ gem install rails
Fetching rack-test-1.1.0.gem
Fetching activesupport-5.2.2.gem
Fetching i18n-1.5.1.gem
Fetching tzinfo-1.2.5.gem
Fetching thread_safe-0.3.6.gem
Fetching rack-2.0.6.gem
Fetching concurrent-ruby-1.1.4.gem
Fetching loofah-2.2.3.gem
Fetching rails-html-sanitizer-1.0.4.gem
〜〜〜以下略〜〜〜

 これで最新の Rails が使えるようになりました。ちなみに gem も rails も rbenv でインストールされた現在のバージョンの Ruby 環境に紐づいているので、 rbenv で Ruby のバージョンを切り替えたときには改めて rails 等もインストールする必要があります。

$ rails -v
Rails 5.2.2
$ which rails
~/.rbenv/shims/rails

 試しに Rails アプリを作成してみます。

$ rails new hello_cloud9
      create  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  Gemfile
         run  git init from "."
Initialized empty Git repository in /home/ec2-user/environment/hello_cloud9/.git/
      create  package.json
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/javascripts/application.js
      create  app/assets/javascripts/cable.js
〜〜〜以下略〜〜〜

 問題なく作成されたらアプリを起動してみます。

$ cd hello_cloud9/
$ rails s
=> Booting Puma
=> Rails 5.2.2 application starting in development 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:8080
Use Ctrl-C to stop

 ローカルで開発しているときはブラウザを開いてアクセス確認をしますが、 Cloud9 の場合はコンソールからプレビュー用のメニューを選択します。

f:id:akanuma-hiroaki:20190112140019p:plain

 コンソール内にプレビュー用のペインが表示されますが、その中では表示されないので(原因はまだ調べてませんが)、ブラウザの別タブとして開くためのボタンをクリックします。

f:id:akanuma-hiroaki:20190112140444p:plain

 ブラウザの新しいタブで下記のように Rails のデフォルト画面が表示されれば成功です。

f:id:akanuma-hiroaki:20190112140535p:plain

まとめ

 今更ながらに Cloud9 で環境を作ってみましたが、思ったよりもかなり使いやすそうでした。エディタも用意されていますが、普段 Vim を使っているので、 Vim の環境を整えてターミナルの方を全画面表示してしまえば、ローカルで使っているのに近い感じで使えそうです。

f:id:akanuma-hiroaki:20190112141022p:plain

 クラウドの IDE はローカルの環境に依存せずに使えるのも良いですし、Lambda 等の AWS リソースとの連携もやりやすくなっているようなのでその辺りも今後試してみようと思います。

 利用料は Cloud9 自体は無料ですが EC2 の利用料金はかかるので、実際にどれぐらいになるかも確認してみたいと思います。

公式チュートリアルで Kubenetes 入門(後編)

 前回の記事で Kubernetes の公式チュートリアル Kubernetes Basics の前半3 Modulesを進めてみました。

blog.akanumahiroaki.com

 今回は後半の 4 〜 6 Modules を進めてみます。

Services and Labels(Module 4)

 Service は Pod とそれらにアクセスするためのポリシーの論理的なセットです。 Service が対象とする Pod は LabelSelector によって定義されます。

 Pod はユニークな IP アドレスを保持していますが、 Service なしではその IP アドレスはクラスタの外へ公開されることはありません。

 Service は ServiceSpec で指定した type によって異なる方法で公開されます。

  • ClusterIP(デフォルト):クラスタ内の internal IP 上に Service を公開します。クラスタ内からのみ到達可能です。

  • NodePort:NAT を使用して Node 上のポートに Service を公開します。外部からは Node の IP とポートを指定することでアクセス可能です。 ClusterIP の Superset です。

  • LoadBalancer:使用しているクラウド環境でサポートされていれば、外部ロードバランサーを作成し、 Service に外部 IP アドレスをアサインします。 NodePort の Superset です。

  • ExternalName:任意の名称を使用して、 CNAME レコードでその名称を返すことで Service を公開します。プロキシは使用されません。このタイプを使用するには v1.7 以上の kube-dns が必要です。

 Selector なしで作成された Service はエンドポイントも作成しません。これによってマニュアルで Service を特定のエンドポイントにマップすることも可能になります。

 Service は Pod のセットを横断してトラフィックをルーティングします。

 Label はオブジェクトにアタッチされたキーバリューペアで、様々なケースで使用可能です。

 まず Pod の状態を確認してみます。

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-dg96d   1/1       Running   0          31s

 Service の一覧を確認してみます。

$ kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   59s

 kubernetes という名前の Service が Minikube でのクラスタ作成時にデフォルトで作成されています。新しい Service を作成して公開するには、 kubectl の expose コマンドを使用します。 type には NodePort を指定します。(Minikube ではまだ LoadBalancer オプションがサポートされていません)

$ kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service/kubernetes-bootcamp exposed

 作成された Service を確認してみます。

$ kubectl get services
NAME                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1      <none>        443/TCP          3m
kubernetes-bootcamp   NodePort    10.108.91.62   <none>        8080:31092/TCP   31s

 kubernetes-bootcamp という名前の Service が作成されていて、 CLUSTER-IP や EXTERNAL-IP、 PORT が確認できます。

 どのポートが外部に公開されているか確認するには、 describe service コマンドを使用します。

$ kubectl describe services/kubernetes-bootcamp
Name:                     kubernetes-bootcamp
Namespace:                default
Labels:                   run=kubernetes-bootcamp
Annotations:              <none>
Selector:                 run=kubernetes-bootcamp
Type:                     NodePort
IP:                       10.108.91.62
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31092/TCP
Endpoints:                172.18.0.2:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

 API エンドポイントにアクセスして動作を確認するためにポート番号を取得して環境変数に格納します。

$ export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
$ echo NODE_PORT=$NODE_PORT
NODE_PORT=31092

 curl で外部にアプリケーションが公開されているかを確認してみます。

$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-dg96d | v=1

 Deployment 作成時には Pod に対して自動的に Label が作成されます。 describe deployment コマンドで Label を確認することができます。

$ kubectl describe deployment
Name:                   kubernetes-bootcamp
Namespace:              default
CreationTimestamp:      Sat, 29 Dec 2018 11:21:24 +0000
Labels:                 run=kubernetes-bootcamp
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=kubernetes-bootcamp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  run=kubernetes-bootcamp
  Containers:
   kubernetes-bootcamp:
    Image:        gcr.io/google-samples/kubernetes-bootcamp:v1
    Port:         8080/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   kubernetes-bootcamp-5c69669756 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  9m    deployment-controller  Scaled up replica set kubernetes-bootcamp-5c69669756 to 1

 Pod のリストを取得するコマンドに Label を使用してみます。 kubectl get pods コマンドに -l オプションで Label を指定します。

$ kubectl get pods -l run=kubernetes-bootcamp
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-dg96d   1/1       Running   0          11m

 Service についても同様に実行可能です。

$ kubectl get services -l run=kubernetes-bootcamp
NAME                  TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes-bootcamp   NodePort   10.108.91.62   <none>        8080:31092/TCP   9m

 Pod 名を使用するために環境変数に格納します。

$ export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-5c69669756-dg96d

 新しい Label を適用するには label コマンドをオブジェクトタイプ、オブジェクト名、新しい Label と共に使用します。

$ kubectl label pod $POD_NAME app=v1
pod/kubernetes-bootcamp-5c69669756-dg96d labeled

 適用された Label を確認してみます。

$ kubectl describe pods $POD_NAME
Name:           kubernetes-bootcamp-5c69669756-dg96d
Namespace:      default
Node:           minikube/172.17.0.54
Start Time:     Sat, 29 Dec 2018 11:21:31 +0000
Labels:         app=v1
                pod-template-hash=1725225312
                run=kubernetes-bootcamp
〜〜〜以下略〜〜〜

 新しい Label で Pod のリストを取得することも可能です。

$ kubectl get pods -l app=v1
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-dg96d   1/1       Running   0          16m

 Service を削除するには delete service コマンドを使用します。ここでも Label を使用することができます。

$ kubectl delete service -l run=kubernetes-bootcamp
service "kubernetes-bootcamp" deleted

 Service が削除されたことを確認します。

$ kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   18m

 外部へ公開されなくなったことを確認します。

$ curl $(minikube ip):$NODE_PORT
curl: (7) Failed to connect to 172.17.0.54 port 31092: Connection refused

 Pod の中ではアプリケーションが稼働していることを確認します。

$ kubectl exec -ti $POD_NAME curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-dg96d | v=1

Scaling an application(Module 5)

 Scaling は Deployment 内のレプリカ数を変更することで達成されます。

 Service は統合されたロードバランサーを持っていて、ネットワークトラフィックを全ての Pod に配分します。また、Service はエンドポイントを使用して継続的に監視を続け、使用可能な Pod にのみトラフィックが送られるようにします。

 複数のインスタンスでアプリケーションを稼働させていれば、ダウンタイムなしでのローリングアップデートが可能です。

 まずは現在の Deployment の状態を確認してみます。

$ kubectl get deployment
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1         1         1            1           12s

 各 state の内容は下記の通りです。

  • DESIRED: 設定したレプリカ数

  • CURRENT:現在稼働しているレプリカ数

  • UP-TO-DATE:設定にマッチするようにアップデートされたレプリカ数

  • AVAILABLE:ユーザーに対して有効なレプリカ数

 Deployment のレプリカ数を 4 に設定してみます。 kubectl scale コマンドで Deployment Type、Deployment Name、レプリカ数を指定します。

$ kubectl scale deployments/kubernetes-bootcamp --replicas=4
deployment.extensions/kubernetes-bootcamp scaled

 Deployment の状態を確認します。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4         4         4            4           3m

 変更が適用されて、4インスタンスが有効になっていることがわかります。 Pod も確認してみます。

$ kubectl get pods -o wide
NAME                                   READY     STATUS    RESTARTS   AGE       IP     NODE
kubernetes-bootcamp-5c69669756-nkddq   1/1       Running   0          3m        172.18.0.2   minikube
kubernetes-bootcamp-5c69669756-sddhp   1/1       Running   0          1m        172.18.0.7   minikube
kubernetes-bootcamp-5c69669756-xvv29   1/1       Running   0          1m        172.18.0.6   minikube
kubernetes-bootcamp-5c69669756-zsvnd   1/1       Running   0          1m        172.18.0.5   minikube

 4つの Pod が異なる IP アドレスで稼働していることがわかります。この変更は Deployment のイベントログに記録されます。 describe コマンドで確認してみます。

$ kubectl describe deployments/kubernetes-bootcamp
〜〜〜中略〜〜〜
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  5m    deployment-controller  Scaled up replica set kubernetes-bootcamp-5c69669756 to 1
  Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set kubernetes-bootcamp-5c69669756 to 4

 Service がトラフィックを分散させていることを確認します。公開されている IP と Port を確認するには describe service コマンドを使用します。

$ kubectl describe services/kubernetes-bootcamp
Name:                     kubernetes-bootcamp
Namespace:                default
Labels:                   run=kubernetes-bootcamp
Annotations:              <none>
Selector:                 run=kubernetes-bootcamp
Type:                     NodePort
IP:                       10.101.88.200
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  30677/TCP
Endpoints:                172.18.0.2:8080,172.18.0.5:8080,172.18.0.6:8080 + 1 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

 ポート番号を環境変数に格納します。

$ export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
$ echo NODE_PORT=$NODE_PORT
NODE_PORT=30677

 curl で複数回エンドポイントにアクセスしてみます。

$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-xvv29 | v=1
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-nkddq | v=1
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-sddhp | v=1
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-xvv29 | v=1

 アクセスする度に違う Pod にアクセスしているので、ロードバランシングが機能している事がわかります。

 レプリカ数を 2 にダウンするには再度 scale コマンドを使用します。

$ kubectl scale deployments/kubernetes-bootcamp --replicas=2
deployment.extensions/kubernetes-bootcamp scaled

 Deployment の設定が変更されたことを確認します。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   2         2         2            2           10m

 Pod の状態も確認しておきます。

$ kubectl get pods -o wide
NAME                                   READY     STATUS    RESTARTS   AGE       IP     NODE
kubernetes-bootcamp-5c69669756-nkddq   1/1       Running   0          11m       172.18.0.2   minikube
kubernetes-bootcamp-5c69669756-xvv29   1/1       Running   0          8m        172.18.0.6   minikube

Updating an application(Module 6)

 ローリングアップデートではインクリメンタルに Pod のインスタンスを新しいものにアップデートしていくことでダウンタイムゼロでのデプロイを可能にします。

 Kubernetes ではアップデートはバージョン管理され、後で以前のバージョンに切り戻す事が可能です。

 アップデート中は Service によって有効な Pod にのみロードバランスされます。

 まずは Deployment と Pod の状態を確認しておきます。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4         4         4            0           8s
$
$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-2tl2v   1/1       Running   0          17s
kubernetes-bootcamp-5c69669756-4khpv   1/1       Running   0          17s
kubernetes-bootcamp-5c69669756-d5t5l   1/1       Running   0          17s
kubernetes-bootcamp-5c69669756-vmm2h   1/1       Running   0          17s

 現在のアプリケーションのバージョンを確認するには describe pods コマンドを使用し、 Image フィールドを確認します。

$ kubectl describe pods
Name:           kubernetes-bootcamp-5c69669756-2tl2v
Namespace:      default
Node:           minikube/172.17.0.27
Start Time:     Sat, 29 Dec 2018 12:10:42 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.2
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://2c2beb5e1f0cc006400c48838e0cb42d41d0a8b86c69e0688de54ac5e6589f2d
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
〜〜〜中略〜〜〜
Name:           kubernetes-bootcamp-5c69669756-4khpv
Namespace:      default
Node:           minikube/172.17.0.27
Start Time:     Sat, 29 Dec 2018 12:10:42 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.5
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://5bee3f08bfda17166d4692cf58ab626c8e1f9260fa28ab09537427615a84d053
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
〜〜〜中略〜〜〜
Name:           kubernetes-bootcamp-5c69669756-d5t5l
Namespace:      default
Node:           minikube/172.17.0.27
Start Time:     Sat, 29 Dec 2018 12:10:42 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.3
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://3251469b06d51131b426bfb5a50fd67b0e2c66b6d0a4dc54a56aa6f5e307dc63
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
〜〜〜中略〜〜〜
Name:           kubernetes-bootcamp-5c69669756-vmm2h
Namespace:      default
Node:           minikube/172.17.0.27
Start Time:     Sat, 29 Dec 2018 12:10:42 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.4
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://5b05a470deddcd0b461430ad441aaa3bcb9e6ffe58062365451fc9404568e24a
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
〜〜〜以下略〜〜〜

 現在 v1 になっているアプリケーションのバージョンを v2 にアップデートするには、 set image コマンドを使用して、 Deployment Name と新しいイメージのバージョンを指定します。

$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
deployment.extensions/kubernetes-bootcamp image updated

 このコマンドによって Deployment に新しいイメージを使うことを通知し、ローリングアップデートを開始します。新しい Pod のステータスや古い Pod の停止を確認するには get pods コマンドを使用します。

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-7799cbcb86-4b57t   1/1       Running   0          46s
kubernetes-bootcamp-7799cbcb86-9dp7j   1/1       Running   0          47s
kubernetes-bootcamp-7799cbcb86-9jvwg   1/1       Running   0          46s
kubernetes-bootcamp-7799cbcb86-t9qxj   1/1       Running   0          47s

 アプリケーションの稼働を確認してみます。公開されている IP と Port を確認するには describe service コマンドを使用します。

$ kubectl describe services/kubernetes-bootcamp
Name:                     kubernetes-bootcamp
Namespace:                default
Labels:                   run=kubernetes-bootcamp
Annotations:              <none>
Selector:                 run=kubernetes-bootcamp
Type:                     NodePort
IP:                       10.103.83.106
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  30046/TCP
Endpoints:                172.18.0.10:8080,172.18.0.11:8080,172.18.0.8:8080 + 1 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

 ポート番号を環境変数に格納しておきます。

$ export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
$ echo NODE_PORT=$NODE_PORT
NODE_PORT=30046

 curl でエンドポイントにアクセスしてみます。

$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-9dp7j | v=2
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-4b57t | v=2
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-4b57t | v=2
$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7799cbcb86-9jvwg | v=2

 アクセスごとに違う Pod にアクセスしていて、バージョンも v2 になっている事がわかります。

 アップデートのステータスは rollout status コマンドでも確認できます。

$ kubectl rollout status deployments/kubernetes-bootcamp
deployment "kubernetes-bootcamp" successfully rolled out

 describe pods でイメージのバージョンも確認してみます。

$ kubectl describe pods | grep Image
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5

 それでは次に別のアップデートとして、 v10 というイメージをデプロイしてみます。

$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10
deployment.extensions/kubernetes-bootcamp image updated

 Deployment の状態を確認してみます。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4         5         2            3           13m

 今度は AVAILABLE が期待した通りになっていません。 Pod のリストを確認してみます。

$ kubectl get pods
NAME                                   READY     STATUS             RESTARTS   AGE
kubernetes-bootcamp-5f76cd7b94-tgshj   0/1       ImagePullBackOff   0          1m
kubernetes-bootcamp-5f76cd7b94-zvkd6   0/1       ImagePullBackOff   0          1m
kubernetes-bootcamp-7799cbcb86-4b57t   1/1       Running            0          8m
kubernetes-bootcamp-7799cbcb86-9dp7j   1/1       Running            0          8m
kubernetes-bootcamp-7799cbcb86-t9qxj   1/1       Running            0          8m

 上の二つの Pod の状態が ImagePullBackOff になっていて、 READY が 0/1 になっています。

 さらに調べるために describe コマンドを使用します。

$ kubectl describe pods
Name:           kubernetes-bootcamp-5f76cd7b94-tgshj
Namespace:      default
Node:           minikube/172.17.0.27
Start Time:     Sat, 29 Dec 2018 12:23:17 +0000
Labels:         pod-template-hash=1932783650
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Pending
IP:             172.18.0.3
Controlled By:  ReplicaSet/kubernetes-bootcamp-5f76cd7b94
Containers:
  kubernetes-bootcamp:
    Container ID:
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v10
    Image ID:
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       ImagePullBackOff
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-qcw86 (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          False
  PodScheduled   True
Volumes:
  default-token-qcw86:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-qcw86
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason                 Age               From               Message
  ----     ------                 ----              ----               -------
  Normal   Scheduled              2m                default-scheduler  Successfully assigned kubernetes-bootcamp-5f76cd7b94-tgshj to minikube
  Normal   SuccessfulMountVolume  2m                kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-qcw86"
  Normal   Pulling                49s (x4 over 2m)  kubelet, minikube  pulling image "gcr.io/google-samples/kubernetes-bootcamp:v10"
  Warning  Failed                 48s (x4 over 2m)  kubelet, minikube  Failed to pull image "gcr.io/google-samples/kubernetes-bootcamp:v10": rpc error: code = Unknown desc = unauthorized: authentication required
  Warning  Failed                 48s (x4 over 2m)  kubelet, minikube  Error: ErrImagePull
  Normal   BackOff                23s (x6 over 2m)  kubelet, minikube  Back-off pullingimage "gcr.io/google-samples/kubernetes-bootcamp:v10"
  Warning  Failed                 23s (x6 over 2m)  kubelet, minikube  Error: ImagePullBackOff
〜〜〜以下略〜〜〜

 イベントログを見ると、 v10 というイメージがなくてエラーになっているという事がわかります。以前のバージョンにロールバックしてみたいと思います。ロールバックするには rollout undo コマンドを使用します。

$ kubectl rollout undo deployments/kubernetes-bootcamp
deployment.extensions/kubernetes-bootcamp

 Pod の状態を確認してみます。

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-7799cbcb86-4b57t   1/1       Running   0          12m
kubernetes-bootcamp-7799cbcb86-9dp7j   1/1       Running   0          13m
kubernetes-bootcamp-7799cbcb86-jv7k7   1/1       Running   0          48s
kubernetes-bootcamp-7799cbcb86-t9qxj   1/1       Running   0          13m

 4つの Pod が全て Running になっている事が確認できました。 Image も確認してみます。

$ kubectl describe pods | grep Image
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5
    Image:          jocatalin/kubernetes-bootcamp:v2
    Image ID:       docker-pullable://jocatalin/kubernetes-bootcamp@sha256:fb1a3ced00cecfc1f83f18ab5cd14199e30adc1b49aa4244f5d65ad3f5feb2a5

 全て v2 のイメージが使われています。

まとめ

 後編ではスケーリングやローリングアップデートといった内容も試す事ができ、 Kubernetes を使った運用のイメージをつかむ事ができました。まだ用意された仮想環境で想定された手順を踏んでいっただけなので、今後は自分のローカル環境やクラウド環境で動かせるように挑戦してみたいと思います。チュートリアルも Kubernetes Basics 以外にも色々あるので、試してみたいと思います。

公式チュートリアルで Kubenetes 入門(前編)

 今まではなんとなくの知識だけで、自分で触ってみたことはなかったので、今更ですが公式チュートリアルで Kubernetes に入門してみました。

Tutorials - Kubernetes

 チュートリアルの中にも色々あるのですが、今回は一番基本的な Kubernetes Basics をやってみます。

kubernetes.io

 チュートリアルはブラウザ上で仮想ターミナルからコマンドを実行できる Katacoda の環境の上で提供されています。

www.katacoda.com

 また、 Katacoda の上で Minikube という簡単にローカルに Kubernetes 環境を構築するツールが使われています。

github.com

 Kubernetes Basics は6つの Modules に分かれているので、一つずつ進めてみます。分量が多くなってしまったので、今回は Module 1 から 3 までを進めます。

Kubernetes Clusters(Module 1)

 まずは Kubernetes Clusters についてです。 Kubernetes は可用性の高いクラスタ環境を管理し、アプリケーションコンテナの管理を自動化します。

 クラスタの中には Master と Node が存在し、 Master がクラスタを管理します。 Node は実際にサービスを提供するVMもしくは物理コンピュータになります。

 また、 Node が Master と連携するために Kubelet というエージェントが存在します。

 さらに Node はコンテナのオペレーションをハンドリングするために、 Docker または rkt を使用します。

 Kubenetes Cluster ではトラフィックを処理するためには最低3つの Node を保持する必要があります。

 では実際にチュートリアルを進めてみます。チュートリアルは Katacoda 環境でコマンドを実行しながら進めることができます。

f:id:akanuma-hiroaki:20181229171152p:plain

 まずは Minikube のバージョンを確認します。

$ minikube version
minikube version: v0.28.2

 そして Minikube を実行します。

$ minikube start
Starting local Kubernetes v1.10.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.

 Minikube によって Kubernetes 環境が構築されました。

 Kubernetes の操作には CLI の kubectl を使用します。まずはバージョンを確認してみます。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.0", GitCommit:"91e7b4fd31fcd3d5f436da26c980becec37ceefe", GitTreeState:"clean", BuildDate:"2018-06-27T20:17:28Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.0", GitCommit:"fc32d2f3698e36b93322a3465f63a14e9f0eaead", GitTreeState:"clean", BuildDate:"2018-04-10T12:46:31Z", GoVersion:"go1.9.4", Compiler:"gc", Platform:"linux/amd64"}

 Client Version は kubectl のバージョンで、 ServerVersion は Master にインストールされている Kubernetes のバージョンになります。

 次にクラスタの情報を確認してみます。

$ kubectl cluster-info
Kubernetes master is running at https://172.17.0.48:8443

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

 チュートリアル中は CLI のみで進めますが、表示された URL にアクセスすると、クラスタの情報が確認できるダッシュボードを表示することができます。

 続いて Node を確認してみます。

$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     <none>    7m        v1.10.0

 現在は Node は一つだけで、 STATUS が Ready になっていて、アプリケーションのデプロイが可能な状態になっています。

Kubernetes Deployments(Module 2)

 Kubernetes Cluster を稼働させていれば、その上にコンテナ化されたアプリケーションをデプロイすることができます。そのためには Kubernetes Deployment の設定を作成します。

 もし Node がダウンしたり削除されたりすると、 Deployment コントローラはそれをリプレイスします。これによってメンテナンス時や障害時の自律回復機構を提供します。

 Deployment の管理は kubectl で行うことができます。 Deployment を作成するときは、アプリケーションのコンテナイメージと、稼働させるレプリカの数を指定します。このチュートリアルでは Docker コンテナにパッケージされた Node.js アプリケーションが使われます。

 チュートリアルを開始すると下記のように自動的にシェルが実行され、クラスタが起動します。

$ sleep 1; launch.sh
Starting Kubernetes...
Kubernetes Started

 Node を確認すると下記のように一つだけ稼働していることがわかります。

$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     <none>    2m        v1.10.0

 新しい Deployment を作成するには、 kubectl run コマンドを使用します。実行時には Deployment名とコンテナイメージのロケーションを指定する必要があります。また、特定のポートでアプリを実行するためには --port オプションを使用します。

$ kubectl run kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --port=8080
deployment.apps/kubernetes-bootcamp created

 Deployment を確認してみます。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1         1         1            1           30m

 Deployment が一つあり、一つのインスタンスで稼働していることがわかります。このインスタンスは Node の Docker コンテナ上で稼働しています。

 Kubernetes 内で稼働している Pod はプライベートで独立したネットワーク内で稼働しています。デフォルトでは同じクラスタ内からのみアクセス可能です。 kubectl 使用時には API エンドポイントを通して連携が行われています。また、 kubectl コマンドでクラスタ内のプライベートネットワーク内でのアクセスを転送するプロキシを作成することができます。作業用のターミナルとは別ターミナルで下記のコマンドを実行します。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

 これでオンラインターミナルが稼働しているホストと Kubernetes Cluster 間でのコネクションが作成されました。プロキシによって API にターミナルからダイレクトにアクセスできるようになっています。

$ curl http://localhost:8001/version
{
  "major": "1",
  "minor": "10",
  "gitVersion": "v1.10.0",
  "gitCommit": "fc32d2f3698e36b93322a3465f63a14e9f0eaead",
  "gitTreeState": "clean",
  "buildDate": "2018-04-10T12:46:31Z",
  "goVersion": "go1.9.4",
  "compiler": "gc",
  "platform": "linux/amd64"
}

 API サーバは自動的に Pod名をベースにしたエンドポイントを作成します。プロキシを通してそのエンドポイントにもアクセスすることができます。そのためには Pod名が必要なので、 Pod名を取得して環境変数に保持します。

$ export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-5c69669756-288s7

 この Pod名を使ってエンドポイントにアクセスしてみます。 Pod の API の URL は下記のようになります。

$ curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-288s7 | v=1

Kubernetes Pods And Nodes(Module 3)

 Pod は一つまたはそれ以上のアプリケーションコンテナ(Docker または rkt)を表していて、いくつかのリソースをコンテナ間で共有します。リソースには下記のようなものが含まれます。

  • 共有ストレージ

  • ネットワーク(ユニークなクラスタIP)

  • コンテナイメージのバージョンや使用するポートなどの情報

 Pod 内のコンテナは IP アドレスとポートの空間を共有します。

 Pod は Kubernetes プラットフォームでの最小ユニットになります。

 また、 Pod は常に Node 上で稼働します。

 全ての Node では少なくとも Kubelet(Pod とコンテナを管理し、Master と Node の連携を行う)と、コンテナのランタイム(Docker や rkt など)が稼働します。

 現在の Pod を確認するには kubectl の get コマンドを使用します。

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-sv4wc   1/1       Running   0          1m

 Pod 内のコンテナや、そのコンテナで使われているイメージを確認するには、 kubectl describe pods コマンドを使用します。このコマンドによって Pod の IP アドレスやポート、ライフサイクルイベントなども確認することができます。

$ kubectl describe pods
Name:           kubernetes-bootcamp-5c69669756-sv4wc
Namespace:      default
Node:           minikube/172.17.0.4
Start Time:     Sat, 29 Dec 2018 10:40:18 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.2
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://051074e6dc2c7b6fb75c92ace46fad77838d4392b7270a7b2fa9dad372a9086d
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 29 Dec 2018 10:40:18 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-l6t4t (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  default-token-l6t4t:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-l6t4t
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason                 Age              From               Message
  ----     ------                 ----             ----               -------
  Warning  FailedScheduling       4m (x4 over 4m)  default-scheduler  0/1 nodes are available: 1 node(s) were not ready.
  Normal   Scheduled              4m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-sv4wc to minikube
  Normal   SuccessfulMountVolume  4m               kubelet, minikube  MountVolume.SetUpsucceeded for volume "default-token-l6t4t"
  Normal   Pulled                 4m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machine
  Normal   Created                4m               kubelet, minikube  Created container
  Normal   Started                4m               kubelet, minikube  Started container

 プロキシ経由で Pod 関連の操作を行うために再度別ターミナルでプロキシを実行します。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

 Pod 名を環境変数に格納します。

$ export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-5c69669756-sv4wc

 curl コマンドでアプリケーションのアウトプットを確認します。

$ curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-sv4wc | v=1

 アプリケーションが標準出力に送ったものは Pod 内のコンテナのログになります。そのログは kubectl logs コマンドで確認することができます。 Pod 内にコンテナが一つの場合はコンテナ名を指定する必要はありません。

$ kubectl logs $POD_NAME
Kubernetes Bootcamp App Started At: 2018-12-29T10:40:18.825Z | Running On:  kubernetes-bootcamp-5c69669756-sv4wc

Running On: kubernetes-bootcamp-5c69669756-sv4wc | Total Requests: 1 | App Uptime: 725.334 seconds | Log Time: 2018-12-29T10:52:24.159Z

 Pod が稼働していれば、コンテナ上で直接コマンドを実行することができます。そのためには exec コマンドと Pod 名を使用します。環境変数のリストを表示する例は下記の通りです。

$ kubectl exec $POD_NAME env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubernetes-bootcamp-5c69669756-sv4wc
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
NPM_CONFIG_LOGLEVEL=info
NODE_VERSION=6.3.1
HOME=/root

 bash のセッションを開始することもできます。

$ kubectl exec -ti $POD_NAME bash
root@kubernetes-bootcamp-5c69669756-sv4wc:/#

 セッション開始後はそのままコマンドが実行できます。

root@kubernetes-bootcamp-5c69669756-sv4wc:/# ls
bin   core  etc   lib    media  opt   root  sbin       srv  tmp  var
boot  dev   home  lib64  mnt    proc  run   server.js  sys  usr

 NodeJS コンテナ内からの実行になるので、 curl も localhost 指定で実行可能です。

root@kubernetes-bootcamp-5c69669756-sv4wc:/# curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-sv4wc | v=1

まとめ

 仮想環境が提供されているので、すぐにチュートリアルが始められるのはハードルが低くて良いですね。今回は前半まででしたが、クラスタ環境の構成まではイメージすることができました。後編ではスケーリングやローリングアップデートも扱われているので、サクッとやってみたいと思います。

SORACOM Krypton で AWS IoT にデバイスを登録する

 SORACOM Technology Camp 2018 で関連するセッションに参加してから気になっていたものの触れていなかった SORACOM Krypton を触ってみました。公式のチュートリアルを参考に、 AWS IoT へのデバイス登録を試してみます。

dev.soracom.io

SORACOM Krypton とは

 詳細な説明は公式ページ(SORACOM Krypton とは)をご覧いただければと思いますが、すごく簡単に言うと、 IoT デバイスのクラウドサービスへの接続設定をセキュアに行うためのサービスです。

 IoT デバイスをクラウドサービスへ接続するには認証情報が必要になりますが、デバイス上に認証情報を配置するには出荷時に組み込んでおくか、あとで個別にデバイス上に配置していく必要がありますが、いずれもかなり手間がかかりますし、配置する認証情報とデバイスの組み合わせを間違う可能性もあります。SORACOM Krypto を使用すると、 SORACOM Air のセルラー回線の情報を使った認証か、SIM の情報を使って SORACOM Endose での認証を行い、認証情報を自動的に発行してデバイスを登録することができますので、初期設定の手間をかなり減らすことができます。

AWS IoT ポリシー設定

 まずは登録するデバイスに付与する権限を設定するため、 AWS IoT のポリシーを作成します。 AWS IoT Core の 安全性 -> ポリシー の画面から 作成 をクリックします。

f:id:akanuma-hiroaki:20181222201556p:plain

 ポリシーの作成フォームが表示されますので、任意の名前を設定します。アクションとしては、今回はとりあえず AWS IoT のフル権限を与えておくために、 iot:* を設定します。リソース ARN も任意に設定し、効果は 許可 を選択して 作成 をクリックすると AWS IoT ポリシーが作成されます。

f:id:akanuma-hiroaki:20181222201638p:plain

AWS ユーザ設定

 次にデバイス登録時に証明書を発行する権限を持つ IAM ユーザを作成します。IAM のユーザーメニューで ユーザーを追加 をクリックします。

f:id:akanuma-hiroaki:20181222202207p:plain

 ユーザー名は任意に設定し、アクセスの種類は プログラムによるアクセス を選択して 次のステップ: アクセス権限 をクリックします。

f:id:akanuma-hiroaki:20181222202247p:plain

 アクセス許可の設定として、 既存のポリシーを直接アタッチ を選択し、「CreateKeysAndCertificate」ポリシーを付与します。

f:id:akanuma-hiroaki:20181222202322p:plain

 ポリシーを設定したら ユーザーの作成 をクリックしてユーザーを作成します。

f:id:akanuma-hiroaki:20181222202444p:plain

 ユーザーが作成されたら、あとで SORACOM 側の設定時に必要になるので、 csv ファイルで認証情報をダウンロードしておくか、アクセスキー ID とシークレットアクセスキーをメモしておきます。

f:id:akanuma-hiroaki:20181222213222p:plain

 ただ実際にこの権限だけで試してみると、あとで登録を実行しようとすると、下記のように権限が足りないと言うことでエラーになります。

$ curl -X POST -H 'content-type: application/json' https://krypton.soracom.io:8036/v1/provisioning/aws/iot/bootstrap
{"code":"DIC0001","message":"Error occurred: User: arn:aws:iam::365361468908:user/krypton-aws-iot-provisioner is not authorized to perform: iot:CreateKeysAndCertificate on resource: * (Service: AWSIot; Status Code: 403; Error Code: Access
DeniedException; Request ID: e5908284-05a0-11e9-a0d6-5f7d4bdeb37f)"}

 なので今回はとりあえずインラインポリシーで「iot:CreateKeysAndCertificate」を追加しました。

f:id:akanuma-hiroaki:20181222205329p:plain

 最終的に下記二つのポリシーを設定しています。

f:id:akanuma-hiroaki:20181222205458p:plain

SORACOM Krypton セットアップ

 ここまでで AWS 側の設定は完了なので、続いて SORACOM User Console での設定を行います。

 ユーザーアカウントのメニューから、 セキュリティ をクリックします。

f:id:akanuma-hiroaki:20181222203011p:plain

 認証情報ストアメニューから 認証情報を登録 をクリックします。

f:id:akanuma-hiroaki:20181222203402p:plain

 登録フォームが表示されたら、認証情報 ID と概要に任意の内容を設定します。種別は AWS 認証情報を選択して、先ほど作成した IAM ユーザのアクセスキー ID とシークレットアクセスキーを設定して 登録 をクリックします。

f:id:akanuma-hiroaki:20181222203524p:plain

 次に Krypton を使用するための SIM のグループを作成します。 SIM グループの画面から 追加 をクリックします。

f:id:akanuma-hiroaki:20181222204030p:plain

 登録フォームが表示されたら任意のグループ名を設定して グループ作成 をクリックします。

f:id:akanuma-hiroaki:20181222204230p:plain

 グループが作成されたら、グループ設定の「SORACOM Krypton 設定」の項目の設定を ON にして、 + をクリックして AWS IoT を選択します。

f:id:akanuma-hiroaki:20181222204525p:plain

 設定フォームが表示されますので、 AWS リージョンには AWS IoT の設定を行ったリージョンを設定し、認証情報は先ほど SORACOM User Console から登録した認証情報を選択します。 Policy name は AWS IoT で登録したポリシー名を設定します。 Thing name pattern には Krypton 経由で AWS IoT のデバイスを登録するときのネーミングのパターンを指定します。 $imsi は実際の IMSI に置き換えられます。ホスト名はアカウント固有の AWS IoT エンドポイントです。ルート認証局証明書はブランクで問題ありません。以上の設定ができたら OK をクリックします。

f:id:akanuma-hiroaki:20181222204701p:plain

 上記の設定内容については公式の下記ページの一番下にも説明があります。

dev.soracom.io

 グループの設定はここまでで完了なので、このグループに SIM を紐付けます。 SIM の管理画面で対象の SIM のチェックボックスにチェックを入れて、 操作 メニューから 所属グループ変更 を選択します。

f:id:akanuma-hiroaki:20181222204944p:plain

 変更フォームが表示されたら、先ほど作成したグループを選択して グループ変更 をクリックします。

f:id:akanuma-hiroaki:20181222205214p:plain

Raspberry Pi から登録確認

 ここまでで全ての設定は完了なので、実際の登録を行ってみたいと思います。今回は Rapberry Pi 3 Model B を USB ドングル + SORACOM Air でセルラー回線に接続し、 curl で Krypton の API にアクセスします。すると認証情報が作成され、下記のように JSON 形式でレスポンスを受け取ることができます。

$ curl -X POST -H 'content-type: application/json' https://krypton.soracom.io:8036/v1/provisioning/aws/iot/bootstrap | jq                                                                                                    
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4945  100  4945    0     0    658      0  0:00:07  0:00:07 --:--:--  1216
{
  "region": "ap-northeast-1",
  "certificate": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIUYrFCx+WJLTtlZNHykz2jt2B2y8swDQYJKoZIhvcNAQEL\nBQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\nSW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE4MTIyMjA0Mzg0\nNFoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0\nZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMiKSq8YxvnVHyTVRApZ\nundMbttTH9iP87NiwbKKQWPwFyRQGUIhaLxamIcjRV9FBwdj5F4eTBtGdKjSrjbG\nS0DlLoyL4rkNKRoVYpn8Rnr2Akt9LUmzf8ckQMZEhn04U5Ac8Q8YlkPvY+r4Lfb1\n〜〜〜中略〜〜〜-----END CERTIFICATE-----\n",
  "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAyIpKrxjG+dUfJNVEClm6d0xu21Mf2I/zs2LBsopBY/AXJFAZ\nQiFovFqYhyNFX0UHB2PkXh5MG0Z0qNKuNsZLQOUujIviuQ0pGhVimfxGevYCS30t\nSbN/xyRAxkSGfThTkBzxDxiWQ+9j6vgt9vXgQJfvwalMHBdb99NEtOiLuWVTl8jC\nDR/yKcIMe4DSIO18UZS1P4KI48WtwTMQN1DiZ1CYEHTEME+AWSggrl6edAox4wBD\nmcGFCOYF/sLZq/c4c/EKo8qW/G7NVZyFUDNbgAHe3X+eASR0Zk8uLUjU2LLSK6a8\ncQNTcVrmOYbN87buqxUxG750HGI+UEq80kdF4QIDAQABAoIBAQCKt4ZDqjrw0+gw\nHUlsc3bVUMyZ79zme2TSoCxmKNZkn84SM0nVgbCmIXDCgbZZ2TCsfv+XzxNMBqdp\n〜〜〜中略〜〜〜-----END RSA PRIVATE KEY-----\n",
  "rootCaCertificate": "-----BEGIN CERTIFICATE-----\r\nMIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\r\nyjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\r\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\r\nU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\r\nZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\r\n〜〜〜中略〜〜〜-----END CERTIFICATE-----",
  "host": "XXXXXXXXXXXXXX-ats.iot.ap-northeast-1.amazonaws.com",
  "clientId": "KryptonDevice_XXXXXXXXXXXXXXX"
}

 AWS IoT のコンソールから確認すると、下記のようにデバイスが追加されていることがわかります。

f:id:akanuma-hiroaki:20181222205640p:plain

 何度 API にアクセスしても、同じ SIM からであればデバイス情報は重複して作成されることはありませんが、証明書はアクセスする度に追加で作成されてしまいますのでご注意ください。

f:id:akanuma-hiroaki:20181222205852p:plain

まとめ

 今回は Krypton の API を呼んで認証情報を登録するところまででしたが、実際はレスポンスとして受け取った認証情報をファイルに保存して、それ以降のアクセス時に使用する形になるかと思います。最初の設定さえ済ませてしまえば、それ以降は新しいデバイスを追加する際には API を呼ぶだけでクラウドサービスへの登録ができてしまうので、とても便利ですね。ただラズパイのようにLinuxベースでリソースが豊富なデバイスと違い、 M5Stack などリソースが限られているマイコンでは X.509 認証などが使えないケースもあるので、その際はまた違った対応が必要かと思います。