GKEでのService(ClusterIP/NodePort/LoadBalancer)とIngress

(2018-06-23)

疎通確認用アプリケーション

GETでは200を返し、POSTではURLにGETリクエストを送ってステータスコードを返す。

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type PostBody struct {
	URL string `json:"url"`
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodGet {
		fmt.Fprintln(w, "ok")
	} else if r.Method == http.MethodPost {
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err.Error())
			return
		}
		p := PostBody{}
		if err := json.Unmarshal(data, &p); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintln(w, err.Error())
			return
		}

		resp, err := http.DefaultClient.Get(p.URL)
		if err != nil {
			fmt.Fprintln(w, err.Error())
			return
		}
		defer resp.Body.Close()
		fmt.Fprintln(w, resp.StatusCode)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func main() {
	http.HandleFunc("/", handler)
	fmt.Println("Listen on :8080")
	http.ListenAndServe(":8080", nil)
}
FROM golang:1.10-alpine3.7 AS build-env
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp

FROM alpine:3.7
RUN apk update && \
   apk add ca-certificates && \
   update-ca-certificates && \
   rm -rf /var/cache/apk/*
WORKDIR /app
COPY --from=build-env /app/goapp /app
EXPOSE 8080
ENTRYPOINT ./goapp
$ docker build -t asia.gcr.io/*****/network-checker .
$ gcloud docker -- push asia.gcr.io/*****/network-checker

DeploymentとServiceを作成

ksonnetでマニフェストを生成した。

ksonnetでkubernetesのmanifestを環境ごとに生成/applyする - sambaiz-net

$ ks init try-service
$ cd try-service
$ ks generate deployed-service clusterip-app --image asia.gcr.io/****/network-checker --type ClusterIP
$ ks generate deployed-service nodeport-app --image asia.gcr.io/l****/network-checker --type NodePort
$ ks generate deployed-service loadbalancer-app --image asia.gcr.io/****/network-checker --type LoadBalancer

containerPortを8080に変更して、PodがPendingしないようにresourcesを絞る。

"containers": [
  {
     ...
     "resources": {
        "limits": {
           "cpu": params.cpu,
           "memory": params.memory
        },
        "requests": {
           "cpu": params.cpu,
           "memory": params.memory
        }
     }
  }
]
$ ks apply default
$ kubectl get service
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE
clusterip-app      ClusterIP      10.11.253.76    <none>            80/TCP         8m
kubernetes         ClusterIP      10.11.240.1     <none>            443/TCP        3h
loadbalancer-app   LoadBalancer   10.11.250.126   130.211.166.214   80:32627/TCP   8m
nodeport-app       NodePort       10.11.253.190   <none>            80:31233/TCP   8m

LoadBalancer

L4のNetwork Load Balancerが立ち、ヘルスチェックが通っていればEXTERNAL-IPでそのままアクセスできる。

Network Load Balancer

$ curl -d '{"url": "http://clusterip-app"}' http://130.211.166.214/
200

$ curl -d '{"url": "http://nodeport-app"}' http://130.211.166.214/
200

クラウドごとに異なるクラスタ外のLoadBalancerの操作はcloudprovider.LoadBalancer interfaceになっていて、service_controllerに渡すことができるようになっている。

KubernetesのCustom Resource Definition(CRD)とCustom Controller - sambaiz-net

NodePort

ファイアウォールルールでGCEのポートが開いていないのでそのままではアクセスできない。開けばNodeのIPとnodePortでアクセスできる。

ファイアウォールルール

$ kubectl get node -o wide
NAME                                          STATUS    ROLES     AGE       VERSION         EXTERNAL-IP       OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
gke-test-cluster-default-pool-6601d6a8-07gm   Ready     <none>    53m       v1.8.10-gke.0   130.211.190.139   Container-Optimized OS from Google   4.4.111+         docker://17.3.2
gke-test-cluster-default-pool-6601d6a8-6881   Ready     <none>    4h        v1.8.10-gke.0   35.224.204.110    Container-Optimized OS from Google   4.4.111+         docker://17.3.2
$ curl -d '{"url": "http://clusterip-app"}' 35.224.204.110:31233
200

ちなみにNetworkPolicyを使うとPodのトラフィックを制御できる。

KubernetesのNetworkPolicy Resource - sambaiz-net

Ingressを作成

Ingressはホストやパスで異なるServiceにルーティングしたり、 SSL終端にすることができる。

local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["ingress"];
[
  {
    "apiVersion": "extensions/v1beta1",
    "kind": "Ingress",
    "metadata": {         
      "name": params.name,
      "annotations": {
        "ingress.kubernetes.io/rewrite-target": "/"
        // "kubernetes.io/ingress.allow-http": "false"
      }
    },
    "spec": {
      /*
      "tls: {
        "secretName": ""
      },
      */
      // default backend
      "backend": {
        "serviceName": "nodeport-app",
        "servicePort": 81 // fail to reach app
      },
      "rules": [
        {
          "http": {
            "paths": [
              {
                "path": "/aaa",
                "backend": {
                  "serviceName": "nodeport-app",
                  "servicePort": 80
                }
              }
            ]
          }
        }
      ]
    }
  }
]
$ ks apply default -c ingress
$ kubectl get ingress
NAME      HOSTS     ADDRESS         PORTS     AGE
ingress   *         35.241.38.159   80        22m

$ curl 35.241.38.159
default backend - 404

$ curl 35.241.38.159/aaa
ok

L7のHTTP Load Balancerが立っている。

HTTP Load Balancer