Cluex Developersブログ

株式会社Cluexでは、子育てをするママのためのメディア - mamanoko(ままのこ)をRuby on Railsで運営しております。

Yarnを導入しました!

f:id:cluex-developers:20170628130919p:plain

こんにちは、エンジニアの井戸田です。

弊社が運用しているmamanokoはRuby on Railsで実装しております。
以前まではnpmを使用していたのですが、先日Yarnに移行しました。
今回はYarnのメリットやnpmからYarnへの移行方法をDockerおよびDocker Composeを使用して書いていきます。

Yarnの特徴

厳密にバージョンを固定

Yarnの場合パッケージのインストール時にデフォルトで yarn.lock というファイルが生成されます。これによって複数人の開発時でも同じパッケージがインストールされるので、バージョンの違いで1人だけエラーが出るということが無くなります。

この機能はnpm v4までの shrinkwrap にあたります。ただ shrinkwrap の場合、package.json に手動でパッケージを追加後、それを削除すると追従することが出来ずエラーが発生し、結局 npm-shrinkwrap.json を削除して再インストールするなどの問題が発生していたようです。Yarnの場合そのようにnpmで悩んでいた依存性の問題も解決しております。

ただこの前リリースされたnpm v5ではバージョンを固定するようになったようです。

高速なインストー

npmの場合直列で1つ1つのパッケージをインストールしていたのに対して、Yarnの場合は並列でパッケージのインストールをしています。これにより高速にインストールすることができます。
また一度インストールするとキャッシュするので2回目以降のインストールはオフラインでも可能になります。

下記のURLを見てみると、スピードは Yarn > npm v5 > npm v4 の順に速いみたいです。npm v5でスピードが改善されたようですが、Yarnの方がまだまだ高速です。

github.com

よりセキュアに

パッケージのインストール前にチェックサムで整合性を確認しており、誤りがないかをチェックできるため信頼性が高まります。

本番運用するにあたって

弊社で運用しているmamanokoは、これまで本番環境にはAWSのEC2を使用していましたが、今年の3月にAWSのECS+EC2によるDockerでのサーバー運用にインフラを全面移行しました。

cluex-developers.hateblo.jp

開発環境もDocker及びDocker Composeで管理しており、本番環境との差異が以前に比べて格段に減りました。 またDockerfileとyamlで管理することにより、移行にあたって今までサーバーの中に入って作業しなければいけなかったのが、常にコードでインフラ環境を管理するようになりラクになりました。

導入方法

Docker Hubで配布されているnodeのコンテナを利用していきます。

https://hub.docker.com/_/node/

nodeコンテナのv8.1のコードを見てみるとYarnをインストールする記述があります。なのでDockerファイルにYarnをインストールするコードを書かなくても、すぐに使用することが出来ます。
ちなみにnpmをインストールする記述もあるのですぐに利用可能です。

# ~~~~~

ENV YARN_VERSION 0.24.6

RUN set -ex \
  && for key in \
    6A010C5166006599AA17F08146C2130DFD2497F5 \
  ; do \
    gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
    gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \
    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \
  done \
  && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
  && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \
  && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
  && mkdir -p /opt/yarn \
  && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/yarn --strip-components=1 \
  && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \
  && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz

# ~~~~~

https://github.com/nodejs/docker-node/blob/31cfb8b96b69351b3552592dd1e2d62e73a5c5b9/8.1/Dockerfile

Dockerfileを作成して必要なコードを記述していきます。

docker/node/Dockerfile

# node v8.1のイメージを指定
FROM node:8.1

# コンテナに `/var/www/client/node_modules` ディレクトリを作成
RUN mkdir -p /var/www/client/node_modules

# `./client` を `/var/www/client` にコピー
# `./client/` はcss, javascriptなどasset部分の必要なファイルが入っているディレクトリ
COPY ./client/ /var/www/client/

# `/var/www/client` に移動
WORKDIR /var/www/client/

# `/var/www/client/package.json` を元に'Yarnを使用してインストール
RUN yarn install

# ローカルで配信するためのコード、本番にrelaseするためのコードなどを書いていく

npmとYarnでは、コマンドが少しずつ変わっているので注意が必要です。
下記のnpmとYarnのコマンドを比較した表を参考にしてみてください。

npm Yarn
npm install yarn install
(N/A) yarn install –flat
(N/A) yarn install –har
(N/A) yarn install –no-lockfile
(N/A) yarn install –pure-lockfile
npm install [package] (N/A)
npm install –save [package] yarn add [package]
npm install –save-dev [package] yarn add [package] [–dev/-D]
(N/A) yarn add [package] [–peer/-P]
npm install –save-optional [package] yarn add [package] [–optional/-O]
npm install –save-exact [package] yarn add [package] [–exact/-E]
(N/A) yarn add [package] [–tilde/-T]
npm install –global [package] yarn global add [package]
npm update –global yarn global upgrade
npm rebuild yarn install –force
npm uninstall [package] (N/A)
npm uninstall –save [package] yarn remove [package]
npm uninstall –save-dev [package] yarn remove [package]
npm uninstall –save-optional [package] yarn remove [package]
npm cache clean yarn cache clean
rm -rf node_modules && npm install yarn upgrade

まとめ

Yarnの特徴、移行についてをまとめてみました。
npmからYarnの移行に関して、npmのコマンドをYarnのコマンドに変更するだけで済んだのでとても手軽に出来ました。
またnpm v5からはYarnのlockファイルなどを取り入れており、Yarnもnpmもこれから発展していきそうです。

We’re hiring!!

Cluexではエンジニアサイド、ビジネスサイド共にメンバーを募集しています! お気軽にご連絡下さいませ!エンジニアの方、ぜひ情報交換しましょう!

www.wantedly.com

www.wantedly.com

5分でたどる!インフラアーキテクチャの歴史

f:id:cluex-developers:20170705174545j:plain designed by Creativeart - Freepik.com

こんにちは。エンジニアの内山です。 少しずつ暑くなってきましたね。季節の変わり目は体調を崩しやすいので、食事や運動により気を使っていきたいと思う今日この頃です。

今回はインフラのことについて書きます。私自身は普段Ruby on Railsを使って開発を行うwebエンジニアなのですが、以前インフラエンジニアとして仕事をしていた経験があります。日々プログラミングを行なっているとなかなかサーバやネットワークのアーキテクチャについては意識しないのですが、そういったインフラの変遷を知っておくこともweb開発の役に立つのではないかと考え、今回おさらいしてみようというわけです。

インフラとは何か

そもそもインフラとはなんでしょう。Wikipediaを調べてみましょう。 「インフラエンジニア」の項が参考になりそうです。

情報システムの構成要素には様々な技術が使用されているが、H/W、仮想化、OS、ネッ トワーク、ミドルウェア、セキュリティといった基盤となる領域を担当するのがインフラ エンジニアである。(中略)IaaS等のクラウドコンピューティングやデータセンター、システム運用も主要な職務範囲である。アプリケーションを含んだ全体最適化等に取り組むインフラエンジニアはITアーキテクトと呼ばれる。

引用元:インフラエンジニア - Wikipedia

つまりインフラとはITを使って仕事をしたり、サービスを受けたり提供したりするときに、その土台となる設備や情報機器のことですね。より具体的には、サーバやネットワーク機器を想像すると良さそうです。また、要件に応じたインフラの構成をアーキテクチャというようです。

集約型から分割型へ

システムの黎明期は、大型コンピュータ(ホスト、メインフレーム)で全ての業務を行う形態でした。いろんなアプリが一つのコンピュータに載っているイメージです。こういった構成を「集約型」と呼びます。 確かにコンピュータは1台ですから管理は簡単そうです。しかしあらゆる業務アプリが稼働しているわけですから、その1台が故障するだけで業務が止まってしまいます。故障しないように部品を多重化したりメンテナンスしたりするわけですが、コストがかさみます。 f:id:cluex-developers:20170705174950p:plain

集約型の特徴
 メリット
  - 構成がシンプルで管理しやすい。
 デメリット
  - 大型コンピュータの導入コストが高い。
  - 1台故障したときの影響範囲が大きい。
  - 停止しないように部品を多重化するなど、維持コストが高い。
  - 拡張性に限界がある。

そこで、各機能を複数の小型サーバに分けて一つのシステムを構築するという発想が生まれます。これを「分割型」と呼びます。安いコンピュータ(サーバ)を何台か調達して、それぞれのサーバで業務アプリを稼働させるのです。これなら1台壊れても全ての業務が止まってしまうことは避けられます。とはいえ、一台のサーバが故障すると、そのサーバで動いていたDBや他の機能まで使えなくなってしまうので、他システムとの連携などに影響が出てしまいます。 f:id:cluex-developers:20170705175006p:plain

分割型の特徴
 メリット
  - 小型サーバを使用して安価にシステムを構築できる。
  - 拡張性が高い。
 デメリット
  - 台数が増え、管理が複雑になる。
  - サーバが壊れたときの影響範囲はまだ大きい。

垂直分割型へ

上記の分割型アーキテクチャでは、サーバ故障時の影響範囲が問題になっていました。そこで、webシステムではおなじみの3階層型が登場します。すなわち、webサーバ、APサーバ、DBサーバの構造です。システムを分割し、サーバごとに単機能を持たせてやるという発想です。これならwebサーバが壊れたとしても他のシステムにDBのデータを連携でき、影響を抑えることができます。 f:id:cluex-developers:20170705175016p:plain

垂直分割型の特徴
 メリット
  - 特定のサーバへの負荷集中が改善される。
  - 特定のサーバで発生した障害が他のサーバに影響しづらい。
 デメリット
  - サーバの台数が増え、管理は依然として複雑である。

水平分割型へ

システムを3階層型で構成し、故障時の影響範囲を抑えることができました。垂直分割では機能ごとにサーバを分割しましたが、さらにユーザが増えてくると、今度は負荷を分散し信頼性を確保するために同じ機能のサーバを拡張していく必要が出てきます。 そこで最後に水平分割型を紹介します。同じ用途のサーバを増やすことで信頼性、性能を向上させます。これにより1台の故障が全体に与える影響がさらに下がります。また、こういったアーキテクチャでは多くの場合ロードバランサなどを利用して負荷を分散します。 f:id:cluex-developers:20170705175026p:plain

水平分割型の特徴
 メリット
  - 信頼性が向上する。
 デメリット
  - サーバの台数が増え、管理は以前として複雑である。

さらに進化はつづく・・・

