Release Engineering / CDについて最近読んだ記事のメモ

  • 昨晩に読んだCD関係の記事が面白かったので、忘れるまでに概要と感想をメモ。

Deploy != Release (Part 1)

Deploy != Release (Part 1). The difference between deploy and… | by Art Gillespie | Turbine Labs

概要

  • 同じような意味で使われがちだが、DeployRelease は全く別の営み
    • 混乱して使われがちなこれらの用語の定義を整理し、言葉をより正確に使うことで、Software Release,CDなどの問題をより解像度高く考えることができる
  • 用語の定義

    • Deploy : Production環境にSoftwareをInstallする作業
    • Release : DeployされたSoftwareに対し、Production TrafficをRe-routingする作業
  • Release In Place, つまり、ビルドしたアプリケーションを既存のアプリを全入れ替えする形でリリースすると、DeployとReleaseを同時に実行することになるので、Deployのリスクが顧客に転嫁されてしまう。そうではなく、DeployとReleaseを疎結合(decouple)にすることでリスクを分割することが重要で、方法としてBlue / Greenがある(詳細はPart2)

  • Release In Place,により(耐障害性的な意味で)壊れてしまったアプリケーションが、Rollbackによって復旧されるとは限らない。壊れる前のState*1が復旧される保証はどこにもないのだから。

*1 これは多義的で、Systemのhealt . DataのConsistency両方を含む?

感想

  • 個人的に、Release, Deployの定義は有用だと感じました。上記の定義を使うと、「k8s manifestで定義のアプリを更新したら、起動に失敗した / liveness Probeに失敗した」は「Deploy Failure」であり、「更新したアプリがユーザーからのhttpリクエストを受け取ったが処理に失敗し500を返した。この種のリクエストはそれまでは処理に成功していた」みたいな「Release Failure」とは別の問題と考えることができる。そうすることで、我々がCDのDeployment Strategyでやりたいことをより解像度高く考えることができるのではないか?と思いました。

  • ReleaseによるSoftware故障の解決策として、Rollbackが万能ではないという話に同意します。以前から漠然と思っていたことを言語化してもらった気持ちになりました

Deploy != Release (Part 2)

Deploy != Release (Part 2). Decouple deploy and release, mitigate… | by Art Gillespie | Turbine Labs

概要

  • Part1を受けて、DeployとReleaseを疎結合にする手段としてのBlue/Greenの話
  • BllueGreenではDeployとReleaseを分けたい。だから...
    • まず先にDeployだけする。アプリケーションを配備・起動し、特定のPortでListenさせる状態までもっていく
    • 次にProduction Trafficを向ける前に何らかのTestingを行い、、Production Trafficを向けても大丈夫だよね?ということを確認する
    • その後初めて、商用トラフィックをReroutingする

感想

Every Release is a Production Test

Every Release is a Production Test | by Mark McBride | Turbine Labs

概要

  • 一般に、ProductionにSoftwareをReleaseするときに、Release直後の監視メトリクスの挙動を見て問題がないかどうかを確認したりするが、そうであるならば、そこで行っていることは本質的には、Production環境でTestingをしているという事に他ならない

  • 経済活動において、BenefitがCostを上回れば「利益がある」ということでそれをやることの正当性が担保されるように、TesingにおいてもBenefitがCostを上回るのであればそれを行うことは正当である。そう考えると、Production Tesingは適切に実行するとBenefitがCostを大きく上回る

感想

  • 「Testが十分になされ、安全性が確認されてからリリースする」という感覚自分の中にあったので、「Real World Tesing」的な思想は苦手なのですが、Prudction環境においてObserbabilityを適切にEnginneringしつつ、ユーザー影響の発生しないRelease Enginneeringをすることで単位時間における学習量、得られた知見の量を最大化しようという方向性が今後は増えてくるのかなと思いました。(Chaos Engineeringもそうですが)

そうしますと、最適なシステムの在り方も少し変わってくると思いますので、観点として頭の片隅に置いておこうと思います。

Scala入門

Scalaに入門したので、覚書。

波括弧とブロック

  • 以下の2つは等価となる
// 波括弧()の中には式は1つしか書けない
scala> Seq(Seq(1,2),Seq(3,4)).flatten.map(a => a * 2)
res27: Seq[Int] = List(2, 4, 6, 8)

// ブロックの場合、2つ以上の式を {...} 内に書くことが可能
scala> Seq(Seq(1,2),Seq(3,4)).flatten.map { a => a * 2}
res28: Seq[Int] = List(2, 4, 6, 8)

mapの引数に _ を使うパターン

  • 以下2つは等価となる
scala> Seq(Seq(1,2),Seq(3,4)).flatten.map(a => a * 2)
res35: Seq[Int] = List(2, 4, 6, 8)

scala> Seq(Seq(1,2),Seq(3,4)).flatten.map(_ * 2)
res36: Seq[Int] = List(2, 4, 6, 8)

flatten

  • 入れ子構造になっているSeqを展開する事ができる
scala> Seq(Seq(1,2),Seq(3,4)).flatten
res14: Seq[Int] = List(1, 2, 3, 4)
  • 展開される入れ子構造は1階層のみ
scala> Seq(Seq(1,2),Seq(3,Seq(4,5,6))).flatten
res33: Seq[Any] = List(1, 2, 3, List(4, 5, 6))

flattenとflatMap

  • 以下の2つは等価となる -> つまり、flatMapはmapを実行後にflattenしてくれている??
scala> Seq("Hello","World").map(_.toSeq).flatten
res17: Seq[Char] = List(H, e, l, l, o, W, o, r, l, d)

scala> Seq("Hello","World").flatMap(_.toSeq)
res18: Seq[Char] = List(H, e, l, l, o, W, o, r, l, d)

メソッドのジェネリクス

// メソッドの宣言
scala> def method[T](t: T):T = { t }
method: [T](t: T)T

// 使用例 1
scala> val s = method(1)
s: Int = 1

// 使用例 2
scala> val s = method("Hello World")
s: String = Hello World

高階関数

// もらった引数を、コールバック関数に渡して実行するだけの関数
scala> def func(n: Int, f: Int => Int): Int = { f(n) }
func: (n: Int, f: Int => Int)Int

// 使用例
scala> func(2,  _ * 2)
res37: Int = 4

小ネタ: dfとlsblkの結果からloopback deviceを除外する

背景

  • Ubuntuでsnapを使っていると、 df(1)lsblk(1) の結果に loop が沢山表示されてnoisy
  • outputからloopback deviceを除外したい

手順メモ

dfの場合

  • -x オプションでFilesystem Typeを渡すことで、outputをfilterできる
# loop(squshfs)とtmpfsを除外
$ df -x squashfs -x tmpfs
Filesystem     1K-blocks     Used Available Use% Mounted on
udev             5977180        0   5977180   0% /dev
/dev/sda3      474749992 81447660 369116684  19% /
/dev/sda1         967320   178332    722636  20% /boot
/dev/sdb1         262144    50520    211624  20% /boot/efi

.bashrc への追加例

# dfからtmpfsを除外
alias df='df -x squashfs -x tmpfs'

参考:How to clean up snap and loop devices from df output in Ubuntu 18.04 and 20.04

lsblkの場合

  • -e or --exclude オプションにblock device番号を渡すことで、そのBlock Deviceを実行結果から除外することができる
# loopback deviceは7番
$ sudo cat /proc/devices | grep "Block" -A 1
Block devices:
  7 loop

# lsblkの実行結果らかloopを除外
$ lsblk -e 7
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0 465.8G  0 disk 
├─sda1   8:1    0   976M  0 part /boot
├─sda2   8:2    0   3.8G  0 part [SWAP]
└─sda3   8:3    0   461G  0 part /

参考:Exclude loop* snap devices from lsblk output?

メモ:DATETIME型はMySQL 5.6からミリ秒の取り扱いが変わった

MySQL 5.6からミリ秒の取り扱いが変わった

バージョンによって挙動が異なるので注意が必要です。 MySQL 5.5 までは、強制的に切り捨てされて格納、MySQL 5.6 以降は四捨五入されて格納されるという動作になっています。

doruby.jp

以下、実験ログ

ysql> CREATE TABLE fractest( c1 TIME(2), c2 DATETIME, c3 TIMESTAMP(2) );
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO fractest VALUES  ('17:51:04', '2018-09-08 17:51:04.499', '2018-09-08 17:51:04');
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO fractest VALUES  ('17:51:04', '2018-09-08 17:51:04.500', '2018-09-08 17:51:04');
Query OK, 1 row affected (0.03 sec)

// 2つめのレコードのみ、小数点第一位が四捨五入された

mysql> select * from fractest;
+-------------+---------------------+------------------------+
| c1          | c2                  | c3                     |
+-------------+---------------------+------------------------+
| 17:51:04.00 | 2018-09-08 17:51:04 | 2018-09-08 17:51:04.00 |
| 17:51:04.00 | 2018-09-08 17:51:05 | 2018-09-08 17:51:04.00 |
+-------------+---------------------+------------------------+
2 rows in set (0.00 sec)

SpringBootで作成したExecutableJARをgcloud app deploy できない

環境

  • AppEngine Standard 第2世代
  • Java11
  • SpringBoot 2.2.1

問題

  • ./mvnw package で生成のExecutableJARを gcloud app deploy しようとすると、以下のようにコマンドの実行に失敗する
  • JARファイルのルックアップに失敗している様子??
$ gcloud app deploy
(略)
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 111 files to Google Cloud Storage              ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...failed.                                                                                                                                  
ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build XXXX-XXXX-XXXX-XXXX-XXXXXXXX status: FAILURE
Error ID: 838926df
Error type: UNKNOWN
Error message: did not find any jar files with a Main-Class manifest entry
Full build logs: https://console.cloud.google.com/cloud-build/builds/XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX?project=XXXXXXXXXXXXXXXX

対処

  • Executable JARの相対パスentrypoint: として指定する内容の app.yaml を追加したら、エラーがでなくなりました。 gcloud app deploy の実行も成功しました
$ cat app.yaml 
runtime: java11
entrypoint: java -Xmx64m -jar target/myapp-0.0.1-SNAPSHOT.jar

原因

  • 以下に記載の Alternatively, App Engine will use the contents of the optional entrypoint field in your app.yaml file. の手順を実行した形ですが、なぜこれが必要なのか理解できていません。またちゃんと調べようと思います。
    • デフォルトだと、どのようなルールに従いJARを探しにいくのかがよくわかっていません

その他参考にしたサイト

読書メモ:データモデル大全

本の概要

  • 業務アプリケーションに使われる様々なDBのデータモデルを紹介&解説している本です
  • ER図ではなくデータの具体例を付記することが可能な独自形式のデータモデル図を採用しています
    • 個人的には、ER図よりもテーブルの中身がイメージしやすく好きです

読書メモ

データモデルの表記方法

  • 主キーは { ... } で表現する

参照関係

  • あるテーブルの主キーが別のテーブルの非主キーから参照される場合、「2つのテーブルの間に参照関係がある」と考える
    f:id:smatsuzaki:20200829095503p:plain
    P30 図 1-12の図を元に作成

親子関係

  • あるテーブルの主キーが別のテーブルの複合主キーに使われる場合、「2つのテーブルの間に親子関係がある」と考える
    f:id:smatsuzaki:20200829095815p:plain
    P29 図1-11を元に作成