新年なのでAWSのリソースだけで1年の目標管理マイクロサービスを作ったお話

久保田です。

2017年が始まりましたね。。。
早いもので今年の4月で3年目、今年はますます頑張りたいなと思います。

僕は毎年、年始に意識高く目標を立てているのですが、
だいたい3月くらいには忘れてしまっています。笑

なので今年は忘れないよう、目標管理をしてくれるマイクロサービス的なシステムを作ったので、書きたいと思います。
ぜひ真似してください。

今回はAWSのサービスのみで構築しました。構成は以下のような感じです。

f:id:AdwaysEngineerBlog:20170113150018p:plain

そういえばAWS、コンソール画面変わってましたね。これで伝わるのかな。。。

CloudWatchのスケジュール機能でLambdaの関数を動かし、3ヶ月に一度目標のメールを自分に送ります。
送られてくるメールは以下の画像のようなイメージです。

f:id:AdwaysEngineerBlog:20170113150108p:plain

そしてメールには目標とそれぞれの目標に対応したURLがあり、そのURLをクリックするとAPI GateWayにリクエストが送信され、Lambdaが動き、S3にある目標データが入っているjsonファイルを書き換えます。さらに目標が全て達成されたら、CloudWatchのスケジュールをdisableにします。

では早速作っていきます。

S3に目標データを作る

まずはS3にデータを置いておきます。
今年の目標を意識高く掲げておきましょう。

  • goal.json
{
  "1": "腹筋を割る",
  "2": "引っ越す"
}

このファイルをS3のどこかにuploadしておきます。

Lambda関数を作っておく

先にLambdaの関数を作っておきます。
先に作っておかないとCloudWatchのスケジュールが登録できないためです。

今回は、 S3から目標ファイルを読み込みメールを送る関数と、
API GatewayにリクエストがきたらS3の目標ファイルを更新する関数
の2つが必要となります。

適当に名前をつけてください。
僕は メールを送る関数を 「sendNotify」
更新する関数を 「clear」
としました。

実装はまた後で行います。

CloudWatchにスケジュールを登録する

次にCloudWatchにスケジュール登録します。

左ペインからイベントの下にある ルール を選び、以下のCron式を登録します。

0 10 1 3,6,9,12 ? *

そしてこのスケジュールに紐付けるLambda関数を選択します。

f:id:AdwaysEngineerBlog:20170113150131p:plain

API Gatewayでエンドポイントの作成

API Gatewayを使ってエンドポイントを作成します。
このエンドポイントにリクエストがきたらS3の目標ファイルを更新します。

今回は目標が達成されたら対象のデータを消すので、DELETEメソッドで作ります。
そして消す目標のIDをパスパラメータで受け取るようにします。

なのでリソースとメソッドは以下の画像のようになります。
(※目標更新の方のLambda関数を紐付けておいてください。)
(※ GETがある理由は後でわかります。)

f:id:AdwaysEngineerBlog:20170113151940p:plain

そしてマッピングテンプレートを設定し、パスパラメータをLambdaが受け取れるようにします。

f:id:AdwaysEngineerBlog:20170113150250p:plain

そしてDELETEなので、ステータスコードは204を返すようにしておきます。

メソッドレスポンスと統合レスポンスを編集します。

f:id:AdwaysEngineerBlog:20170113150231p:plain

f:id:AdwaysEngineerBlog:20170113150214p:plain

そしてデプロイしておきます。
デプロイしたら得られるエンドポイントをメモっておいてください。

これでDELETEメソッドは完成です。

次にGETメソッドの設定をしていきます。
なぜかというと、メールからURLをクリックした時に発行されるリクエストのメソッドはGETだからです。
なので、このGETを通って上で作ったDELETEメソッドに流します。

このようにしておきます。

f:id:AdwaysEngineerBlog:20170113151801p:plain

エンドポイントURLのところに上でメモしておいたエンドポイントを入れてください。

SNSの設定

Lambdaからメールを送る必要があるので、SNSにトピックを作ります。
ここも名前はなんでもいいです。僕は「goalNotify」としました。

購読の設定まで完了させておきます。

Lambda関数の実装

ここまでで型が完成したので、中身のLambda関数を作っていきます。
僕はPythonを使いました。

まずはS3から目標ファイルを読み込みメールを送る関数です。

import boto3
import json
import collections

BUCKET = 'バケット名'
KEY    = 'ファイル名'

def lambda_handler(event, context):
    # S3からファイルの読み込み、jsonからdict型のオブジェクトの生成
    s3 = boto3.client('s3')
    obj = s3.get_object(Bucket=BUCKET, Key=KEY)
    decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict)
    jsonObj = decoder.decode(obj['Body'].read())
    

    # メール本文作成
    message = ""
    for k, v in jsonObj.iteritems():
        message += v + "\n" + " if you clear this goal, click under link. \n ここにエンドポイント"  + k + "\n\n"
    
    # SNSからトピックのarnを取得  
    sns = boto3.resource('sns')
    topic = sns.create_topic(Name="トピック名").arn
    # メール送信
    sns.Topic(topic).publish(
        Subject="goal of 2017.",
        Message=message,
    )


    return

次にAPI GatewayにリクエストがきたらS3の目標ファイルを更新する関数です。

import boto3
import json
import collections

BUCKET = 'バケット名'
KEY    = 'ファイル名'

def lambda_handler(event, context):
    # S3からファイルの読み込み、jsonからdict型のオブジェクトの生成
    s3 = boto3.client('s3')
    obj = s3.get_object(Bucket=BUCKET, Key=KEY)
    decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict)
    jsonObj = decoder.decode(obj['Body'].read())
    
    # 対応するgoalIdがあったら消して更新
    if event['goalId'] in jsonObj:
        del jsonObj[event['goalId']]
        jsonStr = json.dumps(jsonObj)
        s3.put_object(Bucket=BUCKET, Key=KEY, Body=jsonStr)
        
        # もし全て完了したら、スケジュールを止める。
        if len(jsonObj) == 0:
            cloudwatch = boto3.client('events')
            cloudwatch.disable_rule(
            Name='sendNotify'
        )

        
    return

これで完成です。 試したい方はS3から目標ファイルを読み込みメールを送る関数を動かしてみてください。

さて、あとは3ヶ月後1つでも押せるように日々努力していくだけ、、、ですね!

今回は以上です。今年もよろしくお願いいたします。

新年明けましておめでとうございます

f:id:AdwaysEngineerBlog:20161227144327j:plain

謹んで新年のお慶びを申し上げます。

昨年は大変お世話になりありがとうございました。
本年も昨年同様よろしくお願い申し上げます。
皆様のご健康とご多幸を心よりお祈り申し上げます。

平成29年元日

株式会社アドウェイズ
サービスデベロップメントグループ一同

すげーロゴ

こんにちは、久保田です。

2016年も終わりですね。。。

私達エンジニアブログもこの投稿で今年最後の更新とさせていただきます。

今回はちょっと変わったプログラムを作りました。

こちらはアドウェイズのロゴです。

f:id:AdwaysEngineerBlog:20161118192801g:plain

これをRubyのプログラムにしてみました。

gist81462828b3e6db3179d2c5e8285fa575

そしてこれを動かすと、、、

f:id:AdwaysEngineerBlog:20161227141919p:plain:w300

アドウェイズのスローガンが出力されます。

gist埋め込みにしたらなんか縦長になっちゃいましたが、、、 コンソールで見るとこんな感じです。

f:id:AdwaysEngineerBlog:20161227142754p:plain:w300

会社のロゴをかたどったプログラムを実行すると会社のスローガンが出力されるというおしゃれプログラムです。

unicodeの該当するコードポイントを出したり、shift-jisのコードポイントを計算したり、2進数から文字列になんやかんやしたりしてます。笑

ぜひコピペして実行してみてください!

ではよいお年をお過ごしください。
今年もありがとうございました。🐵

Scalaでマイクロサービス化を進めるために考えたこと

