元営業WEBエンジニアのアプリ開発日記

営業出身のWEB系エンジニアが気になったものから作ってはメモを残してくブログ

KubernetesでGolangのPodにHttp/2を通信

概要

KubernetesGolangのPodにHttp/2を通信させる。
NginxにtlsでHttp/2通信すことできたから、Golangも楽勝でしょ♪
とか思ってたらそこそこ詰まったのでメモしとくっす

基本的には以下2つで構築した環境を前提に記載する

自己証明書関連の設定

自己証明書の作成とSecret登録

minikubeでTLS通信して裏側のNginxでHTTP/2.0を受けるの「自己証明書の作成」=>「自己証明書をSecretsとして登録」の通りにして、go.ucwork.localtls通信できるように準備する

証明書をgolangのpodに登録

volumesに証明書を登録したsecretを設定し、podの任意のパスにマウントする。

apiVersion: v1
kind: Service
metadata:
  name: go
  labels:
    app: go
spec:
  ports:
    - port: 443
  selector:
    app: go
    tier: backend
  type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: go-pv-claim
  labels:
    app: go
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: go
  labels:
    app: go
spec:
  selector:
    matchLabels:
      app: go
      tier: backend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: go
        tier: backend
    spec:
      containers:
      - image: shintaro0123/golang:latest
        name: go
        env:
        - name: GO_DB_HOST
          value: go-mysql
        - name: GO_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 443 
          name: go
        volumeMounts:
        - name: go-persistent-storage
          mountPath: /var/www/html
        - name: tls-cert       # ②podの好きなパスにマウントする
          mountPath: /go/tls   # ↑
      volumes:
      - name: go-persistent-storage
        persistentVolumeClaim:
          claimName: go-pv-claim
      - name: tls-cert           # ①作成したsecretを指定
        secret:                  # ↑
          secretName: tls-secret # ↑

Golangファイルの作成

http2のパッケージ取得

$ go get -u golang.org/x/net/http2

http2を受けることができるようgoファイル作成

kubernetesでローカル環境にNGINX, golang, MySQL環境作ってみるこの時の記述に引っ張られてDB関連のことも記載されているが、
ポイントは以下2点。それ以外は消しちゃってもいい世界

  • マウントした証明書のパスを指定してListen
    • srv.ListenAndServeTLS("/go/tls/tls.crt", "/go/tls/tls.key")
  • http2で通信を受けることができように設定
    • http2.ConfigureServer(srv, nil)

最初ネットで調べてListenAndServeTLS(":443", "/go/tls/tls.crt", "/go/tls/tls.key")これでなんとかやっていたが、どうしても「CrashLoopBackOff」になりpodが立ち上がらなかった・・・
そもそもgo初心者なんで、まぁ細々したことは学習しながら学んでいこう。

main.go

package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "os"

    "golang.org/x/net/http2"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handler)

    srv := &http.Server{
        Addr:    ":443",
        Handler: mux,
    }
    http2.VerboseLogs = true
    http2.ConfigureServer(srv, nil)
    log.Fatal(srv.ListenAndServeTLS("/go/tls/tls.crt", "/go/tls/tls.key"))
}

func handler(w http.ResponseWriter, r *http.Request) {
    /** URLパス表示 */
    w.Write([]byte("url path is " + r.URL.Path[1:] + "\n"))

    /** DB接続 */
    var dbConnectQuery string
    dbConnectQuery = "root:" + os.Getenv("GO_DB_PASSWORD") + "@tcp(" + os.Getenv("GO_DB_HOST") + ":3306)/ucwork"
    db, err := sql.Open("mysql", dbConnectQuery)
    if err != nil {
        panic(err.Error())
    }
    defer db.Close() // 関数がリターンする直前に呼び出される

    rows, err := db.Query("SELECT * FROM user") //
    if err != nil {
        panic(err.Error())
    }

    columns, err := rows.Columns() // カラム名を取得
    if err != nil {
        panic(err.Error())
    }

    values := make([]sql.RawBytes, len(columns))

    scanArgs := make([]interface{}, len(values))
    for i := range values {
        scanArgs[i] = &values[i]
    }

    for rows.Next() {
        err = rows.Scan(scanArgs...)
        if err != nil {
            panic(err.Error())
        }

        var value string
        for i, col := range values {
            // Here we can check if the value is nil (NULL value)
            if col == nil {
                value = "NULL"
            } else {
                value = string(col)
            }
            w.Write([]byte(columns[i] + ": " + value + "\n"))
        }
        fmt.Println("-----------------------------------")
    }
}

ビルドする

kubernetesでローカル環境にNGINX, golang, MySQL環境作ってみる
ここの「golang」にあるように、ビルドしてデプロイ

$ env GOOS=linux GOARCH=amd64 go build main.go
$ # 生成されたmainをpodに反映させる

検証する

curlでアクセスしてみるとHTTP/2でアクセスできた!!

$ curl --resolve go.ucwork.local:443:`minikube ip` -k https://go.ucwork.local -v
...
> GET / HTTP/2
> Host: go.ucwork.local
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 66
< date: Sat, 30 Mar 2019 20:28:25 GMT
<
url path is
id: 1
name: taro
id: 2
name: jiro
id: 3
name: hanako
* Connection #0 to host go.ucwork.local left intact

まとめ

早くgolang自体の勉強したいのになかなか環境構築で手こずるw
やっと最低限くらいができたんで、あとはSKAFFOLD使ったローカル開発方式だけまとめて、golang自体の勉強にスイッチしよう!!!