はじめに
こんにちは!
本記事は「本気で学ぶKubernetes」シリーズの第11回です。このシリーズでは、Kubernetesの基礎から実践まで、段階的に学んでいきます。
このシリーズは、第1回から順に読むことで体系的に学べる構成にしています。
まだご覧になっていない方は、ぜひ最初からご覧ください!
今回は、Ingressについて学んでいきたいと思います。
以前の記事でServiceのタイプ(ClusterIP / NodePort / LoadBalancer)について触れましたが、今回はさらにその上のレイヤーであるIngressを使って、より高度にルーティングを行っていく方法を見ていこうと思います。
【本気で学ぶKubernetes】もう迷わない!Service タイプの正しい使い分け
この記事は人間がKubernetesの公式ドキュメントを読み漁りながら、人間の手で書いていますのでご安心ください!
ServiceとIngressの関係について
以前までの記事で利用していたServiceの機能はL4のトランスポート層でトラフィックがきた場合にルーティングを行うというものでしが。つまりはIPアドレスとポート番号を使ってPodにトラフィックを振り分けていました。
今回触れるIngressはより上位のレイヤーであるL7のアプリケーション層で動作するものとなっており、HTTPのパスやホスト名を参照して、それを異なるServiceに振り分けることができる機能となっています。
LoadBalancerの課題について
ServiceにはLoadBalancerのタイプがあります。
例えば、以下のような構成を考えて課題を整理してみます。
- フロントエンドアプリ(
frontend-service) - APIサーバー(
api-service) - 管理画面(
admin-service)
ServiceごとにLoadBalancerを作成する
この場合、Service1つにつき1つのLoadBalancerが必要なため、例えばフロントエンド、APIサーバー、管理画面等で分ける公開したい場合は、Kubernetesにおいて3つのLoadBalancerを作らなければならないということになります。
パスベースルーティングができない
同じドメインで動作させる場合にURLのパスによって異なるServiceに振り分けたい場合もあります。
-
example.com/→ フロントエンド -
example.com/api→ APIサーバー -
example.com/admin→ 管理画面
LoadBalancerだけだと、これを実現するのはなかなか難しいです。
ホストベースルーティングもできない
また同じドメインでなくとも、複数のサブドメインを使ってそれぞれ異なるServiceに振り分けたい場合も同様です。
-
app1.example.com→ App1 -
app2.example.com→ App2
Ingressとは
IngressはKubernetesのリソースの1つで、HTTP/HTTPSレベルでのルーティングを行うことができます。
冒頭でもお伝えした通り、ServiceのLoadBalancerはL4のトランスポート層でIPアドレスとポート番号で振り分けを行いますが、IngressはHTTPのパスやホスト名を見て振り分けることができるL7のアプリケーション層のロードバランサーです。
Ingressを使うことにより、1つのIPアドレス(または1つのLoadBalancer)で複数のServiceにルーティングできるようになります。
出典: Kubernetes公式ドキュメント - Ingress
Ingressを使っていく上で、2つの概念を押さえておく必要があります。
Ingress Controller
実際にHTTPリクエストを受け取り、ルーティングを行うロードバランサーの実態のようなものです。
主なIngress Controllerには以下のようなものがあり、自動的にインストールされるわけではないので選択して自分で有効化する必要があります。
- nginx-ingress-controller: 最も広く使われている
- Traefik: 動的な設定変更に強い
- HAProxy: 高性能
- GCE Ingress Controller: GKE専用
- AWS Load Balancer Controller: EKS専用
Ingress Resource
ルーティングのルールを定義するマニフェストです。
YAML形式で「どのパスをどのServiceに振り分けるか」といったロードバランサーの設定を行います。
Ingress ControllerがIngress Resourceの設定を読み込んで、実際のルーティングを行っていくというわけです。
出典: Kubernetes公式ドキュメント - Ingress Controllers
実際にIngressを使ってみる
今回の構成では2つのアプリケーションを用意します。
それぞれDeploymentとServiceを作成し、Ingressで振り分けるように構成していきます。
-
app1:
/app1にアクセスすると「Hello from App1」を返す -
app2:
/app2にアクセすると「Hello from App2」を返す
Ingress Controllerの有効化
minikubeでは、nginx-ingress-controllerがaddonとして提供されていますので、以下のコマンドを実行してIngress Controllerを有効化します。
minikube addons enable ingress
# ...省略...
# 🌟 'ingress' アドオンが有効です
以下のコマンドで確認ができます。
kubectl get pods -n ingress-nginx
# NAME READY STATUS RESTARTS AGE
# ingress-nginx-admission-create-xxxxx 0/1 Completed 0 2m
# ingress-nginx-admission-patch-xxxxx 0/1 Completed 1 2m
# ingress-nginx-controller-xxxxx 1/1 Running 0 2m
ingress-nginx-controllerというPodがRunningになっていれば問題ありません。
ちなみにadmission-createとadmission-patchは初期セットアップ用のJobのようで、役目が終わったためCompletedでReady 0/1となっているようです。
app1のマニフェスト
ここからはnginxが動作する2つのアプリケーションのマニフェストを作成していきます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1
spec:
replicas: 2
selector:
matchLabels:
app: app1
template:
metadata:
labels:
app: app1
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app1-service
spec:
selector:
app: app1
ports:
- protocol: TCP
port: 80
targetPort: 80
2つ目のアプリケーションのマニフェストです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2
spec:
replicas: 2
selector:
matchLabels:
app: app2
template:
metadata:
labels:
app: app2
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app2-service
spec:
selector:
app: app2
ports:
- protocol: TCP
port: 80
targetPort: 80
nginxのデフォルトページを返すシンプルな構成としていて、DeploymentでPodを2つ起動されServiceでClusterIPを割り当て流ようになっています。
Ingressマニフェスト
次に、Ingressマニフェストを作成していきます。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /app1
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- path: /app2
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
簡単に解説すると
annotationsとは
nginx.ingress.kubernetes.io/rewrite-target: /という値を設定していますが、これは例えばブラウザでhttp://example.com/app1/index.htmlにアクセスしたとき、
Ingress Controllerはapp1-serviceに転送しまう都合/app1/index.htmlというパスになってしまうため /index.htmlというパスを書き換えてから転送しています。
rulesとは
ルーティングのルールです。
-
path: /app1→app1-serviceに転送します -
path: /app2→app2-serviceに転送します
マニフェスト自体はここまで挙動確認しながら進めている方にとってはそこまで複雑なものではないと思います。
デプロイと動作確認
それでは、マニフェストをデプロイしてみます。
kubectl apply -f app1.yaml
kubectl apply -f app2.yaml
kubectl apply -f ingress.yaml
# deployment.apps/app1 created
# service/app1-service created
# deployment.apps/app2 created
# service/app2-service created
# ingress.networking.k8s.io/example-ingress created
Ingressの状態を確認してみます。
kubectl get ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# example-ingress nginx * 192.168.49.2 80 30s
minikube ip
# 192.168.49.2
ADDRESSにIPアドレスが表示されていれば正常にリソースが作成できています。
ちなみにこのIPアドレスは、minikubeのIPアドレスと同じになっているはずですので確認してみてください!
それぞれのPodにindex.htmlファイルを作成していきます。
# app1のPodに「Hello from App1」を書き込む
kubectl exec -it $(kubectl get pods -l app=app1 -o jsonpath='{.items[0].metadata.name}') -- bash -c "echo 'Hello from App1' > /usr/share/nginx/html/index.html"
# app2のPodに「Hello from App2」を書き込む
kubectl exec -it $(kubectl get pods -l app=app2 -o jsonpath='{.items[0].metadata.name}') -- bash -c "echo 'Hello from App2' > /usr/share/nginx/html/index.html"
curlでアクセスして確認してみます。
curl http://192.168.49.2/app1
# Hello from App1
curl http://192.168.49.2/app2
# Hello from App2
パスによって異なるServiceに振り分けられていることが確認できました!
環境によっては、上記のcurlコマンドで応答が返ってこない場合があります。
生成AIでトラブルシューティングをしたところ、minikubeでDockerドライバーを使用している場合はminikube IPには外部から直接アクセスできないことがあるようです。
その場合は、別のターミナルでminikube tunnelを実行してトンネルを作成すると、curl http://localhost/app1のようにlocalhostでアクセスできるようになるとのことです。
ホストベースルーティング
Ingressでは、パスだけでなくホスト名(ドメイン名)でも振り分けができます。
例えば、以下のようなルールを定義できます。
spec:
rules:
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- host: app2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
この設定を適用するとapp1.example.comへのリクエストはapp1-serviceに、app2.example.comへのリクエストはapp2-serviceに振り分けられます。
そのため、ホスト名をベースにルーティングを行うことができます。
# http://app1.example.com
# http://app2.example.com
実際の本番環境では、DNSレコードを設定してホスト(ドメイン)ベースでルーティングを行っていくことがほとんどだと思います。
またHTTPSを使って外部公開していくケースが多いと思いますので、今後GKEなどにデプロイした後に実践編として試していければと思います。
Ingressは当然HTTPSにも対応しています。HTTPSを有効にするには、証明書と秘密鍵をSecretに格納してIngressに参照させてからSSL終端の設定を行います。
クリーンアップ
今回作成したリソースを削除しておきます。
kubectl delete -f ingress.yaml
kubectl delete -f app1.yaml
kubectl delete -f app2.yaml
# ingress.networking.k8s.io "example-ingress" deleted
# deployment.apps "app1" deleted
# service "app1-service" deleted
# deployment.apps "app2" deleted
# service "app2-service" deleted
Ingress Controllerは今後も使うかもしれないのでそのまま残しておきますが、もし無効化したい場合は以下のコマンドで無効化できます。
minikube addons disable ingress
まとめと次回予告
LoadBalancer TypeのServiceだけだと、Service1つにつき1つのロードバランサーが必要になりますが、Ingressでは1つのIPアドレスやLoadBalancerから複数のServiceにルーティングできることがわかりました。
Ingressは単体で使うものではなくServiceを組み合わせます。さらにHTTPS化することで本番環境でも利用可能なレベルでアクセスをより柔軟に管理できるようになります。
次回は、KubernetesのCPUやメモリのリソース指定が可能なResource Limits/Requestsについて触れていきたいと思います。
それでは、また次回!