前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >K8s简明实践教程

K8s简明实践教程

作者头像
小炜同学
发布2022-09-23 10:41:33
2.3K0
发布2022-09-23 10:41:33
举报
文章被收录于专栏:Java领域客栈

开篇:Kubernetes是什么以及为什么需要它

Kubernetes是一个可扩展的,用于容器化应用程序编排,管理的平台。由Google于2014年基于其大规模生产实践经验而开源出来的。Kubernetes目前在容器编排领域已经成为事实上的标准,社区也非常活跃。

Kubernetes在国内外都已经得到广泛的应用,无论是Google,Amazon,GitHub等还是国内的阿里,腾讯,百度,华为,京东或其他中小公司等也都已全力推进Kubernetes在生产中的使用。

现在无论是运维,后端,DBA,亦或者是前端,机器学习工程师等都需要在工作中或多或少的用到Docker,而在生产中大量使用的话Kubernetes也将会成为趋势,所以了解或掌握Kubernetes也成为了工程师必不可少的技能之一。

Kubernetes是什么?

当提到Kubernetes的时候,大多数人的可能会想到它可以容器编排,想到它是PaaS(PlatformasaService)系统,但其实不然,Kubernetes并不是PasS系统,因为它工作在容器层而不是硬件层,它只不过提供了一些与PasS类似或者共同的功能,类似部署,扩容,监控,负载均衡,日志记录等。然而它并不是个完全一体化的平台,这些功能基本都是可选可配置的。

Kubernetes可支持公有云,私有云及混合云等,具备良好的可移植性。我们可直接使用它或在其之上构建自己的容器/云平台,以达到快速部署,快速扩展,及优化资源使用等。

它致力于提供通用接口类似CNI( Container Network Interface ), CSI(Container Storage Interface), CRI(Container Runtime Interface)等规范,以便有更多可能,让更多的厂商共同加入其生态体系内。它的目标是希望在以后,任何团队都可以在不修改Kubernetes核心代码的前提下,可以方便的扩展和构建符合自己需求的平台。

为什么需要Kubernetes

我们回到实际的工作环境中。

  • 如果你是个前端,你是否遇到过npm依赖安装极慢,或是nodesass安装不了或者版本不对的情况?
  • 如果你是个后端,是否遇到过服务器与本地环境不一致的情况,导致部分功能出现非预期的情况?
  • 如果你是个运维,是否遇到过频繁部署环境,但中间可能出现各种安装不了或者版本不对的问题?

目前来看,对于这些问题,最好的解决方案便是标准化,容器化,现在用到最多的也就是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探索。(以下统一将Kubernetes简写为K8S)项目组目前就只有一个成员,我们称他为小张。项目组刚成立的时候,小张也没想好,具体要做什么,但肯定要对外提供服务的,所以先向公司申请了一台服务器。

img
img

Node

这台服务器可以用来做什么呢?跑服务,跑数据库,跑测试之类的都可以,我们将它所做的事情统称为工作(work)那么,它便是工作节点(workerNode)对应于K8S中,这就是我们首先要认识的Node

Node可以是一台物理机,也可以是虚拟机,对于我们此处的项目来讲,这台服务器便是K8S中的Node。

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),这样我们的服务器便正式的完成交付。我们来看下其他的部分。

Deployment和Pod

现在小张拿到的服务器已经是可用状态,虽然此时尚不知要具体做什么,但姑且先部署一个主页来宣布下项目组的成立。

我们来看下一般情况下的做法,先写一个静态页面,比如叫index.html然后在服务器上启动一个Nginx或者其他任何Web服务器,来提供对index.html的访问。

Nginx的安装及配置可参考 Nginx的官方文档。最简单的配置大概类似下面这样(仅保留关键部分):

代码语言:javascript
复制
location/{
root/www;
indexindex.html;
}

对于K8S而言,我们想要的,能提供对index.html访问的服务便可理解为Deployment的概念,表明一种我们预期的目标状态。

而对于Nginx和index.html这个组合可以理解为其中的Pod概念,作为最小的调度单元。

ContainerRuntime

虽然此刻部署的内容只有官网,但是为了避免单点故障,于是小张又申请了两台服务器(虽然看起来可能是浪费了点),现在要对原有的服务进行扩容,其实在新的服务器上我们所做的事情也还保持原样,部署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的整体架构,以便于后续在此基础上进行探索和实践。

C/S架构

从更高层来看,K8S整体上遵循C/S架构,从这个角度来看,可用下面的图来表示其结构:

img
img

左侧是一个官方提供的名为Kubectl的CLI(Command Line Interface)工具,用于使用K8S开放的API来管理集群和操作对象等。

右侧则是K8S集群的后端服务及开放出的API等。根据上一节的内容,我们知道Node是用于工作的机器,而Master是一种角色(Role),表示在这个Node上包含着管理集群的一些必要组件。具体组件的详细介绍参考第11小节对各组件的详细剖析。

当然在这里,只画出了一个Master,在生产环境中,为了保障集群的高可用,我们通常会部署多个Master。

Master

下面我们来逐层分解, 首先是 Master ,这里我们只介绍其管理集群的相关组件。Master 是整个 K8S 集群的“大脑”,与大脑类似,它有几个重要的功能:

  • 接收:外部的请求和集群内部的通知反馈
  • 发布:对集群整体的调度和管理
  • 存储:存储

这些功能,也通过一些组件来共同完成,通常情况下,我们将其称为 control plane 。如下图所示:

img
img

它主要包含以下几个重要的组成部分。

Cluster state store

存储集群所有需持久化的状态,并且提供 watch 的功能支持,可以快速的通知各组件的变更等操作。

因为目前 Kubernetes 的存储层选择是 etcd ,所以一般情况下,大家都直接以 etcd 来代表集群状态存储服务。即:将所有状态存储到 etcd 实例中

刚才我们说 Master 相当于是 K8S 集群的大脑,更细化来看,etcd 则是大脑中的核心,为什么这么说?可以参考后面详细剖析的章节,本章我们先从更高的层次来看集群的整体架构。

你可能会问, etcd 是必须的吗?就目前而言,etcd 是必须的,这主要是 Kubernetes 的内部实现。

而早在 2014 年左右,社区就一直在提议将存储层抽象出来,后端的实际存储作为一种插件化的存在。呼声比较大的是另一种提供 k/v 存储功能的 Consul

不过得益于 etcd 的开发团队较为活跃,而且根据 K8S 社区的反馈做了相当大的改进,并且当时 K8S 团队主要的关注点也不在此,所以直到现在 etcd 仍不是一个可选项。

如果现在去看下 Kubernetes 的源代码,你会发现存储层的代码还比较简洁清晰,后续如果有精力也许将此处插件化也不是不可能。

API Server

这是整个集群的入口,类似于人体的感官,接收外部的信号和请求,并将一些信息写入到 etcd 中。

实际处理逻辑比三次握手简单的多:

  • 请求 API Server :“嗨,我有些东西要放到 etcd 里面”
  • API Server 收到请求:“你是谁?我为啥要听你的”
  • 从请求中,拿出自己的身份凭证(一般是证书):“是我啊,你的master,给我把这些东西放进去”
  • 这时候就要看是些什么内容了,如果这些内容 API Server 能理解,那就放入 etcd 中 “好的 master 我放进去了”;如果不能理解,“抱歉 master 我理解不了”

可以看到,它提供了认证相关的功能,用于判断是否有权限进行操作。当然 API Server 支持多种认证方法,不过一般情况下,我们都使用 x509 证书进行认证。

API Server 的目标是成为一个极简的 server,只提供 REST 操作,更新 etcd ,并充当着集群的网关。至于其他的业务逻辑之类的,通过插件或者在其他组件中完成。关于这部分的详细实现,可以参考后面的 API Server 剖析相关章节。

Controller Manager

Controller Manager 大概是 K8S 集群中最繁忙的部分,它在后台运行着许多不同的控制器进程,用来调节集群的状态。

当集群的配置发生变更,控制器就会朝着预期的状态开始工作。

Scheduler

顾名思义,Scheduler 是集群的调度器,它会持续的关注集群中未被调度的 Pod ,并根据各种条件,比如资源的可用性节点的亲和性或者其他的一些限制条件,通过绑定的 API 将 Pod 调度/绑定到 Node 上。

在这个过程中,调度程序一般只考虑调度开始时, Node 的状态,而不考虑在调度过程中 Node 的状态变化 (比如节点亲和性等,截至到目前 v1.11.2 也暂未加入相关功能的稳定特性)

Node

Node 的概念我们在上节已经提过了,这里不再过多赘述,简单点理解为加入集群中的机器即可。

那 Node 是如何加入集群接受调度,并运行服务的呢?这都要归功于运行在 Node 上的几个核心组件。我们先来看下整体结构:

img
img

Kubelet

Kubelet 实现了集群中最重要的关于 Node 和 Pod 的控制功能,如果没有 Kubelet 的存在,那 Kubernetes 很可能就只是一个纯粹的通过 API Server CRUD 的应用程序。

K8S 原生的执行模式是操作应用程序的容器,而不像传统模式那样,直接操作某个包或者是操作某个进程。基于这种模式,可以让应用程序之间相互隔离,互不影响。此外,由于是操作容器,所以应用程序可以说和主机也是相互隔离的,毕竟它不依赖于主机,在任何的容器运行时(比如 Docker)上都可以部署和运行。

我们在上节介绍过 Pod,Pod可以是一组容器(也可以包含存储卷)K8S 将 Pod 作为可调度的基本单位, 分离开了构建时和部署时的关注点

  • 构建时,重点关注某个容器是否能正确构建,如何快速构建
  • 部署时,关心某个应用程序的服务是否可用,是否符合预期,依赖的相关资源是否都能访问到

这种隔离的模式,可以很方便的将应用程序与底层的基础设施解耦,极大的提高集群扩/缩容,迁移的灵活性

在前面,我们提到了 Master 节点的 Scheduler 组件,它会调度未绑定的 Pod 到符合条件的 Node 上,而至于最终该 Pod 是否能运行于 Node 上,则是由 Kubelet 来裁定的。关于 Kubelet 的具体原理,后面有详细剖析的章节。

搭建 Kubernetes 集群 - 本地快速搭建

通过之前的学习,我们已经知道了 K8S 中有一些组件是必须的,集群中有不同的角色。本节,我们在本地快速搭建一个集群,以加深我们学习到的东西。

方案选择

在上一节中,我们知道 K8S 中有多种功能组件,而这些组件要在本地全部搭建好,需要一些基础知识,以及在搭建过程中会浪费不少的时间,从而可能会影响我们正常的搭建集群的目标。

所以,我们这里提供两个最简单,最容易实现我们目标的工具

Minikube

介绍

Minikube 是 K8S 官方为了开发者能在个人电脑上运行 K8S 而提供的一套工具。实现上是通过 Go 语言编写,通过调用虚拟化管理程序,创建出一个运行在虚拟机内的单节点集群。

注:从这里也可以看出,对于 K8S 集群的基本功能而言,节点数并没有什么限制。只有一个节点同样可以创建集群。

前期准备

可在安装Minikube前安装好Docker或Docker Desktop

Mac M1 Max(Arm)

安装
代码语言:javascript
复制
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

Windows

使用PowerShell安装:

代码语言:javascript
复制
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

添加到环境变量

代码语言:javascript
复制
$oldPath = [Environment]::GetEnvironmentVariable('Path', [EnvironmentVariableTarget]::Machine) if ($oldPath.Split(';') -inotcontains 'C:\minikube'){ `   [Environment]::SetEnvironmentVariable('Path', $('{0};C:\minikube' -f $oldPath), [EnvironmentVariableTarget]::Machine) ` } Copy

启动k8s集群

代码语言:javascript
复制
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命令访问新部署的集群:

代码语言:javascript
复制
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

代码语言:javascript
复制
minikube kubectl -- get po -A

也可以在shell中配置别名:

代码语言:javascript
复制
alias kubectl="minikube kubectl --"

为了验证我们的集群目前是否均已配置正确,可以执行以下命令查看:

代码语言:javascript
复制
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:

代码语言:javascript
复制
kubectl get nodes    
NAME       STATUS   ROLES                  AGE   VERSION
minikube   Ready    control-plane,master   50m   v1.23.3

minikube 也搭载了Kubernetes Dashboard(K8s仪表盘),方便我们了解集群配置机运行状况:

代码语言:javascript
复制
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节点启动

img
img
部署应用(以kubectl部署hello-minikube为例)
代码语言:javascript
复制
kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.4
kubectl expose deployment hello-minikube --type=NodePort --port=8080
删除应用
代码语言:javascript
复制
kubectl delete -n default service hello-minikube 
kubectl delete -n default deployment hello-minikube 
查看服务状态
代码语言:javascript
复制
kubectl get services hello-minikube
使用minikube启动浏览器访问
代码语言:javascript
复制
minikube service hello-minikube
使用kubectl启动NodePort端口转发
代码语言:javascript
复制
kubectl port-forward service/hello-minikube 7080:8080

Tada! Your application is now available at http://localhost:7080/.

部署mysql
代码语言:javascript
复制
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 的内部原理。在此基础上,想要在物理机上完全一步步搭建集群,便轻而易举。

集群管理:初识 kubectl

从本节开始,我们来学习 K8S 集群管理相关的知识。通过前面的学习,我们知道 K8S 遵循 C/S 架构,官方也提供了 CLI 工具 kubectl 用于完成大多数集群管理相关的功能。当然凡是你可以通过 kubectl 完成的与集群交互的功能,都可以直接通过 API 完成。

对于我们来说 kubectl 并不陌生,在第 3 章讲 K8S 整体架构时,我们首次提到了它。在第 4 章和第 5 章介绍了两种安装 kubectl 的方式故而本章不再赘述安装的部分。

整体概览

首先我们在终端下执行下 kubectl:

代码语言:javascript
复制
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文件

代码语言:javascript
复制
ls $HOME/.kube/config
/Users/3rock/.kube/config

查看配置文件

代码语言:javascript
复制
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 中主要包含着:

  • K8S 集群的 API 地址
  • 用于认证的证书地址

当然,我们在第 5 章时,也已经说过,也可以使用 --kubeconfig 或者环境变量 KUBECONFIG 来传递配置文件。

另外如果你并不想使用配置文件的话,你也可以通过使用直接传递相关参数来使用,例如:

代码语言:javascript
复制
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

从get说起

无论是第 4 章还是第 5 章,当我们创建集群后,我们都做了两个相同的事情,一个是执行 kubectl get nodes 另一个则是 kubectl cluster-info,我们先从查看集群内 Node 开始。

这里我们使用了一个本地已创建好的 minikube 集群。

代码语言:javascript
复制
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 参数以得到不同格式的输出。

代码语言:javascript
复制
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 进行内容提取。例如:

代码语言:javascript
复制
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 资源及别名和描述等信息。

答疑解惑的命令 explain

当通过上面的命令拿到所有支持的 API 资源列表后,虽然后面基本都有一个简单的说明,是不是仍然感觉一头雾水?

别担心,在我们使用 Linux 的时候,我们有 man ,在使用 kubectl 的时候,我们除了 --help 外还有 explain 可帮我们进行说明。 例如:

代码语言:javascript
复制
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 的部分,即创建。

集群管理:以 Redis 为例-部署及访问

上节我们已经学习 4了本节我们使用kubectl在k8s中进行部署

前面我们已经说过,Pod 是 K8S 中最小的调度单元,所以我们无法直接在 K8S 中运行一个 container 但是我们可以运行一个 Pod 而这个 Pod 中只包含一个 container 。

从kubectl run开始

查看run命令用法

代码语言:javascript
复制
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).

创建pod

代码语言:javascript
复制
# 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

直接创建Deployment

代码语言:javascript
复制
# 创建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用法

代码语言:javascript
复制
minikube kubectl -- create deployment redis --image='redis:alpine'

Deployment

Deployment 是一种高级别的抽象,允许我们进行扩容,滚动更新及降级等操作。我们使用 kubectl run redis --image='redis:alpine 命令便创建了一个名为 redis 的 Deployment,并指定了其使用的镜像为 redis:alpine。

同时 K8S 会默认为其增加一些标签(Label)。我们可以通过更改 get 的输出格式进行查看。

代码语言:javascript
复制
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
代码语言:javascript
复制
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 有什么作用呢?它们可作为选择条件进行使用。如:

代码语言:javascript
复制
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

ReplicaSet 是一种较低级别的结构,允许进行扩容。

我们上面已经提到 Deployment 主要是声明一种预期的状态,并且会将 Pod 托管给 ReplicaSet,而 ReplicaSet 则会去检查当前的 Pod 数量及状态是否符合预期,并尽量满足这一预期。

ReplicaSet 可以由我们自行创建,但一般情况下不推荐这样去做,因为如果这样做了,那其实就相当于跳过了 Deployment 的部分,Deployment 所带来的功能或者特性我们便都使用不到了。

除了 ReplicaSet 外,我们还有一个选择名为 ReplicationController,这两者的主要区别更多的在选择器上,我们后面再做讨论。现在推荐的做法是 ReplicaSet 所以不做太多解释。

ReplicaSet 可简写为 rs,通过以下命令查看:

代码语言:javascript
复制
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

Service 简单点说就是为了能有个稳定的入口访问我们的应用服务或者是一组 Pod。通过 Service 可以很方便的实现服务发现和负载均衡。

代码语言:javascript
复制
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类型

Service 目前有 4 种类型:

ClusterIP: 是 K8S 当前默认的 Service 类型。将 service 暴露于一个仅集群内可访问的虚拟 IP 上。

NodePort: 是通过在集群内所有 Node 上都绑定固定端口的方式将服务暴露出来,这样便可以通过 : 访问服务了。

LoadBalancer: 是通过 Cloud Provider 创建一个外部的负载均衡器,将服务暴露出来,并且会自动创建外部负载均衡器路由请求所需的 Nodeport 或 ClusterIP 。

ExternalName: 是通过将服务由 DNS CNAME 的方式转发到指定的域名上将服务暴露出来,这需要 kube-dns 1.7 或更高版本支持。

Service实践

上面已经说完了 Service 的基本类型,而我们也已经部署了一个 Redis ,当还无法访问到该服务,接下来我们将刚才部署的 Redis 服务暴露出来。

代码语言:javascript
复制
# 发布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 的方式让它可在集群外部访问。

代码语言:javascript
复制
# 通过NodePord转发到6379端口
kubectl port-forward service/redis 6379:6379

在另一个本地终端内可通过 redis-cli 工具进行连接:

代码语言:javascript
复制
redis-cli -h 127.0.0.1 -p 6379

当然,我们也可以使用 NodePort 的方式对外暴露服务。

代码语言:javascript
复制
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

第二节中,我们提到过 Pod 是 K8S 中的最小化部署单元。我们看下当前集群中 Pod 的状态。

代码语言:javascript
复制
kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
mysql-66766b4745-qfx4d   1/1     Running   0          19h
redis-7b76576cd4-bdr47   1/1     Running   0          106m

我们进行一次简单的扩容操作。

代码语言:javascript
复制
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 都已过期,并将在未来的版本移除。

img
img

认证(Authentication)

认证,无非是判断当前发起请求的用户身份是否正确。例如,我们通常登录服务器时候需要输入用户名和密码,或者 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 支持以下认证机制:

  • X509 客户端证书:这个认证机制我们并不陌生,我们前面搭建集群时,虽然没有指定配置文件,但 kubeadm 已经添加了默认参数 --client-ca-file=/etc/kubernetes/pki/ca.crt 而在进行认证时,将会使用客户端证书 subject 的 CN 域(Common Name)用作用户名,O 域(Organization)用作组名。
  • 引导 Token:这个我们也不会陌生,前面我们搭建集群时,当集群通过 kubeadm init 初始化完成后,将会展示一行提示,其中便携带着引导 Token。如果不使用 kubeadm 时,需要设置 --enable-bootstrap-token-auth=true。
  • 静态 Token 文件:启动 Kube-apiserver 时,设置 --token-auth-file=SOMEFILE 并在请求时,加上 Authorization: Bearer TOKEN 的请求头即可。
  • 静态密码文件:与静态 Token 文件类似,设置 --basic-auth-file=SOMEFILE 并在请求时,加上 Authorization: Basic BASE64ENCODED(USER:PASSWORD) 的头即可。
  • Service Account Token:这是默认启用的机制,关于 Service Account 前面也已经介绍过了,不再赘述。
  • OpenID:其实是提供了 OAuth2 的认证支持,像 Azure 或 Google 这类云厂商都提供了相关支持。
  • 认证代理:主要是配合身份验证代理进行使用,比如提供一个通用的授权网关供用户使用。
  • Webhook:提供 Webhook 配合一个远端服务器使用。

可选择同时开启多个认证机制。比如当我们使用 kubeadm 创建集群时,默认便会开启 X509 客户端证书和引导 Token 等认证机制。

授权(Authorization)

授权,也就是在验证当前发起请求的用户是否有相关的权限。例如,我们在 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:通常用于测试,禁止全部。

角色(Role)

上面提到了 RBAC,为了能更好的理解,我们需要先认识下 K8S 中的角色。K8S 中的角色从类别上主要有两类,Role 和 ClusterRole。

Role:可以当作是一组权限的集合,但被限制在某个 Namespace 内(K8S 的 Namespace)。

ClusterRole:对于集群级别的资源是不被 Namespace 所限制的,并且还有一些非资源类的请求,所以便产生了它。

当已经了解到角色后,剩下给用户授权也就只是需要做一次绑定即可。在 K8S 中将这一过程称之为 binding,即 rolebinding 和 clusterrolebinding。 我们来看下集群刚初始化后的情况:

代码语言:javascript
复制
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 。

代码语言:javascript
复制
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
代码语言:javascript
复制
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 中又是什么情况呢?

代码语言:javascript
复制
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

为了演示,这里创建一个新的 NameSpace ,名为 work。

代码语言:javascript
复制
kubectl create namespace work
namespace/work created


kubectl get ns work
NAME   STATUS   AGE
work   Active   24s

创建用户

创建私钥

代码语言:javascript
复制
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上获取;

MAVEN打包

devops-dept在生产环境运行的最终形态是Jar,我们需要通过Maven将源码编译为Jar;

代码语言:javascript
复制
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文件路径为

代码语言:javascript
复制
/Users/3rock/IdeaProjects/devops-dept/target/devops-dept-1.0.0-SNAPSHOT.jar

Dockerfile构建镜像

编写Dockerfile

代码语言:javascript
复制
# 设置镜像基础(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构建镜像

代码语言:javascript
复制
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镜像

代码语言:javascript
复制
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

通过镜像启动容器进行测试

代码语言:javascript
复制
docker run --name devops-dept -p 8080:8080 -d 4wd3rock/devops-dept
6cefce25d6682fedd51b38aadd37d9a97b62e6a36f384297c9a95f338e22b0c3

访问查看

代码语言:javascript
复制
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 的配置文件进行转换。

代码语言:javascript
复制
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

Namespace

这里采用直接编写的方式。同时,我们部署至一个新的名为 work 的 Namespace 中。

