VPC内にElastic Beanstalk + RDS の環境を構築して Laravel アプリをデプロイする [VPC作成編]
今後も何度か同じことを調べそうなので忘備録としてまとめておく。手順をstep by stepで記述するが、それぞれ分量が多いので以下の通り章立てを行ってエントリを分割する。 VPC構築編(本エントリ) VPCの作成 VPC、NetworkACL、SecurityGroupの確認、タグ付け VPCのDNSホスト名を有効にする サブネットの作成、タグ付け インターネットゲートウェイの作成、タグ付け、VPCにアタッチ パブリックサブネット用RoutingTableの作成 パブリックサブネット用RoutingTableにインターネットゲートウェイの関連付け 作成したサブネットをパブリックサブネット用RoutingTableに繋ぎかえる RDS構築編 MySQLの設定変更(DBパラメータグループの作成) DBサブネットグループを作成 RDS用セキュリティグループの作成 RDSインスタンスの作成 エンドポイントの確認、疎通確認 Elastic Beanstalk構築編 環境の構築 .ebextensionsによるカスタマイズ Laravel5アプリデプロイ編 gitリポジトリからclone .envの編集 アプリで使うDBのダンプファイルをRDSにコピー Laravelインストーラの準備 Laravel本体のインストール ビルトインサーバでの疎通確認 Elastic Beanstalkへのデプロイ 前提 aws cli, eb-cli をインストールしておくこと。 $ pwd /home/ikuty $ aws --version aws-cli/1.10.37 Python/2.6.6 Linux/2.6.32-573.22.1.el6.x86_64 botocore/1.4.27 $ eb --version EB CLI 3.7.6 (Python 2.7.9) Administrator AccessポリシーをもつユーザをIAMで作成し、aws-cli から該当ユーザの権限で操作できるようにしておくこと。 $ aws configure AWS Access Key ID [****************] AWS Secret Key [***********************] Default region name [ap-northeast-1] Default output format [json] VPCの環境構築 VPCを作成する。アドレスの範囲をCIDR標記で記述する。ネットワーク部を16bit取る。 vpc-cc2b11a9というIDのVPCが作成されたという報告がjsonで戻る。 $ aws ec2 create-vpc --cidr-block 10.0.0.0/16 { \"Vpc\": { \"VpcId\": \"vpc-cc2b11a9\", \"InstanceTenancy\": \"default\", \"State\": \"pending\", \"DhcpOptionsId\": \"dopt-fec65c9b\", \"CidrBlock\": \"10.0.0.0/16\", \"IsDefault\": false } } vpc-cc2b11a9というVPCにNameタグを付与する。 $ aws ec2 create-tags --resources vpc-cc2b11a9 --tags Key=Name,Value=\"vpc\" VPCとは、クラウド上に閉じたネットワークを構築する機能であり、VPC作成時に、閉じたネットワークを構成する要素が同時に生成される。構成要素は以下の通り。 Routing Table Subnet Network ACL Security group なお、Network ACL、Security group について AWS のドキュメントでは以下の通り説明している。 Amazon VPCのネットワークACL(アクセスコントロールリスト)は、サブネット内外のトラフィックを制御するファイアウォールとして任意のセキュリティを提供します。セキュリティグループの設定と同じようにACLのルールを適応することによって、VPCに追加のセキュリティ層を提供します。EC2はサブネット指定ができませんのでネットワークACLを利用することはできません。 なんのこっちゃ、だが、図を見ると一目瞭然である。NetworkACLを使ってサブネット毎にファイアウォールを定義できる! NetworkACL、SecurityGroupいずれもファイアウォールのように見えるが、以下のような違いがある。 セキュリティグループ ネットワーク ACL インスタンスレベルで動作します(第 1 保護レイヤー) サブネットレベルで動作します(第 2 保護レイヤー) ルールの許可のみがサポートされます ルールの許可と拒否がサポートされます ステートフル: ルールに関係なく、返されたトラフィックが自動的に許可されます ステートレス: 返されたトラフィックがルールによって明示的に許可されます トラフィックを許可するかどうかを決める前に、すべてのルールを評価します トラフィックを許可するかどうかを決めるときに、順番にルールを処理します インスタンスの起動時に誰かがセキュリティグループを指定した場合、または後でセキュリティグループをインスタンスに関連付けた場合にのみ、インスタンスに適用されます。 関連付けられたサブネット内のすべてのインスタンスに自動的に適用されます(バックアップの保護レイヤーなので、セキュリティグループを指定する人物に依存する必要はありません) Routing tableにタグをつける vpc-cc2b11a9というIDを持ったVPCのRouting tableを確認する。rtb-964da5f2というIDを持ったRouting tableを確認できる。 $ aws ec2 describe-route-tables --filters \"Name=vpc-id,Values=vpc-cc2b11a9\" { \"RouteTables\": [ { \"Associations\": [ { \"RouteTableAssociationId\": \"rtbassoc-b2fd42d6\", \"Main\": true, \"RouteTableId\": \"rtb-964da5f2\" } ], \"RouteTableId\": \"rtb-964da5f2\", \"VpcId\": \"vpc-cc2b11a9\", \"PropagatingVgws\": [], \"Tags\": [], \"Routes\": [ { \"GatewayId\": \"local\", \"DestinationCidrBlock\": \"10.0.0.0/16\", \"State\": \"active\", \"Origin\": \"CreateRouteTable\" } ] } ] } $ aws ec2 create-tags --resources rtb-964da5f2 --tags Key=Name,Value=\"rtb main\" NetworkACLにタグをつける $ aws ec2 describe-network-acls --filters \"Name=vpc-id,Values=vpc-cc2b11a9\" { \"NetworkAcls\": [ { \"Associations\": [], \"NetworkAclId\": \"acl-25fb1a41\", \"VpcId\": \"vpc-cc2b11a9\", \"Tags\": [], \"Entries\": [ { \"CidrBlock\": \"0.0.0.0/0\", \"RuleNumber\": 100, \"Protocol\": \"-1\", \"Egress\": true, \"RuleAction\": \"allow\" }, { \"CidrBlock\": \"0.0.0.0/0\", \"RuleNumber\": 32767, \"Protocol\": \"-1\", \"Egress\": true, \"RuleAction\": \"deny\" }, { \"CidrBlock\": \"0.0.0.0/0\", \"RuleNumber\": 100, \"Protocol\": \"-1\", \"Egress\": false, \"RuleAction\": \"allow\" }, { \"CidrBlock\": \"0.0.0.0/0\", \"RuleNumber\": 32767, \"Protocol\": \"-1\", \"Egress\": false, \"RuleAction\": \"deny\" } ], \"IsDefault\": true } ] } $ aws ec2 create-tags --resources acl-25fb1a41 --tags Key=Name,Value=\"acl main\" SecurityGroupにタグをつける SecurityGroupを確認する。ID=sg-23fe8447というSecurityGroupが存在することがわかる。 $ aws ec2 describe-security-groups --filters \"Name=vpc-id,Values=vpc-cc2b11a9\" { \"SecurityGroups\": [ { \"IpPermissionsEgress\": [ { \"IpProtocol\": \"-1\", \"IpRanges\": [ { \"CidrIp\": \"0.0.0.0/0\" } ], \"UserIdGroupPairs\": [], \"PrefixListIds\": [] } ], \"Description\": \"default VPC security group\", \"IpPermissions\": [ { \"IpProtocol\": \"-1\", \"IpRanges\": [], \"UserIdGroupPairs\": [ { \"UserId\": \"281631559249\", \"GroupId\": \"sg-23fe8447\" } ], \"PrefixListIds\": [] } ], \"GroupName\": \"default\", \"VpcId\": \"vpc-cc2b11a9\", \"OwnerId\": \"281631559249\", \"GroupId\": \"sg-23fe8447\" } ] } $ aws ec2 create-tags --resources sg-23fe8447 --tags Key=Name,Value=\"sg main\" VPCのDNSホスト名を有効にする VPC内のRDSに外部から接続できるようにDNSホスト名を有効にする。 #有効にする $ aws ec2 modify-vpc-attribute --vpc-id vpc-cc2b11a9 --enable-dns-hostnames #確認する $ aws ec2 describe-vpc-attribute --vpc-id vpc-cc2b11a9 --attribute enableDnsHostnames { \"VpcId\": \"vpc-cc2b11a9\", \"EnableDnsHostnames\": { \"Value\": true } } サブネットを作成する 最初のVPC作成時に、16ビットをネットワーク部として使う意図で、CIDR標記で/16を設定した。さらに、8ビットをサブネットとして利用するため、CIDR標記で/24を設定する。8ビット分(第3オクテット分)がサブネットとして利用でき、そのうち .0、.1、.2 のサブネットを作成する。第4オクテットがホスト部となるが、8ビット全て利用することはできず、利用できる最大数がそれぞれ\"AvailableIpAddressCount\"に書かれてる。なお、各サブネットにそれぞれIDが振られる。 RDSをMultiAZで運用しない場合でもDBサブネットグループを作成する際に2つのAvailabilityZoneが必要なので異なるAvailabilityZoneに対応するサブネットを作成する。 $ aws ec2 create-subnet --vpc-id vpc-cc2b11a9 --cidr-block 10.0.0.0/24 --availability-zone ap-northeast-1a { \"Subnet\": { \"VpcId\": \"vpc-cc2b11a9\", \"CidrBlock\": \"10.0.0.0/24\", \"State\": \"pending\", \"AvailabilityZone\": \"ap-northeast-1a\", \"SubnetId\": \"subnet-b71eeac1\", \"AvailableIpAddressCount\": 251 } } # ap-northeast-1a AvailabilityZone $ aws ec2 create-subnet --vpc-id vpc-cc2b11a9 --cidr-block 10.0.1.0/24 --availability-zone ap-northeast-1a { \"Subnet\": { \"VpcId\": \"vpc-cc2b11a9\", \"CidrBlock\": \"10.0.1.0/24\", \"State\": \"pending\", \"AvailabilityZone\": \"ap-northeast-1a\", \"SubnetId\": \"subnet-131feb65\", \"AvailableIpAddressCount\": 251 } } # ap-northeast-1c AvailabitliyZone $ aws ec2 create-subnet --vpc-id vpc-cc2b11a9 --cidr-block 10.0.2.0/24 --availability-zone ap-northeast-1c { \"Subnet\": { \"VpcId\": \"vpc-cc2b11a9\", \"CidrBlock\": \"10.0.2.0/24\", \"State\": \"pending\", \"AvailabilityZone\": \"ap-northeast-1c\", \"SubnetId\": \"subnet-d6d2d38f\", \"AvailableIpAddressCount\": 251 } } サブネットにタグを付与する 作成したサブネットには、それぞれ、subnet-b71eeac1、subnet-131feb65、subnet-d6d2d38f というIDが振られている。それぞれにタグを付与する。 $ aws ec2 create-tags --resources subnet-b71eeac1 --tags Key=Name,Value=\"subnet public web\" $ aws ec2 create-tags --resources subnet-131feb65 --tags Key=Name,Value=\"subnet public db1\" $ aws ec2 create-tags --resources subnet-d6d2d38f --tags Key=Name,Value=\"subnet public db2\" インターネットゲートウェイの作成 インターネットゲートウェイを作成し、タグを付与する。 $ aws ec2 create-internet-gateway { \"InternetGateway\": { \"Tags\": [], \"InternetGatewayId\": \"igw-5bb8203e\", \"Attachments\": [] } } $ aws ec2 create-tags --resources igw-5bb8203e --tags Key=Name,Value=\"igw main\" インターネットゲートウェイをVPCにアタッチする。 #アタッチ $ aws ec2 attach-internet-gateway --internet-gateway-id igw-5bb8203e --vpc-id vpc-cc2b11a9 #確認 $ aws ec2 describe-internet-gateways --internet-gateway-id igw-5bb8203e { \"InternetGateways\": [ { \"Tags\": [ { \"Value\": \"igw main\", \"Key\": \"Name\" } ], \"InternetGatewayId\": \"igw-5bb8203e\", \"Attachments\": [ { \"State\": \"available\", \"VpcId\": \"vpc-cc2b11a9\" } ] } ] } パブリックサブネット用のRoutingTableを作成する VPC作成時に\"rtb-964da5f2\"というIDを持つRoutingTableが作成されていることを確認した。\"rtb-964da5f2\"にはVPC作成時に自動生成されたデフォルトサブネットが紐付いている。 今回、オリジナルのサブネット、インターネットゲートウェイを新たに作成するのに合わせて、RoutingTableを新規作成する。 # RoutingTable の作成 $ aws ec2 create-route-table --vpc-id vpc-cc2b11a9 { \"RouteTable\": { \"Associations\": [], \"RouteTableId\": \"rtb-fe5cb49a\", \"VpcId\": \"vpc-cc2b11a9\", \"PropagatingVgws\": [], \"Tags\": [], \"Routes\": [ { \"GatewayId\": \"local\", \"DestinationCidrBlock\": \"10.0.0.0/16\", \"State\": \"active\", \"Origin\": \"CreateRouteTable\" } ] } } # タグ付け $ aws ec2 create-tags --resources rtb-fe5cb49a --tags Key=Name,Value=\"rtb public\" パブリックサブネット用RoutingTableにインターネットゲートウェイを関連付ける $ aws ec2 create-route --route-table-id rtb-fe5cb49a --destination-cidr-block 0.0.0.0/0 --gateway-id igw-5bb8203e { \"Return\": true } 新たに作成したサブネットをパブリックサブネット用RoutingTableに繋ぎかえる $ aws ec2 associate-route-table --route-table-id rtb-fe5cb49a --subnet-id subnet-b71eeac1 { \"AssociationId\": \"rtbassoc-0ce55a68\" } $ aws ec2 associate-route-table --route-table-id rtb-fe5cb49a --subnet-id subnet-131feb65 { \"AssociationId\": \"rtbassoc-c1e45ba5\" } $ aws ec2 associate-route-table --route-table-id rtb-fe5cb49a --subnet-id subnet-d6d2d38f { \"AssociationId\": \"rtbassoc-76c97612\" }
FitbitAPI long range restingHeartRate plot as health index .
If the sympathetic nervous system is stimulated, the points of restingHeartRate rise directly.I think \"restingHeartRate\" will reveal the index of relaxing myself. Long term observation of restingHeartRate may specify mental or physical rhythms. So, I scracthced the script to retrieve the long term \"restingHeartRate\" from the Fitbit Heartrate API to plot its trend. The restingHeartRate itself may not be userful, but relatively useful The point of restingHeartRate itself (75,82,..) may not be useful to observe the trend of individulas. The uptrend of restingHeartRate will warn that sympathetic nervous system is continuously stimulated, and show the necessity of resting, then avoid from streath. So it\'s important the standard of restingHeartRate and it\'s important to relate the point and physical or mental conditions. Index for rest Vice versa, if this index get the uptrend, agressively rest. Plot example As below, the x axis show the date, between 2015/08 and 2016/06, about 10months. the y axis show the point of the restingHeartRate retrieved from Fitbit Heartrate api.
OAuth2 implicit grant flow で FitbitAPI の認証をパスするサンプル
これまで、OAuth2 Authenticate code grant flow で FitbitAPI の認証をパスしてデータを取得する事例を書いてきた。implicit grant flow では認証をパスできないのか調べてみた。 Fitbit API の認証を implicit grant flowでパスする際の特徴は以下の通り。 implicit grant flow でFitbitAPI の認証をパスするには、アプリケーションタイプを Client として登録する必要がある。 access tokenの消費期限はユーザが決定する。 refresh tokenを使ってaccess tokenを更新する場合、ユーザの承認が必要となる。 本エントリにて、implicit grant flow で FitbitAPI の認証する方法を step by step で追ってみる。 Authenticate code grant flow と implicit grant flow の違いについて、↓が参考になりました。What is the difference between the 2 workflows? When to use Authorization Code flow? Authenticate code grant flow Authenticate code grant flow は以下の流れだった。 ClientID, ClientSecret から認可コードを取得する 認可コードと accessToken を交換する accessTokenを取得するためには ClientID, ClientSecret を保持しておく必要があった。 本家から図を転載する。 implicit grant flow 対して、implicit grant flow は以下のような流れとなる。 まず、本家からの図の転載。 まとめると次の通り。 ClientIDを使って accessToken を取得する https://www.fitbit.com/oauth2/authorize?state=fx2D6zGcbNVltC15sQlxh4wP8U8HH53%2B &scope=activity+heartrate+location+profile+settings+sleep+social+weight &response_type=token &client_id=xxxxxx &redirect_uri=http%3A%2F%2Fhoge.com%3A8002%2Ftest.php ブラウザ上で認証画面が表示される。この画面はブラウザである必要があり埋め込んでスルーしたりすることはできない。 許可すると、指定したコールバックURLがパラメータ付きで呼び出される。(当然伏字です) http://hoge.com:8002/test.php#scope=weight+location+social+settings+heartrate+sleep+activity+profile &state=fx2D6zGcbNVltC15sQlxh4wP8U8HH53%252B &user_id=xxxxx &token_type=Bearer &expires_in=69483 &access_token=eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NjY5MzMyMzcsInNjb3BlcyI6InJ3ZWkgc nBybyByaHIgcmxvYyByc2xlIHJzZXQgcmFjdCByc29jIiwic3ViIjoiM1FaVllYIiwiYXVkIjoiMjI3V EZKIiwiaXNzIjoiRml0Yml0IiwidHlwIjoiYWNjZXNzX3Rva2VuIiwiaWF0IjoxNDY2ODYzNzU0fQ.oy 0Px-mEauh5Jgw1yS8PF94U37tUW2Q35fbCHKcDFHU ここで得られたaccess_tokenを使って、データアクセスAPIを呼び出す。その際、クエリに含まれるaccess_tokenをAPIにアクセスする度に利用する。クエリに付けるのではなくBASIC認証のヘッダとして付与する。 URLのクエリからaccess_tokenを取得しBASIC認証で利用する。 GET https://api.fitbit.com/1/user/-/profile.json Authorization: Bearer eyJhbGciOiJIUz******************************* ******BybyByaHIgcmxvYyByc2xlIHJzZXQgcmFjdCByc29jIiwic3ViIjoiM1FaVll YIiwiYXVkIjoiMjI3VEZKIiwiaXNzIjoiRml0Yml0IiwidHlwIjoiYWNjZXNzX3Rva2 V**************************************eLtCF0V6IISPinTxy_ZgCLQl1tB0 rEMeqVk4 access_token を使いまわしていると、いずれ access_token の消費期限に到達する。 消費期限に到達した access_token を使ってデータ取得を行うと、以下のようなレスポンスが返ってくる。 { \"errors\": [ { \"errorType\": \"expired_token\", \"message\": \"Access token expired: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MzAzNDM3MzUsInNjb3BlcyI6Indwcm8gd2xvYyB3bnV0IHdzbGUgd3NldCB3aHIgd3dlaSB3YWN0IHdzb2MiLCJzdWIiOiJBQkNERUYiLCJhdWQiOiJJSktMTU4iLCJpc3MiOiJGaXRiaXQiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4iLCJpYXQiOjE0MzAzNDAxMzV9.z0VHrIEzjsBnjiNMBey6wtu26yHTnSWz_qlqoEpUlpc\" } ] } ここで、Authenticate code grant flow と同様に refresh_token を使って access_token を更新する必要があるのだが、ドキュメントに記述があるように、implicit grant flow では refresh_token を取得するために client_id を使って再度認証する必要がある。 client_id はアプリケーションにストアすべきデータではなく、ユーザによる操作が必要。 Unlike the Authorization Code Grant Flow, the refresh tokens are not issued with the Implicit Grant flow. Refreshing a token requires use of the client secret, which cannot safely be stored in distributed application code. When the access token expires, users will need to re-authorize your app. また、以下に記述があるように、implicit grant flow における access_token の消費期限は Authenticate code grant flow のそれよりも長めに設定される。アプリケーション側で予め消費期限を設定できるが、最終的には認証画面においてユーザが消費機嫌を決めること値が決まる。 Access tokens from the Implicit Grant Flow are longer lived than tokens from the Authorization Code Grant flow. Users may specify the lifetime of the access token from the authorization page when an application uses the Implicit Grant flow. The access token lifetime options are 1 day, 1 week, and 30 days. Applications can pre-select a token lifetime option, but the user ultimately decides.
FitbitAPI 1分毎の歩数を24時間、1ヶ月分 3次元プロットしてみる
前回、1分毎の消費カロリーを24時間 1ヶ月分 3次元プロットしてみた。 データを眺めていてもイメージがわかないので、前回と同様に歩数データもプロットしてみる。 Rawデータの3次元プロット X軸が日にち、Y軸が00:00:00からの経過時間(分)、Z軸が歩数である。 データはFitbitのtrackerが計測したもので、心拍数と同様に、1分あたり小数点のデータが入っている。 あくまでセンサーが出力した数値がそのまま出てくるのだろうと推測される。 (積分すると、ちゃんと1日の歩数になる。サンプリンク周期の方を1分に固定したために歩数の方が小数点になっているんだろう) 値が0から170-180までのレンジにあることが興味深い。 ジムのマシンでガンガン飛ばしているとき、マシンの数値は75回転/分から80回転/分程度。 1回転するのに2歩必要なのだから、150歩から160歩ということになる。 ちょっと多いような気もするが、だいたいそんなもん。 消費カロリーの3次元プロットと比べると、カロリーを消費していない時間帯にも割と歩いていることがわかる。 消費カロリーがフラットだが、歩数が立ち上がっている領域は、心拍数が低い領域である。 心拍数を上げずに歩いてもカロリーは消費されないことがわかる。 積分値の3次元プロット trackerが出力したRawデータを積分して、累積値を3次元プロットしてみる。 消費カロリーの積分プロットは、基礎代謝のおかげて傾きがゼロになることはないのだが、 座り仕事が続くと、歩数に関しては傾きがゼロになり、上記の図のように階段状になる。 気をぬくと、24時間フラットな図になるだろう。 基礎代謝の分を除けば、概ね、消費カロリーと歩数は相関がありそう(計算してないけど)。 万歩計はとても安くて揃えやすいから、歩数を健康の目標にすることが多いのだろうね。
OAuth2 Implicit Grant Flow とセキュリティ
Implicit Grant Flow を「認証」のための方法として使ってはならない、というが、ちょっと不勉強で理解が曖昧だったので、少し深く理解してみることにした。 参考にしたソースは下記記事 The problem with OAuth for Authentication. 単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる OAuth2 implicit grant flow 自分が所有する情報に対してアクセスを認めることを認可と呼ぶ。 自分が所有する情報と自分の間に第三者が入らない場合 その鍵を自分が管理することに問題はない アクセスするための鍵を自分自身が管理し、安全が保障されている通信の中で直接鍵を利用できる。 自分が所有する情報と自分の間に第三者(Webシステムやアプリ)が入る場合 Webシステムやアプリに対して自分自身の情報に対するアクセス権を移譲する。 鍵をWebシステムやアプリに渡してしまうと、Webシステムやアプリの脆弱性により鍵そのものが危険にさらされてしまう。 代わりに合鍵(accessToken)を作成し、Webシステムやアプリは合鍵を使って自分自身の情報にアクセスするようにする。 以後、第三者は大元のアクセス権に触れずに、accessToken/refreshTokenを使う。 第三者システムを仮に実装してみると、accessToken/refreshTokenを該当システムのIDに紐付けて保存することで、そのシステム上のログインユーザにリソースオーナーへのアクセス権を付与できる implicit grant flow を認証に使う implicit grant flow を認証に使う、というのは、第三者システムが、リソースオーナーから取得したIDをそのまま第三者システムのIDとして使うことを指す。例えば、GoogleやYahooに保存されたID(emailなど)に対してアクセスを許可するだけで、第三者システムのログインそのものを許可してしまうようなものを指す。 だいたいどんなサービスを作るにしても、最初は知名度が低くて、ユーザ登録などしてくれないことがほとんど。 サービス提供者が考えるのは、GoogleやYahooなどのログインを自システムへのログインに代替できないか、ということ。 自分自身の情報へのアクセスを認めただけなのに、勝手に自分自身のログインであることの証明に使われてしまう、というのが問題。 問題は 第三者システムに悪意はなくて、ただ借りパクしたいだけなら被害はない。 第三者システムが悪い奴で、取得したaccessToken/refreshTokenを使って、本人になりすまして、別の第三者システムにログインしてしまったら...。 別の第三者システム的には、本人からのアクセスなのか、本人になりすましたシステムからのアクセスなのか、区別することができないから、CSRF対策を行って防げる攻撃ではない。
FitbitAPI 24時間消費カロリーを1か月分 3次元プロットしてみる
ふと、そのようなことを思いついた。 Fitbitのtrackerは、消費カロリー、歩数、移動距離、階数、上下移動距離を24時間1分間隔で保存している。 心拍数は「1秒間隔」というエクストリームな保存の仕方だが、アクティビティは常識的な詳細度だ。 1分間隔で保存された各値を積分すると1日分の値になる。 各値と、累積値をそれぞれプロットしてみる。 Rawデータの3次元プロット APIから返されるデータをそのまま3次元プロットしてみた。 心拍数が安静時心拍数に近い領域では消費カロリー値がゼロに近い。たぶんこれが基礎代謝。 日中(ほとんどPC作業...orz)、やはりゼロに近い(1から3の間)。 歩いたり、ジムで運動したときにオレンジの領域がポツポツ現れる。 累積値の3次元プロット 当たり前だが、1日の消費カロリーは瞬間消費カロリーの積分だ。 各値の積分値が1日分の値になるはずだ。1分毎の累積値を計算してプロットしてみた。 ジムで1時間程度の有酸素運動を行うとき、傾きが一気にきつくなり最終的に2700から2800程度に到達する。 サボると2400程度に留まる。 上記は、傾きが小さい(基礎代謝が低い)のを、なんとか運動で補正してカロリーを消費している図。 消費カロリーには基礎代謝が積分で効いてくるので、基礎代謝の上昇量(値)から想像する以上に消費カロリーは上昇することになる(と思う)。 筋肉無いのは悪だ。
FitbitAPI 1ヶ月分の24時間心拍数を3次元プロットしてみる
ふと、タイトルのようなことを思い立った。 詳細度=1分間隔で1日分の心拍数を取得する 月初から月末まで繰り返す X軸=日にち、Y軸=0時0分からの経過時間、Z軸=心拍数となるようにデータを格納する gnuplotのsplotコマンドで格納したデータをプロットする Fitbit API の呼出し制限 思い立つこと20分、コードを作成しAPIサーバからガシガシデータを取得していたのだが、ある時点からデータが取れなくなった。 以下のようなメールがFitbitから届いた。 Hello XXXXXXX, This is an automated courtesy notice. No action is required. Your application, Test Php Application, exceeded the Fitbit API client+viewer rate limit for the users listed below. HOGEHOGE (Hit: 2016/06/18 18:08:38, Reset time: 2016/06/18 18:59:59) If you receive this notice regularly and would like to discuss potentially more efficient ways to use the Fitbit API, please contact api@fitbit.com. Best regards, The Fitbit API team 1分刻みの24時間データを31日分、5回ほどのTry&Errorでリミットに達した。APIから取得したデータをファイルに書き込んでみると、20KBくらいになる。jsonのオーバーヘッドがあるとしても1MBは取っていないはずだ。1時間後にはリミッターが解除されるので、あくまで攻撃対策なのだろうか。 実際、サービスを作るとしたらもっと高頻度にAPIを叩くと思うので、ちょっと大風呂敷広げている割にはリミッターが早いな、という印象。 ちなみに、石狩リージョンのさくらvpsからFitbitAPIに上記のような叩き方をした場合、ターンアラウンドタイムは5秒くらい。結構長い。都度つど取って良いデータではなく、1度とったら使いまわせるように保存しておかないとダメだ。 プロットしてみる プロットしてみた。gnuplotで時系列データを扱うのは割と面倒。 set grid xtics ytics mxtics mytics set datafile separator \',\' set size square set xdata time set timefmt \'%Y-%m-%d\' set format x \'%d\' set xrange [\'2016-05-01\':\'2016-05-31\'] set yrange [0:1440] set zrange [40:200] set xtics 345600 set mxtics 2 set xlabel \"day\" set ylabel \"minutes elapsed [min]\" set zlabel \"heart rate [bpm]\" set terminal png set view 60,30,1,1 set output \"hoge.png\" set ticslevel 0.5 splot \'hoge.csv\' using 1:2:3 w d lc palette title \'Heart Rate every day\' 結果 なんとなく絵になるグラフが出てきただけで、思っていたのと違うな。 手前から奥へ0時0分からの時間経過を表すが、大陸棚みたいになっている部分が睡眠時間。経験的に大陸棚が浅いと体調が悪い。心肺機能を高めると安静時心拍数が下がる(大陸棚が深くなる)ので、結果として体調が良くなる効果がある(と思う)。 奥の方でオレンジ色がポツポツ立ち上がっているのは、週4でジムに通って1時間の有酸素運動をしている部分。有酸素運動1時間というのは慣れても楽な運動ではないのだが、1日のうちの割合でいったら大した率ではないことがわかる。こうして体積を俯瞰してみると、ジムで運動することによる心拍数の増加時間の体積は無視できるような大きさに過ぎない。あぁ、悲しいなぁ。 大陸に上がってから、日によってベースとなる高度が異なることがわかる。例えば12日あたりを境にベースとなる高度が下がっている。カレンダーを遡ってみると、実はこの日は慢性的なストレスを抱える原因になっている出来事にある程度の区切りがついた日でした!すげぇ、心拍数。すげぇ。 心拍数の推移を体積として俯瞰すると、こうして何事もなかったかのようにやり過ごした心身に対するストレスを自覚するきっかけになる(かもしれない)。 ということで、とても面白いので、もう少しFitbitAPIで遊んでみます。
FitbitAPI 詳細な心拍数データを時系列で取得する
FitbitAPI 詳細な心拍数データを時系列で取得する おそらく、Fitbitに蓄えられているデータのうち最も利用価値が高いであろう、詳細な心拍数データを取得してみる。ドキュメントの先頭に以下の記述があり、Personal としてアプリを登録した場合にのみ利用できる。 Access to the Intraday Time Series for personal use (accessing your own data) is available through the \"Personal\" App Type. このデータを使うアプリケーションは、ユーザにとってFitbitに代えがたい価値を提供しなければならない。としている。心拍数データがあってもなくても良いようなクソアプリには使うなよ、ということだろう。非営利目的ならOKとのこと。24時間心拍数を計測可能なウェラブルデバイスが何に応用できるのか、Fitbitもこうして一般に門戸を開いて研究中ということか。超絶便利で画期的なアプリやシステムを考案したら、case-by-caseで商用利用可能の許可が下りるそうだ。 Fitbitという会社の価値は24時間心拍数データの保存にあるということはわかった。 Access to the Intraday Time Series for all other uses is currently granted on a case-by-case basis. Applications must demonstrate necessity to create a great user experience. Fitbit is very supportive of non-profit research and personal projects. Commercial applications require thorough review and are subject to additional requirements. Only select applications are granted access and Fitbit reserves the right to limit this access. To request access, email api@fitbit.com. 実際、データ量が大きいので、現実的な問題としてリソース負荷も大きな理由の一つだろう。 API概要 RESTfulAPI は以下の通り用意されている。何故か dateとend-dateを両方指定できる書式があるが、以下の通り 1日以上のデータを取得することはできない。これはimplicitに\"超絶凄いアプリを考え付いたら複数日分取得できるようにするよ\"ということなのかもしれない。 If your application has the appropriate access, your calls to a time series endpoint for a specific day (by using start and end dates on the same day or a period of 1d), the response will include extended intraday values with a one-minute detail level for that day. GET /1/user/-/activities/heart/date/[date]/[end-date]/[detail-level].json GET /1/user/-/activities/heart/date/[date]/[end-date]/[detail-level]/time/[start-time]/[end-time].json GET /1/user/-/activities/heart/date/[date]/1d/[detail-level].json GET /1/user/-/activities/heart/date/[date]/1d/[detail-level]/time/[start-time]/[end-time].json parameterformatmeanings dateyyyy-MM-ddデータを取得したい日付 end-dateyyyy-MM-ddデータを取得したい日付(dateと同じである必要がある...) start-timeHH:mm(オプション)取得時間を範囲指定する場合、開始時刻を指定できる end-timeHH:mm(オプション)取得時間を範囲指定する場合、終了時刻を指定できる detail-level1sec or 1minデータを1秒区切りで取得するか1分区切りで取得するか。1日は24*60*60=86400秒だ。巨大なレスポンスとなる。 解像度と精度 脈波から心拍数を推測しているとはいえ、心拍数はだいたい1分間に60から120くらいの数値だ。1秒に1回か2回、1Hzか2Hzか、そんな程度の頻度で出現するデータだ。「1秒毎の心拍数」というのは一体どう解釈すれば良いのだろうか。 実際に計測しているのは腕の血流量だが、もしかしたら血流量の変動は心拍数を推測する以上に高頻度に変動する量なのかもしれない。実際、本エントリの末尾に「1秒毎の心拍数」をプロットして貼ってみたが、同一時間でかなりの幅をもっている。単なる歪みなのか、それとも...。detail-level = 1sec を許す fitbit の狙いが知りたい。 レスポンス概要 activities-heart-intradayの下にdatasetという名前で、時刻と心拍数のセットが永遠に繰り返される。 実行例 レスポンスが巨大すぎて実行結果をWordPressのエディタに貼り付けたら編集できなくなった。。代わりにデータをプロットしたので貼っておく。 本当に24時間分の心拍数を1秒区切りで返してくる。ときどき1.5秒になったりする。APIサーバの応答は早い。APIサーバとクライアントの間のネットワーク帯域に依存しそう。PHPがjsonをパースして配列に格納する処理の時間も無視できなさそう。 こんなデータを24時間記録するのにバッテリーが5日も持つなんて!面白くないが最初の2分を載せてみる。こんな調子で24時間心拍数データを記録できるFitbitChargeHRに感謝! [activities-heart-intraday] => Array ( [dataset] => Array ( [0] => Array ( [time] => 00:00:05 [value] => 73 ) [1] => Array ( [time] => 00:00:20 [value] => 74 ) [2] => Array ( [time] => 00:00:30 [value] => 76 ) [3] => Array ( [time] => 00:00:45 [value] => 76 ) [4] => Array ( [time] => 00:00:50 [value] => 74 ) [5] => Array ( [time] => 00:01:05 [value] => 74 ) [6] => Array ( [time] => 00:01:10 [value] => 73 ) [7] => Array ( [time] => 00:01:20 [value] => 72 ) [8] => Array ( [time] => 00:01:30 [value] => 74 ) [9] => Array ( [time] => 00:01:40 [value] => 75 ) [10] => Array ( [time] => 00:01:55 [value] => 75 )
FitbitAPI 心拍数ゾーン毎の滞在時間を取得する
一般的に運動の強度を心拍数の下限と上限の範囲で定義することが多いようだ。Fitbitでは心拍数ゾーンが定義されていて、各ゾーンでの滞在時間数を取得できるようになっている。 API概要 書式は2通り用意されている。 GET /1/user/[user-id]/activities/heart/date/[date]/[period].json GET /1/user/[user-id]/activities/heart/date/[base-date]/[end-date].json parameterformatmeaning dateyyyy-MM-ddperiodパラメータを用いて期間指定する場合にその最終日を指定する period1d, 7d, 30d, 1w, 1m定義された識別子により範囲指定を行う。 base-date]yyyy-MM-dd取得範囲を指定する場合の開始日 end-date]yyyy-MM-dd取得範囲を指定する場合の終了日 他のAPIと同様に1か月を超えて範囲を設定することはできない。 レスポンスのスキーマ heartRateZoneオブジェクト parameterformatmeaning caloriesOutXXX.XXXX該当心拍数ゾーンにおける消費カロリー 有効桁数が考慮されている(と思う) maxXXX該当心拍数ゾーンにおける最大心拍数 minXXX該当心拍数ゾーンにおける最小心拍数 minutesXXX該当心拍数ゾーンにおける滞在時間 nameXXX該当心拍数ゾーンを識別する名称Out of Range,Fat Burn,Cardio,Peakが定義されているようだ。このうち、Fat Burnには脂肪燃焼ゾーン、Cardioには有酸素運動ゾーン、のように日本語訳が付けられている。 restingHeartRate安静時心拍数 その他 parameterformatmeaning dateTimeyyyy-MM-ddデータの日時 restingHeartRate安静時心拍数 実行例 customHeartRateZones という空のオブジェクトが返ってきた。どこかで設定できるのだろうか。 Array ( [activities-heart] => Array ( [0] => Array ( [dateTime] => 2016-06-14 [value] => Array ( [customHeartRateZones] => Array ( ) [heartRateZones] => Array ( [0] => Array ( [caloriesOut] => 1480.75044 [max] => 92 [min] => 30 [minutes] => 1137 [name] => Out of Range ) [1] => Array ( [caloriesOut] => 824.94048 [max] => 128 [min] => 92 [minutes] => 221 [name] => Fat Burn ) [2] => Array ( [caloriesOut] => 585.03432 [max] => 156 [min] => 128 [minutes] => 67 [name] => Cardio ) [3] => Array ( [caloriesOut] => 0 [max] => 220 [min] => 156 [minutes] => 0 [name] => Peak ) ) [restingHeartRate] => 69 ) ) )
FitbitAPI 体重-体脂肪率グラフを作ってみる
せっかく生データを取得できるようになったので、記念にグラフを作ってみることにする。3ヶ月分のデータを2軸のグラフとしてプロットしてみたい。 方針は以下の通り。 認証 djchen/oauth2-fitbitを使用しOAuth2をパスする 過去エントリを参照のこと 取得 BMI/体重/(体脂肪率)取得APIを叩く 期間は3ヶ月分とする。APIを3回叩くことになる。 出力 gnuplotで描画するため、csvとする 体重、体脂肪率はFitbitAPIから得たデータを加工なしにそのまま使う 日付についてFitbitのyyyy-MM-ddからgnuplotのyyyy/MM/ddに変換する 描画 gnuplot。学生のとき以来だな^^ sakura vps の CentOS6 に yum で入れる。便利になったものだ。 gnuplot sakura vpsにgnuplotをインストール。当然 X11とかないのでバッファに出力するだけ。 $ pwd /home/ikuty $ sudo yum install gnuplot gnuplotスクリプトは以下の通り。やはり単にプロットするだけなら R より GnuPlot の方が楽で良い。 set xdata time set timefmt \"%Y/%m/%d\" set datafile separator \",\" set terminal png set output \"plot.png\" set y2tics set y2range [12:22] set multiplot set xlabel \"date\" set ylabel \"weight[kg]\" set y2label \"fat[%]\" set grid plot \"plot.csv\" using 1:2 with lp axes x1y1 replot \"plot.csv\" using 1:3 with lp axes x1y2 外部ファイルを読み込む。 $ gnuplot < gnu.plot 結果 Arialフォントが無いので文字がとても汚いが、グラフ専用ツールなので形にはなる。 朝晩2回計測しているのだがWiththingsとの連携だと1日のうち最後に計測した値だけがFitbitにストアされる。なので、以下のグラフは夜の体重、体脂肪率のグラフということになる。それではハッキリ言って意味がない。 ※体脂肪率が下がっていないのに体重が落ちているのは、筋肉量が落ちている、ということ。カロリー収支のマイナス幅が大きすぎて筋肉量も減らしてしまっているのかも。