Docker で nmv を使うと docker exec で node コマンドが使えない時の対処法

[Fusic Advent Calendar 2023](https://qiita.com/advent-calendar/2023/fusic) の15日目の記事です。
弊社での数年ぶりのアドベントカレンダーですが、昔はWeb系一色だったのに今ではバリエーション豊か~~カオス~~でとても嬉しいです。

今回はNode14を使う古めの環境をDockerfileで再現しようとしたときのTipsです。

# Setupスクリプトは非推奨になっていた

特定バージョンのnodeをインストールするときに使っていた `https://deb.nodesource.com/setup_X` がいつの間にか非推奨になっていました。

最後の60秒を待つとインストールできるようにパッチでレポジトリを登録してくれますが、あまりお行儀が良くなさそうなのでnvmで入れるようにします。

```bash
curl -fsSL https://deb.nodesource.com/setup_14.x | bash -

================================================================================
================================================================================

                              DEPRECATION WARNING

  Node.js 14.x is no longer actively supported!

  You will not receive security or critical stability updates for this version.

  You should migrate to a supported version of Node.js as soon as possible.
  Use the installation script that corresponds to the version of Node.js you
  wish to install. e.g.

   * https://deb.nodesource.com/setup_16.x — Node.js 16 "Gallium"
   * https://deb.nodesource.com/setup_18.x — Node.js 18 LTS "Hydrogen" (recommended)
   * https://deb.nodesource.com/setup_19.x — Node.js 19 "Nineteen"
   * https://deb.nodesource.com/setup_20.x — Node.js 20 "Iron" (current)

  Please see https://github.com/nodejs/Release for details about which
  version may be appropriate for you.

  The NodeSource Node.js distributions repository contains
  information both about supported versions of Node.js and supported Linux
  distributions. To learn more about usage, see the repository:
    https://github.com/nodesource/distributions

================================================================================
================================================================================

Continuing in 20 seconds ...


================================================================================
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
================================================================================

                           SCRIPT DEPRECATION WARNING


  This script, located at https://deb.nodesource.com/setup_X, used to
  install Node.js is deprecated now and will eventually be made inactive.

  Please visit the NodeSource distributions Github and follow the
  instructions to migrate your repo.
  https://github.com/nodesource/distributions

  The NodeSource Node.js Linux distributions GitHub repository contains
  information about which versions of Node.js and which Linux distributions
  are supported and how to install it.
  https://github.com/nodesource/distributions


                          SCRIPT DEPRECATION WARNING

================================================================================
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
================================================================================

TO AVOID THIS WAIT MIGRATE THE SCRIPT
Continuing in 60 seconds (press Ctrl-C to abort) ...
```

# Docker : Ubuntu に nvm をインストール

```Dockerfile
FROM ubuntu:22.04

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN apt-get update \
    && apt-get install -y git tar curl \
    && apt-get -y autoremove \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ENV NVM_VERSION v0.39.1
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION v14.21.3

RUN mkdir /usr/local/nvm \
    && curl https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default \
    && npm i -g yarn
```

上記の方法でnvm入りのDocker Imageを作成し、コンテナに入るとnode, npm, yarnが使えます。

```bash
> docker build --file ./Dockerfile -t example .
> docker run --name test-container -it --rm example /bin/bash
root@121346301d72:/# node -v
v14.21.3
```

ただ、直接 Docker run の COMMAND で node を実行しようとすると以下のエラーが出ます。

```bash
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "node": executable file not found in $PATH: unknown.
```

node を実行するため、都度 bash でコンテナの中に入るのは面倒です。

# 原因
nvm からインストールした node を使うには、nvmのフォルダに設置された node のコマンドに対する適切なパスが設定されている必要があります。
nvmをインストールすると、 .bashrc にパスを通すためのスクリプトが追加されます。

`docker run --it /bin/bash` でコンテナに入った場合は、bashのプロセスとして起動し .bashrc を読み込むため、そのままCLI上で利用が可能です。

しかし `docker run --it node -v`の場合、直接 node コマンドを呼びに行くため、 .bashrc を通らないため失敗してしまいます。

# 対応
https://gist.github.com/remarkablemark/aacf14c29b3f01d6900d13137b21db3a
gist上で議論がされており、シンボリックリンクのパターンを利用しました。

```Dockerfile
FROM ubuntu:22.04

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN apt-get update \
    && apt-get install -y git tar curl \
    && apt-get -y autoremove \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ENV NVM_VERSION v0.39.1
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION v14.21.3

RUN mkdir /usr/local/nvm \
    && curl https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default \
    && npm i -g yarn \
    && ln -sf $NVM_DIR/versions/node/$NODE_VERSION/bin/node /usr/bin/nodejs \
    && ln -sf $NVM_DIR/versions/node/$NODE_VERSION/bin/node /usr/bin/node \
    && ln -sf $NVM_DIR/versions/node/$NODE_VERSION/bin/npm /usr/bin/npm \
    && ln -sf $NVM_DIR/versions/node/$NODE_VERSION/bin/yarn /usr/bin/yarn
```

清々しいど力技ですねｗ
nvmでインストールしたコマンドを、シンボリックリンクで `/usr/bin` 以下に配置してDocker起動時に実行できるようにしています。

```bash
> ❯ docker run --name test-container -it --rm example node -v
v14.21.3
```

無事、docker runで実行が出来ました。

# おわり
`https://deb.nodesource.com/setup_X` も近いうちに使えなくなりそうなので、小手先ですが自分の中での対応方針が決めれてよかったです。
ニッチな内容ですが誰かの助けになれば幸いです。