在电脑上的K8s集群中进行GuestBook教程实践

我在我的电脑上部署了一个带有Redis的PHP Guestbook应用的示例,在上游Kubernetes集群中进行了实施。这个环境是通过在15个步骤中学习从Docker开始进入Kubernetes容器开发到K8s生产运营的学习环境2来获得的。当然,也可以在IBM Cloud OpenShift上运行和尝试。

在这篇文章中,我想写一些教程中未涉及的要点以及在个人电脑上使用本地环境时的差异。因此,我建议与教程一起参考。

在本篇文章中使用的学习用Kubernetes环境是通过https://github.com/takara9/vagrant-kubernetes 进行配置的。这个环境可在 macOS、Windows10、Linux 等不同的操作系统上使用,因此不仅可以利用公有云服务,还可以一窥背后的运作情况,对于构建具备控制供应商依赖的本地环境并获得相关技能也非常有帮助。

教程的结构

在本教程中要构建的配置如下图所示。

configuration.png

从浏览器访问前端URL并提交消息时,PHP应用将引用redis-master,将数据写入Redis主服务器。另外,若仅执行引用操作,如重新加载前端URL,PHP应用将引用服务redis-slave,从Redis从属服务器获取数据。

Web应用程序的读写比例通常被视为8:2,因此使用Redis从服务器处理读取流量,使用Redis主服务器处理写入流量。

在障碍处理方面,frontend、redis-master和redis-slave的每个应用程序的pod都在各自的部署控制器中被监视其启动数量。如果出现异常情况,例如pod停止,它们将执行自我恢复。

在负载处理方面,前端应用可以通过增加Pod数量来提高处理能力以应对访问量增加。同时,可以增加用于参考流量的redis-slave的数量。由于redis-slave的设置将redis-master配置为主服务器并同步数据,因此增加redis-slave的Pod数量就可以提高参考方面的性能。

最终,redis-master的Pod(容器)将成为整体性能的瓶颈,但可以通过增加分配给redis-master Pod内部容器的CPU和内存的量来采取扩展性措施以解决该问题。

获取并修改K8s构建代码

使用学习环境2,通过15个步骤掌握从Docker到Kubernetes的学习,涵盖容器开发到K8s的正式运营。

筆者最近對剛剛發布的1.17.0版本進行了測試,發現在同一節點上的Redis主從之間,在數據同步過程中會出現超時問題。因此,這次我們選擇使用與本書相同的1.14版本。當然,由於1.14的支持即將結束,我們將繼續追蹤1.17版本的原因和解決方案。

注意: 在1.17.0版本中存在故障,但之后发布的1.17.1版本没有出现问题。所以,请使用GitHub的1.17分支的版本从1.17.1及以后开始使用。

tkr@luigi:~$ git clone https://github.com/takara9/vagrant-kubernetes k8s-1.14
tkr@luigi:~$ cd k8s-1.14

如果你想从电脑外部访问这个操作,可以采用以下方法应对。需要确保IP地址与电脑所在网络的地址相同。如果仅限制在电脑上的浏览器访问,那么这个操作就不必要了。

取消Vagrantfile的第13行和第41行的注释符号,启用计算机的IP地址。这样,就可以从计算机的外部访问K8s集群了。

<中略>
    11      machine.vm.hostname = 'node1'
    12      machine.vm.network :private_network,ip: "172.16.20.12"
    13      machine.vm.network :public_network, ip: "192.168.1.92", bridge: "en0: Ethernet"
    14      machine.vm.provider "virtualbox" do |vbox|
<中略>
    39      machine.vm.hostname = 'node2'
    40      machine.vm.network :private_network,ip: "172.16.20.13"
    41      machine.vm.network :public_network, ip: "192.168.1.93", bridge: "en0: Ethernet"
    42      machine.vm.provider "virtualbox" do |vbox|

使用以下命令启动K8s集群。

tkr@luigi:~/k8s-1.14$ vagrant up

设置环境变量后,启动完成后,执行”kubectl get node”命令来确认其运行情况。

tkr@luigi:~/k8s-1.14$ export KUBECONFIG=`pwd`/kubeconfig/config
tkr@luigi:~/k8s-1.14$ kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   12m   v1.14.3
node1    Ready    <none>   12m   v1.14.3
node2    Ready    <none>   12m   v1.14.3

创建和切换至专为应用程序的命名空间。

为了避免与其他应用程序环境混淆,我们创建了一个专用的命名空间来分离教程应用程序的执行环境,这是教程中没有涉及的任务。

创建一个专供留言簿使用的命名空间”guestbook”。

tkr@luigi:~/k8s-1.14$ kubectl create ns guestbook

为了切换命名空间,我们需要创建一个上下文。

tkr@luigi:~/k8s-1.17$ kubectl config set-context gb --namespace=guestbook --cluster=kubernetes --user=kubernetes-admin

