写点什么

Docker 下的 OpenResty 三部曲之三:OpenResty 加 Tomcat 的服务

作者:程序员欣宸
  • 2022 年 5 月 14 日
  • 本文字数:5715 字

    阅读完需:约 19 分钟

Docker下的OpenResty三部曲之三:OpenResty加Tomcat的服务

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本章是《Docker 下的 OpenResty 三部曲》的终篇,前面两章我们实战了 OpenResty 的 lua 脚本开发,并且将 web 服务发布在 Docker 环境下,本章我们在 kubernetes 环境下搭建 OpenResty 加 Tomcat 的 web 服务;

前文信息

kubernetes 下实战 Nginx 和 Tomcat 的参考文章

环境信息

  • 本次实战搭建的环境如下所示:


本次实战的 web 服务

  • 本次实战的 web 服务,主要功能为:浏览器发起 http 请求到 OpenResty,OpenResty 负责去处理业务,处理过程中向 Tomcat 发起 http 请求,将 Tomcat 响应的信息返回给浏览器;

和常见的 Nginx 加 Tomcat 有什么不同?

  • 上图的架构和常见的 Nginx 加 Tomcat 很相似,不过还是有区别的:


  1. Nginx 节点主要工作是将请求转发到 Tomcat,由 Tomcat 去执行具体业务;

  2. OpenResty 节点上的 lua 脚本可以执行具体业务,处理的过程中如有必要可以主动发起 http 请求到 Tomcat,调用 Tomcat 提供的服务;

  3. 关于 OpenResty 加 Tomcat 架构的更多特性和优点请移步开涛大神的博客:《跟我学Nginx+Lua开发》

实战源码和 Docke 镜像材料下载

  • 本次构建镜像所需的材料我已准备齐全,您可以在 github 下载到:



  • 这个 git 项目中有多个目录,本次所需的资源放在 tomcat_openresty_docker_image_files,如下图红框所示:


  • 注意,本章用到的资源目录是 tomcat_openresty_docker_image_files,上一章用的是 nginx_lua_docker_image_files,里面是不同资源;


  • Tomcat 上的 web 工程源码在 k8stomcatdemo 目录下,是个普通的 springboot 工程,提供一个 http 接口可以返回 Tomcat 所在机器的 IP 地址,这个 web 工程已经被制作成镜像文件 bolingcavalry/k8stomcatdemo:0.0.1-SNAPSHOT,有关这个工程的更多信息,请参照《kubernetes下的Nginx加Tomcat三部曲之二:细说开发》

列举步骤

  • 今天的整个实战,我们要做以下事情:


  1. 在前一章的基础上重做 OpenResty 的 Docker 镜像,新增一些内容;

  2. 在 kubernetes 创建 Tomcat 的副本集和 service;

  3. 在 kubernetes 创建 OpenResty 的副本集和 service;

  4. 通过浏览器验证 kubernetes 环境中的 OpenResty 加 Tomcat 提供的服务;

为什么要重做 Docker 镜像

  • 在上一章我们已经做了 OpenResty 的镜像并体验过了,为何本章还要重做呢?


  1. 在镜像中安装 vim,并解决 vim 显示中文乱码的问题;

  2. 新增一个 http 接口和 lua 脚本,浏览器访问这个接口时,对应的 lua 脚本会向 Tomcat 发请求;

  3. 将 OpenResty 的 http 模块集成到镜像中;

  4. 曲折的解决 OpenResty 的 http 模块无法解析域名的问题;


  • 前期铺垫说了这么多,接下来我们就开始实战吧;

制作 OpenResty 镜像

安装 vim,并解决 vim 显示中文乱码的问题

  • 以下是 Dockerfile 中 vim 相关的内容:


#创建安装命令RUN apt-get install -y make vim gcc libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl 
#修改vim的配置信息,修复中文乱码问题RUN sed -i '$a\set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936' /etc/vim/vimrcRUN sed -i '$a\set termencoding=utf-8' /etc/vim/vimrcRUN sed -i '$a\set encoding=utf-8' /etc/vim/vimrc
复制代码


  • 如上所示,通过 sed 命令向/etc/vim/vimrc 文件中追加了三行,以保证中文显示;

添加 OpenResty 的 http 模块

  • 首先下载 http.lua 和 http_headers.lua 这两个文件,放在 Dockerfile 所在目录,然后在 Dockerfile 添加以下内容:


#将http库文件复制到默认库COPY ./http.lua $INSTALL_PATH/lualib/restyCOPY ./http_headers.lua $INSTALL_PATH/lualib/resty
复制代码

增加一个 http 接口

  • 为了对外新增一个 http 接口 lua_http,在 boling_cavalry.conf 文件中增加了以下内容,


location ~ /lua_http/(\d+)/(\d+) {        #设置nginx变量        set $a $1;        set $b $host;        default_type "text/html";        #nginx内容处理        content_by_lua_file /usr/local/work/lua/test_http.lua;    }
复制代码


  • 如上所示,当浏览器输入 http://192.168.119.150:9000/lua_http/333/666 这样 URL 时,就会由 test_http.lua 这个脚本来处理,test_http.lua 的内容如下所示:


ngx.say("---------------------------------------------------------<br/>");
local http = require("resty.http")--创建http客户端实例local httpc = http.new()
local resp, err = httpc:request_uri("http://127.0.0.1:80",{ method = "GET", path = "/tomcat_proxy/getserverinfo", headers = { ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36" }})
if not resp then ngx.say("request error :", err) returnend

ngx.say("resp status--", resp.status)
ngx.say("<br/><br/>resp body--", resp.body)
httpc:close()
复制代码


  • 如上所示,该脚本的处理很简单:发起一个 http 请求,将收到响应的 code 和 body 在页面上显示出来;

  • 你们注意到了吗?请求的地址是 127.0.0.1,是 OpenResty 自己,不是 Tomcat!!!

  • 为什么要向自己发 http 请求呢?不是应该发请求给 Tomcat 么?我们一起来梳理一下吧:

  • 按照原本的设计,此处的 URL 应该是 http://tomcathost:8080/getserverinfo,tomcathost 是 Tomcat 的 Pod 在 kubernetes 暴露的 service 名称,在 kubernetes 环境中 tomcathost 会被解析成 Tomcat 的 Pod 地址,请求就到了 Tomcat 了;

  • 但是,OpenResty 的 httpc:request_uri 这个 API 无法将 tomcathost 这个 hostname 解析成 Tomcat 的 Pod 地址,或者说不会用到 kubernetes 提供的 DNS 服务,因此请求无法到达 Tomcat 的 Pod;


  • 开涛大神的实例也用到了这个 API,他在 nginx.conf 中配置了 resolver 8.8.8.8,访问的是外网 hostname:s.taobao.com,但我这里是 kubernetes 环境内的 hostname,试过此方法,失败;

  • 网上也有遇到此类问题的,写了脚本定时扫描/etc/hosts,将 hostname 对应的 IP 保存来下,在 OpenResty 中发起 http 请求时直接从这个对应关系中取 IP,这个方法在 Docker 环境使用是 OK 的,Docker 的 link 用的就是/etc/hosts 中的关系,但是在 kubernetes 下,OpenResty 的/etc/hosts 中并没有写入 Tomcat 的 IP,所以,失败;

我的笨办法,让 OpenResty 请求到 Tomcat

  • 我用的是笨办法,能将请求发到 Tomcat,但是很曲折,理论上存在性能损失,亲爱的读者如果您有更科学的方法,期望能得到您的指点;

  • 笨办法的思路如下:


  1. 在 nginx.conf 中新建一个 upstream:


upstream tomcat_client {         server tomcathost:8080;    } 
复制代码


  • 此 upstream 中的 server 用到了 tomcathost,这是 kubernetes 中 Tomcat 的 service 的名字,能被成功的解析成 Tomcat 的 Pod 的 IP;


  1. 再增加一个 http 接口 tomcat_proxy,boling_cavalry.conf 文件中增加了以下内容:


location ~ /tomcat_proxy/(.*) {        rewrite /tomcat_proxy(/.*) $1 break;        proxy_pass http://tomcat_client;        proxy_redirect default;        proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;    }
复制代码


  • 如上所示,如此一来,所有 url 是 tomcat_proxy/xxxxxxx 的请求都会被转发到 Tomcat 了;

  • 注意这一行:rewrite /tomcat_proxy(/.*) $1 break;,可以这么理解:a. http://127.0.0.1:80/tomcat_proxy/getserverinfo 这个请求可以被 tomcat_proxy(/.*)这个正则表达式匹配;b. 因为**/getserverinfo 和第一个圆括号中的表达式匹配,所以 $1 的值就是/getserverinfo**;c. 因此请求**/tomcat_proxy/getserverinfo 被 rewrite 成了 $1,也就是/getserverinfo,带上 IP 的完整的 url 就是 http://127.0.0.1:80/getserverinfo ;d. 所以,http://127.0.0.1:80/tomcat_proxy/getserverinfo 被转发到了 http://tomcathost:8080/getserverinfo ,在 rewrite 的时候将 tomcat_proxy**抹去了;

  • 最终,在调用 OpenResty 的 request_uri 方法的时候,请求的 URL 是 http://127.0.0.1:80/tomcat_proxy/getserverinfo ,可以被成功转发到 Tomcat,关键是 Nginx 中的 upstream 的 hostname 在 kubernetes 可以被成功解析成 IP 地址;

  • 最后,在制作 Dockerfile 的时候,记得将新增的 test_http.lua 复制到镜像中:


COPY ./test_http.lua $WORK_PATH/lua
复制代码


  • 以上就是制作新的 Dockerfile 时要新增的内容了,在 Dockerfile 所在目录执行以下命令构建镜像,注意 TAG 是 0.0.2,区别于上一章镜像的 0.0.1:


docker build -t bolingcavalry/ubuntu16-openresty:0.0.2 .
复制代码

让 kubernetes 的机器用上 web 工程的 Docker 镜像

  • 现在的镜像只存在于开发和构建 web 工程的电脑上,为了让 kubernetes 的 node 机器能用上这个镜像,可以用以下四种方式实现:


  1. 用 docker push 命令将本机镜像推送到 hub.docker.com 网站,这样其他机器都可以通过 docker pull 命令取得了,我就是用的这种方法,需要在 hub.docker.com 上注册;

  2. 用 docker save 命令导出镜像文件,再用 docker load 命令导入到 kubernetes 的 node 机器;

  3. kubernetes 所在机器安装 java 和 maven 环境,将工程在这里编译构建;

  4. 使用 docker 私有仓库,例如搭建局域网私有仓库或者阿里云私有仓库,参考《maven构建docker镜像三部曲之三:推送到远程仓库(内网和阿里云)》

在 kubernetes 创建 Tomcat 的副本集和 service

  • 登录到一台可以执行 kubectl 命令的机器,创建文件 tomcat.yaml:


apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: tomcathostspec:  replicas: 3  template:    metadata:     labels:       name: tomcathost    spec:     containers:     - name: tomcathost       image: bolingcavalry/k8stomcatdemo:0.0.1-SNAPSHOT       tty: true       ports:       - containerPort: 8080
复制代码


  • 以上文件用来定义 Tomcat 的 Pod 的副本集;

  • 在 tomcat.yaml 所在目录创建 tomcat-svc.yaml 文件,内容如下:


apiVersion: v1kind: Servicemetadata:  name: tomcathostspec:  type: ClusterIP  ports:       - port: 8080  selector:    name: tomcathost
复制代码


  • 以上文件将 Tomcat 的 Pod 副本包装成 service,名字是 tomcathost,这样 kubernetes 环境中的其他 Pod 通过 tomcathost 这个 hostname 就能访问到 Tomcat 的 Pod 副本了;

  • 在 tomcat.yaml 文件所在目录下执行以下命令,创建 Pod 和 service:


kubectl create -f tomcat.yaml,tomcat-svc.yaml
复制代码


  • 以上命令依次创建 Pod 和 service;

  • 执行 kubectl get service 查看 service 情况,如下:


root@maven:/usr/local/work/openrest_rewrite# kubectl create -f tomcat.yaml,tomcat-svc.yamldeployment "tomcathost" createdservice "tomcathost" createdroot@maven:/usr/local/work/openrest_rewrite# kubectl get serviceNAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGEkubernetes     ClusterIP   10.43.0.1       <none>        443/TCP          62dtomcathost     ClusterIP   10.43.192.125   <none>        8080/TCP         1m
复制代码


  • 至此,Tocmat 服务就 OK 了,接下来我们创建 OpenResty 服务吧;

在 kubernetes 创建 OpenResty 的副本集和 service

  • 在 tomcat.yaml 所在目录下,创建文件 openresty.yaml:


apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: openresty001spec:  replicas: 1  template:    metadata:     labels:      name: openresty001    spec:     containers:     - name: openresty001       image: bolingcavalry/ubuntu16-openresty:0.0.2       ports:       - containerPort: 80
复制代码


  • 以上文件用来定义 OpenResty 的 Pod 的副本集;

  • 在 openresty.yaml 所在目录创建 openresty-svc.yaml 文件,内容如下:


apiVersion: v1kind: Servicemetadata:  name: openresty001spec:  type: NodePort  ports:       - port: 80         nodePort: 30007  selector:    name: openresty001
复制代码


  • 以上文件将 openresty 的 Pod 副本包装成 service,名字是 openresty001,并且将 80 端口和所在节点机器的 30007 端口建立映射;

  • 在 openresty.yaml 文件所在目录下执行以下命令,创建 Pod 和 service:


kubectl create -f openresty.yaml,openresty-svc.yaml
复制代码


  • 以上命令依次创建 Pod 和 service;

  • 执行 kubectl get service 查看 service 情况,如下:


root@maven:/usr/local/work/openrest_rewrite# kubectl create -f openresty.yaml,openresty-svc.yamldeployment "openresty001" createdservice "openresty001" createdroot@maven:/usr/local/work/openrest_rewrite# kubectl get serviceNAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGEkubernetes     ClusterIP   10.43.0.1       <none>        443/TCP          62dopenresty001   NodePort    10.43.148.118   <none>        80:30007/TCP     8stomcathost     ClusterIP   10.43.192.125   <none>        8080/TCP         43m
复制代码


  • 终于将所有的服务都创建好了,接下来开始验证吧;

验证服务

  • OpenResty 服务所在的机器 IP 是 192.168.119.153,所以在浏览器输入:http://192.168.119.153:30007/lua_http/333/666 ,可以看到如下效果:



  • 如上所示,结合 test_http.lua 的源码来看,“200”是 Tomcat 的返回 code,“10.42.11.12”是 Tomcat 所在 Pod 的 IP 地址,我们去 kubernetes 的 dashboard 印证一下,如下图,红框 1 是查看信息的路径,红框 2 是 Pod 的 IP 地址,果然有 10.42.11.12:



  • 至此,《Docker 下的 OpenResty 三部曲》就全部结束了,由于文章大纲没有设计好,导致第三章内容太多,在这里向您道歉了,如果有疑问欢迎您邮件至 zq2599@gmail.com,我们一起探讨,也希望本文能对您的 kubernetes 实战有所帮助;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 刚刚阅读数: 2
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Docker下的OpenResty三部曲之三:OpenResty加Tomcat的服务_Kubernetes_程序员欣宸_InfoQ写作社区