Kubernetesのノード障害時の挙動とノード復帰後の再スケジューリング
Kubernetes上のアプリケーションの可用性について調べていたところ、ノード障害時に気をつけたほうが良い点を見つけたので記事にしてみます。
Kubernetesのノード障害時にはノードのステータスがNotReady
になります。Kubernetesクラスタが以下の通り構成されていたとしましょう。
# kubectl get node NAME STATUS ROLES AGE VERSION test-cluster-7ajeznlcoium-master-0 Ready master 147m v1.14.0 test-cluster-7ajeznlcoium-minion-0 Ready <none> 147m v1.14.0 test-cluster-7ajeznlcoium-minion-1 Ready <none> 147m v1.14.0 test-cluster-7ajeznlcoium-minion-2 Ready <none> 147m v1.14.0
さらに、Podは次のように起動していたとしましょう。
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-6b77d6648-4jl4z 1/1 Running 0 20s 10.100.0.13 test-cluster-7ajeznlcoium-minion-0 <none> <none> httpd-6b77d6648-h7kjb 1/1 Running 0 20s 10.100.1.18 test-cluster-7ajeznlcoium-minion-1 <none> <none> httpd-6b77d6648-vd9xs 1/1 Running 0 20s 10.100.2.14 test-cluster-7ajeznlcoium-minion-2 <none> <none> nginx-7db9fccd9b-g8fjl 1/1 Running 0 26s 10.100.1.17 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-lp4fv 1/1 Running 0 26s 10.100.0.12 test-cluster-7ajeznlcoium-minion-0 <none> <none> nginx-7db9fccd9b-xdkfn 1/1 Running 0 27s 10.100.2.13 test-cluster-7ajeznlcoium-minion-2 <none> <none>
ここで、ワーカーノードtest-cluster-7ajeznlcoium-minion-2
のkubeletサービスを停止します。
(test-cluster-7ajeznlcoium-minion-2) # sudo systemctl stop kubelet
40秒ほど経つと、STATUS
がNotReady
になるはずです。
# kubectl get node NAME STATUS ROLES AGE VERSION test-cluster-7ajeznlcoium-master-0 Ready master 148m v1.14.0 test-cluster-7ajeznlcoium-minion-0 Ready <none> 149m v1.14.0 test-cluster-7ajeznlcoium-minion-1 Ready <none> 149m v1.14.0 test-cluster-7ajeznlcoium-minion-2 NotReady <none> 148m v1.14.0
ノードがNotReady
だから、Podも移動しているだろう。。。と思いませんか?
ですが。。。
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-6b77d6648-4jl4z 1/1 Running 0 3m50s 10.100.0.13 test-cluster-7ajeznlcoium-minion-0 <none> <none> httpd-6b77d6648-h7kjb 1/1 Running 0 3m50s 10.100.1.18 test-cluster-7ajeznlcoium-minion-1 <none> <none> httpd-6b77d6648-vd9xs 1/1 Running 0 3m50s 10.100.2.14 test-cluster-7ajeznlcoium-minion-2 <none> <none> nginx-7db9fccd9b-g8fjl 1/1 Running 0 3m56s 10.100.1.17 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-lp4fv 1/1 Running 0 3m56s 10.100.0.12 test-cluster-7ajeznlcoium-minion-0 <none> <none> nginx-7db9fccd9b-xdkfn 1/1 Running 0 3m57s 10.100.2.13 test-cluster-7ajeznlcoium-minion-2 <none> <none>
2分経っても移動しません。。。
実は、v1.13.0からTaintBasedEvictionというPod退避機能がデフォルトで有効になったようです。この機能ではTaintとeffect
がNoExecute
なTolerationを使って、Podを退避させます。具体的にはノードがNotReady
になると自動的にノードにtaint node.kubernetes.io/not-ready
が付くようになり、加えて全てのPodに対してデフォルトのtolerationとしてこのtaintに対するNoExecute
tolerationが付与されます。これにより当該機能を実現しています。
デフォルトではPod退避の猶予期間であるtolerationSeconds
パラメータは300秒(5分)です。このため、デフォルトでは5分経たないとPodは再スケジューリングされなかったのです。この猶予期間を短くするには、kube-apiserverの起動オプションに以下を指定します。この例では30秒にしています。
--default-not-ready-toleration-seconds=30 --default-unreachable-toleration-seconds=30
では再チャレンジ。。。
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-6b77d6648-7hf2x 1/1 Running 0 4m7s 10.100.0.14 test-cluster-7ajeznlcoium-minion-0 <none> <none> httpd-6b77d6648-dshdc 1/1 Running 0 4m8s 10.100.1.21 test-cluster-7ajeznlcoium-minion-1 <none> <none> httpd-6b77d6648-l9tkd 1/1 Terminating 0 4m7s 10.100.2.15 test-cluster-7ajeznlcoium-minion-2 <none> <none> httpd-6b77d6648-qjmfm 1/1 Running 0 12s 10.100.1.24 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-hh5gk 1/1 Terminating 0 4m1s 10.100.2.16 test-cluster-7ajeznlcoium-minion-2 <none> <none> nginx-7db9fccd9b-q9psw 1/1 Running 0 12s 10.100.1.23 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-r2td2 1/1 Running 0 4m1s 10.100.0.15 test-cluster-7ajeznlcoium-minion-0 <none> <none> nginx-7db9fccd9b-wq2hg 1/1 Running 0 4m1s 10.100.1.22 test-cluster-7ajeznlcoium-minion-1 <none> <none>
見事、30秒程度で退避できました! でもちょっとまってください。もしこの状態でノードが復活したらどうなるでしょう。
NAME STATUS ROLES AGE VERSION test-cluster-7ajeznlcoium-master-0 Ready master 173m v1.14.0 test-cluster-7ajeznlcoium-minion-0 Ready <none> 174m v1.14.0 test-cluster-7ajeznlcoium-minion-1 Ready <none> 174m v1.14.0 test-cluster-7ajeznlcoium-minion-2 Ready <none> 173m v1.14.0
Podの稼動状態はこのようになりました。
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-6b77d6648-7hf2x 1/1 Running 0 5m51s 10.100.0.14 test-cluster-7ajeznlcoium-minion-0 <none> <none> httpd-6b77d6648-dshdc 1/1 Running 0 5m52s 10.100.1.21 test-cluster-7ajeznlcoium-minion-1 <none> <none> httpd-6b77d6648-qjmfm 1/1 Running 0 116s 10.100.1.24 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-q9psw 1/1 Running 0 116s 10.100.1.23 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-r2td2 1/1 Running 0 5m45s 10.100.0.15 test-cluster-7ajeznlcoium-minion-0 <none> <none> nginx-7db9fccd9b-wq2hg 1/1 Running 0 5m45s 10.100.1.22 test-cluster-7ajeznlcoium-minion-1 <none> <none>
test-cluster-7ajeznlcoium-minion-2
には再スケジューリングされません。Podが片寄状態になってしまっています。もしこの状態でたくさんPodがのったノードが停止してしまうと。。。
という事態を防ぐためにdeschedulerというプロダクトがあります!
これはKubernetesのJobとして機能させることで、Podのスケジューリングの偏りを検知して、再スケジューリングをかけてくれます。早速導入してみます。
git clone https://github.com/kubernetes-incubator/descheduler cd descheduler/kubernetes kubectl apply -f rbac.yaml kubectl apply -f configmap.yaml cat <<EOF | kubectl apply -f - apiVersion: batch/v1beta1 kind: CronJob metadata: labels: run: descheduler-cronjob name: descheduler-cronjob namespace: kube-system spec: concurrencyPolicy: Allow jobTemplate: spec: template: metadata: name: descheduler-pod annotations: scheduler.alpha.kubernetes.io/critical-pod: "" spec: containers: - name: descheduler image: bbrfkr0129/descheduler:latest volumeMounts: - mountPath: /policy-dir name: policy-volume command: - "/bin/descheduler" args: - "--policy-config-file" - "/policy-dir/policy.yaml" - "--v" - "3" restartPolicy: "Never" serviceAccountName: descheduler-sa volumes: - name: policy-volume configMap: name: descheduler-policy-configmap schedule: '*/3 * * * *' EOF
この例ではCronJobを用いて3分ごとに再スケジューリングするようにしています。
3分後、見事偏りがなくなりました。。。!!
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-6b77d6648-7hf2x 1/1 Running 0 14m 10.100.0.14 test-cluster-7ajeznlcoium-minion-0 <none> <none> httpd-6b77d6648-dshdc 1/1 Running 0 14m 10.100.1.21 test-cluster-7ajeznlcoium-minion-1 <none> <none> httpd-6b77d6648-zvznn 1/1 Running 0 44s 10.100.2.18 test-cluster-7ajeznlcoium-minion-2 <none> <none> nginx-7db9fccd9b-hqj72 1/1 Running 0 44s 10.100.2.20 test-cluster-7ajeznlcoium-minion-2 <none> <none> nginx-7db9fccd9b-q9psw 1/1 Running 0 10m 10.100.1.23 test-cluster-7ajeznlcoium-minion-1 <none> <none> nginx-7db9fccd9b-r2td2 1/1 Running 0 14m 10.100.0.15 test-cluster-7ajeznlcoium-minion-0 <none> <none>
Rancher Submarinerを動かしてみた
3/12にRancher公式ブログで発表された、「Submariner」という新しいOSSを動かしてみましたので、軽く所感を書きます。
Submarinerってなに?
複数のKubernetesクラスタのPodネットワークおよびServiceネットワークを繋げるプロダクトです。NodePortサービスを作って外部公開せずとも、複数Kubernetesクラスタ内のPod同士で通信することが可能になります。
公式では以下の用途にマッチするとしています。
利用手順概要
ここからは私が試した手順の概要を記載いたします。といっても公式GitHubが秀逸なので、特に面白い内容ではないですが。。。
- Kubernetesクラスタを3つ準備
submarinerを試すには最低限3つのKubernetesクラスタが必要です。2つは実際に接続したいクラスタ「west」と「east」、残りの1つは「broker」という、westとeastのネットワーク情報を同期する仲介役として動きます。 クラスタ要件として以下が求められるので、ユーザが自由に制御可能なバニラKubernetesで試すのがおすすめです。
* 任意のクラスタ間でpodネットワークcidrおよびserviceネットワークcidrおよびcluster local domainが異なること * 任意のクラスタ間でインターネットを通じて直接通信できるか、同じネットワークに所属していること
- brokerクラスタに対し、以下のコマンドを実行
# helmの初期化 kubectl -n kube-system create serviceaccount tiller kubectl create clusterrolebinding tiller \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:tiller helm init --service-account tiller helm repo update # submariner-brokerのインストール SUBMARINER_BROKER_NS=submariner-k8s-broker helm install submariner-latest/submariner-k8s-broker \ --name ${SUBMARINER_BROKER_NS} \ --namespace ${SUBMARINER_BROKER_NS}
- brokerクラスタの情報を収集
SUBMARINER_BROKER_URL=$(kubectl -n default get endpoints kubernetes -o jsonpath="{.subsets[0].addresses[0].ip}:{.subsets[0].ports[0].port}") SUBMARINER_BROKER_CA=$(kubectl -n ${SUBMARINER_BROKER_NS} get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='${SUBMARINER_BROKER_NS}-client')].data['ca\.crt']}") SUBMARINER_BROKER_TOKEN=$(kubectl -n ${SUBMARINER_BROKER_NS} get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='${SUBMARINER_BROKER_NS}-client')].data.token}"|base64 --decode)
- IPSec通信するための事前共有キーの作成
SUBMARINER_PSK=$(cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1)
- westクラスタに対し、以下のコマンドを実行
# eastクラスタに対する通信のゲートウェイとなるノードにラベル付け kubectl label node <GATEWAY_NODE> "submariner.io/gateway=true" # helmの初期化 kubectl -n kube-system create serviceaccount tiller kubectl create clusterrolebinding tiller \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:tiller helm init --service-account tiller # submarinerのインストール helm install submariner-latest/submariner \ --name submariner \ --namespace submariner \ --set ipsec.psk="${SUBMARINER_PSK}" \ --set broker.server="${SUBMARINER_BROKER_URL}" \ --set broker.token="${SUBMARINER_BROKER_TOKEN}" \ --set broker.namespace="${SUBMARINER_BROKER_NS}" \ --set broker.ca="${SUBMARINER_BROKER_CA}" \ --set submariner.clusterId="west-cluster" \ --set submariner.clusterCidr="<westクラスタのpod network cidr>" \ --set submariner.serviceCidr="<westクラスタのservice network cidr>" \ --set submariner.natEnabled="false"
- eastクラスタに対し、以下のコマンドを実行
# westクラスタに対する通信のゲートウェイとなるノードにラベル付け kubectl label node <GATEWAY_NODE> "submariner.io/gateway=true" # helmの初期化 kubectl -n kube-system create serviceaccount tiller kubectl create clusterrolebinding tiller \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:tiller helm init --service-account tiller # submarinerのインストール helm install submariner-latest/submariner \ --name submariner \ --namespace submariner \ --set ipsec.psk="${SUBMARINER_PSK}" \ --set broker.server="${SUBMARINER_BROKER_URL}" \ --set broker.token="${SUBMARINER_BROKER_TOKEN}" \ --set broker.namespace="${SUBMARINER_BROKER_NS}" \ --set broker.ca="${SUBMARINER_BROKER_CA}" \ --set submariner.clusterId="east-cluster" \ --set submariner.clusterCidr="<eastクラスタのpod network cidr>" \ --set submariner.serviceCidr="<eastクラスタのservice network cidr>" \ --set submariner.natEnabled="false"
- westクラスタ上にnginx deploymentとserviceを作る(動作確認)
kubectl run nginx --image nginx kubectl expose deploy nginx --port 80
- 作成したサービスのIPを取得(動作確認)
NGINX_SERVICE_IP=$(kubectl get svc nginx -o jsonpath="{.spec.clusterIP}")
kubectl run busybox --restart=Never --image busybox:1.28 -- sleep 3600 kubectl exec -it busybox -- wget -q -O - ${NGINX_SERVICE_IP}
無事、nginxのスタートページが表示されればOKです。
まとめ
submarinerをざっと試してみましたが、そもそもKubernetesクラスタはプロジェクトやチームなど意味あるかたまりで分割されるであろうことから、そもそもpodネットワークやserviceネットワークをつなぎたい需要ってそんなにあるのかな? と疑問に思っています。 Rancherの提示している使い方以外に使えるとすれば、PVのクラスタ間移行なんかには便利に使える気はしますね。
現時点ではクラスタ超えのDNSには対応していないので、クラウドネイティブに使うとなるとちょっと物足りないところ。今後の発展に期待です!
Kubernetesの認定資格「CKA」を取得しました 〜これから受ける方へのアドバイス〜
この度、Kubernetesの認定資格「CKA(Certified Kubernetes Administrator)」を取得いたしました! 今後受ける方のお役に立てばと、受験レポートを残しておきます。
要約 〜忙しい人はここだけ読んでね〜
- 試験に関する説明の書かれている英語のドキュメント「Handbook」「Important Tips」を熟読しよう!
- 「Kubernetes完全ガイド」の4章から8章は可能な限り、9章から12章は余力と相談して押さえておこう!
- 「Kubernetes The Hard Way」を1度実施し、各オペレーションの意味を理解しておこう!
受験動機
私の場合は、今まで培ってきたKubernetesの技術知識を試すことが目的でした。また副次的な目的で、社内でコンテナ技術推進活動をしているため、その発言に説得力をもたせる意味もありました。
「Kubernetesの技術を身につけるきっかけにする」というパターンの方もいるかと思います。
自分がやったこと
自分が受験をするぞ!と決めてやったことは以下の4つです。私の場合は既にKubernetesを結構触ってしまっていたので役に立つかわからないですが、かけた時間を括弧付けで示しておきます。
- パスポート取得(1週間)
- 貸し会議室押さえ(30分)
- 試験のHandbookとImportant Tipsに目を通す(6時間くらい)
- Kubernetesの機能復習(8時間くらい)
パスポート取得
CKAを受験するには氏名がローマ字記載の写真付き身分証明書が必要になります。おそらくパスポート一択な気がします。
私は国際旅行等で海外に出たことが一回もなく、人生で初めてパスポートを取得しました。まさか資格試験のためにパスポートを取得することになろうとは…
パスポートの発行は私の居住する地域だと1週間程度かかりました。もし受験する熱意があるけどお持ちでない方は、受験予定日を決めたら早めに取得することをおすすめします。
ちなみに発行料は5年パスポートで1万円程度でした。
貸し会議室押さえ
CKAの受験は準備も含めて3時間半程度、誰もいない、誰も入ってこない、静かで、机・椅子以外ほぼ何もないスペースが必要になります。
私の場合は会社の会議室で試験を実施するのが難しい事情があったため、貸し会議室を予約し、受験することにしました。
試験日のスケジューリングをしてから空いている会議室を探すのが良いと思います。2000円くらいあれば借りられると思います。
試験のHandbookとImportant Tipsに目を通す
CKAは国際試験なので現状、試験に関する情報は英語になります。ですので、試験のレギュレーションを理解する時間をしっかり取りました。
Kubernetesの機能復習
先人の方々が残してくれた受験記はチラ見していましたが、なかなか実際の問題のイメージが膨らまなかったので、今まで触ってきた機能の復習は少々行いました。
具体的にはCyberAgentの青山さんが書いた本「Kubernetes完全ガイド」の4章〜12章くらいを見直したことと、Kubernetesクラスタをスクラッチで構築するハンズオン「 Kubernetes The Hard Way」を通しで1回、やり直してみました。
試験当日の対応
試験日当日の流れは以下のようになります。
Webカメラ有効化・デスクトップ共有
試験当日は予約した会議室で試験サイトにアクセスし、試験官と英語のチャットでやり取りする形になります。まずはじめにWebカメラを有効化し、デスクトップを有効化するよう、指示されました。
パスポート提示
Webカメラが有効になったら、カメラ経由でパスポートの顔写真が写っているページを試験官に見せます。ここはすんなり通りました。
受験部屋の状態確認
次に試験を受験する部屋を一周くまなくカメラで見せるように指示があります。この際、特に机上を注意してみているようで、机上をゆっくり見せてほしいと指示がありました。
Webカメラ位置調整
試験中は基本的に休憩を申し出ない限りは、受験用端末のスクリーンを見続けていなければなりません。
顔がきれいに試験官に見えていなかったのか、Webカメラを少し下げるように指示されました。ディスプレイに付属のカメラを使う人は辛いディスプレイの角度で受験することになるかもしれないです。私は許容範囲でしたが…
許容できなければ外部Webカメラがあるとよいかも…?!
受験開始
以上の準備を終えて、試験官が試験ページを更新し、問題文とコンソールが表示されました。そしたら試験開始です。開始の合図は特にありませんでした。
試験に受かるために必要だと思うこと
私が考えるCKA合格に必要なことは以下の5点になります。
- 試験レギュレーションの事前理解
- kubectlの便利機能の知識
- 基本的なKubernetesオブジェクトの知識
- Kubernetesクラスタの運用の知識
- ツールに頼らないKubernetesクラスタの構築知識
試験レギュレーションの事前理解
これが大前提になります。試験のルールは全て英語で記載されたHandbook、Important Tipsしかないので、抵抗がある方もいらっしゃるかと思いますが、禁止事項を理解しておかないと即時試験終了されてしまう恐れがあるので、HandbookとImportant Tipsの中身はよく理解するようにすると良いです。
特に、試験中に参照できるWebページの制限(k8sの公式ドキュメント、ブログ、公式GitHubのみ)には注意が必要です。Kubernetes公式ドキュメントからドキュメント検索をかけると、外部ページも引っかかるため、不意にそれらのページリンクをクリックしてしまうと不正行為になります。また、表示できるタブの数(試験サイト以外、一つのみ)も注意が必要なルールの一つです。
kubectlの便利機能の知識
kubectlを使ってオブジェクトの名前だけをリストしたり、特定のラベルを持つオブジェクトだけを検索したりする等、普段使っていない方もkubectlの効率的な使い方は理解しておいたほうが良いでしょう。青山さんの本「Kubernetes完全ガイド」だと4章を網羅的に理解しておけば十分でしょう。
基本的なKubernetesオブジェクトの知識
Deployment、ReplicaSet、Pod、各種Serviceなどの本当に基本的なオブジェクトの作り方から、マニフェストの書き方はしっかり押さえておくべきでしょう。マニフェストのサンプルはKubernetes公式ドキュメントから引っ張ってこれますし、kubectl run --dry-run -o yaml
でも確認できるので、ゼロから作るというよりかは各パラメータの意味を理解してカスタマイズできるかが重要になります。
なおその他のオブジェクトについても、私が受けた感覚では「Kubernetes完全ガイド」の5章〜8章は可能な限り押さえ、余力があれば9章、10章を理解しておくぐらいで十分かなと思います。
Kubernetesクラスタの運用の知識
Kubernetesを運用するための知識として、「Kubernetes完全ガイド」の11章の知識は押さえておきましょう。またnode selector
やtaint
、toleration
などを用いたPodスケジューリングは覚えておいて損はないと思います。
ツールに頼らないKubernetesクラスタの構築知識
「kubeadm」などのツールを用いてしかKubernetesクラスタを構築したことがない方はぜひ一度Kubernetes The Hard Way」を1度実施してください。そしてオペレーションの一つ一つの意味を理解しておくほうが良いです。
結果 〜スコアと証書〜
上記準備で、無事CKAを取得することができましたが、スコアは80%と、合格ライン74%と比較してギリギリでした。一問以外はわからない問題ではなかったのですが、失点の理由が知りたいのがホンネ…
本レポートが今後CKAを受験する方のお役に立つことを願っております!
KubernetesのHorizontalPodAutoscalerでハマった話
ふとしたきっかけで、deploymentを以下のように定義していたわけです。
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: v1 name: nginx spec: replicas: 1 selector: matchLabels: app: v1 strategy: {} template: metadata: creationTimestamp: null labels: app: v1 spec: containers: - image: nginx name: nginx resources: requests: cpu: "100m" memory: 128Mi limits: cpu: "100m" memory: 256Mi
このマニフェストによるdeployment、kubectl autoscale
でもhpaが機能しません!
以下のように、app
ラベルはapp: <deployment名>
である必要があるみたいです。。。
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: nginx name: nginx spec: replicas: 1 selector: matchLabels: app: nginx strategy: {} template: metadata: creationTimestamp: null labels: app: nginx spec: containers: - image: nginx name: nginx resources: requests: cpu: "100m" memory: 128Mi limits: cpu: "100m" memory: 256Mi
今日、1/3日ぐらいハマってしましました。。。
OpenStack Swiftの既存リングを拡張する
OpenStack Swiftで新しくストレージノードを構築し、既存リングを拡張する機会がありましたので、その際の手順をメモしておきます。
手順
proxyノードで以下のコマンドを叩きます。
# accountリングの拡張 swift-ring-builder account.builder add r<region num>z<zone num>-<storage node ip>:6202/<device name> 100 swift-ring-builder account.builder rebalance # containerリングの拡張 swift-ring-builder container.builder add r<region num>z<zone num>-<storage node ip>:6201/<device name> 100 swift-ring-builder container.builder rebalance # objectリングの拡張 swift-ring-builder object.builder add r<region num>z<zone num>-<storage node ip>/<device name> 100 swift-ring-builder object.builder rebalance
region番号とzone番号は既存リング情報から得ることができます。以下コマンドからどうぞ。
swift-ring-builder account.builder swift-ring-builder container.builder swift-ring-builder object.builder
Portusでプライベートなコンテナイメージレジストリを立ててみる
Dockerを使う際にまずお世話になるのがパブリックなコンテナイメージレジストリサービスであるDocker Hubかと思うのですが、業務で本格的にDockerを使うとなった際にはクローズドなネットワークセグメントでしかDockerホストを実行できなかったりして、プライベートなイメージレジストリが欲しくなりますよね?
そんなときのためにDocker側でプライベートレジストリサーバ用のコンテナイメージが用意されているのですが、UIがないのでイメージを一覧や削除などの管理をしようとすると、直にAPIを叩かなくてはならず辛みがあります。そこでUI付きのプライベートレジストリを探すと、プライベートGitリポジトリでも有名なGitLabが引っかかるのですが、あれってリポジトリだけでなく、CIやKubernetes連携など、いろいろな機能がついているじゃないですか。そこまで仰々しくしたくないんですよね…
シンプルにレジストリとUIと認証周りの機能がついていればいいのよ! というモチベーションで再度探すと、ありました! PortusというOSSが…!
が、例のごとく構築に少しつまずいたので、構築・設定方法を残しておきたいと思います。今後Portusを構築する人のために、つまづきポイントを共有できればと思います。
Portusとは
SUSEが開発している、プライベートレジストリに認証機能とWeb UIを提供するOSSです。 ざっくりとどんなものか知りたい方はCyberAgentの青山さんが記事にしてくれているので、そちらをご参照。
構築手順
では、本題に入っていきましょう。
前提
まず前提を示していきますが、以下のとおりです。
- OS: CentOS 7
- IP: 192.168.0.100
- 作業ユーザ: root
- 構築方法: docker-composeを利用
PortusもDockerコンテナで動作するので、パッケージの導入手順くらいを置き換えていただければ、Dockerが動作するどんなLinuxでも動作するかと思います。
Dockerのインストール
MinimalのCentOS 7のマシンにログインしたら、まずDockerをインストールします。
yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io
インストールしたらサービスを設定しましょう。
systemctl enable docker systemctl start docker
docker-composeのインストール
docker-composeを使ってPortusを構築するので、インストールしておきます。
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/bin/docker-compose chmod +x /usr/bin/docker-compose
Gitのインストール
Portusのソースコードの中にサンプルのdocker-compose.ymlが入っています。これをDLするためにGitをインストールします。
yum install -y git
Portusのソースコードを取得
Portusのソースコードを取得します。ソースコードは/etc/docker/
に置くことにします。
cd /etc/docker git clone https://github.com/SUSE/Portus.git
今回はVer 2.4をデプロイしたいと思います。
cd Portus git checkout remotes/origin/v2.4 git checkout -b v2.4
以降の作業は/etc/docker/Portus/example/compose
で行いますので、移動しておきます。
cd /etc/docker/Portus/examples/compose
証明書群の作成
PortusのWeb UIおよびプライベートレジストリに使われるSSL証明書を作成します。今回は自己署名認証局(CA)を同ホスト内に作り、証明書を発行します。
まずはCAを作成します。
cd secrets openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -subj "/CN=192.168.0.100" -days 7305 -out ca.crt
次にサーバの秘密鍵を作ります。
openssl genrsa -out portus.key 2048
CSRを発行して、CAで署名付き証明書を作ります。
cat <<EOF > csr.conf [ req ] default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [ dn ] C = JP ST = Tokyo L = Shinjuku #O = <organization> #OU = <organization unit> CN = 192.168.0.100 [ req_ext ] subjectAltName = @alt_names [ alt_names ] IP.1 = 192.168.0.100 [ v3_ext ] authorityKeyIdentifier=keyid,issuer:always basicConstraints=CA:FALSE keyUsage=keyEncipherment,dataEncipherment extendedKeyUsage=serverAuth,clientAuth subjectAltName=@alt_names EOF openssl req -new -key portus.key -out portus.csr -config csr.conf openssl x509 -req -in portus.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out portus.crt -days 10000 -extensions v3_ext -extfile csr.conf cd ..
.envファイルの修正
docker-composeに読み込ませる.env
ファイルを修正します。
cat <<EOF > .env MACHINE_FQDN=192.168.0.100 SECRET_KEY_BASE=$(openssl rand -hex 64) PORTUS_PASSWORD=password DATABASE_PASSWORD=p@ssw0rd EOF
nginx.confの修正
nginxの設定ファイルnginx.conf
内のserver_name
パラメータを修正します。
sed -i 's/server_name.*/server_name 192.168.0.100;/g' nginx/nginx.conf
initスクリプトの修正
今回作成したCAをプライベートレジストリに信頼させるため、以下の記述をregistry/initに追加します。
#!/bin/sh set -x cp /secrets/portus.crt /usr/local/share/ca-certificates # here!! cp /secrets/ca.crt /usr/local/share/ca-certificates update-ca-certificates registry serve /etc/docker/registry/config.yml
Portusの起動
あとはdocker-composeで起動するだけです。
docker-compose up -d
以上で、構築は完了です。あとはPortus上で初期設定をしていきましょう。
設定手順
Portus上で設定を行うためにhttps://192.168.0.100
にアクセスしましょう!
管理ユーザ設定
まずは管理ユーザであるadmin
ユーザを作ります。
レジストリサーバ登録
レジストリサーバ登録をして…
ユーザ作成
一般ユーザbbrfkr
を作ります。
再ログイン
そして再ログイン。
実際にイメージをプッシュしてみる
作ったレジストリサーバにログインするためにはCA証明書をクライアントマシンの適切な場所に置く必要があります。以下の場所に置きましょう。
/etc/docker/certs.d/192.168.0.100/ca.crt
その後レジストリサーバにログインして、nginx:latestをプライベートレジストリにプッシュしてみます。
docker login 192.168.0.100 docker pull nginx:latest docker tag nginx:latest 192.168.0.100/bbrfkr/nginx:v1.0 docker push 192.168.0.100/bbrfkr/nginx:v1.0
プッシュが成功すると、Portus上で確認できるようになります。
KubeVirtを使ってみる
KubeVirtをお試ししてみたので、その際のメモを残しておきます。
KubeVirtとは
KubeVirtはRed Hat社が主に開発している、CNCF MemberプロダクトのOSSです。KubeVirtを使うとKubernetes上で仮想マシンを管理することができるようになります。これによりコンテナよりも隔離レベルの高い仮想マシンを使いながら、KubernetesのCloud Nativeな機能であるPodネットワーク、Service、サービスディスカバリを仮想マシンに対し適用することができるようになります。
KubeVirtのインストール
インストールは至極簡単です。Kubernetesクラスタを用意したら、以下のコマンドを実行するだけです。
export VERSION=v0.11.0 kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/$VERSION/kubevirt.yaml
もし、Workerノードが仮想化支援機構(IntelでいうVT-x)に対応しておらず、ハードウェアエミュレーションを使いたい場合は、事前に以下のコマンドを実行します。
kubectl create configmap -n kube-system kubevirt-config --from-literal debug.useEmulation=true
KubeVirtを使ってみる
KubeVirtで仮想マシンを操作するには、専用のCLIツールであるvirtctl
があると便利です。virtctl
をインストールしましょう。
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/$VERSION/virtctl-$VERSION-linux-amd64 chmod +x virtctl sudo chown root:root virtctl sudo mv virtctl /usr/bin
インストールしたらまずは超軽量OSであるCirrOSを起動してみましょう。仮想マシンオブジェクトをKubernetes上に作成します。
kubectl apply -f https://raw.githubusercontent.com/kubevirt/demo/master/manifests/vm.yaml
kubectl get vm
コマンドを実行すると、以下のような出力が得られると思います。
NAME AGE RUNNING VOLUME testvm 6s false
RUNNIG
がfalse
とあるように、この時点ではまだ仮想マシンは起動していません。起動にはvirtctl
コマンドを使って以下のようにします。
virtctl start testvm
すると、RUNNING
がtrue
に変わります。
NAME AGE RUNNING VOLUME testvm 1m true
実は仮想マシンオブジェクトはコンテナで言うReplicaSet
のようなもので、仮想マシンの起動定義情報が格納されているだけです。実際の仮想マシンのポインタ情報は「仮想マシンインスタンスオブジェクト」に格納されています。仮想マシンインスタンスオブジェクトを一覧するにはkubectl get vmi
コマンドを実行します。
NAME AGE PHASE IP NODENAME testvm 3m Running 10.100.0.6 kubevirt-test-jbsl2irizcpz-minion-1
すると、上記のように仮想マシンの現在の状態やIP情報などを手に入れることができます。
仮想マシンのコンソール画面(CUIベース or GUIベース)にアクセスするには、またvirtctl
コマンドを使います。
virtctl console testvm
virtctl vnc testvm
しかし、Cloudイメージを使う場合、通常パスワード認証できないので、これらのコマンドを使う機会は少ないでしょう。
仮想マシンにSSHログインするにはコンテナと同様に仮想マシンを Service で外部公開する必要があります。これもvirtctl
コマンドで簡単に行なえます。
virtctl expose vm testvm --name testvm --port 22 --type NodePort
kubectl get svc -o wide
すると、確かにNodePort
Serviceが作成されてますね!
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 18m <none> testvm NodePort 10.254.20.5 <none> 22:30296/TCP 64s kubevirt.io/domain=testvm,kubevirt.io/size=small
どこかのNodeの30296
番ポートをsshで突っついてみましょう。ユーザ名はcirros
、パスワードはgocubsgo
です。
ssh -p 30296 cirros@<NodeのIP> $
無事ログインできましたか?
仮想マシンの停止にもvirtctl
を使います。
virtctl stop testvm
仮想マシンの削除にはkubectl delete vm
コマンドです。これを実行すると該当の仮想マシンインスタンスも一緒に消えます。
kubectl delete vm testvm
最後に
いかがでしたでしょうか。仮想マシンの隔離レベルを担保しなくてはいけないけれど、コンテナは使えないといったケースや、コンテナネットワークに仮想マシンを直接繋いでレイテンシを改善したい場合などに使えそうかなと思っています。 時間を見つけて任意の仮想マシンイメージを起動する方法も書こうと思いますので、お楽しみに!