はじめに
この記事では、JavaScriptランタイムであるBunをDockerコンテナ内で使う方法を紹介します。
その際、Bunでサーバーを立ち上げ、ホストPCからアクセスするというのをやってみました。
筆者はDocker初心者なので、必ずしも記事の内容が正しいわけではありません。
他のサイトや記事も参考にすることをお勧めします。
この記事はZennとのマルチポストです。ご了承ください。
Bunのホームページはこちら。
環境
- Docker 25.0.3
- VSCode 1.93.0
- Mac
やり方
やり方はいくつかあると思いますが、この記事では以下の方法を使います。
- Docker Composeを使用
- 公式から配布されているイメージを使用
また、execでコンテナに入れるようにします。
ざっくりとした手順はこちらです。
-
Dockerfileにベースイメージを書く -
Dockerfileにコンテナが終了しないためのコマンドを書く -
compose.yamlに設定を書く - コマンドでコンテナを立ち上げる
Dockerfileを作成する
まずはプロジェクトルートに移動して、Dockerfileを作成します。
ベースイメージを設定する
Bunの公式Dockerイメージはこちら。
今回はこのイメージを使ってコンテナを作成します。
Dockerfileに以下を追記します。
# ベースイメージ
FROM oven/bun:latest
# プロジェクトルートを変更する(任意)
WORKDIR /project
コンテナが終了しないようにする
このイメージだけだと、私の環境ではコンテナが終了してしまいました。
今回はコンテナに入ってbunコマンドを実行したいので、この状態を修正する必要があります。
Dockerfileを以下のように編集してください。
# ベースイメージ
FROM oven/bun:latest
# プロジェクトルートを変更する(任意)
WORKDIR /project
# コンテナが終了しないようにする
CMD ["bash"]
ちなみにCMD ["bash"]とcompose.yamlのtty: trueを組み合わせることによって、コンテナが停止するのを防ぐことができます。
compose.yamlに設定を書く
Docker Composeでコンテナを立ち上げるため、compose.yamlを作成します。
そして内容を以下のように編集します。
version: '3'
services:
app:
build: .
tty: true
volumes:
- .:/project # パスはWORKDIRの記述と揃えるのがおすすめ
ports:
- 3000:3000
ここではappという名前のサービスを作成しています。
-
build: .: カレントディレクトリにあるDockerfileを使用 -
tty: true: コンテナを終了させないように指定 -
volumes: コンテナとホストでファイルを共有するのに使用 -
ports: ホストからコンテナ内で立てたサーバーにアクセスするため、ポートを共有している
なお最後のportsは、Bunをサーバーの起動以外に使う場合は不要です。
また、サーバーを起動するポートをJavaScript側で変更する場合、こちらの設定を変更する必要があります。
コンテナを起動する
これで準備は済んだので、いよいよコンテナを起動してみます。
コンテナを起動して入るには、以下のコマンドを入力します。
docker compose up -d
docker compose exec app bash
実行すると、プロンプトが変わりDockerコンテナに入ることができるはずです。
bunコマンドが使えることを確認する
このコンテナはBunのイメージを使っているため、bunコマンドが使えるはずです。
試しに以下のコマンドを実行してみてください。
bun --help
ヘルプが出力されたら成功です。
サーバーを起動してみる
では、JavaScriptで書いたサーバーをBunで動かしてみます。
まずはサーバーになるJavaScriptファイルを作成します。
index.jsを作成し、以下のように編集してください。
// サーバーを起動する
Bun.serve({
fetch(req) {
// リクエストが来たらログを出力
console.log('request!')
// Bunというレスポンスを返す
return new Response("Bun");
},
});
Bun.serve関数については、ドキュメントが参考になると思います。
コンテナとホストでファイルを共有(マウント)しているので、ホストでindex.jsを作成すると自動的にコンテナにもindex.jsが作成されます。
次に、このファイルをBunで実行してみます。
以下のコマンドを入力してください。
bun index.js
サーバーが起動できたら、ホストから http://localhost:3000 にアクセスしてみてください。
Bunというレスポンスが返ってきたら成功です。
コンテナをサーバー起動用にする
ところで、こちらで作成したDockerコンテナですが、当然ながらBunのサーバーを終了してもコンテナは終了しません。
開発時はこれで大丈夫ですが、完成した時にこれだとサーバーの軌道に少し手間がかかります。
ということで、次はコマンド一つでコンテナが起動してサーバーが立ち上がり、そのコマンドを終了するとコンテナもサーバーも停止ようにします。
なお、作成したJavaScriptコードががサーバーのように常時稼働するものではない場合、JavaScriptコードはコンテナ内でBunによって実行され、実行が終わるとコンテナも終了します。
やり方
大まかには以下のような手順を踏みます。
-
Dockerfileの常時起動用の行を削除する -
Dockerfileにサーバーを起動するコマンドを書く -
compose.yamlのttyを削除する - Volumeを使っていたところを
COPYに置き換える(任意) -
start-server.shを作成する(任意)
なお、Gitを使っている場合、この作業を始める前にブランチを分けておくことをお勧めします。
多分そうすれば本番環境用のブランチと開発用のブランチができると思います。多分。
Dockerfileを書き換える
まずはDockerfileを編集していきます。
ここでは常時起動用の行を削除し、代わりにサーバーを起動するためのコマンドを追記します。
# ベースイメージ
FROM oven/bun:latest
# プロジェクトルートを変更する(任意)
WORKDIR /project
# サーバーを起動する
CMD ["bun", "index.js"]
index.jsはエントリーポイントとなるJavaScriptファイルのパスです。
将来的にindex.jsをsrcに移動したり、TypeScriptに変えてindex.tsになった場合、このDockerfileも変更する必要があることに注意してください。
また、本番でファイルをマウントするならVolumeよりCOPYを使ってコピーするのがおすすめだとChatGPTが言っていたので、こちらも書き換えてみます。
# ベースイメージ
FROM oven/bun:latest
# プロジェクトルートを変更する(任意)
WORKDIR /project
# ファイルをコピーする
COPY ./ ./
# サーバーを起動する
CMD ["bun", "index.js"]
ちなみにですが、おそらく変更しなくても動きます。
compose.yamlを書き換える
次はcompose.yamlの設定を変更します。
ここではttyとvolumeの設定を削除します。
version: '3'
services:
app:
build: .
ports:
- 3000:3000
なお、上でDockerfileにCOPYを追加していない場合、volumeの削除は必要ありません。
ここまででサーバーを直接起動することができるようになったはずなので、一度試してみます。
コンテナを立ち上げるため、以下のコマンドを入力してください。
docker compose up --build
実行するとコンテナが起動し、その中でサーバーが立ち上がるはずです。
ホストから http://localhost:3000 にアクセスし、Bunというレスポンスが返ってきたら成功です。
なお、--buildというオプションは、使用しているイメージをビルドし直すものになります。
イメージを作り直さないと、Dockerfileに変更を加える前のイメージが使われてしまいます。
それを防ぐために今回はこのオプションをつけています。
サーバーを起動する実行ファイルを作る
サーバーを起動したいときにdockerコマンドをいちいち打ってもいいですが、場合によっては少しわかりにくいかもしれません。
どうせなら一つのファイルにまとめてしまおうと思います。
ということで、start-server.shを作成し、内容を以下のようにします。
docker compose up
docker compose down
下の行は、コンテナが停止(docker compose upが終了)した際に自動でコンテナを破棄するためのコマンドです。
ここにオプションを付与することで、イメージなどを自動で削除することもできるみたいです。
ファイルが作れたら、試しに実行してみます。
以下のコマンドで実行してみてください。
bash start-server.sh
もしくは、実行権限を付与して./start-server.shとしても実行できます。