Spring boot で多くの非同期処理を行うAPIを作成する時に気をつけたこと
はじめに
自己紹介
こんにちはこんばんは、さっちゃん(@toshiakisan1127)です。
(未経験で"駆け出し"てから)5年目のソフトウェアエンジニアで、AWS、Spring boot、Nuxtを主に触ってきました。最近は部署移動して、Claude codeやCopilotを使ってAIに設計書を書かせたり脆弱性の対応をやってもらったりしてます。kafkaとredisのキャッチアップも急いでやってます。
github.com
この記事について
自分は1年ほど、アプリケーションのパフォーマンス改善のリードをしていました。
その中で、今回はSpring bootで10以上の子スレッドを使うAPIを作成した時にプロジェクトをリードするにあたって気をつけたこと、躓いたことを備忘録としてここで書いておきます。
作成したAPI
今回作成したAPIは、いわゆるBFFに相当するものです。ここで言うBFFはBackend For Frontendで、1APIが1画面に必要な情報を他のマイクロサービスからかき集める責務を持っています。(BFFといってもNuxt/Nextのようにフレームワーク内にサーバーの機能を持っていてCookieをいじったりするものもありますが、それとはまた別の責務を持つマイクロサービスです。)
課題
開発していると、最初は1画面から1APIというようなルールを設けているプロダクトは多いかと思います。これにはクライアント-オリジン間(もしくはクライアント-CDN間)の往復を少なくすることで画面の描画を早くしたり、強制的に画面の責務を小さくなるようにするなど多くのメリットがあります。しかしながら、プロダクトが成長するにつれて組織も肥大化すると、さまざまな要因によってそのルールは破られてしまいます。今回の課題は、1画面から多数のAPIが同期的、非同期的に呼び出されることでブラウザ-オリジン間の通信が複数回にわたり、画面を開くのに数秒かかっていました。また、以下の制約もありました。
目標
1秒前後でページを表示する→達成
解決策
BFFの役割をするAPIを配置して、静的なAPI以外はバックエンドのマイクロサービス間で叩くことでブラウザ-オリジン間の通信を最小限にする。(BFFからは非同期で各マイクロサービスから情報を取得します)
BFF導入前のイメージ
graph TB
Client[クライアント<br/>ブラウザ]
Client -->|非同期<br/>10以上のAPI呼び出し| MS1[マイクロサービス1<br/>ユーザー情報]
Client -->|非同期| MS2[マイクロサービス2<br/>プロフィール]
Client -->|MS1のレスポンスを使って呼ぶ| MS3[マイクロサービス3<br/>コンテンツ]
Client --> MS4[マイクロサービス4<br/>]
Client --> MS5[マイクロサービス5<br/>]
Client --> MS6[マイクロサービス6<br/>]
Client --> MS7[マイクロサービス7<br/>]
Client --> MS8[マイクロサービス8<br/>]
Client --> MSN[その他多数の<br/>マイクロサービス]
style Client fill:#ff9999
style MS1 fill:#cccccc
style MS2 fill:#cccccc
style MS3 fill:#cccccc
style MS4 fill:#cccccc
style MS5 fill:#cccccc
style MS6 fill:#cccccc
style MS7 fill:#cccccc
style MS8 fill:#cccccc
style MSN fill:#cccccc
classDef problem fill:#ff9999,stroke:#333,stroke-width:2px
class Client problem
BFF導入後のイメージ
graph TB
Client[クライアント<br/>ブラウザ]
BFF[BFF API<br/>Backend for Frontend<br/>Spring Boot + WebFlux]
Client -->|1回の通信| BFF
BFF -->|非同期並列処理<br/>CompletableFuture| MS1[マイクロサービス1<br/>ユーザー情報]
BFF -->|非同期| MS2[マイクロサービス2<br/>プロフィール]
BFF -->|依存関係を内部で制御| MS3[マイクロサービス3<br/>コンテンツ]
BFF --> MS4[マイクロサービス4<br/>]
BFF --> MS5[マイクロサービス5<br/>]
BFF --> MS6[マイクロサービス6<br/>]
BFF --> MS7[マイクロサービス7<br/>]
BFF --> MS8[マイクロサービス8<br/>]
BFF --> MSN[その他多数の<br/>マイクロサービス]
style Client fill:#99ff99
style BFF fill:#66b3ff
style MS1 fill:#cccccc
style MS2 fill:#cccccc
style MS3 fill:#cccccc
style MS4 fill:#cccccc
style MS5 fill:#cccccc
style MS6 fill:#cccccc
style MS7 fill:#cccccc
style MS8 fill:#cccccc
style MSN fill:#cccccc
classDef solution fill:#99ff99,stroke:#333,stroke-width:2px
classDef bff fill:#66b3ff,stroke:#333,stroke-width:3px
class Client solution
class BFF bff
気をつけたこと
案件を止めないためにAPIは毎週一つか二つずつBFFに移行
大規模な組織で毎日のように手が入る画面なので、一気に移行するわけにはいきませんでした。ロードマップを引いて、案件を進める方々と調整し、毎週影響調査、テスト項目書作成、実装、テスト、リリースしました。これは、事故が起きた時に影響範囲を限定的にするためでもあります。
エラーハンドリングは既存に合わせる
非同期処理で悩むことの一つはエラーハンドリングをどうするかです。失敗しても処理を継続させたい場合もあれば、絶対に見せられないこともあります。この辺は既存の画面(ユーザーへの見せ方)に合わせることでステークホルダーと合意をとりました。
Spring boot のデフォルトのスレッドプールの設定
この辺の話。
誤解しがちなThreadPoolTaskExecutorの設定
デフォルトのままだと、スレッドプールサイズが上限に達すると、タスクがキューに無限に蓄積され、処理が止まったりメモリ不足のリスクがあります。
複雑な非同期処理にはCompletableFutureを使った
Spring bootを使って非同期処理を行うならまずは@Asyncを使いますが、複雑な非同期処理の制御をしたい場合はCompletableFutureがやりやすいです。(他にいい方法があれば教えてください)
applyAsync, thenApplyAsync, thenCombineAsyncなど便利なメソッドが多いです。
CompletableFutureのExecutorには統一されたThreadPoolを使う
管理するThreadPoolはひとつにしないと管理の複雑さが増加します。
trace idをログから追えるように、ExecutorをMicrometerでラップする
trace idの情報はThreadLocalに保存されているが、Executorを指定しないでCompletableFutureを使うとThreadLocalの情報が子スレッドに引き継がれない。次のようにContextExecutorServceでデフォルトのExecutorをラップする。
ContextExecutorService.wrap(
taskExecutor.getThreadPoolExecutor(),
ContextSnapshot::captureAll
);
最後に
gRPCにするなどもっとやりようはありますが、その辺は必要になったらでいいでしょう。
これは数ヶ月かかったプロジェクトでしたが、自分がリードした中ではかなりうまくいったので誰かの参考になればいいなと思います。
【Nuxt3.6.5】Nuxt3でSPAのデフォルトのローディング画面を自作ページに変えたい
やりたいこと
HTMLやJSの取得、middlewareの実行中にNuxt3のデフォルトのローディング画面がでます。
This is what I mentioned. pic.twitter.com/VtcOC595nl
— さっちゃん (@toshiakisan1127) 2023年7月22日
個人開発だとこれはこれでかっこいいのですが、業務で使うとなるとNuxtで書かれていることがあまりにも分かりすぎますし、ユーザーには必要のない情報です。せっかく自分たちのコンポーネントでアプリケーションの統一感を出していても、このロード画面が出ることで統一感が損なわれUXが落ちる可能性があります。そこで、このロード画面を自作のページにしたいです。
結論
Nuxt3は ~/app/spa-loading-template.htmlを取ってきて表示しているだけなので、~/appディレクトリを作成し、そこにspa-loading-template.htmlを作成すればokです。
デフォルトのNuxtのロード画面から、自作のHTMLを表示させることができた。(動画のSecretページを押した後のspinner)
— さっちゃん (@toshiakisan1127) 2023年7月22日
ロード時には ~/app/spa-loading-template.htmlを見るので、そこに自分のHTMLファイルをおけば好きにリソース取得時のロード画面を表示できる。
c.f. https://t.co/Zgx52eDLdf) pic.twitter.com/yQEHiyvk6g
参考
【Nuxt3.5.x】middlewareのなかでnavigateTo(path or url, {external: true})を使ってリダイレクトしようとすると見せたくないページが見えてしまう
これはなに?
Nuxt3にて、ページAからページBに遷移する際に、middlewareの中でnavigateToを使ってlocation.hrefまたはlocation.replaceでリダイレクトするとページBがちらっと見えてしまう現象の解決策を書きます。
発生した環境
- Nuxt3.5.3
- Chrome
結論
Nuxt3.6以上にしてください。
はじめに
こんにちは。最近はもっぱらフロントエンドばかり触っている3年目中途半端エンジニアのさっちゃんです。バックエンドとクラウドインフラ(AWS)もちょこちょこやります。猫が好きです。Twitter: @toshiakisan1127
さて、今回は自分が悩んだことを共有しておきます。簡単に言ってしまえばNuxt側の問題だったんですが、修正されたのが1ヶ月前と新しいこともありたどり着くまでに苦労しました。
前提:Nuxt3ではmiddlewareで、ユーザーが各ページに遷移できるかの認可をかけるのが一般的かと思います。そしてユーザーが閲覧権限を持っていなければ適切なページにnavigateToでリダイレクトしますが、ここではそのリダイレクト先のページが外部のサイトであることを想定して、navigateTo(path, {external: true}) でリダイレクトすることを考えます。
現象:indexページからSecretページに遷移する際にmiddlewareの中でリダイレクトされてログインページに遷移するが、その時にSecretページの内容が一瞬見えてしまう。
nuxt3.5.x
— さっちゃん (@toshiakisan1127) 2023年7月22日
secret のページが一瞬見えてしまっている pic.twitter.com/gXlLKvHM2I
再現方法:
github.com
これをcloneしてきて、package.jsonにあるnuxtのversionを3.5.3にしてREADMEに書いてある手順を実行すれば再現できます。
調査
- navigateToでリダイレクトするならそこで処理が終わって欲しいのに、実際は次のページのmountまで処理されている。
- navigateToの後にsleepを入れれば白画面になり一旦問題ないが、テクニカルなのでやりたくない
- navigateToの後にabortNavigationをすれば見せたくないページのチラ見せは無くなるが、今度は404のチラ見えが始まる。
navigateToのオプションやいろいろ試しましたが、結局このissueに辿り着きました。
github.com
やってることsetTimeoutとほぼ変わらんけど
> I like the idea of returning a never-resolving promise (return new Promise*1 for this case 👍
これ頭いいな。
これにより、次の動画のようになりました。
検証用に1時間半くらいでパッと作ったけど良い感じ!https://t.co/CRh2CrZfgE pic.twitter.com/Z5ucP4qLpr
— さっちゃん (@toshiakisan1127) 2023年7月21日
*1:) => {}
未経験からソフトウェアエンジニアになって2年が経ちました。
これはなに
開発未経験からソフトウェアエンジニアになって2年が経ったので、自分が何ができるのか棚卸ししておくだけの記事です。
目的
- これからエンジニアになる人への道標
- 何やっているかを聞かれた時に投げつける用
- 転職の際に職務経歴書に貼る用(今のところ予定なし)
自己紹介
こんにちは。さっちゃん(Twitter:@toshiakisan1127)です。猫が好きです。
物理学科を卒業して大学院を中退して未経験でソフトウェアエンジニアになりました。詳しくは過去記事を見てもらえればと思います。
あれから2年と数ヶ月、今のところ何とかやれています。今回は1年目と2年目を振り返りつつ、技術の棚卸しを行います。毎年やるのは、新卒で入った会社の先輩に、自分が何ができるか分からなくなるからやっておいた方がいいと言われてその通りだなと納得したからです。一年目は以下の記事に書いています。
physics-heibon.hatenablog.com
ちなみに、特に転職を考えているからというわけではないです。たまにカジュアル面談とかやってますが、それでも結局今の会社がいいと感じています。別に会社が好きではないのですが、部の人たちは好きですし給料も3年目にしては悪くはないと思ってます。働き方も柔軟。何より学ぶことが多くありますし周りもみんなそれぞれすごいポイントがあって刺激になります。もちろん思うことはなくはないですが、それはどこに行ってもあるものなのでokです。
1年目
大学院を中退して6月の終わりに就職活動を始め、新卒でIT企業のポテンシャル枠の採用に潜り込みました。ソフトウェアエンジニアになりたかったので入社まで少しでもプログラミングに慣れようと、9ヶ月くらいAtCoderで競技プログラミングをやったり、基本情報技術者試験やJavaScriptの勉強をしました。2.5ヶ月の研修では、プログラミングに触ったことがない、または少し触ったことがある人たちで、JavaScriptから始まってVue, Spring boot, PostgreSQL, Dockerを使ってローカルで動く認証認可付きのチャットアプリを作成しました。この時は自分はたまたまリードする方だったので業務時間中は周りや他のチームの手伝いに行って業務後2時くらいまで勉強や検証を行っていました。ある日は朝5時まで同期とやっていて、研修で教えてもらった内容の深掘りをやったりして楽しかったです。これが青春かと思いました。
7月からは新規開発のチームにアサインされたのですが、まず業務で書くコードの膨大さに驚いて、1ヶ月くらいは何が分からないかも分からない状態でした。しかし、先にエンジニアとして採用されていた同期や周りの人に恵まれてなんとか...なっていたかは怪しいです。
9月あたりからは新しいマイクロサービスをツヨツヨエンジニアと一緒に立ち上げることになったのですが、ここでAWSの話が会議に出てきてまた何も分からない状態になりました。VPCすらよく知らなかったですし、それなんですか?と聞いたらAWSの英語ドキュメントを渡されて読めといわれたので頑張って読んでました。休日もAWSの勉強をして、11月にはAWS SAAの資格を取ったあたりから設計にも口を出せるようになって楽しかったです。
この頃はSpring bootでAPIを建てたり、AWS SAM(CloudFormation)でIaC化したり、LambdaをPythonで書いてCognito、Neptune、DynamoDBで認証認可の機構を作ってました。Auth2.0の勉強も必要に応じてやりました。
2月あたりにはkubernetesの勉強を始めて業務でHelmを使い始めたり、SAMでデプロイしていたものの管理が辛くなってきたのでTerraformに全書き換えしました。特にkubernetes周りはめっちゃ強くて良い先輩と一緒にペアプロしていて、日曜日にも二人で出社して誰もいない会社で踊るなど一番楽しい期間でした。
4月あたりからはフロントエンドの人が足りないと言うことで自分もvueを書いてました。プロダクトオーナーと要件の認識を合わせてUIを考えて合意を取ってから実装、バックエンドとの疎通、stgテストを行いました。これを7月まで続け、転職することになります。(転職理由については直接会って聞いてください、大体家庭の事情です。)
2年目
8月からは心機一転、新しい会社でtoBの大規模開発に参画しました。すでに出来ている文化にはなかなか馴染めず、最初の3ヶ月くらいは毎日つらいつらいと言っていましたが、この辛さは自分が選んだものであり、この辛さが自分の成長のためには必要だと思っていたので感情とは裏腹に計画通りだなーと客観的に考えていました。最初はフロントエンドの簡単な開発から入りましたが、大規模なシステムのコードを見るのは初めてだったのでまた途方にくれることになりました。しかし、前職ですぐに泣きつく重要さを理解していたので何も出来ずに時間が過ぎるというようなことはなかったように思います。会議で出た分からない用語は全部メモして、適当な人の時間を確保して壁打ちしてました。
そんなこんなで引き続きVue、Spring bootを中心に案件をこなしながら最近まで生きています。大規模開発ならではの事例として、大量のレコードを捌く時に適当にやっているとO(N^2)になってしまうところをO(NlogN)にするなど、競技プログラミングで培ったパフォーマンスを気にする実装が出来て興奮しました。「月刊 競技プログラミングは役に立つのか」A. 俺には役に立った!
やっていることとしては基本的に、企画の方がやりたいことに対して何のためにやるのか、どういう手段でやるのか、小さく早くユーザーに価値を届けるにはどうすればいいかを一緒に考えて、概要の設計をして見積もってGOが出れば詳細に設計したり実装、テスト、リリースまで行っています。
そんなこんなで最近は品質を保つためにチーム内で学習コンテンツを用意して半年に一度やってもらったり、パフォーマンス改善部隊としてパフォーマンスを測定する環境を整えて改善し、よくない実装が入ったら修正の提案をしたり楽しくやってます。また、Nuxt3も業務で触るようになりましたが、ルールがあってvueに比べて好き勝手かけないところが自分は大好きです。個人開発でもNuxt3 + app runnerを使っています。
技術
()はチームでは使っていたが自分は触っていないのであまり自信がない。思いついた順です。
終わりに
AWS DOPと英語を勉強中!まったりがんばろ〜。
grinem_python チートシート
これはなに?
gremlin_pythoについての知見が自分の中で溜まってきたので、共有も兼ねてここに残しておきます。
目標
gremlin python で基本的なクエリの書き方が分からない方が雰囲気で書けるようになる
こんにちは
ソフトウェアエンジニア一年生のさっちゃん(twitter: @toshiakisan1127)です。普段はWebアプリケーションのバックエンドをSpring boot, Pythonを使って書いたり、CloudFormationやTerraformでAWS上のリソースを管理をしています。好きなAWSのサービスはLambdaです。
はじめに
この記事を見る方はgremlin pythonについてはご存知だと思いますので、グラフDBやgremlin python自体の説明は省きます。知らない方は Amazon Neptune 触ってみた。~グラフDB楽しい。~ を見てもらえるとありがたいです。 また、Neptuneを使っている方はjupyter notebookからグラフを操作することもあると思いますが、その際にgremlin_pythonと書き方が少し違ったりするので、gremlin_pythonでの書き方と同時にjupyter_notebookでの書き方も一緒に載せておきます。
version
gremlinpython==3.5.2 python3.8
グラフDBとの接続
Lambdaなどから接続できない場合はセキュリティグループの設定やプライベートサブネットのルートテーブルのデフォルトルートがNATゲートウェイに向いているかを確認してください。
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection from gremlin_python.process.anonymous_traversal import traversal g = traversal().withRemote(DriverRemomteConnection("wss://(endpoint_name):(port_number)/gremlin','g'"))
Neptuneのマネジメントコンソールから開いた場合は, %%gremlin をセルの最初に書けば g というのが GraphTraversalSourceオブジェクトとして認識されます。
書き込み系
頂点を追加する
gremlinpython
g.addV("cat").toList() # Labelが cat の頂点ができる。戻り値はvertex object のリスト: [ v[abcd-12e...] ] .
Neptune jupyter notebook
%%gremlin
g.addV("cat")
頂点を作成する時にpropertyを追加する
gremlinpython
%%gremlin g.addV("cat").property("name", "Tama").toList() # idがuuid, Labelが cat , name property が Tama の頂点ができる。
Neptune jupyter notebook
%%gremlin g.addV("cat")..property("name", "Tama")
頂点のpropertyを変更する
gremlinpython
from gremlin_python.process.traversal import Cardinality cat_vertex = g.addV("cat").property(Cardinality.single, "name", "Tama").toList()[0] # idがuuid, Labelが cat , name property が Tama の頂点ができる。 g.V(cat_vertex).property(Cardinality.single, "name", "Pochi").toList()
Neptune jupyter notebook
%%gremlin g.addV("cat").property("name", "Tama")
%%gremlin g.V().has(id, "cat_vertex_id").property(single, "name", "Pochi")
辺を追加する
from gremlin_python.process.traversal import Cardinality cat_vertex = g.addV("cat").property(Cardinality.single, "name", "Tama").toList()[0] food_vertex = g.addV("food").toList()[0] g.addE('love').from_(cat_vertex).to(food_vertex).toList()
Neptune jupyter notebook
%%gremlin g.addE("love").from(g.V().has(id, "vertex1_id")).to(g.V().has(id, "vertex2_id"))
辺にpropertyを追加する
from gremlin_python.process.traversal import Cardinality cat_vertex = g.addV("cat").property(Cardinality.single, "name", "Tama").toList()[0] food_vertex = g.addV("food").toList()[0] g.addE('love').property("hoge", "fuga").from_(cat_vertex).to(food_vertex).toList()
Neptune jupyter notebook
%%gremlin g.addE("love").property("hoge", "fuga").from(g.V().has(id, "vertex1_id")).to(g.V().has(id, "vertex2_id"))
読み込み系
特定のIDを持つ頂点を取得する
from gremlin_python.process.traversal import T cat_vertex_list = g.V().has(T.id, "<vertex_id>").toList()
Neptune jupyter notebook
%%gremlin g.V().has(id, "<vertex_id>")
特定のLabelを持つ頂点を取得する
cat_vertex_list = g.V().hasLabel("cat").toList()
Neptune jupyter notebook
%%gremlin
g.V("cat")
特定のpropertyを持つ頂点を取得する
cat_vertex_list = g.V().has("property_1", "hoge").toList()
Neptune jupyter notebook
%%gremlin cat_vertex = g.V().has("property_1", "hoge")
特定のIDを持つ辺を取得する
from gremlin_python.process.traversal import T edge_list = g.E().has(T.id, "<edge_id>").toList()
Neptune jupyter notebook
%%gremlin
edge = g.E().has(T.id, "<edge_id>")
特定のLabelを持つ辺を取得する
edge_list = g.E().hasLabel("some_label").toList()
Neptune jupyter notebook
%%gremlin
edge = g.E("some_label")
特定のpropertyを持つ辺を取得する
edge_list = g.E().has("property_1", "hoge").toList()
Neptune jupyter notebook
%%gremlin edge = g.E().has("property_1", "hoge")
特定の頂点から伸びる辺を取得する
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").outE().toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").outE()
特定の頂点から伸びる辺を取得する(Labelで絞る)
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").outE("some_edge_label").toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").outE("some_edge_label")
特定の頂点に入る辺を取得する
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").inE().toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").inE()
特定の頂点に入る辺を取得する(Labelで絞る)
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").inE("some_edge_label").toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").inE("some_edge_label")
特定の頂点に関する辺を向きに関係なく取得する
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").out
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").both("some_edge_label")
特定の頂点から別の頂点へ伸びる辺を取得する
from gremlin_python.process.traversal import T edge_list = g.V().has(T.id, "some_vertex_id").outE("some_edge_label").as_('e').inV().hasLabel('vertex_label').select('e').toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id").outE("some_edge_label").as_('e').inV().hasLabel('vertex_label').select('e')
辺を無向辺だとみなしたときに連結な頂点の中から特定の頂点を探す
from gremlin_python.process.traversal import T #dedup().by(T.id) でidが重複している格納しているリストの中から消す。これによって無限ループにならない。 vertex_list = g.V().has(T.id, "some_vertex_id") \ .repeat(__.both().dedup().by(T.id) \ .until(__.has(T.id, "some_vertex2_id")) \ .toList()
Neptune jupyter notebook
%%gremlin edge = g.V().has(id, "some_vertex_id") \ .repeat(__both()).dedup().by(id))\ .until(__.has(id, "some_vertex2_id")) \ .toList()
他の小技
jupyter nootbook上でグラフをいい感じに表示する
%%gremlin -p v,oute, inv g.V().outE().inV().path().by(elementMap())
備考
使うサーバーによってはidがuuidだったりintegerだったりするので注意。
終わりに
案外多かった...。他にもあれば追加します。誰かの役に立てば幸いです。
参考
terraform destroy したときにサブネットがなかなか消えないときは何かリソースが乗ってるかも
結論
タイトルの通りです。
こんにちは
さっちゃん(@toshiakisan1127)です。ソフトウェアエンジニア1年生で、普段はWebアプリケーションの開発でバックエンド(with Python, Spring boot)と、インフラ(with CloudFormation, terraform, kubenetes)をやってます。
現象
先日、開発環境のアーキテクチャを変更するために、サブネット上に乗っているk8sクラスターをdeleteして、インフラ部分を
terraform destroy
しようとすると、あるサブネットだけ待てど暮らせどdestroyされず気づいたらおじいちゃんになってしまいました。(実際は20分くらい待った。)しかしエラーにはならないので、強制終了するとtfstateが壊れそうなので踊るしかやることがなかったです。
調査
まずはマネジメントコンソールから直接サブネットを壊そうとすると下の画像のように、「ネットワークインターフェースが残っている」と言われて消せませんでした。

