yu nkt’s blog

nkty blog

I'm an enterprise software and system architecture. This site dedicates sharing knowledge and know-how about system architecture with me and readers.

アクターモデルとアプリケーションアーキテクチャの関係

背景

マイクロサービスアーキテクチャが浸透し、それに伴いDDDを導入する企業も増えている気がします。 それと同時に、アクターモデルの話題も最近以前より聞くようになった気がします。 ただ、以下のような疑問を持つ人は多くいるのではないでしょうか?

  • アクターモデルは聞いたことがあるけど、重要性が分からない
  • 使い所が分からない
  • サーバーレスコンピューティングなの?でもAkkaの説明ばかり出てくるけど?

こういう状況になっている要因の一つは、おそらく、アクターモデルの説明の多くが分散システムにフォーカスしており(当たり前なんですが)、アプリケーションアーキテクチャとの関係性については、使う人まかせになっているためではないでしょうか。

ここでは、アプリケーションアーキテクチャと合わせて、アクターモデルの使い所を考えてみます。

続きを読む

プロキシ環境下でのkind (kubernetes in docker)

背景

お手軽kubernetesの最近の流行りは、kind (kubernetes in docker)だそうです。

kind.sigs.k8s.io

ただし、かなり厳しいプロキシ環境で実行していたら、少しハマりました。 私の会社だけの状況かもしれないので、ニーズないかもしれませんが、今回は、その解説です。

環境

  • プロキシ環境下 (ポート6443がプロキシで制限されている!)
  • MacOS
  • Docker desktop for Mac
  • bash

プロキシ環境下でのkindインストール手順

まず、kindをbrewでインストールしましょう。

brew install kind

次に、クラスタを構築します。ただ、その前に、プロキシの設定をしましょう。

公式ページのQuick startには、以下のように書かれています。

Note: If you set a proxy it would be used for all the connection requests. It's important that you define what addresses doesn't need to be proxied with the NO_PROXY variable, typically you should avoid to proxy your docker network range NO_PROXY=172.17.0.0/16

よって、まずはDockerにプロキシ情報とNO_PROXYを入れます。 NO_PROXYは、「Bypass proxy settings for these hosts & domains」の欄に、172.17.0.0/16と記載すればOKです。 HTTP_PROXYやHTTPS_PROXYは、その上の欄に記載しておきます。認証付きプロキシなら、http(s)://(ID):(PASS)@(URL):(PORT)です。設定したら、Docker desktopを再起動します。

f:id:yunkt:20200710231459p:plain

これで行けるかと思い、kind create clusterとすると、「Starting control-plane」で数分止まり、その後以下のようなエラーが出るはずです。

153 round_trippers.go:443] GET https://kind-control-plane:6443/healthz?timeout=10s  in 27 milliseconds
(このメッセージが多量に出たあと)
                Unfortunately, an error has occurred:
                        I0710 14:02:51.404598      153 round_trippers.go:443] GET https://kind-control-plane:6443/healthz?timeout=10s  in 27 milliseconds
time out waiting for the condition

                This error is likely caused by:
                        - The kubelet is not running
                        - The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)
(その後もエラーメッセージが続く)

kindでどこまでできるか分かっていませんが、1台の端末内でお試しの環境を構築したいだけなら、以下の付け焼き刃の方法で対応できます。

  1. bashのNO_PROXYにkind-control-planeを追加 (export NO_PROXY=$NO_PROXY,kind-control-plane)
  2. /etc/hostskind-control-planeを追加 (echo "127.0.0.1 kind-control-plane" | sudo tee -a /etc/hosts)

すなわち、kind-control-planeにアクセスできないのは、kind-control-plane宛のパケットがプロキシを経由しているためです。 プロキシを経由しないよう、上記のように設定してやれば、kindでクラスタが構築できます。

SlackのSlash commandとAWSで作る猫監視アプリ

背景

AWS API Gateway, AWS SQS, Slack (file.uploadとスラッシュコマンド)で、自宅の猫を遠隔から監視するアプリを作りました。

AWSもSlack Appもほぼ初心者の自分が、数週間、隙間時間で調べながら作ってみました。 一度作れば簡単なのですが、初めてだと結構はまりどころが多かったので、今回は、作成過程とはまりどころを、丁寧に説明します。

アプリの概要

Raspberry PiにつながったUSBのWebカメラで、定期的に(1時間間隔くらい)、部屋の写真を撮ります。 撮影された写真は、Slackにアップロードされます。

さらに、Slackのスラッシュコマンドで、今の部屋の状況を撮影してアップロードすることもできます。

構成

f:id:yunkt:20200103131931p:plain
全体構成

矢印がループして見にくいですが、それぞれの矢印を解説していきます。

Java appからSlackへの矢印は、SlackにWebカメラで撮った画像をアップロードする矢印です。 アップロードされたら、Slackの指定のチャンネルに、画像が表示されます。

SlackからAWS API Gatewayの矢印は、Slackのユーザーが自作のスラッシュコマンドを入力した後にSlackからAWS API Gatewayに送信されるHTTPリクエストです。 主に、Slackユーザーが、今の猫の様子を見たい時に、このスラッシュコマンドを入力します。

AWS API GatewayからAWS SQSへの矢印は、AWS API Gatewayが、受け取ったHTTPリクエストに応じてAWS SQSに送るメッセージの流れです。

AWS SQSからJava appの矢印は、SQSのキューからJava appに送られるメッセージの流れです。 ただ、実際にはJava appからAWS SQSにロングポーリングしてメッセージを取得しにいき、キューにメッセージがあれば取り出します(取得したのち削除)。 メッセージが取り出せたら、Java appはWebカメラで写真を撮り、Slackに画像をアップロードします。

開発過程

上の構成に従って、以下の手順で開発していきました。

  • 画像を撮影する機能を作成
  • Java appからSlackに画像を送信する機能を作成
  • Slackのスラッシュコマンドを作成
  • SQSのキューを用意
  • API Gatewayを用意とSQSの連携
  • SQSのキューからメッセージを取得する機能を作成

この手順に従って、解説します。

(Java app) 画像を撮影する

まず、Webカメラで画像を撮影する処理を実装しました。 これが、意外にもしょっぱなから詰まったポイントでした。

JavaWebカメラを操作するライブラリは、Webcam Captureが有名ですが、私の開発環境のmacOS Catalinaでは、このライブラリがうまくWebカメラを認識しませんでした(参照)。 開発者の方が開発中のようですが、別の方法を検討を模索し、Webカメラでの撮影を行うコマンドをJavaから叩くことにしました。

Linuxでは、fswebcamコマンドが有名です。コマンド一発で、簡単にWebカメラで写真が撮影できます。 しかし、Macでそれに相当するのは、imagesnapコマンドです。 私の環境は、開発はMac、実稼動はRaspberry Piなので、環境によって画像の撮影の処理が異なる事態になりました。 仕方ないので、System.getPropertyメソッドで、Java app稼動中のOS名を取得して、撮影の機能を持つWebカメラの適切なインスタンスを生成するような設計になりました。

以下のように、インターフェースにファクトリメソッドを持たせて、OSに応じて適切なインスタンスを返します。

public interface Camera {

    public static Camera createCamera(){
        final String OS_NAME = System.getProperty("os.name").toLowerCase();
        switch (OS_NAME) {
            case "linux":
                return new CameraOnLinux();
            case "mac os x":
                return new CameraOnMac();
            case "windows 10":
                return new CameraOnWindows();
            default:
                throw new RuntimeException("Unsupported OS: " + OS_NAME);
        }
    }

    Photograph takePhoto() throws IOException;
}

(Slack) ファイルをSlackに送信

SlackのAPIを使って、Slackにファイルを送信します。

まずは、Slackのアプリ、というものを理解する必要があります。 Slackの一つのワークスペースには、無料だと最大10個まで、アプリがつけられます。 アプリというのは、ボットや、投票機能など、Slackの機能拡張だと思えばいいと思います。 メッセージやファイルの操作など、あらゆる機能を自分で用意したい場合は、このアプリをまずは作成し、そのアプリに機能を設定して、ワークスペースに機能追加するのが基本方針です。

Slackアプリの作成や、ワークスペースへのインストールは、こちらのページから行えます。

api.slack.com

また、公式の説明はここにまとまっています。

api.slack.com

自分はファイル(画像)のアップロードをしたいので、上のドキュメントの"Working with files"を見ていきます。 要約すると、このような手順です。

  1. Slack appを作り、ファイルの送受信に関するPermissionの設定をして、自分のworkspaceに作ったSlack appをつける
  2. files.upload APIを叩く

files.upload APIの詳しい説明は、こちらに書いてあります(Slackのドキュメントは、情報が散らばっていて、やや調べるのに苦戦しました)。

api.slack.com

まずは、上のページの"Tester"タブで、APIを試すことができるので使って見ます。 おそらく、唯一困るのは、tokenとは?だろうと思います。 これは、作成した自分のSlack appのページに行き、"Features"->"OAuth & Permissions"のページにある、"Tokens for Your Workspace"のことです。 "xoxp-"で始まる文字列です。

f:id:yunkt:20200103115622p:plain

"Tester"でうまくいったら、Postmanなどでも試し、問題なさそうであれば、Java appに実装を加えていきましょう。 自分は最初、Unirest-javaを使っていたのですが、 Slackからinvalid_form_dataが返ってきて、なぜかうまくいきませんでした。

unirest.io

Java appからcURLを叩く実装にしたところ、問題なく行けました。が、環境にcURLが存在することを前提とする構成になってしまいました…(これはリファクタリング予定)。

(Slack) Slash commandの設定

スラッシュコマンドとは、Slackで"/"から始まるコマンドを入力して、Slackに搭載された何らかの機能を呼び出せられる機能です。 例えば、"/remind"は、指定した時刻に何かをやることをリマインドしてくれる通知機能です。

まずは、Slash commandの仕組みの理解が必要です。 Slash commandを自作する場合、以下の構成になります。

f:id:yunkt:20200103130640p:plain

Slack自体は、ユーザーからスラッシュコマンドのメッセージを受け取ると、Slackとは関係ないWebアプリにHTTPのPOSTリクエストを送るだけです。 POSTリクエストのレスポンスが届いたら、Slackはその内容をスラッシュコマンドが送られたチャンネルに表示します。

開発者の作業が必要なのは、以下の2点です。

  • 自分が用意したSlack appに、Slash commandを追加し、後段のWebアプリのURLなどを設定
  • 後段のWebアプリを用意

ここまで理解できたら、作るの自体は簡単です。 さきほど作ったSlack appのページから、"Slash command"というリンクを辿り、"Create New Command"ボタンを押して、必要事項を書くだけです。

ただ、まだ後段のWebアプリができていないので、Request URLの項目は記載できません。 あとでこの設定に戻ってくることにしましょう。

ちなみに、私は以下のように、新しく"/tousatu"というスラッシュコマンドを作りました。 Slack app名の"Wakame"は、私の家の猫の名前です。

f:id:yunkt:20200103121354p:plain

AWS側の構成

後段のWebアプリは、AWS API GatewayAWS SQSで、プログラミングレスに構築しました。

