想学习k8s但没有环境?使用minikube轻松搭建一个

Kubernetes(常简称为K8s)是用于自动部署、扩展和管理“容器化(containerized)应用程序”的开源系统。该系统由Google设计并捐赠给Cloud Native Computing Foundation(今属Linux基金会)来使用。

它旨在提供“跨主机集群的自动部署、扩展以及运行应用程序容器的平台”。 它支持一系列容器工具,包括Docker等。

Kubernetes(在希腊语意为“舵手”或“驾驶员”)由Joe Beda、Brendan Burns和Craig McLuckie创立,并由其他谷歌工程师,包括Brian Grant和Tim Hockin等进行加盟创作,并由谷歌在2014年首次对外宣布 。 该系统的开发和设计都深受谷歌的Borg系统的影响,其许多顶级贡献者之前也是Borg系统的开发者。在谷歌内部,Kubernetes的原始代号曾经是Seven,即星际迷航中的Borg(博格人)。Kubernetes标识中舵轮有七个轮辐就是对该项目代号的致意。

Kuberbetes一直是IT行业炽手可热的技术,很多同学都想学习它,但是刚想上手遇到了一个麻烦,需要搭建一个k8s的集群。虽然云服务提供商比如阿里云、百度云都提供了k8s集群的服务,但是还需要花一笔钱去购买服务和节点。如何在自己的机器上搭建一个k8s集群学习呢?本文给你介绍一种使用minikube搭建一个k8s测试集群的方法。

安装

minikube 主要基于运行一个单节点 Kubernetes 集群,以便支持在本地机器上的 VM 内进行开发。它支持虚拟机驱动程序,如 VirtualBox、HyperV、KVM2。由于 Minikube 是 Kubernetes 体系中相对成熟的解决方案,支持的功能列表非常令人印象深刻。这些功能是负载均衡器、多集群、节点端口、持久卷、入口、仪表板或容器运行时。

基于 Minikube 开源工具,使得开发、运维人员及 DevOps 工程师能够快速在本地搭建 Kubernetes 单节点集群环境,毕竟,Minikube 对软硬件资源没有太高的要求,方便技术人员学习与实践,以及进行日常的项目开发。

运行minikube,你至少需要:

  • 2 CPU或者更多
  • 2GB 内存
  • 20GB 磁盘空间
  • 互联网
  • 容器或者虚机管理器如: Docker, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox, or VMware Fusion/Workstation

我使用的MacBook Pro M1的苹果本,并且机器上已经安装Docker,所以这些需求都满足。如果你是Linux或者Windows,也可以安装,只不过你需要下载相应的安装程序。
本文以我的环境为例进行介绍。

对于MacOS,你可以有两种方式进行安装。
一种是手工安装方式:

1
2
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64
sudo install minikube-darwin-amd64 /usr/local/bin/minikube

如果你因为一些原因没有办法下载这个文件,也可以到github网站上下载二进制文件,放到/usr/local/bin/minikube中。

第二种方式就是使用brew命令安装:

1
2
3
4
5
brew install minikube
// 查看minikube安装在哪里
➜ ~ which minikube
/usr/local/bin/minikube

启动

接下来就是启动minikube。

执行下面的命令就可以启动:

1
minikube start

第一次启动需要下载k8s依赖的各种镜像,可能因为墙的原因,一些镜像会拉不下来安装失败。

我采用的方法是从阿里云拉取镜像,再重新打tag到对应的k8s依赖的镜像,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.22.3
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.22.3 k8s.gcr.io/kube-proxy:v1.22.3
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.4
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.4 k8s.gcr.io/coredns/coredns:v1.8.4
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.22.3
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.22.3 k8s.gcr.io/kube-scheduler:v1.22.3
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5 gcr.io/k8s-minikube/storage-provisioner:v5
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.22.3
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.22.3 k8s.gcr.io/kube-apiserver:v1.22.3
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.0-0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.0-0 k8s.gcr.io/etcd:3.5.0-0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.22.3
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.22.3 k8s.gcr.io/kube-controller-manager:v1.22.3
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.5
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.5 k8s.gcr.io/pause:3.5

手工把这些镜像拉到本地后,再执行minikube start就顺利启动成功了。

我还在网上看到另外一种启动方式:

1
minikube start --image-mirror-country='cn'

我觉得这是一种更简捷的方式,不过我已经安装好了,所以这种方式我没有试过,你可以试试。

minikube命令提供了非常多的配置参数,比如:

--driver= 从1.5.0版本开始,Minikube缺省使用系统优选的驱动来创建Kubernetes本地环境,比如您已经安装过Docker环境,minikube 将使用 docker 驱动
--cpus=2: 为minikube虚拟机分配CPU核数
--memory=4096mb: 为minikube虚拟机分配内存数
--registry-mirror=
为 Docker daemon 配置镜像加速,比如阿里云镜像服务
--kubernetes-version=***: minikube 虚拟机将使用的 kubernetes 版本

现在,你就可以打开Kubernetes控制台了: minikube dashboard

现在,一个本地测试用的k8s集群就搭建好了。

如果你暂时不用,可以调用minikube stop暂停,等需要的时候再启动。

插件

你现在可以直接使用kubectl命令快速创建、更新和删除Kubernetes 对象。你可以自由自在的在自己的集群中学习、测试k8s的技术了。

minikube也提供了很多插件,比如istio、ingress、helm-tiller等等。随着你对k8s的掌握程度,可以逐步的安装测试这些扩展的功能。
插件列表如下:

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
➜ ~ minikube addons list
|-----------------------------|----------|--------------|-----------------------|
| ADDON NAME | PROFILE | STATUS | MAINTAINER |
|-----------------------------|----------|--------------|-----------------------|
| ambassador | minikube | disabled | unknown (third-party) |
| auto-pause | minikube | disabled | google |
| csi-hostpath-driver | minikube | disabled | kubernetes |
| dashboard | minikube | enabled ✅ | kubernetes |
| default-storageclass | minikube | enabled ✅ | kubernetes |
| efk | minikube | disabled | unknown (third-party) |
| freshpod | minikube | disabled | google |
| gcp-auth | minikube | disabled | google |
| gvisor | minikube | disabled | google |
| helm-tiller | minikube | disabled | unknown (third-party) |
| ingress | minikube | disabled | unknown (third-party) |
| ingress-dns | minikube | disabled | unknown (third-party) |
| istio | minikube | disabled | unknown (third-party) |
| istio-provisioner | minikube | disabled | unknown (third-party) |
| kubevirt | minikube | disabled | unknown (third-party) |
| logviewer | minikube | disabled | google |
| metallb | minikube | disabled | unknown (third-party) |
| metrics-server | minikube | disabled | kubernetes |
| nvidia-driver-installer | minikube | disabled | google |
| nvidia-gpu-device-plugin | minikube | disabled | unknown (third-party) |
| olm | minikube | disabled | unknown (third-party) |
| pod-security-policy | minikube | disabled | unknown (third-party) |
| portainer | minikube | disabled | portainer.io |
| registry | minikube | disabled | google |
| registry-aliases | minikube | disabled | unknown (third-party) |
| registry-creds | minikube | disabled | unknown (third-party) |
| storage-provisioner | minikube | enabled ✅ | kubernetes |
| storage-provisioner-gluster | minikube | disabled | unknown (third-party) |
| volumesnapshots | minikube | disabled | kubernetes |
|-----------------------------|----------|--------------|-----------------------|

启用某个插件可以用下面的命令:

1
2
3
4
5
6
➜ ~ minikube addons enable ingress
💡 After the addon is enabled, please run "minikube tunnel" and your ingress resources would be available at "127.0.0.1"
Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
Using image k8s.gcr.io/ingress-nginx/controller:v1.0.4
Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
🔎 Verifying ingress addon...

k8s集群初步

既然我们搭建好了一个k8s集群,我们不妨使用它部署一下应用和服务。

我们以rpcx微服务框架的一个hello world程序为例。

部署一个rpcx服务端的应用

rpcx服务端的程序很简单:

server.go
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
package main
import (
"context"
"flag"
"fmt"
example "github.com/rpcxio/rpcx-examples"
"github.com/smallnest/rpcx/server"
)
var addr = flag.String("addr", ":8972", "server address")
type Arith struct{}
// the second parameter is not a pointer
func (t *Arith) Mul(ctx context.Context, args example.Args, reply *example.Reply) error {
reply.C = args.A * args.B
fmt.Println("C=", reply.C)
return nil
}
func main() {
flag.Parse()
s := server.NewServer()
// s.Register(new(Arith), "")
s.RegisterName("Arith", new(Arith), "")
err := s.Serve("tcp", *addr)
if err != nil {
panic(err)
}
}

写一个Docker文件,编译并生成镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM golang:1.18-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache ca-certificates tzdata
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o rpcx_server
FROM scratch as runner
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/app/rpcx_server /opt/app/
EXPOSE 8972
CMD ["/opt/app/rpcx_server"]

生成镜像(我还把它推到了docker平台):

1
2
docker build . -t smallnest/rpcx-server-demo:0.1.0
docker push smallnest/rpcx-server-demo:0.1.0

接下来就是写部署文件了,使用我们的镜像,副本数为3:

rpcx-server-demo.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: rpcx-server-demo-deployment
spec:
selector:
matchLabels:
app: rpcx-server-demo
replicas: 3
template:
metadata:
labels:
app: rpcx-server-demo
spec:
containers:
- name: rpcx-server-demo
image: smallnest/rpcx-server-demo:0.1.0
ports:
- containerPort: 8972

