RareJob Tech Blog

レアジョブテクノロジーズのエンジニア・デザイナーによる技術ブログです

デザインスプリントを行った話

どうも!デザイナーの渡辺です!

みなさん新年が明け、お仕事が始まってから2週間程立ちましたが

いかがお過ごしでしょうか?(゚∀゚)
私は今年からジムに通い始めて
筋肉痛の毎日を過ごしておりますヽ(=´▽`=)ノイタイイタイ

さて、今回は昨年末にデザインチームで行った
デザインスプリントについてお話していきたいと思います!

デザインスプリントとは?

そもそもデザインスプリントって?という方もいらっしゃるかと思います。

デザインスプリントはGoogleが開発した
【5日間で価値のあるプロダクトを開発するフレームワーク
なんだか凄そうですよね( ´∀`)bグッ!

それなら入れない理由はない!ということで
我らデザインチームが新しいプロジェクトで取り入れてみました!
(新しいこと取り入れるのが好きなチームなので(ノω・)ヘヘ)

5日間の流れ

デザインスプリントでは、5日間でやることが毎日変わってきます。
具体的には下記の流れで行っていきます。

1日目:理解 2日目:発散 3日目:決定 4日目:試作 5日目:検証

デザインチームで実際にやってみました

1日目

理解

こちらでは、問題点を洗い出し、課題の決定をしていきます。
理解というくらいですので、これでもかっ!というくらいに
知ってる情報を出し認識を深めて行き
課題の決定をしていきます。
ここでは、デザイナーだけでなくフロントエンジニアの方も交え
システム側の課題点も合わせて行いました!
複数人で行うので自身だけでは見えていなかった物が見え
「へー!そんな事あったんだ!」「確かに。それも課題だね」
という声が結構上がります(^^)/

2日目

発散

1日目で見えてきた課題に対し、それを解決する案をひたすら出していきます!

案を出す際に行うのはスケッチ!
こちらはツールではなく、
ひたすら紙に手書きで書いていくことを言います(^^)
黙々とワイヤー案を書いていきます!
これが結構楽しいんですよね(´ω`)

3日目

決定

それぞれがスケッチしてきた紙を出し合い
全員でブレストをしていき、
どの案で進めるかを決めていきます!
ここではやることの決定ですので、予算であったり、
スケジュール等の観点が入ってくることもあります。

今回私達のデザインスプリントでは
POにも参加してもらい決定していきました!

4日目

試作

ここでは3日目に決定したスケッチを元に
プロトタイプを作成していきます。
我々がプロトタイプ作成に使用したのは【Figma】です!

以前、Figmaに関しても少し記事を書きましたので
ご興味ある方はこちらもご覧ください(*´∀`)

【レアジョブのデザインチームがFigmaを導入したお話】 https://appeal.rarejob.co.jp/2019/08/06/6116/

5日目

検証

最終日!【検証】です!
5日間の集大成をお披露目する場面ですね!

実際のユーザーを招待して行うこともありますが
今回は社内のプロジェクトに関わる方に実際に見ていただき
FigmaでFBをいただくという形を取りました(*゚∀゚)

1〜3日目までの間に情報を出し切り、
その上で案を出していることでブラッシュアップされ

1人で作業するときよりも
FBの数は少なくなっているように感じます。

頂いたFBを元に今後の動き、修正するのかなどを決めていきます。

実際にやってみて

デザイナー全員の時間を使ってしまうので
他に並行している作業があると
時間の調整が難しいという難点がありますが
プロジェクトが大きければ大きいほど
5日間という短い時間の中で
認識のずれを軽減でき、質の高いプロトタイプを
生み出せるフレームワークだなと思いました!

ただし、逆を言えば小さいプロジェクトだと使う機会が少ないため
自社で行う際のルール作りなどは
精査できるまで時間がかかるのかなという印象です。

5日間がっつりコミュニケーションを取るので
常に黙々と作業しているというチームには
コミュニケーションを図る良い
機会にもなるのかなと思います(๑•̀ㅂ•́)و✧

また個人的には、「みんなで1つものを作っているんだ!」というのが
実感できるフレームワークだったので
やっていてモチベーションアップに繋がっているなと感じました!

