エンジニアリング

GitLabの導入(Backlog + GitHub からの移行)


いま、社内に GitLab を導入中です。数ヶ月かけて、これまで利用していた Backlog と GitHub を、GitLab に置きかえていく予定です。

Backlog は課金額がユーザ数に依存しません。そこで、いろいろな人が関わる課題管理には Backlog を利用しています。過去の履歴を見ると、2013年から 5年以上もお世話になっています。現在のアクティブユーザー数は120名ほど。

一方 Backlog の Gitリポジトリ機能はそれほど魅力的ではないため、コード管理には GitHub を利用しています。しかし GitHub はユーザ毎にお金がかかるため、コードを書く人に限定して使っています。現在のアクティブユーザー数は50名ほど。

開発の流れ

当社の開発の流れは、だいたい次のようになっています。

  1. 誰かが「○○をやりたい」と提案する。誰が何を提案してもいい。要点をまとめて Backlog に proposal(提案)として登録する。
  2. やるか、やらないか、優先度はどうするか、を毎朝のミーティングで決める。
  3. やることになった案件は planning(企画)フェーズに移行し、ディレクター、デザイナー、エンジニアなど案件毎に適切な人が仕様を決める。
  4. planningフェーズの仕様をレビューして問題がなければ、dev フェーズに移行させる。課題の優先度は毎朝の会議で決める。
  5. 優先度高の課題の中から好きなものを、手が空いたエンジニアが自分で取っていく。
  6. 開発が終わったら、提案者やディレクターがテストする。問題がなければリリース待ちとなる。
  7. リリースする。「街コンジャパン」の場合、少ない日で 1件、多い日で 10件くらいのリリースをする。ただし、売上が一番多いのが土日祝であるため、休みの前日のリリースはなるべく避ける。

提案から仕様を作るフェーズまでが Backlog で、開発が GitHub で、開発が終わるとまた Backlog に戻ってくる。これで仕事は充分に回っているのですが、開発の全ライフサイクルにまたがった、全プロダクト横断での進捗状況の見通しが悪い状態でした。

課題

  1. 全プロダクトを横断した状況の把握がしにくい
    Backlog でも FindIssueAllOver という全プロダクト(プロジェクト)横断の検索機能がありますが、おまけ程度の存在で、あまり実用的ではありません。エンジニアやディレクターが豊富にいるわけではないので、プロダクトにかかわらず、少しでも優先度の高い案件を先にやりたいという時の機動性はあまり提供してくれません。

  2. 開発の全ライフサイクルを横断して状況が把握しにくい
    Backlog と GitHub に情報がわかれていて、相互に簡単に行き来できないため、提案、企画、開発、テスト、リリースと、ライフサイクル全体を一望しにくいという欠点がありました。それぞれの開発担当者は、同時に数個程度の案件を頭にいれておけば良いので問題になりませんが、リーダーやマネージャが数十個、数百個単位で管理する場合、ライフサイクル横断性の悪さは非効率につながります。

  3. 工数のトラッキング手法が確立されていない
    これは Backlog や GitHub の問題ではありませんが、GitLab の工数トラッキング機能は魅力的に見えました。毎月月初に、経理に工数を報告しているのですが、過去3年近くにわたって、Excelファイルを各メンバーが作成して提出しています。正確性に欠ける上に、開発者にはあまり評判がよくありません。

  4. GitHub はユーザ数とコストが比例する
    たいした出費ではないものの、ユーザ数とコストが比例するので、本当に必要なユーザのみに限定しがちになります。

導入

GitLab の EC2 へのインストール

インストール先は AWS にしました。インフラチームが用意してくれました。当社のインフラチームは、AWS が大好きです。というか、基本的に特に理由がなければ AWS を使うようにしています。

GitHubリポジトリのインポート

影響の少なそうなリポジトリから、手作業で一つずつ、インポートしていきました。現時点で 82リポジトリなので、なんとかなります。Gitのリポジトリも Issue も、全自動でインポートしてくれます。

