2018年只剩最后30天了。Jerry在2017年的最后一天,曾经立下一个目标:这个微信公众号在2018年保证至少每周发布一篇SAP原创技术文章。
从Jerry在后台统计的2018全年文章数量来看,这个目标已经提前实现了。为了感谢大家的支持,在2018年的最后一天,Jerry会发布一个合集:《SAP成都研究院2018年XX篇原创文章合集》,包含了2018年全年SAP成都研究院的同事们发布过的文章。
Jerry在11月份中旬去SAP上海研究院参加了Kubernetes的内部培训(详情参考我的前一篇文章:站在巨人肩膀上的牛顿:Kubernetes和SAP Kyma)。在SAP上海研究院的同事们如果想参加这个内部培训,可以联系同事Yang Katie。
为了避免很快就把三位老师传授的知识忘得精光,我得给自己找点练习来巩固所学的东西。
Jerry 2014年底加入SAP CRM Fiori开发团队时,我们开发的CRM Fiori应用,还是部署在传统的SAP Netweaver上的,详情参考我的文章:SAP Fiori应用的三种部署方式。
后来,我陆续接触了Salesforce的云平台Heroku,也学着很多程序员一样把自己的博客搭在github上,再后来接触了SAP自己的云平台,自然而然地就会试着把SAP UI5部署到这些平台上:
现在既然学了Kubernetes,那么就来试试将SAP UI5应用运行在Kubernetes上面吧。
我用来部署的UI5应用名叫Jerry's Service Order, 是一个典型的Master-Detail风格的应用,左边Master List是所有服务订单列表,选中任意一个,在右边的Detail页面显示选中的服务订单的明细。
这个UI5应用的外观如上图所示。为简单起见,所有显示的数据都是从项目里的一个json文件读取的,不支持新订单的创建或修改。该应用可以从我的github获取:
https://github.com/i042416/jerrylist
如本文标题所示,这个练习的终极目标就是让该UI5应用运行于Kubernetes上,那么第一步就是先让它运行于容器里。和SAP Kubernetes内部培训一样,我选择了Docker这个非常受欢迎的容器引擎作为这个Kubernetes练习的容器技术。
关于Docker的简介和安装介绍,请参阅阮一峰大神的文章:Docker 入门教程
http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html
为什么我们要使用Docker容器?下面这段话摘自阮一峰的博客:
“Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。”
Jerry梳理了一下将SAP UI5应用运行在Docker容器里的全过程,总共分三个步骤:
1. 让UI5应用运行在本地容器内
2. 将包含了UI5应用的本地容器打成一个新镜像
3. 将本地镜像上传到Docker hub,再下载测试
下面是详细步骤。
1. 让UI5应用运行在本地容器内
如果仅仅只会跑Docker的Hello World(其实Jerry两周前就是这个水平,囧),拿到这个需求,从什么地方入手?
当然是从包含了能运行UI5应用的那些web服务器的镜像入手,这里我选择了Nginx镜像,在Docker hub上有10.4k个stars。
用下面的命令直接运行这个镜像:
docker run -it nginx
用docker ps拿到实例化的容器id:
然后进入处于运行状态中的容器,执行shell命令:
docker exec -it bbc5d48a761c /bin/sh
看到#提示符后,进入容器内部的目录:/usr/share/nginx/html
如果我们能将github上的UI5应用的文件想办法拷贝到这个目录下面,就达到了在本地Docker容器运行UI5应用的目的了。
有很多种办法可以把github里的资源下载到Docker容器内部这个指定的目录下, 这里Jerry用一种我觉得最简单的方式,即通过Docker Volume技术将宿主机上的某个目录A以Volume的方式挂接到容器内部的html文件夹上,这样我们直接把github仓库上的webapp文件夹下载到宿主机的文件夹A即可,这个文件夹会以Volume的形式自动出现在容器内部映射好的目录内。
docker run -d -p 1081:80 -v pwd
/webapp:/usr/share/nginx/html/webapp –name jerry-custom nginx
使用参数-p 1081:80将Nginx服务通过端口1081暴露出来,因此我这次要使用http://localhost:1081测试新启动的容器实例。
再次执行docker exec进入docker容器内部,确保/usr/share/nginx/html文件夹下确实包含了期望看到的UI5应用。
浏览器里输入localhost:1081/webapp,确保UI5应用能够正常访问,至此这个应用已经在本地docker容器里成功运行起来了。
2. 将包含了UI5应用的本地容器打成一个新镜像
到目前为止这个本地docker实例是没有办法给其他人使用的,为此我们得先利用dockerfile制作一个包含了UI5应用的docker镜像,上传到docker hub上,以便其他人下载。
随便创建一个文件夹,比如jerry-build, 然后把webapp文件夹放进去,再创建一个dockerfile文件,内容就三行:
FROM nginx:stable
COPY webapp/ /usr/share/nginx/html/webapp/
RUN ls -la /usr/share/nginx/html/webapp*
这三个指令从语义上不难理解,第一行FROM命令告诉docker镜像构建例程使用nginx的stable版本作为基础镜像进行新镜像的构建。第二行COPY命令负责把webapp文件夹下的所有UI5资源文件拷贝到nginx docker镜像的对应目录内。第三行RUN命名执行shell命令ls,生成新的镜像文件。
dockerfile的详细语法请参考Docker 官方文档:
https://docs.docker.com/engine/reference/builder/#usage
执行命令docker build ., 最后一个.代表“当前目录”。
看到上图"Successfully built(成功构建)"的输出信息后,我们加上参数-t jerry-nginx-image:1.0重新构建一个名为jerry-nginx-image的镜像:
成功构建后,使用参数-p暴露一个新的端口1082:
docker run -d -p 1082:80 jerry-nginx-image:1.0
现在localhost:1082/webapp也能访问UI5应用了。
使用docker images, 现在我们能看到这个构建好的镜像了,接下来我们会将其推送到Docker hub上。
3. 将本地镜像上传到Docker hub
Docker hub的使用方式几乎和github完全一致。说句题外话:虽然github今年6月份被微软收购了,但是用户体验一点也没变,一如既往的优秀。
关于github更多另类用法,请参阅Jerry的文章:写在Github被微软收购之际 - Github的那些另类用法。
首先在Docker hub上注册一个帐号:
创建一个新仓库:
取名i042416/ui5-nginx:
新建好的空的仓库看起来是这样的:
使用docker ps得到本地正在运行的docker容器的ID:
使用commit命令提交这个本地容器的修改(类比git commit ):
docker commit 53de4188b702 i042416/ui5-nginx
现在准备将这个本地提交过后的镜像推送到Docker hub了。
执行命令docker login:
在CloudFoundry上部署应用的朋友们可以把docker login类比成cf login(下面是cf login的截图):
最后一步就是用docker push将本地镜像推送到Docker hub:
刷新Docker hub上新建的仓库,能观察到刚才的本地推送记录和镜像尺寸。
现在可以通知您的朋友,在其电脑上消费这个镜像了。当然您也可以把自己电脑上的本地镜像删除,再使用docker run执行。
在两种情况下,由于本地镜像检索失败,我们都将看到提示信息:Unable to find image ‘i042416/ui5-nginx:latest' locally, 然后观察到远端镜像的下载过程。
使用1080端口基于镜像i042416/ui5-nginx启动一个新的容器:
localhost:1080/webapp能够正常工作:
docker inspect命令证实了这个启动的容器确实是基于镜像i042416/ui5-nginx的。
在这个主题的下半部分,我们将使用这个i042416/ui5-nginx镜像,开始我们的Kubernetes之旅。敬请期待。
更多阅读
要获取更多Jerry的原创文章,请关注公众号"汪子熙":