🐳

DockerでBunを使ってサーバーを立ててみた

2024/09/13に公開

はじめに

この記事では、JavaScriptランタイムであるBunをDockerコンテナ内で使う方法を紹介します。
その際、DockerからBunでサーバーを立ち上げ、ホストPCからアクセスするというのをやってみました。

Bunのホームページはこちら。

https://bun.sh

環境

  • Docker 25.0.3
  • VSCode 1.93.0
  • Mac M1

やり方

やり方はいくつかあると思いますが、この記事では以下の方法を使います。

  • Docker Composeを使用
  • 公式から配布されているイメージを使用

また、execでコンテナに入れるようにします。

ざっくりとした手順はこちらです。

  1. Dockerfileにベースイメージを書く
  2. Dockerfileにコンテナが終了しないためのコマンドを書く
  3. compose.yamlに設定を書く
  4. コマンドでコンテナを立ち上げる

Dockerfileを作成する

まずはプロジェクトルートに移動して、Dockerfileを作成します。

ベースイメージを設定する

Bunの公式Dockerイメージはこちら。
今回はこのイメージを使ってコンテナを作成します。

https://hub.docker.com/r/oven/bun

Dockerfileに以下を追記します。

Dockerfile
# ベースイメージ
FROM oven/bun:latest

# プロジェクトルートを変更する(任意)
WORKDIR /project

コンテナが終了しないようにする

このイメージだけだと、私の環境ではコンテナが終了してしまいました。
今回はコンテナに入ってbunコマンドを実行したいので、この状態を修正する必要があります。

Dockerfileを以下のように編集してください。

Dockerfile
# ベースイメージ
FROM oven/bun:latest

# プロジェクトルートを変更する(任意)
WORKDIR /project

# コンテナが終了しないようにする
CMD ["bash"]

ちなみにCMD ["bash"]compose.yamltty: trueを組み合わせることによって、コンテナが停止するのを防ぐことができます。

compose.yamlに設定を書く

Docker Composeでコンテナを立ち上げるため、compose.yamlを作成します。
そして内容を以下のように編集します。

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を作成し、以下のように編集してください。

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.yamlttyを削除する
  • Volumeを使っていたところをCOPYに置き換える(任意)
  • start-server.shを作成する(任意)

なお、Gitを使っている場合、この作業を始める前にブランチを分けておくことをお勧めします。
多分そうすれば本番環境用のブランチと開発用のブランチができると思います。多分。

Dockerfileを書き換える

まずはDockerfileを編集していきます。
ここでは常時起動用の行を削除し、代わりにサーバーを起動するためのコマンドを追記します。

Dockerfile
# ベースイメージ
FROM oven/bun:latest

# プロジェクトルートを変更する(任意)
WORKDIR /project

# サーバーを起動する
CMD ["bun", "index.js"]

また、本番でファイルをマウントするならVolumeよりCOPYを使ってコピーするのがおすすめだとChatGPTが言っていたので、こちらも書き換えてみます。

Dockerfile
# ベースイメージ
FROM oven/bun:latest

# プロジェクトルートを変更する(任意)
WORKDIR /project

# ファイルをコピーする
COPY ./ ./

# サーバーを起動する
CMD ["bun", "index.js"]

ちなみにですが、おそらく変更しなくても動きます。

compose.yamlを書き換える

次はcompose.yamlの設定を変更します。
ここではttyvolumeの設定を削除します。

compose.yaml
version: '3'
services:
  app:
    build: .
    ports:
      - 3000:3000

なお、上でDockerfileCOPYを追加していない場合、volumeの削除は必要ありません。


ここまででサーバーを直接起動することができるようになったはずなので、一度試してみます。
コンテナを立ち上げるため、以下のコマンドを入力してください。

ターミナル
docker compose up --build

実行するとコンテナが起動し、その中でサーバーが立ち上がるはずです。
ホストから http://localhost:3000 にアクセスし、Bunというレスポンスが返ってきたら成功です。

なお、--buildというオプションは、使用しているイメージをビルドし直すものになります。
イメージを作り直さないと、Dockerfileに変更を加える前のイメージが使われてしまいます。
それを防ぐために今回はこのオプションをつけています。

サーバーを起動する実行ファイルを作る

サーバーを起動したいときにdockerコマンドをいちいち打ってもいいですが、場合によっては少しわかりにくいかもしれません。
どうせなら一つのファイルにまとめてしまおうと思います。

ということで、start-server.shを作成し、内容を以下のようにします。

start-server.sh
docker compose up
docker compose down

下の行は、コンテナが停止(docker compose upが終了)した際に自動でコンテナを破棄するためのコマンドです。
ここにオプションを付与することで、イメージなどを自動で削除することもできるみたいです。

ファイルが作れたら、試しに実行してみます。
以下のコマンドで実行してみてください。

ターミナル
bash start-server.sh

もしくは、実行権限を付与して./start-server.shとしても実行できます。

Discussion