Dockerでポートフォワーディング解説


ポートフォワーディングとは

ポートフォワーディング:
インターネットから特定のポート番号宛にパケットが届いたときに、あらかじめ 設定しておいたLAN側の機器にパケットを転送する機能です。

※引用元:https://www.itbook.info/study/nat6.html

  • ポートの紐付けというイメージ

  • 仮想環境で開発をする場合、このポートフォワーディングが必須となると思いますが、今回はDockerを例に少し解説します。
    ※今回はポートフォワーディングについてDockerをもとに話すため、Dockerについては解説しません。

Dockerイメージ

  • 概要
    • スクリプト
      • Go言語で作成
    • Dockerfile
      • golangのベースイメージを使用
  • 挙動
    • コンテナを実行したら「start server」と出力
    • HTTPリクエストに対し、リクエスト側に「Hello Docker!!」と出力、リクエストを受けた側には「received request」と出力
    • 「8080」のポートを解放
sample.go
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // クライアントからリクエストを受けた場合
        log.Println("received request")
        // HTTPリクエストを受けた場合
        fmt.Fprintf(w, "Hello Docker!!")
    })

    log.Println("start server")
    server := &http.Server{Addr: ":8080"}
    if err := server.ListenAndServe(); err != nil {
        log.Println(err)
    }
}
Dockerfile
FROM golang:1.9
RUN mkdir /echo
COPY main.go /echo
CMD ["go", "run", "/echo/main.go"]

Dockerイメージのビルド

  • イメージをビルドする
$docker image build -t sample:latest .
Sending build context to Docker daemon   5.12kB
Step 1/4 : FROM golang:1.9
1.9: Pulling from library/golang
55cbf04beb70: Pull complete 
1607093a898c: Pull complete 
9a8ea045c926: Pull complete 
d4eee24d4dac: Pull complete 
9c35c9787a2f: Pull complete 
8b376bbb244f: Pull complete 
0d4eafcc732a: Pull complete 
186b06a99029: Pull complete 
Digest: sha256:8b5968585131604a92af02f5690713efadf029cc8dad53f79280b87a80eb1354
Status: Downloaded newer image for golang:1.9
 ---> ef89ef5c42a9
Step 2/4 : RUN mkdir /echo
 ---> Running in 99e306a53ced
Removing intermediate container 99e306a53ced
 ---> f9b0ec578390
Step 3/4 : COPY main.go /echo
 ---> b71ab1e040bd
Step 4/4 : CMD ["go", "run", "/echo/main.go"]
 ---> Running in fff606860921
Removing intermediate container fff606860921
 ---> 2c9096d1911b
Successfully built 2c9096d1911b
Successfully tagged sample:latest
$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
sample               latest              2c9096d1911b        16 seconds ago      750MB

Dockerコンテナの実行

  • バックグラウンドでコンテナを実行
  • -pオプション
    • ポートフォワーディングの設定
      • 左側(9000)がホストのポートで、右側(8080)がコンテナのポート
        • 上記でGo言語で書かれたスクリプトを説明しましたが、8080のみ解放されている為、必然的にコンテナ側のポートは8080になります。

※ホスト側とコンテナ側のポートを分けたのは、アクセス時にどちらからアクセスしたのか、を分かりやすくする為です。

$ docker run -d -p 9000:8080 2c9096d1911b
5e3f8c5181b99c72002fe1917044c38426132c863350c47db828909294823b0b
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
5e3f8c5181b9        2c9096d1911b        "go run /echo/main.go"   4 seconds ago       Up 3 seconds        0.0.0.0:9000->8080/tcp   objective_leavitt 

HTTPアクセス

  • コンテナに対してcurlでアクセス
$ curl http://localhost:9000
Hello Docker!!

ここまでを図解

  • Dockerfileを元にbuildしてイメージを作成
  • イメージをrunしてコンテナを立ち上げる
    • コンテナのステータスは実行中

  • コンテナ実行時(run)、-pオプションでポートフォワーディングの設定をする
    • イメージは下図のように、コンテナに穴を開けるイメージ
      • 8080のポートを解放し、9000ポートを紐づける事で、アクセスが可能な状態した

  • curlで9000ポートでHTTPリクエストを送る
    • 「Hello Docker!!」とレスポンスが返ってくる

まとめ

こんな感じでポートフォワーディングでポートの紐付けを行ってあげる事で、仮想環境に対してもアクセスできるようになるわけです。

余談ですが、-pオプションでポートフォワーディングで設定をしましたが、もしホスト側の設定をしない場合どうなると思いますか?

// こんな感じでコンテナ側のポートしか設定しない場合です
$ docker run -d -p 8080 abcdefg

まぁ知ってる方多いと思いますが、よしなに空いているポート番号を割り振って、そのポート番号と紐付けを行ってくれます。

上記を実行した後にdocker psをして、PORTSを確認すると、どのポートと紐づけられているか、が分かりますので、試したことの無い方はぜひお試し下さい。