Kubernetes

Docker Desktop 安装K8s集群

转载:https://zhuanlan.zhihu.com/p/388874530

前言


之前给大家介绍过几种在笔记本电脑上安装Kubernetes集群的工具,虽然安装起来不太繁琐但是多多少少还是需要花一些时间的,对于不想瞎倒腾,就想快速安装个本地集群开始学习和测试的同学,推荐你们试一试Docker桌面应用里自带的Kubernetes集群。其实我也是之前用Minikube安装的集群莫名其妙坏掉启动不起来后,偶然发现Docker桌面应用里内嵌了一个Kubernetes集群,试了试效果感觉还是挺不错的。下面我带大家简单过一下启用集群方法,全程几乎就是点点点,也不需要做啥。

 

启用Kubernetes


也不知道是什么时候开始(反正老早就有了,我这个是前年装的…一直没升级过),Docker的桌面应用除了提供Docker CLI集成外还内嵌了一个Kubernetes集群,默认是不开启的,启用后这个单点的Kubernetes集群会运行在本地的Docker实例中。

要启用这个集群只需要打开Docker应用的首选项(Preferences)界面,选择Kubernetes选项卡。

 

点击应用并重启,就可以去刷小视频了,再回来集群就安装好了。

除了启用Kubernetes集群外,如果电脑上之前没有安装过 kubectl(客户端命令工具)的话还会为你自动安装上kubectl,并配置连接到刚才启动的本地集群上。如果是本地之前安装过kubectl 和 存在其他Kubernetes集群,想要连接到Docker桌面应用内嵌的集群则需要切换一下 kubectl 的上下文。

$ kubectl config get-contexts
$ kubectl config use-context docker-desktop

 

使用Kubernetes


集群启用完成后,在打开Docker桌面应用的选项卡在 UI 上会有些微的变化,证明Kubernetes集群已经成功启动起来了。

 

让我们随便运行几个 kubctl 命令,试试效果。

➜  ~ kubectl get node                     
NAME             STATUS   ROLES                  AGE   VERSION
docker-desktop   Ready    control-plane,master   13d   v1.21.1

可以看到我们现在使用的是一个单节点,名字叫docker-desktop的集群(这个名没地方改)。

使用Docker桌面应用自带的Kubernetes集群还有一点方便的地方就是,集群外部通过 127.0.0.1 就能访问集群内部,也就是我们通过 NodePort类型的 Service 向集群外暴露的资源,通过 127.0.0.1:port 的形式就能访问到。

我们随便拿一个之前我们搭建MySQL开发环境的例子测试一下。搭建MySQL需要的YAML定义文件和步骤我就不再重复说了,忘记的同学之前访问上一篇文章:利用Kubernetes搭建便携式开发环境之MySQL和Redis

## 切到定义文件在的目录
kubectl apply -f mysql-configmap.yaml

kubectl apply -f deployment-service.yaml

--------------------------------------------------------
## 可以看到下面的Pod和Service资源
kubectl get pod

NAME                    READY   STATUS    RESTARTS   AGE
mysql-cc4479465-gwdj7   1/1     Running   0          13d
--------------------------------------------------------
kubectl get svc mysql
NAME    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
mysql   NodePort   10.104.123.151   <none>        3306:30306/TCP   13d

在电脑上只要通过127.0.0.1:30306就能访问到我们刚刚安装的MySQL啦,持久化之类的事情我也试过,只要我们不主动把MySQL这个Pod删掉数据就会一直在。

 

安装 dashboard


‼️重要:dashboard启动需要科学上网,原因是不科学上网有些镜像拉取不下来?

‼️重要:如果运行时发现dashboard 的相关服务起不来了,dashboard打不开,暂时解决方法是,挂vpn翻墙,然后重启 docker desktop服务。

  

  • k8s集群启动以后,就可以用下面的命令确认集群是否ready
