本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

WSL+Docker:Windows 桌面上的 Kubernetes

引言

刚接触 Windows 10 和 WSL2,或刚接触 Docker 和 Kubernetes?欢迎阅读这篇博客文章,我们将从头开始在 Docker KinDMinikube 中安装 Kubernetes。

为什么要在 Windows 上运行 Kubernetes?

在过去的几年里,Kubernetes 已成为在分布式环境中运行容器化服务和应用程序的实际标准平台。虽然有各种各样的发行版和安装程序可以将 Kubernetes 部署到云环境(公共、私有或混合)或裸机环境,但仍然需要本地部署和运行 Kubernetes,例如在开发人员的工作站上。

Kubernetes 最初设计用于部署和运行在 Linux 环境中。然而,相当多的用户(不仅仅是应用程序开发人员)使用 Windows 操作系统作为他们的日常操作系统。当微软发布 WSL - 适用于 Linux 的 Windows 子系统时,Windows 和 Linux 环境之间的界限变得更加模糊。

此外,WSL 带来了几乎无缝地在 Windows 上运行 Kubernetes 的能力!

下面,我们将简要介绍如何安装和使用各种解决方案来本地运行 Kubernetes。

先决条件

由于我们将解释如何安装 KinD,因此不会详细介绍 KinD 依赖项的安装。

但是,这里是所需的先决条件及其版本/通道的列表

  • 操作系统:Windows 10 版本 2004,内部版本 19041
  • 已启用 WSL2
    • 要默认将发行版安装为 WSL2,一旦安装了 WSL2,请在 Powershell 中运行命令 wsl.exe --set-default-version 2
  • 从 Windows 应用商店安装的 WSL2 发行版 - 使用的发行版是 Ubuntu-18.04
  • 适用于 Windows 的 Docker Desktop,稳定通道 - 使用的版本是 2.2.0.4
  • [可选] 从 Windows 应用商店安装了 Microsoft Terminal
    • 打开 Windows 应用商店并在搜索中输入“Terminal”,它将是(通常)第一个选项

Windows Store Terminal

实际上就是这样。对于适用于 Windows 的 Docker Desktop,无需配置任何内容,我们将在下一节中解释。

WSL2:初次接触

一切安装完成后,我们可以从“开始”菜单启动 WSL2 终端,然后输入“Ubuntu”搜索应用程序和文档

Start Menu Search

找到后,单击名称,它将启动默认的 Windows 控制台,其中运行着 Ubuntu bash shell。

与任何正常的 Linux 发行版一样,您需要创建一个用户并设置密码

User-Password

[可选] 更新 sudoers

由于我们通常在本地计算机上工作,更新 sudoers 并将组 %sudo 设置为无需密码可能会很好

# Edit the sudoers with the visudo command
sudo visudo

# Change the %sudo group to be password-less
%sudo   ALL=(ALL:ALL) NOPASSWD: ALL

# Press CTRL+X to exit
# Press Y to save
# Press Enter to confirm

visudo

更新 Ubuntu

在进入 Docker Desktop 设置之前,让我们更新我们的系统并确保我们在最佳条件下开始

# Update the repositories and list of the packages available
sudo apt update
# Update the system based on the packages installed > the "-y" will approve the change automatically
sudo apt upgrade -y

apt-update-upgrade

Docker Desktop:WSL2 更快

在进入设置之前,让我们做一个小测试,它将真正展示与 Docker Desktop 的新集成有多酷

# Try to see if the docker cli and daemon are installed
docker version
# Same for kubectl
kubectl version

kubectl-error

你收到错误了吗?太棒了!这实际上是个好消息,所以现在让我们继续设置。

Docker Desktop 设置:启用 WSL2 集成

首先,如果尚未启动,请启动适用于 Windows 的 Docker Desktop。打开 Windows “开始”菜单并输入“docker”,单击名称启动应用程序

docker-start

您现在应该会在时钟附近看到带有其他任务栏图标的 Docker 图标

docker-taskbar

现在单击 Docker 图标并选择设置。将出现一个新窗口

docker-settings-general

默认情况下,WSL2 集成未激活,因此单击“启用基于 WSL 2 的实验性引擎”并单击“应用并重启”

docker-settings-wsl2

此功能在幕后创建了两个新的 WSL2 发行版,包含并运行所有必要的后端套接字、守护程序以及 CLI 工具(即:docker 和 kubectl 命令)。

但是,此第一个设置仍不足以在我们的发行版中运行命令。如果我们尝试,我们将收到与之前相同的错误。

为了解决这个问题,并最终能够使用这些命令,我们还需要告诉 Docker Desktop “附加”到我们的发行版

docker-resources-wsl

现在让我们切换回 WSL2 终端,看看我们是否可以(最终)启动命令

# Try to see if the docker cli and daemon are installed
docker version
# Same for kubectl
kubectl version

docker-kubectl-success

提示:如果没有发生任何事情,请重新启动 Docker Desktop 并在 Powershell 中重新启动 WSL 进程:Restart-Service LxssManager 并启动新的 Ubuntu 会话

成功了!基本设置现已完成,我们继续安装 KinD。

KinD:容器中的 Kubernetes 变得简单

现在,我们已经安装、配置了 Docker,并且上次测试运行良好。

但是,如果我们仔细查看 kubectl 命令,它找到了“客户端版本”(1.15.5),但没有找到任何服务器。

这是正常的,因为我们没有启用 Docker Kubernetes 集群。所以让我们安装 KinD 并创建我们的第一个集群。

由于来源总是重要的,我们将(部分)遵循 官方 KinD 网站 上的操作指南

# Download the latest version of KinD
curl -Lo ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-linux-amd64
# Make the binary executable
chmod +x ./kind
# Move the binary to your executable path
sudo mv ./kind /usr/local/bin/

kind-install

KinD:第一个集群

我们已准备好创建我们的第一个集群

# Check if the KUBECONFIG is not set
echo $KUBECONFIG
# Check if the .kube directory is created > if not, no need to create it
ls $HOME/.kube
# Create the cluster and give it a name (optional)
kind create cluster --name wslkind
# Check if the .kube has been created and populated with files
ls $HOME/.kube

kind-cluster-create

提示:如您所见,终端已更改,因此所有漂亮的图标都已显示

集群已成功创建,并且由于我们使用的是 Docker Desktop,因此网络已为我们设置好,可以直接使用。

所以我们可以在 Windows 浏览器中打开 Kubernetes master URL

kind-browser-k8s-master

这就是适用于 Windows 的 Docker Desktop 与 WSL2 后端集成的真正优势。Docker 确实做了一个令人惊叹的集成。

KinD:数一数二数三

我们的第一个集群已创建,它是一个“正常”的单节点集群

# Check how many nodes it created
kubectl get nodes
# Check the services for the whole cluster
kubectl get all --all-namespaces

kind-list-nodes-services

虽然这对于大多数人来说已经足够了,但让我们利用其中一个最酷的功能,多节点集群

# Delete the existing cluster
kind delete cluster --name wslkind
# Create a config file for a 3 nodes cluster
cat << EOF > kind-3nodes.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker
  - role: worker
EOF
# Create a new cluster with the config file
kind create cluster --name wslkindmultinodes --config ./kind-3nodes.yaml
# Check how many nodes it created
kubectl get nodes

kind-cluster-create-multinodes

提示:根据我们运行“获取节点”命令的速度,可能并非所有节点都已就绪,等待几秒钟并再次运行,一切都应该就绪

就是这样,我们已经创建了一个三节点集群,如果我们再次查看服务,我们将看到现在有三个副本的服务

# Check the services for the whole cluster
kubectl get all --all-namespaces

wsl2-kind-list-services-multinodes

KinD:我能看到一个漂亮的仪表板吗?

命令行操作总是好的,并且非常富有洞察力。然而,在处理 Kubernetes 时,我们可能在某个时候需要一个可视化的概述。

为此,Kubernetes Dashboard 项目应运而生。安装和首次连接测试非常快,所以让我们开始吧

# Install the Dashboard application into our cluster
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc6/aio/deploy/recommended.yaml
# Check the resources it created based on the new namespace created
kubectl get all -n kubernetes-dashboard

kind-install-dashboard

由于它创建了一个带有 ClusterIP(即:内部网络地址)的服务,因此如果我们直接在 Windows 浏览器中输入 URL,将无法访问它

kind-browse-dashboard-error

那是因为我们需要创建一个临时代理

# Start a kubectl proxy
kubectl proxy
# Enter the URL on your browser: https://:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

kind-browse-dashboard-success

最后,要登录,我们可以输入一个我们没有创建的令牌,或者输入我们集群的 kubeconfig 文件。

如果我们尝试使用 `kubeconfig` 登录,我们将收到错误“内部错误 (500):没有足够的数据来创建身份验证信息结构”。这是由于 `kubeconfig` 文件中缺少凭据。

因此,为了避免您遇到同样的错误,让我们遵循推荐的 RBAC 方法

让我们打开一个新的 WSL2 会话

# Create a new ServiceAccount
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
EOF
# Create a ClusterRoleBinding for the ServiceAccount
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

kind-browse-dashboard-rbac-serviceaccount

# Get the Token for the ServiceAccount
kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')
# Copy the token and copy it into the Dashboard login and press "Sign in"

