LoginSignup
5
3

More than 1 year has passed since last update.

Kubernetes Service カテゴリのハンズオンを輪読会でやってみた

Posted at

はじめに

皆さんこんにちはasmgです。普段社内の新卒有志でKubernetes完全ガイド輪読会(勉強会)をおこなっています。
その中で、Kubernetes Serviceカテゴリのハンズオンを行なったのでその内容をまとめていきたいと思います。

本記事の対象者

  • 本記事では、Kubernetes完全ガイド第2版の5章までを読み進めている人を対象としています。
    • 完全ガイドを読んでいなくとも、Deploymentリソースがわかっていれば基本的に問題はありません。

※ 本記事は、KubernetesのService カテゴリーの挙動を確認する記事になります。なので、Serviceカテゴリについては説明を書きますが、他のリソースについては説明しません。

環境

  • 検証機
    • Mac Book pro 14inch M1 Pro
  • 検証環境
    • kind(Kubernetes in Docker)

サービスカテゴリとは?

Podの集合で実行されているアプリケーションをネットワークサービスとして公開する抽象的な方法です。
Kubernetesでは、なじみのないサービスディスカバリーのメカニズムを使用するためにユーザーがアプリケーションの修正をする必要はありません。 KubernetesはPodにそれぞれのIPアドレス割り振りや、Podのセットに対する単一のDNS名を提供したり、それらのPodのセットに対する負荷分散が可能です。
ref: Service | Kubernetes

今回ハンズオンで扱うもの

今回は、以下の2つのサービスカテゴリをハンズオンで扱います。

  • ClusterIP
  • NodePort

今回のKubernetes環境の構成図と環境の構築方法

cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
terminal
➜ kind create cluster --config cluster.yaml -n serivce-test

受け元のKubernetesマニフェストファイル

ディレクトリ構造
├── employer
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── namespace.yaml
namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: employer
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: employer-deployment
  namespace: employer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: employer-app
  template:
    metadata:
      labels:
        app: employer-app
    spec:
      containers:
      - name: employer-deployment
        image: nginx
        volumeMounts:
          - {name: curl-sh-cluster, mountPath: /curl-cluster.sh ,subPath: curl-cluster.sh}
          - {name: curl-sh-node, mountPath: /curl-node.sh ,subPath: curl-node.sh}
      volumes:
        - {name: curl-sh-cluster, configMap: {name: curl-config, items: [{key: curl-bash-cluster, path: curl-cluster.sh}]}}
        - {name: curl-sh-node, configMap: {name: curl-config, items: [{key: curl-bash-node, path: curl-node.sh}]}}

configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: curl-config
  namespace: employer
data:
  curl-bash-cluster: |
    #!/bin/sh
    for ((i=0; i < 10; i++)); do
      curl -w "\n" http://cluster-service.clusterip.svc.cluster.local:5000
    done
  curl-bash-node: |
    #!/bin/sh
    for((i=0; i < 10; i++)); do
      curl -w "\n" http://serivce-test-worker:30000
    done

kustomization.yaml
resources:
  - deployment.yaml
  - namespace.yaml
  - configmap.yaml

受け元のKubernetesリソースのconfigmapでスクリプトを2つ作っていますが、これは今後ClusterIPとNodePortの動作確認をする際に使います。
curl-cluster.shcurl-node.shというスクリプトを作成していますが、スクリプトの具体的な処理の内容は、特定のエンドポイントにcurlを10回叩くという処理になります。

導入

➜ kubectl apply -k employer/

今回検証で使うアプリケーションのサンプルコード

アプリケーションのディレクトリ構造
├── app
│   ├── Dockerfile
│   ├── app.py
│   └── requirements.txt
app.py
from flask import Flask
import socket

app = Flask(__name__)

@app.route("/")
def index_page():
    return socket.gethostbyname(socket.gethostname())

if __name__ == "__main__":
    app.run(host="0.0.0.0")
requirements.txt
click==8.1.3
Flask==2.2.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.2.2
Dockerfile
FROM python:3.10
WORKDIR /
COPY app.py requirements.txt ./
RUN pip install -r requirements.txt
CMD ["python","app.py"]

今回は検証用のアプリケーションをFlask(Pythonのウェブアプリケーションフレームワーク)で作成しました。
アプリケーションの処理は、クライアントがアプリケーションにアクセスするとサーバーのIPをレスポンスするようになっています。

コンテナをkindに登録する

➜ kind load docker-image test-python:latest -n serivce-test

Kubernetes Service カテゴリーのハンズオンをやってみる

ClusterIPのハンズオン

ClusterIPとは?

ClusterIPとは?

