Kubernetes Basics

Kubernetes Basics

支持 HackTricks

本页面的原作者是 Jorge (阅读他的原始帖子 这里

架构与基础

Kubernetes 的作用是什么?

  • 允许在容器引擎中运行容器。

  • 调度使容器任务高效。

  • 保持容器存活。

  • 允许容器之间的通信。

  • 允许部署技术。

  • 处理大量信息。

架构

  • 节点:带有 pod 或多个 pod 的操作系统。

  • Pod:围绕一个或多个容器的包装。一个 pod 应该只包含一个应用程序(因此通常,一个 pod 只运行 1 个容器)。pod 是 Kubernetes 抽象运行容器技术的方式。

  • 服务:每个 pod 从节点的内部范围中有 1 个内部 IP 地址。但是,它也可以通过服务暴露。服务也有一个 IP 地址,其目标是维护 pod 之间的通信,因此如果一个 pod 死亡,新的替代品(具有不同的内部 IP)将可访问并暴露在服务的相同 IP 上。可以配置为内部或外部。服务还充当负载均衡器,当 2 个 pod 连接到同一服务时。 当创建一个 服务 时,可以通过运行 kubectl get endpoints 找到每个服务的端点。

  • Kubelet:主要节点代理。建立节点与 kubectl 之间通信的组件,只能运行 pods(通过 API 服务器)。Kubelet 不管理未由 Kubernetes 创建的容器。

  • Kube-proxy:负责 apiserver 和节点之间通信(服务)的服务。基础是节点的 IPtables。经验丰富的用户可以安装来自其他供应商的其他 kube-proxies。

  • Sidecar 容器:Sidecar 容器是应该与 pod 中的主容器一起运行的容器。此 sidecar 模式扩展并增强当前容器的功能,而无需更改它们。如今,我们知道我们使用容器技术来包装应用程序在任何地方运行所需的所有依赖项。一个容器只做一件事,并且做得很好。

  • 主进程:

  • Api 服务器: 是用户和 pod 与主进程通信的方式。仅允许经过身份验证的请求。

  • 调度器:调度是指确保 Pods 与 Nodes 匹配,以便 Kubelet 可以运行它们。它具有足够的智能来决定哪个节点有更多可用资源并将新 pod 分配给它。请注意,调度器不会启动新 pod,它只是与运行在节点内部的 Kubelet 进程通信,该进程将启动新 pod。

  • Kube Controller 管理器:检查资源,如副本集或部署,以检查例如,是否正在运行正确数量的 pods 或节点。如果缺少 pod,它将与调度器通信以启动新的 pod。它控制 API 的复制、令牌和帐户服务。

  • etcd:数据存储,持久、一致和分布式。是 Kubernetes 的数据库和键值存储,保存集群的完整状态(每个更改在此处记录)。调度器或控制器管理器等组件依赖于此数据以了解发生了哪些更改(节点的可用资源、正在运行的 pod 数量...)。

  • Cloud Controller 管理器:是流控制和应用程序的特定控制器,即:如果您在 AWS 或 OpenStack 中有集群。

请注意,由于可能有多个节点(运行多个 pod),因此也可能有多个主进程,它们对 Api 服务器的访问是负载均衡的,并且它们的 etcd 是同步的。

卷:

当 pod 创建的数据在 pod 消失时不应丢失时,应存储在物理卷中。Kubernetes 允许将卷附加到 pod 以持久化数据。卷可以在本地机器上或在 远程存储 中。如果您在不同的物理节点上运行 pods,则应使用远程存储,以便所有 pods 都可以访问它。

其他配置:

  • ConfigMap:您可以配置 URLs 以访问服务。pod 将从这里获取数据以了解如何与其余服务(pods)通信。请注意,这不是保存凭据的推荐位置!

  • Secret:这是 存储机密数据 的地方,如密码、API 密钥...以 B64 编码。pod 将能够访问这些数据以使用所需的凭据。

  • Deployments:这是指示 Kubernetes 运行的组件的地方。用户通常不会直接与 pods 一起工作,pods 在 ReplicaSets(相同 pods 的数量复制)中被抽象,后者通过部署运行。请注意,部署适用于 无状态 应用程序。部署的最小配置是名称和要运行的镜像。

  • StatefulSet:此组件专门用于需要 访问相同存储 的应用程序,如 数据库

  • Ingress:这是用于 通过 URL 公开应用程序的配置。请注意,这也可以通过外部服务完成,但这是公开应用程序的正确方式。

  • 如果您实现了 Ingress,您将需要创建 Ingress 控制器。Ingress 控制器是一个 pod,将是接收请求的端点,并检查并将其负载均衡到服务。Ingress 控制器将 根据配置的 ingress 规则发送请求。请注意,ingress 规则可以指向不同的路径或甚至子域到不同的内部 Kubernetes 服务。

  • 更好的安全实践是使用云负载均衡器或代理服务器作为入口点,以避免 Kubernetes 集群的任何部分暴露。

  • 当收到不匹配任何 ingress 规则的请求时,ingress 控制器将其定向到 "默认后端"。您可以 describe ingress 控制器以获取此参数的地址。

  • minikube addons enable ingress

PKI 基础设施 - 证书颁发机构 CA:

  • CA 是集群中所有证书的受信任根。

  • 允许组件相互验证。

  • 所有集群证书均由 CA 签名。

  • etcd 有自己的证书。

  • 类型:

  • apiserver 证书。

  • kubelet 证书。

  • 调度器证书。

基本操作

Minikube

Minikube 可用于在 Kubernetes 上执行一些 快速测试,而无需部署整个 Kubernetes 环境。它将在一台机器上运行 主进程和节点进程。Minikube 将使用 virtualbox 来运行节点。请参见 这里了解如何安装

$ minikube start
😄  minikube v1.19.0 on Ubuntu 20.04
✨  Automatically selected the virtualbox driver. Other choices: none, ssh
💿  Downloading VM boot image ...
> minikube-v1.19.0.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
> minikube-v1.19.0.iso: 244.49 MiB / 244.49 MiB  100.00% 1.78 MiB p/s 2m17.
👍  Starting control plane node minikube in cluster minikube
💾  Downloading Kubernetes v1.20.2 preload ...
> preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB  100.00% 2.59 MiB
🔥  Creating virtualbox VM (CPUs=2, Memory=3900MB, Disk=20000MB) ...
🐳  Preparing Kubernetes v1.20.2 on Docker 20.10.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎  Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by defaul

$ minikube status
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

---- ONCE YOU HAVE A K8 SERVICE RUNNING WITH AN EXTERNAL SERVICE -----
$ minikube service mongo-express-service
(This will open your browser to access the service exposed port)

$ minikube delete
🔥  Deleting "minikube" in virtualbox ...
💀  Removed all traces of the "minikube" cluster

Kubectl 基础

Kubectl 是用于 kubernetes 集群的命令行工具。它与主进程的 Api 服务器通信,以在 kubernetes 中执行操作或请求数据。

kubectl version #Get client and server version
kubectl get pod
kubectl get services
kubectl get deployment
kubectl get replicaset
kubectl get secret
kubectl get all
kubectl get ingress
kubectl get endpoints

#kubectl create deployment <deployment-name> --image=<docker image>
kubectl create deployment nginx-deployment --image=nginx
#Access the configuration of the deployment and modify it
#kubectl edit deployment <deployment-name>
kubectl edit deployment nginx-deployment
#Get the logs of the pod for debbugging (the output of the docker container running)
#kubectl logs <replicaset-id/pod-id>
kubectl logs nginx-deployment-84cd76b964
#kubectl describe pod <pod-id>
kubectl describe pod mongo-depl-5fd6b7d4b4-kkt9q
#kubectl exec -it <pod-id> -- bash
kubectl exec -it mongo-depl-5fd6b7d4b4-kkt9q -- bash
#kubectl describe service <service-name>
kubectl describe service mongodb-service
#kubectl delete deployment <deployment-name>
kubectl delete deployment mongo-depl
#Deploy from config file
kubectl apply -f deployment.yml

Minikube Dashboard

仪表板使您更容易查看minikube正在运行的内容,您可以在以下位置找到访问它的URL:

minikube dashboard --url


🔌  Enabling dashboard ...
▪ Using image kubernetesui/dashboard:v2.3.1
▪ Using image kubernetesui/metrics-scraper:v1.0.7
🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
http://127.0.0.1:50034/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

YAML 配置文件示例

每个配置文件有 3 个部分:metadataspecification(需要启动的内容)、status(期望状态)。 在部署配置文件的规范中,您可以找到定义了新配置结构的模板,定义了要运行的镜像:

在同一配置文件中声明的 Deployment + Service 示例(来自 这里)

由于服务通常与一个部署相关,因此可以在同一配置文件中声明两者(此配置中声明的服务仅可在内部访问):

apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017

外部服务配置示例

此服务将可从外部访问(检查 nodePorttype: LoadBlancer 属性):

---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000

这对于测试很有用,但在生产环境中,您应该仅拥有内部服务和一个 Ingress 来暴露应用程序。

Ingress 配置文件示例

这将会在 http://dashboard.com 上暴露应用程序。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: dashboard.com
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80

示例的秘密配置文件

注意密码是以B64编码的(这并不安全!)

apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU=
mongo-root-password: cGFzc3dvcmQ=

ConfigMap 示例

一个 ConfigMap 是提供给 pods 的配置,以便它们知道如何定位和访问其他服务。在这种情况下,每个 pod 将知道名称 mongodb-service 是它们可以通信的 pod 的地址(该 pod 将执行 mongodb):

apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service

然后,在deployment config中,可以通过以下方式指定此地址,以便将其加载到pod的环境中:

[...]
spec:
[...]
template:
[...]
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
[...]

示例卷配置

您可以在 https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumes 找到不同的存储配置 yaml 文件示例。 请注意,卷不在命名空间内

命名空间

Kubernetes 支持 多个虚拟集群,这些集群由同一个物理集群支持。这些虚拟集群称为 命名空间。它们旨在用于用户分布在多个团队或项目中的环境。对于用户数量在几到十几的集群,您不需要创建或考虑命名空间。您只有在需要更好地控制和组织在 Kubernetes 中部署的应用程序的每个部分时,才应该开始使用命名空间。

命名空间为名称提供了作用域。资源的名称在一个命名空间内需要是唯一的,但在不同命名空间之间不需要。命名空间不能相互嵌套,并且 每个 Kubernetes 资源 只能 一个 命名空间 中。

如果您使用 minikube,默认情况下有 4 个命名空间:

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-public       Active   1d
kube-system       Active   1d
  • kube-system: 这不是供用户使用的,您不应该触碰它。它是为主节点和 kubectl 进程准备的。

  • kube-public: 公开可访问的数据。包含一个 configmap,其中包含集群信息。

  • kube-node-lease: 确定节点的可用性。

  • default: 用户将用于创建资源的命名空间。

#Create namespace
kubectl create namespace my-namespace

请注意,大多数Kubernetes资源(例如pods、services、replication controllers等)位于某些命名空间中。然而,其他资源如命名空间资源和低级资源,例如nodes和persistentVolumes则不在命名空间中。要查看哪些Kubernetes资源在命名空间中,哪些不在命名空间中:

kubectl api-resources --namespaced=true #In a namespace
kubectl api-resources --namespaced=false #Not in a namespace

您可以在该上下文中为所有后续的 kubectl 命令保存命名空间。

kubectl config set-context --current --namespace=<insert-namespace-name-here>

Helm

Helm 是 Kubernetes 的 包管理器。它允许将 YAML 文件打包并分发到公共和私有仓库。这些包称为 Helm Charts

helm search <keyword>

Helm 也是一个模板引擎,允许生成带有变量的配置文件:

Kubernetes 秘密

Secret 是一个 包含敏感数据 的对象,例如密码、令牌或密钥。这些信息可能会被放在 Pod 规范或镜像中。用户可以创建 Secrets,系统也会创建 Secrets。Secret 对象的名称必须是有效的 DNS 子域名。在这里阅读 官方文档

Secrets 可能是以下内容:

  • API、SSH 密钥。

  • OAuth 令牌。

  • 凭据、密码(明文或 b64 + 加密)。

  • 信息或注释。

  • 数据库连接代码、字符串……。

Kubernetes 中有不同类型的秘密

Opaque 类型是默认类型,用户定义的典型键值对。

Secrets 的工作原理:

以下配置文件定义了一个名为 mysecretsecret,包含 2 个键值对 username: YWRtaW4=password: MWYyZDFlMmU2N2Rm。它还定义了一个名为 secretpodpod,该 pod 将在 环境变量 SECRET_USERNAME __ 和 __ SECRET_PASSWOR 中暴露 mysecret 中定义的 usernamepassword。它还将 挂载 mysecret 中的 username secret 到路径 /etc/foo/my-group/my-username,权限为 0640

secretpod.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
---
apiVersion: v1
kind: Pod
metadata:
name: secretpod
spec:
containers:
- name: secretpod
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
volumeMounts:
- name: foo
mountPath: "/etc/foo"
restartPolicy: Never
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 0640
kubectl apply -f <secretpod.yaml>
kubectl get pods #Wait until the pod secretpod is running
kubectl exec -it  secretpod -- bash
env | grep SECRET && cat /etc/foo/my-group/my-username && echo

Secrets in etcd

etcd 是一个一致且高度可用的 键值存储,用于作为 Kubernetes 所有集群数据的后端存储。让我们访问存储在 etcd 中的秘密:

cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd

您将看到证书、密钥和 URL 在文件系统中的位置。一旦您获取到这些,您将能够连接到 etcd。

#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] health

ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] health

一旦您建立了通信,您将能够获取机密:

#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] get <path/to/secret>

ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] get /registry/secrets/default/secret_02

为ETCD添加加密

默认情况下,所有的秘密都以明文存储在etcd中,除非您应用加密层。以下示例基于 https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

encryption.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: cjjPMcWpTPKhAdieVtd+KhG4NN+N6e3NmBPMXJvbfrY= #Any random key
- identity: {}

之后,您需要在 kube-apiserver 上设置 --encryption-provider-config 标志,以指向创建的配置文件的位置。您可以修改 /etc/kubernetes/manifest/kube-apiserver.yaml 并添加以下行:

containers:
- command:
- kube-apiserver
- --encriyption-provider-config=/etc/kubernetes/etcd/<configFile.yaml>

在 volumeMounts 中向下滚动:

- mountPath: /etc/kubernetes/etcd
name: etcd
readOnly: true

向下滚动到 volumeMounts 中的 hostPath:

- hostPath:
path: /etc/kubernetes/etcd
type: DirectoryOrCreate
name: etcd

验证数据是否被加密

数据在写入etcd时被加密。在重新启动您的kube-apiserver后,任何新创建或更新的秘密在存储时应被加密。要检查,可以使用etcdctl命令行程序检索您的秘密内容。

  1. default命名空间中创建一个名为secret1的新秘密:

kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
  1. 使用etcdctl命令行,从etcd中读取该秘密:

ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C

其中[...]必须是连接到etcd服务器的附加参数。 3. 验证存储的秘密以k8s:enc:aescbc:v1:为前缀,这表明aescbc提供者已加密结果数据。 4. 验证通过API检索时秘密被正确解密:

kubectl describe secret secret1 -n default

应匹配mykey: bXlkYXRh,mydata被编码,查看解码秘密以完全解码秘密。

由于秘密在写入时被加密,对秘密进行更新将加密该内容:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

最终提示:

参考文献

支持 HackTricks

Last updated