本文先介绍一下K8s的ConfigMap资源和Ingress的基本概念,然后讲一下如何在K8s上部署Flink,最后看一下Flink的Operator又是怎么编写的
问题背景:
生产环境可能需要多个Config文件,命令行参数和环境变量进行配置组合,因此容器部署的时候,应该把配置从应用程序的镜像中解耦出来,以保证镜像的可移植性.
ConfigMap是用来存储通用的配置变量的,类似于配置文件,使得用户可以将分布式系统中用于不同模块的环境变量统一到一个对象中进行管理.
他可以看做K8s的一个基本资源类型,是一个键值组类型,存储被Pod或者其他资源对象访问的信息.但与secret相比,主要区别在于其不存储敏感信息,只存储简单的文本信息
简而言之: 在创建pod的时候对configmap进行绑定,pod内的应用可以直接引用ConfigMap的配置,相当于configmap为应用/运行环境封装配置.
创建pod的时候要绑定ConfigMap,通常用于: 设置环境变量的值,设置命令行参数等
ConfigMap的核心是把配置数据注入容器的方式,同时保证这个机制对容器来说是透明的.
ConfigMap可以用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象
eg;
ConfigMap可以包含细粒度的配置项,如:example.property.1;也可以包含粗粒度的配置文件,如:example.property.file
kind: ConfigMap
apiVersion: v1
metadata:
creationTimestamp: 2016-02-18T19:14:38Z
name: example-config
namespace: default
data:
# example of a simple property defined using --from-literal
example.property.1: hello
example.property.2: world
# example of a complex property defined using --from-file
example.property.file: |-
property.1=value-1
property.2=value-2
LoadBalancer和NodePort都是Svc向外暴露服务的方法,那么Ingress有什么不同呢?
根据官方文档,Ingress公开了从集群外部到集群内Services的HTTP和HTTPS路由,流量路由由Ingress资源上定义的规则来控制.
Ingress的作用:
Ingress controller 负责来实现Ingress,经常是使用一个负载均衡器来实现的.
那如果要把HTTP或者HTTPS以外的服务公开给外部的时候,通常使用Service.Type=NodePort或者Service.Type=LoadBalancer
eg:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
backend:
serviceName: test
servicePort: 80
选一些独特的来介绍:
1.annotations:
注解,用于配置一些选项
2.Ingress规则
上面的yaml文件没有注明,说明规则适用于通过特定IP地址进站的HTTP流量,如果指定了主机,那么规则只会适用于该主机
路径列表
每一个路径对应一个后端
后端
一个后端由serviceName
和servicePort
来定义,与规则的主机和路径匹配的对Ingress的HTTP请求将会发送到后端.
Flink由Job Manager和Task Manager两部分组成,Job Manager负责协调流处理作业,管理作业的提交以及生命周期,并且把工作分配给任务管理器.任务管理器执行实际的流处理逻辑,同一个时间只能有一个活跃的Job Manager,但是可以有多个Task Manager
Flink还引入了Checkpoint机制,来周期性记录各种流处理操作的状态,并且进行持久化存储.在从故障中恢复的时候,流处理作业可以从最新的检查点继续执行.
Checkpoint也是由Job Manager进行协调和更新的
Job Manager和Task Manager都采用deployment来进行部署
另外还需要定义相应的configmap和service文件,使其能够暴露一个端口供外界访问
首先配置ConfigMap
configMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: flink-config
labels:
app: flink
data:
flink-conf.yaml: |+
jobmanager.rpc.address: flink-jobmanager
taskmanager.numberOfTaskSlots: 1
blob.server.port: 6124
jobmanager.rpc.port: 6123
taskmanager.rpc.port: 6122
jobmanager.heap.size: 1024m
taskmanager.heap.size: 1024m
log4j.properties: |+
log4j.rootLogger=INFO, file
log4j.logger.akka=INFO
log4j.logger.org.apache.kafka=INFO
log4j.logger.org.apache.hadoop=INFO
log4j.logger.org.apache.zookeeper=INFO
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.file=${log.file}
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
log4j.logger.org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline=ERROR, file
主要是把日志文件以及配置文件和创建Pod解耦开来
那相应的deployment要怎么和configMap绑定呢?
jobManager-deployment.yaml
在volumes那里,configMap指定定义好的configMap的名称,items项,并且指定好key:path键值对
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: flink-jobmanager
spec:
replicas: 1
template:
metadata:
labels:
app: flink
component: jobmanager
spec:
containers:
- name: jobmanager
image: flink:latest
workingDir: /opt/flink
command: ["/bin/bash", "-c", "$FLINK_HOME/bin/jobmanager.sh start;\
while :;
do
if [[ -f $(find log -name '*jobmanager*.log' -print -quit) ]];
then tail -f -n +1 log/*jobmanager*.log;
fi;
done"]
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob
- containerPort: 8081
name: ui
livenessProbe:
tcpSocket:
port: 6123
initialDelaySeconds: 30
periodSeconds: 60
volumeMounts:
- name: flink-config-volume
mountPath: /opt/flink/conf
volumes:
- name: flink-config-volume
configMap:
name: flink-config
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- key: log4j.properties
path: log4j.properties
taskmanager-deployment.yaml
同理,设置task-manager的镜像
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: flink-taskmanager
spec:
replicas: 2
template:
metadata:
labels:
app: flink
component: taskmanager
spec:
containers:
- name: taskmanager
image: flink:latest
workingDir: /opt/flink
command: ["/bin/bash", "-c", "$FLINK_HOME/bin/taskmanager.sh start; \
while :;
do
if [[ -f $(find log -name '*taskmanager*.log' -print -quit) ]];
then tail -f -n +1 log/*taskmanager*.log;
fi;
done"]
ports:
- containerPort: 6122
name: rpc
livenessProbe:
tcpSocket:
port: 6122
initialDelaySeconds: 30
periodSeconds: 60
volumeMounts:
- name: flink-config-volume
mountPath: /opt/flink/conf/
volumes:
- name: flink-config-volume
configMap:
name: flink-config
items:
- key: flink-conf.yaml
path: flink-conf.yaml
- key: log4j.properties
path: log4j.properties
kubectl create -f flink-configuration-configmap.yaml
kubectl create -f jobmanager-service.yaml
kubectl create -f jobmanager-deployment.yaml
kubectl create -f taskmanager-deployment.yaml
然后可以使用多种方式访问UI
kubectl proxy
1.运行kubectl proxy
2.前往 http://localhost:8001/api/v1/namespaces/default/services/flink-jobmanager:ui/proxy
kubectl port-forward
kubectl port-forward flink-jobmanager-845f844595-lcpxw 8081:8081
访问 http://localhost:8081
那我们分别写了Deployment和Service的yaml文件,很容易就能联想到,我们是否能干脆写一个Flink的K8s operator呢?这样管理应用状态会更加地方便
https://github.com/lyft/flinkk8soperator
最后我们再来看一下如何利用Lyft开源的flink operator来定义一个Flink
的Job
先占个坑,以后更新
https://blog.csdn.net/bbwangj/article/details/81776648
https://blog.csdn.net/cpongo3/article/details/89027097