前回はGAE/GoとFirebaseで認証付きAPIを実装するコードについて書きましたが、今回はこのコードを踏まえてGAE/GoとFirebaseで開発するための環境構築についてまとめてみます。


ローカル開発環境

GAE向けのコードは dev_appserver.py というコマンドを使うことでローカル環境で実行することができます。
デプロイするためには認証が必要ですが、ローカル環境で動かすだけであれば不要です。

また、GAE/Goの環境と合わせるためGoのバージョンは1.11としています。

このあたりの開発環境の設定は各自よしなにでもいいのですが、複数人で開発するときは各自の環境には多かれ少なかれ差異がありますし、なにか変更があった場合なども都度やりとりが発生するのは面倒なので、基本的にはDockerを使って開発環境を統一できるようにしています。

パッケージ管理について

Goのパッケージ管理については当初depを使うものと思っていたのですが、GAEのデプロイ時にハマるという情報があったのと(実際ハマった)過渡期ではあるものModulesというのが使えるようになるとのことで、Modulesを使うことにしました。

GAE/Go向けのDocker環境

必要なのはGo 1.11で最低限必要なコマンド類が実行可能な環境なので、 golang:1.11.6 をベースイメージに、 gcloudgolangci-lint をインストールするだけのDockerfileを用意しました。

# Dockerfile
FROM golang:1.11.6

RUN apt-get update -y && \
    apt-get install lsb-release -y

RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
    echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
    apt-get update -y && \
    apt-get install google-cloud-sdk google-cloud-sdk-app-engine-python google-cloud-sdk-app-engine-go google-cloud-sdk-datastore-emulator -y

RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0

WORKDIR /go/src/YOUR_VCS/YOUR_NAME/YOUR_REPO

/YOUR_VCS/YOUR_NAME/YOUR_REPO は以下ご自身の環境に読み替えてください。

.env に必要な環境変数を記述し、 docker-compose.yml を以下のようにしました。

GO111MODULE は上記のModulesを使えるようにするため、 GOOGLE_APPLICATION_CREDENTIALS はFirebaseの認証情報をJSONから読み込むために設定します。

# .env
GO111MODULE=on
GOOGLE_APPLICATION_CREDENTIALS=/go/src/YOUR_VCS/YOUR_NAME/YOUR_REPO/app/serviceAccountKey.json
# docker-compose.yml
version: '3'

services:
  go:
    build: dockerfiles/go
    env_file:
      - .env
    volumes:
      - .:/go/src/YOUR_VCS/YOUR_NAME/YOUR_REPO
    ports:
      - 8080:8080
      - 8000:8000
    restart: 'no'
    command: make serve

docker-compose.yml でコマンドを make serve としていますが、これは Makefile に以下のように記述しています。

# Makefile
serve:
	dev_appserver.py app.yaml --host=0.0.0.0 --admin_host=0.0.0.0 --support_datastore_emulator=False

この時点でディレクトリ構成は以下のようになっています。 serviceAccountKey.json はFirebaseのコンソールからダウンロードしてあります。( .gitignore には追加済み)

.
├── .env
├── .gitignore
├── Makefile
├── README.md
├── app
│   ├── main.go
│   └── serviceAccountKey.json
├── app.yaml
├── docker-compose.yml
├── dockerfiles
│   └── go
│       └── Dockerfile
├── go.mod
└── go.sum

app.yamlはとりあえず最低限。

# app.yaml
runtime: go111

main: app
handlers:
- url: /.*
  script: auto
  secure: always

以上で docker-compose で開発環境を立ち上げる準備ができました。

$ docker-compose up
# localhost:8080, localhost:8000にアクセスできる

dev_appserver.py はコードの変更を検知してビルドしてくれて、ビルドが走るとModulesも勝手に必要な分を引っ張ってきてくれるので、あとは好きなエディタでコードを書いていけばOK、という環境ができました。
(とはいえエディターでフォーマットなどの支援ツールを使えるようにするには各自でよしなにという感じですが)


GAEへのデプロイ

上記の状態で gcloud コマンドで認証などのセットアップを済ませていれば、デプロイはコマンド一発で簡単にできます。

$ gcloud app deploy app.yaml

お手軽過ぎる。


CircleCIの活用

コードをPushしたときにCircleCIで色々済ませたかったので、とりあえず以下のような設定ファイルを用意しました。

# .circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: golang:1.11.6
        environment:
          GO111MODULE: 'on'
    working_directory: /go/src/YOUR_VCS/YOUR_NAME/YOUR_REPO
    steps:
      - checkout
      - restore_cache:
          key: modules-{{ checksum "go.mod" }}
      - run:
          name: install modules
          command: go mod tidy
      - save_cache:
          key: modules-{{ checksum "go.mod" }}
          paths:
            - /go/pkg/mod
      - run:
          name: lint
          command: |
            curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0
            golangci-lint run --verbose
      - run:
          name: build
          command: go build app/main.go

デフォルト設定の golangci-lint のLinterに引っかからないこと、コードをビルドできることをチェックしているだけなので、あとはここにテストやデプロイを組み込んでいこうと考えています。

Modulesのキャッシュについて

CircleCIではModulesをキャッシュできるようにしていて、 /go/pkg/mod 以下にダウンロードされたModulesを go.mod のchecksumをキーに保存しています。

これについて、Modulesを使うと go.modgo.sum が生成されるので、なんとなく他のパッケージマネージャーの感覚で go.sum が所謂lockファイルなのかなと思いましたが、どうやらそうではないようです。

Wikiによると go.sum はlockファイルではなく、 go.mod のみで再現性のあるビルドが可能だそうなので key: modules-{{ checksum "go.mod" }} としました。


以上でローカルでの開発、デプロイからCIまでの環境が構築できました。

GAEもGoも移り変わりが激しそうですし、情報をキャッチアップしながら少しずつ改良していこうと思っています。