Linuxのメモリ使用状況を把握したい

はじめに

仕事で、担当しているシステムがハングアップしたような事象が起こりました。その際、sarコマンドの結果を見て、「メモリが枯渇している」という説明を受けたのに対して違和感を感じました。

freeでの使用状況は枯渇していないことを示していますし、スワップ使用量が増えるようなこともありませんでした。一方で、自分の説明に説得力をもたせることができなかったので、ソースコードを調べて観ました。

誤り等見つけたらご指摘いただけるとありがたいです。

対象サーバ

Redhat Enterprise Linux 7.7

freeコマンド

freeはRHEL6系とRHEL7系で出力結果が変わって、7系では使用量にキャッシュ等は含まない、その時点で利用可能なメモリ量をavailable で示してくれるようになったと理解しています。

今回確認したソースコードは以下のURLからとってきました。後から気づいたのですが、サーバのfreeのバージョンを調べて該当のソースを読むべきでした。
https://gitlab.com/procps-ng/procps

ざっくりとした構造

freeはコマンドなので、main関数が起点ですね。なので、そこから読みました。

今回、freeをオプション等なしで実行したパターンを想定したので、オプション解析などはすべてスキップします。そのため、重要なのは326行目からのdo while ループです。
見ると分かりますが、353行目以降は、出力のためのコードで、メモリの値を取り出しているのは349行目の meminfo() だけと推測しました。

meminfo関数

定義は、同じディレクトリのsysinfo.c に書かれていました。細かいことは省くと、おそらく754行目からのforループが/proc/meminfo の情報をmemtableの各変数にセットしているように思いました。

  for(;;){
    tail = strchr(head, ':');
    if(!tail) break;
    *tail = '\0';
    if(strlen(head) >= sizeof(namebuf)){
      head = tail+1;
      goto nextline;
    }
    strcpy(namebuf,head);
    found = bsearch(&findme, mem_table, mem_table_count,
        sizeof(mem_table_struct), compare_mem_table_structs
    );
    head = tail+1;
    if(!found) goto nextline;
    *(found->slot) = (unsigned long)strtoull(head,&tail,10);
nextline:
    tail = strchr(head, '\n');
    if(!tail) break;
    head = tail+1;
  }

kb_main_used が kb_main_total から (kb_main_free と kb_main_cached と kb_main_buffers の合計)を引いているのと、kb_main_cached が kb_page_cache と kb_slab_reclaimable の和になるように計算していますが、それ以外は、/proc/meminfoの値をそのまま使っているように思いました。

freeの使用量にはキャッシュやバッファを含まないと結論付けました。

topコマンド

次にtopコマンドを調べました。なんとなくfreeと同じだろうと予測があったので、ざくっとしか読んでいません。

top.cのmain関数から、frame_make() → sysinfo_refresh(int forced) → meminfo() と遷移しているのを追えました。このmeminfoはfreeで読んだものと同じなので、おそらくtopとfreeの出力はだいたい同じだと理解しました。

sarコマンド

さて問題のsarコマンドです。sarの出力は、キャッシュなども使用量に含むので、freeとかよりも使用率が高く出るでしょ、という点を確認したかったのでした。

今回確認したソースコードは以下のURLからとってきました。
https://github.com/sysstat/sysstat

最初、sar.c のmain関数から読み始めたのですが、いろいろ複雑な処理をしているように見えたので、途中で方針を変えて、出力する部分を中心に読むことにしました。どうやらpr_stats.c が出力部分を担当しているようなので、この中の stub_print_memory_stats 関数から読み始めました。

すると、526行目で空きメモリを計算して、534行目でシステムのメモリ総量から空きメモリを引いて、使用量を計算しています。あれれ?これだとfreeと一緒だな、となりちょっと困惑。

その後で、ChangeLogを読んでみると、version11.7.4でfree(1)やtop(1)の出力と合わせるように、計算方法変えましたという記述がありました。
https://github.com/sysstat/sysstat/blob/master/CHANGES#L371

対象のサーバのバージョンを調べると sysstat バージョン 10.1.5 だっったので、該当のバージョンのファイルを調べると、キャッシュとかバッファとか込みで計算しているのが確認できました。
https://github.com/sysstat/sysstat/blob/v10.1.5/pr_stats.c#L443

なお、メモリ使用状況の情報源はfreeとかtopと同様に/proc/meminfo のように思いました。(自信ないです。)

まとめ

バージョン11.7.4より前のsarでは、使用量にキャッシュやバッファを含んでいるので、その使用率を見て「メモリが逼迫している」と言ってはいけないと結論づけました。

Linuxのメモリ管理やキャッシュの仕組みの理解が甘いことを痛感したので、この本を読み直そうと思います。「詳解 システム・パフォーマンス」も読みかけですし、しっかりと理解できるようにしたいと思います。

PCIの勉強続き

はじめに

以前、PCIについての記事を書いたのですが、わかったようなわからないような感じだったのでもうちょっと勉強してみました。

なんとなく分かってきた気がしています

8割方、気のせいだと思います。

最初にたどり着いたのがQiitaのページです。そこから東北学院大学のページを知り、読んでみました。

どうやら、CPUにはIOポートを直接操作する命令が用意されていて、それを使って指定のアドレスを読み書きするか、メモリマップドの場合は対応するメモリを読み書きすることでPCIのデバイスを操作できるようになる、というもののようだと理解しました。

この後は

この記事が良さそうだったので、再読しようと思います。(以前読んだことが合ったのですが、当時はチンプンカンプンでした。今ならもうちょっと分かるかな?)

ちょっと話はそれますが、上記のURLやRui Ueyamaさんのコンパイラブックのような有料の書籍として出しても十二分に価値があるコンテンツを無料で誰でも読めるようにしてくれているというのはとてもありがたいですね。感謝しかありません。

さらに、有料の書籍であっても、数千円で自力では到底調べきれないであろう知見を共有してくれることは大変ありがたいです。お金出して買っているから、という態度ではなく、知見を共有してくれることに感謝を忘れずにいたいと思います。IT系の書籍でなく小説などでも、やはり書いてくれる方がいるから我々も楽しむことができるわけなので、感謝を忘れずにいようと思います。

2021/03/28追記

上記のURLの他にこのサイトも見て、ちょっと分かってきたかなーという感じです。

どうやらPCI Configuration Spaceは0x0cf8に、Config Dataは0x0cfcにマップされているらしいです。Configuration Spaceへバス番号、デバイス番号、ファンクション番号を指定して読み書きしたいレジスタをセットして、0x0cfcの値を読み出したり、そこへ値を渡したりすることで読み書きができる、というようですね。

SATAやネットワークカードの場合に、具体的にどのようにデータを読み書きするのかまではたどり着けていない感じですが、一歩進んだ手応えを感じ中です。

PCIとLinux

はじめに

単なるメモです。なんでだったのか思い出せませんが、PCIに関する記事を読んで調べ始めたもののあまり理解できていないので、どんな内容を読んだか記録しておいて、後でもう一度学習し直そうと思います。

何を読んだのか

たしか、最初はこれでした。

実際にlspciとかやってみて、なんだか理解できたような気がしてきました。が、一方でなんとなくあやふやで、理解できてないような感じがして、これを読みました。

しかし、やっぱり理解できたようなできていないような感じがぬぐえず。
その原因は、CPUとPCIとの関係(IO空間だったりメモリマップドIOだったり)を理解できていないからかなーと思うようになりました。

もっときちんと理解したいので

まずは、PCIの規格について少し理解してみようと思い、このページを読もうとしています。

その後で、CPUとのインターフェースなども勉強してみようと思います。この点は良い資料を見つけられていないので、またゆっくり探してみようと考え中です。インテルの情報を読むのが一番間違いなさそうではあるのですが、いきなりそこに突っ込むと挫折する確率大なので、迷い中です。

Muttとgetmailを導入した話

はじめに

もともとメインのPCとしてThinkPad x61sを使ってきたのですが、さすがにスペックが弱いからか最近、動きがもっさりとしてきました。(OSはDebian10)

x220の方にLinux Mintを入れていたので、そちらを使うことが増えてきたので、メールの移動も兼ねてMuttとgetmailを導入した、という話です。

getmail

・getmail
http://pyropus.ca/software/getmail/

aptでぱぱっとインストールして、前のPCからgetmailrcをもってきて、とやったらGmailの認証エラーがでました。うーん?よく把握していないのですが、どうもOauth2を使った認証の方が良さそうということで、認証をOauth2に切り替えました。

仕組みをよく理解できていないので、とりあえず下記URLの記事を参考にgmail側の手順を済ませました。後日、ちゃんと勉強しないと、と思います。

・NeomuttでGmailにOAuth2.0する
https://qiita.com/ksuzuki_TVDn1/items/9b2f8d1f24ed81456793

getmailrc側の設定はこの記事のとおりで大丈夫でした。

・getmail-5.6 XOAUTH2 configuration for gmail
https://www.bytereef.org/howto/oauth2/getmail.html

メール落としてくるのに時間かかりました…

Mutt

Muttもaptでインストールして、元のPCのmuttrcを移動して完了です。

GPGで署名したメールと暗号化したメールがちゃんと検証・復号できているのか確認する作業が残っていますが、とりあえずは完了です。

2020/08/28 追記

署名と暗号化は問題なかったのですが、Gmail宛のSMTPで苦労しました。
まず、Gmail宛のSMTP設定ファイルを作成して読み込むようにします。

~.mutt/gmail.rc というファイルを作ります。

# Gmail SMTP Setting                                                                                                                        
 
set smtp_url = "smtp://#{自分のメールアドレス}@smtp.gmail.com:587/"
set smtp_authenticators = "oauthbearer"
set smtp_oauth_refresh_command = "/path/to/oauth2.py --quiet   
    --user=#{自分のメールアドレス}\
    --client_id=#{client_id}\
    --client_secret=#{client_secret}\
    --refresh_token=#{refresh_token}

それを読み込むようにmuttrcに設定します。

source "~/.mutt/gmail.rc"

とりあえず、これでOKです。

GPGで保護する

せっかくなので、認証周りの情報が書いてあるファイルはGPGで保護しようと思います。

gpg -r #{自分のメールアドレス}.com -ea gmail.rc

muttrc の記載を修正。ちゃんと送信できることを確認して、元のテキストファイルはバックアップメディアに移して削除しました。

source "gpg -d ~/.mutt/gmail.rc.asc |"

ファイルシステムが壊れたらDHCPクライアントも暴走した話

はじめに

職場で一部の人から「ネットワークにつながらない」という申告があり、調べたら自分が原因でした、というお話です。

朝のことです

一部の職員から「ネットワークにつながりません」という申告がありました。朝で担当の職員がいなかったこともあり、自分が代わって対応しました。

確認できたのは以下のような事象でした。

  • 会社の無線LANのうち、あるSSIDではつながらない
  • 別のSSIDなら大丈夫
  • ipconfigしてもらうと、つながらないというSSIDはIPを取得できていない
  • Wifiはつかめているっぽい

PCのIPアドレスはDHCPなので、DHCPサーバになにか問題が起きたかなぁというのが私の見立て。

しかし、この時点でいくつか疑問が。

  • DHCPサーバの不具合なら、別のSSID(別セグメント)では問題起きないのは不思議
  • DHCPリレーの不具合でも同様
  • 問題のあるSSIDも、大丈夫な人と大丈夫じゃない人がいる。DHCPサーバの問題なら大丈夫な人がいるのも不思議。
  • 大丈夫じゃない人も複数いる。PC再起動してもダメ。PC起因が複数台で同時発生というのもありえなくないけど、確率低い

そんなこんな考えると、IPアドレス枯渇が一番ありえそうな気がする一方で、つながらない人が全員、後から接続しようとしたわけじゃなく、大丈夫な人よりも先につなごうとしていたので、はっきりと断言できるわけでもなし。

調査結果を聞く

ネットワークの担当者が来たので、そちらに任せていると「DHCPのアドレス枯渇が原因です。特定のクライアントから10秒毎に新しいIPを払い出すよう依頼が来ている」という連絡が。しかも、私が実験用に使っているPCから。

そのPCはMinIO の実験用に職員用セグメントに立てていたDebianマシン。そういえば、昨日の午後にファイルの送受信試したな、と。
マシンを見てみると、IOエラーが大量に出ているようですが、sshも通るしネットワーク的にはおかしくはない感じ。IPアドレス確認しようとコマンド打ったらびっくり!確かに有線のinterfaceにIPアドレスが大量に割り当ててられているではないですかー。とりあえず迷惑かけ続けるのもアレなので、シャットダウンしました。

問題のマシンを調査してみる

IOエラーがDHCPの暴走になるのか?というのが疑問で、所定の用事を済ませたた後に、問題のマシンを調査してみました。

問題起こすわけにもいかないので、いったん、有線ケーブルは抜いて起動して、syslogとかを見てみようとすると、ガーン!起動しないじゃないの。手動でfsckしてね、と言われるので、言われるがまま作業してなんとか起動。

で、syslog見てみると、どうも前日の18時前くらいからログがない様子。その少し前のログを見るとIOエラーでファイルシステムをReadOnlyに、と書かれているので、syslogも書き込めなくなったのかも、という結論に。

しかし、それでなぜDHCPが?syslogを見ると前日の12時くらいにリース更新の記録があり、残りが12時間弱くらいになっていたのです。そういえば、前日の23時台から問題の挙動が起き始めている、とネットワーク担当者が言っていたな…

dhclient-enp2s0.leasesを見ると、正常に取得していたIPだけが書かれていて、それがsyslogの内容とも概ね合致している感じ。その後IPを大量に取得しているにもかかわらずそれがleasesに反映されていない。

ということは、こんな感じってことですかね。

DHCPのリース更新をした
→ちゃんと更新できているにもかかわらず、DBを更新できないためにリース期限を更新できなかったと思いこむ
→リース切れだと思い、IP取得に行く
→取得できているのにDB更新できないから取得できなかったと思い込み、また取得に行く(以下これを繰り返す)

まとめ

問題のマシンは、以前もファイルシステムエラーを起こしていたので、ディスクが壊れているのか、MinIOとext4の組み合わせが良くないのか、ちゃんと調べてから使うべきでした。

きちんと調査せずに、安易にOS再インストールして、かつサーバのように起動させっぱなしにしていたのが良くなかったです。

psコマンドとかで見た限り怪しいプロセスもなかったし、おそらくセキュリティインシデント(マルウェア感染とか)ではなかったと思われるので、それは良かったです。

参考にしたサイトなど

あと、dhclientのソースコードもちょっとだけ目を通しました。

df コマンドを調べてみた

はじめに

職場で、mount で表示すると出てくるのに、dfだと表示されないものがあるというので、ちょっと勉強してみたのでそのメモです。

手がかり

職場での出来事なので、あまり詳細について書くことができません。

NFSサーバの3つのディレクトリをそれぞれ、別のマウントポイントでマウントしているそうです。そのうちの1つだけが表示されない状況。
そうすると、NFSサーバ側の問題は考えにくいと思います。

複数のマシンからマウントしているみたいですが、特定の1つだけdfで表示できないっぽい感じ。(このあたり詳細を聞いていないのでなんとも。)

mountコマンドで表示すると出てくるのに、dfだと出てこない。
あと、マウントポイントの先のデータも参照等できている。(なのでマウント自体はできていると思われる。)

仮説: dfを疑おう

dfとmountでおそらく情報源となっているものが違っていて、それが何らかの原因で差異が生じているのでは、と仮説を立ててみた。

mount コマンドの情報源

マニュアル見ると書いてあった。

mount および umount プログラムによって現在マウントされているファイルシステムの一覧は /etc/mtab ファイル中に記述されている。 mount が
引き数なしで実行された場合には、 このリストが表示される。

man 8 mount より

自分の手元のPC(Linux Mint 19.3)だと /etc/mtab は/proc/self/mounts へのシンボリックリンクでした。職場のはRedHatEnterpriseLinuxの7系だった気がするので、また違うかもしれない。

df コマンドの情報源

df のマニュアル見たけど、特に書いていない。うーん、しょうがない。ソースコードを読もう。でもバージョンわかんない。まぁいいや。とりあえず割と新しいやつを読んでみておこう、というわけで GNU coreutilsの8.32を読むことに。

特に手がかりとかないので、まずはmainから。