Mac-mini ~ % kubectl get node
NAME             STATUS   ROLES           AGE    VERSION
docker-desktop   Ready    control-plane   2d2h   v1.32.2
  • 接着运行以下命令来安装 Kubernetes Dashboard:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
  • 创建服务账户
admin-user.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
kubectl apply -f admin-user.yaml 

输出:
serviceaccount/admin-user created
  • 绑定服务账户集群角色
admin-user-role-binding.yaml

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
kubectl apply -f admin-user-role-binding.yaml 

输出:
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
  • 创建 admin-user 的 token(这个token好像会过期,如果过期的话,就用下面命令重新创建个)

Kubernetes 从 1.24 版本开始,默认启用ServiceAccount令牌的自动管理,生成的 Token 默认有效期为1 小时3600秒)。

kubectl -n kubernetes-dashboard create token admin-user
  • 创建永久 Token(不推荐,存在安全风险)
通过Secret手动创建不指定过期时间的 Token:

apiVersion: v1
kind: Secret
metadata:
  name: admin-user-permanent-token
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/service-account.name: "admin-user"  # 关联ServiceAccount
type: kubernetes.io/service-account-token

应用该 Secret 后,通过以下命令获取 Token:
该 Token 无过期时间,需手动删除 Secret 才能失效。
生产环境中应避免使用永久 Token,建议通过 RBAC 控制权限并定期轮换短期 Token。
kubectl get secret admin-user-permanent-token -n kubernetes-dashboard -o jsonpath="{.data.token}" | base64 -d
  • 用下面的命令启动 dashboard
kubectl proxy --port=8001
  • 用下面的url访问 dashboard
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login
  • 输入刚刚创建的 admin-user 的 token, 然后登录就可以访问dashboard了

  

  

dashboard 重启时 镜像拉取不了的解决办法


kubernetes-dashboard     dashboard-metrics-scraper-5bd45c9dd6-q4wml              1/1     Running            22 (16m ago)    43d
kubernetes-dashboard     kubernetes-dashboard-79cbcf9fb6-bvj58                   0/1     ImagePullBackOff   0               92s
kubernetes-dashboard     kubernetes-dashboard-api-7b6b887946-76ncg               1/1     Running            32 (16m ago)    43d
kubernetes-dashboard     kubernetes-dashboard-auth-769bf978b9-tnc8m              1/1     Running            22 (16m ago)    43d
kubernetes-dashboard     kubernetes-dashboard-kong-65548bdf66-drm48              0/1     Running            13 (5m9s ago)   5d5h
kubernetes-dashboard     kubernetes-dashboard-metrics-scraper-76df4956c4-k64r9   1/1     Running            22 (16m ago)    43d
kubernetes-dashboard     kubernetes-dashboard-web-56df7655d9-rx7hh               1/1     Running            22 (16m ago)    43d
wechat-applet-services   service-notification-deployment-54bdd8655c-45tpm        1/1     Running            8 (16m ago)     20d
wechat-applet-services   service-task-executor-deployment-698cd9547-wvbvs        1/1     Running            5 (16m ago)     7d7h
wechat-applet-services   service-thumbnail-deployment-59b4f4759d-8qjbd           1/1     Running            4 (16m ago)     6d
wechat-applet-services   service-wechat-deployment-7f497855df-6rg9v              1/1     Running            2 (16m ago)     5d21h

kubectl logs kubernetes-dashboard-79cbcf9fb6-bvj58 -n kubernetes-dashboard
Error from server (BadRequest): container "kubernetes-dashboard" in pod "kubernetes-dashboard-79cbcf9fb6-bvj58" is waiting to start: trying and failing to pull image

可以看到 kubernetes-dashboard-79cbcf9fb6-bvj58 这个pod起不来,镜像拉取失败。

执行以下命令查看该 Pod 的详细配置,重点关注镜像相关信息:

kubectl get pod kubernetes-dashboard-79cbcf9fb6-bvj58 -n kubernetes-dashboard -o yaml
kubectl get pod kubernetes-dashboard-79cbcf9fb6-bvj58 -n kubernetes-dashboard -o yaml

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2025-07-03T12:48:56Z"
  generateName: kubernetes-dashboard-79cbcf9fb6-
  labels:
    k8s-app: kubernetes-dashboard
    pod-template-hash: 79cbcf9fb6
  name: kubernetes-dashboard-79cbcf9fb6-bvj58
  namespace: kubernetes-dashboard
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: kubernetes-dashboard-79cbcf9fb6
    uid: 5d718a39-36d4-45ca-8f82-7e5b18c58cd0
  resourceVersion: "5231806"
  uid: 42a3f595-2356-4a79-9010-60e3285e98e6
spec:
  containers:
  - args:
    - --auto-generate-certificates
    - --namespace=kubernetes-dashboard
    image: kubernetesui/dashboard:v2.7.0
    imagePullPolicy: Always
    livenessProbe:
      failureThreshold: 3
      httpGet:
        path: /
        port: 8443
        scheme: HTTPS
      initialDelaySeconds: 30
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 30
    name: kubernetes-dashboard
    ports:
    - containerPort: 8443
      protocol: TCP
    resources: {}
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsGroup: 2001
      runAsUser: 1001
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /certs
      name: kubernetes-dashboard-certs
    - mountPath: /tmp
      name: tmp-volume
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-7pk9w
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: docker-desktop
  nodeSelector:
    kubernetes.io/os: linux
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  serviceAccount: kubernetes-dashboard
  serviceAccountName: kubernetes-dashboard
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kubernetes-dashboard-certs
    secret:
      defaultMode: 420
      secretName: kubernetes-dashboard-certs
  - emptyDir: {}
    name: tmp-volume
  - name: kube-api-access-7pk9w
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2025-07-03T12:49:26Z"
    status: "True"
    type: PodReadyToStartContainers
  - lastProbeTime: null
    lastTransitionTime: "2025-07-03T12:48:56Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2025-07-03T12:48:56Z"
    message: 'containers with unready status: [kubernetes-dashboard]'
    reason: ContainersNotReady
    status: "False"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2025-07-03T12:48:56Z"
    message: 'containers with unready status: [kubernetes-dashboard]'
    reason: ContainersNotReady
    status: "False"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2025-07-03T12:48:56Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - image: kubernetesui/dashboard:v2.7.0
    imageID: ""
    lastState: {}
    name: kubernetes-dashboard
    ready: false
    restartCount: 0
    started: false
    state:
      waiting:
        message: 'Error response from daemon: Get "https://registry-1.docker.io/v2/":
          net/http: request canceled while waiting for connection (Client.Timeout
          exceeded while awaiting headers)'
        reason: ErrImagePull
    volumeMounts:
    - mountPath: /certs
      name: kubernetes-dashboard-certs
    - mountPath: /tmp
      name: tmp-volume
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-7pk9w
      readOnly: true
      recursiveReadOnly: Disabled
  hostIP: 192.168.65.3
  hostIPs:
  - ip: 192.168.65.3
  phase: Pending
  podIP: 10.1.1.124
  podIPs:
  - ip: 10.1.1.124
  qosClass: BestEffort
  startTime: "2025-07-03T12:48:56Z"

可以看到有 Error response from daemon: Get “https://registry-1.docker.io/v2/, 看来还是访问https://registry-1.docker.io/v2/镜像仓库有问题。

但是可以看到docker本地是有这个镜像的。

 

修改 Deployment 使用本地镜像:临时修改 Deployment,强制使用本地已导入的镜像(不通过网络拉取)

kubectl edit deployment kubernetes-dashboard -n kubernetes-dashboard

将 imagePullPolicy 改为 IfNotPresent(如果本地有镜像,则不尝试拉取):