中規模、大規模のプロジェクトを行う際には
ぜひデザインスプリントを
試してみてはいかがでしょうか?ヽ(=´▽`=)ノ

それではノシ

Kinesis Video Streams (WebRTC)の話をしよう

AKEOME。ジャンボです。 今日はKinesisの話をして優勝していこうと思います。細かいWebRTCに関する説明は飛ばします。

Kinesis Video Streams とは

Amazon Kinesis Video Streams を使用すると、分析、機械学習 (ML)、再生、およびその他の処理のために、接続されたデバイスから AWS へ動画を簡単かつ安全にストリーミングできるようになります。

要するにストリーミングコンテンツを解析する仕組みです。IoT の文脈で監視カメラの映像で不審者の検出や工場の不整製品検知などがユースケースとして想定されます。これまでは映像を受け入れて解析することがサービスの主体でしたが、AWS re:Invent 2019 にてWebRTCのサポートが発表になりました。

今回のリリースでできるようになったこと

Kinesis Video Streams がサポートするオープンソースプロジェクトの WebRTC は、リアルタイムのメディアストリーミング、ウェブブラウザ間のインタラクション、モバイルアプリケーション、シンプルな API によるコネクテッドデバイスを可能にします。 用途としては、ビデオチャットやピアツーピアのメディアストリーミングが一般的です。

この記述のところですね。少し既存のユースケースとは実は違くて、Kinesisが拡張したというより、「WebRTCを使うための基礎的な仕組みを一部AWSが提供し始めた」というのが正しいかと思いました。

具体的に提供されるもの

  • シグナリングのためのマネージド型エンドポイント
  • STUN/TURNのマネージド型エンドポイント
  • 各種SDK

これらはすでに他社製の既存のWebRTCプラットフォームでも提供されており、ここにAmazonが乗り出した感じですね〜気になる流れ 👀

コスト

従量課金製で使用量にコストがかかる形です。WebRTCはシチュエーションによったり、ユースケースで通信料が大きく変わるので見積もりが結構難しかったりするんですが、比較的安価に見えます。具体例を見ている限り基本的には

  • 時間
  • クライアント数

が大きな変数となります。

試してみる

*ちなみにAWSの無料範囲外なので気をつけて下さい。

labからサンプルが公開されており、設定さえすればすぐに使えるようになっています。

言葉にすると簡単なんですが、

  1. 以下のようにチャンネルを作成
  2. 必要なtoken/userを作成して値をSDKに渡す
  3. アプリケーションをつくる

f:id:jumbos5:20200109181855p:plain

これだけ。WebRTCの闇は運用してからなのでこの辺が簡単なのはもはや当たり前な時代・・・

webコンソールにこんな機能があり、作成したチャネルのパフォーマンスをモニターできるのはいいなと思いました。

f:id:jumbos5:20200109184616p:plain

サンプルコードとSDKの実装、APIのドキュメントから何ができるかを見ていきましょう。

サンプルコード・SDK

READMEにほぼ概要はありますが、気になる箇所をピックアップしてみてみましょう。

// SDKからオブジェクトのイニシャライズ
const kinesisVideoClient = new AWS.KinesisVideo({
        region: formValues.region,
        accessKeyId: formValues.accessKeyId,
        secretAccessKey: formValues.secretAccessKey,
        sessionToken: formValues.sessionToken,
        endpoint: formValues.endpoint,
    });

// 自分で作ったチャネルを指定してここから配信する
const getSignalingChannelEndpointResponse = await kinesisVideoClient
    .getSignalingChannelEndpoint({
        ChannelARN: channelARN,
        SingleMasterChannelEndpointConfiguration: {
            Protocols: ['WSS', 'HTTPS'],
            Role: KVSWebRTC.Role.VIEWER,
        },
    })
    .promise();
const endpointsByProtocol = getSignalingChannelEndpointResponse.ResourceEndpointList.reduce((endpoints, endpoint) => {
    endpoints[endpoint.Protocol] = endpoint.ResourceEndpoint;
    return endpoints;
}, {});
// にあるようにWebRTCの基本通信概念であるICEを自身でハンドリングする必要がある
signalingClient.on('open', async () => {
...
signalingClient.on('sdpAnswer', async answer => {
...

ここから読み取れるのは

  • 配信はチャネルごとで基本的な話者(PeerIDなど)を指定するような概念はない
  • role {Role} "MASTER" or "VIEWER" があり、双方向配信はこれが前提
  • ICEを自分で裁く必要がある
  • IE以外はサポートしている
  • 実装はちゃんとWebRTCの知識がいる(よりコアに触れそう:小並感)

API Doc

絶対読みましょう。結構大事な制約とかが書いてあります。

  • Currently, a signaling channel can only have one master
  • A signaling channel can have up to 10 connected viewers

WebRTC Technology ConceptsとかはWebRTCに必要なことをシンプルな文章で登場人物とかをまとめてくれているので勉強になります。一方でこの記述を明記することは「フルマネージドなのはサーバの運用だけでICEをはじめとしたクライアント側の技術はラップしない、ちゃんと理解しろよ」っいうAmazonの優しさと切なさと心強さなので座して読みましょう。

サンプルコードではwebのコンソールからチャネルを作成していますが、リファレンスをみる限りhttpのエンドポイントも提供されており自身で増減させることが可能のようです。

テレビ会議システムで言えば作成されるルームごとにこのハンドリングをするようなイメージなので、webのコンソールだけだとかなりユースケース限られるなと思っていたのでさっと目を通すと良いです。

まとめ

全体的にWebRTCのコア機能に対して基本的なサーバサイドのマネージドサービスとそれを利用するための抽象度低いSDKとインターフェースを提供しています。 現状はまだ多機能とはいきませんが、AWS CloudTrailとの連携などクライアントのイベント管理と解析をできたり、単純にcloudwatchと連携できるのですでにインフラ基盤がAWSなら連携できるメリットがありますね。

ただユースケースとしてライブ配信や解析をベースとしたインターフェースになっているのとSDKの抽象度的に結構余力がないと導入できないと感じました。

ユースケース次第ではハマりますね。

それではハッピーフライデー。🍺

今更わかるPostman〜便利なAPI開発補助ツール〜

みなさん、今日も元気にAPI開発していますか?
最近社内勉強会で改めてPostmanについて説明したら
意外と機能を知らない人が多かったので
API開発で色々便利な機能満載のPostmanを
API Clientとしての機能をメインに紹介しようと思います。

Postman is 何?

Postman, Inc.が開発しているAPI開発コラボレーションツールです。
API Client機能やチーム開発の共有、MockServerの作成機能、自動テスト機能などがあります。
弊社ではローカルでの開発の振る舞いテストで主に使用しています。

インストール方法

mac(Homebrew-cask)

Homebrew-caskでインストール可能です。

$ brew cask install postman

mac & windows

www.getpostman.com

こちらのリンク先のDownload the Appをクリックしてダウンロードしてインストールしてください。

新規設定

f:id:dedekopon:20191213160428p:plain 画面左上の[New]をクリックして新規の設定を行う

f:id:dedekopon:20191213161311p:plain BUILDING BLOCKS

Request:新規でリクエストを作成する。
Collection:リクエストのフォルダを作成する。
Environment:Postman上の環境変数の設定を行う。

ADVANCED(ログイン後の機能)

API Documentation:ドキュメントを作成する。ログイン後のページで共有される。 Mock Server:リクエストのモックを作成する。
Monitor:自動テストの設定、パフォーマンスチェックを行う。

メイン画面

f:id:dedekopon:20191213163133p:plainAPI
APIの説明
APIのメソッド・URL
④リクエストの設定
⑤リクエストの設定詳細項目
環境変数設定
APIのリクエスト例
⑧クッキーの設定・別媒体でのリクエストサンプル

リクエストの設定

Params

APIのqueryパラメータとpathパラメータを設定する。
f:id:dedekopon:20191213164104p:plain pathパラメータはURL中に:param_nameで設定、queryパラメータは通常のGETパラメータなので?param_name=valueの形で定義する。
例えばhttp://example.com/test/:test_id/?param=testの場合は図のようになる。

Authorization

APIを使用する際に認証が必要な場合などに設定する。

Headers

リクエストのヘッダーの設定を行う。Authorizationにauthorizeヘッダーを設定している場合はこちらには不要です。

Body

リクエストのボディの設定を行う。

pre-request script & tests

f:id:dedekopon:20191213164914p:plain Intro to scripts | Postman Learning Center
リクエスト送信時に前処理、後処理を簡易的ですが実装できます。
以下にちょっとした一例を書いておきます。

  • 環境変数から保持しているトークンの有効期限を確認して、エラーを出すpre-request script
// 環境変数から値を取得する
var ttl = pm.environment.get("TOKEN_TTL")
var now = Date.now()
if (now > ttl) {
    // throw new Errorされた場合、処理はそこで止まります
    throw new Error("No available token set. Please set environment TOKEN and TOKEN_TTL")
}
var refresh = false
// レスポンスのステータスコードが200かチェックする
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
    refresh = true
});

if (refresh) {
    // 環境変数のトークンを更新する
    console.log('token refresh')
    pm.environment.set("TOKEN", pm.response.json().token);
    pm.environment.set("TOKEN_TTL", Date.now() + (3600 * 1000));    
}

環境変数設定

f:id:dedekopon:20191213170830p:plain 環境変数の切り替えを行うことができます。
こちらで設定した値はpre-request script,tests

pm.environment.get("VALUE_NAME") // 取得
pm.environment.set("VALUE_NAME", "value") // 更新

のような形で操作できる他
{{URL}}/test/のようにURLやパラメータ中に{{VALUE_NAME}}の形で埋め込むことができます。

APIのリクエスト例

f:id:dedekopon:20191213171745p:plain APIのリクエスト例を保存することができ、簡単に呼び出すことができます。

別媒体でのリクエストサンプル

f:id:dedekopon:20191213172118p:plain CodeをクリックすることでPHP,Ruby,Goでの実装サンプルが確認することができます。
別の環境で確認するときなどにとても重宝します。

エクスポート

これらの設定は全てインポート・エクスポートすることが可能です。
弊社ではAPIのサンプルの共有用リポジトリを用意し、Git上で管理しています。

まとめ

  • Postmanは優秀なAPI開発補助ツール
  • ただリクエストを送るだけだと勿体無いので色々設定して使おう
  • ログインして使う場合はさらに便利機能が色々使えるよ

RDS Proxyでサーバレス捗りそうだと思いました

f:id:shino8383:20191205183647j:plain

おはようございます。レアジョブのHUNTER×HUNTER芸人こと、DevOpsチームのshinoです。 (前回の記事: EC2 Auto Scalingを導入する際のポイント - RareJob Tech Blog)

HUNTER×HUNTERAWSの知識をインプットするのが日課になっています。

今まさにAWS re:Inventが開催中です。来年は行きます。

この時期はAWSに関する大量の新サービス・アップデート情報が流れてくるのでキャッチアップが大変です。

今回はAWS re:Inventの中で発表があった情報の中で、個人的に熱いと思ったRDS Proxyについて触れたいと思います。

RDS Proxyとは

f:id:shino8383:20191205184022p:plain

RDS Proxyとは名前の通りRDSへの接続をプロキシするお方です。

RDS Proxyの主なメリットは以下のようになります。

  • RDSへの接続にコネクションプールを利用できる
  • RDS Proxyの利用にはSecrets ManagerかIAM認証を用いて接続するので、よりセキュアな認証を強制できる(推奨はIAM認証)

また、RDS Proxy自体は可用性を持ったマネージドなプロキシサービスなのでプロキシサーバの管理コストはこちらが持つ必要がありません。 移行コストについては、従来利用していたRDSエンドポイントをRDS Proxyが提供するエンドポイントに差し替えるだけで利用可能です。

クライアント側でコネクションプールを実装しなくていいってだけでも嬉しいです。

このようなサービスのRDS Proxyですが、私が初見で感じた感想は

「サーバレス捗りそう(小並感)」

でした。

LambdaとRDS(RDB)の話

話は変わりますが、LambdaとRDS(RDB)は相性が悪く、アンチパターンと言えます。

なぜ相性が悪いのでしょうか?

一般的にRDBは大量の接続要求をさばくのが苦手だからです

RDBがクエリを処理する際にかかるコストに比べ、TCP接続を確立するコストやDB側でコネクションを生成するコストが比較的高いため、 高負荷なワークロードでは"接続"にかかるコストでCPUやメモリを食いつぶしてしまうことが多いです。

そのため、アプリケーション規模が大きくなってきてRDBが悲鳴を上げ始めたらコネクションプーリングなどを用いて、DBへの接続にかかるコストを低くするなどの対処が取られるケースが多いかと思います。 ただし、コネクションをプールして利用するのにもメモリやCPUを使いますし、レスポンスタイムも遅くなることが多いのでRDBへの負荷が小さいうちは割に合わないと思います。

(RDBじゃないですがRedisでもコネクションプールを使わずにCPUリソースを食いつぶしてしまったことがあります。)

これがどうLambdaと関係あるかと言うと、

簡単にスケールしてしまうLambdaはコネクションプールを使うことができず、 Lambda->RDSを利用しようと思うと大量の接続要求を行ってしまいRDSが悲鳴をあげる、

という図です。 夜間のバッチ処理とかなら使えるとは思いますが、用法用量を守る覚悟が必要です。

また、RDS側がリソース的に耐えられたとしてもDBへの最大接続数の制限に引っかかってエラーとかも全然あります。

なので、サーバレスやろうぜってなってもRDS(RDB)をデータソースの候補にあげることができず、DynamoさんやElastiCache For Redisさんなどが使われる事例が多いと思います。

今回、RDS Proxyさんが降臨なさったのでLambdaからRDS使えるぞ!というのが

「サーバレス捗りそう(小並感)」

に繋がります。

終わりに

RDS Proxyの登場で、RDBMSの種類問わずにRDSをデータソースとしたアーキテクチャをサーバレスで構築することが可能になります。 控えめに言って熱いです。

現在はまだPreview版なのでMySQLとAuroraしか使えないですが、東京リージョンでもお試し頂けます。

隙あらば利用していきたいと思います。

そういう訳で我々は一緒に働く仲間を大募集しております。

少しでも興味のある方は以下からどうぞ!

👏採用/求人情報 | アピール | 未来の教育を作る人のマガジン インフラエンジニア(AWS)

Alexaスキル開発してます

プラットフォームチーム所属の南です
弊社では有志で勉強会のような場を設けることができます。分科会という呼び方をしているのですが、その中で自分はAlexaスキル開発に参加していました。今回はその活動を通じて学んだことなどを徒然なるままに書きたいと思います。
以前、勉強会でLT枠としても発表したのでその資料も下にリンクしておきます

なぜやるのか?

  • 新たな技術分野に乗り遅れないようにするため
    • 広まりつつある技術に対し、いつ事業として取り組むことになったとしても乗り遅れないように知見を貯める
  • 気楽な気持ちで利用してもらい「学習体験の向上」を目指す
    • 教材やPCを準備することなく学習を始めることができる

大きくはこの2点を目的として始動しました。

どのように作るのか?

AlexaのようなVUIを設計するのは初めての経験でした。 そのため以下をまず決定しました。

  • なにを作るのか
    • リスニングからTrue/Falseを答えるシンプルなスキルを作る
  • スタートから終わりまでの道筋
    • 目に見えないものを設計する

スタートから終わりまでの道筋

今回の開発ではここに一番時間がかかりました

f:id:nannannanan:20191129174909p:plain
ユーザにどのような遷移をして欲しいのかを考える
上記の図のように、どのタイミングでどの処理が実行されユーザが遷移するのかを話し合いました。しかし、図で表した内容を実際にデバッグすると間延びしたつまらないスキルになることも多く、問いかける順番や反応に対する処理はGUIと同じかそれ以上に気を配る必要がありました。

わかったこと

  • 気をつけないといけないことが多い
    • 間の取り方や問いかけの回数
    • VUIに慣れている人はまだ少ない
    • 想定外の返答に対する制御
  • シンプルな受け答えで学習を提供できるのは純粋におもしろい
  • 手ぶらで受けられるなど、VUIの特性をうまく利用すれば学習体験は向上できる

VUI自体がこれからさらに広まり、声を使った操作に慣れた人が増えてくれば、もっと面白いスキルが作れるように感じました。

最後に

今後、弊社がAlexaスキルをリリースした際は利用して頂けると幸いです。

こんな感じで新しい技術に対しても積極的に取り組むことができるので、興味がある方はとりあえず下のリンクを踏んでくれると嬉しいです。 https://appeal.rarejob.co.jp/recruit/

勉強会 LT資料 -> Alexaスキル開発をして分かった VUIの難しさ - Speaker Deck

AWS Device Farm でブラウザから実機でアプリを動かそう

AWS Device Farm

AWS Device Farmクラウドでモバイル端末をエミュレートできる仕組みです。 CodepipelineなどのCI/CDとも連携可能で、E2Eやエミュレート環境としてAWSから提供されています。

できること

  1. iOS/Android端末がクラウド上で動かせる
  2. 2019/11/05時点で利用可能なデバイス266種類
  3. 「AutomatedTest」と「RemoteAccess」が可能(後述
  4. テスト後に動画やログを自動で保存してくれているのでDLできる

最新のデバイス・OSサポート

現状ではあまり期待できないようです。色々な会社の抱えている課題の一つに 十全に検証機を用意できない というのがあると思います。特に最新のデバイスなどは価格やそもそもの入手困難さからサポートが後手になることが多いと思います。 またOSに関しても社内の検証端末のOSを上げるのはちょっと骨が折れる・・・

iOSだと特にまだ最新のXcodeでビルドできるように対応できてないのに最新のOSで問題がッ!とか起きることもあると思うのでそういうケースで使えてほしい・・・アップデート頻度等を見てもそこのスピード感はあまりないように思います。

AutomatedTest

f:id:jumbos5:20191122130444p:plain

ios/android共にE2Eテストやモンキーテストを実行できます。

テストケース

様々なテストケースの準備ができます。

f:id:jumbos5:20191122130604p:plain

中でも準備がいらないものとしてFuzzTestがあります。モンキーテストに相当するものなので、かなりシンプルですが使えるユースケースは多いと思います。

Testing mobile apps with Cucumber and Appium through TestNG on AWS Device Farmとかを見ると、上記の写真にもある通りAppium/Calabashでもテストケースが欠けるので細かい手順も実行できます。


Remote Access

f:id:jumbos5:20191122130553p:plain

こちらは本当にブラウザ上でapk/ipaのアップロードをして端末に入れてそれをポチポチ動かせるような仕組みですね。

WebRTCは動くの?

結論動く。 特に準備もいりません、うごくapk上げるだけ。 エミュレーターだとダミー映像が出るかと思いますが、DeviceFarmでは真っ黒の映像が出ます。 カメラはアクティブにはなっているようですが、設置環境によって映像が黒くなっているようです。キャリアやSMSは動かないので、ある程度基本的な動作を確認するに限定されるかと思います。

そのためカメラアプリなどでも一応動かすことはできるかもです。 ただ画面自体は黒くなるので、E2Eで成功結果として撮影物のデータ量や流れるストリームで見てしまうとコケるかもしれません。

f:id:jumbos5:20191122130628p:plain

コスト

0.17USD/デバイス

です。1分を1デバイスで = 1デバイス分なので、1つのリモートアクセスで5分使ったとすると85円くらいですかね?例えばAutmatedTestを複数デバイスで動かした場合はそのデバイス分のコストがかかります。

最初の1000分までは無料のため、ぜひ試してください。10分もあれば動きを確認できるかと思います。

プライベートデバイス

こういう概念があり、理由としてはCircleCIのようにインスタンスリザーブされ続けているわけじゃないので混んでいる時間には待ち時間が発生します。私が検証している時に長時間待つようなことはなかったのですが、将来的には利用を検討すべきかと思います。 コストは 200 USD/月 とまぁまぁ高いので、それなら端末買ったほうがよくない???とも思いますが、現在はCodePipelineと連携してCIのプロセスとも連携できるので、本格的なE2Eをデプロイメントパイプラインの中に内包したいのであればかなり有効かなと感じます。

IPなどが固定されるわけではないと思うので例えばネットワーク的にクローズとなSTG環境を持っていて、それのアプリをE2Eしたい場合はどうすれば?とか考えましたが

docs.aws.amazon.com

上記を読んでいる感じはできそうだと感じました。設定はめんどくさそうだが・・・

エミュレーション

などがエミュレートできる。GPSとか地味に地図系のアプリとかだと重要だと思うし、他アプリのインストールも複数アプリで連携するようなケースでは有用ですね。

まとめ

  • バイスごとに挙動の違うWebRTCのE2Eをデバイス*OSを並列・包括的に実行できる
  • skywayもちゃんと動くことを確認できた
  • 相手の映像は見れないのでp2psfuの接続のチェックなどのみ、カメラは真っ黒だが生きておりマイクはない
  • プラグインがあるので既存のデプロイメントプロセスに載せてE2Eが実施可能

Client VPNで分析環境を用意してみた

こんにちは。@hayata-yamamotoです。

普段は、EdTech Labという研究開発のチームで機械学習とデータ分析をしています。 AllenNLPmypyが好きです。

さて今回は、AWSのClientVPNを用いて手軽にVPN環境を用意し、Jupyterを動かしてみたいと思います。 ClientVPNの使い方はAWSの公式にありますが、Terraformと組み合わせたものは見当たらなかったので誰かの参考になれば嬉しいです。

今回出てくる技術

なお今回の内容はGitHubにも置いてあります。

github.com

Client VPN

AWSが用意するVPNサービスの1つです。2018年12月にリリースされました。東京リージョンは、2019年5月から利用可能になっています。

aws.amazon.com

そもそもVPNは、あるネットワークやデバイスからある別のネットワークへの安全でプライベートに接続するために設定します。 公式によるとClient VPNは、ユーザー需要に合わせて利用可能な Client VPN 接続の数を自動的にスケールアップまたはスケールダウンする、完全マネージド型で伸縮自在な VPN サービスです。使ってみた感じ、クラウド側でそれぞれのクライアントのIPをClient VPNに割り振っているCIDR範囲と紐づけてくれています。それゆえ、クライアント側はVPN用のソフトウェアをダウンロードするだけで済みます。

詳細な説明やユースケースについては、AWSの公式も合わせて確認されると良いと思います。

aws.amazon.com

どのようなものか

少し触れてしまいましたが、このサービスは以下のような仕組みで動いています。

f:id:hayata-yamamoto:20190727133707p:plain
Client VPN 仕組み(AWSより)

この通り設定すると、環境が用意できるようになります。

  1. Client VPN Endpointを設定します
  2. VPNをサブネットに関連づけます
  3. ユーザーがリソースにアクセスできるように認証ルールを追加します
  4. 他のネットワークにアクセスできるようにルールやルートを追加します(インターネットとか)

設定方法

クライアント認証用の証明書を発行する

acmに登録するサーバーとクライアントの証明書を発行します。

$ git clone https://github.com/OpenVPN/easy-rsa.git
$ cd easy-rsa/easyrsa3

新しいPKI, CA環境を作って、サーバーとクライアントの証明書を発行します。

$ ./easyrsa init-pki
$ ./easyrsa build-ca nopass
$ ./easyrsa build-server-full server nopass
$ ./easyrsa build-client-full client1.domain.tld nopass

適当な場所にファイルを移動して、awsコマンドでACMにインポートします。

$ cp pki/ca.crt /custom_folder/
$ cp pki/issued/server.crt /custom_folder/
$ cp pki/private/server.key /custom_folder/
$ cp pki/issued/client1.domain.tld.crt /custom_folder
$ cp pki/private/client1.domain.tld.key /custom_folder/
$ cd /custom_folder/

# サーバー証明書のインポート
$ aws acm import-certificate --certificate file://server.crt --private-key file://server.key --certificate-chain file://ca.crt --region region

# クライアント証明書のインポート
$ aws acm import-certificate --certificate file://client1.domain.tld.crt --private-key file://client1.domain.tld.key --certificate-chain file://ca.crt --region region

環境を用意

今回は、Terraformを使って設定します。ただ、Terraformだと執筆時点でAPIへのサポートが微妙に足りませんので、そこだけはコンソールから操作をします。具体的には、aws_client_vpn_route_table, aws_client_vpn_authorization_ruleに相当するものがありません。

github.com

# variables.tf

variable "region" {
  default = "ap-northeast-1"
}

variable "availability_zone" {
  default = "ap-northeast-1a"
}

variable "instance_info" {
  type = "map"
  default = {
    ami           = "ami-0c6dcc2b75586bc5d"
    instance_type = "t2.small"
  }

}

variable "cidr_blocks" {
  type = "map"
  default = {
    vpc        = "10.0.0.0/16"
    subnet     = "10.0.0.0/24"
    global     = "0.0.0.0/0"
    client_vpn = "100.0.0.0/16"
  }
}

variable "my_cidr_block" {}

variable "ssh_keyname" {}
# data.tf

data "aws_acm_certificate" "server_certificate" {
  domain = "server"
}

data "aws_acm_certificate" "client_certificate" {
  domain = "client1.domain.tld"
}
# resources.tf

provider "aws" {
  region = var.region
}

resource "aws_vpc" "vpc" {
  cidr_block           = var.cidr_blocks.vpc
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "vpc"
  }
}

resource "aws_subnet" "subnet" {
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = var.cidr_blocks.subnet
  availability_zone       = var.availability_zone
  map_public_ip_on_launch = true

  tags = {
    Name = "subnet"
  }
}

resource "aws_route_table" "rt" {
  vpc_id = aws_vpc.vpc.id

  route {
    cidr_block = var.cidr_blocks.global
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "rt"
  }
}

resource "aws_route_table_association" "a" {
  route_table_id = aws_route_table.rt.id
  subnet_id      = aws_subnet.subnet.id
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "igw"
  }
}

resource "aws_security_group" "sg" {
  name   = "sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.my_cidr_block]
  }

  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = aws_ec2_client_vpn_network_association.vpn.security_groups
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = [var.cidr_blocks.global]
  }
  tags = {
    Name = "sg"
  }
}

resource "aws_instance" "myinstance" {
  ami                         = var.instance_info.ami
  instance_type               = var.instance_info.instance_type
  vpc_security_group_ids      = [aws_security_group.sg.id]
  subnet_id                   = aws_subnet.subnet.id
  key_name                    = var.ssh_keyname
  associate_public_ip_address = true

  tags = {
    Name = "myinstance"
  }
}


resource "aws_ec2_client_vpn_endpoint" "vpn" {
  description            = "client vpn endpoint test"
  server_certificate_arn = data.aws_acm_certificate.server_certificate.arn
  client_cidr_block      = var.cidr_blocks.client_vpn
  authentication_options {
    type                       = "certificate-authentication"
    root_certificate_chain_arn = data.aws_acm_certificate.client_certificate.arn
  }
  connection_log_options {
    enabled = false
  }
  tags = {
    Name = "vpn"
  }
}

resource "aws_ec2_client_vpn_network_association" "vpn" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.vpn.id
  subnet_id              = aws_subnet.subnet.id
}
# outputs.tf

output "instance_public_ip" {
  value = aws_instance.myinstance.public_ip
}

output "instance_private_ip" {
  value = aws_instance.myinstance.private_ip
}

適当な場所に上記の.tfを作ってもらって、

$ terraform init 
$ terraform plan 
$ terraform apply

とし、自分のCIDR範囲を入力してください。(123.1.1.1/32みたいなやつです) 実行が終わると、outputでインスタンスのpublic_ipが吐き出されるので、その情報を使ってsshインスタンスに接続してください。

ssh -i keyname ubuntu@instance-public-ip

認証とルートテーブルを追加する

実は先ほどの動作で、上記の4つの手順のうち2つが終わっています。あとは、認証とルートテーブルを付け加えて、完成です。

認証は、以下から

f:id:hayata-yamamoto:20191004000754p:plain

このように、サブネットのCIDR範囲を設定しておきます。

f:id:hayata-yamamoto:20191004000820p:plain ルートテーブルも先ほどと同様の画面から

f:id:hayata-yamamoto:20191004000854p:plain

インターネットに出れるようにルートを設定しておきましょう

f:id:hayata-yamamoto:20191004000916p:plain

接続する

tunnelblickをインストールしておいてください。

tunnelblick.net

OpenVPNやTunnelblickで使う設定ファイルをクライアントエンドポイントのページからダウンロードしてください。 適当にファイル名は変えて大丈夫です。

f:id:hayata-yamamoto:20191004001056p:plain

最初に作ったclient用の証明書と鍵と同じ場所に設定ファイルを配置し、

# hoge.vpn
...

cert /path/to/client1.domain.tld.crt
key /path/to/client1.domain.tld.key

と追記してこの.opvnファイルを開いてください。設定ファイルを読み込んだらTunnelblickを起動し、接続できるか確認しましょう。

Jupyterを立てる

さて、最終ゴールはJupyterの環境にアクセスすることでした。 ひとまず設定したVPNの環境を切断して、sshからEC2のインスタンスにアクセスしてください。

$ ssh -i keyname ubuntu@instance-global-ip

ちなみに、このインスタンスDeep Learning AMIを使っています。なので最初からJupyterNotebookは使えます。sshでトンネリングしてjupyterにアクセスすればOKです。サーバー側で

$ jupyter notebook --no-browser --NotebookApp.token=""

を実行し、別のタブで以下を実行してトンネリングを行います。

$ ssh -i keyname ubuntu@instance-private-ip -L 8888:localhost:8888 -N 

ブラウザでlocalhost:8888/treeにアクセスして、jupyterの画面が表示されれば設定は完了です。

まとめ

手軽にできるVPNの設定を書いてみました。利便性が高く、クライアント側の設定がほとんどいらないのがメリットです。分析環境などを用意する際は、データをバケットやRDSに用意することが多いと思いますが、作業するEC2だけにARNを設定したりしてセキュリティをいい感じに高めながら安全に、かつ快適に開発を進めていきたいものです。