インフラ運用管理共通ツールセット「gi」のこれまでとこれから

IT 基盤部の廣瀬です。

IT 基盤部ではグループ横断で使っている、インフラの運用管理を支援する共通ツールセットがあります。今回は、このツールセットが生まれた背景、存在意義、メリット・デメリット、今までの反省と今後について話します。

gi とは

IT 基盤部には 5 つのグループがあり、弊社で手がけているサービスのほとんどを IT 基盤部の各グループで担当して運用管理しています。

ゲームを始め、ヘルスケア事業、ソーシャル LIVE 事業、スポーツ事業など、多岐にわたるサービスが存在しています。

インフラの運用管理にあたり、それを補助、支援するスクリプトというのはどうしても必要になってきますが、このような状況で、各グループ、各サービス担当チームで各々スクリプトを実装するのはさまざまなデメリットがあるため、IT 基盤部では共通のツールセットをみんなでメンテナンスして使っています。

この共通ツールセットのことを、部内では「gi」と呼んでいます。

gi が生まれた背景

私が入社した 2011 年ごろは、今ほどサービスのバリエーションは多くなく、主軸はゲーム事業でした。

ゲーム事業の開発には内製の MobaSiF と呼ばれる WAF を使っていたのですが、インフラが使うスクリプト群もこの MobaSiF のリポジトリに含まれていました。また、使用している内製のライブラリや、たとえば冗長性の担保といったさまざまなしくみの実装も、ゲームとインフラとで強く結合し、不可分な状態でした。

ソーシャルゲーム勃興期の Mobage のような、急激に成長するサービスを切り盛りするには、このようなモノリシックな構成は一番効率がよいと個人的には思っています。しかし時を経るにつれ、たとえばインフラ側でモジュールを追加したりモジュールのバージョンアップをしようとしても、密結合しているゲームへの影響が怖くて実施できないといった、スピード感が損なわれるケースが散見されるようになってきました。

ちょうどそのような時期に、海外向けのサービスをオンプレではなくクラウドで行うため、インフラ環境をゼロから作る必要がある案件があったり、Perl ではない言語で (MobaSiF は Perl ベースです) サービスを開発する案件が持ち上がりつつありました。

そこでこれを契機に、次のことを実現するために新しいインフラの共通ツールセットを開発しようということになりました。

  • サービスとは独立した、インフラの運用管理のためのツールセット
  • オンプレだけでなくクラウドにも対応

設計と初期の実装は私が行ったのですが、加えて次の点も意識しました。

  • 今後、ゲーム以外のさまざまな新規案件が立ち上がるのを見越して、どんなサービスでも対応できる柔軟性を持たせる
  • 将来的な既存案件のスムーズな移行のために、サービス側でも使っているモジュールやサーバ管理のしくみは互換性を維持する

git log をみたところ最初の commit は 2012-11-12 でした。

初期の実装こそ私が主に行っていましたが、これまで延べ 100 人以上の committer による 45,000 近い commit 、今では IT 基盤部が担当するほぼすべてのサーバで gi は導入されており、gi は IT 基盤部みんなで作り上げたツールセットに成長しています。

共通ツールセットのメリット

質の向上

特に監視や構築スクリプトが顕著ですが、各グループにはそれぞれのノウハウが蓄積されています。

それを持ち寄り共通のスクリプトに実装することで、IT 基盤部全体、ひいては弊社のサービス全体の質の高い安定運用への貢献、また運用作業の効率化が実現できます。

学習コストを低く抑えられる

IT 基盤部ではグループ間の異動がたびたびあります。

もし、グループ間で運用スクリプトがバラバラだったら、たとえば、用途やタグによってサーバを検索するような日常的に使うスクリプトの使い方が全然違っていたら、あるいは、同じミドルウェアなのに構築、運用、監視のスクリプトがまったく異なっていたら、異動後にキャッチアップするまでに時間がかかってしまうでしょう。

ツールセットを共通化することで、こういった時間を最小限にカットできます。

また、異なるグループの人どうしの会話でも、 gi が共通言語になるのでやりとりもスムーズになります。スクリプトの名前を挙げれば、どのグループの人でも何を目的としてどのような動作をするものか頭に浮かぶからです。個人的にはこれがかなり大きなメリットじゃないかと思っています。

共通ツールセットのデメリット

一方でデメリットがまったくないわけではありません。

新規メンバーの学習コストが高い

学習コストについてはメリットの節で挙げたような利点がある一方で、新規メンバーにとっては、膨大な数のスクリプト、さまざまな運用管理のしくみなどをひとつひとつ理解していくのは気が遠くなる作業に感じられるでしょう。

新規メンバーにはメンターがついて OJT で学んでもらっていますが、段階的に学べる教材を用意するのは課題としてあります。

ガラパゴス化の懸念

今ならもっと効率がよかったり信頼性が高かったりするツールやシステムが存在するにもかかわらず、なかなか外に目が向かず、時代遅れの内製実装を使い続ける懸念があります。

導入・移行コストはかかるものの、しっかり見極めてよいものは貪欲に採用していきたいところではあります。

修正に対する気後れ

gi が大きくなった副作用か、「gi はこういうものだ」という現在の状態を絶対視しがちな傾向があるようで、便利なオプションのアイデアを思いついても実装しようとしなかったり、「gi が対応していないからあのミドルウェアは導入できない」といったような話をたまに耳にします。

また、コードの修正が他グループへ影響するのを懸念して亜流が生まれてしまっています。たとえば、ディスクの残量を監視する Nagios プラグインのスクリプトは、対応デバイスが微妙に異なるのが複数存在してしまっています。

これらは、たとえばレビューの強制といったシステム的な手法で改善できるものもありますが、gi の根幹の設計思想やガイドライン、gi はみんなで作り上げるみんなのものだよ、といったことを IT 基盤部のメンバーに伝え続けることが何より足りなかったなと反省しています。

新しい gi

既報のとおり、現在、弊社はオンプレからクラウドへ大規模な移行作業を行っている最中です。

そしてこれを機に、これまでの改善点や反省点を踏まえ、gi とその周辺のインフラ運用管理のための内製のミドルウェアを再設計して実装している真っ最中です。

クラウド移行が落ち着いたら、新 gi とその周辺の話もまた別の機会にできるんじゃないかと思います。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

DeNAのリアルイベントの舞台裏〜ネットワーク構築編

はじめに

こんにちは、IT基盤部の兵藤です。 主に社内ネットワークのインフラを担当しています。

弊社では、DeNA TechConなどの技術系イベント、Pocochaで開催しているリアルイベントなどを主催しており そのイベント会場のネットワーク構築は我々(IT基盤部ネットワークグループ)が行っています。

今回のブログでは、そのイベントの舞台裏となる 会場ネットワーク構築の手順などをお伝えしていこうと思います。

イベントのネットワーク構築の流れ

イベントのネットワーク構築は ざっとこのような手順で行っていきます。

  1. イベント会場のネットワーク状況の把握
  2. ネットワーク設計
  3. キッティング
  4. 当日の構築

一つずつ簡単に説明していきます。

1.イベント会場のネットワーク状況の把握

まずはイベント会場のネットワーク状況について把握する必要があります。 会場によって既設の設備も様々であり、利用させてもらえるものは利用したほうが費用が安く抑えられる場合もあるからです。 無線の設備がある会場もなかにはありますが、こちら側で無線の状態などコントロールができないため、基本的には利用していません。

会場の既設ネットワークの確認では主に以下の項目を確認しています。

  1. 回線の種類は何を使っているのか。 こちらについては利用している回線の種別から、回線の品質についてある程度判断しています。 例えばフレッツ光ネクストを利用しているのであれば、ベストエフォートで1Gbpsの回線であることがわかります。

  2. 回線の実効速度はどの程度か。 回線の種別がかわかったとしても、実効速度がどの程度あるのかを調べておかないと、ベストエフォート回線では致命的になります。 フレッツ回線の場合はまず間違いなく1Gbpsは出ませんので、実際に接続させてもらい、複数の速度測定サイトなどを利用して平均値を測っています。

  3. 回線は安定して利用できているか。 こちらについては情報があればもらうようにしています。 上記で実効速度を測ったとしても、それは観測時点での結果であり、継続してその速度が出る保証はどこにもありません。 なので、ある程度のスパンで回線の状況について計測しているデータが有ればもらうようにしていますが、フレッツを利用しているところは大抵持っていませんので判断に困ります。

上記のようなことを総合して、既設回線を利用するのか、新規に安定した回線を引き込むのかを判断して対応しています。

これまで対応してきた中では、会場の状況については下記のようなものが挙げられます。

  1. 会場の既設ネットワークが利用可能であり、帯域的にも十分利用に耐えうる
  2. 会場の既設ネットワークが利用可能ではあるが、帯域が乏しく利用が難しい
  3. 会場の既設ネットワークが利用不可だが、回線の引き込みは可能
  4. 会場の既設ネットワークが利用不可でありかつ、新たな回線の手配も不可能

ほとんどの会場が2か3に該当することが多く、会場となる場所へ新規にインターネット回線を引き込みさせていただいています。 1ができる会場は稀で、4についてはそもそも会場選びの候補から外してもらっています。

TechConで利用しているヒカリエホールは、1に該当してますが、通常利用ではベストエフォート100Mbpsの帯域のみの利用であるため、帯域を1Gbpsへ拡張するために利用料を上乗せして払っていたりします。

インターネット回線を会場へ引き込む場合、新規の回線敷設には1.5ヶ月〜3ヶ月必要と回線業者から言われることが多く、早め早めに行動することが必要になってきます。 回線敷設については、NTTが絡むことが多く、回線業者でもコントロールすることが難しい部分もあるため、毎度綱渡り状態で回線敷設していたりします。

2. ネットワーク設計

会場の状況が確認でき、インターネット回線利用の方向性が固まったら、次にネットワーク設計をします。 基本的な構成は下図のように設計・構築しているのですが、会場によっては既設部分とのつなぎの仕様を確認したりして柔軟に対応しています。

Network-diagram.png

会場の大きさ、形、有線LAN利用の要望などを考慮し、無線のカバレッジホールができないように、機器の台数、設置位置などについて設計していきますが、、、

DeNAではイベント用に専用のネットワーク機器を確保しているわけではありません。 都度、必要台数を設計し、通常運用で確保している予備機器を利用して、構築しています。 予備機の状況によっては、要件を満たずのが困難だったりもしますが、その時は必要最低限の箇所に絞ってもらい対応するなどしています。

また、論理設計だけでなく、物理設計についても都度会場に合わせて設計をしています。 会場によって、どこに電源があるのか、どこに機器を設置するのか、どのようにケーブルを取り回して配線するのか、など考慮する必要があります。 ケーブルの長さ、本数などが足りなければ購入するなどして対応しています。

3. キッティング

設計が終わればキッティングを実施します。 キッティングと言ってもすでに何度もイベントを実施しているため、基本的な設定はすべて完了しているため、ただただ地道にコンフィグ設定をしていきます。

設定が全て終われば、仮組みをし設定に問題がないか確認します。 仮組みで問題なければ、機器、ケーブル、電源タップなど必要機材一式を梱包し、会場へ配送手配をします。

4. 当日の構築

イベントでは前日に準備日を設けている場合と、そうでない場合があります。 前日準備が可能であれば、構築時間も潤沢にあるので問題ないのですが、当日準備の場合は限られた時間の中で構築する必要があり、NOCメンバー全員で手分けして構築作業をしています。 広い会場では物理的な配線作業が一番時間を要します。多いときは6人で3時間ずっと配線作業していたりします。

物理的な構築が完了したら、無線のカバレッジホールが無いかどうか、会場全体を無線スキャンしながら回ります。 問題がなければ基本的な構築は完了です。あとは本番に向けて監視の設置をしていきます。

監視についてはnagios(機器監視)などを構築して使っていたり、Cisco Meraki機器を利用してネットワーク構築した際などはMeraki Dashboard上でトラフィックの傾向確認をしていたりします。。 機器監視については、本番中に機器が停止していないか、CPUなどのリソースは問題ないかを監視しています。 過去の例では、イベントの関係者が誤って電源を抜いてしまったなどのトラブルがありましたが、監視のおかげですぐさま復旧させ事なきを得ました。

トラフィック傾向確認については、今後のイベントのために傾向を見れるように可視化するために使っています。 過去のTechConでは来場者1000人程度に対して、最大同時接続650台程度で300Mbps程度のトラフィックを観測しています。

イベントが終了したあとは、撤収作業も自分たちで行っています。撤収も時間が限られているため時間との戦いです。 設営のときほど気を使わなくていいので、一気に撤去作業を進めていきます。整理整頓は後でいいので撤去優先でどんどん実施します。

自社によるネットワーク構築のメリットデメリット

このようにイベントにおいて自社でネットワークの環境を構築していると普段あまり体験しないレイヤー1の部分からすべて自分で対応することで、ネットワーク構築の経験を積めるといったメリットがあります。 構築費用面においても、自分たちですべて実施することで、外部への費用流出を抑え低コストで実施できます。 設計についても自分たちでやることで、柔軟に対応でき、スピード感を持って対応することができます。

一方でトラブル時などは全て自分たちで解決しないといけないといったデメリットは懸念されるところですが、事前検証をみっちりやることで未然にトラブルを防ぐようにしています。

おわりに

以上、ネットワーク構築の流れやメリットなどを紹介させて頂きました。

簡単ではありますが、少しでも良い環境構築の参考になれば嬉しく思います。 最後までお読みくださり、ありがとうございました。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Auto Scaling から見るInfra System の構成

こんにちは、IT 基盤部の Wei です。大規模ゲームタイトルおよびゲームプラットフォームのインフラを運用しております。

私のグループが運用しているシステムでは Auto Scaling を導入してからもうすぐ2年が経ちます。

この2年で Infra System の構成は徐々に変わってきました。今回は Auto Scaling という観点から、現時点の Infra System の構成がどのようになっているかをご紹介します。

Instance の台数管理

Auto Scaling を導入する前、Instance の台数管理は人手でした。台数管理には必要台数の計算から Instance の構築・撤去まで Instance 関連の処理が全て含まれるため、人手で行うには多くの工数が掛かります。また、人手で対応するため想定外のトラフィックが押し寄せた場合に台数を増やすのに時間が掛かるというケースも多々ありました。更に、人手だと管理頻度にも限界があり、余剰リソースを持つ時間帯が多く、それゆえ無駄なコストも掛かっていました。

以上の課題を解決するため、Instance の台数管理を自動化する必要があります。当時マネージドの Auto Scaling サービスで我々のシステムの要件を満たすものはなく、また Spot Instance との併用時に求められる柔軟性などを考慮し、Auto Scaling システムは独自に実装することにしました。ちなみに、このシステムは現時点では GCP の Managed Instance Group と連携するなど、一部マネージドサービスの機能も取り込んでいます。

台数管理システムには大きく2つの Component があります。台数維持を担当する Component (以下、Instance Controller)と台数計算を担当する Component (以下、Auto scaler)です。

Instance Controller は、設定された台数を維持することを目標にします。サーバが落ちたら自動的に Instance を作成しますし、Instance 台数が設定値を上回ったら Instance を撤退します。Instance Controller に対する入力は必要台数の総数のみですが、単に台数を足すだけではなく様々な事情を考慮する必要があります。Spot Instance と On-demand Instance の比率をどうするのか、複数の Instance タイプをどのように混在させるのか、どの Availability Zone に Instance を立てるのかなど。例えば Spot Instance が枯渇したら On-demand Instance を利用する必要がありますし、特定の Instance タイプの在庫が枯渇した場合は別な Instance タイプを利用する必要があります。ちなみに、なぜ種類が多いか以前の記事 DeNA の QCT マネジメント を参考できると思います。

一方、Auto scaler は必要台数を計算して Instance Controller に伝えます。台数の計算は、Metrics (基本的にCPU) に基づいていますので、Metrics 集計システムが必要です。一般には CloudWatch、Prometheus などが使われますが、我々のシステムはここも独自実装になっています。フラッピングを避けるため、Auto scaler は CPU 利用率が一定の範囲内に収まるように指示を出します。具体的には CPU 使用率が60%を超えたら台数を増やし、40%を下回ったら台数を減らす、という具合です。そして、Instance の構築はどれだけ高速化を頑張っても数分単位で時間が掛かるため、瞬間的になトラフィック増には間に合いません。そのようなケースは事前に予測可能なことがほとんどなので、事前に台数を増やしておくことで対応しています (そのようなスケジューリング機能も持っています)。

flow0.png

Instance の作成

DeNA にはサーバのメタデータを管理するための専用のシステムが存在します。これは主にオンプレ環境でサーバを維持管理するために必要なシステムでした。クラウドの IaaS 環境においても、このサーバのメタデータ管理システムが必要です。

このメタデータ管理システムと連携するため、Synchronizer と呼ばれるコンポーネントを開発しました。オンプレ環境においてはこのシステムに登録する作業は人手で行なっていましたが、IaaS 環境においては Terrafrom や Google Managed Instance Group や AWS Auto Scaling Group などと連携する必要があり、その処理を自動化するために Synchronizer が必要です。

クラウドのリソース情報をメタデータ管理システムと連携させるにはもう一つ問題があります。それは利用するクラウドの種類が増えるたびに、そのクラウド専用のロジックを実装する必要があることです。この問題を解消するため、我々は Cloud Adapter と呼んでいる抽象化層を実装しました。例えば、Instance にアタッチする Volume として AWS であれば EBS、GCP であれば Persistent Disk がありますが、これらはVolume という概念で抽象化しています。こうすることで他システムからはクラウドサービスの違いを意識することなく統一した Interface で扱うことが可能です。

flow1.png

ちなみに、Interface 自体は、Protocol Buffers で定義しています。例えば、Instance の定義:

 message CloudInstance {
   string id = 1;
   cloud.CloudProvider provider = 2;
   string zone = 3;
   string name = 4;
   // Private IP of first NIC
   string private_ip = 5;
   // Public IP of first NIC
   string public_ip = 6;
   // Subnet Reference of first NIC
   cloud.ResourceReference subnet_ref = 7;
   string instance_type = 8;
   map<string, string> label = 9;
   google.protobuf.Timestamp launch_time = 10;
   string fqn = 11; // Fully qualified name such as self-link, ARN, etc.
 ...
 }

以上で、Instance を作成したら同時にメタデータ管理システムにも登録することが可能になります。同時に Instance の Life cycle も開始されます。

Instance の Life cycle 管理

Instance を作成したら、OS 設定、アプリケーションコードのデプロイ、テスト、監視設定などの作業を行います。この一連の作業はこれまで専用のスクリプトで行なっていましたが、スクリプトだと以下のような問題がありました。

  • スクリプトがどんどん太り、メンテナンスコストが増え続ける
  • 長大なスクリプトは冪等性を担保することが難しい
  • 処理の途中からリトライさせるような処理を書きづらい
  • スクリプトの実行ホストが落ちた場合に構築・撤去処理自体が失敗する
  • 構築・撤去処理がどこまで進んでいるのか見えづらい
  • 構築・撤去処理の負荷分散がしづらい
  • 構築・撤去処理の並列実行やパイプライン化が難しい
  • 実行してるスクリプトを丁寧にキャンセルしにくい

構築台数が増えれば増えるほど、上記の問題は顕在化してきます。この問題を解消するため、Workflow というシステムを開発しました。

Workflow は大きく2つの Component から成ります。タスクの管理とディスパッチを行う Scheduler とタスクを実際に実行する Executor です。Scheduler と Executor はそれぞれ冗長化していますので、数台落ちても Workflow の実行に与える影響は軽微です。Workflow は各処理に対してリトライやタイムアウトを設定することが可能です。またタスクの単位を小さくすることで冪等性も実装しやすくなります。

Workflow は Jenkins のように設定したタスクを順番に実行していきます。さらに、Workflow はタスクの分散処理、パイプライン化、自動リトライ、依存関係を考慮したタスクの実行などを行うことができます。Workflow で実行するタスクを設計する上で二つ重要なポイントがあります。1つ目は各タスクに冪等性を持たせること、2つ目はタスク間の依存関係を減らすことです。この2つを満たすことでタスクの途中失敗を減らすことができます。

flow2.png

Instance の構築タスクは前述の通り様々ありますが、その中で一番難しいのはアプリケーションのデプロイです。デプロイの方法次第では、その処理自体が非常に時間が掛かってしまいます。この問題には、事前に最新のアプリケーションをデプロイした Image を用意しておき、そこから Instance を作成するという方法で対処することにしました。

Instance の撤退

Instance の Life cycle の終わりは、Instance をサービスから完全に切り離し、Instance 自体を削除することです。メタデータ管理システムからの削除も行う必要があります。これで Instance の撤退が完了だと思いますが、実はそうではないです。Volume の Life cycle も考慮する必要があります。

なぜ Volume の Life cycle と Instance の Life cycle が違うかというと、主にログ回収のためです。ログの回収は、エラーログなど即時転送しているログもありますが、データ量によって即時転送していないログもあります。さらに、転送先(例えば Fluentd サーバや Stackdriver Logging など) の障害が起きる可能性があります。障害のタイミングによってログを即時に回収できない場合もあります。溜まっていたログを転送するために、その間 Instance を起動し続けるのは非効率ですし、AWS Spot Instance や GCP Preemptible Instance などは Instance は短時間で削除されてしまうので、Volume の Life cycle と Instance 分けるのは一つの案になります。

そのため、Instance を撤去するときは Instance はそのまま削除しますが、ログがある Volume はまだ消さないようにします。我々のログ回収システムは、使い済みの Instance の Volume を mount して、中身を S3 や Google Cloud Storage に転送しています。ちなみに、転送したログは、AWS Athena と Google BigQuery を活用して、検索することも可能です。

まとめ

今回は Auto Scaling の観点から、私のグループの Infra System の構成を大ざっぱに紹介しました。構成はそんなに複雑ではないですが、様々な問題に対処するため、工夫を凝らしています。そして、紹介したシステムは一気に完成したのではなく、最初は簡単なスクリプトから始まり徐々に進化してきました。現在でもまだ進化中です。もし詳細にご興味をお持ちの方やシステムをより進化させるために一緒に取り組んでくださる方がいらっしゃれば、ぜひ DeNA に join してください。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Auroraの高速フェイルオーバーと無停止での切り替え

こんにちは、IT基盤部の川原﨑です。

私の所属する第四グループでは、超大規模ゲームタイトルおよびゲームプラットフォームのインフラを運用しております。 そこでのAuroraの高速フェイルオーバーの仕組みと、実際に無停止で切り替えを行った手順について紹介させていただきます。

はじめに

第四グループでは、コストコントロールの一環でInstance数の増減・Instance Typeの変更を頻繁に実施しています。
例えば、

  • イベントなどでリクエスト増加が見込まれるときにInstance数を増やす、またはInstance Typeを1つ上のものに変更する
  • リクエストが減少傾向にあれば、Instance数を減らす、またはInstance Typeを1つ下のものに変更する

などです。
これはWebサーバだけにとどまらず、DBサーバについても同様です。

EC2上でMySQLを運用している環境では、フェイルオーバーの仕組みとしてMHA for MySQLを使用しております。
MHA for MySQLでは数秒でフェイルオーバーが完了するため、ピークタイムを避けた時間帯であればエラー率も無視できるレベルです。 しかし、Aurora導入後はドキュメントに記載されている通り、フェイルオーバーに1分ほど要してしまうことが見込まれるため、フェイルオーバーの品質が大幅に下がってしまう懸念があります。

ダウンタイムの検証

まずは実際にどれぐらいのダウンタイムが発生するのかを確認しました。

検証環境

db.r4.large Multi-AZの3台構成
Auroraバージョン 5.6.10a

事前準備として検証で使用するテーブルを作成しておきます。

 CERATE DATABASE aurora_test;
 CREATE TABLE test (
     col int(10) DEFAULT NULL
 ) ENGINE=InnoDB;
 INSERT INTO test (col) VALUES (unix_timestamp());
確認コマンド
 while sleep 1
 do
     date && echo "update test set col=unix_timestamp();"  | mysql -uroot -p<password> -h<Cluster Endpoint> -N --connect-timeout=1 aurora_test
 done

 

 while sleep 1
 do
     date && echo "select col from test;"  | mysql -uroot -p<password> -h<Reader Endpoint> -N --connect-timeout=1 aurora_test
 done
検証結果

手動フェイルオーバーによるダウンタイム秒数

role1回目2回目3回目4回目5回目
writer28s11s17s14s24s
reader27s21s13s21s12s

1分までとはいかないまでも平均して20秒程度かかっています。

writer側では接続エラーが収まった後に以下のエラーがしばらく継続し、完全に切り替わるようです。

 ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement

reader側では接続できるときとできないときが上記秒数の間に発生するという状況が確認できました。 これはCluster Endpoint/Reader Endpointの更新までにタイムラグがあることが推測されます。

次にReaderであるInstanceを減らす際のダウンタイムについても計測してみましたが、Reader Endpointに対しての接続エラーは計測 できませんでした。 Instanceの削除には時間がかかり、Statusがdeletingの状態でもしばらく接続ができる状態であるため、接続ができなくなる前にDNSへの変更が完了するからかもしれません。

MHA for MySQLと比較するとフェイルオーバー時のダウンタイムは見劣りしてしまうため、本番サービスにAuroraを導入するにあたり ダウンタイムを短くすることが課題とわかりました。

高速フェイルオーバーの仕組み

Auroraの高速フェイルオーバーの仕組みとして

  • MariaDB Connector/J
  • ProxySQL
  • HAProxy

などが知られていますが、 私たちのチームではこれから紹介させていただく仕組みで高速フェイルオーバーを実現させています。

DeNAでは、ローカルのDNSとしてMyDNSを使用したDNSラウンドロビンの仕組みがあります。 この仕組みでは応答しないサーバを検知してMyDNSのレコードを消して自動でサービスアウトする、アプリケーションはMySQLを直接参照することでDNSラウンドロビンのデメリットである近いIPアドレスに集中しないよう分散させています(書籍『Mobageを支える技術 』参照)。

AuroraもEC2インスタンスと同様にMyDNSに登録しています。 それにより以下のメリットがあります。

  • AuroraのEndpointを使用しないのでDNSへの更新に関するタイムラグがない
  • アプリケーション側は既存の仕組みのままでいい
  • Instance Typeの変更・Instanceの再起動時にはMyDNSからレコードを削除すればよい

ただ、既存の検知の仕組みではサービスアウトさせるということしかできないため、Aurora用に別途検知の仕組みが動いております。

check-aurora.png

  • failoverが実行された際にinnodbreadonlyが0のInstanceでwriterのレコードをREPLACEする
  • readerが応答しない際にweightを0にする
  • すべてのreaderが応答しない際にreaderのレコードにあるwriterのweightを100にする

以下は、failover実行時の時系列での状態です。
aurora-test-w が書き込み用、aurora-test-r が読み込み用のMyDNSに登録されているEndpoint名です。

通常時

endpointinstanceinnodb_read_onlyweight
aurora-test-waurora-test-instance-010100
aurora-test-raurora-test-instance-021100
aurora-test-raurora-test-instance-031100
aurora-test-raurora-test-instance-0100

failover時のWriter候補再起動時

nameinstanceinnodb_read_onlyweight
aurora-test-waurora-test-instance-010100
aurora-test-raurora-test-instance-021100
aurora-test-raurora-test-instance-0310
aurora-test-raurora-test-instance-0100

Writer切り替え後

nameinstanceinnodb_read_onlyweight
aurora-test-waurora-test-instance-030100
aurora-test-raurora-test-instance-021100
aurora-test-raurora-test-instance-0300
aurora-test-raurora-test-instance-0110

旧Writer復帰時

nameinstanceinnodb_read_onlyweight
aurora-test-waurora-test-instance-030100
aurora-test-raurora-test-instance-021100
aurora-test-raurora-test-instance-0300
aurora-test-raurora-test-instance-011100

以下、高速フェイルオーバー導入後のダウンタイムの計測結果です。

手動フェイルオーバーによるダウンタイム秒数

role1回目2回目3回目4回目5回目
writer5s7s4s6s7s
reader6s1s4s7s5s

MHA for MySQLまでとはいかないまでも、ダウンタイムはInstanceが再起動の時だけに限定されるため、かなり早くなりました。

無停止でのAuroraへ切り替え

MySQLからAuroraへ切り替えはメンテナンスを設けずに無停止で以下の手順で実施しました。

まずはMySQLのReplication SlaveとしてAuroraクラスタを構築します。 もし問題があった場合にMySQLに切り戻しができるよう、Auroraのbinlogを有効にしておきます。

migration.png

  1. ttlを1秒にする
  2. MyDNSのslaveの向き先をAuroraのreaderに向ける
  3. MySQL側で書き込み権限があるユーザをRenameし、書き込みを止める
  4. Aurora側に上記がReplicationされてしまっているのでAurora側でユーザ名を戻す
  5. MySQLとAurora間のReplicationを止める
  6. SHOW MASTER STATUSでMaster Positionを確認する
  7. MyDNSのmasterの向き先をAuroraのwriterに向ける
  8. ttlをもとに戻す
  9. 上記7で確認したMaster PositionをもとにMySQLをAuroraのReplication Slaveと設定する

MySQLに戻す場合は上記の手順をMySQLに置き換えて再度実行することになります(実際に切り戻すことはありませんでしたが)。 この状態でしばらく様子を見て、問題なければMySQLを撤去します。

上記の手順のうちエラーが発生するのは3~7の間だけです。実際の切り替え時は手順の1~8までをスクリプト化しており、failoverとほぼ同等レベルのダウンタイムで切り替えることができました。

最後に

以上、Auroraの高速フェイルオーバーの仕組みと無停止による切り替えについて紹介させていただきました。 Auroraに切り替えることで、深夜問わず発生するEC2インスタンスのダウンなどによるDBサーバの再構築という工数が削減できており、インフラエンジニアに優しい運用になりました。
MyDNSの利用による運用はDeNAに特化したことであまり参考にならないかもしれませんが、 Aurora導入の参考になれば幸いです。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

メールを送信する話

こんにちは、IT基盤部の中村です。 主に社内システムのインフラを担当しています。 現在の業務内容とは少しずれてしまいますが、最近までメール系のインフラには深く携わっていたこともあり、今回はメールシステムについてお話します。

今どきSMTPなどレガシーな話かもしれませんね。しかしながら良くも悪くも枯れたSMTPはインターネット基盤の根底に位置する息の長い技術でもあります。 きっとみんなまだ使ってるはずなのに、あまりノウハウが出回っていないのが辛いと感じているそこの担当者の方、よろしければ少しの間お付き合いください。

サービスでのメールの利用用途

我々がサービスとしてメールを取り扱うとき、その主だった利用用途は下記の2つです。

  1. メールマガジンなどの情報発信
  2. 入会・ユーザ登録時などのユーザ存在確認

メールマガジンはユーザーに情報を新たに届けたり、あらたに弊社の別のサービスに触れて頂く機会を提供するために送信されています。 こちらは単位時間あたりの送信量も送信時間もコントロールが可能ですし、宛先不明メールなどの送信不能メールを予めクリーニングを実施した上で送信できます。

逆に入会系のメールは、とあるゲームやサービスがリリースした直後に爆発的に急増します。こちらは時間帯のコントロールができない上に、流量についてもある程度の予測をしたところでそれを上回ってくる事が多く、さらにユーザ登録時のメールアドレス間違いなどで一定数の宛先不明メールも発生します。

graph.png

これらのメールを確実に(できるだけ)早く先方に届けることがシステム上の命題となります。

特に「入会・ユーザ登録」は大事な顧客獲得のチャンスでもあり、インフラの問題でメールが届かないなんてことは可能な限り避けなければなりません。

今回は、世界に向けて発信したサービスの導入時に発生した、ユーザ登録のメールを相手に届けるにあたって検討・設定したことについてお話したいと思います。

クラウド化に向けて

閑話休題ですが、これらの仕組みをクラウドに持っていくにあたってはAWSのSESなどのマネージドサービスを検討しました。 残念ながら、既存のメール配信のマネージドサービスは、突発的に大量にメールを送信する可能性がある弊社の環境とは相性が良くありませんでした。 逆に、大量のメールを定期的に送信するような環境であれば積極的に利用すべきです。

我々の場合は、クラウド上に自前でメールサーバを構築し、運用するという選択をしました。

メールを送信するということ

メールを送信する。ということは当然受信する相手がいるわけです。 いくらこちらからメールを送り付けたところで、受信者側の対応如何でこちらからのメールは一方的に破棄されてしまいます。 受信者側の対策は世の中には公開されていません。どうすれば100%相手に到達するかといったノウハウは受信側からは提供されません。

受信側はおそらく送り手側が怪しいメールの送信者でないかという観点で受信したメールを受け入れるべきか判断しているはずです。 送信元としてはそれが正しく自分たちの送出したメールであり、さらに迷惑メールを送信するようなサーバではないことを証明し続けるほかありません。

送信ドメイン認証

自らの出自を明らかにするための方法として送信ドメイン認証という考え方があります。 細かい説明はもっと詳しいサイトがあるのでそちらに譲りますが、要するに送信者のアドレスが正規なものであることを証明する技術です。 SPF/DKIM/DMARCなどがあります。 設定の優先度はSPF>DMARC>DKIMの順でしょうか(DMARCはDKIMの設定を実施していなくても設定できます)。

サービス開始当初はSPFとDKIMのみ設定していましたが、一部のフリーメールでは、どちらも正しく設定されているのにスパム判定されてしまいました。 DMARCを投入することで問題なく受け入れてもらえるようになりました(DMARCの投入にはそれなりに面倒がありましたが、それはまたの機会に)。

レピュテーションへの対策

レピュテーションは送信元メールサーバのIPアドレスの「評判」です。行儀の悪いメールサーバの評判は悪くなり、逆に正しく振る舞うほどに評価は上昇します。 評価が悪くなっていく状況を我々はよくIPアドレスが「汚れる」と言っています。 IPアドレスが汚れれば汚れるほど、そこからやってくるメールは、受信側に受け取ってもらえない可能性が高まります。

評判を公表しているサイト

いくつかのベンダーがIPアドレスのレピュテーションを公開してくれています。これが全てではありませんが、我々は以下のようなサイトを参考にしています。 AWSでEIPをとったタイミングでこれらのサイトで検索してみると、近い過去にメールサーバとして使われていたかどうかなどがわかる場合もあります。

DNSBLの監視

最も端的にIPアドレスが汚れているか判断できるのはDNSBLだと思います。 いわゆるブラックリストです。これに登録されてしまうと、即座にブロックが始まるので、できれば迅速に対応したいところです。 どれだけ手厚くみていても、たった一通のバウンスメールで登録されてしまうこともあります。油断は禁物です。

複数のブラックリスト検索には一覧検索に優れたサイトを使います。現状を知るには良い方法です。

影響度が大きいと思われるブラックリストは個別に監視して、登録され次第リストからの消去などのアクションを起こすようにします。 他にもいくつか見ていますが、主に下記あたりは注視しておいたほうがよいです。

それでも問題は起きる

とまあこれだけ対策しても、大量にメールを送信すると問題は発生します。

メール送信が始まってものの10分ほどでとあるドメイン宛のメールがすべて遅延するようになりました。

おそらく単位時間あたりの流量や、それまでのメール送信元サーバのIPアドレスからのメール到達の実績との差異などがトリガーとなっているのだと思われます。 普段はほとんどメール送信がないメールサーバからいきなり大量にメールが送信されると、その挙動そのものが怪しげなものに見えるようです(先述の通り、こちらからするとこれはこれで正しい挙動なのですが)。 一定の期間後メールは受信してもらえるのですが、この遅延は入会へのモチベーションに対しては致命的です。

暖気

確証は有りませんでしたが、通常時のメール流量が殆どないメールサーバーからの送信が怪しいのであれば、最初から少しは流しておけばいいと考えました。 特に流量による制御を実施していると思われるフリーメールに対しては、だいたいサービスリリースの2週間くらい前からメールを定期的に送信しておくようにしました。 我々はメールサーバの暖気と呼んでます。 実際こちらを実施することにより、長期間のメールのブロックはかなり減りました。

IPアドレスは多めに用意

それでも受信側のメールサーバの挙動は分からないことが多いです(特に海外のドメイン)。 メールログからなにか規則性を探ろうとしましたがほとんどが徒労に終わりました。

最後は力技ですが、仕方がありません。

メールキューの監視を行いつつ数十個のIPアドレスを用意して問題があれば利用を停止するようにしました。潤沢なグローバルIPアドレスをあてにするこの手法はパブリッククラウドならではの手法でありました。

まとめ

大量にメールを送りたいなら

  • 送信ドメイン認証は手を抜かず確実に設定しましょう
  • レピュテーションには常に気を配りましょう
  • 暖気も時には有効
  • それでもだめなら力技もあり

最後に

いかがでしたでしょう。 実際、大量のメール送信というユースケースはあまりないかもしれません。 当時は調べても有用な情報が出てこず頭を抱えたものでした。 詳細を省略している部分も多いですが、おおよそ我々がとった対応については記載しました。同じようなことで悩んでいるどなたかの参考になれば幸いです。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

アプリログを BigQuery に入れるまで

こんにちは、IT基盤部の貴田です。

DeNA では分析環境の BigQuery 移行を進めています。
先の記事では、移行の背景や、 MySQL のデータを Embulk を用いて BigQuery に入れる工程を紹介しました。
今回は、ウェブアプリケーションが出力したログを定常的に BigQuery に入れて活用するフローについて書きます。

文中の料金については、すべて 2019年10月時点の 東京リージョンのものです。

大まかな流れ

applog2BQ.png

  1. アプリがログを吐く
  2. サーバー内の daemon がログを処理し、適切な Cloud Storage Bucket にアップロードする
  3. Cloud Storage から BigQuery に import する

というシンプルな構成ですが、設計するにあたりいくつか考慮した点があるので、順に説明します。 概略図中に、 1 ~ 4 の数字を振っている部分です。

(1) どうして直接 BigQuery にデータを送信せず、 Cloud Storage に格納するのか?

web server から bq load や ストリーミング挿入 を用いて直接 BigQuery にデータを集めることも検討しましたが、コストと安定性を考え、まずは Cloud Storage にデータを格納し、必要なものを必要なタイミングでだけ BigQuery にエクスポートする構成としました。

コスト

1GB・1ヶ月あたりの料金はこのようになっており、 めったに使わないデータは Coldline Storage に置くことでストレージコストを下げられます。

サービス料金
Cloud Storage (Standard Storage)$0.023
Cloud Storage (Coldline Storage)$0.006
BigQuery$0.023

また、 BigQuery のストレージ料金は非圧縮の状態のデータサイズが課金対象となります。
Cloud Storage 上に gzip で圧縮した状態で保持することで、ストレージ料金を大きく下げることができます。

Cloud Storage から BigQuery にエクスポートする際に料金が少しかかりますが、それについては後述します。

安定性

BigQuery のテーブルは型を持っているため、何かしらのバグでログに不正な文字列が入ると、 BigQuery へのインサートは失敗します。その場合にログを web server 内部に溜めてしまうと web server のディスク領域が逼迫したり、本番稼働しているサーバーに入っての復旧作業が必要となったりするデメリットがあります。

まずはどんなデータでも受け入れてくれる Cloud Storage にデータを入れてしまい、その後起きうる問題と web server を切り離す意味でも、まずは Cloud Storage にデータを入れる構成が優位です。

(2) gcs daemon の役割

分析用のアプリログはもともと hdfs に格納することを前提に出力していたため、各行が下記フォーマットになっています。

 ${hdfs上のpath情報}\t${その他メタ情報...}\t${データ本体(JSON または LTSV)}

また、 DeNA の web app は多くの場合、分析用のアプリログを単一のファイルに書き出し続けています。

そのため、

  • hdfs上のpath情報 を Cloud Storage Bucket 名・path への変換を行う
  • ログファイルを一定時間ごと・bucket/pathごとに分割する
  • BigQuery に直接 export できるファイル形式(json.gz) に変換する

という処理を行っています。

(3) BigQuery へのデータ取り込み vs 外部データソースの活用

Cloud Storage に格納されたデータを活用する場合、2通りの方法があります。

  • データを BigQuery にエクスポートする
  • BigQuery の 外部データソース機能 を用いて Cloud Storage 上のデータを直接活用する

DeNAでは前者の BigQuery にエクスポートする方法を採用しました。

ドキュメントに記載があるとおり、後者の方法を用いると、 BigQuery の機能が一部制限されてしまいます。

なかでも、

  • テーブルのパーティショニングがサポートされない
  • クエリの実行結果がキャッシュされない

という制限は致命的で、大規模なデータを扱った場合にクエリ料金が跳ね上がる恐れがあります。

一方、前者の方法でエクスポートする場合、それ自体にほとんど料金が発生しません。
Cloud Storage (Standard Storage) と BigQuery が同リージョンにある場合、エクスポートで発生する料金は下記のもののみです。

  • Cloud Storage の get api 料金 10,000ファイルあたり $0.004
  • BigQuery ストレージ料金 1ヶ月・1Gb あたり $0.023
    • 時間単位での課金なので、利用後すぐに消せば費用が非常に小さくなる

このとおり、 Cloud Storage -> BigQuery へのエクスポートは非常に安価に行うことができます。
エクスポートすれば BigQuery の機能をフルに活用できるため、大規模なデータ分析において外部データソースの機能を使うシーンはほぼ無いと思います。

(4) 型自動判定の夢...

Cloud Storage 上の JSON を BigQuery にエクスポートする際、すべてのカラムの型を明示的に指定しています。
利用者にあらかじめスプレッドシートにカラム名と型を記載してもらうことで実現しています。

BigQuery へのデータ読み込みでは、型の自動判定機能があり(CLI でいうところの --autodetect オプション)、当初はこちらの活用を考えていました。

実際、ほとんどのケースでは自動判定はうまく働きます。
しかし、極稀にエラーが起きてしまうケースがありました。
例えば、ほとんどの行は version: "3.0" と記載されており、実数値として判定されるが、新しく version: "3.0.1" がリリースされるとこれは実数に変換できないのでエラーになる、というケースです。

BigQuery には一部のカラムだけの型を指定してデータを取り込む機能がないため、利用者に全ての型をあらかじめ入力してもらう UX となってしまいました。

BigQuery は型を意識した分析用だと割り切る必要がありそうです。

まとめ

今回の設計を通して得られた知見をまとめます。

  • BigQuery にデータを直接入れるのではなく、 Cloud Storage をデータレイクとして活用することで、安価でかつ安定する
  • Web server 上で BigQuery に読み込ませられる形式に変換・圧縮して Cloud Storage に配置することで、後続の処理をシンプルにできる
  • BigQuery の外部データソース機能は使わず、 Cloud Storage から都度 export するほうがよい
  • BigQuery を使う以上、型を意識した設計にすべき。 型自動判定は万能でなく、アドホックな処理のみに利用する

最後に

hadoop を用いた分析基盤から BigQuery を用いた分析基盤に移行するにあたり、とくに迷った部分・試行錯誤の結果当初の方針を変更した点などを紹介させていただきました。
何かしらの参考になれば幸いです。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

アクセス制御を厳格に行っている環境からのs3利用

こんにちは、IT基盤部第一グループの生井です。
DeNAが提供するヘルスケア系サービスのインフラを担当しています。

ヘルスケア領域ではセンシティブ情報を扱いますので、日々高レベルなセキュリティ設計・運用を行う必要があります。 今回はその一例として、アクセス制御を厳格に行っている環境からS3を利用する際に行った対応を紹介したいと思います。

はじめに

あるプロジェクトで、センシティブ情報を扱う環境から、S3の特定バケットにのみ、awscliでのデータdownload/uploadを許可したいという要件がありました。
  補足:特定バケットに限定するのは出口対策のためです。任意のS3バケットへのアクセスを許可してしまうと、内部犯行によるデータの持ち出しや、マルウェア感染によるデータ漏洩のリスクが高まります。

対応として、この環境で実績のある、FWでのFQDNベースでのアクセス制御を行うことにしました。 S3へのAPIアクセスは、以下2つの形式がありますが、

  • パス形式:s3-<region>.amazonaws.com/<bucket>
  • 仮想ホスト形式:<bucket>.s3-<region>.amazonaws.com

バケットの命名規則に従ってバケットを作成していれば、 awscliはデフォルトで仮想ホスト形式<bucket>.s3-<region>.amazonaws.comのアドレスでS3にアクセスします。
したがって、仮想ホスト形式のFQDNに一致する場合のみ、FWで許可するようにしました。

ところが、S3の場合には、FQDNは一致しているのにもかかわらず、通信が拒否されてしまうケースが発生し期待通りにアクセス制御ができないことがわかりました。
FWのFQDNベースでのアクセス制御は、FQDNに対するDNS問い合わせ結果をFWがキャッシュとして保持し、クライアントが名前解決を行い接続するIPと、FWのDNSキャッシュ情報にあるIPが一致する場合にアクセスを許可する、という動作でしたが、 S3のようにTTLが短い接続先の場合は、IPが一致しないケースが発生してしまうことが原因でした。
また、ベンダーに問い合わせた結果、この環境におけるFWの仕様として、FWのDNSキャッシュ期間をコントロールできないことがわかりました。

そこで、FWでの制御をあきらめ、代替策としてproxyサーバを用意して対応することにしました。
この環境の構成について次項に書きます。

構成

1g.aws.proxy.blog.png

上図は、システム構成の概要を示した図です。AWS環境にproxyサーバを建てて、Site-to-Site VPNで接続しています。通信品質の優れるDirect Connectを選択する案もありましたが、この環境の使用方法では妥協ができること、及び、コストが抑えられること、後からDirect Connectに変更することが容易であること、からSite-to-Site VPNを選択しています。
proxyインスタンスは異なるアベイラビリティゾーンに1台ずつ作成しLBを通して冗長化しています。インスタンスOSはCentOS7です。
セキュリティグループやproxy設定、IAMやバケットのポリシーでアクセス制御を行っています。許可する通信を最小限にするため、利用するAWSサービスの各種VPC endpointを作成しています。ログ保管や監視では、CloudWatchを利用しています。
以下で、もう少し具体的に構成について紹介していきます。

proxy

proxyは、ApacheやSquidがメジャーですが今回はホワイトリスト管理のしやすいSquidを採用しました。
squid.confでホワイトリストファイルを次のように指定します。

 acl whitelist-domain dstdomain "/path-to/whitelist-domain"

ホワイトリストファイル(上記例だとwhitelist-domain)には、アクセス許可したいバケットのFQDNを列挙します。

  <bucket1>.s3-<region>.amazonaws.com
  <bucket2>.s3-<region>.amazonaws.com
  ・・・
S3へのアクセス制御

