- 投稿日:2020-05-21T22:35:17+09:00
Goで書いたサーバーをCloud Runで動かす
Goで書いたサーバーをデプロイする先を探していて、Google App Engine(SE)やArukas、Herokuなどを試してきたが、今回は新たにCloud Runを試したのでその手順をここに記しておく。
前提
- Google Cloud Platformのアカウント(請求先の登録が必要)
- Google Cloud Platformのプロジェクト作成
- Google Cloud SDKのインストール
- Dockerがインストールされていること+起動していること
この辺りの説明は省略。
Cloud Runの有効化(初回のみ)
Cloud consoleでCloud Runのページを開き、Cloud Runを有効にしておく。フルマネージド環境で使う場合は「CLOUD RUNの使用を開始する」を選べばOK。
検証用サーバープログラムの準備
リクエストがくるとHello worldを返すだけのシンプルなHTTPサーバーを用意した。
Cloud Runで動かすコンテナは、環境変数PORT
で指定されたポートでListenできるようにしておかなければいけないので、Listenするポート番号を引数で指定できるようにしている。(環境変数を直接参照しても良いが)https://cloud.google.com/run/docs/reference/container-contract?hl=ja#port
main.gopackage main import ( "fmt" "net/http" "os" "strconv" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello world\n") } func main() { port, _ := strconv.Atoi(os.Args[1]) fmt.Printf("Starting server at Port %d", port) http.HandleFunc("/", handler) http.ListenAndServe(fmt.Sprintf(":%d", port), nil) }動作確認のため、ローカル3000版ポート指定で起動してみる。
go run main.go 3000別のShellからcurlでリクエストを送ってみて、Hello worldが返ってきたらOK。サーバーを停止する。
curl http://localhost:3000/Dockerイメージの作成
DockerfileFROM golang:latest as builder ENV CGO_ENABLED=0 ENV GOOS=linux ENV GOARCH=amd64 WORKDIR /go/src/github.com/yokoe/cloudrunexp COPY . . RUN go build main.go # runtime image FROM alpine COPY --from=builder /go/src/github.com/yokoe/cloudrunexp /app CMD /app/main $PORTalpineをベースに、最小限のサーバーのイメージを作る。サーバー起動時の引数に環境変数
$PORT
を渡すことで、$PORT
番号のポートでListenするようにする。docker build -t cloudrunexp .ビルドに失敗する場合はDockerが起動しているか確認する。
docker run -e "PORT=3000" -p 3000:3000 -t cloudrunexp動作確認用に
$PORT
=3000で、サーバーを起動してみる。ポートフォワーディングをして、http://localhost:3000/
でコンテナ内のサーバーにアクセスし、レスポンスが返ってくることを確認する。curl http://localhost:3000/Container RegistryへのPush
作成したイメージをGoogle Container Registry(GCR)にPushする。
Container Registry APIの有効化(初回のみ)
Container Registry APIを有効にしたことがない場合は、コンソールから有効にしておく。
Docker認証ヘルパーの有効化(初回のみ)
gcloud auth login gcloud auth configure-dockergcloudで認証し、Docker認証ヘルパーを有効にすることで、
docker push
コマンドでGCRにイメージをPushできるようになる。https://cloud.google.com/container-registry/docs/advanced-authentication?hl=ja
タグをつける
docker tag cloudrunexp gcr.io/go-server-exp/cloudrunexp:firstbuildアップデートしていく時に、どのイメージがどのビルドかわかるようにタグをつけておく。今回は最初なので
firstbuild
というタグをつけた。GCRにイメージをPushする
docker push gcr.io/go-server-exp/cloudrunexp:firstbuild
docker push
コマンドを使って、firstbuild
タグのついたイメージをGCRにPushする。Pushに成功すると、Cloud consoleのContainer Registry画面でPushされたイメージのリストを確認できる。
サービスの作成
Container Registryでイメージの確認
Container Registryのイメージのメニューから「Cloud Runへデプロイ」を選ぶと、Cloud Runのサービス作成画面が開く。
サービスの作成
自分で管理しているGKE上で動かしたいわけではないので、フルマネージドを選ぶ。また、パブリック(誰でもアクセスできる)なウェブサーバーとして動かしたいので「未認証の呼び出しを許可」を選択。
コンテナイメージが、先ほどContainer Registryで選んだイメージになっていることを確認して「作成」する。
作成完了すると、早速利用可能となり、自動生成されたURLが表示される。アクセスしてみて、Hello worldが表示されれば成功。カスタムドメインを割り当てたい場合は、URL横のinfoボタンから、「MANAGE CUSTOM DOMAINS」に進み、マッピングを追加すれば簡単に利用できた。
なお、今回はサービスの設定、デプロイはWeb UI経由で行ったがCI経由、Terraformなどを使っても操作できる。
感想
GKEでDeploymentやService、Ingressの設定をするのに比べると非常に楽だった。カスタムドメインの割当ても簡単だったし、ちょっとしたHTTPサーバーを立ち上げるのには結構良さそう。
HerokuのDocker Deployも同様に簡単で良かったが、GCSやCloudSQLなどGCP系のサービスと組み合わせる場合はCloud Runも良さそう。
参考
https://cloud.google.com/container-registry/docs/quickstart?hl=ja
- 投稿日:2020-05-21T18:15:29+09:00
巨大なデータを取得する関数でデータを戻り値にする代わり
sink
って名前がわかりやすいなと思ったので忘れないようにメモ。Goで巨大なデータを取得する処理の場合、配列に入れて返すのどうなんってなりがち。
func FetchTooBigData() ([]Datum, error) { var data []Datum ... data = append(data, Datum{...}) ... return data }メモリに全部載せるの流石にキツイ。
受け取るための某を作って引数で渡してやるのが設計的に簡単で良さそう。
type DataSink interface { Add(Datum) error } func FetchTooBigData(sink DataSink) error { ... if err := sink.Add(Datum{...}); err != nil { return err } ... }もっとちゃんと作り込んで、他の言語でよく見るチェーンっぽい書き方をするなら
type EnumerableData interface { Next() bool Current() Datum } func FetchTooBigData() (EnumerableData, error) { ... // よしなに } data, err := FetchTooBigData() if err != nil { panic(err) } for data.Next() { datum := data.Current() ...後続処理 }みたいなコルーチンじみたアレを作るんだろうけど、流石に面倒くさい。
- 投稿日:2020-05-21T17:16:35+09:00
[Go]ElasticSearchの実行クエリをログ出力する
モチベーション
GoからElasticSearchを操作するクライアントとしてgithub.com/olivere/elasticがある。
実行時に展開される生queryをみたかった。以下のようにセットするとログ出力で確認できる。sample.goes, err := elastic.NewClient( // other option... elastic.SetTraceLog(log.New(os.Stdout, "[elastic-search]", 0)), // [elastic-search]はlog prefix )参考
- 投稿日:2020-05-21T08:56:50+09:00
golang 戻り値が関数の時の初期化的な流れ
関数が戻り値の時に、どういう流れで初期化されているのか確認してみた
関数を返す前までの準備はされた上で、関数が戻り値として帰る。その戻った関数が実行されるのは、それが実行される時(わかりにくい表現だなぁ)
closure.gopackage main import "fmt" func intSeq() func() int { i := 0 fmt.Println("init") return func() int { i += 1 return i } } func main() { fmt.Println("before init") nextInt := intSeq() fmt.Println("after init") fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) newInts := intSeq() fmt.Println(newInts()) }結果
before init init after init 1 2 3 init 1
- 投稿日:2020-05-21T04:50:03+09:00
【Golang】http.ClientにRoot CAを読み込ませる
RootCAs が nil だと普通にPCの証明書を取りに行っちゃうのでRoot CAをハードコードする
これでBurp Suiteとかを使った中間者攻撃を対策できる・・・ハズ
main.goca := `-----BEGIN CERTIFICATE----- 読み込ませる Root CA の中身 -----END CERTIFICATE-----` caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM([]byte(ca)) c := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: caCertPool, }, }, }