int
main (int argc, char **argv)
{
  struct stat *stats IF_LINT ( = 0);
  initialize_main (&argc, &argv);
  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  atexit (close_stdout);
  fs_select_list = NULL;
  fs_exclude_list = NULL;
  show_all_fs = false;
  show_listed_fs = false;
  human_output_opts = -1;
  print_type = false;
  file_systems_processed = false;
  exit_status = EXIT_SUCCESS;
  print_grand_total = false;
  grand_fsu.fsu_blocksize = 1;
  /* If true, use the POSIX output format. */
  bool posix_format = false;
  const char *msg_mut_excl = _("options %s and %s are mutually exclusive");

  while (true)
    {
      int oi = -1;
      int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, &oi);

最初の方のやつは、GNUのコマンドでよく出てくるやつですね。(前にls を読んだときにもあった。)その後の変数は名前から推測すると、コマンドのオプション値を保存するためのものかなぁと思います。while文はコマンドオプションの処理だと思います。

というわけでこの辺をずーと飛ばしてみると、1780行目にマウントしているもののリストを取り出しているっぽいところを発見。

mount_list =
  read_file_system_list ((fs_select_list != NULL
                          || fs_exclude_list != NULL
                          || print_type
                          || field_data[FSTYPE_FIELD].used
                          || show_local_fs));

read_file_system_list は lib/mountlist.c にありました。

struct mount_entry *
read_file_system_list (bool need_fs_type)
{
  struct mount_entry *mount_list;
  (省略)
#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
                             also (obsolete) 4.3BSD, SunOS */
  {
    FILE *fp;
#if defined linux || defined ANDROID
   /* Try parsing mountinfo first, as that make device IDs available.   
      Note we could use libmount routines to simplify this parsing a 
      little (and that code is in previous versions of this function), 
      however libmount depends on libselinux which pulls in many 
      dependencies. */ 
    char const *mountinfo = "/proc/self/mountinfo";
    fp = fopen (mountinfo, "r");
    if (fp != NULL) 
      {
        (省略) 
      } else /* fallback to /proc/self/mounts (/etc/mtab). */
#endif /* linux || ANDROID */
      {
        struct mntent *mnt;
        char const *table = MOUNTED;
        fp = setmntent (table, "r");
         (省略)

どうやら、最初に/proc/self/mountinfo を読もうとしていて、それができないときに/etc/mtab にフォールダウンするみたいですね。

ちなみに、MOUNTEDは、マクロで_PATH_MOUNTEDと定義されていました。_PATH_MOUNTEDは、glibcのsysdeps/unix/sysv/linux/paths.hによると/etc/mtab なので、上記の記載とも一致しています。

結論: まだわからないです

出勤したら、また調べてみようと思います。
とりあえず、/proc/self/mountinfo と/etc/mtab の差異を調べて見る感じですね。でも、自分がログインして調べるわけじゃないのが、ちょっと面倒です。

サーバの運用保守を委託してやっているので、あんまり自分がログインして、とかすると責任分解が、とか言われてしまうのです。とほほ。

2020/07/06 追記

原因は、このChangeLogにある内容でした。

df: prioritize mounts nearer the device root
In the presence of bind mounts of a device, the 4th “mount root” field
from /proc/self/mountinfo is now considered, so as to prefer mount
points closer to the root of the device. Note on older systems with
an /etc/mtab file, the source device was listed as the originating
directory, and so this was not an issue.
Details at http://pad.lv/1432871

coreutils/ChangeLog

ソースコードの続きの箇所で、filter_mount_list() という関数があって、そこで、bind mount の場合に重複して表示させないように処理しつつ、NFSの場合は意図したものとして、ちゃんと表示するように書いてありました。

bool target_nearer_root = strlen (seen_dev->me->me_mountdir) 
                          > strlen (me->me_mountdir);
/* With bind mounts, prefer items nearer the root of the source */bool source_below_root = seen_dev->me->me_mntroot != NULL
                    && me->me_mntroot != NULL
                    && (strlen (seen_dev->me->me_mntroot)
                        < strlen (me->me_mntroot));
if (! print_grand_total
    && me->me_remote && seen_dev->me->me_remote
    && ! STREQ (seen_dev->me->me_devname, me->me_devname))
  {
    /* Don't discard remote entries with different locations,
       as these are more likely to be explicitly mounted.
       However avoid this when producing a total to give
       a more accurate value in that case.  */
  }

無駄といえば無駄でしたが、いろいろ勉強になったのでラッキーでした。

読書通帳⑦

読んだ本

・[試して理解] Linuxのしくみ
 https://gihyo.jp/book/2018/978-4-7741-9607-7

内容と感想

Software Designで連載されている記事をまとめたものなのかな、と思って買いました。ボリューム的に雑誌連載をまとめたものよりも多い気がするので、お得な気分です。

プロセススケジューリング、メモリ管理、仮想記憶、ファイルシステム、ストレージデバイスの順で書かれていて、それぞれでLinuxがどのように振る舞うのかを、実験してみせてくれているのがとてもうれしいです。

いったん、全部に目を通した後、実験プログラムを追試験?してみています。ちゃんと理解しながらやろうと思っているので、それなりに時間がかかっていますが、その価値はあると感じています。

Linuxのより詳細については、Linuxカーネル2.6解読室やLinuxシステムプログラミングなどの方が詳しい印象ですが、Linuxマシンの運用が主体の私にとっては、この本の方が良かったです。

Software Designの連載は続いているので、この本も続編が出るのかな〜と期待しています。楽しみです。

ちなみに

実験プログラムの中身で、なんでこうしているのだろうとわからないところがあったので、Twitterで著者に質問したら、とてもフランクに回答してくださいました。

お尋ねしてすぐに回答をいただけるのはとてもうれしかったです。

GnuPG鍵の有効期限を延長する

はじめに

2020年最初にGnuPG鍵の有効期限の延長をやったので、その記録です。

もともとは

PGPの勉強のために、2018年2月に鍵を作成しました。有効期限を2年にしていたため、期限が近づいてきていたのです。

プライマリキーを退避させていたので、いったんそれを戻してからの作業です。

$ gpg --edit-key <key id> 
gpg> key 0
gpg> expire

ここで有効期限を尋ねられました。無期限(0)でも良い気がしましたが、一応3年(3y)にしてみました。また、3年後に同じような作業が必要になりますね。
最後に保存して終了です。

gpg> save

実際には、プライマリキーとサブキーの両方の有効期限を延長しています。
今回、2020年1月1日に作業したので、次に有効期限が2022年12月31日となんともわかりやすい日付になりました。

PCでのパスワード管理(pass)や、スマホのPassword Storeアプリ にもGPG鍵を使用しているので、有効期限をちゃんと延長できて安心しました。

参考にしたサイト

入門 監視を読みました

はじめに

オライリーの入門 監視を読みましたので感想文です。

入門 監視
https://www.oreilly.co.jp/books/9784873118642/

監視の考え方

一番、印象的だったのがシステムの監視をする際にCPU使用率だったりメモリ使用率だったりを取得するわけですが、それ自体はそれほど役に立たないという趣旨の記載の部分でした。

実際、CPU使用率高騰でアラートメールを受信することは多いのですが、理由がバッチ処理で一時的に負荷がかかっているだけで、システムとしては正常というケースが多いので、疑問を感じていました。ユーザから観て、正常に動作しているのかどうかという視点でアラートを考えるべき、というのが腹にストンと落ちる感じがしました。

メトリクスの重要性

また、ちょうど業務で不審な通信の有無を探すということをやって、ログからそうした傾向を調査しようとしていろいろ試行錯誤をしていたところ、メトリクスの重要性を感じるきっかけにもなりました。

ログそのものではなくて、メトリクス(ゲージやカウンタ)という形で普段から取得しておくことで、いざそういう調査をする際に速やかな調査が可能になるな、というのを感じました。

これまでGrafanaやKibanaをうまく活用しきれていないと感じていたので、それらをどう活用するかの方向性が見えたような気がしました。

まとめ

ちょうど読んだタイミングが良かったのだと思いますが、この本に書かれている内容を実感するような実例を業務で携わることもできて、まさに自分が求めていることが書かれている!という感想を持ちました。

オススメです!

いかすEKS②

いかすESKエントリーの②です。
過去記事はこちら

クラスタの作成

クラスタは毎回、作業後に落とすので最初はクラスタの作成からです。節約です。前回、オプションを忘れてworker用にm5.largeを起動してしまったので、今回は慎重に。公式に書いてるとおりの内容です。

$ eksctl create cluster --name [cluster-name] --version 1.13 --nodegroup-name [worker-group-name] --node-type t3.medium --nodes 3 --nodes-min 1 --nodes-max 4 --node-ami auto

そこそこ時間がかかるので慌てずに。kubectlで接続できることを確認します。
オンプレ環境でkubeadmを使ってクラスタを作ったのに比べるととても楽ですね。

しかしエラーが出る

なぜかエラーが出ました。うーん、前回はちゃんとクラスタ作れたのに。違うのはオプションの内容だけなので、少しずつ削ってみるなどしてみます。AutoScalingなんとかとエラーが出ていたので、nodes-minとnodes-maxを削るもエラーが増える感じ。ハマってしまった感じがします。焦ります。
とりあえず、クラスタを削除して気持ちを落ち着かせます。

再度チャレンジ

前回と大きく違うのは、node-typeでt3.mediumを指定したところかなと思うので、そこを削ってみます。m5.largeでいいじゃないか(泣)

うまく起動できたので、今後のために、ちゃんと出力内容も読んでおきます。

$ eksctl create cluster --name ikasu --version 1.13 --nodegroup-name ikasu-workers
[ℹ]  using region ap-northeast-1
[ℹ]  setting availability zones to [ap-northeast-1a ap-northeast-1c ap-northeast-1d]
[ℹ]  subnets for ap-northeast-1a - public:192.168.0.0/19 private:192.168.96.0/19
[ℹ]  subnets for ap-northeast-1c - public:192.168.32.0/19 private:192.168.128.0/19
[ℹ]  subnets for ap-northeast-1d - public:192.168.64.0/19 private:192.168.160.0/19
[ℹ]  nodegroup "ikasu-workers" will use "ami-0a67c71d2ab43d36f" [AmazonLinux2/1.13]
[ℹ]  using Kubernetes version 1.13
[ℹ]  creating EKS cluster "ikasu" in "ap-northeast-1" region
[ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial nodegroup
[ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-1 --name=ikasu'
[ℹ]  CloudWatch logging will not be enabled for cluster "ikasu" in "ap-northeast-1"
[ℹ]  you can enable it with 'eksctl utils update-cluster-logging --region=ap-northeast-1 --name=ikasu'
[ℹ]  2 sequential tasks: { create cluster control plane "ikasu", create nodegroup "ikasu-workers" }
[ℹ]  building cluster stack "eksctl-ikasu-cluster"
[ℹ]  deploying stack "eksctl-ikasu-cluster"
[ℹ]  building nodegroup stack "eksctl-ikasu-nodegroup-ikasu-workers"
[ℹ]  --nodes-min=2 was set automatically for nodegroup ikasu-workers
[ℹ]  --nodes-max=2 was set automatically for nodegroup ikasu-workers
[ℹ]  deploying stack "eksctl-ikasu-nodegroup-ikasu-workers"
[✔]  all EKS cluster resource for "ikasu" had been created
[✔]  saved kubeconfig as "/home/hideo/.kube/config-first-eks-cluster"
[ℹ]  adding role "arn:aws:iam::313028364465:role/eksctl-ikasu-nodegroup-ikasu-work-NodeInstanceRole-B5H6FNMHI28Y" to auth ConfigMap
[ℹ]  nodegroup "ikasu-workers" has 0 node(s)
[ℹ]  waiting for at least 2 node(s) to become ready in "ikasu-workers"
[ℹ]  nodegroup "ikasu-workers" has 2 node(s)
[ℹ]  node "ip-192-168-54-59.ap-northeast-1.compute.internal" is ready
[ℹ]  node "ip-192-168-7-9.ap-northeast-1.compute.internal" is ready
[ℹ]  kubectl command should work with "/home/hideo/.kube/config-first-eks-cluster", try 'kubectl --kubeconfig=/home/hideo/.kube/config-first-eks-cluster get nodes'
[✔]  EKS cluster "ikasu" in "ap-northeast-1" region is ready

ゲストブックの起動

Kubernetesのゲストブックアプリケーションを起動します。EKSクラスタのテストですね。どうやら、このリポジトリのようです。
https://github.com/kubernetes/examples/tree/master/guestbook-go

公式に書いてあるとおりに実行していきます。yamlじゃないんですね。
最後にExternalIPを確認します。

$ kubectl get svc -o wide
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)          AGE     SELECTOR
guestbook      LoadBalancer   10.100.156.158   a879b183ee57c11e9970906dc18345ed-1049393403.ap-northeast-1.elb.amazonaws.com   3000:32031/TCP   18s     app=guestbook
kubernetes     ClusterIP      10.100.0.1       <none>                                                                         443/TCP          15m     <none>
redis-master   ClusterIP      10.100.144.119   <none>                                                                         6379/TCP         2m34s   app=redis,role=master
redis-slave    ClusterIP      10.100.180.106   <none>                                                                         6379/TCP         85s     app=redis,role=slave

ブラウザで当該URLにアクセスしてみるも「サーバが見つかりません」となりました。公式に書いてあるようにDNSの反映に少し時間がかかるようです。少し待つとアクセスできました!

ゲストブックアプリケーションの起動

最後に

お小遣いを無駄にしないために、クリーンアップを忘れないようにします。