TECH PLAY

ニフティ株式会社

ニフティ株式会社 の技術ブログ

487

この記事は、 ニフティグループ Advent Calendar 2023 18日目の記事です。 こんにちは。会員システムグループでエンジニアをしている山田です。 前回 はDocker Composeを利用し、コンテナ上でアプリケーションを実行する環境を整えました。今回はコーディングまでを含めた開発環境を整えていきます。 コンテナ環境の課題 コンテナという独立した環境を用意したので、コーディング作業もコンテナ内で行いたいところです。LinterやFormatterなどのツールもコンテナ内で完結させることにより、統一された開発環境をチームメンバー全員に提供できます。 一方でコンテナ環境は基本的にCUI環境です。GUIアプリケーションを利用する方法がなくはないですが、ローカルPC側・コンテナ側とも設定が煩雑になるため、基本的にCUIアプリケーションを利用することになります。こうなるとVSCodeなどのリッチなIDEを利用できません。 Dev Containers 前述の問題を解決するのがDev Containersです。 これは元々「VSCode Remote – Container」と呼ばれるVSCode拡張機能だったもので、現在ではコア部分がVSCodeとは独立した OSS になっています。最近ではIntelliJなどでも 使えるようになっている ようです。 仕組みとしては以下のようになっています。前回のモノレポ構成にDev Containersを追加した図です。 コンテナ作成時にVSCodeを追加インストールし、 VSCodeごとコンテナ内で動かします 。ローカルPC側のVSCodeはコンテナ内のVSCodeと通信し、画面描画のみを担当することになります。エディタ自体がサーバ・クライアント構成をとることで、コンテナ内で開発環境を完結させつつ、リッチなエディタを利用することができます。 設定方法 今回はVSCodeでの設定方法を紹介します。設定方法はDockerfileを利用する方法と、Docker Composeを利用する方法の2種類がありますが、後者の方が柔軟なのでこちらで解説を行います。 VSCode拡張機能のインストール VSCodeをインストールし、 Dev Containers拡張機能 をインストールしておきます。 Docker Composeの用意 前回用意したようなDocker Composeの定義ファイルを用意しておきます。単一のcompose.ymlでも、統合機能を利用したcompose.yml + compose-dev.ymlのような複数ファイル構成でも、どちらでも可能です。 設定ファイルの用意 設定ファイルは . devcontainer / ディレクトリに配置するので、このディレクトリを作ります。これはコンテナごとに用意することになります。 ポリレポであれば repository/ - .devcontainer/ - compose-devcontainer.yml - devcontainer.json - src/ - compose.yml のような構成になりますが、モノレポであれば repository/ - frontend/ - .devcontainer/ - compose-devcontainer.yml - devcontainer.json - src/ - backend/ - .devcontainer/ - compose-devcontainer.yml - devcontainer.json - src/ - compose.yml のように、各サブプロジェクトごとに用意することになります。 設定ファイルとしては以下の2つが必要です。 compose-devcontainer.yml 名前は何でも良いのですが、動作上書き用のDocker Compose定義ファイルを用意します。 # compose-devcontainer.yml services: <開発対象コンテナのservice名>: command: /bin/sh -c "while sleep 1000; do :; done" あらかじめ用意したcompose.ymlではアプリケーションが全て起動してしまうため、コーディング後の反映にコンテナ再起動が必要になってしまいます。これは面倒なので、commandの書き換えによって「ただ動き続けるだけ」のコンテナにしてしまい、アプリケーション実行は自力でコマンドを叩くようにします。 devcontainer.json Dev Containers特有の設定ファイルです。最低限必要なのは以下の設定になります。 { "name": "frontend", "dockerComposeFile": [ "../../compose.yml", "compose-devcontainer.yml" ], "service": "frontend", "workspaceFolder": "/usr/src/app", } name エディタ上での表示名 dockerComposeFile Docker Composeの定義ファイル . devcontainer ディレクトリからの相対パスで記述する 複数指定することで - f オプションと同じ動きになるので、最後に前述のcommand上書き用のファイルを指定する service 開発対象となるコンテナの、Docker Compose上でのservice名 workSpaceFolder コンテナ内で、VSCodeが開くディレクトリのパス compose.ymlでソースコードをマウントした先のパスになるはず 起動方法 VSCodeで 開発対象コンテナの プロジェクトルートディレクトリを開きます。 . devcontainer を置いたのと同階層です。 モノレポの場合は開くディレクトリを間違えやすいので注意してください。 repository/ <- ココじゃなくて - frontend/ <- ココ!!! - .devcontainer/ - backend/ - .devcontainer/ - compose.yml 開いたら、「Cmd+Shift+P (WindowsはCtrl+Shift+P)」でコマンドパレットを開き、「Reopen with Container」を実行します。 またはプロジェクトを開いた時点でダイアログが出るはずなので、こちらからでも同じ操作が可能です。 初回起動時はコンテナ起動に加えてVSCodeのインストールなどが走るので時間がかかります。無事起動すると左下が「Dev Container」または「開発コンテナー」になります。 アプリケーション実行 commandの書き換えによりアプリケーションの実行を止めているため、自力でアプリケーションの実行が必要になります。 VSCode内蔵のターミナルはコンテナ内に接続されているため、ここからアプリケーション起動コマンドを実行しましょう。 # Node.jsアプリケーションの場合 pnpm install pnpm dev コンテナの終了 VSCodeを閉じた時点でコンテナも同時に終了されます。 ただし閉じた後すぐに開きなおすと前のコンテナの終了が完了しておらず、ポート干渉などでコンテナ起動に失敗することがあります。慌てずにリトライしましょう。 コンテナ定義を変えた場合 Dev Containersは一度起動・停止した後は前回起動したコンテナを再利用しようとします。compose.ymlやDockerfile、devcontainer.jsonなどを書き換えた場合はコンテナ定義の再読み込みが必要です。 すでにDev Containersを開いた状態の場合、再読み込みはコマンドパレット(Cmd+Shift+P)から「Rebuild Container」を実行します。 開く前の場合はコマンドが「Rebuild and Reopen in Container」に変わります。 追加設定 VSCode拡張機能 Dev Containersで利用されるVSCodeはコンテナに新規インストールしたものなので、ローカルPC側のVSCodeでインストール済みの拡張機能は引き継がれません。 devcontainer . json にはエディタごとのカスタマイズ設定があり、ここに拡張機能を記載することでインストールすることが可能です。 { ... "customizations": { "vscode": { "extensions": [ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "stylelint.vscode-stylelint" ] } } } ユーザ拡張機能設定 Vimキーバインドを使いたい、Copilotを使いたいなど、プロジェクトごとではなくユーザごとにインストールする拡張機能を変えたい場合があります。この場合は、VSCodeのユーザ設定に記述を加えます。ユーザ設定はmacであれば $ HOME / Library / Application Support / Code / User / settings . json にあるはずです。 { ... "dev.containers.defaultExtensions": [ "github.copilot" ] } defaultExtensionsに設定した拡張機能は、そのユーザが起動するDev Containers全てに追加されるようになります。上記例ではGithub Copilot拡張機能が追加されることになります。 VSCodeの設定 VSCodeのユーザ設定は引き継がれますが、チーム内で統一する設定はリポジトリ内で管理すると良いでしょう。これは通常のVSCodeと同じく、 . vscode / settings . json に記載します。 { "editor.formatOnSave": true, ... } なお拡張機能と同じくdevcontainer.jsonに書くことも可能ですが、設定の反映に「Rebuild Container」の実行が必要になってしまうため、あまりお勧めしません。 注意点 暗黙的なファイルコピー Dev Containersは起動時にコンテナ定義を書き換えてVSCodeをインストールするほか、以下のファイルをローカルPCからコピーします。 ~/.gitconfig ~/.ssh/* これはコンテナ内でもGit操作を行えるようにするための措置ですが、プロキシの設定やローカルPC上にしかないツールの指定を行っている場合、コンテナ内でのGit操作に失敗する場合があります。 このような場合、 devcontainer . json でコンテナ起動後のコマンドを指定できるので、該当箇所を消す処理を入れるようにしましょう。 { ... // postAttachCommandで、コンテナが起動してVSCodeにアタッチされた時の処理を指定できる // プロキシやテンプレートの削除、エディタ指定の上書きを実施 "postAttachCommand": "(git config --global --unset http.proxy || exit $(($? == 5 ? 0 : $?))) && (git config --global --unset commit.template || exit $(($? == 5 ? 0 : $?))) && git config --global core.editor vim" } モノレポ時のコンテナ切り替え モノレポ環境では以下の点に注意が必要です。 同時に複数のコンテナでDev Containersを起動することは不可能 Dev Containersの対象コンテナを切り替える場合、「Rebuild Container」の実行が必要 Dev ContainersはDocker Composeで起動したコンテナのうち、開発対象コンテナ1つだけにVSCodeをインストールします。複数同時にインストールするように作られてはいないので、複数コンテナ同時に開発を進めることはできません。必ず切り替えて使う必要があります。 またコンテナを切り替えた場合には既存コンテナをリセットし、VSCodeをインストールするコンテナを変える必要があります。このため、切り替え時に毎回「Rebuild Container」を実行する必要があります。忘れがちなので注意が必要です。 実際に使ってみて 最後に1年以上Docker ComposeとDev Containersによる開発を行ってみたメリット・デメリットをまとめてみます。 メリット 環境によって起こるエラーに悩まされなくなった メンバージョイン時の負担が少ない PC交換時にもすぐ開発を始められる DB接続状態での自動テストを考慮に入れられるようになった やはり環境の可搬性・再現性という面が大きいです。git cloneしてVSCodeを開くだけで環境が出来上がるので、細かくメンテしていた構築手順書が不要になりましたし、新メンバーが来ても「とりあえず動かしてみて!」ができるようになりました。CI上でもDocker Compose一発なのは変わらないので、テストの幅も広がっています。 デメリット Dockerを動かす分のオーバーヘッドがある Windowsだとファイル更新検知ができない 一方で性能面での問題はどうしてもあり、MacbookだとM1 mac以降は問題ないのですが、Intel macだと正直厳しいと感じる時が多かったです。Docker for mac特有のI/O性能問題で遅いと感じることもあります。 またDocker for Windows特有の問題として、ファイル更新(inotify)がWindows – コンテナ間で共有されないという問題があります。ホットリロードが効かなくなるなどの弊害があるため、Windowsユーザが多い環境では注意が必要です。 まとめ 以上2回にわたってコンテナによるローカル開発環境を作る話でした。 デメリットも挙げましたが、環境構築に迷わなくなる利点は大きく、周りのプロジェクトでもコンテナベースの開発環境が浸透していっています。私個人としてもHomebrewやasdfの使い方を忘れるくらいに利用しています。 本番でコンテナを使っていなくとも、ローカル開発環境だけでも十分にメリットはあるので、皆さんも導入してみてはいかがでしょうか。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 17日目の記事です。 こんにちは。会員システムグループでエンジニアをしている山田です。 私が担当するシステムでは、コンテナベースでの開発環境を整えて開発を行っています。その内容を社内レポートにまとめたりしていたりもするのですが、プライベートでも確認したいという声が社内から挙がったので、出せる範囲で公開しようと思います。 内容が長いので2回に分けての記事となります。 コンテナで開発するモチベーション システム開発を行う際、ローカルPC上でコーディングを行い、それをなんらかの方法で本番環境にデプロイする、というのがよくあるフローかと思います。デプロイがCI/CDで自動化されているかいないか、などの違いはあれど、大きな流れとしては同じでしょう。 この際、ローカルPC上に直接開発環境をセットアップする場合には色々な問題がありました。 構築・起動が面倒 複数のツールのインストールが必要 アプリ本体の他に、別途DBの起動や初期化などを要する 人による環境差分 人による手順違い、導入時期などによる差異 PC上にインストールしている他の物の影響 CI環境とローカル環境の環境差分 複数プロジェクト兼任時の干渉 同一ミドルウェアのバージョン・パッケージ違いの併存 同一DBへのデータ同居 干渉については、 anyenv ・ asdf などのランタイム管理ツールや、言語ごとの環境分離機構(Pythonのvenvなど)で軽減することが可能です。しかし経験上、これらツールの操作手順は継続的にメンテされないことが多く、数年後に新メンバーがジョインした時などに問題を起こしがちです。 これらを解決するには、 一発で起動でき ・ 差分なく再現可能で ・ プロジェクトごとに独立した 開発環境が必要になります。これをコンテナ(Docker Compose)によって実現しようという話になります。 事前の注意 環境を作る前に、いくつか注意ポイントがあります。 本番環境とのコンテナイメージ統一を目指さない コンテナの利点の1つとしてどんな環境でも同じコンテナイメージが動く、というものがありますが、開発環境においてはこれを意識しない方が良いと思っています。 本番用イメージと開発用イメージでは、以下のように求めるものが全く異なります。 本番用イメージ 極力イメージサイズを抑え、最小限のものしか入れない、なんならログインシェルすら不要 実行ファイルはイメージ内に内蔵する 開発用イメージ Git、各種ターミナルコマンド、デバッグツールなど多種のツールが入っていて欲しい 実行ファイルはイメージ内に不要、ソースコードをディレクトリごとマウントする 強引にイメージを統一しようとすると開発が辛くなるので、独立したイメージとして管理した方が現実的です。 # 本番用 FROM node:18.19-alpine ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile COPY . . RUN yarn build CMD yarn start # 開発用 FROM node:18.19-bookworm RUN apt-get install -y git vim Apple Sillicon搭載macの場合 Apple M1などのCPUはarm64アーキテクチャを採用しています。最近のイメージはarm64対応していることが多いですが、使用している言語バージョンが古いなどの場合、x86_64イメージしかない場合があります。この場合でもRosettaによるエミュレーションで動かせることも多いですが、動作しないこともあるのでご注意ください。 Docker Composeの構成 実際に環境を作っていくにあたり、ソースコードリポジトリをモノレポとするか、ポリレポとするかで方針が異なってきます。どちらを選ぶかは運用体制やシステム規模によりますが、いずれにしても docker compose up docker compose down のみで開発環境を起動・停止し、デバッグできるようにします。 モノレポの場合 あるシステムを構成する 全てのサブシステムをローカルで起動しちゃおうぜ! という方式です。Docker Compose一発でシステムの全てを再現することを目指します。 この場合、リポジトリは1つになり、サブディレクトリにサブシステムが並ぶモノレポ構成になります。 repository/ - frontend/ - Dockerfile - src/ - test/ - backend/ - Dockerfile - src/ - test/ - mock-ext-api/ - Dockerfile - src/ - test/ - compose.yml compose.ymlは全てのアプリケーションやDB、モックなどを起動するように設定します。 version: '3' services: frontend: build: context: frontend volumes: - ./frontend:/usr/src/app ports: - 3000:3000 backend: build: context: backend volumes: - ./backend:/usr/src/app ports: - 3080:3000 mock-ext-api: build: context: mock-ext-api volumes: - ./mock-ext-api:/usr/src/app database: image: mysql volumes: - database-volume:/var/lib/mysql volumes: database-volume: 動作イメージは以下のようになります。 モノレポ構成 Docker Compose上でサブシステムが全て起動している状態で、各コンテナに対してディレクトリをマウントすることによりソースコードを同期します。 開発に必要な言語ランタイム、ツールなどは全てDockerfileで定義しておき、ローカル環境には依存しないようにします。 コンテナ間での通信はコンテナ間通信で行い、基本的にlocalhostを経由しません。各コンテナのホスト名はDocker Composeに定義したserviceの名前(上記ではfrontend, backend, …etc)で解決されるので、これらを指定するようにします。ローカルPC側から確認したいポートのみローカルPC側に公開します。 メリット 複数サブシステム間の結合動作が容易に確認できる CI上での実行も容易 Docker Compose内でほぼ全ての処理が完結するため、再現性が高い 同一DBを複数サブシステムで共用するような環境にも対応できる デメリット システム規模が大きい場合、マシンスペックを大きく消費するため現実的でない 外部システムへの依存度が高い場合、メリットが薄くなる モックなどを使ってローカル完結させることもできるが、動作の信頼性は下がる リポジトリをモノレポ構成にできない場合がある サブシステムごとに担当者が異なる場合 CI/CDツールの発火条件をディレクトリ単位で制限できない場合 同一チームで複数サブシステムを複数運用していて、サブシステム間の結合処理が重要な場合に有効な構成です。 ポリレポの場合 開発したいシステムだけ起動しようぜ! という方式です。この場合は1リポジトリ1サブシステムのポリレポ構成となります。 repository/ - src/ - test/ - Dockerfile - compose.yml compose.ymlにはアプリコンテナは開発対象の1つのみとなり、その他は直接使用するDBなどのみが記載されることになります。 version: '3' services: backend: build: context: backend volumes: - ./backend:/usr/src/app ports: - 3080:3000 database: image: mysql volumes: - database-volume:/var/lib/mysql volumes: database-volume: 動作イメージは以下のようになります。 ポリレポ構成 アプリコンテナと必須のDBコンテナなど最低限のコンテナのみが起動した状態になります。別のサブシステムはDocker Composeの外にあるため、結合動作の確認は諦めるか、複数のDocker Composeの起動で対応します(後述)。 メリット 最低限のコンテナ起動で済むので、マシンスペックの消費が少ない リポジトリ管理上の制約が少ない デメリット サブシステム間の結合動作の確認が難しい 特にCI上で確認しようとすると、手順が複雑になりがち システムが大規模で全サブシステムの起動が困難であったり、担当者が分かれているような場合はこちらを採用することになります。 応用設定 以上で基本的なDocker Composeの環境は用意できましたが、実際に使っていくには追加の修正が必要です。 書き込みの多いディレクトリをVolumeにする Dockerはイメージとして保存することを前提としたファイルシステムを採用しています。このため、コンテナ内への書き込みは著しく遅くなります。 書き込みが多いと思われる 言語ごとのパッケージディレクトリ node_modulesなど ビルド結果の出力先ディレクトリ キャッシュディレクトリ DBコンテナのデータディレクトリ などはDocker Volumeとして切り出しましょう。コンテナを再作成したとしてもデータが消えなくなるという利点もあります。 version: '3' services: frontend: volumes: ... - node_modules:/usr/src/app/node_modules ... volumes: node_modules: (macOS限定) cached/delegatedオプションの活用 macOSではホストディレクトリをマウントした場合、I/O性能が著しく落ちるという問題が知られています。最近では virtiofsの採用 などで高速化を図っているとはいえ、依然として遅い状態は変わっていません。 マウント時のオプションを指定することで、ホスト-コンテナ間の整合性の担保を一部省略し、多少高速化するので設定しておきましょう。オプションは2つあり、 cached: ホストを信頼し、コンテナ側への反映が遅延することを許容 delegated: コンテナ側を信頼し、ホスト側への反映が遅延することを許容 の違いがあります。ホスト側でコーディングするならcachedを、コンテナ側でコーディングするならdelegatedを設定するようにしましょう。 services: backend: volumes: - ./backend:/usr/src/app:delegated // コンテナに入ってコーディングする場合 ... またそもそも、ホスト-コンテナ間で同期の必要がないディレクトリはDocker Volumeによるマウントにしましょう。DBのデータディレクトリまでホストディレクトリのマウントで行っているような例をたまに見かけますが、I/O性能が大きく悪化するので避けるべきです。 本番・開発間での設定共有 本番用と開発用でDockerイメージを分けるという話をしましたが、「それでも本番用イメージをビルドして動作確認をしたい」という要望はあります。このような場合はDocker Composeの統合機能を利用します。 docker composeコマンドは docker compose -f a.yml -f b.yml のように、fオプションで複数のファイルを指定できます。これは後続のファイルが前のファイルを上書きしていく、という挙動をします。 これを利用し、本番相当のcompose.ymlを用意しておいて # compose.yml services: frontend: build: context: frontend ports: - 3000:3000 開発用に上書きしたい部分だけ別ファイルを作ります。 # compose-dev.yml services: frontend: build: dockerfile: Dockerfile.dev volumes: - ./frontend:/usr/src/app この2ファイルを先の上書き挙動によってマージすると、以下のような結果が得られるはずです。 services: frontend: build: context: frontend dockerfile: Dockerfile.dev ports: - 3000:3000 volumes: - ./frontend:/usr/src/app こうすることで、 docker compose up とだけ指定した場合には本番用イメージで起動し、開発時には docker compose -f compose.yml -f compose-dev.yml up として上書きすることで、開発用Dockerfileやホストディレクトリのマウントを使う設定にできます。 ポリレポでの結合動作 ポリレポでは結合動作の確認が難しいという話をしましたが、一工夫することで結合動作の確認が可能です。 docker composeは何も設定しない場合、compose.ymlごとに別々のDockerネットワークを作成して環境を分離します。一方、ネットワークを明示的に指定することも可能です。 services: frontend: networks: - <ネットワーク名> networks: <ネットワーク名>: external: true として外部ネットワークを指定します。「<ネットワーク名>」の部分はネットワークを共用したいサブシステム全てで共通の名前を使用してください。こうしておいて docker network create <network> (サブシステムAで)docker compose up (サブシステムBで)docker compose up ... として順次コンテナを起動していくことで、compose.ymlが分かれていても同一ネットワーク内で起動し、コンテナ間通信ができるようになります。 この場合の注意点です。 最初に一度必ず docker network create < ネットワーク名 > を実行する必要がある 全てのコンテナにnetworksの指定を行う必要がある 複数コンテナから同一DBにアクセスするなど、共通リソースがある場合の再現は難しい 単独動作か結合動作、どちらかの確認を諦めなければならない まとめ 以上、Docker Composeによるローカル開発環境の構築でした。リポジトリ構成をどうするかという決めの問題がありますが、これ以外はDocker Composeの基本に則った内容かと思います。 これでコンテナ実行環境は整ったのですが、コーディングを行う上ではまだまだ課題があります。 次の記事 ではDev Containersを利用したコーディング環境の構築についてご紹介します。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 9日目の記事です。 こんにちは。会員システムグループの三浦です。とあるプロダクトの開発チームリーダーを担当しています。 今回は、チームで行ったふりかえりで特にやって良かったと感じた、フォースフィールドアナリシスについて紹介します。 チーム背景の詳細についてもある程度書いています。ぜひあなたのチームが同じ状況に陥っていないか、その時どうすればいいのか、参考にしていただければと思います。 ふりかえりをする前のチームの現状 私たちのチームは、開発メンバーが自分を含め4人。当時、新規プロダクトのWEBシステム開発にアサインしていました。 元々の見積もりでは開発開始から約1ヶ月程度経ったタイミングで「そろそろWEB、API、データベースの最低限の機能は構築して、開発用のインフラも作ってみて、申込は試せるかな?」くらいのペースだったのですが・・・。 実際にはようやく1つ目のAPIがローカルで動くかという頃で、インフラ構築も、WEB画面も用意できませんでした。 この進捗遅れについてはバーンダウンチャートを作っていたおかげで気づくことができました。 青色の線が順調に作業ポイントを消化できていた場合の線、赤色が自分たちの消化した作業ポイントの線です どうみても青色の線と赤色の線が乖離しています。消化予定のポイントとはかなりの乖離がありました。明確に見積もりから進捗が遅れている状況です。 ふりかえりの場を作る さて、バーンダウンチャートによってチーム内で「これは遅れているという状況なんじゃないか?」という認識ができたところで、チームのふりかえりの時間になりました。 メンバーは皆なんとかして遅れを取り戻さなければ!と思っていましたが、こういった悪い状況を改善するアクティビティをやる前には、チーム全員でとある認識が必要です。 ふりかえりでは課題に対しての改善を探しますが、その時に「もっとこうしていれば」という発想がどうしても生まれます。 この発想のよくないところは、他人に対しても自分に対しても、過去の原因に目を向けさせてしまうところです。私たちは今開発の遅れを取り戻したいと考えており、それは未来の話なのです。 まず最初に読み上げたのは、森 一樹氏が作成・公開された「 ふりかえりカタログ 」の1番目の項目にもある「Norm Kerthの最優先指令」です。 どんな道をたどったにせよ、 当時の知識・技術・能力・利用可能な リソース・状況の中で、みんなができる 限り最高の仕事をしたはずです。 それを心から信じます。 その後、「非難をしないこと」というルールに賛同できるかを全員に問いました。 儀式的なものに感じられるかもしれないですが、この確認を行うことで、課題に対する原因探しから未来を改善させることに意識を集中できるようになります。 フォースフィールドアナリシスの実施 振り返りをやる場を設定したら、次にメインのフォースフィールドアナリシスを行います。 フォースフィールドアナリシスは、チームが置かれている現状、課題に対して何が改善の要因となるのか、何が抑制の要因となるのかを列挙して分析する手法です。 列挙した要因は矢印の太さによってその要因の強さを相対的に表現し、どうすればより改善の要因を強め、どうすればより抑制の要因を弱めることができるのかを考えます。 実践した手順は以下の通りです。かける時間についても設定していますが、多少のブレはあります。全体の時間は40~50分程度ですが、余裕を持って1時間確保してもよいでしょう。 テーマ(現状)を決定(3min) この場で分析したい現状を決める。 改善を促進する要因を書き出す(8min) この現状を改善したい、となったときに、現状を変えるようなきっかけ、要因、行動、理由を書き出す。 書き出した要因の重みづけ(4min) 書き出した要因に対して、どれがより効果が高いかを上から順に並べてみる。 改善を抑制してしまう要因を書き出す(3min) この現状を改善したい、となったときに、現状を維持、悪化させてしまう要因、行動、理由を書き出す 書き出した要因の重みづけ(4min) 書き出した要因に対して、どれがより影響が大きいかを並べてみる。 最も促進する要因・抑制してしまう要因を改善するアクションのアイデアだし(13min) 改善を促す要因については、それをどうすれば引き起こすことができるか 改善を抑制する要因については、それをどうすれば止めることができるか アクションの決定(5min) 出てきたアクションに対してドット投票を行い、次のアクションを決める 私たちのチームが書き上げたフォースフィールドアナリシスは以下の通りです。ツールはGoogle Jamboardを使用しました。 全体の進捗が遅れていることを改善すべき現状と置き、まずはそれに対しての改善要因と抑制要因をカードで列挙します。その後、緑やオレンジのカードで出てきた要因をさらっと確認しました。 太さの違う矢印を引いていくことでどの要因が強いのか、どの要因が弱いのかを可視化するのが本来の手順なのですが、カードで列挙したため、カードを上から影響度が強い順で並べてみました。 すると複数のカードが同じ「知らないこと・不確定なこと」に関する要因になっていることがわかってきます。 今回では、開発が始まってから分かる不確定な要素が多く、それを出てくるたびに再検討し直してチームで合意を取る、というのを繰り返していて、それによって開発の進捗が遅れていた・・・ということがアクティビティで浮かび上がりました。 そのため、不確定要素の多さを強い要因としてグルーピングし、それに対する改善行動をピンクのカードで深掘りしていくようにします。 ふりかえりでは実現確率を高めるためになるべく具体的な行動を1つ決めることを目指します。 今回は、私たちが忙しさを理由に出来ていなかったリファインメント(プロダクトバックログアイテムに対する見直し)を実施し、タスクのゴールや詳細を詰めることを決めました。 時間についても2時間を制限とし、次のプランニングから早速組み込みました。 ふりかえりの効果 リファインメントを実施することにより、開発チーム内にあった知識の偏りがある程度解消され、メンバーそれぞれが主体的に実装方法やテストを検討することができるようになりました。またプロダクトバックログアイテムに詳細な情報が集約されるようになり、作業量の見積もり制度も向上しました。 結果的に、元々見積もっていた作業量からはタスクの詳細化によってさらに作業量が増加しましたが、その詳細をチームメンバーが把握できていることによる案件全体のポイント消化効率が上がったことにより、チームの生産性は確実に上がりました。 青色の計画を示す線の角度がなだらかになっているのは作業量が増加している兆候ですが、赤色の実作業の線がその作業量を超える角度で緩やかになっているため、良い兆候が見えるようになりました。 ただこれだけで全てが良くなるというものでもなく、実際にはこの後にもステークホルダーとのコミュニケーション不足による手戻りや突発的な他案件との調整など、さまざまな課題に立ち向かうことになるのですがそれはまた今度の話とします。 さいごに チーム開発はうまくいかない場面も多々ありますが、それを乗り越えるのもまたチームです。課題のないチームなどないですが、少しずつ行動を変えていくことでチームは成長していける、それを改めて感じられる出来事でした。ブログを読んでいる皆様のチームも多かれ少なかれ課題があると思いますが、こうした解決事例を参考にして、よりよいチーム開発を作っていただければと思います。 ニフティではスクラム開発が主な開発手法として浸透しています。こうしたチーム活動などに興味のある方に対して技法を教え合うような場も設定されていますし、手助けができる環境です。こうした事例に興味を持った方がいらっしゃいましたら、ぜひ以下のリンクからお気軽にご連絡ください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 16日目の記事です。 InnerSource Commons #11 にて、ニフティでのインナーソースを導入期の事例紹介し、ディスカッションパートでは、全社展開に向けての悩んでいることについて話しました。 12/11 InnerSource Commons #11にて当社エンジニアが登壇いたします 本記事の内容 InnerSource Commons Japanのイベント ニフティの事例紹介 事例紹介の内容で参加者の方と話したこと 社内展開に向けて悩んでいることの共有 他の会社はどのように進めたのか? 成熟度モデルは誰がどの単位で自己評価していくか? メインストリームの開発業務とのバランスをどう考えるか? 感想 こんにちは!会員・認証基盤システムの開発・運用をしている基幹システムグループの小松です。 InnerSource Commons #11 の発表したことやディスカッションした内容を一部紹介します。 ディスカッションパートではとても勉強になる情報を教えていただきました! InnerSource Commons Japanのイベント connpassにてInnerSource Commons Japanのイベント が開かれています。 まだ日本の事例は少ないので、有識者の方と直接お話ができる有意義なイベントとなっています。 アーカイブも YouTube にて公開されています。 2024年度からは、コミュニティ内の繋がりを活発にするためオフラインの開催も増えるらしいです。 ニフティの事例紹介 InnerSource Commons #11 ではまずニフティから、インナーソース導入に向け今までやったことの概要、インナーソース実験プロジェクトの結果と全エンジニアに向けたアンケートの結果を紹介しました。 ブログ記事( インナーソースを導入してみた その① お試し導入編 )にアンケート結果など書かれているので気になる方はご覧ください。 登壇時に発表した資料は以下です。 発表パートはアーカイブに残っています。 事例紹介の内容で参加者の方と話したこと ブログの内容も含め取り組みに感動したと言うフィードバックをいただけて嬉しかったです。 日本ではまだ事例が少ないので、ニフティとしても取り組み内容など情報発信をしていきたいと思います。 「インナーソースを始めたモチベーションは、やはりインナーソースのような開発の必要性を感じているのが大きいか?」という質問が来ました。 ニフティとしては必要性を感じています。 私と芦川が所属している基幹システムグループでは、共通基盤となるシステムが多くあるため、今後の開発業務で、共通基盤で必要となる修正がボトルネックになることを避けるためにもインナーソースは必要だと思っています。 「実験プロジェクトに参加した人数に比べて、アンケートの内容はポジティブな人が多かった。実際の社内の雰囲気はどうだったのか?」という質問が来ました。 2ヶ月の実験プロジェクト(社内ツール1リポジトリをインナーソース化)で約150人エンジニアがいるなかIssueをアサインした人が7名でした。 しかしアンケートは43名が回答してくれ、全社展開に賛成が76.7%、中立が23.3%、反対は0%でした。 メリットについても多くの方が記入してくれました。 実際、実験プロジェクトを始めますとエンジニア全体会で発表したときも、「〇〇リポジトリをインナーソース化したい!」といったフィードバックが何件か来ていたので、メリットは伝わっていてインナーソースに対してポジティブな人が多いと感じていました。 良い取り組みにポジティブな反応をしてくれるニフティのエンジニアの雰囲気もあると思います。 実験プロジェクトの参加者が少なかったのは、メインストリームの開発業務とのバランスがネックになっていたと思います。 これについては後述する悩みにも入っています。 社内展開に向けて悩んでいることの共有 現在インナーソースガイドラインやインナーソースポータルを公開し、いくつかインナーソースリポジトリが徐々に増えてきている段階です。 全社展開に向けての悩んでいることを共有し、ディスカッションさせていただきました。 他の会社はどのように進めたのか? 悩みの背景 ニフティの規模に近いサンプルがあるのか 組織の人数感、文化、B2B orB2C、など 浸透速度なども知りたい 社内に普及するためのコツはありますか? ニフティではトップダウンでなくボトムアップで進めている 導入にはポジティブな雰囲気だが、スピード感が早いとは言えない状況 ディスカッションした内容 まずニフティの規模(エンジニア約150名)に近いサンプルは日本にはない。 ヨーロッパの方には近しいものがあるが、日本ではまだ知られない会社が多い。ソフトウェアベンダーの会社が多い。 社内に進めるスピード感について。 ここまでの流れは世界的にみても平均よりは早い方。 ただし更に社内展開していくところから色々な課題がでてくるので、ここからの方が重要でスピード感が求められる。 もともとインナーソースに近い活動をすでにしていた組織ではインナーソース普及が早いとのことでした。 ニフティはチームを超えて協力しあう雰囲気はすでにあったので、ここまでは順調に来れたのではないかと思います。 全社展開のコツ。 やはり人が多くなると時間もかかり、1年かかるところもあれば2,3年くらいかかるところもある。 社内で引っ張っていく人を1、2人を決めるのがよい。 元々OSSコントリビュートをやっていた人がなるケースがある。 成熟度モデル は誰がどの単位で自己評価していくか? 悩みの背景 例えば、透明性(プランや開発プロセス、ツール、意志決定など)は各プロダクト単位で自己評価していくとよさそうだが、ガバナンス(リワード、カルチャーなど)は全社的にインナーソースオフィスが評価すべきか? ディスカッションした内容 あくまでベンチマークなので、組織的な課題と突合させて判断する。 成熟度モデルに関しては、誰のためのアセスメントなのかが重要。 単純に数値を上げることに注力しては意味がない。 自分たちの課題を見つけてレベルアップするために使うもの。 第三者含め、背景なども踏まえ議論しながら使うのがよい。 最初から全ての項目を測る必要はない。 事例として全てをチェックしたらどの項目も最低値になり、結局どこから手を付けるのかがわからなることがあった。 まず導入期は細かいプロダクト単位で測っていくのがよい。 徐々に案件や組織ベースで測っていくのがよいとのこと。 アンケートなどで課題や目指す姿を聞いてみるの効果的。 メインストリームの開発業務とのバランスをどう考えるか? 悩みの背景 そもそもインナーソースは余力時間にやるものなのか 例えば、スクラムで開発をしている場合で、期待する成果物をより早くデプロイできる場合、プロダクトバックログアイテムに他リポジトリへのコントリビュートをすることも視野にいれていくのか スクラムをやっているチームはどのようにインナーソースを取り入れているか ディスカッションした内容 まずボトムアップで組織にインストールする場合、良いカルチャーがあるとこの活動いいね、やりたいまではすぐ行くが、実際の業務で効率が上がったと認識するまでにはハードルが高い。 メインストリームの開発とのバランスを考えるよりもインナーソースを使って生産性を上げるという考え方への変換が必要になりそう。 余力時間を会社が認めて上げることも必要。 コントリビュートなどを試してみる時間の確保(Googleの20%ルールなど)や評価制度などのサポートがあるとよい。 実際のコントリビュートは余力時間以上のコストがかかるのでサポートが必要。 評価システムによって導入が難航している事例が多い。 とある事例では、コントリビューターを引き付けるようなものがあるとサイクルが回ったとのこと。 コントビュートされる側が一緒にやりましょうと呼びかけ、コントビュートする側もコントビュートされる側もプロダクトが良くなって、より多くの人が使われるようになり評価もされるとのこと。 スクラムとの兼ね合いについて。 スクラムはスプリントで期限が決まっているので、インナーソースと同期できないので単一のスクラムチームでは厳しい。 Large-Scale Scrum (LeSS) とインナーソースの相性はいいかもしれない。 スプリントゴールを達成するために、他チームのリポジトリに手を入れる必要がある場合が発生するため。 感想 まだニフティは導入始めたばかりでインナーソース文化の定着まではできていませんが、InnerSource Commons で事例紹介をさせていただく機会をいただけて嬉しかったです。 とても有意義なイベントでした。 まだ日本での事例が少ないので、情報発信をしつつ社内の普及活動を続けていきたいです。 悩みの部分に関してもいい情報をもらえたので良かったです。 成熟度モデルの評価はまずはできるところから検討してやっていこうと思います。 今回の話を聞かなかったら、とりあえず全部計測してみるというアンチパターンをやってしまいそうでした。 コントリビュートをサポートの仕組みなども検討していきます。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
こんにちは。ニフティの浅見( @rubihiko )です。 ニフティでは毎年エンジニアの新人研修を先輩エンジニアが 内製 で行う文化があります(通称、 エンジニア定例 と呼ばれています)。 今年から、資料を一般公開いたします(一部公開出来ない資料もございます)。 公開時期が遅くなってしまったことについて補足しますと、公開する取り組みが今年度が初めてで、資料を公開用に手直しなど諸々作業が発生したためです。 来年度以降はスムーズに公開できるよう調整していきたいと思います。 特徴 基礎学習とチーム開発を経験しながら研修は進んでいきます。 新人は来年は講師として教える側になるという特徴があります。 また、毎年内製研修の内容を改善していて、新しいテーマが研修内容に加わるのも特徴です。 2023年度はプロダクティビティが加わりました。 研修では以下の点を重視しています。 ニフティの開発基礎を学ぶ チーム開発を経験する サービス運用を意識・経験する 来年は教える立場だと意識する 生徒→講師による継続的な新人研修の運用 目標 基本的なWebサービスの開発・運用に関する知識とチーム開発を身につけます。 期間は、4月後半〜6月中旬にかけて行います。 ニフティでは、6月末頃からOJTが始まります。それまでにニフティにおける開発運用(+α)の基本を身につけることが目標です。 他の研修もあるので、新人はインプットが多くなり大変な時期です。 今年のテーマは以下 開発の基礎 フロントエンド バックエンド データベース Webフレームワーク セキュリティ コンテナ Git AWS オブジェクト指向 機械学習 スクラム SRE スマートフォンアプリ開発 バックボーンネットワーク [NEW] プロダクティビティ 研修資料一覧 講義の内容は、座学パートと演習パートに分かれており、基本からしっかり学べます。 演習パートは個人作業だったりグループ作業だったり講義によって特徴がでます。 開発の基礎 Webフレームワーク フロントエンド(Speaker Deck) バックエンド(Speaker Deck) データベース データベース基礎(Speaker Deck) セキュリティ セキュリティ入門(Speaker Deck) コンテナ コンテナの基礎(Notion) Git 初めてのバージョン管理(Speaker Deck) AWS AWS基礎(Speaker Deck) オブジェクト指向 機械学習 機械学習の基礎(Notion) スクラム SRE SRE実践(Notion) スマートフォンアプリ開発 Androidアプリ開発(Notion) バックボーンネットワーク バックボーンネットワーク(Notion) プロダクティビティ プロダクティビティ(Notion) 最後に ニフティの内製研修の歴史は長く、資料アップデートや新規講義の追加、今年の新人は来年の講師、といったことが継続的に行われてきました。新人だけでなく、2年目以降のメンバーの成長にもつながるよい文化ができていると思います。 教育面で力をいれており、毎年充実したカリキュラムを考えています。今後も新人研修の様子や資料を公開していこうと思いますので、ブログを確認市ていただければ幸いです。 We are hiring! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 15日目の記事です。 はじめに こんにちは SREチームの @rubihiko です。 CodeWhispererですが、最近コマンドラインツールにも対応したので試してみたいと思います。 CodeWhispererは2つのTierで提供されており、今回は個人利用の方法について試します。 詳しい料金体系はこちらを確認してください。 https://aws.amazon.com/jp/codewhisperer/pricing/ Amazon CodeWhispererについて https://aws.amazon.com/jp/codewhisperer/ IDE とコマンドラインのための AI 搭載生産性向上ツール Amazon CodeWhisperer は、コメントと既存のコードに基づいて、スニペットから完全な関数まで、さまざまなコードの提案を IDE 上でリアルタイムで生成します。また、コマンドラインでの CLI 補完や自然言語から bash への翻訳もサポートしています。 CLIのセットアップ https://docs.aws.amazon.com/codewhisperer/latest/userguide/command-line.html インストール こちらからダウンロードしてインストールします https://docs.aws.amazon.com/codewhisperer/latest/userguide/command-line-getting-started-installing.html 認証情報の設定 個人で利用する場合は、Builder IDで認証を行います ※Builder IDの取得はこちらから https://docs.aws.amazon.com/signin/latest/userguide/sign-in-aws_builder_id.html あとは指示にした以外インストールを完了させます。 以下の項目を有効にしてください。 shell integrations Enable accessibility サポートされているコマンドライン環境 現時点では以下が対応しています Operating systems: macOS Shells: bash, zsh, fish Terminal emulators: iTerm2, macOS terminal, Hyper, Alacritty, Kitty, wezTerm IDEs: VS Code terminal, Jetbrains terminals (except Fleet) CLIs: 500+ of the most popular CLIs such as git, aws, docker, npm, yarn 補完機能 AWS CLIはもちろんですが、gitやdockerなどのコマンドにも対応しています 自然言語からコマンドを生成 以下のコマンドで自然言語からコマンドを生成することができます cw ai cw ai prompt # promt # で呼び出さないよう無効にすることもできます ファイルをs3にコピーする aws s3 cp $FILENAME s3://$BUCKET_NAME/$FILENAME 起動中のEC2インスタンスの一覧を取得する aws ec2 describe-instances --query 'Reservations[*].Instances[*].{InstanceId:InstanceId,State:State.Name,PublicIP:PublicIpAddress,PrivateIP:PrivateIpAddress}' --output table 問題なければそのまま実行できますし、修正も可能です。 危険なコマンドの場合は警告が出ます 感想 いかがでしょうか。 今回はCLIにフォーカスして試してみましたが、自然言語からコマンドを生成できるのはかなり便利に感じます。 よく使うものであれば、そのまま alias に登録してしまうのも良いかもしれません。 また、個人で利用する分には無料なもの嬉しいポイントです。 参考 CodeWhisperer for command line Introducing Amazon CodeWhisperer for command line AWS re:Invent 2023 – Best practices for Amazon CodeWhisperer (DOP201) ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 15日目の記事です。 はじめに ニフティ株式会社の島田です。 以前ご紹介したAWS Chatbotの送信内容をカスタムする方法がCustom notificationsとして正式に提供されたため、使ってみました。 EventBridge+ChatbotでECSタスクの状態をslackに通知してみた ついでにAmazon CloudWatch Logsから特定のワード発生を検知し、slackに通知する仕組みをAWS Lambdaを使わずに実現したので紹介します。 一般的なログ検知 一般的なログ検知、通知の手法として、以下のような構成があると思います。 登場するリソースは以下です。 Amazon EC2 (以降EC2) / Amazon ECS (以降ECS) これらはアプリケーションが起動しているコンピューティングを表しています Amazon CloudWatch (以降CloudWatch) / Amazon CloudWatch Logs (以降CloudWatch Logs) AWS Lambda (以降Lambda) コンピューティング環境から収集したログをCloudWatch Logsに連携し、サブスクリプションフィルターで検知したログをLambdaに連携、イベントをパースしてslackに連携します。 ノーコードの構成 今回は以下の構成でノーコードを実現しました。 新たに登場するリソースは以下です。 Amazon Kinesis Data Firehose (以降Kinesis Firehose) Amazon S3 (以降S3) Amazon EventBridge (以降EventBridge) Amazon SNS (以降SNS) AWS Chatbot (以降Chatbot) リソースは増えますが、Lambdaを使わずにslack通知を実現しています。 ちょっとしたハマりポイントもあるので順を追って説明します。 CloudWatch → Kinesis Firehose → S3 まず、対象ログを配置するS3を作成します。 次に、S3をターゲットにしたストリームを作成します。このとき、S3にアクセス可能な権限を付与する必要があります。 最後にCloudWatch Logsの対象のロググループにサブスクリプションフィルターを設定します。 送信先としてKinesis Firehoseを選択し、Kinesis Firehoseへのアクセスを許可します。 これらをTerraform化したサンプルです。 # S3 resource "aws_s3_bucket" "main" { bucket = "logs" } # Firehose resource "aws_kinesis_firehose_delivery_stream" "firehose" { name = "subscriptionfilter-stream" destination = "extended_s3" extended_s3_configuration { role_arn = aws_iam_role.firehose.arn bucket_arn = aws_s3_bucket.main.arn prefix = "subscriptionfilter/" error_output_prefix = "subscriptionfilter/error/" buffering_interval = 60 compression_format = "GZIP" } } resource "aws_iam_role" "firehose" { name = "FirehoseRole" assume_role_policy = jsonencode({ "Version" : "2008-10-17", "Statement" : { "Effect" : "Allow", "Principal" : { "Service" : "firehose.amazonaws.com" }, "Action" : "sts:AssumeRole" } }) } resource "aws_iam_policy" "firehose" { name = "FirehosePolicy" policy = jsonencode({ "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Action" : [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject" ], "Resource" : [ "${aws_s3_bucket.main.arn}", "${aws_s3_bucket.main.arn}/*" ] } ] }) } resource "aws_iam_role_policy_attachment" "firehose" { role = aws_iam_role.firehose.name policy_arn = aws_iam_policy.firehose.arn } # CloudWatch Logs resource "aws_cloudwatch_log_subscription_filter" "filter" { name = "filter" role_arn = aws_iam_role.filter.arn log_group_name = ${var.log_group_name} filter_pattern = "?ERROR ?Error" destination_arn = aws_kinesis_firehose_delivery_stream.firehose.arn distribution = "Random" } resource "aws_iam_role" "filter" { name = "LogFilterRole" assume_role_policy = jsonencode({ "Version" : "2008-10-17", "Statement" : { "Effect" : "Allow", "Principal" : { "Service" : "logs.ap-northeast-1.amazonaws.com" }, "Action" : "sts:AssumeRole", "Condition" : { "StringLike" : { "aws:SourceArn" : "arn:aws:logs:ap-northeast-1:${var.account_id}:*" } } } }) } resource "aws_iam_policy" "filter" { name = "LogFilterPolicy" policy = jsonencode({ "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Action" : ["firehose:PutRecord"], "Resource" : ["${aws_kinesis_firehose_delivery_stream.firehose.arn}"] } ] }) } resource "aws_iam_role_policy_attachment" "filter" { role = aws_iam_role.filter.name policy_arn = aws_iam_policy.filter.arn } これでサブスクリプションフィルターの対象のログをS3に保存することができるようになりました。 S3 → EventBridge 作成したS3のイベントをEventBridgeから取得できるようにします。 これをTerraform化したサンプルです。 # S3 resource "aws_s3_bucket_notification" "main" { bucket = aws_s3_bucket.main.id eventbridge = true } # EventBridge resource "aws_cloudwatch_event_rule" "event" { name = "event" description = "object created event" is_enabled = true event_pattern = jsonencode({ "source" : ["aws.s3"] "detail-type" : [{ "prefix" : "Object Created" }], "detail" : { "bucket" : { "name" : ["${aws_s3_bucket.main.bucket}"] } } }) } S3のオプションでcreateイベントを送信することもできるのですが、この後Chatbotにわたす際にイベントを編集したいのでこの方法を使っています。 また、Object Createdイベントは完全一致でなくprefixとして指定する必要があります。 EventBridge → SNS イベント送信先のSNSトピックを作成します。このとき、SNSのリソースベースポリシーとしてEventBridgeを許可する必要があります。 また、EventBridgeで入力を変換し、ChatbotのCustom notificationsの構造を作って送信します。 これらをTerraform化したサンプルです。 # EventBridge resource "aws_cloudwatch_event_target" "event" { rule = aws_cloudwatch_event_rule.event.name arn = aws_sns_topic.sns.arn input_transformer { input_paths = { source = "$.source", id = "$.id", bucket = "$.detail.bucket.name", key = "$.detail.object.key" } input_template = <<EOF { "version": "1.0", "source": "custom", "id": <id>, "content": { "title": "log detected", "description": "S3 object created event\n - source: `<source>`\n - bucket: `<bucket>`\n - key: `<key>`" } } EOF } } # SNS resource "aws_sns_topic" "sns" { name = "SNS" } resource "aws_sns_topic_policy" "sns" { arn = aws_sns_topic.alert.arn policy = jsonencode({ "Version" : "2008-10-17", "Id" : "__default_policy_ID", "Statement" : [ { "Sid" : "__default_statement_ID", "Effect" : "Allow", "Principal" : { "AWS" : "*" }, "Action" : [ "SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish" ], "Resource" : "${aws_sns_topic.sns.arn}", "Condition" : { "StringEquals" : { "AWS:SourceOwner" : "${var.account_id}" } } }, { "Sid" : "SNS topic policy from EventBridge", "Effect" : "Allow", "Principal" : { "Service" : "events.amazonaws.com" }, "Action" : [ "SNS:Publish" ], "Resource" : "${aws_sns_topic.sns.arn}", "Condition" : { "StringEquals" : { "aws:SourceAccount" : "${var.account_id}" } } } ] }) } リソースベースポリシーはマネジメントコンソールから作成した場合のデフォルトのポリシーを含めています。 SNS → Chatbot Chatbotが利用するロールを先に作成しておきます。 現時点ではTerraformでChatbotを作成できないため、Chatbot自体はマネジメントコンソールから作成します。 これらをTerraform化したサンプルです。 # Chatbot resource "aws_iam_policy" "chatbot" { name = "ChatbotPolicy" description = "AWS Chatbot policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "cloudwatch:GetMetricData", "cloudwatch:ListMetrics", "cloudwatch:ListDashboards" ] Effect = "Allow" Resource = "*" }, ] }) } resource "aws_iam_role" "chatbot" { name = "ChatbotRole" assume_role_policy = jsonencode({ "Version" : "2008-10-17", "Statement" : [ { "Sid" : "", "Effect" : "Allow", "Principal" : { "Service" : "chatbot.amazonaws.com" }, "Action" : "sts:AssumeRole" } ] }) } resource "aws_iam_role_policy_attachment" "chatbot" { role = aws_iam_role.chatbot.name policy_arn = aws_iam_policy.chatbot.arn } 結果 こんな感じで通知が可能になりました。 まとめ Custom notificationsのおかげで、色々なslack通知がChatbotに任せられるようになりました。 今回の構成はLambdaを使った場合と比べて以下のようなメリット/デメリットが考えられます。 メリット コード、Lambdaの管理をしなくて良い ログがS3に保存される デメリット 通知が来るまで若干時間がかかる ログの内容は出力できない 特に今回作った通知ではログの内容を知ることはできないため、広くフィルタを適用する場合などは不向きと考えられます。 それが許容できる場合はLambdaの管理を手放す事ができ、コードを書くことなく実現可能ですので用途に合わせて参考にして頂ければと思います。 明日は、 @sonoha さんの記事です。 お楽しみに! 参考記事 https://aws.amazon.com/jp/about-aws/whats-new/2023/09/custom-notifications-aws-chatbot/ https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ev-mapping-troubleshooting.html https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/confused-deputy.html https://docs.aws.amazon.com/ja_jp/chatbot/latest/adminguide/custom-notifs.html https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ev-events.html ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 14日目の記事です。 はじめに こんにちは。会員システムグループの渡邊です。 最近、あるサイトのリニューアルプロジェクトを担当しており、その中で私はエンジニアですがサービス設計にも携わる機会がありました。 そこでエンジニアがサービス設計に積極的に参加することには多くのメリットを感じたので、今回は紹介していきます。 サービス設計の流れ まず、今回行ったサービス設計は以下の流れで行いました。 既存の課題や成功要因の洗い出し ユーザー行動の理解 競合サービスとの比較 アイデアの発想とブレスト コンセプトの選定 ワイヤーフレームの作成 すべての工程を企画側と一緒に行い、認識のズレをなくすことを重視しました。 そこで、いくつかの気づきがありましたので以下のようにまとめました。 1. 技術的な実現性の向上 まず、エンジニアがサービス設計に参加することで、提案されたサービスの技術的な実現性を早い段階で評価できます。 サービス設計の段階はついつい、夢を詰め込み過ぎちゃうことがあるため実際に設計、実装を行うエンジニアがいると非現実的な機能や要件を未然に防ぎ、実装可能な範囲でサービスを設計することができます。 しかし、エンジニアは今後の実装を見据えて考えてしまい、手堅い構造にしてしまう傾向があるため、ここでは柔軟な考え方が必須です。 2. パフォーマンスと拡張性の確保 サービス設計で考慮されたユーザーストーリーや要件を元に、エンジニアがシステムを設計を行うため、サービス設計を深く理解する必要がありますが、サービス設計も行ったエンジニアがシステム設計を行うと将来的な変更や機能の拡張がスムーズに考えることができるようになります。 実際には、サービス設計の段階で出てきたアイディアが初期のリリースでは厳しいが、その後のリリースでは可能という判断もできました。 3. ユーザビリティと技術のバランス 次に、ユーザビリティと技術的な側面のバランスが取りやすくなります。 ユーザーエクスペリエンスを向上させる一方で、実装の複雑さや開発リソースの最適な活用を考慮し、実現可能なサービスをデザインできます。 開発を進めていく中で、新しいアイディアが出てくることも多々あるのでこういったことが、サービス設計から盛り込めるのは非常にいいですね。 4. 早期段階での課題解決 実装段階で発生するかもしれない問題や技術的な課題を早い段階で見つけ出し、解決することができます。これにより、開発プロセスがスムーズに進み、最終的なサービスの品質向上が期待できます。 おわりに エンジニアがサービス設計に積極的に参加することで、ユーザビリティと技術のバランスを取ることができます。これにより、ユーザーエクスペリエンスを向上させながら実装の複雑さを最小限に抑え、企画側とのやり取りの頻度を減らすこともできます。 その結果、サービス設計は単なるアイデアだけでなく、具体的で実現可能なものとなり、プロジェクトの成功に向けたしっかりとした基盤を築くことができるようになると考えています。 ビジネス視点でサービスを見ることがあまりなかったので、今回のサービス設計は良い経験になりました。 今後も試行錯誤を通じて得た気づきは積極的に公開していきたいと思います! 明日は、 rubihiko さんです。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
はじめに こんにちは、新卒一年目OJT中の平野です。 今年は暖冬と言われていますが流石に夜はエアコンと電気毛布が手放せなくなりました。春が待ち遠しいです。今回は簡単なToDoアプリをペアプログラミングで開発しました。その中で、宣言的UIやSvelteの記法、リアクティブ、フロントエンドの歴史などを知ることができました。今回はそのペアプロを通して得られた知見を共有していきたいと思います。先輩社員のたけろいどさんにもご協力いただきナビゲーターが得られた知見も記載していますので、ぜひご覧ください! 背景 弊社のエンジニアにはOJTという教育制度が導入されています。OJTは、実際の業務を通じて知識や技術を身につける制度です。 私たち新入社員は新人研修を終えた後3ヶ月ずつ3つの部署を渡り実際の業務にあたります。この期間は私のようなトレーニーに対し、一人のトレーナーがついて業務のサポートを行っていただきます。現在、私には先輩社員のたけろいどさんにトレーナーとしてついていただいています。積極的に私の業務や目標達成のためのフォローをしたいただいており、感謝の毎日です。 そんなたけろいどさんにSvelteについて勉強したいと言ったところ、一ヶ月間、毎週1時間ペアプログラミングで開発を行う時間をとっていただくことになりました。たけろいどさんの専門分野でもあるフロントエンドについて今回の開発を知見を深めることができました。 つくったもの 今回は4時間ほどの開発時間をいただけたので、コード・見た目ともに非常にシンプルなToDoリストを作成しました。少ない開発時間でしたがリアクティビティなどのSvelteの特徴やフロントエンドの歴史について知る機会になりました。 また、ペアプログラミングで作ったToDoリストを個人でBootstrapを用いて簡単に装飾、機能を追加を行い、簡単なカンバンボードのような見た目のものを作りました。バックエンドの処理は作成していないのでページをリロードするとデータはなくなってしまいますが、それっぽいものはできたかなと思います。class名だけで画面を簡単に装飾できて便利ですね。ホットリロードというコードの変更がすぐに画面に反映される仕組みも相まって楽しい開発体験でした。 作成した画面 ドロップダウンメニューやドラッグ&ドロップで移動、削除ボタンといった機能を実装 既存のカンバンボードを見てみると使いやすいよう工夫がされていてまだまだ自分の作った物には改善の余地があることが分かります。勉強になりました。 今回フロントエンドでの開発に触れて、デザインやUIの部分でやってみたいことがいくつか出てきたので、勉強を進めていきます。 これまで大学の授業などでHTML、CSS、JavaScriptなどを触ったことはありましたが、今回初めてモダンフレームワークに触れました。今回学んだことについていくつか書いていきます。 ペアプログラミングで学んだこと モダンフレームワークを触った感想として、これまで開発者が複雑に感じていたことをシンプルにできるような仕組みがいくつか導入されていることが分かりました。 例えばSvelteにはコンポーネントベースのアーキテクチャ、宣言的UI、といったモダンフレームワークの特徴があります。 コンポーネントベースのアーキテクチャを簡単にいうとUIをコンポーネントに分割してカプセル化することで再利用やメンテナンスを簡単にするのもです。 宣言的UIは、インターフェースを構築する際のアプローチです。SvelteではHTMLのような構文を用いてUIの構造を宣言し、フレームワークが内部的に必要なDOM操作を処理しています。コードが直感的で読みやすいという利点があります。 逆にJavascriptは命令的UIというアプローチで要素をどのように変更するかを詳細に記述しています。こちらには細かい制御が可能といった利点があります。私が今回Svelteで開発を行った感想として、HTML、CSS、JavaScriptを使った開発と比較して楽で直感的な開発ができました。Svelteのこれらの特徴の恩恵にあやかることができたのではないかと思います。 ペアプログラミングをやってみた感想 今回、トレーナーのたけろいどさんと開発を行いましたが、詳しい方に教えていただきながら開発することで、環境構築やわからない部分に詰まることもなく開発することができました。また、開発中は自分がやっていることと関連する知識を教えてくださり、ゲームのチュートリアルのように、ストレスフリーに知識を得ることができました。これはたけろいどさんの手腕のおかげであり、自分がいずれ同じようなことをすることが来たときの参考になりました。学びの多い4時間でした。 先輩社員の視点:ペアプログラミングの効果 改めまして、先輩社員のたけろいどです。 ペアプロを通して学んでいた知見を書いていきます。よろです。 新しい視点の価値 トレーニーとのペアプログラミングは、私にさまざまな気づきを与えてくれました。日常的に行っている実装作業に対しても、彼らは多くの疑問を投げかけ、「知識の呪い」を改めて感じさせてくれました。特にリアクティビティの概念に関しては、その重要性を再認識しました。 私はjQueryやvanillaJSに精通しているため、リアクティビティの複雑さやその仕組みを理解しています。しかし、トレーニーはJavaScriptの経験が少ないため、リアクティビティがどのように機能しているのか、その重要性を十分に把握していないようでした。 そこで、リアクティビティの基本原理と、それがどのようにTodoアプリに応用されているかを実際にコードを書きながら説明しました。このプロセスを通じて、トレーニーはリアクティビティの重要性や、現代のフレームワークが開発者の作業負荷をどのように軽減しているかを実感できたと思います。 リアクティビティの例はほんの一つに過ぎませんが、私たちが気づかないうちに「知識の呪い」は数多く存在しています。そのため、積極的にコミュニケーションを取り、トレーニーの成長を支援していくことが重要だと感じています。 通常業務との兼ね合い ペアプログラミングは、トレーニーの育成において優れたツールであり、コミュニケーションがとりやすいことから非常に有効だと考えています。しかし、トレーナーおよびトレーニー双方の時間を拘束するため、過度に時間を割くと通常業務に影響を及ぼす恐れがあります。特に、toCのサービスを扱う私たちの場合、日々お客様のトラブル解決などの運用業務が発生し、これらに迅速に対応する必要があります。そのため、運用業務とペアプログラミングの時間配分のバランスを取るのが難しいと感じています。 私たちのチームではスクラム方式で開発を進めており、タスクには優先度が設定されています。このアプローチにより、運用業務とペアプログラミングとを比較的良好なバランスで進めることができました。さらに、ペアプロに割く時間を1時間に制限することで、作業にメリハリをつけ、業務との並行実施が可能となりました。 一方で、反省点もあります。時間を区切ってペアプロを行うことはメリハリをもたらしましたが、その1時間でどこまで進めるかという目標を事前に共有することができませんでした。その結果、途中で作業が終了してしまったり、時間配分に誤りが生じたりすることがありました。次回からは、トレーニーと事前に目標を明確に共有することで、互いに安心してペアプログラミングに取り組めるようになるという重要な気づきを得ることができました。 まとめ 今回はトレーナーのたけろいどさんとペアプログラミングを通じてToDoアプリを作成しました。Svelteの特徴やトレーニーへの教え方など、今後自分に必要になる知見を得られたと思います。たけろいどさんにはこの場を借りて感謝を伝えます。ありがとうございました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 14日目の記事です。 はじめまして。新卒1年目の高田です。 弊社にはジョブローテーションという制度があり、新卒で入社した社員は様々な部署を数ヶ月単位で横断します。 その中で私は未経験でアプリ開発に着手させていただいており、今回はそこでの学びの一つをブログとして執筆していきたいと思います。 作るもの それでは、タイトル通り円グラフを描画してみます。 今回は、下図のような図形を作っていきます。 アニメーションをつけるとこんな感じ まずは円弧を描いてみる まずは、円弧を描くところから始めましょう。 描画にはCanvasのdrawArcを使用します。 startAngleで開始地点を指定して、sweepAngleで弧の角度を指定します。そうすると、指定した開始地点から時計回りに描画をしてくれます。例えば、開始地点を0度、角度を270度に設定すると下図のような円を描くことができます。 Canvas(modifier = Modifier.size(200.dp)) { drawArc( color = Color.Red, startAngle = 0f, sweepAngle = 270f, useCenter = false, style = Stroke( width = 20.dp.toPx() ) ) } グラフっぽくする 円弧の描画を少し応用して、グラフを作ってみます。 以下の実装では、それぞれの円弧を組み合わせて、円を構成させるということをしています。 // テスト用のデータを用意する val data :List<Float> = listOf(20f, 15f, 5f, 10f) // 描画用のデータに変換する val sweepAngleList: List<Float> = data.map { 360f * it / data.sum() } val colorList: List<Color> = listOf( Color.Red, Color.Blue, Color.Green, Color.LightGray, ) val pieChartData: List<Pair<Color, Float>> = colorList.zip(sweepAngleList) // グラフを描画する pieChartData.forEachIndexed { _, data -> drawArc( color = data.first, startAngle = -90f, sweepAngle = data.second, useCenter = false, style = Stroke(width = chartBarWidth.toPx(), cap = StrokeCap.Butt) ) startAngle += data.second } 円グラフっぽくなりましたね!? 静止画だけで良い人はここまで。 最後にアニメーションをつけて少し豪華にしていきます。 アニメーションもつけて豪華にする ここからアニメーションをつけて少し豪華にします。 もっと良い実装方法があるのかな〜とは思いますが、筆者は以下のように実装を進めてみました。 以下の実装では、 Animatable  のリストを作成し、それぞれの円弧に対して遅延して順番にアニメーションを適用することで、円弧が描画されているようなアニメーションを実現しています。 // Animatableのlistを作成する val animaTableList = List(datasets.size) { remember { Animatable(0f) } } animaTableList.forEachIndexed { index, animaTable -> LaunchedEffect(animaTable) { // 各アニメーションを遅延させる delay(index * 600L) animaTable.animateTo( targetValue = 1f, animationSpec = tween( durationMillis = 600, easing = LinearEasing ) ) } } Canvas(modifier = Modifier.size(radiusOuter)) { var startAngle = -90f // 各円弧に順番にanimaTableListを適用して、描画順にアニメーションを再生させる pieChartData.forEachIndexed { index, data -> drawArc( color = data.first, startAngle = startAngle, sweepAngle = data.second * animaTableList[index].value, useCenter = false, style = Stroke( width = chartBarWidth.toPx(), cap = StrokeCap.Butt ) ) startAngle += data.second } } どうでしょうか?それっぽいアニメーションになってますかね!? 最後に 「Androidでアニメーション付き円グラフを作ってみよう」ということで、円弧の描画から円グラフへのちょっとした応用までやってみました。 もっといい方法をお前に教えてやるぜ!という方がいましたら、下記リンクからコンタクトをどうぞ! (冗談です) 明日は、dev-shimadaさんの「ログのエラー検知をノーコードでslackに通知してみる」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 10日目の記事です。 「CLIを使っていて、複数の選択肢から1つ選びたい。なおかつ値を入力するのではなく、カーソルを移動して選択したい。」と思ったことはありませんか?少なくとも私にはありました。 bashには dialog コマンドが実装されており、予め設定した選択肢から1つを選択するような操作画面を出すことができます。 しかし、powershellにはそういった機能はありません。正確にはあるにはあるのですが、.NET Frameworkの機能を使って別ウィンドウを新たに生成するといったものになっています。できれば別ウィンドウは開かず、 dialog のようにCLI上ですべて完結したいですよね。 そこで今回は、powershellを用いて dialog を実装してみようと思います。 つくってみた array を渡すと選択肢が表示されて、選択した要素のindexが返ってくるようなpowershellコマンドを作ります。 環境 PS C:\Users> $PSVersionTable Name Value ---- ----- PSVersion 7.4.0 PSEdition Core GitCommitId 7.4.0 OS Microsoft Windows 10.0.19045 Platform Win32NT PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0 powershellのモジュールについて powershellモジュールのディレクトリ構成は以下の様になります。 MyPsModule/ └ MyPsModule.psm1 作成したモジュールは、 .psm1 で保存します。 保存するディレクトリ名とモジュールファイル名は同じにしてください。powershellはモジュール置き場に置かれたディレクトリ名と同じpsm1モジュールを検索して読み込みます。 どこに置けばよいかは、 $env:PSModulePath で確認することができます。 PS C:\\Users\\xxxxx> $env:PSModulePath C:\\Users\\xxxxx\\Documents\\PowerShell\\Modules;C:\\Program Files\\PowerShell\\Modules;c:\\program files\\powershell\\7\\Modules;C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules ここで表示されているいずれかのディレクトリにファイルをモジュールディレクトリを格納してpowershellを再起動すると、自動でモジュールが取り込まれます。 実装 MyPsModule.psm1 function Show-Dialog { param ( [string[]] $array ) # 何列目を選択中か $row = 0 # メニューカーソルの位置を取得 $firstLine = $Host.UI.RawUI.CursorPosition while ($true) { $counter = 0 # # 情報を1つずつ出力 foreach ($item in $array) { if ($counter -eq $row) { Write-Host "> " -NoNewline Write-Host $($item) -BackgroundColor White -ForegroundColor Black } elseif ($counter -eq ($array.Length - 1)) { Write-Host " " -NoNewline Write-Host $($item) -ForegroundColor White -NoNewline } else { Write-Host " " -NoNewline Write-Host $($item) -ForegroundColor White } $counter++ } $key = [Console]::ReadKey($true) # enterが押されたらrowを格納して終了 if ($key.Key -eq 'Enter') { return $row } # 入力ボタンに応じてrowの値を変更 elseif (($key.Key -eq 'UpArrow') -and ($row -gt 0)) { --$row } elseif (($key.Key -eq "DownArrow") -and ($row -lt ($array.Length - 1))) { ++$row } $cursorPos = $Host.UI.RawUI.CursorPosition $cursorPos = $firstLine $Host.UI.RawUI.CursorPosition = $cursorPos } } Set-Alias -Name dialog -Value Show-Dialog Export-ModuleMember -Function Show-Dialog -Alias dialog 動作確認 選択肢が表示され、カーソルが動くことを確認しました まとめ 今回はpowershellでdialog機能を作成してみました。まだまだ荒削りなので、エラー処理ができていなかったり、カーソルが1度でも改行された状態でコマンドを使うとバグが出たりします。 今後はそのあたりもつめつつ、より実用性のあるパッケージにしていきたいです。 参考記事 シェルスクリプトでカーソルメニューつくる | matsub Powershellでプロセスのメモリ使用量をロギングする ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 13日目の記事です。 はじめに こんにちは。新卒4年目の大里です。12月になって寒くなってきたため、今回はp5.jsで雪が降るようなアニメーションを実装しました。 p5.jsとは? p5.jsとはクリエイティブ・コーディングのために作られたJavaScriptのライブラリです。クリエイティブ・コーディングはプログラミングを使ってアート作品などを創作することを指します。 公式ページ: https://p5js.org/ 完成例 実行例 p5.jsを使って実装すればブラウザ上で表示できる以下のようなアニメーションを作成できます。 p5.jsで雪を降らした様子 ファイル構成 p5.jsでアニメーションを制御しているファイルはsketch.jsです。index.htmlではsketch.jsのインポートとCDNからp5.jsの取得を行っています。 └─Snow index.html sketch.js index.htmlの中身 <html> <head> <meta charset="UTF-8"> <script src="https://cdn.jsdelivr.net/npm/p5@1.8.0/lib/p5.js"></script> <script src="./sketch.js"></script> </head> <body> </body> </html> sketch.jsの中身 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { push(); stroke(255); translate(this.x, this.y); for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } pop(); } } 実行方法 index.htmlをブラウザで開けば雪が降るアニメーションが表示されます。 作成手順 雪を降らすアニメーションの実装の仕方を段階を追って説明します。アニメーションはsketch.jsのみで実装しているため、これ以降はsketch.jsのみに言及します。 背景を描く 実装例 function setup(){ // キャンバスのサイズ指定 createCanvas(windowWidth, windowHeight); } function draw(){ // 背景を記述 background(30,30,55); } コード説明 setup関数はアニメーションを描写する前に一度だけ実行される関数です。このコードではcreateCanvasという関数を使ってキャンバス(画面)の大きさを設定しています。 draw関数はアニメーションを描写する際に1フレームごとに実行される関数です。デフォルトでは60fpsで実行されます。このコードではbackground関数を使ってキャンバスの背景の色をRGBで指定しています。 windowWidth, windowHeightはp5.jsが提供する環境変数です。ウィンドウの幅、高さを表しています。 実行例 背景を描いたコードの実行例 円を描く 実装例 function setup(){ createCanvas(windowWidth, windowHeight); } function draw(){ background(30,30,55); // この行以降の図形描写には輪郭線の描写なし noStroke(); // この行以降の図形描写では白色で塗りつぶす fill(255); // 円を描く circle(width/2, height/2, 100); } コード説明 circle関数では引数でx軸の座標、y軸の座標、大きさを指定しています。widthはキャンバスの幅、heightはキャンバスの高さを表す環境変数です。 p5.jsの原点はキャンバスの左上に位置しています。x軸の正方向は原点から右方向です。y軸の正方向は原点から下方向になります。 キャンバスの座標軸 実行例 p5.jsで円を描いた様子 円を大量に描く 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); // 雪の配列を作成する for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); // 配列全ての雪を描写する for(let snow of snows) { snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(0, height); size = random(6, 20); drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } コード説明 雪(円)を大量に描くために雪クラスの配列を用いています。雪クラスではコンストラクターとして円のx座標, y座標, 円の大きさをランダムで設定しています。drawSnowFlakeメソッドで円を描くメソッドを定義しています。 実行例 p5.jsで大量に円を描いた様子 それぞれの円を降らす 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { // フレームごとに雪の位置を更新する snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } コード説明 雪を降らせるためにコンストラクターではy座標の修正と速度の設定を行いました。y座標はキャンバスから見えない上方向に設定しています。 updatePositionメソッドでフレームごとにy座標が増えていくように更新するようなメソッドを実装しました。加えて、雪がキャンバスから外れたときにキャンバスの上方向に戻るようにしています。 実行例 p5.jsで円を大量に降らした様子 円を雪の結晶に変える 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { // 現在のスタイル設定と変換を保存する push(); // 輪郭線を白色で描写する stroke(255); // 原点を雪の位置に移動させる translate(this.x, this.y); // 線を6本放射線状に描く for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } // スタイル設定と変換を元に戻す pop(); } } コード説明 円から雪の結晶に変えるためにdrawSnowFlakeメソッドを修正しました。push関数を使って現在の原点を保持しています。その後translate関数で原点を現在の雪の位置に移動させています。これをしておくと、雪の位置を原点として6本の放射線を描くための座標指定が簡単になります。最後にpop関数で原点をキャンバスの左上に戻します。push関数とpop関数はセットで使う必要があります。 実行例 p5.jsで雪を降らした様子 おわりに 今回はp5.jsを使って雪が降るアニメーション作成を行いました。p5.js特有の関数や環境変数を覚える必要がありますが、簡単にアニメーションをプログラミングすることができました。p5.jsはJavaScriptのライブラリであるため、Webサイトの装飾にも使うことができると思います。 明日は、D_Wさんのエンジニア目線で考えるサービス設計です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 8日目の記事です。 はじめに コロナに罹患してしまってアドカレ更新がすっかりおくれてしまいました。アドカレ掲載日前には書き上げていたんだよ。本当だよ。 寒いですね!って書き出しにしたらなんか妙に暖かい予報の日がふえてきました。西野です。 私はマイ ニフティというニフティ会員向けアプリでスクラムマスターをやっています。ほかにも、N1!というニフティのスペシャリスト制度でスクラムエヴァンジェリストを担っています。 というわけで、今日のアドベントカレンダーでは単なる1on1ではなく「スクラムを支援する方法としての1on1」について紹介します。 1on1の失敗 数年前、自分が初めて1on1で聞く側(コーチ)の立場になったときは、あまり効果的なものにできませんでした。聞き手としての能力不足などもありますが、1on1の目的を誤っていたことに一番の理由があったと今は考えています。 機能しなかった1on1の目的 チームメンバーのキャリアパスをフォローする 抱えている課題について聞き、解決するためのアクションを出す ぱっと見、1on1の目標としてそれなりにありがちというか、そこまで悪い感じはしないとは思います。 実際、チームメンバーの目標や課題を知る場としては悪くはありませんでした。しかし、以下の理由で効果的に機能しませんでした。 チームメンバーの課題に対して良いアドバイスができない チームメンバーの目標や課題を、1on1をやっている自分だけが知っている(チームに共有しない)メリットがわからない 当時はスクラムマスターとしてではなくチームリーダーとして1on1をやっているという意識だったこともあり、なにかチームメンバーの悩みを解決したり、目標に近づけるようなアクションを 自分が提示しなくてはいけない と思い込んでいました。しかし、人の課題すべてに答えを見つけられる人間なんていません。当然失敗しました。 その後コーチングについて知ったことで、今までの活動がうまくいかなかった理由がわかりました。 アドバイスは成長にはつながらない 「良いアドバイスができなかった」と書きましたが、もし完璧なアドバイスができたとしても、1on1に参加する人全員にとって意義のある時間にはできなかったと思います。 なにか課題があってアドバイスでそれがうまくいったとしても、それは成長ではありません。 スクラムマスターがチームのお母さんにならないための方法 でも書きましたが、考えれば自分でできたはずのものをアドバイスによって達成させてしまったなら、成長機会を奪った可能性さえあります。 コーチングの基本である「人の話を聞く」ことの背景には「その人の中に答えがあると信じる」ことがあります。それができないまま1on1をやってもほとんど得られる価値はありません。 もしアドバイスがほしいなら、特定の人でなくチームメンバー全員に話した方がいろいろな意見がもらえたり、フォローもしてもらえるかもしれません。1on1をアドバイスの場にしてしまうことが非効率な理由はそこにもあると思います。 スクラムマスターとして1on1をやろう この失敗をふまえて、チームリーダーとしてではなくスクラムマスターの活動のひとつとして、1on1の目的を変更しました。 うまくいかなかった1on1 チームメンバーのキャリアパスをフォローする 抱えている課題について聞き、解決するためのアクションを出す ↓↓↓ 改善後の1on1 傾聴力の訓練機会を増やす 相互のフィードバック機会を増やす スクラムマスターにしてほしい支援を伝える 以前の1on1の時に思っていた「チームメンバーを1on1によって助けたい」という気持ちは完全に捨てました。スクラムマスターとしても利があること、得になることを考えています。 この1on1の目的が、そのままスクラムマスターが1on1をやったほうがいい理由3つにつながってきます。 「傾聴力の訓練機会を増やす」のはなぜか ただ話を聞くだけは、多くの人ができている(つもりになっている)と思います。 しかし「話を聞き、その人の中にある答えが見つかるような質問をする」という聞き方はなかなかできるものではありません。 もともとある程度の上手い下手はあれど、一人では練習ができないので他人に協力してもらい練習する必要があります。 有志でコーチングの練習をしよう もしあなたがスクラムマスターではないとしても、将来マネジメント職を目指すなら今のうちに1on1の練習、特に「傾聴」の練習は絶対にしたほうがいいです。 現在自分は月1で1on1をしていますが、これだけでは到底訓練としては十分な回数ではありません。そのため、月1の1on1に加えて、隔週でコーチングの練習をしています。 自分のコーチングを効果的にスキルアップするためにはコーチ(聞き手)・プラクティショナー(話し手)のほかにそれを観察する「オブザーバー」が必要です。 オブザーバーを交えたコーチングの練習方法 コーチ・プラクティショナー・オブザーバーの3人1組をつくる 5分間、以下を行う プラクティショナー:なんでもいいので相談をする(スクラムに関する相談など) コーチ:その相談の答えをプラクティショナー自身が見つけられるように質問をする オブザーバー:コーチとプラクティショナーを観察する。オンラインの場であればビデオやボイスはオフにして存在感を消す。プラクティショナーが話しづらそうにしていないか、コーチはどんな表情で話を聞いているか、よい質問ができているかなどを観察する。 メンバーを変えてこれを繰り返す(3人であれば3回) これを「コーチングスキルアップセッション」と題し、1on1とは別に、コーチング能力を高める場として社内の有志で集まって隔週開催しています。 ここで得たスキルをチームの1on1で発揮していくようなイメージです。 「相互のフィードバック機会を増やす」のはなぜか プロダクトオーナー・デベロッパーはレトロスペクティブの場や、日々の中でスクラムマスターからなんらかのフィードバックを受け取る機会があります。 しかし、スクラムマスターは「スクラムマスターとして良かった・微妙だった」といったフィードバックをされる場がありません。自分がスクラムマスターとしてよりよく活動できているか、悩んだことがある人は多いと思います。 もう一点、スクラムマスターはスクラムの理解と実践を支援する立場にありますが、例えば誰かが良い行動・または改善できそうな行動をしているときに、すぐにフィードバックできるとは限りません。あとから思い返して気づくいたり、様子を見ていたら機を逃してしまうこともあるでしょう。 1on1の場でフィードバックの枠をつくれば、違和感なく相互にフィードバックすることができます。普段から問題なくフィードバックし合えている関係性であればこれは不要になってしまうかもしれませんが、チーム形成の途中にあるようなチームであれば、機能しやすいと思います。 もしかしたら、他者にフィードバックすること・されることに少し不安になってしまう人もいるかもしれません。こういうときは、一度に大量のフィードバックをしないことをお互いに約束しましょう。GOODひとつ、デルタ(改善点)ひとつくらいであれば、不安感も軽減されるはずです。 「スクラムマスターにしてほしい支援を伝える」のはなぜか スクラムマスターは、スクラムチームや組織へのスクラム理解と確立のための支援を行いますが、その支援の方法はどこにも決められていません。また、チームのどの部分を支援すべきかも、誰も教えてはくれません。 スクラムマスターとしてチームを観察していると、チームの長所、または足りていない部分が見えてくると思います。しかし、逆にチームからスクラムマスターはどう見られているでしょうか。自分が十分に支援ができているか気になるが、それをチームメンバーに問いかけるのは難しいという人もいるでしょう。 1on1の場であれば、比較的それがやりやすいです。事前に「自分はスクラムマスターとしてスキルアップしたい。なので、スクラムマスターからどのような支援があると嬉しいか知りたい」と言っておくことをお勧めします。そうすれば、スクラムチームもポジティブな気持ちでスクラムマスターに要望を伝えられます。 スクラムマスターはスクラムを教える立場になりがちなため、自分自身の「スクラムマスター」という役割をチームに主体的に理解してもらうことは難しいのではないかとずっと思っていました。 ですが、スクラムマスターもまたチームによって育ててもらうという考えを持つようになったことで、「チームからスクラムマスターとしての課題を与えてもらう」「その課題がわかるチームになるためにスクラムの理解を促す」という好循環が生み出せるようになった気がします。 スクラムマスターが1on1をやったほうがいい3つの理由 まとめです。 人の課題を聞く機会が増えるほど傾聴力の訓練ができる スクラムマスターとしてのフィードバックをもらう機会ができる チームメンバーが具体的に必要としているスクラムの支援がわかる この3つは、なかなか普段の業務の中では機会をつくりにくいものです。 もちろん1on1をやらなくてもスクラムマスターは務まりますが、1on1を行った方が効果的なアプローチがしやすくなります。特にチーム形成がまだ過渡期にあるチームだと機能しやすく、スクラムマスターにとっても得なことが多いです。 最後に:1on1は人のためならず…… 1on1というと、どうしても聞く側(コーチ)が上位者で、話す側(プラクティショナー)が下のようなイメージを持っていました。 しかし、プラクティショナーだけでなく、コーチのほうもスキルアップしたいものや得たいものを明確にすることで、1on1の場を「与える人・請う人」ではなく、「与える人同士」の状態に近づけることができます。 スクラムマスターのフィードバックループをどうまわすかを考えた時に、1on1はかなり機能的だと思います。1on1は、相手のためだけでなく「自分と相手のために」やるものじゃないかと思います。 スクラムマスターがスキルアップする手段として、1on1にぜひ挑戦してみてください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する 明日は、三浦さんによる「フォースフィールドアナリシスでチームのアクションを作る」です。 一生懸命書いてる感じがslackから伝わってきたので…きっと書けてるとおもいます(圧) お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2023 12日目の記事です。 はじめに こんにちは。ニフティに新卒で入社して五年目の佐々木です。今回はAWSのサービスの一つである CloudFront についてコスト削減を行う方法を紹介します。 以前ご紹介したS3のコスト削減については、 こちら のブログ記事をご参考ください。 背景 ニフティではサービス基盤にAWSを活用しており、コスト削減のためにサービスのインフラ効率を追求する取り組みや、有志で取り組んでいる社内でのコスト削減勉強会なども実施しています。 前回の記事 に引き続き、CloudFront周りでもコスト削減を行う機会があったので、コストを下げられるポイントや注意点について簡単にご紹介したいと思います。 この記事の内容 触れること CloudFrontのコスト削減方法 CloudFrontのコストを削減する上での注意点 実際に試した社内でのコスト削減事例とTips 触れないこと CloudFront自体の詳細の説明 自社での詳しいインフラ構成(コスト削減が目的のため、今回は一般的なインフラ構成のご紹介に限らせていただきます) CloudFrontの基本 CloudFrontとは、ざっくり説明するとウェブコンテンツの配信速度の高速化を目的としたAWSのCDN(Content Delivery Network)サービスです。CDNはサーバーの負荷を軽減し、コンテンツをキャッシュすることで素早くユーザにデータを提供するためのシステムです。 CloudFrontを用いることで、システムのサーバーの負荷を下げたり、エッジロケーションの活用によるコンテンツの配信の高速化などを実現できるので、そのような目的でコンテンツを配信するサーバーの前段に置かれることが多いです。 CloudFrontのコスト削減 課金形態 大規模サービスなどでは、CloudFrontはAWSの中でも特に活用されることが多いサービスですが、課金形態としてはリクエスト量に応じて課金されるため、リクエスト数が多いサービスだと特にコストが高くなってしまいがちです。 サービスの運用にかかるコストを正確に把握するためにも、まずはCloudFrontの課金形態をご紹介します。 2023年11月現在では以下のようになっています。 データ転送に対する課金 エッジロケーションから外部のインターネットに向けて転送されたデータ量に対して課金されます HTTP/HTTPSリクエストに対する課金 リクエストの数とタイプ(HTTP または HTTPS)に応じて課金されます その他にも課金要素はありますが、主に重要なのはこの2つだと思います。 詳細はこちらの AWS公式の資料 をご参考ください。 コスト削減方法 次に、CloudFrontに対する一般的なコスト削減方法をご紹介します。 CloudFront Security Savings Bundle を利用する CloudFront利用料の前払いの義務を果たすことで利用料に対する割引が受けられる制度です。CloudFrontの向けのSavings Plansのようなものです。 1年間、月額最低使用料をコミットする前提で、CloudFrontの請求額に対して最大30%のクレジットが適用されます。 既に運用しているサービスのアーキテクチャに変更予定がない場合や、年間で利用されるCloudFrontの料金がある程度決まっている場合は、もしまだ利用していなければ迷わず購入してよいと思います。一番手軽かつ削減幅も大きいです。 キャッシュを最適化する CloudFrontにはCDNとして一般的なキャッシュ機能が存在します。 画像などのコンテンツをキャッシュさせ、キャッシュヒット率を上げることで、エッジロケーションからのサービス提供を増やしてコスト削減ができます。 削減方法として主に2つで、どちらの方法でもコスト削減ができますが、できるのであればなるべくユーザーに近い箇所でキャッシュをすることでより大きなコスト削減効果が狙えるかと思います。 ブラウザキャッシュ:ブラウザ側でキャッシュを行い、CloudFrontへのリクエストそのものを軽減させる方法 CDNキャッシュ:CloudFront側でキャッシュを行い、CloudFrontの背後にあるサーバーへのリクエストを軽減させる方法。(この場合はCloudFront自体のコスト削減効果はなく、オリジンとして設定している側のコストが減る可能性があります。) データ転送量を削減する 画像やファイルの圧縮を行い、データサイズをできるだけ小さくすることで、そもそものCloudFrontからのデータ転送量を削減できます。 CloudFrontを経由させる必要がないものはCloudFrontを使わない こちらはコンテンツを取得するための構成や取得内容の根本を見直す方法です。 表示するコンテンツの内容次第では必ずしも常にCloudFrontを経由して使う必要はないため、別の代替案などを検討することなどでコスト削減の効果があるかもしれません 不正なbotアクセスを遮断する もし不適切なbot等のトラフィックがもしあれば、CloudFrontの前段にあるWAFで該当のUAやIPを遮断することで、不要なリクエストを削減できる可能性があります。 しかし、サイトにアクセスしているbotがGooglebotなどの有益なクローラーである可能性もあるため、遮断する際に本当に不要なbotアクセスなのかをしっかり調べた上で防ぐなどの注意が必要です。 WAF自体へのリクエストやWeb ACLにルールを追加することでもコストはかかり、必ずしもコスト削減に繋がるとは限らないので、周辺のインフラ環境も含めて考慮が必要です。 実際に社内で試したこと 画像ブラウザキャッシュでのリクエスト削減 こちらはブラウザキャッシュを最適化することで、CloudFrontへのリクエスト自体を減らしてしまう方法です。 画像やCSS、JSファイルなどあまり変更されないコンテンツはブラウザキャッシュを効かせることで、そもそものリクエストを大幅に削減することができます。 担当したサービスでは、ブラウザキャッシュのキャッシュ有効期間(Cache-Control など)を見直すことで、結果的に合計1,000USD/月 程度のコスト削減ができました。 キャッシュ周りのコスト削減に関しては、 SvelteKit, Next.jsの導入事例紹介など 〜ニフティのフロントエンドの今とこれから〜 でも紹介しています。 データ取得経路をCloudFront経由の構成からS3直アクセスにする こちらに関しては、データ取得の経路そのものを変えてしまう方法になります。 この方法については、コンテンツ自体の取得は必須であるものの、アクセス自体をCloudFrontを経由せずとも行える場合に有効だと思います。 取得経路そのものを変えることになるのでやや実装自体が手間ではありますが、取得するコンテンツのリクエスト数が多い場合は効果が大きくなります。 比較として、2023年11月現在ではap-northeast-1 の場合、 CloudFront と S3 のリクエスト料金は以下のようになっており、CloudFrontと比べるとS3のアクセスの場合はリクエスト量だけで半分以下のコストになることが分かります。 CloudFront 0.0120 USD(HTTPSリクエスト1万件あたり) 0.0090 USD(HTTP リクエスト1万件あたり) S3 0.0037USD(GETリクエスト1万件あたり) コンテンツの取得構成変更 次に社内での削減事例を簡単にご紹介します。 今回コスト削減を行った担当サービスでは、WEBサイトの表示に必要なデータを取得するためCloudFrontを経由していましたが、コスト削減のためこれをCloudFrontを経由せずにデータ取得を行う構成に変更しました。 詳細のインフラ構成はここでは割愛しますが、以下のようにインフラ構成を変更しました。 変更前の構成 ブラウザ → CloudFront → フロントエンドサーバー → CloudFront → バックエンドサービス 変更後の構成 ブラウザ → CloudFront → フロントエンドサーバー → バックエンドサービス 今回の構成においては、バックエンドで取得するコンテンツがキャッシュによる恩恵が少ないものだったこともあり、バックエンドの前段にCloudFrontを挟むのは過剰な構成になっていました。 そこで、コスト削減のためにフロントエンドから直接バックエンドを叩く形に変更しました。この方法を取ることで、周辺のインフラ構成も含めて合計1,200USD/月 程度のコストを削減することができました。 ただ、取得するコンテンツの種類によっては、CloudFrontでのパスのルーティングや、WAFを用いたセキュリティの制限をかけた方が良い場合もあるかと思うので、そこはコンテンツの要件と照らし合わせて構成を練る必要がありそうです。 不要な海外botアクセスの制御 こちらも同じく、不要なアクセスそのものを減らし、CloudFrontへのリクエスト料金を削減してしまう手法です。 もし、不適切なbotなどのアクセスがある場合は、WAFなどで防ぐことができます。 明らかにコストが跳ね上がっている場合は異常に気づけますが、今回はこれとは別にCost ExplorerでCloudFrontの料金をリージョン別に見ていてたまたま気づき、特定のUAからの海外アクセスが定期的に発生していることを発見できました。 こちらに関してはWAFで特定のUAをブロックすることで簡単にアクセスを制御することができました。 学んだ教訓とTips 今回のコスト削減の実施全体を通しての感想ですが、CloudFrontについてはとにかくリクエスト料金が高く付きがちなので、如何にリクエスト量を効率的に削減できるか?が勝負になるのかなと個人的には感じました。 また、普段キャッシュを意識するとコンテンツ取得の前段にCloudFrontを挟もうという設計になることが多いかと思いますが、コスト削減の観点で見ると「CloudFrontを経由させずにS3に直アクセスをさせる」ことで意外とコストを減らせるのだな、という気づきを得られたことが一番の発見でした。 心構えとしてのTipsになってしまいますが、「とりあえずCloudFrontを前段に設置しよう!」という考えではなく、結局はコスト面やリソース効率なども考慮した上でCloudFrontの利用やキャッシュ設定を適切にチューニングしていくことを意識して設計してくことが大事だと思いました。 後からアーキテクチャに手を加えると大変なこともあるので、設計の段階でコスト効率の良いアーキテクチャを考慮できると良いのかなと感じました。これは今度の業務にも学びとして活かしていきたいです。 まとめ 今回はCloudFrontのコスト削減方法についてご紹介しました。 実際のコスト削減方法についてですが、社内事例として紹介したように、現状のコストを分析することで不要なbotアクセスなどの発見に繋がることがあったので、意図していないコストに気づくという観点でもまずは「普段から使用しているサービスをCostExplorerなどで見ておく癖をつける」といった点に尽きるのかなと思います。 また、合わせて利用しているサービスの料金形態を把握することで、具体的なコスト削減のアイデアが浮かんでくると思うので、実際のコストと照らし合わせて確認するようにすると良いと感じました。 他のAWSサービスでもコスト削減事例が思いついたら別のブログ記事にする予定なので、また機会があれば紹介したいと思います! 参考記事 https://aws.amazon.com/jp/cloudfront/ https://aws.amazon.com/jp/s3/pricing/ We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass 明日は、kinari321 さんの「isucon初参加&0点劇」というテーマの記事のようです。お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2023 12日目の記事です。 ニフティのN1! Machine Learning Product Engineer 中村です。 最近はAmazon Bedrockを活用した生成AIをプロダクト実装することにハマってます。 NIFTY Tech Book #1を作りました 完成したNIFTY Tech Book #1 以前の投稿 でもお知らせした通り、技術書典15および全社イベントのNIFTY Tech Day 2023で来場者特典として、NIFTY Tech Book #1を配布させていただきました。 技術書典では120冊が3時間ほどで頒布終了となり、終了後も「まだ配布していますか?」と伺っていただいた方も多くいらっしゃいました。(間に合わなかった方々、ごめんなさい!) まだ電子版は購入可能になっていますので、まだご覧になっていない方はぜひこちらからご覧ください。 どうやって作ったのか? というわけでNIFTY Tech Bookというものを作ったのですが、自分はこういった印刷物を作成したことが今まで一度もありませんでした。どういうふうに進めて、どういうことを考慮するべきなのか?ということを自分なりにまとめてみたいと思います。 なおニフティには技術広報という固有の肩書きは現在は存在しません。そのため、エンジニアリングを楽しもう・みんなで作ってみよう、という有志の会で成り立っています。 まず応募しよう 技術書典は半年に1度開催されている技術書同人誌イベントです。 https://techbookfest.org/ その他にも最近では技術書同人誌博覧会というイベントも年に2回ほど開催されています。 https://gishohaku.dev/ これは持論ですが、人間は締め切りがないと基本的に動けません。自分も他社の合同技術本を見ながら、「こういう本が自分でも作れたらな〜」と思っていただけで、実際には何も行動していませんでした。良くないですね。 そこで今回は、技術書典15にえいやと応募してしまって、やるしかないという状況を自分で作ってしまって、そこからどんどん動き出していきました。締め切り駆動開発は偉大です。 人を集めよう 自分が作りたいなと思っていたのが、「みんなでわいわいと集まって作る合同本」だったので、やると決めたら人を集めていきます。 ニフティでは2ヶ月に1度ほどLT大会が開催されるのですが、LT大会に登壇し、最後に番宣をするという形で人を集めたり、直接声をかけたりして、参加者を募っていきます。 Slack上で少し反応してくれている人には「じゃあ書こうね」とか「〇〇の内容でどうよ?」と声をかけていき、最終的に8人の執筆者が集まりました。 幸運なことに表紙を描いてもらえることにもなったので、表紙のデザインや入稿時のデータ作成などを分担することもここでできました。 (Special Thanks to 表紙を描いてくれたムサシ) 内容について 一般的な合同技術本であれば、エンジニアリング周りのことだけを扱うのだと思います。しかし、編集長の自分自身が「Team Geek」だったり「アジャイル式健康改善ガイド」だったり「Cooking for Geek」だったりのマインド・健康・人間の暮らしのような本も好物にしているため、今回は技術に関連する内容であれば無制限として執筆者のみなさんには書いてもらいました。 あとで執筆者に聞くと、「何でも書いていいよ、と言われたので本当に自由に書いた」という意見をいくつかもらいました。結果的に本全体の内容バランスも良かったので、この方針は今回の執筆者には上手くはまったと思っています。 発売後に行われた技術書典のYouTubeでの紹介でも、「C言語でQRコードを書くという内容から、ランニングを続ける技術まで本当に幅広い」ということで特徴づけられていたので、自由に書いてもらって良かったなと思っています。 執筆しよう 執筆作業に入るわけですが、今回はRe:VIEWというツールを使わせていただきました。 Re:VIEWは裏側はTeXで動作する執筆ツールで、執筆内容なども含めて全てをGitHub上で管理する体制にしました。同時に執筆者のためのDiscordサーバも開設し、コミットがあるたびにDiscordに通知が飛んでくるようにして、互いに執筆しなければいけないという空気感を作ることに(多分)成功しました。 「作業が終わったらPRを出してレビューしてもらうこと!」としていたので、誰が執筆が完了したのかが認識でき、自分自身も校閲漏れがなく進められたので、もう一度本を作るとしてもRe:VIEWで作ると思います。 (素晴らしいツールを開発していただいている開発者およびコミュニティのみなさんありがとうございます) 校閲しよう 執筆者に執筆してもらい、全部の文章が集まったら、校閲作業が入ります。校閲作業が必要なので、印刷所の入稿ぎりぎりを締め切りにすると痛い目をみます。(今回は2週間前を締め切りとしていたので大丈夫でした) 有志で集まって書いているとはいえ、会社の名前で書いている書籍のため、会社のネガティブキャンペーンになりそうな内容については編集者としては許すわけにはいきません。 ということで校閲をするわけですが、120ページもある本文を校閲するのはなかなか骨が折れます。 エンジニアらしくこの作業を自動化できないんだろうか?と考え始めました。 AIは編集長の夢を見るか? そこで、人知れずチャレンジしていたのがAIによる校閲作業です。実は私は本業は機械学習エンジニアなので、昨今のAIブームの流れには乗れているはずです。というわけで、大規模言語モデル(Amazon Bedrock, Claude2)を使って校閲をさせてみます。 ・プロンプト あなたは優秀な日本語の編集長です。これから入力する本文に対して、会社の評判を下げるような文章や、誤字脱字、内容の間違いがあれば、行番号とともに指摘してください。入力本文の最初を1行目とします。もし指摘事項がない場合には「指摘事項はありません」と返してください。 ### 出力例 line 1 : 「解凍」ではなく「解答」を使うべきです line 5 : 会社のネガティブキャンペーンになっている可能性があります ・出力(全て) 指摘事項はありません ・・・うまくいかない・・・。 というわけで、なぜかわかりませんがAIによる校閲は全くできませんでした。自分で人力で行ったほうが早いと判断して、2日ほどで黙々と校閲作業を行い、結果をGitHubにコミットして執筆者に見てもらうという作業を続けて、最終版に向けた校閲を行いました。 今でも生成AIはどんどん進化しており、次巻を作るときにはもっと優れたAIやプロンプトが生まれているのではないかという期待を持っています。次回こそAIによる校閲が成功すればいいなと思っています。 いざ、入稿! 校閲も済んだら入稿です。冒頭にも述べたように、自分には入稿の経験が全くないです。しかし、Re:VIEWを使って執筆している場合、設定を少し変更するだけで入稿用の本文データが生成できます。隠しノンブルなども完璧に入れてくれるため、これによって知識がない自分でも入稿を行うことができました。 そして、これは書籍の中にも書いているのですが、ここでトラブルがありました。 NIFTY Tech Bookは最初から左綴じを想像して作っており、入稿も左綴じでお願いをしました。しかし、自分の伝達ミスで、表紙データが右綴じを想定したデザインになっており、表紙を捲ると逆向きに始まってしまうという状態になっていました。 幸いなことに、印刷所の人が向きが逆なことに気づいてくださり、再入稿を行うことでトラブルを未然に防ぐことができました。印刷所の皆さん、その節はありがとうございました。 印刷所に入稿するという経験はなかなかないですが、印刷所の方々はプロなので、そこに上手く甘えつつ入稿に挑戦するというのがベストなように感じています。 技術書典の場合は直接持ってきてくれる! あとは実際の本の完成を今か今かと待ちます。 技術書典の場合、バックアップ印刷所に指定されている印刷所(今回は日光企画さんにお願いしました)を使うと、オフライン開催の当日に、会場へ直接搬入されます。そのため、自分達も完成品を当日に初めて見ることになります。 技術書典15は11時開場ですが、10時に会場入りしたメンバーで「やっぱり紙の本はいいね」「実際に物になると嬉しいね」「この厚さを無料配布している企業はここしかない!」などなど、会場前から盛り上がっていました。 みんなも紙の本を書こう そのような流れで今回技術書典に参加してきました。 最初は自分の思いつきから始まった今回のNIFTY Tech Book #1ですが、執筆をしてくれた有志のみなさんからは 「紙の本になって実物を見ると頑張って書いて良かったなと思った」 「実物が想像以上に厚くなってびっくりした」 「もう少しテーマを絞って、ニッチなテーマで有料にして売ってみたい」 「#2もあるならぜひ書きたい」「今回は準備してくれてありがとう」 という嬉しい言葉をたくさんもらいました。 最初は知らないことだらけなので大変なのですが、終わってみれば「案外大したことなかったな」と思いますし、何物にも変え難い達成感を感じられます。技術書典が終わった後の1人打ち上げは最高の気分でした。(そういえば執筆者打ち上げをしてないなとこの記事の執筆中に思い出しました) この記事を読んでいるあなたにも、ぜひ本の執筆、できれば紙の本の印刷に挑戦してもらえれば、この体験記を書いた意味があるなと思います。みなさんの挑戦を応援しています。 NIFTY Tech Book #2も夢想中なので、ぜひ一緒に書いてくれる人を募集しています! 明日の ニフティグループ Advent Calendar 2023 は「12月だからp5.jsで雪を降らす」です。ビジュアルが見える系のプログラミングって面白いですよね。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 11日目の記事です。 はじめに こんにちは。ニフティ株式会社の添野翔太です。 AWS Lambda でGoランタイムがサポートされなくなるため 弊社のブログ記事 を参考にカスタムランタイムに移行しようとした際にハマった問題と解決方法をお伝えします。 また、このハマったポイントはサービス固有のものではないので、見ていただいた方の助けになっていれば幸いです! 背景 AWS LambdaでGoランタイムがサポートされなくなるためカスタムランタイムに移行しようとしました。 ハマったところ 弊社のブログ記事 を参考にして、実施したところ、 INIT_REPORT Init Duration: 10009.48 ms Phase: init Status: timeout INIT_REPORT Init Duration: 10010.51 ms Phase: invoke Status: error Error Type: Runtime.Unknown というエラーが出るようになりました。 原因 この問題の原因としては aws-lambda-goモジュール のバージョンがカスタムランタイムに対応したバージョン(v1.18.0/ 関連PR )よりも古いもの(v1.14.0)を使っていたことでした。 そこで、アップデートをしたところエラーは出なくなりました。 % go get github.com/aws/aws-lambda-go go: upgraded github.com/aws/aws-lambda-go v1.14.0 => v1.41.0 provided.al2はカスタムランタイムなので、 Lambda ランタイム API がサポートされているバージョンを使ってビルドしていない場合、カスタムランタイムがAWS Lambdaから呼び出しイベントを受信し、応答データをAWS Lambda実行環境内に送り返せない状況になります。そしてタイムアウトになるようです。 まとめ 今回は、AWS LambdaでGoランタイムからカスタムランタイムに移行した際にハマったことと解決策について紹介しました。 明日は @uyuqui1718 さんの「AWSのコスト削減を試した話(CloudFront編)」です。お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 11日目の記事です。 基幹システムグループ N1! オートメーションスペシャリストの南川です。 現在、私が所属しているチームでは複数のプロダクトの開発をしており、プロダクトごとに GitHub リポジトリも分かれています。複数のリポジトリの Issue を一つの Project で管理するために、 Issue を Project に追加する作業を行う必要がありますが、それらを手動でやるとなると面倒だと思います。手動でやるとなると手間もかかりますし、Project への追加し忘れや追加先を間違えるといったミスも出てきます。この作業を自動化することで、手作業によるコストや作業ミスを減らし、ストレスの無い開発をすることができます。 今回は、GitHub の Issue を自動で Project に追加する方法として以下の3つを紹介します。 (方法 A) 組み込みの自動化ワークフローを使う (方法 B) GitHub Actions + actions/add-to-project を使う (方法 C) GitHub Actions + GitHub CLI を使う なお、こちらの記事は2023年11月に開催された技術書展15にて頒布したニフティの技術書「 NIFTY Tech Book #1 」で掲載された第8章の内容とほぼ同一のものとなります。無料で電子版を頒布しているので、他の記事も気になる方はダウンロードしてみてください。 また、明日に技術書の主催による技術書を出版するまでの記事も投稿されるので、そちらも要チェックです! (方法 A) 組み込みの自動化ワークフローを使う 2023年1月 エンタープライズアカウント向けに、特定の条件を満たしたIssueをProjectに追加する組み込みワークフローがリリースされました。 組み込みワークフロー では、ノーコードで以下のような一連の流れを自動化することができます。 項目 (Issue や Pull request) がプロジェクトに追加された場合、それらのステータスを Todo に設定する。 項目が Close された場合、それらのステータスを Done に設定する。 新規作成や更新された項目が特定の条件を満たしていた場合、それらを Project に追加する。 設定方法については 公式のドキュメント が分かりやすいのでそちらをご参照ください。 メリットとデメリット この方法のメリットは、コードを書かずに GUI 上で設定でき、手軽に導入できるという点です。組み込みワークフローは複製することができ、複数のリポジトリに対する導入も容易だと思います。 しかし、作成できる個数に上限が決まっているというデメリットもあります。このワークフローを作成できる上限はプランによって異なり、以下の表の通りです。 プラン 上限個数 GitHub Free 1 GitHub Pro 5 GitHub Team 5 GitHub Enterprise Cloud 20 GitHub Enterprise Server 20 自動追加ワークフローの個数の上限 Each workflow can target a different repository, allowing you to add items from up to four repositories. https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically また、最大4つのリポジトリまで項目を追加できるということで、それ以上のリポジトリを持っている私のチームではこの方法では厳しいと判断しました。 そのような場合に、これから紹介する2つ目、3つ目の方法が有用です。 GitHub Actionsを使う GitHub Actions は GitHub まわりのワークフローを自動化するプラットフォームで、以下の用途などに使うことができます。 Issue が作成されたら、 Project に追加する。 Pull request が作成されたら、 lint 、テストを実行する。 失敗した場合はマージをブロックする。 Pull request がマージされたら、開発環境にデプロイする。 コミットがプッシュされたら、 AWS CodeCommit とミラーリングする。 GitHub Actions で Issue を Project に追加する方法は以下の2通りになります。 actions/add-to-project を使う。 GitHub CLI ( gh project item-add コマンド) を使う。 それぞれの方法についてこれから説明していきます。 (方法 B) GitHub Actions + actions/add-to-project を使う actions/add-to-project は、 Issue や Pull request を Project に追加する Action です。 GitHub Actions と actions/add-to-project を使って、新たに作成した Issue を Project に自動追加させる手順を以下に示します。 (1) Personal Access Token の作成 公式のドキュメント を参考に、 Classic 版の Personal Access Token (PAT) を作成します。指定するスコープは以下の通りです。 project repo (プライベートリポジトリの場合) 参考 : https://github.com/marketplace/actions/add-to-github-projects#creating-a-pat-and-adding-it-to-your-repository (2) Repository Secret の追加 公式のドキュメント を参考に、 Repository Secret に手順 (1)で作成した PAT を追加します。 今回追加する Secret は以下の通りです。 Name : ADD_TO_PROJECT_PAT Secret : < 手順 (1)で作成したPAT ( ghp_ …) > (3) ワークフローファイルの作成 .github/workflows 配下に以下のファイルを作成します。 name: Add issue to project on: issues: types: - opened jobs: add-to-project: name: Add issue to project runs-on: ubuntu-latest steps: - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/<orgs|users>/<ownerName>/projects/<projectNumber> github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} 解説 name: Add issue to project name では、リポジトリの Actions タブで表示されるワークフロー名を指定しています。 ここで指定した名前は、 Actions タブの実行履歴で表示されます。 on: issues: types: - opened on では、このワークフローを実行するトリガー (条件) を指定しています。 issues は、ワークフローのリポジトリ内の Issue に対し、 types で指定したアクティビティが発生したときにイベントをトリガーするイベントです。 今回は opened というアクティビティを指定しており、 Issue が Open になる(作成される)時にトリガーが発動します。 jobs: add-to-project: name: Add issue to project runs-on: ubuntu-latest steps: ... jobs では、トリガーが発動した際に実行するジョブを定義します。 今回は add-to-project というIDのジョブを定義し、名前 ( name ) とジョブを実行するマシン ( runs-on ) を指定しています。 - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/<orgs|users>/<ownerName>/projects/<projectNumber> github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} uses では、このステップで実行するアクションを指定し、そのアクションの入力パラメータは with で指定します。 with で指定する入力パラメータは以下の通りです。 project-url (必須) Issue を追加する GitHub Project の URL。 github-token (必須) repo と project のスコープを持つ Personal Access Token(PAT) 。 labeled (オプション) Project に追加する対象となる Issue をフィルタリングするためのラベル。 複数指定する場合はカンマ区切りで指定します。 指定しない場合は全ての Issue を Project に追加します。 label-operator (オプション) ラベルフィルタの動作を指定します。 AND, OR, NOT のいずれかを指定します。デフォルトは OR です。 (方法 C) GitHub Actions + GitHub CLI を使う GitHub CLI は、コマンドラインから GitHub を使用するためのオープンソースのツールです。 Issue の作成や一覧表示、 Pull request の差分表示やマージなどが出来ます。ドキュメントは こちら になります。 また、 GitHub Actions ではシェルを使用したコマンドラインプログラムも実行することができます。そして、 GitHub CLI はプリインストールされており、 GitHub Actions のワークフロー上で使うことができます。 GitHub Actions と GitHub CLI を使って、新たに作成した Issue を Project に自動追加させる手順を以下に示します。なお、手順(1)(2)は方法Bの手順(1)(2)と同様のため、省略します。 (3) ワークフローファイルの作成 .github/workflows 配下に以下のファイルを作成します。 name: Add issue to project on: issues: types: - opened jobs: add_issue_to_project: name: Add issue to project runs-on: ubuntu-latest steps: - run: gh project item-add $PROJECT_ID --owner $PROJECT_OWNER --url $ISSUE env: GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} ISSUE: ${{ github.event.issue.html_url }} PROJECT_OWNER: <ownerName> PROJECT_ID: <projectNumber> 解説 - run: gh project item-add $PROJECT_ID --owner $PROJECT_OWNER --url $ISSUE env: GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} ISSUE: ${{ github.event.issue.html_url }} PROJECT_OWNER: <ownerName> PROJECT_ID: <projectNumber> run では、このステップで実行するコマンドを指定します。 GitHub CLI を使う各ステップでは、環境変数 GITHUB_TOKEN で必要なスコープを持つトークンを定義する必要があります。 今回は、 2023年7月に追加された gh project item-add コマンドを使って、 Project に Issue を追加します。 env では、このステップで利用する 環境変数 を定義します。 「 ${{ github.event.issue.html_url }} 」 でトリガーの発火元(作成した) Issue の URL を取得できます。 これは、 コンテキスト (Context) と呼ばれ、これを使うことで、変数やランナーの環境、ステップの情報などにアクセスできるようになります。 メリットとデメリット GitHub Actions を使う方法のメリットは、自由度が高いという点です。今回は Issue の Project への追加だけですが、 actions/slack-send を使って Slack チャンネルに Issue が作成されたことを通知するといったことも可能です。 しかし、ワークフローファイルを作成するのが手間であるというデメリットもあります。ただし、一度作ってしまえばコピペだけで済むのでそこまで気にならないでしょう。 方法B vs 方法C actions/add-to-project と GitHub CLI のどちらを使えば良いかという話ですが、個人的にはどちらの方法でも良いかと思います。影響がありそうな違いでいうと、 actions/add-to-project は利用するバージョンを固定できますが、 GitHub CLI はプリインストールのためバージョンを固定するのは難しいかと思います。そのため、 GitHub CLI で今回使った gh project item-add コマンドについて破壊的変更があった場合、今回のコードに修正が必要になる可能性があり、その部分のメンテナンスコストを鑑みて決定することになるでしょう。 終わりに 今回は、作成した Issue を自動で GitHub Project に追加する方法を3つ紹介しました。紹介したそれぞれの方法についてですが、以下の判断基準で選ぶと良いと思います。 対象となるリポジトリが少なく、手軽に導入したい場合は、組み込みワークフローを使う方法A。 通知等の機能を追加するといった自由度を求める場合は、 GitHub Actions を使う方法B,C。 方法BとCはお好みで選ぶ。 方法Cはバージョン固定が難しいため、破壊的変更があった場合は対応が必要。 GitHub はアップデートで自動化などに役立つ便利な機能が追加されることがあるため、 変更履歴 を定期的に見てみるのもオススメです。 明日は、@iNakamura さんの「出版について何も知らない状態から技術書典に参加する技術」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 10日目の記事です。 こんにちは。会員システムグループでエンジニアをしている山田良介です。 私の担当するシステムではNext.jsへのシステムリプレースを行い、順調に稼働しています。開発効率向上、テスタビリティの向上など恩恵が大きい一方で、ブラウザサポートの面で課題も見えてきました。 Next.js化での課題 ReactはもともとSPAのためのフレームワークです。HTMLはほぼ空の状態からスタートし、ブラウザ上でJavaScript(React)がほぼ全てのDOMを構築します。 サーバサイドレンダリング(SSR)フレームワークであるNext.jsでもその基本は変わりません。サーバ上で一度JavaScriptを実行してHTMLを構築したのち、ブラウザ上でもう一度JavaScriptを実行し、状態を同期(hydration)することになります。HTMLのDOM全体をJavaScriptが管理している、という状況はReact単体で利用している場合と変わりません。 ここで次の2点が問題になります。 未キャッチのエラーが発生した場合、 ページ全体が非表示 になってしまう ブラウザによって対応する JavaScriptの文法が異なる ため、予期せぬ文法エラーが起こる 後者が非常に厄介です。「このブラウザはECMAScript20XXまで対応」のような形ならまだ対応しやすいですが、現実には個別の文法ごとに対応状況がバラバラで、全ての文法を網羅的に確認するのは現実的に不可能です。そして文法エラーが発生した場合、前者によりページ全体が落ちてしまいます。 Chrome、Edgeなどのモダンブラウザには自動アップデート機能があるため、基本的に最新版を使っている前提が置けます。しかしSafariはOSごとにバージョンが固定であるため、古いmacOS、iOSの環境でページ全体の表示ができない不具合が発生し、これに悩まされることになりました。 フォールバック対応 まずは緩和策での対応です。 User Agentによる強制リダイレクト 古今すべてのブラウザへ対応するのは不可能です。IE11などサポート対象外とするブラウザを明確に決め、User Agentによる判定でエラーページに遷移させることとしました。 if (checkSupportedBrowser(ua)) { ... } else { return { redirect: { permanent: false, destination: '/error_page' } } } なおエラーページもNext.jsで作ってしまうと、遷移先のエラーページも表示できないという事態に陥るため注意が必要です。 Error Boundaryの設置 Next.jsでエラーを未キャッチにしてしまうとDOM全体がアンマウントされてしまいますが、 Error Boundary を設置することで代替コンポーネントを表示することができます。これは通常のエラー処理でも有用です。 標準機能としては特定のメソッドを実装したコンポーネントを作る必要がありますが、サードパーティーの react-error-boundary などを利用することもできます。 最上位の_app.tsxに最低限設置するほか、必要に応じて各所に挿入することで非表示部分を最小限に抑えることができます。 _app.tsx const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => { return ( <ErrorBoundary FallbackComponent={() => <DefaultFallbackPage />}> <Component {...pageProps}> </ErrorBoundary> ); }; export default MyApp; ただしErrorBoundaryコンポーネントそのものやフォールバック先のコンポーネントも文法エラーになってしまうことがあるため、レガシーブラウザ対応という観点では100%安全ではありません。 JavaScriptダウングレード対応 JavaScriptのトランスパイルにより古いブラウザに対応させることで、対応するブラウザの幅を広げる対応です。 トランスパイルの設定 TypeScriptの出力ターゲットをサポートしたいブラウザの対応範囲まで下げます。 tsconfig.json { "compilerOptions": { "target": "es5" ... } } またpackage.jsonでbrowserslistを指定することで、トランスパイル時に対応ブラウザが考慮されるようにします。文法は playground で確認すると良いでしょう。 Next.jsの場合、webpackでのJavaScriptおよびCSS Modulesのトランスパイル結果に影響します。 package.json { ... "browserslist": [ "> 0.5%", "Safari >= 11", "iOS >= 11", ... ] } transpilePackagesの指定 Next.jsはデフォルト設定のままだと、プロジェクト内のソースのみをトランスパイルし、node_modulesをトランスパイルしません。従って、ライブラリで新しい文法が使われている場合に対応できません。 node_modules内のパッケージをトランスパイルするためには、next.config.jsでtranspilePackagesを指定します。 next.config.js const transpilePackages = [ '@tanstack/query-core', '@tanstack/react-query', 'ky', 'zod', ]; /** @type {import('next').NextConfig} */ const nextConfig = { ... transpilePackages: transpilePackages } module.exports = nextConfig; 一括で全てトランスパイル対象にするようなオプションはなく、対象とするパッケージ名を個別に指定する必要があります。 なおjestで next / jest を使用している場合、この設定はjestのtransform設定にも反映されます。 polyfillの追加 上記設定ではトランスパイルで解決できる文法が修正されますが、polyfillが必要な文法に対応できません。エラーとなる文法を特定した上で、 polyfill.io を利用することで対処します。 _document.tsx const Document = (): JSX.Element => { return ( <Html> <Head /> <body> <Main /> <NextScript /> <Script src="https://polyfill.io/v3/polyfill.min.js?features=globalThis" strategy="beforeInteractive" /> </body> </Html> ); }; export default Document; polyfill.ioはUser Agentを元にブラウザごとにpolyfillを返してくれるサービスです。featuresでpolyfill対象の文法を指定することができます。上記例ではglobalThisに対するpolyfillが返ります。 調査環境の整備 上記対応は一度設定すれば恒久的に使えるようなものではありません。 改修やパッケージアップデートなどで要対応箇所が増える可能性があるため、継続的なメンテナンスが必要です。そのためにはデバッグ環境を用意する必要があります。 ブラウザテストサービスの利用 レガシーブラウザでの検証を実機で行うのは困難です。古いOSを搭載した実機を、確認したいブラウザバージョン数の分だけ用意する必要があります。またOSが古いことによるセキュリティリスクもあります。 このため、リモートでブラウザを借りて利用できるSaaSを利用することにしました。代表的なところだと BrowserStack や Sauce Labs などがあります。ブラウザ経由でSaaS側が用意したブラウザを操作することができ、開発者ツールなども使用可能です。 サービスによっては日本国内のサーバがなかったり、日本語入力に難があったりなどの問題はあるものの、最低限の動作確認はできるようになりました。 自動検知 リリースのたびにレガシーブラウザまで手動検証しなければならないのは面倒なので、なんとかして自動化したいところです。上記に挙げたサービスでは各種E2Eテストツールにも対応しているため、CIでの自動化ができそうです。これは現在検証中です。 ただしレガシーブラウザを対象とすることを考えると、以下の制約があります。 ツールはSelenium一択状態 JavaScript文法エラーそのものを検出することはできない Cypress や Playwright といった最近のE2Eテストツールは操作対象のブラウザバージョンが指定されており、基本的に最新のものを要求します。従ってレガシーブラウザ対応では利用できません。WebDriver対応ブラウザに対して汎用的に使える Selenium か、そのラッパーが現状唯一の選択肢になっています。 また文法エラーはブラウザコンソールログへの出力で確認が可能ですが、コンソールログを取得する機能はWebDriverの規格にはありません。Chromeの独自機能としては存在しますが、Safariなどでは利用できません。このため、エラー検知は画面要素の検証によって行い、具体的なエラー内容は別途手動でデバッグするしかなさそうです。 また、 Sentry などのエラートラッキングツールを利用することでも検知は可能です。ただしSentryに到達する前に文法エラーでJavaScriptの読み込みが停止した場合、検知不可能になります。 まとめ Next.jsでのレガシーブラウザ対応についてご紹介しました。 Next.jsに限らず、JavaScriptを多用するモダンなフロントエンドフレームワークは互換性の問題を抱えがちです。サポートするブラウザのポリシーを明確にし、検証体制を整えた上で臨むようにしましょう。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 7日目の記事です。 こんにちわ!NIFTY engineering運用チームのいかりがわです! 今回はWordPressのテーマをGitHub管理できるようにし、EC2に自動でデプロイするようにしたので、その手法をまとめていきたいと思います。 背景 私たちが運用しているNIFTY engineeringでは、何らかの変更があったときは FileManager というWordPressのファイル管理用プラグインを使って手動で変更を加えていました。 しかし、これでは手動での変更反映になり、以下のような問題が起こります。 人為的なミスが発生する可能性が高まる 設定や手順のミス、環境の不整合などが起きやすくなります。 一貫性が保てない NIFTY engineeringでは、本番環境、開発環境の2つの環境を用意しています。 運用上では開発環境で確認後、本番環境にデプロイするようにしていますが、緊急時の対応などは急いで対応するため、本番のみに反映しがちになります。 ニフティのプロジェクトでは基本的にCI/CDパイプラインが整備されています。 NIFTY engineeringもCI/CDのパイプラインを使用して変更を容易にしていきたいと考えており、AWS CodePipelineによる自動デプロイのインフラを作っていこうと考えました。 手法 私たちは、GitHub ActionsからAWS CodeCommitへソースコードをコピーして、CodePipelineを使用するようにしました。 また、WordPressのテーマをEC2に自動デプロイする手法を採用しました。 具体的な流れは以下の通りです。 まず、DeveloperがソースコードをGitHubリポジトリにプッシュします。 GitHub Actionsはリポジトリの変更を監視し、変更があったらトリガーします。 変更があると、GitHub ActionsはCodeCommitにソースコードをコピーします。 CodeCommit内のコードが変更されると、CodePipelineがトリガーします。 CodePipelineのデプロイフェーズにてAWS CodeDeployが実行されます。 CodeDeployにて、EC2の特定のディレクトリに変更をデプロイします。 これで、GitHubでのソースコードの管理が容易になり、自動デプロイによる効率的な運用が可能となります。 構成図 構成図は以下のようになっています。構成図内の処理の順番は手法の手順に沿っています。 TerraformでAWSのリソースを作る では実際にCI/CDインフラを作ってみたいと思います。 まず、AWSのリソースをTerraformで作っていきます。 ちなみにここではTerraformのプロバイダ設定やバージョンの定義は割愛して、リソースの定義のみにしています。 また、EC2インスタンスはすでに起動していることを想定します。 CodeCommit CodeCommitのリポジトリを作成します。リポジトリ名を指定するだけです。 resource "aws_codecommit_repository" "repository" { repository_name = "codecommit-repository" } CodeDeploy resource "aws_iam_role" "deploy" { assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Sid = "" Principal = { Service = "codedeploy.amazonaws.com" } }, ] }) } resource "aws_iam_role_policy_attachment" "deploy" { role = aws_iam_role.deploy.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" } resource "aws_codedeploy_app" "deploy" { compute_platform = "Server" name = "deploy" } resource "aws_codedeploy_deployment_group" "deploy" { app_name = aws_codedeploy_app.deploy.name deployment_group_name = aws_codedeploy_app.deploy.name deployment_config_name = "CodeDeployDefault.OneAtATime" deployment_style { deployment_option = "WITHOUT_TRAFFIC_CONTROL" deployment_type = "IN_PLACE" } ec2_tag_set { ec2_tag_filter { key = "Name" type = "KEY_AND_VALUE" value = {デプロイ先EC2インスタンス名} } } service_role_arn = aws_iam_role.deploy.arn auto_rollback_configuration { enabled = true events = ["DEPLOYMENT_FAILURE"] } } aws_iam_role、aws_iam_role_policy_attachmentを使用し、CodeDeployで使用するIAMロールを作成します。 指定したポリシーはマネージドポリシーの AWSCodeDeployRole です。 また、aws_codedeploy_appを使用して、CodeDeployのリソースを作成し、aws_codedeploy_deployment_groupにて、CodeDeployで使用するデプロイメントグループを作成します。 以下のようにec2_tag_setでNameタグを指定することで、その名前と一致するEC2インスタンスを指定することができます。 これにより、CodeDeployがEC2へデプロイできるようになります。 ec2_tag_set { ec2_tag_filter { key = "Name" type = "KEY_AND_VALUE" value = {デプロイ先EC2インスタンス名} } } CodePipeline resource "aws_iam_role" "pipeline" { assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Sid = "" Principal = { Service = "codepipeline.amazonaws.com" } }, ] }) } resource "aws_iam_policy" "pipeline" { name = "codepipeline-policy" path = "/service-role/" description = "Policy for CodePipeline to deploy" policy = file("policies/codepipeline_policy.json") } resource "aws_iam_role_policy_attachment" "pipeline" { role = aws_iam_role.pipeline.name policy_arn = aws_iam_policy.pipeline.arn } resource "aws_codepipeline" "pipeline" { name = "pipeline" role_arn = aws_iam_role.pipeline.arn artifact_store { location = "unique-s3-bucket-name" type = "S3" } stage { name = "Source" action { category = "Source" configuration = { BranchName = "main" PollForSourceChanges = false RepositoryName = "codecommit-repository" } name = "Source" output_artifacts = ["SourceArtifact"] owner = "AWS" provider = "CodeCommit" run_order = "1" version = "1" } } stage { action { category = "Deploy" configuration = { ApplicationName = aws_codedeploy_app.deploy.name DeploymentGroupName = aws_codedeploy_app.deploy.name } input_artifacts = ["SourceArtifact"] name = "Deploy" owner = "AWS" provider = "CodeDeploy" run_order = "1" version = "1" } name = "Deploy" } depends_on = [aws_codedeploy_deployment_group.deploy] } aws_iam_role、aws_iam_policy、aws_iam_role_policy_attachmentを使用して、CodePipeline用IAMロールを作成しています。 また、aws_iam_policyではpolicies/codepipeline_policy.jsonというファイルを参照しており、そこにポリシーの設定が記述されています。(後述) aws_codepipelineを使用し、CodePipelineのリソースを定義していきます。 stageを使用することで、CodePipeline内の各フェーズを定義することができます。 今回は上で定義したCodeCommitとCodeDeployのリソースを紐付け、フェーズとして定義していきます。 stage { name = "Source" action { category = "Source" configuration = { BranchName = "main" PollForSourceChanges = false RepositoryName = "codecommit-repository" } name = "Source" output_artifacts = ["SourceArtifact"] owner = "AWS" provider = "CodeCommit" run_order = "1" version = "1" } } stage { action { category = "Deploy" configuration = { ApplicationName = aws_codedeploy_app.deploy.name DeploymentGroupName = aws_codedeploy_app.deploy.name } input_artifacts = ["SourceArtifact"] name = "Deploy" owner = "AWS" provider = "CodeDeploy" run_order = "1" version = "1" } name = "Deploy" } CodePipeline用IAMロール AWS公式のユーザーガイドを参考に作成しています。 https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/security-iam.html policies/codepipeline_policy.json { "Statement": [ { "Action": [ "iam:PassRole" ], "Resource": "*", "Effect": "Allow", "Condition": { "StringEqualsIfExists": { "iam:PassedToService": [ "ec2.amazonaws.com" ] } } }, { "Action": [ "codecommit:CancelUploadArchive", "codecommit:GetBranch", "codecommit:GetCommit", "codecommit:GetUploadArchiveStatus", "codecommit:UploadArchive" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "codedeploy:CreateDeployment", "codedeploy:GetApplication", "codedeploy:GetApplicationRevision", "codedeploy:GetDeployment", "codedeploy:GetDeploymentConfig", "codedeploy:RegisterApplicationRevision" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "ec2:*", "cloudwatch:*", "s3:*", "sns:*" ], "Resource": "*", "Effect": "Allow" } ], "Version": "2012-10-17" } CodePipeline用S3バケット CodePipelineでは各フェーズ間のデータの受け渡しに使用されます。 今回の例だと、CodeCommitからCodeDeployへソースコードを受け渡すために使用されます。 resource "aws_s3_bucket" "codepipeline" { bucket = "unique-s3-bucket-name" } resource "aws_s3_bucket_ownership_controls" "codepipeline" { bucket = aws_s3_bucket.codepipeline.id rule { object_ownership = "BucketOwnerEnforced" } } EventBridge これまでの定義で一通りのリソースは定義されました。しかし、これでは何をもってCodePipelineが実行され、デプロイが走るのかが定義されていません。 ここでEventBridgeを定義し、CodeCommitのリポジトリでソースコードが変更されたときにCodePipelineがトリガーされるようにしていきます。 resource "aws_iam_policy" "events_trigger" { description = "Policy for notice update to codepipeline" name = "events-trigger-policy" path = "/service-role/" policy = jsonencode({ "Statement": [ { "Action": [ "codepipeline:StartPipelineExecution" ], "Effect": "Allow", "Resource": "*", } ], "Version": "2012-10-17" }) } resource "aws_iam_role" "events_trigger" { name = "events-trigger-role" assume_role_policy = jsonencode({ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": "events.amazonaws.com" } } ], "Version": "2012-10-17" }) } resource "aws_iam_role_policy_attachment" "events_trigger" { role = aws_iam_role.events_trigger.name policy_arn = aws_iam_policy.events_trigger.arn } resource "aws_cloudwatch_event_rule" "trigger" { name = "codecommit-trigger-rule" description = "update event" event_pattern = templatefile("events/events_trigger_rule.tpl.json", { repository_arn = aws_codecommit_repository.repository.arn }) is_enabled = "true" } resource "aws_cloudwatch_event_target" "trigger" { arn = aws_codepipeline.pipeline.arn role_arn = aws_iam_role.events_trigger.arn rule = aws_cloudwatch_event_rule.trigger.name } IAMロールはCodePipelineと同様のやり方で定義しています。 EventBridgeはCodePipelineを実行することができれば良いので、StartPipelineExecutionというポリシーのみ定義しています。 { "Statement": [ { "Action": [ "codepipeline:StartPipelineExecution" ], "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17" } イベントパターン イベントパターンの定義は別ファイルで定義しています。 CodeCommitリポジトリでソースコードが変更されたときにトリガーされるようにしています。 events/events_trigger_rule.tpl.json { "detail": { "event": [ "referenceCreated", "referenceUpdated" ], "referenceName": [ "${branch_name}" ], "referenceType": [ "branch" ] }, "detail-type": [ "CodeCommit Repository State Change" ], "resources": [ "main" ], "source": [ "aws.codecommit" ] } IAM 最後にIAMユーザーです。 GitHubからCodeCommitへソースコードをプッシュできるようにするために作成します。 作成したIAMユーザーはGitHubリポジトリのシークレットに紐づけて使用します。 resource "aws_iam_user" "github" { force_destroy = "false" name = "github-user" path = "/" } resource "aws_iam_user_policy_attachment" "github_codecommit" { user = aws_iam_user.github.name policy_arn = "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser" } Terraformでの反映 applyして、AWSの環境にリソースを追加します。 terraform apply 無事反映されました!例としてCodePipelineの画面です。 SSHキーの登録 ローカル環境にて、ssh-keygenコマンドを実行します。 ssh-keygen 保存先とパスフレーズの入力を求められるので任意の保存先、パスフレーズを入力します。 Generating public/private rsa key pair. Enter file in which to save the key (~~~/.ssh/id_rsa): Enter passphrase (empty for no passphrase): 指定した保存先にid_rsaとid_rsa.pubが出力されました。 id_rsaをGitHubへ、id_rsa.pubをIAMユーザーへそれぞれ登録していきます。 ls -la | grep id_rsa -rw------- 1 username staff 2610 12 6 13:43 id_rsa -rw-r--r-- 1 username staff 579 12 6 13:43 id_rsa.pub IAMユーザーにSSH公開キーを登録 id_rsa.pubを開いて、中身をIAMユーザーに登録します。 コンソールからIAMユーザーのページへ遷移し、Terraformで作成したIAMユーザーを探します。 IAMユーザーを選択し、セキュリティ認証情報へ遷移します。 すると、AWS CodeCommitのSSH公開キーという項目があるので、SSH公開キーのアップロードを選択します。 そして、先ほど作成したid_rsa.pubの内容をコピペします。 すると、SSHキーIDが表示されるようになるので、これをコピーしておきます。(GitHubに登録します) IAMユーザーの設定は完了です。 GitHubに非公開キーとIAMユーザーのIDを登録 続いてGitHub Actionsに必要なシークレットを登録していきます。 必要なシークレットは以下のようになっています。 CODECOMMIT_SSH_USERNAME IAMユーザーのSSHキーID CODECOMMIT_SSH_PRIVATE_KEY id_pubの中身 GitHubリポジトリのページより、Settingsへ遷移します。 そして、左メニューからSecrets and variablesより、Actionsの項目を選択します。 ここからRepository secretsを登録できるので、New repository secretsで変数を登録していきます。 これでSSHキーの登録は完了です。 GitHub Actions 続いてGitHub Actionsを作っていきます。 簡単にディレクトリ構成を紹介します。 対象のリポジトリは以下のようなディレクトリ構成をしています。 . ├── .github │ └── workflows │ ├── mirror_codecommit.yaml │ └── scripts │ └── mirror.sh ├── .gitignore ├── README.md ├── appspec.yml └── myTheme ├── <テーマに関連するファイル群> ├── : └── : .github/workflows/mirror_codecommit.yaml 実行されるGitHub Actionsの設定ファイル .github/workflows/scripts/mirror.sh 上記GitHub Actionsにて、実行されるシェルスクリプト このシェルスクリプトでCodeCommitへのコピーを行います appspec.yml CodeDeployで使用されるデプロイの手順を定義する設定ファイル myTheme/ WordPressに配置されるディレクトリ デプロイ対象となる mirror_codecommit.yaml GitHub Actionsの設定ファイルを作成していきます。 name: Mirror codes to CodeCommit on: push: branches: - "main" permissions: contents: read jobs: mirror-to-codecommit: name: Mirror codes to CodeCommit runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Git push env: TARGET_REPO_URL: ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/<CodeCommitのリポジトリ名> SSH_USERNAME: ${{ secrets.CODECOMMIT_SSH_USERNAME }} SSH_PRIVATE_KEY: ${{ secrets.CODECOMMIT_SSH_PRIVATE_KEY }} run: bash "${GITHUB_WORKSPACE}/.github/workflows/scripts/mirror.sh" ここではmainブランチへのプッシュを検知し、ソースコードをCodeCommitリポジトリにミラーリングするジョブを定義しています。 後述のシェルスクリプトを使用してCodeCommitにプッシュが行われます。 SSHキーは先ほど登録したシークレットから取得されます。 mirror.sh 続いて、GitHub Actionsから実行されるシェルスクリプトです。 #!/usr/bin/env sh set -eu # copy credential mkdir -p ~/.ssh echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # push to mirror rpository export GIT_SSH_COMMAND="ssh -v -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -l $SSH_USERNAME" git remote add mirror "$TARGET_REPO_URL" git push --tags --force --prune mirror "refs/remotes/origin/*:refs/heads/*" あらかじめGitHub Actions上で登録された環境変数を読み取り、SSHキーなどのプッシュに必要な情報を取得します。 その情報を使用して、指定のCodeCommitのリポジトリにコードをプッシュしています。 appspec.yml 最後にappspec.ymlです。こちらはCodeDeployで使用されるデプロイの手順を定義する設定ファイルとなっています。 version: 0.0 os: linux files: - source: myTheme/ destination: /bitnami/wordpress/wp-content/themes/myTheme file_exists_behavior: OVERWRITE filesを使用して、デプロイ元のファイルと、デプロイ先EC2インスタンスのディレクトリを指定します。 今回の例だと、myTheme配下にテーマを配置しているので、こちらをデプロイするようにしています。 files: - source: myTheme/ destination: /bitnami/wordpress/wp-content/themes/myTheme また、以下の設定を追加することで、ファイルが存在する場合は上書きするように設定しました。 file_exists_behavior: OVERWRITE これで、全ての設定が完了しました! 動かしてみる 全ての設定が完了したので、実際にリポジトリにプッシュして、GitHub ActionsやCodePipelineが動作することを確認してみます。 変更を反映していつものようにリポジトリにプッシュしてみます。 git push origin main すると、GitHub Actionsが動き出しました! CodeCommitにミラーリングされています! (リポジトリ名などが違いますがそこはご愛嬌…) CodePipelineもこんな感じで実行されています! これで一通りのデプロイまでの流れを試すことができました。 まとめ 今回はCodePipelineを使ってWordPressのテーマをデプロイしてみました。 これでNIFTY engineeringの変更が容易になり、より快適なブログライフを送ることができるようになりました! WordPressのGitHub管理やCI/CDは悩ましいところが多いのでぜひ参考にしてみてください! 明日は、 @kanishionori さんの「今すぐ1on1をやった方がいい3つの理由」です。お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 6日目の記事です。 ニフティにはWEBサービスの基盤として20年物の自社製CMSがあります。10年以上前から時代に合わないものになっていましたが、まだ多くのWEBサイトで使用され続けています。しかし自社製CMSをメンテナンスし続けるのは困難なため、今回いくつかのサイトをmicroCMS+Astroの構成に移行しました。 本日は、その作業の中でもコンテンツ管理について話していこうと思います。 自社製CMSの役割と機能 自社製というのもあり多くの機能がありますが、主に必要とされているのは以下の4つ。 コンテンツ管理機能 ファイルサーバー機能 静的ページ出力機能 動的ページ動作環境 WordPressで例えると以下のようになります。 コンテンツ管理機能 → 複数サイトやカスタム投稿機能 ファイルサーバー機能 → FileManagerプラグイン 静的ページ出力機能 → Simply Staticプラグイン 動的ページ動作環境 → WordPressテーマのphp ブログのようなコンテンツメインのサイトなら、WordPressへの移行も有用な選択肢のひとつです。 ただLPのようなページが多くある場合、WordPressでは要件が合わない場面が多々あります。かといってレンタルサーバーでは機能面で心許ない。自社製CMSと同じ機能をフルで持ったCMSは非常に高額であるか、存在しないことが多いです。 そこでコンテンツ管理以外の機能はホスティング先などに任せて、Headless CMSを使用することが適切ではないかと考えました。 Headless CMSの選定 自社製CMSを使ったサイトでもピンからキリまであって、比較的シンプルに利用していたサイトを移行対象として選定しました。選定のポイントとしては以下の点を重視して選びました。 開発者ではない人でもコンテンツ運用できるか ユーザーフレンドリーなUI 日本語対応 開発者がサイト開発しやすいか APIやSDKや連携の充実度 管理がしやすいか SaaSとして提供されているか(CMS自体の管理はしたくない) SAMLやSCIM対応 費用 いくつか運用上欲しい機能が足りていない面もありましたが、今後のアップデートに期待することにして、感覚的に使えて触り心地のよかったmicroCMSを選びました。 移行後の変化 デプロイフロー Headless CMSに変えることで、いままであったファイル管理やホスティング先はどうしたかというと、こちらもマネージドのものに変えました。 ファイル管理はGUIベースからGitHub管理に変更しました。これでようやく一般的なホスティング環境をそのまま使えるようになるので、いまはAmplifyを使っています。GitHubにpushするかmicroCMSでコンテンツを編集すると、Amplifyのビルドが回るように設定しています。 リッチテキストエディタ 自社製CMSでもコンテンツのテキストフィールドにhtml直書きしてコンテンツ運用していました。ここはmicroCMSに移行しても変わらない点です。ただcssの調整ができた部分についてはリッチテキストエディタも採用しています。テーブル表記はリッチテキストエディタのほうがタグ記述ミスが少ないので運用負荷が特に下がります。 ひとつのコンテンツ内でhtml直書きかリッチテキストエディタどちらかしか使えないのではなく、必要に応じて使い分けができるのは良いポイントですね。 最後に 実はサイトを作り直したと言ってもいちから書き直したわけではなく、コンテンツ取得や利用部分のロジックは残してテンプレート言語を変更し、コンポーネント構成のためフォルダ構成も綺麗にしただけです。そのためコンポーネントごとにちゃんと独立してなかったりjQueryは残っています。 それでも自社製CMSからの脱却は済み、microCMSでコンテンツ運用は問題なくできていて、各種フローも綺麗にできたので、基盤の移し替えとしては良い結果となったと思います。前と比べかなり低コストで運用できていますし。 今回はコンテンツ管理についてお話ししましたが、ほかにもフレームワーク・テンプレート言語選定、複数環境対応などなどネタはたくさんありますので別の機会に書かせていただこうかと思います。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター