Kubernetes是一个可扩展的,用于容器化应用程序编排,管理的平台。由Google于2014年基于其大规模生产实践经验而开源出来的。Kubernetes目前在容器编排领域已经成为事实上的标准,社区也非常活跃。
Kubernetes在国内外都已经得到广泛的应用,无论是Google,Amazon,GitHub等还是国内的阿里,腾讯,百度,华为,京东或其他中小公司等也都已全力推进Kubernetes在生产中的使用。
现在无论是运维,后端,DBA,亦或者是前端,机器学习工程师等都需要在工作中或多或少的用到Docker,而在生产中大量使用的话Kubernetes也将会成为趋势,所以了解或掌握Kubernetes也成为了工程师必不可少的技能之一。
当提到Kubernetes的时候,大多数人的可能会想到它可以容器编排,想到它是PaaS(PlatformasaService)系统,但其实不然,Kubernetes并不是PasS系统,因为它工作在容器层而不是硬件层,它只不过提供了一些与PasS类似或者共同的功能,类似部署,扩容,监控,负载均衡,日志记录等。然而它并不是个完全一体化的平台,这些功能基本都是可选可配置的。
Kubernetes可支持公有云,私有云及混合云等,具备良好的可移植性。我们可直接使用它或在其之上构建自己的容器/云平台,以达到快速部署,快速扩展,及优化资源使用等。
它致力于提供通用接口类似CNI( Container Network Interface ), CSI(Container Storage Interface), CRI(Container Runtime Interface)等规范,以便有更多可能,让更多的厂商共同加入其生态体系内。它的目标是希望在以后,任何团队都可以在不修改Kubernetes核心代码的前提下,可以方便的扩展和构建符合自己需求的平台。
我们回到实际的工作环境中。
目前来看,对于这些问题,最好的解决方案便是标准化,容器化,现在用到最多的也就是Docker。Docker通过Dockerfile来对环境进行描述,通过镜像进行交付,使用时不再需要关注环境不一致相关的问题。
现在面试的时候,无论前后端,我们总会多问下是否了解或者使用过Docker。如果使用过,那自然会问如果规模变大或者在生产中如何进行容器编排,部署扩容机制如何。
多数人在这个时候都已经回答不上来了,一方面是因为非运维相关岗位的同学,可能在实际工作中并不了解整体的架构体系,没有相关的知识积累。另一方面,对于运维同学可能尚未接触到这部分。
作为一个技术人员,我们应该对整体的体系架构有所了解,掌握更多的技能,了解软件的完整生命周期,包括开发,交付,部署,以及当流量变大时的扩容等。
在容器编排领域,比较著名的主要有三个:Kubernetes,Mesos,及Docker自家的Swarm.对这三者而言,较为简单的是Swarm,因为它本身只专注于容器编排,并且是官方团队所作,从各方面来看,对于新手都相对友好一些。但如果是用于生产中大规模使用,反而就略有不及。
而Mesos也并不仅限于容器编排,它的创建本身是为了将数据中心的所有资源进行抽象,比如CPU,内存,网络,存储等,将整个Mesos集群当作是一个大的资源池,允许各种Framework来进行调度。比如,可以使用Marathon来实现PaaS,可以运行Spark,Hadoop等进行计算等。同时因为它支持比如Docker或者LXC等作资源隔离,所以前几年也被大量用于容器编排。
随着Kubernetes在目前的认可度已经超过Mesos,DockerSwarm等,无疑它是生产环境中容器应用管理的不二之选。
本小册的目标是帮助更多开发者(不局限于运维,后端,前端等)认识并掌握Kubernetes的基础技能,了解其基础架构等。但是Kubernetes涉及知识点很多,且更新迭代很快,本小册集中于使用轻快的文字帮助大家掌握K8S的通用基础技能,对于其中需掌握的关于Docker,及Linux内核相关的知识不会过于深入解释。主要以最常见case入手,帮助大家更快的掌握相关知识并将其用于生产实践中。
好了,总算开始进入正题,抛弃掉死板的说教模式,我们以一个虚构的新成立的项目组为例开始我们的Kubernetes探索。(以下统一将Kubernetes简写为K8S)项目组目前就只有一个成员,我们称他为小张。项目组刚成立的时候,小张也没想好,具体要做什么,但肯定要对外提供服务的,所以先向公司申请了一台服务器。
这台服务器可以用来做什么呢?跑服务,跑数据库,跑测试之类的都可以,我们将它所做的事情统称为工作(work)那么,它便是工作节点(workerNode)对应于K8S中,这就是我们首先要认识的Node。
Node可以是一台物理机,也可以是虚拟机,对于我们此处的项目来讲,这台服务器便是K8S中的Node。
当我们拿到这台服务器后,首先我们登录服务器查看下服务器的基本配置和信息。其实对于一个新加入K8S集群的Node也是一样,需要先检查它的状态,并将状态上报至集群的master。我们来看看服务器有哪些信息是我们所关心的。
首先,我们所关心的是我们服务器的IP地址,包括内网IP和外网IP。对应于K8S集群的话这个概念是类似的,内部IP可在K8S集群内访问,外部IP可在集群外访问。
其次,我们也会关心一下我们的主机名,比如在服务器上执行 hostname 命令,便可得到主机名。K8S集群中,每个Node的主机名也会被记录下来。当然,我们可以通过给Kubelet传递一个--hostname-override的参数来覆盖默认的主机名。(Kubelet是什么,我们后面会解释)
再之后,我们需要看下服务器的基本信息,比如看看系统版本信息, cat /etc/issue 或者 cat /etc/os-release 等方法均可查看。对于K8S集群会将每个Node的这些基础信息都记录下来。
我们通常也都会关注下,我们有几个核心的CPU,可通过 cat /proc/cpuinfo 查看,有多大的内存通过 cat /proc/meminfo 或 free 等查看。对于K8S集群,会默认统计这些信息,并计算在此Node上可调度的Pod数量。(Pod后面做解释)
对于我们拿到的服务器,我们关心上述的一些基本信息,并根据这些信息进行判断,这台机器是否能满足我们的需要。对K8S集群也同样,当判断上述信息均满足要求时候,便将集群内记录的该Node信息标记为Ready ( Ready = True),这样我们的服务器便正式的完成交付。我们来看下其他的部分。
现在小张拿到的服务器已经是可用状态,虽然此时尚不知要具体做什么,但姑且先部署一个主页来宣布下项目组的成立。
我们来看下一般情况下的做法,先写一个静态页面,比如叫index.html然后在服务器上启动一个Nginx或者其他任何Web服务器,来提供对index.html的访问。
Nginx的安装及配置可参考 Nginx的官方文档。最简单的配置大概类似下面这样(仅保留关键部分):
location/{
root/www;
indexindex.html;
}
对于K8S而言,我们想要的,能提供对index.html访问的服务便可理解为Deployment的概念,表明一种我们预期的目标状态。
而对于Nginx和index.html这个组合可以理解为其中的Pod概念,作为最小的调度单元。
虽然此刻部署的内容只有官网,但是为了避免单点故障,于是小张又申请了两台服务器(虽然看起来可能是浪费了点),现在要对原有的服务进行扩容,其实在新的服务器上我们所做的事情也还保持原样,部署Nginx,提供对index.html的访问,甚至配置文件都完全是一样的。可以看到在这种情况下,增加一台服务器,我们需要做一件完全重复的事情。
本着不浪费时间做重复的工作的想法,小张想,要不然用 Ansible 来统一管理服务器操作和配置吧,但考虑到后续服务器上还需要部署其他的服务,常规的这样部署,容易干扰彼此的环境。
所以我们想到了用虚拟化的技术,但是根据一般的经验,类似 KVM 这样的虚拟化技术,可能在资源消耗上较多,不够轻量级。而容器化相对来看,比较轻量级,也比较符合我们的预期,一次构建,随处执行。我们选择当前最热门的Docker.
既然技术选型确定了,那很简单,在我们现在三台服务器上安装Docker,安装过程不再赘述,可以参考 Docker的官方安装文档 。
此时,我们需要做的事情,也便只是将我们的服务构建成一个镜像,需要编写一个Dockerfile,构建一个镜像并部署到每台服务器上便可。
聊了这么多,我们现在已经将我们的服务运行到了容器中,而此处的Docker便是我们选择的容器运行时。选择它的最主要原因,便是为了环境隔离和避免重复工作。
而Docker如果对应于K8S集群中的概念,便是ContainerRuntime,这里还有其他的选择,比如rkt,runc和其他实现了OCI规范的运行时。
在这节里面,我们了解到了Node其实就是用于工作的服务器,它有一些状态和信息,当这些条件都满足一些条件判断时,Node便处于Ready状态,可用于执行后续的工作。
Deployment可理解为一种对期望状态的描述,Pod作为集群中可调度的最小单元,我们会在后面详细讲解其细节。
Docker是我们选择的容器运行时,可运行我们构建的服务镜像,减少在环境方面所做的重复工作,并且也非常便于部署。除了Docker外还存在其他的容器运行时。
了解到这些基本概念后,下节我们从宏观的角度上来认识K8S的整体架构,以便我们后续的学习和实践。
工欲善其事,必先利其器。本节我们来从宏观上认识下K8S的整体架构,以便于后续在此基础上进行探索和实践。
从更高层来看,K8S整体上遵循C/S架构,从这个角度来看,可用下面的图来表示其结构:
左侧是一个官方提供的名为Kubectl的CLI(Command Line Interface)工具,用于使用K8S开放的API来管理集群和操作对象等。
右侧则是K8S集群的后端服务及开放出的API等。根据上一节的内容,我们知道Node是用于工作的机器,而Master是一种角色(Role),表示在这个Node上包含着管理集群的一些必要组件。具体组件的详细介绍参考第11小节对各组件的详细剖析。
当然在这里,只画出了一个Master,在生产环境中,为了保障集群的高可用,我们通常会部署多个Master。
下面我们来逐层分解, 首先是 Master ,这里我们只介绍其管理集群的相关组件。Master 是整个 K8S 集群的“大脑”,与大脑类似,它有几个重要的功能:
这些功能,也通过一些组件来共同完成,通常情况下,我们将其称为 control plane 。如下图所示:
它主要包含以下几个重要的组成部分。
存储集群所有需持久化的状态,并且提供 watch 的功能支持,可以快速的通知各组件的变更等操作。
因为目前 Kubernetes 的存储层选择是 etcd ,所以一般情况下,大家都直接以 etcd 来代表集群状态存储服务。即:将所有状态存储到 etcd 实例中。
刚才我们说 Master 相当于是 K8S 集群的大脑,更细化来看,etcd 则是大脑中的核心,为什么这么说?可以参考后面详细剖析的章节,本章我们先从更高的层次来看集群的整体架构。
你可能会问, etcd 是必须的吗?就目前而言,etcd 是必须的,这主要是 Kubernetes 的内部实现。
而早在 2014 年左右,社区就一直在提议将存储层抽象出来,后端的实际存储作为一种插件化的存在。呼声比较大的是另一种提供 k/v 存储功能的 Consul 。
不过得益于 etcd 的开发团队较为活跃,而且根据 K8S 社区的反馈做了相当大的改进,并且当时 K8S 团队主要的关注点也不在此,所以直到现在 etcd 仍不是一个可选项。
如果现在去看下 Kubernetes 的源代码,你会发现存储层的代码还比较简洁清晰,后续如果有精力也许将此处插件化也不是不可能。
这是整个集群的入口,类似于人体的感官,接收外部的信号和请求,并将一些信息写入到 etcd 中。
实际处理逻辑比三次握手简单的多:
可以看到,它提供了认证相关的功能,用于判断是否有权限进行操作。当然 API Server 支持多种认证方法,不过一般情况下,我们都使用 x509 证书进行认证。
API Server 的目标是成为一个极简的 server,只提供 REST 操作,更新 etcd ,并充当着集群的网关。至于其他的业务逻辑之类的,通过插件或者在其他组件中完成。关于这部分的详细实现,可以参考后面的 API Server 剖析相关章节。
Controller Manager 大概是 K8S 集群中最繁忙的部分,它在后台运行着许多不同的控制器进程,用来调节集群的状态。
当集群的配置发生变更,控制器就会朝着预期的状态开始工作。
顾名思义,Scheduler 是集群的调度器,它会持续的关注集群中未被调度的 Pod ,并根据各种条件,比如资源的可用性,节点的亲和性或者其他的一些限制条件,通过绑定的 API 将 Pod 调度/绑定到 Node 上。
在这个过程中,调度程序一般只考虑调度开始时, Node 的状态,而不考虑在调度过程中 Node 的状态变化 (比如节点亲和性等,截至到目前 v1.11.2 也暂未加入相关功能的稳定特性)
Node 的概念我们在上节已经提过了,这里不再过多赘述,简单点理解为加入集群中的机器即可。
那 Node 是如何加入集群接受调度,并运行服务的呢?这都要归功于运行在 Node 上的几个核心组件。我们先来看下整体结构:
Kubelet 实现了集群中最重要的关于 Node 和 Pod 的控制功能,如果没有 Kubelet 的存在,那 Kubernetes 很可能就只是一个纯粹的通过 API Server CRUD 的应用程序。
K8S 原生的执行模式是操作应用程序的容器,而不像传统模式那样,直接操作某个包或者是操作某个进程。基于这种模式,可以让应用程序之间相互隔离,互不影响。此外,由于是操作容器,所以应用程序可以说和主机也是相互隔离的,毕竟它不依赖于主机,在任何的容器运行时(比如 Docker)上都可以部署和运行。
我们在上节介绍过 Pod,Pod可以是一组容器(也可以包含存储卷),K8S 将 Pod 作为可调度的基本单位, 分离开了构建时和部署时的关注点:
这种隔离的模式,可以很方便的将应用程序与底层的基础设施解耦,极大的提高集群扩/缩容,迁移的灵活性。
在前面,我们提到了 Master 节点的 Scheduler 组件,它会调度未绑定的 Pod 到符合条件的 Node 上,而至于最终该 Pod 是否能运行于 Node 上,则是由 Kubelet 来裁定的。关于 Kubelet 的具体原理,后面有详细剖析的章节。
通过之前的学习,我们已经知道了 K8S 中有一些组件是必须的,集群中有不同的角色。本节,我们在本地快速搭建一个集群,以加深我们学习到的东西。
在上一节中,我们知道 K8S 中有多种功能组件,而这些组件要在本地全部搭建好,需要一些基础知识,以及在搭建过程中会浪费不少的时间,从而可能会影响我们正常的搭建集群的目标。
所以,我们这里提供两个最简单,最容易实现我们目标的工具
Minikube 是 K8S 官方为了开发者能在个人电脑上运行 K8S 而提供的一套工具。实现上是通过 Go 语言编写,通过调用虚拟化管理程序,创建出一个运行在虚拟机内的单节点集群。
注:从这里也可以看出,对于 K8S 集群的基本功能而言,节点数并没有什么限制。只有一个节点同样可以创建集群。
可在安装Minikube前安装好Docker或Docker Desktop
brew install minikube
Running `brew update --preinstall`...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> New Formulae
bkt gst-plugins-rs trzsz
dpp rospo xkcd
==> Updated Formulae
Updated 51 formulae.
==> Downloading https://ghcr.io/v2/homebrew/core/kubernetes-cli/manifests/1.23.4
########################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/kubernetes-cli/blobs/sha256:df6
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
########################################### 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/minikube/manifests/1.25.2
############################################ 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/minikube/blobs/sha256:6dee5f22e
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
############################################# 100.0%
==> Installing dependencies for minikube: kubernetes-cli
==> Installing minikube dependency: kubernetes-cli
==> Pouring kubernetes-cli--1.23.4.arm64_monterey.bottle.tar.gz
???? /opt/homebrew/Cellar/kubernetes-cli/1.23.4: 227 files, 56.3MB
==> Installing minikube
==> Pouring minikube--1.25.2.arm64_monterey.bottle.tar.gz
==> Caveats
zsh completions have been installed to:
/opt/homebrew/share/zsh/site-functions
==> Summary
???? /opt/homebrew/Cellar/minikube/1.25.2: 9 files, 70.3MB
==> Running `brew cleanup minikube`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Caveats
==> minikube
zsh completions have been installed to:
/opt/homebrew/share/zsh/site-functions
使用PowerShell安装:
New-Item -Path 'c:\' -Name 'minikube' -ItemType Directory -Force Invoke-WebRequest -OutFile 'c:\minikube\minikube.exe' -Uri 'https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe' -UseBasicParsing Copy
添加到环境变量
$oldPath = [Environment]::GetEnvironmentVariable('Path', [EnvironmentVariableTarget]::Machine) if ($oldPath.Split(';') -inotcontains 'C:\minikube'){ ` [Environment]::SetEnvironmentVariable('Path', $('{0};C:\minikube' -f $oldPath), [EnvironmentVariableTarget]::Machine) ` } Copy
minikube start
???? Darwin 12.2.1 (arm64) 上的 minikube v1.25.2
✨ 自动选择 docker 驱动
???? Starting control plane node minikube in cluster minikube
???? Pulling base image ...
???? Downloading Kubernetes v1.23.3 preload ...
> preloaded-images-k8s-v17-v1...: 419.07 MiB / 419.07 MiB 100.00% 11.80 Mi
> index.docker.io/kicbase/sta...: 343.12 MiB / 343.12 MiB 100.00% 4.79 MiB
❗ minikube was unable to download gcr.io/k8s-minikube/kicbase:v0.0.30, but successfully downloaded docker.io/kicbase/stable:v0.0.30 as a fallback image
???? Creating docker container (CPUs=2, Memory=1988MB) ...
❗ This container 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/
???? 正在 Docker 20.10.12 中准备 Kubernetes v1.23.3…
▪ kubelet.housekeeping-interval=5m
▪ 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
???? Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
可以直接通过kubectl命令访问新部署的集群:
kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-64897985d-bqknj 1/1 Running 0 2m58s
kube-system etcd-minikube 1/1 Running 0 3m11s
kube-system kube-apiserver-minikube 1/1 Running 0 3m10s
kube-system kube-controller-manager-minikube 1/1 Running 0 3m10s
kube-system kube-proxy-tzm5h 1/1 Running 0 2m58s
kube-system kube-scheduler-minikube 1/1 Running 0 3m10s
kube-system storage-provisioner 1/1 Running 1 (2m27s ago) 3m9s
也可以通过minikube下载kubectl
minikube kubectl -- get po -A
也可以在shell中配置别名:
alias kubectl="minikube kubectl --"
为了验证我们的集群目前是否均已配置正确,可以执行以下命令查看:
kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:52286
CoreDNS is running at https://127.0.0.1:52286/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
查看所有node:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 50m v1.23.3
minikube 也搭载了Kubernetes Dashboard(K8s仪表盘),方便我们了解集群配置机运行状况:
minikube dashboard
???? 正在开启 dashboard ...
▪ Using image kubernetesui/metrics-scraper:v1.0.7
▪ Using image kubernetesui/dashboard:v2.3.1
???? 正在验证 dashboard 运行情况 ...
???? Launching proxy ...
???? 正在验证 proxy 运行状况 ...
???? Opening http://127.0.0.1:52605/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
可以通过面板看到以Minikube为Node节点启动
kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.4
kubectl expose deployment hello-minikube --type=NodePort --port=8080
kubectl delete -n default service hello-minikube
kubectl delete -n default deployment hello-minikube
kubectl get services hello-minikube
minikube service hello-minikube
kubectl port-forward service/hello-minikube 7080:8080
Tada! Your application is now available at http://localhost:7080/.
kubectl create deployment mysql --image=mysql/mysql-server
kubectl expose deployment mysql --type=NodePort --port=3306
kubectl delete -n default service mysql
kubectl delete -n default deployment mysql
本节中,我们为了能更快的体验到 K8S 集群,避免很多繁琐的安装步骤,我们选择了使用官方提供的 Minikube 工具来搭建一个本地集群。
Minikube 的本质其实是将一套 “定制化” 的 K8S 集群打包成 ISO 镜像,当执行 minikube start 的时候,便通过此镜像启动一个虚拟机,在此虚拟机上通过 kubeadm 工具来搭建一套只有一个节点的 K8S 集群。关于 kubeadm 工具,我们在下节进行讲解。
同时,会通过虚拟机的相关配置接口拿到刚才所启动虚拟机的地址信息等,并完成本地的 kubectl 工具的配置,以便于让用户通过 kubectl 工具对集群进行操作。
事实上,当前 Docker for Mac 17.12 CE Edge 和 Docker for Windows 18.02 CE Edge ,以及这两种平台更高的 Edge 版本, 均已内置了对 K8S 的支持,但均为 Edge 版本,此处暂不做过多介绍。
K8S 生产环境可用的集群方案有很多,本节我们选择一个 Kubernetes 官方推荐的方案 kubeadm 进行搭建。
kubeadm 是 Kubernetes 官方提供的一个 CLI 工具,可以很方便的搭建一套符合官方最佳实践的最小化可用集群。当我们使用 kubeadm 搭建集群时,集群可以通过 K8S 的一致性测试,并且 kubeadm 还支持其他的集群生命周期功能,比如升级/降级等。
我们在此处选择 kubeadm ,因为我们可以不用过于关注集群的内部细节,便可以快速的搭建出生产可用的集群。我们可以通过后续章节的学习,快速上手 K8S ,并学习到 K8S 的内部原理。在此基础上,想要在物理机上完全一步步搭建集群,便轻而易举。
从本节开始,我们来学习 K8S 集群管理相关的知识。通过前面的学习,我们知道 K8S 遵循 C/S 架构,官方也提供了 CLI 工具 kubectl 用于完成大多数集群管理相关的功能。当然凡是你可以通过 kubectl 完成的与集群交互的功能,都可以直接通过 API 完成。
对于我们来说 kubectl 并不陌生,在第 3 章讲 K8S 整体架构时,我们首次提到了它。在第 4 章和第 5 章介绍了两种安装 kubectl 的方式故而本章不再赘述安装的部分。
首先我们在终端下执行下 kubectl:
kubectl
kubectl controls the Kubernetes cluster manager.
Find more information at:
https://kubernetes.io/docs/reference/kubectl/overview/
Basic Commands (Beginner):
create Create a resource from a file or from stdin
expose Take a replication controller, service, deployment or pod and
expose it as a new Kubernetes service
run 在集群中运行一个指定的镜像
set 为 objects 设置一个指定的特征
Basic Commands (Intermediate):
explain Get documentation for a resource
get 显示一个或更多 resources
edit 在服务器上编辑一个资源
delete Delete resources by file names, stdin, resources and names, or
by resources and label selector
Deploy Commands:
rollout Manage the rollout of a resource
scale Set a new size for a deployment, replica set, or replication
controller
autoscale Auto-scale a deployment, replica set, stateful set, or
replication controller
Cluster Management Commands:
certificate 修改 certificate 资源.
cluster-info Display cluster information
top Display resource (CPU/memory) usage
cordon 标记 node 为 unschedulable
uncordon 标记 node 为 schedulable
drain Drain node in preparation for maintenance
taint 更新一个或者多个 node 上的 taints
Troubleshooting and Debugging Commands:
describe 显示一个指定 resource 或者 group 的 resources 详情
logs 输出容器在 pod 中的日志
attach Attach 到一个运行中的 container
exec 在一个 container 中执行一个命令
port-forward Forward one or more local ports to a pod
proxy 运行一个 proxy 到 Kubernetes API server
cp Copy files and directories to and from containers
auth Inspect authorization
debug Create debugging sessions for troubleshooting workloads and
nodes
Advanced Commands:
diff Diff the live version against a would-be applied version
apply Apply a configuration to a resource by file name or stdin
patch Update fields of a resource
replace Replace a resource by file name or stdin
wait Experimental: Wait for a specific condition on one or many
resources
kustomize Build a kustomization target from a directory or URL.
Settings Commands:
label 更新在这个资源上的 labels
annotate 更新一个资源的注解
completion Output shell completion code for the specified shell (bash, zsh
or fish)
Other Commands:
alpha Commands for features in alpha
api-resources Print the supported API resources on the server
api-versions Print the supported API versions on the server, in the form of
"group/version"
config 修改 kubeconfig 文件
plugin Provides utilities for interacting with plugins
version 输出 client 和 server 的版本信息
Usage:
kubectl [flags] [options]
Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all
commands).
kubectl 已经将命令做了基本的归类,同时显示了其一般的用法 kubectl [flags] [options] 。
使用 kubectl options 可以看到所有全局可用的配置项。
在我们的home目录,可以看到一个名为 .kube/config 的配置文件,我们来看下其中的内容(此处以本地的 minikube 集群为例)。
首先找到config文件
ls $HOME/.kube/config
/Users/3rock/.kube/config
查看配置文件
cat /Users/3rock/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority: /Users/3rock/.minikube/ca.crt
extensions:
- extension:
last-update: Wed, 02 Mar 2022 13:41:18 CST
provider: minikube.sigs.k8s.io
version: v1.25.2
name: cluster_info
server: https://127.0.0.1:52286
name: minikube
contexts:
- context:
cluster: minikube
extensions:
- extension:
last-update: Wed, 02 Mar 2022 13:41:18 CST
provider: minikube.sigs.k8s.io
version: v1.25.2
name: context_info
namespace: default
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /Users/3rock/.minikube/profiles/minikube/client.crt
client-key: /Users/3rock/.minikube/profiles/minikube/client.key
$HOME/.kube/config 中主要包含着:
当然,我们在第 5 章时,也已经说过,也可以使用 --kubeconfig 或者环境变量 KUBECONFIG 来传递配置文件。
另外如果你并不想使用配置文件的话,你也可以通过使用直接传递相关参数来使用,例如:
kubectl --client-key='/Users/3rock/.minikube/client.key' --client-certificate='/Users/3rock/.minikube/client.crt' --server='https://192.168.49.2:8443' get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 169m v1.23.3
无论是第 4 章还是第 5 章,当我们创建集群后,我们都做了两个相同的事情,一个是执行 kubectl get nodes 另一个则是 kubectl cluster-info,我们先从查看集群内 Node 开始。
这里我们使用了一个本地已创建好的 minikube 集群。
kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 171m v1.23.3
kubectl get node
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 171m v1.23.3
kubectl get no
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 171m v1.23.3
可以看到以上三种“名称”均可获取当前集群内 Node 信息。这是为了便于使用而增加的别名和缩写。
如果我们想要看到更详细的信息呢?可以通过传递 -o 参数以得到不同格式的输出。
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
minikube Ready control-plane,master 172m v1.23.3 192.168.49.2 <none> Ubuntu 20.04.2 LTS 5.10.76-linuxkit docker://20.10.12
当然也可以传递 -o yaml 或者 -o json 得到更加详尽的信息。
使用 -o json 将内容以 JSON 格式输出时,可以配合 jq 进行内容提取。例如:
kubectl get nodes -o json | jq ".items[] | {name: .metadata.name} + .status.nodeInfo"
{
"name": "minikube",
"architecture": "arm64",
"bootID": "8d55b16c-d5df-4622-9a85-aefac5babf93",
"containerRuntimeVersion": "docker://20.10.12",
"kernelVersion": "5.10.76-linuxkit",
"kubeProxyVersion": "v1.23.3",
"kubeletVersion": "v1.23.3",
"machineID": "7f42765e713c4b909dde4d5f15b8d18f",
"operatingSystem": "linux",
"osImage": "Ubuntu 20.04.2 LTS",
"systemUUID": "7f42765e713c4b909dde4d5f15b8d18f"
}
以此方法可得到 Node 的基础信息。
那么除了 Node 外我们还可以查看那些资源或别名呢?可以通过 kubectl api-resources查看服务端支持的 API 资源及别名和描述等信息。
当通过上面的命令拿到所有支持的 API 资源列表后,虽然后面基本都有一个简单的说明,是不是仍然感觉一头雾水?
别担心,在我们使用 Linux 的时候,我们有 man ,在使用 kubectl 的时候,我们除了 --help 外还有 explain 可帮我们进行说明。 例如:
kubectl explain node
KIND: Node
VERSION: v1
DESCRIPTION:
Node is a worker node in Kubernetes. Each node will have a unique
identifier in the cache (i.e. in etcd).
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
Spec defines the behavior of a node.
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
status <Object>
Most recently observed status of the node. Populated by the system.
Read-only. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
本节我们大致介绍了 kubectl 的基础使用,尤其是最常见的 get 命令。可通过传递不同参数获取不同格式的输出,配合 jq 工具可方便的进行内容提取。
以及关于 kubectl 的配置文件和无配置文件下通过传递参数直接使用等。
对应于我们前面提到的 K8S 架构,本节相当于 CURD 中的 R 即查询。查询对于我们来说,既是我们了解集群的第一步,同时也是后续验证操作结果或集群状态必不可少的技能。
当然,你在集群管理中可能会遇到各种各样的问题,单纯依靠 get 并不足以定位问题,我们在第 21 节中将介绍 Troubleshoot 的思路及方法。
下节我们来学习关于 C 的部分,即创建。
上节我们已经学习 4了本节我们使用kubectl在k8s中进行部署
前面我们已经说过,Pod 是 K8S 中最小的调度单元,所以我们无法直接在 K8S 中运行一个 container 但是我们可以运行一个 Pod 而这个 Pod 中只包含一个 container 。
kubectl run --help
Create and run a particular image in a pod.
Examples:
# Start a nginx pod
kubectl run nginx --image=nginx
# Start a hazelcast pod and let the container expose port 5701
kubectl run hazelcast --image=hazelcast/hazelcast --port=5701
# Start a hazelcast pod and set environment variables "DNS_DOMAIN=cluster" and
"POD_NAMESPACE=default" in the container
kubectl run hazelcast --image=hazelcast/hazelcast --env="DNS_DOMAIN=cluster"
--env="POD_NAMESPACE=default"
# Start a hazelcast pod and set labels "app=hazelcast" and "env=prod" in the container
kubectl run hazelcast --image=hazelcast/hazelcast --labels="app=hazelcast,env=prod"
# Dry run; print the corresponding API objects without creating them
kubectl run nginx --image=nginx --dry-run=client
# Start a nginx pod, but overload the spec with a partial set of values parsed from JSON
kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'
# Start a busybox pod and keep it in the foreground, don't restart it if it exits
kubectl run -i -t busybox --image=busybox --restart=Never
# Start the nginx pod using the default command, but use custom arguments (arg1 .. argN) for that
command
kubectl run nginx --image=nginx -- <arg1> <arg2> ... <argN>
# Start the nginx pod using a different command and custom arguments
kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
Options:
--allow-missing-template-keys=true: If true, ignore any errors in templates when a field or
map key is missing in the template. Only applies to golang and jsonpath output formats.
--annotations=[]: Annotations to apply to the pod.
--attach=false: If true, wait for the Pod to start running, and then attach to the Pod as if
'kubectl attach ...' were called. Default false, unless '-i/--stdin' is set, in which case the
default is true. With '--restart=Never' the exit code of the container process is returned.
--cascade='background': Must be "background", "orphan", or "foreground". Selects the deletion
cascading strategy for the dependents (e.g. Pods created by a ReplicationController). Defaults to
background.
--command=false: If true and extra arguments are present, use them as the 'command' field in
the container, rather than the 'args' field which is the default.
--dry-run='none': Must be "none", "server", or "client". If client strategy, only print the
object that would be sent, without sending it. If server strategy, submit server-side request
without persisting the resource.
--env=[]: Environment variables to set in the container.
--expose=false: If true, create a ClusterIP service associated with the pod. Requires
`--port`.
--field-manager='kubectl-run': Name of the manager used to track field ownership.
-f, --filename=[]: to use to replace the resource.
--force=false: If true, immediately remove resources from API and bypass graceful deletion.
Note that immediate deletion of some resources may result in inconsistency or data loss and requires
confirmation.
--grace-period=-1: Period of time in seconds given to the resource to terminate gracefully.
Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true
(force deletion).
--image='': 指定容器要运行的镜像.
--image-pull-policy='': The image pull policy for the container. If left empty, this value
will not be specified by the client and defaulted by the server.
-k, --kustomize='': Process a kustomization directory. This flag can't be used together with -f or
-R.
-l, --labels='': Comma separated labels to apply to the pod. Will override previous values.
--leave-stdin-open=false: If the pod is started in interactive mode or with stdin, leave stdin
open after the first attach completes. By default, stdin will be closed after the first attach
completes.
-o, --output='': Output format. One of:
json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file.
--override-type='merge': The method used to override the generated object: json, merge, or
strategic.
--overrides='': An inline JSON override for the generated object. If this is non-empty, it is
used to override the generated object. Requires that the object supply a valid apiVersion field.
--pod-running-timeout=1m0s: The length of time (like 5s, 2m, or 3h, higher than zero) to wait
until at least one pod is running
--port='': The port that this container exposes.
--privileged=false: If true, run the container in privileged mode.
-q, --quiet=false: If true, suppress prompt messages.
-R, --recursive=false: Process the directory used in -f, --filename recursively. Useful when you
want to manage related manifests organized within the same directory.
--restart='Always': The restart policy for this Pod. Legal values [Always, OnFailure, Never].
--rm=false: If true, delete the pod after it exits. Only valid when attaching to the
container, e.g. with '--attach' or with '-i/--stdin'.
--save-config=false: If true, the configuration of current object will be saved in its
annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to
perform kubectl apply on this object in the future.
--show-managed-fields=false: If true, keep the managedFields when printing objects in JSON or
YAML format.
-i, --stdin=false: Keep stdin open on the container in the pod, even if nothing is attached.
--template='': Template string or path to template file to use when -o=go-template,
-o=go-template-file. The template format is golang templates
[http://golang.org/pkg/text/template/#pkg-overview].
--timeout=0s: The length of time to wait before giving up on a delete, zero means determine a
timeout from the size of the object
-t, --tty=false: Allocate a TTY for the container in the pod.
--wait=false: If true, wait for resources to be gone before returning. This waits for
finalizers.
Usage:
kubectl run NAME --image=image [--env="key=value"] [--port=port] [--dry-run=server|client]
[--overrides=inline-json] [--command] -- [COMMAND] [args...] [options]
Use "kubectl options" for a list of global command-line options (applies to all commands).
# Start a redis pod and set expose port
kubectl run redis --image='redis:alpine' --port=6379
# 查找所有pod、service、deployment、replicaset.apps
kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mysql-66766b4745-qfx4d 1/1 Running 0 17h
pod/redis-7b76576cd4-jwrb4 1/1 Running 0 7m23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
service/mysql NodePort 10.96.39.133 <none> 3306:32042/TCP 17h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1/1 1 1 17h
deployment.apps/redis 1/1 1 1 7m23s
NAME DESIRED CURRENT READY AGE
replicaset.apps/mysql-66766b4745 1 1 1 17h
replicaset.apps/redis-7b76576cd4 1 1 1 7m23s
3rock@4WD3RockdeMBP / % kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mysql-66766b4745-qfx4d 1/1 Running 0 17h
pod/redis-7b76576cd4-jwrb4 1/1 Running 0 13m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
service/mysql NodePort 10.96.39.133 <none> 3306:32042/TCP 17h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1/1 1 1 17h
deployment.apps/redis 1/1 1 1 13m
NAME DESIRED CURRENT READY AGE
replicaset.apps/mysql-66766b4745 1 1 1 17h
replicaset.apps/redis-7b76576cd4 1 1 1 13m
# 删除pod
kubectl delete -n default pod redis
# 创建redis deployment
kubectl create deployment redis --image='redis:alpine'
# 绑定6379端口
kubectl expose deployment redis --type=NodePort --port=6379
# 通过NodePord转发到6379端口
kubectl port-forward service/redis 6379:6379
minikube kubectl -- create deployment redis --image='redis:alpine'
Deployment 是一种高级别的抽象,允许我们进行扩容,滚动更新及降级等操作。我们使用 kubectl run redis --image='redis:alpine 命令便创建了一个名为 redis 的 Deployment,并指定了其使用的镜像为 redis:alpine。
同时 K8S 会默认为其增加一些标签(Label)。我们可以通过更改 get 的输出格式进行查看。
kubectl get deployment.apps/redis -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
redis 1/1 1 1 17m redis redis:alpine app=redis
kubectl get deploy redis -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
redis 1/1 1 1 18m redis redis:alpine app=redis
那么这些 Label 有什么作用呢?它们可作为选择条件进行使用。如:
kubectl get deploy -l app=redis -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
redis 1/1 1 1 22m redis redis:alpine app=redis
我们在应用部署或更新时总是会考虑的一个问题是如何平滑升级,利用 Deployment 也能很方便的进行金丝雀发布(Canary deployments)。这主要也依赖 Label 和 Selector, 后面我们再详细介绍如何实现。
Deployment 的创建除了使用我们这里提到的方式外,更推荐的方式便是使用 yaml 格式的配置文件。在配置文件中主要是声明一种预期的状态,而其他组件则负责协同调度并最终达成这种预期的状态。当然这也是它的关键作用之一,将 Pod 托管给下面将要介绍的 ReplicaSet。
ReplicaSet 是一种较低级别的结构,允许进行扩容。
我们上面已经提到 Deployment 主要是声明一种预期的状态,并且会将 Pod 托管给 ReplicaSet,而 ReplicaSet 则会去检查当前的 Pod 数量及状态是否符合预期,并尽量满足这一预期。
ReplicaSet 可以由我们自行创建,但一般情况下不推荐这样去做,因为如果这样做了,那其实就相当于跳过了 Deployment 的部分,Deployment 所带来的功能或者特性我们便都使用不到了。
除了 ReplicaSet 外,我们还有一个选择名为 ReplicationController,这两者的主要区别更多的在选择器上,我们后面再做讨论。现在推荐的做法是 ReplicaSet 所以不做太多解释。
ReplicaSet 可简写为 rs,通过以下命令查看:
kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
mysql-66766b4745 1 1 1 17h mysql-server mysql/mysql-server app=mysql,pod-template-hash=66766b4745
redis-7b76576cd4 1 1 1 22m redis redis:alpine app=redis,pod-template-hash=7b76576cd4
在输出结果中,我们注意到这里除了我们前面看到的 app=redis 标签外,还多了一个pod-template-hash=7b76576cd4标签,这个标签是由Deployment controller 自动添加的,目的是为了防止出现重复,所以将pod-template 进行 hash 用作唯一性标识。
Service 简单点说就是为了能有个稳定的入口访问我们的应用服务或者是一组 Pod。通过 Service 可以很方便的实现服务发现和负载均衡。
kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h <none>
mysql NodePort 10.96.39.133 <none> 3306:32042/TCP 17h app=mysql
redis NodePort 10.98.50.92 <none> 6379:32480/TCP 26m app=redis
通过使用 kubectl 查看,能看到主要会显示 Service 的名称,类型,IP,端口及创建时间和选择器等。我们来具体拆解下。
Service 目前有 4 种类型:
ClusterIP: 是 K8S 当前默认的 Service 类型。将 service 暴露于一个仅集群内可访问的虚拟 IP 上。
NodePort: 是通过在集群内所有 Node 上都绑定固定端口的方式将服务暴露出来,这样便可以通过 : 访问服务了。
LoadBalancer: 是通过 Cloud Provider 创建一个外部的负载均衡器,将服务暴露出来,并且会自动创建外部负载均衡器路由请求所需的 Nodeport 或 ClusterIP 。
ExternalName: 是通过将服务由 DNS CNAME 的方式转发到指定的域名上将服务暴露出来,这需要 kube-dns 1.7 或更高版本支持。
上面已经说完了 Service 的基本类型,而我们也已经部署了一个 Redis ,当还无法访问到该服务,接下来我们将刚才部署的 Redis 服务暴露出来。
# 发布Service服务
kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server
service/redis-server exposed
# 查找Service服务端点
kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h <none>
mysql NodePort 10.96.39.133 <none> 3306:32042/TCP 18h app=mysql
redis NodePort 10.98.50.92 <none> 6379:32480/TCP 30m app=redis
redis-server ClusterIP 10.103.89.194 <none> 6379/TCP 47s app=redis
通过 kubectl expose 命令将 redis server 暴露出来,这里需要进行下说明:
port: 是 Service 暴露出来的端口,可通过此端口访问 Service。
protocol: 是所用协议。当前 K8S 支持 TCP/UDP 协议,在 1.12 版本中实验性的加入了对 SCTP 协议的支持。默认是 TCP 协议。
target-port: 是实际服务所在的目标端口,请求由 port 进入通过上述指定 protocol 最终流向这里配置的端口。
name: Service 的名字,它的用处主要在 dns 方面。
type: 是前面提到的类型,如果没指定默认是 ClusterIP。
现在我们的 redis 是使用的默认类型 ClusterIP,所以并不能直接通过外部进行访问,我们使用 port-forward 的方式让它可在集群外部访问。
# 通过NodePord转发到6379端口
kubectl port-forward service/redis 6379:6379
在另一个本地终端内可通过 redis-cli 工具进行连接:
redis-cli -h 127.0.0.1 -p 6379
当然,我们也可以使用 NodePort 的方式对外暴露服务。
kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server-nodeport --type=NodePort
service/redis-server-nodeport exposed
# 查看NodePort绑定
kubectl get service/redis-server-nodeport -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
redis-server-nodeport NodePort 10.106.207.199 <none> 6379:30966/TCP 42s app=redis
我们可以通过任意 Node 上的 30966 端口便可连接我们的 redis 服务。当然,这里需要注意的是这个端口范围其实是可以通过 kube-apiserver 的 service-node-port-range 进行配置的,默认是 30000-32767。
第二节中,我们提到过 Pod 是 K8S 中的最小化部署单元。我们看下当前集群中 Pod 的状态。
kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-66766b4745-qfx4d 1/1 Running 0 19h
redis-7b76576cd4-bdr47 1/1 Running 0 106m
我们进行一次简单的扩容操作。
kubectl scale deploy/redis --replicas=2
deployment.apps/redis scaled
kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-66766b4745-qfx4d 1/1 Running 0 19h
redis-7b76576cd4-bdr47 1/1 Running 0 107m
redis-7b76576cd4-rr4p8 1/1 Running 0 20s
可以看到 Pod 数已经增加,并且也已经是 Running 的状态了。(当然在生产环境中 Redis 服务的扩容并不是使用这种方式进行扩容的,需要看实际的部署方式以及业务的使用姿势。)
本节我们使用 Redis 作为例子,学习了集群管理相关的基础知识。学习了如何进行应用部署, Service 的基础类型以及如何通过 port-forward 或 NodePort 等方式将服务提供至集群的外部访问。
同时我们学习了应用部署中主要会涉及到的几类资源 Deployment,Replicaset,Service 和 Pod 等。对这些资源及它们之间关系的掌握,对于后续集群维护或定位问题有很大的帮助。
下节,我们开始学习在生产环境中使用 K8S 至关重要的一环,权限控制。
本节我们将开始学习将 K8S 应用于生产环境中至关重要的一环,权限控制。当然,不仅是 K8S 对于任何应用于生产环境中的系统,权限管理或者说访问控制都是很重要的。
通过前面的学习,我们已经知道 K8S 中几乎所有的操作都需要经过 kube-apiserver 处理,所以为了安全起见,K8S 为它提供了三类安全访问的措施。分别是:用于识别用户身份的认证(Authentication),用于控制用户对资源访问的授权(Authorization)以及用于资源管理方面的准入控制(Admission Control)。
下面的图基本展示了这一过程。来自客户端的请求分别经过认证,授权,准入控制之后,才能真正执行。
当然,这里说基本展示是因为我们可以直接通过 kubectl proxy 的方式直接通过 HTTP 请求访问 kube-apiserver 而无需任何认证过程。
另外,也可通过在 kube-apiserver 所启动的机器上,直接访问启动时 --insecure-port 参数配置的端口进行绕过认证和授权,默认是 8080。为了避免安全问题,也可将此参数设置为 0 以规避问题。注意:这个参数和 --insecure-bind-address 都已过期,并将在未来的版本移除。
认证,无非是判断当前发起请求的用户身份是否正确。例如,我们通常登录服务器时候需要输入用户名和密码,或者 SSH Keys 之类的。
在讲认证前,我们应该先理一下 K8S 中的用户。
K8S 中有两类用户,一般用户及 Service Account。
一般用户:一般用户只能通过外部服务进行管理,由管理员进行私钥分发。这也意味着 K8S 中并没有任何表示一般用户的对象,所以一般用户是无法通过 API 直接添加到集群的。
Service Account:由 K8S API 管理的用户,与特定的 NameSpace(命名空间)绑定。由 API Server 自动创建或者通过 API 手动进行创建。 同时,它会自动挂载到 Pod 中容器的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录中,其中会包含 NameSpace token 等信息,并允许集群内进程与 API Server 进行交互。
对集群操作的 API 都是与用户相关联的,或者被视为匿名请求。匿名请求可通过 kube-apiserver 的 --anonymous-auth 参数进行控制,默认是开启的,匿名用户默认的用户名为 system:anonymous,所属组为 system:unauthenticated。
理完 K8S 中的用户,我们来看下 K8S 中的认证机制。
K8S 支持以下认证机制:
可选择同时开启多个认证机制。比如当我们使用 kubeadm 创建集群时,默认便会开启 X509 客户端证书和引导 Token 等认证机制。
授权,也就是在验证当前发起请求的用户是否有相关的权限。例如,我们在 Linux 系统中常见的文件夹权限之类的。
授权是以认证的结果为基础的,授权机制检查用户通过认证后的请求中所包含的属性来进行判断。
K8S 支持多种授权机制,用户想要正确操作资源,则必须获得授权,所以 K8S 默认情况下的权限都是拒绝。当某种授权机制通过或者拒绝后,便会立即返回,不再去请求其他的授权机制;当所有授权机制都未通过时便会返回 403 错误了。
K8S 支持以下授权机制:
ABAC(Attribute-Based Access Control):基于属性的访问控制,在使用时需要先配置 --authorization-mode=ABAC 和 --authorization-policy-file=SOME_FILENAME 。ABAC 本身设计是非常好的,但是在 K8S 中使用却有点过于繁琐,这里不再赘述。
RBAC(Role-based access control):基于角色的访问控制,自 K8S 1.6 开始 beta,1.8 进入稳定版,已被大量使用。而当我们使用 kubeadm 安装集群的时候,默认将会添加 --authorization-mode=Node,RBAC 的参数,表示同时开启 Node 和 RBAC 授权机制。当然,如果你对 MongoDB 有所了解或者比较熟悉的话,这部分的内容就会很容易理解,因为 MongoDB 的权限控制也使用了 RBAC (Role-based access control)。
Node:这是一种特殊用途的授权机制,专门用于对 kubelet 发出的 API 请求做授权验证。
Webhook:使用外部的 Server 通过 API 进行授权校验,需要在启动时候增加 --authorization-webhook-config-file=SOME_FILENAME 以及 --authorization-mode=Webhook
AlwaysAllow:默认配置,允许全部。
AlwaysDeny:通常用于测试,禁止全部。
上面提到了 RBAC,为了能更好的理解,我们需要先认识下 K8S 中的角色。K8S 中的角色从类别上主要有两类,Role 和 ClusterRole。
Role:可以当作是一组权限的集合,但被限制在某个 Namespace 内(K8S 的 Namespace)。
ClusterRole:对于集群级别的资源是不被 Namespace 所限制的,并且还有一些非资源类的请求,所以便产生了它。
当已经了解到角色后,剩下给用户授权也就只是需要做一次绑定即可。在 K8S 中将这一过程称之为 binding,即 rolebinding 和 clusterrolebinding。 我们来看下集群刚初始化后的情况:
kubectl get roles --all-namespaces=true
NAMESPACE NAME CREATED AT
kube-public kubeadm:bootstrap-signer-clusterinfo 2022-03-02T05:41:17Z
kube-public system:controller:bootstrap-signer 2022-03-02T05:41:16Z
kube-system extension-apiserver-authentication-reader 2022-03-02T05:41:16Z
kube-system kube-proxy 2022-03-02T05:41:17Z
kube-system kubeadm:kubelet-config-1.23 2022-03-02T05:41:16Z
kube-system kubeadm:nodes-kubeadm-config 2022-03-02T05:41:16Z
kube-system system::leader-locking-kube-controller-manager 2022-03-02T05:41:16Z
kube-system system::leader-locking-kube-scheduler 2022-03-02T05:41:16Z
kube-system system:controller:bootstrap-signer 2022-03-02T05:41:16Z
kube-system system:controller:cloud-provider 2022-03-02T05:41:16Z
kube-system system:controller:token-cleaner 2022-03-02T05:41:16Z
kube-system system:persistent-volume-provisioner 2022-03-02T05:41:19Z
kubernetes-dashboard kubernetes-dashboard 2022-03-02T05:45:25Z
可以看到默认已经存在了一些 role 和 rolebindings。 对于这部分暂且不做过多说明,我们来看下对于集群全局有效的 ClusterRole 。
kubectl get clusterroles
NAME CREATED AT
admin 2022-03-02T05:41:15Z
cluster-admin 2022-03-02T05:41:15Z
edit 2022-03-02T05:41:15Z
kubeadm:get-nodes 2022-03-02T05:41:17Z
kubernetes-dashboard 2022-03-02T05:45:25Z
system:aggregate-to-admin 2022-03-02T05:41:16Z
system:aggregate-to-edit 2022-03-02T05:41:16Z
system:aggregate-to-view 2022-03-02T05:41:16Z
system:auth-delegator 2022-03-02T05:41:16Z
system:basic-user 2022-03-02T05:41:15Z
system:certificates.k8s.io:certificatesigningrequests:nodeclient 2022-03-02T05:41:16Z
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 2022-03-02T05:41:16Z
system:certificates.k8s.io:kube-apiserver-client-approver 2022-03-02T05:41:16Z
system:certificates.k8s.io:kube-apiserver-client-kubelet-approver 2022-03-02T05:41:16Z
system:certificates.k8s.io:kubelet-serving-approver 2022-03-02T05:41:16Z
system:certificates.k8s.io:legacy-unknown-approver 2022-03-02T05:41:16Z
system:controller:attachdetach-controller 2022-03-02T05:41:16Z
system:controller:certificate-controller 2022-03-02T05:41:16Z
system:controller:clusterrole-aggregation-controller 2022-03-02T05:41:16Z
system:controller:cronjob-controller 2022-03-02T05:41:16Z
system:controller:daemon-set-controller 2022-03-02T05:41:16Z
system:controller:deployment-controller 2022-03-02T05:41:16Z
system:controller:disruption-controller 2022-03-02T05:41:16Z
system:controller:endpoint-controller 2022-03-02T05:41:16Z
system:controller:endpointslice-controller 2022-03-02T05:41:16Z
system:controller:endpointslicemirroring-controller 2022-03-02T05:41:16Z
system:controller:ephemeral-volume-controller 2022-03-02T05:41:16Z
system:controller:expand-controller 2022-03-02T05:41:16Z
system:controller:generic-garbage-collector 2022-03-02T05:41:16Z
system:controller:horizontal-pod-autoscaler 2022-03-02T05:41:16Z
system:controller:job-controller 2022-03-02T05:41:16Z
system:controller:namespace-controller 2022-03-02T05:41:16Z
system:controller:node-controller 2022-03-02T05:41:16Z
system:controller:persistent-volume-binder 2022-03-02T05:41:16Z
system:controller:pod-garbage-collector 2022-03-02T05:41:16Z
system:controller:pv-protection-controller 2022-03-02T05:41:16Z
system:controller:pvc-protection-controller 2022-03-02T05:41:16Z
system:controller:replicaset-controller 2022-03-02T05:41:16Z
system:controller:replication-controller 2022-03-02T05:41:16Z
system:controller:resourcequota-controller 2022-03-02T05:41:16Z
system:controller:root-ca-cert-publisher 2022-03-02T05:41:16Z
system:controller:route-controller 2022-03-02T05:41:16Z
system:controller:service-account-controller 2022-03-02T05:41:16Z
system:controller:service-controller 2022-03-02T05:41:16Z
system:controller:statefulset-controller 2022-03-02T05:41:16Z
system:controller:ttl-after-finished-controller 2022-03-02T05:41:16Z
system:controller:ttl-controller 2022-03-02T05:41:16Z
system:coredns 2022-03-02T05:41:17Z
system:discovery 2022-03-02T05:41:15Z
system:heapster 2022-03-02T05:41:16Z
system:kube-aggregator 2022-03-02T05:41:16Z
system:kube-controller-manager 2022-03-02T05:41:16Z
system:kube-dns 2022-03-02T05:41:16Z
system:kube-scheduler 2022-03-02T05:41:16Z
system:kubelet-api-admin 2022-03-02T05:41:16Z
system:monitoring 2022-03-02T05:41:15Z
system:node 2022-03-02T05:41:16Z
system:node-bootstrapper 2022-03-02T05:41:16Z
system:node-problem-detector 2022-03-02T05:41:16Z
system:node-proxier 2022-03-02T05:41:16Z
system:persistent-volume-provisioner 2022-03-02T05:41:16Z
system:public-info-viewer 2022-03-02T05:41:15Z
system:service-account-issuer-discovery 2022-03-02T05:41:16Z
system:volume-scheduler 2022-03-02T05:41:16Z
view 2022-03-02T05:41:15Z
kubectl get clusterrolebindings
NAME ROLE AGE
cluster-admin ClusterRole/cluster-admin 26h
kubeadm:get-nodes ClusterRole/kubeadm:get-nodes 26h
kubeadm:kubelet-bootstrap ClusterRole/system:node-bootstrapper 26h
kubeadm:node-autoapprove-bootstrap ClusterRole/system:certificates.k8s.io:certificatesigningrequests:nodeclient 26h
kubeadm:node-autoapprove-certificate-rotation ClusterRole/system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 26h
kubeadm:node-proxier ClusterRole/system:node-proxier 26h
kubernetes-dashboard ClusterRole/cluster-admin 25h
minikube-rbac ClusterRole/cluster-admin 26h
storage-provisioner ClusterRole/system:persistent-volume-provisioner 26h
system:basic-user ClusterRole/system:basic-user 26h
system:controller:attachdetach-controller ClusterRole/system:controller:attachdetach-controller 26h
system:controller:certificate-controller ClusterRole/system:controller:certificate-controller 26h
system:controller:clusterrole-aggregation-controller ClusterRole/system:controller:clusterrole-aggregation-controller 26h
system:controller:cronjob-controller ClusterRole/system:controller:cronjob-controller 26h
system:controller:daemon-set-controller ClusterRole/system:controller:daemon-set-controller 26h
system:controller:deployment-controller ClusterRole/system:controller:deployment-controller 26h
system:controller:disruption-controller ClusterRole/system:controller:disruption-controller 26h
system:controller:endpoint-controller ClusterRole/system:controller:endpoint-controller 26h
system:controller:endpointslice-controller ClusterRole/system:controller:endpointslice-controller 26h
system:controller:endpointslicemirroring-controller ClusterRole/system:controller:endpointslicemirroring-controller 26h
system:controller:ephemeral-volume-controller ClusterRole/system:controller:ephemeral-volume-controller 26h
system:controller:expand-controller ClusterRole/system:controller:expand-controller 26h
system:controller:generic-garbage-collector ClusterRole/system:controller:generic-garbage-collector 26h
system:controller:horizontal-pod-autoscaler ClusterRole/system:controller:horizontal-pod-autoscaler 26h
system:controller:job-controller ClusterRole/system:controller:job-controller 26h
system:controller:namespace-controller ClusterRole/system:controller:namespace-controller 26h
system:controller:node-controller ClusterRole/system:controller:node-controller 26h
system:controller:persistent-volume-binder ClusterRole/system:controller:persistent-volume-binder 26h
system:controller:pod-garbage-collector ClusterRole/system:controller:pod-garbage-collector 26h
system:controller:pv-protection-controller ClusterRole/system:controller:pv-protection-controller 26h
system:controller:pvc-protection-controller ClusterRole/system:controller:pvc-protection-controller 26h
system:controller:replicaset-controller ClusterRole/system:controller:replicaset-controller 26h
system:controller:replication-controller ClusterRole/system:controller:replication-controller 26h
system:controller:resourcequota-controller ClusterRole/system:controller:resourcequota-controller 26h
system:controller:root-ca-cert-publisher ClusterRole/system:controller:root-ca-cert-publisher 26h
system:controller:route-controller ClusterRole/system:controller:route-controller 26h
system:controller:service-account-controller ClusterRole/system:controller:service-account-controller 26h
system:controller:service-controller ClusterRole/system:controller:service-controller 26h
system:controller:statefulset-controller ClusterRole/system:controller:statefulset-controller 26h
system:controller:ttl-after-finished-controller ClusterRole/system:controller:ttl-after-finished-controller 26h
system:controller:ttl-controller ClusterRole/system:controller:ttl-controller 26h
system:coredns ClusterRole/system:coredns 26h
system:discovery ClusterRole/system:discovery 26h
system:kube-controller-manager ClusterRole/system:kube-controller-manager 26h
system:kube-dns ClusterRole/system:kube-dns 26h
system:kube-scheduler ClusterRole/system:kube-scheduler 26h
system:monitoring ClusterRole/system:monitoring 26h
system:node ClusterRole/system:node 26h
system:node-proxier ClusterRole/system:node-proxier 26h
system:public-info-viewer ClusterRole/system:public-info-viewer 26h
system:service-account-issuer-discovery ClusterRole/system:service-account-issuer-discovery 26h
system:volume-scheduler ClusterRole/system:volume-scheduler 26h
可以看到 K8S 中默认已经有很多的 ClusterRole 和 clusterrolebindings 了,我们选择其中一个做下探究。
我们一直都在使用 kubectl 对集群进行操作,那么当前用户是什么权限呢? 对应于 RBAC 中又是什么情况呢?
kubectl config current-context # 获取当前上下文
minikube
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority: /Users/3rock/.minikube/ca.crt
extensions:
- extension:
last-update: Thu, 03 Mar 2022 14:22:03 CST
provider: minikube.sigs.k8s.io
version: v1.25.2
name: cluster_info
server: https://127.0.0.1:59983
name: minikube
contexts:
- context:
cluster: minikube
extensions:
- extension:
last-update: Thu, 03 Mar 2022 14:22:03 CST
provider: minikube.sigs.k8s.io
version: v1.25.2
name: context_info
namespace: default
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /Users/3rock/.minikube/profiles/minikube/client.crt
client-key: /Users/3rock/.minikube/profiles/minikube/client.key
前面是通过实际用户来反推它所具备的权限,接下来我们开始实践的部分,创建用户并为它进行授权。
我们要创建的用户名为 backend 所属组为 dev。
为了演示,这里创建一个新的 NameSpace ,名为 work。
kubectl create namespace work
namespace/work created
kubectl get ns work
NAME STATUS AGE
work Active 24s
创建私钥
mkdir work
cd work
openssl genrsa -out backend.key 2048
Generating RSA private key, 2048 bit long modulus
..........................................+++
........................+++
e is 65537 (0x10001)
ls
backend.key
cat backend.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAzk7blZthwSzachPxrk6pHsuaImTVh6Iw8mNDmtn6sqOqBfZS ...
bNKDWDk8HZREugaOAwjt7xaWOlr9SPCCoXrWoaA1z2215IC4qSA2Nw==
-----END RSA PRIVATE KEY-----
使用私钥生成证书请求。前面已经讲过关于认证的部分,在这里需要指定 subject 信息,传递用户名和组名
本节我们开始学习如何将实际项目部署至 K8S 中,开启生产实践之路。
本节所用示例项目是一个典型的SpringBoot项目(devops-dept),与自然资源交易部浏览服务类似,集成了Hutool-db及Redis,主要实现的功能是Sql查询及缓存;项目代码可在Gitlab上获取;
devops-dept在生产环境运行的最终形态是Jar,我们需要通过Maven将源码编译为Jar;
mvn clean package
/Users/3rock/Library/Java/JavaVirtualMachines/azul-1.8.0_312/Contents/Home/bin/java -Dmaven.multiModuleProjectDirectory=/Users/3rock/IdeaProjects/devops-dept -Dmaven.home=/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven3 -Dclassworlds.conf=/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven3/bin/m2.conf -Dmaven.ext.class.path=/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven-event-listener.jar -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=49785:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds.license:/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.6.0.jar org.codehaus.classworlds.Launcher -Didea.version=2021.3.1 --update-snapshots clean package
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< cn.devops:devops-dept >------------------------
[INFO] Building devops-dept 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ devops-dept ---
[INFO] Deleting /Users/3rock/IdeaProjects/devops-dept/target
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ devops-dept ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ devops-dept ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 14 source files to /Users/3rock/IdeaProjects/devops-dept/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ devops-dept ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory /Users/3rock/IdeaProjects/devops-dept/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ devops-dept ---
[INFO] Changes detected - recompiling the module!
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ devops-dept ---
[INFO]
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ devops-dept ---
[INFO] Building jar: /Users/3rock/IdeaProjects/devops-dept/target/devops-dept-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.6.2:repackage (repackage) @ devops-dept ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.520 s
[INFO] Finished at: 2022-03-04T09:42:46+08:00
[INFO] ------------------------------------------------------------------------
可以看到打包的Jar文件路径为
/Users/3rock/IdeaProjects/devops-dept/target/devops-dept-1.0.0-SNAPSHOT.jar
编写Dockerfile
# 设置镜像基础(Arm架构需要查看官方镜像是否支持aarch64)
FROM openjdk:11
# 维护人员信息(官方文档已标识MAINTAINER指令过期)
LABEL org.opencontainers.image.authors="4WD3Rock"
# 设置镜像对外暴露端口
EXPOSE 8080
# 设置容器时区与宿主机同步
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 将当前 target 目录下的 jar 放置在根目录下,命名为 app.jar,推荐使用绝对路径(使用到上一步打包的Jar文件)
ADD target/devops-dept-1.0.0-SNAPSHOT.jar /devops-dept-1.0.0-SNAPSHOT.jar
# 执行启动命令
ENTRYPOINT java ${JVM:=-Xms1024m -Xmx1024m} -jar /devops-dept-1.0.0-SNAPSHOT.jar
通过Dockerfile构建镜像
docker build -t 4wd3rock/devops-dept .
[+] Building 1.6s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 565B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11 0.0s
=> [internal] load build context 1.4s
=> => transferring context: 51.56MB 1.3s
=> [1/3] FROM docker.io/library/openjdk:11 0.0s
=> CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/PRC /etc/localtime && echo PRC > /etc/timezone 0.0s
=> [3/3] ADD target/devops-dept-1.0.0-SNAPSHOT.jar /devops-dept-1.0.0-SNAPSHOT.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:d6cd40d45bdd19f97c9029648e66b4391c1e32f9f98fb84c5347080dca41ffb6 0.0s
=> => naming to docker.io/4wd3rock/devops-dept 0.0s
查看本地Docker镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
4wd3rock/devops-dept latest d6cd40d45bdd 2 minutes ago 701MB
kicbase/stable v0.0.30 6a29e77b4fe6 3 weeks ago 1.04GB
openjdk 11 48e4690d429d 5 weeks ago 649MB
mysql/mysql-server latest 0f37da883ef8 6 weeks ago 468MB
redis latest f16c30136ff3 2 months ago 107MB
alpine latest 8e1d7573f448 3 months ago 5.33MB
通过镜像启动容器进行测试
docker run --name devops-dept -p 8080:8080 -d 4wd3rock/devops-dept
6cefce25d6682fedd51b38aadd37d9a97b62e6a36f384297c9a95f338e22b0c3
访问查看
curl -i -H "Accept: application/json" -X GET "http://localhost:8080/findEmployeeListByName?firstName=Armond"
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked
Content-Type: application/json
Date: Fri, 04 Mar 2022 01:58:58 GMT
在 K8S 中进行部署或者说与 K8S 交互的方式主要有三种:
第 7 节介绍过的 kubectl create deployment redis --image='redis:alpine' 这种方式便是命令式,这种方式很简单,但是可重用性低。毕竟你的命令执行完后,其他人也并不清楚到底发生了什么。
命令式对象配置,主要是编写配置文件,但是通过类似 kubectl create 之类命令式的方式进行操作。
再有一种便是声明式对象配置,主要也是通过编写配置文件,但是使用 kubectl apply 之类的放好似进行操作。与第二种命令式对象配置的区别主要在于对对象的操作将会得到保留。但同时这种方式有时候也并不好进行调试。
接下来,为 saythx 项目编写配置文件,让它可以部署至 K8S 中。当然,如果已经创建过了 docker-compose.yml 的配置文件,并且也验证了其可用性,可以直接使用 Kompose 工具将 docker-compose.yml 的配置文件进行转换。
kompose convert -f docker-compose.yaml
INFO Kubernetes file "frontend-service.yaml" created
INFO Kubernetes file "redis-master-service.yaml" created
INFO Kubernetes file "redis-slave-service.yaml" created
INFO Kubernetes file "frontend-deployment.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created
INFO Kubernetes file "redis-slave-deployment.yaml" created
这里采用直接编写的方式。同时,我们部署至一个新的名为 work 的 Namespace 中。
cat namespace.yaml
# namespace配置文件
apiVersion: v1
kind: Namespace
metadata:
name: work
# 通过kubectl创建命名空间
kubectl apply -f namespace.yaml
namespace/work created
cat devops-dept.yaml
# deployment配置文件
apiVersion: apps/v1
# 指定采用Deployment部署,这里我们创建的是一个 Pod,当然根据你的实际情况,这里资源类型可以是 Deployment、 Job、Ingress、Service 等。
kind: Deployment
# 元数据定义,包含了我们定义的 Pod 的一些 meta 信息,比如名称、namespace、标签等等信息
metadata:
labels:
app: devops
name: devops-dept
namespace: work
# 包括一些 containers,storage,volumes,或者其他 Kubernetes 需要知道的参数,以及诸 如是否在容器失败时重新启动容器的属性。你可以在特定 Kubernetes API 找到完整的
spec:
selector:
matchLabels:
app: devops
# 创建3个pod副本
replicas: 3
template:
metadata:
labels:
app: devops
spec:
# 设定容器镜像
containers:
- image: 4wd3rock/devops-dept
name: devops-dept
ports:
- containerPort: 8080
通过配置文件进行部署
kubectl apply -f devops-dept.yaml
deployment.apps/devops-dept created
# 指定命名空间查询部署情况
kubectl -n work get all
NAME READY STATUS RESTARTS AGE
pod/devops-dept-8d5c7c9bd-2rxf4 1/1 Running 0 26s
pod/devops-dept-8d5c7c9bd-8wx44 1/1 Running 0 26s
pod/devops-dept-8d5c7c9bd-n7825 1/1 Running 0 26s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/devops-dept 3/3 3 3 26s
NAME DESIRED CURRENT READY AGE
replicaset.apps/devops-dept-8d5c7c9bd 3 3 3 26s
可以看到 Pod 已经在正常运行了。我们进入 Pod 内查看启动日志
kubectl -n work exec -it devops-dept-8d5c7c9bd-2rxf4 bash
cat devops-dept-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: devops
name: devops-dept
namespace: work
spec:
ports:
- protocol: TCP
port: 8080
targetPort: 8080
selector:
app: devops
type: NodePort
kubectl apply -f devops-dept-service.yaml
service/devops-dept created
kubectl get svc -n work
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
devops-dept NodePort 10.98.50.217 <none> 8080:32538/TCP 41s
现在均已经部署完成。并且可直接通过 Node 端口进行访问。
kubectl port-forward service/devops 8080:8080
curl -i -H "Accept: application/json" -X GET "http://localhost:8080/findEmployeeListByName?firstName=Armond"
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked
Content-Type: application/json
Date: Fri, 04 Mar 2022 01:58:58 GMT
通过本节的学习,我们已经学习到了如何将项目实际部署至 K8S 中,可以手动编写配置也可以利用一些工具进行辅助。同时也了解到了如何应对应用的扩缩容。
但如果应用需要进行升级的话,则需要去更改配置文件中相关的配置,这个过程会较为繁琐,并且整体项目线上的版本管理也是个问题:比如组件的个人升级,回滚之间如果有依赖的话,会比较麻烦。我们在接下来的两节来学习如何解决这个问题。
你也可以尝试通过命令方式进行部署
kubectl create deployment devops --image= 4wd3rock/devops-dept
kubectl expose deployment devops --type=NodePort --port=8080
kubectl port-forward service/devops 8080:8080
通过上一节的学习,我们快速的使用 Minikube 搭建了一个本地可用的 K8S 集群。默认情况下,节点是一个虚拟机实例,我们可以在上面体验一些基本的功能。
大多数人的需求并不只是包含一个虚拟机节点的本地测试集群,而是一个可在服务器运行,可自行扩/缩容,具备全部功能的,达到生产可用的集群。
K8S 集群的搭建,一直让很多人头疼,本节我们来搭建一个生产可用的集群,便于后续的学习或使用。
由于缺少Arm服务器集群,有待下一步实现;