Raccoon Tech Blog [株式会社ラクーン 技術戦略部ブログ]

株式会社ラクーン 技術戦略部より、tipsやノウハウなど技術的な話題を発信いたします。

AWS LambdaとAPI Gatewayを利用し、PagerDutyのインシデント発生時にSlackに専用チャンネルを作成する #2

こんにちは、インフラ及びシステム運用を担当している田中です。

前回の記事に引き続き、AWSのAPI Gateway経由でLambdaファンクションを呼び出し、Slackの
APIを叩いて専用チャンネルを作成する手順を説明していきます。

前回の記事ではPagerDutyのWebhookをLambda関数で受け取るところまでご紹介しました。

これからLambda関数内でSlackのAPIをコールするための処理を記述することになります。

簡単なLambda関数を書くだけであれば特に開発環境が必要になるわけではないのですが、少し複雑な処理を行おうとすると外部モジュールが必要になってきたりしますので、まずは開発環境を作ることから始めましょう。

また、開発したコードのアップロードも管理コンソールからのアップロードは面倒ですし、ファイルサイズ等の制限もあります。
そこで、AWS CLIを利用して開発したコードをS3経由でデプロイするようにします。
続きを読む

現場のシステム開発を体験できる!開発インターンをレポートします

ラクーンのエンジニア石川です。
グループ内の売掛保証システムを担当しています。

弊社では様々なインターンシッププログラムを実施しており、その一環で技術戦略部も開発体験のプログラムを用意しています。
開発体験とはいいつつ内容の幅は広く、ビジネスモデルに関する研修から始まり、データセンター見学や先輩エンジニアとの交流など、様々な経験ができます。

今回は私がメンターを担当した2016年3月に行われたインターン研修についてご紹介します 。

▼メンター石川 / インターン生の尾碕さん / メンター阿部
c34b4d9d

どんなことをするのか?

今回インターン生に開発してもらうのは、「売掛保証システムの会員登録画面に新規項目を追加」という案件です。
なんと、実際に本番稼働しているサービスの開発案件を担当してもらいインターン期間中にリリースまで行います。

開発期間が短い上に不具合も許されないのでインターン生も、私たちメンター側も真剣に取り組む必要があり正に生の開発現場を体験できる研修になっています。
また、インターネットで公開されているサービスから案件を選んでいるため、インターン期間後に学校の仲間や家族に見てもらうこともできて、より大きな達成感を感じてもらうことができるんじゃないかと思います。

▼インターンシップのカリキュラム
1日目 : ビジネスモデル研修・システム構成講義 
2日目 : システム開発体験 
3日目 : データセンター見学・自由時間 
4日目 : システム開発体験 
5日目 : リリース・発表・打ち上げ

1日目 : ビジネスモデル研修・システム構成講義

ビジネスモデル研修 

この研修では、弊社代表の小方がラクーンで行っているサービスについて説明します。
後で感想を聞いたところ、普段は馴染みのないビジネスモデルという概念が新鮮で非常に興味深かったようです。

システム構成講義

業務システムの開発にはプログラミングスキルだけでなく、様々な経験と知識が必要です。
そこで、まずは座学で一般的なシステム開発の流れを学んでもらいます。

続いて、携わってもらう売掛保証サイトの紹介と、今回追加したい機能を説明します。
サービスに対して、どんな意味がある機能をどんな流れで実装してリリースするのかを理解してもらいます。
 
▼携わることになるサービスやシステムの説明
写真 (1)

説明が終わったら、次はいよいよ売掛保証サービスの担当者に具体的なヒアリングを行います。

サービス担当者へのヒアリング

ヒアリングでは担当者の頭の中にあるイメージをどれだけ具体化できるのかが重要です。
完成した後に「イメージしていたものと違う!」と、ならないようにお互いの齟齬をなくします。

ヒアリングした要件は下記のようなものでした。
  • 項目名は連絡事項とする
  • 連絡事項を会員登録画面に追加して、入力されたデータは社内の管理画面から閲覧できる
  • 用途を限定せずに汎用的な目的に使えるように自由な文字列で入力できる
  • 入力必須ではない
  • 文字数制限を100文字とする
ヒアリングして分かったのですが、現在は連絡事項の入力ができないので申し込み前後に電話でのやりとりが発生するケースがあるとのこと。また予想外の使い方として、営業担当者が出先でお客様からの依頼を受けて代理登録する際に、社内担当者への業務連絡などにも利用できるようです。

▼ヒアリング時に作成した画面モック
モック

ヒアリングではインターン生も積極的に意見を述べ議論に加わっていました。
現場の意見を聞きながら仕様を策定していく場に参加できたことは貴重な体験になったのではないかと思います。 

2日目 : 開発

2日目、いよいよ開発がスタートします。

事前に用意しておいた開発環境一式で作業にはいります。
使い方の説明が終わったら開発環境上で実際のプログラムを動かしてみます。
ソースコードを自由に書き換えながら動かすことで、フレームワークやシステム構成をより深く理解できたようです。

▼インターン生への説明風景、弊社エンジニアは全員2~3枚のディスプレイをデスクに並べています
F1000332

▼最終日に行われたインターン生の発表資料から抜粋。システムをよく理解していることが伺えます。
MVC

3日目 : データーセンター見学・東京見学

3日目はいったんシステム開発をお休みして、午前中にデータセンター見学に行きました。
元々インフラにも興味があったらしく、普段目にすることのない無機質な空間が新鮮だったようです。

そして午後は自由時間です。今回のインターン生は首都県外から来てくれていたので、東京観光を楽しんだようです。
翌日どこに行ったのかを聞いてみたら、かねてから興味のあった秋葉原のボードゲーム専門店に行ってきたのだとか?その実力の程は5日目の打ち上げで発揮されることとなります。
 

4日目 : 開発・テスト

4日目は開発の続きです。今回の案件では、データベースへのカラム追加が必要です。
データベースを使ったことはなかったようですが、簡単な説明をしたら、あとは自分で調べてSQLを使いこなしていたので驚きました。

システムテスト

一通りの開発が完了したので、機能として問題ないかテストを行います。
弊社で利用しているフォーマットに合わせてテスト仕様書を作成してもらいます。
メンター側でテスト仕様書をチェック、足りないテスト項目を追加してもらい、求められている要求に即しているか、プログラムは問題なく動いているか、などをテストしてもらいました。
実装に問題ないことが確認できたら、今度はユーザ部門の担当者に確認してもらいます。
担当者チェックも問題なかったので、あとは5日目のリリースを待つばかりです。

5日目 : リリース・発表・打ち上げ

5日目、最終日です。

リリース

最後の作業となるリリースですが、本番環境の権限の関係で、メンターが作業するのを横で見ていてもらいました。

リリース前に連絡事項がない状態で会員登録フォームを画面に表示してもらいます。
そして、リリース作業の完了後に開いているページを更新してもらった結果、連絡事項が表示されました。
無事リリース完了です!

▼連絡事項が追加されました完成
一通りの作業を完了しました、期間も短く大変だった分、達成感も大きかったと思います。

発表会

インターンの集大成となる発表会です。
事前にメンターと行ったリハーサルでは、上手く話せるか不安がっていましたが、最後までしっかりした発表になりました。

▼リラックスしたムードながらしっかりした発表会となりました
F1000328

思いが伝わる良い発表会だったと思います、お疲れさまでした!

打ち上げ

主賓が未成年ということもあり打ち上げはノンアルコールで行いました。
社内のフリースペースを利用してドリンクを持ち寄り乾杯!
メインは人形町今半のすき焼きです。

▼沢山の先輩エンジニアに囲まれて少し緊張気味の滑り出し
F1000335

インターン中は忙しくてなかなかコミュニケーションが取れなかったメンバーからも、労いの言葉や発表会の感想などをもらっていました。

そんな中、嬉しいサプライズが!ボードゲーム好きということを聞きつけた先輩からボードゲームのプレゼントがありました。

▼いい笑顔です
F1000333

▼お馴染み?のレジスタンス:アヴァロン。詳しくは『レジスタンス:アヴァロン』を遊びやすくするDIY
F1000334_

この流れで、ボードゲーム部主催でボードゲーム大会を行いました。
「スカル」というゲームです。
 
▼ボードゲーム 「スカル」
F1000336

初めてのゲームだったようですが、似たゲームをやったことがあったのか、なんと!優勝していました。

開発実習で疲れきった頭を休めるはずが、よりヒートアップした感もありますが・・・、大変お疲れさまでした。

インターン生の感想まとめ

開発にあたって工夫した点・苦労した点
  • MVCモデルを教えて貰った後、そのソースコードを詳しく見て、次回に変更する点や、編集するファイルを考えていました。
  • MySQLやOracleなどのDBは今まで触れたことがなかったため、操作に苦労しました。
  • 単体テストの時に、一部のブラウザでは問題なく動作したはずが別のブラウザだと動作しない不具合が起こってしまい、様々な動作環境でテストすることの大切さを学びました。
システム開発体験をして
  • 実際に社内の雰囲気を感じながら社員の方と同じように作業したのは、とてもいい経験になりました。
  • 自分が作ったものが社会に出ていることが実感できるのは強いやりがいを感じました。
  • なぜ「このようにしたのか」「別の方法の方がいいのではないか」と疑問に思ったことを聞くと、「このようにした理由」「開発するにあたって考えるといい点」など、授業では学べないことを知ることができました。
5日間を通して

今回私は、5日間でのシステム開発の体験をさせて頂きました。
社員さんと一緒の現場で開発や、データセンターの見学、社員の方の経験談を聞くなど様々な経験を得ることができました。
今回の経験を、これから就職活動などで是非活かしたいです。
 

最後に

ラクーンにおける開発インターンの良い所をご紹介!

実戦経験
実際に業務に参加してもらうことで、自分が学んでいる技術が社会でどんなふうに利用されているのかわかる。

スキルアップ
現場で活躍する先輩エンジニアから、実践で役立つノウハウを学ぶことができます。
 
職場の雰囲気を体感できる
周囲にいる社員の発言や動き、時間の過ごし方なども観察することができ、職場の雰囲気を肌で感じることができます!

以上、ラクーン技術部のインターンシップの紹介でした。 

Goの入門書を書きました

開発の松尾です。
このたび、とある縁に恵まれまして翔泳社よりGoの入門書である『スターティングGo言語』を上梓しました。

12924367_10208745634417646_849801601955218169_n

最近、少しずつですがGoに関連した書籍が増えてきて嬉しい限りですが、「入門書」という体裁の本はあまり見かけないので、これからGoを学びたいというプログラマの方に手にとってもらえると嬉しいですね。2016年初頭にリリースされた最新のGo 1.6に対応しているところがポイントでしょうか。

また、本の発売に合わせて、本日4/21より下記のインタビュー記事がCodeZineに掲載されています。

仕事の言語に飽きてきた人はGoを使ってみてほしい――『スターティングGo言語』著者が語るGoのパワー

このような本を執筆する機会に巡り会えたのは、昨年1月にCodeZineに掲載された記事『EclipseでGoプログラミング! GoClipseのインストールとGojiフレームワークを使ったWeb APIの作成』がきっかけでした。ラクーンのブランディングの一環として外部媒体に記事を出したい、というどちらかと言えば会社の要望があって書いてみた記事ですが、まさかこの記事一本がきっかけになって「本を書いてみませんか」というお誘いを受けるとは思ってもみませんでした。

いただいた提案は嬉しいものの、会社の業務と執筆のバランスがとれるだろうかと悩んでいましたが、自身も2冊の著作経験がある社長・小方功の「業務時間の一部を執筆にあてて良し」という鶴の一声に背中を押されて執筆にとりかかりました。以後、とくに執筆期間の4,5ヶ月の間は「技術書を書くというのはこんなにも大変なのか!」と苦闘の連続でしたが、ラクーンという会社環境の応援もあり何とか走り切ることができました。

※ただ、いくら会社の理解があったとしても業務と執筆の両立は恐ろしく難しかったのは事実です。もっとも筆が進んだのは、間違いなく休日のガストでした(笑)

さて、こんな沿革で書き上げた『スターティングGo言語』ですが、プログラミング経験者が新しくGoを学ぶというコンセプトで構成していますので、「最近ちょっとGoに興味が出てきた」という方にはぴったりなのではないかと自負しています。実は、ラクーンではさほどGoは使っていないのですが(笑)、適したところがあればどんどん活かしていきたいと考えています。

それでは今後のGoの発展を願って~

AWS LambdaとAPI Gatewayを利用し、PagerDutyのインシデント発生時にSlackに専用チャンネルを作成する #1

こんにちは、インフラ及びシステム運用を担当している田中です。

当社ではサーバーやネットワークに障害が発生した際に、障害対応の担当者へ通知を行う手段としてPagerDutyを利用しています。

担当者が障害を認知した後は障害内容を調査し、必要に応じて開発者や責任者へエスカレーションを行うことになります。

障害の発生が平日の日中帯であれば関係者間のコミュニケーションに問題はないのですが、夜間・休日の場合は関係者がそれぞれ別の場所に居ることになるため、障害ごとにSlackの専用チャンネルを作成して関係者間で情報を共有しています。

SlackにはPagerDutyのWebhookを受け、インシデントの内容を特定のチャンネルにpostすることができる機能が用意されています。
しかし、あくまでも特定のチャンネルにpostすることができるだけで、インシデントごとに専用のチャンネルを作成したりすることはできません。

そこで、AWSのAPI Gateway経由でLambdaファンクションを呼び出し、SlackのAPIを叩いて専用チャンネルを作成するようにしています。

今回から数回に分け、この仕組について説明したいと思います。
続きを読む

春なのでSpringへの移行ガイド

こんにちは、開発ユニットbcです。

みなさんご存知かとは思いますが、Seasar2のサポートが2016年9月26日を以って終了します。
弊社の運営するSUPERDELIVERYでも一部の機能でSeasar2を現在も利用しています。

今回はSeasar2からの移行を検討している方の参考になればと思い、以前に行った移行の事例を紹介させていただきます。

2014年にSUPERDELIVERYの商品登録機能をリニューアルした際、弊社ではSeasar2からSpring Framework(4系)へ移行することを選択しました。
選択の理由はプロダクトの開発が停滞していないことと、Seasar2からの移行のしやすさでした。
なぜ移行がしやすいかというと、Seasar2の各機能を代替する機能がSpring Frameworkにも備わっているからです。

ではまずはSeasar2とSpring Frameworkの構成の比較から。

構成の比較

移行前 移行後
Controller S2Struts Spring MVC
DI Seasar2 Spring Framework
DAO S2Dao Spring Data

このようにSeasar2の機能の移行先がSpring Frameworkにもあることが分かります。
では、商品一覧表示の機能を例にしてどのように移行するかを見ていきましょう。

移行前と移行後の実装を比較

移行前のシステムでは、コンポーネント毎に設定ファイルへ記述するスタイルで開発していたので、
新たに機能の追加がある場合は以下のファイルの追加が必要でした。
  1. XXXAction.java
  2. XXXActionImpl.java
  3. XXXService.java
  4. XXXServiceImpl.java
  5. XXXDao.java
  6. XXX.dicon
  7. struts-config-XXX.xml
■ProductSearchAction.java
public interface ProductSearchAction {
  String execute();
}
■ProductSearchActionImpl.java
public class ProductSearchActionImpl implements ProductSearchAction {
  private ProductSearchService productSearchService;
  private List<Product> products;
  private ProductSearchForm searchForm;
	
  public String execute() {
  products = productSearchService.findProducts(searchForm.createProductSearchCondition());
  return "success";
  }

  public List<Product> getProducts() {
  return products;
  }

  public void setProductSearchService(ProductSearchService productSearchService) {
  this.productSearchService = productSearchService;
  }

  public void setSearchForm(ProductSearchForm searchForm) {
  this.searchForm = searchForm;
  }
}
■ProductSearchService.java
public interface ProductSearchService {
List<Product> findProducts(ProductSearchCondition condition);
}
■ProductSearchServiceImpl.java
public class ProductSearchServiceImpl {
private ProductDao productDao;

public List<Product> findProducts(ProductSearchCondition condition) {
return productDao.findByStatusCode(condition.getStatusCode());
}

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
}
■ProductDao.java
public interface ProductDAO {
  @Query("status_code = ?")
  List<Product> findByStatusCode(Integer statusCode);
  void insert(Product product);
  void update(Product product);
  void delete(Product product);
}
■action.dicon
<components>
... <component name="productSearchAction" class="jp.ne.raccoon.action.product.impl.ProductSearchActionImpl" instance="request"> </component>
... </components>
■service.dicon
<components>
  ...
  <component
    class="jp.ne.raccoon.service.product.impl.ProductSearchServiceImpl">
  </component>
  ...
</components>
■dao.dicon
<components>
  ...
  <component
    class="jp.ne.raccoon.dao.ProducDao">
  </component>
  ...
</components>
■struts-config-product.xml
<struts-config>
  <action-mappings>
... <action path="/product/search" type="jp.ne.raccoon.action.product.ProductSearchAction" parameter="execute"> <forward name="success" path="/product/search.html" /> </action>
... </action-mappings> </struts-config>
  1. DIはdiconファイルにコンポーネントを個別に定義して、Seasar2のセッターインジェクションを利用していました。
  2. リクエストのマッピングは、struts-config.xmlにaction-mappingsをaction毎に個別に設定していました。
 
移行後は以下のような実装になりました。
■ProductSearchController.java
@Controller
public class ProductSearchController {
@Autowired private ProductSearchService productSearchService; @RequestMapping(value = "/search", method = RequestMethod.GET) public String execute( @Valid @ModelAttribute("searchForm") ProductSearchForm searchForm, BindingResult bindingResult, Model model) {
model.addAttribute(
"products",
productSearchService.findProducts(searchForm.createProductSearchCondition()));
return "product/search"; } }
■ProductSearchServiceImpl.java (※interfaceは移行前と同じなので割愛してます。)
@Service
public class ProductSearchServiceImpl implements ProductSearchService {
@Autowired private ProductRepository productRepository; public String findProducts(ProductSearchCondition condition) { return productRepository.findByStatusCode(condition.getStatusCode()); } }
■ProductRepository.java
public interface ProductRepository extends JpaRepository<Product, Long> {
  List<Product> findByStatusCode(Integer statusCode);
}

移行前と比べると以下のように実装を置き換えることができました。
Action → Controller
Service → Service
Dao → Repository
diconファイル → @Controller @Service等のアノテーション
struts-config → @RequstMapping
  1. 設定ファイルを必要とせず、Springのアノテーションで コンポーネントの定義とDIを行うようになりました。
  2.  @RequestMappingを利用することでリクエストメソッドの指定やパラメーターの受け取り方等がより柔軟な設定を書けるようになりました。
  3. Daoのinsert/update/deleteのメソッドは、継承元に定義されているのでメソッドを用意する必要はなく、@Queryに相当する物はfindByXXXの部分の命名規則によってwhere句が生成されます。※where status_code = ? が生成されます。
これだけで、新機能の追加に必要な作業がだいぶ軽減されました。
順調に移行できそうでしたが、問題点もありました。

問題点

一つ目が、sqlファイルの外部化です。
S2Daoの資産として、参照系の動的なsqlは外部ファイル化していました。 
Springのプロダクトでは対応している物が見当たらず、可能ならば慣れ親しんだ形で移行したいという思いもあって、Mirageというライブラリを導入することにしました。 

■ProductDao_findProducts.sql
//日付の範囲指定と商品の状態を検索条件で指定できることを例としてます。
select
  product_code
from
  product
/*BEGIN*/ where /*IF dateFrom != null*/ and created_at >= /*dateFrom*/ /*END*/ /*IF dateTo != null*/ and created_at < /*dateTo*/ + 1 /*END*/ /*IF productStatus !=null*/ and product_status = /*productStatus*/
 /*END*/
/*END*/
■ProductDao.java
@Component
@Transactional(readOnly = true)
public class ProductDaoImpl implements ProductDao {
  @Autowired
  private SqlManager sqlManager;

  public Product findProducts(SearchCondition condition) {
//上記sqlファイルを指定 SqlResource sqlResource = new ClasspathSqlResource("ProductDao_find_products.sql"); return sqlManager.getResultList(Product.class, sqlResource, condition); } }
mirageで実装したDaoも@ComponentでSpringのコンポーネントとして定義しました。
必要に応じてspringDataとmirageを使い分けるようにして、移行前の資産も活かすことができました。

二つ目がSeasar2とSpringでのコンポーネントのライフサイクルの違いでした。
S2Strutsの時は、Actionはリクエスト単位で作成していましたが、SpringMVCのControllerでは、デフォルトがシングルトンなため、リクエストやセッション等の短いライフサイクルのコンポーネントのDIができないということでした。
@Controller
@Scope(value = WebApplicationContext.SCOPE_REQUEST) public class XXXController { }
@Scopeの設定でライフサイクルを変えることで対応はできますが、全部のControllerに書くのは・・・ということで、以下のようなアノテーションを作成してライフサイクルをリクエストにしたいControllerに指定するようにしました。
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Controller
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public @interface RequestScopeController {
}
利用する側のController
@RequestScopeController
public class XXXController {
}
これで、リクエストやセッションのライフサイクルのコンポーネントをDIしても問題なく動作するようになりました。

まとめ  

局所的ではありますが、以上がSeasar2から移行した時の内容です。いかがだったでしょうか。
Seasar2のサポート終了をきっかけに新しいことに挑戦していきたいですね。
記事検索