helm images

適用情境,客戶是離線環境,沒辦法直接拉 container image,所以要先知道 helm chart 裏面用到的 container image,用這指令,就可以找到所有 helm chart 所使用到的 image,再用 skopeo/podman pull/docker pull 拉取下來,存為 tarball ,並帶到客戶端。

專案網址:https://github.com/nikhilsbhat/helm-images

安裝

安裝好 helm 以後,可以用以下指令安裝

helm plugin install https://github.com/nikhilsbhat/helm-images

使用

簡單說,就是拿 helm install 的指令來用,並改為 helm images get。

所以在配置好 helm repo 以後,就可以用 helm images 取得

例如 redis-operator

helm images get redis redis-operator/redis-operator

又例如 zabbix operator

helm images get zabbix zabbix-chart-7.0/zabbix-helm-chrt

就可以取得 container images 的網址。


題外話,應該要來熟悉一下 skopeo 怎麼用才是。

用trivy掃描KBOM

KBOM = Kubernetes Bills Of Material,就是 Kubernetes 的物料清單。

trivy 是由 Aqua Security 所開發的一個掃描工具,現在支援了 KBOM,可以依據 Kubernetes 務料清單裡的軟體來進行掃描,並產出報告。

安裝可以從 https://github.com/aquasecurity/trivy 來下載安裝,有提供以下方式

  • Container image
  • Debian package
  • RPM package
  • Homebrew

安裝好以後,就可以使用了。

那要掃描 KBOM ,要先產出 kbom.json,用以下指令就可以產出。這邊要注意,執行 trivy 的電腦上必須已經放置 ~/.kube/config ,這樣 trivy 才能存取到 k8s cluster。

trivy k8s cluster --format cyclonedx --output kbom.json

有了 kbom.json 以後,就可以用以下指令產出報告

trivy sbom kbom.json

我的 k8s 報告如下

2024-02-18T15:29:26.693+0800	INFO	Vulnerability scanning is enabled
2024-02-18T15:29:26.694+0800	INFO	Detected SBOM format: cyclonedx-json
2024-02-18T15:29:26.720+0800	WARN	No OS package is detected. Make sure you haven't deleted any files that contain information about the installed packages.
2024-02-18T15:29:26.721+0800	WARN	e.g. files under "/lib/apk/db/", "/var/lib/dpkg/" and "/var/lib/rpm"
2024-02-18T15:29:26.721+0800	INFO	Detected OS: ubuntu
2024-02-18T15:29:26.721+0800	WARN	This OS version is not on the EOL list: ubuntu 22.04.3
2024-02-18T15:29:26.721+0800	INFO	Detecting Ubuntu vulnerabilities...
2024-02-18T15:29:26.721+0800	INFO	Number of language-specific files: 3
2024-02-18T15:29:26.722+0800	INFO	Detecting kubernetes vulnerabilities...
2024-02-18T15:29:26.728+0800	INFO	Detecting gobinary vulnerabilities...

kbom.json (ubuntu 22.04.3)

Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


 (gobinary)

Total: 3 (UNKNOWN: 0, LOW: 0, MEDIUM: 3, HIGH: 0, CRITICAL: 0)

┌──────────────────────────────────┬─────────────────────┬──────────┬────────┬───────────────────┬────────────────┬──────────────────────────────────────────────────────────┐
│             Library              │    Vulnerability    │ Severity │ Status │ Installed Version │ Fixed Version  │                          Title                           │
├──────────────────────────────────┼─────────────────────┼──────────┼────────┼───────────────────┼────────────────┼──────────────────────────────────────────────────────────┤
│ github.com/containerd/containerd │ CVE-2023-25153      │ MEDIUM   │ fixed  │ 1.6.15            │ 1.5.18, 1.6.18 │ containerd: OCI image importer memory exhaustion         │
│                                  │                     │          │        │                   │                │ https://avd.aquasec.com/nvd/cve-2023-25153               │
│                                  ├─────────────────────┤          │        │                   │                ├──────────────────────────────────────────────────────────┤
│                                  │ CVE-2023-25173      │          │        │                   │                │ containerd: Supplementary groups are not set up properly │
│                                  │                     │          │        │                   │                │ https://avd.aquasec.com/nvd/cve-2023-25173               │
│                                  ├─────────────────────┤          │        │                   ├────────────────┼──────────────────────────────────────────────────────────┤
│                                  │ GHSA-7ww5-4wqc-m92c │          │        │                   │ 1.6.26, 1.7.11 │ containerd allows RAPL to be accessible to a container   │
│                                  │                     │          │        │                   │                │ https://github.com/advisories/GHSA-7ww5-4wqc-m92c        │
└──────────────────────────────────┴─────────────────────┴──────────┴────────┴───────────────────┴────────────────┴──────────────────────────────────────────────────────────┘

