树莓派运行 nfs-server

1 安装

apt-get install portmap nfs-common nfs-kernel-server

modprobe nfs
modprobe nfsd

2 编辑配置文件

以上一篇挂载的移动硬盘为例,将它作为一个 nfs 目录

cat <<EOF > /etc/exports
/home/pi/disk2t *(rw,sync,no_subtree_check,no_root_squash)
EOF

3 运行服务

systemctl restart nfs-kernel-server.service

# 确认运行状况,显示active即说明成功(不用在意exited)
systemctl status nfs-kernel-server.service

# 或
ps aux | grep nfs

树莓派挂载移动硬盘

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

参考资料