RareJob Tech Blog

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

Amazon S3にファイルをフロントエンドから直接アップロードしたかった話

バックエンドエンジニアです。 涼しくなってきましたが、みなさんファッション好きですか? 私は好きです。zozotownに掲載して頂きました。

今回やりたかった事

とある業務で500MB以上の動画ファイルをシステムに保存し、参照できるようにしたいという事でした。

前提

・S3にアクセスするアクセスキーとシークレットキーはセキュアにする為、Backendに持っている。
・S3には限定的なアクセス権を持たせて特定のディレクトリにアクセスしたい。

課題

原始的にバックエンド経由でアップロードする実装にしたところ、メモリ不足のエラーが出てしまった。

解決策

Amazon S3にはPre-Signed URLという機能があり、これを使うと期限付きで直接JavaScriptからS3へアップロード出来る事がわかったので、 上記課題を解決できると判断し、使ってみました。 バッファリングに仕様を変更する改修やCognitoを用いて一時的な認証方法を付与する方法も検討しましたが、今回はPre-Signed URLを使ったほうがさくっと対応出来そうなのでこちらを採用しました。

処理フロー

f:id:daichangdesu:20190913180026p:plain ざっくりとこんな感じのフローを考えました。

①request Pre-signed URL to Backend

JavaScriptからBackendにアップロード必要な情報をAPIで要求します。

②request Pre-signed URL to AWS

BackendからAWSAccess KeyとSecret Keyを使ってPre-signed URLをAWSに要求します。 ※Laravel5.5、league/flysystem-aws-s3-v3を使用したサンプルコード

public function getPreSignedUrl(string $filePath): string
{
    $s3 = Storage::disk('s3_private');
    $client = $s3->getDriver()->getAdapter()->getClient();
    $expiry = "+30 minutes";

    $command = $client->getCommand('PutObject', [
        'Bucket' => Config::get('filesystems.disks.s3_private.bucket'),
        'Key' => $filePath,
        'ACL' => 'public-read'
    ]);

    $request = $client->createPresignedRequest($command, $expiry);

    return (string) $request->getUri();
}

③return Pre-signed URL from AWS

AWSからPre-signed URLを取得します。

④return Pre-signed URL from Backend

取得したURLをJavaScriptに返します。

⑤upload file to AWS

JavaScriptから取得したURLを使ってアップロードします。

考察

結果問題なくアップロード出来るようになりました。
実装時にAWSから取得する所まではすんなり行ったのですが、フロントから動画ファイルをアップロード するときにヘッダーの書き方によってはアップロードが完了してもファイルが壊れてしまっていたりしてとても焦りました。
今回はaxiosを使って以下のようにアップロードする事により実現しました。

return axios.put(preSignedUrl, this.videoDataBinary, {
    headers: {
        'Content-Type': 'video/mp4'
},

シンプルにこれだけで行けました.... 今回の学びは今後アプリからファイルをアップロードしたりする時等に使えそうだなと思いました。

iPadのレッスンルームに端末回転をサポートした話しとユニバーサル対応のお話し

開発本部APP・UXチームの玉置(@tamappe)です。 今回の投稿が3回目になります。お盆は夏季休暇を利用して地元に帰省していました。
帰省中は暑いながらもどんな記事を書こうかなと思いながら時間が無下に過ぎ去ってしまい直近になって焦りながら記事を書いております。

今回は最近リリースしましたレッスンルームのiPad横向き対応について技術的な話しをしたいと思います。
最初は端末回転だけをサポートすればいいのかと思っていましたが色々ハマったポイントがありましたので後述します。

レッスンルームとは

そもそもレアジョブアプリを使ったことがない方向けにレッスンルームについてご紹介したいと思います。 レッスンルームとはレアジョブが提供している講師とのレッスンの場です。

f:id:qed805:20190821173322p:plain
lesson_room_ipad_portrait

(画像はiPadでの縦向きでのレッスンルームの様子です)

それぞれのUIを細かく分類すると下記のようになります。

f:id:qed805:20190821172506p:plain
lesson_room_ipad_portrait_description

動画の部分は本来はフィリピン講師が映る形になりますが開発中はレッスンルームを出入りできますしカメラで自分の顔が見れていますので 顔部分だけ加工してみました。

今回はこのレッスンルームに端末回転を許可する形で横向きのレイアウトを開発することがタスクでした。 注意したいことはこの端末回転はiPadだけですのでiPhoneではそもそも横向きはサポートしていません。

f:id:qed805:20190821172557p:plain
lesson_room_ipad_landscape

(iPadを横向きにして入室した場合の画面)

f:id:qed805:20190821172732p:plain
lesson_room_ipad_landscape_open

(教材を開いている時の画面)

こちらが今回のiPad横画面対応しました横画面のレイアウトになります。

f:id:qed805:20190821172830p:plain
lesson_room_landscape_description

このように分類分けするレイアウトにしました。
レアジョブアプリのデザインは基本的にstoryboardを使ってデザインを開発しています。
そのため、storyboardにautolayoutを貼って端末間のサイズを調整しています。

本来のやり方であればiPad横画面用のstoryboard ファイルを作成して横画面のレイアウトを作るのが最短だと思います。 ですがそのstoryboardを確認すると1つのstoryboardにUINavigationControllerや複数のContainerViewが存在していましたので レッスンルームのControllerだけを切り分けして横画面用のstoryboardを作るのは難しそうだと判断しました。

そこで今回採用したのがSizeClassという技術です。 これを使って1 つのstoryboard内で完結する様に調整したのです。

SizeClassとは

SizeClassとはAppleが提供しているデバイスのサイズを判別するための新しいクラスでiOS 8から導入されました。 iPhone 4 / iPhone 4S までは 端末のサイズが 320 x 480 だったのが
iPhone 5 の登場で初めて320 x 480 以外のサイズで iOS端末が出てきたのがきっかけです。iPhone 5 は 320 x 568 で高さが高くなっています。
さらに翌年には iPhone 6 の登場で高さだけでなく横幅も320 以外のサイズが登場してしまいiOSアプリエンジニアは端末対応に追われていた当時が懐かしく感じます。
当時のAppleはこのようなサイズ違いの端末の対応として AutoLayout という技術を使って解決するように促していましたが、 今度はタブレット端末として登場したiPadでとうとうAutoLayoutでは対応しきれない状況になりました。
iPhoneiPad で別々でstoryboardを管理したりコードでView を切り分けたりはできますが同じViewでそれぞれ2つのクラスを用意するのは非常に大変です。
さらに端末回転もサポートすると縦横比が変わってレイアウトが崩れてしまいますね

そこで登場したのがSize Class というクラスです。

AutoLayoutやSize Class の使い分けは Appleの Human Interface Guidelines にある Adaptivity and Layout という記事が参考になります。

developer.apple.com

非常に簡略化してこれを説明すると

iOS端末のサイズを大きく2つの概念に分けました。

  • Regular (denotes expansive space)
  • Compact (denotes constrained space)

このRegular と Compactをそれぞれ幅と高さに当てはめると

  • Regular width, Regular height
  • Compact width, Compact height
  • Regular width, Compact height
  • Compact width, Regular height

のように4通りに切り分けられます。この4通りは上記の参考記事にも記載されています。
この概念をiPhoneiPadにそれぞれ適用させると iPadなどのタブレットはPortrait(縦向き)やLandscape(横向き)に関わらず Regular width, Regular height に、iPhone端末は一部例外がありますがPortrait(縦向き) のときは Compact width, Regular height, Landscape(横向き) のときは Regular width, Compact height になります。

このように分類分けするときの技術が Size Class になります。

Size Class の使い方について

これをレッスンルームのstoryboardに当てはめて iPad の横画面の場合にのみ レイアウトを大幅に変える対応を行います。

記事の冒頭でお見せしましたレッスンルームのstoryboardは次の画像のようになります。

f:id:qed805:20190814135735p:plain
lesson_room_storyboard

基準がiPhone 8 Plus となっていてこれをベースにタブレット端末で横画面のときにのみレイアウトを切り替える仕様になります。

storyboardのベースの切り替え方は次のようにします。

f:id:qed805:20190814135829p:plain
lesson_room_storyboard_2

iPadを選択します。そうすると次のようにstoryboardのviewのサイズがiPad Pro のサイズに切り替わります。

f:id:qed805:20190814135928p:plain
lesson_room_storyboard_3

このようになります。

また端末の縦向き・横向きの切り替えはOrientation を切り替えることでPortrait やLandscape に切り替えられます。
これがSizeClass を適用させる上での前提知識になります。

次にstoryboardを使っている場合はAutoLayoutでレイアウト間のマージンをそれぞれ設定していると思います。 SizeClass の場合はこれが肝となります。 AutoLaytoutのNSLayoutConstraint をそれぞれのViewにRegular Size(以下R)のときの値 , Compact Size(以下C)の時の値をそれぞれ設定することができるのです。

私の場合、当てはめ方としては

  1. Rは iPad の幅と高さのすべて
  2. Cは iPhoneの幅と高さ
  3. iPhone 6 以上の高さが 縦向き のときだけR
  4. Plus 端末の幅が横向きのときだけR ( iPhone XS MaxiPhone XR の幅は横向きのときにR)

のルールで覚えています。これでも例外として() の部分が漏れてしまいます。

今回の仕様はiPadの場合でかつ横向きだったときにレイアウトを変更すればよいので例外ルールは考える必要はありません。
1のルールに従ってiPadは基本RなのでR指定でConstraint を指定すればよいと考えます。
つまりはstoryboard 上で 通常時の constraint と R 用の constraint をそれぞれ設定します。

R用のconstraint の設定の方法を紹介します。

今回は参考として 画像の灰色のView(チャット部分のView)のbottomに Regular の場合は20 px マージンを取るように変更してみます。

f:id:qed805:20190814160752p:plain
lesson_room_storyboard_4

灰色のViewが選択された状態で新しく bottom のconstraint を指定します。 20pxとします。

f:id:qed805:20190814160928p:plain
lesson_room_landscape_5

20pxのbottom のconstraint を作るとエラーが発生しますがエラーを無視します。

f:id:qed805:20190814161044p:plain
lesson_room_constraint

f:id:qed805:20190814161140p:plain
bottom_constraint

作成した20px のbottom のconstraint をダブルクリックして編集画面を表示させます。

f:id:qed805:20190814161231p:plain
bottom_constraint_2

このように表示されたあとは一番下に「+ installed」の項目があります。
この左側の「+」をタップすると

f:id:qed805:20190814161355p:plain
bottom_constraint_3

CompactとRegularを選べますのでwidth / height ともに Regular を選択して「Add Variation」をクリックして
新しい「wR hR installed」が作成されます。

これが width Regular かつ height Regular だった場合の contraint です。
ただ2つともinstalled のチェックボックスが入っているときの挙動だと不安定になりますので
「wR hR installed」の部分だけチェックボックスが入るように1つ目のチェックボックスを外します。

f:id:qed805:20190814161834p:plain
bottom_constraint_4

これで Regular width と Regular height の場合のみこのconstraint が使われるようになります。
ですがこれだけだともともとのbottomの 0px マージンのconstraint がすべての場合に適用されますのでエラーは消えません。
そこでもともとのbottomの 0px マージンのconstraint の方を修正します。

bottomの 0px マージンのconstraint をダブルクリックしてconstraint の編集画面を開きます。

f:id:qed805:20190814162149p:plain
bottom_constraint_5

このcontraint に 「wR hR installed」のvariation を追加します。追加した「wR hR installed」のチェックボックスを外します。

f:id:qed805:20190814162312p:plain
bottom_constraint_6

この設定をすればさきほどのエラーが解消されたことが分かります。
これでiPad でRegular size だったときの constraint の追加が完成します。

ただSizeClass においてはiPadはPortrait とLandscape の両方で R ですので端末が縦向きであったときと横向きだったときのハンドリングができません。
そのためここで黒魔術を使って iPadで横向きのときには 高さを C に変換する必要がありました。

特定条件で 端末のSizeを Compact や Regular に変換する

前回のSizeClass の登場とともに端末回転で重要なUITraitCollection というクラスが登場しました。
端末の向きに関するクラスUIInterfaceOrientationが非推奨になり、UITraitCollectionを使って実装しなければなりません。
結論からいうとUITraitCollectionのoverrideTraitCollection(forChildViewController:)を使うことで端末の Regular size と Compact size を入れ替えることができます。
こちらのメソッドはUIViewControllerに存在します。
レッスンルームの画面はUINavigationControllerが持ちますのでNavigationController を継承したカスタムクラスを作成して これをUINavigationController に使います。

class SampleNavigationController: UINavigationController { // UIViewController であればこちらを継承させる

    /// SizeClassを意図的に上書きすることができるメソッド
    override func overrideTraitCollection(forChild childViewController: UIViewController) -> UITraitCollection? {
        let any = UITraitCollection(verticalSizeClass: .unspecified)
        let compact = UITraitCollection(verticalSizeClass: .compact)
        
        // landscape (ここでiPad 端末かつ横向きの場合の変換処理を返す)
        if [iPad端末のフラグ] && self.view.frame.width > self.view.frame.height {
            return UITraitCollection(traitsFrom: [any, compact])
        }
        
        // portrait
        return UITraitCollection(traitsFrom: [any, any])
    }
}

このようにコードを書いたクラスをプロジェクトファイルに追加してstoryboard のUINavigationController に継承させます。
これによって iPad で横画面になったときに検知して 高さが Regular から Compact に切り替わります。

f:id:qed805:20190814170037p:plain
iPad_convert

最後になりますが、今回紹介したSize Class で上のレクチャーは「wR hR installed」でしたがプロダクトコードを確認したら 「hC installed」とちゃんとheight がcompact のときのconstraint を設定していました。
「wR hR installed」ではなくて 「hC installed」になります。

これでiPad でかつ 横向き用のConstraint が機能して横向き用のレイアウトが表示されるようになります。
この技術を使って1つのstoryboard でiPhoneiPad それぞれ別々のconstraint を設定して見栄えの異なるデザインを設計できました。

このようにうまくSize Class を利用すると特定のstoryboard にだけレイアウトの違うデザインを実現させることができます。
ぜひSize class を使ってみてください。

最後に勉強会イベントの話しを

余談としてイベントの告知をします。 来週28日(水)にメドピア社と共同で「IT x 社会貢献」をテーマに勉強会を開催します。

medpeer.connpass.com

レアジョブとしては「学習体験の向上」を軸に、アプリ開発、データ活用をメインコンテンツとしてお話します。
私はアプリ開発者として「レアジョブアプリでのアクセス負荷で生じたAPI遅延問題をアプリエンジニア視点で対策を考えてみる」をテーマにして登壇します。
近年のモバイルアプリの開発においてもはやサーバーサイドとの連携は切っても切れない役割を果たしています。 今回のイベントではそのAPIを考慮したWebRTCとの連携開発で起こり得たツラミを知見としてまとめて発表する予定です。

懇親会もあるので楽しめるイベントになると思います。 ご興味のある方はぜひこのイベントに参加していただければいいなと思います。

Pug + gitlab pages + fractalでちょっとだけ幸せなコーディング作業

ジャンボです。 暑い日が続きますが、みなさん。

カポエラやってますか?

私はやっています。

最近の実験

さて。 ちょっと丸っとマークアップする案件があり、実験してみた件です。

f:id:jumbos5:20190815185438p:plain

ざっくり書くとこんな感じのフローです。

① AppのPush

Pug + Sass + gulpな構成にしたのは好きだからです。アイコンも全体的に夏っぽくて可愛いです。

② GitLab-CI

要件として社内から適当にHTMLとかホスティングして見たいというのがありつつも、上記の技術セットでマークアップをしたかったので、社内で運用されているGitLabのPagesでうまいことやれないかと考慮した結果、なんやかんやありつつもうまくいきました。 AWSのリソースを使うのも考えたんですが、一番ミニマムでやる方法を考えた結果ここに行き着きました。

GitLab pagesでは .public がデフォルトでhostingされるためにGitLab CIでmaster push時にビルドして出力されたassetsをコピーしているだけです。

// .gitlab-ci.yml
pages:
  script:
  - npm i
  - npm run build
  - cp -r public .public
  artifacts:
    paths:
    - public
  only:
  - master

Github pagesは経験があったんですが、社内にホスティングされたGitLabで動かすのには少しつまずきました。

③ Build & Deploy to Pages, ④ Hosting to Pages

実質やることはないです。 .gitlab-ci.yml を入れておけばよしなに動きます。

またこの時に fractal を設定しておけばこちらも一緒に Pages上で見れるので あとからstyleguideを見たいというときもリポジトリをcloneしてビルドしなきゃという手間もなく楽チンですね。fractal-pug-adapterを入れればpugも使えます。

ミニマムな構成で◎

Vue + Storybookな構成も考えましたが、技術要件上そこまでリッチにする必要もなく開発速度と構成のミニマム化を考えてこの構成でマークアップを進めてみました。GitLab Pagesの仕様でURLが {group name}.{domain}/{application name} みたいになっちゃうので参照パスが絶対パスでやると面倒なことになるのでここだけ工夫が必要です。

あとはローカルで開発するときと大差なく動くのでチームでさくっと静的なページを作るときはこの構成もありだなという感じでした。

サーバレスで外形監視を実装しオンプレとの違いを検証してみた

はじめに

レアジョブのプラットフォームチームでチームリーダーをさせていただいている塚田です。 Microservicesで共通基盤を作るチームですが、チームの特性上コンテナやLambdaを触ることが多いチームです。 今回はLambdaについての記事です。

課題

サービス提供会社として、自身が提供しているサービスが問題なく動いているか監視する必要があります。 監視という観点でいうと、サーバのリソースを描写し、閾値を超えた場合通知するよう作るのも必要ですが、 実際ユーザーはどのくらいの体感速度だったのかという観点は非常に重要です。

ユーザーの体感を定量的に可視化するため、我々は昔からオンプレのZabbixを用いて外形監視の一つとしてユーザー視点でレスポンスタイムを観測してきました。 しかし、そろそろオンプレの運用も脱出したい。願わくばサーバ(IaaS)を使わず運用したい。

そんな中、Lambdaでカスタムランタイムをサポートしたというニュースが2018年に流れたので、 シェルを動かせばソッコー実装できるのではと思い試してみることにしました。

やってみた

まず、LambdaのLayersを作成します。 ここでLayersを作る目的は、大きく2つありました。

  • シェルを実行できるようにする
  • awscliを使えるようにする

という理由です。Layer化しておけば外部ライブラリを簡単にインポートできるようになります。

ソースコードは公式が公開 チュートリアル – カスタムランタイムの公開 - AWS Lambda しているものを少し調整しています。 以下のような感じです

#!/bin/sh
set -euo pipefail

# tmpフォルダ内にawscliを配置するためHOMEとPATHを設定
export HOME="/tmp"
export PATH="$HOME/.local/bin:$PATH"

# install pip
cd /tmp
curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --user

# install awscli using pip
pip install awscli --user 

# ハンドラ関数の読み込み
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

# Processing
while true
do
    HEADERS="$(mktemp)"
     
    # Get an event
    EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
     
    # ハンドラ関数を実行
    # Execute the handler function from the script
    RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
     
    # 結果を返却
    # Send the response
    curl -kL -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"  -o /dev/null 2> /dev/null
done

その後、zip化。zip awscli.zip bootstrap runtimes は python3.7 を指定します。

コマンドでLambdaへuploadする際は、以下のようなコマンドで実施できます。

aws lambda publish-layer-version \
    --layer-name awscli \
    --zip-file fileb://awscli.zip \
    --compatible-runtimes python3.7

もし、コンソール画面から行う場合はここから登録します。

f:id:sumito1984:20190807105432p:plain
Lambda-Layers

ここまでは準備段階です。

その後、Lambda関数用にシェルを作成します。

function handler () {
    res=$(curl -kL 'https://www.rarejob.com/dna' -o /dev/null -w "%{time_total}" 2> /dev/null)
    aws cloudwatch put-metric-data --metric-name responseTime --namespace responseTime --value $res --dimensions Name=Time,Value=second 
}

運用時はURLをここにベタに書かず、DynamoDBから参照させたりした方が良いと思いますが、今回は便宜上ベタ書きにします。

今回の監視対象はDaily News Article。レアジョブで最も人気のある、日替わりのニュース教材です。 www.rarejob.com

その後、 function.shをzip化。zip function.zip function.sh Lambda関数を登録します。

f:id:sumito1984:20190807112235p:plain
lambda関数

ランタイムはカスタムランタイム、 ハンドラーはfunction.handlerをしてください。

Lambdaの実行IAMに

  • AWSLambdaBasicExecutionRole
  • CloudWatchの書き込み権限

を与えてください。

レイヤーには準備としてuploadしたLayersのawscliを指定します。

f:id:sumito1984:20190807112533p:plain
Layers

トリガーには、CloudWatch Eventsで、スケジュール式: rate(3 minutes) を設定。 今回は3分単位で監視するようにします。

あとは、CloudWatchのダッシュボードをよしなに設定すれば、描写を確認することができます。

f:id:sumito1984:20190807113034p:plain
CloudWatch

既存の監視(オンプレのzabbix)と比較してみる

f:id:sumito1984:20190807113112p:plain
zabbix

異常値を無視すると、 オンプレのzabbixは0.1 秒程度のレスポンスタイムを確認 一方で今回のLambdaで実施したレスポンスタイムは0.3秒程度でした。

考察

レスポンスタイムが、オンプレのzabbixとLambdaで一致しているわけではないというのが注意です。

  • 単純に処理のタイミング。
  • zabbixに組み込まれているレスポンスタイム処理と、今回curlコマンドでのレスポンスタイム処理自体の違い。
  • zabbixはオンプレで動いているが、Lambdaは毎回起動される度に同一サーバではない可能性があるという点。

などなど、考えられるので原因は複合的かもしれません。 現在は検証段階なので、外形監視の本格移行などは予定はしておりません。

今回の検証は、いちエンジニアの「やりたいこと」に過ぎなかったのですが、レアジョブでは「やりたいこと」が推奨されている会社です。 その中では、サービスの一部としてとして形になっているもの、検証だけで終わってしまったもの様々です。

しかしどんどんチャレンジして、世の中にアウトプットを出して行こうと思っています。

レッスンルームをリファクタリングした話

初めましてAPP・UXチーム/フロントエンドエンジニアの大谷です。
今回は入社(今年5月)して初めて関わったレッスンルームのリファクタリングのプロジェクトを事例にリファクタリングの進め方・効果・反省点などまとめていきたいと思います。

経緯

弊社ではレッスンルームという英会話の授業をするためのプロダクトを提供しています。 レッスンルームについて詳しくはこちら

現在のレッスンルームは先生側のシステムと生徒側のシステムが別々のプロジェクトで管理されていて、それぞれ異なるフレームワークで作られています。また、一部独自で作成したライブラリも含まれており、機能改善を続けていくにあたって今の構成では属人化してしまう問題があり、対応が難しくなってくることが予想されたためリファクタリングしよう!という運びになりました。

リファクタリングの進め方

方針決め

リファクタリングしていくにあたって、目的や技術的な方針など軸を決めておくことで効率的に作業が進められるようになります。

リファクタリングの目的

リファクタリングは機能追加などと違って見た目の変化がないため、何がゴールなのかわからなくなる怖さもあります。目的をしっかり持つことでスコープが絞られエンドレスな開発にならずにすみます。
今回は以下の目的があげられました。

  • 不具合の発生原因を特定しやすい改善をしやすいシステムにする
  • テストしやすい環境の整備
  • 保守性の向上
    • 講師・生徒のシステムを一つのプロジェクトで管理し、ロジックを共通化
      • SDKの更新がまとめてできる
      • 属人化の解消
技術構成

技術選定は講師側はAnguler JS→Vue.jsに変更し、生徒側と同一のプロジェクトで管理するようにしました。独自で作成したライブラリを廃止し、Ajax通信ライブラリはVue.jsで推奨されているaxiosを使うなどスタンダードに沿ったものを選定するようにしました。 f:id:kinokonotani5656:20190729223120p:plain ディレクトリ構成はAtomic Designを採用し不明確だったコンポーネントの単位に基準を設けました。

いよいよ実装

仕様の整理

既存のプログラムからリファクタ後のプロジェクトにコードを書き換える際に、明文化されてない仕様や複雑になってしまっているロジックに出会うことがあります。
そんな時は下記のように整理しました。
フローチャートを書くなどして仕様を整理→チーム内で合意→無駄な部分は廃止してスリム化
既存の仕様だからという理由で残すのではなく、都度仕様を整理してわかりやすくい構成にしていくサイクルをスピード感を持って行えたのがとてもよかったと思います。

コーディング

見通しのいいコードにして行くために行ったTipsはこちらの記事をご参照ください。 また今回、Ajax通信の仕方をXMLHttpRequestからaxiosを使うように変更しました。変更した際にカスタムヘッダーに設定漏れがあり、Ajax通信として判定してくれず少々ハマったので設定方法を残しておきます。

axios.create({
  baseURL: baseUrl,
  headers: {
    'X-Requested-With': 'XMLHttpRequest', // この設定が必要
    'Content-Type': 'application/json'
  }
  timeout: timeout,
  responseType: 'json'
})

新しいライブラリを導入する時などは特にunitテストを作成し、前後で意図しない変更がないことを確認できる仕組みを事前に整えておくことをおすすめします。

検証環境の整備

今回はコードのリファクタリングだけでなく、検証環境の改善も行いました。 レッスンルームの開発中にちょっとした表示確認をフィリピンのスタッフと行いたい場合、今までは修正してから確認対象であるレッスンルームにアクセスするために、STG環境を使用していためスケジュール調整をしたり、レッスン予約をして指定した時間にならないとレッスンルームに入れないなどとても手間がかかっていました。

f:id:kinokonotani5656:20190729223304p:plain

QAなど本番と同等の条件でのテストの場合は話が変わってきますが、表示や軽微な動作・疎通チェックなどであればレッスンルーム入室前のプロセスはカットし、修正したらすぐデプロイして確認できる環境がほしい。。という思いがありAWS上にフロントの動作確認用の環境を作ることになりました。 f:id:kinokonotani5656:20190729223317p:plain コマンドをたたくだけでローカルからデプロイ可能にし、必要なデータはLambdaを使ってモックデータを受け取れるような構成になっています。Lambda/API gatewayについての詳しい内容はまたいつか。
この取り組みで他チームと干渉することなく簡単に動作確認を行えるようになりました。今後の開発を続けていくにあたってとても大きな改善になったと思います。

効果

今回リファクタリングをしてみて、以下のような効果がありました。

  • チーム内でのシステムへの理解度が高まった
  • 見えていなかった課題が見えてきた
  • 日本とフィリピンで保守が協力しあえるようになった(独自のライブラリの廃止・ロジックの共通化などで)
  • 適切なログを完備することで不具合調査しやすくなった
  • 手軽に日本・フィリピン間での動作確認ができるようになった

リファクタリング工数もかかりますし、見た目の変化がないわりに手間もかかります。 ですが普段あまり改修が入らない部分のコードをみる機会ができるので表面化していなかったバグの温床に気づくことができたり、なによりシステムへの理解度が高るので改善のアイディアがでてくるなどメリットの方が大きいように思います。

反省点

技術構成や方針など最初に固めていても、進めてみてからやはりこうすればよかったというところもでてきました。

  • lintやフォーマッターは最初にしっかり
    今回stylelintなど設定していましたが、ゆるめの設定にしていました。ですが後から変更したくなった場合差分が大量に出てしまうため、設定するのであればなるべく最初の方に検討した方がいいです。
  • store(Vuex)の構成
    storeの定義を機能単位にしていて定義が曖昧でした。pages/organizmsのみでstoreに接続し、それ以外はprop inで情報を受け渡すなど定義を明確化しておくとよかったと思います。

反省点も次のスコープで改善していってさらに見通しの良いプロダクトにしていければと思います。

まとめ

リファクタリングによってコードの見通しがよくなっただけでなく、チーム内でシステムに対しての理解度が深まる→次の改善のアイディアが生まれるなどプラスの連鎖もありました。 今回でてきたアイディアや課題など一つ一つ改善していって、もっと快適なレッスンの場を提供できればと考えています!

Amazon CloudWatch Automatic Dashboards を利用した簡易ダッシュボード表示

はじめまして。DevOps チームのおくさんです。現在は弊社オンライン英会話サービスのインフラに関わる業務を行っております。好きな AWS サービスは Elastic Load Balancing と Amazon Route 53 です。

弊社では以下の記事にて記載がございます通り、パブリッククラウドとして AWS を主に利用してオンライン英会話サービスの運営を行っております。

appeal.rarejob.co.jp

AWS にはリソースモニタリング関連のサービスとして Amazon CloudWatch というサービスがあり、弊社でももちろん利用しております。しかしながら、ダッシュボードの作成において、各 EC2 インスタンスのメトリクスを取得してダッシュボードのウィジェットを作成して・・・と非常に手間がかかるなと感じていました。

そんな中、Amazon CloudWatch Automatic Dashboards という機能を利用するだけで、AWS サービスの各種標準メトリクスを利用したダッシュボードを簡単に表示できるということで、弊社でも利用を始めてみました。 今回は、この Amazon CloudWatch Automatic Dashboards についてご紹介しようと思います。

目次

Amazon CloudWatch Automatic Dashboards とは

Amazon CloudWatch Automatic Dashboards は、約半年前の 2018/11/20 にリリースされた機能であり、AWS のサービスが推奨するベストプラクティスにあわせて、AWS リソースの状態とパフォーマンスをまとめてダッシュボードに表示することができる機能です。リリースニュースは以下をご参照下さい。

aws.amazon.com

自動ダッシュボードは、AWS のサービスが推奨するベストプラクティスにあらかじめ構築済みであり、リソースを認識したまま、動的に更新して重要なパフォーマンスメトリクスの最新状態を反映します。

こちらの機能は、AWS Resource Groups を利用することにより、 Resource Group 単位で簡単に各種サービスのメトリクスを利用したダッシュボードを表示させることが可能です。そのため、以下のようなダッシュボードを、特定の用途毎にグルーピングした AWS リソースで簡単に表示することが可能となります。

ダッシュボード1ダッシュボード2
ダッシュボード

ダッシュボードの表示をやってみる

さっそくですが、Amazon CloudWatch Automatic Dashboards と AWS Resource Groups を利用した、Resource Group 単位 のダッシュボードを表示してみましょう。2019/07/22 現在の AWS Management Console を利用した手順は、大きく以下のような流れとなります。

  1. AWS Management Console にログインする
  2. Resource Group より、グルーピングを行う AWS リソースにタグを設定する
  3. 設定したタグをもとに、Resource Group を作成する
  4. Amazon CloudWatch より、作成した Resource Group のダッシュボードを表示する

それでは、それぞれの手順についてご紹介します。なお、ご紹介におきまして AWS Management Console のスクリーンショットを利用していますが、AWS 側のアップデート等により画面の構成が変更になる可能性がありますので、予めご留意下さい。

1. AWS Management Console にログインする

こちらに関しては、特に説明は不要かと思います。以下の URL より、AWS Management Console にログインして下さい。

https://console.aws.amazon.com/console/home

2. Resource Group より、グルーピングを行う AWS リソースにタグを設定する

AWS サービスのリソースに対し、Resouce Group で利用するためのタグを設定していきます。今回は例として、Amazon EC2Amazon RDS に対しタグの設定します。また、設定するタグは以下とします。

タグのキー タグの値
Role SiteA

EC2 タグRDS タグ
タグ

3. 設定したタグをもとに、Resource Group を作成する

「2.」で作成したタグを利用して、Resouce Group を作成します。リソースグループは、AWS Management Console の上部にある「リソースグループ」より作成を行います。

リソースグループ
リソースグループ

Resouce Group の作成画面になりますので、以下の情報を入力します。

  1. 「タグベース」を選択
  2. リソースタイプは「AWS::EC2::Instance」と「AWS::RDS::DBInstance」を選択
  3. タグキーを「Role」、オプションタグを「SiteA」と入力し、「追加」ボタンを押下
  4. 「グループ分けの条件」上部にある「グループリソースの表示」ボタンを押下
  5. 「グループリソース」項目に、設定したタグが付与されているリソースが表示されることを確認
  6. グループ名など必要な情報を入力し、「グループの作成」を実施

リソースグループ作成
リソースグループ作成

以上で、Role:SiteA というタグを利用したリソースグループの作成が完了しました。

4. Amazon CloudWatch より、作成した Resource Group のダッシュボードを表示する

Resource Group の作成が完了しましたら、AWS Management Console より、CloudWatch サービスを選択して下さい。CloudWatch のページが表示されますので、プルダウンメニューより、作成した Resouce Group 名を選択します。

CloudWatch
CloudWatch

Resouce Group 名選択しますと、以下のように Resource Group でグルーピングされたリソースのダッシュボードが表示されます。

SiteA ダッシュボード
SiteA ダッシュボード

また、「EC2 ダッシュボードの表示」をクリックすると、EC2 に特化したダッシュボードが表示されることが確認できます。

EC2 ダッシュボード
EC2 ダッシュボード

さいごに

いかがでしたでしょうか。今まで疲弊していたダッシュボードの作成が大変楽になり、私自身様々な役割毎のリソースでダッシュボードを作成しております。もし、監視メトリクスのダッシュボード作成にお悩みの方は、ぜひ一度お試し頂ければと思います。

それでは、みなさま良いダッシュボードライフを!!!

PlantUML で仕様を明確に、常に最新に更新する

どうも。システムディレクションチームのいとうです。 幼少向け学校法人様向け などいろいろなサービスの開発側ハンドリングをしています。

さて、いろいろな要求仕様を具体的な設計に落とし込んでいったり、既存の仕様を整理するために、ユースケースフローチャートを作成したり ER 図を書いたりすることはよくあるかと思います。

こういう図って、性格が出ますよね。 ツールによってはどうしても線が曲がってしまったり、きれいに整列させることが難しかったり、またせっかくきれいに書いていても、途中で間にオブジェクトを追加するとレイアウトが崩れたりなかなか厄介です。オンライン・オフライン問わず、ライセンスの数が足りなくなったりもありますよね。 また、最終的に出力されるのが画像なので、変更差分を目で見ると大変ですし、履歴を残すにしても人によって変更履歴の粒度はまちまちです。

そこで、PlantUML

f:id:lunasys:20190716165553p:plain
sequence_sample
こんなのや
f:id:lunasys:20190716165819p:plain
usecase_sample
こんなのがテキストベースで簡単に管理できてしまいます。

IDEプラグインとしては、 VS Code 用 だったり IntelliJ 用 があるのでローカル環境では瞬殺で導入することができます。 Markdown にも対応しているのですんなり Git 管理下にも置くことができます。

とはいえ、非エンジニアがプラグインを入れないと見れないのは辛い。そのために、わざわざ画像で出力するのはめんどくさい効率が悪すぎる。 Confidential な内部仕様を 公式の PlantUML Server を利用して描画するのは問題があるため、専用のインスタンスAWS Fargate で立ち上げてみましょう。

AWS Fargate で立ち上げる

  1. クラスター作成
    適当な VPC 配下にクラスターを作成します。直接グローバル IP を付与しないのであれば、 ALB を立てる必要があるので、サブネットは最低 2つの AZ が必要です。
  2. タスク定義
    FARGATE タスクを作成します。タスクロールはデフォルトの ecsTaskExecutionRole のままでいいでしょう。タスクサイズは最低の 512MB メモリと 0.25vCPU でもとりあえず問題ないです。コンテナイメージは、 公式のものを そのまま指定します。 (plantuml/plantuml-server:tomcat)
  3. サービスの追加
    作成したクラスターからサービスを作成します。起動タイプ FARGATE タスク定義に、作成したタスク定義を指定します。タスクの数もまずは 1 でいいでしょう。ネットワーク構成、AutoScaling (必要あれば) を完了すれば、自動的にコンテナが起動します。

起動したコンテナの http://[INSTANCE HOST]:8080/uml もしくは ALB 経由の http://[ALB ARN]/uml でアクセスできれば準備完了です。

Confluence で利用する

Confluence では、 Cloud 向け には Draw.io が、 Server 向け には avono AG がそれぞれアプリを公開しています。

Gitlab で利用する

弊社では Git 環境で主に Gitlab を利用していますが、公式で PlantUML に対応しています。 https://docs.gitlab.com/ee/administration/integration/plantuml.html#gitlab から有効にし、 PlantUML URL にコンテナの URL を入力すれば、 WikiIssue でインライン Markdown で PlantUML が使えるようになります。 Issue で細かい仕様を詰めつつ、成果物を Confluence に上げていくような使い方もできますね。

GitHub で利用する

GitHub ではいまのところ、 まだ検討の前段階 のようですね。 クラスメソッドの中の人が Chrome 拡張 を公開してくれているようです。

さいごに

絵心がある人もない人も、UML チャートならみんなが共通の認識を持つことができます。 複雑化するプロダクトも、エンジニアと非エンジニアも、UML チャートで正しく最新に管理しましょう。

UML を書いたことがある人もない人も、一緒にいいプロダクトを作りたい方、ぜひお待ちしています!