迁移传统应用到 Kubernetes 步骤详解——以 Hadoop YARN 为例

本文档主要介绍如何将已有的传统分布式应用程序迁移到 Kubernetes 中。如果你想要直接开发 Kubernetes 原生应用,可以参考 适用于 Kubernetes 的应用开发部署流程

应用迁移的难易程度很大程度上取决于原应用是否符合云原生应用规范(如 12 因素应用)。符合规范的应用迁移会比较顺利,否则可能遇到较大阻碍。

迁移策略概述

下图展示了将单体应用迁移到云原生的整体策略:

将单体应用迁移到云原生 (图片来自 DevOpsDay Toronto)
将单体应用迁移到云原生 (图片来自 DevOpsDay Toronto)

本文将以 Spark on YARN 迁移为例进行详细说明。该例子具有足够的复杂性和典型性,掌握这个案例有助于理解大部分传统应用的迁移过程。

下图是整个架构的示意图,所有进程管理和容器扩容通过 Makefile 实现:

spark on yarn with kubernetes
spark on yarn with kubernetes

重要提示:本案例仅用于演示迁移步骤和复杂性,生产环境使用需要进一步验证和优化。

核心概念与术语

在开始迁移之前,需要了解过程中涉及的关键概念:

术语
术语

为了更好地理解迁移细节,本文所有操作都通过命令手动完成,不使用自动化工具。待充分理解细节后,可以引入自动化工具优化流程,提高效率并减少人为错误。

迁移实施步骤

分解步骤解析
分解步骤解析

第一步:应用服务化拆解

在制作镜像和编写配置之前,首先需要梳理应用架构,识别哪些组件可以作为独立服务运行。

拆解原则

  • 遵循最小可变原则
  • 将不变的部分编译到同一个镜像中
  • 保持服务的功能内聚性

对于 Spark on YARN,可以拆解为以下核心服务:

  • ResourceManager:资源管理服务
  • NodeManager:节点管理服务
  • Spark Client:Spark 应用客户端

第二步:容器镜像制作

根据服务拆解结果,需要制作以下镜像:

Hadoop 基础镜像

FROM my-docker-repo/jdk:8u321

