やりたいこと
nuxt.config.jsで環境変数をセットしたい。
本番環境でも動作するSSRなDocker Imageを作りたい。
結論
nuxt.jsを本番環境用にdocker化するには、.envを使わずに
- CIないし、ローカルマシンなど、docker buildする環境でで、環境変数を全て展開(export)する
- 展開したら
yarn build
を実行する -
.nuxt
をCOPYする形でdocker build
してimageを作る
経緯
手法1: Dockerfileのentrypointを yarn start
にして、docker-compose等で環境変数を渡す
例えば、nuxt.jsでaxiosを使う場合、APIサーバーなどへのリクエストする時にはbaseURLの設定をする必要がある。
baseURLは環境変数を用いて適用したくなることもある。
ここでnuxt.jsを本番環境で公開しようとした時のステップを整理すると
$ yarn build && yarn start
のように、一度buildして、そのbuildしたデータ(.nuxtディレクトリ)を使って、startすることでserveできるようになっている。
nuxt.jsでは、このbuild時に環境変数を全て展開した上で、.nuxtを作成するようになっている。
例: HELLO=world という環境変数がセットされていた時
console.log(process.env.HELLO)
は 具体的な console.log('world')
という環境変数の中身によって置き換わったjsファイルが生成されることになる。
そのため、build時にはすでに環境変数がセットされている必要がある。
逆に言えば、startのタイミングで環境変数を読み込んでいるわけではない、ということである。
ということは、axiosなどのbaseURLを環境変数で指定する場合、buildの時点で環境変数がセットされている必要があることになる。
その前提でいうと、環境変数の読み込みは、yarn start
時では遅いということになる。
なので、entrypointを指定する形のdockerfileで、外から環境変数を与えて起動しても、タイミング的に環境変数は.nuxtに適用されない。
FROM node:10-alpine
WORKDIR /app
ENV NUXT_HOST 0.0.0.0
COPY ./nuxt .
RUN yarn install && yarn build #この時点で環境変数が埋め込まれ済み
EXPOSE 3000
ENTRYPOINT ["yarn"]
CMD ["start"] # この時点では、例えばNUXT_HOSTは文字列の'0.0.0.0'に直接置き換わっている
じゃあnuxtをcopyした時に、.envを読み込ませればいいか?というと、またそうでもない問題が発生する。
あと、.envファイルはimageにあんまり載せたくない気持ちもある。
手法2: dotenvを使って環境変数を読み込む
一旦、doetnvを使って環境変数を読み込んでみた。
すると、なんとbaseURLは空になった。
いろいろ検証してみると、どうやらyarn buildの時に、読み込まれる順番として
- exportされた環境変数が、
process.env
に読み込まれる - nuxt.config.js内での環境変数呼び出し部分に値が入る
- nuxt.config.jsの中のenvオブジェクトに値が入る(
env: { baseURL: process.env.BASE_URL }
などの記述) - dotenvファイルの読み込みが発生して、このタイミングで、
process.env.BASE_URL
などの値がセットされる - clientのjsファイルが、
process.env
の値を具体的な値に置き換えた状態で生成される
という順番になる様子。
つまり、 dotenvはnuxt.config.jsの中の環境変数呼び出しには間に合わない ということになる。(少なくとも再現時はそうでした)
なので、nuxt.config.jsの中で環境変数を展開して欲しいタイミングでは、dotenvはまだ読み込まれていない状態となる。
手法3: .nuxtの生成はCIかローカルマシンで行う
結局この方法をとった、もしかしたら常識だったかもしれないし、もっといい方法があるかもしれないけど、あんまり経験がなかったのでこの方法で実装することにした。
結論と同じだが、環境変数の読み込みはyarn build
するよりも前に実行する必要がある。
そのため、buildする環境では予め環境変数をセットしておいて、その中でbuildを行うことにする。
なので、ローカルマシンでyarn buildまでは実行しておいてその.nuxt込みでDockerfileにCOPYするようにする。
すると、docker buildしてできたdocker imageは、yarn startするだけでproduction環境として実行可能なnuxt.jsサーバーとなる。
それが結論に書いた方法になる。
build環境で環境変数を予めセットしておかないといけないという煩わしさは、ローカルマシンでbuildできる環境が必要ということはあるものの
いったんこの方法に落ち着きましたので、忘備録として残しておきます。
誰かの役に立つと良いなーと思います、もっといい方法があれば是非教えてください!