Misskey API の使い方
Misskey API の基本的な使い方をまとめておきます。
他の人のノートを取得したくて調べてみました。
ちなみに、`userId` で沼って意外と時間がかかってしまいました。
公式サイトの説明が一番わかり易い。
misskey-hub.net
トークンの取得以外は単体で完結しています。
トークンの取得
「設定>API」からトークンを取得できる。
こういう記事もある。
Misskeyのアクセストークン発行方法 | BaskMedia
メタデータの取得
- https://misskey-hub.net/docs/api/endpoints/meta.htmlで自サーバーのデータを取得する。
`host`にはホスト名を、`token`には先程取得したトークンを代入する。
host = "" token = "" headers = {'Content-Type': 'application/json'} url = f"https://{host}/api" body = { "i" : token, "detail": False, } r = requests.post(f"{url}/meta", headers=headers, json=body) print(r.text)
ノートの作成
`host`にはホスト名を、`token`には先程取得したトークンを代入する。
host = "" token = "" headers = {'Content-Type': 'application/json'} url = f"https://{host}/api" text = "Hello, World!" body = { "i" : token, "visibility": "home", "text": text, } r = requests.post(f"{url}/notes/create", headers=headers, json=body)
ノートの検索
`host` にはホスト名を、`token` には先程取得したトークンを代入する。
`user_id` にはユーザーID を入れる。
自分のユーザーID は、「設定>その他>アカウント情報」から見ることができる。
他の人のものは、検索タブでその人を指定して検索を行い、開発者ツールのネットワークから「search」を探して、リクエストの body を見るとわかる。
host = "" token = "" headers = {'Content-Type': 'application/json'} url = f"https://{host}/api" user_id = "" body = { "i" : token, "limit": 10, "query": "", "userId": user_id, } r = requests.post(f"{url}/notes/search", headers=headers, json=body) print(r.text)
Raspberry Pi クラスター
Raspberry Pi を集めて、Kubernetes クラスターを作った。
コンテナランタイムは cri-o を、CNI は Flannel を利用した。
後述の参考のコピペがほとんど。
sudo がついてたりついてなかったりする。
以下の Ansible を見ればわかる。そのうち変わってるかも。
https://github.com/kamojiro/study/tree/main/raspberry_pi_cluster/ansible
基本的にはこれを書き下したものになるはず。
基本情報
ハードウェア
- Raspberry Pi B4 RAM 4GB × 3
- Switch Science で Raspberry Pi 4 スターターキット(4GB RAM版)を買った
- クラスターケース
- SDカード × 3
- スターターキットについてきたのと、家にあったやつ
- スイッチングハブ
- LANケーブル
- USB充電器
- USBケーブル
IP アドレスとか
raspberry pi | host | role | IP address | MAC address | disk |
4B | k8s-master | master | 192.168.10.101 | e4:5f:01:e2:59:e4 | |
k8s-worker1 | worker | 192.168.10.102 | e4:5f:01:e2:59:14 | 128GB | |
k8s-worker2 | worker | 192.168.10.103 | e4:5f:01:e2:5a:1e |
ネットワーク関連の設定
やらなくていいかも(よくわからずにやった)
/etc/netplan/99-network.yaml
network: version: 2 renderer: networkd ethernets: eth0: dhcp4: false dhcp6: false addresses: - 192.168.1.101/24 # ラズパイ毎でIPを変更してください。(末尾がそれぞれ101, 102, 103になります。) routes: # 自宅のルーターからゲートウェイを調べて下さい。ここを間違えるとラズパイはネットに繋がらなくなります… - to: default via: 192.168.10.1 nameservers: addresses: - 192.168.10.1
sudo netplan apply
ホスト名の変更
@raspberry_pi
ラズパイのOSインストール時に指定したので以下のコマンドは不要
sudo hostnamectl set-hostname <hostname> hostname
DNS的なののために、etc/hosts に追記
192.168.1.101 k8s-master kube-master.kamoj.com 192.168.1.102 k8s-worker1 kube-master1.kamoj.com 192.168.1.103 k8s-worker2 kube-worker2.kamoj.com
IPv6 の無効化
etc/sysctl.conf の最後に追記
net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.eth0.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1
timezone, keymap の変更
今回は OS インストール時に設定したので不要
# タイムゾーンの変更 sudo timedatectl set-timezone Asia/Tokyo # keymapの変更 sudo localectl set-keymap jp106
ssh
今回は OS インストール時に設定したので不要
cri-o
カーネルパラメーターの設定
sudo modprobe overlay # 必要なカーネルパラメータの設定をします。これらの設定値は再起動後も永続化されます。 cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF sudo sysctl --system
準備
sudo apt install -y curl gnupg2 libseccomp2
cri-o のインストール
OS=xUbuntu_22.04 VERSION=1.25 echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list mkdir -p /usr/share/keyrings curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | apt-key add - curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | apt-key add - apt update apt install -y cri-o apt install -y cri-o-runc
cri-o と cri-o-runc を並べてインストールしようと失敗する(?)
```bash sudo systemctl daemon-reload sudo systemctl start crio sudo systemctl enable crio systemctl status crio
コントロールプレーンノードの kubelet によって使用される cgroup ドライバーの設定
CRI の場合は、/etc/default/kubelet
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
sudo systemctl daemon-reload sudo systemctl restart kubelet
おまじない
再起動後とか、うまくいかないときはこれを再実行してみる。
sudo modprobe br_netfilter echo '1' > /proc/sys/net/ipv4/ip_forward
Kubernetes
requirements
VXLAN モジュールのインストール
Flannel で使うので。
sudo apt install -y linux-modules-extra-raspi
Flannel のデプロイ前までに再起動が必要。
cgroup で memory の有効化(よくわからない)
/boot/firmware/cmdline.txt の末尾に cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory を追記
おまじない
echo '1' > /proc/sys/net/ipv4/ip_forward
swap の無効化
sudo swapoff -a
iptables が nftables バックエンドを使用しないようにする
sudo apt install -y iptables arptables ebtables
# レガシーバイナリがインストールされていることを確認してください sudo apt install -y iptables arptables ebtables # レガシーバージョンに切り替えてください。 sudo update-alternatives --set iptables /usr/sbin/iptables-legacy sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy sudo update-alternatives --set arptables /usr/sbin/arptables-legacy sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy
準備
sudo apt install -y apt-transport-https curl
kubeadm、kubelet、kubectlのインストール
- kubeadm: クラスターを起動するubectl label node work02 node-role.kubernetes.io/worker=workerコマンドです。
- kubelet: クラスター内のすべてのマシンで実行されるコンポーネントです。 Podやコンテナの起動などを行います。
- kubectl: クラスターにアクセスするためのコマンドラインツールです。
sudo apt update && sudo apt install -y apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list deb https://apt.kubernetes.io/ kubernetes-xenial main EOF sudo apt update sudo apt install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl
Kubernetes クラスターの作成
コントロールプレーンの設定@master
sudo kubeadm init --apiserver-advertise-address=192.168.10.101 --pod-network-cidr=10.244.0.0/16
- apiserver-advertise-address
- このオプションを利用して明示的にAPIサーバーのadvertise addressを設定します。
- 明示的に指定しない場合はデフォルトゲートウェ 佐野なつイに関連付けられたネットワークインターフェースを使用して設定されます。
- pod-network-cidr
初期化後、kubeadm join 192.168.1.101:6443 --token ...という出力が出たら、どこかのテキストエディタにコピーしておきます。
このコマンドはワーカーノードを追加する際に利用します。
kubectl の設定
# ホームディレクトリに.kubeディレクトリを作成 mkdir -p ~/.kube # Kubernetesのadmin.confを.kubeディレクトリのconfigファイルへコピー sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config # configファイルの所有者がrootになっているのでk8suserへ変更 sudo chown $(id -u):$(id -g) ~/.kube/config # .bashrcへ環境変数の追加 echo 'KUBECONFIG=$HOME/.kube/config' >> ~/.bashrc # コマンドの入力補完を設定 echo "source <(kubectl completion bash)" >> $HOME/.bashrc # 変更を適用 source ~/.bashrc
Flannel のデプロイ@master
Flannel をデプロイするまでは、coredns は pending 状態になっている。
以下は、やってなかったらやっておく
sudo apt install -y linux-modules-extra-raspi sudo reboot
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
kubectl get pods -n kube-flannel
MetalLB のデプロイ
IaaS だと、type: LoadBalancer で IP アドレスを払い出してくれる。
そんな感じで、IP アドレスの自動割当をやってくる。
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml # ... kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
kubectl get pod -n metallb-system
ワーカーノードの追加
modprobe br_netfilter
sudo kubeadm join 192.168.1.101:6443 --token 3u2z7v.qx81p3azvu15ftzw --discovery-token-ca-cert-hash sha256:0e731a79605ba03cfbc823e86e8b81b2fcdcf7f1887c1d2d86239ed87fff8d04
kubectl get nodes
kubectl label node k8s-worker1 node-role.kubernetes.io/worker=worker kubectl label node k8s-worker2 node-role.kubernetes.io/worker=worker
kubectl get nodes
確認
$ kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 46h v1.26.0 k8s-worker1 Ready worker 33m v1.26.0 k8s-worker2 Ready worker 19m v1.26.0 $ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-flannel kube-flannel-ds-dm67n 1/1 Running 0 33m kube-flannel kube-flannel-ds-hj2zl 1/1 Running 516 45h kube-flannel kube-flannel-ds-kx9c8 1/1 Running 0 20m kube-system coredns-787d4945fb-dhmzs 1/1 Running 0 46h kube-system coredns-787d4945fb-zjp28 1/1 Running 0 46h kube-system etcd-k8s-master 1/1 Running 1 46h kube-system kube-apiserver-k8s-master 1/1 Running 1 46h kube-system kube-controller-manager-k8s-master 1/1 Running 1 46h kube-system kube-proxy-5hxm7 1/1 Running 1 46h kube-system kube-proxy-8dtn8 1/1 Running 0 20m kube-system kube-proxy-xfrd2 1/1 Running 0 33m kube-system kube-scheduler-k8s-master 1/1 Running 1 46h metallb-system controller-577b5bdfcc-ql2fb 1/1 Running 1 (31m ago) 4h28m metallb-system speaker-j5shp 1/1 Running 0 19m metallb-system speaker-lfvxc 1/1 Running 0 32m metallb-system speaker-ttxpb 1/1 Running 2 (101m ago) 4h28m
おまけ
Kubernetes クラスターの削除
sudo kubeadm reset && rm ~/.kube/config && rm -r /etc/cni/net.d && sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F && sudo iptables -X sudo kubeadm init --apiserver-advertise-address=192.168.10.101 --pod-network-cidr=10.244.0.0/16 sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config && sudo chown $(id -u):$(id -g) ~/.kube/config
参考文献
Raspberry Piをかき集めてKubernetesを体感する | フューチャー技術ブログ
Raspberry Pi で【リアル☆Kubernetes】を作る!!
Raspberry Pi 4 でおうちKubernetesを作ろう(Raspbian Buster Lite対応版) - Qiita
Raspberry Pi×4台によるKubernetesクラスター構築 Yu'n Craft
Raspberry Pi + MicroK8sで居室環境モニタリングシステムを構築してみました | NTTテクノクロスブログ
Raspberry PiでおうちKubernetesクラスタ
おうちKubernetes構築日記その1 Raspberry Pi編 - nownab.log
Raspberry Piで動かしていたKubernetesクラスターを壊した私(後半) | by gavin.zhou | Medium
Raspberry PiでおうちKubernetesクラスタを構築する - sambaiz-net
アドオンのインストール | Kubernetes
Creating a cluster with kubeadm | Kubernetes
GitHub - cri-o/cri-o: Open Container Initiative-based implementation of Kubernetes Container Runtime Interface
Raspberry Pi 4BにKubernetesをインストール(2021年版) - @uyorumの雑記帳
gRPC
下書きに残っていて書く気は無いけど、もったいないので出しておく。
gRPCの備忘録
gRPC の g は、多分 google の g。
google が開発したものは g を付けがちなので。
概要
google に開発されたハイパフォーマンスな RPC のプロトコルです。
高速かつ HTTP/2 の機能を利用した双方向通信なども利用でき、インターフェース定義言語(Protocol Buffers)をベースに、複数言語のクライアントモジュールとサーバーサイドモジュールを生成できます。
仕組み
Protocol Buffers から、クライアントモジュールとサーバーサイドモジュールを生成する。(protoc)
以下の図にある、gRPC サーバーや gRPC Stub は単一の Protocol Buffers から生成されています。
C++ や Ruby など様々な言語のモジュールを自動生成します。
特徴
- 高速な通信
- 複数の通信方法
- 単一の Protocol Buffers から複数言語のモジュールを生成
高速な通信
複数の通信方法
HTTP/1.1 までで利用されてきた、GET、POST などのメソッドは、1つのリクエストに対して、1つのレスポンスを返すという方式でした。
HTTP/2 では、ストリームという機能により、複数のリクエスト、レスポンスを同時に処理することができるようになりました。
gRPC には、以下の4つのサービスメソッドがあります。
- unary RPC
- リクエスト 1 : レスポンス 1
- server streaming RPC
- リクエスト 1 : レスポンス 多
- client streaming RPC
- リクエスト 多 : レスポンス 1
- bidirectional streaming RPC
- リクエスト 多 : レスポンス 多
単一の Protocol Buffers から複数言語のモジュールを生成
gRPC では、Protocol Buffers というインターフェース定義言語を用いてサービスを定義しています。
これを基に各言語のモジュールを自動生成します。
多くの場合、各言語のビルドツールなどが依存関係などを考慮して自動でダウンロードしてくれます。
たまにその機能を実装していないツールもあります。
Rust MongoDB Driver
Rust MongoDB Driver
Rust の MongoDB Driver を少しやったので。
Rust よくわからないので結構詰まった。
MongoDB Driver のバージョンは、2.x です。
ソースコード
サンプルコード
use mongodb::bson::{doc}; use mongodb::options::{FindOptions,ClientOptions}; use mongodb::{Client, Collection}; use tokio; use crate::CustomError::NotFound; use futures::stream::StreamExt; use serde::{Serialize,Deserialize}; use derive_more::{Display, Error}; const DB_NAME: &str = "todo_list"; const COLLECTION_NAME: &str = "todos"; #[derive(Debug, Serialize, Deserialize)] pub struct Todo { // #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub name: String, } #[derive(Clone, Debug)] pub struct MongoDbClient { client: Client, } impl MongoDbClient{ pub async fn new(mongodb_uri: String) -> Self { println!("{}", mongodb_uri); let mut client_options = ClientOptions::parse(mongodb_uri).await.unwrap(); println!("bbb"); client_options.app_name = Some("My App".to_string()); let client = Client::with_options(client_options).expect("failed to create client"); for db_name in client.list_database_names(None, None).await.expect("failed to get database list") { println!("{}", db_name); } MongoDbClient{ client: client, } } pub async fn get_all_todos(&self) -> Result<Vec<Todo>>{ let mut result = Vec::new(); let collection = self.get_todos_collection().await; let filter = doc!{}; let find_options = FindOptions::builder().sort(doc! {"name": 1}).build(); let mut todos = collection.find(filter, find_options).await?; while let Some(todo) = todos.next().await { result.push(todo?) } Ok(result) } pub async fn create_todo(&self, todo: Todo) -> Result<Todo>{ let collection = self.get_todos_collection().await; let insert_result = collection.insert_one(todo, None).await?; let filter = doc!{"_id": &insert_result.inserted_id}; collection.find_one(filter, None).await?.ok_or(NotFound { message: String::from("Can't find a created todo") }) } pub async fn get_todos_collection(&self) -> Collection<Todo>{ self.client .database(DB_NAME) .collection::<Todo>(COLLECTION_NAME) } } #[derive(Debug, Display, Error)] pub enum CustomError { #[display(fmt = message)] MongoDbError { message: String, }, #[display(fmt = message)] NotFound { message: String, }, } impl From<mongodb::error::Error> for CustomError { fn from(source: mongodb::error::Error) -> Self { Self::MongoDbError { message: source.to_string(), } } } impl From<mongodb::bson::oid::Error> for CustomError { fn from(source: mongodb::bson::oid::Error) -> Self { Self::NotFound { message: source.to_string(), } } } #[allow(dead_code)] type Result<T> = std::result::Result<T,CustomError>; #[tokio::main] async fn main(){ // let _ = _connecting().await; let mongodb_uri = "mongodb://localhost:27017".to_string(); // let _ = _connecting().await; let mongodb_client = MongoDbClient::new(mongodb_uri).await; let todo_list = match mongodb_client.get_all_todos().await{ Ok(x) => x, Err(_) => Vec::new(), }; eprintln!("zz {}", todo_list.len()); for x in todo_list{ println!("{}", x.name) } let todo = Todo{ name: "hogeee".to_string(), }; let _ = mongodb_client.create_todo(todo).await; }
Cargo.toml
[package] name = "mongodb" version = "0.1.0" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] mongodb = "*" tokio = "*" serde = "*" derive_more = "*" futures = "*"
grundy数とNim
典型90の031で困ったので。
031 - VS AtCoder(★6)
https://atcoder.jp/contests/typical90/tasks/typical90_ae
納得できなかったので、簡単に証明をあてたり補足したり。
grundy数
いろいろと端折ってDAGから初めます。
詳細は、以下などを参照ください。
Grundy数(Nim数, Nimber)の理論
を頂点集合とし、 を辺集合とします。
ここで、 の元は頂点 から への辺を、 で表すものとします。
また、 で頂点 の grundy 数を表すことにします。
また、 により、集合に含まれない最小の非負整数を表すものとします。
grundy 数が で後手必勝、 より大きい値で先手必勝とします。
grundy数の性質
1. は後手必勝
2.
3. である場合、任意の に対して、 なる頂点 への辺が存在する(遷移できる)
考察1
のとき、 なる任意の に対して、 が成立する。
性質2を書き下した。
考察2
のとき、 なる任意の に対して、 が成立する。
さらに、各 に対して、 なる を選んで、 となるようにできる。
性質 2 と 3 を書き下した。
Nim
Nim の後手必勝の条件は、 であることです。
(以下、この条件を のように表します。)
ここで、 は、grundy 数です。
これを証明します。
方針
競技プログラミングでよく出てくるゲームでは相手にある状態(負けの状態)を押し付け続けることが必勝法になります。
証明
相手に、 を押し付け続けることができれば勝利できます。
特に、すべての に対して、 であれば、 であるため、この状態であれば負けています。
あとは、自分の手番において であれば、相手に押し付け続けることができることを示せば十分です。
これは、grundy数の性質の 3. から grundy数をそれより小さい、任意の数字にできることから分かります。
また、 の性質から、 に操作を行ったとき、必ず となります。
補足
でも、ある と頂点 が存在して、 と、 となることがあります。
これは、 などの場合に発生します。
なぜこれを考える必要がないのか?がこの補足です。
簡単に言うと、利点がないため必勝法において利用されることがないためです。
grundy数の性質 3. から、grundy数が大きくなるほど(元の選択肢を包含する形で)選択肢が増えます。
例えば、grundy数が 2 のときは、grundy数が 0、1 になる頂点に移動できます。
grundy数が 4 のときは、grundy数が 0、1、2、3 になる頂点に移動できます。
これは、grundy数が 2 の場合の移動先をすべて含んでいます。
相手の選択肢を増やす操作に利点がありません。
特に、grundy数はmexで十分意味を持つことも分かります。
MongoDB
MongoDB
MongoDB はドキュメントデータベースです。
The most popular database for modern apps | MongoDB
ドキュメントデータベースは、JSON のような形式でデータを保存するデータベースです。
感覚的には、キーバリューストアのバリュー部分に JSON を利用できるデータベースです。
MongoDB では、JSON を拡張した BSON(Binary JSON)を利用しています。
データ形式の変更が多いゲームアプリケーションや、様々なデータ形式で出力されるログなどの格納先として利用されます。
余談ですが、MongoDB のドキュメントは様々な情報が丁寧に記載されており、読みやすくかつ非常に読み応えがあります。
The MongoDB 4.4 Manual — MongoDB Manual
RDBMS とよく比較される NoSQL です。
お気持ちとしては、データベースのスキーマをデータベースで決めるのが RDBMS で、ドキュメントストアはアプリケーションで設計します。
どっちが大変かくらいの差のような気がしてます。
基本的には、RDBMS を使うのが良さそうな気がしています。
システムが超大規模になるとか、ユースケースにめっちゃ合ってるとかなら、MongoDB を利用するのもありみたいです。
system-design-primer/README-ja.md at master · donnemartin/system-design-primer · GitHub
というか、大規模な場合だとシャーディング構成になるけど、シャーディングのバックアップ/リストアって有料版の機能なんですよね。
つまり、そういうこと。
MongoDB の特徴
前述のように、ドキュメントストアであることが大きな特徴です。
ドキュメントストアですと、Apache CouchDB もありますが、こちらは MongoDB よりもシンプルなものになっています。
MongoDB は何でもできるデータベースという感じです。
アーキテクチャー
BSON をドキュメントと呼び、ドキュメントの集まりをコレクションといいます。
コレクションは、RDB のテーブルに対応するものです。
コレクションの集まりをデータベースと呼びます。
MongoDB にドキュメントを格納すると、実際には以下のようになります。
var mydoc = { _id: ObjectId("5099803df3f4948bd2f98391"), name: { first: "Alan", last: "Turing" }, birth: new Date('Jun 23, 1912'), death: new Date('Jun 07, 1954'), contribs: [ "Turing machine", "Turing test", "Turingery" ], views : NumberLong(1250000) }
_id は、ObjectId と呼ばれ、ドキュメントを一意に特定するもので、RDB での主キーに対応するものになります。
その他、利用できる型は以下にまとめられています。
BSON Types — MongoDB Manual
インデックス
RDB と同様にインデックスを張ることができます。
デフォルトでは、_id がインデックスと利用されますが、ランダムな値であるため高速化に寄与しません。
高速化をおこなうために、特定のフィールドに対してインデックスを張ることができます。
このインデックスは必ずしもすべてのドキュメントに対応させる必要はなく、特定のフィールドを持つものだけを対象としてインデックスを張れます。
Change Stream
コレクションを監視して、更新処理を時系列データとして利用できます。
例えば、変更を検知した場合に、Apache Kafka などのメッセージングキューにデータを送信する、などの処理ができます。
レプリカセット
レプリケーションにより冗長化できます。MongoDB ではプライマリーとセカンダリーからなるレプリケーションクラスターをレプリカセットと呼びます。
一般的なレプリケーションと同様に、プライマリーのデータをセカンダリーに同期します。
レプリケーションは、oplog (operation log)と呼ばれる、実行コマンドを記録したログを用いて行われます。
また、レプリケーションは非同期レプリケーションです。
各ノード同士は、ハートビートにより生存確認を行っており、プライマリーに障害が発生した場合にはセカンダリーをマスターに昇格させます。
このとき、クォーラムを行うために3台以上のノードが必要となります。
しかし、コストなどの理由により高性能なサーバーを2台しか用意できない場合、データをもたない Arbiter というノードを利用できます。
Arbiter はデータを持たず、クォーラムによるマスターの選出にのみ参加します。
話をセカンダリーに戻すと、セカンダリーサーバーは複数の種類があります。
主に以下のものがあります。
種類 | マスターに昇格する | アプリケーションからの参照 | マスターデータとの違い |
---|---|---|---|
通常 | ◯ | ◯ | 同じ |
Priority 0 Replica Set Member | ☓ | ◯ | 同じ |
Hidden Replica Set Member | ☓ | ☓ | 同じ |
Delayed Replica Set Member | ☓ | ☓ | 古い |
Hidden Replica Set Member は、Priority 0 Replica Set Member であり、Hidden Replica Set Member は、Delayed Replica Set Member です。
Priority 0 Replica Set Member は、広域災害などに備えてプライマリーや通常のセカンダリーから離れたデータセンターに配置します。
Hidden Replica Set Member は、管理者用のセカンダリーになります。通常のアプリケーションからのデータを処理していないため、データ分析などの用途に利用しやすいです。
Delayed Replica Set Member は、不足の事態によりデータが失われた場合に備えたものになります。
シャーディング
MongoDB は、ドキュメントストアであり、水平スケーリングに適しています。
サーバーの台数を増やすことで、参照・更新ともに負荷分散が実現可能です。
Shard、Config Server、mongos から構成されます。
- Shard:MongoDB インスタンスです。冗長性を確保するためにレプリカセットにすることが多いです。
- Config Server:クラスターを管理するサーバーであり、メタデータを保持しています。可用性を確保するためにレプリカセットにすることが多いです。
- mongos:アプリケーションサーバーに配置されるルーターです。リバースプロキシのような役割を担っており、Config Server から取得したメタデータを元に、データを Shard たちにルーティングします。
シャーディング方式としては、ハッシュ値を利用したハッシュシャーディングと値を範囲を利用したレンジシャーディングが利用できます。
シャーディングにおいて、データはチャンクと呼ばれる単位で管理されています。
最大64MBほどのデータとなっています。
MongoDB では、シャードごとのデータが均等になるようにチャンクを統合・分割したり、シャード間で移動させることで適切な状態を保ってくれます。
特に、チャンクの格納されるデータは上記のシャーディング方式に従ってまとめられています。
いい感じにデータが分散できるように、シャーディング方式を選択する必要があります。