【学习记录】ArgoCD GitOps 实践三:Git 项目组织方法

在根目录创建 ApplicationSet

在 Git 仓库根目录下创建 argo-apps.yaml 的文件,定义 ArgoCD 的 ApplicationSet:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps-mycluster # ApplicationSet 名称,建议带集群名称后缀
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - git: # 通过当前Git仓库的apps目录下的子目录自动生成Application
        repoURL: git@yourgit.com:your-org/your-repo.git
        revision: HEAD
        directories:
          - path: apps/*
  template:
    metadata:
      name: "{{.path.basename}}-mycluster" # 自动创建的Application的名称格式为: 目录名-集群名
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: git@yourgit.com:your-org/your-repo.git
        targetRevision: HEAD
        path: "apps/{{.path.basename}}" # 自动生成的 Application 使用的 YAML 内容在对应子目录下
      destination:
        name: mycluster # Application 被部署的目的集群
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
Read more...

【学习笔记】ArgoCD GitOps 实践二:集群与 Git 仓库管理

管理方法

推荐每个集群使用一个 Git 仓库来存储该集群所要部署的所有应用的 YAML 与配置。

如果多个集群要部署相同或相似的应用,可抽取成单独的 Git 仓库,作为 submodule 引用进来。

这样做的好处是既可以减少冗余配置,又可以控制爆炸半径。submodule 可能被多个 Git 仓库共享(即多个集群部署相同应用), 但如果不执行git submodule update --remote的话,引用的 commit id 是不会变的, 所以也不会因为上游应用更新而使所有使用了该应用的集群一下子全部都更新。

添加集群

Read more...

【学习笔记】ArgoCD GitOps 实践一:ArgoCD 的安装与配置

使用 kustomize 安装 ArgoCD

官方提供了安装 ArgoCD 的 YAML,可以使用 kubectl 一键安装,但我建议使用 kustomize 来安装,因为这样一来可以将自定义配置声明并持久化到文件中,避免直接集群中改配置,也利于后续 ArgoCD 的自举,即用 ArgoCD 自身来用 GitOps 管理自身。

准备一个目录:

1
2
mkdir argocd
cd argocd

下载 argocd 部署 YAML:

1
wget -O install.yaml https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

后续升级 argocd 时,可以用上面相同命令更新下 YAML 文件。

创建kustomization.yaml:

1
2
3
4
5
6
7
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: argocd
patches:
  - path: argocd-cm-patch.yaml
resources:
- install.yaml
Read more...

【深度解析】Kube Proxy如何监听Service和Endpoint以及更新策略规则

本文会探讨Kubernetes另一个核心网络组件Kube-Proxy,它承担着Service及其后端Pod对宿主机配置的影响。

一、 先聊一下三个核心API

这三个API在不同版本下的Kube-Proxy发挥着主要作用,尤其是后两者。先上几个三个资源的常用配置一步步展开。

1. Service

Service作用核心其实就是提供一个ClusterIP和域名的配置容器以及EP/EPS组织,并没有一个单独的controller进行管理,而是被endpointslice-controller所控制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
  labels:
    app: my-app
  annotations:
    description: "This is a demo service"
spec:
  selector:
    app: my-app
    tier: backend
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
      name: http
  ## 粘性会话,一般用于长连接并且开启ClusterIP的场景
  sessionAffinity: None
  ## Cluster和Local,默认前者,后者用于本地流量请求
  ## 例如说,在Node1的PodA请求ServiceB,只能路由到Node1的ServiceB的Pod地址,如果本地没有则无法请求,报文会被iptables drop掉
  externalTrafficPolicy: Cluster
  ## 控制NotReady的Pod是否要包含进Service,为true的话有可能导致流量损失
  publishNotReadyAddresses: false
  ipFamilyPolicy: SingleStack
  ipFamilies:
    - IPv4
  healthCheckNodePort: 30009

2. Endpoints、EndpointSlice

EndpointSlice是在k8s 1.19版本开始默认支持的,相较于Endpoints能支持更大规模部署,受限于etcd的value值大小,ep即一个Service只能部署6000个节点, 而eps理论上可以无上限,并且eps支持网络拓扑,它可以根据集群节点的资源信息,按需部署Pod数量。具体文档在这K8s文档-EndpointSlice

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  annotations:
    ## 每一次Service或Pod改动都会update这个字段
    endpoints.kubernetes.io/last-change-trigger-time: "2022-08-19T03:32:20Z"
  generateName: python-server-headless-
  generation: 27
  labels:
    endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
    kubernetes.io/service-name: python-server-headless
    service.kubernetes.io/headless: ""
  name: python-server-headless-5xvm5
  namespace: net-test
  ownerReferences:
    - apiVersion: v1
      blockOwnerDeletion: true
      controller: true
      kind: Service
      name: python-server-headless
      uid: fb7d24b3-c7cb-4b1a-8bc2-7caf2cb32101
  resourceVersion: "6538663"
  uid: d7475629-ac4f-46bc-9aeb-479417162ec3
addressType: IPv4
endpoints:
- addresses:
  - 10.100.58.221
  conditions:
    ready: true
    serving: true
    terminating: false
  nodeName: k8s-node02
  ## 控制器基本上靠着这些信息对eps进行增删改查
  targetRef:
    kind: Pod
    name: python-server-65b886d59c-nnktx
    namespace: net-test
    uid: b3cf8828-5311-44cc-b9b1-6e8165f793f4
ports:
  - name: ""
    port: 80
    protocol: TCP

二、Kube-Proxy的源码分析

首先我们还是需要先认识一下Kube-Proxy的整体架构:

  • cmd/kube-proxy/app/server.go 这是程序主入口,对宿主机的参数和启动参数进行注入
    • cmd/kube-proxy/app/server_linux.go 对应不同编译平台的代码,这里会继续设置部分协议栈的一些配置,如nf_conntrack、iptables等
      • 这里就会应用到pkg/proxy中的各种 Proxy 创建器,根据不同设置有对应的代理
        • pkg/proxy/iptables/proxier.go
        • pkg/proxy/ipvs/proxier.go
      • pkg/util/async/bounded_frequency_runner.go 这是控制更新iptables/ipvs的核心组件,可以自定义更新的频率窗口
      • 两个资源的同步器,被绑定在infomer的回调函数上,用来存储监听到的缓存
        • pkg/proxy/endpointschangetracker.go
        • pkg/proxy/servicechangetracker.go

下面是kube-proxy的代码主流程,没有太复杂的内容

kube-proxy.png

Read more...

【问题小解决】提供一个直接进入Pod网络命名空间的Shell脚本

我们使用 Kubernetes 时难免发生一些网络问题,往往需要进入容器的网络命名空间 (netns) 中,进行一些网络调试来定位问题,这里直接提供一个脚本,申请各种步骤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #!/bin/bash # 函数:进入网络命名空间并进行调试 enter_netns() { local pod_name=$1 local namespace=$2 local container_name=$3 # 获取指定 Pod 中指定容器的容器 ID container_id=$(kubectl -n $namespace get pod $pod_name -o jsonpath="{.status.containerStatuses[?(@.name==\"$container_name\")].containerID}" | sed 's|containerd://||') if [ -z "$container_id" ]; then echo "未找到 Pod $pod_name 中容器 $container_name 在命名空间 $namespace 中的容器 ID" exit 1 fi # 获取容器进程的 PID pid=$(crictl inspect $container_id | grep -i '"pid"' | head -n 1 | awk '{print $2}' | sed 's/,//') if [ -z "$pid" ]; then echo "未找到容器 $container_id 的 PID" exit 1 fi # 进入容器的网络命名空间 echo "进入 Pod $pod_name 中容器 $container_name 的网络命名空间,PID 为 $pid" nsenter -n --target $pid } # 检查是否提供了必要的参数 if [ $# -ne 3 ]; then echo "用法: $0 <pod_name> <namespace> <container_name>" exit 1 fi # 调用函数进入网络命名空间 enter_netns $1 $2 $3 调试网络 成功进入容器的 netns,可以使用节点上的网络工具进行调试网络,可以首先使用ip a验证下 ip 地址是否为 pod ip:
Read more...

【公司分享学习】容器磁盘占满导致CPU飙高

这是公司大佬的一次内部分享,我尝试简短总结一下

问题描述

某服务的其中两个副本异常,CPU 飙高。

指标排查

  1. 查看container_cpu_usage_seconds_total监控,CPU 飙升,逼近 limit。

  2. 查看container_cpu_cfs_throttled_periods_total监控,CPU 飙升伴随 CPU Throttle 飙升,所以服务异常应该是 CPU 被限流导致。

  3. 查看container_cpu_system_seconds_total监控,发现 CPU 飙升主要是 CPU system 占用导致,容器内pidstat -u -t 5 1可以看到进程%system占用分布情况。

  4. perf top看 system 占用高主要是 vfs_write 写数据导致。

img.png

Read more...

【问题小排查】关于device or Resource Busy如何排查

背景 在 kubernetes 环境中,可能会遇到因目录被占用导致 pod 一直 terminating: 1 Aug 27 15:52:22 VM-244-70-centos kubelet[906978]: E0827 15:52:22.816125 906978 nestedpendingoperations.go:270] Operation for "\"kubernetes.io/secret/b45f3af4-3574-472e-b263-c2b71c3b2ea0-default-token-fltdk\" (\"b45f3af4-3574-472e-b263-c2b71c3b2ea0\")" failed. No retries permitted until 2021-08-27 15:54:24.816098325 +0800 CST m=+108994.575932846 (durationBeforeRetry 2m2s). Error: "UnmountVolume.TearDown failed for volume \"default-token-fltdk\" (UniqueName: \"kubernetes.io/secret/b45f3af4-3574-472e-b263-c2b71c3b2ea0-default-token-fltdk\") pod \"b45f3af4-3574-472e-b263-c2b71c3b2ea0\" (UID: \"b45f3af4-3574-472e-b263-c2b71c3b2ea0\") : unlinkat /var/lib/kubelet/pods/b45f3af4-3574-472e-b263-c2b71c3b2ea0/volumes/kubernetes.io~secret/default-token-fltdk: device or resource busy" 本文记录下排查方法。 找出目录被谁占用的 看下目录哪个进程 mount 了: 1 2 $ find /proc/*/mounts -exec grep /var/lib/kubelet/pods/0104ab85-d0ea-4ac5-a5f9-5bdd12cca589/volumes/kubernetes.io~secret/kube-proxy-token-nvthm {} + 2>/dev/null /proc/6076/mounts:tmpfs /var/lib/kubelet/pods/0104ab85-d0ea-4ac5-a5f9-5bdd12cca589/volumes/kubernetes.io~secret/kube-proxy-token-nvthm tmpfs rw,relatime 0 0 根据找出的进程号,看看是谁干的:
Read more...

【问题小排查】针对containerd V1.4.3拉取镜像的问题

问题描述

在 containerd 运行时的 kubernetes 线上环境中,出现了镜像无法下载的情况,具体报错如下:

1
Failed to pull image&nbsp;`  `"ccr.ccs.tencentyun.com/tkeimages/tke-hpc-controller:v1.0.0"`  `: rpc error: code = NotFound desc = failed to pull and unpack image&nbsp;`  `"ccr.ccs.tencentyun.com/tkeimages/tke-hpc-controller:v1.0.0"`  `: failed to unpack image on snapshotter overlayfs: failed to extract layer sha256:d72a74c56330b347f7d18b64d2effd93edd695fde25dc301d52c37efbcf4844e: failed to get reader from content store: content digest sha256:2bf487c4beaa6fa7ea6e46ec1ff50029024ebf59f628c065432a16a940792b58: not found

containerd 的日志中也有相关日志:

1
2
containerd[136]: time="2020-11-19T16:11:56.975489200Z" level=info msg="PullImage \"redis:2.8.23\""
containerd[136]: time="2020-11-19T16:12:00.140053300Z" level=warning msg="reference for unknown type: application/octet-stream" digest="sha256:481995377a044d40ca3358e4203fe95eca1d58b98a1d4c2d9cec51c0c4569613" mediatype=application/octet-stream size=5946

尝试复现

分析环境信息:

  • container v1.4.3 运行时。
  • 基于 1.10 版本的 docker 制作的镜像(比如 dockerhub 镜像仓库中的 redis:2.8.23)。

然后根据以上版本信息构造相同环境,通过如下命令拉取镜像:

1
2
$ crictl pull docker.io/libraryredis:2.8.23
FATA[0001] pulling image failed: rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/redis:2.8.23": failed to unpack image on snapshotter overlayfs: failed to extract layer sha256:4dcab49015d47e8f300ec33400a02cebc7b54cadd09c37e49eccbc655279da90: failed to get reader from content store: content digest sha256:51f5c6a04d83efd2d45c5fd59537218924bc46705e3de6ffc8bc07b51481610b: not found

问题复现,基本确认跟 containerd 版本与打包镜像的 docker 版本有关。

Read more...

【学习记录】如何在不容器进行抓包,学习使用ksniff

Kubernetes 环境中遇到网络问题需要抓包排查怎么办? 传统做法是登录 Pod 所在节点,然后 进入容器 netns,最后使用节点上 tcpdump 工具进行抓包。 整个过程比较繁琐,好在社区出现了 ksniff 这个小工具, 它是一个 kubectl 插件,可以让我们在 Kubernetes 中抓包变得更简单快捷。

本文将介绍如何使用 ksniff 这个工具来对 Pod 进行抓包。

安装

Read more...

【学习记录】在容器中使用crontab

准备 crontab 配置文件

新建一个名为crontab的配置文件,写定时任务规则:

1
* * * * * echo "Crontab is working" > /proc/1/fd/1

/proc/1/fd/1表示输出到容器主进程的标准输出,这样我们可以利用 kubectl logs 来查看到执行日志。

准备 Dockerfile

  • CentOS 镜像
1
2
3
4
5
6
7
8
9
FROM docker.io/centos:7

RUN yum -y install crontabs && rm -rf /etc/cron.*/*

ADD crontab /etc/crontab
RUN chmod 0644 /etc/crontab
RUN crontab /etc/crontab

CMD ["crond", "-n"]
Read more...