在本章中,我们将介绍在Kubernetes上部署关键的大数据技术——Spark、Airflow和Kafka。随着容器编排和管理在高效运行数据工作负载中的重要性日益增加,Kubernetes已经成为事实上的标准。通过学习本章的内容,你将能够成功地在Kubernetes上部署和管理大数据堆栈,以构建稳健的数据管道和应用程序。
我们将从使用Spark Operator在Kubernetes上部署Apache Spark开始。你将学习如何在Kubernetes集群上配置和监控作为Spark应用运行的Spark作业。能够在Kubernetes上运行Spark工作负载带来了重要的好处,如动态扩展、版本控制和统一资源管理。
接下来,我们将在Kubernetes上部署Apache Airflow。你将配置在Kubernetes上的Airflow,将其日志链接到S3以便于调试和监控,并设置其以编排使用Spark等工具构建的数据管道。在Kubernetes上运行Airflow可以提高可靠性、扩展性和资源利用率。
最后,我们将在Kubernetes上部署Apache Kafka,这对于流数据管道至关重要。在Kubernetes上运行Kafka可以简化操作、扩展和集群管理。
通过本章的学习,你将获得在Kubernetes上部署和管理大数据堆栈的实践经验。这将使你能够利用Kubernetes作为容器编排平台构建稳健可靠的数据应用程序。
本章主要内容:
- 在Kubernetes上部署Spark
- 在Kubernetes上部署Airflow
- 在Kubernetes上部署Kafka
技术要求
进行本章中的活动,你需要具备以下条件:
- 拥有一个AWS账户
- 安装了kubectl、eksctl和helm
关于如何设置AWS账户以及安装kubectl和eksctl的说明,请参见第1章。有关helm的安装说明,请访问helm安装文档。
我们还将使用Titanic数据集进行练习。你可以在以下链接找到我们将使用的版本:Titanic数据集。
本章中的所有代码都可以在GitHub仓库中的Chapter08文件夹下找到,访问链接:Bigdata-on-Kubernetes。
在Kubernetes上部署Spark
为了在Kubernetes上部署资源,我们将使用Helm。Helm是Kubernetes的包管理器,有助于安装应用程序和服务。Helm使用称为Charts的模板,将安装配置、默认设置、依赖项等打包成易于部署的包。
另一方面,我们有Operators。Operators是自定义控制器,扩展了Kubernetes API以管理应用程序及其组件。它们提供了一种声明性的方法来在Kubernetes上创建、配置和管理复杂的有状态应用程序。
使用Operators的主要好处包括:
- 简化的应用程序部署和生命周期管理:Operators抽象掉了底层细节,并提供高层次的抽象,使得无需了解Kubernetes的复杂性即可部署应用程序。
- 与监控工具的集成:Operators暴露自定义指标和日志,使得可以与监控堆栈(如Prometheus和Grafana)集成。
- Kubernetes原生:Operators利用Kubernetes的可扩展性,专为Kubernetes编写,使其能够实现云无关性。
Operators通过创建自定义资源定义(CRD)和控制器来扩展Kubernetes。CRD允许你在Kubernetes中定义一种新资源类型。例如,SparkOperator定义了一个SparkApplication资源。
然后,Operator创建一个控制器,该控制器监视这些自定义资源并根据资源规格执行操作。
例如,当创建一个SparkApplication资源时,SparkOperator控制器将执行以下操作:
- 根据规格创建driver和executor Pods
- 挂载存储卷
- 监控应用程序的状态
- 执行日志记录和监控
部署步骤
创建AWS EKS集群
首先,我们使用eksctl创建一个AWS EKS集群:
eksctl create cluster --managed --alb-ingress-access --node-private-networking --full-ecr-access --name=studycluster --instance-types=m6i.xlarge --region=us-east-1 --nodes-min=3 --nodes-max=4 --nodegroup-name=ng-studycluster
记住,这段代码需要几分钟才能完成。现在,有一些重要的配置需要给我们的Kubernetes集群权限来代表我们创建卷。为此,我们需要安装AWS EBS CSI驱动程序。这对于部署Spark应用程序不是必须的,但对于Airflow的部署将非常重要。
首先,我们需要将IAM OIDC提供程序与EKS集群关联,使得IAM角色和用户可以对Kubernetes API进行身份验证。在终端中输入以下命令:
eksctl utils associate-iam-oidc-provider --region=us-east-1 --cluster=studycluster --approve
接下来,我们将在kube-system命名空间中创建一个名为ebs-csi-controller-sa的IAM服务账户,附带指定的IAM角色和策略。这个服务账户将由EBS CSI驱动程序使用:
eksctl create iamserviceaccount --name ebs-csi-controller-sa --namespace kube-system --cluster studycluster --role-name AmazonEKS_EBS_CSI_DriverRole --role-only --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy --approve
最后,我们将在集群中启用EBS CSI驱动程序,并将其与之前创建的服务账户和角色关联。记住将<YOUR_ACCOUNT_NUMBER>替换为实际值:
eksctl create addon --name aws-ebs-csi-driver --cluster studycluster --service-account-role-arn arn:aws:iam::<YOUR_ACCOUNT_NUMBER>:role/AmazonEKS_EBS_CSI_DriverRole --force
部署Spark Operator
首先,我们将创建一个命名空间来组织我们的资源:
kubectl create namespace spark-operator
接下来,我们将使用在线提供的SparkOperator Helm Chart来部署操作器:
helm install spark-operator https://github.com/kubeflow/spark-operator/releases/download/spark-operator-chart-1.1.27/spark-operator-1.1.27.tgz --namespace spark-operator --set webhook.enable=true
检查操作器是否正确部署:
kubectl get pods -n spark-operator
你应该看到类似以下的输出:
NAME READY STATUS
spark-operator-74db6fcf98-grhdw 1/1 Running
spark-operator-webhook-init-mw8gf 0/1 Completed
接下来,我们需要将AWS凭证注册为Kubernetes Secret,以便Spark可以使用它们访问AWS资源:
kubectl create secret generic aws-credentials --from-literal=aws_access_key_id=<YOUR_ACCESS_KEY_ID> --from-literal=aws_secret_access_key="<YOUR_SECRET_ACCESS_KEY>" -n spark-operator
开发Spark代码
现在,你应该已经将Titanic数据集存储在Amazon S3上。在这里可以找到一个简单的代码,该代码从S3桶中读取Titanic数据集并将其写入另一个桶中(此第二个S3桶必须已经创建——可以在AWS控制台中进行)。
将此文件保存为spark_job.py并上传到不同的S3桶。这是SparkOperator将查找代码以运行应用程序的位置。请注意,此PySpark代码与我们之前在第5章中看到的略有不同。在这里,我们将Spark配置与Spark会话分开设置。我们将详细介绍这些配置:
.set("spark.cores.max", "2"):将此Spark应用程序使用的最大核心数限制为2,防止资源分配过多。.set("spark.executor.extraJavaOptions", "-Dcom.amazonaws.services.s3.enableV4=true")和.set("spark.driver.extraJavaOptions", "-Dcom.amazonaws.services.s3.enableV4=true"):启用使用签名版本4认证读取和写入S3的支持,更加安全。.set("spark.hadoop.fs.s3a.fast.upload", True):启用S3A连接器的快速上传功能,提高保存数据到S3时的性能。.set("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem"):将S3文件系统实现设置为较新的、优化的s3a,而不是旧的s3连接器。.set("spark.hadoop.fs.s3a.aws.credentials.provider", "com.amazonaws.auth.EnvironmentVariableCredentials"):配置Spark从环境变量中获取AWS凭证,而不是在代码中直接指定它们。.set("spark.jars.packages", "org.apache.hadoop:hadoop-aws:2.7.3"):添加对Hadoop AWS模块的依赖,以便Spark可以访问S3。
请注意,默认情况下,Spark使用INFO级别的日志记录。在此代码中,我们将其设置为WARN以减少日志记录并提高日志的可读性。记得将<YOUR_BUCKET>替换为你自己的S3桶。
上传此代码到S3后,现在是时候创建一个包含SparkApplication定义的YAML文件了。代码内容可在这里找到。
该代码定义了一个新的SparkApplication资源。只有因为SparkOperator创建了SparkApplication自定义资源,这才是可能的。让我们仔细看看这个YAML定义在做什么。
YAML文件的第一块指定了apiVersion和资源类型为SparkApplication。它还设置了应用程序的名称和命名空间。
第二块定义了一个名为“ivy”的卷挂载,用于缓存依赖项,避免每次作业运行时重新获取它们。它挂载到driver和executors中的/tmp。
第三块配置了Spark属性,启用了Ivy缓存目录并设置了Kubernetes的资源分配批量大小。
第四块配置了Hadoop属性以使用S3A文件系统实现。
第五块将此Spark应用程序设置为Python应用程序,指定使用的Python版本,运行在集群模式,并指定Docker镜像——在这种情况下,是一个预先准备好的Spark镜像,集成了AWS和Kafka。它还定义了镜像将始终从Docker Hub拉取,即使它已经存在于集群中。
第六块指定了S3中主Python应用程序文件的位置和Spark版本——在这种情况下是3.1.1。
第七块将restartPolicy设置为Never,因此应用程序只运行一次。
剩余的块为driver和executor Pods设置了配置。在这里,我们设置了访问S3的AWS密钥秘密,请求一个核心和1GB内存用于driver和相同的资源用于executors,我们挂载了一个名为“ivy”的emptyDir卷用于缓存依赖项,并设置了Spark和driver Pod标签用于跟踪。
将此文件保存在计算机上,并且你已经在S3上拥有了.py文件后,现在是时候运行Spark应用程序了。在终端中输入以下命令:
kubectl apply -f spark_job.yaml -n spark-operator
我们可以检查应用程序是否成功提交:
kubectl get sparkapplication -n spark-operator
我们可以使用以下命令获取应用程序的更多详细信息:
kubectl describe sparkapplication/test-spark-job -n spark-operator
要查看我们的Spark应用程序的日志,输入以下命令:
kubectl logs test-spark-job-driver -n spark-operator
就这样!你刚刚在Kubernetes上运行了你的第一个Spark应用程序!Kubernetes不允许你部署另一个具有相同名称的作业,因此,要再次测试,你应该删除应用程序:
kubectl delete sparkapplication/test-spark-job -n spark-operator
现在,让我们看看如何使用官方Helm Chart在Kubernetes上部署Airflow。
在Kubernetes上部署Airflow
在Kubernetes上部署Airflow非常简单。然而,在Helm chart配置中有一些重要细节需要注意。
首先,我们将下载最新的Helm chart到本地环境:
helm repo add apache-airflow https://airflow.apache.org
接下来,我们需要配置一个custom_values.yaml文件,以更改chart的默认部署配置。这个YAML文件的示例可以在这里找到。我们不会逐行讲解整个文件,只关注本次部署所需的最重要配置:
- 在
defaultAirflowTag和airflowVersion参数中,确保设置为2.8.3。这是1.13.1 Helm chart版本可用的最新Airflow版本。 executor参数应设置为KubernetesExecutor。这确保Airflow将使用Kubernetes基础设施动态启动任务。- 在
env部分,我们将配置“远程日志记录”以允许Airflow将日志保存在S3中。这是审计和节省Kubernetes存储资源的最佳实践。这里,我们为Airflow配置了三个环境变量。第一个将远程日志记录设置为"True";第二个定义Airflow将日志写入的S3桶和文件夹;最后一个定义Airflow将在AWS中进行身份验证的“连接”。我们稍后将在Airflow UI中设置此连接。这个块的示例如下:
env:
- name: "AIRFLOW__LOGGING__REMOTE_LOGGING"
value: "True"
- name: "AIRFLOW__LOGGING__REMOTE_BASE_LOG_FOLDER"
value: "s3://airflow-logs-<YOUR_ACCOUNT_NUMBER>/airflow-logs/"
- name: "AIRFLOW__LOGGING__REMOTE_LOG_CONN_ID"
value: "aws_conn"
- 在
webserver块中,我们必须配置第一个用户的凭证和服务类型。service参数应设置为“LoadBalancer”,以便我们可以从浏览器访问Airflow UI。defaultUser块应如下所示:
defaultUser:
enabled: true
role: Admin
username: <YOUR_USERNAME>
email: admin@example.com
firstName: NAME
lastName: LASTNAME
password: admin
重要的是,在values文件中使用一个简单的密码,并在部署准备就绪后立即在UI中更改它。这样,你的凭证就不会以明文形式存储。这将是一个重大的安全事故。
redis.enabled参数应设置为false。由于我们使用的是Kubernetes执行器,Airflow不需要Redis来管理任务。如果我们不将此参数设置为false,Helm将仍然部署一个Redis Pod。- 最后,在
dags块中,我们将配置gitSync。这是将DAG文件发送到Airflow并在GitHub(或任何其他你喜欢的Git存储库)中保持更新的最简单方法。首先,你应该创建一个名为“dags”的文件夹来存储Python DAG文件。然后,配置gitSync块如下:
gitSync:
enabled: true
repo: https://github.com/<USERNAME>/<REPO_NAME>.git
branch: main
rev: HEAD
ref: main
depth: 1
maxFailures: 0
subPath: "dags"
请注意,为了提高可读性,我们省略了原文件中的若干注释。custom_values.yaml文件已准备好进行部署。现在我们可以在终端中运行以下命令:
helm install airflow apache-airflow/airflow --namespace airflow --create-namespace -f custom_values.yaml
此部署可能需要几分钟才能完成,因为Airflow将在使UI可用之前进行数据库迁移作业。
接下来,我们需要获取UI的LoadBalancer URL。在终端中输入以下命令:
kubectl get svc -n airflow
在EXTERNAL-IP列中,你会注意到airflow-webserver服务有一个非空值。复制此URL并将其粘贴到浏览器中,添加":8080"以访问Airflow的正确端口。
登录到UI后,点击菜单中的“Admin”和“Connections”以配置AWS连接。将名称设置为aws_conn(如我们在values文件中声明的),选择Amazon Web Services作为连接类型,并输入你的访问密钥ID和秘密访问密钥。(此时,你应该在本地存储了你的AWS凭证——如果没有,请在AWS控制台中转到IAM,为你的用户生成新的凭证。你将无法在屏幕上看到旧的凭证。)
最后,我们将使用从第5章改编的DAG代码,该代码将在Kubernetes上顺利运行。此DAG将自动从互联网下载Titanic数据集,执行简单计算,并打印结果,这些结果将在“日志”页面上访问。代码内容可在这里找到。
将此Python代码的副本上传到你的GitHub存储库,几秒钟后,它将显示在Airflow UI中。现在,激活你的DAG(点击开关按钮)并跟踪执行情况(图8.1)。
然后点击任何任务以选择它,并点击日志。你将看到Airflow日志直接从S3读取(图8.2)。
恭喜你!你现在已经成功地在 Kubernetes 上运行了 Airflow。在第10章,我们将把这些部件整合起来,构建一个完全自动化的数据管道。
接下来,我们将使用 Strimzi 操作员在 Kubernetes 上部署一个 Kafka 集群。让我们开始吧。
在 Kubernetes 上部署 Kafka
Strimzi 是一个开源操作员,它通过创建新的 CRDs(自定义资源定义),方便在 Kubernetes 上部署和管理 Kafka 集群。它由 Strimzi 项目开发和维护,该项目是 Cloud Native Computing Foundation(CNCF)的一部分。Strimzi 操作员提供了一种声明式的方法来管理 Kubernetes 上的 Kafka 集群。你不需要手动创建和配置 Kafka 组件,而是使用 Kubernetes 自定义资源定义你希望的 Kafka 集群状态。然后操作员会根据指定的配置部署和管理 Kafka 组件:
要在 Kubernetes 中部署 Strimzi,首先我们需要安装它的 Helm chart:
helm repo add strimzi https://strimzi.io/charts/
接下来,我们使用以下命令安装操作员:
helm install kafka strimzi/strimzi-kafka-operator --namespace kafka --create-namespace --version 0.40.0
我们可以用以下命令检查部署是否成功:
helm status kafka -n kafka
kubectl get pods -n kafka
现在,是时候配置我们的 Kafka 集群的部署了。这是一个使用新 CRDs 配置 Kafka 集群的 YAML 文件。让我们分块来更好地理解:
kafka_jbod.yaml
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: kafka-cluster
spec:
kafka:
version: 3.7.0
replicas: 3
代码的第一部分指定了 API 版本和正在定义的资源种类。在这种情况下,它是由 Strimzi 操作员管理的 Kafka 资源。然后,我们为 Kafka 资源定义元数据,具体是设置其名称为 kafka-cluster。下一个块指定了 Kafka brokers 的配置。我们设置 Kafka 版本为 3.7.0 并指定我们希望集群中有三个副本(Kafka broker 实例):
listeners:
- name: plain
port: 9092
type: internal
tls: false
- name: tls
port: 9093
type: internal
tls: true
- name: external
port: 9094
type: loadbalancer
tls: false
接下来,我们定义 Kafka brokers 的监听器。我们配置了三个监听器: plain:在端口 9092 上的内部监听器,没有 TLS 加密 tls:在端口 9093 上的内部监听器,启用了 TLS 加密 external:在端口 9094 上作为 LoadBalancer 服务公开的外部监听器,没有 TLS 加密
readinessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
livenessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
接下来的块配置了 Kafka brokers 的就绪和存活探针。就绪探针检查 broker 是否准备好接受流量,而存活探针检查 broker 是否仍在运行。initialDelaySeconds 参数指定在执行第一次探针之前要等待的秒数,timeoutSeconds 参数指定探针被认为失败的秒数:
config:
default.replication.factor: 3
num.partitions: 9
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 1
log.message.format.version: "3.7"
inter.broker.protocol.version: "3.7"
min.insync.replicas: 2
log.retention.hours: 2160
这个 kafka.config 块指定了 Kafka brokers 的各种配置选项,如默认复制因子、新主题的分区数、偏移量和事务状态日志主题的复制因子、日志消息格式版本和日志保留期(以小时计)。Kafka 的默认日志保留时间为7天(168小时),但我们可以根据需要更改此参数。重要的是要记住,更长的保留期意味着更多的磁盘存储使用,所以要小心:
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: 15Gi
deleteClaim: false
- id: 1
type: persistent-claim
size: 15Gi
deleteClaim: false
kafka.storage 块配置了 Kafka brokers 的存储。我们使用 Just a Bunch of Disks (JBOD) 存储类型,允许我们为每个 broker 指定多个持久卷。在这种情况下,我们定义了两个各为 15 GiB 的持久卷声明,设置 deleteClaim 为 false 以防止删除 Kafka 集群时删除持久卷声明:
resources:
requests:
memory: 512Mi
cpu: "500m"
limits:
memory: 1Gi
cpu: "1000m"
接下来,kafka.resources 块指定了 Kafka brokers 的资源请求和限制。我们请求 512 MiB 的内存和 500 millicpu,并将内存限制设置为 1 GiB,CPU限制设置为 1 cpu:
zookeeper:
replicas: 3
storage:
type: persistent-claim
size: 10Gi
deleteClaim: false
resources:
requests:
memory: 512Mi
cpu: "250m"
limits:
memory: 1Gi
cpu: "500m"
最后,我们有一个配置 ZooKeeper 集群的 zookeeper 块。我们为 ZooKeeper 指定了三个副本,使用 10 GiB 的持久卷声明作为存储,并设置与 Kafka brokers 相似的资源请求和限制。
一旦配置文件准备好在你的机器上,输入以下命令将集群部署到 Kubernetes:
kubectl apply -f kafka_jbod.yaml -n kafka
让我们检查 Kafka 集群是否正确部署:
kubectl get kafka -n kafka
这将产生以下输出:
NAME DESIRED KAFKA REPLICAS DESIRED ZK REPLICAS
kafka-class 3 3
我们还可以获得有关部署的详细信息:
kubectl describe kafka -n kafka
现在,检查 Pods:
kubectl get pods -n kafka
输出显示三个 Kafka brokers 和 ZooKeeper 实例:
NAME READY STATUS
kafka-class-kafka-0 1/1 Running
kafka-class-kafka-1 1/1 Running
kafka-class-kafka-2 1/1 Running
kafka-class-zookeeper-0 1/1 Running
kafka-class-zookeeper-1 1/1 Running
kafka-class-zookeeper-2 1/1 Running
恭喜!你现在有一个在 Kubernetes 上完全运行的 Kafka 集群。接下来的步骤是部署一个 Kafka Connect 集群,并为实时数据管道做好一切准备。我们现在不会进行这一步,以节省云成本,但我们会在第10章回到这个配置。
总结
在本章中,你学习了如何在 Kubernetes 上部署和管理诸如 Apache Spark、Apache Airflow 和 Apache Kafka 等关键的大数据技术。在 Kubernetes 上部署这些工具提供了几个好处,包括简化操作、更好的资源利用、扩展、高可用性和统一的集群管理。
你首先在 Kubernetes 上部署了 Spark 操作员并运行了一个 Spark 应用程序来处理来自 Amazon S3 的数据。这使你能够以云原生的方式利用 Kubernetes 运行 Spark 任务,利用动态资源分配和扩展。
接下来,你使用官方的 Helm 图表在 Kubernetes 上部署了 Apache Airflow。你配置了 Airflow 以 Kubernetes 执行器运行,使其能够动态启动 Kubernetes 上的任务。你还设置了对 Amazon S3 的远程日志记录,以便更轻松地监控和调试。在 Kubernetes 上运行 Airflow 提高了数据管道编排的可靠性、可扩展性和资源利用率。
最后,你使用 Strimzi 操作员在 Kubernetes 上部署了 Apache Kafka。你配置了一个包括 brokers、ZooKeeper 集群、持久存储卷和内部/外部监听器的 Kafka 集群。在 Kubernetes 上部署 Kafka 简化了操作、扩展、高可用性和集群管理,为构建流数据管道提供了便利。
总的来说,你现在拥有了在 Kubernetes 上部署和管理大数据堆栈关键组件的实践经验。这将使你能够构建强大、可扩展的数据应用程序和管道,利用 Kubernetes 的容器编排能力。在本章中学到的技能对于在云原生环境中高效运行大数据工作负载至关重要。
在下一章,我们将看到如何在 Kubernetes 之上构建数据消费层,并如何通过工具连接这些层以可视化和使用数据。