なぜK8sでWebアプリのセッションが壊れるか? ~原因と対処~


0. 要約

  • KubernetesにセッションをもつWebアプリをデプロイした場合を考える.
  • セッションの保管先は,マシンの内部と外部に分類される.
  • セッションをマシンの内部にもつと,スケーラビリティが損なわれる.
  • セッションをマシン内部に保持するアプリをスケーラビリティをもたせる方法は次の2つがある.
    • Session Affinity
    • セッション切り出し
  • スケーラビリティで重要なのはソフトウェアアーキテクチャである.
  • コンテナでもVMでもマシン内部に状態を保持するとスケーラビリティに課題が生じる.

1. はじめに

KubernetesでスケーラビリティをもたせたWebアプリケーションのデプロイをするときに苦労したセッション管理について紹介します.アプリケーションをKubernetesにデプロイする場合の注意点と対策を紹介します.

2. セッションとは

HTTPはステートレス(状態をもたない)プロトコルです.そのため,Cookieに代表される識別子を付与することでセッションを実現しています.この記事ではWebアプリケーションにおいてCookieにより実現されるセッションの管理について説明します.

3. セッションの保管先

Webアプリケーションにおいてセッションはログイン状態の維持でよく使われています.セッションを保持する方法の例には以下があります.

  • マシン内のメモリやファイル
  • マシン外のデータベースやKVS

セッションを識別するための識別子の例には以下があります.

  • Cookie
  • HTTPヘッダ

この章では,セッションの保管先を比較・検討します.

3.1. マシン内のメモリやファイル

メモリやファイルによる方法では,マシンごとに内部でセッションを保持しています.スケールアウトによりマシンの台数を増えると,マシンごとにセッションが独立して保持されます.その結果,マシンによってセッションの存在するものと,存在しないものが生まれます.これでは分散システムで重要な一貫性(consistency)や独立性(isolation)が維持できません.

3.2. マシン外のデータベースやKVS

データベースやKVSによる方法では,マシンの外部でセッションを保持しています.マシンが増えてもセッションはマシンの外部で保持するため,マシン間でセッションが共有されます.これにより,どのマシンにアクセスしても同一のセッションを利用することができます.

4. スケーラビリティの検討

マシン内のメモリやファイルを利用する場合,スケールアウトするとマシンごとにセッションが独立するためセッションの不整合が発生します.マシン外のデータベースやKVSを利用する場合は,セッションがマシン間で共有されるためスケールアウトした場合もセッションが壊れません.

以下では,マシン内のメモリやファイルをセッション管理に使うアプリケーションをスケールアウトさせる方法を検討します.

4.1. (方法1) セッション固定

マシン内でセッションを保持するアプリケーションをスケールさせるための方法としてIngress(Serviceのtype:ingress)によるセッション固定があります.これはSession Sticky(Session Affinity)とよばれます.Session StickyをKubernetes上で実現する方法には,Nginx IngressやAmbassadorの利用があります.

以下は,Nginx IngressによるCookieベースのSession Stickyです.
Sticky Sessions - NGINX Ingress Controllerより引用

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-test
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"

spec:
  rules:
  - host: stickyingress.example.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /

以下は,AmbassadorによるCookieベースのSession Stickyです.
Sticky Sessions / Session Affinity - Load Balancing in Ambassador Edge Stack | Ambassadorより引用

apiVersion: getambassador.io/v2
kind:  Mapping
metadata:
  name:  quote-backend
spec:
  prefix: /backend/
service: quote
resolver: my-resolver
load_balancer:
  policy: ring_hash
  cookie:
    name: sticky-cookie
    ttl: 60s

4.2. (方法2) セッションの切り出し

セッションをマシン内に保管するWebアプリケーションをスケールさせるには,セッションを外部へ保管する実装への変更が必要となります.

例えばNode.jsのWebフレームワークであるExpressの場合,connect-redisを使った外部のRedisにセッションを保管する実装へ修正が必要です.

PHPの場合,php-redis-extension を使うことで,実装を変更せずにセッションをRedisへ保管できます.

マシン間のメモリを共有するIn-Memory Data Gridも利用することでメモリ空間を共有できるかもしれません.

参考: “限界”を突破する技術、「インメモリデータグリッド」 (1/2) - ITmedia エンタープライズ

4.3. 比較

Session Affinity

  • メリット: ソフトウェア実装を変更せずにスケーラビリティを高められる.
  • デメリット: Session Affinityを実装しているロードバランサ(Ingress)でしか実現できない.

セッションの切り出し

  • メリット: Session Affinityに比べてアプリケーションとミドルウェアの依存度を下げられる.
  • デメリット: ソフトウェア実装の変更が必要になる.

5. おわりに

KubernetesでスケーラビリティをもたせたWebアプリケーションのデプロイをするときに苦労したセッション管理について紹介しました.マシン内部に状態を保持するアーキテクチャのWebアプリケーションはVMやコンテナに関わらず,スケーラビリティに課題が発生することが分かります.ソフトウェア設計においてスケーラビリティを意識して設計できるかは,重要な観点だといえます.

既存のDockerイメージがスケーラビリティを持つかは,Dockerfileやソースコードを読まない限り把握できないのが課題だと考えています.わたし自身もDocker Hubで公開されているDockerイメージがスケーラビリティをもつかの判断に苦労しています.Helmを使った場合も同様にチャートの作者以外がスケーラビリティをもつかは判断しにくいです.

ソフトウェアのアーキテクチャ設計においてスケーラビリティを配慮すると,Kubernetesに載せたときに幸せになれると思います.

参考資料