访问集群中的应用程序
- 1: 部署和访问 Kubernetes 仪表板(Dashboard)
- 2: 访问集群
- 3: 使用端口转发来访问集群中的应用
- 4: 使用服务来访问集群中的应用
- 5: 使用 Service 把前端连接到后端
- 6: 创建外部负载均衡器
- 7: 列出集群中所有运行容器的镜像
- 8: 在 Minikube 环境中使用 NGINX Ingress 控制器配置 Ingress
- 9: 为集群配置 DNS
- 10: 同 Pod 内的容器使用共享卷通信
- 11: 访问集群上运行的服务
- 12: 配置对多集群的访问
1 - 部署和访问 Kubernetes 仪表板(Dashboard)
Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源 (如 Deployment,Job,DaemonSet 等等)。 例如,你可以对 Deployment 实现弹性伸缩、发起滚动升级、重启 Pod 或者使用向导创建新的应用。
Dashboard 同时展示了 Kubernetes 集群中的资源状态信息和所有报错信息。
部署 Dashboard UI
默认情况下不会部署 Dashboard。可以通过以下命令部署:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml
访问 Dashboard 用户界面
为了保护你的集群数据,默认情况下,Dashboard 会使用最少的 RBAC 配置进行部署。 当前,Dashboard 仅支持使用 Bearer 令牌登录。 要为此样本演示创建令牌,你可以按照 创建示例用户 上的指南进行操作。
命令行代理
你可以使用 kubectl
命令行工具来启用 Dashboard 访问,命令如下:
kubectl proxy
kubectl 会使得 Dashboard 可以通过 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 访问。
UI 只能 通过执行这条命令的机器进行访问。更多选项参见 kubectl proxy --help
。
欢迎界面
当访问空集群的 Dashboard 时,你会看到欢迎界面。
页面包含一个指向此文档的链接,以及一个用于部署第一个应用程序的按钮。
此外,你可以看到在默认情况下有哪些默认系统应用运行在 kube-system
名字空间 中,比如 Dashboard 自己。
部署容器化应用
通过一个简单的部署向导,你可以使用 Dashboard 将容器化应用作为一个 Deployment 和可选的 Service 进行创建和部署。你可以手工指定应用的详细配置,或者上传一个包含应用配置的 YAML 或 JSON _清单_文件。
点击任何页面右上角的 CREATE 按钮以开始。
指定应用的详细配置
部署向导需要你提供以下信息:
-
应用名称(必填):应用的名称。内容为
应用名称
的 标签 会被添加到任何将被部署的 Deployment 和 Service。在选定的 Kubernetes 名字空间 中, 应用名称必须唯一。必须由小写字母开头,以数字或者小写字母结尾, 并且只含有小写字母、数字和中划线(-)。小于等于24个字符。开头和结尾的空格会被忽略。
- 容器镜像(必填):公共镜像仓库上的 Docker 容器镜像 或者私有镜像仓库 (通常是 Google Container Registry 或者 Docker Hub)的 URL。容器镜像参数说明必须以冒号结尾。
-
Pod 的数量(必填):你希望应用程序部署的 Pod 的数量。值必须为正整数。
系统会创建一个 Deployment 以保证集群中运行期望的 Pod 数量。
-
服务(可选):对于部分应用(比如前端),你可能想对外暴露一个 Service ,这个 Service 可能用的是集群之外的公网 IP 地址(外部 Service)。
Note: 对于外部服务,你可能需要开放一个或多个端口才行。其它只能对集群内部可见的 Service 称为内部 Service。
不管哪种 Service 类型,如果你选择创建一个 Service,而且容器在一个端口上开启了监听(入向的), 那么你需要定义两个端口。创建的 Service 会把(入向的)端口映射到容器可见的目标端口。 该 Service 会把流量路由到你部署的 Pod。支持 TCP 协议和 UDP 协议。 这个 Service 的内部 DNS 解析名就是之前你定义的应用名称的值。
如果需要,你可以打开 Advanced Options 部分,这里你可以定义更多设置:
- 描述:这里你输入的文本会作为一个 注解 添加到 Deployment,并显示在应用的详细信息中。
-
标签:应用默认使用的 标签 是应用名称和版本。 你可以为 Deployment、Service(如果有)定义额外的标签,比如 release(版本)、 environment(环境)、tier(层级)、partition(分区) 和 release track(版本跟踪)。
例子:
release=1.0 tier=frontend environment=pod track=stable
-
名字空间:Kubernetes 支持多个虚拟集群依附于同一个物理集群。 这些虚拟集群被称为 名字空间, 可以让你将资源划分为逻辑命名的组。
Dashboard 通过下拉菜单提供所有可用的名字空间,并允许你创建新的名字空间。 名字空间的名称最长可以包含 63 个字母或数字和中横线(-),但是不能包含大写字母。
名字空间的名称不能只包含数字。如果名字被设置成一个数字,比如 10,pod 就
在名字空间创建成功的情况下,默认会使用新创建的名字空间。如果创建失败,那么第一个名字空间会被选中。
-
镜像拉取 Secret:如果要使用私有的 Docker 容器镜像,需要拉取 Secret 凭证。
Dashboard 通过下拉菜单提供所有可用的 Secret,并允许你创建新的 Secret。 Secret 名称必须遵循 DNS 域名语法,比如
new.image-pull.secret
。 Secret 的内容必须是 base64 编码的,并且在一个.dockercfg
文件中声明。Secret 名称最大可以包含 253 个字符。在镜像拉取 Secret 创建成功的情况下,默认会使用新创建的 Secret。 如果创建失败,则不会使用任何 Secret。
- CPU 需求(核数)和内存需求(MiB):你可以为容器定义最小的 资源限制。 默认情况下,Pod 没有 CPU 和内存限制。
- 运行命令和运行命令参数:默认情况下,你的容器会运行 Docker 镜像的默认 入口命令。 你可以使用 command 选项覆盖默认值。
- 以特权模式运行:这个设置决定了在 特权容器 中运行的进程是否像主机中使用 root 运行的进程一样。 特权容器可以使用诸如操纵网络堆栈和访问设备的功能。
- 环境变量:Kubernetes 通过
环境变量
暴露 Service。你可以构建环境变量,或者将环境变量的值作为参数传递给你的命令。
它们可以被应用用于查找 Service。值可以通过
$(VAR_NAME)
语法关联其他变量。
上传 YAML 或者 JSON 文件
Kubernetes 支持声明式配置。所有的配置都存储在清单文件 (YAML 或者 JSON 配置文件)中。这些 清单使用 Kubernetes API 定义的资源模式。
作为一种替代在部署向导中指定应用详情的方式,你可以在一个或多个清单文件中定义应用,并且使用 Dashboard 上传文件。
使用 Dashboard
以下各节描述了 Kubernetes Dashboard UI 视图;包括它们提供的内容,以及怎么使用它们。
导航
当在集群中定义 Kubernetes 对象时,Dashboard 会在初始视图中显示它们。 默认情况下只会显示 默认 名字空间中的对象,可以通过更改导航栏菜单中的名字空间筛选器进行改变。
Dashboard 展示大部分 Kubernetes 对象,并将它们分组放在几个菜单类别中。
管理概述
集群和名字空间管理的视图, Dashboard 会列出节点、名字空间和持久卷,并且有它们的详细视图。 节点列表视图包含从所有节点聚合的 CPU 和内存使用的度量值。 详细信息视图显示了一个节点的度量值,它的规格、状态、分配的资源、事件和这个节点上运行的 Pod。
负载
显示选中的名字空间中所有运行的应用。 视图按照负载类型(如 Deployment、ReplicaSet、StatefulSet 等)罗列应用,并且每种负载都可以单独查看。 列表总结了关于负载的可执行信息,比如一个 ReplicaSet 的就绪状态的 Pod 数量,或者目前一个 Pod 的内存用量。
工作负载的详情视图展示了对象的状态、详细信息和相互关系。 例如,ReplicaSet 所控制的 Pod,或者 Deployment 所关联的新 ReplicaSet 和 HorizontalPodAutoscalers。
服务
展示允许暴露给外网服务和允许集群内部发现的 Kubernetes 资源。 因此,Service 和 Ingress 视图展示他们关联的 Pod、给集群连接使用的内部端点和给外部用户使用的外部端点。
存储
存储视图展示持久卷申领(PVC)资源,这些资源被应用程序用来存储数据。
ConfigMap 和 Secret
展示的所有 Kubernetes 资源是在集群中运行的应用程序的实时配置。 通过这个视图可以编辑和管理配置对象,并显示那些默认隐藏的 Secret。
日志查看器
Pod 列表和详细信息页面可以链接到 Dashboard 内置的日志查看器。 查看器可以深入查看属于同一个 Pod 的不同容器的日志。
What's next
更多信息,参见 Kubernetes Dashboard 项目页面.
2 - 访问集群
本文阐述多种与集群交互的方法。
使用 kubectl 完成集群的第一次访问
当你第一次访问 Kubernetes API 的时候,我们建议你使用 Kubernetes CLI,kubectl
。
访问集群时,你需要知道集群的地址并且拥有访问的凭证。通常,这些在你通过 启动安装安装集群时都是自动安装好的,或者其他人安装时 也应该提供了凭证和集群地址。
通过以下命令检查 kubectl 是否知道集群地址及凭证:
kubectl config view
有许多 例子 介绍了如何使用 kubectl, 可以在 kubectl 参考 中找到更完整的文档。
直接访问 REST API
Kubectl 处理 apiserver 的定位和身份验证。 如果要使用 curl 或 wget 等 http 客户端或浏览器直接访问 REST API,可以通过 多种方式查找和验证:
- 以代理模式运行 kubectl。
- 推荐此方式。
- 使用已存储的 apiserver 地址。
- 使用自签名的证书来验证 apiserver 的身份。杜绝 MITM 攻击。
- 对 apiserver 进行身份验证。
- 未来可能会实现智能化的客户端负载均衡和故障恢复。
- 直接向 http 客户端提供位置和凭据。
- 可选的方案。
- 适用于代理可能引起混淆的某些客户端类型。
- 需要引入根证书到你的浏览器以防止 MITM 攻击。
使用 kubectl proxy
以下命令以反向代理的模式运行 kubectl。它处理 apiserver 的定位和验证。 像这样运行:
kubectl proxy --port=8080 &
参阅 kubectl proxy 获取更多详细信息。
然后,你可以使用 curl、wget 或浏览器访问 API,如果是 IPv6 则用 [::1] 替换 localhost, 如下所示:
curl http://localhost:8080/api/
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
不使用 kubectl proxy
在 Kubernetes 1.3 或更高版本中,kubectl config view
不再显示 token。
使用 kubectl apply
和 kubectl describe secret ...
及 grep 和剪切操作来为 default 服务帐户创建令牌,如下所示:
grep/cut
方法实现:
首先,创建 Secret,请求默认 ServiceAccount 的令牌:
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: default-token
annotations:
kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF
接下来,等待令牌控制器使用令牌填充 Secret:
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
echo "waiting for token..." >&2
sleep 1
done
捕获并使用生成的令牌:
APISERVER=$(kubectl config view | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret default-token | grep -E '^token' | cut -f2 -d':' | tr -d ' ')
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
jsonpath
方法实现:
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode )
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}
上面的例子使用了 --insecure
参数,这使得它很容易受到 MITM 攻击。
当 kubectl 访问集群时,它使用存储的根证书和客户端证书来访问服务器
(它们安装在 ~/.kube
目录中)。
由于集群证书通常是自签名的,因此可能需要特殊配置才能让你的 http 客户端使用根证书。
在一些集群中,apiserver 不需要身份验证;它可能只服务于 localhost,或者被防火墙保护, 这个没有一定的标准。 配置对 API 的访问 描述了集群管理员如何进行配置。此类方法可能与未来的高可用性支持相冲突。
以编程方式访问 API
Kubernetes 官方提供对 Go 和 Python 的客户端库支持。
Go 客户端
- 想要获得这个库,请运行命令:
go get k8s.io/client-go/<version number>/kubernetes
。 参阅 https://github.com/kubernetes/client-go 来查看目前支持哪些版本。 - 基于这个 client-go 客户端库编写应用程序。
请注意,client-go 定义了自己的 API 对象,因此如果需要,请从 client-go 而不是从主存储库
导入 API 定义,例如,
import "k8s.io/client-go/1.4/pkg/api/v1"
才是对的。
Go 客户端可以像 kubectl CLI 一样使用相同的 kubeconfig 文件 来定位和验证 apiserver。可参阅 示例。
如果应用程序以 Pod 的形式部署在集群中,那么请参阅 下一章。
Python 客户端
如果想要使用 Python 客户端,
请运行命令:pip install kubernetes
。参阅
Python Client Library page
以获得更详细的安装参数。
Python 客户端可以像 kubectl CLI 一样使用相同的 kubeconfig 文件 来定位和验证 apiserver,可参阅 示例。
其它语言
目前有多个客户端库 为其它语言提供访问 API 的方法。 参阅其它库的相关文档以获取他们是如何验证的。
从 Pod 中访问 API
当你从 Pod 中访问 API 时,定位和验证 API 服务器会有些许不同。
请参阅从 Pod 中访问 API 了解更多详情。
访问集群上运行的服务
上一节介绍了如何连接到 Kubernetes API 服务器。 有关连接到 Kubernetes 集群上运行的其他服务的信息,请参阅 访问集群服务。
请求重定向
重定向功能已弃用并被删除。请改用代理(见下文)。
多种代理
使用 Kubernetes 时可能会遇到几种不同的代理:
-
- 在用户的桌面或 Pod 中运行
- 代理从本地主机地址到 Kubernetes apiserver
- 客户端到代理将使用 HTTP
- 代理到 apiserver 使用 HTTPS
- 定位 apiserver
- 添加身份验证头部
-
- 内置于 apiserver 中
- 将集群外部的用户连接到集群 IP,否则这些 IP 可能无法访问
- 运行在 apiserver 进程中
- 客户端代理使用 HTTPS(也可配置为 http)
- 代理将根据可用的信息决定使用 HTTP 或者 HTTPS 代理到目标
- 可用于访问节点、Pod 或服务
- 在访问服务时进行负载平衡
-
- 运行在每个节点上
- 代理 UDP 和 TCP
- 不能代理 HTTP
- 提供负载均衡
- 只能用来访问服务
-
位于 apiserver 之前的 Proxy/Load-balancer:
- 存在和实现因集群而异(例如 nginx)
- 位于所有客户和一个或多个 apiserver 之间
- 如果有多个 apiserver,则充当负载均衡器
-
外部服务上的云负载均衡器:
- 由一些云提供商提供(例如 AWS ELB,Google Cloud Load Balancer)
- 当 Kubernetes 服务类型为
LoadBalancer
时自动创建 - 只使用 UDP/TCP
- 具体实现因云提供商而异。
除了前两种类型之外,Kubernetes 用户通常不需要担心任何其他问题。 集群管理员通常会确保后者的正确配置。
3 - 使用端口转发来访问集群中的应用
本文展示如何使用 kubectl port-forward
连接到在 Kubernetes 集群中
运行的 MongoDB 服务。这种类型的连接对数据库调试很有用。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
- 安装 MongoDB Shell。
创建 MongoDB deployment 和服务
-
创建一个运行 MongoDB 的 deployment:
kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-deployment.yaml
查看输出是否成功,以验证是否成功创建 deployment:
deployment.apps/mongo created
查看 pod 状态,检查其是否准备就绪:
kubectl get pods
输出显示创建的 pod:
NAME READY STATUS RESTARTS AGE mongo-75f59d57f4-4nd6q 1/1 Running 0 2m4s
查看 Deployment 状态:
kubectl get deployment
输出显示创建的 Deployment:
NAME READY UP-TO-DATE AVAILABLE AGE mongo 1/1 1 1 2m21s
Deployment 自动管理 ReplicaSet。 查看 ReplicaSet 状态:
kubectl get replicaset
输出显示创建的 ReplicaSet:
NAME DESIRED CURRENT READY AGE mongo-75f59d57f4 1 1 1 3m12s
-
创建一个在网络上公开的 MongoDB 服务:
kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-service.yaml
查看输出是否成功,以验证是否成功创建 Service:
service/mongo created
检查 Service 是否创建:
kubectl get service mongo
输出显示创建的 Service:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE mongo ClusterIP 10.96.41.183 <none> 27017/TCP 11s
-
验证 MongoDB 服务是否运行在 Pod 中并且监听 27017 端口:
# Change mongo-75f59d57f4-4nd6q to the name of the Pod kubectl get pod mongo-75f59d57f4-4nd6q --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
输出应该显示 Pod 中 MongoDB 的端口:
27017
(这是 Internet 分配给 MongoDB 的 TCP 端口)。
转发一个本地端口到 Pod 端口
-
kubectl port-forward
允许使用资源名称 (例如 pod 名称)来选择匹配的 pod 来进行端口转发。# Change mongo-75f59d57f4-4nd6q to the name of the Pod kubectl port-forward mongo-75f59d57f4-4nd6q 28015:27017
这相当于
kubectl port-forward pods/mongo-75f59d57f4-4nd6q 28015:27017
或者
kubectl port-forward deployment/mongo 28015:27017
或者
kubectl port-forward replicaset/mongo-75f59d57f4 28015:27017
或者
kubectl port-forward service/mongo 28015:27017
以上所有命令都应该有效。输出应该类似于:
Forwarding from 127.0.0.1:28015 -> 27017 Forwarding from [::1]:28015 -> 27017
kubectl port-forward
不会返回。你需要打开另一个终端来继续这个练习。
- 启动 MongoDB 命令行接口:
mongosh --port 28015
-
在 MongoDB 命令行提示符下,输入
ping
命令:db.runCommand( { ping: 1 } )
成功的 ping 请求应该返回:
{ ok: 1 }
(可选操作)让 kubectl 来选择本地端口
如果你不需要指定特定的本地端口,你可以让 kubectl
来选择和分配本地端口,
以便你不需要管理本地端口冲突。该命令使用稍微不同的语法:
kubectl port-forward deployment/mongo :27017
kubectl
工具会找到一个未被使用的本地端口号(避免使用低段位的端口号,因为他们可能会被其他应用程序使用)。
输出应该类似于:
Forwarding from 127.0.0.1:63753 -> 27017
Forwarding from [::1]:63753 -> 27017
讨论
与本地 28015 端口建立的连接将转发到运行 MongoDB 服务器的 Pod 的 27017 端口。 通过此连接,您可以使用本地工作站来调试在 Pod 中运行的数据库。
kubectl port-forward
仅适用于 TCP 端口。
在 issue 47862
中跟踪了对 UDP 协议的支持。
What's next
进一步了解 kubectl port-forward。
4 - 使用服务来访问集群中的应用
本文展示如何创建一个 Kubernetes 服务对象,能让外部客户端访问在集群中运行的应用。 该服务为一个应用的两个运行实例提供负载均衡。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
Objectives
- 运行 Hello World 应用的两个实例。
- 创建一个服务对象来暴露 node port。
- 使用服务对象来访问正在运行的应用。
为运行在两个 pod 中的应用创建一个服务
这是应用程序部署的配置文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
selector:
matchLabels:
run: load-balancer-example
replicas: 2
template:
metadata:
labels:
run: load-balancer-example
spec:
containers:
- name: hello-world
image: gcr.io/google-samples/node-hello:1.0
ports:
- containerPort: 8080
protocol: TCP
-
在你的集群中运行一个 Hello World 应用: 使用上面的文件创建应用程序 Deployment:
kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
上面的命令创建一个 Deployment 对象 和一个关联的 ReplicaSet 对象。 这个 ReplicaSet 有两个 Pod, 每个 Pod 都运行着 Hello World 应用。
-
展示 Deployment 的信息:
kubectl get deployments hello-world kubectl describe deployments hello-world
-
展示你的 ReplicaSet 对象信息:
kubectl get replicasets kubectl describe replicasets
-
创建一个服务对象来暴露 Deployment:
kubectl expose deployment hello-world --type=NodePort --name=example-service
-
展示 Service 信息:
kubectl describe services example-service
输出类似于:
Name: example-service Namespace: default Labels: run=load-balancer-example Annotations: <none> Selector: run=load-balancer-example Type: NodePort IP: 10.32.0.16 Port: <unset> 8080/TCP TargetPort: 8080/TCP NodePort: <unset> 31496/TCP Endpoints: 10.200.1.4:8080,10.200.2.5:8080 Session Affinity: None Events: <none>
注意服务中的 NodePort 值。例如在上面的输出中,NodePort 是 31496。
-
列出运行 Hello World 应用的 Pod:
kubectl get pods --selector="run=load-balancer-example" --output=wide
输出类似于:
NAME READY STATUS ... IP NODE hello-world-2895499144-bsbk5 1/1 Running ... 10.200.1.4 worker1 hello-world-2895499144-m1pwt 1/1 Running ... 10.200.2.5 worker2
-
获取运行 Hello World 的 pod 的其中一个节点的公共 IP 地址。如何获得此地址取决于你设置集群的方式。 例如,如果你使用的是 Minikube,则可以通过运行
kubectl cluster-info
来查看节点地址。 如果你使用的是 Google Compute Engine 实例,则可以使用gcloud compute instances list
命令查看节点的公共地址。 -
在你选择的节点上,创建一个防火墙规则以开放节点端口上的 TCP 流量。 例如,如果你的服务的 NodePort 值为 31568,请创建一个防火墙规则以允许 31568 端口上的 TCP 流量。 不同的云提供商提供了不同方法来配置防火墙规则。
-
使用节点地址和 node port 来访问 Hello World 应用:
curl http://<public-node-ip>:<node-port>
这里的 <public-node-ip>
是你节点的公共 IP 地址,<node-port>
是你服务的 NodePort 值。
对于请求成功的响应是一个 hello 消息:
Hello Kubernetes!
使用服务配置文件
作为 kubectl expose
的替代方法,你可以使用
服务配置文件 来创建服务。
Cleaning up
想要删除服务,输入以下命令:
kubectl delete services example-service
想要删除运行 Hello World 应用的 Deployment、ReplicaSet 和 Pod,输入以下命令:
kubectl delete deployment hello-world
What's next
- 进一步了解通过服务连接应用。
5 - 使用 Service 把前端连接到后端
本任务会描述如何创建前端(Frontend)微服务和后端(Backend)微服务。后端微服务是一个 hello 欢迎程序。 前端通过 nginx 和一个 Kubernetes 服务 暴露后端所提供的服务。
Objectives
- 使用部署对象(Deployment object)创建并运行一个
hello
后端微服务 - 使用一个 Service 对象将请求流量发送到后端微服务的多个副本
- 同样使用一个 Deployment 对象创建并运行一个
nginx
前端微服务 - 配置前端微服务将请求流量发送到后端微服务
- 使用
type=LoadBalancer
的 Service 对象将全段微服务暴露到集群外部
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
本任务使用外部负载均衡服务, 所以需要对应的可支持此功能的环境。如果你的环境不能支持,你可以使用 NodePort 类型的服务代替。
使用部署对象(Deployment)创建后端
后端是一个简单的 hello 欢迎微服务应用。这是后端应用的 Deployment 配置文件:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
selector:
matchLabels:
app: hello
tier: backend
track: stable
replicas: 3
template:
metadata:
labels:
app: hello
tier: backend
track: stable
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-go-gke:1.0"
ports:
- name: http
containerPort: 80
...
创建后端 Deployment:
kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml
查看后端的 Deployment 信息:
kubectl describe deployment backend
输出类似于:
Name: backend
Namespace: default
CreationTimestamp: Mon, 24 Oct 2016 14:21:02 -0700
Labels: app=hello
tier=backend
track=stable
Annotations: deployment.kubernetes.io/revision=1
Selector: app=hello,tier=backend,track=stable
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: app=hello
tier=backend
track=stable
Containers:
hello:
Image: "gcr.io/google-samples/hello-go-gke:1.0"
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: hello-3621623197 (3/3 replicas created)
Events:
...
创建 hello
Service 对象
将请求从前端发送到到后端的关键是后端 Service。Service 创建一个固定 IP 和 DNS 解析名入口, 使得后端微服务总是可达。Service 使用 选择算符 来寻找目标 Pod。
首先,浏览 Service 的配置文件:
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
tier: backend
ports:
- protocol: TCP
port: 80
targetPort: http
...
配置文件中,你可以看到名为 hello
的 Service 将流量路由到包含 app: hello
和 tier: backend
标签的 Pod。
创建后端 Service:
kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml
此时,你已经有了一个运行着 hello
应用的三个副本的 backend
Deployment,你也有了
一个 Service 用于路由网络流量。不过,这个服务在集群外部无法访问也无法解析。
创建前端应用
现在你已经有了运行中的后端应用,你可以创建一个可在集群外部访问的前端,并通过代理 前端的请求连接到后端。
前端使用被赋予后端 Service 的 DNS 名称将请求发送到后端工作 Pods。这一 DNS
名称为 hello
,也就是 examples/service/access/backend-service.yaml
配置
文件中 name
字段的取值。
前端 Deployment 中的 Pods 运行一个 nginx 镜像,这个已经配置好的镜像会将请求转发 给后端的 hello Service。下面是 nginx 的配置文件:
# The identifier Backend is internal to nginx, and used to name this specific upstream
upstream Backend {
# hello is the internal DNS name used by the backend Service inside Kubernetes
server hello;
}
server {
listen 80;
location / {
# The following statement will proxy traffic to the upstream named Backend
proxy_pass http://Backend;
}
}
与后端类似,前端用包含一个 Deployment 和一个 Service。后端与前端服务之间的一个
重要区别是前端 Service 的配置文件包含了 type: LoadBalancer
,也就是说,Service
会使用你的云服务商的默认负载均衡设备,从而实现从集群外访问的目的。
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
selector:
app: hello
tier: frontend
ports:
- protocol: "TCP"
port: 80
targetPort: 80
type: LoadBalancer
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
selector:
matchLabels:
app: hello
tier: frontend
track: stable
replicas: 1
template:
metadata:
labels:
app: hello
tier: frontend
track: stable
spec:
containers:
- name: nginx
image: "gcr.io/google-samples/hello-frontend:1.0"
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
...
创建前端 Deployment 和 Service:
kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/frontend-service.yaml
通过输出确认两个资源都已经被创建:
deployment.apps/frontend created
service/frontend created
与前端 Service 交互
一旦你创建了 LoadBalancer 类型的 Service,你可以使用这条命令查看外部 IP:
kubectl get service frontend
外部 IP 字段的生成可能需要一些时间。如果是这种情况,外部 IP 会显示为 <pending>
。
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend 10.51.252.116 <pending> 80/TCP 10s
当外部 IP 地址被分配可用时,配置会更新,在 EXTERNAL-IP
头部下显示新的 IP:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend 10.51.252.116 XXX.XXX.XXX.XXX 80/TCP 1m
这一新的 IP 地址就可以用来从集群外与 frontend
服务交互了。
通过前端发送流量
前端和后端已经完成连接了。你可以使用 curl 命令通过你的前端 Service 的外部 IP 访问服务端点。
curl http://${EXTERNAL_IP} # 将 EXTERNAL_P 替换为你之前看到的外部 IP
输出显示后端生成的消息:
{"message":"Hello"}
Cleaning up
要删除服务,输入下面的命令:
kubectl delete services frontend backend
要删除在前端和后端应用中运行的 Deployment、ReplicaSet 和 Pod,输入下面的命令:
kubectl delete deployment frontend backend
What's next
- 进一步了解 Service
- 进一步了解 ConfigMap
- 进一步了解 Service 和 Pods 的 DNS
6 - 创建外部负载均衡器
本文展示如何创建一个外部负载均衡器。
创建服务时,你可以选择自动创建云网络负载均衡器。这提供了一个外部可访问的 IP 地址, 可将流量分配到集群节点上的正确端口上 ( 假设集群在支持的环境中运行,并配置了正确的云负载平衡器提供商包)。
有关如何配置和使用 Ingress 资源为服务提供外部可访问的 URL、负载均衡流量、终止 SSL 等功能, 请查看 Ingress 文档。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
配置文件
要创建外部负载均衡器,请将以下内容添加到 服务配置文件:
type: LoadBalancer
你的配置文件可能会如下所示:
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- port: 8765
targetPort: 9376
type: LoadBalancer
使用 kubectl
你也可以使用 kubectl expose
命令及其 --type=LoadBalancer
参数创建服务:
kubectl expose rc example --port=8765 --target-port=9376 \
--name=example-service --type=LoadBalancer
此命令通过使用与引用资源(在上面的示例的情况下,名为 example
的 replication controller)相同的选择器来创建一个新的服务。
更多信息(包括更多的可选参数),请参阅
kubectl expose
指南。
找到你的 IP 地址
你可以通过 kubectl
获取服务信息,找到为你的服务创建的 IP 地址:
kubectl describe services example-service
这将获得如下输出:
Name: example-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=example
Type: LoadBalancer
IP: 10.67.252.103
LoadBalancer Ingress: 192.0.2.89
Port: <unnamed> 80/TCP
NodePort: <unnamed> 32445/TCP
Endpoints: 10.64.0.4:80,10.64.1.5:80,10.64.2.4:80
Session Affinity: None
Events: <none>
IP 地址列在 LoadBalancer Ingress
旁边。
如果你在 Minikube 上运行服务,你可以通过以下命令找到分配的 IP 地址和端口:
minikube service example-service --url
保留客户端源 IP
由于此功能的实现,目标容器中看到的源 IP 将 不是客户端的原始源 IP。 要启用保留客户端 IP,可以在服务的 spec 中配置以下字段(支持 GCE/Google Kubernetes Engine 环境):
service.spec.externalTrafficPolicy
- 表示此服务是否希望将外部流量路由到节点本地或集群范围的端点。 有两个可用选项:Cluster(默认)和 Local。 Cluster 隐藏了客户端源 IP,可能导致第二跳到另一个节点,但具有良好的整体负载分布。 Local 保留客户端源 IP 并避免 LoadBalancer 和 NodePort 类型服务的第二跳, 但存在潜在的不均衡流量传播风险。
service.spec.healthCheckNodePort
- 指定服务的 healthcheck nodePort(数字端口号)。 如果未指定healthCheckNodePort
,服务控制器从集群的 NodePort 范围内分配一个端口。 你可以通过设置 API 服务器的命令行选项--service-node-port-range
来配置上述范围。 它将会使用用户指定的healthCheckNodePort
值(如果被客户端指定)。 仅当type
设置为 LoadBalancer 并且externalTrafficPolicy
设置为 Local 时才生效。
可以通过在服务的配置文件中将 externalTrafficPolicy
设置为 Local 来激活此功能。
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- port: 8765
targetPort: 9376
externalTrafficPolicy: Local
type: LoadBalancer
回收负载均衡器
在通常情况下,应在删除 LoadBalancer 类型服务后立即清除云提供商中的相关负载均衡器资源。 但是,众所周知,在删除关联的服务后,云资源被孤立的情况很多。 引入了针对服务负载均衡器的终结器保护,以防止这种情况发生。 通过使用终结器,在删除相关的负载均衡器资源之前,也不会删除服务资源。
具体来说,如果服务具有 type
LoadBalancer,则服务控制器将附加一个名为
service.kubernetes.io/load-balancer-cleanup
的终结器。
仅在清除负载均衡器资源后才能删除终结器。
即使在诸如服务控制器崩溃之类的极端情况下,这也可以防止负载均衡器资源悬空。
外部负载均衡器提供商
请务必注意,此功能的数据路径由 Kubernetes 集群外部的负载均衡器提供。
当服务 type
设置为 LoadBalancer 时,Kubernetes 向集群中的 Pod 提供的功能等同于
type
等于 ClusterIP,并通过使用 Kubernetes pod 的条目对负载均衡器(从外部到 Kubernetes)
进行编程来扩展它。
Kubernetes 服务控制器自动创建外部负载均衡器、健康检查(如果需要)、防火墙规则(如果需要),
并获取云提供商分配的外部 IP 并将其填充到服务对象中。
保留源 IP 时的注意事项和限制
GCE/AWS 负载均衡器不为其目标池提供权重。 对于旧的 LB kube-proxy 规则来说,这不是一个问题,它可以在所有端点之间正确平衡。
使用新功能,外部流量不会在 pod 之间平均负载,而是在节点级别平均负载 (因为 GCE/AWS 和其他外部 LB 实现无法指定每个节点的权重, 因此它们的平衡跨所有目标节点,并忽略每个节点上的 Pod 数量)。
但是,我们可以声明,对于 NumServicePods << NumNodes
或 NumServicePods >> NumNodes
时,
即使没有权重,也会看到接近相等的分布。
一旦外部负载平衡器提供权重,就可以将此功能添加到 LB 编程路径中。 未来工作:1.4 版本不提供权重支持,但可能会在将来版本中添加
内部 Pod 到 Pod 的流量应该与 ClusterIP 服务类似,所有 Pod 的概率相同。
7 - 列出集群中所有运行容器的镜像
本文展示如何使用 kubectl 来列出集群中所有运行 Pod 的容器的镜像
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
在本练习中,你将使用 kubectl 来获取集群中运行的所有 Pod,并格式化输出来提取每个 Pod 中的容器列表。
列出所有命名空间下的所有容器
- 使用
kubectl get pods --all-namespaces
获取所有命名空间下的所有 Pod - 使用
-o jsonpath={.items[*].spec.containers[*].image}
来格式化输出,以仅包含容器镜像名称。 这将以递归方式从返回的 json 中解析出image
字段。- 参阅 jsonpath 说明 获取更多关于如何使用 jsonpath 的信息。
- 使用标准化工具来格式化输出:
tr
,sort
,uniq
- 使用
tr
以用换行符替换空格 - 使用
sort
来对结果进行排序 - 使用
uniq
来聚合镜像计数
- 使用
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c
上面的命令将递归获取所有返回项目的名为 image
的字段。
作为替代方案,可以使用 Pod 的镜像字段的绝对路径。这确保即使字段名称重复的情况下也能检索到正确的字段,例如,特定项目中的许多字段都称为 name
:
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}"
jsonpath 解释如下:
.items[*]
: 对于每个返回的值.spec
: 获取 spec.containers[*]
: 对于每个容器.image
: 获取镜像
kubectl get pod nginx
,路径的 .items[*]
部分应该省略,
因为返回的是一个 Pod 而不是一个项目列表。
按 Pod 列出容器镜像
可以使用 range
操作进一步控制格式化,以单独操作每个元素。
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort
列出以标签过滤后的 Pod 的所有容器
要获取匹配特定标签的 Pod,请使用 -l 参数。以下匹配仅与标签 app=nginx
相符的 Pod。
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" -l app=nginx
列出以命名空间过滤后的 Pod 的所有容器
要获取匹配特定命名空间的 Pod,请使用 namespace 参数。以下仅匹配 kube-system
命名空间下的 Pod。
kubectl get pods --namespace kube-system -o jsonpath="{.items[*].spec.containers[*].image}"
使用 go-template 代替 jsonpath 来获取容器
作为 jsonpath 的替代,Kubectl 支持使用 go-templates 来格式化输出:
kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"
What's next
参考
- Jsonpath 参考指南
- Go template 参考指南
8 - 在 Minikube 环境中使用 NGINX Ingress 控制器配置 Ingress
Ingress是一种 API 对象,其中定义了一些规则使得集群中的 服务可以从集群外访问。 Ingress 控制器 负责满足 Ingress 中所设置的规则。
本节为你展示如何配置一个简单的 Ingress,根据 HTTP URI 将服务请求路由到
服务 web
或 web2
。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
Your Kubernetes server must be at or later than version 1.19. To check the version, enterkubectl version
.
如果你使用的是较早的 Kubernetes 版本,请切换到该版本的文档。
创建一个 Minikube 集群
- 使用 Katacoda
- 本地
- 如果已经在本地安装Minikube,
请运行
minikube start
创建一个集群。
启用 Ingress 控制器
-
为了启用 NGINIX Ingress 控制器,可以运行下面的命令:
minikube addons enable ingress
-
检查验证 NGINX Ingress 控制器处于运行状态:
kubectl get pods -n ingress-nginx
Note: 最多可能需要等待一分钟才能看到这些 Pod 运行正常。输出类似于:
NAME READY STATUS RESTARTS AGE ingress-nginx-admission-create-g9g49 0/1 Completed 0 11m ingress-nginx-admission-patch-rqp78 0/1 Completed 1 11m ingress-nginx-controller-59b45fb494-26npt 1/1 Running 0 11m
kubectl get pods -n kube-system
Note: 最多可能需要等待一分钟才能看到这些 Pod 运行正常。输出类似于:
NAME READY STATUS RESTARTS AGE default-http-backend-59868b7dd6-xb8tq 1/1 Running 0 1m kube-addon-manager-minikube 1/1 Running 0 3m kube-dns-6dcb57bcc8-n4xd4 3/3 Running 0 2m kubernetes-dashboard-5498ccf677-b8p5h 1/1 Running 0 2m nginx-ingress-controller-5984b97644-rnkrg 1/1 Running 0 1m storage-provisioner 1/1 Running 0 2m
请确保可以在输出中看到一个名称以
nginx-ingress-controller-
为前缀的 Pod。
部署一个 Hello World 应用
-
使用下面的命令创建一个 Deployment:
kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
输出:
deployment.apps/web created
-
将 Deployment 暴露出来:
kubectl expose deployment web --type=NodePort --port=8080
输出:
service/web exposed
-
验证 Service 已经创建,并且可能从节点端口访问:
kubectl get service web
输出类似于:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web NodePort 10.104.133.249 <none> 8080:31637/TCP 12m
-
使用节点端口信息访问服务:
minikube service web --url
输出类似于:
http://172.17.0.15:31637
Note: 如果使用的是 Katacoda 环境,在终端面板顶端,请点击加号标志。 然后点击 Select port to view on Host 1。 输入节点和端口号(这里是31637
),之后点击 Display Port。输出类似于:
Hello, world! Version: 1.0.0 Hostname: web-55b8c6998d-8k564
你现在应该可以通过 Minikube 的 IP 地址和节点端口来访问示例应用了。 下一步是让自己能够通过 Ingress 资源来访问应用。
创建一个 Ingress
下面是一个定义 Ingress 的配置文件,负责通过 hello-world.info
将请求
转发到你的服务。
-
根据下面的 YAML 创建文件
example-ingress.yaml
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - host: hello-world.info http: paths: - path: / pathType: Prefix backend: service: name: web port: number: 8080
-
通过运行下面的命令创建 Ingress 对象:
kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml
输出:
ingress.networking.k8s.io/example-ingress created
-
验证 IP 地址已被设置:
kubectl get ingress
Note: 此操作可能需要几分钟时间。接下来你将会在ADDRESS列中看到IPv4地址,例如:
NAME CLASS HOSTS ADDRESS PORTS AGE example-ingress <none> hello-world.info 172.17.0.15 80 38s
-
在
/etc/hosts
文件的末尾添加以下内容(需要管理员访问权限):Note: 如果你在本地运行 Minikube 环境,需要使用minikube ip
获得外部 IP 地址。 Ingress 列表中显示的 IP 地址会是内部 IP 地址。172.17.0.15 hello-world.info
添加完成后,在浏览器中访问URL
hello-world.info
,请求将被发送到 Minikube。
-
验证 Ingress 控制器能够转发请求流量:
curl hello-world.info
你应该看到类似输出:
Hello, world! Version: 1.0.0 Hostname: web-55b8c6998d-8k564
Note: 如果你在使用本地 Minikube 环境,你可以从浏览器中访问 hello-world.info。
创建第二个 Deployment
-
使用下面的命令创建第二个 Deployment:
kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
输出:
deployment.apps/web2 created
-
将第二个 Deployment 暴露出来:
kubectl expose deployment web2 --port=8080 --type=NodePort
输出:
service/web2 exposed
编辑现有的 Ingress
-
编辑现有的
example-ingress.yaml
,在文件最后添加以下行:- path: /v2 pathType: Prefix backend: service: name: web2 port: number: 8080
-
应用变更:
kubectl apply -f example-ingress.yaml
输出:
ingress.networking/example-ingress configured
测试你的 Ingress
-
访问 HelloWorld 应用的第一个版本:
curl hello-world.info
输出类似于:
Hello, world! Version: 1.0.0 Hostname: web-55b8c6998d-8k564
-
访问 HelloWorld 应用的第二个版本:
curl hello-world.info/v2
输出类似于:
Hello, world! Version: 2.0.0 Hostname: web2-75cd47646f-t8cjk
Note: 如果你在本地运行 Minikube 环境,你可以使用浏览器来访问 hello-world.info 和 hello-world.info/v2。
What's next
- 进一步了解 Ingress。
- 进一步了解 Ingress 控制器
- 进一步了解 服务
9 - 为集群配置 DNS
Kubernetes 提供 DNS 集群插件,大多数支持的环境默认情况下都会启用。 在 Kubernetes 1.11 及其以后版本中,推荐使用 CoreDNS, kubeadm 默认会安装 CoreDNS。
要了解关于如何为 Kubernetes 集群配置 CoreDNS 的更多信息,参阅 定制 DNS 服务。 关于如何利用 kube-dns 配置 kubernetes DNS 的演示例子,参阅 Kubernetes DNS 插件示例。
10 - 同 Pod 内的容器使用共享卷通信
本文旨在说明如何让一个 Pod 内的两个容器使用一个卷(Volume)进行通信。 参阅如何让两个进程跨容器通过 共享进程名字空间。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
创建一个包含两个容器的 Pod
在这个练习中,你会创建一个包含两个容器的 Pod。两个容器共享一个卷用于他们之间的通信。 Pod 的配置文件如下:
apiVersion: v1
kind: Pod
metadata:
name: two-containers
spec:
restartPolicy: Never
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: debian-container
image: debian
volumeMounts:
- name: shared-data
mountPath: /pod-data
command: ["/bin/sh"]
args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]
在配置文件中,你可以看到 Pod 有一个共享卷,名为 shared-data
。
配置文件中的第一个容器运行了一个 nginx 服务器。共享卷的挂载路径是 /usr/share/nginx/html
。
第二个容器是基于 debian 镜像的,有一个 /pod-data
的挂载路径。第二个容器运行了下面的命令然后终止。
echo Hello from the debian container > /pod-data/index.html
注意,第二个容器在 nginx 服务器的根目录下写了 index.html
文件。
创建一个包含两个容器的 Pod:
kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml
查看 Pod 和容器的信息:
kubectl get pod two-containers --output=yaml
这是输出的一部分:
apiVersion: v1
kind: Pod
metadata:
...
name: two-containers
namespace: default
...
spec:
...
containerStatuses:
- containerID: docker://c1d8abd1 ...
image: debian
...
lastState:
terminated:
...
name: debian-container
...
- containerID: docker://96c1ff2c5bb ...
image: nginx
...
name: nginx-container
...
state:
running:
...
你可以看到 debian 容器已经被终止了,而 nginx 服务器依然在运行。
进入 nginx 容器的 shell:
kubectl exec -it two-containers -c nginx-container -- /bin/bash
在 shell 中,确认 nginx 还在运行。
root@two-containers:/# ps aux
输出类似于这样:
USER PID ... STAT START TIME COMMAND
root 1 ... Ss 21:12 0:00 nginx: master process nginx -g daemon off;
回忆一下,debian 容器在 nginx 的根目录下创建了 index.html
文件。
使用 curl
向 nginx 服务器发送一个 GET 请求:
root@two-containers:/# curl localhost
输出表示 nginx 提供了 debian 容器写的页面:
Hello from the debian container
讨论
Pod 能有多个容器的主要原因是为了支持辅助应用(helper applications),以协助主应用(primary application)。 辅助应用的典型例子是数据抽取,数据推送和代理。辅助应用和主应用经常需要相互通信。 就如这个练习所示,通信通常是通过共享文件系统完成的,或者,也通过回环网络接口 localhost 完成。 举个网络接口的例子,web 服务器带有一个协助程序用于拉取 Git 仓库的更新。
在本练习中的卷为 Pod 生命周期中的容器相互通信提供了一种方法。如果 Pod 被删除或者重建了, 任何共享卷中的数据都会丢失。
What's next
- 进一步了解复合容器的模式
- 学习模块化架构中的复合容器
- 参见配置 Pod 使用卷来存储数据
- 参考在 Pod 中的容器之间共享进程命名空间
- 参考 Volume
- 参考 Pod
11 - 访问集群上运行的服务
本文展示了如何连接 Kubernetes 集群上运行的服务。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
To check the version, enterkubectl version
.
访问集群上运行的服务
在 Kubernetes 里,节点、 Pod 和 服务 都有自己的 IP。 许多情况下,集群上的节点 IP、Pod IP 和某些服务 IP 是路由不可达的, 所以不能从集群之外访问它们,例如从你自己的台式机。
连接方式
你有多种可选方式从集群外连接节点、Pod 和服务:
- 通过公网 IP 访问服务
- 使用类型为
NodePort
或LoadBalancer
的服务,可以从外部访问它们。 请查阅服务 和 kubectl expose 文档。 - 取决于你的集群环境,你可以仅把服务暴露在你的企业网络环境中,也可以将其暴露在 因特网上。需要考虑暴露的服务是否安全,它是否有自己的用户认证?
- 将 Pod 放置于服务背后。如果要访问一个副本集合中特定的 Pod,例如用于调试目的, 请给 Pod 指定一个独特的标签并创建一个新服务选择该标签。
- 大部分情况下,都不需要应用开发者通过节点 IP 直接访问节点。
- 使用类型为
- 通过 Proxy 动词访问服务、节点或者 Pod
- 在访问远程服务之前,利用 API 服务器执行身份认证和鉴权。 如果你的服务不够安全,无法暴露到因特网中,或者需要访问节点 IP 上的端口, 又或者出于调试目的,可使用这种方式。
- 代理可能给某些应用带来麻烦
- 此方式仅适用于 HTTP/HTTPS
- 进一步的描述在这里
- 从集群中的 node 或者 pod 访问。
- 从集群中的一个节点或 Pod 访问
- 运行一个 Pod,然后使用 kubectl exec 连接到它的 Shell。从那个 Shell 连接其他的节点、Pod 和 服务
- 某些集群可能允许你 SSH 到集群中的节点。你可能可以从那儿访问集群服务。 这是一个非标准的方式,可能在一些集群上能工作,但在另一些上却不能。 浏览器和其他工具可能已经安装也可能没有安装。集群 DNS 可能不会正常工作。
发现内置服务
典型情况下,kube-system 名字空间中会启动集群的几个服务。
使用 kubectl cluster-info
命令获取这些服务的列表:
kubectl cluster-info
输出类似于:
Kubernetes master is running at https://104.197.5.247
elasticsearch-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://104.197.5.247/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy
这一输出显示了用 proxy 动词访问每个服务时可用的 URL。例如,此集群
(使用 Elasticsearch)启用了集群层面的日志。如果提供合适的凭据,可以通过
https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/
访问,或通过一个 kubectl proxy
来访问:
http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/
。
kubectl proxy
。
手动构建 API 服务器代理 URLs
如前所述,你可以使用 kubectl cluster-info
命令取得服务的代理 URL。
为了创建包含服务末端、后缀和参数的代理 URLs,你可以在服务的代理 URL 中添加:
http://
kubernetes_master_address
/api/v1/namespaces/
namespace_name
/services/
service_name[:port_name]
/proxy
如果还没有为你的端口指定名称,你可以不用在 URL 中指定 port_name。 对于命名和未命名端口,你还可以使用端口号代替 port_name。
默认情况下,API 服务器使用 HTTP 为你的服务提供代理。 要使用 HTTPS,请在服务名称前加上 https:
:
http://<kubernetes_master_address>/api/v1/namespaces/<namespace_name>/services/<service_name>/proxy
URL 的 <service_name>
段支持的格式为:
<service_name>
- 使用 http 代理到默认或未命名端口<service_name>:<port_name>
- 使用 http 代理到指定的端口名称或端口号https:<service_name>:
- 使用 https 代理到默认或未命名端口(注意尾随冒号)https:<service_name>:<port_name>
- 使用 https 代理到指定的端口名称或端口号
示例
-
如要访问 Elasticsearch 服务末端
_search?q=user:kimchy
,你可以使用:http://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy
-
如要访问 Elasticsearch 集群健康信息
_cluster/health?pretty=true
,你会使用:https://104.197.5.247/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true`
健康信息与下面的例子类似:
{ "cluster_name" : "kubernetes_logging", "status" : "yellow", "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 5, "active_shards" : 5, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 5 }
-
要访问 https Elasticsearch 服务健康信息
_cluster/health?pretty=true
,你会使用:https://104.197.5.247/api/v1/namespaces/kube-system/services/https:elasticsearch-logging/proxy/_cluster/health?pretty=true
通过 Web 浏览器访问集群中运行的服务
你或许能够将 API 服务器代理的 URL 放入浏览器的地址栏,然而:
- Web 服务器通常不能传递令牌,所以你可能需要使用基本(密码)认证。 API 服务器可以配置为接受基本认证,但你的集群可能并没有这样配置。
- 某些 Web 应用可能无法工作,特别是那些使用客户端 Javascript 构造 URL 的 应用,所构造的 URL 可能并不支持代理路径前缀。
12 - 配置对多集群的访问
本文展示如何使用配置文件来配置对多个集群的访问。 在将集群、用户和上下文定义在一个或多个配置文件中之后,用户可以使用 kubectl config use-context
命令快速地在集群之间进行切换。
kubeconfig
的文件。
Before you begin
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:
要检查 kubectl 是否安装,
执行 kubectl version --client
命令。
kubectl 的版本应该与集群的 API 服务器
使用同一次版本号。
定义集群、用户和上下文
假设用户有两个集群,一个用于正式开发工作,一个用于其它临时用途(scratch)。
在 development
集群中,前端开发者在名为 frontend
的名字空间下工作,
存储开发者在名为 storage
的名字空间下工作。 在 scratch
集群中,
开发人员可能在默认名字空间下工作,也可能视情况创建附加的名字空间。
访问开发集群需要通过证书进行认证。
访问其它临时用途的集群需要通过用户名和密码进行认证。
创建名为 config-exercise
的目录。 在
config-exercise
目录中,创建名为 config-demo
的文件,其内容为:
apiVersion: v1
kind: Config
preferences: {}
clusters:
- cluster:
name: development
- cluster:
name: scratch
users:
- name: developer
- name: experimenter
contexts:
- context:
name: dev-frontend
- context:
name: dev-storage
- context:
name: exp-scratch
配置文件描述了集群、用户名和上下文。config-demo
文件中含有描述两个集群、
两个用户和三个上下文的框架。
进入 config-exercise
目录。输入以下命令,将群集详细信息添加到配置文件中:
kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster scratch --server=https://5.6.7.8 --insecure-skip-tls-verify
将用户详细信息添加到配置文件中:
kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-seefile
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password
注意:
- 要删除用户,可以运行
kubectl --kubeconfig=config-demo config unset users.<name>
- 要删除集群,可以运行
kubectl --kubeconfig=config-demo config unset clusters.<name>
- 要删除上下文,可以运行
kubectl --kubeconfig=config-demo config unset contexts.<name>
将上下文详细信息添加到配置文件中:
kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-scratch --cluster=scratch --namespace=default --user=experimenter
打开 config-demo
文件查看添加的详细信息。 也可以使用 config view
命令进行查看:
kubectl config --kubeconfig=config-demo view
输出展示了两个集群、两个用户和三个上下文:
apiVersion: v1
clusters:
- cluster:
certificate-authority: fake-ca-file
server: https://1.2.3.4
name: development
- cluster:
insecure-skip-tls-verify: true
server: https://5.6.7.8
name: scratch
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
- context:
cluster: development
namespace: storage
user: developer
name: dev-storage
- context:
cluster: scratch
namespace: default
user: experimenter
name: exp-scratch
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
user:
client-certificate: fake-cert-file
client-key: fake-key-file
- name: experimenter
user:
password: some-password
username: exp
其中的 fake-ca-file
、fake-cert-file
和 fake-key-file
是证书文件路径名的占位符。
你需要更改这些值,使之对应你的环境中证书文件的实际路径名。
有时你可能希望在这里使用 BASE64 编码的数据而不是一个个独立的证书文件。
如果是这样,你需要在键名上添加 -data
后缀。例如,
certificate-authority-data
、client-certificate-data
和 client-key-data
。
每个上下文包含三部分(集群、用户和名字空间),例如,
dev-frontend
上下文表明:使用 developer
用户的凭证来访问 development
集群的
frontend
名字空间。
设置当前上下文:
kubectl config --kubeconfig=config-demo use-context dev-frontend
现在当输入 kubectl
命令时,相应动作会应用于 dev-frontend
上下文中所列的集群和名字空间,
同时,命令会使用 dev-frontend
上下文中所列用户的凭证。
使用 --minify
参数,来查看与当前上下文相关联的配置信息。
kubectl config --kubeconfig=config-demo view --minify
输出结果展示了 dev-frontend
上下文相关的配置信息:
apiVersion: v1
clusters:
- cluster:
certificate-authority: fake-ca-file
server: https://1.2.3.4
name: development
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
user:
client-certificate: fake-cert-file
client-key: fake-key-file
现在假设用户希望在其它临时用途集群中工作一段时间。
将当前上下文更改为 exp-scratch
:
kubectl config --kubeconfig=config-demo use-context exp-scratch
现在你发出的所有 kubectl
命令都将应用于 scratch
集群的默认名字空间。
同时,命令会使用 exp-scratch
上下文中所列用户的凭证。
查看更新后的当前上下文 exp-scratch
相关的配置:
kubectl config --kubeconfig=config-demo view --minify
最后,假设用户希望在 development
集群中的 storage
名字空间下工作一段时间。
将当前上下文更改为 dev-storage
:
kubectl config --kubeconfig=config-demo use-context dev-storage
查看更新后的当前上下文 dev-storage
相关的配置:
kubectl config --kubeconfig=config-demo view --minify
创建第二个配置文件
在 config-exercise
目录中,创建名为 config-demo-2
的文件,其中包含以下内容:
apiVersion: v1
kind: Config
preferences: {}
contexts:
- context:
cluster: development
namespace: ramp
user: developer
name: dev-ramp-up
上述配置文件定义了一个新的上下文,名为 dev-ramp-up
。
设置 KUBECONFIG 环境变量
查看是否有名为 KUBECONFIG
的环境变量。
如有,保存 KUBECONFIG
环境变量当前的值,以便稍后恢复。
例如:
Linux
export KUBECONFIG_SAVED=$KUBECONFIG
Windows PowerShell
$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG
KUBECONFIG
环境变量是配置文件路径的列表,该列表在 Linux 和 Mac 中以冒号分隔,
在 Windows 中以分号分隔。
如果有 KUBECONFIG
环境变量,请熟悉列表中的配置文件。
临时添加两条路径到 KUBECONFIG
环境变量中。 例如:
Linux
export KUBECONFIG=$KUBECONFIG:config-demo:config-demo-2
Windows PowerShell
$Env:KUBECONFIG=("config-demo;config-demo-2")
在 config-exercise
目录中输入以下命令:
kubectl config view
输出展示了 KUBECONFIG
环境变量中所列举的所有文件合并后的信息。
特别地,注意合并信息中包含来自 config-demo-2
文件的 dev-ramp-up
上下文和来自
config-demo
文件的三个上下文:
contexts:
- context:
cluster: development
namespace: frontend
user: developer
name: dev-frontend
- context:
cluster: development
namespace: ramp
user: developer
name: dev-ramp-up
- context:
cluster: development
namespace: storage
user: developer
name: dev-storage
- context:
cluster: scratch
namespace: default
user: experimenter
name: exp-scratch
关于 kubeconfig 文件如何合并的更多信息,请参考 使用 kubeconfig 文件组织集群访问
探索 $HOME/.kube 目录
如果用户已经拥有一个集群,可以使用 kubectl
与集群进行交互,
那么很可能在 $HOME/.kube
目录下有一个名为 config
的文件。
进入 $HOME/.kube
目录,看看那里有什么文件。通常会有一个名为
config
的文件,目录中可能还有其他配置文件。请简单地熟悉这些文件的内容。
将 $HOME/.kube/config 追加到 KUBECONFIG 环境变量中
如果有 $HOME/.kube/config
文件,并且还未列在 KUBECONFIG
环境变量中,
那么现在将它追加到 KUBECONFIG
环境变量中。
例如:
Linux
export KUBECONFIG=$KUBECONFIG:$HOME/.kube/config
Windows Powershell
$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME\.kube\config"
在配置练习目录中输入以下命令,查看当前 KUBECONFIG
环境变量中列举的所有文件合并后的配置信息:
kubectl config view
清理
将 KUBECONFIG
环境变量还原为原始值。 例如:
Linux
export KUBECONFIG=$KUBECONFIG_SAVED
Windows PowerShell
$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED