- OpenShift在企業中的實踐:PaaS DevOps微服務(第2版)
- 魏新宇 郭躍軍
- 5876字
- 2021-11-05 10:17:24
4.2.6 S2I實現應用容器化
1.OpenShift S2I的介紹
Source-to-Image(S2I)是紅帽OpenShift開發的一個功能組件。目前可以獨立于OpenShift運行。在社區里,被稱為Java S2I,GitHub的地址為https://github.com/openshift/source-to-image/blob/master/README.md。
Java S2I容器鏡像使開發人員能夠通過指定應用程序源代碼或已編譯的Java二進制文件的位置,在OpenShift容器平臺中按需自動構建、部署和運行Java應用程序。此外,S2I還支持Spring Boot、Eclipse Vert.x和WildFly Swarm。使用S2I的優勢在于:
·簡單而靈活:Java S2I鏡像可以處理復雜的構建結構,但默認情況下它會假定在成功構建后/target目錄中將運行要運行的JAR。我們也可以使用環境變量ARTIFACT_DIR指定要運行的JAR。此外,如果構建生成多個JAR文件,則可以使用環境變量JAVA_APP_JAR指定要運行的JAR文件。但是,在大多數情況下,我們所要做的就是直接指向源存儲庫,Java S2I容器鏡像將自動完成配置。
·自動JVM內存配置:在OpenShift中通過Quota做資源限制。如果存在此類限制,Java S2I鏡像將自動采用JVM內存設置,避免JVM過量使用內存。
·控制鏡像大小:為了使鏡像保持最小化,可以在構建最終容器鏡像之前在S2I腳本中刪除Maven本地倉庫的數據。將環境變量MAVEN_CLEAR_REPO設置為true,則會在構建過程中刪除Maven本地倉庫。
2.OpenShift S2I原理解析
OpenShift可以直接基于Git存儲庫中存儲的源代碼來創建應用。oc new-app指定Git的URL后OpenShift會自動檢測應用所用的編程語言,并選擇合適的Builder鏡像。當然,我們也可以手工指定Builder鏡像。
那么,S2I應該如何識別Git上的內容來自動檢測編程語言呢?它會檢測Git上的特征文件,然后按照表4-1的方式選擇構建方式。
例如,如果Git上有pom.xml文件,S2I將會因為需要使用jee的構建語言,所以查找jee的Image Stream。
表4-1 S2I特征文件與構建方式

