スマートホーム支援LINE BOT
最近スマートホームって流行ってますよね
もはやスマホといえばスマートフォンでなくスマートホームだろってくらいの勢いなわけですが,その流行に乗っかり我が家も順調にスマートホーム化されつつあります
アクチュエータは全部Alexa対応なので声だけで操作可能
ただ、いろんなメーカーのが入り混じってるせいで、家の外から操作しようとすると各メーカーのアプリからじゃないとダメ
これがまあ不便なので、統一されたインターフェースから全部操作できたら楽ですよね
じゃあ何から操作しようかと考えたとき,一番工数少なくて楽そうなのはLINE BOTかなぁと
ついでにいろいろ盛り込んでちょっと作ってみました.
概要
という感じで主な機能はLambda様が行ってくださってます.
現状の機能としては
- 部屋の温湿度情報提供(家に配置したRaspberry Pi + DHT11から取得)
- 次のバス時刻情報提供(家⇔最寄り駅の双方向 自作のライブラリ使用)
- 天気情報提供(LWWSから取得)
- エアコン操作(IFTTT使ってスマートリモコン制御)
- チャットボット(A3RTのTalk APIと繋ぐ)
見ての通り,エアコン以外に本来想定してた機能を載せていません.
外から操作したい家電ってエアコン以外なかった
コード
完成品
いちおう全部動きました
下のリッチメニューが地味に便利でよい
不満なとこ
相変わらずコードが絶望的に汚い
絶対もっときれいに書くやり方あるはず
あと、作ったはいいもののバス時刻取得用の自作のライブラリが仕様上反応遅いのでこれもDynamoDBにキャッシュしとこうかなと思案中
Kubernetes in Docker こと kind の環境構築
Kubernetesの基本については完全に理解したので、実際に触っていろいろやってみたくなりました
そこで実際に環境構築するうえで取れる手段は以下の3つくらいかなぁと
- Google Kubernetes Engine や Amazon EKSなんかのパブリッククラウドで使う
- Raspberry Pi なんかの安価なPC複数台で実際に作ってみる
- 学習用のツールを使う
1は多少とはいえ料金発生しちゃいますし、2は初期投資額がでかい
もちろんそのくらい払えやってのはあるんですがある程度使えるようになるまでは安く済ませたい
ということで、3の学習用ツールに目を付けました
ツールもいろいろあるんですが、後述の通り機能面に若干の不満が
そこで最近知った kind を使って環境構築してみましたので以下まとめ
kind とは
Kubernetes IN Dockerの略称
k8sをローカルで構築しテストするためのツールの一つ
Windows,Mac,Linuxに対応
k8s公式もこれを使ってテストしてるらしい
他のツールとの相違点
kind以外にもminikubeなんかがある
kind最大の特徴はマルチノードクラスタが作成可能な点
k8sでは、複数のノードが参加して一つのクラスタを構成するのが一般的
しかし、他のツールではシングルノードクラスタしか構成できず、限られた機能しか使うことができなかった
kindはマルチノードクラスタが作成可能なため、全てではないがより多くの機能を使用可能
kindの仕組み
kindでは、各ノードとして動作するdockerコンテナをデプロイしてクラスタを構成する
デフォルトで提供されているコンテナは、Ubuntu18.04を元に必要ないサービスを削除してサイズを小さくしたイメージ「node-image」を使用
自身で用意したイメージを使用して構成することもできる
Quick Start
今回はVMwareの仮想マシン上にマルチノードクラスタを作成し、nginxをデプロイするとこまでやってみます
仮想マシンはUbuntu18.04.3で、メモリ8GB、CPU4コアで動かしてます
メモリは公式によると最低8GB必要で、コア数についての記述は見つかりませんでしたがマルチノードクラスタなら4コアはあげといたほうが無難かと
kindのインストール準備
kindインストールの前に、docker、kubectl、Go言語を使用可能にする必要がありますのでまずはそっちから
とりあえずcurl
とgcc
が必要になるので入れておきましょう
$ sudo apt install -y curl gcc
1. dockerのインストール
dockerはapt install
で
$ sudo apt install -y docker.io $ docker -v Docker version 18.09.7, build 2d0083d $ sudo usermod -aG docker suzkor
最後に、dockerグループにユーザーを追加します
これでdockerは準備完了
グループ変更したので、一回再起動しておきます
2. kubectlのインストール
公式のドキュメントを参考にいれて行きます
$ sudo apt-get update && sudo apt-get install -y apt-transport-https $ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - $ echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list $ sudo apt-get update $ sudo apt-get install -y kubectl $ kubectl version Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.3", GitCommit:"2d3c76f9091b6bec110a5e63777c332469e0cba2", GitTreeState:"clean", BuildDate:"2019-08-19T11:13:54Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"} The connection to the server localhost:8080 was refused - did you specify the right host or port?
サーバーと通信できてないというエラーは出てますが、まあそんなものはないので当然
これでkubectlの準備もできました
3. Go言語のインストール
$ curl -O https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz $ sudo tar -C /usr/local -xzf go1.12.9.linux-amd64.tar.gz $ export PATH=$PATH:/usr/local/go/bin $ export PATH=$PATH:$(go env GOPATH)/bin
最後の2行は起動時毎回必要なので .profile
にでも記載しておきましょう
これでGoの準備もできたので、kindを入れていきます
kindのインストール
以下のコマンドですんなり入ります
$ GO111MODULE="on" go get sigs.k8s.io/kind@v0.5.0 $ kind version v0.5.0
これでインストール完了です
クラスタ作成
kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.15.3) 🖼 ✓ Preparing nodes 📦 ✓ Creating kubeadm config 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Cluster creation complete. You can now use the cluster with: export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" kubectl cluster-info $ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" $ kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready master 96s v1.15.3
作成後、言われた通りのコマンド実行するとkubectl
が通るようになります
見てみると、シングルノードで動いてるのがわかります
というわけでテストはおk 作ったクラスタを削除しましょう
$ kind delete cluster
次は実際にマスター1、ワーカー2のクラスタ作ってみましょう
まずはYAMLでクラスタの設定ファイル作ります
kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 nodes: - role: control-plane - role: worker - role: worker extraPortMappings: - containerPort: 30000 hostPort: 80
これでマスターノード(control-plane)とワーカーノード(worker)2つが作られます
extraPortMappingsを設定しておくと、ホストとノードコンテナのポートフォワーディングが設定できます
動作としてはdocker run
の-p
オプションとおんなじだと思う
コンテナポートが30000なのは後で説明します
$ kind create cluster --config kind-example.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.15.3) 🖼 ✓ Preparing nodes 📦📦📦 ✓ Creating kubeadm config 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 ✓ Joining worker nodes 🚜 Cluster creation complete. You can now use the cluster with: export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" kubectl cluster-info $ kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready master 2m30s v1.15.3 kind-worker Ready <none> 113s v1.15.3 kind-worker2 Ready <none> 113s v1.15.3
お手軽にマルチノードクラスタができましたね、素晴らしきかな
お次はnginxをデプロイしてみましょう
YAMLでマニフェストを作成します
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deplpoy spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-svc spec: type: NodePort ports: - port: 80 nodePort: 30000 #NodePortのため30000~32767 selector: app: nginx
テスト用なので、とりあえずレプリカ数は1で
ServiceはNodePortでやるので、デフォルトだと30000~32767のポートしか使えません
おとなしく30000にしときましょう 先ほどホストとマッピングしたポートに合わせてください
出来上がったらあとはk8sに丸投げして少し待ったらcurlでホストから確認してみましょう
$ kubectl apply -f nginx-conf.yaml deployment.apps/nginx-deplpoy created service/nginx-svc created $ curl localhost <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
nginxうごいてる! すごい! らくちん!
しばらくはこれ使ってk8s勉強したいとおもいます
Kubernetesのおべんきょう
いまさらながらKubernetesのお勉強始めました
手始めに、入門Kubernetesを読んでみたので今のとこの自分の理解を忘備録がてらまとめてみます
まあ細かいこと書くのは後に回してとりあえず概要だけですが
Kubernetes(k8s)とは
Dockerなどのコンテナランタイムを用いて、サービスのデプロイ、スケールや運用などを自動化してくれるやつ
もとはGoogleによって開発されており、現在コンテナオーケストレーションツールはほぼk8s一強状態
すごくつよい
主な機能
ロールアウト
アプリの新バージョンリリース時、一部Podだけアップデートさせることを全体が新バージョンになるまで繰り返す
Podというのはk8sでの最小単位で、1つ以上のコンテナの集合体
これならダウンタイムなしにデプロイ可能 おんなじ感じでロールバックもできるよスケール
アプリを提供しているPodの数を増減することで、水平スケーリングが可能
Podへの割り当てリソースを増減する垂直スケールは未実装いちおう提供されてる? github.com Pod単位でスケーリングを行うため、1サービス1サーバーというような提供の仕方よりも効率的にマシンのリソースを使用可能自動回復
各Podなどのヘルスチェックを行い、正常に動作していないものを再起動させたりすることが可能
ヘルスチェックも様々な方法で行うことができ高い汎用性を持ってる
この機能により高い可用性を誇ってるすごい
アーキテクチャ
まだまだ理解が追い付いてないので細かい部分をはしょった概要図でお茶を濁します
Cluster
k8sの最大単位
複数のnodeからなっていて、これで1つのシステムとして動作するNode
クラスタを構成するマシンで、MasterとWorkerの2種類が存在- Master Node
クラスタを管理するためのNode- etcd
k8sのリソースの情報を保存しておくKey-Value Store
これを使ってリソースの永続化を行っている - apiserver
etcdに対しての読み書き操作を行うAPIを提供
他のコンポーネントは直接ではなく、ここを通してetcdを参照する
また、ユーザーからのコマンドであるkubeletも同様にこれを通しetcdを参照、変更する
このユーザーのコマンドもシステムの動作も同じ方法で行うのはk8sの特徴の1つ
これによって高い拡張性が確保できる - scheduler
各NodeのリソースやPodの情報などを参照し、作成したPodをどのNodeにバインドするかを決定する - controller-manager
そのほかのリソースを管理するためのコントローラのマネージャ
- etcd
- Worker Node
まとめ
とりあえず現状の理解ではこういうものだと思ってます
たぶん間違ってるとことか言葉が足りないとことか多々あるとは思うけどそこは追々
自作PC三世
ブログ飽きてません。論文で忙しかっただけです。
巷で噂の第三世代Ryzen
流石に第四世代i7ではそろそろHaswellおじさん呼ばわりされそうなので買い換えました
実は3か月ほど前からちまちまパーツは買いそろえていたので、ケースとGPU以外は全とっかえ
構成
種類 | 型番 |
---|---|
CPU | AMD Ryzen 3700X |
クーラー | サイズ 風魔 弐 |
M/B | ASRock X470 Master SLI |
メモリ | G.SKILL SNIPER X 3600MHz 8GB×2 |
電源 | Corsair RM850X 2018 |
ストレージ1 | シリコンパワー SSD PCIe3.0×4 512GB |
ストレージ2 | SanDisk SSD SATA 480GB×2 RAID0 |
グラボ | GIGABYTE RTX2070 |
ケース | FractalDesign Define R5 |
汚いけど試運転中
試運転はクーラーはリテール、グラボはGTX970で回してました
CPU
言わずと知れた新生Ryzenのミドルレンジコスパキング 3700X
ロマンで3900Xと迷ったけどお財布が許してくれなかったためこっちに
クーラー
虎徹で有名なサイズのツインタワー型空冷クーラー
NoctuaのNH-D15とか同じくサイズの忍者5とかと迷ったけどやはりお財布と冷却性能で考えた結果これに
M/B
X570にしなかった理由としては値段と機能
PCIe 4.0対応の機器はグラボもSSDも当分買う気はないので安いほうを選択
メモリ
今回唯一失敗したと思ってるのはこれ
F4-3600C19D-16GSXWB DDR4-3600 8GB×2の16GB構成
高騰前に勝ったから1万くらいだったけども、こいつがまあ安定しない
素直に3600Mhzで動くとまでは期待してなかったが、3000Mhzでもブルスクがしょっちゅう起きる
しょうがないので低めに2800で運用中
今度やる気が起きた時に最新BIOSにあげたりプロファイルいじったりで対応予定
だめなら買い替えかなぁ… 3200MHzくらいは何とか出したいところ
電源
以前はクロシコの750W Silverだったけども100W上げてCorsairのGoldへ
特に壊れたりとかはしてなかったけども、買い替えたかったので買い換えた。後悔はしていない。
初めてモジュラー式の電源買ったけど、すっきりしてかなりいい
ストレージ
メインはPCIeのSSD、ゲーム用にSATA SSDのRAID0構成
ほかの大容量ファイルはファイルサーバで管理してるのでストレージはこれだけ
特にこだわりはないので以上
グラボ
RTX2070です
2070 Super? 知らない子ですね…
メインモニタが4Kなせいでゲームしようとすると性能不足で最高画質にならん
次買い換えるならグラボかなぁ
ケース
特に光らせたりはしてないので窓もない地味なやつ
けどフロントパネルの開閉とか高静穏性とかで実は結構気に入ってる
3.5インチシャドウベイも多いので、サーバー機組みなおす時にはこっちに入れようかなと画策中
こないだ誤って梅酒ぶっかけたのですこしべたべたしてるのが難点
ベンチ
とりあえずCPU性能だけ見たいので新しいほうのCinebench
スコアは4717とやっぱりかなり高め
DDR4-2133のときは4611と少し下がる
やっぱり8スレッドのi7-4790と比べると圧倒的に早いし満足
ただ財布はかなり薄くなった。後悔はしていない。
IFTTTimer
Alexaでエアコンタイマーをセットしたいときとかあると思うのですよ。
そういうとき、IFTTTにタイマー機能があればできると思ったのですよ。
でも調べた限りではいまのとこそういうのはないみたいなんですよ。
だから少し作ってみたのですよ。
使い方
- IFTTTの"That"のほうでWebhooksのMake a web requestを選択
- URLはhttps://opqc356q12.execute-api.ap-northeast-1.amazonaws.com/beta/iftttimer
MethodはPOST
Content Typeはapplication/json
Bodyは
{ "url":"https://maker.ifttt.com/trigger/{EventName}/with/key/{Your_Key}", "time":{minutes} }
としとくと、EventNameに入れたAppletをminutes分後に実行してくれる
minutes分後ってすごく頭悪そう
仕組み
POSTをAWS API Gatewayで受け取ってLambdaのIFTTTimer_setにポイ
IFTTTimer_setはそれをDynamoDBにPUT
import json from datetime import datetime import boto3 def lambda_handler(event, context): print(event) body=json.loads(event["body"]) timer_time=int(datetime.now().timestamp())+int(body["time"]) url=body["url"] db = boto3.resource('dynamodb') table = db.Table('IFTTTimer_TimeTable') response = table.put_item( Item={ "time":timer_time, "resource":event['resource'], "url":url } )
1分ごとに定期実行されてるIFTTTimer_mainが毎度DBをチェック
時間を過ぎてるものがあったら指定リンクにPOSTしてDBからレコード削除
import boto3 import requests import json from datetime import datetime from boto3.dynamodb.conditions import Key, Attr def lambda_handler(event, context): db=boto3.resource('dynamodb') table=db.Table("IFTTTimer_TimeTable") response = table.query(KeyConditionExpression=Key("resource").eq("/iftttimer") & Key("time").lte(int(datetime.now().timestamp()))) print(response["Items"]) for x in response["Items"]: requests.post(x['url'], data = json.dumps({ "value1":"test" })) table.delete_item(Key={ 'time': int(x['time']), 'resource': x['resource'] })
詰まったとこ・現状気に入らないとこ
table.queryはパーティションキーとはeq()でしか比較できないらしい
かといってソートキーのみで検索もできない
データベース全然知らないからわからんけども、これが普通なのでしょうか
解決としてパーティションキーを適当な一意のものにしてソートキーに時刻を入れてlte()使ってます
でも力業過ぎてきたない&気持ち悪いから要改善
そもそもなぜIFTTTにはタイマー処理がないんだ