天天看点

Look,容器中应用的构建方法!

文章导读

  • 本文仅代表作者的个人观点;
  • 本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
  • 本文素材是红帽公司产品技术和手册;

一、什么是构建?

构建是:

  • 将输入参数转换为结果对象的过程
  • 用于将源代码转换为可运行容器映像的构建
  • 构建配置或构建配置文件的特点是: 构建策略、至少有一个来源
  • 策略决定了过程;源提供输入

容器中应用的四种构建策略:

4构建策略:

  • Source-to-Image (S2I)
  • Docker
  • Pipeline
  • Custom

作为构建输入提供的六种源类型:

  • Git
  • Dockerfile
  • Binary
  • Image
  • Input secrets
  • External artifacts

二进制源类型构建

  • 从本地文件系统到构建器的二进制格式流内容
  • 完全基于oc的起始构建

从二进制源代码开始构建,使用以下选项之一调用oc start-build:

Look,容器中应用的构建方法!

S2I增量构建重用以前构建的image中的工件 要创建增量构建,请修改BuildConfig策略定义:

Look,容器中应用的构建方法!
  • 指定支持增量构建的映像
  • Flag控制是否尝试增量构建
  • 如果构建器映像不支持增量构建,则构建仍会成功
  • 由于缺少save-artifacts脚本,日志消息表明增量构建不成功

将构建分配给特定节点

通过在构建配置的nodeSelector字段中指定标签,可以将构建目标定位为在特定节点上运行nodeSelector在调度构建窗体时与节点标签匹配的键值对的值集:

Look,容器中应用的构建方法!

与构建配置关联的构建仅在具有region = primary和nodetype = build标签的节

链接构建:

Build 1生成编译的工件

Build 2将工件放置在运行工件的单独image中。

链接构建示例

S2I构建与Docker构建相结合

在单独的运行时映像中编译工件和位置

Look,容器中应用的构建方法!

链接构建BuildConfig - 构建1

第一次构建产生包含WAR的图像,image被推送到工件image stream,输出工件路径取决于S2I构建器的汇编脚本

输出工件/wildfly/standalone/deployments/ROOT.war:

Look,容器中应用的构建方法!

链接构建BuildConfig - 构建2

在第一次构建时使用输出图像内的WAR文件路径的image stream

内联Dockerfile将WAR文件复制到运行时映像中:

Look,容器中应用的构建方法!

from指定Docker构建包括来自工件图像图像流的图像输出,即先前构建的目标

paths指定目标映像中包含哪些路径以包含在当前Docker构建中

运行时映像用作Docker构建的源映像

每次第一次构建成功完成时,ImageChange触发器都会调用第二次构建

使用OpenShift构建Maven Slave图像

OpenShift Build Configs可用于构建Maven Slave Image

Build Config必须位于将引用image的Jenkins项目中

示例:使用skopeo构建从属映像

Look,容器中应用的构建方法!

二、实验展现

登录Gogs上,在CICDLabs组织下,创建一个名为openshift- task- Private的新存储库,并确保它被设置为Private。

制作一个openshift任务库的副本并将其推入Gogs:

Look,容器中应用的构建方法!

注意GOGS储存库路径。

复制OpenSHIFT任务库并将其推入Gogs:

Look,容器中应用的构建方法!

创建一个指向此存储库的新应用程序:

首先创建一个gogs-secret

oc create secret generic gogs-secret --from-literal=username=david --from-literal=password=david

Look,容器中应用的构建方法!

创建应用:

oc new-app --template=eap70-basic-s2i --param APPLICATION_NAME=tasks --param SOURCE_REPOSITORY_URL=http://gogs.xyz-gogs80.svc.cluster.local:3000/CICDLabs/openshift-tasks-private1.git --param SOURCE_REPOSITORY_REF=master --param CONTEXT_DIR=/ --param MAVEN_MIRROR_URL=http://nexus3.xyz-nexus80.svc.cluster.local:8081/repository/maven-all-public

Look,容器中应用的构建方法!
Look,容器中应用的构建方法!

缓存的工件

JBoss EAP S2I builder映像支持在构建之间保存构建工件,这大大减少了构建时间。构建配置需要反映这一点。

更改构建配置以增量方式构建。将forcePull更改为false。这可以防止OpenShift每次在构建应用程序时都拉拽生成器映像,这将花费很多时间。

使用oc patch编辑bc,在sourceStrategy下添加增量标志,并将forcePull更改为false:

oc patch bc/tasks --patch='{"spec": {"strategy": {"sourceStrategy": {"incremental": true}}}}'

oc patch bc/tasks --patch='{"spec": {"strategy": {"sourceStrategy": {"forcePull": false}}}}'

Look,容器中应用的构建方法!
Look,容器中应用的构建方法!
Look,容器中应用的构建方法!

三、实验展现:环境介绍实现二进制构建

使用OpenJDK S2I映像演示使用现有Spring引导应用程序进行的二进制构建。

创建Spring启动应用程序

从https://github.com/wkulhanek/ola.git创建一个新的Java Spring引导应用程序。

使用带有1.2标签的redhat-openjdk18-openshift image stream来构建应用程序。

Look,容器中应用的构建方法!

确保在应用程序创建之后为其创建路由:

Look,容器中应用的构建方法!

确保应用在运行:

Look,容器中应用的构建方法!

部署使用二进制构建

使用二进制构建策略构建相同的应用程序,这意味着您首先在本地构建应用程序,然后创建一个二进制构建配置,最后使用本地构建的JAR文件作为二进制构建的输入启动一个二进制构建。

cd $HOME

git clone https://github.com/wkulhanek/ola.git

cd ola

mvn clean package

Look,容器中应用的构建方法!

查看构建成功的jar:

Look,容器中应用的构建方法!

java -jar $HOME/ola/target/ola.jar

Look,容器中应用的构建方法!

创建一个名为ola-binary的二进制构建,这个构建现在需要本地文件系统中的二进制部署工件。

Look,容器中应用的构建方法!

启动一个新的构建,并将编译后的文件流到构建中。确保在构建执行时遵循它。

Look,容器中应用的构建方法!
Look,容器中应用的构建方法!

当您执行oc start-build命令时,您将看到构建很快完成。二进制构建复制预构建的工件,并将副本移动到正确的目录中。在本例中,它复制ola。jar文件放入S2I映像,然后将其移动到/deployments。

构建完成后,从新创建的映像部署应用程序。

将应用程序公开为ola-binary route。

oc new-app ola-binary

oc expose svc/ola-binary --port=8080

Look,容器中应用的构建方法!
Look,容器中应用的构建方法!

四、实验展现:实现链接构建

链接构建,首先使用build image构建应用程序。然后将构建的工件部署到第二次运行时映像中。这只对Java或Go这样的编译语言有意义。

第一步,通过S2I构建应用镜像

在这个步骤中,我们就是上对go进行了编译。

go应用的源码:

https://github.com/tonykay/ose-chained-builds/blob/master/go-scratch/hello_world/main.go

package main

import (

"fmt"

"net/http"

)

const (

port = ":8080"

)

var calls = 0

func HelloWorld(w http.ResponseWriter, r *http.Request) {

calls++

fmt.Fprintf(w, "Hello, world! You have called me %d times.\n", calls)

fmt.Fprintf(w, "You have successfully finished this lab.\n")

}

func init() {

fmt.Printf("Started server at http://localhost%v.\n", port)

http.HandleFunc("/", HelloWorld)

http.ListenAndServe(port, nil)

}

func main() {}

然后,使用S2I - Go作为builder image,编译go源码。

首先,从DockerHub导入jorgemoralespou/ S2I - Go映像作为您的S2I Go映像。

Look,容器中应用的构建方法!

创建一个新的构建来编译示例Go应用程序。

要使用的上下文目录是/go-scratch/hello_world。

oc new-build s2i-go~https://github.com/tonykay/ose-chained-builds \

--context-dir=/go-scratch/hello_world --name=builder

遵循构建日志,等待Go应用程序编译,并等待映像完成构建,然后继续下一步。

我们查看go编译的日志:

[xiwei-redhat.com@bastion 0 ~]$ oc logs builder-1-build

===> Build started at Wed Aug 29 05:55:55 UTC 2018

---> Preparing source...

---> Downloading dependencies...

main

---> Building application source...

runtime/internal/sys

runtime/internal/atomic

runtime

errors

internal/race