pec:
  containers:
  - name: kubernetes-dashboard
    image: kubernetesui/dashboard:v2.7.0
    imagePullPolicy: IfNotPresent  # 原为 Always

改完Deployment以后,Pod会重新部署,可以发现可以正常启动了。

  

安装 Metrics API


Metrics API(由 metrics-server 组件提供),该组件用于收集 Pod 和节点的资源使用数据(CPU、内存等)。

Metrics API 是 Kubernetes 中的核心监控组件,负责收集和暴露集群资源使用数据(如 CPU、内存、磁盘 I/O 等)。它作为标准化接口,为上层工具(如 kubectl top、Horizontal Pod Autoscaler、Prometheus)提供统一的指标查询方式。以下是详细介绍:

 

1. 核心概念与架构

Metrics API 由两部分组成:

  1. API 服务(metrics.k8s.io):
    • 属于 Kubernetes API 体系的一部分,提供标准化的 REST API 接口。
    • 版本:v1beta1(Kubernetes 1.8+),未来可能升级到 v1
  2. 数据采集组件(默认由 metrics-server 实现):
    • 周期性从节点的 kubelet 收集容器和 Pod 的资源使用数据。
    • 将数据聚合后存储在内存中(不支持历史数据持久化)。

 

2. 核心作用

Metrics API 是 Kubernetes 监控体系的基础组件,支撑以下功能:

  • 命令行工具:
    kubectl top nodes/pods 命令直接从 Metrics API 获取实时数据。
  • 自动扩缩容:
    Horizontal Pod Autoscaler(HPA)根据 Metrics API 的指标自动调整 Pod 数量。
  • 调度决策:
    Kubernetes 调度器根据节点资源使用情况(通过 Metrics API)选择最优节点。
  • 可视化工具:
    如 Dashboard、Grafana 等可通过 Metrics API 获取实时监控数据。

 

3. 安装(需要翻墙)❗️❗️

  • 3.1 下载 最新版本 metrics-server 的 components.yaml
curl -O https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
  • 3.2 修改配置文件
# 修改配置(解决证书和性能问题)
vi components.yaml

# 添加以下参数(在 spec.containers.args 中):
- --kubelet-insecure-tls          # 跳过 TLS 验证(测试环境使用)
- --metric-resolution=30s        # 设置指标采集频率(默认 15s,可调整)

❗️❗️ 因为需要从registry.k8s.io 拉取下面镜像,国内网络可能无法访问,所以需要翻墙。

# 应用配置
kubectl apply -f components.yaml
  • 3.3 验证metrics-server是否启动
# 检查 metrics-server Pod 状态
kubectl get pods -n kube-system -l k8s-app=metrics-server

输出如下:
NAME                              READY   STATUS    RESTARTS   AGE
metrics-server-8467fcc7b7-j7rl9   1/1     Running   0          34m
  • 3.4 验证Metrics API是否可用
# 检查 Metrics API 是否可用
kubectl get --raw "/apis/metrics.k8s.io/v1beta1"

输出如下:
{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"metrics.k8s.io/v1beta1","resources":[{"name":"nodes","singularName":"","namespaced":false,"kind":"NodeMetrics","verbs":["get","list"]},{"name":"pods","singularName":"","namespaced":true,"kind":"PodMetrics","verbs":["get","list"]}]}
  • 3.5 用kubectl命令 查询 Node集群 和 Pod 的 内存 CPU等指标
# 检查 Node 指标
kubectl top nodes

输出:
NAME             CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)   
docker-desktop   187m         1%       5398Mi          69%   

# 检查 所有命名空间的 Pod指标
kubectl top pods -A

