dockerfileのダイエット

# はじめに
他社案件で、docker環境を構築されてましたが、イメージ容量が大きかったので、ダイエットした際の備忘録となります

# 前提
ruby:3.3.0
rails:7.1.3
css , js: jsbundling-rails + cssbundling-rails 構成

# 作業内容
## 初期状態
```docker
FROM ruby:3.3.0

# 必要パッケージ
RUN apt-get update \
  && apt-get install -y curl

# install node
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - \
  && apt-get install -y nodejs \
  && apt-get install -y ffmpeg \
  && apt-get install -y tzdata

# install yarn
RUN npm install --global yarn

ENV TZ Asia/Tokyo

# コンテナの作業ディレクトリ
WORKDIR /workspaces

# コンテナの作業ディレクトリにコピー
COPY Gemfile /workspaces
COPY Gemfile.lock /workspaces

# 依存関係をインストール
RUN gem update --system \
  && bundle install -j 4

# yarnインストール
COPY package.json /workspaces
RUN yarn install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```shell
$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED      SIZE
workspaces-web   latest    6030e6770b2c   6 days ago   2.32GB
```
初期の状態ですが、容量が2.32GB！


## bullseyeにイメージ変更
```docker
FROM public.ecr.aws/docker/library/ruby:3.3.0-bullseye

# 必要パッケージ
RUN apt-get update \
  && apt-get install -y curl

# install node
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - \
  && apt-get install -y nodejs \
  && apt-get install -y ffmpeg \
  && apt-get install -y tzdata

# install yarn
RUN npm install --global yarn

ENV TZ Asia/Tokyo

# コンテナの作業ディレクトリ
WORKDIR /workspaces

# コンテナの作業ディレクトリにコピー
COPY Gemfile /workspaces
COPY Gemfile.lock /workspaces

# 依存関係をインストール
RUN gem update --system \
  && bundle install -j 4

# yarnインストール
COPY package.json /workspaces
RUN yarn install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```shell
$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED              SIZE
workspaces-web   latest    9f65c361d72a   11 seconds ago       2.13GB
<none>           <none>    6030e6770b2c   About an hour ago    2.32GB
```

約200M削減！

## slimにイメージ変更
```docker
FROM public.ecr.aws/docker/library/ruby:3.3.0-slim

# 必要パッケージ
RUN apt-get update -qq && \
  apt-get install --no-install-recommends -y curl build-essential default-libmysqlclient-dev

# install node
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - \
  && apt-get install -y nodejs \
  && apt-get install -y ffmpeg \
  && apt-get install -y tzdata

# install yarn
RUN npm install --global yarn

ENV TZ Asia/Tokyo

# コンテナの作業ディレクトリ
WORKDIR /workspaces

# コンテナの作業ディレクトリにコピー
COPY Gemfile /workspaces
COPY Gemfile.lock /workspaces

# 依存関係をインストール
RUN gem update --system \
  && bundle install -j 4

# yarnインストール
COPY package.json /workspaces
RUN yarn install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```shell
$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED              SIZE
workspaces-web   latest    351fa94c4348   17 seconds ago       1.89GB
<none>           <none>    9f65c361d72a   About an hour ago    2.13GB
<none>           <none>    6030e6770b2c   About an hour ago    2.32GB
```

さらに約200M削減！

## nodeをコピー(マルチステージビルド)

```docker
FROM public.ecr.aws/docker/library/node:21.7-slim as node
FROM public.ecr.aws/docker/library/ruby:3.3.0-slim

# 必要パッケージ
RUN apt-get update -qq \
  && apt-get install --no-install-recommends -y curl build-essential default-libmysqlclient-dev \
  && rm -rf /var/lib/apt/lists/*

# install node
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
  && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg

ENV TZ Asia/Tokyo

# コンテナの作業ディレクトリ
WORKDIR /workspaces

# コンテナの作業ディレクトリにコピー
COPY Gemfile /workspaces
COPY Gemfile.lock /workspaces

# 依存関係をインストール
RUN gem update --system \
  && bundle install -j 4

# yarnインストール
COPY package.json /workspaces
RUN yarn install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```
$ docker images
REPOSITORY       TAG        IMAGE ID       CREATED             SIZE
workspaces-web   latest     66ae3fe16408   11 seconds ago      1.2GB
<none>           <none>     351fa94c4348   13 minutes ago      1.89GB
<none>           <none>     9f65c361d72a   47 minutes ago      2.13GB
<none>           <none>     6030e6770b2c   About an hour ago   2.32GB
```

約700M削減！

## gemを切り分けコピー(マルチステージビルド)

```docker
FROM public.ecr.aws/docker/library/node:21.7-slim AS node
FROM public.ecr.aws/docker/library/ruby:3.3.0-slim as base

ENV TZ Asia/Tokyo

# 必要パッケージ
RUN apt-get update -qq \
  && apt-get install --no-install-recommends -y curl build-essential default-libmysqlclient-dev \
  && rm -rf /var/lib/apt/lists/*

# コンテナの作業ディレクトリ
WORKDIR /workspaces

FROM base as bundler

COPY Gemfile Gemfile.lock /workspaces

# 依存関係をインストール
RUN gem update --system \
  && bundle install -j 4

FROM base as dev
# install node
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
  && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg

# yarnインストール
COPY package.json /workspaces
RUN yarn install

COPY --from=bundler /usr/local/bundle /usr/local/bundle
COPY . .

# entrypoint
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```
$ docker images
REPOSITORY       TAG        IMAGE ID       CREATED             SIZE
workspaces-web   latest     8da8f4c14f78   2 minutes ago       1.19GB
<none>           <none>     66ae3fe16408   About an hour ago   1.2GB
<none>           <none>     351fa94c4348   About an hour ago   1.89GB
<none>           <none>     9f65c361d72a   About an hour ago   2.13GB
<none>           <none>     6030e6770b2c   About an hour ago   2.32GB
```

約10M削減

## node_moduleを切り分けコピー(マルチステージビルド)
```docker
FROM public.ecr.aws/docker/library/node:21.7-slim AS node
FROM public.ecr.aws/docker/library/ruby:3.3.0-slim as base
ENV TZ Asia/Tokyo
# 必要パッケージ
RUN apt-get update -qq \
  && apt-get install --no-install-recommends -y curl build-essential default-libmysqlclient-dev \
  && rm -rf /var/lib/apt/lists/*
# コンテナの作業ディレクトリ
WORKDIR /workspaces

FROM base as bundler
COPY Gemfile Gemfile.lock /workspaces
RUN gem update --system \
  && bundle install -j 4

FROM base as yarn-installer
# install node
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
  && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
COPY package.json yarn.lock /workspaces
RUN yarn install

FROM base as development
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
  && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
COPY --from=bundler /usr/local/bundle /usr/local/bundle
COPY --from=yarn-installer /workspaces/node_modules node_modules
COPY . .

# entrypoint
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0" "-p" "3000"]
```

```
$ docker images
REPOSITORY       TAG        IMAGE ID       CREATED             SIZE
workspaces-web   latest     3dc2bec63248   8 minutes ago       820MB
<none>           <none>     8da8f4c14f78   About an hour ago   1.19GB
<none>           <none>     66ae3fe16408   About an hour ago   1.2GB
<none>           <none>     351fa94c4348   About an hour ago   1.89GB
<none>           <none>     9f65c361d72a   About an hour ago   2.13GB
<none>           <none>     6030e6770b2c   About an hour ago   2.32GB
```

約300M削減！

# まとめ
| 状態                                              |   容量 |
| :------------------------------------------------ | -----: |
| 初期状態                                          | 2.32GB |
| bullseyeにイメージ変更                            | 2.13GB |
| slimにイメージ変更                                | 1.89GB |
| nodeをコピー(マルチステージビルド)                |  1.2GB |
| gemを切り分けコピー(マルチステージビルド)         | 1.19GB |
| node_moduleを切り分けコピー(マルチステージビルド) |  820MB |

# さいごに
イメージ変更でも結構容量が減ることが分かり、今後は注意が必要と感じました。
また、(当たり前だが)マルチステージビルドだとさらに削れることが再認識できました。
今回の件で見ると、node関係が容量食ってるのが分かったので、ここも注意が必要です。
今回は、初期状態から半分以下に容量削減ができたので、良しとします。
他にもこんな方法もあるよ！という方がいらっしゃれば、ご教授頂けますと幸いです。