記事もろもろ
jemallocに関するチケット
malloc_trim(0)に関するチケット
その他
ld(1)の --as-needed
がデフォルトになった影響でSEGVする場合の対応
LDFLAGS="-Wl,--no-as-needed" RUBY_CONFIGURE_OPTS="--disable-install-doc --enable-shared --with-jemalloc"
こんな感じでオプション設定が必要
ruby 3系であればパッチがあたっている模様
この記事はAWS for Games Advent Calendar 2022の19日目の記事です。
@Spring_MT と言います。
今回は、AgonesをEKS上で動かすためのサンプルを作ったので、そのご紹介ができればと思います。
Agonesはマルチプレーヤーゲーム用のDedicated Game Server(DGS)をKubernetes上で動かすためのOSSです。
Agones自体の説明はいろいろな記事で紹介されているので本エントリーでの詳細な説明は省きます。
このAgonesをAWSのEKSを使って運用するためのベースとなるサンプルがaws-samplesにできたのでそのご紹介をできればと思います。
サンプルのリポジトリは下記になります。
このサンプルで構築できるAgonesのクラスタの大きな特徴は下記のとおりです。
大規模なゲームだと1台のゲームサーバーですべてのユーザーのトラフィックをさばくことはほぼ不可能です。
Agonesは複数のゲームサーバーを管理することが可能です。
本サンプルでは、routing専用のclusterを1つ用意し、複数のゲームサーバーに振り分けできるようにしています。
このサンプルでは、2つのゲームサーバーを構築するようになっています。
本サンプルでは、EKSのクラスターオートスケーラーとしてKarpenterを利用しています。
Karpenterは Kubernetes Cluster Autoscalerの代わりになるものです。
柔軟で高速なコンピューティングリソースを提供することができます。
ここではKarpenterについての詳細には触れませんので、下記サイトなどを参照いただければと思います。
AgonesのPodの配置戦略として Packed
を利用する場合、Pod Affinityを利用します。
Karpenterはv0.9.0でPod Affinityについてサポートされたので、v0.9.0以降であればKarpenterは利用可能です。
Karpenterの設定については下記PRを参照ください。
Agones以外での、Terraformを使ったKarpenterの設定の参考にもなるかと思います。
このサンプルでは、すべてのNodeがGraviton2で構築されています。
Agonesは 1.23、1.24でARM対応を行っています。
これに伴いGraviton2でもAgonesを動かすことが可能となりました。
Graviton2を使うことで、コスト最適化だけでなくワークロードによってはパーフォマンス向上も見込める可能性があります。
こちらについては、ぜひそれぞれの環境で負荷試験して試していただければと思います。
負荷試験のクレジットなどは、お近くのAWS SAまで聞いてみてください。
AgonesのメトリックスをCloudWatchでみれるサンプルもつけています。
ダッシュボードを簡単に構築するためのJSONはレポジトリのexmapleディレクトリ配下に入っていますので、ぜひ使ってみてください。 github.com
本サンプルはTerraformを利用して構築を行います。
セットアップ方法は下記手順を参考に実施をお願いします。
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において、ネットワーク帯域の制御などは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)は、TCP/IPのフィルタリングとアドレス変換行うOpenBSDでの仕組みです。
macOSはBSD系OSなので、pfを使うことでTCP/IPのフィルタリングを行うことになります。(iptablesとかではない)
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コマンドを利用して、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は帯域のコントロール、ネットワークトラッフィクの遅延やパケロス率の制御などを行えるネットワークのエミュレーターです。
いわるゆ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
パケットフィルタ規則の構文 - Oracle® Solaris 11.3 でのネットワークのセキュリティー保護
http://info.iet.unipi.it/~luigi/dummynet/
https://www.jstage.jst.go.jp/article/itej/64/10/64_1473/_pdf
macOSでiptableってあるのかなと思ってコマンドを呼び出そうとしたら、iptabコマンドとipcountコマンドを偶然見つけた。
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コマンドはIPアドレスの範囲の計算をしてくれるコマンドです
例を上げると
% ipcount 192.0.2.0/24 192.0.2/24 192.0.2.0 - 192.0.2.255 [256]
% 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にはIP アドレスを扱うのためのIPAddrクラスがあります。
この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
便利!
MTUは最大転送単位(Maximum Transmission Unit)
MTUはデータリンク層(L2)の性質となる。
データリンク層での最大のフレーム長(データリンク層のパケットを表すときにはフレームが使われる。)
MTUはデータリンク層での性質なので、IP層(L3)のヘッダーまでを含んでいる。
IPはデータリンクの上位層なので、MTUを隠蔽する役割を持つ。
そこで、データリンクを司るルーターなどはMTUに応じてIPデータグラムを分割する(IP fragmentation フラグメンテーション)。 (データグラムはIPなどのネットワーク層以上でのパケット単位のデータ構造を持つプロトコルで利用される表現)
IPヘッダーには分割されたパケットを管理するためのフラグがある。
分割されたIPデータグラムを元のIPデータグラムに戻す再構築の処理は終点の宛先ホストでのみ行われる。
トランスポート層(L4)でTCPを使う場合、一度にTCPで含められるアプリケーションデータのサイズは、MTUからIPヘッダー(20バイト)とTCPヘッダー(20バイトとオプションがあると60バイトまで増える)を除いたバイト数となる。
このサイズをMSS(Mazimum Segment Size 最大セグメント長)と呼ぶ。(セグメントはTCPに含まれるデータの表すときに使う)
TCPでは3ウェイ・ハンドシェイクのときに、コネクション確立要求を送るときに、送信側、受信側ともに自身のMSSを通知し、小さいMSSを採用する。
MSSに沿って、データを区切って送信されるため、IPでの分割は行わず、TCP側で再構築をする。
再送処理はMSS単位で行われる。
MSSを相互に通知している様子
MSSによって分割されたデータを再構築している
https://www.cloudflare.com/ja-jp/learning/network-layer/what-is-mtu/