Adways Advent Calendar 16日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar/archive


こんにちは、古川です。現在アドテクチームに所属しています。

まさか、まさか一番最後になるとは思っていませんでした。。。あまりでかいことを書ける自信は持ちあわせてませんが頑張って書いていきたいとおもいますのでよろしくお願いします。

アドウェイズのアドテクチームでは、Perlで運用してきましたが、現在Scalaで既存サービスに対してマイクロサービスを進めてきています。

それを実践するにあたって、出てきた課題をどのように改善したかについて共有したいと思います。

マイクロサービスで取り組むにあたって出てきた課題

ビジネスロジックがWebフレームワークや具体的な実装に縛られていた

いままでの既存サービスはPerlのCatalystであり、MVCの考えで実装されていましたが、ビジネスロジックがコントローラやモデルに相当する箇所に書かれていたりしました。

「コントローラにビジネスロジックが書かれている」にはフレームワークのものと密結合しているため、このような問題がありました。

  • テストコードが書けない
  • つぎのことをやろうとしたときにビジネスロジックにも影響が及んでしまう
    • フレームワークのアップデートや切り替え
    • Webアプリの機能の切り捨て

一方、「モデルにビジネスロジックが混ざって書かれている」ケースについては、ビジネスロジックがモデルと結びついているため、これらの問題がありました。

  • 使われ方と結びついているため、そのモデルが複雑になる
  • モデルのテストコードも複雑になる
  • モデルに関するライブラリのアップデートなどをしようとしたとき、ビジネスロジックにも影響が及んでしまう

両方に共通している問題点は、ビジネスロジックがフレームワークや具体的な実装に縛られているような書き方にあり、これを防ぎたいと感じていました。

この問題については、つぎの手段で解決を試みました。

ヘキサゴナルアーキテクチャーの考えをプロジェクトに採用

「実践ドメイン駆動設計」の本を読んでいったところ、4章あたりにある「ヘキサゴナルアーキテクチャー」というのを見つけました。「ヘキサゴナルアーキテクチャー」はビジネスロジックが書かれるドメインやそれ以外の実装と明確に分離できるようなモデルだったので、まずはこの考えを取り入れることを検討しました。

他社さんの実装事例がないか確認

まずは、他社さんがすでにアーキテクチャーで実装している事例がないかさがしました。

するとセプテーニさんのところで「ヘキサゴナルアーキテクチャー」でプロジェクト構成している事例を見つけました。

Scalaで学ぶヘキサゴナルアーキテクチャ実践入門

最初は、ここに使っているプロジェクトをそのまま採用しようとしましたが、以下のような事情があったためしませんでした。

  • ほとんどの人がPerlやMVCフレームワークしかさわっていなかったため
  • 今までの方式と異なるこの手法を教えるのには時間がかかる

上記を参考にプロジェクトにヘキサゴナルアーキテクチャーをどう落とし込むか考えました。

WebフレームワークなどのUIやバッチについて

WebフレームワークなどのUIやバッチのエンドポイントと、ビジネスロジックであるドメインはプロジェクト単位でわけるようにしました。

具体的にはつぎのようにわけました。

  • web:Webフレームワークのコントローラなどを管理するところ
  • batch名:バッチの処理を管理するところ
  • core:ビジネスロジックなどのドメインを含む

そして、core以外のプロジェクトにビジネスロジックを埋めないようにしています。

このような方法をとることで、Webフレームワークなどとビジネスロジックを分離させることにしました。

MySQLへのアクセスする処理などについて

MySQLへのアクセスなどの処理については一旦coreのなかで管理することにしましたが、アプリケーション層・ドメイン層・インフラ層の3層に分けることで、ビジネスロジックと別々に管理するようにしました。

  • アプリケーション層:(使い所がまだ理解していないものの)Webフレームワークとビジネスロジックの間で調整する役割
  • ドメイン層:ビジネスロジック全体
  • インフラ層:DBアクセスなどの具体的な実装

DIパターンを採用を検討

ここまでの方法だけだと、書き方によってドメイン層がインフラ層に依存してしまいます。

そこで「DIパターン」を使って、ドメイン層がインフラ層に直接依存させないよう試みました。

従来のcake patternについて

まずは、一番最初に開発したScalaプロジェクトでは、こちらのページのCake Patternを採用していました。

Cake Pattern を理解する - daimatz.net -

この方法はぱっと見が複雑だったので、採用しませんでした。

Google Juiceを使った動的DIについて

つぎに、Google Juiceを使った動的DIの実装も検討しましたが、こちらについてはこのような問題があり、採用しませんでした。

  • Google Juiceの使い方について学ばなければいけない
  • 書き方も色々あってどれを使えばいいかわかりづらい
  • DIの仕組みがライブラリに依存してしまうので、ライブラリのアップデートで影響を受けてしまう
Minimal Cake Patternについて

さいごにドワンゴが考えた「Minimal Cake Pattern」という実装の仕方について検討しましたが、今回はこちらを採用しました。

Scalaにおける最適なDependency Injectionの方法を考察する 〜なぜドワンゴアカウントシステムの生産性は高いのか〜

採用した理由としては、

  • 従来のcake patternと比べて「Mixin」や「継承」のみしか使っていなく見た目が比較的シンプルだった
  • Google Juiceのように特定のライブラリに依存することがなかった

と、他の手法と比べてデメリットが克服されていたと感じたからです。

この手法をドメイン層からインフラ層を呼び出す箇所で利用しました。

Scalaのマイクロサービス化でやるべきことが非常に多かった

1つ目のマイクロサービスがある程度でき、この考えを他のチームに展開しようとしましたが、

プロジェクトを作るだけでも考えることが非常に多いことに気づきました。

たとえば、

  • 環境設定はどこに配置すればよいのか?
  • DBの接続はどのようにするのか?
  • ログはどのように吐かせるのが望ましいか?
  • プロジェクトの分離はどのようにすればいいのか? などなど

を考慮しなければいけませんでした。

ほかにも、sbtやlogbackの初期の設定でハマっていました。

ここに時間が割かれることを避けたかったため、つぎの手段をとりました。

最初のマイクロサービス化で作成したプロジェクトをベースにテンプレートを作成

そもそもうちの会社では、テンプレートのようなプロジェクトを持っていませんでしたので、今回それを持つことでこの手間の省力化を図ろうとしました。

そこで最初のマイクロサービス化で作成したプロジェクトをベースに、不要な箇所を取り除くことでテンプレートを作成することにしました。

というのも、このプロジェクトではある程度アーキテクチャーが固まっていたからです。

今回つくったテンプレートについては、簡単な実装例も混ぜたものを公開します。もしよければ、ご参考ください。

※ 名前等は若干変更を加えていますが、問題なく動作はできます。

github.com

最後に

既存サービスのマイクロサービス化にあたって上記のことを取り組んだことで、少なくともテストはだいぶ書きやすくなりました。

「改修しやすいか」という面についてはまだまだこれからですので、今回作成したテンプレートを他のプロジェクトに投入したり、運用させてみたりすることで検証してみたいと考えています。

また、今回のことを通してDDDについても部分的ですが学ぶことができました。

今後は、ユビキタス言語とかドメインやコンテキストマッピングとかも学び・実践していけば、よりよいサービスが作れると感じていますので、そちらにも力を入れていこうかと思います。

消えたモニタを探せ!

Adways Advent Calendar 15日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar/archive


みなさんこんにちは。インフラDivの須藤です。このブログを書くのも実に2年ぶりとなりました。
前回はiOSデベロッパーアカウントの管理について執筆させていただきました。
今回は、ハードウェアや備品の管理をバッチファイルを書いて行うという、非常にマニアックなネタを紹介したいと思います。

資産管理の部署のみなさん。どこの誰に備品を貸したか忘れちゃった、または備品を貸したっきり戻ってこない!なんて事ありませんか?
そんな資産管理部門の悩みを解決するべくWindowsのバッチファイルを作りました。