インポート時に GitLab側にユーザが存在すると、GitHub のユーザと GitLab のユーザを紐付けてくれます。そこで、50ユーザほどを手作業で作りました。これで移行した後も快適に使えるはずです。しかしここにワナが潜んでいました。

Google Account で OAuth

当社はメールやスケジュールは G Suite を利用しているので、Googleアカウントでログインできるものは、可能な限り Googleアカウントにしています。GitLab も、当然 Googleログインしたくなります。

ところが、OAuth でログインしようとすると、既に登録済みの既存のアカウントと衝突してエラーになります。これは GitLab のバグや機能不足ではなく、「メアドが同じだからといって同じユーザであるとみなしてはいけない」というセキュリティポリシーから来ているようです。

仕方が無いので、PostgreSQL に直接アクセスして、usersテーブルのメアドを一時的に .example.com 付きに変えて待避し、利用者に OAuth でログインしてもらい、identitiesテーブルの user_id を付け替えるという方法で、既存アカウントと Googleアカウントを紐付けました。

Kubernetes で CI

リポジトリが無事に移行できたので、次は CI です。当初は、GitHub で利用していた CircleCI をそのまま使うつもりでした。しかし、CircleCI は GitLab に対応していなかったのでした。

残念に思ったものの、調べてみると GitLab の CI は、かなり強力であることがわかりました。Circle CI よりも良い感じです。しかも Kubernetes(以下 k8s)に対応している。もう、心は決まりました。使ったことのない GKE で動かそう、と。これでソース、課題管理が AWS で CI が GCP になりました。(なお、当社のプロダクトの一つは、一年くらい前から GKE で動いています)

GitLab の CI は、Runner と呼ばれるサーバ上で実行されます。GitLabサーバが、Runnerと通信して、Runner が Dockerコンテナを立ち上げて CI します。この Runner は、ローカルの PC を登録してもいいし、EC2インスタンスを登録してもいいし、k8sクラスタを登録してもいい。k8sで動かすのが、一番難易度が高くて格好良さそうです。

しかし k8s上の Runner には、バグや制約がいくつかあったのです。そしてそれはドキュメントのどこにも書かれておらず、GitLab社の issue tracker を漁って初めてわかることでした。

その1. コンテナ間の通信がホスト名でできない

以下のように書いておけば、アプリケーションからは mysql という名前のホスト名で通信できる、というのが GitLab CI の仕様です。

services:
  - mysql:5.7

しかし k8s Runner では、ホスト名に 127.0.0.1 を指定しないと通信できませんでした。
https://gitlab.com/gitlab-org/gitlab-runner/issues/2229

その2. ファイルがキャッシュできない

以下のように書いておけば、ダウンロードしたライブラリがキャッシュされて、ビルドが短縮されるはずでした。

cache:
  paths:
    - vendor/bundle
    - node_modules

しかし k8s runner では動的にノード数が変わるため、キャッシュがサポートされていません。代わりに、S3 にファイルを待避するなどの仕掛けを自分で組み込む必要があります。

今回は、Dockerイメージに gem もインストールして container registry に置いておくことで、とりあえず回避しました。gem のバージョンアップ毎に少しずつ CI が遅くなりますが、数ヶ月は耐えることでしょう。。。

CI の時間

実は GitLab CI に移行してから、CI の時間が長くなりました。k8sのノードの vCPU数を増やしても、効果は無いようでした。CI の時間は開発効率に直接の影響を与えるので、ここは何とかしたいところですが、まだ試行錯誤中です。Circle CI って、優秀だったのですね。

GitHub + Circle CI: 8分
GitLab + k8s: 12分
GitLab + k8s with CircleCI MySQL image: 10分

創業7年を経過したリンクバルは、毎期増収増益中です。リンクバルでは、会社と共に成長していきたいエンジニアを大募集中です。あなたも一緒に、リンクバルの DevOps環境を進化させてみませんか?

リンクバル 採用情報
https://hrmos.co/pages/linkbal


Adding a Rails Email Delivery Method to Work with SendGrid’s Web API


Hi! I’m Dennis and I’ve been working at Linkbal as a software engineer since October 2017.

In this article, we are going to create a new ActionMailer delivery method that works with SendGrid through its Web API and integrate it to a simple Rails project created with scaffolding. This post assumes you are already have some familiarity with Ruby on Rails.

Introduction

When searching for information about how to integrate Rails projects to SendGrid, we are usually presented with articles that describe how to do it through SMTP. That is the case even in SendGrid’s own Knowledge Center. This is the easiest implementation for sure as it requires very little setup.

But, what if we want to use SendGrid’s Web API instead? Well, there’s no official plug&play option as is the case for other services, such as Postmark. There are unofficial options, such as this gem, but they are not much popular and sometimes we just want a simpler email delivery implementation. In this case, we can create our own implementation to customize ActionMailer to use SendGrid’s Web API.

Preparation

Test Rails Project

Let’s create a minimal Rails project to test our implementation:

$ rails new sendgrid_integration_test
$ cd sendgrid_integration_test

Now, we are going to a very simple contact form through scaffolding:

$ rails generate scaffold Contact name:string email:string message:text
$ rails db:migrate

SendGrid API Key

To use SendGrid’s API, we need to create a new SendGrid account if we don’t have one (there’s a free trial period and, after that, we can send up to 100 emails a day for testing purposes) and create a new API key (it’s good practice to remove API keys when they are not needed anymore). It will be used for authentication and authorization when using the API. In the SendGrid dashboard, go to Settings > API Keys and click the “Create API Key” button.

Setting to manage API keys

Give it any name you want and choose the “Restricted Access” option, so we can customize the access level for the key. For this guide, it should be enough to set “Mail Send” to “Full Access”, as shown below.

Creating a SendGrid API key and customizing its permissions

Confirm and the key will be displayed to you. It is just displayed once for security reasons and the page recommends us to save it somewhere safe.

Environment Variable for the Key

We are now going to set the newly created API key as an environment variable:

$ export SENDGRID_API_KEY='api_key_copied_from_sendgrid_dashboard'

To make this permanent, add the line above to .bash_profile if you are on a Mac or to .bashrc if on Linux.

sendgrid-ruby gem

