目次
- 概要
- 講座の構成
- 1. Introduction
- 2. Core Concepts
- 3. Scheduling
- 4. Logging & Monitoring
- 5. Application Lifecycle Management
- 6. Cluster Maintenance
- 7. Security
- 8. Storage
- 9. Networking
- DNS on Linux
- Core DNS
- Pre-requisite - Network Namespace
- Linux Bridge
- Pre-requisite - Docker Networking
- Pre-requisite - CNI (Container Network Interface)
- Cluster Networking
- Important Note about CNI and CKA Exam
- Pod Networking
- CNI in Kubernetes
- Practice Test - Deploy Network Solution
- IP Address Management (IPAM)
- Service Networking
- DNS in kubernetes
- CoreDNS in Kubernetes
- Ingress
- IPアドレスレンジの確認方法
- Design and Install a Kubernetes Cluster
- Install Kubernetes the kubeadm way
- End to End Tests on a Kubernetes cluster
- Troubleshooting
- Other Topics
- 個人メモ
概要
- Kubernetesの試験の1つであるCertified Kubernetes Administrator (CKA)を勉強中。
- Udemyで「Certified Kubernetes Administrator (CKA) with Practice Tests」を受講。
- 忘備のため勉強した内容をメモする
講座の構成
- Introduction
- Core Concepts
- Scheduling
- Logging & Monitoring
- Application Lifecycle Management
- Cluster Maintenance
- Security
- Storage
- Networking
- Design and Install Kubernetes
- Install "Kubernetes the kubeadm way"
- End to End Tests on Kubernetes Cluster
- Troubleshooting
- Other Topics
- Lightning Labs
- Mock Exams
- Course Conclusion
講座構成順に重要と思われるところをメモしていく。
1. Introduction
スキップ。
2. Core Concepts
- Cluster Architecture
- Master Node
- ETCD
- Kube-API Server
- Kube Controller Manager
- Kube Scheduler
- ReplicaSet
- Deployment
- Namespace
- Service
- Woker Node
- Kubelet
- Pod
- Kube Proxy
↑のうち、いくつかの役割を簡単にメモ。
Master Node
Kuberntesクラスタの管理やインターフェースを司るノード
Woker Node
Kuberntesクラスタでアプリケーション処理を行うノード
ETCD
ETCD自体は分散型のKey-Valueストア。KubernetesではPodやNodeなどの情報をETCD上に保管している。kubectl getコマンドによる情報取得元はすべてETCDサーバ。
Kube-API Server
Kubernetesリソース間でやり取りを行うためにAPIインターフェースを提供するサーバ。いまいちKube-APIが何をしているのかわかりにくいので、Pod作成時のKube-APIサーバの動きを見てみる。
・Kube-API Serverの動作~Pod作成
- Pod作成のAPIリクエストを受信。ユーザ認証する
- APIリクエストのバリデーション
- Pod作成 ※Kube-APIサーバがPodを作成する。ここでは未だNodeはアサインしない
- ETCDサーバにPod作成を通知。ETCDサーバがデータ更新。Kube-APIサーバがPod作成をユーザに通知
- Kube Schedulerが定期的にKube-APIサーバをモニタしていて、新たなPodにNodeがアサインされていないことに気づく。で、Kube SchedulerがPodをどのNodeに配置するか決定し、Kube-APIサーバに通知。
- Kube-APIサーバがPodがどのNodeに配置されるか決定されたことをETCDサーバに通知。
- さらにKube-APIサーバはPod配置先のNodeにいるKubeletにPodの配置を伝える。
- KubeletはPodをNode上に作成し、コンテナランタイム(Docker)にアプリケーションイメージをデプロイするよう指示する。
- それが完了したら、KubeletはKube-APIサーバにPodが配置されたことを通知。それを受けて、Kube-APIサーバがETCDサーバに通知。ETCDサーバがデータ更新。
Kube Controller Manager
コンテナコントローラーやノードコントローラといった複数のコントローラーを管理するサーバ。コントローラ自体は、①継続的にステータスをチェックして、②復旧措置を行う。
・コントローラの一例~Node-Controllerの動作
Node-Controllerは、例えばノードの状態を5秒毎に確認する。ノードからのハートビートが40秒間止まりっぱなしだとノードをUnrechableとしてマークする。Unrechableの状態が5分間治らない場合、そのノードに配置していたPodを削除して正常なノードに配置する。
Kube Scheduler
PodをどのNodeに配置するかを決定するサーバ。※Node上にPodを配置するのはKubeletの役目。
Kube-Proxy
KubernetesクラスタではすべてのPodが他のすべてのPodに到達可能だけれど、これはPod間ネットワークがあるため。Pod間ネットワークはすべてのノード間にわたる仮想ネットワーク。Kube-proxyは各Node上で稼働しているプロセスで新たなServiceが作成される度に他のノードに転送するためのルール(IPテーブルなど)を作成する。
3. Scheduling
Kube SchedulerはPodをどのNodeに配置するかを決定するサーバ。※Node上にPodを配置するのはKubeletの役目。ではどうやってPodの配置を決定するのか。以下の仕組みでスケジューリングを行う。1つずつ概要をメモしていく。
- Taints & Tolerations
- Node Selectors
- Node Affinity
- Resource Requirements & Resource Limits
- 特殊な例
- Daemonsets
- Static Pod
- Multiple Scheduler
Taints & Tolerations
英語でTaintsは「汚れ」、Tolerationsは「寛容」という意味。だが、それだとちょっとわかりにくい。「虫よけスプレー」と「防護マスク」に例えるとわかりやすい。
Nodeに「Taints」という虫よけスプレーを振りかける。すると「虫=Pod」はNodeに近づけない=配置されない。でも虫よけスプレー(Taints)に耐えられるようPodに「防護マスク」をつけてやる(虫に防護マスクなんてつけられないけどそこは気にしないでください)。そうするとPodはNodeに近づける=Nodeに配置可能になる。
- Taints:Node側の設定
kubectl taint nodes <ノード名> app=batch:NoSchedule # Taintsを設定 kubectl taint nodes <ノード名> app=batch:NoSchedule- # Taintsを解除
- Tolerations:Pod側の設定
spec: tolerations: - key: "app" operator: "Equal" value: "batch" effect: "NoSchedule"
これでTolerationsで指定していないPodはNodeに配置されなくなる。
エフェクトにはNoScheduleも含め3種類ある。PreferNoScheduleは要件を満たすNodeが他になければ、Podを配置可能。NoExecuteは既にNodeにスケジュールされているPodであっても、PodにTolerationsが設定されてない限りNodeから削除される。
- NoSchedule
- PreferNoSchedule
- NoExecute
Node Selectors
Node上へのPodのスケジューリング | Kubernetes
Taints & Tolerationsは、Podが特定のNodeに配置されないよう指定する仕組みだったのに対し、Node SelectorsはPodを特定のNodeに配置する仕組みである。Labels & Selectorsを使ってPodを配置するNodeを指定する。NodeのLabels設定でKey-Valueを指定し、PodのSelectors設定でNodeに設定したKey-Valueを指定してやる。
- Lables:Node側の設定
kubectl label nodes <ノード名> Size=Large
- Selectors:Pod側の設定
spec: nodeSelector: Size: Large
これで指定したNodeにのみPodが配置される。
Node Affinity
Node上へのPodのスケジューリング | Kubernetes
Node AffinityはNode Selectorsと同じくPodを特定のNodeに配置する仕組みである。違いは、より複雑な配置ルールを作成できる点である。例えばNOTやORといった条件を指定できる。
- Lables:Node側の設定
kubectl label nodes <ノード名> Size=Large
- nodeAffinity:Pod側の設定
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: Size operator: In values: - Large - Small
これで条件に該当するNodeにのみPodが配置される。
Node Affinity Typesには「requiredDuringSchedulingIgnoredDuringExecution」含め3種類存在する。
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
- requiredDuringSchedulingRequiredDuringExecution
Resource Requirements & Resource Limits
Resource RequirementsではPodが必要とするリソース(CPU、メモリ)をYAMLファイルで設定することによりリソースを確保したNodeにのみPodが配置されるようになる。一方、Resource LimitsはPodが使用するリソースの上限値を設定しPodによるリソースの占有を防ぐことができる。CPUをResource Limitsを超えて利用しようとした場合にはスロットルされる。一方、メモリをLimitsを超えて継続的に利用しようとした場合にはPodは削除される。
- Resource Requirements & Resource Limits:Pod側の設定
spec: containers: - name: app image: images.my-company.example/app:v4 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m"
Daemonsets
Daemonsetsは新たなNodeが追加される度に自動でPodを配置する。DaemonsetsはすべてのNodeに1つずつPodを配置する。DaemonsetsのユースケースとしてはNodeの監視などが考えられる。各Nodeに配置されたDaemonsets PodによってNodeを監視する。Daemonsetsを利用すれば、NodeにPodを配置する、あるいはNodeからPodを削除することを気にしないでよい。ほかにもKube Proxyを各Nodeに配置することも1つのユースケースとして考えられる。
DaemonsetsのYAML定義ファイルはReplicasetsに似ている。
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
DaemonsetsでどのようにPodをNodeに配置しているのだろうか。Nodeの配置には現在Node AffinityとデフォルトのKube Schedulerを利用している。詳細は割愛。
Static Pod
Masterノードで稼働しているKube APIサーバ、Kube Scheduler、Kube Controller、ETCDクラスタがすべて無くなってしまった(存在しない)状況を想定する。つまり、Worker NodeとWorker Node上に存在するKubelet、Dockerコンテナ(あるいは他のコンテナランタイム)しか存在してないとする。その場合PodをNode上で稼働させることは可能だろうか。実はその状況でもPodを稼働させるができる。そのようなPodをStatic Podと呼ぶ。
Kube APIサーバがないので、当然APIでPodを作成することはできない。では、どうするのか。PodのYAML定義ファイルをWorker Node上の指定されたフォルダに入れてあげればよい。Kubeletは定期的にフォルダを見に行って、Podの定義ファイルが存在したらそれに基づいてPodを作成する。もちろんこのやり方ではReplicasetsやDeploymentsは作成することはできない(Replication-ControllerやDeployment-Controllerが存在しないため)。
Kubeletにコマンドでフォルダの場所を知らせるので、フォルダは任意の場所でよい。
フォルダの居場所を調べるにはps -ef | grep kubelet
というコマンドを使うとよい。
ユースケースとしてはStatic Podを使って、Kube APIサーバやETCDクラスタをMaster Nodeに配置することが考えられる。実際KubeadmではそのようにPodを配置している。
Static Podはkubectl get pods
で確認することができるがkubectl
で削除やエディットはできないことに注意。
Multiple Scheduler
Configure Multiple Schedulers | Kubernetes
Kubernetesでは独自のカスタムSchedulerを作成することができる。カスタムSchedulerはデフォルトのSchedulerと一緒に稼働することができる。
Kube Schedulerのバイナリをダウンロードしてオプションで独自の--scheduler-name
を設定すれば、カスタムスケジューラを作成することができる。
Kubeadmの場合だと、PodとしてKube Schedulerを稼働させている。Podの- command:
欄で下記3点を指定してあげればよい。
- --leader-elect=true - --scheduler-name=my-custom-scheduler - --lock-object-name=my-custom-scheduler
マルチマスタ構成でない場合は--leader-elect=false
とする。一方、マルチマスタ構成の場合は--leader-elect=true
として--lock-object-name=my-custom-scheduler
も設定する。
- Scheduler用のPod設定
apiVersion: v1 kind: Pod metadata: name: my-custom-scheduler namespace: kube-system spec: containers: - command: - kube-scheduler - --address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --scheduler-name=my-custom-scheduler - --lock-object-name=my-custom-scheduler image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3 name: kube-scheduler
- スケジュールされる側のPod設定
schedulerName
でカスタムスケジューラ名を設定してやる。
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - image: nginx name: nginx schedulerName: my-custom-scheduler
4. Logging & Monitoring
Monitoring
2018年時点でビルトインのモニタリングツールはK8sにはない。代わりにオープンソースのモニタリングソースが色々ある。例えば、PrometheusやElastic Stack、DATADOG、dynatraceなど。メトリクスサーバはその1つ。
Resource metrics pipeline | Kubernetes
メトリクスサーバではノードやポッドからメトリクスを収集しアグリゲートする。クラスターあたり1つのメトリクスサーバが設定可能。
メトリクスサーバではKubelet内にcAdvisor=Container Advisorを設置する。cAdvisorがPodからパフォーマンスデータを集めてKube-APIを通じてメトリクスサーバに渡す。メトリクスサーバはインメモリに収集したメトリクスのデータを保管する。なので、デスクトップに保存したい場合はメトリクスサーバ以外のソリューションを利用しなければならない。
メトリクスサーバの導入手順
- gitからクローンして、
kubectl create
コマンドでメトリクスサーバを作成(DeploymentやService、clusterroleなどが稼働) kubectl top node
、またはkubectl top pod
コマンドでCPU・メモリ使用率を確認
git clone https://github.com/kodekloudhub/kubernetes-metrics-server.git cd kubernetes-metrics-server/ kubectl create -f . kubectl top node
Logging
kubectl logs -f <pod名> <コンテナ名>
でライブストリームログが見られる
5. Application Lifecycle Management
Rollout and Rollback
Rollout
RolloutはDeploymentのコンテナイメージをバージョンアップすること。Rolloutするたびに新たなDeployment Revisionが作成される。
Deploymentの方法(Deployment Strategy)は2種類ある。
- Recreate
- Rolling Update
RecreateはいったんPodをすべて削除し、その後Podを作成する。そのためApplicationのダウンタイムが発生する。一方Rolling Updateは1つのPodを削除し代わりに新たなPodを1つ作成してから次のPodに移るのでダウンタイムが発生しない。デフォルトはRolling Update。
Deployment Strategyはkubectl describe deployment
コマンドで確認できる。StrategyType:
にRecreate
あるいはRollingUpdate
と表示される。
Rolling Updateは、DeploymentのYAML定義ファイルでコンテナイメージの新バージョンを設定して、下記コマンドを実行することで開始される。
kubectl apply -f <YAMLファイル>
他の方法として、下記コマンドでも同様にRolloing Updateが行われる。
kubectl set image deployment/<デプロイメント名> <コンテナ名>=nginx:1.9.1
Deploymentの進捗および履歴は下記コマンドで確認できる。
- DeploymentのRollout状況確認(m個中l個のReplicaがアップデートされた)
kubectl rollout status deployment/myapp-deployment
- Revisionの変更履歴確認
kubectl rollout history deployment/myapp-deployment
Rollback
新たなRevisionに問題があった場合にはRollbackすることができる。下記コマンドで以前のRevisionにただちにRollbackできる。
kubectl rollout undo deployment/myapp-deployment
Configure Applications
- Configuring Command and Arguments on applications
- Configuring Environment Variables
Commands and Arguments
Define a Command and Arguments for a Container | Kubernetes
Pod YAML定義ファイルでcommand:
欄にJson配列フォーマットでコンテナ起動時に実行する(エントリポイント)コマンドを記述できる。args:
欄で実行するコマンドの引数を指定できる。
spec: containers: - name: ubuntu-sleeper image: ubuntu-sleeper command: ["sleep"] args: ["10"]
Environment Variables
環境変数の設定は3種類の方法がある。
- Plain Environment Variables
- ConfigMaps
- Secrets
Plain Environment Variables
env:
フィールドで環境変数を定義できる。
spec: containers: - name: image: env: - name: value:
ConfigMaps
CongigMapとしてまとめて環境変数を扱えるようにして、管理を楽にする。次の2ステップでConfigMapをPodに適用する。
- ConfigMap作成
- Pod設定でConfigMapを割り当て
ConfigMap作成
3つのやり方がある。
①Key-Valueからコンフィグマップを作成する。
kubectl create configmap \ <コンフィグマップ名> --from-literal=<キー>=<バリュー>
②ファイルをもとにコンフィグマップを作成する。
kubectl create configmap \ <コンフィグマップ名> --from-file=<ファイルのパス>
ファイルは下記のフォーマットで作成しておく。
APP_COLOR: blue APP_MODE: prod
③YAMLでConfigMapの定義ファイルを作成し、コンフィグマップを作成。
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_COLOR: blue APP_MODE: prod
YAML定義ファイルをもとにコンフィグマップを作成。
kubectl create -f config-map.yaml
Pod設定でConfigMapを割り当て
PodでConfigMapを指定してやる。これでコンフィグマップで定義した環境変数を扱えるようになる。
spec: containers: - name: image: envFrom: - configMapRef: name:
configMapRef:
で指定する以外にも、ほかに2つの方法がある。
env: -name : APP_COLOR valueFrom: configMapKeyRef: name: app-config key: APP_COLOR
volumes: - name : app-config-volume configMap: name: app-config
Secrets
ConfigMapにパスワードなどのクレデンシャルを保存するのはよろしくない。そこで出てくるのがSecrets。Secretsは環境変数の値をBase64でエンコーディングして保管する。
2つのステップでSecretsを利用できる。
- Secretsを作成し、
- PodでSecretsを指定する
Secretsの作成
①Key-Valueからシークレットを作成する。
kubectl create secret generic \ <シークレット名> --from-literal=<キー>=<バリュー>
※バリューはハッシュ化されていない平文でよい。
②ファイルをもとにシークレットを作成する。
kubectl create secret generic \ <シークレット名> --from-file=<ファイルのパス>
③YAMLでSecretの定義ファイルを作成し、それをもとにシークレットを作成。
apiVersion: v1 kind: Secret metadata: name: app-secret data: User: bxlZC= Password: cm9vDa=
※環境変数の値にはBase64エンコーディングした値を入れる。 でも、Base64エンコーディングされた値ってだけじゃ全然安全じゃない。。(より安全な方法については後述)
echo -n 'test' | base64
YAMLファイルをもとにシークレットを作成。
kubectl create -f secret.yaml
Pod設定でSecretを割り当て
spec: containers: - name: image: envFrom: - secretRef: name: app-secret
シークレットのリスクについて
シークレットは暗号化されていないため全然安全ではない。
とはいっても、シークレットをより安全に利用するベストプラクティスがある。
- Not checking-in secret object definition files to source code repositories.
- Enabling Encryption at Rest for Secrets so they are stored encrypted in ETCD.
Scale Applications
前述したので割愛
Multi Container Pods
Podの中に複数のコンテナを入れる。Webサーバとログ取得の機能をそれぞれ別々に開発し、セットでデプロイしたいということがあるが、そういったユースケースで利用される。Pod内ではネットワークとストレージを共用するのでServiceなどの追加は不要。
spec: containers: - name: image: - name image:
Multi Container Pods Design Patterns
CKADの範囲なので割愛
- サイドカー
- アダプター
- アンバサダー の3種類がある。
Init Containers
コンテナで利用するコードやバイナリファイルを事前に取得しておきたいということがある。本命のコンテナが稼働する前に、別のコンテナを起動して処理を行う。 そのコンテナがInit Container。
initContainersが複数ある場合には、記述された順に処理を行う。initConainersのうち1つでも完了できなかった場合には、Podをリスタートする。
spec: containers: - name: myapp-container image: busybox:1.28 command: ['sh', '-c', 'echo The app is running! && sleep 3600'] initContainers: - name: init-myservice image: busybox:1.28 command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] - name: init-mydb image: busybox:1.28 command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
Self Healing Applications
CKADの範囲なので割愛
6. Cluster Maintenance
OS Upgrades
OSをアップグレードする、あるいはセキュリティなどのパッチをあてる際にはノードがオフラインになる。
ノードがオフラインになった場合、マスターノードはデフォルトで5分間待機する。この待機時間をPod eviction timeoutという。5分間経ってもノードがオンラインにならない場合はノードが死んだと考え、PodがReplicasetの一部である場合には別のノードにPodを再配置する。5分経過後にノードがオンラインになったとしても、Podをそのノードに再配置することはしない。PodがReplicasetの一部でない場合にはPodは別のノードに作成されることはなく、ただ削除されたままの状態になる。
上記の影響があったも問題ない場合には特になにも対策せずに、OSをアップグレードして大丈夫。でも、上記の影響を避けたい場合にはOSをアップグレードする前にPodを退避してあげる必要がある。
下記コマンドでノードに配置されていたPodを削除して別のノードに再配置する。drainされたノードはUnschedulableになり、drainが解除されるまでPodが配置されることはなくなる。
kubectl drain <ノード名>
同様に新たなPodがスケジュールされないようノードをUnschedulableに設定できる。但し、ノード上で稼働していたPodは単に削除されるだけで別のノードに再配置されない点に注意。
kubectl cordon ノード名
drain/cordonによってUnschedulableになったノードのUnschedulable状態を解除する。解除しても他のノードに配置されたPodが再配置されるわけではない。
kubectl uncordon <ノード名>
Kubernetes Software Versions
kubernetes自体のバージョン管理について。Kubernetesのバージョンは3つからなる。
v1.12.0
左から
- Major version
- Minor version
- Patch
である。Minor versionでは機能やフィーチャーが追加される。Patchはバグフィックス。
Cluster Upgrade Process
Upgrading kubeadm clusters | Kubernetes
Kubernetesクラスタのバージョンアップをするためには、各コンポーネントのバージョン互換ルールに注意しなければならない。
K8sバージョン互換ルール
- 基本的に、Kube APIサーバより新しいバージョンだとダメ。同じバージョンか1つ古いマイナーバージョンである必要がある。
- KubeletとKube Proxyは2つ前のマイナーバージョンでもOK。
- kubectlは1つ新しいマイナーバージョンでもOK。同じバージョン、1つ古いのバージョンもOK。
- Kubernetesは直近3つのマイナーバージョンしかサポートしない。
- マイナーバージョンのアップグレードは、飛び級せずに1つずつバージョンを上げていくことが推奨されている。
Kubernetesのアップグレードは、クラスタのデプロイ方法により3つの方法に分かれる。
パブリッククラウドの場合は数クリックでアップグレードできる。スクラッチからクラスタデプロイするケースは割愛する。
ここではKubeadmのケースを扱う。
マスターノード、ワーカーノードの順でアップグレードを行う。
マスターノードをアップグレード中もワーカーノードは稼働し続けるのでユーザ影響はない。ただ、マスターがオフラインなのでPod作成や編集ができない。またワーカーノード上で稼働しているPodが落ちても復旧できない。
ワーカーノードのアップグレード方法は3つある。
- すべてのノードをいっぺんにアップグレードする方法。このやり方だとユーザがアクセスできず影響がでる。
- ノード毎にアップグレードする。1つノードをオフラインにして、そこにいたPodを別のノードで稼働させる。アップグレードが終わったら別のノードをオフラインにしてアップグレードする。
- 新しいバージョンの新しいノードを追加する。そしてノードを1つずつアップグレードしていく。
アップグレード手順
事前にマスターノードのアップグレードに関する情報を確認しておく。
kubeadm upgrade plan
マスターノードのバージョンアップ。マスターノード上の各コンポーネントをアップグレードする。
kubeadm upgrade apply v1.12.0
Kubeletがマスターノード上に存在する場合はKubeletをアップグレードしてやる。Kubeletのアップグレードは行えないためマニュアルでアップグレードする必要あり。
apt-get upgrade -y kubelet=1.12.0-00 systemctl restart kubelet
これでマスターノードのコントロールプレーンおよびKubeletがアップグレードされた。
次に、Worker NodeのKubeletアップグレードを行う。アップグレードするワーカーノードからPodを削除して別のノードに再作成しておく。
kubectl drain node01
Kubeadm、Kubelet、Nodeをアップグレードし、Kubeletをリスタートする。
apt-get upgrade -y kubeadm=1.12.0-00 apt-get upgrade -y kubelet=1.12.0-00 kubeadm upgrade node config --kubelet-version v1.12.0 systemctl restart kubelet
これでWorker Nodeが新しいバージョンでアップしてくる。
ノードのUnschedulable状態を解除するのを忘れずに。
kubectl uncordon node01
Backup and Restore Methods
バックアップ方法は3種類ある。
- リソースコンフィグのバックアップ
- ETCDクラスタのバックアップ
- パーシステントボリュームのバックアップ
K8sリソースコンフィグのバックアップ
K8sリソースコンフィグのバックアップを取得する。
kubectl get all --all-namespaces -o yaml > all-deploy-services.yaml
上のコマンドで取得できないリソースも存在する。VELEROを使えばK8sクラスタのバックアップを取得できる。
ETCDクラスタのバックアップ
Operating etcd clusters for Kubernetes | Kubernetes
ETCDクラスタは、K8sクラスタのステート情報を保管している。ETCDのデータはすべて特定のフォルダに保管されているのでそのフォルダをバックアップしてもいいが、以下ではスナップショットを作成する方法を紹介する。
スナップショット取得
ETCDCTL_API=3 etcdctl snapshot save shapshot.db \ --endpoints=https://[127.0.0.1]:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key
エンドポイントなどを指定する必要がある。
スナップショットの中身を確認
etcdctl snapshot status shapshot.db
次に、ETCDクラスタのスナップショットから復元していく。
まずはkube APIサーバを停止
service kube-apiserver stop
で、リストア。
etcdctl snapshot restore snapshot.db --data-dir /var/lib/etcd-from-backup
「/var/lib/etcd-from-backup」は新たなデータディレクトリ。
そして、リスタート。
systemctl daemon-reload service etcd restart service kube-apiserver start
これでETCDクラスタのスナップショットから復元完了。
マネージドK8sサービスを使っている場合にはETCDクラスタにアクセスできないので、Kube-APIサーバでK8sリソースコンフィグのバックアップを取ることになるらしい。
7. Security
Authentication
認証を使って、どのようにクラスタをセキュアに保つかについて見ていく。
Accounts
アカウントは大きく分けて2種類ある。 User: クラスタを操作する人(アドミン、開発者) Service accounts: 外部アプリケーションなどでクラスタにアクセスが必要なもの
K8sは基本的にアカウント管理を行わない。ユーザリストなどの外部ソース(ファイル)を利用する。
つまり、kubectlコマンドでUserアカウントを作成できない。 一方、以下のようにService Accountは作成できる。
kubectl create serviceaccount sa1 kubectl get serviceaccount
Service Accountについては後述する。ここではUserについてさらに見ていく。
Userのアクセスはkube-apiサーバによって認証(Authenticate)される。
認証方法は以下の3つ。
- Static Password File
- Static Token File
- Certificates
- External Identity Services (LDAP)
Static Password File
curl -v -k https://master-node-ip:6443/api/v1/pods -u "user1:password123"
※この認証方法は推奨でないことに注意。TLS Certificatesの利用が推奨されている。TLS Certificatesを使用した認証については後述
Static Token File
curl -v -k https://master-node-ip:6443/api/v1/pods -u --header "Authorization: Bearer <トークン>"
※この認証方法は推奨でないことに注意。TLS Certificatesの利用が推奨されている。TLS Certificatesを使用した認証については後述
TLS Basics
PKI (Public Key Infrastructure)の概要を押さえておくこと。
PKI概要
目的
サーバークライアント間で暗号化した通信を行いたい
実現方法
サーバ自身が、自分が偽のサーバでないことをクライアントに対して証明する。 クライアントが生成した対称鍵を、クライアントーサーバ間で交換する。
手順
- サーバが生成したサーバ秘密鍵とサーバ公開鍵のキーペアのうち、サーバ公開鍵をCSR(Certificate Signing Request)としてCA(Certification Authority)に送る。
- CAはCA秘密鍵とCA公開鍵をもっている。CSRを受信したら、CA秘密鍵でCSRを暗号化する。つまりCA公開鍵でのみ復号できるようにする。そして、CA秘密鍵で暗号化されたCSRをサーバに送り返す。
- サーバは、CAから受信した「CA秘密鍵で暗号化されたCSR」をクライアントに送信する。
- クライアントは、CA公開鍵をもっている。クライアントは、サーバから受信した「CA秘密鍵で暗号化されたCSR」をCA公開鍵で復号する。これでCSRが取り出される。さらにCSRの中に格納されているサーバ公開鍵を取り出す。
- クライアントは対称鍵(Symmetric Key)を生成する。生成した対称鍵をサーバ公開鍵で暗号化する。そして、「サーバ公開鍵で暗号化した対称鍵」をサーバに送る。
- サーバは、受信した「サーバ公開鍵で暗号化した対称鍵」をサーバ秘密鍵で復号し、対称鍵を取り出す。
- これでクライアントーサーバ間で安全に対称鍵を交換できた。今後は対称鍵を使って、通信を暗号化する。
※ 前提として、秘密鍵で暗号化したものは公開鍵によってのみ復号化可能。反対に、公開鍵で暗号化したものは秘密鍵によってのみ復号化可能。
TLS in Kubernetes
前述のとおり証明書には以下の3種類がある。但し、以降証明書という呼び方は、証明書(=公開鍵)と秘密鍵のペアを指す。
証明書ファイルは、拡張子が.crt
や.pem
になっている。
一方、秘密鍵ファイルは、.key
や-key.pem
になっている。
Kubernetesクラスタ内のノード間通信、Kube-apiサーバとユーザとの通信、マスターノード内の通信はすべて暗号化される。 この要件を満たすためには、以下のように証明書を配置する必要がある。ややこしいのは、KubeletとKube-APIサーバがクライアント証明書とサーバ証明書を両方とも持っているところ。Kube-APIはクライアント証明書をETCD用とKubelet用にそれぞれ1つずつ持っているので、クライアント証明書2つにサーバ証明書1つの合計3つということになる。ややこしい。。。
さらに、上記の図には含まれていないが、最低1つのCAのサーバ証明書(秘密鍵と公開鍵のペア)が存在する。
TLS in Kubernetes - Certificate Creation
Open SSLを使って、証明書を生成していく。
CAのルート証明書
まずはCAのルート証明書から。
1.秘密鍵 ca.keyを生成
openssl genrsa -out ca.key 2048
2.ca.keyと対になる公開鍵を含んだCSR ca.csrを生成
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
3.CSRに署名し、証明書 ca.crtを生成
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
これで、秘密鍵 ca.key、証明書(公開鍵) ca.crt が準備できた。
アドミンユーザのクライアント証明書生成
次に生成したCAのサーバ証明書を用いて、アドミンユーザのクライアント証明書を生成していく。
1.秘密鍵 admin.keyを生成
openssl genrsa -out admin.key 2048
2.admin.keyと対になる公開鍵を含んだCSR admin.csrを生成 ※アドミンユーザと他のユーザの区別は、Groupで行っている。 SYSTEM:MASTERSというグループを指定することで、アドミン権限をもつ。
openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr
3.CAのキーペアを使ってCSRに署名し、証明書 admin.crtを生成
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
アドミンユーザとして、Kube-apiサーバにアクセスするには以下のようにオプションを指定すればよい。
curl https://kube-apiserver:6443/api/v1/pods \ --key admin.key \ --cert admin.crt \ --cacert ca.crt
kube-config.yamlでユーザを設定する場合には、以下のように設定する。
apiVersion: v1 clusters: - cluster: certificate-authority: ca.crt server: https://kube-apiserver:6443 name: kubernetes kind: Config users: - name: kubernetes-admin user: client-certificate: admin.crt client-key: admin.key
他のクライアント証明書
同様にKube-Scheduler、Kube-controller-manager、Kube-proxyのクライアント証明書を作成していく。
サーバ証明書
次にサーバ証明書を見ていく。 (スキップ)
View Certificate Details
KubeadmでK8sクラスタをデプロイした場合の証明書の確認方法をみていく。
Kubeadmでデプロイした場合、Kube-apiサーバといったコントロールプレーンのコンポーネントはPodとしてデプロイされる。そのPodの定義ファイルは/etc/kubernetes/manifests/kube-apiserver.yaml
に保存されている。このようなPodをStatic Podと呼ぶ(詳細後述)
(K8sコンフィグの保管場所) Implementation details | Kubernetes
YAMLファイルの証明書の設定に関する部分を抜きだしたものがこちら。
spec: containers: - command: - kube-apiserver - --authorization-mode=Node,RBAC - --advertise-address=172.17.0.32 - --allow-privileged=true - --client-ca-file=/etc/kubernetes/pki/ca.crt # - --disable-admission-plugins=PersistentVolumeLabel - --enable-admission-plugins=NodeRestriction - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt # - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt # - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key # - --etcd-servers=https://127.0.0.1:2379 - --insecure-port=0 - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt # - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key # - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --secure-port=6443 - --service-account-key-file=/etc/kubernetes/pki/sa.pub - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt # - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key #
以下の証明書が設定されている。
- クライアントのルート証明書
- ETCDクライアントとしてのルート証明書およびクライアント証明書
- Kubeletクライアントとしてのクライアント証明書
- Kube-apiサーバとしてのKube-apiサーバ証明書が設定されている
試しにopenssl x509
コマンドで/etc/kubernetes/pki/apiserver.crt
の中身を見てみる。
- 発行者
- 期限
- コモンネーム
- オルタナティブネーム といった情報が記載されていることがわかる。
Serviceのログ確認
journalctl -u etcd.service -l
Podのログ確認
kubectl logs etcd-master
Dockerコンテナのログ確認
K8sがダウンした場合
docker ps -a
docker logs <コンテナ>
Certificates API
アドミンユーザとは別に新しいユーザを作成したい場合、以下の手順でCertificateを作成する。
- Create CertificateSingningRequest Object
- Review Requests
- Approve Requests
- Share Certs to Users
以下では、新しく「jane」というユーザを作成するケースを例として考える。
Create CertificateSingningRequest Object
Certificate Signing Requests | Kubernetes
openssl genrsa -out jane.key 2048 openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
janeのCSRをもとにCertificateSingningRequestオブジェクトを作成する。
apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest name: jane spec: signerName: kubernetes.io/kube-apiserver-client groups: - system: authenticated usages: - digital signature - key encipherment - client auth requests: <Base64エンコーディングしたCSR>
Kubernetes signers
Certificate Signing Requests | Kubernetes
K8sではカスタムsignerNameを作成できる。一方、K8sはビルトインのsignerを提供している。ビルトインのsignerは以下の種類がある。
kubernetes.io/kube-apiserver-client
kubernetes.io/kube-apiserver-client-kubelet
kubernetes.io/kubelet-serving
kubernetes.io/legacy-unknown
kubernetes.io/kube-apiserver-client-kubelet
だけkube-controller-managerによって自動で承認される可能性がある。他は自動で承認されることはない。
signerの種類によっては、usages:
に入れられるキーが限定される。
kubernetes.io/kube-apiserver-client
であれば、- client auth
はマスト。- digital signature
、- key encipherment
はオプションで追加できるが、それ以外(例えば- server auth
)は不可。
kubernetes.io/kube-apiserver-client-kubelet
の場合は、- client auth
、- digital signature
、- key encipherment
をすべて入れる必要がある。- server auth
は不可。
それ以外の詳細はK8sのサイトを確認。 Certificate Signing Requests | Kubernetes
Review Requests
作成したCertificateSingningRequestオブジェクトは以下のコマンドで確認できる。
kubectl get csr
Approve Requests
Certificate Signing Requests | Kubernetes
アドミニストレータがCertificateSingningRequestオブジェクトを承認する。
kubectl certificate approve jane
すると、KubernetesがCAキーペアを使って署名しCertificateを払い出す。
Share Certs to Users
Certificate Signing Requests | Kubernetes
Kubernetesによって払い出されたCertificateは、次のコマンドで表示できる。
kubectl get csr jane -o yaml
status: certificate:
フィールドがBase64エンコーディングされたCertificate(公開鍵)になっている。
CSR-APPROVING、CSR-SIGNINGといったタスクは、Controller Managerが行っている。
KubeConfig
複数のクラスターへのアクセスを設定する | Kubernetes
KubeConfigファイルは、$HOME/.kube/config
に保存されている。
クラスタが複数ある場合には、どのユーザがどのクラスタを使えるか定義する必要がある。それを$HOME/.kube/config
で設定している。
$HOME/.kube/config
の中身を以下のような感じ。kubectl create
コマンドなどでオブジェクトを作成する必要はない。ファイルを書き換えると自動的に反映される。
apiVersion: v1 kind: Config clusters: - name: development cluster: certificate-authority: /etc/kubernetes/pki/ca.crt server: https://development-kubeapi-server:6443 contexts: - name: dev-frontend context: cluster: development user: developer users: - name: developer user: client-certificate: /etc/kubernetes/pki/users/admin.crt client-key: /etc/kubernetes/pki/users/admin.key
以下のコマンドで$HOME/.kube/config
の中身を見ることができる。
kubectl config view
KubeConfigファイルをデフォルトの$HOME/.kube/config
ではなく他のファイルを使う場合には、以下のようにオプションでKubeConfigファイルを指定する。
kubectl config view --kubeconfig=my-custom-config
複数のコンテキストが存在する場合には、次のコマンドで利用するコンテキストを指定できる。
kubectl config use-context dev-frontend
API Groups
APIのパスでグルーピングされている。例えば/apps
など。さらにその配下にAPIリソース、Verbが定義されている。
これらのAPIグループを使って、認可(Authorization )を行う。
Authorization
認証は本人であることを確認すること。一方、認可(Authorization )は特定の権限のみを与えること。
Authorization には次の4つがある。
- Node
- ABAC
- RBAC
- Webhook
- AlwaysDeny
- AlwaysAllow
Node
Worker node(Kubelet)がMaster nodeにアクセスするのを許可する。
ABAC (Attribute-based access control)
ユーザ名などアトリビュート(属性値)ごとに権限を付与する方法。
RBAC(Role-based access control)
開発者やアドミンなどのロールごとに権限を付与する。
Webhook
代わりに認可機能を実施する外部のツールを使う。例えばOpen Policy Agentなど。
Authorization Mode
上記のAuthorization方法=Authorization Modeを複数利用することもできる。 その場合、順序性を持っているため、最初のAuthorization Mode認可されれば、権限が付与される。
RBAC詳細
Roleオブジェクト
RoleオブジェクトのYAMLファイルは以下。
resourceNames
を指定することでPod名を絞って指定することができる。
YAMLを作成したらkubectl create -f
コマンドでRoleオブジェクトを作成する。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-reader rules: - apiGroups: [""] # "" indicates the core API group resources: ["pods"] verbs: ["get", "watch", "list"] resourceNames: ["blue", "orange"]
RoleBindingオブジェクト
↑で作成したRoleをUserに紐づけるオブジェクト。
kubectl create -f
コマンドでRoleBindingオブジェクトを作成する。
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods subjects: - kind: User name: dev-user apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
View RBAC
kubectl get roles kubectl get rolebindings kubectl describe role pod-reader kubectl describe rolebinding read-pods
Check Access
Authorization Overview | Kubernetes
権限の有無を確認できる。以下の例ではユーザ「dave」がprodネームスペースにdeploymentを作成できるかを調べている。 権限を保有している場合は「yes」、権限を保有してない場合は「no」が応答される。
kubectl auth can-i create deployments --namespace prod --as dave
Cluster Roles and Cluster Role Bindings
Using RBAC Authorization | Kubernetes
ResourceにはNamespacedリソースと非Namespacedリソース(Cluster Scopedリソース)がある。NamespacedリソースにはPodsやDeploymentsが含まれる。一方、非NamespacedリソースにはNodesやNamespacesが含まれる。PodはNamespacesを指定して操作を行えるが、一方NodesはNamespaceは指定できないため、上記のようにNamespacedリソースと非Namespacedリソースという分類になる。
Namespacedリソース、非Namespacedリソースの一覧は下記コマンドで確認できる。
kubectl api-resources --namespaced=true
非Namespacedリソースの「Node」や「Namespace」といったリソースへのアクセス権限付与に用いるのが「ClusterRole」と「ClusterRoleBinding」である。
- ClusterRole
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: cluster-administrator rules: - apiGroups: [""] resources: ["nodes"] verbs: ["create", "get", "list", "delete"]
- ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: cluster-admin-role-binding subjects: - kind: User name: cluster-admin apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-administrator apiGroup: rbac.authorization.k8s.io
ClusterRoleをnamespacedリソース(Podsなど)にも適用することもできる。そのClusterRoleをClusterRoleBindingによりUserに割当てた場合、UserはNamespaceを問わず全てのNamespacedリソース(Pods)にアクセスできる。
Service Accounts
Accountには、User AccountとService Accountがある。PrometeheusがKuberntesクラスタ監視をする際にKuberntesクラスタのアカウントが必要。または、Jenkinsが自動デプロイする際にKuberntesクラスタアカウントが必要。そういったときに使われるのがService Account。
Service Accountは下記コマンドで作成・確認できる。
kubectl create serviceaccount dashboard-sa kubectl get serviceaccount
Service Accountを作成すると自動的にToken
が払い出される。Service AccountはTokenを利用してKuberntesクラスタにアクセスできる。但し、TokenはSecret Objectに入れられているので下記コマンドでTokenそのものは確認できない。
kubectl describe serviceaccount dashboard-sa
Tokenを見るには下記コマンドを実行する。
kubectl describe secret dashboard-sa
実際に上記コマンドでTokenを表示させてみる。
$ kubectl describe secret ray-operator-serviceaccount-token-cvl29 Name: ray-operator-serviceaccount-token-cvl29 Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: ray-operator-serviceaccount kubernetes.io/service-account.uid: 50f1e4f3-5b00-4249-9ef3-4466a3c0476f Type: kubernetes.io/service-account-token Data ==== ca.crt: 1066 bytes namespace: 7 bytes token: eyJhbGciXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXkU8A
Kube APIを呼ぶときに上記TokenをAuthorization Bearer Headerに指定することで、Kuberntesクラスタへのアクセスが許可される。
curl https://kube-apiserver:6443/api -insecure --header "Authorization: Bearer eyJhbGci"
実はデフォルトで、すべてのNamespaceでdefault
という名前のService Accountが作成されている。
実はPodを作成すると、このdefault
ServiceAccountがPodにマウントされている。なぜかというとPodにKube APIを呼ぶ権限を与えるため。但し、default
Service Accountで呼べるのは基本的なAPIだけに限られる。Podのマウントは下記コマンドで確認できる。
kubectl describe pod my-kubernetes-dashboard
例
まずPodにマウントされているService Accountのディレクトリを確認する。
$ kubectl describe pod ray-operator-5b985c9d77-t2hlc Name: ray-operator-5b985c9d77-t2hlc Namespace: default Containers: ray: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-f2z8h (ro)
Podの中に入る。
$ kubectl exec -it ray-operator-5b985c9d77-t2hlc -- bash
Tokenの中身を見てみる。
$ cat /var/run/secrets/kubernetes.io/serviceaccount/token eyJhbGciXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXkU8A
PodにデフォルトでないService Accountを割り当てるには、PodのYAMLファイルのspec:
にserviceAccountName
を追記する。
デフォルトのService AccountをPodにマウントしたくない場合は、PodのYAMLファイルのspec:
にautoMountServiceAccountToken: false
を記載する。
Image Security
Pull an Image from a Private Registry | Kubernetes
- Dockerイメージをセキュアなリポジトリに保管し、
- セキュアなレポジトリからイメージをプルする方法。
image: nginx
と記述するが、これはレジストリとアカウントが省略されている。正式には、image: docker.io/library/nginx
である。
docker.io
がレジストリを表しており、library
がアカウントを表している。
Podが、Docker Hub上にあるプライベートレジストリからイメージをプルしたい場合には
①プライベートDockerリポジトリのクレデンシャルをもとにSecret Objectを作成
kubectl create secret docker-registry regcred \ --docker-server=<your-registry-server> \ --docker-username=<your-name> \ --docker-password=<your-pword> \ --docker-email=<your-email>
②Pod YAMLファイルのimagePullSecrets:
でSecret Objectを指定する
apiVersion: v1 kind: Pod metadata: name: private-reg spec: containers: - name: private-reg-container image: <your-private-image> imagePullSecrets: - name: regcred
Security Contexts
Configure a Security Context for a Pod or Container | Kubernetes
Pod YAMLファイルのspec:
配下、またはspec: containers:
配下にsecurityContext
を設定して、Podを操作するユーザID runAsUser:
と実行できる操作 "ケーパビリティ" capabilities: add: ["user"]
を限定することができる。
ケーパビリティの設定は、spec: containers:
配下の場合のみ。spec:
配下では設定できない。
ケーパビリティは、例えばLinux capabilitiesがある。 capabilities(7) - Linux manual page
apiVersion: v1 kind: Pod metadata: name: security-context-demo-4 spec: containers: - name: sec-ctx-4 image: gcr.io/google-samples/node-hello:1.0 securityContext: capabilities: runAsUser: <User ID> add: ["NET_ADMIN", "SYS_TIME"]
Network Policy
Kubenetesクラスタの仮想ネットワークは、デフォルトでAll Allowになっており、クラスタ内のPod/Serviceから任意のPod/Serviceにアクセスできる。
Podにネットワークポリシーを適用するには、NetworkPolicy
オブジェクトを作成し、podSelector:
で該当するLabelを持つPodにネットワークポリシーを紐づける。
具体例をみていく。
role: db
というラベルを持つPodがあるとする。そのPodに「role: api-pod
というラベルを持つPodからの3306ポート宛てのTCPトラヒックを許可する」というネットワークポリシーを割り当てる。その場合のNetworkPolicy
オブジェクトは以下のようになる。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy spec: podSelector: matchLabels: role: db policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: role: api-pod ports: - protocol: TCP port: 3306
すべてのNetwork SolutionがNetworkPolicy
オブジェクトをサポートしているわけではないことに注意!
NetworkPolicy
オブジェクトをサポートしている
- Kube-router
- Calico
- Romana
- Weave-net
NetworkPolicy
オブジェクトをサポートしてない
- Flannel
8. Storage
Storage in Docker
(Skip)
Volume Driver Plugin in Docker
ストレージドライバーについて | Docker ドキュメント
ストレージドライバー vs. Docker ボリューム🔗
Docker uses storage drivers to store image layers, and to store data in the writable layer of a container. The container’s writable layer does not persist after the container is deleted, but is suitable for storing ephemeral data that is generated at runtime. Storage drivers are optimized for space efficiency, but (depending on the storage driver) write speeds are lower than native file system performance, especially for storage drivers that use a copy-on-write filesystem. Write-intensive applications, such as database storage, are impacted by a performance overhead, particularly if pre-existing data exists in the read-only layer.
Use Docker volumes for write-intensive data, data that must persist beyond the container’s lifespan, and data that must be shared between containers. Refer to the volumes section to learn how to use volumes to persist data and improve performance.
Container Storage Interface (CSI)
(Skip)
Volume
コンテナが消えると、コンテナで扱っていたデータが消えてしまう。そこで、コンテナ上でVolumeを作ってそれをホストのディレクトリにマウントする。
hostPath
※hostPathはセキュリティ的に非推奨。
apiVersion: v1 kind: Pod metadata: name: pv-recycler spec: volumes: - name: vol hostPath: path: /any/path/it/will/be/replaced containers: - name: pv-recycler image: "k8s.gcr.io/busybox" volumeMounts: - name: vol mountPath: /scrub
シングルノードの場合はhostPathでいいが、マルチノードの場合は無理。そこでNFS, GlusterFS, FlockerといったStorage Solutionを利用する。
AWS EBS
例えば、AWS EBSを利用する場合には以下のようになる。 ※EBS自体は別途AWS上で作成しておく必要がある。
apiVersion: v1 kind: Pod metadata: name: test-ebs spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /test-ebs name: test-volume volumes: - name: test-volume # This AWS EBS volume must already exist. awsElasticBlockStore: volumeID: "<volume id>" fsType: ext4
Persistent Volumes
いちいちPod定義のYAMLファイルにVolumeを記載するのは面倒なので、別のやり方を考えたい。
apiVersion: v1 kind: PersistentVolume metadata: name: pv0003 spec: accessModes: - ReadWriteOnce capacity: storage: 5Gi awsElasticBlockStore: volumeID: "<volume id>" fsType: ext4 persistentVolumeReclaimPolicy: Retain
Persistent Volume Claim
Persistent Volumeの要求。要求を満たすPersistent Volumeがあれば、PVCのステータスがPendingからBoundになる。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi
View PVC
kubectl get persistentvolumeclaim
Delete PVC
kubectl delete persistentvolumeclaim myclaim
persistentVolumeReclaimPolicy: Retain
となっている場合には、PVCを削除してもPersintent Volumeは削除されず、また使いまわすこともできない。
persistentVolumeReclaimPolicy: Delete
となっている場合には、PVCを削除するとPersintent Volumeも削除される。そして、エンドストレージデバイスも空になる。
persistentVolumeReclaimPolicy: Recycle
となっている場合には、PVCを削除するとPersintent Volumeの中身が空になり、他のPVCで利用できるようになる。
Using PVC in Pod
spec: volumes:
配下で、persistentVolumeClaim:
を設定する。
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: myfrontend image: nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: myclaim
Storage Class
PVを作成する前に、マニュアルでAWS上にEBSを作成する必要があった。 Storage Classを利用することで、自動でAWSなどのクラウド上にストレージをプロビジョニングしてくれる。
- Storage Class
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: aws-ebs-storage provisioner: kubernetes.io/aws-ebs parameters: type: io1 iopsPerGB: "10" fsType: ext4
- PersistentVolumeClaim
Storage Classを利用する場合は、storageClassName:
にStoragClass名を入れる。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: foo-pvc namespace: foo spec: storageClassName: aws-ebs-storage accessModes: - ReadWriteOnce resources: requests: storage: 8Gi
- Pod
Storage Classを利用する場合でも、Podはこれまで同様persistentVolumeClaim:
でPVCを指定してあげればよい。
9. Networking
DNS on Linux
K8sではなく、まずLinuxでのDNSについて再確認していく。
Name resolution
/etc/hosts
にホスト名とIPアドレスを記載することで、Linuxでホスト名をIPアドレスに変更できるようになる。
/etc/hosts
ファイル
192.168.1.11 db
ping db
とすると、ping 192.168.1.11
となる。
DNS
/etc/resolv.conf
にDNSサーバのIPを入れておく。そうすることでping <ホスト名>
としたときに、DNSに名前解決しにいく。
/etc/resolv.conf
ファイル
nameserver 192.168.1.100 nameserver 8.8.8.8
8.8.8.8
は、Googleが無償で提供している「Google Public DNS」のIPアドレス
/etc/hosts
と/etc/resolv.conf
どちらとも設定してある場合、つまりLinuxローカルにホスト名も登録しているけど、DNSも設定している場合には、/etc/nsswitch.conf
に記載されている順番で名前解決を行う。
以下の例だと、まずは/etc/hosts
でローカルに登録しているホスト名を見に行く。そこになければ、DNSサーバに名前解決にいく。
/etc/nsswitch.conf
ファイル
hosts: files dns
但し、nslookup
はDNSサーバにしか名前解決しにいかないので注意。/etc/hosts
ファイルにホスト名を登録していても意味ない。dig
はnslookup
より詳しい応答を返してくれる。
ドメイン名を省略したい場合には/etc/resolv.conf
でsearch
を利用する。
/etc/resolv.conf
でsearch
ファイル
search mycompany.com
こうすると、ping web
としたときにping web.mycompany.com
とインテリジェントに解釈してくれる。
Core DNS
サービスディスカバリーにCoreDNSを使用する | Kubernetes
CoreDNS は CNCF によってホストされている OSS の DNS サーバ。
設定手順
ちょっと便利に使う CoreDNS - Retty Tech Blog
Pre-requisite - Network Namespace
Create Network Namespace on Linux
# Network Namespaceの作成 ip netns add red ip netns add blue # 作成したNamespaceの確認 ip netns # "red" Namespace上でのip link/arp/の確認 ip netns exec red ip link ip netns exec red ip arp # IPリンクの作成 ip link add veth-red type veth peer name beth-blue # 作成したIPリンクをNamespaceにアタッチ ip link set veth-red netns red ip link set veth-blue netns blue # IPアドレスを割り当てる ip -n red addr add 192.168.15.1 dev veth-red ip -n blue addr add 192.168.15.1 dev veth-blue # IPリンクをUpにする ip -n red link set veth-red up ip -n blue link set veth-blue up # "red" Namespaceからveth-blueにping ip netns exec red ping 192.168.15.2 # "red" Namespaceからarp ip netns exec red arp
Linux Bridge
仮想的なスイッチを作成し、Namespaceにアタッチしていく。 仮想的なスイッチを作成できるソリューションは色々あるが、ここではネイティブのLinux Bridgeを使う。
# 新しいIFの作成 ip link add v-net-0 type bridge # IPリンクをUpにする ip link set dev v-net-0 up # スイッチにNamespaceをアタッチしていくので、いったん作成済みのリンクを削除する ip -n red link del veth-red # リンクを作成する ip link add veth-red type veth peer name veth-red-br ip link add veth-blue type veth peer name veth-blue-br # 作成したリンクをNamespaceにアタッチする ip link set veth-red netns red ip link set veth-red-br netns master v-net-0 ip link set veth-blue netns blue ip link set veth-blue-br netns master v-net-0 # IPアドレスを割り当てる ip -n red addr add 192.168.15.1 dev veth-red ip -n blue addr add 192.168.15.1 dev veth-blue ip addr add 192.168.15.5/24 dev v-net-0 # IPリンクをUpにする ip -n red link set veth-red up ip -n blue link set veth-blue up
Pre-requisite - Docker Networking
(Skip)
Pre-requisite - CNI (Container Network Interface)
これはコンテナランタイム間のネットワーク設定の差異を乗り越えるためのコンテナ標準NW IF。
Cluster Networking
Ports and Protocols | Kubernetes
ポート番号
- Kube-API: 6443
- ETCD Server: 2379
- ETCD Client: 2380
- Kubelet: 10250
- Kube-scheduler: 10251
- Kube-controller-manager: 10252
- Services: 30000 - 32767
Important Note about CNI and CKA Exam
Weave-netプラグインのイントール方法がK8s公式に記載されていたが今は無くなっている。曰く、「If you have a question to install it, they will provide a link to it.」とのこと。 Hi all, Currently I'm going through the CKA exam prep and in the installing netw . . . - Kubernetes-Slack Discussions - KodeKloud - DevOps Learning Community
Ciliumのインストール方法がK8s公式にあったので、一応ブックマークしておいたほうが良さそう。 Use Cilium for NetworkPolicy | Kubernetes
Pod Networking
CNI in Kubernetes
KubeletがどのNetwork Solutionを利用しているかは/etc/cni/net.d
配下のファイルをみればわかる。
ps -aux | grep kubelet ls /opt/cni/bin ls /etc/cni/net.d cat /etc/cni/net.d/10-bridge.conf
Practice Test - Deploy Network Solution
https://uklabs.kodekloud.com/topic/practice-test-deploy-network-solution-2/
IP Address Management (IPAM)
/etc/cni/net.d/net-script.conf
で、type": "host-local" とすると、ローカルホストでIPアドレスのアサインを管理
WeaveのデフォルトIPレンジは10.32.0.0/12
で、アサインすることができるアドレスは10.32.0.1 ~ 10.47.255.254
このアドレスレンジをノード数分に等しく分割して、それぞれのレンジ内でIPをアサインする。
{ "name": "k8s-pod-network", "cniVersion": "0.3.0", "type": "net-script", "bridge": "cni0", "ipam": { "type": "host-local", "subnet": "10.244.0.0/16", "routes": [ {"dst": "0.0.0.0/0"} ] } }
Service Networking
Serviceはプロセスでもネームスペースでもインターフェースでもなく、仮想的なオブジェクト。 クラスタは特定のノードに存在するわけではなく、クラスタ内の全ノードにわたって仮想的に存在している。
Serviceが新しく作成されたらKube-Proxyが認識し、まず事前に定義されたIPレンジからServiceにIPを割り当てる。そして、フォワーディングルールを作成する。このルールにより、Serviceに割当てられたIPにきたパケットをPodに転送する。これによってServiceにアクセスすることで、Podにアクセス可能になる。
ではどのようにフォワーディングルールを作成するか。3種類のやり方=Proxy-modeがある。
- userspace
- iptables
- ipvs
kube-proxy --proxy-mode [ userspace | iptables | ipvs ]
デフォルトはiptables
。Kube-Proxyにより、作成されたIP tableは以下で確認できる。
iptables -L -t nat | grep db-service
また、/var/log/kube-proxy.logでNATが登録されていることを確認できる。
Serviceには3種類ある。
- ClusterIP: クラスタ内のPodからアクセス可能にする
- NodePort: クラスタ外部からアクセス可能なポートを割り当てる。
- LoadBalancer: クラスタ外部からアクセス可能なロードバランサー。クラウドプロバイダー上でのみ使用可能
DNS in kubernetes
K8sではデフォルトでビルトインのDNSサーバをデプロイする。これによりクラスタ内で、PodやServiceの名前解決ができる。
Serviceが作成されると、Kuberntes DNSはServiceのホスト名とIPのマッピングを登録する。これによりService名でアクセス可能になる。
仮にアクセス先のServiceが、アクセス元とは別のNamespaceであった場合、<Service名>.<Namespace>
という形式でアクセス可能。
各Namespaceに対し、DNSサーバはサブドメインを作成する。すべてのServiceはsvc
というサブドメインにグループ化される。さらに、すべてServiceとPodはcluster.local
というルートドメインとしてグループ化される。
つまり、DNSに登録されるServiceのAレコードは、
<Service名>.<Namespace>.svc.cluster.local
となる。
Podの場合は、
<PodのIPアドレス*1>.<Namespace>.pod.cluster.local
である。
*1: PodのIPアドレスは、ドットをダッシュに変えたものになる。つまり、10.244.2.5であれば、10-244-2-5となる。
CoreDNS in Kubernetes
サービスディスカバリーにCoreDNSを使用する | Kubernetes
CoreDNSは、PodやServiceが作成されたら自動でDNSにA/AAAAレコードを作成する。 CoreDNSはDeploymentとしてKube-systemネームスペースにデプロイされる。
CoreDNSのコンフィグファイルは、/etc/coredns/Corefile。Corefileの中身はこちら。 Customizing DNS Service | Kubernetes
このコンフィグファイルは、ConfigMapオブジェクトとして、Podに渡される。
CoreDNSがDeploymentとして作成される際に、デフォルトでkube-dns
という名前でServiceも作成される。新しくPodが作成されると、Pod内の/etc/resolve.conf
にkube-dns
ServiceのIPアドレスが登録される。これによって新規に作成されたPodが、名前解決のためにCoreDNSサーバにアクセスできる。
www.google.comといったウェブサイトの名前解決リクエストは、CoreDNS Pod内の/etc/resolbe.confファイルに記載されたネームサーバに転送される。
Ingress
URL Pathベースルーティングを提供するロードバランサ。TLS終端をサポート。
Ingress Controller
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: minimal-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx-example rules: - http: paths: - path: /testpath pathType: Prefix backend: service: name: test port: number: 80
Ingress を作成したら、NodePortといったServiceと紐づける。
Ingressの転送先にはServiceを指定する。Service名とServiceで開けているポートを指定してやる。 Ingress | Kubernetes
IPアドレスレンジの確認方法
Node
ip addr
でeth0
に割当てられているアドレスレンジを確認する
Pod
ip addr
でCNIプラグイン名のIFに割当てられているアドレスレンジを確認する
または、ログ中のipalloc-range:
を確認する。
kubectl logs <weave-pod-name> weave -n kube-system
Service
kube-apiサーバに割当てられているservice-cluster-ip-range
を確認する。
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep service-cluster-ip-range
Design and Install a Kubernetes Cluster
Design a Kubernetes Cluster
K8sで扱える最大容量 Considerations for large clusters | Kubernetes
Choosing Kubernetes Infrastructure
(Skip)
Configure High Availability
Master Nodeの冗長化を考える。
その場合、Kube-APIは、Active-Activeでロードバランシングする。 一方、Controller ManagerとSchedulerは、Active-Standby構成
ETCDは2種類の冗長化方法がある。
1つがStacked Topology。他のMaster Nodeのコンポーネントと同じノードに、ETCDをデプロイする。 もう1つがExternal ETCD Topology。他のMaster Nodeとは別のノードにETCDをデプロイする。
ETCD in HA
分散データベース。すべてのノードで保管されているデータが一致。どのノードに対しても読み書きできる。
ノード間の差分がないので、どのノードに対しても読み込みができる。
Leader Election - RAFT
ETCDでは、RAFTプロトコルでLeaderとFollowerを決定する
それぞれのETCDサーバがランダムタイマー待機後に、リーダーになりたいというリクエストを送る。最初にリーダーのリクエストを送ったサーバがリーダーになる。
リーダーからの応答がなくなった場合には、残りのETCDサーバがリーダーを決定するために再度Electionを行う。
Leaderは書き込みを受け付けて、他のノードにも書き込みをリクエストする。他のノードから応答が帰ってきたら、書き込みを完了とする。もし、Followerが書き込みリクエストを受け付けた場合には、Leaderにリクエストを転送する。
Leaderからの書き込みリクエストに対して、他のノードから応答が返ってこない場合に、round(N/2+1)個のノードから応答が返ってくれば、書き込みが正常に完了したと判断する。
ノード数は奇数が望ましい。偶数の場合には冗長性がイマイチであるため。
Install Kubernetes the kubeadm way
Introduction to Deployment with Kubeadm
kubeadmを使用したクラスターの作成 | Kubernetes
- Master node, Worker nodeを準備する
- Dockerコンテナランタイムをインストール
- kubeadmをインストール
- Master nodeを初期化
- ネットワークプラグインをインストール
- Worker nodeをjoinさせる
Deploy with Kubeadm - Provision VMs with Vagrant
(Skip)
Demo - Deployment with Kubeadm
(Skip)
End to End Tests on a Kubernetes cluster
(Skip) As per the CKA exam changes (effective September 2020), End to End tests is no longer part of the exam
Troubleshooting
トラブルシューティングのテクニックや手順の概要を示す。
Application Failure
アプリケーションのトラブルシューティング | Kubernetes
PodやServiceの接続図を描くとわかりやすくなる 仮にユーザからアプリケーションに接続できないと申告があった場合、
まず、アプリケーションのフロントエンドを調べる。
curl http://web-service-ip:node-port
Service
次にWebサービスのServiceを調べる。ServiceのSelector、PodのLabelが一致していることを確認する。
kubectl describe service web-service
Pod
次にPodがRunning
ステータスになっていることを確認する。
kubectl get pod
また、Podのイベントログを確認する。
kubectl describe pod web
さらにPodのログを調べる。
kubectl log web
Podがリスタートしてログが消えてしまう場合は、-f
でリスタートしてもログが消えないようにするか、--previous
で前回のログを確認する。
Controlplane Failure
Troubleshoot Clusters | Kubernetes
まずは、Nodeのステータスをチェック。
kubectl get nodes
KubeadmでK8sクラスタがデプロイされている場合、コントロールプレーンはPodとしてデプロイされている。なので、kube-system
NamespaceのPodをチェックする。
kubectl get pods -n kube-system # Podのステータス確認 kubectl logs kube-apiserver-master -n kube-system # Podのログ確認
コントロールプレーンのコンポーネントがServiceとしてデプロイされている場合は、Serviceをチェックする。
service kube-apiserver status service kube-controller-manager status service kube-scheduler status service kubelet status # Worker node service kube-proxy status
Serviceの場合のログ確認は下記コマンドで行う。
sudo journalctl -u kube-apiserver
Workder Node Failure
まずはNodeのステータスをチェック。
kubectl get nodes
次にフラグを確認する。メモリ不足やディスク不足がわかる。 Master nodeに接続できない場合は、ステータスが「Unknown」になる。その場合、LastHeartbeatTimeでいつクラッシュしたかがわかる。
$ kubectl describe node ip-192-168-26-141.us-west-2.compute.internal Name: ip-192-168-26-141.us-west-2.compute.internal Conditions: Type Status LastHeartbeatTime LastTransitionTime Reason Message ---- ------ ----------------- ------------------ ------ ------- MemoryPressure False Fri, 04 Mar 2022 10:32:12 +0900 Fri, 25 Feb 2022 13:22:28 +0900 KubeletHasSufficientMemory kubelet has sufficient memory available DiskPressure False Fri, 04 Mar 2022 10:32:12 +0900 Fri, 25 Feb 2022 13:22:28 +0900 KubeletHasNoDiskPressure kubelet has no disk pressure PIDPressure False Fri, 04 Mar 2022 10:32:12 +0900 Fri, 25 Feb 2022 13:22:28 +0900 KubeletHasSufficientPID kubelet has sufficient PID available Ready True Fri, 04 Mar 2022 10:32:12 +0900 Fri, 25 Feb 2022 13:22:49 +0900 KubeletReady kubelet is posting ready status
Worker node上でCPU、メモリの使用状況をチェック
top # CPUの使用状況 df -h # メモリの使用状況
次に、Kubeletのステータスを調べる。
service kubelet status
さらにKubeletのログを調べる。
sudo journalctl -u kubelet
証明書が期限切れしてないかを確認する。
openssl x509 -in /var/lib/kubelet/worker-1.crt -text
Network Troubleshooting
Network Plugin in kubernetes
Kubernetes uses CNI plugins to setup network. The kubelet is responsible for executing plugins as we mention the following parameters in kubelet configuration.
- cni-bin-dir: Kubelet probes this directory for plugins on startup
- network-plugin: The network plugin to use from cni-bin-dir. It must match the name reported by a plugin probed from the plugin directory.
Note: If there are multiple CNI configuration files in the directory, the kubelet uses the configuration file that comes first by name in lexicographic order.
DNS in Kubernetes
Debugging DNS Resolution | Kubernetes
Kubernetes uses CoreDNS. CoreDNS is a flexible, extensible DNS server that can serve as the Kubernetes cluster DNS.
Troubleshooting issues related to coreDNS 1. If you find CoreDNS pods in pending state first check network plugin is installed. 2. coredns pods have CrashLoopBackOff or Error state
If you have nodes that are running SELinux with an older version of Docker you might experience a scenario where the coredns pods are not starting. To solve that you can try one of the following options:
a)Upgrade to a newer version of Docker.
b)Disable SELinux.
c)Modify the coredns deployment to set allowPrivilegeEscalation to true:
d)Another cause for CoreDNS to have CrashLoopBackOff is when a CoreDNS Pod deployed in Kubernetes detects a loop.
There are many ways to work around this issue, some are listed here:
Add the following to your kubelet config yaml: resolvConf: <path-to-your-real-resolv-conf-file> This flag tells kubelet to pass an alternate resolv.conf to Pods. For systems using systemd-resolved, /run/systemd/resolve/resolv.conf is typically the location of the "real" resolv.conf, although this can be different depending on your distribution.
Disable the local DNS cache on host nodes, and restore /etc/resolv.conf to the original.
A quick fix is to edit your Corefile, replacing forward . /etc/resolv.conf with the IP address of your upstream DNS, for example forward . 8.8.8.8. But this only fixes the issue for CoreDNS, kubelet will continue to forward the invalid resolv.conf to all default dnsPolicy Pods, leaving them unable to resolve DNS.
If CoreDNS pods and the kube-dns service is working fine, check the kube-dns service has valid endpoints.
kubectl -n kube-system get ep kube-dns
If there are no endpoints for the service, inspect the service and make sure it uses the correct selectors and ports.
Kube-Proxy
Troubleshooting issues related to kube-proxy 1. Check kube-proxy pod in the kube-system namespace is running.
Check kube-proxy logs.
Check configmap is correctly defined and the config file for running kube-proxy binary is correct.
kube-config is defined in the config map.
check kube-proxy is running inside the container
Other Topics
JSON PATH
(Skip)
Advanced Kubectl Commands
(Skip)
個人メモ
staticPodPathの確認方法
・ps -aux | grep kubelet ・「--config=/var/lib/kubelet/config.yaml」を見つける ・cat /var/lib/kubelet/config.yaml する ・staticPodPath: /etc/kubernetes/manifests
scpコマンド
・リモートホストにscpでファイルを転送 ・scp test.txt node01:/etc/kubernetes