12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWSハイブリッド構成にてKubernetesクラスターを構築する(kopsを利用)

Posted at

はじめに

AWSハイブリッド構成とは、Amazon VPCとオンプレミス環境をAWS Direct ConnectやインターネットVPNを利用して接続し、運用する形態のことです。この場合、Amazon VPCは企業内であれば社内ネットワークの一部として扱われる構成となります。また、特に企業内での利用においては、セキュリティ上の理由からVPCにはインターネットゲートウェイはアタッチされておらず、インターネットアクセスはオンプレミス側に用意されているゲートウェイ/プロキシサーバを通るような構成にされることが多いかと思います。

このような構成の場合、Amazon VPC上にKubernetesを自前で構築するのは一苦労です。そもそもKubernetesのマニュアル構築自体も面倒ですし、その上でインターネットに直接出れないといったネットワーク制約もあり、なかなか大変です。

そこで本記事では、このような環境でKubernetesクラスターをkopsを利用してVPC上にお手軽に構築する方法について説明します。

kopsとは

Kubernetesクラスターを構築するためのデプロイツールのひとつです。Kubernetesオフィシャルなツールのひとつとして開発されています。利用環境としては現時点ではAWSのみが公式サポート環境であり、実質AWS向けのKubernetesクラスター構築ツールです。

本記事では、AWSハイブリッド構成においてkopsでKubernetesクラスターを構築する上でのポイントを中心に説明します。kopsそのものについてはQiitaでも既にいくつか記事がありますので、そちらを参照されるとよいでしょう。

AWSハイブリッド構成における課題

AWSハイブリッド構成でプライベートなVPCの構成の場合、下記点の考慮が必要です。

HTTP(S)プロキシの設定が必要

kopsで構築・運用するにはインターネット経由でリソースの取得が必要になってきます(kubeletのインストールなど)。また、AWSの各種サービスを利用する上でもプロキシ設定が必要です(例えば、DockerイメージのレジストリにAmazon ECRを使うなど)。

kopsのデフォルトネットワーク設定であるkubenetはプライベートサブネットで使えない

VPCはインターネットから接続されていないプライベートサブネットとなる場合、デフォルトのkubenetを使うことができないという制約があります。以下、kopsのネットワークのドキュメントより引用です。

Users running --topology private will not be able to choose kubenet networking because kubenet requires a single routing table. These advanced users are usually running in multiple availability zones and NAT gateways are single AZ, multiple route tables are needed to use each NAT gateway.

kubenetはルートテーブルが1つの環境でしか動かず、AZ単位にNATゲートウェイを立てて冗長化する一般的なプライベートネットワークを構成する場合は、複数のルートテーブルが必要になってくるので対応できない、とのことです(実際に試しにkubenetで構築してみたところ、AZをまたいでPOD間通信ができないという現象になりました)。

この話は下記のとおり、kopsのネットワークトポロジーのドキュメントの方にも書かれており、kubenet以外のネットワーク設定を利用する必要があります

In the case of a private cluster you must also set a networking option other than kubenet.

ロードバランサは内部ELBを使う必要あり

AWSを使う場合、KubernetesのServiceやIngressでELBを使うことができますが、プライベートネットワークのためパブリックなELBは使えず、内部ELBを使う必要があります。

kopsによるクラスター構築のポイント

上記課題に対応方法について具体的に説明していきます。なお、kopsではCUIでクラスター構築時にオプションで設定項目を指定できますが、kopsのバージョンによっては対応していない設定項目があるため、本記事ではkopsのYAMLファイルによるクラスター定義での設定例を記載しています。とりあえず適当なオプションでkops create cluster $CLUSTER_NAME--yesなしでクラスター定義だけをまず作成し、kops edit cluster $CLUSTER_NAMEにてクラスター定義を編集しつつ、kops update cluster $CLUSTER_NAME --yesにて詳細設定した内容でクラスターを構築できます。

HTTP(S)プロキシの設定

私がちょうどkopsを触り始めた時期(2017年の秋頃)に、kopsのHTTPプロキシ対応がコミットされました。当時は未リリースのため自分でビルドして使っていましたが、kops 1.8以降であれば簡単に設定できるようになっています!

kopsのHTTPフォワードプロキシのドキュメントに書かれているように、下記のようにkopsのクラスター定義のYAMLにegressProxyを追加してクラスターを構築するだけで、必要な箇所にHTTPプロキシ設定がまとめて行われます。


spec:
  egressProxy:
    httpProxy:
      host: proxy.corp.local
      port: 3128
    excludes: corp.local,internal.corp.com

ネットワーク設定はkubenet以外を使う

kubenetは使えないため、それ以外のものを利用します。kopsのクラスター定義のnetworkingに設定します。kopsのネットワークのドキュメントによるといくつか利用可能なタイプがあります。例えばflannel-vxlanを使う場合は以下のように設定します。

spec:
  networking:
    flannel:
      backend: vxlan

また、プライベートネットワークのため、kopsのクラスター定義のtopology設定を以下のようにします。

spec:
  topology:
    dns:
      type: Private
    masters: private
    nodes: private

なお、既存のVPC/サブネットに対してKubernetesクラスターを構築する場合は、下記のようにnetworkCIDRにVPCのネットワークアドレスを、networkIDにVPCのIDを、subnetsにサブネット情報を(既存のサブネットを流用する場合は、idでサブネットIDを)を設定します。既存のVPC/サブネットを利用する方法についての詳細は、kopsにドキュメントに記載があります。

  networkCIDR: 172.16.0.0/24
  networkID: vpc-54baa4eb
  subnets:
  - cidr: 172.16.0.0/25
    id: subnet-11e5ee67
    name: ap-northeast-1a
    type: Private
    zone: ap-northeast-1a
  - cidr: 172.16.0.128/25
    id: subnet-a6113be0
    name: ap-northeast-1c
    type: Private
    zone: ap-northeast-1c

ロードバランサーに内部ELBを利用する

Kubernetes APIサーバのELB

kopsのクラスター定義にて、下記のように設定することで、KubernetesのAPIサーバのフロントに内部ELBを利用することができます。

spec:
  api:
    loadBalancer:
      type: Internal

Service/IngressのELB

こちらはクラスター構築後の話ですが、Kubernetesのドキュメントに記載のとおり、AWSの場合はannotationsservice.beta.kubernetes.io/aws-load-balancer-internalを追加することで、Service/Ingressのロードバランサーに内部ELBを利用することができます。

kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app: ingress-nginx
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
    # Enable PROXY protocol
    service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: '*'
    # Increase the ELB idle timeout to avoid issues with WebSockets or Server-Sent Events.
    service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600'
spec:
  type: LoadBalancer
  selector:
    app: ingress-nginx
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https

その他、AWSならではの設定

DockerレジストリにECRを利用する

AWSを使うのであれば、Dockerイメージの管理にはECRを使いたくなります。しかし、kopsで構築したノードに対してデフォルトで設定されるIAMロールは、ECRへのアクセス権をもちません。別途アクセス権を付与する必要があります。これは、kopsのIAMロールに関するドキュメントに記載のとおり、クラスター定義にて下記のようにallowContainerRegistryを設定すればよいです。

spec:
  iam:
    allowContainerRegistry: true
    legacy: false

上記設定でクラスターを構築すると、自動的に作成されるIAMロール(nodes.クラスター名)に下記のようにECRへの参照権限が追加されます。このIAMロールがKubernetesのノード(=EC2インスタンス)のIAMロールとして付与されることで、Kubernetesクラスター内からECRを利用できるようになります。

        {
            "Sid": "kopsK8sECR",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:BatchGetImage"
            ],
            "Resource": [
                "*"
            ]
        }

なお、注意点として、IAMロールで下回りのEC2インスタンスにECRの参照権限付与してしまうため、セキュリティ上考慮が必要な場合があります。例えば、Kubernetesクラスターを共用サービスとして不特定多数の方に提供した場合は、ECRはだれからも参照できる状態になってしまいます(更新は不可です)。ユースケースによってはこの点注意が必要かと思います。
将来的には、Amazon EKSが正式リリースされればIAMロールの適用をもっと細かく制御できるようになるのではないか、と期待しています。

DNSサーバにRoute 53 Private Hosted Zoneを利用する

AWSを使うのであれば、DNSの管理にはRoute 53を使いたくなります。Kubernetes上にアプリケーションをデプロイした際に、自動的にアプリケーションのDNS登録もできるとユースケースによっては便利です。KubernetesのIncubatorプロジェクトのひとつであるExternalDNSがこれを実現してくれるものですが、kopsを使うとこれを簡単に組み込むことができるようになっています。kopsのドキュメントのとおり、クラスター定義にexternalDnsを下記のように設定してクラスターを構築すると有効化されます。

spec:
  externalDns:
    watchIngress: true

これでIngressの設定にあるhostを自動的にDNSに登録してくれるようになります。例えば、下記のようなIngress設定をデプロイすると、myapp.example.orgが自動的にRoute 53に登録されます。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: myapp-ingress 
  namespace: myapp
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: myapp.example.org
    http:
      paths:
      - backend:
          serviceName: myapp-svc
          servicePort: 80

なお、Route 53 Private Hosted ZoneをAWSハイブリッド環境で利用するには、オンプレミス側のDNS環境によっては色々やり方があります。クラスメソッドさんの記事、「AWSハイブリッド構成のDNS設計レシピ」が参考になりますが、例えば、この記事の「設計レシピ4 : Route 53 Private Hosted Zone + Amazon DNSを利用する」のような構成を取る必要があります。
もし、オンプレミス環境に既にDNS権威サーバがあり、Route 53 Private Hosted Zoneにはサブドメインを管理させたい場合は、サブドメインの委任が必要ですが残念ながらRoute 53 Private Hosted Zoneは対応していません。その場合、最近私が書いた「AWSハイブリッド構成にてRoute 53 Private Hosted Zoneでサブドメインを管理する」のような方式をとることで、Route 53を活用することができます。

ログ収集にAmazon CloudWatch Logsを利用する

AWSを使うのであれば、Kubernetesクラスター上で動くアプリケーションのログ管理に、CloudWatch Logsを使いたくなります。アプリケーションはDockerコンテナとして動作しますが、DockerがそもそもCloudWatch Logsに対応しているので、Dockerの設定変更のみで対応することができます。kopsを利用する場合は、クラスター定義にて下記のようにdocker配下でログ周りの設定ができます。

spec:
  docker:
    version: 17.09.0
    logDriver: awslogs
    logOpt:
    - awslogs-region=ap-northeast-1
    - awslogs-group=/k8s-cluster
    - awslogs-create-group=false
    - tag={{.Name}}/{{.ID}}

また、CloudWatch Logsへの登録のための権限をIAMロールに付与する必要があります。kopsのクラスター定義にて、下記のようにadditionalPolicies設定しておくことで、クラスター構築時にIAMロールを追加設定することができます。

spec:
  additionalPolicies:
    master: |
      [
        {
          "Effect": "Allow",
          "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": ["*"]
        }
      ]
    node: |
      [
        {
          "Effect": "Allow",
          "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": ["*"]
        }
      ]

注意点として、logDriverawslogsにするとkubectl logsでのログ参照はできなくなってしまいます。両立させたいのであれば、logDriverでCloudWatch Logsに転送するのではなく、Fluentdなどを使用してCloudWatch Logsに転送する必要があります。

その他、問題と対策

インターネットゲートウェイ問題

プライベートネットワークのためVPCにインターネットゲートウェイのアタッチは不要なのですが、この状態でkopsで既存のVPCに対してクラスターを構築すると、インターネットゲートウェイがアタッチされておらず正常終了しません。一応、#3031 Skip the creation of a IGW にてプルリクエストが出ていますがマージされていません。現状、クラスター構築時は残念ながら一時的にインターネットゲートウェイをVPCにアタッチさせる必要があります(アタッチされていればよく、ルートテーブル設定でインターネットゲートウェイに通信を流す必要はありません)。

まとめ

(あまりニーズはなさそうですが)AWSハイブリッド環境にてKubernetesクラスターを構築する際のノウハウをいくつか紹介しました。何かの助けになれば幸いです。

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?