CINXE.COM
ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog
<!DOCTYPE html> <html lang="ja" data-admin-domain="//blog.hatena.ne.jp" data-admin-origin="https://blog.hatena.ne.jp" data-author="bengo4" data-avail-langs="ja en" data-blog="bengo4.hatenablog.jp" data-blog-host="bengo4.hatenablog.jp" data-blog-is-public="1" data-blog-name="弁護士ドットコム株式会社 Creators’ blog" data-blog-owner="bengo4" data-blog-show-ads="" data-blog-show-sleeping-ads="" data-blog-uri="https://creators.bengo4.com/" data-blog-uuid="13574176438040458148" data-blogs-uri-base="https://creators.bengo4.com" data-brand="devblog" data-data-layer="{"hatenablog":{"admin":{},"analytics":{"brand_property_id":"","measurement_id":"G-KJB0HLBDNN","non_sampling_property_id":"","property_id":"","separated_property_id":"UA-29716941-25"},"blog":{"blog_id":"13574176438040458148","content_seems_japanese":"true","disable_ads":"custom_domain","enable_ads":"false","enable_keyword_link":"false","entry_show_footer_related_entries":"true","force_pc_view":"true","is_public":"true","is_responsive_view":"true","is_sleeping":"false","lang":"ja","name":"\u5f01\u8b77\u58eb\u30c9\u30c3\u30c8\u30b3\u30e0\u682a\u5f0f\u4f1a\u793e Creators\u2019 blog","owner_name":"bengo4","uri":"https://creators.bengo4.com/"},"brand":"devblog","page_id":"entry","permalink_entry":{"author_name":"a2920201-4a78-4cbb-a105-1e26fdfb","categories":"CLOUDSIGN\tSRE","character_count":7262,"date":"2024-10-22","entry_id":"6802418398297825760","first_category":"CLOUDSIGN","hour":"7","title":"\u30bc\u30ed\u30c0\u30a6\u30f3\u30bf\u30a4\u30e0\u3067 Amazon EC2 \u3067\u7a3c\u50cd\u3057\u3066\u3044\u308b nginx \u3092 AWS Fargate \u306b\u79fb\u884c\u3057\u305f","uri":"https://creators.bengo4.com/entry/2024/10/22/073000"},"pro":"pro","router_type":"blogs"}}" data-device="pc" data-dont-recommend-pro="false" data-global-domain="https://hatena.blog" data-globalheader-color="b" data-globalheader-type="pc" data-has-touch-view="1" data-help-url="https://help.hatenablog.com" data-hide-header="1" data-no-suggest-touch-view="1" data-page="entry" data-parts-domain="https://hatenablog-parts.com" data-plus-available="1" data-pro="true" data-router-type="blogs" data-sentry-dsn="https://03a33e4781a24cf2885099fed222b56d@sentry.io/1195218" data-sentry-environment="production" data-sentry-sample-rate="0.1" data-static-domain="https://cdn.blog.st-hatena.com" data-version="add51ddc2d3c0599e98ea1239ac012" data-initial-state="{}" > <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="robots" content="max-image-preview:large" /> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=7; IE=9; IE=10; IE=11" /> <title>ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog</title> <link rel="canonical" href="https://creators.bengo4.com/entry/2024/10/22/073000"/> <meta itemprop="name" content="ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog"/> <meta itemprop="image" content="https://cdn.image.st-hatena.com/image/scale/5c69b9d99e3c3a35016dc41b6effe644e0fa9c38/backend=imagemagick;height=1300;version=1;width=1300/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241021%2F20241021192009.png"/> <meta property="og:title" content="ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog"/> <meta property="og:type" content="article"/> <meta property="og:url" content="https://creators.bengo4.com/entry/2024/10/22/073000"/> <meta property="og:image" content="https://cdn.image.st-hatena.com/image/scale/5c69b9d99e3c3a35016dc41b6effe644e0fa9c38/backend=imagemagick;height=1300;version=1;width=1300/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241021%2F20241021192009.png"/> <meta property="og:image:alt" content="ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog"/> <meta property="og:description" content="こんにちは。弁護士ドットコム クラウドサイン事業本部で SRE をしています、大内と申します。 クラウドサイン事業本部の SRE ではサービスの可用性、信頼性の向上や開発の高速化、省力化を目指した開発を日々行っています。 クラウドサインは 2024 年 10 月で 10 年目のサービスとなりました。 裏ではさまざまなアプリケーション(定期実行バッチ、常駐バッチ、内部 API サーバーなど)が稼働し、相互に連携してサービスを提供しているのですが、中には非常に古くから稼働しているものも存在します。 今回お話する nginx もその 1 つです。 クラウドサインの裏で稼働するアプリケーションのほとん…" /> <meta property="og:site_name" content="弁護士ドットコム株式会社 Creators’ blog"/> <meta property="article:published_time" content="1729549800" /> <meta property="article:tag" content="CLOUDSIGN" /> <meta property="article:tag" content="SRE" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:image" content="https://cdn.image.st-hatena.com/image/scale/5c69b9d99e3c3a35016dc41b6effe644e0fa9c38/backend=imagemagick;height=1300;version=1;width=1300/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241021%2F20241021192009.png" /> <meta name="twitter:title" content="ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した - 弁護士ドットコム株式会社 Creators’ blog" /> <meta name="twitter:description" content="こんにちは。弁護士ドットコム クラウドサイン事業本部で SRE をしています、大内と申します。 クラウドサイン事業本部の SRE ではサービスの可用性、信頼性の向上や開発の高速化、省力化を目指した開発を日々行っています。 クラウドサインは 2024 年 10 月で 10 年目のサービスとなりました。 裏ではさまざまなア…" /> <meta name="twitter:app:name:iphone" content="はてなブログアプリ" /> <meta name="twitter:app:id:iphone" content="583299321" /> <meta name="twitter:app:url:iphone" content="hatenablog:///open?uri=https%3A%2F%2Fcreators.bengo4.com%2Fentry%2F2024%2F10%2F22%2F073000" /> <meta name="twitter:site" content="@bengo4_creators" /> <meta name="description" content="こんにちは。弁護士ドットコム クラウドサイン事業本部で SRE をしています、大内と申します。 クラウドサイン事業本部の SRE ではサービスの可用性、信頼性の向上や開発の高速化、省力化を目指した開発を日々行っています。 クラウドサインは 2024 年 10 月で 10 年目のサービスとなりました。 裏ではさまざまなアプリケーション(定期実行バッチ、常駐バッチ、内部 API サーバーなど)が稼働し、相互に連携してサービスを提供しているのですが、中には非常に古くから稼働しているものも存在します。 今回お話する nginx もその 1 つです。 クラウドサインの裏で稼働するアプリケーションのほとん…" /> <script id="embed-gtm-data-layer-loader" data-data-layer-page-specific="{"hatenablog":{"blogs_permalink":{"has_related_entries_with_elasticsearch":"true","blog_afc_issued":"false","is_blog_sleeping":"false","entry_afc_issued":"false","is_author_pro":"true"}}}" > (function() { function loadDataLayer(elem, attrName) { if (!elem) { return {}; } var json = elem.getAttribute(attrName); if (!json) { return {}; } return JSON.parse(json); } var globalVariables = loadDataLayer( document.documentElement, 'data-data-layer' ); var pageSpecificVariables = loadDataLayer( document.getElementById('embed-gtm-data-layer-loader'), 'data-data-layer-page-specific' ); var variables = [globalVariables, pageSpecificVariables]; if (!window.dataLayer) { window.dataLayer = []; } for (var i = 0; i < variables.length; i++) { window.dataLayer.push(variables[i]); } })(); </script> <!-- Google Tag Manager --> <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-P4CXTW');</script> <!-- End Google Tag Manager --> <link rel="shortcut icon" href="https://creators.bengo4.com/icon/favicon"> <link rel="apple-touch-icon" href="https://creators.bengo4.com/icon/touch"> <link rel="icon" sizes="192x192" href="https://creators.bengo4.com/icon/link"> <link rel="alternate" type="application/atom+xml" title="Atom" href="https://creators.bengo4.com/feed"/> <link rel="alternate" type="application/rss+xml" title="RSS2.0" href="https://creators.bengo4.com/rss"/> <link rel="alternate" type="application/json+oembed" href="https://hatena.blog/oembed?url=https%3A%2F%2Fcreators.bengo4.com%2Fentry%2F2024%2F10%2F22%2F073000&format=json" title="oEmbed Profile of ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した"/> <link rel="alternate" type="text/xml+oembed" href="https://hatena.blog/oembed?url=https%3A%2F%2Fcreators.bengo4.com%2Fentry%2F2024%2F10%2F22%2F073000&format=xml" title="oEmbed Profile of ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した"/> <link rel="author" href="http://www.hatena.ne.jp/a2920201-4a78-4cbb-a105-1e26fdfb/"> <link rel="preload" href="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241011/20241011212321.png" as="image"/> <link rel="stylesheet" type="text/css" href="https://cdn.blog.st-hatena.com/css/blog.css?version=add51ddc2d3c0599e98ea1239ac012"/> <link rel="stylesheet" type="text/css" href="https://usercss.blog.st-hatena.com/blog_style/13574176438040458148/d55616d8a82dc73f1254718e666a1d73d39c566d"/> <script> </script> <style> div#google_afc_user, div.google-afc-user-container, div.google_afc_image, div.google_afc_blocklink { display: block !important; } </style> <script type="application/ld+json">{"@context":"http://schema.org","@type":"Article","dateModified":"2024-10-22T07:30:17+09:00","datePublished":"2024-10-22T07:30:00+09:00","headline":"ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した","image":["https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241021/20241021192009.png"]}</script> <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-KJB0HLBDNN"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-KJB0HLBDNN'); </script> </head> <body class="page-entry header-image-only enable-bottom-editarea category-CLOUDSIGN category-SRE globalheader-off globalheader-ng-enabled"> <div id="globalheader-container" data-brand="hatenablog" style="display: none" > <iframe id="globalheader" height="37" frameborder="0" allowTransparency="true"></iframe> </div> <div id="container"> <div id="container-inner"> <header id="blog-title" data-brand="hatenablog"> <div id="blog-title-inner" style="background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241011/20241011212321.png'); background-position: center 0px;"> <div id="blog-title-content"> <h1 id="title"><a href="https://creators.bengo4.com/">弁護士ドットコム株式会社 Creators’ blog</a></h1> <h2 id="blog-description">弁護士ドットコムがエンジニア・デザイナーのサービス開発事例やデザイン活動を発信する公式ブログです。</h2> </div> </div> </header> <div id="content" class="hfeed" > <div id="content-inner"> <div id="wrapper"> <div id="main"> <div id="main-inner"> <!-- google_ad_section_start --> <!-- rakuten_ad_target_begin --> <article class="entry hentry test-hentry js-entry-article date-first autopagerize_page_element chars-7600 words-800 mode-markdown entry-odd" id="entry-6802418398297825760" data-keyword-campaign="" data-uuid="6802418398297825760" data-publication-type="entry"> <div class="entry-inner"> <header class="entry-header"> <div class="date entry-date first"> <a href="https://creators.bengo4.com/archive/2024/10/22" rel="nofollow"> <time datetime="2024-10-21T22:30:00Z" title="2024-10-21T22:30:00Z"> <span class="date-year">2024</span><span class="hyphen">-</span><span class="date-month">10</span><span class="hyphen">-</span><span class="date-day">22</span> </time> </a> </div> <h1 class="entry-title"> <a href="https://creators.bengo4.com/entry/2024/10/22/073000" class="entry-title-link bookmark">ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した</a> </h1> <div class="entry-categories categories"> <a href="https://creators.bengo4.com/archive/category/CLOUDSIGN" class="entry-category-link category-CLOUDSIGN">CLOUDSIGN</a> <a href="https://creators.bengo4.com/archive/category/SRE" class="entry-category-link category-SRE">SRE</a> </div> </header> <div class="entry-content hatenablog-entry"> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241021/20241021192009.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>こんにちは。弁護士ドットコム クラウドサイン事業本部で SRE をしています、大内と申します。 クラウドサイン事業本部の SRE ではサービスの可用性、信頼性の向上や開発の高速化、省力化を目指した開発を日々行っています。</p> <p>クラウドサインは 2024 年 10 月で 10 年目のサービスとなりました。 裏ではさまざまなアプリケーション(定期実行バッチ、常駐バッチ、内部 API サーバーなど)が稼働し、相互に連携してサービスを提供しているのですが、中には非常に古くから稼働しているものも存在します。 今回お話する nginx もその 1 つです。</p> <p>クラウドサインの裏で稼働するアプリケーションのほとんどはコンテナで動作しています。 基盤としては <a href="https://aws.amazon.com/jp/fargate/">AWS Fargate</a> を使用しています。 昔は EC2 で稼働していたさまざまなアプリケーションも、サービス提供しながら裏でひっそりと Fargate に移行していきました。 nginx は、そんな Fargate 移行対象の中の最後のアプリケーションでした。</p> <p>今回は、そんな EC2 上で稼働していた nginx をゼロダウンタイムで Fargate に移行したお話をご紹介します。</p> <ul class="table-of-contents"> <li><a href="#背景">背景</a></li> <li><a href="#移行前と移行後">移行前と移行後</a></li> <li><a href="#移行手法">移行手法</a><ul> <li><a href="#TTL-を小さい値にする">TTL を小さい値にする</a></li> <li><a href="#ルーティングポリシーを加重にする">ルーティングポリシーを「加重」にする</a></li> </ul> </li> <li><a href="#デプロイ環境の整備">デプロイ環境の整備</a><ul> <li><a href="#テンプレートエンジンを-gomplate-に変更">テンプレートエンジンを gomplate に変更</a></li> <li><a href="#gomplate-で環境変数にアクセスするときの注意点">gomplate で環境変数にアクセスするときの注意点</a></li> </ul> </li> <li><a href="#移行結果">移行結果</a></li> <li><a href="#まとめ">まとめ</a></li> </ul> <h2 id="背景">背景</h2> <p>EC2 から Fargate に移行したかった理由は大きく 2 つありました。</p> <ol> <li>EC2 の保守に工数がかかる</li> <li>nginx の設定管理に Ansible を使っていて、これの実行が SRE でないとできない</li> </ol> <p>EC2 を使う場合の悩みごとは、パッチ適用です。 nginx しか動かす必要がなくとも、EC2 上にはその他にもさまざまなプロセスが稼働しています。 セキュリティパッチなどを適用する際は nginx 以外のプロセスにも影響が出ていないか確認しなければなりません。</p> <p>また nginx の設定管理をしている Ansible を SRE しか実行できないのも課題でした。 SRE にしかできない作業をなるべく減らし、エンジニアなら誰でも設定変更できる環境にしたかったのです。</p> <p>これらの問題を解決するために、以下 2 つの対応をすることにしました。</p> <ol> <li>nginx を EC2 から Fargate に移行する</li> <li>CI から nginx をデプロイできるようにする</li> </ol> <h2 id="移行前と移行後">移行前と移行後</h2> <p>まず、移行前のインフラ構成はおおむね以下のような構成となっていました。 VPC 内に ALB と EC2 が存在しており、一般ユーザーは ALB 経由で EC2 にアクセスします。 そして、EC2 の保守作業のため、SRE のみ Ansible を使って EC2 のプロビジョニングができるようになっていました。</p> <p><figure class="figure-image figure-image-fotolife" title="移行前のインフラ構成"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073002.png" alt="移行前のインフラ構成" width="423" height="370" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>移行前のインフラ構成</figcaption></figure></p> <p>上記インフラは、移行により以下のような構成となりました。 EC2 は Fargate に変わり、SRE によるプロビジョニングは GitLab CI + CodePipeline + CodeBuild による自動デプロイに変わりました。 GitLab CI からリリース対象のソースコードを S3 にアップロードし、CodePipeline がそれをトリガーにデプロイを開始します。 デプロイ操作はエンジニアと SRE のどちらも可能になっています。 便宜上、下図の「エンジニア」は「SRE 以外のエンジニア」として記載しています。</p> <p><figure class="figure-image figure-image-fotolife" title="移行後のインフラ構成"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073004.png" alt="移行後のインフラ構成" width="800" height="475" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>移行後のインフラ構成</figcaption></figure></p> <h2 id="移行手法">移行手法</h2> <p>前述の Fargate への移行には、Amazon Route 53 の加重ルーティングを使用しました。</p> <p>加重ルーティングは、1 つのドメインに対して複数のリソースを登録し、それぞれのリソースにルーティングするトラフィック量を指定できる機能です<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>。 いわゆる DNS ラウンドロビンをより便利にした機能です。</p> <p>移行では、加重ルーティングを使って nginx とその前段にいる ALB を丸ごと切り替えるようにしました。 Fargate 版 nginx の手前にも ALB を配置し、ALB のドメインを重量調整により少しずつ切り替えていきました。</p> <p>大まかな移行の流れは、以下のとおりです。</p> <ol> <li>旧 nginx 用の ALB(以降、旧 ALB)の Route 53 レコードの TTL を小さい値に変更する</li> <li>旧 ALB のレコードのルーティングポリシーを「シンプル」から「加重」に変更する <ol> <li>このとき、重量を 100 にする</li> </ol> </li> <li>新 nginx 用の ALB(以降、新 ALB)ドメインを Route 53 にルーティングポリシー「加重」で登録する <ol> <li>このとき、重量を 0 にする</li> </ol> </li> <li>新 ALB のレコードの重量を 1 〜 200 に徐々に増やしつつ経過観察する</li> <li>旧 ALB のレコードの重量を 0 に変更して、数週間ほど経過観察する</li> <li>旧 ALB のレコードを削除する</li> <li>旧 nginx のリソースを削除する</li> <li>新 ALB のレコードのルーティングポリシーを「シンプル」に変更する</li> <li>新 ALB の Route 53 レコードの TTL を元の値に戻す</li> </ol> <p>これを図で表現すると次のようになります。</p> <p><figure class="figure-image figure-image-fotolife" title="移行の概念図"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073008.png" alt="移行の概念図" width="691" height="732" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>移行の概念図</figcaption></figure></p> <p>以降はこれらの詳細を説明します。</p> <h3 id="TTL-を小さい値にする">TTL を小さい値にする</h3> <p>DNS レコードの TTL は、レコードのドメインに対応する IP アドレスの問い合わせ結果をキャッシュしてよい時間(秒)を表します。</p> <p>DNS レコードの値を変更したり、複数値登録する際は、TTL を短い値にするのが一般的です。</p> <p>TTL が大きい場合、キャッシュされた問い合わせ結果は長い間保持されます。 つまり「レコードの値を変更したのに、古いキャッシュの値が使用され続けていつまでも新しい値が使用されない」といった事象が起こります。</p> <p>今回の件でも、ALB を切り替えるのに DNS レコードを使用するため、TTL を小さい値に設定しました。</p> <h3 id="ルーティングポリシーを加重にする">ルーティングポリシーを「加重」にする</h3> <p>Route 53 レコードのルーティングポリシーを加重に変更することで、加重ルーティングが利用できます。</p> <p>加重ルーティングを使う場合、同一ドメインで複数レコード登録して使うことになりますが、レコードが 1 つだけでも問題ありません。 トラフィック量を指定するためのパラメータ「重量」には、1 以上の値を設定すれば良いのですが、今回はトラフィック量を細かく調整したかったため 100 にしました。</p> <p>重量によるトラフィック量の計算式は、AWS 公式で公開されています[^1]。 その計算式は「特定のレコードの重量 ÷ すべてのレコードの重量の合計」です。</p> <p>例えば「重量が 1 のレコード old」と「重量が 2 のレコード new」が存在する場合を考えます。 レコード old のトラフィック量は <code>1 / (1 + 2)</code> となるため、3 分の 1 です。 逆にレコード new のトラフィック量は <code>2 / (1 + 2)</code> となるため、3 分の 2 です。</p> <p><figure class="figure-image figure-image-fotolife" title="加重ルーティングの例(1)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073011.png" alt="加重ルーティングの例(1)" width="611" height="402" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>加重ルーティングの例(1)</figcaption></figure></p> <p>このため、少しずつトラフィック量を増やしたい場合は、片方の重量を大きい値にします。</p> <p>今回のケースでは、旧レコードの重量を 100 にし、新レコードの重量を 1 から始めたため、101 分の 1 のトラフィック量から始まります。 新レコードの重量が 100 になると、200 分の 100、つまり 2 分の 1 となるので、新旧レコードのトラフィック量は均一になります。</p> <p>このように徐々に新 ALB へのトラフィックを増やしていき、最終的にはすべてのトラフィックを新 ALB に切り替えます。</p> <p><figure class="figure-image figure-image-fotolife" title="加重ルーティングの例(2)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073014.png" alt="加重ルーティングの例(2)" width="611" height="402" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>加重ルーティングの例(2)</figcaption></figure></p> <p>もし切り替え後に何か問題が見つかった場合は Route 53 レコードの重量を変更して、旧レコードを 100、新レコードを 0 にすればロールバックは完了です。</p> <p>なお実際の移行作業では、監視系から以下を確認しながら作業しました。</p> <ul> <li>監視系のアラート有無</li> <li>ALB のレスポンスコード、レイテンシ</li> <li>nginx のログ、ステータス、レイテンシ</li> <li>nginx の後続アプリケーションにアクセスが到達しているか</li> <li>移行前の nginx と移行後の nginx のトラフィック量の変化</li> </ul> <h2 id="デプロイ環境の整備">デプロイ環境の整備</h2> <p>EC2 から Fargate に切り替えた後のデプロイでは GitLab CI + CodePipeline + CodeBuild を使うようにしました。 GitLab CI では簡単なテストを実行し、テストがパスした場合のみ CD (CodePipeline) を実行するようにしています。</p> <p><figure class="figure-image figure-image-fotolife" title="CICD構成"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20241022/20241022073004.png" alt="CICD構成" width="800" height="475" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>CICD構成</figcaption></figure></p> <p>CI ではそこまで複雑なテストはしていません。以下の 2 つをテストしています。</p> <ol> <li>nginx の Docker イメージのビルドが通ること</li> <li>nginx の設定ファイル検証(<code>nginx -t</code>)が通ること</li> </ol> <p>これで設定不備は最低限検出できるようになりました。 今までは Ansible を使う際に SRE が都度手動でチェックしていたのですが、それらを CI に寄せられました。</p> <h3 id="テンプレートエンジンを-gomplate-に変更">テンプレートエンジンを gomplate に変更</h3> <p>Ansible を使わなくするため、テンプレートエンジンをどうするかが問題となりました。</p> <p>Ansible では、テンプレート機能を使うことで設定ファイルの内容を環境ごとに切り替えることが可能です。 Ansible が使用しているテンプレートエンジンは Jinja2 です<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>。</p> <p>Jinja2 の機能により、例えば環境ごとに値を変更したい場合は以下のように記述できます。</p> <pre class="code j2" data-lang="j2" data-unlink>numprocs={{ numprocs }}</pre> <p>また条件分岐によりコード自体の切り替えも可能です。</p> <pre class="code j2" data-lang="j2" data-unlink>{% if enable_flag %} command=application --flag1 --flag2 --flag3 {% else %} command=application {% endif %}</pre> <p>Jinja2 は Ansible 専用の機能ではなく、独立したテンプレートエンジンであるため、Jinja2 だけインストールして使用できます。 そのため、以下の 2 つの選択肢がありました。</p> <ol> <li>Jinja2 を使って今の設定ファイルを可能なかぎりそのまま引き継ぐ</li> <li>別のテンプレートエンジンに切り替えて、設定ファイルも修正する</li> </ol> <p>私は 2 を選択し、テンプレートエンジンを gomplate <sup id="fnref:3"><a href="#fn:3" rel="footnote">3</a></sup> に変更しました。 gomplate の採用理由は、以下の 3 つです。</p> <ol> <li>gomplate は単一の実行コマンドのため、簡単にインストールして使用できる</li> <li>テンプレート構文は Go 言語標準の HTML テンプレートとほぼ同じため、クラウドサインのエンジニアに馴染み深く扱いやすい</li> <li>開発が活発に行われている</li> </ol> <p>前述のとおり、エンジニアも設定変更しやすくしていきたかったため、クラウドサインのエンジニアが馴染み深い構文でテンプレートを記述できる gomplate は最適でした。 構文も細かい部分で Jinja2 と異なりますが、そこまでコードの修正も難しくありませんでした。</p> <p>例えば、以下のように、環境変数を埋め込むことも、環境変数で条件分岐も可能です。</p> <pre class="code tpl" data-lang="tpl" data-unlink>numprocs={{ .Env.NUMPROCS }} {{ if eq .Env.ENABLE_FLAG "true" -}} command=application --flag1 --flag2 --flag3 {{- else -}} command=application {{- end }}</pre> <p>このテンプレートに対して gomplate を実行すると、次のようになります。 環境変数の埋め込みも、条件分岐も処理されていることが分かります。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ <span class="synStatement">export</span> <span class="synIdentifier">NUMPROCS</span>=<span class="synConstant">2</span> $ <span class="synStatement">export</span> <span class="synIdentifier">ENABLE_FLAG</span>=true $ gomplate <span class="synSpecial">-f</span> sample.ini.tmpl <span class="synIdentifier">numprocs</span>=<span class="synConstant">2</span> <span class="synStatement">command=</span>application <span class="synSpecial">--flag1</span> <span class="synSpecial">--flag2</span> <span class="synSpecial">--flag3</span> </pre> <h3 id="gomplate-で環境変数にアクセスするときの注意点">gomplate で環境変数にアクセスするときの注意点</h3> <p><code>range</code> 内で <code>.Env</code> の環境変数にアクセスしようとするとエラーになります。 以下のような <code>range</code> を使ったテンプレートに対して gomplate を実行してみます。これは成功します。</p> <pre class="code tpl" data-lang="tpl" data-unlink>{{- range $i, $v := coll.Slice "hello" "world" -}} i: {{ $i }}, v: {{ $v }} {{ end -}}</pre> <pre class="code lang-sh" data-lang="sh" data-unlink>⟩ gomplate <span class="synSpecial">-f</span> sample.ini.tmpl i: <span class="synConstant">0</span>, v: hello i: <span class="synConstant">1</span>, v: world </pre> <p>この <code>range</code> 内で、環境変数にアクセスしようとすると、エラーになります。環境変数へのアクセスを追加します。</p> <pre class="code lang-diff" data-lang="diff" data-unlink> {{- range $i, $v := coll.Slice "hello" "world" -}} i: {{ $i }}, v: {{ $v }} <span class="synIdentifier">+app: {{ .Env.APP }}</span> {{ end -}} </pre> <pre class="code lang-sh" data-lang="sh" data-unlink>$ <span class="synStatement">export</span> <span class="synIdentifier">APP</span>=sample $ gomplate <span class="synSpecial">-f</span> sample.ini.tmpl i: <span class="synConstant">0</span>, v: hello app: 11:58:42 ERR <span class="synIdentifier">err</span>=<span class="synStatement">"</span><span class="synConstant">renderTemplate: failed to render template sample.ini.tmpl: template: sample.ini.tmpl:3:12: executing </span><span class="synSpecial">\"</span><span class="synConstant">sample.ini.tmpl</span><span class="synSpecial">\"</span><span class="synConstant"> at <.Env.APP>: can't evaluate field Env in type interface {}</span><span class="synStatement">"</span> </pre> <p>これは、Go のテンプレートエンジンの仕様として、<code>{{ . }}</code> でループ変数などにアクセスできる構文と衝突しているからと思われます。 以下のように <code>{{ . }}</code> を呼び出してみると、<code>$v</code> と同じ値が埋め込まれます。</p> <pre class="code tpl" data-lang="tpl" data-unlink>{{- range $i, $v := coll.Slice "hello" "world" -}} i: {{ $i }}, v: {{ $v }} app: {{ . }} {{ end -}}</pre> <pre class="code lang-sh" data-lang="sh" data-unlink>$ gomplate <span class="synSpecial">-f</span> sample.ini.tmpl i: <span class="synConstant">0</span>, v: hello app: hello i: <span class="synConstant">1</span>, v: world app: world </pre> <p>そのため <code>range</code> 内で <code>.Env.APP</code> にアクセスしようとすると、 前述の例では文字列型に対して Env フィールドにアクセスしようとするためエラーになります。</p> <p>これを回避するには、Env を別の変数に移してアクセスすることで回避できます。 以下のように <code>$env</code> 変数などを定義して、<code>$env</code> 経由で環境変数にアクセスできます。</p> <pre class="code tpl" data-lang="tpl" data-unlink>{{- $env := .Env -}} {{- range $i, $v := coll.Slice "hello" "world" -}} i: {{ $i }}, v: {{ $v }} app: {{ $env.APP }} {{ end -}}</pre> <pre class="code lang-sh" data-lang="sh" data-unlink>$ gomplate <span class="synSpecial">-f</span> sample.ini.tmpl i: <span class="synConstant">0</span>, v: hello app: sample i: <span class="synConstant">1</span>, v: world app: sample </pre> <h2 id="移行結果">移行結果</h2> <p>移行は無事成功しました。 事前に検証環境でリハーサルをして問題なく切り替えられることは確認済みでしたが、それでもやはり本番環境の切り替えはドキドキする作業です。</p> <p>日中帯での切り替え作業でしたが、特に可用性、スループット、レイテンシのいずれも大きな変化はなく、ゼロダウンタイムで移行作業を終えることができました。</p> <p>経過観察の期間の後、旧 nginx, EC2, ALB など諸々のリソースを削除しました。 nginx の設定はすべて Docker で管理するようになり、CI/CD を整備したことで SRE 以外も設定を修正できるようになりました。 当初課題に感じていたすべては解消できました。</p> <h2 id="まとめ">まとめ</h2> <p>内容をまとめると、以下のとおりです。</p> <ul> <li>nginx を EC2 から Fargate に移行する背景を説明した <ul> <li>EC2 の保守に工数がかかる</li> <li>nginx の設定変更が SRE しかできない</li> </ul> </li> <li>移行前後のインフラ構成について説明した</li> <li>移行手法について説明した <ul> <li>Route 53 の加重ルーティングを使って、徐々に重量を調整して切り替えた</li> </ul> </li> <li>nginx の CI/CD を整備した <ul> <li>GitLab CI + CodePipeline + CodeBuild で自動デプロイするようにした</li> <li>設定ファイルのテンプレートエンジンを Jinja2 から gomplate に変更した</li> </ul> </li> <li>移行結果について説明した <ul> <li>可用性、スループット、レイテンシに影響を与えること無く、ゼロダウンタイムで切り替えを完了した</li> </ul> </li> </ul> <p>以上です。</p> <div class="footnotes"> <hr/> <ol> <li id="fn:1"> <a href="https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/routing-policy-weighted.html">Amazon Route 53 - 加重ルーティング</a>, 2024-09-04<a href="#fnref:1" rev="footnote">↩</a></li> <li id="fn:2"> <a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_templating.html">Templating (Jinja2) - Ansible Community Documentation</a>, 2024-09-09<a href="#fnref:2" rev="footnote">↩</a></li> <li id="fn:3"> <a href="https://github.com/hairyhenderson/gomplate">hairyhenderson/gomplate - GitHub</a>, 2024-09-09<a href="#fnref:3" rev="footnote">↩</a></li> </ol> </div> </div> <footer class="entry-footer"> <div class="entry-tags-wrapper"> <div class="entry-tags"> <span class="entry-tag"> <a href="https://d.hatena.ne.jp/keyword/AWS" class="entry-tag-link"> <span class="entry-tag-icon">#</span><span class="entry-tag-label">AWS</span> </a> </span> <span class="entry-tag"> <a href="https://d.hatena.ne.jp/keyword/Fargate" class="entry-tag-link"> <span class="entry-tag-icon">#</span><span class="entry-tag-label">Fargate</span> </a> </span> <span class="entry-tag"> <a href="https://d.hatena.ne.jp/keyword/nginx" class="entry-tag-link"> <span class="entry-tag-icon">#</span><span class="entry-tag-label">nginx</span> </a> </span> <span class="entry-tag"> <a href="https://d.hatena.ne.jp/keyword/route53" class="entry-tag-link"> <span class="entry-tag-icon">#</span><span class="entry-tag-label">route53</span> </a> </span> <span class="entry-tag"> <a href="https://d.hatena.ne.jp/keyword/Codepipeline" class="entry-tag-link"> <span class="entry-tag-icon">#</span><span class="entry-tag-label">Codepipeline</span> </a> </span> </div> </div> <p class="entry-footer-section track-inview-by-gtm" data-gtm-track-json="{"area": "finish_reading"}"> <span class="author vcard"><span class="fn" data-load-nickname="1" data-user-name="a2920201-4a78-4cbb-a105-1e26fdfb" >a2920201-4a78-4cbb-a105-1e26fdfb</span></span> <span class="entry-footer-time"><a href="https://creators.bengo4.com/entry/2024/10/22/073000"><time data-relative datetime="2024-10-21T22:30:00Z" title="2024-10-21T22:30:00Z" class="updated">2024-10-22 07:30</time></a></span> </p> <div class="hatena-star-container" data-hatena-star-container data-hatena-star-url="https://creators.bengo4.com/entry/2024/10/22/073000" data-hatena-star-title="ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した" data-hatena-star-variant="profile-icon" data-hatena-star-profile-url-template="https://blog.hatena.ne.jp/{username}/" ></div> <div class="social-buttons"> <div class="social-button-item"> <a href="https://b.hatena.ne.jp/entry/s/creators.bengo4.com/entry/2024/10/22/073000" class="hatena-bookmark-button" data-hatena-bookmark-url="https://creators.bengo4.com/entry/2024/10/22/073000" data-hatena-bookmark-layout="vertical-balloon" data-hatena-bookmark-lang="ja" title="この記事をはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/button-only.gif" alt="この記事をはてなブックマークに追加" width="20" height="20" style="border: none;" /></a> </div> <div class="social-button-item"> <div class="fb-share-button" data-layout="box_count" data-href="https://creators.bengo4.com/entry/2024/10/22/073000"></div> </div> <div class="social-button-item"> <a class="entry-share-button entry-share-button-twitter test-share-button-twitter" href="https://x.com/intent/tweet?hashtags=AWS&hashtags=Fargate&hashtags=nginx&hashtags=route53&hashtags=Codepipeline&text=%E3%82%BC%E3%83%AD%E3%83%80%E3%82%A6%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%A7+Amazon+EC2+%E3%81%A7%E7%A8%BC%E5%83%8D%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B+nginx+%E3%82%92+AWS+Fargate+%E3%81%AB%E7%A7%BB%E8%A1%8C%E3%81%97%E3%81%9F+-+%E5%BC%81%E8%AD%B7%E5%A3%AB%E3%83%89%E3%83%83%E3%83%88%E3%82%B3%E3%83%A0%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE+Creators%E2%80%99+blog&url=https%3A%2F%2Fcreators.bengo4.com%2Fentry%2F2024%2F10%2F22%2F073000" title="X(Twitter)で投稿する" ></a> </div> </div> <div class="customized-footer"> <div class="entry-footer-modules" id="entry-footer-secondary-modules"> <div class="hatena-module hatena-module-related-entries" > <!-- Hatena-Epic-has-related-entries-with-elasticsearch:true --> <div class="hatena-module-title"> 関連記事 </div> <div class="hatena-module-body"> <ul class="related-entries hatena-urllist urllist-with-thumbnails"> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://creators.bengo4.com/entry/2024/10/09/073000"> <img alt="AWS COST CUT FIGHT 回答を作ってみました" src="https://cdn.image.st-hatena.com/image/square/511df004a6ecc99e66c03522a77e4b7cfb05073d/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241008%2F20241008230505.png" class="urllist-image related-entries-image" title="AWS COST CUT FIGHT 回答を作ってみました" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://creators.bengo4.com/archive/2024/10/09" rel="nofollow"> <time datetime="2024-10-08T22:30:00Z" title="2024年10月9日"> 2024-10-09 </time> </a> </div> <a href="https://creators.bengo4.com/entry/2024/10/09/073000" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">AWS COST CUT FIGHT 回答を作ってみました</a> <div class="urllist-entry-body related-entries-entry-body">概要 10月5日(土)に開催されたYAPC::Hakodate 2024 で「AWS COS…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://creators.bengo4.com/entry/2023/11/01/083000"> <img alt="税理士ドットコム流のCI/CDを設計する考え方と実践" src="https://cdn.image.st-hatena.com/image/square/d7e4960a5b7df935ef264d9267a8561b95e189ca/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20231030%2F20231030120247.png" class="urllist-image related-entries-image" title="税理士ドットコム流のCI/CDを設計する考え方と実践" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://creators.bengo4.com/archive/2023/11/01" rel="nofollow"> <time datetime="2023-10-31T23:30:00Z" title="2023年11月1日"> 2023-11-01 </time> </a> </div> <a href="https://creators.bengo4.com/entry/2023/11/01/083000" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">税理士ドットコム流のCI/CDを設計する考え方と実践</a> <div class="urllist-entry-body related-entries-entry-body">今年の頭から税理士ドットコム事業部に異動した @komtaki です…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://creators.bengo4.com/entry/2023/04/10/083000"> <img alt="モノリシックなアプリケーションを疎結合にする取り組み" src="https://cdn.image.st-hatena.com/image/square/80e11a90abaadd59febc54d3e687f913abbd705a/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fo%2Foden77%2F20230406%2F20230406124639.png" class="urllist-image related-entries-image" title="モノリシックなアプリケーションを疎結合にする取り組み" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://creators.bengo4.com/archive/2023/04/10" rel="nofollow"> <time datetime="2023-04-09T23:30:00Z" title="2023年4月10日"> 2023-04-10 </time> </a> </div> <a href="https://creators.bengo4.com/entry/2023/04/10/083000" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">モノリシックなアプリケーションを疎結合にする取り組み</a> <div class="urllist-entry-body related-entries-entry-body">こんにちは。弁護士ドットコム クラウドサイン事業本部 Product…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://creators.bengo4.com/entry/postcss-preset-env"> <img alt="postcss-preset-envで少し未来のCSSを予習する" src="https://cdn.image.st-hatena.com/image/square/03161d83485bdd8eeaef5527d651de940eaa3b0b/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fs%2Fshellme%2F20220330%2F20220330093753.png" class="urllist-image related-entries-image" title="postcss-preset-envで少し未来のCSSを予習する" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://creators.bengo4.com/archive/2022/03/31" rel="nofollow"> <time datetime="2022-03-30T23:30:00Z" title="2022年3月31日"> 2022-03-31 </time> </a> </div> <a href="https://creators.bengo4.com/entry/postcss-preset-env" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">postcss-preset-envで少し未来のCSSを予習する</a> <div class="urllist-entry-body related-entries-entry-body">こんにちは、弁護士ドットコムでデザイナーをしているhosogaiで…</div> </div> </li> </ul> </div> </div> </div> </div> </footer> </div> </article> <!-- rakuten_ad_target_end --> <!-- google_ad_section_end --> <div class="pager pager-permalink permalink"> <span class="pager-prev"> <a href="https://creators.bengo4.com/entry/2024/11/05/080000" rel="prev"> <span class="pager-arrow">« </span> イベント開催レポート:「出張!俺の電子… </a> </span> <span class="pager-next"> <a href="https://creators.bengo4.com/entry/2024/10/19/060000" rel="next"> 弁護士ドットコムはVue Fes Japan 2024に… <span class="pager-arrow"> »</span> </a> </span> </div> </div> </div> <aside id="box1"> <div id="box1-inner"> </div> </aside> </div><!-- #wrapper --> <aside id="box2"> <div id="box2-inner"> <div class="hatena-module hatena-module-profile"> <div class="hatena-module-title"> プロフィール </div> <div class="hatena-module-body"> <a href="https://creators.bengo4.com/about" class="profile-icon-link"> <img src="https://cdn.profile-image.st-hatena.com/users/bengo4/profile.png?1695814907" alt="id:bengo4" class="profile-icon" /> </a> <span class="id"> <a href="https://creators.bengo4.com/about" class="hatena-id-link"><span data-load-nickname="1" data-user-name="bengo4">id:bengo4</span></a> <a href="https://blog.hatena.ne.jp/guide/corporation" title="はてなブログ for DevBlog"><i class="badge-type-devblog">はてなブログ for DevBlog</i></a> </span> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> <div class="profile-about"> <a href="https://creators.bengo4.com/about">このブログについて</a> </div> </div> </div> <div class="hatena-module hatena-module-search-box"> <div class="hatena-module-title"> 検索 </div> <div class="hatena-module-body"> <form class="search-form" role="search" action="https://creators.bengo4.com/search" method="get"> <input type="text" name="q" class="search-module-input" value="" placeholder="記事を検索" required> <input type="submit" value="検索" class="search-module-button" /> </form> </div> </div> <div class="hatena-module hatena-module-html"> <div class="hatena-module-title">採用情報</div> <div class="hatena-module-body"> <div class="b4-module-banner"> <a class="b4-module-banner-link" href="https://hrmos.co/pages/bengo4/jobs/0009602?utm_source=referral&utm_medium=creatersBlog&utm_campaign=sideBanner" target="_blank"> <img class="b4-module-banner-image--outline" src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20220131/20220131211957.jpg" alt="採用情報"> </a> </div> </div> </div> <div class="hatena-module hatena-module-recent-entries "> <div class="hatena-module-title"> <a href="https://creators.bengo4.com/archive"> 最新記事 </a> </div> <div class="hatena-module-body"> <ul class="recent-entries hatena-urllist "> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <div class="b4-module-post"> <a class="b4-module-post-link" href="https://creators.bengo4.com/entry/2024/11/05/080000"> <div class="b4-module-post-head"> <img class="b4-module-post-image" src="https://cdn.image.st-hatena.com/image/square/337345157192e1ddef992a9df627c402ccb389d5/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241105%2F20241105080022.png"> </div> <div class="b4-module-post-body"> <span class="b4-module-post-date">2024-11-05</span> <span class="b4-module-post-title">イベント開催レポート:「出張!俺の電子契約」〜クラウドサインをアクセシビリティチェック〜</span> </div> </a> </div> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <div class="b4-module-post"> <a class="b4-module-post-link" href="https://creators.bengo4.com/entry/2024/10/22/073000"> <div class="b4-module-post-head"> <img class="b4-module-post-image" src="https://cdn.image.st-hatena.com/image/square/c1dfc03847a3f2cce3cb951118c7127ebc14768f/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241021%2F20241021192009.png"> </div> <div class="b4-module-post-body"> <span class="b4-module-post-date">2024-10-22</span> <span class="b4-module-post-title">ゼロダウンタイムで Amazon EC2 で稼働している nginx を AWS Fargate に移行した</span> </div> </a> </div> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <div class="b4-module-post"> <a class="b4-module-post-link" href="https://creators.bengo4.com/entry/2024/10/19/060000"> <div class="b4-module-post-head"> <img class="b4-module-post-image" src="https://cdn.image.st-hatena.com/image/square/78550c9140689b604ecd1443b7fbafed9a699798/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241019%2F20241019060008.png"> </div> <div class="b4-module-post-body"> <span class="b4-module-post-date">2024-10-19</span> <span class="b4-module-post-title">弁護士ドットコムはVue Fes Japan 2024にスポンサー&登壇します!</span> </div> </a> </div> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <div class="b4-module-post"> <a class="b4-module-post-link" href="https://creators.bengo4.com/entry/2024/10/10/083000"> <div class="b4-module-post-head"> <img class="b4-module-post-image" src="https://cdn.image.st-hatena.com/image/square/45c91a5a2433d1e863405ce9eeb9a5c0803289c0/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241010%2F20241010084002.png"> </div> <div class="b4-module-post-body"> <span class="b4-module-post-date">2024-10-10</span> <span class="b4-module-post-title">MockServiceWorker(MSW) を使った高速開発のための運用事例</span> </div> </a> </div> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <div class="b4-module-post"> <a class="b4-module-post-link" href="https://creators.bengo4.com/entry/2024/10/09/073000"> <div class="b4-module-post-head"> <img class="b4-module-post-image" src="https://cdn.image.st-hatena.com/image/square/511df004a6ecc99e66c03522a77e4b7cfb05073d/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fb%2Fbengo4%2F20241008%2F20241008230505.png"> </div> <div class="b4-module-post-body"> <span class="b4-module-post-date">2024-10-09</span> <span class="b4-module-post-title">AWS COST CUT FIGHT 回答を作ってみました</span> </div> </a> </div> </div> </li> </ul> </div> </div> <div class="hatena-module hatena-module-archive" data-archive-type="default" data-archive-url="https://creators.bengo4.com/archive"> <div class="hatena-module-title"> <a href="https://creators.bengo4.com/archive">月別アーカイブ</a> </div> <div class="hatena-module-body"> <ul class="hatena-urllist"> <li class="archive-module-year archive-module-year-hidden" data-year="2024"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://creators.bengo4.com/archive/2024" class="archive-module-year-title archive-module-year-2024"> 2024 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/11" class="archive-module-month-title archive-module-month-2024-11"> 2024 / 11 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/10" class="archive-module-month-title archive-module-month-2024-10"> 2024 / 10 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/09" class="archive-module-month-title archive-module-month-2024-9"> 2024 / 9 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/08" class="archive-module-month-title archive-module-month-2024-8"> 2024 / 8 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/07" class="archive-module-month-title archive-module-month-2024-7"> 2024 / 7 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/05" class="archive-module-month-title archive-module-month-2024-5"> 2024 / 5 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/04" class="archive-module-month-title archive-module-month-2024-4"> 2024 / 4 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/03" class="archive-module-month-title archive-module-month-2024-3"> 2024 / 3 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/02" class="archive-module-month-title archive-module-month-2024-2"> 2024 / 2 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2024/01" class="archive-module-month-title archive-module-month-2024-1"> 2024 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2023"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://creators.bengo4.com/archive/2023" class="archive-module-year-title archive-module-year-2023"> 2023 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/12" class="archive-module-month-title archive-module-month-2023-12"> 2023 / 12 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/11" class="archive-module-month-title archive-module-month-2023-11"> 2023 / 11 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/10" class="archive-module-month-title archive-module-month-2023-10"> 2023 / 10 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/09" class="archive-module-month-title archive-module-month-2023-9"> 2023 / 9 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/08" class="archive-module-month-title archive-module-month-2023-8"> 2023 / 8 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/07" class="archive-module-month-title archive-module-month-2023-7"> 2023 / 7 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/06" class="archive-module-month-title archive-module-month-2023-6"> 2023 / 6 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/04" class="archive-module-month-title archive-module-month-2023-4"> 2023 / 4 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/03" class="archive-module-month-title archive-module-month-2023-3"> 2023 / 3 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/02" class="archive-module-month-title archive-module-month-2023-2"> 2023 / 2 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2023/01" class="archive-module-month-title archive-module-month-2023-1"> 2023 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2022"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://creators.bengo4.com/archive/2022" class="archive-module-year-title archive-module-year-2022"> 2022 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/12" class="archive-module-month-title archive-module-month-2022-12"> 2022 / 12 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/11" class="archive-module-month-title archive-module-month-2022-11"> 2022 / 11 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/10" class="archive-module-month-title archive-module-month-2022-10"> 2022 / 10 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/09" class="archive-module-month-title archive-module-month-2022-9"> 2022 / 9 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/08" class="archive-module-month-title archive-module-month-2022-8"> 2022 / 8 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/07" class="archive-module-month-title archive-module-month-2022-7"> 2022 / 7 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/06" class="archive-module-month-title archive-module-month-2022-6"> 2022 / 6 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/05" class="archive-module-month-title archive-module-month-2022-5"> 2022 / 5 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/04" class="archive-module-month-title archive-module-month-2022-4"> 2022 / 4 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/03" class="archive-module-month-title archive-module-month-2022-3"> 2022 / 3 </a> </li> <li class="archive-module-month"> <a href="https://creators.bengo4.com/archive/2022/02" class="archive-module-month-title archive-module-month-2022-2"> 2022 / 2 </a> </li> </ul> </li> </ul> </div> </div> <div class="hatena-module hatena-module-category"> <div class="hatena-module-title"> カテゴリー </div> <div class="hatena-module-body"> <ul class="hatena-urllist"> <li> <a href="https://creators.bengo4.com/archive/category/CLOUDSIGN" class="category-CLOUDSIGN"> CLOUDSIGN (38) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B7%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3" class="category-アクセシビリティ"> アクセシビリティ (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%BB%E3%82%AB%E3%83%B3%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9" class="category-イベント・カンファレンス"> イベント・カンファレンス (19) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/SRE" class="category-SRE"> SRE (5) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89" class="category-フロントエンド"> フロントエンド (9) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%83%AA%E3%83%BC%E3%82%AC%E3%83%AB%E3%83%96%E3%83%AC%E3%82%A4%E3%83%B3%E9%96%8B%E7%99%BA" class="category-リーガルブレイン開発"> リーガルブレイン開発 (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E7%A8%8E%E7%90%86%E5%A3%AB%E3%83%89%E3%83%83%E3%83%88%E3%82%B3%E3%83%A0" class="category-税理士ドットコム"> 税理士ドットコム (3) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E5%BC%81%E8%AD%B7%E5%A3%AB%E3%83%89%E3%83%83%E3%83%88%E3%82%B3%E3%83%A0" class="category-弁護士ドットコム"> 弁護士ドットコム (23) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E5%BC%81%E3%82%B3%E3%83%A0%E3%81%AA%E4%BA%BA%E3%80%85" class="category-弁コムな人々"> 弁コムな人々 (15) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/QA" class="category-QA"> QA (2) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/CRE" class="category-CRE"> CRE (2) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0" class="category-データエンジニアリング"> データエンジニアリング (2) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E9%83%A8%E6%B4%BB%E5%8B%95" class="category-部活動"> 部活動 (2) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E6%83%85%E5%A0%B1%E3%82%AC%E3%83%90%E3%83%8A%E3%83%B3%E3%82%B9%E6%9C%AC%E9%83%A8" class="category-情報ガバナンス本部"> 情報ガバナンス本部 (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E4%BA%BA%E4%BA%8B%E6%9C%AC%E9%83%A8" class="category-人事本部"> 人事本部 (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3" class="category-セキュリティ"> セキュリティ (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E3%82%A2%E3%83%89%E3%83%99%E3%83%B3%E3%83%88%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC" class="category-アドベントカレンダー"> アドベントカレンダー (25) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/BUSINESS%20LAWYERS" class="category-BUSINESS-LAWYERS"> BUSINESS LAWYERS (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/Professional%20Tech%20Lab" class="category-Professional-Tech-Lab"> Professional Tech Lab (1) </a> </li> <li> <a href="https://creators.bengo4.com/archive/category/%E6%96%B0%E5%8D%92%E6%8E%A1%E7%94%A8" class="category-新卒採用"> 新卒採用 (1) </a> </li> </ul> </div> </div> <div class="hatena-module hatena-module-html"> <div class="hatena-module-title">リンク</div> <div class="hatena-module-body"> <div class="b4-module-banner"> <p class="b4-module-banner-title">クラウドサイン note ~Inside~</p> <a href="https://note.com/cloudsign/m/md653f118eed4" class="b4-module-banner-link"> <img class="b4-module-banner-image" src="https://cdn-ak.f.st-hatena.com/images/fotolife/b/bengo4/20220407/20220407152735.jpg" alt="20220407152735"> </a> </div> </div> </div> </div> </aside> </div> </div> <div id="bottom-editarea"> <div class="b4-module-footer"> <p class="b4-module-footer-copyright">© Bengo4.com, Inc. 2005</p> </div> </div> </div> </div> <script async src="https://s.hatena.ne.jp/js/widget/star.js"></script> <script> if (typeof window.Hatena === 'undefined') { window.Hatena = {}; } if (!Hatena.hasOwnProperty('Star')) { Hatena.Star = { VERSION: 2, }; } </script> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&appId=719729204785177&version=v17.0"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> <div class="quote-box"> <div class="tooltip-quote tooltip-quote-stock"> <i class="blogicon-quote" title="引用をストック"></i> </div> <div class="tooltip-quote tooltip-quote-tweet js-tooltip-quote-tweet"> <a class="js-tweet-quote" target="_blank" data-track-name="quote-tweet" data-track-once> <img src="https://cdn.blog.st-hatena.com/images/admin/quote/quote-x-icon.svg?version=add51ddc2d3c0599e98ea1239ac012" title="引用して投稿する" > </a> </div> </div> <div class="quote-stock-panel" id="quote-stock-message-box" style="position: absolute; z-index: 3000"> <div class="message-box" id="quote-stock-succeeded-message" style="display: none"> <p>引用をストックしました</p> <button class="btn btn-primary" id="quote-stock-show-editor-button" data-track-name="curation-quote-edit-button">ストック一覧を見る</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="message-box" id="quote-login-required-message" style="display: none"> <p>引用するにはまずログインしてください</p> <button class="btn btn-primary" id="quote-login-button">ログイン</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="quote-stock-failed-message" style="display: none"> <p>引用をストックできませんでした。再度お試しください</p> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="unstockable-quote-message-box" style="display: none; position: absolute; z-index: 3000;"> <p>限定公開記事のため引用できません。</p> </div> </div> <script type="x-underscore-template" id="js-requote-button-template"> <div class="requote-button js-requote-button"> <button class="requote-button-btn tipsy-top" title="引用する"><i class="blogicon-quote"></i></button> </div> </script> <div id="hidden-subscribe-button" style="display: none;"> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> </div> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <script src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/external/jquery.min.js?v=1.12.4&version=add51ddc2d3c0599e98ea1239ac012"></script> <script src="https://cdn.blog.st-hatena.com/js/texts-ja.js?version=add51ddc2d3c0599e98ea1239ac012"></script> <script id="vendors-js" data-env="production" src="https://cdn.blog.st-hatena.com/js/vendors.js?version=add51ddc2d3c0599e98ea1239ac012" crossorigin="anonymous"></script> <script id="hatenablog-js" data-env="production" src="https://cdn.blog.st-hatena.com/js/hatenablog.js?version=add51ddc2d3c0599e98ea1239ac012" crossorigin="anonymous" data-page-id="entry"></script> <script>Hatena.Diary.GlobalHeader.init()</script> </body> </html>