TECH PLAY

ニフティ株式会社

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

487

基幹システムグループ所属、新卒1年目のいなつぐです。 現在はジョブローテでPythonやJavaを使った業務に携わっています。 本稿ではMacのメニューバーアプリが簡単に作成できるpythonライブラリであるrumpsについて紹介したいと思います。 rumpsの基本的な使い方から始め、勤怠関係の定常業務を一つにまとめてみたというrumpsの活用方法までを紹介します。 rumpsとは? rumpsとは、Macのメニューバーアプリを簡単に作成できるpythonライブラリです。 Macでメニューバーアプリを作成するにはXcodeでSwiftやObjective-Cなどの言語を使用する必要があり、簡単なアプリでもこれらの言語の初学者にとっては学習コストがめっっちゃ高い(個人的な感想)ため、取っ付きにくく感じました。 ですが、 そんなハードルを全て薙ぎ倒してくれるのがこのrumpsです。 QtやtkinterでGUIアプリを作成したことがあればイメージが付きやすいですが、GUIのビューを構成する数行のコードとそれぞれのボタンを押した時に発火するコールバック関数を書くだけでメニューバーアプリとして動作します。 rumpsを動かしてみる 本項ではサンプルを用いて rumps を使ったメニューバーアプリ作成の基本を紹介します。 自身のメニューバーアプリ作成の参考になれば幸いです。 サンプルでは実例とともにrumpsの機能を軽く紹介します。 利用準備 Mac pyenv Python 3.11.4 # pyenvのPythonはデフォルトではフレームワークとして使えないためenable-frameworkオプションを指定 env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.11.4 # グローバルで使用するバージョンを3.11.4に切り替える pyenv global 3.11.4 rumpsライブラリ   pip にてインストールする場合は以下のコマンドをコマンドプロンプト等で実行します。 pip install rumps ファイル構成 iconは こちら のサイトからダウンロードできます。 ※Tools icons created by Freepik – Flaticon . ├── rumps_sample.py ├── icon    ├── icon_on.png    └── icon_off.png 動作デモ(rumps_sample.py) 当デモスクリプトは、以下の動作を行います。 メニューバーアプリのUI表示 通知機能 補足 通知やポップアップウィンドウの表示にはmac側の許可が必要になります。 動作イメージ from rumps import * # ログを表示できるようにする rumps.debug_mode(True) class SampleApp(App): ### メニューバーアプリのUI作成 def __init__(self): super(SampleApp, self).__init__("sample") self.menu=[ MenuItem("hoge",callback=self.hoge), [MenuItem("menu"),[MenuItem(f"submenu{i}", callback=self.hoge) for i in range(10)]], MenuItem("fuga",callback=self.fuga), rumps.separator, MenuItem("none",callback=None), rumps.separator, ] self.icon="icon/icon_on.png" ### コールバック関数 def hoge(self, sender): print("hoge") def fuga(self,sender): data = {"key1":"value1", "key2":"value2"} notification(title="title", subtitle="subtitle", message="message", data=data) ### 通知をクリックした時の処理 @notifications def notification_center(data): print(data["key2"]) if __name__ == "__main__": app = SampleApp() sample_timer = rumps.Timer(app.hoge, 60) sample_timer.start() app.run() コードの説明 App.menu 初期化部分ではApp.menuにMenuItemを配列として入れることで、よく見るメニューの形にしてくれます。 それぞれのMenuItemにはコールバック関数を指定することができ、指定したものは押下可能状態になります。ボタンを押下することで指定した関数が実行できます。 また、menu配列に配列を入れることでサブメニューを作成することもできます。 App.menu App.icon App.iconには画像を指定することでメニューバーアプリのアイコンを設定、変更できます。 プログラムの実行中でもiconを変更することができるため、何かのステータスをアイコンで表示するということもできます。 def __init__(self): super(SampleApp, self).__init__("sample") self.menu=[ MenuItem("hoge",callback=self.hoge), [MenuItem("menu"),[MenuItem(f"submenu{i}", callback=self.hoge) for i in range(10)]], MenuItem("fuga",callback=self.fuga), rumps.separator, MenuItem("none",callback=None), rumps.separator, ] self.icon="icon/icon_on.png" noticication notificationが呼ばれると通知が発火し、dataに入れたdictはrumps.notificationsに渡されます。 title, subtitle, messageそれぞれに指定したテキストが表示されます。 def fuga(self,sender): data = {"key1":"value1", "key2":"value2"} notification(title="title", subtitle="subtitle", message="message", data=data) notification notifications 下記は通知をクリックした時の処理を設定できます。notificationから渡ってきた辞書型のdataを利用できます。 @notifications def notification_center(data): print(data["key2"]) timer rumps.Timerを用いれば一定間隔で特定の処理をバックグラウンドで実行することができます。 以下の例ではhoge関数を1分置きに実行しています。 タイマーを停止するにはどこかで sample_timer.stop() する必要があります。 一度タイマーを停止することでプログラム実行中でも sample_timer**.**interval **=** 30 のように実行する間隔を変更することができます。 if __name__ == "__main__": app = SampleApp() sample_timer = rumps.Timer(app.hoge, 60) sample_timer.start() app.run() スタンドアロン化(参考) py2appのインストール rumpsアプリをビルドすることができるpy2appを導入する pip install py2app setup.pyの作成 py2applet --make-setup rumps_sample.py ファイルが生成されるので以下のように編集する """ This is a setup.py script generated by py2applet Usage: python setup.py py2app """ from setuptools import setup APP = ['rumps_sample.py'] APP_NAME = "rumps_sample" # アプリの名前 DATA_FILES = [] OPTIONS = { 'plist': { 'LSUIElement': True, # 実行時にDockに表示させない設定 'CFBundleName': APP_NAME, 'CFBundleDisplayName': APP_NAME, }, 'iconfile':'icon/icon_off.png', 'resources': ['icon'], } setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], ) ビルドするとdist配下にアプリケーションが生成される python setup.py py2app TIPS アプリケーション実行時にアセットの読み込みやライブラリが不足していた場合エラーになるが詳しいエラー内容まではわからない。下記のようにアプリケーションを実行するとターミナルに詳細なエラーが表示される。 dist/rumps_sample.app/Contents/MacOS/rumps_sample 開発時にデバックモードをオンにしておくとタイマーなどの挙動がわかりやすい。 rumps.debug_mode(True) 業務ツールを作ってみた(作例) 機能 出勤 slackステータス変更 slack APIよりステータス変更 打刻(ページ遷移) 打刻ページをブラウザで開く 退勤 slackステータス変更 slack APIよりステータス変更 打刻(ページ遷移) 打刻ページをブラウザで開く 日報 日報を投稿する機能 日報に APIがないためバッグラウンドでseleniumで動作 設定 勤務地を設定する機能 打刻忘れアラート APIで打刻状況を取得、打刻忘れがあれば通知を出す。 イメージ ※nigiri icons created by Freepik – Flaticon メニュー 日報フォーム 最後に いかがだったでしょうか?メニューバーアプリが数行のコードで簡単に作成できることを分かっていただけたかと思います。 Pythonで開発ができるので、機械学習や計算などの豊富なライブラリを利用できます。そのため、様々な機能の実装が選択肢に入ってくるのが魅力的だと思います。 作例として定常ワークフローをツールにしたものでは、ワークフロー全体を一か所で管理することが可能となり、出勤時に必要なタスクを簡単に処理できるようになりました。この効率化は打刻を忘れるリスクを減少させ、出勤時のワークフローをスムーズに実行できるようになったかと思います。また、メニューバーに彩りが増えることで気分的にハッピーになったような気がしますね。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは!新卒入社4年目の加藤です。 皆さんはサーバーの設定をした際に通信の疎通が取れなくて困ったことはありませんか? そんなときには通信の中身を見てみるとなにかわかるかもしれません。 今回はそんなパケットキャプチャについて簡単にご説明します。 パケットキャプチャとは? その名の通り、ネットワークを流れるパケットをキャプチャすることです。 パケットの中には、宛先のIPアドレスやポート番号、送受信されるデータ自体も含まれるため、そのネットワーク上でどんなデータが流れているか、どんな問題が発生しているのかを切り分けることができます。 方法はいくつかありますが、今回はPC上で直接取得してみましょう。 パケットキャプチャの簡単な始め方 では実際にパケットをキャプチャしてみましょう! …の前に、パケットをキャプチャするには専用のツールが必要です。 とは言っても、簡単にインストールができる他、OSに標準で用意されていることも多いです。 多く利用されるキャプチャツールをまずはご紹介します。 Wireshark Wireshark(ワイヤーシャーク)は、オープンソースのパケットキャプチャツールです。 Windows / macOS / Linuxなど幅広いOSに対応しており、インストールするだけでGUI上からキャプチャを行うことができます。 そのため、誰でも簡単にパケットキャプチャを行うことができるため、基本的にWiresharkを利用することをおすすめします。 https://www.wireshark.org/ tcpdump tcpdump(ティーシーピーダンプ)は主にLinuxで利用されるキャプチャツールです。 OS標準で付属していて、コマンドを利用することでキャプチャすることができます。 Linuxを利用していて、サーバー環境であるという理由などからWiresharkをインストールしづらい場合や、GUIが利用できないLinux環境の場合はこちらを使うのがおすすめです。 https://www.tcpdump.org/ % tcpdump -w dump.pcapng src host 192.168.1.1 and port 80 # 192.168.1.1を発信元とする80番ポートの通信のみをキャプチャ PktMon PktMon(Packet Monitor/パケットモニター)はWindows 10やWindows Server 2019以降で利用できるキャプチャツールです。 OS標準で付属していて、コマンドを利用することでキャプチャすることができます。 Windowsを利用していて、サーバー環境であるという理由などからWiresharkをインストールしづらい場合や、Server CoreなどGUIが利用できない環境の場合はこちらを使うのがおすすめです。 https://learn.microsoft.com/ja-jp/windows-server/networking/technologies/pktmon/pktmon C:\\Windows\\System32> pktmon filter add -i 192.168.1.1 -p 80 # 192.168.1.1との80番ポートのパケットのみ C:\\Windows\\System32> pktmon start --capture --pkt-size 0 # サイズが0以上のパケットをキャプチャ開始 C:\\Windows\\System32> pktmon stop # キャプチャ終了(PktMon.etlというファイルができる) C:\\Windows\\System32> pktmon etl2pcap PktMon.etl # キャプチャファイルをWiresharkで見られる形式に変換 実際にパケットキャプチャしてみよう 今回はWiresharkを利用してHTTPのパケットをキャプチャしてみましょう。 簡易的に、Microsoft Azure上にNGINXを起動したVMを用意して、検証してみます。 HTTPの通信 ではまずはシンプルにHTTPのパケットをキャプチャしてみましょう。 PCからのキャプチャ結果がこちらです。 ( 192.168.100.2 がPCで、 20.89.88.240 がVMです) 本当は通信がたくさんあるのですが、「 ip.addr==20.89.88.240 」と入力し、VMとの通信のみ表示するように絞っています。 まず最初に、PCとVMが通信を始めるためにコネクションを確立しようとしています。 PC → VMにSYNを送る(No. 25) VM → PCにSYN/ACKを返す(No. 26) PC → VMにさらにACKを返す(No. 27) これによってPCとVMの間で通信の準備が整いました。 次に通信の準備ができたので、実際にHTTPの通信をしています。 PC → VMにHTTP GETを送る(No. 28) VM → PCにHTTP 200 OKを返す(No. 30) なお、それぞれの間に通信が届いたことを表すACKをお互いに返しています。(No. 29/31). これで通信は終わりですので、最後にコネクションを切断しています。 PC → VMにFINを送る(No. 32) VM → PCにFIN/ACKを返す(No. 33) PC → VMにさらにACKを返す(No. 34) 通信を遮断してみよう では次に通信を遮断した状態で見てみましょう。 今回はVM上のLinuxに入っているiptablesで、HTTPで利用されている80番ポートをREJECTする設定を入れて検証してみます。 PCからのキャプチャ結果がこちらです。 なにやら赤文字で「 TCP Retransmission 」とたくさん書かれています。 これは、最初にコネクションを確立するために送ったPC → VMへのSYNパケット(No. 5)に対して、VM→PCに返ってくるはずのACKが一定時間内に返ってこなかったため、PCがVMにパケットを再送している状態です。 今回はiptablesを遮断したためとわかっていますが、原因がわからない状態でこの結果だと、 経路上のネットワーク VMに設定しているネットワーク VMに設定しているNetwork Security Group iptablesなどのVMのファイアウォール VM上のWebサーバ のどこに問題があるか、更にそれぞれPC → VM / VM → PCのどちらの方向の通信に問題があるかがわかりません。 では、VM上ではどうなっているのでしょうか。 VMはLinuxで起動しているため、SSH接続してtcpdumpコマンドで取得してみました。 その結果がこちらです。 ( 10.0.0.4 がVMで、PCのグローバルIPアドレスは伏せています) どうやら、PC → VMの通信はきちんとVMまで到達しているようですね。 (No. 36 / 38 / 40 / 75 / 77/ 79 / 81 / 157) つまり、PC → VM方向は問題がなさそうです。 そして、こちらでは緑色の文字で「 Port Unreachable 」とたくさん書かれてたパケットが送られています。 ICMPとはインターネットの通信における情報の通知などに利用されるプロトコルです。 (ちなみに、pingはこのICMPを利用しています。 ) 今回は「 Port Unreachable 」のため、ポートに到達できなかったことを表しています。 つまり VM上のWebサーバ iptablesなどのVMのファイアウォール に問題がありそう、ということがわかります。 (iptablesは設定によってICMPを返さなかったり、別のタイプを返すことも可能です) また、VM → PCにICMPを返しているにも関わらずPCでは受け取れていないことから、その方向の経路上でICMPがブロックされている可能性もありそうです。 このように、ブラウザからアクセスするだけですと読み込み中のままに見えますが、パケットをキャプチャしてみるとここまで原因や問題点を絞り込むことができます。 最後に ネットワークの中身は目に見ることが難しいため敬遠されがちですが、パケットキャプチャを行うことでどこに問題が発生しているのかがわかるようになります。 また、ネットワークがどのようなルールで通信されているかを知れば知るほど、パケットキャプチャも面白くなります。 これを機にぜひ試してみてください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは。ニフティ株式会社の添野翔太です。 今回は Amazon RDS のメンテナンスイベントをSlackに通知する機構を作成した話を共有します。 背景 先日、Amazon RDSのメンテナンスを原因として、とあるアプリケーションが一時的にダウンしました。振り返る中でメンテナンスイベントを見逃していたという課題が上がり、再発防止策の一環として、メンテナンスイベントの通知を行う機構を作成しました。 Amazon RDSのメンテナンスイベントをSlackに通知する チームで慣れている人が多いGoを利用してプログラムを作成しました。また AWS Lambda を基盤とし、定期的に発火させるために Amazon EventBridge を利用しました。 アーキテクチャは、以下の通りです。 ディレクトリ構成は以下の通りです。なお単体テスト用のファイルやREADME.mdなどは除いています。 src ├─lib │ ├─get_events.go │ ├─logger.go │ └─slack.go ├─go.sum ├─go.mod └─main.go 次に、src/lib/logger.go、src/go.sum以外のコードを以下に示します。 まずはsrc/main.goです。こちらではセッション作成など実施しています。 package main import ( "context" "os" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/rds" lib "github.com/sample/lib" "go.uber.org/zap" ) func handler(ctx context.Context, event events.CloudWatchEvent) { // NOTE: ログの設定 logger := lib.LoggerInit() defer logger.Sync() // NOTE: Slack WebHook URLを環境変数から取得 webhookURL := os.Getenv("SLACK_WEBHOOK_URL") if webhookURL == "" { logger.Warn("msg", zap.String("id", "DB-MAINTENANCE-NOTIFICATION-001"), zap.String("body", "Slack WebHook URLが設定されていない"), ) return } // NOTE: セッションを作成 sess, err := session.NewSessionWithOptions(session.Options{ Config: aws.Config{ Region: aws.String("ap-northeast-1"), // NOTE: 適切なリージョンに置き換えてください }, }) if err != nil { logger.Fatal("msg", zap.String("id", "DB-MAINTENANCE-NOTIFICATION-002"), zap.String("body", "セッション作成エラー"), zap.Error(err), ) return } rdsClient := &lib.RDSClient{ Client: rds.New(sess), } maintenanceActions, err := rdsClient.DescribePendingMaintenanceActions() if err != nil { logger.Fatal("msg", zap.String("id", "DB-MAINTENANCE-NOTIFICATION-003"), zap.String("body", "メンテナンス情報取得エラー"), zap.Error(err), ) return } lib.NotifySlackWithWebhook(maintenanceActions, webhookURL) } func main() { lambda.Start(handler) } 次にsrc/lib/get_events.goを示します。こちらでは DescribePendingMaintenanceActions を呼び出し、データ形式を整えた上で呼び出し元にデータを返却します。 package lib import ( "strconv" "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/rds/rdsiface" ) type RDSClient struct { Client rdsiface.RDSAPI } // NOTE: DescribePendingMaintenanceActionsを呼び出して、メンテナンス情報を取得する関数 func (c *RDSClient) DescribePendingMaintenanceActions() ([]*rds.ResourcePendingMaintenanceActions, error) { // NOTE: すべてのイベントデータを取ってくる input := &rds.DescribePendingMaintenanceActionsInput{} result, err := c.Client.DescribePendingMaintenanceActions(input) if err != nil { return nil, err } return result.PendingMaintenanceActions, nil } // NOTE: 複数アイテムが有る場合は結合して返却する関数。プロパティが欠損している場合は空白となる func getDescriptionAndTime(details []*rds.PendingMaintenanceAction) (string, string) { var descriptions []string var currentApplyDates []string for i, detail := range details { description := strconv.Itoa(i+1) + ": " currentApplyDate := strconv.Itoa(i+1) + ": " if detail.Description != nil { description += aws.StringValue(detail.Description) } if detail.CurrentApplyDate != nil { currentApplyDate += aws.TimeValue(detail.CurrentApplyDate).Format(time.RFC3339) } descriptions = append(descriptions, description) currentApplyDates = append(currentApplyDates, currentApplyDate) } return strings.Join(descriptions, "\n"), strings.Join(currentApplyDates, "\n") } 次にsrc/lib/slack.goを示します。こちらでは取得できたメンテナンスイベントをもとにSlackへ通知を行います。なお情報を瞬時に確認できるよう、DescriptionとCurrentApplyDateのみを返却するようにしました。 package lib import ( "bytes" "encoding/json" "fmt" "net/http" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" ) func NotifySlackWithWebhook(maintenanceActions []*rds.ResourcePendingMaintenanceActions, webhookURL string) error { // NOTE: イベントがない場合の代替メッセージ if len(maintenanceActions) == 0 { message := map[string]interface{}{ "text": "No pending RDS maintenance actions at the moment.", } payload, err := json.Marshal(message) if err != nil { return err } resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(payload)) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("Slack notification failed with status code %d", resp.StatusCode) } } // NOTE: イベントがある場合、メンテナンスアクションの通知を送信 for _, action := range maintenanceActions { description, currentApplyDate := getDescriptionAndTime(action.PendingMaintenanceActionDetails) message := map[string]interface{}{ "text": "Pending RDS Maintenance Action", "attachments": []map[string]interface{}{ { "title": "Pending RDS Maintenance Action", "text": "The following maintenance action is pending:", "fields": []map[string]interface{}{ { "title": "DB Instance ID", "value": aws.StringValue(action.ResourceIdentifier), "short": true, }, { "title": "Description", "value": description, "short": false, }, { "title": "Current Apply Date", "value": currentApplyDate, "short": false, }, }, }, }, } payload, err := json.Marshal(message) if err != nil { return err } resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(payload)) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("Slack notification failed with status code %d", resp.StatusCode) } } return nil } 最後にsrc/go.modを示します。 module github.com/sample go 1.19 require ( github.com/aws/aws-lambda-go v1.41.0 github.com/aws/aws-sdk-go v1.45.24 go.uber.org/zap v1.26.0 ) require ( github.com/jmespath/go-jmespath v0.4.0 // indirect go.uber.org/multierr v1.10.0 // indirect ) 通知の見た目は以下の通りです。 もしメンテナンスイベントがある場合は、 となり、無い場合には以下の通りです。 おわりに 本記事では、Amazon RDSのメンテナンスイベントをSlackに通知する機構を作成した話について述べました。この機構によりメンテナンスイベントを見逃すリスクが減ります。 参考記事 Amazon RDSのメンテナンスについて調べてみた We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
NIFTY Tech Day 2023では、インターネットサービス開始36年目を迎えた当社の信念や姿勢を発信いたします。 エンジニア社員がこれまでに得た自身の知見や当社での取り組み内容を発表します。 今年は、「ニフティのリアルを伝える」というコンセプトのもと、当社代表取締役社長によるこれまでとこれからを語る特別講演や当社社員が登壇するセッション、体験型ブース(オフライン参加のみ対象)を用意しています。 概要 公式サイト https://techday.nifty.co.jp/2023/ 日程 11月18日(土)10:00〜18:00(9:30会場受付開始)        18:00~ 懇親会(オフライン) 開催方式 ハイブリッド開催 会場参加:ニフティ株式会社 本社(新宿フロントタワー 18F) オンライン:YouTubeライブ配信(配信URLは準備次第公式サイトに掲載予定) ※ 対面、オンラインのハイブリッド開催です。どちらかの方法で参加ください。  ※ 会場参加の方には来場者特典を用意しています。 プログラム抜粋 セッション ニフティの過去・現在・未来 Notionへのデータ移行を成功させる5つのポイント 生成AIが切り拓く未来〜AWSにおける機械学習と自然言語処理〜 詳細は 公式サイト にてご確認ください。 ブース アンケートに回答してくじ引きに参加「リアルニフくじ」  「NIFTY Tech Day 2023」記念限定グッズをゲットしよう! 悪いコードをレビューせよ「コードレビューチャレンジ」 ニフティのエンジニアと協力してコードの間違いを見つけよう! イベント参加の記念撮影ができる「フォトブース」 参加方法 公式サイト の「参加登録はこちら」からconnpassにて登録をお願いいたします。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
NIFTY Tech Day 2023 https://techday.nifty.co.jp/2023/ 昨年ニフティグループとして初めての技術イベントを開催しました。 今年はオンラインとリアルのハイブリッド開催にパワーアップして実施いたします! 詳しくは NIFTY Tech Day 2023公式サイト をご覧ください。参加登録受付中です。みなさまのご参加をお待ちしています。 NIFTY Tech Talk NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 ニフティグループの社員が業務を通じて学んだことを発信しています! ほぼ毎月1回実施しています。 2023年10月現在、以下のスケジュールで開催を予定しております。 2023年10月31日(火)12時〜 今さら聞けない!GitHub Actions超入門 詳細が決まり次第 NIFTY engineering Eventカテゴリ と connpassのNIFTYグループ にて告知いたしますのでお楽しみに。 また、connpassでNIFTYグループのメンバーに登録いただきますとイベント情報が届きますのでご活用ください。 Rustオープン社内勉強会 ニフティ社員の有志が集まって行うRust勉強会を公開していました。 ほぼ毎週金曜日13時〜14時に開催していましたが、2023年8月25日回をもって一旦区切りをつけました。 また別のオープン勉強会を検討しております。次の開催につきましては connpass NIFTYグループ でご確認ください。
アバター
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! TechTalk#15は「今さら聞けない!GitHub Actions超入門」です。 GitHub Actionsをこれから使おうとしている方、GitHub Actionsを使っているもののもっと便利に使えるのではと思っている方はぜひご参加ください。 概要 日程:10月31日(火)12:00〜13:00 配信方法:YouTube Live 視聴環境:インターネット接続が可能なPC/スマートフォン 参加方法 connpass にて登録をお願いいたします。 YouTube Liveにて配信いたします。 ※YouTube LiveのURLは決定後、参加者への情報欄に記載いたします。 こんな方におすすめ GitHub Actionsをこれから使おうとしている方 GitHub Actionsを使っているもののもっと便利に使えるのではと思っている方 タイムテーブル 時間 コンテンツ 12:00 – 12:10 オープニング+会社紹介 12:10 – 12:25 LT1: 今からでも遅くない!GitHub Actions 入門 12:25 – 12:40 LT2: GitHub ActionsでLambdaコンテナをデプロイしてみた 12:40 – 12:55 LT3: デプロイだけじゃない!GitHub Actionsの活用法 12:55 – 13:00 まとめ+クロージング テーマ GitHub Actions入門、活用事例 登壇者プロフィール 宮本 達矢(登壇者 ) ニフティ株式会社 会員システムグループ第1開発チーム @niftyトップページなどの自社WEBサービスの開発・運用担当をしています 村松 啓寛(登壇者) ニフティ株式会社 会員システムグループ 第2開発チーム ニフティニュースの開発・運用を担当し、最近はモダンなフロントエンド開発を学びながら、実践中。 渡邊 大介(登壇者) ニフティ株式会社 会員システムグループ第1開発チーム @niftyトップページなどの自社WEBサービスの開発・運用担当をしています。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! 第14回目は「GitHub Copilotの導入や活用」です。 導入から活用まで開発業務でGitHub Copilotを使用している方の中から3名が語る回となります。 【9/26(火)12:00~】GitHub Copilotの導入や活用を開催します! 【LT1】GitHub Copilotの導入と開発生産性への効果 ニフティにおけるGitHub Copilot導入の流れや、導入後の効果などを数字も含めてお話しします。 導入を検討されている方に参考になること請け合い。 NIFTY engineeringには文章付で同様の記事も公開されているので、もっと詳しく知りたいという方はこちらもご覧ください。 https://engineering.nifty.co.jp/blog/21632 資料 【LT2】クリーンアーキテクチャとTDDの中でGitHub Copilotを使ってみた! テストコードの作成や整形で活用した事例のお話しとなっております。 チームの仲間への思いやりに満ちた内容となっております。うまく使えればチーム内の空気がよくなるという効果が期待できるかもしれません。 資料 【LT3】GitHub Copilotを使って爆速コーディングする めんどくさがりの方に是非とも見ていただきたい内容となっております。 GitHub Copilotを使うとこんなに簡単に、速くできるようになるという事例を見せてくれます。 配信中に飼っている犬の鳴き声が入るというほっこりするハプニングもありましたので、気になる方は アーカイブ のほうも見てください。 資料 まとめ ニフティでのGitHub Copilotの導入から実際の活用事例といった発表内容で、どれもよい効果を得られたというものになっていました。 これを見て導入してみたいと思った方や、今の使い方とは違った用途でも使えるのではという気付きなど、これが有意義な時間になっていただけたなら幸いです。 アーカイブ(YouTube) 発表資料(Speaker Deck) We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
こんにちは、新卒1年目の江口と西根です! 新人研修の一環としてニフティ2023年度新卒入社の10名がAWS JumpStart 2023 for NewGradsに参加しました! AWS JumpStart 2023 for NewGrads 設計編とは? 23卒の新卒1年目エンジニアを対象とした、AWS活用をリードする人材になるための入り口として提供されている実践的なAWS研修プログラムです。 AWSサービスについての学習のみならず、要件に沿った適切なアーキテクチャの検討・設計をする経験ができる内容になっています。 プログラムの到達目標 一般的なリファレンスアーキテクチャを理解できる AWSのコアサービスの概要や選定基準を理解できる AWSのアーキテクチャ図を作成する流れを知る 参加するまでにやること AWS JumpStart事前学習コンテンツ AWSに関する知識を少し身に着けておくために、以下の事前学習動画が用意されていました。 1. はじめてのアーキテクティング(60分) 2. AWS Solution Architect Associate資格勉強会(講義パート)(180分) ニフティでは内製の研修として先輩エンジニアが講義を行うエンジニア定例という制度があります。AWS JumpStart参加前にエンジニア定例でAWSの基礎について学んでおり、クラウドやAWSについて触れたことがあるという状態で参加しました。 内容 講義 事前学習動画の内容を、より初学者向けにかみ砕いた内容にして講義形式で学ぶ ハンズオン 実際にAWSサービスを使用して、アーキテクチャとリソースの構築方法について学ぶ アーキテクチャ検討 1. まずは課題に対して個人ワークとしてアーキテクチャを検討する 2. その後チームで最適なアーキテクチャを再度検討する 3. 要件が増えたらそれに対応するように、それぞれの要件にあったアーキテクチャを検討する 開催方法 オンライン ニフティは新入社員で同じ会議室で参加しましたが、他社だと在宅の方、執務室らしき場所で参加している方など様々でした! お菓子、飲み物の持ち込み自由とのことだったので、会議室にスターバックスのコーヒーのポットサービスを用意しました 参加人数 ニフティからは10名 5人1チーム×2 全体では400名以上 SIer、Web、ゲームなど様々な業種からの参加 使用したツール Slack アナウンスや質問、成果物の共有など Zoom 講義やブレイクアウトルームでのチーム別作業 Miro アーキテクチャ図の作成 AWSアカウント ハンズオン スケジュール 5/31(水) ~ 6/2(金)の3日間 ※6/3(day3)とありますが、正しくは6/2(day3)になります。 活動内容 1日目 1日目は、事前学習で学んできたことの振り返りをしつつ、ハンズオンを進めていくという流れで、Amazon ECS, AWS Fargate, Amazon RDSなどのサービスを使って、ToDoアプリを作るという内容でした。 スケーラブルハンズオンということで、システムに問題が発生した際の挙動の再現を行いました。Amazon ECSでは、2つ稼働しているタスクのうち1つを停止させ、自動で復旧していく様子を確認しました。そしてAmazon RDSでは、用意した2つのDBインスタンスのうちライターインスタンスに対してフェイルオーバーを行い、ライターとリーダーが自動で入れ替わる様子を確認しました。 これらのハンズオンを通じて、Design for Failureの考えに基づく可用性の高いシステムの魅力を体感することが出来たと思います。 2日目 2日目はAmazon EC2を使った簡単なハンズオンと、今回のイベントの目玉であるアーキテクティング課題に取り組みました。詳細について詳しくは説明できませんが、とあるサービスの可用性やスケーラビリティなどの観点を意識したアーキテクチャ設計を行うという課題で、私たちの班はこのような設計を考えました。 AWSに関することを調べていると必ず出てくるアーキテクチャ図ですが、実際に自分たちで作るとなるとかなり難しかったです。どこまでをパブリックサブネット配下にしたらいいだろうか、ロードバランサーはどこに置くのが正解か、といったような会話をしながら、手探りで形にしていきました。他班が作っているアーキテクチャ図の共有もあり、その設計が何を大事にしようとしているのかというのを、少しずつ読み取れるようになっていきました。 3日目 3日目はAWS Lambdaを使ったサーバーレスなサービスのハンズオンと、アーキテクティング課題の追加課題に取り組みました。 午前のハンズオンでは、AWS LambdaのトリガーをAmazon API Gatewayに設定しAmazon Translateを使って受け取った内容を翻訳させ、その内容をAmazon DynamoDBに流す、というようなサービスを作っていきました。 午後のアーキテクティング課題では、昨日作成したものにいくつかの機能追加を行っていきました。これらの機能に関しても詳細には語れませんが、私たちの班は最終的にこのような構成になりました。 AWS CodePipelineの流れや、Amazon ECSとAmazon ECRの繋がりなどを理解するのに苦労しました。同じサービスでも様々な形のアーキテクチャがあり、自分たちがやろうとしていることを実現させるにはどの構成が最適だろうかといった議論が白熱していました。 感想・学び 江口 ハンズオンやアーキテクティング課題を通じて、AWSのサービスに対する理解が深まっただけでなく、ユースケースに応じた最適な構成を作るためにはどのようなことを考慮するべきなのかといったような、実際の業務に活かせる視点を伸ばすことが出来たと思います。 また設計を進めていく中で様々なサービスが登場し、それらを理解するための情報を調べていく中で、それ以外の技術的なことにも触れることが出来ました。色々な分野の知識をつまんで、それをグループで話し合って噛み砕いていくといったようなことが出来て、非常に良い学びになりました。 西根 このプログラムに参加するまでは、AWSはニフティ内の研修で少し触ったことがあるだけで、アーキテクチャ図を見ても何が何だかわからない…という状態でした。3日間のプログラムを終えて、実際に自分でアーキテクチャを検討したり、他の人が書いたアーキテクチャ図を理解することができるようになりました。実際の業務でもアーキテクチャを検討したり、既存のサービスのアーキテクチャを理解するのに非常に役立ちました。 3日間という短い時間でしたが、難しくて怖いものだったAWSが、便利でおもしろいものに変わりました。ニフティには、社員が学習目的で利用できるAWSアカウントを提供する制度があり、個人のスキルアップに活用できるようになっています。こういった制度も利用しつつ、さらにAWSのサービスへの理解や活用を進めていければと思います! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
こんにちは、宮本です。最近は Astroフレームワーク を触っています。今まではReactのNext.jsしか触ってこなかったので、なかなか新鮮な気持ちです。……が、残念ながら本文はAstroは関係ないです。そのうちAstroの記事も書きたいですね。 はじめに さて、今回はGitHubのプルリク作成時のちょっと便利なGitHub Actionの紹介です。 プルリク作成時に地味にこんな悩みがありました。 「たまにプルリクを作成したマージ先のブランチで、masterとdevelopを間違える」 ……あまりに初歩的すぎて何も言葉が出ないかもしれませんが、それでも極々稀に、本当に稀にやらかします。今のところは流石に作った後で気づいて直したり、レビュー内で指摘が入って本当にやらかしたことはまだないですが、もしかするといずれやらかすかもしれません。CI/CDを完備していた場合には、そのままうっかり本番環境に……なんて目も当てられません。 というわけで、以下のような感じでプルリクを作成したら自動で主張激しくbotにコメントをしてもらっています。 GitHub Actionsを利用したbotのコメント投稿 実装 仕掛けているワークフローは以下の通りです。 name: Pull Request Comment on: pull_request: types: [opened] jobs: post_comment: runs-on: ubuntu-latest timeout-minutes: 5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout uses: actions/checkout@v3 - name: Check Base Branch name # 設定ファイルを取得し、コメント投稿対象のプルリクか判定 working-directory: .github/workflows/conf run: | BRANCH_ACTION=`cat pull_request_comment.json | jq '.["${{ github.base_ref }}"]' -r` if [ "$BRANCH_ACTION" != "null" ]; then echo "IS_COMMENT_POST=true" >> $GITHUB_ENV COMMENT_FILE_PATH=`echo $BRANCH_ACTION | jq '.["comment-file-path"]' -r` echo "COMMENT_FILE_PATH=$COMMENT_FILE_PATH" >> $GITHUB_ENV fi - name: Create comment # ghコマンドを利用してコメントを投稿 working-directory: .github/workflows/conf if: ${{ contains( env.IS_COMMENT_POST, 'true') }} env: URL: ${{ github.event.pull_request.html_url }} run: | gh pr comment -F ${{ env.COMMENT_FILE_PATH }} "${URL}" 設定ファイルとして、 .github/workflows/conf/pull_request_comment.json を定義しています。 { "master": { "comment-file-path": "comment_files/master.md" }, "develop": { "comment-file-path": "comment_files/develop.md" } } このjsonには、キーにプルリクエスト作成時にコメントを投稿したいheadブランチを指定しています。 そしてこのjsonファイルで指定したパス(.github/workflows/conf/comment_files/master.md)には、プルリク作成時に投稿したいコメントを書いたmdファイルを配置します。mdファイルにコメント内容を分離して、コメントの編集は気楽にできるようにしています。 # masterブランチへのマージ 本番環境へのリリースを行うPRです。 さいごに さて、プルリク作成時に、ブランチごとに任意のコメントを投稿するワークフローの紹介でした。 今回の例ではただブランチ名とリリース用のプルリクだと教えてくれるだけですが、投稿するコメント内容を編集すれば、開発時のプルリクには不要だけどリリース前に必要なチェック項目(リリース日の調整、GTMの設定変更の確認など)を自動で投稿するようなこともできます。逆もまた然り。プルリクテンプレートに記述しておくこともできますが、この項目はリリース時にしかいらないのに行が多くて邪魔だし毎回消すのも面倒、と思った場合は入れてみるのもありかもしれません。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは、会員システムグループの上原です。 先日、社内ISUCONを開催し、参加者から好評の声をいただくことができました。 当日どんな感じだったのか気になる方は以下のブログ記事をご覧ください。 https://engineering.nifty.co.jp/blog/21057 本イベントでは参加者にハンズオンで使用するサーバーを配布しました。参加者の人数とクォータ制限の観点から、数百台のサーバーをマルチアカウント・マルチリージョンで展開する必要があったのですが、手動でやるのは面倒だったので、可能な限り自動化してみました。 目標 数百台の同じ設定を入れ込んだサーバーを複数のAWSアカウント・複数のリージョンで簡単に立ち上げたい 従来の方法 同じ設定のサーバーを大量に立ち上げるためにまずはAMIを作成し、そのAMIを元にサーバーを起動する方法が思いつきます。 具体的には以下の手順になります。 UbuntuやAmazon LinuxなどのOSが入っているAMIからEC2インスタンスを立ち上げる sshやセッションマネージャなどでサーバにログインし、EC2インスタンスに必要なソフトウェア・設定を入れ込む EC2インスタンスをAMI化する 作成したカスタムAMIを使って複数アカウント・複数リージョンにサーバーを立てる 従来の方法の問題点とその解決策 従来の方法の問題は、AMIを作成するためだけにEC2インスタンスを起動しコマンドを打ちこみイメージを作成する必要があるところです。 ですが、手作業だと抜け漏れやミスが発生しやすく、何しろ面倒です。 そこで、AMIを自動的に作成する方法はないかどうか探してみたところ見つけたのが、Amazon EC2 Image Builderというサービスです! Amazon EC2 Image Builderとは? Amazon EC2 Image BuilderとはAWSのフルマネージドサービスで、AMIを自動で生成・検証・配布してくれるサービスです。 手順をYAMLファイルに書いておけばその手順をもとにAMIを作成・検証し、AMIを他リージョン・他アカウントにも共有・コピーして使えるようにしてくれます。 より詳しく知りたい方は以下のBlack Beltのpdfがわかりやすいので参考にしてください。 https://pages.awscloud.com/rs/112-TZM-766/images/20200825_BlackBelt_EC2imagebuilder.pdf ハンズオン 今回はお試しとしてslコマンドを入れたサーバーを5台同時に立ててみます 使用するOSはUbuntu 22.04 公式のAMI(ami-0d52744d6551d851e)を使用する AMIは自アカウントのap-northeast-1とus-east-1に展開します ちなみに、今回のterraformのコードをいじると別のアカウントにも展開できるようにすることもできます 作成したAMIを使ってEC2インスタンスをap-northeastに5台起動します 今回使うコードはこちらのレポジトリに配置しています。 https://github.com/Penpen7/ec2-image-builder-test Amazon EC2 Image Builderでパイプラインを作成する ここではAWS EC2 Image Builderをterraformを使って立ててみます レポジトリをcloneします git clone https://github.com/Penpen7/ec2-image-builder-test.git terraform実行に必要なS3バケットや認証情報を入れてください cd ec2-image-builder-test vim ec2/providers.tf vim image-builder/providers.tf terraform initで初期化、terraform applyでAWS環境に反映します cd image-builder terraform init terraform apply 実行が終わればEC2 Image Builderのパイプラインが作られた状態になります コンソールに入って設定を見てみる AWS コンソールにログインし、EC2 Image Builderでイメージパイプラインを表示します 以下のように作成したパイプラインが表示されるはずです パイプラインの中を見ると、パイプラインで出力したイメージ(AMI)・作成手順・配布設定・トリガーの設定を見ることができます レシピはどのようにAMIを構築するか指定します ベースとなるイメージをUbuntuに指定し、slコマンドをインストールする手順をコンポーネントに記述します ディストリビューション設定をクリックし、image-builder-testの中身を見ると以下の通りになっています ap-northeast-1とus-east-1に作成したAMIを展開するように設定されています パイプラインを実行し、AMIを作成する 手動でパイプラインを実行します。イメージパイプラインからimage-builder-testにチェックを入れてパイプラインを実行するをクリックします パイプラインの出力イメージに作成中のイメージが追加されます 出力イメージをクリックすると作成状況がわかります 作成中はEC2 Image BuilderがEC2を起動して作業をしています 完了になればAMIがap-northeast-1とus-east-1に作成されているはずです EC2を起動する terraformコードで今回作成したAMIを参照してEC2を5台起動できるようにしているので、ec2ディレクトリに移動しterraform applyで反映させます cd ../ec2 terraform init terraform apply EC2コンソールでサーバーが5台起動していることがわかります セッションマネージャでログインするとslコマンドがインストールされてることがわかります このようにAMIの作成手順や設定を記述しパイプラインを実行することで、AMIの作成・配布を自動化できます 設定を変更し、もう一度パイプラインを実行すればAMIを更新してくれます お片付け terraform destroyでEC2 Image BuilderとEC2を破棄します cd ../ec2 terraform destroy cd ../image-builder terraform destroy コンソール画面でバージニア北部と東京リージョンに作成されたAMIの登録解除を行い、スナップショットを削除します EC2 Image Builderのterraformのコード説明 もし気になる方向けにterraformのコードの中身を簡単に説明します Image Pipeline AMIを生成・配布するための自動実行されるパイプラインです CodePipelineのPipelineと同じやつです 1つのパイプラインから1つのAMIができます terraformの設定の中身はレシピとインフラ設定、配布設定のarnを指定しているだけです その他スケジュールで定期的にパイプラインを実行することもできます resource "aws_imagebuilder_image_pipeline" "this" { name = var.name # AMIを作成するためのレシピ image_recipe_arn = aws_imagebuilder_image_recipe.this.arn # AMIを作成するためのインフラ構成 infrastructure_configuration_arn = aws_imagebuilder_infrastructure_configuration.this.arn # AMIを作成した後の配布設定 distribution_configuration_arn = aws_imagebuilder_distribution_configuration.this.arn } https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_pipeline Recipes レシピはAMIをどのように作っていくか記述していきます ここでslをインストールするように指示する自作のコンポーネントを指定します resource "aws_imagebuilder_image_recipe" "this" { name = var.name # ベースとなるAMI parent_image = var.parent_image working_directory = "/tmp" version = "1.0.0" block_device_mapping { device_name = "/dev/sda1" no_device = false ebs { delete_on_termination = true encrypted = false volume_size = 16 volume_type = "gp2" } } # slをインストールするよう指示するコンポーネントのarnを指定 component { component_arn = aws_imagebuilder_component.this.arn } # SSMを配布するAMIから削除するかどうか # defaultだとtrueのはず systems_manager_agent { uninstall_after_build = false } } https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_recipe Components コンポーネントはAMIを作成するためにどのようなコマンドを実行するかを記述します resource "aws_imagebuilder_component" "this" { name = var.name platform = "Linux" version = "1.0.0" data = file( var.component_build_path ) } AWSが公式に出しているコンポーネントもあります 他のレシピからも参照できる再利用可能なコンポーネントです 中身はYAMLで記述します ビルドフェーズでaptを使ってslをインストールしますテストフェーズで正常動作するか検証することができ、今回はwhichを使ってslコマンドが存在するか検証します yamlに記述するnameにスペースがあるとエラーになるので注意してください 引数を受け取って処理させることも可能です(今回は引数を受け取りません) name: install_sl description: install sl schemaVersion: 1.0 phases: - name: build steps: - name: install_sl action: ExecuteBash inputs: commands: - sudo apt update - sudo apt install sl - name: test steps: - name: run_sl action: ExecuteBash inputs: commands: - which sl https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_component infrastructure Configuration Image BuilderがAMIを作成するために立ち上げるEC2の設定を記述します Image Builderが起動するEC2には以下のポリシーが必要です arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilder arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore SSM agentとSSMが疎通できる環境でないとImage Builderは使えないです セキュリティグループでインターネットへの外向きのアクセスを全開放にして、サブネットをパブリックに、起動時にパブリックIPが自動的につくように設定すると簡単に疎通できると思います resource "aws_imagebuilder_infrastructure_configuration" "this" { name = var.name # AMIを作成するためのEC2につけるIAMロール instance_profile_name = aws_iam_instance_profile.image_builder.name # AMIを作成するためのEC2のインスタンスタイプ instance_types = [var.instance_type] # AMIを作成するためのEC2につけるセキュリティグループ security_group_ids = [aws_security_group.this.id] # AMIを作成するためのEC2につけるサブネット subnet_id = aws_subnet.public.id # 異常終了時にインスタンスを削除するかどうか # パイプラインが失敗しデバッグしたい時はfalseにしてインスタンスを残すようにした方が良い terminate_instance_on_failure = false lifecycle { ignore_changes = [instance_metadata_options, logging] } } https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_infrastructure_configuration Distribution Configuration 作成したAMIをどのアカウント・どのリージョンに展開するか記述します resource "aws_imagebuilder_distribution_configuration" "sample" { name = var.name dynamic "distribution" { for_each = var.regions content { ami_distribution_configuration { name = "${var.name}{{imagebuilder:buildDate}}" ami_tags = { Name = "${var.name}" } # AMIのコピー先 target_account_ids = [ data.aws_caller_identity.current.account_id, ] # AMIの共有先(コピーせず、自アカウントのAMIを他アカウントから読めるようにする) launch_permission { user_ids = var.ami_share_ids } } # リージョン region = distribution.value } } } data "aws_caller_identity" "current" {} https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_distribution_configuration 1リージョンごとに設定を入れます terraformだとdynamicという構文を使うとfor文で一気に設定を入れることができます ami_tagsでamiのtagを指定できます target_account_idにAMIをコピーして配置したいIDを指定します 他のAWSアカウントでも可能ですが、ここで指定したアカウントにクロスアカウントアクセス用のロールを作成する必要があります 逆にコピーせず共有したい時にはlaunch_permissionでIDを指定しましょう resource "aws_iam_role" "ec2_image_builder_distribution_cross_account_role" { name = "EC2ImageBuilderDistributionCrossAccountRole" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Sid = "" Principal = { AWS = "arn:aws:iam::${var.origin_aws_id}:root" } }, ] }) managed_policy_arns = [ "arn:aws:iam::aws:policy/Ec2ImageBuilderCrossAccountDistributionAccess", ] } https://docs.aws.amazon.com/imagebuilder/latest/userguide/cross-account-dist.html 最後に 今回は社内ISUCONで参加者にサーバーを配布するために使用したAmazon EC2 Image Builderをご紹介しました。 このサービスを使うと、AMIの作成を自動化することができ非常に便利です。 もしよろしければ使ってみてください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
みなさんこんにちは、ニフティ株式会社新卒 1 年目の中井です。突然ですが皆さん、業務の新しいバッチ処理を Rust で書いてみようと思ったことはありませんか?ありますよね。私も入社して半年の 9 月に達成しました!!わーい! 実は元々ニフティ社内で Rust が使われていたわけではありません。それなのになぜ入社してこんなにすぐ Rust を導入することができたのかというと、他にも Rust を導入しようと企んでいる先輩がすでに オープン社内 Rust 勉強会 を開いてくださっていたからなんです。そこで学んだ知識も活かして、今回はぜひ Rust を書きたいと主張して書かせていただきました! 以前は 退勤を失敗して対策を練ったり していましたが、今回は今風な技術を使ってみたというお話です。やらかしてばかりでもないのです。 Rust とは Rust という名前だけは聞いたことがある方もいらっしゃるのではないでしょうか。自分も数々の ストリーマーがギャンブルに興じている動画 を見たことがあります。いえ、そちらではなくて、プログラミング言語の Rust です。 公式サイトが https://www.rust-lang.org/ にあるのですが、簡単にいうと、「速い」「安全な」そして「使いやすい」言語です。言語の習得がちょ〜〜っとだけ難しいという話もありますが、エコシステムとしては本当に完成度が高いです。 Cargo というパッケージマネージャー兼ビルダーの存在 Rust Analyzer という随一のエディタ支援機能 Rust の良さは無限に語れてしまうのですが、本題ではないので、とりあえず最強の言語があるということだけ覚えてください。 お題: 画像を S3 から S3 へ移動させる Lambda をつくれ ニフティには AWS の練習用アカウントを用意して月 100 ドルまで使わせてくれる実弾演習場という制度があります。ということで、Rust でどうやって Lambda を書いていくのか、小さいアプリケーションを使って実際にデプロイしてみましょう。 …待ってください、たしかにこれだけだとただの暇を持て余した人の遊びみたいな感じですが、この Lambda 部分で画像を加工してみたり、文章を要約してみたり、いろいろ夢がひろがるじゃないですか! Cargo Lambda と Terraform を組み合わせる 作るものは決まったとして、問題はデプロイ方法です。 Lambda を zip ファイルに固めて terraform apply する、という方法なら比較的簡単です。ただ自動化が大変ですし、そもそも毎回 Terraform を実行するのは怖いです。何かのタイミングでインフラを壊しそうで…。 かといって、世の中には SAM とか Serverless Framework とかいうものもあるとは聞きますが、難しそうなので何も理解していません。 今回は Cargo Lambda を採用します。これは Cargo という Rust のパッケージマネージャーを拡張して、Lambda 用のコマンドを多数増やしてくれるツールです。 https://www.cargo-lambda.info/ これを使うと、デプロイまでの流れはこんな感じ。簡単なので私でも使えます。 cargo lambda new でプロジェクトを作成 cargo lambda build で Lambda 用のバイナリをビルド cargo lambda deploy で AWS 上にデプロイ 本当に Lambda 単体がただ動けばよいだけならこれだけで OK です。最低限の IAM ロールを含め、全てをデプロイしてくれます。 ただし今回は、インフラ側は Terraform で管理した上で、うまく Cargo Lambda と組み合わせることにしました。というのも、どうせ S3 が必要になるし、また S3 へのアクセス権など IAM ロール自体の調整もあるからです。すなわち…. Terraform で S3 や IAM ロール、ダミーの仮の Lambda まで作成してしまう Cargo Lambda でホンモノの Lambda を上書きする これで、Lambda を含めたインフラ全体を Terraform で管理しつつ、日常的な Lambda のデプロイには Terraform を利用しない形にできます。 …ちなみに私は AWS 初心者なので、他にもっといい方法がある気がしています。よければ教えてください。やっぱり SAM 勉強したほうが良いですか? 実際につくってみる 何はともあれ、実際に作ってみましょう。ちなみに完成品はこちらにおいておきます。 https://github.com/statiolake/aws-terraform-rust Terraform のメインファイルを書く まずはお決まりのやつです。AWS を使いたいので aws プロバイダを指定します。 provider "aws" { region = "ap-northeast-1" shared_credentials_files = ["~/.aws/credentials"] profile = "{your-aws-profile}" default_tags { tags = { managed_by = "terraform" } } } main.tf S3 のバケットやイベントとの連携を用意する 次に S3 関連の設定を書いてしまいます。今回は入力・出力用に 2 つのバケットを用意します。また、Put イベントで Lambda を呼び出すように設定します。 {your-prefix-} には、他の人とぶつからなさそうな、自分だけの好きな文字列を入れてください。S3 の名前は全世界で重複しない必要があるらしいので、私が作った S3 バケットと衝突してエラーになってしまいます。 # 入力側 resource "aws_s3_bucket" "input" { # 名前は適当なプレフィックスを付けるなど、必ず変更すること bucket = "{your-prefix-}example-aws-terraform-rust-input" } # 出力側 resource "aws_s3_bucket" "output" { # 名前は適当なプレフィックスを付けるなど、必ず変更すること bucket = "{your-prefix-}example-aws-terraform-rust-output" } # 入力側の S3 の Put イベントで Lambda を呼び出す resource "aws_s3_bucket_notification" "put_notification" { bucket = aws_s3_bucket.input.id lambda_function { lambda_function_arn = aws_lambda_function.lambda.arn events = ["s3:ObjectCreated:Put"] } } resource "aws_lambda_permission" "allow_s3_invoke" { statement_id = "AllowS3Invoke" action = "lambda:InvokeFunction" function_name = aws_lambda_function.lambda.function_name principal = "s3.amazonaws.com" source_arn = aws_s3_bucket.input.arn } s3.tf ダミーの Lambda を Terraform で作成する さて、ここからがトリックの 1 つ目、ダミーの Lambda の作成です。 後で本物の Lambda をデプロイすることになるので、いろいろなオプションは本物の Lambda が動く基準に合わせて作ります。 runtime : provided.al2 handler : bootstrap また、後で Cargo Lambda によりコード部分を上書きするわけですが、それを後でまたダミーに書き戻されてしまっては困ります。これを避けるため、 source_code_hash を ignore_changes に指定して、その差分を無視してもらうようにします。 resource "aws_lambda_function" "lambda" { function_name = "example-aws-terraform-rust" handler = "bootstrap" role = aws_iam_role.lambda_role.arn runtime = "provided.al2" filename = data.archive_file.lambda.output_path source_code_hash = data.archive_file.lambda.output_base64sha256 environment { variables = { AWS_OUTPUT_BUCKET_NAME = aws_s3_bucket.output.bucket } } lifecycle { ignore_changes = [source_code_hash] } } # ここではダミーを指定する data "archive_file" "lambda" { type = "zip" source_dir = "dummy_lambda" output_path = "archive/dummy_lambda.zip" } lambda.tf そして、Terraform ではダミーの Lambda をデプロイします。 環境に provided.al2 と bootstrap を指定しているので、その環境で動くようにダミーの方から合わせてあげる必要があります。要するにダミーには Python とかは使えないわけですが、まあダミーなので中身はなんでも大丈夫です。シェルスクリプトで bootstrap というファイルを作って、簡単にエラー終了するようにしておきました。 #!/bin/sh echo "WARNING: This is not an actual lambda!" echo "This is a placeholder during terraform setup." echo "Please replace this lambda to the actual function by cargo-lambda." exit 1 インフラをデプロイする terraform apply をして、AWS 環境を確認すると… S3 バケットあります。 ダミーの Lambda もあります。いい感じです! この時点でイベントのセットアップなども完了しているので、バケットにファイルを入れると CloudWatch Logs にエラーが流れるはずです。 うまく動いていそうですね! cargo lambda new で Lambda を作成 次はお待ちかね、Rust での Lambda の作成です。前述の通り、Cargo Lambda というツールを使います。 このツール、インストール方法がしっかり整備してあって、簡単に使い始めることができます。 ※ 情報が古くなっている可能性もあるので、 公式サイト も合わせてご確認ください。 macOS / Linux をお使いの方であれば、homebrew から簡単に入ります。 brew tap cargo-lambda/cargo-lambda brew install cargo-lambda Windows の方は scoop から入れられるらしいですね。 scoop bucket add cargo-lambda <https://github.com/cargo-lambda/scoop-cargo-lambda> scoop install cargo-lambda/cargo-lambda PC 環境を汚したくない方に向けては Docker イメージも提供されていますので、 公式サイト を確認してみてください。 インストールが終われば、次は早速プロジェクトを作成していきましょう。 今回は actual_lambda という名前でプロジェクトを作っていきます。おもむろに cargo lambda new actual_lambda としてみてください。すると… $ cargo lambda new actual_lambda > Is this function an HTTP function? No > AWS Event type that this function receives s3::S3Event 画面上のいろいろと質問に答えるだけでプロジェクトが完成、最低限のボイラープレートも全部書いてくれています。なんと便利な。 ここから先は、もう普通の Rust プログラミングです。 Rust プロジェクトに AWS SDK を追加する 今回は、Lambda 内から S3 にアクセスしたいんでした。プロジェクトに AWS SDK を追加します。ちなみに Rust の AWS SDK はサービスごとに分離されています。 使いたいサービスを探し、必要なライブラリを追加してください。 https://awslabs.github.io/aws-sdk-rust/ 今回は S3 なので、ターミナルにこんな感じで打ち込めば完了です。 cargo add aws-config cargo add aws-sdk-s3 何気なく aws-config も追加していますが、これは設定の読み込みのためのライブラリで、どのサービスを使うにしても必要なものっぽいです。 本体を Rust で書く こんどこそ処理を編集して、実際に S3 から S3 へ画像が移動するようにしてみましょう。Rust は src/main.rs ファイルからスタートなので、このファイルを書き換えていくことになります。 S3 から S3 へ移動させるサンプルコードを下に掲載します。 あっ、コピペしたくなりますよね。わかります。全然コピペでもいいんですけど…。 ただ、もう少しだけ時間があるなら、せっかくなので手で写経してみませんか。Rust の最強支援機能、Rust Analyzer をぜひ使ってみてください。補完から型ヒントまで、とても気持ち良く書けるんです。 VSCode をお使いの方なら、Rust Analyzer は拡張機能から簡単にインストールできます。 use std::env; use aws_config::load_from_env; use aws_lambda_events::event::s3::S3Event; use aws_sdk_s3::Client; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; /// This is the main body for the function. /// Write your code inside it. /// There are some code example in the following URLs: /// - <https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples> /// - <https://github.com/aws-samples/serverless-rust-demo/> async fn function_handler(event: LambdaEvent<S3Event>) -> Result<(), Error> { let LambdaEvent { payload, .. } = event; for record in payload.records { let bucket = record.s3.bucket.name.unwrap(); let key = record.s3.object.key.unwrap(); let config = load_from_env().await; let client = Client::new(&config); let output_bucket_name = env::var("AWS_OUTPUT_BUCKET_NAME") .expect("failed to get output bucket name from env var"); let object = client.get_object().bucket(&bucket).key(&key).send().await?; client .put_object() .bucket(&output_bucket_name) .key(&key) .body(object.body) .send() .await?; client .delete_object() .bucket(&bucket) .key(&key) .send() .await?; } Ok(()) } #[tokio::main] async fn main() -> Result<(), Error> { tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) // disable printing the name of the module in every log line. .with_target(false) // disabling time is handy because CloudWatch will add the ingestion time. .without_time() .init(); run(service_fn(function_handler)).await } actual_lambda/src/main.rs 入力側 S3 からオブジェクトを取得して、出力側 S3 に書き込んで、入力側 S3 のオブジェクトを削除する、というコードになっています。 Cargo Lambda でビルドする 出来上がったら、次はビルドをしましょう。 cargo lambda build --release --target x86_64-unknown-linux-gnu --target では、Lambda のタイプを指定します。 x86_64-unknown-linux-gnu : いわゆる普通の Lambda です。 aarch64-unknown-linux-gnu : ちょっと安い ARM (Graviton) タイプの Lambda です。 こんなスイッチ一つ切り替えるだけでいい感じにネイティブバイナリが作れる、これも Rust のいいところだと思っています。 Cargo Lambda でデプロイする ビルドがうまくいけば、デプロイです。 cargo lambda deploy --profile {your-aws-profile} --binary-name actual_lambda example-aws-terraform-rust 最後の引数 example-aws-terraform-rust は Lambda の名前です。忘れないでくださいね!これを忘れてしまうと全く新しく actual_lambda という名前の Lambda が生成されてしまいます。そうすると IAM ロールなども自動で作成されてしまってゴミが増えるのでご注意を… (n 敗)。 ちなみにサンプルリポジトリの方には Docker を使うバージョンのコマンドを Makefile に書いてあるので、参考になるかもしれません。 さて、デプロイが完了したらもう動くようになっているはずです! お楽しみの動作確認 入力用の S3 バケットに適当なファイルをアップロードすると…. 出力用の S3 バケットに出力されました!わーい。 まとめ 今回は AWS 上に Terraform でインフラを作成し、その中の Lambda を Rust で実装していきました。 Terraform ではダミーの Lambda を作成し、Cargo Lambda を使ってデプロイすることで、インフラとコードのデプロイを分離することに成功しました。また、Rust 側のほとんどの作業は Cargo や Cargo Lambda が面倒を見てくれることも紹介しました。 どうでしょう。Rust で Lambda と聞くと難しそうな印象もあったかもしれませんが、思ったより簡単そうだなと思ってもらえたら幸いです。 ぜひ一度 Rust で書いてみてください。そして Rust を広めていきましょう!! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
ニフティ株式会社では、2023年9月29日(金)に開催される「SRE NEXT 2023 IN TOKYO」にGOLDスポンサーとして協賛いたします。 また、ブースを出展いたしますので、ご来場をお待ちしております。 SRE NEXT 2023 IN TOKYO SRE NEXT 2023 IN TOKYO イベント概要 開催日:2023年9月29日 (金) 10:00 〜 18:00 (懇親会 19:00 – 21:00) オフライン会場: 九段会館テラス オンライン会場: YouTube ※詳細は公式サイトをご覧ください https://sre-next.dev/2023/ 前回(SRE NEXT 2022)ではスポンサーセッションに参加しました 当社、浅見( @rubihiko ) の発表資料はこちらです。 ブースについて ニフティブースでは、現場のエンジニアがローテーションして常駐しております。 是非お越しください! We are hiring! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
N1!のPorductivity Engineerとして生産性向上に関わる活動をしている石川です。 ニフティでは2023年6月からPoCとしてGitHub Copilot for Businessを限定的に導入していましたが、2023年9月に正式導入することになりました。現在では希望する方は誰でも利用することができます。 本日はGitHub Copilotの利用状況と効果について話したいと思います。 GitHub Copilotの利用状況 Acceptance Rates 言語別 利用者へのアンケート結果 工数削減効果 改善されたことと変わらなかったこと 生産性は向上したか(実感ベース) 費用対効果について 最後に 宣伝 GitHub Copilotの利用状況 Acceptance Rates 20人ほどでPoCを行っていたときのCopilotの提案採用率となります。 導入初期は好奇心からか採用率は50%を超えてましたが時間が経って30%前後に集約しました。Copilot利用者の 提案採用率は約30% なので近い値にはなっていますね。 利用者がまだ少ないのと人や言語差の影響が大きく採用率が乱高下してますが、利用者がもっと増えてきたらまた変わってくるかと思います。 言語別 大抵のプログラミング言語では30%超えてますね。Javaに関しては60%超えているので、GitHubのブログにあった通りなので本当に精度がいいのだと思います。 記述内容が短文だったり自由度の高い言語ほど採用率が低い傾向にあるように見えます。 こちらもまだ母数が少なく使う人の採用スタイルに率は大きく依存するので、もっと普及するとどう変化するか追っていきたいと思います。 利用者へのアンケート結果 PoC時に取ったアンケートの結果も一部共有いたします。 工数削減効果 アンケートで取った以下の質問を散布図にしたものがこちらになります。 1日あたりCopilotの効果でどのくらい時間が節約できたか 10分未満, 20分, 30分, 1時間, 2時間, 3時間, 4時間以上 1日の仕事の中でコーディングに費やす時間 20%未満,40%,60%,80%以上 集計すると平均して 1日38分 工数削減できていて、コーディングに費やす時間が 23.73% 減ったと実感しているという結果となりました。 GitHub Copilot を使用するとタスクを 55% 早く完了できる らしいので、それに比べるとまだまだですが十分効果は出ていると思います。 改善されたことと変わらなかったこと Copilot利用前後での変化を聞いた結果がこちらになります。 主にコーディング速度とそれに伴うタスク完了までの速度に大きく影響を与えていますね。 書くのがめんどうな型定義やテストまわりで活躍していて精神面でもよい効果が出ているようです。 都度ドキュメントを調べたりする機会が減ることで集中力が高まる効果もあるかと思ったのですが、そこについては変わらないという結果となりました。 生産性は向上したか(実感ベース) 8割以上 が生産性向上を実感している。 費用対効果について GitHub Copilot for Business は $19/user/month と決して安くはないですが、コーディングに費やす時間23%減という効果は出ているため、下振れたとしてもある程度の時間コーディングしているのであれば十分元は取れるものだと思います。 ただ今回の結果が一時的なものである可能性もあるため、効果については他の要素との相関も見つつ継続して計測していくつもりです。 最後に PoC開始時点では試すだけ試してみるかというスタンスでしたが、実際に使ってみるとプログラミング体験がガラッと変わりました。ちょうどいいところにちょうどいいものを提案してくれる体験、悩んでいると合っているかは置いておいて候補を出してくれるという体験はとても心地よいものでした。 ChatGPTで十分と考えている方も一度GitHub Copilotを触ってみることをおすすめします。 公式がCodespacesでCopilotを体験できる環境を用意してくれているので無料で触れます。 Take GitHub Copilot on a test-flight. – GitHub Resources GitHub Copilot ChatもPublic betaになりましたし、Copilotは利活用できる余地は多く残されていて、まだまだ生産性を上げてくれるのではないかと考えています。 宣伝 来週 9月26日(火) 12:00〜13:00にYouTube LiveにてGitHub CopilotをテーマにしたLTを行います。こちらでは今回書いていない利用者視点の話もあります。 興味がある方がいましたら以下のconnpassにご登録をお願いいたします。 GitHub Copilotの導入や活用 – connpass We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
こんにちは。会員システムグループの渡邊です。 皆さんはGitHubのリリースノートを使っていますか? リリースノートはGitHubリポジトリでプロダクトのリリースを管理し、ユーザーに新しいバージョンや変更点を提供するための機能です。 私が所属するチームではスクラムを採用しており、細かいリリースが1スプリントで多く行わるので、このリリースノートをスプリントで行ったリリースの実績として活用しています。 ただし、私たちは複数のリポジトリにまたがった開発を行っており、各リポジトリごとに手動でリリースノートを作成するのは非常に時間がかかる作業でした。そこで、リリースノートの自動化を検討し、以下のような条件を付け加えて実装を行いました。 2週間で行われるスプリントの最終日に今回のスプリントで行ったリリース内容がリリースノートに公開される どのスプリントかをわかりやすくするために、リリースノートのタグとタイトルにスプリント名(sprint1,sprint2)をつける 前提 今回は Release Drafter というGitHub Actionsのワークフローを使って実装していきます。 簡単にRelease Drafterの説明をします。 Release DrafterはPRを出したタイミングで変更したファイルの種類を検出して、自動的にラベルを付与します。 そのラベルをもとにmasterにpushなどの特定のトリガーを基準にリリースノートのドラフトを自動で作成するワークフローです。 加えて、GitHub公式が提供している github-script というGitHub Actionを使うことで、ワークフロー内でJavaScriptを実行し、ワークフローをカスタマイズしています。 実装 configファイル作成 .github/release-drafter.yml というファイルを作成します。 これはconfigファイルになっていて、リリースノートに記載する内容の制御やPR時に付与するラベルの条件を設定することができます。 ラベルを付与する条件は、ディレクトリや特定の拡張子を指定することが可能です。 categories: - title: ' Release' labels: - 'release' - title: ' Features' labels: - 'feature' - 'enhancement' - title: ' Bug Fixes' labels: - 'fix' - 'bugfix' - 'bug' - title: ' Maintenance' label: 'chore' - title: ' Refactoring' label: 'refactor' - title: ' Documentation' label: 'documentation' - title: ' Dependency update' label: 'dependencies' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. template: | $CHANGES autolabeler: - label: feature branch: - '/^feat(ure)?[/-].+/' - label: bug branch: - '/^fix[/-].+/' - label: chore branch: - '/^chore[/-].+/' - label: refactor branch: - '/(refactor|refactoring)[/-].+/' - label: documentation branch: - '/doc(umentation)[/-].+/' files: - '*.md' - label: release branch: - '/release[/-].+/' - label: enhancement branch: - '/(enhancement|improve)[/-].+/' - label: nextjs files: - 'src/**/*' - 'package.json' - label: github files: - '.github/**/*' 実行ワークフロー作成 次に.github/workflow/release-drafter.yml というワークフローを作成します。 name: Release Drafter on: schedule: - cron: '0 8 * * 2' #JST 17:00 on Tuesday pull_request: types: [opened, reopened, synchronize] jobs: update_release_draft: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: checkout uses: actions/checkout@v3 - name: Get latest release tag id: get_tag uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const repo = context.repo; const { data } = await github.rest.repos.getLatestRelease({ owner: repo.owner, repo: repo.repo, }); let tag = data.tag_name; console.log(data) const regex = /sprint(\d+)/; const match = tag.match(regex); if (match) { const sprintNumber = parseInt(match[1]); tag = sprintNumber + 1; } return tag; - name: Check release date id: check_date uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const repo = context.repo; const { data } = await github.rest.repos.getLatestRelease({ owner: repo.owner, repo: repo.repo, }); const releaseDate = new Date(data.published_at); const currentDate = new Date(); const diffTime = Math.abs(currentDate - releaseDate); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays >= 10; # リリース日から10日以上経過している場合はリリースを作成する - name: create release if: ${{ steps.check_date.outputs.result == 'true' }} uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: publish: true name: 'sprint${{ steps.get_tag.outputs.result }}' tag: 'sprint${{ steps.get_tag.outputs.result }}' # リリース日から2週間以上前の場合はリリースを作成しない - name: add label if: ${{ steps.check_date.outputs.result == 'false' }} uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: disable-releaser: true 処理内容について簡単に説明します。 スケジュールおよびプルリクエストトリガーの設定 on セクションで、このワークフローが2つのトリガーで起動するように設定されています。 schedule トリガーは、毎週火曜日のJST 17:00(UTC 8:00)に設定されています。 pull_request トリガーは、プルリクエストが開かれた、再オープンされた、または同期された場合に発生します。 最新リリースタグの取得 get_tag ステップは、github-scriptを使用して最新のリリースノートのタグを取得します。 タグ名は sprintX の形式で、X は現在のスプリント番号です。 取得したタグを次のスプリントのタグに更新します。 リリース日のチェック check_date ステップは、最新のリリースノートの公開日と現在の日付を比較して、その間の日数を計算します。 日数が10日以上経過している場合、後続のリリースノートを作成するための if 条件が満たされます。 リリースノートを作成した日付を基準に動くため、2週間スプリントなので14日と設定すると1日でもリリースノート作成がずれると正しく動かないので、余裕を持って10日と設定しています。 リリースノートの作成 create release ステップは、 release-drafter アクションを使用して、日数が10日以上経過している場合はリリースノートを作成し、公開します。 タグとリリースノート名は、前のステップで取得した次のスプリント番号を使用します。 ラベルの追加 add label ステップは、日数が10日未満の場合に実行され、最新のリリースノートを自動で作成することを制御します。 リリースノートの作成タイミングを変更する場合 scheduleのcronを実行したい日付に変更 GitHub ActionsのタイムゾーンはUTCなので、日本時間に合わせるため9時間プラスします。 check_date ステップの diffDays >= 10 を変更 1週間のスプリントならcronだけでいいので、 check_date と add label ステップを削除します。 動作確認 今回は火曜日の17:00に実行するようにcronを設定しています。 GItHubとslackを連携しているので、17時頃に実行者が github-actions[bot] でリリースノートとタグが自動的に作成されました。 まとめ 今回はRelase Drafterをチームの開発手法に合わせてカスタマイズしました。 github-scriptを組み合わせることによって、JavaScriptでGitHub APIが叩けるようになり、求めていた動作を実現することができました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
始めに こんにちは、ニフティ新卒1年目の高田と村山です。 現在はジョブローテーション期間中で、いろいろな部署を回っています。 現在は、情報システムチームに配属されており、社内向けシステムの開発や運用を行っています。その業務の一環として便利ツールを2人で作成したので、それについて話していきたいと思います!SlackBotを作ってAWSにデプロイしたい人はぜひ読んでみてください! 背景 ニフティでは出勤時に勤務場所と仕事内容をSlackに投稿し、Slackのステータスも勤務地に応じたものに変えるというルールがあります。また、退勤時にもSlackへの報告が必要です。これは、リモートワークによるハイブリッドな働き方により、社員全員がオフィスにいるわけではないので、どこで働いているか、今日のタスクはどうなっているかなどを共有する必要があるためです。 従来は、すべて手動で行わなければならなかったため、他の出勤時のタスクと合わせて行わなければならず、忘れがちだったり、作業の負担になっていたりしました。 そこで、我々新人はそんな状況を打破すべく、先に挙げたSlackへの投稿・ステータス変更を自動化するシステムである、「出退勤楽楽くん」の制作に取り組みました。 出退勤楽楽くんとは? 出退勤楽楽くんとは、以下のように、Slackの出退勤報告用チャンネルに報告用スレッドが毎日自動的に投稿され、 メッセージ内にあるボタンを押すだけでSlackへの出勤報告やSlackのステータス変更が行われるというシステムです。 これにより、先に挙げた報告やステータス変更をボタン一つで完了できるため、楽に出退勤時のタスクをこなすことができます。 また、社内で使用されているスケジュール管理システムのIDとPWを事前に登録しておくことで、 報告内容に当日の業務内容を自動的に含めることができます。 システム構成 アーキテクチャ 本システムは、対象がニフティの社員(数百人程度)&1日に一人当たり2回利用されることを想定した比較的小規模なツールです。ですので構成は、呼び出しが少ない場合に低コストで運用できるサーバレスとし、AWS Lambda上にデプロイを行いました。詳細なアーキテクチャは以下の通りです↓ デプロイにはServerless Frameworkを用いました。設定ファイルは以下の通りです(一部省略)↓ service: attendance-slack frameworkVersion: '3' provider: name: aws runtime: python3.10 region: ap-northeast-1 iam: role: statements: - Effect: Allow Action: - lambda:InvokeFunction - lambda:InvokeAsync Resource: "*" - Effect: Allow # ParameterStore関連 Action: - ssm:PutParameter - ssm:GetParameter Resource: "*" - Effect: Allow Action: - kms:Decrypt Resource: "*" - Effect: Allow # DynamoDB関連 Action: - dynamodb:UpdateItem - dynamodb:GetItem - dynamodb:DeleteItem Resource: "*" environment: TZ: Asia/Tokyo SERVERLESS_STAGE: ${opt:stage, 'dev'} package: patterns: - "!**" - "function/**" functions: app: # メイン関数 handler: function.slack_bolt_function.lambda_handler name: attendance-slack-${sls:stage} url: true maximumEventAge: 21600 maximumRetryAttempts: 0 post_attendance_message: # 出勤報告用メッセージ投稿関数 handler: function.post_attendance_message.lambda_handler name: attendance-slack-post-attendance-message-${sls:stage} maximumEventAge: 21600 maximumRetryAttempts: 0 events: - schedule: cron(0 22 ? * 1-5 *) # 平日朝7時(JST)に実行 post_leaving_message: # 退勤報告用メッセージ投稿関数 handler: function.post_leaving_message.lambda_handler name: attendance-slack-post-leaving-message-${sls:stage} maximumEventAge: 21600 maximumRetryAttempts: 0 events: - schedule: cron(50 2 ? * 2-6 *) # 平日朝11時50分(JST)に実行 manage_channels: # チャンネル管理用関数 handler: function.manage_channels.lambda_handler name: manage-channels-${sls:stage} url: true maximumEventAge: 21600 maximumRetryAttempts: 0 # 外部パッケージをLambda上で動かすプラグイン plugins: - serverless-python-requirements custom: pythonRequirements: zip: true slim: true useDownloadCache: false useStaticCache: false # DynamoDBのインスタンス作成 resources: Resources: ondemanddb: Type: 'AWS::DynamoDB::Table' Properties: TableName: user AttributeDefinitions: - AttributeName: slack_id AttributeType: S KeySchema: - AttributeName: slack_id KeyType: HASH # オンデマンドキャパシティモード BillingMode: PAY_PER_REQUEST Bolt for Pythonによるアプリ制作 出退勤楽楽くんはSlackの公式ライブラリである Bolt for Python を使って作られています。これによってユーザからのアクション(スラッシュコマンドやボタンクリックなど)と処理の紐づけを非常に簡単に実装できます。 メッセージ等アプリ内コンポーネントの成形には Block Kit Builder を使用しています。たとえば↑のメッセージブロックのうち、ワンクリック版は以下のようなソースでできています。 blocks = [ { "type": "section", "text": { "type": "mrkdwn", "text": ":syukkin: *おはようございます!*:syukkin:\nボタンを押して出勤報告しましょう!", }, }, {"type": "divider"}, {"type": "section", "text": {"type": "mrkdwn", "text": "*ワンクリック版*"}}, { "type": "actions", "block_id": "place_oneclick_block", "elements": [ { "type": "button", "text": {"type": "plain_text", "emoji": true, "text": "新宿17F"}, "value": "新宿17F", "action_id": "button_17f", }, { "type": "button", "text": {"type": "plain_text", "emoji": true, "text": "新宿18F"}, "value": "新宿18F", "action_id": "button_18f", }, { "type": "button", "text": {"type": "plain_text", "emoji": true, "text": "在宅"}, "value": "在宅", "action_id": "button_home", }, { "type": "button", "text": {"type": "plain_text", "emoji": true, "text": "横浜"}, "value": "横浜", "action_id": "button_yokohama", }, { "type": "button", "text": {"type": "plain_text", "emoji": true, "text": "その他"}, "value": "その他", "action_id": "button_others", }, ], }, {"type": "divider"}, # 詳細版も同様に... ] これをSlackの chat.postMessage APIにblocksとして渡してAPIを叩くことで指定のチャンネルに送ります。ボタンのアクションは以下のようにblock_idとaction_idを指定して受取り、値はaction[“value”]で受け取ります。また、bodyにリクエスト情報が一通り入っています。 def attendance_oneclick_button_clicked( action: dict, body: dict, client: WebClient ) -> None: # ユーザid取得 user_id = body["user"]["id"] schedule = "some schedule" # ここでスケジュール管理ツールから予定をとってくる schedule = "\n--やること--\n" + schedule msg = f"{body['user']['username']}が勤務を開始しました。\n場所: " + action["value"] + schedule # 出勤報告メッセージ投稿 client.chat_postMessage( channel=body["channel"]["id"], text=msg, thread_ts=body["message"]["ts"], username="出退勤楽楽くん", ) # ステータス変更 place = action["value"] if place == "新宿17F": status_text = "オフィスワーク中 17F" status_emoji = ":17f:" elif place == "新宿18F": status_text = "オフィスワーク中 18F" status_emoji = ":18f:" elif place == "在宅": status_text = "リモートワーク中" status_emoji = ":working-from-home:" elif place == "横浜": status_text = "オフィスワーク中 横浜" status_emoji = ":yokohama:" else: status_text = None status_emoji = None # 勤務場所が「その他」のとき以外は変更する if status_text and status_emoji: user_id = body["user"]["id"] client.users_profile_set( token=ps.get_parameter("SLACK_USER_TOKEN"), user=user_id, profile={ "status_text": status_text, "status_emoji": status_emoji, "status_expiration": 0, }, ) # Lazy Listener設定 app.action({"block_id": "place_oneclick_block", "action_id": re.compile("button_.+")})( ack=just_ack, lazy=[attendance_oneclick_button_clicked] ) Boltで構築したSlackアプリをLambda上で動かす場合はLazy Listenerの設定が必要です。これについては先輩が すでに記事を書いているので見てみてください 。なお、 users.profile.set APIを用いて他人のステータス変更を行う場合、その人の メンバー種別 より上位のメンバーが発行したユーザトークンが必要です。今回はプライマリーオーナー権限で払い出してもらっています。 使ってみてくれた人の感想 ニフティではSlackに個人の分報チャンネルを作成してつぶやきを発信する文化があります。そこに投稿されていた感想の一部をご紹介します。 好評ですね! 工夫した点 AWSにデプロイするにあたり、比較的安い構成を採用 規模を考慮した完全サーバレス構成 社内ツールであり、ユーザが多くて数百人程度なので、サーバは立てずにリクエストベースのLambda+DynamoDB構成にしました。 シークレットの管理をParameter Storeで行う ローテーション機能を要さないことなどから、Secrets ManagerでなくParameter Storeを使うことでコストを抑えました。 API GatewayでなくLambda Function URLsを利用 HTTPエンドポイントとしてLambda Function URLsを直接利用するようにし、認証等はSlack SDKに任せています。 開発効率を上げるためにIaCを組んだ 本プロジェクトでは、AWSを採用しているため、コンソールを使用した手動でのデプロイには時間や工数がかかります。 そのため、上記のようにServerless Frameworkを使用して、デプロイできるようにしました。また、Serverless FrameworkはTerraform等と比べて簡単に設定を書けるので、新人エンジニアがIaCを始めるきっかけとしては、とても良いのではないでしょうか。 苦労した点 AWS, SlackAPI初心者2人での設計・開発 私たちはどちらもAWSやSlackAPIを用いた開発の経験がなかったため、仕様の検討には時間がかかりました。 AWSでいえば、DBの選択や設計、シークレットの管理方法、その他必要なサービスの取捨選択などに苦労しました。わからない点は社内分報チャンネルでぼやいていると先輩方が教えてくれたり、あとはChatGPTに相談したりして決めていきました。GPT4は最強でした。 (余談ですが、弊社では上記のようにSlackでいつでも質問可能なAIが在中しています。詳細は こちら ) またSlackAPIでは、Tokenの権限、Tier(1分当たりの呼び出し可能回数)やインタラクションの実装方法(ショートカットやスラッシュコマンドなど)の検討に悩まされました。 ジョブローテによる開発期間の制約 9月下旬には他の部署へ移るため、開発はもちろん、企画や仕様の決定、諸々の調整などを含めて3か月で開発しなくてはなりませんでした。また先述の通り私たちには技術を調べる時間も必要だったため、うまくことを進めていくのが大変でしたが、先輩に頼ったり、IaCによる効率化などにより何とか乗り切りました。 まとめ 今回は出退勤時のタスクを自動化するSlackアプリを作成しました。 今回、ふたりともAWSやBoltを使用した開発は初めてでしたが、3か月という短い期間の中でひとつ形にできたのでよかったです。先ほど紹介した感想を含め、チームの方々がみんな楽になったと言ってくれてうれしかったです。 もしみなさんにも、こういったSlackでの毎日のタスクがあるなら、私たちのように自動化を考えてみてはいかがでしょうか。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに 基幹システムグループの湊谷です。 普段は主にニフティのお客様の情報を管理するシステムの開発・運用を行っています。 「◯◯の情報を取得するにはどのAPIを利用すれば良いか?」といったシステム目線のもの、「◯◯といった属性を持つお客様にメールを送りたいため、IDリストを抽出してほしい」といった企画・営業目線のもの、日々様々な問い合わせが来ます。 今年でこのシステムに携わり始めて4年目になりますが、まだまだ把握しきれていないことも多く、問い合わせを対応する時は都度調べることが欠かせません。 その中でも、「過去の類似の問い合わせを見つけ出し、その時の調査内容・回答から今回の回答のヒントを得る」ことが多いです。 しかし、問い合わせはSlackチャンネルで受け付けているのですが、数多い問い合わせの中検索して必要な情報を探し出すのは大変・・・ しかも、調査段階で右往左往してる・・・ どこかにナレッジとしてちゃんとまとめておきたい!でも手動でやりたくない!  ということで今回は、「Slackチャンネルで問い合わせが投稿されると、Notion DBに新規ページを作成し、その作成されたページがSlackチャンネルに投稿される」ことを実現します。 ちなみに、過去に Slack ワークフロー+Boltで依頼・受付の調整をスッキリさせよう! という記事も書いているので、よろしければご覧ください。 Slack BoltとZapierを使ってNotionと連携させる やることは大きく分けて以下の五つです。 Slack Bolt for Javascriptを使ってbotを作成する 問い合わせを受け付けるSlackワークフローを作成する Notionにデータベースを用意する ZapierでNotion データベースに新規ページを作成し、Slackに通知するワークフローを作成する botの実装を行う それでは一つずつ見ていきます。 Slack Bolt for Javascriptを使ってbotを作成する 早速上記の過去の記事と同様になりますが、まず最初に、Boltを使用して、チャンネルに投稿されたメッセージを受け取り応答するbotを用意しておきます。 Slackアプリの構築の仕方については、 slack apiのドキュメント や Bolt 入門ガイド に詳細があるため省略します。 問い合わせを受け付けるSlackワークフローを作成する 私たちのシステムでは、問い合わせを受けつけるチャンネル(以下、 QAチャンネル )と、システム担当者が調査・やり取りを行うチャンネル(以下、 内部用チャンネル )の二つを用意し、QAチャンネルにSlackワークフローを作成して問い合わせを受け付けています。 ※今回は、2023年6月に提供開始された 新バージョンのSlack ワークフロー ではなく、レガシー版のワークフローを用いて作成しました。 まずはワークフローを開始したチャンネル=QAチャンネルに、問い合わせの内容をそのまま投稿します。 今回の実装とは関係ありませんが、QAチャンネルに投稿されたメッセージのスレッドに、以下の様な返信を行い、システム担当者が確実に気付けるようにしています。 次に、内部用チャンネルにも投稿を行います。 基本的には問い合わせを受け付けたチャンネルに投稿した内容と同じものを投稿しますが、大切なのは一番下の部分です。 先ほど作成したSlack botへのメンションと、「Notion DBにコピーしてください」という文言を一緒に投稿しています。 後ほどbotの実装を行う際に、「アプリにメンションが飛んだ時、更に特定の文字を含んでいる」場合をトリガーとする実装を行うため、忘れずにつけておきます。 Notionにデータベースを用意する Notionに問い合わせをまとめるページを作成します。 データベースを選択してページを新規作成します。 使いやすいようにプロパティを編集しましょう。今回は「進捗」「Slackスレッド」「担当者」の三つのプロパティを持たせています。 ZapierでNotion データベースに新規ページを作成し、Slackに通知するワークフローを作成する Zapierとは、複数のアプリやサービスを連携してワークフローを作成することができるサービスです。 今回は「NotionDBにページを作成し、そのページをSlackに通知する」ワークフローを作成します。 ※Notionにページを作成しSlackに通知させる、といった複数のステップを組み合わせたワークフローの作成や、今回トリガーに使っているものは有料プランのみ可能です。 まず、「何をきっかけにして作成したワークフローを発火させるか」となるトリガーを選択します。 「Webhooks by Zapier」を選び、Eventは「Catch Hook」を選択します。 「Trigger」は特に指定せず次に進みます。 「Test」に進むと「Your webhook URL」が表示されます。 このURLを呼び出すと叩くと、現在作成しているワークフローを発火させることができます。 パラメータに、「Notion DBに作成するページのタイトル(title)・本文(detail)」と、「投稿するSlackのスレッド(ts)」の情報を持たせます。指定する値はここでは適当で問題ありません。 https://hooks.zapier.com/hooks/catch/XXXXXXX/XXXXXX/?title=test&detail=korehatestdesu&ts=hogehoge このURLを一度ブラウザで叩いてみましょう。 “status”: “success” となればOKです。 次にNotion DBにページを作成します。Add a stepでNotionを選択し、Eventには「Create Database Item」を指定します。 Accountでは使用する自身のアカウントを連携させます。 連携させる際に、先ほど作成したページを指定しておきます。 Actionで、作成するNotion DBのページの設定をしていきます。 Database  作成したページを指定 名前  ページのタイトル。パラメータに指定したtitleを選択 slackスレッド  <内部用チャンネルのURL>+/p+パラメータに指定したtsを選択 Content  ページの本文。お好みの文章と、パラメータに指定したdetailを選択 Content Format  マークダウン式で書く場合にはMarkdownを選択 これでテストをすると、先ほど作成したNotionページに新しくページが追加されていることを確認できました。 最後に、Slackに通知をする設定をします。 Zapierで設定を行う前に、Notionのページを通知させたいチャンネルに適当に投稿します。 今回は架空の問い合わせを投稿してみました。 リンクをコピーし、URLからメッセージのをタイムスタンプを確認します。 https://hoge.slack.com/archives/XXXXXXX/p1234567890123456 URLの一番最後、pの後の16桁の数字で、右から6つ目と7つ目の間にピリオドを打ったものがタイムスタンプです。 1234567890.123456 Add a stepでSlackを選択し、Eventには「Send Channel Message」を指定します。 Accountでは使用する自身のアカウントを連携させます。 Actionで、Slackにメッセージを送る際の設定をしていきます。他の項目はお好みで設定しましょう。 Channel 内部用チャンネルを選択 Message Text お好みの文章と作成したページのURLを選択 Thread パラメータに指定したtsを選択 これでテストすると、Slackに作成されたNotionのページ付きで投稿がされました! これでZapierのワークフローは完成なので、Publishしておきます。 Slack botの実装を行う やっていることは、「slackアプリに対してメンションが飛ばされた時、特定の文章を含んでいたら、ZapierのURLを叩く」です。 アプリの起動部分などは省略しています。 // Slackに投稿された文章の中から問い合わせの本文を抽出する const getElementsBetween = ( array: Array<string>, startFlag: string, endFlag: string, ): string[] => { let outputArray: Array<string> = []; let startCollecting = false; for (let i = 0; i < array.length; i++) { // startFlgが見つかったら、startCollectingをtrueにする // startFlgの要素は配列に入れないためcontinue if (array[i] === startFlag) { startCollecting = true; continue; } // endFlgが見つかったら、startCollectingをfalseにする if (array[i] === endFlag) { startCollecting = false; } // startFlgとendFlgの間の要素を配列に入れる if (startCollecting) { outputArray.push(array[i]); } } return outputArray; }; // Zapier URLを呼び出す const createQaDb = (title: string, detail: string, ts: string) => { const uri = `https://hooks.zapier.com/hooks/catch/XXXXXX/XXXXXX/?title=${title}&detail=${detail}&ts=${ts}`; const encoded = encodeURI(uri); return fetch(encoded) .then((res) => { console.log('問い合わせDB作成'); }) .catch((error) => { console.error(`問い合わせDB作成失敗:${error}`); }); }; // botの処理 // アプリにメンションが飛んだ時に実行 app.event('app_mention', async ({ body }) => {  // もし投稿の中に「Notion DBにコピーしてください」という文言が含まれていたら if (body['event']['text'].includes('Notion DBにコピーしてください')) {   // bodyのメッセージから必要な情報を取得 const text = body['event']['text']; const textArray = text.split('\n'); const title = textArray[3]; const detailArray = getElementsBetween( textArray, '■問い合わせ詳細', '■いつまでに回答が必要ですか?', ); const detail = detailArray.join('\n');   // 本文、タイトル、タイムスタンプ情報を持たせてZapier URLを呼び出す createQaDb(title, detail, body['event']['ts']); } }); これで チャンネルに問い合わせが投稿されると 内部用チャンネルに内容が転送され その情報を基にNotion DBに新しくページが作成され そのページが内部用チャンネルのスレッドに通知される ようになりました! 最後に 以前の記事に引き続き 、Slackワークフロー+Boltを使って作成したbotアプリを組み合わせた活用例を紹介しました。 今回はZapierを活用することで、SlackだけではなくNotionとの連携も簡単に行うことができました。 SlackワークフローもZapierも、完全にノーコードで様々な作業を自動化させることが可能です。 特にSlackワークフローは 2023年6月に新バージョンの提供が開始されたそうです。 まだ新バージョンの方は触れていないため、何か業務に活用できないか模索していきたいと思います。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
基幹システムグループ N1! オートメーションスペシャリストの南川です。 今回は、 Docker イメージがビルドできるか定期的にチェックし、失敗したら Slack に通知する GitHub Actions のワークフローを紹介します。 背景 Docker のベースイメージの OS や使用しているパッケージのバージョンアップにより、何もしていないのに Docker イメージのビルドが失敗するようになることがあるかと思います。また、ビルドに失敗した場合、その原因が機能追加や修正によるものなのか、自分の環境によるものなのか分からないといった問題もあります。 こうした背景から、以下のような GitHub Actions のワークフローを作成することにしました。 定期的 (平日 8:30) に Docker イメージのビルドが成功するかチェックする。 ビルドに失敗したら Slack のチャンネルにアラートを通知する。 もし、 Slack チャンネルにアラートが通知されていたら、担当者はワークフローの実行ログを確認し、 GitHub で Issue を作成して対応します。 このワークフローにより、ベースイメージやパッケージの更新による Docker イメージのビルド失敗の検知が迅速化されるほか、ビルド失敗の原因の調査にも役立つことが期待されます。 手順 前提として、今回は Docker Compose を使っているリポジトリに対して実装していきます。 (1) Slack Incoming Webhook を作成 以下のページを参考に Slack Incoming Webhook を作成します。 https://api.slack.com/messaging/webhooks 作成したら Webhook URL ( https://hooks.slack.com/services/T.../B.../... ) をメモします。 (2) Webhook URL をリポジトリシークレットに追加 GitHub のリポジトリのページを開きます。 リポジトリのメニューから「 Settings 」を選択します。 左メニューから「 Secrets and variables > Actions 」を選択します。 「 New repository secret 」ボタンを押す。 以下のように入力し、「 Add secret 」ボタンを押す。 Name : ALERT_SLACK_WEBHOOK_URL Secret : 手順 (1) でメモした Webhook URL 参考 : https://docs.github.com/ja/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository (3) GitHub Actions ワークフローファイルを作成 ブランチを切り、 .github/workflows/ 配下に以下のファイル ( check_docker_build.yml ) を作成します。 name: check docker build on: schedule: - cron: '30 23 * * 0,1,2,3,4' workflow_dispatch: jobs: check_build: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 - name: define run url run: echo "ACTION_URL=https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> $GITHUB_ENV - name: docker-compose build run: docker-compose build - name: slack notification if: ${{ failure() }} uses: slackapi/slack-github-action@v1.24.0 with: payload: | { "text": ":github: Dockerイメージのビルド失敗 :docker:\n失敗の原因を調査し、必要に応じてhotfixのIssueを作成し、対応してください。", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": ":github: Dockerイメージのビルド失敗 :docker:" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "失敗の原因を調査し、必要に応じてhotfixのIssueを作成し、対応してください。" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "${{ env.ACTION_URL }}" } } ] } env: SLACK_WEBHOOK_URL: ${{ secrets.ALERT_SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK この時のディレクトリ構成 (関係のあるファイルのみ) は以下のようになります。 |-- .github | `-- workflows | `-- check_docker_build.yml |-- Dockerfile `-- docker-compose.yml 解説 name: check docker build リポジトリの Actions タブで表示されるワークフロー名を指定しています。 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#name ここで指定した名前は、 Actions タブの実行履歴で表示されます。 on: schedule: - cron: '30 23 * * 0,1,2,3,4' workflow_dispatch: このワークフローを実行するトリガー (条件) をここで指定しています。 schedule https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule cron 式で指定した日時に実行するようにする。 UTC で指定する必要があるので注意。 (例) 月曜日 (曜日 = 1) 8:30 を指定する場合は、 UTC に変換すると、日曜日 (曜日 = 0) 23:30 になるので、「 30 23 * * 0 」となります。 今回は平日 (月曜~金曜) の 8:30 に実行するようにしています。 workflow_dispatch https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch ワークフローを手動で実行できるようにします。 https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow 今回はワークフローの動作確認のために使います。 - name: checkout uses: actions/checkout@v3 リポジトリにチェックアウトし、ワークフローでアクセスできるようにします。 https://github.com/marketplace/actions/checkout - name: define run url run: echo "ACTION_URL=https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> $GITHUB_ENV 環境変数 ACTION_URL を定義し、環境ファイル $GITHUB_ENV に書き込みます。 ワークフローの実行結果のURLを環境変数 ACTION_URL の値として定義しています。 ${GITHUB_REPOSITORY} , ${GITHUB_RUN_ID} はそれぞれ GitHub が設定済みの既定の環境変数。 ${GITHUB_REPOSITORY} : 所有者およびリポジトリ名。 ${GITHUB_RUN_ID} : リポジトリ内の各ワークフローの実行に割り振られたユニークな番号。 https://docs.github.com/ja/actions/learn-github-actions/variables#default-environment-variables $GITHUB_ENV はワークフローコマンドから変数を設定するファイルへのランナー上のパス。 https://docs.github.com/ja/actions/learn-github-actions/variables#default-environment-variables これによって、後続のステップで環境変数 ACTION_URL の値を使うことができます。 https://docs.github.com/ja/actions/using-workflows/workflow-commands-for-github-actions#environment-files - name: docker-compose build run: docker-compose build Docker イメージをビルドするコマンド。 必要に応じて 「–build-arg」 でオプションを指定します。 - name: slack notification if: ${{ failure() }} 前のステップ (Docker イメージのビルド)が失敗した場合に、このステップを実行(Slackにメッセージを投稿)するようにします。 https://docs.github.com/ja/actions/learn-github-actions/expressions#failure uses: slackapi/slack-github-action@v1.24.0 with: payload: | { "text": ":github: Dockerイメージのビルド失敗 :docker:\\n失敗の原因を調査し、必要に応じてhotfixのIssueを作成し、対応してください。", "blocks": [ ... { "type": "section", "text": { "type": "mrkdwn", "text": "${{ env.ACTION_URL }}" } } ] } env: SLACK_WEBHOOK_URL: ${{ secrets.ALERT_SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK Slack Incoming Webhook を用いて、メッセージを Slack チャンネルに投稿します。 https://github.com/marketplace/actions/slack-send payload には Slack チャンネルに投稿するメッセージを記載します。 text https://api.slack.com/methods/chat.postMessage#arg_text blocks が指定されていない場合、チャンネルに投稿されるメッセージ。 blocks が指定されている場合、 Slack で通知されるときのメッセージ。 block https://api.slack.com/methods/chat.postMessage#arg_blocks JSON で記述された構造化されたメッセージ。 詳しくはこちらを参照してください。 https://api.slack.com/messaging/composing/layouts 先ほど定義した環境変数 ACTION_URL の値を使っています。 「 "text": "${{ env.ACTION_URL }}" 」 https://docs.github.com/ja/actions/learn-github-actions/variables#using-contexts-to-access-variable-values https://docs.github.com/ja/actions/learn-github-actions/contexts#env-context 環境変数 SLACK_WEBHOOK_URL に、手順(2)で設定したリポジトリシークレットの値を指定しています。 https://docs.github.com/ja/actions/security-guides/encrypted-secrets#using-encrypted-secrets-in-a-workflow (4) 動作確認 ビルドを失敗するように Dockerfile を書き換えてコミットし、 push します。 リポジトリの「Settings」タブを選択します。 Default branch をこのワークフローを開発しているブランチに切り替えます。 リポジトリの 「Actions」 タブを選択します。 左メニューから 「check docker build」 を選択します。 参考 : https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow#running-a-workflow 「Run workflow」 のプルダウンを開き、現在開発しているブランチを指定して、緑色の 「Run workflow」 ボタンを押します しばらくすると、チャンネルにビルド失敗のメッセージが投稿されます。 Botのアイコンがお気に入りです。 https://www.irasutoya.com/2016/11/blog-post_684.html 平日の8:30頃にビルド失敗通知に投稿されれば OK。 ビルド失敗するようにしたコミットを revert します。 平日の8:30頃にビルド失敗通知に投稿されなければ OK。 Actionsタブから実行されている(Dockerイメージのビルド成功している)ことを確認します。 土日の8:30頃にワークフローが実行されていなければOK。 Actionsタブから実行されていないことを確認します。 おわりに 今回は、GitHub Actions で定期的に Docker イメージをビルドできるかチェックし、ビルドできない場合は Slack にメッセージを投稿するワークフローについて説明しました。 Docker イメージのビルドチェック以外にも色々応用できるので、是非活用してみてください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは。ニフティ株式会社の宮永です。4月に新卒で入社しました。現在はインフラシステムグループ ISPオペレーションサブチームに所属しています。 7月9日に電気通信主任技術者試験(伝送交換)を受験し、合格しました。受験者が少ないためかネットに体験談を載せている人も少なく、今後受ける人の役に立つかもしれませんので、自分の経験をお話しします。 電気通信主任技術者とは そもそも「電気通信主任技術者」とは何でしょうか。 電気通信主任技術者は、電気通信ネットワークの工事、維持及び運用の監督責任者です。 電気通信事業者は、その事業用電気通信設備を、総務省令で定める技術基準に適合するよう、自主的に維持するために、電気通信主任技術者を選任し、電気通信設備の工事、維持及び運用の監督にあたらなければなりません。 ( 日本データ通信協会HP より引用) 電気通信主任技術者は、(原則として)「電気通信主任技術者資格者証」を持っている人から選任します。この「電気通信主任技術者資格者証」を得るための試験が「電気通信主任技術者試験」です。 電気通信主任技術者試験には 伝送交換主任技術者試験 線路主任技術者試験 の2種類あり、「伝送交換主任技術者試験」を受験しました。 なお、一定規模以上の電気通信事業者は有資格者を配置する必要があり、当社も該当します。 試験内容 試験科目は以下の3科目です。3科目それぞれ6割以上で合格です。 電気通信システム 20問 80分 電磁気学や電気工学の問題や、電気通信の基礎問題が問われます。 高校物理や符号理論、TCP/IPなど通信の基本が絡むため比較的理解しやすかったです。 伝送交換設備及び設備管理 大問9問 150分 伝送交換設備の概要や運用に加え、セキュリティやソフトウェア管理について問われます。 実務の経験が無いと聞いたことが無い用語が多く、苦戦しました。 法規 大問5問 80分 電気通信事業法をはじめとする関連法規について問われます。 いわゆる理論的な話ではなく暗記が物を言うため、苦戦しました。 科目合格制度もあるので、2科目合格して次回の試験で1科目合格する……という方法もあります。 なお、「工事担任者」などの資格を持っていると一部科目で免除があります。私は該当する資格を持っていなかったので、3科目受験しました。 勉強方法 基本的には過去問周回です。試験では過去問と同じor非常に似た問題が多く出題されるので、「見たことがある問題を可能な限り多く作り、絶対に落とさない」という戦法で挑みました。6割合格なので、この戦法で合格できると信じました。 勉強をするにあたり色々なテキストを買いたくなりますが、あれこれ買うと高くつくので、「自分はこれを完ぺきにするんだ!」というものを決めて買いました。どんな勉強でも反復学習が大事なので、決めたものをひたすら繰り返します。 試験はすべて選択マーク式です。「以下から正しいものを選びなさい」という問題がある際は、他の選択肢がなぜ間違っているのか、どこを直せば正しくなるのかを説明できるようにすることを心掛けました。 総勉強時間は約60時間でした。うち伝送交換設備及び設備管理に約30時間かけているので苦戦したことが分かります…… 試験当日 試験は日曜日の午前から始まります。年に2回しかない試験なので、寝坊すると次のチャンスは半年後になってしまいます。(受験料も2万円弱するのでもったいない) 受験会場に向かう際は若い人や女性の方もたくさんいたのですが、ほとんどが同じ場所で行っている英検の受験会場に吸い込まれていきました。 電気通信主任技術者試験の会場には200人ほどがおり、ほとんどが40~50代の男性に見えました。休日のおじ様たちがラフな格好で来ているのが大半でした。おそらく自分が会場内で一番若かったです。 各科目の試験時間は長いですが大半の方が途中退出します。お昼ご飯の時間も十分にあるので、時間が余りがちでした。空いた時間に最後のつめこみができるように、勉強道具を持っていくのがおすすめです。 腕時計を持ち込みする際、 アナログ式時計(液晶表示のあるものは認めない) という独特なルールがあるので注意です。日付表示だけ液晶表示といった時計も禁止です。隣の席の人が監督員に注意されていました。また、腕時計は外して机に置くよう指示されます。自立しないタイプの腕時計だと厳しいかもしれません。 ティッシュはカバンにしまい、使うときに監督員を呼ぶように指示されました。毎日ティッシュが欠かせない私としてはこれが一番大変でした。試験時間中に何度監督員を呼んだことか…… 問題冊子は持ち帰るように言われます。自己採点のために自分の回答を問題冊子にメモしておくと良いです。 結果発表 試験の数日後に正答がwebで公開されます。結果発表が待ちきれず自己採点しました。3科目とも7~8割で合格点に達していそうで一安心です。とはいえ結果発表までは安心できません。 試験から3週間後に結果発表があります。web上で合否を確認します。 無事に合格しました。勉強した成果が出てよかったです。 これで現状の社内で最年少の合格者になれました。 資格者証申請 まだ安心できません。試験に合格した人は申請して「電気通信主任技術者資格者証」の交付を受けなければいけません。管轄の総合通信局へ申請書を提出します。 顔写真、収入印紙、住民票の写し等を用意します。この資格者証、運転免許証などと異なり 有効期限が無い ので残念な写真は使わない方が良いです。一生残念な写真の資格者証と共に過ごすことになります。 申請から約1か月で資格者証が届きました。これで堂々と有資格者と名乗ることができます。 資格手当 先述の通り有資格者が社内で必要であるため、資格を取ると補助が会社から出ます。初任給に+で補助が出るのは大きいです。 おわりに 入社してすぐの環境に慣れない中でしたが、受験し合格することができてよかったです。 電気通信主任技術者に興味があるかたは是非受験してみてください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass カジュアル面談も受け付けています! ニフティ株式会社 – Meety
アバター
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! TechTalk#14は「GitHub Copilotの導入や活用」です。 どれほどのものを開発業務にもたらしてくれるのか、その便利さとは。 使ってみたいけどなにが出来るのかよくわからないなどなど気になる方は是非ご視聴ください! 概要 日程:9月26日(火)12:00〜13:00 配信方法:YouTube Live 視聴環境:インターネット接続が可能なPC/スマートフォン 参加方法 connpass にて登録をお願いいたします。 YouTube Liveにて配信いたします。 ※YouTube LiveのURLは決定後、参加者への情報欄に記載いたします。 こんな方におすすめ GitHub Copilotに興味がある方 導入を検討している方 タイムテーブル 時間 コンテンツ 12:00 – 12:10 オープニング+会社紹介 12:10 – 12:25 LT1: GitHub Copilotの導入と開発生産性への効果 12:25 – 12:40 LT2: クリーンアーキテクチャとTDDの中でCopilotを使ってみた! 12:40 – 12:55 LT3: Copilotを使って爆速コーディングする 12:55 – 13:00 まとめ+クロージング テーマ GitHub Copilotの導入や活用 登壇者プロフィール 石川 貴之(登壇者 ) ニフティ株式会社 会員システムグループ 第1開発チーム AWS/GCPの組織管理、Notion/GitHub等のSaaS管理、自社Webサービス基盤の担当をしています。 たけろいど(登壇者) ニフティ株式会社 会員システムグループ 第2開発チーム Copilotを使って開発を行うWEBアプリケーションエンジニアです。 普段はPythonやSvelteのコーディング支援をしてもらっています。 いかりがわ(登壇者) ニフティ株式会社 会員システムグループ 第1開発チーム 新卒3年目。ニフティのポータルサイトの開発運用を担当しています。 普段はNext.jsやGoをCopilotさんと一緒に書いてます。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
はじめに こんにちは。会員システムグループの上原です。 8月末に社内でISUCONを開催・運営したので、レポーティングしていこうと思っています。 ISUCONとは? ISUCON ( Iikanjini Speed Up Contest) は、限られたリソース・時間の中でWebサービスをどれだけ高速にできたかを競うコンテストです。 ISUCONで参加者は動作が遅いWebアプリケーションを与えられ、アプリケーションを高速化していくために設定を最適化したり、コードを改善したりする必要があります。 チーム間でスコアを競い合いながら、高速化していけるところがISUCONの醍醐味です。 ISUCONを通じて、モニタリングやデータベースのチューニングなどのスキルを向上させることができます。 ちなみに、優勝すると賞金100万円をゲットできます! (※「ISUCON」は、LINE株式会社の商標または登録商標です。 https://isucon.net ) 今回のイベント 本家ISUCONと同じく、webアプリケーションをどれくらいチューニングできるかチームで競っていくぜ…と行きたかったのですが、パフォーマンスチューニングって何?という人も多い状況でいきなり競技形式で行うのは負担が大きいと思われたので、座学とハンズオン形式を組み合わせたワークショップを行いました。 また、より多くのエンジニアに参加してもらうため、デフォルトでエンジニア全員を参加対象とし、欠席者は個別に連絡してもらうオプトアウト形式を取ることにしました。 そのおかげからか94名の社内のエンジニアの皆さんに参加いただくことができました! タイムスケジュール 以下のように前半1時間は座学、後半3時間はハンズオン形式とし、講師と一緒にパフォーマンスチューニングを学べるようにしました。 座学(1時間) 高速なWebサービスの意義 チューニングの基礎 モニタリングの基礎 ハンズオン(3時間) ワークロードの分析&ボトルネックの発見 WEBサーバのログからレイテンシを調べる スロークエリを探す データベースのチューニング SQLの実行計画の分析 N+1問題の解決 Webサーバのチューニング なお、内容は以下の書籍を参考にさせていただきました。著者の方々に感謝申し上げます。 達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践 座学 高速なwebサービスとは何か?ハンズオンをやる上であらかじめ知っておくべき解説をやっていきました。 (内容紹介は省略…) ハンズオン 題材 今回使用する題材はpixiv株式会社の社内ISUCONで使用されたprivate-isuを使用しました。 https://github.com/catatsuy/private-isu このアプリケーションはisucogramという仮想の写真共有SNSで、写真投稿機能やコメント機能など簡単な機能が実装されています。 今回は、参加者全員にアプリケーションサーバー1台とベンチマークサーバー1台を提供しました。 本家ISUCONでは序盤はチューニングをいきなり行うのではなく、必要なツールの導入・設定などを行う必要がありますが、時間が限られていることもありサーバー設定、ツール導入、サーバー立ち上げなど必要な準備は運営側で全て行いました。 ということで、参加者がサーバーにsshできればすぐにパフォーマンスチューニングを楽しめる状態にしました。 また、sshでサーバー接続がうまくできないことも見越して、AWSのセッションマネージャー経由でサーバーにログインできるよう設定を仕込んだり、言語が障壁とならないように、社内でもっとも読み書きできる人間の多い python に実装を変更したりしました。 その際以下のレポジトリを参考にしました。作者の方には感謝申し上げます。 https://github.com/methane/pixiv-isucon2016-python 負荷試験を行う 早速、ベンチマークを走らせ負荷試験を行っていきます。アプリケーションに残ったログにalpコマンドやpt-query-digestコマンドを噛ませて、ボトルネックをどのように見つけていくか体験してもらいました。 この段階ではまだ0点です。 初めてのチューニング htopでCPUコアが一つしか使われていないことがわかったので、設定を変更してCPUコアをフルで使えるようにしました。 1000点くらいまで伸びました。 データベースチューニング pt-query-digestでスロークエリを特定、EXPLAINで実行計画を分析したのち、適切なインデックスを貼っていってもらいました。これだけでもスコアは15000点くらいまで伸びます。 スコアがいきなり1万点台に到達したので驚いた人もいたようです。 ほかにもN+1問題の解消などにも取り組んでもらいました。 リバースプロキシ データベースに画像が保存されている問題があるため、アプリケーションコードとリバースプロキシの設定をいじって、ファイルから画像を参照するような修正を行いました。 また、gzip配信やブラウザキャッシュなどにも取り組み、最終的には7万点くらいになりました。 ボトルネックに集中して消化することは大事 パフォーマンスチューニングはボトルネックに集中して解消しなければならないと言われます。 インデックスを消すだけで、スコアは一気に1000点台まで下がるため、ボトルネックを解消することの大切さを認識できたのではないかなと思います。 感想 イベント中、slackチャンネルも盛り上がりわいわいとした雰囲気でできたので良かったです。 参加者からのアンケートの結果、ほとんどの方から好意的な感想をいただくことができました。 理由としては、パフォーマンスチューニングの結果がスコアとして数字に出てくるのが良かったようです。 具体的な数値としてスコア改善が見られるのは面白かった。 クエリチューニングでどれくらい性能差が出るのか実感できて面白かった。 一方で4時間という短い時間に内容を詰め込んでしまったため、内容についていけないという方もいたようです。 業務に影響がないように短い時間内に収めるのはなかなか難しいですが、1日目は座学、2日目はハンズオンなど日程を分けて行うのもいいのかなと思いました。 ただ運営としては、半年間準備を行いオプトアウト形式で実施したため絶対に失敗できない…!というプレッシャーがあったのですが、参加者から学びが多かったという声が多く、苦労が報われて良かったです。 最後に 今回はワークショップ形式で行いましたが、次回は本家ISUCONと同じくチーム競技でやっていきたいと思っています。 本家ISUCONの方には今回の運営メンバーでチームを組み挑んでいきたいと思いますので、ISUCONに参加される方はよろしくお願いいたします! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター