Ubuntu 18.04のSambaサーバー上にTimeMachineのバックアップ環境を構築

普段使用しているMacBook Proのバタフライキーボードにおいて特定のキーが繰り返し入力されたりバッテリーが膨張しつつあったりするようになってしまったため、TimeMachineを用いてバックアップを取ろうと思ったが、調べたところ最近のSambaでは特定の条件が揃えばネットワーク越しにバックアップを取ることが可能であることが分かったため、その際に設定した環境構築のメモを下記に記す。

手順

  1. ppaを追加し新しめのバージョンのsambaを入れる
  2. /etc/samba/smbd.confを編集する
  3. avahi-daemonの設定をする
  4. sambaのdaemonをrestartする

1.ppaを追加し新しめのバージョンのsambaを入れる

2020/10/31現在、aptでインストールされるsambaはバージョン4.7.6であるが、 バージョン4.8以降でなければTimeMachineバックアップ用の設定ができないのでリポジトリを追加した上で新しいバージョンのsambaをインストールする。 このQAのAnswerを参考に下記のようにppaを追加する。

$ sudo add-apt-repository ppa:linux-schools/samba-latest
$ sudo apt-get dist-upgrade
$ sudo apt-get install samba

バージョンは下記のように確認可能である。

$ smbd -V
> Version 4.10.18

2. /etc/samba/smbd.confを編集する

様々なサイトを参考に下記のような項目を追加した(が不要な設定もあるかもしれない)。 pathは適宜共有したいディレクトリに書き換えること。またfruit:time machine max sizeの項目は適切なサイズを設定しておかないとTimeMachineはディスクをフルに使おうとしてしまうので注意が必要である。 正確な設定項目についてはマニュアルを見たほうが良い。

[global]

# Special configuration for Apple's Time Machine
vfs objects = catia fruit streams_xattr 
fruit:model = MacPro
fruit:advertise_fullsync = true
fruit:aapl = yes

[TimeMachine]
  comment = Backup for Mac Computers
  path = /path/to/share
  writable = yes
  browsable = yes
  guest ok = yes
  fruit:time machine = yes
  fruit:time machine max size = 600G
  durable handles = yes
  kernel oplocks = no
  kernel share modes = no
  posix locking = no

3. avahi-daemonの設定をする

上記設定を施したのちにsambaのデーモンを再起動した時点で使えるかと思ったがどうやら別途avahi-daemon(mDNS)の設定をしなければならなかったことが分かった。 この記事を参考に下記のような設定を記述したファイルを/etc/avahi/services/timemachine.serviceとして作成した。 なおここではavahi-daemon自体はaptで別途インストール済みであることを想定している。

<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
 <name replace-wildcards="yes">%h</name>
 <service>
   <type>_smb._tcp</type>
   <port>445</port>
 </service>
 <service>
   <type>_adisk._tcp</type>
   <txt-record>dk0=adVN=TimeMachine,adVF=0x82</txt-record>
 </service>
</service-group>

ここで<txt-record>dk0=adVN=TimeMachine,adVF=0x82</txt-record>TimeMachineとなっている部分は/etc/samba/smb.confの共有名と共通にしておく必要がある。

4. sambaのdaemonをrestartしMacOSからTimeMachineを実行

3までを実施した後で、sudo systemctl restart avahi-daemonおよびsudo systemctl restart smbdを実行しavahi-daemonとsmbdをrestartさせると MacOS(手元のマシンはCatalina 10.15.7)のシステム環境設定にあるTimeMachineのディスクを選択の項目から設定したディスクが探せるようになっているはずである。 保存先として設定するとIDとパスワードを入力するダイアログが出るため書き込み権限のあるユーザーIDを打ち込むとバックアップが進行するようになった。

参考にさせていただいたサイト

  • https://askubuntu.com/questions/1166875/unable-to-built-samba-4-10-6-from-source
    • ppaからのsambaをインストールする際の参考にしました
  • https://ideal-reality.com/computer/server/time-machine-samba/
    • 設定の参考にしました
  • https://qiita.com/upsilon/items/c368726845f89cb0ffe9
    • avahiの設定を参考にしました

Streamlitを用いた音響信号処理ダッシュボードの開発(Tokyo BISH Bash #03発表資料)

Streamlitを用いた音響信号処理ダッシュボードの開発(Tokyo BISH Bash #03発表資料)

本記事はTokyo BISH Bash #03の発表資料です。

In [1]:
from IPython.display import IFrame
IFrame("https://www.hiromasa.info/slide/22.slides.html", "100%", "450px")
Out[1]:

自己紹介およびPyData Osakaの紹介

  • 別スライドで説明します

本日の概要

  • Streamlitの紹介
  • 実例を踏まえた使用方法の紹介
  • Herokuへのデプロイ方法の紹介
  • その他

実行環境

Streamlitとは

  • Webブラウザで動作するダッシュボードを作成するためのライブラリ
    • 複数のコンポーネントが連携して動作するもの
  • Pythonのみで記述が可能
    • plotly/dashと比較されることが多い

どんな場面で必要か

  • 実装したアルゴリズムを他者に試してもらいたい
    • Webフロントエンドがあると便利
  • パラメータ変更による試行錯誤が必要な場面
    • 調整ツールとして使用

実例

  • 今回作成したダッシュボードを簡単に紹介

Dashとの違い

  • Towards Data Scienceの記事
    • Dash
      • プロダクション/エンタープライズ環境での実行に主眼
    • Streamlit
      • ラピッドプロトタイピングに主眼

image.png

Streamlitの魅力

  • 手軽にフロントエンドが構築可能
    • javascript不要、スクリプトライクな記述
    • 各種pythonライブラリにGUIを付与
  • 豊富な描画ライブラリ
    • matplotlib, plotly, altair, ...
    • Dashはplotlyを前提

本発表におけるバージョン

基本機能の紹介

導入方法

  • pip install streamlit

実行方法

In [2]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
Overwriting app.py
In [3]:
!streamlit run app.py
  You can now view your Streamlit app in your browser.

  Network URL: http://172.17.0.2:8501
  External URL: http://49.251.189.62:8501

^C
  Stopping...

image.png

  • ノートブックが固まるため以降はターミナル上からstreamlitコマンドを実行

jupyterlabが8888番ポートのみ稼働している環境上でのアクセス

  • jupyter-server-proxyの導入によるproxyアクセス

アプリ更新時の反映が容易

  • ファイルの上書きを検知しRerunボタンを自動表示

image.png

In [4]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
st.write("# Where is Rerun button?")
Overwriting app.py

image.png

スクリプトエラーもブラウザ上に表示

  • デバッグも容易
In [5]:
%%writefile app.py
import streamlit as st

st.write("# Hello, Streamlit!")
raise ValueError
Overwriting app.py

image.png

st.write

  • 引数に与えたオブジェクトを画面に表示
  • markdownはHTMLに変換して表示
  • listやdictは階層表示
  • ndarrayやpandasのDataFrameはテーブルとして表示
In [6]:
%%writefile app.py
import numpy as np
import pandas as pd
import streamlit as st

st.write([1, 2, 3])
st.write({"hello": "world!"})
st.write(np.arange(10).reshape(1,10))
st.write(pd.DataFrame(np.random.randn(10, 4), columns=["1", "2", "3", "4"]))
Overwriting app.py

image.png

本日紹介するダッシュボード

ブランチ

  • master
  • heroku
    • Herokuへのアップロードに必要なファイルを格納
  • espnet2
    • 3.6.x系で動作させるため別ブランチ
    • Steramlitのバージョンは0.66.0

github上のstreamlitアプリ

  • 一般的には直接実行可能
    • streamlit run https://github.com/user/repos.git/master/app.py
  • 今回のアプリはimportの都合でエラーが生じる

窓関数ビューワの構築

  • scipy.signalを使用
  • Streamlitによるアプリ構築をライブコーディング的に実施

image.png

窓関数の選択インタフェース

  • サイドバーにselectboxで表示
  • サイドバー上への配置
    • st.sidebar.xxx
    • st.xxxと本体に配置できるウィジェットは何でも配置可能

st.selectbox

st.selectbox(ラベル名, 選択肢のリスト, デフォルト項目のインデックス)

image.png

In [7]:
%%writefile app.py
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]
Overwriting app.py
  • %%writefile -a app.pyapp.pyに追記
In [8]:
%%writefile -a app.py

win_name = st.sidebar.selectbox("window", windows, 4)
st.write(win_name)
Appending to app.py
  • この時点の見た目

image.png

窓関数の窓長およびFFT長の取得

  • 同様にst.selectboxで2の累乗の値を取得
In [9]:
%%writefile -a app.py
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_name, Nx, nfft)
Appending to app.py
  • この時点での見た目

image.png

窓関数の取得および描画

  • 窓関数の取得
    • scipy.signal.get_window
    • scipy.fft.fftで周波数分析
  • 描画
    • matplotlibをひとまず使用
  • 必要なライブラリをimport
In [10]:
%%writefile -a app.py
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
Appending to app.py
  • 窓関数を取得し周波数特性を計算
In [11]:
%%writefile -a app.py
eps = 1.e-12
win = sg.get_window(win_name, Nx)
W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
W = fft.fftshift(W)
Appending to app.py
  • matplotlibを用いて描画
In [12]:
%%writefile -a app.py
fig, axes = plt.subplots(2, 1)
axes[0].plot(win)
axes[1].plot(W)
st.pyplot(fig)
Appending to app.py

st.pyplot

  • matplotlibのfigureを描画
    • st.pyplot(fig)
  • 使い慣れたmatplotlibを使える
    • が、javascriptの恩恵が得られない
  • この時点での見た目

