阅读 293

k8s 实战

目的
随着部署组件的增多和数据中心的增长, 配置以及管理并保持系统的正常运行变得复杂与困难. 
在想要获得足够高的资源利用率并降低硬件成本情况下, 把组件部署在什么地方变得越来越难以决策. 
自动化调度, 配置, 监管以及故障处理, 正式 k8s 解决的痛点
复制代码
带来的便捷
为应用程序提供一致的环境, 便于今早发现问题
提供更加便捷的持续交付能力
复制代码
k8s架构
组件介绍

控制面板用于控制集群工作, 其中包含的组件可以部署在多个节点上以保证高可用性

kubernetes API 服务器 控制面板中各个组件之间通信的桥梁
schedule 应用调度器
controller manager 执行集群级别功能, eg: 复制组件, 持续跟踪工作节点, 处理失败节点等
etcd 分布式数据存储, 持久化存储集群配置
工作节点是运行容器化应用的机器
容器运行时 docker 等其他的容器类型
kubelet 与 api 服务器通信, 并管理它所在节点的容器
kube-proxy 负责组件之间的负载均衡网络流量
容器生命周期示例
docker build -t kubia .
docker images | grep kubia
docker run --name kubia-container -p 8080:8080 -d kubia
curl localhost:8080
docker inspect kubia-container
docker exec -ti kubia-container bash
docker stop kubia-container
docker rm kubia-container
docker tag kubia mingch94/kubia
docker push mingch94/kubia
环境准备
yum install bash-completion 安装 bash-completion
source <(kubectl completion bash) 生效 kubectl 的自动补全
alias k=kubectl 设置 kubectl 的别称
source <(kubectl completion bash | sed s/kubectl/k/g) 调整别称 k 的自动补全
alias kcd='k config set-context $(k config current-context) --namespace' 设置切换名称空间别称
集群操作
kubectl cluster-info 查看集群状态
kubectl get nodes 列出集群节点
kubectl describe node docker-desktop 查看集群节点更多信息
k run kubia --image=mingch94/kubia --port=8080 部署应用
k get po 列出 pod
k describe po kubia 查看 pod 更多详细信息
k apply -f k8s.io/examples/co… 创建 rc
k get rc 查看 rc 资源
k expose po kubia --type=LoadBalancer --name kubia-http 创建服务, 对外暴露 pod
k get svc 查看服务对象
curl localhost:8080 访问服务
k scale rc kubia —replicas=3 水平扩展 pod 到 3 个
get pods -o wide 展示更多列
k get po kubia -o yaml 检查现有 pod 的 yaml 描述
k explain po 解析 pod 定义
k explain pod.spec 查看 spec 属性
k create -f kubia-manual.yaml 从 yml 创建 pod
kubectl logs kubia-manual -c kubia 查看 pod 中容器 kubia 的日志
k port-forward kubia-manual 8888:8080 将本地 8888 流量转发到 pod 的 8080 端口
k get po --show-labels 查看 pod 以及所有的 label
k get po -L creation_method,env 标签单独展示到对应列
k label po kubia-manual creation_method=manual 创建标签
k label po kubia-manual-v2 env=debug --overwrite 修改标签
k get po -l creation_method=manual 指定标签值查询 pod
k get po -l env 指定标签名查询 pod
k get po -l '!env' 指定不包含 env 标签名查询 pod, 支持 !, in, notin
k label node docker-desktop gpu=true 给 node 添加指定的标签
k annotate pod kubia-manual mycompany.com/someannotat… bar" 创建注解
k get ns 查看集群中所有的命名空间
k get po -n kube-system 查看指定命名空间的 pod
k create ns custom-namespace 创建命名空间
k config get-contexts 查看当前上下文以及对应使用的名称空间
k delete pod kubia-gpu 根据名称删除 pod
k delete po -l creation_method=manual 根据标签删除 pod
k delete ns custom-namespace 删除名称空间以及其下边所有的 pod
k delete po --all 删除所有的 pod
k delete all --all 删除当前名称空间下所有资源
k logs kubia-liveness --previous 获取前一个容器日志
k edit rc kubia 编辑 rc
k delete rc kubia --cascade=false 仅删除 rc, 保留 pod 正常运行
k get rs 查看 rc
k get ds 查看 ds
k get jobs 查看 jobs
k exec kubia-p5mz5 -- curl -s http://10.96.72.201 pod 中执行命令, - - 代表 kubectl 命令的结束, 如待执行命令无 - 参数可去掉
k exec -it kubia-72v4k -- bash 交互式进入 pod 中执行命令
k get endpoints 查看 endpoints 资源
k get nodes -o json json 方式展示 node 信息
k get po --all-namespaces 显示所有 ns 下的的 pod
k get ingresses 查看 ingress 服务
k run dnsutils --image=tutum/dnsutils 使用带 dns 的 pod
k proxy 通过 kube-proxy 与 api 服务器进行交互, 访问根路径可以查看当前支持的 api
k create -f kubia-deployment-v1.yaml --record 创建 deployment 资源, --record 表示记录版本号
k patch deployment kubia -p '{"spec": {"minReadySeconds": 10}}' 设置 pod 创建完成后至少 10s 后才可用
k set image deployment kubia nodejs=luksa/kubia:v2 修改镜像为 luksa/kubia:v2 , 镜像名称为 nodejs
k rollout status deployment kubia 查看升级过程
k rollout undo deployment kubia 回滚到上一个版本
k rollout undo deployment kubia --to-revision=1 回滚到指定版本
k rollout pause deployment kubia 暂停滚动升级
k rollout resume deployment kubia 取消滚动升级, 回滚到上一个版本
k apply -f kubia-deployment-v3-with-readinesscheck.yaml 通过一个完整的 yml 定义来修改对象

当一个 pod 包含多个容器时候, 这些容器总是会运行在同一个节点. 也就是一个 pod 绝不会跨越多个工作节点

由于不能讲多个进程聚集在一个单独的容器中, 因此需要一种更高级的结构将容器绑定在一起, 也就是 pod. 可以把 pod 理解为现实世界中的主机

同一个 pod 中的容器共享相同的 ip 和端口, 因此可以通过 localhost 与同一个 pod 中的其他容器通信

pod 定义的几个主要部分
API 版本以及 YAML 描述的资源类型 apiVersion, kind
metadata 包括 名称, 命名空间, 标签以及其他容器信息 name, namespace, labels
spec 包括 pod 内容的实际说明(容器, 卷和其他数据) containers, volumes
status 包含当前运行的 pod 的信息(所处条件, 容器描述和状态, 内部 IP 等) conditions, containerStatuses, phase, hostIP
# Kubernets API 版本为 v1
apiVersion: v1
# 资源类型为 pod
kind: Pod
metadata:
  # 资源名称为 kubia-maual
  name: kubia-manual
spec:
  containers:
  # 指定镜像
  - image: luksa/kubia
    # 容器名称
    name: kubia
    ports:
    # 容器监听的端口为 8080
    - containerPort: 8080
      protocol: TCP
复制代码

名称空间不对正在运行的对象做任何隔离, 同时名称空间之间的网络是否隔离取决于 k8s 采取的网络解决方案

如果需要部署的应用自动保持运行且保持健康, 无任何手动干预, 则不能直接创建 pod, 而是创建 rc 或 deployment 这样的资源来管理 pod.

直接创建的 pod 只会被 k8s 监控失败重启(节点上的 Kubelet 操作的), 但是整个节点失败, 无法被新节点替换

探测容器机制
HTTP GET 探针 对容器的 IP 地址执行 HTTP GET 请求. 响应状态码为 2xx 或 3xx 时候则认为探测成功
TCP 套接字探针 尝试与容器指定端口建立 TCP 连接. 连接成功建立则探测成功. 否则容器重启
Exec 探针 在容器内执行任意命令, 并检车命令的退出状态码. 状态码为 0 则探测成功
标签匹配过程
ReplicationController 三要素
label selector 标签选择器, 用于确定 rc 作用域中有哪些 pod
replica count 副本个数, 指定应运行的 pod 数量, 会影响现有的 pod
pod template pod 模板, 用于创建新的 pod 副本
rc 版本
apiVersion: v1
# rc 类型
kind: ReplicationController
metadata:
  # rc 名称
  name: kubia
spec:
  # 副本数量
  replicas: 3
  # 标签选择器
  selector:
    app: kubia
  # 创建新 pod 时候的模板
  template:
    metadata:
      labels:
        # 模板中的标签必须与标签选择器中的一致, 避免无休止的创建 pod
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
        ports:
        - containerPort: 8080
复制代码
rs 版本
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    matchExpressions:
      - key: app
        operator: In
        values:
         - kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
复制代码
ds 版本
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ssd-monitor
spec:
  selector:
    matchLabels:
      app: ssd-monitor
  template:
    metadata:
      labels:
        app: ssd-monitor
    spec:
      nodeSelector:
        disk: ssd
      containers:
      - name: main
        image: luksa/ssd-monitor
复制代码
batch-job 版本
apiVersion: batch/v1
kind: Job
metadata:
  name: batch-job
spec:
  template:
    metadata:
      labels:
        app: batch-job
    spec:
      restartPolicy: OnFailure
      containers:
      - name: main
        image: luksa/batch-job
复制代码
service 版本
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  # 会话亲和性, 同一个clientIP 的流量会被发送到同一个 pod
  sessionAffinity: ClientIP
  ports:
  # 服务可用端口
  - port: 80
    # 服务将连接转发到的容器端口
    targetPort: 8080
  selector:
    # 具有 app=kubia 标签的 pod 都属于该服务
    app: kubia
复制代码
内部集群测试服务
创建 pod 请求发送到服务的集群 ip 并记录响应, 可通过 pod 的日志查看响应
ssh 到任意节点 在远程 node 上执行 curl
kubectl exec 在一个已经存在的 pod 中执行 cul 命令
将服务暴露给外部客户端
NodePort 集群节点在节点上打开一个端口, 并将该端口上的流量重定向到基础服务
LoadBalance NodePort 类型的一种扩展, 使得服务可以通过专用的负载均衡器访问
Ingress 通过一个 IP 地址公开多个服务, 运行在 7 层网络
service 示例 负载均衡器示例 ingress 示例
几种常见的卷类型

更多详细见 kubernetes.io/docs/concep…

emptyDir 存储临时数据的简单空目录
hostPath 将目录从工作节点的文件系统挂载到 pod 中, 指向节点文件系统上特定的文件或目录. 持久性的卷, 但是 pod 一旦调度到其它节点就会有问题
gitRepo 检出 git 仓库的内容来初始化券
nfs 挂载到 pod 的 nfs 共享卷
gcePersistentDIsk google 的高效能存储磁盘卷
awsElasticBlockStore aws 的服务弹性块存储卷
azureDisk Microsoft Zaure 磁盘卷
configMap, secret, downwardAPI 将 k8s 部分资源和集群信息公开给 pod 的特殊卷
persistentVolumeClaim 预置或动态配置的持久性存储类型
emptyDir 卷使用
apiVersion: v1
kind: Pod
metadata:
  name: fortune
spec:
  containers:
  - image: luksa/fortune
    name: html-generator
    volumeMounts:
    #  名为 html 的卷挂载在容器 /var/htdocs, 可读可写
    - name: html
      mountPath: /var/htdocs
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    # 名为 html 的卷挂载在容器 /usr/share/nginx/html, 只读
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  # 名为 html 的卷, 挂载在上面两个容器中
  - name: html
    emptyDir: {}
复制代码
gitRepo 卷使用
apiVersion: v1
kind: Pod
metadata:
  name: gitrepo-volume-pod
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    gitRepo:
      repository: https://github.com/chaimcode/kubia-website-example.git
      revision: master
      directory: .
复制代码
api 服务使用
k proxy
curl http://localhost:8001

{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
    "/apis/admissionregistration.k8s.io",
    "/apis/admissionregistration.k8s.io/v1",
    "/apis/admissionregistration.k8s.io/v1beta1",
    "/apis/apiextensions.k8s.io",
    "/apis/apiextensions.k8s.io/v1",
    "/apis/apiextensions.k8s.io/v1beta1",
    "/apis/apiregistration.k8s.io",
    "/apis/apiregistration.k8s.io/v1",
    "/apis/apiregistration.k8s.io/v1beta1",
    "/apis/apps",
    "/apis/apps/v1",
    "/apis/authentication.k8s.io",
    "/apis/authentication.k8s.io/v1",
    "/apis/authentication.k8s.io/v1beta1",
    "/apis/authorization.k8s.io",
    "/apis/authorization.k8s.io/v1",
    "/apis/authorization.k8s.io/v1beta1",
    "/apis/autoscaling",
    "/apis/autoscaling/v1",
    "/apis/autoscaling/v2beta1",
    "/apis/autoscaling/v2beta2",
    "/apis/batch",
    "/apis/batch/v1",
    "/apis/batch/v1beta1",
    "/apis/certificates.k8s.io",
    "/apis/certificates.k8s.io/v1beta1",
    "/apis/compose.docker.com",
    "/apis/compose.docker.com/v1alpha3",
    "/apis/compose.docker.com/v1beta1",
    "/apis/compose.docker.com/v1beta2",
    "/apis/coordination.k8s.io",
    "/apis/coordination.k8s.io/v1",
    "/apis/coordination.k8s.io/v1beta1",
    "/apis/events.k8s.io",
    "/apis/events.k8s.io/v1beta1",
    "/apis/extensions",
    "/apis/extensions/v1beta1",
    "/apis/networking.k8s.io",
    "/apis/networking.k8s.io/v1",
    "/apis/networking.k8s.io/v1beta1",
    "/apis/node.k8s.io",
    "/apis/node.k8s.io/v1beta1",
    "/apis/policy",
    "/apis/policy/v1beta1",
    "/apis/rbac.authorization.k8s.io",
    "/apis/rbac.authorization.k8s.io/v1",
    "/apis/rbac.authorization.k8s.io/v1beta1",
    "/apis/scheduling.k8s.io",
    "/apis/scheduling.k8s.io/v1",
    "/apis/scheduling.k8s.io/v1beta1",
    "/apis/storage.k8s.io",
    "/apis/storage.k8s.io/v1",
    "/apis/storage.k8s.io/v1beta1",
    "/healthz",
    "/healthz/autoregister-completion",
    "/healthz/etcd",
    "/healthz/log",
    "/healthz/ping",
    "/healthz/poststarthook/apiservice-openapi-controller",
    "/healthz/poststarthook/apiservice-registration-controller",
    "/healthz/poststarthook/apiservice-status-available-controller",
    "/healthz/poststarthook/bootstrap-controller",
    "/healthz/poststarthook/ca-registration",
    "/healthz/poststarthook/crd-informer-synced",
    "/healthz/poststarthook/generic-apiserver-start-informers",
    "/healthz/poststarthook/kube-apiserver-autoregistration",
    "/healthz/poststarthook/rbac/bootstrap-roles",
    "/healthz/poststarthook/scheduling/bootstrap-system-priority-classes",
    "/healthz/poststarthook/start-apiextensions-controllers",
    "/healthz/poststarthook/start-apiextensions-informers",
    "/healthz/poststarthook/start-kube-aggregator-informers",
    "/healthz/poststarthook/start-kube-apiserver-admission-initializer",
    "/livez",
    "/livez/autoregister-completion",
    "/livez/etcd",
    "/livez/log",
    "/livez/ping",
    "/livez/poststarthook/apiservice-openapi-controller",
    "/livez/poststarthook/apiservice-registration-controller",
    "/livez/poststarthook/apiservice-status-available-controller",
    "/livez/poststarthook/bootstrap-controller",
    "/livez/poststarthook/ca-registration",
    "/livez/poststarthook/crd-informer-synced",
    "/livez/poststarthook/generic-apiserver-start-informers",
    "/livez/poststarthook/kube-apiserver-autoregistration",
    "/livez/poststarthook/rbac/bootstrap-roles",
    "/livez/poststarthook/scheduling/bootstrap-system-priority-classes",
    "/livez/poststarthook/start-apiextensions-controllers",
    "/livez/poststarthook/start-apiextensions-informers",
    "/livez/poststarthook/start-kube-aggregator-informers",
    "/livez/poststarthook/start-kube-apiserver-admission-initializer",
    "/logs",
    "/metrics",
    "/openapi/v2",
    "/readyz",
    "/readyz/autoregister-completion",
    "/readyz/etcd",
    "/readyz/log",
    "/readyz/ping",
    "/readyz/poststarthook/apiservice-openapi-controller",
    "/readyz/poststarthook/apiservice-registration-controller",
    "/readyz/poststarthook/apiservice-status-available-controller",
    "/readyz/poststarthook/bootstrap-controller",
    "/readyz/poststarthook/ca-registration",
    "/readyz/poststarthook/crd-informer-synced",
    "/readyz/poststarthook/generic-apiserver-start-informers",
    "/readyz/poststarthook/kube-apiserver-autoregistration",
    "/readyz/poststarthook/rbac/bootstrap-roles",
    "/readyz/poststarthook/scheduling/bootstrap-system-priority-classes",
    "/readyz/poststarthook/start-apiextensions-controllers",
    "/readyz/poststarthook/start-apiextensions-informers",
    "/readyz/poststarthook/start-kube-aggregator-informers",
    "/readyz/poststarthook/start-kube-apiserver-admission-initializer",
    "/readyz/shutdown",
    "/version"
  ]
}
复制代码
deployment 版本
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    matchExpressions:
      - key: app
        operator: In
        values:
         - kubia
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: luksa/kubia:v1
        name: nodejs
复制代码
deployment 需注意的属性
minReadySeconds pod 最少可用等待时间, 即使就绪探针返回成功, 也需要等改时间
revisionHistoryLimit 限制历史版本数量
maxSurge 滚动升级时候, 期望的副本数, 默认 25%, 则如果副本数为 4, 则滚动期间不会超过 5 个 pod 实例
maxUnavailable 滚动升级时候, 允许最多多少 pod 不可用状态.