kubernetes-kafka をminikubeの開始点として使用します。
これは、クラスター内のサービス検出にStatefulSetと headless service を使用します。
目標は、個々のKafkaブローカーを外部に公開することです。これらのブローカーは、以下のように内部的に対処されます。
kafka-0.broker.kafka.svc.cluster.local:9092
kafka-1.broker.kafka.svc.cluster.local:9092
kafka-2.broker.kafka.svc.cluster.local:9092
制約は、この外部サービスがブローカーに具体的に対処できることです。
これについて正しい(または可能な1つの)方法は何ですか? kafka-x.broker.kafka.svc.cluster.local:9092
ごとに外部サービスを公開することは可能ですか?
これまでのソリューションは、私自身にとって十分に満足のいくものではなかったので、自分の答えを投稿します。私の目標:
Yolean/kubernetes-kafka で始まる、欠けている唯一のことは、サービスを外部に公開することと、そうすることの2つの課題です。
ポッドごとのラベルと外部サービス:
ポッドごとにラベルを生成するには、 この問題 が非常に役立ちました。これをガイドとして使用して、次の行を 10broker-config.ymlinit.sh
プロパティに追加します。
kubectl label pods ${HOSTNAME} kafka-set-component=${HOSTNAME}
既存のヘッドレスサービスを保持しますが、ラベルを使用してポッドごとに外部サービスも生成します( 20dns.yml に追加しました):
apiVersion: v1
kind: Service
metadata:
name: broker-0
namespace: kafka
spec:
type: NodePort
ports:
- port: 9093
nodePort: 30093
selector:
kafka-set-component: kafka-0
内部/外部リスナーでKafkaを構成する
この問題 は、Kafkaの設定方法を理解しようとする際に非常に役立ちました。
この場合も、 10broker-config.yml のinit.sh
およびserver.properties
プロパティを次のように更新する必要があります。
以下をserver.properties
に追加して、セキュリティプロトコルを更新します(現在PLAINTEXT
を使用):
listener.security.protocol.map=INTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT
inter.broker.listener.name=INTERNAL_PLAINTEXT
init.sh
の各Podの外部IPおよび外部ポートの動的な決定:
EXTERNAL_LISTENER_IP=<your external addressable cluster ip>
EXTERNAL_LISTENER_PORT=$((30093 + ${HOSTNAME##*-}))
次に、advertised.listeners
およびEXTERNAL_LISTENER
のlisteners
およびINTERNAL_LISTENER
IPを構成します(init.sh
プロパティでも)。
sed -i "s/#listeners=PLAINTEXT:\/\/:9092/listeners=INTERNAL_PLAINTEXT:\/\/0.0.0.0:9092,EXTERNAL_PLAINTEXT:\/\/0.0.0.0:9093/" /etc/kafka/server.properties
sed -i "s/#advertised.listeners=PLAINTEXT:\/\/your.Host.name:9092/advertised.listeners=INTERNAL_PLAINTEXT:\/\/$HOSTNAME.broker.kafka.svc.cluster.local:9092,EXTERNAL_PLAINTEXT:\/\/$EXTERNAL_LISTENER_IP:$EXTERNAL_LISTENER_PORT/" /etc/kafka/server.properties
明らかに、これは本番用の完全なソリューションではありません(たとえば、外部に公開されたブローカーのセキュリティに対処するため)。また、社内の生産者/消費者がブローカーとも通信できるようにする方法についての理解をさらに高めています。
ただし、これまでのところ、これはKubernetesとKafkaを理解するための最良のアプローチです。
1.7では、ヘッドレスサービスをType=NodePort
に変更し、externalTrafficPolicy=Local
を設定することでこれを解決しました。これにより、サービスの内部負荷分散がバイパスされ、そのノードポート上の特定のノード宛てのトラフィックは、そのノードにKafkaポッドがある場合にのみ機能します。
apiVersion: v1
kind: Service
metadata:
name: broker
spec:
externalTrafficPolicy: Local
ports:
- nodePort: 30000
port: 30000
protocol: TCP
targetPort: 9092
selector:
app: broker
type: NodePort
たとえば、2つのノードnodeAとnodeBがあり、nodeBはkafkaポッドを実行しています。 nodeA:30000は接続しませんが、nodeB:30000はnodeBで実行されているkafkaポッドに接続します。
https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typenodeport
これは1.5および1.6でもベータアノテーションとして利用可能でしたが、機能の可用性についてはこちらをご覧ください: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load -balancer /#preserving-the-client-source-ip
また、これはkafkaポッドを特定の外部ネットワークIDに結び付けますが、ストレージボリュームがそのネットワークIDに結び付けられることを保証するものではないことに注意してください。 StatefulSetでVolumeClaimTemplatesを使用している場合、ボリュームはポッドに関連付けられますが、kafkaはボリュームがネットワークIDに関連付けられることを期待します。
たとえば、kafka-0ポッドが再起動し、kafka-0がnodeAではなくnodeCで起動した場合、kafka-0のpvc(VolumeClaimTemplatesを使用している場合)にはnodeA用のデータがあり、kafka-0で実行されているブローカーはリクエストを拒否し始めますそれはnodeCではなくnodeAであること。
これを修正するために、Local Persistent Volumesを楽しみにしていますが、現時点ではkafka StatefulSetの単一のPVCがあり、そのPVCの$NODENAME
の下にデータを保存して、ボリュームデータを特定のノード。
https://github.com/kubernetes/features/issues/121https://kubernetes.io/docs/concepts/storage/volumes/#local
私は、この質問と回答を3回読んだ後、ヘッドレスサービスとは何か、それらのポイントは何であるかについて頭をかき回そうとしたことを言いたいです。 (そして、私はヘッドレスサービス、またはこのQ&Aが何であったかを完全には理解していませんでした。)
そして4回目の読解(さらに自分自身を教育した後、もう一度読む)で、ついにクリックしました/ついに理解しました。
したがって、この答えの目的は、Nadirの質問/問題/およびそれを小学生に説明するかのように答えることです。これにつまずく他の人がNadirの素晴らしいソリューションの重要性最初の読み取り。
有用な背景知識:
タイプがExternalNameのサービスが存在します。
ExternalNameサービスは、単にDNSアドレスを指します。
ExternalNameサービスには2つのフレーバーがあります。
ステートフルセットには、そのアイデンティティに対して3つの部分があります。
Kube-Proxyについて覚えておくべき3つの重要なことがあります。
ヘッドレスサービスについて覚えておくべき4つの重要なことがあります。
問題の理解が深まったので、次の質問に戻りましょう:ヘッドレスサービス(ステートフルセットの個々のメンバーを指す)を外部に公開するにはどうすればよいですか?
ソリューションパート1:
クラスター内の任意のポッドがステートフルセットのメンバーと通信できます。
ステートフルは、次の形式の予測可能な内部クラスターDNSアドレスを使用して、ヘッドレスサービスを生成するためです。
statefulsetname-#。associatedheadlessservice.namespace.svc.cluster.local:port
kafka-0.broker.kafka.svc.cluster.local:9092
kafka-1.broker.kafka.svc.cluster.local:9092
kafka-2.broker.kafka.svc.cluster.local:9092
broker.kafka.svc.cluster.local:9092は、使用可能なものを参照するためにも使用できます。
ソリューションパート2:
外部トラフィックを受け入れることができる2つ目のサービスを導入し、そのサービスからのトラフィックをインターネットトラフィックのみを受け入れるヘッドレスサービスにリダイレクトすることにより、外部トラフィックがステートフルセットのメンバーと通信できるようにします。
ステートフルセットの各ポッドに対して、Kube-Proxyによって管理される仮想静的ClusterIPアドレスを持つタイプExternalNameのサービスが作成されます。これらのExternalName Servicesのそれぞれは、ソリューション1で識別された予測可能な静的内部クラスターDNSアドレスをポイント/リダイレクトします。このExternalNameサービスにはKube-Proxyを介して管理される仮想静的ClusterIPがあるため、NodePortsからそれにマッピングすることができます。
サービスをヘッドレスClusterIPからNodePortに変更します。NodePortは、設定ポート(私の例では30092)のnodesのいずれかに要求を転送し、Kafkasのポート9042に転送します。ポッドの1つをランダムにヒットしますが、それで問題ないと思います。
20dns.ymlは次のようになります:
# A no longer headless service to create DNS records
---
apiVersion: v1
kind: Service
metadata:
name: broker
namespace: kafka
spec:
type: NodePort
ports:
- port: 9092
- nodePort: 30092
# [podname].broker.kafka.svc.cluster.local
selector:
app: kafka
免責事項:2つのサービスが必要になる場合があります。 1つは内部DNS名用のヘッドレス、もう1つは外部アクセス用のNodePortです。私はこれを自分で試したことはありません。
kubernetes kafka documentation から:
ホストポートを使用した外部アクセス
別の方法は、外部アクセスにホストポートを使用することです。これを使用する場合、各ホストで実行できるkafkaブローカーは1つだけです。
ホストポートに切り替えるには、kafkaアドバタイズアドレスを、ブローカーを実行しているノードのExternalIPまたはExternalDNS名に切り替える必要があります。 kafka/10broker-config.ymlで切り替えます
OUTSIDE_Host=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}') OUTSIDE_PORT=${OutsidePort}
そしてkafka/50kafka.ymlにホストポートを追加します:
- name: outside containerPort: 9094 hostPort: 9094