1. 程式人生 > >遷移傳統應用到Kubernetes步驟詳解

遷移傳統應用到Kubernetes步驟詳解

前言

本文已歸檔到 kubernetes-handbook 【第三章使用者指南】的【在Kubernetes中開發部署應用】小節中,一切更新以 GitHub 為準。

本文件不是說明如何在 kubernetes 中開發和部署應用程式,如果您想要直接開發應用程式在 kubernetes 中執行可以參考 適用於kubernetes的應用開發部署流程

本文旨在說明如何將已有的應用程式尤其是傳統的分散式應用程式遷移到 kubernetes 中。如果該類應用程式符合雲原生應用規範(如12因素法則)的話,那麼遷移會比較順利,否則會遇到一些麻煩甚至是阻礙。具體請參考 遷移至雲原生應用架構

接下來我們將以 Spark on YARN with kubernetes 為例來說明,該例子足夠複雜也很有典型性,瞭解了這個例子可以幫助大家將自己的應用遷移到 kubernetes 叢集上去,程式碼和配置檔案可以在 

這裡 找到(本文中加入 Spark 的配置,程式碼中並沒有包含,讀者可以自己配置)。

下圖為整個架構的示意圖,程式碼和詳細配置檔案請參考 kube-yarn(不包含 ingress、spark 配置),所有的程序管理和容器擴容直接使用 Makefile,如何使用請參考該專案文件。

spark on yarn with kubernetes 架構圖

注意: 該例子僅用來說明具體的步驟劃分和複雜性,在生產環境應用還有待驗證,請謹慎使用。

術語

對於為曾接觸過 kubernetes 或對雲平臺的技術細節不太瞭解的人來說,如何將應用遷移到 kubernetes 中可能是個頭疼的問題,在行動之前有必要先了解整個過程中需要用到哪些概念和術語,有助於大家在行動中達成共識。

過程中可能用到的概念和術語初步整理如下:

Terms

為了講解整改過程和具體細節,我們所有操作都是通過命令手動完成,不使用自動化工具。當您充分了解到其中的細節後可以通過自動化工具來優化該過程,以使其更加自動和高效,同時減少因為人為操作失誤導致的遷移失敗。

步驟詳解

分解步驟解析

整個遷移過程分為如下幾個步驟:

1. 將原有應用拆解為服務

我們不是一上來就開始做映象,寫配置,而是應該先梳理下要遷移的應用中有哪些可以作為服務執行,哪些是變的,哪些是不變的部分。

服務劃分的原則是最小可變原則,這個同樣適用於映象製作,將服務中不變的部分編譯到同一個映象中。

對於像 Spark on YARN 這樣複雜的應用,可以將其劃分為三大類服務:

  • ResourceManager
  • NodeManager
  • Spark client

2. 製作映象

根據拆解出來的服務,我們需要製作兩個映象:

  • Hadoop
  • Spark (From hadoop docker image)

因為我們執行的是 Spark on YARN,因此 Spark 依賴與 Hadoop 映象,我們在 Spark 的基礎上包裝了一個 web service 作為服務啟動。

映象製作過程中不需要在 Dockerfile 中指定 Entrypoint 和 CMD,這些都是在 kubernetes 的 YAML 檔案中指定的。

Hadoop YARN 的 Dockerfile 參考如下配置。

FROM my-docker-repo/jdk:7u80

