S2I(Source to Image)解说-3. 尝试S2I增量构建(S2I incremental build)来介绍S2I的功能
使用go语言执行S2I增量构建,S2I增量构建是S2I的一个功能,通过读取与上次构建的差异来缩短构建过程。
S2I建构流程
执行以Go语言为例的S2I增量构建。
首先,将展示并解释执行S2I增量构建的命令步骤。然后,通过图示解释S2I构建后增量构建工作流的机制。另外,在下一节中将解释执行S2I增量构建所需的S2I脚本。
直到执行S2I增量构建
首先,我们要创建s2i builder image。就像上次构建用于Python环境的容器镜像一样,我们要进行容器镜像的构建。
- 首先,创建用于创建容器镜像的目录s2i-golang-incremental,并按照以下方式准备:
s2i-golang-incremental/
├── builder
│ ├── Dockerfile
│ └── s2i
│ └── bin
│ ├── assemble
│ ├── run
│ ├── save-artifacts
│ └── usage
└── test-app
├── app.go
├── go.mod
└── go.sum
在这里,builder目录和test-app目录分别是用于构建S2I构建器镜像和源代码的目录。builder目录中包含Dockerfile和S2I脚本。以下展示了这些源代码:
# ベースイメージ
FROM openshift/base-centos7
# ラベルを指定
LABEL io.openshift.s2i.scripts-url="image:///usr/libexec/s2i/bin"
# Goのバージョンとインストール先
ARG GO_VERSION=1.19
ARG GO_INSTALL_DIR=/usr/local/
# 環境変数
ENV APP_ROOT=/opt/app-root
ENV BUILDER_VERSION 1.0
ENV GOPATH ${APP_ROOT}/src/go
ENV PATH=${PATH}:${GOPATH}/bin:${GO_INSTALL_DIR}/go/bin
# Goのインストール
RUN curl -sSL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz -o go${GO_VERSION}.linux-amd64.tar.gz && \
tar -C ${GO_INSTALL_DIR} -xzf go${GO_VERSION}.linux-amd64.tar.gz && \
rm -f go${GO_VERSION}.linux-amd64.tar.gz && \
mkdir -p ${GOPATH}
# s2i scriptsをコンテナへコピー
COPY s2i /usr/libexec/s2i
# パーミッションを設定
RUN chown -R 1001:1001 ${APP_ROOT} && \
chown -R 1001:1001 /usr/libexec/s2i
# default user
USER 1001
# default CMD for the image
CMD ["/usr/libexec/s2i/usage"]
#!/bin/bash -e
# usageスクリプトを実行
if [[ "$1" == "-h" ]]; then
exec /usr/libexec/s2i/usage
fi
# S2I増分ビルドを行うためのスクリプト
echo
echo "---> Checking for cache..."
if [ "$(ls /tmp/artifacts 2>/dev/null)" ]; then
pushd /tmp/artifacts >/dev/null
echo "-----> Pulling cache..."
shopt -s dotglob
if [ -d src ]; then
echo "Restoring cache ${GOPATH}/src/..."
mv src ${GOPATH}/src
fi
if [ -d pkg ]; then
echo "Restoring cache ${GOPATH}/pkg/..."
mv pkg ${GOPATH}/pkg
fi
shopt -u dotglob
popd >/dev/null
fi
# Goソースファイルのビルド
echo
echo "---> Building application from source..."
pushd /tmp/src/ >/dev/null
go build -o ${APP_ROOT}/bin/app
popd >/dev/null
${APP_ROOT}/bin/app
pushd ${GOPATH} >/dev/null
if [ -d src ]; then
chmod -R +w src
tar cf - src
fi
if [ -d pkg ]; then
chmod -R +w pkg
tar cf - pkg
fi
popd >/dev/null
上述代码的解释将在s2i-scripts中进行。此外,test-app中包含了本次使用的golang示例文件。以下是源代码:
package main
import (
"fmt"
_ "github.com/golang/mock/gomock"
_ "github.com/gorilla/mux"
)
func main() {
greeting := fmt.Sprintf("Hello, %s", "World")
fmt.Println(greeting)
}
在具有Dockerfile和s2i脚本的目录中执行以下操作。假设当前位于s2i-golang目录中。然后,使用以下命令构建s2i构建器镜像:
$ docker build -t s2i-golang s2i-golang/builder
使用s2i-golang/builder目录下的s2i脚本和Dockerfile来执行此命令,以构建名为s2i-golang的s2i builder镜像。
首先,构建增量迭代之前的容器镜像,然后执行第1次s2i构建。
$ s2i build --copy test-app/ s2i-golang my-go-app
–copy : githubなどのリポジトリの代わりにローカルのディレクトリからアプリケーションソースコードをコンテナへコピーするためのオプションである.
追加したライブラリを使用してS2i増分ビルドを行います。
package main
import (
"fmt"
_ "github.com/golang/mock/gomock"
_ "github.com/gorilla/mux"
_ "github.com/joho/godotenv"
_ "github.com/labstack/echo/v4"
_ "github.com/sirupsen/logrus"
_ "gorm.io/driver/mysql"
)
func main() {
greeting := fmt.Sprintf("Hello, %s", "James")
fmt.Println(greeting)
}
$ go mod tidy
执行此操作将更新go.mod和go.sum文件,并添加库信息。然后,执行S2I增量构建。
$ s2i build --rm --copy test-app/ s2i-golang my-go-app --incremental=true
这个命令是用于增量构建名为my-go-app的容器镜像。命令的参数如下:
–rm : 増分前のコンテナイメージを削除する
–incremental=true : 増分ビルドを行うためのオプションである
S2I增分构建的机制
执行S2I增量构建时,将执行以下过程:
-
- 查找在本地存在的上次创建的S2I容器镜像(可以使用docker images命令查看的容器镜像)。
从上次构建的镜像中启动容器。
提取启动的容器中的库(每种语言编写的二进制文件),将其打包成tar存档,并将其流式传输到标准输出。
将新的容器镜像作为标准输入接收tar存档,并由S2I进行解压。
然后执行assemble,构建新的容器镜像。
关于s2i-scripts的说明
在这里,我们将详细解释S2I脚本中所记录的内容。
组合剧本
#!/bin/bash -e
# usageスクリプトを実行
if [[ "$1" == "-h" ]]; then
exec /usr/libexec/s2i/usage
fi
# S2I増分ビルドを行うためのスクリプト
echo
echo "---> Checking for cache..."
if [ "$(ls /tmp/artifacts 2>/dev/null)" ]; then
pushd /tmp/artifacts >/dev/null
echo "-----> Pulling cache..."
shopt -s dotglob
if [ -d src ]; then
echo "Restoring cache ${GOPATH}/src/..."
mv src ${GOPATH}/src
fi
if [ -d pkg ]; then
echo "Restoring cache ${GOPATH}/pkg/..."
mv pkg ${GOPATH}/pkg
fi
shopt -u dotglob
popd >/dev/null
fi
# Goソースファイルのビルド
echo
echo "---> Building application from source..."
pushd /tmp/src/ >/dev/null
go build -o ${APP_ROOT}/bin/app
popd >/dev/null
依次查看脚本的内容。
echo "---> Checking for cache..."
if [ "$(ls /tmp/artifacts 2>/dev/null)" ]; then
pushd /tmp/artifacts >/dev/null
echo "-----> Pulling cache..."
shopt -s dotglob
检查/tmp/artifacts/目录是否存在。如果存在,则使用pushd /tmp/artifacts >/dev/null将目录更改为/tmp/artifacts/。通过使用pushd命令,可以在保存移动目标历史的堆栈的同时,切换到目录中。>/dev/null是一个输出丢弃的重定向。通过shopt -s dotglob启用dotglob选项。
shopt命令用于更改shell的选项,-s用于确定要更改的选项,dotglob选项的启用可以将以”.”开头的文件包括在文件名展开中。
if [ -d src ]; then
echo "Restoring cache ${GOPATH}/src/..."
mv src ${GOPATH}/src
fi
如果存在src文件,则将src目录移动到${GOPATH}/src。
if [ -d pkg ]; then
echo "Restoring cache ${GOPATH}/pkg/..."
mv pkg ${GOPATH}/pkg
fi
shopt -u dotglob
popd >/dev/null
fi
如果存在pkg目录,则移动到${GOPATH}/pkg。使用shopt -u dotglob命令禁用dotglob选项。使用pushd命令将保存在堆栈中的目录历史移动到/tmp/artifacts/。
echo
echo "---> Building application from source..."
pushd /tmp/src/ >/dev/null
go build -o ${APP_ROOT}/bin/app
popd >/dev/null
在/tmp/src目录中,将历史记录作为堆栈保存,并进行移动。对于${APP_ROOT}/bin/app目录,执行go build。使用popd命令返回之前保存在堆栈中的目录。
保存工件脚本
“save-artifacts” 脚本是进行 S2I 增量构建所必需的脚本。该脚本的作用是缓存增量之前的信息。我们来详细查看脚本的内容。
pushd ${GOPATH} >/dev/null
if [ -d src ]; then
chmod -R +w src
tar cf - src
fi
if [ -d pkg ]; then
chmod -R +w pkg
tar cf - pkg
fi
popd >/dev/null
解释命令:
chmod -R +w src
tar cf - src
使用chmod命令和tar命令进行的操作
-
- chmod : ファイルやディレクトリのパーミッション (アクセス権限) を変更するコマンドである.
- ここでは,srcディレクトリ内のすべてのファイルとサブディレクトリへの書き込み権限を追加している。オプション-Rはディレクトリ内のすべてのファイルとサブディレクトリに対して再帰的 (Recursive)1に操作を行う.また,オプション+wはsrcへ書き込み権限を追加する.
-
- tar : ファイルやディレクトリをアーカイブ(まとめてひとつのファイルにすること)するために使用される.
- ここではsrcディレクトリをtarアーカイブにパッキングし、そのアーカイブを標準出力に書き出す操作をしている。ここで,cfは、アーカイブを作成するためのオプションであり、-はアーカイブを標準出力に書き出すことを意味する。
正在检查S2i增量构建中的日志
日志级别选项 (Rogu opushon)
在s2i build命令中,通过使用–lolevel选项可以详细查看构建过程的日志。只需执行以下命令即可。
$ s2i < 他のオプション > --loglevel=5 &> /tmp/s2i.log
日志级别从级别0到5。
-
- Level 0 – assemble と assemble-runtimeスクリプトを実行しているコンテナからの出力と遭遇したエラーすべてを表示する.
-
- Level 1 – 実行されたプロセスに関して標準的な情報を表示する
-
- Level 2 – 実行されたプロセスに関して非常に詳細な情報を表示する.
-
- Level 3 – 実行されたプロセスに関して非常に詳細な情報を、また,tar の内容も表示する。
-
- Level 4 – 現在はlevel 3と同じ情報が表示される.
- Level 5 – 実行されたプロセスに関して非常に詳細な情報,tar の内容Dockerレジストリの認証情報、コピーされたソースファイルを一覧表示する。
观测日志信息
通过观察构建过程的日志信息,可以了解到S2I如何执行构建以及每个选项执行了什么。
首先,让我们来看一下增量构建选项–incremental的执行过程:
首先,如果没有名为my-go-app的s2i容器映像(应用程序映像),则通过以下命令执行s2i构建:
$ s2i build --rm --copy test-app/ s2i-golang my-go-app --incremental=true
–增量是这样的:
然后,将获得以下日志:
docker.go:487] Image "my-go-app:latest" not available locally, pulling ...
docker.go:566] pulling image error : Error response from daemon: pull access denied for my-go-app, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
sti.go:497] Unable to pull previously built image "my-go-app": unable to get my-go-app:latest
sti.go:215] Clean build will be performed
-
- 首先,由于本地不存在my-go-app图像,所以尝试进行拉取。
-
- 此时,由于my-go-app图像不存在,导致拉取图像错误。
-
- 因此,s2i无法拉取my-go-app图像。
- 将执行干净的新构建。
换言之,如果没有可供参考的镜像,就会执行普通的s2i构建。
接下来,如果存在名为my-go-app的容器映像,则执行S2I增量构建。
$ s2i build --rm --copy test-app/ s2i-golang my-go-app --incremental=true
在这种情况下,我们可以获得以下类似的日志记录:
docker.go:491] Using locally available image "my-go-app:latest"
sti.go:213] Existing image for tag my-go-app detected for incremental build
sti.go:218] Performing source build from test-app/
-
- 声明使用本地存在的图像my-go-app:latest
-
- 确定本地存在可用于增量构建的镜像my-go-app:latest。
执行从test-app/目录下构建源码的操作。
sti.go:524] Saving build artifacts from image my-go-app to path /tmp/s2i2961405759/upload/artifacts
- イメージmy-go-app内のpath/tmp/s2i2961405759/upload/artifactsからbuild artifactsを保存する。
接下来,为了提取artifacts,使用assemble函数在user1001中。通过s2i从上次构建的镜像中启动容器,从artifacts中提取包,并将其打包成tar存档文件。将tar存档文件导入标准输出,新的构建容器接收作为标准输入并进行解压缩。
sti.go:550] Using assemble user "1001" to extract artifacts
docker.go:731] Image sha256:c3a02d781... contains io.openshift.s2i.scripts-url set to "image:///usr/libexec/s2i/bin"
docker.go:805] Base directory for S2I scripts is '/usr/libexec/s2i/bin'. Untarring destination is '/tmp'.
docker.go:971] Creating container with options {Name:"s2i_my_go_app_f4cb4822" Config:{Hostname: Domainname: User:1001 ...
docker.go:1003] Attaching to container ...
docker.go:1014] Starting container ...
接下来,我们将不断地进行创建和提取操作,将与库相关的信息收集到tar文件中。
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod/github.com
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod/github.com/golang
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod/github.com/golang/mock@v1.6.0
tar.go:391] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod/github.com/golang/mock@v1.6.0/ci
tar.go:400] Creating directory /tmp/s2i3409979752/upload/artifacts/pkg/mod/github.com/golang/mock@v1.6.0/ci
......
......
......
tar.go:424] Done extracting tar stream
另外,对于S2I在增量前构建中下载的库,会使用缓存。对于新增加的库,我们可以看到正在重新下载:
- 増分前のログ
sti.go:713] ---> Checking for cache...
sti.go:713]
sti.go:713] ---> Building application from source...
sti.go:717] go: downloading github.com/golang/mock v1.6.0
sti.go:717] go: downloading github.com/gorilla/mux v1.8.0
docker.go:1070] Invoking PostExecute function
- 増分後のログ
sti.go:713] ---> Checking for cache...
sti.go:713] -----> Pulling cache...
sti.go:713] Restoring cache /opt/app-root/src/go/pkg/...
sti.go:713]
sti.go:713] ---> Building application from source...
sti.go:717] go: downloading github.com/joho/godotenv v1.5.1
.....
.....
.....
sti.go:717] go: downloading golang.org/x/text v0.11.0
docker.go:1070] Invoking PostExecute function
这次,关于S2I的解释文章到此结束了。