Travis CIを使用したGolangビルド

みなさん、Golang書いてますか?

お久しぶりです。メディアシステム開発部の森竹です。 前回はDocker for Macを使ってRuby on Rails開発環境を構築するを紹介させて頂きました。 今回はTravis CIを使用したGolangビルドを紹介させて頂きます。

ビルド

Travis CIでGolangビルドを実行し、AWS S3へPUTします。ビルドサーバーでGolangビルドする案もありましたが、下記の観点を鑑み、Travis CIを選択しました。

Travis CIビルドサーバー
運用コスト月額固定(https://travis-ci.com/plans)サーバー費用など
管理コスト小(Golangバージョンアップは.travis.ymlの変更のみ)大(ビルドサーバー自体を管理する必要があり、Golangバージョンアップはサーバー全体に影響する)
成果物AWS S3ビルドサーバーのストレージなど

.travis.ymlは下記のように定義しています。

.travis.yml

dist: trusty

language: go

go:
  - 1.7.5

env:
  global:
    - TZ=Asia/Tokyo

before_install:
  - make glide  # vendoringツールのGlideをインストールします
  - make awscli # aws-cliをインストールします

install:
  - make deps # glide instalを実行し、パッケージをインストールします

before_script:
  - make fmt      # gofmtを実行します
  - make imports  # goimportsを実行します
  - make lint     # golintを実行します
  - make vet      # go vetを実行します
  - make mysql    # go testで使用するDBのcreate、migrateを実行します
  - make test     # go testを実行します

script:
  - make -f development.mk # Development環境のgo buildを実行します
  - make -f staging.mk     # Staging環境のgo buildを実行します
  - make -f production.mk  # Production環境のgo buildを実行します

addons:
  apt:
    packages:
    - mysql-server-5.6
    - mysql-client-core-5.6
    - mysql-client-5.6
  artifacts:
    s3_region: "ap-northeast-1"
    paths:
      - $(git ls-files -o --exclude-standard | tr "\n" ":")
    debug: true
    target-paths: $TRAVIS_REPO_SLUG/$TRAVIS_BRANCH
    bucket: "xxxxx-golang-artifacts"

AWS S3へのPUTは、Travis CIのaddonsであるtravis-ci/artifactsを使用しています。上記のように定義すると、s3://xxxxx-golang-artifacts/repository_fqdn/repository_path/branch/au_web_portal.production.linux.amd64.gzのような構成で成果物が配置されます。

AWS S3へのPUTにはARTIFACTS_KEYARTIFACTS_SECRETが必要です。こちらはTravis CIのSettingsからEnvironment Variablesに定義すると、.travis.ymlに定義する必要がありませんのでお勧めです。

基本的にコードフォーマット(gofmtgoimports)、静的解析(golintgo vet)、単体テスト(go test)が通らないとGolangビルド(go build)は実行されません。

Travis CIで実行するコマンドなどはMakefileに定義し、.travis.ymlからmakeする方針としました。 Production環境のgo buildを実行するMakefileは下記のように定義しました。

production.mk

.PHONY: build clean

NAME := au_web_portal
ENV := production
GOOS := linux
GOARCH := amd64

HASH := $$(git rev-parse --verify HEAD)
DATE := $$(date '+%Y/%m/%d %H:%M:%S %Z')
GOVERSION = $$(go version)

build: $(NAME).$(ENV).$(GOOS).$(GOARCH).gz

$(NAME).$(ENV).$(GOOS).$(GOARCH):
    GOOS=$(GOOS) GOARCH=$(GOARCH) \
        go build -tags=$(ENV) \
            -o $(NAME) \
            -ldflags "-X main.Hash=$(HASH) \
            -X \"main.Date=$(DATE)\" \
            -X \"main.GoVersion=$(GOVERSION)\"" \
            ./au_web_portal
    mv $(NAME) $@

$(NAME).$(ENV).$(GOOS).$(GOARCH).gz: $(NAME).$(ENV).$(GOOS).$(GOARCH)
      gzip -f $<

clean:
    rm -rf $(NAME).$(ENV).$(GOOS).$(GOARCH).gz
    rm -rf $(NAME).$(ENV).$(GOOS).$(GOARCH)

環境毎に異なる値はBuild Constraintsを使用し、各環境(Production、Staging、Development)のgo buildを実行します。

またldflagsオプションを指定し、Golangバイナリに情報を埋め込みます。開発段階ではGitHubへのpush後すぐ(go buildが実行される前)にデプロイを実行し、変更が反映されていない状況があったりしました。そのような時に、Golangバイナリがどのような状態で動作しているのかを確認するのに便利です。

デプロイ

デプロイはCapistranoを使用しました。AWS S3から各環境に対応したGolangバイナリを取得し、対象サーバーへデプロイします。 下記のようなコマンドを実行します。

$ bundle exec cap branch=master production deploy

おわりに

Travis CIを使用したGolangビルドを紹介させて頂きました。 今回はTravis CIが実行されると各環境(Production、Staging、Development)に対応したGolangビルドが実行されますが、GitHubブランチルールを策定し、各ブランチに対応した環境のGolangビルドを実行するようにしても良いかもしれません。

  • 例)
    • masterブランチの場合、Production環境のGolangビルドを実行する
    • release/99999999ブランチの場合、Staging環境のGolangビルドを実行する
    • feature/xxxxxxxxブランチの場合、Development環境のGolangビルドを実行する

またGolangバイナリのAWS S3へのPUTですが、aws-cliを使用し、下記のように実行しても良かったと思っています。

$ aws s3 cp au_web_portal.production.linux.amd64.gz  s3://xxxxx-golang-artifacts/repository_fqdn/repository_path/branch/

今後はGolangバージョンアップなど、継続的にアップデートや改善をして行きたいと思います。

参考