クラスター内部のIPでServiceを公開する。このタイプではServiceはクラスター内部からのみ疎通性があります。
ref: Service ClusterIP

  • 一般的なClusterIPのイメージ(App Aにする場合)
    monorepo-EKS-testing - 2ページ.png

  • ClusterIPのFQDN

    • [service name].[namespace].svc.cluster.local:[port]

本記事で行うハンズオンでは、以下2つのパターンを確認したいと思います。

  1. パラメータを設定しない場合(default)
  2. sessionAffinityを有効にした場合

sessionAffinityパラメータをClusterIPで検証する理由
基本的にKubernetesクラスターでPodにアクセスする際、ClusterIPを経由してPodにアクセスします。なので、sessionAffinityパラメータはClusterIPで検証するのが良いと考え検証しています。

manifestの作成

本manifestは、ClusterIP検証namespace clusteripに作成します。
※ディレクトリ構造に関しては以下に示します。

ディレクトリ構造
├── cluster_ip
│   ├── service.yaml
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── namespace.yaml
1 . パラメータを設定しない場合(default)
namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: clusterip
kustomization.yaml
resources:
  - deployment.yaml
  - namespace.yaml
  - service.yaml
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-deployment
  namespace: clusterip
spec:
  replicas: 2
  selector:
    matchLabels:
      app: cluster-app
  template:
    metadata:
      labels:
        app: cluster-app
    spec:
      containers:
      - name: cluster-deployment
        image: test-python
        imagePullPolicy: IfNotPresent
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: cluster-service
  namespace: clusterip
spec:
  type: ClusterIP
  ports:
  - name: cluster-service
    protocol: "TCP"
    port: 5000
    targetPort: 5000
  selector:
    app: cluster-app
2 . sessionAffinityを有効にした場合

今回は、ClusterIPのパラメーターsessionAffinityを入れてみようと思います。
1 . パラメータを設定した場合のmanifestをベースにservice.yamlにだけ修正を入れます。

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: cluster-clusterip
  namespace: clusterip
spec:
  type: ClusterIP
  ports:
  - name: cluster-service
    protocol: "TCP"
    port: 5000
    targetPort: 5000
  selector:
    app: cluster-app
+  sessionAffinity: ClientIP
+  sessionAffinityConfig:
+    clientIP:
+      timeoutSeconds: 1

sessionAffinityとは?
sessionAffinityとは、timeoutSecondsで指定した秒数間同じPodsにリクエストを流すことができます。
ref:IPVSプロキシーモード

動作の確認

1 . パラメータを設定しない場合

動作確認は動作確認用に作ったPodからパラメータを設定しない場合のパターンで作ったClusterIPにアクセスをしてみます。
今回は事前にスクリプトを作成しているのでそちらを使ってアクセスしてみます。

パラメータを設定しない場合のパターンで作ったmanifestをapplyし、動作を確認します。

パラメータを設定しない場合のパターンで作ったmanifestをapply
➜ kubectl apply -k cluster_ip/
ClusterIPの検証
❯ kubectl get pods -n clusterip -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP           NODE                   NOMINATED NODE   READINESS GATES
cluster-deployment-6785dbc954-2wf64   1/1     Running   0          13h   10.244.2.5   serivce-test-worker2   <none>           <none>
cluster-deployment-6785dbc954-wxfn7   1/1     Running   0          13h   10.244.1.5   serivce-test-worker    <none>           <none>

➜ kubectl get pods -n employer
NAME                                   READY   STATUS    RESTARTS   AGE
employer-deployment-54bf65f757-rtmrn   1/1     Running   0          13h

# employer-deployment-54bf65f757-rtmrn のpodsにアクセスしてスクリプトを叩きます。
➜ kubectl exec -it employer-deployment-54bf65f757-rtmrn -n employer bash
root@employer-deployment-54bf65f757-rtmrn:/# bash curl-cluster.sh
10.244.2.5
10.244.1.5
10.244.2.5
10.244.2.5
10.244.1.5
10.244.1.5
10.244.1.5
10.244.1.5
10.244.2.5
10.244.1.5

実際に、ClusterIPのエンドポイントを叩くと各nodeにpodsに分散されてアクセスがされていることがわかります。

2 . sessionAffinityを有効にした場合

動作確認は動作確認用に作ったPodからsessionAffinityを有効にした場合のパターンで作ったClusterIPにアクセスをしてみます。
今回も同様事前にbashを作成しているのでそちらを使ってアクセスしてみます。

sessionAffinityを有効にした場合のパターンで作ったmanifestをapplyし、動作を確認します。

