kubernetes 基础总结
注: 文章中的部分图片及部分内容借鉴了《Kubernetes 从上手到实践》和《云原生技术公开课》,若有侵权,请告知删除,谢谢!
简单介绍
kubernetes简称k8s(后续大多以k8s代替说明),是一个用于对容器化应用进行编排、管理的工具。由Google于2014年开源。经过几年沉淀,k8s已成为容器编排领域事实上的标准。
从定义看出k8s是对容器进行编排(编排在这里可理解为部署、扩容、监控、负载均衡、日志记录等)的。因其操作对象为容器,故这里对容器进行简要说明。
容器
在实际工作环境中,经常会由于在搭建服务应用时因为应用的依赖、部署的环境以及应用的迁移遇到各种各样的问题,可见对这些服务的环境进行隔离打包显得非常重要,故容器应运而生。因此我们可以将容器定义为 视图受限、资源隔离的一组进程集合 。而组成这些容器的文件集合则称为该容器的镜像。目前来看,在标准化、容器化做的最好的就是Docker。故接下来我们只说k8s对Docker下的容器编排管理。
初步了解
Node
我们知道k8s是对容器进行管理的,而许许多多不同的容器常常是在多个机器(或称为服务器)上运行的。若k8s只能在一台机器上进行管理,则它的效用显然违背它的目标。故k8s经常是在一个集群下工作的。即k8s是对多台机器进行管理。这里的每台机器我们称为每个Node(即节点),这些Node里面肯定有一个Node是作为主节点来对其他工作节点(work node)进行管理的。其管理结构类似于 master-slave 模式。
Pod
k8s规定其最小的调度单元是Pod,k8s并不是直接操纵容器的。容器都被放入到了Pod当中,而k8s通过管理Pod来管理容器,关于为什么要用Pod并作为原子调度单位可参考这里。故我们可以简单理解Pod是一个或一组容器的集合。
Deployment
k8s中并不是直接对Pod进行管理的,而是依靠Deployment来解决。这是为了保证集群内一组Pod能维持指定的数量,并使得该组Pod能一起更新镜像版本、快速回滚。故我们知道Deployment是用来对Pod进行管理的。Deployment虽然能创建Pod,但创建Pod的副本数是由Deployment下的ReplicaSet管理的。ReplicaSet用来使Pod的副本数达到用户期望的状态。创建Pod的语法(通常以yaml文件进行创建)基本信息如下:
整体结构
从更高层来看K8S整体上遵循C/S架构,结构如下图:
![cs架构]](https://img-blog.csdnimg.cn/20191230191401978.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpYW9iYWlfb2w=,size_16,color_FFFFFF,t_70)
可以看到其很像master-slave结构
master
mater内部由以下几部分组成:
master是k8s集群的“大脑”(通常称为control plane),其负责对集群进行调度、管理与存储等。
API Server
接收外部的信号与请求,并将一些信息写入到etcd中。对外部的请求做一些权限管理与认证。其以REST方式实现。
Controller
是k8s集群中最繁忙的部分,在后台控制各种进程,对集群进行调节管理等。
Scheduler
是集群的调度器,持续关注集群中未被调度的Pod,并根据条件来将Pod调度到对应的Node上。
etcd
存储集群的状态日志等信息,供API Server调用。
Node
Node节点的内部组成如下:
Kubelet
Kubelet实现了集群中Node节点的Pod的控制功能。当一个未被创建的Pod被Scheduler调度到符合条件的工作节点上后,将由kubelet来决定该Pod是否能运行在该Node上。
Container runtime
Container runtime 主要用来下载镜像并创建对应的容器。它的实现最常见的就是Docker。
Kube Proxy
我们都知道,想要访问某个服务,要么通过域名,要么通过 IP。而每个 Pod 在创建后都会有一个虚拟 IP,K8S 中有一个抽象的概念,叫做 Service ,kube-proxy 便是提供一种代理的服务,让你可以通过 Service 访问到 Pod。
安装
因k8s是对Docker下的容器进行管理的,故需要机器先安装Docker环境,因Docker在这里不是讲解的重点,故Docker的安装步骤在这里省略(离线安装方法可参考这篇文章)。接下来就是安装k8s环境,若是只想对k8s做一个了解,可只搭建实验环境进行体验。
实验环境
Kind
kind内部集成了k8s,并将k8s的相关命令进行了封装。关于kind的安装与使用可直接参考该项目主页下的说明文档。
Minikube
minikube是k8s官方为开发者提供的一套k8s体验工具。该工具安装前一般需要安装虚拟化管理程序如VirtualBox(若是不安装则minikube运行时将–vm-driver参数设置为none)。为了在创建k8s集群后我们能观察集群节点、pod的状态以及对集群进行管理,这里还需要安装 kubectl 工具。前面已经了解到k8s是典型的C/S架构,而kubectl就是CLI工具,kubectl的安装参考 这里。minikube的安装与使用可直接参考项目文档。minikube的好处在于不用关注太多安装方面的过程,直接在终端下输入 minikube dashboard
就能在浏览器中通过系统界面查看集群状态。如下图:
生产环境
通常情况下,我们不只是搭建一个测试环境,而是需要一个生产可用、功能完备的集群环境。k8s生产环境的搭建方案有多种,这里我们选择官方的一个推荐方案使用 kubeadm 进行搭建。
前期准备
安装之前需要做一些准备,如下:
- 禁用swap
自k8s 1.8之后,启动kubelet时,需要禁用swap,禁用的方法如下:
使用 sudo cat /proc/swaps 验证 swap 配置的设备和文件。
通过 swapoff -a 关闭 swap 。
使用 sudo blkid 或者 sudo lsblk 可查看到我们的设备属性,请注意输出结果中带有 swap 字样的信息。
将 /etc/fstab 中和上一条命令中输出的,和 swap 相关的挂载点都删掉,以免在机器重启或重挂载时,再挂载 swap 分区。
- 解除端口占用
k8s是C/S架构,在启动后会固定监听一些端口用于提供服务,可以通过sudo netstat -ntlp |grep -E '6443|23[79,80]|1025[0,1,2]'
命令来查看这些端口是否被占用,若占用,则手动释放。 - Docker安装
前面说到,容器运行时,需要Docker,因此需要安装Docker。一般直接选择最新稳定版安装即可。
安装kubeadm和kubelet
安装之前,需要了解要安装的版本,通过以下命令(要保证linux机器能上网,k8s要在root环境下安装)获取版本号:
curl -sSL https://dl.k8s.io/release/stable.txt
v1.16.3
下载对应的二进制包,进行安装:
[aaa@qq.com ~]# curl -sSL https://dl.k8s.io/release/v1.16.3/bin/linux/amd64/kubeadm > /usr/bin/kubeadm
[aaa@qq.com ~]# chmod a+rx /usr/bin/kubeadm
[aaa@qq.com ~]# kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"clean", BuildDate:"2019-11-13T11:20:25Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
kubeadm至此已经安装完成,下来就是kubelet的安装了,运行如下命令:
[aaa@qq.com tmp]# wget -q https://dl.k8s.io/v1.16.3/kubernetes-server-linux-amd64.tar.gz
[aaa@qq.com tmp]# tar -zxf kubernetes-server-linux-amd64.tar.gz
[aaa@qq.com tmp]# ls kubernetes
addons kubernetes-src.tar.gz LICENSES server
[aaa@qq.com tmp]# ls kubernetes/server/bin/ | grep -E 'kubeadm|kubelet|kubectl'
kubeadm
kubectl
kubelet
可以看到在server/bin目录下已有所需的内容,将 kubeadm kubectl kubelet移动到 /usr/bin/目录下:
[aaa@qq.com tmp]# mv kubernetes/server/bin/kube{adm,ctl,let} /usr/bin/
[aaa@qq.com tmp]# ls /usr/bin/kube*
/usr/bin/kubeadm /usr/bin/kubectl /usr/bin/kubelet
配置
为了在生产环境中稳定运行,下面增加对kubelet的systemd的配置:
[aaa@qq.com tmp]# cat <<'EOF' > /etc/systemd/system/kubelet.service
[Unit]
Description=kubelet: The Kubernetes Agent
Documentation=http://kubernetes.io/docs/
[Service]
ExecStart=/usr/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
[aaa@qq.com tmp]# mkdir -p /etc/systemd/system/kubelet.service.d
[aaa@qq.com tmp]# cat <<'EOF' > /etc/systemd/system/kubelet.service.d/kubeadm.conf
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
EOF
[aaa@qq.com tmp]# systemctl enable kubelet
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.
启动
启动之前,还需要安装两个工具 crictl 和 socat, crictl 是 kubelet CRI (Container Runtime Interface) 的 CLI。安装可以进入crl-tools的Release页面选择对应版本下载即可:
[aaa@qq.com ~]# wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.17.0/crictl-v1.17.0-linux-amd64.tar.gz
[aaa@qq.com ~]# echo 'ccf83574556793ceb01717dc91c66b70f183c60c2bbec70283939aae8fdef768 crictl-v1.11.1-linux-amd64.tar.gz' | sha256sum -c -
crictl-v1.11.1-linux-amd64.tar.gz: 确定
[aaa@qq.com ~]# tar zxvf crictl-v1.17.0-linux-amd64.tar.gz
[aaa@qq.com ~]# mv crictl /usr/bin/
socat是一款强大的命令行工具,能实现端口转发。k8s在向外暴露服务时可以用到该工具。socat安装直接使用 sudo apt-get install -y socat 即可。
初始化集群
因集群中管理的是Pod,而Pod之间通信需要相应的网络方案,这里选择 flannel(flannel的说明请参考网络),初始化集群,并传递参数:
kubeadm init --pod-network-cidr=10.244.0.0/16
[init] using Kubernetes version: v1.11.3
...
Your Kubernetes master has initialized successfully!
中间省略了一些打印信息(该打印信息记得保存下来,后续需要用到),其中最后的打印信息会显示让配置kubectl。我们直接复制打印信息即可:
[aaa@qq.com ~]# mkdir -p $HOME/.kube
[aaa@qq.com ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[aaa@qq.com ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
[aaa@qq.com ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master NotReady master 13h v1.16.3
此时我们看到虽然集群中有主节点了,但还是NotReady状态,是因为网络配置还没有完成,接着进行如下配置:
# 下载flannel的配置文件kube-flannel.yml
[aaa@qq.com ~]# wget https://raw.githubusercontent.com/coreos/flannel/v0.11.0/Documentation/kube-flannel.yml
打开该文件,找到 “name”: "cbr0"这一行,在其上加上 “cniVersion”: “0.3.1”,(若已有该行,则修改)并保存,接下来运行如下命令:
[aaa@qq.com ~]# kubectl apply -f kube-flannel.yml
稍等片刻,再次查看Node状态:
[aaa@qq.com ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 12m v1.16.3
继续查看Pod状态:
[aaa@qq.com ~]# kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-78fcdf6894-h7pkc 0/1 ContainerCreating 0 12m
kube-system coredns-78fcdf6894-lhlks 0/1 ContainerCreating 0 12m
kube-system etcd-master 1/1 Running 0 5m
kube-system kube-apiserver-master 1/1 Running 0 5m
kube-system kube-controller-manager-master 1/1 Running 0 5m
kube-system kube-flannel-ds-tqvck 1/1 Running 0 6m
kube-system kube-proxy-25tk2 1/1 Running 0 12m
kube-system kube-scheduler-master 1/1 Running 0 5m
我们发现有两个 coredns 的 Pod 是 ContainerCreating 的状态,但并未就绪,此时稍等片刻,就会变成Running状态。
新增Node
若要新增一个机器作为工作节点加入到集群中,则可按照以上安装步骤进行安装(kubeadm init后的操作不再需要)。在主机进行kubeadm init 后,最后有一串打印信息如下:
kubeadm join 202.182.112.120:6443 --token t14kzc.vjurhx5k98dpzqdc --discovery-token-ca-cert-hash sha256:d64f7ce1af9f9c0c73d2d737fd0095456ad98a2816cb5527d55f984c8aa8a762
我们将该打印信息在工作节点运行即可:
[aaa@qq.com ~]# kubeadm join 202.182.112.120:6443 --token t14kzc.vjurhx5k98dpzqdc --discovery-token-ca-cert-hash sha256:d64f7ce1af9f9c0c73d2d737fd0095456ad98a2816cb5527d55f984c8aa8a762
...
This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the master to see this node join the cluster.
接下来在主节点运行如下命令,查看节点状态
[aaa@qq.com ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 12m v1.16.3
node1 Ready <none> 7m v1.16.3
至此,我们搭建了有两个节点的集群。
kubectl
因kubectl是k8s的CLI,故我们对集群的许多操作都依赖该命令,这里对kubectl做一个简单的说明。
整体概览
我们在终端下执行kubectl:
➜ ~ kubectl
kubectl controls the Kubernetes cluster manager.
...
Usage:
kubectl [flags] [options]
可以看到kubectl有许多的命令,其一般用法为:kubectl [flags] [options]
。
get使用
前面看到,无论是查看节点状态还是Pod状态,我们都用的get命令, 若是想查看打印后的详细信息,可在后面加上 -o参数(-o wide 或-o yaml可看到更加详细的信息)。
➜ ~ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
minikube Ready master 2d v1.16.3 10.0.2.15 <none> Buildroot 2019.11 4.15.0 docker://17.12.1-ce
了解信息
当我们使用命令时,若不知道操作的命令是什么,除了 --help外还可使用explain参数,如想知道node是什么意思,可用如下命令:
➜ ~ 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).
# ... 省略输出
部署web服务
Service
Service是k8s中的一个概念,这里有必要进行说明。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 16m <none>
通过使用 kubectl 查看,能看到主要会显示 Service 的名称,类型,IP,端口及创建时间和选择器等。
类型
Service 目前有 4 种类型:
ClusterIP: 是 K8S 当前默认的 Service 类型。将 service 暴露于一个仅集群内可访问的虚拟 IP 上。
NodePort: 是通过在集群内所有 Node 上都绑定固定端口的方式将服务暴露出来,这样便可以通过 <NodeIP>:<NodePort> 访问服务了。
LoadBalancer: 是通过 Cloud Provider 创建一个外部的负载均衡器,将服务暴露出来,并且会自动创建外部负载均衡器路由请求所需的 Nodeport 或 ClusterIP 。
ExternalName: 是通过将服务由 DNS CNAME 的方式转发到指定的域名上将服务暴露出来,这需要 kube-dns 1.7 或更高版本支持。
实践
说完了 Service 的基本类型,当我们部署一个应用如 Redis ,其还无法被外部访问,我们需要将该Redis 服务暴露出来。
➜ ~ kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server
service/redis-server exposed
➜ ~ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 49m <none>
redis-server ClusterIP 10.108.105.63 <none> 6379/TCP 4s run=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 的方式让它可在集群外部访问。
➜ ~ kubectl port-forward svc/redis-server 6379:6379
Forwarding from 127.0.0.1:6379 -> 6379
Forwarding from [::1]:6379 -> 6379
Handling connection for 6379
当然,我们也可以使用 NodePort 的方式对外暴露服务。
➜ ~ kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server-nodeport --type=NodePort
service/redis-server-nodeport exposed
➜ ~ kubectl get service/redis-server-nodeport -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
redis-server-nodeport NodePort 10.109.248.204 <none> 6379:31913/TCP 11s run=redis
我们可以通过任意 Node 上的 31913 端口便可连接我们的 redis 服务。当然,这里需要注意的是这个端口范围其实是可以通过 kube-apiserver 的 service-node-port-range 进行配置的,默认是 30000-32767。
Helm
我们知道k8s是用来管理容器的,而我们通常将开发的web服务用Docker做成相应镜像,并最终在某台机器下以容器运行,通常,我们会将web服务用做成几部分镜像,并最终用docker-compose来编排。那怎样才能将这些容器放到k8s中进行管理呢?这里有一个工具 Kompose。该工具能将docker-compose.yml文件转化为k8s能识别的yaml文件,kompose的安装使用可参考项目主页的说明文档。
因web服务运行时有多个容器,并且容器之间可能有关联,故在k8s中单个的进行yaml文件部署显得非常麻烦,其次如果我们的web服务开发了新功能,再单个的更新yaml文件进行服务升级也显得困难。这里我们选择以Helm工具来在k8s中安装web服务。
Helm是构建于k8s上的包管理器,类似于Python中的 pip, linux中的Yum、APT。它是CNCF的孵化项目。
Helm安装比较简单,按照官方文档下载对应的包安装即可。下面可以了解一下Helm的相关概念。
Chart
chart 就是 Helm 所管理的包,类似于 Yum 所管理的 rpm 包或是 Homebrew 管理的 Formulae。它包含着一个应用要部署至 K8S 上所必须的所有资源。
Release
Release 就是 chart 在 K8S 上部署后的实例。chart 的每次部署都将产生一次 Release。这和上面类比的包管理器就有所不同了,多数的系统级包管理器所安装的包只会在系统中存在一份。我们可以以 Pip 在虚拟环境下的包安装,或者 Npm 的 local install 来进行类比。
Repository
Repository 就是字面意思,存储 chart 的仓库。还记得我们上面执行 helm init 时的输出吗?默认情况下,初始化 Helm 的时候,会添加两个仓库,一个是 stable 仓库 kubernetes-charts.storage.googleapis.com 另一个则是 local 仓库,地址是 http://127.0.0.1:8879/charts 。
Config
前面提到了 chart 是应用程序所必须的资源,当然我们实际部署的时候,可能就需要有些自定义的配置了。Config 便是用于完成此项功能的,在部署时候,会将 config 与 chart 进行合并,共同构成我们将部署的应用。
使用Helm部署应用
上面已经说过Helm中chart的含义,因此要部署一个项目,则首先要创建一个chart。例如我们要部署一个名为webapp的项目,则命令如下:
aaa@qq.com:/home/xiaobai# helm create webapp
Creating webapp
aaa@qq.com:/home/xiaobai# tree -a webapp/
webapp/
├── charts
├── Chart.yaml
├── .helmignore
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
3 directories, 10 files
可以看到创建后会生成一个chart模板。在这写模板里面我们主要关注templates文件夹与values.yaml文件。values.yaml 存放着项目的一些可配置项,如镜像的名称或者 tag 之类的。作用就是用于和模板进行组装。而我们部署项目的yaml文件则是放在templates文件夹中。Chart.yaml文件是每个chart必不可少的文件,其中的几个重要属性说明如下:
apiVersion:目前版本都为 v1
appVersion:这是应用的版本号,需要与 apiVersion, version 等字段注意区分
name: 通常要求 chart 的名字必须和它所在目录保持一致,且此字段必须
version:表明当前 chart 的版本号,会直接影响 Release 的记录,且此字段必须
description:描述
NOTES.txt 文件内的内容会在 helm install 执行成功后显示在终端,用于说明服务如何访问或者其他注意事项等。
下面就是对chart进行部署了。若要在k8s上部署该项目,我们在webapp父目录下直接使用如下命令:
# webappname为chart实例的名字,可任取
helm install webappname webapp
该项目对应的Pod、service则会自动被部署到k8s中,若想删除该项目,则使用如下命令即可:
helm uninstall webappname
当然,我们也可以将chart打包,以便于分发:
helm package webapp
其他扩展
Dashboard
前面我们都是用命令kubectl 或者helm等命令行工具来部署应用的。这样操作可能不太直观,因此我们若是能通过界面直接操作将显得更加简单,而 Dashboard就是一款用于web界面化部署容器的应用程序。它和大多数Dashboard项目类似,可以对集群状态进行直观的展示。
Dashboard的安装步骤较为麻烦一点,具体安装步骤,可参考这篇文章。
FAQ
- kubernetes安装过程中报错如何解决?
一般在安装过程中出现报错,我们可以使用系统监控日志的记录来查看报错信息,查看命令为 journalctl -u kubelet -f。然后根据具体报错信息来搜索解决方案。更多关于安装报错的解决方式,请参考这篇文章。 - Pod/Service/Node的状态不正常如何解决?
当Pod的状态不正常时,我们可以使用 kubectl describe pod/podname --namespace spacename命令(若是查看node,则命令为node/nodename, podname为具体的pod的名称),通过查看该命令返回的信息来对pod或service的状态进行查看纠正。
参考
参考一: 《Kubernetes 从上手到实践》
参考二:《云原生技术公开课》
下一篇: Docker镜像操作命令实战