确认确立了上下文。

tkr@luigi:~/k8s-1.17$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
          gb                            kubernetes   kubernetes-admin   guestbook
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin   

以下命令可固定地切换上下文。这样,命名空间guestbook将成为默认命名空间。

tkr@luigi:~/k8s-1.17$ kubectl config use-context gb
Switched to context "gb".

现在,确认自己正在使用的上下文。如果NAMESPACE列为gustbook,则可以接受。

tkr@luigi:~/k8s-1.17$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         gb                            kubernetes   kubernetes-admin   guestbook
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin 

启动Redis主服务器。

从这里开始,根据例子:使用Redis部署PHP访客留言板应用程序,尽量避免重复并继续进行。为此,我认为参考官方页面以及本文,会更加清楚易懂。

通过部署来启动Redis主服务器并进行服务配置。

tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-deployment.yaml
tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-service.yaml

启动Redis从服务器

创建一个由 Redis 主服务器同步数据的从服务器。同时还要创建相应的服务。

tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-deployment.yaml
tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-service.yaml

这个清单所述的容器镜像的创建位于以下的Git仓库中,因此可以更改应用程序和配置。https://github.com/kubernetes/examples/tree/master/guestbook

Redis从属容器将通过以下启动脚本启动。else语句下方是通常使用的启动命令,而redis-master将成为前述主服 们的服务名称。

if [[ ${GET_HOSTS_FROM:-dns} == "env" ]]; then
  redis-server --slaveof ${REDIS_MASTER_SERVICE_HOST} 6379
else
  redis-server --slaveof redis-master 6379
fi

通过K8s内部DNS,redis-master的IP地址得到解析,可以访问到Redis主服务器。

tkr@luigi:~/k8s-1.14$ kubectl get svc
NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
redis-master   ClusterIP   10.32.0.252   <none>        6379/TCP       3h10m
redis-slave    ClusterIP   10.32.0.34    <none>        6379/TCP       3h10m

如果Redis主服务器停止,根据本教程的配置,从服务器不会自动升级为主服务器,因此需要SRE进行处理。由于本教程未使用Redis的持久性存储,所以数据会在Pod停止时丢失。

前端启动

最后,启动前端应用程序并部署服务,使其能够从外部访问。这样就可以通过电脑浏览器进行访问了。

tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
tkr@luigi:~/k8s-1.14$ kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml

前端服务类型将作为NodePort公开。可以使用节点的IP地址和NodePort端口号访问。

tkr@luigi:~/k8s-1.14$ kubectl get svc
NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
frontend       NodePort    10.32.0.178   <none>        80:31767/TCP   3h6m
redis-master   ClusterIP   10.32.0.252   <none>        6379/TCP       3h10m
redis-slave    ClusterIP   10.32.0.34    <none>        6379/TCP       3h10m

这个应用程序是用PHP开发的,代码存放在Git仓库中。仓库的地址是https://github.com/kubernetes/examples/tree/master/guestbook/php-redis。

以下的PHP代码是为了将输入写入Redis主节点,并在读取时从从节点读取。读取和写入的切换是通过从controllers.js接收参数并在PHP端的if语句中进行分支的。然后,else语句中的redis-slave会将请求分散到多个Redis从节点,并将已启动的从节点的IP地址随机转换。

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
if (isset($_GET['cmd']) === true) {
  $host = 'redis-master';
  if (getenv('GET_HOSTS_FROM') == 'env') {
    $host = getenv('REDIS_MASTER_SERVICE_HOST');
  }
  header('Content-Type: application/json');
  if ($_GET['cmd'] == 'set') {
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);
    $client->set($_GET['key'], $_GET['value']);
    print('{"message": "Updated"}');
  } else {
    $host = 'redis-slave';
    if (getenv('GET_HOSTS_FROM') == 'env') {
      $host = getenv('REDIS_SLAVE_SERVICE_HOST');
    }
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);
    $value = $client->get($_GET['key']);
    print('{"data": "' . $value . '"}');
  }
} else {
  phpinfo();
} ?>

访问测试

通过浏览器,访问与kubectl get svc显示的端口号相匹配的IP地址,例如http://192.168.1.92:31767,可以显示出“Guestbook”的输入字段。在输入字段中输入消息,然后单击“提交”按钮,即可将数据写入Redis。

guestbook.png

由于此应用程序不支持多字节字符,所以重新加载后,日语显示将无法正确显示。

总结

通过应用简单的YAML文件来增加或减少Pod数量,可以体验到通过缩放和部署控制器启动Pod来提高可用性的特点。要充分发挥Kubernetes的优势,也需要理解适当的应用程序设计是必要的。

由于Redis是一个具有状态的应用程序,用于持续存储数据,因此仅仅使用本教程的配置无法满足生产环境的要求,但只要在此基础上逐步添加即可。

bannerAds