TECH PLAY

ニフティ株匏䌚瀟

ニフティ株匏䌚瀟 の技術ブログ

å…š517ä»¶

この蚘事は、リレヌブログ䌁画「24卒リレヌブログ」の蚘事です。 はじめに 初めたしお、新卒1幎目の緑川です。 珟圚、OJTゞョブロヌテの1期目ずしお、 䌚員システムグルヌプの第二開発チヌム に所属しおいたす。䞻な業務はオプションサヌビスの開発ず運甚であり、フロント゚ンド・バック゚ンド問わず様々な技術を日々孊んでいたす。 開発にあたっお Svelte ずいうフレヌムワヌクを䜿甚する堎面があり、そのために Svelteチュヌトリアル をやっおみたので、この蚘事ではそこで重芁だず思った点や難しかった点、知らなかった甚語などを自分なりの理解でたずめおいきたいず思いたす。あくたでチュヌトリアルの説明の補足のようなものなので、そちらを読んでいる前提で執筆したす。たた、各項目の解説の密床は、自分の理解床に䟝拠しおいるために偏りがあり、䞀郚の項は䞀纏めにしおいたす。 なお、チュヌトリアルはSvelteずSvelteKit(Svelteの拡匵版のようなもの。今回は割愛)それぞれの基本ず応甚ずで4぀のPartに分かれおいたすが、今回は Part1:Basic Svelte をやりたした。 Svelteずは たずSvelteに぀いお説明したす。Svelteずは、前述のチュヌトリアルによれば、 web アプリケヌションを構築するためのツヌルです。他のナヌザヌむンタヌフェヌスフレヌムワヌクず同様、マヌクアップ(markup)、スタむル(styles)、振る舞い(behaviours) を組み合わせたコンポヌネントでアプリを  宣蚀的(declaratively)  ã«æ§‹ç¯‰ã™ã‚‹ã“ずができたす。 Svelte を理解するには、HTML、CSS、JavaScript の基本的な知識が必芁です。 https://learn.svelte.jp/tutorial/welcome-to-svelte だそうです。少し䜿っおみた感じではHTMLずJS(JavaScript)をシヌムレスに融合させ、その䞊簡単に曞けるようにしたものずいった印象を受けたした。自分はJSに぀いおも詳しくはないので、本蚘事で解説する単語の䞭にはJSのものも含たれおいたす。 Introduction Svelteにようこそ ここから解説に入っおいきたす。 Svelteチュヌトリアルは以䞋のような画面で進行したす。 巊䞊がファむル構造を衚しおいたす。右䞊がファむルの䞭身で、ここを倉曎するずリアルタむムで䞋の衚瀺に反映されたす。 フットプリント プログラムが動䜜する際のメモリ䜿甚量の倚さ コンポヌネント 次項で解説される オヌバヌヘッド プログラムが行う䜜業のために間接的に生じる䜙蚈な負荷 Your first component, Dynamic attributes ここでは、倉数やコヌドを䞭括匧で埋め蟌むずいうSvelteの基本的な動䜜が解説されおいたす。倉数の䞭身にはテキストだけでなく画像も䜿甚するこずができたす。 Styling これは芁するにCSSです。 Nested components この項目はSvelteを孊ぶにあたっお重芁です。この埌もほが党おの項でコンポヌネントのむンポヌトが行われたす。 HTML tags @html によっおHTMLタグを䜿うこずができたす。これは埌述の BindingsTextarea inputs でも出おきたす。 DOM
Document Object Modelの略。Webペヌゞの芁玠やコンテンツなどをツリヌ構造で衚珟したもの brob
Binary Large Objectの略。単にバむナリデヌタの塊を衚珟したもの サニタむズ 脆匱性を突こうずする入力を別の衚蚘に眮き換え無害化するこずを指す。 英語では「消毒する」などの意味がある Reactivity Assignments カりントを増やす関数ず、カりントを衚瀺するテキストを䜜り、「クリックした時にこの関数を実行する」ずボタンに蚭定するこずでそれらを結び぀けおいたす。 Declarations, Statements, Updating arrays and objects 他の倉数に䟝存する倉数は、 $: を぀ければ自動的に連動蚈算しおくれたす。たた、 $: は倉数だけでなく文やブロックなどにも付䞎できたす。ただし、連動蚈算は代入によっお起こるものであり、リストに远加する堎合はもう䞀工倫必芁だそうです。 このリアクティブ宣蚀ずいう抂念は開発でよく䜿う印象がありたす。 statements プログラミングにおける文のこず Props Declaring props, Default values, Spread props IntroductionStyling の項などでコンポヌネント同士の独立性に぀いおの話がありたしたが、もちろんデヌタの受け枡しも可胜です。受け枡しにあたっおは import ず export をセットで䜿甚したす。これも開発には欠かせないものです。 プロパティはデフォルト倀も蚭定するこずができ、しない堎合はundefinedになりたす。たた、枡す倉数が耇数あり、か぀その数ず名前が䞀臎しおいれば、䞀気に代入できたす。 Logic If blocks, Else blocks, Else-if blocks 他のあらゆる蚀語、フレヌムワヌクず同様、if、else、else-ifも実装されおいたす。これらのブロックは # にはじたり / に終わりたす。elseのような間に挟むものには : が぀きたす。 HTMLでこのような凊理を実装しようずするずJSを組み蟌む必芁がありたすが、Svelteであれば地続きの感芚で蚘述できたす。 Each blocks # にはじたり / に終わるのはifず同じです。配列やそれに類するものをあらかじめ宣蚀しおおけば、eachブロックを利甚しお䞀気に代入できたす。 Keyed each blocks ここは少し理解に時間がかかった郚分です。 たず初期化時に、 Thing コンポヌネントに things の䞭身が枡され、 emoji は things の name に察応したものがセットされたす。 things が決めおいるのは name ずコンポヌネントの数なので、 things = things.slice(1); によっお配列の先頭を消すず、コンポヌネントは banana 〜 egg の぀になりたす。しかし、 emoji を決めおいるのは Thing であり初期化時点から動かないこず、そしおコンポヌネントは末尟から消える法則があるこずから、ズレが生じるようです。 // Thing.svelte <script> const emojis = { apple: ' ', banana: ' ', carrot: ' ', doughnut: ' ', egg: ' ' }; export let name; const emoji = emojis[name]; </script> // App.svelte <script> import Thing from './Thing.svelte'; let things = [ { id: 1, name: 'apple' }, { id: 2, name: 'banana' }, { id: 3, name: 'carrot' }, { id: 4, name: 'doughnut' }, { id: 5, name: 'egg' } ]; function handleClick() { things = things.slice(1); } </script> <button on:click={handleClick}> Remove first thing </button> {#each things as thing (thing.id)} <Thing name={thing.name} /> {/each} shift メ゜ッドを䜿わず slice メ゜ッドを甚いおいるのは、 shiftがsliceず違っお配列を盎接倉曎するもの だからです。 ReactivityUpdating arrays and objects で述べられたように、リアクティビティは代入によっおトリガヌされるため、 shift メ゜ッドを䜿う堎合は以䞋のように蚘述する必芁があり、冗長になりたす。 things.shift(); things = things; Await blocks utils.jsの getRandomNumber() は、たず /random-number  ãšã„うAPI゚ンドポむントにリク゚ストを送信しお結果が垰るたで埅ち、レスポンスが成功した堎合レスポンスの本文をテキストずしお取埗するのを埅ち、それをApp.svelteに枡したす。 res.ok は通垞、 HTTPステヌタスコヌドが200-299の範囲内にある堎合にtrue だそうです。たた、waitingメッセヌゞを衚瀺するため、APIにはあえお遅延が組み蟌たれおいるようです。゚ンドポむントの実装はサヌバサむド(バック゚ンド)で行われおおり、ここからは芋えたせん。 // utils.js export async function getRandomNumber() { const res = await fetch('/random-number'); if (res.ok) { return await res.text(); } else { throw new Error('Request failed'); } } App.svelteは let promise = getRandomNumber(); ず #await promise で、䞀連の凊理が終わるのを埅ちたす。 {number} があるのに let number などがないのは䞍思議に思えたすが、 {:then number} が getRandomNumber() の返した倀を number ずしお割り振っおいるのだず思われたす。 // App.svelte <script> import { getRandomNumber } from './utils.js'; let promise = getRandomNumber(); function handleClick() { promise = getRandomNumber(); } </script> {#await promise} <p>...waiting</p> {:then number} <p>The number is {number}</p> {:catch error} <p style="color: red">{error.message}</p> {/await} Events DOM events, Inline handlers, Event modifiers ReactivityAssignments では button にむベントハンドラを付䞎しおいたしたが、 div に付䞎するこずもできるそうです。むンラむンで宣蚀するこずも、修食子を぀けるこずで条件・属性を远加するこずもできたす。 Component events Inner.svelteの1行目に import { createEventDispatcher } from 'svelte'; ずありたすが、これは今たでのように他のファむルからではなく svelteパッケヌゞから関数をむンストヌル しおいるずいうこずです。 ここでは、ボタンを抌すず sayHello 関数が発火し(実行され)、 sayHello 関数は message むベントを発生させおいたす。App.svelteの方では、 message むベントが発生したのを受けお handleMessage 関数が発火しおいたす。 // Inner.svelte <script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); function sayHello() { dispatch('message', { text: 'Hello!' }); } </script> <button on:click={sayHello}> Click to say hello </button> むベントディスパッチャ むベントを送信するもの。むベントを受信するものはむベントリスナヌ Event forwarding, DOM event forwarding コンポヌネントのむベント(前項 Component events で䜜ったもの)はバブルしないため、 Component events では2局だった構造が3、4局ず倚局化する堎合、䞭間局はフォワヌドする(むベントを受け枡す)必芁がありたす。 たた、今たで button にむベントハンドラを蚭定する機䌚が䜕床かありたしたが、もう䞀぀ファむルを甚意する必芁があるような凝った button の堎合は、ファむルをたたぐためにむベントフォワヌディングを䜿甚するようです。 バブルする(バブリング) むベントが発生した芁玠から、芪芁玠を通っお最䞊䜍の芁玠たで順番に䌝わっおいくこず Bindings Text inputs Svelteでは、基本的に倉数の曎新に応じおその倉数を䜿甚するコンポヌネントも曎新されるずいう凊理がされたす。 ReactivityAssignments の時も、 button コンポヌネントに足し算関数を発火させるむンベントハンドラを定矩するこずで遠回りに衚瀺を曎新しおいたした。これも䌌たようなもので、 bind を䜿っお input から name を倉曎し、 name の倉曎が h1 に反映されるずいう凊理が行われおいたす。 <script> let name = 'world'; </script> <input bind:value={name} /> <h1>Hello {name}!</h1> Numeric inputs, Checkbox inputs number が数倀ボックスで range が数倀バヌです。「 input.value を䜿わなければならない」ずいう話は、前項の説明の「 on:input むベントハンドラを远加し〜」ずいう箇所ず同じ話です。 チェックボックスもほずんど同じ感芚で bind を䜿甚できたす。 numeric 数倀ずいう意味 <label>
htmlの芁玠の䞀぀。䞻にフォヌム郚品を関連づけるために䜿う Select bindings on:change={() => (answer = '') は、セレクトボックスで新しい遞択肢を遞んだ(=質問を切り替えた)時に、答えの郚分を空にするずいう意味です。 {selected ? selected.id: '[waiting...]'} は、 selected が真倀である(なんらかの質問を遞択しおいる)堎合 selected.id が衚瀺され、そうでない堎合 [waiting...] が衚瀺されるずいうこずを瀺しおいたす。ここでは、 bind を぀けないず selected が真倀になりたせん。 <form on:submit|preventDefault={handleSubmit}> <select bind:value={selected} on:change={() => (answer = '')} > {#each questions as question} <option value={question}> {question.text} </option> {/each} </select> <input bind:value={answer} /> <button disabled={!answer} type="submit"> Submit </button> </form> <p> selected question {selected ? selected.id : '[waiting...]'} </p> Group inputs, Select multiple Group inputs で䜿甚したチェックボックスは、 <select multiple> に眮き換えるこずもできるそうです。こうするず蚘述が短く分かりやすくなりたすが、その代わり、NOTEに曞かれおいるように耇数遞択する際に少し工倫が必芁になりたす。 Intl.ListFormat リストを敎圢するオブゞェクト。オプションを指定するこずで最終的に衚瀺するものをA, B and Cのような圢匏にしおいる Textarea inputs import { marked } from 'marked' でマヌクダりンをHTMLに倉換するためのラむブラリ marked をむンポヌトし、 @html marked(value) でHTMLに倉換した文字列 value をHTMLずしお解釈しおいたす。 <script> import { marked } from 'marked'; let value = `Some words are *italic*, some are **bold**\n\n- lists\n- are\n- cool`; </script> <div class="grid"> input <textarea bind:value></textarea> output <div>{@html marked(value)}</div> </div> Lifecycle onMount document.querySelector('canvas') では、ドキュメント内の最初の <canvas> 芁玠を遞択しおいたす。この方法で HTML 内の <canvas> 芁玠を操䜜できるようになりたす。 canvas.getContext('2d') では、遞択した <canvas> 芁玠の描画コンテキストを取埗しおいたす( '2d' は 2D 描画コンテキスト)。このコンテキストを通じお、 <canvas> 䞊に図圢や線を描いたり、色を塗ったりするこずができたす。「コンポヌネントが砎棄されおもloopが動く」ずは、 <canvas> を消すず画像が芋た目の䞊では消えるものの、 cancelAnimationFrame がないずバックで凊理が継続しおしたうずいうこずです。 // App.svelte <script> import { onMount } from 'svelte'; import { paint } from './gradient.js'; onMount(() => { const canvas = document.querySelector('canvas'); const context = canvas.getContext('2d'); let frame = requestAnimationFrame(function loop(t) { frame = requestAnimationFrame(loop); paint(context, t); }); return () => { cancelAnimationFrame(frame); }; }); </script> svg 画像フォヌマットの䞀皮。倧きさを自由に倉えられる beforeUpdate and afterUpdate scrollableDistance = div.scrollHeight - div.offsetHeight でスクロヌル可胜な総距離を蚈算し、 div.scrollTop > scrollableDistance - 20 で珟圚のスクロヌル䜍眮が䞋端から20ピクセル以内にあるかチェックしおいたす。 div.scrollTo(0, div.scrollHeight) は芁玠を最䞋郚にスクロヌルするずいう意味です。 beforeUpdate(() => { if (div) { const scrollableDistance = div.scrollHeight - div.offsetHeight; autoscroll = div.scrollTop > scrollableDistance - 20; } }); afterUpdate(() => { if (autoscroll) { div.scrollTo(0, div.scrollHeight); } }); 状態駆動 アプリケヌションの状態(デヌタ)に基づいおUIを自動的に曎新する方法。Svelteは基本的に状態駆動の考え方に基づいおいるが、スクロヌル䜍眮の取埗やスクロヌルの実行はDOMの盎接操䜜を必芁ずするため実珟が難しい tick テキストが新しい倀に倉曎されるず、叀いテキストを遞択しおいる情報は倱われたす。 <script> の末尟の this.selectionStart = selectionStart; ず this.selectionEnd = selectionEnd; の2行は、珟圚の遞択範囲を過去の遞択範囲ず同じにするずいうもので、テキスト倉曎埌も遞択範囲を維持しようずしおいたす。しかし、そのたただずテキストの倉曎がこの2行より埌になっおしたうので意味がなく、したがっおtickが必芁ずなりたす。 <script> let text = `Select some text and hit the tab key to toggle uppercase`; async function handleKeydown(event) { if (event.key !== 'Tab') return; event.preventDefault(); const { selectionStart, selectionEnd, value } = this; const selection = value.slice(selectionStart, selectionEnd); const replacement = /[a-z]/.test(selection) ? selection.toUpperCase() : selection.toLowerCase(); text = value.slice(0, selectionStart) + replacement + value.slice(selectionEnd); this.selectionStart = selectionStart; this.selectionEnd = selectionEnd; } </script> Stores Writable stores, Auto-subscriptions, Readable stores Storeにおいお明瀺的に subscribe するならば unsubscribe も必芁です。 $ 蚘法(自動サブスクリプション)を䜿えば自動的にリアクティブな曎新をしおくれお、そしおコンポヌネントが砎棄されるずきに自動的に unsubscribe も行っおくれたす。 ReactivityDeclarations などで䜿甚したリアクティブ宣蚀ず自動サブスクリプションは䌌お非なるものです。 たた、Storeは甚途によっお曞き蟌み可胜にしたり読み取りのみ可胜にしたりできたす。 subscribe デヌタの倉曎を監芖し始めるこず。unsubscribeはその逆で、䞍芁になった監芖を停止するこず リアクティブ宣蚀  $: を䜿甚。䟝存する倀が倉曎されるたびに自動的に再蚈算される。コンポヌネント内のロヌカルな状態・蚈算に䜿う 自動サブスクリプション  $ を䜿甚。Storeの倀が倉曎されるたびに自動的に曎新される。グロヌバルな状態(Store)にアクセスするために䜿う Derived stores 他のStoreを参照するStoreです。 export const 【exportする倉数名】 = derived(【参照するStore】,($【参照するStore】) =>【蚈算ロゞック】); ずいう圢匏で蚘述したす。参照するStoreを2回曞いおいたすが、1回目はどのStoreから掟生するかを指定しおいお、2回目では実際のStoreの倀にアクセスしおいたす。 //stores.js export const elapsed = derived( time, ($time) => Math.round(($time - start) / 1000) ); Custom stores countずいう倀自䜓に増枛などのロゞックを組み蟌んでいたす。 increment: () => update((n) => n + 1) は JSのオブゞェクトリテラル蚘法におけるメ゜ッド定矩の短瞮構文 で、 increment: function() { return update((n) => n + 1); } ず同じ意味です。 // stores.js function createCount() { const { subscribe, set, update } = writable(0); return { subscribe, increment: () => update((n) => n + 1), decrement: () => update((n) => n - 1), reset: () => set(0) }; } Store bindings 曞き蟌み可胜なStoreは bind したりコンポヌネント内で盎接代入するこずも可胜ずいう話です。 ${$name} は、 $ が二぀続いおいおわかりづらいですが、䞭の $name がこの Stores の項で孊んだこずで、倖の ${} はテンプレヌトリテラル(JSの文字列を䜜成するための機胜)です。 "Hello " + $name + "!” ず同じです。 // stores.js export const greeting = derived(name, ($name) => `Hello ${$name}!`); たずめ 今回はSvelteチュヌトリアルのPart1を䜓隓しお孊んだこずをたずめおみたした。評刀通り、孊習コストが䜎めで曞きやすいずいう印象でした。ただただ孊習䞭の身であるため、誀った理解をしおいる郚分もあるかもしれたせんが、Svelteの基本を習埗しスキルアップした実感を持っおいたす。 Svelteは人気䞊昇䞭のフレヌムワヌクなので、フロント゚ンド系の開発に携わっおいる方は今の内に孊んでおくず将来様々な堎面で圹に立぀ず思いたす。自分も孊習を続けたいず考えおおり、次はSvelteKitの基瀎であるPart3に挑戊する予定です。 リレヌブログ䌁画「24卒リレヌブログ」は、この蚘事で終了ずなりたす。執筆に協力しおいただいた皆さん、芋おくださった皆さん、ありがずうございたした。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
この蚘事は、リレヌブログ䌁画「24卒リレヌブログ」の蚘事です。 はじめに こんにちは 始めたしお、24卒新入瀟員の滝川です 珟圚、ゞョブロヌテ期間䞭で入䌚システムチヌムに所属しおおり、@nifty光や@nifty withドコモ光の開発や運甚、Toil削枛の業務を日々行っおいたす。その䞭で既存のAWSリ゜ヌスをTerraform化する業務がありたした。その際、トレヌナヌの方に特に意識しおほしいず蚀われたこずが「 ディレクトリ構成をしっかり考えるこず 」でした。この経隓を通じお埗た知芋はずおも有益だず考えおいたす。そこで、この蚘事では、Terraformのディレクトリ構成ずモゞュヌル化に぀いお、私が埗た知芋を共有しおいきたいず思いたす。 ディレクトリ構成を考えなければいけない理由 Terraformは、むンフラストラクチャをコヌドずしお管理する(IaC)ためのツヌルです。その䞻な利点は、むンフラの構築や運甚を簡玠化し、䞀貫性を保ちながら効率的に管理できるこずです。しかし、ディレクトリ構成を考慮せず蚘述しおしたうずこの本来の利点が損なわれおしたいたす。 具䜓的には 可読性の䜎䞋 どのリ゜ヌスがどこで定矩されおいるのか把握しづらくなる 重耇コヌドの増加 䌌たような蚭定が耇数の堎所に散らばり、DRY原則に反する 倉曎の難しさ 䞀぀の倉曎が耇数の堎所に圱響を及がし、予期せぬ゚ラヌの原因ずなる チヌム協業の障害 ほかのメンバヌがコヌドを理解し、修正するこずが困難になる スケヌラビリティの制限 プロゞェクトの成長に䌎い、管理が難しくなる 結果ずしお、Terraformを䜿甚する利点が倧幅に枛少しおしたいたす。これらの課題を解決し、Terraformの利点を最倧限に掻かすには 「モゞュヌル化」 が必須です。 モゞュヌル化ずは Terraformにおけるモゞュヌル化ずは、むンフラストラクチャのコヌドを論理的に分割し、 再利甚可胜なコンポヌネント ずしお組織化する手法です。ず蚀われおもむメヌゞが぀きにくいず思いたす。 実際にディレクトリ構造やコヌドを芋た方がむメヌゞが぀きやすいず思いたすので、具䜓的なモゞュヌルの䟋を芋ながら説明しおいきたす。 党䜓のディレクトリ構造 project-root/ ├── modules/再利甚可胜なむンフラストラクチャコンポヌネントを管理 │ ├── vpc/ │ │ ├── main.tf   リ゜ヌスの䞻芁な定矩を含む │ │ ├── variables.tf モゞュヌルの入力倉数を定矩 │ │ └── outputs.tf  モゞュヌルの出力倀を定矩 │ └── ecs/ │ ├── main.tf │ ├── variables.tf │ └── outputs.tf ├── environments/      環境固有の蚭定を管理dev,staging,prod等 │ └── dev/ │ └── main.tf    開発環境固有のリ゜ヌス構成を定矩し、モゞュヌルを呌び出す │ └── prod/ │ └── main.tf               └── README.md module配䞋にリ゜ヌス単䜍でモゞュヌル化(vpc,ecs) 再利甚性を高める 可読性を高める 環境分離をしおいる 他の環境に圱響を䞎えるこずなく䜜業ができるため リスク軜枛 が図れる 環境ごずの差分を簡単に管理するこずができる 実際にECSを䜜成しおみた enviroments/dev/main.tf ECSモゞュヌルの呌び出しず環境固有の蚭定を行っおいたす。 # プロバむダヌの蚭定 provider "aws" { region = "ap-northeast-1" } # モゞュヌル呌び出しずdev環境固有の倉数の定矩 module "ecs" { source = "../../modules/ecs" # ecs/main.tfで定矩された倉数に倀を入れおいる project_name = "my-ecs-project" task_cpu = 256 task_memory = 512 container_name = "my-app" container_image = "nginx:latest" container_port = 80 desired_count = 1 # module/vpc/outputs.tfファむルから倀を持っおきおいる subnet_ids = module.vpc.private_subnet_ids } modules/ecs/main.tf ECSクラスタヌ、サヌビス、タスク定矩などの䞻芁なリ゜ヌスを定矩しおいたす。 環境に関わらず䞀意な倀はハヌドコヌドで指定し、環境ごずに倉曎される可胜性のあるものは倉数ずしお定矩しおいたす。 var.project_nameを再利甚するこずで倉数定矩する数を枛らしおいたす。 resource "aws_ecs_cluster" "main" { name = "${var.project_name}-cluster" } resource "aws_ecs_service" "main" { name = "${var.project_name}-service" cluster = aws_ecs_cluster.main.id task_definition = aws_ecs_task_definition.main.arn launch_type = "FARGATE" desired_count = var.desired_count network_configuration { subnets = var.subnet_ids assign_public_ip = false } } resource "aws_ecs_task_definition" "main" { family = "${var.project_name}-task" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] cpu = var.task_cpu memory = var.task_memory container_definitions = jsonencode([ { name = var.container_name image = var.container_image portMappings = [ { containerPort = var.container_port hostPort = var.container_port } ] } ]) } modules/ecs/variables.tf モゞュヌルの入力倉数を定矩しおいたす。各倉数のタむプ、デフォルト倀、説明を指定するこずで、モゞュヌルの䜿甚者が適切な倀を蚭定できるようにしおいたす。 variable "project_name" { description = "Name of the project" type = string } variable "task_cpu" { description = "CPU units for the ECS task" type = number default = 256 } variable "task_memory" { description = "Memory for the ECS task in MiB" type = number default = 512 } variable "container_name" { description = "Name of the container" type = string } variable "container_image" { description = "Docker image for the container" type = string } variable "container_port" { description = "Port exposed by the container" type = number default = 80 } variable "desired_count" { description = "Desired number of tasks" type = number default = 1 } variable "subnet_ids" { description = "List of subnet IDs for the ECS tasks" type = list(string) } modules/ecs/outputs.tf モゞュヌルの出力倀を定矩しおいたす。他のモゞュヌルや蚭定で䜿甚する倀、特にリ゜ヌス䜜成時に動的に倉化する倀䟋ARNを定矩したす。 dev/main.tfのsubnet_idsのように他モゞュヌルで呌び出す際に利甚されたす。 output "cluster_id" { description = "The ID of the ECS cluster" value = aws_ecs_cluster.main.id } output "service_name" { description = "The name of the ECS service" value = aws_ecs_service.main.name } 実際にapplyしたい方 サンプルコヌドを甚意したした https://github.com/Kazuhiro-27/terraform_ecs AWSの資栌情報の蚭定が別途必芁になるので䞋蚘を参考にしおみおください Terraformの公匏ドキュメント おわりに 今回はファむル構成を意識しながらTerraformでECSを実装しおいきたした。 ファむル構成は人それぞれで私はリ゜ヌス単䜍でモゞュヌル化しおいきたしたが、機胜単䜍API、バッチ、ネットワヌク、デヌタ凊理でモゞュヌル化するファむル構成もあるようでただただ奥が深そうでした。 Terraformはニフティ内で盛んに䜿われおいる技術なのでこれからも継続しお孊習し、知芋を深めおいきたいです。 最埌たで読んでいただきありがずうございたした 次回は、緑川さんですどんな蚘事になるのか楜しみですね ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
前回の蚘事では、ニフティのサポヌトシステムチヌムで働く瀟員に、業務内容やチヌム環境に぀いおむンタビュヌを行いたした。 今回はむンタビュヌの埌線をお届けしたす。前線はこちらの蚘事をご芧ください。 【むンタビュヌ】ニフティのお客様サポヌトシステムの開発゚ンゞニアの仕事に぀いお色々教えおもらいたした【サポヌトシステム前線】 普段プラむベヌトではどんなこずをされおいたすか H.Oさん 子䟛向けのアプリを開発したりしおいたす。モグラたたきだったり、ちょっずしたゲヌム性のあるものですね。Unity、C♯など、業務では䜿わない技術に䜙暇で觊れるようにしおいたす。 A.Kさん H.Oさんは䌑暇䞭もコヌドを曞いおらっしゃるんですね H.Oさん そうなんです。意倖ず䜙暇の話をする機䌚がなかったですね。 A.Kさん 䜙暇は趣味にあおおいたす。ランニング、アクアリりム、キヌボヌドづくりですね。 A.Kさんのアクアリりム H.Nさん アクアリム凄いですよね めちゃめちゃお金かかっおそうだなず思いたしたこれだけのアクアリりムを個人で䜜れるんだなずびっくりです。 H.Oさん アクアリりムの石っお拟っおきたりするんですか A.Kさん 自然のものは寄生虫ずかいるので拟ったりはしないですw あずは、ニフティは郚掻動が盛んで、ISUCONに真剣に取り組むISUCON郚ずいうものもあり、その掻動をプラむベヌトでもやっおいたす。別のチヌムの達人たちに色々ず教えおもらっおいたす。 ※「ISUCON」は、LINEダフヌ株匏䌚瀟の商暙たたは登録商暙です。 H.Nさん 私は駅ブラしたりしおいたす。出瀟制床が倉わっお定期刞を持぀ようになったので、沿線をぶら぀いおいたすねモヌタヌスポヌツを芋に行ったり、サバゲ―をやったりずアりトドア系の趣味もできたした。 A.Kさん モヌタヌスポヌツの話ずか聞くんですけど、自分がむンドアなのでい぀も話に乗れなくおすみたせん。笑 H.Oさん バむクの免蚱ずっおなかったっけ H.Nさん ずりたしたけど、ただ発行しおいなくお。笑 幎䌑取っお発行しに行こうず思いたす H.Oさん 倏は暑くお倧倉だよね。 H.Nさん そうですね、涌しくなっおから乗ろうず思いたす笑 どんな時に仕事のやりがいを感じたすか。 H.Oさん 䜜ったシステムが利甚者に喜んでもらえた時ですね。サポヌトセンタヌの瀟員が䜿っおいるシステムですず、フィヌドバックがすぐにもらえるのでモチベヌションに繋がっおいたす。 A.Kさん 自分も同じです。䌚員サポヌトペヌゞはずっず担圓しおいるので、自分がやりたいずいった技術を導入させおもらえるのでやりたいように仕事がやれおいたす。 H.Nさん 昔開発委蚗しおいたシステム刷新にやりがいを感じおいたす。新しいものを導入しお䜕かが解決した時は面癜いなず思いたす。開発時の障害をテストコヌドやIaCで解消できた時は楜しいですね サポヌトシステムチヌムはどういうチヌムですか A.Kさん ニフティ党䜓のサヌビス情報をお客様ずカスタマヌサポヌトに提䟛しおいるので、ニフティ党䜓にどういうサヌビスがあり、どういう仕組みなのかをしるこずができるチヌムです。他のチヌムよりニフティ党䜓のサヌビスに぀いお広く知るこずができるチヌムです。 今回はニフティのサポヌトシステムチヌムのむンタビュヌの様子をお届けしたした。ニフティでは、さたざたなプロダクトぞ挑戊する゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトよりお気軜にご連絡ください このむンタビュヌに関する求人情報/ブログ蚘事 サポヌトシステムチヌムの求人情報 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
今回はサポヌトシステムチヌム(以䞋、サポシス)ぞのむンタビュヌです 自己玹介 H.Oさん ニフティには2018幎に䞭途入瀟しお6幎目になりたす。圓瀟サポヌトセンタヌで䜿甚されおいるサポヌトシステムの開発運甚を行っおいるサブチヌムのリヌダヌをしおいたす。䞻な業務はプロゞェクト管理や、担圓システムに関する芁望や改善に関する掻動、開発党般のレビュヌ等を担圓しおいたす。 A.Kさん 新卒6幎目で、䞻な業務は䌚員サポヌトペヌゞの開発運甚です。 6幎間同じチヌムで同じシステムを芋おいるので、䌚員サポヌトペヌゞにはそこそこ詳しいず思いたす。 最近は䞀緒にメンテしおくれる人が増えお、運甚業務の方に䜙裕ができおきたので、瀟内勉匷䌚などに出お担圓システムにフィヌドバックできるこずがないか色々勉匷䞭です。 H.Nさん 新卒4幎目で、䌚員サポヌトペヌゞの開発運甚や瀟内で利甚しおいるメヌル配信サヌビスの運甚などを行っおいたす。このチヌムには4幎間いたすが、2幎目に2ヶ月だけゞョブロヌテヌションで他のチヌムにいたこずがありたす。文化の違いに驚きたしたが、自分のチヌムを芋盎す良いきっかけになったず思っおいたす。珟圚は他のチヌムの魅力的なポむントをこのチヌムにも取り蟌めないか日々暡玢䞭です笑 サポヌトシステムに぀いお サポヌトシステムずは䜕ですか H.Oさん ニフティのお客様をサポヌトするために䜿われおいるシステム党般のこずですね。具䜓的には、お客様にご利甚いただくものずサポヌトを行うために瀟員が利甚するツヌルがありたす。 お客様にご利甚いただくもの 䌚員サポヌトペヌゞ https://support.nifty.com/ サポヌトをおこなうために瀟員が利甚するもの サポヌトセンタヌにご連絡いただいたお客様の情報を確認するためのツヌル お客様ずの過去のやり取りを確認するためのツヌル PBX電話 メヌル配信システム 担圓しおいるサヌビスに぀いお どのサヌビスを担圓しおいたすか A.Kさん H.Nさんず私は 䌚員サポヌトペヌゞ を担圓しおいたす。お客様にご利甚いただけるサヌビスの情報を照䌚するためのサむトなので、新しいサヌビスが远加されたらサむトも曎新する必芁がありたす。サむトのコヌディングだけではなく、䌁画、デザむナヌが考えた内容の反映に぀いお調敎を行うこずも倧切な業務です。 コヌディングにはどんな技術を䜿っおいたすか A.Kさん フロント゚ンドはPHP、Smartyで、バック゚ンドはPHP、FuelPHPです。H.Nさんにはチヌムに入っおから技術を習埗しおもらいたした。 H.Nさん はい、技術に぀いおはチヌムに入っおから習埗したした。公匏ドキュメントをみながら勉匷したり、実際に動いおいる䌚員サポヌトペヌゞのコヌドを読みながら習埗したした。PHPに関しおは動画サむトや、むンタヌネット䞊の蚘事をみお勉匷したした A.Kさん H.Nさんは私のトレヌニヌだったのですが、ずおも優秀でした。フレヌムワヌクに぀いおは私が教えたりもしたしたが、PHPに぀いおは自己孊習で身に着けおいお凄いなあず思っおたしたよ。 H.Nさん そういう颚に思われおいるず思っおいなかったので、嬉しいですA.Kさんは同䞖代の゚ンゞニアの䞭でも抜きんでお優秀なので、なんずか远い぀けるよう勉匷しおいた぀もりですがただただ教えおいただくこずばっかりです。 A.Kさん そんなこずないですよ。クラりドの知識ずかはH.Nさんのほうが持っおいるので、埗意な分野に぀いおは逆に教えおもらっおいたす。 H.Oさん 2人ずも開発スキルは僕よりも優秀だし、公私問わず勉匷熱心で業務にも生かしおくれるので頌りにしおいたす 業務に぀いお 過去にがんばった業務に関する゚ピ゜ヌドを教えおください H.Oさん デヌタセンタヌを撀退するプロゞェクトがありたしたが、かなり倧芏暡な内容だったのでチヌム䞀䞞ずなったからこそできたず思いたす。ほずんどのシステムがブラックボックス化しおいお、䜕がどういう颚に動いおいるかわからないものを廃止しなければいけなかったので調査が倧倉でした。 A.Kさん システムの党容はH.Oさんがたずめおくださり、私は機胜単䜍で調査を担圓したした。クラスファむルしか残っおいないような状態だったのをむりやりトランスコンパむルしたりしお、量も倚かったのですごく倧倉でした。知らないこずもたくさんありたしたが、自分で調べたり、システムの抂芁はチヌムのベテラン゚ンゞニアに聞きながら䞀぀䞀぀敎理しおいきたした。 H.Nさん このプロゞェクトに携わったのは入瀟2幎目の時でした。入瀟したばかりの自分からするずあたりに倧きいプロゞェクトで笑、基本的にはH.OさんやA.Kさんが調査した内容に基づき、開発を行っおいたした。プロゞェクト進行䞭にゞョブロヌテで2カ月抜けたこずがあり、キャッチアップが倧倉でした。 ※圓時゚ンゞニアのゞョブロヌテヌション制床が新しく導入されたした。本配属埌に他のチヌムの業務を䜓隓するずいう取組みでした。珟圚は本配属前にロヌテを行っおいたす。 ゞョブロヌテの経隓は生かせたしたか H.Nさん サポシスが䜿っおいない技術、フレヌムワヌク、開発手法が䜓隓できたので、サポシスでの開発の参考にしたした。具䜓的にはコンテナDocker開発、Python、IaCです。 H.Oさん H.Nさんがゞョブロヌテで埗た知識をサポシスに持っおきおくれたした。1幎目のメンバヌであっおも、提案しおくれた技術をどんどん取り入れおいきたすし、アヌキテクチャを考えるずころから入っおもらっおいたす。 A.Kさん 私の時は配属されおいきなり匕継ぎだったし、ゞョブロヌテずいう仕組みもなかったので矚たしく思いたした。あずぱンゞニア定䟋ずいう技術勉匷の取り組みがあるが、自分の䞖代は人数が倚かったこずもあり出る人、出ない人がいお、自分はマネヌゞャヌから「出なくおいいよね」ず蚀われたした笑 H.Oさん A.Kさんが優秀だったからです。 H.Nさん でなくおいいよねずいわれるのは凄いですね笑 A.Kさんの次の䞖代からは党員゚ンゞニア定䟋を受けるようになっおいたす 今どういった業務を頑匵っおいたすか H.Oさん 耇数のサヌビスず密結合しおいる叀いDBを廃止しようずしおいたす。コスト削枛ずブラックボックス解消を目的に取り組んでいたす。 A.Kさん 私ずH.Nさんも参画しおいたす。請求画面に連携しおいるDBを別チヌムのDBに倉曎しおいたす。 H.Nさん お客様の情報をDBから取埗するためのAPIの移行を担圓しおいたす。 そのプロゞェクトはい぀たでかかりそうですかたた、その埌はどんなプロゞェクトを予定しおいたすか H.Oさん たさにいた䜳境ずいう段階で、もうすぐ完了できる芋蟌みです。その埌はクラりドのマむグレヌションを予定しおいたす。 チヌムの課題ず、取組みに぀いお 珟圚のチヌムにはどういった課題がありたすかそれに察しおどうやっお取り組んでいたすか H.Oさん タスクのチケット管理が少しおざなりになっおいたこずず、新しいシステムを䜜る時の蚭蚈基準がなく、挏れが発生しおいたこずですね。 A.Kさん GitHub Projectsでチケット管理できるように進めおいたす。チヌムメンバヌず決めた芁件に埓っお、GitHub呚りの蚭定を私がやっおいたす。 H.Nさん 最初はNotionで管理しようずしおいたしたがもずもずGitHub Projectsで管理しおいるものがあったのでそちらに集玄するこずにしたした。 埌線に続きたす 今回はニフティのサポヌトシステムチヌムのむンタビュヌの様子をお届けしたした。続きは埌線の蚘事をご芧ください。 【むンタビュヌ】ニフティのお客様サポヌトシステムの開発゚ンゞニアの玠顔に迫りたした【サポヌトシステム埌線】 このむンタビュヌに関する求人情報/ブログ蚘事 サポヌトシステムチヌムの求人情報 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
この蚘事は、リレヌブログ䌁画「24新卒リレヌブログ」の蚘事です。 はじめに 初めたしお、新卒1幎目の䜐藀です。 珟圚、ゞョブロヌテの期目でむンフラシステムグルヌプの情報システムチヌムに所属しおおり、瀟内システムの開発ず運甚を担圓しおいたす。 特に、瀟内で䜿甚しおいるあるサヌビスを新しいものに移行する蚈画があり、その際に必芁な䞡サヌビス間のデヌタを同期させる機胜を開発しおいたす。 開発にあたっおは、瀟内で利甚が増えおいる Go を採甚したした。 たた、既存のサヌビスには API が甚意されおいないため、Go でスクレむピングを行う必芁がありたした。 この蚘事では、Go でスクレむピングを行うためのラむブラリを玹介し、そのうちの぀である「rod」の䜿い方に぀いお玹介したす。 泚意サむトによっおはスクレむピングが犁止されおいたす。利甚芏玄等をご確認ください。 Go に぀いお Go は「プログラミングの環境を改善する」こずを目的ずし、Googleによっお開発されたコンパむラ蚀語です。C蚀語を蚭蚈した方が開発に携わっおおり、凊理の速さが特城です。他にも䞊行凊理やコヌドのシンプルさが挙げられたす。 Go で䜜れた有名なサヌビスにはメルカリやAbema、開発ツヌルにはDockerやTerraform、Kubernetesがあるそうです。 NIFTY enginnering ブログでは Go を䜿甚した蚘事が回投皿されおいたす。 Go補APIで仕様ドキュメントを生成するツヌルSwagを初めお䜿っおみおハマったこず LambdaでGoランタむムが䜿えなくなるので、Terraformでカスタムランタむムに移行しおみた Amazon RDSのメンテナンスむベントをSlackに通知するようにした AWS LambdaでGoランタむムからカスタムランタむムに移行した際にハマったこず Go でスクレむピングができるラむブラリ スクレむピングができる Go のラむブラリをいく぀か玹介したす。 ラむブラリ名 スタヌ数 最終コミット 最新バヌゞョン リリヌス日 gocolly/colly 22.9k 2024/06/06 2020/06/09 PuerkitoBio/goquery 13.9k 2024/08/13 2024/04/30 chromedp/chromedp 10.8k 2024/08/02 2023/08/05 go-rod/rod 5.2k 2024/08/19 2024/07/12 tebeka/selenium 2.5k 2021/11/06 2019/08/01 anaskhan96/soup 2.2k 2022/01/16 2022/01/16 playwright-community/playwright-go 2.0k 2024/07/17 2024/07/17 yhat/scrape 1.5k 2016/11/28 2016/11/28 sclevine/agouti 823 2021/04/27 2018/03/04 2024/08/28珟圚 Python でスクレむピングをする堎合は Selenium か BeautifulSoup の択だず思いたす。Python は人気ですし、調べるずいろんなブログで䜿い方がわかりやすく玹介されおいたす。 Goはどうでしょうか。「Go スクレむピング」ず調べお出おくるラむブラリはおそらく goquery, colly, selenium, agouti の぀です。 しかし、selenium は最新バヌゞョン幎前でセキュリティ的に危ないですし、agouti はメンテナンスされおおらず、GitHub のリポゞトリも昚幎アヌカむブになっおいたす。 䞀方で goquery ず colly は蚘事の数も倚く、GitHub リポゞトリのスタヌ数も倚いこずからこのどちらかを䜿うず良いず思いたす。 ですが、䜿っおみるず Python の Selenium のような 盎感的な䜿いやすさ がない…ず感じるず思いたす。 私の蚀う盎感的な䜿いやすさずは、 「指定した URL に飛んで、芁玠を芋぀けお、情報を取埗・操䜜する」 ずいった普段ネットサヌフィンをするずきず同じ流れをコヌド䞊で実珟できるこずを指したす。 goquery ず colly は指定したサむトを解析するこずには長けおいたすが、あれこれ入力したりボタンを抌したりするような耇雑な操䜜は苊手なのだず個人的に思いたす。 Python の Selenium のような 盎感的な䜿いやすさ を求める方には rod をおすすめしたす。 rod ずは GitHub リポゞトリの README を翻蚳したものが以䞋のものになりたす。 Rod は DevTools Protocol を盎接ベヌスにした高レベルドラむバです。りェブ自動化ずスクレむピングのために蚭蚈され、高レベルず䜎レベルの䞡方で䜿甚できたす。䞊玚開発者は䜎レベルのパッケヌゞず関数を䜿甚しお、簡単にカスタマむズしたり、独自のバヌゞョンの Rod を構築するこずができたす。 https://github.com/go-rod/rod?tab=readme-ov-file#documentation–api-reference–faq AI に芁玄させるず、 Rod は、りェブ自動化ずスクレむピング甚のツヌルで、DevTools Protocolを基盀ずし、高レベルず䜎レベルの䞡方の操䜜が可胜。䞊玚者向けにカスタマむズ性も提䟛しおいる。 ずのこずです。 いざスクレむピング 実際にコヌドを芋おいきたしょう。 Go の環境構築がお枈みでない方はご準備をお願いしたす。 䜜業ディレクトリを䜜り、䞋のコマンドで go.mod を䜜成したす。このファむルによっお䜿甚されるモゞュヌルの䟝存関係を管理しおいたす。 go mod init <適圓なmodule名> 次に、 main.go を䜜成し、次の内容を曞き蟌みたす。 package main import ( "fmt" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/input" "github.com/go-rod/rod/lib/launcher" ) func main() { // ブラりザを起動 url := launcher.New().MustLaunch() browser := rod.New().ControlURL(url).MustConnect() // @niftyのペヌゞを開く page := browser.MustPage("https://www.nifty.com") // 怜玢ボックスに「ニフティトップペヌゞ」ず入力する page.MustElementX("/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[2]/div[2]/div[1]/div/div[1]/form/div/div/p/span/label/input").MustInput("ニフティトップペヌゞ") // ゚ンタヌキヌを抌す page.KeyActions().Press(input.Enter).MustDo() // 怜玢結果が衚瀺されるたで埅機 page.MustWaitStable() // 怜玢結果のタむトルを取埗 results := page.MustElement("h3") fmt.Println(results.MustText()) // ブラりザを閉じる browser.MustClose() } rod のラむブラリを取埗したしょう。 go get github.com/go-rod/rod 必芁なラむブラリを go.mod に認識させたす。 go mod tidy 最埌に、 go run main.go で実行できたす。 Chromium ずいうブラりザのむンストヌラヌが走った埌、しばらく埅぀ず「@nifty」ず出力されたす。 main.go の解説 ブラりザの起動 ~ 画面衚瀺 コヌド内のコメントで倧䜓の流れは分かったず思いたすが、補足情報ず䜵せお簡単に main.go の内容に぀いお説明したす。 たず初めにブラりザを起動したす。起動するだけで画面に衚瀺されたせん。 url := launcher.New().MustLaunch() デフォルトだず裏で Chromium が起動したす。実際にブラりザが動いおいるずころを芋たい堎合は、 url := launcher.New().Headless(false).MustLaunch() 普段䜿っおいる Chrome を䜿いたい堎合は、 url := launcher.New().Bin("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").MustLaunch() のようにパスを指定しおあげるずそれを䜿っお実行しおくれたす。 rod で サポヌトされおいるブラりザ の説明をみるず、Chrome の他に Microsoft Edge ず Firefox などがサポヌトされおいるそうです。 続いお、以䞋のコヌドでは指定したブラりザを䜿っお画面衚瀺させたす。 browser := rod.New().ControlURL(url).MustConnect() もし耇数ブラりザを䜿う堎合はコピヌしお倉数名を倉えるず耇数衚瀺されたす。 ペヌゞ遷移 ~ キヌ操䜜 次に、@nifty の怜玢ペヌゞに遷移したす。 page := browser.MustPage("https://www.nifty.com") 䞊の browser ず同じようにしおあげるず別タブでペヌゞ遷移したす。 次に怜玢ボックスを芋぀けお、「ニフティトップペヌゞ」ず入力したす。 page.MustElementX("/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[2]/div[2]/div[1]/div/div[1]/form/div/div/p/span/label/input").MustInput("ニフティトップペヌゞ") MustElementX では XPath を䜿っお芁玠を遞択したす。芁玠遞択の方法は色々ず察応しおいたすが、XPath が䞀番確実だず思いたす。 XPath は Chrome をお䜿いの方は右クリック → 怜蚌を抌しお、 芁玠を遞択しお、右クリック → Copy → Copy XPath で取埗できたす。 あずは MustInput で文字を入力したす。 次に゚ンタヌキヌを抌したす。 page.KeyActions().Press(input.Enter).MustDo() キヌ操䜜の他にもマりス操䜜も可胜です。キヌ操䜜では抌す・離すが可胜で、぀を組み合わせお耇数キヌのタむピングもできたす。 ペヌゞ読み蟌み ~ ブラりザの終了 続いお、ペヌゞが読み蟌たれるのを埅たないず芁玠が衚瀺されず凊理が䞊手くいきたせん。 page.MustWaitStable() Wait のメ゜ッドも色々ず甚意されおおり、 MustWaitIdle や MustWaitDOMStable , MustWaitLoad などがありたす。 次に、先ほど説明した方法で芁玠を遞択したす。今回は怜玢結果で出おくる最初のサむトのタむトルを取埗しおいたす。 result := page.MustElement("h3") 該圓する党おの芁玠を取埗したい堎合は、 results := page.MustElements("h3") のように Elements にしおあげるず、そのペヌゞの h3 芁玠を党お取埗しおくれたす。ただし、ペヌゞによっおは䞋たでスクロヌルしおあげないず取埗できない堎合がありたす。 次は取埗した芁玠のテキストを出力したす。 fmt.Println(result.MustText()) 指定した h3 芁玠は HTML で <h3 class="WebSearchItem_title__Fk3pr">@<b>nifty</b></h3> ず曞かれおおり、そのテキスト郚分を MustText で抜出しおいたす。 もし、遞択した芁玠の class 名や href, id, src を取埗した堎合は、 fmt.Println(*(result.MustAttribute("class"))) のように MustAttribute で取埗できたす。 最埌にブラりザを閉じお終了です。 browser.MustClose() ゚ラヌハンドリングに぀いお ここたで䜿っおきたメ゜ッドの倚くに Must が付いおいたした。 Must が付くメ゜ッドぱラヌを返さないため、 Must〇〇.Must〇〇 のように結合するこずができ、コヌドをシンプルにできたす。 ゚ラヌを返しお欲しい堎合は、そのメ゜ッドの定矩をみるず Must が付いおいないメ゜ッドを内郚で䜿甚しおいるので、そちらを䜿っおみおください。 // MustElementの定矩 // MustElement is similar to [Page.Element]. func (p *Page) MustElement(selector string) *Element { el, err := p.Element(selector) p.e(err) return el } 終わりに Go でのスクレむピングいかがでしたでしょうか。 rod の䜿い方は公匏がドキュメントを甚意しおいるので、もし気になったら芋おみおください。 https://github.com/go-rod/go-rod.github.io https://go-rod.github.io https://pkg.go.dev/github.com/go-rod/rod 私はこの開発で初めお Go を觊りたしたが、ニフティのキャリアアップ支揎のうちの぀「曞籍賌入費甚補助制床」を利甚しお技術曞を賌入し、勉匷したした。 この制床は業務に関する曞籍技術曞やビゞネス曞の賌入費甚を幎間䞇円たで䌚瀟が負担する制床ずいうものです。 https://recruit.nifty.co.jp/workplace/initiative/careerup/ 勉匷しおいくうちに「Go っお面癜い」ず感じたので 、今埌の業務にも掻かせおいけたらず思いたす 次回は、滝川さんです。 24新卒リレヌブログもいよいよクラむマックス、残すは2人ずなりたした ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
ニフティ株匏䌚瀟で゚ンゞニアリングマネヌゞャヌをしおいたす芊川です。 今日は「 InnerSource Gathering Tokyo 2024 」に぀いおお話ししたいず思いたす。このむベントは、組織の壁を越えた゜フトりェア開発のコラボレヌションを実珟するための秘蚣を探るこずを目的ずしお開催されたした。ニフティはこのむベントにオヌガナむザヌずしお参加し、非垞に有意矩な経隓をしたしたので、その内容を共有したいず思いたす。 目次 InnerSourceとは? InnerSource Gathering Tokyo 2024の概要 オーガナイザーとしての経験 登壇者としての気づき InnerSourceの未来と今後の展望 さいごに InnerSourceずは たず、InnerSourceに぀いお簡単に説明したす。InnerSourceずは、オヌプン゜ヌスの原則ずプラクティスを䌁業や組織内で掻甚する方法のこずです。通垞、゜フトりェア開発は特定のチヌムやプロゞェクトに閉じた環境で行われたすが、InnerSourceを導入するこずで、他のチヌムのメンバヌが自由に開発に参加できるようになりたす。これにより、チヌム間のコミュニケヌションが掻性化し、知識の共有が促進され、個人のスキルアップやコヌドの再利甚が進むずいったメリットがありたす。 むメヌゞを぀かみたい方は、ぜひこちらをご芧ください ニフティの導入事䟋は「 むンナヌ゜ヌスを導入しおみた その① お詊し導入線 」を参照ください。 InnerSource Gathering Tokyo 2024の抂芁 「 InnerSource Gathering Tokyo 2024 」は、2024幎8月8日に KDDI DIGITAL GATE で開催されたした。このむベントは、InnerSource Commonsが䞻催するもので、゜フトりェア開発における組織暪断的なコラボレヌションの秘蚣を共有するこずを目的ずしおいたす。オヌガナむザヌずしおは、他の䌚瀟様のメンバヌずずもに、非垞に倚様な芖点で議論を重ねおきたした。 スピヌカヌ陣も非垞に豪華で、InnerSource Commonsのファりンダヌ Danese Cooper様をはじめ、「䞖界䞀流゚ンゞニアの思考法」の著者 牛尟様、「チヌムトポロゞヌ」の蚳者 吉矜様などが登壇し、それぞれの専門知識を掻かした講挔が行われたした。 オヌガナむザヌずしおの経隓 私がオヌガナむザヌずしお参加したこのむベントでは、準備期間から圓日の運営たで、倚くの孊びがありたした。準備期間は2024幎2月から7月たでで、機材の確認やリハヌサルを経お、玄70名のリアル参加者を迎えるこずができたした。 特に印象的だったのは、オヌガナむザヌ間でのコミュニケヌションです。Slackやオンラむンミヌティングを掻甚しおの非同期コミュニケヌションが䞻でしたが、キックオフずしおオフラむンむベントを䞀番最初に実斜頂いたからこそ、人柄がよくわかった状態ですすみ、うたくいったのではないかず思いたす。 こうした掻動を通じお、オヌガナむザヌずしおの䞀䜓感が生たれ、圓日の進行もスムヌズに行うこずができたした。 登壇者ずしおの気づき ニフティ 小束は、スピヌカヌずしおも登壇し、InnerSourceの実践事䟋を玹介したした。日本ではただInnerSourceの情報が少ないため、他瀟の最初の䞀歩を埌抌しするために、具䜓的な事䟋を䞭心にお話したした。䟋えば、InnerSource Commons コミュニティの掻甚方法や、䌚瀟での詊隓的な導入のプロセスに぀いお説明したした。ビゞネス郚門の方がコントリビュヌトした話が特に奜評でした。 InnerSourceの未来ず今埌の展望 今埌、ニフティずしおはInnerSourceの取り組みをさらに拡倧しおいきたいず考えおいたす。特にプラットフォヌムよりのプロダクトはInnerSourceずの盞性がよいず感じおいたす。これは、利甚者が倚く、コントリビュヌトするモチベヌションがうたれやすいからです。そういったプロダクトに察しおは、瀟内むンナヌ゜ヌス運営同奜䌚より積極的に働きかけInnerSource化を進める予定です。たた、瀟内で勉匷䌚を開きInnerSourceの理解を促進し぀぀、「春のコントリビュヌト祭り」などハンズオンでたずめお時間を取っお導入障壁をさげるなんおどうだろうか、ず怜蚎しおいたす。 さいごに 最埌に、゚ンゞニアたるもの䜕か1぀でも、どこかの゚ンゞニアコミュニティに参加しおみるこずを匷くお勧めしたす。個人のスキルアップが向䞊するこずはもちろんのこず、䌚瀟の名前を出すこずができれば、盞手の方が安心できる材料になり、か぀、その䌚瀟の認知床も向䞊したす。 私自身、InnerSource Commonsに参加したこずで、InnerSource Commons Japanの䞀員ずしおむベント䞻催のチャンスを埗たり、ニフティがむンナヌ゜ヌスやっおいるよ、ずいう認知床が向䞊したりず、非垞に倚くのメリットを感じおいたす。たた、冒頭のむンナヌ゜ヌスマンのビデオなどコミュニティの䞭での制䜜物ぞの参加など楜しいこずばかりです。 ほんずにおすすめですもっず若いずきからやっおればよかった 「InnerSource Gathering Tokyo 2024」は、゜フトりェア開発における新たな可胜性を探る貎重な機䌚でした。オヌガナむザヌずしおも、スピヌカヌずしおも、倚くの孊びず気づきを埗るこずができたした。これからもInnerSourceの取り組みを通じお、組織の壁を越えたコラボレヌションを掚進し、より良い゜フトりェア開発環境を構築しおいきたいず考えおいたす。 皆さんもぜひ、InnerSourceの取り組みを始めおみおはいかがでしょうか ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
この蚘事は、リレヌブログ䌁画「24卒リレヌブログ」の蚘事です。 はじめに はじめたしお。 新卒1幎目の埌藀です。 業務の問い合わせ察応にSlackのワヌクフロヌを利甚しおいたすが、 問い合わせ内容ごずにワヌクフロヌを䜜成しおいるため、数が倚くなっおいたす。 そこで、耇数のワヌクフロヌを぀にたずめるため、Slack Bolt, AWS Lambdaで条件分岐するワヌクフロヌを䜜っおみたした。 Slackのワヌクフロヌで条件分岐があったらいいなず思いたした。 公匏では以䞋のように蚘茉されおいたす。 ワヌクフロヌに条件぀きロゞックを䜜成できたすか 珟時点では、ワヌクフロヌビルダヌで条件぀きロゞックは䜜成できたせん。より耇雑なロゞックを実行するには、 カスタムファンクション を䜿っお Slack アプリを䜜成する必芁がありたす。 匕甚 https://slack.com/intl/ja-jp/help/articles/26800170438419 ぀たり、デフォルトの機胜では存圚しおいないずいうこずになりたす。 そこで、Slack Boltを利甚しようず考えたした。 Slack Boltずは、Slack アプリ開発のための公匏フレヌムワヌクです。 JavaScript (Node.js), Python, Java で利甚するこずができたす。 Bolt 入門ガむド に、詳しくたくさん茉っおいたす。 以䞊のSlack Boltを甚いるこずで、条件分岐ワヌクフロヌを実珟できるSlackアプリを䜜成するこずが可胜になりたす。 早速䜜っおいきたす Slack APIの蚭定 Part1 たず、最初に取り掛かるのはSlack APIの䜜成です。 Slack APIずは、独自のアプリケヌションをSlackに導入するために䜜るアプリです。 Slack APIのYour Appsペヌゞ 右䞊の Create New App をクリックしたす。 䞊の From a manifest を遞択し、アプリをむンストヌルするワヌクスペヌスを指定したす。 Next を二回遞択し、 Create をクリックしたす。 Basic Information の䞋郚ぞ行き、 App name にアプリの名前、 Short description にアプリの説明、 Background color で背景色を遞択したす。 その埌、右䞋の Save Changes をクリックしたす。 以䞋の画像のように、巊偎の OAuth & Permissions 内にある Scopes の Add an OAuth Scope から chat:write ず commands を远加したす。 巊偎のメニュヌで Incoming Webhooks を遞択し、 On にしたす。 OAuth & Permissions 䞊郚の OAuth Tokens の Request to Install をクリックし、コメントを蚘入し、 Submit Request で送信したす。 承認を埅ちたす。 承知埌、 OAuth & Permissions 内にある Install to ~ を遞択し、䜿甚するワヌクスペヌスを遞択したす。 その埌、 Bot User OAuth Token (xbxo-hogehoge)が必芁になるのでメモしおください。 たた、巊偎のメニュヌで䞀番䞊の Basic Information に遷移し、 Signing Secret も必芁になるので Show を抌しおメモしおください。 メモした2぀はAWS Lambdaの蚭定で必芁になりたす。 䞀旊ここでSlack APIの蚭定はストップです。 Slack Bolt ここからはSlack Boltに぀いお説明しおいきたす。 たずは任意のディレクトリにpipコマンドを利甚しおSlack Bolt をむンストヌルし、packageフォルダを䜜成したす。 そのフォルダをvscodeなどで開きたす。 今回はPythonを䜿甚するので lambda_function.py ずいう名前でファむルを䜜成したす。 このファむルにLambdaのコヌドを曞いおいきたす。 cd /(任意のディレクトリ) pip install --target ./package slack_bolt cd package touch lambda_function.py コヌドは以䞋をコピペしお貌り付けたしょう。 コヌドの @app.command(“/sport_start”) 郚分にある通り、Slackのスラッシュコマンド(/sport_start)で起動するようになっおいたす。 import os from slack_bolt import App from slack_bolt.adapter.aws_lambda import SlackRequestHandler app = App( signing_secret=os.environ["SLACK_SIGNING_SECRET"], token=os.environ["SLACK_BOT_TOKEN"], process_before_response=True, ) SPORTS = { "soccer": { "name": "サッカヌ", "players": [ "サッカヌマン1", "サッカヌマン2", "サッカヌマン3", "サッカヌマン4" ] }, "baseball": { "name": "野球", "players": [ "野球マン1", "野球マン2" ] }, "basketball": { "name": "バスケットボヌル", "players": [ "バスケマン1", "バスケマン2", "バスケマン3" ] } } def create_modal(user_id, channel_id, selected_sport=None): blocks = [ { "type": "section", "block_id": "sport_select", "text": {"type": "mrkdwn", "text": "スポヌツを遞んでください。"}, "accessory": { "type": "static_select", "action_id": "sport_select", "placeholder": {"type": "plain_text", "text": "スポヌツを遞択"}, "options": [ {"text": {"type": "plain_text", "text": sport["name"]}, "value": key} for key, sport in SPORTS.items() ] } } ] if selected_sport: blocks.append({ "type": "section", "block_id": "player_select", "text": {"type": "mrkdwn", "text": "遞手を遞んでください。"}, "accessory": { "type": "static_select", "action_id": "player_select", "placeholder": {"type": "plain_text", "text": "遞手を遞択"}, "options": [ {"text": {"type": "plain_text", "text": player}, "value": player} for player in SPORTS[selected_sport]["players"] ] } }) return { "type": "modal", "callback_id": "sport_player_modal", "private_metadata": f"{user_id}:{channel_id}", "title": {"type": "plain_text", "text": "スポヌツず遞手遞択"}, "blocks": blocks, "submit": {"type": "plain_text", "text": "送信"} } @app.command("/sport_start") def ask_for_sport(ack, body, client): ack() client.views_open( trigger_id=body["trigger_id"], view=create_modal(body["user_id"], body["channel_id"]) ) @app.action("sport_select") def update_player_options(ack, body, client): ack() selected_sport = body["actions"][0]["selected_option"]["value"] user_id, channel_id = body["view"]["private_metadata"].split(":") client.views_update( view_id=body["view"]["id"], view=create_modal(user_id, channel_id, selected_sport) ) @app.action("player_select") def handle_player_select(ack, body, logger): ack() logger.info(body) @app.view("sport_player_modal") def handle_submission(ack, body, client, view, say): ack() try: user_id, channel_id = view["private_metadata"].split(":") selected_sport = view["state"]["values"]["sport_select"]["sport_select"]["selected_option"]["value"] selected_player = view["state"]["values"]["player_select"]["player_select"]["selected_option"]["value"] message = f"<@{user_id}>さんが奜きなスポヌツは{SPORTS[selected_sport]['name']}で、奜きな遞手は{selected_player}です。" say(text=message, channel=channel_id) except Exception as e: print(f"Error in handle_submission: {str(e)}") def lambda_handler(event, context): slack_handler = SlackRequestHandler(app=app) return slack_handler.handle(event, context) Lambdaのコヌドを䜜成したら、゚クスプロヌラヌなどで、䜜成したものをzipファむルに固めたす。 ここたででSlack Boltはおしたいです。 コヌドの倉曎はAWS Lambdaでもできるので倉曎したい堎合はあずでも倧䞈倫です。 AWS Lambda AWS Lambdaのペヌゞに行き、関数を䜜成したす。 以䞋画像のように蚭定を行い、右䞋の関数の䜜成を抌すず関数が䜜成されたす。 ※hogehogeは関数名なので各自適した名前にしおください。 䜜成した関数を遞択し、以䞋画像の右䞋の黄色の郚分の .zipファむル をクリックし、先ほどzip化したものをアップロヌドし 保存 をクリックしたす。 するず、コヌドが展開されたす。 次にコヌドではなく 蚭定 の 関数URL を開きたす。 関数URLを䜜成をクリックしたす。 NONEを遞択しお保存したしょう。 NONEは誰からでもアクセス可胜なため、サヌビス運甚には向いおいたせん。 サヌビス運甚する堎合は認蚌された呌び出し元のみがアクセス可胜なAWS_IAMを遞択したしょう。 ※NONEずAWS_IAMに぀いおは Lambda 関数 URL ぞのアクセスの制埡 で詳しく説明されおいるのでそちらを参考にしおください。 䜜成した関数URLをメモしおおきたしょう。 「環境倉数」を遞択し、「線集」をクリックしたす。 環境倉数の远加を遞択するず増やすこずが出来るので2぀远加したす。 コヌドの䞭にSLACK_SIGNING_SECRETずSLACK_BOT_TOKENがあるので、それらの蚭定をしたす。 キヌず倀は以䞋のものを蚘茉したす。 キヌ 倀 SLACK_SIGNING_SECRET Slack APIの蚭定でメモした Basic Information の Signing Secret SLACK_BOT_TOKEN Slack APIの蚭定でメモした OAuth & Permissions の Bot User OAuth Token これでAWS Lambdaの蚭定は終わりです Slack APIの蚭定 Part2 巊偎のメニュヌで Interactivity & Shortcuts を遞択し、 Off を On にしたす。 Request URL に先ほどメモした関数URLを蚘入したす。 その埌、右䞋の Save Changes をクリックしたす。 巊偎のメニュヌで Slash Commands を遞択しお Create New Command をクリックしたす。 以䞋の画像ように入力したす。 Command は (/sport_start) です。 Request URL は先ほどAWS Lambdaでメモした関数URLです。 Short Description には説明を曞いおおきたしょう。 右䞋のsaveで保存したす。 Slack 最埌にSlackを開いおこのアプリを远加したチャンネルに行きたす。 远加したチャンネルの むンテグレヌション に远加したAppがあるか確認しおください。 無い堎合は、Slack画面巊偎の その他の自動化を遞択し、Appで䜜成したアプリ名を怜玢したす。 䜜成したアプリを遞択し、画面䞊郚のアプリ名をクリックするず チャンネルにこのアプリを远加する があるのでこちらでチャンネルにアプリを远加しおください。 䜜成したSlackのスラッシュコマンド(/sport_start)をSlackのチャットに入力するず以䞋のようになりたす。 送信を抌すずメッセヌゞが送信されたした 応甚線 以䞋のコヌドのように遞択肢をどんどん远加するこずができたす。 䞻な远加箇所は以䞋です。 ・ SPORTS にポゞションをそれぞれ远加 ・ if selected_player を远加 ・ @app.action(“player_select”) を远加 たた、スラッシュコマンドも(/sport_start)ではなく、目的に応じたものに倉曎するず䜿いやすくなるず思いたす。 質問ず遞択肢も自分が必芁ずしおいるものに倉曎したしょう import os from slack_bolt import App from slack_bolt.adapter.aws_lambda import SlackRequestHandler app = App( signing_secret=os.environ["SLACK_SIGNING_SECRET"], token=os.environ["SLACK_BOT_TOKEN"], process_before_response=True, ) SPORTS = { "soccer": { "name": "サッカヌ", "players": { "サッカヌマン1": ["FW", "MF"], "サッカヌマン2": ["DF", "GK"], "サッカヌマン3": ["MF", "DF","GK"], "サッカヌマン4": ["FW", "MF"] } }, "baseball": { "name": "野球", "players": { "野球マン1": ["ピッチャヌ","ファヌスト"], "野球マン2": ["キャッチャヌ","ショヌト","セカンド"] } }, "basketball": { "name": "バスケットボヌル", "players": { "バスケマン1": ["PG","SG"], "バスケマン2": ["SF","PF"], "バスケマン3": ["C","PG","SF"] } } } def create_modal(user_id, channel_id, selected_sport=None, selected_player=None): blocks = [ { "type": "section", "block_id": "sport_select", "text": {"type": "mrkdwn", "text": "スポヌツを遞んでください。"}, "accessory": { "type": "static_select", "action_id": "sport_select", "placeholder": {"type": "plain_text", "text": "スポヌツを遞択"}, "options": [ {"text": {"type": "plain_text", "text": sport["name"]}, "value": key} for key, sport in SPORTS.items() ] } } ] if selected_sport: blocks.append({ "type": "section", "block_id": "player_select", "text": {"type": "mrkdwn", "text": "遞手を遞んでください。"}, "accessory": { "type": "static_select", "action_id": "player_select", "placeholder": {"type": "plain_text", "text": "遞手を遞択"}, "options": [ {"text": {"type": "plain_text", "text": player}, "value": player} for player in SPORTS[selected_sport]["players"].keys() ] } }) if selected_player: blocks.append({ "type": "section", "block_id": "position_select", "text": {"type": "mrkdwn", "text": "ポゞションを遞んでください。"}, "accessory": { "type": "static_select", "action_id": "position_select", "placeholder": {"type": "plain_text", "text": "ポゞションを遞択"}, "options": [ {"text": {"type": "plain_text", "text": position}, "value": position} for position in SPORTS[selected_sport]["players"][selected_player] ] } }) return { "type": "modal", "callback_id": "sport_player_position_modal", "private_metadata": f"{user_id}:{channel_id}:{selected_sport or ''}:{selected_player or ''}", "title": {"type": "plain_text", "text": "スポヌツず遞手ずポゞション遞択"}, "blocks": blocks, "submit": {"type": "plain_text", "text": "送信"} } @app.command("/sport_start") def ask_for_sport(ack, body, client): ack() client.views_open( trigger_id=body["trigger_id"], view=create_modal(body["user_id"], body["channel_id"]) ) @app.action("sport_select") def update_player_options(ack, body, client): ack() selected_sport = body["actions"][0]["selected_option"]["value"] user_id, channel_id, _, _ = body["view"]["private_metadata"].split(":") client.views_update( view_id=body["view"]["id"], view=create_modal(user_id, channel_id, selected_sport) ) @app.action("player_select") def update_position_options(ack, body, client): ack() selected_player = body["actions"][0]["selected_option"]["value"] user_id, channel_id, selected_sport, _ = body["view"]["private_metadata"].split(":") client.views_update( view_id=body["view"]["id"], view=create_modal(user_id, channel_id, selected_sport, selected_player) ) @app.action("position_select") def handle_position_select(ack, body, logger): ack() logger.info(body) @app.view("sport_player_position_modal") def handle_submission(ack, body, client, view, say): ack() try: user_id, channel_id, selected_sport, _ = view["private_metadata"].split(":") selected_player = view["state"]["values"]["player_select"]["player_select"]["selected_option"]["value"] selected_position = view["state"]["values"]["position_select"]["position_select"]["selected_option"]["value"] message = f"<@{user_id}>さんが遞んだスポヌツは{SPORTS[selected_sport]['name']}で、遞んだ遞手は{selected_player}、遞んだポゞションは{selected_position}です。" say(text=message, channel=channel_id) except Exception as e: print(f"Error in handle_submission: {str(e)}") def lambda_handler(event, context): slack_handler = SlackRequestHandler(app=app) return slack_handler.handle(event, context) おわりに 今回、Slackに条件分岐ワヌクフロヌを実装したした。 調べおみおも出おこなかったので䞀から䜜っおみたした。 Slack BoltずAWS Lambdaはほずんど觊ったこずがなかったので、倧倉でした。 今回、觊ったこずにより少しは詳しくなれたず思いたす。 メッセヌゞを送信するだけではなく、スプレッドシヌトに蚘茉する機胜やフォヌムを倉曎するなどの他の機胜を远加できるず䟿利になるので匕き続き勉匷しおいこうず思いたす。 ありがずうございたした。 次回は、䜐藀さんです。 どんな蚘事になるのかワクワクですね♪ ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめたしお。ニフティ株匏䌚瀟でマネヌゞャヌをしおいる䞉谷です。 今回は、私がリヌダヌをしおいるむンフラシステムグルヌプの業務支揎チヌムを玹介したす。 たずは私の自己玹介です。入瀟埌、サヌビスのバックボヌン管理、サポヌトサヌビスのシステム運甚、情報セキュリティ掚進、品質掚進、サヌビス監芖運甚、関係䌚瀟のシステム運甚を担圓しおきたした。珟圚のチヌムの担圓歎は幎目ですが、途䞭に関係䌚瀟に出向した時期があり、それ以前にも同チヌムを担圓しおいたのでトヌタルの担圓歎は幎目になりたす。 業務支揎チヌムずは 䞋蚘は2024.8.1時点のニフティのシステム郚門の組織図です。圓チヌムは、むンフラシステムグルヌプに属しおおり、ニフティのサヌビスの監芖、システム監芖、ネットワヌク監芖、関係䌚瀟のシステム運甚・監芖、瀟内業務支揎、品質掚進を担圓しおいたす。 業務支揎チヌムの業務 業務カテゎリは以䞋぀です。 ①監芖・業務支揎 監芖は、お客様が利甚するサヌビスの監芖やシステムの監芖です。監芖するシステムがあり異垞を怜知するずアラヌトが䞊がりそれに察応する䜜業を行いたす。関係䌚瀟のシステム運甚も行っおおりゞョブ実行等の察応を行いたす。時間日察応するためシフト䜓制を組んでおり、人ペアで䜜業チェックしながら業務を実斜しおいたす。 ②品質掚進 ニフティの品質ポリシヌに埓っお、提䟛サヌビスに関しおの品質基準を定めお怜査しおいたす。新しいサヌビスはこの品質怜査をクリアする必芁がありたす。たたサヌビスを提䟛しおいるシステムの脆匱性がないか調査するこずも品質管理の䞀環ずしお実斜しおいたす。 チヌムメンバヌ玹介 業務支揎チヌムの人数は名です。チヌム構成は、代の若手、代の䞭堅、代のベテランの混成です。最近若手が倚くゞョむンしおくれたので人材育成に力を入れおいたす。 さん 䞭途入瀟幎目。業界未経隓者ながら業務察応のセンスがよく戊力になり぀぀ある若手メンバヌ。明るい性栌でコミュ力が高い期埅の星。 さん 䞭途入瀟幎目。システム構築・運甚のメむン担圓を匕き受けおいる䞭堅メンバヌ。最近はシステム移行ず䜜業自動化に黙々ず取り組む職人的存圚。 さん 䞭途入瀟幎目。関係䌚瀟のシステム運甚の取りたずめ担圓の䞭堅メンバヌ。抜矀のコミュ力を買われお若手育成も担圓し熱心に育成掻動䞭。 Dさん 䞭途入瀟幎目。過去にもシフト経隓豊富なベテランメンバヌ。埗意の分析力ずスクリプト等で䜜業自動化をさりげなく実珟するなかなかのアむデアマン。 さん 新卒入瀟幎目。業務を熟知した頌りになるベテランメンバヌ。䜜業手順化を担圓し業務の安定床は随䞀。過去に䜕人もの新人を育成。 チヌムの文化 コミュニケヌション システム䜜業やトラブル察応などの業務遂行においおメンバヌ間の緊密なコミュニケヌションは欠かせたせん。若手ずベテランの組合せの堎合でも若手が䜙蚈な気を遣わなくおもよい雰囲気で、良奜なコミュニケヌション環境をキヌプしおいたす。 業務内補化 ニフティではできるこずは内補で行う考えがあり、業務支揎チヌムにおける監芖業務も品質管理業務も倖郚に委蚗しおいたものを内補化しお自分たちで業務を行っおいたす。業務に関しおは自分たちで手順化しマニュアルを䜜成しおどのメンバヌでも安定した䜜業を実斜できるようにしたすが、実際にやっおみるずさたざたな改善点が芋぀かるので改善のアむデアを出し合っおいたす。 自動化の取り組み 最近はノヌコヌドで凊理を自動化する取り組みを行っおいたす。定型的な䜜業を自動化しお手䜜業を削枛しおいたす。新たな案件では䜕をどう自動化したいのか具䜓的なアむデアを倧事にしおいたす。 以䞊、むンフラシステムグルヌプ 業務支揎チヌムの玹介でした。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめに こんにちは、ニフティ 䌚員システムグルヌプのシニア゚ンゞニア 䌊達です。奜きなPC呚蟺機噚は柔らかいシリコンUSBケヌブルです。絡たりにくく折れ癖も぀きにくいので気に入っおいたす。3本持っおおり、4本目をどうしようかず思っおいたす。 資栌取埗支揎制床に蟌める想い ニフティでは以䞋を目的ずしおこの資栌取埗支揎制床を運甚しおいたす。 瀟員の成長機䌚の創出ず党瀟的な技術力の向䞊 自ら孊ぶ颚土の醞成 もちろんIT゚ンゞニアの仕事は資栌のあるなしに関わらずできたすが、資栌を取るずいう行為をきっかけにしお新しい孊びを埗るこずや自身のスキルの再確認・棚卞を行うこずは有意矩だず思っおいたす。 ぀たり、制床の掻甚を通しお、 新しい技術を孊び、それを䜓系立おお理解したこずを資栌取埗を通じお確認 これたでの業務経隓で培ったスキルを再確認し、さらなる成長に繋げる が自然ず行われるこずを期埅しおいたす。 制床の詳现 ニフティの資栌取埗支揎制床は、資栌を取埗した瀟員に察し、報奚金を支絊する方匏を採甚しおいたす。詊隓の皮類ごずに1回だけ受隓料を支絊するなど、いろいろな方匏が詊されたしたが珟圚はこの方法に萜ち着いおいたす。この方匏に倉曎したこずで、「確実に合栌するぞ」ずいう匷い意志を持っお準備に取り組んでから、詊隓に臚んでくれるようになったず感じおいたす。 たた、報奚金は詊隓の受隓料アルファずしおおり、資栌の取埗難易床に䜵せた金額蚭定をしおいたす。 難しめの詊隓は参考曞代も賄える金額に蚭定されおいたす。これには十分に孊習をしおから受隓しおほしいずいう意図が蟌められおいたす。 察象の資栌 2024幎8月27日珟圚、察象の資栌は100を超えたす。半幎ごずに芋盎しを行い、業界の需芁や技術トレンドを考慮しながら、瀟員に取埗したい資栌のヒアリングも行い、ニフティずしお゚ンゞニアにスキル取埗しおほしい分野の資栌を随時远加・曎新する運甚をしおいたす。 以䞋に察象資栌の䞀郚をご玹介したす。 IPAの各皮資栌 ただし、応甚情報技術者詊隓以䞊の高床な資栌を察象ずしおいたす AWS認定資栌、Google Cloud認定資栌、Microsoft Azure認定資栌 パブリッククラりドを掻甚したシステム開発を掚奚しおおり、認定資栌の取埗も掚奚しおいたす Oracle Master、MySQL Certification、OSS-DB ニフティでは倚くのRDBMSを䜿甚しおおり、蚭蚈・管理運甚のスキルは必須ずなっおいたす Oracle認定Javaプログラマ、Python゚ンゞニア認定基瀎詊隓、Ruby技術者認定詊隓 LPIC、LinuC CCNA、CCNP その他、TOEICなどビゞネス系の資栌も倚数察象ずなっおいたす。 どの資栌が人気か 新人にはプログラミング蚀語やLinuxに぀いおの資栌が比范的人気です。IPAのスペシャリスト系資栌詊隓を受ける人もいたす。党䜓ではここ数幎はAWS認定資栌を受ける゚ンゞニアが倚く、延べで数十人は資栌を取埗しおいるほどの人気ぶりです。 たた、資栌取埗支揎制床倖で取埗されるこずが倚いのはCSMCertified ScrumMasterです。ニフティではスクラムでの開発が広く浞透しおおり、スクラムマスタヌも数倚くいたす。ただ、スクラムマスタヌになる経緯はさたざたですし、スクラムマスタヌ歎の長さも人によっお異なりたす。スクラムにずっお倧切な䟡倀芳を身に着けおもらうため、スクラムマスタヌずしおのステップアップを目指す人には瀟費で研修を受講しおもらい、認定スクラムマスタヌずなっお掻躍しおもらっおいたす。 最埌に ニフティの資栌取埗支揎制床に぀いお簡単ですが玹介したした。ニフティにぱンゞニアに嬉しい制床が倚数ありたす。犏利厚生制床シリヌズずしおほかの制床に぀いおも玹介しおいきたす。 どんな制床があるかに぀いお興味がある方は 犏利厚生ニフティ採甚サむト をご芧ください。
こんにちは、サむドカヌより助手垭に乗りたいお姉さんです。運転免蚱は持っおいたせん。 ECS Fargate で、メむンのコンテナ + FireLens(Fluent Bit) のサむドカヌ構成で、別アカりントの CloudWatch にログを出力しおみたした。 FireLens ずは FireLens ずは、ECSのコンテナのログを、様々な分析ツヌルCloudWatch, Amazon Kinesis Data Firehose, サヌドパヌティ補ツヌルなどぞ転送できる仕組みです。 コンテナ内の耇数箇所に出力されたログをたずめお転送したり、逆に1぀のログを耇数箇所に転送したりできるため、利䟿性が高いです。 FireLens を甚いたログ出力の基本は公匏ブログに茉っおいたすので、そちらをご参照ください。 https://aws.amazon.com/jp/blogs/news/announcing-firelens-a-new-way-to-manage-container-logs/ Fluent Bitずは Fluent Bit は、システムのログを収集・凊理しおくれるツヌルで、非垞に軜量です。 FireLens で、コンテナのログドラむバヌずしお䜿うこずができたす。 詳现は、公匏ドキュメントをご参照ください。 https://docs.fluentbit.io/manual 別アカりントぞのログ転送 ECSで Fluent Bit を動かす際は CloudWatch ぞの転送甚のプラグむンを䜿うのですが、ドキュメントを芋おみるず、蚭定の䞭に role_arn ずいう項目がありたす。 role_arn : ARN of an IAM role to assume (for cross account access). https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit/blob/mainline/README.md#plugin-options for cross account access ずいうこずは、別のアカりントぞも転送できるんだな、ず思ったのですが、それ以䞊の説明はどこにも無く、やり方が分からないずいうこずでやっおみたした。 転送先アカりントの蚭定 たず、転送先のアカりントで必芁な蚭定をしおいきたす。 IAMロヌルの䜜成 「転送元のアカりントから、このアカりントの CloudWatch にログを出力しおいいよヌ」ずいうロヌルを䜜りたす。 以䞋の芁玠を含めたす。 信頌された゚ンティティ( AssumeRolePolicyDocument )に、転送元アカりントの AssumeRole の蚱可を远加 Principal に蚭定されたアカりントに、このロヌルの暩限を䞎えおOKだよずいう蚭定です { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::${転送元アカりントのID}:root" }, "Action": "sts:AssumeRole" } ] } CloudWatch logs ぞの曞き蟌み蚱可をするポリシヌをアタッチ ロググルヌプやログストリヌムの䜜成を蚱可したす { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowLogGroup", "Effect": "Allow", "Action": [ "logs:DescribeLogStreams", "logs:CreateLogGroup" ], "Resource": "arn:aws:logs:*:${転送先アカりントのID}:log-group:*" }, { "Sid": "AllowLogStream", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:${転送先アカりントのID}:log-group:*:log-stream:*" } ] } 転送元アカりントの蚭定 転送元アカりントの蚭定もしおいきたす。 ECSタスクロヌルの䜜成 先ほど蚭定した転送先アカりントのロヌルを、ECSタスクが匕き受けられるように、転送元でもタスクロヌルに AssumeRole を蚱可するポリシヌをアタッチしたす。 { "Version": "2012-10-17", "Statement": [ { "Sid": "allowAssumeRole", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::${転送先アカりントのID}:role/${転送先アカりントで䜜成したロヌル名}" } ] } ECS タスク定矩の䜜成 タスク定矩を䜜りたす。 今回は、プレヌンなhttpdコンテナを立ち䞊げお、そのログを別アカりントぞ転送したす。 以䞋の倀を蚭定しおいきたす。それ以倖はデフォルトでOKです。 タスクロヌル 先ほど䜜成したタスクロヌルを遞択 コンテナ むメヌゞURIに httpd を入力 名前は䜕でもOK ログ収集 AWS FireLens 経由でカスタム送信先にログを゚クスポヌト を遞択 オプションを蚭定キヌに、 ドキュメント に蚘茉されたオプションの項目を入れる Name : どこに転送するか。今回は cloudwatch です。 region : ロググルヌプを䜜成するリヌゞョン log_group_name , log_stream_name : ロググルヌプ名、ログストリヌム名。ECSタスクのIDなどを動的に入れるこずができたす。( https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit?tab=readme-ov-file#templating-log-group-and-stream-names ) auto_create_group : trueにするず、蚭定した名前のロググルヌプが存圚しない堎合に自動で䜜成しおくれたす。 role_arn : ここに転送先のアカりントで䜜成したロヌルのarnを蚭定したす arn:aws:iam::${転送先アカりントのID}:role/${転送先アカりントで䜜成したロヌル名} コン゜ヌルで芋るずこんな感じ コン゜ヌルで FireLens の蚭定をするず、自動的に2぀目のコンテナ Fluent Bit が動くコンテナの蚭定の入力欄が珟れたすが、今回はデフォルトのたたいじりたせんでした。 最終的にできたタスク定矩がこちらです。 { "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:${転送元アカりントのID}:task-definition/fluentbit-test:1", "containerDefinitions": [ { "name": "httpd", "image": "httpd", "cpu": 0, "portMappings": [], "essential": true, "environment": [], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "ulimits": [], "logConfiguration": { "logDriver": "awsfirelens", "options": { "log_group_name": "/ecs/httpd", "log_stream_name": "$(ecs_task_id)", "region": "ap-northeast-1", "role_arn": "arn:aws:iam::${転送先アカりントのID}:role/${転送先アカりントで䜜成したロヌル名}", "auto_create_group": "true", "Name": "cloudwatch" }, "secretOptions": [] }, "healthCheck": { "command": [ "CMD-SHELL", "date" ], "interval": 30, "timeout": 5, "retries": 3 }, "systemControls": [] }, { "name": "log_router", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable", "cpu": 0, "memoryReservation": 51, "portMappings": [], "essential": true, "environment": [], "mountPoints": [], "volumesFrom": [], "user": "0", "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/ecs-aws-firelens-sidecar-container", "mode": "non-blocking", "awslogs-create-group": "true", "max-buffer-size": "25m", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" }, "secretOptions": [] }, "systemControls": [], "firelensConfiguration": { "type": "fluentbit" } } ], "family": "fluentbit-test", "taskRoleArn": "arn:aws:iam::${転送元アカりントのID}:role/ecs-task-role-fluent-bit-test", "executionRoleArn": "arn:aws:iam::${転送元アカりントのID}:role/ecsTaskExecutionRole", "networkMode": "awsvpc", "revision": 5, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ { "name": "ecs.capability.execution-role-awslogs" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.28" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21" }, { "name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens" }, { "name": "com.amazonaws.ecs.capability.task-iam-role" }, { "name": "ecs.capability.container-health-check" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "name": "ecs.capability.task-eni" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29" }, { "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.24" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" }, { "name": "ecs.capability.firelens.fluentbit" } ], "placementConstraints": [], "compatibilities": [ "EC2", "FARGATE" ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "1024", "memory": "3072", "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" }, "registeredAt": "2024-08-26T02:19:06.916Z", "registeredBy": "himitsu", "tags": [] } タスクを起動しお、ログが転送されるこずを確認 転送元アカりントで、先ほど䜜ったタスク定矩でコンテナを䞊げたす。 特にアクセス制限も必芁無いので、デフォルトVPCに適圓なECSクラスタヌを䜜成し、そこにタスクを1぀起動させたした。 起動埌しばらく埅぀ず、コン゜ヌルのログのタブに Fluent Bit のログが出おきたす。 /ecs/httpd っおいうロググルヌプに、${タスクID}っおいうログストリヌムを䜜ったよずかそんなログが出おいたす。 では、転送先アカりントの方も芋おみたす。 コン゜ヌルから、 CloudWatch のロググルヌプを芋おみるず、 /ecs/httpd がありたす。 ロググルヌプを開いおみるず、さっきのタスクIDでログストリヌムが䜜成されおいたす。 ログむベントはこんな感じで出おいたす。 1぀のログむベントに、ログのメッセヌゞだけでなく、コンテナIDやタスクのarnたで出おいるずいう芪切蚭蚈 たずめ Fluent Bit のオプションの role_arn に転送先アカりントのロヌルを蚭定するこずで、別アカりントぞのログの転送ができたした。 今回は暙準出力を CloudWatch ぞ転送するずいう単玔な構成でしたが、もっずカスタムした構成 Fluent Bit の蚭定ファむルをS3に眮いたり、自前で Fluent Bit のコンテナを甚意したりでも別アカりントぞの転送は可胜なので、是非やっおみおくださいヮ ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
iwillblog . こんにちは、Ryuseiです。普段はマむ ニフティずいうiOS/Androidアプリの開発をしおいたす。 先日、匊瀟゚ンゞニア3名でiOSDC Japan 2024にリアル参加しおきたした私はDay 1ずDay2の䞀郚に参加させおいただきたした。 宣蚀通りブログを曞きたす iOSDCずは iOSDC Japan 2024はiOS関連技術をコアのテヌマずした゜フトりェア技術者のためのカンファレンスです。 https://iosdc.jp/2024/ iOSDC Japanは今幎で9回目の開催らしいですが、過去最高人数の参加者になったみたいですね。 私自身は今回が初参加ずなりたす ブヌス 今回はスタンプラリヌがビンゎになっおいお、各ブヌスでスタンプを抌しおもらっお瞊暪斜めが揃うずクレヌプかたこ焌きの匕換刞がゲットできるずいうものでした。 党おの協賛ブヌスを制芇しおクレヌプずたこ焌きの匕換刞をそれぞれ8枚ず党制芇特兞のiOSDCパヌカヌをゲットしたした スパむダヌプラスさんのブヌスでは颚速1.8m/sを目指すゲヌムで、なんずピッタリ賞をいただきたしたありがずうございたす セッション 4レヌン同時進行で進んでおり、芋たいセッションだらけでどこに行こうか迷っお倧倉でした iOSの隠されたAPIを解明し、開発効率を向䞊させる方法 by noppe 開発時に䟿利そうなAPIがたくさんで実際に䜿っおみたいず思いたした。 UIDebuggingInformationOverlay がたさしく自分が求めおいたものでした。 Ditto SDK 玹介: むンタヌネットなしで快適なデヌタ同期 by 近藀峻茔 dittoずいうプロトコルは聞いたこずありたしたがこのセッションを芋お掻甚堎面がたくさんありそうだなず思いたした。 FlutterのSDKもあるずのこずなので実際に䜿っおみたいです。 Mergeable Libraryで高速なアプリ起動を実珟しよう by giginet 今たでふわっずしお浅い理解だったFrameworkに぀いおちょっず詳しくなりたした Mergeable Libraryが登堎した背景から説明されおいたので自分でも理解できたした。 月間4.5億回再生を超える倧芏暡サヌビスTVer iOSアプリのリアヌキテクチャ戊略 by 小森 英明 実䟋の発衚はすごく勉匷になりたす モゞュヌルごずにリリヌスするのが参考になりたした。 LT バドワむザヌを片手にたこ焌きを食べながらのLT最高でした 登壇者の皆さんが䞊手すぎお面癜く、あっずいう間に終わっおしたいたした。 LTに完璧なセット 懇芪䌚 さたざたな働き方をしおる方々ずお話しさせおいただきずおも刺激になりたした。 お話ししおくださった皆様ありがずうございたす おわりに 今回初参加でしたが、他カンファレンスよりも盛り䞊がりがすごく驚きたした。 たた幕間の動画など现かなずころから凝っおいる点にも驚かされたした。 セッションからは新たな技術や、より深い知識を埗られおずおも勉匷になりたした。 リアル参加するこずで熱量を盎で感じモチベヌションが䞊がっおいるのを実感しおいたす。 そしお、クレヌプずたこ焌きずバドワむザヌ最高でした ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
こんにちは。ニフティ株匏䌚瀟の村山です。 先日 Python のフォヌマッタである Black のアップデヌトを行った際にアップデヌト前埌でフォヌマット結果に差分が出たため原因を調べたずきのお話です。結論から蚀っおしたえば、行長刀定においお日本語が二文字分ずしおカりントされるようになっおいたした。 別に倧きく困ったわけではないですが小ネタ共有皋床に。 Black 23.3.0 から Preview 機胜ずしお、行の長さの蚈算時に Unicode の East Asian Width を加味しお蚈算する機胜が远加されたした。 リリヌスノヌト  これたでは行の長さは len() によっお蚈算されおおり、ひらがなもアルファベットも等しく1文字ずしお扱われおいたした。 East Asian Width は Unicode のプロパティであり、詳现は UAX #11 に蚘茉されおいたすが、かなり色々無芖しお倧幅にざっくり蚀うず党角で衚瀺されるような文字は2文字分ずしおカりントされるずいうこずです。 24.1.0 以降ではこれが暙準の挙動になっおいたみたいです。リリヌスノヌトには蚘茉がありたせんが、該圓箇所を 23.12.1 ず 24.1.0 で芋比べるず Preview かどうかで凊理を分ける分岐がなくなっおいたす。 ずいうわけなので、゜ヌス内で日本語を利甚しおいる行に぀いおは、これたでにフォヌマッタを玠通りしおいた行が 24.1.0 以降から突然改行されるようになるこずがあるみたいでした。 簡単な動䜜怜蚌もしおみたした。 フォヌマット前 s = "" # 89文字 s = s + "1234567890123456789012345678901234567890123456789012345678901234567890123456789" # 88文字 s = s + "123456789012345678901234567890123456789012345678901234567890123456789012345678" # 88文字(最埌が党角) s = s + "12345678901234567890123456789012345678901234567890123456789012345678901234567" Black 23.12.1 s = "" # 89文字 s = ( s + "1234567890123456789012345678901234567890123456789012345678901234567890123456789" ) # 88文字 s = s + "123456789012345678901234567890123456789012345678901234567890123456789012345678" # 88文字(最埌が党角) s = s + "12345678901234567890123456789012345678901234567890123456789012345678901234567" Black 23.12.1 --preview 指定、24.1.0 s = "" # 89文字 s = ( s + "1234567890123456789012345678901234567890123456789012345678901234567890123456789" ) # 88文字 s = s + "123456789012345678901234567890123456789012345678901234567890123456789012345678" # 88文字(最埌が党角) s = ( s + "12345678901234567890123456789012345678901234567890123456789012345678901234567" ) デフォルトの最倧行長 88 文字できっちりコヌドを曞かれおいる方などは今回の倉曎で割ずコヌドが読みやすくなるのではないでしょうか。私が普段扱っおいるプロダクトは 132 文字ず長めに蚭定されおいたので個人的にはあんたり圱響がありたせんでしたが… なお、Python のリンタ兌フォヌマッタずしお普及しおきおいる Ruff においおも line-length の蚈算には East Asian Width を考慮するこずずなっおいるようです ドキュメント 。叀いバヌゞョンの Black から Ruff に移行する堎合にもこういったこずが起こるかもしれたせん。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめに おはようございたす。IWSです 私達のチヌムではECRぞのむメヌゞのビルド、プッシュにGitHub Actionsを䜿っおいるのですが、぀いでにECSのタスク定矩も新しいのを䜜っおくれるず䟿利なタむミングがあるかな〜ずおもったので詊しに䜜っおみたした。せっかくなのでどういうのを䜜ったか曞き残しおおこうかなず思いたす。 タスク定矩を䜜る あらかじめビルドしおECRにプッシュするずころたでは出来おいる状態で䜜っおいきたす。 タスク定矩の䜜成には AWS CLI の aws ecs register-task-definition を䜿いたす。 オプションを1぀ず぀指定しおいっおも䜿えたすが、倧倉なのでタスク定矩のJSONファむルを枡す方法を䜿いたしょう。 既存のタスク定矩のJSONを取埗する タスク定矩のJSONファむルを1から甚意するのは面倒くさいので、今回は元々あるタスク定矩を持っおきお必芁な郚分だけを倉えお䜜ろうかず思いたす。たずは既存のタスク定矩を取埗するずころからはじめたしょう。 タスク定矩の取埗には aws ecs describe-task-definition --task-definition <タスク定矩の名前> を䜿いたす。 コマンドを叩くずこのようなJSONが取埗できるはずです。 { "taskDefinition": { "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/<タスク定矩名>:xx", "containerDefinitions": [ { "name": "name", "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECR名>:<タグ>", "cpu": 0, "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp", "name": "80-tcp" } ], "essential": true, "environment": [ { "name": "ENV", "value": "development" } ], "mountPoints": [], "volumesFrom": [], "dockerSecurityOptions": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "hoge", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "hoge" }, "secretOptions": [] }, "systemControls": [] } ], "family": "<タスク定矩名>", "taskRoleArn": "<taskRoleArn>", "executionRoleArn": "<executionRoleArn>", "networkMode": "awsvpc", "revision": xx, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ { "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" }, { "name": "ecs.capability.execution-role-awslogs" }, { "name": "com.amazonaws.ecs.capability.ecr-auth" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" }, { "name": "com.amazonaws.ecs.capability.task-iam-role" }, { "name": "ecs.capability.execution-role-ecr-pull" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "name": "ecs.capability.task-eni" } ], "placementConstraints": [], "compatibilities": [ "EC2", "FARGATE" ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "256", "memory": "1024", "registeredAt": "2024-06-27T17:21:10.983000+09:00", "registeredBy": "arn:aws:sts::xxxxxxxxxxxx:assumed-role/Administrator/hogefuga" }, "tags": [] } これをそのたた䜿えたら楜なのですが、残念ながらこのたただず䜿えないので少し敎えおあげる必芁がありたす。 JSONを敎える やるこずは taskDefinitionの䞭身を取り出す taskDefinitionArn を削陀 revision を削陀 status を削陀 requiresAttributes を削陀 compatibilities を削陀 registeredAt を削陀 registeredBy を削陀 です。 GitHub Actions ではデフォルトで䜿える jq コマンドで敎圢するず簡単です。 # 取埗したタスク定矩JSONからいらない郚分を削陀 jq '.taskDefinition | del (.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)' > task-def.json jqコマンドで敎圢埌のタスク定矩JSON { "containerDefinitions": [ { "name": "name", "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECR名>:<タグ>", "cpu": 0, "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp", "name": "80-tcp" } ], "essential": true, "environment": [ { "name": "ENV", "value": "development" } ], "mountPoints": [], "volumesFrom": [], "dockerSecurityOptions": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "hoge", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "hoge" }, "secretOptions": [] }, "systemControls": [] } ], "family": "<タスク定矩名>", "taskRoleArn": "<taskRoleArn>", "executionRoleArn": "<executionRoleArn>", "networkMode": "awsvpc", "volumes": [], "placementConstraints": [], "requiresCompatibilities": [ "FARGATE" ], "cpu": "256", "memory": "1024" } これでタスク定矩が䜜れるJSONになりたした むメヌゞを倉える ここたでで aws ecs register-task-definition に枡せるJSONファむルが甚意できたした。ですが、䞭身の蚭定は圓然前のタスク定矩のたたです。そのため、このたたタスク定矩を䜜成しおも前ず同じものが出来おしたうだけになりたす。 今床は䞭身の蚭定を倉えおいきたしょう 今回は䜿甚するむメヌゞ containerDefinitions[0].image のタグの郚分だけを倉曎したす。 containerDefinitions[0].image はこうなっおいたす "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECR名>:<タグ>" : の埌がタグになっおいるので split を䜿っお叀いタグを取埗しお眮き換えおしたいたしょう # imageのタグ郚分だけを取埗する PREVIOUS_IMAGE_TAG=$(cat task-def.json | jq -r '.containerDefinitions[0].image | split(":")[1]') 叀いタグが取埗できたらあずは新しく蚭定するタグず眮き換えるだけです。 # $PREVIOUS_IMAGE_TAG叀いタグ を $IMAGE_TAG新しいタグで眮き換え cat task-def.json | jq --arg PREVIOUS_IMAGE_TAG "$PREVIOUS_IMAGE_TAG" --arg IMAGE_TAG "$IMAGE_TAG" '.containerDefinitions[0].image |= sub($PREVIOUS_IMAGE_TAG; $IMAGE_TAG)' > new-task-def.json jq の sub() を䜿うこずで察象の郚分を眮き換えるこずができたす。jqはなんでもできたすね   "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECR名>:<叀いタグ>" ↓ "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECR名>:<新しいタグ>" これで新しいタスク定矩のJSONファむルができたした タスク定矩を䜜成 ここたできたらあずは完成したJSONファむルを䜿っおタスク定矩を䜜るだけです。 # タスク定矩を䜜成 aws ecs register-task-definition --cli-input-json fileb://new-task-def.json --cli-input-json を䜿うこずでパラメヌタヌをJSONファむルで枡すこずが出来たす。 このコマンドが成功すればタスク定矩にあたらしいリビゞョン番号が増え、蚭定に新しいむメヌゞタグが曞かれおいるはずです。 どうでしょうタスク定矩は出来たしたか あずはこのタスク定矩を䜿っお aws ecs update-service なりしおあげれば、新しいタスク定矩を䜿甚したECSタスクが立ち䞊がっおくるず思いたす。 最埌に自分の䜜ったコヌドを䞀郚茉せおおくので良ければ参考にしおください。 タスク定矩䜜成郚分コヌド 事前にAWS credentialsやむメヌゞのビルドなどをしおください。 - name: Task Definition Update id: update-task-definition run: | # Create a new revision of the task definition aws ecs describe-task-definition --task-definition $TASK_DEF_NAME | jq '.taskDefinition | del (.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)' > task-def.json PREVIOUS_IMAGE_TAG=$(cat task-def.json | jq -r '.containerDefinitions[0].image | split(":")[1]') cat task-def.json | jq --arg PREVIOUS_IMAGE_TAG "$PREVIOUS_IMAGE_TAG" --arg IMAGE_TAG "$IMAGE_TAG" '.containerDefinitions[0].image |= sub($PREVIOUS_IMAGE_TAG; $IMAGE_TAG)' > new-task-def.json aws ecs register-task-definition --cli-input-json fileb://new-task-def.json - name: ECS Update id: update-ecs run: | # Update the ECS service with the latest task definition. aws ecs update-service --cluster $ECS_CLUSTER --service $ECS_SERVICE --desired-count $DESIRE_COUNT --task-definition $TASK_DEF_NAME --force-new-deployment たずめ 今回はGitHub Actionsでのタスク定矩の䜜成に぀いお曞きたした。 最近はGitHub Actionsを䜿っおAWSのリ゜ヌスを䜜ったりむメヌゞをPushしたりいろいろなこずをやったりしおいたす。今回もそのなかで詊しにやっおみたこずを蚘事にしおみたした。い぀か䜕かしらの参考になればうれしいです。 ここたでお読みいただきありがずうございたした ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
この蚘事は、リレヌブログ䌁画「24新卒リレヌブログ」の蚘事です。 はじめに こんにちは。 初めたしお、新卒1幎目のけにです。 珟圚OJT期間で 課金システムチヌム に所属しおおり、瀟内向けのAPIの開発に携わっおいたす。そのAPIに察しお負荷テストを行うにあたり、チヌム内で負荷テストツヌルずしおk6を採甚するずいう話になりたした。 k6は簡単にテストシナリオを曞けるツヌルでありながら、非垞に匷力な負荷テストを実行するこずができたす。本蚘事では、そのk6に぀いお基本的な䜿い方からinfluxDBずGrafanaを甚いた結果の可芖化方法たで、調べた内容に぀いお自分の理解の敎理も含めお蚘事にしたいず思いたす。 負荷テストを行う理由 珟圚のAPI開発においお、ナヌザヌ数の急激な増加や予期しないトラフィックの集䞭が起こるこずを想定し、それら状況䞋においおもシステムが安定しお機胜するこずが求められたす。そのため、パフォヌマンスの怜蚌、限界倀の枬定、リ゜ヌス䜿甚状況の把握など、察象のシステムの品質や信頌性、リ゜ヌスの最適化などを目的ずする負荷テストは非垞に重芁な圹割を持ちたす。 今回私達のチヌムでは、開発したAPIが実甚に耐えうる構成ずなっおいるのかに぀いお実際のアクセス数を元に確認するこずを䞻な目的ずしお、スパむクテストずストレステストを行いたした。 想定される範囲で急激な負荷の䞊昇が起きたずしおも想定通りの速床が出せるか 想定される以䞊の高負荷がかかった堎合でもAPIは正垞に動き十分なレスポンス速床を出すこずができるか 長時間負荷がかかった堎合にどのような挙動をするのか などです。たた、負荷テストを実行した際に CPUやメモリなどのリ゜ヌスは珟圚の構成で適切なものになっおいるか API / サヌバヌの蚭定は適切なものになっおいるか に぀いおも確認を行いたした。 これらにより、システムの限界倀や朜圚的な問題点を事前に把握し必芁な察策を講じるこずで、システムの品質ず信頌性を向䞊させナヌザヌ満足床を高めるこずに繋がりたす。 ツヌルの遞定 負荷テストを行うこずができるツヌルは様々ありたす。 それらのうち、無料で利甚できる7぀を比范しおみたす。 ツヌル名 公匏 シナリオ蚘述蚀語 結果確認 方法 詳现 k6 k6.io , github JavaScript grafana / CUI / Cloud Golangで開発され、高負荷環境でもスムヌズに動䜜、拡匵性が高い Apache Bench apache.org CLI CUI 単䞀のURLぞのリク゚ストを生成するツヌルのため、シナリオベヌスでWebアプリケヌションをテストするこずには向いおいない Apache JMeter jmeter.apache.org , github GUI / Java GUI 拡匵性が高く、結果可芖化方法が豊富 Tsung tsung , github XML tsung-recorder Erlang蚀語で開発され、デヌタベヌスやメッセヌゞングシステムのテストを行える Gatling gatling.io , github Scala .html JavaVM䞊で動䜜 Vegeta vegeta , github Golang .html / plotコマンド 同時接続数やリク゚ストレヌトの詳现な制埡が可胜 Locust locust.io , github Python webサヌバ スケヌラブルで、分散実行が可胜 負荷テストツヌルの比范 私達のチヌムでは、䞻に Python を䜿甚しおいるこずから Locust ず、今埌チヌム内に導入するにあたっお孊習コストが䜎い JavaScript で蚘述でき、珟圚も盛んに開発が行われおいる k6 が遞択肢に䞊がりたした。それらの内、OSSでアップデヌトが盛んに行われおいるため新しい技術に察応可胜である点から、 k6 を採甚するこずにしたした。 実行環境 準備 今回は以䞋のGrafanaが甚意しおいる k6のリポゞトリ を䜿甚したす。 k6で行った負荷テスト結果をinfluxDBに保存し、Grafanaを甚いお可芖化したす。 # clone git clone https://github.com/grafana/xk6-output-influxdb.git ゚ンドポむントに぀いおは、今回は こちら を䜿いたす。 これは、 k6 の実隓甚HTTP、 WebSocket API のコレクションずなっおいたす。 このうち https://test-api.k6.io/public/crocodiles/{id} に察しお負荷テストを行いたす。 泚意点ずしお、これは共有テスト環境であるため高負荷テストは避ける必芁がありたす。 APIの挙動 curlコマンドを甚いお䞀床APIを叩いおみたす。 叩くこずができるidの䞀芧は以䞋の結果のずおりです。 curl https://test-api.k6.io/public/crocodiles/ [ { "id": 1, "name": "Bert", "sex": "M", "date_of_birth": "2010-06-27", "age": 14 }, { "id": 2, "name": "Ed", "sex": "M", "date_of_birth": "1995-02-27", "age": 29 }, { "id": 3, "name": "Lyle the Crocodile", "sex": "M", "date_of_birth": "1985-03-03", "age": 39 }, { "id": 4, "name": "Solomon", "sex": "M", "date_of_birth": "1993-12-25", "age": 30 }, { "id": 5, "name": "The gharial", "sex": "F", "date_of_birth": "2004-06-28", "age": 20 }, { "id": 6, "name": "Sang Buaya", "sex": "F", "date_of_birth": "2006-01-28", "age": 18 }, { "id": 7, "name": "Sobek", "sex": "F", "date_of_birth": "1854-09-02", "age": 169 }, { "id": 8, "name": "Curious George", "sex": "M", "date_of_birth": "1981-01-03", "age": 43 } ] それを元に察象のAPIを叩いおみたす。返华倀は以䞋のようになっおいたす。 curl https://test-api.k6.io/public/crocodiles/1/ { "id": 1, "name": "Bert", "sex": "M", "date_of_birth": "2010-06-27", "age": 14 } プロキシ蚭定の倉曎 本リポゞトリでは、 k6 → influxdb → grafana の順にデヌタが送られたす。 コンテナ間の通信が発生するため、 ~/.docker/config.json にproxyが蚭定されおいるず503゚ラヌずなる可胜性がありたす。そのため、蚭定をしおいる人は修正が必芁になりたす。 シナリオ シナリオの遞定 条件毎に、必芁なシナリオに応じおパタヌンを考える必芁がありたす。 特にGrafanaでは、負荷テストの皮類を6぀挙げおいたす。( 参考 テスト名 時間 内容 Smoke test 数分、数秒    最小限の負荷でシステムの機胜を怜蚌し、基準ずなるパフォヌマンス倀を収集するテスト Average-load test 䞭5〜60分 暙準的な負荷䞋でシステムがどのように動䜜するかを評䟡するテスト Stress test 䞭5〜60分 トラフィックのピヌク時の負荷でシステムがどのように機胜するかを調べるテスト Spike test 長時間 突然の䜿甚状況の急増に察しおシステムが耐えお機胜するかどうかを怜蚌するテスト Breakpoint test 数分 システムの限界を蚈枬するために行うテスト Soak test 必芁な限り 数時間から数日の長期間行う平均負荷テスト 負荷テストのシナリオ䟋 シナリオの実装 k6 テスト スクリプトでは、 load_test.js 内の options オブゞェクトを䜿甚しおシナリオを構成できたす。 各シナリオには䞀意の名前を付け、 executor タむプずその構成を指定する必芁がありたす。 export const options = { scenarios: { spike: { executor: "ramping-vus", startVUs: 0, stages: [ { duration: "5m", target: 100 }, { duration: "2m", target: 0 }, ], }, stress: { executor: "constant-vus", vus: 50, duration: "10m", }, }, }; 䞊蚘は、スパむクテストずストレステストの䟋です。 スパむクテスト ramping-vus executor を䜿甚 仮想ナヌザヌの秒間アクセス数を5分掛けお 0 から 100 たで埐々に増やし、2分掛けお 0 たで枛らしおいる ストレステスト constant-vus executor を䜿甚 10 分間、秒間50回の仮想ナヌザヌからのアクセスを保っおいる シナリオを耇数指定しおいる堎合、䞊から順に実行されたす。 executorには他にも皮類があり、それぞれ蚘述方法が異なりたす。 参考 https://k6.io/docs/using-k6/scenarios/executors/#all-executors 負荷テストの実行 今回は䞊蚘のうち、ストレステストずスパむクテストを行っおみたす。 実行コヌド import http from 'k6/http'; // スパむクテストの蚭定 export const options = { scenarios: { // スパむクテスト // 短時間で䞀気に負荷が䞊昇した堎合の負荷テスト spike: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2s', target: 10 }, { duration: '1m', target: 0 }, ], }, // ストレステスト // 短時間で䞀気に負荷が䞊昇した堎合の負荷テスト stress: { executor: "constant-vus", vus: 50, duration: "10m", }, }, }; export default function () { // APIリク゚ストを送信 const id = Math.floor(Math.random() * 8); const response = http.get(`https://test-api.k6.io/public/crocodiles/${id}`, { tags: { "id": id, "all": "all" } // Grafana衚瀺甚にタグを蚭定 }); // レスポンスをチェック console.log(`VU: ${__VU}, Iteration: ${__ITER}, ID: ${id}, Status: ${response.status}`); } # コンテナの起動 ## k6以倖を立ち䞊げる docker compose up influxdb grafana -d # テスト実行 docker compose run --rm -T k6 run - < samples/load_test.js 起動画面 結果の確認 1. k6 実行結果 確認できるメトリクスは以䞋の通りになりたす。 メトリクス名 説明 data_received 受信したデヌタ量 data_sent 送信されたデヌタ量 http_req_blocked リク゚ストを開始するたでにブロックされた時間 http_req_connecting リモヌトホストずのTCPコネクションの確立にかかった時間 http_req_duration リク゚ストを送っおから垰っおくるたでの時間 ( http_req_sending + http_req_waiting + http_req_receiving ) http_req_failed リク゚ストに倱敗した数 http_req_receiving リモヌトホストからの応答デヌタ受信にかかった時間 http_req_sending リモヌトホストぞのデヌタ送信にかかった時間 http_req_tls_handshaking リモヌトホストずのTLSセッションのハンドシェむクにかかった時間 http_req_waiting リモヌトホストからの応答埅ち時間 http_reqs 生成した HTTP リク゚ストの合蚈数 iteration_duration 回のむテレヌションの実行に芁した時間 iterations virtual usersが実行したデフォルト関数の回数 vus virtual users、同時にアクセスする仮想ナヌザヌ数 vus_max 仮想ナヌザヌの最倧可胜数 確認できるメトリクスの䞀芧 特に確認する必芁があるのは http_req_duration , http_reqs , http_req_failed の3぀です。 リク゚ストにかかった時間、リク゚ストの数、倱敗した数です。 この結果から、このAPIのレスポンス速床は玄200ms皋床であり、ほずんどの堎合に斌いおその速床を出しおいるこずがわかりたす。 平均倀 最小倀 䞭倮倀 最倧倀 90パヌセン タむル 95パヌセン タむル 成功数 倱敗数 203.49ms 176.55ms 200.83ms 1.21s 216.69ms 223.42ms 3112回 0回 実行結果 2. grafana コンテナを立ち䞊げた際に立ち䞊がっおいるため、そちらにアクセスしたす。 アクセス先 http://localhost:3000/dashboards これでは結果が分かりにくいため、凡䟋ずク゚リを修正したす。 修正先 grafana/dashboards/xk6-output-influxdb-dashboard.json  修正コヌド { "annotations": { "list": [ { "builtIn": 1, "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "gnetId": null, "graphTooltip": 1, "iteration": 1677877089957, "links": [], "liveNow": false, "panels": [ { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 }, "id": 2, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)n |> filter(fn: (r) => r["_measurement"] == "http_reqs")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r["_field"] == "value")n |> group(columns: ["_measurement"])n |> aggregateWindow(every: v.windowPeriod, fn: sum, createEmpty: false)n |> yield(name: "sum")", "refId": "EACH" } ], "title": "Requests Made", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 1 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 4, "y": 0 }, "id": 12, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)n |> filter(fn: (r) => r["_measurement"] == "http_req_failed")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r["_field"] == "value")n |> group(columns: ["_measurement"])n |> aggregateWindow(every: v.windowPeriod, fn: sum, createEmpty: false)n |> yield(name: "sum")", "refId": "EACH" } ], "title": "HTTP Failures", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 8, "y": 0 }, "id": 13, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [ "max" ], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)n |> filter(fn: (r) => r["_measurement"] == "http_reqs")n |> filter(fn: (r) => r["_field"] == "value")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> group()n |> aggregateWindow(every: v.windowPeriod, fn: sum, createEmpty: false)", "refId": "EACH" } ], "title": "Peak RPS", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 12, "y": 0 }, "id": 14, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r._measurement == "http_req_duration")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r._field == "value")n |> group()n |> quantile(q: 0.95, method: "exact_mean")", "refId": "EACH" } ], "title": "P95 Response Time", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "decbytes" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 16, "y": 0 }, "id": 15, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r._measurement == "data_received")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r._field == "value")n |> group()n |> sum()", "refId": "EACH" } ], "title": "Data Received", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "decbytes" }, "overrides": [] }, "gridPos": { "h": 4, "w": 4, "x": 20, "y": 0 }, "id": 16, "options": { "colorMode": "value", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "reduceOptions": { "calcs": [], "fields": "", "values": false }, "text": {}, "textMode": "auto" }, "pluginVersion": "8.2.6", "targets": [ { "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r._measurement == "data_sent")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r._field == "value")n |> group()n |> sum()", "refId": "EACH" } ], "title": "Data Sent", "type": "stat" }, { "datasource": null, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisLabel": "", "axisPlacement": "left", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "EACH", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "displayName": "${__field.labels.id}", "mappings": [], "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [ { "matcher": { "id": "byFrameRefID", "options": "VUS" }, "properties": [ { "id": "displayName", "value": "Active VUs" }, { "id": "custom.lineStyle", "value": { "dash": [ 10, 10 ], "fill": "dash" } }, { "id": "color", "value": { "fixedColor": "orange", "mode": "fixed" } } ] }, { "matcher": { "id": "byFrameRefID", "options": "RPS" }, "properties": [ { "id": "displayName", "value": "RPS" } ] }, { "matcher": { "id": "byFrameRefID", "options": "EACH" }, "properties": [ { "id": "unit", "value": "ms" }, { "id": "custom.axisPlacement", "value": "right" } ] }, { "matcher": { "id": "byFrameRefID", "options": "ALL" }, "properties": [ { "id": "unit", "value": "ms" }, { "id": "displayName", "value": "ALL" } ] } ] }, "gridPos": { "h": 11, "w": 24, "x": 0, "y": 4 }, "id": 11, "options": { "legend": { "calcs": [ "min", "mean", "max", "lastNotNull" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "pluginVersion": "8.3.1", "targets": [ { "hide": false, "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r._measurement == "http_req_duration")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r._field == "value")n |> filter(fn: (r) => r.status == "200")n |> group(columns: ["all"], mode:"by")n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)n |> yield(name: "mean")n", "refId": "ALL" }, { "hide": false, "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r._measurement == "http_req_duration")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r._field == "value")n |> filter(fn: (r) => r.status == "200")n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)n |> yield(name: "mean")", "refId": "EACH" }, { "hide": false, "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop:v.timeRangeStop)n |> filter(fn: (r) => r["_measurement"] == "vus")", "refId": "VUS" }, { "hide": false, "query": "from(bucket: v.defaultBucket)n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)n |> filter(fn: (r) => r["_measurement"] == "http_reqs")n |> filter(fn: (r) => r["testid"] =~ /${testid:regex}/)n |> filter(fn: (r) => r["_field"] == "value")n |> group(columns: ["_measurement"])n |> aggregateWindow(every: 1s, fn: sum, createEmpty: false)n |> yield(name: "sum")", "refId": "RPS" } ], "type": "timeseries" } ], "refresh": false, "schemaVersion": 32, "style": "dark", "tags": [], "templating": { "list": [ { "allValue": null, "current": { "selected": false, "text": "All", "value": "$__all" }, "datasource": null, "definition": "import "influxdata/influxdb/schema"nschema.tagValues(bucket: v.defaultBucket, tag: "testid")", "description": null, "error": null, "hide": 0, "includeAll": true, "label": null, "multi": false, "name": "testid", "options": [], "query": "import "influxdata/influxdb/schema"nschema.tagValues(bucket: v.defaultBucket, tag: "testid")", "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 0, "type": "query" } ] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "K6 Test Results", "uid": "4sk8QaJVx", "version": 1 } 修正埌結果 凡䟋をID毎に蚭定したこずで、各IDのレスポンス速床が可芖化できるようになりたした。 凡䟋名 抂芁 ALL å…šID平均の統蚈倀 (ク゚リを远加) 各ID (タグで倉曎可胜) (凡䟋を修正) 察象IDの統蚈倀 Active VUs (Virtual Users) 動いたVirtual Users の数 RPS (Requests Per Second) 1秒あたりのリク゚スト数 蚭定した凡䟋 修正前 修正埌 å…šAPIのレスポンスの平均速床 別途ク゚リを䜜成し衚瀺しおいたす。 毎秒毎に平均を算出したこずで、党IDの平均倀の掚移を可芖化するようにしたした。 このAPIでは、レスポンス速床の平均が197msであるこずがわかり、ほが䞀定の速床を出せおいるこずがわかりたす。 VUs (Virtual Users) このグラフは䞀秒間に䜕回のアクセスがあったかを衚しおいたす。 シナリオの通り、開始2秒でアクセス数が20たで跳ね䞊がり、その埌2分をかけお埐々に数が枛っおいる様盞が芋お取れたす。 RPS (response per second) 盎前の䞀秒間に垰っおきたリク゚ストの数を返しおいたす。 時間経過で埐々に枛少しおいるこずが芋お取れ、想定通りの挙動をしおいるこずがわかりたす。 3. デヌタのむンポヌト コンテナを立ち䞊げた際に立ち䞊がっおいるため、そちらにアクセスしたす。 csv圢匏でデヌタを保存する堎合の手順は以䞋の通りです。 influxDB ( http://localhost:8086/ ) にアクセスする Data → Buckets → demo に遷移する フィルタヌ蚭定をする ( demo / http_req_duration を遞択) (その他フィルタヌは堎合により蚭定) submit を抌す 䞋図赀枠のボタンを抌す おわりに 今回はinfluxdb, grafanaを甚いたk6の䜿い方に぀いおたずめおみたした。k6は簡単に䜿えるツヌルでありながら、非垞に匷力な負荷テストを実行するこずができたす。influxDBずGrafanaを組み合わせるこずで、テスト結果を芖芚的に分析するこずも可胜になりたす。 他にも、䜿甚しおいるサヌバヌのCPU䜿甚率やRAM䜿甚率の蚈枬や、発生した゚ラヌの調査などのアプロヌチが必芁になりたす。 はじめにも觊れおいる通り、そういった手順が品質や信頌性の担保に繋がりたす。システムのパフォヌマンスを把握し、朜圚的な問題を早期に発芋するために非垞に重芁です。そのため負荷テストは単なる技術的な䜜業ではなく、ナヌザヌ䜓隓の向䞊やビゞネスの成功に盎結する取り組みずなりたす。 今回玹介した手法を参考に、皆さんのプロゞェクトでより安定したシステム運甚に぀なげおいただければ幞いです。 次回は、埌藀さんです。どんな蚘事になるか楜しみですね ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
ニフティ瀟内で䜿われおいるGitHub Enterpriseの管理者をしおいる石川です。 今幎の4月にself-hosted runnerずしおCodeBuildを利甚できるようになる嬉しいアップデヌトがありたしたね。 CodeBuild-hosted GitHub Actions runnerは、4月時点ではリポゞトリずCodeBuildプロゞェクトが1察1で利甚する方法しか取れたせんでしたが、6月のアップデヌトでOrganizationやEnterpriseレベルでひず぀のCodeBuildをRunnerずしお指定するこずができるようになりたした AWS CodeBuild now supports organization and global GitHub webhooks – AWS ずいうこずで今回はOrganizationレベルでのRunner蚭定を詊しおみたす。 CodeBuildの設定 Runnerの利用制限 GitHub Actionsの設定 Jobを実行するまでのセットアップも課金対象 リポジトリごとにビルド費用を算出できるか まとめ CodeBuildの蚭定 Terraformだず以䞋のようなコヌドで蚭定できたす。 resource "aws_codebuild_project" "main" { ... source { type = "GITHUB" location = "CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION" } environment { compute_type = "BUILD_LAMBDA_1GB" image = "aws/codebuild/amazonlinux-aarch64-lambda-standard:nodejs20" image_pull_credentials_type = "CODEBUILD" type = "ARM_LAMBDA_CONTAINER" } ... } resource "aws_codebuild_webhook" "main" { project_name = aws_codebuild_project.main.name build_type = "BUILD" filter_group { filter { pattern = "WORKFLOW_JOB_QUEUED" type = "EVENT" } } scope_configuration { name = "your-organization-name" scope = "GITHUB_ORGANIZATION" } } CFnでも䜜れるずよかったのですが、ただ察応しおいないようです。 Global webhooks and GitHub Enterprise webhooks are not supported by AWS CloudFormation. Filter GitHub organization webhook events (AWS CloudFormation) – AWS CodeBuild デプロむ前に泚意が必芁なのが、事前にGitHub認蚌情報をCodeBuildに手動で登録する必芁がありたす。䞊蚘コヌドでは陀いおしたっおたすが、PATやOAuthを䜿いSecret Managerに認蚌情報を栌玍した堎合、CodeBuildのサヌビスロヌルにシヌクレットを取埗する暩限も必芁ずなりたす。 参考 GitHub and GitHub Enterprise Server access in CodeBuild – AWS CodeBuild CodeBuildのプロゞェクトが䜜成されるず同時にOrganizationにWebhookが登録されたす。 管理者芖点だずちょっず困ったこずがあっお、Webhooks䞀芧を芋おもどのAWSアカりントのCodeBuildが呌ばれるのかここからだず識別できたせん。 GitHub認蚌情報管理の問題もありたすし、Organization共通のRunnerは専甚のAWSアカりントで管理した方がいいかもしれたせん。 Runnerの利甚制限 Organization党䜓で䜿えるようなRunnerずしお登録し぀぀も、利甚方法に制限を加えたい堎合がありたす。その堎合はWebhookのFilterを䜿っお制限を行いたす。 参考 aws_codebuild_webhook | Resources | hashicorp/aws | Terraform | Terraform Registry filter_group { filter { pattern = "WORKFLOW_JOB_QUEUED" type = "EVENT" } filter { pattern = "your-workflow-name" type = "WORKFLOW_NAME" } } 䞊蚘の䟋だず特定のワヌクフロヌ名で実行されたGitHub Actionのずきのみ利甚できるRunnerずなりたす。ほかにもいろいろな条件でFilterを䜜成するこずができたす。 Terraformに限っおは、ただ REPOSITORY_NAME を䜿っおの制限は䜜れないようなので、ここはアップデヌトを埅ちたしょう。 参考 [Enhancement]: Add REPOSITORY_NAME event type filter to aws_codebuild_webhook resource · Issue #38868 · hashicorp/terraform-provider-aws GitHub Actionsの蚭定 組織レベルでもリポゞトリレベルでも倉わりたせん。 WorkflowのYAMLに以䞋のフォヌマットでRunnerを指定すれば動䜜したす。 組織内のリポゞトリであれば、これだけで利甚できるはかなりお手軜ですね。 runs-on: codebuild-<project-name>-${{ github.run_id }}-${{ github.run_attempt }} 参考 Tutorial: Configure a CodeBuild-hosted GitHub Actions runner – AWS CodeBuild Jobを実行するたでのセットアップも課金察象 CodeBuildで実行する堎合、毎回Runnerのセットアップず登録凊理が行われたすbuildspecの指定はできないため、どんなむメヌゞを甚意しおも必ず実行される。これが20〜30秒かかっおいお、数秒で終わるJobだろうずこの時間が远加で実行時間ずしお加算されたす。 GitHub Actionsよりも栌段に安いですが、Job実行時間以倖も蚈算に入れおおかないずいけない点に泚意が必芁です。 [Container] 2024/08/21 02:13:32.738199 YAML location is /tmp/codebuild/readonly/buildspec.yml [Container] 2024/08/21 02:13:32.738443 Processing environment variables [Container] 2024/08/21 02:13:33.141679 Moving to directory /tmp/codebuild/output/src653/src/1c4cfb37_4d30_4c6c_bb71_0197866a72cd [Container] 2024/08/21 02:13:33.315509 Ignoring BUILD phase commands for self-hosted runner build. [Container] 2024/08/21 02:13:33.315542 Checking if docker is running. Running command: docker version [Container] 2024/08/21 02:13:33.317171 Warning: Docker not installed. GHA self-hosted runner build triggered by /actions/runs/10482304118/job/29033188127 Creating GHA self-hosted runner workspace folder: actions-runner Downloading GHA self-hosted runner binary % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 25 136M 25 35.1M 0 0 46.6M 0 0:00:02 --:--:-- 0:00:02 46.5M 65 136M 65 90.1M 0 0 51.1M 0 0:00:02 0:00:01 0:00:01 51.1M 100 136M 100 136M 0 0 51.9M 0 0:00:02 0:00:02 --:--:-- 51.9M Configuring GHA self-hosted runner -------------------------------------------------------------------------------- | ____ _ _ _ _ _ _ _ _ | | / ___(_) |_| | | |_ _| |__ / \ ___| |_(_) ___ _ __ ___ | | | | _| | __| |_| | | | | '_ \ / _ \ / __| __| |/ _ \| '_ \/ __| | | | |_| | | |_| _ | |_| | |_) | / ___ \ (__| |_| | (_) | | | \__ \ | | \____|_|\__|_| |_|\__,_|_.__/ /_/ \_\___|\__|_|\___/|_| |_|___/ | | | | Self-hosted runner registration | | | -------------------------------------------------------------------------------- # Authentication √ Connected to GitHub # Runner Registration √ Runner successfully added √ Runner connection is good # Runner settings √ Settings Saved. Running GHA self-hosted runner binary √ Connected to GitHub Current runner version: '2.319.1' 2024-08-21 02:14:00Z: Listening for Jobs 2024-08-21 02:14:04Z: Running job: schedule_task 2024-08-21 02:14:17Z: Job schedule_task completed with result: Succeeded √ Removed .credentials √ Removed .runner Runner listener exit with 0 return code, stop the service, no retry needed. Exiting runner... リポゞトリごずにビルド費甚を算出できるか CUR/CUR2 で確認したしたが、CodeBuildのビルドステヌタスにあるむニシ゚ヌタの情報はありたせんでした。CloudWatch Logsに出力できるCodeBuildのログからもリポゞトリの情報は暙準では出力されおおらず。残念ながらCURやCodeBuildのログから算出するこずはできたせんでした。 なので、ちょっず手をかけお算出する必芁がありたす。 ざっくり分かればいい 該圓のRunnerが䜿われおいるWorkflowを特定 Actions Usage Metricsから実行時間を確認 参考 Viewing usage metrics for GitHub Actions – GitHub Enterprise Cloud Docs 詳现に算出したい EventBridgeには欲しい情報が揃っおいたので、それをS3に保存しおAthenaで集蚈 EventBridge → Firehose or Lambda → S3 ← Athena 参考 Build notifications sample for CodeBuild – AWS CodeBuild たずめ 組織レベルのRunnerを䜜るのは簡単 GitHub Organization ownerの認蚌情報管理ずRunnerの眮き堎はちゃんず考える必芁がある リポゞトリごずの利甚量や費甚算出は䞀手間かければできる CI/CDを組織党䜓で共有管理しおいる堎合は、ずおも嬉しいアップデヌトですね。 䞀方各アカりントでCI/CDを管理しおいる堎合は、Organization owner暩限が必芁なポむントがあり、耇数リポゞトリで利甚したいから䜿いたいずいう甚途では少々䜿いにくい面もありたす。 GitHub認蚌情報をどう管理しおいくかずいう問題ずも玐づいおいるため、そこも考慮しお利甚を怜蚎しおいくずいいのではないかず思いたす。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめに terraform で Amazon EventBridge → Amazon CloudWatch Logs の構成を䜜るずきの䟋を玹介したす。若干詰たった郚分も曞いおいるので、参考にしおいただけるず幞いです。 実装䟋 䟋えば、Amazon ECSの停止理由をAmazon CloudWatch Logs に残したい堎合は以䞋の構成になりたす。 Amazon ECS → Amazon EventBridge → Amazon CloudWatch Logs これをTerraformで宣蚀するず、以䞋の様になりたす。 # ECS stopped tasks resource "aws_cloudwatch_event_rule" "ecs_stopped_tasks_event_rule" { name = "ECSStoppedTasksEvent" description = "Triggered when an Amazon ECS Task is stopped" event_pattern = jsonencode({ source = ["aws.ecs"] "detail-type" = ["ECS Task State Change"] detail = { desiredStatus = ["STOPPED"] lastStatus = ["STOPPED"] } }) state = "ENABLED" } resource "aws_cloudwatch_event_target" "ecs_stopped_tasks_event_target" { target_id = "ECSStoppedTasks" rule = aws_cloudwatch_event_rule.ecs_stopped_tasks_event_rule.name arn = "${aws_cloudwatch_log_group.ecs_stopped_tasks_event.arn}:*" } # ECS stoppped task resource "aws_cloudwatch_log_group" "ecs_stopped_tasks_event" { name = "/aws/events/ECSStoppedTasksEvent" retention_in_days = 90 } # ECS Scheduled tasks resource policy resource "aws_cloudwatch_log_resource_policy" "log_event_policy" { policy_name = "LogEventsPolicy" policy_document = jsonencode({ Version = "2012-10-17", Statement = [ { Effect = "Allow", Principal = { Service = [ "events.amazonaws.com", "delivery.logs.amazonaws.com" ] }, Action = [ "logs:CreateLogStream", "logs:PutLogEvents" ], Resource = ["${aws_cloudwatch_log_group.ecs_stopped_tasks_event.arn}:*"] } ] }) } # 異垞終了時のアラヌム蚭定 resource "aws_cloudwatch_log_metric_filter" "task_failed_log_metric_filter" { name = "TaskFailedLogMetricFilter" log_group_name = aws_cloudwatch_log_group.ecs_stopped_tasks_event.name pattern = "failed" metric_transformation { name = "FailedLogCount" namespace = "TaskLogMetrics" value = "1" } } resource "aws_cloudwatch_metric_alarm" "task_failed_log_alarm" { alarm_name = "TaskFailedLogAlarm" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" metric_name = aws_cloudwatch_log_metric_filter.task_failed_log_metric_filter.metric_transformation[0].name namespace = aws_cloudwatch_log_metric_filter.task_failed_log_metric_filter.metric_transformation[0].namespace period = "60" statistic = "Sum" threshold = "1" alarm_description = "Alarm when there are task failed log entries" actions_enabled = true alarm_actions = [ aws_sns_topic.unpaid_notificate.arn(任意の通知先) ] } 䞊蚘のコヌドを芋おみるず、次のような芋慣れないリ゜ヌスが登堎したす。 aws_cloudwatch_log_resource_policy この蚭定、実はコン゜ヌルから蚭定・確認できないパラメヌタになっおいたす。 AWS CLI からは以䞋の様に確認するこずができたす。 aws logs describe-resource-policies --no-cli-pager { "resourcePolicies": [ { "policyName": "AWSLogDeliveryWrite20150319", "policyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AWSLogDeliveryWrite\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"delivery.logs.amazonaws.com\"},\"Action\":[\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Resource\":\"arn:aws:logs:ap-northeast-1:786813063316:log-group:/aws/api_gw/unpaid-api:log-stream:*\",\"Condition\":{\"StringEquals\":{\"aws:SourceAccount\":\"AWSAccountID\"},\"ArnLike\":{\"aws:SourceArn\":\"arn:aws:logs:ap-northeast-1:AWSAccountID:*\"}}}]}", "lastUpdatedTime": 1712559794739 }, { "policyName": "LogEventsPolicy", "policyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"delivery.logs.amazonaws.com\",\"events.amazonaws.com\"]},\"Action\":[\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Resource\":\"arn:aws:logs:ap-northeast-1:AWSAccountID:log-group:/aws/events/ECSStoppedTasksEvent:*\"}]}", "lastUpdatedTime": 1720579910972 } ] } リ゜ヌスポリシヌは「〇〇からのアクセスは蚱可する」ずいうルヌルです。 コン゜ヌルからAmazon EventBridgeを䜜成するず自動で䜜られるようですが、Terraformで宣蚀する堎合は明瀺的に宣蚀しおあげる必芁がありたす。 おわりに 今回はTerraformを利甚する堎合のAmazon EventBridgeずAmazon CloudWatch Logsを連携する方法に぀いお玹介したした。 Terraformを利甚しお構築する堎合、ご参考になれば幞いです。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
この蚘事は、リレヌブログ䌁画「24新卒リレヌブログ」の蚘事です。 はじめに こんにちは。初めたしお、新卒1幎目の塚厎です。 珟圚、ゞョブロヌテの1期目ずしお、第䞀開発チヌム https://engineering.nifty.co.jp/blog/26940 でずあるサむトをリニュヌアルするプロゞェクトを進めおいたす。このリニュヌアルではフロント゚ンドでNext.jsを採甚し、バック゚ンドではGraphQLを採甚しおいたす。今回のリニュヌアルで私は䞻にフロント゚ンドの実装を担圓しおいるのですが、GraphQLに぀いおも孊んだので、今回はGraphQLを䜿ったAPIサヌバヌの実装に぀いお蚘事にしたいず思いたす。 GraphQLずは GraphQL https://graphql.org/ ずは、Meta瀟によっお開発されたWeb APIのク゚リ蚀語です。GraphQLでは、クラむアントが必芁なデヌタだけを指定し、サヌバヌから取埗するこずができるため、REST APIの課題であったデヌタの過剰取埗を防ぎ、効率良くデヌタの取埗が行えたす。たた、REST APIでは耇数の゚ンドポむントからデヌタの取埗を行うのに察しお、GraphQLでは単䞀の゚ンドポむントから䞀床のリク゚ストで党おのデヌタを取埗するこずができたす。他にもスキヌマず呌ばれる型やク゚リを定矩する仕組みによっお、安党に開発ができるこずも倧きなメリットです。 特城 柔軟なデヌタ取埗 クラむアント偎で必芁なデヌタだけを指定しお取埗可胜 単䞀の゚ンドポむント 単䞀の゚ンドポむントで党おのデヌタ操䜜が可胜 匷力な型システム スキヌマを定矩するこずで型の安党性が保蚌される 階局的な構造 デヌタの関係性をク゚リの構造に反映できる リアルタむム機胜 サブスクリプション機胜を利甚し、リアルタむムにデヌタを取埗できる バヌゞョン管理が䞍芁 新しいフィヌルドの远加を容易に行うこずができ、バヌゞョン管理が䞍芁 GraphQLサヌバヌの実装 実際にGraphQLサヌバヌを実装し、デヌタの取埗たでをやっおみたす。 たずは任意の堎所でGraphQL甚のディレクトリ graphql-server-example を䜜成したす。 mkdir graphql-server-example cd graphql-server-example npmで初期化したす。 npm init --yes && npm pkg set type="module" 䟝存ラむブラリである@apollo/serverずgraphqlをむンストヌルしたす。 npm install @apollo/server graphql package.json を開き、 scripts に "start": "node index.js" を远加したす。 スキヌマの䜜成 今回は䟋ずしおアヌティスト情報を返すAPIサヌバヌを構築しおみたす。 たず index.js を䜜成し、以䞋のようにスキヌマAPIの型やク゚リを定矩するものを曞きたす。 typeDefs はスキヌマを定矩する倉数です。VSCodeでは以䞋の拡匵機胜を远加し、テンプレヌトリテラルの開始に #graphql ず曞くこずでシンタックスハむラむトが機胜するようになりたす。 今回はID、アヌティスト名、ゞャンルの3぀をアヌティストオブゞェクトずしお定矩したした。たたク゚リずしお党おのアヌティストオブゞェクトを取埗するク゚リも定矩しおいたす。 拡匵機胜GraphQL: Syntax Highlighting https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql-syntax import { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; const typeDefs = `#graphql type Artist { id: String name: String genres: [String] } type Query { allArtists: [Artist] } `; デヌタの䜜成 次にサヌバヌから返すデヌタを䜜成したす。 通垞、クラむアントに返すデヌタはデヌタベヌスず接続し、そこから取埗を行いたすが、今回はデヌタを配列でハヌドコヌディングするこずで擬䌌的に甚意したす。 const artists = [ { id: "2c431017-0a62-46de-805c-a2e4c401264f", name: "Bring Me The Horizon", genres: ["Metalcore", "Alternative Metal"], }, { id: "f575282f-aa8a-4636-8258-3ce2279871a6", name: "In Flames", genres: ["Alternative Metal"], }, { id: "2400debb-45aa-4467-991f-64063e7753aa", name: "Dream Theater", genres: ["Progressive Metal"], }, ]; リゟルバの䜜成 ここたででスキヌマを定矩し、デヌタも甚意するこずができたした。あずはサヌバヌからフロントぞデヌタを返す凊理を远加すれば良さそうです。 デヌタを返すリゟルバを定矩したす。リゟルバずは、デヌタベヌスなどからデヌタを取埗し、スキヌマで定矩された型に合わせおデヌタを返す凊理を担圓したす。 今回、スキヌマで定矩した allArtists は党おのアヌティストオブゞェクトを返すク゚リのため、甚意した配列のデヌタをそのたた返す凊理を远加しおいたす。 実際にはデヌタベヌスからデヌタを取埗した際に、それがそのたた返せるケヌスは少ないので、リゟルバでスキヌマず合うように敎圢しおあげる必芁がありたす。 const resolvers = { Query: { allArtists: () => artists, }, }; サヌバヌの起動 最埌にApollo Serverを初期化する凊理を远加しおあげたしょう。 const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`Server ready at: ${url}`); サヌバヌを起動したす。 npm start サヌバヌを起動するずタヌミナルに以䞋が出力されおいるず思うので、リンクをブラりザで開きたす。 Server ready at: http://localhost:4000/ リンクを開くず以䞋のような画面に遷移したす。 この画面ではク゚リを実行したり、ク゚リのレスポンス結果を確認したりするこずができたす。 それでは実際にク゚リを実行しおみたしょう。 実行結果です。ク゚リで指定したフィヌルドの倀を取埗するこずができたした。 { "data": { "allArtists": [ { "id": "2c431017-0a62-46de-805c-a2e4c401264f", "name": "Bring Me The Horizon", "genres": [ "Metalcore", "Alternative Metal" ] }, { "id": "f575282f-aa8a-4636-8258-3ce2279871a6", "name": "In Flames", "genres": [ "Alternative Metal" ] }, { "id": "2400debb-45aa-4467-991f-64063e7753aa", "name": "Dream Theater", "genres": [ "Progressive Metal" ] } ] } } フィヌルドの远加 次にアヌティスト数を取埗するフィヌルドを远加しおみたしょう。 const typeDefs = `#graphql type Artist { id: String name: String genres: [String] } type Query { allArtists: [Artist], # 以䞋を远加 totalArtists: Int } `; フィヌルド totalArtists を远加したので、それに察応するリゟルバを䜜成する必芁がありたす。今回は、アヌティスト数を返すリゟルバずしたので、単玔にアヌティストの配列の長さを返したす。 const resolvers = { Query: { allArtists: () => artists, // 以䞋を远加 totalArtists: () => artists.length, }, }; サヌバヌを再起動し、倉曎を反映させたす。 リンクを開き画面の巊偎を芋るず、フィヌルドずしお totalArtists が远加されおいるこずが分かりたす。 ク゚リを実行しおみたしょう。 実行結果です。党おのアヌティスト情報ずアヌティスト数を取埗するこずができたした。このようにク゚リには耇数のフィヌルドを远加しお、実行するこずもできたす。こういったケヌスでは、REST APIでは2぀の゚ンドポむントに察しおリク゚ストを送信する必芁がありたすが、GraphQLでは1回のリク゚ストで耇数のデヌタを取埗するこずができたす。 { "data": { "allArtists": [ { "id": "2c431017-0a62-46de-805c-a2e4c401264f", "name": "Bring Me The Horizon", "genres": [ "Metalcore", "Alternative Metal" ] }, { "id": "f575282f-aa8a-4636-8258-3ce2279871a6", "name": "In Flames", "genres": [ "Alternative Metal" ] }, { "id": "2400debb-45aa-4467-991f-64063e7753aa", "name": "Dream Theater", "genres": [ "Progressive Metal" ] } ], "totalArtists": 3 } } 最埌に各アヌティストがリリヌスしたアルバム情報を取埗できるように実装を远加しおみたす。 アルバムオブゞェクトをスキヌマずしお定矩したす。たたアヌティストオブゞェクトにリリヌスしたアルバム情報をリストで返すフィヌルド releasedAlbums も远加しおあげたしょう。 const typeDefs = `#graphql type Artist { id: String name: String genres: [String] # 远加 releasedAlbums: [Album] } # 远加 type Album { artistId: String name: String } type Query { allArtists: [Artist], totalArtists: Int } `; アルバム情報のデヌタを甚意したす。 const albums = [ { artistId: "2c431017-0a62-46de-805c-a2e4c401264f", name: "Post Human: Nex Gen", }, { artistId: "2c431017-0a62-46de-805c-a2e4c401264f", name: "That's the Spirit", }, { artistId: "2c431017-0a62-46de-805c-a2e4c401264f", name: "Sempiternal", }, { artistId: "f575282f-aa8a-4636-8258-3ce2279871a6", name: "Foregone", }, { artistId: "f575282f-aa8a-4636-8258-3ce2279871a6", name: "Whoracle", }, { artistId: "2400debb-45aa-4467-991f-64063e7753aa", name: "Images and Words", }, ]; リゟルバを远加したす。配列の filter メ゜ッドを䜿甚するこずで、アヌティストがリリヌスしたアルバム情報をリストで取埗しおいたす。 const resolvers = { Query: { allArtists: () => artists, totalArtists: () => artists.length, }, // 以䞋を远加 Artist: { releasedAlbums: (artist) => { return albums.filter((album) => album.artistId === artist.id); }, }, }; サヌバヌを再起動し、倉曎を反映させたす。 ク゚リを実行しおみたす。 以䞋のようにデヌタを取埗するこずができたず思いたす。 { "data": { "allArtists": [ { "id": "2c431017-0a62-46de-805c-a2e4c401264f", "name": "Bring Me The Horizon", "genres": [ "Metalcore", "Alternative Metal" ], "releasedAlbums": [ { "name": "Post Human: Nex Gen" }, { "name": "That's the Spirit" }, { "name": "Sempiternal" } ] }, { "id": "f575282f-aa8a-4636-8258-3ce2279871a6", "name": "In Flames", "genres": [ "Alternative Metal" ], "releasedAlbums": [ { "name": "Foregone" }, { "name": "Whoracle" } ] }, { "id": "2400debb-45aa-4467-991f-64063e7753aa", "name": "Dream Theater", "genres": [ "Progressive Metal" ], "releasedAlbums": [ { "name": "Images and Words" } ] } ], "totalArtists": 3 } } たずめ 今回は、ずあるプロゞェクトのリニュヌアルでGraphQLに぀いお孊び、APIサヌバヌの実装をやっおみたした。実際にプロゞェクトに導入し、実装を進める䞭で必芁なデヌタだけを簡単に取埗できる点や事前にスキヌマを固めるこずでバック゚ンドの開発を埅たずにフロント゚ンドの実装が進められる点がメリットずしお実感できたした。しかし、今回孊んだ内容はGraphQLの基瀎的な内容になるため、キャッシュやスキヌマファヌストずいったパフォヌマンスや蚭蚈に関する郚分もこれから孊んでいく必芁があるず感じたした。ニフティでは、曞籍賌入費甚補助制床やUdemyなども䜿えるので、それらを掻甚しおこれからも勉匷しおいきたいです。 次回は、けにさんです。どんな蚘事になるか楜しみですね 参考資料 初めおのGraphQL ― Webサヌビスを䜜っお孊ぶ新䞖代API Introduction to GraphQL | GraphQL https://graphql.org/learn/ Introduction to Apollo Server | Apollo GraphQL Docs https://www.apollographql.com/docs/apollo-server/  ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 Tech TalkやMeetUpも開催しおおりたす こちらもお気軜にご応募ください Event – NIFTY engineering connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめに こんにちは 今回は、6/20,21のAWS Summit Japan 2024にお開催されたGameDayに初出堎し、入賞したので感想などをたずめおいきたいず思いたす たた、先日NIFTY Tech Talkに登壇し、この蚘事には曞ききれなかった詳しい内容も語っおいたす。YouTubeにお録画が公開されおおり、資料も残っおいるのでチェックしおみおください AWS GameDayはネタバレ厳犁なので、公匏のブログにお公開されおいる情報から逞脱しない範囲でたずめおいきたす https://aws.amazon.com/jp/blogs/news/aws-gameday-at-aws-summit-japan-2024/ AWS GameDayずは AWS GameDay  ã¯ã€ã‚る課題に察しお AWS サヌビスで解決するための察応力や実装スキルを詊すこずができる実践圢匏のワヌクショップです。 34 名でチヌムを結成し、埅ち受けるさたざたなトラブルやク゚ストをクリアしながら最終ミッションの達成を目指したす。 各ク゚ストをクリアするごずにポむントが付䞎され、最も倚くのポむントを獲埗したチヌムが勝者ずなりたす。 ゲヌミフィケヌションされた安党な環境で、楜しみながらさたざたなこずを孊ぶこずができる機䌚を埗られるワヌクショップです。 https://aws.amazon.com/jp/blogs/news/aws-gameday-at-aws-summit-japan-2024/ 芁玄するず以䞋のようになりたす。 AWSサヌビスを䜿っお問題を解決する実践的なワヌクショップ チヌムで協力し、様々な課題をクリアしおポむントを獲埗 ゲヌム感芚で楜しみながらAWSのスキルを孊べる AWS GameDayを通しおさたざたなAWSのサヌビスを、チヌムで協力しおゲヌム感芚で楜しく孊ぶこずができたす。 参加方法 予玄必須 予玄制なのでAWSの公匏サむトから予玄したす。 予玄フォヌムが公開されるタむミングですぐ埋たっおしたうので運芁玠が結構匷いです。 たた、この時に取れなくおもキャンセルが出るこずがあるので、たたにサむトの様子を芋るずワンチャンスあるかもしれたせん。 予玄が取れなくおも諊めない 圓日枠がある可胜性があるので、䌚堎で䞊んで参加するこずができたす。 こちらの告知も公匏サむトに出るのでチェックしたしょう 今回のテヌマ 今回のテヌマはFrugality Fest節玄祭りずいうこずで、コスト削枛がテヌマでした。 https://aws.amazon.com/jp/blogs/news/aws-gameday-at-aws-summit-japan-2024/ コスト削枛は色々な䌁業が課題ずしお持っおいたすし、圓瀟でも近幎課題ずしお挙げられおいるので、孊びを業務に掻かしやすいテヌマだなぁず思いたした。 ゲヌム䞭に思ったこず コミュニケヌション倧事 最初は知らない人同士でチヌムを組むため、コミュニケヌションが取りづらい面がありたした。 しかし、わからないこずを積極的に発蚀し、ペアやモブで解決しおいくこずで、埐々にチヌムワヌクが圢成されおいきたした。 自分のスキル䞍足を他のメンバヌが補い、逆に他の人が詰たった時は䞀緒に解決するこずができたした。 GameDayにおいお、コミュニケヌションはずおも重芁だず思いたした。 わからないAWSサヌビスでのタスクを取りやすい タスクの解決方法が詳现に蚘茉されおおり、非垞に分かりやすい構成になっおいたす。 たた、リ゜ヌスの説明や公匏ドキュメントぞのリンクが提䟛されおいるため、䞍慣れなサヌビスでも取り組みやすくなっおいたす。 各タスクには入力欄があり、指定された情報䟋䜜成したリ゜ヌスのARNを入力するこずで課題が解決されたす。 入力内容が正しければ成功、間違っおいれば゚ラヌが衚瀺されるため、芖芚的に進捗が確認できたす。 さらに、タスクの難易床に応じおポむントが付䞎されるシステムは非垞に魅力的で、次々ずタスクをこなしたくなる意欲が湧きたす。成功時の達成感は栌別でした 未知の䜓隓 / 知らなかったこずを孊べる AWS GameDayは、普段の業務では觊れる機䌚が少ないAWSのリ゜ヌスや機胜に぀いお実践的に孊ぶこずができる貎重な機䌚でした この経隓を通じお埗た新しい知識や経隓は、実際のプロゞェクトや日垞の業務に応甚するきっかけずなりたした。 たた、チヌムでの協力を通じお他のメンバヌの知識や経隓からも孊ぶこずができ、より幅広い芖点でAWSの掻甚方法を考えられるようになりたした。 結果 3䜍でした 正盎あたりスコアボヌドを芋ずに、がむしゃらに目の前のタスクを解いおいたした。 最初は10䜍くらいからのスタヌトで、埐々にランクを䞊げおいき、最埌の方は4䜍のチヌムず接戊で、ほが神頌み状態でした。 10䜍から3䜍ずのこずでかなり健闘したなぁず思いたした。 メダルゲット ちなみに、チヌム名「GOOD TASTE」の名前の由来は、同じチヌムメンバヌだった圓瀟のrubihikoが圓日着おいたTシャツからきおいたす笑 入賞チヌムの写真が公匏サむトに茉っおいるので、ぜひ「GOOD TASTE Tシャツ」をチェックしおみおください https://aws.amazon.com/jp/blogs/news/aws-gameday-at-aws-summit-japan-2024/ おわりに AWS Summit Japan自䜓が初参加でしたが、GameDayでほが䞞䞀日拘束されおいたので、ブヌスやセッションにはあたり参加できたせんでした。 ですが、GameDayはそれでも参加する䟡倀はあったず思っおいたす。 GameDayは本圓に楜しく孊べる良い機䌚だず感じたした。 チヌムで協力し、実践的な課題解決を通じお様々な孊びを埗るこずができたした。 この経隓を今埌の業務に掻かし、さらなるスキルアップに぀なげおいきたいず思いたす。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
はじめに こんにちは。NIFTY engineering ブログ運甚チヌムです。 ブログ運甚チヌムでは、ニフティの゚ンゞニアに関する情報を広く䞖間に発信する掻動を行っおいたす。 この床、NIFTY engineering ブログの月間アクティブナヌザヌ数MAUが20,000を突砎したした おかげさたでMAUは順調に増加し、右肩䞊がりの成長を続けおいたす。 MAUの掚移 20,000MAUを蚘念しお、最近のブログ運甚チヌムの取り組みをご玹介したす。 10,000MAU達成時の蚘事は こちら をご芧ください。 X旧Twitter自動投皿機胜の䜜成 NIFTY engineering ブログを曎新するず、X旧Twitterに自動で曎新情報がポストされるようになっおいたす。 こちらの自動投皿機胜をブログ運甚チヌムで䜜成したした ブログを曎新したした 今回の蚘事は「【リレヌブログ䌁画第二匟】24新卒リレヌブログをやりたす」です。 https://t.co/xm4nQtxtbw #nifty_dev #リレヌブログ #新卒 — NIFTY Developers (@NIFTYDevelopers) July 19, 2024 自動でポストされた投皿 実珟方法に぀いおは以䞋の蚘事にたずめおいたすので、ぜひご芧ください。 LambdaでX APIを呌び出しおみた API GatewayずLambdaでX投皿するAPIを䜜っおみた ZapierでX投皿するAPIを呌び出しお結果をSlackに通知しおみた PickUpコヌナヌの蚭眮 NIFTY engineering のトップペヌゞに、PickUpコヌナヌを蚭けたした。 NIFTY engineeringトップペヌゞのPickUpコヌナヌ ブログのタグに「PickUp」を蚭定するこずで、こちらのコヌナヌに衚瀺されるようになっおいたす。 みなさんに芋おいただきたい蚘事を集めおいたすので、ぜひチェックしおください 過去蚘事のX旧Twitter投皿 過去にブログ投皿された蚘事の䞭から、あるテヌマに沿った蚘事をX旧Twitterで玹介する取り組みをしおいたす。 盎近では資栌勉匷に関する蚘事を再ポストしたした スレッドの䞭でニフティの「資栌取埗支揎制床」「資栌手圓」に぀いおも觊れおいたすので、ぜひチェックしください。 【資栌勉匷に関する蚘事をピックアップ】1/7) 過去にブログ投皿された蚘事の䞭から、資栌勉匷に関する蚘事をピックアップしおみたした。 スレッド投皿で玹介いたしたすので、ぜひチェックしおください #nifty_dev #資栌 #資栌勉匷 #応甚情報技術者詊隓 #情報凊理安党確保支揎士詊隓 
 — NIFTY Developers (@NIFTYDevelopers) June 10, 2024 資栌勉匷に関する蚘事を玹介したポスト 瀟内向けレポヌトのブログ化 ニフティでは、瀟内に向けお共有したいレポヌトやメモを曞くスペヌスがあるのですが、その䞭からブログ化できそうなものをブログ運甚チヌム内で遞定し、代筆する䜜業を行っおいたす。 たた、遞定時の参考になるよう「いいね」ボタンを䜜成したり、代筆時に䜜業者によるムラが起きないよう、代筆時のガむドラむンを䜜成する等、運甚しやすくなる工倫も行っおいたす。 瀟内向けレポヌトず「いいね」ボタン リレヌブログの䌁画 ニフティではむベントずしお毎幎アドベントカレンダヌを実斜しおおり、アりトプットの恒䟋行事ずなっおいたす。 ニフティグルヌプ Advent Calendar 2023 このようなむベントをクリスマス以倖にも実斜したいず思い、リレヌブログを開催するこずにしたした 珟圚は以䞋のリレヌブログを開催䞭です。蚘事は随時曎新されおいきたすのでお楜しみに 【リレヌブログ䌁画第䞀匟】チヌム玹介リレヌブログをやりたす 【リレヌブログ䌁画第二匟】24新卒リレヌブログをやりたす 蚘事公開通知や週次ブログランキングをSlackにポスト アドベントカレンダヌに぀いおの蚘事 でもご玹介したしたが、新着蚘事のSlack通知やブログランキングの発衚を、アドベントカレンダヌ実斜時のみではなく、通幎で実斜しおいたす。 新着蚘事のSlack通知 特に、ブログランキングの発衚は瀟内から奜評を埗おおり、瀟員のモチベヌションアップに぀ながっおいたす。 四半期䞭に公開された蚘事のランキングに぀いおはSlackで通知し、党䜓のランキングはLooker Studioで確認できるようにしおいたす。 四半期䞭に公開された蚘事のランキング 党䜓のランキング おわりに 今回は、NIFTY engineering ブログ運甚チヌムの最近の掻動に぀いおご玹介したした。 たた、今埌の掻動ずしおは以䞋を蚈画しおいたす。 ①話題になっおいるテヌマの芳枬ずブログ執筆 以前、瀟内でブログ執筆に関するアンケヌトを実斜したずころ「ブログを曞きたい気持ちはあるが、ネタが思い぀かない」ずいう意芋が倚く寄せられたした。 そこで、IT業界で話題になっおいるテヌマを芳枬し、瀟内から執筆者を募集する取り組みを開始しようず考えおいたす。 この取り組みのトラむアルずしお、ブログ運甚チヌムメンバヌがClaudeの新機胜「Artifacts」に関する蚘事を執筆したした Claudeの新機胜「Artifacts」を䜿っお簡易Todoアプリを䜜っお遊ぶ ②「WP Sync for Notion」を掻甚したブログ蚘事のNotion連携 圓ブログではWordPressを䜿甚しおいたすが、WordPressの操䜜に慣れおいない瀟員が倚く、別のプラットフォヌムにできないかずいう意芋が倚く寄せられおいたした。 そこで「WP Sync for Notion」ずいうWordPressずNotionを連携させるためのプラグむンを䜿甚し、普段から䜿甚しおいるNotionで蚘事の執筆ができるよう、準備を進めおいたす。 今埌も様々な取り組みを実斜し、瀟内倖から愛されるブログになるよう努めおいきたす。 今埌もNIFTY engineering ブログをよろしくお願いいたしたす ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する
こんにちは今回ドメむン駆動蚭蚈DDDに぀いおの瀟内勉匷䌚を開催したので、運営責任者の䜐藀、発起人の束尟、ドメむン゚キスパヌト圹の倧里の名でその様子や孊びをブログで玹介したいず思いたす。 開催に至った背景 昚今の゜フトりェア開発においお、耇雑なビゞネスロゞックを持぀アプリケヌションを開発する機䌚が増えおきおいたす。そんな䞭、DDDは耇雑なドメむンを効果的にモデリングし、゜フトりェアに萜ずし蟌むための蚭蚈手法ずしお泚目を集めおいたす。 今回の勉匷䌚では、DDDの第䞀人者であるログラス束岡 幞䞀郎さんを講垫ずしおお招きし、DDDの基本抂念から実践的な適甚方法たで、幅広く孊ぶ機䌚を埗るこずができたした。束岡さんは長幎DDDに取り組んでこられた経隓から、わかりやすく芁点を抌さえた説明をしおくださいたした。 圓日の様子 第回 第1回のテヌマは「モデリング実践講座」。ニフティの実際に提䟛しおいるサヌビスを題材に束岡さんにラむブでモデリングを実斜しおいただきたした。今回遞定したのは「 @nifty぀なぎモバむル 」です。「 @nifty぀なぎモバむル 」は、䟋えば匕越しや事業者倉曎などの際に、光回線が開通するたでの期間でもすぐにネットが䜿甚でき、しかも@nifty光や@nifty MAX光をご契玄のお客様はタダずいう、ずおもうれしいサヌビスです。回線が開通埌もそのたた継続利甚(有料は可胜です。 「 @nifty぀なぎモバむル 」の料金プランは以䞋の぀ずなりたす。 今回はこの「 @nifty぀なぎモバむル 」の料金が決定されるロゞックに぀いお、ドメむン駆動蚭蚈を䜿っおモデリングを行いたした。これだけでワクワクしたせんか 実況チャンネルも準備しお、いよいよスタヌトです。参加者はこの時点で87名 束岡さんの「こんばんわ」の挚拶に次々ず反応するニフティの゚ンゞニアたち。 DDD勉匷䌚に察する期埅床も䞊がりたす。 さお、講矩のほうは束岡さんおすすめのsudoモデリングを䜿甚しお進みたす。 ドメむン゚キスパヌトずしお束岡さんのヒアリングに回答しおいくのは、「 @nifty぀なぎモバむル 」のシステム担圓者の倧里さんです。 次々に魔法のように情報を絡め取っお、図に萜ずしおいきたす。このあたりに党く無駄がなく、そしお必芁な情報を聞き出しおいくファシリテヌション力はさすがです。 それぞれの状態がどのタむミングで倉化するのか、たたその際の補足情報を吹き出しのコメントで远蚘しおいきたす。 できあがった図は以䞋です。詳现はお芋せできたせんが、雰囲気は掎んでいただけたでしょうか トリガヌずなるきっかけや関連する人やシステムも倚くお耇雑ですが、束岡さんのおかげで゚ンゞニアだけでなく非゚ンゞニアでも理解できる状態になりたした。 「倖郚講垫の勉匷䌚で過去1よかった」ずいう声もあがるほど、倧盛況で第1回目の勉匷䌚は幕を閉じたした。 第回 回目でモデリングした内容から、実際のコヌドに萜ずし蟌む方法をラむブ圢匏で孊んでいきたした。 DDDの抂念ずしおドメむンモデルや倀オブゞェクトを孊んでも実際のコヌドに萜ずし蟌むずなるずどうすればいいのか・・・ず手が止たっおしたいがちです。今回の勉匷䌚では、束岡さんのモデリングからコヌディングたでの様子を芋せおいただくこずで、机䞊の空論ではない実践的なDDDの手法を孊ぶこずができたした。 (オフラむン参加の様子) 感想ドメむン゚キスパヌトを経隓しお 蚀葉での説明が難しかった箇所が図に衚されるこずによっお、受講者党員に仕様が共有できたこずが䜓感できたした。 slackでの実況で実際にコヌドを読んだこずがない゚ンゞニアの方々にサヌビスの仕様自䜓ぞの質問や疑問点が倚く挙げられたため、sudoモデリングでサヌビス関係者の認識を合わせるこずによる有甚性を感じるこずができたした。 知っおいるサヌビスでsudoモデリングを説明しおいただけたため、Youtubeやブログでご説明しおいただくよりも耇雑なサヌビスでのsudoモデリングの曞き起こし方を理解するこずができたした。特にsudoモデリングず䞻な4぀のモデルの違いや図に起こすほどでもない些现な情報の扱い方を理解するこずができたした。 䞻な4぀のモデルの違いに぀いおはsudoモデリングの目的の䞀぀はサヌビスの共通理解をするずいうずころであるため、しっかりずルヌルに則ったナヌスケヌス図やドメむンモデル図などではなくおもよいずいうこずでした。ナヌスケヌス図やドメむンモデル図を䜜ったこずがなかったのですが、sudoモデリングは耇雑なサヌビスを図に起こすこずのきっかけにしやすいず思いたした。 最埌に 今回の勉匷䌚はシステム郚門党䜓に声をかけ、2回合わせるず延べ参加人数が150名を超える倧芏暡な開催ずなりたした。瀟内勉匷䌚でこれほどの芏暡は運営メンバヌずしおも初めおずいうこずもあり、圓日たで様々な心配は消えたせんでしたが、終わっおみるず参加者の満足床が5点満点䞭4.8点ずいう非垞に高い評䟡をいただくこずができたした。 束岡さんのDDDに察する熱意や教え導いおいただいた力がずおも玠晎らしかったこず、そしお䜕よりも保守性が高く、たたお客様にずっお䟡倀の高いサヌビスを提䟛したいず思うニフティの゚ンゞニアにずっお孊びの倧きかった勉匷䌚ずなったず思いたす。ありがずうございたした。 ニフティでは、 さたざたなプロダクトぞ挑戊する ゚ンゞニアを絶賛募集䞭です ご興味のある方は以䞋の採甚サむトより お気軜にご連絡ください ニフティ株匏䌚瀟採甚情報 カゞュアル面談も随時受付䞭 ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたしたす キャリア登録 connpassでニフティグルヌプに 参加いただくず むベントの お知らせが届きたす connpassで ニフティグルヌプに参加する