VPCエンドポイントを活用したコスト削減 完全版 - 後編

こんにちは。アスクルの荒木(@news_it_enj)です。
先日投稿したVPCエンドポイントを活用したコスト削減 完全版 - 前編の後編になります。

EC2 その他 料金

前編の最後をおさらいです。
ログ出力構成はCloudWatch Logsを使用せず、コンテナのサイドカーとしてFireLens(Fluent Bit)を使い、Firehoseを経由して S3/New Relic に出力するようになりました。

そして料金はCloudWatchが減りましたが、EC2 その他が増えました。

EC2 その他の料金の内訳は次のようにNAT Gatewayの処理データ料金が高いことがわかりました。

ちなみに内訳の確認方法はこちらになります。
(コスト削減芸人への第一歩)

blog.serverworks.co.jp

NAT Gatewayの処理データ料金が増加した原因

Firehoseのエンドポイントへの送信は、インターネットへのアウトバウンド通信となります。
そしてログ出力量が多いため、インターネットへのアウトバウンド通信量も多くなり、NAT Gatewayの処理データ料金が増加しました。
公式ドキュメントにはアウトバウンド通信になる旨のような記載が見当たりませんでしたが、次の記事がとても参考になりました。

cyberagent.ai

NAT Gatewayの処理データ料金の増加対策への道のり

直近でAWS SAPの勉強している中で解いた、とある模擬試験で同じような問題に遭遇しており、これはVPCエンドポイントを作成すればどうにかなると考えていました。

また、他社の事例でまさに似たような構成に対するドンピシャな解決方法(VPCエンドポイント作成)を発見したので、さらに確度が高まりました。

VPCエンドポイント作成

アスクルではTerraformを使っているため、次のようなコードでVPCエンドポイントを作成しました。

data "aws_vpc_endpoint_service" "kinesis_firehose" {
  service      = "kinesis-firehose"
  service_type = "Interface"
}

resource "aws_vpc_endpoint" "kinesis_firehose" {
  vpc_id            = aws_vpc.this.id
  service_name      = data.aws_vpc_endpoint_service.kinesis_firehose.service_name
  vpc_endpoint_type = "Interface"
  # private_dns_enabledがtrueのインターフェイス型VPCエンドポイントは1サービスに付き1つしか作成できない
  # https://aws.amazon.com/jp/premiumsupport/knowledge-center/vpc-interface-endpoint-domain-conflict/
  private_dns_enabled = true
  # 1つのVPCエンドポイントに紐付けられるVPCサブネットは、1az毎に1つのみ
  subnet_ids = [
    aws_subnet.prod_private[0].id,
    aws_subnet.prod_private[1].id,
    aws_subnet.prod_private[2].id,
  ]
  security_group_ids = [aws_security_group.vpc_endpoint.id]

  tags = merge(
    {
      Name = "${local.account_name}-vpce-kinesis-firehose"
    },
    local.tags
  )
}

resource "aws_security_group" "vpc_endpoint" {
  name_prefix = "${local.account_name}-vpce-sg"
  description = "Security Group for VPC Endpoint"
  vpc_id      = aws_vpc.this.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(
    {
      "Name" = "${local.account_name}-vpce-sg"
    },
    local.tags,
  )
}

resource "aws_security_group_rule" "vpce_ingress_from_private_subnets" {
  security_group_id = aws_security_group.vpc_endpoint.id
  type              = "ingress"
  protocol          = "tcp"
  from_port         = 443
  to_port           = 443
  cidr_blocks = concat(
    local.prod_private_subnets,
  )
  description = "All HTTPS from private subnets for private subnets"
}

セキュリティグループのインバウンドルールはコンテナが配置されるプライベートサブネットからの443ポートを許可するのみになります。
(この設定が合っているか不安でしたが、後述の疎通確認や実際に動作していることを確認できたので、これで合っていました)

VPCエンドポイント疎通確認

踏み台からFirehoseエンドポイントへのdigコマンドを実行し、VPCエンドポイントのプライベートIPアドレスが返って来れば疎通OKです。
(踏み台はVPCエンドポイントにルーティング可能なサブネットに配置されている前提)

$ dig +short firehose.ap-northeast-1.amazonaws.com.
10.144.32.7
10.144.34.20
10.144.33.43

VPCエンドポイント作成後のコスト

ちなみにVPCエンドポイントを作成すると、作成した分のコストが発生します。
NAT Gatewayの処理データ量が少ない状態でVPCエンドポイントを作成すると逆にコストが増えるので注意です。
そのあたりの分岐点としては、NAT Gatewayの処理データ量が1ヶ月あたり約800GBがポイントになり、それを超えるとVPCエンドポイントを使ったほうがお安くなります。
詳細はこちらの記事がとても参考になります。

chariosan.com

まとめ

おまけ

ログ出力では、Firehoseを使用していますが、ログ量が多かったためにスロットリングが発生していました。
(スロットリングが発生すると、対象のログが連携先(S3/New Relic)に出力されない)

FireLensのログにも上限エラーが出力されていたので、スロットリングに気づけました。

[error] [output:kinesis_firehose:kinesis_firehose.2] Thoughput limits may have been exceeded, example-name
[error] [output:kinesis_firehose:kinesis_firehose.2] PutRecordBatch request returned with no records successfully recieved, example-name
[error] [output:kinesis_firehose:kinesis_firehose.2] Failed to send log records
[error] [output:kinesis_firehose:kinesis_firehose.2] Failed to send records

こちらに関しては、サービス上限緩和申請からDelivery Stream Throughput (MiB/Sec)の申請をすることで、スロットリングの対応ができました。

これにてハッピーエンド
〜完〜

ASKUL Engineering BLOG

2021 © ASKUL Corporation. All rights reserved.