前ふり

  • kubernetes 1.20.0
  • kubeadm v1.21.0

tl;dr;

  • 証明書の期限切れ

流れ

  • 2021/07/28 の昼頃から、kubectl を使って pod に処理を実行させる系のジョブがすべてコケる
  • 自分の PC から kubectl コマンドを打つと error: You must be logged in to the server (Unauthorized) がでる
  • 再度 kubectl コマンドを自分の端末から打つと、The connection to the server 192.168.10.190:6443 was refused - did you specify the right host or port? となった
  • kubelet のログを journalctl -u kubelet で見たところ、 part of the existing bootstrap client certificate is expired: 2021-07-27 06:24:58 +0000 UTC というメッセージが出て異常終了していた。

復旧手順

去年の手順と完全に一緒。

バックアップを取得

1
2
3
sudo su -
mkdir ~/k8s_backup_20210728
cp -rva /etc/kubernetes ~/k8s_backup_20210728/

証明書更新

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
# cd /etc/kubernetes/pki
# mv {apiserver.crt,apiserver-etcd-client.key,apiserver-kubelet-client.crt,front-proxy-ca.crt,front-proxy-client.crt,front-proxy-client.key,front-proxy-ca.key,apiserver-kubelet-client.key,apiserver.key,apiserver-etcd-client.crt} ~/k8s_backup_20210728/
# kubeadm init phase certs all --apiserver-advertise-address 192.168.10.190

W0728 01:03:46.960780 4926 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Using existing ca certificate authority
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubemaster kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.10.190]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Using existing etcd/ca certificate authority
[certs] Using existing etcd/server certificate and key on disk
[certs] Using existing etcd/peer certificate and key on disk
[certs] Using existing etcd/healthcheck-client certificate and key on disk
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Using the existing "sa" key

# cd /etc/kubernetes
# mv {admin.conf,controller-manager.conf,kubelet.conf,scheduler.conf} ~/k8s_backup_20210728/
# kubeadm init phase kubeconfig all
W0728 01:05:49.334781 5092 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file

ここで一度 reboot を行った

1
2
3
4
5
6
7
8
`# ~/.kube/config を置換
$ sudo cp /etc/kubernetes/admin.conf ~/.kube/config

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.3", GitCommit:"ca643a4d1f7bfe34773c74f79527be4afd95bf39", GitTreeState:"archive", BuildDate:"2021-07-16T17:16:46Z", GoVersion:"go1.16.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.0", GitCommit:"cb303e613a121a29364f75cc67d3d580833a7479", GitTreeState:"clean", BuildDate:"2021-04-08T16:25:06Z", GoVersion:"go1.16.1", Compiler:"gc", Platform:"linux/amd64"}

Server Version が表示されていればOK!

蛇足

通常の証明書は kubeadm certs renew all で更新可能だが、control-plane が起動時に使用する証明書は更新できない? そんなバカな話も無いと思うので何か手順を忘れているのかなんなのか…

まえがき

この文章は割と未完です。現在進行形で試行錯誤している状態なのです。

はじめに

多分、余計な話は良いから中身読みたいと思うので、動機とかは後ろにまとめました。

この文章は、セットアップ自体の手順より、ハマったところ、それを通じて分かったことを中心に記述します。

前提

サーバーはすべて VM で、CPU コア数は Ryzen 1700 の 1 コアです。

構築時の Elastic のバージョンは 6.4 です。

構成

  • ログ基盤サーバー (2Core , 4GB RAM, 100GB SSD, Ubuntu 18.04LTS)
  • その他いろいろなサーバー群 (Ubuntu 16.04, Windows Server 2016)

ログ基盤サーバーの構築

elasticsearch, logstash をログ基盤サーバーにインストールした。公式ドキュメント通りなのでインストール自体は省略。ハマった所だけ書いていく

elasticsearch の listen ip

初期設定は 127.0.0.1 みたいになっているので外からの接続を受け付けない。

外向きの IP アドレスに変更する必要があった。ここで、外向きの IP アドレスを指定した場合、kibana も Logstash も、接続先を外向きの IP アドレスを指定する必要がある。(localhost は通らない)

今試したところ、 0.0.0.0 と指定すれば全インターフェイスに listen してくれます。これでいいかも。

kibana の discovery を開くと何もでない

データがないからなので問題ない。あとで設定を変えれば表示されるのでくじける必要はないです。

filebeat-* みたいな表示がされているが、これは Index 名で、RDBMS で言う所のテーブル名。*がついているのは普通にワイルドカードを表している。

filebeat-2018.09.11 のように日付をつけた index が作成(といってもデータ投入時に自動で作られるので手動で作ることはしません)されるとそれ全てに検索をかける。という意味のようです。

日付を index 名に付けることで、古いログを捨てるのがとても簡単にできます。

例えば curl -X DELETE http://elasticsearch:9300/filebeat-2018.09.11 と叩けば消えてしまいます。あっという間だし、自動化も簡単。すごい便利。

kibana の用語がよくわからない

discovery・・・データをとりあえず時系列で表示して抽出等してみる画面。Excel のピボットテーブルみたいな感じ。データさえ入っていればとりあえず表示できる。

visualize・・・データをどう可視化するかを定義する。zabbix のグラフ一個が 1 visualize

dashboard・・・複数の visualize を組み合わせて画面を作ったもの

Timelion・・・すごそう (参考1)[http://acro-engineer.hatenablog.com/entry/2016/02/04/121500"] (参考2)[http://acro-engineer.hatenablog.com/entry/2016/02/04/121500]

APM・・・各アプリケーションにモジュールを入れると性能情報が取得できる・・・ぽい。

Dev Tool・・・便利ツール。Grok Debugger は logstash の設定するときに便利

Monitoring・・・ログ基盤自体のモニタリング。

Management・・・elasticsearch の index を一覧したり消したりできる。あと kibana の設定。Index-pattern の設定を変えれば discovery に色々表示できる。

X-Pack

以前のバージョンでは拡張機能を使う場合には X-Pack を入れる必要があった。この部分が商用だと有償だったみたいな経緯があってあちこちに X-Pack という記述があるが、6.4 では本体と一緒にインストールされるようになった。この部分は、ライセンスが異なったりするかもしれないので利用する際は注意が必要。

可視化回り(サーバーの負荷状況等)

metricbeat のインストール

何はともあれ、何かが可視化されないとモチベーションが沸かないので、metricbeat から試した。

インストールは単純に、apt・・・ではない。いきなりの落とし穴があった。
要約:apt で入れたあと、tar.gz に入っている設定ファイルをコピーする必要があった。

設定ファイルは /etc/metricbeat/metricbeat.yml で、必要なのは kibana の URL と elasticsearch に接続するエンドポイントくらい。ハマりそうなのは以下の場所くらいか

設定ファイルはサーバーが複数あってもまったく同じで OK なので、Ansible で入れるのが楽

1
2
3
4
`output.elasticsearch:
# Array of hosts to connect to.
hosts: ["192.168.10.20:9200"]
protocol: "http" # 内部なのでhttpsにしていない

