如何用S2I创建一个builder image


【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外,针对Paas平台的特点,对Kubernetes进行了深度开发。文中介绍的S2I就是OpenShift之所以区别于Kubernetets,能成为PaaS平台的实现中一个主要环节。通过选择对应的image,编写assmble和run的脚本,最终生成包含依靠源代码生成的可以执行文件的docker image,并利用OpenShift现有的体系架构(BC和DC)直接发布,完成所有的应用的生命周期管理,让开发者真正仅仅关注与代码本身,这才是一个真正的Paas平台需要实现的。
openshift.png

Source-To-Image(S2I)是一个在创建builder image时可以独立运行的常用的工具。在OpenShiftV3里,它还是一个创建应用程序的主要手段。这是因为它具备如下优势:
  • 速度快 —— 用S2I在应用程序编译的过程中可以在不创建新的layer的情况下执行大量的复杂操作,导致编译速度加快。
  • 可以打补丁 —— 比如当你发现你的image存在一个安全问题时,S2I可以帮助你持续不断的rebuild你的应用程序。
  • 用户效率高 —— S2I防止开发人员随意的用yum对image进行安装操作,因为这种操作会拖慢开发迭代的效率。
  • 生态性 —— S2I鼓励共享一个生态系统的image,你可以利用它去运行你的应用程序。


这篇文档就是关于如何去创建一个简单的S2I的builder image。

总的来说,S2I利用源代码和builder Docker image去生成一个新的Docker image。S2I这个项目本身其实已经包含了一些常用的Docker builder image,比如用来创建PythonRuby的builder image,当然你也可以根据需要去扩展它。

在讨论如何去创建一个builder image之前,我们先来解释一下S2I是如何工作的以及build image过程中builder image的角色。S2I(Source-To-Image),从名字上可以知道,它其实是负责把你应用程序源代码转换成可以在OpenShiftV3或者用docker run运行的Docker Image。所以这个Builder image包含了一些能够生成可执行镜像的所需的能力(也成为:build artifact)。整个的build过程包含对生成的image的调用,分几个步骤完成。在这边文章里我们用一个最简单的流程说明:
  1. 下载S2I脚本(或者用builder image 里面的)
  2. 下载你的应用程序代码
  3. S2I stream脚本们和应用程序代码到builder image的容器里
  4. 在Build image里执行定于好的编译脚本(这个脚本就是上一步stream进去的)
  5. 保存这个容器到一个新的image


当然,为了让builder image完成所有这些工作,我们还需给它一些传入一些content。首先,因为builder image 负责实际意义上的build 你的源代码,所以它必须包含build和运行这个应用程序所依赖的libray和tools。比如,一个tomcat的builder image需要包含Tomcat,JDK和Maven。一个Python builder image包含Gunicorn或者cherry.py,SciPy的二进制包和pip。其次,它还需要包含一些执行build和run操作的逻辑脚本。这部分被下面两个S2I的脚本调用:
  • assemble——负责build一个应用程序
  • run——负责运行一个应用程序


下面的我将介绍如何利用lighttpd server的docker image构造一个builder image去运行一个静态的html页面。

当然,这其中还包括了用GitHub存储创建过程中产生的文件。

现在,我们开始我的旅程,首先从https://github.com/openshift/s ... ases/ 获取最近版本的S2I(作者在写此篇文章时的最新版是1.0.9,译者翻译时是1.1.8)

步骤 1

S2I 是一个独立的命令,能够创建一个builder image所需要的目录结构,从这里可以得到更多关于它的信息,现在我们用下面命令创建出所必须的结构:
s2i create lighttpd-centos7 s2i-lighttpd

我调用了s2i create命令传入了我想要的builder image的名字(lighttpd-centos7)它的目录结构也已经被创建完成(s2i-lighttpd)。如果这个目录不存在,这个命令就会为你创建它。所以,运行上面那个命令你将得到下面这个目录结构:
s2i-lighttpd/
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│   └── bin
│       ├── assemble
│       ├── run
│       ├── save-artifacts
│       └── usage
└── test
├── run
└── test-app
    └── index.html


步骤 2

为了定制builder image,我们需要修改那个刚刚用S2I命令生成的Dockerfile
# We are basing our builder image on openshift base-centos7 image
FROM openshift/base-centos7

Inform users who's the maintainer of this builder image

MAINTAINER Maciej Szulik <maszulik@redhat.com>

Inform about software versions being used inside the builder

ENV LIGHTTPD_VERSION=1.4.35

Set labels used in OpenShift to describe the builder images

LABEL io.k8s.description="Platform for serving static HTML files" \
  io.k8s.display-name="Lighttpd 1.4.35" \
  io.openshift.expose-services="8080:http" \
  io.openshift.tags="builder,html,lighttpd"

Install the required software, namely Lighttpd and

RUN yum install -y lighttpd && \
# clean yum cache files, as they are not needed and will only make the image bigger in the end
yum clean all -y

Defines the location of the S2I

Although this is defined in openshift/base-centos7 image it's repeated here

to make it clear why the following COPY operation is happening

LABEL io.openshift.s2i.scripts-url=image:///usr/local/s2i

Copy the S2I scripts from ./.s2i/bin/ to /usr/local/s2i when making the builder image

COPY ./.s2i/bin/ /usr/local/s2i

Copy the lighttpd configuration file

COPY ./etc/ /opt/app-root/etc

Drop the root user and make the content of /opt/app-root owned by user 1001

RUN chown -R 1001:1001 /opt/app-root

Set the default user for the image, the user itself was created in the base image

USER 1001

Specify the ports the final image will expose

EXPOSE 8080

Set the default CMD to print the usage of the image, if somebody does docker run

CMD ["usage"] 

