文章导读
- 本文仅代表作者的个人观点;
- 本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
- 本文素材是红帽公司产品技术和手册;
一、什么是构建?
构建是:
- 将输入参数转换为结果对象的过程
- 用于将源代码转换为可运行容器映像的构建
- 构建配置或构建配置文件的特点是: 构建策略、至少有一个来源
- 策略决定了过程;源提供输入
容器中应用的四种构建策略:
4构建策略:
- Source-to-Image (S2I)
- Docker
- Pipeline
- Custom
作为构建输入提供的六种源类型:
- Git
- Dockerfile
- Binary
- Image
- Input secrets
- External artifacts
二进制源类型构建
- 从本地文件系统到构建器的二进制格式流内容
- 完全基于oc的起始构建
从二进制源代码开始构建,使用以下选项之一调用oc start-build:
S2I增量构建重用以前构建的image中的工件 要创建增量构建,请修改BuildConfig策略定义:
- 指定支持增量构建的映像
- Flag控制是否尝试增量构建
- 如果构建器映像不支持增量构建,则构建仍会成功
- 由于缺少save-artifacts脚本,日志消息表明增量构建不成功
将构建分配给特定节点
通过在构建配置的nodeSelector字段中指定标签,可以将构建目标定位为在特定节点上运行nodeSelector在调度构建窗体时与节点标签匹配的键值对的值集:
与构建配置关联的构建仅在具有region = primary和nodetype = build标签的节
链接构建:
Build 1生成编译的工件
Build 2将工件放置在运行工件的单独image中。
链接构建示例
S2I构建与Docker构建相结合
在单独的运行时映像中编译工件和位置
链接构建BuildConfig - 构建1
第一次构建产生包含WAR的图像,image被推送到工件image stream,输出工件路径取决于S2I构建器的汇编脚本
输出工件/wildfly/standalone/deployments/ROOT.war:
链接构建BuildConfig - 构建2
在第一次构建时使用输出图像内的WAR文件路径的image stream
内联Dockerfile将WAR文件复制到运行时映像中:
from指定Docker构建包括来自工件图像图像流的图像输出,即先前构建的目标
paths指定目标映像中包含哪些路径以包含在当前Docker构建中
运行时映像用作Docker构建的源映像
每次第一次构建成功完成时,ImageChange触发器都会调用第二次构建
使用OpenShift构建Maven Slave图像
OpenShift Build Configs可用于构建Maven Slave Image
Build Config必须位于将引用image的Jenkins项目中
示例:使用skopeo构建从属映像
二、实验展现
登录Gogs上,在CICDLabs组织下,创建一个名为openshift- task- Private的新存储库,并确保它被设置为Private。
制作一个openshift任务库的副本并将其推入Gogs:
注意GOGS储存库路径。
复制OpenSHIFT任务库并将其推入Gogs:
创建一个指向此存储库的新应用程序:
首先创建一个gogs-secret
oc create secret generic gogs-secret --from-literal=username=david --from-literal=password=david
创建应用:
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
缓存的工件
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}}}}'
三、实验展现:环境介绍实现二进制构建
使用OpenJDK S2I映像演示使用现有Spring引导应用程序进行的二进制构建。
创建Spring启动应用程序
从https://github.com/wkulhanek/ola.git创建一个新的Java Spring引导应用程序。
使用带有1.2标签的redhat-openjdk18-openshift image stream来构建应用程序。
确保在应用程序创建之后为其创建路由:
确保应用在运行:
部署使用二进制构建
使用二进制构建策略构建相同的应用程序,这意味着您首先在本地构建应用程序,然后创建一个二进制构建配置,最后使用本地构建的JAR文件作为二进制构建的输入启动一个二进制构建。
cd $HOME
git clone https://github.com/wkulhanek/ola.git
cd ola
mvn clean package
查看构建成功的jar:
java -jar $HOME/ola/target/ola.jar
创建一个名为ola-binary的二进制构建,这个构建现在需要本地文件系统中的二进制部署工件。
启动一个新的构建,并将编译后的文件流到构建中。确保在构建执行时遵循它。
当您执行oc start-build命令时,您将看到构建很快完成。二进制构建复制预构建的工件,并将副本移动到正确的目录中。在本例中,它复制ola。jar文件放入S2I映像,然后将其移动到/deployments。
构建完成后,从新创建的映像部署应用程序。
将应用程序公开为ola-binary route。
oc new-app ola-binary
oc expose svc/ola-binary --port=8080
四、实验展现:实现链接构建
链接构建,首先使用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映像。
创建一个新的构建来编译示例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,也可以看到构建成功的文件:
而/opt/app-root/src/go/src/main/main是个可运行的二进制文件:
接下来,我们检查生成的构建程序imagestream:
创建第二个(链接的)构建,它获取构建的工件(/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
查看命令输出结果:
[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