設定変更後、一度だけどこかのサーバーで metricbeat setup を実行すると kibana にダッシュボードが作成される。サーバーの追加は metricbeat を入れるだけで自動的に認識される。

簡単に、かなりのビジュアライズがされるのですごさを簡単に味わえる

地味なポイント

metricbeat, packetbeat 等は、データを構造化(決まった形)で送信するので、elasticsearch に直接繋いで OK。

いろいろなログを elasticsearch に入れる

前説

一番やりたいことだが最難関。

何が難しいかというとログファイルは形式が一定ではない上に、どういう情報が含まれているのかも決まっていない。かといってそのままでは扱いにくいので、内容をある程度解釈してあげる必要がある(=どう解釈するか設定が必要)。

データの流れ

ログの取込には、filebeats と logstash が登場するが、役割は filebeat でログ内容を(ちょっとだけメタ情報を付加して(解釈はせずに) logstash に送信、logstash で内容を解釈して構造化した状態で保存。という流れが基本(細かくは蛇足参照)

ログ形式

ログ出力側でログを構造化できるのであればそれを行った方がよい。

個人的には LTSV 形式おすすめ。

容量的には多少大きくなるけれども、人間が読んでも読みやすく、機械にも優しいフォーマットだと思います。(なお、項目数が必ず一定である必要はありません。)

後述しますが、普通にログを解釈するのは割と大変です。

index 名

ログの種別ごとに index 名を切り替えたくなりますが、慣れるまでは辞めておきましょう。
デフォルトの filebeat-*を使わないと、kibana の visualization が適用できません。
幸い、いろいろな種類のログを混ぜても問題ないようにできています。
※というより、ログ種別の区分用のフィールドが自動で付加されています。

蛇足

最初はまず 1 台分の 1 ログだけをターゲットにして、感じに慣れた方がよい。
2 ログを流し込もうとするとハマるので注意

最初のログ取込: Nginx アクセスログ

満足度が高そうなので Nginx のアクセスログを取り込むことにした。

nginx ログ形式を LTSV に変更

全サービスが通過するリバースプロクシがあるのでそこに以下の設定を入れた。

conf.d 以下にある他の設定ファイルで access_log を指定している場合、そちらも設定変更が必要なことに注意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<code class="language-/etc/nginx/nginx.conf">    log_format ltsv 'time:$time_iso8601\t'
'http_host:"$host"\t'
'remote_addr:$remote_addr\t'
'method:"$request_method"\t'
'request:"$request"\t'
'request_length:$request_length\t'
'status:$status\t'
'bytes_sent:$bytes_sent\t'
'body_bytes_sent:$body_bytes_sent\t'
'referer:"$http_referer"\t'
'user_agent:"$http_user_agent"\t'
'upstream_addr:"$upstream_addr"\t'
'upstream_status:$upstream_status\t'
'request_time:$request_time\t'
'upstream_response_time:$upstream_response_time\t'
'upstream_connect_time:$upstream_connect_time\t'
'upstream_header_time:$upstream_header_time';

access_log /var/log/nginx/access.log ltsv;

設定してみるとわかるが、人間から見ても可読性は悪くない

filebeat インストール&設定

filebeat のインストールと、 /etc/filebeat/filebeat.yml の設定は省略。

ハマりどころとしては、 elasticsearch と logstash の出力はどちらか片方のみ。

両方設定すると起動時に落ちます。filebeat は出力先を 1 つしか設定できません。

※ファイルごとに 1 つとかではなく、サーバーごとに 1 つだけ。つらい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- module: nginx
# Access logs
access:
enabled: true

# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
var.paths:
- /var/log/nginx/access.log

# Error logs
# 複数ログを同時に送らないように enabled : false にしておく
error:
enabled: false

# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
var.paths:
- /var/log/nginx/error.log

logstash 設定

logstash は conf.d 以下のファイルを読み込むので複数種類突っ込むときは

それなりの書き方をしないと混ざる。

なお、filebeat の nginx モジュールで送信するときに既に構造化はされていて、いくつかのメタ情報が付加されている。例えば、 [fileset][module] は filebeat がセットしている値。

元のログは message に格納されているので、そこを LTSV として解釈して elasticsearch に投入。という流れ。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
input {
beats {
port => 5400
}
}

filter {

if [fileset][module] == "nginx" {
if [fileset][name] == "access" {

kv {
field_split => "\t"
value_split => ":"
}

mutate {
convert => ["request_length", "integer"]
convert => ["status", "integer"]
convert => ["bytes_sent", "integer"]
convert => ["body_bytes_sent", "integer"]
convert => ["upstream_status", "integer"]
convert => ["request_time", "float"]
convert => ["upstream_response_time", "float"]
convert => ["upstream_connect_time", "float"]
convert => ["upstream_header_time", "float"]
}

geoip {
source => "remote_addr"
target => "geoip"
add_tag => [ "nginx-geoip" ]
}

date {
match => [ "time" , "ISO8601" ]
}

useragent {
source => "user_agent"
}
}
}
}

output {
elasticsearch {
hosts => ["192.168.10.20:9200"]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
document_type => "nginx_logs"
}
}

要注意;logstash の pipeline について

ログが入力されてからどう処理するかの設定は /etc/logstash/pipelines.yml に定義されているが、デフォルトでは、全てのログが conf.d 以下の config ファイル全てを通過する。

なので、設定上で、どのログのときに処理を行うのかを判定する必要がある。

私は思いっきり勘違いをしたのだが、上の設定で言うと、 port 5400 に飛んできたログは、このファイルの定義で解釈する。のではなく、どこから飛んできたログであろうと、conf.d 以下の全てのファイルの設定を通過するので必ず対象でなければ何も filter しないように書かなければいけないようだ。ようするに・・・ログ種別の分だけ if 文が増える。 これはつらい。なので以下の記事参照・・・

https://qiita.com/micci184/items/24e197a168891f089b3d

でも、filebeat は出力先 1 個しか指定できない、どうすればいいの?

蛇足

動機

最近、Mastodon を運用始めたり、色々と Web アプリを動かしていたりサーバーが増えたりでログを見るところまで手が回らなくなってきたので、ログ基盤というのを作ってみたくなった。

※ ゆくゆくは、zabbix から Kibana に移行できたらなぁと思っている。

beats ではなく beat

名前をよく間違えて怒られた。 metricbeat”s”とか filebeat”s”ではなく単数形。

filebeat と logstash

logstash は自分でログファイルを開いて解釈して、elasticsearch に入れるところまで出来る。

ようするに logstash が入ってるのであれば filebeat は不要。では filebeat はなんで存在するのかというと、logstash は Java 製で、多機能で重量級なので全部のサーバーに入れるには重すぎる。

なので、filebeat で最低現のメタ情報だけを付加して、後はどこかの logstash に任せる。という運用がよい。

logstash の運用

(こういうことはできません)

ログの種類 1 種類ごとに pipeline を 1 個つくって、別のポートで待受させて、filebeat でどこに投げ込むかを決めればスッキリするかなと思った(filebeat は出力先を 1 個しか指定できない)

しかし、logstash はスッキリするけど filebeat 側がスッキリしないのであえてこうなってるのかなぁと思った。

謝辞

Twitter で色々と教えて下さった Jun Ohtani さん ( https://twitter.com/johtani )

多分、色々と教わらなかったらとっくに心折れてたと思います。ありがとうございます。

動機とか

Ubuntu 16.04 の apt でインストールされる samba は、4.3.x だが、Timemachine over SMB を使ってみたいので、

最新の Samba を導入してみることにした。
特段用事がないならコンパイルするよりパッケージマネージャからインストールしたいのですが、Ubuntu 16.04LTS 用の Samba 4.9.4 のパッケージがないようなので仕方なくコンパイルすることにします。

※ ppa で 4.8.x まではあったのですが… 多分、18.04LTS でもほぼ同じ手順でいけると思いますが、ppa から入れた方が楽です。

公式手順書

実際にやった手順

sudo 権限のある一般ユーザー権限で実行している。

パッケージマネージャからインストールした Samba を削除

これは、ビルドに成功してからやった方がよいかもしれない。
失敗して切り戻す可能性もあるので。

1
2
3
4
5
`# configのバックアップ
sudo cp /etc/samba/smb.conf ~

# 削除。 /etc/samba/smb.conf も消されるので注意
apt purge samba*

ソースを取得&展開

1
2
3
4
cd ~
wget https://download.samba.org/pub/samba/stable/samba-4.9.4.tar.gz
tar xvf samba-4.9.4.tar.gz
cd samba-4.9.4

ビルドに必要なパッケージをインストール

ドキュメントの通り。 libgpgme-dev パッケージは存在しないので、 libgpgme11-dev パッケージを入れるように変更した。

1
2
3
4
5
6
7
8
9
apt-get install acl attr autoconf bind9utils bison build-essential \
debhelper dnsutils docbook-xml docbook-xsl flex gdb libjansson-dev krb5-user \
libacl1-dev libaio-dev libarchive-dev libattr1-dev libblkid-dev libbsd-dev \
libcap-dev libcups2-dev libgnutls28-dev libgpgme-dev libjson-perl \
libldap2-dev libncurses5-dev libpam0g-dev libparse-yapp-perl \
libpopt-dev libreadline-dev nettle-dev perl perl-modules pkg-config \
python-all-dev python-crypto python-dbg python-dev python-dnspython \
python3-dnspython python-gpgme python3-gpgme python-markdown python3-markdown \
python3-dev xsltproc zlib1g-dev liblmdb-dev lmdb-utils libgpgme11-dev

configure

1
2
3
# 設定ファイルを /etc/samba に変更する。しないと /usr/local/samba/etc/ になってしまい使いにくい
./configure --systemd-install-services --with-systemd \
--without-ad-dc --enable-fhs --prefix=/usr/local --sysconfdir=/etc --localstatedir=/var

実行中、 not found とか no とかが出ていても、エラーメッセージがでて停止しない限りは無視してよい。

停止した際も親切なエラーメッセージが出るのでそれに従えばよい。

※ なお、有効なオプション一覧は ./configure --help で確認できる
※ デフォルトでは、 /usr/local/samba/bin にインストールされるので、 prefix を指定している
※ –enable-fhs Filesystem Hierarchy Standard 準拠の場所にインストール
※ AD DC 機能はいらないので --without-ad-dc を指定している。不要な機能は止めておくとコンパイルが早くなります(対象が減るので)

'configure' finished successfully (1m41.605s)

と表示されれば OK

make

コンパイルするだけなのでまだ root 権限は不要。それなりに時間がかかるので休憩するならここ。

1
make

'build' finished successfully (23m39.848s) と表示されれば OK

インストール

前までのステップでコンパイル”は”完成しているのでリンクしつつ実行バイナリを作成してインストール。
もし、パッケージマネージャでインストール済みの samba があるのであれば、ここで削除しておいた方が無難。
ここで時間がかかって SSH が切断される恐れがあるので、tmux や byobu 等を使用した方がよいかもしれない。

1
sudo make install

'install' finished successfully (4m39.752s) と表示されれば成功

設定と daemon の自動起動

ここで、 /etc/samba/smb.conf を作成する(が内容については割愛)

1
2
3
sudo systemctl enable winbind.service
sudo systemctl enable nmb.service
sudo systemctl enable smb.service

で終わり。と言いたいところだが、当方の環境では、 unit ファイルの実行パスが誤っていて起動できなかった。

1
2
3
4
5
6
7
8
9
# パスを確認しておく。 当方の環境では /usr/local/sbin/smbd
which smbd

export EDITOR=vim

# ExecStart=/usr/local/samba/sbin/winbindd のようになっているのでパスを変更する
sudo systemctl edit --full winbind.service
sudo systemctl edit --full nmb.service
sudo systemctl edit --full smb.service

これでも、smb.serviceの起動に失敗した。エラーメッセージは以下の通り。

1
2
create_local_token failed: NT_STATUS_ACCESS_DENIED
ERROR: failed to setup guest info.

これは、以下のコマンドで解決できる

1
2
# ネタ元: https://bugzilla.redhat.com/show_bug.cgi?id=1648399#c1
net -s /dev/null groupmap add sid=S-1-5-32-546 unixgroup=nobody type=builtin

おまけ

しくじった時は以下のコマンドでやり直しできる

1
2
3
4
5
6
# インストールしたものを削除
sudo make uninstall

# コンパイルしたものを全部削除。やらなくても大丈夫だとは思うが念のため
# インストール先のパスを変える程度であればこれを省略するととても時間短縮になる
make clean

設定ファイル

/etc/samba/smb.conf の設定内容は以下の通り。以下の conf で macOS X Mojave 10.14.3(18D109) から

Tiemmachine のバックアップが可能なことを確認した。

※ なお、Timemachine 設定前に一度 Finder から手動でこのボリュームをマウントしておく必要がある。(? Timemachine のディスクに出てこない場合のみ。当方環境では普通に表示された)

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
[global]

## Browsing/Identification ###
local master = no
preferred master = no

# Change this to the workgroup/NT-domain name your Samba server will part of
server role = standalone server
netbios name = chinachu
workgroup = WORKGROUP
server string = %h server (Samba, Ubuntu)

dos charset = CP932
unix charset = utf-8
unix extensions = yes

load printers = no
disable spoolss = yes

#### Debugging/Accounting ####

# This tells Samba to use a separate log file for each machine
# that connects
log file = /var/log/samba/log.%m

# Cap the size of the individual log files (in KiB).
max log size = 5000

####### Authentication #######

# If you are using encrypted passwords, Samba will need to know what
# password database type you are using.
security = user
map to guest = never

# timemachine
vfs objects = catia fruit streams_xattr
fruit:aapl = yes
fruit:model = MacSamba

fruit:resource = file
fruit:metadata = netatalk
fruit:locking = netatalk
fruit:encoding = native
durable handles = yes
kernel oplocks = no
kernel share modes = no
posix locking = no
fruit:advertise_fullsync = true
smb2 leases = yes

#======================= Share Definitions =======================
[timemachine]
comment = timemachine
path = /opt/timemachine/
guest ok = no
browseable = yes
read only = no
create mask = 0666
directory mask = 0777
fruit:time machine = yes

蛇足 (2019/03/13 追記)

これでコンパイルした Samba に大して Timemachine バックアップを行うと core 吐いて落ちるんですがこれは…

蛇足 (2019/03/13 追記)

Samba 4.9.5 でも同じ手順でコンパイルが可能な事を確認しました。

2019/03/14 追記

smb.conf を差し替えました。以前の設定はこちら。この設定で core を吐かずにバックアップが完了しました。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[global]
## Browsing/Identification ###
local master = no
preferred master = no

# Change this to the workgroup/NT-domain name your Samba server will part of
netbios name = filesrv
workgroup = WORKGROUP
server string = %h server (Samba, Ubuntu)

dos charset = CP932
unix charset = utf-8
unix extensions = yes

load printers = no
disable spoolss = yes

#### Debugging/Accounting ####

# This tells Samba to use a separate log file for each machine
# that connects
log file = /var/log/samba/log.%m

# Cap the size of the individual log files (in KiB).
max log size = 5000

####### Authentication #######

# If you are using encrypted passwords, Samba will need to know what
# password database type you are using.
security = user
map to guest = never

# timemachine
vfs objects = catia fruit streams_xattr
fruit:aapl = yes
fruit:model = MacSamba

fruit:resource = file
fruit:metadata = netatalk
fruit:locking = netatalk
fruit:encoding = native

#======================= Share Definitions =======================
[timemachine]
comment = timemachine
path = /opt/timemachine/
guest ok = no
browseable = yes
read only = no
create mask = 0660
directory mask = 0770
fruit:time machine = yes
fruit:time machine max size = 900G

最初に

タッチセンサーは有効にできなかった。キャリブレーション画面までは出せるのだが反応してくれない。個体不良なのか、構成ミスなのかわからず。利用目的的に問題なさそうなので諦めた。

そもそも、Raspberry Pi Zero W に 4DPi-32 を組み合わせるのが Not Supported な組み合わせ。

ピンの数が違うのでこれが本記事に影響している可能性がある。

そもそも論として同じタッチパネル液晶でも HDMI 経由で出力するような液晶をチョイスした方が良い。

が。4DPi32 は見た目がスッキリしているので悩みどころ。(もちろん、買うなら Rev2 で)

Rev2 と Rev1 の違いは、製品種別が自動判別できるようになったことと、ピンがラズパイ 1 用の 26 ピンからラズパイ 2 以降用の 40 ピンになったこと程度で、基本構成は一緒、ドライバというかカーネルも同じ。Rev1 だと config.txt に手で書かなければいけない部分が Rev2 だと自動。という程度の認識

ちなみに、接続は Raspberry Pi Zero の microSD カードを左側にした状態で、4DPi32 が Zero の下側にはみ出す方向に横長に置いた状態でピンを左寄せして接続すれば良い。

Zero の GPIO の右側(=電源 LED がある方)14 ピン分余っている状態なら OK。

もちろん、Rev2 であれば 40 ピン全部を覆うようになっている。

必要なリソース

取説の手順だと、tarball を持ってきて、/に展開すると kernel 等一式が展開されて、これで OK と書いているがそれをやると kernel panic で起動すらしなくなった。別の Linux PC で上書きした場合は起動したのでなにか間違えたのかもしれない。

手順

初期設定

この時点では HAT を接続しないこと(重要)

update をかけないこと(重要)

  • raspbian を焼く。執筆時点では 2020-12-02-raspios-buster-armhf.img
  • wpa_supplicant.conf と ssh を boot にコピー
  • microSD カードをラズパイに装着
  • microHDMI 接続のディスプレイと、USB キーボード、マウスを接続する
  • 最初の起動。
  • 初期設定を行う(ここで完了させておかないと、液晶では解像度が足りずボタンが押せない。)
  • 日本語にしたくなるが、英語にしておくと明らかに反応がよくなる。フォント周りの負荷?
  • アップデートするか?という手順は Skip する
  • IP アドレスと ssh 接続確認(注意:右上のネットワークインジケータにマウスオーバーした際に no wireless lan と表示されるのは問題ではない。クリックしたときに SSID と IP アドレスが表示されていれば良い)
  • 【オプション】vim を入れる。vi だとつらい

4DPi 用のカーネル導入

  • boot 内の overlays -> overlays_org にリネーム(念の為)
  • tarball を展開した中の /boot/overlays を boot にコピー
  • tarball 内の /boot/*.img と config.txt を boot にコピー
  • tarball 内の /lib/modules/* を rootfs の /lib/modules/ にコピー(上書きが発生したら何か間違えている)
  • tarball 内の /etc/modules を rootfs の /etc/modules に上書きコピー
  • config.txt に dtoverlay=4dpi-32 を追記(場所はあまり関係ない)

4DPi に GUI を出力するように設定

次のステップからは microHDMI での GUI は表示されなくなる。

  • 4DPi を接続する
  • tarball 内の /etc/X11/xorg.conf.d/99-fbturbo.conf を rootfs の /etc/X11/xorg.conf.d にコピー
  • 【オプション】/usr/share/X11/xorg.conf.d/99-fbturbo.conf を 99-fbturbo.conf.nouse 等にリネーム
  • 起動させて 4DPi に GUI が表示されるかテスト

最後に

apt update, upgrade をやってしまうと壊れる可能性がある。執筆時点(2020/12/19)では、この手順で動いたあとに、upgrade をすると、表示されなくなった。

やり直しは…最初からになるので、なにか事情があって apt upgrade を実行するのであれば、SD カードのフルバックアップを取ってから行うことをおすすめする。

トラブルシュート

基本

  • dmesg で起動時のメッセージを読んで、エラーになっているのを潰していく。
  • raspbian を素で起動しても出るエラーもあるのであまり深追いしないように注意
  • lsmod で指定したモジュールがロードされているかチェック。されていなければ、modprobeを使って手動ロードさせてみる。ダメならなにかエラーが出るはず。
  • GUI 周りのログは /var/log/Xorg.0.log を見る。HDMI にも LCD にも画面が出ていなければなにか出ているはず
  • デバイスファイルが作成されているかチェックする /dev/fb1 がないとか

microHDMI に画面出力したい

LED に画面が表示されていると GUI 操作できないに近いので、HDMI に戻したい場合は、

/etc/X11/xorg.conf.d/99-fbturbo.conf99-fbturbo.conf.nouse のようにリネームして再起動すれば OK。 LED に出力させるようにする場合は、ファイル名を元に戻す。

やらなかった手順

以下は覚書。問題が起きたときにやったが、それにより他の問題を引き起こした等でやらなかったか意味がなかった手順。

  • overlay_org -> overlays にファイルを上書き(これをしないと WiFi が使えなくなったことがあった)
  • /dev/fb1 が存在せず、 /dev/fb0 のみがある場合は、上記ファイルの /dev/fb1 を /dev/fb0 に書き換える…だが、おそらくこの場合は何かが間違っていると思われる。(config.txt の dtoverlay が間違っている?)

補遺

wpa_supplicant.conf

/boot/wpa_supplicant.conf (boot パーティションの /wpa_supplicant.conf)に以下の内容でファイルを作っておくと、ラズパイ起動時に /etc/wpa_supplicant/wpa_supplicant.conf に上書きされる(上書きしたあと boot の方は削除される)。

boot パーティションは FAT32 なので Windows でも読み書きできるが、rootfs は ext4 なので Linux 以外で読み書きできないという事情があるのでこういう親切をしてくれていると思われる。

1
2
3
4
5
6
7
8
9
`country=ja
update_config=1
ctrl_interface=/var/run/wpa_supplicant

network={
scan_ssid=1
ssid="your_sssid"
psk="your_psk"
}

補遺 2

バックライトの PWM 制御

ソフトウェア的にはデフォルトで PWM 制御が ON。

4DPi 本体の裏側のジャンパで、バックライトの PWM 制御が OFF になっているので、そこを変更する。

明るさの変更は、

1
echo 100 | sudo tee /sys/class/backlight/4dpi-32-pwm/brightness

明るさは、 (暗)0〜100(明)の間の整数で指定する。0 だとバックライトオフ。

datasheet のコマンド sudo echo 0 > ... では Permission Denied になってしまうので tee を挟む必要があった。

ディスプレイの自動スリープ OFF

http://gml.blog.jp/archives/8144348.html

/etc/xdg/lxsession/LXDE/autostart に以下を追記

1
2
3
@xset s off
@xset s noblank
@xset -dpms

補遺 3

タッチパネル

1
2
sudo apt-get install evtest libts-bin libts0
sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/event0 ts_calibrate

/dev/input/event0 はマウス等を接続していると /dev/input/event4 になったりする。

間違っている場合は ts_calibrate を起動した瞬間にエラーが出るのでわかる。

・・・が。タッチパネルの調整画面は表示されるが反応しない。

ここで心が折れてしまった。

参考リソース

環境

  • Rails 4.2.6
  • Ruby 2.2.4
  • request_scope 1.3.1

どんな用途?

以下のような場合に有用です。

  • IP アドレスをログに出したりしたい
  • Model からセッションに入っているユーザー情報を参照したい

How To

  • gem ‘request_store’ を Gemfile に追加
  • bundle install
  • application_controller.rb に before_filter を追記
  • どこからでも RequestStore.fetch() で 3 でセットした内容を使用可能

コード例

上記手順の 3 で追記するコードは以下の通り

1
2
3
4
5
6
class ApplicationController < ActionController::Base
before_filter :set_request_store
def set_request_store
RequestStore.store[:request] = request
end
end

セットしたものを使いたい場合のコード

1
2
RequestStore.fetch(:request) { nil }
RequestStore.fetch(:request) # 取得できないと no block given エラー
  • 後ろについている { nil } は指定した値が取得できなかった(セットされていない)場合に実行されるブロック。
  • 取得できなかった場合はブロックの返り値が返される(例だと、取得できなかった場合は nil)
  • ブロックを書かずに取得出来なかった場合は no block given (yield) エラー
  • 取得できた場合は、ブロックが無くても普通に動く

参考

リクエスト単位でグローバルな参照を持たせて Audit ログをスッキリ実装したい

http://qiita.com/ainame/items/823b396d560d82194d48

その他

参考にした Qiita に書いてある request_store_rails は最近でも更新があってそっちのがよさそうに見える。

が、しかし。  request_store は大人気(私的見解) gon が依存しているので

ただの request_store で良いだろうと思っている。

ためした環境

  • SpringBoot 1.3.5
  • Java 8

基本

FORM のフィールドにアノテーションをつけると、自動的にバリデーションを
行ってくれる。SpringBoot の場合は、Controller の BindingResult に結果が入る。

1
2
3
4
5
6
// lombok使ってると思ってください
@Data
public class MyForm {
@NotNull
String value;
}

単項目チェックはこれで十分。javax.constraints に用意されているだけでも

結構な数のバリデータがそろっているので割といける。

https://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

独自のバリデーションを作りたい場合も案外難しくはない。そう、単項目チェックならね。

複合チェック

バリデータをがんばって作ればできるらしいので頑張る。
…がそんなのは忘れるので応用のパターン使えばよいのではないか

応用

BeanValidation のチェック対象は、フィールドだけではなく、メソッドの返り値もチェックできる。以下は例

1
2
3
4
5
6
7
8
9
@Data
public class MyForm {
String value;

@AssertTrue
public boolean isValueNotNull() {
return (value != null);
}
}

エラーメッセージは異なるが、value == null の時検証エラーになる。(NotNull と同条件)

ということは、これで複数のフィールドにまたがるチェックを行うと話が早いだろう。

注意事項としては、getter と同じルールのメソッド名にしないとチェックされない。

本気でハマるので注意。

エラーメッセージ

普通に BeanValidation を使うと、エラーメッセージがかなりシステム寄りのものになる。

それではいろいろと困る場合(ほとんど困る)メッセージリソースに以下のように記述する。

1
AssertTrue.myForm.value = カスタムエラーです。

みたいな形で書くと各 form ごとにメッセージを変えられる。

中の動きを想像してみる

検証対象クラス内の public メソッドを抽出する

→getter として正しくないメソッドは捨てる(返り値の型含めて)

boolean & isMyMethod() は OK。 boolean でなければ get ~である必要がある。

→ それぞれのメソッドごとに、アノテーションを探す。メソッドについていればその検証
→ getter ぽいメソッド名からフィールド名を算出して、そのフィールドにアノテーションがあれば検証。
みたいな動きしているのかなーと妄想。(ソース見ればいいんだけれども)

チェック用アノテーションがついていても、getter として正しくない返り値と、メソッド名の
組み合わせの場合、無視されるので大体あっているのではないか。

TODO

記事内のソースがあってるかテストする

環境

  • Rails 4.2.6
  • web_console 2.3.0

原因

RAILS_RELATIVE_ROOT を設定した結果、web_console がたたく API の
エンドポイントが変わっているにも関わらず、web_console がそれを認識していない。

対応

以下の行を追記して、web_console のマウントポイントを設定する

1
<code class="language-rb:config/environments/development.rb">config.web_console.mount_point = config.relative_url_root + '/web_console'

蛇足

修正されたようです。

前提

  • Biscuit (<https://eatbiscuit.com/ja) の Linux 版は AppImage で配布されている
  • AppImage のアプリは、メニューにも出てこないし、Dock とか Dash に登録できない
  • .Desktop ファイルを /home/yakumo/.local/share/applications/ に置くことでメニューに出てくる

問題点

  • Dash にアプリケーションを登録することはできるが、アプリが起動すると Dock のアイコンではなく別のアイコンが表示されてしまう(Biscuit が 2 個になってしまう)

why?

  • おそらく実行ファイル名とウィンドウ名が異なるため

解決策

  • .Desktop ファイルに StartupWMClass=biscuit を追加する。

調べ方

対象のアプリケーションを起動した状態で xprop を実行するとマウスカーソルがターゲットの形になるので、その状態で調べたいアプリケーションをクリックする。

WM_CLASS(STRING) = “biscuit”, “biscuit” となっているので、それをそのまま .Desktopファイルの StartupWMClass に指定すれば良い

(Firefox など、名前が異なるものが 2 つ表示される場合があるが、この場合どちらを指定するかは不明。どちらかを指定すれば当たると思われる)

蛇足

biscuit.Desktop の中身は以下の通り

1
2
3
4
5
6
7
8
9
10
11
12
13
`#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Name=Biscuit
Keywords=
Exec=AppImageへのパス %u
Terminal=false
X-MultipleArgs=false
Type=Application
Categories=Network;
Icon=アイコンファイルへのパス(png形式)
StartupNotify=true
StartupWMClass=biscuit

こんなにあるキーボード選びの観点

Twitter に書いては消し書いては消しするとアレなので書いてみる。
個人的好みは色付きの方

  • キー配列 日本語 or 英語
  • Windows キー
  • マクロキー有無
  • テンキー有
  • やじるしキーの配列が逆T字必須 or どうでもいい
  • CTRL キーの位置 Aの横 or 左下 or 左下Fnの一つ右
  • キーの方式 メカニカル or 静電容量式 or メンブレン or バックスプリング or パンタグラフ
  • 接続方法 有線 or 無線(USB) or 無線(BlueTooth)
  • 刻印の印字方法
  • カナ印字有
  • カナ印字場所(キートップ・キー手前)
  • 打ち心地・音 うるさいのが好み or 静かなのがいい

2022/06/09 追記

IIJの公式にあったブログも、SA-M0に関する記述がほぼ削除されました。
正直なところ、終息になるのは仕方ないにしても設定プロトコルくらいは公開してくれても良かったのではないかと思いますし、わざわざアプリ削除までしなくても…というのが素直なところです。
軽くググった範囲ではAPKファイルも手に入らなそうな感じです。

では、ゴミなのか?

いや、ちょっと待ってください。SA-M0の中には ROHM BP35A1 が入っています。
ちょっと分解すればすぐに取り出せるので、これに ROHM BP35A7A と BP35A7-accessories を購入してやれば、Raspberry Piであるとか、Arduino機器で使用することができます。
上記は、Chip1Stopで1000円もせずに購入可能です。BP35A1は5000円以上する高級なものなのでそのままジャンク品としてオークション等に出品しても良いでしょう
(品番 C1S625901561294 C1S625901542509)

概要

nodejs を使って SA-M0 を経由してスマートメーターの値を取得します。

SA-M0 を経由することで、Wi-SUN アダプタの事を考えずに、LAN 上にスマートメーターが存在しているように見えます。

まず最初に

ゼロスタートだったので、基礎知識を身につける為に以下を読みました。

ECHONET 電文の作り方

これを読まないと echonet-lite の出力が理解できません。略語だらけなので。。。

一応本稿でも必要な略語は解説していますがこちらの方が正しい解説です。

https://qiita.com/miyazawa_shi/items/725bc5eb6590be72970d

WiSUN を直接喋る場合と SA-M0 を経由する場合の違い

http://route-b.iij.ad.jp/archives/128

Wi-SUN アダプタを使って直接やりとりする場合は、上記 blog のスマートメーターの探索認証・接続の処理を行う必要がありますが、SA-M0 を経由した場合、この部分はすべて SA-M0 がやってくれます。(ただし、スマホアプリで初期設定をすることが必要です)

SA-M0 からデータを取得するアプリケーションは、単純に暗号化等の事を考えずに、リクエストを送信すれば応答してくれます。楽です。

Node.js の echonet-lite ライブラリの使い方

今回必要なのは情報の取得だけですが、思いっきりドハマリしたので echonet-lite ライブラリの使い方も含めて書いていきます。

機器スキャンのサンプルプログラム(下記 URL) を題材にして説明します。

https://www.npmjs.com/package/echonet-lite

基本的な考え方

  • ECHONET 機器(SA-M0)と通信を行う為には、自分自身も ECHONET 機器になる必要があります。
  • ECHONET の通信対象の区別は、 (IPを使う場合) IPアドレス + 機器ID で行われます。
  • echonet-lite 的に命令の送信とその応答は非同期で扱う必要があります。

ECHONET 機器になる部分は、echonet-lite が受け持ってくれるので難しいことはありません。

今回であれば、自分自身はコントローラ (05ff01) になれば良いので、サンプルをそのまま使うことができます。

# 機器 ID はなんでもよさそうですが、下手な機器を設定すると他の ECHONET 機器から発見されて状態取得系の命令が飛んでくるかもしれません。

やりとりは、命令を送信すると、それに対する応答が返ってくる。という単純なものです。(命令によっては、PUSH 通知的な動きもありそうですが未確認)

落とし穴なのは、データ送信をした結果の応答は EL.initialize の第三引数の function に返ってくるという点です。

データの送信

1
EL.sendOPC1 = function( ip, seoj, deoj, esv, epc, edt)

これを使えばよい。 大事なことなので強調しますが、応答はEL.initializeの第三引数のfunctionで受け取ります

引数 内容 設定値
ip 要求先IPアドレス 192.168.1.25 (EL.search()して見つけておく)
seoj 要求元機器ID 05ff01 (コントローラー) *1
deoj 要求先機器ID 028801 (低圧スマート電力メーター) *1
esv 命令コード EL.GET (0x62: Get) *2
epc 引数1(プロパティを指定する) 0xE0 (積算電力量) *3 *4
edt 引数2(Set系命令の値) 空文字列(Get系命令であれば無視される為) *4

*1 機器 IDについて

APPENDIX 機器オブジェクト詳細規定に規定されている。 (今回は、EL.search()して見つけた機器を片っ端から調べた)

https://echonet.jp/spec_object_rk/

*2 命令コード

第二部 ECHONET Lite 通信ミドルウェア仕様の 3-6 ページに記載されている、 表 3-9 要求用 ESV コード一覧に規定されている。

EL. 定数を見れば分かるかもしれない。

https://echonet.jp/spec_v113_lite/

*3 プロパティ (EPC)

APPENDIX 機器オブジェクト詳細規定に規定されている。 スマートメーターは 3.3.25 (3-290 ページ)

https://echonet.jp/spec_object_rk/

*4 引数

規格上は、複数のプロパティを同時に要求できるが、 echonet-lite ではその機能は実装されていない。

# 送ろうと思えば送れそうな関数はあるが、一個ずつ要求しても良いだろうという考えだと思われる。

データの受信

データの受信は、 EL.initialize の第三引数の関数にて行われます。

第三引数の関数は function( rinfo, els, err ) となっており、それぞれ

引数 内容 設定値
rinfo 送信元情報 IPアドレス以外は使わないかも
els 受信情報 主に扱う部分
err エラー情報 err != undefined ならエラーなので無視するなりなんなりする必要あり

rinfo サンプルデータ

1
<code class="language-javascript">{address: '10.1.0.1xx', family: 'IPv4', port: 35110, size: 18 }

こんな感じなので、IP アドレス以外はつかわなそうです。

els サンプルデータ

els サンプルデータは、下記のコマンドを実行した際の応答例である。

1
EL.sendOPC1('10.1.0.100', '05ff01', '028801', EL.GET, "e8", "");
1
2
3
4
5
6
7
8
9
{ EHD: '1081',
TID: '0000',
SEOJ: '028801',
DEOJ: '05ff01',
EDATA: '7201e804001e0096',
ESV: '72',
OPC: '01',
DETAIL: 'e804001e0096',
DETAILs: { e8: '001e0096' } }
引数 内容 備考
EHD ECHONETバージョン 1081固定
TID トランザクションID echonet-liteを使う限りは 0000固定
SEOJ 送信元機器ID 要求のDEOJと等しいはず
DEOJ 送信先機器ID 自分自身の機器ID
EDATA 生データ データ部を解釈しないでそのまま格納したもの
ESV 応答・通知用ESVコード 72は GETに対する応答を表す *1
OPC プロパティ数 回答データ数。echonet-liteを使う限りは 01 固定
DETAIL データ詳細 解釈前データ
DETAILs データ詳細 解釈後データ。要求プロパティがキーになっている。この例だと e8を要求したのでe8だけが含まれる

*1 ESV コード

第二部 ECHONET Lite 通信ミドルウェア仕様の 3-6 ページに記載されている、 表 3-10 応答・通知用 ESV コード一覧に規定されている。

https://echonet.jp/spec_v113_lite/

注意点

  • 自分が送信した命令も受信してしまうので、それは処理しないようにする必要がある。

例えば、 rinfo が自分自身の IP アドレスである場合は無視するとか、SEOJ が自分の機器 ID だったら無視するとか

  • 数値は 16 進数なので注意。 parseInt(value, 16) で 10 進に変換可能。

プログラムの終了

echonet-liteのサンプルプログラムを動作させると、いつまでまっても終了しない。これは、ソケットの受信待ちがずっと行われている為である。

プログラムを終了する為には以下の用にすれば良い。

(EL.initialize が内部で使用する dgram.socket が返ってくるので、これをクローズすればイベントハンドラが終了できる)

1
2
3
4
5
6
7
8
9
10
11
12
var elsocket = EL.initialize( objList, function( rinfo, els, err ) {
(略)
    if (データ受信完了) {
global.complete_flag = true; // グローバル領域にフラグを立てる
}
}

setTimeout(function(sock) {
if (global.complete_flag) { // データ受信完了なら
sock.close(); // sock = elsocket
}
, 10000, elsocket); // 関数に EL.initializeの返り値を渡す

雑なプログラムですが、こんな感じでフラグを監視するようにすれば OK です。

まとめ

出来たものは、下記 URL で公開しています。

https://github.com/yakumo-saki/b-route-reader

蛇足

sendOPC1 を promise でラップしたら使いやすさが一気に上がる気がする。