# 添加原生库
ARG HADOOP_VERSION=3.3.4
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/* && \
  mkdir -p ${HADOOP_PREFIX}/logs

WORKDIR $HADOOP_PREFIX

# 端口暴露
EXPOSE 8020 8030 8031 8032 8033 8040 8042 8088 9000 50070

Spark 镜像

基于 Hadoop 镜像构建 Spark 镜像,并包装 Web 服务:

FROM hadoop-base:latest

ARG SPARK_VERSION=3.3.2
ADD spark-${SPARK_VERSION}-bin-hadoop3.tgz /usr/local
ENV SPARK_HOME=/usr/local/spark
ENV PATH=${PATH}:${SPARK_HOME}/bin:${SPARK_HOME}/sbin

RUN cd /usr/local && \
  ln -s ./spark-${SPARK_VERSION}-bin-hadoop3 spark

EXPOSE 4040 7077 8080 8081

注意:镜像制作时不需要在 Dockerfile 中指定 ENTRYPOINT 和 CMD,这些在 Kubernetes YAML 中定义。

第三步:配置文件准备

准备服务运行所需的配置文件,存放在 artifacts 目录:

artifacts/hadoop/
├── bootstrap.sh              # 启动脚本
├── capacity-scheduler.xml    # 容量调度器配置
├── core-site.xml            # Hadoop 核心配置
├── hadoop-env.sh            # Hadoop 环境变量
├── hdfs-site.xml            # HDFS 配置
├── log4j2.properties        # 日志配置
├── mapred-site.xml          # MapReduce 配置
├── start-yarn-nm.sh         # NodeManager 启动脚本
├── start-yarn-rm.sh         # ResourceManager 启动脚本
├── yarn-env.sh              # YARN 环境变量
└── yarn-site.xml            # YARN 配置

第四步:Kubernetes 资源定义

根据应用特性选择合适的 Kubernetes 资源对象。由于 NodeManager 需要使用主机名向 ResourceManager 注册,采用 StatefulSet 和 Headless Service。

配置文件存储在 manifests 目录:

manifests/
├── namespace.yaml           # 命名空间
├── configmap.yaml          # 配置映射
├── yarn-rm-statefulset.yaml # ResourceManager
├── yarn-nm-statefulset.yaml # NodeManager  
├── spark-statefulset.yaml  # Spark 服务
└── ingress.yaml            # 外部访问

ResourceManager StatefulSet 示例

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: yarn-rm
  namespace: yarn-cluster
spec:
  serviceName: yarn-rm
  replicas: 1
  selector:
  matchLabels:
    app: yarn-rm
  template:
  metadata:
    labels:
    app: yarn-rm
  spec:
    containers:
    - name: yarn-rm
    image: hadoop:latest
    resources:
      requests:
      memory: "2Gi"
      cpu: "1000m"
      limits:
      memory: "4Gi" 
      cpu: "2000m"
    env:
    - name: HADOOP_ROLE
      value: "resourcemanager"
    volumeMounts:
    - name: hadoop-config
      mountPath: /tmp/hadoop-config
    volumes:
    - name: hadoop-config
    configMap:
      name: hadoop-config

第五步:启动脚本编写

Bootstrap 脚本根据 Pod 环境变量和主机名动态修改配置并启动相应服务:

#!/bin/bash

# 复制配置文件
cp /tmp/hadoop-config/* $HADOOP_CONF_DIR/

# 根据角色启动不同服务
if [[ "${HOSTNAME}" =~ "yarn-rm" ]]; then
  echo "Starting ResourceManager..."
  # 修改 ResourceManager 特定配置
  sed -i "s/RESOURCEMANAGER_HOST/${HOSTNAME}/g" $HADOOP_CONF_DIR/yarn-site.xml
  
  # 启动 ResourceManager
  $HADOOP_PREFIX/sbin/yarn-daemon.sh start resourcemanager
  
elif [[ "${HOSTNAME}" =~ "yarn-nm" ]]; then
  echo "Starting NodeManager..."
  # 动态设置资源限制
  sed -i "s/MEMORY_LIMIT/${MY_MEM_LIMIT:-2048}/g" $HADOOP_CONF_DIR/yarn-site.xml
  sed -i "s/CPU_LIMIT/${MY_CPU_LIMIT:-2}/g" $HADOOP_CONF_DIR/yarn-site.xml
  
  # 启动 NodeManager
  $HADOOP_PREFIX/sbin/yarn-daemon.sh start nodemanager
fi

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

第六步:ConfigMap 创建

将配置文件作为 ConfigMap 资源保存:

# 创建 Hadoop 配置
kubectl create configmap hadoop-config \
  --from-file=artifacts/hadoop/ \
  --namespace=yarn-cluster

# 创建 Spark 配置  
kubectl create configmap spark-config \
  --from-file=artifacts/spark/ \
  --namespace=yarn-cluster

部署与管理

配置完成后,可以使用以下命令部署和管理集群:

# 创建命名空间
kubectl apply -f manifests/namespace.yaml

# 部署 ConfigMaps
kubectl apply -f manifests/configmap.yaml

# 部署服务
kubectl apply -f manifests/yarn-rm-statefulset.yaml
kubectl apply -f manifests/yarn-nm-statefulset.yaml
kubectl apply -f manifests/spark-statefulset.yaml

# 配置外部访问
kubectl apply -f manifests/ingress.yaml

最佳实践与注意事项

  1. 资源限制:合理设置 CPU 和内存限制,避免资源争抢
  2. 健康检查:配置 liveness 和 readiness 探针
  3. 数据持久化:对于有状态服务,使用 PersistentVolume
  4. 网络策略:配置适当的网络安全策略
  5. 监控告警:集成监控系统,及时发现问题
  6. 备份恢复:制定完整的备份恢复策略

通过以上步骤,可以成功将传统的 Hadoop YARN 应用迁移到 Kubernetes 平台。整个过程需要充分理解原应用架构和 Kubernetes 特性,确保迁移后的系统稳定可靠。

文章导航

章节内容

这是章节的内容页面。

章节概览

评论区