はじめに
こんにちは!ITスクールRareTECHにてCS(Customer Support)を担当している池村です。今回の記事はDockerのネットワークについてです。ここは初学者にとってとてもつまづきやすいポイントだと思っています。
というかネットワークの知識が必須なんですよね。ネットワークの基礎を疎かにしていると痛い目を見ます。(ブーメランですが💧)
Dockerネットワークとは
コンテナの中は、基本的に独立した環境なので、外との通信や、コンテナ間の通信を実現させるためにはネットワークを繋げてあげる必要があります。
Dockerのネットワークは作ることができます。
まずは作り方から見ていきましょう。
docker network create my_network
上記のコマンドを叩くと、ネットワークが作成できます。
docker network ls
上記コマンドを叩くことで、一覧が出ると思います。
デフォルトでいくつかあるネットワークについては後ほど説明するとして、先ほど作成したmy_network
はあるでしょうか?
次にネットワークの詳細を見てみましょう。
docker network inspect my_network
以下が筆者のローカル環境上の表示結果です。
[
{
"Name": "my_network",
"Id": "eb552f0eafb95b88844be750416ba7c19cff66a903b6fc30ce81b175776b7442",
"Created": "2025-01-04T14:44:19.314223857+09:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.117.0/24",
"Gateway": "192.168.117.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
全部を理解する必要は本当にないのですが、主要なところは押さえておきたいですね。
Name
ネットワークの名前ですね。
Id
ネットワークを識別するためのユニークなIDです。
Created
作成した日時ですね。
Scope
localになっているので、ローカルのホストのみ有効なネットワークです。要は自分のPC上で動くためだけのものです。(細かく言うと自身のPC上のDockerEngineのみ)これがglobalになっていると複数ホスト間で動きます。複数ホストは分散システムや別のサーバーで動くコンテナとも共有できるという意味です。
Driver
ネットワークの種類です。詳しくは後述します。
IPAM
ここはかなり重要で、IPアドレスの管理について書かれています。特に重要なのが以下になります。
Subnet: 192.168.117.0/24
ネットワークの範囲(サブネット)です。このネットワーク内で使用できるIPアドレスが 192.168.117.1 から 192.168.117.254 に設定されています。
Gateway: 192.168.117.1
このネットワークのゲートウェイ(ルーターの役割を果たすアドレス)です。コンテナが外部のネットワークにアクセスする際にこのアドレスを使用します。
Containers
現在このネットワークが使われているコンテナを表示します。空なので、今は何も接続されていないただのネットワークです。
飛ばした項目は初学者が覚えてもあまり効果がないので、飛ばしてOKです。
ネットワークの種類について
このネットワークにもいくつか種類があるのでご紹介します。
ただ、基本はBridgeです。
Bridge
これはコンテナ間の通信を可能にするネットワークです。
これを作成することで、例えば何かしらの言語でWebアプリを動かすコンテナと、MySQLなどのデータベースを動かすコンテナ間を通信させることができます。前項で作成したのもBridgeネットワークになります。ポートフォワーディングを使うことで外部との接続も可能です。
Host
Hostは名前の通り、ホストであるサーバー or PCのネットワークを使って通信を行うことができます。ホストのネットワークを直接使うわけですから、速度に関してはとても早いです。もちろんポートもホストのポートを使うことができます。セキュリティの観点からもあまりオススメする繋ぎ方ではないと思っています。よっぽどのことがない限りBridgeを使いたいですね。
使いたい場合は以下のコマンドを叩いてください。
docker run --network host my_container
None
こちらはネットワークから完全に分離する形です。何も繋がっていないので外部とも別コンテナとも通信できません。私自身あまり使ったことないですね。
docker run --network none my_container
Overlay(上級者向け)
複数のホストにまたがったネットワークを作成します。
分散システム向けで、複数ホストをまたげる分速度もBridgeより遅いです。IPアドレスはDockerが管理しています。分散システム向けって何?と言われると、DockerSwarmやKubernetesのことを指すことが多いです。が、これらはかなり上級者向けて初学者はもっと後半で学習することになります。
Macvlan(上級者向け)
こちらは、コンテナが独自のIPアドレスとMACアドレスを持つことができるものです。これはルータとかスイッチからも独立したデバイスとして認識されるので、例えば家のプリンターとかとも直接通信できるわけです。ただ、IPアドレスを割り当ててあげる必要はあります。
ポートフォワーディングについて
ではポートフォワーディングについてみていきましょう。
これはホストのポートとコンテナのポートを繋げてあげることですね。そうすることでコンテナの中に外部の通信を送ることができます。
ポートとは?
ポートはPCやサーバーで通信を受け取る際の入り口のことですね。
コンピュータはどの通信をどのポートで受け取るか決まっています。例えばHTTPの通信なら80番、SSHの通信なら22番とかです。これはそのままプロトコル(通信規格)と関係しています。
すでに決まっているポートの他に、自由に使えるポートがあって、Dockerでコンテナに繋げるのはそちらの自由なポートになります。
ポート番号について詳しくなりたいなら、頑張ってググってください🖐️
ネットワークの基本の基本です。
やはり簡単な確認方法はWebサーバーですかね。以下のコマンドを叩いて実行してみましょう。
docker run -d -p 8080:80 nginx
NginxはWebサーバーをたてるためのアプリケーションです。これを使うことで、80番ポートでの通信が可能になります。
無事にコンテナが動き始めたら、以下のURLをクリックしてみましょう。
これは、ホストの8080番ポートと、コンテナの中の80番ポートが繋がっているということです。URLのlocalhostは自身のPCを指していると思ってください。
ポートフォワーディングをするためのオプションは-p
ですのでお間違いなく。
Bridgeでコンテナ間通信をしてみよう
ではここからはコンテナ間通信をどうやって行うかをみていきましょう。
Webアプリが一番わかりやすい(自分が慣れている)ので、簡単なWebサーバーのコンテナと、データベースのコンテナを立ち上げて、その二つのコンテナ間で通信してみます。
この記事をどんな方が読むかわからないですが、今回はPython(記述量が比較的楽)で作成してみます。
Flask(Python)とMySQLのコンテナ間通信
Webサーバーとデータベースについて、ある程度理解している前提で進めさせていただきます。
MySQLのコンテナを作成
docker run --network my_network --name mysql_container \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=test_db \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=password \
-d mysql:latest
docker exec -it mysql_container mysql -u root -proot
SHOW DATABASES;
以下のように表示されていれば、test_dbのデータベースはできています。
ではテーブルを作っていきます。
USE test_db;
次にSQLを実行しましょう。今回は超シンプルなテーブルにします。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
これでデータベースの準備は整いました。次はFlaskですね。
Flaskのコンテナを作成
コンテナを作成する前に、ローカルにディレクトリを作成して、そこにPythonのファイルを作成します。あとはrequirements.txtも必要ですね。ディレクトリはどの場所でも大丈夫です。ホームディレクトリに作るのはあまりおすすめしません。
mkdir flask_app && cd flask_app && touch app.py requirements.txt
ディレクトリ構成は以下のようになっていればOKです。
.
└── flask_app
├── app.py
└── requirements.txt
2 directories, 2 files
では次にapp.py
の中身を書いていきます。
from flask import Flask, request, jsonify
import mysql.connector
app = Flask(__name__)
db_config = {
"host": "mysql_container",
"user": "user",
"password": "password",
"database": "test_db"
}
@app.route('/add', methods=['POST'])
def add_data():
data = request.json
name = data.get("name")
try:
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
cursor.execute("INSERT INTO users (name) VALUES(%s)", (name,))
conn.commit()
cursor.close()
conn.close()
return jsonify({"message": "success"})
except mysql.connector.Error as err:
return jsonify({"error": str(err)}), 500
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
中身の簡単な説明をすると
- 今回使うFlaskのライブラリ群をインポート
- PythonでMySQLと接続するためのライブラリをインポート
- 『 http://localhost:5000/add 』というURLにPOSTリクエストでデータを送ると、そのデータをデータベースに登録する処理
でできています。
次にrequirements.txt
の中身ですね。
Flask
mysql-connector-python
この二つができたら準備完了です。すでにMySQLのコンテナは動いているので、Flaskのコンテナも同じネットワークを指定して立ち上げます。
docker run --network my_network --name flask_container -v "$(pwd)/flask_app:/app" -w /app -p 5000:5000 python:3.9-slim sh -c "ls && pip install -r requirements.txt && python app.py"
以下のコマンドを叩くのはflask_app
ディレクトリのある場所です。
あとMacだと標準で5000番ポートが使われていたり、ローカルで動いているFlaskアプリとポートが競合する可能性があります。その際には5001:5000
とか、5002:5000
などポートの調整をするといいかもしれません。
curl
コマンドでリクエストを送ってみる
もう一つターミナルを開いて(もしくはtmuxで分割して)、curl
コマンドでPOSTリクエストを送ってみましょう。curl
コマンドはWebサーバーにGETリクエストやPOSTリクエストを送ってレスポンスを受け取ることができるコマンドです。
curl -X POST -H "Content-Type: application/json" -d '{"name": "TestName"}' http://localhost:5000/add
ここも、5001や5002のポートにフォワーディングしているなら、そちらに合わせてください。
今回はTestNameという名前のデータをWebサーバーに送っています。これがデータベースに登録されているなら、二つのコンテナの通信はうまくいっています。
では実際に登録されているか確認してみます。
docker exec -it mysql_container mysql -u root -proot -e "SELECT * FROM test_db.users;"
これでテーブルが表示されていればOKです。
検証のため、私はいくつかデータを送ってみましたが、問題なく登録されていますね。
おわりに
Dockerのネットワークの話が終わったので、ようやくDockerfileの話に入れそうです。
正直、コマンドの記述量的に非効率すぎて大変なので、もっと簡略化するためにDockerfileを早く使いたかったです。ただ、ここは順番なのでここまでが基礎、ここからが応用的な話として割り切りっていきたいですね。
改めて、基礎は大事です。ただ基礎ばっかりやってるとつまらないのが難点。
次の記事👇