CentOS on WSL

参考

メモ

Redhat系LinuxをWSL(ないしWSL2)で動かしたい、という話。

wsl2上で無料でCentOS8を動かそう で挙げられているレポジトリはアーカイブされているため、 代替手段で対応した。

CentOS

CentOS7

CentOS-WSL を参考に、まずは手堅くCentOS7。

wsl2上で無料でCentOS8を動かそう を参考にすすめる。

CentOS7.zip をダウンロードし解凍。 中に含まれている rootfs.tar.gz を解凍しておきます。 ここでは、 C:\Users\dobachi\Downloads\CentOS7\rootfs.tar\rootfs.tar として解凍されたものとします。

インポート先のディレクトリがなければ、予め作成。 ここでは以下にしました。(他のイメージと同じ感じで)

1
C:\Users\dobachi\AppData\Local\Packages\CentOS7
1
wsl --import CentOS7 "C:\Users\dobachi\AppData\Local\Packages\CentOS7" "C:\Users\dobachi\Downloads\CentOS7\rootfs.tar\rootfs.tar"

必要に応じてWSL2用に変換。

1
wsl --set-version CentOS7 2

その他、必要に応じてWindows Terminalの設定を行う。

また日本語とGUI利用のための設定は、UbuntuとCentOSは少し違うので注意。 日本語でのハマりポイントは以下。

  • localectlが使えない
    • /etc/profileに直接ロケール情報を記載すると良い
  • Ubuntuとは日本語対応方法が異なる
    • CentOSではibus-kkcを利用すると良い

CentOS8 Stream

<あとで書く>

Fedora

<あとで書く>

共有

readPredicates in inserting records

参考

メモ

前提

Delta Lake 0.7.0

動機

Delta Lakeのトランザクション管理について、 org.apache.spark.sql.delta.OptimisticTransactionImpl#readPredicates がどのように利用されるか、を確認した。

特にInsert時に、競合確認するかどうかを決めるにあたって重要な変数である。

org/apache/spark/sql/delta/OptimisticTransaction.scala:600

1
2
3
4
5
6
7
val predicatesMatchingAddedFiles = ExpressionSet(readPredicates).iterator.flatMap { p =>
val conflictingFile = DeltaLog.filterFileList(
metadata.partitionSchema,
addedFilesToCheckForConflicts.toDF(), p :: Nil).as[AddFile].take(1)

conflictingFile.headOption.map(f => getPrettyPartitionMessage(f.partitionValues))
}.take(1).toArray

準備

SparkをDelta Lakeと一緒に起動する。 (ここではついでにデバッガをアタッチするコンフィグを付与している。不要なら、--driver-java-optionsを除外する)

1
dobachi@home:~/tmp$ /opt/spark/default/bin/spark-shell --packages io.delta:delta-core_2.12:0.7.0 --conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" --conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog" --driver-java-options "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005"

ファイルはSparkに含まれているサンプルファイルを使うことにする。

1
2
3
4
5
scala> val filePath = "/home/dobachi/Sources/spark/examples/src/main/resources/users.parquet"
filePath: String = /home/dobachi/Sources/spark/examples/src/main/resources/users.parquet

scala> val df = spark.read.parquet(filePath)
df: org.apache.spark.sql.DataFrame = [name: string, favorite_color: string ... 1 more field]

こんな感じのデータ

1
2
3
4
5
6
7
scala> df.show
+------+--------------+----------------+
| name|favorite_color|favorite_numbers|
+------+--------------+----------------+
|Alyssa| null| [3, 9, 15, 20]|
| Ben| red| []|
+------+--------------+----------------+

テーブル作成

1
df.write.format("delta").save("users1")

ここまでで後でデータを追記するためのテーブル作成が完了。

簡単な動作確認

ここで、競合状況を再現するため、もうひとつターミナルを開き、Sparkシェルを起動した。

1
/opt/spark/default/bin/spark-shell --packages io.delta:delta-core_2.12:0.7.0 --conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" --conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"

ターミナル1でデバッガを有効化しながら、以下を実行。

1
2
val tblPath = "users1"
df.write.format("delta").mode("append").save(tblPath)

ちなみにブレイクポイントは以下の箇所に設定した。

org/apache/spark/sql/delta/OptimisticTransaction.scala:482

1
2
3
deltaLog.store.write(
deltaFile(deltaLog.logPath, attemptVersion),
actions.map(_.json).toIterator)

ターミナル2で以下を実行し、コンパクション実施。

