shibomb

Dockerで始める開発環境構築入門!「私のPCでだけ動かない」を卒業する実践ガイド

はじめに

こんにちは!プログラミングの世界へようこそ。開発を始めたばかりのころ、「自分のパソコンでは動くのに、他の人のパソコンやサーバーに持っていくと動かない…」なんて経験、ありませんか?これは「環境差異」と呼ばれる、多くの開発者が一度は頭を悩ませる問題です。OSの違い、インストールされているライブラリのバージョンの違いなど、原因は様々です。

この記事では、そんな開発環境の悩みを解決する魔法のようなツール「Docker」について、ゼロから実践的に学んでいきます。Dockerを使えば、アプリケーションとその実行に必要なもの全てを「コンテナ」という箱に詰め込み、どこでも同じように動かすことができます。まるで、引越し先でも家具の配置が全く同じになる魔法の箱のようです。

このチュートリアルを終える頃には、あなたはDockerの基本的な概念を理解し、実際にWebアプリケーションの開発環境を自分で構築できるようになっています。もう「環境」で悩む時間を減らし、もっと創造的なコーディングに集中できるようになりましょう!小さな成功体験を積み重ねながら、一緒に楽しく学んでいきましょう。

前提知識の確認

本格的に学習を始める前に、いくつか準備と心構えについてお話しします。でも、安心してください。完璧に理解している必要はありません。

必要な基礎知識

  • 基本的なコマンドライン操作: ターミナル(WindowsならPowerShellやコマンドプロンプト、Macならターミナル)で、cd(ディレクトリ移動)やls(ファイル一覧表示)などの基本的なコマンドを使ったことがあるとスムーズです。もし自信がなくても、記事中でコマンドを丁寧に解説するので大丈夫です。

事前に理解しておきたい概念

  • 仮想マシンとの違い: 以前は「仮想マシン」という技術で開発環境を揃えることが多かったです。これはOSごとコンピュータを丸ごとシミュレートする方法で、非常に重たいのが難点でした。一方、Dockerの「コンテナ」は、ホストOSのカーネルを共有し、アプリケーション部分だけを隔離するため、非常に軽量で高速に動作します。今は「Dockerは軽くて速いんだな」くらいの理解で十分です。

「分からなくても大丈夫」な部分

  • ネットワークやOSの深い知識: DockerはネットワークやOSの仕組みの上に成り立っていますが、最初から全てを理解する必要はありません。まずは「おまじない」としてコマンドを打ち、動かしてみることから始めましょう。なぜそう動くのかは、使いながら少しずつ理解していけば大丈夫です。
仮想マシンとコンテナをそれぞれ箱で表現し、仮想マシンのほうがはるかに大きく、コンテナは小さく軽量であることを示すイラスト。

環境構築:最初の一歩

それでは、実際にDockerをあなたのパソコンにインストールしていきましょう。ここが最初のハードルですが、乗り越えれば楽しい世界が待っています。

開発環境の準備(初心者向け解説)

Dockerは「Docker Desktop」というアプリケーションをインストールするだけで、必要なツールが全て揃います。Windows、Mac、Linuxのそれぞれに対応したインストーラーが公式サイトで配布されています。

必要なツールとインストール方法

  1. Docker Desktopのダウンロード: 公式サイトにアクセスし、お使いのOSに合った「Docker Desktop」をダウンロードします。

  2. インストール: ダウンロードしたインストーラーを実行し、画面の指示に従ってインストールを進めます。途中でいくつか許可を求められることがありますが、基本的にはデフォルト設定のまま進めて問題ありません。

  3. インストールの確認: インストールが完了したら、ターミナルを開いて以下のコマンドを実行してみてください。

    docker --version

    Docker version 20.10.17, build 100c701 のようにバージョン情報が表示されれば、インストールは成功です!

環境構築でつまずきやすいポイント

  • Windowsの場合: Docker Desktop for Windowsは、WSL 2 (Windows Subsystem for Linux 2) という仕組みを利用します。インストール中にWSL 2の有効化を求められることがあります。画面の指示に従って有効化してください。古いWindows 10では動かないことがあるので、OSは最新の状態にしておきましょう。
  • BIOS/UEFI設定: PCのBIOS/UEFI設定で「仮想化支援機能(Intel VT-xやAMD-V)」が無効になっていると、Dockerが起動しません。もしエラーが出た場合は、PCの起動時に特定のキー(Del、F2など)を押して設定画面に入り、この機能を有効にする必要があります。

