蚊帳の中の日記

ゆるく生きてます

redisってデータを永続化する機能を持ってるって知らなかった...。

つい先日、仕事でredisについて触れる機会があったのだけど、redisのことについて「なんかkey-valueというタイプのNoSQLデータベース」くらいの認識で正直詳しいことをあんまり知らなかったので、公式ドキュメントを読んだりした。 NoSQLのイメージとして、「メモリとかにデータを保持して読み込み書き込みが早いけど、メモリに何かあったらデータ全部なくなっちゃう揮発性がある」というのがあったので、サーバーが不都合で落ちたらデータなくなると思ってたのだけど、実はデータを永続化する仕組みがあるというのを知って、本当勉強不足だな〜と思ったのでまとめてみる。


redisについて、おさらいした

redisのdata typeはいくつかあるけど、基本的なものは

  • string
    • keyとそれに関連するvalueで、一つのデータ
  • lists
    • その名の通りで、配列と同じような感じ。インデックス値がわかっていればアクセスは高速。
  • sets
    • 順序を持たないstringの集合体
  • sorted sets
    • 順序並び替えされたstringの集合体
  • hashes
    • その名の通りハッシュのようなデータ構造。特定のフィールド値と値がセットになったデータの集合体

というものがある*1。これらデータの有効期限TTLを設定もできる。 コマンドのドキュメントもわかりやすくて、改めて勉強して見るととっかかりやすいミドルウェアだなと感じた。

下の動画でシュッと学べたし、公式ドキュメントにもデータタイプのサマリーが載っている。

www.youtube.com

今回、この動画の後半で redis data persistence についての話が出ていて勉強になった

redisのデータ永続化

redis.io

タイプとしては

  • RDB persistence
  • AOF(Append Only File) persistence

の二種類があり、どちらか片方を設定 or もしくは両方を設定できる。どちらもメリット・デメリットはあって、RDBタイプredis-cliとかからSAVEとかを叩くとある一定期間内のデータセットのdumpファイルみたいなのを作成してくれる。

$ redis-cli
127.0.0.1:6379> SAVE
OK
127.0.0.1:6379> exit
$ strings dump.rdb
REDIS0009
redis-ver
5.0.4
redis-bits
ctime
used-mem

.......
(省略)
.......

users??
John Doe
Brad
Kate
Mike
John

これを定期的に実行してdumpファイルを作っておけば、ある特定の期間のデータをRestoreできるという仕組みで、dumpファイルは定期的にS3とかにバックアップとして残しておけば何か合っても大丈夫ということ。ただこのdumpのタイミングが5分とか10分とかだと、もし次のdumpファイル作成の間に障害が起きたらその間のデータは損失してしまうということなので、データの損失期間を少なくしたい場合には、もう一つのAOFが適しているっぽい。 AOFもdumpみたいに永続化するファイルを作成するのだけど、そのファイルがredisに対して実行した実行処理にhistoryを保存する。つまりそのhistoryをすべて再実行すれば全く同じデータを再構築することができるという仕組みらしい。dumpと違って実行処理のhistoryログは非常に大きくなりやすいが、先に行ったデータ損失期間がほぼないのがメリット。ただ、fsyncポリシーによってはlogを保存する処理が遅くなることがあるので注意が必要になるみたい(1秒ごとの書き込みでもパフォーマンスは優れていると言ってるが、もっと短くするとどうなるかわからない)。

用法によると思うが、公式ドキュメントにも

The general indication is that you should use both persistence methods if you want a degree of data safety comparable to what PostgreSQL can provide you.

とあり、普通のRDBのデータと差異がないデータを保全するには両方の永続機能を使うのを推奨しているみたい。


redisをちょっと知れた気がする。

話変わるけどredisのドキュメント見ると、Redis Pub/SubとかRedis transactionsとかReplicationとか、いままでの自分のNoSQLの思い込みを払拭するような気になるワードがいっぱいあって、土曜日はこれで潰れた。 他に調べた機能については、またの機会にまとめたいと思う。

あと、正直memcachedとの違いがよくわからないから、そのあたりも勉強しなかきゃな

その他参考にした資料

*1:Bit arraysとHyperLogLogsというのもあるんだけど、あんまり使う機会が少なそうだな〜と思ったので割愛

k8sのIngressがよくわからなかったけど、k8s完全ガイドとかL4/L7ロードバランサの事を学んで、しっくり理解できた気がした

最近お仕事でk8sを触る機会が多いのだけど、k8sは色んな技術や用語がたくさんあって大変。正直、ふんわりとしかわかっていない技術も多い。 その中の一つにIngressがあるのだけど、これがserviceリソースとどう違うのか???イマイチよくわかってなかったので勉強し直した。

まずserviceリソースについてk8s完全ガイド第6章を読み直す

serviceリソースは「podに繋ぐため通信(IPやらDNSやら)や、各PodへのトラフィックをさばくLB的な存在」という認識だった。

公式ドキュメントにも、

In Kubernetes, a Service is an abstraction which defines a logical set of Pods and a policy by which to access them

とあって、それっぽいことが書かれている。 serviceリソースによって、selectorが特定のラベルがついたpodを探し、設定したポートにアクセスが来たら、探したポッドの特定のポートに通信を受け渡すような仕組みになっている。pod同士の通信だけであれば、クラスタを作成したときにk8sがNodeにpod用の内部ネットワークを作成してくれるので、serviceリソースはいらないらしいのだけど、

  • 各podへのロードバランシング
  • サービスのディスカバリ
  • クラスタ内のDNS

といった機能を実現するにはserviceリソースが必要になる。cluster ipという自動で振られる(静的設定もできる)仮想ipができて、そのipを使って各podへ通信するという感じ。

podへのアクセス方法もいくつか種類があり、service typeと呼ばれ、cluster ip(external ip)NodePortLBheadlessExternalNodeなど、それぞれpodへのアクセス方法が異なり、一長一短がある。ここまでKubenetes完全ガイドを読み直して復習した。

で、ここまででserviceリソースについて勉強したけど、(いままで勉強する順番が良くなかったのかかもしれないが)IngressもLB的な役割をするということが書いてあって、あんまり違いがよくわかっていなかった。

serviceリソースとIngressの違いはL4/L7ロードバランサーを知ると良さそう

ググったりすると「http, httpsからの外部通信を制御できたりする!」とかあるけど、いまいちピンとこない。まあ、言葉通りなんだろうけど。k8s完全ガイドをよく読み直してみると、

ここまで説明してきたserviceがL4ロードバランシングを提供するリソースであるのに対し、IngressはL7ロードバランシングを提供するリソースです。

とあった。

ごめん。L4/L7ロードバランサの違いがよくわかってない。。。コイツらを調べればserviceリソースとingressを明確にそれぞれ理解できそうだったので、それらを調べた。で、色々調べて、以下のslideshareがわかりやすくて参考になった。拝借させていただく :pray:

www.slideshare.net

なるほど。

  • podへの通信振り分けをip + tcpipポートで行うL4ロードバランシングが、serviceリソース
  • L4ロードバランシングが振り分けに使う情報に加えて、L7が使う情報(pathやhostなど)も使って通信振り分けをするL7ロードバランシングが、ingress

ということかな。確かにingressリソースはpaths:とかでパスによるルーティング定義ができる。

k8s完全ガイドのまとめにも

Ingress - L7ロードバランシング - HTTPS終端 - パスベースルーティング

とあった。L4/L7ロードバランシングの知識が肝だった*1

参考にさせてもらったURL


余談だけど、先輩が以前「k8sを勉強するときは、既存のウェブシステムとかと見比べて、それぞれがk8sのどのアーキテクチャに類する技術なのかを考えながら勉強するといい」的なアドバイスをくれて、なるほどなーとなった。 今回はこの既存のウェブシステムにあたるL4/L7ロードバランサーの知識がそもそも無くて、いまいち自分の中でしっくり来てないのが原因だったのが大きい気がしてる。自分のように所謂インフラに近い知識が薄い人は、k8sを学ぶと同時に、k8sを使っていないよくあるインフラの技術や知識も学ぶのがいい気がすると、今回の調査をして思った。

*1:なんで数字の4と7なの?に関してはOSI参照モデルについて理解するとわかるはず。4は第4層のネットワーク層。7は第7層のアプリケーション層

気づいたらお値段の張るノイキャンイヤホンが手元に3つもあった...。

1年位前からBose quietcomfort 20という有線ノイズキャンセリングイヤホンを持っていて自宅で愛用していた。会社にも持ち込んだりして集中したいときによくつけてて、もうノイキャンイヤホンないとなかなか集中できない人間になってるくらい愛用している。

でも時代はワイヤレスイヤホンで、iphoneXSに機種変更をしてからもイヤホンジャックがなくなってしまったので「いよいよワイヤレスのノイキャンイヤホン買ったほうが不便しないな〜」と思っていて、迷いに迷っていた。で、1ヶ月ほど前に購入したのがBose quietcomfort 30というやつ。

Sonyのノイキャンイヤホン・ヘッドホンも巷で大人気の中、「やっぱりBoseでしょ!」とか思いたって買ってみたのだけど、しばらく使ってみて、首のフックがずれたり、首に違和感があって正直辛くなってきてしまった。高い買い物したのに「うーん」と思いつつ、結局Sonyのノイキャンイヤホンを買ってしまった。

こっちはBoseの方と比べてカナル型だから耳の穴が痛くなるけど、そこまで長時間イヤホンをつけ続けることはなかったし、何よりストレスが少ない。別に音質にそこまでこだわりはなかったのだけど、つけてみると非常にいい音で最高。ワイヤレスでたまに起きる音切れとかも殆どない。ストレスフリーで、めちゃくちゃ外音を消し去ってくれる。

で...もう3つもノイキャンイヤホンある。商品レビューするyoutuberみたいな状態。quietcomfort 30はメルカリ行きかな。。。

Dockerコンテナの/var/lib/docker/overlay配下の容量が大きくなって起動できない事象に遭遇したので周辺知識を調べた。

業務でとあるサーバー上のDockerコンテナが立ち上がらなくなってしまう事象に出会った。 原因を先にいうと、Dockerコンテナ内のvar/lib/docker/overlay配下の容量が肥大化してコンテナのデータ容量が100%になってしまっていた。 色々やって結局先輩エンジニア方が解決してくれたんだけど「そもそもoverlayって何?」って感じで調べたら、この間勉強したLPICの内容も少し絡んでいて面白かったのでまとめてみた。

原因

先程も書いたが、今回の事象の原因は、var/lib/docker/overlayの容量が大きくなってしまって、データ領域が足りなくなりコンテナが立ち上がらなくなってしまっていた。*1

overlayって何?

で、「overlayってなんぞ?」となったのだけど、やっぱり公式を見るのが一番ということで調べてみたら、DockerのドキュメントにoverlayFS driverというのがあった。

docs.docker.com

overlayFSのTL;DR

Dockerイメージはread onlyなlayerを束ねたものなんだけど、それらlayer(一つ一つがファイルシステム)のデータ情報などはoverlayFSという技術を使って管理されている。*2 overlayFSの実態は/var/lib/docker/overlayもしくは/var/lib/docker/overlay2配下に集約されていて、一度システムを読み込んでしまえば、キャッシュによるシステム起動も早くなるし、iノードの節約にも繋がって容量がエコになるらしい。

概要を自分用にまとめたのが以下。

  • DockerのoverlayFileSystem(以降、overlayFSと略す)はDockerコンテナ上のデータ領域やイメージの情報を管理するファイルシステムのこと。
  • OverlayFSという技術自体はDocker特有の物というよりもシステム関連の技術っぽくて、こちらの記事が簡単なまとめをしてくださっていてわかりやすかった。既存で存在しているaufsというものの類似技術で、複数のシステムのファイルとかディレクトリなどを階層構造上に持ち、それぞれを透過的に重ねてマージして一つのシステムとして管理できるシステムのことを言うみたい。
  • overlayFSはxfsをサポートしている。LPICにも出てくるのだけど、xfsはシステム操作をjournalに保存する仕組みになっているファイルシステム。つまり、システム操作ログなどはjournalctlコマンドで見れる。
    • まあ、正直ふーんという感じだけど、Dockerの公式ドキュメントのイメージ画像がわかりやすかった。
    • aufsよりも早くて実装がシンプルらしい。aufsをよく知らないからふーんという感じだけど、まあすごいみたい。
  • ちなみに現状はoverlayoverlay2の2つのバージョンがあり、自分のコンテナのがどっちを使っているかはdocker infoで見れる。

    f:id:kayamelo151515:20191031225325p:plain
    https://docs.docker.com/storage/storagedriver/overlayfs-driver から拝借

  • 「階層構造上に持つ」とは具体的には、一つのLinuxホストにupper dir, lower dirという2つのディレクトリ構造を持っていて、その2つを統合した(mergedディレクトリという構成になっている。このmergedされたのが一つのシステムとしてマウントされてるらしい。ちなみに、このディレクトリをlayerと呼んでいる。

  • で、この階層構造の実態は/var/lib/docker/overlayで、ここにlayerが保存されている。つまり今回はこのlayerが肥大化したのが原因と言い換えられる。
    • lower dir: 読み込み専用
    • upper dir: 書き込み可能
    • merged: 上2つを統合したレイヤ。効率的なマウントポイント。
  • この機能によって何が嬉しいのか?と言うと、Dockerドキュメントにはよると、docker buildやdocker commitなどのレイヤー関連のDockerコマンドのパフォーマンスが向上し、ファイルシステムで消費するiノードが少なくなる。
    • ちなみにiノードLPICでも習った。*3
    • パフォーマンス向上に関しては、一度mergedされたファイルを読み出してしまえば、システムがキャッシュを使ってくれるので、次からの起動が早くなるということらしい。
    • iノードについては、merged layerにあるシステムのiノードに、残りのlower dir, upper dirにあるシステムがハードリンクがされている。ハードリンクされているということはiノードが同じなので、layerが増えてもmerged layerにあるノードだけで済んでiノードの節約ができるという原理みたい。*4
    • ちなみに、df -iでiノードの利用状況を確認することができる。

dockerで触ってたときに、モヤモヤしてた所が少しわかった

ここまでまとめて見て、いままでdockerを触っていたときにモヤモヤしていた所が少しだけ分かってきた気がした。

確かにdocker pullとかしたとき、

$ docker pull centos
latest: Pulling from centos
f1b103484249: Pull complete
23456d61e65: Pull complete
12322fbe74aa5: Already exists
centos:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:as334436c65123532ecb7bb790b1db0279668d3763c3b81f31bc6c4e60eooa112
Status: Downloaded newer image for centos:latest

みたいに複数のハッシュ値みたいなのがログに出ていたけど、これがそれぞれlayerっぽい。なるほどな〜。 ちなみに、このlayerたちはdocker imageの履歴としてdocker historyで確認できる。

で、結局今回の問題はどうやって対処したか?

docs.docker.com

ドキュメントに不要なオブジェクトの排除方法みたいなのが載っていた。

forums.docker.com

他にも同じような事象になる質問があって、これらにある情報と同じ感じで先輩エンジニアが docker container prune overlayで対処してくれて、事なきを得た。

対処後の疑問

この後に「layerがどんどん増えていったら、また将来的にoverlayが肥大化しちゃうから、うまい具合にoverlayを自動で消してくれないかな」とか思ったけど、ドキュメントをよく見ると、

Docker takes a conservative approach to cleaning up unused objects (often referred to as “garbage collection”), such as images, containers, volumes, and networks: these objects are generally not removed unless you explicitly ask Docker to do so.

「dockerは諸々のオブジェクトを掃除してくれるアプローチを持っているけど、それは明示的にコマンドを叩くなどして要求しないと駄目よ」的なことが書いてあった。なるほど。容量がでかくなったら、docker hogehoge pruneするなどしてくれる自動化するのが今後の再発防止策なのかもしれないな〜とか思ったりした。

あとはこちらのスライドも参考にさせてもらったのだけど、overlaysのディレクトリを別システムに移してそちらで管理するとかもできるらしい。なるほどな〜。色々アプローチはありそう。

ちなみに、調べた感じoverlayFSは制限がないので、運用上の注意が必要とのこと。なるほど。容量制限もできないのか。


難しいdockerについてちょっとだけ詳しくなれた日だった。

*1:この記事とよく似た状況と原因だった。

*2:overlayFS以外にもストレージ管理の技術はあるようで、これはそのうちの一つ。cf: Use the OverlayFS storage driver | Docker Documentation

*3:iノートとはファイル属性を格納するもので、ディスク上にファイル情報(アクセス権や所有権)が記録される。すべてのファイルにはこのiノードがあり、これが枯渇すると新しいファイルを作成するなどができなくなります。例えディスクに空き容量があってもiノードが残っていなければ駄目。小さなファイルをたくさん作ると、iノードが枯渇することがある。

*4:https://www.slideshare.net/HommasSlide/docker-52965982

コミットするときに意識することが増えた

kayanaka.hatenablog.com

ふとこの自分の記事を読み返したときに、最近意識していることが増えたなーと思ったので、付け加えておく

  1. git commit --one-lineの1行は「何故変更したのか?」という理由をつけて、出来るだけ簡潔かつ具体的な修正内容を書く(例:N + 1問題が発生しているため、includeを追加する)
  2. 1.で説明しきれない内容や参考にしたURLなどは、git commitの3行目以降の補足?に記述する。
  3. コミットの粒度は余り大きくなり過ぎない。

これに加えて

テストコードを書く場合は、実装とテストで1コミットにしている

これをやると

  • 実装した内容がどのように動作するのか?をテストコードでも見れる。レビュワーに優しい。と思う
  • コミット単位を小さく意味のある単位で切るように心がけていれば、実装した内容も小さくフォーカスできて、テストの漏れも見つかりやすい

というのが、メリットかな。1コミットにテストコードも入れるので、コミットが大きくなりすぎることは気をつけないと行けないけど。

これも、最初に書いた3つの意識していることについてアドバイスしてくれた同僚の先輩が、教えてくれたこと。

これを意識してから(自分で言うのもあれだが)以前よりもPRのコミット履歴がきれいに保たれるようになった気がする。後で自分でPRを見直しても見やすい。 OSSでもこういうった所への気配りは非常に重要らしくて参考になるプラクティスだった。

続きを読む

TemplateMethodパターンについて少し勉強した

最近、良いサービス設計のためにどういったプラクティスがあるのか?、色々少しずつ勉強している。 今回は TemplateMethodパターンに少し弁甲したのでメモ

Templateと名前がついているのでなんとなく「テンプレートを使い回す用法なのかな」と思っていたが、勉強してみると思った通りの設計パターンだったと思う。

概要

テンプレートとなる抽象基底クラス(呼びづらいのでテンプレクラスと呼ぶことにする)と、それらテンプレートクラスを利用して具体的な処理内容を書く具象クラスに処理を分割して設計をする。 参考にしたURL *1 によると、

  • 処理の中で内容が変わらないものをテンプレクラスに書いて
  • 処理が場合によって変わるものを具象クラスに書く

例えば、ECサイトでMailerを実装するときに、問い合わせ先などがよく書かれているフッターなどはどのメールでも同じテンプレート情報としてまとめられそうなのでテンプレクラスに具体的な内容をまとめて、タイトルや本文は注文完了メールや発送完了メールなどメールの種類によって変わるので変わるものとして、具象クラスに具体的な内容をまとめられる。

自分はここを写経してイメージを掴んだ github.com

継承を使って、具象クラスはテンプレクラスのメソッドを必要に応じてオーバーライドする *2 。オーバーライドして具体的な処理内容を書くという感じ。これで、Templateとなるテンプレクラスを中心に、新しい具象クラスも追加しやすい。具象クラス内で必ずオーバーライドさせるようにしたい場合は、raiseさせたりして工夫する。

似たような処理があって微妙にcase文などを入れないと駄目なメソッドやクラスがあったら、型となる処理フロー(変わらない部分)と微妙に違う処理(変わる部分)に分けて設計するこのTemplateMethodパターンは有効とのこと。


メリットとしては、新しいパターンが増えても、テンプレクラスを継承した具象クラスを増やすだけで済むのでメンテナンス性が良くなることかなと思っている。比較的わかりやすくて、今後の実装に活かせそうな予感がしてる。一つの処理で分岐がたくさんあるけど、処理フローは変わらない、みたいなときに有効な気がしているので、そういったパターンが出たら使ってみようかな。

(今借りているリファクタリングRubyの後半にもTemplateMethodの話が少し載ってたので見てみよう)

*1:Rubyによるデザインパターン5原則 - Qiita

*2:このオーバーライドするべき(しなくても良い)メソッドをフックメソッドというらしい(railsのフックメソッドと混乱するからむしろ覚えないほうがいいかも)。まあ、とりあえずオーバーライドするメソッドたちと覚えておけば良さそう。

週記を書くの辞めた

その日にやったことを記録するために日記だと骨が折れそうなので、週記をしばらく続けてみたのだが、書いていてつまらなかったし、あんまり良いアウトプットになってない気がしてきたので辞めようかと思う。

週記なので1週間のダイジェスト的な感じで、1日の出来事を短文でまとめるように意識してきたのだけど、いままでの週記を見返してみるとなんか内容が薄くてつまらない。かと言って長文にすると週記のボリュームが出て見返すのがだるくなりそうだし、日毎に違うトピックが混ざって見づらい。 内容が薄くなってしまっているので、自分で1週間の振り返りで見返しても「これなんだっけ?」と思い出すのに時間がかかったりする。これなら、雑でもいいから、なにか一つのトピックの単位で記事を書いていったほうが、そのトピックに関する詳しい話も書けて楽しくなりそう。振り返りもしやすそう。少し内容の濃い良いアウトプットにもなりそうな気がしている。

という感じで週記は一旦終わり。