LXD 使ってみた
内容
LXDの導入、bridge, macvlanのコンテナを作成するまで
LXDについて
LXD は次世代のシステムコンテナーおよび仮想マシンのマネージャーです。 コンテナーあるいは仮想マシンの内部で稼働する完全な Linux システムに対して統一されたユーザーエクスペリエンスを提供します。
いろいろな Linux ディストリビューション のあらかじめビルドされたイメージを使ったイメージベースのマネージャーであり、非常に強力でありながら、非常にシンプルに、REST API を使って構築されます。
導入は簡単だったが、VM/物理マシンでのネットワークの設定で戸惑った。。
環境
Ubuntu 20.04 LTS (物理マシン)
よさげLink
install
snapでパッケージインストール
# snap install lxd
バイナリからインストールもできるよう。
起動、初期設定
/etc/sysctl.conf
のnet.ipv4.ip_forward=1
のコメントを外しておく。←いらないかも?
lxd
グループに入っていたら、sudo
なしでlxd
やlxc
コマンドを利用できる。
# lxd init
今回は、すべてデフォルト設定にした。(lxd init --auto
)
image
OSimageは配布されているものを使う。それを基にしたオリジナルimageを使うことも可能。
配布imageにはUbuntuやCentOSなどバージョンも多くの種類が用意されている。
Cloud-initが動作する/しないimage、vmバージョンのimageなど様々。
imageの検索
lxc image list [image_server]: [keyword] .. # lxc image list images: amd64 gentoo
インスタンスの実行
lxc launch [image_server]:[image_alias] [name] [flags] # lxc launch ubuntu:f uf1
[flags]
に--vm
とすれば、VMが起動する。
今回使ったものは、ubuntu:f
。
インスタンスを操作
名前を指定してインスタンスにコマンドを実行させる。
名前はlxc list
などで確認する。
lxc exec [name] -- [command] # lxc exec uf1 bash
--
はなくてもいいが、オプションを渡す場合はこれをつけないとうまくいかない。
また、コンテナ側でリダイレクトを利用するときは、bash -c 'echo hoge > /tmp/hoge.txt'
というようにまとめてから実行させないと、ホスト側にリダイレクトされるので注意。
ネットワークについて
ここまではGettingStartedをみればできるが、ネットワークの設定で躓いた。
まず、インスタンスの設定項目にはnetwork
とprofile
がある。
network
ip l
で出てくるデバイスについて詳細を見る
# lxc network show lxdbr0
lxdbr0
は、デフォルト設定でlxd init
すれば作成されるデバイス。
インスタンスを作成するとき、デフォルトではこれがbridge
として使われる。
bridge
というのは、ネットワークタイプの設定で、ほかにはmacvlan
やovn
などがある。
bridge
は、ホストからインスタンスには通信可能だが、外部からインスタンスには通らない。(private)
macvlan
は、ホストの有線nI/Fを使う(ブリッジする)必要がある(複数のMACアドレスを扱うため。無線は複数MACに対応していないらしい)。外部からインスタンスにアクセスできる(public)
macvlan
を使う場合は、ブリッジするホストのデバイスのDHCPをオンにしておく必要がある。今回は、/etc/netplan/
のconfigからdhcp4: true
となっているのを確認。
VM(VBox)では、インスタンスから外部に通信できないなどうまくできなかった。
物理マシンでは、アパート管理のDHCPサーバーを利用しているためか、ipの割り振りが時々されず。。
結局、安定しているbridge
を使うことにした。
外部からホストを中継してsshすればアクセスできる(後述)し、
bridge
なら、ホストマシンのlxd
がdnsmasq
(lxdのdnsmasqなので、マシンに入れていなくておkだしsystemd-resolvedとの競合もなさげ。)を使ってDHCP割り振りを行っているので管理しやすい。
profile
profileは、インスタンスの設定をテンプレにしたもので、何のprofileを適用するかlaunch時に指定できる。
デフォルトではdefault
というのが使われる。オリジナルのprofileを作るときはこれを基に書き換えるなどする。
defaultにはnetwork: lxdbr0
とあるため、デフォルトインスタンスはlxdbr0
のbridge
となる。
# lxc profile show default
例えばmacvlanのインスタンスを作成するprofileを作成する場合
# lxc profile copy default mvlprof # lxc profile edit mvlprof
とすることで、viを使って編集できる。
このように設定した。
config: {} description: MACVLAN profile devices: eth0: name: eth0 nictype: macvlan parent: enp1s0 type: nic root: path: / pool: default type: disk name: mvlprof used_by: []
parent
の値は、ip l
で出てくる有線デバイス。
これを適用してlaunchすると、macvlanのインスタンスが作成される。
# lxc launch -p mvlprof ubuntu:f mvluf1 # lxc list (launchしただけのときにparentの範囲のipが振られていない場合は、インスタンスを再起動する。) # lxc restart mvluf1
外部からpingを通してみる
$ ping [instance_ip] 64 bytes from 192.168.12.16: icmp_seq=1 ttl=63 time=4.67 ms ...
ipの固定
作成 lxc network attach [network] [instance_name] [insatnce_new_I/F] 割り当て lxc config device set [instance_name] [instance_new_I/F] ipv4.address [ip_address] # lxc network attach lxdbr0 uf1 eth1 # lxc config device set uf1 eth1 ipv4.address 10.91.103.50 (反映させるためにインスタンスの再起動または dhclient を実行させる) # lxc restart uf1 または # lxc exec uf1 dhclient eth1
新しくインスタンス内のネットワークデバイスを作成して割り当てている。
だが、既存のデバイスを指定してもおk。
lxc config device set uf1 eth0 ipv4.address 10.91.103.50
のようにすると、eth0に2つのipを割り当てることになる。
個人的に、"network"と"profile"の関係性を整理するのに少し躓いた。
network
はip a
で出るあのデバイス(正しくはlxc network list
で出るやつ)のことで、
profile
には、それらのどれを物理デバイスとして使うか、ソフト的にはbridgeやmacvlanなどどれを使うのか、を記述している。
…という認識。
/sbin/initが動くので、ansibleのテスト環境としてよさそうに思う。 imageの、cloud-init動作版やvm版との違いがまだわからないが、VMより軽快なのでansibleの簡易テスト環境として使ってみる所存。
よく使うコマンドリスト
状態確認
lxc network show lxdbr0 lxc network list lxc profile show default lxc profile list lxc config device show default lxc config device show uf1 lxc config device list uf1 lxc config show uf1 --expanded // dnsmasq leases cat /var/snap/lxd/common/lxd/networks/lxdbr0/dnsmasq.leases lxc network list-leases
ネットワーク関連
networkデバイス新規作成 # lxc network create newbr0 defaultを流用してnetworkを作成 # lxc profile copy default ufnet 書き換え(bridgeなら lxdbr0 を ufnet に書き換えるだけ) # lxc profile edit ufnet profileの変更 # lxc profile assign uf1 ufnet lxdbr0を利用してコンテナ内のeth1としてネットワークデバイスを作成 # lxc network attach lxdbr0 uf1 eth1 固定IPを設定(lxdbr0のsubnet範囲内) # lxc config device set uf1 eth1 ipv4.address 10.91.103.50 コンテナに反映 # lxc exec f2 dhclient eth1
シェルスクリプト
引数に指定したコンテナのipアドレスを取得(CIDR表記)
#!/bin/bash container=$1 echo $container lxc exec $container -- ip a | awk '$1=="inet"{print $2}' | grep -v 127.0.0.1
全コンテナ名を取得
lxc list | awk 'NR>3{print $2}' | grep -v '^$'
全コンテナを削除
lxc delete `lxc list | awk 'NR>3{print $2}' | grep -v '^$'` --force
ブリッジに鍵登録
cat ~/.ssh/id_rsa.pub | xargs -i% lxc exec container -- bash -c 'echo % >> /root/.ssh/authorized_keys'
外部からlxdホストマシンを中継してコンテナに接続
ssh -t [lxd_host] ssh root@[container_ip]
公式のほかで参考にしたサイト様たち
Dockerのubuntuにopenssh-server入れたらsystemctlできた(がsnapdが動かない?)
タイトルで落ちてる
docker run
時のオプション指定に--privileged
、プロセス指定に/sbin/init
とすることも必要。
追記 2021.06.28
コンテナでvm寄りのことをしたいなら、LXDが便利です。おすすめ。
簡単な解説記事書いてますのでどうぞー
追記 2021.03.30
snapdが動かない。
root@cf10f188a61e:/# apt update && apt upgrade -y && apt install -y snapd root@cf10f188a61e:/# systemctl start snapd root@cf10f188a61e:/# systemctl status snapd ● snapd.service - Snap Daemon Loaded: loaded (/lib/systemd/system/snapd.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2021-03-30 12:36:13 UTC; 2s ago TriggeredBy: ● snapd.socket Main PID: 1111 (snapd) Tasks: 10 (limit: 4585) Memory: 16.6M CGroup: /docker/cf10f188a61ef15c9cc1a860c7fa3056c92aeb57f22f753698c307d4803d6b55/system.slice/snapd.service └─1111 /usr/lib/snapd/snapd Mar 30 12:36:13 cf10f188a61e systemd[1]: Starting Snap Daemon... Mar 30 12:36:13 cf10f188a61e snapd[1111]: AppArmor status: apparmor is enabled and all features are available Mar 30 12:36:13 cf10f188a61e snapd[1111]: daemon.go:347: started snapd/2.48.3+20.04 (series 16; classic) ubuntu/20.04 (> Mar 30 12:36:13 cf10f188a61e snapd[1111]: daemon.go:440: adjusting startup timeout by 30s (pessimistic estimate of 30s > Mar 30 12:36:13 cf10f188a61e snapd[1111]: backend.go:134: snapd enabled root filesystem on overlay support, additional > Mar 30 12:36:13 cf10f188a61e snapd[1111]: helpers.go:105: error trying to compare the snap system key: system-key missi> Mar 30 12:36:13 cf10f188a61e systemd[1]: Started Snap Daemon. root@cf10f188a61e:/# sudo snap install core; sudo snap refresh core error: cannot perform the following tasks: - Setup snap "core" (10908) security profiles (cannot reload udev rules: exit status 1 udev output: Failed to send reload request: No such file or directory ) snap "core" is not installed
やっぱり、コンテナをVM代わりにするのは無理があるのか…?
仕方ないのでDockerでなく大人しくVMを使うとするか……
追記終わり
ことの経緯
ansible のテスト環境をDockerで構築できたら便利なのになー、でもVMじゃないからWSLみたいにsystemctlできないのでは??
↓
検索してこの記事が出てくる
あー、やっぱだめなんだ。。検索結果みる限り、CentOSではできるっぽい?うーん。。。
記事の最後にあったsupervisorを調べてみるか。
↓
うーむ、supervisorではだめだな。ansibleのテストをするのにsystemdが使えないのは言語道断だ。
とりあえず、このページを参考に、dockerの練習としてコンテナを作成してみる。(docker初体験故)
supervisorは入れたらめんどくさそうなので、それ以外のパッケージだけ入れる。
コンテナつくれた。こんなんなのかすげー
…でもマジでsystemctl動かないのか、どれどれ、確かCentOSではこういう風にしなきゃだっけ…
$ docker run -itd --privileged <imageID> /sbin/init $ docker exec -it <コンテナID> # systemctl status ssh ● ssh.service - OpenBSD Secure Shell server Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2021-03-29 20:19:48 UTC; 1min 58s ago Docs: man:sshd(8) man:sshd_config(5) Process: 41 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS) Main PID: 45 (sshd) Tasks: 1 (limit: 4585) Memory: 1.5M CGroup: /docker/955ad934203feab908973ded3b82ab7859f294a53cb727abd3000b1ed285d22f/system.slice/ssh.service └─45 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
う、動いたーーー!?!?
じゃあ素のubuntuでは……できない。
何が要因なのか調査
シンプルなDockerfileを書いてちまちまimageを作成、systemctlできるようになる要因を探る。
で、動作確認が取れたのがこの構成
FROM ubuntu:20.04 RUN apt update && apt install -y openssh-server RUM mkdir -p /var/run/sshd ENTRYPOINT ["/sbin/init"]
あらかじめサービスをインストールすることで使えるようになる…?
試しにssh-serverの代わりにnginxにして作成→だめ 今度はsambaにして作成→だめ
…なんなのか。
まあ、ansibleのテスト環境は作れそうなのでとりあえずヨシ!
apt-keyがUbuntu 22.04までしか使えないらしいので手動でGPG鍵追加メモ
apt-key add
の代替手法
Ubuntu 20.04 LTS では非推奨について書かれていないようだが、Ubuntu 20.10からは( del 以外は)非推奨になり、Ubuntu 22.04で最後となるらしい。
man pageをみると、20.10からdeprecatedになっていると確認できる。
非推奨となる理由としては、/etc/apt/trusted.gpg
や/etc/apt/trusted.gpg.d/
に鍵を置くだけでaptは完全にそのリポジトリサーバーを信頼するため、リポジトリサーバーがパッケージを任意のものに置き換えると、自サーバーのパッケージも任意のものに置き換えられてしまうかもしれないため。らしい。
まあ、お偉いさんが非推奨って言ったら黙って従うまで そういうことで調べてみると、apt-key add
の代替手法がいくつか見つかったので、この際忘れないように自分なりに解釈して書き留めておく。
この回答 https://askubuntu.com/a/1307181 が詳しくてわかりよい。ただしここでは少し違う(import, exportを使わない)やり方を紹介する。
環境
Ubuntu 20.04 LTS
つまり
こういうこと
#!/bin/bash keyURI=$1 keyName=$2 wget -O - $keyURI | gpg --dearmor > /usr/share/keyrings/${keyName}.gpg
手順概要
- wget/curl で鍵をDL
- ascii-armordなら
gpg --dearmor
で解除 /usr/share/keyrings/
に鍵を追加
--- ここまでで鍵追加完了 ---/etc/apt/sources.list.d/
にlistファイルを追加/etc/apt/preferences.d/
にprefファイルを追加(任意)
詳細
1. wget/curl で鍵をDL
wget版
# path指定するとき wget -O <filePath> <URL> # カレントディレクトリにそのままDLするとき wget <URL>
curl版
# path指定するとき curl -o <URL> # カレントディレクトリにそのままDLするとき curl -O <URL>
2. ascii-armordなら gpg --dearmor
で解除
file <鍵path>
もしPGP public key block Public-Key (old)
と出力されれば、ascii-armorとなっているため、gpg --dearmor
でバイナリに変換する。
もしdata
と出力されれば、変換作業はいらない。
ascii-armorを解除する
# path指定するとき cat <鍵path> | gpg --dearmor > <鍵path> # カレントディレクトリに<鍵名>.gpgで出力するとき( key.gpg.gpg みたいになるので注意) gpg --dearmor <鍵path>
.asc
などgpg以外の鍵名をそのまま使うと、 apt update
のときに鍵を認識してくれずにエラーが出るので、 必ず .gpg
の拡張子を追加する。
3. /usr/share/keyrings/
に鍵を追加
/usr/share/keyrings/
は、以前の/etc/apt/trusted.gpg.d/
と同じように、鍵を格納するディレクトリとして扱われる。
鍵を置いておく場所は/usr/local/share/keyrings/
でもどこでもいいが、その場合は新たにディレクトリを作成する必要がある。
鍵を移動
mv <鍵path> /usr/share/keyrings/
鍵追加はこれで完了。
続いてaptにリポジトリを登録する。
4. /etc/apt/sources.list.d/
にlistファイルを追加
signed-by=<鍵path>
と書くところがポイント。
ソースURIやdistributionのところに入れる文字は、それぞれのサイトを見れば書いてあるはず。
ここでは、google cloud sdk のインストール を例に挙げている。
サイトの指示通りであるhttp:
とせずに、勝手にhttps:
と設定したが問題なく動作したため、動作確認時にエラーが出ないならhttps:
でいいのではと思う。
deb [signed-by=/usr/share/keyrings/追加した鍵.gpg] <リポジトリのソースURI> <distribution> <component> # example # deb [signed-by=/usr/share/keyrings/apt-key.gpg] https://packages.cloud.google.com/apt cloud-sdk main
Deb822 formatで、listではなくsourcesファイルとして追加
よりわかりやすい書式として、deb822ファイルフォーマットで書くこともできる。
ただし、拡張子がlist
ではなくsources
となることには注意する。
Types: deb URIs: https://packages.cloud.google.com/apt Suites: cloud-sdk Components: main Signed-By: /usr/share/keyrings/apt-key.gpg
5. /etc/apt/preferences.d/
にprefファイルを追加 (任意)
prefファイルを記述することで、追加したリポジトリから特定のパッケージのみをインストールするように設定できるらしい。(ここらへんよくわからない。ドキュメント語読みづら過ぎる…)
Package: <インストールを許可するパッケージ>
全て許可ならばPackage: *
とする。
Pin: origin <ソースURIのオリジン>
など (originの他にもlocal mark?などがあるらしいがよくわからない。DebianRepository/UseThirdParty - Debian Wiki
Pin-Priority: <num>
100はupgradeを許可。1はupgradeを禁止。ディストリビューションのデフォルトのパッケージを上書きするかもしれない(1000を超えるとダウングレードが可能になる)から、高い値にしてはいけないらしい。
Linux Certif - Man apt_preferences(5)
Package: google-cloud-sdk Pin: origin packages.cloud.google.com Pin-Priority: 100
最後に、動作確認として、apt update
してみて問題なければおしまい。
うーん、面倒、、代替となるコマンド、提供されるのかしら。。。
References
DebianRepository/UseThirdParty - Debian Wiki
What commands (exactly) should replace the deprecated apt-key? - Ask Ubuntu
Ubuntu20.04 固定ip設定+VMware(NAT)とSSH接続準備メモ
Ubuntuのipアドレスをnetplanとsystemd-resolvedで固定化、
ホストからSSH接続できるようにホスト側(Win)の設定を調整する。
ゲストはNAT接続(VMnet8)。
環境
Windows10 20H2
WSL2 Ubuntu20.04 LTS (ホスト)
VMware Workstation 16
Ubuntu20.04 LTS (NAT)(ゲスト)
netplanでipを固定する
/etc/netplan/hogehoge
を編集
nameserversのアドレスは任意のDNSサーバーを記述。
network: ethernets: ens33: dhcp4: false addresses: [192.168.47.100/24] gateway4: 192.168.47.2 nameservers: addresses: [1.1.1.1,1.0.0.1] version: 2 renderer: networkd
ゲストと通信するための調整
上記設定にて、ネットワークデバイスのaddressesは、C:\ProgramData\VMware\vmnetdhcp.conf
のrange外かつsubnet内にしておく。
ゲートウェイは C:\ProgramData\VMware\vmnetnat.conf
のNAT gateway address。
また、Windowsのコントロール パネル\ネットワークとインターネット\ネットワーク接続
から、使用しているネットワークデバイスを選択、プロパティから共有タブを開き、共有するアダプタにVMnet8を選択する。
その後、VMnet8のIPv4のプロパティから、アドレスを vmnetdhcp.conf
の fixed-address
と同じにする。
編集が終わったら、最後に
netplan try
エラーでなければ(serviceがstopとか出てもENTER押してacceptedなら問題ない)
netplan apply
2021.04.01 追記
netplan try
するとエラーが出る
環境: 無線LAN接続
Job for netplan-wpa-wlp3s0.service canceled. An error occurred: Command '['systemctl', 'stop', 'systemd-networkd.service', 'netplan-wpa-*.service']' returned non-zero exit status 1. Reverting. Warning: Stopping systemd-networkd.service, but it can still be activated by: systemd-networkd.socket
以下の方法で解決した。
systemctl stop netplan-wpa-*.service
としてから、直接
netplan try
エラーがないことを確認したら、
またnetplan-wpa-*.service
が起動しているので手動で止める。
systemctl stop netplan-wpa-*.service
直接applyする。
netplan apply
(注意: サービス名は、起動中なら*
で補えるが、停止中はそうはいかないようなので、stop
した後でstatus
などしたいときは*
を使わない。)
systemd-resolvedでdnsを設定
/etc/systemd/resolved.conf
を編集
先のnetplanで書いたnameserversのアドレス。
NetworkManagerとは共存できないらしい?のでnetworkmanagerはdisableであると確認しておく。
[Resolve] DNS=1.1.1.1 FallbackDNS=1.0.0.1
バグ(?)対策
/etc/resolve.conf -> ../run/systemd/resolve/stub-resolv.conf
のリンクを張りなおす
unlink /etc/resolv.conf ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
これでおしまい。
ゲストに接続するには、普通に、ゲストでip a
して出てくるアドレスにリクエスト投げればよい。
おまけ
タイムゾーンの設定
timedatectl set-timezone Asia/Tokyo
JavaScriptで動体検知PWAを作ってみた【OpenCV.js】
ぶぉんなたーれ!クリスマスはリベといっしょにパネトーネを食べたい人生だった…。
クリスマスといえばサンタさん。
その姿を一目見てやろうと夜更かしすると,サンタさんは来なくなります。
夜更かしは悪い子のすることなので。
悪い子にならずに,サンタさんの姿を確認したい…
ということで,カメラに映る動くものを検知するサイトを作りました。
この記事は SLP KBIT Advent Calendar 2020 25日目の記事です。
こんな感じ。
ここで動作します。
ただし,いまのところ(2020.12.25現在),DesktopのfirefoxではA2HS(ホーム画面に追加)が利用できません。利用可否の確認
代わりに,chrome系のブラウザやEdgeを使うと利用できます。モバイル版ならfirefoxでも利用できます。
https://yassi-github.github.io/move-capture-pwa/
コードはこちら。
https://github.com/yassi-github/move-capture-pwa
PWAとは
PWAとは,プログレッシブウェブアプリの略です。
ざっくりと表現すると,DLしてインストールするよくあるアプリのようなWebアプリケーションのことです。
- URLを持つため簡単にアクセスできかつ,オフラインでも利用できる。
- 端末にDL,インストールしてアプリアイコンから起動することができる。
- さらに,カメラや位置情報など端末の周辺機器を利用した動作も可能。
他にも,通知やレスポンシブであることなどPWAに関する機能はありますが,今回実装したのは以上のようなこと。
PWAは,その名前にあることから,プログレッシブであることが重要です。
つまり,先ほど挙げた機能を使うためにはブラウザーの機能を利用することが必要ですが,ブラウザーが古くて機能が制限された状態でも,一応は使えるようであることが重要ということです。
…今回の場合はカメラが使えないと何もできないのでプログレッシブではないですね。あ,じゃあPWAではないのでは…?
OpenCV.jsについて
カメラの動きを検知するには,フレームごとの画像処理が必要です。
画像処理といえば,OpenCVですね。
今回はWebアプリケーションなので,JavaScript版となるOpenCV.jsを使います。
これを使うには,releasesからopencv-<virsion>-docs.zip
をDLし,その中にあるopencv.js
をhtmlでスクリプトとして読み込みます。
解凍したディレクトリにはたくさんのディレクトリがありますが,opencv.js
は直下にあるので無駄に探し回らないように注意です(一敗)
詳しい手順は,公式サイト(英語)の使い方チュートリアルをご覧ください。
サンプルコードも豊富にありますし,英語を読まなくても雰囲気でわかるかと思います。
実装部分
参考サイトは大体以下の通りです。
OpenCV: Getting Started with Videos
鳥取大学さんのサイトには,OpenCVのページを日本語に翻訳したものがあります。Python版ですが。
画像のしきい値処理 — OpenCV-Python Tutorials 1 documentation
PWAの機能
サービスワーカー
サービスワーカーを設定することで,キャッシュにhtmlや何やらを保存し,オフラインでの利用が可能になります。
リソースの取得リクエストを,サーバーの代わりにサービスワーカーが受け取ることでオフライン利用や高速な再読み込みが実現します。
この機能を使うには,サービスワーカーの動作を記述したjsファイルを作成し,それを登録させる必要があります。それにより,初回起動時のインストールの設定,リクエストの受け取りと応答設定,データ更新時のキャッシュ削除設定などを記述できます。
sw.js
let cacheName = 'v1'; self.addEventListener('install', function(event) { event.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll([ '/move-capture-pwa/', '/move-capture-pwa/index.html', '/move-capture-pwa/main.js', '/move-capture-pwa/opencv.js' ]); }) ); });
このようにサービスワーカーを設定したら,main.js
のほうで
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js') .then(function(register) { if (register.installing) { console.log("sw installing"); } else if (register.waiting) { console.log("sw installed"); } else if (register.active) { console.log("sw active"); } }).catch(function(error) { console.log("Registration failed with " + error); }); }
と書くことで,キャッシュストレージが利用できるようになります。
リクエストをサービスワーカーが応答するなど,他の設定はすべてsw.js
(サービスワーカーのjsファイル)のほうに記述していきます。
ホーム画面に追加(A2HS)
モバイルブラウザでQiitaを見ると画面下に出てくるあれです。
これで,ネイティブアプリのように直接起動でき,見た目もフルスクリーンにしたりできます。
この機能を実現するためには,JSON形式のマニフェストファイルを作成する必要があります。
なお,ここで必須となるアイコン画像ですが,サイズが144x144以上でないといけないので注意です。
マニフェストファイルにはこのように,アプリとしての動作設定を記述します。
manifest.webmanifest
{ "backgroung_color": "LightGreen", "display": "fullscreen", "icons": [ { "src": "icon/icon256.png", "sizes": "256x256", "type": "image/png" } ], "name": "Move capture", "short_name": "Detect", "start_url": "/move-capture-pwa/index.html" }
その後,main.js
でこのように記述すると,ホーム画面に追加機能が使えるようになります。
(この部分はMDNさんの解説コードをそのまま使っています…)
main.js
let deferredPrompt; const addBtn = document.querySelector('.a2hs-button'); addBtn.style.display = 'none'; window.addEventListener('beforeinstallprompt', (e) => { // Prevent Chrome 67 and earlier from automatically showing the prompt e.preventDefault(); // Stash the event so it can be triggered later. deferredPrompt = e; // Update UI to notify the user they can add to home screen addBtn.style.display = 'block'; addBtn.addEventListener('click', (e) => { // hide our user interface that shows our A2HS button addBtn.style.display = 'none'; // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } deferredPrompt = null; }); }); });
通知
通知を許可するように求めるダイアログを見たことがあるかもしれませんが,それのことです。
別のタブを見ていても通知が届くので,強力ですが煩わしいかもしれません。
そして,通知APIは2種類あります。
Notificationのほうは簡単に実装できますが,
Push通知のほうはサーバーを使う必要があるようで,複雑です。
なお,今回は実装していません。
カメラの使用
PWAの機能というわけではないのですが,端末の周辺機器を使うことはPWAに関連する事項なので,ここで紹介します。
getUserMediaでvideo要素をオンにして,カメラを利用しています。
ここで内カメラと外カメラのどちらを使うかや,カメラのアスペクト比を変えられるらしいのですが,うまくいかず。。
main.js
// video要素にカメラをストリーム? function startCapture(opt) { if (video.srcObject != null) { // stop both video and audio stopButton.click(); } let optionSetting = { video: true, audio: false, facingMode: null, width: { ideal: window.screen.width/2 }, height: { ideal: window.screen.height/2 }, aspectRatio: window.screen.width/window.screen.height }; if (opt === 1) { optionSetting.video.facingMode = "environment"; } else { optionSetting.video.facingMode = "user"; } navigator.mediaDevices.getUserMedia(optionSetting) .then(function(stream) { video.srcObject = stream; video.play(); }) .catch(function(err) { console.log("An error occurred! " + err); document.body.innerHTML = 'Camera is NOT available!'; window.stop(); }); }
optionSettingがうまく動いていない模様…?
動体検知の処理部分について
ここは昔にPythonで動体検知スクリプトを作ってみたときのコードをベースにしました。
OpenCV.jsでは使えない関数(accumulateWeighted)がありましたが,なくても検知はできているのでおそらく問題ないです。
輪郭を検出し,それの画素?数を前のフレームと比べ,変化量を見ています。
変化量が閾値以上なら検知する仕組みです。
また,動きの検知はオンオフが激しいため,ある程度連続して動いた場合にだけ検知するようにしました。
SlackのWebhookを使って通知させる部分について
動きを検知したら,Webhookを通してSlack botに通知させられるようにしました。
検知時の画像を表示させたかったのですが,画像はブラウザ上で一時的に表示させているだけなので無理でした。
通知メッセージはJSON形式で自由に設定できるのですが,わかりやすい例があったのでここで検証しながら作成しました。
いろいろなテンプレートを選べるので便利です。
main.js
function sendSlackNotify() { const data = { "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*Detected*\n" + new Date().toTimeString().replaceAll(' ', '_') + "\n" }, "accessory": { "type": "image", "image_url": "https://" +document.location.host +"/move-capture-pwa/icon/icon64.png", "alt_text": "Detected camera image" } } ] } const option = { "method": "POST", "body": JSON.stringify(data) } const url = document.getElementById('webhook-url').value; fetch(url, option) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => console.log('Success!!:', data)) .catch((error) => console.log('Error!!:', error)); // SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data }
コメントにあるように,なぜか構文エラーが出ちゃうんですよね。JSONの文法がミスってるのか…?まあ動くのでヨシ!
UIについて
面倒なのでほぼ触ってないです。
長くなってしまいましたが,今回は以上です。
はしがき
動作の軽量化やインタフェースの変更など,課題はたくさんあるので,また改善していきたいと思います。
では,これで2020年のアドベントカレンダーが無事終了となります。
皆さん,お疲れ様でした。ありがとうございました。
よいお年を!
JavaScriptでライフゲームを作った
この記事は SLP KBIT Advent Calendar 2020 2日目の記事です。
ライフゲームをつくりました。紹介と振り返っての感想です。
GitHub Pagesで公開してます。
https://yassi-github.github.io/LifeGame/
ライフゲームとは
ちょっと昔の数学者が考えた,生物集団の生死を簡単に再現するもの。らしい。
集団は過疎でも過密でも死んじゃう。ほどよい具合だと誕生したり生存できたりする。考えた人すごい。
で,これ,作られる模様がいいんですよ。ドット絵のピコピコ感が非常によき。
さらに,作られるパターンに名前があって,宇宙船とかパルサーとかSFみがあってすき。シュシュポッポ列車なんてのもあるみたい。かわいい。
こういうのをつくって遊べるのをつくりました。
JavaScriptでの実装
コードはここに置いてます
GitHub - yassi-github/LifeGame: 古典的で簡単なシミュレーションゲーム
機能紹介
- お絵かきの要領で,フィールドにドット絵を描きます。
- PLAYボタンでシミュレート!固定か滅亡するまでつづきます。
マスの数は3から30まで変えられるようになっています。
シミュレート中に変更を加えることもできます。
作成過程
- お絵かきをするコードを参考に,ドット絵描画ができるようにする
- 配列とドット絵をリンクさせる
- アルゴリズムを実装する
- 動作の不具合やミスを修正
- インタフェースを改善
って感じです。
参考にしたコードは「JavaScript お絵かき」などと検索してでてきたサイトで解説してたものです。
そもそも,イベントリスナーってなんぞ?みたいに,JSの基本的な書き方がよくわかっていないので,全体的な形がわかると(テンプレ的に使えるので)すごく助かる。キャンバス要素なんて存在程度しか知らなかったし,自分で暗中模索して時間つぶすぐらいなら,基礎となる手法は既存の手法を真似るのがいいよね…。
ハマったとこ
- 開発ツールの仕様
2次元配列の値を変更する過程を出力しようと,ループ中にconsole.log
して開発ツールで見たら,全部の出力が最終結果と同じになってる。
Node.jsで動かしたら正常に表示されるのに…。
試行錯誤した結果…
開発ツールのコンソールで出力される2次元配列は,そのままだとタブで閉じられてるまま。この状態では値が入っていない?みたいで,タブを開いた時点の状態が表示される仕様という認識に至った。
- 2次元配列のコピー,比較
concatだと1次元しか対応してない。でもエラーは出ないしコピーはできてるので問題ないと判断してしまった。後から値を変更したらコピー元とコピー先両方に変更が加えられてしまうのだった。
比較も,イコールで結ぶのはダメだし,どうしたものかと思っていた。Google先生に聞くとJSON.parse(JSON.stringify())
を使えばいいと。
結果,コピーも比較もJSONさんで解決。
- マスサイズの更新
入力値に基づいてマスサイズを決定するため,どうやってJSを更新させよう…?とグローバル領域に変数置いたりごちゃごちゃしていた。
結局,パラメータによってページごとに決定するものになった。ページ自体を遷移させるのは気が進まないが,他に方法見つからなかったので仕方なし。
おわりに
動くようになったときはプログラミング楽しい~~~~!!!ってなったのでこの気持ちを忘れないように精進してまいります。
まだアドカレの担当残ってるんでそっちも進めなきゃ…終わるかな…
CentOS8を入れてハマったとこメモ
そろそろLinuxを触り始めてみたいと思ったので,
最近出たらしいCentOS 8をノートPCに入れてみた.
インストールからハマったので,この際メモを作っておこうと思います.
問題点と解決法を(できるかぎり)順次載せていく予定です.
1. OSインストール時にエラーが出て入れられない
解決法: ddモードにする
インストールメディアはUSBで,Rufusを使って作成.
USBから起動する途中でハングアップしちゃうので,ファイルをDLし直したりしてました.(SHA-256のCHECKSUMをSHA-1での結果と比較しちゃってたのは内緒)
Rufusでメディアを作成するときに,iso(推奨)にするかddにするか を聞かれると思うので,ddモードに変更すると成功しました.
2. VNCクライアントからアプリの操作ができない
解決法: Xorg を使うようにする
Tiger vnc server を使用.Windows の UltraVNC Client からローカル内で直接つなぎます.
dnfでインストールしてパスワード設定,設定ファイルのコピーとUSERの書き換え,Firewalldの設定を終えていざ接続…!お,繋がった!が,アプリが起動しない…?デスクトップ画面は動くのに,ターミナルやブラウザなどのアプリは表示されない…ホストでアプリが起動してしまいます.
似たような症状は見つけられず(ググり力が試されているのか)VNCの解説記事などに書いてある設定変更を試してみまくった結果,起動不能やエラーの発生と悪化しました.systemctl start vncserver@:[ディスプレイ番号]
と vncserver :[ディスプレイ番号]
で起動されるプロセスが一致しないのは異常だったんですね…
そんな中,VNC Server構築方法が書いてある記事を発見し,救われました!GNOMEに原因があったみたい?です.
記事中にあるように,/etc/gdm/custom.conf
のWayland~
をコメントアウトするだけです!SELinuxのくだりはしなくて大丈夫でした.
他の設定ファイルを書き換える必要はありません.デフォルトでおkです.また,/usr/lib/systemd/system/vncsever@.service
のコメント通り,/etc/systemd/system
にコピーしたものの
これで,クライアントでアプリの起動が可能かつ,ホストの画面には影響しないようにできました.
今度はディスプレイ番号1に繋げない,なんてことも発生しています.ホストにおいて先ほどと同じようにアプリが表示されないこともありましたが,ログアウトすることで対処できました.おそらく,ディスプレイのセッションが2つ起動していることが関係しているのだと思われます.ps -C Xorg
やps -ef | grep Xorg
で確認できます.kill -9 [pid]
で消しても,vnc繋げたら2つに戻っちゃうのでどうしたものか…クライアントの終了方法をログアウトにしてるんですけど,それがいけなかったり…?うーん…
日本語入力の切り替えショートカットが設定不可
winではcapslock英数キーで日本語と英数の切り替えができていたので,同じようにしたかった.
キーの割り当てをしたいのにcapslockキーが割り当てられない…なんでやねん。
半角全角キーは使えるんだけどな…謎.
とりあえずここまで.