代码语言:javascript
复制
cat namespace.yaml
# namespace配置文件
apiVersion: v1
kind: Namespace
metadata:
  name: work


# 通过kubectl创建命名空间
kubectl apply -f namespace.yaml
namespace/work created

Deployment 配置文件

代码语言:javascript
复制
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

通过配置文件进行部署

代码语言:javascript
复制
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 内查看启动日志

代码语言:javascript
复制
kubectl -n work exec -it devops-dept-8d5c7c9bd-2rxf4 bash

Service

代码语言:javascript
复制
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 端口进行访问。

代码语言:javascript
复制
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 中,可以手动编写配置也可以利用一些工具进行辅助。同时也了解到了如何应对应用的扩缩容。

但如果应用需要进行升级的话,则需要去更改配置文件中相关的配置,这个过程会较为繁琐,并且整体项目线上的版本管理也是个问题:比如组件的个人升级,回滚之间如果有依赖的话,会比较麻烦。我们在接下来的两节来学习如何解决这个问题。

你也可以尝试通过命令方式进行部署

代码语言:javascript
复制
kubectl create deployment devops --image= 4wd3rock/devops-dept
kubectl expose deployment devops --type=NodePort --port=8080
kubectl port-forward service/devops 8080:8080

动手实践:搭建一个 Kubernetes 集群 - 生产可用

通过上一节的学习,我们快速的使用 Minikube 搭建了一个本地可用的 K8S 集群。默认情况下,节点是一个虚拟机实例,我们可以在上面体验一些基本的功能。

大多数人的需求并不只是包含一个虚拟机节点的本地测试集群,而是一个可在服务器运行,可自行扩/缩容,具备全部功能的,达到生产可用的集群。

K8S 集群的搭建,一直让很多人头疼,本节我们来搭建一个生产可用的集群,便于后续的学习或使用。

由于缺少Arm服务器集群,有待下一步实现;

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年04月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开篇:Kubernetes是什么以及为什么需要它
    • Kubernetes是什么?
      • 为什么需要Kubernetes
      • 初步认识:Kubernetes基础概念
        • Node
          • Node状态
        • Deployment和Pod
          • ContainerRuntime
            • 总结
            • 宏观认识:总体架构
              • C/S架构
                • Master
                  • Cluster state store
                  • API Server
                  • Controller Manager
                  • Scheduler
                • Node
                  • Kubelet
              • 搭建 Kubernetes 集群 - 本地快速搭建
                • 方案选择
                  • Minikube
                    • 介绍
                    • 前期准备
                    • Mac M1 Max(Arm)
                    • Windows
                    • 启动k8s集群
                  • 总结
                    • 方案选择
                    • 集群管理:初识 kubectl
                      • 整体概览
                        • 基础配置
                          • 从get说起
                            • 答疑解惑的命令 explain
                              • 总结
                              • 集群管理:以 Redis 为例-部署及访问
                                • 从kubectl run开始
                                  • 查看run命令用法
                                  • 创建pod
                                  • 直接创建Deployment
                                  • minikube用法
                                • Deployment
                                  • ReplicaSet
                                    • Service
                                      • Service类型
                                      • Service实践
                                    • Pod
                                      • 总结
                                      • 安全重点: 认证和授权
                                        • 整体概览
                                          • 认证(Authentication)
                                            • 授权(Authorization)
                                              • 角色(Role)
                                                • 查看用户权限
                                              • 实践:创建权限可控的用户
                                                • 创建 NameSpace
                                                • 创建用户
                                            • 应用发布: 部署实际项目
                                              • 整体概览
                                                • MAVEN打包
                                                  • Dockerfile构建镜像
                                                    • 编写配置文件并部署
                                                      • Namespace
                                                      • Deployment 配置文件
                                                      • Service
                                                    • 总结
                                                    • 动手实践:搭建一个 Kubernetes 集群 - 生产可用
                                                    相关产品与服务
                                                    容器服务
                                                    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档