独立な確率変数の共分散がゼロであること
[mathjax] 共分散と相関係数の定義について過去に書いていた。 そもそも共分散が発生するのは、2つの確率変数が連動して動くから。 2つの確率変数が独立している場合は、共分散、相関係数共にゼロ。 共分散の定義 まず、共分散、相関係数の定義はこの通り。 2次元のデータ((x_1,y_1),(x_2,y_2),cdots,(x_n,y_n))が与えられた場合、 変数(x)と(y)の相関係数(r_{xy})は、それぞれの標準偏差(S_x,S_y)と、共分散(C_{xy})を使って以下となる。 begin{eqnarray} r_{xy} &=& frac{C_{xy}}{S_x S_y} \\ &=& frac{sum_{i=1}^n(x_i-bar{x})(y_i-bar{y})/n}{sqrt{sum_{i=1}^n{(x_i-bar{x})^2}/n} sqrt{sum_{i=1}^n{(y_i-bar{y})^2}/n}} \\ &=& frac{sum_{i=1}^n(x_i-bar{x})(y_i-bar{y})}{sqrt{sum_{i=1}^n{(x_i-bar{x})^2}} sqrt{sum_{i=1}^n{(y_i-bar{y})^2}}} \\ end{eqnarray} [clink url=\"https://ikuty.com/2018/08/13/correlation_coefficient/\"] [arst_adsense slotnumber=\"1\"] そもそもの共分散 確率変数(X),(Y)があったとする。それぞれの期待値は(E(X)),(E(Y))、分散は(V(X),V(Y))。 定義通りに(V(X+Y))を式展開していくと以下の通りになる。 begin{eqnarray} V(X+Y) &=& E(((X+Y)-mu_{X+Y})^2) \\ &=& E((X+Y-mu_x-mu_y)^2) \\ &=& E(((X-mu_x) + (Y-mu_y))^2) \\ &=& E((X-mu_x)^2) + E((Y-mu_y)^2) + 2E((X-mu_x)(Y-mu_y)) \\ &=& V(X) + V(Y) + 2E((X-mu_x)(Y-mu_y)) \\ &=& V(X) + V(Y) + 2C_{xy} end{eqnarray} ここで、(C_{xy}=2E((X-mu_x)(Y-mu_y)))を共分散としている。 (V(X+Y))は、(V(X))と(V(Y))の和に(C_{xy})で補正をかけた値になっている。 では、(X)と(Y)が独立であるとなぜ(C_{xy}=0)になるのか。 (C_{xy})を式変形していくと以下のようになるが、 begin{eqnarray} frac{1}{2} C_{xy} &=& E((X-mu_x)(Y-mu_y)) \\ &=& E(XY)-mu_yE(X)-mu_xE(Y) +mu_x mu_y \\ &=& E(XY) -mu_x mu_y - mu_x mu_y + mu_x mu_y \\ end{eqnarray} (X)と(Y)が独立であると(E(XY)=E(X)E(Y)=mu_x mu_u)となるから、 begin{eqnarray} frac{1}{2} C_{xy} &=& E(XY) -mu_x mu_y - mu_x mu_y + mu_x mu_y \\ &=& mu_x mu_y-mu_x mu_y - mu_x mu_y + mu_x mu_y \\ &=& 0 end{eqnarray} こうやって、独立であるなら共分散がゼロといえる。 [arst_adsense slotnumber=\"1\"]
PostgreSQL スキーマをコピーする
スキーマをコピーする方法はない。 代わりに以下の方法で同じ効果を得る。 スキーマ名Aをスキーマ名Bに変更する スキーマ名Bの状態でpg_dumpする スキーマ名Bをスキーマ名Aに変更する スキーマ名Bを作成する pg_dumpしたファイルをリストアする Statementは以下の通り。 $ psql -U user -d dbname -c \'ALTER SCHEMA old_schema RENAME TO new_schema\' $ pg_dump -U user -n new_schema -f new_schema.sql dbname $ psql -U user -d dbname -c \'ALTER SCHEMA new_schema RENAME TO old_schema\' $ psql -U user -d dbname -c \'CREATE SCHEMA new_schema\' $ psql -U user -q -d dbname -f new_schema.sql $ rm new_schema.sql [arst_adsense slotnumber=\"1\"]
AWS常時SSL リダイレクトループしない.htaccessの書き方
HTTPSを強制するために .htaccess に細工をするのは有名。例えば以下のような書き方が王道。 RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L] これをそのままElasticBeanstalkにデプロイするとリダイレクトループが発生する。正確に書くとリバースプロキシ(ロードバランサ)が有効になっている場合にリダイレクトループが発生する。 原因 原因についてはココが詳しい。ざっくりまとめると、 ロードバランサが443へのアクセスを80へのアクセスに変換する .htaccess内の RewriteCond ${HTTPS} が永遠に on にならず、リダイレクトの度にRewriteRule が走ってしまう 元々のアクセスが https か http のどちらかが分かれば良いのだが、上記の挙動のせいで、https にリダイレクトしたとしても http からアクセスされたことになり、これが永遠に繰り返されてしまう。 解決策 (記事主さんが)無茶苦茶泥臭く挙動を追跡したところ、ロードバランサに到着した元のアクセスが http のときに限り、X-Forwarded-Proto というヘッダが付与され値が入るらしい。なので、X-Forwarded-Protoヘッダの内容を http か https かの判断基準にすれば良い、というのが基本的なアイデア。本人も言っているが、it\'s just an empiric result... である。 その .htaccess が以下 RewriteEngine On # Force HTTPS RewriteCond %{HTTP:X-Forwarded-Proto} !=https RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R,L] これを ElasticBeanstalkにデプロイすると見事に動作する。 [arst_adsense slotnumber=\"1\"] 開発環境との共存 開発もAWSで行っていればこれで良いのだがそうでない場合も多いと思う。 上記のAWS用.htaccessを非AWSな開発環境に持ってくると今度は RewriteCond %{HTTP:X-Forwarded-Proto} !https が常に真になり、リダイレクトループが発生する。 あっちが立てばこっちが立たない! いろいろ試行錯誤した結果、以下なら両立できた。(2017/7/8訂正) RewriteEngine On # Force HTTPS RewriteCond %{HTTPS} !=on RewriteCond %{HTTP:X-Forwarded-Proto} !=https RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R,L] 根拠となる X-Forwarded-Proto がとっても経験的!なので、いつの日か使えなくなる日が来るかもしれない。 [arst_adsense slotnumber=\"1\"]
正規分布に従う確率変数の二乗和はカイ二乗分布に従うことの証明
[mathjax] 母平均(mu)、標準偏差(sigma)の正規分布から(n)個の標本を無作為抽出したとき、 (n)個の標本について二乗和(V)を計算した場合(V)はどのような分布をするか。 begin{eqnarray} V = x_1^2 + x_2^2 + cdots + x_n^2 end{eqnarray} (V)の分布は自由度nのカイ二乗分布になる。 なお、実際にデータを表示してみた記事は以下。 [clink url=\"https://ikuty.com/2019/08/12/chi-square-distribution_handson/\"] 証明の式変形が気持ち良いことで有名?なので1度やってみる。 証明が奇跡的だったのでまとめてみる 自由度(n)のカイ二乗分布の確率密度関数。 これでもかっ、というくらいにいろいろ乗っかってる。 begin{eqnarray} f_n(x) = frac{1}{2^{frac{n}{2}}Gamma({frac{n}{2}})}x^{frac{n}{2}-1}e^{-frac{x}{2}} end{eqnarray} 標準正規分布と同じ扱いで、 (x)に関する積分が1になるようにガンマ関数による定数項がついてる。 勢い以下のような見方になる。 begin{eqnarray} f_n(x) = left( frac{1}{2^{frac{n}{2}}Gamma({frac{n}{2}})} right) x^{frac{n}{2}-1} e^{-frac{x}{2}} end{eqnarray} [arst_adsense slotnumber=\"1\"] だから何なのか、と思うけども、一度は証明を見ておくと良し、という意見がある。 ド直球に、標準正規分布の確率密度関数から2乗和の分布を求めようとして、 奇跡的に上記の確率密度関数になってかなり面白かったのでまとめてみた。 (n=1)のときの証明 (X)が標準正規分布に従うときの確率密度関数は以下。 begin{eqnarray} f(x) = frac{1}{sqrt{2pi}}e^{-frac{x^2}{2}} end{eqnarray} このとき(X)の2乗の分布(Y=X^2)の分布を考えようとするとき、 (Yle y)となる確率(P(Yle y))は、 begin{eqnarray} P(Yle y) = P(-sqrt{y} le X le sqrt{y}) end{eqnarray} となるので、(Y)の確率分布関数(F(y))は、 begin{eqnarray} F(y) &=& int_{-sqrt{y}}^{sqrt{y}}f(x)dx \\ &=& 2 int_{0}^{sqrt{y}}f(x)dx end{eqnarray} (y=x^2)という変数変換をして微分すると、(frac{dy}{dx}=2x)から、(dy=2xdx=2sqrt{y}dx)。 これを使って書き直すと、(コレ考えたやつ頭おかしい...) begin{eqnarray} F(y) &=& 2int_{0}^{sqrt{y}}frac{1}{sqrt{2pi}}e^{-frac{x^2}{2}}dx \\ &=& 2 frac{1}{2} int_{0}^{sqrt{y}}frac{1}{sqrt{2pi}y}e^{-frac{x^2}{2}}dy \\ &=& int_{0}^{sqrt{y}}frac{1}{sqrt{2pi}y}e^{-frac{y}{2}}dy \\ &=& int_{0}^{sqrt{y}}frac{1}{2^{frac{1}{2}}sqrt{pi}}y^{-frac{1}{2}}e^{-frac{y}{2}}dy \\ end{eqnarray} ガンマ関数(Gamma(n))って何だっけ...、というところで力尽きた。 (Gamma(frac{1}{2}))だけ複素数にならず(sqrt{pi})になる。 (F(y))をガンマ関数を入れて書き直すと、 begin{eqnarray} F(y) = int_{0}^{sqrt{y}}frac{1}{2^{frac{1}{2}}Gamma(frac{1}{2})}y^{-frac{1}{2}}e^{-frac{y}{2}}dy \\ end{eqnarray} この式は奇跡的に(n=1)のとき、カイ二乗分布の確率密度関数になってる。 begin{eqnarray} f_1(x) &=& frac{1}{2^{frac{1}{2}}Gamma({frac{1}{2}})}x^{frac{1}{2}-1}e^{-frac{x}{2}} end{eqnarray} [arst_adsense slotnumber=\"1\"] (n ge 2)のときの証明 数学的帰納法で証明する。このワード、何年振りだろうか...。 Wikipediaによると、 数学的帰納法(すうがくてききのうほう、英: mathematical induction)は自然数に関する命題 P(n) が全ての自然数 n に対して成り立っている事を証明するための、次のような証明手法である。 P(1) が成り立つ事を示す。 任意の自然数 k に対して、「P(k) ⇒ P(k + 1)」が成り立つ事を示す。 以上の議論から任意の自然数 n について P(n) が成り立つ事を結論づける。 準備として、確率密度関数の畳み込みについて。 2つの確率変数(X_1)、(X_2)が互いに独立に標準正規分布に従い、 (Y_1=X_1^2)、(Y_2=X_2^2)とおいたとき、(Z=Y_1+Y_2)が従う確率密度関数を求める。 確率変数(Y_1)、(Y_2)双方とも、確率密度関数(h_1(x))に従うときは、 (x=y_1+y_2, y_1,y_2 ge 0, z ge 0)に注意して、 以下を計算することで確率変数(Z=Y_1+Y_2=X_1^2+X_2^2)が従う確率密度関数が求まる。 begin{eqnarray} h_2(x) = int_0^{z}h_1(y)h_1(z-y)dy end{eqnarray} (P(1))は既に示されている。任意の自然数 (n) に対して、「(P(n) ⇒ P(n + 1))」が成り立つ事を示す。 (Y=X_1^2+X_2^2+cdots+X_{n-1}^2)が自由度(n-1)のカイ二乗分布に従い、 (X_n^2)が自由度(1)のカイ二乗分布に従うとき、(Y+X_n)が自由度(n)のカイ二乗分布に従うことを示す。 示すのは以下。 begin{eqnarray} f_n(x) = int_{0}^{x}f_{n-1}(t)f_1(x-t)dt end{eqnarray} 右辺を展開していく。 begin{eqnarray} int_{0}^{x} frac{1}{2^{frac{n-1}{2}}Gamma(frac{n-1}{2})}t^{frac{n-3}{2}} e^{-frac{x}{2}} cdot frac{1}{2^{frac{1}{2}}Gamma({frac{1}{2})}}t^{-frac{1}{2}}e^{-frac{x}{2}} end{eqnarray} (t)に対する定数項を積分の外に出せる。 begin{eqnarray} frac{e^{-frac{x}{2}}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})sqrt{pi}} int_{0}^{x}t^{frac{n-3}{2}}(x-t)^{-frac{1}{2}}dt end{eqnarray} ここで(u=frac{t}{x})とおくと、(frac{du}{dt}=frac{1}{x})だから、(dt=xdu)。 変数を置き換える。奇跡的に(x)が積分の外に出る。 begin{eqnarray} frac{e^{-frac{x}{2}}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})sqrt{pi}} int_{0}^{1}(ux)^{frac{n-3}{2}}(x-ux)^{frac{1}{2}}xdu \\ = frac{e^{-frac{x}{2}}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})sqrt{pi}} int_{0}^{1}x^{frac{n-3}{2}} u^{frac{n-3}{2}} x^{frac{1}{2}}(1-u)^{frac{1}{2}}xdu \\ = frac{e^{-frac{x}{2}}x^{frac{n-3}{2}-frac{1}{2}+1}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})Gamma(frac{1}{2})} int_{0}^{1} u^{frac{n-3}{2}}(1-u)^{-frac{1}{2}}du end{eqnarray} 積分の部分は、昔みた覚えがあるけど、もう力尽きたので結論だけ... 以下の関係式があって、 begin{eqnarray} B(p,q) &=& int_{0}^{1} x^{p-1}(1-x)^{q-1}dx \\ &=& frac{Gamma(p)Gamma(q)}{Gamma(p+q)} end{eqnarray} (p,q)を以下のように選ぶと、 begin{eqnarray} B(frac{n-1}{2},frac{1}{2}) = frac{Gamma(frac{n-1}{2})Gamma(frac{1}{2})}{Gamma(frac{n}{2})} end{eqnarray} これを使って式を書き直すと、一気に約分されて自由度(n)のカイ二乗分布の式が現れる。 begin{eqnarray} frac{e^{-frac{x}{2}}x^{frac{n-3}{2}-frac{1}{2}+1}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})Gamma(frac{1}{2})} int_{0}^{1} u^{frac{n-3}{2}}(1-u)^{-frac{1}{2}}du \\ = frac{e^{-frac{x}{2}}x^{frac{n-3}{2}-frac{1}{2}+1}}{2^{frac{n}{2}}Gamma(frac{n-1}{2})Gamma(frac{1}{2})} frac{Gamma(frac{n-1}{2})Gamma(frac{1}{2})}{Gamma(frac{n}{2})} \\ = frac{1}{2^{frac{n}{2}}Gamma({frac{n}{2}})}x^{frac{n}{2}-1}e^{-frac{x}{2}} \\ = f_n(x) end{eqnarray} Q.E.D.!! あぁ、これは気持ち良い。 [arst_adsense slotnumber=\"1\"]
標本の標準偏差とルートnの法則
[mathjax] 平均(mu)、標準偏差(sigma)からなる母集団から標本を取り出したとき、 標本の平均は母集団の平均(mu)に収束する。 では、もう一つの統計量である標準偏差はどうか。 意外と簡単にわかるようなのでまとめてみる。 誤差伝播法則 まず、下準備として、加法の誤差の見積もりについて。 今、(M_1)というサンプルが誤差(epsilon_1)、(M_2)というサンプルが誤差(epsilon_2)を持つとする。 つまり、それぞれ(M_1pmepsilon_1)、(M_2pmepsilon_2)。 その上で、((M_1pmepsilon_1) pm (M_2pmepsilon_2) ) について誤差の項をどう見積れるか、という話。 例えば以下の関係があったとき、 begin{eqnarray} z &=& f(x,y) end{eqnarray} 以下とすると、 begin{eqnarray} x &=& x_0 pm e_x \\ y &=& y_0 pm e_y end{eqnarray} (z)は、以下のようになる。 begin{eqnarray} z &=& z_0 pm e_z end{eqnarray} ここで(e_z)は以下となる(公式)。 偏微分とか何年振りだよ..と、思うがなんとなく確率の式より把握しやすい。 begin{eqnarray} e_z = sqrt{left( frac{partial f}{partial x} right)^2 e_x^2 + left( frac{partial f}{partial y} right)^2 e_y^2} end{eqnarray} 最初のサンプルと誤差を上記に入れてみると、 begin{eqnarray} sigma &=& sqrt{left( frac{partial (M_1+M_2)}{partial M_1} epsilon_1 right)^2 + left( frac{partial (M_1+M_2)}{partial M_2} epsilon_2 right)^2} = sqrt{ epsilon_1^2 + epsilon_2^2 } end{eqnarray} 両辺2乗して、 begin{eqnarray} sigma^2 &=& left( frac{partial (M_1+M_2)}{partial M_1} epsilon_1 right)^2 + left( frac{partial (M_1+M_2)}{partial M_2} epsilon_2 right)^2 = epsilon_1^2 + epsilon_2^2 end{eqnarray} ここから一番最初に戻ると、 begin{eqnarray} (M_1 pm epsilon_1) pm (M_2 pm epsilon_2) end{eqnarray} 上の誤差伝播式から以下が導かれる。 誤差項は以下の通りとなる様子。 begin{eqnarray} (M_1 pm M_2 ) pm sqrt{( epsilon_1^2 + epsilon_2^2 )} end{eqnarray} [arst_adsense slotnumber=\"1\"] ルートnの法則 母集団から(N)個のサンプルを取り出したときの平均は以下の通り。 begin{eqnarray} bar{x} = frac{x_1+x_2+cdots+x_N}{N} end{eqnarray} どの(x_i)も同じ母集団から取り出したサンプルなので、 それぞれの標準偏差は以下の通り全て同じ。 begin{eqnarray} sigma_1 = sigma_2 = cdots = sigma_N = sigma end{eqnarray} (bar{x})は真の値に誤差を加算した値であるが、誤差項は誤差伝播法則から以下の通りとなる。 begin{eqnarray} sqrt{sigma_1^2 + sigma_2^2 + cdots + sigma_N^2} = sqrt{sigma^2 + sigma^2 + cdots + sigma^2} = sqrt{N}sigma end{eqnarray} サンプル1個あたりの誤差、つまり標準偏差は、 begin{eqnarray} frac{sqrt{N}sigma}{N} = frac{sigma}{sqrt{N}} end{eqnarray} まとめ 平均(mu)、標準偏差(sigma)からなる母集団から標本を取り出したとき、 標本の平均は母集団の平均(mu)と等しい。 標本の標準偏差は( frac{sigma}{sqrt{N}} )である。 特に、標準偏差が(1/sqrt{N})倍となり、母集団と比較してより狭い範囲に値が集中する。 [arst_adsense slotnumber=\"1\"]
External Network Accessを使ってSnowflakeとFitbitAPIを繋いでみた話
FitbitはAPIがしっかり整備されていて、OAuth2 endpoint経由でデータが取り放題。 せっかくなので、話題のExternal Network Access(2023年12月現在 PuPr)を試してみようと思う。 つまり、FitbitAPI→Snowflakeをやってみようと思う。 Fitbit APIを使用するにはOAuth2.0 Authorizationを通す必要がある。 Snowflakeの公式にOAuth2.0 Endpoint経由でGoogle翻訳APIと連携する段取りが書かれていて、 それをそのままFitbit APIのものに差し替えるだけで動いた。 外部ネットワークアクセスの例 外部ネットワークアクセスについては以下。 [clink implicit=\"false\" url=\"https://docs.snowflake.com/ja/developer-guide/external-network-access/creating-using-external-network-access#label-creating-using-external-access-integration-network-rule\" imgurl=\"https://www.snowflake.com/wp-content/uploads/2017/01/snowflake-logo-color-300x69.png\" title=\"外部アクセス統合の作成と使用\" excerpt=\"特定の外部ネットワークロケーションへのアクセスを有効にするには、外部ロケーションのリストと使用を許可されるシークレットのリストを指定する外部アクセス統合を作成します。UDF の作成時、あるいは CREATEFUNCTION または CREATEPROCEDURE でプロシージャを作成する際に、 EXTERNAL_ACCESS_INTEGRATIONS 句を使用してこの統合を参照することで、ハンドラーコードが外部ロケーションとの認証コードにシークレットを使用できるようになります。\"] 2016年6月に書いた記事。phpで検証をしていた。 この辺りからバッテリーがダメになる度に新しいFitbit Charge(1,2,3)を買って溜めてきた。 この間、FitbitがGoogleに買われてしまったり、スマホアプリが大幅に変わったり、色々あった。 基本的な機能はずっと動いているので、7年分のデータが溜まっているんじゃないかな、と期待。 [clink url=\"https://ikuty.com/2016/06/07/fitbitapi-authenticate-grant-flow/\"] Fitbit API側の準備 OAuth2連携に必要な情報を dev.fitbit.com から取得する必要がある。 Authorization Code Grant Flow with PKCE こちらを参考にさせていただいた。 [clink implicit=\"false\" url=\"https://www.zenryoku-kun.com/post/fitbit-api#register-app\" imgurl=\"https://www.zenryoku-kun.com/home/sakura-400w.jpg\" title=\"FitbitのWeb APIを実行する方法\" excerpt=\"Fitbit Sense2を購入しました。はじめてのスマートウォッチです。Fitbitデバイスでは、心拍数や歩数等、収集したデータをWeb APIで取得することが可能です。さっそく使って遊んでみようと思ったら、Web APIの認証がなかなか通らない、、、ドキュメントはとても充実しているのですが、OAuth2.0の認証パターンがImplicit Grant Flowの場合、Authorization Code Grant Flowの場合、PKCEを使う場合、、、などなど、情報量がとにかく多く混乱してしまいました。何はともあれ、何とか認証を通して、こんな感じで歩数などのアクティビティ情報や、心拍数や血中酸素濃度(SpO2)を取得することが出来ました。\"] 以下を準備すればOK。 access-token refresh-token client-id Snowflakeでリソース作り Snowsightでポチポチとリソースを作っていく。 USE ROLE SYSADMIN; -- 外部ロケーションを表すネットワークルールの作成 -- CREATE OR REPLACE NETWORK RULE fitbit_apis_network_rule MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = (\'api.fitbit.com\'); -- 外部ロケーションとの認証に必要なOAuth認証情報を保持するセキュリティ統合の作成 -- CREATE OR REPLACE SECURITY INTEGRATION fitbit_api_oauth TYPE = API_AUTHENTICATION AUTH_TYPE = OAUTH2 OAUTH_CLIENT_ID = \'\' OAUTH_CLIENT_SECRET = \'\' OAUTH_TOKEN_ENDPOINT = \'https://api.fitbit.com/oauth2/token\' OAUTH_AUTHORIZATION_ENDPOINT = \'https://www.fitbit.com/oauth2/authorize\' ENABLED = TRUE; -- セキュリティ統合に含まれる認証情報を表すシークレットの作成 -- CREATE OR REPLACE SECRET fitbit_api_oauth_token TYPE = oauth2 API_AUTHENTICATION = fitbit_api_oauth OAUTH_REFRESH_TOKEN = \'\'; 最後に外部アクセス統合を作成する。 ストレージ統合や、Notification統合など、統合の作成にはACCOUNTADMINが必要で、 同様に外部アクセス統合の作成にはACCOUNTADMINが必要とのこと。 USE ROLE ACCOUNTADMIN; CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION fitbit_apis_access_integration ALLOWED_NETWORK_RULES = (fitbit_apis_network_rule) ALLOWED_AUTHENTICATION_SECRETS = (fitbit_api_oauth_token) ENABLED = TRUE; 外部ロケーション(ネットワーク)にアクセスするUDFsを書くロールを作成する。 UDFsを書く際に、シークレットを参照する必要がある。 UDFsを書けるロールにシークレットのREAD権限を付与しておく必要がある。 以下、そのままでは SECURITYADMINがDB・スキーマに触れないので環境により修正が必要。 USE ROLE USERADMIN; CREATE OR REPLACE ROLE ikuty_fitbitapi_developer; USE ROLE SECURITYADMIN; USE SCHEMA IKUTY_DB.PUBLIC; GRANT READ ON SECRET IKUTY_DB.PUBLIC.fitbit_api_oauth_token TO ROLE ikuty_fitbitapi_developer; GRANT USAGE ON INTEGRATION fitbit_apis_access_integration TO ROLE ikuty_fitbitapi_developer; GRANT ROLE ikuty_fitbitapi_developer TO role SYSADMIN; 本体の実装 PythonでOAuth2 Endpoint経由でFitbit APIにGETリクエストを投げるFunctionを書く。 最初、トークンのexpire時のrefreshを自力で書いていたが、get_oauth_access_token(\'cred\')により、 自動的にrefreshしてくれていることに気づいた。 use role sysadmin; use schema IKUTY_DB.PUBLIC; CREATE OR REPLACE FUNCTION fitbit_python() RETURNS STRING LANGUAGE PYTHON RUNTIME_VERSION = 3.8 HANDLER = \'hello_fitbit\' EXTERNAL_ACCESS_INTEGRATIONS = (fitbit_apis_access_integration) PACKAGES = (\'snowflake-snowpark-python\',\'requests\') SECRETS = (\'cred\' = fitbit_api_oauth_token ) AS $$ import _snowflake import requests import json def hello_fitbit(): with requests.Session() as s: access_token = _snowflake.get_oauth_access_token(\'cred\') url = \"https://api.fitbit.com/1/user/-/activities/steps/date/today/1m.json\" res = s.get(url,headers={\"Authorization\": \"Bearer \" + access_token}) res_data = res.json() return res_data $$; 実行結果は以下。1日毎の歩数を1ヶ月分取得できた(恥...)。 select parse_json(fitbit_python()); { \"activities-steps\": [ { \"dateTime\": \"2023-11-23\", \"value\": \"15570\" }, { \"dateTime\": \"2023-11-24\", \"value\": \"5392\" }, { \"dateTime\": \"2023-11-25\", \"value\": \"8993\" }, { \"dateTime\": \"2023-11-26\", \"value\": \"10525\" }, { \"dateTime\": \"2023-11-27\", \"value\": \"6371\" }, { \"dateTime\": \"2023-11-28\", \"value\": \"2713\" }, { \"dateTime\": \"2023-11-29\", \"value\": \"9252\" }, { \"dateTime\": \"2023-11-30\", \"value\": \"0\" }, { \"dateTime\": \"2023-12-01\", \"value\": \"7947\" }, { \"dateTime\": \"2023-12-02\", \"value\": \"11265\" }, { \"dateTime\": \"2023-12-03\", \"value\": \"8557\" }, { \"dateTime\": \"2023-12-04\", \"value\": \"2366\" }, { \"dateTime\": \"2023-12-05\", \"value\": \"7985\" }, { \"dateTime\": \"2023-12-06\", \"value\": \"8109\" }, { \"dateTime\": \"2023-12-07\", \"value\": \"6852\" }, { \"dateTime\": \"2023-12-08\", \"value\": \"3707\" }, { \"dateTime\": \"2023-12-09\", \"value\": \"12640\" }, { \"dateTime\": \"2023-12-10\", \"value\": \"7122\" }, { \"dateTime\": \"2023-12-11\", \"value\": \"7190\" }, { \"dateTime\": \"2023-12-12\", \"value\": \"8034\" }, { \"dateTime\": \"2023-12-13\", \"value\": \"5228\" }, { \"dateTime\": \"2023-12-14\", \"value\": \"2861\" }, { \"dateTime\": \"2023-12-15\", \"value\": \"6785\" }, { \"dateTime\": \"2023-12-16\", \"value\": \"11720\" }, { \"dateTime\": \"2023-12-17\", \"value\": \"11021\" }, { \"dateTime\": \"2023-12-18\", \"value\": \"0\" }, { \"dateTime\": \"2023-12-19\", \"value\": \"11021\" }, { \"dateTime\": \"2023-12-20\", \"value\": \"0\" }, { \"dateTime\": \"2023-12-21\", \"value\": \"2703\" }, { \"dateTime\": \"2023-12-22\", \"value\": \"3336\" }, { \"dateTime\": \"2023-12-23\", \"value\": \"7497\" } ] } 結論 PuPrのExternal Network Accessを使用して、FitbitAPI→Snowflakeが出来ることを確認した。 (途中、自動的にトークンをrefreshしてくれている、と書いたが、何度かExpireさせないと良くわからない。) 相手がOAuth2.0ならとても簡単に繋ぐことができると思う。 次は、せっかくなのでSiS(Streamlit in Snowflake)で可視化してみたりしたい。
AirflowでEnd-To-End Pipeline Testsを行うためにAirflow APIを調べてみた話
Airflow自体にDAGの実行結果をテスト(End-To-End Pipeline Tests)する仕組みは無いようで、 以下のような地道な仕組みを自力で作る必要がありそうです。 テストデータを用意する Airflowが提供するAirflow APIを使用してDAGを実行する DAGの終了を待つ 結果をAssertする 他にAirflow CLIも使えそうですが、pythonコードの一部にするならAPIの方が使い勝手が良さそうです。 API仕様書を上から読んでみたので、その感想を書いてみます。 他にもあるのですが、今回の用途に使いそうなものを抜粋しています。 \"読んでみた\"だけなので、誤りがあるかもしれません。概要を理解するぐらいの気持ちで読んでください。 [arst_toc tag=\"h4\"] Airflow API概要 今日時点のAirflow APIのAPI仕様書は以下です。 Airflow API (Stable) (2.10.0) RESTful APIとなっていて、Resourceに対するCRUDをHTTP Methodで表現します。 1つ、update_maskという考え方があります。リソースの値を更新する際、リソースjsonと同時に クエリパラメタで\"変更したい値は何か\"を渡すことで、リソースjsonの該当値のみを更新できます。 resource = request.get(\'/resource/my-id\').json() resource[\'my_field\'] = \'new-value\' request.patch(\'/resource/my-id?update_mask=my_field\', data=json.dumps(resource)) API Authenticationがusername/passwordで雑ですが、 DAGのis_pausedをtrueにするには、以下の通りpatchを叩くようです。 curl -X PATCH \'https://example.com/api/v1/dags/{dag_id}?update_mask=is_paused\' -H \'Content-Type: application/json\' --user \"username:password\" -d \'{ \"is_paused\": true }\' CORSを有効にする必要があります。Enabling CORS 様々なAPI認証が用意されています。API認証はAirflowのauth managerで管理されます。Authentication エラーはRFC7807準拠です。つまり、Unauthenticated、PermissionDenied、BadRequest、NotFound、MethodNotAllowed、NotAcceptable、AlreadyExistsが扱われます。Errors Connections ざっとAPIを眺めていきます。 まずはConnection。順当なCRUDです。patchでupdate_maskが使われます。 コードから一通りConnectionを触れそうです。 Testって何か調べてみました。 デフォルトでdisabledになっていますが、Airflow UI(Connections)から\"Test\"ボタンを押下できます。 Connectionと関連付けられたhookのtest_connection()メソッドを実行するようです。 これと同等の機能が動くようです。 Method Endpoint Overview Response GET /connections List Connection array of objects(ConnectionCollectionItem). POST /connections Create a Connection created connection. GET /connections/{connection_id} Get a connection connection PATCH /connections/{connection_id} Update a connection updated connection DELETE /connections/{connection_id} Delete a connection (Status) POST /connections/test Test a connection (Status) DAG 次はDAG。まずDAG一覧に対する操作。一覧に対してpatchを叩ける様子です。 Method Endpoint Overview GET /dags List DAGs in the database. dag_id_pattern can be set to match dags of a specific pattern PATCH /dags Update DAGs of a given dag_id_pattern using UpdateMask. This endpoint allows specifying ~ as the dag_id_pattern to update all DAGs. New in version 2.3.0 次は個別のDAGに対する操作。 Method Endpoint Overview GET /dags/{dag_id} Get basic information about a DAG.Presents only information available in database (DAGModel). If you need detailed information, consider using GET /dags/{dag_id}/details. PATCH /dags/{dag_id} Update a DAG. DELETE /dags/{dag_id} Deletes all metadata related to the DAG, including finished DAG Runs and Tasks. Logs are not deleted. This action cannot be undone.New in version 2.2.0 GET /dags/{dag_id}/tasks/detail Get simplified representation of a task. GET /dags/{dag_id}/detail Get a simplified representation of DAG.The response contains many DAG attributes, so the response can be large. If possible, consider using GET /dags/{dag_id}. Airflowにおいて、Operatorのインスタンスに\"Task\"という用語が割り当てられています。 つまり、「Operatorに定義した処理を実際に実行すること」が\"Task\"としてモデリングされています。 「\"Task\"をA月B日X時Y分Z秒に実行すること」が、\"TaskInstance\"としてモデリングされています。 あるDAGは、実行日/実行時間ごとの複数の\"TaskInstance\"を保持しています。 以下のAPIにおいて、DAGが保持する\"Task\",\"日付レンジ\"等を指定して実行します。 \"TaskInstance\"を\"Clear(再実行)\"します。また、\"TaskInstance\"の状態を一気に更新します。 Method Endpoint Overview POST /dags/{dag_id}/clearTaskInstances Clears a set of task instances associated with the DAG for a specified date range. POST /dags/{dag_id}/updateTaskInstancesState Updates the state for multiple task instances simultaneously. GET /dags/{dag_id}/tasks Get tasks for DAG. なんだこれ、ソースコードを取得できるらしいです。 Method Endpoint Overview GET /dagSources/{file_token} Get a source code using file token. DAGRun \"Task\"と\"TaskInstance\"の関係と同様に\"DAG\"と\"DAGRun\"が関係しています。 「A月B日X時Y分Z秒のDAG実行」が\"DAGRun\"です。DAGRun。順当な感じです。 新規にトリガしたり、既存のDAGRunを取得して更新したり削除したり、再実行したりできます。 Method Endpoint Overview GET /dags/{dag_id}/dagRuns List DAG runs.This endpoint allows specifying ~ as the dag_id to retrieve DAG runs for all DAGs. POST /dags/{dag_id}/dagRuns Trigger a new DAG run.This will initiate a dagrun. If DAG is paused then dagrun state will remain queued, and the task won\'t run. POST /dags/~/dagRuns/list List DAG runs (batch).This endpoint is a POST to allow filtering across a large number of DAG IDs, where as a GET it would run in to maximum HTTP request URL length limit. GET /dags/{dag_id}/dagRuns/{dag_run_id} Get a DAG run. DELETE /dags/{dag_id}/dagRuns/{dag_run_id} Delete a DAG run. PATCH /dags/{dag_id}/dagRuns/{dag_run_id} Modify a DAG run.New in version 2.2.0 POST /dags/{dag_id}/dagRuns/{dag_run_id}/clear Clear a DAG run.New in version 2.4.0 以下はスキップ.. Method Endpoint Overview GET /dags/{dag_id}/dagRuns/{dag_run_id}/upstreamDatasetEvents Get datasets for a dag run.New in version 2.4.0 PATCH /dags/{dag_id}/dagRuns/{dag_run_id}/setNote Update the manual user note of a DagRun.New in version 2.5.0 DAGWarning DAGのimport_errors一覧を返します。 Method Endpoint Overview GET /dagWarnings List Dag Waranings. DAGStats A DAG Run status is determined when the execution of the DAG is finished. The execution of the DAG depends on its containing tasks and their dependencies. The status is assigned to the DAG Run when all of the tasks are in the one of the terminal states (i.e. if there is no possible transition to another state) like success, failed or skipped. The DAG Run is having the status assigned based on the so-called “leaf nodes” or simply “leaves”. Leaf nodes are the tasks with no children. There are two possible terminal states for the DAG Run: success if all of the leaf nodes states are either success or skipped, failed if any of the leaf nodes state is either failed or upstream_failed. Method Endpoint Overview GET /dagStats List Dag statistics. ImportError Airflow Best PractiveのTesting a DagにDAGのテスト観点に関する記述が(サラッと)書かれています。 まず、DAGは普通のpythonコードなので、pythonインタプリタで実行する際にエラーが起きないことを確認すべし、とのことです。 以下の実行により、未解決の依存関係、文法エラーをチェックします。もちろん、どこで実行するかが重要なので、DAG実行環境と合わせる必要があります。 Airflow APIにより、このレベルのエラーがDAGファイルにあるか確認できるようです。 $ python your-dag-file.py Method Endpoint Overview GET /importErrors List import errors. GET /importErrors/{import_error_id} Get an import error. Variables DAGに記述したくないCredentials等を管理する仕組みで、Airflow UIからポチポチ操作すると作れます。 Variableはkey-valueそのままです。DAGからkeyを指定することで参照できます。 Airflow APIからもVariableをCRUDできます。 Method Endpoint Overview GET /variables List variables.The collection does not contain data. To get data, you must get a single entity. POST /variables Create a variable. GET /variables/{variable_key} Get a variable by key. PATCH /variables/{variable_key} Update a variable by key. DELETE /variables/{variable_key} Delete a variable by key. まとめ RESTfulAPIが用意されているということは、内部のオブジェクトをCRUD出来るということなのだろう、 という推測のもと、Airflow APIのAPI仕様書を読んで感想を書いてみました。 Airflowの概念と対応するリソースはAPIに出現していて、End-To-End Pipeline Testを書く際に、Assert、実行制御を記述できそうな気持ちになりました。 Assert、実行制御、だけなら、こんなに要らない気もします。 API呼び出し自体の煩雑さがあり、Testの記述量が増えてしまうかもしれません。 以下の記事のようにwrapperを書く必要があるかもしれません。 https://github.com/chandulal/airflow-testing/blob/master/src/integrationtest/python/airflow_api.py DAGの入力側/出力側Endに対するファイル入出力は別で解決が必要そうです。 「API仕様書を読んでみた」の次の記事が書けるときになったら、再度まとめ記事を書いてみようと思います。
Azure Functionsの機能まとめ(座学版)
タイトルの通り、Azure Functionsの機能をまとめてみた。 [arst_toc tag=\"h4\"] 課金モデル 課金モデルが5パターンあるのではなく、運用方式が5パターンあり、それぞれ課金方式が違う。 呼称がMECEでなかったり公式ドキュメントで表記揺れが存在したり親切でない点はある。 Premium、DedicatedはApp Service Planで動かすことができ、かなり微妙に繋がっている。 実質的にPremium、DedicatedはApp Service Planで実現され課金がかかる。 コールドスタートに対する改善の歴史を感じる。 課金モデル 概要 従量課金 オーソドックスなFaaS。名前の通り資源の使用量に応じて課金。必要最低限のネットワーク分離が提供される。既存VNetとの統合は不可。コールドスタート。アプリのロード・アンロードが頻繁に発生し、しばしば遅い。 Premium 資源の使用量に応じて課金。従量課金よりも高機能な従量課金(言葉辛い..)。既存VNetとの統合がサポートされる。コールドスタートを回避するために用意された。インスタンス数をゼロまでスケールインさせないことでホットスタートを実現している。アクティブなインスタンスのコア数(vCPU/h)、メモリ使用量(GB/h)に課金。裏側はApp Service Planだが手持ちのカスタムイメージをACRに登録しApp Serviceにホストすることが可能。 Dedicated 通常のApp Service Planとして課金される。既にApp Serviceインスタンスを実行しており新たにFunctionを相乗りさせる時に使用する。従量課金的な要素が無いので(高価だけれども)コストを予測できる。 App Service Environment(ASE) 超強力なDedicated。1人の顧客に限定された専用環境。ASE v1,v2,v3と脈々と新しい奴が作られている。高スケール、分離およびセキュリティで保護されたネットワーク アクセス、高いメモリ使用率などが書かれている。マルチリージョンにまたがって構成できる。高RPS(Requests per Seconds)ワークロード向けに用意されるApp Serviceの強化版。 Container Apps Hosting Azure Container Appsでコンテナ化されたFunctionsの開発・デプロイ・管理。Kubernetes ベースの環境で関数を実行できる。現在プレビュー。 従量課金とPremiumの違い リッチな従量課金プランであるPremiumについて詳細なドキュメントがある。 Azure Functions の Premium プラン そのメリットとして、以下が列挙されている。 インスタンスをウォーム状態に維持することでコールド スタートを回避します 仮想ネットワーク接続 より長いランタイム期間をサポートします Premium インスタンス サイズの選択 従量課金プランと比較して、予測可能な料金 複数の Function App を含むプランでの高密度アプリ割り当て 従量課金プランは、インスタンス数をゼロまでスケールインできる。 その結果としてその料金の料金はかからない一方、リクエストが来たときにゼロから1個以上まで スケールアウトする際に\"コールドスタート\"時間を要する。 Premiumプランには、\"常時使用可能なインスタンス\"という考え方がある。 要はインスタンス数をゼロまでスケールインさせず、常にアクティブにしておくということらしい。 当然、\"常時使用可能なインスタンス\"は常時課金される。 他に\"事前ウォーミング可能なインスタンス\"という考え方がある。 常時使用可能なインスタンスが負荷分散してリクエストを捌いている間、 事前ウォーミング可能なインスタンスが後で立ち上がる。常時使用可能なインスタンスの負荷が 規定値を超えると、事前ウォーミング可能なインスタンスがアクティブに昇格し捌き始める。 事前ウォーミング可能なインスタンスは昇格するまでの間立派に課金されてしまう。 Premiumプランは実際はApp Serviceの仕組みで動く。 プラン名に規約がありEで始めるとElastic Premium、つまり、App Serviceで動かすPremiumということになる。また、Pで始めると動的スケールしないDedicated Hostingプランということになる。 Azure Functions は Azure App Service プラットフォームで実行できます。 App Service プラットフォームでは、Premium プラン関数アプリをホストするプランは Elastic Premium プランと呼ばれており、EP1 のような SKU 名があります。 Premium プランで関数アプリを実行することを選択した場合、EP1 のように \"E\" で始まる SKU 名を持つプランを必ず作成してください。 P1V2 (Premium V2 Small プラン) のように \"P\" で始まる App Service プラン SKU 名は実際には Dedicated ホスティング プランです。 Dedicated であり、Elastic Premium ではないため、\"P\" で始まる SKU 名のプランは動的にスケールせず、コストが増えることがあります。 実行継続時間 従量課金プランは1回の実行の最大は10分。Premiumプランはデフォルトで最大30分。 ただし、Premiumプランの最大値は延長して無制限まで拡張できる。 プラットフォームのアップグレードにより、マネージド シャットダウンがトリガーされ、関数の実行が停止する可能性があります プラットフォームの停止により、処理されないシャットダウンが発生し、関数の実行が停止する可能性があります 新しい実行がない状態で 60 分経つと worker を停止するアイドル タイマーがあります スケールイン動作により、60 分後に worker のシャットダウンが発生する可能性があります スロットのスワップにより、スワップ中にソース スロットとターゲット スロットの実行が終了される可能性があります これはFunctionのタイムアウト期間であって、HTTPトリガーの応答にはAzure Load Balancerの タイムアウト期間(=230秒)が適用される。HTTPトリガで長時間処理を実現する場合、 Durable Functionで作るか、即時応答・非同期処理のパターンにすべきとのこと。 Function App タイムアウト期間 Durable Functions とは 実行時間の長い関数を使用しない HTTPトリガで長時間処理を実装するパターン 可能な限り、大きな関数は、連携して高速な応答を返す、より小さな関数セットにリファクタリングしてください。 たとえば、webhook または HTTP トリガー関数では、一定の時間内に確認応答が必要になる場合があります。webhook は通常、即座に応答を必要とします。 この HTTP トリガー ペイロードは、キュー トリガー関数によって処理されるキューに渡すことができます。 このアプローチを使用すると、実際の作業を遅らせて、即座に応答を返すことができます。 ネットワーク 既存のAzureリソースとAzure Functionsを連携する際に、どのように既存リソースと連携できるか、 各実現方式毎にやれることが決まっている。以下が参考になった。 Azure Functions のネットワーク オプション 特徴 従量課金 Premium Dedicated ASE 受信アクセス制限 ✅ ✅ ✅ ✅ プライベートエンドポイント ❌ ✅ ✅ ✅ 仮想ネットワークの統合 ❌ ✅ ✅ ✅ VNet Trigger(非HTTP) ❌ ✅ ✅ ✅ Hybrid接続 ❌ ✅ ✅ ✅ 送信IPの制限 ❌ ✅ ✅ ✅ 受信アクセス制限は、送信元のIPアドレスに対するAllow/Denyを設定する機能。 IPv4/v6のIPアドレスを直接指定するか、サービスエンドポイントを使用するVNetのサブネットを指定可。 より詳細な記述は、Azure App Service のアクセス制限を設定するを参照。 プライベートエンドポイントは、VNet内からプライベートエンドポイントを介したPrivateLink接続。 AWS VPCと異なり、Azure VNetはリソースの論理的なグルーピングに過ぎない、という側面があり、 通信を秘匿化したいという文脈でなくても、PrivateLinkを使って連携せざるを得ない事情がある。 プライベートエンドポイントのDNSはAzureが良しなに作ってくれる。 仮想ネットワークの統合(VNet統合)は、Azure Functionsを指定のVNetに論理的に配置するオプション。 これにより、FunctionからVNet内のリソースにアクセスできるようになる。 FunctionからVNet内リソースに対して送信呼び出しを行うために使われる。逆には使われない。 従量課金ではN.G.だがPremiumクラスの従量課金なら可能になる。これはメリット。 リージョン内であれば、VNet側にVirtual Network Gatewayは必要ないがリージョン間であれば必要。 Virtual Network Gatewayを必要とする場合、通信に大きな制約がかかる。 なお、Azure FunctionsをASEで運用する場合、FunctionはASE内に物理的に配置されるため、 論理的なVNet統合を行う必要はないとのこと。 トリガについては後述する。オーソドックスな従量課金モデルはHTTPトリガしかサポートしない。 Premium以降で他のトリガが解放される。 ハイブリッド通信は、Windowsで動作している従量課金以外の全てのFunctionについて、 他のネットワークのリソースにアクセスできる機能。Azure Relayという機能の1つ。 Windowsを使わないといけないため特殊な用途となる。省略。 トリガとバインド トリガーによりFunctionが発火し実行される。つまりトリガーにより関数の呼び出し方法を定義する。 トリガーとバインドについてはAzure Functions でのトリガーとバインドの概念が参考になる。 トリガーにはデータが紐付けられていて、呼び出しの際のペイロードとなる。 バインドとは、別のリソースを宣言的に接続する方法。入力バインド/出力バインドがある。 バインドからのデータは、Functionから見てパラメータとして利用できる。 Azure Functionsのバージョンにより対応可否が異なる。現在のバージョンはv4。 比較的マイナーと思われるものについて、割と昔出来ていたことが出来なくなったパターンが多い。 Kafka、RabbitMQは従量課金プランではサポートされない。 Typev1.xv2.x以降トリガー入力出力 Blob Storage✔✔✔✔✔ Cosmos DB✔✔✔✔✔ Azure Data Explorer✔✔✔ Azure SQL✔✔✔✔ Dapr✔✔✔✔ Event Grid✔✔✔✔ Event Hubs✔✔✔✔ HTTP✔✔✔✔ IoT Hub✔✔✔ Kafka✔✔✔ Mobile Apps✔✔✔ Notification Hubs✔✔ Queue Storage✔✔✔✔ Redis✔✔ Rabbit MQ✔✔✔ SendGrid✔✔✔ Service Bus✔✔✔✔ SignalR✔✔✔✔ Table Storage✔✔✔✔ Timer✔✔✔ Twillo✔✔✔ 例えば、HTTPトリガーとバインドの例は以下。 RESTfulAPI的にURLにペイロードを含めることができる。 (ドキュメントを見ても何が正解が分からないし、もちろんどこかに実行例がある訳でもない) ここで、リクエストパラメタが入力バインド、レスポンスが出力バインド、ということになる..(のかな)。 import logging import azure.functions as func @app.function_name(name=\"httpTrigger\") @app.route(route=\"products/{category:alpha}/{id:int?}\" auth_level=func.AuthLevel.ANONYMOUS) def main(req: func.HttpRequest) -> func.HttpResponse: category = req.route_params.get(\'category\') id = req.route_params.get(\'id\') message = f\"Category: {category}, ID: {id}\" return func.HttpResponse(message) こうしておくと、例えば以下のURLで定義したhttpTriggerを実行できる。 http://.azurewebsites.net/api/products/electronics/357 auth_levelは認可レベル。URLのリクエストに必要な認可キーを指定する。 ANNONYMOUSなら不要、FUNCTIONなら関数固有のAPIキー、ADMINならマスターキー(?)。 詳細はこちら。 まとめ Azureドキュメントを見ながらAzure Functionの概要をまとめてみた。 実装例が少なくまとまったドキュメントが少ない、という問題があり、 座学版の他に「やってみた」を繰り返す必要がありそう。
TableauServer 準備
以下の項目についてオンラインヘルプをまとめてみた. 工数1日程度の理解なのと無理やり1行に詰めた感は否めないのですが, そもそも自分用なのでご容赦を... ハードウェア要件 ソフトウェア要件 ライセンス サーバプロセス 分散環境と高可用性 データソース 1. 全ての技術仕様. https://www.tableau.com/ja-jp/products/techspecs 1. ハードウェア最小要件 1. 最低要件 RAM 16GB, CPU x64 空きディスク容量 15GB, コア数は物理コア. HTは考慮されない. 1. 本番シングル RAM 32GB, CPU x64 8コア 2.0GHz以上 空きディスク容量 50GB. 1. 本番マルチ 運営に聞いてね. 1. ソフトウェア要件 1. サポートするOS WindowsServer2012,2012R2,2016,2019, AmazonLinux2, RHEL7.3,CentOs7.3+,Debian9+,OracleLinux7.3,ubuntu16.04LTS,18.04LTS 1. ブラウザ Chrome(Windows,Mac,Android),MS Edge,IE11,FireFox,Safari, + Tableau Mobile 1. メールアラートのオプション STMPをセットアップする必要あり. TLS有効化によりSMTP TLSを透過的に使用可. 1. ウイルス対策の懸念事項 TableauServerのインストールや使用に干渉する可能性あり. 公式がプログラムフォルダ,データフォルダを除外する案内を実施. 1. TCPポート.TSM=8850,TS GW=80.TS GWをSSLとする場合443. 1. 専用サーバーの目的と利点. パフォーマンス. セキュリティ. 相互運用性. https://help.tableau.com/current/guides/everybody-install/ja-jp/everybody_admin_install.htm 1. クラウドで稼働させる際の検討事項. オンプレミスと比較してハードウェアコスト不要.アップタイム,信頼性,耐故障性が良い. AWS,Azure,GCP,AlibabaCloud(?)の4環境のガイドあり. 1. ライセンス発行 1. ライセンスタイプはコア毎,ユーザ毎の2種類.ユーザライセンスの場合,\"ライセンス\"-\"サイトロール\"-\"パーミッション\"によるアクセシビリティマトリクス(的なもの)を作れる. 1. ライセンスタイプ=>Creator,Explorer,Viewer.の3種類. 1. Creator=>コンテンツ作成,データソース設計,サイトロールとパーミッション設計.パブリッシュ.PrepBuilder.管理者向け.TableauServerを管理. 1. Explore=>PrepBuilderNG.パブリッシュNG.既存のデータソースの使用,既存のデータソースを使ったダッシュボードの作成.TableauServerのコンテンツを管理. 1. Viewer=>パブリッシュ済みのダッシュボードを表示する.自分でダッシュボードを作らない. 1. サイトロール=>サイトに対してユーザが持つことができる最大のアクセスレベル.サイトロールの最大の権限をユーザが利用できるかはコンテンツに設定されているパーミッションによる. 1. Creatorライセンスを使用するサイトロール 1. サーバ管理者=>TableauServerでのみ使用可能.OnlineではNG.全てのコンテンツに対して無制限のアクセス権. 1. サイト管理者Creator=>Onlineでも可能.サイトレベルのアクセス権を除く.サイトが用意された前提で全てのアクセス権. 1. Creator=>管理者以外では最大のアクセスレベル.ブラウザでの外部データへの接続など 1. Explorerライセンスを使用するサイトロール 1. サーバ管理者=>TableauServerでのみ使用可能.OnlineではNG.管理者ユーザ作成時にサーバで認証された最大のライセンスタイプがExploreの場合,CreatorではなくExploreのサーバ管理者になる. 1. サイト管理者Explorer=>Onlineでも可能.既存のデータソースを使用して既存のワークブックの編集・保存をおこなえる. 1. Explorer(パブリッシュ可能)=>既存のデータソースを使用してWebからワークブックをパブリッシュする. 単なるExploreに記述された特記事項ができる. 1. Explore=>Webからワークブックに埋め込まれたデータソースから新しいスタンドアロンデータソースを保存できない.もちろん新規にデータソースを作成できない. 1. Viewerライセンスを使用するサイトロール 1. Viewer=>既存のパブリッシュ済みのビューを表示する.データへの接続,コンテンツの作成,編集,パブリッシュ,データアラートの設定などできない. 1. ライセンスなし 1. サインインできない. CSVからのインポート時, ユーザ追加時に利用可能なライセンスが無い場合.など. 1. コンテンツをパブリッシュできる人物 => サーバ管理者,サーバ管理者Creator,Creator, Explore(パブリッシュ可能),サイト管理者Explore 1. サーバプロセス 1. TableauServiceManager(TSM).インストール後の初期構成.設定変更.トポロジ変更.継続的な(日常的な?)構成管理.バックアップの復元,ログ圧縮,管理タスクの実行など. 1. TableauServer実行に開始(running),停止じに停止(stopping). 1. アプリケーションサーバ. WebアプリケーションおよびREST APIを処理.参照と検索をサポート. 1. \"データに聞く(AskData)\". 1. バックグラウンダー => 抽出の更新,サブスクリプション,\"今すぐ実行\",tabcmdから実行するタスクなどを実行. 1. キャッシュサーバ => クラスタ全体でクエリと結果を分散/共有. アプリケーションサーバ,データサーバがリクエスト. 1. クラスタコントローラ => 各種コンポーネントの監視,障害検出,フェイルオーバーの実行. 1. データエンジン => データ抽出を作成.クエリを処理する. 1. データサーバ => データソースへの接続を管理する. 1. データソースプロパティ => 「データに聞く」等のクライアントサービスに,パブリッシュされたデータソースのメタデータを提供する. 1. ElasticServer => 「データに聞く」がデータをインデックスするために使用する. 1. ファイルストア => ローカル,SAN,NASなどストレージを抽象化する. 1. ゲートウェイ => ブラウザ, TableauDesktop,その他のクライアントから, TableauServerへの全ての要求を処理する. Webサーバ. 1. 内部データソースプロパティ => データソースプロパティとのみ通信する. 1. メッセージングサービス => TableauServerのマイクロサービス間の通信をサポート. 1. メトリクスサービス => メトリクスデータの読み書き. 1. リポジトリ => 実体はPostgreSQL. TableauServerのメインデータベース.ワークブックとユーザのメタデータを保存. Tableau Catalogが有効な場合,コンテンツと外部アセットのメタデータを保存. 1. SAMLサービス => TableauServerとSAML IdP間のプロキシ. 1. 検索と参照 => コンテンツのメタデータを高速に検索する.フィルター,取得,表示を処理する. 1. Tableau Prep Conductor => フローの実行,接続の認証資格情報の確認,フロー失敗時のアラート送出. 1. VizQLサーバ => ビューを読み込み,レンダリング,クエリを計算. 1. 一緒にデータエンジンもインストールするプロセス => アプリケーションサーバ,バックグラウンダー,データサーバ 1. Tableauマイクロサービスコンテナ. コンテナ内に複数のプロセス. 全部実行中=>running, 一部実行中=>degraded, 全て停止=>error,インタラクティブ,非インタラクティブの2種類. 1. TSMプロセス. TSMの初期化完了=>running. TableauServerが停止しても走り続ける. 1. 管理エージェント => 構成/トポトジの変更がないか調整サービスを監視する.新しい構成を各サービスに提供 1. 管理コントローラ => TSMへの要求を処理.構成とトポトジの変更,サービスプロセス全体のワークフローを調整. REST APIのエンドポイント(HTTPS) 1. クライアントファイルサービス => 複数のノード間で共有ファイル(認証関連の証明書,キー,OpenID,SAML,Kerberos等のファイル)を管理. 1. 調整サービス => 唯一の参照元.?? 1. サービスマネージャ => 不明 ?? 1. ライセンスマネージャ => ライセンスを扱う?? 1. メンテナンスプロセス. 通常stopped. ジョブ開始時にrunning,上部終了時にstopped. 1. データベースメンテナンス => TableauServerリポジトリの保守操作. 1. バックアップ/復元 => TableauServerリポジトリ, および, ファイルストアに保管されているデータのバックアップおよび復元操作. 1. サイトのインポート/エクスポート => クラスタ間でTableauServerを移行. 1. 分散環境と高可用性環境におけるプロセス 1. 本番環境の推奨は8コア以上. 1. バックグラウンダープロセスを専用のコンピュータで実行.(バックグラウンダーはCPUを大量に使用する) 1. VizQLとバックグラウンダーを分ける. 1. 抽出を頻繁におこなう可能性がある場合 => バックグラウンダーを増やす 1. ファイルストアと同じノードにあるデータエンジンはビューのクエリに使われる. バックグラウンダーが重いことでビュー操作がモタつくのを回避するため、ファイルプロセスとバックグラウンダーを分ける. 1. リポジトリ(pgsql)とファイルストアをファイルコントローラと同じノードに配置 => TableauServerのバックアップにかかる時間を短縮 1. フェイルオーバー 1. ファイルストア,リポジトリをフェイルオーバー対応させる=>最大3大のコンピュータが必要.1台は\"最初のノード\",2,3台は追加ノード. 1. 複数のゲートウェイ.3台のコンピュータ+ロードバランサ.ゲートウェイプロセスを全ノードにインストールしてロードバランサをゲートウェイに向ける.1ロードバランサx3ノード. 1. 高可用性 => 最初のノードの実行プロセスを少なく. 2,3台目を多く. 1. データソース 1. 接続情報を記録する.ライブか抽出か? 計算,セット,グループ,bin,パラメタ,カスタムフィールド. サーバの場所,認証資格,アクセストークン,セキュリティ情報... 1. なぜパブリッシュ(管理/共有)するのか? 各クライアント毎に似て非なる設定が増えるのを回避.サーバ側で抽出結果をサーバに残すアーキテクチャをとれる=>ネットワークトラフィックが減る. 1. サーバ側でコネクションを設定する. 例)MySQL. サーバ側にODBCドライバをインストールしてODBC経由でMySQLに接続する. 例)ファイル => Excelなど. 例)キューブ =>Oracle Essbase,Teradata OLAPなど 1. 抽出とライブ.抽出とはスナップショット.ライブとは都度取得.データソースに都度クエリを投げる.
TableauServer 構成
TableauServerのインストール時に気をつけること. 過去の経緯からかライセンス,サイトロールの関係が結構カオス. 増築しました感がかなりある. ライセンス,サイトロール,パーミッションの3要素からアクセス権が決まる. 複雑... 1. キャッシュサーバの構成. 1. 実体はCacheServerプロセス. 1. クエリと実行結果のペアをキャッシュする. Webブラウザの操作によりクエリが実行されるときにキャッシュを更新. 1. 可用性(性能)を上げるにはキャッシュサーバプロセスを複数のノードに構成する. 1. tsmコマンドで構成を変更. tsm data-access caching set -r . 規定値は全キャッシュ. 有効時間(value)を指定可能. 1. プロセス分散の適用 1. 分散パターンは3種類. シングルノード,マルチノード,高可用性. 高可用性はマルチノードのより冗長なサブセット. 1. マルチノードにおいて\"最初のコンピュータ(初期ノード)\"だけ他のノードと扱いが異なる. 初期ノードにしかインストールできないプロセスがある. 1. サブスクリプション/メールアラート 1. 「ビュー」「ワークブック」の\"イメージ\",\"PDFスナップショット\"を定期的に作成しメールで送信する機能. 1. 自分自身向けか(所有者,プロジェクトリーダー,管理者であれば)人向けにサブスクライブできる. 1. [検索]-[全てのワークブック]-[ツールバー]-[サブスクライブ] 1. サブスクリプションを受け取るには[画像]と[画像/PDFのダウンロード]パーミッションが必要. 1. アカウントをメールアドレスとして読んで送るため受け取るアカウントがメールアドレスでないといけない. 1. サイト構成オプション 1. ユーザー数,[ユーザー]から確認. 1. ストレージ容量,[サーバーのステータス]-[サーバーディスク空き容量].過去30日間のディスク使用量,先月のディスク使用量推移.GBと%. 1. サイトサブスクリプションの有効化-[設定]-[サブスクリプション]-[ユーザーにワークブックおよびビューのサブスクライブを許可する] 1. サイトサブスクリプションの編集-[タスク]-[サブスクリプション]-[アクション]-[スケジュールの変更]/[件名の変更]/[空きビューモードの変更]/[サブスクリプションの解除] 1. プロジェクト構成オプション-[検索]-[プロジェクト]-[共有]/[名前の変更]/[移動]/[パーミッション]/[所有者の変更]/[削除] 1. ユーザの構成オプション-[ユーザ]-[各ユーザ]-[設定] 1. サブスクリプションタイムゾーン -> スケジュールのタイムゾーン設定 1. 抽出,フロー,スケジュールされた更新 -> ジョブのアップデートがあったときにメール通知するか否か 1. サブスクリプション一時停止通知 -> 繰り返しエラーを検知したときにサブスクリプションが止まる -> メール通知するか否か 1. データアラート一時停止通知 -> 繰り返しエラーを検知したときにデータアラート通知が止まる -> メール通知するか否か 1. 誰がユーザーを追加できるか? 1. 前提として十分なユーザーライセンスとロールライセンスが必要 1. サーバ管理者サイトロールはユーザを追加できる. サイト管理者サイトロールはサーバ管理者サイトロールを持つユーザが許可した場合に限りユーザを追加できる. 1. ユーザーの制限とライセンス 1. コアベースライセンスの場合,定義した数のCreatorライセンス,無制限のExploreライセンス 1. ユーザーベースライセンスの場合, ライセンスに所有可能なユーザーの最大数が記載. 1. コアベースライセンスからユーザーベースライセンスへの移行(ライセンス変換)が可能. 1. ユーザーの追加 1. ユーザの追加体系は大枠でサーバーレベル,サイトレベルの2種類.サイトが1つの構成では自動的にサーバレベルの体系が適用. 1. サイトが2つ以上の場合,サーバレベル/サイトレベルの並列.サーバ管理者のみがサーバレベル追加可.[サーバーユーザー]と[サイトユーザー]の2通りの画面に入れる. 1. ライセンスタイプとサイトロール 1. ライセンスタイプはユーザ毎に定義. ユーザにどのサイトロールを割り当てるかにより必要なライセンスタイプが異なる. 1. サイトロールはユーザ毎に定義. マルチサイトではサイト毎に異なるサイトロールを持てる. あるサイトではCreatorサイトロール,別のサイトではViewerサイトロール.など. 1. サイトロールはユーザが持ち得る最大の権限. だが, ユーザがサイトロールの最大の権限を利用できるかは,コンテンツ毎に設定されたパーミッションにより決まる. 1. 管理者レベル 1. サーバ管理者=>TableauServerでのみ利用可能.全リソースに対する無制限のアクセス権. 1. サイト管理者=>TableauOnlineではこれのみ利用可能. サーバ管理者がサイト管理者にユーザの管理/サイトロール,サイト追加を許可するかを決定できる. 1. パブリッシュ可能/不可能な人物 1. Creatorライセンス-サーバ管理者/サイト管理者Creator/Creator => 可能 1. Explorerライセンス-サーバ管理者/サイト管理者Explorer/Explorer(パブリッシュ可能) => 可能 1. Explorerライセンス-Explorer => 不可 1. Explorer(パブリッシュ可能)についてはCreatorに纏わる権限(データソースへの接続など)に制限がある. 1. ローカル認証/ActiveDirectory経由のインポート 1. ローカル認証時のユーザ追加 - [新規ユーザー]押下. ローカル認証時にユーザ名の重複を避けるために電子メールアドレスをユーザ名として使うと良い. 1. ActiveDirectoryを介したインポート - TableauServerでActiveDirectory認証をおこなう設定をしている場合,ドメイン名無しでActiveDirectoryユーザを入力できる.フルネーム禁止. 1. パーミッション 1. パーミッションの構成 1. コンテンツ/プロジェクトに対して, ユーザ/グループに許可/不許可を与える. 1. パーミッションの段階的構成. Lv1.プロジェクトレベルに設定/ Lv2.コンテンツレベルに設定. プロジェクトに設定したパーミッションはサブコンテンツとネストされたプロジェクトに適用. 1. パーミッション設定画面の使い方. 上ペインで[ユーザ]/[グループ]を選択する. => 下ペインに該当ユーザの有効なパーミッション一覧が表示される/編集できる. 1. 下ペインのパーミッショングリッドのセル(許可/不許可が表示される部分)にカーソルを合わせると, 許可/不許可の理由が得られる. 1. プロジェクトのパーミッションロック => コンテンツ, ネストされたプロジェクトのパーミッションをカスタマイズできないように保護する. 1. 種類 => \"許可\",\"拒否\",\"未指定\". 1. 複雑にしないために => ユーザではなくグループに対して設定すべき. コンテンツではなくプロジェクトに設定すべき. 1. パーミッションの詳細 1. プロジェクト 1. ビュー => 許可の場合,プロジェクトを表示できる. プロジェクト内のコンテンツに関してではなく, プロジェクト自身の表示に関する. 1. パブリッシュ => Tableau Desktop, Tableau Prep Builderからプロジェクトにコンテンツをパブリッシュできる. コンテンツの移動,Web作成時の保存にも必要. 1. ワークブック 1. ビュー => 許可の場合,ワークブックを表示できる. 1. フィルター => 許可の場合,ビュー内のフィルターを操作できる. 不許可の場合,フィルターが表示されない. 1. コメントの表示 => 許可の場合,ワークブック内のビューに関連付けられたコメントを表示できる. 1. コメントの追加 => 許可の場合,ワークブック内のビューに対してコメントを追加できる. 1. イメージ/PDFのダウンロード => 許可の場合,ワークブック内のビューをPNG,PDF,PowerPointとしてDownloadできる. 1. サマリーデータのダウンロード => ユーザはビュー内や選択したマーク内の集計データを表示したり, CSVとしてDownloadできる. 1. データソース 1. ビュー => 許可の場合,サーバ上のデータソースを表示できる 1. 接続 => 許可の場合,Tableau Desktop,Tableau Prep Builder,データに聞く,Web編集でデータソースに接続できる. 1. データソースのダウンロード => 許可の場合,サーバからデータソースを*.tdsxとしてダウンロードできる. 1. 上書き => 許可の場合,データソースをパブリッシュしサーバ上のデータソースを上書きする. 1. 削除 => 許可の場合,データソースを削除できる. 1. パーミッションの設定 => 許可の場合,パーミッションルールを作成して編集できる. 1. Tableauのセキュリティモデル 1. プロジェクト 1. コンテンツへのアクセスを整理,管理するために使用するコンテナ. プロジェクト単位で権限を処理する. 1. 階層. 上位プロジェクトを作成できるのは管理者のみ. 所有者とプロジェクトリーダが上位プロジェクトの下にネストされたプロジェクトを作成できる. 1. 所有者とプロジェクトリーダはプロジェクト,コンテンツ,下位プロジェクトに対してアクセス権を持つ.