使用kubectl的Deployment实践练习创建App

Kubernetes Deployments

kubectl 的Deployment 功能可以指令Kubernetes 如何创建和更新应用实例。通过kubectl 命令创建一个deployment后,Kubernetes master 会调度deployment配置好的应用实例到集群特定的节点运行。

一旦应用实例被创建好之后,Kubernetes Deployment Controller 会 持续监控 这些实例。如果运行这些实例的节点down机或被删除,Deployment controller 会替换掉它。所以Kubernetes 提供了一种自修复机制来处理机器故障或维护。

进入下面的链接进行部署应用练习。

练习环境:https://kubernetes.io/docs/tutorials/kubernetes-basics/deploy-app/deploy-interactive/

kubectl get nodes

终端输入 kubectl get nodes 命令可以来查看当前集群

kubectl run命令

run命令创建一个新的部署。 我们需要提供部署名称和应用程序镜像位置 。 如果希望在特定端口上运行应用程序,需要添加port参数 :

kubectl run kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --port=8080

上面的命令通过创建deployment部署了一个应用程序,命令完成了几件事:

  • 搜索可以运行应用程序实例的适当节点(目前只有一个)。
  • 调度应用程序在该节点上运行。
  • 配置集群,以便在需要时重新调度新节点上的实例。

kubectl get deployments

此命令可以查看刚才新创建的部署

查看 app

在Kubernetes内部运行的Pods运行在一个私有的、隔离的网络上。默认情况下,它们可以从同一个kubernetes集群内的其他pod和服务中看到,但不能从 集群 网络之外看到。 当我们使用kubectl时,我们通过API端点与应用程序进行交互。

kubectl命令可以创建一个代理,将通信转发到集群范围的私有网络。按control-C可以终止代理,运行时不会显示任何输出。

打开第二个终端窗口来运行代理。 kubectl proxy

现在我们在主机(在线终端)和Kubernetes集群之间建立了连接。代理允许从这些终端直接访问API。您可以看到通过代理端点托管的所有这些api,现在可以通过http://localhost:8001获得这些api。例如,我们可以使用curl命令直接通过API查询版本: curl http://localhost:8001/version

API服务器将根据pod名称为每个pod自动创建端点,这些端点也可以通过代理访问。

首先我们需要获得Pod名称,我们将存储在环境变量POD_NAME中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}') echo "Name of the Pod: $POD_NAME"

现在,我们可以向在该pod中运行的应用程序发出HTTP请求:

curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/

结果如图:

Kubernetes Pods

创建部署时,Kubernetes创建了一个Pod来托管应用程序实例。 Pod是一个Kubernetes抽象概念,它表示一个或多个应用程序容器的组合(如Docker或rkt),以及这些容器的一些共享资源。 这些资源包括:

  • 共享存储,如卷
  • 网络,作为唯一的集群IP地址
  • 关于如何运行每个容器的信息,例如容器镜像版本或要使用的特定端口

Pod为特定于应用程序的“逻辑主机”建模,可以包含相对紧密耦合的不同应用程序容器。 例如,Pod可能既包括带有Nodejs应用程序的容器, 也包括由Nodejs web服务器发布的提供数据的容器。 Pod中的容器共享一个IP地址和端口空间,它们总是位于同一个节点上,并在同一个上下文中运行。

Pods在Kubernetes 平台上是原子单位。 当我们在Kubernetes上创建部署时,该部署将创建包含容器的Pods(而不是直接创建容器)。 每个Pod被绑定到调试执行它的节点上,并一直保持到终止(根据重启策略)或删除。 在节点失败的情况下,相同的pod会在集群中的其他可用节点上被调度。

Nodes

Pod总是在节点上运行。 节点是Kubernetes中的工作机器,根据集群的不同,它可以是虚拟机器,也可以是物理机器。 每个节点都由主节点管理。 一个节点可以有多个pod, Kubernetes master自动处理集群中节点之间的pod调度。 主节点的自动调度考虑到每个节点上的可用资源。

每个Kubernetes节点都至少运行的服务:

Kubelet, 负责Kubernetes Master 和 Node 之间通信的进程; 它管理在机器上运行的pod和容器。

容器运行时(如Docker、rkt)负责从registry中提取容器镜像、解压缩容器并运行应用程序。

kubectl概述

kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。运行kubectl命令的语法如下所示

$ kubectl [command] [TYPE] [NAME] [flags]

comand:指定要对资源执行的操作,例如create、get、describe和delete

TYPE:指定资源类型,资源类型是大小写敏感的,开发者能够以单数、复数和缩略的形式。例如:

$ kubectl get pod pod1 
$ kubectl get pods pod1 
$ kubectl get po pod1

NAME:指定资源的名称,名称也大小写敏感的。如果省略名称,则会显示所有的资源。

flags:指定可选的参数。例如,可以使用-s或者–server参数指定Kubernetes API server的地址和端口。

另外,可以通过kubectl helpkubectl -h命令获取更多的信息。

资源对象

kubectl大部分子命令后都可以指定要操作的资源对象,可以用kubectl api-resources命令参考

全局参数

kubectl options命令可以列出可以全局使用的命令参数,比较重要的有:

--cluster='': 指定命令操作对象的集群
--context='':  指定命令操作对象的上下文
-n, --namespace='': 指定命令操作对象的Namespace

资源字段

kubectl explain命令可以输出资源对应的属性字段及定义,在定义资源配置文件时候非常有用。

# Usage:
  kubectl explain RESOURCE [options]
# Examples: 
$ kubectl  explain deployment.spec.selector
KIND:     Deployment
VERSION:  extensions/v1beta1

RESOURCE: selector <Object>

DESCRIPTION:
     Label selector for pods. Existing ReplicaSets whose pods are selected by
     this will be the ones affected by this deployment.

     A label selector is a label query over a set of resources. The result of
     matchLabels and matchExpressions are ANDed. An empty label selector matches
     all objects. A null label selector matches no objects.

FIELDS:
   matchExpressions     <[]Object>
     matchExpressions is a list of label selector requirements. The requirements
     are ANDed.

   matchLabels  <map[string]string>
     matchLabels is a map of {key,value} pairs. A single {key,value} in the
     matchLabels map is equivalent to an element of matchExpressions, whose key
     field is "key", the operator is "In", and the values array contains only
     "value". The requirements are ANDed.

环境准备

kubectl安装后,默认是没有比如自动补全等功能的,频繁使用比较不方便。目前已经有各类kubectl小工具可以提高效率,还有kubectl专用的shell了。个人感觉比较好用有以下这些:

自动补全

kubectl 命令在bash中默认是没有自动补全的,需要安装bash_completion,添加自动补全脚本。这里以CentOS为例,其他操作系统配置可以参看Install and Set Up kubectl

# 安装bash-completion
yum install -y epel-release.noarch
yum install -y bash_completion
# 添加补全脚本
kubectl completion bash >/etc/bash_completion.d/kubectl

重新登录shell,可以发现kubectl的子命令,包括资源名称都可以用Tab键自动补全了。

快速切换集群和Namespace

生产环境一般是多集群,至少也是多NS的环境,免不了经常在不同集群和不同NS间切换。切换集群要修改环境变量、切换NS要在命令跟上 -n namespace,都不是太方便。而用kubectx和kubens两个小工具可以实现快速切换。这俩在同一项目里:ahmetb/kubectx

# 安装
sudo git clone https://github.com/ahmetb/kubectx /opt/kubectx
sudo ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx
sudo ln -s /opt/kubectx/kubens /usr/local/bin/kubens

