Gateway APIの開発が進んできて、2021年5月12日 現在アルファであるGateway APIを見ていきたいと思います。
- https://gateway-api.sigs.k8s.io/
- https://kubernetes.io/blog/2021/04/22/evolving-kubernetes-networking-with-the-gateway-api/
- https://projectcontour.io/guides/gateway-api/
本記事は、上記ページを参考にして作成しております。また、Contourを使って試しています。
TOC
背景
KubernetesにはIngressと呼ばれるリソースがあり、多くの方がIngressを利用してアプリケーションを外部に公開してきました。約5年経過し、コアのAPIに加えて、ベンダー固有の機能をアノテーションで使用していたり、Ingress自体では実現できないために、独自のCRDが作成されてきました。これは、独自のアノテーションやCRDを使うということになり、別のIngress Controllerにした場合には、リソースのマニフェストを変更する必要が出てきます。 こういったポータビリティを維持するために、Gateway APIの開発が進められてきました。
Gateway APIのコンセプト
下記4点がGateway APIのコンセプトになります。
- Role-oriented
ユーザのペルソナに合わせたAPIリソースの提供 - Portable
別のGateway API実装を利用した際に、マニフェストの変更が不要である - Expressive
Ingressのアノテーションなどで提供されてきたヘッダーマッチングなどをAPIリソース内で表現できる - Extensible
CRDといくつかのレイヤーで関連付けをできるようにします。これにより、細かいカスタマイゼーションをAPIの構造の正しい場所でできるようにする。
nginx Ingress Controllerのアノテーションの量や各LBベンダーが固有のCRDを実装しているのを見ると、Gateway APIというユニバーサルなリソースが開発されたのは理にかないます。特に、ラボ環境でアノテーションを使ってできたことが、プロダクションの環境だと実装されてない、もしくは、別のアノテーションを使う必要があるなど、様々な問題点がありました。これらを解決するためのリソースだと考えられます。
Gateway APIのモデル
コンセプトにある通り、GatewayAPIのリソースは誰がどのリソースを定義するのか意識して開発されています。ネットワークという共有のインフラを使用する性質上、誰が何をするのか、どこまで管理するのかという境界を引くのは大変重要です。それらをどうやって実装しているのか見ていきます。
まず、今回でてくる3つのロールについて説明します。
- Infrastructure provider
インフラの管理者。実際の物理的もしくは論理的なロードバランサーを管理する人たち - Cluster Operator
Kubernetesクラスタを管理する人たち - Application developer もしくはSite Developer
そのKubernetesクラスタ上でアプリケーションを提供する人たち
この3つのロールに合わせたAPIリソースが下記の通り提供されています。
インフラの管理者向け GatewayClass
クラスタスコープなリソースで、クラスタ内で利用可能なGatewayを定義します。
Contourの例では、spec/controller
にこのGatewayを管理するコントローラーをドメイン/パス
方式で提供しています。また、spec/parametersRef
では他のKubernetesリソースを使って、Gatewayに必要な設定を指定できます。今回だと、contour-gateway-sample
で作られたContourのインスタンスを使うという内容が記載されています。
kind: GatewayClass
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
name: sample-gatewayclass
spec:
controller: projectcontour.io/contour-operator
parametersRef:
group: operator.projectcontour.io
kind: Contour
scope: Namespace
name: contour-gateway-sample
namespace: contour-operator
クラスタ管理者向け Gateway
ネームスペーススコープなリソースで、インフラにロードバランサーのインスタンスを作ることができます。ここでいうロードバランサーは論理的なロードバランサーを示しており、ハードウェアのロードバランサーの場合には、設定の追加を示す場合もありますし、VMベースのロードバランサーであればVMのデプロイ、パブリッククラウドであれば新規のロードバランサーサービスの展開を意味します。
spec/gatewayClassName
でどのGatewayClassを使用するか指定します。
また、spec/listeners
という項目で、どのHTTPRoute
リソースをこのGatewayに設定できるか制御します。つまり、クラスタ管理者側で明示的にどのアプリケーションチームがこのGatewayを使えるか制御できるのです。今までは、Ingressの作成をアプリケーションチームに許可すると、インフラやクラスタ管理者からIngressの設定を許可したり拒否するスキームがありませんでした。この点がRole-orientedである所以でもあります。
Contourの例は以下の通りで、app: kuard
をラベルにもつHTTPRoute
のみがこのGatewayを使うことができます。今回の例だとprojectcontour
ネームスペースに作成されたHTTPRoute
のみが対象です。
kind: Gateway
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
name: contour
namespace: projectcontour
spec:
gatewayClassName: sample-gatewayclass
listeners:
- protocol: HTTP
port: 80
routes:
kind: HTTPRoute
selector:
matchLabels:
app: kuard
- protocol: HTTPS
port: 443
routes:
kind: HTTPRoute
selector:
matchLabels:
app: kuard
アプリケーションチーム向け HTTPRoute
ネームスペーススコープなリソースで、HTTPトラフィックを制御するものになります。今まで、Ingressを使ってHostname、 TLS、 ルールを指定してきたと思いますが、それらはここで定義されます。GatewayとHTTPRouteは双方向バインディングが必要なので、Gateway側で指定したラベルをつけないとHTTPRouteがGatewayに設定されません。
今回の例だと、hostnameがlocal.projectcontour.io
となるHTTPリクエストをkuard
と呼ばれるサービスに転送します。
kind: HTTPRoute
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
name: kuard
namespace: projectcontour
labels:
app: kuard
spec:
hostnames:
- "local.projectcontour.io"
rules:
- matches:
- path:
type: Prefix
value: /
forwardTo:
- serviceName: kuard
port: 80
また、下記の通り複数サービスへの重み付けロードバランシングもできるようになってます。それらの機能については次回以降で触れます。
動作確認
Contourのガイドを参考に実施します。サポートされてるKuberneteのバージョン等には注意してください。
今回のKubernetesのバージョンは以下のとおりです。
# kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.0", GitCommit:"cb303e613a121a29364f75cc67d3d580833a7479", GitTreeState:"clean", BuildDate:"2021-04-08T21:15:16Z", GoVersion:"go1.16.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.1", GitCommit:"c4d752765b3bbac2237bf87cf0b1c2e307844666", GitTreeState:"clean", BuildDate:"2020-12-18T12:00:47Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
Contourのオペレータをセットアップをします。
kubectl apply -f https://projectcontour.io/quickstart/operator.yaml
オペレータが動いていることを確認します。
# kubectl -n contour-operator get pod
NAME READY STATUS RESTARTS AGE
contour-operator-7c6cfb8845-jpt56 2/2 Running 0 109m
Contour、GatwayClass、Gatewayを作成します。
kubectl apply -f https://projectcontour.io/quickstart/gateway.yaml
作ったリソースを確認します。
# kubectl -n contour-operator get contour.operator.projectcontour.io
NAME READY REASON
contour-gateway-sample True GatewayClassAdmitted
# kubectl get gatewayclass.networking.x-k8s.io/sample-gatewayclass
NAME CONTROLLER
sample-gatewayclass projectcontour.io/contour-operator
# kubectl -n projectcontour get gateway.networking.x-k8s.io/contour
NAME CLASS
contour sample-gatewayclass
HTTPRouteとそのバックエンドで動くサンプルアプリをデプロイします。
kubectl apply -f https://projectcontour.io/quickstart/kuard.yaml
作成したリソースを確認します。
# kubectl get po,svc,httproute -n projectcontour -l app=kuard
NAME READY STATUS RESTARTS AGE
pod/kuard-798585497b-cxgpz 1/1 Running 0 5s
pod/kuard-798585497b-sbjtp 1/1 Running 0 5s
pod/kuard-798585497b-zmbbr 1/1 Running 0 5s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kuard ClusterIP 172.30.66.190 <none> 80/TCP 5s
NAME HOSTNAMES
httproute.networking.x-k8s.io/kuard ["local.projectcontour.io"]
私の環境下では、ContourのenvoyがExternalIPを持っているので、そのIPを使用してアプリケーションにアクセスします。
# kubectl -n projectcontour get svc envoy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
envoy LoadBalancer 172.30.214.210 192.168.11.62 80:32679/TCP,443:32001/TCP 4m7s
curlで動作確認します。
curl -H 'Host: local.projectcontour.io' http://192.168.11.62/
下記のようにレスポンスが返ってくれば成功です。
$ curl -H 'Host: local.projectcontour.io' http://192.168.11.62/
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>KUAR Demo</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/styles.css">
<script>
var pageContext = {"hostname":"kuard-798585497b-zmbbr","addrs":["172.16.2.19"],"version":"v0.8.1-1","versionColor":"hsl(18,100%,50%)","requestDump":"GET / HTTP/1.1\r\nHost: local.projectcontour.io\r\nAccept: */*\r\nUser-Agent: curl/7.64.1\r\nX-Envoy-Expected-Rq-Timeout-Ms: 15000\r\nX-Envoy-Internal: true\r\nX-Forwarded-For: 192.168.10.62\r\nX-Forwarded-Proto: http\r\nX-Request-Id: 17a98ac8-3ed7-4791-be82-7971f6ae9e5e\r\nX-Request-Start: t=1620785532.982","requestProto":"HTTP/1.1","requestAddr":"172.16.2.17:47774"}
</script>
</head>
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-power" viewBox="0 0 32 32">
<title>power</title>
<path class="path1" d="M12 0l-12 16h12l-8 16 28-20h-16l12-12z"></path>
</symbol>
<symbol id="icon-notification" viewBox="0 0 32 32">
<title>notification</title>
<path class="path1" d="M16 3c-3.472 0-6.737 1.352-9.192 3.808s-3.808 5.72-3.808 9.192c0 3.472 1.352 6.737 3.808 9.192s5.72 3.808 9.192 3.808c3.472 0 6.737-1.352 9.192-3.808s3.808-5.72 3.808-9.192c0-3.472-1.352-6.737-3.808-9.192s-5.72-3.808-9.192-3.808zM16 0v0c8.837 0 16 7.163 16 16s-7.163 16-16 16c-8.837 0-16-7.163-16-16s7.163-16 16-16zM14 22h4v4h-4zM14 6h4v12h-4z"></path>
</symbol>
</defs>
</svg>
<body>
<div id="root"></div>
<script src="/built/bundle.js" type="text/javascript"></script>
</body>
</html>
さいごに
現時点でのGatewayAPIの実装について見てきました。Ingressと比べると複数のリソースの定義が必要なものの、ペルソナに合わせたリソースの定義がされていて使いやすい印象です。実運用だとGatwayClassとGatewayリソースを頻繁に更新することもないと思いますので、今までのIngressと同じように運用はでき、インフラやクラスタ管理者からある程度制御ができるきれいな構造になっています。
今後GatewayAPIにてサポートされているいくつかの機能も触って行ければと思ってます。