まっしろけっけ

めもてきなやーつ

高速に機能開発を行う際の思考

はじめに

僕自身は普段の開発において手が早いと言われることが多く、現職も含めて 3 社全てでそう言われてきたので実際にそうなのだろうなという気がしている。
見積もりをする際も「 n 時間(またはポイント)ですね」と答えると「じゃ他の人ならそれの 2 〜 3 倍くらいかな」みたいな会話をされたこともある。

何故自分は手が早いんだろうか?他の人との違いは何なのだろうか?というのを考えはじめたのだが、他人の頭の中を覗くのは不可能なので
一般的な機能開発に絞って自身がどのような思考で開発を行なっているのか?というのを考えてみた。(他の部分でもこういう思考をしているかもしれないが今回は機能開発の際にどういう思考になっているかしかまとめません)

要件定義

新しい機能を作る際はだいたい誰かしらが◯◯という課題を解決するために◯◯という機能を作りたいというのから始まり、本当にユーザが求めてるのはその機能なのか?という話だったりその機能で課題を解決できるのか?みたいな話をすると思うんですよ。この段階では特にそれについて考える以上のことはないので省略しますが、大事なのはこの後の機能を作る際の仕様を固めていく要件定義の段階。

結論から言うと要件定義が終わった段階で自身の頭の中には動くコードが既に出来上がっています。(自分の中ではコードが降って来たと表現している)

何故そのようなことになるかと言うと大事なのはパターンなのかなと。

何かの機能を作るとなった場合、ほとんどの場合(極)一部だけ同じような仕様や似たような仕様の場合がほとんどだと僕は思っていて、そういう過去に経験した似たパターンを組み合わせることによって新しい 1 つの機能を作ることが可能になると考えています。

例えば数値を入力させる場合上限と下限は幾つが良いのか?というのも要件定義に含まれると思いますが、このような入力に上限/下限を設けるのも一つのパターンとして捉えています。

このように細かいパターンを組み合わせることによって一つの機能が出来上がると捉えているので要件定義が終わった段階で僕の頭の中にはパターンが積み重なって出来た 1 つの機能が出来上がっているわけです。

さらにこのパターンは頭の中で実際のコードと紐づいているんですよね。先の数値の上限/下限であれば Validation が必要で Rails だと model に greater_than_or_equal_to などを使って記述するのような感じで。
なのでこのパターンが積み重なって 1 つの機能が出来上がった段階でそのパターンに紐付く実際のコードも出来上がるということになります。

全てが自身の頭の中にあるパターンに当てはまらないかもしれませんが、細かく分解すればほぼ当てはまるしもし当てはまらなくても当てはまらないということが発生することがまだ知らないことがあってそれを解決できるという楽しさに繋がっていると感じています。当てはまらない場合も機能全体が当てはまらないのではなくその機能の極一部なので他の部分のコードは既に頭の中では出来上がっているので当てはまらない部分を集中して考えるだけになります。

実際にコードを書く

コードを書く際は頭の中にあるコードをタイピングするだけなので、その際にも他のことを考えています。
例えば何かユーザにフォームから入力される場合は上記でまとめたパターンに漏れがないか?を頭の中で様々な入力に対してコードを走らせてみてみたり、仕様の漏れを探すということをすることが多い気がします。でこの仕様の漏れなどがあった場合はそれを test として書きどのような結果が適切なのかをコードに落とし込んでおく。

1 日で全てのコードを書き切れる程度の機能ならいいのですが、大きい機能だとそういう訳にもいかないので基本的に必ず機能単位でのキリが良いところで終わりにします。
これは翌日などに続きをするとなった際にどこまでやったっけ?と思い出すのを少なくするためです。
また、1 日で終わらない場合は翌日のコードを書き始める前に仕様をもう一度確認して頭の中にパターンを思い出させるということをやります。

コードを書く際にこれも大事なことだと思っているのですが、帰宅中や犬の散歩,お風呂などで書いたコードに対してリファクタリングできないか?をよく考える気がします。
タイピングしている際もここは DRY に出来るから module にまとめておくかみたいなことはよくあるのですが、一旦時間を置いて自身のコードと向き合うというのが重要かと思います。この際はビジネスロジックに関して考えるとこはせずあくまでリファクタリングに関してのみ考えています。(ビジネスロジックまで考えると仕事感が出すぎるので好きなリファクタだけを考えている)
これによってレビュー時の指摘なども減らせます。

その後

そのあとは実際に動作検証とかをしてリリースになると思うので特別考えていることはないですが、 test に書いてあることを何回も動作検証を行わないとか決済系以外はリリース後に問題(バグ)があってもまぁなんとか出来るだろうという考えでいるのでガンガンリリースしようという考えだという程度かなと思います。

さいごに

ということで機能開発を行う際にどのような思考で開発を行なっているか?をまとめてみた。
一文で表すと

パターンを蓄積しパターンを適切に配置、パターンと実際のコード(処理)を紐付けておくことによって高速に機能開発が出来ている

というのが僕が手が早いと言われる理由の一端かなともちろんこれが全てではないと思うので機能開発に関わらず書くかもしれない。


ちなみにこの記事は 7/15 に書いていたのですが同僚ライバルの pyama が同じようなテーマ(彼の場合は高速に開発するための自身の行動をどうするか?みたいな話)でブログを更新してたので公開を先延ばしにしました。

エンジニアとしての境界を超えることについて

はじめに

web service というものを開発するエンジニアには サーバサイド/インフラ/フロントエンド/iOS/Android などのそれぞれの専門(強み)を持ったエンジニアが存在していると思います。その専門性を境界として見た際に越境する/しないエンジニアではどのような違いがあるのかというのを自身の経験などから僕自身が考えている事をまとめていきます。

自身について

10 年以上お金を貰ってソフトウェアエンジニアをやっていて、その過程でサーバサイド/インフラ/フロントエンド/Android に関しての実務を一定期間行ってきたという経歴があります。現在は主にサーバサイド/インフラを中心として minne というサービスのシニアエンジニアリングリードというものをやっている。

書いたような領域以外でも DevOps みたいな領域の違いみたいなものもあると思いますが、僕自身 Dev と Ops に違いがあるというのは全然感じたことがなく Effective DevOps という書籍を読んでもふむ〜?となったし弊社の VPoE の @hsbt に 「DevOps 上手い」というような事を言われた記憶があるのだが(うろ覚え)なるほど(よくわかっていない)となった記憶がある。

越境する/しないの違い

上記で説明した通り僕自身は越境した側の人間でしてなかった時とした後で実際にどういう変化があったのか?というと細かくあげるといっぱいあるのだが大きいものを 2 点ほどあげる。他にも

1. それぞれの苦労が理解できる

それぞれの領域のエンジニアが、(特に)領域が繋がる部分でどのような事で苦労するか?やどのような状態なら嬉しいか?がわかるようになった。

例えば API のレスポンスで json を返す際に特定の値が基本的には文字列だが数字のみだったら int 型のような API だった場合にアプリエンジニアとしてはとても困るという事が発生するんですけど、iOSAndroid 側に越境した事がある開発者であればその事を理解できているはずなのでそもそもそんなレスポンスになる API は作らないという事になるはず。

この事によってリリース後に気づいて慌てて修正するだったり、リリース前の検証で発覚して修正をするというような手間が発生せず効率的に開発ができるという事になります。越境した事があるエンジニアがチームに 1 人いるだけでも仕様検討やレビューの段階でこのような事に指摘をしてくれるのでチームとしての生産性も向上すると思われます。

2. 各領域の話が理解できる

これは自分の現在の立場的によかったと感じるというのが大きいかもしれないのですが、シニアエンジニアリングリードというのは事業部のリードエンジニア兼エンジニアリングマネージャーなのでシステム全体に関して色々な事を考えたり時には各エンジニアに技術的な仕様について相談されたりという事があったり、部署のエンジニアを評価するという事が必要になってくる。

上記のような事があり、越境しているおかげでどういう事がしたいのか?だったり技術的にどんな高度な事をしているのか?を判断できるようになったという部分がある。

やってみたと実務の違い

それぞれの領域でとりあえずチュートリアルやって動くもの作りましたというのだけで越境したと捉えられる人もいるかもしれませんが、それだけでは継続的に開発をして得る事のできる知識とは雲泥の差がある為、実務にこだわらずともそれぞれの領域と関わる形(Android アプリなら API を利用する等)で継続的に開発を行ったことがあるという事を越境した事があると捉えています。

越境について

それじゃみんな越境すればいいのか?とか全ての技術を平均的にできたらいいのか?という話になると思うんですが、これに対する僕の考えを書いておくと

みんな越境すればいいのか?

これに対する僕の考えは(経験年数が浅い人ほど)興味があるならして欲しい。なぜかというと先に述べた 1 のメリットが大きいという考えからです。
また、越境した結果その分野が実はその人が一番楽しいと思える分野かもしれないからです。僕自身はサーバサイド/インフラから始まり一通り終えてサーバサイド/インフラがやっぱり楽しいと感じましたが、どこかでサーバサイド/インフラより面白いと感じるものがあったのならそこに留まっていたと思うし、仕事をするなら楽しいと思える事をやりたいじゃないですか。経験年数が浅い人ほどというのをつけたのは歳を取ると越境する体力みたいなのが徐々になくなってくるのかなぁと思うからです(個人の感想)

全ての技術を平均的にできたらいいの?

これ今回一番書きたかったことかもしれないんですが、僕自身としてはこの答えは no です。
時間は有限ですし、僕自身は全ての技術を高レベルで習得するというのは無理だと考えています。なので特定の技術領域に軸足を置きつつ他の領域のことも理解しているというのが良いと思います。(よく T 型と呼ばれるやつ)
一点だけを突き詰めていくんだという強い気持ちがある人はその限りではなくその一点を突き詰めていって欲しいですし。

なぜ上記のように考えるかというと僕のチームで働いている人には自身の市場価値を意識して欲しいなという考えがあって、僕のチームはそこまで多くないですがこの業界は転職する人も割と多くいる業界なので(勿論そうなって欲しくはないし、一緒に働けなくなるのは悲しいという前提はあるものの)チームメンバーが転職するとなる自体はある程度覚悟しているんですよ。

でその転職するとなったメンバーが一番入りたいと思った会社から採用されるエンジニアになって欲しいという思いがあるからで恐らく平均的にアプリからバックエンドまで出来るエンジニアと iOS アプリに関して深い理解があり技術的にも優れているエンジニアが iOS エンジニアのポジションに応募した場合に後者が採用される率の方が高いわけですよ。更にいうと iOS エンジニアとしての技量が同じくらいだった場合はその他の領域の事が少し出来るという人の方がチームとして働く上でプラスに捉えられる事が多いと思うのでそういう意味もあります。

全ての技術を平均的に出来るエンジニアというのはチームとしてはとても扱いやすいというのがあると思います。どこかの領域のリソースが足りなかったら今週はそこのタスクをやってもらって〜と動けるので...ただそういう便利屋さん的な人がいざ外に出るとなった際に便利屋さん的ポジションを大々的に求めている会社というのは少ないと思います。(スタートアップを除く、スタートアップでも T 型とかの方がいいのかなぁ?働いた事ないので知らない)
自ら望んでそういうエンジニアになるというのは良いと思いますが、強制的にそういうエンジニアにするというのは僕個人としてはその人のエンジニアとしての未来を潰しているという感じがしてしまうので行なっていません。

まとめ

長々と書きましたが、うちのチームでは基本的に「好きな(自分がやりたい)技術をやればいいよ」と言っており実際にどういうエンジニアになりたいか?は各々に任せています。

僕はメンバーが転職した際は転職先の人に強いエンジニアが来たって思って欲しいんですよね(勿論そうなって欲しくはないし、一緒に働けなくなるのは悲しいという前提はあるものの大事な事なので 2 回)

エンジニアとしての境界について書きましたが似たような事は他の職種間でも当てはまるのでは?と思います。

長文の日本語を書くのはまだまだ下手...

Golang で ImageMagick を使わずに画像をいじる②

はじめに

shiro-16.hatenablog.com

前回は上記の記事で変換/切り抜き/合成あたりをやりました。
今回は画像のリサイズを行いたいと思います。

画像のリサイズ

まずは画像の縦横のサイズを半分にしてみます。

package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"os"
	"golang.org/x/image/draw"
)

func main() {
	flag.Parse()
	args := flag.Args()

	f, err := os.Open(args[0])
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create(args[1])
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	rct := img.Bounds()
	
	dst := image.NewRGBA(image.Rect(0, 0, rct.Dx() / 2, rct.Dy() / 2)) // ここで 1/2 のサイズ指定をしている
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, rct, draw.Over, nil)

	jpeg.Encode(fso, dst, &jpeg.Options{Quality: 100})
}

元の画像が 150x150 なので 1/2 することで 75x75 の画像が出来上がります。
それが下記の画像

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200605113615j:plain

今回は width と height 両方を 1/2 しましたが image.Rect(0, 0, rct.Dx() / 2, rct.Dy() / 2) の部分を image.Rect(0, 0, 100, 50) などに変更すると下記のような画像が出来上がります

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200605114055j:plain

アスペクト比を無視して縮小が行われるので潰れた画像が出来上がってしまう。

そこでアスペクト比を維持したまま長辺のサイズを指定するというのを実現するのが下記のコード

// 上記の defer fso.Close() まで略
	arg, _ := strconv.ParseFloat(args[2], 64)

	rct := img.Bounds()

	var w, h int
	if rct.Dx() > rct.Dy() { // 横長画像
		m := arg / float64(rct.Dx())
		w = int(arg)
		h = int(float64(rct.Dy()) * m)
	} else { // 縦長画像
		m := arg / float64(rct.Dy())
		w = int(float64(rct.Dx()) * m)
		h = int(arg)
	}

	dst := image.NewRGBA(image.Rect(0, 0, w, h))
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, rct, draw.Over, nil)

	jpeg.Encode(fso, dst, &jpeg.Options{Quality: 100})
}

わかりやすいように横長の画像を使用

150 × 84 を使用して下記のように長辺(今回は横)を 100 で指定する

$ go run main.go test.jpg out.jpg 100

すると下記のような 100 × 56 が出来上がる

元画像 切り取り後
f:id:shiro-16:20200605114910j:plain f:id:shiro-16:20200605115217j:plain

終わりに

前回の記事の内容と組み合わせると golang だけで様々な画像加工ができるのではないでしょうか?
計算とかめんどくさいという人は https://github.com/nfnt/resize を使うのも良いかもしれません。

Golang で ImageMagick を使わずに画像をいじる

はじめに

最近動的に画像を変換するみたいなことをやっていて ImageMagick を使えば簡単にできるんですが
Golang の場合 ImageMagick を使わなくても様々な画像の加工が可能なので ImageMagick を使わずにやった時のメモ

画像形式の変換

最初は画像形式の変換の説明

jpg を png にしたり webp にしたりなどです。

ちなみに Golang では webp の Encode は標準でサポートされていないので今回は https://github.com/chai2010/webp を使用します。

package main

import (
	"flag"
	"fmt"
	"github.com/chai2010/webp"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"os"
	"strings"
)

func main() {
	flag.Parse()
	args := flag.Args()

	f, err := os.Open(args[0]) // 元画像読み込み
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f) // 元画像デコード
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create(args[1]) // 変換後画像作成
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	slice := strings.Split(args[1], ".")

	switch slice[len(slice)-1] { // 出力画像の拡張子によってエンコードを変える
	case "jpeg", "jpg":
		jpeg.Encode(fso, img, &jpeg.Options{})
	case "png":
		png.Encode(fso, img)
	case "gif":
		gif.Encode(fso, img, nil)
	case "webp":
		webp.Encode(fso, img, &webp.Options{Lossless: true})
	default:
	}
}

こんなコードを用意して下記のように実行すると変換されます。

$ go run main.go test.jpg hoge.png
$ go run main.go test.jpg hoge.webp
元画像 変換後(gif)
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529113617g:plain

画像の切り抜き

続いて画像の一部を切り抜く方法を説明

package main

import (
	"fmt"
	"image"
	"image/jpeg"
	"os"
)

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func main() {
	f, err := os.Open("test.jpg")
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create("out.jpg")
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	cimg := img.(SubImager).SubImage(image.Rect(50, 0, 150, 100))

	jpeg.Encode(fso, cimg, &jpeg.Options{Quality: 100}) // Quality を指定しないと荒すぎる画像が出来上がるよ
}

image.Rect で横は 50px ~ 150px 縦は 0px ~ 100px まで切り取るという指定をしています。
こうして出来上がったのが下記の画像

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529115148j:plain

画像の合成

最後に画像の合成

2 つの画像を重ね合わせるなどして合成する方法を説明します。

package main

import (
	"fmt"
	"golang.org/x/image/draw"
	"image"
	"image/color"
	"image/jpeg"
	"os"
)

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func main() {
	f, err := os.Open("test.jpg")
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create("out.jpg")
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	m := image.NewRGBA(image.Rect(0, 0, 200, 200)) // 200x200 の画像に test.jpg をのせる
	c := color.RGBA{0, 0, 255, 255} // RGBA で色を指定(B が 255 なので青)

	draw.Draw(m, m.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) // 青い画像を描画

	rct := image.Rectangle{image.Point{25, 25}, m.Bounds().Size()} // test.jpg をのせる位置を指定する(中央に配置する為に横:25 縦:25 の位置を指定)

	draw.Draw(m, rct, img, image.Point{0, 0}, draw.Src) // 合成する画像を描画

	jpeg.Encode(fso, m, &jpeg.Options{Quality: 100})
}

こうして出来上がったのが下記の画像

元画像 合成後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529122847j:plain

上記では青い画像を作成して合成を行なったがもちろん既存の画像を使うことも可能で下記のように書く

// 上記の defer fso.Close() まで略
        f2, _ := os.Open("hoge.jpg") // 元になる画像
        img2, _, _ := image.Decode(f2)

        rgba := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{200, 200}}) // RGB形式の画像を用意する

        draw.Draw(rgba, image.Rectangle{image.Point{0, 0}, img2.Bounds().Size()}, img2, image.Point{0, 0}, draw.Src) // 元になる画像を描画する

        rct := image.Rectangle{image.Point{25, 25}, img2.Bounds().Size()} // 元画像への描画位置を決める

        draw.Draw(rgba, rct, img, image.Point{0, 0}, draw.Src) // 乗せる画像を描画

        jpeg.Encode(fso, rgba, &jpeg.Options{Quality: 100})
}

これで出来上がるのが下記の画像

元画像 合成する画像 合成後
f:id:shiro-16:20200529125808j:plain f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529125837j:plain

終わりに

ちょっと長くなってしまったので今日はここまで
続きは次週とかに書くかもしれない

AWS CloudFront で Cache を Purge する(PHP/Ruby編)

はじめに

最近 CloudFront を使い始めたんですよね。以前は別の CDN を使っていたのですが

たまに Cache Purge したいよねってなることはあるわけでその処理を PHPRuby で書いたのでメモ
数年ぶりに PHP を書いたのですが...

ついでに AWS console からの方法も書いておく

Cache Purge するアカウントに cloudfront:CreateInvalidation の権限が必要なので注意

前提

既に CloudFront の Distribution は作成済み

PHP

まずは aws-sdk を追加

$ composer require aws/aws-sdk-php

後は PHP から使ってあげるだけ

use \Aws\CloudFront\CloudFrontClient as AwsCFClient;

$client = AwsCFClient([
  'region'  => /* region を入れるのだが CloudFront  では region の概念がないのでどこでも良いっぽい*/,
  'version' => '2016-01-28',
  'credentials' => [
    'key'    => /* AWS_ACCESS_KEY_ID を入れる*/,
    'secret' => /* AWS_SECRET_ACCESS_KEY を入れる*/
  ]
]);

$paths = ["/hoge", "/test"]; // purge したい cache の path
$result = $client->createInvalidation([
  'DistributionId' => /* ここに作成済みの Distribution id を入れる*/,
  'InvalidationBatch' => [
    'CallerReference' => time(),
    'Paths' => [
      'Quantity' => count($paths),
      'Items' => $paths
    ]
  ]
]);

Ruby

aws-sdk の gem の追加から

$ vi Gemfile
+ gem 'aws-sdk'
$ bundle install
# or
$ gem install aws-sdk

後は Ruby から使ってあげる

client = Aws::CloudFront::Client.new(
    region: REGION,
    access_key_id: AWS_ACCESS_KEY_ID,
    secret_access_key: AWS_SECRET_ACCESS_KEY
)

res = client.create_invalidation({
  distribution_id: distribution_id,
  invalidation_batch: {
    paths: {
      quantity: paths.count,
      items: paths
    },
  caller_reference: Time.now.to_i.to_s
  }
})

res&.invalidation&.status == 'InProgress'

console で確認

指定した Distribution の詳細を開き Invalidations の tab を開くと In Progress と表示されているはず。

f:id:shiro-16:20200522111914p:plain

完了すると Completed になる。

もちろんこの画面からも cache purge 出来る。

画像の Create Invalidation をクリックすると paths を入れるフォームが表示されるので入力して Invalidate を押すだけ。

最後に

caller_reference はユニークである必要があるので並列で呼び出すとかの場合はマイクロ秒単位にするとかもしくは別の方法で生成するなどした方が良いかと。

cache purge はお金がかかるからお気をつけを(無料枠もあります)

docs.aws.amazon.com

リモートワークが長くなったのでマンションを買った

はじめに

完全に出落ち感あるタイトル...タイトルは完全に釣りでリモートワークが長くなったからではなくタイミング的にそんな感じになっただけです。

なぜ購入したか?

以前住んでいたマンションの契約更新時期が今年(2020 年) 9 月末だったのだけれど、もう少し広いところに住んで飼っている犬を家の中で走り回らせたいなぁと思い出したのが昨年の夏くらい。

そこから 2LDK で職場にある程度近い賃貸を探してみたのだけれど、 20 万以上/月という感じで出せなくはないが高いなぁ...という感じだった。

そこで買ったらどうなるん?と思いつき調べたらローンだけなら 15 万/月もかからないじゃんということがわかり買うかとなった。

購入時期的には 2020 年はオリンピックがある(はずだった)ので開催中に探すのは色々大変だろうなというのがあったので開催前に買うのが良いかという感じでこのタイミングになったのだった。(コロナの影響で結局オリンピックは延期になったわけだが...)

購入までの流れ

物件調査

昨年の夏 ~ 今年の 1, 2 月まで主にインターネットで自分の条件に合う物件を探していた。
cowcamo/SUUMO/HOME'S あたりをよくみていた気がする。

内見

今年に入って実際に見てみるか〜となって 2 月末 ~ 3 月中旬は十数件の物件を内見してみた。

内見するとこの平米数ならこのくらいの広さなのか〜ふむふむとなり平米数で言うなら最低このくらい欲しいなというのも決まり良かった。

物件決定

そんなこんなで 3 月中旬に内見した今の家がほぼ全ての条件をクリアしていたので購入することを決定した。
この段階でローンの事前審査をした気がする。

諸々契約

それ以降 は大量の書類に個人情報とハンコを押しまくっていた気がする。

ローンの事前審査が通ったら、そのマンション買いますよ的な申し込みをしつつローンの本審査を行った気がする。
ローンの審査は 1 週間ほどで終わるはずで本審査が無事通ったので売買契約の締結やローンの契約/支払いなどを 3 月末 ~ 4 月中旬に終わらせて購入完了となった。

その後

その後は前の家の退去申請などをやったり引っ越しの手配やら荷造り、新居で買うものを決めたりなどこれは賃貸だろうと購入しようとあまり差は無さそう。

そんなこんなで GW 前に無事に引っ越しができ GW 中は新居用に買った様々なものが届いたのでそれの配置など行って過ごしていた。

最後に

当初の目的通り、引っ越し後は犬が家の中を走り回っていて可愛い。

これは床暖が好きになっている様子
www.instagram.com


また第二の目的だった書斎も出来た。
f:id:shiro-16:20200508114126j:plain

ということで最後にアレです。
www.amazon.co.jp

kubeval を CI に組み込んで kubernetes の yaml をチェックする

はじめに

kubeval is これ
github.com

1 年くらい前に kubernetesyaml の形式が正しいのか?を CI とかでチェックしたいな〜という気持ちになり色々なツールを調べはしたんですが、
その時はいまいちしっくりこなかった(記憶が曖昧)で導入を見送ったのですが下記の資料を見てイケるやんと思ったので導入した。

speakerdeck.com

資料にもある通り kubectl apply --dry-run とかで似たようなことができるが、CI から cluster に接続とかするのがめんどくさかったというのもあり --dry-run も採用しておらず kustomize build が正常に終了するということだけを test していました。

しかもこの資料見る前日にまさに数字と文字列の違いで apply に失敗ということが実際に起きてたので入れとこってなった。

CI に組み込む

今回は kustomize build した結果を kubeval に送って validation をしてもらう

まずは Dockerfile

kustomize と kubeval が使える Dockerfile を用意する

FROM golang:1.12-alpine
RUN apk add git gcc g++ libc-dev && \
    wget  https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.3/kustomize_kustomize.v3.2.3_linux_amd64 && \
    chmod +x ./kustomize_kustomize.v3.2.3_linux_amd64 && \
    mv kustomize_kustomize.v3.2.3_linux_amd64 /usr/local/bin/kustomize && \
    wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz && \
    tar xf kubeval-linux-amd64.tar.gz && \
    cp kubeval /usr/local/bin

version とかはまぁいい感じに

drone.yaml

社では drone を使っているので drone.yaml を書いていく

---
clone:
  depth: 1
build:
  image: xxxxxxxxxx # 上記の Dockerfile を build した image を指定
  auth_config: &auth_config
    username: xxxx
    password: xxxxxxx
    email: xxxxxx
  commands:
    - for env in `ls "overlays"` ; do # env 毎に kustomize build を行ってチェックしたい
        kustomize build "overlays/${env}" | kubeval;
      done

あとは branch を push した際に CI が走るように設定を行えば終わり.

最後

skip-kinds のオプションを使えば特定のリソースを除外することもできたりします。
そこらへんは docs に書いてあるので参考にすると良いかと