步骤 3

一旦Dockerfile修改完成,我们就可以填充builder image的剩余部分,也就是S2I的那些脚本。首先我们从assemble脚本开始, 它是负责build我们的应用。在这个例子里,它仅仅负责拷贝源代码到Lighttpd server的目录下:
#!/bin/bash -e
#

S2I assemble script for the 'lighttpd-centos7' image.

The 'assemble' script builds your application source ready to run.

#

For more information refer to the documentation:

https://github.com/openshift/s ... ge.md

#

echo "---> Installing application source"
cp -Rf /tmp/src/. ./ 

默认情况下,s2i build命令将源代码放在/tmp/src目录下,在build过程中这个目录下的源代码文件和其他相关文件会被使用。当然你也可以通过修改io.openshift.s2i.destination这个label或者传入--destination这个参数去修改它,如果是那样的话源代码会被放在你指定的目录下的src子目录下。在相面cp命令中的第二个参数./是用来设置在openshift/base-centos7中的工作目录,也就是/opt/app-root/src

好,现在让我去处理第二个脚本文件run,故名思义它负责运行我们的应用程序。在我们的例子中,它会启动lighttd server:
#!/bin/bash -e
#

S2I run script for the 'lighttpd-centos7' image.

The run script executes the server that runs your application.

#

For more information see the documentation:

https://github.com/openshift/s ... ge.md

#

exec lighttpd -D -f /opt/app-root/etc/lighttpd.conf 

在上面的命令中我们用execrunlighttpd的进程。这是为了所有由Docker发送给lighttpd的信号都会输出到stdoutstderr

因为在我们的例子中,我们不关注增量build,所以我们可以去掉save-artifacts这个脚本。最后我们写一些关于如何使用这个build image的方法在usage脚本中:
#!/bin/bash -e

cat <<EOF
This is the lighttpd-centos7 S2I image:
To use it, install S2I: https://github.com/openshift/source-to-image

Sample invocation:

s2i build https://github.com/soltysh/sti-lighttpd.git --context-dir=test/test-app/ lighttpd-centos7 sample-app

You can then run the resulting image via:
docker run -p 8080:8080 sample-app
EOF 

注意:修改这个脚本的执行权限,推荐为755

步骤 4

我们builder image还剩最后的一部分,一个关于lighttpd的配置文件。这个配置文件会被Dockerfilerun脚本去使用。如果没有这个配置文件,lighttpd恐怕不能够正常启动。我用下面的配置为lighttpd生成配置文件,将其保存在s2i-lighttpd/etc/lighttpd.conf
# directory where the documents will be served from
server.document-root = "/opt/app-root/src"

port the server listens on

server.port = 8080

default file if none is provided in the URL

index-file.names = ( "index.html" )

configure specific mimetypes, otherwise application/octet-stream will be used for every file

mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"


这是一个关于lighttpd的最简单配置文件,它仅仅设置了:
  • lighttpd处理的源代码路径(/opt/app-root/src)
  • lighttpd监听的端口号(8080)
  • 默认文件的路径(index.html)
  • mimetype影射属性


到这步,我们的builder image已经做完了。我们能够确定的是s2i-lighttpd目录下的make命令会在适当的时候去build它,当然你已经发现了这是因为里面有一个Makefile,里面调用了docker build命令。

步骤 5

现在我们用一个简单的应用程序去测试这个builder image。我创建了一个index.htmls2i-lighttpd/test/test-app目录下:(译者注:在现在的1.18版本中,这个测试文件会被自动创建)
<!doctype html>
<html>
<head>
<title>test-app</title>
</head>
<body>
<h1>Hello from lighttpd served index.html!</h1>
</body>

有了这个文件,我们现在可以运行s2i build了。在s2i-lighttpd目录下执行下面的命令:
s2i build test/test-app/ lighttpd-centos7 sample-app

我们从test/test-app目录下创建了一个应用程序,用了我们刚刚的build image(lighttpd-centos7)。生成了一个叫sample-app的image。默认的S2I build 会打印出所有的由assemble产生的输出,所以你会看到下面的输出在你的终端上(你可以忽略关于用本地builder image和非git目录产生的warning):
---> Installing application source

现在我们实际的测试一下生成的image。运行这个image,暴露容器的8080端口到主机上:
docker run -p 8080:8080 sample-app

现在你可以通过http://localhost:8080/访问到index.html的内容了。

步骤 6

这里面还有一些需要补充的部分:tests,大多数情况下可以用s2i-lighttpd/test/run这个脚本去处理,假设你在Dockerfile里选择默认的端口。在这个情况下,你可以在s2i-lighttpd目录下运行下面这个命令:
make test

注意:设置test/run的执行权限,建议设置成700。

这个脚本会运行s2i build(请却保s2i在你的环境变量path里)用test-app作为应用程序源代码,并且执行一些测试以确保image的可用性。这些测试包括:
  • 检查s2i build运行没有出错
  • 检查usage脚本运行正确
  • 测试最后生成的image
  • 检查运行应用程序的容器有正确的响应


在这个例子里我没有增加对test/run的说明,因为那个文件太长了。感兴趣的读者可以去看一下https://github.com/soltysh/sti-lighttpd/。提交的历史可以准确地显示本文中给出的每一个步骤。

现在祝贺你,已经有了一个可以使用的S2I builder image,使用S2I(Source Build strategy in OpenShift),处理一个在git代码仓库里的html文件,并且生成一个新的image去对外提供访问。总的来说,通过修改assemblerun这两个脚本可以很容易的定义其他类型的builder image,然后用相应的image去运行对应类型的应用程序。

原文连接:How to Create an S2I Builder Image (翻译:王晓轩)

0 个评论

要回复文章请先登录注册