CubicLouve

Spring_MTの技術ブログ

rubyのメモリ周りのメモ

記事もろもろ

techracho.bpsinc.jp

www.joyfulbikeshedding.com

techlife.cookpad.com

jemallocに関するチケット

bugs.ruby-lang.org

malloc_trim(0)に関するチケット

bugs.ruby-lang.org

その他

ld(1)の --as-needed がデフォルトになった影響でSEGVする場合の対応

thatsdone-j.blogspot.com

bugs.ruby-lang.org

bugs.ruby-lang.org

github.com

LDFLAGS="-Wl,--no-as-needed" RUBY_CONFIGURE_OPTS="--disable-install-doc --enable-shared --with-jemalloc"

こんな感じでオプション設定が必要

ruby 3系であればパッチがあたっている模様

KarpenterとGraviton2を使った大規模ゲーム向けAgones on EKS を立ち上げるサンプル multi-cluster-allocation-demo-for-agones-on-eks の紹介

この記事はAWS for Games Advent Calendar 2022の19日目の記事です。

@Spring_MT と言います。

今回は、AgonesをEKS上で動かすためのサンプルを作ったので、そのご紹介ができればと思います。

Agones

Agonesはマルチプレーヤーゲーム用のDedicated Game Server(DGS)をKubernetes上で動かすためのOSSです。

Agones自体の説明はいろいろな記事で紹介されているので本エントリーでの詳細な説明は省きます。

このAgonesをAWSのEKSを使って運用するためのベースとなるサンプルがaws-samplesにできたのでそのご紹介をできればと思います。

サンプルのリポジトリは下記になります。

github.com

このサンプルで構築できるAgonesのクラスタの大きな特徴は下記のとおりです。

  1. Agonesの複数クラスタのサポート
  2. Karpenterのサポート
  3. ARM(Graviton2)のサポート
  4. Amazon CloudWatchを使ったAgonesのダッシュボードの作成

1. Agonesの複数クラスタのサポート

大規模なゲームだと1台のゲームサーバーですべてのユーザーのトラフィックをさばくことはほぼ不可能です。

Agonesは複数のゲームサーバーを管理することが可能です。

agones.dev

github.com

本サンプルでは、routing専用のclusterを1つ用意し、複数のゲームサーバーに振り分けできるようにしています。

https://github.com/aws-samples/multi-cluster-allocation-demo-for-agones-on-eks/raw/main/imgs/overview.png

このサンプルでは、2つのゲームサーバーを構築するようになっています。

2. Karpenterのサポート

本サンプルでは、EKSのクラスターオートスケーラーとしてKarpenterを利用しています。

Karpenterは Kubernetes Cluster Autoscalerの代わりになるものです。

柔軟で高速なコンピューティングリソースを提供することができます。

ここではKarpenterについての詳細には触れませんので、下記サイトなどを参照いただければと思います。

aws.amazon.com

karpenter.sh

AgonesのPodの配置戦略として Packed を利用する場合、Pod Affinityを利用します。

Karpenterはv0.9.0でPod Affinityについてサポートされたので、v0.9.0以降であればKarpenterは利用可能です。

Karpenterの設定については下記PRを参照ください。

Feature/karpenter by SpringMT · Pull Request #7 · aws-samples/multi-cluster-allocation-demo-for-agones-on-eks · GitHub

Agones以外での、Terraformを使ったKarpenterの設定の参考にもなるかと思います。

3. ARM(Graviton2)のサポート

このサンプルでは、すべてのNodeがGraviton2で構築されています。

Agonesは 1.23、1.24でARM対応を行っています。

これに伴いGraviton2でもAgonesを動かすことが可能となりました。

Graviton2を使うことで、コスト最適化だけでなくワークロードによってはパーフォマンス向上も見込める可能性があります。

aws.amazon.com

こちらについては、ぜひそれぞれの環境で負荷試験して試していただければと思います。

負荷試験のクレジットなどは、お近くのAWS SAまで聞いてみてください。

4. Amazon CloudWatchを使ったAgonesのダッシュボードの作成

AgonesのメトリックスをCloudWatchでみれるサンプルもつけています。

https://github.com/aws-samples/multi-cluster-allocation-demo-for-agones-on-eks/raw/main/imgs/dashboard.png

ダッシュボードを簡単に構築するためのJSONはレポジトリのexmapleディレクトリ配下に入っていますので、ぜひ使ってみてください。 github.com

構築方法

本サンプルはTerraformを利用して構築を行います。

セットアップ方法は下記手順を参考に実施をお願いします。

GitHub - aws-samples/multi-cluster-allocation-demo-for-agones-on-eks: Terraform code to deploy multi-cluster configuration Agones on Amazon EKS

さいごに

AgonesはAWS上で動かすことは可能です。

さらに、このサンプルを使うことで、AWSの最大のメリットといってもよいGraviton2(さらにはGraviton3)を使うことができるうえに、Karpenterを使いリソースを最適化することができます。

ぜひこのサンプルを触っていただき、AgonesをEKSで動かしてみてはいかがでしょうか?

このサンプルを試したあとは必ずclean upをお忘れなく。

https://github.com/aws-samples/multi-cluster-allocation-demo-for-agones-on-eks#clean-up

ダラダラ考えるということ

仕事で新しいアイディアをその場で生み出すことは難しいと思います。

自分も、なにか新しいアイディアぽいものを言い出すとき、その場で思いついたものはほとんどありません。

そこで、自分なりにどういうことをしているかを改めて考えたとき、

ダラダラ考える

ということをしているのではと思ったのでそのメモぽいものを残します。

アイディアの源泉

一人で新奇性があるアイディアを思いつことはないでしょう。

なにかしらの情報に触れ、その情報をもとに応用していくことがほとんどだと思います。

なので、色々な情報に触れることがまずは重要です。

自分も本でもTwitterでもなんでもいいのでいろいろな情報に触れるようにしています。

そのときに気をつけていることは、自分で情報を咀嚼することです。(自分にとって重要か重要じゃないかくらいでも全然よいと思っています)

本であれば、線を引くなどの行為を行います。

自分の場合は 三色ボールペンで読む日本語 で書いてある方法をそのまま実践しています。

本に線を引く以外は特にメモを残したりはしていません。

これを行うことで、自分の感覚的には脳になにかしら書き込まれる感じがしています。

情報に触れたあとは?

そのあとはただ必要になるタイミングまで寝かしておけばよいです。

なにか折りに触れて似たような情報に接触すると、思い出すことがあるのでそのときに改めてなにかを考えるようにしています。

自分は物理本を保持する流派なんですが、本の整理などで手に取るタイミングで線を引いたところをパラパラと見返して あー こんなことあったなあ くらいの感じで思い返したりします。

こういうのを数年繰り返していると重要な情報は繰り返し登場するので、その度に理解が深まっていったり、その時その時の自分の課題によって視点が変わって新しい気付きがあったりします。

ふと思い出すこともあるので、そのときも情報を改めて頭の中で整理してみたりします。

なんとなくの自分の感覚

こうやって脳に一度何かしらの情報を残しておくと、忘れたと思っていてもバックグランドで処理されて整理されている気がして、長くやっていると色々と思いつくことが増えている感触があります。

なんかこういう研究している人いないかな。

ダラダラ考えるの重要じゃないですかね?

Macで自由にパケットのフィルタリング、帯域制限、パケロス率の設定をする

Macにおいて、ネットワーク帯域の制御などはNetwork Link Conditionerで対応可能です。

下記ページよりAdditional Tools for Xcode を探してdmgをダウンロードし、その中にあるNetwork Link Conditioner.prePane からインストール可能です。

https://developer.apple.com/download/all/

そうすると、システム環境設定の中にNetwork Link Conditionerがあるので、そこから利用可能です。

このNetwork Link Conditionerを利用すれば、帯域幅の制限やパケロス率の設定などを簡単に実現できます。

ただし、UDPパケットを全部落としつつ、TCPパケットについて帯域幅やパケロス率を設定したいという複雑なネットワーク環境を再現するためにはNetwork Link Conditionerだけでは実現できませんでした。

そこで、macOSにある、packet filterとdummynetを組み合わせることでこのような複雑なネットワーク環境を再現できました。

この記事ではpacket filterやdummynetの仕組みを紹介しつつ、どのように設定すれば、このような複雑なネットワーク環境を再現できるかをご紹介します。

実際 、Network Link Conditionerはpacket filterやdummynetをGUIからうまく設定できるような感じになっています。

packet filter(pf)

packet filter(pf)は、TCP/IPのフィルタリングとアドレス変換行うOpenBSDでの仕組みです。

macOSBSD系OSなので、pfを使うことでTCP/IPのフィルタリングを行うことになります。(iptablesとかではない)

pf.conf

packet filterの設定ファイルになります。

ここでパケットフィルターの設定をします。

macOSだと、/etc/pf.conf というファイルが最初からあるかと思います。

ここでは、 pf.conf において、パケットフィルタリングについての設定についてのみ言及します。

特定のIPアドレスからのUDPパケットを落とす設定例は下記のとおりです。

block return in proto udp from 192.0.2.1 to any
  • アクション(1つ目の区切り) パケットをブロックするための block というアクションを指定しています。 2つ目の指定で return としていますが、これは、tcpパケットに対して TCP RST を返し、UDP およびその他のパケットに対して ICMP UNREACHABLE を返すようにするという指定です。 単純に落とすだけであれば、drop を指定してください。

  • in以降のオプション in ( または out ) を指定することで、 受信パケット(送信パケット)を対象にするかを指定します。

proto で対象となるprotocolを指定します。今回は udp を指定しています。

from と toでそれぞれ、送信元アドレスやポート 宛先アドレスやポートを指定します。

他でできることは、 man pf.conf で確認してもらえると。

他にもアクションは様々設定できます。

pfctlコマンドとオプション(抜粋)

pfctlコマンドを利用して、packet filterの状況やpf.confの反映を行います。

確認オプション

-s のあとに続く文字列で取得できるもの選べます。

-v をつけることでより詳細な情報をみれるのと、-v を複数回書くことで詳細度が上がります。

気をつけたいのは、 -vvv とかではなく、 -v -v などと複数回書く必要があることです。

-si or -s info

filterの統計情報などが見れます

-sr or -s rules

現在ロードされているルールを表示します。

反映

-f でルールの反映を行います。 ファイル or 標準出力でも可能。

n をつけることで実際には反映させずparseだけ行うようにできます。

sudo pfctl -f /etc/pf.conf

dummynetとdnctlコマンド

dummynetは帯域のコントロール、ネットワークトラッフィクの遅延やパケロス率の制御などを行えるネットワークのエミュレーターです。

いわるゆtraffic shaperですね。

BSD系のOSに組み込まれており、macOSにも標準でインストールされています。

このdummynetはdnctlコマンドで制御を行います。

dnctlコマンドではpipeを使って帯域幅、遅延、キューサイズ、パケロス率を設定できます。

コマンド例

dnctl pipe 1 config delay 250ms bw 1Mbit/s plr 0.1

ここでは、遅延250 ms 、帯域幅 1M bit/s、パケロス率10%を pipe 1に設定するコマンドになっています。

dnctlだけではこの設定はつかわれません。

packet filterを使ってdummynetの設定にトラフィックを流すようにします。

この設定については、dnctlのmanにしかなく、pf.confのmanには記載されていないので要注意です。

pf.confには下記のように設定します

dummynet in quick proto tcp all pipe 1

この場合tcpのパケットがpipe 1のdummynetを通ることになります。

dnctlで設定した内容は

dnctl pipe show

こんな感じで見れます。

全部合わせてみると

UDPパケットを全部落として、TCPパケットについてはパケロス10%遅延250ms 帯域 1M bit /s にしてみます。

dnctlで下記を設定

dnctl pipe 1 config delay 250ms bw 1Mbit/s plr 0.1

pf.confには下記を設定

block return in proto udp from 192.0.2.1 to any
dummynet out quick proto tcp all pipe 1

参考

OpenBSD PF: User's Guide

PF: OpenBSD パケットフィルター

トラフィックシェーピング - Wikipedia

パケットフィルタ規則の構文 - Oracle® Solaris 11.3 でのネットワークのセキュリティー保護

http://info.iet.unipi.it/~luigi/dummynet/

https://www.jstage.jst.go.jp/article/itej/64/10/64_1473/_pdf

Bandwidth Throttling on macOS - Three f plus one

qiita.com

iptabコマンドとipcountコマンド

macOSでiptableってあるのかなと思ってコマンドを呼び出そうとしたら、iptabコマンドとipcountコマンドを偶然見つけた。

iptabコマンド

iptabコマンドはIPv4形式での各ネットマスクに応じたアドレス数やプレフィックスの一覧を表示をする

% iptab
+----------------------------------------------+
| addrs   bits   pref   class  mask            |
+----------------------------------------------+
|     1      0    /32          255.255.255.255 |
|     2      1    /31          255.255.255.254 |
|     4      2    /30          255.255.255.252 |
|     8      3    /29          255.255.255.248 |
|    16      4    /28          255.255.255.240 |
|    32      5    /27          255.255.255.224 |
|    64      6    /26          255.255.255.192 |
|   128      7    /25          255.255.255.128 |
|   256      8    /24      1C  255.255.255.0   |
|   512      9    /23      2C  255.255.254.0   |
|    1K     10    /22      4C  255.255.252.0   |
|    2K     11    /21      8C  255.255.248.0   |
|    4K     12    /20     16C  255.255.240.0   |
|    8K     13    /19     32C  255.255.224.0   |
|   16K     14    /18     64C  255.255.192.0   |
|   32K     15    /17    128C  255.255.128.0   |
|   64K     16    /16      1B  255.255.0.0     |
|  128K     17    /15      2B  255.254.0.0     |
|  256K     18    /14      4B  255.252.0.0     |
|  512K     19    /13      8B  255.248.0.0     |
|    1M     20    /12     16B  255.240.0.0     |
|    2M     21    /11     32B  255.224.0.0     |
|    4M     22    /10     64B  255.192.0.0     |
|    8M     23     /9    128B  255.128.0.0     |
|   16M     24     /8      1A  255.0.0.0       |
|   32M     25     /7      2A  254.0.0.0       |
|   64M     26     /6      4A  252.0.0.0       |
|  128M     27     /5      8A  248.0.0.0       |
|  256M     28     /4     16A  240.0.0.0       |
|  512M     29     /3     32A  224.0.0.0       |
| 1024M     30     /2     64A  192.0.0.0       |
| 2048M     31     /1    128A  128.0.0.0       |
| 4096M     32     /0    256A  0.0.0.0         |
+----------------------------------------------+

ipcountコマンド

ipcountコマンドはIPアドレスの範囲の計算をしてくれるコマンドです

例を上げると

IPアドレス範囲の表示

% ipcount 192.0.2.0/24
  192.0.2/24   192.0.2.0 - 192.0.2.255     [256]

IPアドレス範囲の計算

% ipcount 192.0.2.0 - 192.0.2.127
  192.0.2.0/25  192.0.2.0 - 192.0.2.127     [128]

中途半端な範囲でも計算してくる

% ipcount 192.0.2.0 - 192.0.2.100
    192.0.2.0/26        192.0.2.0 - 192.0.2.63      [64]
   192.0.2.64/27      192.0.2.64 - 192.0.2.95      [32]
   192.0.2.96/30      192.0.2.96 - 192.0.2.99      [4]
   192.0.2.100/32     192.0.2.100 - 192.0.2.100     [1]

rubyのIPAddrクラスを使えばIPアドレスがCIDR範囲に含まれるかもチェックできる

rubyにはIP アドレスを扱うのためのIPAddrクラスがあります。

docs.ruby-lang.org

このIPAddrクラスを使えば、IPアドレスがCIDR範囲に含まれるかもチェックできます。

irb(main):001:0> require 'ipaddr'
=> true
irb(main):002:0> cidr_sample = IPAddr.new("192.0.2.0/24")
=> #<IPAddr: IPv4:192.0.2.0/255.255.255.0>
irb(main):003:0> cidr_sample.include? "192.0.2.1"
=> true
irb(main):004:0> cidr_sample.include? "192.0.2.255"
=> true
irb(main):005:0> cidr_sample.include? "192.1.2.0"
=> false

便利!

参考

datatracker.ietf.org

MTUについて

MTUとは

MTUは最大転送単位(Maximum Transmission Unit)

MTUはデータリンク層(L2)の性質となる。

RFC 791: Internet Protocol

データリンク層での最大のフレーム長(データリンク層のパケットを表すときにはフレームが使われる。)

MTUはデータリンク層での性質なので、IP層(L3)のヘッダーまでを含んでいる。

IPはデータリンクの上位層なので、MTUを隠蔽する役割を持つ。

そこで、データリンクを司るルーターなどはMTUに応じてIPデータグラムを分割する(IP fragmentation フラグメンテーション)。 (データグラムはIPなどのネットワーク層以上でのパケット単位のデータ構造を持つプロトコルで利用される表現)

IPヘッダーには分割されたパケットを管理するためのフラグがある。

https://packetpushers.net/wp-content/uploads/2019/11/IPv4-Headers-Standard-Fragmentation-Highlighted.png

分割されたIPデータグラムを元のIPデータグラムに戻す再構築の処理は終点の宛先ホストでのみ行われる。

TCPの場合

トランスポート層(L4)でTCPを使う場合、一度にTCPで含められるアプリケーションデータのサイズは、MTUからIPヘッダー(20バイト)とTCPヘッダー(20バイトとオプションがあると60バイトまで増える)を除いたバイト数となる。

このサイズをMSS(Mazimum Segment Size 最大セグメント長)と呼ぶ。(セグメントはTCPに含まれるデータの表すときに使う)

TCPでは3ウェイ・ハンドシェイクのときに、コネクション確立要求を送るときに、送信側、受信側ともに自身のMSSを通知し、小さいMSSを採用する。

MSSに沿って、データを区切って送信されるため、IPでの分割は行わず、TCP側で再構築をする。

再送処理はMSS単位で行われる。

手元でWireSharkで確認してみた例

MSSを相互に通知している様子

MSSによって分割されたデータを再構築している

参考文献

インターネット用語1分解説~MTUとは~ - JPNIC

https://www.cloudflare.com/ja-jp/learning/network-layer/what-is-mtu/

【図解】MTUとMSS, パケット分割の考え方 ~IPフラグメンテーションとTCPセグメンテーション~ | SEの道標

MTUの最適値を調べる方法 | server-memo.net