k3s でローカルストレージを使って minio を動かしてみました。

確認環境

  • k3s version v0.5.0 (8c0116dd)
  • minio/minio の docker image

k3s

kubectl で接続する k3s サーバーは

curl -sfL https://get.k3s.io | sh -

または

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable-agent" sh -

でインストールしました。 バージョンアップも同じコマンドです。

サーバーでノード追加用のトークンを確認しておきます。 16 進数っぽいところを h に置き換えると以下のような感じです。

root@k3s-01:~# cat /var/lib/rancher/k3s/server/node-token
Khhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh::node:hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh

他のノードはサーバーの IP アドレス (仮に 10.1.2.3) と先ほどのトークンを使って

curl -sfL https://get.k3s.io | K3S_URL=https://10.1.2.3:6443 K3S_TOKEN=Khhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh::node:hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh sh -

のように追加します。

kubectl

サーバー上の /etc/rancher/k3s/k3s.yaml を手元にコピーして ~/.kube/config に置きます。 すでに他の context が存在する場合は退避しておくか、頑張ってマージします。

ちゃんと設定できていれば、以下のようにノードを確認できます。

% kubectl get node
NAME      STATUS   ROLES    AGE   VERSION
k3s-01    Ready    <none>   XXd   v1.14.1-k3s.4
k3s-02    Ready    <none>   XXd   v1.14.1-k3s.4
k3s-03    Ready    <none>   XXd   v1.14.1-k3s.4

StorageClass 作成

local-storage.yaml を作成して kubectl apply -f local-storage.yaml で適用します。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

作成できていると、以下のようになります。

% kubectl get storageclass
NAME            PROVISIONER                    AGE
local-storage   kubernetes.io/no-provisioner   30h

PersistentVolume 作成

お試し用ということで、適当に tmp と同じパーミッションにして install -m 1777 -d /data/vol1install -m 1777 -d /data/vol2 でディレクトリを作成して、対応する PersistentVolume を作成します。 レプリカ数以上の PersistentVolume が必要なので、お試し用ということで、各ノードに2個ずつ作成しました。

hostname が k3s-01,k3s-02,k3s-03 で path が vol1,vol2 の組み合わせで 6 個になりました。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-01-1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-01
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-02-1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-02
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-03-1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-03
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-01-2
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol2
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-01
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-02-2
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol2
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-02
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-03-2
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data/vol2
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k3s-03

minio もデプロイした後は以下のようになります。

% kubectl get pv
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                  STORAGECLASS    REASON   AGE
local-pv-01-1   1Gi        RWO            Delete           Bound       default/data-minio-2   local-storage            28h
local-pv-01-2   1Gi        RWO            Delete           Available                          local-storage            28h
local-pv-02-1   1Gi        RWO            Delete           Bound       default/data-minio-1   local-storage            28h
local-pv-02-2   1Gi        RWO            Delete           Bound       default/data-minio-3   local-storage            28h
local-pv-03-1   1Gi        RWO            Delete           Bound       default/data-minio-0   local-storage            28h
local-pv-03-2   1Gi        RWO            Delete           Available                          local-storage            28h

minio の deploy

Download から Kubernetes を選んで、 Access Key と Secret Key は空欄にしておくと自動生成してくれるようなので、空欄にしておいて、 Standalone を Distributed に変更して、 Number of Nodes は 4 以上と言われるので 4 にして、 Size は 2 GB 以上と言われるので 2 GB にして生成しました。

実際に使ったものから Access Key と Secret Key を S3 のサンプルのものに書き換えたものが以下になります。

apiVersion: v1
kind: Service
metadata:
  name: minio
  labels:
    app: minio