VPC内からのS3バケットアクセスのみを許可するため、S3用のVPC endpoint を作成して、 IAMユーザに割り当てるポリシーのCondition要素で、接続元のVPC endpoint idが一致する場合のみアクセス許可するように設定しています。VPC endpointとS3は同一リージョンである必要があります。

       "Condition": {
         "StringEquals": {
           "aws:SourceVpce": "<vpce-id>"
         }
       }
ログの保管
infra.awslog.blog.png

ログ保管のため、Squidログやsecureログなどのセキュリティ系ログをインスタンスから CloudWatchへ送っています。ログを送るためインスタンスでawslogsを稼働させています。
CloudWatchに恒久的にログを保管すると、費用が高くつきますので、古いログはさらにS3に退避するようにしています。このためログ保管専用のバケットを用意しています。
S3へのログ退避は、ログをS3にexportする処理を行うLambda関数を作成して、 CloudWatch Eventsをトリガーとして、dailyで定期実行するようにしています。
またS3のアクセスログ記録も有効にして、log保管用の専用バケットにログを保管するようにしています。

改竄防止

セキュリティ系ログを改竄されないように、バケットポリシーで権限を最小限に絞り、ログの削除・変更をできないようにしています。 以下はバケットポリシーに追加する設定の例です。ログを保管するバケットではバージョニングを有効にしています。

 {
     "Version": "2012-10-17",
     "Statement": [
         {
             "Sid": "<description>",
             "Effect": "Deny",
             "Principal": "*",
             "NotAction": [
                 "s3:List*",
                 "s3:Get*",
                 "s3:AbortMultipartUpload",
                 "s3:PutObject",
                 "s3:PutObjectTagging",
                 "s3:PutBucketTagging",
                 "s3:PutMetricsConfiguration",
                 "s3:DeleteObjectTagging",
                 "s3:DeleteObjectVersionTagging",
                 "s3:ListBucket"
             ],
             "Resource": [
                 "arn:aws:s3:::<bucket>",
                 "arn:aws:s3:::<bucket>/*"
             ]
         }
     ]
 }

rootアカウントでは、バケットポリシーの変更が可能ですが、弊社にはAWSアカウントの自動監査 の仕組みがあり、この仕組みによりrootアカウントログインがあった場合は通知により気づくことができます。

インスタンスへのログイン

