一、环境说明
系统centos7.6
k8s:用kubeadm安装上k8s见我的文章 《0基础自学linux运维-3.8-Centos7 kubeadm1.14搭建k8s群集》
master:192.168.3.176
node1:名字vm61 ip:192.168.3.61
node1:名字vm62 ip:192.168.3.62
二、StatefulSet理解
一个应用的所有 Pod,是完全一样的。所以,它们互相之间没有顺序,也无所谓运行在哪台宿主机上。需要的时候,Deployment 就可以通过 Pod 模板创建新的 Pod;不需要的时候,Deployment 就可以“杀掉”任意一个 Pod。
但是,在实际的场景中,并不是所有的应用都可以满足这样的要求。尤其是分布式应用,它的多个实例之间,往往有依赖关系,比如:主从关系、主备关系。
还有就是数据存储类应用,它的多个实例,往往都会在本地磁盘上保存一份数据。而这些实例一旦被杀掉,即便重建出来,实例与数据之间的对应关系也已经丢失,从而导致应用失败。
所以,这种实例之间有不对等关系,以及实例对外部数据有依赖关系的应用,就被称为“有状态应用”(Stateful Application)。
StatefulSet 的设计其实非常容易理解。它把真实世界里的应用状态,抽象为了两种情况:
1)拓扑状态:
这种情况意味着,应用的多个实例之间不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个 Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的 Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。
2)存储状态
这种情况意味着,应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A 第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间 Pod A 被重新创建过。这种情况最典型的例子,就是一个数据库应用的多个存储实例。
所以,StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。
三、建立Headless Service
Service被访问的2种方式:
1. 第一种方式,是以 Service 的 VIP(Virtual IP,即:虚拟 IP)方式
比如:当我访问 10.0.23.1 这个 Service 的 IP 地址时,10.0.23.1 其实就是一个 VIP,它会把请求转发到该 Service 所代理的某一个 Pod 上。这里的具体原理,我会在后续的 Service 章节中进行详细介绍。
2.而在第二种 Service DNS 的方式下,具体还可以分为两种处理方法:
1)第一种处理方法,是 Normal Service。这种情况下,你访问“my-svc.my-namespace.svc.cluster.local”解析到的,正是 my-svc 这个 Service 的 VIP,后面的流程就跟 VIP 方式一致了。
2)而第二种处理方法,正是 Headless Service。这种情况下,你访问“my-svc.my-namespace.svc.cluster.local”解析到的,直接就是 my-svc 代理的某一个 Pod 的 IP 地址。
所谓的 Headless Service,其实仍是一个标准 Service 的 YAML 文件。只不过,它的 clusterIP 字段的值是:None,即:这个 Service,没有一个 VIP 作为“头”。这也就是 Headless 的含义。所以,这个 Service 被创建后并不会被分配一个 VIP,而是会以 DNS 记录的方式暴露出它所代理的 Pod。
3.1 建立Headless Service
#即clusterIP: None的Service
#编写部署(master节点)注意yaml空格要严格对,
#本来是用mysql主从的,为了方便使用nginx,熟悉之后再改为mysql主从
mkdir -pv /disk1/myk8s
cd /disk1/myk8s/
cd /disk1/myk8s/
cat>nginx-ser-none.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-ser-none
labels:
app: nginx-ser-none
spec:
selector:
app: nginx2
ports:
- port: 80
targetPort: 82
# 这个名字需要
name: web
# nodePort: 30082
# clusterIP: None 时,不能用 nodePort属性
clusterIP: None
EOF
kubectl apply -f nginx-ser-none.yaml --record
注:
targetPort: 80,这样就会找取“app=nginx”且端口为80的pod
Service中的clusterIP: 一定要为None,即clusterIP: None
当你按照这样的方式创建了一个 Headless Service 之后,它所代理的所有 Pod 的 IP 地址,都会被绑定一个这样格式的 DNS 记录,如下所示:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
3.2 建立StatefulSet的yaml
在这里我不用Deployment的RS(Replicas)我这里用StatefulSet
#node vm61上操作
mkdir -pv /disk1/www/t1
echo "vm61 index.html" >/disk1/www/t1/index.html
#node vm62上操作:
mkdir -pv /disk1/www/t1
echo "vm62 index.html" >/disk1/www/t1/index.html
#在master端操作
cat>nginx-statefulset.yaml<<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx-ser-none"
replicas: 3
selector:
matchLabels:
app: nginx2
template:
metadata:
labels:
app: nginx2
spec:
containers:
- name: nginx2
image: nginx:1.15.12
ports:
- containerPort: 82
name: nginx2
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /disk1/www/t1
EOF
#--record 的 flag 设置为 true可以在 annotation 中记录当前命令创建或者升级了该资源
#我推荐你使用 kubectl apply 命令,来统一进行 Kubernetes 对象的创建和更新操作
#kubectl create -f nginx-statefulset.yaml --record
kubectl apply -f nginx-statefulset.yaml --record
#因为没用到部署和无状态的rs,所以为空
[root@vm176 myk8s]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 39s
web-1 1/1 Running 0 31s
web-2 1/1 Running 0 26s
[root@vm176 myk8s]# kubectl get deployments
No resources found.
[root@vm176 myk8s]# kubectl get rs
No resources found.
[root@vm176 myk8s]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web-0 1/1 Running 0 44s app=nginx,controller-revision-hash=web-6bc58d48c5,statefulset.kubernetes.io/pod-name=web-0
web-1 1/1 Running 0 36s app=nginx,controller-revision-hash=web-6bc58d48c5,statefulset.kubernetes.io/pod-name=web-1
web-2 1/1 Running 0 31s app=nginx,controller-revision-hash=web-6bc58d48c5,statefulset.kubernetes.io/pod-name=web-2
[root@vm176 disk1]# kubectl get statefulsets
NAME READY AGE
web 3/3 5m15s
3.3 更新nginx版本
#根据hub.docker.com搜索nginx知道最新版本为1.16.0
#也可以使用 kubectl edit deployment/nginx-deployment
kubectl set image statefulsets/web nginx=nginx:1.16
#如果是用apply命令创建的,可以直接修改nginx-deployment.yaml,再执行apply -f生效
sed -i '/nginx:1.15.12/s/1.15.12/1.16/' nginx-statefulset.yaml
grep 'nginx:' nginx-statefulset.yaml
kubectl apply -f nginx-statefulset.yaml
#按ctrl+c退出
[root@vm176 myk8s]# kubectl get pods -w -l app=nginx2
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m7s
web-1 1/1 Running 0 4m20s
web-2 1/1 Running 0 4m27s
四、测试statefulSet
4.1查看一下主机名
[root@vm176 myk8s]# kubectl exec web-0 hostname
web-0
[root@vm176 myk8s]# kubectl exec web-1 hostname
web-1
[root@vm176 myk8s]# kubectl exec web-2 hostname
web-2
##看到没有,web-0、web-1、web-2永远是固定不变的,hostname永远不变,只有IP才变
##所以不能通过IP访问,只能通过DNS
4.2 分析pod dns
#用nslookup测试一下
yum install bind-utils -y
#尝试直接使用主机名访问
[root@vm176 myk8s]# nslookup web-0
Server: 192.168.128.2
Address: 192.168.128.2#53
** server can't find web-0: REFUSED
上面分析其中192.168.128.2是服务器的DNS,我这里用的是网关,所以跑出网关去查找这个主机名了,明显没有找到的
[root@vm176 myk8s]# kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
nslookup: can't resolve 'web-0'
容器内访问也不行,查看官网pod dns规则
得知:以下ns为Namespace意思
Service (ns/name):default/nginx-ser-none
StatefulSet (ns/name):default/web
Hostname:web-{0..N-1}我这里只查看web-0
StatefulSet Domain格式为
<svc-name>.<namespace>.svc.cluster.local
Pod DNS Pod格式为:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
例如web-0
web-0.nginx-ser-none.default.svc.cluster.local
#查看 nslookup <svc-name>
#从上面知道访问web-0的pod dns为web-0.nginx-ser-none.default.svc.cluster.local
可以通过内部访问nginx,内部端口为80(虽然我指定了容器端口为82,但是还是默认用80)
我这里我们可以理解成,用statefulset建立的容器可以通过一个容器内部访问到它
我这里我建立一个nginx容器,statefulset建立的web-0、web-1、web-2做后端服务器,nginx做前端
4.3 用deployment建立一个容器,为了访问我这里rs用2个
#分别在vm61和vm62执行
mkdir -pv /disk1/k8sConf/nginx-lb
cd /disk1/myk8s/
cat>nginx-deployment.yaml<<EOF
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16
ports:
- containerPort: 80
EOF
#创建服务
cat>nginx-ser.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-ser
labels:
app: nginx-ser
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
#port of pod
targetPort: 80
#nodePort: 30080
clusterIP: 10.100.0.80
externalIPs:
- 192.168.3.176
EOF
kubectl apply -f nginx-deployment.yaml --record
kubectl apply -f nginx-ser.yaml --record
#查看是否下载完
[root@vm176 myk8s]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-66f9f9cfd5-stp74 1/1 Running 0 2m36s
nginx-deployment-66f9f9cfd5-tlh84 1/1 Running 0 2m36s
web-0 1/1 Running 0 48m
web-1 1/1 Running 0 40m
web-2 1/1 Running 0 39m
[root@vm176 myk8s]#
curl -s 10.100.0.80
#修改nginx配置,做成前端代理方式
kubectl exec nginx-deployment-66f9f9cfd5-stp74 cat /etc/nginx/conf.d/default.conf >>default.conf
vim default.conf
#修改成如下格式
upstream k8s_ngx {
server web-0.nginx-ser-none.default.svc.cluster.local:80 weight=1;
server web-1.nginx-ser-none.default.svc.cluster.local:80 weight=1;
server web-2.nginx-ser-none.default.svc.cluster.local:80 weight=1;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://k8s_ngx;
}
...
}
#把刚才设置好的nginx配置文件分别弄到vm61和vm62中的disk1/k8sConf/nginx-lb目录下
scp default.conf root@192.168.3.61:/disk1/k8sConf/nginx-lb
scp default.conf root@192.168.3.62:/disk1/k8sConf/nginx-lb
#修改一下nginx-deployment.yaml文件
cat>nginx-deployment.yaml<<EOF
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/conf.d/
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /disk1/k8sConf/nginx-lb
EOF
#应用修改
kubectl apply -f nginx-deployment.yaml
#打开浏览器访问,不断按F5刷新发现在
http://192.168.3.176
#在master主机上访问
[root@vm61 nginx-lb]# curl 10.100.0.80
vm61 index.html
[root@vm61 nginx-lb]# curl 10.100.0.80
vm61 index.html
[root@vm61 nginx-lb]# curl 10.100.0.80
vm62 index.html
说明成功了
网友评论