本教程的目标是将一个简单的 Go 程序转换为在 Kubernetes 上运行的程序。

本教程将向你展示如何获取在计算机上开发的代码,将其转换为 Docker 容器映像,然后在 Minikube 上运行该映像。

Minikube 提供了一种在本地计算机上免费运行 Kubernetes 的简单方法。


目标

  • 运行一个 Go 应用程序;
  • 部署程序到 minikube;
  • 查看程序日志;
  • 更新程序镜像;

准备工作

使用 macOS 的用户,可以用 Homebrew 来安装 minikube。

注意:如果在将计算机更新到 macOS 10.13 后运行 brew update 时看到以下 Homebrew 错误:

1
2
3
Error: /usr/local is not writable. You should change the ownership
and permissions of /usr/local back to your user account:
sudo chown -R $(whoami) /usr/local

你可以按照以上提示,也可以通过重装 Homebrew 来解决这个问题:

1
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • 基于 Go 开发的简单程序;

  • 安装 Docker,在 macOS 上,我们推荐 Docker for Mac.

创建一个 minikube 集群

本教程使用 Minikube 创建本地群集,本教程还假设你在 macOS 上使用 Docker for Mac。

如果你使用的是 Linux 之类的其他平台,或使用 VirtualBox 而不是 Docker for Mac,则安装 Minikube 的说明可能略有不同,有关 Minikube 的一般安装说明请参阅 Minikube 安装指南

使用 Homebrew 安装最新的 Minikube 版本:

1
$ brew cask install minikube

安装 HyperKit 驱动,按照这里所描述的 Minikube driver installation guide

使用 Homebrew 下载 kubectl 命令行工具,你可以使用该工具与 Kubernetes 集群进行交互:

1
$ brew install kubernetes-cli

确定你是否可以在没有代理的情况下直接访问 https://cloud.google.com/container-registry/ 等网站,方法是打开新终端并使用:

1
$ curl --proxy "" https://cloud.google.com/container-registry/

确保已启动 Docker 守护程序,你可以使用以下命令确定 docker 是否正在运行:

1
$ docker images

如果不需要代理,请启动 Minikube 集群:

1
$ minikube start --vm-driver=hyperkit

如果需要代理服务器,请使用以下方法启动具有代理设置的 Minikube 集群:

1
$ minikube start --vm-driver=hyperkit --docker-env HTTP_PROXY=http://your-http-proxy-host:your-http-proxy-port  --docker-env HTTPS_PROXY=http(s)://your-https-proxy-host:your-https-proxy-port

或者根据文档:,在 ~/.minikube/machines/minikube/config.json 修改 HostOptions.EngineOptions.Env 对应的 HTTP_PROXY 和 HTTPS_PROXY:

1
2
3
4
5
"Env": [
        "HTTP_PROXY=http://192.168.1.8:1087",
        "HTTPS_PROXY=http://192.168.1.8:1087",
        "NO_PROXY=192.168.99.0/24"
       ],

然后再重新执行:

1
$ minikube start --vm-driver=hyperkit --bootstrapper=localkube

如果执行之后,长时间没有响应,可以 Ctrl-C 中断,然后再重试,如果还是不行那么你可以参照以下文章中所提到的方法去调试,应该就能解决了。

如果出现状态为 ContainerCreating 的,一般都是因为网络问题导致无法正常拉取镜像导致的。

1
2
3
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                          READY     STATUS              RESTARTS   AGE
kube-system   kube-addon-manager-minikube   0/1       ContainerCreating   0          16m

Kubernetes 墙内使用技巧

如果你遇到墙的问题了,可能会有以下表象:

  • minikube dashboard 长时间无响应;
  • kubectl run xxx 长时间处于 ContainerCreating 等;

--vm-driver=hyperkit 标志指定你使用的是 Docker for Mac, 默认的 VM 驱动程序是 VirtualBox,需要你额外安装它。

现在设置 Minikube context, context 决定了哪个集群的 kubectl 与之交互。 你可以在 ~/.kube/config 文件中查看所有可用的 context。

1
2
$ kubectl config use-context minikube
Switched to context "minikube".

验证 kubectl 是否与已配置的集群通信:

1
2
3
4
$ kubectl cluster-info
Kubernetes master is running at https://192.168.64.4:8443

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

在浏览器中打开 Kubernetes dashboard:

1
$ minikube dashboard

创建你的 Go 应用程序

下一步是编写程序,将代码保存在名为 minikube-hello-go 的文件夹中,文件名为 server.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Welcome to minikube server!")
	})

	http.ListenAndServe(":8080", nil)
}

运行你的程序:

1
$ go run server.go

或者

1
2
$ go build
$ ./minikube-hello-go

你可以通过浏览器访问 http://localhost:8080/ 看到 Welcome to minikube server! 消息。

通过 Ctrl-C 来结束运行的 Go 服务。

下一步是将你的应用程序打包到 Docker 容器中。

创建一个 Docker 容器镜像

minikube-hello-go 文件夹中创建一个文件 Dockerfile。 用 Dockerfile 文件来描述你想构建的这个镜像。你可以基于一个已经存在的镜像来构建一个 Docker 容器镜像,在这个教程中,我们依赖 golang:1.10.3 镜像。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM golang:1.10.3

EXPOSE 8080

WORKDIR /go/src/server
COPY . .

RUN go install -v ./...

CMD [ "server" ]

这个 Docker 镜像的配方从 Docker 官方仓库中拉取 golang:1.10.3 镜像,公开端口 8080,将当前文件复制到镜像中的 /go/src/server/ 文件夹中,然后运行 go install 进行安装,然后启动服务器。

因为本教程使用 Minikube ,而不是将 Docker 镜像推送到仓库,所以你可以使用与 Minikube VM 相同的 Docker 主机来构建镜像,以便自动显示镜像,为此,请确保使用 Minikube Docker 守护程序:

1
$ eval $(minikube docker-env)

注意:当你稍候不再希望使用 Minikube 主机时,可以通过运行 eval $(minikube docker-env -u) 撤消此更改。

使用 Minikube Docker 守护进程构建 Docker 镜像(注意最后的 .):

1
$ docker build -t hello-go:v1 .
1
2
3
$ docker images
REPOSITORY                                TAG                 IMAGE ID            CREATED             SIZE
hello-go                                  v1                  f9a6fbe2045d        13 minutes ago        801MB

现在,Minikube VM 可以运行你构建的映像。

创建一个 Deployment

Kubernetes Pod 是一个由一个或多个容器组成的组合,用于管理和联网。 本教程中的 Pod 只有一个 Container。 Kubernetes Deployment 会检查 Pod 的运行状况,并在 Pod 终止时重新启动 Pod 的容器。 Deployments 是管理 Pod 的创建和扩容的推荐方法。

使用 kubectl run 命令创建管理 Pod 的 Deployment。 Pod 根据你的 hello-go:v1 Docker 镜像运行一个 Container。 将 --image-pull-policy 标志设置为 Never 以永远使用本地映像,而不是从 Docker 仓库中提取它(因为你没有将其推送到那里):

1
$ kubectl run hello-go --image=hello-go:v1 --port=8080 --image-pull-policy=Never

查看 Deployment:

1
2
3
$ kubectl get deployments
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-go   1         1         1            1           40s

查看 Pod:

1
2
3
$ kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
hello-go-648b9c5f5f-zdlnc   1/1       Running   0          1m

查看集群事件:

1
2
3
4
5
6
7
8
9
$ kubectl get events
LAST SEEN   FIRST SEEN   COUNT     NAME                                           KIND         SUBOBJECT                     TYPE      REASON                  SOURCE                  MESSAGE
2m          2m           1         hello-go-648b9c5f5f-zdlnc.15468316fd9704a1     Pod                                        Normal    Scheduled               default-scheduler       Successfully assigned hello-go-648b9c5f5f-zdlnc to minikube
2m          2m           1         hello-go-648b9c5f5f.15468316fd36e46d           ReplicaSet                                 Normal    SuccessfulCreate        replicaset-controller   Created pod: hello-go-648b9c5f5f-zdlnc
2m          2m           1         hello-go.15468316fc5d32bd                      Deployment                                 Normal    ScalingReplicaSet       deployment-controller   Scaled up replica set hello-go-648b9c5f5f to 1
2m          2m           1         hello-go-648b9c5f5f-zdlnc.1546831710495581     Pod                                        Normal    SuccessfulMountVolume   kubelet, minikube       MountVolume.SetUp succeeded for volume "default-token-5nndf"
2m          2m           1         hello-go-648b9c5f5f-zdlnc.1546831734d69b39     Pod          spec.containers{hello-go}     Normal    Pulled                  kubelet, minikube       Container image "hello-go:v1" already present on machine
2m          2m           1         hello-go-648b9c5f5f-zdlnc.1546831737b6f001     Pod          spec.containers{hello-go}     Normal    Created                 kubelet, minikube       Created container
2m          2m           1         hello-go-648b9c5f5f-zdlnc.154683173df5a719     Pod          spec.containers{hello-go}     Normal    Started                 kubelet, minikube       Started container

查看kubectl配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /Users/xxxx/.minikube/ca.crt
    server: https://192.168.64.3:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /Users/xxxx/.minikube/client.crt
    client-key: /Users/xxxx/.minikube/client.key

有关 kubectl 命令的更多信息,请参阅 kubectl 概述

创建一个 Service

默认情况下,Pod 只能通过 Kubernetes 集群中的内部 IP 地址访问。要使 Kubernetes 虚拟网络外部可以访问 hello-go Container,你必须将 Pod 公开为 Kubernetes Service

在你的开发机器上,你可以使用 kubectl expose 命令将 Pod 公开到公共网络:

1
$ kubectl expose deployment hello-go --type=LoadBalancer

查看你刚刚创建的 Service:

1
2
3
4
$ kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
hello-go     LoadBalancer   10.107.111.2   <pending>     8080:32056/TCP   1s
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          12h

--type=LoadBalancer 标志表示你要在集群外部公开服务。在支持负载均衡的云提供商上,将配置外部 IP 地址以访问服务。 在 Minikube 上,LoadBalancer 类型通过 minikube service 命令使服务可访问。

1
2
$ minikube service hello-go
Opening kubernetes service default/hello-go in default browser...

这将使用为你的应用程序提供服务的本地 IP 地址自动打开浏览器窗口,并显示 Welcome to minikube server! 消息。

假设你已使用浏览器或 curl 向新 Web 服务发送请求,你现在应该能够看到一些日志:

1
$ kubectl logs -f <pod-name>

更新你的应用程序

编辑 server.go 文件以返回新消息:

1
	fmt.Fprintf(w, "Welcome to minikube go server v2!")

构建新版本的镜像(注意最后的 .):

1
$ docker build -t hello-go:v2 .
1
2
3
4
$ docker images
REPOSITORY                                TAG                 IMAGE ID            CREATED             SIZE
hello-go                                  v2                  f37ea6b3ca9b        15 hours ago        801MB
hello-go                                  v1                  41e419790e7d        15 hours ago        801MB

更新 Deployment 的镜像:

1
$ kubectl set image deployment/hello-go hello-go=hello-go:v2

或者直接修改 deployment/hello-go 的文件:

1
$ kubectl edit deployment/hello-go

使用浏览器或者 curl 查看运行结果,发现显示内容也已经更新了。

或者你也可以使用 minikube service hello-node ,他会自动打开浏览器并加载网址显示信息。

启用插件

Minikube 有一组内置插件,可以在本地 Kubernetes 环境中启用,禁用和打开。

首先列出当前支持的插件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ minikube addons list
- addon-manager: enabled
- coredns: disabled
- dashboard: enabled
- default-storageclass: enabled
- efk: disabled
- freshpod: disabled
- heapster: enabled
- ingress: disabled
- kube-dns: enabled
- metrics-server: disabled
- nvidia-driver-installer: disabled
- nvidia-gpu-device-plugin: disabled
- registry: disabled
- registry-creds: disabled
- storage-provisioner: enabled

必须运行 Minikube 才能使这些命令生效。 要启用 heapster 插件,例如:

1
2
$ minikube addons enable heapster
heapster was successfully enabled

查看刚刚创建的 Pod 和 Service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ kubectl get po,svc -n kube-system
NAME                                        READY     STATUS    RESTARTS   AGE
pod/heapster-gb7jj                          1/1       Running   0          10h
pod/influxdb-grafana-m5ltj                  2/2       Running   0          10h
pod/kube-addon-manager-minikube             1/1       Running   0          13h
pod/kube-dns-6dcb57bcc8-g6q9w               3/3       Running   0          10h
pod/kubernetes-dashboard-5498ccf677-p92sz   1/1       Running   0          10h
pod/storage-provisioner                     1/1       Running   0          10h

NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
service/heapster               ClusterIP   10.101.28.158   <none>        80/TCP              10h
service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       10h
service/kubernetes-dashboard   NodePort    10.107.13.248   <none>        80:30000/TCP        10h
service/monitoring-grafana     NodePort    10.110.220.73   <none>        80:30002/TCP        10h
service/monitoring-influxdb    ClusterIP   10.107.27.163   <none>        8083/TCP,8086/TCP   10h

打开端点以在浏览器中与 heapster 进行交互:

1
2
$ minikube addons open heapster
Opening kubernetes service kube-system/monitoring-grafana in default browser...

可能你打开查看监控是没有数据的,你可以模拟访问请求,然后再查看监控的 dashboard。

1
$ while sleep 0.1; do curl http://192.168.64.3:32056/; done

如果你要关闭 heapster:

1
2
$ minikube addons disable heapster
heapster was successfully disabled

heapster 的完全关闭需要等一段时间。

清理

现在,你可以清理在群集中创建的资源:

1
2
3
4
$ kubectl delete service hello-go
service "hello-go" deleted
$ kubectl delete deployment hello-go
deployment.extensions "hello-go" deleted

(可选)强制删除创建的 Docker 镜像:

1
2
3
4
5
6
7
8
$ docker rmi hello-go:v1 hello-go:v2 -f
docker rmi hello-go:v1 hello-go:v2 -f
Untagged: hello-go:v1
Deleted: sha256:41e419790e7da55ac34456c09e921f2d9dfa336aab6a47a67079746a8428ae4e
...
Untagged: hello-go:v2
Deleted: sha256:f37ea6b3ca9bb06496751be9e93e50456a49363707606625b85b3811a663c364
...

(可选)停止 Minikube VM:

1
2
$ minikube stop
$ eval $(minikube docker-env -u)

eval $(minikube docker-env -u) 这个非常重要,如果你没有执行这个的话,你本地的 docker 运行会执行到 minikube 上的 vm ;

(可选)删除 Minikube VM:

1
$ minikube delete

其他问题

  1. Error restarting cluster: restarting kube-proxy: waiting for kube-proxy to be up for configmap update: timed out waiting for the condition

这个原因很可能是有 minikube 旧版本导致的,你可以执行 minikube delete,然后再执行 minikube start

  1. Error starting host: Error creating new host: Driver "hyperkit" not found. Do you have the plugin binary "docker-machine-driver-hyperkit" accessible in your PATH?

从 RELEASE 上下载,然后将其移动到 /usr/local/bin/ 目录下;

还需要执行以下命令,给予权限:

1
$ sudo chown root:wheel /usr/local/bin/docker-machine-driver-hyperkit && sudo chmod u+s /usr/local/bin/docker-machine-driver-hyperkit

然后再运行:minikube start --vm-driver=hyperkit --bootstrapper=localkube --docker-env HTTP_PROXY=http://127.0.0.1:1087 --docker-env HTTPS_PROXY=http://127.0.0.1:1087 即可(--docker-env 可要可不要)。

参考资料

  1. Hello Minikube
  2. Kubernetes 墙内使用技巧

茶歇驿站

一个可以让你停下来看一看,在茶歇之余给你帮助的小站,这里的内容主要是后端技术,个人管理,团队管理,以及其他个人杂想。