ここでつまずいても、焦らないでください。エラーメッセージをよく読み、検索すれば大抵の解決策は見つかります。これもエンジニアとしての大切なスキルアップの一環です。

基本概念の理解

Dockerを使いこなすには、3つの重要なキーワードを理解することが近道です。ここでは、料理に例えて分かりやすく説明します。

核となる考え方

  1. Dockerfile(レシピ): コンテナをどうやって作るかを記述した設計書です。どのOSイメージをベースにするか、どんなソフトウェアをインストールするか、どのファイルを追加するか、といった手順が書かれています。

  2. Dockerイメージ(調理セット): Dockerfile というレシピを元に作られた、アプリケーションを実行するために必要なものが全てパッケージングされたものです。ミールキットのように、必要な材料や下ごしらえが全て済んだ状態を想像してください。このイメージさえあれば、誰でも同じ料理(アプリケーション)を作れます。

レシピ、調理済み材料セット、完成した料理をそれぞれ視覚的に表現したイラスト。
  1. Dockerコンテナ(完成した料理): Dockerイメージ という調理セットを実際に動かした状態です。イメージはただの設計図やテンプレートですが、コンテナは実際に動作しているアプリケーションそのものです。一つのイメージから、複数のコンテナ(料理)を同時に作ることもできます。

身近な例での説明

「カレーライスを作る」というタスクで考えてみましょう。

  • Dockerfile: カレーのレシピ。「玉ねぎを切り、肉を炒め、水とカレールーを入れる」といった手順が書かれています。
  • Dockerイメージ: レシピ通りに下ごしらえされた「カレーの素」のパック。このパックさえあれば、誰でも同じ味のカレーが作れます。
  • Dockerコンテナ: 「カレーの素」を鍋に入れて温め、食べられる状態になった「カレーライス」。実際に私たちが食べるものです。

この関係性を理解することが、Dockerを使いこなす第一歩です。

「なぜそうなるのか」の理解

なぜこのような仕組みが必要なのでしょうか?それは「環境の再現性」を保証するためです。レシピ(Dockerfile)が同じなら、誰が作っても同じ調理セット(イメージ)ができあがり、その結果、同じ料理(コンテナ)が完成します。これにより、「私のPCでだけ動かない」という問題が根本的に解決されるのです。

実践編:手を動かして学ぶ

理論はここまでにして、実際に手を動かしてDockerの世界を体験してみましょう!簡単なWebアプリケーションの環境をステップバイステップで構築していきます。

ステップ1: 基本的な実装

まずは、最もシンプルなWebサーバーであるNginxのコンテナを動かしてみましょう。ターミナルで以下のコマンドを実行してください。

docker run --name my-nginx -p 8080:80 -d nginx
  • docker run: コンテナを起動するコマンドです。
  • --name my-nginx: コンテナに my-nginx という名前をつけます。
  • -p 8080:80: あなたのPCの8080番ポートを、コンテナの80番ポートに接続します。これにより、ブラウザからアクセスできるようになります。
  • -d: コンテナをバックグラウンドで動かします。
  • nginx: nginx という名前の公式イメージを使ってコンテナを作成します。

実行後、ブラウザで http://localhost:8080 にアクセスしてみてください。「Welcome to nginx!」というページが表示されれば成功です!これがあなたの最初のコンテナです。おめでとうございます!

動かしたコンテナを停止・削除するには以下のコマンドを実行します。

docker stop my-nginx
docker rm my-nginx

ステップ2: 機能の拡張

次に、自分で作ったWebアプリケーションをDockerで動かしてみましょう。ここでは簡単なNode.jsアプリを使います。まず、作業用のディレクトリ(例: docker-practice)を作成し、その中に以下の3つのファイルを作成してください。

app.js

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello from Node.js in Docker!');
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

package.json