kind-browse-dashboard-login-success

成功!让我们也看看我们列出的节点

kind-browse-dashboard-browse-nodes

一个漂亮闪亮的三节点出现了。

Minikube:无处不在的 Kubernetes

现在,我们已经安装、配置了 Docker,并且上次测试运行良好。

但是,如果我们仔细查看 kubectl 命令,它找到了“客户端版本”(1.15.5),但没有找到任何服务器。

这是正常的,因为我们没有启用 Docker Kubernetes 集群。所以让我们安装 Minikube 并创建我们的第一个集群。

由于引用来源总是很重要,我们将(部分)遵循 Kubernetes.io 网站上的操作指南

# Download the latest version of Minikube
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
# Make the binary executable
chmod +x ./minikube
# Move the binary to your executable path
sudo mv ./minikube /usr/local/bin/

minikube-install

Minikube:更新主机

如果我们按照操作指南,它指出我们应该使用 --driver=none 标志,以便直接在主机和 Docker 上运行 Minikube。

不幸的是,我们将收到一个关于运行 Kubernetes v1.18 需要“conntrack”的错误

# Create a minikube one node cluster
minikube start --driver=none

minikube-start-error

提示:如您所见,终端已更改,因此所有漂亮的图标都已显示

所以让我们通过安装缺失的软件包来解决这个问题

# Install the conntrack package
sudo apt install -y conntrack

minikube-install-conntrack

让我们再试一次

# Create a minikube one node cluster
minikube start --driver=none
# We got a permissions error > try again with sudo
sudo minikube start --driver=none

minikube-start-error-systemd

好的,这个错误在过去可能会有问题……幸运的是,我们有解决方案

Minikube:启用 SystemD

为了在 WSL2 上启用 SystemD,我们将应用 Daniel Llewellyn脚本

我邀请您阅读完整的博文,了解他是如何找到解决方案的,以及他为解决各种问题所做的多次迭代。

简而言之,以下是命令

# Install the needed packages
sudo apt install -yqq daemonize dbus-user-session fontconfig

minikube-systemd-packages

# Create the start-systemd-namespace script
sudo vi /usr/sbin/start-systemd-namespace
#!/bin/bash

SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')
if [ -z "$SYSTEMD_PID" ] || [ "$SYSTEMD_PID" != "1" ]; then
    export PRE_NAMESPACE_PATH="$PATH"
    (set -o posix; set) | \
        grep -v "^BASH" | \
        grep -v "^DIRSTACK=" | \
        grep -v "^EUID=" | \
        grep -v "^GROUPS=" | \
        grep -v "^HOME=" | \
        grep -v "^HOSTNAME=" | \
        grep -v "^HOSTTYPE=" | \
        grep -v "^IFS='.*"$'\n'"'" | \
        grep -v "^LANG=" | \
        grep -v "^LOGNAME=" | \
        grep -v "^MACHTYPE=" | \
        grep -v "^NAME=" | \
        grep -v "^OPTERR=" | \
        grep -v "^OPTIND=" | \
        grep -v "^OSTYPE=" | \
        grep -v "^PIPESTATUS=" | \
        grep -v "^POSIXLY_CORRECT=" | \
        grep -v "^PPID=" | \
        grep -v "^PS1=" | \
        grep -v "^PS4=" | \
        grep -v "^SHELL=" | \
        grep -v "^SHELLOPTS=" | \
        grep -v "^SHLVL=" | \
        grep -v "^SYSTEMD_PID=" | \
        grep -v "^UID=" | \
        grep -v "^USER=" | \
        grep -v "^_=" | \
        cat - > "$HOME/.systemd-env"
    echo "PATH='$PATH'" >> "$HOME/.systemd-env"
    exec sudo /usr/sbin/enter-systemd-namespace "$BASH_EXECUTION_STRING"
fi
if [ -n "$PRE_NAMESPACE_PATH" ]; then
    export PATH="$PRE_NAMESPACE_PATH"
fi
# Create the enter-systemd-namespace
sudo vi /usr/sbin/enter-systemd-namespace
#!/bin/bash

if [ "$UID" != 0 ]; then
    echo "You need to run $0 through sudo"
    exit 1
fi

SYSTEMD_PID="$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')"
if [ -z "$SYSTEMD_PID" ]; then
    /usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
    while [ -z "$SYSTEMD_PID" ]; do
        SYSTEMD_PID="$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')"
    done
fi

if [ -n "$SYSTEMD_PID" ] && [ "$SYSTEMD_PID" != "1" ]; then
    if [ -n "$1" ] && [ "$1" != "bash --login" ] && [ "$1" != "/bin/bash --login" ]; then
        exec /usr/bin/nsenter -t "$SYSTEMD_PID" -a \
            /usr/bin/sudo -H -u "$SUDO_USER" \
            /bin/bash -c 'set -a; source "$HOME/.systemd-env"; set +a; exec bash -c '"$(printf "%q" "$@")"
    else
        exec /usr/bin/nsenter -t "$SYSTEMD_PID" -a \
            /bin/login -p -f "$SUDO_USER" \
            $(/bin/cat "$HOME/.systemd-env" | grep -v "^PATH=")
    fi
    echo "Existential crisis"
fi
# Edit the permissions of the enter-systemd-namespace script
sudo chmod +x /usr/sbin/enter-systemd-namespace
# Edit the bash.bashrc file
sudo sed -i 2a"# Start or enter a PID namespace in WSL2\nsource /usr/sbin/start-systemd-namespace\n" /etc/bash.bashrc

minikube-systemd-files

最后,退出并启动一个新会话。您**无需**停止 WSL2,新会话就足够了

minikube-systemd-enabled

Minikube:第一个集群

我们已准备好创建我们的第一个集群

# Check if the KUBECONFIG is not set
echo $KUBECONFIG
# Check if the .kube directory is created > if not, no need to create it
ls $HOME/.kube
# Check if the .minikube directory is created > if yes, delete it
ls $HOME/.minikube
# Create the cluster with sudo
sudo minikube start --driver=none

为了能够让我们的用户使用 `kubectl`,而不是 `sudo`,Minikube 建议运行 `chown` 命令

# Change the owner of the .kube and .minikube directories
sudo chown -R $USER $HOME/.kube $HOME/.minikube
# Check the access and if the cluster is running
kubectl cluster-info
# Check the resources created
kubectl get all --all-namespaces

minikube-start-fixed

集群已成功创建,Minikube 使用了 WSL2 IP,这出于多种原因非常棒,其中之一是我们可以在 Windows 浏览器中打开 Kubernetes master URL

minikube-browse-k8s-master

WSL2 集成的真正优势在于,端口 `8443` 一旦在 WSL2 发行版上打开,它实际上会将其转发到 Windows,因此无需记住 IP 地址,我们也可以通过 `localhost` 访问 `Kubernetes master` URL

minikube-browse-k8s-master-localhost

Minikube:我能看到漂亮的仪表板吗?

命令行操作总是好的,并且非常富有洞察力。然而,在处理 Kubernetes 时,我们可能在某个时候需要一个可视化的概述。

为此,Minikube 内置了 Kubernetes Dashboard。多亏了它,运行和访问 Dashboard 非常简单

# Enable the Dashboard service
sudo minikube dashboard
# Access the Dashboard from a browser on Windows side

minikube-browse-dashboard

该命令还会创建一个代理,这意味着一旦我们通过按 `CTRL+C` 结束命令,仪表板将不再可访问。

但是,如果我们查看 `kubernetes-dashboard` 命名空间,我们会发现服务仍然存在

# Get all the services from the dashboard namespace
kubectl get all --namespace kubernetes-dashboard

minikube-dashboard-get-all

让我们编辑服务并将其类型更改为 `LoadBalancer`

# Edit the Dashoard service
kubectl edit service/kubernetes-dashboard --namespace kubernetes-dashboard
# Go to the very end and remove the last 2 lines
status:
  loadBalancer: {}
# Change the type from ClusterIO to LoadBalancer
  type: LoadBalancer
# Save the file

minikube-dashboard-type-loadbalancer

再次检查仪表板服务,并通过 LoadBalancer 访问仪表板

# Get all the services from the dashboard namespace
kubectl get all --namespace kubernetes-dashboard
# Access the Dashboard from a browser on Windows side with the URL: localhost:<port exposed>

minikube-browse-dashboard-loadbalancer

结论

很明显,我们离完成还很远,因为我们可能还需要实现一些负载均衡和/或其他服务(存储、Ingress、注册表等)。

关于 WSL2 上的 Minikube,由于它需要启用 SystemD,我们可以认为它是一个需要实现的中间级别。

那么有了两种解决方案,哪种才是“最适合你的”呢?两者都有各自的优点和缺点,所以这里仅从我们的角度进行概述

标准KinDMinikube
在 WSL2 上安装非常简单中等
多节点
插件手动安装
持久性是,但并非为此设计
替代方案K3dMicrok8s

我们希望您能真正体验到不同组件(WSL2 - Docker Desktop - KinD/Minikube)之间的集成。这为您在 Windows 和 WSL2 上使用 KinD 和/或 Minikube 的 Kubernetes 工作流程提供了一些想法,甚至更好的答案。

期待在 Kubernetes 的海洋中再次相遇,共探更多冒险。

NunoIhor