Kubernetes 是当前最流行,功能最齐全的 Container Orchestration 平台,如果 Docker 是集装箱,那么 Kubernetes 就是一个调度,交通,管理等一应俱全的码头。

本文希望能帮助读者不打针,不吃药,不用安装 minikube,无痛地在Mac上愉快的与 Kubernetes 玩耍。

翻译改编自 Romin Irani 的 https://rominirani.com/tutorial-getting-started-with-kubernetes-with-docker-on-mac-7f58467203fd

预备工作与环境

  1. MacOS High Sierra
  2. 安装 Docker Edge 版本(写此文时版本号是 18.02.0-ce-mac53)
  3. 我使用的命令行是 oh-my-zsh, 在 ~/.zshrc 的 plugins 部分添加 kubectl 和 docker,方便 命令自动补全。
  4. 对 Kubernetes 的基本概念 pod,deployment,service namespace 有基本了解(不了解也没问题,文末有链接)

在 Docker Edge 配置里,”Enable Kubernetes” -> “Apply” -> “Install”, 就会安装好所需的库,在后台跑起一个默认的 Kubernetes cluster。

看到 “Docker is running”, “Kubernetes is running” 两个绿点,万事俱备了。

检查一下

可以在命令行环境里检查一下安装。Server 和 Client 的版本可能会不一样,如果安装了 gcloud sdk 里的 Kubernetes,current-context 也会不一样(取决于你的 server 在哪儿)

1
2
3
4
5
6
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.6", GitCommit:"6260bb08c46c31eea6cb538b34a9ceb3e406689c", GitTreeState:"clean", BuildDate:"2017-12-21T06:34:11Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.2", GitCommit:"5fa2db2bd46ac79e5e00a4e6ed24191080aa463b", GitTreeState:"clean", BuildDate:"2018-01-18T09:42:01Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl config current-context
docker-for-desktop

再看看 cluster,里面目前只有一个node。

1
2
3
4
5
6
7
8
9
$ kubectl cluster-info
Kubernetes master is running at https://localhost:6443
KubeDNS is running at https://localhost:6443/api/v1/namespaces/kube-system/services/kube-dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-for-desktop Ready master 9d v1.9.2

安装 Kubernetes Dashboard

下一步,给刚才的 Kubernetes cluster 安装一个 Dashboard,安装的过程也正是 Kubernetes 创建 deployment/services 的过程。Dashboard 的 kubernetes-dashboard.yaml 文件也是一个很好的学习例子,可以读一读。

1
$ kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
secret “kubernetes-dashboard-certs” created
serviceaccount “kubernetes-dashboard” created
role “kubernetes-dashboard-minimal” created
rolebinding “kubernetes-dashboard-minimal” created
deployment “kubernetes-dashboard” created
service “kubernetes-dashboard” created

在 kubernetes-dashboard.yaml 中,我们注意到 metadata 部分的 namespace 是 kube-system,来看一下 kube-system 下有哪些 pods。

1
2
3
4
5
6
7
8
9
$ kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
etcd-docker-for-desktop 1/1 Running 0 9d
kube-apiserver-docker-for-desktop 1/1 Running 0 9d
kube-controller-manager-docker-for-desktop 1/1 Running 0 9d
kube-dns-6f4fd4bdf-6cmxv 3/3 Running 0 9d
kube-proxy-fmpgn 1/1 Running 0 9d
kube-scheduler-docker-for-desktop 1/1 Running 0 9d
kubernetes-dashboard-845747bdd4-9fm69 1/1 Running 1 9d

和 Kuberenetes architecture 图一一对应:
Kubernetes architecture diagram on Wikipedia
每个 pod 一开始状态都是 ContainerCreating,小等几秒钟,就成功变成 Running。

成功运行后,我们开启 proxy server 从本地访问 Kubernetes API server:

1
2
$ kubectl proxy
Starting to serve on 127.0.0.1:8001

浏览器里访问 http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy

选 SKIP,就能看到 Dashboard 了:

在侧边栏点 Nodes,就能看到之前我们显示的 “docker-for-desktop”

注: 原文中用的是 kubectl port-forward 8443 端口,所以截图中地址栏都是 localhost:8443。原文使用的命令是:

1
$ kubectl port-forward kubernetes-dashboard-845747bdd4-9fm69 8443:8443 — namespace=kube-system
Forwarding from 127.0.0.1:8443 -> 8443

但通过搜索,貌似 kubectl proxy 的方法更为简洁和主流。

Nginx 示例

跑一个 Nginx container 来看看整个过程:

1
$ kubectl run hello-nginx --image=nginx --port=80
deployment “hello-nginx” created

这行命令创建了一个 deployment,该 deployment 会创建一个 pod,pod 负责运行 container:

1
2
3
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-nginx-556b7bf96-2xw8f 0/1 ContainerCreating 0 12s

等几秒钟,再看 Dashboard 的 Deployments:

当 Pods 由 0/1 变成 1/1 的时候, 刚才的命令结果也变成 Running 了

1
2
3
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-nginx-556b7bf96-2xw8f 1/1 Running 0 4m

点侧边栏的 Pod,再点我们刚创建的 Pod “hello-nginx-556b7bf96-2xw8f”,能看到这个 pod 的细节:

这里能看到给定的默认 labels,和分配的 IP (来此 docker-for-desktop Node)。

右上角的这几个按钮也是我们经常用的功能,EXEC 打开一个浏览器中的 shell ssh 进入 pod,LOGS 查看日志。

同样的,我们也可以通过 kubectl describe node/pod 命令来查看信息,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ kubectl describe pod hello-nginx-556b7bf96-2xw8f
Name: hello-nginx-556b7bf96-2xw8f
Namespace: default
Node: docker-for-desktop/192.168.65.3
Start Time: Tue, 20 Feb 2018 22:34:19 -0800
Labels: pod-template-hash=112636952
run=hello-nginx
Annotations: <none>
Status: Running
IP: 10.1.0.54
Controlled By: ReplicaSet/hello-nginx-556b7bf96
Containers:
hello-nginx:
Container ID: docker://5c5f68348ed8a204dcce089a21d71e36cbfdb6c7955fd28a7f148d5f44662861
Image: nginx
Image ID: docker-pullable://nginx@sha256:65f0ed5362b39c16cad51f27217c12ac9dd0db4dc47b0197969885f05198d4d8
Port: 80/TCP
State: Running
Started: Tue, 20 Feb 2018 22:34:37 -0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-m99ws (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-m99ws:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-m99ws
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 15m default-scheduler Successfully assigned hello-nginx-556b7bf96-2xw8f to docker-for-desktop
Normal SuccessfulMountVolume 15m kubelet, docker-for-desktop MountVolume.SetUp succeeded for volume "default-token-m99ws"
Normal Pulling 15m kubelet, docker-for-desktop pulling image "nginx"
Normal Pulled 15m kubelet, docker-for-desktop Successfully pulled image "nginx"
Normal Created 15m kubelet, docker-for-desktop Created container
Normal Started 15m kubelet, docker-for-desktop Started container

通过外部访问

之前我们提到过,可以用 port-forward pod 来实现外部访问:

1
2
3
4
5
6
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-nginx-556b7bf96-2xw8f 1/1 Running 0 46m

$ kubectl port-forward hello-nginx-556b7bf96-2xw8f 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080

但这只暴露特定 pod 的端口,在实际中并不实用。

所以我们换一个方法,把 deployment 暴露成一个服务,供外部访问:

1
2
3
4
5
6
7
8
9
10
11
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
hello-nginx 1 1 1 1 18m

$ kubectl expose deployment hello-nginx --type=NodePort --name=hello-nginx-service
service "hello-nginx-service" exposed

$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-nginx-service NodePort 10.109.0.203 <none> 80:30351/TCP 30s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d

现在,浏览器里就能访问 localhost:30351 了。

注意到真正的 EXTERNAL-IP 都是 none,这是因为 Mac 安装的 Docker cluster 并没有 LoadBalancer, 只有云服务上才有。