k8s

应用上云需要注意的问题

背景

现在上云是一个趋势,无论是私有云还是公有云。上云之给开发和运维都有带来了非常大的好处,比如自动化运维,持续开发和部署等,这是虚拟机无法给到的。
那么应用如何上云呢?上云的过程中可能会遇到哪些问题呢?

应用容器化

应用上云最基本的要求是应用要容器化。即编写一个Dockerfile,创建你的应用镜像。

当然Dockerfile也不能乱写,不然应用可能会启动失败或者有风险。那么容器化需要考虑哪些问题呢?

主要就是这4个方面:稳定性、可用性、性能、安全
file

应用本身

  • 启动速度:如果应用启动速度很慢的话,既不利于应用发布也不利于故障恢复。
    • 如何优化:看启动需要拉取的数据是否是必须的
  • 健康检查:
    • 优雅启动:liveness,readness,startup probe
    • 优雅终止:如何正确处理SIGTERM。用httpget还是exec
  • 启动参数:容器的启动参数也不能过长,否则可能导致容器无法启动。还有比如es需要设置java内存使用量。

Dockerfile

  • 用什么作为基础镜像:基础镜像越小越好。
  • 需要装哪些工具:看后续是否有调试的需求,当然也可以用busybox来进行调试。
  • 多少个进程:
    • 主次进程需要分清楚,哪个是决定容器状态的主进程。健康检查检查哪个进程,哪个进程控制容器的退出。
    • 进程炸弹:防止应用一直fork进程,导致pid用完,pod被驱逐到了新的节点上,再次重复。
  • 代码和配置分离:
    • 配置如何管理:
      • 传入方式:
        • 环境变量env
        • volume mount
      • 数据来源:
        • secret
        • configmap
        • Downward API
  • 分层的控制:镜像层不是越多越好,层多的话性能会有损耗。把相同功能的命令放到一层去。
  • Entrypoint:是容器的入口,容器启动时要去启动的进程。

容器额外开销和风险

log driver

比如docker的log driver,docker logs命令查看的容器日志就是容器内的进程中的stdout和stderr这些标准日志,再通过log driver转储到本地文件中。

这里可能会遇到一个问题:如果应用启动输出了大量的debug log,很快就把标准输出的buffer占满了,log driver也是读取这个buffer来持久化。这时就需要看log driver是处于那种模式了:

  • blocking mode:
    • docker默认的模式,如果数据没有及时落盘,log driver会阻塞新日志写入,就无法看到最新的日志输出。
    • 应用启动变慢,需要等日志转储完。
    • 如果平台有应用启动超时的配置。那么应用就可能会启动失败,而在虚拟机上是正常的。
  • Non blocking mode:如果buffer满了,log driver来不及转储,日志可以继续写入,在缓冲区内被覆盖的日志就直接丢弃了。

容器共用kernel

容器和虚拟机不一样,所有容器是共用kernel的,包括:

  • 系统参数配置共享
  • 进程数共享 – Fork bomb
  • fd数共享
  • 主机磁盘共享

容器化应用的资源监控

容器中看到的资源是主机资源:

  • Top
  • Java runtime.GetAvailableProcesses()
  • cat /proc/cpuinfo
  • cat /proc/meminfo
  • df -k

    原因

    因为/proc文件系统并不知道用户通过cgroup给这个容器做了什么限制,所以它返回的还是整个宿主机的资源。

    解决方案

    查询 / proc/1/cgroup 是否包含 kubepods 关键字 (docker 关键字不可靠)。

    11:cpu,cpuacct:/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-
    pod521722c3_85a8 11e9 87fc 3cfdfe57c998.slice/9568ccOd8ae182395elce172e2cac
    723c4781a999e89e0f9f10d33af079a56e9

    包含此关键字,则表明是运行在 Kubernetes 之上。

内存

  • 配额
    cat /sys/fs/cgroup/memory/ memory.limit_in_bytes
    36854771712
  • 用量
    cat /sys/fs/cgroup/memory/memory.usagein bytes
    448450560

CPU

  • 配额,分配的 CPU个数= quota / period, quota = -1 / 代表 besteffort
    • cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
      -1
    • cat /sys/fs/cgroup/cpu/cpu.cfs_period_us
      100000
  • 用量
    • cat /sys/fs/cgroup/cpuacct/cpuacct.usage_percpu(按CPU区分)
      140669504971 148500278385 149957919463 152786448674
    • cat /sys/fs/cgroup/cpuacct/cpuacct.usage
      12081100465458

其他方案

  • Lxcfs
    不挂载主机的/proc目录,通过lxcfs来实现隔离。lxcfs在宿主机上维护进程组的信息。然后容器启动时把lxcfs维护的进程组信息所在目录挂载到容器的/proc目录,在容器中获取/proc信息时,实际上获取的是宿主机上该容器的进程信息。

  • Kata
    VM 中跑 container。

  • Virtlet
    直接启动VM。

对应用造成的影响

  • Java
    • Concurrent GC Thread:正常是看cpuinfo文件,影响应用的GC性能。
    • Heap Size:默认是操作系统内存的百分比
    • 线程数不可控。
  • Node.js
    多线程模式启动的 Thread 数量过多,导致 OOM Kill。

应用上云

pod spec

pod spec也就是pod的template中的spec,需要注意的点是:

  • 初始化需求 (init container)
  • 需要几个主 container
  • 权限?
    • Privilege
    • SecurityContext(PSP)
  • 共享哪些 Namespace(PID, IPC, NET, UTS, MNT)
  • 配置管理
  • 优雅终止
  • 健康检查
    • Liveness Probe
    • Readiness Probe
  • DNS 策略以及对 resolv.conf 的影响
  • imagePullPolicy Image 拉取策略

健康检查注意:Probe误用可能会导致pid被用完。

比如一个pod的readnessprobe中定义了一个shell脚本,脚本是curl endpoint,看返回码是否正常。
但是这个脚本是跑在envoy的pod中,envoy只是一个反向代理软件,没有任何维护子进程的能力。kubelet中的probe manager发起健康检查,创建了一个新进程,然后这个进程的父进程就是容器的entrypoint,也就是envoy进程。这就导致一直在创建新的子进程,旧的子进程变成僵尸进程,而且没有被父进程回收,可能会导致pid被用完。

如何防止pid泄漏

  • 单进程容器
  • 多进程容器
    • 容器的初始化进程必须负责清理 fork 出来的所有子进程
    • 开源方案
      • Tini https: //qithub.com/krallin/tini
      • 采用 Tini 作为容器的初始化进程(PID=1),容器中僵尸进程的父进程会被置为1
      • ENTRYPOINT ["/tini”, "–" ],CMD ["/your/program", "-and", "-its",“arguments"]
  • 如果不采用特殊初始化进程
    • 建议采用 HTTP get 作为 Probe
    • 为 exec Probe 设置合理的超时时间

pod数据管理

每个应用实例需要多少磁盘,磁盘的IO要求,本地还是网络。
local-ssd:独占的本地磁盘,独占IO,固定大小,读写性能高。
Local-dynamic:基于 LVM,动态分配空间,效率低。
file

高可用部署

  • 需要多少实例?
  • 如何控制故障域,部署在几个可用区,region,AZ,集群?
  • 如何进行精细的流量控制?看集群的算力
  • 如何做按地域的顺序更新?
  • 如何回滚?
  • 更新策略
    • MaxSurge
    • MaxUnavailable(需要考虑 ResourceQuota 的限制)
  • 深入理解 PodTemplateHash 导致的应用的易变性

如何应对基础架构带来的影响

file
pdb的介绍在这里

服务发布

需要把服务发布至集群内部或者外部,服务的不同类型:

  • ClusterlP ( Headless)
  • NodePort
  • LoadBalancer
  • ExternalIP

证书管理和七层负载均衡的需求
需要 gRPC 负载均衡如何做?
DNS需求
与上下游服务的关系

无状态应用管理

  • Replicaset 副本集
    • 用什么 Pod 模版创建多少个实例。
    • replicas: 3
  • Deployment 部署过程
    • 版本管理
      annotations:
      deployment.kubernetes.io/revision: "1"
      spec:
      revisionHistoryLimit: 10
    • 滚动升级策略
      strategy:
      rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 1
      type: RollingUpdate

有状态应用管理

statefulset相比deployment,多了:

serviceName: xxxx
volumeClaimTemplates:
  - apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/instance: www
      name: data
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 8Gi
      storageClassName: nfs-client
      volumeMode: Filesystem

有状态应用现在大多都是通过Operator来管理。

分类: k8s
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x