{
  "name": "docker-node-app",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

そして、このアプリを動かすための「レシピ」である Dockerfile を作成します。

Dockerfile

# ベースとなるNode.jsのバージョンを指定
FROM node:18-alpine

# コンテナ内での作業ディレクトリを設定
WORKDIR /usr/src/app

# 依存関係のファイルをコピー
COPY package*.json ./

# 依存関係をインストール
RUN npm install

# アプリケーションのソースコードをコピー
COPY . .

# コンテナが公開するポートを指定
EXPOSE 3000

# コンテナ起動時に実行するコマンド
CMD ["npm", "start"]

ファイルが準備できたら、ターミナルで以下のコマンドを実行して、イメージをビルド(調理セットを作成)します。

docker build -t my-node-app .

ビルドが完了したら、そのイメージからコンテナを起動します。

docker run --name my-app-container -p 3000:3000 -d my-node-app

ブラウザで http://localhost:3000 にアクセスし、「Hello from Node.js in Docker!」と表示されれば成功です!

ステップ3: 実用的な応用

実際の開発では、Webサーバー、アプリケーション、データベースなど複数のコンテナを連携させることがほとんどです。これらを一つずつコマンドで管理するのは大変です。そこで登場するのが docker-compose です。

docker-compose は、複数のコンテナ構成を docker-compose.yml というファイルに定義し、コマンド一発で全てを起動・停止できるようにするツールです。先ほどのNode.jsアプリに、データベース(PostgreSQL)を追加してみましょう。

作業ディレクトリに docker-compose.yml という名前でファイルを作成します。

version: '3.8'

services:
  # Node.jsアプリケーションサービス
  app:
    # カレントディレクトリのDockerfileをビルドして使用
    build: .
    ports:
      - "3000:3000"
    volumes:
      # ホストのソースコードをコンテナにマウント(コード変更が即時反映される)
      - .:/usr/src/app
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydatabase
    depends_on:
      - db

  # PostgreSQLデータベースサービス
  db:
    image: postgres:14-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydatabase
    volumes:
      # データを永続化するためのボリューム
      - postgres-data:/var/lib/postgresql/data

# データの永続化に使用する名前付きボリュームを定義
volumes:
  postgres-data:

このファイルがあるディレクトリで、以下のコマンドを実行します。

docker-compose up --build -d

これだけで、Node.jsアプリとPostgreSQLデータベースの2つのコンテナが起動し、互いに通信できる状態になります。止める時も簡単です。

docker-compose down

ステップ4: チーム開発を意識した改善

docker-compose.ymlvolumes 設定に注目してください。

    volumes:
      - .:/usr/src/app

この一行は、あなたのPCのソースコードがあるディレクトリ (.) を、コンテナ内の /usr/src/app ディレクトリに「マウント(同期)」させる設定です。これにより、PC上でソースコードを編集すると、それが即座にコンテナ内に反映されます。これにより、コードを修正するたびにイメージを再ビルドする必要がなくなり、開発効率が劇的に向上します。

また、イメージに不要なファイル(node_modulesなど)を含めないように、.dockerignore ファイルを作成することも重要です。

.dockerignore

node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore

これにより、イメージが軽量になり、ビルド時間も短縮されます。

実際の開発現場での活用

Dockerはもはや現代の開発現場ではなくてはならないツールです。

業務での使用例

  • ローカル開発環境: 新しいメンバーがプロジェクトに参加した際、git clone して docker-compose up するだけで、数分後には開発環境が完全に再現されます。環境構築に数日かかる、なんてことはもうありません。
  • CI/CDパイプライン: GitHub ActionsなどのCI/CDツールで、テストやビルドをDockerコンテナ内で行います。これにより、開発者のローカル環境とCI環境の差異がなくなり、「CIでだけテストが失敗する」といった問題を減らせます。
  • 本番環境へのデプロイ: 作成したDockerイメージを、そのまま本番サーバーやクラウドサービス(AWS, GCPなど)で動かすことで、開発環境と本番環境の差異を最小限に抑えられます。

チーム開発でのベストプラクティス

  • docker-compose.yml をバージョン管理する: Dockerfiledocker-compose.yml をGitで管理することで、チーム全員が常に同じ環境定義を共有できます。
  • 環境変数を活用する: データベースのパスワードなどの機密情報は、コードに直接書かず、.env ファイルや環境変数を使ってコンテナに渡すのが定石です。
  • 公式イメージをベースにする: 可能な限り公式が提供しているDockerイメージをベースに使いましょう。セキュリティアップデートが定期的に行われており、信頼性が高いです。

保守性を意識した書き方

  • Dockerfileをシンプルに保つ: 1つのコンテナには1つの責務(例: Webサーバー、アプリケーションサーバー)を持たせるようにし、Dockerfileが複雑になりすぎないように心がけましょう。
  • マルチステージビルドの活用: ビルド時だけに必要なツールと、実行時に必要なファイル群を分けることで、最終的なイメージサイズを劇的に小さくできます。これにより、デプロイ時間の短縮やセキュリティ向上に繋がります。

よくあるつまずきポイントと解決策

誰でも最初はエラーに遭遇します。大切なのは、エラーを恐れず、その原因を突き止める力です。

初心者が陥りやすい問題

  • ポートの競合: Error ... port is already allocated というエラーが出たら、指定したポートがPC上の他のプログラムで既に使用されています。docker-compose.yml のポート番号(例: "8080:80"8080 の部分)を別の番号に変更するか、競合しているプログラムを停止してください。
  • ファイルパスの問題: WindowsとMac/Linuxではファイルパスの書き方が異なるため、Dockerfiledocker-compose.yml でパスを指定する際に問題が起きることがあります。相対パスをうまく使うことで、OS間の差異を吸収できます。

エラーメッセージの読み方

エラーメッセージは解決への最大のヒントです。焦らずに最後まで読みましょう。特に docker-compose up が失敗した場合は、エラーを起こしているサービス名(appdb など)と、その後のエラー内容に注目してください。

デバッグの基本的な考え方

  • ログを確認する: コンテナが正常に起動しない場合、まずはログを確認するのが鉄則です。

    # docker-composeの場合
    docker-compose logs <サービス>
    
    # docker runの場合
    docker logs <コンテナ>
  • コンテナの中に入って調査する: どうしても原因がわからない場合は、コンテナの中に入って直接調査することができます。

    docker-compose exec <サービス> sh

    これにより、コンテナのシェルが起動し、lscat コマンドで内部のファイルを確認できます。

継続的な学習のために

Dockerの世界は非常に奥が深いです。今日の学びをスタート地点として、さらに知識を広げていきましょう。

次に学ぶべきこと

  • Docker Networking: コンテナ間の通信の仕組みを深く理解すると、より複雑なアプリケーション構成に対応できるようになります。
  • マルチステージビルド: 本番用のイメージをより小さく、セキュアにするためのテクニックです。
  • Kubernetes: 多数のコンテナを本番環境で管理・運用するための「コンテナオーケストレーション」ツールです。Dockerの次のステップとして学ぶ人が多い技術です。

おすすめの学習リソース

  • 公式ドキュメント: 何よりも正確で最新の情報源です。最初は難しく感じるかもしれませんが、必要な情報を引き出す練習をすることは非常に重要です。
  • 技術ブログやチュートリアル: 多くのエンジニアが自身の知見をブログなどで公開しています。様々なユースケースに触れることで、実践的な知識が身につきます。

コミュニティとの関わり方

勉強会やオンラインコミュニティに参加して、他の学習者や経験豊富なエンジニアと交流するのもおすすめです。自分一人では解決できなかった問題のヒントがもらえたり、新しい技術トレンドを知るきっかけになったりします。アウトプットを意識して、学んだことを発信してみるのも素晴らしい学習方法です。

まとめ:成長のための次のステップ

今回は、Dockerの基本的な概念から、docker-compose を使った実践的な開発環境の構築までを駆け足で学びました。手を動かして、コンテナが起動し、ブラウザに文字が表示された瞬間の「動いた!」という小さな感動を大切にしてください。その小さな成功体験の積み重ねが、あなたをより優れたエンジニアへと成長させてくれます。

Dockerは単なるツールではありません。チーム開発を円滑にし、インフラとアプリケーションの垣根を下げ、開発者一人ひとりがより広い領域に責任を持てるようにする、現代の開発文化そのものです。今日学んだことを土台に、ぜひあなたのプロジェクトでもDockerを活用してみてください。

これからも学びの旅は続きます。エラーを恐れず、好奇心を持って、楽しみながら次のステップへ進んでいきましょう!応援しています!

関連記事