目次
- Dockerfileを書ける
- コンテナの中の世界と外の世界を区別できる
- Dockerによる開発環境周りでトラブルがあった時に迅速に復旧できる
- チームのローカル開発環境を整備できる
- CI/CDパイプラインを整備できる
対象読者
- Docker学習中の方
- どこまでDockerを学習したらいいか分からない方
1. Dockerfileを書ける
マルチステージビルドで軽量なイメージを作成できる
マルチステージビルドを使用することで、ビルド時に必要な依存関係を最終イメージに含めず、軽量なイメージを作成できます。
例:
# ビルドステージ
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 実行ステージ
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
Dockerキャッシュを意識して高速にビルドできる
Dockerはレイヤーキャッシュを活用します。変更頻度の低いファイルを先にコピーすることで、ビルド時間を短縮できます。
なぜなら、変更がない=再ビルドしなくてよいのでここは以前のキャッシュを有効活用したいためです。逆に開発をゴリゴリと進めていくアプリケーションコードは頻繁に変更が起こるので、最後の方にコンテナに持っていくようにします。
例:
FROM ruby:3.2-alpine
WORKDIR /app
# 変更頻度の低い依存関係ファイルを先にコピー
COPY Gemfile Gemfile.lock ./
RUN bundle install
# アプリケーションコードは最後にコピー
COPY . .
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
2. コンテナの中の世界と外の世界を区別できる
Dockerのネットワークを理解している
docker-composeで繋がれたコンテナ同士ならコンテナ名でアクセス可能だが、コンテナ化していない場合はlocalhostでのアクセスになります。
例:
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
# webコンテナからdbコンテナにアクセス
DATABASE_URL: postgresql://postgres:password@db:5432/myapp
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
volumes:
# コンテナ内のデータをDocker管理領域に永続化するボリュームマウント
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Dockerのコンテナの世界とホストマシンの世界を繋げられる
ボリュームマウント → gemやDBのデータの永続化
注意: これはコンテナ内でのデータ永続化の話で、ホストマシンは直接関係ありません。Docker管理領域に保存されます。
例:
services:
app:
image: ruby:3.2
volumes:
# 名前付きボリュームでgemを永続化
- bundle_cache:/usr/local/bundle
db:
image: postgres:15
volumes:
# DBデータの永続化
- postgres_data:/var/lib/postgresql/data
volumes:
bundle_cache:
postgres_data:
バインドマウント → コンテナ内とホストマシン内のファイルシステムの同期
例:
services:
web:
build: .
volumes:
# ホストのカレントディレクトリをコンテナの/appにマウント
- .:/app
# node_modulesは除外(コンテナ内のものを使用)
- /app/node_modules
ports:
- "3000:3000"
ポートのマッピング
例:
services:
web:
image: nginx:alpine
ports:
# ホストの8080ポートをコンテナの80ポートにマッピング
- "8080:80"
# ホストの8443ポートをコンテナの443ポートにマッピング
- "8443:443"
Dockerのコンテナの外の世界とホストマシンの世界を区別できる
# コンテナ内に入る
docker exec -it container_name /bin/bash
# コンテナ内で実行
root@container:/app# ls
root@container:/app# rails console
# ホストマシンで実行
$ docker ps
$ docker logs container_name
3. Dockerによる開発環境周りでトラブルがあった時に迅速に復旧できる
docker process でプロセスの確認
# 実行中のコンテナを確認
docker ps
# すべてのコンテナを確認(停止中のコンテナも含む)
docker ps -a
# コンテナのリソース使用状況を確認
docker stats
Dockerコンテナ、イメージ、ボリューム削除
# rmはremoveで削除の意味
# コンテナの削除
docker rm container_name
docker rm -f container_name # 強制削除
# イメージの削除
docker rmi image_name
docker image prune # 未使用イメージの一括削除
# ボリュームの削除
docker volume rm volume_name
docker volume prune # 未使用ボリュームの一括削除
# すべてのリソースをクリーンアップ
docker system prune -a --volumes
4. チームのローカル開発環境を整備できる
Docker化すべきかどうかの判断
以下のような事項から判断できます:
- 複数の言語やランタイムバージョンが混在
- データベースやキャッシュサーバーなど複数のミドルウェアが必要
- チームメンバーのOS環境が異なる(Mac/Windows/Linux)
- 新メンバーのオンボーディング時間を短縮したい
- 本番環境との環境差異を最小化したい
Dockerfileの作成
例:
FROM ruby:3.2-alpine
# 必要なパッケージのインストール
RUN apk add --no-cache \
build-base \
postgresql-dev \
nodejs \
yarn \
tzdata
WORKDIR /app
# 依存関係のインストール
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY package.json yarn.lock ./
RUN yarn install
# アプリケーションコードのコピー
COPY . .
# アセットのプリコンパイル(本番環境の場合)
# RUN RAILS_ENV=production bundle exec rails assets:precompile
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
docker-composeの整備
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp_development
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
web:
build:
context: .
dockerfile: Dockerfile
command: bundle exec rails server -b 0.0.0.0
volumes:
- .:/app
- bundle_cache:/usr/local/bundle
- node_modules:/app/node_modules
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://postgres:password@db:5432/myapp_development
REDIS_URL: redis://redis:6379/0
depends_on:
- db
- redis
volumes:
postgres_data:
redis_data:
bundle_cache:
node_modules:
テスト環境の整備
# docker-compose.test.yml
version: '3.8'
services:
db_test:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp_test
tmpfs:
- /var/lib/postgresql/data # テストDBはメモリ上で実行
test:
build:
context: .
dockerfile: Dockerfile
command: bundle exec rspec
volumes:
- .:/app
- bundle_cache:/usr/local/bundle
environment:
RAILS_ENV: test
DATABASE_URL: postgresql://postgres:password@db_test:5432/myapp_test
depends_on:
- db_test
volumes:
bundle_cache:
テストの実行:
# testサービスの実行をする
# db_testサービスに依存しているのでdb_testサービスも起動してくれる
docker-compose -f docker-compose.test.yml run --rm test
5. CI/CDパイプラインを整備できる
GitHub Actionsコンテナ内でのDockerイメージのbuild
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Docker image
uses: docker/build-push-action@v4
with:
context: .
push: false
tags: myapp:test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run tests
run: |
docker run --rm \
--network host \
-e RAILS_ENV=test \
-e DATABASE_URL=postgresql://postgres:password@localhost:5432/myapp_test \
myapp:test \
bundle exec rspec
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
myuser/myapp:latest
myuser/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
まとめ
上記の内容すべてをスラスラ覚えていないにしろ、概念理解とAI使いながら適切に書ける、読めるという感じであれば、Webアプリケーション開発において自信を持っていいのではないかな?と思います。