スタイル・エッジ技術ブログ

士業集客支援/コンサルティングのスタイル・エッジのエンジニアによるブログです。

React で QR コードを生成して、タイトルを QR コードの下に描画したうえで画像ファイルとしてダウンロードしてみたい!!

はじめに

こんにちは、しおです。少し前までひどく暑かったのに、もうすっかり年の瀬ですね。

冬っぽい画像を生成してみたので、ぜひ癒されてください。

癒されましたね。

さて、いま私が開発で携わっているプロダクトにて、『QR コード*1をアプリケーション内で生成して画像として表示&ダウンロードしたい。その際に、任意のタイトルを画像に埋め込みたい。』という要件が発生しました。

そこで、今回はその対応で実際に使用した qrcode.react という React 向けのライブラリを用いて React アプリケーション内で QR コードとタイトルを描画したうえで画像ファイルとしてダウンロードさせるまでの流れを紹介します。


目次


qrcode.react の紹介

QR コード生成用のライブラリとして、qrcode.react を使用します。

www.npmjs.com

このライブラリは、QR コードを Canvas または SVG で描画でき、JSX でシンプルな記述かつ柔軟なカスタマイズができるためお気に入りです。
公式ドキュメントでも推奨されているように SVG の方が設定に柔軟性があるようですが、今回は後からタイトルを追加で描画するために Canvas を使用します。

qrcode.react では、コンポーネントの読み込み時にいくつかのプロパティを設定するだけで QR コードのアプリケーション内での生成が可能です。

いろいろなプロパティ

先述した通り QR コード描画時にいろいろなプロパティが設定できますが、代表的なものをいくつかピックアップして紹介します。

title

QR コードにタイトルを付与できます。ただし、これは今回のように SVG や Canvas 内に描画するためのものではなく、アクセシビリティやSEO目的で SVG や Canvas に title 要素を埋め込むためのものです。

私がやりたかったことは QR コードの下にタイトルを表示することだったので、このプロパティを使用するのではなく自前で実装することにしました。

level

QR コードのエラー訂正レベルを指定するプロパティです。

なお、エラー訂正レベルは、QR コードが部分的に破損した場合でもデータを復元できる割合を示します。

  • L (Low): データの約7%を復元可能。最も冗長性が低く、その分容量も最小。
  • M (Medium): データの約15%を復元可能(デフォルト)。
  • Q (Quartile): データの約25%を復元可能。
  • H (High): データの約30%を復元可能。最も冗長性が高く、信頼性が必要な場合に推奨される。

印刷するような用途でなければデフォルトの M でよいと思いますが、後述する画像埋め込みで複雑な画像を埋め込む際は、高めのレベルに設定しておくと安心かもしれません。

imageSettings

QR コードの中央に画像を埋め込むための設定を行うプロパティです。任意の画像を QR コード内に配置できます。 企業ロゴやブランドロゴ等を指定するケースが多そうです。

  • src: 埋め込む画像のURL。
  • height: 埋め込む画像の高さ。
  • width: 埋め込む画像の幅。
  • excavate: 画像部分の背景(QR コード)を削除するかどうか(true/false)。
  • x: 画像の左上の X座標(QR コードの左上を基準)を指定。デフォルトは QR コードの中央に自動配置。
  • y: 画像の左上の Y座標(QR コードの左上を基準)を指定。デフォルトは QR コードの中央に自動配置。
  • opacity: 画像の透明度(0: 完全に透明 から 1: 完全に不透明)を指定。デフォルト値は 1。
  • crossOrigin : クロスオリジンの画像を取得する際のリクエスト設定を指定。anonymous(認証情報なし) または use-credentials(認証情報あり)を指定。

作業手順

実際に qrcode.react を React プロジェクトに導入して実装していく手順を紹介します。なお、React プロジェクト自体の構築手順については今回触れません。

今回の実装で使用したライブラリやフレームワークのバージョンは以下の通りです。同じ環境で再現したい方は、これらのバージョンを参考にしてください。

  • React: 18.3.1
  • qrcode.react: 4.1.0

インストール

まずは qrcode.react ライブラリをプロジェクトにインストールします。

npm install qrcode.react

基本的な QR コードの描画機能を実装

早速、QR コードを React コンポーネントとして描画してみます。

以下のように QRCodeCanvas コンポーネントを使用して、ユーザーが入力した URL を元に QR コードを生成します。

import React, { useState } from 'react';
import { QRCodeCanvas } from 'qrcode.react'; // 今回は Canvas を利用

const QR_CANVAS_SIZE = 200; // 初期値は 128

const App = () => {
  const [url, setUrl] = useState('');

  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>QR Code Generator</h1>
      <input
        type="text"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
        placeholder="Enter URL"
        style={{ padding: '10px', width: '300px' }}
      />
      <div style={{ marginTop: '20px' }}>
        <QRCodeCanvas value={url} size={QR_CANVAS_SIZE} />;
      </div>
    </div>
  );
}

export default App;

これで、ユーザーが入力した URL に基づいて QR コードが生成されるシンプルなアプリケーションが完成しました。簡単ですね。

ユーザーが入力したタイトルを追加で描画する機能を実装

次に、生成された QR コードの下にユーザーが入力したタイトルを描画してみます。canvas の 2D 描画コンテキスト (getContext('2d')) を利用して、テキストを追加します。

まずは、共通で使用する定数類を記述します。

const QR_PADDING = 40;
const QR_PADDING_VERTICAL = QR_PADDING * 2;
const QR_PADDING_HORIZONTAL = QR_PADDING * 4;
const QR_CANVAS_SIZE = 200; // 初期値は 128
const TITLE_PADDING = 5;
const TITLE_PADDING_VERTICAL = TITLE_PADDING * 2;
const TITLE_FONT_SIZE = 30;
const TITLE_FONT_FAMILY = 'sans-serif';
const TITLE_BG_COLOR = '#fafafa';
const TITLE_FG_COLOR = '#131313';

次に、描画用の関数を定義します。タイトルの描画位置を決めるのに手こずっていますね。

  const drawQRCodeWithTitle = (
    qrCodeCanvas: HTMLCanvasElement,
    ctx: CanvasRenderingContext2D,
    qrCodeImage: HTMLImageElement,
    title: string
  ) => {
    const titleHeight = (TITLE_FONT_SIZE + TITLE_PADDING);
    const qrAreaHeight = QR_PADDING_VERTICAL + qrCodeImage.height;
    const titleAreaHeight = titleHeight + TITLE_PADDING_VERTICAL;
    qrCodeCanvas.height = QR_AREA_HEIGHT + TITLE_AREA_HEIGHT;

    // 背景を準備
    ctx.fillStyle = TITLE_BG_COLOR;
    ctx.fillRect(0, 0, qrCodeCanvas.width, qrCodeCanvas.height);

    // QR コードを描画
    ctx.drawImage(qrCodeImage, QR_PADDING_VERTICAL, QR_PADDING);

    // タイトルを描画
    const titleXPosition = qrCodeCanvas.width / 2;
    const titleYPosition = qrCodeImage.height + QR_PADDING_VERTICAL;
    ctx.fillStyle = TITLE_FG_COLOR;
    ctx.font = `${TITLE_FONT_SIZE}px ${TITLE_FONT_FAMILY}`;
    ctx.textAlign = 'center';
    ctx.fillText(title, titleXPosition, titleYPosition);
  };

最後に、URL もしくはタイトルが変更されたときに先述の drawQRCodeWithTitle() 関数を実行する useEffect フックを作成します。

  useEffect(() => {
    const canvas = document.querySelector('canvas'); // ※①
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const qrCodeImage = new Image();
    qrCodeImage.src = canvas.toDataURL('image/png'); // ※②

    qrCodeImage.onload = () => {
      canvas.width = qrCodeImage.width + QR_PADDING_HORIZONTAL;
      canvas.style.display = 'block';

      drawQRCodeWithTitle(canvas, ctx, qrCodeImage, title);
    };
  }, [url, title]);

※① document.querySelector('canvas') では単純に最初に見つかった canvas 要素を返すため、本来は ref で操作対象の DOM を指定するのが望ましいですが、今回は QR コードを一つだけ描画するため簡易的な記述を採用しています。
※② toDataURL() は Canvas の内容をエンコードしてデータ URL として取得するため、Canvas のサイズが大きい場合や高解像度の画像を扱う場合、処理に時間がかかることがあり頻繁に呼び出すとパフォーマンスの低下を招く可能性があります。

これで、ユーザーが入力したタイトルを QR コードの下に表示させることができました。

ダウンロード機能を実装

最後に、ユーザーが生成した QR コードとタイトルを含む画像をダウンロードできる機能を追加します。

まずはダウンロード処理用の関数を実装。QR コードを描画した QRCodeCanvas から HTMLImageElement を生成し、その画像を PNG 形式でダウンロードさせます。
canvas.toDataURL() を使って画像データを取得し、a 要素を利用してダウンロード可能にしています。

const downloadQRCodeImage = () => {
  const canvas = document.querySelector('canvas');
  if (!canvas) return;

  const link = document.createElement('a');
  link.download = title
    ? `${title}_qrcode.png`
    : `qrcode.png`;

  const qrCodeImage = new Image();
  qrCodeImage.src = canvas.toDataURL('image/png');

  qrCodeImage.onload = () => {
    link.href = canvas.toDataURL('image/png');
    link.click();
  };
};

ボタンも適当に作っておきます。

<button 
  type="button"
  onClick={downloadQRCodeImage} 
  style={{
    padding: '10px 10px',
    fontSize: '12px',
    color: '#fff',
    backgroundColor: '#a4a4a4',
    borderRadius: '5px',
    cursor: 'pointer',
  }}
>
  Download
</button>

これで、ユーザーは QR コードと入力したタイトルが描かれた Canvas を PNG ファイルとしてダウンロードできるようになります!

全文は下記に貼っておきます。

コード全文

import React, { useState, useRef, useEffect } from 'react';
import { QRCodeCanvas } from 'qrcode.react'; // 今回は Canvas を利用

// 定数類
const QR_PADDING = 40;
const QR_PADDING_VERTICAL = QR_PADDING * 2;
const QR_PADDING_HORIZONTAL = QR_PADDING * 4;
const QR_CANVAS_SIZE = 200; // 初期値は 128
const TITLE_PADDING = 5;
const TITLE_PADDING_VERTICAL = TITLE_PADDING * 2;
const TITLE_FONT_SIZE = 30;
const TITLE_FONT_FAMILY = 'sans-serif';

const App = () => {
  const [url, setUrl] = useState('');
  const [title, setTitle] = useState('');
  const qrCodeRef = useRef(null);

  const drawQRCodeWithTitle = (
    qrCodeCanvas: HTMLCanvasElement,
    ctx: CanvasRenderingContext2D,
    qrCodeImage: HTMLImageElement,
    title: string
  ) => {
    const titleHeight = (TITLE_FONT_SIZE + TITLE_PADDING);
    const qrAreaHeight = QR_PADDING_VERTICAL + qrCodeImage.height;
    const titleAreaHeight = titleHeight + TITLE_PADDING_VERTICAL;
    qrCodeCanvas.height = qrAreaHeight + titleAreaHeight;

    // 背景を準備
    ctx.fillStyle = TITLE_BG_COLOR;
    ctx.fillRect(0, 0, qrCodeCanvas.width, qrCodeCanvas.height);

    // QR コードを描画
    ctx.drawImage(qrCodeImage, QR_PADDING_VERTICAL, QR_PADDING);

    // タイトルを描画
    const titleXPosition = qrCodeCanvas.width / 2;
    const titleYPosition = qrCodeImage.height + QR_PADDING_VERTICAL;
    ctx.fillStyle = TITLE_FG_COLOR;
    ctx.font = `${TITLE_FONT_SIZE}px ${TITLE_FONT_FAMILY}`;
    ctx.textAlign = 'center';
    ctx.fillText(title, titleXPosition, titleYPosition);
  };

  const downloadQRCodeImage = () => {
    const canvas = document.querySelector('canvas');
    if (!canvas) return;

    const qrCodeImage = new Image();
    qrCodeImage.src = canvas.toDataURL('image/png');

    const link = document.createElement('a');
    link.download = title
      ? `${title}_qrcode.png`
      : `qrcode.png`;

    qrCodeImage.onload = () => {
      link.href = canvas.toDataURL('image/png');
      link.click();
    };
  };

  useEffect(() => {
    const canvas = document.querySelector('canvas');
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const qrCodeImage = new Image();
    qrCodeImage.src = canvas.toDataURL('image/png');

    qrCodeImage.onload = () => {
      canvas.width = qrCodeImage.width + QR_PADDING_HORIZONTAL;
      canvas.style.display = 'block';

      drawQRCodeWithTitle(canvas, ctx, qrCodeImage, title);
    };
  }, [url, title]);

  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>QR Code Generator with Title</h1>
      <input
        type='text'
        value={url}
        onChange={e => setUrl(e.target.value)}
        placeholder='Enter URL'
        style={{ padding: '10px', width: '200px', marginBottom: '20px' }}
      />
      <input
        type='text'
        value={title}
        onChange={e => setTitle(e.target.value)}
        placeholder='Enter Title'
        style={{ padding: '10px', width: '200px', marginBottom: '20px' }}
      />
      <div style={{ marginTop: '20px', display: 'flex', justifyContent: 'center' }} ref={qrCodeRef}>
        <QRCodeCanvas value={url} size={QR_CANVAS_SIZE} />;
      </div>
      <button 
        type="button"
        onClick={downloadQRCodeImage} 
        style={{
          padding: '10px 10px',
          fontSize: '12px',
          color: '#fff',
          backgroundColor: '#a4a4a4',
          borderRadius: '5px',
          cursor: 'pointer',
        }}
      >
        Download
      </button>
    </div>
  );
}

export default App;

まとめ

React で QR コードを生成し、さらにその下にユーザーが入力したタイトルを Canvas に描画する方法を紹介しました。
今回は深くカスタマイズしませんでしたが、qrcode.react はプロパティも数多く存在するため、よりカスタマイズした機能を作成することも可能です。

今回、技術ブログでは3回目の執筆をさせていただきました。
1回目と2回目は、それぞれ下記のような記事を書いていたので、お時間ある方はこちらもぜひ読んでいってください!

techblog.styleedge.co.jp techblog.styleedge.co.jp

また、スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。
もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください!

recruit.styleedge.co.jp

せっかくなので、最後に qrcode.react で作成した QR コードも掲載しておきます!それではまた!

*1:※QRコードは株式会社デンソーウェーブの登録商標です。

SoftEther VPN運用の裏側 – 台風による緊急対応の学び

はじめに

こんにちは!
システム事業部のfujikoです。
新卒で入社し現在2年目で、社内のITインフラ周りを担当する基盤チームに所属しています。
具体的な業務内容は、プロダクトインフラの構築・運用、メールサーバー・VPNサーバーの運用保守、その他ITサポートなど多岐にわたります。
その中でも特にVPNの運用業務は、リモートワークの普及に伴いその重要性が高まってきています。

8月のある日、台風が接近し、普段は出社勤務を行っている社員が在宅勤務に切り替える動きが増えたことで、VPNアカウントの発行申請が一気に集中しました。
その際、我々のチームは迅速に連携してVPN環境を整え、リモートワークへのスムーズな移行を実現することができました。
今回は、その緊急対応の流れや具体的な対策、そしてそこから得た学びについて紹介します。

generated by DALL-E

緊急対応の背景

8月に発生した台風は被害が大きくなることも予想され、外出が難しくなることが懸念されました。
そのため在宅勤務を希望する社員が急増し、台風の前日に、社員から次々と新たなVPNアカウントの発行依頼が寄せられました。
通常であれば、依頼は月に数件ほどなのですが、今回は数十件の発行依頼が一度に集中し、迅速な対応が求められました。
また、社員のほとんどが在宅になった場合のVPNサーバーの負荷状況がどうなるか読めなかったため、サーバーのスペック増強も視野に入れて対応を進めました。

対応の流れ

通常の流れ

普段のVPNアカウントの発行までの流れは以下の通りです。

  1.  VPNアカウントの発行依頼が基盤チームに届く
  2.  申請書をもとに発行VPNアカウントを決定
  3.  VPNサーバーにアカウントを登録
  4.  接続情報を依頼者に共有
  5.  登録済みアカウント一覧を更新

台風前日

届き続けるVPNアカウントの発行依頼

勤務開始時刻が過ぎたあたりから基盤チームに対してVPNアカウントの発行依頼が複数届き始めました。
また、この日は夏季休暇期間の中日ということもあり、運悪く基盤チームの人手も足りていませんでした。
普段であれば上記の流れに沿って作業を進めていきますが、この後いつまで依頼が届き続けるのかわからない中で待機し続けるとVPNアカウントの発行が間に合わない状況でした。
チームメンバー各々が他のタスクも抱えており、時間も限られているということで、状況整理も兼ねて一度チーム内で相談する時間を取りました。

対応方針決定

相談の結果、「依頼を待つのではなく、こちらから発行が必要なVPNアカウントのリストを関係各所に問い合わせよう」という結論でそれぞれ動き出しました。 またセキュリティの観点から、発行希望のアカウントが永続利用か一時利用かについても確認しました。
慌てる場面もありましたが、関係各所の協力のおかげで、何とか発行が必要な方の情報を整理することができ、漏れなく対応を完了することができました。

まとめると以下のような流れです。

  1.  VPNアカウントの発行依頼が基盤チームに届く
  2.  台風接近による依頼が複数届き始めたため、関係各所にVPNアカウント新規発行希望者の確認を依頼
  3.  確認の結果をもとにVPNアカウントを発行
  4.  接続情報をまとめて各代表者に共有
  5.  登録済みVPNアカウント一覧を更新

VPNサーバーのスペックアップ

チームでの相談の際、アカウント発行の件とは別に、サーバーのスペックアップが必要ではないかと話が上がりました。
普段出社している社員が一気に在宅勤務になるためです。

弊社では、SoftEther VPNを使用してVPN環境を提供しています。構成のポイントは以下の通りです。

 サーバー環境:AWS EC2インスタンス上にSoftEther VPNをインストールして構築。
 接続方式:L2TP/IPsecを使用し、WindowsやMacの標準VPNクライアントから接続。OpenVPNもサポート。
 トラフィックルーティング:すべてのトラフィックがVPN経由となるフルトンネル*1を採用。

このような構成で運用しているため、特に勤務開始時刻前後はEC2のCPU負荷が急上昇し、スペックが足りていないVPNサーバーにおいてはダウンの可能性も考えられました。
そこで各VPNサーバーのインスタンスタイプ、登録済みアカウント数、普段のCPUの状況等を改めて確認した結果、VPNサーバーのスペックを上げることになりました。
基本的に社員の勤務開始は午前8時~9時に集中するため、事前にVPNサーバーメンテナンスの周知をしたうえで、台風当日の早朝7時からスペックアップ作業を実施しました。

※インスタンスタイプの変更は互換性を考慮する必要があるため、事前に確認が必要です。 aws.amazon.com

台風が過ぎ去って

結果的には何事もなく、ホッとしました。
ただ、台風当日の早朝にスペックを上げたサーバーの1つは、CPU使用率が70%付近になっている時間帯もあり、スペックを上げていなかったらと思うとゾッとします。
セキュリティの観点から、一時的に発行したアカウントは後日削除し、ようやく一件落着。

緊急対応での学び

今回の緊急対応で学んだことを以下にまとめます。

  • 手薄な日の対応計画を見直す必要性
    長期休暇等の事情で基盤チームの人数が少なくなることを見越し、VPNやメールサーバーなど業務の基盤となる部分については、より一層の知識共有が重要だと感じました。
    すでに基本的な体制は整っていますが、手薄な日でも誰もが必要な対応を取れるよう、さらなるスムーズな対応を目指した体制強化が今後の課題だと改めて考えさせられました。
  • 状況に応じた柔軟な対応の重要性
    通常のフローにこだわらず、急な状況に合わせて柔軟に対応する重要性を実感しました。
    今回は、事前にVPNアカウント未発行者を確認し、まとめて対応することで、効率よく処理できました。
  • 緊急事態に備えたフローとリソースの整備の重要性
    緊急時に即座に対応できるフローや専用のアカウント管理があれば、さらにスムーズに対応できたはずです。今後は、こうした状況に対応できるような準備を進めることが課題です。

これらを踏まえ、今後も迅速かつ効率的に対応できる体制を整備していきたいと思います。

おわりに

最後まで読んでいただきありがとうございました!

スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。
もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください!

recruit.styleedge.co.jp

*1:VPN接続時に、すべての通信がVPNを経由する方式。インターネットアクセスも含めた全トラフィックが暗号化されるため、セキュリティが強化される。

Amazon S3にある大量のオブジェクトをGoogleドライブに移行しました

はじめに

こんにちは!システム事業部のNTです。

ある日、クライアント様から「スタイル・エッジ提供のシステムに保存されているファイルを、Googleの共有ドライブに移行したい」というご相談をいただきました。

システムに登録されている現状のファイル数を確認すると約50万にもなり、共有ドライブに移行するにあたっては作業時のパフォーマンスが大きな課題となりました。
また、システム上で表示されているファイル名と、ストレージとして利用しているAmazon S3に保存されている実際のオブジェクト名が異なるため、移行時にオブジェクトのリネーム処理を挟む必要があることも課題でした。

上記2つの課題を克服しながら進めた オブジェクトのリネーム作業Googleドライブへの転送作業 について、使用したサービスと具体的な手順をご紹介します!

generated by DALL-E3

利用したサービスについて

移行作業をスムーズに進めるために、今回は以下4つのサービスを組み合わせて活用しました。

  • Amazon S3バッチオペレーション + AWS Lambda
  • Rclone + Google Drive API (Google Cloud)

Amazon S3バッチオペレーションとは

Amazon S3バッチオペレーションは、S3上に格納されているオブジェクトに対して一括操作を実行できるサービスです。
大規模なデータセットの一括処理が必要な場合に特に有効で、具体的には以下のようなケースで利用されます。

  • 数百万から数十億個のオブジェクトに対して一括コピーや複製をする場合
  • オブジェクトへのタグ付与や削除、メタデータの変更等を一括で行う場合

S3バッチオペレーションでは、操作対象のオブジェクトを指定するためにマニフェストファイル(CSV形式やS3インベントリレポートなど)を使用します。

実際のジョブを設定する際には、マニフェストファイルの指定やLambda関数の呼び出しといった操作を行います。
Lambda関数を利用することで、オブジェクトのリネームやフィルタリングなどの柔軟な操作が可能です。 aws.amazon.com

Rcloneとは

Rcloneは、クラウドストレージ上のファイル管理を行う、オープンソースのコマンドライン型プログラムです。
UNIXの rsynccp に相当するコマンドを使用でき、ファイルの転送、コピー、バックアップ、暗号化などの機能を有しています。
Amazon S3やGoogle Drive、Dropbox等、標準的なサービスを含めて70を超えるクラウドストレージに対応しています。

rclone.org

今回、Rcloneの利用を決めた理由は大きく二つあります。

  • 並列でのファイルアップロードが可能で、転送速度の高速化が期待できる
  • スクリプトやコマンドラインから容易に操作ができ、エラーハンドリングやリトライ機能が充実している

今回は移行対象のファイル数が多かったため、並列実行可能な点は特に魅力的でした!

移行計画について

移行環境

  • 移行元: Amazon S3バケット
  • 移行先: Googleドライブ(クライアント様指定の共有ドライブ)
  • ファイル数: 約50万ファイルで、合計サイズは約180GB。
  • 注意点: S3上のオブジェクト名を、システム上の名前に合わせてリネームする必要がある

ざっくり移行手順

  1. S3上のオブジェクトをリネーム
    S3バッチオペレーションでマニフェストファイルを読み込み、Lambda関数を呼び出してオブジェクトを別バケットにコピーし、リネームします。
  2. S3からGoogleドライブへ転送
    Rcloneを使用してS3からGoogleドライブに転送を行います。

移行準備

S3バッチオペレーションの設定

1. マニフェストファイルの作成

マニフェストファイルを作成するために、下記の情報を取得します。

  • 変更前のファイル名(システム上のファイル名)
  • 変更後のファイル名(S3 上のオブジェクト名)
  • S3 バケット名

取得した情報を基に、CSV形式のマニフェストファイルを作成します。

なお、マニフェストファイルは、「オブジェクトのS3バケット名」と「オブジェクトキー」、「オブジェクトバージョン(※オプション)」という3カラムからなる CSV ファイルです。
オブジェクトキーにはURLエンコードされたJSON文字列を利用でき、後続のLambda関数にパラメータを渡すことが出来ます。

ここでは、「オブジェクトのS3バケット名」と「オブジェクトキー」の2つを指定しています。
システム内のDBに全ての情報が揃っていたので、SELECT文で取得して整形しました。

"S3バケット名","変更前ファイル名,変更後のファイル名"
"s3-bucket","{s3Key:test/フォル1/変更前.xlsx,newKey:test/フォル1/変更後.xlsx}"

次に、Lambda関数に値を渡すために「"変更前ファイル名,変更後のファイル名"(オブジェクトキー)」をURLエンコードしたJSON文字列に変換します。

"s3-bucket","%7B%22s3Key%22%3A%22test%2F%E3%83%95%E3%82%A9%E3%83%AB%E3%83%801%2F%E5%A4%89%E6%9B%B4%E5%89%8D.xlsx%22%2C%22newKey%22%3A%22test%2F%E3%83%95%E3%82%A9%E3%83%AB%E3%83%801%2F%E5%A4%89%E6%9B%B4%E5%BE%8C.xlsx%22%7D"

Lambda関数には、下記のようなJSONがS3 バッチオペレーションより連携されます。

{
"invocationSchemaVersion": "1.0",
    "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo",
    "job": {
        "id": "f3cc4f60-61f6-4a2b-8a21-d07600c373ce"
    },
    "tasks": [
        {
            "taskId": "dGFza2lkZ29lc2hlcmUK",
            "s3Key": "%7B%22s3Key%22%3A%22test%2F%E3%83%95%E3%82%A9%E3%83%AB%E3%83%801%2F%E5%A4%89%E6%9B%B4%E5%89%8D.txt%22%2C%22newKey%22%3A%22test%2F%E3%83%95%E3%82%A9%E3%83%AB%E3%83%801%2F%E5%A4%89%E6%9B%B4%E5%BE%8C.txt%22%7D",
            "s3VersionId": "1",
            "s3BucketArn": "arn:aws:s3:::S3-bucket"
        }
    ]  
}

docs.aws.amazon.com

2. Lambda関数の作成

S3バッチオペレーションから連携されたJSON内に記述された変更前後のファイル名を取得し、オブジェクトを別のS3バケットにコピーするLambda関数を作成します。
JSON内の s3Key には、マニフェストファイルの "変更前ファイル名,変更後のファイル名" の値が格納されており、
この値をデコードすることで、S3バッチオペレーションから任意のオブジェクト名を指定することが可能になります。

import json
from datetime import datetime
from urllib.parse import unquote
import boto3

s3 = boto3.client('s3')

destination_bucket = 'コピー先のバケット名を指定' # 簡略化のため、定数として定義

def lambda_handler(event, context):
    print('Loading function')

    date = datetime.utcnow()
    print('invoke: ' + date.isoformat())

    results = []

    for task in event['tasks']:
        try:
            task_id = task['taskId']
            s3_key_encoded = task['s3Key']
            bucket_arn = task['s3BucketArn']
            source_bucket = bucket_arn.split(':')[-1]

            s3_key = unquote(s3_key_encoded)
            print(f'decoded s3Key: {s3_key}')

            key_data = json.loads(s3_key)
            original_key = key_data['s3Key']
            new_key = key_data['newKey']

            copy_source = {'Bucket': source_bucket, 'Key': original_key}
            s3.copy_object(CopySource=copy_source, Bucket=destination_bucket, Key=new_key)
            print(f'Copied {original_key} from {source_bucket} to {new_key} in {destination_bucket}')

            results.append({
                'taskId': task_id,
                'resultCode': 'Succeeded',
                'resultString': s3_key
            })
        
        except json.JSONDecodeError as e:
            print(f'JSONDecodeError: {str(e)}')
            results.append({
                'taskId': task_id,
                'resultCode': 'Failed',
                'resultString': f'JSON decode error: {str(e)}'
            })
        except Exception as e:
            print(f'Exception: {str(e)}')
            results.append({
                'taskId': task_id,
                'resultCode': 'Failed',
                'resultString': f'Error: {str(e)}'
            })

    return {
        'invocationSchemaVersion': '1.0',
        'treatMissingKeysAs': 'PermanentFailure',
        'invocationId': event['invocationId'],
        'results': results
    }

なお、今回は一時的に別のS3バケットにコピーしてからリネームする方式としましたが、別バケットにコピーすることで次のメリットがあります。

  • 既存バケット内のオブジェクトに影響を与えず、システムを稼働しながらでも安全なリネームが可能
  • 既存バケットとは異なったディレクトリ階層でのオブジェクト保存が可能

Google Cloudの設定とRcloneの準備

1. Google Cloudの設定

Google CloudコンソールからGoogle Drive APIを有効化し、Rclone用のサービスアカウントを発行します。

サービスアカウントとは、ユーザーではなくアプリケーションに使用されるアカウントを指しています。
アカウント固有のメールアドレスで識別も可能です。 cloud.google.com

2. Rcloneの準備

EC2上にRcloneをインストールし、必要な設定を行います。
Google Cloudコンソールで発行したサービスアカウントのキーをRcloneの設定ファイルに追加します。
rclone.org

3. 共有ドライブの作成

今回は、後述する理由により移行先の共有ドライブを複数用意していただきました。
各ドライブのメンバーとして、Rclone用サービスアカウントのメールアドレスを追加します。
サービスアカウントを追加することで、Rcloneでのファイル転送が可能になります。

移行時の課題点と対応策

課題①:Lambda関数の同時実行数の制限

AWS Lambdaの同時実行数は、デフォルトで1,000に制限されています。
操作対象のオブジェクト数が多い場合には同時実行数の上限を超えてしまう可能性があるため、サービスクォータの引き上げも視野に入れる必要があります。

今回は10万ファイルごとに操作し、同時実行数は最大で1,040ほどになりました。
デフォルトの1,000を超えてしまいましたが、念のためにサービスクォータを引き上げていたので、問題にはなりませんでした!

課題②:Google 共有ドライブの制約

共有ドライブの最大アイテム数上限が約50万のため、すべてのファイルを1つのドライブに移行することは不可能でした。 support.google.com

