AWSの話題を中心に、日々の業務やプログラミングの徒然を綴るエンジニアブログです。

HANDS LAB

HANDS LAB ENGINEERS BLOG

ハンズラボエンジニアブログ

カテゴリー: Lambda

すごいVBAをLambda+HTML帳票+Puppeteerに置き換えた話

Pocket

CRMチームのyktakaha4です。

今日は、私たちが日々開発・運用しているハンズネットにおけるレガシーマイグレーションの現場をお届けします。

みなさんは、Excel、とりわけVBAはお好きでしょうか?

方々のエンジニアから親の仇のように忌避されながらもなぜだか中々無くならず、今日においても大小様々な企業で使われ続ける不思議な存在です。
嘘だとお思いでしたらInxxxxやレバxxxで「VBA」で検索してみてください。年収700万円も夢じゃないみたいですよ…?

私はExcelもVBAも好きです(でした)。

Officeの入ったWindowsがあれば使えるという可搬性に加え、
ユーザーフォームやExcel関数、CreateObjectやDeclareまで駆使すれば、控えめに言ってどんな処理でも実現できます。
また、Numbersやスプレッドシートの関数名、GASにおける各種オブジェクト名なんかを見ても、彼らがいかに広く浸透した概念であるか分かります。

前職では金融系の社内システムを開発していたのですが、資格を取得するくらいには活用していました。
もちろん不満・力不足に思う部分も沢山ありますが、エンドユーザと同じコンテキストを共有できて、どこでも使えて何でも作れるって、それ普通に全エンジニアの夢じゃないかと思うのですが…!

しかしながら、潮目は少しずつ、でも着実に変わってきています。
過去にはExcelでPythonが使えるようになるかもというニュースがあったり(結局JavaScriptになったんでしょうか?)、東急ハンズでも各種RPAツールの導入や、G Suiteの利用を推進していたりと、その利用の場は減っています。
令和の時代にはきっと新たな何かが世を席巻し、その陰に消えゆく運命にあるのでしょう…

そんなVBAに最後の餞として用意されたかのごとく、東急ハンズでExcelとVBAが大活躍する業務がありました。

そう、ハンズネットの荷札発送業務です。

ハンズネットは、すごーく大まかに言って、東急ハンズ新宿店の在庫をバックヤードで梱包してヤマト運輸経由で発送しています。
ヤマト運輸で商品を発送するためには、所定の送り状にお届け先の情報を印字してダンボールに貼り付ける必要がありますが、
この荷札の作成及び印刷をExcelとVBAマクロを駆使しておこなっていたのです。

図にあらわすと以下のような感じです。

出荷担当者目線では「梱包作業の時間になったらWebの画面にボタンが出るんで、押したら荷札が出てくる」程度のものですが、
システム担当者からすると、業務ロジックに基づいた荷札種類の決定、データへの書式設定、mm単位での微調整が必要なレイアウト、バーコードの生成、複数プリンタを指定する必要のある印刷処理などが渾然一体となったすごいやつ(尊敬)を保守し続けなければならず、
私含むエンジニアのほぼ全員がMacbook Proで開発を行なっている現状においては、環境を用意するだけでも一苦労…という状況でした。

そんな中、今年の10月に施行される消費増税、とりわけ経過措置としての軽減税率導入へ向けた対応の中で、当マクロの改修が必要なことが分かりました。
東急ハンズでは軽減税率対象となる食料品を一部扱っており、税率を分ける必要がある→荷札と一緒に添付する領収書で表示する税額を分けたり、商品ごとの税区分を明示する必要があったんですね。

現行のマクロを改修するという逃げの一手もありましたが、CRMチームのエンジニアリーダであるwatarukuraさんの判断もあり、この機会に保守が容易な別の仕組みに置き換えようという運びとなりました。
(私見ですが、こうした判断をエンジニアサイドから自発的におこなえるのは弊チームの良いところの一つだと思います。こちらもよければご覧ください)

今回は、マイグレーションによってコードを保守可能なものに置き換えることと、複数システムにまたがる税対応の中でスピード感を持って改修することを優先し、
外部サービスやパッケージを使うといった抜本的な運用変更までは考えず、既存と同等の荷札を別の仕組みで出力できるようにする…という方向性で改修方針を立てました。

改修後は以下のようになりました。