情報収集

まず、モニタを探す手がかりですが、モニタには シリアル番号という固体固有の番号があります。通常は外箱だったり、モニタの背面シールに記載されているのですが、モニタがPCに接続されていれば、レジストリ情報から取ってくることができます。

以下の図はregeditというWindowsのアプリケーションで、レジストリをGUIでカスタマイズするものです。

f:id:AdwaysEngineerBlog:20161219181130p:plain

モニタに関する情報はEDIDという場所に記載されています。シリアル番号が異なるモニタを接続する台数が増えた分だけ、ここのレジストリに登録されます。

因みに接続されていないモニタ、または無効なモニタの場合BAD_EDIDと記録されています。

f:id:AdwaysEngineerBlog:20161219181140p:plain

これですね。

f:id:AdwaysEngineerBlog:20161219181151p:plain

EDIDは最初からバイナリ値ではありません。16進数をバイナリ値に変換した値がシリアル番号として認識できる値となります。

f:id:AdwaysEngineerBlog:20161219181210p:plain

16進数からバイナリ値への変換

16進数からバイナリ値に変換するDOSコマンドはcertutilを使います。
certutilは証明書の管理するコマンドとして知られていますが、エンコードやデコードできることはあまり知られていないようです。(私もバッチファイルを作るまで知りませんでした)このコマンドに-decodehexオプションをつけるとバイナリ値に変換できます。
因みにエンコードする場合は、-encodehex、そのまんまですね。以下に参考URLを載せておきます。

https://technet.microsoft.com/en-us/library/cc732443(v=ws.11).aspx

何台かモニタを試しましたが、DELLのモニタの場合、16進数の176桁目から202桁目までにシリアル番号が記録されているようです。ですので、このコマンドを使ってシリアル番号を取得してみました。

以下はバッチファイルの一部です。EDID値の176桁目から202桁目を取得し、certutilコマンドでバイナリ値に変換しています。

for /F "usebackq tokens=*" %%s in (`reg query "HKLM\SYSTEM\CurrentControlSet\Enum\%%t" /s^|findstr EDID`) DO @SET RESULT=%%s
    if not "!RESULT!" EQU "" (
    set DISPNUM=!RESULT:~176,26!
    echo !DISPNUM! > %AUDITDIR%\cache\%computername%\serial.txt
    certutil -f -decodehex %AUDITDIR%\cache\%computername%\serial.txt %AUDITDIR%\cache\%computername%\serial.txt.bin
    for /F "usebackq tokens=*" %%t in (`type     %AUDITDIR%\cache\%computername%\serial.txt.bin`) DO @SET mon_seri=%%t

    if "!mon_seri!" == "!mon_seri_back!" goto HW_check

応用してみる

上記のバッチファイルとExcelのマクロを連携すれば、モニタの所有者の検索もできます。

f:id:AdwaysEngineerBlog:20161219181221p:plain

※PC番号はモザイク加工しました。

この他にも、モニタの管理台帳(使用者とモニタのシリアル番号一覧をエクセルで出力したもの)を出力することも可能です。

f:id:AdwaysEngineerBlog:20161219181229p:plain

※個人情報や機密情報に関わる部分はダミーデータに加工しています。
※昭和臭がプンプンしますね(汗)。申し訳ありません。

今後の課題

実はこのバッチファイル、まだ完全ではありません。
PCの電源を入れたときにバッチファイルが実行されるように作成していますが、PCによってはバッチファイルがうまく実行されない場合があります。
それゆえ、地味な割にはバッチのメンテナンスに掛ける人的コストが大きいので、なかなか改善に時間が割けられていないのが現状です。

とはいえ、このバッチファイルで7~8割程度のモニタの所有者を判別できるようになったので、世に出回る資産管理ツールで、モニタのシリアル番号を取得し管理できるツールはまだ存在しないので(2016年12月時点)、時間の許す限りメンテナンスはちょっとずつ続けていきたいと思います。


次は古川さんの記事です。

http://blog.engineer.adways.net/entry/advent_calendar/16