# 使用kubectx
# kubectx  : 列出所有上下文
# kubectx  : 切换到某个上下文
$ kubectx minikube
Switched to context "minikube".
# kubectx -   : 切换回上一个上下文
$ kubectx -
Switched to context "oregon".
# kubectx = : 重命名一个集群上下文
$ kubectx dublin=gke_ahmetb_europe-west1-b_dublin
Context "dublin" set.
Aliased "gke_ahmetb_europe-west1-b_dublin" as "dublin".
# kubectx =. : 重命名当前上下文
# kubectx -d   : 删除上下文

# 使用kubens
# kubens : 列出所有的NS
# kubens   : 切换当前NS
$ kubens kube-system
Context "test" set.
Active namespace is "kube-system".
# kubens - : 切换回上一个NS
$ kubens -
Context "test" set.
Active namespace is "default".

关于多集群切换的配置和上下文的概念可以参看官方文档,有中文。

kubectl shell

kubectl已经有比较成熟的专用shell了,优化了自动补全,模糊匹配等功能:

但实际使用过程中,偶尔还是会各种小问题。推荐两个比较热门的,有需要可以尝试一下:
kube-shell
kube-prompt

kubectl命令

Kubectl 命令工具可以获取关于已部署应用程序及其环境的信息。 最常见的操作可以使用以下kubectl命令完成:

  • kubectl get – 列资源
  • kubectl describe – 显示有关资源的详细信息
  • kubectl logs – 从一个pod的容器打印日志
  • kubectl exec – 在pod中的容器上执行命令

您可以使用这些命令查看应用程序的部署时间、当前状态、运行位置和配置。

练习环境: https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-interactive/

Kubectl常用子命令大概分为以下几类:

声明式资源对象管理

对集群资源的声明式管理,是Kubernetes最主要的特性之一,而kubectl apply命令是最能体现这个特性的命令。apply命令最主要的参数有两个:

# Usage:
  kubectl apply (-f FILENAME | -k DIRECTORY) [options]

-f 参数后 跟yaml或json格式 的资源配置文件,-k 参数后跟 kustomization.yaml 配置文件的位置。

为什么说apply是声明式管理呢,因为所有对集群的增改操作,都能用apply命令完成,一切取决于后面的配置文件:

  • 如果配置文件中的资源找集群中不存在,则创建这个资源。
  • 如果配置文件中的资源在集群中已存在,则根据配置对资源字段进行更新

举个例子:

# 部署一个goweb应用,配置pod数为4个:
[root@master-1 ~]# grep replicas deployment-goweb.yaml 
  replicas: 4
# 使用 apply 创建资源
[root@master-1 ~]# kubectl apply -f deployment-goweb.yaml 
deployment.apps/goweb created
[root@master-1 ~]# kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
goweb-6b5d559869-4x5mb   1/1     Running   0          14s
goweb-6b5d559869-77lbz   1/1     Running   0          14s
goweb-6b5d559869-9ztkh   1/1     Running   0          14s
goweb-6b5d559869-ccjtp   1/1     Running   0          14s

# 修改pod数量为2个:
[root@master-1 ~]# sed -ri 's/4$/2/g' deployment-goweb.yaml
[root@master-1 ~]# grep replicas deployment-goweb.yaml       
  replicas: 2

# 使用apply更新资源
[root@master-1 ~]# kubectl  apply  -f deployment-goweb.yaml 
deployment.apps/goweb configured
[root@master-1 ~]# kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
goweb-6b5d559869-4x5mb   1/1     Running   0          8m21s
goweb-6b5d559869-77lbz   1/1     Running   0          8m21s

# pod数已更新为2个

可以看到,同一个kubectl apply -f deployment-goweb.yaml命令,可以用来创建资源也可以更新资源。
简单来说,apply命令的作用就是一个:使集群的实际状态朝用户声明的期望状态变化,而用户不用关心具体要进行怎样的增删改操作才能呢达到这个期望状态,也即Kubernetes的声明式资源管理。

kubectl apply

通过文件名或控制台输入,对资源进行配置。接受JSON和YAML格式的描述文件。

kubectl apply -f FILENAME

# 将pod.json中的配置应用到pod
$ kubectl apply -f ./pod.json

# 将控制台输入的JSON配置应用到Pod
$ cat pod.json | kubectl apply -f -

命令式资源对象管理

命令式管理类就是直接通过命令执行增删改的操作,除了删除资源外,下面的命令能用apply代替,kubernetes也建议尽量使用apply命令。

  • 创建资源
kubectl create deployment my-dep --image=busybox        # 创建一个deplpyme
kubectl expose rc nginx --port=80 --target-port=8000    # 创建一个svc,暴露nginx这个rc
  • 更新资源
kubectl scale --replicas=3 -f foo.yaml                     # 将foo.yaml中描述的对象扩展为3个
kubectl annotate pods foo description='my frontend'        # 增加description='my frontend'备注,已有保留不覆盖
kubectl label --overwrite pods foo status=unhealthy        # 增加status=unhealthy 标签,已有则覆盖
  • 删除资源
kubectl delete -f xxx.yaml                      # 删除一个配置文件对应的资源对象  
kubectl delete pod,service baz foo              # 删除名字为baz或foo的pod和service  
kubectl delete pods,services -l name=myLabel    # -l 参数可以删除包含指定label的资源对象                            
kubectl delete pod foo --grace-period=0 --force # 强制删除一个pod,在各种原因pod一直terminate不掉的时候很有用

查看资源状态

  • get

最常用的查看命令,显示一个或多个资源的详细信息

# Usage:
  kubectl get
[(-o|--output=)](TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags] 
[options]
# Examples: 
kubectl get services                          # 列出当前NS中所有service资源
kubectl get pods --all-namespaces             # 列出集群所有NS中所有的Pod
kubectl get pods -o wide                      # -o wide也比较常用,可以显示更多资源信息,比如pod的IP等
kubectl get deployment my-dep                 # 可以直接指定资源名查看
kubectl get deployment my-dep --watch         # --watch 参数可以监控资源的状态,在状态变换时输出。在跟踪服务部署情况时很有用
kubectl get pod my-pod -o yaml                # 查看yaml格式的资源配置,这里包括资实际的status,可以用--export排除
kubectl get pod my-pod -l app=nginx           # 查看所有带有标签app: nginx的pod

kubectl 可用JSONPATH来过滤字段,JSON Path的语法可参考这里

kubectl get pods --selector=app=cassandra rc -o jsonpath='{.items[*].metadata.labels.version}' # 获取所有具有 app=cassandra 的 pod 中的 version 标签
  • describe

describe命令同样用于查看资源信息,但相比与get只输出资源本身的信息,describe聚合了相关资源的信息并输出。比如,在describe node信息时,同时会输出该node下的pod的资源利用情况。所以describe命令在排错和调试时非常有用。

# Usage:
kubectl describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME) [options]
# Examples: 
kubectl describe nodes my-node    # 查看节点my-node的详细信息
kubectl describe pods my-pod      # 查看pod my-pod的详细信息

容器管理

虽然逻辑上,Kubernetes的最小管理单位是Pod,但是实际上还是免不了与容器直接交互,特别是对于多容器的Pod,任意容器有问题,都会导致Pod不可用。

  • 日志查看
# Usage:
  kubectl logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER] [options]
# Examples: 
kubectl logs my-pod                              
# 输出一个单容器pod my-pod的日志到标准输出
kubectl logs nginx-78f5d695bd-czm8z -c nginx     
# 输出多容器pod中的某个nginx容器的日志
kubectl logs -l app=nginx                        
# 输出所有包含app-nginx标签的pod日志
kubectl logs -f my-pod                           
# 加上-f参数跟踪日志,类似tail -f
kubectl logs my-pod  -p                          
# 输出该pod的上一个退出的容器实例日志。在pod容器异常退出时很有用
kubectl logs my-pod  --since-time=2018-11-01T15:00:00Z
# 指定时间戳输出日志            
kubectl logs my-pod  --since=1h 
# 指定时间段输出日志,单位s/m/h
  • 执行命令

命令作用和参数基本与 docker exec 一致

# Usage:
kubectl exec POD [-c CONTAINER] -- COMMAND [args...] [options]
# Examples:
kubectl exec my-pod ls                         # 对my-pod执行ls命令
kubectl exec -t -i nginx-78f5d695bd-czm8z bash # 进入pod的shell,并打开伪终端和标准输入
  • 文件传输

在排错和测试服务的时候,时不时需要和容器互相交互文件,比如传输容器内存的dump到宿主机,或从宿主机临时拷贝个新配置文件做调试,这时就可以用 kubectl cp 命令。要注意的是,cp命令需要容器里已安装有tar程序

# Usage:
kubectl cp   [options]
# Examples:  
kubectl cp /tmp/foo_dir :/tmp/bar_dir                 # 拷贝宿主机本地文件夹到pod
kubectl cp /:/tmp/foo /tmp/bar        # 指定namespace的拷贝pod文件到宿主机本地目录
kubectl cp /tmp/foo :/tmp/bar -c  # 对于多容器pod,用-c指定容器名

集群管理

除了和具体的资源打交道,在对集群进行维护时,也经常需要查看集群信息和对节点进行管理,集群管理有以下这些常用的命令:

  • 集群信息查看
 kubectl cluster-info      # 查看master和集群服务的地址
 kubectl cluster-info dump # 查看集群详细日志
 kubectl version           # 查看Kubernetes集群和客户端版本
  • 节点管理

在集群节点出问题时,可能希望把一个节点不再被调度pod,或把节点目前的pod都驱逐出去

kubectl cordon my-node       
# 标记 my-node 为 unschedulable,禁止pod被调度过来。注意这时现有的pod还会继续运行,不会被驱逐。
kubectl uncordon my-node 
# 与cordon相反,标记 my-node 为 允许调度。
kubectl drain  my-node
# drain字面意思为排水,实际就是把my-node的pod平滑切换到其他node,同时标记pod为unschedulable,也就是包含了cordon命令。
# 但是直接使用命令一般不会成功,建议在要维护节点时,加上以下参数:
kubectl drain my-node  --ignore-daemonsets  --force  --delete-local-data  
# --ignore-daemonsets 忽略daemonset部署的pod
# --force 直接删除不由workload对象(Deployment、Job等)管理的pod
# --delete-local-data  直接删除挂载有本地目录(empty-dir方式)的pod

DaemonSet确保集群中每个(部分)node运行一份pod副本,当node加入集群时创建pod,当node离开集群时回收pod。如果删除DaemonSet,其创建的所有pod也被删除,DaemonSet中的pod覆盖整个集群。

使用kubectl故障排除

检查应用程序配置

我们将使用kubectl get命令并查找现有的Pods: kubectl get pods

会看到如图的结果:

接下来,为了查看Pod中的容器以及用于构建这些容器的图像,我们运行describe pods命令: kubectl describe pods

会看到如图的结果:

在终端显示app

Pods是在一个隔离的私有网络中运行的——因此我们需要代理访问它们,以便调试和与它们交互。为此,我们将使用kubectl proxy命令在第二个终端窗口中运行代理: kubectl proxy

现在,我们将再次获得Pod名称,并通过代理直接查询该Pod。获取Pod名称并将其存储在POD_NAME环境变量中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}');echo "Name of the Pod: $POD_NAME"

要查看应用程序的输出,请运行curl请求: curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/

查看容器日志

应用程序通常发送到STDOUT的任何内容都会成为Pod中容器的日志。我们可以使用kubectl logs命令检索这些日志: kubectl logs $POD_NAME

继续学习

https://jimmysong.io/kubernetes-handbook/guide/using-kubectl.html


   转载规则


《使用kubectl的Deployment实践练习创建App》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录