LoginSignup
0

More than 5 years have passed since last update.

SPAとGoAppEngineのサービスをCircleCIでデプロイする(サンプル)

Last updated at Posted at 2019-02-11

はじめに

CircleCIでSPA(それ単体でも動くもの)とGoAppEngineをデプロイできるようにしたので設定方法のメモです。

やりたいこと

こんなディレクトリ構成で

myrepo/
├── spa/
└── go-app/

SPAはそれ単体でも動いていて、GoAppEngine側にそのSPAを取り込んで動くようなWebアプリをデプロイしたい。

また、CI上のJobはSPAとGoAppでそれぞれ分けたい。
→JSとGo両方入ったイメージを作りたくないため。(自分でイメージを管理したくない等の理由。。。)

障害となったポイント

  • SPAのJob(ビルド)の結果をGoAppのJobに渡すのに工夫が必要。
    • Job間のImageが異なるので、キャッシュの復元時にPermission問題とかが出てくる。
  • AppEngineにデプロイできて、Goも使えるイメージの用意が大変。
    • これはやや手抜きですが今回メルカリのイメージを使って解決しました。

設定方法

ディレクトリ構成

ほぼ上の再掲ですが、こんな感じです。

myrepo/
├── .circleci/
├── spa/
│   └── public/
└── go-app/
    └── public/
  • .circleci/にはご存知の通りCircleCIのconfig.ymlが入ります。
  • spa/には、Javascriptで作られたアプリが入っています。
    • npm scriptでビルド等ができるように準備されてるものとします。
    • 成果物はpublic/に生成されます。 1
  • go-app/には、GoAppEngineのアプリが入っています。
    • その下のpublic/に、SPAのビルド成果物を持ってくるようにします。

config.yml

早速ですがymlファイルです。ポイントとなる箇所にコメントを入れています。

version: 2.1

jobs:
  build:
    # ポイント(1)
    working_directory: /tmp/myapp/spa
    docker:
      - image: circleci/node:10
    steps:
      - checkout:
          # ポイント(1)
          path: /tmp/myapp
      - run:
          name: Update npm
          command: "sudo npm install -g npm@latest"
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: Install npm wee
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
      - run:
          name: Build SPA
          command: npm run build
      # ポイント(2)
      - save_cache:
          key: assets-cache-{{ .Environment.CIRCLE_SHA1 }}
          paths:
            - /tmp/myapp/spa/public

  deploy:
    working_directory: /go/src/github.com/myrepo/myapp/go-app
    docker:
      - image: mercari/appengine-go:1.11
    steps:
      - checkout:
          path: /go/src/github.com/myrepo/myapp
      - run:
          name: Install dep
          command: go get -u github.com/golang/dep/cmd/dep
      - restore_cache:
          key: dependency-cache-{{ checksum "Gopkg.lock" }}
      - run:
          name: Install go deps
          command: make setup
      - save_cache:
          key: dependency-cache-{{ checksum "Gopkg.lock" }}
          paths:
            - vendor
      # ポイント(3)
      - restore_cache:
          key: assets-cache-{{ .Environment.CIRCLE_SHA1 }}
      - run:
          name: Get spa assets
          command: mv /tmp/myapp/spa/public ./public
      # ポイント(4)
      - run:
          name: Set gcloud credential
          command: |
            echo $GCLOUD_SERVICE_KEY | base64 --decode | gcloud auth activate-service-account --key-file=-
            gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
            gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE}
      - run:
          name: Deploy to GAE
          command: make deploy-ci

workflows:
  version: 2
  build-deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - master

ポイント(1) 複数のジョブ間でキャッシュを復元できるようにSPA側は/tmpで作業する

これは上の方でも書きましたが、Permission問題などへの解決策です。
save_cacheでビルドの成果物を複数のJobで取り回したいとき、
例えば

  • /home ディレクトリ以下でcacheをしてみた場合
    • 複数Jobでユーザーが異なると、cacheを復元できない場合があります。
  • / (root) ディレクトリ以下でcacheをしてみた場合
    • root権限を持たないユーザーで動くImageの場合、cacheを置けない/復元できない場合があります。

今回のconfig.ymlだとnode.jsのImageはcircleciユーザーで動いていて、appengine-goのimageはrootユーザーで動いてるので、後者に当たります。(/goの下とかにsave_cacheできれば最高なんだけど、circleciユーザー権限なんでそれができない)

このことについてはこちらにも記載があります。
本当はジョブ間で共通のユーザー(またはイメージ)で動くようになってればこんなハマらないのですが
今回は自前でイメージを作りたくなかったのでこうしました。

ポイント(2) SPAの成果物をsave_cacheする

後にGoApp側でSPAを取ってこれるようにsave_cacheしています。
成果物はcommit単位で異なると想定して .Environment.CIRCLE_SHA1 でキャッシュしています。

ポイント(3) GoApp側Jobでrestore_cacheでSPAの成果物を取得

上記の通りsave_cacheした成果物を復元しています。
そのすぐ下のrunで、GoApp側の欲しい場所に成果物publicをコピーしています。(ちょっとダサい・・・。)

ポイント(4) AppEngineのデプロイ用にCredentialを設定

CircleCIのドキュメントに書いてる通りですが、ここでGoogleCloudのCredentialを設定しています。

環境変数GCLOUD_SERVICE_KEYには、base64エンコードしたGoogleCloudサービスアカウントのキーファイル内容を設定しておきます。
以下のようなコマンドで生成できるはず。

base64 my-service-account-key.json

AppEngineにデプロイするため、サービスアカウントには少なくとも以下のような権限が必要です。

  • App Engine デプロイ担当者
  • App Engine サービス管理者
  • Cloud Build 編集者
  • ストレージのオブジェクト管理者

肝心のデプロイ部分はmake deploy-ciで誤魔化してますが、中身はgcloud app deploy --quietするだけです。

ポイントとしては以上です。

終わりに

今回のconfig.ymlではTestやLint等が抜けてるので、ちゃんとした物を作る場合は入れておきたいです。
SPAのtest等は、今のbuildの中でそのままやれば良いかなと思います。
Goのtest等は、ひとつjobを増やして今のdeployとは分けたいかな・・・。綺麗なやり方は模索中です

また、そんな風にjobが増えたりでキャッシュのsave/restoreがなんども行われる場合は、CircleCI2.0のcommandsを使うとDRYに書けるのでオススメです。今回は、そんなに複雑にならなかったので使いませんでした

以上、半分は自分向けの備忘録ですが、誰かの参考になれば幸いです。


  1. ちなみに、今回は全く内容に出てきませんが自分はGatsbyJSで作った成果物を利用しています 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0