最近在看kubernetes的相关内空,参考《Kubernetes权威指南》第一章,搭建一个简单的应用,它里面使用的是RC,我直接使用RS来搭建。

项目架构

我们通过kubernetes来部署一个应用,这个应用后台是一个php网站,数据库使用redis,redis采用一主两从的部署方式,做到读写分离,写操作走redis主库,读操作走reids从库。并且启动多个应用副本,达到负载均衡,总体的应用架构如下图

创建redis-master 服务

老版本的kubernetes使用RC(ReplicationController)来创建pod,但是随着RS(ReplicationSet)的出现,已经取代了RC,所以我们直接采用RS来创建pod,创建redis-master.yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: redis-master
labels:
name: redis-master
spec:
replicas: 1
selector:
matchLabels:
name: redis-master
template:
metadata:
labels:
name: redis-master
spec:
containers:
- name: r-master
image: kubeguide/redis-master
ports:
- containerPort: 6379

yaml文件的编写很是麻烦,尤其是apiVersion: apps/v1 的选择,不同的apiVersion 对应下面的指令不同,写好的yaml文件可以在 https://kubeyaml.com/ 这个网站上校验一下。

通过 kubectl create -f redis-master.yaml 命令创建ReplicaSet, 使用 kubectl get pod 来查看pod的创建情况

1
2
3
# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-master-n6wbp 0/1 ContainerCreating 0 11s

过一会就可以创建成功了,过程中通过status查看状态,有时会创建失败,失败的原因很多,遇到到在网上搜索一下吧。到时候再进行总结。

创建完pod以后,如果没有创建与之对应的service,则该pod也无法正常工作,接下来先创建这个service

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
name: redis-master
spec:
ports:
- port: 6379
targetPort: 6379
selector:
name: redis-master

使用 kubectl create -f redis-master-service.yaml 创建service,使用kubectl get services 查看service状态。

1
2
3
4
5
6
# kubectl create -f redis-master-service.yaml 
service/redis-master created
# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
redis-master ClusterIP 10.109.162.197 <none> 6739/TCP 10s

看到redis-master使用了10.109.162.197 这个虚拟的ip,之后创建的pod可以使用这个ip和端口访问这个redis服务。

通过kubernetes创建的pod,虚拟IP是动态的,如果重启以后可能就会变了,所以其它的服务如redis-slave要同步的话是不能使用这个虚拟IP的,kubernetes通过环境变量来记录从服务到虚拟IP的关系。

创建redis-slave 服务

创建redis-slave.yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
replicas: 2 #创建两个副本
selector:
matchLabels:
name: redis-slave
template:
metadata:
labels:
name: redis-slave
spec:
containers:
- name: r-slave
image: kubeguide/guestbook-redis-slave
env: #添加环境变量
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 6379
1
2
3
4
5
6
7
# kubectl create -f redis-slave.yaml 
replicaset.apps/redis-slave created
# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-master-n6wbp 1/1 Running 0 37m
redis-slave-7zj5x 0/1 ContainerCreating 0 12s
redis-slave-hp2pt 0/1 ContainerCreating 0 12s

我们来查看一下redis-slave中的环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-master-n6wbp 1/1 Running 0 39m
redis-slave-7zj5x 1/1 Running 0 2m7s
redis-slave-hp2pt 1/1 Running 0 2m7s
root@kubernetes-master:/usr/local/docker/kubernetes/yaml/phpredis# kubectl exec redis-slave-7zj5x env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=redis-slave-7zj5x
GET_HOSTS_FROM=env
REDIS_MASTER_SERVICE_HOST=10.109.162.197
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT_6739_TCP=tcp://10.109.162.197:6739
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
REDIS_MASTER_PORT_6739_TCP_PORT=6739
REDIS_MASTER_PORT_6739_TCP_ADDR=10.109.162.197
KUBERNETES_SERVICE_PORT=443
REDIS_MASTER_PORT_6739_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
REDIS_MASTER_PORT=tcp://10.109.162.197:6739
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
REDIS_VERSION=3.0.3
REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.0.3.tar.gz
REDIS_DOWNLOAD_SHA1=0e2d7707327986ae652df717059354b358b83358
HOME=/root

可以看到

1
2
REDIS_MASTER_SERVICE_HOST=10.109.162.197
REDIS_MASTER_SERVICE_PORT=6379

可以通过这两个环境变量来动态获取上面redis-master的IP与PORT

然后再创建与redis-slave对应的service服务

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
ports:
- port: 6379
targetPort: 6379
selector:
name: redis-slave

通过kubectl get service 看到现在已经有三个服务了。

1
2
3
4
5
# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
redis-master ClusterIP 10.109.162.197 <none> 6379/TCP 31m
redis-slave ClusterIP 10.104.211.17 <none> 6379/TCP 18s

创建PHP应用

yaml文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: phpserver
labels:
name: phpserver
spec:
replicas: 3
selector:
matchLabels:
name: phpserver
template:
metadata:
labels:
name: phpserver
spec:
containers:
- name: phpfront
image: kubeguide/guestbook-php-frontend
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 80

创建pod,并查看pod状态

1
2
3
4
5
6
7
8
9
10
# kubectl create -f phpfront.yaml 
replicaset.apps/phpserver created
# kubectl get pods
NAME READY STATUS RESTARTS AGE
phpserver-5bwzh 0/1 ContainerCreating 0 7s
phpserver-6v4dd 0/1 ContainerCreating 0 7s
phpserver-dbdmn 0/1 ContainerCreating 0 7s
redis-master-n6wbp 1/1 Running 0 53m
redis-slave-7zj5x 1/1 Running 0 16m
redis-slave-hp2pt 1/1 Running 0 16m

创建相应的service

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: phpservice
labels:
name: phpservice
spec:
type: NodePort
ports:
- port: 80
nodePort: 30001
selector:
name: phpserver

运行创建命令并且查看services

1
2
3
4
5
6
7
8
# kubectl create -f phpservice.yaml 
service/phpservice created
# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
phpservice NodePort 10.100.94.114 <none> 80:30001/TCP 9s
redis-master ClusterIP 10.109.162.197 <none> 6739/TCP 45m
redis-slave ClusterIP 10.104.211.17 <none> 6739/TCP 14m

注意到phpservice的type为NodePort,ports为80:30001/TCP,则这时可以通过主机的30001端口来访问该应用

访问 http://192.168.206.128:30001/ 来访问应用

应用更新

应用回滚

动态调整资源

可以手工的调整pod资源数量,如网站今天要做活动,预料到流量会增加,需要增加一些副本,则可以使用

kubectl scale rs rsname --replicas=4 命令来改变副本的数量,–replicas 值为要修改的量,当活动结束以后再手工的改回来。

当然对于这种纯手工的来修改副本数,还是不够智能,我们真正希望的是,当一个网站流量变大的时候可以自动的扩容,当网站的流量减少的时候,可以自动的缩容。

这个可以通过 HPA 来实现

一些问题

  1. 环境变量是怎么传递的

  2. spec.type的类型

    Kubernetes的三种外部访问方式