GitHubフローを助けてくれるボットを作った
今回はGitHubフローを助けてくれる簡単なボットを作ったので紹介します。
ボット環境
弊社ではHubotを使ってSlack用のボットを作っています。
HubotはGitHub製のボットライブラリで、coffeescriptで簡単にかけて、そのままHerokuに簡単に上げることができます。
最初はSlackに移行したときにせっかくならボットを作ろうと思って始めて、ネタ機能が多かったり、新人研修の題材に使っていましたが、現在はユーザー数をログから計算して教えてくれたり、サービスにエラーが発生したら教えてくれたりなど実用的な機能を追加しています。
弊社でのGitHubフロー
開発のフローは基本的に以下のようになっています
- 実装方針などをissueに書く
- 実装する
- プルリクエストを出す
- レビューする
- マージする
普通のフローですが、弊社ではブランチ名にもルールを設けています。
(feature|refactor|fix)-[issue No.]-description
例をあげるとfeature-100-implement_user_registration
という感じです。
基本的にプルリクエストはひとつのissueに紐付いているため、プルリクエストがマージされるとそのブランチとissueは不要となるため消すことになっていたのですが、人間はサボる生き物なので徹底されずに、ゴミのブランチやissueが多く残ってしまっていました。
ボットを作った
そこで、プルリクエストをマージしたら関連するブランチとissueを自動的に削除するボットを作りました!
apiUrl = "https://api.github.com/repos/xxxx" querystring = require('querystring') module.exports = (robot) -> robot.router.post("/github/pr/cleaner", (req, response) -> data = req.body action = data.action pullRequest = data.pull_request if action == 'closed' && pullRequest.merged repo = pullRequest.head.repo name = repo.name branchName = pullRequest.head.ref # delete branch deleteBranchRequest = robot.http("#{apiUrl}/#{name}/git/refs/heads/#{branchName}") .header('Authorization', "token #{process.env.GITHUB_TOKEN}") .del() deleteBranchRequest((err, res, body) -> if err robot.messageRoom(channel_name, "Deleting #{branchName} was failed") ) # close issue issueNumber = branchName.match(/-(\d+)-/) if issueNumber[1] != undefined number = issueNumber[1] console.log("request #{apiUrl}/#{name}/issues/#{number}") closingIssueRequest = robot.http("#{apiUrl}/#{name}/issues/#{number}") .header('Authorization', "token #{process.env.GITHUB_TOKEN}") .patch(JSON.stringify(state: 'closed')) closingIssueRequest((err, res, body) -> if err robot.messageRoom(channel_name, "Closing \##{number} was failed") ) url = require('url') query = querystring.parse(url.parse(req.url).query) channel_name = query.channel user = pullRequest.user.login robot.messageRoom(channel_name, "Good job, #{user}!\nLeave the rest to me!") response.end('') )
これをHerokuに上げた状態でGitHubのWebhookにhttps://xxx/github/pr/cleaner?channel=yyy
というのを設定してプルリクエストのフックをとれるようにすればもう動きます。
これにより100%ブランチとissueが整理されるようになりました。
まとめ
このような感じで弊社ではボットを使って開発や運用の効率を上げる取り組みを行なっています。ボットがフローの一部となって人間の負担を減らしてくれるととても助かりますし、ボットにもっとすごいことをやらせてサービスを加速させることもできます。
Circle CI + AWS + Dockerでデプロイを自動化する
今回はタイトルの通り、Circle CI + AWS + Dockerの組み合わせで簡単にサービスのデプロイを自動化した話をします。
弊社サービスWorkinGoodではGItHub flowを採用しており、masterへマージした後に自動的にElastic Beanstalkへ新しいバージョンを作るところまで行っています。依存サービスとの関係もありデプロイするタイミングはこちらが手動でAWSのGUI上から行っています。
また、弊社では開発環境構築にDockerを使っていますが、本番環境へのデプロイにも使用しています。
http://blog.matchingood.com/entry/2016/11/11/145246blog.matchingood.com
これを実現するために具体的には以下のものを使っています。
この中でもデプロイを圧倒的に簡単にしているのがDockerとElastic Beanstalkの組み合わせです。Elastic BeanstalkとはAWSが提供しているサービスの一つで、あらかじめ用意しておいた環境定義ファイルによって自動的にAuto Scalingとデータベースを構築してくれるサービスです。
こちらに簡単な説明があったので見てみるといいと思います。
かなりお手軽にこの仕組みを作ることができるので簡単に手順を示していきたいと思います。
circle.yml
最初にcircle.ymlにどのように書けばいいかの例です。
machine: timezone: Asia/Tokyo services: - docker dependencies: pre: - sudo pip install --upgrade awscli deployment: production: branch: master commands: - ./deploy.sh $CIRCLE_SHA1
今回の話に関係のあるところだけ書きました。
deploymentにbranchをmasterに指定することでmasterブランチのCIが通った時にcommands以下が実行されます。Circle CI内ではGitHubのコミットIDが$CIRCLE_SHA1という変数に入っています。
aws-cliを使用するために$AWS_ACCESS_KEY_IDと$AWS_SECRET_ACCESS_KEYの環境変数を設定する必要がありますが、セキュリティ上の問題からバージョン管理に含めずにCircle CI上のUIから設定します。
deploy.sh
SHA1=$1 DOCKERRUN_FILE=$SHA1-Dockerrun.aws.json EB_BUCKET= # Elastic Beanstalkのバージョンファイルが入るS3のバケット名 aws ecr get-login | bash docker build -t {AWS ID}.dkr.ecr.us-east-1.amazonaws.com/{リポジトリ名}:$SHA1 . docker push {AWS ID}.dkr.ecr.us-east-1.amazonaws.com/{リポジトリ名}:$SHA1 sed "s/<tag>/$SHA1/" < Dockerrun.aws.json > $DOCKERRUN_FILE aws s3 cp $DOCKERRUN_FILE s3://$EB_BUCKET/$DOCKERRUN_FILE aws elasticbeanstalk create-application-version\ --application-name application \ --version-label $SHA1 \ --source-bundle S3Bucket=$EB_BUCKET,S3Key=$DOCKERRUN_FILE
Elastic Beanstalkのバージョンはただのjsonファイルで管理されているのであらかじめDockerrun.aws.jsonという名前で以下のファイルを用意しています。
{ "AWSEBDockerrunVersion": "1", "Image" : { "Name" : "{AWS ID}.dkr.ecr.us-east-1.amazonaws.com/{リポジトリ名}:<tag>" }, "Ports" : [{ "ContainerPort": "80" }] }
やっていることの流れとしては
という感じです。 ここまで行えばElastic Beanstalkに新しいバージョンが作成されているのがわかると思います。
注意点
Circle CI用に適切な権限を持ったIAMを用意する必要があります。以下の操作ができるようにIAMを作りましょう。
- S3のバージョンファイルが入るバケットへのアップロード
- Docker Container Registryへのpush
- Elastic Beanstalkのバージョンの作成
まとめ
簡単にデプロイの自動化ができそうなのがわかっていただけたと思います。弊社のような小規模のチームでの開発では自動化できるところは自動化を進めて効率を高める必要があります。 このようなモダンな仕組みもすべてアルバイトエンジニアによって構築されています。プログラミング初心者の方はこのような新しい仕組みを学ぶことができ、実力のある人はDevOpsをさらに磨き上げることもできる職場です。
PHP勉強会でLTしてきた話
10月のPHP勉強会@東京でLTをしてきたのでその話をしようと思います。
PHP勉強会の雰囲気
社内の勉強会には複数の会社に参加したことがあるのですが、社外でいろいろな人が来るような勉強会は初めてでした。
しかし、この勉強会は毎回半数近くが初参加で、毎回最初に全員で自己紹介するというとても馴染みやすい雰囲気でした。また、年齢も上から下まで色々な方がいましたし、女性の方も数人いらっしゃいました。
ちなみに学生は僕の他にもう一人いました。
LT
勉強会初参加なので当然LTも初めてでした。5分間の発表時間なので技術的な話題をしようと思うとかなり辛いです。最初はコードのスライドが5枚くらいあって5分じゃ無理だとなったので何回もリファクタリングしました。
以下が実際のスライドです。
評判
最初の方はテンションを上げてなんとか場を盛り上げられてた感があったのですが、いざコードが登場すると一気に反応が悪くなって失敗したなという感じでした。
きている人の層が初心者から上級者までさまざまなので、全員に受けるような話をしようと思うともっとシンプルにまとめる必要がありそうです。
一応2次会ではエンジニアの方に良かったと言ってもらえたのでホっとしました笑
まとめ
PHP勉強会@東京は初心者や勉強会初めての人でも参加しやすいので時間があれば是非参加してみるといいと思います。技術的な話を聞くだけでなく色々な人と出会えて、普段はできないようは話ができるのもいいところだと思います。次回の募集が始まっているので参加してみてはいかがでしょうか。
DockerでLaravelの開発環境を爆速で構築する
弊社では人材紹介会社様と人材派遣会社様向けのシステムの開発をしており、今年初めごろに派遣会社スタッフ様向けの新サービスであるWorkinGoodを始めました。
主にPHPを使用して開発していますが、新しいサービスに関してはAWSやDocker、Reactなどトレンドとなっているものに追随して技術を選択しています。
そこで今回は弊社の開発環境構築にDockerを採用した話をしたいと思います。
なぜDockerを導入したか
弊社ではプロダクションへのデプロイでもDockerを使用していますが、先ほどもお話ししたように弊社では複数のプロジェクトがあり、それぞれが異なるPHPのバージョンで動いています。このため毎回環境構築しようと思うと複数バージョンのPHPを入れる必要がありとても手間となってしまいます。
そこでDockerを使うことで気軽に環境を構築できるようにしました。
Dockerに関しては様々な記事が出ているのでここでは説明しませんが、今ではDocker ToolboxやDocker for Mac、Docker for Windowsのおかげで手軽に導入することができます。
どうやっているのか
ここではLaravelとMySQLの標準的な環境を想定します。
まずはLaravelのコンテナを作る必要があります。
FROM php:7.0.8-alpine RUN apk --no-cache add curl nodejs python make g++ &&\ npm install -g gulp &&\ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer &&\ docker-php-ext-install pdo_mysql curl WORKDIR /var/www/localhost/htdocs EXPOSE 8000 CMD ["php", "-S", "0.0.0.0:8000", "-t", "/var/www/localhost/htdocs/public"]
alpine linuxという軽量なイメージをベースにしたPHP7のコンテナを使用しています。
最後にPHP付属の開発用サーバーを起動して終わりです。
MySQLは特にカスタマイズしないので公式のものをそのまま使います。
Dockerコンテナ立ち上げ
# 用意したDockerfileのビルド docker build -t matchingood/application:dev . # 現在のパスを取得 path=$(cd $(dirname $0) && pwd) # MySQLの公式イメージ docker pull mysql # 各イメージを動かす docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -d mysql docker run -p 8000:8000 \ --link mysql:mysql \ --name application \ -v ${path}:/var/www/localhost/htdocs \ -t -d matchingood/application:dev # Laravelの設定ファイル cp .env.example .env # applicationとリンクしているMySQLのホストを取得 dbhost=$(docker exec application env | grep MYSQL_PORT_3306_TCP_ADDR | cut -d '=' -f 2-2) # Laravel設定ファイルのMySQLの情報をコンテナ内から見えるホストに置換 sed -i -e "s/DB_HOST=127.0.0.1/DB_HOST=${dbhost}/g" .env # MySQLコンテナにアプリケーション用のデータベースを作成 docker exec mysql mysql -u root --password=mysql -e "create database application;" # Laravelを動かすために必要なものたち docker exec application composer install docker exec application php artisan key:generate docker exec application php artisan migrate docker exec application php artisan db:seed
これでもうLaravelの開発環境を整えることができました!
Docker Compose
LaravelのコンテナにMySQLとElasticSearchのコンテナをリンクさせるときにlinkオプションで指定しています。ですが、Docker Composeと言うものを使うことで綺麗にこのあたりのオーケストレーションを行うことができます。ただ今回はそこまで複雑な構成ではないのでbashスクリプトで書いています。
まとめ
実際の例を見て、Dockerで簡単に環境を構築できることが分かったと思います。
AWSLambdaの私的Tips
今回はSQSLoggerとその周辺のお話ということで、AWS LambdaについてのTipsを書こうかと思います。
※SQSLoggerは私が以前作成したLaravelフレームワーク上で動くログデータをSQSへプッシュするライブラリです。詳しくはこちらの記事をご覧ください。
AWS Lambdaとは?
AWS Lambdaは、サーバレスでコードを実行できるAWSのサービスです。実行できるコードは現在Node.js, Python2, Java8に限られていますが、特に環境の設定をせず気軽にコードが実行できる点が魅力です。
実行タイミングは、cronのような時間に基づいた定期実行もできますが、AWSの他のサービスが発したイベントをListenして実行することもできます!実行タイミングの指定はWebUI上ででき、手軽です。
料金体系は使用した時間に対する課金で、無料利用枠も十分にあるので「定期的にコードを実行したいけどEC2のインスタンス借りるのはちょっと…」っていう人にもおすすめです。
本題: Node.jsでLambda Functionを書くときのTIPS
自分がLambdaを使うときはjsを使うのですが、以前はLambdaに関する資料や実装例があまりなく、結構苦労しました。そこで、自分がLambdaに触る上で手に入れたTIPSを書き残しておこうと思います。ちなみに筆者の環境はUbuntu15.10ですが、Macならおおよそ流用できるかと思います。
1. aws-sdkはいらない!
Node.jsを用いて書く場合、npmライブラリを使う場合はindex.jsとnode_modulesディレクトリをzipしてアップロードする必要があります。しかし、もし使うライブラリがaws-sdkのみである場合は、必要ありません。
これは、LambdaFunctionが実行されるインスタンス上で、requireできるパス上にすでにaws-sdkが存在するためです。こうすることでデプロイの時間を少し短縮することができます。
2. zipするときはzip -r project.zip index.js node_modules
!
とはいえ、npmライブラリがどうしても使いたい時があるとおもいます。そういう時はindex.jsとnode_modulesの入ったprojectというディレクトリを以下のようにzipしたくなりますが… $ zip -r project.zip project このzipファイルをアップロードするとLambdaに'index.jsが見つからない'と怒られてしまいます。これはLambdaの仕様で、展開したディレクトリ上に直にindex.jsがないといけません。よって正しいコマンドは
$ cd project $ zip -r project.zip index.js node_modules
になります。地味にハマるポイントではないでしょうか。
3. Nodeのバージョンは合わせておこう!
自分ではあまりはまったことがないのですが、普段node 6.x系使っているともしかしたら動かない可能性があるかもしれません。(とくにnaitive extensionを使っている場合とか)Lambdaの実行環境は2016/10/13現在では4.3なので、ローカルの環境を確認しておきましょう。
Nodeのバージョン管理といえば、rbenvやpyenvと同様にnodenvがありますが、個人的にはnpmでインストールできて気軽に使えるnがおすすめです。
$ npm install -g n $ n 4.3.0
これだけで終わり!(Linux環境等では適宜sudoしてinstallしてください)
4. ハンドラに渡される引数を有効に使おう!
Lambda関数を書く時、雛形ファイルとして与えられる書式には、関数の引数として3つの変数が用いられています。
exports.handler = (event, context, callback) => { . . . };
これらの引数はそれぞれ実行にあたって有効な情報を与えてくれます。
eventは言わずもがな、Lambda関数を発火したイベントの詳細情報が入ってきます。たとえば、S3上のファイル作成がトリガーになったときには、作成されたファイルのkeyやbucketを始めとした様々な情報が入ってきます。
"Records": [ { "eventVersion": "2.0", "eventTime": "1970-01-01T00:00:00.000Z", "requestParameters": { "sourceIPAddress": "127.0.0.1" }, "s3": { "configurationId": "testConfigRule", "object": { "eTag": "0123456789abcdef0123456789abcdef", "sequencer": "0A1B2C3D4E5F678901", "key": "HappyFace.jpg", "size": 1024 }, "bucket": { "arn": bucketarn, "name": "sourcebucket", "ownerIdentity": { "principalId": "EXAMPLE" } }, "s3SchemaVersion": "1.0" }, "responseElements": { "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", "x-amz-request-id": "EXAMPLE123456789" }, "awsRegion": "us-east-1", "eventName": "ObjectCreated:Put", "userIdentity": { "principalId": "EXAMPLE" }, "eventSource": "aws:s3" } ] }
contextにはさまざまな実行時情報が入ってきます。特によく使うのはgetRemainingTimeInMillisで、呼び出した段階での残りコンピュート時間をミリ秒で返してくれます。これをつかえば、残り時間が5秒をきったら処理をやめる、というようなことができます。なにかをループするような場合には以下のようにwhileの条件として使います。
while (context.getRemainingTimeInMillis() > 5000) { hogehoge(); . . . }
callbackは最近Lambdaのnodeのバージョンが上がって初めて追加されたもので、関数の実行結果を呼び出し元に返すのに用います。第一引数には関数が失敗した時の実行結果を表し、第二引数は正常終了した場合に用います。
callback(Error error, Object result);
使用は任意なので無理に使うことはないですが、AWS上でデバッグする必要があるときは重宝します。
まとめ
以上、Lambdaを使う上で気になるTIPSをいくつか書かせていただきました。LambdaはEC2やRDSなどの基幹サービスに比べれば迫力は落ちますが、応用できる幅が非常に広いサービスです!イベントが発生した時にSlack等になにか軽いNotificationを送りたい場合や、同じくAWSのサービスであるAPI Gatewayを使ってステートレスなWebAPIを作成できる(!!)など、既にたくさんの応用例があります!
もしあなたがなにかサーバー上でちょっとしたことをやることになったら、まずはLambda上でできるかを考えてみませんか?
私達の会社では、AWS上の様々サービスを使ってWebアプリケーションをより効率的かつスケーラブルに実装することが今ホットな話題の一つになっています。
エンジニアが職場環境をモダンにした話
以前の職場環境
僕がマッチングッドで働き始めてからちょうど2年経ちました。
入った直後に使われていたツールや開発環境は以下のような感じでした。
最初はWebについての知識はほとんど持っていなかったので、こんなものかなと思っていたのですが、自分で情報を集めるようになるうちにこの環境はもう時代遅れなのだということがわかるようになりました。
会社の環境を大きく変える転機
入社してから1年が経つ頃に、新規のシステムを自分たちで作るチャンスがやってきました。これは現在稼働しているWorkinGoodというサービスです。
これは一から作るなら全て最新のものを使って作ろうと決めて作り始めたので、開発しているチームでは技術だけでなくツールやフローも含めて最新を目指した結果以下のようになりました。
先ほどと同じ会社とは思えないほどの変化です。
SkypeからSlackへの変化は単にサービスの使い勝手がよくなっただけでなく、ボットと連携することができるのでいくつかのオペレーションをボットから行うことができるようになったり、天気を教えてくれたり、チャットツールに止まらない可能性を秘めていることを実感しました。
開発環境で最も変わったのはインフラ周りだと思います。今まで専用のスクリプトでパッケージ化して配布していたリリースフローを、CIとDockerとAWSによってデプロイを自動化したことにより少人数でも安全にオペレーションできるようになりました。
会社全体に広めた
この開発チームでの環境がとても良かったので、会社全体に広げることにしました。
コミュニケーションの変化
今までSkypeとメールでしていたやり取りは全てSlackで行うようにしたことでコミュニケーションが円滑になっただけではなく、チャンネル機能を駆使して今まで以上に社員の親密度が上がった気がします。
フローの変化
また、今まで明確な開発フローがなく、masterブランチに直pushしていたのをGitHub Workflowをベースにした独自のフローを定めることにより、安全にコードを管理、開発できるようになりました。
共有の変化
最も大きな変化は、SkypeからSlackへ移ったことと、エンジニアのタスク管理をAsanaからGitHubのissue に移したことで他の社員が今何をやっているのかがわかるようになったことです。個人的にこれが最大のポジティブな影響を与えていると思います。
他の人が今何をやっているのかがわかると、プロジェクト全体の見通しをつけることができるだけではなく、自分自身のモチベーションに繋がります。他の人がすごいことをやっていれば、自分も負けじと頑張れる人にはとてもいい環境です。
まとめ
ツールや環境が新しくなることで生産性の向上だけでなく雰囲気までよくなった(当社比)気がします。
まず身内で使ってみて、それが良いものであるとわかったなら是非上司を説得して広めてみてください。エンジニアにとって開発環境とはカメラマンにとってのカメラであり、画家にとっての筆と絵の具です。
そこを妥協してしまったらいいものができるはずがありません。会社を次のステップへ行かせようと思ったらモノを作っているエンジニアが職場を変えるべきだと思います。