树莓派挂载移动硬盘

1 插上硬盘

lsblk

1591145558312

发现一个1.8T的磁盘,并且没有挂载点。

2 挂载

# 创建挂载点
mkdir -p /home/pi/disk2t

# 挂载
mount /dev/sda1 /home/pi/disk2t

3 挂载成功

lsblk

1591145655671

发现有了挂载点,再进去看看,发现已经能正常读写了。

4 卸载

umount -v /dev/sda1         #  通过设备名卸载  
umount -v /home/pi/disk2t   #  通过挂载点卸载  

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