今後ファイルが増加することも見越し、今回は複数の共有ドライブに分散して移行しました。

おわりに

移行作業には、以下の時間を要しました。

  • ファイルのリネーム:約10万ファイルを処理するのに、30~45秒
  • Rcloneでの転送:並列転送を実施したことにより、約15時間

特にリネーム処理は、想定していた以上に時間が短く衝撃でした!
大量のS3オブジェクトの操作や移行時は、S3バッチオペレーションやRcloneの利用を検討してみてはいかがでしょうか?

スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。 もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください! recruit.styleedge.co.jp

AWS DMSを使って再構築したDBへデータ移行させた話

はじめに

こんにちは。システム事業部のTAKです。
記事を書くのは1年7ヶ月ぶりで、今回で3回目となりました。
入社直後に立ち上げに関わった社内システム(以下、システムA)も5年目に突入しようとしています。
運用年数を重ねると、構成が混沌としてくることがあります。
突貫対応による不要な枝葉や緊急対応として迂回したネットワーク構成、新しいシステムや要件に対応するために別VPCに切り出されたリードレプリカなど……

そう、混沌としてきたのです!

そうした経緯があり、今後のシステム間連携などを考えるうえで、システムAが持つRDSのVPCやサブネットなどのネットワーク構成を再構築することとなりました。
稼働を継続しながら別途構築を進める新RDS環境に対して、データを同期させる方法としてAWS DMSを利用しました。 DMSを利用して良かった点や注意する点などをお伝えできればと思います。

created by DALL-E

AWS Database Migration Serviceとは

AWS Database Migration Service (DMS) は、データベースの移行を簡単に行うためのマネージドサービスです。
オンプレミスやクラウドのデータベースをAWSに移行し、ダウンタイムを最小限に抑えることができます。
リアルタイムでのデータ複製も可能なので、運用停止できないようなサービスのDB移行に最適です。 aws.amazon.com

移行の背景

前述したように、5年以上前に構築されたネットワーク構成のため、現在のシステムAを扱ううえで技術的負債が溜まっていました。
具体的にはRDSの属するVPCのCIDRが社内ルールに則っていなかったり、リードレプリカが別VPC上に構築されていたりしました。
その結果、MySQLのバージョンアップやメンテナンス等でB/Gデプロイが使用できなかったり*1、IP管理や設定に支障がでる*2など負の要因がでてきていました。
また、基本的に24時間稼働させる必要があるRDSであったため、今回のデータ移行を含めメンテナンスを行う際は極力停止しない方針で進める必要がありました。

RDSを停止させずに移行したかったのでDMSを採用しましたが、システム停止時間を取れるのであればdumpでのデータ移行が手軽で、コストもあまりかからないと思います。

環境構成

移行前

移行後

DB再構築にあたって、新たに環境を用意しました。
図の背景色が青色が移行前のVPC(以下、旧VPC)、緑色が再構築用のVPC(以下、新VPC)です。
DMSによって継続的なデータ同期が有効になっている状態で、EC2(Webサーバー)のDB参照先を移行後のRDSに切り替えることで、システムを停止せずに切り替えを実施しました。

移行の流れ

移行手順

  1. 新VPCを新たに作成
  2. 新VPCにRDSインスタンス(以下、移行先RDS)を作成
  3. 移行先RDSに見直しを行ったセキュリティグループを適用
  4. DMSで、現行稼働中のRDS(以下、移行元RDS)から移行先RDSへの移行タスクを作成しタスクを実行
  5. 移行元RDSを参照していたサーバーのDB接続先を、移行先RDSのエンドポイントに変更
  6. システムが正常稼働していることを確認後、旧VPCおよび移行元RDSを削除

移行先にテーブルを用意する

DMSはデータ移行を行うサービスであるため、移行先のDBやテーブルは別途手動で作成する必要があります。
DMS実行時にテーブル等がなければ自動で作成してくれますが、データから推測して作成されるため、移行元と完全一致とならないので注意が必要です(詳しくは後述します)。

DMS利用におけるシステム構成

DMS環境は以下の4つを用意する必要があります。

機能 説明
ソースエンドポイント 移行元DBへの接続情報
ターゲットエンドポイント 移行先DBへの接続情報
レプリケーションインスタンス データベース移行処理を実行する環境(EC2/サーバレス)
データベース移行タスク どのレプリケーションインスタンスで、どのソースエンドポイントからどのターゲットエンドポイントに対して、どのスキーマからどんな条件でデータ移行を行うか等、DMSのメイン処理を扱う

DMS環境のVPCはネットワーク構成上扱いやすいよう新VPC内に作成していますが、旧VPCや別のVPCとしても問題ありません。

DMSを利用して

良かった点

  • データ移行先DBについても移行元DBと同じく書き込み可能であるため、Webサーバー側のDB接続先の設定を切り替えるだけでDBの移行が完了した
    • リードレプリカを昇格させて切り替える方法でも似たような動作となるが、昇格作業後からWebサーバー側設定の切り替えの間に同期が停止してしまうため、短時間ではあるがシステム停止が発生してしまう
  • (今回はAWSのDBを使用したが)ソースエンドポイントはAWS以外のDBや、MySQL⇨PostgreSQLなど異なるデータベースを指定することも可能

注意すべき点

  • レプリケーションインスタンスの稼働コストや(リージョンやAZが異なる場合は)データ転送コストがかかる
    • DMSでの移行が終わり次第、速やかにレプリケーションインスタンスを停止する必要がある
  • MySQLの場合、移行元DBのバイナリログ設定を「ROW」にしておく必要がある*3
  • 移行先のテーブルや列が無い場合、DMSが移行データから推測して作成してしまう
    • AUTO_INCREMENTの設定が欠落してしまったり、符号なし数値型(unsigned integer)だったものが符号付き数値型(signed integer)で作成される場合がある
    • データベース移行タスクの設定で「ターゲットテーブル準備モード」がデフォルトのまま(ターゲット上のテーブルを削除)だと常に発生するため、データのみ削除する「切り捨て」とするほうがよい
    • DMS同期中に移行元DBのテーブル列追加や変更を行った場合は、移行先DBのテーブルを再構築しないと推測で列を追加されてしまう(それらしく作られるため、移行後すぐには気付きづらい)

おわりに

設定や準備に時間とコストがかかる面はありますが、停止せずにDB移行を安全に実施できる仕組みがあるのはありがたいです。
特に移行先DBが書き込み可能なまま同期できるのが衝撃でした。
無停止でのDB移行を考える際は、DMSの利用を検討してみてはいかがでしょうか?

スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。
もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください! recruit.styleedge.co.jp

*1:B/Gデプロイを行う際は、紐付くすべてのリードレプリカが同一リージョン、同一VPCである必要があるなど制約がある

*2:VPCのCIDRが同一のものがあり、直接VPCピアリングすることができなかった

*3:https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.MySQL.html#CHAP_Source.MySQL.CustomerManaged

CCNA合格体験記

はじめに

こんにちは!
システム事業部のaoです。新卒で入社して3年目になります。
現在、ネットワークチームに所属し、社内およびクライアントのネットワーク設計から保守まで担当しております。
今回は、先日合格したCCNAの合格体験記を書いていきます!

created by DALL-E

CCNAとは

CCNA(Cisco Certified Network Associate)は世界最大のネットワーク機器メーカーであるCisco Systemsが提供する資格です。CCNAを取得することで、ネットワークの基礎知識およびCisco機器(ルーター、スイッチ、アクセスポイント)に関するスキルを証明できます。
出題内容と割合は以下の通りです。

出題範囲 割合
ネットワークの基礎 20%
ネットワークアクセス 20%
IPコネクティビティ 25%
IPサービス 10%
セキュリティの基礎 15%
自動化とプログラマビリティ 10%

参考:https://learningnetwork.cisco.com/s/ccna-exam-topics

試験時間は120分でした。

CCNAを取得しようと思ったきっかけ

1年ほどネットワーク業務に携わっていましたが、業務でよく使う技術を中心に理解していったため、基礎的な知識が断片的に身についていました。知識を整理し、自信をつけるために受験しました。
また、福利厚生の「STEP BY YOU」により、自己負担無しで受験できました。(詳細は後述します。)
CCNAの受験料は、46,860円(2024年4月時点)とIT系の資格試験の中では非常に高額ですが、経済面での重荷が消え、受験の後押しになりました。

STEP BY YOUとは

スタイル・エッジでは、社員の成長を支援する「能力開発応援制度:STEP BY YOU」を導入しています。
この制度は、カンファレンス参加、書籍購入支援、資格取得支援、スクール受講の4つの柱で構成されています。
社員は関連するカンファレンスやセミナーへの参加費用、技術書や専門書の購入費用、資格試験の受験料、スクールの受講料を全額補助されます。
個々の成長が会社全体の発展に繋がると考え、社員一人ひとりのスキル向上を全力でサポートしています。
※内容は2024年7月時点のものです。

CCNAの勉強方法

本格的に勉強した期間は、約3.5ヶ月です。

使用した参考書とサービス

  • 1週間でCCNAの基礎が学べる本 第3版
  • 基礎からわかる!CCNA最短合格講義
  • Cisco CCNA問題集[200-301 CCNA]対応(以下、黒本)
  • ping-t(有料版)  ※ping-tとは、IT系の様々な資格に対応したWeb問題集です。

「1週間でCCNAの基礎が学べる本」を読み、黒本にチャレンジしたところあまり解けませんでした。
こちらの本は、CCNAの出題範囲の約3割程度がわかりやすく記載されていると感じましたが、出題範囲の全てをカバーしているわけではないように思いました。
そこでより詳しく説明している「基礎からわかる!CCNA最短合格講義」を読み、知識量を増やしながらping-tを中心に問題を解き始めました。
黒本がping-tよりも難易度が高く感じたため、より深く学びたい時は黒本を使っていました。
最終的に、ping-tは4周、黒本は1.5周程度、勉強していました。

試験勉強中に意識していたこと

どうしても、試験勉強に切羽詰まると出題率の低い箇所の学習は後回しにしてしまいがちです。
しかし、今回の受験は知識を整理して業務で活用できるようにし、自信をつけるためのものだったので、出題頻度の低い単元でも、業務で必要とする箇所は重点的に勉強するようにしました。
例を挙げると、CCNAではルーティングの優先順位やスイッチの設定の出題率が高い一方で、DHCPやNATの出題率は低いですが、業務で必要な知識なので諦めないようにしていました。

また、実際の機器を触った方が理解が一気に進むのでおすすめです。
CCNAの勉強期間には、業務にてCiscoスイッチでセグメント分けの構築を行う機会がありました。CCNAの範囲であるVLANやACLを実機で設定することで理解を深めることができました。

結果

最終的に、模試の得点率が90%になった状態で受験しました。本番は、76%程度で合格しました。

実際に受験してみて思ったこと

問題文が理解しづらい

他の方の合格体験記にもある通り、全体の約20%は直訳的で読み取りづらい文章になっており、問題の意図を理解できませんでした。
Cisco機器の設定で使用する用語は英語が元になっています。用語をそのまま覚えるのではなく、日本語でイメージして覚えておくことが大事だと感じました。

コマンドシミュレーション問題(LABO問題)

コマンドシミュレーション問題は、和訳に不自然な箇所があり問題の意図を理解することに時間がかかったり、長いコマンドを打った後で削除しようとすると画面が固まってしまう場合があることを認識しておいた方が良いと思いました。
また、いつコマンドシミュレーション問題が出題されるかわからないので、時間配分が難しいです。私の場合、1問目のコマンドシミュレーション問題に時間をかけすぎてしまい、2・3問目をきちんと解ききることができませんでした。
わからない問題は諦めて飛ばす覚悟が必要だと思いますが、1度回答した問題は見直せないので注意が必要です。

本番の受験環境

今回の試験では、PearsonVUEからオフライン受験で申し込みました。試験中のメモは、紙ではなくホワイトボードとペンを使用します。普段と違う道具を使うため、不安な方はそれらの道具を用いて練習しても良いかもしれません。   

私が受験した際は、最後の問題を解いて、次へ進むボタンを押した瞬間に結果が表示されました。心の準備が必要だと思います。
(他の方の合格体験記では、結果が表示されなかったパターンもあるらしいので、人によるかもしれません。)

CCNAを勉強してよかったこと

理解が足りていなかった基礎的な部分を学び直すことができました。
ネットワーク構成の設計や検証する際にとにかく「第何層」かを意識する癖がつきました。OSI基本参照モデルやTCP/IPモデルを用いて「なぜこの挙動なのか」という理解が早くなったと思います。

©2024 styleedge

最後に

ネットワークは奥深いですが、CCNAを取得できたことでネットワーク知識に少し自信がつきました。
今後は、CCNAと業務で培ったオンプレの知識を生かしてクラウドにも挑戦していきたいと思っています。

スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。
もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください!

recruit.styleedge.co.jp

参考

GitHub Copilot Businessをチームに導入してみた

はじめに

こんにちは。システム事業部イネイブリングチームに所属しているOrochiです。
イネイブリングチーム*1は、開発チームの開発生産性向上の活動や技術支援などを行っています。
その取組みの一環として、GitHub Copilot Businessを導入しました。
現在は、開発チームメンバーであれば申請することで誰でも使える状態になっています。

Created by DALL-E

この記事では、GitHub Copilot Businessを導入した背景や注意点、導入後の効果などについて紹介します。

GitHub Copilotとは?

この記事を読んでくださっている方は既にご存知かと思いますが、GitHub Copilotは、OpenAIのCodexモデルを利用したコード補完ツールで、プログラミング中に次に書くべきコードを提案してくれます。 docs.github.com docs.github.com

Visual Studio Codeなどのエディタに拡張機能を導入することで利用可能です。
また、GitHub CLIを使用するとコマンドライン上でも動作します。

Copilot Chatという機能もあり、こちらを使用することで特定のファイルに記載されている処理の解説などをCopilotにお願いすることができます。
例えば、私はOSSのフレームワーク理解のために、内部処理の解説をさせています。

Laravelの内部処理を聞いている画像

導入の背景

弊社の開発チームがGitHub Copilotを導入するに至った背景には、以下の社内課題とニーズがありました。

エンジニアの不足

会社の成長と共に、開発チームのプロジェクト数が増える一方で、エンジニアの増員が追いつかない状態です。
それに対して、イネイブリングチームとしては効率化に関する各種施策を打ち出していましたが、開発チームの協力が不可欠でありながらも、日々の業務に追われて協力の余裕がない状況が続いていました。

記述量の多いコード

最近は、従来のプロダクトの反省を踏まえて、スパゲッティコードを避けるための適切なファイル分割と責務の分離を徹底しユニットテストを必ず書くようになりました。
しかし、それに伴ってコードの記述量が増加し、物量的に開発スピードが出なくなるといった問題が発生していました。

導入を決定した理由

これらの課題に対処するため、GitHub Copilotの導入を決定しました。その理由は以下の通りです。

生産性向上の効果が期待できた

他社の技術ブログなどで情報収集をし、
「繰り返しの作業を迅速にできるようになった」
「開発効率が上がった」
という記事が多かったため、コードの記述スピードや正確性を向上させ、反復的な作業やテストコードの自動生成に効果が期待でき、組織の開発生産性の向上に繋がると考えました。

既存のGitHubアカウントの活用

弊社では現在ソースコードの管理をオンプレミスのGitLabで行っていますが、イネイブリングチームの施策として、CI/CDの拡張性などを加味してGitHubへ移行する計画がありました。
そのため、一部では既にGitHubを利用しており、サブスクリプションも進めていたのでスムーズに導入できました。

チームメンバーの興味

現場からもCopilotを使用したいという話は上がっていて、その後押しもあり意思決定は比較的スムーズにできました。上司の理解があったのも大きかったです。

注意点とデメリット

社内のコードの漏洩やライセンス侵害などの可能性

GitHub Copilotを利用する際に、社内のコードがGitHub Copilotの学習に利用され、外部に漏れるリスクが懸念されました。また、OSSのコードを参考にコード補完を行うため、脆弱性を埋め込んだり、ライセンス侵害をしたりといったリスクも考えられます。

弊社は、公開コードに一致する提案を無効に設定しているため、OSSのコードをGitHub Copilotが提案しないようにしています。*2
このため、ライセンス侵害の可能性は低く、リスク許容としています。
脆弱性に関しては、複数人でコードレビューすることでセーフティネットを張っています。

若手メンバーの成長を妨げる可能性がある

AIサービス全般に言えることですが、今は「考える」ということをAIに任せられるため、経験年数の浅い若手メンバーが最初からGitHub Copilotなどを使用してコーディングをすると、AIなしではコードが書けない人材になってしまうことが懸念されました。

こちらは、コードレビューでコードの意図を問うようにしたり、ペアプロを通して教育していくことで、何も考えずにGitHub Copilotから提案されたコードを採用するのではなく、自分で書いたコードとして判断する癖をつけるように対応しています。

導入後の効果とメリット

コーディング効率の向上

GitHub Copilotの導入により、特にテストコードや反復的な作業において、コーディング効率が大幅に向上しました。具体的には、次のような効果がありました。

テストコードの自動生成

同じようなテストコードを何度も書く手間が省け、効率が向上しました。
また、ある程度コメントでどういったテストを書いてほしいかを示すことで、全く新しいテストコードも生成可能になりました。

プロダクトコードの自動生成

プロダクトコードもGitHub Copilotに提案してもらえるようになり、こちらも作業効率が向上しました。
適切なファイル分割と責務の分離を徹底していたため、Copilotから提示されたコードは比較的正確なものであることが多いです。

フレームワークやライブラリの理解の補助

弊社のアプリケーション開発では主にLaravelを使用し、中には設計思想に応じた拡張を加えているものもあります。GitHub Copilotによって、Laravelや拡張部分の理解が進み、実装のスピードが向上しました。

開発チームのフィードバック

GitHub Copilot導入後、他社の技術ブログやSPACEフレームワーク*3を参考に開発チームにアンケートを実施し、以下のようなフィードバックを得ました。

GitHub Copilotは何をする時に使用しますか?

一日の仕事の中で、コーディングに費やす時間はどれくらいですか?

GitHub Copilotを使用することでより生産的に仕事ができるようになりましたか?

GitHub Copilotを使用することでより早く繰り返しの作業を完了できるようになりましたか?

GitHub Copilotを使用することでより早くタスクを完了できるようになりましたか?

GitHub Copilotを使用すると繰り返し作業に費やす精神的な負担が軽減されましたか?

1日あたり、GitHub Copilotを使用することでおおよそどれくらいの時間を節約できましたか?

その他の結果

GitHub Copilotを使用するとコーディングのストレスが軽減されますか?

GitHub Copilotを使うことで仕事に充実感を感じられるようになりましたか?

GitHub Copilotを使うことでより満足度の高い仕事ができるようになりましたか?

GitHub Copilotを使用することで作業に集中できる状態に入りやすくなりましたか?

GitHub Copilotを使用することで検索にかかる時間が短縮されましたか?

GitHub Copilotについて使い方やここが便利!など共有したいことがあれば教えてください。

  • 同じようなことを何回も記述することが減った
  • 別の箇所に書いてある内容を読み取って書いてくれるので、リファクタなど一箇所変えれば他のところも読み取って書き換えてくれる
  • コード補完がとにかく便利
  • 人が書いたコードを読み解くときにGitHub Copilotに説明してもらえるのも便利
  • ショートカット設定しておくと、キーボードから手を離さなずにできてめちゃくちゃ便利

その他の意見

  • フレームワークのソースコードを読み取るのが楽になった
  • 三項演算子やスプレッド演算子などの名称を調べるときに便利。スプレッド演算子はgoogleで検索しても「...」などでは引っ掛からなかった
  • 共有というほどではないですが、コードを説明してくれることと書いているときに補完してくれることがありがたい
  • コードの候補がでるので、時短になる
  • Seederやテスト用コードの自動作成・自動修正などは正確に作成してくれる

GitHub Copilotについてここが使いづらい....など共有したいことがあれば教えてください。

  • 長いコードを書いてくれる時は少し時間がかかるので、もう少し早くなったらいいなと思う
  • コード補完を一度に複数行生成されちゃうと、一部だけacceptするようなことができません。全部acceptしてから直すみたいな手間があるのはちょっと使いづらいかも
  • 設問にあった「繰り返し作業」をさせる発想がなかったので、試してみたい
  • 便利な使い方集や、自分はこうやって使っている、という情報共有できる場があると良さそうだなと感じた
  • コード以外のことを調べることができない

導入後アンケートまとめ

全体的に肯定的な意見が多く、生産性の向上や繰り返し作業のスピードアップなどが実感としてある回答が多くありました。
導入前の解決したい問題にアプローチすることができたので、導入して効果が出ているということが開発者のアンケートでもみることができました。
一方で、長めのコード生成に関してはまだ課題感を感じているメンバーもいるため、改善を期待すると共に、GitHub Copilotを扱う上でのテクニックやナレッジについても、見つけ次第随時共有していこうと思います。

おわりに

GitHub Copilotの導入によって、開発チームのプロジェクトによってはベロシティが1.5倍になったという報告もあり、導入効果は目に見えて出ていると感じます。
導入フローは整備しましたが、まだ使用していないメンバーもいるので、よりGitHub Copilotを利用・活用できるように働きかけ、利用率の増加とさらなる開発生産性の向上を目指し、よりよいアウトカムをだせるように活動を続けていきます。

スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。
もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください! recruit.styleedge-labo.co.jp

参考資料

*1:チームトポロジーという組織設計のフレームワークにおける、チームタイプの一つ。

*2:公開コードに一致する提案を有効または無効にする設定について

*3:Satisfaction and well being/Performance/Activity/Communication and collaboration/Efficiency and flow の頭文字をとったもの。開発者の生産性を測るフレームワークの一つ。

Aurora Serverless v2を導入してみた

はじめに

こんにちは。システム事業部のgakiです。新卒入社3年目にして初めての技術ブログ執筆です。 私事ではありますが、最近AWS Certified Solutions Architect - Professionalに合格したこともあり、最近は特定の言語やプロダクトに偏ることなくAWS周りの相談に乗ったり、新規案件のインフラ設計なども行っています。

さて、今回のテーマはAurora Serverless v2についてです。基本的な仕様や導入のきっかけについて書いていきます。

generated by DALL-E3

Aurora Serverless v2とは何か

AuroraはAmazon RDSのサービスの1つで、高可用なデータベースを提供しています。 Aurora Serverless v2はAuroraの中でも比較的新しいサービスで、負荷状況に合わせてDBのスペックを自動でスケールアップ・ダウンする機能が備わっています。

スペックを表す単位としてACUというものがあり、1ACUに対して約2GiB のメモリと対応する CPU、ネットワークが付与されます。 詳細は公式ドキュメントをご覧ください。

docs.aws.amazon.com

Aurora Serverless v2 導入のきっかけ

弊社のあるプロダクトではデータベースにAuroraを使用しています。しかし、プロダクトの規模が大きくなるにつれて、高負荷のSQLが特定の時間帯に多く実行されることによりCPU使用率が100%に張り付き、レスポンス性能が低下するといったことに悩まされていました。 サーバレス導入前は、下記のような構成で運用していました。

システム側での検索や帳票出力など、高負荷な読み取りSQLはリーダーインスタンスに振り分けることで負荷分散していました。しかし負荷はデータ量の増加に伴い上昇していき、SQLのチューニングは定期的に行っていたものの対処しきれず、DBインスタンスのサイズを上げて何とか負荷に対応していました。高負荷時のレスポンス性能を改善するため、スパイクアクセスに対して優れたスケーリング性能を発揮するデータベースである、Aurora Serverless v2を採用しました。

導入手順

Auroraでは、1つのクラスターの中でプロビジョンドインスタンスとサーバレスインスタンスを共存させることができるため、段階的な導入が可能でした。 下記の簡単な手順でクラスターにサーバレスインスタンスを追加することができます。

  • Auroraクラスターのメニューから、「アクション」を選択
  • DBインスタンスクラスに「Serverless v2」を選択

また、既存のプロビジョンドインスタンスをサーバレスインスタンスに変更することも可能です。

置き換え方法

今回は、本番環境で稼働しているリードレプリカのみサーバレスに置き換える方針で進めることになり、具体的な作業は下記の流れで行いました。

  1. 置き換え前 リーダーインスタンスと同数のサーバレスインスタンスを仕込んでおく
  2. 置き換え中 カスタムエンドポイントの宛先DBをレプリカ(プロビジョンド)→レプリカ(サーバレス)に変更

障害時は、カスタムエンドポイントの宛先DBをレプリカ(プロビジョンド)に戻すことで復旧が可能です。

今回Aurora Serverless v2を導入するプロダクトではリードレプリカへの接続はカスタムエンドポイントを使用していたため、置き換え作業はカスタムエンドポイントのターゲットインスタンスの変更で対応しました。

移行後に感じたメリット/デメリット

メリット

パフォーマンスの向上

導入前後で比較すると、高負荷時のレスポンス速度がかなり改善しました。導入前はデータを大量に出力する際にタイムアウトが発生することもありましたが、Aurora Serverless v2を導入して以降、タイムアウトは一度も発生していません。

メンテナンスコストの削減

現在このプロダクトで運用しているAurora Serverless v2は、以前のDBの1/8~4倍のスケーリング幅で柔軟に稼働しています。そのため、毎日のように来ていたDBのCPUアラートが止まり、高頻度の調査・メンテナンス作業から解放されました。

高速なスケーリング

実用性を評価する上で一番気になるところといえば、スケーリングの速度かと思います。

最大ACU使用時のCPU使用率と、サーバレスインスタンスのACUの推移を下記の通り比較してみました。 (Aurora Serverless v2ではCPUUtilizationの値がその時点でのACU数に対する使用率となるため、数式を利用し最大ACU使用時に対応する値に換算しています)

この図から、スケールアップは負荷の上昇の1分以内には開始しており、スケールダウンは比較的ゆっくりと行われるということが分かるかと思います。

一方でAuroraは、負荷上昇に対応する別機能として、Aurora Auto Scalingというものも提供しています。
こちらはスケールアウトの方式で、一時的にインスタンスを増設し負荷に対応するというものです。 しかし、増設インスタンスのプロビジョニング時間が追加でかかるため、負荷上昇への反応速度は10分ほど遅れます。 以前、同プロダクトにてAurora Auto Scalingを導入していましたが、増設インスタンスの立ち上がりが遅く、実際に稼働し始める頃には負荷は減少傾向になっているといったことが多くありました。

docs.aws.amazon.com

以上から、現状のAmazon RDSが提供するスケーリングの仕組みの中では、Aurora Serverless v2が最も負荷の増減に流動的に対応できるサービスであると考えられます。

ただ、スケーリングの速度が常に高速かと言われると、そうでは無いようです。 下記公式ドキュメントによると、スケーリングのスピードはその時点でのACU数が多い方が速くなる、との記載がありました。 docs.aws.amazon.com

現在、同プロダクトのAurora Serverless v2は4ACU~32ACUで設定しておりますが、プロダクトの要件として1分以内のスケーリングで速度面は十分なため、性能面の問題は無いと感じています。 ACUの範囲については、負荷テストなどを実施した上で良い落とし所を見つけていただければと思います。

デメリット

持続的な負荷がある場合はコスト効率を上げにくい

Aurora Serverless v2は使用量による従量課金です。そのため、持続的な負荷がある場合は料金が高くなる可能性があるため注意が必要です。
というのもAurora Serverless v2は、その負荷に相当するプロビジョンドDBインスタンスを利用した場合と比較すると高コストであるためです。

東京リージョンの料金で汎用インスタンスであるdb.m5.largeと、それに相当する4ACUのAurora Serverless v2で料金を比較してみました。

1時間あたりの料金1
db.m5.large USD 0.235
Aurora Serverless v2 (4ACU) USD 0.8

このように、持続的な負荷がある場合はAurora Serverless v2が原因でコストパフォーマンスがあまり向上しなかったり、かえって高コストになってしまう可能性があります。 Aurora Serverless v2 を導入する際は、持続的な負荷が原因で料金がかさまないよう注意が必要です。

移行の教訓とベストプラクティス

負荷状況がAurora Serverless v2のユースケースとマッチするか確認しよう

お話ししてきた通り、Aurora Serverless v2がハマるのは常時低負荷&突発的な高負荷といった状況になります。

例えば下記のような場合は相性が良いかと思います

  • 常時負荷は低いが、1日数回の高負荷なバッチ処理を行うため泣く泣く高スペックのデータベースを稼働させている
  • 検証環境やデータ分析など短い使用時間のために、ほぼアイドル状態のデータベースを稼働させている

このような状況ですとAurora Serverless v2を導入することで、必要な時は必要なスペックで稼働させることができ、一方で大きなコスト削減も見込めます。

負荷が無い時間帯は、最低ACUを下げてコストダウンを図ろう

夜間や休日などにシステムがあまり利用されない場合は、その時間帯だけ最低ACUを下げることでコストの削減が可能です。

下記のように、1コマンドでデータベースの設定値を更新できるため、EventBridgeと組み合わせるなどしてACU調整の自動化も容易に行うことができます。 docs.aws.amazon.com

便利さに依存せず、継続的なSQLのチューニングを

Aurora Serverless v2を導入することで、使用量に応じた従量課金となります。そのため負荷の高いSQLを発行していると、その分だけ料金がかさみます。

導入前になるべくSQLのチューニングを済ませておく

負荷が高くなる要因は事前に取り除いておくと、性能面、コスト面において適切なスケール幅を設定することが可能です。

導入後も負荷軽減が可能なところは改善を続け、スケーリング幅の見直しも行う

改善できそうなSQLがあればチューニングをし、適切なスケーリング幅を設定することで、コスト効率を高められます。
負荷の高いSQLを効率的に検出するために、Performance Insightsの利用を検討してみても良いかもしれません。 docs.aws.amazon.com

おわりに

以上、Aurora Serverless v2導入について書いてみました。品質の維持やメンテナンスコスト削減の面では大きな強みを持っているサービスですが、負荷状況によっては割高になってしまうため状況によって適切な判断が必要となります。

導入の方法は柔軟に決めることができるため、簡単に試すことができるのが大きな強みだと感じています。DBのコスト削減を目指していたり、バースト的な負荷によりレスポンスに懸念があるような状況でしたら、ぜひ導入を検討してみてください。


スタイル・エッジでは、一緒に働く仲間を絶賛大募集しています。 もし興味を持っていただけましたら、以下の採用サイトも一度覗いてみてください!

recruit.styleedge-labo.co.jp


  1. 2024.5.29現在