kubernetesの基礎を学ぼう!~第2章 Service API~
こんにちは!ヒロケイと申します!
ここ最近kubernetesに興味を持ち始め、基礎知識を蓄えてきました。
そこで、本シリーズからkubernetesの概要について、Dockerをほんのちょっと触ったことがある初学者でも分かるように解説していきます。
本シリーズでは特に、Kubernetesが提供しているAPIリソースでできることを網羅していきます。
以下のような読者を想定しています。
想定する読者
- コンテナ技術については知っているが、複数のコンテナを管理、運用する方法を探している方
- Kubernetesについて勉強したいけど、何から始めれば良いかわからない方
- 公式ドキュメントや技術書を読むのが面倒くさくて、全体像を掴みたい方
Dockerを使って開発環境を整えたりした経験があれば、難なく読み進められるかと思います。
このシリーズでKubernetesでできることについて大枠を理解していただき、公式ドキュメントや技術書、勉強会での理解のサポートができればとても嬉しく思います!
第1章~第4章まで理解すると得られるもの
- Kubernetesを使うとできることの半分くらいが理解できる
- 技術イベントや勉強会で出てくる言葉の意味を理解でき、内容が大体理解できるようになる。
- APIリソースの全体像を掴むことができる
扱わないテーマ
- kubernetes環境を構築する方法
- コンテナオーケストレーションを実現する上でのテクニック
- kubernetesが提供する詳細な機能
こちらはKubernetesの基礎を学ぼう!の第2章です。
前回の記事はこちらからご覧ください。
Service API
まずは、Service APIという塊が何を意味するのかを説明します。
Service APIの役割としては、
- クラスタ上のコンテナに対するエンドポイントの提供
- ラベルに一致するコンテナの発見
を担っています。
Kubernetesクラスタには、複数のコンテナが起動しています。
そんな中、特定のコンテナにリクエストを送信したいと思う時があるでしょう。
そこでService APIを使うことで、リクエストの送信先を固定させたり、通信範囲(クラスタ外からの通信か、クラスタ内からなのか)を細かく設定することでクラスタ内のネットワーク構成を整備できます。
Service APIがないとどんなことになる?
では、Sevice APIがないとどんなことになるのかについて考えてみましょう。
前提として、一つのPodには一つのIPアドレスが割り当てられます。
同一Podのコンテナにはlocalhost宛に通信し、別Podへの通信にはPodのIpアドレス宛に通信を行います。
起動するPodのIPアドレスはそれぞれ異なるため、リクエストを均等に分けて負荷分散しようとすると、リクエストが飛んでくるたびに毎回PodのIPアドレスを調べたり、転送先の宛先を設定する必要があります。
そこでService APIを使うと、受信したトラフィックを複数のPod に自動的に負荷分散できます。
トラフィックとは?
trafficの直訳は、「交通」を意味する。
IT分野でのトラフィックは大きく分かれて2つに分かれている。
- Web トラフィック
- Network トラフィック
Web トラフィック
ウェブサイトやオンラインプラットフォームへのアクセスや閲覧の量を指す。
ウェブトラフィックは、特定のサイトが人気があるかどうかを示す指標として使用される。
Network トラフィック
データ通信ネットワークを介して送受信されるデータパケットの量や流れを指す。
ネットワークトラフィックは、ネットワークのパフォーマンスを監視し、最適化するために分析される。
Kubernetesやコンテナオーケストレーションにおけるトラフィックは、主にNetworkトラフィックを指す。
PodからPodにリクエストを送信した時のデータの流れがこれに当たる。
めちゃめちゃ便利ですね!
Service API 構成要素
Service APIが抱えるリソースは、以下の通りです。
理解する必要はありません!こんなものがあるんだなぁと思いながら眺めてみてください。
リソース名 | 機能 |
---|---|
ClusterIP | クラスタ内でアクセスできるIPアドレスを提供 |
NodePort | クラスタ外からアクセスできるエンドポイントを提供 |
LoadBalancer | NodePortで作成したエンドポイントのリクエストを負荷分散 |
Headless | クラスタ内から特定のPodにアクセスするためのエンドポイントを提供 |
ExternalName | 外部からの通信を可能にするための短縮されたエンドポイントを提供 |
None-Selector | クラスタ内から任意のIPアドレスにアクセスできるエンドポイントを提供 |
Ingress | ルーティングによって外部からのアクセスを制御 |
これらのAPIリソースで何が実現できるのかを知ることで、Kubernetesクラスタを構築する基礎力は間違いなく向上するでしょう!
それぞれどんなことができるリソースなのか、順番に見ていきましょう。
ClusterIP
最初に登場するServiceAPIリソースは、ClusterIPです。
ClusterIPの役割としては、クラスター内部からのみアクセスできるIPアドレスをPodに提供することです。
ユーザーから直接アクセスする必要がないPodに関しては、基本ClusterIPが設定されています。
具体的なPodの例としては、
- Postgresなどのデータベースサーバー
- 一時的に情報を保持し、サーバーのパフォーマンスを向上させるキャッシュサービス
- マイクロサービスアーキテクチャにおけるサービス
などが挙げられます。
複数のPodを運用しているケースはどうなるの?
PodにIPアドレスを提供できるのがClusterIPですが、ReplicaSetやDeploymentなどで複数のPodを起動している場合はどうなるのでしょうか?
ReplicaSet, Deploymentとは?
複数Podを運用しているケースについて具体的なイメージが湧かない場合は、Podに関するWorkload APIに関する記事をご覧ください!
Kubernetesに関する前提知識がなくても読めますよ〜
答えは、複数のPodを束ねて一つのエンドポイントを提供してくれるようになります。
では、Podのエンドポイントを束ねた場合、どのPodへリクエストを送信するのでしょうか?
基本的には、自動的に均等にリクエストを送信するようにしてくれます。
ラウンドロビン方式というキーワードがこれに当たります。
作ってみる
では、実際にClusterIPのマニフェストを作り、エンドポイントを確認してみましょう!
// ClusterIPを作るためのPodを定義
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginx:latest
ports:
- containerPort: 80
---
// ClusterIPを定義
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
$ kubectl get po
NAME READY STATUS RESTARTS AGE
myapp-deployment-774767dd4f-2v8lb 1/1 Running 0 8s
myapp-deployment-774767dd4f-7577k 1/1 Running 0 8s
myapp-deployment-774767dd4f-nsx9b 1/1 Running 0 8s
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service ClusterIP 10.96.109.255 <none> 80/TCP 86s
これで、ClusterIPを作ることができました!
次に、ClusterIPによってIPアドレスが提供されていることを確認してみましょう!
提供されたIPアドレスを確認
$ SERVICE_IP=$(kubectl get svc myapp-service -o=jsonpath='{.spec.clusterIP}')
echo "Service IP: $SERVICE_IP"
Service IP: 10.96.109.255
Podにアクセスし、リクエストを送ってみる
$ kubectl run -i --tty --rm debug --image=alpine -- sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- http://$SERVICE_IP
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
これで、クラスタ内通信におけるIPアドレスを提供できました。
では、クラスタ外部通信におけるIPアドレスを提供するには、どうすれば良いのでしょうか?
次のセクションで見ていきましょう。
NodePort
クラスタ外からの通信を可能にする機能を提供をしてくれるのは、NodePortというリソースです。
NodePortとは名前の通り、クラスタ上で稼働しているNode上で指定したポートを公開してくれるリソースです。
クラスタ外部からノードのIPアドレスと指定したポートを使用することでアクセスできるようになります。
また、NodePortはClusterIPの機能を備えています。
NodePortが作られたら、同時にClusterIPも作られるのです!
作ってみる
apiVersion: v1
kind: Service
metadata:
name: myapp-nodeport-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 9376
type: NodePort
$ kubectl apply -f ./nodeport.yaml
service/myapp-nodeport-service created
$ kubectl get service
myapp-nodeport-service NodePort 10.96.193.184 <none> 80:31727/TCP 2m1s
ここでいう、80:31727/TCPの80が公開されているPortです。
LoadBalancer
次に紹介するのが、LoadBalancerです。
役割はNodePortと似ていて、クラスタ外部から通信可能なエンドポイントを提供してくれるのが機能です。
機能としては名前から想像できるように、外部からの通信を複数の場所に分散してくれる機能を持ちます。
NodePortとの違い
NodePortで外部通信ができるし、LoadBalancerも外部通信ができる。
なら、両者の違いはなんなのでしょうか?
それは、ロードバランシング機能です。
kubernetesクラスタは、内部にNodeがあり、その中にPodが格納されています。
特に、クラスタの中に複数のNodeが格納されている場合を考えてみましょう。(マルチノードクラスタ)
NodePortでサービスを外部公開すると、NodeのIPアドレスとPortを使うことでPodにアクセスできます。
なので、3つのNodeを含んだクラスタ上でNodePortを作成すると、エンドポイントが3つ作成されることになります。
もし、一つのNodeにアクセスしている場合、そのNodeが障害を起こした場合はクラスタにアクセスできなくなってしまいます。
またマルチノードクラスタの場合は、アクセスされていないNodeが発生してしまいますよね?
そこで、一部のNodeが稼働できなくなってもクラスタにアクセスできるよう、リクエストを分散することで解決できます。
ここで登場するのがLoadBalancerです。
外部疎通性のあるエンドポイントを一つ作り、公開されているNodePortを束ねることで、それぞれのNodeに均等にリクエストが飛んでくるようにすることができます。
NodePortにリクエストを捌く機能を追加したものがLoadBalancerであるので、LoadBalancerを作成したときには自動的にNodePort, ClusterIPも作成されるようになります。
作ってみる
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
$ kubectl apply -f ./loadbalancer.yaml
service/myapp-loadbalancer-service created
$ kubectl get service
myapp-loadbalancer-service LoadBalancer 10.96.233.230 <pending> 80:32640/TCP 24s
$ kubectl get endpoints my-headless-service
NAME ENDPOINTS AGE
my-headless-service 10.244.0.93:9376,10.244.0.94:9376,10.244.0.95:9376 5m20s
Headless Service
// スナイパーのように、特定のPodにクロスヘアが当たっている様子をイラスト
今までは、複数のPodを外部公開するために必要なリソースを取り扱ってきました。
しかし、Service APIの機能はそれだけではありません。
ここからは、Pod間の通信を行うために便利な機能についてご紹介します。
まずは、Headless Serviceです。
Headless Serviceは、Podに直接アクセスするためのエンドポイントを提供してくれるものです。
ClusterIPが提供してくれるエンドポイントは仮想のIPアドレスなのに対し、Headless Serviceは固有のDNSエントリを提供してくれます。
Headless Serviceのメリットは何なのでしょうか?
それは、特定のPodと直接通信することが可能になる点です。
StatefulSetなどの情報を保持しているPodにアクセスしたいとき、Headless Serviceを用意することで、特定のStatefulSetにアクセスでき、情報の永続性を確保できます。
なぜなら、エンドポイントが常に同じなので、通信先のStatefulSetを固定できるからです。
作ってみる
実際にHeadless Serviceを作り、エンドポイントが提供できていることを確認してみましょう!
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
$ kubectl get services my-headless-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-headless-service ClusterIP None <none> 80/TCP 44s
ExternalName
ExternalNameは、Cluster内のPodに対して、外部からの通信を可能にするための短縮されたエンドポイントを提供するService APIのリソースです。
外部のサービスやホストに対してDNSエイリアスを提供し、Kubernetesクラスタ内からその外部サービスにアクセスできるようになります。
ExternalNameを使用することで、Kubernetes内のPodは外部のサービスを Cluster 内のリソースと同様にアクセスできます。
これは、特定のサービスに対してクラスタ外からアクセスする必要がある場合に便利です。
作ってみる
具体的なExternalNameの使用例を見てみましょう。
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: example.com
ports:
- protocol: TCP
port: 80
上記の例では、my-external-serviceというExternalNameサービスが作成され、example.comという外部ホストに対してTCPポート80でアクセスできるようになります。
None-Selector
None-Selectorは、特定のラベルセレクターがない(selectorがselector: {}となっている)Service APIのリソースです。通常のServiceリソースは、ラベルセレクターを指定して特定のPodを対象にしますが、None-SelectorはどのPodにも関連付けられず、クラスタ内の任意のIPアドレスにアクセスすることができます。
None-Selectorを使用すると、クラスタ内のあらゆるIPアドレスにアクセスする単一の仮想IPアドレスが提供されます。これは、特定のPodに直接アクセスする必要がなく、クラスタ内の任意の場所に通信を行いたい場合に便利です。
作ってみる
None-Selectorの使用例を見てみましょう。
apiVersion: v1
kind: Service
metadata:
name: my-none-selector-service
spec:
selector: {}
ports:
- protocol: TCP
port: 8080
targetPort: 80
上記の例では、my-none-selector-serviceというNone-Selectorサービスが作成され、クラスタ内の任意の場所にTCPポート8080でアクセスできるようになります。
Ingress
Ingressは、クラスタ内のサービスに対する外部からのアクセスを制御するためのリソースです。Ingressを使用すると、HTTPおよびHTTPSトラフィックをクラスタ内のサービスにルーティングできます。通常、Ingressリソースを作成すると、クラスタ内にIngressコントローラがデプロイされ、外部からのトラフィックを適切なサービスに転送します。
Ingressは、複数のパスやホストベースのルーティング、SSL証明書の終端など、高度なトラフィック制御を提供します。
作ってみる
具体的なIngressの使用例を見てみましょう。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /app
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
上記の例では、my-ingressというIngressリソースが作成され、myapp.example.comのホストに対して/appへのパスがルーティングされます。このリクエストは、クラスタ内のmyapp-serviceサービスに転送されます。
まとめ
今回は、クラスタ内外でのPodの通信を細かく設定できる、Service APIについて紹介しました!
かなりボリューミーでしたが、これらの機能を理解できれば、実務でKubernetesを扱うための基礎力はかなりついてきるはずです!
お疲れ様でした!
いかがでしたでしょうか?
次回は、Config, Storage APIです。
クラスタを運用する上でとても重要になってくるリソースです!ぜひ読んでみてくださいね(^^)
kubernetesの基礎を学ぼう!~第3章 Config, Storate API~
Discussion