目录
无状态
在Kubernetes(k8s)中,"无状态"通常指的是应用程序的设计方式,其中应用程序实例之间的状态不共享,并且可以在需要时水平扩展。无状态应用程序不依赖于本地存储或会话信息,使它们更容易部署、扩展和管理。在Kubernetes中,无状态应用程序可以通过部署多个相同的副本,以实现负载均衡和高可用性。
以下是在Kubernetes中创建无状态应用程序的一般步骤和一些相关的概念:
-
容器化应用程序: 首先,将应用程序容器化,将应用程序和其依赖项打包到容器中。这通常涉及创建一个Docker镜像。
-
声明性配置: 使用Kubernetes的声明性配置,定义应用程序的规范,包括所需的容器镜像、副本数等。这可以通过YAML文件完成。
-
副本集(ReplicaSet): 通过创建一个ReplicaSet对象,指定所需的副本数。ReplicaSet确保在集群中运行指定数量的副本,并在发生故障时进行自动修复。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: example-app
spec:
replicas: 3
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
containers:
- name: app-container
image: example-image:latest -
服务(Service): 为无状态应用程序创建一个Service,它为副本集提供一个稳定的网络终结点。Service允许其他应用程序通过Service的虚拟IP和端口与应用程序通信。
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- protocol: TCP
port: 80
targetPort: 8080 -
水平扩展: 通过更改ReplicaSet中的副本数,可以水平扩展应用程序,以适应不同的负载。Kubernetes会自动管理副本的创建和销毁。
通过这些步骤,你可以在Kubernetes中创建一个无状态应用程序,它可以以水平扩展的方式运行,并通过服务提供稳定的网络终结点。这种设计使得应用程序更容易维护和扩展,同时提供高可用性。
有状态(StatefulSet)
介绍
出现这种问题的原因可能是由于两种不同的环境或网络配置所致。让我们分析一下可能的原因以及如何解决这个问题:
-
DNS配置不同:
-
•
在第一个命令中,您在Kubernetes主机上运行了nslookup命令,并且DNS服务器配置正确,所以它成功解析了 "web-1.nginx.default.svc.cluster.local"。
-
•
在第二个命令中,您在一个BusyBox容器内运行了nslookup命令,该容器可能没有正确配置的DNS服务器信息,或者可能没有与Kubernetes集群内部DNS服务器通信的网络访问权限。
-
-
网络问题:
-
•
确保BusyBox容器内部的网络配置是正确的,能够与Kubernetes集群的DNS服务器进行通信。如果容器无法访问Kubernetes DNS服务器,它将无法解析服务名称。
-
•
您可以尝试运行
nslookup
命令时指定DNS服务器的IP地址,例如:nslookup web-1.nginx.default.svc.cluster.local 10.96.0.10
,这将明确指定DNS服务器来解析名称,以确保DNS服务器的正确配置。
-
-
DNS缓存问题:
-
•
如果在BusyBox容器内部多次运行
nslookup
并且之前有解析失败的尝试,可能会出现DNS缓存问题。您可以尝试清除DNS缓存,然后再次运行nslookup
。
-
-
Service名称错误:
-
•
确保Service的名称 "nginx" 和命令中的名称匹配。检查是否有任何拼写错误或大小写问题。
-
-
Kubernetes集群问题:
-
•
检查Kubernetes集群的状态,确保所有组件都在正常运行。DNS解析依赖于Kubernetes DNS服务的正常工作。
-
请首先检查上述问题,特别是在容器内部的网络和DNS配置。如果问题仍然存在,请提供更多有关您的环境配置和日志信息,以便更深入地分析和解决问题。
应用
创建web.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
这是一个Kubernetes配置文件,用于创建一个Service和一个StatefulSet,这两者都用于部署和管理NGINX应用。以下是对配置文件中各部分的详细解释:
Service部分:
•
apiVersion: v1
和kind: Service
表明这是一个Kubernetes Service 资源。•
metadata
包含有关Service的元数据,包括名称和标签。•
name: nginx
定义了Service的名称为 "nginx"。•
labels
定义了标签,其中app: nginx
将该Service与后面的StatefulSet关联起来。•
spec
定义了Service的规格。
•
ports
定义了Service监听的端口,这里监听端口80,并且命名为 "web"。•
clusterIP: None
意味着这是一个Headless Service,不会分配Cluster IP。
StatefulSet部分:
•
apiVersion: apps/v1
和kind: StatefulSet
表明这是一个Kubernetes StatefulSet 资源。•
metadata
包含有关StatefulSet的元数据,包括名称。•
name: web
定义了StatefulSet的名称为 "web"。•
spec
定义了StatefulSet的规格。
•
serviceName: "nginx"
指定了StatefulSet关联的Service的名称,这将允许StatefulSet的Pods在DNS中使用 "nginx" 作为服务名。•
replicas: 3
指定了要创建的Pod副本数,这里是3个。•
selector
指定了用于选择要管理的Pod的标签。
•
matchLabels
定义了标签选择器,它与Service的标签匹配,以确保StatefulSet管理的是Service的Pod。•
template
定义了要创建的Pod的模板。
•
metadata
包含有关Pod模板的元数据,包括标签。
•
labels
标签与Service的标签匹配,以确保Pod与Service关联。•
spec
定义了Pod的规格。
•
containers
定义了容器列表。
•
name: nginx
定义了容器的名称为 "nginx"。•
image: nginx
指定要使用的NGINX镜像。•
imagePullPolicy: IfNotPresent
意味着在本地不存在NGINX镜像时才拉取。•
ports
定义了容器监听的端口,这里监听端口80,并且命名为 "web"。这个配置文件创建了一个Headless Service(Cluster IP为None)以及一个StatefulSet,该StatefulSet使用NGINX容器创建3个Pod,并将它们关联到该Service上,以便通过Service名称 "nginx" 在集群内部访问这些Pod。这种设置通常用于需要有状态应用程序,因为StatefulSet可以为每个Pod分配唯一的标识,并提供有序的部署和伸缩机制。
[root@k8smaster Stateful]# kubectl apply -f web.yaml
[root@k8smaster Stateful]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 81s
web-1 1/1 Running 0 79s
web-2 1/1 Running 0 76s
[root@k8smaster Stateful]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d22h
mynginx NodePort 10.103.164.206 <none> 80:30009/TCP 2d18h
mysql-service NodePort 10.109.26.85 <none> 3360:30008/TCP 2d18h
nginx ClusterIP None <none> 80/TCP 94s
[root@k8smaster Stateful]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-6949477b58-nwmr2 1/1 Running 0 2d19h
calico-node-5jq26 1/1 Running 0 2d19h
calico-node-lk4zv 1/1 Running 0 2d19h
calico-node-n4742 1/1 Running 0 2d19h
coredns-7f89b7bc75-7zdk5 1/1 Running 0 2d22h
查看 **endpoint**
[root@k8smaster Stateful]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.2.210:6443 2d22h
mynginx <none> 2d18h
mysql-service <none> 2d18h
nginx 10.244.185.197:80,10.244.249.4:80,10.244.249.5:80 8m37s
进入ngixn里
[root@k8smaster Stateful]# kubectl exec -it web-1 -- bash
root@web-1:/# curl web-1.nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
下载bind-utils
yum install bind-utils -y
[root@k8smaster Stateful]# nslookup web-1.nginx.default.svc.cluster.local 10.96.0.10Server: 10.96.0.10
Address: 10.96.0.10#53
Name: web-1.nginx.default.svc.cluster.local
Address: 10.244.249.4
使用 kubectl run
运行一个提供 nslookup
命令的容器,该命令来自于 dnsutils
包。 通过对 Pod 的主机名执行 nslookup
,你可以检查这些主机名在集群内部的 DNS 地址:
kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
这将启动一个新的 Shell。在新 Shell 中运行:
# 在 dns-test 容器 Shell 中运行以下命令
nslookup web-0.nginx
输出类似于:
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.1.6
nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.6
(现在可以退出容器 Shell:exit
)
应用2
以一个简单的nginx服务web.yaml为例:
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx # 将StatefulSet的名称改为"nginx"
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx # 添加选择器以匹配Pod的标签
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
[root@k8smaster service]# kubectl apply -f statefulsets.yaml
service/nginx unchanged
statefulset.apps/nginx created
# 查看创建的headless service和statefulset
[root@k8smaster service]# kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 71m
1. 状态持久性:
-
•
无状态应用程序: 无状态应用程序通常不需要持久性的存储。它们的每个实例都是相互独立的,可以在需要时动态创建和销毁。无状态应用程序不依赖于本地状态或会话信息。
-
•
有状态应用程序: 有状态应用程序需要维护一些持久性的状态或数据,例如数据库。这种状态通常需要在容器重新调度或故障时保持不变。
2. 水平扩展:
-
•
无状态应用程序: 无状态应用程序可以很容易地进行水平扩展,通过增加或减少实例的数量来处理负载。每个实例都是相同的,可以随意添加或删除。
-
•
有状态应用程序: 有状态应用程序的水平扩展可能更为复杂,因为每个实例可能会维护特定的状态。在水平扩展时,需要考虑如何处理和同步状态,以确保应用程序的一致性。
3. 存储和卷:
-
•
无状态应用程序: 通常,无状态应用程序可以使用临时存储,而无需关心数据的持久性。Kubernetes可以随时重新创建实例,而不会丢失重要数据。
-
•
有状态应用程序: 有状态应用程序通常需要使用持久卷(Persistent Volumes)来存储和保护其状态。这确保了即使实例重新调度,应用程序仍然可以访问其之前的状态。
4. Pod标识和网络标识:
-
•
无状态应用程序: 无状态应用程序通常可以使用随机的Pod标识符,因为它们的实例之间是相互独立的。它们依赖于服务发现机制来实现负载均衡。
-
•
有状态应用程序: 有状态应用程序通常需要稳定的网络标识符和主机名,以确保它们在重新调度或扩展时能够保持一致的网络标识。
5. 部署和更新:
-
•
无状态应用程序: 无状态应用程序的部署和更新通常更加简单,因为新的实例可以替代旧的实例而不会影响整体系统的一致性。
-
•
有状态应用程序: 有状态应用程序的部署和更新可能需要更复杂的策略,以确保状态的平滑过渡,例如滚动更新和分阶段部署。
总体而言,选择使用有状态或无状态应用程序取决于应用程序的要求和特性。有状态应用程序通常更适合于需要维护持久状态或数据的情况,而无状态应用程序则更适合于可以水平扩展和无状态实例之间相互替换的情况。
有状态和无状态对比
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计)
在Kubernetes(k8s)中,有状态应用程序和无状态应用程序在设计和管理上存在一些关键的区别。下面是有状态和无状态应用程序的详细对比:
1. 概念:
有状态应用程序(Stateful Applications):
- 有状态应用程序通常需要在其生命周期内维护和管理持久状态,例如数据库。
- 这类应用程序对于节点之间的状态保持和标识是敏感的,通常需要唯一标识符来管理其状态。
- 有状态应用程序的实例通常被认为是持久性的,它们拥有独特的标识符,比如Pod名称。
无状态应用程序(Stateless Applications):
- 无状态应用程序不依赖于在其运行时维护的任何持久状态。
- 这类应用程序的实例是可以互相替代的,因为它们的状态不会影响系统的整体运行。
- 无状态应用程序通常更容易水平扩展,因为它们的实例之间是相互独立的。
2. 数据持久性:
有状态应用程序:
- 需要对数据进行持久化存储,通常使用持久卷(Persistent Volumes)或其他持久化存储解决方案。
- 数据通常存储在外部数据库中,而Pod实例只是应用程序的前端。
无状态应用程序:
- 不需要在本地持久化存储数据,通常将数据存储在外部数据存储或数据库中。
3. 扩展性:
有状态应用程序:
- 扩展有状态应用程序可能更具挑战性,因为每个实例需要维护自己的状态,并且可能需要处理数据同步等问题。
无状态应用程序:
- 由于无状态应用程序的实例是相互独立的,它们通常更容易水平扩展,可以通过增加实例数量来应对负载增加。
4. 管理和部署:
有状态应用程序:
- 部署和管理有状态应用程序可能需要更多的关注和复杂性,因为需要确保状态的一致性和可靠性。
无状态应用程序:
- 无状态应用程序通常更容易部署和管理,因为它们的实例可以相对容易地替代彼此。
5. 故障恢复:
有状态应用程序:
- 故障恢复可能需要更多的关注,因为在一个节点故障时,需要确保状态的无缝切换。
无状态应用程序:
- 由于无状态应用程序的实例是相互独立的,故障恢复通常更为简单,新的实例可以替代故障的实例。
在选择有状态或无状态应用程序时,需要考虑应用程序的特性、性能需求和运维复杂性。有时候,应用程序可能会包含既有状态的组件又有无状态的组件,这需要综合考虑和合理设计。