それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

windowsのdiskpart のコマンドは先頭数文字入れればいい。

windows の diskpart のコマンドは長すぎる

たとえば、パーティションを選択するときに、select に続いて partition と英文字で打ち込むのだが・・・・長いです。

select partition 2 

partition はついつい t / i /oタイプミスしがちです。

短縮形でOK。

パーティションを選ぶときの partition や、削除するときの delete などは、まぁ打ち間違いやすい傾向にあります。 これらの単語は、同じ文字が何度も出てくるわけですから。

これ短縮できるんです。知らなかったです。

part でいい

partitionpart でいいんです。

DISKPART> select part 4

Partition 4 is now the selected partition.

del でいい

deletedel まで短縮できるんですよ。

DISKPART> del part 
 Virtual Disk Service error:
 Cannot delete a protected partition without the force protected parameter set.
DISKPART> del part override
DiskPart successfully deleted the selected partition.

ある程度の音節で済ませられるのは便利ですね。

省略形のほうが打ちやすい

今まで、なんで気づかなかったのが自分でも不思議です。

先頭3文字で許される。

list は lis でいいし、disk は dis でいい。

 DISKPART> lis dis

  Disk ###  Status         Size     Free     Dyn  Gpt
  --------  -------------  -------  -------  ---  ---
  Disk 0    Online          476 GB   175 GB        *
  Disk 1    No Media           0 B      0 B

select も sel でいいんですね。

DISKPART> sel disk 0

Disk 0 is now the selected disk.

select のあとの partitionpar まで省略できるんですね。

WindowsというかDOS時代に拡張子を3文字に限定したMSらしいといえばMSらしいですね。

現実的な落とし所は4文字だと思うけど。

pyenv で一時的にバージョンを変える- pyenv shell

pyenv で一時的にバージョンを変える

pyenv には、そのシェル内部だけ、一時的にバージョンを変えることができるサブコマンド shell が存在する。

pyenv shell の例

pyenv shell 3.7.2

コマンド類

基本的なコマンド pip / python などは pyenv で指定した方に併せられます。 これは pyenv の shims で実現されています。

シェルの有効範囲

シェルから起動したシェルは基本的に設定を引き継ぐ。

たとえば、次のような起動は、設定を引き継ぐ。

  • pyenv shell から起動したシェル(bash)から起動した bash ( pyenv shell → bash )
  • pyenv shell から起動したシェル(bash)から起動した sh ( pyenv shell → sh )

pyenv のスコープ(有効範囲)

python の pyenv には、影響スコープの設定がいくつかあって

  • system
  • global
  • local
  • shell

の順にバージョン指定を変えることができる。

global / local は .python-version を使って実現するのだが、

shell は完全に現在のシェルだけに適用されるので便利です。

pyenv shell は virtualenv activate とほぼ同じですね。

GPTとMBRの相互変換をWindowsのdiskpartコマンドで。

MBRとGPTの変換 ( windows )

WindowsMBR と GPT を変換する。

すでにGPTが普通になっているが、ときどきMBRなディスクが必要になる。 その時のために、 MBR/GPT の相互変換をWindowsでもできるように調べておいた

DISKPART の起動

ディスクの変更は、 コントロール・パネルのディスクの管理からでもできるが、diskpart のほうが楽だと思う。

管理者のPowerShellかコマンド・プロンプトで diskpart を起動する

MBRからGPTへ

diskpart に、convert コマンドがあるので、それを使えばオッケ。

convert gpt

GPTからMBR

逆をするだけですね。

convert mbt

MBR から GPTへ

mbr から gpt へ変更したいと思います。

ディスクの一覧の確認

最初に、接続されているディスクを確認します。Disk2を変更します。

Diskには、GPT にフラグ * がついてないので、MBRであるとわかります。

DISKPART> list disk

  Disk ###  Status         Size     Free     Dyn  Gpt
  --------  -------------  -------  -------  ---  ---
  Disk 0    Online          476 GB   175 GB        *
  Disk 1    No Media           0 B      0 B
  Disk 2    Online           14 GB    14 GB

ここからディスクを選びます。

DISKPART> select disk 2

いったん、このディスクをclean にします。

DISKPART> clean
DiskPart succeeded in cleaning the disk.

clean で掃除したら、GPTに変更します。

DISKPART> convert gpt

DiskPart successfully converted the selected disk to GPT format.

変換できました。

結果を確認します。Gpt 列にフラグ * がついているので、変換できたことがわかります。

DISKPART> list disk

  Disk ###  Status         Size     Free     Dyn  Gpt
  --------  -------------  -------  -------  ---  ---
  Disk 0    Online          476 GB   175 GB        *
  Disk 1    No Media           0 B      0 B
* Disk 2    Online           14 GB    14 GB        *

macでwq-quick のDNS設定使うとDNS問い合わせがおかしくなる

mac で wireguardをコマンドから使うと・・・

wg-quick を使って設定からDNSを設定しているのですが、DNSがおかしくなるのです。

wireguard のコマンド wg-tools をhomebrew でインストールし、それを使ってWiregurard接続をした。

接続は問題なく稼働する。だが、接続切断後後にDNS設定が元に戻されない。

調査してみた。

up / down を繰り返して表示されるログを確認した。

wg-quick up wg0
wg-quick down wg0

接続に使った設定は次の通り。

[Interface]
PrivateKey = YE7mc**************
Address = 172.16.4.3
DNS= 192.168.1.1

[Peer]
PublicKey = Dgh8*********s=
EndPoint= mydns.example.com: 12345
PresharedKey= 9YnmaD4***********fPvY=
AllowedIPs = 0.0.0.0/0

wg-quick の設定に DNS を設定している。

接続コマンドの実行結果

上記設定で実行すると、接続とDNS設定はうまくいくのだが、、、、DNSの書き換えが乱暴

takuya@wireguard$ wg-quick up wg0
(略
[#] networksetup -setdnsservers Bluetooth PAN 192.168.2.1
[#] networksetup -setdnsservers Wi-Fi 192.168.2.1
[#] networksetup -setdnsservers USB 10/100/1000 LAN 192.168.2.1
[+] Backgrounding route monitor

すべてのネットワーク接続設定に対し、set DNSが発行されています。

この設定はあまりにも乱暴だと思うんです。

切断後も残る。

切断してみると。DNS設定を削除していないことがわかります。

takuya@wireguard$ wg-quick down wg0
Warning: `/usr/local/etc/wireguard/wg0.conf' is world accessible
[+] Interface for wg0 is utun2
[#] rm -f /var/run/wireguard/utun2.sock
[#] rm -f /var/run/wireguard/wg0.name

接続時にはDNSを設定して、切断後にはDNS設定を消してないんですね。

設定が残ってしまった。これを確認する。

本当に設定が残ってしまったのか、確認してみます。

IFS=$'\n'; for i in $( networksetup  -listallnetworkservices | \grep -v aster); do echo "networksetup -getdnsservers  $i";networksetup -getdnsservers  $i  ;done

networksetup -getdnsservers  Bluetooth PAN
192.168.1.1
networksetup -getdnsservers  Thunderbolt ブリッジ
192.168.1.1
networksetup -getdnsservers  PPPoE
192.168.1.1
networksetup -getdnsservers  mopera
192.168.1.1
networksetup -getdnsservers  USB 10/100/1000 LAN
192.168.1.1
networksetup -getdnsservers  iPhone USB 3
192.168.1.1

これは残ってますね。これではVPN(Wiregurard)を接続していないと何もできなくなります。というかWiregurardのPeer先をIPでなくホスト名(ドメイン名)で指定していると二度と接続できなくる。

未削除で残存するDNS

マジか。こりゃ大変だ。ローカルDNSを参照したら詰む。

これは wg-quick のバグですね。

設定を削除します。

これでは大変なので、設定を一旦リセットしました。

IFS=$'\n'; for i in $( networksetup  -listallnetworkservices | \grep -v aster);do ;networksetup -setdnsservers $i "Empty" ;done

wg0.conf からDNSの設定を削除します。

原因は、wg-quick の設定でDNSエントリでのネットワーク設定が、妙ちくりんなで、それを使ったためです。

ということは、DNS設定は、マニュアルで PostUp / PostDown で書くしかありません。

wg0.conf

[Interface]
PrivateKey = XXXXXXXYE7mc=
Address = 172.16.0.3
### !ここが今回のエラーの原因。
# DNS= 192.168.1.1

[Peer]
PublicKey = XXXXXXXXXXXjs=
EndPoint= mydns.example.com:12345
PresharedKey= XXXXXXXXXfPvY=
AllowedIPs = 0.0.0.0/0

PostUp/PostDownをDNSの代わりに使います。

networksetup  -listallnetworkservices | \grep -v as  |  xargs -d '\n' -n 1 echo networksetup -setdnsservers $i "Empty" 

私は、GNU Xargsを使っているので、 -d のデリミタオプションで解決している。しかし、BSDPOSIX xargs だともっと大変になるだろう

PostUp   = bash -sc 'networksetup  -listallnetworkservices | \grep -v as  |  /Users/takuya/.bin/xargs -d "\n" -n 1 -I@  networksetup -setdnsservers @ "192.168.1.1"'
PostDown = bash -sc 'networksetup  -listallnetworkservices | \grep -v as  |  /Users/takuya/.bin/xargs -d "\n" -n 1 -I@  networksetup -setdnsservers @ "Empty"'

wireguard のDNS設定は危ういですね。

sed で改行削除/sedで全文(複数行マッチ)

sed で改行を消してCSVにしたい。

コマンドの結果の改行を消してCSVのようなカンマ区切りに変換したい。

他の置換ツールや python / rubyワンライナーに回せばいいと思うけど、sedでできたほうが汎用性が高そう。そのためsedで改行を置換、改行を別の文字に変えてCSVのような文字区切りをやってみる。

sed は行ごとの処理

sed は、「行ごと」で処理するのが基本動作なので、改行にマッチとか、改行を削除することはできない。

grep でもそうなんだけど、行処理が暗黙の前提なものを全体マッチにするときは、ちょっとオプションを見てあげると解決。

全体にマッチする。

マッチする箇所を「全体」に設定してあげるといい。

networksetup  -listallnetworkservices | \grep -v as | sed -z 's/\n/,/g'

-z オプション

-z オプションを使って、複数行に亘るマッチングができる。z は「全文・全体」と覚えれば絶対に忘れない。

bsd sed / gnu sed

macos で試したが、 -z があるのは GNU sed のみ。

macos などのBSDななsedでは、次のように。

sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'

macの場合は、brewgnu sed ( gsed ) をインストールしてもいいですね。

参考資料

https://orebibou.com/ja/home/201607/20160714_003/

マルチコアのCPUを使い切って圧縮を速くする

gzip の限界 = CPU 1コア

マルチコア・マルチスレッドのCPUがあるのに、gziplzma(xz)や bzipといったメジャーな圧縮は、CPUを1コアで処理するんですね。

CPU使用率を見てみたら、CPU利用率は100%を超えないんですね。

HDD・SSDの書き込み速度に限界があるからそれでも良かったんだろうが。いまはメモリが一般的に64GBもある時代です。うちのマシンでもメモリが12GBもあるのに3GB程度の圧縮に、5分とか耐えられません。もうちょっと速くしたい。

cpu利用率が100%で頭打ちになる。gzip

gzipを使ってると、CPU利用率が100%で止まるんですよね。lzma などの他の圧縮でも同じ。

f:id:takuya_1st:20210413030211p:plain

gzip/ gunzip をマルチで処理する pigz / unpigz

Pigz のマニュアルには次のように書いてある。スレッドを使って並列処理をするっぽい。

Pigz  compresses using threads to make use of multiple processors and cores.  The in‐
       put is broken up into 128 KB chunks with each compressed in parallel.  The individual
       check  value  for  each chunk is also calculated in parallel.  The compressed data is
       written in order to the output, and a combined check value is calculated from the in‐
       dividual check values.

実際にやってみると

cpu の利用率が、グンと上がる。CPUが余ってるなら、圧縮でもCPUをフルに使ったほうが良さそう。

f:id:takuya_1st:20210413031647p:plain

インストール

gzip の場合

sudo apt install pigz

bzip の場合

sudo apt install pbzip2 

xzの場合

sudo apt install pixz

まとめてインストールできない感じなのが辛い。piXXだとか pbzip2 とか表記が揺れているのも辛い。

詳しくは、参考資料に上げたURLに記載されているコマンドを必要に応じてインストールする必要がある。

7z でも マルチスレッド対応らしいが、time 7z a -mmt=on sample.gz ./sample を見た感じだと、gzipなどはマルチコア・マルチスレッドを使わないようだ。

実行速度比較

使用したCPU

takuya@livaz:~$ 7z

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,
64 bits,4 CPUs Intel(R) Pentium(R) CPU N4200 @ 1.10GHz (506C9),ASM,AES-NI)

使用したファイル

takuya@livaz:/var/lib/libvirt/images$ ll -h sample
-rw-r--r-- 1 takuya takuya 3.6G Apr 13 02:04 sample

gzip での速度

takuya@livaz:/var/lib/libvirt/images$ time gzip sample

real    4m10.198s
user    3m58.115s
sys     0m5.359s

pigz での速度

takuya@livaz:/var/lib/libvirt/images$ time pigz sample

real    1m38.232s
user    4m56.837s
sys     0m9.070s

比較

real で比較

コマンド real user
gzip 4m10.198s 3m58.115s
pigz 1m38.232s 4m56.837s

CPUを4スレッド使うことで、ちゃんと約4倍の時間を稼げていることがわかる。もちろん分散のオーバーヘッドが入るので、完璧に1/4にはならないのですが。

まとめ

CPUが余ってるなら マルチスレッドをCPUで使い切ったほうが絶対早い。

参考資料

OPNSense でAPIを使う。

opnsense でAPIを使う。

APIを使うには、キーが必要

APIのリクエストのエンドポイント(OPNSenseのアドレス)

APIのリクエストのパス(ドキュメントから)

キーの作成

APIの認証キーの作成は、管理画面から行える。

ユーザーをクリック、編集、APIキー作成を選んで作成。作成するとキーファイルがダウンロードされる。

ユーザを選んで

f:id:takuya_1st:20210412173639p:plain

キーを作成。

f:id:takuya_1st:20210412173703p:plain

ファイルがダウンロードされる。

f:id:takuya_1st:20210412173724p:plain

かんたんにリクエストを投げてみる。

作成されたファイルは、イコールで結ばれたキーバリューなのでそのままソースコードBashに貼り付けて使える。

key=XXX
secret=XXX

これを curl コマンドとして作成。

f:id:takuya_1st:20210412173818p:plain

あとはリクエストを投げるだけ。

サンプルで投げたAPIリクエストは、 ファームウェアの更新状況が完了しているか見るもので、マニュアルはここ ( https://docs.opnsense.org/development/api/core/firmware.html ) にある。

設定をAPIでバックアップする

たとえば、設定をAPI経由でバックアップするのは、システムのファームウェアから、os-api-backup のパッケージを追加する。 すると、API経由でXMLがダウンロードできるようになる。

パッケージインストール

f:id:takuya_1st:20210412174507p:plain

リクエス

key=XXXX
secret=XXXX
host=192.168.1.1
curl -k -u $key:$secret https://${host}/api/backup/backup/download -o config.$(date -I).xml

これで設定のバックアップは自動化できる。pfsense/OPNsenseは良く出来てるよなぁ。

参考資料

Firmware — OPNsense documentation

https://forum.opnsense.org/index.php?topic=18218.0

SPFからGoogleのSMTPのIPアドレスをぱぱっと調べる。

Google GMAILSMTPIPアドレス範囲

以前、Google workspaceで のGmailに存在しないアドレスはローカルのSMTPサーバーで処理するように、メールのルーティングルールを書いたわけですが。

ルールをかいたので、ローカルのSMTPサーバーへは、Gmailからメールが転送されてくるわけです。

しかし、ローカルの「ポート25」を全部開放すると大変なことになるわけです。もちろんPostfixのフィルタやスパムフィルタで処理もしているのですが、やっぱり スパムはとんでもない量が飛んでくるために、Postfixが詰まってしまうし、スパムフィルタでCPU使用率も跳ね上がるわけです。

そこで、IPアドレス範囲を絞ってGmailSMTPリレー以外をドロップするようにFWに記述し、port 25 のアクセスをSPFベースで絞ったほうがマシであろうと思われるわけです。

Google IP address ranges for outbound mail servers - Google Workspace Admin Help

SFP を見てみる。

gmailSPFのTXTレコードは次のようになっている。

$ dig txt gmail.com +short | grep spf
"v=spf1 redirect=_spf.google.com"

gmailspf 一覧は _spf.google.com に書かれているとわかる。

SFPレコードからinclude を取り出す。

では、_spf.google.com を調べてみる。

dig txt  _spf.google.com +short | $(which grep) -Po '(?<=include:)[^\s]+'

SFPレコードを見てみると

$ dig txt  _spf.google.com +short
"v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"

これをgrep でマッチすることで、

dig txt  _spf.google.com +short | $(which grep) -Po '(?<=include:)[^\s]+'
_netblocks.google.com
_netblocks2.google.com
_netblocks3.google.com

ここで、include なので、3つのドメイン名のTXTレコードを合わせたものが、GmailSPFだとわかる。

spf レコードから IPアドレスを取り出す。

それでは、 _netblocks.google.com をさらに見てみる。

dig txt   _netblocks.google.com +short | $(which grep) -Po '(?<=ip4:)[0-9\./]+'

これを実際に実行してみると、次のようになる。

dig txt   _netblocks.google.com +short | $(which grep) -Po '(?<=ip4:)[0-9\./]+'
35.190.247.0/24
64.233.160.0/19
66.102.0.0/20
66.249.80.0/20
72.14.192.0/18
74.125.0.0/16
108.177.8.0/21
173.194.0.0/16
209.85.128.0/17
216.58.192.0/19
216.239.32.0/19

これで、IPアドレスが得られることがわかる。

gmailSPFのTXTレコードについて。

ここまでで、GmailSPFレコードは、次のような階層を用いて管理されているとわかる。

redirect → include  → ipv4:[address/mask address/mask address/mask]

まとめてIPアドレスを取り出す。

for i in $( dig txt  _spf.google.com +short | \grep -Po '(?<=include:)[^\s]+' ); do 
  dig $i +short  txt  | \grep -Po '(?<=ip4:)[0-9\./]+'
done

これを実行することでSPFレコードに記載されたIPアドレスが得られることがわかる。

for i in $( dig txt  _spf.google.com +short | \grep -Po '(?<=include:)[^\s]+' ); do 
 dig $i +short  txt  | \grep -Po '(?<=ip4:)[0-9\./]+';
done | sort  -n | uniq
35.190.247.0/24
35.191.0.0/16
64.233.160.0/19
66.102.0.0/20
66.249.80.0/20
72.14.192.0/18
74.125.0.0/16
108.177.8.0/21
108.177.96.0/19
130.211.0.0/22
172.217.0.0/19
172.217.128.0/19
172.217.160.0/20
172.217.192.0/19
172.217.32.0/20
172.253.112.0/20
172.253.56.0/21
173.194.0.0/16
209.85.128.0/17
216.239.32.0/19
216.58.192.0/19

SPFレコードからIPアドレス範囲を得ることで、送信元のIPアドレスを見ることができる、そしてそのIPアドレスをから 送られてきたメールのみに絞ることができるが、FWのフィルタリングにも使えそうであるとわかる。

今回使った grep

上記の例で使ったGREPのコマンドは、GNU grep拡張正規表現を用いて、マッチした場所を取り出すオプションと、先読みを使っている。そのために随分とシンプルになって嬉しい。

参考資料

Google IP address ranges for outbound mail servers - Google Workspace Admin Help

cloudflare 以外からのアクセスを拒否する/ipsetとiptablesで

linux のFWで特定のアクセス元だけを許可する。

たとえば、cloudflare からのアクセスを許可して、通常のアクセスは全部拒否する。

アクセス元IPで接続を限定する。 クラウドVPSにデータを置くときには、アクセス制限をキッチリやらないから、情報漏洩が起きたときに被害甚大で「何万人」のデータが閲覧可能だったみたいな報道になっちゃうわけで。

cloudflareのIPリストはこれ

2021-04-06 現在では、次の場所に公開されています。https://www.cloudflare.com/ips/

173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12
172.64.0.0/13
131.0.72.0/22

ipset でリストを作る。

ipset create cloudflare
curl -s https://www.cloudflare.com/ips-v4 | xargs -n 1   echo sudo ipset add cloudflare @

アクセス元IPで制限。

cloudflare以外のアクセスを全部拒否する。

送信元のIPアドレスが指定のポートに来たときにチェックして、特定のIPアドレス以外を全部削除(DROP)する設定を次のように iptablesに入れてやる。

IF=eth0
dport=443
list=cloudflare
iptables -A INPUT -i $IF -p tcp -m comment --comment "takuya: deny ${dport} no cloudflare " \
   -m conntrack  \
   --ctstate NEW -m tcp --dport $dport -m set ! --match-set $list src -j DROP

消したいときは -Aを-Dに変える。

IF=eth0
dport=443
list=cloudflare
iptables -D INPUT -i $IF -p tcp -m comment --comment "takuya: deny ${dport} no cloudflare " \
   -m conntrack  \
   --ctstate NEW -m tcp --dport $dport -m set ! --match-set $list src -j DROP

iptablesについて。

上記の書き方だと次のとおりになる。

削除(DROP)ではなく拒否・拒絶(REJECT)でもいい。

-A は先頭にルールを追記するので即座にルールが最優先でマッチングされる。

-m comment --comment 'コメント' は、コメントを突っ込むモジュール。iptables -Lしたときにコメントが表示されるので便利。

-m tcptcp接続に限定している。http2 は UDPも使うので tcp はなくてもいいかもしれない。

-dport は宛先ポート

-m conntrack --ctstate NEWTCP新規接続を指定している。(SYN+SYN/ACK+SYN)の SYNを指定する。

-m set は ipset を使う設定。マッチしたときに適用される。 ! -m set は 否定でマッチしないとき(set以外)に適用される。

-set name は ipset のルール名を指定する。

参考資料

Allowing Cloudflare IP addresses – Cloudflare Help Center

chgrp で Operation not permittedになる場合の対処

事象

Linuxでファイルの所属グループを変更したいが、Operation not permitted になって権限が足りずにできない。

chgrp www-data virtualhosts/prod/test.com
chgrp: changing group of 'virtualhosts/prod/test.com': Operation not permitted

対応1:sudo をつける

sudo すればできるわけですが、sudo に頼るのも良くないですよね。

対応2:実行ユーザーが所属するグループを確認する。

chgrp を実行ユーザーが所属するグループにだけ、ファイルの所属グループを変更できます。

ユーザーが所属するグループ

たとえば、takuyaが以下の場合、www-dataに変更することはできない。

$ id takuya
uid=1000(takuya) gid=1000(takuya) groups=1000(takuya),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)

上記の状態で chgrp www-dataするとパーミッションエラーになる。

ユーザーtakuyaが、www-dataに所属していれば、www-dataへ変更することができる。

www-dataグループにtakuyaが所属する

usermod -aG www-data takuya
logout

usermodは一度ログアウトしないと反映されない。

パーミッションエラーにならない。

グループに所属していればエラーにならない。

chgrp www-data virtualhosts/prod/test.com
## エラーなく実行完了

たまに、新規インストールしたLinuxでユーザーのグループ設定をわすれて、パニクることがあるのでまとめておいた。

lxc storage を btrfs のサブボリュームから作成する

lxc storage を btrfs のサブボリュームから

参考資料を見ながら、lxc の storage をbtrfs のサブボリュームで作ることにした。

全体の流れ

  • btrfs のストレージを用意する
  • ストレージを接続する
  • btrfs でフォーマットする。
  • btrfs をマウントする
  • btrfs でサブボリュームを作る
  • サブボリュームをlxc のストレージ保存場所に mount --bindする。
  • lxc にストレージとして認識させる。

できれば lxd init の実行前にやっておくと無難。

btrfs のストレージを作ってサブボリュームを作る

btrfs で初期化する。

sudo mkfs.btrfs -f /dev/sda1

btrfs をマウントする

sudo btrfs check /dev/sda1
sudo mkdir /mnt/8ce1230a
sudo mount /dev/sda1 /mnt/8ce1230a

btrfs のサブボリュームを作る(サブボリュームにはマウントが必須)

sudo btrfs subvolume create /mnt/8ce1230a/LXC-pool0

btrfsのボリュームとサブボリュームを snap/lxd にマウント ( bind )

takuya@ubuntu:/etc/systemd/system$ sudo btrfs subvolume list /mnt/8ce1230a
ID 256 gen 12 top level 5 path LXC
ID 257 gen 84 top level 5 path LXC-pool0

コマンドからサブボリュームを作成しマウントできた。あとは設定を書いて起動時に行うようにする。

マウント設定を書く。

マウント設定は、fstab でもいいのだけど、lxd が起動する直前のタイミングでマウントをしたいので、今回はsystemdに書いた。

$ cat <<'EOF' | sudo tee /etc/systemd/system/mnt-8ce1230a.mount
[Unit]
Description=External USB Ssd Storage
Before=snap.lxd.daemon.service

[Mount]
What=/dev/disk/by-uuid/8ce1230a-xxxx-xxxx-xxxx-xxxxxxx
Where=/mnt/8ce1230a
Type=btrfs

[Install]
WantedBy=multi-user.target
RequiredBy=snap.lxd.daemon.service

マウント設定を有効にしてマウントする

sudo systemctl enable mnt-8ce1230a.mount

systemd のマウント設定は、マウントするPATH名がファイル名になることが必須なので注意。

bind 設定も合わせて書く。

lxd がもともと使っている disks フォルダそのものをマウントしてしまったほうが楽だろうということで。

また、このbindのマウントの前にbind元になる/mnt/ のマウント後実行したい。ので順番を意識してunitsファイルを書く。

$ cat <<'EOF' | sudo tee /etc/systemd/system/var-snap-lxd-common-lxd-disks.mount
[Unit]
Description=External Storage for LXD
After=usb-ssd.mount
Before=snap.lxd.daemon.service

[Mount]
What=/mnt/8ce1230a/LXC
Where=/var/snap/lxd/common/lxd/disks
Options=bind

[Install]
WantedBy=multi-user.target

[Install]
WantedBy=multi-user.target
RequiredBy=snap.lxd.daemon.service
EOF

有効にしてマウント。

sudo systemctl enable var-snap-lxd-common-lxd-disks.mount

マウントできた

これで、2つのマウントが完成した。

/dev/sda1 on /mnt/8ce1230a type btrfs (rw,relatime,ssd,space_cache,subvolid=5,subvol=/)
/dev/sda1 on /var/snap/lxd/common/lxd/disks type btrfs (rw,relatime,ssd,space_cache,subvolid=256,subvol=/LXC)

lxd を初期化する。

lxd を初期化する

lxd init 

ここでサブボリュームで指定した lxd のパスを入れてあげればいい。すでに、初期化している場合でも再初期化すればいい。

ストレージ追加で使う場合

ストレージ追加で使う場合は、次の通り

 lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0

サブボリュームをsourceを指定すればいい。

lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0
lxc profile show default
lxc profile device set default root pool=pool0

初期化済みの場合、デフォルト・プロファイルの設定がlxd init で定義されたストレージを使うようになっているため、いったんプロファイルを別に作成してdefaultを削除して作り直してもよさそう。

ストレージ作成

lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0
takuya@ubuntu:~$ lxc storage list
+---------+-------------+--------+--------------------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |                   SOURCE                   | USED BY |
+---------+-------------+--------+--------------------------------------------+---------+
| default |             | btrfs  | /var/snap/lxd/common/lxd/disks/default.img | 1       |
+---------+-------------+--------+--------------------------------------------+---------+
| pool0   |             | btrfs  | /mnt/8ce1230a/LXC-pool0                    | 0       |
+---------+-------------+--------+--------------------------------------------+---------+

ストレージ切り替え

takuya@ubuntu:~$ lxc profile device set default root pool=pool0

結果

takuya@ubuntu:~$ lxc storage list
+---------+-------------+--------+--------------------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |                   SOURCE                   | USED BY |
+---------+-------------+--------+--------------------------------------------+---------+
| default |             | btrfs  | /var/snap/lxd/common/lxd/disks/default.img | 0       |
+---------+-------------+--------+--------------------------------------------+---------+
| pool0   |             | btrfs  | /mnt/8ce1230a/LXC-pool0                    | 1       |
+---------+-------------+--------+--------------------------------------------+---------+

参考資料

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0571

LXCの起動済みのコンテナにmacvlanのネットワークを足してホスト側ネットと通信する

LXCの起動済みのコンテナにmacvtap/macvlan を足す。

既存のコンテナ・インスタンスがあって、そこにmacvtap を追加する。

すでにあるコンテナは、次の通りのsample01を作ってある。これはlxdbr0を経由して外部と接続する。

takuya@ubuntu:~$ lxc start sample01
takuya@ubuntu:~$ lxc list
+----------+---------+---------------------+-----------+-----------+
|   NAME   |  STATE  |        IPV4         |    TYPE    | SNAPSHOTS |
+----------+---------+---------------------+-----------+-----------+
| sample01 | RUNNING | 10.135.9.111 (eth0) | CONTAINER | 0         |
+----------+---------+---------------------+-----------+-----------+

ネットワークを追加する

次のコマンドで起動中のコンテナ・インスタンスにネットワーク・インターフェイスを追加する。追加するNICはmacvlan でホスト側のネットワークに直接接続することに。

追加するコマンド。

lxc config device add sample01 mytap0 nic nictype=macvlan parent=eth0

lxc config device add で、インスタンスを指定しデバイス追加する。追加するのはnic です。

コマンドの実行書式

lxc config device add コンテナ名  設定名 nic nictype=タイプ parent=元になるやつ

タイプは、macvlan かbridged などいくつかある。だいたいはbridgedかな。

実行するとNIC接続される。

この追加したネットワークをdhcp 有効にして設定を書いておく。

cloud-init ファイルは永続化ファイルじゃないのでOS再起動で変更が消失云々と書かれているのですが、実験なのでとりあえず書く。

/etc/network/interfaces.d/50-cloud-init

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet dhcp

書いたらネットワークを再起動

/etc/init.d/networking restart

DHCPが降ってきた。

root@sample01:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:45:89:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.135.9.111/24 brd 10.135.9.255 scope global dynamic eth0
       valid_lft 3018sec preferred_lft 3018sec
    inet6 fd42:d79a:8b11:68a4:216:3eff:fe45:89a7/64 scope global dynamic mngtmpaddr
       valid_lft 3557sec preferred_lft 3557sec
20: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:cc:85:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.11127/24 brd 192.168.11255 scope global dynamic eth1
       valid_lft 42621sec preferred_lft 42621sec

ついかしたNICはホスト側でも確認できる

takuya@ubuntu:~$ lxc list
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+
|   NAME   |  STATE  |         IPV4         |                     IPV6                      |   TYPE    | SNAPSHOTS |
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+
| sample01 | RUNNING | 192.168.11127 (eth1) | fd42:d79a:8b11:68a4:216:3eff:fe45:89a7 (eth0) | CONTAINER | 0         |
|          |         | 10.135.9.111 (eth0)  |                                               |           |           |
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+

ネットワークを再起動して接続したら、 ホスト側にveth が増えた。

ホスト側で ip a を叩くと veth が増えていた。

19: veth466e08c2@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master lxdbr0 state UP group default qlen 1000
    link/ether 5e:04:fc:b6:ee:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0

んんんん。vethかぁ。macvlan の空間に接続するveth なのかなぁ。とりあえずmacvtapではなさそう

仮想マシンに追加された、ホスト側との接続用IPを確認する。ホスト側のネットワークからDHCPでIPをもらってくる

takuya@ubuntu:~$ lxc exec sample01 ip addr show eth1
20: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:cc:85:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.11127/24 brd 192.168.11255 scope global dynamic eth1
       valid_lft 43014sec preferred_lft 43014sec
    inet6 fe80::216:3eff:fecc:85e2/64 scope link
       valid_lft forever preferred_lft forever

このIPアドレスについて、疎通を確認する。

とうぜんだけど、ホスト・ゲスト間で通信はできない。macvlan をホスト側に作っておくと通信できる。

ping 192.168.11127 -I eth0
ping 192.168.11127 -I macvlan1

永続化設定

先程は、とりあえずで /etc/networkに書いたが、macvlan の設定に問題なければ、永続化の設定を書く。または デフォルト・プロファイルに記述する。

cloud-init を使って起動されたコンテナはcloud-init の流儀に従う。debian なら /var/lib/cloud/seed/nocloud-net/network-config 、 ubuntuなら netplanですかね。

/var/lib/cloud/seed/nocloud-net/network-config

version: 1
config:
  - type: physical
    name: eth0
    subnets:
      - type: dhcp
        control: auto
    name: eth1
    subnets:
      - type: dhcp
        control: auto

この他の追加方法

今回は、コンテナに直接デバイスmacvlan を追加した。

この他にも、コンテナ作成時のデフォルト設定(プロファイル)にデバイスを追加しておくと、コンテナ作成時に最初から追加された状態で初期化される。という方法を取ることもできる。

参考資料

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0535?page=2

https://linux-svr.com/%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E4%BB%AE%E6%83%B3%E5%8C%96/59.php#default-bridge

https://lxd-ja.readthedocs.io/ja/latest/cloud-init/

macvtap でできた仮想マシンとホストと通信してみる。macvlan/macvtap

macvtap でできた仮想マシンとホストと通信してみる。

macvtap でできた仮想マシンとホストと通信してみる。

macvtap を使った場合、ホスト・ゲスト間の通信ができない。

しかたないので、ホストに別にNICをmacvlan で定義して、そこを経由して通信するとしてみる。

sudo ip link add dev macvlan1 link eth1 type macvlan mode bridge
sudo ip addr add 192.168.11.112/24 dev macvlan1
sudo ip link set macvlan1 up

macvtap に向けてパケットを送ってみる、応答がきて通信がができることがわかる。

ping 192.168.11.103 -I macvlan1

削除するには

sudo ip link set macvlan0 down
sudo ip addr del 192.168.11.112/24 dev macvlan0
sudo ip link del dev macvlan0 

ちゃんと macvlan 経由で出ていくことがわかる。

takuya@ubuntu:~$ ip route get 192.168.11.103
192.168.11.103 dev macvlan1 src 192.168.11.101 uid 1001
    cache
    

ちょっと理解が追いつかず怖いなと思った箇所が次の通り。

パケットはごちゃまぜ(?)になって eth0から出たはずのパケットは、内部でeth1 へ到達している。

ホストに [eth1,eth0] の2物理NICがあって、macvlan1@eth1として設定。ゲストでは macvtap はmacvtap@eth0 に割り当てたのだが、

ゲスト側のネットワークは次の通り

root@OpenWrt:/# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 52:54:00:ab:ff:35 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.12/24 brd 192.168.11.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feab:ff35/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 52:54:00:3a:09:af brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.107/24 brd 192.168.11.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe3a:9af/64 scope link
       valid_lft forever preferred_lft forever

ホスト側のネットワークは次の通り。

takuya@ubuntu:~$ ip a
### もともとの物理NIC
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:dd:23:c4 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.111/24 brd 192.168.11.255 scope global dynamic eth0
       valid_lft 42628sec preferred_lft 42628sec
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether e8:fc:af:c7:aa:21 brd ff:ff:ff:ff:ff:ff
### ここが macvlan
5: macvlan1@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether aa:85:1b:0e:e4:59 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.101/24 brd 192.168.11.255 scope global dynamic macvlan1
       valid_lft 42623sec preferred_lft 42623sec
### ゲストの eth0
10: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 500
    link/ether 52:54:00:ab:ff:35 brd ff:ff:ff:ff:ff:ff
### ゲストの eth1
11: macvtap1@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 500
    link/ether 52:54:00:3a:09:af brd ff:ff:ff:ff:ff:ff

[ホスト macvtap0@eth0 , ゲスト側eth0]、[ホスト macvtap1@eth1 , ゲスト側eth1]、がそれぞれペアとなる割当にしてある。

ここで、ホストの macvlan1@eth1 から ゲストの eth0 へ向かってping を投げると・・・到達するんですよね。ホスト外部には出ません。これがちょっと気持ち悪いと思った点。

macvlan はホスト内部で処理されてしまう。つまりホスト内部のvlan という扱いなのですかね。ちょっとモヤっとする。

ホスト・ゲストで通信ができた。

これで無事に macvtap と疎通する事ができるとわかる。

ただし、これを起動時に最初からやるのはしんどいのです。if-up / if-down スクリプトをこのご時世に書いて管理するのもちょっと。

そこでnetworkd が動いているなら、それを使うと、macvlan を起動時にと来ることができるはずです。

3つのファイルを作ります。

ぱぱっとファイルを作ります。

touch /etc/systemd/network/{00-eth1.network,00-macvlan1.netdev,00-macvlan1.network}

つくった。

takuya@ubuntu:~$ ll  /etc/systemd/network/*
-rw-r--r-- 1 root root 46  3月 24 22:25 /etc/systemd/network/00-eth1.network
-rw-r--r-- 1 root root 60  3月 24 22:22 /etc/systemd/network/00-macvlan1.netdev
-rw-r--r-- 1 root root 44  3月 24 22:18 /etc/systemd/network/00-macvlan1.network

eth1 に macvlan1 をbridge定義する 設定 macvlan1 をネットワークデバイスとする 設定 macvlan1 にIPアドレスを割り当てる 設定

ですかね

/etc/systemd/network/00-eth1.network

[Match]
Name=eth1

[Network]
MACVLAN=macvlan1

/etc/systemd/network/00-macvlan1.netdev

[NetDev]
Name=macvlan1
Kind=macvlan

[MACVLAN]
Mode=bridge

/etc/systemd/network/00-macvlan1.network

[Match]
Name=macvlan1

[Network]
DHCP=ipv4

最後の netplan を networkd 経由に

ubuntu の場合 netplan が動作しているので。

/etc/netplan/50-cloud-init.yaml

network:
    renderer: networkd

これで、 networkd を優先にする。

netplan generate で設定を作り出す。

sudo netplan --debung generate

追記

netplan を renderer を networkd に変更した場合、/run/systemd/network/10-netplan-eth0.networkが作成される。

sudo ls  /run/systemd/network/10-netplan-eth0.network

これよりも、networkd が作成するファイルが優先するために、 10-netplan-eth0.networkより、優先度の高いファイル名を設定する必要がある

もしかしてnetplan っていらない子なんじゃ。。。

再起動

sudo netplan --debug apply
sudo systemctl restart systemd-networkd.service

netplan を適用しつつ、networkd も設定してやる。

結果を確認

takuya@ubuntu:~$ sudo systemctl status systemd-networkd.service
● systemd-networkd.service - Network Service
     Loaded: loaded (/lib/systemd/system/systemd-networkd.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2021-03-24 22:48:36 JST; 7s ago
TriggeredBy: ● systemd-networkd.socket
       Docs: man:systemd-networkd.service(8)
   Main PID: 3526 (systemd-network)
     Status: "Processing requests..."
      Tasks: 1 (limit: 9257)
     CGroup: /system.slice/systemd-networkd.service
             └─3526 /lib/systemd/systemd-networkd

 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth1: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: Enumeration completed
 3月 24 22:48:36 ubuntu systemd[1]: Started Network Service.
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth1: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: DHCPv4 address 192.168.11.101/24 via 192.168.11.1
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: DHCPv4 address 192.168.11.111/24 via 192.168.11.1

これで、networkd でも macvlan が作成されるとわかる。

netplan は非対応

raspi 4 に ubuntu を入れているので、ubuntu だしnetplan で設定しようかと思ったが、netplan は macvlan を作れないらしい。

なので if-up/if-down を書くか、/etc/network/interfacesを書くか、networkd を書く必要がある。今回は networkd に書くことで対応した。

参考資料

https://tenforward.hatenablog.com/entry/20111221/1324466720

https://major.io/2015/10/26/systemd-networkd-and-macvlan-interfaces/

Today's HOGE(2019-10-04)

Raspberry Pi4 にKVM+qemu で仮想マシンの仮想環境を作る/raspi+kvm

Raspberry Pi4 にKVM+qemu仮想マシンの実行環境を作る

Raspberry Pi4 もメモリが8GBもあれば、仮想マシンを動かすのに十分な性能があると思うんですね。

仮想マシンを動かしたらいろいろ便利そうなので、仮想マシンを動かすことにした

SDカードにOSを準備

KVMが有効な仮想マシンを動かすために、通常のRaspbianではちょっと厳しい。

Raspbian はKVMのサポートが無効化された32bit版が配布されている。動くには動くだろうが、KVMのサポートは欲しい。

そこで、KVMサポートがされたOSをインストールする。

選択肢はこの通りになる。

  • Raspbian 64bit
  • Ubuntu for raspi arm64 server
  • Arch とか

raspbianの64bit版は、KVMが有効化された状態でコンパイルされているので、Raspbianなら64bit版を使う。

わたしは、初回インストールの手間を考えて、ubuntuにした。

ubuntu には、arm4でraspiようにビルドされたイメージが配布されているので楽そうだった。

ubuntuの場合は、次のイメージを探してきて、SDカードに書き込む

ubuntu-20.04.2-preinstalled-server-arm64+raspi.img

初回起動後

初回起動したら、そのままSDカード容量に合わせて、ext4がリサイズされる。

初回アップデートも初回起動時に行われる。

ssh など必要な初回インストール作業が少なくて済むのがubuntu-server版の魅力

いつもの通り、piユーザーの代わりになる、自分のユーザーを作り、piユーザーはログインを無効化しておく。

ubuntu版の場合は、piユーザーではなく id/pw=ubuntu/ubuntu がプリインされている。

ユーザー作成

sudo adduser takuya
sudo usermod -aG ubuntu takuya
sudo usermod -aG adm takuya
sudo usermod -aG dialout takuya
sudo usermod -aG cdrom takuya
sudo usermod -aG floppy takuya
sudo usermod -aG sudo takuya
sudo usermod -aG audio takuya
sudo usermod -aG dip takuya
sudo usermod -aG video takuya
sudo usermod -aG plugdev takuya
sudo usermod -aG netdev takuya
sudo usermod -aG lxd takuya

ssh 設定

PasswordAuthentication no
Match User ubuntu Address 192.168.100.0/24
  PasswordAuthentication yes

カーネル確認

ubuntu で raspi4 にインストールしたら、この表記になった。

takuya@ubuntu:~$ uname -a
Linux ubuntu 5.4.0-1030-raspi #33-Ubuntu SMP PREEMPT Wed Feb 24 11:20:11 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux

apt 設定

インストールを最小限にしておく。

echo -e  "APT::Install-Suggests 0;\nAPT::Install-Recommends 0;" | sudo tee /etc/apt/apt.conf.d/00-no-install-recommends

apt ミラーサーバー設定(わからない

apt ミラー設定は、ミラーサーバーの設定がわからない・・・ちょっと特殊なバイナリ( arm64 )なのであとに回す

たとえばJAISTミラーには、ARM64がない。arm/arm64バイナリはミラー郡の提供に含まれないっぽい。

ちょっと特殊みたいなので、いつものUbuntuミラーサーバーは使えないっぽい。

gihyo の ubuntu weekly recipe (柴田充也氏)の記事によると。

ちなみにarm64やarmhfなどamd64以外のアーキテクチャーパッケージについては,ports.ubuntu.comで提供されています。こちについては「公式のミラーサーバー」は用意されていないため,Raspberry Piなどでは原則として「ports.ubuntu.com」を指定することになります。 https://gihyo.jp/admin/serial/01/ubuntu-recipe/0677?page=2

ということです。

apt の更新

sudo apt update && sudo apt upgrade -y

タイムゾーンの設定

sudo timedatectl set-timezone Asia/Tokyo

ロケール設定

sudo apt-get install language-pack-ja
sudo update-locale LANG=ja_JP.UTF-8
sudo locale-gen
sudo locale -a

または

sudo dpkg-reconfigure locales

GUIでやるなら後者が確実。

http://www.dreamedge.net/archives/111

LXD 最初から入ってる。つおい

ちなみに、ubuntuだと、Snapcraftの snapd がLXDで最初から導入されているので、qemu/libvirtを使わなくても仮想マシン(コンテナ)を手軽に作れる。

わざわざqemukvmを動かさなくても、殆どの場合LXC/lxd で十分な気もする。

takuya@ubuntu:~$ sudo snap list
Name    Version   Rev    Tracking       Publisher   Notes
core18  20210128  1990   latest/stable  canonical✓  base
lxd     4.0.4     19040  4.0/stable/…   canonical✓  -
snapd   2.49      11115  latest/stable  canonical✓  snapd

kvm qemulibvirt で仮想環境を作る

kvm が使えるかどうか、念のためにチェック

sudo apt install cpu-checker

KVM が使える!。raspberry Pi4 でもKVMが使えます。

takuya@ubuntu:~$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used

kvm qemu 一式をインストールする

kvmqemulibvirt 経由で使うために、つぎをインストール。

sudo apt -y install qemu-kvm libvirt-daemon-system \
libvirt-daemon virtinst bridge-utils libguestfs-tools virt-top

uefi をインストール

qemu-efi がないと動かない。(aarch64用)

sudo apt install qemu-efi

virtio で ネットワークを扱う。

インストールしてないと、failed to find romfile "efi-virtio.rom" になり、ネットワークを扱えない仮想マシンになった。

sudo apt install ipxe-qemu

virbr0 を使う。

libvirtd が提供する virbr0 を使うために、dnsmasq が必要だった。インストールが必要だった。

sudo apt install dnsmasq

virt/kvm グループに参加

自分のユーザーで、system virshを使いたいのでグループに参加しておく。ユーザーセッションでもいいんだけど、qemu://system も使えたほうが楽だし。

sudo usermod -aG libvirt takuya
sudo usermod -aG libvirt-qemu takuya
sudo usermod -aG kvm takuya

qemu仮想マシンを起動(openwrt)

とりあえず、OpenWRTでも動かしてみようか。

openwrt には、arm64の仮想マシンで動かすための armvirt/64bit のバイナリが配布されていて、起動方法も公式Wikiに記載されていたのでこれを起動しようと思う。

openwrtの公式→ https://openwrt.org/docs/guide-user/virtualization/qemu#openwrt_in_qemu_aarch64

イメージをダウンロード → https://downloads.openwrt.org/releases/19.07.6/targets/armvirt/64/

ダウンロードしたら、ext4squashfs は qcow2 に変えておく qemu-img convert -f raw $in -O qcow2 $out

とりあえず、起動してみる。

KERNEL_IMAGE=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image
INITRAMFS=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image-initramfs
DISK=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-root.squashfs.qcow2
sudo qemu-system-aarch64 --enable-kvm -m 1024 -smp 2 -cpu host -M virt -nographic \
  -kernel $INITRAMFS \
  -drive if=none,file=$DISK,id=hd0 \
  -device virtio-blk-device,drive=hd0 \

OpenWRTがちゃんと起動する。

qemuの起動方法をよく知らないので、マニュアルが助かった。

virt-install仮想マシンを作成

qemukvm 仮想マシン起動ができたので、同じように、virt-install から libvirt経由で使える仮想マシンとしてインストールしてみる。

ネットワークはとりあえず、br0 を作ってそれを使うことに。

br0を作成

sudo brctl addbr br0
sudo brctl addif br0 eth1
sudo ip link set br0 up

eth1 を使ってるのは SSH接続が切れないように。USB-LANをRaspiに挿してeth1として使ってる。SSH経由で作業するeth0 (メイン・オンボード)をbr0に入れるとSSH経由で完結しなくなる面倒が起きる。それを避けるために eth1を使った。

virt-install仮想マシン作成。(openwrt)

KERNEL_IMAGE=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image
INITRAMFS=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image-initramfs
DISK=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-root.ext4.qcow2

sudo virt-install   \
  --connect qemu:///system \
  --name=wrt01  \
  --disk path=$DISK,format=qcow,device=disk,bus=virtio  \
  --ram=512 \
  --arch=aarch64 \
  --cpu host-passthrough \
  --virt-type kvm \
  --graphics none \
  --network bridge=br0,model=virtio \
  --boot kernel=$KERNEL_IMAGE,initrd=$INITRAMFS,kernel_args='root=fe00' \
  --import \
  --features acpi=off \
 

acpiをOFFにしないとエラーなる。仕方ないこととはいえ、ちょっとめんどくさい。

acpiが使えないので、virsh から shutdown が使えない。デメリットである。終了はdestroyをする必要がある。

ただ、macvtap などネットワークの管理がvirt-managerに任せられるのでメリットである。

ubuntu仮想マシンを起動。

qemuubuntu 仮想マシンを起動しよう。とおもったんだけど、LXCあるから別にいらないのでパス。

raspbian の仮想マシンを起動

Raspbianがちゃんと動かせたら、RaspiAP のプロジェクトRaspberry Pi - Wifi Routerのプロジェクトの成果物を仮想マシンで動かせるじゃん。

って思ったんだけど、よく考えたら、仮想マシンWifi渡せないしRaspApプロジェクト動かせないんだよね。。どうせWifiバイスをUSBパススルーは地獄なんだし。OpenWRTが仮想マシンで動いてたら、スマホから扱えるルーターにはなるわけだし。

とりあえずraspbian を このraspi ubuntu で起動しよう。とおもったんだけど、よく考えたら、raspiのarmのあれこれだとか吸収しても、ちょっと風変わりなDebianになるだけなので、労苦の割に使いにくいものにしかならない。Debian欲しいならLXDで素のDebian起動したらいいじゃないかと思ったのでパス。

adguard home

Raspiにadguard homeとか入れたら面白そうだと思ってて、仮想マシンでやろうと思ってたんだけど、UbuntuにしたのでLXD/Docker使えるので、これもVMじゃなくても良くなってしまった。

kvm+qemuな wrtから iperf3をやってみる。

仮想マシンで起動したwrt から

root@OpenWrt:~# iperf3 -c 192.168.100.1
(...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.06 GBytes   912 Mbits/sec  352             sender
[  5]   0.00-10.05  sec  1.06 GBytes   905 Mbits/sec                  receiver
iperf Done.

ubuntuをインストールしたraspiから

takuya@ubuntu:~$ iperf3 -c 192.168.100.1
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.10 GBytes   941 Mbits/sec  119             sender
[  5]   0.00-10.01  sec  1.09 GBytes   937 Mbits/sec                  receiver

仮想マシン化による速度低下は気にならないレベルだった

まとめ raspi4で kvm + qemu 起動

8GBのメモリがあると快適に動く。

Ubuntuを使うと楽ちん。ubuntuのRaspi arm64 server版だと、最初からKVMが有効になってた。

らくにqemu と libvirtdで仮想マシンを扱えるのだけど、UbuntuだとSnapdとLXDがインストール済みで配布されているので。よくあるLinux仮想マシンを扱う場合ではdocker / lxd が使えるんでそんなにメリットはないかも。

WindowsのArmかOpenWrtくらいしか動かしてメリットあるものはないと思うんだ。仮想マシンでwrt動かせるから、Raspi4をルーターとして放置しつつ他の用途にも使えそうで熱い感じはある。自分で、arm64用になにかプログラミングするならとてもいいと思うんだけどね。

今回は、Ubuntuサーバー版をつかって仮想マシン作ったんだけど、GUIを扱うときにqemuでそこそこそ軽快に動かせるから管理は楽になるかもしれない。

純粋なサーバーとしてRaspiの4K2画面出せるグラフィック系の機能を殆ど使わないので、だいぶ勿体ない贅沢な使い方だとは思う。

参考

http://www.rcps.jp/doku.php?id=linux:kvm

https://www.kkaneko.jp/tools/container/ubuntu_qemu.html

https://kb.novaordis.com/index.php/Virt-install#Optional_Installation_Method_Option

https://dustymabe.com/2020/01/30/virt-install-boot-from-specific-kernel/initrd-just-for-install/

http://manpages.ubuntu.com/manpages/eoan/man1/virt-install.1.html

https://translatedcode.wordpress.com/2017/07/24/installing-debian-on-qemus-64-bit-arm-virt-board/

https://openwrt.org/docs/guide-user/virtualization/qemu#openwrt_in_qemu_aarch64

https://kitsunemimi.pw/notes/posts/running-windows-10-for-arm64-in-a-qemu-virtual-machine.html

gsuite のアカウント増やさずにメアドだけ増やして mailcow で管理する。

概要

GSuite(google workspace) 宛のメールを、独自のメールホストへ転送してGoogle アカウント管理下にないメールアドレスを使う。

前提条件

課金済み

重大な前提条件 Google Workspaceで課金済み。

課金しているGsuiteが必要です。歴史的経緯の無償版Gsuiteで使えません。

postfix などのメールサーバー

postfix単体が正しく設定されたもの。

デフォルトインストールでは、スパム処理や配送処理をしてないので、そのままでは内部転送が使い物にならないので devecot などIMAPを正しく設定しておいたもの( とりあえずmailcow いれとけばおっけ)

設定ステップ

設定は4ステップが必要

  1. Gmailの設定でホストを追加
  2. 転送設定(ルーティングを書く)
  3. 送信設定(返信・送信をGmail経由にする)
  4. postfix 側でメールボックスを追加

1.google admin でホストを追加

Google admin console にアクセスし、アプリからGmailを開いてホストを追加する。

f:id:takuya_1st:20210318190645p:plain

2. google admin → gmail → でルーティングを追加

ルーティングを追加する。

ルーティング設定の概要は次の通り

  • 対象経路
    • 外部から受信
    • Gmail内部から受信メール
  • 対象メアド
    • 受信者が一致
      • パターンに一致
      • 正規表現パターンに合致したもの
    • 存在しないメールアドレス
  • 転送先
    • 先程設定した mailcow サーバー
    • バウンスメールはOP25Bのために転送されないので無視する

      f:id:takuya_1st:20210318190929p:plain

      f:id:takuya_1st:20210318191000p:plain

      f:id:takuya_1st:20210318191023p:plain

3 送信ルート・返信ルートをGmail経由にする。

mailcow posfixから直接相手に送信せずに、GoogleSMTPを経由するよう設定する。

まず、GoogleSMTP経由を許可させる。 f:id:takuya_1st:20210318191147p:plain

つぎに、PostfixのTransport設定する

トランスポート設定で、smtp-relay.gmail.com:587 をスマートリレーで設定する。

このとき、SMTP認証が必要になるので、Google Workspace中の適当な アカウントをless secure apps(安全性の低いアプリ)に認定するか、アプリごとのパスワード生成(2FA時)をしてリレーを設定すればオッケ。

もし送信元のmailcowが固定IPアドレスであれば、SMTP認証は不要なのだが・・・

mailcow の場合

mailcow で転送設定するなら。次のステップで

  • 一般からSender Transportを追加
  • ドメインから 追加したSender Transportを指定

mailcow にログイン して 右上メニュ → ルーティング → Sender Transport

f:id:takuya_1st:20210318192212p:plain

右上メニュ→メールセットアップ→ドメイン→編集 f:id:takuya_1st:20210318192414p:plain

ドメインが追加されてない場合は追加しておく。

4.posftix でメールボックスを追加

postfix でちゃんと配送設定を書いていれば不要。

mailcow の場合。

mailcow を使うなら設定をする必要がある。

mailcow にログイン → 右上メニュー → mailsetup → 真ん中タブ → mailboxes の順にたどってメアドを追加。 f:id:takuya_1st:20210318192008p:plain

最後に

メールの送受信をチェックして終了。

GoogleSMTP経由で送信されるので、うっかりSPAM判定されるということはまずなさそう。

受信もGoogleSMTP経由から配送されるのでスパムフィルタもそこそこ動いてくれそう。

メールアドレスだけほしいために、Google Driveの容量とかGCPAPI利用可能なアカウントを発行するのは煩雑すぎるしこれでいいや。

google workspace がメールのルーティング機能を提供してくれているとか優しすぎです。Postfixの地獄のセットアップ修行をしなくてもmailcow 入れていたらとりあえず動くのも幸せすぎます。

ただ、gsuite と同じドメインで、独自メアド運用すると、一部のgoogle accounts 管理やログイン連携がちょっと面倒なことになりそうなんですよね。

参考資料

https://support.google.com/a/answer/60730?hl=ja&ref_topic=2921034

https://support.google.com/a/answer/2685650?hl=ja&ref_topic=2921034#zippy=%2Csmtp-%E3%83%AA%E3%83%AC%E3%83%BC-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%A6-gmail-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E7%B5%8C%E7%94%B1%E3%81%A7%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%2C%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E7%B5%8C%E7%94%B1%E3%81%A7%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B

https://support.google.com/a/answer/2956491#zippy=%2Cpostfix