kubernetes controller manager 报错 CIDRNotAvailable

今天查看集群事件

kubectl get events --sort-by=.metadata.creationTimestamp
kubectl get events -A -w

发现 controller manager 时不时有报错:

Node xxx status is now: CIDRNotAvailable

和同行稍微交流一会,参考官网文档 https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/

将controller-manager的配置 --allocate-node-cidrs 改为 false,错入日志就没有了。

实际上集群也没有问题,只是不断会有这样的错误日志。具体原因还待深究。目前只做记录。

这个配置的官方解释是:

--allocate-node-cidrs
Should CIDRs for Pods be allocated and set on the cloud provider.

【Calico系列】6 Calico 镜像初探

这篇文章从 calico 的安装内容中了解 calico 的组件,基于calico v3.10.3。

标准的 kubernetes 安装 calico 的过程,参考官网手册:https://docs.projectcalico.org/getting-started/kubernetes/

参考安装的 yaml 文件,calico 包含一个配置文件,一个密钥文件,几个角色配置,一个deployment和一个daemonset:

  1. secret: calico-etcd-secrets
  2. ConfigMap: cni的网络配置:
  3. ClusterRole: calico-kube-controllers/calico-node
  4. ClusterRoleBinding: calico-kube-controllers/calico-node
  5. ServiceAccount:calico-node/calico-kube-controllers
  6. Deployment:calico-kube-controllers
  7. DaemonSet:calico-node
    1. init容器
      1. install-cni (This container installs the CNI binaries)
      2. flexvol-driver ( Felix 通过socket与api交互,socket落盘.)
    2. calico-node
      1. felix 每个节点上的一个守护进程,负责编写路由和ACLs(访问控制列表). 还有一些其它节点上需要设置的东西。
      2. bird bgp广播
      3. confd 监听etcd修改BGP配置 AS number, logging levels, IPAM信息等

我们主要来看两个容器:

  1. calico-kube-controller
  2. calico-node

Calico Kubernetes controllers

源码地址:https://github.com/projectcalico/kube-controllers

官方文档:https://docs.projectcalico.org/reference/kube-controllers/configuration

必须有 k8s 的 api 读取权限,才可以监控数据变化。

必须使用 etcd 存储 calico 数据,下列controller才会生效:

  1. policy controller: 监控policies事件同步到 Calico 数据存储,和配置policies 。
  2. namespace controller: 监控 ns 事件同步到 Calico 数据存储和配置 Calico profiles 。
  3. serviceaccount controller: service accounts 监控和配置 Calico profiles。
  4. workloadendpoint controller: pod labels 监控和 Calico workload endpoints 配置。
  5. node controller: 监控节点增减,移除calico相关联的数据,可以配置监控 host endpoint 的创建和同步。默认情况下不启用这个controller。

install-cni

用来在本机生成一些配置。

https://github.com/projectcalico/cni-plugin

1591000684051

flexvol-driver

FlexVolumes的驱动,用来保障 Pods 和 守护进程的安全通信。

https://github.com/projectcalico/pod2daemon

1591000699990

Calico-node

核心容器,源码地址:https://github.com/projectcalico/node

1590720690537

calico-node 基本思路是将所有的组件打包到一个项目中进行管理,减少新手上手的难度。(不由得想起了Istio 1.5)。

calico-node 使用了 runit 进行进程管理, 简单说就是entrypoint 中将需要运行的应用拷贝到 /etc/service/enable 目录下, 当runsvdir检查应用时,runsvdir 会启动一个 runsv 进程来执行和监控run脚本。

runit 是个进程守护程序,有着纯净的进程状态、可靠的日志记录工具、并进行系统快速启停。同时可移植、封装友好、轻量。同类应用还有 systemd/monit/supervisor 等。

普通情况下 calico-node 中主要运行这三个应用:

  1. felix 每个节点上的一个守护进程
  2. bird bgp广播
  3. confd 监听etcd修改BGP配置 AS number, logging levels, IPAM信息等

贴一个启动脚本rc.local,可以帮助理解:

# Handle old CALICO_NETWORKING environment by converting to the new config.
if [ -n "$CALICO_NETWORKING" ]; then
	echo "WARNING: $CALICO_NETWORKING will be deprecated: use $CALICO_NETWORKING_BACKEND instead"
	if [ "$CALICO_NETWORKING" = "false" ]; then
		export CALICO_NETWORKING_BACKEND=none
	else
		export CALICO_NETWORKING_BACKEND=bird
	fi
fi

# Run the startup initialisation script.  These ensure the node is correctly
# configured to run.
calico-node -startup || exit 1

# Set the nodename based on the value picked by the startup procedure.
if [ ! -f "/var/lib/calico/nodename" ]; then
	echo "/var/lib/calico/nodename does not exist, exiting"
	exit 1
fi
NODENAME=$(cat /var/lib/calico/nodename)
export NODENAME

# If possible pre-allocate any tunnel addresses. 
calico-node -allocate-tunnel-addrs || exit 1

# Create a directly to put enabled service files
mkdir /etc/service/enabled

# XXX: Here and below we do all manupulations on /etc/service avoiding rm'ing
# dirs contained in Docker image. This is due to bug in Docker with graphdriver
# overlay on CentOS 7.X kernels (https://github.com/docker/docker/issues/15314)

# Allow felix to be disabled, for example, if the user is running Felix
# outside the container.
if [ -z "$CALICO_DISABLE_FELIX" ]; then
  cp -a /etc/service/available/felix /etc/service/enabled/
fi

case "$CALICO_NETWORKING_BACKEND" in
	"none" )
	# If running in policy only mode, we don't need to run BIRD / Confd.
	echo "CALICO_NETWORKING_BACKEND is none - no BGP daemon running"
	;;
	"vxlan" )
	# If running in VXLAN-only mode, we don't need to run BIRD / Confd.
	echo "CALICO_NETWORKING_BACKEND is vxlan - no need to run a BGP daemon"
	;;
	"gobgp" )
	# Run calico-bgp-daemon instead of BIRD / Confd.
	echo "CALICO_NETWORKING_BACKEND is gobgp - run calico-bgp-daemon"
	cp -a /etc/service/available/calico-bgp-daemon /etc/service/enabled/
	sh -c 'for file in `find /etc/calico/confd/conf.d/ -not -name 'tunl-ip.toml' -type f`; do rm $file; done'
	cp -a /etc/service/available/confd /etc/service/enabled/
	;;
	* )

	# Enable the confd and bird services
	cp -a /etc/service/available/bird  /etc/service/enabled/
	cp -a /etc/service/available/bird6 /etc/service/enabled/
	cp -a /etc/service/available/confd /etc/service/enabled/
	;;
esac

if [ "$CALICO_DISABLE_FILE_LOGGING" = "true" ]; then
	rm -rf /etc/service/enabled/bird/log
	rm -rf /etc/service/enabled/bird6/log
	rm -rf /etc/service/enabled/confd/log
	rm -rf /etc/service/enabled/felix/log
	rm -rf /etc/service/enabled/calico-bgp-daemon/log
fi

echo "Calico node started successfully"

参考资料


kubernetes pod 挂载不同路径

今天给一个daemonset 创建挂载卷,要求多个pod不会挂载同一个挂载卷,查看了相关资料,有两种方式可以达到:

  1. 使用 statefulset的volumeClaimTemplate
  2. 加载环境变量挂载subpath

第一种方式因为需要使用 storageClassName ,目前的环境没有做相关的东西,嫌麻烦就先不做了,第二种方式简单,需要在普通挂载的基础上新增两个内容:

  1. 环境变量 valueFrom/fieldRef
  2. 挂载 subPathExpr
apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      subPathExpr: $(POD_NAME)
      readOnly: false
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods

参考资料


使用 fluentd 抓取 k8s 的组件日志并推送至 EFK 日志栈

以容器方式启动的组件,常常随着容器的停止而销毁,给平时定位问题带来了一定的难度。

这一篇记录如何使用 fluentd 采集 kubernetes 的容器组件的日志,并推送到es中。本文适用的 kubernetes 版本为 v1.10。

日志类型

k8s系统组件有两种类型:在容器中运行的和不在容器中运行的。例如:

  • 在容器中运行的 kube-scheduler 和 kube-proxy。
  • 不在容器中运行的 kubelet 和容器运行时(例如 Docker)

kubelet和docker的日志都由 journald 进行管理,定位问题时问题不大。

容器组件日志位置

文件夹 /var/log/containers 下包含了所有组件的日志链接。

进入这个文件夹我们可以看到一家人的组件排的整整齐齐:

$ ls /var/log/containers

calico-node-xxx.log
coredns-xxx.log
kube-apiserver-xxx.log
kube-controller-manager-xxx.log
kube-proxy-xxx.log
kube-scheduler-xxx.log
traefik-ingress-controller-xxx.log

实际上这是个软链接,后边还跟了两层软链接:

-> /var/log/pods

-> /var/lib/docker/containers/

更多需要考虑的

想象一下我们使用 kibana 查看日志,在满屏的日志中,我们需要区分这些日志:

  • 不同的节点
  • 同一节点的不同组件
  • 同一组件的不同容器

其中同一节点不同组件,我们可以在 fluentd 中对 日志来源打 tag 标记。可以参考 fluentd 官方帮助 https://docs.fluentd.org/input/tail

不同容器可以通过 fluentd 配置把文件名附到日志字段。

节点名也同样如此。那么fluentd 容器如何获得节点名呢?但是这有个窍门,为 fluentd 容器增加一个环境变量:

env:
  - name: MY_NODE_NAME
    valueFrom:
      fieldRef:
        fieldPath: spec.nodeName

更多实用的变量可参考官方:https://kubernetes.cn/zh/docs/tasks/inject-data-application/environment-variable-expose-pod-information/

开始

基本思路是fluentd 的配置文件使用 configmap 进行保存。

然后将所有组件的日志和configmap都挂在到 fluentd 容器中。

创建 fluentd 配置文件

注意 fluentd 要配置 read_from_head true ,否则会有组件启动日志薛定谔采集的问题。

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-for-k8s-component
data:
  fluent.conf: |-
    <source>
    @type tail
    read_from_head true
    path /var/log/containers/kube-apiserver-*.log
    path_key file_name 
    pos_file /log/fluentd/pos/pos-kube-apiserver.log
    tag runtime-kube-apiserver
    limit_recently_modified 7d
    multiline_flush_interval 1s
    <parse> 
        @type json
        time_key time
        time_type string
        time_format %Y-%m-%dT%H:%M:%S.%N%z
    </parse>
    </source>
    ... ...
    # 依葫芦画瓢把一系列的日志搞进来
    ... ...
    
    # 为每条记录附加节点字段
    <filter runtime-*>
      @type record_transformer
      <record>
        nodename "#{ENV['MY_NODE_NAME']}"
      </record>
    </filter>

    # es地址
    <match runtime-*>
    @type forward
    <server>
      host 1.2.3.4
      port 12345
      weight 50
    </server>
    <server>
      host 1.2.3.4
      port 12346
      weight 50
    </server>
    
    <buffer tag,time>
        @type file
        path /var/log/td-agent/buffer/docker
        timekey 3600
        timekey_wait 0s
        timekey_use_utc false
        flush_mode interval
        flush_interval 10s
        chunk_limit_size 20M
    </buffer>
    </match>

创建 fluentd damenset

需要注意的有:

  • 采集容器在每个节点上都有,所以要避免 calico 分配IP导致路由表难看,直接使用 host 的IP即可。
  • 注意污染和容忍的配置,要容忍所有污染,否则一旦新增一个污染就取不到这个节点的日志了。
  • 镜像尽可能选新的
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-for-k8s-component
  labels:
    k8s-app: fluentd-for-k8s-component
    version: v1
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-for-k8s-component
  template:
    metadata:
      labels:
        k8s-app: fluentd-for-k8s-component
        version: v1
    spec:
      containers:
        - name: fluentd
          image: "fluent/fluentd:v1.4"
          resources:
            limits:
              memory: 200Mi
            requests:
              cpu: 100m
              memory: 200Mi
          env:
            - name: MY_NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
          volumeMounts:
            - name: fluentd-pos
              mountPath: /log/fluentd/pos
            - name: fluentd-config
              mountPath: /fluentd/etc/fluent.conf
              subPath: fluent.conf
            - name: var-log-containers
              mountPath: /var/log/containers
              readOnly: true
            - name: var-log-pods
              mountPath: /var/log/pods
              readOnly: true
            - name: var-log-real
              mountPath: /var/lib/docker/containers
              readOnly: true
          securityContext:
            runAsUser: 0
      terminationGracePeriodSeconds: 30
      volumes:
        - name: fluentd-config
          configMap:
            name: fluentd-for-k8s-component
            defaultMode: 420
        - name: fluentd-pos
          hostPath:
              path: /app/fluentd/pos
              type: DirectoryOrCreate
        - name: var-log-containers
          hostPath:
              path: /var/log/containers
              type: DirectoryOrCreate
        - name: var-log-pods
          hostPath:
              path: /var/log/pods
              type: DirectoryOrCreate
        - name: var-log-real
          hostPath:
              path: /var/lib/docker/containers
              type: DirectoryOrCreate
      hostNetwork: true
      tolerations:
      - operator: Exists
        effect: NoSchedule
      - operator: Exists
        effect: NoExecute
      - key: CriticalAddonsOnly
        operator: Exists

参考资料


通过 laravel horizon 和 telescope 加强队列的管理

原文链接:Laravel HorizonとLaravel Telescopeでできること オープンロジにおけるQueueの運用管理 ,扫盲向而且挺实用,转载过来记录几个要点。

PPT地址: https://cdn.kelu.org/blog/2020/06/20190318-Laravue8-OPENLOGI.pdf

大家好,我是Open Logic的Igarashi。关于Vue和前台的大家讨论了很多,接下来我谈谈服务器端。

上个月我在 OpenLogi 的 Laravel 分享会中谈到 “Laravel 队列操作与管理”,今天我就这个话题进一步展开。

CgJcYXTTVMGJpoYfyyoAva

首先做个自我介绍…// (介绍他们公司的,主要做物流的,就不转了。)

SKkNfG6v4j24cRM83coW8F

我们的技术栈去年有分享过,在Qiita上可以找到。简单来说,服务器端是Laravel,前端是React。我将Vue用于新产品。

job 基础

20多年前,Bertrand Meyer在他的《Object-Oriented Software Construction》一书中提出了CQS(Command Query Seperation,命令查询分离)的概念,后来,Greg Young在此基础上提出了CQRS(Command Query Resposibility Segregation,命令查询职责分离),将CQS的概念从方法层面提升到了模型层面,即“命令”和“查询”分别使用不同的对象模型来表示。

采用CQRS的驱动力除了从CQS那里继承来的好处之外,还旨在解决软件中日益复杂的查询问题,比如有时我们希望从不同的维度查询数据,或者需要将各种数据进行组合后返回给调用方。此时,将查询逻辑与业务逻辑糅合在一起会使软件迅速腐化,诸如逻辑混乱、可读性变差以及可扩展性降低等等一些列问题。

对于Web应用程序,处理中要做的主要事情是读取或写入信息。

6j1pZKdkokWfBPAaEdKBgj

上图是一个简单的配置,读取意味着通过Web服务器从数据库读取数据并将结果返回给客户端。

如果有要写入的内容,则可以通过POST等方法通过Web服务器将其写入数据库,如果成功,则将结果返回给客户端。

原则上,我们不应在同一台Web服务器上进行读写。做到这一点的方法是利用工作。首先,以Command 的形式将其扔到 Web 服务器。Web 服务器将其加入队列。然后,执行可变处理的作业服务器使队列出队并执行写处理。

img

将结果返回给客户端的方式有很多种选择,可以使用Pusher 和 Redis。我们使用 Pusher 来实现。即使 Pusher 服务器不同,也可以正确地异步发送反馈。

看起来非常困难,但是由于Laravel从一开始就考虑到DDD和CQRS的设计,因此实现起来非常容易。

img

在Controller上有一个方法,例如 updateItem,CQRS 的做法就是创建一个名为updateItem的作业并将其分派。

job的状态管理

img

这是 Laravel Horizon和Laravel Telescope。 它们很相似。

img

Telescope 是一个开发调试工具,用于了解 Laravel 中的状态。虽然我们有了 Laravel Debugbar 这样的工具用于开发,但是 Laravel 中会发生各种事件,例如请求,查询和作业状态以及通知事件的结果,Telescope 可以将这些状态集中起来看。

从这里可以看到,有各种参数,例如Exception,log,query,model,event等。

Horizon 是用于生产环境的监视工具,而 Telescope 是用于给开发人员使用的工具。

两者都支持队列,不过 Horizon 仅支持 Redis。Telescope 不仅限于Redis,它还支持 DB 队列、SQS队列,并且可以看到所有同步作业的状态。

Horizon 主要是用于调试和监视。

img

如上图,在OpenLogi中,我们单独记录了job的状态,通过将所有更新处理整合到job中来,我们还可以跟踪谁进行了什么操作以及何时进行。

img

Telescope 也做了类似的事情,它会记录用户使用哪些参数运行的作业。这种记录会增加数据库的负担,不过它仅用于调试,线上不运行,但这是我想要的。

img

您可以看到针对查询,职位和电子邮件发出了什么样的查询,因此我认为这对开发很有用。

重新执行失败的作业也是操作中的常见情况。

img

img

关于这一点,Laravel Horizon 中有一个类似的页面,但是我们在Horizon 之前就设计了这类工具。

img

我需要像 Horizon 这样的监视工具,但同时我也希望能够像 Telescope 这样正确地记录日志,有点鱼和熊掌不可得兼的感觉。我正在寻找一种可以管理的工具,但是现在我们打算自己实现它。

总结

img


阿里的一些论文

google,Facebook等国外大佬经常在顶级会议上(SOSP/PLDI/NSDI等等)发论文,阿里也发表了好些的论文。以前整理了相关东西,不成章法,凑了这篇文章,以后研究的话再捡起来。

SIGCOMM 2020

《VTrace: Automatic Diagnostic System for Persistent Packet Loss in Cloud-Scale Overlay Network》

超大规模下的云网络异常定位、

传统网络工具 VS 大数据染色报文分析

云网络碰到类似问题只能用网工三板斧来处理: 抓包,ping, trace。

阿里云网络团队首次采用大数据结合染色报文的方式,通过大数据技术给这个交警配备了一个超强的大脑,让他能实时处理千万级网络数据,同时,结合染色报文技术让所有网络里面的数据包信息实时传递给我们的云网络交警。最后的结果就是这个云网络交警能实时感知到整个云网络每台设备的丢包和拥塞情况。阿里云网络给这个交警取了一个名字,叫vTrace。当用户在上网过程中碰到网络问题时,vTrace能很快找到对应链路上出现问题的节点在哪里,解决了云网络排查问题难的痛点,加快用户网络问题恢复过程。

SIGCOMM2019

