(共556篇)
全部分类

基础命令
[ Docker ] 

k8s 备忘录

kubeadm

--image-repository: kube 默认使用的 k8s.grc.io, 国内访问不到, 所以直接指定国内的仓库,来下载需要的资源包,参考文档

--pod-network-cidr: 使用 kubeadm 初始化主节点环境时, 需要通过这个参数来告诉 flannel 插件如何分配 IP 地址参考文档

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 初始化master节点
kubeadm init --v=5 --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers --pod-network-cidr=10.244.0.0/16

# 还原kubeadm init产生的影响
kubeadm reset

# master节点创建新的token, 用于用于子节点加入集群
kubeadm token create --print-join-command

# 子节点加入指定集群
kubeadm join 192.168.20.12:6443 --token lrev2c.3cmn8j3yd47po0u4 --discovery-token-ca-cert-hash sha256:bbf1fe9d085838927fb97538e389d03787311eff608af0af1850db4e15ee0839

kubectl

kubectl 完整文档可以查看官方手册

kubectl 中的几种资源(斜杠后是缩写):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
namespace/ns        # 命名空间
node/no             # 节点
deployment/deploy   # 部署资源, 1个deployment中可以包含多个容器镜像, 每个deployment的副本数量由deployment:replicas决定
ReplicaSet/rs       # deployment的副本控制器, 用来维护deployment的副本数量, 即pod数量
pod/po              # deployment副本, 一个pod内可以有多个container实例
service/svc         # 网络服务, 为容器内的pod资源提供网络管理服务
configmap/cm        # 共享的配置信息, 可以理解为类似nacos中的共享配置, pod引入指定的configmap后可以访问configmap中定义的配置信息
secret              # 共享的加密信息, 也可以理解为类似nacos中的共享配置,pod引入指定的secret后可以访问secret中定义的配置信息, secret中定义的data信息必须是加密后的Base64字符串,取值的时候会被k8s自动解密
cronjob/cj          # 定时任务
ResourceQuota/quota # 资源配额, 主要用于限制各命名空间内的资源总量
serviceaccount/sa   # 服务账号
DaemonSet/ds        # 守护型副本管理服务, 和replicaset相似, 会自动在符合条件的node中创建pod, 但由它创建的pod属于守护型pod,使用drain清理节点时,默认不会被清理

可以通过k api-resources查看所有的资源类型及其缩写

关于 deployment 与 pod 的关系,就好比 image 与 container 的关系, image 是镜像, 以一个 image 为基础可以运行多个 container 实例, 只不过 image 是死的 , 未运行状态的, container 实例是运行状态的; depoyment 就类似于 image, 是一个未运行状态的封闭系统,里面包含了多个 container 实例, pod 就类似于 container, 是一个运行状态的 deployment.

ResourceQuota 文档

pod

pod有三种创建来源:

  1. 直接通过k run pod-name --image=iamge-name创建, 如果要删除, 需要直接通过delete pod pod-name删除
  2. 通过 deployment 自动创建, 如果需要删除, 可以直接通过删除 deployment 删除对应的所有 pod 副本
  3. 通过 DaemonSet 创建, 这些 pod 属于守护型 pod, 使用 drain 清理节点时,默认不会被清理

get

可以通过kubectl get 资源类型 资源名 [参数]查看对应资源的列表信息, 参数:

1
2
3
4
5
6
7
8
9
-A                # 在所有命名控件中查找 相当于 --all-namespace
-n namespace      # 在指定命名控件查找
-o 格式名          # 格式化输出信息
-l k1=v1,k2=v2    # 根据selector筛选要查看的资源, 相当于 --selector
-f path           # 通过制定的配置文件确定要查看的资源列表, path可以是文件名/目录名/URL
-w                # 实时监控并更新资源列表信息
--sort-by=''      # 对列表信息排序
--field-selector  # 通过资源信息的指定字段筛选要查看的资源, 比如筛选正在运行的pod: k get pod pod-name --field-selector=status.phase=Running
--show-labels     # 在列表中显示各资源的标签信息

格式化-o的值有json, yaml, name, go-template, go-template-file, template, templatefile, jsonpath, jsonpath-as-json, jsonpath-file, custom-columns, custom-columns-file, wide 等

describe

通过kubectl descibe 资源类型 资源名 [参数]查看对应资源的详细信息, 它与 get 不同之处在于: get 获取的是多个资源的列表信息, describe 更注重的是获取单个资源的详细信息

describe 的参数与 get 大致相同

config

1
2
3
4
5
kubectl config view                                       # 查看当前集群基础信息
kubectl config set-context --current --namespace=default  # 在指定上下文中持久性地保存名字空间,供所有后续 kubectl 命令使用
kubectl config get-contexts                               # 显示所有的上下文列表
kubectl config current-context                            # 展示当前所处的上下文
kubectl config use-context my-cluster-name                # 设置默认的上下文为 my-cluster-name

describe

通过kubectl describe 资源类型 资源名 [参数]查看资源的详细信息, 参数:

1
-n namespace # 指定命名空间

apply

使用配置文件创建资源

1
2
3
4
kubectl apply -f ./my-manifest.yaml           # 创建资源
kubectl apply -f ./my1.yaml -f ./my2.yaml     # 使用多个文件创建
kubectl apply -f ./dir                        # 基于目录下的所有清单文件创建资源
kubectl apply -f https://git.io/vPieo         # 从 URL 中创建资源

create

通过命令行参数的方式手动创建资源, 这种方式能够创建的资源有很多, 官方文档上有很多的例子

create 可以通过命令行的方式创建资源, 也可以通过 yaml 文件创建资源

1
2
3
4
5
# 通过指定的image创建一个deployment, 默认只创建1个副本
kubectl create deployment deployment-name --image=image-name

# 通过指定文件创建资源, path可以是单文件/目录/URL
kubectl create -f path

create 与 apply 都可以通过文件有创建资源的功能, 区别在于:

  1. 通过 apply 创建资源时, 先检查 yaml 文件对应的资源是否已存在, 如果已存在, 就更新资源, 否则才会创建新资源; create 只有在对应的资源不存在时,才会创建资源, 否则会直接失败
  2. apply 可以根据 yaml 文件更新资源, create 不行
  3. apply 更新资源的时候, yaml 可以是完整配置的一部分内容, create 创建资源时, 必须是一个完整的资源配置文件

explain

通过kubectl explain 资源类型查看资源的配置文件说明

1
2
kubectl explain pod # 查看pod类型资源的字段描述
kubectl explain service # 查看service类型资源的字段描述

delete

通过kubectl delete 资源类型 资源名 [参数]删除指定资源, 参数:

1
2
3
4
5
6
7
-n namespace      # 指定命名空间
-f path           # 通过指定的配置文件确定要删除的资源, path可以是文件名/目录名/URL
-l                # 通过指定label确定要删除的资源
--now             # 尽快删除指定资源
--force           # 强制删除指定资源, 这种方式不会等待资源内进程结束, 是一种非"优雅删除"的方式
--grace-period=n  # 设置资源的自动关闭时间, 以秒为单位, 设置为1就相当于让资源立即删除了, 只有--force的时候, 才能设置为0
--all             # 删除所有指定类型的资源

案例:

1
2
kubectl delete pod --all  # 删除所有的pod资源
kubectl delete service --all  # 删除所有的service资源

patch

通过kubectl patch 资源类型 资源名 -p '{}'来更新资源的配置信息, 官方文档

1
2
3
-f path     # 通过指定的配置文件更新资源, path可以是单个文件, 目录, URL, 相当于--filename
-p options  # 通过指定的json字符串更新资源, 相当于--patch
--type      # 指定资源更新方式, 有strategic,json, merge三种

--type=strategic 默认值, 表示策略性合并, 策略性合并会逐步移除旧的 pod,并根据 deployment 的配置重新生成新的 pod, 策略性合并对于key:value类型的值执行 replace 操作, 对于list类型的值,默认执行 replace 操作, 但是对于某些指定了 patch-strategy:merge 属性的字段,会执行 merge 操作, 比如 pod:spec:container 或者 container:env 字段的值, 在策略性合并模式下都会执行 merge 操作

--type=json, 使用 json 操作符更新资源, 这种方式会根据操作符来明确的执行 add, replace, remove 等操作来更新资源配置

--type=merge, 以 merge 的方式使用 json 重新指定资源信息, 它与--type=json的区别可以参考文档

案例:

1
2
3
4
5
6
7
8
# 把指定节点转为不可调度节点
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

# 更新deployment的副本数量为3个
kubectl patch deployment patch-demo --type='merge' -p '{"spec":{"replicas":3}}'

# 更新某个pod内指定container的镜像, name是必填的,因为它是container的关键字, 如果pod内存在多个container, 要通过name来确认更新哪个container的镜像
kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

edit

修改资源的配置, 除了 patch,还客户已使用 edit 命令, 直接修改某个资源的信息, 修改完成后, k8s 会自动更新资源

scale

通过kubectl scale 资源类型/资源名 [参数]来控制资源的副本数量

1
2
3
4
5
6
7
8
# 通过deployment名称控制副本数量
kubectl scale deploy/deploy-name --replicas=3

# 通过replicasets名称控制副本数量
kubectl scale rc/rc-name --replicas=3

# 通过资源配置文件控制副本数量
kubectl scale -f path --replicas=3

logs

通过kubectl logs 资源类型/资源名 [参数]来查看 pod 的日志, 如果是 pod 资源, 资源类型时可以省略的, 如果通过其他资源查看日志, 资源类型不能省略

参数

1
2
3
4
5
6
7
--all-containers    # 默认值是false, 获取pod内所有容器的日志
-c container-name   # 指定获取某个container的日志
-l k1=v1,k2=v2      # 查看符合label条件的pod内的container日志
--timestamps        # 默认值是false, 在日志中显示日志的时间戳
--tail=10           # 默认值是-1, 相当于10,  控制首次显示日志时读取的行数
-f                  # 流式输出日志, 相当于--follow
--since=2m          # 查看指定时间内的日志, 值可以设为5s,2m,3h等
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
kubectl logs my-pod                                 # 获取 pod 日志(标准输出)
kubectl logs -l name=myLabel                        # 获取含 name=myLabel 标签的 Pods 的日志(标准输出)
kubectl logs my-pod --previous                      # 获取上个容器实例的 pod 日志(标准输出)
kubectl logs my-pod -c my-container                 # 获取 Pod 容器的日志(标准输出, 多容器场景)
kubectl logs -l name=myLabel -c my-container        # 获取含 name=myLabel 标签的 Pod 容器日志(标准输出, 多容器场景)
kubectl logs my-pod -c my-container --previous      # 获取 Pod 中某容器的上个实例的日志(标准输出, 多容器场景)
kubectl logs -f my-pod                              # 流式输出 Pod 的日志(标准输出)
kubectl logs -f my-pod -c my-container              # 流式输出 Pod 容器的日志(标准输出, 多容器场景)
kubectl logs -f -l name=myLabel --all-containers    # 流式输出含 name=myLabel 标签的 Pod 的所有日志(标准输出)
kubectl logs deploy/deploy-name                     # 查看指定deployment资源的副本日志(只有一个容器的情况下),
kubectl logs deploy/deploy-name -c container-name   # 查看指定deployment资源的副本日志(拥有多个一个容器的情况下),指定要查看的容器日志

run

通过kubectl run pod-name --image=image-name [参数]手动创建并运行一个 pod

run 的参数与通过 docker 创建容器类似, 具体的可以参考官方文档, 官方文档上也提供了几个简单的 demo

cordon 与 uncordon

通过 kubectl cordon node-name标记某个节点不参与调度

通过 kubectl uncordon node-name标记某个节点参与调度

通过 kubectl drain node-name 清空某个节点数据, 通常在做节点维护时, 需要这个操作

drain

通过 kubectl drain node-name 清空某个节点数据, 通常在做节点维护时, 需要这个操作, 一旦执行了此操作, 会先把节点标记为不可调度状态, 再逐步驱逐节点内的 pod, 在其他节点中重建 pod

参数:

1
2
--ignore-daemonsets # 强制驱逐由DaemonSet创建的Pod
--delete-emptydir-data # 强制驱逐使用了EmptyDir卷的Pod

EmptyDir是 K8S 提供的一种特殊挂在卷

访问集群中的应用程序

通过创建 service 访问

如果应用程序是通过 deployment 创建的, 可以通过下面的方式创建一个 Service 资源:

1
2
3
4
--type=NodePort         # Service类型, 值有ClusterIP(默认值), NodePort, LoadBalancer, ExternalName四种
--name=service-name     # Service名称
--port=8080             # Service本身的端口
--target-port=80        # 应用程序的端口
1
kubectl expose deploy deploy-name --type=NodePort --name=service-name --port=8080 --target-port=80

此时通过kubectl describe svc service-name查看一下创建的 service 资源信息, 类似于这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Name: patch-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.99.18.140
IPs: 10.99.18.140
Port: <unset>  8080/TCP
TargetPort: 80/TCP
NodePort: <unset>  31160/TCP # 这是自动给servie分配的外部访问端口
Endpoints: 10.244.1.36:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>

现在, 在集群内的主机可以通过localhost:31160访问应用程序, 或者在集群外的主机上通过http://集群内任意节点IP:31160访问应用程序

在集群外主机通过端口转发访问

相当于在集群外主机开通一个 proxy

