はじめに
AWSのサービス間の関係性を測定する方法はないだろうか?
昨年 AWSの認定試験の勉強をするに際して、改めてそのサービスの多さに圧倒されました。と、同時にふと「これらのサービス間同士の繋がりって、どうなっているのだろうか?」、「どうにかサービス間の関係性を測定して、可視化する方法はないだろうか?」 ということが気になり始めました。そこで今回、サービス間の繋がりをざっくりと測定できる方法について考えてみました。
[やりたいことのイメージ] AWSサービス間の繋がりを測定・可視化する
当記事における関係性と中心性の定義
理想をいえば、AWSの各サービスを熟知したエキスパートの方々が持つ実践的な知識セットをナレッジグラフのように生成できればいいのですが、なかなかハードルが高いので、、、もっと簡素な方法で行います。その方法とは、AWSの各サービスのガイド(PDFのドキュメント)をもとにサービス間の関係性をリバースしてみる方法です。
関係性の基本的な考え方
当記事における基本的な考え方は、次のようなものです。
『AWSの各サービス(例:EC2)のユーザーガイド・開発者ガイドの中に登場する別のサービス名(例:S3やVPC 等)の登場頻度をサービス同士の繋がりの強さとして捉える』
別の言い方をすると、ガイド内での各サービスへの言及の度合いを繋がりとして捉えます。例えば、EC2のユーザーガイドの中にS3やVPCのサービス名が多く登場すれば、それだけ強い繋がりがあり、逆にガイド内にあまり登場しないサービスとは繋がりが弱いとします。この繋がりの強さ、弱さの尺度を以降、**『重み』**と呼ぶことにします。また、関係性は双方向で測定します。つまり、EC2のガイド内でのS3の言及度合いに加えて、S3のガイド内でのEC2の言及の度合いも測定します。
また、言及の度合いといったときに、ガイド内でのサービス名の登場回数を単純にカウントした数値を採用すると、ガイドの文章量の多寡により偏りが生じる可能性があります。文章量が多ければ相対的にサービス名の登場回数も多くなる可能性がありますし、少ない文章量の中でも確実に登場してくるサービス名には繋がりを感じます。そこでガイドのサイズに応じた標準化も必要になってきます。
以上を踏まえた上で、当記事におけるサービス間の関係性の定義に入ります。
関係性の重み(強さ・弱さ)の定義
当記事における「AWS上のあるサービスAから見た別のサービスBへの関係性の重み」は、
サービスAからサービスBへの関係性の重み = \frac {サービスAのガイドに登場するサービス名Bの登場回数}{サービスAのガイドのテキストサイズ(単位: Mb)}
とします。
重要な役割を果たしているサービスはどれか?
関係性の重みが定義できると、次にそこから派生して「多くのサービスから最も言及されている重要なサービスは何か?」についても測定したいと思います。もちろん、どのサービスにも担っている役割があり、いづれも重要であることは間違いありませんが、当記事では他のサービスから言及される度合いが高いほど重要なサービスとして位置付けようと思います。この重要度のことを**『中心性』**と呼ぶことにして、当記事におけるサービスの中心性の定義に入ります。
中心性の大きさの定義
当記事における「AWS上のあるサービスAが持つ中心性の大きさ」は、
サービスAの中心性 = \sum_{\substack{サービス B \in 測定対象のサービス \\ サービスB ≠ サービス A}} サービスB から サービスA への関係性の重み
とします。
他の定義の仕方(例えば、総和ではなく平均を軸にした定義など)もあると思いますが、今回 他サービスからの重みの総和で定義したいと思います。
今回の関係性で表現できていないこと
ここまでの定義を見てきて分かる通り、今回の当記事で定義する関係性はあくまでガイド内の登場頻度を基にしているので、以下のような関係性は表現できていません。予めご了承下さい。
- サービス間の物理的、実質的な関係性が保証されているわけではない。あくまでガイドに登場したか否か。
- ガイド内の文脈は捉えられていない。単純な繰り返しや脈絡のない場面、また否定の文脈の中での登場も拾う。
この辺り、自然言語処理にお強い方であれば、文脈、類似性等も含めた もっとエレガントな関係性を定義できると思います。
測定対象のサービス
前節で測定したい値を定義したので、次に実際の測定で使用するガイドを選定していきます。
AWSのサービスの母数を把握するために、以下のドキュメントサイトを基準にしました。
また、ガイドのリソース自体は、ドキュメントサイトで提供されているPDFファイルを用いることにします。
AWSの公式ドキュメントサイト
その上で今回、測定対象とするAWSサービスをおおまかに以下の基準で30個 選びました。
- AWSの代表的なサービス (例: EC2、S3、Lambda)
- 色々なサービスとの繋がりがありそうなサービス (例: IAM、CloudWatch、Glue)
- 認定試験にも登場してくるサービス
- サービス名が固有名詞として識別しやすいサービス
逆に、以下のサービスは主要サービスではあるが、以下の理由で今回断念しました。
- EBSは、ユーザーガイドがEC2と兼用(EC2のガイドに包含)になっており、部分抽出したりの特別ハンドリングを入れるのが面倒くさかったので断念。
- CloudFormationは、PDFファイルがドキュメントサイトになかったので断念。
測定対象のサービス一覧
なお、使用したガイドは、2021年末頃の英語版のPDFガイドになります。
# | Category | Service | 使用したガイド (PDF) |
---|---|---|---|
1 | Compute | EC2 | User Guide for Linux Instances |
2 | Lambda | Developer Guide | |
3 | Elastic Beanstalk | Developer Guide | |
4 | Containers | ECS | Developer Guide |
5 | EKS | User Guide | |
6 | Storage | S3 | User Guide |
7 | EFS | User Guide | |
8 | Database | Aurora | Amazon Aurora User Guide |
9 | DynamoDB | Developer Guide | |
10 | RDS | Amazon RDS User Guide | |
11 | Redshift | Redshift Database Developer Guide | |
12 | Security, Identity, & Compliance | IAM | IAM User Guide |
13 | Cognito | Developer Guide | |
14 | Cryptography & PKI | KMS | Developer Guide |
15 | Machine Learning | SageMaker | Developer Guide |
16 | Management & Governance | CloudWatch | CloudWatch User Guide |
17 | Developer Tools | Cloud9 | User Guide |
18 | X-Ray | Developer Guide | |
19 | Networking & Content Delivery | API Gateway | Developer Guide |
20 | CloudFront | Developer Guide | |
21 | ELB | User Guide | |
22 | Route 53 | Developer Guide | |
23 | VPC | User Guide | |
24 | Front-End Web & Mobile | Amplify | User Guide |
25 | Analytics | Athena | User Guide |
26 | EMR | Management Guide | |
27 | Glue | Developer Guide | |
28 | Lake Formation | Developer Guide | |
29 | Application Integration | SNS | Developer Guide |
30 | SQS | Developer Guide |
測定結果
それでは前置きが長くなりましたが、測定結果について述べます。
今回、測定に使用したサービスは前述の30サービスです。
関係性の測定結果
以下、Pandasでの表示結果。全870 (= 30 * 29) 行の結果データは、こちら relationship.csv
- source: ガイドのサービス
- target: sourceガイド内に登場するサービス
- word_count: sourceガイド内でのtargetの登場回数
- text_size_mb: sourceガイドのテキストサイズ(Mb)
- weight: 重み。登場回数をテキストサイズ(Mb)で標準化した値。
中心性の測定結果
以下、Pandasでの表示結果。中心性の大きい順にサービスを並べています。
全30サービスの結果データは、こちら centrality.csv
関係性と中心性の可視化
関係性と中心性をネットワーク図として可視化した図。中心性が大きいサービスほどノードを大きく、関係性の重みが大きいほどエッジを太く表現しています。
[ AWS Service Co-Occurrence Network ]
測定結果の考察
当記事における 中心性 第1位のサービスに輝いたのは IAM 🎉という結果(今回の定義で言うと、選定した30サービスのうち他のサービスから最も高頻度に言及されているサービス)となりました。ま~、そりゃそうか~ 😁。そして、Topの顔ぶれは IAM、EC2、S3、VPC、Lambda、CloudWatch とAWSの中核をなすビルディングブロックで占められたのも納得ですね。また、上位20% (Top6) までの中心性の総和が、全体30サービスの中心性の総和の80%を占めており、パレートの法則も現る。もちろん、今回の結果は測定対象としてどの30サービスを選ぶかで結果も流動的に変わってきます。測定対象のサービスを変えたり、増やしたりすると、新たな考察が得られるかもしれません。
皆さんの興味のあるサービスの中心性はどうだったでしょうか?具体的に、どのサービスが どのサービスへ どれくらいの重み で言及しているかは、前述の relationship.csv をぜひ参照下さい。
今回の記事のメインパートは以上なります。
次節以降は、今回の測定の備忘と再現性のため測定に利用したコードや測定手順を記載しておきます。
備忘:測定手順
検証と再現性のために測定手順を記載しておきます。今回せっかくなので、AWSのサービス間分析をするのにAWS自身のサービスを使ってみました。環境は、SageMaker のJupyter Notebook + Python で実施します。とはいっても、AWS固有の機能を使うわけではないのでローカルの Jupyter Notebookでも可能です。同様の内容をGitHubにもあげています。
先立って手順概要を一文で述べておくと、『AWSのドキュメントサイトから各サービスのユーザーガイドまたは開発者ガイドのPDFファイルをダウンロードしてきて、そのドキュメント中に登場する他のサービス名の単語をSageMaker上でカウントして、どれくらいの頻度でそのサービス名が登場するかで関係性の重みを求めて、それをもとに中心性を算出し、最後にネットワーク図として可視化する』という流れになります。
詳しくは、以下の6ステップになります。
1. SageMaker の起動
SageMaker のコンソール画面から新規にノートブックインスタンスを作成します。今回 特殊な設定は必要なくデフォルトの設定で進めました。ノートブックインスタンスの起動後に Jupyterを開いて、以下のように1つのipynbファイル(今回の分析用のノートブックファイル。ファイル名は任意。)と、『pdf』フォルダー、『text』フォルダーを新規に作成します。
2. ガイドの格納
前出のサービス一覧に載っているガイド(PDFファイル)をAWSのドキュメントサイトからダウンロードします。そして、ダウンロードしたPDFファイル群をJupyter上のpdfフォルダーへと放り込みます。ここで各PDFのファイル名をサービス名にしておきます。今回 このファイル名(=サービス名)を使って検索します。以下のようなフォルダとファイル一覧になります。
3. PDFからテキストへの変換
前ステップで格納したPDFファイルをノートブックを利用してテキストファイルに変換していきます。
まず、今回使うPDF用のライブラリ PyMuPDF を ノートブック上からインストールします。
! pip install pymupdf
次に、pdfフォルダー内のPDFファイル群をテキストファイルに変換してtextフォルダーに格納します。
import glob
import os
import fitz
pdf_paths = glob.glob("./pdf/*.pdf")
for pdf_path in pdf_paths:
pdf_object = fitz.open(pdf_path)
output_text_path = "./text/" + os.path.basename(pdf_path) + ".txt"
# PDFオブジェクトからテキストを抽出して、テキストファイルを作成
with open(output_text_path, "w") as text_file:
for page in pdf_object:
text_file.write(page.get_text("text"))
pdf_object.close()
text_file.close()
上記を実行すると、textフォルダに以下のようなテキストファイル群が作成されます。これで下準備が整いました。次のステップから関係性、中心性を求めていきます。
4. 関係性の重みの算出
それでは関係性の重みを算出していきます。
まずサービス名の登場回数の集計を行っていきます。各サービスのユーザーガイドや開発者ガイドの中から、他のサービス名がどれくらい登場するかを各ガイドごとにカウントします。例えば S3のガイドの中に、EC2やIAMといったサービス名がいくつ登場するか、そしてEC2のガイドの中にいくつS3やIAMといったサービス名が登場するかをカウントします。最後に、カウント結果を集計してテキストサイズで標準化すれば、どのサービスが別のどのサービスをどれくらいの頻度で言及しているかという重みが算出されます。
実際のコードは以下になります。
サービス名の文字列マッチングは正規表現で行います。ガイド内においてサービス名の文字列が登場する箇所は純粋な説明文の中以外に、ARNの例の中、SDKコードの例の中、URLの中、Policy Template の例の中など本質ではないところにも偶発的に登場します。極力、自然な文章の中で登場するパターンを拾うように正規表現を組みました(が、あまり完全ではないです… SDKコード内を拾い気味…)。正規表現でマッチした数をカウントして、最後にPandasで集計します。
あと、すみませんが、現状 以下には対応できておりません。今後の対応課題。
- サービス名称の別称の対応 (『ELB』はカウントされるが、『Elastic Load Balancing』 はカウントされない。)
- 1行毎に正規表現マッチングしているので、サービス名が行を跨ったときの対応 (今回 サービス名が1単語 or 2単語で、行跨りの発生頻度は少ないとは思っていますが)
import pandas as pd
import re
file_info = {}
text_paths = glob.glob("./text/*.txt")
# テキストファイル情報のセットアップ
for text_path in text_paths:
service_name = os.path.basename(text_path).replace(".pdf.txt", "")
file_info[service_name] = {
"path": text_path,
"text_size_mb": os.path.getsize(text_path) / (1024.0 ** 2),
}
relationship_df = pd.DataFrame(
index=[], columns=["source", "target", "word_count", "text_size_mb", "weight"]
)
# 全サービス分の測定を開始
for source_service in file_info.keys():
text_df = pd.read_csv(
file_info[source_service]["path"], sep="\r\n", header=None, names=["text_line"], engine="python",)
text_size_mb = file_info[source_service]["text_size_mb"]
for target_service in file_info.keys():
if source_service == target_service: # 自己参照は除外
continue
# 正規表現のパターン。 ARN、SDKの例やURLの中に登場するサービス名は可能な範囲で除外。
pattern = re.compile("(?i)(^|[- \[{(])" + target_service + "($|[- ,.!?\]})])")
# 1行の中に登場するサービス名をカウント
word_count_df = text_df["text_line"].apply(
lambda text_line: len(pattern.findall(str(text_line)))
)
# 1ガイド分を集計
relationship_df = relationship_df.append(
{
"source": source_service,
"target": target_service,
"word_count": word_count_df.sum(),
"text_size_mb": round(text_size_mb, 3),
"weight": round(word_count_df.sum() / text_size_mb, 3),
},
ignore_index=True,
)
relationship_df = relationship_df.sort_values(["source", "target"]).reset_index(drop=True)
relationship_df
再掲になりますが、結果が以下のように出力されます。weightの部分が、sourceサービスからtargetサービスへの関係性の重みになります。
5. 中心性の算出
次に、先ほど求めた関係性の重みから各サービスの中心性を求めます。単純にtargetのサービスを軸に重みを合算すれば、今回の定義よりそのサービスの中心性が求まります。
centrality_df = (
relationship_df.groupby("target", as_index=False)["weight"]
.agg("sum")
.set_axis(["service", "centrality"], axis=1)
.sort_values("centrality", ascending=False)
.reset_index(drop=True)
)
centrality_df
再掲になりますが、結果が以下のように出力されます。中心性の大きい順に出力しています。
6. サービス間の関係性・中心性の可視化
次にネットワークの可視化に進みます。ここでは2通りのやり方を紹介します。
1つは、SageMaker にもデフォルトでインストールされているネットワーク分析の定番ライブラリ NetworkX を使う方法。
もう1つは、テキストベースのフォーマットを用いてブラウザ上でインターラクティブにネットワークを描画できる Blitzboard を利用する方法です。
NetworkX での可視化
NetworkX での可視化用のコードは、以下になります。
import networkx as nx
import matplotlib.pyplot as plt
# Node のセットアップ
node_list = centrality_df["service"].values
centrality_list = centrality_df["centrality"].values
pos = {
"IAM": (0, 50),
"EC2": (0, -300),
"Elastic Beanstalk": (100, -400),
"Lambda": (-100, -400),
"Amplify": (250, -350),
"RDS": (400, -280),
"Aurora": (450, -230),
"Redshift": (400, -180),
"DynamoDB": (350, -230),
"ECS": (400, -80),
"EKS": (400, 0),
"KMS": (400, 100),
"S3": (400, 185),
"EFS": (400, 265),
"Athena": (300, 400),
"Glue": (225, 350),
"Lake Formation": (150, 400),
"EMR": (225, 450),
"SageMaker": (0, 400),
"API Gateway": (-150, 310),
"CloudFront": (-180, 420),
"ELB": (-320, 420),
"Route 53": (-360, 310),
"VPC": (-300, 240),
"SNS": (-400, 160),
"SQS": (-400, 80),
"CloudWatch": (-400, -70),
"X-Ray": (-370, -230),
#'CloudShell': (-370, -330),
"Cloud9": (-250, -330),
"Cognito": (-370, -330),
}
# Edge のセットアップ
edge_list = []
linewidth_list = []
for index, row in relationship_df.iterrows():
if row["word_count"] > 0:
edge_list.append((row["source"], row["target"]))
linewidth_list.append(row["weight"] / 100)
# 可視化
plt.figure(figsize=(24, 20))
G = nx.MultiDiGraph()
G.add_nodes_from(node_list)
nx.draw_networkx(
G,
pos,
with_labels=True,
edgelist=edge_list,
node_size=centrality_list,
width=linewidth_list,
node_color="darkcyan",
edge_color="darkgray",
alpha=0.7,
font_size=26,
arrowsize=16,
)
plt.show()
再掲になりますが、描画結果は以下になります。中心性が大きいサービスほどノードを大きく、関係性の重みが大きいほどエッジを太く表現しています。
Blitzboard での可視化
続いて Blitzboard。Blitzboardは、PGフォーマット(Property Graph Format)というテキストベースのフォーマットに従いノードとエッジを記述することでインターラクティブにネットワーク図を描画できるライブラリです。Sandbox環境( こちら )もあり簡単に試すことが出来ます。Blitzboard で可視化した結果は以下になります。ノードとエッジは測定結果の全量からはいくつかサマリーして描画しています。実は記事冒頭の [やりたいことのイメージ] の挿絵は、Blitzboardを利用して描画したものになります。
まとめ
今回、AWSの各種ユーザーガイド・開発者ガイドからサービス間の関係性・中心性を測定する試みをやってみました。今回の測定の方法自体は非常に単純で、ガイド内に登場するサービス名の登場頻度だけを基にしています。ですので、そこまで深みのある関係性を表現できたわけではなく、あくまで文字列をなぞった表面的な関係性に過ぎませんが、ざっくり全体感を把握するには手頃な方法かなと思います。
ひとたび関係性が分かると、例えば 新たにあるサービスを学ぶ際に背景知識として他に何のサービスを知っておく必要があるかなどの道標に出来るかもしれません。また、どのサービスを学ぶにあたっても、根底としてIAMは必ず登場してくるというのも改めて認識しました。
最後までお読みいただき、ありがとうございました。