AWS API Gatewayは、HTTPリクエストとWebsocketのメッセージを受け付けて、後段のAWSコンポーネントにメッセージを受け渡す、ゲートウェイです。 AWSでWebアプリケーションを作成する場合、必ず必要になります。 通常は、AWS WAFというファイアーウォールとセットで利用し、リクエストのフィルタやトラフィックの可視化を行い、セキュアに運用しますが、今回はまだ使用していません(追って使用する予定です)。

AWS SQSは、簡易的なメッセージングキューです。処理のリクエストを一時的に貯めるためのバッファと思えば良いと思います。 システムのコンポーネント間を、非同期に連携させるときに使います。

今回は、Java appは自宅のラズパイであり、グローバルIPをそのラズパイにつけたくないので、ラズパイがAWSにアクセスしてスラッシュコマンドのメッセージを取得しにいく形式にする必要がありました。 そのため、AWS SQSにいったんスラッシュコマンドのメッセージを置き、Java appはロングポーリングでそのキューを監視する形式になりました。

(AWS SQS) キューの用意

AWS SQSでキューを用意します。

AWSの構築をする際には、まず初めに、AWS IAMでユーザーを作ります。

作成するユーザ(ユーザーが所属するグループ)につける権限は、以下の通りです。

  • AmazonSQSFullAccess
  • CloudWatchEventsReadOnlyAccess

一つ目は、AWS SQSの構築をするために必要です。 二つ目は、AWS SQSのコンソール画面から、届いたメッセージ量のモニタリングを閲覧するために必要です。

では次に、AWS SQSのコンソール画面から、新しいキューを作ります(別にCLIからでもいいです)。 キューの種類には、標準キューとFIFOキューがあり、順序保証をするならFIFOにする必要がありますが、今は全くそんな要件はないので、標準キューにしました。

キューの設定で重要なのは、「メッセージ受信待機時間」を20sにしておくことです。この設定は、ロングポーリングの設定です。 キューに何もメッセージが入っていない場合、20秒間はクライアントからのリクエストに対し、レスポンスをすぐに返さず、クライアントを待たせます。 20秒の間にキューにメッセージが届いたら、即座にレスポンスを返します。

この設定をすることで、Java app側では、AWS SQSへのメッセージを無限ループのポーリングで問い合わせるだけで、キューのメッセージをリアルタイムに確認できます。 また、AWS SQSの課金形態は、この問い合わせに対して課金されるので、リアルタイムかつコストを抑える、という意味でも必須の設定です。

設定が終わったら、キューの名前(<ユーザー番号>/<キュー名>)を控えておきましょう。

(AWS IAM) API Gatewayの構築のためにIAMユーザー権限の追加

AWS API GatewayAPIエンドポイントを作り、AWS SQSのキューにメッセージを転送する設定をします。

そのために、先ほど用意したIAMユーザーに新たに以下の権限を付けます。

  • AmazonAPIGatewayInvokeFullAccess
  • AmazonAPIGatewayPushToCloudWatchLogs
  • AmazonAPIGatewayAdministrator
  • (独自ポリシー) PassRole

上三つは、API Gatewayを操作するための権限です。 四つ目は、AWS サービスにロールを渡す設定をするためのPassRoleという権限です。

PassRoleとは何か、ですが、AWS IAMには、AWSのサービスにも権限を設定でき、その権限のことをロールと言います。 AWS API GatewayはSQSのキューにメッセージを投入するためのロールを付けなければいけません。 ただ、このロールをつけるためには、その操作をするユーザーにAWS IAMの操作権限が必要です。 いちユーザーに、AWS IAMの操作権限を与えてしまったら、何でも出来ることになってしまうので危険です。 そこで、AWSのサービスにロールをつけることだけ可能にした権限が必要であり、それをPassRoleと言います。

以下のAWS IAMのコンソール画面から、ポリシーを開き、ポリシーの作成、をクリックします。

f:id:yunkt:20200103201418p:plain
AWS IAMのコンソール画面

そして、JSONというタブを開き、以下を記述してポリシーの確認をクリックし、作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:Get*",
                "iam:List*",
                "iam:PassRole"
            ],
            "Resource": "*"
        }
    ]
}

上のポリシーでは、AWS IAMのユーザーやロールなどの情報を取得すること、その一覧を閲覧すること、PassRole、の三つを許可しています。

ユーザーに(ユーザーが所属するグループに)このポリシーを追加します。

最後に、AWS API Gatewayに付ける、AWS SQSのキューにメッセージを投入するためのロールを用意します。

AWS IAMのロールの画面から、「ロールの作成」をクリックし、このロールを使うサービスにAPI Gatewayを選択。 アタッチするポリシーにAmazonSQSFullAccessを設定します。 ロールが作成できたら、ロールの識別子である「ロール ARN」を控えておきましょう。

(AWS API Gateway) HTTPリクエストを受けてSQSにエンキュー

AWS API GatewayAPIエンドポイントを作成する手順は以下のとおりです。

  • リソースの作成
  • メソッドの作成
  • ステージの作成
  • APIのデプロイ

API Gatewayのコンソール画面を開き、リソースとPOSTメソッドを用意します。

通常、APIエンドポイントを用意したら、「メソッドリクエスト」の項目で、受け付けるリクエストのパラメータやボディのモデルなどを設定します。 しかし、今回は、「スラッシュコマンドのリクエストが来た」ことのみが重要で、リクエストの内容は全くどうでもいいので、そう言った設定は何もする必要がありません。

ポイントは、「統合リクエスト」の方です。 こちらは、AWS SQSにメッセージを送信する設定です。

まず以下のように設定します。

f:id:yunkt:20200103203640p:plain

実行ロールの項目は、上で控えておいた、ロールのARNです。

次に、URLクエリ文字列パラメータを次のように設定します。

f:id:yunkt:20200103203801p:plain

