Kubernetes Basics

Kubernetes 基础

支持 HackTricks

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

架构与基础知识

Kubernetes 的作用是什么?

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

  • 调度允许容器高效地执行任务。

  • 保持容器运行。

  • 允许容器间通信。

  • 允许部署技术。

  • 处理大量信息。

架构

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

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

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

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

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

  • Sidecar 容器:Sidecar 容器是应该与 pod 中的主要容器一起运行的容器。这种 sidecar 模式扩展并增强了当前容器的功能,而不会改变它们。现在,我们知道我们使用容器技术来封装应用程序在任何地方运行所需的所有依赖项。一个容器只做一件事,并且做得非常好。

  • 主进程

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

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

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

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

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

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

卷:

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

其他配置:

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

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

  • 部署:这是指示由 Kubernetes 运行的组件的位置。用户通常不会直接使用 pod,pod 在 ReplicaSets(复制的相同 pod 数量)中抽象,通过部署运行。请注意,部署适用于无状态应用程序。部署的最小配置是名称和要运行的镜像。

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

  • Ingress:这是用于使用 URL 公开应用程序的配置。请注意,也可以使用外部服务来执行此操作,但这是公开应用程序的正确方式。

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

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

  • 当接收到不匹配任何 Ingress 规则的请求时,Ingress 控制器将将其定向到“默认后端”。您可以使用 describe 命令获取此参数的地址。

  • minikube addons enable ingress

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

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

  • 允许组件相互验证。

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

  • ETCd有自己的证书。

  • 类型:

    • apiserver证书。

    • kubelet证书。

    • scheduler证书。

基本操作

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 仪表盘

该仪表盘允许您更轻松地查看 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个部分:metadata(元数据)、specification(需要启动的内容)、status(期望状态)。 在部署配置文件的规范中,您可以找到使用新配置结构定义要运行的镜像的模板:

部署+服务在同一配置文件中声明的示例(来自 这里

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

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: LoadBalancer 属性):

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

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

然后,在部署配置中,可以通过以下方式指定该地址,以便在 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 和 persistenVolumes)不属于命名空间。要查看哪些 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>

Kubernetes secrets

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

Secrets可能是以下内容:

  • API、SSH密钥。

  • OAuth令牌。

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

  • 信息或注释。

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

Kubernetes中有不同类型的secrets

内置类型用途

Opaque

任意用户定义的数据(默认)

kubernetes.io/service-account-token

服务账户令牌

kubernetes.io/dockercfg

序列化~/.dockercfg文件

kubernetes.io/dockerconfigjson

序列化~/.docker/config.json文件

kubernetes.io/basic-auth

基本身份验证凭据

kubernetes.io/ssh-auth

SSH身份验证凭据

kubernetes.io/tls

TLS客户端或服务器数据

bootstrap.kubernetes.io/token

引导令牌数据

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

Secrets的工作原理:

以下配置文件定义了一个名为mysecretsecret,具有2个键值对username: YWRtaW4=password: MWYyZDFlMmU2N2Rm。它还定义了一个名为secretpodpod,该pod将在环境变量SECRET_USERNAMESECRET_PASSWORD中公开mysecret中定义的usernamepassword。它还将在路径/etc/foo/my-group/my-username中以0640权限挂载mysecret中的username secret。

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

在 etcd 中的 Secrets

etcd 是一个一致且高可用的 键-值存储,被用作 Kubernetes 的后备存储,用于存储所有集群数据。让我们访问存储在 etcd 中的 secrets:

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

您将看到证书、密钥和网址的位置在文件系统中。一旦获取这些信息,您就能够连接到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 后,任何新创建或更新的 secret 在存储时应该是加密的。您可以使用 etcdctl 命令行程序检索您的 secret 的内容来进行检查。

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

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

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

其中 [...] 必须是连接到 etcd 服务器的附加参数。 3. 验证存储的 secret 是否以 k8s:enc:aescbc:v1: 为前缀,这表示 aescbc 提供程序已加密生成的数据。 4. 验证通过 API 检索时是否正确解密了该 secret:

kubectl describe secret secret1 -n default

应该匹配 mykey: bXlkYXRh,mydata 已编码,查看 解码 secret 来完全解码该 secret。

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

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

最后建议:

参考资料

支持 HackTricks

Last updated