從報告可以看出軟體元件的版本跟漏洞報告。

如果不產出 kbom.json ,也可以用另外一個方式掃描

trivy k8s cluster --scanners vuln --report summary

不過這指令在我的 k8s cluster,會有問題,我猜想是我 k8s cluster 不夠力的關係,就先這樣吧。

trivy 也有 operator 可以安裝,有機會再來看看怎麼使用。

參考資料

如何檢查GitOps Manifests

Kubernetes 主要會用這幾種檔案格式:

  1. YAML
  2. Kustomize
  3. Helm Chart

在使用前,是否可以檢查呢?以下就分別來介紹

YAMLint

YAML 可以使用 yamllint 來檢查,在 RHEL9 裡可以直接安裝 yamllint 。

sudo yum install yamllint

用以下指令檢查

yamllint service.yml

可以使用 -f 來指定輸出格式。

Kustomize

kustomize 可以用 kustomize build 來檢查。

kustomize build path/to/folder

Red Hat 有寫一個輔助腳本:validate_manifest.sh

Helm

helm 本身有提供 subcommand 來檢查:helm lint

helm lint Chart.yaml

參考資料

Kubernetes刪除資源卡在Terminating

資料來源:

整理一下,若遇到刪除不了,一定是有原因。以 Namespace 來說,通常是有些資源刪除不掉,這時候要花點耐心去找。

若不想等,就是加上 --grace-period=0 --force ,強制來做。

Pod 也是類似的作法。

若還是不行,可以把 finalizer 清掉,以下是 pod 的例子,但 namespace 也可以用。

kubectl patch pod <pod>-p '{"metadata":{"finalizers":null}}'

最後一招,終極的作法,是直接存取 etcd ,這個會看是用哪個容器平台而有不同的作法。像 OpenShift ,etcd 是以 pod 形式存在,所以需要進到 etcd 的 pod 裡,用 etcdctl 去查詢跟刪除。

雖然通常都是建議不要,但難免會遇到這種情況,就參考看看。

Rancher安裝

Rancher 是 RKE 的 Web Console。

參考資料

事前準備

安裝 helm

Rancher 是用 helm 安裝的,所以要先安裝 helm (文件)

到這邊挑選版本下載:https://github.com/helm/helm/releases

wget https://get.helm.sh/helm-v3.13.0-linux-amd64.tar.gz tar xf helm-v3.13.0-linux-amd64.tar.gz sudo mv linux-amd64/helm /usr/local/bin/helm

安裝

增加 helm repo

helm repo add jetstack https://charts.jetstack.io helm repo add rancher-stable https://releases.rancher.com/server-charts/stable

jetstack 是 cert-manager 的 repo

安裝 cert-manager

helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.11.0 --set installCRDs=true

安裝 rancher

helm install rancher rancher-stable/rancher \ --namespace cattle-system \ --set hostname=rancher.experteam.com.tw \ --set replicas=1 \ --set useBundledSystemChart=true \ --set bootstrapPassword="Redhat12345678"

這邊要加 useBundledSystemChart=true ,若不加,佈署時會失敗。

若安裝失敗,可以用 uninstall

helm uninstall rancher -n cattle-system

配置 Load Balancer

由於會自動安裝 nginx-ingress ,所以可以使用以下幾種方式

  1. 在 F5 配置,將 master, worker 都配置進去即可。
  2. 新增一台 haproxy ,將 master, worker 配置到 backend
  3. 新增一台 nginx ,將 master, worker 配置進去

或者是偷懶的在 DNS 新增一筆 A record,填入 master 的 node IP 即可。

接下來應該就可以登入了。

RKE安裝

因公司某產品說在 RKE 平台上有問題,所以就找了資料,裝一套起來試試看,想不到意外的簡單。

參考資料

準備 VM 與配置 VM 環境

建立兩台 VM,可以使用 RHEL8

接下來動作,兩台都要作

移除 podman

sudo yum remove podman runc docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

安裝 docker-ce

sudo ls /etc/yum.repos.d wget https://download.docker.com/linux/centos/docker-ce.repo -O - | sudo tee /etc/yum.repos.d/docker.repo sudo yum install -y docker-ce-cli-20.10.24-3.el8 docker-ce-20.10.24-3.el8 sudo systemctl enable --now docker

關閉 swap

sudo swapoff -a

編輯 /etc/fstab ,註解掉 swap 該行

關閉 selinux

sudo setenforce 0 sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

關閉防火牆

sudo systemctl stop firewalld sudo systemctl disable firewalld

新增 sysctl 設定

新增 /etc/sysctl.d/99-k8s.conf

net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1

增加使用者 apadmin

sudo useradd apadmin sudo passwd apadmin sudo usermod -aG docker apadmin

安裝

在 bastion 上作業

下載 rke

因這邊有需求要裝 kubernetes 1.24,所以要下載對應的 rke 版本 1.43-rc3

這是在 bastion 主機上作

wget https://github.com/rancher/rke/releases/download/v1.4.3-rc3/rke_linux-amd64 -O /tmp/rke sudo mv /tmp/rke /usr/local/bin/rke sudo chmod +x /usr/local/bin/rke

配置 ssh 免密碼登入

ssh-keygen ssh-copy-id apadmin@<master> ssh-copy-id apadmin@<worker>

rke設定檔

先列出此版 rke 可安裝的 kubernetes 版本

rke config --list-version -all

輸出結果是

v1.24.8-rancher1-1 v1.25.5-rancher1-1 v1.23.14-rancher1-1

新增 cluster.yml ,裡面的 kubernetes_version 指定的 kubernetes 版本

cluster_name: ithome-rancher kubernetes_version: "v1.24.8-rancher1-1" nodes: - address: 10.11.23.81 user: apadmin role: [controlplane,worker,etcd] - address: 10.11.23.82 user: apadmin role: [worker,etcd] services: etcd: backup_config: enabled: true interval_hours: 6 retention: 60 network: plugin: flannel

開始安裝

執行以下指令就可以安裝

rke up

安裝後配置

安裝完成後,目錄裡會有 kube_config_cluster.yml 的檔案

這是給 kubectl 使用的檔案,先下載 kubectl

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" sudo mv kubectl /usr/local/bin sudo chmod +x /usr/local/bin/kubectl

把 kube_config_cluster.yml 複製為 ~/.kube/config

mkdir -p ~/.kube cp kube_config_cluster.yml ~/.kube/config

接下來就可以用 kubectl 指令查看

kubectl get nodes kubectl get pods -A

避免 ephemeral storage 空間用完

來源:Avoid running out of ephemeral storage space on your Kubernetes worker Nodes

Kubernetes 裡用 emptyDir 的 volume 的話,就是用 ephemeral storage,這塊空間就是 node 上的磁碟空間。所以用完的話,表示 node 上也沒空間了,其他 pod 在使用上可能就會有狀況。

node 上的位置可能會因發行版而有差異,文章裡提供的位置是:/var/lib/kubelet/pods//volumes/kubernetes.io~empty-dir//…

避免的方法有兩種。

第1種是在掛載 volume 時,設定 sizeLimit ,例如

volumes:
- name: www-content
  emptyDir:
    sizeLimit: 2Mi

缺點是當超過這限制時,kubernetes 會把這個 pod evict 掉。

第二種是設定 ResourceQuotas/LimitRange,先新增以下 YAML,然後 apply

apiVersion: v1
kind: ResourceQuota
metadata:
  name: default-resource-quotas
  namespace: my-application-namespace
spec:
  hard:
    limits.cpu: "2"
    limits.memory: 8Gi
    limits.ephemeral-storage: 2Gi
    requests.cpu: "1"
    requests.memory: 4Gi
    requests.ephemeral-storage: 1Gi
---
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limit-ranges
  namespace: my-application-namespace
spec:
  limits:
  - default:
      cpu: 100m
      memory: 128Mi
      ephemeral-storage: "2Mi"
    defaultRequest:
      cpu: 25m
      memory: 64Mi
      ephemeral-storage: "1Mi" 
   type: Container

再來在 deployment/statefulset 裡增加 resource 指定,例如

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-deployment-3
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: helloworld-deployment-3
  replicas: 2
  template:
    metadata:
      labels:
        app.kubernetes.io/name: helloworld-deployment-3
    spec:
      volumes:
      - name: www-content
        emptyDir: {}
      containers:
        - name: hello-world
          image: helloworld:1.0
          volumeMounts:
          - mountPath: /www
            name: www-content
          resources:
            requests:
              ephemeral-storage: "1Mi"
            limits:
              ephemeral-storage: "2Mi"


在 Kubernetes 裡雖然可以很容易擴充,但不表示資源無限,反而更應該去管控好,避免浪費。

k8s.gcr.io改為registry.k8s.io

新聞:k8s.gcr.io Redirect to registry.k8s.io – What You Need to Know | Kubernetes

簡單的說,k8s.gcr.io 要被關掉了,所以從 k8s.gcr.io 拉取的 image ,未來會有影響,例如砍掉 pod 以後,有可能拉不到 image 了。

所以要找出所有使用 k8s.gcr.io 的 deployment/statefulset/pod … 資源,方法有兩種。

第1種是用 kubectl 來找

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

簡單的說,就是列出所有 pod 的 YAML,取出裡面的 image ,整理過以後,列出來。

第二種是用 kubectl krew 安裝 community-images plugin

kubectl krew install community-images
kubectl community-images

使用以上其中一種方法找到以後,該怎麼辦?

接下來就是用 kubectl edit 去修改 image 位址。

若是用 gitops 或 helm 的方法,作法也相似,就是去修改 manifest yaml ,然後重新佈署就可以了。

[Kubernetes]Pod rebalancing and allocations

文章來源:Pod rebalancing and allocations

簡單整理如下:

  • 應用程式是 deployment,有兩個 pod
  • 本來只有一個 node,兩個 pod 都會佈署在上面,所以如果一個 node 掛了,那麼應用程式就無法回應了。現在增加另外一個 node ,一般會預期 pod 移動一個到新的 node 上,但實際上 Kubernetes 不會做任何動作。

可以怎麼做?

Pod affinity

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: ellery-lab
spec:
  # 省略
  template:
    metadata:
      labels:
        app: MyTestApp
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: disktype
                operator: In
                values:
                - ssd

pod topology spread constraints

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: ellery-lab
spec:
  # 省略
  template:
    metadata:
      labels:
        app: MyTestApp
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: zone
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            foo: bar

descheduler

這個是需要另外安裝的,安裝以後會在 kube-system 裏面找到,是一個 pod 。

The descheduler pod is run as a critical pod in the kube-system namespace to avoid being evicted by itself or by the kubelet

安裝完成以後,還需要設定 policy ,比較麻煩一些,但可以做到動態的處理,功能也比較多。

[Kubernetes]沒有downtime的佈署

來源:Zero-Downtime Rolling Deployments in Kubernetes | Rakuten Engineering Blog

以下是摘錄。

在刪除掉 Pod 之前,流量還是會導向 Pod,要處理這種狀況,就需要去處理 SIGTERM。

文章建議三種方法。

  • 在 pre-stop hook 裡用 sleep
  • 延遲應用程式中止
  • 動態延遲應用程式中止

在 pre-stop hook 裡用 sleep

spec:
  terminationGracePeriodSeconds: 60
  containers:
  - name: "{{APP_NAME}}"
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh","-c","sleep 20"]

缺點,sleep 的時間是要去測量跟估算的,每個應用程式不同。而且,pre-stop 的主要用途不是用來做這事情的。

延遲應用程式中止

這個是要改應用程式的程式碼。

這裡有舉一個 wrapper script 的例子

#!/bin/bash

HAPROXY_PID=-1

# wait for 10 seconds, then send a USR1 signal to HAproxy
kill_haproxy(){
  sleep 10
  kill -USR1 $HAPROXY_PID
}
 
# invoke kill_haproxy function when receives the TERM signal
trap kill_haproxy SIGTERM

# Support the user hitting Ctrl-C, but only in interactive shells
if [[ -t 1 ]] ; then
  trap 'kill -USR1 "$HAPROXY_PID"' SIGINT
fi
 
haproxy -f config.cfg & HAPROXY_PID=$!
 
if [ -z "$HAPROXY_PID" ]
then
  echo "haproxy: Haproxy failed to start"
else
  echo "haproxy: haproxy started"
fi

在 Dockerfile 就這樣用

FROM alpine:3.5

# install bash, tini and haproxy
RUN apk add --no-cache bash tini haproxy=2.0.14-r0
 
# Run tini
ENTRYPOINT ["/usr/local/bin/tini", "—"]
CMD ["/launcher.sh"]

缺點跟前者差不多。

動態延遲應用程式中止

這個說不一定可用,就跳過了。

結論

好好處理 SIGTERM 。