White Box技術部

WEB開発のあれこれ(と何か)

ブログを書かなくなった現状を打破するために雑記カテゴリを作った

ブログの記事が下書きで止まる問題

ここ2年ほど、ブログを書いては下書き保存で終わるということが続いており、結果として全然ブログを書いてない感じになっています。 その下書きも、ちゃんと記事の体になしていれば、「忘れないようにブログに書いておく」という目的は達成できるので良いのですが、あいにくそうはなっていないため、読み返してみても

「これなんで書いたんだっけ?」

「あれどこに書いたっけ?そもそも書いてたっけ?」

と、あまり芳しくありません。

このままだといい加減まずいかなと思ったので、ちょっと対応することにしました。

雑にでも形にしたらどうだろうか?

そもそも記事を公開できていないのは、

  • 題材に技術的なもの・調査が必要なものを選びがち
  • 1つの記事をちゃんとまとめて、形なりにも読み物にしようとする
  • そうすると1記事書くのにいつも数時間かかる
  • そんな時間を捻出できない

というところにあるので、これらの問題を解消するために、普段メモするようなことを雑に内容をまとめて公開することにしました。

具体的には、以下のような気持ちでやっていきたいなと思っています。

  • 業務や調査での学びを、とりとめもなく書く
  • 平日時間が取れれば、業務終了後に書く
  • 1つ30分1時間位で書く(30分は無理そうなので
  • 時間が来たらそこで公開する

雑記カテゴリの作成

こういった記事については、通常の記事と区別するために【雑記】のカテゴリを付けることにします。

雑記に記載した内容をまとめられれば、対応する雑記の方は消すかもしれません。そんなカテゴリにする予定です。

これで書くようになると良いんですが・・・

しかしホント書いてないな。。

数年前から自分でも書く頻度は落ちてきているなと思ってはいたのですが、ここ2年は本当にひどいですね。 こうなってくるとスタートアップに転職したのが多分に影響しているのかなという感じがします。

今の会社だと自分のやることが目まぐるしく変わっていて、2ヶ月同じ業務やっていたら長い!みたいなところがあり、 意識高い感じで言うと学びが多い期間ではあるのですが、多すぎて振り返っていれなかったのかもしれません。

私の意識は割と低いので、再利用できないと思ってしまったのかもしれないですね(将来、楽するためにブログを書いているので)。

でも最近は業務が一巡したというか、再利用することも出てきたので、やっぱりまとめないとなと危機感が募ってきて、こんな記事を書いた次第です。

ちなみにこの記事を書くには1時間半くらいかかりました。

CircleCIでDockerfileのあるプロダクトをテストする

既存のアプリケーションをコンテナ化したら、CircleCIで回していたテストがコケていたので、その修正の記録です。

CircleCIの設定を書く

CircleCIの設定をいじるのは久しぶりだったので、以下のようなことも調べつつやっていたのですが、結局ハマってだいぶ時間をとられてしまいました。

  • dockerキー配下のimageは、先頭のコンテナイメージが、stepsのコマンド実行に使われる
  • コンテナから別コンテナを呼ぶ際の名前を変えたければ、nameキーを使う
  • ポートフォワードの指定はconfig.ymlからできない
  • dockerhubのdockerはalpineだったので、ソフトウェアのインストールはapk add

テスト用イメージの作成(ハマったとこ)

何にハマっていたのかと言うと、大きくは2つで

  1. コンテナから他のコンテナに繋げない
  2. コンテナの状態を育てていけない

というところでした。

1. コンテナから他のコンテナに繋げない

こういうことをしたかった、という実行環境のイメージは以下のような感じでした。

f:id:seri_wb:20200126004427j:plain:w600

各種stepを実行するためのコンテナイメージ(docker)がCircleCI上で動作していて、 そのコンテナの中で、アプリケーション(python)用のコンテナがビルドされ、起動後にテストが実行される・・・みたいな。

つまり、テストアプリケーションのコンテナを、Docker in Dockerの状態にしたかったのです。

そして、アプリケーションのコンテナから参照するテスト用のコンテナ(MySQL、Azurite)は、実行用コンテナと同列で起動させれば、ローカルのDocker Composeと同じ状態にできるなって思ったのですが、 結論から言うとこれはできないそうです。

f:id:seri_wb:20200315195206j:plain:w600

そうとは知らず、色々試してはみたので、結果だけは残しておこうかなって・・・

privilegedオプションでできること・できないこと

まずは、stepsの実行イメージをdockerベースのコンテナにし、そのコンテナでビルドしたイメージをprivilegedオプションを付けて起動させて、やりたいことができるか確認しました。

  build:
    docker:
      - image: docker:git
      - image: arafato/azurite:latest
        name: azurite
...
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build image
          command: docker build -t hoge/hoge-app .
      - run:
          name: Run image
          command: docker run -td --privileged --name hoge-app hoge/hoge-app bash

この状態で、それぞれのコンテナからできたことと、できなかったことは以下になりました。

dockerイメージのコンテナを実行コンテナ、その上でビルドしたイメージをアプリケーションとして書いています。

できた

  • 実行コンテナからAzuriteのイメージ名でcurl接続

できなかった

  • アプリケーションからAzuriteへのcurl接続(ホスト名解決できない)
  • 実行コンテナからlocalhostでAzuriteにcurl接続(ホスト名をazuriteにするとできる)

ちなみにAzuriteのホスト名解決を行うためには、dockerキーのvalue値でnameの指定も必要です(image名と同じだとしても)。

2. コンテナの状態を育てていけない

次に、アプリケーションのコンテナでテストを実行できるようにするには、 Dockerfileで実行される処理以外にも処理を実行する必要があったので、コンテナの状態を育てていく必要がありました。

そこで、docker run時にコマンドとしてpipenv sync --devを渡していたわけですが、これがなぜか実行されなかったため、 run時はDockerfile上のCMDを動作させないためにbashを渡し、別のステップでdocker execで実施するようにするしかありませんでした。

また、docker execでpipenv sync --devを実行したステップの後、pipenv run lintなどをexecに渡して実行すると

「mypyなんぞない」

と言われたので、、、execにコマンドを渡す際はbash -cを使って渡すようにしました。

      - run:
          name: Install dependencies
          command: docker exec hoge-app bash -c 'pipenv sync --dev'
      - run:
          name: Lint code
          command: docker exec hoge-app bash -c 'pipenv run lint 2>&1' | tee lint.log
      - run:
          name: Test hoge-app
          command: docker exec hoge-app bash -c 'pipenv run test 2>&1' | tee test.log

      - store_artifacts:
          path: lint.log
      - store_artifacts:
          path: test.log

とりあえずこうすると、Docker in DockerでもLintとTestの実行はできたのですが、名前解決がうまく行かないことなどから肝心のテストがコケるので、サポート問い合わせした結果、やりたいような構成はできないという回答をもらったわけです。

以下にも直接通信ができないと書いてあるので、そのとおりだと言う話でもあるのですが。。。

最終的なconfig.yml

というわけで、最終的には実行コンテナをDockerfileのベースイメージにし、Dockerfileに書いている処理をstepsに記載するという、普通のやり方で対応しました。

以下は最終的なconfig.ymlを適当に抜粋したものになります。

version: 2

jobs:
  build:
    docker:
      - image: circleci/python:3.6.6
        name: hoge-app
        environment:
          DB_HOST: test-db
      - image: circleci/mysql:5.7-ram
        name: test-db
        environment:
          MYSQL_USER: root
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_TCP_PORT: 3306
      - image: redis
        name: redis
      - image: arafato/azurite:latest
        name: azurite
    working_directory: ~/hoge-app
    environment:
      LANG: ja_JP.UTF-8
      LC_ALL: ja_JP.UTF-8
      LANGUAGE: ja_JP:ja
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Set locales
          command: sudo localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
      - run:
          name: Update submodule
          command: git submodule update --recursive --init
      - run:
          name: Install dependencies
          command: |
            sudo apt-get update && \
            sudo apt-get install -y curl build-essential \
            mecab mecab-ipadic libmecab-dev
      - run:
          name: Install dependencies
          command: pipenv sync --dev
      - run:
          name: Setup azurite filedata
          command: curl -v -X PUT -H 'x-ms-version:2017-11-09' http://azurite:10000/azureacount/filedata?restype=container

      - run:
          name: Lint code
          command: pipenv run lint 2>&1 | tee lint.log
      - run:
          name: Test hoge-app
          command: pipenv run test 2>&1 | tee test.log

      - store_artifacts:
          path: lint.log
      - store_artifacts:
          path: test.log

TypeScriptでwindowにプロパティを追加するいくつかの方法

DjangoのテンプレートにReactを埋め込むとかいう、妙なことをしているときに、とある理由からwindowにReactやらを追加しておく必要ができたのですが、TypeScriptだとそのまま突っ込むと型の関係で怒られました。

調べてみるとほぼこのタイトルの記事があったのですが、他にもいくつか解決策が出たので残しておきます。

ちなみに、怒られたのはuseStateがReact自身の同一性を見ているからでした。 https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react

1. windowを作り直す

最初は上記の記事にあるように、window.tsファイルを作成して実装しました。

たしかこんな感じで書いたらいけるはずです。

  • window.ts
interface MyWindow extends Window {
  HogeForm: Comment
}
declare var window: MyWindow;
export default window;
  • index.ts
import React from 'react';
import { render } from 'react-dom';
import HogeForm from '../containers/HogeForm';
import window from './window';

render(<HogeForm />, document.getElementById('hoge_container'));

これで、hoge_containerタグの場所の表示を置き換えて表示しました。HogeFormの型はCommentでいけます。

この場合、表示するクラスやらが増える度にwindow.tsを修正することになります。

2. as anyを使う

管理ファイルが増えるのも煩わしいなぁとぼやいていたら、同僚にanyを使ったらやり方を教えてもらいました。

import React from 'react';
import ReactDOM from 'react-dom';
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;

import { HogeForm } from '../containers/HogeForm';
(window as any).HogeForm = HogeForm; 

これだとファイルも増えないので、わかりやすさは上がった気がします。 でもTypeScript使っててanyって明示するのもなぁと、同僚と唸ることに。

3. expose-loaderを使う

といったわけで、最終的にはexpose-loaderを使って解消しました。

import 'expose-loader?React!react';
import 'expose-loader?ReactDOM!react-dom';

import 'expose-loader?Containers!./containers/'; 

ライブラリを追加する必要はありますが、コード自体はだいぶスッキリしたので、対価には見合っているかなと。 ただ、直接関数をロードすると、呼び出す側が正しく認識できなかったので、いったんObjectにするようにしています(ここではContainers)。

これによって使う側がどう変わるかと言うと、django-react-templatetagsでの書き方が、

  • expose-loader導入前
{% load react %}
{% react_render component="HogeForm" props=hoge_data %}
  • expose-loader導入後
{% load react %}
{% react_render component="Containers.HogeForm" props=hoge_data %}

のようになります。


といったのを9月くらいにやっていたのですが、気付けばもう年も変わっているという、、、

今年は忘れないうちにアウトプットしていきたいところです。

【主にスクラム向け】プロダクトバックログフォーマットと運用フローについて

プロダクトバックログのテンプレートが欲しい

「プロダクトバックログなんて、そんな何個も作るものじゃないでしょ」

とか思っていたときもあったのですが、ここ数年、公私合わせて、年2、3個はプロダクトバックログ(PBL)を作り、その都度フォーマットを作っていたので、 いい加減(自分のためにも)PBLのフォーマットを決め、テンプレートを用意するようにしました。

これが「さいきょうのPBLだ!」

最強なのかはともかく、現時点で一番わかりやすく運用しやすいPBLのフォーマットは、以下のようになりました。

f:id:seri_wb:20190610002920j:plain

テンプレートにはGoogleスプレッドシートを利用しています。

なぜかというと、現状ではこれが一番全体の把握がしやすく、多くの人が特に追加費用無しでアクセス可能であり、独自カスタマイズを許容できるツールになるからです。
セルに記述するというのも、アイテムを追加する人の心理的障壁を下げていますし、途中を許容しつつ議論ができるのも利点だと思うので、 特に強いこだわりがないのであれば、現状はスプレッドシート一択で良いのではないでしょうか。

PBL項目

スプレッドシートの各項目は以下のような用途を想定しています。

項目 用途
No 通番。優先度で前後させても通番を振り直しはしない
エリア 開発範囲を示すときに使う。LeSS Hugeのエリアに相当
ステータス プロダクトバックログアイテムの状態を表す(詳細は後述)
誰が 誰のための価値になるものを作るのかを明示する。ユーザストーリのWho
何をしたい どういったことを実現するのか。ユーザストーリのWhat
それはなぜか(価値) どういう価値が提供されるのか(リリース後に検証する内容)を書く。ユーザストーリのWhy
ポイント ストーリポイントを書く
レビューチェック内容 このアイテムでどういったことを実現するのかを具体的に書く。スプリントレビューの受け入れ基準に相当(How)
備考 SKIPにした理由や、アイテム分割した際の情報などを書く

PBLに携わる人数が多ければ、起票者の項目もあると便利です

スクラムでのPBL運用フロー

ではこのPBLをスクラムのイベントに沿って運用するフローを提示してみたいと思います。

1. プロダクトバックログリファインメントを実施

  1. 各自がプロダクトバックログにTODOでアイテムを追加する(事前記入だったり、時間を取ってやったりする)
  2. プロダクトバックログリファインメントを実施し、アイテムの上の方から内容を確認していく
    1. TODOを追加した人から内容の説明を受け、チーム内のゴール認識を合わせる(レビューチェック内容を決める)
    2. アイテムのストーリーポイントを見積もる
    3. アイテムの状況に合わせて、ステータスを変更する
  3. アイテムの粒度が大きすぎたり、不明瞭な場合は、実現可能な粒度にアイテムを分割して、再度内容を精査する

ステータスは以下のような意味合いで利用します。

ステータス 意味
TODO アイテムを起票した状態で、まだチームへの展開が行われていない
READY 展開された内容が、現時点で着手可能
NOT READY 展開された内容が、まだ着手不可能
DOING スプリントで実施中
DONE アイテムが完了している
WAIT 現状議論する段階にない(議論できない)
PENDING どうするかを保留した
SKIP 一旦実施しないこととした

2. スプリントプランニングを実施

PBL上位のアイテムでステータスがREADYとなっているものからスプリントで実施するアイテムを決め、そのアイテムのステータスをDOINGにする。

選んだアイテムはスプリントバックログ(ホワイトボードを使うのがお奨め)に移動し、タスクを詳細化・細分化する。

3. スプリントレビューを実施

スプリントで作成した成果(プロダクトインクリメント)を、PBLのレビューチェック内容と照らし合わせながらレビューする。

実現できていればステータスはDONEにし、追加で実現したいことが見つかったならPBLに新しいアイテムをTODOで追加する。

4. 繰り返し

これらを繰り返していく。

まとめ

個人的には、価値を生み出す作業にどれだけ注力できるかが、アジャイル開発の目指すところかなと思っているので、それ以外の部分については可能な限り力をかけずに済ませるようにしたいです。

ちょっとでもPBLのフォーマットに迷うくらいであれば、とりあえずこのフォーマットに書き殴ってみて、後で適当なものに切り替えてみるとかはどうでしょうか。