Docker-Composeでコンテナに固定IPアドレスを割り当てる


# 【はじめに】

本学では3年生の学生実験で、仮想環境上でUbuntuを立ち上げてホストとルーターに見立てて経路制御やファイアウォールとはなんぞやというのを知るための実験をします。
昨年までは仮想環境にVirtualboxを使用していましたが、学生全員にUbuntuのイメージを共有するのにデータ量莫大になることや、インストールやセットアップ、起動に時間がかかるというデメリットがかなり浮き彫りになってしまいました。

そこで、今回は卒業研究のおまけとしてDockerを使ってこの実験環境を構築するという命を受けましたのでやってみました。

ちなみに僕はDockerはなんとなく使っているだけで特別Dockerに詳しいわけでもネットワークに詳しいわけでもありませんのでご容赦くださいね！

# 【開発環境】

今回開発に使った環境は以下の2つです。
どちらでも同様に動作することを確認しています。

## Windows

- Windows10 Home Edition
- Docker version 20.10.14, build a224086
- Docker Compose version v2.4.1

## macOS

- macOS Monterey version 12.3.1
- Docker version 20.10.12, build e91ed57
- Docker Compose version version 1.29.2, build 5becea4c

# 【完成形】

はじめに完成形をわかりやすくしておきましょう。

## 作成するサービス

| サービス名 |          IPアドレス          |
| :--------: | :--------------------------: |
|   host4    |         192.168.9.2          |
|   host5    |         192.168.8.2          |
|  router1   | 192.168.9.254, 192.168.1.254 |
|  router2   | 192.168.1.253, 192.168.8.254 |

## 作成するネットワーク

| ネットワーク名 | IPアドレス範囲 |
| :------------: | :------------: |
|     route1     | 192.168.1.0/24 |
|     route8     | 192.168.8.0/24 |
|     route9     | 192.168.9.0/24 |

これらのネットワーク上にhostとrouterを立ち上げて経路制御の実験をします。

想定している構成図は以下のとおりです。
![Network Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1102723/3ceb25aa-427a-e549-ee98-1598bd21bd54.png)

# 【ホストとルーターに使用するDockerfileについて】

今回ホストとルーターにはUbuntu20.04の公式Docker imageをもとにしたDocker imageを作成します。
具体的に変更点は、Docker公式のUbuntu20.04にはネットワーク系のコマンド(pingやroute等)が入っていません。
そのため、Dockerfileを自作してビルド時にそれらのコマンドをインストールさせます。
完成したDockerfileは以下のとおりです。

```Dockerfile:dockerfile
FROM ubuntu:20.04

RUN apt-get update -y && apt-get upgrade -y
RUN apt-get install -y iputils-ping net-tools netcat traceroute ufw systemctl
```

# 【Docker-compose.ymlでネットワークとコンテナの設定をする】

Docker-compose.ymlで先程の完成図にあったようなネットワークとすでに作ってあるDockerfileからビルドするように定義していきます。
全文は下記のとおりです。

```yml:docker-compose.yml
version: '3'
networks:
  route1_network:
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
  route8_network:
    ipam:
      driver: default
      config:
        - subnet: 192.168.8.0/24
  route9_network:
    ipam:
      driver: default
      config:
        - subnet: 192.168.9.0/24

services:
  host4:
    build:
      context: .
      dockerfile: ./host/dockerfile
    command: tail -f /dev/null
    privileged: true
    networks:
      route9_network:
        ipv4_address: 192.168.9.2

  host5:
    build:
      context: .
      dockerfile: ./host/dockerfile
    command: tail -f /dev/null
    privileged: true
    networks:
      route8_network:
        ipv4_address: 192.168.8.2

  router1:
    build:
      context: .
      dockerfile: ./host/dockerfile
    command: tail -f /dev/null
    privileged: true
    networks:
      route1_network:
        ipv4_address: 192.168.1.254
      route9_network:
        ipv4_address: 192.168.9.254

  router2:
    build:
      context: .
      dockerfile: ./host/dockerfile
    command: tail -f /dev/null
    privileged: true
    networks:
      route1_network:
        ipv4_address: 192.168.1.253
      route8_network:
        ipv4_address: 192.168.8.254
```

## ネットワークの作成

まずはネットワークを作成します。

```yml:docker-compose.yml
networks:
  route1_network:
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
```

networksの下に、作成したいネットワークの名前をつけておきます。今回であればroute1_networkです。
重要なのはsubnetの部分ですね。
こちらで設定したいIPアドレスとその範囲を指定しておきます。

続いてがこの設定したネットワークをサービスに割り当てていく作業になります。

## サービスの作成とネットワークの割り当て

```yml:docker-compose.yml
  router1:
    build:
      context: .
      dockerfile: ./host/dockerfile
    command: tail -f /dev/null
    privileged: true
    networks:
      route1_network:
        ipv4_address: 192.168.1.254
      route9_network:
        ipv4_address: 192.168.9.254
```

はじめにbuildの部分でcontextとdockerfileの位置を指定してあげます。
このcontextの設定によってそのコンテナのルート位置が決まってアクセスできる場所が決まってきますので、開発するサービスによって適宜変更してください。
参考にできそうなページはこちら

- [docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話](https://qiita.com/sam8helloworld/items/e7fffa9afc82aea68a7a)

そしてcommandを指定しているのもポイントです。
Dockerのコンテナは実行するものがなくなれば自動的に落ちますので、このコマンドでコンテナを立ち上げっぱなしにします。

さぁ本題のnetworkの部分をごらんください。

```yml:docker-compose.yml
    networks:
      route1_network:
        ipv4_address: 192.168.1.254
      route9_network:
        ipv4_address: 192.168.9.254
```

見ただけで何が書いてあるかわかりますね、先程作成したネットワークを割り当てて、このコンテナに割り当てるIPアドレスを書く。
それだけです。

# 【まとめ】

なんとも簡単でした。
この後はdocker-compose upして、ルーターやホストのルーティングテーブルを作ってあげれば完璧ですね。
今後はルーティングテーブルを手動で設定するのではなく、動的経路制御ができればいいなと思っております。

やったことのメモにどんどん記事書いていきますのでまたどこかで。