はじめに
こんにちは。 suzukirito です。フロントエンドエンジニアですが、最近は Node.js アプリケーションの開発をはじめとして、 docker や CI 環境の整備など幅広く触れていて楽しんでいます。
ビットバンクでは Node.js を全面採用していますが、自作ライブラリの管理方法を現在鋭意刷新中ですので、その一部である「npm パッケージの CI/CD 化」について紹介したいと思います!
CI/CD とは
CI は Continuous Integration の略であり、一般的には継続的にプロダクトが正しい状態であることを検証するために自動テストをリポジトリに含めることを指します。
CD は Continuous Delivery の略であり、継続的にプロダクトを Delivery (アプリケーションにおいてはリリース) することです。
具体的にビットバンクでは Gitlab でソースコードを管理し、 Gitlab のビルトイン機能である GitlabCI を用いて自動テストを実行し、 production へのマージを以ってデプロイされる方式を採用中です。
npm モジュールの CI/CD
上記の CI/CD の手法は主にアプリケーションで用いられがちですが、ビットバンクでは npm モジュールにも CI/CD を取り入れました。
大きな理由としては主に 2 つあります。
- リリース承認 = マージ = リリースを一元化したい
- 手元での publish によるリスクを排除したい
前者はそのまま、承認をもらってからリリースという作業を行うため、手間と時間がかかるという問題があります。
後者の publish を行う人やマシンへの依存することによるリスクとして、ざっくり以下のような問題があると感じます。
- publish を行う人/タイミングにより、 node インタプリタやライブラリのバージョンが異なる恐れがある
- publish を行った際のコマンドや、 node のバージョンが記録されない
- リポジトリと異なるソースコードを publish することが原理的に可能である
- (本人の意図するところであるなしにかかわらず)マルウェアが混入する恐れがある
このことから、ビットバンクではより堅牢でトレーサビリティのある方法として CD を採用しました。
GitlabCI のうれしい機能の紹介
Gitlab を採用していることから GitlabCI を採用した、ということもありますが、GitlabCI はかゆいところに手が届く機能を多く備えています。
なお GitlabCI は GitHub からでも使用できます! CI と docker で消耗している方は試してみてはいかがでしょうか。
docker in docker が手軽に行える
ライブラリによってはテストに外部のイメージが必要になることがあると思います。
MySQL や Redis 等の public image であれば、 CircleCI でも容易に使うことができます。
しかし自社のアプリケーションを起動してテストする場合などは、リポジトリが同一であれ別であれ、起動への依存を極小にするために docker-compose を使うのが良いと考えます。
また、CircleCI や GitlabCI で標準提供されている Service の場合
- port のフォワードが変えられない(ので複数台建てられない)
- 名前解決がローカルともプロダクションとも異なる
などの実装への副作用を含む側面もあります。
そのため、ビットバンクでは MySQL や Redis も docker-compose で詳細に設定を管理しています。
環境変数の権限管理が行える
npm publish が可能な token を全員が見れる箇所に配置するのは内部統制の観点から良くないです。
GitlabCI では protected environment という機能があり、 protected branch の CI でのみ 参照される環境変数が設定できます。
protected branch は権限保持者しかマージできず、かつマージされたときにしか CI が走らないので、 echo
する CI を入れて覗くことはできません。安全ですね。
実際にビットバンクでやっている CI/CD
テストの実行
ユニットテストのほか、 docker-compose を用いてサーバを起動した状態でのクライアントのテストなどを実行します。
多くの場合、ビルドチェック、 Lint や Format 漏れ検出もここのフェーズに含むのではないかと思います。
npm audit の実行
npm6 以降では依存パッケージの脆弱性検査を行える npm audit
というコマンドが追加されました。npm audit
: identify and fix insecure dependencies
Hosting の Gitlab であるため、 greenkeeper に対応していないので、継続的に脆弱性検査を行うために以下の手法を検討しています。
- CI を定期的に実行し、エラーがあった際にアラートを飛ばす
- hothouse 等のオープンソースに gitlab 対応をコントリビュートして使用する
npm パッケージであるため、audit した時のライブラリのバージョンとアプリケーションで使用されるライブラリのバージョンが異なってしまうと脆弱性検知漏れの可能性があります。
そのためパッケージ側では lock ファイルではなく package.json での公開パッケージ指定は ^
を使用せず、バージョン固定しています。
npm prepare の実行
node8(npm5)以降では npm prepublish を廃止して prepare を使用してビルドフローを実行しています。
とはいえ npm prepublish の現状と今後どう変わっていくか によると prepublish / prepublishOnly / prepare は今後変更が入ることがわかっているため、適切なタイミングで prepublish に変更しようと考えています。
npm can publish の実行
azu さんの npm publish できるかを判定するコマンドラインツール: can-npm-publishを使用し、バージョンがインクリメントされているかを確認しています。
これは master branch のみで実行し、 master の CI が通った状態でのみ production にマージできるようにしています。
npm publish の実行
これは protected である production branch のみで実行します。
production branch へのマージは権限保持者のみができるため、内部統制も兼ねています。
サンプルコード
では実際にどういうコードになるのか、というのを紹介します。
.gitlab-ci.yml
ベースイメージは docker:dind (alpine) をベースに指定の Node のバージョンを入れたものを使っています。
image: your.cool.registory/node-6.15.1-alpine-dind/node:6.15.1-alpine-dind
stages:
- test
- publish
npm-test:
stage: test
script:
- dockerd-entrypoint.sh &
- touch ~/.npmrc
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_READ_TOKEN}" >> ~/.npmrc
- printf "cat <<++eos\n`cat test/config/json/aws.json.org`\n++eos\n" | sh > test/config/aws.json
- apk add curl mysql-client redis
- npm install
- npm run ci:test
npm-lint:
stage: test
script:
- touch ~/.npmrc
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_READ_TOKEN}" >> ~/.npmrc
- printf "cat <<++eos\n`cat test/config/json/aws.json.org`\n++eos\n" | sh > test/config/aws.json
- npm install
- npm run ci:lint
npm-prepare:
stage: test
script:
- touch ~/.npmrc
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_READ_TOKEN}" >> ~/.npmrc
- printf "cat <<++eos\n`cat test/config/json/aws.json.org`\n++eos\n" | sh > test/config/aws.json
- npm install
- npm run prepare
npm-audit:
stage: test
script:
- touch ~/.npmrc
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_READ_TOKEN}" >> ~/.npmrc
- npm install -g npm@6
- npm install --package-lock-only
- npm audit
can-publish:
stage: test
script:
- touch ~/.npmrc
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_READ_TOKEN}" >> ~/.npmrc
- npm install -g npm@6
- npm install -g can-npm-publish
- can-npm-publish
only:
- master
publish-production:
stage: publish
script:
- touch ~/.npmrc
# この token は production branch の CI でしか参照されない
- echo "//registry.npmjs.org/:_authtoken=${NPMJS_PUBLISH_TOKEN}" >> ~/.npmrc
- npm install
- npm publish
only:
- production
終わりに
ここまでお読みいただきありがとうございました。私が 3 年ほど愛用している GitlabCI の魅力がみなさんに届けばうれしく思います。
このように、ビットバンクではアプリケーション以下のレイヤでも新しい技術を積極的に導入することで、効率的かつ安全なシステム開発を推進しています。
ビットバンクでは Node.js を好きな人のほかにも、このように開発環境を加速したいエンジニアも募集しています。
ご興味を持たれましたら、まずはぜひお話を聞きに来てください!