インフラの変遷について駆け足で見てきましたがいかがでしょうか。インフラが進化していく過程は、さまざまなメリットを獲得したり課題を克服していく歴史だということがよくわかると思います。 上記の説明で、台数が増えて管理が煩雑になったサーバですが、後に仮想化技術やクラウド技術が登場することで大きく改善されていくことになります。

かつては多くの企業で物理サーバをオンプレミスで構築することが常識でしたが、クラウドの登場により、高い信頼性を確保しつつ、コストの面ではより安価に、調達リードタイムの面ではより迅速に(クリックするだけで!)構築できるのが今日のインフラです。 提供するサービスに応じて、最適なインフラを構築しましょう。 今後もいったいどのような革命的なインフラ技術が登場するのか楽しみですね。

(参考図書:山崎泰史他『絵で見てわかるITインフラのしくみ』、翔泳社

We’re hiring!!

Cluexではエンジニアサイド、ビジネスサイド共にメンバーを募集しています!
お気軽にご連絡下さいませ!エンジニアの方、ぜひ情報交換しましょう! www.wantedly.com www.wantedly.com

「Swiftデ、オ天気アプリヲ作成セヨ」

こんにちは。エンジニアの高島です。
先日、iOS開発にアサインされる前に大体の感覚を掴むために課題を頂きました。
その課題をこなす際に詰まったところを記事にさせて頂こうと思います。(課題未完)

課題の内容

事前講義

  • UIViewController, UITableView, UITableViewCellの説明
  • APIからJson取得してパースする時の流れの説明
  • Podfileの説明 ほか、基礎知識について

テーマ

「お天気アプリ」 ざっくりこんな感じです。 f:id:cluex-developers:20170627171210j:plain slackに送信するボタンをつける、Alertボタン押して都道府県画面に戻る機能は、一度基本機能を作った後で追加課題という事で実装しました。
その後、機能が一通り実装できたところで、クリーンアーキテクチャに則らせる(+RxSwift使う)という、3段階の流れで課題を進めています。

実装

以下、CleanArchitectureでの実装になります。
こういうやり方もあるよ!等ありましたら、是非ご意見頂けると嬉しいです。

詰まった箇所

1. mappingがうまくいかない

今回使用させて頂いたAPIは、livedoor天気情報
タイトル, 天気概況文, 今日の天気アイコンのURL, 今日の最高気温(摂氏), 今日の最低気温(摂氏)を使って実装しています。
敢えて今日のとつけたのには理由があって、これらの情報は今日、明日、明後日の3日分の配列から取得できます。
配列をどのようにObjectMapperでmapするのかは講義で学んだものの、ここでうまくパース出来なくて詰まりました。

解決策

構造体を別途準備して配列に持たせました。

struct ForecastEntity {
    var imageUrl: String?    // map["image.url"]
    var max: String?            // map["temperature.max.celsius"]
    var min: String?             // map["temperature.min.celsius"]
}

struct WeatherEntity {
    var title: String?                             // map["title"]
    var description: String?                 // map["description.text"]
    var forecasts: [ForecastEntity]?   // map["forecasts"]           ※ここに上の構造体を入れています
}

2. Entityが配列の時にいつ分けるのか

練習も兼ねて、都道府県名とIDをDataStoreに置く際に、タプルの配列型にしていました。
DataSotre

let prefectures: [(name: String, id: String)] = [
    ("埼玉", "110010"),
    ("東京", "130010"),
    ("千葉", "120010"),
    ("静岡", "220010"),
    ("茨城", "080010"),
    ("山梨", "190010"),
    ("群馬", "100010"),
    ("岩手", "030010"),
    ("福島", "070010"),
]

Entity

struct PrefectureListEntity {
    var list: [(name: String, id: String)]
}

といった形です。 しかし、Viewではセルに値を渡す為、配列でなく要素を取得する必要があります。
配列から要素に分けるタイミングで少し悩みました。

解決策

ViewModelの段階で要素を取得する様にしました。
ModelからViewで使える様にViewModelに変換しているのですが、その際に、

struct PrefectureListViewModel {
    func PrefectureViewModelProtocol(index: Int) -> PrefectureViewModelProtocol {
        return PrefectureViewModel(prefecture: prefectureList.list[index])
    }
    ...

この様な形で、配列から要素であるPrefectureViewModelを取得出来る様にしています。

3. xibを作らずにUIStoryboardの上にUITableViewCellを作る

都道府県リストだけでなく、天気画面やお問い合わせ画面もUITableViewを配置して、各セルに分けて作成しています。
例えば、お問い合わせ画面の場合、名前ラベルとテキストフィールドで1つ、といった形です。
クリーンアーキテクチャに則る前はそれぞれxibを作成していたのですが、則るタイミングで別途xibを作らずに、UIStoryboard上に全て配置して作成することにしました。 f:id:cluex-developers:20170627171214p:plain 変更してから実行してみたところ、dequeueResusableCellを呼ぶとCould not load NIB in bundleが出てセルが表示されなくなりました。

解決策

xibを使う際は必要なUINibの初期化部分を消しました。

let forecastNib = UINib(nibName: CellNames[Cells.Forecast.rawValue], bundle: nil)
tableView.register(forecastNib, forCellReuseIdentifier: CellNames[Cells.Forecast.rawValue])

4. UITextViewで高さ自動設定時に高さが0になる

天気画面とお問い合わせ画面はheightForRowAt, estimatedHeightForRowAtともにUITableViewAutomaticDimensionを返すようにしています。 この時、UITextViewの高さが0になる事に頭を悩ませていました。
これが実装中一番困ったことだったと言っても過言ではない程度には。

解決策

ConstraintsでHeightを付与しました。
Top, Bottomは与えていたので問題ないのかと思っていましたが、このHeightが重要でした。
なぜそうなのかは以下の記事で学ばせて頂きました。
http://cockscomb.hatenablog.com/entry/uitextview-on-uitableview

UITextView の intrinsic content size はどうかというと、そのままでは width も height も -1 の CGSize が返る。UITextView は UIScrollView を継承しているので、表示する内容に関わらず、ちょうどよいサイズというものを持たないのである。

現状

構成

f:id:cluex-developers:20170627171217p:plain

残り

  • キーボードに合わせてスクロールする量がキーボードの予測変換を考慮できてない(計算の仕方が単純におかしいのかもしれない)
  • Warningが出ている(Alertを二度表示しようとしていて出る, キーボード出した時に出る)

まだあと少し残っていますが、実装中に詰まったところはこのくらいでしょうか。
まだまだ写経してる感があるので、早く使いこなせる様になりたいです。

We’re hiring!!

Cluexではエンジニアサイド、ビジネスサイド共にメンバーを募集しています!
お気軽にご連絡下さいませ!エンジニアの方、ぜひ情報交換しましょう! www.wantedly.com www.wantedly.com

自己紹介 - 高島

f:id:cluex-developers:20170606100933j:plain

はじめまして!入社3ヶ月目のエンジニアの高島です。
今回は初ブログ記事ということで、技術記事は免除してもらい、自己紹介をさせて頂きたいと思います。

と言いましても、入社までの経緯や理由に関しては以前Wantedlyのインタビュー記事を書かせて頂きましたので、興味がおありでしたらぜひご覧ください。 www.wantedly.com

ここでは入社してからの所感について書かせて頂きますね。

入社してからの振り返り

振り返ってみて、この2ヶ月は今までの経験や考え方とのギャップを埋める期間だったなと感じます。

前提

自分の今までの開発経験は、専門学校で作成していたものを含めても(クオリティはさておき)、

機種 言語 内容
Windows C,C++ シューティング2D,3D
C++ ノベルゲーム
音ゲー要素持たせたブロック崩し
C# 帳票アプリ
Xbox C# アクション2D,3D
iPhone C++ ミニゲーム
アクション

など、思い出せる限りでこのような感じなので、今までWebに関わったことがありませんでした。 通信周りもなかなか理解ができなくて、避けて通って来ていましたし、ほぼWebの知識はないと言っても過言ではないところからスタートしました。

ギャップ1: Git

今までの開発環境で使っていたバージョン管理は、Subversionでした。 大した違いはないだろうと思っていたのですが、PullRequestやrebaseなど、耳慣れない言葉がポンポン出て来て戸惑いました。 SVNを使ってる時も、既にあるブランチにぽんぽんコミットしていったり、マージしたりしかしていなかったので、リポジトリに対しての意識もなかったですし、どうにもブランチ切るのがおっかなびっくりになります。 未だにPR出すのもドキドキします。

ギャップ2: インプットの習慣

これは本当にお恥ずかしい話なのですが、今までは自分の興味のある情報をたまーにチラ見したりする程度で、最新技術を追ったり、常に情報を仕入れたりはしてきませんでした。 最新ゲームや最近のゲームの動向は掴もうとしていましたが、それも海外のサイト見たりしてた訳でもないですし、今思えば甘かったなと思います。
Web界隈は範囲も広ければ、流行り廃りも早く、常に情報収集は当たり前の世界なので、生活の中でインプットしていく習慣をつけないとついていけないと思いますが、この習慣をつけることに今一番苦労しています。 技術系の基礎を身につけながら、情報のキャッチアップをしていくという2本立てにどう時間配分をするか、早く落としどころを見つけたいと思います。

ギャップ3: 公開までの工程

サイトの開発だと、Staging環境で動作確認をして、その後即リリースという流れですよね。 このリリースの早さが良くもあり、怖くもあります。 というのも、α→β→マスター版の工程や、テスターの存在になれきっていたのもあって、「ここで全部のバグを検知出来ないと本気でまずい!」という危機意識が薄いところがあったからです。 Cluexではコードレビューがあるのですが、それがなかったらと思うとぞっとしますね。もっと慎重さを持って、気をつけていかないといけないなと思います。 レビューは、まだまだ分からないところが多く、人がレビューしたのを見たり、質問するばかりですが、早く有意義なレビューをしていきたいと思います。

ギャップ4: ベンチャーでの開発

一人の采配が大きいという理由もあって、ベンチャーを選択しましたが、自分が予想している以上で、戸惑う程でした。 任せて頂いたタスクがユーザーに直結しているというのを日々痛感します。
また、苦手なところも自分でやらなくてはいけないのも、ベンチャーならではかなと思います。 今までは行き詰まって、ここで詰まっててこのくらい遅れそうですと報告すると、そのタスクは掬い取られて他のタスクを回される、ということが多かったので、苦手なものはあまり回されてきませんでした。 しかし、人数がそこまでいない状況だと自分が必要な分は自分で準備する必要があるので、苦手とも言ってられず、やるしかありません。 追い込まないと動かない(自分のような)人にはうってつけですね。ちょっと焦りますけど。

これから

大きくギャップがあったのはこの辺りでしょうか。
入社して2ヶ月と少しが経ち、今ようやく概要を掴めたかなと感じています。 これからは、使われている各技術についての基礎知識をつけていかなければと思います。 焦ると碌なことにならないのは今までの経験から分かっているので、焦らず急いで、しっかりアウトプットしながら一つずつ技術を習得していきます。

これからもよろしくお願いします。

We’re hiring!!

Cluexではエンジニアサイド、ビジネスサイド共にメンバーを募集しています!
お気軽にご連絡下さいませ!エンジニアの方、ぜひ情報交換しましょう! www.wantedly.com www.wantedly.com

エンジニアが暗号理論を学んでみた。〜共通鍵暗号と公開鍵暗号〜

こんにちは、神山です!

先日GWでしたが、皆さんはいかがでしたか。
自分は2日目から風邪を引いて、GWの最後まで寝ておりました。

東京の風邪と夜通しのモノポリーほど長引くものはないですね。

さて、前回は暗号理論の基礎的な内容を書いたので、今回は2種類の暗号方式(共通鍵暗号公開鍵暗号)について説明しようと思います。

前回の記事はこちら

f:id:cluex-developers:20170512201053j:plain

では、まず共通鍵暗号について解説していきます。

共通鍵暗号

共通鍵暗号とは暗号化と復号に同じ鍵を使用する暗号方式です。秘密鍵暗号とも言います。

身近な例

合言葉や共通認識等は共通鍵暗号です。

よくお酒を飲みに行くときに「一杯ひっかけます?」と言ったりしますよね。これは「お酒を飲みに居酒屋に寄って行きます?」ということを暗号化しております。
「お酒を飲みに居酒屋に寄って行きます?」を「一杯ひっかけます?」と暗号化していることを受け手が知っていれば、きちんと復号して飲みに行くことを理解出来ます。

あまりに身近な例なので、今度は有名な共通鍵暗号を紹介します。

シーザー暗号

これは英文を伝える時に、全てのアルファベットを指定数分ずらして英文を送ることです。

シフトさせる文字数を3文字としたとき。
a -> d
k -> n
のように、3文字シフトさせて暗号化する。

「THE ALFEE」
をシーザー暗号(文字数3)で暗号化すると、
「WKH DOIHH」
となる。

鍵は文字数(今回だと「3」)です。
受け手がこの鍵を知っていれば、送られてきた英文を3文字前に戻すことで「THE ALFEE」を得ることが出来ます。

しかし、共通鍵暗号には重大な欠点があります。

鍵共有問題

これは肝心の鍵を共有する安全な方法がないということです。
共有鍵暗号はお互いに鍵を知っているという前提のもと成り立ちます。その為、まず鍵を安全に伝える手段がないと使う事ができません。

口頭で伝えるという原始的な手段はあります。しかしこれはあくまで伝えたい人が近くに居る状態で、誰にも聞かれない状態でなければなりません。
そもそもこの状態であれば、暗号化する必要もないでしょう。

通信手段を使うには、安全に伝えられる方法を探さなければなりません。安全に伝える手段こそ暗号なのですが、皮肉なことに暗号を使うために暗号が必要になってしまいました。
これでは鶏が先か、卵が先か問題になってしまいます。

f:id:cluex-developers:20170512201217j:plain

さてこの問題を解決してくれるのが、公開鍵暗号です。

公開鍵暗号

公開鍵暗号とは、暗号化する鍵と復号する鍵が違う暗号方式です。暗号化する鍵(公開鍵)は公開し、復号する鍵(秘密鍵)は秘密に管理します。
RSA暗号公開鍵暗号の代表的な例です。

身近な例だと難しいので、少し特殊ですが分かり易い例を用意しました。

南京錠

ある南京錠があります。この南京錠を開ける鍵はAさんが持っている一本の鍵のみです。もちろん複製や他の手段では開けられません。
またこの南京錠自体はどこでも手に入るものだとします。

Aさんに秘密のメッセージを送りたいときは、メッセージを適当な箱に入れてこの南京錠でロックします。
すると当然ながらこの箱を開けられるのは鍵を持っているAさんしかいません。つまりAさんに安全にメッセージを伝えることが出来ます。

これが公開鍵暗号の仕組みです。南京錠自体はいくら公開しても問題ありません。

では実際に使われている公開鍵暗号が、具体的にどのような仕組みになっているか説明します。

※ ここからは数学の話が出てきます。ただ出来るだけ分かり易く言葉で説明するので、数学に抵抗意識がある人もどうか読んで頂ければと思いますm( )m

一方向性関数

一方向性関数とは、一方の計算は簡単なのにもう一方の計算は極度に難しい関数のことです。

たとえば、1454867素因数分解してみてください。

PCを使わずに、出来た人はすごいです。答えは911 × 1597です。

さて、では911 × 1597を計算してみてください。こちらはPC使わなくても簡単ですね。もちろん1454867です。

みなさんも体感したように、素数を掛け合わせるのは簡単ですが、それを素因数分解するのはとても難しいことです。
これは人間だけでなくPCも同じです。桁数は大きくなりますが、数百桁の素数を掛け合わせた数をPCに素因数分解をさせると、解くのに何万年という膨大な時間が掛かります。 時間が無限にあればいつかは解けますが、現実的に考えると難しいと言えます。

一方向性関数を使った南京錠

先程、南京錠の例を用いたので、今回はそれに類似させた例を作りました。

Aさんのアドレスが書かれている50桁のダイヤルが付いた南京錠があります。また謎の数百桁の数が書かれてあります。これを開けるにはこの数を素因数分解して、一番大きい素数の最初の50桁をダイヤルにセットしなくてはいけません。 (桁数の50は適当です。。)
Aさんにメッセージを送りたいときは、同じように適当な箱にメッセージを入れてこの南京錠で閉じます。

もうお分かりかと思いますが、この南京錠を開けられるのは素因数分解の答えを知っているAさんのみです。

つまり相手の公開鍵さえ知っていれば、自分の情報をその人に安全に伝える事ができます。

RSA暗号

公開鍵暗号を使用した暗号の代表例です。 現在の安全な通信技術にはRSA暗号が使用されております。 仕組みは素因数分解を用いているのですが、理解するには数学の高度な知識が必要になるので、また別の機会にでも説明しようと思います。

まとめ

共通鍵暗号公開鍵暗号について説明しました。理解をして頂くことに重点を置いたので直感的な内容になってしまったかも知れません。
間違いなどがありましたらご指摘をお願い致します。

APIにGolangを導入しました

こんにちは、Cluexの高橋です。

社内で開発を進めているモバイルバックエンドのAPIに初めてGolangを導入しました。

Cluexでは創業から現在に至るまでの3年間、Webアプリケーションの開発にRuby on Railsを使用してきました。 Golangの導入自体はそこまで真新しいものでも無くなりましたが、Golangに関して社内に開発ノウハウはまだなく、Gopherももちろんいません。

そうした状況の中でGolangを導入した理由や、実際にGolangで開発をスタートしてからの事などを、複数に分けて書いていきます。

今回はGolangの導入に至る背景や、導入に際して考慮した事などを簡単にまとめていきます。

公式ブログによると、

1. 簡潔さ

2. 並行処理

3. 処理速度

Golangの盛り上がりを支えているようです。

f:id:cluex-developers:20170428200508p:plain

Go 2016 Survey Results - The Go Blog

本番環境のインフラストラクチャ

実際に本番環境で使用するにあたって、 インフラの構成やデプロイ周りは必ず考慮しなければなりません。

これまで本番環境にはAWSのEC2を使用していましたが、今年の3月にAWSのECSによるDockerでのサーバー運用にインフラを全面移行しました。

cluex-developers.hateblo.jp

Dockerによるサーバー運用に切り替えたことで、従来のEC2での運用に比べてサーバーの運用・管理が楽になりました。

Infrastructure as Codeの浸透

これまでもChefやAnsibleのようなInfrastructure as Codeを導入してインフラの構築を行なっていましたが、 徐々にコードが管理されなくなってしまったり、どうしても障害発生時の急な対応などからサーバー間の環境差異が発生していました。

今回の移行によってサーバー環境も全てDockerfileとyamlによる管理になり、常にコードでインフラ環境を管理するようになりました。

本番環境と開発環境の差異

開発環境も全てDocker及びDocker Composeによる管理にしたことで、コマンド一発でポータブルな開発環境の構築が可能になり、 本番環境との差異も以前に比べて格段に無くすことができていると思います。

本番稼働系も全てDockerで管理している為、別の(新しい)言語に移る時もインフラ構築の障壁が低くなったことがGolangの導入を後押しする大きな要因となっています。

モノリシックサービス化の予兆とマイクロサービス化

これまで運営しているサービスが一つだったということもあり、1つのRailsアプリケーションに全てのソースコードが入っていました。 しかしコードベースが増えていくにつれて全容の把握が難しくなり、エラーやバグを生む温床となっていきます。

複数サービス間での連携を踏まえたサービスの多角化を行う上では、このような状況はより一層好ましくありません。

小さなアプリケーションの集合体として全体のアプリケーションを管理していく為にも、 今回は別アプリケーションとしてAPIのアプリケーションを組むことにしました。

APIとしての実行パフォーマンス

ちょうど今日、バージョン5.1が正式に発表されましたがRuby on Railsは依然として、とても素晴らしいフレームワークです。コミュニティも非常に盛んで、欲しい機能はgemを探せば大体見つかりますし、スタートアップにおけるwebアプリケーション開発には欠かせない、非常に頼もしい存在です。

ですが、今回のようなモバイルバックエンドのAPIだとRuby on Railsはパフォーマンスの面でデメリットが大きくなってきます。 マイクロサービス化していく中で今回のAPIも別アプリケーションにするので、慣れ親しんでいるという理由の他にRuby on RailsAPIを組む大きな理由がそこまでありません。 Rubyだとマルチスレッド処理の実装が大変である故にマルチプロセスで処理を行なったり、メモリの消費量が多くなったりという事がずっと言われており、言語自体の処理パフォーマンスにおいてGolangが良さそうです。

ライブラリ選定

WebアプリケーションフレームワークにはEcho、ORMにはgormを採用しました。

GitHub - labstack/echo: High performance, minimalist Go web framework

GitHub - jinzhu/gorm: The fantastic ORM library for Golang, aims to be developer friendly

またrevelなどフレームワークに入っているようなホットリロードの機能がない為、開発環境にはfreshを使用したホットリロードを行なっています。

GitHub - pilu/fresh: Build and (re)start go web apps after saving/creating/deleting source files.

フレームワーク

Webアプリケーションフレームワークに関しては、APIを構築するということで、フルスタックな機能は必要ありません。 とはいえhttpサーバーとしての機能やルーティングなど、基本的なリクエストとレスポンスの処理はフレームワークに任せたいところです。スター数や更新頻度、issueなども勘案して今回はEchoを選びました。

GolangフレームワークRubyでいうSinatraRailsのような、立ち位置が定まっているフレームワークがまだはっきりしていない状況です。 またirisのような、スター数は多くてもOSSでの開発方針として、一般的な方針とは乖離が大きいフレームワークもあったりします。

ORM

ORMに関しては、現状Golangの主なWebアプリケーションフレームワークにはORMが入っていません。 ORMのライブラリとしてはいくつも候補がありますが、gormはActiveRecordに近い書き方で実装出来るようになっている為、Railsエンジニアも比較的手をつけやすいと思います。

サードパーティパッケージ

フレームワークやORMを含めて、GolangサードパーティのパッケージはGemに比べるとまだまだ発展途上で、似たようなものが乱立している状況です。

下手にアプリケーションに取り込んで後に更新が止まったりしてしまうと、負債となってしまいそうなので、可能な限り少ないライブラリで対応するようにしています。

ライブラリもフレームワークも、別のパッケージに乗り換えやすいようなコードベースへの配慮が必要かと思います。

まとめ

Golangの導入の背景や、導入に際して考慮した点などを簡単にまとめました。

また近いうちに、実際に開発していく上で良かった面や大変だった面などを記事にしていければと思います。

Docker with ECS × Railsを実現させるために考えたこと(デプロイ編)

こんにちは。エンジニアの志村です。

cluex-developers.hateblo.jp

の続きとなります。 今回はタイトル通り、デプロイ辺りを執筆していければと思います。

Blue-Green Deployment

Blue-Green Deploymentはマーチン・ファウラー氏が提唱したデプロイ方式です。 blue, greenとほぼ同じ本番環境を2系統用意してデプロイを行います。このデプロイ方式ですが、

  1. ダウンタイムの極小化
  2. ロールバックが容易

という大きな2つの利点があります。

Dockerizeへのモチベーションとして前回の記事にも書いたのですが、徐々にドッグフーディング環境・本番環境で差異が出てきました。
そのような状況だと、ドッグフーディングでは正しく動作したのに本番環境では意図した挙動とは違うという場面も出てきます。
上記のようなトラブルがあった際に今までのIn place型のデプロイ方式だとロールバックに時間がかかり、ユーザーに迷惑をかけてしまう時間が長くなるという問題がありました。
Blue−Green Deploymentでは特に2.の「ロールバックが容易」という点が非常に魅力でした。
何かトラブルがあったとしてもELB設定を変えてしまえば瞬時に旧環境に戻せるという点で、デプロイに対する心理的ハードルが社内全体で下がりました。

また、ECSの仕組みにより今までのようにCapistranoによるSSHを用いたデプロイが難しかったという理由も相俟って、弊社でもBlue−Green Deploymentを用いております。

ECSによるBlue-Green Deployment

前回の記事にも書いた通り、ECSを使用した際のBlue−Green Deploymentにはいくつかやり方が存在します。

  1. ECSのMaximum healthy percent, Minimum healthy percentを調整し、徐々にタスクが切り替わるように調整する。
  2. CLB(Classic Load Balancer)× Autoscaling Groupを使用して切り替える
  3. Routes53を使用し、Standby, ActiveのELBを切り替える
  4. ALBに紐付いているTarget groupの優先順位を切り替える

ECSのMaximum healthy percent, Minimum healthy percentを調整し、徐々にタスクが切り替わるように調整する。

利点

  • ECSデフォルトの機能を用いるので、他に余計な操作をする必要が一切ない
  • タスクの増減を元にデプロイを行うのでインスタンス料金を低く抑えることが出来る

欠点

ECSの特徴が最大限発揮されたデプロイ方式だと考えます。
ECSには「タスク」という概念があります。タスクはDocker imageを起動させる際の設定、またそのリビジョンです。
タスクは

  1. 使用するdocker image
  2. コンテナ間のリンク
  3. コンテナのコマンド
  4. データボリューム

等が設定できます。 リビジョンが用意されたdocker-composeだと考えると分かりやすいかと思います。
タスクに関しては公式のドキュメントが分かりやすいです。
Amazon ECS タスク定義

そのタスクを管理するのがサービスという概念です。
サービスはざっくり言うと、ロードバランサーとの連携、上記のタスクをインスタンスにどう配置するか、最大・最小・維持タスク数をいくつにするかの取り決めです。

ECSのサービスのデプロイメントオプションに、maximum helathy percent(最大率), minimum healthy percent(最小ヘルス率)という項目があります。
この値を調整することによって、Blue-Green Deploymentを実現することが出来ます。

例えば
* maximum healthy percent: 200%
* minimum healthy percent: 100%
と設定すれば、
デプロイ時に通常のタスク数の2倍、デプロイ終了後にタスク数1倍という形になります。

仮に1インスタンス1タスクという戦略であれば、デプロイ時に2倍のインスタンス数が必要ということになります。
* maximum healthy percent: 50%
* minimum healthy percent: 100%

個人的な感触ですが、既存のタスクをkillして新しいタスクに入れ替えるのに結構な時間がかかっている印象でした。
つまりは、ロールバック時にも同じ作業をしなければならない≒ロールバックに時間がかかると予想される為、今回は見送りました。

CLB(Classic Load Balancer)× Autoscaling Groupを使用して切り替える

利点

  • 設定がほとんど必要ない。実装が楽
  • ロールバックが早い
  • ダウンタイム無し

欠点

  • SpotFleetが使用できない
  • Blue, Greenが重なるタイミングがある

恐らく現時点で最もポピュラーなBG Deploymentの方式かと思います。 実装もAutoscaling Groupを作成すればOKという簡便さです。

ALBを今回使用したいと考えていましたが、コンソールを見てもALBが使用できない雰囲気ではありました。 しかしながら、現在では下記のように使用できるようです。

dev.classmethod.jp

これで課題は1つクリアしましたが、SpotFleetがAutoscaling Groupに登録出来ない為、今回この方式は断念しました。
ロールバックを容易かつスピーディにするにはインスタンスの数が単純に倍必要となります。SpotFleetの使用は予算の都合上、必要でした。 またこの方式だとヘルスチェックの観点から、Blue, Greenが重なるタイミングがどうしても出てきます。
マイグレーション・モデルの変更をした際に、旧稼働系・新稼働系が混ざるとエラーを引き起こし兼ねません。この観点からも今回は見送りました。

Route53を使用し、Standby, ActiveのELBを切り替える

利点

  • ダウンタイムほぼ無し
  • シンプル

欠点

一番古典的かつ確実方式です。 Route53のWeightを利用し、Blue→GreenのELBに徐々に切り替える方式です。
実装がシンプルではありますが、一番の問題点はDNSキャッシュです。
キャッシュサーバによってはTTLを無視して一定期間キャッシュするものもあるらしいので、実際にデプロイが完全に完了したかを確認するのが難しくなります。
上記のようなキャッシュのコントロールの難しさから、今回この方式は見送りました。

ALBに紐付いているTarget groupの優先順位を切り替える

利点

  • ダウンタイムほぼ無し
  • ロールバックが早い
  • メンテナンスにすぐ切り替えられる

欠点

  • 実装が面倒

結論から言うとこの方式を採用しました。
いくらか利点はありますが、特にロールバックが容易な点が大きいかと思います。
ALBにはパスベースルーティング、ホストベースルーティングという機能があります。
パスベースルーティングは下記にある通り、パスによってどのターゲットグループにトラフィックを流すかを決定できます。

tartget.png

例えば、

IF: /target1/*, THEN: target1
IF: /target2/*, THEN: target2

のように設定すると、 host/target1/の際にはtarget1に紐付いたインスタンスに、
host/target2/の際にはtarget2に紐付いたインスタンストラフィックを分岐することが出来ます。
また、このルーティングでは優先順位というものがあります。
同じパスであっても優先順位が高い(数字が小さい)方にトラフィックが流すということが可能になります。この特徴を活かし、

優先順位 パス ターゲットグループ
1 * target-blue
2 * target-green
last Requests otherwise not routed target-maintenance

上記のように設定しました。
こうすると通常、blueにトラフィックが流れます。
デプロイ時にこの優先順位を切り替えると、green系にトラフィックが変わります。
ロールバック時も同じように優先順位を変更するだけで、一瞬で切り替わります。
メンテナンスページを出したい場合、他のルーティングルールを削除すればOKです。

上述の通り、ALBの特徴を使用すれば一瞬でロールバックが可能になり、さらにメンテナンスページまで容易に出すことが出来ます。

欠点としてはこの部分の実装が面倒だという点です。
今回、harmonikというデプロイツールを用意しました。

harmonik

事情によりGithub等に公開はしておりません…(早くしたい)
harmonikは今回のBG Deploymentを使用するにあたって新しく作成したデプロイツール(CLI)です。
主に下記のような機能があります

  1. デプロイ
  2. 単発のタスク起動
  3. ロールバック
  4. ステータス表示

1.は他のECS向けデプロイツールとほぼ同じです。

ALBの優先順位からデプロイ先を決定
↓
task definitionを更新
↓
serviceを更新
↓
taskがRUNNING状態かどうか判別
↓
target groupのヘルスチェックがhealthyかどうか判別
↓
ALBの優先順位切り替え

上記のようにしてBG Deploymentを実現しております。

2.はマイグレーション等の単発のタスクに使用します。
CIでは、単発のタスク→Blue or Greenにデプロイ
という流れをとっております。

3.は単純で、ALBの優先順位を変更してロールバックさせているだけです

4.は稼働系のステータス(クラスタ名・インスタンス数・デプロイ履歴等)を表示させるものです。

以上になります。
この実装により、Capistranoとほぼ同じような感覚で簡単にデプロイをすることが可能になりました。
Dockerの運用に切り替えてからサーバのメンテナンス等に時間を割かなくて済むようになったので非常に運用が楽になりました。

※まだ運用から日が浅く、間違えている箇所があるかもしれません。
その際はお手数ですがコメント頂けますと幸いです。