如果应用程序是通过 deployment 创建的, 像下面这样, 80 表示应用程序使用的端口, 57749 表示本地要访问的端口, 如果不设置此端口, kubectl 会自动分配, 下面方式启动后在集群外的主机上, 通过localhost:57749访问到应用程序

1
2
3
4
$ kubectl port-forward deploy deploy-name 57749:80
# orwarding from 127.0.0.1:57749 -> 80
# Forwarding from [::1]:57749 -> 80
# Handling connection for 57749

也可以直接转发打指定的 Pod 上, 下面方式启动后, 可以在集群外主机上通过localhost:57782访问到应用程序, 通常不建议这么做, 因为这种方法完全失去了 K8S 的意义,如果 pod 崩溃, 应用程序就访问不到了

1
2
3
4
$ kubectl port-forward pod/patch-demo-66586975bd-8jg8z :80
# Forwarding from 127.0.0.1:57782 -> 80
# Forwarding from [::1]:57782 -> 80
# Handling connection for 57782

IP 地址分配

先说一下集群内各资源的 IP 分配:

node 节点

node: node 上有两个 IP, 一个是主机本身的 IP, 一个是主机在集群内的 IP

集群内的 IP 就好比路由器的网关地址, 给节点内的 Pod 分配 IP 时, 都与这个"网关地址"有关

1
2
3
4
node12: 主机IP:192.168.20.12, 在集群内的IP: 10.224.1.0
node13: 主机IP:192.168.20.13, 在集群内的IP: 10.224.2.0
node14: 主机IP:192.168.20.14, 在集群内的IP: 10.224.3.0
node15: 主机IP:192.168.20.15, 在集群内的IP: 10.224.4.0

PodIP

给 Pod 分配 IP 时, 根据 Pod 所在的主机在集群内的 IP 进行分配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
node12: 主机IP:192.168.20.12, 在集群内的IP: 10.224.1.0
  pod1: 10.224.1.44
  pod2: 10.224.1.30
node13: 主机IP:192.168.20.13, 在集群内的IP: 10.224.2.0
  pod1: 10.224.2.15
  pod2: 10.224.2.20
node14: 主机IP:192.168.20.14, 在集群内的IP: 10.224.3.0
  pod1: 10.224.3.40
  pod2: 10.224.3.100
node15: 主机IP:192.168.20.15, 在集群内的IP: 10.224.4.0
  pod1: 10.224.4.21
  pod2: 10.224.4.19

ClusterIP 与 ServiceIP

ServiceIP 与端口配合只是对一组 pod 中指定应用程序的代理, 它与 ClusterIP 相同都是由 k8s 统一管理的, 大部分情况下, ClusterIP 与 ServiceIP 都是以 10 开头

1
2
3
4
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
echo-service-cluster   ClusterIP   10.103.139.141   <none>        80/TCP         34m
echo-service-node      NodePort    10.96.139.86     <none>        80:30227/TCP   34m
kubernetes             ClusterIP   10.96.0.1        <none>        443/TCP        30d

client_address

流量到达 pod 后, pod 获取到的客户端地址会根据 service 的类型而变化

PodIP

假设一个 Pod 部署在 10.224.2.0 主机上, PodIP 为:10.244.2.16, 应用程序端口号为 80:

Pod 创建成功后, 可以通过kubectl exec pod-name的方式进入 pod 内访问应用程序, 在 Pod 内访问应用程序, 不同访问地址, client_address 的值是这样的:

1
2
3
kubectl exec echoserver-8585bfb456-2d48s -- curl -s 10.244.2.16 # client_address: 10.244.2.16
kubectl exec echoserver-8585bfb456-2d48s -- curl -s localhost   # client_address: ::1
kubectl exec echoserver-8585bfb456-2d48s -- curl -s 127.0.0.1   # client_address: ::ffff:127.0.0.1

还可以在集群内的任何主机上通过PodIP:AppPort访问到应用程序, 基本上可以看出规律, 在非 Pod 所在节点上访问, client_address 获取到的是执行 curl 命令节点的 IP 地址, 在 Pod 所在节点上访问, client_address 获取到的是节点本身的x.1地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 在10.244.0.0节点上执行:
curl 10.244.2.16  # client_address: 10.244.0.0

# 在10.244.1.0节点上执行:
curl 10.244.2.16  # client_address: 10.244.1.0

# 在10.244.2.0节点上执行:
curl 10.244.2.16  # client_address: 10.244.2.1

# 在10.244.3.0节点上执行:
curl 10.244.2.16  # client_address: 10.244.3.0

关于为什么在 10.224.2.0 节点上访问, 显示的 client_address 却是 10.224.2.1, 这和系统的路由规则有关, 简单看一下:

在 node14 上执行以下命令, 里面有这么几行内容, 可以看到如果在 10.224.2.0 主机上访问 PodIP 为 10.224.2.x 的应用程序,最终会被路由到 10.224.2.1 上,再转给 10.224.2.x

1
2
3
4
5
[root@node14 ~]# ip route
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink               # 发往10.224.0.x的数据, 全部都由flannel.1设备处理, 流向下个地址10.244.0.0
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink               # 发往10.224.1.x的数据, 全部都由flannel.1设备处理, 流向下个地址10.244.2.0
10.244.2.0/24 dev cni0 proto kernel scope link src 10.244.2.1   # 发往10.224.2.x的数据, 全部都由cni0设备处理, 且指定了cni0的网卡地址为10.224.2.1
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink               # 发往10.224.3.x的数据, 全部都由flannel.1设备处理, 流向下个地址10.244.3.0

ClusterIP

Service, 相当于是为单个 pod 或者 pod 组,创建了一个代理, Service 本身的一个端口会与 Pod 内应用程序的端口做一层映射关系

ClusterIP-Service 长得像这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[root@node12 ~]# k describe svc echo-service-cluster
Name:              echo-service-cluster
Namespace:         default
Labels:            app=echoserver
Annotations:       <none>
Selector:          app=echoserver
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.102.238.26    # 这是Service的IP地址
IPs:               10.102.238.26
Port:              <unset>  80/TCP  # 这是Service本身的端口
TargetPort:        80/TCP           # 这是应用程序的端口
Endpoints:         10.244.1.51:80,10.244.2.17:80,10.244.3.32:80
Session Affinity:  None
Events:            <none>

所以 ClusterIP 类型的 Service 创建好之后, 可以在集群内的任意主机上通过ServiceIP:ServicePort访问到应用程序, 集群外访问不到

需要注意的是, 当 deployment 在多个节点上都有副本 Pod 时, 在集群内的任意主机上访问 Service, 应用程序获取到的 client_address 是会动态变化的, 因为 Service 代理的是一组 Pod,Service 有一套自己的负载均衡体系, 可能会路由到任何一台 Pod 副本所在的主机上

在 Pod 内也可以通过ServiceIP:ServicePort访问到应用程序, 像下面这样, 如果被路由到 Pod 所在的主机节点上, client_address 获取的是 ip route 指定的地址, 如果被路由到其他节点上, client_address 获取的是执行 curl 命令的 Pod 本身的 IP 地址

1
kubectl exec pod-name -- curl ServiceIP:ServicePort

NodePort

NodePort 类型的 Service 是 ClusterIP 的超集, 所以 ClusterIP 可以用的访问方式, NodePort 同样适用

NodePort-Service 的内容长得像这样, 注意它里面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[root@node12 ~]# k describe svc echo-service-node
Name:                     echo-service-node
Namespace:                default
Labels:                   app=echoserver
Annotations:              <none>
Selector:                 app=echoserver
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.99.66.47         # 这是Service的IP地址
IPs:                      10.99.66.47
Port:                     <unset>  80/TCP     # 这是Service本身的端口
TargetPort:               80/TCP              # 这是应用程序的端口
NodePort:                 <unset>  31706/TCP  # 这是为Service分配的可访问端口NodePort
Endpoints:                10.244.1.51:80,10.244.2.17:80,10.244.3.32:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

NodePort-Service 创建好之后, 最大的功能在于, 它可以在集群外的主机上通过集群内任意NodeIP:NodePort访问到应用程序

与 ClusterIP-Service 一样, Service 代理的是一组 Pod, 所以在访问时, 应用程序获取到的 client_address 也是会根据 NodeIP 的变化而变化

CNAME

Service 在创建的时候, 一般都会有一个 service-name 字段, 集群内的任意 Pod 都可以通过这个字段访问到 Service

1
2
3
4
5
[root@node12 ~]# k get svc
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
echo-service-cluster   ClusterIP   10.102.238.26   <none>        80/TCP         26m
echo-service-node      NodePort    10.99.66.47     <none>        80:31706/TCP   10m
kubernetes             ClusterIP   10.96.0.1       <none>        443/TCP        30d

比如上面的案例, 在集群内的任意 Pod 内执行curl 10.102.238.26curl echo-service-cluster的结果是一样的

在实际运用中, 微服务架构之间的 RCP 调用, 在不知道 ServiceIP 的情况下, 就可以通过 CNAME 来访问对应的服务

源 IP

前面写了通过 Pod, ClusterIP-Service,NodePort-Service 访问时, 应用程序在不同访问方式下获取 client_address 的值