1
2
3
4
5
6
7
8
9
10
val tblPath = "users1"
spark.read
.format("delta")
.load(tblPath)
.repartition(2)
.write
.option("dataChange", "false")
.format("delta")
.mode("overwrite")
.save(tblPath)

ターミナル1の処理で使用しているデバッガで、以下のあたりにブレイクポイントをおいて確認した。

org/apache/spark/sql/delta/OptimisticTransaction.scala:595

1
2
3
4
5
val addedFilesToCheckForConflicts = commitIsolationLevel match {
case Serializable => changedDataAddedFiles ++ blindAppendAddedFiles
case WriteSerializable => changedDataAddedFiles // don't conflict with blind appends
case SnapshotIsolation => Seq.empty
}

この場合、org.apache.spark.sql.delta.OptimisticTransactionImpl#readPredicates はこのタイミングでは空のArrayBufferだった。

readPredicatesに書き込みが生じるケース

以下の通り。

org/apache/spark/sql/delta/OptimisticTransaction.scala:239

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def filterFiles(filters: Seq[Expression]): Seq[AddFile] = {
val scan = snapshot.filesForScan(Nil, filters)
val partitionFilters = filters.filter { f =>
DeltaTableUtils.isPredicatePartitionColumnsOnly(f, metadata.partitionColumns, spark)
}
readPredicates += partitionFilters.reduceLeftOption(And).getOrElse(Literal(true))
readFiles ++= scan.files
scan.files
}

/** Mark the entire table as tainted by this transaction. */
def readWholeTable(): Unit = {
readPredicates += Literal(true)
}

org.apache.spark.sql.delta.OptimisticTransactionImpl#filterFiles メソッドは主にファイルを消すときに用いられる。 DELETEするとき、Updateするとき、Overwriteするとき(条件付き含む)など。

org.apache.spark.sql.delta.OptimisticTransactionImpl#readWholeTable メソッドは ストリーム処理で書き込む際に用いられる。 書き込もうとしているテーブルを読んでいる場合はreadPredicatesに真値を追加する。

共有

Double count of dstat

参考

メモ

dstatのtotalが2倍?になる?という話があり簡単に確認。

結論

ディスク単位のI/Oを total に入れるべきなのだが、 正規表現上の仕様により、 xvda1 などのパーティション単位のI/Oも total に足されている。

実は、パーティションの値を取り除く正規表現ではあるのだが、当時の実装(※)としては xvd で始まるディスク名に対応できていなかったようだ。 なお、その後、いくつか正規表現が改変されたような経緯がコミットログやGitHub Issueから見られた。

※CentOS7等で採用されたdstat0.7.2のリリース時期は2010年ころ

事象の確認

環境情報の確認。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[centos@ip-10-0-0-217 ~]$ uname -a
Linux ip-10-0-0-217.ap-northeast-1.compute.internal 3.10.0-1127.13.1.el7.x86_64 #1 SMP Tue Jun 23 15:46:38 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

[centos@ip-10-0-0-217 ~]$ dstat --version
Dstat 0.7.2
Written by Dag Wieers <dag@wieers.com>
Homepage at http://dag.wieers.com/home-made/dstat/

Platform posix/linux2
Kernel 3.10.0-1127.13.1.el7.x86_64
Python 2.7.5 (default, Apr 2 2020, 13:16:51)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

Terminal type: xterm-256color (color support)
Terminal size: 86 lines, 310 columns

Processors: 4
Pagesize: 4096
Clock ticks per secs: 100

internal:
aio, cpu, cpu24, disk, disk24, disk24old, epoch, fs, int, int24, io, ipc, load, lock, mem, net, page, page24, proc, raw, socket, swap, swapold, sys, tcp, time, udp, unix, vm
/usr/share/dstat:
battery, battery-remain, cpufreq, dbus, disk-tps, disk-util, dstat, dstat-cpu, dstat-ctxt, dstat-mem, fan, freespace, gpfs, gpfs-ops, helloworld, innodb-buffer, innodb-io, innodb-ops, lustre, memcache-hits, mysql-io, mysql-keys, mysql5-cmds, mysql5-conn, mysql5-io, mysql5-keys, net-packets, nfs3,
nfs3-ops, nfsd3, nfsd3-ops, ntp, postfix, power, proc-count, qmail, rpc, rpcd, sendmail, snooze, squid, test, thermal, top-bio, top-bio-adv, top-childwait, top-cpu, top-cpu-adv, top-cputime, top-cputime-avg, top-int, top-io, top-io-adv, top-latency, top-latency-avg, top-mem, top-oom, utmp,
vm-memctl, vmk-hba, vmk-int, vmk-nic, vz-cpu, vz-io, vz-ubc, wifi

[centos@ip-10-0-0-217 ~]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 60G 0 disk
└─xvda1 202:1 0 60G 0 part /

dstat 0.7.2は2010年にリリースされたようだ。

1
2
$ git log -1 --format=%ai 0.7.2
2010-06-14 22:25:44 +0000

参考までにpcpのバージョン

1
2
[centos@ip-10-0-0-217 ~]$ pcp --version
pcp version 4.3.2

ここから動作確認。

fioを実行。

1
2
3
[centos@ip-10-0-0-217 ~]$ fio -rw=randwrite -bs=16k -size=1000m -iodepth=32 -directory=/tmp -direct=1 -invalidate=1 -runtime=300 -numjobs=8 -name=iotest -ioengine=libaio -group_reporting

(snip)

裏でdstatを実行

1
2
3
4
5
6
7
[centos@ip-10-0-0-217 ~]$ dstat -td -D total,xvda                                                                                                                                                                                                                                                                     ----system---- -dsk/total----dsk/xvda-
time | read writ: read writ
09-04 08:40:50| 373k 315k: 187k 158k
09-04 08:40:51| 0 95M: 0 47M
09-04 08:40:52| 0 87M: 0 44M

(snip)

およそ2倍になっている。

pcp-dstatだと?

1
2
3
4
5
6
7
8
9
10
[centos@ip-10-0-0-217 ~]$ pcp dstat -td -D total,xvda
----system---- --dsk/xvda---dsk/total-
time | read writ: read writ
09-04 09:16:47| :
09-04 09:16:48| 0 41M: 0 41M
09-04 09:16:49| 0 45M: 0 45M
09-04 09:16:50| 0 50M: 0 50M
09-04 09:16:51| 0 35M: 0 35M^

(snip)

ということで、2倍になっていない。

ちなみに、そもそもpcp-dstatは、上記のdstatとは全く異なる実装に見える。

doolだと?(2021/4/8時点のmasterブランチ)

1
2
3
4
5
6
7
8
9
[centos@ip-10-0-0-217 Sources]$ ./dool/dool -td -D total,xvda
-----system---- -dsk/total----dsk/xvda-
time | read writ: read writ
Apr-09 11:24:54| 577k 3307k: 289k 1653k
Apr-09 11:24:55| 0 66M: 0 33M
Apr-09 11:24:56| 0 25M: 0 13M
Apr-09 11:24:57| 0 44M: 0 22M^

(snip)

ということで、dstatと同様。

関連する実装は?

dstat-real dstat の実装を確認する。

dstat_disk クラスのコンストラクタに、後に用いられるフィルタ用の正規表現定義がある。

1
683         self.diskfilter = re.compile('^(dm-\d+|md\d+|[hsv]d[a-z]+\d+)$')

この正規表現を利用する箇所はいくつかあるが、その一つが total の値を計算するところ。 このフィルタにマッチしない場合に、 total に足される計算になっている。

1
2
728             if not self.diskfilter.match(name):
729 self.set2['total'] = ( self.set2['total'][0] + long(l[5]), self.set2['total'][1] + long(l[9]) )

ここで正規表現の中身を見ると、例えば dm-0sda1 のような「パーティション名」を表すものとマッチするようになっている。 つまり、 not 条件と合わせて、「パーティション名以外のものを total に足す」という動作になる。

このとき、よく見ると、今回使用した環境で用いている、 xvda1 という名称は正規表現にマッチしないことがわかる。 したがって total に足されてしまい、なぜか total の表示が大きくなる、という事象が生じたと思われる。

total disk data include virtual lvm device data (so doubling the calculated values) にも似たような議論があるようだ。

doolの登場

dstatの源流は今はメンテされておらず、 dool が開発されている。

Dool is a Python3 compatible clone of Dstat.

また dstatが開発終了した話 に記載されているが、他にもpcp系列のdstatが残っている。

ただし、doolにおいても、正規表現は同じようなものだった。

共有

When DeltaLog ID is created

参考

メモ

Delta LakeのDelta LogのIDがいつ確定するのか、というのが気になり確認した。

前提

  • Delta Lakeバージョン:0.8.0

createRelationから確認する

org.apache.spark.sql.delta.sources.DeltaDataSource#createRelation をエントリポイントとする。

ポイントは、DeltaLog がインスタンス化されるときである。

まず最初にインスタンス化されるのは以下。

org/apache/spark/sql/delta/sources/DeltaDataSource.scala:141

1
val deltaLog = DeltaLog.forTable(sqlContext.sparkSession, path)