sessionAffinityを有効した場合のパターンで作ったmanifestをapply
➜ kubectl apply -k cluster_ip/
terminal
❯ kubectl get pods -n clusterip -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP           NODE                   NOMINATED NODE   READINESS GATES
cluster-deployment-6785dbc954-2wf64   1/1     Running   0          13h   10.244.2.5   serivce-test-worker2   <none>           <none>
cluster-deployment-6785dbc954-wxfn7   1/1     Running   0          13h   10.244.1.5   serivce-test-worker    <none>           <none>

➜ kubectl get pods -n employer
NAME                                   READY   STATUS    RESTARTS   AGE
employer-deployment-54bf65f757-rtmrn   1/1     Running   0          13h

# employer-deployment-76d7784fc7-nv8gg のpodsにアクセスしてスクリプトを叩きます。
➜ kubectl exec -it employer-deployment-54bf65f757-rtmrn -n employer bash
root@employer-deployment-54bf65f757-rtmrn:/# bash curl-cluster.sh
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5
10.244.2.5

sessionAffinity: ClientIPを有効にするとsessionAffinityで設定した秒数間(timeoutSeconds)同じPodに振り分けられていることがわかります。

sessionAffinityが有効なパターン
sessionAffinityを有効にすると、timeoutSecondsに設定した時間の間同じPodにアクセスが流れます。
セッションを意識したい通信を設計したい場合には有効です。

NodePortのハンズオン

NodePortとは?

Nodeportとは?
各NodeのIPにて、静的なポート(NodePort)上でServiceを公開します。そのNodePort のServiceが転送する先のClusterIP Serviceが自動的に作成されます。NodeIP:NodePortにアクセスすることによってNodePort Serviceにアクセスできるようになります。
ref: Service NodePort

  • 一般的なNodePortのイメージ
    monorepo-EKS-testing - 2ページ (4).png

  • NodePortのFQDN

    • [node-name] : [port]
  • ※このFQDNはKubernetes内部からからNodeにアクセスしたい場合に使うエンドポイントになります。

    • 外部からアクセスしたい場合には、[node ip] : [port]になります。

前章でClusterIP、本章でNodePortをまとめているので一応ここでざっくりとClusterIPとNodePortの違いをまとめたいと思います。

ClusterIPとNodePortの違い

  • ClusterIPはKubernetes内部からアクセスされるServiceカテゴリ
  • NodePortはKubernetes外部からアクセスされるServiceカテゴリ

manifestの作成

本manifestは、NodeIP検証namespace nodeportに作成します。

ディレクトリ構造
├── node_port
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   ├── namespace.yaml
│   └── service.yaml
namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: nodeport
kustomization.yaml
resources:
  - deployment.yaml
  - namespace.yaml
  - service.yaml
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodeport-deployment
  namespace: nodeport
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodeport-app
  template:
    metadata:
      labels:
        app: nodeport-app
    spec:
      containers:
      - name: recipient-deployment
        image: test-python
        imagePullPolicy: IfNotPresent
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
  namespace: nodeport
spec:
  type: NodePort
  ports:
  - name: nodeport-service
    protocol: "TCP"
    port: 5000
    targetPort: 5000
    nodePort: 30000
  selector:
    app: nodeport-app

動作の確認

terminal
➜ kubectl get pods -n nodeport -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP           NODE                   NOMINATED NODE   READINESS GATES
nodeport-deployment-6879847b7d-4fcwm   1/1     Running   0          13h   10.244.1.3   serivce-test-worker    <none>           <none>
nodeport-deployment-6879847b7d-k4pc9   1/1     Running   0          13h   10.244.2.2   serivce-test-worker2   <none>           <none>

➜ kubectl get pods -n employer
NAME                                   READY   STATUS    RESTARTS   AGE
employer-deployment-54bf65f757-rtmrn   1/1     Running   0          13h

# employer-deployment-54bf65f757-rtmrn のpodsにアクセスしてスクリプトを叩きます。
➜ kubectl exec -it employer-deployment-54bf65f757-rtmrn -n employer bash
root@employer-deployment-54bf65f757-rtmrn:/# bash curl-node.sh
10.244.2.2
10.244.1.3
10.244.2.2
10.244.2.2
10.244.2.2
10.244.2.2
10.244.2.2
10.244.1.3
10.244.2.2
10.244.2.2

nodeに向けてcurlを叩くとNodeIPを介してclusterにアクセスしCluster IPを通じて各NodeのPodsにアクセスが流れていることがわかります。

まとめ

今回は、Kubernetes Serviceカテゴリのハンズオンで行った内容をまとめてみました。
やはり手を動かして動作を確認してみると理解が深まるなという気がしました。
本記事を読むことでServiceカテゴリを学習されている皆さんの解像度が少しでも上がると嬉しいです。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3