spec:
  clusterIP: None
  ports:
    - port: 9000
      name: minio
  selector:
    app: minio
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: minio
spec:
  serviceName: minio
  replicas: 4
  template:
    metadata:
      labels:
        app: minio
    spec:
      containers:
      - name: minio
        env:
        - name: MINIO_ACCESS_KEY
          value: "AKIAIOSFODNN7EXAMPLE"
        - name: MINIO_SECRET_KEY
          value: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
        image: minio/minio
        args:
        - server
        - http://minio-{0...3}.minio.default.svc.cluster.local/data
        ports:
        - containerPort: 9000
        # These volume mounts are persistent. Each pod in the PetSet
        # gets a volume mounted based on this field.
        volumeMounts:
        - name: data
          mountPath: /data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 2
      # Uncomment and add storageClass specific to your requirements below. Read more https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1
      storageClassName: local-storage
---
apiVersion: v1
kind: Service
metadata:
  name: minio-service
spec:
  type: LoadBalancer
  ports:
    - port: 9000
      targetPort: 9000
      protocol: TCP
  selector:
    app: minio

デプロイした後は以下のようになります。

% kubectl get pvc
NAME           STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS    AGE
data-minio-0   Bound    local-pv-03-1   1Gi        RWO            local-storage   28h
data-minio-1   Bound    local-pv-02-1   1Gi        RWO            local-storage   28h
data-minio-2   Bound    local-pv-01-1   1Gi        RWO            local-storage   28h
data-minio-3   Bound    local-pv-02-2   1Gi        RWO            local-storage   28h
% kubectl get pod
NAME                        READY   STATUS      RESTARTS   AGE
minio-0                     1/1     Running     0          28h
minio-1                     1/1     Running     0          28h
minio-2                     1/1     Running     0          28h
minio-3                     1/1     Running     0          28h
svclb-minio-service-b74jd   1/1     Running     0          28h
svclb-minio-service-hnfnj   1/1     Running     0          28h
svclb-minio-service-r8w5g   1/1     Running     0          28h

トラブルシューティング

kubectl describe pod minio-00/3 nodes are available: 3 node(s) didn't find available persistent volumes to bind. のように pvc が失敗して pod が起動できない場合は pvc に対応できる pv が足りないのが原因でした。

PersistentVolume の yaml の内容がまずくて、ちゃんと作成できていなかったり、数が足りない場合におきていました。

動作確認

kubectl port-forward minio-0 9000:9000

で port forwarding しつつ http://localhost:9000 を開いて Access Key と Secret Key でログインして試しました。

そして /data/vol1/data/vol2 にちゃんとファイルが保存されているのを確認しました。

削除

kubectl delete -f minio-deployment.yaml だけでは pvc は消えなくて、 pv は pvc を削除しておかないと消せないようで、以下のようにすると消せました。

% kubectl delete -f minio-deployment.yaml
service "minio" deleted
statefulset.apps "minio" deleted
service "minio-service" deleted
% kubectl delete pvc data-minio-{0,1,2,3}
persistentvolumeclaim "data-minio-0" deleted
persistentvolumeclaim "data-minio-1" deleted
persistentvolumeclaim "data-minio-2" deleted
persistentvolumeclaim "data-minio-3" deleted
% kubectl delete -f pv.yaml
persistentvolume "local-pv-01-1" deleted
persistentvolume "local-pv-02-1" deleted
persistentvolume "local-pv-03-1" deleted
persistentvolume "local-pv-01-2" deleted
persistentvolume "local-pv-02-2" deleted
persistentvolume "local-pv-03-2" deleted
% kubectl delete storageclass local-storage
storageclass.storage.k8s.io "local-storage" deleted

/data/vol1/data/vol2 も不要なら削除しておきます。 この中身が残っていれば minio の方はデプロイし直せばまた見えるようになりました。

まとめ

kubernetes は勉強中なので、まだよくわかっていないところも多いですが、 チュートリアルによくあるステートレスな pod 以外も動かせるようになりました。

とりあえずローカルストレージの PersistentVolume の使い方がある程度わかったので、 単独サーバーの docker の代わりに使っていけそうな気がしました。

Disqus Comments

Kazuhiro NISHIYAMA

Ruby のコミッターとかやってます。 フルスタックエンジニア(って何?)かもしれません。 About znzに主なアカウントをまとめました。

znz znz


Published