1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Dev Containersのビルド時間を短縮し,サービスの再起動を可能にした

Last updated at Posted at 2025-01-09

これは何?

VSCodeのDev ContainersというDockerコンテナ内でVSCodeを開く機能を使っていて

  • ビルド時間が長いこと
  • サービスの再起動をするのに再ビルドが必要

の2つの問題を改善したら開発体験がよくなったので同じようなところでストレスを感じている人の助けになればと


対象リポジトリ

去年のアドベントカレンダーでも何回か登場したOpenRestyのリバースプロキシを対象にした。

変更前の状態

変更前のリポジトリ

devcontainer.json
{
  "name": "dev_container", // 任意
  "dockerComposeFile": [
    "../compose.yaml",
    "compose.yaml"
  ],
  "service": "reverse_proxy_app", // compose.yamlのサービス名
  "workspaceFolder": "/usr/local/openresty",
  // Dev Container起動時に開発ツールをインストール
  "postCreateCommand": "./.devcontainer/install-pkg.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "DavidAnson.vscode-markdownlint",
        "exiasr.hadolint",
        "oderwat.indent-rainbow",
        "ionutvmi.path-autocomplete",
        "sumneko.lua",
        "trixnz.vscode-lua",
        "gccfeli.vscode-lua"
      ]
    }
  }
}
compose.yaml
# Dev Containerではこちらが優先される。
services:
  reverse_proxy_app:
    build:
      target: devcontainer
      context: ./reverse_proxy
      dockerfile: Dockerfile
    image: reverse_proxy_devcontainer:latest
    container_name: reverse_proxy-devcontainer-container
    volumes:
      - ./reverse_proxy/:/usr/local/openresty/reverse_proxy
      # NOTE: install-pkg.shをコンテナに追加する
      - ./.devcontainer:/usr/local/openresty/.devcontainer 

  redis_app:
    build:
      context: ./redis
      dockerfile: Dockerfile
    image: redis-img:latest
    container_name: redis_container
    ports:
      - 6379:6379 # localport:dockerport
  # NOTE: redis_appに初期データを投入するために一時的なコンテナを立ち上げている。
  redis_client:
    image: redis:latest
    volumes:
      - ./redis/initial_data_redis.sh:/tmp/initial_data_redis.sh
    command: >
      /bin/bash -c 'source /tmp/initial_data_redis.sh'
    depends_on:
      - redis_app

"postCreateCommand": "./.devcontainer/install-pkg.sh",の部分で必要なライブラリをインストールしている。

install-pkg.sh
#!/bin/bash
package_list="net-tools \
  curl \
  wget \
  rsync \
  unzip \
  zip \
  vim \
  jq \
  less \
  git \
  ca-certificates \
  iputils-ping \
  dnsutils \
  iproute2 \
  tcpdump \
  procps
"
apt-get update -y
apt-get install -y --no-install-recommends ${package_list[@]}
rm -rf /var/lib/lists

# hadolint
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.10.0/hadolint-Linux-x86_64
chmod 755 /usr/local/bin/hadolint

# redis-cli
REDIS_VERSION=7.4.1
# NOTE: `./redis-cli -h redis_app -p 6379`で接続可能
wget https://github.com/redis/redis/archive/refs/tags/${REDIS_VERSION}.tar.gz
tar xzvf ${REDIS_VERSION}.tar.gz
cd redis-${REDIS_VERSION}
make
cp src/redis-cli /usr/local/bin
cd ..
rm ${REDIS_VERSION}.tar.gz
rm -rf redis-${REDIS_VERSION}

個人的に気になっていた部分は以下。

  • Dev Containerでビルドすると通常のDockerビルドよりもかなり時間がかかる
  • nginxの再起動をするためには再ビルドが必要: コンテナ永続化プロセスである,nginxを止めることになるのでコンテナが落ちてしまうため

ビルド速度の改善のためVolumeマウントをキャッシュ代わりに使う

このinstall-pkg.shの中でredisのビルドを行って,redis-cliだけ抽出してパスを通すみたいなことをやっていたため,(redis-cliを単体でinstallする方法が多分ないので謎のこだわりを発揮していた)のでそこそこビルド時間がかかっていた。

Dev Container内でのみ実行するスクリプトには(多分)Dockerのレイヤキャッシュが効かないはず
TODO: 効かせる方法がないか今度調べてみる。

そこで,初回のビルド時に作成したredis-cliをvolumeマウント使ってみた。

自分はDev Container用のcompose.yamlを作っており,.devcontainer/cacheをコンテナの/cacheにvolumeマントする設定を追記した。

compose.yaml
 # NOTE: ./install-pkg.shでインストールしたパッケージをキャッシュするためのディレクトリ
      - ./.devcontainer/cache:/cache 

install-pkg.sh変更部分
# redis-cli
setup_redis_cli() {
  # 2回目以降のbuild時にはvolumeマウントしている/cacheをつかう
  cp /cache/redis-cli /usr/local/bin/redis-cli
  if command -v redis-cli > /dev/null 2>&1; then
    return 0
  fi
  REDIS_VERSION=7.4.1
  # NOTE: `./redis-cli -h redis_app -p 6379`で接続可能
  wget https://github.com/redis/redis/archive/refs/tags/${REDIS_VERSION}.tar.gz
  tar xzvf ${REDIS_VERSION}.tar.gz
  cd redis-${REDIS_VERSION}
  make
  cp src/redis-cli /usr/local/bin
  cp src/redis-cli /cache # NOTE: ビルド時間短縮のために/tmpをvolumeマウントしてキャッシュする
  cd ..
  rm ${REDIS_VERSION}.tar.gz
  rm -rf redis-${REDIS_VERSION}
}
setup_redis_cli

この変更により,初回ビルド以外にはredisnビルドが走らなくなった。万歳!


そもそもビルドする回数を減らす: コンテナ永続化プロセスをnginxにするのをやめる

コンテナは起動しつづけるためには永続的なプロセスが必要である。
DockerfileのCMDとかENTRYPOINTとかに書いてあるあれ

自分はCLIツールを作る時のような永続化プロセスを持たないコンテナでDevContainerを使う時のみ,devcontainer.jsonに"overrideCommand": trueを使ってコンテナを永続化していた。
Dockerfileがnginxのような永続化プロセスを持っている際にはわざわざこの機能を使う意味はないなと思っていたのだが,以下の記事を見て,永続化プロセスを持っているDockerifileを扱う場合でも"overrideCommand": trueを使うべきだと知った。

  • "overrideCommand": trueでコンテナを永続化させる。
  • 変わりにnginxはdevcontainer.jsonの中で"postStartCommand": "openresty",として起動させる

これを実施したことにより,nginxを再起動してもコンテナが止まることがなくなった。
そのため,実装を変更するたびにDev Containerを再ビルドする必要がなくなった。万歳!


まとめ

  • Dev Container内でのみ実行するスクリプトには(多分)Dockerのレイヤキャッシュが効かないので,
    • なるべく処理を減らす
    • 重い処理はなるべく再実行しなくて済む方法を考える。
  • コンテナ永続化プロセスはDev Containerの機能でOverrideしてやってもらう。変わりにサービスは通常起動することで止めてもコンテナが止まらなくなる。
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?