はじめに
皆さんこんにちは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環境の構成図と環境の構築方法
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
➜ kind create cluster --config cluster.yaml -n serivce-test
受け元のKubernetesマニフェストファイル
├── employer
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ └── namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
name: employer
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}]}}
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
resources:
- deployment.yaml
- namespace.yaml
- configmap.yaml
受け元のKubernetesリソースのconfigmapでスクリプトを2つ作っていますが、これは今後ClusterIPとNodePortの動作確認をする際に使います。
curl-cluster.sh
とcurl-node.sh
というスクリプトを作成していますが、スクリプトの具体的な処理の内容は、特定のエンドポイントにcurlを10回叩くという処理になります。
導入
➜ kubectl apply -k employer/
今回検証で使うアプリケーションのサンプルコード
├── app
│ ├── Dockerfile
│ ├── app.py
│ └── requirements.txt
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")
click==8.1.3
Flask==2.2.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.2.2
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
本記事で行うハンズオンでは、以下2つのパターンを確認したいと思います。
- パラメータを設定しない場合(default)
- 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)
kind: Namespace
apiVersion: v1
metadata:
name: clusterip
resources:
- deployment.yaml
- namespace.yaml
- service.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
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にだけ修正を入れます。
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し、動作を確認します。
➜ kubectl apply -k cluster_ip/
❯ 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し、動作を確認します。
➜ kubectl apply -k cluster_ip/
❯ 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
前章で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
kind: Namespace
apiVersion: v1
metadata:
name: nodeport
resources:
- deployment.yaml
- namespace.yaml
- service.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
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
動作の確認
➜ 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カテゴリを学習されている皆さんの解像度が少しでも上がると嬉しいです。