ニフティ株式会社のブログ - TECH PLAY

TECH PLAY

ニフティ株式会社

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

529

この記事は、 ニフティグループ Advent Calendar 2024 11日目の記事です。 こんにちは! 今回は、JavaScriptとTypeScriptの三大ランタイム環境であるDeno、Bun、Node.jsについて比較していこうと思います。 概要 Node.js: 2009年に登場した最も成熟したJavaScriptランタイム Deno: 2018年にNode.jsの創始者によって開発された、より安全でモダンなランタイム Bun: 2022年に登場した高速で全機能を備えたJavaScript/TypeScriptランタイム 主な特徴 Node.js 広大なエコシステムと豊富なnpmパッケージ 長年の実績と安定性 非同期I/Oに最適化 Deno セキュリティ重視(デフォルトで安全) TypeScriptのネイティブサポート URLベースのモジュールインポート Bun 高速な起動とランタイム実行 Node.jsとの高い互換性 内蔵のパッケージマネージャとバンドラー 比較 パフォーマンス比較簡単なベンチマークとして、各ランタイムで1から1000万までの数を数える処理の実行時間を比較してみましょう。 Node.js Homebrewや公式サイト( https://nodejs.org/en )などでインストールする benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する node benchmark.js 実行結果 1回目 $ node benchmark.js Starting benchmark... Counting completed in 7.381 ms Final count: 10000000 2回目 $ node benchmark.js Starting benchmark... Counting completed in 8.529 ms Final count: 10000000 3回目 $ node benchmark.js Starting benchmark... Counting completed in 8.092 ms Final count: 10000000 Deno Homebrewなどでインストールする https://docs.deno.com/runtime/getting_started/installation/ benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = performance.now(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = performance.now(); const duration = end - start; console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する deno run benchmark.js 実行結果 1回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.852 ms Final count: 10000000 2回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.905 ms Final count: 10000000 3回目 $ deno run benchmark.js Starting benchmark... Counting completed in 9.019 ms Final count: 10000000 Bun Homebrewなどでインストールする https://bun.sh/docs/installation benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する bun benchmark.js 実行結果 1回目 $ bun benchmark.js Starting benchmark... Counting completed in 7.849 ms Final count: 10000000 2回目 $ bun benchmark.js Starting benchmark... Counting completed in 9.821 ms Final count: 10000000 3回目 $ bun benchmark.js Starting benchmark... Counting completed in 14.227 ms Final count: 10000000 結果 すべてほぼ同等のパフォーマンスを示しています。 HTTPサーバー作成 今度は各ランタイムで簡単なHTTPサーバーを作成してみます。 Node.js server.jsというファイルを作成します。 const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); 実行 $ node server.js Server running at http://localhost:3000/ TypeScriptファイルを試してみる server.tsを作成する import * as http from 'http'; const server: http.Server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); 実行 実行するとエラー $ node server.ts (node:38672) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) /Users/server.ts:1 import * as http from 'http'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (node:internal/modules/cjs/loader:1350:18) at Module._compile (node:internal/modules/cjs/loader:1379:20) at Module._extensions..js (node:internal/modules/cjs/loader:1518:10) at Module.load (node:internal/modules/cjs/loader:1249:32) at Module._load (node:internal/modules/cjs/loader:1065:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:158:12) at node:internal/main/run_main_module:30:49 Node.js v22.2.0 TypeScriptをネイティブサポートしていないことが分かります。 Deno server.tsというファイルを作成します。 DenoはTypeScriptをネイティブサポートしています。 import { serve } from "https://deno.land/std@0.181.0/http/server.ts"; const handler = (req: Request): Response => { return new Response("Hello Worldn", { status: 200 }); }; console.log("Server running at http://localhost:3000/"); await serve(handler, { port: 3000 }); 実行 Denoはセキュリティを重視しているため、ファイルアクセスやネットワークアクセスにはパーミッションが必要です。 例えば、ファイル読み書きの場合は以下のようにします。 deno run --allow-read --allow-write file_io.ts $ deno run --allow-net server.ts Server running at http://localhost:3000/ Listening on http://localhost:3000/ ネットワークアクセス許可(--allow-net)を与えないで実行すると、許可してよいかと聞かれます。 $ deno run server.ts Server running at http://localhost:3000/ ┏ Deno requests net access to "0.0.0.0:3000". ┠─ Requested by `Deno.listen()` API. ┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable. ┠─ Learn more at: https://docs.deno.com/go/--allow-net ┠─ Run again with --allow-net to bypass this prompt. ┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) > Bun server.tsというファイルを作成します。 BunはTypeScriptをネイティブサポートしています。 const server = Bun.serve({ port: 3000, fetch(req) { return new Response("Hello Worldn"); }, }); console.log(`Listening on http://localhost:${server.port}`); 実行 $ bun run server.ts Listening on http://localhost:3000 DenoとBunの比較 1. モジュールのインポート:    Deno: 外部モジュールをURLから直接インポートします。 import { serve } from "https://deno.land/std@0.181.0/http/server.ts"; Bun: 組み込みのグローバル  Bun  オブジェクトを使用し、追加のインポートは不要です。 2. サーバーの設定:    Deno:  serve  関数を使用し、ハンドラー関数と設定オブジェクトを渡します。 await serve(handler, { port: 3000 }); Bun:  Bun.serve  メソッドを使用し、設定オブジェクトを渡します。 const server = Bun.serve({ ... }); 3. リクエストハンドラー:   Deno: 独立した関数として定義します。 const handler = (req: Request): Response => { ... }; Bun: 設定オブジェクト内の  fetch  メソッドとして定義します。 fetch(req) { ... } 比較ポイント 上記の比較結果では、各ランタイムの HTTP サーバー実装の違いがありました。 以下に、主要な比較ポイントをまとめました。 1. TypeScript サポート : Node.js: 追加のツール (ts-node) とタイプ定義 (@types/node) が必要。 Deno: ネイティブサポート。 Bun: ネイティブサポート。 2. セキュリティモデル : Node.js: デフォルトですべてのシステムリソースにアクセス可能。 Deno: 明示的な権限付与が必要( --allow-net フラグ)。 Bun: Node.js 互換のモデルを採用(デフォルトでアクセス可能)。 3. API設計 : Node.js: 標準的な http モジュールを使用。 Deno: モダンな Web 標準に基づいた API。 Bun: シンプルなオブジェクトベースの API。 4. モジュールシステム : Node.js: ES Modules (import 構文) を使用。 Deno: ES Modules と URL ベースのインポートを使用。 Bun: 組み込みの Bun オブジェクトを使用。 5. 実行の簡単さ : Node.js: TypeScript を直接実行するには追加のセットアップが必要。 Deno: 追加のセットアップなしで TypeScript を直接実行可能。 Bun: 追加のセットアップなしで TypeScript を直接実行可能。 結論 結論として、Node.js、Deno、Bunの3つのランタイムは、それぞれ独自の強みを持ち、JavaScriptエコシステムの多様化と進化に大きく貢献しています。 また、これらのランタイムは急速に進化を続けているため、定期的に最新の動向をチェックし、必要に応じて選択を見直すことをお勧めします。 本ブログが、読者の皆様のプロジェクト選択や技術的な意思決定の一助となれば幸いです。 より詳細な情報や具体的な使用例については、各ランタイムの公式ドキュメントを参照することをお勧めします。 明日は、Tsan0409さんの記事です。お楽しみに。
この記事は、 ニフティグループ Advent Calendar 2024 10日目の記事です。 CLIツールを社内にプライベートtapリポジトリを作成し、社内に配布しました。 意外と記事がなかったのでその方法をご紹介します。 アプリケーションのリリースに使っているのはGoReleaserです。 きっかけ ニフティ株式会社では現在Oneloginを使っています。 AWSのIAM RoleにAssumeRoleする際は以下のOneloginが提供しているツールを使っている社員がほとんどです。 https://github.com/onelogin/onelogin-python-aws-assume-role このツールはjava版もあるのですが、どちらにせよランタイムのインストールが必要です。 また比較的頻繁に更新されているわけではないため、バージョンによっては依存ライブラリの問題でセットアップが失敗するなどの問題がありました。 そこで、これをGoで作り直すことでシングルバイナリかつセットアップ不要にしつつ、brewで配布することでMacやWSL環境への導入をより簡単にしようと決めました。 サードパーティ製の他のツールを使うという手段もありましたが、私自身がインフラ管理者ではない(全社向けの選定権がない)ことや、現在使っているツールからの移行モチベーションを上げるため設定ファイルを流用することが重要だったこと、インナーソース化することでより柔軟に開発者自身が必要な機能を追加していけることを重視し、自作することに優位性があると判断しました。 Tapリポジトリの作成 バイナリを配布するためのtapリポジトリを作成します。 このリポジトリの名前は homebrew-<repo> の形式である必要があります。 https://docs.brew.sh/Taps#repository-naming-conventions-and-assumptions プライベートなtapリポジトリを作成する場合、GitHubのトークンを使ってダウンロードURLを作る必要があります。 以下のGistが非常に参考になるため、これを使います。 https://gist.github.com/ZPascal/b21c652b811872b3f56db9d54d61d6c6 GitHubPrivateRepositoryDownloadStrategy というクラスがダウンロード戦略となります。 指示通り、tapリポジトリのlib/配下に配置します。 アプリケーションの作成 今回はHomebrewで配布する部分がメインなので、ここではGoReleaserの設定について紹介します。 GoReleaserは簡単に複数のOSやCPUアーキテクチャ向けにBuildしたり、パブリッシュすることができるツールです。 https://github.com/goreleaser/goreleaser このツールを使ってGitHub Actionsでリリースしています。 アプリケーションリポジトリからtapリポジトリにpushしてPRを作成する必要があるため、PrivateAccessTokenが必要になります。 ダウンロード戦略として先ほど作成した GitHubPrivateRepositoryDownloadStrategy を使うよう指定します。 .goreleaser.yaml version: 2 before: hooks: - go mod tidy - go generate ./... builds: - main: ./cmd/ binary: onelogin env: - CGO_ENABLED=0 goos: - linux - windows - darwin archives: - format: tar.gz name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} format_overrides: - goos: windows format: zip changelog: sort: asc filters: exclude: - "^docs:" - "^test:" brews: - name: onelogin download_strategy: GitHubPrivateRepositoryReleaseDownloadStrategy custom_require: lib/private_strategy repository: owner: niftycorporation name: homebrew-tap branch: "{{ .ProjectName }}" token: "{{ .Env.PRIVATE_GITHUB_TOKEN }}" pull_request: enabled: true draft: false base: branch: master workflow name: Upload Release Asset on: push: tags: - 'v*' permissions: contents: write jobs: build: name: Upload Release Asset runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser version: "~> v2" args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PRIVATE_GITHUB_TOKEN: ${{ secrets.PRIVATE_GITHUB_TOKEN }} このWorkflowが実行されるとreleasesが作成され、tapリポジトリにPRが作成されます。 一部変更していますが、以下のようなファイルが生成されます。 GoReleaserを使わなくてもこのようなファイルがあれば配布すること自体は可能です。 onelogin.rb # typed: false # frozen_string_literal: true # This file was generated by GoReleaser. DO NOT EDIT. require_relative "lib/private_strategy" class Onelogin < Formula desc "" homepage "" version "1.0.2" on_macos do on_intel do url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Darwin_x86_64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end on_arm do url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Darwin_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end on_linux do on_intel do if Hardware::CPU.is_64_bit? url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Linux_x86_64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end on_arm do if Hardware::CPU.is_64_bit? url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Linux_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end end end brew installする まずローカルからプライベートリポジトリにアクセスするためにPrivateAccessTokenを作成し、 HOMEBREW_GITHUB_API_TOKEN という環境変数に指定します。 tapリポジトリを指定し、インストールします。 このとき、リポジトリ名の homebrew- 部分は省略することができます。 brew tap niftycorporation/tap brew install niftycorporation/tap/onelogin Brewfileには以下のように記載します。 tap "niftycorporation/tap" brew "niftycorporation/tap/onelogin" これでプライベートのtapリポジトリでバイナリを配布することができました。 まとめ ここまでプライベートのtapリポジトリを作成し、配布する方法を紹介してきました。 企業内で独自のソフトウェアを配布したいことはそれなりに多いんじゃないかと思っています。 特に以下はかなりハマりポイントでした。 tapリポジトリの命名規則 PATを使ったダウンロード戦略の作成 PATを使ったダウンロード戦略の指定 今ハマっている方や今後やろうと思っている方はぜひこれらに気をつけていただければと思います。 ここからはタイトルに関係のない今後の展望についてですが、このリポジトリは現在インナーソースとして社内公開していて利用者も比較的多いプロジェクトになっていると考えています。 しかしコントリビュータがなかなか増えず、敷居の高さや関心を引けていないことが問題かと思っています。 ツール自体メイン機能はほぼ完成していますが、爆速で作ったためリファクタリングや処理の効率化、ドキュメントの整備などやりたい事自体はまだまだある状況です。 また、普段使わない言語や領域に触れるなどエンジニアとしての成長も望めるため、コントリビュータを増やしインナーソースやOSSに興味を持ってもらえるよう活動していく予定です。 tapリポジトリについても現在配布されているのがこのツールのみということもあり寂しいので、こちらも敷居を下げより簡単に追加できるようになればいいなと想像しています。 最後に、この記事がどなたかのお役に立てば幸いです。 明日は、@hayabusa01 さんの記事です。 お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2024 9日目の記事です。 こんにちは。ニフティ株式会社の statiolake です。 私は生粋の Neovimmer なのですが、これはなぜかというと自分の開発環境を自分好みにカスタマイズすることが大好きだからです。 複雑で覚えていられない処理を自動化して隠蔽し、シンプルで明快な操作で実行できるようにする。エンジニアリングの粋と言ってもよいでしょう。この営みを最も高速にイテレートできることこそ、自分の開発環境を整えることなのです。 いきなり脱線して恐縮なのですが、先日開催された VimConf 2024 のキーノートで、Neovim プラグイン開発者でストリーマーでもある TJ DeVries 氏もそのような講演をされていました。 こちら、VimConf ながら Vim 固有の話はほぼありませんので、どなたでも楽しんでいただけると思います。語り口の軽快さはさすがストリーマーといったところ。聞いていて飽きることがありませんでした。 つい最近 YouTube に動画が上がったようなので、よろしくお願いします。 何の話だ。 ええ、そういうこともあって、私はメタプログラミングも大好きです。 複雑で書いていられないボイラープレートを隠蔽し、簡単で直感的なコードで呼び出せるようになると、なんかいいですよね。動的型付けで様々なものがオブジェクトとして扱える Python は、コードを簡単にかけるようにすることにかけては超一流といって良いでしょう。 今回はそういったメタプログラミングで使えそうなテクニックの一つとして、メソッドのフリをするオブジェクトを作る方法を学んだので、共有したいと思います。 これを使ったら何ができるかは、またいずれ別の記事にするかもしれません。 ※ なお、これは言語仕様などに立ち入った厳密な話ではないことを先んじてお断りしておきます。そこまで詳しいわけではないです… 関数のフリをするオブジェクトの作り方 いきなりメソッドに入る前に、普通の関数について考えてみましょう。 def add(a, b): return a + b print(add(1, 2)) # => 3 実は、関数のように使えるオブジェクトを作るのは結構簡単です。 確かに関数には add(1, 2) という特別な文法を与えられてはいます。しかし、それは実は add.__call__(1, 2) と同じ意味になっています。なので単に __call__ というマジックメソッドを定義してあげれば呼び出し構文が使えるようになります。 class AddFunction: def __call__(self, a, b): return a + b add = AddFunction() print(add(1, 2)) # => 3 # ^^^ AddFunction クラスのインスタンスであり、関数ではないが、呼び出せる 完璧です。 メソッドはどうして難しいのか? これができるなら、別にメソッドだって同じような気がしますよね。 class Calculator: def add(self, a, b): return a + b c = Calculator() print(c.add(1, 2)) # => 3 実際、これを先ほどのように __call__() を持ったオブジェクトで置き換えても一見、使えているように見えます。 class AddFunction: def __call__(self, a, b): return a + b class Calculator: # def add(self, a, b): # return a + b add = AddFunction() c = Calculator() print(c.add(1, 2)) # => 3 しかし、これには致命的な欠点があります。それは add() がインスタンスに結びついていないことです。 メソッドなのに self に結びついていない 例えば電卓の M+ ボタンのように、Calculator クラス内に値を保持するメソッドを作りたいとします。しかし、今の形だとそれが実装できません。 AddFunction の __call__ にある self はあくまでも AddFunction であって Calculator ではないからです。 そもそも振る舞いが違う Python のメソッドは少し不思議な性質を持っています。 add() がメソッドの場合、 class Calculator: def add(self, a, b): return a + b c = Calculator() print(Calculator.add(c, 1, 2)) # => 3 print(c.add(1, 2)) # => 3 このように インスタンス.メソッド名(引数...) で呼び出したときは自動で self にオブジェクトが入り、 クラス名.メソッド名(インスタンス, 引数…) で呼び出したときには self にあたるものも自分で指定することができます。 ところが、今作ったオブジェクトを使ってみると… class Calculator: add = AddFunction() c = Calculator() print(Calculator.add(c, 1, 2)) # ^ TypeError: AddFunction.__call__() takes 3 positional arguments but 4 were given print(c.add(1, 2)) # => 3 なんと、3 つの引数で呼び出すところが例外になってしまいました。確かに __call__() には 2 つの引数しか入れられないので、エラーになるのは理解できます。 しかし、ならばなぜメソッドはエラーにならないのでしょうか? 2 つの問題というふうにお伝えしましたが、本質的には 1 つだけです。 結論から言えば、魔法がかかるのはメソッド呼び出し時です。メソッド add の本当の姿は、 クラス名.メソッド名 で呼び出されるのと同じ、 self を含めた 3 引数の関数と考えてください。 つまり、Python が インスタンス.メソッド名(引数...) のときだけ勝手に第一引数にインスタンスを結びつけてくれている、よって 2 引数の関数のように使えている、ということになります。 普通のオブジェクトがメソッドのふりをするためには、この仕組みを自分で実装しなければいけません。そしてそれを可能にする仕組みがデスクリプタです。 デスクリプタ ここから少し込み入った話になりますが、Python にはデスクリプタというものが存在します。標準にある @property デコレータの裏側で使われている機能でもあります。 デスクリプタ は、メソッドやフィールドを取得しようとしたときに割り込んで好き勝手なオブジェクトを返すことができる機能です。まあ他にもできることはあるんですが、ここでは割愛します。 たとえば、以下の RandomValueDescriptor を使うと参照するたびに値が変わっている不思議なフィールドを作ることができます。 class RandomValueDescriptor: def __get__(self, instance, owner): return random.randint(1, 100) class Calculator: value = RandomValueDescriptor() c = Calculator() print(c.value) # => 34 (例) print(c.value) # => 84 (例) 普通のフィールドの場合、 value に代入してもいないのに値が変わっているということはありません。 しかしデスクリプタを使うと、 value にアクセスしたときに毎回 __get__() 関数を通してくれるようになります。そこではあらゆるコードが実行できるため、このように、ランダムな値を返すこともできるわけです。 そして、 __get__ にはいくつかの引数があります。 ここで着目したいのは instance です。ここには インスタンス.フィールド名 で呼ばれた時の インスタンス が与えられています。さらに、クラス名を経由して呼ばれたときは None が入っています。 class InstanceDetectorDescriptor: def __get__(self, instance, owner): if instance is None: return "without instance" return "with instance" class Calculator: instance_detected = InstanceDetectorDescriptor() c = Calculator() print(Calculator.instance_detected) # => without instance print(c.instance_detected) # => with instance なるほど、 instance を見てインスタンスがあるかないかを取ることができる。…もうおわかりですかね? デスクリプタでメソッドのフリをする ここまでの考察を踏まえて、以下のうち memory_add() メソッドをオブジェクトに置き換えてみましょう。 class Calculator: def __init__(self): self.memory = 0 def memory_add(self, delta): self.memory += delta c = Calculator() Calculator.memory_add(c, 1) # memory: 0 + 1 = 1 print(c.memory) # => 1 c.memory_add(1) # memory: 1 + 1 = 2 print(c.memory) # => 2 この中の、引数の数がインスタンスかどうかで変わってくる部分が難しいのでした。しかし今の我々には、デスクリプタを使って以下のことが可能です。 参照された瞬間を __get__() で割り込むことができる __get__() の中でインスタンスの有無がとれ、好きなものを返せる であれば、こうでしょう。 インスタンスが与えられなかったときは、普通の関数を返す。 インスタンスが与えられたときは、それを第一引数に紐づけ、一つ引数が減った関数を返す。 class MemoryAddMethodDescriptor: def __get__(self, instance, owner): def inner(self, delta): self.memory += delta if instance is None: # インスタンスがない場合 - クラス名.メソッド名(インスタンス, 引数...) で呼び出された # 普通に関数をそのまま返す return inner # インスタンスがある場合 - インスタンス.メソッド名(引数...) で呼び出された # 第一引数にインスタンスを与え、引数を一つ減らした関数を作って返す # ここでは手っ取り早く lambda 式を使っている return lambda delta: inner(instance, delta) class Calculator: def __init__(self): self.memory = 0 memory_add = MemoryAddMethodDescriptor() いけるはずです。さて、実行してみると… c = Calculator() Calculator.memory_add(c, 1) # memory: 0 + 1 = 1 print(c.memory) # => 1 c.memory_add(1) # memory: 1 + 1 = 2 print(c.memory) # => 2 成功です!しかも、きちんとインスタンスにもアクセスできていますね。 ここでは __get__() が返すものをただの関数にしているので、あまり便利さが実感できないかもしれません。 しかし、ここで先程の「関数のふりをするオブジェクト」をさし挟むことができてしまうとなれば、どうでしょうか? あれも基本的には普通のクラスの普通のオブジェクトですから、その中にメソッドを追加することもできますし、そうすると… いろいろなことができてしまいます。 長くなってしまうので、今日はここまでですね。 終わりに 今回はメソッドのふりをするオブジェクトを作ってみました。もちろん、これは単体であれば普通にメソッドを定義すれば良く、大した意味を持ちません。しかし、オブジェクトには好き勝手に別のフィールドやメソッドを増やすことができ、自由度が高まります。 たとえば、私はこの機能を単体テストのためのモッククラスに応用してみています。 user = MockUserRepository() with user.get_all.patch([User(id=1, name="Alice")]): assert user.get_all() == [User(id=1, name="Alice")] この例では、 user.get_all の戻り値を一時的に上書きする機能を提供しています。 get_all はメソッドのように呼び出せますが、そこに patch() というメソッドがあるのは、今回の方法を使って「メソッドのふりをしたオブジェクト」に差し替えているからです。具体的には、また別の記事を書くかもしれません。 そんなわけで、ちょっとした小道具でした。長らくお付き合いいただきありがとうございます。みなさんも何かご存知の小道具があれば、ぜひ教えてください!あと、TJ DeVries さんの講演もぜひ見てみてください。 明日は、dev-shimada さんの「社内Tapリポジトリを作り自作ツールをHomebrewで配布する」です。 お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2024 7日目の記事です。 はじめに こんにちは、会員システムグループでエンジニアをしている山田です。 皆様、CloudFront Functionsは使っていますでしょうか。 CloudFrontでエッジ処理を行いたい場合、 Lambda@Edge もしくは CloudFront Functions のいずれかを使用することになります。CloudFront Functionsはランタイムの強い制限を代償に、Lambda@Edge比で料金が約1/6と、安価に使用することができます。ニフティ社内でも静的サイトでのリダイレクト処理などに活用しています。 軽い処理であるとはいえ、継続的な運用を行うには型チェックやテストの恩恵を受けたいものです。 本記事はそのための環境をどう整えるか、という内容になります。 結論 結論から先に述べると TypeScriptだけで良い場合: tscでトランスパイルする テストもしたい場合: バンドラーでバンドル後、 正規表現でexportを除去 する という形になります。 前提 前提として、CloudFront FunctionsはサーバサイドJavaScriptではあるものの、 Node.jsではない ランタイムで動作します。このため、以下のような文法上の制約があります。(JavaScript 2.0ランタイムの場合) ECMAScript 5ベース const/let、async/awaitなど一部のES6文法にも対応 ES Modules非対応 CommonJS向けのexport構文がエラーとなる module.exports はmoduleがundefinedのためエラー 1ファイルのみデプロイ可能 これらの制約を満たすように作る必要があります。 対応方法 型チェックのみで良い場合 型チェックができればよい場合は、TypeScriptの導入のみで良いです。 pnpm add -D typescript tsconfigの設定は以下のようにします。 { "compilerOptions": { "target": "es5", ... "baseUrl": "./src", "outDir": "./dist" }, "include": ["**/*.ts"], "exclude": ["node_modules", "dist"] } targetはES5にしておきます。ES6文法の多くはCloudFront Functionsでも使用可能ですが、一部非対応の文法を使用しても気づかないため、ES5にしておくのが無難でしょう。 次にCloudFront Functions用の型定義を用意します。npmパッケージとして 公開されているもの があるため、これを使います。手元の環境では型情報をうまく参照できなかったため、内容をコピーして利用しています。 declare namespace AWSCloudFrontFunction { interface Event { version: '1.0'; context: Context; viewer: Viewer; request: Request; response: Response; } interface Context { distributionDomainName: string; distributionId: string; eventType: 'viewer-request' | 'viewer-response'; requestId: string; } interface Viewer { ip: string; } interface Request { method: string; uri: string; querystring: ValueObject; headers: ValueObject; cookies: ValueObject; } interface Response { statusCode: number; statusDescription?: string; headers?: ValueObject; cookies?: ResponseCookie; } interface ValueObject { [name: string]: { value: string; multiValue?: Array<{ value: string; }>; }; } interface ResponseCookie { [name: string]: { value: string; attributes: string; multiValue?: Array<{ value: string; attributes: string; }>; }; } } CloudFront Functionsのソースには上記の型を指定します。 function handler( event: CloudFrontFunction.Event ): AWSCloudFrontFunction.Request | AWSCloudFrontFunction.Response { ... } あとはtscでコンパイルすればJavaScriptファイルが得られます。型チェックのみを行う場合は別途コマンドを用意しておくとよいでしょう。 tsc テストもしたい場合 テストも行いたい場合、テスト対象関数をexportする必要が出てきます。またファイルを分割したくなることもあるでしょう。このため、基本的にバンドラを使うことになります。 しかしながら、バンドラの対応するいずれの出力形式でも問題を抱えています。 ESM形式: handlerのexportが必須、 export ~ が文法エラー CJS形式: handlerのexportが必須、 module.exports = ~ がmodule変数の未定義によりエラー IIFE形式: 関数定義が大きく変わる ESM、CJS形式ではexportされていない関数は不要と判断され、バンドル後の結果に残らなくなります。一方でexportすると出力結果にexportの文法が残るため、エラーを引き起こしてしまいます。これらは esbuildのissue でも議論されていますが、いずれもHackyな回避策となっています。 そこで、 ESM形式で出力したものからexportを除去する というアプローチを行います。 バンドラとして今回はtsupを使用します。 pnpm add -D tsup 以下のように設定を行います。 import { defineConfig } from 'tsup'; export default defineConfig({ entry: [ // 出力したいhandlerがあるファイル群 ], format: 'esm', platform: 'node', splitting: false, sourcemap: false, minify: false, clean: true, }); entryは複数指定できるため、複数の関数ファイルを一括して生成することが可能です。 またビルド後のファイルからexportを取り除くスクリプトを用意しておきます。 (コードによっては正規表現を調整する必要があるかもしれません) /* ビルド後のファイルを加工するスクリプト usage: node scripts/postbuild.js {build_dir} CloudFront Functionsはexportを解釈できないため、ビルド後のファイルからexportを削除する */ import * as fs from 'node:fs'; import * as path from 'node:path'; // export文を判別する正規表現 const regex = /export\s*{[^}]*};?/gs; const target = process.argv[2]; const targetPath = path.resolve(target); if (!fs.statSync(targetPath).isDirectory()) { console.error('Target is not a directory'); process.exit(1); } const files = fs.readdirSync(targetPath); for (const file of files) { const filePath = path.join(targetPath, file); const stats = fs.statSync(filePath); if (stats.isDirectory()) { continue; } const ext = path.extname(filePath); if (ext !== '.js') { continue; } console.log('Removing export statement from', filePath); const fileContents = fs.readFileSync(filePath, { encoding: 'utf-8' }); const newContents = fileContents.replace(regex, ''); fs.writeFileSync(filePath, newContents); } あとはビルド後にこのスクリプトを実行するようにしておけばOKです。 tsup && node postbuild.js ./dist まとめ CloudFront FunctionsをTypeScript・テストに対応した環境で書くための方法について解説しました。 どうしてもハック感のある方法を取らざるを得ないのですが、行っていることはシンプルなので今のところこれで運用することができています。テストにも対応できたことで、リダイレクトルール変更時のデグレ防止にも役立っています。 CloudFront Functionsの中身が複雑になってきて、動作の担保に不安がある方は試してみてはいかがでしょうか。 次回はnunaさんの記事です。お楽しみに。
この記事は、 ニフティグループ Advent Calendar 2024  6日目の記事です。 はじめに こんにちは。ニフティ株式会社の並木です。 皆さんは、タスクの割り当てや順番決めに頭を悩ませたことはありませんか? 私はこれまで、オンラインのあみだくじやルーレットツールを利用していましたが、あみだくじの結果を目で追ったり、何度もルーレットを回したりと、意外と手間がかかっていました。 「もっと簡単に、一発で機械的にタスクを割り当ててくれるツールが欲しい!」という思いから、SlackのワークフローとZapierを使用して、タスクを簡単に割り当てるツールを作成しました。 今回は、その方法についてご紹介いたします。 Slackのワークフローを作成する Slackでワークフローを新規作成します。 「Slack内のリンクから」を選択してください。 ステップを追加します。 「フォーム」を選択してください。 フォームの内容を設定します。 質問1でタスク名を入力するよう設定します。 質問2で人の名前を入力するよう設定します。 質問1、質問2で入力された内容を、slackにメッセージとして送信できるよう設定します。 「メッセージ」を選択してください。 「チャンネルへメッセージを送信する」を選択してください。 今回は「ワークフローが使用されたチャンネル」に、以下の内容のメッセージが送信されるよう設定します。 これでワークフローの作成は完了です! Zapierを設定する まずはトリガーの設定をします。 Appは「Slack」、Trigger eventは「New Mention」を選択します。 Slackに「タスク簡単割り当てワークフローを起動しました。」という文章が投稿されたらZapierが起動するよう設定したいので、Highlight Wordには「タスク簡単割り当てワークフローを起動しました。」を設定します。 Trigger for Bot Messages?は、Botによる投稿もトリガーの対象に含めるかを確認していますので、今回は「True」を選択してください。 タスクをランダムに割り当てる処理を書いていきます。 Appは「Code by Zapier」、Action eventは「Run Python」を選択します。 言語はPythonのほかにJavascriptを選択することもできます。 Input Dataの右側の入力ボックスにSlackで投稿された文章を設定し、左側の入力ボックスにキー名を入れます。 以下画像ではキー名を「original_text」としましたが、どんな名前をつけても良いです。 Codeの中で input_data['original_text'] と書くことで、Input Dataで設定したデータを取り出すことができます。 def custom_shuffle(lst): # 簡易的な疑似乱数生成 seed = sum(ord(c) for c in ''.join(lst)) for i in range(len(lst) - 1, 0, -1): seed = (seed * 1103515245 + 12345) & 0x7fffffff j = seed % (i + 1) lst[i], lst[j] = lst[j], lst[i] return lst # ワークフローを使った人の名前とタスクと人名を取り出す text = input_data['original_text'] lines = text.split('n') mention = lines[0] tasks = lines[4].split(',') people = lines[6].split(',') # タスクと人名をシャッフル tasks = custom_shuffle(tasks) people = custom_shuffle(people) # 結果を生成 result = "" for i in range(len(tasks)): person_index = i % len(people) # 人数を超えた場合は最初から割り当て直す result += f"{tasks[i]}:{people[person_index]}n" output = [{'result':result, 'mention':mention}] タスクを割り当てた結果を、Slackに投稿します。 Appは「Slack」、Trigger eventは「Send Channel Message」を選択します。 ChannelにSlack投稿するチャンネル名、Message textにSlack投稿するメッセージを入力します。 Codeの最後に output = [{'result':result, 'mention':mention}] と記載しておくことで、タスクを割り当てた結果とワークフローを使った人の名前を取り出して、メッセージに含めることができます。 これでZapierの設定は完了です! 実際に動かしてみる ワークフローを実行し、タスク名と名前をカンマ区切りで入力します。 「送信する」を押下すると、ワークフローを実行したチャンネルに、以下のメッセージが投稿されます。 さらに数秒待つと、タスクを割り当てた結果が「#bot_タスク割り当て」チャンネルに投稿されます! おわりに 以前の記事「 ZapierでX投稿するAPIを呼び出して結果をSlackに通知してみた 」では、Zapierから Slackへの連携を試しましたが、今回は逆に Slackの投稿をトリガーにして Zapierを起動させる方法を初めて実践しました。 今後も、SlackとZapierの連携を活用して、業務効率化や情報共有の改善に役立つツールを開発していきたいと思います。 明日は、 jimmysharp さんの「CloudFront FunctionsでもTypeScript&テストしたい」です。 お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2024  5日目の記事です。 はじめに ニフティでWEBサービスの開発・運用を担当している渡邊です。 最近、運用の手間をかけずにWEBサービスを立ち上げる方法はないかなと考えていたところ、AWS App Runner(以下App Runner)に出会いました。 この記事では、App Runnerの特徴や他のサービスとの違いについて書いていきます。 App Runnerとは App RunnerはAWSのサービスの1つで、コンテナアプリケーションを簡単にデプロイ・運用できるプラットフォームです。 GitのコードやコンテナをAWSにアップロードするだけで環境が作れるので、インフラ周りを気にする必要がありません。 ECS(Fargate)をベースにしているので、負荷に応じて自動的にスケールするのが特徴です。 また、VPCやロードバランサーの設定が不要なので、ネットワークの知識がなくても使えます。 他のAWSサービスとの違い AWSには似たようなサービスがいくつかありますが、App Runnerは「とにかく簡単に使える」ことを重視して作られています。 主なサービスを比べてみましょう。 サービス メリット デメリット Amazon EC2 • サーバーを自由に設定できる • OSやミドルウェアを自分で選べる • どんな用途でも使える • 管理が大変 • 予想以上に費用がかかることも Amazon ECS •コンテナ運用に便利 • Fargateなら運用が楽 • 自動でスケールする • インフラの知識が必要 • 初期設定が面倒 Elastic Beanstalk • デプロイが簡単 • AWSの他サービスと連携しやすい • サーバー管理不要 • 自動でスケールする • AWSへの依存が強くなる • 初期設定が面倒 AWS App Runner • デプロイが簡単 • シンプルな設定項目 • 運用の手間なし • GitやDockerからすぐ使える • 自動でスケールする • 細かい設定ができない • AWSへの依存が強くなる 設定項目 設定する項目が少なくて、画面の流れに沿って進めるだけで使えます。 WAFも設定できるので、IP制限をかけたい社内向けのアプリも作りやすいです。 コスト フロントとバックエンドを分けたアプリを例に、コストを計算してみました。 トラフィック: 月間50万リクエスト 1リクエストあたり平均200ミリ秒 リソース要件: フロントエンド:1 vCPU、2GBメモリ バックエンド:2 vCPU、4GBメモリ サービス タスク/インスタンス ALB NAT Gateway データ転送 合計 ECS Fargate $138.66 $20.20 $32.40 $57.36 $248.62 EC2 $46.80 $20.20 $32.40 $57.36 $156.76 App Runner $195.75 不要 不要 不要 $195.75 計算式 ECS(fargate) フロントエンド vCPU コスト: $0.05056 × 1 vCPU × 750時間 = $37.92 メモリ コスト: $0.00553 × 2GB × 750時間 = $8.30 合計:$37.92 + $8.30 = $46.22 バックエンド vCPU コスト: $0.05056 × 2 vCPU × 750時間 = $75.84 メモリ コスト: $0.00553 × 4GB × 750時間 = $16.60 合計:$75.84 + $16.60 = $92.44 EC2 フロントエンド: $0.0208 × 750時間 = $15.60 バックエンド: $0.0416 × 750時間 = $31.20 データ転送の仮定条件 ALB経由(クライアント → Fargate): 平均リクエストサイズを1MBと仮定 データ転送量:500,000リクエスト × 1MB = 500GB NAT Gateway経由(Fargate → インターネット): データ転送量:10GB/月 データ転送コスト ALB 経由: (500GB - 1GB無料枠) × $0.114 = $56.91 NAT Gateway 経由: 10GB × $0.045 = $0.45 合計データ転送コスト: $56.91 + $0.45 = $57.36 コスト面ではEC2が最も安価ですが、運用の手間を考えるとApp Runnerも悪くないかなと思います。 App Runnerの裏側はECS(Fargate)のため、同じ環境でありながら安くなることがわかります。 さらに、App Runnerは実行時間ベースの課金体系を採用しているため、コストの予測が容易という利点もあります。 App Runnerの制限と考慮すべきポイント ARM64をサポートしていない 記事執筆時点ではARM64のコンテナイメージには対応していないため、x86_64アーキテクチャのイメージを使用する必要があります。 ARM64は、コストとパフォーマンスの両面でメリットがあるため、今後の対応に期待です。 カスタマイズ性の制限 とにかく簡単に使えることを重視しているので、細かい設定はできません。 特にVPC接続やログ設定などの高度な構成が必要な場合、作業が複雑になることがあります。 オートスケーリングの指標が限定的 同時実行数でしかスケールできず、CPUやメモリの使用率では制御できません。 そのため、特定のアプリケーションではスケーリング要件との不一致が生じ、効率的な運用が難しくなる可能性があります。 サイドカーコンテナ非対応 サイドカーコンテナが使えないので、ログ収集やモニタリングをする時に工夫が必要です。 まとめ コンテナを使うのが当たり前になってきた今、シンプルなWEBサービスを作るならApp Runnerはかなり使えると思います。 実際に試してみたら、約20分ほどでアプリケーションがAWS上で動作できるようになりました。プロトタイプや社内ツールを作るのにぴったりですね。 インフラの心配をせずに開発に集中できるのが、個人的にすごく気に入っています。 ただ、細かい設定やスケーリングが必要な場合は、ECSやEC2も検討した方がいいかもしれません。 参考 AWS App Runner AWS Fargate Amazon EC2 AWS Elastic Beanstalk
はじめに ニフティグループ Advent Calendar 2024 2日目の記事です ! はじめまして! 私はサポシスと呼ばれるチームに所属しており、普段カスタマーサポートセンター(以降CS)向けのアプリケーションの開発と運用を担当しています。 CS向けのアプリケーションはパッケージアプリケーションを利用するだけでなく、完全内製のアプリケーションの作成なども行っています。サポシスでは、これらの内製ツールのデプロイは Terraform と AWS SAM を使用しています。 本記事では、それらのツールを選択した理由と、実際の使ってみた開発メンバーの声を紹介していきたいと思います。 IaCの選択肢 デプロイ先はAWSとします。AWSで利用可能なIaCの一覧は以下の通りです。 Cloudformation AWS CDK AWS SAM AWS Chalice Serverless Frameworks Terraform Pulumi 各ツールの説明については、ここでは割愛します。 現在サポシスでは、ECSなどを含むアプリケーションは Terraform、Lambdaのみで完結するツールはAWS SAMを使用しています。 ツールの選定理由 それぞれのツールを選んだ理由について、各ツールの長所と短所を交えながら説明していきます。 Terraform 冪等性が担保される Terraformは宣言型のIaCとして設計されており、状態をtfstateファイルを用いて管理します。デプロイにはAWSのAPIを利用しており、APIの呼び出し結果を元に、tfstateファイルを更新することで冪等性を担保しています。また、デプロイ時にはtfstateファイルとAWS上のリソースを比較し、差分がある場合のみデプロイを実行するため、無駄なデプロイを防ぐことができます。 リソースのAPIコール順序はTerraform側で自動的に制御されるため、コード内での記述順序を気にする必要はありません。 社内・社外ともにナレッジが豊富 Terraformはニフティ内で5年以上前から利用されており、多くの社員が使い慣れているツールです。そのため、トラブルが発生した際も社内の経験者に相談することで解決できることが多く、社内ドメインに沿ったベストプラクティスなども成熟しつつあります。 また、社外でも広く利用されているため、インターネット上の情報も豊富で、トラブルシューティングが容易です。 モジュール分割機能が便利 モジュールを再利用可能な単位に分割でき、環境依存の設定を外側に分離することで、本番環境と開発環境のデプロイを効率的に管理できます。 サーバーレスアプリの開発には向いていない Terraformはサーバーレスアプリケーションの管理が得意ではありません。特にLambdaに関しては、1つの関数に対して多くのプロパティを設定する必要があり、また、デプロイに必要なzipファイルのビルドプロセスも複雑になりがちです。そのため、私たちのチームではこれらの課題に対処するためにAWS SAMを採用しています。 コラム:動画紹介 Terraformのメリットについて、もっと詳しく知りたい方は、うちのメンバーが分かりやすく解説しているTech talkの動画もチェックしてみてください! AWS SAM 各種パッケージの管理が容易 AWS SAM はデプロイ前にビルドプロセスが実行します。その際に必要なパッケージのインストールとデプロイ用のzipファイルの作成が自動的に行われます。 こちらのbuildはオプションでコンテナ上でも行うことができるため、build環境に依存しない作りにすることができます。 開発機能が豊富 ローカル環境での実行・テストが可能で、コンテナベースの動作により環境依存性を排除できます。また、syncというリモートのAWS上とローカルのコードを動悸する機能も搭載されており、開発効率を向上させることができます。 チーム間での管理が容易 CloudFormationを利用して管理されているため、tfstateファイルといった依存関係管理ファイルをS3にアップロードする必要がありません。また、リソースの削除もCLIだけでなく、AWSコンソールから一括で行うことができます。 大規模アプリケーションは不向き CloudFormationをベースとしているため、モジュール化して分離することがほぼ不可能となっています。また、YAML形式で書かれているため、コードが冗長になってしまい、可読性が低下することもAWS SAMの課題です。 実際に運用してみて チームメンバーからの感想を聞いてみました。 コンソールでの作業手順の作成が不要になったのは嬉しい デプロイ時はIaCを通さなければいけないので、全員が変更履歴を追えるようになった Git(GitHub)で管理できるようになったので、バージョン管理ができるようになった デプロイ時は環境差分のみに集中すればよくなったので、非常にリリース時のミスが減った AWS SAMに関しては、実行まで手元のエディタで全て完結するのは開発体験としていい デプロイが遅いので、待たなければいけないのは不便(lambdaのコピペは直ぐにすんだので) 管理が楽になると回りから聞いていたけど、実際は思っていたより楽ではなかった IaCが読めるようになるまでの学習コストが高い コードで全て管理できるが、グラフィカルに依存関係を見ることができないのは残念 IaCの導入によって管理効率や変更履歴の追跡が向上した一方で、学習コストやデプロイ時間などの運用面での課題も見えてきました。 まとめ 本記事では、ニフティのサポシスチームにおけるIaCツールの選択と運用について紹介しました。Terraformは冪等性の担保や豊富なナレッジ、モジュール分割機能など、大規模なインフラ管理に適している一方、AWS SAMはサーバーレスアプリケーションの開発に特化した機能を提供しています。 実際の運用では、変更履歴の追跡やバージョン管理、環境差分の管理が容易になるなどのメリットが確認できました。一方で、学習コストやデプロイ時間、可視化の課題なども浮き彫りになりました。 これらのツールを目的に応じて使い分けることで、より効率的なインフラ管理が可能になっています。今後も開発者の声に耳を傾けながら、より良い開発環境の整備を進めていきたいと思います。
初日から遅刻してすみません。12/1が日曜始まりであることを完全に忘れていました。 ニフティでスクラムエバンジェリストをしている西野です。業務では2チームのスクラムマスターをしています。 最近、シチュエーショナルリーダーシップの本を読んで実践してみたので、今回はスクラムにおけるリーダーシップについてお話ししたいと思います。 スクラムチームにリーダーは不要? 「スクラムマスターはチームリーダーではない」「スクラムチームには上下関係がないので、いわゆるリーダーはいない」という話を聞いたことはないでしょうか。 (スクラムガイドには「スクラムマスターは真のリーダーである」とも書かれてあり、混乱するかもしれませんが、これについては後述します) リーダーってなんだろう? リーダーと聞いてどんなイメージが浮かびますか? 社会人ならチームリーダーや上司、学生の頃なら部活の部長や生徒会長が身近なリーダー像でしょうか。最近では新しい総理大臣のリーダーシップにも注目が集まっています。 いずれにしろ、リーダーとはチームをゴールに導く力があり、組織の意思決定を行い、人が後ろをついていきたくなるようなスター性、カリスマ性を持っているようなイメージがあります。 そんな理想的なリーダーに自分がなれと言われたら困りますが、身近にいてくれたらどれほど頼もしいでしょうか。スクラムでは、そのようなリーダーを「不要」としているわけではありません。 リーダーと似たような肩書きとして「ボス」もあります。ボスという言葉には「ふんぞりかえって椅子に座り、権威的であり、指示はするが直接手は動かさない人」のようなイメージがあります。 この「ボス」と一緒に働くのは(たとえボスに心酔しているとしても)楽しいよりは苦しそうなイメージがあります。 一方で、もし「頼りになるリーダー」と一緒に働けたら、より安心して、幸せに働けそうなイメージがわくのではないでしょうか。 スクラムチームにボスは不要ですが、リーダーは必要です。 さらに、スクラムチームのリーダーはひとりだけではありません。リーダーシップが発揮できる人がいればいるほど、チームのアジリティが増していくのです。 この「チームにリーダーがたくさんいる」とはどんな状態でしょうか。 柔軟なリーダーシップスタイル 自分がスクラムにおけるリーダーシップについて意識するようになったのは、 Regional Scrum Gathering Tokyo 2022 1日目基調講演にあったAgile Program Managementというセッションがきっかけでした。 このセッションで「(スクラムチームは)全員がリーダーであり、リーダーとフォロワーがシーンによって切り替わる」という話を聞き、参加者同士でリーダーシップの解釈についてかなり盛り上がった記憶があります。 この話を聞いた当初は、全員がリーダーという状況がイメージできませんでした。全員が意思決定者という状況では、進むものも進みません。「船頭多くして船山に上る」という諺もあるくらい普遍的なアンチパターンです。 ここで大事なのは、全員がリーダーという点よりも「 リーダーとフォロワーがシーンによって切り替わる 」という点です。 例えば、あるシーンではiOSが得意なAさんがリーディングするが、別のシーンではAndroidが得意なBさんがリーディングするという状況を指しています。 この場合、AさんもBさんもどちらもリーダーシップを発揮しています。しかし、AさんもBさんも常にリーダーではなく、お互いが得意なシーンごとにリーダーをサポートする側(フォロワー)にまわっています。 技術領域以外でも、説明が上手なCさんにチーム外との交渉をお願いしたり、精度が求められる仕事はゆっくりだが確実に仕事をこなしてくれるDさんを中心にお願いしたりするなど、成熟したチームではその人の特性や得意分野に応じて自然と場をリーディングする人が変わっていきます。 シチュエーショナルリーダーシップという考え方 当時この「リーダーとフォロワーがシーンによって切り替わる」という考え方に感銘をうけたのですが、先日同僚から「新1分間リーダーシップという本が良い」と聞いて読んだところ、この「柔軟なリーダーシップスタイル」についてわかりやすく書かれていたので紹介します。 人がどういう成長過程にあるかにあわせて、発揮するリーダーシップを変える 例えば特定の分野が未経験の新入社員と、経験済み10年目の社員が同時にチームにアサインされたときに、業務に対して同じ教え方をするでしょうか。 1年目のメンバーにはかかりきりで教えることになるでしょうし、10年目のメンバーには逆に「もっと効率的なやり方はないか」と教えを乞うかもしれません。 このような単純な例だとわかりやすいですが、現実はもっと複雑です。 対応者の分野ごとの熟練度とリーダーの接し方がかみあわないと、「わかっているのに細かく指示をしてくる」「ぜんぜんわからないのに何の指示もなく途方にくれる」といったチーム内不和がうまれます。 意識合わせ(アラインメント)対話をする その人の熟練度の認識とリーダーの接し方で不和を生まないために、リーダーは1on1の場でこの認識を合わせます。スクラムの場合は、まずはスクラムマスターとチームメンバーで1on1をするのが良いでしょう。 自分も月1の頻度でチームメンバーと1on1をしているので、実際に以下について聞いてみました。 成長段階の認識合わせ 「新1分間リーダーシップ」の本では、仕事における発達の過程を以下のように定義しています。 D1:意欲満々な初心者 メンバーの状態:技能が低く、意欲が高い 仕事のやりはじめ。なにもわからないが、指示にどんどん従って仕事を覚えるやる気に満ち溢れている。 リーダーの接し方:細かく具体的に指示を出す「指示型」で接する。リーダーが自らやってみせるシーンもある。 D2:期待が外れた学習者 メンバーの状態:技能が低〜中で、意欲は低い 学習が進んで技能は身についてくるが、さらに見えてきた難しさや自分の技能の追いつかなさなどでやる気が低下している リーダーの接し方:「指示型」と「支援型」どちらも行う。指示の背景にある意図を説明し、メンバーの意見を聞いて意思決定に加わるよう促す。 D3:慎重になりがちな貢献者 メンバーの状態:技能が中〜高で、意欲は不安定 できることが増えてきて、仕事に対する意欲が戻ってくる。しかし、ひとりで回せるほどの技能はなく自信をもって取り組めない。 リーダーの接し方:「指示型」は少なく、「支援型」が多い。メンバーとリーダー共同で意思決定を行う。リーダーは聞き役に回ってアイデアを引き出す。 D4:自立した達成者 メンバーの状態:技能が高く、意欲も高い この分野においては何でも任せろ!という状態。自信もある。 リーダーの接し方:意思決定も含めてその人に任せ、指示はしない。その人の貢献を評価し、成長を応援する。 このように、学習段階に応じてリーダーの接し方が変わるということを説明した上で「自分がどの段階にいるか」をメンバーに確認してもらいます。 本来はタスクごとに細かく認識合わせをしたほうがいいのですが、まずは「今自分がいるスクラムチームの仕事においてどのくらいか」という粒度から始めました。 また、スクラムマスターの私から見てどう見えているかということも伝えて相互に認識合わせをしました。 自分がジョインしている2つのチームでやってみたところ、相互の認識はほぼ一致していましたが、ズレる場合は本人が思うより私の評価の方が高く感じていることが多かったです。 基本的にはメンバーの意見を重視しますが、過度に低く評価しているようであれば、事実を交えてもっとできていることを伝えることも行いました。 もし1on1のコーチ側が思うよりメンバー自身の評価を高く置いている場合は、メンバーの認識そのままで目標を立ててよいそうです。高い目標に向けて頑張ろうとするので、次第に本当にその評価に追いつくと本にありました。 全員がリーダーに「なれる」チームをめざす 上記の認識合わせはまだ1回やっただけなので、これによって具体的にチームが変わったということは現段階では起きていません。ただ、1on1の始まりのフェーズで今後も使っていきたいと思っています。 自分の立ち位置を示すものとしてわかりやすいこともありますが、個人的には、まずはこのような考え方を通じて、可変するリーダーシップのあり方をメンバーに伝えていくことが大事だと考えているからです。 冒頭に「スクラムチームにリーダーは不要?」と書きましたが、スクラムチームには、ボスや固定のリーダーは不要です。シーンに応じて誰でもリーダーの帽子を被れるようにします。 これは「スクラムマスターは真のリーダーである」というスクラムガイドの言葉とも矛盾はしません。私はこの言葉を、スクラムマスターは、チームや組織のあり方を良くできそうなトライアンドエラーを 誰よりも率先して行う という意味で捉えています。 例えば、シチュエーショナルリーダーシップをスクラムマスターが率先して実施し、それを見てチームメンバーがリーダーシップを発揮してくれるとしたら、それはスクラムマスターが真にチームをリーディングしている状態といえるのではないでしょうか。 リーダーとは、その名の通り先頭に立ってものを引っ張る人です。ずっと一人でやっていたら疲れてしまうし、フォロワーも一人の背中を見て学べることは限られてしまいます。 シーンに応じて色々な人がリーディングできれば、色々な人の背中を見て学びをもっと増やすことができます。また、自分がリーディングしているときも、後ろにはリーダーになれるくらい強力なフォロワーがついているという安心感もあります。 スクラムチームは決してリーダーが不在なチームではありません。シーンに応じて誰でもリーダーの帽子を被ることができるチームなのです。 シチュエーショナルリーダーシップという考え方は、自律的なチームを育むために良い足掛かりになると思います。 スクラムにおけるリーダーシップのあり方を伝えるために、1on1などのシーンで、シチュエーショナルリーダーシップの説明とお互いの「成長段階の認識合わせ」にぜひトライしてみてください。
この記事は、 ニフティグループ Advent Calendar 2024 3日目の記事です! 2日続けての投稿になります。 はじめに Azure も AWS と同じく仮想ネットワークのサブネットをつくることができます。 今回は、いつもどれくらいのCIDRで作成すればいいのかわからなくなるので、少し考察してみたときの記録をご紹介します。 システム的な最小値 サブネット サポートされる最小の IPv4 サブネットは /29、最大は /2 です。 Azure Virtual Network に関する FAQ サブネット自体の制限として、上記のルールがあります。 AWSのサブネットは /28 までなので、かなり細かく分割できますね。 その他のサービス Azureには サブネットの委任 という仕組みがあります。 Azure 仮想ネットワークでのサブネットの委任とは これは、特定のAzure PaaSサービスでサブネットを占有する仕組みであり、Azure Database for MySQL などをプライベートなネットワーク上に構築したいときなどに使用します。 察しが良い方は気づいたかもしれませんが、実はPaaSサービスごとに委任されるサブネットの大きさに制限がかかっています。 サービス名 最小 最大 備考 参考 Azure App Service /28 /26(1つのマルチテナントの最大スケール数) MPSJ を利用する場合は /26 が最小 https://learn.microsoft.com/ja-jp/azure/app-service/overview-vnet-integration Azure Database for MySQL /29 不明 https://learn.microsoft.com/ja-jp/azure/mysql/flexible-server/concepts-networking-vnet システムに予約されるIP サブネットを作成した時、サブネットに割り当てたIPがすべて使えるわけではありません。先頭と末尾のアドレスはそれぞれネットワークアドレスとブロードキャストアドレスとして予約されます。また、Azureは1つのサブネットにつき3つのアドレスをシステム的に予約します。 そのため、最終的に利用可能なIPは、 サブネットのIPCIDRから5アドレス除いたIPになります。 Azure Virtual Network に関する FAQ 運用に沿った最小値を考察 IP制限するということは、それだけスケーリングの幅を狭めることとなります。また、仮想ネットワークを分割しすぎると管理することも大変です。そのためAzureでは VNetは大きく、Subnetは小さく作ることが推奨されています。 Azure Virtual Network に関する FAQ しかしながら、弊社のネットワーク構成の制約上、VNetは大きく作成することができません。 では、どれくらいのCIDRが現実的な設定値なのでしょうか? 1つのVNetを占有するアプリケーションの場合 おそらく1つのVNetを1つのアプリケーションで占有することになると思います。 利用するサービスにもよりますが、最低でも /26 のVNetを用意して、4つ以上( /28 )に分割して使いたいです。 より大規模な場合は、 /24 で払い出したうえで8つ以上( /27 )に分割して使えると安定して運用ができそうです。 1つのVNetに複数のアプリケーションがあいのりする場合 こちらはあいのりするアプリケーションの数によります。 4つほどなら、最低でも /26 のVNetを用意して、4つ以上( /28 )に分割で足りると思います。 それ以上は、4つほどなら、最低でも /25 のVNetを用意して、8つ以上( /28 )に分割で足りると思います。 小さいアプリケーションだと思うので、1つのサブネットは /28 で十分賄えると思います。 まとめ 本記事では、サブネットの設定をしたときの考察を書きました。 IP制限されている環境と通信したい場合は適切にsubnetを作成して管理してきたいですね。
この記事は microCMSでこんなことができた!あなたのユースケースを大募集 by microCMS Advent Calendar 2024 3日目の記事です。 はじめに こんにちは、ニフティ株式会社の宮本です。 今回はmicroCMSのアドベントカレンダー参加記事なので、初めてNIFTY engineeringを閲覧する方も多いと思います。 ニフティでもmicroCMSを利用して複数のサイトを運用をしており、せっかくなので今回のアドベントカレンダーに参加させていただきました。 コンテンツの内容によって通知先を変えたい microCMSには、コンテンツの変更があった際に叩くことのできるWebhook機能があります。デフォルト機能として、Slackへの通知や主要なホスティングサービスのビルド用Webhookが用意されています。ビルドも通知もコンテンツの更新時に実施したいので、欠かせない機能ですね。 さて、本題ですが自分が触っているAPIの中には、コンテンツの内容によって叩きたいWebhookが変わるAPIがあります。たとえばお知らせAPIでも、お知らせの種類によって別のSlackに通知をしたいなどです。あまり考えたくはないですが、障害関連のお知らせは障害用のチャンネルにも流すなど……。 このとき、microCMSのデフォルトのWebhookだと、Webhookを叩くコンテンツの更新タイミング自体を細かくカスタマイズすることはできても、コンテンツ内容によって叩くWebhookを決めることはできません。 これを解決しようとした場合、カスタムWebhookを使った上で処理内容によって分岐するAPIを作成する必要があります。実現する方法の一例としてAPI Gateway + Lambdaが挙げられますが、わざわざこれを立てるのは結構面倒です。というか面倒でした。別件でやや複雑な処理をすためにAPIをわざわざ作成したんですが、cdkでAPI Gatewayを建てるのが地味に面倒くさい……。できれば、もっと簡単な方法で実現したいです。 そこで、Zapierを用いてWebhookの叩き先を柔軟にカスタマイズする方法を紹介します。 Zapier Zapier はノーコードでワークフローを作成できるiPaaSです。他の様々なSaaSと組み合わせることでいろいろなことができ、社内でも さまざまな場面 で利用されています。 なお、今回扱うWebhook機能は有料プランでなければ利用できないためご注意ください。 microCMSの更新をZapierで受け取って通知する microCMS側でお知らせの種類をセレクトボックスで選べるようにした次のようなスキーマのAPIで考えます。 これをお知らせの種類に合わせてSlack通知するには、以下のような手順が必要です。 ZapierのWebhookを起点にmicroCMSからのデータを取得 コンテンツの種類に応じた叩きたいWebhookを取得 通知用のWebhookを叩く ZapierのWebhookを起点にmicroCMSからのデータを取得 Zapierで提供されているWebhook Appをトリガーにすることで、取得自体はできるようになります。この時点でWebhookを叩くテストが実行ができるので、microCMSのカスタムWebbhookにZapier側で発行されたWebhookを指定すると、実際に動作した際の返却値を確認できます。 返却値は以下のよう確認できます(だいぶ長いので一部抜粋)。 含まれるデータは microCMSの公式ドキュメント から確認できます。 このとき、新規作成・公開・下書き追加・公開した状態での下書き追加・削除等々、発火時の操作によって含まれるコンテンツのデータの量がやや異なるため注意が必要です。また、この後のステップで参照したい値はテスト時のリクエストに含まれている必要があります。 下書きが存在する公開済みのコンテンツの下書きを更新する操作でWebhookを発火させておくと、ひとまず全部載せのデータが取れるはずです。 通知先を取得 Zapierには処理を分岐させるPath Appがあり、これを使うと簡単に分岐処理を分岐できます。しかし、分岐上限がデフォルト5件と決まっています。上限を増やすこともできるそうですが、それよりも便利な方法があります。 あらかじめGoogle スプレッドシートやZapier Tableにコンテンツの内容と叩きたいWebhookのURLを記入しておけば、コンテンツの内容から検索して対応する行の情報を取得することができます。 こんな感じのスプレッドシートを用意して コンテンツのステータスをもとに検索するようにすれば Zapier側で該当の行を取得できます。 取得したWebhookを叩く 最後は簡単です。これもWebhook Appを使って、前のステップで取得したURLに向けてリクエストを投げるだけですね。 また、通知内容も編集できるためコンテンツの内容を含めた通知にすることも可能です。 Headerなども自由に指定できるため、単なるSlack通知以外にも、認証が必要なWebhookを叩くことができます。その際はデータを取得するWebhookをまとめたスプレッドシートなどに、認証情報の行などを追加して参照できるようにしておくと便利です。 最後に microCMSとZapierを組み合わせることで、コンテンツの更新の種類だけでなくコンテンツの内容に応じて、通知先を柔軟に変更することができます。Slackへの通知文言に更新内容を含ませるだけでも便利かもしれません。通知の作成自体がとても簡単なのも嬉しいです。今回紹介したZapも、たった3ステップでできています。 また、使いようによってはSlack通知以外にも、かなり柔軟に別のSaaSへの連携などを仕込むことができます。 一方でZapierにも限界はあり、より複雑なことや、クラウドリソースに対して何かしようとする場合はAPI Gateway + Lambdaのような構成を作る必要もあると思います。この辺りについては、事前に何がしたいかしっかり考える必要がありますが、とりあえずZapierで動くか試してみることが簡単にできることも魅力の一つだと思います。microCMS自体とても使いやすいので、日々の運用をより快適なものにするためにも、いろいろと工夫のしがいがありますね。 参考 Automate without limits | Zapier コンテンツのWebhookを設定|microCMSドキュメント Paths – limited to 5? | Zapier Community
目次 はじめに dbt CoreのEOLバージョンサポートについて dbt Coreをアップグレードするときの推奨事項 環境について [前半] エラーの解決 [後半] 解決に至るまでのプロセス はじめに データ基盤チームの鹿野です。 データ基盤チームではソースデータのデータ変換処理、及びデータモデルの管理用途でData build toolのOSSリリース dbt Coreを利用しています。 本記事では、dbt Core 1.5へのアップグレード時に直面したエラーについて、その解決策、解決までのプロセス、そして得られた教訓を共有します。 本記事のメイン部分は前半・後半に分かれています。 前半は1〜2分で読める内容です。同様のエラーでお困りの方は、まず前半をご覧ください。 dbt CoreのEOLバージョンサポートについて dbt CoreのEOLバージョンサポートはそのマイナーバージョンのリリース日から1年間と定められています。 1 サポート期間が終了すると、セキュリティパッチとドキュメントの提供が終了します。 また新機能を利用できなくなるため、特別な理由がない限りは定期的なアップグレード対応が推奨されます。 dbt Coreをアップグレードするときの推奨事項 アップグレード対象のバージョンは、現在のマイナーバージョンから1つまたは2つの範囲内に留めること これはOSSアップグレードに限らず、変更によるリスクを最小限に抑えるため アップグレードと関係のない修正は避けること 新機能の追加や既存の挙動変更でリファクタリングしたくなっても、それは後回しにすること 環境について dbt CoreはAmazon Redshiftデータプラットフォームに接続することを前提としています。 dbt Coreバージョン dbt Redshiftアダプターバージョン 実行環境 1.4.9 1.4.1 コンテナ(Python 3.9 slimイメージベース) Github Actions ホステッドランナー(ubuntu-latest) [前半] エラーの解決 エラーの説明と補足 遭遇した複数のエラーの中から、特に解決に時間を要した1つを取り上げます。 dbt run実行中に発生したエラーで、スタックトレースは残っていないため文字情報のみです。 Connection reset by peer  見覚えのある方も多いのではないでしょうか。 これはdbt Core固有のエラーではなく、ネットワークスタック(TCPレイヤー)に起因するエラーです。 以下に補足を記載します。 調査の過程で判明したのですが、このエラーはGithub Actionsホステッドランナー環境でのみ発生していました。 どうやって解決したか? A. 実行環境をGithub Actionsホステッドランナーからセルフホステッドランナーへ移行しました。 具体的には、Github公式が提供するubuntu-latestランナーから、EKSクラスター上のm5.largeタイプのワーカーノード上のランナー(ポッド)へ移行することで解決しました。 補足で既に答えを示唆していますが、特定環境でのみ発生する問題だと判明したため、実行環境の変更で解決を図りました。 内容を深めるため、以降のセクションで補足説明を加えます。 Github Actions セルフホステッドランナー環境について 正式名称は self-hosted runner です。 弊社では、dbtモデルを安全に本番環境へ公開するためのCIをGithub Actionsで構築していました(以降、CIワークフローと呼びます)。 CIワークフローでは、Github公式が提供するubuntu-latestランナーを使用していましたが、これを自前で用意したランナーに切り替えました。 Github Actions セルフホステッドランナーの考慮事項 セルフホステッドランナー環境の構築自体は公式ドキュメントに従えば容易ですが、運用面では以下の懸念事項を考慮する必要があります。 ランナーを実行するマシンをどうするか? マシンのメンテナンス マシンのセキュリティ担保 Githubホステッドランナーではこれらの懸念事項はGithub側が対応してくれますが、セルフホステッドランナーは名前の通り実行環境を自己管理する必要があります。 このセクションでは結論のみ記載しますが、 Actions Runner Controller (ARC) の導入でこれらの課題を解決しました。 詳細なプロセスは後半で説明します。 [後半] 解決に至るまでのプロセス 前半は前座で、後半がメインです。 ただし、こちらは主に経緯を詳しく説明する内容となるため、文章量が多くなり、より詳細な記述となります。 ご理解いただいた方からお進みください。 以下に、解決までのプロセスを時系列順に記載します。 各ステップについて補足を加え、最後に得られた知見をまとめます。 解決までのプロセスを時系列順に列挙しました。 それぞれのセクションで補足をし、最後に気づきをまとめます。 dbt Coreを1.5にアップグレード dbt run でエラー発生 Redshiftクラスターのモニタリングデータ、およびエラーログの確認 dbt-redshiftアダプター設定の見直し RedshiftアダプターのCHANGELOG確認 類似の事象探索・調査 今後の進め方を決める Github Actions セルフホステッドランナーでCIワークフロー検証 Github Actions セルフホステッドランナー環境の選定 まとめ dbt Coreを1.5にアップグレード dbt Coreはpipでインストールをしています。 1.5へのアップグレードと書いていますが、1.8より前のバージョンでは、dbt Core自体を直接インストールするのではなく、各データベース用のアダプターをインストールすることが推奨されています。 dbt Coreは、インストールしたアダプターの依存関係として自動的にインストールされます。 pip install dbt-redshift==1.5.11 この時点では、アダプターのバージョン1.5.11をインストールしていました。 これは単に最新のパッチバージョンを見落としていたためです。 dbt run でエラー発生 Github Actionsホステッドランナー環境で実行されるCIワークフローのdbt runステップにおいて、「Connection reset by peer」エラーが発生しました。 このエラーは、実行コストの高い一部のモデルでのみ発生することが判明しました。 しかし、この段階では実行環境の違いがエラーの発生に関係しているという可能性に気づいていませんでした。 Redshiftクラスターのモニタリングデータ、およびエラーログの確認 RedshiftクラスターはサーバーレスではなくRA3インスタンスのノードで構成されています。 実行コストの高いモデルでのみエラーが発生していたため、Redshiftクラスターに負荷がかかっているのではないかと考え、モニタリングデータとエラーログを確認しました。 マシンリソース(CPU・メモリ) アクティブ接続数 クエリの同時実行数 キューに入ったクエリ数と処理されたクエリ数の統計 エラーログには特に目立った問題は見られず、接続数、同時実行数、クエリ統計のいずれもクォータには達していませんでした。 CPUの使用率は高止まりしていたため、スペックアップで解決できる可能性はありましたが、まずは他の解決方法を探ることにしました。 dbt-redshiftアダプター設定の見直し クライアント側で負荷を軽減できないかという観点からアダプター設定を見直すことにしました。 1.5で非推奨となった設定項目がいくつか見つかりましたが、これらは一旦保留としました。 threads: 8 -> 4 -> 2 connect_timeout: 30 -> None read operation timed out がIssueにあったのでここで同時対応 sslmode: prefer -> disable keepalives_idle: 非推奨となったので削除 threadsの調整により一部のモデルでは問題が解消されましたが、より処理コストの高いモデルについては依然としてエラーが発生しました。 その他のパラメータ変更では目立った効果は得られませんでした。 keepalives_idleが非推奨になっていたことが気になったため、次にアダプターにどのような変更が入ったのかを確認することにしました。 RedshiftアダプターのCHANGELOG確認 CHANGELOGを確認したところ、以下の重要な点が判明しました。 Redshiftクラスターへの接続ドライバが psycopg2 から redshift_connector に変更されていること さらに、この時点で「開発環境ではdbt runのエラーが発生していない」という重要な事実に気づきました。 類似の事象探索・調査 dbt、redshift_connector、environmentをキーワードとして、以下の情報源で類似事象を調査しました。 dbt-labs/dbt-core のIssue dbt-labs/dbt-redshift のIssue aws/amazon-redshift-python-driver のIssue dbt公式のユーザーコミュニティ 完全に一致する事象は見つかりませんでしたが、ネットワークスタックに関連すると思われるIssueをいくつか発見しました。 https://github.com/dbt-labs/dbt-redshift/issues/701 https://github.com/dbt-labs/dbt-redshift/issues/518 https://github.com/aws/amazon-redshift-python-driver/issues/212 これらのIssueから、Github ActionsやCodeBuildなどの環境で同様のエラーが報告されていることが分かりました。 いずれのケースも明確な対処策やパッチは提供されず、「Redshift Pythonドライバーには安定したネットワーク接続が必要」「実行環境に起因する問題」という結論でクローズされていました。 今後の進め方を決める 実行環境の変更で解決できる可能性が見えてきたため、次の2つの対応を進めることにしました。 アダプターのバージョンを最新(1.5.12)にアップデートする セルフホステッドランナーでCIワークフローの検証を行う 検証結果でエラーが発生しなければCIワークフローの実行環境を変更し、エラーが続く場合は別の方針を検討する パッチ適用だけでは解決できない可能性が高いと予想されましたが、可能な対応は全て試してみることにしました。 Github Actions セルフホステッドランナーでCIワークフロー検証 EC2インスタンス(Amazon Linux 2023、t3.small)にセルフホステッドランナーを構築してCIワークフローを実行したところ、エラーは発生しませんでした。 これにより、実行環境の変更を解決策として進めることを決定しました。 Github Actions セルフホステッドランナー環境の選定 セルフホステッドランナーは自己管理が必要なため、実運用開始前に運用方針とセキュリティ面について十分な検討が必要です。 マシンの運用 VMのメンテナンス 静的なVM一台の管理負担が発生 ランナーのバージョン管理、セキュリティパッチ適用、OSアップグレードなどが必要 スケーリングの課題 オートスケーリングの実装は過度な工数となるため現実的ではない 非稼働時間の扱い ワークフローの実行時間は1日のごく一部のみ ワークフローのprehook、posthookでマシンの起動・停止を制御することは技術的には可能 ワークフロー以外のタスクを組み込むとメンテナンスコストが増大するため避けたい 実行環境の構成管理 ランナーのホストマシンのランタイムやライブラリの構成管理が必要 コンテナによる環境分離は可能だが、管理負担が増加しワークフローの変更も必要となる モニタリング リソース・ログ・コストの監視が必要 セキュリティ面 マシンのライフサイクル管理 ホストマシンとランナーは、ホステッドランナー同様のクリーンな環境を維持することが望ましい(環境汚染やバックドアを防ぐため) 必要時のみ起動し、用済み後は速やかに終了する運用が望ましい 実行権限管理 ワークフロー実行時のユーザー権限を適切に制御する必要がある セットアップの利便性のために「runner ALL=NOPASSWD」などの安易な設定を行うと、環境が脆弱になるリスクがある 要件を整理してみると考慮すべき点が多岐にわたり、さらに検討漏れもありそうです。 マシンの適切な運用には相当な労力が必要となるため、できれば避けたいところです。 これらの課題を検討しながらGithub Actionsの公式ドキュメントを確認していたところ、ほとんどの課題を解決できそうなARCを発見したため、検証を経て実運用への導入を決定しました。 まとめ 今回のdbt Coreアップグレード作業から得られた教訓を以下にまとめました。 この内容が誰かの参考になれば幸いです。 アップグレード作業における基本 アップグレード作業の際は、変更点の確認と影響範囲の特定を標準プロセスとして組み込むこと また、環境ごとのアップグレードガイドラインを事前に整備することで、担当者によらず安定的なアップグレードを実現できる可能性が高まる 問題解決アプローチの改善点 詰まっている時ほど客観的な視点での状況把握が重要、そのためには意識的に「リセット」できると良い 適切な判断を導くために 公式ドキュメントや一次情報を優先的な情報源として参照すること 解決策の選択に関する考察 その場における最適解はあるが、正解はない 最適解を追求する姿勢は大切だが、過度に固執すると単なる時間の無駄になってしまうこともあるため、バランスを保つことが重要 単一の解決策にとらわれず、複数の選択肢を検討することも重要 参考資料 https://docs.getdbt.com/docs/dbt-versions/core https://docs.getdbt.com/blog/upgrade-dbt-without-fear https://docs.getdbt.com/docs/dbt-versions/core ︎
こんにちは!NIFTY engineeringブログ運用チームです! 今年もあっという間に残りわずかとなりました。クリスマスが迫り、アドベントカレンダーの季節が到来しましたね! ニフティグループでは毎年、アドベントカレンダーに積極的に参加しており、今年でなんと9回目の開催となります! 日頃の知見をアウトプットする機会として、ニフティグループのエンジニアが楽しみながら成長していくことができています。 今年も皆様に新たな技術や知識をお届けできることを楽しみにしています! アドベントカレンダーとは? 元々はクリスマスまでの日数をカウントダウンするために使われていたカレンダーで、12月1日からはじまり、25個ある「窓」を毎日1つずつ開けて中に入っている小さなお菓子やプレゼントを楽しむものです。 このカレンダーにならい、定められたテーマに従い参加者が持ち回りで自身のブログやサイトに記事を投稿する、リレー形式のブログ執筆イベントです! ニフティグループ アドベントカレンダー ニフティグループではフリーテーマとなっておりますので、好きに執筆してもらっています。 また、記事に関してはQiita様のアドベントカレンダーページにて登録させていただき、 公開自体はNIFTY engineeringや ニフティライフスタイル Tech Blog 、Qiitaにしていく予定です!(ニフティ、ニフティライフスタイル、セシールでそれぞれ投稿場所が異なります) ニフティグループ AdventCalendar 2024 アドベントカレンダーでは、ニフティグループのエンジニアが日頃の業務で培ったノウハウを共有していきますのでぜひチェックしてみてください!
こんにちは、新卒1年目の後藤と滝川と藤岡です! 新人研修の一環としてニフティ2024年度新卒入社の9名がAWS JumpStart 2024 for NewGradsに参加しました! AWS JumpStart for NewGradsとは?   新卒1年目 のエンジニアの方々を対象とした、2日間の実践的な研修プログラム。 将来的にAWS活用をリードする人材になるための第一歩をスムーズに踏み出せるようなコンテンツをというコンセプトで企画されているため、単なるAWSサービスの学習だけでなく、チームに分かれて要件に合った適切なアーキテクチャを検討・設計する経験を積む部分にフォーカスした内容。 プログラムの到達目標 一般的なリファレンスアーキテクチャを理解できる AWSのコアサービスの概要や選定基準を理解できる AWSのアーキテクチャ図を作成する流れを知る 参加するまでやること 事前学習では、システムアーキテクチャ設計の考え方とAWSの主要サービスについて動画を視聴しました。 1.Cloud for Beginners(約40分) Webサービスの基礎 2-1.AWS Skill Builder (約4時間) AWS の基本的な概念を包括的に or 2-2.Cloud for Beginners(約2時間) Webサービスの基礎(1の続き) 内容 アジェンダ AWSやアーキテクチャ設計に関する講義 チームに分かれてのWebアプリケーションのアーキテクチャ検討 ハンズオンワークショップ 実施期間 開催日時 (各回2日間) 2024 年 5 月 21 日(火)~ 22 日(水)各日 09:00 – 18:00 オンライン開催 2024 年 9 月 26 日(木)~ 27 日(金)各日 09:00 – 18:00 オンライン開催 スケジュール 1日目 VPC、ECS Fargate、Application Load Balancer (ALB)、RDSを使用してToDoアプリケーションの構築 VPCの設定:ネットワーク環境を構築 ECS Fargateの設定:コンテナ化されたToDoアプリケーションのデプロイ AutoScaringの設定 ALBの設定:トラフィックの分散とルーティングの実装 RDSの設定:データベースの構築(Writerインスタンス、Readerインスタンスの作成) システムの耐障害性テスト(Taskを意図的に停止させ、システムの挙動を観察) Taskの停止:手動でTaskを終了 新しいTaskの立ち上がりを確認:システムが自動的に新しいTaskを起動 サービスの継続性を確認:アプリケーションが中断することなく稼働し続けることを確認 RDSの構成確認RDSの構成 Writerインスタンスにフェイルオーバーを行い、Readerインスタンスと入れ替わることを確認 成果 2日目 アーキテクチャ検討 miroを使用し、まずは個人でアーキテクチャを検討していく課題です。 ECサイトの作成を目的として1日目の内容を参考にしながら各自、アーキテクチャ図を作成していきます。 そして、午後からは5人1グループずつほどに分かれ各グループでそれぞれのアーキテクチャ図を発表しました。 各グループは運営の方が決め、初対面の人同士で議論し組み合わせながらチームでひとつのアーキテクチャを完成させました。 以下それぞれ3人のチームのアーキテクチャ図です。 成果 後藤チーム 私たちのチームでは、Amazon ECRでコンテナイメージを一元管理し、Elastic Load Balancerで負荷分散を実現しました。Auto Scalingにより、スケーラブルな環境を構築しています。セキュリティ面では、VPCによるネットワーク分離とIAMを最小権限の原則に則ってアクセス制御を導入しました。さらに、AWS LambdaとAPI Gatewayを活用したサーバーレス構成を採用し、CloudWatchで監視を行っています。データ管理にはAmazon RDSを使用し、クラスタ構成にすることで高可用性を確保しました。外部API連携やCI/CDパイプラインは、AWS CodePipelineとCodeBuildを用いて自動化しています。チームメンバーのAWSの経験が浅い中、1日目や資料を確認しながらなんとか作成することができました。チームメンバー全員のAWS経験が浅かったにもかかわらず、1日目の学びと提供された資料を活用しながら、協力してアーキテクチャ図を完成させることができました。 滝川チーム 私たちのチームでは1日目で作成したToDoアプリをベースに構成図を作成していきました。 意識した点は可用性を高めること、管理の容易さの2点です。 可用性の点では、AZにリソースを分散配置、RDSの冗長化構成の導入、AutoScalingを活用した自動スケーリング機能の実装。 管理の容易さの点では、CI/CDの構築、CloudFrontを利用したコンテンツ配信の最適化、カスタマーサポート用の管理画面へのセキュアなアクセス経路の確立。 実際のAmazon ECサイトをモデルとしてイメージすることで、必要な要素を明確に把握し、効率的に作業を進めることができました。 藤岡チーム 私たちのチームでは2つの可用性ゾーン(Availability Zone 1とAvailability Zone 2)を設定することで、冗長性と高可用性を確保しつつ「Elastic Load Balancing」を中央に配置し、ユーザーからのリクエストが均等に分散されるようにしました。ロードバランサーの後ろには「Auto Scaling」機能で、需要に応じてサーバー数を自動的に増減させています。また、アプリケーションはコンテナとして実行しています。 チームメンバーで全体的にAWSの経験はそこまでなかったですが、1日目の資料を参考にしたり運営の方への質問、さらにAmazon Bedrockを活用することでなんとか形にすることができました。 感想 後藤 AWSについて、業務で少し触れた程度の経験しかありませんでした。アーキテクチャ図の作成を通じて、これまで業務で使用したことのないサービスについても知識を深めることができました。さらに、チームでリソースの必要性を議論する機会があり、実際の業務に近い形で経験を積むことができました。この研修は非常に学びの多いものでした。 滝川 AWS JumpStartでは特に可用性に重点が置かれている気がしました。具体的には、Auto ScalingやRDSのライター・リーダーインスタンス、Application Load Balancer (ALB)の重要性について学ぶことができました。クラウドを利用する上で、セキュリティは利用者自身が責任を持って管理しなければならない領域のため、今回セキュリティについて学べたことは非常に有意義でした。 藤岡 私はAWSを触ったことがある程度の状態で今回の研修に参加しました。2日間の研修の中でも主に実践課題に取り組むことで理解が深まったと感じました。アーキテクチャ検討では、正解は1つではなくチーム内でも様々な意見が出ましたが、生成AIであるBedrockをうまく活用することができチームの方向性が定まっていきました。今回の研修で、答えのないところから構築していくAWSの面白さを感じたとともに生成AIの活用の幅広さを体感できました。
こんにちは。2024年に中途入社した おおい と申します。 今回はニフティに中途入社してみて私自身が感じたことを書いてみようと思います。 ニフティという会社の雰囲気をなんとなく感じていただけると嬉しいです。 自己紹介 前職はSIerでエンジニアとして、主にJavaを使ってシステム開発業務をしておりました。 ご縁がありニフティに入社し、基幹システムグループのサービスインフラチームに配属となり、 @nifty v6サービス の申込システム担当として業務にいそしんでいます。 入社後に感じたこと そんなSIer出身の私がニフティに入社してから感じたことを3つお伝えします。 わりと自由 服装や髪型が自由であることや、社内で使用しているコミュニケーションツールでちょっとした雑談があるなど働くうえでかなり自由にさせてもらえているなあと感じました。 社内イベントも活発で、イベント時には多くの方が全力で楽しんでいる姿をよく目にします。 また業務においても裁量をもって遂行させてもらえているので、これまで未経験だった技術スタック (Python、DevContainer etc…) を使用して開発してみるといったこともできています。 思ったよりも運用業務が多い 前職ではお客さまのシステムを開発しており、そのほとんどが開発業務でした。 一方でニフティではお客さまに直接サービスを提供している会社なので、時期や所属部署などにもよりますが、日々の業務では開発に関する業務に比べると運用に関する業務のほうが多めに感じました。 ただ入社して日が浅い私にとっては、担当システムの知識を習得するのにとても助かった点だったりします。 挑戦する機会がたくさん 先に「業務においても裁量をもって遂行させてもらえる」と書いたとおり日々の業務ではもちろん、このエンジニアブログの執筆や定期的に開催されるLT大会への登壇など、挑戦することによって自分を成長させることができる機会がたくさんあると感じました。 おわりに ニフティは自由な雰囲気の中で挑戦、成長できる環境が整っています。 日々の運用業務を通じてシステムへの理解を深めながら、新しい技術にも挑戦できる。そんなバランスの取れた環境で、エンジニアとしての可能性を広げていけることを実感しています。 これからもこの環境を最大限に活かしながら、お客さまのため、ニフティの発展のために貢献していきたいと思います。
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! 天気予報があなたの手元に届けられるまで   〜今日傘を持って出掛けることができるための仕組みの裏側〜 2008年に開始した@nifty天気予報がついにリニューアル。その開発のウラ側についてのテーマでお話をしました。 また、Tech Talk#22はコラボ企画となっており、ウェザーニューズ様とコラボしています。 リニューアルで使用したNext.js、Go、GraphQLでどのように開発していったのか、気象データを提供する側の仕組みなどをウェザーニューズとニフティの技術者が紹介しております。 当日見ていない方もご興味あれば是非、 アーカイブ からご視聴いただければと思います。 【10/2 (水曜日) 19:00~20:30 ハイブリッド開催】ウェザーニューズ社コラボイベント! Next.js/Go/GraphQLで生まれ変わった@nifty天気予報 開発のウラ側を開催します! 【LT1】天気予報があなたに届けられるまで ウェザーニューズからの登壇者によるお話しで、気象情報の収集から提供についてが主な内容です。 天気予報という身近なものでその仕組みでとても興味深いお話しでした 資料 【LT2】@nifty天気予報:フルリニューアルへの挑戦 このプロジェクトの全体像がよくわかります。体制や採用した技術などなど。 @niftyが提供する天気サービスは1997年から始まったらしく1997年入社な私としてはちょっと親近感わきます 資料 【LT3】@nifty天気予報のフロントエンドを実装するまで タイトルどおりフロントエンド開発についてです。 採用した技術の理由など、これからの開発の参考になるという方もいるのではないでしょうか。 資料 【LT4】AWS AppSyncを用いたGraphQL APIの開発について バックエンドのお話しです インフラ構築のお話しからやっているので、これからシステム構築されるような方に参考になるかもです 資料 まとめ 初の他社様とのコラボレーションとなりましたが、いままでのオフライン参加よりも多くの方に来ていただけました。質疑応答でも毎回何件か質問いただき、なかなか盛り上がったのではないかと思います。 フロントエンド、バックエンドで同じ技術を使うではなく、それぞれに適したものを検討・採用されており、工夫されて作られているのだなという感想を持ちました。 ご視聴された方々に少しでも参考になる内容であったなら幸いです。 次回予告 次回のTech Talkは2024年11月26日12:00~13:00開催を予定しております。 テーマはTerraformの入門編です アーカイブ(YouTube) 発表資料(Speaker Deck)
ねんがんの Notionフォーム がリリースされました! プロパティの入力値をフォームのフィールドとして設定でき、フォームを好きな場所に埋め込むことも可能です。Notion AIと組み合わせれば、自由記述からのキーワード抽出ができ、さらにグラフで可視化もできます。アンケートフォームなどでの活躍が期待できますね。 Notionフォームで主にできること プロパティの入力 リレーションの選択も可能 ページ本文の入力 フィールドの入力必須化 フィールドの説明 フィールドの種類 プロパティの種類に応じて変化 フィールドのValidation 見出しやテキストの挿入 入力後の自動処理 データベースオートメーション フォームの埋め込み Notionフォームには基本的な機能はありますが、まだ完全とは言えません。 特にValidation機能がないのが個人的に困っている点。プロパティなら仕方ないと思っていましたが、フォームの形をしているとこの機能が欲しくなります。 そこで今回は、NotionフォームにValidation機能を追加できないか検証してみます。 入力時やPOST前に制限をかけることはNotion単体ではできないため、入力後に値が期待通りかチェックする方法を取ります。今回のアップデートでオートメーションのアクションに数式や変数が使えるようになったので、これを早速活用してみます。 データベースオートメーションと数式(Formula)でValidationっぽいものを作る 覚えておくべきオートメーションの仕様 トリガー → アクション * n の2ステップまで アクションはおそらく非同期で処理される アクションを実行するユーザーは Notion (ユーザープロパティ等では選択できないシステムユーザー) Notion ユーザーが実行したアクションは新たなオートメーションをトリガーしない アクションの実行有無に関する条件分岐は設定できない Slackへの通知内容はカスタマイズできない メールアドレスと文字列のprefixをチェックするサンプルフォーム フォームに入力してもらいたいフィールドを用意。 メールアドレスとSlackのチャンネル名を入力してもらい、入力規則に沿っているか確認するのが目的。 次に入力された内容が正常か確認するための Validate と Log プロパティを用意。 Validation本体はオートメーションを利用。 数式で正規表現にマッチするか調べて変数に格納。 変数の値を元に Validate と Log プロパティの値を更新、最後に入力したユーザーに通知を送信。 これで入力値が正しければ正常なメッセージの通知が行われ、間違っていれば修正を促す通知がフォーム送信者に送られる、Validationっぽいものが完成です。 ただし、この構成には課題もあります。初回フォーム送信時は問題ありませんが、編集時には文字を記入中でもトリガーが発火されるため、通知が頻繁に来てしまいます。通知はデータベースのページごとにまとめられるので、まだ許容できる範囲ではありますが、トリガーはボタンにしてもいいかもしれません。 まとめ 自力でValidationっぽいものは作れましたが、完成度がいまひとつですね。 フォームの内容に厳密性を求める場合は、まだGoogleフォームなど他のツールを使った方が良さそうです。フォーム機能の改善については、Notionにリクエストを送り実装されるのを期待して待ちましょう。 触ってみると分かりますが、最近のアップデートで表現力や柔軟性はかなり上がっています。特にオートメーションで数式や変数を使用できるのを求めていた人は多かったのではないでしょうか。この記事を読んで「今ならここまでできるのか、それならこれもできるのでは?」と新しいアイデアが湧くきっかけになったら嬉しいです。
ニフティ株式会社でエンジニアリングマネージャーをしています芦川です。 今回は、インナーソースを日本に広めるべく、InnerSource Commons Japanの運営メンバーとしての立場からも意見を書いてみたいと思います。 ニフティではここ数年間、社内にインナーソースを導入するための活動を行ってきました。 導入のきっかけや詳しい活動内容については、以下をご参照ください。 インナーソースを導入してみた その① お試し導入編 インナーソースを導入し てみた その② 土台作り編 導入後、学習を続けていく中で、社内でインナーソースが広まりやすい部分と、広まりにくい部分が見えてきたように感じます。実際の社内の結果や、今後に期待できる点も含めて書いてみたいと思いました。 ここからの話は、あくまで「こういうパターンだとインナーソースは広まりやすい」という話になります。本来、インナーソースでは社内で自由にコントリビュートできる環境を目指しています。パターンにあてはまらなくても当然個人のスキルアップやエンジニア体験の向上が期待できます。そのため、「これ以外のパターンはやらない方がいい」という制限を意図しているわけではありませんのでご注意を! では、インナーソースが効果的に導入できるパターンについて、モデル図を作成しましたのでご紹介いたします。 効果的な導入1️⃣ みんなで便利ツールにコントリビュートしよう! これはご想像の通りだと思いますが、出退勤や日報(弊社には日報文化があります)、コミュニケーションツールなど、業務に直接関わらないツールについて、エンジニアが自ら作成しやすく、また、作成したツールを社内に広めやすい環境があります。このようなツールに対してインナーソースは非常に効果的です。まさにOSSのように、コントリビュートが生まれやすい環境と言えるでしょう。 効果的な導入2️⃣ プラットフォームチームのリポジトリにコントリビュートしよう! ここからは、チームトポロジーのチームタイプに基づいた例になります。 もう少し業務的な例として、プラットフォームチームのリポジトリへのコントリビュートが挙げられます。プラットフォームチームは、広範な部署からの依頼を受けることが多く、全社にわたる影響を持つこともあります。 ニフティでは、リポジトリ内での定義追加などをプルリクエストとして他チームから受けることで、依頼のワークフローが簡略化され、リードタイムが短縮されました。新機能のエンハンスや新しいAPI I/Fの提案などもっといきたいところはありますが、まだそこは道半ばではあります。 なぜプラットフォームチームだけがそうなのか、と考えた理由ですが、上の便利ツールと同じくメリットを享受できる人が、つまり利用者が単純に多くなるからです。 そもそもコントリビュートは、コントリビュートしたいモチベーション、つまり利用者自身へのメリットがあることが前提になります。 例えば、特定のストリームアラインドチームの持つリポジトリへ、プラットフォームチームのメンバーがコントリビュートするのか?そこでの業務的なメリットはなんだろうか?そのモチベーションは果たしてなんでしょうか?と考えていくと、4つのチームタイプの中ではプラットフォームへのコントリビュートが広まりやすいのでは?という話になります。 (コンプリケイテッドなシステムについて、スキルアップを目指してコントリビュートする例も、組織的な観点から見ると、おそらくその会社にとって重要な意味をもつシステムであるはずなので、そのシステムを知る人間が増えるということは会社にとってすごく大事なところです。もっと将来ではそういうところも目指していきたいです。) 効果的な導入3:ドメイン知識が似てビジネスの方向性が同じストリームアラインドチームのコラボレーション ここからは、弊社でまだ実践例が少ないものの、期待が大きいパターンになります。 コントリビュートのモチベーションが広まりやすさに大きく関係すると考えていますが、その意味では、ドメイン知識が似てビジネスの方向性が同じストリームアラインドチーム同士では、お互いにメリットが生まれることが多いと思います。 例えば、あるチームが作成したAPIを、別のストリームアラインドチームが共通のものとして利用するケースです。弊社では、郵便番号から住所を引くAPIや共通のID登録を行うAPIなどがこれに該当します。こうしたコラボレーションでは、プルリクエストを柔軟に行うことでリードタイムが短縮され、他のチームにもメリットが生まれます。 逆に、ドメインが全く異なるストリームアラインドチーム同士のコラボレーションは、両者にメリットが少なく、認知負荷の増大にもつながるため、あまり広がりそうにないのかな、と思っています。 効果的な導入4:ドキュメントへのコントリビュートを全社的に推進しよう! 最後に、広がりが期待できるのがドキュメントへのコントリビュートです。 最近発表された「 State of InnerSource Report 2024 」でも言及されていますが、企業がインナーソースを導入する際に、ドキュメント管理は比較的ハードルが低いとされています。 documentation-as-codeが一般的な初期のInnerSourceプロジェクトタイプとして登場していること。この傾向は、組織がInnerSourceの取り組みを始める際に、実践的でハードルの低い入り口を見つけていることを示唆している。 State of InnerSource Report 2024 , P2 ただし、ニフティの場合、すべての開発ドキュメントがgitで管理されているわけではなく、主にNotionに保存されています。また、全社の人事・総務なども含めたドキュメントを対象とする場合、GitHubアカウントがエンジニアにしか配布されていないことが課題となっています。 しかし!コードとして管理されているドキュメントに限らず、「コントリビュートして互いに助け合い、良いドキュメントを作る」ことを目的とするならば、Notionのサジェスト機能を活用できるのではないかと考えています。 Notionサジェスト機能について サジェストモードでNotionページを編集すると、ページの持ち主に対して編集の提案ができ、承認されると反映されます。これは、ゆるいプルリクエストのようなもので、社内でのインナーソース活動と同義です。エンジニアに限らず誰でも使える機能であり、インナーソースの認知を広げる起爆剤としても活用できるのではないでしょうか。 以上、ニフティでもインナーソースはまだまだ促進中で、未来は明るく、これからもやれることがたくさんあると感じています。今後も導入事例や展望を発信していきます! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは、ニフティ株式会社の小林です。 最近、日常業務の効率化に向けて積極的に新しい方法を探求しています。 百聞は一見に如かず 文章での説明に加えて、視覚的な例を用いて質問をさせてください。 以下、左右にあるサイトを見比べて何が異なるのかを探してみましょう。 ※ 私の友達であるmyfriendGPTを利用し、適当にコンテンツを作成しています。  ⇒ ChatGPT APIを活用したSlackbotを作成して、30分以内に会社内に有能な友達を作る 1問目 質問 以下の画像でお互いに違う箇所はどこでしょうか。 答え 申し訳ありません。実は、画像の内容は同一ですが、URLのみが異なっています。 2問目 質問 以下の画像でお互いに違う箇所はどこでしょうか。今度はURLは同じとします。 答え ID部分が異なっています。 似て非なる環境が生み出す問題 上部の質問で登場した環境は非常に似ていて、開発環境と本番環境の見た目の近さが思わぬ問題を引き起こすことがあります。 以下のような問題が時折発生すると考えています。 本番環境でのデータ調査を意図していたが、開発環境で実施していた 気付くまでに不要な工数がかかってしまった 開発環境での作業を想定していたが、本番環境で実施していた 内容次第では障害に繋がるリスクがある なぜ起こるか 本問題は、システム的要因と人的要因が絡み合って発生すると考えています。 システム的要因 開発環境と本番環境の区別が不明確 視覚的な差異が少なく、誤操作のリスクが高い ログイン情報の長期保持 アカウント切り替え時の確認不足を誘発 人的要因 作業への慣れ 警戒心の低下や確認作業の省略に繋がる 疲労 注意力の低下 視覚的な認識能力の低下 小さな文字の見落とし 焦点が合わせにくくなる 対応策 このブログの本題となりますが、以上の発生に対する対応策を提示します。 本番環境のみタイトルを赤色にする ブログ用の極端な色付けのため、実際は目に優しい色でも問題ないと思います。 特定のURL/文字列に該当する場合に制御できる 気付きそうですね どう実現したか Tampermonkeyを使用 Tampermonkeyは、ウェブページのHTML要素やCSSを動的に変更できるJavaScriptコードを実行する拡張機能です。主にChromeで使用されますが、EdgeやFirefoxなど他のブラウザでも利用可能です。 以下等、他に影響を与える実行をしないこと 過剰なリクエストの送信 著作権侵害 プライバシーの侵害 マルウェアの配布 上記の通り、Tampermonkeyは要素変更以外にも多様な機能を持つため、意図しない影響を及ぼさないよう注意が必要です。HTMLやCSSの要素変更のみであれば、サーバには影響を与えないことを「開発者ツール」のNetworkから送信されていないことから確認しております。 以下に例を示しますが、より深い理解を得たい方は、「DOM」や「JavaScript」について学習することをお勧めします。 URLベースでスクリプトを変更可能 Tampermonkey内の@matchに該当のURLを含めるとURLベースで実行することが出来ます。 // ==UserScript== // @name Header Background Color Changer // ~~~ // @match http://localhost:8088/hoge.html // ~~~ // ==/UserScript== 文字列制御でスクリプトを実行可能 コード内に以下のような特定の文字列の有無で実行を制御するように変更することが出来ます。 // ページ内に特定の文字列があるかチェックする関数 function checkForTargetString() { return document.body.innerText.includes('1992TORI'); } 最後に 本番環境と開発環境の視覚的な違いについては、実装上必要な場合もありますが、一般的には両環境の見た目を同一に保つことが、運用とテストの観点から望ましいと考えています。 このような場合、Tampermonkeyなどのブラウザ拡張機能を活用し、開発者側で対応することをお勧めします。これらのツールは簡単にオン/オフを切り替えられるため、視覚的なテストにも柔軟に対応できます。 本ブログが、人ではなく仕組みで解決する方法の1つになって頂ければ幸いです。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
前回の記事では、ニフティのメールチームで働く社員に、業務内容やチーム環境についてインタビューを行いました。 今回はインタビューの後編をお届けします。前編はこちらの記事をご覧ください。 【前編】@niftyメールの開発チームに業務のあれこれ聞いてみた!   過去頑張ったエピソードを教えてください。 M.Sさん 冒頭に触れた基盤移行ですね。基盤移行のきっかけにもなったのですが、ニフティのメール基盤は独特な作りをしていて作った人にしかわからないような状態でした。期限が迫る中、バグを踏んで対応に追われたり色々あったなという感じです。 Y.Mさん 3月に入ったばっかりなのでまだあまりエピソードはありませんが、メールを理解するところかなと。アーキテクチャはしっかりされていながらも、理解しないと進められないところもあり、OJTではそのあたりを知る機会がなかったので勉強しながら進めています。 K.Fさん 私も基盤移行です。スケジュールが厳しくて、2週間で社内向けのメール送信システムを作らなければいけない状況もありましたが、何とか完遂しました。 休日はどう過ごしていますか? M.Sさん 去年くらいから犬を飼い始めたので犬と遊んでいます。社交的なわんこなので、週末はドッグランに連れて行っています。 M.Sさんのわんこ K.Fさん リモート会議の時にワンちゃんの声がして癒されました。 M.Sさん 足元にじゃれついてきて鳴くので、その時の声かな? Y.Mさん 家でのんびりしていて、ゲームをやっています。金曜に友人からゲームの誘いがあって、朝までやって、昼起きて夜はまたゲームしています。 M.Sさん 月曜は辛くないですか?笑 Y.Mさん 日曜は早めに切り上げています。笑 夜遅いながらも、朝も早めに行動していて最近8時に出社しています。朝は涼しいとマネージャーに教えてもらったので… K.Fさん 最近ビリヤードにはまってます。バッティングセンターに行ったりもして、おとといは群馬でパターゴルフをしたりしています。ゴルフ、サバゲ―にも興味が出てきて忙しいです! M.Sさん K.Fさんが多趣味なのは知ってましたが、ゴルフは初めて聞きました。 Y.Mさん きくたびに新しい趣味が出来てる気がしますw K.Fさん 筋トレははまりすぎて日常になりました。社内のSlackの筋トレチャンネルで筋トレ情報をのやりとりをしていたりします。ブカツを通してボルダリングにも興味を持ち、プライベートで登ったりもしています。 いま楽しみなことは何ですか? M.Sさん 来月沖縄に行くので楽しみです。2年前に行った時は台風が直撃してずっと雨だったので、今回こそは晴れてほしい。沖縄料理が好きです。 K.Fさん NIF BARというニフティ社員のためのバータイムが始まったので、お酒を飲みながらボドゲをやりたいです。 Y.Mさん 実家に帰るので、実家で焼肉を食べるのが楽しみです! 今回はニフティのメールチームのインタビューの様子をお届けしました。ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
今回はメールチームへのインタビューです!   M.Sさん 2018年5月にキャリア採用で入社しました。入社から現在までメールチーム一筋です。2023年10月から体制変更に伴いメール開発サブチームのリーダーを担当しています。@nifty 安心メールパックやWebメールの開発・運用を中心に、@niftyメールを構成するシステムの面倒を幅広く見ています。仕事の後はスプラトゥーンをゆる〜くプレイしています。2023年に犬を飼い始めてから毎日溺愛しています。 K.Fさん 2020年4月に新卒で入社しました。2か月の研修を経て2020年6月に配属されました。私が配属された当初はメールチームでAWSが利用されておりませんでしたが、新規サービスを中心に積極的にAWSを導入していきました。今は@nifty 安心メールパックの開発・運用を担当しています。週末はバイクでツーリングに出かけたり、キャンプで自然を満喫しています。楽器やビリヤード、ボルダリングなど、いろんな趣味に挑戦するのが好きです。体力づくりのため、週に5日ほど筋トレに励んでいます。 Y.Mさん 2023年4月に新卒で入社しました。1年間3つのチームの業務を体験できるジョブローテーションを経て、2024年3月にメール開発チームへ配属されました。担当業務は@nifty 安心メールパックの開発・運用です。ゲームが好きで、友人とスプラなどをやります。社会人になってからはおいしいランチも趣味です ニフティメールとは何ですか? M.Sさん ニフティメールはざっくりというとニフティの会員向けに提供しているメールサービスです。歴史が長く、パソ通時代のNIFTY-Serveから続いているニフティでも歴史のあるサービスなんです。無料で利用できる迷惑メールフィルタ、有償のメールウイルスチェック、@nifty セキュア・プライバシー for Mailなど、様々なオプションが利用できるメールサービスです。 そういえばK.FさんとY.Mさんはニフティ、ニフティメールを入社前から知っていましたか? K.Fさん 実は全くニフティのことを知りませんでした。知らずに説明会にきてしまったのですが笑、説明会で魅力的な会社だと感じられたので入社しました。横のつながり、縦のツナガリが感じられて、透明感が高く風通しがよさそうだと感じました。なんていうかこう…まぶしく見えました。自社開発メインなのも大きかったです。 Y.Mさん 同じく私もニフティのことを知らなかったです。デイリーポータルZの会社として認識していて、興味を持って参加しました。説明会の雰囲気も良かったので入社を決めました。 M.Sさん 私はニフティが回線サービスを提供していることも知っていました。学生時代にユーザーでもあったので、ちょっとショックですね。笑 安心メールパックとは何ですか? M.Sさん 通信端末が故障した時に修理費用を補償するサービスのことです。 (安心メールパックのサービスサイトは こちら ) 名前にメールという単語がついているんですが、メールアドレスが一つついてくるためです。( セカンドメール と言います) 2021年3月末くらいからサービスを立上げ、1か月後の4月末にはリリースしました。かなり急ピッチでの開発となりましたが、企画からの強い要望があり、最優先で対応しました。 K.Fさん インフラ、バッチの開発を担当していたのですが、2年目でまだ経験が浅かったのですが、PythonやTerraformなど初めて触る技術で楽しく開発させていただきました。 M.Sさん AWSの環境構築はすべてK.Fさんが担当してくれました。 K.Fさん 資格勉強はしていて、AWS認定資格をいくつか取得したのですが実践は初めてでした。 Y.Mさん 急いで作られたとは仰ってましたが、アーキテクチャがしっかりしているのですごくやりやすいです。 メールシステムにはどういった技術が使われていますか? M.Sさん Python、Terraform、AWSがメインです。 Webメールの現在のバージョンは2010年に作られていて、バックエンドがJava、Tomcatです。フロントエンドはPrototype JavaScript FrameworkやjQueryを使用しているので、Webメールをモダンな技術を使って刷新しようというプロジェクトを計画しています エンジニアが触ってて楽しい技術を採用していきます! K.Fさん 心躍ります! Y.Mさん わくわくが止まんないです。 メールチームのチームはどういう構成になっていますか? M.Sさん メール開発チーム、メールDevOpsチームの2チーム体制です。もともとメールDevOpsチームが主に担当していた配送基盤をKDDIさんの基盤へ移行したのですが、現在のメールDevOpsチームはニフティに残った配送系システムの運用や認証系システムの開発・運用を主に担当しています。 メールDevOpsチームは、現在Gmailの送信者ガイドライン対応などのメール配送の重要な部分をより良くするための開発をKDDIさんと協力しながら進めています。 ※メール基盤の移行プロジェクトについてはこちらの動画をご参照ください! 開発チーム、DevOpsチーム間のやりとりはどうしていますか? M.Sさん 迷惑メールフィルタの案件は両チームで動かしています。毎週水曜日にメールチーム全体の定例会で話をしたり、普段からSlackでやり取りしています。 K.Fさん AWSの技術的な相談をしたりしています。コンテナ周りの知識をDevOpsチームに教えています。 M.Sさん K.Fさんは先生役なんです。 安心メールパックについては企画とのやり取りがありますね。 Y.Mさん メールチームの定例に企画の方が参加されるので、新しい企画についての話をしたりしています。 M.Sさん 今年くらいからY.Mさん、K.Fさんに企画とのやり取りは任せています。 企画とやり取りしているときに心掛けていることはありますか? Y.Mさん システム的な話をしても伝わらないので、企画の方に歩調を合わせるようにしています。 K.Fさん 企画から要望があっても嫌な顔をしない、断らないことを心掛けて要望に応えられるように調整するように意識しています。 M.Sさん おかげで企画との関係は良好です! 現在のチームにはどういった課題がありますか?それに対してどうやって取り組んでいますか? M.Sさん メール開発チームとして始動したのが去年の10月で、サブチームリーダーとしての立ち回りもそこからなのでチーム運営をどうしようかという課題はありました。 スクラム、アジャイル開発のフレームワークを取り入れて日々試行錯誤しながら取り組んでいます。スクラムマスターも私が担当しています。 Y.Mさん スクラムのイベントをM.Sさんが企画してくださり、どんどん良くなってます。 K.Fさん 時間を確保してスプリントレビューやレトロスペクティブをやることで、Webメールへの理解が深まったり、アイスブレイクを通してチームのプライベートの姿を知れてチームが良くなっていく実感があります。 M.Sさん K.Fさんの話にもあったんですが、業務と人がセットになって属人的になってしまっていました。開発チームのタスクをチーム全体で見られるようにしないといけないなと考えていました。 優先的なPBIに対してフロー効率を考えてみんなで当たっていくことを目標に、プロダクト勉強会を週一でやっています。 K.Fさん Webメールの森羅万象を知りたいです! M.Sさん 森羅万象。笑 私もWebメールの全てを理解できているわけではなく、主担当になってから5カ月くらいなので勉強しながらやっています。教える立場になってはじめて気づくこともあります! Y.Mさん Webメールなんも分からん状態なので、安心メールパックのことをやりつつ学んでいきたいです。 後編に続きます! 今回はニフティのメールチームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【後編】@niftyメールの開発チームに業務のこととプライベートの過ごし方を聞いてみた! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }