介绍
本文主要介绍如何搭建一个由三个微服务组成的留言板(Guestbook)系统的搭建,对Kubenetes对容器应用的基本操作和用法进行初步介绍。进一步深入Pod、RC、Service等核心对象的用法和技巧,对Kubernetes的应用管理进行全面讲解。
Guestbook留言板系统将通过Pod、RC、Service等资源对象搭建完成,成功启动后再网页中显示一条“Hello World”留言。其系统架构是一个基于PHP+Redis的分布式Web应用,前端PHP Web网站通过访问后端的Redis来完成用户留言的查询和添加等功能。同时Redis以Master+Slave的模式进行部署,实现数据的读写分离能力。
此教程不涉及docker镜像创建相关操作,都是基于已经创建好的docker镜像进行操作,主要目的是让大家学习使用kubenetes。
创建redis-master RC和Service
首先为Redis服务创建一个RC定义文件:redis-master-rc.yaml,下面给出了改文件的完成内容和解释:
apiVersion: v1
kind: ReplicationController # 副本控制器RC
metadata:
name: redis-master
labels:
name: redis-master
spec:
replicas: 1
selector:
name: redis-master
template:
metadata:
labels:
name: redis-master # 标签名称
spec:
containers:
- name: master
image: kubeguide/redis-master
ports:
- containerPort: 6379 # 容器暴露的端口号
执行该文件:
kubectl create -f redis-master-rc.yaml
首次执行需要下载docker镜像,需要等待容器启动
接下来,我们用kubectl命令查看刚刚创建的RC:
[root@bigman-m2 config]# kubectl get rc
NAME DESIRED CURRENT READY AGE
redis-master 1 1 1 90s
使用kubectl命令查看刚刚创建的Pod:
[root@cloud01 helloworld.ex]# kubectl create -f redis-master-rc.yaml
replicationcontroller/redis-master created
[root@cloud01 helloworld.ex]# kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 1 40m
myweb 5 5 5 31m
redis-master 1 1 0 11s
[root@cloud01 helloworld.ex]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-c2vtt 1/1 Running 0 40m
myweb-lb9ck 1/1 Running 0 31m
myweb-ljrjz 1/1 Running 0 31m
myweb-r4dx4 1/1 Running 0 31m
myweb-r7gjf 1/1 Running 0 31m
myweb-vz2hc 1/1 Running 0 31m
redis-master-zrk7c 0/1 ContainerCreating 0 18s
[root@cloud01 helloworld.ex]# kubectl logs redis-master-zrk7c
[1] 06 Jul 23:06:00.181 * Redis 2.8.19 (00000000/0) 64 bit, stand alone mode, port 6379, pid 1 ready to start.
[1] 06 Jul 23:06:00.181 # Server started, Redis version 2.8.19
[1] 06 Jul 23:06:00.181 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
[1] 06 Jul 23:06:00.181 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
[1] 06 Jul 23:06:00.181 * DB loaded from append only file: 0.000 seconds
[1] 06 Jul 23:06:00.181 * The server is now ready to accept connections on port 6379
[1] 06 Jul 23:06:00.181 - DB 0: 1 keys (0 volatile) in 4 slots HT.
[1] 06 Jul 23:06:00.181 - 0 clients connected (0 slaves), 752320 bytes in use
后面我们创建一个与之关联的Kubernetes Service的定义文件redis-master-service.yaml :
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
name: redis-master
spec:
selector:
name: redis-master
ports:
- port: 6379 # Service提供服务的端口号
targetPort: 6379 # 指后端Pod内容器应用监听的端口号
执行该文件:
kubectl create -f redis-master-service.yaml
运行kubectl命令,查看刚刚创建的service
[root@bigman-m2 config]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-master ClusterIP 10.108.233.43 <none> 6379/TCP 8s
注意到redis-master服务被分配了一个值为10.108.233.43的虚拟IP地址,随后kubernetes集群中其他新创建的Pod就可以通过10.108.233.43+端口6379来访问这个服务了。
当一个Pod运行在Node上时,kubelet将为每一个活动的Service添加环境变量,环境变量有两类:
DockerLink 环境变量:相当于Docker的–link参数实现容器连接时设置的环境变量。
Kubernetes Service环境变量:Kubernetes为Service设置的环境变量形式,{SVCNAME}_SERVICE_HOST 和{SVCNAME}_SERVICE_PORT变量,环境变量的名称为大写字母和下划线。
例如:存在一个名称为“redies-master”的Service(它的cluster ip地址为10.108.233.43,端口号为6379,协议为TCP),它的环境变量如下:
[root@cloud01 guestbook.ex]# kubectl exec -it redis-master-zrk7c /bin/sh
# ls
appendonly.aof dump.rdb
# printenv
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=redis-master-zrk7c
MYWEB_SERVICE_HOST=10.101.248.174
MYWEB_PORT_8080_TCP_ADDR=10.101.248.174
HOME=/root
MYWEB_PORT_8080_TCP_PORT=8080
MYWEB_PORT_8080_TCP_PROTO=tcp
MYWEB_SERVICE_PORT=8080
MYWEB_PORT=tcp://10.101.248.174:8080
MYWEB_PORT_8080_TCP=tcp://10.101.248.174:8080
TERM=xterm
MYSQL_PORT_3306_TCP_ADDR=10.103.168.75
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PROTO=tcp
MYSQL_SERVICE_HOST=10.103.168.75
MYSQL_SERVICE_PORT=3306
MYSQL_PORT=tcp://10.103.168.75:3306
MYSQL_PORT_3306_TCP=tcp://10.103.168.75:3306
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/data
#
创建redis-slave RC和Service
现在我们已经成功启动了redis-master服务,接下来我们继续完成redis-slave服务的创建过程。在本案例中会启动redis-slave服务的两个副本,每个副本上的redis进程斗鱼redis-master进行数据同步,与redis-master共同组成了一个具备读写分离能力的redis集群。
首先为MySQL服务创建一个RC定义文件:redis-slave-rc.yaml,下面给出了改文件的完成内容和解释:
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
replicas: 2
selector:
name: redis-slave
template:
metadata:
labels:
name: redis-slave
spec:
containers:
- name: slave
image: kubeguide/guestbook-redis-slave
env:
- name: GET_HOSTS_FROM # 表示从环境变量中取redis-master服务的ip地址
value: env
ports:
- containerPort: 6379
redis-slave镜像启动脚本run.sh中使用了GET_HOSTS_FROM变量:
if [[ ${GET_HOSTS_FROM:-dns} == "env" ]]; then
redis-server --slaveof ${REDIS_MASTER_SERVICE_HOST} 6379
else
redis-server --slaveof redis-master 6379
fi
执行该文件:
kubectl create -f redis-slave-rc.yaml
首次执行需要下载docker镜像,需要等待容器启动
接下来,我们用kubectl命令查看刚刚创建的RC:
[root@bigman-m2 config]# kubectl get rc
NAME DESIRED CURRENT READY AGE
redis-master 1 1 1 36m
redis-slave 2 2 2 106s
使用kubectl命令查看刚刚创建的Pod:
[root@bigman-m2 config]# kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-master-lknwx 1/1 Running 0 36m
redis-slave-2sdc8 1/1 Running 0 68s
redis-slave-bkjhk 1/1 Running 0 68s
后面我们创建一个与之关联的Kubernetes Service的定义文件redis-slave-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
selector:
name: redis-slave
ports:
- port: 6379
执行该文件:
kubectl create -f redis-slave-service.yaml
运行kubectl命令,查看刚刚创建的service
[root@cloud01 guestbook.ex]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11h
mysql ClusterIP 10.103.168.75 <none> 3306/TCP 55m
myweb NodePort 10.101.248.174 <none> 8080:30002/TCP 50m
redis-master ClusterIP 10.107.164.188 <none> 6379/TCP 13m
redis-slave ClusterIP 10.96.24.17 <none> 6379/TCP 11s
[root@cloud01 guestbook.ex]#
[root@cloud01 guestbook.ex]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-c2vtt 1/1 Running 0 64m
myweb-lb9ck 1/1 Running 0 55m
myweb-ljrjz 1/1 Running 0 55m
myweb-r4dx4 1/1 Running 0 55m
myweb-r7gjf 1/1 Running 0 55m
myweb-vz2hc 1/1 Running 0 55m
redis-master-zrk7c 1/1 Running 0 24m
redis-slave-jppx6 1/1 Running 0 3m40s
redis-slave-vn4zg 1/1 Running 0 3m40s
[root@cloud01 guestbook.ex]# kubectl exec -it redis-slave-vn4zg /bin/bash
root@redis-slave-vn4zg:/data# printenv
MYWEB_PORT_8080_TCP_PORT=8080
HOSTNAME=redis-slave-vn4zg
REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.0.3.tar.gz
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
TERM=xterm
MYWEB_PORT_8080_TCP_ADDR=10.101.248.174
KUBERNETES_SERVICE_PORT=443
MYWEB_PORT_8080_TCP_PROTO=tcp
KUBERNETES_SERVICE_HOST=10.96.0.1
GET_HOSTS_FROM=env
REDIS_MASTER_PORT_6379_TCP_ADDR=10.107.164.188
MYWEB_SERVICE_HOST=10.101.248.174
MYSQL_PORT_3306_TCP_PORT=3306
REDIS_MASTER_PORT_6379_TCP=tcp://10.107.164.188:6379
MYSQL_PORT_3306_TCP=tcp://10.103.168.75:3306
MYWEB_PORT_8080_TCP=tcp://10.101.248.174:8080
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
REDIS_MASTER_SERVICE_PORT=6379
PWD=/data
MYWEB_SERVICE_PORT=8080
REDIS_MASTER_SERVICE_HOST=10.107.164.188
SHLVL=1
HOME=/root
KUBERNETES_PORT_443_TCP_PROTO=tcp
REDIS_DOWNLOAD_SHA1=0e2d7707327986ae652df717059354b358b83358
REDIS_VERSION=3.0.3
KUBERNETES_SERVICE_PORT_HTTPS=443
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT_3306_TCP_ADDR=10.103.168.75
REDIS_MASTER_PORT_6379_TCP_PORT=6379
MYSQL_SERVICE_PORT=3306
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT=tcp://10.107.164.188:6379
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
MYSQL_PORT=tcp://10.103.168.75:3306
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
MYWEB_PORT=tcp://10.101.248.174:8080
MYSQL_SERVICE_HOST=10.103.168.75
_=/usr/bin/printenv
root@redis-slave-vn4zg:/data#
注意到redis-master服务被分配了一个值为10.96.24.17的虚拟IP地址,随后kubernetes集群中其他新创建的Pod就可以通过10.96.24.17+端口6379来访问这个服务了。此时redis主从集群模式已经搭建完毕,后面搭建前端服务。
创建frontend和Service
前面已经创建好了一个具备读写分离能力的redis集群,现在开始部署前端PHP应用。
首先为frontend服务创建一个RC定义文件:frontend-rc.yaml,下面给出了改文件的完成内容和解释:
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
labels:
name: frontend
spec:
replicas: 3
selector:
name: frontend
template:
metadata:
labels:
name: frontend
spec:
containers:
- name: php-redis
image: kubeguide/guestbook-php-frontend
env:
- name: GET_HOSTS_FROM # 表示从环境变量中取redis-master服务的ip地址
value: env
ports:
- containerPort: 80
frontend镜像guestbook.php代码中使用了GET_HOSTS_FROM变量:
REDIS_MASTER_SERVICE_HOST
和REDIS_SLAVE_SERVICE_HOST
两个环境变量是k8s默认为每个service生成的,具体前面章节有讲到。
执行该文件:
kubectl create -f frontend-rc.yaml
首次执行需要下载docker镜像,需要等待容器启动
接下来,我们用kubectl命令查看刚刚创建的RC:
[root@cloud01 guestbook.ex]# kubectl get rc
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 18m
mysql 1 1 1 84m
myweb 5 5 5 75m
redis-master 1 1 1 44m
redis-slave 2 2 2 23m
使用kubectl命令查看刚刚创建的Pod:
[root@cloud01 guestbook.ex]# kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-pfjkb 1/1 Running 0 4m55s
frontend-xf5z9 1/1 Running 0 18m
frontend-z9tv2 1/1 Running 0 18m
mysql-c2vtt 1/1 Running 0 84m
myweb-lb9ck 1/1 Running 0 75m
myweb-ljrjz 1/1 Running 0 75m
myweb-r4dx4 1/1 Running 0 75m
myweb-r7gjf 1/1 Running 0 75m
myweb-vz2hc 1/1 Running 0 75m
redis-master-zrk7c 1/1 Running 0 44m
redis-slave-jppx6 1/1 Running 0 23m
redis-slave-vn4zg 1/1 Running 0 23m
后面我们创建一个与之关联的Kubernetes Service的定义文件frontend-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
name: frontend
spec:
selector:
name: frontend
type:
NodePort # 使用节点上物理端口
ports:
- port: 80
nodePort: 30010 # 对外提供30010端口服务
执行该文件:
kubectl create -f frontend-service.yaml
运行kubectl命令,查看刚刚创建的service
[root@cloud01 guestbook.ex]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend NodePort 10.108.199.247 <none> 80:30010/TCP 7s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12h
mysql ClusterIP 10.103.168.75 <none> 3306/TCP 80m
myweb NodePort 10.101.248.174 <none> 8080:30002/TCP 75m
redis-master ClusterIP 10.107.164.188 <none> 6379/TCP 38m
redis-slave ClusterIP 10.96.24.17 <none> 6379/TCP 25m
此时可以通过master节点的30010端口来访问前端页面了: