EKSのPod起動数の制限
最近、いくつかEKSの環境構築を進めているなかで、EKSのPodの起動数の制限がネットワークとインスタンスタイプに依存することが分かりました。すでに知っている人も多いと思いますが、備忘録として残しておきたいと思います。
Podの起動数
通常、Podの起動を考える時に一番気にするポイントとしてはノードのリソース(CPU、メモリ)ではないでしょうか?
当然ですが、ノードインスタンスのCPUやメモリに余裕がないと新たにPodを起動できませんし高負荷時にスケールアウトもできなくなります。
Manifestにもrequestやlimitなどの設定項目がありますしリソースの確保については気を使われているのではないでしょうか?
EKSでは、ノードのリソース状況に加えてネットワークについても意識する必要があります。
Podのネットワーク制限
通常のKubernetesのPod用のネットワークはノードインスタンスとは別の内部ネットワークとなりあまり気にする必要はありません。
しかし、EKSではAmazon VPC CNI plugin for KubernetesというネイティブなVPCを利用可能とするプラグインを用いることで、VPCネットワーク上と同じIPをPodに割り当てることなります。つまり、PodはVPC上のIPを利用して通信するため、Podの起動数がVPCで利用可能なIP数に依存するということになります。
EKSのネットワークの詳細については以下を参照してください。
docs.aws.amazon.com
一般的にAWSのサブネットについては、24ビット以上で切られることも多いと思います。仮に24ビットでサブネットを作成した場合、AWS予約分を除くと利用可能なIP数は251となります。
AWSで指定可能な最も小さな28ビットで作成した場合は、11IPしか利用することができません。
ノードインスタンスをいくつ起動するかにもよりますが、インスタンス自身にもIPが必要となります。
また、RDSやElasticacheなどその他のリソース用のIPも確保しておく必要があるため、実際にPodで利用可能なIPはもっと少なくなります。
さらに、ノードインスタンスのスケールやPodのスケーリングがどれくらいになるかも想定しておく必要があります。
これらを踏まえてネットワーク設計にあたってはIPに余裕をもったCIDRを検討してください。
インスタンスタイプごとの制限
IP数に余裕があって、インスタンスのリソースにも余裕があった場合、1インスタンスに際限なくPodを起動できるのでしょうか?
AWSでは、インスタンスタイプごとに割り当て可能なIP数が制限されているため、こちらも意識しておく必要があります。具体化にはインスタンスタイプごとにアタッチ可能なネットワークインターフェイス(ENI)の数に制限があり、1つのネットワークインターフェイスあたりの利用可能なIP数に制限があります。
実際にc5.large(or m5.large)で見てみると、制限は以下の通りとなります。
ENIの最大数 | 3 |
---|---|
ENIあたりの IPv4 アドレス | 10 |
この場合、利用可能IPは30となります。
特にt系のmicro、smallなどは場合は、利用可能なIP数も少ないため注意する必要があります。
各インスタンスタイプごとの制限は以下のドキュメントを参照してください。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-eni.htmldocs.aws.amazon.com
スケールした場合のIPの消費
例として以下のVPCのケースで考えてみます。
VPC CIDR | 192.168.0.0/16 |
---|
Publicサブネットを2つPrivateサブネットを2つ作成したとして、EKSのノードグループをPrivateサブネットに配置するものとします。
Subnet CIDR | 利用可能IP数 | |
Private A | 192.168.10.0/24 | 251 |
Private C | 192.168.11.0/24 | 251 |
構成
インスタンスタイプ | c5.large |
---|---|
通常時起動台数 | 10 |
ピーク時起動台数 | 20 |
上記のケースの場合、ノードグループは2つのPrivateサブネットに配置されているため502のIPが利用可能です。
c5.largeは最大で30ip利用可能ですが、1台あたり20ip程度利用していたと想定します。
通常時では200ip程度消費することとなり300程度の余裕があります。
しかし、ピーク時では倍の400ipとなり残りが100しかなくなります。
そうすると、さらに負荷が増大した場合にスケールアウト可能なインスタンスが5台までとなり、あまり余裕がありません。
さらにローリングアップデートやBlue・Greenデプロイメントを行うのであれば、余分にipが必要となるためそれらの考慮も必要となってきます。
通常のEC2であれば、24ビットのCIDRで十分なことも多かったかと思いますが、EKSではかなりのipが消費されることになるため注意が必要です。
EKSにおけるSecurity Group変更の注意点
長くなるので結論を先に書きます。
EKSのノードインスタンスにおいてセキュリティグループを変更(アタッチ or デタッチ)する際は、ノードグループを更新しましょう。要するに手動での変更は止めましょうということです。ちなみにアタッチ済のセキュリティグループのルール変更は問題ありません。
手動で変更したために、疎通に関して問題が起こったので経緯と原因について書きたいと思います。
やりたかったこと
以下のような構成でPodからRDSへ疎通させたかった。
構成
対応内容
上記のような構成でノードのセキュリティグループを手動で変更しました。
具体的な設定内容としては、自己参照設定されているセキュリティグループををノードインスタンスとRDSにアタッチしました。
そもそもノードグループはオートスケーリンググループのため、手動変更がよくないことは認識していましたが、開発環境ということや諸々の状況から、一時的な対応として手動で新しいセキュリティグループをアタッチしました。(要するに手抜きでやってしまいました。)
発生事象
上記の対応後に、PodからRDSに接続できたりできなかったりする事象が発生。
原因調査
この時点で以下の確認を行いました。
1.セキュリティグループ
ソース、ポートに設定誤りがないことを確認
全てのインスタンスにセキュリティグループが付与されていることを確認
2.ネットワークACL
何も設定されていないことを確認
3.テスト用Podから接続確認
PodをCreateした直後は高確率でRDSに対して接続可能となるが、Podをkillしてリスタートすると接続できたりできなかったりというかなり微妙な結果となりました。特定のノードで接続できないとか特定のPodに問題があるのかなど確認しましたが特にそういった問題は見受けられませんでした。
4.セキュリティグループの設定変更
RDSのSGにおいて、自己参照ではなくインバウンドのソースにSharedNodeSecurityGroup(eksctlでデフォルトで作成されるノードのSG)を指定するように変更したところ、RDSへ接続が可能となることが判明。
この時点では、明確な原因が分かるところまでは調査できませんでした。
他の環境において全く同様の設定でDB接続に問題は発生していなかったため、自己参照のSGに問題があるとは考えづらかったのですが、状況から見ると何か問題がありそうということで、一旦自己参照における設定はやめることとしました。(結果的に自己参照が問題ではありませんでした。)
余談ですが、この調査の際に以下の方法を利用してホスト側に接続しtcpdumpなどで調査を進めました。
SSH接続しなくてよいのでかなり便利です。
dev.classmethod.jp
EKSのネットワークについて調査
原因がはっきりとしなかったため、後日改めてEKSのネットワークについて調べてみました。
EKSのネットワークについては、Amazon VPC CNI plugin for Kubernetesというプラグインを使用することで、PodについてもVPCネットワーク上のアドレスと同じIPを利用することが可能となっています。
このため、ノードインスタンスではPod数分のIPを確保する必要があるため、ネットワークインターフェースを1ノードに複数アタッチする構成となっています。以下AWSの資料です。
問題が起こった環境では、インスタンスタイプはt3.mediumを利用していました。このタイプのネットワーク制限は以下となり、最大18個のIPが利用可能となります。
■t3.medium
アタッチ可能なENI数 | 3 |
---|---|
ENIあたりのIPv4数 | 6 |
ここでENIが複数アタッチされていることに改めて着目しました。
そもそもセキュリティグループはインスタンスに対して割り当てられるものではなく、ENIにセットされるものとなります。通常、あまり複数ENIを利用することも少ないこともあり、EC2のコンソールからはインスタンスにセットされているように見えますが実際にはENIにセットされていることになります。
この時、EC2のコンソールからセキュリティグループを追加した際に全てのENIに反映されていないのではないかと思い至り、検証してみました。
もし上記の通りの動作だとすると、セキュリティグループがアタッチされたENIからのみRDSへ疎通が可能となり、それ以外のENIからは通信できないという動きになると考えられます。
ENIに対しセキュリティグループがどのようにアタッチされるか?
以下のようにセキュリティグループが割り当てられているインスタンスを用意しました。
3つのセキュリティグループが割り当てられています。
このインスタンスには以下のように3つのENIがアタッチされており、セキュリティグループについても全て同様となっています。
このインスタンスに対してEC2コンソールからadd_sgというセキュリティグループをアタッチしました。
アタッチ完了後にENIを確認したところプライマリENIのみadd_sgが付与されていることが分かりました。
■プライマリENI
■セカンダリENI
検証結果から上記の想定通りの動作ということが確認できました。
接続不具合となった原因
改めて状態を図にしてみると以下のようなイメージになります。
EC2コンソールから手動でセキュリティグループを変更するとENI_1にのみadd_sgがアタッチされることとなります。
そのため、ENI_1に紐付くIPが割り当てられたPodからのみRDSへの疎通が可能となり、そうでないENIに紐付くIPが割り当てられたPodからは疎通ができません。ということで、今回発生した事象について原因が判明しました。
ちなみに何度か検証した際に気付きましたが、EC2コンソールからのセキュリティグループ変更時には、以下のようにインターフェースIDが表示されていました・・・コンソールはしっかり確認する必要がありますね。
全てのENIに手動でセキュリティグループをアタッチすれば問題ないか?
完全に一時しのぎとしてなのですが、仮に全てのENIのセキュリティグループを手動で変更すれば問題ないかという観点でもう少し考えてみました。オートスケーリンググループのテンプレートの問題は一旦おいておきます。
ノードインスタンスは、起動直後にPodの数が少ない場合、ENIは1つしかアタッチされていません。
そこからPodの数が増えて1つのENIで割り当て可能なIP数を超えた場合は、新たにENIがアタッチされることになります。その際、増えたENIはどのようなセキュリティグループがセットされているかを確認してみました。
以下のようなENIが一つのみのインスタンスを用意し、add_sgというセキュリティグループをセットします。
インスタンにENIが一つアタッチされている状態です。
この状態でPodの数を増やし、各ENIの状態を確認しました。
プライマリENIにはadd_sgが付与されていることが分かります。
追加された2つのENIを確認すると、add_sgは付与されていませんでした。
おそらくテンプレートから引っ張ってきてると思うので当然の動作かもしれません。
ということから、仮に全てのENIのセキュリティグループを手動で変更したとしてもENIが勝手に増える可能性があるため、突然、通信できなくなる問題が起こりそうですね。
Amazon CloudWatch SyntheticsでURL監視を設定
Amazon CloudWatch Syntheticsがプレビューされていましたので早速試してみました。
CloudWatch Syntheticsとは、サービスのエンドポイントやAPIのエンドポイントなどをモニタリングするサービスになります。DataDogでも同様のサービスがありますね。
現時点では、US East (N. Virginia), US East (Ohio), and EU (Ireland)のいずれかのリージョンでし利用できませんので、今回は、バージニア北部を利用しました。
Canaryを作成
以下の画面からCanaryを作成を選択します。
デフォルトで以下のように選択されていますのでそのまま次進みます。
Canary名と監視対象のURLを入力します。
スクリプトエディタの内容は自動で生成されるます。
以降はデフォルトのままとし、Canaryを作成します。
モニタリングの実行
作成直後はステータスがグレイアウトされていますが、しばらくすると以下のように実行結果が表示されます。
詳細を確認するとスクリーンショットやHARファイルなどを見ることができます。
実際の画面を取得してくれるのはすごくいいですね。
ログを確認してみると、Lambdaのログに似ていたので、Lambdaのコンソールを確認したところsynthetics用の関数が自動で作成されていました。消したりしないように注意が必要ですね。
アラート通知
また、Threshholdsを有効にし、Canaryでしきい値を有効にすると、アラームが作成されますので、これをもとにchatbotやLambdaと連携することでアラート通知が可能になります。
カスタマイズ
スクリプトがカスタマイズできるので、Basic認証やログインしての監視なども可能ですね。
ちなみにログイン情報などの秘匿情報が必要な場合はSecrets Managerの利用が推奨されています。
料金
1canaryあたり月額$0.0012となっています。
まとめ
特にドキュメント読むことなく簡単に設定することができました。また、スクリプトをカスタマイズできるなど拡張性も高いと思いますので、ぜひ使っていきたいですね。
CloudFormationでDataDogのIntegration設定
CloudFormationで以下のアップデートがありサードパーティ製品についてもCoudFormationで設定できるようになりました。
DataDogのIntegration設定などをCloudFormationで設定できるようなので、早速試してみました。
aws.amazon.com
CloudFormationへDatadogリソースの登録
aws cliで以下のコマンドを実行しリソースの登録を行います。
実行前にawscliのアップデートが必要になります。(pip install -U awscli)
aws cloudformation register-type \ --region ap-northeast-1 \ --type RESOURCE \ --type-name "Datadog::Integrations::AWS" \ --schema-handler-package s3://datadog-cloudformation-resources/datadog-integrations-aws/datadog-integrations-aws-1.0.0.zip
※type-name、schema-handler-packageの設定値についてはこちらのgithubを参照してください。
以下の通りRegistrationTokenが表示されれば登録は完了です。
{ "RegistrationToken": "122c2c39-12c8-4e93-a711-f6eb1234eae0" }
登録されているかは以下で確認できます。
$aws cloudformation list-types { "TypeSummaries": [ { "Description": "Datadog AWS Integrations", "LastUpdated": "2019-11-20T03:13:25.218Z", "TypeName": "Datadog::Integrations::AWS", "TypeArn": "arn:aws:cloudformation:ap-northeast-1:123412341234:type/resource/Datadog-Integrations-AWS", "DefaultVersionId": "00000001", "Type": "RESOURCE" } ] }
リージョンごとにリソース登録が必要となるようですが、このあたりの設定は今後もっと簡略化されていくと嬉しいですね。
Integration設定
事前に設定するDataDogのAPIキーとApplicationキーを取得しておいてください。
以下のyamlをファイルに保存しCloudFormationから実行し、パラメータにて上記のキーを設定して実行してください。
--- AWSTemplateFormatVersion: '2010-09-09' Description: 'IAM Role and IAM Policy for Datadog AWS Integration' Parameters: DatadogAPIKey: Description: "Datadog's API Key" Type: String DatadogAPPKey: Description: "Datadog's APP Key" Type: String Resources: DatadogAWSIntegrationResource: Type: 'Datadog::Integrations::AWS' Properties: AccountID: !Ref AWS::AccountId RoleName: DatadogAWSIntegrationRoleTest HostTags: ["env:staging"] AccountSpecificNamespaceRules: {"ec2": true, "api_gateway": false} DatadogCredentials: ApiKey: !Ref DatadogAPIKey ApplicationKey: !Ref DatadogAPPKey
DataDogの設定画面を確認すると以下の通り登録されていれば完了です。
AWS側のRoleも合わせて作成してくれないか期待しましたが、残念ながらそこまではしてくれないようでした。
また、ExternalIDの取得方法が現状ではなさそうなので今後に期待したいと思いますが、機能が拡充されていくと思いますので、まとめてCloudFormationで管理することが可能になりますね。
Kubernetes Cluster on Raspberry Pi zero using Cluster Hat
Once I've read this blog(3日間クッキング【Kubernetes のラズペリーパイ包み “サイバーエージェント風”】), I wanted to deploy k8s cluster on Raspberry Pi.
However, It's too expensive for me in this blog's way. Need three Raspberry Pi 3 Model B and switching hub ... etc.
So I've created k8s cluster Raspberry Pi Zero, but It was very hard for me than I thought.
I'll introduce the creation of k8s cluster on raspberry pi zero in this time.
And this is totally useless because of using the older version, and not for production. In addition unstable behavior and very slowly.
I made only for my pleasure. If you're interested in, make it.
Component List
Prepare the following components.
We need at least one Raspberry Pi Zero
- Cluster Hat v2.3 x 1 (23.33£)
- Raspberry Pi B+ x 1 ($35)
- Raspberry Pi Zero x 1-4 (3.88£/per)
- Micro SD Card x 2-5
- Power Cable x 1
- Lan Cable or USB Wifi Adaptor
※You can buy Cluster Hat and Raspberry Pi in Pimoroni(https://shop.pimoroni.com/)
Prerequisite
- Assembled with ClusterHat(https://clusterhat.com/setup-assembly)
- Install Raspberry Pi OS(latest) to Controller Node(Raspberry Pi B)
Component Role
Hardware | Role of ClusterHat | Role of K8S |
---|---|---|
Raspberry Pi B+ | Controller | Control-Plane |
Raspberry Pi Zero x (1 - 4) | Node | Node |
ClusterHat | - | - |
※ Raspberry Pi Zero( or Zero W)
This is my environment(actually, I used 3 raspberry pi zero)
1. Create Raspberry Pi Zero Image for ClusterHat and k8s
1-1. Create old raspbian image
Create Raspberry Pi Zero image from 2017-11-29-raspbian-stretch-lite because latest raspbian image can unavailable cpuset.
execute the following command in Controller Node of ClusterHat
# wget http://ftp.jaist.ac.jp/pub/raspberrypi/raspbian_lite/images/raspbian_lite-2017-12-01/2017-11-29-raspbian-stretch-lite.zip # unzip 2017-11-29-raspbian-stretch-lite.zip # apt install kpartx # git clone https://github.com/burtyb/clusterhat-image # cd clusterhat-image/build # mkdir {img,mnt,dest,mnt2} # mv 2017-11-29-raspbian-stretch-lite.img img/ # create.sh 2017-11-29
if it is well, the Raspbian image should be in dest directory.
ClusterCTRL-2017-11-29-lite-1-CNAT.img ClusterCTRL-2017-11-29-lite-1-p1.img ClusterCTRL-2017-11-29-lite-1-p2.img ClusterCTRL-2017-11-29-lite-1-p3.img ClusterCTRL-2017-11-29-lite-1-p4.img
1-2. Write the image to micro sd
Download this Raspbian image to local.
Write CNAT.img to Controller Node(Raspberry Pi B+), others to the node(Raspberry Pi Zero).
Create ssh file at boot directory for connecting ssh each node.
If you're on a Mac, execute the following command after writing.
# touch /Volumes/boot/ssh
1-3. Start Controller Node
Confirm connecting to Controller Node
From local machine.
# ssh pi@[controller ip_addr]
From Controller Node
# ssh pi@[172.19.181.1-4]
Password is raspberry
2. Setting Node of ClusterHat
2-1. Install library to Controller Node
Install python-smbus because lacked library for clusterhat command. Execute the following command.
# apt-get install python-smbus
After install, execute clusterhat command.
# clusterhat status
2-2. Setting Nat
For Node connect to the internet, Setting Nat.
Execute the following command
# apt-get -y install iptables-persistent # iptables -t nat -A POSTROUTING -s 172.19.181.0/24 ! -o brint -j MASQUERADE # iptables -A FORWARD -i brint ! -o brint -j ACCEPT # iptables -A FORWARD -o brint -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # sh -c "iptables-save > /etc/iptables/rules.v4"
3. Setting All Node of ClusterHat
3-1. Hold kernel version
# apt-mark hold raspberrypi-kernel
3-2. upgrade
# apt-get upgrade -s
Check it has not changed kernel version
# uname -a Linux p1 4.9.59+ #1047 Sun Oct 29 11:47:10 GMT 2017 armv6l GNU/Linux
3-3. Install Docker
Install with fixed version(18.06.1~ce~3-0~raspbian)
# curl -sSL https://get.docker.com | sh # usermod -aG docker pi # apt-get install docker-ce=18.06.1~ce~3-0~raspbian
If docker command not well, reinstall docker.
# apt-get remove docker-ce # apt-get install docker-ce=18.06.1~ce~3-0~raspbian
3-4. Hold Docker version
# apt-mark hold docker-ce
3-5. Install Kubernetes
Install kubernetes with below version.
# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - # echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list # apt-get update # apt-cache madison kubeadm # apt-get install kubelet=1.5.6-00 kubeadm=1.5.6-00 kubectl=1.5.6-00 kubernetes-cni=0.5.1-00
4. Setting the Control-Plane Node(Controller Node of ClusterHat)
4-1. Initialize a Kubernetes Control-Plane node
Execute the following command on control-plane
# kubeadm init --pod-network-cidr 10.244.0.0/16
Probably it should stop at the following log (in 5-10minitus)
<master/apiclient> created API client, waiting for the control plane to become ready
Check the logs,you should see the following logs
# journalctl -eu kubelet "error: failed to run Kubelet: unable to load client CA file /etc/kubernetes/pki/ca.crt: open /etc/kubernetes/pki/ca.crt"
※because creating ca(ca.pem) file name and refference ca(ca.crt) file name are different
Solution of this trouble
Change file name as follows
cd /etc/kubernetes/pki
cp ca.pem ca.crt
After change file name, waiting for a while.
if it well, memo the following such a output.
# kubeadm join --token=18b644.0438585f9e20e864 192.168.0.109
4-2. Install flannel
Create file as follows
kube-flannel.yml
--- apiVersion: v1 kind: ServiceAccount metadata: name: flannel namespace: kube-system --- kind: ConfigMap apiVersion: v1 metadata: name: kube-flannel-cfg namespace: kube-system labels: tier: node app: flannel data: cni-conf.json: | { "name": "cbr0", "type": "flannel", "delegate": { "isDefaultGateway": true } } net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan" } } --- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: kube-flannel-ds namespace: kube-system labels: tier: node app: flannel spec: template: metadata: labels: tier: node app: flannel spec: hostNetwork: true nodeSelector: beta.kubernetes.io/arch: arm tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule serviceAccountName: flannel containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.7.1-arm command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ] securityContext: privileged: true env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run - name: flannel-cfg mountPath: /etc/kube-flannel/ - name: install-cni image: quay.io/coreos/flannel:v0.7.1-arm command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ] volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg
Execute the following command.
#kubectl create --validate=false -f kube-flannel.yml
※ it takes a long time(about 30〜40minutes)
5. Setting the Kubernetes Node(Node of ClusterHat)
5-1. Copy ca.crt from Control-Plane
Download ca.crt from Control-Plane, and Upload the file to node.
scp pi@[control-plane ip_addr]:/etc/kubernetes/pki/ca.crt . scp pi@[node ip_addr]:ca.crt /etc/kubernetes/pki/ca.crt
because when executed join command, not created ca.crt(probably bug)
if you execute join command without this 5-1, you should face the following trouble.
after executing join command, the following output.
Node joins complete:
but node not joined.
Check the log.
# journalctl -eu kubelet "error: failed to run Kubelet: unable to load client CA file /etc/kubernetes/pki/ca.crt"
5-2. Join to Control-Plane
Execute the following command(at 4-1's command) each node.
# kubeadm join --token=18b644.0438585f9e20e864 192.168.0.109
6. Check if the installation was successful
If you can see the following output, you got success.
# kubectl get nodes
NAME STATUS AGE
cnat Ready,master 2h
p1 Ready 1m
p2 Ready 9m
p3 Ready 19m
and look this command output
# kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE kube-system dummy-2501624643-8jrkj 1/1 Running 0 2h 192.168.0.109 cnat kube-system etcd-cnat 1/1 Running 0 2h 192.168.0.109 cnat kube-system kube-apiserver-cnat 1/1 Running 0 2h 192.168.0.109 cnat kube-system kube-controller-manager-cnat 1/1 Running 0 2h 192.168.0.109 cnat kube-system kube-discovery-2202902116-pwwaf 1/1 Running 0 2h 192.168.0.109 cnat kube-system kube-dns-2334855451-aq9pj 3/3 Running 1 2h 10.244.0.2 cnat kube-system kube-flannel-ds-5tz4s 2/2 Running 2 22m 172.19.181.3 p3 kube-system kube-flannel-ds-k5vsc 2/2 Running 0 3m 172.19.181.1 p1 kube-system kube-flannel-ds-kasde 2/2 Running 0 1h 192.168.0.109 cnat kube-system kube-flannel-ds-uumv1 2/2 Running 2 11m 172.19.181.2 p2 kube-system kube-proxy-2ku07 1/1 Running 0 2h 192.168.0.109 cnat kube-system kube-proxy-d9xxo 1/1 Running 0 22m 172.19.181.3 p3 kube-system kube-proxy-dz146 1/1 Running 0 11m 172.19.181.2 p2 kube-system kube-proxy-gvcs1 1/1 Running 0 3m 172.19.181.1 p1 kube-system kube-scheduler-cnat 1/1 Running 0 2h 192.168.0.109 cnat
What kind of trouble occurred
1. Unavailable cpuset on latest raspbian image (1-1)
created old raspbian image with being able to use cpuset.
2. Can't initialize kubernetes on kubeadm(4-1)
kubeadm created ca(ca.pem) file, also kubeadm reference ca(ca.crt) file, but both file name is not same.
So Renamed the ca.pem file to the ca.crt.
3. Can't deploy the latest flannel(4-2)
The latest flannel can unavailable in this version of kubeadm.
I used an old flannel version.
4. Node can't join to Control-Plane(5-1)
When execute join command, node can't join to Control-Node.
The cause of this problem is the node doesn't have ca.crt. Probably should be created automatically when joining.
This can be solved by copying the ca.crt from Control-Plane to Node
AWS Client VPN設定のハマりどころ
AWS Client VPNが東京リージョンでも使えるようになりましたね。
これでVPNサーバを立てる手間が減らせるので、費用面は考慮しつつうまく活用していきたいですね。
今回はClient VPNを検証した際に設定でわかりにくかった箇所があったので備忘録として残しておきたいと思います。
Client VPN設定方法
設定方法については、AWSのドキュメントやブログで色々出ていますのでそれを参照して設定してください。
クライアント VPN の使用開始 - AWS Client VPN
[AWS]踏み台をワンチャンなくせる!?VPC接続にClient VPNを使ってみよう | DevelopersIO
構成
以下の構成で検証しました
ハマったポイント
設定後にClient端末からSSHが通らない
VPNは正常に接続できるものの、SSH接続ができない状態となりました。
ルーティングについてはドキュメント通りに設定しており、NACLについては特に設定していませんでした。
セキュリティグループについては、VPCE、EC2のSGそれぞれで以下のとおりClientのCIDR範囲を許可していました。
■SG_VPCE
Port | Source |
---|---|
22 | 192.168.0.0/16 |
■SG_EC2
Port | Source |
---|---|
22 | 192.168.0.0/16 |
設定に問題がないか再確認しつつ、切り分けのためにいったんそれぞれのSGを0.0.0.0でフル解放してSSH接続してみました。
結果は接続が成功し、secureログで接続元のIPを確認すると、172.31.16.xxxとなっておりClientVPNのVPCEndpointのIPであることが分かりました。
Client VPNでの接続については、Client端末の接続がVPCEndpointのIPでNatされるようなので、Client端末のIPをSGで許可しても意味がありませんでした。
上記を踏まえて、以下のようにSGを設定しました。
■SG_VPCE
Port | Source |
---|---|
22 | 192.168.0.0/16 |
■SG_EC2
Port | Source |
---|---|
22 | SG_VPCE |
まとめ
AWS ClientVPNはサーバをきどうする必要もないため手軽に起動できますが、承認設定のような概念が分かりづらい設定もあり少し引っかかりました。トータルではサーバの面倒を見る必要がないという大きなアドバンテージがあるので積極的に活用していきたいと思います。
CO2濃度計をRaspberry Pi Zeroで作ってみた
車の運転中にCO2濃度が高くなると集中力が落ち眠くなりやすくなるそうです。
そこで、車でCO2濃度が測定できるようにRaspberry Piを使って車載用にCO2濃度計を作ることにしました。
まだやりたいことが全部できたわけではないですが、個人的に使うには十分なところまでできましたので作成方法について紹介したいと思います。
機能
最終的に実装した機能は以下になります。
構成
必要部品
部品 | 数量 | 用途 | 金額(目安) |
---|---|---|---|
Raspberry Pi Zero W | 1 | 1300円 | |
MH-Z19 | 1 | CO2センサー | 3800円 |
PiOled | 1 | 表示用小型液晶 | 3800円 |
圧電スピーカ | 1 | アラーム用 | 100円 |
各部品の接続
接続図
上記接続の通り各部品を接続していきます。
CO2センサー(mh-z19)接続
CO2センサーを接続し動作確認を行います。
モジュールのインストール
pip install getrpimodel
接続確認
以下のプログラムを適当な名前で保存し実行します。
import sys import serial import time import subprocess import getrpimodel import datetime from time import sleep import RPi.GPIO as GPIO if getrpimodel.model() == "3 Model B": serial_dev = '/dev/ttyS0' stop_getty = 'sudo systemctl stop serial-getty@ttyS0.service' start_getty = 'sudo systemctl start serial-getty@ttyS0.service' else: serial_dev = '/dev/ttyAMA0' stop_getty = 'sudo systemctl stop serial-getty@ttyAMA0.service' start_getty = 'sudo systemctl start serial-getty@ttyAMA0.service' def mh_z19(): ser = serial.Serial(serial_dev, baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1.0) while 1: result=ser.write("\xff\x01\x86\x00\x00\x00\x00\x00\x79") s=ser.read(9) if len(s)!=0 and s[0] == "\xff" and s[1] == "\x86": return {'co2': ord(s[2])*256 + ord(s[3])} break def main(): subprocess.call(stop_getty, stdout=subprocess.PIPE, shell=True) now = datetime.datetime.now() now_ymdhms = "{0:%Y/%m/%d %H:%M:%S}".format(now) # Get Data value = mh_z19() co2 = value["co2"] print('CO2:' + co2) subprocess.call(start_getty, stdout=subprocess.PIPE, shell=True) if __name__ == '__main__': main()
CO2の値が取得されていれば正しく接続できています。
室内だと通常400〜1000ppm程度かと思いますが、異常な値が取得されていれば以下のサイトを参考に補正してみてください。
qiita.com
液晶(PiOled)接続
PiOledを接続後、以下の設定を行います。
手順はRaspberry Pi 3 Model Bに Adafruit PiOLED を接続 - Qiitaを参考にしました。
必要ライブラリのインストール
sudo apt-get install python-dev sudo apt-get install python-imaging sudo apt-get install libffi-dev sudo pip install smbus-cffi sudo pip install RPi.GPIO
カーネルモジュールのロード
sudo modprobe i2c-dev
$ dmesg | grep i2c
ロードされていることの確認
cat /proc/devices | grep i2c
89 i2c
/dev/i2c-1 の作成
sudo mknod /dev/i2c-1 c 89 1 $ ls /dev/i2c-1 /dev/i2c-1
PiOledの接続確認
以下のように出力されていれば正しく接続されています。
sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
サンプルの実行確認
git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
cd Adafruit_Python_SSD1306
sudo python ./setup.py build
sudo python ./setup.py install
python ./examples/stats.py &
液晶にIPアドレス、CPU、メモリ、ディスクが表示されていればOKです。
圧電スピーカ接続
接続後に以下のプログラムを実行してスピーカーから音が鳴ればOKです。
import RPi.GPIO as GPIO import time SOUNDER = 21 GPIO.setmode(GPIO.BCM) GPIO.setup(SOUNDER, GPIO.OUT, initial = GPIO.LOW) p = GPIO.PWM(SOUNDER, 6500) p.start(50) time.sleep(0.5) p.stop() time.sleep(0.5) p.stop GPIO.cleanup()
プログラムの配置
CO2濃度取得プログラムの配置
以下の通りプログラムを取得し適当なところに配置します。(ここでは/home/pi/program/配下に配置しました。)
git clone https://github.com/cloudfish7/co2_sensor.git
cronの設定
1分ごとと1時間ごとにデータを取得するように設定します。
*/1 * * * * sudo python /home/pi/program/co2_sensor/mh-z19.py minutes 0 * * * * sudo python /home/pi/program/co2_sensor/mh-z19.py hour
表示用Webプログラムの配置
以下の通りプログラムを取得し適当なところに配置します。(ここでは/home/pi/program/配下に配置しました。)
グラフ表示についてはOpen Source Image Charts Replacement | QuickChartを利用しているためraspberry piについてはインターネット接続されている必要があります。
git clone https://github.com/cloudfish7/co2_web.git
以下コマンドで実行します。(自動起動設定してないです)
python app_co2.py
ブラウザで以下アドレスにアクセスするとweb画面が表示されます。
IPアドレスは液晶にも表示されるようになっています。
http:ip_address:8080
車載にあたっての問題
車に積んだ際に電源が問題になります。
シガーソケットからUSBで電源を取得していた場合、エンジンを切ると電源供給が止まるためRaspberry Piが突然落ちることになります。そうするとSDカードへの書き込み途中の場合、ファイルが壊れてしまい最悪OSが起動しなくなる可能性があります。
組み込み系のデバイスの場合はROM化して書き込みを無くすようなので、Raspberry PiでもROM化できないか調べてみたところoverlayfsという仕組みを使うことでSDカードへの書き込みを制御できることが分かりました。
以下の記事に設定方法が詳しく書かれていましたが、設定後システムが不安定になる場合もあるようなので今回は諦めることにしました。
qiita.com
代わりにモバイルバッテリーを電源に使うことにしました。
少し面倒ですがとりあえずは当面これで使ってみたいと思います。OSのシャットダウンについてはWebの画面にシャットダウンボタンを付けて代用しようかと考えています。
まとめ
CO2濃度計を作ってから自宅、車と両方で使っています。
大気中のCO2濃度は約400ppmくらいなので、換気が行き届いている場合は同様の数値となりますが、窓を締めるとすぐに数値が上がっていきました。
実際に車で4人が乗り、内気循環にしていると30分程度で2000ppmを超える濃度となりました。2500ppmを超えるとパフォーマンスが落ち眠気を誘うとの研究もありますので、運転中は適度に換気したほうがよさそうですね。