Oracle Cloudでは期限なく無料で使える、「Always Free」というプランがあると知って、早速こちらで調べてみたところ、1Core CPU、1GBメモリのVMが2つも使える、とのことで、こちらを参考にさせていただきながら、centos7のインスタンスを2台作って、早速Kubernetesをインストールしてみました。
ベアメタルにインストールしたときは、swapをオフにしましたが、このページによるとswapを有効にするように書かれてあったので有効にしました。swapを無効にしないとkubeadmのinitやjoinが失敗するので「–ignore-preflight-erros=swap」を付けました。これで、マスターノード1台、ワーカー ノード1台が無事に起動しました。なお、CNIプロバイダはweave netを使用しました。ちなみに、Oracle Cloudのインスタンスは、インスタンス内でホスト名を変更しても、ある程度時間が経つと勝手に元に戻ってしまい、kubeletを再起動した時にエラーとなってしまうようなので、こちらを参考に、ホスト名を設定し、固定化しました。
そして次に、ストレージの動的プロビジョニングに挑戦しました。OCI Volume Provisionerと、OCI Flexvolume Driverをインストールすれば、PVC使えば自動的にOCIのブロックストレージの作成からPodへのマウントまでしてくれるようになります。しかしここでもかなりハマってしまいました。
まずこれはケアレスミスなのですが、かなり気づくのに長時間かかってしまったのが、provisionerに指定するconfig.yaml(サンプルはこちら)です。このファイルにはpem形式の秘密鍵を貼る必要があるのですが、ペーストした時に空白が混じってしまい、OCIのAPIで401エラーが出てしまいました。ペーストする時にはくれぐれも気を付けないとダメですね。ちなみに今回は、kubernetes用に専用のコンパートメント(CompartmentForK8S)、ユーザー(k8s)とこのユーザーを所属させるグループ(k8s)を作成して、ociコンソールのポリシーのページにて下記のように各種リソースへの権限を設定しました。config.yamlはこれらの情報を元に作成しました。もちろん、k8sユーザーには上記の秘密鍵から公開鍵、フィンガープリントを作成してAPIキーに設定してあります。
Allow group k8s to manage volumes in compartment CompartmentForK8s
Allow group k8s to manage volumes in compartment CompartmentForK8s
Allow group k8s to manage file-systems in compartment CompartmentForK8s
Allow group k8s to manage file-systems in compartment CompartmentForK8s
Allow group k8s to read vnic-attachments in compartment CompartmentForK8s
Allow group k8s to read vnic-attachments in compartment CompartmentForK8s
Allow group k8s to read vnics in compartment CompartmentForK8s
Allow group k8s to read vnics in compartment CompartmentForK8s
Allow group k8s to read instances in compartment CompartmentForK8s
Allow group k8s to read instances in compartment CompartmentForK8s
Allow group k8s to read subnets in compartment CompartmentForK8s
Allow group k8s to read subnets in compartment CompartmentForK8s
Allow group k8s to use volumes in compartment CompartmentForK8s
Allow group k8s to use volumes in compartment CompartmentForK8s
Allow group k8s to use instances in compartment CompartmentForK8s
Allow group k8s to use instances in compartment CompartmentForK8s
Allow group k8s to manage volume-attachments in compartment CompartmentForK8s
次にハマったのは、flexvolume driverのservice accountがない、というエラー。これは単純で、gitから取得したservice accountを作成するyamlにnamespaceの指定がなかったのが原因でした。これは、kube-systemを指定することで解決しました。なお、flexvolume driverにはkubeconfigというファイルが必要になるのですが、kubectlの実行のために~/.kube/configにおいていたものを、そのままkubeconfigファイルとして使いました。
次にハマったのは、同じくflexvolume driverのエラーで、「tried to connect to unsupported OCI region」というものです。調査したところ、regionのキーと名前のリストがハードコーディングされており、私が使用していたリージョンがこのリストになかったことが原因であることがわかりました。具体的には、このコミットのようにキーと名前を追加すれば良いだけです。リージョンのキー、名前はこちらのページを参照しました。
変更内容は単純ですが、ハードコードされたソースを変更するので、当然ながら再度イメージをビルドをする必要があり、それなりの手間はかかりました。このdriverのプロジェクトではwerckerという無料のCIサービスを使用していたので、ソースの変更以降のビルド作業やレジストリへのプッシュについては特に問題なくできました(リポジトリとしてはGitHub、DockerHubを使用)。あとは、flexvolume driverをインストールするためのyamlで定義している2つのDaemonsetのimageを自分でプッシュしたイメージに変更するだけです(master,worker両方)。下記のソースの「これを変更」と示した箇所です。
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: oci-flexvolume-driver-master
namespace: kube-system
spec:
template:
metadata:
name: oci-flexvolume-driver-master
labels:
app: oci-flexvolume-driver
spec:
serviceAccountName: oci-flexvolume-driver
nodeSelector:
node-role.kubernetes.io/master: ""
tolerations:
- key: node.cloudprovider.kubernetes.io/uninitialized
value: "true"
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- image: iad.ocir.io/oracle/oci-flexvolume-driver:0.8.1 → これを変更
imagePullPolicy: Always
name: oci-flexvolume-driver
securityContext:
privileged: true
volumeMounts:
- mountPath: /flexmnt
name: flexvolume-mount
- mountPath: /tmp
name: config
readOnly: true
- mountPath: /tmp2
name: kubeconfig
readOnly: true
volumes:
- name: flexvolume-mount
hostPath:
path: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/
type: DirectoryOrCreate
- name: config
secret:
secretName: oci-flexvolume-driver
- name: kubeconfig
secret:
secretName: oci-flexvolume-driver-kubeconfig
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: oci-flexvolume-driver-worker
namespace: kube-system
spec:
template:
metadata:
name: oci-flexvolume-driver-worker
labels:
app: oci-flexvolume-driver
spec:
containers:
- image: iad.ocir.io/oracle/oci-flexvolume-driver:0.8.1 →これを変更
imagePullPolicy: Always
name: oci-flexvolume-driver
securityContext:
privileged: true
volumeMounts:
- mountPath: /flexmnt
name: flexvolume-mount
volumes:
- name: flexvolume-mount
hostPath:
path: /usr/libexec/kubernetes/kubelet-plugins/volume/exec
type: DirectoryOrCreate
これで終わりかと思いきや、またエラーが発生。volumeをマウントするpodで「attach command failed, status: Failure, reason: node is missing provider id」というエラーが出ていました。ソースを検索してみるとここでこのメッセージを出していました。どうやらkubernetes APIからとってきたnodeの情報にProviderIDがないのがダメみたいです。コメントを見た感じですと、関数lookupNodeIDに渡されたnode名に対応するOCIDがこのProviderIDにセットされていることを想定しているようです。これは下記のコマンドで対応しました。
kubectl patch node k8-node -p '{"spec":{"providerID":"k8-nodeのOCID"}}'
以上の対応により、ようやくPodからOCIのブロックをアタッチしたVolumeをマウントできました。しかしながら、後で気づいたのですが、OCIのAlways Freeなブロックボリュームは、ブート・ボリューム(インスタンス生成時に自動的に作成されてアタッチするボリューム)と通常のブロック・ボリューム(PVC作成時に割り当てられる)合わせて100GBしか受け取れない、且つ50Gバイト以上のブロックストレージしか作れないので、無料枠内ではPVを作れなさそうです・・。しょうがないですね。