ローカルDocker環境で動いていたkafkaを、Kubernetes環境で動かしてみた

## 概要
Kafkaに魅せられて、とりあえず手元のマシンで動かしてみましょうか、、、と、インフラしか知らないSEがMacBookProを新たに購入し、Qiita等にアップされている諸先輩方の記事を参考にさせていただき、ローカルのDocker環境でKafkaを動作確認したまでの手順を、過去に７回に分け記載しました。その７回に記事については[こちらから参照ください](https://qiita.com/turupon/items/12268ddb95ecd7b7ae07)。
今回は、そのDocker環境で動いていたKafka検証システムの docker-compose.yml を Kubernetes環境のマニファスト（yaml形式）でどのように定義し直すのかを確認しました。

今回のKubernetes環境の Kafka検証システム の全体構成は下図のようになります。
![kafka-k8s-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544022/d9aef4d8-c60c-631a-a65f-855da503143d.png)

上図に従い、マニフェストを定義しています。

|Pod|Manifest|
|-|-|
|Producer/Comsumer|python-iot-dev.yaml| 
|zookeeper|zookeeper-v1.yaml|
|broker|broker-v1.yaml|
|ksql-server/ksql-cli|ksql-v1.yaml|


なお、Producwe/Consumer については、Docker Image 作成のため、Dockerfile も定義しています。

## 実行環境
macOS Big Sur 11.1 
Docker version 20.10.2, build 2291f61
Python 3.8.3

## Kubernetes環境
Diamanti spektra kubernetes 
Harbor
windows DNS


## 作業のディレクトリ構成
作業のディレクトリ構成は以下となります

```
$ tree                
.
├── Dockerfile
├── broker-v1.yaml
├── ksql-v1.yaml
├── opt
│   ├── Consumer-v1.py
│   └── Producer-v1.py
├── python-iot-dev.yaml
├── requirements.txt
└── zookeeper-v1.yaml
```

## Producer/Consumer の Dockerfile
Producer/Consumer で共通のDocker Image となります。
Producer/Consumerで稼働させるプログラムをコピーしておきます。

```Dockerfile
FROM python:3.8.3-slim
USER root

RUN apt-get update
RUN apt-get -y install locales && localedef -f UTF-8 -i ja_JP ja_JP.UTF-8

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN apt-get install -y vim less
RUN apt-get install -y dnsutils iputils-ping net-tools procps netcat

COPY requirements.txt .
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN pip install -r requirements.txt

WORKDIR /opt/app
COPY ./opt/*.py /opt/app/.
```

## マニフェスト
zookeeper、broker、 ksql-server/ksql-cli、Producer/Comsumer の４つに分けて定義しました。

```python-iot-dev.yaml
### Producer/Comsumer ###
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iot
  namespace: spektra-demo-tenant01-project-nw-kafka
  labels:
    app: iot
spec:
  replicas: 2
  selector:
    matchLabels:
      app: iot
  template:
    metadata:
      namespace: spektra-demo-tenant01-project-nw-kafka
      labels:
        app: iot
    spec:
      containers:
      - name: iot-dev
        resources:
          limits:
            cpu: 1000m
            memory: 1Gi
        image: harbor.nwdiamanti.local/nw-kafka/python-iot:1.0.0
        tty: true
      dnsConfig:
        options:
        - name: ndots
          value: "1"
      imagePullSecrets:
      - name: harbor
```

```zookeeper-v1.yaml
### zookeeper ###
apiVersion: v1
kind: Service
metadata:
  name: kafka-zookeeper
  namespace: spektra-demo-tenant01-project-nw-kafka
spec:
  ports:
  - port: 32181
    protocol: TCP
    targetPort: 32181
  selector:
    app: kafka-zookeeper
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-zookeeper
  namespace: spektra-demo-tenant01-project-nw-kafka
  labels:
    app: kafka-zookeeper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-zookeeper
  template:
    metadata:
      namespace: spektra-demo-tenant01-project-nw-kafka
      labels:
        app: kafka-zookeeper
    spec:
      containers:
      - name: zookeeper
        image: confluentinc/cp-zookeeper:5.5.1
        imagePullPolicy: Always
        resources:
          limits:
            cpu: 1000m
            memory: 1Gi
        env:
        - name: ZOOKEEPER_CLIENT_PORT
          value: "32181"
        - name: ZOOKEEPER_TICK_TIME
          value: "2000"
        ports:
        - containerPort: 32181
          protocol: TCP
      dnsConfig:
        options:
        - name: ndots
          value: "1"
```

```broker-v1.yaml
### broker ###
apiVersion: v1
kind: Service
metadata:
  name: kafka-broker
  namespace: spektra-demo-tenant01-project-nw-kafka
spec:
  ports:
  - port: 29092
    protocol: TCP
    targetPort: 29092
  selector:
    app: kafka-broker
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-broker
  namespace: spektra-demo-tenant01-project-nw-kafka
  labels:
    app: kafka-broker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-broker
  template:
    metadata:
      namespace: spektra-demo-tenant01-project-nw-kafka
      labels:
        app: kafka-broker
    spec:
      containers:
      - name: broker
        image: confluentinc/cp-kafka:5.5.1
        imagePullPolicy: Always
        resources:
          limits:
            cpu: 1000m
            memory: 2Gi
        env:
        - name: KAFKA_BROKER_ID
          value: "1"
        - name: KAFKA_ZOOKEEPER_CONNECT
          value: kafka-zookeeper:32181
        - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
          value: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
        - name: KAFKA_ADVERTISED_LISTENERS
          value: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092
        - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
          value: "1"
        - name: CONFLUENT_SUPPORT_METRICS_ENABLE
          value: "false"
        ports:
        - containerPort: 9092
          protocol: TCP
        - containerPort: 29092
          protocol: TCP
      dnsConfig:
        options:
        - name: ndots
          value: "1"
```

```ksql-v1.yaml
### ksql-server ###
apiVersion: v1
kind: Service
metadata:
  name: kafka-ksql-server
  namespace: spektra-demo-tenant01-project-nw-kafka
spec:
  ports:
  - port: 8088
    protocol: TCP
    targetPort: 8088
  selector:
    app: kafka-ksql-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-ksql-server
  namespace: spektra-demo-tenant01-project-nw-kafka
  labels:
    app: kafka-ksql-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-ksql-server
  template:
    metadata:
      namespace: spektra-demo-tenant01-project-nw-kafka
      labels:
        app: kafka-ksql-server
    spec:
      containers:
      - name: ksql-server
        image: confluentinc/cp-ksql-server:5.4.3
        imagePullPolicy: Always
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
        env:
        - name: KSQL_CONFIG_DIR
          value: /etc/ksql
        - name: KSQL_LOG4J_OPTS
          value: -Dlog4j.configuration=file:/etc/ksql/log4j-rolling.properties
        - name: KSQL_BOOTSTRAP_SERVERS
          value: kafka-broker:29092
        - name: KSQL_HOST_NAME
          value: ksql-server
        - name: KSQL_APPLICATION_ID
          value: IoT-K8S-1
        - name: KSQL_LISTENERS
          value: http://0.0.0.0:8088
        - name: KSQL_CACHE_MAX_BYTES_BUFFERING
          value: "0"
        - name: KSQL_AUTO_OFFSET_RESET
          value: earliest
        ports:
        - containerPort: 8088
          protocol: TCP
      dnsConfig:
        options:
        - name: ndots
          value: "1"
---
### ksql-cli ###
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-ksql-cli
  namespace: spektra-demo-tenant01-project-nw-kafka
  labels:
    app: kafka-ksql-cli
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-ksql-cli
  template:
    metadata:
      namespace: spektra-demo-tenant01-project-nw-kafka
      labels:
        app: kafka-ksql-cli
    spec:
      containers:
      - name: ksql-cli
        image: confluentinc/cp-ksql-cli:5.4.3
        imagePullPolicy: Always
        resources:
          limits:
            cpu: 1000m
            memory: 512Mi
        tty: true
      dnsConfig:
        options:
        - name: ndots
          value: "1"
```

## Docker環境下での Dockerfile と docker-compose.yml の過去情報
Producer/Comsumer の Dockerfile と docker-compose.yaml については[こちらを参照ください](https://qiita.com/turupon/items/60ac1ec68fe8148b9201)。
zookeeper と broker と ksql-server/ksql-cli の docker-compose.yaml については[こちらを参照ください](https://qiita.com/turupon/items/540be949ead3f4065553)。


## Harbor へ Producer/Comsumer の Docker Image の Push
Docker Image を作成し、コンテナレジストリ（Harbor）に Push します。

```
$ docker image build -t python-iot:3.8.3-slim .
$ docker image ls                            
REPOSITORY        TAG            IMAGE ID       CREATED          SIZE
python-iot        3.8.3-slim     5966a0f42ed5   29 minutes ago   331MB

$ docker tag python-iot:3.8.3-slim harbor.nwdiamanti.local/nw-kafka/python-iot:1.0.0
$ docker login harbor.nwdiamanti.local
$ docker push harbor.nwdiamanti.local/nw-kafka/python-iot:1.0.0
```

## 各Pod の作成
４つのマニフェストから各種Podの起動と確認

```
$ kubectl apply -f python-iot-dev.yaml 
deployment.apps/iot created

$ kubectl apply -f zookeeper-v1.yaml 
　service/kafka-zookeeper created
　deployment.apps/kafka-zookeeper created

$ kubectl apply -f broker-v1.yaml 
　service/kafka-broker created
　deployment.apps/kafka-broker created

$ kubectl apply -f ksql-v1.yaml 
  service/kafka-ksql-server created
  deployment.apps/kafka-ksql-server created
  deployment.apps/kafka-ksql-cli created

$ kubectl get pod -n spektra-demo-tenant01-project-nw-kafka -o wide
   NAME                                READY   STATUS    RESTARTS   AGE
   iot-b948b544-82nhj                  1/1     Running   0          12h   <--- Producer使用
   iot-b948b544-whkbn                  1/1     Running   0          12h   <--- Consumer使用
   kafka-broker-f79866747-vp6tc        1/1     Running   0          12h
   kafka-ksql-cli-64565d78db-fx6xz     1/1     Running   0          12h
   kafka-ksql-server-f8766c6f5-mtgqz   1/1     Running   0          12h
   kafka-zookeeper-6bf5c4c6bf-88zrz    1/1     Running   0          12h
```


## 実行結果
もちろん、[これ](https://qiita.com/turupon/items/bd27834a42dda865469d) と同じ結果となりました。

## 最後に
アプリ屋さんが、kubectl等のコマンドを叩き、Kubernetes環境下でのコンテナプログラムの動きまでを体感しましたが、やはり、アプリ屋が本業に注力するためには、CI/CDの仕組の導入が必要だと痛感した。


## 参考情報
以下の情報を参考にさせていただきました。感謝申し上げます。
[Kubernetes まとめ](https://qiita.com/kanazawa1226/items/53869878fd0679b0944b)
[Kubernetes のServiceについてまとめてみた](https://qiita.com/kouares/items/94a073baed9dffe86ea0)
[コンテナリポジトリ Harbor in kubernetes の検証](https://qiita.com/ysakashita/items/12e2f1e902ca5cbd56ed)
[Kubernetes ：kubectl コマンド一覧](https://qiita.com/suzukihi724/items/241f7241d297a2d4a55c)
[Kubernetes のポッドが起動しない原因と対策](https://qiita.com/minodisk/items/547741b73763f2bab6b8)
[Kubernetes 1.10 Cluster内DNSの挙動確認](https://qiita.com/sugimount/items/1873d9d332a25f5b0167)