それマグで!

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

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

lxc storage コマンドでストレージプールを指定したフォーマット(btrfs/zfs)で作る

lxc でストレージプールを作る

lxc storage create でストレージ・プールを増やせる。 ためしに、btrfs で増やしてみた。

lxc storage create bt02 btrfs

このコマンドは、オプションを指定しなければ、イメージファイルを作成しそこにプールを作ってくれる。

作成されたファイルを確認する

storage list をみれば、どこからストレージ・プールを持ってきてるかわかる。

lxc storage list

+---------+--------+--------------------------------------------+-------------+---------+
|  NAME   | DRIVER |                   SOURCE                   | DESCRIPTION | USED BY |
+---------+--------+--------------------------------------------+-------------+---------+
| bt01    | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img    |             | 9       |
+---------+--------+--------------------------------------------+-------------+---------+
| bt02    | btrfs  | /var/snap/lxd/common/lxd/disks/bt02.img    |             | 0       |
+---------+--------+--------------------------------------------+-------------+---------+

作成されたイメージファイルの確認

lxc storage info で現在の容量を確認できる。

$ lxc storage info bt02
info:
  description: ""
  driver: btrfs
  name: bt02
  space used: 3.93MB
  total space: 30.00GB
used by: {}

作った容量を拡張する

わたしは、snapcraft された LXDで作ったので、/var/snap/lxd/common/lxd/disks にイメージファイルがある。

/var/snap/lxd/common/lxd/disks/bt02.img

このファイルは、loop バックデバイスに接続してあるので、losetup でループ・デバイスを確認する

$ sudo losetup

NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                                         DIO LOG-SEC
/dev/loop8         0      0         1  0 /var/snap/lxd/common/lxd/disks/bt02.img             0     512

今回は loop8 に接続されていた。

イメージファイルの容量を追加

truncate を使って、-s オプションで増分を指定し、容量を拡張する。

sudo trunacate -s +10G /var/snap/lxd/common/lxd/disks/bt02.img

losetup で容量変更を反映

losetup -c これを忘れたら永遠に反映されない。

sudo losetup -c /dev/loop8

-c は -c, --set-capacity <loopdev> resize the deviceで、ディスクイメージファイルに従ってループデバイス化したブロックデバイスの容量を反映してくれる。

btrfs で容量を最大にする。

最初にマウントしておく mount

sudo mount /dev/loop8 /mnt

つぎに、リサイズをかける。 brtfs filesystem resize で max を指定。

sudo btrfs filesystem resize max /mnt

最後にマウントを解除する

sudo umount /mnt

変更を確認

takuya@m75q-1:~$ lxc storage info bt02
info:
  description: ""
  driver: btrfs
  name: bt02
  space used: 3.93MB
  total space: 40.74GB
  

透過圧縮も用意しておくといいと思います。

zstd, lzo, zlib からアルゴリズムを選び指定します。

lxc storage set default btrfs.mount_options user_subvol_rm_allowed,compress=lzo
lxc storage set default btrfs.mount_options user_subvol_rm_allowed,compress=zstd
lxc storage set default btrfs.mount_options user_subvol_rm_allowed,compress=zlib

lxc のストレージ管理

lxc のストレージ管理は lxc storage コマンドで基本的に解決する。

LVMだとかブロックデバイスを直接だとか別の方法を組み合わせてやるとちょっとめんどくさいのことも起きるけど、truncate でファイルを作成しループバックで使ってる限り、そこまで面倒はお気なさそうだ。欲を言えば、ファイルが可変長なら良かったのに。

lxc で ストレージを変更する。(pool/volume間のlxcストレージを移動)

lxc で ストレージを変更する。(pool/volume間の移動)

lxd を使って、コンテナを起動しているときに、そのコンテナが乗っかってるストレージを切り替えたい。

今回は、起動しているコンテナのストレージを、ボリューム(プール)間で移動して、使ってるボリュームを移動させる

操作の概要

全体の操作の流れ

lxc stop NAME
lxc move NAME NAME-tmp --storage=st02
lxc move NAME-tmp NAME 

以下では、lxdMosaicのストレージを default から bt02 に切り替える。

現在のストレージを確認する。

現在のストレージは次のように確認できる。

$ lxc storage list
+---------+--------+--------------------------------------------+-------------+---------+
|  NAME   | DRIVER |                   SOURCE                   | DESCRIPTION | USED BY |
+---------+--------+--------------------------------------------+-------------+---------+
| bt01    | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img    |             | 2       |
+---------+--------+--------------------------------------------+-------------+---------+
| bt02    | btrfs  | /var/snap/lxd/common/lxd/disks/bt02.img    |             | 0       |
+---------+--------+--------------------------------------------+-------------+---------+
| default | zfs    | /var/snap/lxd/common/lxd/disks/default.img |             | 10      |
+---------+--------+--------------------------------------------+-------------+---------+

今回は、 zfs の default から btrfs で作った bt02 へコンテナを移動させる

default ストレージを使ってるコンテナを確認する。

lxc の storage コマンドで 、show のサブコマンドを利用して、default の状況を見る、

lxc storage show  default
config:
  size: 100GB
  source: /var/snap/lxd/common/lxd/disks/default.img
  zfs.pool_name: default
description: ""
name: default
driver: zfs
used_by:
- /1.0/images/27438783f0b038d0a49ec19c8360903d785c3585654dee8959e9295c5c7615a2
- /1.0/instances/apache-php72
- /1.0/instances/lxdMosaic
- /1.0/instances/nextcloud
- /1.0/instances/ubuntu1804
- /1.0/profiles/default
status: Created
locations:
- none

いくつもの、コンテナがこのストレージ上に展開されていることがわかる。

images はコンテナイメージの格納場所だと思う。

ストレージを動かす方法

起動中のコンテナは、最初に停止しておく。

lxc の stop コマンドに、コンテナ名を指定して停止する。

lxc stop lxdMosaic

lxc の info コマンドで、停止を確認

lxc info lxdMosaic
Name: lxdMosaic
Location: none
Remote: unix://
Architecture: x86_64
Created: 2021/03/24 07:34 UTC
Status: Stopped
Type: container
Profiles: default

ストレージ移動は lxd の移動と同様

ストレージの移動は、lxd の通常移動と同じ様になる。

通常では、サーバー間のコンテナ移動と同様のコマンドになるのだが。 今回は、同一サーバー内での移動になる。

そのため、一時的に別名に移動させ、その際にストレージを指定する

lxc コンテナを一時的な名前で移動(ストレージ変更)

lxc move lxdMosaic lxdMosaic-tmp --storage=bt02

移動は、そこそこ時間かかる。nvme のSSDなのだが、ちょっと時間が必要だったね。

時間がかかるので --verbose を使ったほうがいいかもしれない。と思って --verbose してみたが、進捗は表示されませんでした。経過を見る方法はなさそう。

元の名前に戻す。

lxc move  lxdMosaic-tmp lxdMosaic

zfs→btrfs / btrfs → btrfs の場合、内部的に rsync で移動している模様。

rsync か、そりゃ時間かかるわ

zfs → zfs の場合、zfs send を使って移動してた

どっちにしても、時間はかかる。

lxc move の使い方は次の通り。

lxc move はヘルプを見れば、その使い方としてremote への移動が想定されているようだ。

しかし、今回は、ストレージを移動させたいので、別名として移動させたわけですね。

takuya@m75q-1:~$ lxc help move
Description:
  Move instances within or in between LXD servers

Usage:
  lxc move [<remote>:]<instance>[/<snapshot>] [<remote>:][<instance>[/<snapshot>]] [flags]

Aliases:
  move, mv

Examples:
  lxc move [<remote>:]<source instance> [<remote>:][<destination instance>] [--instance-only]
      Move an instance between two hosts, renaming it if destination name differs.

  lxc move <old name> <new name> [--instance-only]
      Rename a local instance.

  lxc move <instance>/<old snapshot name> <instance>/<new snapshot name>
      Rename a snapshot.

Flags:
  -c, --config           Config key/value to apply to the target instance
  -d, --device           New key/value to apply to a specific device
      --instance-only    Move the instance without its snapshots
      --mode             Transfer mode. One of pull (default), push or relay. (default "pull")
      --no-profiles      Unset all profiles on the target instance
  -p, --profile          Profile to apply to the target instance
      --stateless        Copy a stateful instance stateless
  -s, --storage          Storage pool name
      --target           Cluster member name
      --target-project   Copy to a project different from the source

lxc で消せないコンテナができる

lxc move で移動をしていたら、削除も移動も出来ないコンテナが出来た。再現方法は次の通り

  • lxc move を途中で強制終了する。
  • lxc move でbtrfs を使っている。

こうなってしまうと、だいぶめんどくさい。

これらを消すには。

btrfsで管理しているので、btrfs のイメージを loop デバイスからマウントして、btrfs から削除してしまい、lxc から強引に消すことで解決した

LXC で不要になったストレージを取り外す

LXC で不要になったストレージを取り外す

LXC で不要になったストレージを取り外す

使用中のストレージを一覧する

lxc storage list を使って、いま、ストレージが使われているか確認する。

takuya@lxc-host:~$ lxc storage list
+---------+--------+--------------------------------------------+-------------+---------+
|  NAME   | DRIVER |                   SOURCE                   | DESCRIPTION | USED BY |
+---------+--------+--------------------------------------------+-------------+---------+
| bt01    | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img    |             | 7       |
+---------+--------+--------------------------------------------+-------------+---------+
| bt02    | btrfs  | /var/snap/lxd/common/lxd/disks/bt02.img    |             | 1       |
+---------+--------+--------------------------------------------+-------------+---------+

ストレージを使っているコンテナを探す

lxc storage info で ストレージ情報を確認して、ストレージを使ってるインストール(コンテナ)を表示する

takuya@lxc-host:~$ lxc storage info bt02
info:
  description: ""
  driver: btrfs
  name: bt01
  space used: 18.76GB
  total space: 100.0GB
used by:
  instances:
  - ubuntu1804

削除する

削除できるものは削除する。

lxc stop /delete で削除する。

lxc stop ubuntu1804
lxc delete ubuntu1804

他のストレージへ移動させる。

lxc コマンドの stop / move を使い、利用中のインスタを移動する

lxc stop ubuntu1804
lxc move ubuntu1804 ubuntu1804-tmp --storage=bt01
lxc move ubuntu1804-tmp ubuntu1804

move の引数に --storage=NAME を使えば、ストレージ間での移動が可能。 ただし、移動は同名のインスタンスとして移動できないので、いったん別名に移動し、その後元の名前に戻す。

利用状況を再度確認する。

lxc storage list でストレージの利用状況を再度確認する。 どこからも使われてない、未利用のストレージになっていれば、安全に削除ができる。

USED BY が ゼロになったらストレージを取り外しできる。

takuya@lxc-host:~$ lxc storage list
+---------+--------+--------------------------------------------+-------------+---------+
|  NAME   | DRIVER |                   SOURCE                   | DESCRIPTION | USED BY |
+---------+--------+--------------------------------------------+-------------+---------+
| bt01    | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img    |             | 8       |
+---------+--------+--------------------------------------------+-------------+---------+
| bt02    | btrfs  | /var/snap/lxd/common/lxd/disks/bt02.img    |             | 0       |
+---------+--------+--------------------------------------------+-------------+---------+

ストレージを削除する

lxc storage の delete サブコマンドを使えば、未使用のストレージを削除することができる。

lxc storage delete bt02

実際やってみて

実際に移動をつづけて減らしていったのですが。インスタンスによって移動にとても時間がかかるものや、移動が失敗するものが現れる。

悲劇が起きたのは、lxc のインスタンスをストレージ間で移動中に、PCがスリープし、SSHセッションが切れてしまい、中途半端で移動が停止したときに悲劇が起きた。tmux/screen でうまく回避してやらないとめんどくさい。

移動が途中でおわっても元のインスタンスは残ってるが、移動先のストレージは btrfs/ZFS でサブボリュームが壊れてしまい復旧が面倒だった。

lxc の zfs プールを追加する。

lxc の zfs プールを追加する。

zfs が default だけじゃ使いにくいので。zfs のプールを追加する。

現在のプールを確認

lxc は現在 default と名付けられたZFS プールに紐付いている。

zfs プールを確認

lxc に使ってるストレージとはいえ、zfs であるので zpool コマンドをで確認できる。

zpool で一覧

 zpool list
NAME      SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
default    93G  90.1G  2.94G        -         -    71%    96%  1.00x    ONLINE  -

zpool status で詳細

名前がわかれば、zpool status をすることで詳細を見ることができる。

zpool status
  pool: default
 state: ONLINE
  scan: scrub repaired 0B in 0 days 00:07:12 with 0 errors on Sun May  9 00:31:13 2021
config:

        NAME                                          STATE     READ WRITE CKSUM
        default                                       ONLINE       0     0     0
          /var/snap/lxd/common/lxd/disks/default.img  ONLINE       0     0     0

errors: No known data errors

ZFS プールを作成・削除

lxc といえど、zfs で管理されているものは zfs の方法で作成・削除ができる。

ZFS プールに使うファイルを作成するには、zpool で指定してファイルをzfs に追加できる。

sudo zpool zfs01 /var/snap/lxd/common/lxd/disks/zfs01.img

ZFSのプールを作成して、ファイルをzfs プールに追加する。

sudo zpool create  zfs01 /var/snap/lxd/common/lxd/disks/zfs01.img

作ったプールを消すには

sudo zpool destroy zfs01

lxc と紐付けたものを管理するには

もっとかんたんに lxc storage コマンドで処理できる

lxc のコマンドに、storage create があるので、一発でプールの名前とファイル名を指定したストレージの追加ができる。

lxc storage create zfs01 zfs source=/var/snap/lxd/common/lxd/disks/zfs01.img

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