今回は「Kubernetes 上 に MongoDB の レプリカセット を 構築する 方法」についてまとめます。
概要
MongoDB 3台構成 の レプリカセット を構築します。
また、レプリカセット構築にあたっては「キーファイル(--keyFile
)」「認証(--auth
)」「実行ユーザー変更(runAsUser
)」も含めた内容になっています。
最後の StatefulSet がいろいろな情報を含んでしまって濁ってしまっていますが…3種類を含んでいることを踏まえて読んでいただければ理解しやすいと思います。
前提
今回の検証は以下のような環境で実施しています。
- Kubernetes v1.14.2
- Docker v18.09.6
PersistentVolume
今回は3台構成なのでデータ保存先も3か所用意します。 環境の都合で保存先は同じ NFS になっています。
-
マニュフェスト作成
mongo-pv.yml
apiVersion: v1 kind: PersistentVolume metadata: name: pv0001 labels: type: nfs spec: capacity: storage: 3Gi storageClassName: slow accessModes: ["ReadWriteMany"] persistentVolumeReclaimPolicy: Retain nfs: server: 10.51.1.103 path: /var/share/pv0001 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv0002 labels: type: nfs spec: capacity: storage: 3Gi storageClassName: slow accessModes: ["ReadWriteMany"] persistentVolumeReclaimPolicy: Retain nfs: server: 10.51.1.103 path: /var/share/pv0002 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv0003 labels: type: nfs spec: capacity: storage: 3Gi storageClassName: slow accessModes: ["ReadWriteMany"] persistentVolumeReclaimPolicy: Retain nfs: server: 10.51.1.103 path: /var/share/pv0003
-
適用
kubectl apply -f mongo-pv.yml
-
確認
kubectl get pv
Service
Podネットワーク中でサービス名を使った検索をするわけではないので、ClusterIP は不要になります。 必要なのはPodの名前からIPアドレスを引けるようにするため、ヘッドレスサービス を作成します。
-
マニュフェスト作成
mongo-svc.yml
apiVersion: v1 kind: Service metadata: name: mongo-svc labels: app: mongodb spec: ports: - port: 27017 targetPort: 27017 clusterIP: None # Headless Service selector: app: mongodb # match "StatefulSet spec.template.metadata.labels"
HeadlessService は clusterIP に None を指定することで実現できます。
-
適用
kubectl apply -f mongo-svc.yml
-
確認
kubectl get svc
Secret
「ユーザー名、パスワード、キーファイル」を準備します。
-
キーファイル作成
openssl rand -base64 768 > keyfile
MongoDBのキーファイルは「6~1024文字」なのですが…1024文字で生成すると少しデータが膨らむようでうまく動きませんでした。 そのため文字数は少な目で生成します。
-
Secretリソース作成
kubectl create secret generic mongo-secret \ --from-literal=root_username=admin \ --from-literal=root_password=Passw0rd \ --from-file=./keyfile
-
確認
kubectl get secrets
StatefulSet
とても残念なことに MongoDB の公式イメージを素のまま使おうとすると認証回りの初期化処理がうまく動きませんでした。。 仕方ないので、公式イメージのコマンドに対してシェルスクリプトを挟むことで回避するようにしています。 ただ…結果としてマニュフェストが大きくなってしまってます。。 実運用する場合はきちんとDockerイメージ作って運用したほうがよさそうです。
-
マニュフェスト作成
mongo-sts.yml
apiVersion: apps/v1 kind: StatefulSet metadata: name: mongo-sts namespace: default labels: app: mongodb spec: selector: matchLabels: app: mongodb serviceName: mongo-svc # match "Service metadata.name" replicas: 3 template: metadata: name: mongodb namespace: default labels: app: mongodb ver: "0.0.1" env: staging spec: terminationGracePeriodSeconds: 10 containers: - name: mongodb image: mongo:4.0 ports: - containerPort: 27017 command: - "/bin/sh" - "-c" args: - | INIT_FLAG_FILE=/data/db/init-completed INIT_LOG_FILE=/data/db/init-mongodb.log start_mongod_as_daemon() { echo echo "> start mongod ..." echo mongod \ --fork \ --logpath ${INIT_LOG_FILE} \ --quiet \ --bind_ip 127.0.0.1 \ --smallfiles; } create_user() { echo echo "> create user ..." echo mongo "${MONGO_INITDB_DATABASE}" <<-EOS db.createUser({ user: "${MONGO_INITDB_ROOT_USERNAME}", pwd: "${MONGO_INITDB_ROOT_PASSWORD}", roles: [{ role: "root", db: "${MONGO_INITDB_DATABASE}" }] }) EOS } create_initialized_flag() { echo echo "> create initialized flag file ..." echo cat <<-EOF > "${INIT_FLAG_FILE}" [$(date +%Y-%m-%dT%H:%M:%S.%3N)] Initialize scripts is finished. EOF } stop_mongod() { echo echo "> stop mongod ..." echo mongod --shutdown; } start_mongod() { mongod \ --auth \ --clusterAuthMode "keyFile" \ --keyFile "/home/mongodb/keyfile" \ --replSet "rs0" \ --bind_ip_all \ --smallfiles; } if [ ! -e ${INIT_FLAG_FILE} ]; then echo echo "--- Initialize MongoDB --------------" echo start_mongod_as_daemon create_user create_initialized_flag fi echo echo "--- Start MongoDB -------------------" echo start_mongod env: - name: "MONGO_INITDB_ROOT_USERNAME" valueFrom: secretKeyRef: name: mongo-secret key: root_username - name: "MONGO_INITDB_ROOT_PASSWORD" valueFrom: secretKeyRef: name: mongo-secret key: root_password - name: "MONGO_INITDB_DATABASE" value: "admin" volumeMounts: - name: mongo-secret-keyfile mountPath: /etc/mongo/secrets readOnly: true - name: volatile-volume mountPath: /home/mongodb - name: nfs-storage mountPath: /data/db securityContext: runAsUser: 999 runAsGroup: 999 initContainers: - image: centos:7 name: mongo-init volumeMounts: - name: mongo-secret-keyfile mountPath: /etc/mongo/secret - name: volatile-volume mountPath: /home/mongodb command: - "/bin/sh" - "-c" args: - | cp /etc/mongo/secret/keyfile /home/mongodb/keyfile chown 999:999 /home/mongodb/keyfile chmod 700 /home/mongodb/keyfile exit securityContext: fsGroup: 999 volumes: - name: mongo-secret-keyfile secret: secretName: mongo-secret items: - key: keyfile path: keyfile mode: 384 - name: volatile-volume emptyDir: {} volumeClaimTemplates: - metadata: name: nfs-storage spec: storageClassName: slow accessModes: - ReadWriteMany resources: requests: storage: 3Gi
L.12
serviceName
は 作成済みの HeadlessService 名 と同じ名前を指定してあげます。 ここでサービス名が異なっているとサービス利用してPod名を解決できません(=DNSにレコードが登録されない)。L.34-102
DBの初期化で利用するユーザー名、パスワードは 作成済の Secretリソース から取得します。 こうすることで、機微な情報をリポジトリにコミットせずにすみます。 MongoDB公式の初期化処理がいまいちなので…結局このあたりの処理は自前実装しています。L.128, L.146, L.154
keyfile
の パーミッションは実行ユーザーのみに限定しないと MongoDB が起動しません。 これを実現するためには、mode
に 10進数 か 8進数 でパーミッションを指定し、fsGroup
にmongodb (UID=999)
を指定します。 本来であればこの設定(mode
とfsGroup
)のみでうまく動いてほしいのですが… Secrets と ConfigMap はマウントする際、強制的に「root 所有の 読み取り専用、フルアクセス」になってしまいます。 そのため、initContainers
とemptyDir
の組み合わせ技で中間処理を挟んで適切な所有者とアクセス権になるよう修正します。L.161, 162, 163, 166
volumeClaimTemplates
は作成した PersistentVolume にあわせて指定します。 一致しなければいけないのはstorageClassName
、accessModes
。storage
は PersistentVolume より小さい値であればOKです。 -
適用
kubectl apply -f mongo-sts.yml.yml
-
確認
kubectl get sts,po
初期設定
前述までの実装ができていれば MongoDB サーバー が 3台 立ち上がっているはずです。 まだ単独で動作しているだけの状態なので、レプリカセットとしての初期化を行っていきます。
-
いずれかのPodに入る
[root@k8s-master ~]# kubectl exec -it mongo-sts-0 -- sh
-
MongoDBに接続
# mongo mongo-sts-0.mongo-svc
-
adminデータベースにログイン
> use admin > db.auth("admin", "Passw0rd")
-
レプリカセットを初期化
> config = { _id : "rs0", members: [ { _id: 0, host: "mongo-sts-0.mongo-svc:27017" }, { _id: 1, host: "mongo-sts-1.mongo-svc:27017" }, { _id: 2, host: "mongo-sts-2.mongo-svc:27017" }, ] } > rs.initiate(config)
動作確認
動作確認は MongoDB クラスタに接続して以下のようなコマンドを実行することで状態確認してみます。
> rs.status()
今回は「Kubernetes 上 に MongoDB の レプリカセット を 構築する 方法」についてまとめました。 参考になったでしょうか? 本記事がお役に立っていると嬉しいです!!
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!