ユーザ目線では、従来Web画面でボタンを押下した際に自動的に荷札が印刷されていたのが、
出荷対象の全ての荷札が印字されたPDFファイルがダウンロードされるのでそれを印刷する…という形になっています。
印刷方式についてはチーム内でも議論がありましたが、業務部門のOKをもらった上で、今回の作業スコープからは一旦外すこととしました。
(運用がこなれてきたらより良い形を考えて、改めて提案したいですね…!)

業務的な話とは別に、本対応のキモであるVBAをPDF生成APIに置き換えた部分をもう少し詳しく紹介しておきます。

大枠では、VBAに組み込まれていた業務ロジックとデータへの書式設定をNode.jsに置き換えた上で、Excelでレイアウトしていた荷札をテンプレートエンジンであるEJSを用いたHTML帳票として出力し、それを更にGoogle ChromeのヘッドレスブラウザのPuppeteerでPDF化する…という感じです。
HTMLならVBAよりは修正にかかる心理的負荷を軽減できますし(WinMergeを使えば…とかあるかもしれませんが、コードでそのまま比較できるのはデカいと思います)、PDFはフォントや画像埋め込みもできて環境差異が出づらく、かつ業務部門にとってもある程度扱えそうなものだったため、利用技術として選定しました。
Puppeteerを使ったのは、公式で活発に開発が行われていたからというのもありますが、東急ハンズではGoogle Chromeを社内標準のブラウザとして定めており、色々知見が溜まれば今後に活かせそうに思えたからです。

また、今回の業務要件特有の話になりますが、1日の間に締め時間が数回あり、その時点までに受け付けた注文をなる早で荷札出力する必要がある…という課題がありました。
一回の締め処理で扱う注文数は、業務的な繁忙期も考慮すれば最大で約1000注文程度を想定しなければならず、
処理量に応じていい感じにスケールアウトしつつ、出荷のない夜間帯などはお金が掛からない仕組みを構築したいと考えていました。
あと、細かい話ですが、プリンタがジャムったり締め処理後に注文情報が変更になった時用に、個別に荷札を再印刷する機能も作らなければならず、バッチ・Webの双方から呼び出し可能なAPIである必要もありました。

これについては、社内でも幾つかのプロジェクトで利用実績のあったServerless Frameworkを使って、APIとしてリクエストを受ける親のLambda関数と、EJSによるレンダリング〜PuppeteerでPDF出力までを担う子のLambda関数をそれぞれ別に定義し、処理量に応じて子Lambda側の並列実行数を増やして対応することとしました。

平行処理については、作る前は苦戦しそうかな…?と思っていた部分だったのですが、結果的にはほとんど詰まるところがなく、改めてマネージドサービスは便利ですごいな…と思い知りました。
また、Serverless Frameworkについても、ソースコードのパッケージングからデプロイ、関数毎のメモリ使用量やタイムアウト秒数まで面倒見てもらえるので、本番デプロイなどコマンド1発で済んでしまい、大変助かりました。

最後に。
ご参考まで、対応前後の荷札サンプルも貼っておきます。今まで単に荷札と説明してきましたが、右部は商品明細になっていて、切り取ってダンボールの中に入れるようになっています。
上が対応前(VBA)、下が対応後(PDF)です。開発中の写真で位置調整が甘いですが、なんとなく雰囲気は分かってもらえるのでないかと思います。

VBA版
PDF版

余談ですが、従来バーコードコントロール9.0でおこなっていたバーコード出力はJsBarcodeを使うようにしました。フォントもおなじみMSゴシックからNoto Sansに変えたので、なんとなく見やすくなった感じがしますね。

現在は、従来のExcelバージョンとPDFバージョンを双方出力できるような状態で本番化して、処理量と処理速度を監視しつつ、業務部門に不都合がないか最終確認してもらっている段階となっています。
消費税対応のためにはじめたマイグレーション作業でしたが、これをきっかけに、実は他にも荷札で直したいところがあって…というような依頼もいくつか頂くようになり、業務改善にも寄与できそうでいい感じです。

っていうか、ようやく税対応のスタート地点に立つことができたという話なので、
これがキャリア最後のVBA対応になることを強く願いつつ、もう一息頑張っていきます…!

Pocket

re:Invent 2018 新機能!LambdaのCustom Runtimesを試してみた

Pocket

Hello, サービス開発チームの北野です。

re:Inventでラスベガスに来ています。とうとう最終日です。

今日はKeynoteでServerless関連が大きくアップデートしました。
LambdaのRubyサポート, Lambda Layers, API GatewayのWebSocket対応, ALB for Lambdaなどなど、
どれも使ってみるのが楽しみになるアップデートでした。

LambdaのCustom Runtimesの発表を見て、
「これはもしやハンズラボの謎技術ユニケージ on Lambdaができるのでは…?」
という悪魔のささやきが聞こえましたが、無視しました。

ちなみに私は業務でユニケージを触ったことはございません。

それはともかく、LambdaのCustom Runtimesが気になったので、公式チュートリアルに沿って試してみました。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html

Lambdaの作成

まずはLambdaをマネジメントコンソールから作成します。
ランタイムに「独自のランタイムを使用する」という選択肢が増えているので、これを選びます。
名前とロールは適当に設定して関数を作成します。

独自のランタイムを使用する場合、プログラムはマネジメントコンソールで編集できないようなので、プログラムはローカルマシンで作成します。

bootstrapの作成

独自のランタイムを使用する場合、bootstrapシェルスクリプトを起点にLambdaが動くようです。
サンプルスクリプトを見ると、LambdaのRuntimeAPIに対してGETでHTTPアクセスしてLambdaを起動したEventを取得して、
LambdaのRuntimeAPIに対してPOSTでHTTPアクセスしてResponseを返す仕組みになっています。

今回はサンプルスクリプトをちょっとだけ変更して使います。

bootstrap

function.shの作成

bootstrapから呼び出して実行するシェルスクリプトfunction.shを作成します。
シェルスクリプトのfunctionには戻り値が無いので、標準出力を使って擬似的に戻り値があるような振る舞いをさせます。
シェルスクリプトに馴染みがないとよくわからないと思うので、bootstrapの振る舞いと合わせて後ほど解説します。

function.shもサンプルスクリプトを少し変更して使います。

function.sh

bootstrapとfunction.shの振る舞い

シェルスクリプトの動作について少しだけ解説します。
まず環境変数$_HANDLERには、Lambdaで設定しているハンドラが設定されます。
今回はfunction.handlerという値が設定される想定です。

そしてbootstrapの6行目
source $LAMBDA_TASK_ROOT/”$(echo $_HANDLER | cut -d. -f1).sh”

echo $_HANDLER | cut -d. -f1
で、function.handlerを”.”で文字列分割した1つ目を取得しています。
これを展開すると、
source $LAMBDA_TASK_ROOT/function.sh
ということになります。

17行目も似たような形で、
RESPONSE=$($(echo “$_HANDLER” | cut -d. -f2) “$EVENT_DATA”)

echo “$_HANDLER” | cut -d. -f2
で、function.handlerを”.”で文字列分割した2つ目を取得しています。
これを展開すると、
RESPONSE=$(handler “$EVENT_DATA”)
ということになります。
このhandlerはfunction.shで定義したfunctionで、handler実行時の標準出力がRESPONSE変数に格納されます。
$()を使ってfunctionの標準出力を変数に格納することで、擬似的に戻り値があるような振る舞いをさせています。

Lambdaのコードアップロード

作成した二つのファイルはzip圧縮して一つのファイルにします。

  • bootstrap
  • function.sh

マネジメントコンソールを開いて、コードエントリタイプで「.zipファイルをアップロード」を選択します。
アップロードボタンを押して作成したzipファイルを選択し、ハンドラを修正して保存ボタンを押します。

これで動かす準備が出来ました。テスト実行してみましょう。

Lambdaのテスト実行

右上の方にあるテストボタンを押します。

テスト用のEventを作る必要があるので、名前だけつけて適当に作成します。

作成したEventが設定されていることを確認して、再度テストボタンを押します。

「実行結果:成功」でちゃんと動いています。やったね!

終わりに

LambdaのCustom Runtimesを動かすことができました。
便利だなと思う半面、せっかくのシンプルなLambdaにだいぶ管理しなければならないことが増えてしまう印象があり、Custom Runtimesの使用は慎重に検討したいところです。
選択肢が増えたことはとても良いことだと思うので、本当にCustom Runtimesを使う必要があるのか?他にもっと良い方法が無いか?と自問自答して、適切な技術選択をしていけたらと思います。

Pocket

CodePipelineの進行状況をSlackに通知する

Pocket

こんにちは。百木田です。
CI、回してますか?

CodePipelineで実行するステージの進行状況を手軽に見たいと思い、Slackに流れるようにしたので共有します。

ポイントとしては、CodePipelineからLambda functionを呼ぶのではなく、CloudWatchイベントでCodePipelineステージのステータスの変化を検知してLambda functionを呼び出します

今回はできるだけシンプルにするために、すでにCodePipelineは作成済みの想定で、すべてのステージにおけるSUCCEEDEDFAILEDのステータスを通知します。なのでCodePipelineに変更は必要ありません。
また、Slack Botを作成しトークンを発行して使っていますがIncomming Webhookでも同じようなことはできるかと思います。

Serverless Frameworkを使ってCloudWatchイベントの設定と、slackに通知するためのLambda functionをデプロイします。

環境

ディレクトリ構成

デプロイ

コードは記事の下の方に書いときます。上記のディレクトリ構成のように配置したらデプロイします。

デプロイできたらCodePipelineを動かしてみます。そうするとslackにこのような通知が来るかと思います。

いつの実行結果なのかをトレースできるようにCodePipelineの実行IDの前半7桁を表示しています。この辺はお好きにカスタマイズしてみてください。

まとめ

CloudWatchイベントの検知がリアルタイムじゃないので、CodePipelineの進行と通知にラグがあったり、前のステージの実行完了と次のステージの実行開始の通知が前後することなどもありますが、実行結果がわかればいいかなくらいの感じなので気にしないことにしてます。

ありがとうございました。

以下、コードたち

status_notification.py

serverless.env.yml

serverless.yml

※ detailのセクションを消すと、すべてのステータスが検知対象になります。

Pocket

Serverless Frameworkでバイナリデータを処理するアーキテクチャを構築したかった

Pocket

こんにちは。AWSチーム百木田です。

サーバーレス、してますか?

この度、「クライアントから画像を受け取ったらLambdaでその画像の処理をする」ということがしたく、受け取る部分を昨年11月に発表されてできるようになったAPI Gatewayでのバイナリデータサポートを利用してやってみました。
そして何かツールを使って楽にデプロイしたいと思い、今回はServerless Frameworkを利用してやってみることにしました。

環境

  • OS(VM): CentOS Linux release 7.2.1511 (Core)
  • Node Version: 7.5.0
  • Serverless Version: 1.8.0

準備

Serverlessの定義ファイル作成

Serverlessのセットアップについては省略させていただきます。’serverless’コマンドが実行できる前提です。

‘serverless.yml’を作成します。

定義している内容は以下です。

  • LambdaからS3にデータを置くためのIAMロールを定義
  • POSTで受けてLambda処理を行うAPI Gatewayを定義
  • 画像を保存するS3 Bucketを定義

注)S3バケット名の部分は変更してください。

タイトルで「してみた」にできなかったのには理由があり、残念なことに肝心のバイナリデータサポートの設定は現時点でServerlessでは対応していませんでした。そのためプラグインを使用するか他の方法で設定する必要があります。今回は泥臭くデプロイ後にマネジメントコンソールで設定することにします。

Lambda実行コードを作成

Lambdaでの処理は受け取った画像データをS3にアップロードするという簡単なものにしました。

ここではデータを’/tmp’に一度保存していますがもちろん受け取ってそのままS3にあげちゃってもOKです。

デプロイ

バイナリデータサポートを設定

‘serverless deploy’するとS3バケットやAPI Gateway、Lambdaといったリソースが作成されているかと思います。その後、API Gatewayのマネジメントコンソールからバイナリサポートのバイナリメディアタイプに’image/jpg’などPOSTで受ける’Content-Type’に合わせた値を設定します。

バイナリサポート設定後のAPIをデプロイするために再度’serverless deploy’をします。再度デプロイしても手動で設定したバイナリサポートの設定は消えません。

これで設定は完了です。

実際に試してみる

お手軽に試すにはローカル端末から以下のコマンドを実行します。

‘Upload Sccessful’が表示され、S3の指定バケットにファイルが保存されています。

まとめ

今回はServerless Frameworkを利用しましたが他にもSAMApexLamveryなどツールが出ているので今後試していきたいと思います。Serverless Frameworkも順次アップデートされているので今後楽しみです。

ありがとうございました。

Pocket

AWSのEC2イベントを検知してslackへ通知するBotを作ってみた!

Pocket

download

週末、ハンズラボは花見でした。

吉田です。

IMG_1325

IMG_1324

IMG_1323

いやー、楽しかったです(笑)
花を見た記憶がほとんどありませんが・・・
桜も2−3分咲きでしたが、テンションは最高でした。外でご飯を食べて酒を呑む。最高ですね!

さて、今回のブログネタはslack × Lambda です。

続きを読む AWSのEC2イベントを検知してslackへ通知するBotを作ってみた!

Pocket