来自阿里云智能的两篇论文

  • 《HPCC: High Precision Congestion Control》 高速网络拥塞控制协议HPCC
    • 阿里巴巴此前已通过对RDMA网络的改造,从网卡底层开始设计,结合自研交换机能力,建成全球最大规模的“RDMA高速网络”。
    • 在这次的论文中,阿里巴巴就提出了一种全新的网络协议——新一代高速网络拥塞控制协议HPCC(High Precision Congestion Control),不仅保证传输性能快,还能保证传输稳定,真正适用于当下的网络需求。
  • 《Safelyand Automatically Updating In-Network ACL Configurations with Intent Language》 使用意图语言安全且自动地更新网络内ACL配置

FAST2020

存储行业顶级国际会议FAST2020(18th USENIX Conference on File and Storage Technologies)在美国圣克拉拉举行,大会公开论文名单显示,阿里巴巴3篇第一作者论文入选,是全球入选数最多的企业。

在《POLARDB结合可计算存储: 高效支持云原生关系数据库的复杂查询操作》一文中,阿里团队针对PolarDB,把SQL和存储引擎的计算逻辑下推到底层共享存储,并通过定制SSD内部的FPGA进一步下推计算至存储节点的SSD内部,完成更高效率计算的同时,大幅降低主机和网络带宽占用,为PolarDB在复杂查询场景下带来4~5倍的吞吐提升。

另两篇文章,聚焦键值存储(KVS)。

在《FPGA加速Compactions操作,基于 LSM-tree的键值存储》一文中,研究团队首次引入异构硬件FPGA,实现KVS核心操作Compaction加速,较仅CPU处理能力提升2~5倍,整体吞吐性能提升23%,能效提升31.7%。

在《HotRing:热点感知的无锁内存键值系统》一文中,阿里团队提出新型热点感知内存KVS — HotRing,采用轻量级的热点识别策略,在未增加元数据存储开销的同时,还对幂率分布的热点场景进行大量优化,使得HotRing的引擎吞吐性能可达600M ops/s,单次访问平均只需100ns,比目前最快KVS性能提升2.58倍。

ASPLOS’19

摘要: 阿里云首次在ASPLOS上发表论文,第24届ACM编程语言和操作系统(ASPLOS’19),于2019年4月13日至17日,在普罗维登斯召开,阿里云高级技术专家郑晓代表团队在会上发表了技术报告。

第24届ACM编程语言和操作系统(ASPLOS’19),于2019年4月13日至17日,在普罗维登斯召开,阿里云高级技术专家郑晓代表团队在会上发表了技术报告。

论文主题为《Fast and Scalable VMM Live Upgrade in Large Cloud Infrastructure》,作者是张献涛,郑晓,沈益斌等。这篇论文被计算机系统结构的顶级会议ASPLOS’19接受,是业界对于VMM热升级这项突破性技术的认可。

论文ACM下载地址:https://dl.acm.org/citation.cfm?id=3304034 PDF下载地址:https://yq.aliyun.com/download/3532

该论文系统的阐述了当前云计算领域面临的基础架构带业务热升级问题。提出了一种新型的,比热迁移更行之有效的方法,特别适合超大规模集群范围的使用,解决了困扰云计算行业多年的问题。该方案在阿里云大规模采用,服务百万级别的客户虚拟机数量。论文解决了在客户业务不中断的情况下以毫秒级的速度更换底层虚拟化组件。

阿里云热升级技术特点决定了可以同时热升级任意数量任意规格的虚拟机,并且升级时间恒定。更难得的是,在业界尚未有异构计算设备热迁移方案的情况下,阿里云热升级技术同时支持异构计算等以设备直通方式工作的虚拟机。帮助ECS在过去五年进行了快速的升级迭代,保障了产品和业务的快速奔跑。

ASPLOS(编程语言和操作系统的体系结构支持会议)会议全称为ACM International Conference on Architectural Support for Programming Languages and Operating Systems,是综合体系结构、编程语言和操作系统三个方向的计算机系统领域顶级会议,为CCF A类会议。从1982年创办至今的三十多年里,ASPLOS推动了多项计算机系统技术的发展,包括(但不限于)RISC、RAID、大规模多处理器、Cluster架构和网络存储等。