Kubernetes 基础以及 MiniKube 实战

507 阅读7分钟

Kubernetes(k8s) 是一个容器编排系统。

一、工作方式及组件:

k8s cluster = 多个 master node + 多个 worker node

有多个云服务器,其中选择一个云服务器为 master。在每个云服务器上安装三大件(kubelet、kubectl、kubeadm)

下面这张图是 k8s 集群的架构和基础组件:

image.png

Control Plane:控制面板,会为集群做出全局决策。

  • etcd:管理 k8s 集群的 k-v 数据库。
  • scheduler:负责调度,就是使 pod 到合适的 node 上运行。
  • api server:负责公开 k8s 的 api,负责接收并处理 worker 节点的请求。
  • controller-manager(c-m)
  • cloud-controller-manager(c-c-m)
  • master node:主节点在 control plane 里面。

Node:可以理解为 worker 节点,负责维护 pod 的运行。

  • kubelet:node 节点的管理者,管理多个 pod,可与 api server 通信。
  • kube-proxy:node 节点上的网络代理,属于 service 概念的一部分,维护 node 节点的网络规则,外部访问时可以通过 kube-proxy 与 pod 进行通信。多个 node 的 kube-proxy 信息共享,这也是 service 的概念。

用户组件:kubeadm 和 kubectl 是为了用户方便而推出的组件,k8s 不依赖于与它们

  • kubeadm:快速部署 k8s 集群的工具。
  • kubectl:k8s 集群的命令行工具,方便用户进行管理。

二、k8s 中的核心资源

1、Namespace

namespace 默认用来隔离资源,不隔离网络。

命令行:

kubectl create ns hello // 创建一个 namespace

kubectl get ns // 获取所有 namespace

kubectl get pods // 获取所有 default 命名空间下的 pods

kubectl get pods -A // 获取所有 pod 和对应的 namespace

kubectl get pods -n namespace // 获取指定 namespace 下的所有 pods

yaml 文件

# 创建一个 namespace
apiVersion: v1
kind: Namespace
metadata:
  name: hello

2、Pod

pod 是 k8s 中应用的最小单位,指运行中的一组容器。

命令行:

kubectl run pod_name --image=mysql // 创建一个 pod,本质还是 docker 容器,所以需要指定镜像

kubectl describe pod pod-name // 查看指定 pod 的资源信息、构建流程、日志等

kubectl logs -f pod-name // 查看指定 pod 日志,-f 参数表示阻塞式追踪

kubectl get pod -owide // 查看所有 pod 的 详细信息(k8s 为每个 pod 分配了一个 ip 地址)

kubectl exec -it pod-name -- /bin/bash // 进入 pod 中

yaml 文件:

# run 一个 pod
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod_name
  name: pod_name
  namespace: default
spec:
  containers:
  - image: mysql
    name: mysql
  - image: nginx
    name: nginx

pod 特点:

  • pod 内所有容器,使用同一个 namespace
  • pod 内所有容器,共享同一个 hostname 和网络接口
  • pod 内所有容器,文件系统完全隔离

3、Deployment

控制 pod,使得 pod 拥有多副本、自愈、扩容等能力。

命令行:

kubectl create deployment deploy-name --image=mysql --replicas=3 // 部署一个容器,并创建 3 个副本

yaml 文件:

# deploy 一个多副本 pod
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-dep
  name: my-dep
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-dep
  template:
    metadata:
      labels:
        app: my-dep
    spec:
      containers:
      - image: nginx
        name: nginx

deployment 是一种无状态应用部署,也就是如果有 pod 下线,deployment 自愈出来的 pod 是不会记录之前的状态的。

1)多副本能力

使用 deployment 可以在部署 pod 时指定多个副本,副本会创建在不同 node 上面(也就是不同服务器)

2)自愈能力

deployment 命令和 kubectl run 有什么区别吗?

经过实践我们会发现,deploy 的 pod 直接 delete 的话,pod 会自动恢复,这也就是赋予 pod 的自愈能力。

真正的删除则需要删除部署:kubectl delete deploy deploy-name

以上所说的是 pod 出现问题,k8s 会重启一个 pod 来实现自愈,那如果是 node 宕机呢?

k8s 也会在等待超过设定阈值之后,进行故障转移,也就是在剩下正常运行的 node 中重启断连的 pod

3)扩缩容能力

kubectl scale deploy/deploy-name --replicas=n // 扩缩容命令

4)滚动更新能力

kubectl set image deploy/deploy-name mysql=mysql:2.0.1 --record // record 表示记录,方便回滚

kubectl rollout history deploy/deploy-name // 查看版本更新记录

滚动更新能力保证使新 pod 运行起来之后,才会下线老版本 pod ,保证了服务更新不停机。

5)版本回退能力

版本回退的过程也是滚动更新的过程

kubectl rollout history deploy/deploy-name // 查看版本更新记录
kubectl rollout undo deploy/deploy-name --to-revison=n // 回退到哪个版本

三、k8s 的网络模型

1、Service

Service 是将一组具有相同功能的 pod 公开为网络服务的抽象方法。

因为 pod 的副本在不同的 node,也就有着不同的 ip,所以 service 抽象出来一层公共接口以供访问。

命令行:

// port 表示暴露的端口(此时只能在集群内访问),target-port 表示映射 pod 的端口
kubectl expose deploy deploy-name --port=8000 --target-port=80

// --type=NodePort 参数表示端口已经暴露到外网
kubectl expose deploy deploy-name --port=8000 --target-port=80 --type=NodePort

kubectl get service // 查看暴露出的服务信息

yaml 文件:

# 将 pod 的公开网络服务进行一层封装
apiVersion: v1
kind: Service
metadata:
  labels:
    app: my-dep
  name: my-dep
spec:
  selector:
    app: my-dep
  ports:
  - port: 8000
    protocol: TCP
    targetPort: 80
  type: NodePort # 表示将 service 封装的网络服务暴露到外网

service 将多个 pod 封装为公共 ip 之后,会使用负载均衡的方式来保证每个 pod 的访问量。

2、Ingress

Ingress 为 Service 的统一网关入口

也就是说 k8s 网络模型为三层:

  • Ingress 统一网关入口,本质是 nginx 用来反向代理。
  • Service 抽象 pod 服务,提供负载均衡。
  • pod 真正提供服务的应用

所有流量打进来先进入 Ingress,从而分配给不同 service,再由 service 负载均衡到 pod

四、k8s 的存储模型

我们需要将 pod 的文件挂载在宿主机方便修改,比如把 pod 内部的 /logs 挂载到外部为 /log_tmp

但 k8s 上直接挂载到宿主机上存在风险,这是由于 k8s 特殊的自愈和故障转移能力,如果 pod 宕机并转移到了集群的其他宿主机上,那么之前挂载的目录则会出错。

所以 k8s 抽象出了一个存储层,用于挂载 pod 目录方便外部修改。

1、ConfigMap

pod 中的配置文件抽象挂载出来为配置集(ConfigMap)

命令行:

kubectl create confMap redis-conf --from-file=redis.conf

yaml 文件

apiVersion: v1
# data 是所有真正的数据存储形式为 key-value 对(key:默认是文件名 value:配置文件的内容)
data:
  redis.conf: |
    appendonly yes
kind: ConfigMap
metadata:
  name: redis-conf
  namespace: default

五、记录一下 k8s-tutorials 操作和踩坑

GitHub - guangzhengli/k8s-tutorials: k8s tutorials | k8s 教程

1、准备环境:

操作系统:云服务器 ubuntu 22.04

网络环境:clash 代理

k8s 环境:minikube 使用 docker 作为驱动

2、安装 docker

# 更新系统
sudo apt update

# 安装依赖
sudo apt install apt-transport-https curl gnupg-agent ca-certificates software-properties-common -y

# 添加 GPGK key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 添加仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"

# 安装 docker
sudo apt install docker-ce docker-ce-cli containerd.io -y

# 添加当前将用户进用户组(minikube 不建议使用 root 用户启动)
sudo usermod -aG docker $USER
newgrp docker

# 验证 docker 安装是否成功
docker version
docker run hello-world

3、安装 minikube

# 更新并重启机器
sudo apt update -y
sudo apt upgrade -y
sudo reboot

# 安装依赖以及 minikube
sudo apt install -y curl wget apt-transport-https
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube version

# 安装 kubectl
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
kubectl version -o yaml

# 启动 minikube
# minikube start --driver=docker(报错原因暂时定位是版本不兼容)
minikube start --image-mirror-country='cn' --kubernetes-version=v1.23.8 --driver=docker

最后一步启动我卡了很久,这里是有个坑的,原因暂时不明,但最终的解决方案是指定 k8s 版本号。

我发现云服务器配好代理之后,也不用指定版本号了.....,还是网络原因呗。

4、手写 dockerfile 创建镜像

前面就按照教程走,最后 docker build 的时候会出现问题,问题原因就是喜闻乐见的网络原因。

但本人就主打一个激进派,没有去找国内镜像,而是给服务器的网络请求配置了代理。所以下面就分享一下 clash for linux 以及一些注意事项。

首先就去 clash 这里找一个适合自己 linux 的版本。我用的是 clash-linux-amd64-v1.13.0.gz 对应着我的宿主机。

然后执行以下步骤:

mkdir clash
mv clash-linux-amd64-v1.13.0.gz clash
cd clash
tar -zxvf clash-linux-arm64-v1.13.0.gz
mv clash-linux-amd64-v1.13.0 clash-1.13.0
chmod +x clash-1.13.0

# 这里需要注意下后面的订阅链接必须加双引号
wget -O config.yaml "clash的订阅链接"
sudo wget -O Country.mmdb https://www.sub-speeder.com/client-download/Country.mmdb

# 启动 clash(这里可以开个 tmux 分个屏)
# -d . 指定当前目录为 配置目录,也就是 config.yaml 的位置
./clash-1.13.0 -d .

# 暴露端口(这个端口号需要和 config.yaml 里面的代理端口一致)
export http_proxy="http://127.0.0.1:7890"
export https_proxy="http://127.0.0.1:7890"

# 测试一下是否成功
curl http://google.com.hk/

不出意外的话,上面这些操作就可以访问成功了。但我在这里踩了一个坑,就是写 dockerfile 的时候需要去下载依赖,但是一直出现 io timeout,但是网络是没有问题的,最后发现 docker 不走系统代理,需要单独配置,所以下面给出配置步骤:

# 修改 docker 代理
vi /etc/systemd/system/multi-user.target.wants/docker.service
Environment=HTTP_PROXY=http://127.0.0.1:7890
Environment=HTTPS_PROXY=http://127.0.0.1:7890

# 重启 docker 服务
systemctl daemon-reload
systemctl restart docker

六、k8s-tutorials 笔记

1、container 实战

先体会一下容器的作用,先写一个简单的 http 服务端。

package main

import (
	"fmt"
	"io"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "hello Kubernetes")
}

func main() {
	http.HandleFunc("/", hello)
	err := http.ListenAndServe(":3000", nil)
	if err == nil {
		fmt.Println("绑定端口成功")
	}
}

此时需要在宿主机安装 go 环境,配置一些环境变量,才可以跑起来这个程序。然而通过容器化可以很方便的跑起来程序。

先编写一个 dockerfile:

FROM golang:1.16-buster AS builder
RUN mkdir /src
ADD . /src
WORKDIR /src

RUN go env -w GO111MODULE=auto
RUN go build -o main .

FROM gcr.io/distroless/base-debian10

WORKDIR /

COPY --from=builder /src/main main
EXPOSE 3000
ENTRYPOINT ["/main"]

在服务器通过 dockerfile 构建一个镜像:

docker build . -t aandazdz/hello_k8s:v1

其他人只需要拉取镜像并在有 docker 环境的机器上实例化为容器,就可以启动相应服务了。

docker run -p 3000:3000 --name hellok8s -d aandazdz/hello_k8s:v1

可以把 docker 镜像 push 到 dockerhub 上面,方便其他人拉取。

docker login
docker push aandazdz/hello_k8s:v1

2、pod 实践

pod 就是 k8s 中的最小单元,前面也已经做了一些简单的介绍。接下来我们通过实践来感受一下 container 和 pod 之间的区别。

先编写一个 nginx 的 yaml 文件:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: nginx

其中 kind 表示我们要创建的资源是 pod 类型,metadata.name 表示 pod 的名称(名称唯一),spec.containers 表示创建的容器名称,以及拉取的镜像名称(镜像默认来源于 dockerhub)

运行 kubectl apply -f nginx.yaml 来创建 pod,然后可以通过 kubectl get pods 来查看 pod 是否正常启动。

kubectl apply -f nginx.yaml        
# pod/nginx-pod created

kubectl get pods
# nginx-pod         1/1     Running   0           6s

kubectl port-forward nginx-pod 4000:80
# Forwarding from 127.0.0.1:4000 -> 80
# Forwarding from [::1]:4000 -> 80

container 和 pod 区别?

pod 就是管理一组进程的资源,而 container 本质就是进程,所以架构图如下:

image.png

3、deployment 实战

deployment 是帮助我们管理 pod 并使其获得多种能力的一种资源。

通过 deployment.yaml 来更深入的理解一下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellok8s-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellok8s
  template:
    metadata:
      labels:
        app: hellok8s
    spec:
      containers:
        - image: aandazdz/hello_k8s:v1
          name: hellok8s-container

其中以下这部分是定义 deployment 的:

  • kind 表示资源种类
  • metadata.name 表示 deployment 名称
  • spec.replicas 表示副本数量
  • spec.selector.matchLabels 表示创建的这个 deployment 管理哪些 pod,通过 labels 匹配

剩下 template 这部分是定义上面创建 deployment 所管理的 pod 的:

  • template.metadata.labels 表示 pod 的标签,上面 selector 就是查找这个匹配的。
  • template.spec.containers 表示 pod 里面 containers 的镜像和名称等。
  • pod 的 name 是随机生成的。

通过以下的命令应用 deployment 服务:

kubectl apply -f deployment.yaml

kubectl get deployments

我们可以通过修改 deployment.yaml 的 replicas 参数指定 pod 的副本数。 现在如果我们需要将 v1 版本的服务升级为 v2,就可以修改 deployment.yaml 的镜像版本从而使得所有服务以及副本得到升级。

但是现在出现了一个问题,我们如果直接全量更新的话,会导致我们所有的服务短暂不可用,所以 k8s 给我们提供了一个能力 滚动更新

先给出配置文件,再给出解释:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellok8s-deployment
spec:
  strategy:
     rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  replicas: 3
  selector:
    matchLabels:
      app: hellok8s
  template:
    metadata:
      labels:
        app: hellok8s
    spec:
      containers:
      - image: aandazdz/hello_k8s:v2
        name: hellok8s-container

重点解释一下 spec.strategy 参数:

  • Recreate 表示全量更新,也就是先把旧 pod 删除,再去更新新 pod
  • strategy.rollingUpdate 表示滚动更新,可以自定义规则更新
    • maxSurge 指定 pod 数量的最大峰值,因为新旧同时存在会超过设定的副本数量。
    • maxUnavailable 指定最大不可用 pod 数量,也就是最小存活数量。

4、存活探针

在生产环境中我们的服务可能因为死锁等原因导致服务虽然没有宕机但不能提供服务,但是这种情况是不易发现的,所以需要存活探针来主动监控所有容器,从而决定是否需要重启容器。

下面我们来模拟一下这个场景,先写一个服务不可用的服务端:

package main

import (
	"fmt"
	"io"
	"net/http"
	"time"
)

func hello(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "hello Kubernetes")
}

func main() {
	started := time.Now()
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		duration := time.Since(started)
		if duration.Seconds() > 15 {
			w.WriteHeader(500)
			w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
		} else {
			w.WriteHeader(200)
			w.Write([]byte("ok"))
		}
	})

	http.HandleFunc("/", hello)
	http.ListenAndServe(":3000", nil)
}

然后编写 dockerfile 并将镜像推送至 dockerhub,然后就可以编写 deployment.yaml 并在文件中指定探针。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellok8s-deployment
spec:
  strategy:
     rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  replicas: 3
  selector:
    matchLabels:
      app: hellok8s
  template:
    metadata:
      labels:
        app: hellok8s
    spec:
      containers:
        - image: guangzhengli/hellok8s:liveness
          name: hellok8s-container
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 3
            periodSeconds: 3

livenessProbe.httpGet 表示使用 GET 方法请求,以及指定路径和端口号。

periodSeconds 和 initialDelaySeconds 分别表示隔多少秒探测一次,以及第一次探测是多长时间后。

如果探测成功就认为容器健康,探测失败就杀死容器并重新启动。

5、就绪探针

就绪探测器可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。

6、service 实战

Service 就是把一组 pod 的网络服务抽象出来的一种资源。比如如果某个 pod 没有就绪,那么就不会将流量打向该 pod,可以为多个 pod 提供负载均衡的能力。

先把 main.go 更新一下,添加一个返回 host 的功能:

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func hello(w http.ResponseWriter, r *http.Request) {
	host, _ := os.Hostname()
	io.WriteString(w, fmt.Sprintf("[v3] Hello, Kubernetes!, From host: %s", host))
}

func main() {
	http.HandleFunc("/", hello)
	http.ListenAndServe(":3000", nil)
}

写一个 service-hellok8s-clusterip.yaml 的配置文件:

apiVersion: v1
kind: Service
metadata:
  name: service-hellok8s-clusterip
spec:
  type: ClusterIP
  selector:
    app: hellok8s
  ports:
  - port: 3000
    targetPort: 3000

具体参数可以去看之前对 service 概念的讲解。

执行 kubectl apply -f service-hellok8s-clusterip.yaml 命令应用配置文件。

执行 kubectl get endpoints 命令查看 service 维护的这一组 pod 的 ip 地址,只要服务中的 Pod 集合发生更改,Endpoints 就会被更新。

执行 kubectl get service 可以获得封装后的 ip 地址。

执行 curl ip:3000 就可以访问服务了。

注意一点:此时 service 服务还没有暴露到外网,只能在 k8s 内部访问,就比如执行 curl ip:3000 这个命令可以进入之前的 nginx 容器就可以访问到了,图示如下:

image.png

上面介绍的是 ClusterIP 类型的 service,明显的问题在于服务不能暴露到外网,而 NodePort 类型则可以将服务暴露到外网以供访问。

比如将多个 Node 的相同服务都映射到 service 的同个网络服务里面,这样外部流量打进来的时候就可以负载均衡到多个 Node 的 pod 上面了,具体如下图所示:

image.png

7、Ingress 实战

8、Namespace 实战

在实际的生产环境中,开发和测试的环境应该是隔离开互不干扰的,比如 dev 环境给开发使用,qa 环境给测试使用。所以 k8s 为我们提供了 namespace 这个资源,用于隔离环境。我们之前使用的 namespace 是 default,接下来我们自定义一个 namespace 进行开发。

apiVersion: v1
kind: Namespace
metadata:
  name: dev
  
---

apiVersion: v1
kind: Namespace
metadata:
  name: test

下面我们应用资源只需要在命令后面加上 -n namespace 就可以了。

9、ConfigMap 实战

K8S 使用 ConfigMap 来将你的配置数据和应用程序代码分开。

不同的 namespace 使用独立的 ConfigMap,也就是 namespace 之间的资源和配置都是相互隔离的。

这里 go 获取的环境变量 DB_URL 会在后面的配置文件中写入,最后访问这个后端代码,判断环境变量的返回值是否返回预期即可。

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func hello(w http.ResponseWriter, r *http.Request) {
	host, _ := os.Hostname()
	dbURL := os.Getenv("DB_URL")
	io.WriteString(w, fmt.Sprintf("[v4] Hello, Kubernetes! From host: %s, Get Database Connect URL: %s", host, dbURL))
}

func main() {
	http.HandleFunc("/", hello)
	http.ListenAndServe(":3000", nil)
}

这里就是构建对象并推送至远程仓库

docker build -t . aandazdz/hello_k8s:v4
docker push aandazdz/hello_k8s:v4

接下来我们创建两个 configMap 的 yaml 文件:

# hellok8s-config-dev.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hellok8s-config
data:
  DB_URL: "http://DB_ADDRESS_DEV"
# hellok8s-config-test.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hellok8s-config
data:
  DB_URL: "http://DB_ADDRESS_DEV"

然后把两个 yaml 文件分别应用于不同的 namespace

kubectl apply -f hellok8s-config-dev.yaml -n dev
kubectl apply -f hellok8s-config-test.yaml -n test

接下来我们去创建 pod 资源,对应 namespace 下面的 pod 资源会去读取对应的 configMap 配置文件。

# 创建 pod 资源的 yaml 文件
apiVersion: v1
kind: Pod
metadata:
  name: hellok8s-pod
