存在i 1%3ci java_一项神奇的武功:Java S2I

S2I不仅是Openshift的一个功能,还是个开源项目

S2I是红帽Openshift开发的一个功能组件。目前可以独立于Openshift运行。在社区里,他的名字是Java S2I。

Java S2I在github的地址:https://github.com/openshift/source-to-image/blob/master/README.md

6411c4cb6b6b381940c50693ef086c82.png

基于Openshift的S2I内功心法如下:

215879c0915f09ee62e387db4a9fc9ad.png

Java S2I映像使开发人员能够通过简单地指定其应用程序源代码或已编译的Java二进制文件的位置,在OpenShift容器平台中按需自动构建,部署和运行Java应用程序。在许多情况下,这些Java应用程序是可启动的“Fat JAR”,包括应用程序服务器和其他框架的嵌入式版本。此类开源项目的示例包括Spring Boot,Eclipse Vert.x和WildFly Swarm。拥有专为OpenShift设计的通用Java S2I映像,通过包含许多有用的功能,可以更轻松地开发可启动的Fat JAR,例如:

简单而灵活:Java S2I映像可以处理复杂的构建结构,但默认情况下,它会假定在成功构建后,/target目录中将运行要运行的JAR。如果不是这种情况,您可以使用环境变量ARTIFACT_DIR进行调整。此外,如果构建生成多个JAR文件,则可以使用环境变量JAVA_APP_JAR指定要运行的JAR文件。但是,在大多数情况下,您所要做的就是直接指向源存储库,Java S2I映像将起作用。

自动JVM内存配置:在OpenShift中,资源可能受限于配额等。如果存在此类限制,Java S2I映像将自动采用JVM内存设置,以便JVM不会使用比允许的内存更多的内存。use,反过来有助于避免OutOfMemory异常。默认情况下启用此功能,但可以通过在环境变量JAVA_OPTIONS中使用-Xmx为堆设置固定值来禁用此功能。

通过Jolokia公开JMX统计信息:默认情况下,Java S2I映像将启用Jolokia,它通过HTTP公开JMX统计信息和操作。这可以实现更详细的应用程序监视功能。默认情况下启用此功能,但可以通过将环境变量AB_JOLOKIA_OFF设置为true来禁用此功能。

减小docker image大小:为了使docker image的大小保持最小,可以在构建最终图像之前让Java S2I图像删除任何maven repo数据。默认情况下禁用此功能以支持增量构建。要从最终容器映像中删除maven工件,请将环境变量MAVEN_CLEAR_REPO设置为true。

S2I如果独立于Openshift安装,其安装方式如下:

使用 s2i 工具为 S2I 构建器镜像创建所需的模板文件和文件夹。通过运行 s2i create 命令为构建器镜像创建模板文件:

ae567f66c9040a7db1c47a44266032fb.png

四级文件的功能说明:

876b507abfb35158a730cb6d0521712c.png

脚本写好以后,执行:docker build -t s2i-httpd .     生成Buildder Image。

在使用build imager实现S2I的时候,执行:s2i build命令(s2i-httpd是builder iage)。例如:

#s2i build test/test-app/  s2i-httpd s2i-sample-app

...Using "assemble" installed from "image:///usr/libexec/s2i/assemble"

...Using "run" installed from "image:///usr/libexec/s2i/run"

...Using "save-artifacts" installed from "image:///usr/libexec/s2i/saveartifacts"

---> Copying source files to web server directory...

Source-to-Image语言检测OpenShift 可以直接基于 Git 存储库中存储的源代码来创建应用。oc new-app 命令的语法更简洁,它只会将 Git 存储库 URL 用作参数,然后,OpenShift 会尝试自动检测应用所用的编程语言,并选择兼容的构建器镜像。编程语言检测功能会在 Git 存储库的根目录中查找特定的文件名称。

662edac2df723f312ab917343be1db8c.png

OpenShift 会采用多步算法来确定 URL 是否指向源代码存储库,如果是,则还会采用该算法来确定应由哪个构建器镜像来执行构建。以下是该算法的简单介绍:

1.URL 会被当作容器注册表 URL 来进行访问。如能成功访问,则需创建构建配置。OpenShift 会创建部署容器镜像所需的部署配置和其他资源。