输出:
NAMESPACE                NAME                                                    CPU(cores)   MEMORY(bytes)   
kube-system              coredns-668d6bf9bc-7chdd                                3m           48Mi            
kube-system              coredns-668d6bf9bc-tnx27                                3m           34Mi            
kube-system              etcd-docker-desktop                                     20m          310Mi           
kube-system              kube-apiserver-docker-desktop                           27m          323Mi           
kube-system              kube-controller-manager-docker-desktop                  14m          138Mi           
kube-system              kube-proxy-w8mq6                                        1m           78Mi            
kube-system              kube-scheduler-docker-desktop                           10m          82Mi            
kube-system              metrics-server-8467fcc7b7-j7rl9                         4m           24Mi            
kube-system              storage-provisioner                                     2m           20Mi            
kube-system              vpnkit-controller                                       0m           38Mi            
kubernetes-dashboard     dashboard-metrics-scraper-5bd45c9dd6-q4wml              1m           40Mi            
kubernetes-dashboard     kubernetes-dashboard-79cbcf9fb6-cp4px                   1m           68Mi            
kubernetes-dashboard     kubernetes-dashboard-api-7b6b887946-76ncg               1m           51Mi            
kubernetes-dashboard     kubernetes-dashboard-auth-769bf978b9-tnc8m              1m           36Mi            
kubernetes-dashboard     kubernetes-dashboard-kong-79867c9c48-5sx4b              4m           161Mi           
kubernetes-dashboard     kubernetes-dashboard-metrics-scraper-76df4956c4-k64r9   1m           36Mi            
kubernetes-dashboard     kubernetes-dashboard-web-56df7655d9-rx7hh               1m           36Mi            
wechat-applet-services   service-notification-deployment-7dc7db69d9-hbcn8        0m           79Mi            
wechat-applet-services   service-task-executor-deployment-74c7dbd875-xn5t8       2m           132Mi           
wechat-applet-services   service-thumbnail-deployment-77cfbbf8fd-9gq9v           0m           120Mi    
  • 3.6 dashboard 上也能看到集群和Pod的指标图形了

  

k8s部署一个简单完整的示例


我们使用 httpd (Apache) 镜像 来创建一个完整的示例,包括 Deployment、Service,并将服务暴露到集群外部。

 

  • 创建示例的namespace
kubectl create namespace k8s-study
显示:
namespace/k8s-study created
  • 创建 Deployment
# httpd-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  labels:
    app: httpd
spec:
  replicas: 3  # 创建3个Pod副本
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:alpine  # 使用docker hub 官方httpd镜像(Alpine轻量版)
        ports:
        - containerPort: 80  # Apache默认端口
        resources:
          limits:
            memory: "128Mi"
            cpu: "100m"
应用 Deployment:
kubectl apply -f httpd-deployment.yaml -n k8s-study

显示:
deployment.apps/httpd-deployment created
  • 创建 Service

创建一个 NodePort 类型的 Service 来暴露 Apache 服务:

# httpd-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
spec:
  selector:
    app: httpd  # 匹配Deployment中的label
  ports:
    - protocol: TCP
      port: 80        # Service端口
      targetPort: 80  # Pod端口
  type: NodePort      # 暴露到集群外部
应用 Service:
kubectl apply -f httpd-service.yaml -n k8s-study

显示:
service/httpd-service created
  • 验证部署

检查 Deployment 和 Pod 状态:

kubectl get deployments -n k8s-study

kubectl get pods -n k8s-study
  • 检查 Service 状态并获取分配的 NodePort
kubectl get services httpd-service -n k8s-study

显示:
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
httpd-service   NodePort   10.101.194.247   <none>        80:32636/TCP   2m37s

这里的 32636 就是随机分配的 NodePort(范围 30000-32767)。
  • 从集群外部访问服务
curl http://localhost:32636
  • 通过dashboard也能查看pod状态

通过Docker桌面应用在电脑上安装Kubernetes集群可以说是把我们学习K8s的起步依赖降到了最低,让我们能快速跳过工具安装步骤开始正题的学习,想学K8s的同学们赶紧动手试试吧。