spec:
  containers:
    - name: hellok8s-container
      image: aandazdz/hello_k8s:v4
      env:
        - name: DB_URL
          valueFrom:
            configMapKeyRef:
              name: hellok8s-config
              key: DB_URL

前面都已经比较熟悉了,下面我重点介绍一下 env 的配置:

env.name 表示的是将 configmap 中的值写进环境变量(之前 go 代码需要读取的环境变量就是通过这个配置写入的),valueFrom 表示从哪里读取配置,configMapKeyRef.name 表示从该名称的配置文件中读取,configMapKeyRef.key 表示读取该配置文件中的这个 key 对应的 value 值。

现在在 k8s 上实操一下:

# 创建 pod 资源
kubectl apply -f hellok8s.yaml -n dev
kubectl apply -f hellok8s.yaml -n test

# 查看一下是否创建成功(不指定 namespace 默认是 default)
kubectl get pods -n dev
kubectl get pods -n test

# 映射端口并用 curl 去访问对应 URL

kubectl port-forward hellok8s-pod 3000:3000 -n test
curl http://localhost:3000
# [v4] Hello, Kubernetes! From host: hellok8s-pod, Get Database Connect URL: http://DB_ADDRESS_TEST

kubectl port-forward hellok8s-pod 3000:3000 -n dev
curl http://localhost:3000
# [v4] Hello, Kubernetes! From host: hellok8s-pod, Get Database Connect URL: http://DB_ADDRESS_DEV

从上面实践中可以看出,不同 namespace 下面的 pod 资源,会读取对应的 namespace 下面的 configmap,这就是 configmap 提供给我们资源解耦和隔离。

10、Secret 实战

Secret 就是加密版本的 ConfigMap,当我们配置一些机密信息的时候,使用 Secert 的安全性是更好的。

echo "db_password" | base64
# ZGJfcGFzc3dvcmQK

echo "ZGJfcGFzc3dvcmQK" | base64 -d
# db_password
# hellok8s-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: hellok8s-secret
data:
  DB_PASSWORD: "ZGJfcGFzc3dvcmQK"
# hellok8s.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hellok8s-pod
spec:
  containers:
    - name: hellok8s-container
      image: aandazdz/hello_k8s:v5
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: hellok8s-secret
              key: DB_PASSWORD

之后的操作就和 ConfigMap 一致了,这里就不做过多演示了。

11、Job 实战

之前我们学习了很多 k8s 所提供的资源,但这些资源还有一个类型的问题不能解决,那就是一次性的任务和定时任务(pod 都是持续运行的),所以 k8s 提供给我们了 Job 和 CronJob 来解决这一类问题。

1)Job

Job 会根据配置创建指定数量个 pod,当 pod 成功执行的数量达到配置数时,job 就会结束。

其中删除 Job 的操作会清除所有 Pod。 挂起 Job 的操作会停止所有活跃 Pod,直到 Job 被再次恢复执行。

apiVersion: batch/v1
kind: Job
metadata:
  name: hello-job
spec:
  parallelism: 3
  completions: 5
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: echo
          image: busybox
          command: [for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done]

completions 表示包含的 pod 总数,parallelism 表示可以并发执行的 pod 总数。

2)CronJob

CronJob 可以理解为定时任务,创建基于 Cron 时间调度的 Jobs

12、Helm

经过前面的学习,发现搭建一套 k8s 服务是需要部署很多资源的,但我们手动部署显然是反直觉的,所以就有了这个工具 Helm,帮助我们快速搭建一套 k8s 服务。

Helm 是一个 kubernetes 的包管理工具,可以管理 yaml 文件并部署到 kubernetes 上面。

三个重要概念:

  • helm:一个命令行客户端工具,主要用于 chart 的创建、打包、发布和管理
  • chart:应用描述,一系列用于描述 k8s 资源相关文件的集合
  • release:基于 chart 的部署实体,一个 chart 被 helm 运行后会生成对应的 release,在 k8s 中创建出运行的资源对象

先在服务器上安装一个 Helm,我的是 Ubuntu22.04:

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod +x get_helm.sh
./get_helm.sh

1)创建 chart

先用 helm 提供的命令,创建一个初始化的 chart

helm create hello-helm

.
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml