DevOps

Docker と仮想マシンの違い:コンテナが標準になった理由と、最初に覚えるコマンド

「自分のマシンでは動くのに本番で動かない」を潰したのが Docker です。アプリと依存関係をまとめて固め、どこでも同じ状態で起動できるようにした。それだけのことですが、影響は大きなものでした。

どこから来たのか

Docker Neon Logo

もとは dotCloud という PaaS スタートアップの社内ツールでした。LXC (Linux Containers) のラッパーとして作られたもので、2013年の PyCon US で Solomon Hykes がデモしたのが最初の公開です。オープンソースとして出た後の広がり方は、社内ツールの想定を完全に超えていました。

なぜ VM ではなくこちらが残ったか

当時の標準は VMware や VirtualBox のような仮想マシンでした。違いは単純です。

  • VM: ゲスト OS を丸ごと積む。サイズは GB 単位、起動に分単位。
  • コンテナ: ホストのカーネルを共有する。サイズは MB 単位、起動はミリ秒。

コンテナ技術自体は Docker の発明ではありません。LXC も cgroups も先にありました。Docker がやったのは、それを Dockerfile という一枚のファイルと数個のコマンドに落とし込んだことです。使える人の数が一気に変わりました。

何を入れるか

Mac や Windows で動かすにはランタイムが要ります。

  • Docker Desktop: 公式。機能は一通り揃っていますが、常駐時のリソース消費は軽くありません。
  • OrbStack: Mac 向け。起動が速くメモリも控えめで、Docker Desktop からの移行もほぼそのまま通ります。Mac ならこちらを試す価値があります。

最初に覚えるコマンド

動作確認

docker run hello-world

ローカルにイメージがなければ Docker Hub から取得し、実行してメッセージを出して終了します。数秒で終わります。

Web サーバーを起動する

docker run -d -p 8080:80 --name my-nginx nginx

-d はバックグラウンド実行、-p 8080:80 は手元の 8080 番をコンテナの 80 番に繋ぐ指定です。http://localhost:8080 で Nginx の初期ページが出ます。ホストに Nginx は入っていません。

状態を見る、中に入る

docker ps docker exec -it my-nginx bash

docker ps は起動中のものだけを出します。落ちたコンテナを探すときは -a を付けてください。デバッグでは、なぜ落ちたのかを docker logs my-nginx で見るほうが早く済みます。

片付ける

docker stop my-nginx docker rm my-nginx

Dockerfile

手打ちのコマンドは再現できません。イメージの作り方はファイルに書きます。

FROM node:20-alpine WORKDIR /app COPY package.json . RUN npm install COPY . . CMD ["npm", "start"]

FROM は土台のイメージです。alpine は Alpine Linux ベースの軽量版で、イメージサイズがかなり小さくなります。

WORKDIR はこれ以降の作業ディレクトリです。存在しなければ作られます。

COPY package.json .COPY . . が二回に分かれているのが、この Dockerfile で一番重要なところです。Docker は行単位でキャッシュを持ち、ある行の入力が変わるとその行以降を全部やり直します。ソースコードを先に全部コピーしてしまうと、コードを一文字直しただけで npm install からやり直しになります。変更頻度の低いものを先に置く、というのがこの並びの理由です。

CMD は起動時に実行されるコマンドで、これが終了するとコンテナも終了します。

ビルドします。

docker build -t my-app .

複数のコンテナをまとめる

実際のアプリには DB もキャッシュも要ります。docker run を並べて管理するのは現実的ではないので、Compose に書きます。

# compose.yaml services: web: build: . ports: - "3000:3000" db: image: postgres environment: POSTGRES_PASSWORD: password

docker compose up で全部立ち上がります。サービス名がそのままホスト名になるので、web からは db という名前で Postgres に接続できます。

なお、上の例はローカル用です。パスワードを直接書いているので、そのまま本番に持っていってはいけません。