Kubernetes上のコンテナをIngressでインターネットに公開するまで

前回 はKubernetesクラスタを構築してコンテナを起動し、ノードからアクセスできるところまで解説しましたが、クラスタが動くようになったとはいえ、まだ単にコンテナを起動しただけに過ぎません。 そこで今回は、起動したコンテナを複数にスケーリングして通信を分散させた上で、クラスタ外部からアクセスできるようにします。これで、耐障害性を確保しながらインターネット上にコンテナを公開できるようになります。 なお、今回は、Kubernetesクラスタ内に下図を構成します。1つずつ見ていきましょう。

Original Post>

Podを複数配備するDeployment

KubernetesではPodを作成することでコンテナを起動しますが、単一のPodを起動するだけでは耐障害性やスケーリングなど、Kubernetesの恩恵を受けることができません。もしPodが起動しているノードに障害が発生したらPodも同時に停止してしまい、サービスが提供できなくなってしまいます。アップデートすることもできません。

KubernetesでPodを起動するためには、多くの場合でDeploymentリソースを作成し、間接的にPodを作成します。DeploymentリソースはPodとアップデートを管理をしてくれるリソースです。DeploymentリソースでPodを管理することで、同一のPodを複数個並列で起動する、Podが障害などで稼働できない場合には代わりに新しいPodを作成するなどの機能が提供されます。Podのコンテナイメージや設定値をアップデートする際には、Podを1つずつ順番にアップデートするローリングアップデートを自動で実行してくれます。

それでは、実際に前回で作成したPodをDeploymentで管理するようにしてみます。マニフェストの定義は下記の通りです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17

Deploymentリソースでは、spec.template配下にPodの設定を書きます。またreplicas: 3を指定していることで、このPodが3つ作られます。実際にdeploymentを作成して挙動を確認してみましょう。Podが3つ作られたことが確認できます。

$ kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created

$ kubectl get deployment,pod
NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment    3/3     3            3           35s

NAME                                READY   STATUS       RESTARTS    AGE
nginx-deployment-66b6c48dd5-75g6s   1/1     Running      0           35s
nginx-deployment-66b6c48dd5-q6f9w   1/1     Running      0           35s
nginx-deployment-66b6c48dd5-tl7db   1/1     Running      0           35s

Deploymentは3つのPodが稼働している状態を保とうとします。試しに1つ削除してみましょう。すぐにnginx-deployment-66b6c48dd5-jrlwwのPodが新しく作られていることが分かります。

$ kubectl delete pod nginx-deployment-66b6c48dd5-75g6s
pod "nginx-deployment-66b6c48dd5-75g6s" deleted
$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-jrlww   1/1     Running   0          6s
nginx-deployment-66b6c48dd5-q6f9w   1/1     Running   0          14m
nginx-deployment-66b6c48dd5-tl7db   1/1     Running   0          14m

Deploymentは単なるPodと比べて運用面、耐障害面で優れているため、実際の運用ではこのDeploymentを通してPodを作成します。

Advertisements

PodへのアクセスポイントとなるService

さて、PodをDeployment化することで、コンテナを複数並列で動かすことができるようになりました。続いて、これらのPodへ通信を振り分けるServiceの設定をしていきます。前回ではPodにアクセスする際にPodのIPアドレス宛に通信をしていましたが、DeploymentでPodを作成する場合はスケーリングやアップデートによりPodが随時新規に作成され、そのたびにIPアドレスも新しいものがアサインされます。そのため、直接PodのIPアドレス宛に通信することは現実的ではありません。Serviceリソースを使うことで、PodのIPアドレスを指定しなくても、これらDeploymentで管理されているPodに自動で通信を振り分けることができます。

Serviceにはいくつか種類がありますが、ここではClusterIP Serviceを利用します。ClusterIP Serviceを作成すると、クラスタ内部でのみ有効なCluster IPアドレスが払い出されます。このCluster IPアドレス宛の通信が各Podに振り分けられます。

先ほど作成したDeploymentで管理されるPodに、ClusterIP Serviceで通信を振り分けるように設定してみましょう。マニフェストの定義は下記の通りです。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
    - port: 80
  selector:
    app: nginx

通信の振り分け先のPodは、selectorでラベルを指定することで設定します。この「ラベル」はKubernetes上で扱うすべてのリソースに付与できます。先ほど作成したDeploymentの中で、template配下にmetadata.labels.app: nginxと記述していました。この設定で各Podにはapp: nginxというラベルが付与されているので、このラベルを持つPodに振り分ける設定をしています。

ラベルを付与されているPodが新規に作成され通信を受け付けられる状態になると、Serviceの振り分け先として自動的に追加されるため、PodのIPアドレスが頻繁に変わるような状況でも継続してアクセスできます。

Webサービスを公開するIngress

さて、Serviceを設定することにより、Deploymentで作成したPodへ通信を振り分ける仕組みが完成しましたが、現時点では、このサービスにはクラスタ内部からしかアクセスできません。クラスタ外部からもアクセスできるように、Ingressを使って公開しましょう。

サービスの外部公開

Kubernetes上でサービスをクラスタ外部に公開する手段はいくつかありますが、代表的なものを紹介します。

NodePort Service

先に紹介したClusterIP Serviceと同様にServiceの一種です。NodePort ServiceではCluster IPに加えてNodePortと呼ばれるポート番号がアサインされ、Kubernetesの各ノード上でリッスンされます。各ノード上のNodeportにアクセスすることでPodに通信が振り分けられます。すべてのNodeでリッスンされるため、同一クラスタでポートが重複してはいけません。一般にWell-Knownポートではなく30000番台などのハイポートが使われます。

LoadBalancer Service

LoadBalancer ServiceはNodePortサービスと異なり、サービスごとに外部からアクセス可能なExternal IPアドレスがアサインされます。そのため、Well-Knownポート含めすべてのポートが利用可能です。ただし、LoadBalncerサービスを利用するためには外部のロードバランサーと、連携用のプラグインが必要です。

GKEなどのマネージドKubernetesサービスであれば多くの場合で使えるようになっていますが、本連載のようにオンプレミス環境にkubernetesクラスタを構築する場合には、そのままでは利用できません。MetalLBのようなソフトウェアを導入するか、あるいはコントローラープラグインを自作する必要があります。

Ingress

上記の2つとは異なり、Ingressはサービスではなく別のリソースです。Serviceがネットワーク層やトランスポート層のロードバランサーだとするなら、Ingressはアプリケーション層のロードバランサーになります。Ingressを利用することで、ホスト名を利用したバーチャルホスティングなどHTTPに類する通信を柔軟にPodに振り分けられるようになります。ただし、こちらもLoadBalancerサービス同様にデフォルトでは使うことができません。別途Ingress Controllerと外部あるいは内部にロードバランサーが必要です。また、クラスタ内部にロードバランサーを持つタイプでは、別途ロードバランサーをLoadBalncer Service等で公開する必要があります。ややこしいですね。

さて、本連載ではIngressを利用してサービスを外部公開します。Ingress ControllerにはTraefikを利用します。TraefikをDaemonSetモードで利用すると、各ノードの80番ポートで通信をリッスンきます。結果的にNodePortと似たような構成に見えますが、関連性はなく仕組みの異なるものです。あくまで、LoadBalancerとしてTraefikが各Workerに配置されていると考えてください。

Advertisements

Ingressのインストール

Ingressをインストールするには、公式ドキュメントの手順に従って行ってください。

kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v1.7/examples/k8s/traefik-rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v1.7/examples/k8s/traefik-ds.yaml

上記でインストールは完了です。簡単ですね。これで各ワーカにロードバランサーが配置されました。

Ingressの利用

改めて、先ほど作成したClusterIP ServiceをIngressを使って外部に公開しましょう。マニフェストの定義は下記の通りです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: nginx-service
                port: 
                  number: 80
            path: /
            pathType: Prefix

少し複雑になりましたが、rules.http.paths.backend.service配下に転送先のサービス名が指定されていることが分かります。複雑な分だけ柔軟な転送設定が可能なので、色々試してみてください。設定ができたら、実際にアクセスしてみましょう。http://<ノードのIPアドレス>でブラウザからアクセスできる状態になっているはずです。

これで、コンテナで動くWebアプリケーションにアクセスできるようになりました。ここではTraefikを利用しましたが、他のIngress Controllerでも同様にしてコンテナを公開できます。

おわりに

今回は、前回で作成したPodを冗長化し、Ingressを利用して外部に公開しました。Deployment、Service、Ingressと様々な概念が出てきましたが、覚えてしまえば簡単に可用性の高いアプリケーションを展開できることがお分かりいただけたかと思います。

次回はアプリケーションに着目し、単なるnginxではなく複数のコンテナを連携することで動作する、より実践的なアプリケーションをKubernetes上で稼働させることを目指します。お楽しみに!