Before we start creating our new delivery method, let’s add the official Ruby API library (https://github.com/sendgrid/sendgrid-ruby). We are going to use it to communicate with SendGrid’s Web API. Just add the line below to the project’s Gemfile:

gem 'sendgrid-ruby'

And update the dependencies:

$ bundle install

New Delivery Method

The implementation of our new delivery method will be in the lib/send_grid_integration/my_delivery_method.rb file, which contents are shown below:

require 'sendgrid-ruby'

module SendGridIntegration
  class MyDeliveryMethod
    attr_accessor :settings

    # settings is a hash to be added to the configuration of the environment that will use MyDeliveryMethod
    def initialize(settings)
      self.settings = settings
    end

    def deliver!(mail)
      from = SendGrid::Email.new(email: mail.from.first)
      to = SendGrid::Email.new(email: mail.to.first)
      subject = mail.subject
      content = SendGrid::Content.new(type: 'text/plain', value: mail.body.raw_source)
      mail = SendGrid::Mail.new(from, subject, to, content)

      sg = SendGrid::API.new(api_key: settings[:sendgrid_api_key])
      response = sg.client.mail._('send').post(request_body: mail.to_json)

      Rails.logger.info("SendGrid API called. Response code: #{response.status_code}")
    end
  end
end

This was kept as simple as possible as this is not an article on how to use the official Ruby API library. It only supports text emails and does not support cc, bcc or attachments. You can take a look at the sendgrid-ruby repository for details on how to implement the missing features, especially this example file.

Initializing the New Delivery Method

We need to run some intialization code to make our new delivery method available to our application. To do so, add the code below to config/application.rb file.

require 'send_grid_integration/my_delivery_method'
ActionMailer::Base.add_delivery_method :my_send_grid, SendGridIntegration::MyDeliveryMethod

Environment Configuration

Now, we are going to configure our environment to use the new delivery method. Let’s also add a settings hash to illustrate how to pass parameters to our delivery method class. In our case, let’s configure the local environment file config/environments/development.rb by adding the lines below:

config.action_mailer.delivery_method = :my_send_grid
config.action_mailer.my_send_grid_settings = {
  sendgrid_api_key: ENV['SENDGRID_API_KEY']
}

Time to Test

At last, we are going to add a new mailer to test our delivery method. Add the app/mailers/contact_mailer.rb file with the following content:

class ContactMailer < ApplicationMailer
  def thanks(contact)
    mail(to: contact.email,
         subject: 'Thanks for the Contact',
         content_type: 'text/plain',
         body: "#{contact.name}, thanks for taking the time to contact us!")
  end
end

Using the Mailer

At last, we are going to open the app/controllers/contacts_controller.rb file to use our mailer when a new contact happens. Edit the file to add a new line as shown below:

def create
  @contact = Contact.new(contact_params)

  respond_to do |format|
    if @contact.save
      ContactMailer.thanks(@contact).deliver_now

      format.html { redirect_to @contact, notice: 'Contact was successfully created.' }
      format.json { render :show, status: :created, location: @contact }
    else
      format.html { render :new }
      format.json { render json: @contact.errors, status: :unprocessable_entity }
    end
  end
end

Run the Application

Let’s run our application:

$ rails server

And access http://localhost:3000/contacts to create a new contact. Fill the form and make sure to provide a valid email address you can access. You should receive a thanks email if the contact is saved without errors. You can monitor the emails sent by your SendGrid account by accessing the “Activity” menu in the service dashboard.

Activity menu in SendGrid dashboard


AWS DeepLensを購入しました〜セットアップ編〜


リンクバルでエンジニアをしております磯谷です。

何か新たなことをするべく、先日AWSDeepLensを購入しました。DeepLensに関する日本語の記事はまだまだ少ないということもあり、今回から数回に分けてDeepLensについての記事を書こうと思っております。

手始めに今回はセットアップ編です。
続きを読む


SIGNATEさんのData Science Competitionに出題しました


LINKBALでエンジニアをやっています磯谷です。
2018/3/20 ~ 2018/5/21の期間で、Data Science CompetitionでLINKBALのコンペを開催してもらいました。

そして7/5(木)に受賞者の方をお呼びして表彰式を行いました!

今日はそのコンペと表彰式について書かせていただきます。
↑の画像は表彰式で撮った集合写真です。

続きを読む


Postmanツールの機能紹介


エンジニアのクオンです。

今回は、業務でよくお世話になっているPostmanについて紹介させて頂きます。

Postmanとは

Postmanは、Web APIのテストクライアントサービスのひとつです。色々なクライアントの動きをテストできるようスクリプト拡張や、テストスイートシナリオの作成などの機能があります。Postmanで、認証、テスト、ドキュメント作成、バージョン管理などAPIの多くの要素を統合して開発できる環境で、API設計・開発にはとても助かります。

続きを読む


Chrome拡張おすすめ記事から選ぶおすすめ拡張20選

Chromeの拡張

はじめに

どうも、最近まったくの未経験からiOS担当になって日々ヒーヒー言っているキンチキこと水野です。

ネットの海にある数多のChrome拡張おすすめ記事で紹介されている拡張から、実際に僕が使っておすすめの拡張を紹介します。あとはおすすめ記事でおすすめされてないけど、僕がおすすめするものも紹介します。

拡張を全然使っていない人が意外とエンジニアにもいて、もっと楽すればいいのにと思ってます。そして、エンジニアよりPC操作にあまり慣れていない人にこそ拡張はおすすめなので、便利なものはガンガン入れましょう!

続きを読む


Alexa!スキル開発のやり方を教えて!①


4月に新卒入社しました、村上です。

私は入社前にアルバイトとして業務に携わっていたのですが、
そのアルバイト時代に「Alexaスキル」の開発を行いました。

そこで今回はその開発について振り返り、
・Alexaってそもそも何?
・Alexaスキルって何?
・どうやって開発するの?
ということを、初心者向けにご紹介したいと思います!

Alexaとは?

→Amazonが提供する「音声認識サービス」のことです。

続きを読む