every Tech Blog

株式会社エブリーのTech Blogです。

Flutterエンジニアが年150万円のサーバー費用を削減する話

この記事は every Tech Blog Advent Calendar 2024(夏) の18日目の記事です

DELISH KITCHEN 開発部で小売様向き合いで主にアプリ開発をしている野口です。 Flutterエンジニアをしておりますが、直近インフラやサーバーサイドもやらせていただいております。 今回は事業譲渡されたネットスーパーアプリでインフラで使用しているFJcloud-V(旧ニフクラ)で抱えていた問題を一部AWSに置き換えることで解決した話について紹介します。

課題

課題①  SSL証明書更新対応に工数が掛かる

SSL証明書はFJcloud-Vで管理しており、SSL証明書の更新には以下のフローが必要になります。(やりとりが大変だとわかっていただければいいので内容は理解しなくてOKです。)

  1. 小売様の承認

    • 小売様それぞれがドメインを管理しており、whois 情報内のメールアドレス所持者(=小売様内の特定の部署または人)による承認が必要
    • メールの内容に沿って承認手続きを行なってもらう
  2. エンジニアが、FJcloud-Vの管理画面から更新情報を入力し、証明書更新申請を行う

  3. 小売様に証明書更新の承認をしていただく

    • メールの内容に沿って承認手続きを行なってもらう
  4. エンジニアが証明書更新が成功したことを確認し、ロードバランサーに紐づくSSLアクセラレーター更新する

小売様とやりとりしながら更新しなければいけないですし、有効期限ギリギリの場合は小売様を急かすことになり、最悪有効期限が過ぎてしまいサイトを一時閉鎖しないといけないリスクがあります。

また、現在弊社ではネットスーパーアプリを十数社の小売様に提供しているため、この対応を小売様分で行わないといけないのでとても工数が掛かってしまいます。

課題② FJcloud-Vのロードバランサーが小売様ごとに個別に建てられていて費用がかさんでいる

FJcloud-Vのロードバランサーはいくつか種類があるのですが、ネットスーパーで使用しているものはロードバランサー(L4)になります。

https://pfs.nifcloud.com/service/lb.htm

ロードバランサー(L4)は複数ドメインへの対応ができなく、小売様ごとに個別に建てられているため、十数小売様分のロードバランサー費用が掛かっています。

解決方法

課題①

前提としてドメインは小売様のものですが、DNSはエブリー管理なのでDNSレコードの作成をエブリーが行うことは可能です。

なので、DNSレコードを作成し、ALBのDNSの検証を行うことで小売様の承認手続きが不要になります。

また、既存のFJcloud-VのロードバランサーはSSLの終端になっており、この構造を保つために、ロードバランサーをAWSのALBに移行し、証明書をACMに移行することで解決できます。

課題②

ALBは小売様ごとではなく、ネットスーパー全体のロードバランサーを作ることで解決できます。

つまり、ネットスーパー全体のALBとACMで証明書を小売様ごとに作成し、ALBに各小売様用の証明書を紐づけることで解決しました。

構成図はこのようになります。

Route53では複数の小売様のドメインを管理し、ACMから取得した各小売様用の証明書をALBに設定しています。

FJcloud-VのネットワークとVPCはSite-to-Site VPNで疎通できるようにしてあります。

やったこと

以下にはTerraformの実装を記載しています。

  • FJcloud-Vのサーバーとロードバランサーが疎通できるようにターゲットグループを指定する

FJcloud-Vのipに向くターゲットグループを作成します。

# ターゲットグループを作成
resource "aws_lb_target_group" "nifcloud_server" {
    name = "nifcloud-server"
    port                 = 80
    protocol             = "HTTP"
    target_type          = "ip"
}

# FJcloud-Vに向くように設定
resource "aws_lb_target_group_attachment" "nifcloud_server" {
    target_group_arn = aws_lb_target_group.nifcloud_server.arn
    target_id        = &{指定したいIP}
}
  • ALBを作成してターゲットグループを紐づけを行う
# ALB作成
resource "aws_lb" "server" {
    name = "server"
    internal           = false
    load_balancer_type = "application"
}

# ターゲットグループで設定したFJcloud-VのIPと通信するようにする
resource "aws_lb_listener" "server_https" {
  load_balancer_arn = aws_lb.server.arn
  port              = 443
  protocol          = "HTTPS"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.nifcloud_server.arn
  }
}
  • Route 53のホストゾーンと小売様用のalias レコード (ALB に向ける) を作成する
# 小売様Aのホストゾーンの作成
resource "aws_route53_zone" "martA" {
  name = "martA.example.com"
}

# 小売様Aのaliasレコードを作成
resource "aws_route53_record" "martA_a" {
  zone_id = aws_route53_zone.martA.zone_id
  name    = "martA.example.com"
  type    = "A"
}
  • DNS (FJcloud-V管理) を Route53 に向ける

DNSはFJcloud-Vで管理されているので、FJcloud-VからRoute53に向くようにします。

以下の記事のようにRoute53で作成したmartAのホストゾーンのNSレコードをドメインレジストラのネームサーバに設定をすることでFJcloud-VからRoute53に切り替えることができます。

https://dev.classmethod.jp/articles/route53-domain-onamae/

  • Route53に検証用のCNAMEレコードを登録し、ACMで証明書を発行する
# 小売様Aの証明書を発行
resource "aws_acm_certificate" "martA_cert" {
  domain_name       = local.martA_domain_name
  validation_method = "DNS"
}

# Route53に小売様Aの検証用のCNAMEレコードを登録
resource "aws_route53_record" "martA_cert" {
  for_each = {
    for dvo in aws_acm_certificate.martA_cert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = local.cname_default_ttl
  type            = each.value.type
  zone_id         = aws_route53_zone.martA.zone_id
}

# 小売様Aの証明書の検証
resource "aws_acm_certificate_validation" "martA_cert" {
  certificate_arn         = aws_acm_certificate.martA_cert.arn
  validation_record_fqdns = [for record in aws_route53_record.martA_cert : record.fqdn]
}
  • 証明書の検証が通ったらALBに証明書の紐付けを行う
resource "aws_lb_listener_certificate" "server_martA" {
  listener_arn    = aws_lb_listener.server_https.arn
  certificate_arn = aws_acm_certificate.martA_cert.arn
}

作成したALBに対して上記のようにすることで1つのALBに複数の証明書を紐づけることができます。

まとめ

今回の対応で全ての小売様に適用すれば、十数小売様のロードバランサーを1つにすることができます。

FJcloud-Vのロードバランサーが、1ヶ月あたり1万円で1年で12万、小売様が15と仮定すると 12 ✖️ 15で 180万円掛かっていたところ

https://pfs.nifcloud.com/price_extax/network.htm#load

ALBはざっくり概算で1ヶ月あたり5000円で1年で6万円になるので 180 - 6 = 174でだいたい150万円くらいはサーバー費用を浮かせることができます。

また、初めてインフラの構築を行いましたが、クライアントのお仕事と違って、連携するサービスが多く気にしないといけないことが多岐に渡るなと感じました。

ご覧いただきありがとうございました。