Libry Developers Blog

Libry開発チームによるプロダクト開発ブログ

LibryのUIデザイナーってどんなことしてるの?

LibryのUIデザイナーってどんなことしてるの?

f:id:libry:20210716132230p:plain

こんにちは!UIデザイナーの神尾です。文章を書くのが大好きでして、株式会社Libry(リブリー)に入社して1ヶ月ほど経ったので、早速書いていきます。今回は弊社UIデザイナーの仕事内容や、チームとの関わり方、プロダクトについても簡単に紹介していこうと思います。よろしくお願いします!

Libryとは?

f:id:libry:20210716133044p:plain

「Libry」は、中高生向けのデジタル教材プラットフォームです。デジタル教材と聞いても、あまりピンと来ない方も多いのではないでしょうか。中高生のみなさんが日常的に使っている「教科書」や「問題集」を、タブレットやパソコンといったデジタル機器から閲覧できることに加え、学習履歴のデータを蓄積し、そのデータをもとに一人ひとりの個別最適化された学習体験を提供する、それがLibryです。

私たちがやりたいことは、ただデジタル教材を配信するだけではありません。一人ひとりの興味・能力・状況に合わせて、適切な指導や情報を、適切なタイミングで提供できるようになること。これが私たちが目指しているところであり、UIデザイナーもまた、プロダクトを通じて目指す場所を形づくっている最中です。

働く環境について

UIデザイナーはプロダクトづくりの主体となるチームにも所属し、プロダクトデザインに取り組んでいます。弊社ではLeSS(Large Scale Scrum)という大規模スクラムの手法を採用しており、現在は2チームでプロダクトを開発しています。LeSSについて詳しく知りたいという方はこちらを参照してみてください。生徒さん向けのプロダクト「Libry」をメインで担当するAbacus(アバカス)チームと、先生向けのプロダクト「Libry for Teacher(リブリーフォーティーチャー)」を担当するBeaker(ビーカー)チームです。(2021年6月末現在)
教育業界のプロダクトなので、チームの名前もAbacus=そろばん、Beaker=ビーカーという業界ならではの名前を採用しています。自身はAbacusチームに所属しています。(最初はAbacusという名前が覚えられずにあたふたしていました。)

どんな風に仕事しているの?

会社としての目標を目指し、その目標を達成するために何を実現していくか、をチームで取り組む仕事の粒度まで噛み砕いたものを、各個人の1週間の予定表に組み込みながら進めていっています。1週間のサイクルで仕事を進め、進捗を共有し、また次の1週間の予定を立て、仕事を進める。毎週末には全社を通じ、目標に対しどれだけ進んでいるかを共有し合い、他部署がどんなことをしているのかも把握することができます。

全体の仕事の進め方はざっくりとこんな感じですが、UIデザイナーの仕事について、もう少し詳しく説明していきたいと思います。
UIデザイナーは「ユーザーに届けたい体験」が書かれたドキュメントを元にUIデザインをしていきます。ドキュメントには体験が細かく書かれている場合もありますが、「ユーザーに届けたい本当の価値」が書かれていることと違う、と判断した場合は、PO(プロダクトオーナー)と相談して、ドキュメントを改善しながらUIデザインに落とし込む場合もあります。いわゆるUXと呼ばれる、体験設計のところから考えるケースも多く、UIデザイナーという職域ではありますが、枠をこえて柔軟に仕事をすることが可能です。もちろんUIのクオリティもしっかり担保するため、UXだけではなく表層の部分もしっかりと考え抜きデザインに落とし込んでいます。

リブリーならではのやりがい

これまではリブリーでの仕事の仕方などをお話してきましたが、リブリーならではのUIデザインのやりがい、というのを最近感じ始めたので書いていこうかなと思います。

UXからUIまで、しっかりと考え抜く楽しさ

リブリーでは大切にしている7Valuesという行動指針があります。その中のひとつ「Think Deeply(本質を見極める)」が、リブリーでデザインをする上でとても重要で、とても楽しいポイントだなと感じています。
プロダクトは、実際に教育の現場で使われています。学校で教科書を開くのはもちろんですが、先生から宿題をLibryを通じて出されることもあります。なので、先生や生徒さんが「使い方が分からなくて教科書を開けない」「宿題を出せない」「宿題を解きたいけれど、どこから解けるのか分からない」といった体験が、学習をサポートするどころか、阻害してしまうことになってしまいます。
ですので、一般的なスタートアップのプロダクトでよくある「スピード重視で作って、リリースしてみて、ユーザーの反応を見て改善していく」というサイクルでは回すのが難しい。代表の後藤の言葉で印象的だったのが、「ユーザーは実験体じゃない」という言葉です。私は教育の現場を実験場にしてプロダクトを磨き込んでいくのではなく、先生や生徒さんと一緒に良くしていくことがとても重要だと考えています。そのために「Think Deeply(本質を見極める)」ことが重要で、UXからUIまでをしっかり考え抜き、一番良い形で提供する、というところにメンバー全員がしっかり向き合っているなと感じました。ここが今まで私が働いてきた環境では無かった、リブリーらしいポイントなのかなと感じています。

一緒に働く仲間を募集しています

Libryは現在一緒に働く仲間を募集しています。
ご興味のありましたら、ぜひご覧ください。

「技術的負債」についての考察〜「技術的負債」が開発スピードを遅くする魔物〜

f:id:libry:20210531100521j:plain
「技術的負債」についての考察〜「技術的負債」が開発スピードを遅くする魔物〜

こんにちは!
株式会社Libryの開発部にてエンジニアをしている長谷川です。
今回は2回目の記事投稿です。
前回はフロントエンドの方向けにVue3.0の記事を書きましたが、今回はもっと抽象的なお話をしようと思います。
エンジニアではない方々でサービスを抱えている企業に勤めている方にも是非眺めてもらえれば嬉しいです。

今回は「技術的負債」についてお話しようと思います。
皆さんも運用開発を経験しているとこんなことありませんか?

「開発が進むにつれて徐々に障害が出る確率が増えてきた」
「開発初期と今とでリリースされる工数がかなり違いがある(増えてきた)」


これは技術的負債がどんどん蓄積することによって起こっているものです。
開発の「生産性」が落ちてきたと思ったらまずはサービスの「技術的負債」を疑ってみてください。

そもそも技術的負債とは


そもそも技術的負債とはなんでしょうか?

一般には、「早さ」を求めて構築されたシステムの構造的課題が、徐々に蓄積し、債務であるかのように開発速度そのものを遅くしていく現象のこと。


これを技術的負債と言われています。

特にこの言葉を使う目的としては、

エンジニアと非エンジニアのコミュニケーションに使われる言葉

として利用されます。

もう少し、想像しやすく表現すると、

システム開発が徐々に困難になっていく理由は、「ジェンガ」のようなものです。
ゲームの初期段階でのジェンガは、簡単にブロックを抜き出したり、積み上げることができます。
この時はゲームはスピーディーに進んでいきます。
ところが、ゲームをしばらく続けると、バランスを取るのが急激に難しくなっていきます。
これは慎重に抜き差ししないと崩れてしまうためです。
様々な要件の変化によって形作られたジェンガは、徐々に不安定に、積み上げるのが難しく時間がかかるようになります。
これが開発が遅くなる現象とよく似ています。

ookawajengaIMGL1760_TP_V4

なぜ技術的負債の言葉があるのか

技術的負債は、「エンジニアと非エンジニアのコミュニケーションに使われる言葉」とお伝えしました。
では、なぜコミュニケーションの言葉として生まれたのでしょうか?

よく、技術会社にある現象なのですが、
開発内部を知らない人からすると開発当時や昔はすぐに開発できたのに、「予想外のタイミングで、予想外の形で」開発が進まなくなるという現象が発生します。

はたからみると、同じくらいの機能開発に見えるのになぜか、3年前より開発スピードが遅い。。
これは、サービス内に「技術的負債」が存在するがために、開発スピードが遅くなってしまっているのです。

結局技術的負債で何を言い表したいのか

技術的負債の負債は、「放ってはいけない」「定期的に返済すべきもの」として負債という言葉を使われています。
それなのに負債を返済できず、どんどん負債が膨らんでしまうと取り返しのつかないことになってしまいます。

これは技術的な負債にも言い表せます。
負債が大きくなるにつれて、開発スピードがどんどん落ちていき、ビジネス側にも影響が出てくるほどの状態になってしまいます。

技術的負債は、開発サイドだけではなく、ビジネス全体、組織全体で対処する必要のある負債なのです。

技術的負債をめぐる議論

技術的負債という言葉は、アメリカのコンピュータ技術者であるウォード・カニンガムによって1992年に提唱された概念です。
彼は経験レポートの中で、次のように語っています。

最初のコードを出荷することは、借金をしに行くのと同じである。小さな負債は、代価を得て即座に書き直す機会を得るまでの開発を加速する。
危険なのは、借金が返済されなかった場合である。品質の良くないコードを使い続けることは、借金の利息としてとらえることができる。
技術部門は、欠陥のある実装や、不完全なオブジェクト指向などによる借金を目の前にして、立ち尽くす羽目になる

システムの開発を進めるに連れて引き起こされる開発時間の増大という「現象」を巡って、それらを議論するためのコミュニケーションの言葉として広く使われることになりました。
しかし、「技術的負債」という言葉は、会計上も経営指標においても計上されるわけではない空想上の概念にすぎません。
要は、何をもって測定され、どのような返済手段があるのかといった議論も尽くされることなく、広く用いられるようになりました。
その結果、「技術的負債」という言葉の存在で何かを説明したと思い込む人々や、ソフトウェア開発における経営上の問題について関心を抱かない経営者によって、むしろコミュニケーションを断絶する言葉になってしまっているケースがあるように思います。

まとめ

ここまで技術的負債について話してきましたが、Libry開発部としてもサービスの「技術的負債」を問題捉え、返済を積極的に行っております。
具体的には、4月より、

全体の開発工数の30%を「開発改善タスク」を消化する工数
※ 30%というのは時期によっては20%に修正するなど見直しがはいる可能性はあります。

を設けました。
エンジニアとしても今後の事業の発展のためにも今年は新機能開発と共に技術的負債の解消にもしっかり努めていきたいと思います。

もし、技術的負債の解消にも興味がある人はお話聞いてみませんか?
是非お待ちしております。

「エンジニアリング組織論への招待」で輪読会を一通り終えた話

f:id:libry:20210107224638p:plain

こんにちは!
株式会社Libryの開発部にてエンジニアをしている田邊と申します。
最初の挨拶以来の投稿です。
Libry開発部では部署全体の業務効率を上げることを目的に(個人個人のスキルアップをサブ目的に)毎週業務時間から1時間いただき「勉強会」を実施しています。
2020年9月に勉強会をLT会形式から輪読会をメインに据えた形式に変更し、ようやく1冊完了したので、実施した背景・目的や進め方・感想・反省などをお伝えしたいと思います。

輪読会を開催した背景

詳細は本題から外れるので省きますが、輪読会を始めたきっかけは「以前行っていたLT会形式の勉強会では発表準備の負荷が高く、機能してこなくなった」というネガティブなものでした。でも、せっかく業務時間を1時間頂いて勉強会させてもらっているのだから、「勉強会自体をなくす」という選択肢は取りたくなかったです。
そこで、準備の負荷が低そうな輪読会なら継続できそうに思い、輪読会の企画が始まりました。また、個人的には輪読会形式にすることによって下記のような期待値も持っておりました。

  • 弊社のエンジニア採用が活発になってきたのが2018年頃からということもあり、開発部メンバーの多数が2019年以降の入社であったり、メンバーの前職のバックグラウンドも様々であったため、チームの共通言語を作っていきたかった。
  • チームの人数も増え、全員がフルリモートワーク状態(稀に出社してる人もいましたが)だったので、双方向のコミュニケーションを増やしたかった。

対象書籍の選定について

「エンジニアリング組織論への招待」を輪読会の対象書籍にすることが決まりました。

上記の書籍を選定した理由は下記のように多岐に渡ります。

  • エンジニアリングマネージャーが事業戦略の関係上、一時的に開発部から離脱した(今は戻ってきている)。
  • スクラムが上手く機能していなかった。そもそもスクラムの経験値が豊富なメンバーがおらず、一部のスクラムイベントにおいては目的の共通認識が取れていないなどの理由で、MTGが長くなってしまうといった問題も生じていた。
  • コミュニケーションを促進させたかったためチームを複数に分けたくなかったが、技術分野ごとの得手不得手がバラバラであるため、技術のみを扱うような書籍は避けたかった。
  • 私がいつまでも積読して読み進めないため、ゴリ押しした。

そして、対象書籍が決まったので、全員分の書籍を弊社の書籍購入制度を利用して、実際に輪読会が始まりました。弊社のvalueの一つである "Love Growth" に基づいた福利厚生の詳細はこちらの記事を御覧ください。

tech.libry.jp

輪読会の進め方

弊社輪読会では以下のような進め方を行っております。

事前準備

  1. 当番を毎週一人決める(持ち回り)。
  2. 当番は担当する範囲を精読して、要約発表の準備をしてくる。資料作成は必須ではないが、最低限自分用のメモ程度は作成し、分かりづらいと感じる箇所は資料作成する。
  3. 当番でない人も事前に精読してくる。
  4. 当番でない人は投げかけたい質問やコメントをなるべく用意しておく。

輪読会のアジェンダ

全体が40分と決まっていたので、以下のようなアジェンダで実施していました。

# 内容 時間 目的
1 当番から要約発表 10分 読んできた内容を思い出すための時間であり、
その後の議論を活発にさせるための準備
2 質問&議論タイム 25分 読書して響いたポイントや
実践できていなかったことなどを
投げかけ話し合います
3 振り返りと
実践に向けたAction決め
4分 輪読会の進め方の振り返りと
#2で出た内容で実践したいものがあれば
スプリントレトロスペクティブの
議題に上げます
4 次回の範囲と当番を決める 1分 スムーズな運営のために
このタイミングで決める

実施した感想と反省

よかったです!部署内で簡易にアンケート取りましたが、満場一致で「次回の勉強会も輪読会やろう」となりました。 以下、具体的に良かったこと・改善したこと・したいことを書き並べます。

よかったこと

  • 当番以外はそんなに準備に時間かからない。書籍を事前に読んでコメントや質問を考えておくだけ。
  • 開発部内で共通言語・価値観・課題観・暗黙知の醸成ができた気がします。「エンジニアリング組織論への招待」の輪読会では開発における「不確実性」という言葉は全員ほぼ同じ定義で認識できたはずです。
  • 弊社開発部がなぜスクラム開発という開発手法を用いているのか納得感が上がった。
  • 直接的に解決に至っていない問題に対しても、チーム内で「もしかしたら自分だけが課題と感じている!?」と感じることがなくなって精神衛生上良かったです。
  • 以下は輪読会を1周終えた後のアンケートに自由記述(任意回答) に記載されていた回答。
    f:id:libry:20210107221636p:plain
    アンケート回答結果

改善したこと・したいこと

  • 途中で進め方を更新したのですが、輪読会を開始した当初は議論した内容を具体的にActionに起こさなかったので、「いい話だったなー」で終わってしまっておりました。「エンジニアリング組織論への招待」の内容はほぼ毎週濃い内容が多かったので、温度感が高まったときにもっと早くにActionを起こしておけばよかったです。
  • 誰かしらが1回通しで読んだことある書籍が良かったです。稀にあまり議論が深まらないパート(ひたすらアジャイルの歴史を掘り下げたパート等)もあって、そういう週に対しては範囲を調整するなどの事前対策を打てたと感じました。
  • 書籍の選定が難しいです。「エンジニアリング組織論への招待」に関しては程よく抽象度が高く、満場一致で決められました。次回の輪読会ではもう少し技術に寄った書籍を選定する予定ですが、3冊くらいまで絞れたものの、それからは平行線で、取り急ぎ3チームに分けて開催して意見をヒアリングして見る予定です。もし機会があればこの続きも執筆できればと思います。

まとめ

弊社では業務時間を使って輪読会を開催しているため、少々特殊な環境ではありますが、ご参考にしていただければ幸いです。
なお、弊社はこれまで以上に採用を強化中です。
会社紹介スライドも更新したので、興味のある方はぜひご覧ください。

speakerdeck.com

以上、Libry開発部の輪読会のお話でした。

チームでサーバ接続設定を共有した話

f:id:libry:20201224110402p:plain
チームでサーバ接続設定を共有した話

こんにちは、Libryでテックリードをやっている中村です

Libryではチームの開発効率を高めるために日々様々な取組をしています

Docker や GitOps なんかは良くブログとかでも注目を浴びて取り入れているチームも多いと思いますが、 今回は地味で枯れた技術だけれど重要なサーバ接続設定 ssh_config を共有した話を取り上げたいと思います

対象読者

  • サービス・プロダクトを複数抱えている
  • サーバーにSSH接続する機会が間々ある
  • SSH接続設定が原因だったトラブルシューティングをしたことがある

ssh_config とは

ssh_configSSH接続をする際に指定するコマンドオプションを設定ファイル化して管理する仕組みです

設定は任意に名付けできる Host 毎にまとめられ、 ssh {Host値} で接続できるようになります

また Host にはワイルドカードを含めて共通設定を切り出したり、 Include を利用して設定項目を Host 値に縛られずにモジュール化して共有できたりもします

ssh_config の構成図

Libry では下記の構成で設定値をGit管理して共有しています

f:id:libry:20201208174325p:plain

設定ファイル展開

~/.ssh をGitリポジトリにするという大胆な手は取らずに、 ~/.sshconfigconf.d を注入する方式を採用しました

deploy.sh

#!/bin/sh

cd `dirname $0`

# ~/.ssh/conf.d ディレクトリ作成
if [ ! -d ~/.ssh/conf.d ]; then
    mkdir -p ~/.ssh/conf.d
    chmod 700 ~/.ssh ~/.ssh/conf.d
    echo '~/.ssh/conf.d ディレクトリを作成しました'
fi

cp .ssh/config ~/.ssh/config
echo '.ssh/config を ~/.ssh/config に配置しました'

# ~/.ssh/conf.d/* を上書き
for path in .ssh/conf.d/*; do
    if [ `find $path -name '*config' | wc -l` = 0 ]; then
        if [ -d ~/$path ]; then
            rm -fR ~/$path
            echo "$path を削除しました"
        fi
    else
        rm -fR ~/$path
        cp -a $path ~/$path
        echo "$path を ~/$path に配置しました"
    fi
done

~/.ssh/conf.d 全置換だと後述の ~/.ssh/conf.d/private とか扱えなくなるので for ループで conf.d を置換しています

例の libry-inc, hoge-inc あたりを消したい時は .gitkeep だけ入れた空ディレクトリを配置することで削除を実現できるようにしています

エントリーポイント

Include のみで構成します

.ssh/config

Include conf.d/private/preload_config
Include conf.d/common/preload_config
Include conf.d/*/config
Include conf.d/private/postload_config
Include conf.d/common/postload_config

Include は対象ファイル・ディレクトリが無くてもエラーなく動くので、わかり易さ重視で最終的に使わないにしてもとりあえず記述しています

~/.ssh/conf.d/private はgit管理せずに各人が自由に設定を記述できるようにしています

共通項目設定

設定が重複した場合は先に読み込まれた値が優先されるので上書きされても良い共通項目は postload_config に記述します

下記一例です

.ssh/conf.d/common/postload_config

Host *
    AddKeysToAgent yes
    ForwardAgent yes
    IdentityFile ~/.ssh/id_rsa
    ServerAliveCountMax 5
    ServerAliveInterval 30
    UseKeychain yes

非共通項目設定

あとは .ssh/conf.d/*/config を好きなように記述していきます

* の部分は . から始まる隠しフォルダで無ければ何でも良く、自社管理分とパートナー企業管理分みたいに分けたり、サービス毎に分けたり、設定数に応じて適宜細分化すると良いです

Libryの場合は .ssh/conf.d/libry-inc/config から更に Include 多用して複数ファイルに分けて整理していたりします

また、個人端末で利用している時や、まだコミットするまでも無い実験用サーバにアクセスするための設定は ~/.ssh/conf.d/private/config (ssh_config repository の外) に直接記述します

~/.ssh/id_rsa 以外のファイルを IdentityFile として使っている場合なんかは conf.d/private/preload_config に記述して対処できたりします

最後に

これをチームで共有すればサーバ接続設定関連のトラブルシューティングは、最新の ssh_config をプル&デプロイするかPR出して修正かけるかのどちらかで解決できるようになります

Libry ではこの ssh_config の運用が2年以上経ちますが、今のところ問題は発生していないので是非使ってみてください

以上、サーバ接続設定の共有話でした

QAスクラム活動報告(2020年4月〜7月)

f:id:libry:20201211142119p:plain
QAスクラム活動報告

こんにちは。
株式会社LibryにてQAエンジニアをしている大村です。

今回は、2020年4月から7月までやってきたQAスクラムの活動についてご紹介しようと思います。
QAスクラムとは、弊社の全サービスのQA業務を効率化および改善などを目的に発足したスクラムチームです。

Why?

なぜQAスクラムをやることになったのかというと、実は2019年までQA業務はPO(プロダクトオーナー)が管理し対応していた経緯がありました。

というのもLibryのサービス状況は、一般的なBtoCサービスのように急激なサービス拡大およびユーザー数増加とは異なり、大きなリリースは約半年に一回とユーザー数も学校の年度始まりである4月頃に増加するので、QAを実施するリソースの準備が難しかった現状がありました。

  • 累計フローダイアグラム

  • 直近の大きなリリースのリスト


そのため、これまでPOがリソース調整し、そのときの状況によってテスト作業者を用意して一緒にテストしてリリース判断を決め、Libry全体の品質状況をPOが一番把握している対応をしていました。

ですが、今後のスケールを考えると流石に限界ということで新たにQAチームを立ち上げ、チームの中でスクラムを実施しながら『急激なサービス拡大およびユーザー数増加、それにより開発メンバーが増えたときにも柔軟にQA業務ができる環境ができる』体制を作ることを目標に4月から活動をはじめました。

目標設定

4月に2021年までの新たなマイルストーンが確定し、8月頃に次の大きなリリースが予定されたため、8月までに一般的なQA業務の環境が最低限できていることを必須とし、さらにMVV(Mission / Vision / Value)をもとに目標設定を行いました。

一般的なQA業務として、「リグレッションテストできるテストケース」「次のテスト計画」「新規機能テストのテストケース作り」「テスト実施とフィードバック対応」などをイメージしていました。
また、ただこれらができるようにするのではなく、ワークフローやマニュアルなどを一緒に作っていくことも必要不可欠だと考えました。

IT業界内の企業で会社の経営理念などに使われているMVVですが、スクラムをやっているプロジェクトやプロダクトでもMVVを活用するとプランニングやレトロスペクティブなどで効果があると思いやってみました。

一部抜粋ですが、以下のようなMVVの達成を目指すことにしました。
ビジョンが先にある理由は、ビジョンがあってミッションができるイメージをしているためです。

  • V: ビジョン
    • Libryアプリが安全にリリースできる。
  • M: ミッション
    • リリース日から5日前で品質チェックが終わりリリースできる品質であることを証明できる。
    • LibryアプリがSTG環境でデグレが起きる件数が3件以下に減る。
  • V: バリュー
    • リリース前に実施する品質チェック作業(テスト、フィードバック、再テストなど全て含め)が最短5日で終われてリリースできる品質であることを証明できるようになる。
    • 前回の品質チェック作業との差分を可視化でき、次のリリース時にQA業務がスムーズに実施できる環境ができるようになる。
    • STG環境へデプロイする前にツールなどを駆使して最新の開発ブランチで定期的にテストが行えている。

QAチームのスクラム体制

幸いなことにスクラムで必要なPO・スクラムマスター・開発チーム1名、スクラムの最低人数3名を集めることができたためスクラムチームが作ることができ、私は開発チームのメンバーとして参加しました。
ですが、POは開発スクラムを兼任してQAスクラムを実施する形になったので、各スクラムイベントに対して次のような工夫をし、リソースを最適化することにしました

  • インセプションデッキやエレベーターピッチの作成
    • 代わりにMVVを作成
  • プロダクトバックログ
  • スプリントレトロスペクティブ
    • 課題などの共有は「デイリースクラム」で行い、改善については「スプリントプランニング」で一緒に実施
  • インクリメント
    • 必要に応じてスプリントプランニングで実施

活用しているツール

スクラムでは、バッグログやスプリント管理、ドキュメント管理など作業しているとツールの活用は必要不可欠です。
現在は、主にJiraスクラムを実施しており、「ロードマップ」「バックログ」「ボード」「レポート」「ページ」機能を日々活用しています。
この「ページ」機能は、Confluenceに連動しているため正確にはJiraとConfluenceを活用しています。

また、弊社ではConfluenceを活用する以前からドキュメント管理はesaを活用していたため、QAスクラムでは次のルールに基づいてドキュメント管理をesaとConfluenceの両方を活用することにしました。

  • esa
    • 社内展開したい情報
    • 開発チームに共有したい情報
    • ツールやサービスのマニュアル
  • Confluence
    • QAスクラムのみ関連する情報
      • 例: MVVやスプリントプランニングなど
    • ストーリーで発生する情報(※ただしesaにまとめるべきものはesaで管理する)
      • 例: テスト計画やMTGの議事録など

ロードマップ

MVVのミッションに関連するやるべきことの大項目を洗い出し、その各項目をエピック化して管理しました。一部ではありますが、次の項目が実際にエピック化したものになります。

  • QA業務の作業工程
  • 品質OK判断の共有
  • テスト計画
  • 不具合リスト管理
  • テスト準備
  • テスト開始
  • CI/CD対応
  • リグレッションテストの自動化
  • パフォーマンス計測

これらのエピックに関連するストーリーを必ず紐付かせて、エピックに関連しないストーリーがなるべく出ないようにすることで、常にエピックの見直しと無駄なリソースが発生しないように注意していました。

バックログ

ストーリー名には、一目で分かるように「スプリント番号」をタグ付けするようにしました。
例えば、『[S1] QAスクラムのMVVができる』と設定することで1週目のスプリントであることがスプリント名ですぐに把握できます。

image
※ スプリント番号タグ付例

これは、スプリントプランニングでページ上にストーリーのリンクを貼ったときに、自動的にリンクがストーリー名に表示変換してくれるJiraの機能を最大限に活用できると考えました。
また、ストーリーの詳細に「概要/目的」「Doneの定義」を書き込むようにし、ストーリーの詳細を常にスクラムチーム内で同じ認識を持てるように徹底するようにしました。
もし、ストーリー内で小さい作業が発生し、1ストーリー内で管理することが難しいときのみ、ストーリー上の「子課題を追加」からタスクを発行することにしました。

ページ

スプリントプランニングのページの場合は、ストーリー名同様に何週目のスプリントなのかといつ実施したのか一目で分かるように「次のスプリント番号」「年月日」をタグ付けするようにしました。
例えば、『[S2] 2020-04-07 スプリントプランニング』と設定することで管理するようにしました。
また、ストーリー内で発生した情報をページで管理した際には、これも一目で分かるように「ストーリー番号」「ストーリーID」をタグ付けするようにしました。
例えば、『[S1][QA-1] 2020-04 ~ 2020-08 MVV』と設定することでいつ何のために作ったドキュメントなのかをストーリー名で把握できるようにしました。

QA業務

先ほど「QA業務の作業工程」「品質OK判断の共有」「不具合リスト管理」をエピック化したとご紹介しました。
今回の目的は、『8月までに一般的なQA業務の環境が最低限できていること』なんですが、8月リリース予定前に実施するテストを準備するだけでなく、ほぼPOがワンオペしてきたQA業務を可視化させて、PO以外でもQA業務が担当できるような環境を作る必要がありました。

属人化の脱却

どんな仕事や組織でも属人化は発生してしまい、属人化にも「なるべき属人化」と「危険な属人化」があると思います。
POは属人化しやすい業務が多く発生しがちな役割ですが、QA業務がPOに属人化している状態に関しては「危険な属人化」と言えるので、一刻も早くトラブルなく業務を引き継ぎたい思いがありました。

属人化してしまっている業務というのは、そう簡単に引き継げるものではないため、まずは属人化している人から情報を引き出し、業務の詳細を全て把握する必要があります。
特に今回、POからQA業務に関して引き出すべき情報について「どんな作業をやってきたのか」「不具合以外に気になる箇所を見つけた際にどんな判断をしてきたのか」「現状のサービス品質について」などを聞き出さないとテスト計画を作成することが困難だったため、POから約一月ほどQAに関する情報を聞き出して可視化していきました。

テストの基準をどうするか

これまでPOが担当してドキュメントも履歴も残っていないテストの可視化を進めるにあたり、今後の状況に合わせてどこまで・何をマニュアル化やワークフローの整備を行えば良いのか、各スプリントのプランニングで話し合いながら全メンバーの認識を合わせて進めていきました。

現状の品質を確認するテスト(リグレッションテスト)ケース作り


「どのように作るのか」「どのくらいの粒度で作るのか」「どのように管理するのか」など、色々と話せるネタである『テストケース作り』ですが、これまでPOがやってきたテストを可視化させることが最優先であったため、よく使われるスプレッドシートを用いてテストケースを作ることにしました。

まずは、テストケースの詳細をざっくりと決めて、一部ではありますが今回は次のような項目の列を作ってユーザが操作する機能全てを実際に触りながらテストケースの雛形を作りました。

No, 画面, 確認する機能, 前提条件, テスト操作, 期待値, プラットフォーム, 作業日, コメント

これまでPOがやってきたテスト内容を完成した雛形と照らし合わせて、差分を見つけてマージする作業を実施して完了することでリグレッションテストがいつでもできるテストケースの完成です。

最近だと、脱ExcelスプレッドシートとしてZephyrTestRailなどのサービスを用いることも考えられますが、テストケースを作りながらサービスの導入・学習・管理をするには各コストがかかってしまう懸念があり、実際に個人アカウントでやってみたところ、やはりその懸念は晴れませんでした。
スプレッドシートでテストケースを作れば後から比較的移行しやすい状況になります。
また、並行して他の優先度の高い作業ができるなどのメリットもあって、このような判断をしました。

おそらく、テストの規模や内容が拡大すると以下の記事のような状況になると思いますが、当面はスプレッドシートGAS(Google Apps Scripts)の活用で運用できる見込みです。

テストのワークフローとマニュアル化

ただテストケースを作っただけでは、POからQAに関する全ての業務を巻き取ることはできません。

テストする前とテストした後でどんな作業をしていたのか、エンジニアとどのようなコミュニケーションをとってQA業務ができていたのかなど、業務に関する情報をもう少し可視化する必要があると感じ、テストのワークフローおよびテスト作業のマニュアルを作成することにしました。

開発→テスト→リリース→デプロイの各フロー前後でどんな品質管理を実施するのかを考えました。『QAとしてどんな作業工程が生まれるのか』について洗い出した結果がワークフローとなり、テスト作業者のリソースを柔軟に確保できるようにしたかったことからテスト作業者が「どこまで」「何をやったらいいのか」などが分かるマニュアルを用意することで、順調にPOからQA業務を巻き取れるようになりました。

それ以外でやった/やれなかったもの

上記ロードマップでご紹介した他のものについて、「CI/CD対応」は4月以前からMacBookProにJenkins環境を作り、リソース準備→ビルド→UIテスト(Appium)→Firebase App Distributionでデプロイまで一通りCI/CD対応が完了してました。あとは、運用やセキュリティなどを対応して完了予定だったのですが、コロナによりフルリモートワークとなり、社内ネットワークでしか扱えないJenkinsが活用できなくなりました。
ちなみに、Jenkinsを採用したのは一刻も早く自動化を実現したかったためです。

Jenkins環境が動いているPCを会社外からでもアクセスできるようにネットワーク対応も考えましたが、コロナの感染リスクを考えるとやるべきではないと考え、Jenkins/Firebase App DistributionからAzure Pipelines/App Centerに移行することにしました。

リグレッションテストの自動化」「パフォーマンス計測」については、POからテストに関する業務を巻き取るタスク及びCI/CDに関するタスクを優先したため完了できず、現在も引き続き作業を進めています。

最後に

以上、簡単にですがこれまでのQAスクラム活動を振り返ってみました。 スクラム活動する中でわかった事として、次の内容がありました。

  • フルリモートワークでコミュニケーションが減ったことによって、いつもよりデイリースクラムの精度を上げたり、作業履歴を残すなどしてやったことの見える化をしないとスプリントが上手く回らないことがあった。
  • 限られたリソースでスプリントを回すことによって、余裕が無くなり他メンバーよりも目の前のタスクやストーリーに集中しがちになってしまうため、定期的に他ストーリーの状況を確認して助け合いが必要だった。

また、やってよかったと思った事としては、次の内容がありました。

  • Jiraで発行したストーリーやタスクに対してコメントを書ける機能を活用し、メンバーのコメントの他に作業履歴やメモなどを徹底的に記入するように運用したことで、レビュー会やプランニング会などで活用できました。
  • Jiraでスクラムを運用しているとストーリーやタスクの「担当者」を素直に使いすぎてしまい、各メンバーが担当のストーリーやタスクだけを管理して作業しがちになってしまいます。限られたリソースでスクラムをやったということもあり、この点については注意することができました。

最後まで読んでいただいた方にはおわかりかと思いますが、現在のQAスクラムでは通常やるべきスクラムイベントの一部をリソースや兼任などの都合からやっていません。
ですが、大きなトラブルなく目標通りに一般的なQA業務の環境が最低限できている環境は整備できてきたため、やってよかったなと感じています。

まだまだ課題がありますが、これからもQA業務の品質を上げるため、QAスクラムを続けていきます。

フロントリニューアルにVue3を採用した話

f:id:libry:20201029115525j:plain
vue.js

初めまして。
株式会社Libryに9月に入社した新入社員エンジニアの長谷川です。
現在、社内ではフロントリニューアルプロジェクトというものが動いており、入社早々、プロジェクトに入ることになりそうです。

今回は、フロントリニューアルに使用するVue3.0についてお話ししようと思います。
Vue3.0はメジャーバージョンがついこの間リリース(2020/09/19)され、まだまだ記事が少ないため、皆さんの役に立つ記事になれば嬉しいです。

なぜフロントリニューアルにVue3.0を選ぶのか

そもそもなぜVueを採用するのかから話す必要があるかと思います。
Vueを選ぶ理由は大きく2つあります。

1. Progressive Framework

Progressiveとは「進歩的、進行性の」という意味の単語で、
ここでは、段階的に開発者が機能を選択できるフレームワークという意味です。

私たちは、チームのメンバーの人数やスキルという問題やアプリケーション固有の複雑といった問題を考慮してフレームワークを選定します。
ただ、これでめでたしめでたしとならないのがWebアプリケーションの特徴ですよね。。

今回のフロントリニューアルも現状のメンバーやスキル、それにアプリケーションの複雑さ
といった部分は今後状況が変化していく可能性が大いにあるのです。

このあたりの問題をクリアできるように"The Progressive Framework"という思想のもと
設計されたのがVue.jsであり、私たちがVue.jsを選んだ理由の1つです。

2. シンプルで書きやすい

Vueを使用したことがある方ならお分かりかと思いますが、とても使いやすいです。
また、学習コストもそれほどかからず実装することが可能です。
これはスピードを持って開発する必要のあるLibryのようなベンチャー企業にはとても大事な要素です。


上記はVueを選んだ理由を書きました。
それではVue3.0を選ぶ理由についてお話しします。

1. 圧倒的なパフォーマンスの向上


Vue3.0はVue2.0より圧倒的にスピードやパフォーマンスが向上しています。
特徴については後述しますが、パフォーマンスが向上して選択しない手はないですね。

2. TypeScriptの完全サポート


LibryのフロントリニューアルではTypeScriptが採用されています。
また、Vue3.0はTypeScriptで書かれています。
今までアプリケーション内で定義する必要があったTypeScriptの定義も書く必要がなくなります。


これらの理由により、私たちはVueそして、最新のVue3.0を選択しました。

Vue3.0の特徴


まずはVue3.0のリリースノートを見てみましょう。
https://github.com/vuejs/vue-next/releases/tag/v3.0.0
色々書かれていますね。
中でも面白いなと思ったのが、コードネームが「One Piece」となっていること。

Vueは実はメジャーバージョンにはアニメの名前が付けられているんですね。
そしてABCから順番に名付けられているというのが特徴です。

v3.0.0 One Piece
v2.6.0 Macross
v2.5.0 Level E
v2.4.0 Kill la Kill
v2.3.0 JoJo's Bizarre Adventure
v2.2.0 Initial D
v2.1.0 Hunter x Hunter
v2.0.0 Ghost in the Shell
v1.0.0 Evangelion
v0.12.0 Dragon Ball
v0.11.0 Cowboy Bebop
v0.10.0 Blade Runner
v0.9.0 Animatrix


参考URL:https://qiita.com/oz4you/items/7c3368efa687387293fa#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%B3%E3%83%BC%E3%83%89%E3%83%8D%E3%83%BC%E3%83%A0%E3%81%AE%E4%B8%80%E8%A6%A7%E8%A1%A8


(NはVue2系からVue3系に移行をスムーズにしやすくするためのバージョンv2.7.0につくのではと期待されています。)

1. 圧倒的なパフォーマンスの向上


まず、Vue3.0は2系に比べて、圧倒的にパフォーマンスが向上しています。
Vue3.0はVue2.0のコードを書き直して作られています。
要はゼロから再構築しているわけですね。

皆さんも作成したアプリケーションをイチから作り直したらコード綺麗になるのになぁ。
ここ書き直したいなぁ。と思ったことはありませんか。

そうなんです。ゼロから作られてコードがリファクタリングされているわけですね!
Vue製作者のEvan Youさんもコードを縮小することでパフォーマンスを向上しているわけですね。

また、リリースノートを見てみると"compiler-informed Virtual DOM"が新しく導入されたため、
圧倒的にパフォーマンスが向上したということが書かれています。

コンパイラーにかなり手を加えられていそうですね。

以前、Vue3.0のオンライントークに参加させていただいた際、
デモで1つ紹介されていたのですが、Vue 3 Template Explorerを使ってhoistedの再利用に関する最適化の紹介もされていたのが
面白かったので共有します。

下記サイトで確認してもらえればと思うのですが、
hoistedをrender関数の外で生成することによりhoistedを再利用することが可能になり、DOMの再利用が行われていますね。

Vue 3 Template Explorer
上記などを諸々行った結果、Vueの最適化が行われているというわけです。

2. 大規模開発をしやすくするために導入された新しいAPI


Vueは今まで大規模開発には不向きなのではと言われてきました。(個人的にはそんなことはないのですが。)
そんなマイナスイメージを払拭するためにComposition APIが導入されました。

Composition APIとは

OptionsAPIとCompositionAPI

参考:code-organization


Vue3.0で一番注目されているものはこのComposition APIかなと思います。
フロントリニューアルではまだまだ知見が溜まっていないのであまり今回は詳しく書きませんが、
(また知見がたまればCompositionAPIだけで記事を書きたい。)
CompositionAPIのメリットとしては以下があります。

  • TypeScriptと併用して型推論を十分に使えるようになる。
  • 関数を切り分けることができるため、再利用性が高まる。
  • 上記を含めて可読性があがる。


等のメリットがあります。
今後は社内でもCompositionAPIを使用していくと思うので、知見をためて書き起こしていきたいと思います。

3. TypeScriptの完全サポート

Vue3.0を選んだ理由でも書きましたが、Vue3.0のコードベースはTypeScriptで記述されており、
自動的に生成、テスト、バンドルされた型定義が含まれているため、常に最新の状態に保たれます。

よってTypeScriptのための設定ファイルを入れる必要もなくなり、またTSXを完全にサポートされているわけですね。

今Vue3.0にした際のデメリット

既に運用しているものに関しては、いきなりVue3.0に上げてしまうとApp.vueや各コンポーネントの記述・記法が変わってしまっていたりと、
かなり改修が大変になってしまいます。

この後、Vue3.0にスムーズに移行できるようにVue2.7がリリースされる予定みたいなので、
そちらを待ってからVue2.7 → Vue3.0に移行するのがいいのかなと思います。
(※ 実際に私もVue3.0にアップデートした際に少し苦戦したので。。)

Vue3.0を構築してみる

今回私は新規プロジェクトを立ち上げる際の作成と
既存プロジェクト(フロントリニューアルのリポジトリはこっち)のVue2系をVue3.0にアップデートの
両方を経験したので両方とも書いてみようと思います。

新規開発方法

新規開発環境構築はとても簡単です。

まず、vue-cliが最新バージョンになっているか確認します。
(Vue3.0は、@vue/cli 4.5.0以上からサポートされています。)
参考:vue-cli 4.5.0

# vue -V
@vue/cli 4.5.7

もしバージョンが低い場合は、下記コマンドを実行しましょう。

# yarn global add @vue/cli

下記コマンドで新規プロジェクトを作成します。

# vue create プロジェクト名

Vue CLI v4.5.7
? Please pick a preset:
  Default ([Vue 2] babel, eslint)
❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint)
  Manually select features

package.jsonを確認しましょう。

  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0-0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0-0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^7.0.0-0"
  },
# yarn serve

これだけでVue3.0が作成できてしまいましたね。

既存プロジェクトをVue3.0に変更

では今回、私は元々作成してあったVue2.6プロジェクトからVue3.0にバージョンをあげる処理を行いましたので、
そちらの説明もしたいと思います。

既存のプロジェクトのリポジトリで下記コマンドを実行します。

# vue add vue-next

こちらのコマンドで、vue-cli-plugin-vue-next
インストールしています。

このプラグインは、Vue、Vuex、Vue RouterもVue3.0に合わせてアップデートされます。

  • src/main.ts

  • before

import "./registerServiceWorker";
import App from "./App.vue";
import Vue from "vue";
import router from "./router";
import store from "./store";


Vue.config.productionTip = false;
new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");
  • after
import "./registerServiceWorker";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

const app = createApp(App);
app.use(router);
app.use(store);
app.mount("#app");
})();
  • src/store/index.ts

  • before

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {},
});
  • after
import { createStore } from "vuex";

const state = {};
const getters = {};
const actions = {};
const mutations = {};

export default createStore({
  state,
  getters,
  actions,
  mutations,
});
  • src/router/index.ts

  • before

import Home from "../views/Home.vue";
import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "home",
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
  ...
]

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

export default router;
  • after
import Home from "../views/Home.vue";
import { RouteRecordRaw, createRouter, createWebHistory } from "vue-router";

const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "home",
    component: Home,
  },
  ...
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

ここで自分の環境で立ち上げてみるとびっくりするほどエラーが出ました。。笑 うげっ!? image

1つ1つ解消していきます。

まず、TypeScriptが統合されたことにより、Vue.extendが不要になったため、削除

  • before
export default Vue.extend({
})
  • after
export default ({
})

これでProperty 'extend' does not existエラーは解消されました。

次に、ドキュメントのGlobal APIを確認しながら、 エラーを解消していきます。

image 抜粋資料:https://v3.vuejs.org/guide/migration/global-api.html#a-new-global-api-createapp

Vue2系 Global APIの記述をVue3系 Instance APIに変更する。

Global API こちらの内容をもとに下記記述になっているか確認

  • Vue.config → app.config
  • Vue.config.productionTip → 削除
  • Vue.config.ignoredElements → app.config.isCustomElement
  • Vue.component → app.component
  • Vue.directive → app.directive
  • Vue.mixin → app.mixin
  • Vue.use → app.use

大体エラー部分は取り切ったかな?と思ったのですが、エラーが。。
Vue3.0から不要になってpackage.jsonから削除したvue-template-compilerを
一生懸命tests/unit/example.spec.tsで取り出そうとして、
Cannot find module 'vue-template-compiler'が出るのです。。

image

これがなかなか分からず、先輩エンジニアに助けてもらいました。。 下記ファイルを変更して、"vue-jest": "^5.0.0-0"を導入すると簡単に直りました。 エラー部分を断片的に見てしまっていたため、unit-testの設定がおかしいことに気づかなかったことに関しては、 まだまだだなと思います。

  • jest.config.js

  • transformプロパティの追加

transform: {
  "^.+\\.vue$": "vue-jest",
}
  • src/shims-vue.d.ts

  • before

declare module "*.vue" {
  import { ComponentOptions } from "vue";
  const component: ComponentOptions;
  export default component;
}
  • after
declare module "*.vue" {
  import type { DefineComponent } from "vue";
  const component: DefineComponent;
  export default component;
}
  • tests/unit/example.spec.ts

  • after

import HelloWorld from "@/components/HelloWorld.vue";
import { shallowMount } from "@vue/test-utils";

test("HelloWorld.vue", () => {
  const msg = "new message";
  const wrapper = shallowMount(HelloWorld, {
    propsData: { msg },
  });

  expect(wrapper.text()).toMatch(msg);
});

以上が既存プロジェクトをVue3.0に変更する方法でした。

Tips

Vue3.0を学んでいくとこれ便利だなと思った追加機能がいくつかあったので、ご紹介いたします。

・ Provide / inject

通常、親コンポーネントから子コンポーネントにデータを渡す必要がある場合は、propsを使用します。
下記画像のような深いコンポーネント構造になった場合、propsで順番にデータを渡すため、かなり煩雑な形になっていました。
今まではこれらを解決したのがVuexでした。
ただ、Vuexはグローバル変数として呼び出されてしまいコンソール上でデータを叩けるという問題点等もありました。
Vue3.0では、Provide / injectをすることにより、 下記画像のように親コンポーネントでproviderした物を子や孫コンポーネントでinjectして受け取ることができるようになります。 かなり画期的ですね! ぶっちゃけこれを使うとグローバル変数として呼び出すVuexがほとんど必要なくなると思ってます。
参考:https://v3.vuejs.org/guide/component-provide-inject.html#provide-inject

components_provide

・ v-model(双方向バインド) のvalue名を変更できるように

v-modelを使ったことはありますでしょうか。 今までv-modelは、呼び出し元にvalueでしか値を渡すことができませんでした。

<ChildComponent v-model="pageTitle" />

<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

どちらも同義
v-modelは:valueと@inputで構成されているが、
このv-modelのvalueは今まで変えることができなかった。

それが下記の通り、指定した名前で定義することが

<ChildComponent v-model:title="pageTitle" />

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

v-modelの後ろに指定したいkey名を入れることにより、今までvalueだった部分を指定したいkeyに変更できるように

どちらも可読性を上げるためにとても有効な新しいTipsでした。

まとめ

まだまだメジャーバージョンがリリースされたばかりのため、
対応できていないパッケージがあったりとまだまだこれからですが、
今後パッケージも随時対応していくにつれて、私ももっと勉強していく必要があるなと思いました。

EKS, Flux, KMS, sopsを使ったsecureなGitOps環境を構築してみた

f:id:libry:20200801172858p:plain
GitOps構成図

良いエンジニアライフを送ろう!

Libryでスクラムマスター兼フロントエンドエンジニアをしている岩西である。

今回はフロントエンドエンジニアと称しながら、その枠を超えてLibryのインフラ部分の運用改善にどのような取り組みをしているのか、一部分だけでも技術的に掘り下げて話していけたらと思う。

ここでは上の構成図の青枠で囲んだ部分(CD)を中心に書いていこうと思う。

Libryの運用改善に対する取り組み

Libryではプロダクトの改善に加えて、サービス運用改善に向けても取り組んでいる。

例えば自社開発のプロダクトをDockerコンテナを使って開発〜本番環境まで一貫して実行できるように、コンテナ周りの環境整備を徐々に進めていったりとかをしている。

そしてスモールスタートで本番環境にKubernetesを使ったコンテナオーケストレーションツールの導入も進めている。

実際に現在Libry周辺プロダクトの一部サービスはKubernetes上で稼働している。

無論まだまだこのあたりのツール運用には課題があり、そこを少しずつ取り除いていきながら従来よりもインフラ周りで運用しやすい環境を作っていく必要があるが、近いうちEC2上で稼働する全てのプロダクトをコンテナで置き換えKubernetes上に載せることになるだろう。

EKS on Fargate

Dockerコンテナを実行する環境の候補としてAWSの場合ECSやEKS、最近だとRed Hat OpenShiftを使う方法も考えられる。

GCPの場合だとGCEやGKE、Cloud Runあたりが検討に上がってくるだろう。

Libryではクラウド環境としてAWSを採用していたこともあり、スモールスタートで取り組むためAWS上でDocker実行環境を構築することになった。

その中でちょうど今回の取り組みを始めたタイミングでFargateがEKSサポートしたこともありEKS on Fargateが候補となった。

EKSはAWSが提供しているフルマネージド型のKubernetesサービスであり、ざっくり言ってしまうとマスターノードの部分をよしなにしてくれるサービスとなっている。

そのためFargateが来る前はEC2のワーカーノードを立てて管理する必要があった。

しかしFargateによるサポートが入ったEKSでは、このワーカーノードの部分もよしなにしてくれるようになり、より管理の手間を省くことができるようになってきた。(もちろん、その分EKS on Fargate固有の制約もあったりする。)

今回EKS on Fargateを使うことでKubernetesの知見を活かしつつ、サーバー管理の手間を省いたり、スケーリングによる効率的なコンピューティングリソースの活用を実現できたりするのではないかと考え、EKS on Fargateを使ってコンテナ運用環境を構築することを決定した。

eksctlを使ってEKSクラスターを作成

ここではサンプルとしてtest-clusterというEKSクラスターを作成する方法を書き残す。

EKS周りのsetupにはeksctlが使いやすい。

Fargate profileやKMSのサービスアカウントなどを定義した、ざっくりと以下のようなYAML(VPCの設定やPolicyは適宜書き換える必要あり)を書いて保存し、

./path/to/test-cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: test-cluster
  region: ap-northeast-1
  version: "1.16"

vpc:
  subnets:
    public:
      ap-northeast-1a:
        id: subnet-XXXXXXXXXXXXXXXXX
      ap-northeast-1c:
        id: subnet-XXXXXXXXXXXXXXXXX
      ap-northeast-1d:
        id: subnet-XXXXXXXXXXXXXXXXX
    private:
      ap-northeast-1a:
        id: subnet-XXXXXXXXXXXXXXXXX
      ap-northeast-1c:
        id: subnet-XXXXXXXXXXXXXXXXX
      ap-northeast-1d:
        id: subnet-XXXXXXXXXXXXXXXXX

fargateProfiles:
  - name: fp-default
    selectors:
      - namespace: default
      - namespace: kube-system
  - name: fp-flux
    selectors:
      - namespace: flux

iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: key-management-service
        namespace: default
      attachPolicyARNs:
        - "arn:aws:iam::XXXXXXXXXXXX:policy/KeyManagementServiceReadIAMPolicy"

cloudWatch:
  clusterLogging:
    enableTypes:
      - "*"

eksctlのインストールやAWSとの認証などを済ませた環境で以下のコマンドを叩くとEKS on Fargateのクラスターが作成できる。

eksctl create cluster -f ./path/to/test-cluster.yaml

AWSコンソールや kubectl でtest-clusterが作成されたことが確認できる。

GitOpsとFlux

LibryではGitHubを使ってソースコードを管理しており、KubernetesのmanifestsなどもGitHubを使って管理している。

インフラ環境を開発者が容易に参照でき、コード上での変更・レビューができるようにGitHubをSingle Source of Truth(信頼できる唯一の情報源)としたGitOpsをLibryの運用方針として取ることにした。

GitOps実現においては、CNCFのsandbox projectにもなっているFluxを使ってみることにした。

FluxではKubernetes側からpullしてmanifestsを適用していくので、CIツールにkubectl操作のための設定する必要がなく、セキュアで手軽である。

ちなみにもう一つ有名なGitOpsツールとしてArgoがあり、さらにその2つが力を合わしたArgo Flux projectが動いていたりしている。

この中で言及されているGitOps Engineは、今の計画ではFlux v2と統合されないという旨のアナウンスがでていて今後の動向を注意深く見ていく必要があるが、近い将来より充実したGitOps環境が実現できるようになっているかもしれない。

Fluxのsetup

Fluxの使い方は簡単で、ここのGet Startedに沿って進めていけばいい。

fluxctlをインストールし、Kubernetes上にflux用のnamespaceを用意する。

kubectl create namespace flux

Kubernetesのmanifestsなどを配置するgit repository(LibryではInfra repositoryと呼んでいる)を作成し、そこの任意のディレクトリにmanifestsを配置する。

manifestsは何でも良いが、今回はサンプルとして以下を使用する。

./path/to/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  selector:
    matchLabels:
      app: sample
  replicas: 3
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - image: busybox:1.32.0
        name: sample
        command:
        - "/bin/sh"
        - "-c"
        - |
          echo "started"
          while :
          do
            date
            sleep 1m
          done

例えば以下のような構成の場合、

  • Infra repository: git@github.com:libry-inc/test_cluster.git
  • ディレクトリパス: manifests/test

以下のコマンドを実行することでFluxのdeployができる。

fluxctl install \
--git-user=flux \
--git-email=flux@users.noreply.github.com \
--git-url=git@github.com:libry-inc/test_cluster.git \
--git-path=manifests/test \
--namespace=flux | kubectl apply -f -

さらに以下のコマンドでFluxのdeployが完了するのを待つことができ、

kubectl -n flux rollout status deployment/flux

successfully rolled out と表示されたらFluxのdeploy完了となる。

もし今回指定したInfra repositoryがprivateの場合は、以下のコマンドを実行してFluxコンテナ側で生成したssh公開鍵をInfra repositoryのdeploy keyとして登録しておくと良い。

fluxctl identity --k8s-fwd-ns flux

上手く稼働していると、しばらく後にInfra repositoryに配置したmanifestsが適用されているのが確認できる。

$ kubectl get deployment
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
sample-deployment   3/3     3            3           7m45s

secretsをGitHub上で管理する

GitOpsではGitHub上のコードがSingle Source of Truthである。

そのため、DBへの接続情報やAPI Keyなどの機密情報もGitHubで管理することになる。

Kubernetesのsecrets manifestsは、base64エンコードがかけられたYAMLなので容易に中身を読み解くことができる。

安全性を考えるのであれば、GitHubの権限とsecretsのような機密情報のアクセス権限は別のものとして管理する方が良いだろう。

そこで今回はAWS KMSを利用してsecretsを暗号化・復号化し、暗号化したものをGitHub上で管理していく方針を取ることにした。

sopsについて

AWS KMSを使って暗号化・復号化するにはsopsを使うと便利だ。

sopsを使って機密情報が含まれるファイルを暗号化すると、暗号化に使用したメタ情報とともに暗号化されたファイルが生成される。

今回はKubernetesのsecretsとして扱うので、それをさらにsecrets manifestsに変換することになる。

少し煩雑だが以下のような段階を踏むことになる。

  1. 平文の機密情報が含まれるファイル
    • git管理しない
  2. sopsで暗号化したファイル
    • git管理してもしなくてもいい
    • 今回はInfra repositoryにsecretsというディレクトリを作成して、そこにgit管理している
  3. secrets manifests形式のYAMLファイル
    • git管理する
    • Fluxが監視しているディレクトリに配置しなければならない

sopsとKMSを使って暗号化し、secretsを作成する

今回は以下のファイルを暗号化してみる。

./path/to/test-secret

This is secret text.

AWS KMSに任意の鍵を作成し、それのARNを取得する。

sopsがインストールされ、KMSの鍵のアクセス権限を持つ環境で以下のコマンドを実行すると暗号化されたtest-secret.encが生成される。

sops -e --kms "arn:aws:kms:ap-northeast-1:XXXXXXXXXXXX:key/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ./path/to/test-secret > ./path/to/test-secret.enc

メタ情報を持っているので、編集するときは以下のコマンドで済む。

sops ./path/to/test-secret.enc

またgitのdifferを設定すると、復号化した内容でdiffをみることもできるので、機密情報の変更履歴も追うことができる。

暗号化したファイルをさらにsecrets manifestsにするには以下のコマンドを実行する。

kubectl create secret generic test-secret-name --from-file test-secret.enc=./path/to/test-secret.enc --dry-run -o yaml > ./path/to/secrets.yaml

生成された./path/to/secrets.yamlをInfra repositoryのmanifestsが配置されているところに追加するとFluxが読み取ってsecretsリソースをEKS上に作成する。

ちなみにsecretsなどをイミュータブルにする場合は、test-secret.encのハッシュを計算し、test-secret-nameを置き換えるようなスクリプトを用意すると便利である。

EKS上から復号化する

復号化するために、EKSのサービスアカウントに今回のKMSの鍵のアクセス権限を付与する。

今回はkey-management-serviceというサービスアカウントを作っているので、以下のコマンドでサービスアカウントのIAM RoleのARNを取得することができる。

kubectl get serviceaccount/key-management-service -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com/role-arn}'

取得したARNをKMSの鍵のキーユーザーとして紐づけるとEKS上のコンテナからKMSの鍵にアクセスすることができる。

実際に復号化を行う処理はInitContainerを使って記述すると良い。

先ほどのInfra repository上のdeployment.yamlを以下のように書き換える。

./path/to/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  selector:
    matchLabels:
      app: sample
  replicas: 3
  template:
    metadata:
      labels:
        app: sample
    spec:
      serviceAccountName: key-management-service
      volumes:
      - name: secret-dir
        secret:
          secretName: test-secret-name
      - name: env-dir
        emptyDir:
          medium: Memory
      containers:
      - image: busybox:1.32.0
        name: sample
        volumeMounts:
        - name: env-dir
          mountPath: /var/workspace/env
        command:
        - "/bin/sh"
        - "-c"
        - |
          cat /var/workspace/env/test-secret
          while :
          do
            date
            sleep 1m
          done
      initContainers:
      - name: decrypt-secret
        image: mozilla/sops:v3.6.0-alpine
        volumeMounts:
        - name: secret-dir
          mountPath: /root/workspace/secrets
        - name: env-dir
          mountPath: /root/workspace/env
        command:
          - "/bin/bash"
          - "-c"
          - |
            [ -d env/test-secret ] && exit 0
            cd /root/workspace
            sops -d ./secrets/test-secret.enc > ./env/test-secret

変更内容をInfra repositoryに反映し、しばらくするとコンテナ上でtest-secretを復号化して読み込まれていることが確認できる。

$ kubectl logs pod/sample-deployment-XXXXXXXXX-XXXXX
This is secret text.
Sat Aug  1 07:57:40 UTC 2020
Sat Aug  1 07:58:40 UTC 2020

最後に

Libryでは現在、このようにしてGitOps構成図にあるような環境をスモールスタートではあるが運用しはじめている。

今回書くことができなかったCIの部分やこれからさらに取り組んでいくことなどまだまだこの領域には奥が深い内容が多くあり、フロントエンドエンジニアと称しながらも役割を超えて楽しく携わっている。

課題は多くあり、この運用が変わる可能性は十分にあるが、少しでもLibryがどんな技術を扱っているのか参考になれば幸いである。

リブリーの、LoveGrowthな制度の話

はじめまして、デザイナーの永石といいます。社内ではなげっつと呼ばれているので、以降はなげっつと名乗ろうと思います。 この記事では、リブリーの社内制度、とりわけ自分もよく利用している「書籍購入補助・イベント参加費補助制度」について紹介します。

リブリーの社内制度

リブリーの社内制度は、リブリーのValue(行動規範)である「7Values」をベースに設計されています。 まだまだ整っていない部分もありますが、7Valuesをもとに日々制度やカルチャーが作られており、メンバーからの提案も通りやすい環境です。

7Valuesの詳細については、こちらをご覧ください

また、7Valuesと社内制度の話はこちらでも一部紹介しているので、よければこちらも合わせてご覧ください。

www.wantedly.com

www.wantedly.com

書籍購入補助・イベント参加費補助制度の概要

「書籍購入補助・イベント参加費補助」の制度は、7Valuesの「Love Growth」や「Be Professional」につながるもので、書籍の購入や勉強会等に参加する際の費用を会社が負担してくれるものです。

申請のフロー

一応、定められた申請フローはありますが、めちゃくちゃ簡単です。 購入したい書籍や、参加したいイベントがあれば、Slackで「こちら参加(購入)したいです」とURLを添えて投稿するだけ。 投稿に対しマネージャーから「許」のスタンプがつけばOK、「否」のスタンプがつけばダメです。

f:id:libry:20200805124619p:plain
「許」のスタンプがつけばOK🙆‍♂️「否」のスタンプがつけばダメ🙅‍♂️

f:id:libry:20200805124507p:plain
去年のDesignShipの参加を申請した時のスクショ

ちなみに、自分はこれまで申請した中で「否」スタンプをつけられたことは一度もありません。 しかも、書籍購入に関しては「購入よろです」スタンプをつけておけば、購入までしといてくれるという至れり尽くせりっぷりです。ありがたや。

制度を利用して最近買った書籍

せっかくなので、この制度を利用して最近私が購入してもらった書籍を2冊紹介したいと思います。

1つ目は『デザイニング・インターフェース 第2版 ―パターンによる実践的インタラクションデザイン』です。

界隈では「UIデザイナなら絶対読んどけ!」といわれている割に、絶版?となっており、中古で出回っているものもそこそこ高価で、自分で購入するのは少しひよっていた書籍です。 これまで図書館で借りてちまちま読んでましたが、図書館に行かなくて良くなったのでとても助かりました。

2つ目は、『SCRUM BOOT CAMP THE BOOK【増補改訂版】 スクラムチームではじめるアジャイル開発』です。

現在リブリーの開発チームでは、「スクラム見直しプロジェクト」を行っており、その一環でマネージャーから「この書籍ほしい?」と確認があり、購入してもらいました。 私自身、恥ずかしながらスクラムについて「なんとなくわかっているつもり」な部分が多かったので、チームメンバーとの認識をあわせる道具としてとても助かっています。 余談ですが、「スクラム見直しのプロジェクト」についての記事も、きっとそのうち誰かが書くと思うので、どうぞお楽しみに!

この制度を利用して参加した勉強会・イベント

制度を活用して参加させてもらった勉強会・イベントについても軽く紹介させてください。

今年に入ってからは新型コロナウイルスの影響で、なかなか勉強会やカンファレンス等のイベントが開催できない状況になっていますが、 私個人ではこれまでに「Designship」や「AdobeMAX Japan」等イベントに参加させてもらいました。

f:id:libry:20200805125052j:plain
AdobeMAX名物の落書きボード(?)、どこかにリブリーが!(急いでたのでブレブレ...)

他の開発部のメンバーも、「PMConference」や「PHPerKaigi」「Vue fes」といったイベントに参加していました。 この制度のおかげで、「参加したいけど自費で行くにはちょっと検討する」くらいのイベントに参加するハードルが下がるのでとても助かっています。

イベントに参加した後は、各々イベントで学んだことを週1の勉強会の時間にLTをすることもあり、他のメンバーにも知見を共有する文化があります。 他のメンバーがどんなことに関心を持っていて、それをどう活かしていきたいか、といった部分が垣間見えることもあり、聞いていて楽しいです。

いつになるかわかりませんが、早くリアルでもイベントが開催できる世の中に戻るといいですね。


今回は、リブリーの「書籍購入補助・イベント参加費補助制度」について紹介させていただきました。 リブリーは、自分たちの「学び」にも積極的な会社です。 興味を持ってくださった方は、ぜひ一度話を聞きに来てください!