Circle CIでWebpackのビルドがタイムアウトしたときの対処法(TerserPluginのparalell設定)


はい。
タイトルに答え書いてます!

TL;DR

  • CircleCIでWebpackのビルドプロセスが落ちており原因がCPUかメモリーっぽいとき
  • JSの縮小化処理で使ってるterser-webpack-pluginparalellに並列数を明示的に指定すれば解決する!(かもしれない)
  • ここに書いてる https://webpack.js.org/plugins/terser-webpack-plugin/#parallel

問題

  • Circle CIで実行してたwebpackのビルドが頻繁に落ちるようになった
#!/bin/bash -eo pipefail
yarn build
yarn run v1.22.0
$ APP_STAGE=$ENV_NAME run-s clean copy-assets build:webpack
$ rimraf build
$ cpx "src/assets/**/*" ./build
$ webpack --config webpack/config.base.js --mode production
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade caniuse-lite browserslist`
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`

Too long with no output (exceeded 10m0s): context deadline exceeded

こんな感じで普通に10分のデフォルトタイムアウトで落ちる

  • Circle CIにSSHでログインして手でビルドコマンド叩くと数十秒で正常に終わる
  • topコマンドなどで確かめるとnodeのプロセスはかなり動いてCPUに負荷がかかっており、loadaverageも高まっているので思い処理だというのは分かる。
  • なぜ手動だといけるのか。。。

調査、検証

タイムアウトを伸ばす

  • 言うて動くときは動くのでギリギリ10分超えたのではないか
    • ->えーいとりあえず伸ばしてみよう
    • ->30分まで伸ばす
    • 終わらない

いろんなもののバージョンを上げてみよう

  • 何かのバグにはまってて実は最新版なら解消されてる説
  • 上げたもの
    • webpack 4.29.5 -> 4.42.0
      • 結果: 効果なし
    • node 10系 -> 12系
      • docker imageを更新
      • 結果: 動いた!何回かやっても問題なし!

解決?

  • と思ったら数日後また同じエラーが戻ってきた。。なぜ、、、

Webpackチューニング調査

  • 挙動的にはどう考えてもWebpackの処理の重さが原因だ
  • 巷に溢れてるチューニング施策を探して実施してみよう、、!
  • 色々調べて見た結果。。。

解決策発見!

  • どうやら重いのはJS縮小化の処理くさい

    • 縮小化で使われているterser-webpack-pluginparalellオプションがある。
    • CircleCI上でtop見た時にかなりの数のnodeプロセス動いてたんで、並列で動いていると勝手に勘違いしていたのですが、どうやらそういうわけではない。
  • ・・・というか、もろここに書いてるのですが、CircleCIのような実際のCPU数が提供されないような環境ではparalell数を直指定する必要があるとのこと。

  • paralell: trueではだめです。あくまで数指定。

⚠️ If you use Circle CI or any other environment that doesn't provide real available count of CPUs 
then you need to setup explicitly number of CPUs to avoid `Error: Call retries were exceeded`

というわけでこんな感じの設定をWebpackの設定ファイルにterser-webpack-pluginの設定を追加すれば良いです。
※webpack4以降ではmode=productionでビルドすれば特に縮小化の設定はいらないわけですが、実際terser-webpack-pluginが動いているようでオプション設定したい場合はこのような指定が必要です。

const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: 6 // 6は適当です。環境に応じて変えてください。
      })
    ]
  }
}

プロセッサの数はCircleCI上でこれらのコマンドで調べられるので参考にすればよいですが、かなり多く出ると思うのでWebpackのビルドくらいなら使い切るほど指定する必要も無いとは思います。(規模による)

# CPU ごとのコアの数
grep cpu.cores /proc/cpuinfo | sort -u
# 論理プロセッサーの数
grep processor /proc/cpuinfo | wc -l

まとめ

ぐぐっても全然解決策が引っかからず途方にくれたので、少しでも同じ問題に当たった人の助けになればと思い雑に書き残しました。