# Add native libs
ARG HADOOP_VERSION=2.6.0-cdh5.5.2
## Prefer to download from server not use local storage
ADD hadoop-${HADOOP_VERSION}.tar.gz /usr/local
ADD ./lib/* /usr/local/hadoop-${HADOOP_VERSION}/lib/native/
ADD ./jars/* /usr/local/hadoop-${HADOOP_VERSION}/share/hadoop/yarn/
ENV HADOOP_PREFIX=/usr/local/hadoop \
    HADOOP_COMMON_HOME=/usr/local/hadoop \
    HADOOP_HDFS_HOME=/usr/local/hadoop \
    HADOOP_MAPRED_HOME=/usr/local/hadoop \
    HADOOP_YARN_HOME=/usr/local/hadoop \
    HADOOP_CONF_DIR=/usr/local/hadoop/etc/hadoop \
    YARN_CONF_DIR=/usr/local/hadoop/etc/hadoop \
    PATH=${PATH}:/usr/local/hadoop/bin

RUN \
  cd /usr/local && ln -s ./hadoop-${HADOOP_VERSION} hadoop && \
  rm -f ${HADOOP_PREFIX}/logs/*

WORKDIR $HADOOP_PREFIX

# Hdfs ports
EXPOSE 50010 50020 50070 50075 50090 8020 9000
# Mapred ports
EXPOSE 19888
#Yarn ports
EXPOSE 8030 8031 8032 8033 8040 8042 8088
#Other ports
EXPOSE 49707 2122

3. 準備應用的配置檔案

因為我們只製作了一個 Hadoop 的映象,而需要啟動兩個服務,這就要求在服務啟動的時候必須載入不同的配置檔案,現在我們只需要準備兩個服務中需要同時用的的配置的部分。

YARN 依賴的配置在 artifacts 目錄下,包含以下檔案:

bootstrap.sh
capacity-scheduler.xml
container-executor.cfg
core-site.xml
hadoop-env.sh
hdfs-site.xml
log4j.properties
mapred-site.xml
nodemanager_exclude.txt
slaves
start-yarn-nm.sh
start-yarn-rm.sh
yarn-env.sh
yarn-site.xml

其中作為 bootstrap 啟動指令碼的 bootstrap.sh 也包含在該目錄下,該指令碼如何編寫請見下文。

4. Kubernetes YAML 檔案

根據業務的特性選擇最適合的 kubernetes 的資源物件來執行,因為在 YARN 中 NodeManager 需要使用主機名向 ResourceManger 註冊,因此需要沿用 YARN 原有的服務發現方式,使用 headless service 和 StatefulSet 資源。更多資料請參考 StatefulSet

所有的 Kubernetes YAML 配置檔案儲存在 manifest 目錄下,包括如下配置:

  • yarn-cluster 的 namespace 配置
  • Spark、ResourceManager、NodeManager 的 headless service 和 StatefulSet 配置
  • 需要暴露到 kubernetes 叢集外部的 ingress 配置(ResourceManager 的 Web)
kube-yarn-ingress.yaml
spark-statefulset.yaml
yarn-cluster-namespace.yaml
yarn-nm-statefulset.yaml
yarn-rm-statefulset.yaml

5. Bootstrap 指令碼

Bootstrap 指令碼的作用是在啟動時根據 Pod 的環境變數、主機名或其他可以區分不同 Pod 和將啟動角色的變數來修改配置檔案和啟動服務應用。

該指令碼同時將原來 YARN 的日誌使用 stdout 輸出,便於使用 kubectl logs 檢視日誌或其他日誌收集工具進行日誌收集。

啟動指令碼 bootstrap.sh 跟 Hadoop 的配置檔案同時儲存在 artifacts 目錄下。

該指令碼根據 Pod 的主機名,決定如何修改 Hadoop 的配置檔案和啟動何種服務。bootstrap.sh 檔案的部分程式碼如下:

if [[ "${HOSTNAME}" =~ "yarn-nm" ]]; then
  sed -i '/<\/configuration>/d' $HADOOP_PREFIX/etc/hadoop/yarn-site.xml
  cat >> $HADOOP_PREFIX/etc/hadoop/yarn-site.xml <<- EOM
  <property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>${MY_MEM_LIMIT:-2048}</value>
  </property>

  <property>
    <name>yarn.nodemanager.resource.cpu-vcores</name>
    <value>${MY_CPU_LIMIT:-2}</value>
  </property>
EOM
  echo '</configuration>' >> $HADOOP_PREFIX/etc/hadoop/yarn-site.xml
  cp ${CONFIG_DIR}/start-yarn-nm.sh $HADOOP_PREFIX/sbin/
  cd $HADOOP_PREFIX/sbin
  chmod +x start-yarn-nm.sh
  ./start-yarn-nm.sh
fi

if [[ $1 == "-d" ]]; then
  until find ${HADOOP_PREFIX}/logs -mmin -1 | egrep -q '.*'; echo "`date`: Waiting for logs..." ; do sleep 2 ; done
  tail -F ${HADOOP_PREFIX}/logs/* &
  while true; do sleep 1000; done
fi

從這部分中程式碼中可以看到,如果 Pod 的主機名中包含 yarn-nm 欄位則向 yarn-site.xml配置檔案中增加如下內容:

  <property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>${MY_MEM_LIMIT:-2048}</value>
  </property>

  <property>
    <name>yarn.nodemanager.resource.cpu-vcores</name>
    <value>${MY_CPU_LIMIT:-2}</value>
  </property>

其中 MY_MEM_LIMIT 和 MY_CPU_LIMIT 是 kubernetes YAML 中定義的環境變數,該環境變數又是引用的 Resource limit。

所有的配置準備完成後,執行 start-yarn-nm.sh 指令碼啟動 NodeManager。

如果 kubernetes YAML 中的 container CMD args 中包含 -d 則在後臺執行 NodeManger 並 tail 輸出 NodeManager 的日誌到標準輸出。

6. ConfigMaps

將 Hadoop 的配置檔案和 bootstrap 指令碼作為 ConfigMap 資源儲存,用作 Pod 啟動時掛載的 volume。

kubectl create configmap hadoop-config \
	  --from-file=artifacts/hadoop/bootstrap.sh \
	  --from-file=artifacts/hadoop/start-yarn-rm.sh \
	  --from-file=artifacts/hadoop/start-yarn-nm.sh \
	  --from-file=artifacts/hadoop/slaves \
	  --from-file=artifacts/hadoop/core-site.xml \
	  --from-file=artifacts/hadoop/hdfs-site.xml \
	  --from-file=artifacts/hadoop/mapred-site.xml \
	  --from-file=artifacts/hadoop/yarn-site.xml \
	  --from-file=artifacts/hadoop/capacity-scheduler.xml \
	  --from-file=artifacts/hadoop/container-executor.cfg \
	  --from-file=artifacts/hadoop/hadoop-env.sh \
	  --from-file=artifacts/hadoop/log4j.properties \
	  --from-file=artifacts/hadoop/nodemanager_exclude.txt \
	  --from-file=artifacts/hadoop/yarn-env.sh
kubectl  create configmap spark-config \
	  --from-file=artifacts/spark/spark-bootstrap.sh \
	  --from-file=artifacts/spark/spark-env.sh \
	  --from-file=artifacts/spark/spark-defaults.conf

所有的配置完成後,可以可以使用 kubectl 命令來啟動和管理叢集了,我們編寫了 Makefile,您可以直接使用該 Makefile 封裝的命令實現部分的自動化。