SORACOM UG Explorer 2022 秋のハンズオン祭りに参加してきた
つい最近、転職した会社で思いがけずIoT関連の仕事に絡む事となり、SORACOMを使う機会に恵まれたので、SNS上でSORACOMデビューとつぶやいていたところ、それを見つけたかつての職場の同僚がSORACOM UGの運営メンバーだったらしく、以下のイベントを案内してくれた。
個人的にIoTの領域は未経験であり、SORACOM自体も昔の職場で同僚がCanalを使ってAWSとごにょごにょしていたのを傍目で見ていた程度でしか知らず、実機を使ってハンズオン出来る機会というのは願ったり叶ったりだった為、折角なので参加してみた。
SORACOM UG Explorer 2022は、全国5箇所の会場(札幌、東京、広島、高知、福岡)とオンラインで同時開催され、自分は東京で物理参加したのだが、東京会場はヤフーさんであり、紀尾井タワー移転後のオフィスは初めての訪問。
紀尾井タワーと言えば、コロナ前は通勤時間帯のオフィスエレベーターの渋滞が凄まじいという話題を耳にしており、そのくらいのイメージしか無かったのだが、この日はコロナ以降の休日という事もあって、当然ガラガラというか人気も殆ど無く、めちゃくちゃ広いわ静かだわ、お手洗いも綺麗だわで、眺望も素晴らしくて環境としては最高だった。
ハンズオン
各会場のUG支部による挨拶や紹介LT、会場案内、ハンズオンの概要説明等があった後、いよいよハンズオン開始。
今回は以下の3種類のデバイスを使って、SORACOMのサービスを体験する事が出来るといった内容。
- SORACOM LTE-M Button for Enterprise (通称:白ボタン)
- GPSマルチユニットSORACOM Edition
- Soracom Cloud Camera Services ソラカメ
デバイスはSORACOMさんがレンタルしてくれる為、事前に自ら購入して持ち込む必要も無く、予めSORACOMアカウントだけを準備しておけばOKだった。
また、SIMについては特定地域向け IoT SIM (plan-D)サイズ:ナノ(データ通信のみ/D-300MB)を1人につき1枚無償提供頂き、そのまま持ち帰って私物として使わせてもらえる事に(ありがたやありがたや)。
GPSマルチユニット
自分はGPSマルチユニットでのハンズオンを予約していたので、まず最初はSORACOM IoT DIY レシピで公開されている以下のレシピを実践する。
概要を説明すると、SORACOM AirのSIMを挿したIoTデバイスのセンサーから、温度や湿度といったデータを収集してHarvest Dataに蓄積し、それらをGrafanaベースのLagoonで可視化するといった内容。
SIMの登録を済ませて以降は、基本的にレシピの手順通りになぞるだけだったので、難しい操作であったり、詰まってしまう様なところも特に無く、あっさりと終わってしまった。
ハンズオンとしては物足りなさを感じたものの、それくらい殆ど手間をかけずにサンプルにある様なLagoonのダッシュボードを構築して、IoTデバイスの状態を可視化出来てしまうというのは、利便性を感じさせてくれるポイントである。
IoTの文脈で言えば、ここから更にデータを分析して活用する、といったサイクルを築いていく必要がある訳だが、そのIoTの取っ掛かりを部分体験するといった意味で、自分にとっては最初の一歩として丁度良い経験になったと思う。
白ボタン
思いの外、時間がたっぷりと余ってしまった為、次に白ボタンを使ったハンズオンにも取り組んでみた。
白ボタンはレンタル、持ち込み、仮想ボタンを使うケースの3パターンが用意されており、レンタルしても良かったのだが、SORACOM Arcを使ってみたかったので、仮想ボタンのケースを選択する。
このハンズオンの概要は、SORACOM Arcによって仮想ボタンから取得したボタン操作のデータをHarvest Dataに蓄積しつつ、もう一方でOrbitを使いWebAssemblyによってボタン操作に応じた出力データを変換し、BeamでLINE Notifyに転送するといった内容になっているのだが、SORACOM Arcについては仮想ボタンのケースでのみ使用する。
GPSマルチユニットのハンズオンとは異なり、それなりのボリュームもありつつ、デバイスから収集したデータを活用するところまで体験出来る為、個人的にはこちらの方がIoTやってる感があったのと、SIMも実機も無くてもOKというところで、WireGuard含めその仕組みが興味深くて面白かった。
ソラカメ
流石に時間が足りなくなってしまい、こちらまでは取り組めなかったのだが、ソラカメを使ったハンズオンでは、SORACOM CLIとAPIを使って複数の連続した静止画像からタイムラプス動画を作っていた模様。
ATOM Camがお手頃価格なので、自分でも購入して遊んでみたくなった。
LT
SORACOMの中の人達のLTと、一般公募の方々のLTと、色々と面白い発表があったのだが、自分が捕捉出来たスライドはわずかだった(そのうちに、上にもリンクしているConnpassにまとめられるかも…しれない)。
オフィスの課題をIoTで解決。
本日のLT資料です! #soracom #soracomug #explorer2022
— YuheiOkazaki (@Y_uuu) October 22, 2022
SORACOM ArcでオフィスのIoT化を進行中です⭐ https://t.co/btlApoapep
母の愛を感じるIoT事例。
せっかくなのでスライドアップしました!
— はなこ@キャリアの伴走者 (@hanaco00942045) October 22, 2022
このボタンもっと日常的に使えるという可能性を感じてます。
SORACOMボタンで 家庭内ナースコールを 作ってみた話 https://t.co/OosyuiaV3p #soracomug #explorer2022
いずれも身近の課題をしっかりIoTで解決されたというお話で、素人としては感心すると同時に、自分も小さな事からIoTで何かの形を成してみたいなと思わせる、チャレンジ意欲の湧いてくる様なLTでした。
感想
コロナ前から随分と表のイベントには参加しなくなっていたのだが、ひょんな切っ掛けから久々にこういったコミュニティに参加してみて、何やら懐かしさを感じつつ、思っていた以上に楽しむ事が出来た。
人数的には参加者より運営の方が多かったので、最初はどうしても身内ノリ的な感じを受けてしまうところもありはしたものの、コミュニティとして全体の雰囲気は悪くなく、適度なゆるさも相俟ってか、入り辛さみたいな空気はあまり無かった様に思う。
学びたかった事はしっかり学べ、ハンズオン中の雑談やLTでは新たな知見も得る事が出来たし、最終的にはエバンジェリストのMaxさんを始めとして、参加者の皆さんとも大いに盛り上がったので、参加して良かったなというのが正直な感想。
運営の皆さん、SORACOMさん、会場のヤフーさん、そして何より、誘ってくれた元同僚に感謝。
これから
趣味レベルで小さな事からIoTを実践してみたり、業務に関連してAWSとSORACOMを連携させたIoTのインフラアーキテクチャであったり、ネットワークやデバイス監視、セキュリティ、また、AWS IoT等についても学習していきたいので、今後もそういった分野が学べるイベントや機会があれば、積極的に参加していきたい。
M1 Macbook ProでRubyGemsのeventmachineをインストールした際に吐かれたSSL関連のビルドエラーについて
仕事で8年ぶりくらいにRubyに触れる機会があり、M1 Macbook ProでRailsの環境を構築中、RubyGemsのeventmachineをインストールしたらSSL関連のビルドエラーが生じたので、その問題解消までのメモを残しておく。
環境
- M1 Macbook Pro (14インチ、2021)
- macOS Monterey バージョン12.6
- anyenv 1.1.5
- rbenv 1.2.0-33-ga6cf6ae
- Ruby 2.7.5
- RubyGems 3.1.6
- eventmachine 1.2.7
エラー内容
$ gem install eventmachine Building native extensions. This could take a while... ERROR: Error installing eventmachine: ERROR: Failed to build gem native extension. current directory: /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/eventmachine-1.2.7/ext /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/bin/ruby -I /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/2.7.0 -r ./siteconf20221021-64873-1gud8b.rb extconf.rb checking for -lcrypto... no checking for openssl/ssl.h... yes checking for openssl/err.h... yes checking for rb_trap_immediate in ruby.h,rubysig.h... no checking for rb_thread_blocking_region()... no checking for rb_thread_call_without_gvl() in ruby/thread.h... yes checking for rb_thread_fd_select()... yes checking for rb_fdset_t in ruby/intern.h... yes checking for rb_wait_for_single_fd()... yes checking for rb_enable_interrupt()... no checking for rb_time_new()... yes checking for inotify_init() in sys/inotify.h... no checking for __NR_inotify_init in sys/syscall.h... no checking for writev() in sys/uio.h... yes checking for pipe2() in unistd.h... no checking for accept4() in sys/socket.h... no checking for SOCK_CLOEXEC in sys/socket.h... no checking for sys/event.h... yes checking for sys/queue.h... yes checking for clock_gettime()... yes checking for CLOCK_MONOTONIC_RAW in time.h... yes checking for CLOCK_MONOTONIC in time.h... yes CXXFLAGS=-g -O2 -Wall -Wextra -Wno-deprecated-declarations -Wno-ignored-qualifiers -Wno-unused-result -Wno-address creating Makefile current directory: /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/eventmachine-1.2.7/ext make "DESTDIR=" clean current directory: /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/eventmachine-1.2.7/ext make "DESTDIR=" compiling binder.cpp compiling cmain.cpp compiling ed.cpp compiling em.cpp compiling kb.cpp compiling page.cpp compiling pipe.cpp compiling rubymain.cpp rubymain.cpp:220:3: warning: 'rb_rescue' is deprecated: Use of ANYARGS in this function is deprecated [-Wdeprecated-declarations] rb_rescue((VALUE (*)(ANYARGS))event_callback, (VALUE)&e, (VALUE (*)(ANYARGS))event_error_handler, Qnil); ^ /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/include/ruby-2.7.0/ruby/backward/cxxanyargs.hpp:184:1: note: 'rb_rescue' has been explicitly marked deprecated here RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") ^ /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/include/ruby-2.7.0/arm64-darwin21/ruby/config.h:152:49: note: expanded from macro 'RUBY_CXX_DEPRECATED' #define RUBY_CXX_DEPRECATED(msg) __attribute__((__deprecated__(msg))) ^ 1 warning generated. compiling ssl.cpp linking shared-object rubyeventmachine.bundle Undefined symbols for architecture arm64: "_BIO_ctrl", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o _ssl_verify_wrapper in ssl.o SslBox_t::CanGetCiphertext() in ssl.o SslBox_t::PutPlaintext(char const*, int) in ssl.o "_BIO_free", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o _ssl_verify_wrapper in ssl.o "_BIO_new", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o _ssl_verify_wrapper in ssl.o "_BIO_new_file", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_BIO_new_mem_buf", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_BIO_read", referenced from: SslBox_t::GetCiphertext(char*, int) in ssl.o "_BIO_s_mem", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o _ssl_verify_wrapper in ssl.o "_BIO_write", referenced from: _ssl_verify_wrapper in ssl.o SslBox_t::PutCiphertext(char const*, int) in ssl.o "_DH_free", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_EC_KEY_free", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_EC_KEY_new_by_curve_name", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_ERR_print_errors_fp", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o SslBox_t::GetPlaintext(char*, int) in ssl.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) (.cold.2) in ssl.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) (.cold.3) in ssl.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) (.cold.4) in ssl.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) (.cold.5) in ssl.o "_EVP_PKEY_free", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o "_OBJ_sn2nid", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_OPENSSL_init_crypto", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_OPENSSL_init_ssl", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_PEM_read_bio_DHparams", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_PEM_read_bio_PrivateKey", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_PEM_read_bio_X509", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_PEM_write_bio_X509", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o _ssl_verify_wrapper in ssl.o "_SSL_CIPHER_get_bits", referenced from: SslBox_t::GetCipherBits() in ssl.o "_SSL_CIPHER_get_name", referenced from: SslBox_t::GetCipherName() in ssl.o "_SSL_CIPHER_get_version", referenced from: SslBox_t::GetCipherProtocol() in ssl.o "_SSL_CTX_ctrl", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_free", referenced from: SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o "_SSL_CTX_new", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_set_cipher_list", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_set_options", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_set_session_id_context", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_use_PrivateKey", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_use_PrivateKey_file", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_use_certificate", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_CTX_use_certificate_chain_file", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_SSL_accept", referenced from: SslBox_t::GetPlaintext(char*, int) in ssl.o "_SSL_clear", referenced from: SslBox_t::~SslBox_t() in ssl.o "_SSL_connect", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o SslBox_t::GetPlaintext(char*, int) in ssl.o "_SSL_ctrl", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o "_SSL_free", referenced from: SslBox_t::~SslBox_t() in ssl.o "_SSL_get_current_cipher", referenced from: SslBox_t::GetCipherBits() in ssl.o SslBox_t::GetCipherName() in ssl.o SslBox_t::GetCipherProtocol() in ssl.o "_SSL_get_error", referenced from: SslBox_t::GetPlaintext(char*, int) in ssl.o SslBox_t::PutPlaintext(char const*, int) in ssl.o "_SSL_get_ex_data", referenced from: _ssl_verify_wrapper in ssl.o "_SSL_get_ex_data_X509_STORE_CTX_idx", referenced from: _ssl_verify_wrapper in ssl.o "_SSL_get_peer_certificate", referenced from: SslBox_t::GetPeerCert() in ssl.o "_SSL_get_servername", referenced from: SslBox_t::GetSNIHostname() in ssl.o "_SSL_get_shutdown", referenced from: SslBox_t::~SslBox_t() in ssl.o "_SSL_is_init_finished", referenced from: SslBox_t::GetPlaintext(char*, int) in ssl.o SslBox_t::PutPlaintext(char const*, int) in ssl.o "_SSL_new", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o "_SSL_read", referenced from: SslBox_t::GetPlaintext(char*, int) in ssl.o "_SSL_set_bio", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o "_SSL_set_ex_data", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o "_SSL_set_verify", referenced from: SslBox_t::SslBox_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, unsigned long) in ssl.o "_SSL_shutdown", referenced from: SslBox_t::~SslBox_t() in ssl.o "_SSL_write", referenced from: SslBox_t::PutPlaintext(char const*, int) in ssl.o "_TLS_client_method", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_TLS_server_method", referenced from: SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o "_X509_STORE_CTX_get_current_cert", referenced from: _ssl_verify_wrapper in ssl.o "_X509_STORE_CTX_get_ex_data", referenced from: _ssl_verify_wrapper in ssl.o "_X509_free", referenced from: t_get_peer_cert(unsigned long, unsigned long) in rubymain.o SslContext_t::SslContext_t(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in ssl.o SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o SslContext_t::~SslContext_t() in ssl.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [rubyeventmachine.bundle] Error 1 make failed, exit code 2 Gem files will remain installed in /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/eventmachine-1.2.7 for inspection. Results logged to /Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/extensions/arm64-darwin-21/2.7.0/eventmachine-1.2.7/gem_make.out
上記エラーメッセージから一部を抜粋すると、以下の様にssl.cppのコンパイルで落ちている事が分かる。
compiling ssl.cpp linking shared-object rubyeventmachine.bundle Undefined symbols for architecture arm64: 〜〜〜 中略 〜〜〜 ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [rubyeventmachine.bundle] Error 1 make failed, exit code 2
ここから、リンカがSSLライブラリを参照出来ていないのではないかと問題のあたりをつけて調べてみた。
調査
rbenvではRubyが依存しているOpenSSLを、Rubyのバージョン毎に個別にインストールしているのだが(昔はHomebrewのOpenSSLに依存していた)、自分の環境だとanyenvとrbenvを使っているので、バージョン2.7.5が依存しているOpenSSLは以下のディレクトリに入っている。
~/.anyenv/envs/rbenv/versions/2.7.5/openssl
そして、RubyGemsでインストールしたeventmachineのソースは、以下のディレクトリに保存される。
~/.anyenv/envs/rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/eventmachine-1.2.7/ext
この中にextconf.rbで生成されたMakefileが入っているので、定義されているリンカオプションを確認してみたのだが、LDFLAGSにopenssl/libのパスは見当たらなかった。
ldflags = -L. -L/Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib -fstack-protector-strong
これが原因ではないかと推察し、extconf.rbのコードから環境変数「PKG_CONFIG_PATH」が使える感じだったので、ビルド時にHomebrewでインストールしたOpenSSLのpcファイルを参照させてみる事に。
問題解消
MacではmacOS High Sierraから、OpenSSLのハートブリード問題を機に標準のSSLライブラリがLibreSSL(OpenSSL1.0.1からフォークされた)へと代わっており、これを書いている時点ではバージョン2.8.3がインストールされている。
$ openssl version LibreSSL 2.8.3
しかし、Rubyが依存しているのはOpenSSLとなる為、まずはHomebrewを使ってOpenSSLをインストールする。
$ brew install openssl@1.1
ここでバージョン指定しないと現行の最新版であるバージョン3がインストールされるのだが、Ruby 2.7系はOpenSSLのバージョン3に対応していないので、バージョン1.1の方が必要となる。
インストールしたら、環境変数「PKG_CONFIG_PATH」にOpenSSL@1.1のpcファイルのパスを設定するのだが、brew info opsnssl@1.1
コマンドを実行すると、以下の様に環境変数設定のコマンド実行例が出力される。
$ brew info openssl@1.1 ==> openssl@1.1: stable 1.1.1q (bottled) [keg-only] Cryptography and SSL/TLS Toolkit https://openssl.org/ /opt/homebrew/Cellar/openssl@1.1/1.1.1q (8,097 files, 18MB) Poured from bottle on 2022-10-04 at 15:51:35 From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/openssl@1.1.rb License: OpenSSL ==> Dependencies Required: ca-certificates ✔ ==> Caveats A CA file has been bootstrapped using certificates from the system keychain. To add additional certificates, place .pem files in /opt/homebrew/etc/openssl@1.1/certs and run /opt/homebrew/opt/openssl@1.1/bin/c_rehash openssl@1.1 is keg-only, which means it was not symlinked into /opt/homebrew, because macOS provides LibreSSL. If you need to have openssl@1.1 first in your PATH, run: echo 'export PATH="/opt/homebrew/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc For compilers to find openssl@1.1 you may need to set: export LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib" export CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include" For pkg-config to find openssl@1.1 you may need to set: export PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@1.1/lib/pkgconfig" ==> Analytics install: 712,643 (30 days), 2,287,873 (90 days), 11,108,325 (365 days) install-on-request: 26,710 (30 days), 82,513 (90 days), 390,982 (365 days) build-error: 1,533 (30 days)
上記メッセージの通りにコマンドを実行。
$ export PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@1.1/lib/pkgconfig"
その上で、gem install eventmachine
コマンドを再度実行してみると、今度は問題無くインストールが完了した。
改めて、生成されたMakefileに定義されるLDFLAGSを確認してみたところ、以下の様にopenssl/libのパスが追加されている事が分かる。
ldflags = -L. -L/Users/heroween/.anyenv/envs/rbenv/versions/2.7.5/lib -fstack-protector-strong -L/opt/homebrew/Cellar/openssl@1.1/1.1.1q/lib
という訳で、結論としては、やはりリンカがSSLライブラリを参照出来ていなかったというのが、ビルドエラーの根本原因だったと思われる。
おまけ
rbenvでは、Rubyインストール時のオプションによって、バンドルされたOpenSSLを使うのではなく、HomebrewのOpenSSLを使う事も可能となっている模様。
$ export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"
この環境変数を設定してrbenv install
コマンドを実行した場合は、Rubyと一緒にOpenSSLはインストールされなくなり、最初からHomebrewのOpenSSLに依存する様になる。
この形でインストールしていれば、今回のビルドエラーを回避出来たかもしれないのだが、取り敢えず問題は解消しているので、その検証自体はまた後日にでもやろうと思う。
M2 Macbook AirにanyenvとpyenvでAnacondaをインストールする
手持ちのM2 Macbook AirにAnacondaが必要となり、anyenvとpyenvでインストールしてみたので手順を残しておく。
anyenvのインストール
まずはHomebrewで様々な**envコマンドをまとめて管理する為のanyenvをインストールする。
$ brew install anyenv
anyenvがインストールされた事を確認。
$ anynev -v anyenv 1.1.5
インストール出来たらanyenv init
コマンドを実行し、デフォルトシェルの種類に応じたanyenvの設定コマンドを確認する。
$ anyenv init # Load anyenv automatically by adding # the following to ~/.config/fish/config.fish: status --is-interactive; and source (anyenv init -|psub)
anyenvはbash、zsh、ksh、fishをサポートしているので、それぞれのシェルに合わせたメッセージが出力されるのだが、自分の環境はfishを使っているので、上述のメッセージに従ってconfig.fish
に出力されたコマンドを追記してやる。
$ vi ~/.config/fish/config.fish 以下を追記 status --is-interactive; and source (anyenv init -|psub)
追記が済んだらシェルをリロード。
$ exec $SHELL -l
次に、**envコマンドをインストールする為のanyenv-install
プラグインを設定する。
$ anyenv install --init Manifest directory doesn't exist: /Users/heroween/.config/anyenv/anyenv-install Do you want to checkout https://github.com/anyenv/anyenv-install.git? [y/N]: y Cloning https://github.com/anyenv/anyenv-install.git master to /Users/heroween/.config/anyenv/anyenv-install... Cloning into '/Users/heroween/.config/anyenv/anyenv-install'... remote: Enumerating objects: 71, done. remote: Counting objects: 100% (14/14), done. remote: Compressing objects: 100% (13/13), done. remote: Total 71 (delta 4), reused 3 (delta 1), pack-reused 57 Receiving objects: 100% (71/71), 13.15 KiB | 6.58 MiB/s, done. Resolving deltas: 100% (11/11), done. Completed!
anyenv install -l
コマンドでインストール可能な**envコマンドのリストが表示されたらanyenvの設定は完了。
$ ~/.config anyenv install -l Renv crenv denv erlenv exenv goenv hsenv jenv jlenv kubectlenv luaenv nodenv phpenv plenv pyenv rbenv sbtenv scalaenv swiftenv tfenv
pyenvのインストール
anyenvの設定が完了したら、複数バージョンのPythonを共存可能にするpyenvをインストールする。
$ anyenv install pyenv /var/folders/0b/7w0c1g251msb55zpbzt6ls9r0000gn/T/pyenv.20221015230118.98707 ~ Cloning https://github.com/pyenv/pyenv.git master to pyenv... Cloning into 'pyenv'... remote: Enumerating objects: 22020, done. remote: Counting objects: 100% (88/88), done. remote: Compressing objects: 100% (57/57), done. remote: Total 22020 (delta 37), reused 69 (delta 24), pack-reused 21932 Receiving objects: 100% (22020/22020), 4.43 MiB | 990.00 KiB/s, done. Resolving deltas: 100% (14897/14897), done. ~ Install pyenv succeeded! Please reload your profile (exec $SHELL -l) or open a new session.
インストールが完了したらシェルをリロード。
$ exec $SHELL -l
pyenvがインストールされた事を確認。
$ pyenv --version pyenv 2.3.5-2-g03a5d653
pyenvの実体は以下のディレクトリにインストールされている。
$ pyenv root /Users/heroween/.anyenv/envs/pyenv
ここまでで、同一環境に複数バージョンのPythonを共存させる事が出来るようになったものの、同じバージョンの環境を複数構成するといった事は出来ない為、それを実現するpyenvのプラグインとなるpyenv-virtualenvもインストールする。
$ git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
インストールが完了したら再びシェルをリロード。
$ exec $SHELL -l
pyenv-virtualenvのインストールを確認する。
$ pyenv virtualenv --version pyenv-virtualenv 1.1.5 (virtualenv unknown)
これでpyenvの設定は完了。
Anacondaをインストールする
pyenvを使って本来の目的となるAnacondaをインストールする。
pyenv install -l
コマンドを実行すると、インストール可能なPython2、Python3や、Anaconda、Miniconda(Anacondaのmininal版)等の各バージョンがリスト表示されるので、そこからインストールしたいAnacondaのバージョンを取得する。
$ pyenv install -l Available versions: 2.1.3 2.2.3 2.3.7 ~~~~~ 中略 ~~~~~ anaconda3-2020.11 anaconda3-2021.04 anaconda3-2021.05 anaconda3-2021.11 anaconda3-2022.05
執筆時の最新版となるanaconda3-2022.05
をインストールする場合、以下のコマンドを実行する(もっとも、anaconda3-2022.05
以前のバージョンはMacOSX-arm64に未対応なので、そもそもM2 Macbook Airにはインストール出来ないのだけども)。
$ pyenv install anaconda3-2022.05 Downloading Anaconda3-2022.05-MacOSX-arm64.sh... -> https://repo.anaconda.com/archive/Anaconda3-2022.05-MacOSX-arm64.sh Installing Anaconda3-2022.05-MacOSX-arm64... Installed Anaconda3-2022.05-MacOSX-arm64 to /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05
インストールバージョンを確認。
$ pyenv versions * system (set by /Users/heroween/.anyenv/envs/pyenv/version) anaconda3-2022.05
GlobalのPython環境をanaconda3-2022.05
に変更。
$ pyenv global anaconda3-2022.05 $ pyenv versions system * anaconda3-2022.05 (set by /Users/heroween/.anyenv/envs/pyenv/version)
conda
コマンドが機能する事を確認する。
$ conda -V conda 4.13.0 $ conda env list # conda environments: # base * /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05
次に、デフォルトシェルにAnacondaの初期化設定をしてやる。
$ conda init fish no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/condabin/conda no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/bin/conda no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/bin/conda-env no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/bin/activate no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/bin/deactivate no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/etc/profile.d/conda.sh no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/etc/fish/conf.d/conda.fish no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/shell/condabin/Conda.psm1 no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/shell/condabin/conda-hook.ps1 no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/lib/python3.9/site-packages/xontrib/conda.xsh no change /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/etc/profile.d/conda.csh modified /Users/heroween/.config/fish/config.fish ==> For changes to take effect, close and re-open your current shell. <==
すると、fishのconfig.fish
ファイルに以下の様なAnacondaの設定が追記され、シェルの起動時に自動でAnacondaがactivateされる様になる。
# >>> conda initialize >>> # !! Contents within this block are managed by 'conda init' !! eval /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/bin/conda "shell.fish" "hook" $argv | source # <<< conda initialize <<<
なお、conda init --all
を実行すると、Anacondaがサポートしている全てのシェル(bash、zsh、tcsh、fish、xonsh、powershell)に設定が追記されるので、複数シェルでまとめて設定したい場合は--all
オプションを使うと便利。
完了したらシェルをリロード。
$ exec $SHELL -l
これでAnacondaのインストールまでが完了となる。
Anacondaの仮想環境を作成してみる
pyenv-virtualenvを使ってAnacondaの仮想環境を作るには、以下のコマンドを実行する。
$ pyenv virtualenv anaconda3-2022.05 myenv Collecting package metadata (current_repodata.json): done Solving environment: done ==> WARNING: A newer version of conda exists. <== current version: 4.13.0 latest version: 22.9.0 Please update conda by running $ conda update -n base -c defaults conda ## Package Plan ## environment location: /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv added / updated specs: - python The following NEW packages will be INSTALLED: bzip2 pkgs/main/osx-arm64::bzip2-1.0.8-h620ffc9_4 ca-certificates pkgs/main/osx-arm64::ca-certificates-2022.07.19-hca03da5_0 certifi pkgs/main/osx-arm64::certifi-2022.9.24-py310hca03da5_0 libcxx pkgs/main/osx-arm64::libcxx-14.0.6-h848a8c0_0 libffi pkgs/main/osx-arm64::libffi-3.4.2-hc377ac9_4 ncurses pkgs/main/osx-arm64::ncurses-6.3-h1a28f6b_3 openssl pkgs/main/osx-arm64::openssl-1.1.1q-h1a28f6b_0 pip pkgs/main/osx-arm64::pip-22.2.2-py310hca03da5_0 python pkgs/main/osx-arm64::python-3.10.6-hbdb9e5c_0 readline pkgs/main/osx-arm64::readline-8.1.2-h1a28f6b_1 setuptools pkgs/main/osx-arm64::setuptools-63.4.1-py310hca03da5_0 sqlite pkgs/main/osx-arm64::sqlite-3.39.3-h1058600_0 tk pkgs/main/osx-arm64::tk-8.6.12-hb8d0fd4_0 tzdata pkgs/main/noarch::tzdata-2022c-h04d1e81_0 wheel pkgs/main/noarch::wheel-0.37.1-pyhd3eb1b0_0 xz pkgs/main/osx-arm64::xz-5.2.6-h1a28f6b_0 zlib pkgs/main/osx-arm64::zlib-1.2.12-h5a0b063_3 Preparing transaction: done Verifying transaction: done Executing transaction: done # # To activate this environment, use # # $ conda activate myenv # # To deactivate an active environment, use # # $ conda deactivate Looking in links: /var/folders/0b/7w0c1g251msb55zpbzt6ls9r0000gn/T/tmp131_n1rs Requirement already satisfied: setuptools in /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv/lib/python3.10/site-packages (63.4.1) Requirement already satisfied: pip in /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv/lib/python3.10/site-packages (22.2.2)
pyenv virtualenv
コマンドを実行すると仮想環境が作成される訳だが、実行環境にconda
コマンドが存在している場合、内部的にはconda create
が使われる様になっている。
$ pyenv versions * system (set by /Users/heroween/.anyenv/envs/pyenv/version) anaconda3-2022.05 anaconda3-2022.05/envs/myenv myenv $ conda env list # conda environments: # base * /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05 myenv /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv
新しく作成したAnacondaの仮想環境へ切り替えるには、conda activate
コマンドを実行。
$ conda activate myenv $ conda env list # conda environments: # base /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05 myenv * /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv $ pyenv versions * system (set by /Users/heroween/.anyenv/envs/pyenv/version) anaconda3-2022.05 anaconda3-2022.05/envs/myenv myenv
あくまでAnaconda内における仮想環境の切り替えとなる為、pyenv側が切り替わることは無い。
pyenv側で切り替えるのであれば、一旦、conda deactivate
コマンドでAnacondaから抜けた後、pyenv activate
コマンドを実行する形となる。
$ conda deactivate $ pyenv activate myenv $ pyenv versions system anaconda3-2022.05 anaconda3-2022.05/envs/myenv * myenv (set by PYENV_VERSION environment variable) $ conda env list # conda environments: # base * /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05 myenv /Users/heroween/.anyenv/envs/pyenv/versions/anaconda3-2022.05/envs/myenv
なお、pyenvでインストールしたAnacondaと、そのAnacondaを元にpyenv virtualenv(conda create)で作成した仮想環境とでは、デフォルトでインストールされるパッケージ群が異なるので注意が必要(仮想環境では作成時に出力されるメッセージの通り、最低限のパッケージしかインストールしない)。
SoftEther VPN Serverの管理者パスワードを変更する
AWSのEC2(OSはAmazon Linux 2)上で稼働しているSoftEther VPN Serverに設定していた管理者パスワードを忘れたという事案が生じ、管理者パスワードの再設定を行ったので、その手順を残しておく。
作業の流れとしては以下の通り。
作業手順
まずは、SofrEther VPN Serverの稼働しているEC2に、SSHなりSession Managerなりで接続する。
接続出来たら以下のコマンドでSoftEther VPN Serverのサービスを停止。
$ sudo su - $ systemctl stop vpnserver
次に、既存の管理者パスワードを削除するのだが、パスワード自体はSoftEther VPN Serverの設定ファイルであるvpn_server.config
にハッシュ化された形で定義されているので、それを直接編集して削除する必要がある。
件のサーバーでは/usr/local/
ディレクトリにインストールされていたので、設定ファイルは/usr/local/vpnserver/vpn_server.config
にあった。
中身を確認すると、以下の様にroot
のServerConfiguration
にHashedPassword
として管理者パスワードが定義されている。
declare root { 〜〜〜 中略 〜〜〜 declare ServerConfiguration { 〜〜〜 中略 〜〜〜 byte HashedPassword D2Ak3pm0cNSlPrtyCWYig1vDcFE=
このHashedPassword
を削除してやる。
$ cp -vip /usr/local/vpnserver/vpn_server.config /tmp ←バックアップ $ vi /usr/local/vpnserver/vpn_server.config ←ServerConfiguration内のHashedPassword行を削除して保存
なお、ServerConfiguration
以外にVirtualHUB
にもHashedPassword
(こちらは仮想ハブのパスワード)が定義されているので、HashedPassword
だけで検索して誤ってVirtualHUB
の方を削除してしまわない様に注意。
vpn_server.config
の編集を終えたら、SoftEther VPN Serverのサービスを再開。
$ systemctl start vpnserver
サービスの起動を確認したら、SofrEther VPN Serverの管理コマンドとなるvpncmd
を使って、新しい管理者パスワードを設定する。
$ /usr/local/vpnserver/vpncmd
実行すると対話式のプロンプトに切り替わり、このコマンドで何をするのか選択肢が表示されるので、「1」(サーバー管理)を入力してエンターを押す。
vpncmd コマンド - SoftEther VPN コマンドライン管理ユーティリティ SoftEther VPN コマンドライン管理ユーティリティ (vpncmd コマンド) Version 4.34 Build 9745 (Japanese) Compiled 2020/04/05 23:39:56 by buildsan at crosswin Copyright (c) SoftEther VPN Project. All Rights Reserved. vpncmd プログラムを使って以下のことができます。 1. VPN Server または VPN Bridge の管理 2. VPN Client の管理 3. VPN Tools コマンドの使用 (証明書作成や通信速度測定) 1 - 3 を選択: 1
すると、作業対象とするサーバーの接続先を問われるのだが、何も入力せずにエンターを押すとlocalhost
が指定される。
接続先の VPN Server または VPN Bridge が動作しているコンピュータの IP アドレスまたはホスト名を指定してください。 'ホスト名:ポート番号' の形式で指定すると、ポート番号も指定できます。 (ポート番号を指定しない場合は 443 が使用されます。) 何も入力せずに Enter を押すと、localhost (このコンピュータ) のポート 443 に接続します。 接続先のホスト名または IP アドレス:
次に、仮想ハブ名の入力を求められるので、今回はサーバー管理モードで接続する為、ここでも何も入力せずにエンターを押す。
サーバーに仮想 HUB 管理モードで接続する場合は、仮想 HUB 名を入力してください。 サーバー管理モードで接続する場合は、何も入力せずに Enter を押してください。 接続先の仮想 HUB 名を入力:
管理者パスワードが設定されている状態だと、このタイミングでパスワードの入力を求められるのだが、vpn_server.config
から正常に削除出来ていれば、このまま管理者モードでSoftEther VPN Serverに接続され、以下の様なプロンプトが表示される。
VPN Server "localhost" (ポート 443) に接続しました。 VPN Server 全体の管理権限があります。 VPN Server>
プロンプトが切り替わったら管理者パスワードの設定コマンドServerPasswordSet
を実行し、新しいパスワードを2回入力すると設定が完了。
VPN Server>ServerPasswordSet ServerPasswordSet コマンド - VPN Server の管理者パスワードの設定 パスワードを入力してください。キャンセルするには Ctrl+D キーを押してください。 パスワード: ************************ 確認入力 : ************************ コマンドは正常に終了しました。 VPN Server>exit
一度、プロンプトを抜けてから、再度vpncmd
を実行してlocalhost
に接続しようとすると、先程とは異なり管理者パスワードの入力を求められる様になっている。
$ /usr/local/vpnserver/vpncmd vpncmd コマンド - SoftEther VPN コマンドライン管理ユーティリティ SoftEther VPN コマンドライン管理ユーティリティ (vpncmd コマンド) Version 4.34 Build 9745 (Japanese) Compiled 2020/04/05 23:39:56 by buildsan at crosswin Copyright (c) SoftEther VPN Project. All Rights Reserved. vpncmd プログラムを使って以下のことができます。 1. VPN Server または VPN Bridge の管理 2. VPN Client の管理 3. VPN Tools コマンドの使用 (証明書作成や通信速度測定) 1 - 3 を選択: 1 接続先の VPN Server または VPN Bridge が動作しているコンピュータの IP アドレスまたはホスト名を指定してください。 'ホスト名:ポート番号' の形式で指定すると、ポート番号も指定できます。 (ポート番号を指定しない場合は 443 が使用されます。) 何も入力せずに Enter を押すと、localhost (このコンピュータ) のポート 443 に接続します。 接続先のホスト名または IP アドレス: サーバーに仮想 HUB 管理モードで接続する場合は、仮想 HUB 名を入力してください。 サーバー管理モードで接続する場合は、何も入力せずに Enter を押してください。 接続先の仮想 HUB 名を入力: パスワード: ************************ VPN Server "localhost" (ポート 443) に接続しました。 VPN Server 全体の管理権限があります。 VPN Server>exit
ここで新しいパスワードを入力してlocalhost
に接続される事を確認し、問題が無ければ管理者パスワードの変更作業は完了となる。
Redashコンテナの環境変数を更新する
OSSのダッシュボードサービスであるRedashをAWSで運用しているのだが(公式のEC2イメージを使用)、メールサーバーの設定を変更する必要があったので、その手順をまとめておく。
Redashはユーザー作成時の通知等でメールを使えるのだが、そのメールサーバーの設定はWebコンソールからでは行えず、Dockerコンテナの環境変数に定義してやる必要がある。
Dockerコンテナに読み込ませる環境変数ファイルは/opt/redash/env
に設置されており、docker-composeによりevn_fileでコンテナに展開されている。
メールサーバー関連で最低限必要となる環境変数は以下の通り(詳細は公式ドキュメントを参照)。
- REDASH_MAIL_SERVER
- REDASH_MAIL_PORT
- REDASH_MAIL_USE_TLS
- REDASH_MAIL_USERNAME
- REDASH_MAIL_PASSWORD
- REDASH_MAIL_DEFAULT_SENDER
運用している環境ではAWS SES(東京リージョン)を使っていたので、定義している値はこんな感じ。
REDASH_MAIL_SERVER=email-smtp.ap-northeast-1.amazonaws.com REDASH_MAIL_PORT=587 REDASH_MAIL_USE_TLS=true REDASH_MAIL_USERNAME=AKIAXXXXXXXXXXXXXXXX REDASH_MAIL_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX REDASH_MAIL_DEFAULT_SENDER=no-reply@example.com
ここで定義していたSESのSMTP認証情報を変更した為、REDASH_MAIL_USERNAMEとREDASH_MAIL_PASSWORDを更新してRedashコンテナに反映しなければならなくなった。
docker-compose upで更新
Redashコンテナの環境変数を更新するのは実に簡単で、/opt/redash/env
の内容を変更した後、docker-compose up -d
でコンテナを再作成してやるだけ。
$ sudo su - $ cd /opt/redash/ $ vi env ←定義された値を編集 $ docker-compose up -d
docker-compose stop/start
でコンテナを再起動するだけでは、稼働中のコンテナがenv_fileに定義された環境変数を再展開しない為、一旦、コンテナ自体を作り直してやる必要がある。
なお、Redashのデータを保管するpostgresコンテナのVolumeは、ホスト側にマウントされて永続化しているので、コンテナを再作成したとしてもデータが失われる事は無い。
docker-compose stop/startで更新
一応、コンテナを作り直す事はせずに、コンテナファイル自体を書き換えてしまい、dockerデーモンも再起動するのであれば、docker-compose stop/start
でコンテナの環境変数を更新する事も出来る。
Dockerコンテナは起動すると、コンテナ毎に/var/lib/docker/containers/コンテナフルID
といったディレクトリが作成され、その配下に各種の関連ファイルを保存している。
例えば、コンテナID(省略形)「584d782142d7」が起動した場合であると、
/var/lib/docker/containers/584d782142d75d3d67738007f054cccbc846ac22b2e3ad370013754c0f491fda
といったディレクトリが出来て、配下にはコンテナの実体に関わる様々なファイル群が配置される。
$ cd /var/lib/docker/containers/584d782142d75d3d67738007f054cccbc846ac22b2e3ad370013754c0f491fda/ $ ls -la -rw-r----- 1 root root 339367 8月 31 21:12 584d782142d75d3d67738007f054cccbc846ac22b2e3ad370013754c0f491fda-json.log drwx------ 2 root root 4096 8月 31 19:30 checkpoints -rw------- 1 root root 4162 8月 31 19:30 config.v2.json -rw-r--r-- 1 root root 1487 8月 31 19:30 hostconfig.json -rw-r--r-- 1 root root 13 8月 31 19:30 hostname -rw-r--r-- 1 root root 174 8月 31 19:30 hosts drwx------ 3 root root 4096 8月 31 19:30 mounts -rw-r--r-- 1 root root 77 8月 31 19:30 resolv.conf -rw-r--r-- 1 root root 71 8月 31 19:30 resolv.conf.hash
この内の、config.v2.json
というファイルがコンテナの状態を記録しており、
$ cat config.v2.json {"StreamConfig":{},"State":{"Running":true, 〜〜〜 中略 〜〜〜 ,"SeccompProfile":"","NoNewPrivileges":false}
JSON中に以下の様なリスト形式でコンテナに展開されている環境変数を持っている。
"Env":["REDASH_MAIL_SERVER=email-smtp.ap-northeast-1.amazonaws.com","REDASH_MAIL_PORT=587", 以下略]
なので、このファイル自体を書き換えてしまえば、コンテナを作成し直さずに、コンテナの環境変数を更新する事が出来る。
手順としては、起動中のコンテナ、Dockerデーモンという順番で停止してから、config.v2.json
のEnvの値を書き換えて、Dockerデーモン、コンテナと起動しなおす形になる。
$ sudo su - $ cd /opt/redash/ $ docker-compose stop $ systemctl stop docker $ cd /var/lib/docker/containers/コンテナフルID/ $ cp -vip config.v2.json ~/tmp ←バックアップ $ vi config.v2.json ←Envの値を書き換える $ cd - $ systemctl start docker $ docker-compose start
config.v2.json
は環境変数だけを保持している訳では無いので、環境変数以外の設定を変えたいといった場合にも色々と応用は可能である。
しかし、Redashコンテナの環境変数を更新するだけなのであれば、面倒なのでわざわざこの方法をとる必要性はあまり無いかと思う。
Dockerコンテナのレイヤーが増えないので、ストレージ容量的には優しいというメリットが微妙にあるかもしれない。
ちなみに、Redashを構成するコンテナ群は以下の様になっているのだが、
- nginx
- server
- scheduler
- scheduled_worker
- adhoc_worker
- redis
- postgres
ユーザー作成時にメール送信するのはschedulerなので、メールの利用がそこだけなのであれば、最低限、schedulerのconfig.v2.json
だけを更新すれば事は足りる。
API Gatewayの実行エラー「Execution failed due to an vpc link error」について
API GatewayのVPCリンクエラー
API GatewayのVPCリンクを使ったREST API(バックエンドはECSサービス)において、単発の500エラーを検出したのだが、その際、実行ログに以下の様なエラーメッセージが出力された。
(xx389x71-xx67-442x-9073-441x13535xx1) Execution failed due to an vpc link error
実行ログの流れを確認すると、エンドポイントリクエスト後に、このエラーが生じてAPI実行が異常終了しており、メッセージのままの意味であれば、VPCリンクに何かしらの異常が生じたものと思われる。
HTTP APIの場合、VPCリンクは60日間トラフィックが生じないと、ステータスがAVAILABLE
からINACTIVE
へと変わり、VPC内の専用ENIが削除されてしまい、API通信が一時的に不能状態に陥るといった仕様がある(再びAPIリクエストされるとENIが復活するものの、プロビジョニング完了までの数分は通信不能が続く)。
しかし、問題の起きたAPI GatewayはREST APIで、VPCリンクにはVPCエンドポイントサービスが使われており、こちらはHTTP APIとは異なり、長期間のトラフィックが生じないと自動削除されるといった仕様にはなっていない(そして、このAPIは常時、高トラフィックでもあった)。
VPCリンクに設定しているVPCエンドポイントサービスやNLBのメトリクスを確認したのだが、特段の異常が見られなかった為、明確な原因を把握しておきたく、このメッセージを調べてみたものの、公式の開発者ガイドや検索結果に引っかかるものは無かった。
エラーの原因
自身で調べてみても、結局分からなかったので、AWSサポートに問い合わせてみた。
回答としては、やはり、API GatewayからNLB間における一時的な通信障害に起因するもの(明確にどこで発生したもの、というところまでは判明しなかったけど)、という事であった。
通信において経路上のどこでもエラーが発生する可能性はあり、完全に無くす事は出来ない為、処理上で問題となるのであれば、対応としてAWSのドキュメントにもある様に、クライアントサイドでリトライを実装するのが望ましい。
今回のケースでは頻発していた訳でもなく、単発のエラーであったのと、そもそも処理としてリトライもされており、特に大きな問題でもなかったので、この回答をもって調査を終了とした。
API GatewayでVPCリンクを使ったREST APIを利用する上では、VPCエンドポイントサービスやNLBで確認出来るステータスやメトリクスに異常が無くとも、こういったエラーは生じ得る、という事を念頭に置いておく必要がある。
aws-sqsdのSignature(署名リクエスト)エラーについて
aws-sqsdで発生したエラー
Elastic BeanstalkのWorkerインスタンスがSQSのメッセージを捌かなくなり、5XXを頻発するという障害の調査を依頼され、aws-sqsdを確認したところ、ログに以下の様なエラーが出力されていた(タイムスタンプは架空)。
2022-07-24T00:01:02Z healthcheck-err: failed to pull messages from SQS queue with error: Signature not yet current: 20220724T000101Z is still later than 20220723T230411Z (20220723T224911Z + 15 min.) 2022-07-24T00:01:02Z pollers: daemon exception during polling message from sqs: Signature not yet current: 20220724T000102Z is still later than 20220723T230411Z (20220723T224911Z + 15 min.) 2022-07-24T00:01:02Z metrics: Aws::CloudWatch::Errors::SignatureDoesNotMatch: Signature not yet current: 20220724T000102Z is still later than 20220723T225411Z (20220723T224911Z + 5 min.) 2022-07-24T00:01:02Z pollers: daemon exception during polling message from sqs: Signature not yet current: 20220724T000102Z is still later than 20220723T230412Z (20220723T224912Z + 15 min.)
メッセージを読むと時刻のズレに原因があるものと推察出来る。
実際にその通りで、これはAWSのAPIを実行したマシンの時刻が現在時からズレていると生じる、AWS APIの署名リクエストのエラーの一種。
AWS APIはCLIやSDK等でのリクエスト時に、APIの実行時刻をx-amz-dateリクエストヘッダー、乃至はクエリパラメーターとして付与するのだが、その時刻がAPIサーバー側とズレているとこのエラーが返ってくる。
詳細な仕様は下記リンク先の公式ドキュメントを参照。
署名リクエストのエラーは要素に応じて何種類かあるのだが、CLIやSDKを使ってAWSのリソースを操作する場合、ユーザーが意識せずとも暗黙的に正規のリクエストが送られる為、署名リクエストのエラーの中でも、ユーザー側の環境起因で発生する事になるこのエラーに遭遇する確率は高い気がする。
なお、この様なエラーは当然だがaws-sqsdに限らず、他のAWSサービスにおいても発生するもので、S3とかだと以下の様なエラーメッセージをちょくちょく目にする事がある。
The difference between the request time and the current time is too large.
職場でAWSの技術サポート的な事をやっていると、S3の操作が出来ないといった相談を受け、エラーメッセージを送ってもらったらこれで、作業マシンの時刻がズレていました、というのは割とあるあるだったりする。
エラーの原因
上述したaws-sqsdのエラーの原因自体は既に書いた通りなのだが、今回、このエントリを書く切っ掛けとなった障害は、Amazon Linux 2で稼働しているElastic BeanstalkのWorkerインスタンスで発生していた。
Amazon Linux 2の場合、タイムサーバーはchronyが稼働しており、標準で169.254.169.123のリンクローカルIPアドレスでアクセス出来るAmazon Time Sync Serviceを利用して時刻の同期が行われている。
にも関わらず、件のWorkerインスタンスで時刻を確認したところ、数時間単位で未来時刻にズレていたのである。
どういう事だろうかと、まず、journalctlでchronydを見てみたら、以下の様なメッセージが表示された(タイムスタンプは架空)。
Jul 24 07:45:53 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal chronyd[1683]: Forward time jump detected! Jul 24 07:46:27 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal chronyd[1683]: Can't synchronise: no selectable sources Jul 24 07:49:16 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal chronyd[1683]: Selected source 169.254.169.123 Jul 24 09:59:52 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: chronyd.service: main process exited, code=exited, status=1/FAILURE Jul 24 09:59:52 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: Unit chronyd.service entered failed state. Jul 24 09:59:52 ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: chronyd.service failed.
唐突に時間が進んでしまい、AWS Time Sync Serviceとの同期が切れた後、プロセスが異常終了してしまった模様で、そのまま再起動する事も無く、chronydがお亡くなりになっていた。
この前に何があったのか、全体のログを確認してみたところ、直前にOOM Killerが発生しており、aws-sqsdのプロセスが殺されて、chronyd以外のサービスにも色々と異常をきたしていた事が判明。
sysstatでリソースの状況を確認していくと、インスタンスが起動した段階からメモリの使用率は90%程度になっており、aws-sqsdがメッセージを処理し始めてから1時間程度でメモリがパンク寸前に。
また、journalctl -u chronyd
でユニット指定で絞り込んだ時には表示されなかったが、systemdがchronydサービスを異常終了した後に、以下のエラーメッセージが出力されていた事も分かった。
Jul 24 09:59:52 ip-xxx-xxx-xxx-xxx chronyd[1683]: Fatal error : Possible infinite loop in scheduling
このエラーメッセージを検索してみたところ、chronyユーザーのメーリングリストのアーカイブが引っ掛かり、同じ様にメモリの高負荷状況下でchronydがクラッシュして再起動に失敗するという事例が報告されていた。
スレッドを追っていくと、chronyのメンテナーによる返信として、メモリの枯渇でページロードを待機し続けてこのエラーが生じていると推測されており、その後、メモリの高負荷時にこのエラーが出る旨が書かれている。
という訳で、根本原因としてメモリの容量不足が考えられる事は間違い無さそうである。
時刻の同期が狂った状況について、chronyの各種ログを見ればより詳細が分かるのかもしれないが、chronyのログは標準だとsyslogに吐かれるもの以外は出力されない設定になっている。
chronyの設定ファイルである/etc/chrony.conf
のlog
コマンドは以下の通りコメントアウトされており、/var/log/chrony
ディレクトリには何も吐かれない。
# Specify directory for log files. logdir /var/log/chrony # Select which information is logged. #log measurements statistics tracking
Amazon Linux 2でもこの様な設定なので、残念ながらこれ以上の事は分からなかった。
対応と課題
そもそも、chronyの問題は置いておいたとしても、少なくともメモリの容量が不足しているという問題点は明白だったので、まずは、Workerインスタンスのスケールアップで当座を凌いでもらい、Workerアプリケーションのプロセスやメモリ使用量の監視を後日の課題として、一先ずの調査を終えた。