sync/atomic

math

unicode/utf8

unicode

sort

container/list

crypto/subtle

sync

io

syscall

internal/singleflight

hash

crypto/cipher

hash/crc32

crypto/hmac

bytes

strings

bufio

path

strconv

math/rand

time

internal/syscall/unix

reflect

crypto

crypto/aes

crypto/rc4

encoding/base64

crypto/sha512

crypto/md5

crypto/sha1

crypto/sha256

encoding/pem

os

path/filepath

net

fmt

encoding/binary

crypto/des

io/ioutil

compress/flate

math/big

encoding/hex

log

internal/golang.org/x/net/http2/hpack

mime

mime/quotedprintable

net/http/internal

net/url

compress/gzip

crypto/elliptic

crypto/dsa

crypto/rand

encoding/asn1

crypto/rsa

net/textproto

crypto/ecdsa

crypto/x509/pkix

crypto/x509

mime/multipart

crypto/tls

net/http

main

===> Build completed at Wed Aug 29 05:56:37 UTC 2018

Binary at /opt/app-root/src/go/src/main

===> Elapsed time: 42 seconds

Pushing image docker-registry.default.svc:5000/xyz-builds80/builder:latest ...

Pushed 0/7 layers, 1% complete

Pushed 1/7 layers, 19% complete

Pushed 2/7 layers, 32% complete

Pushed 3/7 layers, 48% complete

Pushed 4/7 layers, 62% complete

Pushed 5/7 layers, 95% complete

Pushed 6/7 layers, 97% complete

Pushed 7/7 layers, 100% complete

Push successful

从上面标黄的部分可以看到,go构建成功以后的二进制文件,放到了/opt/app-root/src/go/src/main目录下。

当image构建成功以后,我们用将其部署成pod,也可以看到构建成功的文件:

Look,容器中应用的构建方法!

而/opt/app-root/src/go/src/main/main是个可运行的二进制文件:

Look,容器中应用的构建方法!

接下来,我们检查生成的构建程序imagestream:

Look,容器中应用的构建方法!

创建第二个(链接的)构建,它获取构建的工件(/opt/app-root/src/go/src/main/main)并将其部署到一个小的运行时映像scratch中。而scratch实际上是一个空文件。

步骤是:使用scratch Docker映像作为基础映像,然后,将/opt/app-root/src/go/src/main/main拷贝到/main目录下,然后使用docker build完成

oc new-build --name=runtime \

--source-image=builder \

--source-image-path=/opt/app-root/src/go/src/main/main:. \

--dockerfile=$'FROM scratch\nCOPY main /main\nEXPOSE 8080\nENTRYPOINT ["/main"]'

上面的命令行,相当于创建如下一个docker file,然后在构建的时候,执行docker build

Look,容器中应用的构建方法!

查看命令输出结果:

Look,容器中应用的构建方法!

[xiwei-redhat.com@bastion 0 /]$ oc logs -f bc/runtime

Pulling image "docker-registry.default.svc:5000/xyz-builds80/builder@sha256:3a674cf1b914720204509ab8da9af6f8255a51bd6f1709ea98cbeeabd2d6ebea" ...

Step 1/6 : FROM scratch

--->

Step 2/6 : COPY main /main

---> Using cache

---> 5c07c74949ea

Step 3/6 : EXPOSE 8080

---> Using cache

---> 10efcc15fba7

Step 4/6 : ENTRYPOINT /main

---> Using cache

---> e8c2c705d4ed

Step 5/6 : ENV "OPENSHIFT_BUILD_NAME" "runtime-1" "OPENSHIFT_BUILD_NAMESPACE" "xyz-builds80"

---> Running in 4a2467b85f03

---> 42ecaf43ece5

Removing intermediate container 4a2467b85f03

Step 6/6 : LABEL "io.openshift.build.name" "runtime-1" "io.openshift.build.namespace" "xyz-builds80"

---> Running in d6f93173f1a7

---> 06c1644c70e3

Removing intermediate container d6f93173f1a7

Successfully built 06c1644c70e3

Pushing image docker-registry.default.svc:5000/xyz-builds80/runtime:latest ...

Push successful

oc new-app runtime --name=my-application

oc expose svc/my-application

Look,容器中应用的构建方法!