接下了再定义服务,把我们的这个微服务暴露成k8s的一个服务:

rpcx-server-demo-service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: rpcx-server-demo-service #Service 的名称
labels: #Service 自己的标签
app: rpcx-server-demo #为该 Service 设置 key 为 app,value 为 rpcx-server-demo 的标签
spec: #这是关于该 Service 的定义,描述了 Service 如何选择 Pod,如何被访问
selector: #标签选择器
app: rpcx-server-demo #选择包含标签 app:rpcx-server-demo 的 Pod
ports:
- name: rpcx-server-demo-port #端口的名字
protocol: TCP #协议类型 TCP/UDP
port: 9981 #集群内的其他容器组可通过 9981 端口访问 Service
targetPort: 8972 #将请求转发到匹配 Pod 的 8972 端口

最后执行下面的命令发布应用和服务:

1
2
kubectl apply -f rpcx-server-demo.yaml
kubectl apply -f rpcx-server-demo-service.yaml

可以查看发布的应用和服务:

1
2
3
4
5
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
rpcx-server-demo-deployment-7f9d85c5dc-42wbm 1/1 Running 1 (102d ago) 103d
rpcx-server-demo-deployment-7f9d85c5dc-499gm 1/1 Running 1 (102d ago) 103d
rpcx-server-demo-deployment-7f9d85c5dc-s6gh9 1/1 Running 1 (25h ago) 103d
1
2
3
4
➜ ~ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 114d
rpcx-server-demo-service ClusterIP 10.98.134.3 <none> 9981/TCP 103d

部署rpcx客户端的应用

类似的,我们也部署rpcx客户端的程序,它会调用我们刚才部署rpcx服务。

client.go
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
48
49
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/smallnest/rpcx/protocol"
example "github.com/rpcxio/rpcx-examples"
"github.com/smallnest/rpcx/client"
)
func main() {
flag.Parse()
port := os.Getenv("RPCX_SERVER_DEMO_SERVICE_PORT")
addr := strings.TrimPrefix(port, "tcp://")
fmt.Println("dial ", addr)
d, _ := client.NewPeer2PeerDiscovery("tcp@"+addr, "")
opt := client.DefaultOption
opt.SerializeType = protocol.JSON
xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, opt)
defer xclient.Close()
args := example.Args{
A: 10,
B: 20,
}
for {
reply := &example.Reply{}
err := xclient.Call(context.Background(), "Mul", args, reply)
if err != nil {
log.Fatalf("failed to call: %v", err)
}
log.Printf("%d * %d = %d", args.A, args.B, reply.C)
time.Sleep(time.Second)
}
}

编写一个Dockerfile文件,以便生成镜像:

Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM golang:1.18-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache ca-certificates tzdata
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o rpcx_client
FROM busybox as runner
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/app/rpcx_client /opt/app/
CMD ["/opt/app/rpcx_client"]

编译生成镜像:

1
2
docker build . -t smallnest/rpcx-client-demo:0.1.0
docker push smallnest/rpcx-client-demo:0.1.0

编写yaml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: apps/v1
kind: Deployment
metadata:
name: rpcx-client-demo-deployment
spec:
selector:
matchLabels:
app: rpcx-client-demo
replicas: 1
template:
metadata:
labels:
app: rpcx-client-demo
spec:
containers:
- name: rpcx-client-demo
image: smallnest/rpcx-client-demo:0.1.0

最后发布:

1
kubectl apply -f rpcx-client-demo.yaml

检查client是否发布成功了:

1
2
3
4
5
6
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
rpcx-client-demo-deployment-699bfb8799-wdsww 1/1 Running 1 (25h ago) 103d
rpcx-server-demo-deployment-7f9d85c5dc-42wbm 1/1 Running 1 (102d ago) 103d
rpcx-server-demo-deployment-7f9d85c5dc-499gm 1/1 Running 1 (102d ago) 103d
rpcx-server-demo-deployment-7f9d85c5dc-s6gh9 1/1 Running 1 (25h ago) 103d

因为我们的副本数是1,所以这里只有一个节点。

查看客户端的输出,可以看到它调用服务成功了:

1
2
3
4
5
6
7
8
9
10
➜ ~ kubectl logs rpcx-client-demo-deployment-699bfb8799-wdsww |more
dial 10.98.134.3:9981
2022/06/02 15:45:17 10 * 20 = 200
2022/06/02 15:45:18 10 * 20 = 200
2022/06/02 15:45:19 10 * 20 = 200
2022/06/02 15:45:20 10 * 20 = 200
2022/06/02 15:45:21 10 * 20 = 200
2022/06/02 15:45:22 10 * 20 = 200
2022/06/02 15:45:23 10 * 20 = 200
2022/06/02 15:45:24 10 * 20 = 200