Majestouch MINILA のキートップを新品に交換する
- 在宅勤務で持ち帰っていた Majestouch MINILA のキートップを新品にしてみた。
- 既に8年選手のキーボードが、新品のようによみがえった。
Majestouch MINILA Air専用 Keycap Set 英語配列用
今回使ったのは、メーカーが展開している交換用キートップ。カラフルなカスタム向けの商品もありますが、今回購入したのはノーマルの新品キャップ。
商品名には「MINILA Air 専用」とあるが、USB版の「MINILA」にも使える。
交換中の様子
キャップを交換した MINILA は、2013年の新発売当時に会社にお願いして買って貰った*1もので、もう8年選手になる。購入後、しばらく別の職場に異動して不在の期間もあったけども、その間もちゃんと保管していてくれた今の職場には感謝している。
新品のように綺麗になった。このモデルは、既に絶版なので大切に使っていきたい。
思ったよりよかったので、プライベートで使っている MINILA も交換しようかしら。でも3台あるから、全部交換するとキャップだけで1万円近くになるという・・・。
*1:なお、プライベートでは予約して購入して既に使っていたので、使い勝手の良さはもう分かっており、会社のカネで冒険したわけではない。
LXC非特権コンテナでIntel CPU内蔵GPUを使ってGPGPUする
- 以前、LXCの非特権コンテナに GeForce をパススルーしてCUDAする記事を書いた
- GeForce がいけるなら、Intel GPU をパススルーして OpenCL もイケるのではと思った
- ちょっとしたハマりポイントがあったけど、同様に可能だったので整理しておく
以前 LXCの非特権コンテナで CUDA する記事を書いた
以前、こんな記事を書いた。
kWatanabeは、主に waifu2x と ffmpeg を使って動画ファイルを 4K 化するために、LXCゲストでCUDAを動かしている。
waifu2xは動画を扱えないので、一度 ffmpeg で bmp にバラしてから1フレームずつ処理させてるんだけども、そうなるとフレーム同士に依存関係がないので、複数のGPUやCPUを混ぜて並列処理させることができる。
これまで、GeForceとCPUで処理をしていたわけだけど、ふとCPU内蔵GPUも同じように使えるのではと思ったので、試してみた。
検証環境
ホストの設定
GPUの特定と有効化
Intel の GPU ドライバは Proxmox VE のカーネルにマージされているので、GeForce のようにドライバを導入する必要はない。
確認する場合は、lspci
の -v
オプションで確認すればよい。まず、PCIのバス番号を調べておいて。
$ lspci | grep VGA 00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06)
バス番号を指定して詳細を確認する。
$ lspci -v -s 00:02.0 00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06) (prog-if 00 [VGA controller]) Subsystem: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller Flags: bus master, fast devsel, latency 0, IRQ 44 Memory at f7400000 (64-bit, non-prefetchable) [size=4M] Memory at e8000000 (64-bit, prefetchable) [size=128M] I/O ports at f000 [size=64] [virtual] Expansion ROM at 000c0000 [disabled] [size=128K] Capabilities: <access denied> Kernel driver in use: i915 Kernel modules: i915
見つからないようなら、GeForce のような dGPU のために iGPU が無効化されていないか、BIOS/UEFI などで確認する。
なお、ここで調べたPCIバス番号 ( 上記の場合は 00:02.0
) は後で必要なのでメモしておく。
GPUをパススルー
GeForce の時と同様に、/dev/dri
配下をゲストに bind マウントする。素のLXC の場合は config ファイル、Proxmox VE の場合は /etc/pve/lxc
配下の設定ファイルに以下を書き加える。
lxc.cgroup.devices.allow: c 226:* rwm lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
そして、次がハマりポイント。
/dev/dri
のファイルは root
ユーザ、video
グループ、render
グループが所有者となっているが、LXCの非特権コンテナの中からは nobody
と nogroup
にリマップされるため、このままで権限不足でアクセスできない。
これを解決するためには以下の2通りの方法がある。
- LXC がゲストに bind するのをフックして所有者を変更する
- アクセス権を
660
から666
に変えてしまう
セキュリティ的にはよくないが、後者の方が簡単。メモしておいた PCI バス番号 ( 今回は 00:02.0
) を頼りにデバイスファイルのアタリをつける。
$ ls -l /dev/dri/by-path lrwxrwxrwx 1 root root 8 2月 7 01:06 pci-0000:00:02.0-card -> ../card0 lrwxrwxrwx 1 root root 13 2月 7 01:06 pci-0000:00:02.0-render -> ../renderD128 ...
$ sudo chmod 666 /dev/dri/card0 /dev/dri/renderD128
ゲストの設定
まずは、デバイスファイルが bind マウントされており、アクセス権があることを確認する。
$ ls -l /dev/dri 合計 0 drwxr-xr-x 2 root root 80 2月 7 01:06 by-path/ crw-rw-rw- 1 nobody nogroup 226, 0 2月 7 01:06 card0 crw-rw-rw- 1 nobody nogroup 226, 128 2月 7 01:06 renderD128
問題なければ、実機と同じようにセットアップする。Intel CPU 内蔵 GPU 用の OpenCL ドライバは beignet
になる。
$ sudo apt install beignet beignet-opencl-icd opencl-headers clinfo
clinfo
で OpenCL デバイスとして認識できていることを確認する。
$ clinfo -l Platform #0: Intel Gen OCL Driver `-- Device #0: Intel(R) HD Graphics Haswell GT2 Desktop
以上で、LXCの非特権ゲストに Intel CPU 内蔵 GPU をパススルーして、OpenCL できるようになる。
おわりに
Azure Spot VM の強制削除イベントをハンドルして安全に終了する
- Azure や AWS には大幅なディスカウントのある Spot VM インスタンスがある
- Azure の Spot VM は、インフラの負荷が高まった際、事前通知のうえ強制終了される
- 事前通知されるイベントを検知する仕組みを作り、安全に終了できるようにした
Azure Spot VM
Azure Virtual Machine には、インフラに余力がある場合にのみ利用でき、従量課金制と比べて大幅にディスカウントされる「Azure Spot VM」というサービスがある。
ディスカウント幅は、インフラ側の都合で動的に決定される。Microsoft は最大で90% ディスカウントすると主張している。 一方、インフラの余力が無くなった場合には、30秒前に事前通知したうえで、強制的にシャットダウンするとしている。
また、ユーザから「ここまでディスカウントしてくれるなら使ってもいい。そうでないなら、使うのを止めさせてもらう。」といった宣言も可能で、その場合、インフラの余力がまだ残っていても、価格がユーザ指定値以上になった際にシャットダウンされるようになる。
Azure Metadata Service の Scheduled Events
Spot VM に限らず Azure Virtual Machine には、保守のための再デプロイ(ライブマイグレーション)や再起動など、データがロストする可能性があるイベントが予定された際に、それを VM の内部から検知できる仕組みがある。
以下の様に VM の内部からエンドポイントにアクセスすると、予定されているイベントがあれば JSON で取得できる。
$ curl -H "MetaData:true" "http://169.254.169.254/metadata/scheduledevents?api-version=2019-08-01" { "DocumentIncarnation": {IncarnationID}, "Events": [ { "EventId": ce5f797a-7369-11eb-ba65-a706ed727702, "EventType": "Preempt", "ResourceType": "VirtualMachine", "Resources": ["SpotVM_TEST"], "EventStatus": "Scheduled", "NotBefore": Mon, 15 Feb, 2021 18:29:47 GMT, "Description": "", "EventSource" : "Platform", } ] }
もし、Spot VM がインフラ側の余力不足などで強制削除される場合、その30秒前に EventType
が Preempt
なイベントが通知される。また、Preempt
以外にも以下のイベントが発生する可能性がある。
EventType | 概要 | ロストするデータ |
---|---|---|
Freeze | 一時停止が予定されている。約15分前に発生 | 特になし |
Reboot | 再起動が予定されている。約15分前に発生 | オンメモリ |
Redeploy | ライブマイグレーションが予定されている。約10分前に発生 | 一時ストレージ |
Preempt | Spot VMの削除が予定されている。約30秒前に発生 | すべて |
Terminate | ユーザによる削除が予定されている。(発生タイミングは設定による) | すべて |
エンドポイントをポーリングして、もし、これらのイベントを検知すれば、動かしているプログラムを安全に終了させたり、必要なデータを退避するなどの対応が採れる。
検証環境
検証
Azure Spot VM を作る
具体的な作り方は割愛。閾値を現在価格以下にしてしまうと、そもそも起動できないので、1円上で指定してみた。
検知スクリプトを書く
やっつけだけど、こういうのを書いてみた。とりあえずイベントを検知したら、wall
で警告を挙げて、プロセスを pkill
するだけのシンプルなもの。
MITライセンスで。
結果
まだ、発動していないので、絶賛放置中。かなり渋い価格にしたので直ぐ発動すると思ったんだけど。それとも何かミスったか。
まとめ
nftablesでシンプルなステートフルファイアウォールを作る
- 自宅の DMZ を構成するために nftables でステートフルファイアウォールを作った
- 長年、iptables を使ってきたが Debian 11 リリースに備え、移行することにした
- 単純なファイアウォールを構成するための最低限の設定をまとめておく
iptables から nftables への移行
Debian 11 (bullseye) の Freeze がはじまったので、自宅サーバ移行検証をしていたところ、ちょっとファイアウォールが気になった。
Debian では、10 (buster) で既に nftables がデフォルトになっており、iptables は nftables の互換レイヤーとして機能している。
今のところ、bullseye でも iptables が無くなるという話はないので、もう1回引っ張っても問題は無いのだが、nftables がデフォルトになった次のバージョンで移行というのはタイミングとしても良いので、ここで移行することにした。
検証環境
想定する構成
目指すはシンプルな2ファイアウォール型のDMZを構成すること。抽象化したモデルとしては以下のような形になる。
なお、我が家はいわゆる「逸般の誤家庭」ではないので、Y社 や C社 のエンタープライズ向けルーターなんてものはない*1。 そのため、実態としては以下のような形になる。
この図の「Linux server」が今回のターゲットになる。
簡単のため、以下 DMZ 側の NIC を eth-dmz
、Trust Zone 側の NIC を eth-trust
と記載する。目指すルールは以下。
- Trust Zone から DMZ への通信は全て許可
- DMZ から Trust Zone へのセッション確立済みの通信を許可
- DMZ から Trust Zone への特定のマシンから、特定のマシンの特定のポートへの通信を許可
nftables で作るステートフルファイアウォール
IP forward の有効化
まずは、eth-dmz
と eth-trust
間のパケット転送を有効にする。ここは nftables に限った話じゃないので詳細は触れない。
$ sudo vim /etc/sysctl.conf #net.ipv4.ip_forward=1 ↓ net.ipv4.ip_forward=1
適用。
$ sudo sysctl -p
nftables の導入
Debian buster および bullseye では、iptables と nftables の両方が提供されているため、明示的に nftables を導入する。
なお、パッケージは nftables のみでよい。iptables と iptables-persistent のように、管理インタフェースと永続化のための仕組みで分かれていたりはしない。
$ sudo apt install nftables
nftables のルールを書く
nftables は、全ての操作が nft
コマンドで完結する。
iptables と同様、テーブル、チェイン、ルールの概念がある。ただし、デフォルトのテーブルやチェインは無い。そのため「nftables はなにもしない」状態から設定を作り込んでいく*2。
大まかな流れは以下のようになる。
なお、<>
はパラメータ、[]
は省略可能な引数を指す。
1. 設定済みのルールをクリア
$ sudo nft flush ruleset
2. テーブルを作成
$ sudo nft add table [<アドレスファミリ>] <テーブル名>
アドレスファミリには以下が設定できる。
IP層でのステートフルファイアウォールでは inet
を使えば事足りる。具体例は以下。
$ sudo nft add table inet FIREWALL
3. テーブルにベースチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <ベースチェイン名> \ { type <タイプ> hook <フック> [device <デバイス>] priority <優先度>; [policy <ポリシ> ;] }
ベースチェインは、初めに呼び出されるチェイン。Cでいう main()
みたいなもの*3。
「タイプ」は、チェインの用途を指定する。
タイプ | 用途とアドレスファミリ | 利用できるフック |
---|---|---|
filter |
汎用 (全ファミリ) | 全て |
nat |
NAT (ip , ip6 ) |
prerouting , input , output , postrouting |
route |
ルーティング (ip , ip6 ) |
output |
「フック」は、チェインの実行タイミングを指定する。
フック | タイミング | 利用できるアドレスファミリ |
---|---|---|
ingress |
デバイスがパケットを受信した際 | netdev |
prerouting |
パケット受信後、ルーティング判定の前 | ip , ip6 , inet , bridge |
input |
ルーティング判定後、宛先が自分自身と判断した際 | ip , ip6 , inet , bridge , arp |
forward |
ルーティング判定後、宛先が他ホストと判断した際 | ip , ip6 , inet , bridge |
output |
自分自身が他ホスト宛のパケットを生成した際 | ip , ip6 , inet , bridge |
postrouting |
他ホストへパケットを送信する直前 | ip , ip6 , inet , bridge |
「プライオリティ」は、同じフックが指定されたベースチェインが複数ある場合の優先度を指定する。値が小さいほど、先に実行される。
「ポリシ」は、チェインの中で取り扱いが決まらなかった際の取り扱いを accept
か drop
で指定する。未指定だと accept
になる。
具体例は以下の通り。
$ sudo nft add chain inet FIREWALL FILTER { type filter hook prerouting priority 0 \; }
type filter
(汎用) でhook prerouting
(ルーティング判定前) にpriority 0
(最優先) で処理するベースチェインで、- 合致するルールが無い場合は受け入れる
policy を accept
もしくは省略した場合は、確実に不要なパケットを落とすルールを入れることを忘れてはいけない。
4. テーブルにレギュラーチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <レギュラーチェイン名>
レギュラーチェインは、他のチェインから明示的に呼び出されるチェイン。呼び出し元のチェインの設定を引き継ぐため、チェイン名の登録のみ。具体例は割愛。
5. チェインにルールを登録
$ sudo nft add rule [<アドレスファミリ>] <テーブル名> <チェイン名> <ステートメント>
当該チェインが呼び出された際に、適用するルールを実行順に登録する。
「ステートメント」は、具体的な制御内容を記載するが、網羅すると膨大な数になるため参考ページを記載し、ここでは具体例の記述にとどめる。
「確立済みの通信を許可する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ ct state related,established accept
ct
(コネクション) のstate
(状態) がrelated
かestablished
なものをaccept
(許可) する。
「特定のマシンから、特定のマシンの特定のポートへのアクセスを許可する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport 443 accept
ip
(IPv4ヘッダ) のsaddr
(送信元IPアドレス) が10.0.0.100
であってip
(IPv4ヘッダ) のdaddr
(送信先IPアドレス) が172.16.0.100
であってtcp
(TCPヘッダ) のdport
(接続先ポート番号) が443
のものをaccept
(許可) する。
「(先のステートメントから漏れたパケットで、)特定のNICから入力された全てのパケットを破棄する」ための設定例は以下になる。
$ sudo nft add rule inet FIREWALL FILTER \ meta iifname eth-dmz drop
meta
(その他のメタデータ) のiifname
(入力インターフェース名) がeth-dmz
のものをdrop
(破棄) する
設定済みのルールを確認
$ sudo nft list ruleset
上記のコマンドにて、現在の設定を出力できる。出力結果の具体例は以下になる。
table inet FIREWALL { chain FILTER { type filter hook prerouting priority 0; policy accept; ct state established,related accept ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport https accept iifname "eth-dmz" drop } }
ルールの永続化
list ruleset
の出力は、そのまま nftables の設定ファイルとして用いることができる。
Debian の場合、nftables
サービスが起動する際に /etc/nftables.conf
があればそれをロードする。そのため、以下のようにすることで設定を永続化できる。
$ sudo nft list ruleset | sudo tee /etc/nftables.conf $ sudo systemctl enable nftables $ sudo systemctl restart nftables
なお、手動で設定ファイルをロードしたい場合は以下のようにする。
$ sudo nft -f <ファイル名>
まとめ
最後に、上記のような設定を一発で行うためのスクリプトを作ったので公開しておく。
ライセンスは、MITライセンスで。
所感
RHEL互換ディストリの作り方を手探ってみる2(大晦日ハッカソン)
- 2020年の大晦日に大晦日ハッカソンなるイベントに参加した
- CentOS のようなRHEL互換ディストリの作り方を1日で出来る範囲で手探ってみた
- 1日でできる限られた範囲だけど、得られた知見をここで整理する
はじめに
今回は、そのRHEL*1互換ディストリビューションを自製するためには、どうすればいいのかを手探ってみることにしました。
RHEL互換ディストリビューションとは
過去記事にも書きましたが、RHEL互換ディストリビューションとは、商用ディストリビューションである RHEL から、RedHatの商標や独占的な権限を有するものを削除し、オープンソースライセンスの下に公開されているもののみで再構成したものです。*2
RHEL互換ディストリビューションを作成するための大まかな手順は以下になります。
- RHEL のサブスクリプションを得る
- RHEL のリビルド環境を用意する
- RHEL のソースコード (SRPM) を入手
- ソースコードの全てから、RedHat に関わる商標やその他権利を有するモノを仕分け・排除
- 上記の結果、RHELとの互換性が崩れてしまうものがあれば、それを補填するモノを開発
- リビルドする
以下、実際に大晦日ハッカソンで、kWatanabe が取り組んだ内容を整理していきます。
てさぐれ!RHELもの
RHELのサブスクリプションを得る
RHEL のサブスクリプションを得るには以下の方法があります。
また、RHEL のサブスクリプションは、用途や導入するマシンの種別に応じて、多種多様なものがあります。詳細はサブスクリプションガイドに記載があります。
Red Hat Enterprise Linux Developer Suite
以外のサブスクリプションは、RedHatもしくはリセラーから調達する必要があります。Red Hat Enterprise Linux Developer Suite
は、開発用途専用で、サポートもありませんが、Red Hat Developers にエントリすることで無償で入手できます。今回はこのサブスクリプションを利用しました*3。
なお、Red Hat Developers には以下からエントリできます。
RHEL のリビルド環境を用意する
バージョン齟齬や変な依存関係で悩みたくなかったため、リビルド対象の RHEL 8.3 をそのまま実機にインストールして準備しました。
RedHatカスタマーポータル*4にログインすれば、RHEL のインストールISOを入手できます。9GBを超え、DVD-R DLでも焼けないサイズのため、BalenaEtcher *5 を使って16GBの USB メモリに焼きました。
特に何をやるか決めてないけど。 #大晦日ハッカソン に向けて、余っているマシンに Red Hat Enterprise Linux 8 をインストール。 pic.twitter.com/CDGGADInq0
— kWatanabe (@WWatchin) 2020年12月29日
インストールが終われば、サブスクリプションを認証し、リビルドに必要なパッケージを導入します。この時、CodeReady Linux Builder リポジトリ*6を一緒に有効化しました。
$ sudo subscription-manager register $ sudo subscription-manager attach $ sudo subscription-manager repos --enable codeready-builder-for-rhel-8-x86_64-rpms $ sudo dnf update $ sudo dnf groupinstall "Development Tools" $ sudo dnf install rpm-utils rpm-build texinfo
RHEL のソースコード (SRPM) を入手
ソースコードもまた、RedHatカスタマーポータルから ISO で入手できます。ISO に含まれるものは BaseOSリポジトリとAppStreamリポジトリのみですが、それでも 20GB ほどあります。
これをマシンにコピーし、loop マウントしておきます。
$ sudo mkdir /media/rhel-source $ sudo vim /etc/fstab /home/kwatanabe/rhel-8.3-source-dvd.iso /media/rhel-source iso9660 ro,nofail,loop 0 0 $ sudo mount -a
なお、通常は不要ですが、手探るうえで必要になり CentOS のソースも入手しました。 CentOS は ソース ISOを配布していないため、rsync でリポジトリをミラーしました。
$ sudo mkdir -p /media/centos-source/ $ sudo rsync -avzP rsync://vault.centos.org/8.3.2011/BaseOS /media/centos-source/
RedHat に関わる商標やその他権利を有するモノを仕分け・排除
RHELとCentOSを比較
環境が整えば、実際に手探っていきます。全パッケージを対象にすると、プロジェクトが立ち上がる位の作業量になってしまうので、BaseOS リポジトリに絞って作業します。
まずは RHEL にあって、CentOS にないものを探しました。もちろん、これはチートです。本来は、削除しないとダメなモノを全部自力で調べなければなりません。
CentOSのリポジトリには過去バージョンも含まれていますが、それ以外で RHEL にあって、CentOS にない SRPM は以下の通りでした。
- libica
- libzfcphbaapi
- memstrack
- openssl-ibmca
- qclib
- redhat-indexhtml
- redhat-logos
- redhat-release
- s390utils
諸元から削除された理由を調べる
アタリが付いたら、rpm コマンドを使って中身を調べていきます。まずは、rpm -p -qi
を使って諸元を調べました。例えば、libica
だと以下の結果となります。
$ rpm -p -qi libica-3.7.0-2.el8.src.rpm Name : libica Version : 3.7.0 Release : 2.el8 Architecture: s390x Install Date: (not installed) Group : System Environment/Libraries Size : 552759 License : CPL Signature : RSA/SHA256, 2020年07月21日 17時52分31秒, Key ID 199e2f91fd431d51 Source RPM : (none) Build Date : 2020年07月20日 21時56分29秒 Build Host : s390-046.build.eng.bos.redhat.com Relocations : (not relocatable) Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> Vendor : Red Hat, Inc. URL : https://github.com/opencryptoki/ Summary : Library for accessing ICA hardware crypto on IBM z Systems Description : A library of functions and utilities for accessing ICA hardware crypto on IBM z Systems.
Architecture
や、Description
の内容から、これは IBM z 向けのパッケージと分かります。CentOS は IBM z をサポートしないため、含まれていないようです。同様に、libzfcphbaapi、openssl-ibmca、qclib、s390utilsもアーキテクチャ違いによるものでした。
中身からCentOSで削除された理由を調べる
x86_64向けやアーキテクチャ非依存にもかかわらず CentOS に含まれていないものは、RedHatの商用や権利関係で削除された可能性があります。rpm -ivh
でSRPMを展開し、rpmbuild -bp
でパッチ群をマージして、実物を確認します。
$ rpm -ivh redhat-indexhtml $ rpmbuild -bp ~/rpmbuild/SPECS/redhat-indexhtml.spec $ ls ~/rpmbuild/BUILD/redhat-indexhtml
確認の結果、redhat-indexhtml、redhat-logos は RedHat の商標やロゴが含まれており、NGなものでした。redhat-release は、RHEL のリポジトリ登録のためのパッケージで RHEL互換ディストリビューション では置き換えが必要なものです。
一方、memstrack は問題があるパッケージには見えませんでした。また、大元のソースコードも GitHub で公開されています。
CentOS のリポジトリを見直すと、 BaseOS リポジトリから AppStream リポジトリに移動されていました。理由は不明ですが、削除しなければならないパッケージではなさそうです。
これらの結果から、RHEL 8.3 の BaseOS リポジトリでは以下のものを削除し、オリジナルのものに置き換える必要があると分かりました。
パッケージ | 概要 |
---|---|
redhat-indexhtml | RHELのオフラインマニュアル |
redhat-logos | RHELのロゴ・商標の画像集 |
redhat-release | RHELのリポジトリ情報 |
互換性が崩れてしまうものがあれば補填するモノを開発
抽出したモノのうち、redhat-logosとredhat-releaseは代替となる画像データや、リポジトリを用意しないとディストリビューションとして完結しなくなりそうです。
redhat-indexhtmlは、無くても良い気がしますが、未確認です。
リビルド
だいたいの流れ
リビルド’作業そのものは、通常のRPMのリビルドと大きな差はありません。dnf builddep
で依存パッケージを導入して、RPMをビルドしていきます。
しかし、大きな注意点があります。RHELとの互換性を担保するために、RedHatがビルドに使ったライブラリと同じものを使わなければなりません。これはバージョンや適用されているパッチも完全に一致しなければダメです。
REHLのリポジトリに依存パッケージがあればいいですが、無い場合何が使われているのかを調査し、環境を整える必要があります。ここが、RHEL互換ディストリビューション構築で重要かつ困難な所になります。
依存パッケージを調べる
私は以下の流れで作業を進めました。
dnf builddep
を試す → 何事も無く完了したら、rpmbuild -ba
でビルドする- RedHatのマニュアル「RHEL 8 の導入における検討事項」で、RHEL8.0~8.3で統廃合されたパッケージに該当しないか調べる
- FedoraやEPELなど、RHELの upstream に含まれていないか調べる
- RPM pbone.net などの RPM 検索サイトなどから調べる
- Google 検索する
結果としては、大きく以下の4種類(AND/OR含む)となりました。
もしかしたら、RedHat はビルド環境を RHEL ではなく、upstream の Fedora をベースに構築しているのかもしれません。なお、具体的なパッケージ名は当日ツイートした以下の通りです。
#大晦日ハッカソン の成果
— kWatanabe (@WWatchin) 2020年12月31日
RHELクローンを目指して、BaseOSの全リビルドを試みた。素のRHEL8(BaseOS+AppStream+CodeReady)でビルドできないものが57。うち、EPELでビルドが通ったのが6。
残りは、Fedora、RHEL7のrepoで解決できそうだが、RHELの構成を逸脱するのでうまくしないと互換性が壊れそう。 pic.twitter.com/1myqMUSxuM
なお、調査に使ったリソースは以下からたどれます。
ここで終了・おわりに
ここまで絞り込めれば、ビルドが通らなかったパッケージで使われているライブラリのバージョンを調べ、ビルドできるように進めていくところですが、ここで2020年が終わりました。
真に難しいところの1歩手前で時間切れとなりましたが、それでも CentOS という先人の成果をフル活用してもかなりパワーを使ったので、世のRHEL互換ディストリビューションの構築がいかに大変かがうかがい知れます。
それでは。
*2:単なる再構成ではなく、RHEL とソフトウェア的な互換性が担保されている必要があります。RHEL はエンタープライズなディストリビューションとしてデファクトに近い存在であるので、RHEL と同じ機能・品質を持ち、かつ特定の組織にロックインされないディストリビューションを求める人が大勢いるのです。
*3:RHEL互換ディストリビューションの作成が「開発用途」に含まれるかどうかは不明。限りなく黒に近いグレーな気がしますので、このライセンスで用達したソースを元に再構築したディストリビューションの再配布は止めた方がいいとおもいます。
*5:https://www.balena.io/etcher/
*6:ドライバやパッケージのビルド環境など、開発用途のツールキットを収録した追加リポジトリ。CentOSでいうところの PowerTools に相当。
RHEL互換ディストリの作り方を手探ってみる1(大晦日ハッカソン)
- 2020年はSTAY HOMEのため、帰省できないため何かやりたかった
- ひとりでもくもく会をやってもいいのだが、折角だから大晦日ハッカソンに参加してみる
- 自己啓発として、RHEL互換(かも知れない)ディストリの作り方を手探ってみることにした
大晦日ハッカソン
2012年より、毎年大晦日に開催されているイベントっぽい何かです。
omisoka-hackathon.connpass.com
内容としては、
以上。
2020年は、コロナ渦対策のSTAY HOMEで、旅行も帰省もできないので。ちょっと参加してみようと思ったのです。
何をやるか
とはいいつつも、何かをこれまで作ってきたわけでもなく、何か作りたいものがあるわけでもない。1日で材料から集めてモノを作っても、中途半端になるに決まってる。
そこで、「何かモノを作るのではなく、知見をためよう。知見であれば中間報告であっても、ブログで公開するなり、会社の勉強会で語るなりすれば、それはれっきとした成果物になるだろう。」と言い訳をして、手を動かして何かを手探っていこうと思った次第です。
ネタ探し
1ヶ月前のとある出来事
ところで、1月ほど前にある出来事があり、業界でちょっとした騒動になりました。
超意訳すると、RHEL*1という商用Linuxディストリビューションのクローンとして無償公開されていた CentOS が、突如2021年に開発終了するとアナウンスされたものです*2。
元々、CentOS はクローン元の RHEL と同様に2028年まで開発されると予想されていたので、これに期待して構築されたシステムは RHEL の購入、 Oracle Linux などの他のクローンへの移行、Debian などの他のディストリへの移行のいずれかを強いられることになりました。
RHELクローン(互換ディストリビューション)とは
そもそも、RHELクローンとは何でしょうか。
RHELは商用のLinuxディストリビューションですが、製品の多くはオープンソースプロジェクトの成果物のうえに成り立っています。そのため、RHEL はそれ自身もオープンソースとして公開されています*3。
そして、RHEL の EULA*4には、以下の記載があります。
1. License Grant. Subject to the following terms, Red Hat, Inc. ("Red Hat") grants to you a perpetual, worldwide license to the Programs (most of which include multiple software components) pursuant to the GNU General Public License v.2. The license agreement for each software component is located in the software component's source code and permits you to run, copy, modify, and redistribute the software component (subject to certain obligations in some cases), both in source code and binary code forms, with the exception of (a) certain binary only firmware components and (b) the images identified in Section 2 below. The license rights for the binary only firmware components are located with the components themselves. This EULA pertains solely to the Programs and does not limit your rights under, or grant you rights that supersede, the license terms of any particular component. 2. Intellectual Property Rights. The Programs and each of their components are owned by Red Hat and other licensors and are protected under copyright law and under other laws as applicable. Title to the Programs and any component, or to any copy, modification, or merged portion shall remain with Red Hat and other licensors, subject to the applicable license. The "Red Hat" trademark and the "Shadowman" logo are registered trademarks of Red Hat in the U.S. and other countries. This EULA does not permit you to distribute the Programs or their components using Red Hat's trademarks, regardless of whether the copy has been modified. You may make a commercial redistribution of the Programs only if (a) permitted under a separate written agreement with Red Hat authorizing such commercial redistribution, or (b) you remove and replace all occurrences of Red Hat trademarks. Modifications to the software may corrupt the Programs. You should read the information found at http://www.redhat.com/about/corporate/trademark/ before distributing a copy of the Programs.
要するに、オープンソースライセンスの下に公開されているソースコードは、RedHatの商標やロゴ、その他独占的な権限を有するものを全て排除した場合に限り、そのライセンスに抵触しない範囲で再利用して良いことになっています。
そこで、RHEL のソースコードから RedHat が権利を有するものを削除し、再配布されている Linux ディストリビューションがあります。それが、RHEL クローンと呼ばれるものです。
過去には様々な RHEL クローンがありましたが、2020年現在、プロダクトに耐えうるものは Oracle Linux と Asianux です*5。Asianux は有償のため、無償のものは Oracle Linux のみです。なお、このアナウンスを受けて、CentOS に代わる新たな RHEL クローンを作ろうというプロジェクトがいくつか立ち上がっています。
RHELクローンの作り方
さて、やっと本題になりました。大晦日ハッカソンでは、この RHELクローンの作り方を手探っていくことにします。
大まかには以下の流れになるはずです。
- RHEL のサブスクリプションを得る
- RHEL のソースコード (SRPM) を入手
- ソースコードの全てから、RedHat に関わる商標やその他権利を有するモノを仕分け・排除
- 上記の結果、動かなくなってしまうものがあれば、それを補填するモノを開発
- リビルド
SRPMを入手してビルドするだけなら、RHEL を開発で使ったことがある人なら難なくできる作業です。しかし、RedHat が権利を持つものを削除したうえで、RedHatの権利を侵害すること無く同等の機能を補填し、RHEL と同じ動きをするものを作るとなると、簡単にはいきません。
RHEL クローンは、莫大な資金と人的リソースを費やして開発されているものなので、大晦日にちょっと手を出したくらいで何か形になるとは思いません。ひとつでも知見を得られれば、それで成功だと思っています*6。
おわりに
- 2020年の大晦日ハッカソンでは、RHEL クローン(RHEL 互換ディストリビューション)の作り方を手探っていく
- 目標は、何らかの知見を得ることで、RHEL クローンを作ることでは無い(そもそも無理)
- 得られた知見は、別途整理して、何らかの形でまとめる
*2:より正確には、RHEL のクローンとしての CentOS が開発終了。RHEL のアップストリームとしての CentOS Stream は継続される。
*3: RHEL のサブスクリプションを持っている人には全てのソースコードを詰め込んだ 20GB強もの ISO ファイルがダウンロードできる
*4:End User License Agreement。https://www.redhat.com/ja/about/agreements
*5: ただし、汎用品として。他にも Red Hawk Linux などの特定用途に特化した製品はあります。
*6:そもそも環境の構築方法を調べるだけで1日終わるかもしれないし…
Proxmox VEのLXCでホストにあるISOをマウントする
- Proxmox VEのLXCゲストは、ホストに保存されているISOファイルをマウントできない
- メディアからaptやyumをしたい場合など、ニッチだけど地味に困る
- ホストに保存されているISOの中身を、LXCゲストに見せる方法を整理する
検証環境
- Proxmox VE 6.3
Proxmox VEでは、LXCゲストでISOをマウントできない
Proxmox VEは、KVMゲストにISOファイルをマウントさせることはできるが、LXCゲストではWebインタフェースに項目が無く、マウントさせることはできない。
LXCでは、ISOからOSをインストールする必要が無いため、割り切った仕様にしているのだとは思うが、DVDメディアやISOファイルをリポジトリとして yum や apt を使いたい場合に困る*1。
ダメな例1
Webインタフェースで設定する代わりに、以下のように/etc/pve/lxc/<VMID>.conf
に設定を書き込んでも、エラーになりゲストが起動しなくなる。
mp0: /path/to/isofile,mp=/path/to/mountpoint,ro=1 ★ダメな例1★
ダメな例2
Porxmox VE のLXCゲストの設定ファイルには、通常のLXCのオプションをそのまま書くこともできるので、以下のようにloop
マウントをやりたくなるが、これもうまくいかない。なお、bind
マウントはできる。
lxc.mount.entry = /path/to/isofile path/to/mountpoint iso9660 loop,nofail,ro 0 0 ★ダメな例2★
ISOファイルをLXCゲストに送り込んで、ゲストの中からloop
マウントする方法も考えられるが、これは Permission Denied
になる。回避するためには、apparmorの設定を緩めたうえで、特権コンテナにし、さらにケーパビリティを調整しないといけない(ケーパビリティに関しては過去記事参照)。
素のLXCならともかく、Proxmox VE のようなプラットフォームでホストの設定を変えると、不具合の原因になりかねないので避けたい。
解決策
「ダメな例2」でしれっと書いたが、bind
マウントはできる。そこで、力業だけどもホストでloop
マウントして、そのディレクトリをbind
マウントして投影すればよい。
なお、loop
マウントのマウントポイントをbind
マウントのマウント元に指定しないと、ISOファイルの中身は見えない。横着して、複数のマウントポイントを集めた親ディレクトリをbind
マウントしてもISOの中は見えない。
最終的にはホストで以下のようにする。
- ISOファイルを
loop
マウント
$ sudo vim /etc/fstab /path/to/isofile /path/to/mountpoint-host iso9660 ro,loop,nofail 0 0
loop
マウントしたディレクトリをbind
マウント
$ sudo vim /etc/pve/lxc/<VMID>.conf lxc.mount.entry = /path/to/mountpoint-host path/to/mountpoint-guest none ro,bind,nofail 0 0
これで、ISOファイルの中身がゲストでも中身が見えるようになる。なお、Readonlyとはいえど、ホストとゲストの間で穴を開けることになるので、セキュリティ面では大変よろしくない。使い終わったら、さっさと元に戻すのが吉。
おわりに
- Proxmox VEでは、LXCゲストにISOファイルをマウントする手段を提供していない
- ゲストの中からマウントするには、セキュリティ設定をかなり調整しないとダメ
- ホストで
loop
マウントしてから、bind
マウントすれば間接的にISOファイルをマウントできる - セキュリティ面では大変宜しくないため、使い終わったら元に戻すこと
aptリポジトリにおける優先度のルールと確認方法
- apt には、リポジトリやパッケージが競合する場合に、自動選択する仕組みがある
- これは優先度に基づいて行われるが、デフォルトの優先度は状況によって異なり「リポジトリを登録したのに使われていない」なんてことがまれに生じる
- apt のリポジトリ優先度の考え方と、設定の確認・変更方法をまとめる
検証環境
apt における優先度付けのルール
apt の優先度は静的な順序付けではない
実は apt リポジトリの優先度は、静的に順序付けされるものではない。ユーザが設定ファイルで指定しない限り、優先度は動的に設定される。また、優先度の絶対値にも意味がある。
apt_preferences
のmanページを見れば、具体的なルールが記載されている。
デフォルトの優先度
特に指定のない場合、apt は以下の優先度を付与する。
優先度 | 諸元 |
---|---|
1 | Releaseファイルに NotAutomatic: yes が指定されているが、ButAutomaticUpgrades: yes が指定されていないリポジトリのパッケージ |
100 | (1) Releaseファイルに NotAutomatic: yes と ButAutomaticUpgrades: yes が指定されているリポジトリのパッケージ(2) 既にシステムにインストール済みのパッケージ |
500 | 他の条件に合致していないパッケージ |
990 | ターゲットリリースとして指定されたリポジトリのパッケージ |
なお、ターゲットリリース
とは、/etc/apt/apt.conf
でAPT::Default-Release
に指定しているリリースか、コマンド実行時に -t
オプションで指定したリリースを指す。
絶対値に応じた制御
apt は、リポジトリ同士の相対的な比較とは独立して、絶対値による評価も行う。
ただし、デフォルトの優先度でこれらの値になることはない。ユーザが /etc/apt/preferences
で明示的に優先度を設定して、パッケージやリポジトリのブラックリスト化や優先付けができるようになっている。
優先度を手動で設定する方法
リポジトリの優先度をaptに決めさせるのでなく、手動で設定したい場合は /etc/apt/preferences
に設定を追記する。
なお、優先度はリポジトリではなくパッケージごとに設定できる。
Package: <パッケージ> Pin: release a=<リリース名> Pin-Priority: <優先度の値>
例えば、testing
リポジトリを experimental
リポジトリと同様に、「明示的にターゲットリリースにしない限り、インストールもアップグレードもされないようにする」には、以下のようにする。
Package: * Pin: release a=testing Pin-Priority: 1
また、一時的にターゲットリリースとして設定するには、-t
オプションを指定する。
$ sudo apt install -t testing ...
恒久的にターゲットリリースとして設定するには、/etc/apt/apt.conf
にAPT::Default-Release
を設定する。
APT::Default-Release "testing";
要するに
これらのルールに基づいて整理すると、以下のようになる。
リポジトリの優先度 | 指定方法 | 挙動 |
---|---|---|
1000~ | /etc/apt/preferences |
ダウングレードであっても採用する |
991~999 | /etc/apt/preferences |
ダウングレードで無い限り、ターゲットリリースにパッケージがあっても、こちらを採用する |
990 | -t オプション /etc/apt/apt.conf |
ターゲットリリース。明示的に指定した際に採用する |
501~989 | /etc/apt/preferences |
ターゲットリリースを指定したものの、目的のパッケージが無い場合に採用する |
500 | 自動 | 一般的なリポジトリのデフォルト値 |
101~499 | /etc/apt/preferences |
他のリポジトリに目的のパッケージが無い場合に採用する |
100 | 自動 | インストール済みのパッケージ |
1~99 | /etc/apt/preferences 1 は自動 |
他のリポジトリに存在せず、また、まだインストールされていない場合(アップグレードでない場合)に限り採用する |
0 | 使用禁止 | 未定義動作 |
負値 | /etc/apt/preferences |
いかなる場合でも採用しない |
設定の確認方法
特に何も指定しない場合
現状の設定は apt-cache policy
コマンドで確認できる。
$ apt policy Package files: 100 /var/lib/dpkg/status release a=now 1 http://deb.debian.org/debian experimental/main amd64 Packages release o=Debian,a=experimental,n=experimental,l=Debian,c=main,b=amd64 origin deb.debian.org 100 http://deb.debian.org/debian buster-backports/main amd64 Packages release o=Debian Backports,a=buster-backports,n=buster-backports,l=Debian Backports,c=main,b=amd64 origin deb.debian.org 500 http://ftp.debian.org/debian buster/main amd64 Packages release v=10.6,o=Debian,a=stable,n=buster,l=Debian,c=main,b=amd64 origin ftp.debian.org ...(略)...
この環境の例では、以下の設定となっていることが分かる。
- 不安定な
explerimental
リポジトリは、インストール時に-t experimental
と明示的に指定しない限り、過去に導入済みであったとしても、自動では導入・更新されない。 - 将来バージョンからのバックポートである
buster-backports
は、安定版リポジトリと競合するため、インストール時に-t buster-backports
と明示的に指定しないと導入されないが、一度導入した後は自動更新の対象とされる。 - 標準の安定版リポジトリは、他のリポジトリがターゲットリリースとして指定されない限り、これを使う。
なお、experimental
リポジトリについては以下の過去記事参照。
kwatanabe.hatenablog.jp
ターゲットリリースを明示的に指定した場合
ここで、-t experimental
を付けて、明示的にexperimental
リポジトリをターゲットリリースにしてみる。
$ apt policy -t experimental Package files: 100 /var/lib/dpkg/status release a=now 990 http://deb.debian.org/debian experimental/main amd64 Packages release o=Debian,a=experimental,n=experimental,l=Debian,c=main,b=amd64 origin deb.debian.org 100 http://deb.debian.org/debian buster-backports/main amd64 Packages release o=Debian Backports,a=buster-backports,n=buster-backports,l=Debian Backports,c=main,b=amd64 origin deb.debian.org 500 http://ftp.debian.org/debian buster/main amd64 Packages release v=10.6,o=Debian,a=stable,n=buster,l=Debian,c=main,b=amd64 origin ftp.debian.org ...(略)...
experimental
リポジトリの優先度が990
になっていることが分かる。
まとめ
LXCコンテナの中から、chronydでホストの時刻を補正・配信する
- LXCの標準設定ではセキュリティ上の理由から chrony が動かせないようになっている
- コンテナはホストの時刻を使うため、NTPクライアント(chronyc)は必要はないが、NTPサーバ(chronyd)をコンテナで運用したいことはままある*1
- そこで、セキュリティリスクを承知のうえで、コンテナの中で chronyd を動かして、ホストの時刻を修正したり、時刻を配信したり、できるようにする
検証環境
LXCコンテナ内でのchronyの挙動
とりあえず動かして見る
普通にLXCでコンテナを作った場合、たとえそれが特権コンテナでも chronyd は動作しない。
$ sudo apt install chrony $ sudo systemctl restart chronyd $ sudo systemctl status chronyd ● chrony.service - chrony, an NTP client/server Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled) Active: inactive (dead) Condition: start condition failed at Wed 2020-11-18 04:18:48 UTC; 2min 14s ago └─ ConditionCapability=CAP_SYS_TIME was not met Docs: man:chronyd(8) man:chronyc(1) man:chrony.conf(5) Nov 18 04:18:48 tim systemd[1]: Condition check resulted in chrony, an NTP client/server being skipped.
どうして?
キモになるのはここ。
ConditionCapability=CAP_SYS_TIME was not met
LXCの特権コンテナでは、init が root 権限で実行されている。そのため、何も制限をしないとコンテナの root はホストの root と同等の権限になる*2。
そこでセキュリティ担保のため、コンテナ内の root からは特にセキュリティリスクの高い権限が剥奪されている。CAP_SYS_TIME
はシステムタイマやタイマデバイスを操作するための権限。他にどのような権限があるかは以下の man ページを参照。
自分の環境では、全てのコンテナで共通的に読み込まれる config ファイルで以下のようにいくつかの権限が剥奪されていた。
$ cat /usr/share/lxc/config/common.conf ... # Drop some harmful capabilities lxc.cap.drop = mac_admin mac_override sys_time sys_module sys_rawio ...
lxc.cap.drop
は、ここに定義した権限を剥奪するパラメータ。ここで sys_time
が設定されているので、CAP_SYS_TIME
が剥奪されている。これが、本事象の原因となる。
対策
上記のパラメータから sys_time
を外してしまいたいけども、それをすると全てのコンテナに権限が付与されてしまう。また、困った事にLXCに権限を与えるパラメータはない*3。
仕方がないので、lxc.cap.drop
を再定義する。基本的に lxc.cap.drop
の設定は重ねがけだが、空欄を指定するとそれまで指定した全ての設定がクリアされるという仕様になっている。
そこで、共有の config は手を付けずに、chronyd を導入したいコンテナの config の一番末尾で空欄指定で一度パラメータをクリアしてから、sys_time
を除いた権限を指定する。
$ sudo vim /var/lib/lxc/【コンテナ名】/config ... # Drop some harmful capabilities lxc.cap.drop = lxc.cap.drop = mac_admin mac_override sys_module sys_rawio ...
動作確認
権限を付与するためにコンテナを再起動する。
$ sudo lxc-stop -n 【コンテナ名】 $ sudo lxc-start -n 【コンテナ名】
確認してみる。
$ sudo systemctl status chronyd ● chrony.service - chrony, an NTP client/server Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-11-18 07:04:41 UTC; 10s ago Docs: man:chronyd(8) man:chronyc(1) man:chrony.conf(5) Process: 112 ExecStart=/usr/sbin/chronyd $DAEMON_OPTS (code=exited, status=0/SUCCESS) Process: 124 ExecStartPost=/usr/lib/chrony/chrony-helper update-daemon (code=exited, status=0/SUCCESS) Main PID: 122 (chronyd) Tasks: 2 (limit: 4915) Memory: 1.3M CGroup: /system.slice/chrony.service ├─122 /usr/sbin/chronyd -F -1 └─123 /usr/sbin/chronyd -F -1 Nov 18 07:04:41 tim systemd[1]: Starting chrony, an NTP client/server... Nov 18 07:04:41 tim chronyd[122]: chronyd version 3.4 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASY NCDNS +SECHASH +IPV6 -DEBUG) Nov 18 07:04:41 tim chronyd[122]: Initial frequency -10.270 ppm Nov 18 07:04:41 tim chronyd[122]: Loaded seccomp filter Nov 18 07:04:41 tim systemd[1]: Started chrony, an NTP client/server. Nov 18 07:04:47 tim chronyd[122]: Selected source 23.131.160.7
これで、コンテナの中から、ホストのシステムタイマーを制御できるようになる。
余談だが、ホストで既に chronyd や systemd-timesyncd など、タイマを制御するデーモンが動いているなら、止めておかないと大変なことになる。
おわりに
- LXCでは、セキュリティ上の理由から、標準でタイマを制御する権限が剥奪されている
- コンテナ内で chronyd を動かすために、特定のコンテナからタイマを制御する権限を復活させるための手順を整理した
- タイマの制御は、かなりプリミティブな権限なのでくれぐれも自己責任で
systemdで多段プロキシ・リバースプロキシを作る
- systemd だけを使って、多段プロキシやリバースプロキシを作る
- 用途はいろいろあるけど、深く突っ込んじゃイケない
背景
ユースケースはあえて挙げないけど、多段プロキシできるフォワードプロキシとか、一時的なリバースプロキシなど、便利なプロキシサーバが時たま必要になる。
Squidやnginxなどの汎用のものから、Poundやdelegateなどの専用のものまでいろいろな方法があるけども、諸事情*1でこれらが使えない時もある。
そんな時には systemd が使える。systemd は Linux の比較的新しい*2 init システムだが、 init とは思えないほど多機能*3で、実はプロキシサーバが作れてしまう。その手順をまとめる。
検証環境
systemd でプロキシサーバを作る
socket ファイル
まず、リクエストを受け付けるための socket のユニットファイルを用意する。
ListenStream
にリクエストを待ち受けるインタフェースのIPアドレスとポート番号を指定する。
$ sudo vim /etc/systemd/system/proxy.socket [Unit] Description=HTTP Proxy Socket [Socket] ListenStream=0.0.0.0:80
service ファイル
次に、実際にリクエストを経由する service のユニットファイルを用意する。
ExecStart
でsystemd-socket-proxyd
のパスを指定し、第一引数に転送先(多段プロキシなら次のプロキシサーバ、リバースプロキシなら内側のWebサーバ)のIPアドレスとポート番号を指定する。
注意点としては、2つのユニットファイル名はそろえておかないとダメ。(今回は proxy
)
$ sudo vim /etc/systemd/system/proxy.service [Unit] Description=HTTP Proxy Service [Service] ExecStart=/usr/lib/systemd/systemd-socket-proxyd 192.168.1.1:5963 PrivateTmp=yes
起動
後はお約束。
systemdをリフレッシュして、
$ sudo systemctl daemon-reload
socketを起動して、
$ sudo systemctl start proxy.socket
うまくいっているようなら、永続化する。
$ sudo systemctl enable proxy.socket
まとめ
- systemd 単体で多段プロキシもしくはリバースプロキシとして使える、プロキシサーバを構築した
ローカルのGitBucketをGitHubに移行する(その3)
- ローカルサーバのGitBucketで管理していたリポジトリをGitHubに移行したい
- 過去記事では、リポジトリの移行と、Issue と Pull Request のダンプを行った
- 今回は、GitBacket からダンプした Issue と Pull Request を GitHub に投稿する
- なお、既に close され branch が消えていた Pull Requset は登録できないので、他のIssueやPull Requestの番号に齟齬が生じないように調整するにとどめる
その1とその2はこちら。
背景
過去記事参照。
GitHub の API
Issue と Pull Request を作って閉じるために必要なAPIは以下の通り。
Issue の投稿
GitHub で Issue を作るときは、 Create an issue
の API をコールする。
パラメータは以下の通り。残念ながら、MileStone との対応づけはない。
コール先:POST /repos/:owner/:repo/issues
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
title | string | Issueのタイトル | issue['titile'] |
body | string | 本文 | issue['body'] |
milestone | integer | マイルストン番号 | なし |
labels | array of strings | ラベル名 | issue['labels'][N]['name'] |
assignees | array of strings | アサイン先 | issue['assignees'][N]['login'] |
Issue へのコメントの投稿
作った Issue にコメントする場合は、 Create an issue comment
の API をコールする。
コール先:POST /repos/:owner/:repo/issues/:issue_number/comments
パラメータは以下の通り。issue_comments
は、issue
の comment_url
に格納されているURLから、更にAPIをコールして取得したもの。その2 では、ダンプした issue
の中に comments
という key を追加して埋め込んでいる。
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
body | string | コメント本文 | issue_comments[N]['body'] |
Issue のクローズ
Issue をクローズする場合は、Update an issue
の API をコールする。
コール先:PATCH /repos/:owner/:repo/issues/:issue_number
パラメータは以下の通り。クローズしたいだけなら、state
だけ送ればいい。
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
title | string | Issueのタイトル | issue['titile'] |
body | string | 本文 | issue['body'] |
milestone | integer | マイルストン番号 | なし |
labels | array of strings | ラベル名 | issue['labels'][N]['name'] |
assignees | array of strings | アサイン先 | issue['assignees'][N]['login'] |
state | string | open かclosed |
issue['state'] |
Pull Request の投稿
Pull Request を作るときは、 Create a pull request
の API をコールする。
コール先:POST /repos/:owner/:repo/pulls
パラメータは以下の通り。
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
title | string | タイトル | pull['title'] |
body | string | 本文 | pull['body'] |
head | string | 改修後のブランチ名 | pull['head']['ref'] |
base | string | 改修前のブランチ名 | pull['base']['ref'] |
draft | boolean | Draft Pull Requestか | pull['draft'] |
Pull Request へのコメントの投稿
Pull Request にコメントを投稿するときは、Issue の時と同様に Create an issue comment
の API をコールする。
Pull Request のマージ
Pull Request をマージするときは、 Merge a pull request
の API をコールする。
コール先:PUT /repos/:owner/:repo/pulls/:pull_number/merge
パラメータは以下の通り。ただマージしたいだけなら sha
だけ合わせておけばいい。
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
commit_title | string | コミットメッセージのタイトル | - |
commit_message | string | コミットメッセージ | - |
sha | string | Pull RequestのheadのSHA | pull['head']['sha'] |
merge_method | string | マージする方法 | - |
Pull Request のクローズ
Pull Request をクローズする場合は、Update a pull request
の API をコールする。
コール先:PATCH /repos/:owner/:repo/pulls/:pull_number
パラメータは以下の通り。クローズしたいだけなら、state
だけ送ればいい。
パラメータ | 型 | 概要 | GitBucketからダンプしたモノとの対応 |
---|---|---|---|
title | string | タイトル | pull['title'] |
body | string | 本文 | pull['body'] |
base | string | 改修前のブランチ名 | pull['base']['ref'] |
draft | boolean | Draft Pull Requestか | pull['draft'] |
state | string | open かclosed |
pull['state'] |
手順
これらAPIを用いてGitHubに Issue と Pull Request を移行する。
アクセストークンを発行する
その1で移行したリポジトリに、その2でダンプした Issue を投稿する。
GitHubでもGitBucketの時と同じように、アクセストークンを発行する。
- GitHubにログインする
- アカウントのマイページにアクセスして
- Settings を選択
- Personal access tokens を選択
- Generate new token を選択
- Note にテキトーな名前を入れて
- repo にチェックをして
- Generation Token を選択する
するとアクセストークンが発行されるので、メモしておく。
ダンプした Issue と Pull Request を登録する
注意点は以下の通り。
- Issue 番号 と Pull Request 番号は同じ採番体系になっており、これらは投稿した順に採番される。そのため、元のIssue番号、Pull Request番号の通りに投稿する。
- GitBucket で使っていたユーザ名が、GitHub でも使えるとは限らない。Issue の assignees には GitHub に存在するユーザ名に置換した上で投稿する。
- クローズ済みの Pull Request について、Marge した後、リポジトリから head となる Branch が削除されていたりすると投稿できなくなる。私のリポジトリの場合、クローズ済みの Pull Request にあまり価値はない*1ので、ダミーの Issue を投稿して番号をずらして回避する。
特に工夫点はなく、愚直に API をコールし続ける。その2で作ったコードとマージして完成。
実際のコード
長くなったので、GitHubに公開します。
- ライセンスは The MIT License とします。
結果
- GitBacket 上の Issue と open な Pull Request は Issue 番号やコメントの内容も含めて移行できた。
- クローズした Pull Request はダミーIssue として移行した。
- これで、ローカルのGitBucketのGitHubへの移行は完了となった。
Debian10でGeForce RTX 3080を使ってCUDAするための準備
先日、激戦を勝ち抜き GeForce RTX 3080 を買いました。
ツクモさんで注文し、手に入れることができました!ヒャッホー!!
— kWatanabe (@WWatchin) 2020年10月14日
生まれて初めての単体で6桁円するPCパーツを買いました!!! pic.twitter.com/rlg7Ax45hY
とはいいつつも、私はPCでゲームを殆どしないので、メインマシンは75W級グラボの最高傑作だと思っている Quadro P2200 をそのまま使います。
キター pic.twitter.com/2MN7mjTjoL
— kWatanabe (@WWatchin) 2020年5月2日
では、なぜ GeForce RTX 3080 を調達したかなんですが、自宅のなんちゃって IaaS の GPUコンテナ で CUDA するためです。しかし、GeForce RTX 3080 はリリースされたばかり。debian-backportsでもSidでもドライバが古く、3080 を動かせません。
なので、3080 を動かせるドライバに入れ替えることにしました。
2021/02/03 追記
- 半年の歳月を経て、
buster-backports
にも、GeForce RTX 3080 対応のドライバがリリースされました。現在は、以下の記事に頼らなくても、debian-backports
リポジトリで対応できます。
GeForce RTX 3080 を動かせるLinuxドライバ
GeForce RTX 3080 を使うためには、455.23 以上でなければなりません。2020/10/15現在、Debian の各リポジトリの nvidia-driver のバージョンは以下の通りです。
リポジトリ | バージョン |
---|---|
stable (buster) | 418.152.00-1 |
backports (buster-backports) | 450.66-1~bpo10+1 |
testing (bullseye) | 450.66-1 |
unstable (sid) | 450.66-1 |
不安定版のSidを以てしても、まだ足りません。NVIDIAから公式バイナリを入手する手もあるのですが、できれば apt で一元管理したい。そこで experimental リポジトリを使うことにしました。
experimental リポジトリ
要するに、Sid (不安定版)に取り込む前の開発用リポジトリです。ページには「一般ユーザはこのリポジトリのパッケージを使ってくれるな」(意訳)と書いてあります。
また、各種パッケージのページにも以下の警告が記述されています。
- 警告: このパッケージは experimental ディストリビューションのものです。つまり、おそらく不安定でバグがあり、それどころかデータの損失を起こすかもしれません。使用前には、変更履歴やその他の参照可能なドキュメントを必ず調べてください。
2020/10/15現在、experimental リポジトリの nvidia-driver は455.23です。ちょうど GeForce RTX 3000 をサポートし始めたバージョンです。
導入
手順自体は、debian-backports や Sid を使う時と変わりません。/etc/apt/sources.list
にexperimentalリポジトリを追加して、apt するだけです。
$ sudo vim /etc/apt/sources.list deb http://deb.debian.org/debian/ experimental main non-free contrib $ sudo apt update $ sudo apt -t experimental install nvidia-driver
運が良ければ、ちゃんと稼働してくれることでしょう。
2020/11/28 追記
- もし依存パッケージが解決できないようなら
testing
やbuster-backports
リポジトリも追加してください。 testing
を登録した際、システム全体testing
にアップグレードされることを防ぐために/etc/apt/apt.conf
にAPT::Default-Release "stable";
を付けると良いと思います。
2021/01/20 追記
- ついに、
testing
に 460 のドライバが降りてきました。 - なので、危ない橋を渡って
experimental
リポジトリを使わなくても、testing
リポジトリを足すだけで大丈夫になりました。 - また、今年の夏頃にリリース予定の Debian 11 では、何もしなくても RTX 3080 が使えるようになります。
2021/02/03 追記
- 半年の歳月を経て、
buster-backports
にも、460 のドライバが降りてきました。 - なので、今は Debian10 でも、
buster-backports
リポジトリを足すだけで、RTX 3080 が使えるようになりました。
結果
まだ、GeForce RTX 3080 は組み込んでいませんが、ドライバだけ入れ替えました。GPUをパススルーしたコンテナからも、ちゃんと新しいドライバが動いています。
$ sudo nvidia-smi Thu Oct 15 00:43:32 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 455.23.04 Driver Version: 455.23.04 CUDA Version: 11.1 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 GeForce GTX 105... On | 00000000:0A:00.0 Off | N/A | | 29% 31C P8 N/A / 75W | 1MiB / 4040MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+
あとは、GeForce RTX 3080 を組み込んで CUDA が動けば問題なしですね。
2020/10/16追記
GeForce RTX 3080 組み込み完了しました。 コンテナの中から、ちゃんと GeForce RTX 3080 を認識できています。バッチリですね。
参考
大元のGPUコンテナを作った時の記事はこちら。 kwatanabe.hatenablog.jp
ローカルのGitBucketをGitHubに移行する(その2)
- ローカルサーバのGitBucketで管理していたリポジトリをGitHubに移行したい
- その1では、リポジトリ本体とユーザ情報、タグなどの移行を行った
- 今回は、データベースで管理されている Issue を GitBacket からダンプする
その1はこちら。
その3はこちら。
背景
過去記事参照。
GitBucket から Issue を取り出す
アプリケーショントークンの取得
GitBucket は GitHub REST API v3 のサブセット*1を実装しているので、直接 H2 や PostgreSQL の中身を覗かなくてもある程度はなんとかなる。
REST API なので python なり perl なりでスクリプトでサクッとおわらせたい。そのためにまずはアプリケーショントークンを取得する。
- GitBucket にログイン
- Account settings を選択
- Applications を選択
- Generate new token の Token description にテキトーな説明を入れる
- Generate token を選択
するとトークンが表示されるのでメモっておく。
Issue と Pull Request の取得
アプリケーショントークンを使って、http://GITBACKET_SERVER_URL/api/v3/repos/USERNAME/REPONAME/issues
に対して、APIをコールしてやれば以下のような JSON で降ってくる。
{ 'number': 2, 'title': 'Issueのタイトル', 'user': { 'login': 'USERNAME', 'email': 'USERNAME@example.com', 'type': 'User', 'site_admin': True, 'created_at': '2018-08-08T12: 51: 17Z', 'id': 0, 'url': 'http://GITBACKET_SERVER_URL/api/v3/users/USERNAME', 'html_url': 'http://GITBACKET_SERVER_URL/USERNAME', 'avatar_url': 'http://GITBACKET_SERVER_URL/USERNAME/_avatar' }, 'assignee': { userと同じ key の集まり (valueは異なる) }, 'labels': [ { 'name': 'enhancement', 'color': '84b6eb', 'url': 'http://GITBACKET_SERVER_URL/api/v3/repos/USERNAME/REPONAME/labels/enhancement' } ], 'state': 'closed', 'created_at': '2018-08-13T14:33:25Z', 'updated_at': '2020-10-10T06:34:36Z', 'body': 'Issueのメッセージ本文', 'id': 0, 'assignees': [ { user や assignee 同じ key の集まり (valueは異なる) } ], 'comments_url': 'http://GITBACKET_SERVER_URL/api/v3/repos/USERNAME/REPONAME/issues/2/comments', 'html_url': 'http://GITBACKET_SERVER_URL/USERNAME/REPONAME/issues/2' }
さらに、Issue 内のコメントは、降ってきた comments_url に対してAPIをコールしてやれば、これまた JSON で手に入る。
[ { "id": 3, "user": { "login": "USERNAME", "email": "USERNAME@excample.com", "type": "User", "site_admin": true, "created_at": "2018-08-08T12:51:17Z", "id": 0, "url": "http://GITBACKET_SERVER_URL/api/v3/users/USERNAME", "html_url": "http://GITBACKET_SERVER_URL/USERNAME", "avatar_url": "http://GITBACKET_SERVER_URL/USERNAME/_avatar" }, "body":"Issueのコメント本文" } ]
Pull Request と Issue はAPIのエントリポイントが異なるだけで、全く同じ手順でダンプできる。なので、Issue と Pull Request はメソッドを共通化できる。やっつけだが、概ね以下の通り。
【2020/10/20修正】ちょっとやっつけすぎたので、少しリファクタリング。
import os import requests GITBUCKET_API_BASEURL='http://gitbucket.example.com/api/v3/repos/USERNAME/REPONAME' GITBUCKET_APP_TOKEN='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # GitBucket から issue を取得する def get_issues (base_url, token): headers = {'Authorization': 'token ' + token} issues = [] # issue for issue_type in ('issues', 'pulls'): url = '%s/%s' % (base_url, issue_type) for state in ('open', 'closed'): for page in range(1, 10000): payload = {'state': state, 'page':page} response = requests.get(url, headers=headers, params=payload) new_issues = response.json() if not len(new_issues): break issues.extend(new_issues) issues.sort(key=lambda x: x['number']) # issue comment for issue in issues: response = requests.get(issue['comments_url'], headers=headers) issue['comments'] = response.json() return issues if __name__ == '__main__': issues = get_issues(GITBUCKET_API_BASEURL, GITBUCKET_APP_TOKEN) print(issues)
ハマりポイントとしては2点。
- GitBucket の issues API は GitHub では使える state=all が使えないので、state=openとstate=closedを使って結果を結合しないとダメ。
- またper_page=N も使えず、25個ずつしか取ってこれないので、page=N を順繰りに増やし、空配列しか降ってこなくなるまで取得し続けないとダメ。
これで、issue と Pull Request を一通りぶっこ抜けるようになったので、今度はこれを同じく GitHub API を使って GitHub に突っ込んでいく。
次回予告
- GitBucket からぶっこ抜いた Issue と Issue comment を GitHub に入れていきます。
*1:サブセットなので、いろいろと実装が足りなくてハマりポイント多数。ちゃんとしたドキュメントが欲しい
ローカルのGitBucketをGitHubに移行する(その1)
- ローカルサーバのGitBucketで管理していたリポジトリをGitHubに移行したい
- リポジトリ本体は clone して push すれば事が済むが、ユーザ名やメールアドレスが同じとは限らない
- また、データベースで管理されている Issue などはそう簡単にはいかない
- あんまり頑張りすぎずに、できる範囲で移行した際の作業記録(その1)
その2とその3はこちら。
背景
とある事情にて、ローカルのGitBucketをGitHubのプライベートリポジトリに移行することになった
自宅サーバの構成管理をAnsibleでやってるんだけど、Ansible Playbookのリポジトリを自宅サーバのGitBucketで管理しているので、自宅サーバが壊れたらバックアップから引っ張ってこないと再構築できないよな…。
— kWatanabe (@WWatchin) 2020年10月10日
Ansible Playbook だけでも、GitHubのプライベートリポジトリに外出しするか。
GitHub はリポジトリの移行機能を持っていたり、極端な話 clone して push したら済むので、そう難しくない。でも、PostgreSQL に入っている Issue とか、 pull request とかはどうすりゃいいか分からん。
移行ツールとか無いか、いろいろ漁ってみたもののコレといったものが出てこない。なんとかならんものか。
最低限守りたいモノ
とりあえず、あんまり頑張りたくないので、最低限守りたいものを整理する。
プルリクは、たまたまオープンのものが無かったので、特に意識はしていない。
【2020/10/11訂正】 Issue だけだと、Issue 番号がずれてしまうので、プルリクも入れてやらないとダメだった。
リポジトリ
フルミラーの取得
リポジトリの完全なミラーを取得する。
まずは単純に clone して。
$ git clone <リポジトリのURL>
ブランチを確認して。
$ cd <cloneしたディレクトリ> $ git branch -r | grep -v "\->" | grep -v master origin/issue103
見つけたブランチも引っ張ってくる。
$ git branch --track issue103 origin/issue103 $ git branch issue103 * master
多分大丈夫だと思うけど、fetch と pull もしておく。
$ git fetch --all $ git pull --all
おっけー。
ユーザ情報を GitHub のものに移行
ユーザ情報(AuthorとCommitter)が、ローカルとGitHubアカウントで齟齬があるので、コレを修正する。
齟齬とはいったものの、実態としてローカルのリポジトリでは、好き勝手にでっち上げたユーザ名とメールアドレスを使ってコミットしていたのでもうグチャグチャ。まずは、こいつを GitHub のそれに合わせて整える。
現状を確認する。
$ git log --pretty=full …(略)… Author: XXXX <XXXX@XXXX.com> Commit: XXXX <XXXX@XXXX.com> …(略)…
もし、GitHubのユーザ名が kwatanabe、 メールアドレスが kwatanabe@example.com だとすると、こうする。
$ git filter-branch -f --env-filter "GIT_AUTHOR_NAME='kwatanabe'; GIT_AUTHOR_EMAIL='kwatanabe@example.com'; GIT_COMMITTER_NAME='kwatanabe'; GIT_COMMITTER_EMAIL='kwatanabe@example.com';" HEAD Ref 'refs/heads/master' was rewritten
もし、ブランチがあるならそれぞれで同じ事をやっておく。
$ git checkout BRANCH Switched to branch 'BRANCH' Your branch is up to date with 'origin/BRANCH'. $ git filter-branch -f … (省略)
修正されたことを確認。
$ git log --pretty=full …(略)… Author: kwatanabe <kwatanabe@example.com> Commit: kwatanabe <kwatanabe@example.com> …(略)…
試しに push してみる
とりあえずこれでリポジトリは整った。試しにGitHubにプッシュしてみる。
GitHubでプラベートリポジトリを作る。このとき、「Add a README file」と「Add .gitignore」のチェックを外しておく。
リポジトリのPUSH先URLを変更する。
$ git remote set-url origin https://github.com/<ユーザ名>/<リポジトリ>
あと、GitHubのデフォルトブランチ名は master ではなく main に変わった*1ので、メインブランチ名を変更する。
$ git branch -M main $ git branch issue103 * main
PUSHする。
$ git push --all origin $ git push --tags
GitHubのコンソールに入って、リポジトリのブランチとタグがきちんと反映されてることを確認しておく。
あと、きちんとコミットとユーザが紐付き、いわゆる"草原"に反映されていることを確認する。
次回予告
単なるリポジトリの移行だけであれば、コレで完了。
Issue やら Release やらをコピーしたい場合は、GitBucket から どうにかしてぶっこ抜く必要がある。
これは以下の記事参照です。
LXCの非特権コンテナにホストのGPUをパススルーする
- 2020/11/08 一部のtypoを修正
大要
- Proxmox VEのLXCの非特権コンテナにGPUをパススルーし、CUDAを使う
- KVMのVGAパススルーと違って、ホストに搭載するGPUは1つでよい
- GPUは(性能を考慮しないなら)そのままホストでも利用できる
- (たぶん、)生のLXCでも同等の手順で実現できる
背景
- 我が家では Ryzen マシンに Proxmox VE を入れて、IaaS モドキを運用している(以前は CloudStack + XenServer だったが、XenServer 無料版 の制限強化の際にやめた)
- Ryzen はGPUを持たないので、コンソール用に GeForce GTX 1050Ti を挿しているが、非常時以外は使わないので勿体ない
- LXCの非特権コンテナ上で 1050Ti を使って CUDA ができれば、追加費用なしで手軽にGPUインスタンスが手に入るのではと思った
ターゲット環境
- LXCホスト:Proxmox VE 6.2 (サブスクリプションなし)
- LXCゲスト:Debian 10.6 (非特権コンテナ)
- GPU:Geforce GTX 1050Ti
手順
大まかな手順は以下の通り。
- ホストでGPUのドライバをビルド
- ホストでGPUのデバイスファイルを有効化
- ゲストにGPUのデバイスファイルをリマップ
- ゲストでGPUのドライバをビルド
- ゲストでCUDAのライブラリをインストール(割愛)
ホストでGPUドライバをビルド
サブスクリプションを持たない人向けの Proxmox リポジトリを有効にする。もちろん、サブスクリプションをお持ちの場合は不要。
# vim /etc/apt/sources.list deb http://download.proxmox.com/debian/pve buster pve-no-subscription
Proxmox VE のリポジトリには nvidia-driver がないので、Proxmox VE 6.x のベースである Debian 10 のリポジトリを有効にする。なお、stable リポジトリの nvidia-driver は古いので、backports リポジトリを用いる。
# vim /etc/apt/sources.list deb http://deb.debian.org/debian buster-backports main contrib non-free deb-src http://deb.debian.org/debian buster-backports main contrib non-free
nvidia-driver を導入する。nvidia-driver はバイナリで提供されていないので、DKMS*1 でその場でビルドされる。そのため、カーネルヘッダやビルドツールも一式必要になる。
# apt update # apt upgrade # apt install pve-headers build-essentials # apt install -t buster-backports nvidia-driver nvidia-smi
ホストでGPUデバイスファイルを有効化
DKMSでビルドされたドライバモジュールを有効にする。
# vim /etc/modules-load.d/nvidia-gpu.conf nvidia-drm nvidia nvidia_uvm
udevルールを作成して、ドライバがロードされた時に非特権コンテナからでもデバイスファイルを扱えるように、アクセス権が変更されるようにする。(特権コンテナの場合は不要)
余談だが、非特権コンテナから扱えるようにアクセス権を緩めると、ホストの一般ユーザ(Proxmox VEに一般ユーザはないが)からでもデバイスファイルが叩けるようになってしまう。でも、コンテナを特権コンテナにするよりはまだマシか…?
# vim /etc/udev/rules.d/70-nvidia-gpu.rules KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L && /bin/chmod 666 /dev/nvidia*'" KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u && /bin/chmod 0666 /dev/nvidia-uvm*'"
ホストを一度再起動する。
# reboot
起動したら、GPUが正しくホストで認識されているか確認する。
# nvidia-smi Sun Oct 4 15:42:59 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 440.100 Driver Version: 440.100 CUDA Version: N/A | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 105... On | 00000000:0A:00.0 Off | N/A | | 29% 29C P8 N/A / 75W | 1MiB / 4040MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+
ゲストにGPUデバイスファイルをリマップ
コンテナの設定ファイルを開いて、GPUのデバイスファイルをリマップする。
例えば、対象のコンテナの vmid が 101 だった場合は、以下の通り。
# vim /etc/pve/lxc/101.conf lxc.cgroup.devices.allow: c 195:* rwm lxc.cgroup.devices.allow: c 236:* rwm lxc.cgroup.devices.allow: c 226:* rwm lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
コンテナを起動して、コンテナの中からもデバイスファイルが見えている事を確認する。
$ ls /dev/nvidia* /dev/dri/* /dev/nvidia0 /dev/nvidiactl /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/dri/card0 …(省略)…
ゲストでGPUドライバをビルド
ホストと同様にコンテナでGPUのドライバをビルドする。今回は Proxmox VE のベースである Debian 10 のコンテナなので、カーネルヘッダが異なる以外は同じ。
まずは、backports リポジトリを有効にして…。
$ sudo vim /etc/apt/sources.list deb http://deb.debian.org/debian buster-backports main contrib non-free deb-src http://deb.debian.org/debian buster-backports main contrib non-free
次に、DKMS でドライバをビルドする。
$ sudo apt update $ sudo apt upgrade $ sudo apt install build-essential $ sudo apt install -t buster-backports nvidia-driver nvidia-smi
コンテナの内部からGPUにアクセスできるか確認する。
$ nvidia-smi …(省略)…
あとは、実機でCUDAを使うときと同様に、コンテナにCUDAドライバをインストールする。ここでは割愛する。
備考
- ホストとゲストのドライバは同じバージョンが望ましい。
- Debian 10 以外のコンテナにパススルーする場合は、ディストリビューションのドライバのバージョンを確認して、少しでも違ったら公式ドライバでバージョン合わせをするのが無難
- Proxmox VE 固有の手順は殆どないので、多分、生のLXCでも同じ手順で使えると思う