image.png

streamlitの描画ライブラリ

  • st.line_chart, st.area_chart, st.bar_chart
    • st.altair_chartのsyntax sugar
    • 手軽に使える反面細かい設定は不可能
  • その他vega, plotly, bokehなども使用可能
In [13]:
%%writefile -a app.py
st.line_chart(win)
st.line_chart(W)
Appending to app.py
  • st.line_chartの見た目

image.png

この時点でのファイルの内容

  • 通常の実行スクリプトに近い
  • 手軽にフロントエンドの作成が可能
In [14]:
!cat app.py | pygmentize
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]

win_name = st.sidebar.selectbox("window", windows, 4)
st.write(win_name)
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_name, Nx, nfft)
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
eps = 1.e-12
win = sg.get_window(win_name, Nx)
W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
W = fft.fftshift(W)
fig, axes = plt.subplots(2, 1)
axes[0].plot(win)
axes[1].plot(W)
st.pyplot(fig)
st.line_chart(win)
st.line_chart(W)

複数の窓関数を同時に描画したい

  • st.multiselect(ラベル名, 選択肢のリスト, デフォルト項目の要素のリスト)
    • 複数の選択項目をリストで返す
    • 第三引数は選択肢中に存在する要素のリスト

image.png

  • 先程と同じ
In [15]:
%%writefile app.py
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]
Overwriting app.py
  • 先程と同じ
In [16]:
%%writefile -a app.py

import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
Appending to app.py
  • st.multiselectを使用
In [17]:
%%writefile -a app.py

# ここから先が異なる
win_names = st.sidebar.multiselect("window", windows, [windows[4]])
st.write(win_names)
Appending to app.py
  • win_namesst.writeする以外は同じ
In [18]:
%%writefile -a app.py
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_names, Nx, nfft)
Appending to app.py
  • win_namesをループで処理
In [19]:
%%writefile -a app.py
eps = 1.e-12
for win_name in win_names: # ループとして処理
    win = sg.get_window(win_name, Nx)
    W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
    W = fft.fftshift(W)
Appending to app.py
In [20]:
%%writefile -a app.py
    # ループの続き
    st.write(win_name)
    st.line_chart(win)
    st.line_chart(W)
Appending to app.py
  • 複数の窓が繰り返し描画される

image.png

In [21]:
!cat app.py | pygmentize
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]

import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft

# ここから先が異なる
win_names = st.sidebar.multiselect("window", windows, [windows[4]])
st.write(win_names)
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_names, Nx, nfft)
eps = 1.e-12
for win_name in win_names: # ループとして処理
    win = sg.get_window(win_name, Nx)
    W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
    W = fft.fftshift(W)
    # ループの続き
    st.write(win_name)
    st.line_chart(win)
    st.line_chart(W)

複数の窓をまとめて描画したい

  • pandasのDataFrameの複数カラムにデータを押し込む
  • st.line_chartに渡す
  • 先程と同じ
In [22]:
%%writefile app.py
import streamlit as st

windows = ["boxcar", "triang", "blackman", "hamming", "hann", "bartlett", "flattop",
           "parzen", "bohman", "blackmanharris", "nuttall", "barthann"]
Overwriting app.py
  • pandasを追加
In [23]:
%%writefile -a app.py
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as sg
import scipy.fft as fft
import pandas as pd  # 追加
Appending to app.py
  • 先程と同じ
In [24]:
%%writefile -a app.py

win_names = st.sidebar.multiselect("window", windows, [windows[4]])
st.write(win_names)
Appending to app.py
  • 先程と同じ
In [25]:
%%writefile -a app.py
two_powers = [2**i for i in range(16)]
Nx = st.sidebar.selectbox("Window Length", two_powers, 8)
nfft = st.sidebar.selectbox("FFT Length", two_powers, 10)

st.write(win_names, Nx, nfft)
Appending to app.py
  • 窓と周波数特性をlistに格納
In [26]:
%%writefile -a app.py
eps = 1.e-12
win_ary = []; W_ary = []
for win_name in win_names: # ループとして処理
    win = sg.get_window(win_name, Nx)
    W = 20.0 * np.log10(np.abs(fft.fft(win, nfft)) + eps)
    W = fft.fftshift(W)
    win_ary.append(win)
    W_ary.append(W)
Appending to app.py
  • DataFrameに格納
In [27]:
%%writefile -a app.py
fs = 16000.0
t = np.arange(Nx)
f = (fs / nfft) * np.arange(-nfft/2, nfft/2)

df_win = pd.DataFrame(np.array(win_ary).T, index=t, columns=win_names)
df_W   = pd.DataFrame(np.array(W_ary).T,   index=f, columns=win_names)
Appending to app.py
In [28]:
%%writefile -a app.py
st.line_chart(df_win)
st.line_chart(df_W)
Appending to app.py