LoginSignup
13
9

More than 3 years have passed since last update.

当記事について

2019/11/15にGAになったGCPのコンテナサーバレス実行環境であるGoogle Cloud Runのポートについて理解を深める記事です。
公式ドキュメントのクイックスタートのからポートに関する部分を読み解きつつ解説します。

参考にしたサイト

コンテナ内部で使用するポート

初めに、コンテナ内部で使用しているポートを理会するためにクイックスタートのアプリケーションを見ていきましょう。

/helloworld.go
package main

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

func handler(w http.ResponseWriter, r *http.Request) {
        log.Print("Hello world received a request.")
        target := os.Getenv("TARGET")
        if target == "" {
                target = "World"
        }
        fmt.Fprintf(w, "Hello %s!\n", target)
}

func main() {
        log.Print("Hello world sample started.")

        http.HandleFunc("/", handler)

        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
        }

        log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

handler関数

handler関数では、環境変数"TARGET"から値を取得して、Helloの後ろにくっつけて引数のResponseWriterに出力しているのが解ります。環境変数"TARGET"がなければ"World"が入ります。

main関数

main関数では以下の二つのことを行っているのが解ります。
1. ルートでリクエストを待ち受けて、リクエストが来た場合に上のhandler関数を呼び出すようにしています。
2. 環境変数"PORT"から値を取得して取得した値のポート番号でリクエストを受け付けます。環境変数"PORT"がなければ"8080"が入ります。

コンテナ内部で動くアプリケーションは環境変数"PORT"の値で動いているのが解ります。

環境変数"PORT"の中身

Google Cloud Runの内部の環境変数"PORT"がどのようになっているのか、クックスタートにあるサンプルソースのmain関数を以下のようにしてビルド&デプロイしてみます。

/helloworld.go
func main() {
        log.Print("Hello world sample started.")

        http.HandleFunc("/", handler)

        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
        } else {
            log.Printf("PORT is %s", port)
        }

        log.Printf("Hello world sample is listening on port %s.", port)

        log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
$ gcloud builds submit --tag gcr.io/[PROJECT_ID]/helloworld
(省略)
$ gcloud  run deploy --image gcr.io/[PROJECT_ID]/helloworld
(省略)
Service [helloworld] revision [helloworld-XXXXXX-vof] has been deployed and is serving 100 percent of traffic at https://helloworld-XXXXXXXXX-an.a.run.app

デプロイ後、Loggingでログを確認すると次の画像のようになっていました。
Logging

Google Cloud Run上で動くコンテナの環境変数"PORT"は8080であることが解ります。

外部から接続されるポート

外部から接続されるポートを調べるために、実際にcURLを使いアクセスしてみます。

$ curl https://helloworld-XXXXXXXXX-an.a.run.app
Hello World!
$ curl https://helloworld-XXXXXXXXX-an.a.run.app:443
Hello World!
$ curl https://helloworld-XXXXXXXXX-an.a.run.app:80
curl: (35) error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
$ curl https://helloworld-XXXXXXXXX-an.a.run.app:8080
^C
$ curl https://helloworld-XXXXXXXXX-an.a.run.app:8081
^C

ポート番号443を使用しているのが解ります。8080や8081のように関係のないポートは応答がありませんでした。

外部のポートと内部のポートの紐づけ

外部の443ポートとコンテナ内部の8080ポートが環境変数"PORT"経由で紐づけられているのが解ります。
図示するとこのようになります。

ポートの紐づけ

デプロイ時のコマンドにポートを紐づけるオプションは無いので、Dockerコマンドでいう"-p 443:8080"のようなパラメータはデプロイ時に自動で行ってくれていることになります。

待ち受けポート実装の仕方

デプロイ時に自動で紐づけられるということは、コンテナ内部で待ち受けるポートはGoogle Cloud Runの制約として決められているということになりますね。Cloud Runの制約を読んでみると、コンテナ内部で待ち受けるポートは環境変数"PORT"に収められていると書いてあります。
Google Cloud Runで動かすアプリケーションは環境変数"PORT"を取得してその番号のポートで待ち受けるようにしましょう。

また、既に構築済みのコンテナをGoogle Cloud Runにデプロイする場合は待ち受けポートが何になっているかをきちんと確認するようにしましょう。

13
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
9