URLクエリ文字列パラメータの項目は、AWS API Gatewayが受け取ったリクエストの内容を使って(または任意の文字列を設定して)、AWS SQSへのリクエストを作成するための項目です。 AWS SQSのAPIでは、Actionパラメータで何をするかを指定します。 今回は、メッセージ送信なので、'SendMessage'とします。 'SendMessage'の場合は、メッセージのボディを設定しないといけません。 MessageBodyパラメータに、何でもいいので文字列を設定します。

AWS SQSのAPIは、こちらを確認してください。

https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-making-api-requests.html

連携はこれで完了です。試しにAWS API Gatewayのコンソール画面から、テストを行えます。

あとは、ステージの作成とAPIのデプロイです。 今のままでは、AWSの外部から実際にHTTPリクエストを送信しても、AWS API Gatewayは受信できません。 それをするためには、ステージを作り(ステージとは、開発途中版なのか、リリース版なのかを表すもの)、APIがどれかのステージに属させます。

AWS API Gatewayのコンソール画面で、ステージという項目があるので、そこでステージを作成します。 自分は、適当にdevというステージを作りました。 そうしたら、リソースの画面に戻り、「アクション」のボタンから、APIのデプロイを選択して、devステージにデプロイします。

これができたら、Postmanなどで、HTTPリクエストを送り、AWS SQSのキューにメッセージが届くか確認してみましょう。 問題なさそうであれば、Slackのスラッシュコマンドの設定に戻り、APIのURLをスラッシュコマンドの設定画面に記載します。

ちなみに、APIのURLは、AWS API Gatewayのステージのページに記載されています。 ステージのURLの末尾に、リソースのパスを追加すればOKです。

余談ですが、自分のIAMの設定では、AWS API Gatewayのリソースの設定時に、以下のようなエラーメッセージが出ます。 ただ、特に問題なく操作ができます。 フル権限のAdministratorでは、このメッセージは出ないので、何かIAMの権限が足りていないと思いますが、謎です。

f:id:yunkt:20200103202129p:plain

(Java app) AWS SQSへのポーリング

あとは、コーディングだけです。 AWS SDKは、バージョン1と2がありますが、せっかくなので2を使いましょう。

AWS SQSへリクエストをする処理は、こちらが参考になります。

Gradleを使っているなら、build.gradleの設定はここに書かれています。

https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/developer-guide/setup-project-gradle.html

また、AWS SQSの利用方法は、コード例の章に書かれています。

https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/developer-guide/sqs-examples.html

あとは、このドキュメントに基づいて、粛々とコードを書くのみです。

一応、AWS SDK全般の話として、APIを呼ぶためには、アプリを動かす環境に、AWSの認情報を設定する必要があるので、それをまずは行いましょう。 方法は5種類あり、こちらのドキュメントにまとまっています。 もしラズパイで、AWS SDKを使うアプリを複数同時に動かす予定がなければ、環境変数に設定する方法か、aws configureコマンドでデフォルトを設定してしまうのが手っ取り早いかなと思います。

https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/developer-guide/credentials.html

あと、私が少しハマったポイントですが、AWS SDKのバージョン2は、Gradleのバージョンが5以上でしか使えません。 このネコ監視アプリ特有の話ではありませんが、Intellijで開発している場合、利用するGradleを適切に設定しないと、Gradleのbuild時に、以下のエラーが出ます。

> Could not find method platform() for arguments [software.amazon.awssdk:bom:2.5.29] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

おそらくですが、Intellijの設定画面にある、"Use Gradle from"の項目に、"'gradle-wrapper.properties' file"という選択肢がありますが、これがGradleの5未満のバージョンを利用する設定になっており、そうすると上のエラーが出るようです。 自分は、Gradle 6.0をインストールし、上記の項目を"Specified Location"という選択肢にして、Gradleの場所(/usr/local/Cellar/gradle/6.0/libexec)を設定して対処しました。

f:id:yunkt:20200105190623p:plain
Intellijのgradle設定画面

おわりに

この記事を書くのも疲れるほど、細かい作業、ハマりポイントが多くありましたが、一度分かれば簡単です。 Slackを使ったアプリや、ポーリングで処理リクエスを取得しにいって処理するシステム構成は、汎用性が高いと思いますので、是非覚えたいところです。

あと、今回はAWS SQSを利用しましたが、もっと高機能なメッセージングキューに、AWS MQというサービスがあります。 ただ、このサービスはEC2のように、あらかじめ用意したキューのリソース量と、そのキューを確保している時間で課金されるので、今回のような自分しか使わないお手軽アプリには、コストがかかりすぎます。 そのため、今回はAWS SQSを利用しました。

もう一つ余談ですが、Slackのスラッシュコマンドを入力すると、このようなメッセージが表示されます。

f:id:yunkt:20200103212240p:plain

これは、AWS API GatewayからAWS SQSへのPOSTリクエストのレスポンスが、そのまま返されているのだと思います。 機能的には別に問題ではありませんが、やや無骨なので、レスポンスに手を加える方法を模索中です。

あと、まだ手順に書き漏れがある気がしますが、何か疑問があればコメントください。

Raspberry Pi 4にRaspbian Lite (CUI)をインストール・設定

これからRaspberry Pi 4を触る方の参考までに、つい最近買ったときの購入と設定の流れを残します。

購入

まず、Raspberry Pi4は、3までと、異なる部分が多くあるので、購入段階で注意が必要です。

  • Raspberry Pi 3までよりも、はるかに発熱するのでファンが必要
  • 画面出力がHDMIではなく、miniHDMI
  • 電源のケーブルがmicroUSBではなく、USB type-C
  • microSDの容量が、32GBより大きくてもOK

