Dockerでlocalhostが立たない！そんな時はここをみて！

最近、`Go`と`TypeScript`を使って個人開発しているときに、Goのサーバーが立たないということがありました。

色々調べても中々分からず結構手こずってしまい、友達に聞いたところ３分くらいで解決しました。
そこで言われたのが、**Docker ポートフォワーディング**で調べてみてって言われて調べたら解決したので、今回はそれを記事にしようと思います。

## Dockerのポートフォワーディングとは
まず、`docker-compose.yml`ファイルを見てみましょう。
```docker-compose.yml
version: '3.9'
services:
  backend:
    build:
      context: .
      dockerfile: ./docker/backend/app/Dockerfile
    tty: true
    volumes:
      - type: bind
        source: ./backend
        target: /api
    ports:
      - "8080:8080"
```
これでサーバーを立ててみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/c219925c-c7cc-8838-b6a5-b6d24acfc353.png)
このようになるわけです。

その原因の部分が、
```docker-compose.yml
    ports:
      - "8080:8080"
```
このポート番号が「:」で右と左に分かれています。
この記号を使うことで、コンテナのポート番号と、ローカルのポート番号を紐づけることができます。
右が**コンテナのポート番号**で左が**ホスト（あなた自身のパソコン）のポート番号**です。

今回`go run main.go`を叩くと、ポート番号`3000`が呼ばれていたので、アクセスできなかったんですね。

アクセスできるようにするには、
```docker-compose.yml
    ports:
      - "3000:8080"
```
こうすることで、ちゃんと接続することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/621a35ac-9526-5398-2f05-6d0b00ad2e5e.png)

ここで少し[公式ドキュメント](https://docs.docker.jp/v1.11/engine/userguide/networking/default_network/binding.html?highlight=%E3%83%9D%E3%83%BC%E3%83%88)を見てみると、このように書いてありました。

>デフォルトの Docker コンテナは外の世界と通信できます。しかし、外の世界からはコンテナに接続できません。
[公式ドキュメント](https://docs.docker.jp/v1.11/engine/userguide/networking/default_network/binding.html?highlight=%E3%83%9D%E3%83%BC%E3%83%88)

つまり、ブラウザからコンテナのポート番号にアクセスしようとしてもアクセスできないということが公式ドキュメントにも書いてありましたね。

## Dockerのネットワークを調べてみる
### ネットワークドライバー
`Docker`ではネットワークの扱いが重要で、`Docker`のコンテナ内ではコンテナで１つのプロセスだけを動かすのが定石です。
つまり、１つのコンテナに`Go`と`Ubuntu`を動かすといったことはあまりしない方がいいんですね。
１つのコンテナにつき、１つのプロセスで構成し、それぞれで通信をするのがいいんですね。
以下の画像のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/4483867d-6b01-cf3f-7af9-cd299990383c.png)
（[入門 Docker](https://y-ohgi.com/introduction-docker/2_component/network/
)より引用）

そのネットワークを構成するものとして代表的なものが２つあります。
それが、**bridge**と**host**です。
#### bridge
`Docker`のネットワークを構成する上で、デフォルトが`bridge`です。
何も指定しなければ、ネットワークを構成するものとして、ブリッジが採用されます。
ブリッジは、簡単にいうと、次にどこにいくかを指示してくれる機械です。（厳密には違いますが、ここでは気にしないこととします）

ブリッジを使うことで、コンテナ間で通信をしてくれるわけですね。

実際に見てみましょう。
以下のようなコマンドを叩くことで、`docker`のネットワークにブリッジがあることがわかります。
```
$ docker network ls  

NETWORK ID     NAME                    DRIVER    SCOPE
47ddb51d32a4   bridge                  bridge    local
d7d8e86102f6   host                    host      local
ca24a558faf1   none                    null      local
```
`bridge`があることが確認できました。

#### host
`host`ネットワークは、そのホスト（あなた自身のPC）の構成と同じで、コンテナとホストで使用できるポートを共有します。つまり、
コンテナとローカルのネットワーク分離は行われないため、性能は向上します。
ですが、現在ホストPCのOSが`Linux`の時にしか、このネットワークは使えないんです。
`Docker Desktop for Mac`、`Docker Desktop for Windows`、`Docker EE for Windows Server`では使えないんですね。
その根拠が[公式ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/network/host/)で書かれています。
>ホストネットワークにおけるドライバーは Linux ホスト上においてのみ動作します。 Docker Desktop for Mac、Docker Desktop for Windows、Docker EE for Windows Server ではサポートされていません。

このような経緯があるから、`docker network`のデフォルトにはブリッジが採用されているのかもしれませんね。


【参考資料】

https://docs.docker.jp/v1.11/engine/userguide/networking/default_network/binding.html?highlight=%E3%83%9D%E3%83%BC%E3%83%88

https://y-ohgi.com/introduction-docker/2_component/network/

https://wa3.i-3-i.info/word12031.html

https://matsuand.github.io/docs.docker.jp.onthefly/network/host/