1 - 以独立模式运行 kubelet
本教程将向你展示如何运行一个独立的 kubelet 实例。
你可能会有不同的动机来运行一个独立的 kubelet。 本教程旨在向你介绍 Kubernetes,即使你对此并没有太多经验也没有关系。 你可以跟随本教程学习节点设置、基本(静态)Pod 以及 Kubernetes 如何管理容器。
你学习完本教程后,就可以尝试使用带一个控制平面的集群来管理 Pod、节点和其他类别的对象。例如,你好,Minikube。
你还可以以独立模式运行 kubelet 来满足生产场景要求,例如为高可用、弹性部署的集群运行控制平面。 本教程不涵盖运行弹性控制平面所需的细节。
教程目标
- 在 Linux 系统上安装
cri-o
和kubelet
,并将其作为systemd
服务运行。 - 启动一个运行
nginx
的 Pod,监听针对此 Pod 的 IP 地址的 TCP 80 端口的请求。 - 学习此方案中不同组件之间如何交互。
注意:
本教程中所使用的 kubelet 配置在设计上是不安全的,不得用于生产环境中。
准备开始
- 对使用
systemd
和iptables
(或使用iptables
仿真的 nftables)的 Linux 系统具有管理员(root
)访问权限。 - 有权限访问互联网以下载本教程所需的组件,例如:
- 实现 Kubernetes CRI 的容器运行时。
- 网络插件(通常称为 容器网络接口(CNI))。
- 必需的 CLI 工具:
curl
、tar
、jq
。
准备好系统
配置内存交换
默认情况下,如果在节点上检测到内存交换,kubelet 将启动失败。 这意味着内存交换应该被禁用或被 kubelet 容忍。
说明:
如果你配置 kubelet 为容忍内存交换,则 kubelet 仍会配置 Pod(以及这些 Pod 中的容器)不使用交换空间。 要了解 Pod 实际上可以如何使用可用的交换,你可以进一步阅读 Linux 节点上交换内存管理。
如果你启用了交换内存,则禁用它或在 kubelet 配置文件中添加 failSwapOn: false
。
要检查交换内存是否被启用:
sudo swapon --show
如果此命令没有输出,则交换内存已被禁用。
临时禁用交换内存:
sudo swapoff -a
要使此变更持续到重启之后:
确保在 /etc/fstab
或 systemd.swap
中禁用交换内存,具体取决于它在你的系统上是如何配置的。
启用 IPv4 数据包转发
检查 IPv4 数据包转发是否被启用:
cat /proc/sys/net/ipv4/ip_forward
如果输出为 1
,则 IPv4 数据包转发已被启用。如果输出为 0
,按照以下步骤操作。
要启用 IPv4 数据包转发,创建一个配置文件,将 net.ipv4.ip_forward
参数设置为 1
:
sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.ipv4.ip_forward = 1
EOF
将变更应用到系统:
sudo sysctl --system
输出类似于:
...
* Applying /etc/sysctl.d/k8s.conf ...
net.ipv4.ip_forward = 1
* Applying /etc/sysctl.conf ...
下载、安装和配置组件
安装容器运行时
下载所需软件包的最新可用版本(推荐)。
本教程建议安装 CRI-O 容器运行时(外部链接)。
根据你安装的特定 Linux 发行版,有几种安装容器运行时的方式。
尽管 CRI-O 推荐使用 deb
或 rpm
包,但本教程使用
CRI-O Packaging 项目中的静态二进制包脚本,
以简化整个安装过程,并保持与 Linux 发行版无关。
此脚本安装并配置更多必需的软件,例如容器联网所用的 cni-plugins
以及运行容器所用的 crun
和 runc
。
此脚本将自动检测系统的处理器架构(amd64
或 arm64
),并选择和安装最新版本的软件包。
设置 CRI-O
查阅发布版本页面(外部链接)。
下载静态二进制包脚本:
curl https://raw.githubusercontent.com/cri-o/packaging/main/get > crio-install
运行安装器脚本:
sudo bash crio-install
启用并启动 crio
服务:
sudo systemctl daemon-reload
sudo systemctl enable --now crio.service
快速测试:
sudo systemctl is-active crio.service
输出类似于:
active
详细的服务检查:
sudo journalctl -f -u crio.service
安装网络插件
cri-o
安装器安装并配置 cni-plugins
包。你可以通过运行以下命令来验证安装包:
/opt/cni/bin/bridge --version
输出类似于:
CNI bridge plugin v1.5.1
CNI protocol versions supported: 0.1.0, 0.2.0, 0.3.0, 0.3.1, 0.4.0, 1.0.0
检查默认配置:
cat /etc/cni/net.d/11-crio-ipv4-bridge.conflist
输出类似于:
{
"cniVersion": "1.0.0",
"name": "crio",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"ranges": [
[{ "subnet": "10.85.0.0/16" }]
]
}
}
]
}
说明:
确保默认的 subnet
范围(10.85.0.0/16
)不会与你已经在使用的任一网络地址重叠。
如果出现重叠,你可以编辑此文件并进行相应的更改。更改后重启服务。
下载并设置 kubelet
下载 kubelet 的最新稳定版本。
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubelet"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubelet"
配置:
sudo mkdir -p /etc/kubernetes/manifests
sudo tee /etc/kubernetes/kubelet.yaml <<EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
webhook:
enabled: false # 请勿在生产集群中使用!
authorization:
mode: AlwaysAllow # 请勿在生产集群中使用!
enableServer: false
logging:
format: text
address: 127.0.0.1 # 限制对 localhost 的访问
readOnlyPort: 10255 # 请勿在生产集群中使用!
staticPodPath: /etc/kubernetes/manifests
containerRuntimeEndpoint: unix:///var/run/crio/crio.sock
EOF
说明:
由于你搭建的不是一个生产集群,所以你可以使用明文
HTTP(readOnlyPort: 10255
)对 kubelet API 进行不做身份认证的查询。
为了顺利完成本次教学,身份认证 Webhook 被禁用,鉴权模式被设置为 AlwaysAllow
。
你可以进一步了解鉴权模式和
Webhook 身份认证,
以正确地配置 kubelet 在你的环境中以独立模式运行。
参阅端口和协议以了解 Kubernetes 组件使用的端口。
安装:
chmod +x kubelet
sudo cp kubelet /usr/bin/
创建 systemd
服务单元文件:
sudo tee /etc/systemd/system/kubelet.service <<EOF
[Unit]
Description=Kubelet
[Service]
ExecStart=/usr/bin/kubelet \
--config=/etc/kubernetes/kubelet.yaml
Restart=always
[Install]
WantedBy=multi-user.target
EOF
服务配置文件中故意省略了命令行参数 --kubeconfig
。此参数设置
kubeconfig
文件的路径,指定如何连接到 API 服务器,以启用 API 服务器模式。省略此参数将启用独立模式。
启用并启动 kubelet
服务:
sudo systemctl daemon-reload
sudo systemctl enable --now kubelet.service
快速测试:
sudo systemctl is-active kubelet.service
输出类似于:
active
详细的服务检查:
sudo journalctl -u kubelet.service
检查 kubelet 的 API /healthz
端点:
curl http://localhost:10255/healthz?verbose
输出类似于:
[+]ping ok
[+]log ok
[+]syncloop ok
healthz check passed
查询 kubelet 的 API /pods
端点:
curl http://localhost:10255/pods | jq '.'
输出类似于:
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {},
"items": null
}
在 kubelet 中运行 Pod
在独立模式下,你可以使用 Pod 清单运行 Pod。这些清单可以放在本地文件系统上,或通过 HTTP 从配置源获取。
为 Pod 创建一个清单:
cat <<EOF > static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
EOF
将 static-web.yaml
清单文件复制到 /etc/kubernetes/manifests
目录。
sudo cp static-web.yaml /etc/kubernetes/manifests/
查找 kubelet 和 Pod 的信息
Pod 网络插件为每个 Pod 创建一个网络桥(cni0
)和一对 veth
接口
(这对接口的其中一个接口在新创建的 Pod 内,另一个接口在主机层面)。
查询 kubelet 的 API 端点 http://localhost:10255/pods
:
curl http://localhost:10255/pods | jq '.'
要获取 static-web
Pod 的 IP 地址:
curl http://localhost:10255/pods | jq '.items[].status.podIP'
输出类似于:
"10.85.0.4"
连接到 nginx
服务器 Pod,地址为 http://<IP>:<Port>
(端口 80 是默认端口),在本例中为:
curl http://10.85.0.4
输出类似于:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
了解更多细节
如果你需要排查在学习本教程时遇到的问题,你可以在以下目录中查找监控和故障排查资料:
/var/lib/cni
/var/lib/containers
/var/lib/kubelet
/var/log/containers
/var/log/pods
清理
kubelet
sudo systemctl disable --now kubelet.service
sudo systemctl daemon-reload
sudo rm /etc/systemd/system/kubelet.service
sudo rm /usr/bin/kubelet
sudo rm -rf /etc/kubernetes
sudo rm -rf /var/lib/kubelet
sudo rm -rf /var/log/containers
sudo rm -rf /var/log/pods
容器运行时
sudo systemctl disable --now crio.service
sudo systemctl daemon-reload
sudo rm -rf /usr/local/bin
sudo rm -rf /usr/local/lib
sudo rm -rf /usr/local/share
sudo rm -rf /usr/libexec/crio
sudo rm -rf /etc/crio
sudo rm -rf /etc/containers
网络插件
sudo rm -rf /opt/cni
sudo rm -rf /etc/cni
sudo rm -rf /var/lib/cni
结论
本页涵盖了以独立模式部署 kubelet 的各个基本方面。你现在可以部署 Pod 并测试更多功能。
请注意,在独立模式下,kubelet 不支持从控制平面获取 Pod 配置(因为没有控制平面连接)。
你还不能使用 ConfigMap 或 Secret 来配置静态 Pod 中的容器。
接下来
- 跟随你好,Minikube 学习如何在有控制平面的情况下运行 Kubernetes。minikube 工具帮助你在自己的计算机上搭建一个练习集群。
- 进一步了解网络插件
- 进一步了解容器运行时
- 进一步了解 kubelet
- 进一步了解静态 Pod
2 - 名字空间演练
Kubernetes 名字空间有助于不同的项目、团队或客户去共享 Kubernetes 集群。
名字空间通过以下方式实现这点:
- 为名字设置作用域.
- 为集群中的部分资源关联鉴权和策略的机制。
使用多个名字空间是可选的。
此示例演示了如何使用 Kubernetes 名字空间细分集群。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入 kubectl version
.
环境准备
此示例作如下假设:
- 你已拥有一个配置好的 Kubernetes 集群。
- 你已对 Kubernetes 的 Pod、 服务 和 Deployment 有基本理解。
理解默认名字空间
默认情况下,Kubernetes 集群会在配置集群时实例化一个默认名字空间,用以存放集群所使用的默认 Pod、Service 和 Deployment 集合。
假设你有一个新的集群,你可以通过执行以下操作来检查可用的名字空间:
kubectl get namespaces
NAME STATUS AGE
default Active 13m
创建新的名字空间
在本练习中,我们将创建两个额外的 Kubernetes 名字空间来保存我们的内容。
我们假设一个场景,某组织正在使用共享的 Kubernetes 集群来支持开发和生产:
开发团队希望在集群中维护一个空间,以便他们可以查看用于构建和运行其应用程序的 Pod、Service 和 Deployment 列表。在这个空间里,Kubernetes 资源被自由地加入或移除, 对谁能够或不能修改资源的限制被放宽,以实现敏捷开发。
运维团队希望在集群中维护一个空间,以便他们可以强制实施一些严格的规程, 对谁可以或谁不可以操作运行生产站点的 Pod、Service 和 Deployment 集合进行控制。
该组织可以遵循的一种模式是将 Kubernetes 集群划分为两个名字空间:development
和 production
。
让我们创建两个新的名字空间来保存我们的工作。
文件 namespace-dev.yaml
描述了 development
名字空间:
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
name: development
使用 kubectl 创建 development
名字空间。
kubectl create -f https://k8s.io/examples/admin/namespace-dev.yaml
将下列的内容保存到文件 namespace-prod.yaml
中,
这些内容是对 production
名字空间的描述:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
name: production
让我们使用 kubectl 创建 production
名字空间。
kubectl create -f https://k8s.io/examples/admin/namespace-prod.yaml
为了确保一切正常,我们列出集群中的所有名字空间。
kubectl get namespaces --show-labels
NAME STATUS AGE LABELS
default Active 32m <none>
development Active 29s name=development
production Active 23s name=production
在每个名字空间中创建 Pod
Kubernetes 名字空间为集群中的 Pod、Service 和 Deployment 提供了作用域。
与一个名字空间交互的用户不会看到另一个名字空间中的内容。
为了演示这一点,让我们在 development
名字空间中启动一个简单的 Deployment 和 Pod。
我们首先检查一下当前的上下文:
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://130.211.122.180
name: lithe-cocoa-92103_kubernetes
contexts:
- context:
cluster: lithe-cocoa-92103_kubernetes
user: lithe-cocoa-92103_kubernetes
name: lithe-cocoa-92103_kubernetes
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
user:
password: h5M0FtUUIflBSdI7
username: admin
kubectl config current-context
lithe-cocoa-92103_kubernetes
下一步是为 kubectl 客户端定义一个上下文,以便在每个名字空间中工作。 "cluster" 和 "user" 字段的值将从当前上下文中复制。
kubectl config set-context dev --namespace=development \
--cluster=lithe-cocoa-92103_kubernetes \
--user=lithe-cocoa-92103_kubernetes
kubectl config set-context prod --namespace=production \
--cluster=lithe-cocoa-92103_kubernetes \
--user=lithe-cocoa-92103_kubernetes
默认情况下,上述命令会添加两个上下文到 .kube/config
文件中。
你现在可以查看上下文并根据你希望使用的名字空间并在这两个新的请求上下文之间切换。
查看新的上下文:
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://130.211.122.180
name: lithe-cocoa-92103_kubernetes
contexts:
- context:
cluster: lithe-cocoa-92103_kubernetes
user: lithe-cocoa-92103_kubernetes
name: lithe-cocoa-92103_kubernetes
- context:
cluster: lithe-cocoa-92103_kubernetes
namespace: development
user: lithe-cocoa-92103_kubernetes
name: dev
- context:
cluster: lithe-cocoa-92103_kubernetes
namespace: production
user: lithe-cocoa-92103_kubernetes
name: prod
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
user:
password: h5M0FtUUIflBSdI7
username: admin
让我们切换到 development
名字空间进行操作。
kubectl config use-context dev
你可以使用下列命令验证当前上下文:
kubectl config current-context
dev
此时,我们从命令行向 Kubernetes 集群发出的所有请求都限定在 development
名字空间中。
让我们创建一些内容。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: snowflake
name: snowflake
spec:
replicas: 2
selector:
matchLabels:
app: snowflake
template:
metadata:
labels:
app: snowflake
spec:
containers:
- image: registry.k8s.io/serve_hostname
imagePullPolicy: Always
name: snowflake
应用清单文件来创建 Deployment。
kubectl apply -f https://k8s.io/examples/admin/snowflake-deployment.yaml
我们创建了一个副本大小为 2 的 Deployment,该 Deployment 运行名为 snowflake
的 Pod,
其中包含一个仅提供主机名服务的基本容器。
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
snowflake 2/2 2 2 2m
kubectl get pods -l app=snowflake
NAME READY STATUS RESTARTS AGE
snowflake-3968820950-9dgr8 1/1 Running 0 2m
snowflake-3968820950-vgc4n 1/1 Running 0 2m
这很棒,开发人员可以做他们想要的事情,而不必担心影响 production
名字空间中的内容。
让我们切换到 production
名字空间,展示一个名字空间中的资源如何对另一个名字空间不可见。
kubectl config use-context prod
production
名字空间应该是空的,下列命令应该返回的内容为空。
kubectl get deployment
kubectl get pods
生产环境需要以放牛的方式运维,让我们创建一些名为 cattle
的 Pod。
kubectl create deployment cattle --image=registry.k8s.io/serve_hostname --replicas=5
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
cattle 5/5 5 5 10s
kubectl get pods -l run=cattle
NAME READY STATUS RESTARTS AGE
cattle-2263376956-41xy6 1/1 Running 0 34s
cattle-2263376956-kw466 1/1 Running 0 34s
cattle-2263376956-n4v97 1/1 Running 0 34s
cattle-2263376956-p5p3i 1/1 Running 0 34s
cattle-2263376956-sxpth 1/1 Running 0 34s
此时,应该很清楚地展示了用户在一个名字空间中创建的资源对另一个名字空间是不可见的。
随着 Kubernetes 中的策略支持的发展,我们将扩展此场景,以展示如何为每个名字空间提供不同的授权规则。