私はケースも欲しかったので、こちらのファン付きケース購入しました。 USB type-Cも付属しています。デザインもかっこいいです(上図)。 ただ、組み立てる時に小さいボルトを回すための、先の細いペンチとドライバーがあるといいです。

Miuzei 最新Raspberry Pi 4 ケース ラスベリー パイ 4 ケース+ 冷却ファン+ヒートシンク Raspberry Pi 4 Model B対応(Raspberry Pi 4 Model B 本体含まりません) (電源付き) https://www.amazon.co.jp/dp/B07VC3RWYZ/ref=cm_sw_r_tw_dp_U_x_Jdi7DbX5CSWRC

あとは、持っていなければminiHDMIのケーブルと、microSDを買いましょう。 microSDは、SDカードへの変換アダプターが付いているものの方が良いと思います(一般のPCはSDカードスロットしかないと思うので)。 私は128GBのmicroSDを買いました。

Samsung microSDカード128GB EVOPlus Class10 UHS-I U3対応 Nintendo Switch 動作確認済 正規代理店保証品 MB-MC128GA/ECO https://www.amazon.co.jp/gp/product/B06XSTCW3G/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

OSインストール

SDカード

SDカードをFAT32でフォーマットします。 ただここで注意ですが、Windowsに標準で付属しているフォーマットの機能では、32GBのメモリをFAT32でフォーマットすることができません。 そこで、専用のツールを用います。

こちらのブログに詳しいので、参照してください。

pcmanabu.com

インストールするOSに、私はRaspbian Liteを選びました。 インストール方法は、次のページを参照してください。 ダウンロードするところは、Raspbian Buster Liteを選びます。

www.1ft-seabass.jp

SDを用意するところまでは、上のページと同じです。 Raspberry Piを起動した後は、LiteにはGUIがなく、操作が異なるので、次に説明します。

設定

CUIの設定は大変そうに感じますが、意外とそうでもありません。 個人的には、さほど大変ではないのにわざわざGUIを起動してメモリやCPUを食う方がもったいない気がします。

基本にやるべきことは、キーボードの設定と、Wifiの設定、SSHサーバの有効化、です。

ログイン

ログインしましょう。デフォルトは以下の通りです。

  • ユーザー名
    • pi
  • パスワード
    • raspberry

時刻・キーボード

以下のコマンドで開く画面で、Raspberry Piの基本的な設定を修正することができます。

sudo raspi-config

4 Localisation Optionsを選択した状態でEnter。以降の選択も、選択肢を選択状態にしたらEnterです。

言語は何も設定をいじりませんでした。文字化けとかしたら怖いので…。

時刻は、以下のように選択していきました。

  1. I2 Change Timezone
  2. Asia
  3. Tokyo

最初の設定画面に戻ると思うので、また4 Localisation Optionsを選択します。

キーボードは、私は東プレのRealforceを使っており、以下の設定でうまく行きました。 他の一般的な日本語キーボードもこれでいいと思います。

  1. I3 Change Keyboard Layout
  2. Generic 105-key PC (intl.)
  3. Japanese - Japanese (OADG 109A)
  4. The default for the keyboard layout
  5. No compose key

ここまで終わったら、最初の設定画面に戻ると思うので、右キーを2回押してFinishをEnter。

Wifi

Raspberry Pi 4には、最初からWifi (Wi-Fi 802.11ac)が備わっているので、SSIDにアクセスしに行くだけです。 ただ、CUIだと、自分でコマンドを入力して設定ファイルを編集する必要があります。あまり大変ではありませんが。

設定すべきことは2つです。

  • SSIDにアクセスするための設定
    • /etc/wpa_passphrase.confを編集
    • Windows/Macで、タスクバーからWifiの設定を開いてSSIDを選択してパスワードを入力する作業に相当
  • ネットワーク設定(IPアドレス取得の設定など)
    • /etc/dhcpcd.confを編集

では、それぞれやっていきましょう。

まずはwpa_supplicant.confの編集ですが、これは以下のコマンドでイチコロです。

wpa_passphrase [SSID名] | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf

コマンドを入力するとカーソルが返ってきませんが問題ありません。 その状態で、WiFiのパスワードを入力して、Enterを入力すると、wpa_passphase.confSSIDへのアクセス設定が書き込まれます。

余談ですが、teeコマンドは、あるコマンドの標準出力を、sudoが必要なファイルへ追記する時に、とても便利です。

/etc/dhcpcd.confの設定は、静的IPを振らないのであれば、ものすごく簡単です。

echo "interface wlan0" | sudo tee -a /etc/dhcpcd.conf

設定が終わったらsudo rebootで再起動します。

その後、sudo apt updateでもして、インターネットにアクセスできることを確認しましょう。

SSH

sudo raspi-config
  1. 5 Interfacing Options
  2. P2 SSH
  3. Yes

これでSSHサーバーが起動します。 しかし、piアカウントのパスワードを、デフォルトのraspberryのままにしておくと、他のPCからpiアカウントでログインすることはできません。 パスワードを変更しましょう。

passwd

おわりに

全体的にとても簡単だった気がしますが、何よりも注意しないといけないのは、購入の段階です。 買って届いてwktkした状態で「type-cがない!miniHDMIがない!」とかなると、せっかくのwktk感が阻害されます。

DHARMA: マイクロサービスアーキテクチャのSecurity by Design

背景

マイクロサービスアーキテクチャは、独立して稼動する各マイクロサービス間で、APIコールやメッセージングなどの通信が発生します。 開発していると、それらのI/Fを整えることだけに気がいきがちですが、通信が発生するということは、セキュリティリスクが高まるとも言えます。

私はセキュリティエンジニアではありませんが、そんな人間でもセキュリティを意識せざるを得ない時代が来たといえます。 この記事では、そういった時代に必要な、Security by Designという潮流と、DHARMAという新しいアプローチについて、説明します。

Security by Design

後付けのセキュリティ対策の問題点

多くの開発現場では、セキュリティ対策を、後付けで考えていないでしょうか。 まず、正常系、次に異常系を設計開発します。 それとは独立した議論として、「マシンにセキュリティソフトを入れておく」とか「OSのFirewallの設定はこうしておく」「ルータなどのネットワーク機器はこう設定しておく」とか考えているかと思います。 長期に構成がほとんど変わらずに安定稼働するシステムなら、Waterfall開発としてこのプロセスでも良いかもしれません。

しかし、近年のITシステムにおいては、いくつもの問題があります。

まず、システムの構成は変化することが多いのですが、上記のプロセスでは、その変化への追従が困難です。 例えば、k8sなどの仕組みで処理をスケールアウトする仕組みが一般的ですが、スケールアウトのためにクラスタに参加させるマシン・VMごとに、セキュリティソフトを入れて、OS設定して、というのはコストが高すぎます。 属人化されやすく、デバイスごとに正しい設定になっているか管理するのも大変です。

また、様々な種類のデバイスが混在するシステムでは、統一的なセキュリティ設定が難しい場合があります。 例えば、様々な種類のIoTデバイスが混在する、IoTシステムです。 特定のデバイスには、従来のセキュリティ対策ガイドラインが適用できず、その場しのぎの暫定対応が積み重なることがありえます。

このような静的なガイドラインのせいで、システムの柔軟性が低下し、ビジネスメリットが阻害されるケースが増えてきます。

セキュリティだけでなく、プライバシーデータの保護の分野にも、Privacy by Designという似た概念が提唱されています。GDPRの条文の中にも、わざわざ一章分使って説明されています。

gdpr-info.eu

プライバシー情報を扱う場合に、万が一流出したら手の施しようがない、というのは、「仕方ないこと」ではなく「組織側の怠慢」であり制裁の対象である、ということです。 一例ですが、プライバシー情報は全て、個人ごとに(or 単発のデータごとに)暗号化し、万が一流出した場合に、鍵を削除する、といった仕組みを、システムのセキュリティ機能として、設計段階から組み込まなければいけません。

システム設計にセキュリティ機能を組み入れる

Security by Designとは、システムの重要な機能の一部として、セキュリティ対策に関する保守のための機能を入れましょう、ということです。 例えば、システムをモニタして問題を検知する機能、システムやネットワークをコントロールする機能(できれば最低限のシステムの可用性は担保しつつ)、新たに導入するセキュリティ機能の効果を評価する環境の用意、などなどが考えられます。

とはいっても、システムが長期運用されるなら、将来どんなリスクがあるかわからない、となりそうです。 確かに、個々のビジネス領域におけるリスクは、一般化できるものではなく、これまでの経験や想像力は必要になります。 ただ、これは、要件が最初から定まっていないアジャイル開発とも共通する所であり、基本的な発想は、システムや内部のアプリケーションを、後からセキュリティ機能の拡張を組み入れやすい設計にしておくことや、問題が起こった時を想定したテストを実行するCI環境を用意して、おくことです。

いったんコスパを度外視して、理想論を言うなら、以下のような開発プロセスが必要です。 詳しくは、詳しくは下記引用元を参照してください。

  • システム設計時に、システムやネットワークなどをコントロールする管理者用の機能とAPIを用意する
  • 想定しうるセキュリティリスクのユースケースを定義する
  • サードパーティコンポーネント脆弱性チェックや脆弱性情報を取得する方法を検討する
  • セキュリティテストを用意し、CI環境で結合テストを自動化する
  • インシデント対応手順をテストし、セキュリティの問題についてすべての構成変更を確認する
  • システムの負荷テストを行い、可用性を確認する

medium.com

このように、セキュリティの設計開発をデプロイパイプラインの組み込んで自動化していく文化を、DevSecOpsと呼びます。

インシデント対応としては、Istioなどによる環境の切り替えや、SDNを用いたコントロールプレーン上でのネットワーク構成変更などが考えられます。 その他のAPIアクセス認証の考え方として、次にDHARMAを紹介します。

DHARMA

Domain Hierarchy Access Regulation for Microservice Architectureの略で、恐らく、ダルマと呼ばせたいのだと思います。

DHARMAとは、複数のマイクロサービスのグループ(ここではこれをドメインと呼びます)を階層的に表現し、その各階層においてAPIアクセスを制御するアプローチです。 特に、クロスプラットフォームでマイクロサービスを運用する際に効果的です。 DHARMAだけがマイクロサービスアーキテクチャにおけるSecurity by Designではありませんが、その一つの考え方として紹介します。

日本語の解説があまりありませんが、英語で検索すると、結構情報が出てきます。 "Securing Microservice APIs"という書籍もおすすめです。 Broadcom社が無償でPDF配布しています。

(クリックすると、PDFがダウンロードされます) docs.broadcom.com

Zero Trust Network

DHARMAの背景に、ゼロトラスト、というセキュリティの考え方があります。 ゼロトラストとは、それぞれのサービスが、別のどのサービスも信頼しない(Untrusted)、ということです。

ゼロトラスト以前の一般的なネットワーク設計では、ネットワークを、互いの端末が信頼できる領域(サブネット)で区切るようにして設計してきました。 つまり、同じプライベートNW内の端末同士は、互いに信頼できるという暗黙の前提が含まれています。こういった、信頼できるネットワークを静的に構成する方法は、問題のある端末(乗っ取られた、ウイルス感染した)が現れた場合のセキュリティ対策として不十分です。

そこで、事前に信頼できる端末を設定することなどはせずに全端末が本質的に信頼できないものとみなす、ゼロトラストと言う考えが生まれました。具体的には、全端末間の全通信にプロキシを挟むなど、通信が発生する時に信頼できるかを判定します。これにより、問題が起きても、迅速かつフレキシブルに対応することができます。

マイクロサービスアーキテクチャのサービス間の通信においても、ゼロトラストを用いる、という手は確かにあります。 Kubernetesでも、Istioでこれを実現できます。

ただ、このような方法では通信の効率が悪いのは明らかです。 また、全通信にフラットに同レベルの暗号化を行うなら、その鍵や証明書の管理がシステム全体に及びます。単一障害点にならないように複雑な構成が必要になります。

マイクロサービス間の通信であれば、動的でありつつも、もっと効率的にアクセス制御をすることはできないでしょうか。

そこで、DHARMAという考え方の出番です。

DHARMAの構成

DHARMAでは、サービスの集合であるドメインを定義します。 このドメイン内は信頼でき、別ドメインからのアクセスは、信頼度が低いものとみなします。 ドメイン内のサービスに見せるAPIエンドポイントを、Interior endpointドメインの外からのアクセスを受けるエンドポイントをboundary endpointと呼び、それぞれで認証方式を変えます。

このドメインを、一つのマイクロサービスアーキテクチャのシステム内で、階層的に構築するのが、DHARMAの構成です。

https://image.slidesharecdn.com/sany18-api-security-microservices-v2018-02-28-180315180755/95/api-security-in-a-microservice-architecture-29-638.jpg?cb=1521137447

(引用元:https://www.slideshare.net/MattMcLarty/api-security-in-a-microservice-architecture)

この階層的なドメインのアクセスコントロールを実装していく手順は、以下の通りです。

  1. 信頼できるドメインを定義する (上図のinner domain)。例えば、より密に処理連携が行われて、かつ、同じプラットフォームで稼動させられるサービス同士を含めるとかです(効率重視)。
  2. ドメインごとに、アクセスコントロールの方法を決める
  3. Interior endpointとboundary endpointを決める
  4. ドメインの下回りのプラットフォームを決める

システムを管理する組織が手を出せないところからのアクセス(最も外側の階層のBoundary endpoint)では、OAuth 2.0やOpenID Connectなどが良いでしょう。 API gatewayを用意することになります。これは普通のマイクロサービスアーキテクチャの開発と同じです。

その内側の階層のBoundary endpointでは、比較的組織内でハンドリングできるため、証明書付きの、JWTなどのトークンを使えば良いかもしれません。 内部の階層は、効率重視にすればよいです。ただ、トークンを使うなど最低限のセキュリティは欲しいです。 それは、軽量なリバプロを用意すればよいでしょう。 一応、auditdなどで監査ログも残しておくのが良いと思います。

認証局と証明書管理の仕組み、トークン発行・管理(期限とかも)の仕組みは必要です。 DHARMAでは、適切なドメインのサービスに適切な証明書、トークン発行を行うよう注意して設定する必要があります。 やや面倒かもしれませんが、システムの中で、重要度に合わせて管理を分けられるのは、効率性とセキュリティを調整しやすいとも言えます。

https://image.slidesharecdn.com/sany18-api-security-microservices-v2018-02-28-180315180755/95/api-security-in-a-microservice-architecture-35-638.jpg?cb=1521137447

(引用元:https://www.slideshare.net/MattMcLarty/api-security-in-a-microservice-architecture)

あとは、SDNのコントロールプレーンのように、アクセスポリシーが変更されたら、それに応じてAPI gatewayや軽量プロキシが動的に設定を反映させるようにする必要があります。 その他、細かい注意や、適切な開発チーム構成などは、上記した書籍や他の情報リソースを参考にしてください。

おわりに

基本的には、しっかりセキュリティを意識したエンジニアが設計したら、無意識にDHARMAと同じような構成になるような気はします。 ただ、こういったプラクティスに名前が付けられることで、知見が広まりやすく、もしかしたらより効率的に構築するためのツールやサービスが出てくるかもしれません。

重要なのは、マイクロサービスアーキテクチャを、モノリシックのWaterfall開発の時と同じ観点で構築したら、リスクが増えることです。 設計の段階で、セキュリティ観点(セキュリティ面の動的な設定を行える機能を仕様に入れること)を含めることを心がけましょう。 それがあまりに億劫であれば、本当にマイクロサービスアーキテクチャが必要かという点から見直すのも一つの手です。

古いRaspberry Pi 2 (wheezy)のOSをアップグレードする

会社でアンオフィシャルな開発合宿を企画し、今度の週末、箱根に行ってきます。

合宿で、ずいぶん昔に買ったRaspberry Pi 2を使ってみようと思い、久々に引っ張り出してみました。 そして、起動して、ひとまずパッケージアップデート。

sudo apt-get update

Not Foundの嵐。

リポジトリを変えてみるといいですよー」という文言を見つけ、以下のサイトのように、/etc/apt/sources.listを修正。

raspi.ryo.sc

ただ、URLのところを変えてみたものの、状況変わらず。

もしかして、wheezyとは相当古い?と思い、ミラーリポジトリディレクトリを見に行きました。 そうしたら、wheezyなんてものはなく、その次のjessieというOS用のリポジトリが存在していました。 どうやら、jessieのリポジトリを見に行き、その後OSもjessieにアップグレードしたほうがいいようです。

そのやり方は、こちらのブログで解説されていました。

pimylifeup.com

先ほどの/etc/apt/sources.listで、jessieのリポジトリを見るようにし、OSアップデート。

apt-get updateすると、いまだにどこかでwheezyを見に行っているようなので、自分の環境では/etc/apt/sources.list以外の場所にもリポジトリの指定をかいているのかもしれません。 そのためエラーは出るものの、ひとまず問題はなさそうです。

プロキシ環境でVS Code Remote Development

Visual Studio Codeの新機能で、コンテナ内や、WSL、SSHでリモートアクセスした環境で、開発する事が出来るRemote Development機能が拡張機能として入れられるようになりました。

marketplace.visualstudio.com

こんな人にオススメです。

  • 開発環境はWindowsだが、実装したプログラムを動かすのはLinuxなので、依存するツールをWindowsにも入れないといけない
    • 往往にして入れ方で詰まる(特にプロキシ環境)
  • 手元がWindowsのため、Dockerfileの開発がやりづらい
  • コード類(依存する環境)が特定のサーバーにあり、手元で開発するのが大変

ものすごく、大企業のエンジニアにはありがたい機能だと思います。 そして、大企業あるあるの認証付きプロキシ環境下でも、これはほとんど苦労することなくセットアップ出来ます。

今回は、SSHアクセスした別環境で開発することを念頭に、セットアップの手順を解説します。

最新のVS Codeをインストール

確か'19 5月末まではInsider版だけでしか使えませんでしたが、今では一般公開版でも使えます。 最新版のVS Codeをインストールしましょう。

次に、VS Codeのプロキシ設定を行い、拡張機能のところから、Microsoftが提供しているRemote Developmentをインストールしましょう。 自動的に、5つくらいのRemote Development関連の拡張機能がインストールされます。

リモートアクセス先のプロキシ設定

このRemote Development機能は、リモートアクセス先に、VS Codeのサーバーをインストールすることになります。

code.visualstudio.com

このサーバーのインストールは、後述するVS CodeのRemoteでの最初のSSHアクセス直後に自動的に行われます。

ここで注意するのは、VS Codeサーバーのインストールには、wgetが用いられることです。 もし、リモートアクセス先に、wgetがなければインストールしてください。

そして、wgetのプロキシ設定は、$HOME/.wgetrcで行います。

qiita.com

キーペア生成とインストール

Remote Developmentでは、手元のVS CodeとリモートのVS CodeサーバーのSSHアクセスは、常時繋がっているわけではありません。 VS Codeサーバー側に、手元のVS Codeから処理を依頼するたびに、SSHアクセスが行われます。 そのため、秘密鍵によるログインを設定していないと、膨大な回数のSSHログインパスワードを入力することになります。

SSHへの秘密鍵アクセスをあまり知らない人は、下をご覧ください。

knowledge.sakura.ad.jp

SSHの用意

もし手元がWindowsで、SSHが入っていない場合は、SSHを使えるようにしましょう。 やり方は以下の4つが考えられます。

  • Git Bashをインストール
  • WSLをインストール
  • SSH機能を開始(参考
  • OpenSSHをダウンロード・展開して、パスを張る(Win32-OpenSSH

もっとも簡単な方法は、1点目です。 gitとググって、gitをインストールすれば、勝手にgit bashが付いてきます。 3点目も簡単ですが、後述するssh-copy-idコマンドが使えるか、私は試しておらず怪しいです。

4番目の方法で行うなら、パスにスペースが含まれるところに、SSHの実行ファイルを置かないことをオススメします。 VS Codeは、スペースが含まれているパスを正しく認識しません。 本記事の本質ではないので詳しくは言いませんが、思わぬところでトラブルが起き得ます。

キーペア作成

コマンドでキーペアを作りましょう。 git bashかWSLを使っている前提で話を進めます。

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

これで、秘密鍵と公開鍵のペアを作成できました。 デフォルトでは、id_rsaという秘密鍵と、id_rsa.pubという公開鍵ができます。 作成される場所は、コンソールに出ると思いますが、git bashやWSLでは、$HOME/.ssh/の中です。

最後に、公開鍵をアクセス先にインストールします。

ssh-copy-id (user)@(host)

パスワードの入力を求められると思うので、入力してください。

SSH設定

configファイルを用意

次のような、configという名前のファイルを作成します。

Host (host-name)
    HostName (host)
    User (user)
    Port 22
    IdentityFile ./id_rsa

(host-name)は、わかりやすい文字列なら何でもOKです。

このファイルは、id_rsaが存在するディレクトリと同じ場所に置いておきます。

秘密鍵パーミッション設定

秘密鍵は、他のユーザーが読み取れないよう、パーミッションを設定する必要があります。 Windowsの場合は、パーミッション設定が厄介です。

まず、秘密鍵を右クリックして、セキュリティのタブを開きます。 そして、詳細設定をクリックし、自分以外のユーザーの項目を全て削除します。 自分というのは、Userではなく、Windowsの自分のユーザー名です。 なければ、追加をクリックして、自分のユーザー名を入力します。 パーミッションは、親フォルダから継承せず、フルパーミッションをつければ良いでしょう。

Mac/Linuxの場合は、これだけでOKです。

sudo chmod 400 id_rsa

VS Codeの設定

手元のマシンのVS Codeを設定します。

Ctrl(またはCommand) + ,を入力し、検索欄にremoteと入力します。

Remote.SSH: Config Fileに、作ほど作ったconfigファイルのパスを記載します(例:C:¥ssh¥config)。

アクセス

適切にconfigファイルが認識できたら、画面左側のRemoteアイコンをクリックすると、configファイル内で記述したホストの一覧が出てきます。 その中で、アクセスしたいホスト名の右にある、ウィンドウにプラスがついたようなアイコンをクリックします。

f:id:yunkt:20190616121407p:plain

ここまでの設定に問題がなければ、新しいウィンドウが開き、ウィンドウの最も左下に、アクセス先のホスト名が記載されるはずです。 この環境で拡張機能をインストールすると、実行環境に関するものであれば、リモートのVS Codeサーバーの方に、拡張機能がインストールされます。