これはあるEC2についてるみたいなので、(あれ、EKSクラスター落としたはずだけどなぁ...。)とか思いながらマネコン上をうろうろしてました。
この時点で「何か上に残ってるんでは?」と仮説を立ててましたが、サブネットの上に何が乗っているかを探すのってマネジメントコンソール上では辛いです...。なにかいい方法があったりするんでしょうか。今回は各マイクロサービスのterraformやcloudformationを見て回ることで、該当のサブネットを使ってそうなものがないか探して回りました。(手でリソース作ってたらどーすんねん。)
原因
結局、他のマイクロサービスのLambdaがそのサブネットの上に乗っていたので、そのアプリケーションもdestroyすると一瞬でサブネットが消えました。上記の残っていたネットワークインターフェースはLambdaの実行環境のEC2にアタッチされているものみたいです。>
教訓
- サブネットのdestroyは、上にリソースが乗っている場合にエラーではなくTimeoutになる。(本題)
- Lambdaの実行環境ってEC2の上にあるのかぁ。そりゃそうかもしれないが、裏側が見えた気がしてちょっと面白い(雑談)
とある未経験ITエンジニア、一年目が終わる。
これはなに?
IT業界未経験の自分がエンジニアとして一年間でどれだけ成長できたかを書き留めておくだけです。IT業界に未経験で入る方の参考になればと思います。
結論
やったことのない仕事でも報告・連絡・相談をこまめに行いながら調査、実験することである程度の成果物が安定して出せるようになったことが一番大きいと思います。また、2,3人のタスクを割り出し、共有、管理をやることもあります。
自己紹介
こんにちは。猫好きITエンジニャーのさっちゃん(@toshiakisan1127)です。今回は新卒でIT企業に入社した自分が、研修、仕事を通して何ができるようになったかをまとめようと思います。正直まだまだ「ITエンジニアです」と胸を張れるほどやれていないので私生活では「パソコンをぽちぽちする仕事をしています。」と言っていますが、ここではITエンジニアと言っておきます。ソフトウェアエンジニアと言った方が正確かもしれません。現在の技術スタックは最後にまとめておきます。
入社前にやっていたこと
自分は大学と大学院では物理学をやっていたのですが、さまざまな事が重なって大学院を中退してあるIT企業に入社しました。(中退した理由はこちら)大学院の後期は休学していたため時間があったので、少しでもIT系の知識をつけようと思い以下のことを行いました。
- 競技プログラミング(AtCoder)
- progate
- 基本情報技術者試験
- javascriptのお勉強
- Linux 入門
以下捕捉。 1: 最初はプログラミングの勉強に目的が欲しくて5月ごろに始めましたが、ハマってしまって9,10,11月はずっとやっていました。言語はPythonで、このブログにも記事を載せています。レートは730くらいです。 2: いろんな言語に触ってみたくて、月1000円くらいだったので8月中にHTML, CSS初級・中級、javascript, python, Go, Ruby, スクリプトあたりをやって退会しました。 3: 薄く広く知識をつけたかったので基本情報技術者試験の勉強をしました。合格点は取れたんですが、選択問題をチェックするフラグをつけ忘れて取れませんでした...。ただここで身につけた知識は入社後に役立ちました。(というかこれくらいの知識がないとお話にならないことが多くて危なかった...。) 4: javascript本格入門という本を読みながら言語の使用を理解しようとしていました。しかしあまり身に付かなかったように思います。 5: 新しいLinuxの教科書という本でポチポチ練習しました。これもやっておいてよかったです。
他にもdockerだったりAWSでEC2のインスタンスを立ててsshでログインしてみたりと色々やりましたが、入社後ほどは身に付かなかったです。やっぱり最初の一歩は理解している人に教えてもらいながら進めた方が圧倒的に効率がいいように思います。この経験から、実務でも最初はペアプロ・モブプロを積極的に行なっています。
研修編
自分はエンジニアではなく(エンジニアになる前提で)総合職の枠で入ったので、初めはIT未経験の方々と一緒に2.5ヶ月間研修を受けさせてもらえました。バニラなjavascriptで簡単な呟きアプリを作成するところから始まりましたが、自分は早く終わったので、躓いているチームメンバーに困っていることはないか聞きつつTypeScriptとvue.jsの勉強をしていました。研修の後半ではvue, Spring boot, postgresqlを使ってログイン機能がついたモノリスなチャットアプリを作成しました。チーム開発を通してgitの使い方やタスクの管理を学べましたが、同期と仲良くなれたのが一番良かったかなと思います。dockerで環境を作ること、Spring bootの環境構築に苦労したことを覚えています。班の中では自分が割とできるほうだったので、業務中は班の進捗を管理したりゴールを設定しつつ、夜中まで環境構築したりSpring bootの勉強をして班員に教えられるように勉強していました。(時には日付を超えて勉強していて翌日寝坊してました。)全てが新鮮で「楽しいな〜」と言いながらやってました。
配属後
6月中旬に12人程度の新規開発チームに配属されました。業務知識、アジャイル、スクラム、DDD、オニオンアーキテクチャ、マイクロサービスアーキテクチャなど勉強することが多く、またチームメンバーのことも知らないので「いつでも質問してね」と言われても何を質問すればいいか分からなかった最初の1ヶ月は辛い時期でした。今思うと、質問ではなく「不安」を共有するようにすればよかったですし、今は自分が教える時は「何か質問はありますか?」の後で「何か不安に思っていることはありますか?」と聞くようにしています。7月、8月はSpring bootでAPIの設計・実装・自動テストをしていました。
9月から新しいマイクロサービスの立ち上げを手伝いました。ここで初めてAWSを本格的に触るようになりましたが、API Gateway, Lambda, Cognito, Neptuneなど聞いたこともないサービスもOAuth2.0も分からなくて非常に困りました。会議をしても何を言っているかわかりません。AWS のアーキテクチャのレビュー会議でも、それがいいのか悪いのかを判断する基準がなくて、本当に何も分からん!という感じでした。しかし分からん分からん言ってても分かるようにはならないので、業務後や土日を使ってAWS SAAの勉強を始め、11月に取得しました。さらに業務で話に出たサービスを個人でも遊んでみることで理解を深めました。OAuth2.0も趣味で勉強しました。(というかしないと文字通りお話にならない...。)
また、手でLambdaをデプロイするのは厳しいので、9月に初めてIaCの一種としてAWS SAMに手を出しました。SAMについてはこちらを書きました。SAMはCloudformationのラッパーですが、AWSのドキュメントとの睨めっこが厳しかったです...。CI/CDの構築が楽なのはいいところです。
11月後半あたりからterraformも触りましたが、SAMを触ってからterraformを触ると、ドキュメントが整備されていてめちゃくちゃ楽しかったです。
Python についてはLambdaで使っていますし、コルーチンを使ってシングルスレッドで並列処理を行う方法なども勉強しました。競技プログラミングでは使わない技術なので楽しいです。
そんなこんなでAPIの設計・実装・テスト、インフラ周りの設計・構築、アーキテクチャレビューを主に業務として行っていると3月になりました。kubernetesもjobを書いてapplyしたりしましたが、ちゃんと分かってないので近いうちに勉強します。
最近はterraformでgitlab runnerを作って社内記事にしたりして遊んでます。
これから
あっというまに1年が過ぎようとしています。よく成長したなと思う反面、もっとうまくやれたなーという思いです。これからですが、まだまだ勉強したいことがあるのでそれを消化していきます。転職を考えたりもしていますが、自分は今のチームが好きで学べることが多く、何より楽しくやれているのでもう少し先かなーと考えています。(会社は「どちらかというと好き」くらい。)でもいい条件があれば会社を変えることも考えています。
技術スタック
(技術スタック書くの難しい...。)
- フロントエンド: vue.js
- バックエンド: Spring boot, mysql, postgresql, Python
- その他: git, redmine, docker, AWS, terraform, (kubernetes), datadog, DDD, マイクロサービスアーキテクチャ
業務で使ったことがあるAWSのサービス
IAM, EC2, VPC, EKS, Aurora for mysql, API Gateway, Lambda, DynamoDB, Cognito, Neptune, Cloudformation, Cloudwatch logs, x-ray, Route53, S3, KMS, SAM
触ったことがあるAWSのサービス(遊び含む)
ElasticBeanStalk, ECS, Fargate
資格
AWS SAA
他にも思い出したら追記しておきます。
最後に
自分に対する自分の評価としては、すごく良くできるわけではないですが一年目はこんなもんじゃないですか?知らんけど。