2.如果 URL 指向某个 Git 存储库,则 OpenShift 会检索该存储库中的文件列表并搜索 Dockerfile。如果找到了,则构建配置会使用 docker 策略。否则,构建配置会使用 source 策略,该策略需要使用 S2I 构建器镜像。

3.OpenShift 会搜索语言构建器名称与 supports 注释值相匹配的镜像流。搜索到的第一个匹配项会成为 S2I 构建器镜像。

4.如果没有匹配的注释,则 OpenShift 会搜索名称与语言构建器名称相匹配的镜像流。搜索到的第一个匹配项会成为 S2I 构建器镜像。

只需执行第 3 步和第 4 步,即可轻松向 OpenShift 集群添加新的构建器镜像。这还意味着可能存在多个匹配的构建器镜像。本章前文中已介绍过使用 oc new-app 命令行选项可以避免这种不确定性,并确保选择正确的镜像流作为 S2I 构建器镜像。

Source-to-Image (S2I) 构建流程

S2I 构建流程涉及三个基础组件;通过组合使用这些组件,可以创建最终容器镜像:应用的源代码。

S2I 脚本

一组由构建流程执行以自定义 S2I 构建器镜像的脚本。S2I 脚本可以使用任意编程语言来编写,这些脚本可在 S2I 构建器镜像内执行。

实验1:Apache HTTP 服务器 S2I 构建器镜像 (rhscl/httpd-24-rhel7)。

查看包含自定义 S2I 脚本的应用源代码。

.s2i/bin/assemble 脚本会将应用源中的 index.html 复制到位于 /opt/app-root/src 的 Web 服务器文档中。它还会创建一个包含页面构建时间和环境信息的 info.html 文件:

.s2i/bin/run 脚本会将 Web 服务器中的启动消息默认日志级别更改为 debug:

基于 Git 中的来源创建一个名为 hello 新应用。需要将 httpd 镜像流作为前缀添加到 Git URL 中,以确保应用使用 rhscl/httpd24-rhel7 构建器镜像。使用~表示强制指定image stream,而不是去查找:

使用路由公开应用供外部访问。需要使用 --port 选项,因为容器镜像会公开多个端口:

实验2:使用S2I部署fat jarOpenJDK S2I 构建器镜像 (redhat-openjdk-18/openjdk18-openshift)。

应用 Git 存储库 (java-serverhost)。

基于 Git 中的来源创建新应用。将应用命名为 jhost,并在 oc new-app 命令中使用 --build-env 选项来定义构建环境变量和 maven 存储库位置。

oc new-app --name jhost --build-env MAVEN_MIRROR_URL=http://services.lab.example.com:8081/nexus/content/groups/training-java -i redhat-openjdk18-openshift http://services.lab.example.com/java-serverhost

2c9c0478afc65011e7a02306497fc394.png

681a8be5b36d7a9bad2fcadfe5045ac3.png

将应用更新至 2.0 版。

编辑 /home/student/java-serverhost/src/main/java/com/redhat/training/example/ 文件夹中的 ServerHostEndPoint.java 文件,并更新至 2.0 版:

d7777c923ec76b544d746c0433549ae7.png

72f6b1e6e9465bae4dc872f384311e1e.png

1b5b87ffb6e5023190f6426db9b62aff.png

=====================================================

近两年,笔者的很多客户都在进行容器云的研究,有些客户已经实施容器云。笔者也有幸参与了几项目,因此有一些体会。

整体上看,客户使用容器的用途大致有两种:实现PaaS(包括或不包括微服务)、实现CI/CD。

无论实现哪个功能(或者两个都要实现),第一步都是需要实现应用容器化。接下来,我们先看看应用容器化的方法。

一、应用容器化的方法

应用容器化,常见的方法有三种:

以上三种方式:

第一种本地构建最常见,也比较简单,但效率太低。

第二种方式是通过CI构建。这种方式则是比较传统的方法。需要指出的:CI/CD的实现,与容器并无必然的联系。在容器火之前,很多客户也基于Kenkins和虚拟机实现了CD/CD。只是容器轻量级的优势,更容易实现CI/CD。

在红帽Openshift中,我们可以通过CI构建实现容器镜像。这种构建方式,实际上是在openshift中部署Jenkins Slave Pod,在Slave Pod实现构建。红帽提供四种Jenkins Slave Pod的镜像(根据应用开发语言的不同),有基于maven的,有基于nodejs的、基于.net的、也有基础镜像(使用者基于基础镜像进行构建)。

第三种方式是通过S2I的方式构建应用镜像。这种方式有第二种方式构建的优点(构建好的应用客户实现CI),并且比第二种方式效率更高、适用多种开发语言,听起来非常牛逼,简直是厂商PaaS工程师(尤其是实施工程师)的救星!

在正式介绍S2I之前,我们先介绍一下image stream,因为S2I会用到image stream。

二、镜像流是个啥?

2.1 Image Stream的定义

简单而言,一个image stream是一类应用镜像的一组镜像的集合。而image的tag则定义这些名字及标签指向的具体镜像(这类镜像可能包含很多版本,通过tag来区分)。

例如一个httpd的image stream:

对于一个已经有的镜像,如docker.io上的docker image,如果openshift想使用,则可以通过image导入image stream:

# oc import-image my-ruby --from=docker.io/openshift/ruby-20-centos7 --confirm

这样,导入is的时候,image stream的tag也就被创建了,它指向image:docker.io/openshift/ruby-20-centos7。

需要注意的是,我们在从image 导入image stream的时候,最好加上--scheduled=true。它的作用在于,当image stream创建好以后,IS会定期检查镜像库的更新,然后更新到最新的image。如果不是这样,造成的结果可能是:docker.io有个新的/ruby-20-centos7的镜像,而image stream的latest仍然指向旧的镜像。

我们再举个例子,期初ose-haproxy-router默认是没有image stream的:

我们将其导入image stream:

再进行查看,就可以看到is和istag了。

其中is是包含Openshift docker-registry的地址,istag则指向ose-haproxy-router镜像。

需要指出的是:Image Stream不是K8S的组件,是Openshift的组件:

2.2 镜像流image stream的作用是什么?

刚接触openshift的朋友可能会问:image stream的作用是什么?

image stream的作用:

1.实现BC和镜像仓库的松耦合

2.配合Openshift的S2I全流程

在Openshift,部署应用的方法,通常有如下几种:

2.2.1 .通过docker image部署:这种通常直接部署已经包含应用的打包好的镜像,因此通常没有bc。

2.2.2 .通过S2I 部署:通过选择builder image和指定code。指定完以后,code 先进行build,build成功,会将它push到内部的镜像库,然后部署一个新的pod。因此S2I通常会触发build和deploy。

下图中,选择node.js这个image stream:

部署的时候,可以选择tag的版本,并会提示输入git代码仓库的地址:

2.2.3.通过模板部署

模板是可以把和一套应用相关的配置,都写在一起,然后通过这个模板部署应用。使用模板部署最大的好处在于,它可以加快应用的部署速度。模板是由实现写好的yaml或json文件创建的。

在以上的三种方式中,S2I的部署方式必然会包含BC,而通过模板可能会触发BC。

S2I的bc的阶段,是根据指定的builder imager和source code一起,build形成可被部署的App image,并push到Openshift的docker registry中(内部集成镜像库)。

这样就带来了一个问题,是不是每个S2I的每个BC操作,都需要指定bc成功以后要push的docker registry的地址?这显然非常麻烦。而Image Stream可以做这件事。

2.3 是否只有bc阶段可以调用image stream?

既然上文内容提到了,image stream主要是为S2I的bc阶段服务的(image stream包含了docker-registry的地址,实现了bc与docker registry的松耦合),那么dc阶段,是否可以调用image stream呢?

实际上,在整个完整的S2I流程中,当build成功以后,openshift将镜像push到镜像库的时候,不会也不可能更新builder image的tag,让新的tag指向生成的app image的(下图的nationalpark:latest就是app image,而app image的builder image的image stream是simple-java-s2i):

S2I bc结束以后继续的dc阶段,则是将172.30.174.125:5000/explore-00/nationalpark:latest这个镜像pull到要运行应用pod的node上,然后进行部署。

因此,在dc阶段,openshift已经不会通过image stream部署应用了(因为dc要部署的是app image,而非builder image)。

但是,如果我们手工触发一个应用的部署,但可以通过选择image stream tag(实际上,在创建S2I的时候,输入image stram,实际上也需要指定image stream的tag,只是如果不指定,默认使用latest的istag罢了)来实现的。

原因就在于:虽然image stream包含了docker-registry的地址,但image stream tag则指向docker image。我们当然可以通过指定image stream tag来部署应用了,这实际上也是间接方式的docker image部署(因此说,在这个部署阶段image stream包含的docker-registry是不会用到的,对它也没有什么意义。因为部署过程中,不会有bc过程,也就不会push镜像大到docker registry中)。

当然,我们也可以对一个build成功的app image导入image stream(实际上,在Openshift的角度,builder image和app image并无本质的区别,这两类的区别都是我们人为定的),然后根据mage stream的istag进行部署。例如上面实验中的172.30.174.125:5000/explore-00/nationalpark:latest,它是个app的image,我们可以通过如下命令为这个app image创建is:

#oc import-image nationalpark --from=172.30.174.125:5000/explore-00/nationalpark:latest --confirm

30b4fd950d88b1a695d8cef0f75c0019.png

总结:手工触发SI2的时候,必须要使用builder image的image stream。手工触发一个已有镜像的部署时,我们可以使用docker image的方式,也可以使用image stream tag的方式,只是需要确保image stream tag指向的是我们要部署的app image即可(当然,部署一个不含应用的builder image也可以,但这在生产中,没有什么实际意义。)

三、深度解析S2I的过程

3.1 S2I的概念

S2I是Openshift的原创。其概念上文也大致提到了,根据指定的builder imager和source code一起,build形成可被部署的docker image,并push到Openshift的docker registry中(内部集成镜像库)。而S2I,是Openshift实现CI/CD和Devops的框架。

34f9f389f9ff22440260cf03d9637eca.png

严格意义上讲,代码的构建,不仅可以通过指定building image和source code,还可以通过Binary和building image形成可被部署的镜像,我们称之为:B2I.

3.2 为什么Openshift既有S2I和B2I呢?

其实也很容易理解。代码的构建,可以通过JAR/WAR注入到Build而 image的方式进行构建(maven、nodejs、.net等方式),这是比较常见的。在这种方式下,我们在执行命令时,可以指定svn或git的地址,如以下格式的命令:

#oc new-appwebserver31-tomcat8.0-oraclejdk-openshift~http://gitlab.david.com/openshift/mspp.git

而还有一种情况,比如客户的应用,是基于tomcat的php。如果可能要做应用的增量发布。也就是说,将原有php的一部分内容,通过替换一部分新的jsp来实现,而应用的名称和版本都不发生变化,这时候,jsp就是一种Binary文件,而非source code。

如果触发B2I,命令行如以下格式的命令:

#oc start-builddzwls923c --from-dir=./ --follow=true --wait=true

上面命令的意思是:根据已经有的image stream为dzwls923c的builder image,加上当前目录下的binary文件,构建新的镜像,新的镜像将会被image stream tag(名字为dzwls923c:latest)映射。

3.2 S2I/B2I的4个脚本

一个符合S2I/B2I的building image,在bin目录下,是需要包含如下四个脚本的:

6126cfea1eab31e2a72ce195ed9c5aae.png

3.2.1 assemble脚本:

这个脚本负责将外部代码库的代码下载到本地,并且进行编译打包。

The assemble script is responsible for building the application artifacts from source and placing them into the appropriate directories inside the image. The workflow for the assemble script

3.2.2 run脚本:

这个脚本负责运行assemble编译好的应用。

The run script is responsible for executing your application.

3.2.3 save-artifacts脚本

save-artifacts脚本负责将构建所需要的所有依赖包收集到一个tar文件中。save-artifacts的好处是可以加速构建的过程。

The save-artifacts script is responsible for gathering all the dependencies into a tar file and streaming it to the standard output (eg. for Ruby - gems installed by Bundler, for Java - .m2 contents, etc.). The existence of this can speed up the following build processes. Note: it is critical that the save-artifacts script output only include the tar stream output and nothing else. This is handled by redirecting output to /dev/null in the sample script below.

3.2.4 usage脚本

usage脚本是告诉使用者如果使用镜像。

The usage script is for you (as the builder image author) to inform the user how to use your image.

在S2I的四个脚本中,通常我们只会用到assemble和run两个脚本。

3.4.从tomcat S2I builder image看assemble和run脚本的作用

3.4.1 assemble是做啥的?

assemble是做什么用的?它其实是做组装的用的,也就是构建source code,让它变为可以被运行的状态。

我们看一下tomcat 8.2的S2I building image其中的assemble脚本(篇幅有限,只列出核心部分,注释部分不列出):

echo "---> Installing application source..."

cp -Rf /tmp/src/. ./

ls -l ./

ls -l /tmp/src/

上面代码做的事情,是将负责bc的pod的/tmp/src目录下的source code(source code可以本身就在/tmp/src目录下,也可以被外部注入,下面代码将描述外部注入),拷贝到tomcat 的运行目录。

WORK_DIR=/tmp/src;

cd $WORK_DIR;

if [ ! -z ${SVN_URI} ] ; then

echo "Fetching source from Subversion repository ${SVN_URI}"

svn co ${SVN_URI} --username  ${SVN_USER} --password ${SVN_PWD} --no-auth-cache

export SRC_DIR=`basename $SVN_URI`

echo "Finished fetching source from Subversion repository ${SVN_URI}"

else

echo "SVN_URI not set, skip Subverion source download";

fi

上面代码做的事情,如果在执行S2I的命令中指定SVN的地址,那么构建过程会将svn代码库上的代码,拷贝到bc pod的/tmp/src目录下。

echo "---> Building application from source..."

# TODO: Add build steps for your application, eg npm install, bundle install, pip install, etc.

cd $WORK_DIR/$SRC_DIR/

#mvn package -Dmaven.test.skip=true;

${BUILD_CMD}

echo "---> Build application successfully."

上面代码做的事情,是通过marven,对下载下来的source code进行编译,打包。

find /tmp/src/ -name '*.war'|xargs -i cp -v {} /opt/app-root/jboss-eap-6.3/standalone/deployments

上面代码做的事情,将编译成功的war包,拷贝到tomcat的启动目录。这样,当tomcat运行的时候,编译好的war也就运行了。

3.4.2 run脚本是做啥的?

我们看tomcat 8.5的S2I building image其中的run脚本(篇幅有限,只列出核心部分,注释部分不列出):

#export JAVA_HOME=/opt/app-root/jdk1.6.0_38

cd /opt/app-root/jboss-eap-6.3

exec /opt/app-root/jboss-eap-6.3/bin/standalone.sh

从上面代码可以看出,run脚本就是启动tomcat的。

也就是说,在S2I的build阶段:assemble负责构建source code,让它变为可以被运行的状态。而run脚本负责运行构建好的应用。

而构建一个符合S2I标准的builder image(https://github.com/debianmaster/openshift-java-s2i-example),其流程如下:

9536f18657d8b05f67c989816604455d.png

在这张图中,S2I的脚本,就是上文提到的assemble、run等脚本(他们将会被拷贝到构建成功的app image中);Base image就是标准的docker image;builder image是符合S2I标准的docker image,这个image可以接收外部git、SVN的代码注入,最终形成app的image,然后被openshift push到docker-registery中;push成功以后,Openhift触发dc,根据这个应用的image部署pod。

为了方便理解B2I,这里我放两个脚本:B2I的assemble和run(这是我同事周荣写的两个脚本)。这两个脚本包含在一个weblogic的builder image中,用来实现增量发布:

assemble:

#!/bin/sh

echo "Start copy folders"

cp -a /tmp/deploy/src/* /opt/bea/user_projects/domains/base_domain/deploy

echo "After successful assembling"

Run:

!/bin/bash

exec /opt/bea/user_projects/domains/base_domain/startWebLogic.sh

触发B2I的命令:

#oc start-builddzwls923c --from-dir=./ --follow=true --wait=true

执行上面命令以后,当前目录下的binary文件,会被先拷贝的bc pod的 /tmp/deploy/src目录下,然后assemble脚本将这些binary文件从tmp/deploy/src目录拷贝到应用的运行目录/opt/bea/user_projects/domains/base_domain/deploy(拷贝两次的原因是为了实现松耦合,不同应用的运行目录是不一样的)。拷贝完成以后,实际上app的image已经形成。而run脚本,将会在app image被部署是运行,启动weblogic应用。

总结:我们在构建符合CI/CD的应用镜像,有Jenkins的slave pod构建和SI2/B2I两种方式。

而从经验上看,S2I更灵活一些。并且红帽的官网,registry.access.redhat.com,也为红帽的客户提供了已经做好的,符合S2I标准的builder image。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值