DRYな備忘録

Don't Repeat Yourself.

2分で Google Cloud Functions を使ってみる

GAE/Goが好きなのでだいたいGAEでやっちゃおうとするんですけど、せっかくなのでCloud Functions使ってみたいじゃないですか。

すげー雑に、かんたんなことはGCFでやりましょう、となる。知らんけど。ある1つのトリガーに対して連鎖的に外部APIをトリガーしたいとき、みたいなシンプルで軽い用途に向いている、的な雰囲気だと思う。

% mkdir -p ~/tmp/gcftest
% cd ~/tmp/gcftest
% gcloud components update
% gcloud projects create otiai10-gcftest
# ファイルつくります
% vi hello.js
% cat hello.js
exports.hello = (req, res) => {
    res.send(`Hello World!`);
}

# デプロイします
% gcloud functions deploy hello --trigger-http --project otiai10-gcftest
API [cloudfunctions.googleapis.com] not enabled on project
[otiai10-gcftest]. Would you like to enable and retry?  (y/N)?  y
# 中略
Deploying function (may take a while - up to 2 minutes)...failed.
ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Function load error: File index.js or function.js that is expected to define function does not exist in the root directory.
# は?

# function.js にする
% ls -la
% mv hello.js function.js

# 再度デプロイを試みる
% gcloud functions deploy hello --trigger-http --project otiai10-gcftest
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: hello
httpsTrigger:
  url: https://us-central1-otiai10-gcftest.cloudfunctions.net/hello
# 中略
status: ACTIVE
timeout: 60s
updateTime: '2018-04-27T06:15:24Z'
versionId: '2

regionがus-central1になってるのがアレですが、

% curl https://us-central1-otiai10-gcftest.cloudfunctions.net/hello
Hello World!%                                                                                                                                                                                                                                %

やるじゃん... 2分でできた。

以下、いろいろためす

とりあえずprojectの指定めんどいので

% gcloud config set project otiai10-gcftest
% gcloud functions --help

% gcloud functions call --help
# callでcurl相当のことができる
% gcloud functions call hello
executionId: dnaermtmefak
result: Hello World!
%

% gcloud functions list
NAME   STATUS  TRIGGER       REGION
hello  ACTIVE  HTTP Trigger  us-central1
% gcloud functions describe hello

# 削除
% gcloud functions delete hello
Resource
[projects/otiai10-gcftest/locations/us-central1/functions/hello] will
be deleted.

Do you want to continue (Y/n)?  y

Waiting for operation to finish...done.
Deleted [projects/otiai10-gcftest/locations/us-central1/functions/hello].
% gcloud functions call hello
ERROR: (gcloud.functions.call) ResponseError: status=[404], code=[Not Found], message=[Function hello in region us-central1 in project otiai10-gcftest does not exist]
%

# deployのオプションいろいろある
% gcloud functions deploy --help

# これとか
     --entry-point=ENTRY_POINT
        By default when a Google Cloud Function is triggered, it executes a
        JavaScript function with the same name. Or, if it cannot find a
        function with the same name, it executes a function named function. You
        can use this flag to override the default behavior, by specifying the
        name of a JavaScript function that will be executed when the Google
        Cloud Function is triggered.

# deployのargに渡すNAMEが、HTTPのendpointになって、
# デフォルトではその名前が、exportされてる関数名と紐づくっぽい。
# --entry-pointでは、エントリポイントとなる関数名を指定できるようだ。

% gcloud functions deploy testtest \
--region ap-northeast1 \
--entry-point hello \
--trigger-http

ERROR: (gcloud.functions.deploy) ResponseError: status=[400], code=[Bad Request], message=[The request has errors
Problems:
region asia-northeast1 is not supported.

# マジか

f:id:otiai10:20180427153413p:plain

あ、Tokyoにまだ来てないのか...

% gcloud functions deploy testtest \
--entry-point hello \
--trigger-http


% curl https://us-central1-otiai10-gcftest.cloudfunctions.net/testtest
Hello World!

やるじゃん...!

シークレットの受け渡しどうする

外部APIを叩かせるにはそのAPIシークレットをもたせる必要があるよなー、と思い。環境変数かな?と思ったけどそうではないのかな。

めんどくさそう。jsonでいいや。

const secrets = require("./secrets.json");

exports.leton = (req, res) => {
    res.send(secrets);
}
% gcloud functions deploy leton --trigger-http
% curl https://us-central1-otiai10-gcftest.cloudfunctions.net/leton
{"name":"Hiromu OCHIAI","age":17}
%

秘密が漏れてしまった。ソースコード管理・共有時はsecrets.jsonをignoreするなりすればよいのだろうけど、アーキテクチャ的にプロジェクトのrootディレクトリをGCSに上げてるっぽいし、なんかちょっと気になるなと思った。ので質問してみた。

既出だったらすみません。GCFに、なんらかのシークレットを渡したい(だいたいの場合たぶん外部APIトークンとか)場合のベストプラクティスって紹介されてますか? 環境変数かなと思ってググったらRuntime-Config とかいうやつにぶちあたったんですが、betaっぽいし、今の所普通にデプロイ対象ディレクトリ配下にsecrets.jsonみたいなの作ってdeployしてます。

Functionかー。多少悩ましいねー 最近、適当にSecret ManagerのDemoアプリみたいなのを作ってみたりしていた https://github.com/sinmetal/gcpsm IAP, App Engine, Cloud KMS, Cloud Datastoreを使っている App Engineの時は、インスタンス起動時に取りに行って、メモリに持っておけばいいやーと思っていたけど、Functionですごい細かく起動する場合にこういうことするとLatencyとコストが多少気になるかも?

意外と無い。

意外とふわふわしてるんですね。KMSとかoverkillなので当面 secrets.json でいいや、という気持ちにおちつきました :wink: ありがとうございます :bow:

認証などどうする

世界中にオープンでいくらでもリクエストできるのはおかしい。限られた者からの呼び出しだけ許すにはどうすればよいか。

「CloudStorageへの権限をチェックする」という機構を使って、CloudFunctionsにおける認証機構を実現している。だるい。 これもsecrets.json的な感じでまずは実装してしまおうと思う。

雑感

  • 慣れたらサクッと作れてよさそう
  • とりあえず東京来てくれたのむ

DRYな備忘録として

R(とPython)で平均値、中央値、分散、標準偏差を求める

Rやりはじめた。

otiai10.hatenablog.com

勉強

  • 平均値: 全要素の合計を要素数で割ったもの。重心。集合の代表値のひとつ。
    • (1/n) * Σx(k)
  • 中央値: 全要素を小さい順にならべて真ん中の値そのもの。集合の代表値のひとつ。
    • x([n/2])
  • 分散: 各要素の平均値との差分を自乗したものの和を要素数で割ったもの。集合全体のばらつき度合いを表す。
    • (1/n) * Σ((x(k) - av(k))^2)
  • 標準偏差: 分散の正の平方根。便宜的に、分散の値を、各要素の次元に合わせたもの。
    • √ ((1/n) * Σ((x(k) - av(k))^2))

なるほど。

サンプルデータ

サンプルデータを以下のように定義する。

0 1 2 3 4 5 6 7
167.857 172.364 173.964 125.874 124.698 170.048 134.241 126.285
> n <- c(167.857,172.364,173.964,125.874,124.698,170.048,134.241,126.285)

平均値

> mean(n)
[1] 149.4164

中央値

> median(n)
[1] 151.049
> # 151.049 なんて要素無いが?
> s <- sort(n)
> mean(c(s[4], s[5]))
[1] 151.049
> # 要素数が偶数なので、中央の2値の算術平均ということでした

※ Rは1-origin(インデックスが1から始まる)を知らず体感10分ぐらいハマった

分散

> var(n)
[1] 546.4991

ほんまかいな。ってことで、Pythonで確認。

>>> n = [167.857,172.364,173.964,125.874,124.698,170.048,134.241,126.285]
>>> ave = 149.4164
>>> reduce(lambda x, y: x + (ave - y)**2, [0] + n) / len(n)
478.18674823500004

んんー違うぞ。

なるほど。標本分散を求める式をRでつくってみる。

> # 平均値を各要素に持つ要素数を一致させたベクトルをつくる
> rep(mean(n), length(n))
[1] 149.4164 149.4164 149.4164 149.4164 149.4164 149.4164 149.4164 149.4164
> # これと標本集団との差分を取る
> n - rep(mean(n), length(n))
[1]  18.44063  22.94763  24.54763 -23.54237 -24.71837  20.63163 -15.17537
[8] -23.13137
> # この各要素を自乗する
> (n - rep(mean(n), length(n))) ** 2
[1] 340.0567 526.5935 602.5859 554.2434 610.9981 425.6640 230.2920 535.0605
> # この自乗した各要素を合計する
> sum((n - rep(mean(n), length(n))) ** 2)
[1] 3825.494
> # 要素数で割る
> sum((n - rep(mean(n), length(n))) ** 2) / length(n)
[1] 478.1867

Pythonでシコった値と一致した。

標準偏差

> sd(n)
[1] 23.37732

Pythonで確認

>>> import math
>>> math.sqrt(478.18674823500004)
21.86748152474354
# Rのsdの値と一致しないのは、
# Rのsdは不偏分散varの√を取ったものであろう
# と予想。
>>> reduce(lambda x, y: x + (ave - y)**2, [0] + n) / (len(n) - 1)
546.49914084
# これが不偏分散。
# この√を取ると、
>>> math.sqrt(reduce(lambda x, y: x + (ave - y)**2, [0] + n) / (len(n) - 1))
23.377321079199813
# Rのsdの値と一致した。

雑感

  • 用語の定義を理解していないので、いちいちPythonで確認しており、非効率を感じる
  • けど、たのしい
  • 標準で用意されている関数が多くて、最初はわりと覚えの問題な気がした

DRYな備忘録として

Rによるやさしい統計学

Rによるやさしい統計学

はじめてのR: ごく初歩の操作から統計解析の導入まで

はじめてのR: ごく初歩の操作から統計解析の導入まで

Rによる実証分析 ―回帰分析から因果分析へ―

Rによる実証分析 ―回帰分析から因果分析へ―

はじめてのR

インストール

% brew install r
% R

R version 3.5.0 (2018-04-23) -- "Joy in Playing"
Copyright (C) 2018 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin16.7.0 (64-bit)

R は、自由なソフトウェアであり、「完全に無保証」です。
一定の条件に従えば、自由にこれを再配布することができます。
配布条件の詳細に関しては、'license()' あるいは 'licence()' と入力してください。

R は多くの貢献者による共同プロジェクトです。
詳しくは 'contributors()' と入力してください。
また、R や R のパッケージを出版物で引用する際の形式については
'citation()' と入力してください。

'demo()' と入力すればデモをみることができます。
'help()' とすればオンラインヘルプが出ます。
'help.start()' で HTML ブラウザによるヘルプがみられます。
'q()' と入力すれば R を終了します。

> q()
Save workspace image? [y/n/c]: n
%

イントロ

> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
> x
[1] 10.4  5.6  3.1  6.4 21.7
> y <- 1/x
> y
[1] 0.09615385 0.17857143 0.32258065 0.15625000 0.04608295
> x + y
[1] 10.496154  5.778571  3.422581  6.556250 21.746083
> x * y
[1] 1 1 1 1 1
  • 1行5列のベクトルxをつくる
  • xの各要素の逆数を要素とする1行5列のベクトルyをつくる
  • ベクトルの和
  • ベクトルの内積

わかる。

ひきつづき。

Rによるやさしい統計学

Rによるやさしい統計学

はじめてのR: ごく初歩の操作から統計解析の導入まで

はじめてのR: ごく初歩の操作から統計解析の導入まで

Go言語のgoroutine、channel、sync、contextなどについて学習

なんだかんだでvirtualenvが使いやすかったのでメモ

% pip install virtualenv
% mkdir -p ${HOME}/.venv/default
% virtualenv -p python3 ${HOME}/.venv/default
% echo 'source ${HOME}/.venv/default/bin/activate' >> ${HOME}/.zshrc
% mkdir -p ${HOME}/.venv/myproject
% virtualenv -p python2.7 ${HOME}/.venv/myproject
% source ${HOME}/.venv/myproject/bin/activate
(myproject) % python --version
Python 2.7.10

【Webpack】TypeError: Cannot read property 'watchRun' of undefined

問題

% ./node_modules/.bin/webpack
# 中略
TypeError: Cannot read property 'watchRun' of undefined

調査

解決

% ./node_modules/.bin/webpack --version
3.11.0
% npm install --save-dev -D webpack@4.0
% ./node_modules/.bin/webpack --version
4.0.1
% ./node_modules/.bin/webpack
# うごいた

Pythonの開発環境立ち上げメモ(virtualenvを使った最小構成)

現状確認

% which python
/usr/bin/python
% python --version
Python 2.7.10
% which easy_install
/usr/bin/easy_install
% which pip
pip not found
%
% sudo easy_install virtualenv
% virtualenv -p /usr/bin/python ~/.venv/default
% source ~/.venv/default/activate
(default) % pip install awscli

PROMPTが変わるのうざいな

% export VIRTUAL_ENV_DISABLE_PROMPT=1

デフォルトでdefaultを使うようにしたい

% echo 'source ${HOME}/.venv/default/activate' >> ~/.zshrc

また、何かのプロジェクトで切り分けたい環境があれば

% virtualenv -p /usr/local/bin/python3 ~/.venv/another-project
% source ~/.venv/another-project/bin/activate

とかする。