EKSを経由してS3 Static Websiteにアクセスする


はじめに

静的サイトをサーバレスで公開したいときの選択肢の1つとしてS3のStatic Website Hostingがあります。
今回は独自ドメインでアクセスさせたいときやプライベート空間でアクセスさせたいときに使用できる例としてEKSを経由する方法を紹介します。

概観は次のようになります。ALB Ingress ControllerとNGINX Ingress Controllerを共存させる理由は後述します。

環境情報

macOS Mojave 10.14.6
EKS 1.16
Helm 2.14.3

手順

ここでは手順を説明していきます。EKSとHelm Tillerは既に構築済みであるものとします。

1. S3バケットの作成

今回はstatic-website-test01という名前でS3バケットを作成します。

次にプロパティからStatic Website Hostingの設定を行います。

バケットポリシーはEKSと同一のVPCからのアクセスを許可するようにします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::static-website-test01/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpc": "vpc-XXXXXXXX"
                }
            }
        }
    ]
}

またS3バケットに次のファイルをアップロードしておきます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>S3 Static Website</title>
  </head>
  <body>
    <h1>S3 Static Website</h1>
  </body>
</html>

2. VPC Endpointの作成

AWSコンソールから作成します。
VPCはEKSと同一のものを選択します。ポリシーは特に制限をかけません。

{
    "Statement": [
        {
            "Action": "*",
            "Effect": "Allow",
            "Resource": "*",
            "Principal": "*"
        }
    ]
}

3. Ingress Controllerのデプロイ

ALB1つで対応するためにALB Ingress ControllerとNGINX Ingress Controllerを用意します。
その際kube2iamを用いてALB Ingress ControllerがALBを作成できるようにします。
デプロイはhelmfileを使用します。

helmfile.yaml
repositories:
  - name: stable
    url: https://kubernetes-charts.storage.googleapis.com
  - name: incubator
    url: https://kubernetes-charts-incubator.storage.googleapis.com

releases:
  # https://github.com/helm/charts/tree/master/stable/kube2iam
  - name: kube2iam
    namespace: kube-system
    chart: stable/kube2iam
    version: 2.0.1
    wait: true
    values:
      - host:
          iptables: true
          interface: eni+
        extraArgs:
          auto-discover-base-arn: ""
        rbac:
          create: true
  # https://github.com/helm/charts/tree/master/incubator/aws-alb-ingress-controller
  - name: alb-ingress-controller
    namespace: kube-system
    chart: incubator/aws-alb-ingress-controller
    version: 0.1.11
    wait: true
    values:
      - clusterName: <CLUSTER-NAME>
        autoDiscoverAwsRegion: true
        autoDiscoverAwsVpcID: true
        rbac:
          serviceAccountAnnotations: 
            iam.amazonaws.com/role: arn:aws:iam::XXXXXXXXXX:role/alb-ingress-controller
  # https://github.com/helm/charts/tree/master/stable/nginx-ingress
  - name: nginx-ingress
    namespace: kube-system
    chart: stable/nginx-ingress
    version: 1.39.0
    wait: true
    values:
      - controller:
          service:
            type: NodePort

次のコマンドでデプロイします。

$ helmfile apply

4. Ingress, Serviceのデプロイ

ALBを作成するためにALB Ingress Controllerに読み込ませるためのIngressをデプロイします。
spec.rules[].hostを定義せずに、全リクエストをNGINX Ingress Controllerに飛ばすようにします。
また、annotationsのalb.ingress.kubernetes.io/schemeの値はpublicかprivateかで変わってくることに注意しましょう。

alb-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    kubernetes.io/ingress.class: alb
  name: alb-ingress
  namespace: kube-system
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: nginx-ingress-controller
          servicePort: 80

NGINX Ingress Controllerに読み込ませるIngressを作成します。
ポイントはannotationsにnginx.ingress.kubernetes.io/upstream-vhostを付けてホストヘッダを変えることです。
これは、S3バケット毎にIngressリソースが必要になることを示しています。
このことを考慮しALB Ingress ControllerとNGINX Ingress Controllerを組み合わせて、ALBやCLBの複数作成を防いでいます。

nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/upstream-vhost: static-website-test01.s3-website-ap-northeast-1.amazonaws.com
  name: nginx-ingress
  namespace: default
spec:
  rules:
  - host: s3.sample.com
    http:
      paths:
      - backend:
          serviceName: s3-static-website
          servicePort: 80

ExternalName ServiceでS3バケットのエンドポイントを指定します。

externalname-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: s3-static-website
  namespace: default
spec:
  type: ExternalName
  externalName: static-website-test01.s3-website-ap-northeast-1.amazonaws.com

次のコマンドでデプロイします。

$ kubectl apply -f alb-ingress.yaml
$ kubectl apply -f nginx-ingress.yaml
$ kubectl apply -f externalname-service.yaml

あとは、Route53などでnginx-ingress.yamlで定義したs3.sample.comの名前解決を行いALBに転送されるよう設定します。

確認

試しにブラウザからS3バケットのエンドポイントに直接アクセスしようとしても、公開されていないので拒否されることが確認できます。

次にIngressで定義した独自ドメインでアクセスします。
index.htmlが表示されることが分かります。

まとめ

EKSを経由してS3 Static Websiteにアクセスできることを確認しました。
S3バケットを公開しない方法の1つとして使用できる構成かと思います。
セキュリティを上げるためにNGINX Ingress ControllerにBasic認証を付けるのも良いかもしれません。