Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

TypeScriptで型に? (Optional Property) を使わないほうがいいとき

こんにちは。みらい翻訳でフロントエンジニアをしているWillと申します。

今回は、コードレビューでたまに指摘する、TypeScriptのOptional Propertyを使わないほうが良い場面を紹介します。

Optional Propertyについてのおさらい

まず、Optional Propertyについて軽く確認します。

type User = {
    id: number;
    name: string;
    nickname: string?;
}

この時、User型は、idとnameプロパティは必須です。一方でnicknameプロパティはオプショナルとなり、その存在は必須ではありません。

Optional Propertyに関するドキュメント TypeScript: Handbook - Interfaces

TypeScriptの基本的な機能であり、みなさんも様々な場面でOptional Propertyを使われているかと思います。

今回は、このOptional Propertyではなく、ユニオン型を使ったほうがよい場合を紹介します。

アラートコンポーネントでの例

例えば以下のような条件のReactコンポーネントを考えます。

  • アラートを表示するコンポーネント
    • 「お知らせ」と「警告」の2種類のタイプがある
    • どちらのタイプにもOKボタンがある
    • 警告の場合は、助けを求めるためのヘルプボタンがある

Optional Propertyを使った実装

以下のように実装しました。

const Alert = (param:
    {
        type: 'warning' | 'notice';
        text: string;
        onOkButtonClick: () => void;
        onHelpButtonClick?: () => void
    }
) => {
    return (
        <div>
            <p>
                {param.text}
            </p>
            <button onClick={param.onOkButtonClick}>OK</button>
            {
                param.type === 'warning'
                && <button onClick={param.onHelpButtonClick}>HELP</button>
            }
        </div>
    );
};

onHelpButtonClickコールバックは、Optional Propertyを使って実装されています。

それでは実際に使ってみます。

<Alert type="notice" text="お知らせ" onOkButtonClick={()=>{}}/>

「お知らせ」は問題ないですね。

しかし、

<Alert type="warning" text="警告" onOkButtonClick={()=>{}}/>

「警告」はonHelpButtonClickの実装が要件的には必須であるにも関わらず、型で表現されていないため、 上記のようなonHelpButtonClickの実装が漏れていても型エラーになりません。

これでは、このコンポーネントを利用する際に誤った実装をしてしまう可能性があります。

ユニオン型を使った実装

onHelpButtonClickにOptional Propertyを使っては、要件を正確に型に落とし込むことができません。

では、ユニオン型を使って表現してみましょう。

const Alert = (param: {
    type: 'warning';
    text: string;
    onOkButtonClick: () => void;
    onHelpButtonClick: () => void;
} | {
    type: 'notice';
    text: string;
    onOkButtonClick: () => void;
}) => {
    return (
        <div>
            <p>{param.text}</p>
            <button onClick={param.onOkButtonClick}>OK</button>
            {param.type === 'warning' && <button onClick={param.onHelpButtonClick}>HELP</button>}
        </div>
    );
};

タグ付きユニオン を使って表現します。

'warning'の場合、onHelpButtonClickが必須であることが型レベルで定義されます。

先程の「警告」タイプで、onHelpButtonClickの実装が漏れていた以下のコードはタイプチェックが通りません。

<Alert type="warning" text="警告" onOkButtonClick={() => {}} />; 
 TS2322: Type '{ type: "warning"; text: string; onOkButtonClick: () => void; }' is not assignable to type 'IntrinsicAttributes & ({ type: "warning"; text: string; onOkButtonClick: () => void; onHelpButtonClick: () => void; } | { type: "notice"; text: string; onOkButtonClick: () => void; })'.   Property 'onHelpButtonClick' is missing in type '{ type: "warning"; text: string; onOkButtonClick: () => void; }' but required in type '{ type: "warning"; text: string; onOkButtonClick: () => void; onHelpButtonClick: () => void; }'.

これで、間違ってヘルプボタンが反応しないアラートを作ってしまう危険性がなくなりました。

おまけ

共通のプロパティは以下のように交差型を使って共通化しておくとより分かりやすいです。

type Common = {
    text: string;
    onOkButtonClick: () => void;
};
const Alert = (param: {
    type: 'warning';
    onHelpButtonClick: () => void;
} & Common | {
    type: 'notice';
} & Common) => {

...

まとめ

Optional Propertyを使わないほうがよい場面について紹介しました。

例として割と極端な例を取り扱ったため、分かりやすかったと思いますが、実際のコードでは気づきにくい場合もあります。

型によるドキュメントを意識して、安全なコーディングをしていきましょう。

みらい翻訳ではエンジニアを募集しています

みらい翻訳では、フロントエンジニアを募集しています。

ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。(フロントエンドエンジニア以外も募集中です)

miraitranslate.com

tfsecを試してみる

みらい翻訳のバックエンドエンジニアのtoshと申します。

弊社ではインフラの構築にterraformを利用しています。

今回はterraformのセキュリティスキャンツールを紹介します。

tfsecとは

Aqua Securityという会社が開発しているオープンソースのterraformの静的解析ツールで、セキュリティスキャンをしてくれます。

特徴として、非常に高速に動作することと、ローカルはもちろんCI/CDパイプラインで動作させることが想定されています。

以下はgithubリポジトリからの抜粋です。

GitHub - aquasecurity/tfsec: Security scanner for your Terraform code

tfsec uses static analysis of your terraform code to spot potential misconfigurations.

tfsecは、Terraformコードの静的分析を使用して、潜在的な構成ミスを見つけます。

また、technology radarではAdoptになっていました。 Tools | Thoughtworks

For our projects using Terraform, tfsec has quickly become a default static analysis tool to detect potential security risks. It's easy to integrate into a CI pipeline and has a growing library of checks against all of the major cloud providers and platforms like Kubernetes. Given its ease of use, we believe tfsec could be a good addition to any Terraform project.

Terraformを使用するプロジェクトでは、tfsecはすぐに、潜在的なセキュリティリスクを検出する既定の静的分析ツールになりました。
CIパイプラインに簡単に統合でき、Kubernetesのような主要なクラウドプロバイダーやプラットフォームすべてに対するチェックのライブラリが増えています。
使いやすさを考えると、tfsecはTerraformプロジェクトに追加するのに適していると思います。

ローカルで試してみる

インストール

brew install tfsec

実行

tfsec実行のために、以下のようなセキュリティグループを作成するtfファイルを作成しておきました。

resource "aws_security_group" "test" {
  name        = "test-security-group"
  description = "For test"
  vpc_id      = "vpc-xxxxxx"
}

resource "aws_security_group_rule" "api_ingress" {
  security_group_id = aws_security_group.test.id
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
}

resource "aws_security_group_rule" "egress_all" {
  security_group_id = aws_security_group.test.id
  type              = "egress"
  description       = "Allow all"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}

実行の仕方は以下の通りです。

# tfファイルがあるディレクトリで
tfsec

# パスを指定する場合
tfsec ${terraformファイルがあるディレクトリ}

実行結果

実行結果は下記の通りです。実行結果の中に実行にかかった時間が記載されていますが、非常に高速に動作したことがわかると思います。

また、指摘の詳細と一緒にterraformのドキュメントのリンクを一緒に吐いてくれるのは便利だと思いました。

$ tfsec

Result #1 CRITICAL Security group rule allows ingress from public internet. 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  main.tf:13
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    7    resource "aws_security_group_rule" "api_ingress" {
    8      security_group_id = aws_security_group.test.id
    9      type              = "ingress"
   10      from_port         = 443
   11      to_port           = 443
   12      protocol          = "tcp"
   13  [   cidr_blocks       = ["0.0.0.0/0"]
   14    }
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-ec2-no-public-ingress-sgr
      Impact Your port exposed to the internet
  Resolution Set a more restrictive cidr range

  More Information
  - https://aquasecurity.github.io/tfsec/v1.27.1/checks/aws/ec2/no-public-ingress-sgr/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule#cidr_blocks
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


Result #2 CRITICAL Security group rule allows egress to multiple public internet addresses. 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  main.tf:23
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   16    resource "aws_security_group_rule" "egress_all" {
   17      security_group_id = aws_security_group.test.id
   18      type              = "egress"
   19      description       = "Allow all"
   20      from_port         = 0
   21      to_port           = 0
   22      protocol          = "-1"
   23  [   cidr_blocks       = ["0.0.0.0/0"]
   24    }
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-ec2-no-public-egress-sgr
      Impact Your port is egressing data to the internet
  Resolution Set a more restrictive cidr range

  More Information
  - https://aquasecurity.github.io/tfsec/v1.27.1/checks/aws/ec2/no-public-egress-sgr/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


Result #3 LOW Security group rule does not have a description. 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  main.tf:7-14
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    7    resource "aws_security_group_rule" "api_ingress" {
    8      security_group_id = aws_security_group.test.id
    9      type              = "ingress"
   10      from_port         = 443
   11      to_port           = 443
   12      protocol          = "tcp"
   13      cidr_blocks       = ["0.0.0.0/0"]
   14    }
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-ec2-add-description-to-security-group-rule
      Impact Descriptions provide context for the firewall rule reasons
  Resolution Add descriptions for all security groups rules

  More Information
  - https://aquasecurity.github.io/tfsec/v1.27.1/checks/aws/ec2/add-description-to-security-group-rule/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


  timings
  ──────────────────────────────────────────
  disk i/o             116.642µs
  parsing              870.418µs
  adaptation           4.862873ms
  checks               25.687667ms
  total                31.5376ms

  counts
  ──────────────────────────────────────────
  modules downloaded   0
  modules processed    1
  blocks processed     3
  files read           1

  results
  ──────────────────────────────────────────
  passed               2
  ignored              0
  critical             2
  high                 0
  medium               0
  low                  1

  2 passed, 3 potential problem(s) detected.

CIで動作させる

https://github.com/aquasecurity/tfsec-action

こちらを参考に以下のコードを作成しました。

name: tfsec
on:
  workflow_dispatch:

jobs:
  tfsec:
    name: tfsec sarif report
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write
    steps:
      - name: Clone repo
        uses: actions/checkout@v2
        with:
          persist-credentials: false

      - name: tfsec
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          working_directory: ./tosh/test_tfsec

実行結果

まとめ

セキュアなterraformコードを書くお供としてtfsec使ってみては良いのではないでしょうか。 インストールも簡単で非常に高速に動作するのでおすすめです!

機械翻訳の論文を機械翻訳して読み上げてほしい

機械翻訳エンジン研究開発チームのマネージャーを務めているlisaです。機械翻訳エンジンの不具合解消や新機能実装を監督しながら、中長期的なエンハンスメントのために機械翻訳の論文を読む日々です。とは言え、今年に入ってからまだ100本くらいしか読めていないので、もう少し精進しなくてはなりません。

ところで、最近AmazonのオーディオブックサービスであるAudibleにハマっています。Audibleの良いところは、月1500円で多くのビジネス書が聴き放題対象になることです。筆者は、例えば洗濯物を干したり、化粧や筋トレをしたりしながら、Audibleで興味のある本を2倍速で聴き流し、本当に気に入ったものは後でKindle版を購入して詳しく読む…という使い方をしています。つまりAudibleだけで情報収集するというよりは、「あとで読む」用の本を選ぶためにAudibleを使っているわけです。

このようなAudibleの使い方をしていて、ふと「論文も同じやり方で『あとで読む』ものを選べるのではないか?」と思ったのが、この記事の始まりです。

やりたいこと

今回個人的に満たしたかった要件は以下です。

1. arXivにある最新の機械翻訳関連論文の概要を読み上げてくれる

  • Audibleと同様、PCやスマホから手を離している時に聴き流したい
  • あくまで「あとで読む」ものを選ぶのが目的なので、大まかに何の課題を解こうとしているかわかれば良い

2. iPhoneで動作する

3. 日本語に自動翻訳してくれる

  • arXivの論文はほとんどが英語
  • 一応、元の英語が読める程度の英語力はあるのですが、アイラインを引いたり、スクワットの姿勢に注意を払ったりしながら英語を聴くのは辛い

4. 読み上げ速度を変更できる

  • 筆者はせっかちなので、AudibleだけではなくYoutubePodcastも2倍速にしている

できたもの

早速ですが、こちらが上記の要件を満たした完成品です。(実際、思いついてから30分くらいで出来た)*1


www.youtube.com

処理内容

iPhoneショートカットという、IFTTTのようなオートメーションツールを使って下記の処理を順次自動実行しています。(上記の動画はショートカットを起動してから、1番目の論文の翻訳結果を読んでいるところの途中まで収録)*2

  1. arXivAPIを利用して、要約に「translation」を含む新着論文のリストをRSS形式で取得*3
  2. 上記で取得した論文情報を、iPhoneの翻訳アプリで日本語に翻訳する
  3. 日本語に訳した結果をSiriが読み上げる

具体的なショートカットの設定内容は下記の通りです。

ショートカット設定内容

順次読み上げるだけでは、後で「途中でなんか面白そうな論文あったけどなんだっけ?」となる展開が容易に予想されるため、最後に元の英語の論文要約を一覧表示しています。

ショートカット実行の最後に出る通知

ショートカットの設定で読み上げスピードを自由に設定することも可能です。冒頭の動画ではデフォルト設定よりも速めの読み上げスピードに設定してあります。

読み上げの速度や言語の設定

今後の課題

このように要件通りのものが無事に出来たわけですが、残された課題もいくつかあります。

論文のタイトルや著者情報が読み上げられない

元のRSSには論文のタイトルや著者情報も含まれているのですが、なぜか要約しか読み上げられません。やり方わかる方いらしたら教えてください。

専門用語がうまく読み上げられないことがある

仕方のないことではあるのですが、専門用語が正しく読み上げられないことがあり、聞いていてちょっとびっくりすることがあります。冒頭の動画の中でも「資源の少ない環境で『ネ』を翻訳することは依然として課題です (原文: translating NE in low-resource setting remains a challenge)」という箇所があったことにお気づきの方もいらっしゃるのではないでしょうか?ここで言う「ネ」はnamed entities*4の略である「NE」のことで、日本語では通常「エヌイー」や「固有表現」と呼ばれるものです。読み上げの辞書のようなものが設定できるとより良いのかもしれません。

また、読み上げ以前の機械翻訳で、上記のnamed entitiesのような専門用語が適切な日本語に訳されない場合もあります。これは機械翻訳の一般的な技術課題であり、本業のエンジン開発の中でも取り組んでいきたい、本当の「今後の課題」です。

おわりに

これで日々の暮らしの中で無理なく機械翻訳の論文を読んでいけることを期待しています。しばらく運用してみて、アップデートがあればまたご報告させてください。

当エンジニアリング部では自然言語処理リサーチャーとエンジニアを募集しています。面白い機械翻訳論文を見つけられた方は、ぜひカジュアル面談で教えてください。

miraitranslate.com

*1:筆者iPhoneの画面収録動画。壁紙は宝塚花組トップスターの柚香 光さんです。

*2:本記事ではiOS 15.6.1を利用しています。

*3:arXiv API利用の際は利用規約の内容を確認・遵守してください。

*4:人名・組織名のような固有名詞や、日付・時間・数値を表す表現のこと。

Outboxパターンについて

みらい翻訳のバックエンドエンジニアのtoshと申します。

私が所属しているチームは主に社内のDXの向上をミッションとしているチームで、現在はマイクロサービス化に取り組むための基盤の構築やルールの整備をしています。

マイクロサービス化を進める上で事前に検討しておくべきことの一つとして、サービス間の通信方法が挙げられると思います。

一般的にはRESTのような同期通信をするケースも多いかと思いますが、メッセージングを利用して非同期で連携する仕組みがあると様々なメリットがあります。本稿ではこちらについては詳細は解説しませんが、こちらのブログなどが参考になります。

マイクロサービスとメッセージングのなぜ [概要編] - 赤帽エンジニアブログ

上記の記事によると、メッセージングシステムやイベント駆動アーキテクチャの採用により、サービス間を疎結合に保ち、システム全体の安定性の向上が見込めるとのことです。

このような事情があり、弊社ではメッセージングシステムを共通基盤として構築しています。

実際にメッセージングシステムを利用する際の使い方の一つとして、Outboxパターンを利用すると便利なことがあると考えているため紹介します。

概要

Outboxパターンは、メッセージングシステムを利用する上でのトランザクション制御の信頼性を高めるための設計パターンです。

信頼性を高めるための方法として、メッセージングシステムに直接書き込むのではなくいったんDBに書き込むことにより、他のDB書き込みとのトランザクション同期を取りやすくなります。また、可用性が高く実装難易度の低いRDBMSのメリットを享受しつつ、メッセージングシステムを利用することができます。

コンテキスト

あるシステム間の連携にメッセージングシステムを使用する際に、多くの場合データベースの更新とメッセージングシステムへのイベントの送信と2種類の更新処理を実行します。

例えばECサイトのようなシステムで、注文システムが在庫システムデータを連携する処理を例に考えてみます。

  1. 注文アプリケーションが注文データを注文DBに書き込む

  2. メッセージングシステムに在庫アプリケーションに連携するメッセージを書き込む

このような処理をしたい際に問題になるのが、上記の1の処理と2の処理をトランザクショナルに実行することが難しい点です。DBへの書き込みが成功した場合のみメッセージング処理をさせなければいけないですし、メッセージの送信に失敗した場合は確実にトランザクションロールバックさせたいはずです。

DBの1トランザクションで両方の処理を行う場合、トランザクション内で外部システムの連携を行うことはトランザクションの信頼性の低下と処理時間の長期化の原因となり、このような実装は極力避けるべきと考えます。

2フェーズコミットを実装すればできないこともないですが、外部システム同士の結合度が上がってしまいます。また、DBの書き込みとメッセージングが必要な全ての箇所で2フェーズコミットを実装するのはあまり現実的ではないと思います。そのため、2フェーズコミットもあまり現実的な選択肢ではないと考えます。

 

課題

  • DBの更新処理が成功した時だけメッセージを送信し、DBがロールバックされた時にメッセージを送信したくない

  • 上記の実装のために2フェーズコミット(分散トランザクション)を使いたくない

Outboxパターンとは

Outboxテーブルと呼ばれるメッセージを書き込む専用のテーブルと、DBに書き込まれたデータをメッセージングシステムに送信するシステムを用意します(以降このシステムをメッセージリレーと呼びます)。

Outboxテーブルは更新対象のデータと同一DBに用意します。

実際の処理では、データの更新と同一トランザクション内でOutboxテーブルにメッセージを書き込みます。メッセージリレーがOutboxテーブルに書き込まれたデータをメッセージングシステムに送信します。

このような実装により、データの更新とOutboxテーブルへの書き込みがコミットされた時のみメッセージが送信されます。また、DBがロールバックされた場合にはメッセージは送信されません。

 

Outboxパターンのイメージ図



Outboxパターンの利点

先ほどあげた2つの課題を解消してくれます。

  • DBの更新処理が成功した時だけメッセージを送信し、DBがロールバックされた時にメッセージを送信したくない

→    DBの更新処理が成功した時のみメッセージを送信することができます

→    2フェーズコミットを利用する必要はなく、DBの更新処理のみで実現可能です

 

また、別の視点として

  • 仮にメッセージングシステムに障害が発生していても、DBが稼働しているのならデータの更新処理とOutboxテーブルへの書き込み(実質メッセージの書き込み)処理を成功させることができます。この時、メッセージングシステムが復旧後、正常にメッセージング処理が行われます

  • 一般的にメッセージングシステムへの書き込みのコードを書くよりもDBへの書き込み処理の方が実装が容易です

  • 信頼性の高い大元のデータがDBにあるため、データの損失や破損のリスクが低いです。また、障害発生時の対応も容易です。

     

 

Outboxパターンのデメリット

  • メッセージングリレーを挟むため、送信先にメッセージが到達するまでの時間が増えます

→    リアルタイムに近い処理を要求される場合は選択肢から外れます

→    大量の書き込みが発生する場合、Outboxパターンを使わない場合と比較してパフォーマンスが落ちます

 

総括

Outboxパターンのメリット・デメリットを上げました。上記の通り、1つの処理の中でDBの更新処理とメッセージング処理のどちらも行う場合、有力な選択肢となります。

反対に、高い書き込みパフォーマンスが要求される場合やメッセージング処理単体の場合はシンプルなメッセージングシステムへの書き込みの方が有力な選択肢となり得るかと思います。

EKS on Fargate で aws-loadbalancer-controller を有効にするまで

こんにちは。みらい翻訳 SRE チームの jeff です。

現在 SRE で EKS 導入を進めています。

EKS を導入するにあたり、EKS でロードバランサー気軽に扱えるように aws-loadbalancer-controller を有効化しようとしましたが、色々とハマりポイントがあったので備忘として記事にしました。

aws-loadbalancer-controller とは

EKS で使用するロードバランサーを管理してくれるアドオンです。

Ingress リソースを apply すると自動で ALB, NLB を作ってくれます。

github.com

aws-loadbalancer-controller 導入経緯

EKS で LoadBalancer サービスを作成した場合、デフォルトでは自動で ELB の一つである CLB(Classic Load Balancer)が作られます。 現在構築している EKS は Fargate 環境を使用しています。しかし、 Fargate は CLB に対応していません。 そのため EKS が Fargate に対応したロードバランサー (具体的には ALB(Application Load Balancer) ) を作れるように aws-loadbalancer-controller を導入しようというのが今回の経緯になります。

導入の前に

確認環境

以下の環境で導入及び動作確認をしています。

  • EKS on Fargate
  • aws-loadbalancer-controller 2.4.2

前提条件の確認

AWS のマニュアルから EKS で ALB を使えるようにするための前提条件を確認します。

docs.aws.amazon.com

  • 既存の EKS があること
  • クラスタのバージョン
    • それに伴う aws-loadbalancer-controller のバージョン
  • サブネットの状態とサブネットへのタグ付け

導入手順

基本的には以下のマニュアルに沿っていけば導入を進めます。

尚、aws cli & kubectl を使用しています。

docs.aws.amazon.com

aws-loadbalancer-controller を導入する

IAM ロールを作る

aws-loadbalancer-controller のサービスアカウントが ELB を管理するために使用する IAM ロールを作成します。 また、ロールに必要な IAM OIDC プロバイダーとポリシーを作成します。

EKS クラスタ用の IAM OIDC プロバイダーを作成する

$ aws iam create-open-id-connect-provider \
  --url [クラスターのOIDCプロバイダURL] \
  --client-id-list "sts.amazonaws.com" \
  --thumbprint-list [サムプリント]
クラスターのOIDCプロバイダURL 確認

以下のコマンドで確認します。

$ aws eks describe-cluster \
  --name [EKS クラスター名] 
  --query "cluster.identity.oidc.issuer" \
  --output text
サムプリント確認

こちらの手順でサムプリントを確認します

docs.aws.amazon.com

画面から登録

サムプリントの確認が手間な場合はマネジメントコンソールからも登録できます。

  1. AWS マネジメントコンソールの IAM 管理画面で [ID プロバイダ] を開きます
  2. [プロバイダを追加] を押します
  3. プロバイダのタイプを OpenID Connect を選択します
  4. プロバイダURLに上記 クラスターのOIDCプロバイダURL 確認 で取得した URL を入力します。
  5. [サムプリント取得] を押します
  6. 対象者に sts.amazonaws.com を入力します
  7. [プロバイダを追加] を押します

ポリシーの作成

aws-loadbalancer-controller に必要なポリシーを作成します。 以下の json を使用してポリシーを作成します。

https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.2/docs/install/iam_policy.json

$ aws iam create-policy \
  --policy-name [ポリシー名] \
  --policy-document file://iam_policy.json

ロールの作成

AssumeRole Policy の用意

AWS のドキュメントに沿って AssumeRole Policy の JSON を用意します。

cat >load-balancer-role-trust-policy.json <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com",
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
                }
            }
        }
    ]
}
EOF
ロールの作成&ポリシーアタッチ

こちらも AWS のドキュメントに沿ってロールの作成とポリシーアタッチを行います。

上記で作成した assume role policy の json を使用して IAM ロールを作成します。

$ aws iam create-role \
  --role-name [IAM ロール名] \
  --assume-role-policy-document file://"load-balancer-role-trust-policy.json"

「ポリシーの作成」で作ったポリシーを先ほど作成したロールにアタッチします。

$ aws iam attach-role-policy \
  --policy-arn [IAM ポリシー ARN] \
  --role-name [IAM ロール名]

EKS クラスターにサービスアカウント作成

aws-loadbalancer-controller のサービスアカウントを作成します。アノテーションに上記で作成したロールを設定してサービスアカウントが IAM ロールを使用できるようにします。

apply する yaml を用意します。

cat >aws-load-balancer-controller-service-account.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: aws-load-balancer-controller
  name: aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: [IAM ロール ARN]
EOF

上記で作成した yaml を apply します。

kubectl apply -f aws-load-balancer-controller-service-account.yaml

これで認証周りの準備は OK です。

eksctl を使う

ここまで IAM ロールとサービスアカウントの準備について書きましたが、eksctl を使えばワンコマンドでサクッと作ることができます(ポリシーだけは事前に作成しておきます)。

$ eksctl create iamserviceaccount \
  --cluster=[EKS クラスター名] \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name [IAM ロール名] \
  --attach-policy-arn=[IAM ポリシー ARN] \
  --approve

