kubernetesはいろいろあって難しい感じもしますが、手を動かせば理解も進むと思います。

なので、手を動かしてみましょう。

本当だったらGKEとかを使ってみたいところですが、お金もかかるのでローカルでkubernetesを動かせるminikubeを使います。

minikubeは一つのノードで動くkubernetesクラスタを起動させます。

さて、ここでもうすでにkubernetesの単語がいろいろでてきていて混乱し始めるところでしょう。

まずはでてくるkubernetes用語を少し整理します。

用語の整理

用語の一覧は下記にまとまっています。

https://kubernetes.io/docs/reference/glossary/

さらに kubectl explain コマンドでも詳細を知ることができます。

ただ、このページを読むだけだとわけわからんと思うので、これから関係ありそうな用語を抜き出して簡単にしてまとめます。

クラスタ(cluster)

ノード(node)

Pod

サービス(service)

マスターコンポーネントとノードコンポーネント

ここまできたら実際に手を動かしてみましょう。

macを使っている前提で話を進めます。

下記のリンクの手順をほぼそのまま使っています。

https://kubernetes.io/docs/tasks/tools/install-minikube/

Hypervisorのインストール

Hypervisorとは?

VirtualBox

https://www.virtualbox.org/wiki/Downloads

VMware Fusion

https://www.vmware.com/products/fusion

Hyperkit

https://github.com/moby/hyperkit

ちなみに自分はVirtualBoxを使っています。

minikubeのインストール

brew経由でインストールできます

% brew cask install minikube

kubectlのインストール

gcloud SDK 237.0.0によってkubectlの管理が前に比べて劇的によくなりました。

kubectlのバージョンはv1.11.7になり、さらに、複数のkubectlのバージョンも含まれるようになりました。(kubectl.1.9 kubectl.1.10 kubectl.1.11 kubectl.1.12 kubectl.1.13 というコマンドが含まれるようになりました。)

もうbrewに頼る必要はないのです!

インストール方法

下記に従ってgcloud SDKをインストールしましょう。

https://cloud.google.com/sdk/docs/quickstart-macos?hl=en

gcloud SDKがインストールできればgcloudコマンドが使えるようになっていると思うので、gcloudコマンド経由でkubectlをインストールしましょう。

% gcloud components install kubectl
% kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.7", GitCommit:"65ecaf0671341311ce6aea0edab46ee69f65d59e", GitTreeState:"clean", BuildDate:"2019-01-24T19:32:00Z", GoVersion:"go1.10.7", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:00:57Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}

楽な時代になったものだ。

kubectxのインストール

クラスターの切り替えが楽になるのでいれておくとよいかと思います。

% brew install kubectx

sternのインストール

sternはkubernetesのログを見るためには便利なツールなのでインストールしておきましょう。

brew経由でインストールできます。

% brew install stern

ここまでくれば準備はOKだと思います。

それでは実際にminikubeを起動させてkubernetesの世界に入ってみましょう

% minikube start

動かなかった場合の対応

自分の場合は Error restarting cluster: restarting kube-proxy: waiting for kube-proxy to be up for configmap update: timed out waiting for the condition がでて、下記の用に対応しました。

# とりあえず潰しても問題なさそうであれば
% minikube delete
% minikube start
# 動いた!

minikubeが起動できてたら、kubernetesを触る準備は万全です。

kubectxを使ってどのクラスタを操作しているかを確認しましょう!

minikube起動直後では、minikubeクラスタが指定されていると思います。

% kubectx
minikube

特にまだなにもしていないので、Podとかも上がってないです。

ノードはいます。

% kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     master    42m       v1.13.3
% kubectl get pods
No resources found.

kubernetesのサンプルのマニフェストファイルはkubernetes_introductionにおいておきました。

cloneして使います。

% git clone https://github.com/SpringMT/kubernetes_introduction.git
% cd kubernetes_introduction/manifests

ではPodを作ってみましょう。

kubectl apply コマンドを使います。

事前に kubectx でクラスタがminikubeになっていることを確認しておいてください!

% kubectl apply -f step1/pod.yaml
pod/introduction-web created
% kubectl get pods
NAME               READY     STATUS    RESTARTS   AGE
introduction-web   1/1       Running   0          22s

おおお、無事立ち上がっています。

Podの詳細も確認してみましょう。

% kubectl describe pods introduction-web
Name:               introduction-web
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               minikube/10.0.2.15
Start Time:         Tue, 05 Mar 2019 16:16:32 +0900
Labels:             app=web
Annotations:        kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"web"},"name":"introduction-web","namespace":"default"},"spec":{"container...
Status:             Running
IP:                 172.17.0.6
Containers:
  web:
    Container ID:   docker://828586eb578dade41a1ba49accabd7981e9ffe22086c67eef1a23356d41f6baa
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:e2048a115f51d61fa589f5f592f677300f1c122de07904e0ebefe44a6a89da23
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Tue, 05 Mar 2019 16:16:50 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kgtlz (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-kgtlz:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kgtlz
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  22m   default-scheduler  Successfully assigned default/introduction-web to minikube
  Normal  Pulling    22m   kubelet, minikube  pulling image "nginx"
  Normal  Pulled     22m   kubelet, minikube  Successfully pulled image "nginx"
  Normal  Created    22m   kubelet, minikube  Created container
  Normal  Started    22m   kubelet, minikube  Started container

いろいろでてきましたね。

まあ、いまのところはスルーで大丈夫です。

kubectl getkubectl describe の使い方を手になじませてください。

sternを使ってログも確認してみましょう。

sternは tail -f ぽくログを流してくれます。

% stern introduction
+ introduction-web › web

Podができているログくらいしか確認できないですが、ログがでていることもわかりました。

kubectl logs introduction-web でもログは確認できます。(この場合なにもログはでてきません。ちなみに、 sternのように tail -f のようにログが流すことはできないです)

dashboardを使ってみる

kuberenetesのdashboardでも確認してみましょう。

kubernetesになれないうちはdashboardを使ってGUIで確認するとなにかと理解しやすいかと思います。

ただし本番環境でdashboard機能をonにする場合は気をつけましょう。

こんな事例もあります。

https://redlock.io/blog/cryptojacking-tesla

GKEを使っている場合は、クラスタを作る際にdashboar機能をoffにする機能があるので(どフォルトoff)、これを使ってoffにするとよいかと思います。

minikubeの場合は、 minikube dashboard コマンドを使います。

% minikube dashboard
🔌  Enabling dashboard ...
🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
🎉  Opening http://127.0.0.1:59459/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/ in your default browser...

minikube以外の場合はこちらを参照ください。

https://github.com/kubernetes/dashboard

dashboardはこんな感じになっているかと思います。

kubectl getkubectl describe で取得できる情報と同等の情報が確認できると思います。

Podを削除する

削除する方法は2つあります。

% kubectl delete pods introduction-web

or

% kubectl delete -f step1/pod.yaml

基本的にファイルを指定することで、もれなく削除できるので、ファイル指定をおすすめします。

念の為削除されているか確認もしましょう

% kubectl get pods
No resources found.

step1のマニフェストファイルを使うと、どこにもポートが空いていない状態なので、単純に上がっているだけのPodになります。

では、通信できるPodを作ってみましょう。

今度はstep2のディレクトリに含まれるマニフェストを使います。

# kubectxでクラスタの確認を忘れずに!
% kubectx
% kubectl apply -f step2/pod.yaml
pod/introduction-web created
% kubectl get pods
NAME               READY     STATUS    RESTARTS   AGE
introduction-web   1/1       Running   0          76s

クラスタ内で80番ポートが空いているPodが上がります。

このままでは、クラスタ外からはアクセスできません。

アクセスできるようにするには、サービスを作る方法もありますが、サービス経由の設定はあとにまわして、今回は一旦特定のPodと通信できるようにできるようにプロキシする方法でアクセスしてみます。

ローカルの8080番ポートでPodのアクセスするように設定して試してみます。

% kubectl port-forward introduction-web 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

http://localhost:8080/にアクセスしてみましょう。

ログも見てみましょう。

% stern introduction-web
+ introduction-web › web
introduction-web web 127.0.0.1 - - [05/Mar/2019:09:04:26 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.54.0" "-"
introduction-web web 127.0.0.1 - - [05/Mar/2019:09:04:39 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" "-"
introduction-web web 2019/03/05 09:04:39 [error] 7#7: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:8080", referrer: "http://localhost:8080/"
introduction-web web 127.0.0.1 - - [05/Mar/2019:09:04:39 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" "-"

無事アクセスできました!

Podが一つであればこれくらいでなんとかなりますが、現実はそんな訳ありません。

1Podは今までの1プロセスに当たるくらいの最小の単位です。

現実のアプリケーションを運用するには、複数の種類のPodまとめて管理する必要がでてきます。

それを実現するのがReplicaSetとDeploymentです。

次に行く前に立ち上げたPodをお掃除しておきましょう。

% kubectl delete -f step2/pod.yaml
pod "introduction-web" deleted
% kubectl get pods
No resources found.

ここからkubernetesぽくなってきます。

ちょっとここでkuberenetesの強みについて少しお話します。

今までのPodの立ち上げにおいて、書いているのはyamlのマニフェストファイルだけです。

この記述内容は何が必要かだけを記載しているいわゆる宣言的なコードになっているかと思います。kubernetesはこの宣言的なコードによる管理ができることでシステムの構築や管理を自動化しやすいようにしています。kubernetesの強みの一つはこうした宣言的なコードによる管理ができる点にあります。

もちろんkubernetesの強みはこれだけあるはずがありません。

ここで、ReplicaSetの話に戻します。

ReplicaSetはkubernetesの別の強みである、セルフヒーリングの機能を提供してくれます。

ReplicaSetは実行されているPodの数を保証する仕組みです。

ReplicaSetに定義されているPodのテンプレートを元にPodのレプリカ(複製)を指定された数に一致するように管理してくれます。

では実際に試してみましょう。

ReplicaSetの作成

ではReplicaSetを作ってみましょう

今回は3つのPodを上げます。

# クラスタの確認を忘れずに!
% kubectx
% kubectl apply -f step3/replica_set.yaml
replicaset.apps/intro-rs created
% kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
intro-rs-2wqvw   1/1       Running   0          27s
intro-rs-brllz   1/1       Running   0          27s
intro-rs-mwdjv   1/1       Running   0          27s
# 一個消してみる
% kubectl delete pod intro-rs-2wqvw
# すぐに3個になるようにPodができる
% kubectl get pods
NAME             READY     STATUS              RESTARTS   AGE
intro-rs-7zhxh   0/1       ContainerCreating   0          4s # <- New!!
intro-rs-brllz   1/1       Running             0          2m1s
intro-rs-mwdjv   1/1       Running             0          2m1s

Podが一時的に落ちてもすぐに新しいPodが出来上がる様子が伺えます。

さて、ReplicaSetがPodのセルフヒーリングを提供してくれたわけですが、これによって耐障害性が上がり万々歳とはなりません。。

ReplicaSetはあくまでPodのレプリカを作り指定された数ぶんのPodを立ち上げ続けられるように管理するだけです。

ReplicaSet単位でのアップデートは提供してくれません。

ReplicaSetやさらにはPodのアップデートを提供してくれる仕組みがDeploymentになります。

次に行く前に立ち上げたReplicaSetをお掃除しておきましょう。

% kubectl delete -f step3/replica_set.yaml
replicaset.apps "intro-rs" deleted
% kubectl get pods
No resources found.

DeploymentはReplicaSetとPodのアップデートを担当します。

DeploymentはReplocaSetを管理し、ReplicaSetがPodを作成したりします。

実際はこのDeploymentを使うことがほとんどで、ReplicaSetやPodを直接マニフェストで定義すること滅多にありません。

最初からこれを説明しろよ感がありますが、まあ、PodやReplicaSetを説明しないと分かりづらいと思ったのでここまで引っ張りました。

DeploymentはReplicaSetによって管理されるPodの数を保ちながら、Podをアップデートする仕組みを提供しています。

アップデートの方法の特徴してRollingUpdateがあります。

新しいPodを一定数つくりながら、古いPodをその分だけ削除していく、効率よくアップデートを行う仕組みです。

この仕組みを使ってBlue-Green-Deploymentも実現可能です。

Deploymentの作成

では実際にDeploymentを使ってみましょう。

# クラスタの確認を忘れずに!
% kubectx
% kubectl apply -f step4/deployment.yaml
deployment.apps/intro-deploy created
% kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
intro-deploy-78bd57c9c9-79trj   1/1       Running   0          12s
intro-deploy-78bd57c9c9-99mzp   1/1       Running   0          12s
intro-deploy-78bd57c9c9-l6sxw   1/1       Running   0          12s
% kubectl get deploy
NAME           READY     UP-TO-DATE   AVAILABLE   AGE
intro-deploy   3/3       3            3           19s
# 配布するimageのバージョンを変えてapplyしてみる
% kubectl apply -f step4/deployment.yaml
deployment.apps/intro-deploy configured
% kubectl get deploy
NAME           READY     UP-TO-DATE   AVAILABLE   AGE
intro-deploy   2/3       3            2           92s
# 入れ替え中
% kubectl get pods
NAME                            READY     STATUS              RESTARTS   AGE
intro-deploy-5d77889745-9scnz   0/1       ContainerCreating   0          5s
intro-deploy-5d77889745-jslth   0/1       ContainerCreating   0          5s
intro-deploy-5d77889745-lnznf   0/1       ContainerCreating   0          5s
intro-deploy-78bd57c9c9-79trj   1/1       Running             0          95s
intro-deploy-78bd57c9c9-99mzp   1/1       Running             0          95s
intro-deploy-78bd57c9c9-l6sxw   0/1       Terminating         0          95s
# 入れ替え完了
% kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
intro-deploy-5d77889745-9scnz   1/1       Running   0          73s
intro-deploy-5d77889745-jslth   1/1       Running   0          73s
intro-deploy-5d77889745-lnznf   1/1       Running   0          73s

kubectl set image deployment/intro-deploy nginx=nginx:1.15 のように kubectl set image でimageを入れかえることでアップデートをすることも可能です。

Deploymentまでで、だいたいPodの管理については終わりです。

お掃除しておきましょう。

% kubectl delete -f step4/deployment.yaml
deployment.apps "intro-deploy" deleted

さて、ここまででPodをどうやって一つにまとめて運用するかについてを見てきました。

Pod上でアプリケーションを動かしていても、それにアクセスできなければ、ユーザーは使えないですね。

kubernetesの内部的にはPod同士が通信できるようにネットワークができています。

通信自体は可能なので、自分でPod間のトラフィックを制御するなにかを作って制御することは可能です。

ですが、そんなことをしなくても、kubernetesがちゃんと用意してくれています。

それがServiceです。

Serviceは負荷分散とかいろいろありますが、要はLabelのセレクタを作る仕組みが本質だと思います。

PodのLabelを指定してひとまとめにして、そのLabelがついているPodに対してトラフィックを流すのがServiceの役割になります。

このトラフィックを流すという仕組みが負荷分散の仕組みになり、この負荷分散の仕組はkubernetesは複数用意してくれています。

またトラフィックの制御という観点から、PodのProbeと連携して正常な状態のPodに対してトラフィックを流すという仕組みも備えています。

では、Serviceを実際に触ってみましょう。

ClusterIP typeでのServiceの作成

ServiceとDeploymentは密接に関わっていることが多いので、自分の場合は1ファイルにまとめて管理することが多いです。

まずはClusterIP typeでServiceをつくって試してみましょう。

ClusterIPはServiceの中では一番プリミティブなServiceといっていいでしょう。

ClusterIPはクラスタ内の内部IPでServiceを作り、クラスタ内からのみアクセス可能です。

では試してみましょう。

# クラスタの確認を忘れずに!
% kubectx
% kubectl apply -f step5/service_deployment.yaml
service/intro-api created
deployment.apps/intro-deploy created
% kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
intro-deploy-78bd57c9c9-hsrsx   1/1       Running   0          4m45s
intro-deploy-78bd57c9c9-sg9cn   1/1       Running   0          4m45s
intro-deploy-78bd57c9c9-whzdc   1/1       Running   0          4m45s
% kubectl get deploy
NAME           READY     UP-TO-DATE   AVAILABLE   AGE
intro-deploy   3/3       3            3           4m48s
% kubectl get service
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
intro-api    ClusterIP   10.104.231.157   <none>        8080/TCP   4m51s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    23h

もちろんこれではクラスタ外からはアクセスできません、、、

クラスタの中のPodに一つに入ってみて試して見ましょう。

% kubectl exec -it intro-deploy-78bd57c9c9-hsrsx -- /bin/bash
root@intro-deploy-78bd57c9c9-hsrsx:/# apt-get update
root@intro-deploy-78bd57c9c9-hsrsx:/# apt-get install -y curl
root@intro-deploy-78bd57c9c9-hsrsx:/# curl 10.104.231.157:8080 #何回も叩いてみる
# Serviceオブジェクトが作られると、kubernetes内部のDNSに{:service name}.{namespace名}.svc.cluster.localのドメインが発行される
root@intro-deploy-78bd57c9c9-hsrsx:/# curl intro-api.default.svc.cluster.local:8080

そうするとログがこんな感じででます。

セレクタで指定されたPodに分散している様子が見えます。(curlを叩いてるPodにはアクセスは行かない模様)

作ったServiceは消しておきましょう。

% kubectl delete -f step5/service_deployment.yaml

NodePortを使って外部に公開されているServiceを作る

NodePortはクラスタ上の全てのノード上の指定したポートに到達したトラフィックをPodに流すことができます。

なので、ノードのIPアドレスが公開されていれば、クラスタ外からのトラフィックもPodへ流すことができます。

では早速NodePortのServiceを作ってみましょう。

# クラスタの確認を忘れずに!
% kubectx
% kubectl apply -f step6/service_deployment.yaml
service/intro-api created
deployment.apps/intro-deploy created
% kubectl get pod
NAME                            READY     STATUS    RESTARTS   AGE
intro-deploy-78bd57c9c9-gh5jg   1/1       Running   0          46s
intro-deploy-78bd57c9c9-gngdf   1/1       Running   0          46s
intro-deploy-78bd57c9c9-qcstn   1/1       Running   0          46s
% kubectl get deploy
NAME           READY     UP-TO-DATE   AVAILABLE   AGE
intro-deploy   3/3       3            3           49s
% kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
intro-api    NodePort    10.104.75.252   <none>        8080:30080/TCP   53s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          24h

こんな感じで作られるかと思います。

ではminikubeのクラスタのノードの情報がわかればアクセスできますね!

最初に書いた通りminikubeはノードが1つなので、ノードのIPアドレスも1つです。

確認方法は下記コマンドでできます。

% minikube ip
192.168.99.100

おおお、これがminikubeではノードのIPアドレスと一致します。

ではこのIPアドレスに向けてアクセスしてみましょう。

sternでログを出すの忘れずに!

% curl 192.168.99.100:30080

sternのログはこんな感じになります!

リクエストが来ていることがわかると思います。

これでServiceに関しては入門としては十分かと思います!

は作ったオブジェクトを消しておきましょう。

% kubectl delete -f step6/service_deployment.yaml 
service "intro-api" deleted
deployment.apps "intro-deploy" deleted

Serviceの役割を整理するとこんな感じです。(Kubernetes完全ガイド p194より)

昨今のHTTPサーバーを運用するにはServiceでは不足している部分があります。

そこでL7ロードバランシングを行うIngressがあります。

IngressはServiceとも連携して下記のようなことできます(実装次第でいろいろできます)。

どれを使うべきかは一番の悩みごとだと思います。

https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

ServiceのClusterIPとNodePortは前で説明した通りです。

LoadBalancerは、外部にServiceを公開するためには一般的な方法です。

LoadBalancerは基本的にはクラウドプロバイダーが提供しているしているロードバランサーが使われます。

GKEだとTCPのロードバランサーが作成されます。

L4ロードバランサーなので、フィルターやルーティングなどはないですね。

さらにServiceごとにロードバランサーができるので、お金かかります。

IngressはServiceのタイプではないです。

Ingressは複数のServiceをまとめてルーティングしてくれます。

GKEのingressはHTTP(S)ロードバランサーを使います。

このロードバランサーはvhost的な感じで、サブドメインベースのルーティングをしてくれます。

Ingressは一つのIPで複数のSerivceを公開したりするにはとても便利です。

他にもSSLの終端処理などのいろいろ便利な機能はクラウドプロバイダーごとに提供されているので、確認して使いましょう。

今回は入門ということでこれ以上は深掘りしないのですが、ぜひ本などを読んで理解を深めてください。

kubernetesの入門ということで、ざっくりとkubernetesについて紹介してきました。

kubernetesは強力なツールです。

これを機会にさらに深掘りをしていってみてください。

kubernetesを使って本番運用を行うにはまだまだやらなければならないこともあります。

他にもネタがれば@spring_mtまでお知らせください!

Web上の記事

GKE/Kubernetes でなぜ Pod と通信できるのか

2019/03/05より前の話

多分GCP使っている人がほとんどだと思うので、gcloud経由でインストールしたくなることでしょう。

gcloud経由でインストールすると、なんと、v1.10.7のkubectlがふってきます。古すぎる。

というわけで、brew経由でインストールします。(v1.10系を使っててCronJob周りでなにか踏んだ気もするけど、忘れた。。。)

インストール方法

% brew install kubernetes-cli
# これだとgcloudでもkubectlインストールすると、読み込みのタイミングとあでgcloudのほうが優先されることがある
% vim ~/.zshrc
# これを書いておいてもいいかも
alias kubectl="/usr/local/Cellar/kubernetes-cli/{バージョン}/bin/kubectl"

brewとgcloudの両方でインストールしていると、

gcloud components update

するこんなWarningがでてうざいです。。。

WARNING:   There are older versions of Google Cloud Platform tools on your system PATH.
  Please remove the following to avoid accidentally invoking these old tools:

  /Users/foo/go_appengine/endpointscfg.py
/Users/foo/go_appengine/dev_appserver.py
/usr/local/Cellar/kubernetes-cli/1.11.2/bin/kubectl