許可する通信を最小限にしたかったので、sshのinbound許可の必要がないSSM セッションマネージャを利用しました。
注意点としまして、WebSocket に対応していないproxyを経由してAWS マネジメントコンソールにアクセスしている場合は、SSM セッションマネージャでのコンソール操作ができません(私の使用しているクライアントPCでは動作確認用にinstallしていたツールがWebSocket に対応しておらず、この問題に躓きサポートに頼りました)。
SSM セッションマネージャの利用のためscreenがinstallされていない場合はinstallしておく必要があります。また、amazon-ssm-agentをインスタンスで稼働させる必要があります。このために必要な作業は一時的にsshアクセスを許可して行いました。
SSM セッションマネージャの設定は、AWS マネジメントコンソールでは[セッションマネージャ]の[設定タブ」から行います。
証跡ログを残すため、セッションログをS3に暗号化して保管するようにしています。このログを保管するS3バケット側でも暗号化が有効になっている必要があります。
また、アクティブなセッションデータをKMSで暗号化するようにしています。
amazon-ssm-agentに必要な権限はインスタンスのIAMロールで与えています。デフォルトで用意されているポリシーAmazonEC2RoleForSSMは権限が強い(リソース制限なしのs3:GetObject, s3:PutObjectがある)ので、 必要な権限だけ与えるようにカスタマイズしたポリシーを作成して権限を付けました。 最低限必要な権限は、公式ドキュメントの最小限の Session Manager アクセス権限 と、SSM エージェント の最小 S3 バケットアクセス許可を参照しています。
ログを保管するバケットに対しては、s3:PutObjectの権限が必要で、上記のようにセッションログを暗号化して保管する場合には、ログを保管するバケットに対して、s3:GetEncryptionConfigurationの権限も必要となります。

通信制御

セキュリティグループで許可する通信を最小限にするため、以下のVPC endpointを作成しています(既出のS3 endpointも以下に含めています)。
S3のみgateway type、他はinterface typeのendpointです。

  • com.amazonaws.<region>.s3
  • com.amazonaws.<region>.ssm
  • com.amazonaws.<region>.ec2messages
  • com.amazonaws.<region>.ec2
  • com.amazonaws.<region>.ssmmessages
  • com.amazonaws.<region>.kms
  • com.amazonaws.<region>.monitoring

ssm,ec2messages,ec2,ssmmessagesのendpointはSSM セッションマネージャの利用に必要な通信のため作成しています(詳しくは公式ドキュメントを参照下さい)。 SSM セッションマネージャでセッションデータの暗号化をする場合には、KMS endpoint作成も必要です。monitoring endpointはCloudWatch用です。
仕上げとして必要な通信だけ許可するようにセキュリティグループで設定します。 VPC内のipと、vpn経由の通信以外は、inboundに必要な通信はありませんのでdenyします。
outboundについては、VPC内のipと、vpn経由からの通信以外に、S3 endpointのプレフィックスリストIDに対して、80port,443portを許可します。
80portはSSM セッションマネージャでセッションログをS3に保管する場合に許可が必要です、公式ドキュメントの「Systems Manager の前提条件」に記述がありませんが、ソース の以下箇所で使用しています。S3バケットに対してHEADメソッドを使ってリージョン情報を取得する処理です。

 func getRegionFromS3URLWithExponentialBackoff(url string, httpProvider HttpProvider) (region string, err error) {
    // Sleep with exponential backoff strategy if response had unexpected error, 502, 503 or 504 http code
    // For any other failed cases, we try it without exponential back off.
    for retryCount := 1; retryCount <= 5; retryCount++ {
        resp, err := httpProvider.Head(url)
監視

基本的なリソース監視としてcpu,メモリ,ディスクの使用率をCloudWatchで監視するようにしています。一定の閾値を超えた場合CloudWatchのアラーム設定により通知が飛びます。 メモリ,ディスクの使用率のモニタリングにはインスタンスにモニタリングスクリプトを設置して定期実行する必要があります。
死活監視としてインスタンスのステータスチェックのアラームも作成しています。
また、想定しないアクセスがあった場合に通知するように、CloudWatchに送っているSquidログに対して、メトリクスフィルターを作成し、ログにSquidホワイトリストにない宛先がある場合には、アラーム設定で通知が飛ぶようにしています。

切り替え

新しく環境を用意する場合は気にする必要はありませんが、元々internet経由から接続元IPを制限してS3を利用している場合には、 接続元が特定IPの場合と、特定VPCの場合を、or条件でアクセス許可して、VPCからのアクセスができることを確認してから、特定VPCの場合のみ許可するように絞るのが進め方として安心です。
ポリシー設定で、Conditionの列挙はandで評価されるため、両方の場合を許可するのには、以下のように、特定VPCでない場合、かつ特定接続元IPでない場合をdenyするポリシーを設定します。

   "Statement": [{
     "Effect": "Deny",
     "Action": "s3:<action>",
     "Resource": [
       "arn:aws:s3:::<bucket>",
       "arn:aws:s3:::<bucket>/*"
     ],
     "Condition": {
       "StringNotLike": {
         "aws:sourceVpce": [
           "<vpce-id>"
         ]
       },
       "NotIpAddress": {
         "aws:SourceIp": [
           "<ip-range>"
         ]
       }
     }
   }]

この状態で動作確認行い、S3バケットのアクセスログから、接続元IPがプライベートIPとなっていることを確認できればokです。 最後にVPC経由のアクセスのみ許可するようにポリシーを設定してinternet経由ではアクセスできなくなったことを確認します。

おわりに

以上、proxy環境が必要になった経緯と用意したproxy環境についての紹介をさせて頂きました。
TTLが短い場合にFWでのFQDNベースでの制御が効かないケースは他にも見かけます。類似の問題に取り組んでいる方がいるかと思います、そのような方のお役に立てば幸いです。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Cisco WLC を Act-Act で運用する話

こんにちは、IT 基盤部ネットワークグループの寺増です。

前回の 無線 LAN の通信品質を見える化する話 に続き、今回はヒカリエ本社における Act - Act 構成の WLC 運用とその中での自動化手法を紹介します。内容はネットワークエンジニア、特にエンタープライズの無線 LAN を運用中の方、これから構築を行われる方、そして無線 LAN に興味をお持ちの方向きになっています。

目次

  • はじめに
  • メリット/デメリット
  • 内製 CLI について
    1. 概要
    2. 処理フロー
    3. 自動化への応用
  • 最後に

はじめに

まずは、ヒカリエ本社の無線 LAN 基盤に関するネットワーク構成を簡単に紹介します。

img01.png

主要な機器は以下の通りです。

  • 無線 LAN コントローラ (以下、WLC)

    • Cisco Catalyst 9800 2 台
  • アクセスポイント (以下、AP)

    • Cisco Aironet AP4800 127 台 (1 フロアあたり 16 - 20 台)

全 VLAN とも CAPWAP を用いた Central Switching モデルで、二台の WLC に AP を分散して帰属させた所謂 Act - Act 構成を採用しています。

この Act - Act 構成にはメリットとデメリットが存在しています。本記事ではここから紹介していきます。

補足:本記事で紹介する運用は全て Catalyst 9800 / AP4800 の前身にあたる Cisco CT Series ならびに Lightweight AP でも有効です。

メリット/デメリット

最初にメリットです。これらは実際に我々が肌で感じているポイントです。

  • 費用面 / Cost

    • HA 構成時の N+1 スタンバイ WLC に類する予備機が不要
  • 運用面 / Quality

    • 実質スタンドアローン 2 台で、AP の片寄せが容易に可能
    • 同上の理由につき、トラフィックの負荷分散が可能
    • 同上の理由につき、段階的なバージョンアップ/ダウンが可能
    • 同上の理由につき、HA 構成に関連するバグの回避が可能
    • トラフィックが常に流れているので通信区間の異常に気づき易い
    • HA 構成の運用で発生する設定変更による全断と無縁
    • WLC 障害時の切り替えが比較的高速 (最大約 10 秒程度)

いずれも運用コストや通信品質を重要視する環境では捨てがたい特性となっています。次にデメリットを見ていきます。

  • 費用面 / Cost

    • HA 構成に比べて二倍の AP ライセンスが必要
  • 運用面 / Quality

    • 複数の AP が別々の WLC に帰属するため全体を俯瞰することが困難 (*図2)
    • 端末の移動等でそのデータを持つコントローラが不規則に変動する (*図3)

    img03_2_act_act.png

    図2: メトリクス (Channel Utilization) データが分散

    img02.png

    図3: 端末の移動によってデータを持つ WLC が変動

補足:2 台の WLC 両方で同じ設定変更を実行する必要がある点はデメリットではないと考えています。これは CUI 運用においてさほど大きな手間ではなく、それよりも全断無しで設定変更作業を行えるメリットが大きいと感じているためです。

先述のうち、費用面については Cisco Smart Account の登場によって解消されました。従来は WLC が AP ライセンスを持つモデルでしたが、2019/9E 現在、最新の WLC + AP の構成では AP 自身がライセンスを持つモデルとなっています。つまり Secondary WLC 用の余分なライセンス購入は不要です。

一方の運用面については変わらずで、これらは長期的に見て大きな手間となりえます。解決策として、別途アプライアンス Prime InfrastructureDNA Center を利用する手もありますが、これらはいずれも一長一短です。「可能な限りリアルタイム全体を俯瞰 する」目的においては、どちらも最善の一手と言い辛い部分が残っていました。

DeNA では、これを補う CLI を内製し、運用に組み込むことで Act - Act 構成の環境を成立させています。次項ではこれを紹介していきます。

内製 CLI について

1. 概要

CLI は wlap (cli wrapper for WLC and APs) と命名しています。主たる責務は「WLC を意識せず AP を管理すること」です。

実装について述べる前に、この CLI の利用で改善されているポイントを 3 つほど紹介します。

(1) 判読性の向上

無線 LAN は構成要素やメトリクスが非常に多く、かつ複数台の AP で 1 つのネットワークを構成するという特徴があります。このため、単一コマンドの実行結果だけで全体を俯瞰することが困難です。そしてこれは AP が 2 台の WLC に分散して帰属している Act - Act 構成の環境では尚の事となります。

例えば、単一のカバレッジ (ヒカリエ本社では「1 フロア」が該当します) を構成する AP の設定値と主要メトリクスを網羅的に確認したい場合、通常は以下の 3 ステップが必要になります。

1. 各 WLC で帰属する AP のリストを取得

(例) show advanced 802.11a summary

 # WLC1

 (Cisco Controller) >show advanced 802.11a summary

 Member RRM Information
 AP Name  MAC Address        Slot  Admin    Oper   Channel     TxPower
 -------- ------------------ ----- -------- ------ ----------- -------------
 AP1      6c:9c:ed:eb:e0:a0   1    ENABLED  UP     (136,132)*  *1/7 (17 dBm)
 AP3      24:b6:57:5b:6f:70   1    ENABLED  UP     (136,132)*  *1/7 (17 dBm)

 # WLC2

 (Cisco Controller) >show advanced 802.11a summary

 Member RRM Information
 AP Name  MAC Address        Slot  Admin    Oper   Channel     TxPower
 -------- ------------------ ----- -------- ------ ----------- -------------
 AP4      24:b6:57:35:0d:90   1    ENABLED  UP     (36,40)*    *2/7 (14 dBm)
 AP2      24:b6:57:35:22:70   1    ENABLED  UP     124*        *2/7 (14 dBm)
  • 表示は AP の帰属順です、sort 相当の機能は提供されていないため視認性に難があります
  • Radio に関する各種ステータスは確認出来ますが、RF の主要なカウンタは確認出来ません

2. 各 WLC で取得した AP それぞれで 802.11a の情報を取得

(例) show ap auto-rf 802.11a AP1

 (Cisco Controller) >show ap auto-rf 802.11a AP1
 Number Of Slots.................................. 2
 AP Name.......................................... AP1
 MAC Address...................................... 6c:9c:ed:eb:e0:a0
   Slot ID........................................ 1
   Radio Type..................................... RADIO_TYPE_80211a
   Sub-band Type.................................. All
   Noise Information
     Noise Profile................................ PASSED
     Channel 36...................................  -94 dBm
     Channel 40...................................  -97 dBm
     Channel 44...................................  -95 dBm
     Channel 48...................................  -96 dBm
     Channel 52...................................  -96 dBm
     Channel 56...................................  -96 dBm
     Channel 60...................................  -96 dBm
     Channel 64...................................  -96 dBm
     Channel 100..................................  -96 dBm
     Channel 104..................................  -95 dBm
     Channel 108..................................  -96 dBm
     Channel 112..................................  -96 dBm
     Channel 116..................................  -96 dBm
     Channel 120..................................  -95 dBm
     Channel 124..................................  -93 dBm
     Channel 128..................................  -93 dBm
     Channel 132..................................  -96 dBm
     Channel 136..................................  -95 dBm
     Channel 140..................................  -96 dBm
   Interference Information
     Interference Profile......................... PASSED
     Channel 36................................... -128 dBm @  0 % busy
     Channel 40................................... -128 dBm @  0 % busy
     Channel 44................................... -128 dBm @  0 % busy
     Channel 48................................... -128 dBm @  0 % busy
     Channel 52................................... -128 dBm @  0 % busy
     Channel 56................................... -128 dBm @  0 % busy
     Channel 60................................... -128 dBm @  0 % busy
     Channel 64................................... -128 dBm @  0 % busy
     Channel 100.................................. -128 dBm @  0 % busy
     Channel 104.................................. -128 dBm @  0 % busy
     Channel 108.................................. -128 dBm @  0 % busy
     Channel 112.................................. -128 dBm @  0 % busy
     Channel 116.................................. -128 dBm @  0 % busy
     Channel 120.................................. -128 dBm @  0 % busy
     Channel 124.................................. -128 dBm @  0 % busy
     Channel 128.................................. -128 dBm @  0 % busy
     Channel 132.................................. -128 dBm @  0 % busy
     Channel 136.................................. -128 dBm @  0 % busy
     Channel 140.................................. -128 dBm @  0 % busy
   Rogue Histogram (20/40/80/160)
     .............................................
     Channel 36...................................  0/ 1/ 0/ 0
     Channel 40...................................  0/ 0/ 0/ 0
     Channel 44...................................  0/ 1/ 0/ 0
     Channel 48...................................  1/ 0/ 0/ 0
     Channel 52...................................  0/ 0/ 0/ 0
     Channel 56...................................  0/ 2/ 0/ 0
     Channel 60...................................  0/ 0/ 0/ 0
     Channel 64...................................  0/ 0/ 0/ 0
     Channel 100..................................  0/ 2/ 0/ 0
     Channel 104..................................  0/ 0/ 0/ 0
     Channel 108..................................  0/ 0/ 0/ 0
     Channel 112..................................  0/ 0/ 0/ 0
     Channel 116..................................  0/ 0/ 0/ 0
     Channel 120..................................  0/ 0/ 0/ 0
     Channel 124..................................  0/ 0/ 0/ 0
     Channel 128..................................  0/ 0/ 0/ 0
     Channel 132..................................  0/ 3/ 0/ 0
     Channel 136..................................  0/ 0/ 0/ 0
     Channel 140..................................  0/ 0/ 0/ 0
   Load Information
     Load Profile................................. PASSED
     Receive Utilization.......................... 0 %
     Transmit Utilization......................... 0 %
     Channel Utilization.......................... 22 %
     Attached Clients............................. 31 clients
   Coverage Information
     Coverage Profile............................. PASSED
     Failed Clients............................... 2 clients
   Client Signal Strengths
     RSSI -100 dbm................................ 0 clients
     RSSI  -92 dbm................................ 0 clients
     RSSI  -84 dbm................................ 1 clients
     RSSI  -76 dbm................................ 1 clients
     RSSI  -68 dbm................................ 0 clients
     RSSI  -60 dbm................................ 12 clients
     RSSI  -52 dbm................................ 17 clients
   Client Signal To Noise Ratios
     SNR    0 dB.................................. 0 clients
     SNR    5 dB.................................. 0 clients
     SNR   10 dB.................................. 1 clients
     SNR   15 dB.................................. 1 clients
     SNR   20 dB.................................. 0 clients
     SNR   25 dB.................................. 0 clients
     SNR   30 dB.................................. 3 clients
     SNR   35 dB.................................. 9 clients
     SNR   40 dB.................................. 8 clients
     SNR   45 dB.................................. 9 clients
   Nearby APs
     AP 00:08:30:d7:30:bf slot 1..................  -86 dBm on  44  40MHz (172.24.54.161)
     AP 6c:9c:ed:eb:c9:2f slot 1..................  -72 dBm on 128  40MHz (172.24.54.161)
     AP 7c:95:f3:74:a3:9f slot 1..................  -62 dBm on 128  40MHz (172.24.54.161)
     AP 7c:95:f3:fc:88:1f slot 1..................  -70 dBm on  36  40MHz (172.24.54.161)
   Radar Information
   Channel Assignment Information
     Current Channel Average Energy...............  -50 dBm
     Previous Channel Average Energy..............  -50 dBm
     Channel Change Count......................... 21
     Last Channel Change Time..................... Tue Sep 19 14:52:55 2019
     Recommended Best Channel..................... 128
   RF Parameter Recommendations
     Power Level.................................. 1
     RTS/CTS Threshold............................ 2347
     Fragmentation Threshold...................... 2346
     Antenna Pattern.............................. 0

   Persistent Interference Devices
   Class Type                 Channel  DC (%%)  RSSI (dBm)  Last Update Time
   -------------------------  -------  ------  ----------  ------------------------
   All third party trademarks are the property of their respective owners.
  • 表示が冗長です、egrep 相当の機能は提供されていないため判読性に難があります
  • RF の主要なカウンタは確認出来ますが、Radio に関する各種ステータスは確認出来ません

3. 取得した全 AP の情報をマージおよび整形

この作業を手動で行った場合、慣れていても 5 分 - 10 分程度の時間を要してしまいます。

このようなステップが以下のようなワンライナーで処理出来るようになっています。

 $ wlap show overview --hostname AP1 -i 11a
 This process takes about 4 seconds. Please wait...

   +----------+-------------------+------------+---------------+----------------+-------------+--------------------+--------------+-------------+
   | Hostname | MAC               | OperStatus | ChannelNumber | TxPower        | ClientCount | ChannelUtilization | APGroupName  | Controller  |
   +----------+-------------------+------------+---------------+----------------+-------------+--------------------+--------------+-------------+
   | AP1      | 6c:9c:ed:eb:e0:a0 | UP         | (136,132)*    | *1/7 (17 dBm)  | 31 clients  | [#####     ] 53 %  | Group6       | WLC1        |
   | AP2      | 24:b6:57:35:22:70 | UP         | 124*          | *2/7 (14 dBm)  | 1 clients   | [          ] 9 %   | Group3       | WLC2        |
   | AP3      | 24:b6:57:5b:6f:70 | UP         | (136,132)*    | *1/7 (17 dBm)  | 39 clients  | [######    ] 63 %  | Group4       | WLC1        |
   | AP4      | 24:b6:57:35:0d:90 | UP         | (36,40)*      | *2/7 (14 dBm)  | 13 clients  | [#         ] 16 %  | Group2       | WLC2        |
   +----------+-------------------+------------+---------------+----------------+-------------+--------------------+--------------+-------------+

WLC の存在を意識していないことはもちろん、利用頻度の高いメトリクスに表示を限定した上でテーブル形式とすることで判読性を高め、かつ文字整形の手間も完全に省いた形です。本来数分かかる作業が数十秒で完了することは非常に効率的です。

img04_2.png 図4: メトリクス (Channel Utilization) データが集約
(2) 操作性の向上

単一 AP の設定変更操作を行う場合、Act - Act 構成の環境では最初にログインする WLC を選択する必要があります。しかし、運用で生じる 100 台以上の AP の帰属先変更に、人間の脳で追従することは非常に困難です。そしてこの結果、どうしても「AP1 の設定変更のため WLC1 にログインしたが、実際の帰属先は WLC2 だった」という時間のロスが発生してしまいます。

加えて対象の WLC にログインした後も、複数のコマンドを実行して初めて目的の設定変更を完了出来る場合があります。例えば、従来の CT シリーズでは、特定 AP の Channel を変更するために以下の 3 コマンドを実行する必要がありました。

 (Cisco Controller) > config 802.11a disable AP1
 (Cisco Controller) > config 802.11a channel ap AP1 48
 (Cisco Controller) > config 802.11a enable AP1

補足:Catalyst 9800 では syntax が改善され 1AP あたりワンラインで完結出来るようになっています。

この書式では Channel 番号が二行目にあたるため、100 台以上の AP に対する流し込みコマンドを作成すると設定の見通しが非常に悪くなります。また、事前の設定状態を確認するには更に別のコマンドも必要です。

このようなステップが以下のようなワンライナーで処理出来るようになっています。

 $ wlap set channel --hostname AP1 -i 11a --channel 48 --width 20
 AP1 found on WLC1.

 Current : Global (36,+1ch)
 New     : 48


 ****** CAUTION: AP will STOP PROPAGATION ******

  - Associating devices will be disassociate.

 Are you really sure? (y/n)y
 Applying...

先程と同様 WLC の存在を意識せず、事前・事後の差分と影響範囲を正しく認識した上で変更作業が実行出来る形です。対話式で影響範囲を明示しているのは「コマンド 1 つであっても必ず処理内容を確認し、影響範囲を理解した上で実行する」という我々 IT 基盤の運用文化が背景にあります。AP の設定変更は局所的な瞬断を伴うケースが非常に多いため、これにより改めてリスクに対する警戒を促しています。

(3) 自動化のサポート

無線 LAN コントローラに限らず、元来ネットワーク機器には以下のような特徴があります。

  • スイッチやルータにカテゴライズされる機器のメモリは数百 MB ~ 数 GB 程度
  • niceionice のようにプロセスの優先度を制御するコマンドが存在しない
  • 通信において中間に位置する機器であるため、有事の際の影響範囲が非常に広い

これらの仕様から、ネットワーク機器における無闇なメモリの使用は控えたほうが良い、というのが通説です。対して、サーバサイドで実行速度を制御出来る CLI を使うことで、ここにある程度の融通が利かせられるようになります。

補足:この通説には近い将来動きがあると予測しています。これはコンテナ技術の普及によって、ユーザに提供するメモリを厳格に制限出来るようになったことが背景です。

加えて、サーバ起点でオペレーションが行えるということは、他サーバとの連携も可能であるということを意味します。実際に我々は監視サーバと連携して一定数のオペレーションを自動化しています、後述でその一部を紹介します。

img05.png 図5: 監視と連動して設定変更を行うモデル

2. 処理フロー

CLI の処理フローは至って単純です。以下の 3 ステップで完結しています。

  1. AP の帰属する WLC を特定
  2. その WLC に対して所定の CLI のコマンドを発行 (TTY)
  3. コマンドの実行結果をパースして必要な情報を表示

1 つだけ、Act - Act 構成の運用に大事な事前準備を要するのでまずはこちらを紹介します。

事前準備

Act - Act 構成の環境では AP の帰属先 WLC が運用によって変動します。これに順応するため、前もって WLC の SNMP Agent が公開する情報から AP のリストを生成しています。

この処理は以下のワンライナーで実現しています。

 $ wlap init wlcapmap -C <SNMP Community Name>

情報収集の対象となる WLC は予め YAML で定義しています。

 ---
 production:
   controllers:
     - WLC1
     - WLC2

SNMP Agent のアクセス先は AIRESPACE-WIRELESS-MIB に含まれる OID: 1.3.6.1.4.1.14179.2.2.1.1.3 (bsnAPName) です。

サンプルの値を snmpwalk で取得してみます。バージョン、コミュニティ名等の引数は $SNMP_OPTIONS でまとめて渡しています。

 $ snmpwalk $SNMP_OPTIONS WLC1 1.3.6.1.4.1.14179.2.2.1.1.3
 SNMPv2-SMI::enterprises.14179.2.2.1.1.3.0.167.66.226.0.32 = STRING: "AP1"
 SNMPv2-SMI::enterprises.14179.2.2.1.1.3.92.90.199.97.218.128 = STRING: "AP3"

 $ snmpwalk $SNMP_OPTIONS WLC2 1.3.6.1.4.1.14179.2.2.1.1.3
 SNMPv2-SMI::enterprises.14179.2.2.1.1.3.92.90.199.75.186.160 = STRING: "AP2"
 SNMPv2-SMI::enterprises.14179.2.2.1.1.3.92.90.199.129.183.224 = STRING: "AP4"

<AP MAC を 10 進数に変換した値> (以下、AP OID) とともに、value として AP のホスト名が取得出来ました。 AIRESPACE-WIRELESS-MIB は、このように <OID>.<AP OID> の配下で値を持つことが特徴です。

ここで取得した AP OID とそのホスト名および帰属先コントローラ情報を、以下のような YAML として保存しています。

 ---
 - host: WLC1
   name: AP1
   apoid: 0.167.66.226.0.32
 - host: WLC1
   name: AP3
   apoid: 92.90.199.97.218.128
 - host: WLC2
   name: AP2
   apoid: 92.90.199.149.23.64
 <snip>

このマップファイルをオペレーションの起点に照会することで、帰属先 WLC を意識しない AP 運用が可能になっています。

続いて、CLI 実行時の処理を見ていきます。

1. AP の帰属する WLC を特定

実際にオペレーションを行う CLI の基本書式は以下のようになっています。

 $ wlap <動詞 (init|show|set|exec)> <目的語 (channel|txpower|overview|...)> --hostname <AP ホスト名>

何かしらのコマンドが実行されると、まずは --hostname を元に先のマップで帰属先 WLC を逆引きします。

2. WLC に対して所定のコマンドを発行

次に、逆引きで特定した帰属先 WLC に対して TTY 経経由で所定のコマンドを実行し、その結果を取得します。

現在稼働している多くのネットワーク機器は、未だ十分なプログラマブルインタフェースを持ちません。このため、情報の取得には TTY + expect を採用しています。例えば、先に紹介した show advanced 802.11a summary の取得は以下のような関数で処理しています。

 def show_adv_a_sum(controller, username, password)
   commands = [
     { read: 'Password:',               input: password },
     { read: '\(Cisco Controller\) \>', input: 'config paging disable' },
     { read: '\(Cisco Controller\) \>', input: 'show advanced 802.11a summary' }
   ]
   { management: "ssh #{username}@#{controller}", operation: commands }
 end

この実装には、以下のようなメリットも内包しています。

  • Infrastructure as Code

    ChefAnsible 等と同じく、定義がそのまま操作手順になります。これにより一部の手順書作成は不要になります。

  • 学習コストの低減

    CLI のコマンドだけ覚えておけば OS 別にコマンドを学習する必要が無くなります。これは他社製品への乗り換えはもちろん、Cisco 製品における AireOS から IOS-XE への乗り換えでも効果を発揮しています。

3. コマンドの結果をパースして必要な情報を表示

最後に、取得した結果から必要な情報を抽出、フォーマットを調整しつつ標準出力して処理完了です。

show 系コマンドの処理は以上となります。setexec 系コマンドの場合は、再度 (2) / (3) を繰り返して変更/実行までを実施しています。

3. 自動化への応用

先に紹介している set channel は対話式でしたが、この set 系コマンドにはそれをスキップする --quiet オプションを実装しています。そして、これを監視と連携させることで障害からの復旧をある程度自動化しています。

監視に関する詳細は説明を省きますが、例えば以下のようなケースがこの対象となっています。

1. レーダーによる停波状態からの自動回復

ヒカリエ本社のある渋谷は、西から W53、南東から W56 のレーダーが多く飛来する RF 環境です。AP はこのレーダーを受信すると法規的な仕様に基づき無線の電波を停波し、利用可能な Channel が無ければ最短 30 分間はそのままの状態となります。

img06.png 図6: レーダーによって電波が停波

これに対する一般的な対策には以下のようなものが挙げられますが、いずれも一長一短です。

  • カバレッジにローミング用の W52 AP を混ぜ込む手法

    レーダー検知時にある程度のカバレッジロスを許容することになります。レーダーの受信頻度が低い、または安定したスループットが不要な RF 環境では最適解と考えています。

  • RF Profile の Channel List に W52 Channel を混ぜ込む手法

    DCA 任せになるので、レーダーを受信していない状況下でも混雑の激しい W52 Channel を選定する場合があります。W52 が比較的クリーンな RF 環境では有効な手法と考えています。

DeNA では、通信品質の観点からこれらを採用することが困難でした。このため、別途 Radio ダウン時、周囲の AP と干渉しない W52 Channel に固定化する仮復旧処理W53/W56 の Blacklisting 開放後、Channel の固定化を解除する切り戻し処理 を自動で実行しています。

img07.png 図7: レーダーによって停波した AP #2 の電波伝搬を再開

Hook のトリガーとしている監視は、切り替え時が OID: 1.3.6.1.4.1.14179.2.2.2.1.12 (bsnAPIfOperStatus)、 切り戻り時は OID: 1.3.6.1.4.1.14179.2.2.24.1.1 (bsnAPIfRadarDetectedChannelNumber) が返す合計 Channel 数の閾値監視です。

2. クライアント超過状態からの自動回復

無線 LAN の CSMA/CA は半二重通信です。クライアントが増えれば増えるほど通信待ち時間、つまり遅延の発生する可能性が高くなります。

img08.png 図8: AP #2 のクライアントが超過

これを未然に防ぐため、クライアント接続数が一定数を超えた AP に対して、特定 ESSID に接続する端末を deauth し、再接続を促すことでクライアント数を平準化する処理 を自動で実行しています。

img09.png 図9: AP #2 のクライアントを分散

Hook のトリガーとしている監視は、OID: 1.3.6.1.4.1.14179.2.2.2.1.15 (bsnApIfNoOfUsers) の閾値監視です。

最後に

以上、ヒカリエ本社を例に Cisco WLC の Act - Act 構成運用手法と関連する自動化を紹介させて頂きました。

無線 LAN はまだまだ発展途上の技術です。2020 年に予定されている 802.11ax の標準化もさることながら、その後も更に変化が続いていくものと予想されています。そして、この需要増とともに品質に対する要求が増加することもまた必然です。我々の持つナレッジが、少しでもより良い無線 LAN 構築、より良い運用環境整備の参考になれば嬉しく思います。

最後までお読み頂きありがとうございました。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

社内システムのクラウド移行

こんにちは、IT基盤部の田村です。
社内システムのインフラを担当しています。

我々が管理・運用しているシステムは、先月、エンジニアブログでジュンヤさんの記事にもあった通り、Windows、Linux、アプライアンスなど様々なOS、ミドルウェア、ハードウェアを使用していますので、クラウドへ移行するのも同様に様々なパターンがあります。そのため、オンプレミスと同じような構成で移行できるのか、はたまた、AWS上のサービスを利用出来るのか、模索しながら移行を進めています。

現在、社内システムは、本番環境と開発環境、合わせて300台ほどあります。これらを2019年度末までにクラウドへ移行する予定です。

migration_systems.png ※2019年9月現在の進捗状況

Windowsのシステムを先行して移行していますが、その中から、アプリケーション(WindowsServer2012R2)+コンソールPC(Windows7)のシステム構成をどのように移行したか書きたいと思います。

オンプレミスの構成と利用方法

本番セグメントのサーバーにログインするには、セキュリティー上、必ずゲートウェイサーバーにリモートデスクトップで接続し、そこから本番サーバーに接続するルールになっています。ゲートウェイサーバーでは、ユーザーが何をやったか後から追えるよう、証跡を取っています。
このアプリの担当者は、ゲートウェイを経由してコンソールPCに接続後、コンソールアプリを起動して業務を行います。 コンソールPCはWindows7なので、1台1人しかログインできません。 担当者は十数名いるので、利用時は声を掛け合って利用してます。

on-premises.png

まずはAWSにインスタンスを立ててみよう!

練習がてら、Windowsのインスタンスを立ててみます。
すると、いきなり問題発生!

Amazon Machine Image(AMI)にWindows7がない!
Windows10もない!!

これは困りました。
WindowsServer2012R2で立てるしかないのか。。。と諦めかけたとき、救世主登場!
Amazon WorkSpacesというセキュアなクラウドベースのデスクトップサービスがありました。

Amazon WorkSpacesを使ってみる!

これは使えるかも?ということで、WorkSpacesにインスタンスを立ててみます。

OSは、Windows7か10、またはAmazon Linux2から選べます。
Windows7のEOSは2020年1月14日なので、Windows10を選択します。

料金体系は、月額料金と時間料金のいずれかを選択できます。
今回構築するコンソールPCはそれほどスペックはいらないので、Windowsバンドルのバリューを選択することにします。

 OS         バンドル    月額料金    時間料金
 Windows    バリュー    34USD       10 USD/月 + 0.30 USD/時間

利用時間が月80時間を超えるようであれば月額料金がお得です。
ただ、時間料金だとアクティブではなくなってから指定された期間が経過した後に自動的に停止しますので、起動に時間がかかります。

15分ほどで構築が完了し、実際接続してみます。
自分のPC(MacBook Air)に接続アプリをインストールして直接繋ぐ???
なるほどー、こうやって使うのか!と感心してたのもつかの間、またもや問題発生しました!

  • WorkSpacesを作成したときのアカウントでしか接続出来ない。1つのWorkSpaceに対して最大20ユーザーまで登録が可能だが、ADと連携している場合、1アカウントあたり1つのWorkSpaceとなり、1アカウントで複数台に接続設定をすることが出来ない。
    つまり、担当部署全員を全てのWorkSpacesに権限を付与することが出来ません。
  • 証跡が取れないので、担当者が悪いことをしてもバレない
  • どこからでも接続出来る?

3つ目の「どこからでも接続出来る?」の問題は、WorkSpacesの管理画面にIPアクセスコントロールの設定がありましたので、オフィスのOutbound IPを設定すれば制限出来ました。 しかし、それ以外は仕様上解決出来そうにありません。

根本的に構成を見直し

接続ツールは諦め、IDCとAWSが専用線で接続されているので、IDC経由でコンソールPCに接続出来ないか検証します。
結論を先に述べさせていただくと、全ての問題を解決した理想通りの構成となります!

やりたかったことをおさらい

  • ゲートウェイ経由でアクセスし証跡を取る
  • 個人アカウントで全てのコンソールPCにログイン出来るようにする
  • 接続アプリ経由ではアクセスさせない
  • アクセス元制限をかける

事前に用意するもの

  • WorkSpacesを作成する際に必要なアカウントをActiveDirectoryのUserに必要台数分登録する。
  • AWS Direct ConnectまたはAWS VPNでIDCとクラウド間を接続する。
  • AD Connectorを設定し、ADからユーザー情報が引けるようにする。

構成図

aws.png

実際に構築していきます!

  1. Amazon WorkSpacesコンソールを開きます。
  2. WorkSpacesの起動をクリックします。
  3. 「ディレクトリの選択」
    作成済みのAD Connectorを選択します。
  4. 「ユーザーの特定」
    追加するユーザーは、事前に用意するものに書いてあるWorkSpaces作成用のアカウントです。
  5. 「バンドルの選択」
    使用したいバンドルを選択し、ボリュームサイズを指定するのですが、このユーザーは初期設定時しか使わないので、ユーザーボリュームは最小限(10GB)にしてください。ルートボリュームも後から増やせるので最小の80GBで設定します。
  6. 「WorkSpacesの設定」
    実行モード、暗号化、タグの管理は用途に合わせて設定します。
  7. 内容を確認してWorkSpacesを起動します。
  8. プロビジョンが完了するとログイン手順などが書かれたメールが届きますが、この情報は担当部署には共有しません。
  9. IPアドレスは固定にしたいので、Elastic IPを設定しておきます。
  10. このIPアドレスをDNSに登録しておくと、リモートデスクトップで接続する際便利です。
  11. WorkSpacesのIPアクセスコントロールの設定のところは、OfficeのOutboundIPを設定しておきますが、接続アプリでは接続させたくないので、リモートデスクトップで接続出来るようになったら削除します。

リモートデスクトップで接続

WorkSpacesのセキュリティグループの設定を行い、RDP接続が出来るようにします。

  1. Amazon WorkSpacesコンソールを開きます。
  2. 接続したいWorkSpaceの詳細を表示し、[WorkSpace IP] のIPアドレスをクリップボードにコピーします。
  3. Amazon EC2コンソールを開きます。
  4. ナビゲーションペインの [ネットワークインターフェイス] をクリックします。
  5. 検索ボックスに、先程のIPアドレスをペーストし対象を絞り込みます。
  6. 詳細の[セキュリティグループ]に表示されているリンクをクリックします。複数表示されたら設定したい方を名前をクリックします。
  7. [インバウンド] タブを選択して、[編集] をクリックします。
  8. [ルール追加]で下記を追加します。
    タイプ:RDP
    プロトコル:TCP
    ポート:3389
    ソース:今回はゲートウェイサーバーからのみアクセスさせたいので、そのIPアドレスを入力します。
    説明:allow rdp
  9. [保存]をクリックします。

※参考資料
AWS Knowledge center 「どうすれば、RDP を使用して WorkSpace に接続できるのでしようか?

Windows10の接続権限設定

Windowsに、WorkSpacesの接続ユーザー以外でもログイン出来るようにします。

  1. 接続アプリを使用して、WorkSpacesに接続します。
  2. コンピュータの管理 - ローカルユーザーとグループを開きます。
  3. グループ内のAdministratorsにアクセスさせたいユーザーが属するグループを設定します。

これで設定完了です。
担当部署全員が、ゲートウェイサーバーを経由して、使いたいコンソールPCにログイン出来るようになりました。証跡もバッチリ取られています。

特記事項

  • DドライブにはUserProfileに割り当てられています。この領域はWorkSpaces作成時「ユーザーの特定」で設定したユーザーのみしか使えないため、今回は利用しません。ユーザーボリュームを最小限で作成したのはそういった理由です。
  • 「ユーザーの特定」で登録していないユーザーでログインすると、C:\Users以下にプロファイルが作成されます。(通常のWindowsと同じ)
  • エクスプローラを開いたとき、Cドライブは見えないため、窓にC:\と入力して開く必要があります。
  • ルートディレクトリは定期バックアップ対象ではありませんので、基本的にローカルにデータは置かないほうがいいです。

最後に

Windowsのアプリケーション(WindowsServer2012R2)+コンソールPC(Windows10)のシステムをAWSに立てるというのは、極稀れかも知れませんが、社内ではこの構成が2種類ありますので、きっと需要はあるはずです。そのような方に参考になれば幸いです。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Hadoop環境のクラウド移行

IT基盤部の nodoka です。

私の業務はWebサービスの運用が中心でしたが、数年前からhadoopを中心とした分散基盤環境のインフラも見るようになりました。 当初は巨大なhadoop環境の管理を体系化して引き継ぐことと、運用における属人性を排除することが喫緊の課題でした。 それが落ち着くと、ご多分に漏れずクラウド化を検討・推進するようになったので、その流れをまとめてみようと思います。

  1. DeNAのHadoop環境と改善策
  2. hadoopが抱える課題
  3. GCPへの移行
  4. embulk利用におけるTips

DeNAのHadoop環境と改善策

DeNAにおけるhadoop環境の歴史は古く、DeNAのほとんどのサービスが利用しています。 各サービスでは分析したいログやDBのスナップショットをhadoopのファイルシステムであるHDFSに一旦置きます。 そのHDFSに置かれたファイル群をhadoopを代表とする様々な分析ツールを使って解析します。 時代の流れとともに分析ツールは変化していきますが、データを集積するデータレイクの役割は一貫してhadoopが担ってきました。

hadoop_env.png

データレイクを構成する数100台のdatanodeサーバたちには、cassandraやelasticsearchなどの様々なコンポーネントが相乗りしていました。 よく言えばリソースをぎりぎりまで使い切る工夫と言えましたが、高負荷なコンポーネントが無関係なコンポーネントの障害を誘発してしまう悪環境でもありました。 また、膨大な数のディスクを抱えているにも関わらず、ディスク障害が即時の手動対応を必要とすることも大きな問題の1つでした。 この現状を踏まえて、以下の改善策を進めていきました。

  • ambariによる統合管理の導入
  • hadoopバージョンアップによる不具合の改善
  • 相乗りを解消して、コンポーネントごとのサーバ棲み分け

datanodeが大きなディスクI/Oを伴うので、まずはそれと相性の悪いコンポーネントを外出ししました。 それだけでも大部分の不可解な問題は起きなくなり、障害があったとしても速やかに切り分けできるようになりました。 また、バージョンアップによってディスク周りの不具合も減り、ディスク故障についても即時の対応はいらなくなりました。

さらに構造をシンプルにするため、古いデータのパージや圧縮を行って、リソースにばらつきの目立つ古いサーバたちを処分しました。 ambariによって視覚的な構成管理ができるようになったものの、可能な限り構成や設定を単純化してバリエーションを減らしました。 数種類程度のサーバ構成に抑えることで、慣れないエンジニアたちでも罠に陥らずに運用できる体制を築きました。

hadoopが抱える課題

ようやく落ち着いたところで今後のhadoop運用をどうするか思いを巡らすと、最も大きな課題として浮かび上がったのが、HDFSというファイルシステムの存在でした。

ご存知の通り、hadoopは分散処理フレームワークの中にHDFSという独自の分散ファイルシステムを内包しています。 このHDFSが良くも悪くもhadoopの特徴となっており、その構造を巨大化・複雑化している一因とも言えます。 分散処理とHDFSが一体化しているが故に、バージョンアップ時にも幾つかの問題を抱えてしまいます。

  • バージョンアップ時に長時間ファイルシステムが停止してしまう。
  • バージョンアップ時にダウングレードによる切り戻しを行うのが困難。
  • ファイルシステムが巨大過ぎるため、バックアップが事実上不可能。

バージョンアップによる効率改善を継続したかったのですが、上記理由でカジュアルにアップグレードという訳にはいきませんでした。 仮にHDFSが長期間使えない障害が起きた場合、その業務影響は計り知れないものになってしまいます。 また、独自性が高く分析処理以外で使うことのないファイルシステムを、積極的に運用したがるエンジニアがいないことも課題でした。

以上の理由で、分散処理としてのhadoopはともかく、HDFSの運用を続けることは難しいという判断を下しました。

GCPへの移行

hadoopの移行を検討した際に、選択肢に挙がったのはAWSやGCPといったクラウド環境でした。 hadoopのマネージドサービスを使えば、分散処理とファイルシステムを分離して運用することが可能になるからです。 一方で、並行して分析基盤チームの方でも次期分析ツールの選定を進めており、既に一部で利用していて実績もあるBigQueryへの全面移行が決定しました。

これに伴って、私の方でもBigQueryの利用を前提としたGCPへの移行を検討することにしました。 BigQueryは対象データを自身のストレージに取り込む必要があるのですが、データを展開した状態で保持するので、圧縮された素のデータファイルよりも格納効率が悪くなってしまう可能性があります。弊社の実データで比較してみたところ大きな差が出てしまったので、ペタレベルに膨張したデータレイクの移行先としては選択しづらい状況でした。そこで考えたのがクラウドストレージであるGCSをデータレイクとして併用することです。

直近のデータのみをBigQueryに持って、大部分の古いデータはGCSに置くことにしました。 弊社での利用実績はないものの、BigQueryはGCSのデータも分析対象にすることも出来ることになっています。 何らかの事情でそれが難しい場合は、一時的にhadoopクラスターを組むのもありだと考えました。 BigQuery一本化に比べれば構成的には複雑になってしまいますが、それでも利用頻度やコストを鑑みれば妥当な配置という判断になりました。

データレイクをGCSへ移行する場合、サービス系DBに対して行われていたETL処理も見直す必要がありました。 現在は巨大なhadoopクラスターを利用したSqoopによってDBのスナップショットを毎日取得しています。 しばらくはDBがオンプレに残ることを考えると、クラウドでSqoopを動かすのはリソース及び権限管理、どちらにおいても悩ましい状況でした。 そこで、単体でシンプルに動作し、インプット・アウトプットの選択肢も豊富なembulkへ移行することにしました。

embulk利用におけるTips

embulkでのMySQLからのデータ抽出は、非常に使い勝手がよかったです。 環境さえ整えてしまえば、YAMLファイルを準備するだけで簡単に対象を増やすことが出来ます。 また、抽出処理で問題になりがちなフェッチ数や並列度などもプロパティで制御可能です。 今はデータソースがMySQLだけですが、その気になれば様々なデータに手を広げられるという拡張性も魅力です。

それでも幾つか使い方に悩んだ点はあり、最後におまけとしてそれらを共有させて頂きます。

制御コード問題

まず、初めにつまづいたのは制御コードの問題でした。

旧仕様に合わせてtsvファイルを生成、BigQueryに bq load してみるとエラーで失敗となりました。 該当レコードを確認してみると、エスケープされていない制御コードが含まれていることに気付きました。 tsv以外のフォーマットであればどれもエスケープしてくれたので、利便性を優先してjsonに変更することにしました。 ファイルサイズは圧縮時で1.3倍になってしまったので、ストレージコストが問題になる場合は再検討するつもりです。

文字化け問題

次に問題になったのが、マルチバイトの文字化けでした。

長く続いているサービスはエンコーディングがSJISだったりするので、これにも悩まされました。 文字化けについては、JDBCオプションで characterEncoding を適切に指定すると解消しました。 MySQLのJDBCオプションは結構便利そうなものが少なくないので、一読しておいても損はなさそうです。

具体的には以下のような設定をembulkのliquid.yamlファイルに追加しました。

 options: {characterEncoding: Windows-31J}
タイムアウト問題

そして、最も苦しんだのがタイムアウト問題でした。

今回のembulk構成は embulk-input-mysql でデータを抽出し、 embulk-output-gcs でデータをアップロードします。 embulk-input-myql は一貫性のある読み出しを行うためにトランザクションを開始してからデータの抽出を行います。 その後に embulk-output-gcs のアップロードが始まるのですが、その完了を待ってからトランザクションを閉じます。

  1. トランザクション開始
  2. MySQLからデータ抽出
  3. GCSへデータアップロード
  4. コミット

従って、データ量が大きいとアップロード処理に時間を取られてしまい、トランザクションがタイムアウトするという事態に陥ります。 とはいえ、この時点で抽出もアップロードも終わっているので、処理的には何ら問題になりません。 ただ、embulkの終了ステータスが 1 を返してしまうため、エラーハンドリングが難しくなってしまいます。

たかがタイムアウト、何らかのパラメータを変更すればすぐ直ると思っていました。 ところが、MySQLで幾つかのtimeout値を大きくしても一向に改善しません。 途方に暮れて my.cnf を眺めていると、 wait-timeout が100秒に設定されていることを発見しました。 MySQLクライアントで確認したときには8時間になっている認識だったのです。

賢明な諸兄ならお気付きかもしれませんが、インタラクティブなMySQLクライアントの wait-timeoutinteractive-timeout で上書きされるんですよね。 つまり、インタラクティブでないembulkのタイムアウトは8時間でなく、100秒で動いていたと考えられます。 MySQLクライアントでtimeout値を確認する場合は、global オプションを付けておくことを強くお勧めします。

 show global variables like '%timeout';

wait-timeout は意図的に小さくしているようなので、embulkによるDB接続をインタラクティブ扱いすることで回避しようと思います。 今回もJDBCオプションで interactiveClient を設定することで、タイムアウトを interactive-timeout の値に変更することができました。 先ほどの characterEncoding に加えて、以下のような設定をembulkのliquid.yamlファイルに追加しました。

 options: {interactiveClient: true, characterEncoding: Windows-31J}

以上です。共に日々を戦う皆様の助けになれば幸いです。

備考

記事内に出てくるOSSをまとめておきます。

  • MySQL 5.1
  • Hadoop HDP 2.6.2
  • ambari 2.5.1
  • embulk 0.9.16
続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

オンプレミスとGKEを併用したインフラについて

こんにちは、IT基盤部の宮崎です。
DeNAが提供するヘルスケア系サービスのインフラを担当しています。
今回は、クラウド移行とアーキテクチャ刷新に取り組んでいるプロジェクトがオンプレミスとGKEを併用する構成になりましたので、クラウド移行の過渡期にあたるシステムの紹介を通し構築過程で得た気づきをお伝えさせていただきます。詳細はドキュメントに頼る事が多く説明不足な所があると思いますが、何か得られるものを見つけていただければ幸いです。

概要

はじめに、アーキテクチャ刷新の目的とクラウドプラットフォーム選定の経緯を簡単に紹介します。
その後、システム概要図から、クラウド環境に焦点をあて分割していきます。
アプリケーションはGKE上で動作し、SharedVPCで払い出すネットワークを利用してオンプレと通信します。

アーキテクチャ刷新の目的

オンプレミスのシステムに以下の課題を持っていました。

  • 事業展開スピードに合わせて、プロダクト改善を進められるようにしたい
  • テストの所要時間が長い
  • デプロイにかかる時間が長い
  • コンポネントを容易に増やしたい
  • パフォーマンス面の懸念を動的に解決したい

それを解決するためにコンテナ技術を活用,マイクロサービスアーキテクチャを採用することに決定。

クラウドプラットフォーム選定の経緯

以下の要件や構想を元に、AWSGCPを候補に検討をはじめました。

  • アプリケーションレイヤーにKubernetesを利用する。
    • 懸念無し (ECS, EKS, GKE)
  • 外部連携のデータ受け渡しは専用線を利用する。

  • マネージドサービスを積極的に利用する。

    • CloudMemoryStoreがベータいつGAになるか不明
    • CloudSQLのメンテナンス時間 (CloudSQL Proxyの管理どうする)
    • 懸念なし (LB, RDB, Redis, Storage 等の利用予定のマネージドサービス)
  • BigQueryを分析で利用する。

どちらも機能は備えているが、クラウド上に我々の運用フロー(作業証跡・認証)を別途準備するのは変わらない
BigQuery利用する事が決まっているから、アカウント管理を考えるとGCPに統一した方が楽なのではないか?この事からGCPをメインで検証を進め、最終的にこのプロジェクトはGCPを利用する事に決めました。 2018年秋頃に検討したものなので、2019年9月現在は状況が変わっていると思います。

システム概要図

システムの構成要因は、以下の通りです。

  • システム環境
    • GKE環境
    • オンプレミス環境
  • 外部連携システム
    • ファイル授受
  • 外部サービス
    • 外部API連携
  • システム利用者
    • サービスユーザ
    • 社内メンバー(企画、CSメンバーなど)

これらの関係を図示すると以下のようになります。

onpre_gke_summary.png

オンプレミス環境には既存システムが存在しています。GKE環境からオンプレのSFTPサーバ・メールサーバを利用しています。オンプレ依存を少なくするため外部メールサービス利用を検討しましたが、完全移行まで費用を抑えたい事と海外事業者への第3者情報提供をしないなどの制約を満たすため、完全移行まではオンプレのメールサーバを利用する事にしました。

アプリケーションは全てGKE上で動作し、積極的にマネージドサービスを利用しています。
GKE環境を、クラウドサービスのコンポネントで置き換えると以下のような構成です。

k2_design.jpg

次に、オンプレとどのように接続しているかを説明するため、ネットワークとGCPプロジェクトについて紹介します。

ネットワークとGCPプロジェクト

GCPのネットワークリソースは、リージョン・プロジェクトを跨いで存在できるので、HostProjectで一括管理しServiceProjectにリソースを払い出す事にします。こうするとオンプレとGCP間のVPN接続を3環境(開発, ステージング, 本番)準備する必要があるところが1環境に集約出来てVPN接続を減らすことが出来ます。


VPN接続 一系統(2本)の費用 約[110 USD/月]

ネットワーク管理とログ管理の2つの視点から次の通りGCPプロジェクトを分割しています。

  • HostProject
    • ネットワークの一括管理、SharedVPCでServiceProjectへ払い出す、VPN接続・Firewallルール管理
  • ServiceProject
    • HostProjectから払い出されたネットワークを利用してGKEクラスタ上でアプリケーションを動かす
  • AuditProject
    • 監査ログを保存するプロジェクト

これらの関係を図示すると以下のようになります。ServiceProject, AuditProjectのみ環境毎に存在します。

NW_vpn_SharedVPC.jpg

SharedVPCとアドレス設計

SharedVPCでServiceProjectにネットワークを払い出すのですが、クラスタサイズを決めるためアドレス設計が必要になります。

Defaults and Limits for Range Sizes の通り、標準設定では大量のIP(Node:/20 + Pod:/14 + Service:/20)を必要とします 同様に潤沢に割り当てたいところですが、オンプレと通信するため他の環境と重複しないようにアドレス管理が必要になります。 オンプレとクラウドのネットワークの話の通りサービス数が多く、サービス規模に合わせたアドレス設計が必要です。

 "Considerations for Cluster Sizing"を参考にNodesを決めるとPodが決まります。
 ServiceのIPは削減できると思いますが、GKEクラスタ操作用のoperation networkも含め(/15)に収まるようにしています。

 ## 合計 (/15)
 -------------
 # GKEクラスタ
 Nodes   : /24
 Pod     : /16
 Service : /20

 # Operation
 bastion : /24

 クラスタを追加する場合には、Serviceネットワークのみ別途割り当てる。
 性能不足にはNodesのScaleUPで当面対応できると考えてました。

これらの関係を図示すると以下のようになります。

SharedVPC_GKE.jpg

GKEクラスタ

ネットワーク割り当てが完了しましたので、クラスタを構築したいと思います。
セキュリティを考慮し限定公開クラスタとし、マスターの制御元をoperation networkに限定するためマスター承認済みネットワークとします。

キーワードと、ドキュメントは以下を参考にしています。

  • 共有VPC内に限定公開クラスタを作成する
  • 限定公開クラスタ
    • VPCネイティブクラスタ
    • VPCネットワークピアリング
  • マスター承認済みネットワーク

  • VPC Network Peering

  • authorized networks

以上をまとめると、クラスタ作成するコマンドは次のようになります。

 cluster_name=FOO-BAR-platform-green
 network_project_id=dena-FOO-BAR-platform-nw-gcp  # HostProject  
 vpc_network=vpc-FOO-BAR-platform-1
 subnetwork=subnet-FOO-BAR-platform-1-ane1-node-172-17-100-0-24
 pod_range=subnet-FOO-BAR-platform-1-ane1-pod-172-16-0-0-16
 service_range=subnet-FOO-BAR-platform-1-ane1-service-172-17-0-0-20
 master_range=172.18.0.0/28
 authorized_network=172.17.200.0/24 # operation network
 num_node=6
 node_type=n1-standard-4

 gcloud beta container clusters create $cluster_name \
 --enable-autoupgrade \
 --enable-ip-alias \
 --enable-private-nodes \
 --enable-private-endpoint \
 --enable-master-authorized-networks \
 --no-enable-basic-auth \
 --no-issue-client-certificate \
 --master-authorized-networks $authorized_network \
 --network projects/$network_project_id/global/networks/$vpc_network \
 --subnetwork projects/$network_project_id/regions/asia-northeast1/subnetworks/$subnetwork \
 --cluster-secondary-range-name $pod_range \
 --services-secondary-range-name $service_range \
 --master-ipv4-cidr $master_range \
 --cluster-version=latest \
 --enable-stackdriver-kubernetes \
 --machine-type=$node_type \
 --num-nodes=$num_node \
 --tags=$cluster_name

最後にアプリケーションをデプロイし、セキュリティ構成を付加したサービスレベルの俯瞰図は次の通りになります。

service_security.jpg

最後に

今ではサービスを提供出来る状態になりましたが、ここに到るまでSharedVPC環境でクラスタが作成出来ないSharedVPC配下でGKE Ingressリソースが作成出来ないなど、構築過程でさまざまな問題がありました、エラーログを元に調査しても手がかりが無い場合もありサポートケースGCP Office Hourで相談しながら都度解決してきました。その情報は英語・日本語共にドキュメントを修正していただきましたが、日本語への反映には時間がかかる事に気づきました。

ドキュメントのリンク先を英語で書きましたが、これが今回お伝えしたい気づきです。

❗英語のドキュメントも読もう。❗

常識的な事かもしれませんが、日本語と英語のドキュメントの乖離はあり、一年以上に及ぶのもあります。

この文章を通して、オンプレとGKEを併用したインフラを紹介させていただきました。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

DeNAにおけるLinux環境アラート対処ケーススタディ

はじめに

こんにちは。山本です。 現在、IT基盤部第二グループのマネージャをやらせていただいております。

私のグループではいくつかの領域を扱っていますが、その中で大きな比率を占めるのがオンプレミスのLinuxサーバの管理となります。 業界のトレンドとしてはパブリッククラウド側の基盤が騒がれていますが、まだまだオンプレLinuxも現役であるのは事実。それらのサーバに関して、いかにしてDeNAで管理・運用を実施しているのかという一部を 具体的に かつ 実際に起きている日常の実例を元に お見せしたいと思います。

そもそも管理とはなんなのか?

そもそも、我々が実施している仕事というのがなんなのか、ということですが、大きく分けて以下のものに分類されると考えています。

  • 構築・構成管理 新規のサーバ構築
    • あるべき構成とのずれの検出・修正
  • サービス(事業)側への対応
    • 新規のサーバ準備
    • その他諸々小さな依頼事項への対応
  • 負荷・アラート管理
    • サーバの状態レポートの作成
    • 異常が発生した場合のその原因検知

この中で「構築・構成管理」は各所でツールなどを利用して実施されていると考えますし、「サービス(事業)側への対応」は依頼内容によって異なることもあると思います。 今回は、「負荷・アラート管理」について、特定の例を挙げた上で我々DeNAインフラエンジニアがどのように判断してどのように対応しているか、といったものを示したいと思います。一部改変していますが、おおむね事実に沿っている例です。

ケーススタディ【メモリに異常あり】

以下のようなメールを受け取りました。これは我々の監視兼ツールの一種であり、空きメモリが一定値を下回った時に自動的に特定の種類のプロセスのうち、メモリを最も大量に確保しているものをkillしてしまうというものです。

 [2019/06/15 00:03:13]
 KILLED PROCESSES

 ============================================================================
 = 2019-06-15T00:02:25 =
 == SYSTEM ==
    TOTAL     FREE  BUFFERS   CACHED    REALFREE REALFREE%
 23989.36   268.39   590.23   285.82     1144.44     4.77%

 == KILLED PROCESSES (fat) ==
 STATUS  USER      PPID   PID CHILD     RSS    PRIV    SHAR SHAR% START               CMD
 KILLED  xxxx     42482 45594     0  2229.8   255.9  1973.9 88.5% 2019-06-14T23:58:10 index.fcgi

上の場合は、メモリの空きが4.77%となっていたので、PID 45594のindex.fcgiというプロセスをkillしたということを示しています。

初動対応

インフラエンジニアは科学者である前に、サービス運用に責任を持つ人物です。

したがって、「この原因はXXXだね」という評論の前に、降りかかる火の粉を払う必要があるのです。どんな時でも次の順序で進んでいきます。

  • 状況の確認(特にサービス影響の確認)
  • 影響が出ているならば、周囲にシェアした上で緊急処置
  • 影響が出ていない(or おさまっている)ならば、調査フェーズに

メモリの場合は緊急処置として以下のようなものが考えられるでしょう。

  • 案1:workerがあまっているならばworkerを減らす
  • 案2:緊急サーバ増設
  • 案3:アプリケーション側での緊急処置

今回のケースでは何度かアラートが飛んできましたが、簡単にできる対応として、まずはworkerを5ほど減らしてもらいました。

一旦おさまった後の調査と恒久対策

一旦おさまったのちには我々のグループでは次の調査を実施します。

  • CloudForecastによる短期的・長期的なメモリ推移確認
  • ログからリクエスト数推移確認
  • サーバ毎に1秒単位で取得しているログの確認(メモリ・CPUなど)

ただ漫然と眺めていてもあまり意味がありませんので、どのような観点で眺めているのかということと判断の基本的な流れを記述します。

  • メモリ問題は、リクエスト数増加に伴う自然なものか否か?
  • 特定のサーバで起きているのか、満遍なく起きているのか?
  • 特定のタイミングで急激にメモリが上昇しているのか、満遍なく上昇しているのか?

これは、我々のグループにおいては「テストに出ます」というレベルで必要な観点です。

memory_flowchart.png

考えてみれば当然の流れだとは思います。アプリケーションコードやプロファイルを即座に眺めて詳細に調査する必要はありません。

今回のケースでは、残念ながら、特定時刻以降でどのサーバでも満遍なくメモリが増えているという状況でしたが、どちらかというとタイミングによってたまにガクッとメモリを確保していることがあるようでした。 これは、何らかのコードを通った時に問題が発生する可能性を示唆します

cf_traffic.png

trafficはもちろん上下はあるものの、特段メモリの事象が起きた際に増えているわけではありません。

                          procs -----------memory---------- ---swap-- -----io---- --system--  -----cpu-----
                          r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 20190614 00:01:40        7  0      0 1595940 678600 428780    0    0    56   228 21715 9998 13  2 84  0  0
 20190614 00:01:41        5  0      0 1418176 678600 428816    0    0     4   176 17218 7143 11  2 87  0  0
 20190614 00:01:42        5  0      0 1426624 678648 428880    0    0    68   184 18987 7940 13  2 85  0  0
 20190614 00:01:43       10  0      0 1207144 678648 429004    0    0    96   860 19715 8138 11  3 86  0  0
 20190614 00:01:44        4  0      0 1353092 678648 428940    0    0   100   164 19512 8349 14  2 84  0  0
 20190614 00:01:45        9  0      0 999816 678648 428980    0    0     4   212 22501 9151 17  3 80  0  0
 20190614 00:01:46       10  0      0 633396 678648 429036    0    0     4   196 24249 9229 18  3 79  0  0
 20190614 00:01:47        7  0      0 668244 678648 429056    0    0     0   148 24446 7356 26  3 71  0  0
 20190614 00:01:48       10  0      0 375356 678732 428764    0    0   144  1360 22122 11070 15  5 80  0  0
 20190614 00:01:49        8  0      0 453368 678736 428900    0    0    44   164 22485 8921 23  1 76  0  0
 20190614 00:01:50        8  0      0 475648 629532 417120    0    0     8   200 33972 9238 21  2 77  0  0
 20190614 00:01:51       12  0      0 414768 629532 417184    0    0     0   144 27192 11090 15  6 79  0  0
 20190614 00:01:52        4  0      0 545636 629616 417188    0    0    92   196 22731 9139 15  4 81  0  0
 20190614 00:01:53        8  0      0 171572 629620 417216    0    0     4   720 18312 7093 11  2 86  0  0
 20190614 00:01:54        7  0      0 411384 508808 384700    0    0   268   220 25455 7998 15  5 80  0  0
 20190614 00:01:55        7  0      0 358036 473876 292412    0    0   596   144 28311 6510 15  6 80  0  0
 20190614 00:01:56        6  0      0 489696 473876 292988    0    0   448   164 22794 7818 10  5 85  0  0
 20190614 00:01:57        4  0      0 377520 473876 293024    0    0    12   140 22169 8901 15  2 82  0  0
 20190614 00:01:58        5  0      0 365716 473916 292848    0    0    92  1352 19942 12584 12  1 87  0  0
 20190614 00:01:59       11  0      0 144052 402256 200484    0    0    60   192 20951 6587 12  4 84  0  0
 20190614 00:02:00        4  0      0 810580 306132 173364    0    0   256   168 22044 5282 15  3 82  0  0
 20190614 00:02:01        9  0      0 685372 306148 173112    0    0   108   228 23284 9098 16  4 80  0  0
 20190614 00:02:02        8  0      0 773072 306492 173572    0    0  1360   160 24735 7460 13  7 79  1  0
 20190614 00:02:03        7  0      0 689528 306500 175876    0    0  1752   872 24791 8648 11  4 83  2  0

メモリは、一気に確保したり解放したりといった瞬間があるようです。例えば00:01:42から一気に空きメモリ(free)が減少しています。

アプリケーションコード

ここまでくると、仕方ないので真面目にアプリケーション側のコードの解析を実施します。 これも、漫然と眺めていても仕方ありませんので、DeNAで利用されているperl heap statを利用してみました。

  • perl heap statでの確認
  • (場合によっては)nytprofの確認

perl heap stat(phs)ではコードとその行番号を含めて示してくれます。 DeNAインフラの今の話 第2回「性能劣化、障害発生時のプロファイリングツールの紹介」 にはphsの実行例が出ていますが、問題箇所を的確に指し示してくれることがあります。

今回の場合には、特定のアプリケーションの特定のサブルーチンが示されていました。

 sub addData {
    my ($id) = @_;

 (省略)   
    my $dbh = DA::getHandle($handle);
    my $ret;
    if (exists($CACHE->{'master'})) {
            $ret = $CACHE->{'master'};
        } else {
        eval {
            $ret = SQLを利用し、DBからデータを取得してくるコード
            $CACHE->{'master'} = $ret;
        };
    }
 (省略)
 }

このPerlコードは、 $CACHE->{master}(グローバルなスコープを持つ変数であり、消えない) が存在すればそれを利用し、存在しなければ、実際にDBからデータを取得してきた上で、 $CACHE->{master}にセットするというものです。 特段問題はないように思えますが、ここのmasterにどの程度のレコードが含まれているかということが重要となります。

SQLで取得してきているテーブルを実際に検索し、そのデータ数を確認すると、これがそれなりに多いということがわかりました。その大量のデータを$CACHE->{master}に保持することによって問題が起きうる、ということが仮説として浮かびます。

といって、どうすれば良いのかということになります。$CACHE->{master}にセットせず、毎回SQLを発行してDBから取得するという方法があります。間違いではありませんが、クエリの増加、速度の下降といった問題があります。 我々がこの例で利用している仕組みでは、Perlはhttpdと別プロセスで動作しており、親となるPerlプロセスが各モジュールを読み込んで初期化しておいたのちに、そのプロセスがforkして、多数のworkerプロセスとなります。 したがって、親プロセスとなるべきプロセスで、 $CACHE->{'master'}をセットしておけば、そのメモリはforkされた子プロセスでもそのまま利用可能であり、なおかつCopy-On-Writeの仕組みによって、実際のメモリコピーはなされず、親プロセスで利用していたメモリを利用してくれます。

ですので、コードを次のように書き換えてもらうこととなりました。

 # 高負荷のため、preload化
 sub initializeMaster {
    my $dbh = DA::getHandle($handle);
    my $ret = SQLを利用し、DBからデータを取得してくるコード
    if($ret) {
        $CACHE->{'master'} = $ret;
    }
 (省略)
 }

 initializeMaster();

 # こちらは変更しない
 sub addData {
    my ($id) = @_;

 (省略)   
    my $dbh = DA::getHandle($handle);
    my $ret;
    if (exists($CACHE->{'master'})) {
            $ret = $CACHE->{'master'};
        } else {
        eval {
            $ret = SQLを利用し、DBからデータを取得してくるコード
            $CACHE->{'master'} = $ret;
        };
    }
 (省略)
 }

つまり、今までの処理は変更せず、単純に $CACHE->{master} をモジュールロード時に実行される initializeMaster() でセットしたということになります。

preload.png

preloadに関連する基本的な知識ではありますが、こういったものを調査して、実際にアプリケーション開発側にお伝えして具体的な再発防止策として実施する、というところまで行けるのがベストです。(その後、再発していないことを確認することももちろん重要です。)

このような問題は、開発環境などでは確認しにくく、また、DBのレコード数が徐々に増加してくることによって顕在化してくるとか、該当のコードを通りやすいようなページ構成になるとかいった要因によって発生するものであり、開発中に気づくのは非常に難しいと言わざるを得ません。したがって我々インフラエンジニアが気づいて、本格的な問題が起きる前に修正してもらうのがベストだと考えています。

なお、実際の運用において、素直に全てのメンバーが一本道でゴールにたどり着くというわけではありませんし、「この問題は調査打ち切りとする」というような場面もあります。

他のケースの場合

今回のケースは実際に日常的に起きている例の一つであり、最終的にアプリケーション開発者に連絡して再発防止策実施にまで至った例ですが、この一つを取ってもインフラエンジニアとして ロジカルに判断 しつつ、 背景として技術的知識を利用 する必要があります。 単純に「よくわからないけれどサーバ増設だ!」「手当たり次第に調べてみるぜ!」といったものではなく、都度状況を判断しつつ必要なものを調査し、ゴールに向かっていくという流れとしたいところです。

また、例としてメモリアラートを出したのは、最近私の管轄内で頻発しているからですが、上に書いた進み方の全体方針の考えは他のアラートでも同様です。 「よくわからないけれどネットワークが切断される」「よくわからないけれど、レスポンスが遅い」といった問題に対して切り込むメソッドを磨くことで、インフラ技術者としての練度を上げるべく努力したいと思います。


今回の話の背景知識

webのバックエンドでworkerを走らせる場合のメモリとworker数の関係

上で、workerを減らすという話があります。 workerがあまっているならばこれはメモリ確保のための非常にお手軽な解決策となります。とはいえ、例えばworker数を2/3にすればメモリの使用量が2/3になるというような簡単なものではありません。

memory_workers.png

実際には上の図のように、worker数を減らしても、一般に1workerあたりのメモリ使用量は増加するので、そこまでの効果はありません。 これは、いままで6個のworkerで受けていたリクエストが4個のworkerになったので、1 workerあたりのリクエスト処理量が1.5倍に増えるためです。

あとはアプリケーションプログラムの書き方によるのですが、例えば、リクエストを受けるたびに、プロセスメモリ上にキャッシュとして確保などしているとすれば、プロセスが受けるリクエスト数が増えれば、それだけプロセスのメモリ確保は多くなるのです。当たり前ですね。

といって、効果がないわけではありませんし、お手軽に実施可能ですので、まずはじめに検討してみても良いと思います。

workerがあまっているならば...?

お気軽に「workerがあまっているならば」などと書いてしまいましたが、あまっているかどうか、どうやって知ることができるのかという話があります。

DeNAのperl環境においてはperlstackという仕組みがあるので、実際に遊んでいるworker数を正確に算出していますが、どこでもその方法が使えるわけでもありません。 しかし、それがなくとも簡単に概算できます。

  • 単位時間あたりのリクエスト数
  • 平均レスポンスタイム

この二つがわかれば十分です。単位時間(例えば1分)にリクエストが1000きているとして、平均0.3秒でレスポンスを返しているとしましょう。 すると、1分あたりで、実際に処理が動いているのは「のべ」300秒ということになります。1分(60秒)の間に300秒分の処理をするためには1workerでは足りず、5workersは最低でも必要となります。実際には、リクエストのバラツキもありますから、workerの「待ち時間」というものが発生します。 その分を考えて、余裕を持ったworker数を準備する必要があります。

num_of_workers.png

しかし、例えば「1分に100リクエスト、平均レスポンスタイム0.5秒」という状況で20 workersが用意されているとすれば、それはあまっていると判断して差し支えないでしょう。 もちろん、ピーク時を考慮してリクエスト数は検討する必要がありますが、明らかに過剰と判断すれば、無駄なメモリの肥やしを作る意味はありません。

Perlとメモリ

Perlはメモリの管理が必ずしも先進的ではなく「本物の」Garbage Collector(GC)を持っておりません。 実際に我々の管理しているサーバにおいても、ほっておくとメモリがどんどん溜まってきてしまう例があります。本来的にはメモリに対する全ての参照を破壊すれば、その変数の参照カウントがゼロになるはずですが、おそらくそうなっていない何かが存在することでメモリがどんどん増加するのです。

通常のGCでは、根元から辿って行ってたどり着けるメモリにマークしていって、マークされなかった変数に対して回収をかけます。しかし、Perlの場合には参照の有無をみるだけなので、根元から辿り着けなくとも互いにリファレンスで参照しあっているような変数があれば、最後まで回収されません。一番シンプルな例としては、my $var = \$var; って書いておくと、myのスコープとか関係なくメモリ回収されないということです。

これをどうすべきか、という話では、「もっと近代的な言語に切り替える」「真面目にメモリリーク検出ツールを用いる」といったものもあると思います。しかし、我々の利用しているXSなどを含めたコード全体を見直す手間などを考慮すれば「定期的にプロセスを入れ替える」という現実的な選択肢に落ち着いてしまいました。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

安心・安全・安価な日中間接続~Alibaba Cloud CEN~

こんにちは、IT基盤部第三グループのジュンヤと申します。DeNAに入社してちょうどこの9月で丸9年になります。 入社当初はネットワークエンジニアとして着任しましたが、それから、金融系・EC系システムのインフラに携わり、現在は社内システムのインフラエンジニアのマネージャをしています。ちなみに、FirstNameを名乗っている理由は、同じ部署に同じ名字のメンバーがいるためであり、様々な事故防止が理由であって、それ以上でも以下でもありません。

社内システムのインフラエンジニアとは?

当たり前ですが、インフラエンジニアと一口に言っても、担当しているシステムによって必要なスキルセットは異なります。その中でも、社内システムのインフラエンジニアというポジションは、雑多な幅広いテクノロジーに触れることができます。 管理しているサーバOSという面ではLinuxはもちろん、WindowsServerOS(いまだに2008とか・・・もうすぐ撤廃予定)もあり、仮想化基盤ではKVM、VMwareなどがありますし、ミドルウェアに目を向ければ、ActiveDirectory、MySQLやOracleといったRDBMS、Github:Eや様々なSaaSなどの運用も行っています。 このように、社内システムは多種多様なテクノロジーが混在し、日々、バックオフィスを支える縁の下の力持ちとして運用業務を行っていますが、もちろんそれだけではなく、スタッフから寄せられる要望や問題解決にあたることもあります。今回はその中の一つの例を紹介したいと思います。

「VPN接続ができないんだけど・・・」 〜中国からの便り〜

第一報

もう6月なのに肌寒い日もあり、今年は夏が来ないのでは?などと思っていたある日、あるスタッフからslackで問い合わせがありました。
見ると、週末から突如VPN接続ができなくなった、とのこと。VPNサービスは自宅などからリモートワークをするために必要ですし、それこそインフラエンジニアがメンテナンス目的で多数のサーバへのアクセスをするためだったり、SourceIPでアクセス制限をしているクラウド上のシステムを利用するためだったりと、言うまでもなく社内システムの中でも超重要なサービスの一つです。
嫌な汗が流れそうになるのを感じながらも、どのようなエラーがでているか聞くと、slackに以下のメッセージが貼られます。

 L2TP-VPNのサーバが応答しませんでした。接続し直してください。
 それでも問題が解決しない場合は、設定を確認し、管理者に問い合わせてください。

できれば遭遇したくないエラーメッセージの一つです。
「応答しませんでした」と症状を訴えているところを見ると、クライアントは接続しにいったけど、サーバが応答しない、もしくはサーバまで到達できなかったという問題になります。
ここで、サーバに問題があるとなると、オオゴトになります。VPNサービスは前述したとおり、平常時から利用されているサービスですので、障害が発生するとそれなりの騒ぎになりますが、ざわついているslackチャンネルも見当たらないし、問い合わせも特になし。ましてや監視アラートも発報されていません。念の為にVPNサーバ側での認証ログをみても形跡がない。となると、クライアント側の環境か途中のネットワーク経路に疑いが持たれます。

原因特定。コレはアレでどうしようもないのでは・・・

ここで、問い合わせしてきたスタッフが中国に出張中ということにあらためて気づきます。そして、現地にいる別のスタッフにも同様にVPN接続ができるか念の為に確認を依頼してみると、結果は案の定、接続不可で同じエラーメッセージが表示されるとのこと。ここまできて、コレはアレで打つ手がないのでは・・と原因に目星をつけることになります。

「中国特有の現象」に遭遇

このblogを読まれている方には説明不要かもしれません。ピンとこない方は事象を検索してみて下さい。
今回の不具合は、十中八九、とある事情で接続できない事象(ここでは便宜上、「中国特有の現象」と呼ぶことにします)が原因だと判断した私は、突如戦意を失い、その旨を伝えます。

ジュンヤ「もうダメかもしれない・・・」
スタッフ「業務に支障があります」
ジュンヤ「わかりました・・なんとかしますので時間を下さい」

負けられない戦いがここにある。
サービスを直接支えるエンジニアではないけれども、サービスを作っているエンジニアをさらにその下で支えるエンジニアである、との誇りとともに、「中国特有の現象」に立ち向かうことを決意します。

Alibaba Cloud CENとの出会い

DeNAではオンプレからクラウドへの移行(Cloud Journey)が現在進行中です。現時点では主なクラウド基盤はAWSとGCPで、Alibabaは名前は聞いたことがあるけど(なんか強そう・・・)、というレベルでした。
そんな状況の中、今回のミッションである「中国から日本のシステムにVPNを張る」ために、Alibaba Cloudを利用するという構成が選択肢としてあがりました。
構成案は以下の通りです。

cen1.png

CENを利用したVPN構成

ポイントは「CEN(Cloud Enterprise Network)」というサービスです。これはリージョンの異なるVPC間を簡単にプライベートネットワークでつなぐことができる、というものです。 https://jp.alibabacloud.com/product/cen

「中国特有の現象」の影響を受けないためには、日中間で専用線を敷設することが必須というのがこれまでの常識でした。しかし、そのためには物理的な拠点が必要ですし、コストもリードタイムもバカになりません。 専用線の代わりにCENを利用し、VPNServerをクラウド上に配置することで、安価で柔軟性があるVPN接続ができそう、と期待が持てます。

実際に使ってみる

WEBにはCEN活用例がすでに紹介されていて、Step by Stepでの解説もされています。ありがたいです。
とはいっても、Alibabaを使うのは初めてのため、どれくらい手間がかかるのかわかりません。早速アカウントを作成し、実際に使いながら設定をしてみます。
無料枠(30,000円分)もついていますので、検証目的で気軽に始めることができるのも良い点です。 ログインしてみた第一印象は、「とっつきやすそう」。ユーザーフレンドリーな感じが好印象です。

ali1.png

シンプルなコンソール画面。アバターも設定できたりします
ali2.png
ローカライズも問題ないレベル

具体的な設定手順はここでは割愛します。前述したとおり、既に参考になるサイトがありますし、直感的に操作できます。
CENの注意点としては、実際に設定した後、リージョン間をPINGで疎通確認をするレベルであれば、CENコスト0で可能ですが、実際にSSHログインしたり、curl等でアクセス確認することはできません。
これは、デフォルトでは帯域が1kbpsしかないことが理由で、実用的に使うには帯域パッケージを購入して適用する必要があります。(2Mbpsで約30,000円/月)
また、Alibabaコンソールにログインした画面からは何故かCENコンソールへの導線がありません。CEN関連の作業は別途画面を開く必要があります。(https://cen.console.aliyun.com/

経路を無事確保。セキュアなVPN通信とは?

触り始めて数十分で日中間でのAlibaba VPCで疎通確認をすることができました。想像以上に簡単です。
しかし、ミッション完遂までは、やるべきことはいくつかあります。ざっと以下のようなタスクがあります。

  1. VPNを利用するアカウント数と通信要件のヒアリング
  2. IPアドレス設計(各VPC、クライアントVPN)
  3. JP-RegionとDeNAデータセンタの接続
  4. CN-RegionでVPNServerを構築
  5. 接続テスト
  6. (様子を見て)CEN帯域幅の決定、帯域パッケージの適用

タスクリストだけ見るとすぐにコンプリートできそうですが、セキュリティには十分注意を払う必要があります。 VPNサービスという社内システムを安全に使うためのシステムにセキュリティホールがあっては意味がありません。 特に考慮したポイントは以下になります。

  • VPN接続時のMFA(Multi-Factor Authentication)化
  • VPNサービスのアカウントの自動棚卸し
  • エンドツーエンドでのセキュアな通信をするための仕組みづくり
  • 必要なIP/Portのみ開放する(SecurityGroup)

VPNを利用するアカウント数と通信要件のヒアリング

まずは中国からのVPN利用人数とどのような通信が必要なのかをヒアリングします。
今回は結果として、プロトコルはHTTPS/HTTPのみ、アクセス先は社内システムとそれ以外という分類になりました。 通信要件としてはシンプルなものと言えます。

IPアドレス設計、JP-RegionとDeNAデータセンタの接続

タスクの2と3については、私のグループの管理下ではないため、部内にある専門部隊のNetworkグループに相談をします。優秀なネットワークエンジニアが多数在籍しているため安心して相談することができます。実際に、この作業も非常に短期間で完了しました。Alibaba側で必要な作業としては、VPN-Gatewayというサービスを利用することになります。実際の構築方法については、これも参考になるサイトがありますのでそちらに譲ります。

CN-RegionでVPNServerを立てる

この時点で、Alibaba CN-RegionとDeNAデータセンタの疎通確認が取れる状態になりました。ただし、セキュリティの観点からDCのInbound向きACLはほぼ閉じた状態です。
まずは入り口をしっかり守るという点で、VPN接続にはID/Passwordの他にMFA(Multi-Factor Authentication)を必須としました。ID/Passwordだけでは万が一、漏洩した場合に第三者がそのクレデンシャルを使った「なりすまし」リスクが想定されます。 MFAに対応したVPNソリューションはいくつかあると思いますが、ここではOpenVPN AS(Access Server)を選定しました。デフォルトでGoogleAuthenticatorに対応しています。
mfa.png

ログインにはID/PASSWORDと認証コードが必要

また、接続アカウント(ID)についてもVPNServerのローカルで情報を持ってしまうと棚卸しが大変です。退職等によって権限を剥奪する作業を別途行う必要があるからです。 この点、OpenVPN ASはLDAPに対応しています。DeNAはActiveDirectoryによってスタッフのアカウントの一元管理を行っており、LDAPも話せます。ADとLDAP連携することで、退職者についてはAD上で無効化されたと同時にOpenVPNにもログインできなくなり、別途棚卸しする必要がなくなりますので、この点も◎です。

ldap.png

OpenVPN設定画面より。専用のCNをADに用意すれば、VPN利用対象者を絞ることができる

パブリッククラウドだからこそセキュアな通信を

無事にクライアントPCからVPN接続ができたら一安心、としたいところですが、パブリッククラウドならではの注意するべきポイントがあります。オンプレミス環境とは違い、インフラをすべて自分たちで管理できているわけではないため、「盗聴リスク」も念頭におく必要があります。今回は以下の図のような構成にすることにしました。

vpn.png

エンドツーエンドでのセキュアな通信を考慮した構成

目指したことはクライアントPCからDeNA Data Centerまでの通信はすべてSSL化すること、です。

  • クライアントPC <-> OpenVPN Server間はSSL-VPN
  • OpenVPN ServerからLDAP Serverへの認証はLDAPS
  • クライアントPCからpacファイルへの通信はHTTPS
  • クライアントPCからプロキシサーバへの通信もHTTPS

このような構成にすることで、パブリッククラウド内では平文が流れることがほぼなくなり、盗聴リスクが大幅に軽減されます。
クライアントPCのブラウザにはプロキシを設定します。プロキシを間に挟むことで、アクセスログの取得もできますし、FWの開放Portを最小限に抑えることができます。また、直接、プロキシサーバを指定せずに、自動プロキシ構成(.pac)ファイルを設置したWEBサーバを準備し、そのアドレスを指すようにします。こうすることで、pacファイルによってトラフィックを複数のプロキシサーバに振り分けることができるようになります。DeNAでは、社外サイトへの通信はProxy-A、社内サイトへの通信はProxy-Bへ、といったようにセキュリティレベルにあわせたProxyを利用するようにしています。

仕上げ -各種ACLの見直しを忘れずに-

細かいところに気を配り、セキュリティ向上に努めたとしても、セキュリティグループやネットワークACLがガラ空きなことほど惨めなことはありません。全体構成を絵にして見直し、必要最低限のPortのみ開放するようにします。

最後に

現在、接続テストを実施していますが、大きな問題は発生していません。これから接続する人数を増やしてCENの帯域を増やす必要があるかどうかも判断していく必要がありますが、ワンクリックで即時に帯域を増やせるため、そこまで神経質に見極める必要はなさそうです。 パブリッククラウドという特性上、セキュリティ面には注意を払う必要がありますが、CENを使うことで、非常に簡単にコストを抑えたグローバルなプライベートネットワークを構築することができました。

「中国特有の現象」の前に、諦めたことがある、もしくは今まさに遭遇している、そんな方にCENを使ってみることをおすすめします。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

オンプレとクラウドのネットワークの話

IT基盤部エンジニアブログリレー第4回目。
こんにちは、IT基盤部ネットワークG torigoe です。
オンプレとクラウドのネットワークをどう繋げるのか、クラウド内のネットワークをどうするのか考えた話を書きます。

多くの内部サービスを持っていて、オンプレからクラウドに移行を検討しているネットワークエンジニアの人に読んで頂ければと思います。

はじまり

2018年ある日、mtgに呼ばれて急に DeNAのインフラ全てをオンプレからパブリッククラウド(しかもマルチ)に変えようと思うがどうよ? と言われました。正直その日まであまりパブリッククラウドサービスを触った事がありませんでしたが、説明された内容には納得できたのでそんな事は噯にも出さず同意し、次の日からオンプレ-クラウド移行の為に必要なネットワークの検討を開始する事になりました。(元々Openstackは触ってました)

まずはAWSを色々と触ってみました。 ちらほらなんでこんな仕様なの?という疑問をいだきつつも、ドキュメント読んだり触ったりして大体使い方を把握しました。
AWSの次はGCPを触り、それぞれ次のような感想を持ちました。

  • AWSはユーザファーストの意識が高いように思える
  • GCPはアーキテクチャに拘っていて独自性が高い

要件を整理する

オンプレとクラウドを繋げるネットワークを設計するにあたって、まず要件を整理する事にしました。
ざっくり書き出すと、

  • 最終的にクラウドをマルチ(AWS/GCP)に使いたい
  • DR化も踏まえて検討したい
  • DeNAは非常にサービス数が多い=VPC数が多くなる
  • 各VPCとオンプレ間/VPC間はセキュリティを考慮しつつボトルネック無く通信したい
  • オンプレに多少サービスが残る可能性もケアしておく
  • オンプレネットワーク機器の保守期間の残りや延長可否について決める
  • ネットワーク運用にかかる工数を削減したい
  • ネットワークセキュリティも整理したい

といったところでした。
要件の中に一部弊社オンプレ環境の問題もありますので、今回のブログでは主にクラウドのネットワーク周りにフォーカスして書きます。

検討開始

現状のネットワークの課題整理、クラウド側のネットワーク仕様、最終形、という3つを意識しつつ検討を始めました。

オンプレ~クラウド間の接続

まずオンプレとパブリッククラウドの間を物理的にどうやって繋げるのか検討しました。 経験上インターネットVPNでの通信は遅延や品質を考えると、サービスで使えるものでは無いと判断していました。 ファイアウォールのスペック次第ですがVPN通信のスループットはそこまで大きな帯域を確保出来るものではありませんし、インターネット上をパケットが流れるので我々のコントロール外のネットワークで輻輳や障害が発生しパケロスが発生する事があります。

実際今までにAWS/GCPとのVPN接続を何本も張っていますが、運用中にそこそこの頻度でAWS/GCPとのVPNが落ちる事を経験していました。VPN接続も冗長化している為大きなロスにはならないですが、やはり多少のインパクトはあります。 移行期間中はマスターDBがどちらかにある状態なので、VPNを介したサービスはレイテンシやパケロスにより遅延が発生する事が想定されます。 という事で、本格的にサービス移行&サービス利用用途としての通信手段はVPNでは無く専用線を引くしか無いだろうと考え、専用線の物理接続構成の検討を進めました。

2018年検討時点でのクラウド側(AWS/GCP)の10G専用線接続ポイント(データセンター)は下記の通りでした。

  • AWS Equinix TY2(Tokyo)
  • AWS @Tokyo(Tokyo)
  • AWS Equinix OS1(Osaka)
  • GCP Equinix TY2(Tokyo)
  • GCP ComSpace1(Tokyo)
  • GCP Equinix OS1(Osaka)
  • GCP NTT Dozima(Osaka)

※201906現在GCPは@Tokyo(Tokyo)も接続可能です

弊社のオンプレ環境は上記DCにはありませんので、AWS/GCPと直接通信する為には弊社DCと上記DCのどこかの区間に専用線を敷設する必要があります。 検討を進めていく中で、オンプレに多少サービスが残る可能性がネックになってきました。 というのも、万一最終的にオンプレにある程度のサービス・ラックが残るとなると,最善の最終DC構成がコスト見合いで変わってくる事がわかってきたからです。 そこで一旦不透明な最終形は考えずに、とりあえず現状が使いやすいように専用線を引く方向で検討を進める事にしました。専用線は敷設までの納期や最低利用期間等を考えると引き直しのハードルがやや高いのですが、それは許容する事にしました。

私の考えたマルチクラウドネットワーク+オンプレのロケーションの理想は、 AWS/GCP両方が同一DCで構内接続可能なEquinix or @Tokyoにラックを借りてルータを置き、両パブリッククラウドと構内接続する、サービスラックはそのDCに置くです。そうすれば全て構内配線で接続出来るので、回線周りのコスト・納期やレイテンシ等で優位性があります。

全て同一DCに収めるイメージ

全て同一DCに収めるイメージ

オンプレ-クラウド間のネットワークの繋ぎ方

次にオンプレ-クラウド間のネットワークをどうやって接続するのかを考えてみました。
要件でも挙げた通り、DeNAの特徴として サービス数(アカウント数/VPC数)が非常に多い ので専用線経由で多VPC接続という事を考慮する必要があり、 AWSの専用線ネットワークについて調査を進める中で、AWS DirectConnectのある制限に引っかかる事がわかりました。2018年検討当時、DirectConnect経由でVPCに接続できるのは50本までという制限がありました。(※今も制限ありますが、今はDirectConnectGatewayの仕様変更により50以上接続できます) DeNAのアカウント数は余裕で200を超える事が想定されていたので、この制限数の50に引っ掛かります。世の中のワークアラウンドを調べても、VPC内にルータインスタンスを立ててルーティングさせる、という非常にクラウドっぽく無いノウハウが散見されました。せっかくのクラウドなのでオンプレのようなネットワーク運用はしたくありません。

AWSの場合

DeNAではAWS Office Hourという毎週AWSの人が来社して気軽に相談出来るSpecialなmeeting timeが提供されていたので、早速AWSの人にカジュアルに相談を開始しました。タイミングが良かったのか、相談開始して程無くAWS Transit Gateway(以下TGW)という新ネットワークサービスが始まる、という事をUnderNDAで教えてもらいました。

※201907現在TGWはGAですが、東京リージョンのDirect Connectではまだ利用できません

TGWの仕様を細かく調べると多少課題は見つかったものの、概ね我々のやりたい事は出来そうでした。簡単に説明するとTGW=マネージドルータで、オンプレからTGWに接続すればTGWが各VPCにルーティングしてくれます。

TGWはこんな接続イメージです

TGWはこんな接続イメージです

AWS TGWの特徴は下記の通りです。

  • AWSサービスのマネジメントルータ
  • (TGWの)冗長性を気にする必要は無い
  • ルータなので経路情報を持つ事が出来る
  • TGWから他AWSアカウントのVPCに対して接続可能
  • TGWはデフォルトルートを持つことが可能
  • AWS Internet Gateway (IGW)と接続可能
  • AWS Direct Connect Gateway (DXGW)経由でオンプレと接続可能
  • ip addressの重複は不可
  • IPv6利用可能
  • TGWの帯域等はかなり大きく、ボトルネックにならなそう

TGWが無い場合のオンプレ-多VPC間の接続は大変だと思います。 VPC間通信のみを考えてもVPCピアリングやPrivateLinkというVPC間を繋ぐサービスもありますが、どちらも弱点があり今回の設計ではスコープから外しました。各VPC内のインスタンス上にルータをにたててルーティングさせるという前述の方法を採用すれば目的は達成できそうですが、そのルータの冗長化やネットワーク運用を考えると辛いです。

TGWの仕様を調べる中で課題がいくつか見つかったので、課題についてEBC (Executive Briefing Center)というAWSのサービス開発チームの人との個別セッションの場をAWSの方にセッティング頂き、DeNAの要望をAWSの開発の人に余す事無く伝えきる事が出来ました。まだ一部要望した機能は実装されておりませんが、サービス開発のロードマップには乗っています。
2018年 AWS EBC 集合写真(シアトル)

2018年 AWS EBC 集合写真(シアトル)

DeNAでは、AWSとオンプレのネットワーク接続にTGWを採用する事を決めています。

GCPの場合

並行してGCPも調査していましたが、やはりGCPもAWS同様専用線経由のProjectとの接続数制限がありました。 GCPもAWS同様GCP Office Hourという素晴らし(ry、GCPの人に相談したところ、GCP Shared VPCというサービスがある事がわかりました。
AWSのTGWとはかなり毛色が違い、ある1Project(Host Project)から他のProjectに対してVPCをシェアする、というネットワークサービスです。既に利用出来る状態だったので早速調査&検証を開始しました。

SharedVPC.png

SharedVPC接続イメージです

特徴は下記の通りです。

  • (TGWのような)ルータサービスでは無い
  • Host Project によって作成された1つの VPC を複数の Project で共有するサービス
  • Route, Firewall, Subnet, VPN を Host Project から一元管理できる
  • Organization 内で SharedVPC が有効化された Project が Host Project となる
  • Host Project に Attach した Project(Service Project)が共有されたリソース内でGCPサービスが利用できる(GCP全サービスではない)
  • Service Project 固有のスタンドアローンな VPC やその他のサービスも今で通り利用可能

大体やりたい事が出来る事はわかりましたが、TGWと違ってややイレギュラーな思想の為、TGWとは全く違う課題がいくつか見つかったのでGCP Office Hourの場で改善希望を伝えました。

DeNAでは、GCPとオンプレのネットワーク接続にShared VPCを採用する事を決めて、既に利用を開始しています。

その他考慮するポイント

ネットワークアドレスの管理、ネットワークセキュリティを考慮する必要があります。

ネットワークアドレス設計

当たり前の話ですが、オンプレ-クラウド間・VPC間でNATを介さずに自由に通信させたい場合は、オンプレ・クラウド全てでプライベートIPアドレスを重複しないように管理する必要があります。 クラウドだからと自由にアドレス採番をされてしまうと、 オンプレとネットワークを繋げる事が出来ないケースが出てきます。

※アドレスの設計詳細はセキュリティの都合上お伝えできませんが、書ける範囲で書きます

例えば10.0.0.0/8のプライベートアドレスがすべてクラウドで使えるとしても、前述の通りDeNAはサービス数が非常に多いので、全サービスが/16を使いたいと言い出した場合、本当にあっという間にアドレスが枯渇します。 また、そもそもDeNAはオフィス・社員数が多いので、オフィス用のプライベートアドレス空間は大きめに確保しています。また、データセンターにも数千台規模のサーバ群があるので、こちらもvlan毎に大きめにアドレス空間を確保しています。なので、プライベートアドレスは決して潤沢とは言えません。クラウド側にフルにプライベートアドレスを割り当てられるという事はありませんし、今後AWS/GCP以外にも利用クラウドが増えていく事を考えると、プライベートアドレスの管理はしっかりと運用していく必要があります。 クラウド側は耐障害性を考えてAZを分ける等の対応を取る必要があるので、/24ではほぼ足りず、規模に応じて/22-/20の間で各サービスに採番する事にしました。

その他にも細かいアドレス採番ルールもあるので、プライベートアドレスはIT基盤部で採番管理する事にしています。

ネットワークセキュリティ

実はネットワークセキュリティの検討については一番時間を使ったかもしれません。 現在オンプレ環境ではオフィス-DC間、DC内のvlan間でフィルタを設定していますが、このセキュリティ設定管理が非常に煩雑なのでここをどうにかしたいと考えていました。

その中でも、社内の人間のサービスリソースへのアクセス制限の管理が特に煩雑なポイントでした。 机上で考えるとただ決められたセキュリティルールに基づき初期設定すれば良いだけと思えますが、実際はかなりのイレギュラーケースが発生し、毎日のようにフィルタ設定の変更をする必要が出てきます。開発環境-本番環境間のフィルタも同様で、恒常的にフィルタ設定の変更をする必要があります。

vlanベースでのフィルタの設定にも限界がありました。具体的には、例えばサービスを複数跨ってアクセスするような人がいる場合、vlanベースだと人はvlan Aかvlan Bのどちらかにしか所属してしないのでどちらかにかアクセス出来ません。必要であれば最悪その人の為だけにわざわざ新しいvlanを作成する必要があり、vlanベースの通信制御は破綻していきます。 また、サービス=部署単位では無いので、部署単位でvlanを作ってもその通りにアクセス制御をする事も出来ません。

つまり、やりたい事としては、

  • サービス単位のグループで通信制御
  • 人はグループを跨ってもOK
  • そのグループのメンバーはサービス側で管理する

です。
DeNAではGSuiteを利用している為、GoogleGroupでグループ作成する事が一般的でAD連携が出来る仕組みもあったので、GoogleGroupを活用したアクセスの仕組みを考えました。色々と考えましたが、オフィスからもリモート(自宅)からも同一のセキュリティポリシーでアクセスさせたい事を考えるとVPNで制御するしかないかな、という結論に至りました。いくつかのVPN製品を調べていく中で、これも2018年検討当時に AWS ClientVPN がリリースされるという情報をAWS OHで教えて貰いました。(UnderNDA)
既にリリースもされているのでご存知の方も多いと思いますが、特筆すべきはADのSIDベースで制御できるという部分で、ここがマッチしそうでした。リリースされてから色々と触った所、いくつかの課題が見つかりました。

  • フィルタの順序が変えられない(順序を変えるには全消しして入れ直さないといけない...)
  • SIDを同時に複数指定出来ない
  • denyが書けない
  • クライアントのデバイスが固定出来ない

実際に使ってみて頂くとわかると思いますが、通信制御周りが使いづらいです。 上記に挙げたものは全て重要で、改善要望をあげています。

その他にもネットワークセキュリティ設計として、ネットワークセキュリティポリシーの設計をしました。ここの詳細はセキュリティの都合上割愛します。ポイントとしては、クラウド側のVPCのセキュリティポリシーを誰がどうやって制御するのか、具体的にどういうセキュリティポリシーにするのかといった内容で、セキュリティ部の人達と議論を重ねて大枠を決めていきました。

最後に

スケジュール感としては、2018年8月頃から検討開始、その後2018年内は毎週のように議論を重ねて、設計の大枠が出来たのが2018年末だったと思います。その後は細かい微調整やクラウド側への要望を続けたり、という事を継続しています。

この文章を通して、DeNAのオンプレとクラウドとのネットワーク接続への考え方が伝わればいいな、と思います。

色々と相談に乗って頂いたAWS/GCPの方々、どうもありがとうございました!
今後も引き続きよろしくお願いします。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

DeNA の QCT マネジメント

DeNA の土屋と申します。2014 年に新卒で入社して以来一貫して IT 基盤部に所属し,現在は第四グループのマネージャーとして,全世界向けに提供している超大規模ゲームタイトルおよびゲームプラットフォームのインフラ運用をリードしています。

IT基盤第4G.png

私のグループでは直近 1 年間,クラウド環境におけるコストコントロールの施策中心に,インフラの QCT (Quality, Cost, Time) マネジメントに取り組んで来ました。その具体的な方法や成果については先日の AWS Summit Tokyo 2019 で発表させて頂きました (以下が発表資料です)。

この資料だけを見ると極めて体系的かつ順調に各施策を進めることができた,ともしかしたら見えるかもしれませんが,実際は様々な試行錯誤がありました。本記事ではそのような苦労話に触れつつ,インフラコスト 60% 削減達成までの道のりについてお話しします。

インフラに求められる QCT とは

インフラノウハウ発信プロジェクト第 1 回目の記事でも触れられていた内容ですが,具体的な話に入る前にインフラの文脈における QCT がどのような内容を指すかについて改めて整理したいと思います。

  • Quality
    • 最高品質のインフラを提供することを指します。インフラ起因による障害をなくすことはもちろん,品質維持・向上のためにできることは全て行い,工数の許す限り稼働率 100% を維持し続けることを目指してインフラを運用しています。
  • Cost
    • インフラ費用を適切にコントロールすることを指します。DeNA のインフラ部隊は創業当時から非常に高いコスト意識を持っています。ただし,ただ安ければ良いという考え方ではありません。管理工数や利便性などに照らしてメリットがあるサービスや商品についてはたとえ割高であっても利用するという判断をします。
  • Time (Delivery)
    • インフラ提供のリードタイムを短くすることを指します。品質とコストを求めるあまり,インフラ提供までに何ヶ月も掛かるような状態では DeNA の事業展開スピードを支えられません。どのようなスケジュールでも安定したインフラを提供することも重要なミッションの 1 つです。

本記事ではコスト削減がメインの話題になりますが,他二つの水準を落とさないということは大前提になります。

なぜコストコントロールが必要になったのか

DeNA はもともとメインのシステム基盤にはオンプレを採用していましたが,私のグループが担当するサービスは全世界向け,そして必要リソースが大幅に乱高下するため,サービス開始当初から 100% クラウドで運用していました。

そのような中,2018 年 6 月に大きな意思決定がなされます。全サービスのシステム基盤をクラウド化するという決定です。当時の DeNA インフラの概況や,クラウド化の経緯についての説明は以下の資料に譲り,ここでは割愛します。

オンプレ環境において,DeNA のインフラは圧倒的な低コストを実現していました。これは同一スペックのサーバを同時に大量に購入して調達価格を下げるという工夫や,サーバのリソースを使い切るための技術およびノウハウによるものです。

この状態からクラウド環境へ移行すると費用はどのくらい増えるかを試算したところ,何も工夫せずに移行するとなんと 3 倍にまで膨れ上がるという結果になりました。何も工夫せずと書きましたが,前述のオンプレ環境における技術やノウハウを適用してもなおこのくらい費用が増えてしまうという状況でした。

そこで,このコスト差を埋めるためにコストコントロール施策の実証が必要になった訳です。検討ではなく,実証です。この時点で候補となるような施策はいくつか挙がっていましたが,それらの施策は本当に実現可能なのか,コストは下げられたが品質が劣化することはないか,運用工数が増えることはないか,などの懸念は実際にやってみなければ払拭できません。クラウド移行がすべて完了した後になってやはり実現は無理でした,という事態は何としても避けなければなりませんでした。

システム基盤のクラウド最適化や移行スケジュールの精緻化など,クラウド化の準備として行うべきことは他にもたくさんありましたが,このコストコントロールの実証はすでにクラウドを用いてインフラ運用を行なっている私のグループで担当することになりました。

机上の計算では 50% の削減が可能

クラウドならではのコスト削減はクラウド化の全社決定がなされる前から様々検討はしていました。本腰を入れて進めるにあたり,まずは考えられる手段を列挙し,それらが仮にうまくいった場合に現状のコスト構造に照らしてどの程度のコスト削減が見込めるか,という試算を行いました。その際,阻害要因となるものも一緒に洗い出しています。

例えばオートスケーリング。担当のシステム構成においては,クラウド費用全体の 90% が IaaS の費用であり,その内 60% 程度がオートスケーリングを適用できるサーバでしたので,オートスケーリングによってこれらのサーバの費用が 20% 減らせると仮定すれば全体の費用は 0.90 * 0.60 * 0.20 で約 10% 程度全体の費用を下げられる,というような試算をしました。また,阻害要因としてはオートスケーリングの仕組みを作るための実装コスト,安定して稼働させるまでの導入コスト,またインフラ運用メンバの工数不足などを挙げていました。

他の施策についても同様に試算を行い,全てうまくいった場合 50% の費用削減が可能であるという皮算用を得ました。

ここで重要なのは,もちろんコストを 50% 程度は下げられそうという試算結果も大事なのですが,どの施策から進めるのが費用削減効果と実施工数両方の観点から良さそうか,という優先順位を決めることです。通常のシステム運用をこなしながらこれらの施策を進めていく必要がありましたので,全部を一気に並列に進めることはできません。この優先順位を決めるにあたっては,コスト削減効果の精緻な見積もりよりも (そもそも実際にやってみないと推測の域を出ないので,精緻な見積もり自体不可能),だいたいのオーダ感とどのくらい大変そうかという工数感の方が必要です。

ただし,数値の算出根拠だけはきちんと説明できるようにしておくべきです。先のオートスケーリングの例では 90%,60%,20% という数値が出て来ましたが,90% と 60% は事実なので定数,20% の値は実際のシステムのワークロードに大きく依存するので変数ということになります。こういう説明ができると各施策の勝算の確度が分かるので,優先度を決めるための判断材料の 1 つになりますし,どのくらい未確定要素を含んでいるのか,という認識を関係者間で正しく合わせることにも役立ちます。

オートスケーリングを最優先で導入

コスト削減試算の結果,効果が最も大きかったのはやはり API サーバへのオートスケーリング導入でした。実はオートスケーリングは突発的なトラフィック急騰時のサービス品質への備えとして自動スケールアウト機能だけはすでに導入できていました。また,時間指定の台数の増減もバッチ処理により何とか動いていました。

しかし,これらの既存の仕組みは相当に辛い状態でした。インスタンスの起動,削除,必要台数の計算などオートスケーリングに必要なあらゆる処理が 1 つの巨大なモジュールに全て記述されており,ここに自動スケールインのロジックを追記したり,各処理に対する冪等性担保のための処理やリトライ,エラーハンドリングを入れていくことはもはや不可能でした。台数の自動増減も非常に原始的で crontab に以下のような記述があるだけでした。

 0 21 * * * api-server-service-in  --ammount 50 # API サーバを 50 台追加
 0  0 * * * api-server-service-out --ammount 50 # API サーバを 50 台削除

そこで,既存の資産は活用しつつオートスケーリングの仕組みを抜本的に改善することにしました。この改善施策自体はクラウド化が決定する前の段階から進めており,これから本番環境に導入していくというフェーズでした。刷新後のオートスケーリングシステムの設計や実装の詳細は今後の記事で他メンバから紹介してもらおうと考えていますので,この記事では本番に導入していく過程や,今現在どのように運用しているかをお話ししたいと思います。

本番環境で実際に運用するにあたっては,何よりもまずは安定性を重視しました。API サーバはゲームサーバの根幹を担うものであり,これの台数管理を司るオートスケーリングシステムを安定して稼働させることは大前提です。そうはいっても 1 日に数百台というサーバを作っては壊しという作業をひたすら行うため,どこかしらで必ず不具合が出てきます。

そのため,まずは各処理に対してきめ細やかにエラーハンドリングやリトライを入れていきました。例として対象サーバに対して ssh 接続できるか確認する,という処理の設定ファイルの一部を以下に示します。詳しい書式や定義はここでは省略するとしてリトライ間隔,リトライ回数,タイムアウトが指定できるようになっていることがお分かり頂けると思います。

 [Task]
 Name = "CheckNetowrkSSH"
 Description = "check network connection SSH"

 [Task.Component.Params]
 RetryInterval = 5
 Retry = 30
 Port = 22
 Timeout = 3

また,実際に ssh 接続確認を行うコードは以下の通りで,当然ではありますがきちんとエラーハンドリングを行うコードになっています。

 func checkTCP(target string, timeout time.Duration) error {
     conn, err := makeConnection("tcp", target, timeout)
     if err != nil {
         log.Error(
             "Failed to make a connection.",
             zap.String("protocol", "tcp"),
             zap.String("target", target),
             zap.Error(err),
         )
         return err
     }
     defer conn.Close()
     return nil
 }

続いて,突発的なトラフィック急騰への対応も必要になります。ゲームサーバというものはイベントの開始・終了のタイミングや,日跨ぎ処理のタイミングでトラフィックがわずか数秒間に 3 倍にまで増えるということが毎日起きます。スケールアウトの高速化の工夫は色々と行いましたが,どれだけに高速化してもサーバ起動からサービス投入までに 3 分は要します。

そのため時間指定でのサーバの増設も必要になります。この機能もバッチ処理ではなくオートスケーリングの機能として折り込みました。現在は以下 (10 分間に 20 台ずつ,計 40 台増設する例) のようにコードで設定を書くようになっていますが,将来的には GUI を用意し,インフラメンバだけでなく開発者やビジネスサイドのメンバにも開放し,事前のキャパシティ増設をワンストップでできるようにしたいと考えています。

 launch => {
     amount        => 40,
     time_settings => [
         {
             from   => '05:50:00',
             to     => '05:59:00',
         },
         {
             from   => '06:00:00',
             to     => '06:09:00',
         }
     ]
 }

さて,オートスケーリングの安定化のための工夫をご説明してきましたが,想定外の出来事はどうしても起きます。そういう事態に備えて,オートスケーリングの不具合を早期に検知できるような監視を導入し,問題を検知したらインフラメンバにオンコールで通知することによって 24 時間 365 日即時対応できるような体制を整えています。ここまで至ることは稀ですが,仮に手動対応が必要になったとしても次回以降同じ問題が発生しないようにシステムを改修するということを繰り返し,現時点では相当高品質なシステムに仕上がりました。

最後に実際にオートスケーリングが稼働している様子をお見せします。図 1 からサーバ台数は日々大きく変動していることが分かる一方,図 2 からは CPU 使用率はほぼ一定となるように制御されていることが分かると思います。この結果,最終的に API サーバの費用は 30% 削減することができました。また,これまで手動で台数調整を行ってきましたが,その手間が不要になりましたので運用工数の削減にも大きく寄与しました。

図 1. サーバ台数 (IP Address 数) の推移

図 1. サーバ台数 (IP Address 数) の推移

図 2. API サーバ 1 台の CPU 使用率の推移 (赤: スケールアウト水準,青: スケールイン水準)

図 2. API サーバ 1 台の CPU 使用率の推移 (赤: スケールアウト水準,青: スケールイン水準)

スポットインスタンスへの挑戦

オートスケーリングの導入に加えて,インスタンスタイプの最新化なども一段落したタイミングで基盤が AWS のシステムにおいてはスポットインスタンス導入を進めていくことなりました。2018 年 8 月の下旬頃から仕様確認や実サービスへの導入方法の検討を本格的に始め,その 2 ヶ月後には実際に運用を開始していましたので,かなりのスピード感で進めていたことがお分かり頂けるのではないでしょうか。

実は計画当初,スポットインスタンスの活用は考慮に入れていませんでした。存在自体は認識していましたが,突然シャットダウンされるという話を聞いただけで,最高品質のインフラを提供するための基盤として利用する選択肢には入れられない,入れられたとしても負荷試験用途やワンショットのバッチ処理などサービスに直接影響が及ばない範囲内で,という考えが正直なところありました。しかし,オンデマンドインスタンスと比較すると非常に安価に利用できるという点はやはり無視できず,このタイミングで俎上に載ってきました。

実際に仕様確認を進めていくと,スポットの中断通知から実際にシャットダウンされるまでの 2 分間で完全にサービスから切り離すことさえできれば,あとは通常のインスタンス故障時の対応を自動化だけで品質を落とすことなく運用に落とし込めるということが分かりました。実際に導入を担当したメンバが「スポットインスタンス は高頻度で故障するインスタンスであると考えれば良い」と話をしていたのが印象的です。

また,オートスケーリングに比べて導入できる対象サーバが多いという点もスポットインスタンスの魅力でした。オートスケーリングの導入対象は,台数が非常に多いこと,負荷の変動が大きいこと,スケーリングの判断基準が明確であること,という大きく 3 つの条件を満たす必要があります。これらを満たさないと十分なコストメリットが得られず,オートスケーリングシステムの管理工数の方がかえって高くつくためです。一方スポットインスタンスであれば,ステートレスなサーバに対しては前述の仕組みさえ整えればコストメリットを得ることができます。最終的には API サーバだけでなく,Cache サーバや非同期処理用の Daemon サーバなどステートレスサーバのほぼ全てに対してスポットインスタンス を導入できました。

インスタンス管理の自動化はオートスケーリングのシステムでほぼできていましたので,2 分以内のサービスからの切り離し処理を教科書通りに実装し,まずは数台実際にサービスに投入してみるというところから導入を開始していきました。

ここから先,スポット率をどう上げていくかというところが一苦労でした。最終的にはセカンダリ IP を活用することで図 3 のように使用するプール数を大量に増やし,安定した運用に落とし込むことができたのですが (詳しくは冒頭の AWS Summit Tokyo 2019 の発表資料にまとめていますので,そちらをご参照下さい),そこに至るまでには様々な試行錯誤がありました。

spot-instance-pool.png

図 3. プールごとのスポットインスタンス数

例えば,異なるインスタンスタイプを混ぜて使うための方法は初めから MyDNS とセカンダリ IP で行こうと決められたわけではありません。インスタンスタイプごとにロードバランサも分け,DNS で重み付けすることよってトラフィックのコントロールを行う方法を検討したり,クラウドベンダに対してロードバランサの重み付けラウンドロビンの機能追加リクエストを出したりもしました。

また技術的な課題だけでなく,スポット率を上げて本当に問題ないかという懸念に対して問題ないと示す必要もありました。幸いにもこのケースにおいてはサービス影響が出る確率を評価することができたため,その定量評価によって関係者全員が納得した状態でスポット率を 100% に設定することができました。リスクの伴う変更を行う際はインフラチームのみの判断ではなく,開発者はもちろん事業部の方々にも十分説明をした上で進めることが重要です。

導入当初は 10% 程度だったスポット率も最終的には 100% まで引き上げることに成功し,ステートレスサーバのコストは 60% 削減することができました。また,スポット中断による障害は起きていないという点も特筆事項だと思います。

spot-instance-rate.png

図 4. スポットインスタンス使用率の推移

DB サーバの QCT マネジメント

DeNA が DB サーバとして MySQL を使用していることはご存知の方も多いと思いますが,クラウドにおいても変わらず MySQL を利用しており,なおかつマネージドサービスではなく IaaS 上に MySQL を立てて運用しています。ここでは DB サーバに対する施策について,ゲームサーバにおける DB サーバの負荷傾向について触れながらご説明します。

DB サーバはゲームのリリース当日から数週間程度 CPU,I/O ともに負荷が高い状態となりますが,特に書き込みがボトルネックになり,これをきちんと捌けるかどうかが安定リリースの要となります。この高負荷に対応するため,DeNA ではシャーディング (水平分割) を多用します。タイトルの規模感に合わせて調整しますが,多いものだとシャード数は 3 桁に及びます。また読み込みのスケーラビリティ確保のために,シャーディングされた DB はそれぞれ master-slave 構成を組んでいます。

一方,リリースからしばらく時間が経つとアクティブユーザの数がリリース当初よりは落ち着いてくるため,CPU 負荷も下がっていきます。他方,データは溜まり続けるため,I/O がボトルネックになるという傾向は変わりませんし,ディスクのサイズがネックになることもあります。

このようにリリースフェーズとその後の運用フェーズで大きく負荷傾向が変わってくる DB サーバの運用は非常に難しいです。ステートレスなサーバと違い,単純な台数調整やスペック調整を行うことが難しいからです。特に難しいのがシャーディングした DB のシュリンクです。一度シャードを広げてしまうと,その後の縮小は容易ではありません。しかし,容易ではないからといって放置するわけにもいきません。大量のシャードを運用し続けると膨大な費用が掛かります。したがってシャードの集約が必須になると同時に,どの程度までシャードを集約できるかがそのまま DB サーバのコストコントロールの成果に繋がります。

シャード集約におけるポイントは 2 つあります。

1 つ目は I/O 性能です。シャードを集約していくわけですから,例えば 2 つのシャードを 1 つに集約したら単純に I/O は 2 倍に増えるということになります。ただでさえ I/O がボトルネックであるのに,集約によってその傾向に拍車がかかるため,当然集約後のサーバに対しては高い I/O 性能が求められます。ネットワークストレージをマウントしたインスタンスではこの I/O を捌くのは難しく,私たちはローカルストレージを持つインスタンスを採用することにしました。AWS であれば i3 あるいは i3en インスタンスが,GCP であればローカル SSD をマウントしたインスタンスがこれに相当します。

ローカルストレージは高い I/O 性能を得られる一方で,インスタンスを再起動したり,インスタンス障害などでインスタンスが止まってしまうとデータが全て消えてしまいます。まさに諸刃の剣です。この問題に対しては,データを 4 重に持つことで対応することにしました。すなわち master 1 台に対して slave は必ず 3 台以上用意するということです。また,万一の自体に備えて MySQL のフルダンプを毎日取得し,クラウドストレージへ保存するという仕組みも整えています。

さて,高い I/O 性能は確保できたので次は 2 つ目のポイントである集約方法について考えていきます。普通にシャード集約を行うことを考えると,あるシャードに対して別なシャードのデータを流し込むというのが一般的な方法だと思います。実際に DeNA でも過去この方法を取っていたことがありました。しかしこの方法ではサービスの長時間の停止を伴うメンテナンスが必須になります。他の目的で実施されるメンテナンスに合わせて実施すれば良いと考えられる方もいらっしゃるかもしれませんが,データの流し込みには何時間も要するため,結局シャード集約のためにメンテナンス時間を延長する必要があります。

そこで私たちは 1 つの MySQL プロセスに対してデータを集約していくという方法は諦め,1 台のインスタンスに複数の MySQL プロセスを立てるという構成を取ることでシャード集約を実現しました。この方法は 1 台の物理サーバの性能を使い切るための方法としてオンプレ環境でも多用しています。

まずは 1 台のインスタンスに複数のプライベート IP アドレスをアタッチします。

 $ ifconfig
 eth0      Link encap:Ethernet  HWaddr 02:E1:0C:0A:6E:8A
           inet addr:10.228.155.252  Bcast:10.228.155.255  Mask:255.255.252.0
           inet6 addr: fe80::e1:cff:fe0a:6e8a/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
           RX packets:1999977249 errors:0 dropped:0 overruns:0 frame:0
           TX packets:4789699563 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:1000 
           RX bytes:2036044138753 (1.8 TiB)  TX bytes:6290789751880 (5.7 TiB)

 eth0:ae49853 Link encap:Ethernet  HWaddr 02:E1:0C:0A:6E:8A
           inet addr:10.228.152.83  Bcast:10.228.155.255  Mask:255.255.252.0
           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1

 eth0:ae49922 Link encap:Ethernet  HWaddr 02:E1:0C:0A:6E:8A
           inet addr:10.228.153.34  Bcast:10.228.155.255  Mask:255.255.252.0
           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1

 eth0:ae49af9 Link encap:Ethernet  HWaddr 02:E1:0C:0A:6E:8A
           inet addr:10.228.154.249  Bcast:10.228.155.255  Mask:255.255.252.0
           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1

 eth0:ae49bd4 Link encap:Ethernet  HWaddr 02:E1:0C:0A:6E:8A
           inet addr:10.228.155.212  Bcast:10.228.155.255  Mask:255.255.252.0
           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1

次に,各プロセスに対する MySQL の設定ファイルを用意し,以下のようなスクリプト (実際に私たちが使っているスクリプトを簡略化して記載) で MySQL プロセスを起動します。

 sub start_mysql {
     my ($hostname, $mysqld_opts) = @_;
     my $pid = fork();
     unless ($pid) {
         close STDIN;
         close STDOUT;
         close STDERR;
         exec "/usr/bin/mysqld_safe --defaults-file=/etc/my-$hostname.cnf $mysqld_opts &";
         exit;
     }
 }

すると以下 (ps auxf の結果を整形) のように複数の MySQL プロセスが起動している状態になります。

 root   /bin/sh /usr/bin/mysqld_safe --defaults-file=/etc/my-ae49853.cnf
 mysql    \_ /usr/sbin/mysqld --defaults-file=/etc/my-ae49853.cnf --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --user=mysql ...
 root   /bin/sh /usr/bin/mysqld_safe --defaults-file=/etc/my-ae49922.cnf
 mysql    \_ /usr/sbin/mysqld --defaults-file=/etc/my-ae49922.cnf --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --user=mysql ...
 root   /bin/sh /usr/bin/mysqld_safe --defaults-file=/etc/my-ae49af9.cnf
 mysql    \_ /usr/sbin/mysqld --defaults-file=/etc/my-ae49af9.cnf --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --user=mysql ...
 root   /bin/sh /usr/bin/mysqld_safe --defaults-file=/etc/my-ae49bd4.cnf
 mysql    \_ /usr/sbin/mysqld --defaults-file=/etc/my-ae49bd4.cnf --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --user=mysql ...

後は集約前の master から上記で起動した MySQL プロセスに対してレプリケーションをつなぎ,master を切り替えていくだけです。DeNA では MHA を利用して MySQL の master 切り替えをオンラインで実施できるようにしていますので,メンテナンスなしでのシャード集約が可能です。

この方法を用いることで,DB サーバの数を最大 75% 削減することに成功したタイトルもありました。また,メンテナンスなしでシャード集約を行える方法が確立したことで,今後はリリース初日は DB サーバに十分なキャパシティを持たせるためにあえて最初から数百から数千のシャードを用意しておき,負荷の様子をみながら無停止でシャードを集約していくという運用が可能になりました。

これまでは DB サーバのシャード集約の難しさや維持コストの高さから,必要最低限のシャード数でリリース当日を迎えることがほとんどでしたが,こうしてしまうと万一予想を上回るトラフィックが押し寄せてしまった場合は長期間の障害は免れませんでした。したがって,今回ご説明した運用方法は超大規模ゲームタイトルの安定リリースに大きく貢献し,なおかつコストも速やかに削減することができる非常に優れた方法であると言えると思います。

最後に

今回は主にオートスケーリング,スポットインスタンス,DB サーバの運用に焦点を当ててこの 1 年間の取り組みについてご紹介させて頂きました。QCT 全てを高い水準で満たすために様々な工夫をしているというところを少しでも感じ取って頂けましたら幸いです。技術的な詳細はかなり省略した箇所もありましたが,その内容については今後他メンバからご紹介させて頂く予定ですので,もっと具体的な話にご興味をお持ちの方は引き続き本ブログをご覧下さい。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

DeNA の AWS アカウント管理とセキュリティ監査自動化

こんにちは、IT 基盤部髙橋です。
DeNA が提供するエンタメ系やヘルスケア系のサービスのインフラを見ており、そのグループのマネージャをしています。
先日 AWS Loft Tokyo で DeNA における AWS セキュリティについて発表してきたので、発表では述べられなかった具体的な設定についていくつか記載します。

presentation.jpg

DeNA の AWS アカウント管理とセキュリティ監査自動化 from DeNA

AWS アカウント管理

AWS アカウント管理を効率的に行うために、スライドの中でいくつかの施策を挙げています。 その中の一部をコマンド例を交えて紹介します。

AWS Organizations を利用したマルチアカウント管理

AWS Organizations の特徴としては以下のようなものが挙げられます。

  • アカウントの一元管理
    • OU (Organization Unit) を用いた階層的なグループ化も可能
  • 一括請求 (Consolidated Billing)
    • アカウントの請求と支払いを統合
  • リザーブドインスタンスの共有
    • 他のアカウントで購入したリザーブドインスタンスを共有することができる
  • サービスコントロールポリシー (SCP) の利用
    • Organization 配下の各 AWS アカウント (メンバーアカウント) の権限を指定できる

また AWS Organizations を利用すればコマンドで AWS アカウントを作成することが可能になります。 連絡先情報やクレジットカード情報は Organization マスターアカウントの情報が引き継がれます。
--role-name には作成されるアカウントへの AdministratorAceess 権限を持つロール名を指定します。デフォルトでは以下に記載している通り OrganizationAccountAccessRole という名前になります。 --iam-user-access-to-billing には IAM ユーザーに請求情報へのアクセスを許可する場合は ALLOW を、拒否する場合は DENY を指定してください。

 aws organizations create-account \
     --email ${EMAIL} \
     --account-name ${ACCOUNT_NAME} \
     --role-name OrganizationAccountAccessRole \
     --iam-user-access-to-billing ALLOW

 

 {
     "CreateAccountStatus": {
         "RequestedTimestamp": 1564398749.614, 
         "State": "IN_PROGRESS", 
         "Id": "car-01234567890abcdefghijklmnopqrstu", 
         "AccountName": "sample-account1"
     }
 }

アカウント作成の状況は以下のコマンドで確認することができます。 State の値が SUCCEEDED になるまで定期的に監視すれば、後続の作業も自動化できます。

 aws organizations describe-create-account-status \
     --create-account-request-id ${ACCOUNT_ID}

 

 {
     "CreateAccountStatus": {
         "AccountId": "123456789012", 
         "State": "SUCCEEDED", 
         "Id": "car-01234567890abcdefghijklmnopqrstu", 
         "AccountName": "sample-account1", 
         "CompletedTimestamp": 1564398812.234, 
         "RequestedTimestamp": 1564398749.696
     }
 }

AWS アカウント作成時の初期設定や設定変更方法の整備

AWS アカウントを作成するときには同時に各アカウントに必要な初期設定もしています。 aws sts assume-role コマンドを利用して設定先アカウントの AdministratorAccess 権限を取得します。 ACCOUNT_ID には上記の出力結果から得られたものを入れます。 --role-session-name は権限を得たロールのセッションの識別子のようなものでここでは適当な値を入れておきます。

 aws sts assume-role \
     --role-arn arn:aws:iam::${ACCOUNT_ID}:role/OrganizationAccountAccessRole \
     --role-session-name "RoleSession1"

上記コマンドで得られた情報から AccessKeyId SecretAccessKey SessionToken の値を以下のように環境変数に設定します。

 export AWS_ACCESS_KEY_ID=ABCDEFGHIJKLMNOPQRST
 export AWS_SECRET_ACCESS_KEY=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN
 export AWS_SESSION_TOKEN=abcdefghijk...<remainder of security token>

まず以下のようにして作成しようとしているロールが存在するか判定します。 ROLE_NAME には作成したいロールの名前を入れてください。

 aws iam get-role --role-name ${ROLE_NAME}

IAM ロールがまだなければ作成をします。 --assume-role-policy-document で信頼関係の内容を記載した json 形式のファイルを指定します。

 aws iam create-role --role-name ${ROLE_NAME} \
     --assume-role-policy-document file://path/to/${ROLE_NAME}-Trust-Policy.json

次に IAM ポリシーの作成をします。 POLICY_NAME には作成したいポリシーの名前を入れてください。 --policy-document でポシリーの内容を記載した json 形式のファイルを指定します。 path/to/${ROLE_NAME}-Policy.json には適切なファイルパスを指定してください。

 aws iam create-policy --policy-name ${POLICY_NAME} \
     --policy-document file://path/to/${ROLE_NAME}-Policy.json

作成した IAM ロールに先ほど作成した IAM ポリシーをアタッチします。

 aws iam attach-role-policy --role-name ${ROLE_NAME} \
     --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/${POLICY_NAME}

このようにして初期設定に必要なロールやポシリーの作成を繰り返し行います。 その他初期設定として CloudTrail の設定やそのログを保管する S3 Bucket の作成なども DeNA では行っています。

全 AWS アカウントの設定変更に関しては、上記で述べた各アカウントに存在する OrganizationAccountAccessRole を使い、同じように aws sts assume-role をして設定変更を行います。 AWS Organizations を使えば全 AWS アカウントのリストを取得することも可能なので、これで取得できた全アカウントに対して操作するようなものを書けばよいでしょう。

 aws organizations list-accounts

 

 {
     "Accounts": [
         {
             "Name": "sample-account1", 
             "Email": "sample-email@dena.jp", 
             "Id": "123456789012", 
             "JoinedMethod": "CREATED", 
             "JoinedTimestamp": 1536059567.45, 
             "Arn": "arn:aws:organizations::123456789013:account/o-abcdefghij/123456789012", 
             "Status": "ACTIVE"
         }, 
         {
             "Name": "sample-account2", 
             "Email": "sample-email2@dena.jp", 
             "Id": "123456789013", 
             "JoinedMethod": "CREATED", 
             "JoinedTimestamp": 1536059568.45, 
             "Arn": "arn:aws:organizations::123456789000:account/o-abcdefghij/123456789013", 
             "Status": "ACTIVE"
         }, 
         ...
     ]
 }

IAM ユーザー管理

発表の中では社内ディレクトリで管理されるフェデレーティッドユーザーを利用した AWS マネジメントコンソールログインの方法について説明しました。

ID フェデレーションによる AWS マネジメントコンソールログイン

下記は踏み台アカウントから各サービスのアカウントに Switch Role を利用してロールの切り替えを行う図ですが、この時の各アカウントに存在するロールのポリシーと信頼関係について解説します。

aws-management-console-login-by-id-federation.png

踏み台アカウントにある step-service-account1-admin ロールのポリシーは以下のようにしています。

 {
     "Version": "2012-10-17",
     "Statement": {
         "Effect": "Allow",
         "Action": "sts:AssumeRole",
         "Resource": "arn:aws:iam::*:role/service-account1-admin"
     }
 }

踏み台アカウントにある step-service-account1-admin ロールの信頼関係は以下のようにしています。
STEP_ACCOUNT_ID には踏み台アカウントのアカウント ID を入れてください。 SAML_PROVIDER_NAME には作成した SAML プロバイダの名前を入れてください。

 {
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Principal": {
         "Federated": "arn:aws:iam::${STEP_ACCOUNT_ID}:saml-provider/${SAML_PROVIDER_NAME}"
       },
       "Action": "sts:AssumeRoleWithSAML",
       "Condition": {
         "StringEquals": {
           "SAML:aud": "https://signin.aws.amazon.com/saml"
         }
       }
     }
   ]
 }

各サービスアカウントにある service-account1-admin ロールのポリシーは以下のようにしています。(ここでは AdministratorAccess 権限となっています。)

 {
     "Version": "2012-10-17",
     "Statement": [
         {
             "Effect": "Allow",
             "Action": "*",
             "Resource": "*"
         }
     ]
 }

各サービスアカウントにある service-account1-admin ロールの信頼関係は以下のようにしています。 STEP_ACCOUNT_ID には踏み台アカウントのアカウント ID を入れてください。

 {
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Principal": {
         "AWS": "arn:aws:iam::${STEP_ACCOUNT_ID}:role/step-service-account1-admin"
       },
       "Action": "sts:AssumeRole"
     }
   ]
 }

上記は AdministratorAccess 権限についての設定例ですが、ReadOnlyAccess やカスタマイズした権限ももたせることが可能になっています。

AWS セキュリティ監査自動化

発表では AWS セキュリティ監査自動化システムの実装例としていくつか述べました。 ここではさらにどのようなコマンドを使って判定するかを具体的に書いていきたいと思います。

ルートユーザーに対して MFA を有効化すること

概要: 認証情報レポートでルートユーザーに対して mfa_active の値が true であるか確認します

まずはレポートの生成をします。

 aws iam generate-credential-report

 

 {
     "State": "STARTED", 
     "Description": "No report exists. Starting a new report generation task"
 }

次に生成したレポートを取得します。

 aws iam get-credential-report

取得された内容は Base64 エンコードされているのでデコードする必要があります。 <root_account> の行で mfa_active の値が true であれば MFA が有効であることになります。

 user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated
 <root_account>,arn:aws:iam::123456789012:root,2018-06-20T13:38:27+00:00,not_supported,2019-07-02T09:23:21+00:00,not_supported,not_supported,true,false,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A
 ...

IAM で利用が完了し使われてないアクセスキーは無効化・削除すること

概要: list-users でユーザ一覧を出し、 list-access-keysStatusActive なものの中で、 get-access-key-last-used から得られる LastUsedDate が特定の期間内であるか確認します

まずは list-users で IAM ユーザ一覧の情報を取得します。

 aws iam list-users

 

 {
     "Users": [
         {
             "Arn": "arn:aws:iam::123456789012:user/sample-user1",
             "UserId": "ABCDEFGHIJKLMNOPQRSTU",
             "CreateDate": "2018-07-23T09:37:10Z",
             "UserName": "sample-user1",
             "Path": "/"
         },
         {
             "Arn": "arn:aws:iam::123456789013:user/sample-user2",
             "UserId": "ABCDEFGHIJKLMNOPQRSTV",
             "CreateDate": "2018-08-01T11:51:47Z",
             "UserName": "sample-user2",
             "Path": "/"
         },
         ...
     ]
 }

次に list-access-keysStatus の情報を取得します。 --user-name には上記で取得できたユーザー名を指定します。

 aws iam list-access-keys --user-name sample-user1

 

 {
     "AccessKeyMetadata": [
         {
             "AccessKeyId": "ABCDEFGHIJKLMNOPQRST", 
             "Status": "Active", 
             "CreateDate": "2019-01-09T08:15:14Z", 
             "UserName": "sample-user1"
         }, 
         {
             "AccessKeyId": "ABCDEFGHIJKLMNOPQRSU", 
             "Status": "Active", 
             "CreateDate": "2019-07-29T16:35:35Z", 
             "UserName": "sample-user1"
         }
     ]
 }

最後に --access-key-id でアクセスキー ID を指定してそのキーの LastUsedDate を求めます。得られた LastUsedDate が特定の期間内であるか確認します。

 aws iam get-access-key-last-used --access-key-id ABCDEFGHIJKLMNOPQRST

 

 {
     "AccessKeyLastUsed": {
         "Region": "us-east-1", 
         "LastUsedDate": "2019-01-09T10:52:00Z", 
         "ServiceName": "iam"
     }, 
     "UserName": "sample-user1"
 }

ELB のアクセスログを有効化すること

概要: describe-load-balancer-attributesaccess_logs.s3.enabled の値が true であるか確認します

aws elbv2 コマンドを使います。 --load-balancer-arn には情報を取得する LB の ARN を指定します。

 aws elbv2 describe-load-balancer-attributes --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/app/sample-alb1/abcdefghijklmnop

以下のように色々な値が取れます。今回は access_logs.s3.enabled の値を確認します。

 {
     "Attributes": [
         {
             "Value": "false", 
             "Key": "access_logs.s3.enabled"
         }, 
         {
             "Value": "", 
             "Key": "access_logs.s3.bucket"
         }, 
         {
             "Value": "", 
             "Key": "access_logs.s3.prefix"
         }, 
         {
             "Value": "60", 
             "Key": "idle_timeout.timeout_seconds"
         }, 
         {
             "Value": "false", 
             "Key": "deletion_protection.enabled"
         }, 
         {
             "Value": "true", 
             "Key": "routing.http2.enabled"
         }
     ]
 }

RDS でインターネットからのアクセスが不要な場合はパブリックアクセシビリティを無効にすること

概要: describe-db-instancesPubliclyAccessiblefalse であるか確認します

aws rds コマンドを使います。 --db-instance-identifier には DB の識別子を指定します。

 aws rds describe-db-instances --db-instance-identifier sample-db1

PubliclyAccessible の値が false であれば問題ないです。(かなり多くの情報が取れるので省略します。)

 {
     "DBInstances": [
         {
             "DBInstanceIdentifier": "sample-db1", 
             "AvailabilityZone": "ap-northeast-1a", 
             "DBInstanceClass": "db.m5.large", 
             "BackupRetentionPeriod": 7, 
             "Engine": "mysql", 
             "PubliclyAccessible": true, 
             ...
         }
     ]
 }

おわりに

以上、DeNA での AWS アカウント管理とセキュリティ監査自動化について説明してきました。 DeNA ではこれまで以上に大規模に AWS を利用していくために、セキュリティレベルの高い状態をより効率的に構築管理できる方法を日々考えています。

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

DeNAインフラノウハウの発信プロジェクトを始めます

20190801_blog_1.jpg はじめまして.DeNA システム本部IT基盤部 部長の金子です. IT基盤部は,DeNA全グループの全てのシステム基盤を横断して管理しているインフラ部門です.

DeNAは様々な事業分野にチャレンジし,多種多様なサービスを展開しています. IT基盤部では,その全てのサービス/システムの成功を支えていくために,インフラ部門として,日々様々な技術的なチャレンジを繰り返しています.

  • システムのクオリティ
  • システムのコスト
  • システムのデリバリー

これらの一見,両立/鼎立が非常に難しい3つの要素,この全てで世界最高の水準を達成することが目標です.

  • システムを24時間365日,安定して動かし「続ける」ためには何が必要なのか
  • 数十万リクエスト/秒のトラフィックや,PBクラスのデータを取り扱うには,どのようなインフラ構成が必要になるのか
  • システムの基盤やアーキテクチャはどのような基準で選択すべきなのか
  • システムの運用コストを1円でも安くするためには何をすればよいのか

DeNA創業から20年間,IT基盤部では常にこのような課題にチャレンジし続け,技術的ノウハウを多く蓄積してきました.

また,昨年2018年の6月に,今までシステム基盤のメインとして使ってきたオンプレミス環境を,全てpublicクラウド環境へ移行させることを意思決定し,プロジェクトを開始しています. プロジェクト名は「クラウドジャーニー」,略して「C-Jプロジェクト」と呼んでいます. 現在,このC-Jプロジェクトも順調に進められており,ここでも新たなノウハウが数多く生まれてきています.

このように,技術的なチャレンジを数多く行い,世界にも誇れるノウハウを蓄積してきた IT基盤部なのですが,今まではそのノウハウをあまり多く発信してきてはいませんでした.

そのような中,ここ最近は外部のカンファレンスやセミナーで登壇する機会を頂くことが増え,自分達のチャレンジから得た学びやノウハウを発信してみたところ,非常に多くの方々からご好評を頂き,また多くの方々から詳細についてお問い合わせも頂きました.

20190801_blog_2.jpg

このように多くのリアクションを頂けることは,純粋にエンジニアとしてのモチベーションになります.そしてなにより,この学びやノウハウをより多くの方々と共有し,みなさまに更に使い倒していただくことによって,みなさまと更にエンジニアリングの高みを目指すことができれば,エンジニアとしてこれに勝る喜びはないと思います. そして,DeNAに少しでも興味を持って頂けるキッカケにもなれば嬉しいと考えています.

そこで,本日から来年の4月までの間のほぼ毎週,我々IT基盤部のメンバがそれぞれの業務で得たノウハウを,このDeNAエンジニアブログにて発信していきたいと思います.

担当者の業務都合により,もしかしたら記事を出せない週もあるかもしれませんが,なにとぞ暖かく見守って頂けますと幸いです.

これから約1年間,我々のノウハウをできるだけ多くの方々にお届けできるよう頑張っていきますので,応援のほど,どうぞよろしくお願いします.

20190801_blog_3.jpg

続きを読む
ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る