aws-loadbalancer-controller インストール

認証周りの準備ができたら aws-loadbalancer-controller をインストールします。 Fargate だと cert-manager に対応していないため helm でインストールします。

$ helm repo add eks https://aws.github.io/eks-charts
$ helm repo update

AWS のドキュメント通りでインストールは問題ないですが、Fargate だとリージョンコードと vpcId が必須になります。

$ helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=[EKS クラスター名] \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set region=[リージョン名] \
  --set vpcId=[EKS クラスターが属している VPC の Id]

これで aws-loadbalancer-controller のインストールは完了です!

動作確認

aws-loadbalancer-controller のインストールが完了しました。EKS クラスタIngress リソースを特定のアノテーションを付けて作成すると ALB を自動で作ってくれるので試してみます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/backend-protocol-version: HTTP1
    alb.ingress.kubernetes.io/healthcheck-path: /health
    alb.ingress.kubernetes.io/success-codes: '200'
  name: test
  namespace: test
spec:
  rules:
  - host: hogehoge.com
    http:
      paths:
      - path:
        pathType: ImplementationSpecific
        backend:
          service:
            name: test
            port:
              number: 80

ALB の作成に失敗する

この yaml を apply してみましたがエラーが出てしまいました(エラーメッセージは失念してしまいました)。 pods の状況を確認したところ、pods の起動に失敗していました。

$ kubectl get pods -n kube-system
NAME                                           READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   0/1     ImagePullBackOff   0          10m
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   0/1     ImagePullBackOff   0          10m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          10m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          10m

STATUS をみてみるとイメージの Pull に失敗しているようなことがわかります。 さらに describe で pods の情報を見てみます。

$ kubectl describe pod aws-load-balancer-controller-xxxxxxxxxx-xxxxx -n kube-system

(中略)

Events:
  Type     Reason           Age                  From               Message
  ----     ------           ----                 ----               -------
  Warning  LoggingDisabled  6m48s                fargate-scheduler  Disabled logging because aws-logging configmap was not found. configmap "aws-logging" not found
  Normal   Scheduled        5m57s                fargate-scheduler  Successfully assigned kube-system/aws-load-balancer-controller-xxxxxxxxxx-xxxxx to fargate-ip-xxx-xxx-xxx-xxx.us-west-2.compute.internal
  Warning  Failed           3m26s                kubelet            Failed to pull image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": rpc error: code = Unknown desc = failed to pull and unpack image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to resolve reference "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to do request: Head "https://602401143452.dkr.ecr.us-west-2.amazonaws.com/v2/amazon/aws-load-balancer-controller/manifests/v2.4.3": dial tcp xxx.xxx.xxx.xxx:443: i/o timeout
  Warning  Failed           40s (x2 over 3m26s)  kubelet            Error: ErrImagePull
  Warning  Failed           40s                  kubelet            Failed to pull image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": rpc error: code = Unknown desc = failed to pull and unpack image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to resolve reference "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to do request: Head "https://602401143452.dkr.ecr.us-west-2.amazonaws.com/v2/amazon/aws-load-balancer-controller/manifests/v2.4.3": dial tcp xxx.xxx.xxx.xxx:443: i/o timeout
  Normal   BackOff          25s (x2 over 3m25s)  kubelet            Back-off pulling image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3"
  Warning  Failed           25s (x2 over 3m25s)  kubelet            Error: ImagePullBackOff
  Normal   Pulling          12s (x3 over 5m57s)  kubelet            Pulling image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3"

ネットワーク設定不備

aws-load-balancer-controller のイメージ Pull に失敗する原因としては EKS を構築した VPC では ECR の VPC エンドポイントを使用していたためでした。 ECR への接続はプライベートなネットワークで行われていて、 ECR の VPC エンドポイントが EKS からの接続を許可していませんでした。そのため、ECR の VPC エンドポイントに設定しているセキュリティグループの ingress に EKS クラスタに設定しているセキュリティグループを追加したところ、イメージが Pull されるようになりました。

ただ、これだけでは解決しませんでした・・・

ALB の作成に失敗する2

ingress の apply でエラーは出なくなりましたが、なぜか ALB が作られません。再度 pods の状況を見たところ、 coredns が起動していないことに気づきました。

$ kubectl get pods -n kube-system
NAME                                           READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          15m
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          15m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          15m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          15m

aws-load-balancer-controller のログをみてみると、名前解決できていないようなエラーが出ていたので coredns が起動していないことが原因のように思われました。

$ kubectl logs aws-load-balancer-controller-xxxxxxxxxx-xxxxx -n kube-system

{"level":"error","ts":1661560470.824888,"logger":"controller-runtime.manager.controller.ingress","msg":"Reconciler error","name":"test","namespace":"test-namespace","error":"ingress: test-namespace/test: WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \"https://sts.us-west-2.amazonaws.com/\": dial tcp: lookup sts.us-west-2.amazonaws.com on xxx.xxx.xxx.xxx:xxx: read udp xxx.xxx.xxx.xxx:xxxxx->xxx.xxx.xxx.xxx:xxx: read: connection refused"}
{"level":"error","ts":1661560654.9364552,"logger":"controller-runtime.manager.controller.ingress","msg":"Reconciler error","name":"test","namespace":"test-namespace","error":"ingress: test-namespace/test: WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \"https://sts.us-west-2.amazonaws.com/\": dial tcp: lookup sts.us-west-2.amazonaws.com on xxx.xxx.xxx.xxx:xxx: read udp xxx.xxx.xxx.xxx:xxxxx->xxx.xxx.xxx.xxx:xxx: read: connection refused"}

coredns が原因を調べていたところ、coredns は Fargate だとデフォルトから設定変更が必要とのことを公式ドキュメントで確認しました。

docs.aws.amazon.com

coredns を Fargate 用に設定変更

ドキュメント通りに設定すればOKです

profile の作成

$ aws eks create-fargate-profile \
    --fargate-profile-name coredns \
    --cluster-name [EKS クラスタ名] \
    --pod-execution-role-arn [pod 実行 IAM ロール ARN] \
    --selectors namespace=kube-system,labels={k8s-app=kube-dns} \
    --subnets [サブネットID1] [サブネットID1] [サブネットID1]

アノテーションの削除

$ kubectl patch deployment coredns \
    -n kube-system \
    --type json \
    -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'

設定を完了したら pod を再起動すれば OK です

$ kubectl rollout restart -n kube-system deployment coredns

動作確認2

再度 ingress を apply すると aws-loadbalancer-controller から作成した ALB が作られているのを確認できます。

$ aws elbv2 \
  describe-load-balancers \
  --query "LoadBalancers[].LoadBalancerName" \
  --region us-west-2 \
  --output text

k8s-eksmanag-test-xxxxxxxx

終わりに

今回は EKS on Fargate で aws-loadbalancer-cotroller を導入して動作確認するところまではまった点を書きました。この後 EKS の構築を terraform 化したのでそのことについて書こうと思います。

みらい翻訳ではSREを募集しています

EKS の構築・運用に興味がある方、その他運用改善など SRE に興味がある方を募集しております。 ご興味がおありの方は下記のリンクからご応募・お問い合わせをお待ちしております。

miraitranslate.com

Mirai Translator®︎ の開発体制をチームトポロジーで表現してみた #ちいとぽ

こんにちは。プラットフォーム開発部 EMのchika (@chika-mirai) です。

以前(もう半年近く前ですが)、こちら↓のエントリで書籍「チームトポロジー」の感想を書かせていただきました。

miraitranslate-tech.hatenablog.jp

この中で以下のようなことを発していたのですが、

今の自社の開発チーム編成は4つのチームタイプと3つのインタラクションモードに当てはめるとするならどう表現されるだろうか?というのは読者なら気になるところです。今度自分も描いてみてチーム内で話してみようかなと思います。

これを個人的な宿題にしたままだったので、今回はこの宿題提出のためのエントリです。

あらためて、チームトポロジーとは

↑のエントリにも貼っていますが、聞いたことがない方、あまり覚えていないよ、という方は以下の神スライドをご覧ください。

slide.meguro.ryuzee.com

チーム設計のためのテンプレート

チームトポロジー図のテンプレートは、GitHubにて Miro 、Figmaパワポ など様々なお絵描きツールに対応したものが公開されています。

github.com

弊社ではMiroを導入しているので、今回は コピペで使えるMiro用のテンプレを使って描いてみることにしました。 (Miroに組み込めるテンプレートもあったのですが、こちら気づけず。まぁ次の機会に。)

書籍で描かれている図とイメージが少し異なりますが、このテンプレートでは下の図のように表現されています。

Team Topologies team shape templates

描いてみた

さて、上記のテンプレートを使って、今のMirai Translator®︎の開発チーム体制を表現してみたのがこちらです。

Mirai Translator®︎開発体制(現在)

以下、チームタイプごとに補足説明をします。

ストリームアラインドチーム

  • プロダクトチーム:Mirai Translator®︎の機能開発を行うメインチーム
  • フロントエンド刷新チーム:フロントエンドのリアーキテクチャを行う目的で結成されたプロジェクトチーム

    • プロジェクトチームのため正しくはストリームアラインドチームではないが、便宜的に当てはめて表現
    • UI/UXチームとはガッツリ協業している
  • 認証認可基盤チーム:認証認可基盤を構築しているチーム

    • 将来的にプラットフォームチームになる

イネイブリングチーム

  • QAチーム:ストリームアラインドチームにE2Eツールを提供したりテスト観点・ケースレビューを行う
  • アーキテクチャチーム:バックエンドのリアーキテクチャを推進するチーム
    • 今はアーキテクチャ基盤の設計・構築も行っているが、今後はプロダクトチームとコラボレーション(慣れてきたらファシリテーション)してマイクロサービス化を支援する予定
  • SREチーム:CI/CDパイプラインの構築や開発プロセスに沿った環境整備を行うチーム
    • 現在はIaCのサポートや環境周りの相談などでリソースとしての開発支援が多い
    • アーキテクチャチームにはマイクロサービス基盤の構築面で協業

コンプリケイティッド・サブシステムチーム

  • 翻訳エンジンチーム自然言語処理機械学習などの専門領域の技術を扱うチーム(プラットフォーム開発部とは別部署のエンジニアリング部)
    • アプリケーション(プロダクトチーム)からは利用するだけの状態(X-as-a-Serviceモード)
    • デプロイについてはインフラチームに依頼する形で、ここだけ3つのインタラクションモードに当てはまらない状態

直近の課題

UI/UXチームが独立している

UI/UXチームがプロダクトチームとは別チームとして介入したり依頼を受けたりする立場になっていますが、本来はストリームアラインドチームの一員であるはずと考えています。

SREチーム、QAチームはプラットフォームチームにしたい

現在のSREチームはメンバーの稼働を割いて他チームを支援することがまだ多いためイネイブリングチームで表現しましたが、CI/CDパイプラインを強化したりコンテナ基盤を整備したりで徐々に「ツール提供」による支援範囲を広げていますが、これをより進めてプラットフォームチームとして機能させられたらと思っています。

またQAチームも開発の最後の「QAフェーズ」を担っている状態ですが、この「QAフェーズ」は開発のボトルネックになることがあるので、E2Eテストほか各種自動テストツールをストリームアラインドチームに提供し、エンジニア主導で必要十分なテストを実施できるようにするプラットフォームチームとなるのが理想です。

一方でQAはテスト以外にも要件・設計段階での品質観点のレビューや探索的テストなどの専門的スキルを開発サイクルの中で発揮するという役割もあるため、最近はプロダクトチームのスクラムの一員としても活動し始めています。

逆に独立したQAチームは必要なの?という疑問がよく出るQAですが、このあたりは今回の話題と離れてしまうので別の機会に触れられたらと思います。

少し先の展望

ここから先はあくまで私個人の想像や案であり、会社の具体的な方針を表したものなどではないことを先にお断りしておきます。

上記の課題などを踏まえて少し先の体制案を表したのがこちらです。

Mirai Translator®︎開発体制(近未来)

フロントエンド刷新を行っていたメンバーのほか、UI/UXもプロダクトチームの一員として活動し、プロダクトチームが他の外部のチームに依頼せずとも開発サイクルを回せるようになる状態(フルサイクルな開発チーム)を目指しているイメージです。

SREとQAはプラットフォームチームになっています。

さらに先の将来の展望

組織構造はチームの成熟度、メンバーの入れ替わり、ビジネス環境の変化などにより適切なタイミングで進化させる必要があります。

今後このチームがどのような進化を遂げるかはまだ分かりませんが、せっかくなので少しだけ進化系を表現してみたいと思います。(こちらも先と同じく個人的な案です)

Mirai Translator®︎開発体制(将来案 (仮) )

進化のポイントは大きく2つです。

1つは新しいプロダクトが増えることに伴い、専任で開発するストリームアラインドチームが増えるという、ごく自然な進化です。具体的にどんなプロダクトが増えるかによってチームの形は異なると思いますが、ここではプロダクトチームと同じとみなして重ねて表現しました。

チームトポロジーの考え方として、このような二層にわたる表現をするようなチームがあり得るのかというのは定かではないですが、このような表現をしてみると私の想像した形に近いのであえてこのままにしました。

もう1つは、翻訳エンジンチームとコラボレーションするストリームアラインドチーム(翻訳API開発チーム(仮称))と共にプラットフォームチームを形成するという進化です。

これは、翻訳エンジンチーム自体がインフラチームに依存していたり、一方でプロダクトチームとのインタラクションが不足することによりデリバリー速度が上がらないという課題が顕在化する可能性があるため、翻訳に関するDevOpsを推進していくことが近い将来必要になるだろうと考えて表現しました。

描いてみた感想

これまで開発チーム体制をドキュメントで表現する際の描き方は特にテンプレートがなくチーム間の連携も表現しようとするとセンスが要るなぁと感じていましたが、チームトポロジーでの表現を用いれば、そのチームの役割や他チームとの関わり方もスッキリと分かりやすく描けるので、チームタイプやインタラクションモードの共通理解さえあれば、ツールとして非常に便利ですね。

(ただ慣れないと「このチームはどっちのタイプだろうか?インタラクションは...」と本質的でないところので悩みがちw)

なお前回の投稿でも触れましたが、チームトポロジーで表現するチームは前提として「スモールチームでアジリティのある動き方が出来ていること」が必要なので、チームトポロジーで表現することが相応しいかどうかという問題はあります。

私たちMirai Translator®︎開発チームではリーンな開発を目指してスクラムやリアーキテクチャなどに取り組んでいますが、早くこれらが実を結び理想的なチームトポロジーに沿って進化させていけると良いなと思っています!

We're hiring!

みらい翻訳では、私たちと一緒に開発チームを進化させていけるエンジニアを募集しています! ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。

miraitranslate.com

JaSST'22 Tokyo に参加して、考え始めたこと

こんにちわ。プラットフォーム開発部 QA の hark です。

QA チームでは、昨年より QA プロセスを改善する取り組みを継続的に行っています。その一環で、今年 3 月、JaSSTソフトウェアテストシンポジウム-JaSST'22 Tokyo に参加する機会をもらい、色々なセッションを聴講してきました。それぞれの現場で、QA 担当者が感じている生の声をたくさん聞くことができたこともあり、「○○できたらいいな」と漠然と思っている状況をやめて、「○○するには、弊社の場合は何から始めたらいいのかな」と具体的な一歩目について考えるようになりました。

今回は、興味深かったセッションを数例ピックアップして共有したいと思います。合わせて、まだネタにも種子にもなっていないですが(いずれ芽が出たら改めてレポートしたいと思います。)、JaSST 2022 をきっかけとして、自分の中で燻り続けている「QA とは」についても紹介します。

  • [JaSST'22 聴講レポート 1] QA と Verification と Validation
  • [JaSST'22 聴講レポート 2] QA と フィードバック
  • [JaSST'22 聴講レポート 3] QA と Vision

 

なお、Mirai Translator開発チームでは、「5分だけ勉強会」というエンジニアメンバーの勉強会を毎朝開催しています。(以下のWantedlyの記事参照)

www.wantedly.com

当記事は、この「5分だけ勉強会」で 4 月に発表したものです。社内で共有してから既に3ヶ月が経ち、改めて自分の書いた記事を読み直して、自分の理解が少し進んだかなと思ったので、下記の項目を追加しました。

  • [3ヶ月経って振り返る] Verification と Validation と フィードバック

 

[JaSST'22 聴講レポート 1] QA と Verification と Validation

(出典:品質エンジニアリングと自動化後の世界 / Quality Engineering and the Post-Automated World - Speaker Deck スライド30より)

 

自動テストのコードを書いているときに、「validateXXX()」や「verifyXXX()」といった関数を作ったことは何度もありますが、自分は両者を気分で使い分けていたなぁと反省しました。ともあれ、自分は以下のように理解しています。

 

最終成果物が事前に取り決めたルール(仕様)を遵守しているべきであり、その当たり前を保証(確認)する観点が、いわゆる Verification 視点だと理解しました。開発プロセスの位置によっては、機能テストだったり、E2Eテストだったりしますが、いずれにせよ、仕様が明確に定義されている Verification は、いつ、誰が、テスト仕様書に沿ってテストを実施しても、マル・バツは一意に決まります(基本的に再現性あり)。「テスト」と言われた時に一般的に誰もが抱くイメージが、この Verification ではないでしょうか。

振り返ってみると、現在の QA チームに配属されるまでは、「仕様書という名の正解データ(正例)」と「実際の動作」を比較して、マル・バツをつけることがテストの全てだと思い込んでいました。以前、 TE(テストエンジニア)として別現場で働いていた際、『神の手』と称される同僚がいて、というのも、その人にテストさせるとなぜかバグが発掘されるのでそのように呼ばれていたのですが、「自分もそうありたい」と飲み会で上司に語っていたことをついでに思い出しました。(今でも、その気持ちはありますけど。)

 

対して、Validation 観点は、(乱暴に言ってしまえば)Verification という囲いの中から踏み出して、その外側から全体を観ているように理解しています。「ユーザが何に不満を感じていて、この機能を追加することでそれは解消するのか?」「ユーザの要望に対して正しい解決策を提示できたのか?」「そもそもユーザにとって、この仕様はありなのか?」といった根源的な問いに向き合うために、さまざまな物差し(評価軸)を使って多角的に評価することが  Validation 視点だと思います。

 

Verification は自動化しやすい領域であり、「QA がやるべきこと」ではあるものの、例えば SaaS 型のテスト自動化ツールなどを利用することによって、「QA の属人的な業務」からは少しずつ外れていくのかもしれません。だからこそ、「ユーザの要望に対して正しい解決策を提示できたのか?」を追求する Validation について、QA として取り組む必要があるのではないか、と感じました。

しかし、実際は、Verification に加えて Validation も QA が専任で行うパターンがベースとなると思われます。結局、QA のやるべきタスク、やりたい業務は山積みとなり、それを開発プロセス終盤の QA フェーズで全部消化しようとしても無理なので、できる限りシフトレフトして、開発フェーズのより前の段階でタスクを消化して効率化を図り、その結果として生まれた余力で、開発サイクル全体として品質維持する旗振り(ファシリテータ)をやるのが、QA の Next Stage なのかなと、自分はそのように考えています。

 

 

[JaSST'22 聴講レポート 2] QA とフィードバック 

(出典:従来型QAからアジャイルQAへの進化方法 - Speaker Deck スライド20より)

 

(出典:品質エンジニアリングと自動化後の世界 / Quality Engineering and the Post-Automated World - Speaker Deck スライド33より)

 

アジャイルな開発において、「フィードバックのループ」は必要不可欠な要素です。 このループにより、日々の開発プロセス、行動、そして、プロダクトの品質が絶え間なく改善されていきます。

上記セッションを聞いて、QA が {送る、受け取る} フィードバックって何かを整理しているのですが、未だに納得のいく整理ができていません。(下記は整理中のたたき台であり、実在の組織や事実とは異なるケースを含んでいます。)

 

そんな状況ですが、下記の 2 点について、まずは個人として改善したいと考えています。

  1. ユーザの気持ちになってテストを行うこと
  2. 自動テストの声を聞くこと(自動テスト実施結果からのフィードバック)

 

  1. ユーザの気持ちになってテストを行うこと

まず、1 についてですが、QA には「開発プロセス終盤で行う E2E テストなど通して、出来上がったプロダクトを最初に触るユーザである」という側面があると考えます。だから、テストが完了したのであれば、その製品(機能)に対するユーザ観点の意見をフィードバックできるよなあ、そういう意識を持ってテストを実施しないといけないよな、そう再認識しました。

あと、これは別に本件に限定したことではないのですが、「(言葉は適切ではないかもしれませんが)徹底して粗探しして問題が検出されないことを確認する」ことが(改善のネタ出しとして役に立っているので)フィードバックだと思い込んでいたように感じます。

「今回の○○機能ですが、こういう点が使いやすかったです。」「■■がめちゃ改善しましたね。」みたいに、もっとポジティブなフィードバックもしよう、と言うことが、今、自分が最も強く意識していることです。

 

  1. 自動テストの声を聞くこと

次に、2についてですが、テスト結果(失敗頻度、各種処理時間、、、)が蓄積したものも立派なデータです。そのデータ(テスト結果)を入力として、見える化インサイト)して分析を行い、継続的にモニタリングすることによって、気づきが得られるのではないか。PASS すれば無問題、バグが検出できれば万々歳で、みるのはそこだけだ、そんな認識でいたかなぁと反省しました。

上記セッションを受け、振り返ってみて、以下のような端緒に気づきました。重要度的に今すぐ手はつけられないかもしれませんが、いわゆる「20%ルール」の範囲で考えてみる、などしているところです。

  • 追試したら PASS するので「{たまに、まれに、ごくまれに} 失敗するテストケース」だとは認識していたが、自分はそれ以上の調査は行なっていなかった。
  • 機能が追加される度に RegressionTest の実施時間総計が右肩上がりに増えているけど、例えば半年前に比べてどれくらい増えたのか。そもそも、今どれくらい時間かかるのか、自分は把握できていなかった。(都度、調べて回りたいわけでなく、それらが勝手に蓄積されてその情報に気軽にアクセスできるような仕組みを入れておきたい。)

 

 

[JaSST'22 聴講レポート 3] QA と Vision 

「PdMと考えるQAとプロダクトマネジメント」の中で紹介されていたのですが、これまでの変遷とこれからの未来をビジュアライズした資料で表現し、それを社内で共有しているとのことでした。


調べてみたら、pmconf 2021 でも発表されていたので、既にご存知の方も多いかと思います。

2021.pmconf.jp

 

このセッションで、ホワイトボードや大きめの模造紙にイラストや図を使って会議の内容を見える化するファシリテーターや板書係の手法「グラフィックレコーディング(グラレコ)」を初めて知りました。

ロードマップやビジョンという硬派なものを、このようにビジュアル化(抽象化、見える化)することで、誰もがイメージしやすく、訴求度が高まっているのではないかな、と自分は感じました。

あと、蜂須賀氏は以下のように述べておられました(と記憶しています)。
「このロードマップは、年代別に並べると一枚の絵になるようにしている。ドラゴンボールの単行本の背表紙みたいな。そして、何十年後か後に、これを合体させて、どんな風景が描かれるのか、楽しみだ。」

cf. ドラゴンボール 背表紙 - Google 検索

 

上記テーマは、PMという大きな枠組みに限ったものではなく、どのドメインでも通用する身近な話だと私は捉えており、だからこそ、強烈に印象に残りました。そこで、試みに、2022年度の個人的なロードマップを同じように絵にしてみようと 2 回ほどチャレンジしてみたのですが、いずれも早々に断念しました。グラレコについて書かれたブログを漁って、グラレコについてわかったつもりになったのですが、そもそも図で描こうにも、描き慣れていないとそこでつまづいてしまい、全く立ち上がれません。例えば、ヒトを書こうとするだけでも、「自分はセンスないなぁ」と滅入る始末です。ただ、グラレコはとても興味があるので、長期的に取り組んでいこうと考えています。

 

[3ヶ月経って振り返る] Verification と Validation と フィードバック

 

新機能開発ともなると、開発プロセスには多くの関係者が存在しており、議論と手渡しと修正を繰り返しながら、要求定義、要件定義、各種設計、仕様書を FIX していきます。別の言い方をすると、「お客様の要望」は多くの関係者の手を渡りながら「各種仕様」を生成して、それを参照して実装を行い「サービス(もの)」を生み出しています。

とどのつまり、多種多様なコミュニケーションがそこに介在している以上は、その過程で、情報が欠落したり、誤った解釈により誤情報が追加されたりするリスクがあります。

ソフトウェア開発やシステム開発が「壮大な伝言ゲーム」に喩えられることが多いのは、その辺りに深く由来しているのではないでしょうか。

 

「壮大な伝言ゲーム」において、「ユーザが本当に求めているもの」に近づけるためにできることは、各フェーズで検証と確認を行うことではないか、と思います。

  • 「前フェーズから聞いた内容」と「次フェーズに伝える内容(≒担当者の認識)」が合致していることを検証(Verification)する
  • 最初に与えられたお題「ユーザの元々の要求」と「次フェーズに伝える内容(≒担当者の認識)」が合致していることを妥当性確認(Validation)する

QA はリリース水際の最終行程に近い位置にあり、またテストに紐づくあらゆる要素がタスク領域に含まれているため、検証(Verification)と妥当性確認(Validation)が強く求められます。なので、検証(Verification)と妥当性確認(Validation)は QA が気にしていればいい話なのかなと思っていましたが、開発プロセス全体で各々が意識することで、より効率的にことが運ぶのではないか、そう感じました。

 

 

この喩えを踏襲すると、「ユーザの元々の要求」と合致しているのかを確認(Validation)するとは、物理的にも遠く離れたエンドユーザ(または、導入先の情報システム部門、または、代理店など)とのフィードバック経路を確立するという意味なので、そもそも高コストで高負荷なアクションとなります。さらには、端まで到達してから評価して、やっと、ずれを把握できたとしても、引き返せないところまで来てから判明しても打つ手はほとんど残りません。だから、せめて、隣接する2者間(2社間)で検証(verification)して結果をフィードバックする仕組みの方が、両者のずれを都度補正できて効率的です。

また、アジャイル開発であれば、(理想的には)スプリント毎にユーザフィードバックを得られるので、お手を煩わせるとはいえ、ズレが小さいうちに確認してもらって、フィードバックをこまめに得ることで効率的に補正することになります。

 

ということで、3ヶ月間に書いた自分の記事を振り返って見ると、妥当性確認(Validation)が大事だ、QA でも取り組みたい、と3ヶ月前に簡単に口にしていましたが、自分が考えているほど容易に始められるものではありませんでした。やることは山積みであり、掲げた目標も遥か遠くにあり霞んで見えませんが、できることから、匍匐前進でもいいから進んでいきたいです。

 

 

ちなみに、グラレコは自分には高すぎる壁だと反省したので、まずはヒトが描けるようになろうと考えています。当初、本記事中では棒人間を描いていたのですが、あまりにも殺風景だったので、自分でもできそうなものを探しました。色々彷徨って、「イラストプレゼン研究所」というサイトを見ながら日々練習しています。まだ1ヶ月にも満たず、へたっぴなままですが、毎日最低でも10人書くようにしています。

 

siri-illust.com

 

 

最後に

みらい翻訳では、品質向上に一緒に取り組んでくださるQAエンジニアを募集しています。ご興味のある方は下記リンクからお問い合わせください。

miraitranslate.com