もりはやメモφ(・ω・ )

インフラなエンジニアからSREへ

『新1分間リーダーシップ』を読みました

『新1分間リーダーシップ』を読んだので所感をメモしておきます。

www.diamond.co.jp

読むまでの動機

動機はいくつかありまして以下が大きいです。

  • 4月からマネージャって肩書きがついた
  • マネージャなんもわからん
  • SRE Nextで聴いたそのっつさんのマネージャ目線の素晴らしいプレゼンのなかで紹介されてた*1

読んでみて

期待以上にサクッと読める良書でした。わかりやすい分類とそれぞれに対する手法が読みやすい文体で紹介されており、迷ったときの参考例としてふとしたときに思い出せるようなノウハウが学べたと感じています。*2

状況対応型リーダ

”状況対応型リーダ”の言葉自体は本書で知りましたが、メンバに対して画一的な対応を行わず、それぞれにあったやり方を行う点は漠然と「それはそうだろう」と思ったので期待せずに読み進めたところ、人だけでなく、その人が対応するタスクによってどの段階かを整理し、指導方針を本人との合意の上で変化させていく点が興味深く納得感を得ました。

個人的にもちょうど2020年の上期が終了し、初めてメンバを評価する*3責務を終えたばかりだったこともあり、組織にとって人材を評価し、査定を行う過程で最終的には形式的なランク付けをする必要があることを体感していた事も納得感につながったのでしょう。*4

社会人になってからのキャリアを振り返ると、基本的にはプレイヤとしてパフォーマンスを出していくことに取り組んできたのでこれは新鮮な体験でした。

求められる3つのスキル

前半部分で状況対応型リーダに求められる主要スキルは以下の3つだと説明があります。

  1. 目標設定
  2. 診断
  3. マッチング

目標設定

”始め半分”なんて言葉もありますが、本書でも目標の重要性が語られており、それは”SMART”という語呂合わせで紹介されています。 詳細は省きますが項目だけ並べると以下の通りで、これらを満たした目標を設定するように心がけるべしとありました。

  • Specific - 具体性
  • Motivating - 動機付け
  • Attainable - 達成可能性
  • Relevant - 関連性
  • Trackable - 追跡可能性

字面だけだと分かりづらかったのが”関連性”で、これは会社、チーム、他のタスクとの関連性のことで、良い効果があるか、優先度は高いかを意識しましょうと理解しました。

診断

SMARTで適切な目標を設定した後は、診断してマッチングをしながらコーチングをするフェーズです。

診断では以下の2つの軸から、

  • 技術
  • 意欲

以下の4つのレベルに診断し、

  1. 技能(低) 、意欲(高)
  2. 技能(低から中)、意欲(低)
  3. 技能(中から高い)、意欲(不安定)
  4. 技能(高)、意欲(高)

それぞれに合ったコーチング(付き合い方)でマッチングさせていくとありました。これは一見するとシンプルに分割しすぎているようにも感じましたが、以下のように考えるとある程度納得できました。

  1. 「やっていき」
  2. 「力ついてくるけど飽きてくる」
  3. 「”完全に理解した!”と”何もわからん”」
  4. 「チョットデキル」

マッチング(付き合い方)

診断した後は、フェーズにあった付き合い方をすべしとのことで、4つのやり方を柔軟に使い分けましょうとあります。*5

  1. 指示型
  2. コーチ型
  3. 支援型
  4. 委任型

本書ではそれぞれの型について登場人物を変えながらわかりやすい説明がされており、考え方として参考になりました。

プレイヤとしての自分は基本的に”委任型”が最も好きなやり方で、困ったときだけ”コーチ型” or "支援型"を相手に感謝しつつも申し訳ない気持ちで受け入れるようなところがありましたが、トータルで考えると初速を他者からの指導で加速するのは総合的にメリットが大きいことを体感することが多いです。*6

さて、自分を振り返るとシンプルじゃない感

ここまでざっと書いてきたように、本の中では箇条書きにできてしまうようなシンプルな分類やノウハウが紹介されており、それらはわかりやすくて参考にはなりましたが、自分で消化して多少なりとも良い方向に持って行くかを検討すると「これは大変だな」となります。

みんな基本的に自分より強い

素晴らしいことに、自分より優れてるスキルを持った人ばかりなので全部”委任型”でいいじゃんとなりがち問題があります。

しかしこれは思考放棄だと思い直して、対応するフェーズやタスクが求めるスキルによっては意識的に”委任型”だけではないやり方を模索したいです。

目標設定むずかしい

書内の”SMARTな目標設定”については納得感を持ったものの、実際の組織としての目標設定とは文脈が異なる部分もあって、SMARTエッセンスを適用して考えると結構足りてない部分がすでにありました。

一番難しいと感じているのが’T’の”Trackable(追跡可能性)”で、目標に対して適切な計測指標を設定して、進捗を可視化するのは、目標の質にもよりますが難しい部分もあるなと。

ただ、これはそのままSREの文脈でいうSLI/SLOの話でもあって、それを専門とするチームでもあるはずですから、そんなチームの目標のTrackableがゆるふわなのは苦々しいのでチームで少しずつ修正していく。

まとめ

冒頭にも書いた通りで、シンプルでわかりやすいエッセンスが詰まった良書でした。

読んだだけでは何もならず、日々のなかで自分なりの行動につなげていく必要があるのと、杓子定規に頭でっかちになるまいという自戒も味わえた点で価値ある読書体験になったと言えます。よっておすすめです!

余談:今一つふに落ちないエピソード

”委任型”についての解説でちょっとしたエピソードが紹介されているのですが、この落ちの面白さがわからずにもやっとしました。

とある家庭(両親と幼い娘さん)の話で、母親が家に仕事を持ち帰って夜遅くまで働いているのをみて娘が「お母さんをできない子のグループにすればよいのに」と父親に言うのが面白ポイントで、さらに「だったら、もっと委任すれば良いのに」がより良い回答として落ちとして提示されます。

ここの解釈は以下だと考えていますが、確信には至っていません(どうでもいいですね...)*7

  • 母親は、忙しいのだからもっと他のメンバに委任するべきだった?
  • 母親を「できない子グループにいれる」発言をする娘さんの大人びた発言が面白ポイント?

*1:本当に素晴らしい発表だったし、動画も公開されてます

*2:一方で人間こんなシンプルでもない感もあります

*3:評価と書くと多少語弊があって、会社に対してメンバがどのように貢献し、成長したかを伝えるための文章を書いたがより近い表現

*4:考えてみれば小学生から通知表で無機質な数値にランク付されてきたのだけれど感覚を忘れていた

*5:sonotsさんの資料ではこの部分を引用されていた

*6:新しい技術を教えてもらうことを通して

*7:もやっとして妻にも相談したところ「こんな発言をする娘さんが一番心配」と言われて、それはふに落ちました

Dockerが利用するサブネットを変更する

EC2で稼働していたDockerのサブネットを後から変更したのでメモします。 大体はこちらのブログの通りです。感謝。

support.getjoan.com

経緯

Apache Guacamoleという便利なソフトウェアをご存知でしょうか。簡単に言えば「RDPやVNCの集約ツール」です。WindowsやGUIを有効にしたLinuxなどへのログインを集約できます。

guacamole.apache.org

いずれEKSに載せようと思いつつも、適当に立てたEC2にdocker-composeでインストールした状態で便利に利用していたのですが、特定のサブネットのサーバにだけアクセスできない問題が発生していました。

  • 同じサブネット = 同じRouteTable
  • 同じSG

のEC2からは問題なく疎通ができていたので不思議に思ったのですが、基本に戻ってサーバのルーティングテーブルを確認したところ、Dockerが利用しているサーバ内部のネットワークと、接続できないサーバのネットワークアドレスが重複していました。

これを受けて、Dockerが使用するサブネットの変更を行うことにしました。

なおdocker-compose.ymlについてはGitHubでスターを集めていた以下を少し改変したものを利用しています。こちらも感謝。

github.com

手順

基本的な流れは以下の通りです。

  1. 稼働中のコンテナを停止
  2. 1で消えなかったネットワークがあれば docer network rm で削除
  3. /etc/docker/daemon.json を作成して default-address-pools を指定
  4. docker restart
  5. コンテナを起動

稼働中のコンテナを停止

まずは起動中のコンテナを停止します。docker-composeを利用していたので、 docker-compose down でNetwork Interfaceも削除します。 もちろん停止前にアプリケーションについてはバックアップを行うべきです(今回はGuacamoleのPostgresは事前バックアップ済みでした)

$ cd <docker-compose ファイルのある場所>
$ docker-compose down

ログは以下のような形ででます。

[root@morihaya-101 guacamole]# docker-compose down
Stopping guacamole_guacamole_1 ... done
Stopping guacamole_postgres_1  ... done
Stopping guacamole_guacd_1     ... done
Removing guacamole_guacamole_1 ... done
Removing guacamole_postgres_1  ... done
Removing guacamole_guacd_1     ... done
Removing network guacamole_guacnetwork_compose

消えなかったネットワークがあれば docer network rm で削除

以下のコマンドで、Dockerのnetworkがきれいになったことを確認しましょう。 残っていた場合は docker network rm xxxx で削除します。

ip a
docker network ls

/etc/docker/daemon.json を作成して default-address-pools を指定

daemon.jsonを作成し、Dockerが利用するネットワークを指定します。既存システムが使わないものにしましょう。

vim /etc/docker/daemon.json

jsonの内容は以下です。IP部分はシステムによりますね。

{
 "default-address-pools":
 [
 {"base":"10.254.0.0/16","size":24}
 ]
}

docker restart

dockerを再起動します。

systemctl restart docker.service

コンテナを起動

コンテナを再起動します。

docker-compose up -d

これで無事に既存システムと重複しないサブネットでコンテナを起動することができ、疎通も行えました。

余談

Apache Guacamoleですが、リリース周期は1年ごとと勝手に思っていたところ、5ヶ月間で1.20をリリースしていたのに気付きました。

  • 0.914 (2018-01-18)
  • 1.00 (2019-01-08)
  • 1.10 (2020-01-29)
  • 1.20 (2020-06-28)

guacamole.apache.org

1.20の冒頭にはserverとclientにプロジェクトを分割したなどが書かれていて、開発に動きがあったようで今後も動きが活発になるのかもしれません。

Apache Guacamole is split into two subprojects: "guacamole-client", the HTML5 web application which serves the Guacamole client to users, and "guacamole-server"

digdagでファイルシステム上のファイルをチェックして if> operator につなげるサンプル

既存システムとの連携などが、ファイルシステム上のファイルの有無による「フラグファイル」で行われることがままあります。

ファイルの存在のチェックし、その結果をdigdagの if> operator でやってみたという小ネタです。

Document - if>: Conditional execution

ファイル構成はシンプルに以下。

  • isfile.dig
  • bin/init.py
  • bin/check_file.py

digファイル

digファイルでは

  • チェックするファイルのパスflg_file_path を設定し
  • python オペレータでファイルチェックの結果をTrue or False で返す bin.check_file.CheckFile.check_file を呼び出します
_export:
  flg_file_path: /tmp/flg

+check:
  py>: bin.check_file.CheckFile.check_file

+judge:
  if>: ${check_result}
  _do:
    +exists:
      echo>: The file exists!
  _else_do:
    +nothing:
      echo>: The file does not exist.

Python

ファイルをチェックし、結果を返すPythonです。とてもシンプル。

以下の1行がほぼ全てです。 "check_result": os.path.isfile(digdag.env.params['flg_file_path']) }

digdag.env.storecheck_result 変数に os.path.isfile の結果を返しています。

# -*- coding: utf-8 -*-

import digdag
import os

class CheckFile(object):
    def check_file(self, check_result=""):
        digdag.env.store(
            {
                "check_result": os.path.isfile(digdag.env.params['flg_file_path'])
            }
        )

実行例

ファイルがある場合は "The file exists!" が表示されます。

bash-3.2$ digdag run --rerun isfile.dig
2020-07-31 22:54:45 +0900: Digdag v0.9.42
2020-07-31 22:54:47 +0900 [WARN] (main): Reusing the last session time 2020-07-31T00:00:00+00:00.
2020-07-31 22:54:47 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200731T000000+0000.
2020-07-31 22:54:47 +0900 [INFO] (main): Starting a new session project id=1 workflow name=isfile session_time=2020-07-31T00:00:00+00:00
2020-07-31 22:54:48 +0900 [INFO] (0017@[0:default]+isfile+check): py>: bin.check_file.CheckFile.check_file
2020-07-31 22:54:49 +0900 [INFO] (0017@[0:default]+isfile+judge): if>: true
2020-07-31 22:54:49 +0900 [INFO] (0017@[0:default]+isfile+judge^sub+exists): echo>: The file exists!
The file exists!

ファイルがない場合は "The file does not exist." が表示されます。

bash-3.2$ digdag run --rerun isfile.dig
2020-07-31 22:56:01 +0900: Digdag v0.9.42
2020-07-31 22:56:04 +0900 [WARN] (main): Reusing the last session time 2020-07-31T00:00:00+00:00.
2020-07-31 22:56:04 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200731T000000+0000.
2020-07-31 22:56:04 +0900 [INFO] (main): Starting a new session project id=1 workflow name=isfile session_time=2020-07-31T00:00:00+00:00
2020-07-31 22:56:06 +0900 [INFO] (0017@[0:default]+isfile+check): py>: bin.check_file.CheckFile.check_file
2020-07-31 22:56:07 +0900 [INFO] (0017@[0:default]+isfile+judge): if>: false
2020-07-31 22:56:07 +0900 [INFO] (0017@[0:default]+isfile+judge^sub+nothing): echo>: The file does not exist.
The file does not exist.

入門 監視を読みました

あまり余裕の無い数週間が終わり、のんびりできる休日がやってきたので積読になっていた「入門 監視 モダンなモニタリングのためのデザインパターン」を読みましたので感想をメモ。

f:id:morihaya:20200614233145p:plain

システム運用に関わるエンジニアには広くお勧めしたい一冊

読み終わった第一声としては「めっちゃ良かった」でした。新人時代からインフラエンジニアとしてキャリアを中心に積んできた手前、システム監視にはある程度経験を持っているつもりでしたが

  • ストーリーとして申し分ない章構成
  • 内容の抜群の粒度(入門、というタイトルの通りのレベル感)
  • 適切に整理され、上手く言語化されたノウハウの数々
  • Witに富んだTipsやメモ

などに魅了され一気に読破してしまいました。本の厚さとしても丁度よく梅雨の休日のおともに最適でしょう。

章構成について

名著とされる多くの専門書と共通し、本書の章構成も素晴らしいものとなっています。 目次はオライリー社のページで確認できますので見ていただければ良いですが、どのように素晴らしいかの感想を述べます。

シンプルな2部構成

本書は以下の2部から構成されています。

  • 第Ⅰ部 監視の原則
  • 第Ⅱ部 監視戦略

原則を学び、戦略を実践する。シンプルですがわかりやすいストーリーです。

歴戦の監視上級者であれば、第Ⅱ部から読み進めるのも悪くはありませんが、ぜひ第Ⅰ部から順に読み進めるのが良いでしょう。日頃から実践している工夫や取り組みが本文で言語化されて新たな自信につながるかもしれませんし、アンチパターンなどを通して新たな気づきを得られるでしょう。

原則の多くは基本だが、基本がどれほど重要か

第Ⅰ部は "監視の原則" と題して以下の4章で構成されています。

  • 1章 監視のアンチパターン
  • 2章 監視のデザインパターン
  • 3章 アラート、オンコール、インシデント管理
  • 4章 統計入門

"1章 監視のアンチパターン"で紹介されるいくつか*1に胸を痛めたあとで、"2章 監視のデザインパターン"でなるほどそれな!となり、"3章 アラート、オンコール、インシデント管理"で共感と明日から実践したいノウハウを学び、"4章 統計入門"でフワッとしていたいくつかの用語の理解を改めて行う素晴らしい流れです。

特に3章では、週単位でオンコールをチームで回しながら夜間アラートに叩き起こされる生活をしばしば送っているだけに、実体験を伴った共感と学びがありました。すぐやりたい内容としては

  • アラートの棚卸し
  • 自動復旧の取り組み

の2点で、不要なアラートの見直しと、そもそもアラートを上げさせないための自動復旧についてより意識して取り組んでいきたいと考えました。

もちろんこれまでも同様の動きはチームでもたびたび行ってきましたが、言語化されたことでより方針が固まった感触があります。

実践的な第Ⅱ部

第Ⅱ部は "監視戦略" と題して以下の章立てです。

  • 5章 ビジネスを監視する
  • 6章 フロントエンド監視
  • 7章 アプリケーション監視
  • 8章 サーバ監視
  • 9章 ネットワーク監視
  • 10章 セキュリティ監視
  • 11章 監視アセスメントの実行

私は大抵の技術書を読み始める前にざっと目次を確認するのですが、第Ⅱ部の各章の並びを見ただけで反省の気持ちと、この本はとても良いはずとある程度確信しました。

反省の理由として、私が日頃から意識し改善を行う監視を順番に並べるとすると以下になり、"5章 ビジネスを監視する"と"6章 フロントエンド監視"はあまり意識していませんでした。

  • 8章 サーバ監視
  • 9章 ネットワーク監視
  • 7章 アプリケーション監視
  • 10章 セキュリティ監視

しかし本書の章構成は私に対して以下の言葉を力強く語りかけてきたように感じました。

「何よりもまずはビジネスの観点が大事で、次にユーザに近いフロントエンドだ」

実際に章を読み進めるとKPIのダッシュボードは別のチームの別のツールで実現されていることに気づき「あれもまた監視だったのだ」と再発見につながりました。

フロントエンド監視についてもNewRelicやGoogle Analyticsを通して既に行っている内容も多くありましたが、リアルユーザ監視とシンセティック監視など言葉として整理されたことで理解が進んだと感じています。

付録もナイス

付録についても見過ごせません。特に付録Cについては MackerelのPMを担当されていたsongmu氏による監視SaaSへの手引書になっていて、これもまた読み応えのある内容でした。私はmackerelユーザの端くれでもあるため共感と共に読み進めていましたが"C.6.4 自動復旧のためのアイデア"でmackerel-agentのアクションを自動復旧に利用する部分を読み、すぐにでも実装したい機能を思いつくことができました。

mackerel-agentのアクション機能自体は知識としては知っていましたが、本書で具体的に自動復旧と紐付けられたことで実装のアイデアが浮かんできました。これでまたトイルを一つ滅ぼすことができそうで大変嬉しい気持ちです。

まとめ

以上、入門監視についてざっと感想を書きました。改めて良い本だったと思います。 なお余談ですが本書の大部分は家のリビングでそれぞれ自分のことをやっている家族を横目に黙々と読んでいたのですが、子供からは「表紙のトカゲ?がなんか怖い」とか、妻からは「監視...?」と訝られるなどエンジニアではない層にも話題性があってすごいと思いました。*2

*1:CPUなどOS監視をアラートとして通報とか

*2:これは半分冗談です

物理RHEL7サーバにUSBを接続したときのUSB対応バージョンによるログの違い

業務で少し古めのサーバにUSB3.0の外付けSSDドライブを接続する機会があり、USB3.0対応の機器と、USB2.0までしか対応していない機器が混在しており戸惑ったのでメモします。

調べ方としては接続した時のシスログ(/var/log/messages)を見ればOKです。

USB 2.0 (high-speed)

ポイントは high-speed USB の部分。ここに SuperSpeed と出てないので2.0と判断します。

厄介なのはUSBデバイス側が3.0対応のせいか Direct-Access TO Exter nal USB 3.0 などと3.0に見えるようなログが出るので注意したいです。

May 31 13:47:10 localhost kernel: usb 1-1.3: new high-speed USB device number 3 using ehci-pci
May 31 13:47:10 localhost kernel: usb 1-1.3: New USB device found, idVendor=0080, idProduct=a001
May 31 13:47:10 localhost kernel: usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
May 31 13:47:10 localhost kernel: usb 1-1.3: Product: External USB 3.0
May 31 13:47:10 localhost kernel: usb 1-1.3: Manufacturer: TOSHIBA
May 31 13:47:10 localhost kernel: usb 1-1.3: SerialNumber: 201503310007F
May 31 13:47:10 localhost mtp-probe: checking bus 1, device 3: "/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.3"
May 31 13:47:10 localhost kernel: usb-storage 1-1.3:1.0: USB Mass Storage device detected
May 31 13:47:10 localhost kernel: scsi host3: usb-storage 1-1.3:1.0
May 31 13:47:10 localhost kernel: usbcore: registered new interface driver usb-storage
May 31 13:47:11 localhost kernel: scsi 3:0:0:0: Direct-Access     TO Exter nal USB 3.0      0204 PQ: 0 ANSI: 6

最終行に USB 3.0 と出ているけど勘違いしないで!

USB 3.0 (SuperSpeed)

ポイントは SuperSpeed 。2.0が high-speed のため highとsuperのどっちが早いのか混乱しそうになります。 USBの世界では少なくとも SuperSpeed の方が早いらしいです。

May 31 14:17:35 localhost kernel: usb 5-2: new SuperSpeed USB device number 5 using xhci_hcd
May 31 14:17:35 localhost kernel: usb 5-2: New USB device found, idVendor=0080, idProduct=a001
May 31 14:17:35 localhost kernel: usb 5-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
May 31 14:17:35 localhost kernel: usb 5-2: Product: External USB 3.0
May 31 14:17:35 localhost kernel: usb 5-2: Manufacturer: TOSHIBA
May 31 14:17:35 localhost kernel: usb 5-2: SerialNumber: 201503310007F
May 31 14:17:35 localhost kernel: usb-storage 5-2:1.0: USB Mass Storage device detected
May 31 14:17:35 localhost kernel: scsi host16: usb-storage 5-2:1.0
May 31 14:17:36 localhost kernel: scsi 16:0:0:0: Direct-Access     TO Exter nal USB 3.0      0204 PQ: 0 ANSI: 6

余談

この時はUSBドライブ上にある数TBのデータを1GのLANで転送しようとしましたが、3.0対応のUSBドライブを取り外して転送先サーバに接続し直した方が速度が出ると思ってやったところ、転送先サーバがUSB2.0にしか対応しておらず結局LAN経由に戻したという経緯があります。

カタログスペックでの速度比較としては以下で、USB2.0を利用するよりはLAN経由の方がましですね。そして2019年に策定されたUSB4*1の桁違い感...

転送方法 速度(bps) 速度(Bps)
USB 2.0 480Mbps 60MBps
1G LAN 1,000Mbps 125MBps
USB 3.0 5,000Mbps 625MBps
USB4 40,000Mbps 5,000MBps

速度参考 - wiki/ユニバーサル・シリアル・バス

*1:USB4はSuperSpeedPlusと呼ぶそうです

digdagのループ for_each 利用時の _parallel 指定について

digdagのループ for_each 利用時の _parallel 指定について仕様を調べたのでメモ。

公式ドキュメントの Parallel execution にある通り「子タスクには効果があるが、孫タスクには効果が無い」ことを確認します。

If _parallel: true parameter is set to a group, child tasks in the group run in parallel (grandchildren are not affected):

ケース1: _parallel指定無し

_parallelを指定しなかった場合、デフォルトがシーケンシャルのため、全てがシーケンシャルに実行されます。

+testing:
  _export:
    sweet: &SWEETS
      - apple
      - banana
      - candy

  for_each>:
    sweet: *SWEETS

  _do:
    +task1:
      sh>: echo "$(date) - ${sweet} - 1" ; sleep 3

    +task2:
      sh>: echo "$(date) - ${sweet} - 2"

実行ログは以下のとおり。appleの1, 2が行われ、続いてbanana, candyという順に実行されます。

2020-04-15 22:26:29 +0900: Digdag v0.9.41
2020-04-15 22:26:41 +0900 [WARN] (main): Reusing the last session time 2020-04-14T00:00:00+00:00.
2020-04-15 22:26:41 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000.
2020-04-15 22:26:41 +0900 [INFO] (main): Starting a new session project id=1 workflow name=child session_time=2020-04-14T00:00:00+00:00
2020-04-15 22:26:42 +0900 [INFO] (0017@[0:default]+child+testing): for_each>: {sweet=[apple, banana, candy]}
2020-04-15 22:26:43 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task1): sh>: echo "$(date) - apple - 1" ; sleep 3
Wed Apr 15 22:26:43 JST 2020 - apple - 1
2020-04-15 22:26:46 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task2): sh>: echo "$(date) - apple - 2"
Wed Apr 15 22:26:46 JST 2020 - apple - 2
2020-04-15 22:26:46 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=1=banana+task1): sh>: echo "$(date) - banana - 1" ; sleep 3
Wed Apr 15 22:26:46 JST 2020 - banana - 1
2020-04-15 22:26:50 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=1=banana+task2): sh>: echo "$(date) - banana - 2"
Wed Apr 15 22:26:50 JST 2020 - banana - 2
2020-04-15 22:26:50 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=2=candy+task1): sh>: echo "$(date) - candy - 1" ; sleep 3
Wed Apr 15 22:26:50 JST 2020 - candy - 1
2020-04-15 22:26:53 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=2=candy+task2): sh>: echo "$(date) - candy - 2"
Wed Apr 15 22:26:53 JST 2020 - candy - 2
Success. Task state is saved at /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000 directory.
  * Use --session <daily | hourly | "yyyy-MM-dd[ HH:mm:ss]"> to not reuse the last session time.
  * Use --rerun, --start +NAME, or --goal +NAME argument to rerun skipped tasks.

ケース2: タスクの外で _parallel 指定は意味がない

以下のように、タスクの外で _parallel: true を指定しても効果がないことがわかりました。

_parallel: true

+testing:
  _export:
    sweet: &SWEETS
      - apple
      - banana
      - candy

  for_each>:
    sweet: *SWEETS

  _do:
    +task1:
      sh>: echo "$(date) - ${sweet} - 1" ; sleep 3

    +task2:
      sh>: echo "$(date) - ${sweet} - 2"

ログは以下の通り

2020-04-15 22:30:42 +0900: Digdag v0.9.41
2020-04-15 22:30:54 +0900 [WARN] (main): Reusing the last session time 2020-04-14T00:00:00+00:00.
2020-04-15 22:30:54 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000.
2020-04-15 22:30:54 +0900 [INFO] (main): Starting a new session project id=1 workflow name=child session_time=2020-04-14T00:00:00+00:00
2020-04-15 22:30:55 +0900 [INFO] (0017@[0:default]+child+testing): for_each>: {sweet=[apple, banana, candy]}
2020-04-15 22:30:56 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task1): sh>: echo "$(date) - apple - 1" ; sleep 3
Wed Apr 15 22:30:56 JST 2020 - apple - 1
2020-04-15 22:30:59 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task2): sh>: echo "$(date) - apple - 2"
Wed Apr 15 22:30:59 JST 2020 - apple - 2
2020-04-15 22:30:59 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=1=banana+task1): sh>: echo "$(date) - banana - 1" ; sleep 3
Wed Apr 15 22:31:00 JST 2020 - banana - 1
2020-04-15 22:31:03 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=1=banana+task2): sh>: echo "$(date) - banana - 2"
Wed Apr 15 22:31:03 JST 2020 - banana - 2
2020-04-15 22:31:03 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=2=candy+task1): sh>: echo "$(date) - candy - 1" ; sleep 3
Wed Apr 15 22:31:03 JST 2020 - candy - 1
2020-04-15 22:31:06 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=2=candy+task2): sh>: echo "$(date) - candy - 2"
Wed Apr 15 22:31:06 JST 2020 - candy - 2
Success. Task state is saved at /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000 directory.
  * Use --session <daily | hourly | "yyyy-MM-dd[ HH:mm:ss]"> to not reuse the last session time.
  * Use --rerun, --start +NAME, or --goal +NAME argument to rerun skipped tasks.

ケース3: タスク内で _parallel 指定はループの要素に効果はあるが、doの中には効果がない

タスクの中で _parallel: true を指定すると apple, banana, candy という各要素についてはパラレルで実行されるが、 _do: で指定したタスクはシーケンシャルで実行されました。子タスク=要素のループで、孫タスク=_doの中のタスクと分かりました。

+testing:
  _parallel: true
  _export:
    sweet: &SWEETS
      - apple
      - banana
      - candy

  for_each>:
    sweet: *SWEETS

  _do:
    +task1:
      sh>: echo "$(date) - ${sweet} - 1" ; sleep 3

    +task2:
      sh>: echo "$(date) - ${sweet} - 2"

以下はログ。apple, banana, candy はパラレルで実行されるため、順番も入れ替わって見えますが、task2 が task1 より先に実行されることはありません。

2020-04-15 22:35:13 +0900: Digdag v0.9.41
2020-04-15 22:35:26 +0900 [WARN] (main): Reusing the last session time 2020-04-14T00:00:00+00:00.
2020-04-15 22:35:26 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000.
2020-04-15 22:35:26 +0900 [INFO] (main): Starting a new session project id=1 workflow name=child session_time=2020-04-14T00:00:00+00:00
2020-04-15 22:35:27 +0900 [INFO] (0017@[0:default]+child+testing): for_each>: {sweet=[apple, banana, candy]}
2020-04-15 22:35:29 +0900 [INFO] (0018@[0:default]+child+testing^sub+for-0=sweet=1=banana+task1): sh>: echo "$(date) - banana - 1" ; sleep 3
2020-04-15 22:35:29 +0900 [INFO] (0019@[0:default]+child+testing^sub+for-0=sweet=2=candy+task1): sh>: echo "$(date) - candy - 1" ; sleep 3
Wed Apr 15 22:35:29 JST 2020 - candy - 1
Wed Apr 15 22:35:29 JST 2020 - banana - 1
2020-04-15 22:35:29 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task1): sh>: echo "$(date) - apple - 1" ; sleep 3
Wed Apr 15 22:35:29 JST 2020 - apple - 1
2020-04-15 22:35:32 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task2): sh>: echo "$(date) - apple - 2"
2020-04-15 22:35:32 +0900 [INFO] (0019@[0:default]+child+testing^sub+for-0=sweet=1=banana+task2): sh>: echo "$(date) - banana - 2"
Wed Apr 15 22:35:32 JST 2020 - apple - 2
Wed Apr 15 22:35:32 JST 2020 - banana - 2
2020-04-15 22:35:32 +0900 [INFO] (0018@[0:default]+child+testing^sub+for-0=sweet=2=candy+task2): sh>: echo "$(date) - candy - 2"
Wed Apr 15 22:35:32 JST 2020 - candy - 2
Success. Task state is saved at /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000 directory.
  * Use --session <daily | hourly | "yyyy-MM-dd[ HH:mm:ss]"> to not reuse the last session time.
  * Use --rerun, --start +NAME, or --goal +NAME argument to rerun skipped tasks.

ケース4: タスク内でも、do内でも parallel 指定をすると全てがパラレルで実行される

要素についても、タスクについてもパラレルで実行する場合のケースです。

+testing:
  _parallel: true
  _export:
    sweet: &SWEETS
      - apple
      - banana
      - candy

  for_each>:
    sweet: *SWEETS

  _do:
    _parallel: true
    +task1:
      sh>: echo "$(date) - ${sweet} - 1" ; sleep 3

    +task2:
      sh>: echo "$(date) - ${sweet} - 2"

全てが同じ時間に実行されています。

2020-04-15 22:40:14 +0900: Digdag v0.9.41
2020-04-15 22:40:26 +0900 [WARN] (main): Reusing the last session time 2020-04-14T00:00:00+00:00.
2020-04-15 22:40:26 +0900 [INFO] (main): Using session /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000.
2020-04-15 22:40:26 +0900 [INFO] (main): Starting a new session project id=1 workflow name=child session_time=2020-04-14T00:00:00+00:00
2020-04-15 22:40:27 +0900 [INFO] (0017@[0:default]+child+testing): for_each>: {sweet=[apple, banana, candy]}
2020-04-15 22:40:28 +0900 [INFO] (0018@[0:default]+child+testing^sub+for-0=sweet=0=apple+task2): sh>: echo "$(date) - apple - 2"
2020-04-15 22:40:28 +0900 [INFO] (0019@[0:default]+child+testing^sub+for-0=sweet=1=banana+task1): sh>: echo "$(date) - banana - 1" ; sleep 3
Wed Apr 15 22:40:28 JST 2020 - banana - 1
Wed Apr 15 22:40:28 JST 2020 - apple - 2
2020-04-15 22:40:28 +0900 [INFO] (0020@[0:default]+child+testing^sub+for-0=sweet=1=banana+task2): sh>: echo "$(date) - banana - 2"
2020-04-15 22:40:28 +0900 [INFO] (0022@[0:default]+child+testing^sub+for-0=sweet=2=candy+task2): sh>: echo "$(date) - candy - 2"
Wed Apr 15 22:40:28 JST 2020 - banana - 2
Wed Apr 15 22:40:28 JST 2020 - candy - 2
2020-04-15 22:40:28 +0900 [INFO] (0021@[0:default]+child+testing^sub+for-0=sweet=2=candy+task1): sh>: echo "$(date) - candy - 1" ; sleep 3
Wed Apr 15 22:40:28 JST 2020 - candy - 1
2020-04-15 22:40:28 +0900 [INFO] (0017@[0:default]+child+testing^sub+for-0=sweet=0=apple+task1): sh>: echo "$(date) - apple - 1" ; sleep 3
Wed Apr 15 22:40:28 JST 2020 - apple - 1
Success. Task state is saved at /Users/morihaya/work/testdig/.digdag/status/20200414T000000+0000 directory.
  * Use --session <daily | hourly | "yyyy-MM-dd[ HH:mm:ss]"> to not reuse the last session time.
  * Use --rerun, --start +NAME, or --goal +NAME argument to rerun skipped tasks.

まとめ

当たり前ですが、以下のドキュメントの通りの動きを確認できたので満足しました。 ループと _parallel の組み合わせは強力ですが乱用するとキューが詰まって他のWorkflowに影響が出ることもありますので要注意ですね。

If _parallel: true parameter is set to a group, child tasks in the group run in parallel (grandchildren are not affected):

AWS WorkspacesのDirectryを削除しようとしたら "Cannot delete the directory because it still has authorized applications" が出る対策

検証で利用していたAWS Workspacesのお掃除をしていたところ、以下のエラーが発生して消せなくて焦りました。

An Error Has Occurred Cannot delete the directory because it still has authorized applications. Additional directory details can be viewed at the Directory Service console.

f:id:morihaya:20200403171821p:plain
エラー画面

対策: 利用中のアプリを先に削除する

原因は全てのアプリを削除していないためでした。エラーメッセージのリンク先に飛ぶと、ディレクトリの詳細ページが表示され、以下のように利用中のアプリケーションの一覧んを確認できます。

この図では Amazon WorkDocsEnabled になっているため、ディレクトリを削除できなかったのです。

f:id:morihaya:20200403172317p:plain
利用中のアプリ一覧

リンク先へ飛びWork Docsも削除します。WorkDocsの削除画面には親切なことに I also want to delete the user directory オプションが表示されましたので、チェックを入れて削除を行います。

f:id:morihaya:20200403172624p:plain
WorkDocs削除画面

念のため Workspacesのディレクトリ一覧に戻ると、 deleting ステータスを確認できました。こちらでお掃除が完了です。

f:id:morihaya:20200403172949p:plain
削除されつつあるディレクトリの図

まとめ。

GUIでサクサクと起動できるのはいいですが、裏で複数サービスが連携して作成されることで、一箇所で削除ができないのはよくある話ですが、直面すると少し焦りますね。