質問やフィードバックがありましたら、フォームからお願いします
本文は台湾華語で、ChatGPT で翻訳している記事なので、不確かな部分や間違いがあるかもしれません。ご了承ください
動機
このプロジェクトの開発において、過去の純静的な Landing Page に対し、企画側から API を呼び出してデータを動的に更新する必要が出てきました。さらに、ページのインタラクションが増えてきたため、元々使用していた pug+webpack+jQuery の純静的ページはもはや適していませんでした。そこで、新バージョンの開発では next.js を導入することにしました。
まず、元々の Landing Page の構成について説明します。CI を実行すると、静的な HTML ページがパッケージ化され、CSS、JavaScript、画像が CDN にアップロードされます。HTML は直接サーバーに置き、nginx をリバースプロキシとして前面に配置していました。
え?せっかく CDN にアップロードしているのに、HTML はサーバーに置くの?
これは歴史的な要因に関係しています。まず、会社で使用しているのは自社構築の CDN で、サポートできるカスタムドメインが限られています。したがって、HTML を CDN にアップロードするとドメイン名が変更されてしまうため、企画側にとっては好ましくありません。
さらに、フロントエンドエンジニアが制作した Landing Page 以外にも、企画側の活動でテンプレート Landing Page ジェネレーターを使用してページを生成することがあり、このドメインも固定のため変更できません。そのため、両者(テンプレート Landing Page と開発側で制作した Landing Page)を兼ねるために、nginx をプロキシとして配置することは相対的にバランスの取れたアプローチです。
問題が発生しました。next.js は元々の開発構成と互換性がなく、すべての Landing Page を React に書き直すのは現実的ではありませんでした。そこで、next.js 開発用に新しいサーバーを追加し、nginx を通じて同じドメインを保証することに決めました。
問題点
本文に入る前に、元々の静的サーバーが抱えていた問題と、新たにサーバーを追加することで改善される点を述べます。
1. SEO
最大の考慮事項は SEO です。元々の静的サーバーでも SEO は実現可能ですが、最初に述べたように、API を呼び出してデータを取得する必要がある場合や、ブログ記事のようなニーズがある場合、データはビルド時に生成されるため、API を呼ぶにはフロントエンドの JavaScript で ajax や fetch を使用する必要があります。こういった場合、データは効果的に SEO を実現できません。もう一つの考慮点は CORS の問題です。API サーバーと Landing Page のドメインが異なるため、必ず CORS の問題が発生します。
2. パフォーマンス
Landing Page で使用するデータ量はそこまで大きくありませんが、SSR を実現できることでパフォーマンスが向上します。
3. サーバーの利点
node.js をバックエンドサーバーとして使用することで、将来的には企画側のニーズに対しても柔軟に対応できます。next.js 自体はさまざまなビルド方法をサポートしており、例えば getStaticProps
は静的ファイルにパッケージ化され、getServerSideProps
が SSR を使用します。node.js をバックエンドとして持つことで、キャッシュやデータベースアクセスなどのニーズも実装しやすくなります。
以上の理由から、新しいサーバーを構築することを決定しました。しかし、次に待ち受けていたのは厄介な問題の始まりでした。
奮闘歴
SRE との調整
サーバーを構築するためには、まず SRE と調整し、問題を説明する必要があります。おそらく SRE 特有の慎重な性格から、彼らは多くの懸念を持ち、他のプロジェクトにも責任を負っているため、コミュニケーションには多くの時間がかかりました。しかし、動機は明確で、事前に構成を整理していたため、すぐに許可を得ることができました。
私たちのクラウドはプライベートクラウドで、アルファ環境では各開発者が自分でマシンを構築できるため、アルファの設定はすぐに完了しました。しかし、ベータと正式環境は相対的に複雑な手続きが必要で、ACL を申請し、SRE を通じて設置する必要があります。マシンの構築は簡単ですが、さまざまなパッケージのインストールは面倒でした。監視ツール、node.js、nginx などのインストールについては、SRE の協力があったおかげでスムーズに進みました。
歴史的痕跡のある nginx 設定ファイル
サーバーの構築は簡単でしたが、私を困らせたのは歴史のある nginx 設定ファイルです。そこにはさまざまなソースからのページリダイレクト、メンテナンスモードの処理方法、特定のパスでの特別な処理が含まれていました。
Ansible Playbook を通じたデプロイ
SRE が環境インストールの問題を手伝ってくれましたが、次はデプロイです。この部分は自分でクリアにする必要があります。会社では主に ansible playbook と awx を使用してデプロイを行っており(AWX は API を通じて playbook を実行するための GUI)、幸運にも他のプロジェクトで同僚と協力していくつかの playbook の経験があったため、大きな YAML を見ても混乱することはありませんでした。
Docker
利便性のために、最近のデプロイ方法は、1 台のマシンに Docker をインストールし、その中で systemctl サービスを実行して Docker イメージを動かすというものです。
パッケージ化は簡単だと思っていましたが、実装後にさまざまな問題が発生しました。これは私たちのプロジェクトの歴史にも関わりがあります。当初は lerna と mono repo 構成を使用しており、各サブフォルダーがルートファイルを参照しているため、特定のサブフォルダーのみをパッケージ化するのが非常に難しかったのです。
lerna には主に次の 2 つの特徴があります:
- パッケージのインストール時に、他のプロジェクトで使用されているパッケージがルートディレクトリの
node_modules
にホイストされます。 - サブプロジェクト内で
import a from 'sub-project'
を使用して、他のサブプロジェクトの関数を参照できます(背後の原理はnode_modules
にシンボリックリンクを追加することです)。
すべてをパッケージ化した結果、イメージのサイズは 1GB に達し、node_modules
の恐ろしさを再認識しました。そして、symbolic link
の問題もあり、デバッグに長時間かかりました。シンボリックリンクを作成しなければ、npm はネットワーク上で sub-project
というパッケージを探そうとしますが、見つからないことは明白です。
Jenkins 統合
会社では Jenkins を使用して統合を行っており、Docker イメージも Jenkins でパッケージ化され、社内の Docker hub にアップロードされます。後に、Jenkins でパッケージ化中に時折不明なエラーが発生することが判明しましたが、数回再実行すると正常に戻ることが分かりました。
リスクアセスメント
新しいサーバーを構築したので、プロセス上でリスクアセスメントを実行する必要があります。
セキュリティチェック
大きなプロジェクトごとにセキュリティチェックが行われますが、新しいサーバーの構築でもセキュリティチームがセキュリティ上の問題をチェックし、必要な修正を行います。
スケジュールの誤評価
next.js の導入は私たちのチームから発信されたものではなく、他のオフィスの同僚が他のプロジェクトで導入したものです。私はサーバーがすでに構築されていると思い、Landing Page の開発時にそのまま利用すれば良いと考えていました。しかし、実際には next.js の SSG 機能だけを使用し、生成された静的ファイルを元々の Landing Page サーバーに置くだけでした。
当時は少し驚きましたが、QA テストまではまだ 1 ヶ月以上あり、単に next.js のサーバー機能を追加するだけなので、十分余裕があると思っていました。しかし、すでにいくつかの機能が QA テストを開始しており、Landing Page 自体の開発も始まっていなかったため、ページ自体は 1 週間で完成しましたが、QA 処理や他部門とのコミュニケーション、構成の設計、上記の全体のプロセスを経るうちに、知らぬ間に 1 ヶ月が経過してしまいました。また、新しいプロジェクトも評価期に入っていたため、ローンチ日が迫っており、忙しい中で誰も助けてくれる人がいませんでした。
この期間、私のストレスは非常に重く、休日に残業することもあり、もうすぐ限界に達しそうでした。
考察
このような予期しないニーズには、準備にもっと時間を掛ける必要があります。実際、SEO 改善のような目的は、正直あまり魅力的ではなく、成果を得ることも難しいでしょう。ああ、年齢を重ねるにつれ、自分の働き方を見直す必要があると感じています。
1. 私がボトルネックになった
今回のサーバー構築では、フロントエンド以外の知識が必要なことが多く、会社の内部ツールに精通していないと実現できないことが多かったです。多くのことは私がドキュメントを読みながら一歩一歩探り出したもので、フロントエンドとの関連性が低く、チームメンバーに手伝ってもらうのが難しかったです。自分でもより良い方法がないか考えましたが、こういうことは一度放棄すると、提案が放置されてしまい、誰も手を差し伸べてくれません。でも、これは良い現象ではありません。
2. サーバー構築の複雑さを過小評価していた
サーバー構築が難しいというよりも、部門間のコミュニケーションやその後の準備作業が、開発サイクルを避けられないほど長くしてしまったと言えます。正直なところ、一度も経験しないと本当のことは分からないものです。次回は、今回の経験をできるだけドキュメントにまとめ、今後の道をもっと楽にできるようにしたいです。
3. 部門の大半が日本人
私たちのチームを除いて、他の部門の大半は日本人です。そのため、日本語能力が不足しているとコミュニケーションがうまくいかないことが多く、時には私がコミュニケーションの架け橋になったり、多くの事に参加しなければならなくなることもあります。これもあまり良いことではありません。どうすれば良いか考えています。
4. 事前準備が不十分
サーバーがないという事実に気づかなかったのは、十分な事前調査が行われなかったからです。この点は反省が必要です。今後、同様のサーバー構築のニーズがある場合は、考慮に入れて予防策を講じるために、できるだけ多くの時間を確保することが大切です。
5. 複数のスケジュールが重なった
サーバー構築の他にも、当時は多くのスケジュールが重なっていて、既存の QA 修正、Landing Page 開発、コードレビュー、SRE とのコミュニケーション、要件変更の議論などがあり、コンテキストスイッチが続く中でサーバー構築に集中できる時間が圧縮されていました。
この記事が役に立ったと思ったら、下のリンクからコーヒーを奢ってくれると嬉しいです ☕ 私の普通の一日が輝かしいものになります ✨
☕Buy me a coffee