etcdに格納されたデータを見る
kubernetesがリソースを作成するにあたり、大きく二つのステップがある。
https://kubernetes.io/ja/docs/concepts/overview/components/
リソースを変更・追加するときはetcdにデータが格納される。 コアコンセプトであるReconciliation Loopでetcdと実際のリソースとの差分を検知しリソースを更新することで実現できる。
この記事では前半のkubectlを実行し、etcdにデータが格納されるまでを確認することにした。 Reconciliation Loopの挙動は別途確認することにする。
今回は以下を確認する。
- etcdにデータが格納されるまで
- etcdのデータの中身
前提
- kubernetes v1.22.3
- minikube v1.24.0
- etcdctl 3.3.12
etcdにデータが格納されるまで
kubectl applyで実行する。
まずはマニフェストを準備する。 最近検証で使ってたので、dnsutilsを使っているが特に意味はない。
apiVersion: v1 kind: Pod metadata: name: dnsutils spec: containers: - name: dnsutils image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3 command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
podを立ち上げる。
# k apply -f dnsutils.yaml # k get po NAME READY STATUS RESTARTS AGE dnsutils 1/1 Running 0 8m32s
余談
kubectlは最終的にapiを実行するので、 クラスタに接続できるようにして、直接apiを実行すると同じことが実現できる。
kubectl proxy Starting to serve on 127.0.0.1:8001 curl -X GET http://127.0.0.1:8001/api/v1/namespaces/default/pods # イメージだけ伝わればいいので、所々省略してる { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion": "13267" }, "items": [ { "metadata": { "name": "dnsutils", "namespace": "default", "uid": "c6c5612f-adbe-43b6-8ff6-9dc63381d6b0", "resourceVersion": "12346", "creationTimestamp": "2022-02-23T05:56:42Z", # 省略 }, "spec": { # 省略 }, "status": { # 省略 } } ] }%
こんな感じで、RESTでkubectlに備わってる機能を実行できる。 無数に存在するapiの中で開発に必要な機能を抜粋し、kubectlで叩けるようになっていることがわかる。
api-serverの挙動も今回はソースコード追ったりはしない。 が、etcdにデータが格納される流れとしては、以下のようになる。
etcdのデータを見てみる
今回はetcdctlを用いて検証したが、curlなどのhttpリクエストでも同じことが実現できる。
その前にetcdについて
- etcdとは、キーバリューストアのデータベースである。
- kubernetesのデータ(状態や構成、メタデータ)を保持している。
- Raftという合意アルゴリズムを用いて、可用性と一貫性を担保している。
Raftの合意アルゴリズムの特徴としては、可用性を高めるために必ずリーダが選出され、フォロワにログをレプリケートする。 例えばリーダが正常に稼働しなくなった場合は次のリーダが選出される(Leader Electionという)。
難しくてあんまり理解できてないが、 高い可用性と一貫性を担保している仕組みという理解に止めておく。
参考
kubernetesの場合は、マスターノードの数だけetcdを用意する「stacked etcd」という構成が一般的。
※ etcdをマスターノードではなく外部で構築し運用する場合は「external etcd」という構成になる。 参考
minikubeに接続しetcdctlで検証
$ minikube ssh $ docker run -it --net host gcr.io/etcd-development/etcd /bin/sh $ etcdctl version etcdctl version: 3.3.12 API version: 3.3
やり方は下記を参考にした。 - qiita.com
まずは、必要な環境変数を設定(今回はminikubeの場合)。
export ETCDCTL_API=3 export ETCDCTL_CACERT=/var/lib/minikube/certs/etcd/ca.crt export ETCDCTL_CERT=/var/lib/minikube/certs/apiserver-etcd-client.crt export ETCDCTL_KEY=/var/lib/minikube/certs/apiserver-etcd-client.key
試しにetcdクラスタの一覧を取得。 今回はマスターノードが1台のため一つだけ表示されている。
/ # etcdctl member list aec36adc501070cc, started, minikube, https://192.168.49.2:2380, https://192.168.49.2:2379
keyvalueストアなので、keyを全て出力してみる。
etcdctl get --keys-only --prefix ""
長すぎるので省略するが、例えばnamespaceの場合。
/registry/namespaces/default /registry/namespaces/kube-node-lease /registry/namespaces/kube-public /registry/namespaces/kube-system
kubectlで試しに取得すると同じ結果が出力されている。
k get namespaces NAME STATUS AGE default Active 9h kube-node-lease Active 9h kube-public Active 9h kube-system Active 9h
適当にkeyを指定して、valueを参照する。
# etcdctl get "/registry/namespaces/default" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15809, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":203, "mod_revision":203, "version":1, "value":"Zm9v" } ], "count":1 }
さっき作った、podはどんな感じかというと、
etcdctl get --keys-only --prefix "" | grep dnsutils /registry/events/default/dnsutils.16d654aa24560992 /registry/events/default/dnsutils.16d654aa27a5029a /registry/events/default/dnsutils.16d654aa2e24e48b /registry/events/default/dnsutils.16d66d970a929b22 /registry/events/default/dnsutils.16d66d9748aec6fc /registry/events/default/dnsutils.16d66d974aa74a95 /registry/events/default/dnsutils.16d66d97548d2181 /registry/pods/default/dnsutils
eventと、podのリソースが存在する。 適当に見てみると、
etcdctl get "/registry/events/default/dnsutils.16d654aa24560992" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15809, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":203, "mod_revision":203, "version":1, "value":"Zm9v" } ], "count":1 } etcdctl get "/registry/pods/default/dnsutils" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15972, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":4742, "mod_revision":15215, "version":14, "value":"Zm9v" } ], "count":1 }
ちゃんとデータが格納されとる。
試しに、新しいkeyを作成してデータを入れてみる。
/ # etcdctl put /test/hoge "hoge" OK / # etcdctl get /test/hoge /test/hoge
さらに、バージョン管理も確認してみる。
/ # etcdctl put /test/piyo piyo OK / # etcdctl put /test/piyo2 piyo2 OK / # etcdctl put /test/piyo3 piyo3 OK / # etcdctl get /test/piyo --prefix /test/piyo piyo /test/piyo2 piyo2 /test/piyo3 piyo3 / # etcdctl get /test/piyo --prefix --sort-by=CREATE /test/piyo piyo /test/piyo2 piyo2 /test/piyo3 piyo3 etcdctl get /test/piyo --rev=0 /test/piyo piyo
まとめ
etcdの中身を確認すると、kubectlやapiがなにをしているのかイメージがつきやすい。
keyはetcdctl put
からもわかるように自由に作成できる。
データ格納後は、各種コントローラーが、apiを経由してetcdの変更差分を検知し、schedulerにより実際の変更を行う流れになる。 この拡張性が、カスタムコントローラを開発しやすくすることにつながってる、と現時点では感じた。
etcdが更新される場合に、コントローラーがどのように差分を検知し、処理を行うのかも気になるので今後検証してみる。
参考
AWSでwowzaを構築(EC2) + live配信(RTSP/HLS)を検証
モチベーション
- wowzaって実際どうなっているのかみてみたい
- 構築方法が知りたい
- どんな機能があるか知りたい
- 料金どれくらいなのか知りたい
- live配信を任意のアプリでできるか確認したい
- RTSPで配信したい
※実際検証したのは2ヶ月前、その時検証したことを記載してます。
前提
- AWS Marketplaceにある「Wowza Streaming Engine (Linux PAID)」を利用
- live配信はIOS版の「LiveReporter」を利用
- 今回は大人の事情でプロトコルにRTSPを利用
検証
1. AWS marketplaceでwowzaを購入とセットアップ
1.1 とりあえず購入
購入した後の料金は、「Estimating your costs」でざっと確認することが可能。
m4.large
が現状選べる中で最も小さいインスタンスだった。今回は検証用なのでこれで良い。
これで、m4.largeのインスタンス料金($0.419/hr) + サブスクリプション費用($15/m)の費用がかかるそう。 EC2の料金($0.129/hr)の他に、ソフトウェアライセンス費用($0.29/hr)もかかるみたい。
参考
region → tokyo Fulfillment Option → 64-bit(x86) instance type → m4.large
購入前の設定 Wowza Streaming Engine (Linux PAID)
起動
起動時のオプションは下記のように設定した。
Choosee Action : Launch from Website EC2 Instance Type : m4.large VPC Settings : 普段使ってるVPCID Subnet Settings : ↑のvpc内のsubnet Security Group Settings : 検証のため全開け Key Pair Settings : 普段使ってる鍵
→ 「Launch」を押下してしばらく待つとインスタンスが起動する。めちゃ便利。
ここからアクセスして、ログイン(wowza/${instance_id})。ログイン後にid/passwordを変更してセットアップ完了。
http://${public_dns}:8088/enginemanager
2.「LiveReporter」でRTSPを利用してLive配信してみる
準備
上のpublic dnsにアクセスして、ヘッダ部分の「Applications」を選択。右上の「Test Players」を選択。「Mobile」タグにIOS,Android/Otherそれぞれのurlが記載されている。今回は、Android/Otherのurlをメモっとく(rtsp://${public_ip}:1935/test/myStream)。
LiveReporterをインストールして、「設定」→ 「RTSP」を選択。 インストール先
こんな感じで接続できる。パスワード求められたらさっきwowzaで再設定したid/passwordを設定すれば良い。
その後同じモーダルの、「AppleHLS」の再生ボタンを押すとlive配信ができていることがわかる。
3.せっかくなのでwowzaの気になった機能をいろいろみてみる
Monitoring
通信プロトコルごとのConnectionsと、Networkを確認できる。
network
connections
Sources
サポートされているエンコーダーを指定して、接続するための設定を教えてくれる。いろんな会社のエンコーダーがあるみたい。
Incoming Streams
wowzaを使って配信されている動画を確認できる。
Stream Targets
配信するストリーミングの対象を追加できる。
Source Security
認証などの設定。
Transcoder
ファイル変換のツール。課金が必要。
検証のまとめ
- 5分くらいでwowzaはセットアップできた。
- インスタンスタイプが最低m4.largeとまあまあでかい。
- RTSPでどうやってpush型で配信しているんだろう?
EC2で直接構築したprometheus/alert managerをECSに移行した時に考えたこと(AWS.ECSで監視)
モチベーション
元々amazon-linuxにprometheusをぶち込んで監視していた。prometheusを実際に運用するとなると、
- 俗人化を防ぎたい
- AWSのクレデンシャルの管理
- どの設定ファイルを上書すれば良いか
- ローカルでも動作確認したい
という課題/要望が出てきた。現場に適用して3ヶ月経って、現在行っている工夫やprometheusのCI/CD周り、設定ファイルの管理方法などをまとめておく。
前提
- 監視対象のタスク定義には、exporterが入っている。
- 今回はECSで稼働しているサービスに焦点を当てる。
- alert managerやprometheusの具体的な設定やパラメータの意味は省略する。
実現したこと
- prometheus/alertmanagerの設定ファイルが安全に管理され、開発者が編集/閲覧可能な状態。
- スケールしたインスタンスをサービスディスカバリで検知し、モニタリング可能な状態(grafanaなどで)。
やったこと
exporter編
下準備として、exporterをタスク定義のコンテナに追加する。
containerDefinitionsにprom/node_exporter
を追加する。SGのport開放も忘れずに行っておく(9100)。
今回は、公式のdocker imageであるprom/node-exporterを設定した。
{ "containerDefinitions": [ { // アプリケーションのコンテナ定義 ... }, { // node_exporterのコンテナ定義 "dnsSearchDomains": null, "environmentFiles": null, "logConfiguration": null, "entryPoint": null, "portMappings": [ { "hostPort": 9100, "protocol": "tcp", "containerPort": 9100 } ], "command": null, "linuxParameters": null, "cpu": 0, "environment": [], "resourceRequirements": null, "ulimits": null, "dnsServers": null, "mountPoints": [], "workingDirectory": null, "secrets": null, "dockerSecurityOptions": null, "memory": null, "memoryReservation": null, "volumesFrom": [], "stopTimeout": null, "image": "prom/node-exporter", "startTimeout": null, "firelensConfiguration": null, "dependsOn": null, "disableNetworking": null, "interactive": null, "healthCheck": null, "essential": true, "links": null, "hostname": null, "extraHosts": null, "pseudoTerminal": null, "user": null, "readonlyRootFilesystem": null, "dockerLabels": null, "systemControls": null, "privileged": null, "name": "node-exporter" } ], ... }
タスクを起動して、アクセスできたらOK.
$ curl ${public_ip}:9100/metrics # HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. # TYPE go_gc_duration_seconds summary go_gc_duration_seconds{quantile="0"} 1.3784e-05 go_gc_duration_seconds{quantile="0.25"} 1.3784e-05 go_gc_duration_seconds{quantile="0.5"} 1.6655e-05 go_gc_duration_seconds{quantile="0.75"} 1.6655e-05 go_gc_duration_seconds{quantile="1"} 1.6655e-05 go_gc_duration_seconds_sum 3.0439e-05 go_gc_duration_seconds_count 2
prometheus編
本題。
githubで管理するにあたり
ざっくり下記を工夫した。
- AWSのクレデンシャルは含めない
- 最低限の設定ファイルだけを管理する
- 設定ファイルに含まれるクレデンシャルは
docker build
時に変数を適用する
ローカルで動作確認したい場合には、docker-compose.yaml
をtemplateから作成して(.gitignoreしている)、envにAWSのクレデンシャル情報を記載する。それでもミスるので、git-secret
は入れた方がいいと思う。
docker build
時に、envsubst
を利用して、各yamlの任意の変数を、環境変数の値に適用する仕組みにしている。
ディレクトリ構成
├── _alert.dockerfile // alert manager ├── _prom.dockerfile // prometheus ├── alert_manager │ └── template.config.yaml // alert managerが参照する通知先の設定 ├── prom │ ├── rules.yaml // alert条件の設定 │ └── template.prometheus.yml // prometheusのメトリクス監視の設定 & サービスディスカバリ └── template.docker-compose.yaml
prometheus.yaml
※AWSの変数はenvsubst
で上書きされる。
global: scrape_interval: 15s evaluation_interval: 15s external_labels: monitor: 'monitor' rule_files: - rules.yaml alerting: alertmanagers: - scheme: http static_configs: - targets: - alertmanager:9093 scrape_configs: - job_name: 'prometheus' static_configs: - targets: - prometheus:9090 - node-exporter:9100 - job_name: 'AWS-RESOUCES' ec2_sd_configs: - region: ${AWS_REGION} access_key: ${AWS_ACCESS_KEY} secret_key: ${AWS_SECRET_KEY} port: 9100 # publicIPで取得したい場合は下記を適用する relabel_configs: - source_labels: [__meta_ec2_public_ip] regex: '(.*)' target_label: __address__ replacement: '${1}:9100' - source_labels: [__meta_ec2_tag_Name] target_label: instance
rules.yaml
groups: - name: targets rules: - alert: monitor_service_down expr: up == 0 for: 30s labels: severity: critical annotations: summary: "Monitor service non-operational" description: "Service {{ $labels.instance }} is down."
alert.conf
めんどくさくなって、${WEBHOOK}はべた書きで試したけど、envsubst
で同様に適用することも可能なはず。
global: slack_api_url: '${WEBHOOK}' route: receiver: 'slack' receivers: - name: 'slack' slack_configs: - channel: '#alerts' text: "{{ .CommonAnnotations.summary }}" send_resolved: true
ローカルで動作確認
template.docker-compose.yaml
からdocker-compose.yaml
を作成して、environment
にクレデンシャル情報を記載する。
prometheusのサービスディスカバリはそのままローカルでも適用される(public subnetの場合だけかも)。
# ここを書き換える environment: AWS_REGION: ${AWS_REGION} AWS_ACCESS_KEY: ${AWS_ACCESS_KEY} AWS_SECRET_KEY: ${AWS_SECRET_KEY} # buildして起動 $ cd /path/to/repository/ $ docker-compose build $ docker-compose up
deploy
docker-compose build
で生成された、各イメージ(${REPO}_prometheus
,${REPO}_alertmanager
)をpush。
docker-compose build docker tag ${image_name}:latest ${URI}/${image_name}:latest docker push ${URI}/${image_name}:latest
タスク定義の実行コマンドは下記のようになる、
prometheusの場合
エントリポイント ["sh","-c"] コマンド ["envsubst < /etc/prometheus/template.prometheus.yml > /etc/prometheus/prometheus.yml ; /bin/prometheus --config.file=/etc/prometheus/prometheus.yml --web.console.libraries=/usr/share/prometheus/console_libraries --web.console.templates=/usr/share/prometheus/consoles"] ポートマッピング:9090→9090 tcp # 環境変数も設定しておくこと - AWS_ACCESS_KEY - AWS_REGION - AWS_SECRET_KEY
alertmanagerの場合
エントリポイント ["sh","-c"] コマンド ["--config.file=/etc/alertmanager/config.yaml"] ポートマッピング:9093→9093 tcp
まとめ
成果物は下記にまとめておきました。 また課題が見つかり次第、いろいろ試そうと思っています。
参考
※envsubst
で環境変数を適用する方法など
https://cross-black777.hatenablog.com/entry/2017/10/30/221644