我使用Docker Desktop在Mac中启用Docker和Kubernetes已经有一段时间了。尽管它疯狂地吞噬着CPU和内存,让粉丝们疯狂。但随着“当面”弹出强制升级Docker的弹窗和软件许可的变更,是时候为本地Kubernetes开发寻找其他替代品了。
这篇文章只会专注于Mac平台。如果你在Linux上尝试过,请告诉我情况如何。
卸载Docker Desktop
让我们先从删除Docker Desktop开始。
brew uninstall docker
这不仅会摆脱Docker,还会摆脱Hyperkit以及Docker守护进程。Docker守护进程允许我们构建镜像并适用交互式的Docker CLI与之对话。当然还包括Kubernetes集群和kubectl二进制(除非你有单独部署)。如果你没有使用Homebrew,那么就需要相应地卸载这些工具。
然后,让我们把这些工具逐一找回来。
安装Hyperkit
Hyperkit仍然是在Mac上本地运行Kubernetes集群的一个可行的选择,我们先来安装它。
brew install hyperkit
确认安装正确。
❯ hyperkit -v
hyperkit: 0.20200908Homepage:https://github.com/docker/hyperkit
License: BSD
安装Docker CLI
我们是想摆脱Docker Desktop,但并不是Docker本身。Docker仍然是一个很有用的、开源的容器管理工具,如果你有一堆Dockerfiles需要处理,Docker CLI会很有用。
brew install docker
注意:千万不要运行brew install --cask docker。这会安装Docker Desktop集成版本,我们又要重头来过。
这仅仅会安装Docker CLI,但不会安装Docker守护程序dockerd。你可以通过运行docker info看到这一点。
❯ docker info
Client:
Context: default
Debug Mode: falseServer:
ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
安装Kubectl
brew install kubectl
这一点没什么可说的。
安装Minikube(和Docker守护进程)
随着Hyperkit的部署,我们已经准备好部署Kubernetes集群了。并且,在这个过程中启动了一个Docker守护程序。
brew install minikube
在我们开始使用Kubernetes集群之前,这里有一些小知识需要了解。
使用哪一种驱动?
换句话说,我们是将Kubernetes部署在虚拟机、容器还是直接部署在裸金属服务器上?根据操作系统的不同,这里有多种选择[1]。我们将使用Mac的Hyperkit驱动。
使用哪一种容器运行时?
可用的选项:Docker、containerd和cri-o。鉴于Kubernetes本身正在远离Docker而转向Containerd,Containerd是一个不错的选择。但由于我们希望Docker守护程序能够构建Docker镜像,所以我们还是使用Docker吧。
设定CPU和内存限制
和Docker Desktop一样,设置正确的CPU和内存限制总是明智,特别是如果你打算运行许多Pod。
minikube config set cpus 6
minikube config set memory 12g
最后,启动Kubernetes集群。
❯ minikube start --kubernetes-version=v1.19.14 --driver=hyperkit --container-runtime=docker
使用命令行选项-kubernetes-version来部署特定版本的Kubernetes。不用这个标志的话,默认部署最新的版本。我需要部署一个较早的版本来满足我的需要。
以下是上述命令的输出。
😄 minikube v1.23.0 on Darwin 11.5.2
▪ MINIKUBE_ACTIVE_DOCKERD=minikube
✨ Using the hyperkit driver based on user configuration
👍 Starting control plane node minikube in cluster minikube
💾 Downloading Kubernetes v1.19.14 preload ...
> preloaded-images-k8s-v12-v1...: 470.78 MiB / 470.78 MiB 100.00% 6.17 MiB
🔥 Creating hyperkit VM (CPUs=6, Memory=12288MB, Disk=20000MB) ...
❗ This VM is having trouble accessing https://k8s.gcr.io
💡 To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/
🐳 Preparing Kubernetes v1.19.14 on Docker 20.10.8 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
❗ /usr/local/bin/kubectl is version 1.22.1, which may have incompatibilites with Kubernetes 1.19.14.
▪ Want kubectl v1.19.14? Try 'minikube kubectl -- get pods -A'
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
如果你在本地运行dnsmasq,在集群中可能会出现DNS解析失败。你可以卸载它或者在dnsmasq.conf中添加listen-address=192.168.64.1。更多信息可以在这里[2]找到。
Kube Config的上下文已经设置好了。我们可以用 kubectl来检查集群,如下所示。
❯ minikube kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 7m6s v1.19.14
由于我们已经安装了kubectl二进制程序,我们可以直接使用它。
此时,我们已经有了一个Kubernetes集群,由于我们使用了Docker驱动,Docker守护程序也在运行。所以在我们使用守护进程之前,让我们先设置环境变量。
eval $(minikube docker-env)
确认Docker守护进程是正常工作的。
❯ docker info
Client:
Context: default
Debug Mode: falseServer:
Containers: 14
Running: 14
Paused: 0
Stopped: 0
Images: 10
Server Version: 20.10.8
Storage Driver: overlay2
Backing Filesystem: extfs
...
下面是我们的Minikube集群在K9S中的样子。
新安装的Minikube集群的K9S截图
需要Docker Compose?
用以下命令安装Docker-Compose。
brew install docker-compose
暴露Services到Minikube外部
对于本地开发,通常是通过浏览器或CLI从电脑访问服务。端口转发总是一个选择,但有时Ingress或负载均衡器也是有用的。让我们看看它们是如何与Minikube一起工作的。
处理Ingress资源
我们现在有了一个Kubernetes集群,也可以在上面部署应用程序。但我们如何访问Ingress资源呢?Minikube有一个答案,就是addons(附加组件)。
❯ minikube addons enable ingress
▪ Using image k8s.gcr.io/ingress-nginx/controller:v1.0.0-beta.3
▪ Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0
▪ Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0
🔎 Verifying ingress addon...
这会部署Nginx Ingress控制器。更重要的是,它会把Nginx服务部署以 NodePort的形式部署,并将Minikube的IP直接指向Ingress。让我们先找到这个IP。
❯ minikube ip
192.168.64.12
我们在80端口调用上述IP,我们应该可以从Nginx得到响应。
❯ curl http://192.168.64.12
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
记住,Ingress依赖于DNS工作,它应该被解析到Minikube的IP上。如果后端服务之一调用该DNS,除非明确配置了,否则会解析失败。这个时候,另一个附加组件可以来拯救你。
❯ minikube addons enable ingress-dns
▪ Using image cryptexlabs/minikube-ingress-dns:0.3.0
这将在Kubernetes集群内启动一个DNS服务器,监听在Minikube的IP上。此外,还需要一个自定义的解析器,以强制自定义顶级域名(如.test,不要使用.local)的DNS解析被重定向到上面启动的DNS服务器。Minikube Ingress DNS[3]文档很好地解释了这一点。按照文档里描述的步骤操作,你将会部署成功一个带有自定义DNS的Ingress。
负载均衡类型的Service
部署负载均衡器类型的服务,并从主机上访问它,就像你在云端部署时一样,这不是很好吗?多亏了metalb插件,让这变得很简单。
minikube addons enable metallb
这将部署另外两个Pod,负责为负载均衡器类型的Service分配一个外部IP。如果不这样做,这些服务的外部IP将始终处于“pending”状态。
在使用metallb之前,还有一个步骤。默认情况下,metallb没有办法知道哪个范围的IP可以分配给负载均衡器的服务。运行下面的命令来提供一个范围。
❯ minikube addons configure metallb
-- Enter Load Balancer Start IP: 192.168.64.5
-- Enter Load Balancer End IP: 192.168.64.15
▪ Using image metallb/speaker:v0.9.6
▪ Using image metallb/controller:v0.9.6
✅ metallb was successfully configured
基于你的Minikube IP,分配一个小范围的IP,包括Minikube IP。现在,每当你部署一个负载均衡器服务时,这个范围内的一个IP将被分配。
其他问题
登录到远程镜像仓库
你可能仍然有旧的~/.docker/config.json,其中credsStore设置为osxkeychain或desktop。这在新的配置中不会起作用。为了解决这个问题,我们来安装Docker Credential Helper。
brew install docker-credential-helper
密钥凭证还是会像以前一样存储在MacOS Keychain中。如果这不起作用,一个快速的解决方法是删除~/.docker/config.json文件,然后再次登录到镜像仓库。
保留Docker镜像和持久化存储卷声明
Added 7th Sept 2021
Docker Desktop的一个好处是,你可以关闭Kubernetes集群,以后再启动它,用相同的Docker镜像和持久化卷运行你的Pod。例如,在Kubernetes中运行本地数据库时,这就很有用。能够在多次重启中仍然保持有效的持久化卷是很方便的。
在Minikube中,如果我用minikube stop关闭集群(和Hyperkit虚拟机),它会删除Docker镜像和所有持久化卷,这很麻烦。但幸运的是,Minikube提供了一种防止删除的方法。我们可以暂停Kubernetes集群和Hyperkit VM,而不是停止它。
minikube pause
该命令会终止Kubernetes集群,但不会删除Hyperkit虚拟机。这就释放了更多的CPU,同时仍然保留了所有的Docker镜像和持久化卷。但是,等一下,它变得更好!它不会停止dockerd守护进程。所以你可以继续使用Docker CLI,只是别忘了用eval $(minikube docker-env)设置一下docker环境。
当你想恢复在Kubernetes集群上的工作时,可以运行以下命令。
minikube unpause
而你将拥有所有的系统Pod,包括附加组件Addons。这甚至在笔记本电脑重新启动后,也能正常工作!
在Docker容器中绑定挂载
Added 7th Sept 2021
一些在Reddit[4]上的好心人指出,Docker容器中的绑定挂载(-v)在Minikube和Docker的配置中并不能工作。这是Docker容器的一个常见操作,他理论上应该可以正常工作。
由于存在Hyperkit作为中间层,挂载一个卷其实是分成两步操作的。首先,让我们把笔记本上的磁盘挂载到Hyperkit VM上。
minikube mount /myvolume:/test
这将把本地文件夹/myvolume挂载到Hyperkit VM的/test路径下。这个进程仍然是活跃的,所以你不应该关闭这个终端。
在另一个终端上,运行Docker容器,并将/test卷绑定到容器内的一个路径上。
docker run --rm -it -v /test:/inside busybox /bin/sh
这将在容器内的Hyperkit VM上挂载/test卷,路径为/inside。实际上,这会使得笔记本电脑上/myvolume下的所有处在容器内的文件和文件夹处于读写模式。很好!
总结
在周日下午花了几个小时后,我对这个新的配置相当满意。我们摆脱了Docker Desktop,用Hyperkit和Minikube取代了它。我们仍然可以使用Docker API来管理Docker文件,并在本地Kubernetes集群中部署应用程序。最重要的是,我的笔记本可以愉快地运行,额外的资源可以用来运行Slack、Notion和其他Electron应用程序;-)