背景
最近業務須要使用Flink, 因而把以前Flink的相關技術拿出來從新回顧一下, 因而想起這個以前一直沒有去解決的問題. 本文主要講解如何解決這一問題以及發生這個問題的根本緣由.java
運行Flink 官方docker image
此處很少說,訪問docker hub flink官方的Image. 選擇本身須要版本的flink官方鏡像(此處我選的是flink:scala_2.11 由於要使用到scala shell因此選的scala版本不是最新的) 而後按照官方給的docker-compose 文件簡單改動一下啓動便可web
version: "3" services: jobmanager: image: flink:scala_2.11 expose: - "6123" ports: - "8081:8081" command: jobmanager environment: - "JOB_MANAGER_RPC_ADDRESS=jobmanager" taskmanager: image: flink:scala_2.11 expose: - "6121" - "6122" depends_on: - jobmanager command: taskmanager links: - "jobmanager:jobmanager" environment: - "JOB_MANAGER_RPC_ADDRESS=jobmanager"
此時訪問web ui localhost:8081 查看 jobmanager 的 logs 和 stdout
file unavailable 沒法查看logs 和 stdout. 提交一個wordcount程序
docker
object StreamingJob { def main(args: Array[String]) { val env = StreamExecutionEnvironment.getExecutionEnvironment val textStream = env.fromElements( "hello word", "this is flink demo" ) val counts = textStream.flatMap { _.toLowerCase().split("\\W+") } .map { (_, 1) }.keyBy(0).sum(1) counts.print() env.execute("word count") } }
查看結果, 運行結束且正常.
查看taskmanager的logs 和 stdout 依然是什麼都沒有, 此程序有print運行結果的,可是實際上並未查看到.
針對taskmanger容器 使用docker logs 查看日誌
shell
docker logs 18a4a82ad9fe(taskmanager container id)
看到日誌裏面有print出來結果express
Source: Collection Source -> Flat Map -> Map (1/1) 13fcdc91c88d72a6785c2b3d6f6e23c1. (hello,1) (word,1) (this,1) (is,1) (flink,1) (demo,1)
只是這個結果目前不能在web ui 的 taskmanager stdout上面查看.apache
解決方案
遇到此類配置相關的問題, 想必你們第一時間就是去stackoverflow上面去找答案. 上面確實有這個問題的答案,我把鏈接貼到這裏:Apache Flink: The file STDOUT is not available on the TaskExecutor
bash
解決方案裏面更改了docker-entrypoint.sh 文件將jobmanager和taskmanager 的啓動相關參數, 下面是解決方案裏面的改動
app
下面是改動以前的腳本:less
if [ "$1" = "help" ]; then echo "Usage: $(basename "$0") (jobmanager|${COMMAND_STANDALONE}|taskmanager|help)" exit 0 elif [ "$1" = "jobmanager" ]; then shift 1 prepare_job_manager_start exec $(drop_privs_cmd) "$FLINK_HOME/bin/jobmanager.sh" start-foreground "$@" elif [ "$1" = ${COMMAND_STANDALONE} ]; then shift 1 prepare_job_manager_start exec $(drop_privs_cmd) "$FLINK_HOME/bin/standalone-job.sh" start-foreground "$@" elif [ "$1" = "taskmanager" ]; then shift 1 echo "Starting Task Manager" copy_plugins_if_required TASK_MANAGER_NUMBER_OF_TASK_SLOTS=${TASK_MANAGER_NUMBER_OF_TASK_SLOTS:-$(grep -c ^processor /proc/cpuinfo)} set_common_options set_config_option taskmanager.numberOfTaskSlots ${TASK_MANAGER_NUMBER_OF_TASK_SLOTS} if [ -n "${FLINK_PROPERTIES}" ]; then echo "${FLINK_PROPERTIES}" >> "${CONF_FILE}" fi envsubst < "${CONF_FILE}" > "${CONF_FILE}.tmp" && mv "${CONF_FILE}.tmp" "${CONF_FILE}" exec $(drop_privs_cmd) "$FLINK_HOME/bin/taskmanager.sh" start-foreground "$@" fi exec "$@"
對比一下二者的區別, 改動前爲start-foreground
應該是前臺啓動的意思 改動以後是直接start
看樣子像是變成了後臺啓動. 而後在腳本最後執行了一個tail -f XXXX
的命令 表示持續輸出某個文件的內容到console.
懂docker 容器技術的人看到這個tail -f XXX
應該瞬間明白了爲何做者要這樣處理. 容器中運行的程序至少一個前臺進程, 若是全是後臺進程,那麼容器一啓動就會當即中止. 因此當咱們容器中的程序必定要之後臺進程方式運行的時候,通常容器的啓動腳本中都會加一個tail -f XXXXX
保證有一個前臺程序一直在run, 這樣容器不會被關閉.
ide
因此將docker-entrypoint.sh 執行jobmanager.sh 以及 執行taskmanager.sh的地方更改並在最後加上
exec /bin/bash -c "tail -f $FLINK_HOME/log/*.log"
保證容器中一直有前臺進程運行,改動後的docker-entrypoint.sh 部分以下:
if [ "$1" = "help" ]; then echo "Usage: $(basename "$0") (jobmanager|${COMMAND_STANDALONE}|taskmanager|help)" exit 0 elif [ "$1" = "jobmanager" ]; then shift 1 prepare_job_manager_start $FLINK_HOME/bin/jobmanager.sh start "$@" elif [ "$1" = ${COMMAND_STANDALONE} ]; then shift 1 prepare_job_manager_start $FLINK_HOME/bin/standalone-job.sh start "$@" elif [ "$1" = "taskmanager" ]; then shift 1 echo "Starting Task Manager" copy_plugins_if_required TASK_MANAGER_NUMBER_OF_TASK_SLOTS=${TASK_MANAGER_NUMBER_OF_TASK_SLOTS:-$(grep -c ^processor /proc/cpuinfo)} set_common_options set_config_option taskmanager.numberOfTaskSlots ${TASK_MANAGER_NUMBER_OF_TASK_SLOTS} if [ -n "${FLINK_PROPERTIES}" ]; then echo "${FLINK_PROPERTIES}" >> "${CONF_FILE}" fi envsubst < "${CONF_FILE}" > "${CONF_FILE}.tmp" && mv "${CONF_FILE}.tmp" "${CONF_FILE}" $FLINK_HOME/bin/taskmanager.sh start "$@" fi sleep 1 exec /bin/bash -c "tail -f $FLINK_HOME/log/*.log"
用這個文件替換掉官方的docker-entrypoint.sh 構建本身的flink image 替換掉官方的image
運行docker-compose.yml 訪問localhost:8081查看jobmanager的log
發現經過web ui能夠查看到日誌了.
一樣提交word count的程序查看stdout運行結果
至此,這個問題已經解決了.
問題根本緣由
按照上面的步驟能夠解決遇到的問題,可是須要找到問題的根本,是什麼緣由致使官方的鏡像不可以經過web ui查看log和 stdout, 只可以經過docker logs {container_id}
的方式去查看日誌和運行結果. 追本溯源仍是 jobmanager.sh 和 taskmanager.sh 兩個腳本. 改前與改後的區別就是傳參不一樣, 一個是start-foreground
一個是start
查看jobmanager.sh源碼發現傳入這兩個參數以後真正執行的腳本是不一樣的
if [[ $STARTSTOP == "start-foreground" ]]; then exec "${FLINK_BIN_DIR}"/flink-console.sh $ENTRYPOINT "${args[@]}" else "${FLINK_BIN_DIR}"/flink-daemon.sh $STARTSTOP $ENTRYPOINT "${args[@]}" fi
官方鏡像傳入的start-foreground
後執行的腳本是flink-console.sh
而改動以後執行的是flink-daemon.sh. 其實看到這裏,大概就明白七八成了. 官方的方式運行的是flink-console.sh 便是命令行模式, 憑經驗命令行模式內容都打印在命令行裏, 屬於前臺執行進程. 而改動以後執行的是flink-daemnon.sh 表示flink將之後臺進程執行. 這也能解釋在改動的docker-entrypoint.sh 爲何最後會加一個tail -f XXXX的命令了. 由於此處讓flink後臺執行了, 若是不加tail命令, 那麼容器中只有flink這個後臺進程,一啓動會馬上關閉.
在繼續深刻下去看flink-console.sh 和 flink-daemon.sh 發現有一段日誌設置相關的代碼比較有意思:
flink-console.sh
log_setting=("-Dlog4j.configuration=file:${FLINK_CONF_DIR}/log4j-console.properties" "-Dlogback.configurationFile=file:${FLINK_CONF_DIR}/logback-console.xml")
以及 flink-daemon.sh
FLINK_LOG_PREFIX="${FLINK_LOG_DIR}/flink-${FLINK_IDENT_STRING}-${DAEMON}-${id}-${HOSTNAME}" log="${FLINK_LOG_PREFIX}.log" out="${FLINK_LOG_PREFIX}.out" log_setting=("-Dlog.file=${log}" "-Dlog4j.configuration=file:${FLINK_CONF_DIR}/log4j.properties" "-Dlogback.configurationFile=file:${FLINK_CONF_DIR}/logback.xml")
這裏在flink-daemon.sh 中有一個 -Dlog.file的配置 log.file 很是關鍵的系統變量.而在flink-console.sh裏面沒有設置這個系統變量.
此時能夠查看一下flink下conf裏面關於日誌的配置文件:
好比看log4j.properties
################################################################################ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ################################################################################ # This affects logging for both user code and Flink log4j.rootLogger=INFO, file # Uncomment this if you want to _only_ change Flink's logging #log4j.logger.org.apache.flink=INFO # The following lines keep the log level of common libraries/connectors on # log level INFO. The root logger does not override this. You have to manually # change the log levels here. log4j.logger.akka=INFO log4j.logger.org.apache.kafka=INFO log4j.logger.org.apache.hadoop=INFO log4j.logger.org.apache.zookeeper=INFO # Log all infos in the given file log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.file=${log.file} log4j.appender.file.append=false log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n # Suppress the irrelevant (wrong) warnings from the Netty channel handler log4j.logger.org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline=ERROR, file
發現裏面log4j.appender.file.file
正好使用的是 log.file
這個系統變量.
因此這個問題的根源就是這個log.file
系統變量配置問題. 在官方鏡像執行過程當中, 因爲運行的是flink-console.sh 腳本, 沒有設置這個log.file
所以不會再容器中{FLINK_HOME}/log
下面生成log以及stdout(能夠本身啓動官方鏡像,而後進入容器去check,確實沒有log和out文件生成). 而web ui 正好是從這個目錄下獲取log和stdout. 改動以後使用flink-daemon.sh 啓動時, 會去設置這個log.file
系統變量, flink啓動後, log以及out都會存放在{FLINK_HOME}/log
, 使用web ui 就能看到這些結果了. 其實當flink不以容器啓動的時候,都可以看到log文件夾下面有log和out的文件, 官方鏡像啓動使用flink-console.sh 讓flink前臺啓動,保證容器啓動時有前臺進程,因此致使log和out沒有寫到log文件夾中, 進而也沒法經過web ui 去查看log和stdout了.這就是這個問題的根本緣由.