OpenShift會采用多步算法來確定URL是否指向源代碼存儲庫,如果是,則還會采用該算法來確定應由哪個Builder鏡像來執行構建。以下是該算法的大致執行過程:
1)如果S2I能夠成功訪問指定源碼地址的URL,則需S2I開始進行下一步。
2)OpenShift檢索Git存儲庫,搜索名為Dockerfile的文件。如果找到了Dockerfile,則會觸發容器鏡像構建。如果沒找到Dockerfile,則進行第3步。
3)OpenShift按照表4-1的方式判斷源碼的類型匹配構建語言,自動查找Image Stream。搜索到的第一個匹配的Image Stream會成為S2I Builder鏡像。
4)如果沒有匹配的構建語言,OpenShift會搜索名稱與構建語言名稱相匹配的Image Stream。搜索到的第一個匹配項會成為S2I Builder鏡像。
S2I構建過程涉及三個基本組件:應用程序的源代碼、S2I腳本、S2I Builder鏡像。它們組合在一起構建成最終的應用鏡像,實現應用容器化。
S2I的本質是按照一定的規則執行構建過程,依賴于一些固定名稱的S2I腳本,這些腳本在各個階段執行構建工作流程。它們的名稱和作用分別如下:
·assemble:負責將已經下載到本地的外部代碼進行編譯打包,然后將打包好的應用包拷貝到鏡像對應的運行目錄中。
·run:負責啟動運行assemble編譯、拷貝好的應用。
·usage:告訴使用者如何使用該鏡像。
·save-artifacts:腳本負責將構建所需要的所有依賴包收集到一個tar文件中。save-artifacts的好處是可以加速構建的過程。
·test/run:通過test/run腳本,可以創建簡單的流程來驗證鏡像是否正常工作。
在上面的五個腳本中,assemble和run是必須有的,其他三個腳本是可選的。這些腳本可以由多種方式提供,默認使用存放在鏡像/usr/local/s2i目錄下的腳本文件,也可以由源代碼倉庫或HTTP Server提供這些腳本。
有了這五個S2I腳本之后,就可以按照如下構建流程構建應用鏡像了。
·啟動構建之后,啟動builder容器,首先實例化基于S2I Builder的容器。從Git上獲取應用源代碼,創建一個包含S2I腳本(如果Git上沒有,則使用Builder鏡像中的腳本)和應用源碼的tar文件,將tar文件傳入S2I Builder實例化的容器中。
·tar文件被解壓縮到S2I builder容器中io.openshift.s2i.destination標簽指定的目錄位置,默認是/tmp。
·如果是增量構建,assemble腳本會先恢復被save-artifacts腳本保存的build artifacts。
·assemble腳本從源代碼構建應用程序,并將生成的二進制文件放入應用運行的目錄。
·如果是增量構建,執行save-artifacts腳本并保存所有構建以來的artifacts到tar文件。
·完成assemble腳本執行以后生成最終應用鏡像。
·builder容器調用podman命令,將構建好的應用鏡像推送到內部鏡像倉庫。
·應用鏡像推送到內部鏡像倉庫之后,觸發DeploymentConfig中的Image Stream觸發器自動部署應用鏡像。
·應用鏡像運行,并執行RUN腳本配置應用參數以及啟動應用。
在介紹了S2I的原理后,接下來通過分析一個紅帽的Builder鏡像,進一步加深對S2I的理解。
3.紅帽Builder鏡像分析
目前紅帽的官網提供24個Builder鏡像(https://access.redhat.com/containers/#/explore),如圖4-9所示。

圖4-9 官方鏡像類型
官方提供的Builder鏡像包含了大部分開發語言,如圖4-10所示。

圖4-10 官方Builder鏡像
紅帽Builder鏡像包含了紅帽研發的大量心血,其腳本的書寫判斷條件十分全面。我們以webserver31-tomcat8-openshift這個Builder鏡像為例來體會一下。
首先在OpenShift項目下,通過對應的Image Stream導入webserver31-tomcat8-openshift鏡像。
# oc import-image jboss-webserver31-tomcat8-openshift --from=registry.redhat.io/ jboss-webserver-3/webserver31-tomcat8-openshift --confirm imagestream.image.openshift.io/webserver31-tomcat8-openshift imported
接下來,使用導入成功的Image Stream部署Pod,如圖4-11所示。

圖4-11 選擇Image Stream和版本
在OpenShift中選擇Image Stream和Tag,使用Tag 1.4。部署完成后,等待Pod正常運行,如圖4-12所示。

圖4-12 Pod正常運行
我們查看Pod的名稱。
# oc get pods NAME READY STATUS RESTARTS AGE jboss-webserver31-tomcat8-openshift-597c7f995-wtqrf 1/1 Running 0 25s
登錄到Pod,切換到/usr/local/s2i目錄中,查看鏡像中的S2I腳本文件。
sh-4.2$ cd /usr/local/s2i; ls assemble common.sh run save-artifacts scl-enable-maven
查看assemble腳本。
sh-4.2$ cat assemble #!/bin/sh set -e source "${JBOSS_CONTAINER_UTIL_LOGGING_MODULE}/logging.sh" source "${JBOSS_CONTAINER_MAVEN_S2I_MODULE}/maven-s2i" source "${JBOSS_CONTAINER_JWS_S2I_MODULE}/s2i-core-hooks" maven_s2i_build
在上面腳本中的核心功能是執行maven_s2i_build這個函數。接下來,我們通過分析這個函數來體會Builder鏡像設計的精妙所在。
maven_s2i_build函數在/opt/jboss/container/maven/s2i/maven-s2i文件中進行了定義。
查看/opt/jboss/container/maven/s2i/maven-s2i中maven_s2i_build的函數的描述。
sh-4.2$ cat /opt/jboss/container/maven/s2i/maven-s2i …… # main entry point, perform the build function maven_s2i_build() { maven_s2i_init if [ -f "${S2I_SOURCE_DIR}/pom.xml" ]; then # maven build maven_s2i_maven_build else # binary build maven_s2i_binary_build fi s2i_core_copy_artifacts "${S2I_SOURCE_DIR}" s2i_core_process_image_mounts s2i_core_cleanup rm -rf /tmp/hsperfdata_jboss } ……
上面的函數是一個判斷,當{S2I_SOURCE_DIR}中有pom.xml文件時調用maven_s2i_maven_build函數,否則調用maven_s2i_binary_build函數。在判斷體之外,調用s2i_core_copy_artifacts等函數。
由于篇幅有限,我們接下來只分析maven_s2i_maven_build和s2i_core_copy_artifacts兩個函數。
接下來查看本文件內maven_s2i_maven_build函數的描述。
# perform a maven build, i.e. mvn ... # internal method function maven_s2i_maven_build() { maven_build "${S2I_SOURCE_DIR}" "${MAVEN_S2I_GOALS}" maven_s2i_deploy_artifacts maven_cleanup
也就是說,maven_s2i_maven_build函數調用了maven_build。而maven_build的定義在/opt/jboss/container/maven/default/maven.sh中,內容如下。
function maven_build() { local build_dir=${1:-$(cwd)} local goals=${2:-package} log_info "Performing Maven build in $build_dir" pushd $build_dir &> /dev/null log_info "Using MAVEN_OPTS ${MAVEN_OPTS}" log_info "Using $(mvn $MAVEN_ARGS --version)" log_info "Running 'mvn $MAVEN_ARGS $goals'" # Execute the actual build mvn $MAVEN_ARGS $goals popd &> /dev/null }
也就是說maven_build最終調用的是mvn命令,對源碼進行構建。
整個調用鏈條是:S2I=>assemble腳本=>maven_s2i_build=>maven_s2i_maven_build=>maven_build=>mvn。
我們回到maven_s2i_build,當maven_s2i_maven_build構建成功以后,調用s2i_core_copy_artifacts函數。
我們查看s2i_core_copy_artifacts的定義(/opt/jboss/container/s2i/core/s2i-core文件中)。
# main entry point for copying artifacts from the build to the target # $1 - the base directory function s2i_core_copy_artifacts() { s2i_core_copy_configuration $* s2i_core_copy_data $* s2i_core_copy_deployments $* s2i_core_copy_artifacts_hook $* }
也就是說,s2i_core_copy_artifacts又會包含4個函數。由于篇幅有限,我們僅以s2i_core_copy_data為例進行分析。
在同文件(/opt/jboss/container/s2i/core/s2i-core文件)中的代碼對s2i_core_copy_data進行了定義,內容如下。
# copy data files # $1 - the base directory to which $S2I_SOURCE_DATA_DIR is appended function s2i_core_copy_data() { if [ -d "${1}/${S2I_SOURCE_DATA_DIR}" ]; then if [ -z "${S2I_TARGET_DATA_DIR}" ]; then log_warning "Unable to copy data files. No target directory specified for S2I_TARGET_DATA_DIR" else if [ ! -d "${S2I_TARGET_DATA_DIR}" ]; then log_info "S2I_TARGET_DATA_DIR does not exist, creating ${S2I_TARGET_DATA_DIR}" mkdir -pm 775 "${S2I_TARGET_DATA_DIR}" fi log_info "Copying app data from $(realpath --relative-to ${S2I_SOURCE_DIR} ${1}/${S2I_SOURCE_DATA_DIR}) to ${S2I_TARGET_DATA_DIR}..." rsync -rl --out-format='%n' "${1}/${S2I_SOURCE_DATA_DIR}"/ "${S2I_TARGET_ DATA_DIR}"
代碼會進行一系列判斷,如果源目錄和目標目錄都同時存在,函數會調用rsync命令將源目錄的內容拷貝到目標目錄。
整個調用的鏈條是:S2I=>assemble腳本=>maven_s2i_build=>s2i_core_copy_artifacts=>s2i_core_copy_data=>rsync。
最后,我們查看S2I的run腳本。
sh-4.2$ cat /usr/local/s2i/run #!/bin/sh exec $JWS_HOME/bin/launch.sh
run腳本調用了launch.sh腳本。查看launch.sh腳本的部分內容。
sh-4.2$ cat /opt/webserver/bin/launch.sh #!/bin/sh CATALINA_OPTS="${CATALINA_OPTS} ${JAVA_PROXY_OPTIONS}" escape_catalina_opts log_info "Running $JBOSS_IMAGE_NAME image, version $JBOSS_IMAGE_VERSION" exec $JWS_HOME/bin/catalina.sh run
launch.sh最終調用了catalina.sh腳本來啟動webserver。而$JWS_HOME的參數是讀取的Pod環境變量。
sh-4.2$ env |grep -i JWS JBOSS_CONTAINER_JWS_S2I_MODULE=/opt/jboss/container/jws/s2i JWS_HOME=/opt/webserver
整個調用的鏈條是:S2I=>run腳本=>launch.sh腳本=>catalina.sh腳本。
也就是說,應用的源碼被mvn構建以后,拷貝到Tomcat的部署目錄中,然后catalina.sh腳本啟動webserver,從而啟動應用。
在上文我們只是分析了Builder鏡像中S2I腳本的冰山一角,而紅帽提供Builer鏡像的完備性、功能強大性可想而知,官方提供的Builder鏡像也能夠滿足絕大多數S2I的需求。
4.手工定制Builder鏡像
那么有沒有我們的需求超過紅帽官方提供的Builder鏡像,需要我們定制化的時候呢?
答案是肯定的。我們僅需要滿足S2I的規范就可以自行構建一個支持S2I的Builder鏡像,其流程如圖4-13所示。

圖4-13 定制S2I的流程
構建Builder鏡像的步驟如下:
·首先使用S2I命令行創建目錄結構,目錄中將會包含S2I的腳本、Dockerfile等。
·基礎鏡像通常使用紅帽官網提供的鏡像,我們編寫新的Dockerfile引用基礎鏡像。在構建子鏡像時也可以用新的S2I腳本覆蓋父鏡像的腳本。
·Builder鏡像構建成功以后,可以接收外部Git的代碼注入,對源碼進行編譯打包,最終形成應用鏡像。
·應用鏡像會被部署到OpenShift集群中,并創建Service和Router對象用于應用訪問。
定制化Builder鏡像的第一步就是選擇基礎鏡像,基礎鏡像的選擇決定了工作量和難易程度。通常有兩種選擇:
·選擇使用紅帽已經提供的Builder鏡像進行修改。直接以官方提供的Builder鏡像作為基礎鏡像,書寫Dockerfile進行任何想要的定制化,我們稱生成新的Builder鏡像為子Builder鏡像,官方提供的Builder鏡像為父Builder鏡像。
·使用最底層基礎鏡像(如openjdk或rhel)進行制作。根據社區或紅帽提供的最底層鏡像(如openjdk或rhel)自行書寫Dockerfile、S2I的相關腳本,生成子Builder鏡像。然后基于子Builder鏡像進行S2I,生成應用鏡像,實現應用容器化。
第一種方法的優點是書寫Dockerfile較為簡便(建立在紅帽提供的Builder鏡像的Dockerfile基礎上),缺點是生成的鏡像較大,生成的鏡像需要經過壓縮處理。
第二種方法的優點是生成的鏡像較小,缺點是需要技術人員很熟悉紅帽制作鏡像規范以及OpenShift對鏡像的要求,否則做出來的鏡像有些功能會不工作。
根據經驗,我們建議選擇第一種方法,即選擇使用紅帽已經提供的Builder鏡像進行修改,具體操作通常有三種方法:
·使用已有的紅帽Builder鏡像,在構建應用的時候采用覆蓋默認父鏡像S2I腳本的方法,不構建子Builder鏡像,直接生成應用鏡像,實現應用容器化。
·使用已有的紅帽基礎鏡像或Builder鏡像書寫新的Dockerfile,不覆蓋父鏡像S2I腳本,構建子Builder鏡像。然后基于子Builder鏡像進行S2I,生成應用鏡像,實現應用容器化。
·使用已有的紅帽基礎鏡像或Builder鏡像書寫新的Dockerfile,覆蓋父鏡像S2I腳本,構建子Builder鏡像。然后基于子Builder鏡像進行S2I,生成應用鏡像,實現應用容器化。
在上述三種方法中,定制的復雜度逐級提升。第一種和第三種使用較多,并且比較有代表性,我們將介紹這兩種方法。
(1)采用覆蓋默認父鏡像S2I腳本的方法生成應用鏡像
我們以紅帽提供的rhscl/httpd-24-rhel7 Builder鏡像為例。展示如何通過覆蓋父鏡像S2I腳本的方法生成應用鏡像。
首先導入rhscl/httpd-24-rhel7的Image Stream。
# oc import-image rhscl/httpd-24-rhel7 --from=registry.access.redhat.com/rhscl/ httpd-24-rhel7 --confirm imagestream.image.openshift.io/httpd-24-rhel7 imported
通過docker/podman run運行鏡像。
# podman run --name test -it rhscl/httpd-24-rhel7 bash
查看Builder鏡像的assemble腳本。
bash-4.2$ cd /usr/libexec/s2i/ bash-4.2$ cat assemble #!/bin/bash set -e source ${HTTPD_CONTAINER_SCRIPTS_PATH}/common.sh echo "---> Enabling s2i support in httpd24 image" config_s2i echo "---> Installing application source" cp -Rf /tmp/src/. ./ process_extending_files ${HTTPD_APP_ROOT}/src/httpd-post-assemble/ ${HTTPD_ CONTAINER_SCRIPTS_PATH}/post-assemble/ # Fix source directory permissions fix-permissions ./
查看run腳本內容。
bash-4.2$ cat run #!/bin/bash source ${HTTPD_CONTAINER_SCRIPTS_PATH}/common.sh export HTTPD_RUN_BY_S2I=1 exec run-httpd $@
接下來,我們創建S2I的腳本(assemble和run)和源碼文件(index.html)。其目錄結構如下,需要注意的是s2i必須是隱藏目錄。
# tree -a └── s2i-scripts ├── index.html └── .s2i └── bin ├── assemble └── run
我們查看源碼index.html內容。
# cat /root/david/s2i-scripts/index.html This is David Wei test for S2I!!!
編寫新的assemble腳本,我們可以看到,這和rhscl/httpd-24-rhel7中的assemble腳本內容的區別。新assemble腳本主要完成如下事情:
·執行腳本的時候輸出DavidWei S2I test!!!。
·將/tmp/src/.目錄下的內容拷貝到./,由于后面GIT倉庫地址包含的源碼是index.html,因此拷貝的是該文件。
·將如下內容重定向到./info.html文件中。
Page built on $DATE DavidWei test: Proudly served by Apache HTTP Server version $HTTPD_VERSION
查看腳本內容。
# cat /root/david/s2i-scripts/.s2i/bin/assemble #!/bin/bash set -e source ${HTTPD_CONTAINER_SCRIPTS_PATH}/common.sh echo "---> DavidWei S2I test!!!" config_s2i echo "---> Installing application source" cp -Rf /tmp/src/. ./ process_extending_files ${HTTPD_APP_ROOT}/src/httpd-post-assemble/ ${HTTPD_ CONTAINER_SCRIPTS_PATH}/post-assemble/ # Fix source directory permissions fix-permissions ./ DATE=`date "+%b %d, %Y @ %H:%M %p"` echo "---> Creating info page" echo "Page built on $DATE" >> ./info.html echo "DavidWei test:Proudly served by Apache HTTP Server version $HTTPD_VERSION" >> ./info.html
查看run腳本內容,是打開debug模式。
# cat /root/david/s2i-scripts/.s2i/bin/run # Make Apache show 'debug' level logs during startup run-httpd -e debug $@
接下來,將/root/david/s2i-scripts目錄的內容(S2I腳本和index.html)提交到GitHub。
# git init # git add /root/david/s2i-scripts/* # git add /root/david/s2i-scripts/.s2i/* # git remote add origin https://github.com/ocp-msa-devops/s2itest.git # git commit -m "s2i scripts" # git push -u origin master -f
提交成功以后,使用rhscl/httpd-24-rhel7和剛上傳的源碼地址進行S2I。將應用的名稱設置為weixinyu。
# oc new-app --name weixinyu httpd-24-rhel7~https://github.com/ocp-msa-devops/s2itest --> Found image 0f1cb8c (6 weeks old) in image stream "openwhisk/httpd-24-rhel7" under tag "latest" for "httpd-24-rhel7"
查看Builder Pod的日志,我們可以看到:
·構建是使用rhscl/httpd-24-rhel7指向的Docker鏡像。
·輸出DavidWei S2I test!!!,說明執行了新的assemble腳本。
具體內容如下。
# oc logs -f weixinyu-1-build Using registry.access.redhat.com/rhscl/httpd-24-rhel7@sha256:684590af705d72af64b 88ade55c31ce6884bff3c1da7cbf8c11aaa0a4908f63f as the s2i Builder Image ---> DavidWei S2I test!!! AllowOverride All ---> Installing application source => sourcing 20-copy-config.sh ... => sourcing 40-ssl-certs.sh ... ---> Creating info page Pushing image docker-registry.default.svc:5000/openwhisk/weixinyu:latest ... Push successful
接下來,我們為部署好的Pod創建路由。
# oc expose svc weixinyu --port 8080 route.route.openshift.io/weixinyu exposed # oc get route weixinyu weixinyu-openwhisk.apps.example.com weixinyu 8080 None
通過curl訪問路由,得出的結果正是我們在index.html中定義的內容。
# curl http://weixinyu-openwhisk.apps.example.com This is David Wei test for S2I!!!
通過curl訪問路由,增加info.html URI,得到的返回信息正是我們在新assemble腳本中定義的內容。
# curl http://weixinyu-openwhisk.apps.example.com/info.html Page built on Jun 06, 2019 @ 14:35 PM DavidWei test: Proudly served by Apache HTTP Server version 2.4
也就是說,在S2I過程中指定GitHub地址上的新S2I腳本替換了父鏡像中的S2I腳本。
至此,我們實現了使用已有的Builder鏡像,采用覆蓋默認父鏡像S2I腳本的方法,不重新構建子Builder鏡像,直接生成應用鏡像,也就是實現了應用的容器化。
(2)書寫新的Dockerfile,覆蓋父鏡像S2I腳本,生成子Builder鏡像
接下來,我們看另外一類需求。在前文中,我們分析了Tomcat的Builder鏡像。我們查看鏡像的標簽,我們使用的是latest,如圖4-14所示。

圖4-14 Tomcat鏡像標簽
我們查看1.4-7 tag對應的Dockerfile,它使用的是Tomcat 8:3.1.6,Maven是3.5,如圖4-15所示。
如果客戶需要的S2I builder的Tomcat版本必須是8.5,并且必須包含Maven 3.6.1,此外還必須支持SVN(S2I默認僅支持Git),我們應該怎么做呢?這時需要利用紅帽提供的Builder鏡像自行定制子Builder鏡像。

圖4-15 Tomcat的Dockerfile內容
使用s2i create命令來創建所需的模板文件,以創建新的S2I Builder鏡像。
首先安裝s2i命令行。
# subscription-manager repos --enable="rhel-server-rhscl-7-rpms" Repository 'rhel-server-rhscl-7-rpms' is enabled for this system. # yum -y install source-to-image
創建鏡像和對應的目錄。
# s2i create s2i_tomcat8.5_maven3.6.1 s2i_tomcat8.5_maven3.6.1
查看目錄結構。
# tree s2i_tomcat8.5_maven3.6.1 s2i_tomcat8.5_maven3.6.1 ├── Dockerfile ├── Makefile ├── README.md ├── s2i │ └── bin │ ├── assemble │ ├── run │ ├── save-artifacts │ └── usage └── test ├── run └── test-app └── index.html
使用新的Dockerfile,針對官網提供的webserver31-tomcat8-openshift。
·用本地的apache-tomcat-8.5.24覆蓋到/opt/webserver下。
·用本地的Maven3.6.1覆蓋父Builder鏡像中的Maven3.5。
從互聯網下載Tomcat 8.5.24和Maven3.6.1的安裝包,如圖4-16所示。
放到我們上一步創建的目錄中解壓縮,如圖4-17所示。

圖4-16 Tomcat和Maven安裝包

圖4-17 解壓安裝包
編寫新的Dockerfile,內容如下。
# tomcat8.5 FROM registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift RUN rm -fr /opt/webserver/* COPY ./apache-tomcat-8.5.24/ /opt/webserver RUN ln -s /deployments /opt/webserver/webapps USER root RUN rm -fr /opt/rh/rh-maven35/root/usr/share/maven/* COPY ./maven3.6.1/ /opt/rh/rh-maven35/root/usr/share/maven COPY ./maven3.6.1/bin/ /opt/rh/rh-maven35/root/bin RUN chown -R jboss:root /opt/webserver && \ chmod -R a+w /opt/webserver && \ chmod -R 777 /opt/webserver/bin && \ chmod -R 777 /opt/webserver && \ chmod -R 777 /opt/rh/rh-maven35/root/usr/share/maven && \ chmod -R 777 /opt/rh/rh-maven35/root/bin USER 1002
由于S2I默認僅支持Git,如果我們要用SVN,就需要在assemble腳本中進行相關設置,也就是在執行assemble腳本時觸發命令從SVN_URI參數設置的地址獲取源代碼,即通過svn的方式獲取代碼,然后使用mvn進行編譯。最后將編譯好的War包拷貝到Webserver的webapps的目錄中,Tomcat會自動解壓和部署應用。
if [[ "$1" == "-h" ]]; then exec /usr/libexec/s2i/usage fi # Restore artifacts from the previous build (if they exist). # if [ "$(ls /tmp/artifacts/ 2>/dev/null)" ]; then echo "---> Restoring build artifacts..." mv /tmp/artifacts/. ./ fi echo "---> Installing application source..." cp -Rf /tmp/src/. ./ ls -l ./ ls -l /tmp/src/ 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 echo "---> Building application from source..." cd $WORK_DIR/$SRC_DIR/ ${BUILD_CMD} echo "---> Build application successfully." find /tmp/src/ -name '*.war'|xargs -i cp -v {} /opt/webserver/webapps/
由于紅帽提供的run腳本最終調用catalina.sh是使用的$JWS_HOME,因此更替版本不會使執行路徑發生變化,run腳本使用父鏡像的腳本即可,或直接使用如下內容啟動Webserver。
exec /opt/webserver/bin/catalina.sh run
所有準備工作完成之后就可以手工構建鏡像,輸出內容如下。
Sending build context to Docker daemon 19.66 MB Step 1/9 : FROM registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8- openshift ---> c303ee1e1273 Step 2/9 : RUN rm -fr /opt/webserver/* ---> Running in aa5cab28ecc2 ---> 0eaa859906d7 Removing intermediate container aa5cab28ecc2 Step 3/9 : COPY ./apache-tomcat-8.5.24/ /opt/webserver ---> 132fd21440c3 Removing intermediate container ea09b46d8a1a Step 4/9 : RUN ln -s /deployments /opt/webserver/webapps ---> Running in e9c43075f480 ---> 56faa88135d3 Removing intermediate container e9c43075f480 Step 5/9 : USER root ---> Running in e96b4ef66a20 ---> 2fc5139ee082 Removing intermediate container e96b4ef66a20 Step 6/9 : RUN rm -fr /opt/rh/rh-maven35/root/usr/share/maven/* ---> Running in 89d19c564fdd ---> 38e364d3ab50 Removing intermediate container 89d19c564fdd Step 7/9 : COPY ./maven3.6.1/ /opt/rh/rh-maven35/root/usr/share/maven ---> 2ec060686ce4 Removing intermediate container 047930b49000 Step 8/9 : RUN chown -R jboss:root /opt/webserver && chmod -R a+w /opt/ webserver && chmod -R 777 /opt/webserver/bin && chmod -R 777 / opt/webserver && chmod -R 777 /opt/rh/rh-maven35/root/usr/share/maven && chmod -R 777 /opt/rh/rh-maven35/root/bin ---> Running in dbda875ca5f0 ---> 02bb0fa0eadb Removing intermediate container dbda875ca5f0 Step 9/9 : USER 1002 ---> Running in 4fd3ac609041 ---> 703e3a4d58c2 Removing intermediate container 4fd3ac609041 Successfully built 703e3a4d58c2
查看構建成功的子Builder鏡像。
# podman images | grep -i s2i s2i_tomcat8.5_maven3.6.1 latest 703e3a4d58c2 6 minutes ago 585 MB
我們可以將構建成功的子Builder鏡像推送到自己的鏡像倉庫。至此,我們完成了定制化Builder鏡像。
為了方便后續使用,通常會創建OpenShift的模板來實現構建和部署應用,模板的具體配置方法,我們將在第7章中進行詳細介紹,模板創建成功后,如圖4-18所示。

圖4-18 模板參數
關于構建Builder鏡像采用的基礎鏡像,當然也可以采用更為基礎的基礎鏡像。例如我們可以使用openjdk8作為基礎鏡像來生成s2i_tomcat8.5_maven3.6.1,只不過與使用已有的Builder鏡像:webserver31-tomcat8-openshift相比,這種方式的步驟會多很多。
如果使用openjdk,Dockerfile的部分內容參考如圖4-19所示。
在介紹完應用容器化的方法以后,我們接下來介紹開發人員如何在OpenShift上快速部署應用。

圖4-19 從openjdk構建鏡像的Dockerfile
- 從零開始構建企業級RAG系統
- Visual C++程序設計學習筆記
- Delphi程序設計基礎:教程、實驗、習題
- PostgreSQL Cookbook
- Xcode 7 Essentials(Second Edition)
- UML+OOPC嵌入式C語言開發精講
- Interactive Applications Using Matplotlib
- 微信公眾平臺開發:從零基礎到ThinkPHP5高性能框架實踐
- 青少年信息學競賽
- Python Projects for Kids
- Visual C++程序設計與項目實踐
- 高性能PHP 7
- 一步一步學Spring Boot:微服務項目實戰(第2版)
- 打造流暢的Android App
- 精通Oracle 12c 數據庫管理