QUO CARD Digital Innovation Lab Tech Blog

クオカード デジタルイノベーションラボの技術ブログです

スクラムマスター研修を受講してきました

デジタルイノベーションラボPayチームのゴルゴ&鳥海です。 先日、スクラムマスター研修を受講してきたので、その感想をお届けします。

受講の背景ですが、私達は3年前からスクラム開発を導入していて複数の開発チームが有ります(Payチームもその一つ)が、専任のスクラムマスターはいません。そこでプロダクトオーナーからの提案で、スクラム開発への理解を深めるためにスクラムマスター研修を受講することにしました。

https://www.jp.agilergo.com/online-csm-coplien-202303

研修は4日間で、講師のJames.O.Coplien氏はデンマークからのリモート授業。同時通訳で理解できるか多少心配だったのですが、実際受けてみたところ、あまり問題はありませんでした。 講義の内容はスクラムガイドの細かい解説や、スクラム開発に必要な心構えなどが中心で、時折演習も交えて行われました。以下が印象に残った点のメモです。

  • コミットメントの意味 - 約束や確約と捉えられがちだが、チームがゴール達成に向けて最善を尽くすという気構え。想定と異なり満たせないことは当然ある。その場合は検査と適応で学習していく必要がある。

  • 適切なリファインメントの量 - 2〜3スプリント分がすぐに取り組める状態にまでリファインメントが終わっていると良い。先のタスクをあまり詳細に詰めても無駄になる。リファインメントが不足していると、予想より早く計画したタスクを終えた場合に、次に取り掛かる作業が不足して無駄になる。

  • PBIのタスク化は必要な無駄 - PBIのタスク化は必要だが無駄な作業。短時間で終わらせて実際の開発を行う時間を確保することが重要。
  • デイリースクラム - 目的は課題の見える化。課題について話し込まずに短時間で終わらせる。課題への対応は別途時間をとって行う。
  • スクラム的に正しいかを問うスクラム警察は必要無い」。現場の状況に応じて改善を続けていくことが大切。
  • スクラムマスターに適した人 - よく話を聞く人、信頼関係を作る、円滑なコミュニケーションを確立する、対立が発生した場合は仲介をする、タフになる必要がある。オープンになる必要がある。オープンというのは正直ということでもある。
  • ベロシティ - ベロシティを高めることに意味はない、あくまで指標と考えたほうが良い。ベロシティを調整する、というのはよくない考え方。ベロシティは計測するのみ。フィーチャー工場にならないように!それよりも何が価値のあることなのかを分かっていることが重要。
  • スプリントゴール - スプリントゴールは一つに絞るほうが、チーム全体としてのまとまりが出る。私たちのゴールは何だろうか、ということをもっとみんなで言語化してみるとよいかもしれない。
  • スクラムチームの信頼 - スクラムチームは日頃スプリントゴールを満たしていくことでステークホルダーからの信頼を積み重ねていく必要がある。この信頼は超大事。

全般的に、従来からの理解とは大きなズレはなかったものの、普段スクラム開発に取り組む中でぼんやりと疑問に感じていた点がいろいろ腹落ちし、得るものが多かった研修でした。今後自信を持って開発に取り組んでいけそうです。

また、研修の仕上げとして後日スクラムマスター認定試験を受験する必要があります。合格率はかなり高いようなので逆にそれがプレッシャーですが、しっかりと研修内容を復習して臨みたいと思います。

以上、スクラムマスター研修の感想でした。今後もよりよい開発を行えるよう、日々改善を重ねていきたいと思います。

Four Keysの取得をはじめました

少し前からFour Keysの取得をはじめました。今回はそれについて書きます。

背景

Four KeysとはLeanとDevOpsの科学でも紹介されていますが、GoogleのDevOps Research and Assessment (DORA) チームが確立したソフトウェア開発チームのパフォーマンスを示す指標です。

https://cloud.google.com/blog/ja/products/gcp/using-the-four-keys-to-measure-your-devops-performance?hl=ja

クオカードでも開発生産性の向上は一つの重要なテーマですが、現状を定量的に把握しないことには改善しているのかどうかの判断ができないため、Four Keysを取得する事にしました。実はかなり以前から取得しないといけないという意識はあったのですが、手が回っておらずこのタイミングになってしまったという状況です。

今回の対応

計測にはSwarmiaというツールを利用しています。 結果は各チーム単位にSwarmiaの管理画面で各指標の値を確認し、スプレッドシートに書き込むという運用をしています。 可視化はダッシュボード化も検討しましたが、他に優先度が高いバックログがあるため、まずは大きな工数を掛けずに始めることにしました。

実際の計測について

4つの指標のうち、変更のリードタイム とその他(デプロイの頻度、変更障害率、サービス復元時間)でSwarmia上での確認方法や必要な設定が異なったので、それぞれどのようにして確認できるようにしたか記載します。

変更のリードタイム

変更のリードタイムは、SwarmiaとGithub Organizationを連携させることで、Githubに作成してあるチーム単位での指標が表示されるようになりました。

デプロイの頻度、変更障害率、サービス復元時間

こちらの指標を確認するには、まずはSwarmiaが用意しているDeployments APIを通してデプロイ情報を送る必要があります。 https://help.swarmia.com/sending-deployment-information-to-swarmia

Github Actionsでデプロイ情報を送信

開発チームのリリース作業では主にGithub Actionsのワークフローを使ってデプロイしているので、デプロイに成功したあとに上記APIを呼び出すステップを追加することで、Swarmiaにデプロイ情報を送るようにしました。 中には、1つのワークフロー内でFrontend/Backend等、複数のアプリをデプロイするケースもあるのですが、そういったケースでは全てのデプロイが成功した場合のみ送るようにしました。

一部情報は手動で設定

ここまででデプロイ頻度がわかるようになりますが、変更障害率、失敗率を算出するには、さらに追加の情報も必要です。 何らかの意図通り動かない失敗デプロイがあったとして、その修正のためにデプロイを行った場合には、どのデプロイに対する修正であるかわかるようバージョン番号を指定する必要があります。

この指定は、APIでデプロイ情報を送る際にオプションパラメータ(fixesVersion)で指定もできるのですが、修正バージョンは人が指定する必要があることから、ワークフローの入力パラメータを増やしたり、ワークフローを起動するSlackBotに手を加えたりと工数がかかってしまいます。

そこで他の方法がないか調べたところ、通常のデプロイとして送った情報に対し、あとからSwarmiaの管理画面上でfixesVersionを指定できることがわかったので、修正デプロイを行った場合には手動で設定するような運用手順としました。

連携した結果

これらの対応を行うことで、 デプロイの頻度、変更障害率、サービス復元時間 の指標についても確認できるようになりました。

現状

現在の各指標の値ですが、変更障害率はHigh(変更失敗が少ない),その他はMediumが多いという状況です。何を改善すれば良いか明らかになった為、顧客に価値を提供しつつそれぞれの値を改善していきたいと思います。

Kibanaでユーザ単位の権限管理をできるようにしました

デジタルイノベーションラボのIです。 QUOカードPayのElasticsearch/Kibana環境でユーザ単位の権限管理をできるようにした件について書こうと思います。

背景

AWSのALBのログをElasticsearchに取り込み、Kibanaで可視化しています。

マネージドサービスであるOpenSearch Serviceを使用しているのですが、VPC外部からアクセスするためにnginxをプロキシとしてKibanaにアクセスしています。そのプロキシECSのALBリスナーにおいてCognito認証を組み込んでいました。下記のような構成です。

今回、あるユーザにおいて特定のフィールドを閲覧できないようにしたいという要求が出ました。

何か方法はないか探したところ、OpenSearch Serviceにおいて「きめ細かなアクセスコントロール」を有効化しCognito認証を行い、ユーザごとに別のIAM Roleを割り当てることで実現できそうでした。

記事①

記事②

基本的にはこの2つの記事の通りに作業を実施したのですが、いくつか考慮すべき点があったのでそのあたりを中心に書きます。

変更点

プロキシのHTTPS化

OpenSearch Serviceできめ細かなアクセスコントロールを有効化する際はトラフィックHTTPS化が必須です。

既存構成ではALBより先ではHTTP(80)で通信していたので、ここをHTTPS(443)化する必要がありました。

記事①を参考にしたのですが、この記事においてはnginxをEC2でホストしています。プロキシのためだけにEC2を運用したくはないので、既存構成と同様にECS(Fargate)+ALBを使うことにしました。つまり下記のような構成です。

nginx公式のDocker imageをベースとし、nginx confファイル差し替えとSSL証明書の設置を行ったimageを作成しました。

confファイルは以下です。基本的には記事①に記載されているとおりですが、2つ差異があります。

まず、Elasticsearch バージョン7.10 を使用しているため、_dashboards ではなく _plugin/kibana エンドポイントを指定しています。

また、nginx 1.15.0 以降ではsslディレクティブはdeprecatedとなっているため削除し、listenディレクティブにsslを追加しています。

server {
    listen 443 ssl;
    server_name $host;
    rewrite ^/$ https://$host/_plugin/kibana redirect;

    ssl_certificate           /etc/nginx/conf.d/ssl/cert.crt;
    ssl_certificate_key       /etc/nginx/conf.d/ssl/cert.key;

    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    location /_plugin/kibana {
        # Forward requests to Dashboards
        proxy_pass https://${PROXY_PASS}/_plugin/kibana;

        # Handle redirects to Cognito
        proxy_redirect https://${COGNITO_DOMAIN} https://$host;

        # Update cookie domain and path
        proxy_cookie_domain ${PROXY_PASS} $host;
        proxy_cookie_path ~*^/$ /_plugin/kibana/;

        # Response buffer settings
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }

    location ~ \/(log|sign|fav|forgot|change|saml|oauth2) {
        # Forward requests to Cognito
        proxy_pass https://${COGNITO_DOMAIN};

        # Handle redirects to Dashboards
        proxy_redirect https://${PROXY_PASS} https://$host;

        # Handle redirects to Cognito
        proxy_redirect https://${COGNITO_DOMAIN} https://$host;


        # Update cookie domain
        proxy_cookie_domain ${COGNITO_DOMAIN} $host;
    }
}

既存構成ではALBリスナーにおいてCognito認証を設定していましたが、OpenSearch Serviceで設定するため削除しました。

また前述のnginx設定だとKibanaへ302リダイレクトされるため、ターゲットグループのヘルスチェックのSuccess codesには302を設定しました。プロトコルHTTPSです。

特定フィールドのマスク

記事②の手順24~31にあたる、IAMLimitedUserRoleを割り当てたCognitoユーザーグループ: limited-user-group のKibana操作権限設定の部分です。

今回は読み取り権限のみかつ、request_uri, request_uri_path, request_uri_query, redirect_url というフィールドを閲覧できないようにしたかったので、インデックス許可を以下の様に設定しました。

これにより、limited-user-groupに所属したユーザにおいては、このフィールドのデータが以下のようにマスクされます。

終わりに

繰り返しになりますが、概ねAWS公式ドキュメント通りに進めてやりたいことが実現できました。

そもそもOpenSearch Service側ではなくALBでCognito認証をするというやや邪道な構成だったので、この機会によりよい構成に変更できてよかったです。

脱社内外注を進めています

デジタルイノベーションラボの齋藤です。

少し前から、社内で脱社内外注という話をしています。社内外注の状態でもうまく行っている組織もあると思いますが、クオカードの場合は色々と課題を感じた為、状況を変えようとしています。今回は社内外注にはどのような問題があると考えているか、またどのように脱却しようとしているかを説明します。

本記事内での社内外注、用語の定義は以下とします。

  • ビジネス側が構築するシステムの要件定義を行い、リリース時期と合わせて開発側に依頼(発注)する
  • 開発側は依頼された通りのシステムを構築し納品する
  • 開発側:デジタルイノベーションラボ所属のエンジニア、デザイナー
  • ビジネス側:エンジニア、デザイナー以外

内容的にビジネス側と開発側に対立があるように受け取られる部分があるかもしれませんが、クオカードはかなり協力的に進める事ができている組織だと思います。今回の件もビジネス側に相談したところ、ではどのように進めたら良いか教えて欲しいという申し出を頂きこの記事を書く事にしました。

社内外注の問題

正しいものを作るのが困難

DX 実践手引書 IT システム構築編 で、以下の外部委託の問題が挙げられていますが、この問題は外部委託が社内外注になった場合でもそのまま当てはまると考えています。

DX に取り組む上での課題を設定する際には、現場の人材の課題意識やニーズ、自社の強みを深堀することが重要であるが、それらの本質的な理解を外部の人材に求めることはそもそも難しい

ビジネス側が要件を作成する場合、作成したものを開発側に連携する形になりますが、必要なものを開発前の段階で漏れなく記載するのは不可能に近いと考えています。

要件だけが渡される場合、開発側がそれぞれの要件の背景や文脈を理解するのが難しいというものがあります。その為、要件に問題があったとしても開発中に気づくことができず、問題があったものがそのまま作られる事があります。

また要件だけが渡され、背景や文脈、現在の運用内容などの情報が連携されないと開発側に業務知識が蓄積されず、長期間開発を続けてもビジネス側に確認しないと何を作って良いかわからない状態が続いていきます。

エンジニア/デザイナーの専門知識が生かされにくい

通常システムを開発する際、機能要求に加え、性能やセキュリティなど、非機能要求も合わせて検討して行く事になります。

非機能要求の実現にかかる時間/コストは機能要求により変わる事がありますが、それらはシステム開発の専門家でないとわからず、社内外注の形だと非効率な設計になる事があります。

本来であれば要件の精査を行うところですが、社内外注の場合依頼を受けた時点でリリース時期が決まってしまっているなど、非効率なまま開発に着手せざるを得ない状況もあります。

また設計と実装は不可分なものと考えています。実装中に当初の設計に無理があることに気づいたり、より良い設計を思いつくことがあります。

またデザインもスキルが必要ですが、一方で専門職以外の人にそれが伝わりにくい領域だと感じます。こちらも言われたものをただ作っていくのではなく、必要に応じスキルを持っているメンバーがユーザーテスト等を実施し、正しいものを作るようにしていきたいと思います。

非効率

DX 実践手引書 IT システム構築編 で、以下の外部委託の問題が挙げられていますが、この問題も外部委託が社内外注になった場合でもそのまま当てはまると考えています。

外部委託開発はトライアルからのフィードバック、サービス実装者へのニーズの正確な伝達や設計実装などに時間的オーバーヘッドがかかりすぎることが問題となる。

目指す姿

エンジニアはシステムを作る存在ではなく、顧客(利用者)の課題を解決する存在

こういったものを作ってほしいという依頼が来た場合でも、それをそのまま構築するのではなく、依頼者の抱えている問題を把握し、場合によっては違うソリューションを提案した方が良いケースがあると思います。

例えば運用を変更する事で問題が解決するなら開発期間ゼロで問題が解決するため、ROIは高くなります。また時間をかけずに問題が解決するので顧客の満足度も高くなります。

現在までの取り組み

アナウンス&説明

まずはビジネス側、開発側両方に現在どのような問題があるか、どう改善していきたいかのアナウンスや説明を行いました。

一度伝えたら理解してもらえるというものでもないので、何度か同様の説明を行なっています。

具体的にはビジネス側には要件ではなく背景・課題を共有するようにして欲しい、開発側にはもし要件を受け取ってもそれをそのまま実装するのではなく、背景・課題を確認し、運用フローを作成した上で開発に着手して欲しいと伝えています。

フィードバック

目指したい方向とずれた進め方になっていることに気づいた場合は、できるだけ早めにその旨フィードバックするようにしています。

開発側が運用フローを作成してから開発に着手

課題を明らかにするため、また焦点がずれた機能を開発するのを避ける為、開発側が運用フローを作成してから開発に着手するようにしています。この取り組みにより、開発側もビジネス面の理解が進みました。

上記の取り組みはまだ道半ばですが、できるだけエンジニアがその専門性を生かし、ビジネスに貢献できる組織としたいと考えています。逆に技術だけに興味があるもしくは顧客の問題を解決することに興味が無い、または機能要求、非機能要求などを全て決めて欲しいという人にはマッチしない会社になっていると思います。

クオカードでは、顧客の問題を解決したい、もしくはビジネス側と一緒に要件を考えるなど裁量を持って働きたいエンジニアを募集しています。

採用サイト quo-digital.jp

出典

DX 実践手引書 IT システム構築編

AndroidアプリのDIライブラリを KoinからHiltに移行しました

デジタルイノベーションラボPayチームのゴルゴです。 先日Androidアプリで利用するDIライブラリをKoinからDagger-Hilt(移行Hilt)へ移行したので、今回はその件について書こうと思います。

背景

Androidアプリの初期実装は主に2名のエンジニアで行ったのですが、2018年の当時はまだどちらもDaggerの利用経験がなくDaggerの学習コストが高いことがネックとなり、導入が容易なKoinを採用しました。 Koinを採用してから最近まで特に大きな問題はなかったものの、

  • 近年Hiltがリリースされたことで導入コストが下がった
  • Koinのバージョン変更に伴いコード改修を必要とした
  • Koinが実行時に依存性解決するのに対し、Hiltはビルド時に解決するため問題の検出が早く安全に使える

などを考慮してHiltに移行することにしました。

進め方

準備

移行にあたってはまずはスクラムのスパイクタスクとして試験的に1画面の移行を試し、その中で課題の洗い出しと調査を実施することにしました。 Developersガイドに従って作業すると共に、Hiltのコンポーネントの分け方やライフサイクル、Koinで特殊な使い方をしていた箇所の移行方法についてまとめを行いチーム内で共有しました。 途中で躓いた点もあったのですがそれは後述しています。

本対応

2週間の1スプリントで気合を入れて一気に移行作業を実施しました。 Hiltのコンポーネントファイルは、アプリがMVVM+UseCase/Repository/DataStoreという構成なので、UseCase/Repository/DataStoreの各レイヤ毎に作成し、移行作業を進めました。 ただFirebaseAnalyticsのLoggerのような特定レイヤに属さず全般で使うようなオブジェクトもあるので、そういったものはApplicationModulesを作成してそこに属させることにしました。

また作業中に特に気をつけた点として、途中でリファクタリングやDeprecatedの対応をしたくなったのですが、これをしてしまうと万一問題があった際に原因切り分けに時間を要してしまうので、やむを得ない箇所を除きぐっとこらえて進めました。

躓いた点

途中躓いて調査を要したポイントです

ContextからEntryPoint取得

ActivityやFragment等のHilt既定のAndroidEntryPointと、依存オブジェクトを使いたい箇所が離れていてオブジェクトを引き回すのが難しいケースというのがどうしても出てきました。そのようなケースのためにContextさえあれば独自に定義したEntryPointを取得し、依存オブジェクトを取得する方法が用意されています。

https://dagger.dev/hilt/entry-points

例えば任意の場所でFooBarを欲しい場合は

@EntryPoint
@InstallIn(SingletonComponent::class)
interface FooBarEntryPoint {
    fun get(): FooBar
}

という定義をしておけば、

val fooBar = EntryPointAccessors.fromApplication<FooBarEntryPoint>(application).get()

でオブジェクトを取得することができます。

ActivityとFragmentでViewModelを共有

Activityとその内部に配置しているFragmentで同じViewModelを参照し、Activityからの操作をFragmentの画面に反映したい場面がありました。 このケースではActivity/Fragmentそれぞれで通常通りby viewModels()でオブジェクトを取得すると、別インスタンスになってしまいActivityからの操作が画面に反映されない事態が発生してしまいます。

こういった場合、Fragmentからは

// Fragment内
private val viewModel: FooBarViewModel by activityViewModels()

で取得することで、Activity側と同じインスタンスを共有することができます。

https://developer.android.com/kotlin/ktx?hl=ja#fragment

ViewModelのコンストラクタで動的なパラメータを引き渡し

例えば、一覧から詳細画面に遷移する際に、詳細画面のViewModeにIDを渡すようなケースがあると思います。ViewModelにミュータブルなプロパティやセッターメソッドを用意し、Activity/Fragmentからセットしてもいいのですが、次のようにSavedStateHandleを使うことでイミュータブルにすることができます。

@HiltViewModel
class DetailViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle
) : ViewModel() {
    val id = requireNotNull(savedStateHandle.get<ItemId>("id"))
   …
}

なお、savedStateHandle にはHiltによって activity.getIntent().getExtras()あるいはfragment.getArguments()が渡されますが、任意の値を設定したい場合にはAbstractSavedStateViewModelFactoryを継承したFactoryクラスをつくり、このクラスを使ってViewModelを生成することで実現できました。

fun provideFactory(
    owner: SavedStateRegistryOwner,
    defaultArgs: Bundle? = null,
    foo: Foo
): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
        handle.set("key_foo", foo)
        return TopicViewModel(handle) as T
    }
}

やってみてどうだったか

移行作業の中で単純な記述ミスも時折あったのですが、コード内にEntryPointを定義した時点で依存解決できずにビルドエラーとなる、つまりHiltのメリットとして期待していたとおり、コード実行せずとも設定ミスがわかるので効率的にすすめることが出来ました。 動かしてから初めてエラーになるようなミスはなかったと記憶しています。 実はHiltに移行したアプリをリリースしてから既に数ヶ月経過しているのですが、今のところ移行に起因した問題は出ていません。広範囲の修正をおこなったのでリリース時は不安はありましたが、無事に移行できてよかったです。

この記事がどなたかの助けになれば幸いです。

クオカードを退職します

こんにちは、デジタルイノベーションラボの飯島です。

この度、2022/9/30 にクオカードを退職することにしました。 理由は転職です。 7 月末に実施された月 1 回のエンジニア全体振り返りでこの話をし、「退職エントリを書いてどこかに投稿しときます」といったところ、ノリと流れと目に見えない力で会社のブログに退職エントリを書くことになりました。

クオカードの良い点も悪い点も書いて欲しいとのことでしたので、それを踏まえて立つ鳥跡を濁さないような内容にしようと思います。

転職の理由

ベンチャー企業で働いてみたかったからです。

クオカードは設立から 30 年以上経っており、会社の制度もしっかり整っていて、もうずっと長く働いている人たちもいます。(かと言って悪しき文化に囚われ続けている訳ではなく、全社で Slack の導入が済んでいたり、コミュニケーションや文書のオープン化などの社内改革も実施されています)

転職先は設立から 2 年ほどしか経過していないので、会社の文化や雰囲気が全く違うんじゃないかな、と思っています。 そういった、今とは全く違う環境で働いてみたい、というのが大きな理由です。

また、バックエンドの開発言語も Kotlin から TypeScript になるので、自分が慣れ親しんだ Kotlin + SpringBoot ではない環境ではコードがどう変わってくるんだろう、という興味もありました。

転職の方針

以下のような方針で探していました。

ベンチャー(曖昧)
フルリモート
残業が無い、もしくは少ない
通話などの同期的な MTG が少ない
静的型付言語を主に使っている
提供しているサービスが面白そう

ちなみに働き方の面に関してクオカードはほぼ満たしています。 通話はもうちょっと少なくなれば良いなあと思っていますが、スクラムを導入しているのでイベントの為に割く時間は必要とも思っています。 参考までに、自分の 1 週間の通話時間はこれくらいです。

月 : 朝会 15 分
火 : 朝会 15 分 / スクラムイベント 約 2 時間 (2週間スプリントなので週ごとに変わる)
水 : 朝会 15 分 + 月末に一回 MTG 1 時間
木 : 朝会 15 分 + リファインメント 1 時間 + アーキテクチャ相談会 1 時間
金 : 朝会 15 分 + 月末に一回 MTG 1 時間

平均すると 1日 40 分ほどでした。

多分ですが、これってかなり短い方だと思うので、もっと短くしたいと言う自分の意見は結構わがままな気もしています。 また、毎週木曜日には外部から講師の方を招いたアーキテクチャ相談会が開催されています。サーバーサイドのアーキテクチャや DB 設計の相談をしてアドバイスをいただいたり、最近のエンジニア界の話題などを解説していただいており、とても参考になっています。 転職することでこの相談会に参加できなくなるのが残念です。

静的型付言語も結構重要なポイントとして見ていました。

あくまで自分の感覚ですが、動的に型付けされる言語だとテストが大変だったりエディタの補完がいまいちなイメージがあります。 そういった言語を触っていると頭がこんがらがってきてしまうので、コンパイラがある言語は必須条件でした。(Rust や Scala で求人を出している会社もあり、すごく魅力的で面談もしていただいたのですが、働き方の面でマッチせず何社か辞退しました。) クオカードはバックエンドに Kotlin、フロントエンドに TypeScript を使っているので、これも良いところだと思っています。

提供しているサービスに関しては、自分が今まで携わったことがないような分野や製品に関わりたいと思っていました。

転職活動

5 ヶ月くらいかけて、いろいろな会社の人に面談や面接をしていただきました。

全部で 10 社くらいだったと思います。

面談前に企業とチャットで会話できるサービスもあり、その段階でマッチするかどうか判断して面談に進まない選択もできたので、最近の転職サービスは凄いな、と思いました。

ここに書いて良いのか分かりませんが、クオカードは割と柔軟に1日の勤怠開始終了時刻を決められるので、8:00 - 16:30 に勤務し、30 分息をついて、17:00 - 18:00 で面談する、といった形で進められました。

転職活動後

ありがたいことに、帰ってきたくなったらいつでも帰ってきて良いよ、と言ってもらえました。

ただ自分は 35 歳までに飯島珈琲店を開くという昔からの夢があるので、もしそうなっていた場合はQUOカードPay 加盟店候補として営業にいくかもしれません。

よろしくお願いします。

あと事務手続き等でお世話になっていた S さんに転職の報告をしたところ、とても暖かい言葉をいただきました。

最後に

クオカードで働いた 3 年間で、エンジニアとして何段階も成長できました。 社内テトリス大会がコロナで開催中止になってしまったこと以外心残りはありません。 ありがとうございました。

AWS WAFをv2へ移行しました

デジタルイノベーションラボのIです。 今回はクオカードペイシステムのAWS WAFをClassicからv2へ移行した件について書こうと思います。

背景

クオカードペイではインフラにAWSを使用しており、外部に公開しているALBにはWAF Classicを適用していました。その名の通りこのWAFは旧世代扱いで、現在ではWAF(v2)の使用が推奨されています。

※ 正確には旧世代のWAFは「AWS WAF Classic」、現世代のWAFは「AWS WAF」という名称なのですが、わかりやすく区別するためにこのエントリでは現世代のWAFをAWSAPIのように「v2」と呼ぶことにします。

botからのアクセスを弾くために、WAF bot controlを使いたいというのが移行を考え始めた直接のきっかけでしたが、更にカスタムレスポンス機能だったり、Log4jの脆弱性に対応したマネージドルールだったり、v2にしかない機能を使いたいというケースが増えてきました。

また今後もWAFの新機能はv2でしか実装されないと思われるため、このままClassicを使用していてはその恩恵も受けられません。

このような経緯でv2への移行を決めました。

移行方法

Classicからv2への移行にはAWS公式の手段が存在します。

—------------------------------------------------------------------------------------------------------------------------

1.自動移行では、既存のウェブ ACL に関連するすべての内容が読み込まれます。AWS WAF Classic で変更または削除される内容はありません。ウェブ ACL とその関連リソースの表現を作成します。この表現は、AWS WAF と互換性があります。新しいウェブ ACLAWS CloudFormation テンプレートを生成し、Amazon S3 バケットに保存します。

2.AWS WAF でウェブ ACL および関連リソースを再作成するために、テンプレートを AWS CloudFormation にデプロイします。

3.ウェブ ACL を確認して手動で移行を完了し、新しいウェブ ACL が最新の AWS WAF の機能を最大限に活用するようにします

4.保護されたリソースを新しいウェブ ACL に手動で切り替えます。

—------------------------------------------------------------------------------------------------------------------------

上記引用の通り、既存のClassic ACLの各種設定をもとに、新規でv2 ACLを作成するCloudFormation stackがデプロイされます。

検証環境で一度実際に試してみた結果、主に以下の理由からこの方法は採用しないことにしました。

  • 結局手動での追加作業が必要な部分が多い
  • 新しく作成されるv2 ACLの名前を指定できない(どうやら、「<移行元Classic ACL名>+<自動で付与されるuuid>」となる様子)
  • そもそもクオカードペイのAWS環境は基本的にTerraformで管理しているため、できるだけCloudFormationでのリソース管理は避けたい

よって、完全に一からv2関連リソースを新規作成することにしました。

ルール設定

移行前のClassicではAWS Marketplaceの「Fortinet Managed Rules for AWS WAF Classic - Complete OWASP Top 10」を利用していました。

v2ではそれに代えて、AWS標準のマネージドルールグループを使うことにしました。WCUを1500以内に収める必要があるため、以下のマネージドルールをACLに追加しました。

—--------------------------------------------------------------------

AWSManagedRulesCommonRuleSet
AWSManagedRulesKnownBadInputsRuleSet
AWSManagedRulesSQLiRuleSet
AWS-AWSManagedRulesLinuxRuleSet
AWS-AWSManagedRulesUnixRuleSet
AWS-AWSManagedRulesAmazonIpReputationList
AWSManagedRulesBotControlRuleSet

計1475WCU

—--------------------------------------------------------------------

また、IPセットも使用してIPアドレスでの制御も実施しました。これはClassicと大差ありません。

チューニング

AWS標準のマネージドルールは特にブラックボックス的なので、実際の動作をみながらのルールのチューニングが必須です。

まずは検証環境に適用し、また本番環境でもルールのアクションを最初はCount状態にして動作確認・ログ分析をしました。WAFのログ出力先はいくつか選択肢がありますが、今回は直接S3に出力してAthenaで分析することにしました。

確認の大きな観点は以下の2点です。

  • 不正なリクエストをブロックできているか
  • 正常なリクエストをブロックしてしまっていないか

WAFに期待しているのは当然前者の機能ですが、ある意味後者の確認のほうがより重要だと思います。正常なユーザアクセスに影響があっては本末転倒です。(実際、検証が不十分でトラブルを起こしてしまいました...)

ブロックの原因となっていたルールをログから特定し、ルールグループから除外しました。

まとめと今後の展望

以上、大まかではありますがWAFの移行に関して実施したことを記載しました。 そもそも移行元のWAF Classicのほうでも沢山のルール設定はしておらず、移行すべきものは少なかったため、実質一から構築したものの作業量はそれほど大きくはなりませんでした。もし複雑なルールを多数設定していたら移行ももっと大変だったと思います。

今後についてですが、現在は正常なリクエストが引っ掛かったルールはルールグループから単純に除外しているのみですが、特にWAF Bot Controlに関してはより細かい制御ができるので時機を見てチューニングをしていきたいです。