org.apache.spark.sql.delta.DeltaLog は、 org.apache.spark.sql.delta.SnapshotManagement トレイトをミックスインしている。 当該トレイトには、 currentSnapshot というメンバ変数があり、これは org.apache.spark.sql.delta.SnapshotManagement#getSnapshotAtInit メソッドを利用し得られる。

org/apache/spark/sql/delta/SnapshotManagement.scala:47

1
@volatile protected var currentSnapshot: Snapshot = getSnapshotAtInit

このメソッドは以下のように定義されている。

org/apache/spark/sql/delta/SnapshotManagement.scala:184

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected def getSnapshotAtInit: Snapshot = {
try {
val segment = getLogSegmentFrom(lastCheckpoint)
val startCheckpoint = segment.checkpointVersion
.map(v => s" starting from checkpoint $v.").getOrElse(".")
logInfo(s"Loading version ${segment.version}$startCheckpoint")
val snapshot = createSnapshot(
segment,
minFileRetentionTimestamp,
segment.lastCommitTimestamp)

lastUpdateTimestamp = clock.getTimeMillis()
logInfo(s"Returning initial snapshot $snapshot")
snapshot
} catch {
case e: FileNotFoundException =>
logInfo(s"Creating initial snapshot without metadata, because the directory is empty")
// The log directory may not exist
new InitialSnapshot(logPath, this)
}
}

ポイントは、スナップショットを作る際に用いられるセグメントである。 セグメントにバージョン情報が持たれている。

ここでは3行目の

1
val segment = getLogSegmentFrom(lastCheckpoint)

にて org.apache.spark.sql.delta.SnapshotManagement#getLogSegmentFrom メソッドを用いて、 前回チェックポイントからセグメントの情報が生成される。

なお、参考までにLogSegmentクラスの定義は以下の通り。

org/apache/spark/sql/delta/SnapshotManagement.scala:392

1
2
3
4
5
6
7
8
9
10
11
case class LogSegment(
logPath: Path,
version: Long,
deltas: Seq[FileStatus],
checkpoint: Seq[FileStatus],
checkpointVersion: Option[Long],
lastCommitTimestamp: Long) {

override def hashCode(): Int = logPath.hashCode() * 31 + (lastCommitTimestamp % 10000).toInt

(snip)

上記の通り、コンストラクタ引数にバージョン情報が含まれていることがわかる。

インスタンス化の例は以下の通り。

org/apache/spark/sql/delta/SnapshotManagement.scala:140

1
2
3
4
5
6
7
LogSegment(
logPath,
newVersion,
deltasAfterCheckpoint,
newCheckpointFiles,
newCheckpoint.map(_.version),
lastCommitTimestamp)
共有

Power Grid Data

参考

以下の資料参照。

メモ

電力データ活用に関連する、割と新しい情報をまとめた。

共有

Mesh wifi for Home Network

参考

メッシュWiFiルータ

JPNE v6plus

So-net

au光

動作確認

メモ

メッシュWiFi

メッシュWiFiとは、Elecomの Vol.83 メッシュ(Mesh)Wi-Fiって何?メリットと活用方法を紹介 記事の通り。 戸建ての家などでは、広く安定してカバーできるはず、ということで。

日本国内においてどの機器を使うか?については、 メッシュルーターの選び方について整理した がわかりやすい。

個人的にはIPv6対応している点で、「Synology MR2200ac」が候補に上がった。 仕様は MR2200ac を参照されたし。

JPNE v6プラス

v6プラス(IPv6/IPv4インターネットサービス) によると、

「v6プラス」は、NTT東西の次世代ネットワーク(NGN)を利用しインターネット接続を提供するISP事業者が、 IPv6及びIPv4の設備を持たずに、インターネット接続をお客さま(エンドユーザ)にご提供いただくためのサービスです。

とのこと。

Synology製Wi-FiルーターMR2200acでv6プラス接続設定 によると、MR2200acでv6plusを利用する方法が記載されている。

auひかりのHGW(ホームゲートウェイ)

auひかりのホームゲートウェイはいらない? 知っておきたい不都合な真実 にも記載の通り、

先ほどから触れてるとおり、auひかりはホームゲートウェイが無ければインターネットに接続することができません。 その理由は、ホームゲートウェイ内に、KDDIの認証を取る機能があるからです。

とのこと。

そこで、自前のルータを利用したい場合は、 DMZ機能を利用して、ルータ機能をHGWと自前ルータの療法で動かす設定をすることもある。

au光で市販のルーターを使うが参考になる。 ブリッジモードにせずDMZの下でルーターモードで利用するには も参考になる。こちらはtp-linkのブログ。

so-net

So-net 光 with フレッツ S 東日本 は、フレッツ光の契約と思われる。

So-net 光 プラスの次世代通信 v6プラス は v6プラス を利用したサービスと思われる。 So-net 光 プラスの次世代通信 v6プラスの対象サービス によると、 So-net 光 プラス などが対応している。 その他フレッツ系のサービスが対応しているようだ。

IPv6の動作確認

ipv6 test にアクセスると良い。

共有

Memo of How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh

参考

メモ

Zhamak DehghaniによるHow to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh を読んで簡単にまとめた。

この文章では、ドメインが所有し、提供するデータプロダクトの相互利用に基づく「データメッシュ」の考え方を提唱するものである。 固有技術そのものではなく、アーキテクチャ検討の前の「データを基礎としたサービス開発、ソフトウェア開発のためのデータの取り回し方」に関する示唆を与えようとしたもの、という理解。

共有

Memo of Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics

参考

メモ

Lakehouse A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics の論文を確認し、 簡単なメモを作成した。

考え方を節異名しつつ、Delta Lake、Delta Engineを使用した際のTPC−DSの実行結果、クエリ実行コストの情報が記載されていた。 また関連研究、参考文献も過去から現在のトレンドの背景を知る上で参考になるものが多い。

共有

Getting started of Ansible Vault

参考

公式サイト

ブログ

メモ

Ansible内で使用する変数を平文でファイルに記載し、プレイブック集に入れ込むのに不安を感じるときがある。 そのようなとき、Ansible Vaultを利用すると暗号化された状態で変数を管理できる。 Ansible Vaultで管理された変数をプレイブック内で通常の変数と同様に扱えるため見通しが良くなる。

公式サイトの Encrypting content with Ansible Vault が網羅的でわかりやすいのだが、 具体例が足りない気がしたので以下に一例を示しておく。 ここに挙げた以外の使い方は、公式サイトを参照されたし。

Ansible-Vaultを用いた機密情報の暗号化のTips も参考になった。

暗号化されたファイルの作成

secret.yml 内に変数を記述し、暗号化する。 後ほど暗号化されたファイルを変数定義ファイルとして読み込む。

ここでは以下のような内容とする。

secret.yml

1
2
hoge:
fuga: foo

ファイルを暗号化する。

1
$ ansible-vault create secret.yml

上記ファイルを作成する際、Vault用パスワードを聞かれるので適切なパスワードを入力すること。 あとでパスワードは利用する。

(参考)復号して平文化

1
$ ansible-vault decrypt secret.yml

(参考)暗号化されたファイルの編集

1
$ ansible-vault edit secret.yml

(参考)Vaultパスワードをファイルとして渡す

プロンプトで入力する代わりに、どこかプレイブック集の外などにVaultパスワードを保存し利用することもできる。 ここでは、 ~/.vault_password にパスワードを記載したファイルを準備したものとする。 編集する例を示す。

1
$ ansible-vault edit secret.yml --vault-password-file ~/.vault_password

プレイブック内で変数として利用

変数定義ファイルとして渡し、プレイブック内で変数として利用する。 以下のようなプレイブックを作る。

test.yml

1
2
3
4
5
6
7
- hosts: localhost
vars_files:
- secret.yml
tasks:
- name: debug
debug:
msg: "{{ hoge.fuga }}"

以下、簡単な説明。

  • 変数定義ファイルとして secret.yml を指定( vars_files の箇所)
  • 今回はdebugモジュールを利用し、変数内の値を表示することとする。
  • 暗号化された変数定義ファイル secret.yml に記載されたとおり、構造化された変数 hoge.fuga の値を利用する。
  • 結果として、foo という内容がSTDOUTに表示されるはず。

プレイブックを実行する。

1
$ ansible-playbook test.yml --ask-vault-pass
共有

Use static image on Hexo blog

参考

メモ

Hexo 記事に画像を貼り付ける を参考に、もともとCacooのリンクを使っていた箇所を すべてスタティックな画像を利用するようにした。

post_asset_folderを利用して、記事ごとの画像ディレクトリを利用することも考えたが、 画像はひとところに集まっていてほしいので、 Global-Asset-Folder を利用することにした。

なお、上記ブログでは

1
プロジェクトトップ/images/site

以下に画像を置き、

1
![猫](/images/site/cat.png)

のようにリンクを指定していた。

自身の環境では、rootを指定しているので

1
プロジェクトトップ/image

以下にディレクトリを置き、

1
![猫](memo-blog/images/cat.png)

と指定することにした。

共有