Certified Kubernetes Administrator (CKA) with Practice Tests 勉強メモ
2CoreConcepts
cluster architecture
- master
workerノード(cargo ship)
- 船長:kubelet
- masterと通信。ノードごとに存在
- masterから指示を受けとる、masterに稼働状況・ログを送る。
- kube proxy
- 異なるノード(船)にいるコンテナ同士で通信できるようにするためのサービス
- 船長:kubelet
masterにもworkerにもcontainer runtime (like docker ) が必要。
etcd
key-value ストア
- 構成設定みたいな小さいデータを保存しておいて、高速に読み書きしたい場合に便利。
cluster組めるのが特徴?
ポートは2379
試したければgithubからDLして解凍してrunしてみれば良い
いろんなクライアント(ここでは構成設定を保存したいサービス?)を紐づけ可能。
初期段階ではetcdのCLIだけがクライアントとして登録されている。
etcd set key1 value1 ectd get key1 # value1
kubectl get
ではetcdから引っ張ってきている- k8sのデプロイの仕方によってetcdもデプロイの仕方が2種類ある。スクラッチでやるか。kubeadmでやるか
- スクラッチでやる場合、バイナリを落としてきてサービス起動。
- TLSとクラスタの設定が重要
- etcd起動時のパラメタ:
advertize-client-urls
(etcdがlistenするhttps://IP:port)には、kube-api serverが参照するURLと対応させる必要がある。つまりetcdのエンドポイント。これにアクセスすると各master nodeに存在するetcdのどれかに振られる、みたいな感じなんだろうか。
- kubeadmでデプロイした場合、
- kube-system namespaceにpodとしてetcdが作られる。
kubectl get pods -n kube-system
- kube-system namespaceにpodとしてetcdが作られる。
- ディレクトリの形で情報が保存されている。rootは/registryで、
- その下に./pods, replicas, depolyments...など。active directoryで聞いたことある発想。
- スクラッチでやる場合、バイナリを落としてきてサービス起動。
- 起動時パラメタ
initial-clusrter
で、各master-nodeに配置されたetcdのURLを指定する。advertize-client-urls
がetcdクラスタとしてのエンドポイント、initial cluster
で指定する項目が各master node上のetcd のエンドポイントだろうか。 - 前更新したca.crt, server.crt, server.keyは、etcdctlがetcd apiサーバに対して認証できるようにするためのものだったらしい。
kube-apiserver
- primary manegement componet
- masterからworkerに指示を送る、workerからmasterに報告が飛ぶ
- kubectl も実際はkube-apiserverへのアクセス。
- 認証して、
- コマンドを検証して、
- etcdから情報を取ってきて返す
- をやっている。
- なので、POSTをAPI serverに送ってもkubectltと同じことができる。
- podの作成のリクエストをユーザーが送ると・・・
- まず、kube-apiserver上で、認証、コマンド検証を経て、
- どのノードにも置かれていないpodが作製され、etcdも更新される。
- このタイミングではpod(というかコンテナ)のimageがビルドされるわけではなく、etcdにpodのYAMLを格納したことをもってpodが作製されたと言っていそう
- kube-schedulerがAPI serverを巡回して、根なし草のpodを見つけると、どのノードにその新podを置くのが適切かをkube-apiserverに教える。
- kube-apiserver がetcdを更に更新し、workerノードにいるkubelet(船長)にpodの情報を伝える
- worker nodeにいるkubeletが、自身のnode上にPODを作成し、コンテナのイメージをビルドする
- worker nodeのkubeletが、masterのkube-apiserver にpod作成完了の旨を伝え、kube-apiserverはetcdの情報を「配置済み」と更新する
- 要するに、kube-apiserverはクラスタ内を何かいじろうとした際にいつも中心にいるやつなのだ
- etcdと直接やり取りするのもkube-apiaserverのみ。
- kube-apiserverの仕事まとめ
- 認証
- リクエストの検証
- etcdとのデータの取得・アップデート
- schedulerとのやり取り。shcedulerにせっつかれてkube-apiserverが仕事をする
- workerにいるkubeletとやり取りする
- まあ、とはいえ、普通にkubeadmでk8sを構成した場合、こんなことを気にすることはまずない。
- kubeadmでk8sを構成した場合、kube-apiserverはkube-sysmtemというnamespaceの中のpodとして作られる。
- まあpodの中で1serviceが動いている、という感じなのかも。そういう意味ではこれもserviceだが、podにラップされている
- podのmanifestは、
/etc/kubernetes/manifests/kube-apiserver.yaml
にあるから、見てみると良い。
- kubeadmでk8sを構成した場合、kube-apiserverはkube-sysmtemというnamespaceの中のpodとして作られる。
- 逆にスクラッチで構築する場合は、kube-apiserverをバイナリとしてcurlでダウンロードしてきて、serviceとして動かすことができる。
- kube-apiserverをサービスとして起動するときの引数はいろいろだが、重要なのは
- 証明書たちのファイル名。この証明書があるおかげで、master nodeとworker nodeがセキュアに通信することができる?
- etcd serverのURL。etcdの章で「
kube-api serverが参照するURLと対応させる必要がある。
」と言っていたのがこれ。etcdのサービス起動時に決めた?自分のURLをkube-apiserverにも知ってもらう必要がある
- このservice版apiserverの場合、yamlは
/etc/systemd/system/kube-apiserver.service
においてある- あるいは、せっかくサービスとして動いているので、
ps -aux | grep kube-apiserver
としても、起動時の実行時引数を確認することが出来る
- あるいは、せっかくサービスとして動いているので、
- kube-apiserverをサービスとして起動するときの引数はいろいろだが、重要なのは
kube-controller-manager
- masterにいるcontrollerたちを束ねる親玉がcontroller manager。
- controllerはmasterの各部署みたいなもの。それぞれにそれぞれの使命がある
- リソースたちのステータスを見る
- 状況を理想の状態に修正する
- node-controller:
- 各ノードの状況をモニタリングして、理想の状態になるように何かあれば手を加える。
- 当然この営みはkube-apiserver経由で行われる
- モニタリングは5秒ごと。
- 40秒間ヘルスチェックが途絶えると、そのnodeは異常と見なされ、unreachableとして認識される。
- unreachableと認識された後、5分間はnodeが復帰する時間の猶予を与えることにしている。(eviction timeout)。たいていは再ランやVMHA的な復帰機能が仕込まれていることを期待して。
- 5分経ってもunreachableのnodeが復活してこなかった場合、そのnodeにいるpodは生きているnodeに移される。
- このあたりの数字は、下記のkube-controller-manager serviceの起動オプションとして指定できる。上記はデフォルト値。
- 各ノードの状況をモニタリングして、理想の状態になるように何かあれば手を加える。
- replication-controller
- podがreplicasetの数だけ常に存在することを保証する役目。
- 何かの拍子で数が減ったら新しく作って配置する
- 他にもいろいろあるが、基本的にk8sのリソースはhogehoge-controllerによって実装されていると思ってよい。
- 色々なコントローラーがいるが、実際にはkube-controller-managerという一つのプロセスにまとめられてservice(若しくはpod)として動いている
- 上記のヘルスチェックがらみの時間がオプションとして指定できる
node-monitor-period
とか
- また、どのコントローラーを有効にするかを指定する
--controllers
オプションも存在する。- デフォルトでは
*
、つまり全部起動する。
- デフォルトでは
- 上記のヘルスチェックがらみの時間がオプションとして指定できる
- kubeadmでk8sを構成した場合、相変わらずkube-controller-managerもkube-system namespace上の1podとして配置されている。
- 自力でk8sを構築した場合、kube-controller-managerはmaster node上のserviceとして動いている。
- yamlは
/etc/systemd/system/kube-controller-manager.yaml
- serviceなので、起動オプションは
ps -aux | grep kube-controller-manager
でも確認できる
- yamlは
kube-scheduler
- podとnodeのスケジューリングを担当
- 船の例だとコンテナを船に乗せる吊り機みたいな書かれ方をしているが、あくまでkube-schedulerが担当しているのは「podをどのnodeに載せるかの決定」だけで、実際にpodをnodeに載せるのはkube-apiserverがやっている
- 「このpodはどのnodeに置くのが最適かな~」をどうやって判断するのか。
- まずpodの要件から候補のnodeをフィルタする
- 仮に配置した際のnodeの残りの余力(空きCPUとか)から残った候補をランクづけし、一番ランクが高かったnodeにpodを配置
- nodeを決定する際に参照されるのは以下
- resource requirements and limits
- taint, tolerations
- node selector / affinity
- これもkubeadmで構築したときはmasterのpodとして、手動でインストールしたときはserviceとして存在する。yamlの位置もいつものディレクトリにある。
kubelet
- nodeの船長
- クラスタの一部でいるため事務処理を全部こなすのが仕事
- nodeとmasterの通信はkubeletが行う。
- shcedulerから指示されたとおりにコンテナの積み込み/積み下ろしを実施
- schedulerからkube-apiserver経由で指示が入ると、kubeletがnode内のdockerに指示してコンテナ(pod)をpull, runさせる
- 状況を一定間隔でmasterに報告。ヘルスチェックとかもそう
- そうしてできたpodの状況・nodeの情報を報告
- shcedulerから指示されたとおりにコンテナの積み込み/積み下ろしを実施
- kubeletは、kubeadmを使っても自動でインストールはされないので、必ず手動で各worker nodeに一つ一つインストールしていく必要がある。
- バイナリをDL・解凍・service起動
- ということは、他と違ってkubeletがpodとして配置されることはないのか?そもそもnodeごとに起動すべきものなので、もしpodとして建てようとすると「workerを管理するnamespace」内で名前が衝突しそう。
- serviceとして起動するので、気になるなら
ps -aux | grep kubelet
で稼働状況がわかる- kubeletだけ必ずserviceらしい。
kube-proxy
- 異なるnodeにいるpod同士が通信できる必要がある(「POD network」)
- WebAPL PODからDB PODにアクセスする際は、DB PODのserviceを作成しておき、WebAPL PODからserviceが持つIP or URLにアクセスすることでDB PODに振り分けてもらえる。
- serviceがうまく機能するように実際に頑張っているのが、各nodeのkube-proxy。
- serviceとして登録されたエンドポイントとしてのIPと、実際のPODのIPをiptablesでNAT変換している
- podのIPは可変なので別の不変のエンドポイント(service)を使いますとはいっても、そうはいってもpodのIPは必要なわけで、それを感じさせないように頑張っているのがkube-proxy。
- serviceの更新が入る・serviceに紐づいたPODが増減すると、いちいち全worker nodeのkube-proxyの情報を更新する必要があるため面倒な役目ではある
- kebe-proxyは
- kubeadm構築ではkube-system namespace 内のpodとして配置
- deamonset(すべてのnodeに同一のpodを配置する枠組み)としてデプロイされているため、どのnodeにいるkubeproxyも同一。
- NATの情報は全nodeで揃えておく必要があるため、確かにそうあるべき
- 自力でDLした場合はservice (名前が紛らわしいがこれはsystemctlのservice)
- kubeadm構築ではkube-system namespace 内のpodとして配置
POD入門
# kubectl run <podにつける名前> --image <もとになるコンテナイメージ> kubectl run nginx --image nginx kubectl get pods
この時点ではnodeからしかアクセスできない。ユーザーに公開するにはserviceとかの設定が必要
apiVersion: v1 # kubernetesのAPIバージョンを記載 kind: Pod # 作りたいリソースによってkindとapiVersionの組は決まっている metadata: # metadataはdictionary name: myapp-pod # nameはString。これはpod名 labels: # labelsはdictionary app: myapp type: front-end #好きなだけ好きなものを書いてよい spec: # dictionary containers: # List/Array型:pod内に複数コンテナが乗ることを想定して - name: nginx-container # これはコンテナ名 image: nginx #pullするイメージ名 - name: busybox #2つ目を連続して書いてもよい image: busybox
- apiVersionとkindはStringで記述するが、metadataは辞書型
- 一度yamlを書いてしまえば、
kubectl create -f pod-definition.yml
でpodを作成できる
lab
https://uklabs.kodekloud.com/topic/practice-test-pods-2/
#create a new pod with the nginx image kubectl run nginx --image nginx # Which nodes are these pods placed on? kubectl get pods -o wide # Delete the webapp Pod. kubectl delete pod webapp
- Create a new pod with the name redis and with the image redis123.
# まずyamlファイルを作る。わざわざ自分で最初から書く必要はないのだ・・・。 # helpを読む限り、-dry-runの値はclientにしておくのが無難。そもそもdry-runにオプションとは?という感じ。dry-runのオプションを省略した場合はclientを指定したとみなされるため、別に単に`--dry-run`としてもよい。 # まあ、それを言ったらdry runせずにそのままやっても条件は満たせそうな気がするが。・・ kubectl run redis --image=redis123 --dry-run=client -o yaml > redis-definition.yaml # 作ったyamlをもとにcreate。 # runがコマンドから直接pod生成するコマンドで、create, applyはyamlから作成する、と言った使い分けだろうか。 kubectl create -f redis-definition.yaml
- Now change the image on this pod to
redis
.
# yamlを作っているなら、修正後、apply kubectl apply -f redis-definition.yaml # yamlがない、直接runして作ってしまった場合は kubectl edit pod redis
Replicaset 入門
Replication controller
- replication controller とreplica setは微妙に違う概念であることに注意
- replication controllerは古い。後発のreplica setで代替された技術。
- が、内容はほぼ同一。
- podの個数の監視
- podの数が減った場合、数合わせのpodを作成
- まず最初に、replication controllerを見ていく。
apiVersion: v1 kind: ReplicationController metadata: name: myapp-rc labels: app: myapp type: front-end spec: # replication contorllerのspecはtemplateとreplicasの2つ template: metadata: # replicateしたいpodの定義をそのまま書く。セレクタで指定とかしないのか・・・ name: myapp-pod labels: app: myapp type: fornt-end spec: containers: - name: nginx-container image: nginx replicas: 3 # replica setの場合、この次にselectorが必須だが、replication contorllerの場合は任意。省略した場合、podのmetadataで定義したlabelがselectorとして認識される。
上記でyamlファイルを作成して、
kubectl create -f rc-definition.yml kubectl get replicationcontorller kubectl get pods # pod名の先頭にreplication controllerのnameがつくことを確認 # 折角podのmetadataで定義したnameはどこ行った?
- 次、replica set
- replica setは、replica setが生成したpodでなくても、個数を管理できることが特徴。ただし勝手にpodを生成するにしても、selectorをつけてreplica setが認識できるようにしておくこと。
apiVersion: aapps/v1 # replication contollerから変更 kind: ReplicaSet metadata: name: myapp-rc labels: app: myapp type: front-end metadata: name: myapp-rc labels: app: myapp type: front-end spec: template: metadata: name: myapp-pod labels: app: myapp type: fornt-end spec: containers: - name: nginx-container image: nginx replicas: 3 selector: # replica setではselectorの定義が必須 matchLabels: type: front-end
結局replicasetもreplication contorllerもほとんど変わらない・・・
- selectorの定義が必須か必須でないか(replica setは必須)
- 勝手に定義したpodの面倒も見てくれるか(replica set は見てくれる)
の2点だけが異なると思ってよい。
上記でreplica set を作成したら、
kubectl create -f replicaset-definition.yml kubectl get replicaset kubectl get pods # pod名にreplicasetという直球の名前が入る。
replica setのselectorが各pod のlabelを参照するため、selectorとlabelの整合性に注意。replica setのselectorと、podのmetatadaに記載したlabelが一致しない場合、
kubectl create
するときに怒られるわざわざreplica set内でpodの定義を書く必要があるのだろうか?podのimageに対するポインタだけ持っておけばよいのに。現行のpodの定義と、replica setで記載したpodの定義がずれていた場合、フェイルオーバーすると定義の異なるpodが混在することになるがそれでいいのか?
- 良いのかも。ロールオーバーできそう。
replicationの個数を変更したいときは。
- ymlを編集して、
kubectl replace -f replicaset-def.yml
- replaceは初めて見たな・・・applyじゃないのか。
kubectl scale --replicas=6 -f replicaset-def.yml
でも可。kubectl scale --replicas=6 replicaset myapp-replicaset
でも可。- type, nameの順に指定。
- ymlを編集して、
replica setのコマンドまとめ
# rs作成 kubectl create -f replicaset-definition.yml # rs作成後確認 kubectl get replicaset # rs削除(podも削除される)。type, nameの順 kubectl delete replicaset myapp-replicaset # スケーリング1。applyではないことに注意(podの更新はapplyだった)。 kubectl replace -f replicaset-definition.yml # スケーリング2。ファイルを指定しているもののファイルのreplicasの個数は更新されないため注意。マジ? kubectl scale -replicas=6 -f replicaset-definition.yml
lab
- 既存のreplicasetのpod imageが間違っているため修正して再リリースしたい。
# 元になるyamlが存在しないため、editでreplicasetを編集する kubectl edit replicaset new-replica-set # podのimageをbusybox777 -> busyboxに修正し、現存のpodを手で全部消す kubectl delete pod 4つ並べて書く # もしくは、現行のreplicasetの定義をyamlで吐き出して修正->既存のreplicasetは消して新しいものをcreateでも行けそう。(要検証)
- replicasetの数を変更
# 1.editして更新。 kubectl edit replicaset # 2.scaleする kubectl scale --replicas=2 rs new-replica-set
- ちなみに、
create
,delete
,scale
とかでリソースを選択するとき、rs new-rs
とする派とrs/new-rs
とする派がいるが、どっちがいいんだろう?helpにはrs/new-rs
の書き方で書いてあるが、こっちは補完が効かないため微妙・・・
Deployments
- replica setで定義されたpod達に対する上位概念としてdeploymentsが存在
- ローリングアップデート・ロールバックを可能にするのがでかい。単に複数個の存在を保証するだけのreplicasetとは一線を画す機能なので、敢えて下位/上位概念としてreplicasetとdeploymentを分けたのか。諸学者には大変分かりづらいが
- yamlの定義はreplica setとほぼ同じ。kindをReplicaSet -> Deploymentにするだけ。
kubectl cretate -f deployment-definition.yml kubectl get deployments kubectl get replicaset kubectl get pod # 全部みたいなら kubectl get all
- tips
lab
Create a new Deployment with the below attributes using your own deployment definition file.
Name:
httpd-frontend
; Replicas:3
; Image:httpd:2.4-alpine
# deploymentのyamlはcreateで作ることに注意。多分pod以外は全部create(podのみrun) # kubectl create deployment --helpを見ればほぼ答えが書いてある kubectl create deployment httpd-frontend --replicas=3 --image=httpd:2.4-alpine --dry-run=client -o yaml > new-dep.yml kuebctl create -f new-dep.yml
service
- 要するにロードバランサ。
- 通信にまつわるresource。
- podとユーザー、pod同士、podと外部データレイクも通信を橋渡しする
- リソース同士の疎結合に一役買っている
- エンドユーザーからすると、nodeに対してcurlすると、nodeの中にいるpodから応答が返ってきてほしい
- nodeとpodを仲介するミドルが欲しい->service
- nodeのportをlistenして、期待されるコンテナのip:portに振り分ける仕事をする。(この例はnodeport)
- podやreplicasetと同じ、オブジェクト。
- serviceはvirtual server見たいなもので、それ自身がIPアドレスを持っている(cluster IPと呼ばれる)
serviceの種類
- nodeport
- nodeのportにアクセスすればpodにフォワードされる仕組み
- 設定内容:
- その他tips:
- cluster IP
- service自身が持つIP。勝手に設定される。serviceの種類としてのIPとややこしい・・・
- node のIPは設定不要。各nodeで、nodeportに設定したIPが確保される
- cluster IP
- clusterIP
loadBalancer
- nodeportをエンドユーザにどう開放するか?を考えた際に、
- エンドユーザとk8sの間にload balancer(HA proxyとか)を立てて、名前解決してもらう
- 若しくは、loadbalancerを使う。yamlのtypeをLoadbalancerに変えるだけ。ほんとに?
- クラウドプラットフォームしかサポートしていないことに注意。普通に自前のVirtualBox上でやるとnodeportとしての機能しか果たさない
- nodeportをエンドユーザにどう開放するか?を考えた際に、
nodeportのyamlの記法
apiVersion:v1 kind:Service metadata: name: myapp-service spec: type: NodePort ports: - targetPort: 80 # フォワードしたいpodが開放しているポート。 port: 80 # serviceの(pod向かいの?)ポート nodePort: 30008 # nodeが外部に公開しているport selector: # matchLabels:は要らないのか? app: myapp type: front-end
- cluster IPのyaml
apiVersion: v1 kind: Service metadata: name: backend spec: type: ClusterIP # 省略すると自動的にclusterIPににある ports: - targetPort: 80 port: 80 # serviceの(pod向かいの?)ポート selector: app: myapp type: back-end
lab
- kubernetesがデフォルトで持つserviceを見てみる。
namespace
- 役割
- 文字通り、単に名前空間を分けることと、
- policyを使って権限制御することが目的。
- 各namespaceに対してquotaを設定可能
- 最初からあるnamespace
- default : デフォルト。ユーザーに開放しているデフォルトのnamespace
- kube-system : 制御用。普段使うことはない
- kube-public : すべてのユーザに開放したいリソースが置かれるnamespace
# 自分のnamespaceに存在するdb-serviceにアクセスするなら、単に以下で良いが、 mysql.connect("db-srevice") # よそのnamespaceのdb-serviceにアクセスする場合は以下。 # service name, namespace, service, domain(cluster.local) の順 mysql.connect("db-service.dev.svc.cluster.local")
# 何も指定しないとdefault namesapceのpodが表示される kubectl get pods # namespaceを指定することもできる kubectl get pod --namespace=kube-system # pod生成時も同様。省略するとdefaultになる kubectl create -f def.yml --namespcae=dev # あるいは、yamlファイルのmetadata にnamespace: devと書いておけばcreate時に指定は不要
- namespace のyaml
apiVersion: v1 kind: Namespace metadata: name: dev
上記を書いて、kubectl create -f namespace-dev.yml
すればOK.
- あるいは、単に
kubectl create namespace dev
でも可。
# 現在のnamespaceの確認。どちらでも。 kubectl config get-contexts kubectl config view -minify | grep namesapce: # namespaceを永続的に変更したいときは以下。どちらでも。 kubectl config set-context $(kubectl config current-context) --namespace=dev kubectl config set-context --current --namespace=<insert-namespace-name-here> # namesapceの公式ページを参照
- ついでにresource quotaのyaml についても書いておくと、
apiVersion: v1 kind: ResourceQuota metadata: name: compute-quota namespace: dev spec: hard: pods: "10" requests.cpu: "4" requests.memory: 5Gi limits.cpu: "10" limits.memory: 10Gi
lab
- finace namespaceにredisのpodを作れ
kubectl run redis image=redis --namespace=finance kubectl run redis image=resid -n=finace # でもOK # podなのでcreateではなくrunを使う。 # kubectl run "pod" redis image=redis --namespace=financeとかにしないように。
- 全てのnamespaceのpodを表示する
kubectl get pod --all-namespaces kubectl get pod -A
imperateive(命令的) vs declarative(宣言的)
命令的:
過程を一つ一つ指示する
yamlを使わない諸々の
kubectl
がそう。簡単なのが売りなので試験では重宝する、がhistoryが消えるとなくなるので注意
# create object kubectl run --image=nginx nginx kubectl create deployment --image=nginx nginx kubectl expose deployment nginx --port 80 # update object kubectl edit deployment nginx # ローカルは更新されない、前の状況も残せないので注意 kubectl scale deployment nginx --replicas=5 kubectl set image deployment nginx nginx=nginx:1.18 kubectl create -f nginx.yaml # すでに存在するときにやると失敗するので宣言的とは言えない kubectl replace -f nginx.yaml # まだない時にやると失敗するので宣言的とは言えない kubectl delete -f nginx.yaml
宣言的:
試験役立ちtips
# pod kubectl run nginx --image=nginx --dry-run=client -o yaml # deployment kubectl create deployment --image=nginx nginx --replicas=4 --dry-run=client -o yaml kubectl scale deployment nginx --replicas=5 #serviceは命令的記法だとセレクタ周りが怪しいため注意。podの名前とlabelが現状同一のもののみうまく働く # 簡単にclusteIP作るだけならこれがいちばんらくちん # service : portとあるが実際にはtargetportであることに注意 kubectl expose pod redis --port=6379 --name redis-service --dry-run=client -o yaml kubectl create service clusterip redis --tcp=6379:6379 --dry-run=client -o yaml ## exposeではnodeportは指定できないため、作った後にeditが必要 kubectl expose pod nginx --type=NodePort --port=80 --name=nginx --dry-run=client -o yaml # createならOK kubectl create service nodeport nginx --tcp=80:80 --node-port=30080 --dry-run=client -o yaml
lab
# pod: labelもつけれるぞ! --label="app=myapp, tier=db"など kubectl run redis --image=redis:alpine --labels="tier=db" # portも指定可能。serviceを作るのではなく、コンテナのportを開放するだけ。 kubectl run custom-nginx --image=nginx --port=8080 # clusterIP作るならこれが便利 kubectl expose pod redis --port=6379 --name=redis-service # nodeportとかを作るときはややこしいので注意。 # service kubectl create deployment webapp --image=kodekloud/webapp-color --replicas=3 # namespace kubectl create namespace dev-ns
Create a pod called
httpd
using the imagehttpd:alpine
in the default namespace. Next, create a service of typeClusterIP
by the same name(httpd)
. The target port for the service should be80
.# 1 kubectl run httpd --image=httpd:alpine kubectl expose pod httpd --port=80 --name=httpd # 2 : exposeをつけると、指定したportでcluster ipを同時に作成する。 kubectl run httpd -image=httpd:alpine --port=80 --expose=true
kubectl apply
- 以下の3つを参照してapplyの挙動を決める
- localが更新されると、live configをまず更新して、last applied configもそのあと更新される
- last applied configが必要な理由は、local fileから何が削除されたかがわかるようにするため。
- live configを直接見れば済む話ではないのか...?
tips
- 段々yamlが煩雑になってきたので、yamlを書かないようにするのも手。
kubectl create hogehoge --dry-run=client -o yaml > hogehoge.yml
でyamlを生成できるので、これをひな型にして、編集したものをkubectl create -f hogehoge.yml
とすると試験に役立つ。- podのyaml生成時はrunでyaml作るみたいな空気があった。
- run(pull + create + start)はそもそもpodを動かすためのものなので。helpにpodの話しか書いてない
- deploymentの生成時はcreateでやるっぽい...?
--replicasは
createじゃないと指定できないことに注意。- 無理やり
kubectl run deployment
でyamlを作るとpodができてた
kubectl
に使えるリソースの略称はkubectl api-resource
から確認できるyamlを定義する際に書いてほしい内容は
kubectl explain <resource>(deploymentとか)
に書いてある。特にapiVersionとkindのチェックに便利。podの仕様のうち
- オートスケーリングの機能に相当するのが、deployment とreplica set
- replicasetは単に個数を保証するだけだが
- deploymentはローリングアップデート・ロールバックまで実施してくれる
- ロードバランスの機能に相当するのがservice。
- オートスケーリングの機能に相当するのが、deployment とreplica set
参照するなら
- get -o wide
- describe
- get -o yaml
命令的に書くとき
podは
run
。run = pull + create + start。- podしか使わないので、
kubectl run pod mypod
ではなくkubectl run mypod
でOK
- podしか使わないので、
他はcreate。pod以外のリソースはstartとかないしね。
宣言的に書くとき(yamlを使う)
- 全部create
3 Scheduling
manual scheduling
podを作成すると、裏でschedulerがどのnodeにpodを配置するかを考えていて、実はschedulerがyamlのspecに
NodeName
という項目を書き足している。もし割り振るべきnodeが存在しなかった場合 or shedulerが存在しなかった場合、podは
pending
の状態になる。- schedulerがいないなあと思ったら
kubectl get pod -n kube-system
を見てみると良い。
- schedulerがいないなあと思ったら
配置するnodeを自分で決めたい場合は、自分でpodのyamlに
nodeName: node02
とかを書いてから起動すればよい。ただしpod作製時(create)にしかnodename は編集できない。- pod(container)の実態はnodeで動くプロセスだから。動いているプロセスを別のnodeに持っていくのは難しい。
ので、一回作ってしまったら消してyaml書き換えて作りなおすしか動かす方法はない。
delete->create
か、replace--force
を検討。apiVersion: v1 kind: pod metadata: name: nginx spec: containers: - image: nginx name: nginx nodeName: node01 # この行を書き足す
もしpod起動後に配置されているnodeを鞍替えしたい場合は、podのyamlを直接編集するのではなく、podのbindを記述したyamlを作成し、postリクエストをpod binding API(何?)に投げる。
lab
# shcedulerがいないのでpodがpendingのままになっている # yamlにnodeName: node01を書き加えてpodを再起動、delete->create でもよいが、replace --forceがスマート # --forceをつけると既存のpodがimmedeately remove される # ・・・別に無くても良い?(immedeateではないにせよ) kubectl replcae -f nginx.yml # 何回もget pod するのは面倒なので、--watchオプションをつけると変化が見やすい。 kubectl get pod --watch # なんならこれでもよい watch kubectl get pod
label, selecor
yamlのmetadtaに
label
のセクションがあるkubectl get pod --selector app=App1
--label
ではないので注意
replicaset, serviceでも、どのpodを対象にするかを判断するため内部的に利用されている
labelと同格で、annotationというのもある。podのバージョンとかを記載。
apiVersion: apps/v1 kind: ReplicaSet metadata: name: simple-webapp labels: app: App1 function: Front-End annotations: #これがannotation builderversion : 1.34 spec: ...
lab
# labelの表示 kubectl get pods --show-label # 絞り込み kubectl get pods --selector end=dev # 沢山絞り込み # --selectorの省略形が-lという謎 # label同士は間隔を開けずに書く必要あり kubectl get pods -l env=prod,bu=finance,tier=frontend # 個数を数えたいとき kubectl get pods --selector end=dev --no-headers | wc
taints(汚れ) and tolerations(寛容)
- podが配置されるnodeに対する優先条件を決める
- 「nodeにtaint(汚れ)がついていても、podにtolerationがあれば配置できる」
- taintはpodのスケジューリングを拒否するnodeの仕組み
- node側から拒否できる手段はtaintのみ
- tolerationは、nodeについているtaintを無視してスケジュールを可能にするpodの仕組み
- taintが機能するのはわかるが、tolerationの方はschedulerがnodeを探索する順に依存していて、tolerationを持ったpodが必ずしもtaintを持ったnodeに配置されるわけではないような気がするが・・・
- その通り。あくまで、「もしtaintをもったnodeがあるなら、そのnodeには何もいないか、いたとしてもtolerationをもったpodのみ」というだけ。
- あくまでtolerationはtaintに対する例外処理的な位置づけで、podをこのnodeに配置したい!という場合はaffinityを利用すること。
- taintが機能するのはわかるが、tolerationの方はschedulerがnodeを探索する順に依存していて、tolerationを持ったpodが必ずしもtaintを持ったnodeに配置されるわけではないような気がするが・・・
- masterにpodが配置されることがないのは、実はk8s構築時にmaster nodeに対してNoScheduleのtaintがつけられているから。
Taints: node-role.kubernetes.io/master:NoSchedule
- value省略でkey:taint-effect型
- tolerationの設定方法(to node)
# taint-effect は3種 # ・NoSchedule: 文字通り # ・PreferNoSchedule: 努力目標で配置しない # ・NoExcecute: 配置しない。すでに配置されているpodはevictする。配置後の追い出しに便利 # taintにおけるkeyvalueは、あくまで設定したtaintに対する識別子で、特定のpodに拒否を効かせるものではない # pod のtolarataionよりも先に設定すること! # 運用していてこのnode微妙だな~となったらtaint設定して、podがいなくなったところでnode切り離しとか。 # kubectl taint nodes <node-name> key=value:<taint-effect> # valueは任意。なくてもよい kubectl tiant node node1 app=blue:NoShedule # 確認したいときは kubectl describe node node1 | grep Taints # 消したいときは最後に"-"をつける kubectl tiant node node1 app=blue:NoShedule-
- tolerationの設定方法(to pod)
apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: containers: - name: nginx image: nginx tolerations: #全部""が必要。jsonとの互換性?取ってつけたような設定だな・・・ - key: "app" operator: "Equal" value: "blue" effect: "NoShedule"
node selector
podをこのノードに配置したい!というときには選択肢が二種類あって
1:node selector
podのspecに
nodeSelector
を書き足すnodeにlabelをつけておき、podがそのlabelをnode selectorで選ぶ
replicasetとかserviceとかは、podについてるlabelをみてどのpodを管理するかを判断していたが、今度はpodが(nodeを)選ぶ立場。
# pod側 apiVersion: kind: pod metadata: name: myapp-pod spec: containers: - name: data-processor image: data-processor nodeSelector: # これ size: Large # 最初にやったnodeNameは特定のnode本体を指定してしまう非推奨な手段
# node側 # kubectl label nodes <node-name> <label-key>=<label-value> kubectl label nodes node-1 size=Large
簡単だが応用が利かない。size=large or midium のnodeに載せたいとき、size!=smallのノードに載せたいときはどうするか?が次。
2:node affinityは次で・・・
node affinity
- このノードにpodを配置したい!を実現する2つ目。
- great power comes great complexity...
- 以下は上記のnode selectorと同じ意味になる定義
# pod側 apiVersion: kind: pod metadata: name: myapp-pod spec: containers: - name: data-processor image: data-processor affinity: nodeAffinity: requiredDuringShedulingIgnoredDuringExecution: #スケジューリングの時だけ考慮 nodeSelectorTerms: - matchExpression: - key: size operator: In # valueで指定するリストのどれかに当てはまればtrueを返す。今は1個だけ values: - Large
- large or medium nodeに振り分けたければ、
spec: affinity: nodeAffinity: requiredDuringShedulingIgnoredDuringExecution: #スケジューリングの時だけ考慮 nodeSelectorTerms: - matchExpression: - key: size operator: In # valueで指定するリストのどれかに当てはまればtrueを返す。今は1個だけ values: - Large - Medium
- not small nodeに振り分けたければ
spec: affinity: nodeAffinity: requiredDuringShedulingIgnoredDuringExecution: #スケジューリングの時だけ考慮 nodeSelectorTerms: - matchExpression: - key: size operator: NotIn values: - Small # key=smallを持つnodeが仮に存在しなくてもエラーにはならない
- key が存在するかどうかだけを見たいなら
spec: affinity: nodeAffinity: requiredDuringShedulingIgnoredDuringExecution: #スケジューリングの時だけ考慮 nodeSelectorTerms: - matchExpression: - key: size operator: Exists
- 運用面で、podがスケジュールされた後に、nodeのlabelが書き換えられたとして、そのafiinity で振り分けられたpodの処遇はどうなるのか?
- →node affininty typeで定義されている通りで、Ignoreする。affinityによって割り振られた後は、podはその後自分のnodeslectorとnodeのlabelの対応がどうなろうが気にしない。
- requiredDuringSchedulingIgnoredDuringExecution
- scheduling時:nodeslectorで指定したnodeがない場合はpendingになる?
- その後:気にしない
- preferredDuringSchedulingIgnoredDuringExecution
- scheduling時:nodeslectorで指定したnodeがない場合、しゃーなしで適当なnodeに割り振る。affinityは無視
- その後:気にしない
- (planned) requiredDuringSchedulingRequiredDuringExecution
- podが配置された後もpodのnodeselectorとnodeのlabelを気にしておき、マッチしなくなったら即evictする。
- requiredDuringSchedulingIgnoredDuringExecution
lab
# nodeのラベルを見る kubectl get node node01 --show-labels kubectl describe node node01 # nodeにラベルを付ける kubectl label node node01 color=blue # deploymentが管理するpodにnode affinity をつける # 取ってきた方が早い # https://kubernetes.io/ja/docs/concepts/scheduling-eviction/assign-pod-node/ # podのspecに書くので、containersと同格になる。
taints vs node affinity
もし、
- blue node : blue pod
- green node : green pod
- red node : red pod
- other node 1,2 : other pod 1,2
みたいにしたい場合、
- 色付きnodeにtaint付与
- 色付きpodにtoleration付与
- 色付きnodeにlabel付与
- 色付きpod にnode affinity 付与
で実現できる。taints, tolerationsで変なものが入ってくることを防ぎ、node affinityでpodがどっかに言ってしまうことを防ぐ。a>=b and a<=b みたいな感じだな・・・
resource requirements and limits
- podが必要とするcpu, memory, diskをyamlに書いておくことができる。
- 書いておくとschedulerが空気を読んでリソースに余裕のあるnodeに振ってくれる
- requirementのデフォルトは0.5 CPU, 256MiB
- 下限の設定に加えて、上限も設定可能。limitのデフォルトは1vCPU, 512MiB
- デフォルト、と言っているが、実際にはnamespaceに設定されている値を参照しているので、namespaceのyamlを書き換えればデフォルトの値を変更可能。
apiVersion: v1 kind: Pod metadata: name: myapp labels: name: myapp spec: containers: - name: myapp image: myapp ports: - containerPort: 8080 resources: # pod単位ではなく、podに内包されるコンテナ単位に要求値を設定できる requests: # 下限 memory: "1Gi" # 2^n単位のやつ。 cpu: 1 limits: # 上限 memory: "2Gi" cpu: 2
- 1 cpuという単位は、AWSで言うvCPUであり、GCPで言うGCP coreであり、Azureで言うAzure Coreのこと
cpuがlimitを超えそうになったらk8sがうまいことcpu消費を減らさせるが、メモリが超過した場合はpodがterminateされる。(即消されるわけではないが)
tips
lab
- podの終了理由あれこれ
- OOMKilled : メモリ超過により内部プロセスがpodを削除
- podの状態あれこれ
- CrashLoopBackOff : podを起動しようとして落ちて、k8sが再度起動しようとしてまた落ちて・・・を繰り返す状態。
- ImagePullBackOff : imageのpullに失敗してリトライを何度も繰り返している状態
#podのyamlを書き換えた後、podを再起動したければ、 kubectl delete pod elephant kubectl create -f hoge.yaml #でもよいが kubectl replace --force hoge.yaml #でもOK。replaceオプションは即時podをdelete and re-create する
daemon set
- 各nodeに1個ずつ作る
- nodeが増減した場合も勝手にやってくれる。
- 用途
- モニタリング。各ノードのログの収集、送信とか
- 各nodeに必要なkube-proxyはdaemon setで各ノードに配布されている
weave-netもdaemon setと相性がいい(後述)
yamlはreplicaset ・deploymentとほとんど同じ。replicasの個数を抜いて
kind: DaemonSet
にするだけ。
apiVersion: apps/v1 kind: Daemonset metadata: name: monitoring-daemon spec: selector: matchlabels: app: monitoring-daemon template: metadata: labels: app: monitoring-daemon spec: containers: - name: monitoring-daemon image: monitoring-daemon
- 昔は実装はnodenameを使って各podに行き先を直接教えて各nodeに行かせていたが、今はaffinityをうまいこと使ってやっている。
lab
#daemonset はcreateで作れないのが難点だが、deploymentのyamlのkindを変えてrreplicasを追記するだけでOK。 # 同様に、replicasetもdeploymentのyamlのkindを変えるだけでできるので覚えておくとよい。 #と思ったが、更にいくつか要らない定義を消す必要があった。。。
static pods
- masterとnodeの分担としては、
- masterが、いつ何のpodを保持してほしいかをnodeに指示
- nodeは言われたようにpodを保持
- なので、nodeのkubeletの振舞としては
- 渡されたyamlからpodを生成し
- podが生成されている状態を保つこと
- を仕事としている。
- なので、逆に言うと、適切な方法でkubeletにyamlを渡せば、masterの介入なしにkubeletにpodを生成させることが可能。
- 具体的には、nodeの/etc/kubernetes/manifests配下にyamlを置けば、kubeletがこのフォルダを定期的に巡回して勝手にpodを作ってくれる。このように作られたpodをstatic podと呼ぶ。
- 普段masterのkube-apiserverからyamlをもらうときは、masterのapi-server経由で受け取っているため、このディレクトリを日常的に活用しているわけではない。
- あくまでnode単体で生成できるのはpodのみ。replicasetとかdeploymentとかはmasterの機能が絡んでくるため、nodeだけでは完結できない。
nodeはpodのレベルでしか動作せず、podまでしか理解できないことを示す良い例。
yamlを配置するディレクトリは別に
/etc/kubernetes/manifests
に固定ではない。kubeletの起動オプション--pod-manifest-path
で指定可能。ps-aux
を見よ。あるいは、起動オプションに
--config=kubeconfig.yaml
として、kubeconfig.yamlにstaticPodPath: /etc/kubernetes/manifests
とすることも可能。(kubeadmで構築するとこの方式になっている)
static pod作成後は、node上で
docker ps
するとpodの稼働状況がわかる。- master (のkube-apiserver)が不在の場合、
kubectl get
は使えないため注意。
- master (のkube-apiserver)が不在の場合、
ちなみに 、master不在の前提で話していたが、ふつうにmasterが存在した場合も、nodeローカルのstatic podとkube-apiserver経由のpodは同時に存在することができる(kubeletがどちらも作ってくれる)し、masterから
kubectl get
した場合もstatic podも一緒にリストする。- ただし、masterから
kubectl get
して見えるのはnode上にあるstatic pod本体ではない。masterは、static nodeの存在を検知すると、そのコピーをmasterに(etcdに?kubeapiserverにと言っているように聞こえる)作成していて、kubectl get
はこっちを参照している。なので、static podをmasterからedit, deleteすることはできないことに注意。 - static podは、
kubectl get
するとstatic-web-node1
みたいに、配置されているnode名が名前につく。
- ただし、masterから
- 用途:
- controll plane(master?)そのものをpodとして作るとき。
- を配置すれば、あとはkubeletがmaster として必要なサービスをpodとして作ってくれる。kubeletが管理しているので止まる心配もない。
- kubeadmを使用してk8sを構成すると、apiserver等はpodとして作成されると言ったが、あれはkubeadmが上記のstatic-podを使って最初にapi-server podを作っているから。
kubectl get pods -n kube-system
すると、- etcd-master
- kube-apiserver-master
- kube-contorller-manager-master
- kube-scheduler-master
- のように末尾に
master
がつくが、これはstatic podのネーミングルール:末尾にノード名を入れる を反映している。
- static pod vs daemonset
- static podはkubeletによって生成されるが、daemonsetはkube-apiserverに生成される
- static podはcontroll planeのコンポーネントを作るために使われる、daemonsetはノードのモニタリング・ロギングに使われる
- static podもdaemonsetも、kube-schedulerからは無視される、ノータッチであることは共通している。
lab
- static podは何個?
ps-aux | grep kubelet # --config=/var/lib/kubelet/config.yaml のオプションを確認 cat /var/lib/kubelet/config.yaml | grep staticPodPath # staticPodPath: /etc/kubernetes/manifests であることがわかる ls -l /etc/kubernetes/manifests/ # 計4件ある。
重要コンポーネント(contollplane?)の要素まとめ
- etcd : static pod
- kube-apiserver : static pod
- kube-contoller-manager : static pod
- kube-scheduler : static pod
- coredns : deployment
- kube-proxy : daemonset
- kube-flannnel-ds : daemonset
Create a static pod named
static-busybox
that uses thebusybox
image and the commandsleep 1000
# commandは最後に持ってくることに注意。dryrunを後にすると、sleepの引数として扱われてしまい、podが作られてしまう kubectl run static-busybox --image=busybox --dry-run=client -o yaml --command -- sleep 1000 > /etc/kubernetes/manifests/busybox.yaml
- node01に作ったstatic podを削除しろ
# masterからはどうにもできない。まずはnode01に入ること。 # IPアドレスでも可。kubectl get nodes -o wideのinternal ip参照 ssh node01 cat /var/lib/kubelet/config.yaml | grep staticPodPath # staticPodPath: /etc/just-mess-you であることがわかる rm /etc/just-mess-you/pod.yaml # 10秒ぐらい待つとmaster側で消える。node01での削除がmasterのミラーに反映されるのに時間がかかる? kubectl get pod
Multiple Sheduler
今まで、k8sのschedulerがいかに便利かを勉強してきたが、
実は独自のアルゴリズムを持つschedulerを自分で作って、特定のpodのスケジューリングに使うこともできる。
podとかdeploymentとかをデプロイするタイミングで、どのschedulerを使うか指定することができる ```yaml apiVersion: v1 kind: pod metadata: name: nginx spec: containers:
- image: nginx name: nginx schedulerName: my-costom-scheduler # ここで指定できる。podに対して指定するのでcontainersと同じ高さ ```
schedulerの名前の付け方
service版: schedulerのserviceを動かすタイミングで、
--scheculer-name=defalut-scheduler
のように名前を指定できる。pod版(kubeadm版): ```yaml apiVersion:v1 kind: Pod metadata: name: my-custom-schduler namespace: kube-system spec: containers:
- command: - kube-scheduler - --address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true # 複数masterでschedulerが起動しているときに使うオプション # 両方起動して片方だけ使用する。 # 単一masterならそもそもfalseにすべき。複数ならtrueで - --scheduler-name=my-custom-scheduler # これを追記すればOK - --lock-object-name=my-custom-scheduler # leader-electをtrueにするなら(=複数masterなら)これも必要 image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3 name: kube-scheduler
```
どのスケジューラが動作したか確認する方法
kubectl get events
- クラスタ全体のイベントを確認。(-> podのログかも?)
my-coustom-scheduer
起因でpodのスケジューリング(nodeへの割り当て)が発生したことがわかる。
kubectl logs my-custom-scheduler --namespace=kube-system
- スケジューラそのもののログを確認できる
lab
よくわからん。要復習。
configmap 、clusterrolebinding, serviceaccount作ったが、何に使ったのかはわからない。
とにかくpodのyamlに schedulerName: my-costom-scheduler
を書けば独自スケジューラを使ってスケジューリングできる
tips
- daemonset, replicaset, deploymentは従兄弟ぐらいの関係性。
kubectl create
できるのはdeploymentだけなので、daemonsetとreplicasetを作りたいときは、deploymentとしてcreateでyamlを作って、そこからkindの修正(daemonsetの場合はreplicasをけす)をすればOK。- と思ったが、更にいくつか要らない定義を消す必要があった。。。
remove the replicas, strategy and status fields
- と思ったが、更にいくつか要らない定義を消す必要があった。。。
- daemonsetとreplicasetはどうも公式のページの情報量が多すぎていけてない・・・
- 各nodeのipは
-o wide
で分かるぞ
4 logging and monitoring
monitor k8s cluster
- k8sにはビルトインの良いモニタリングが存在しない(マジ?)
- HEAPSTAR:ビルトイン・非推奨
- やりすぎ?
- Metrics Server :
- クラスタに一台入れればOK
- インメモリ・揮発性であることに注意。古いデータも見れない。
誰が各ノードのmetricsを集計するのか?
→kubeletのサブコンポーネントである、cAdvisor (container Adviser)
cAdvisor
- nodeのmetricsをkube-apiserverを通じて公開する →metrics serverから集計可能に!(metrics serverに都度送っているわけではない?)
導入方法:
minikube :
minikube addons enable metrics-server
だけでOK
その他、普通のk8s
git clone https://hoge kubectl create -f deploy/1.8+/
導入が済んだら、以下が叩けるようになる ```bash kubectl top node # linuxのtopコマンドと同様 kubectl top pod
kubectl top pod --sort-by-"cpu" kubectl top pod --sort-by-"memory" ```
logging
まずdockerのlogについて
docker logs -f ecf
でecf
コンテナに関するログが見れる-f
は標準出力に表示するオプション。ログが画面に流れる
k8sのログ
kubectl logs -f pod名
でpodが持つコンテナに関するログが見れる- あくまで
docker logs
のラッパー機能だと思った方が良い。
- あくまで
ここでも
-f
は標準出力に表示するオプション。ログが画面に流れる# podが一個しかコンテナを持たないときは、単にpod名の指定でOK kubectl logs -f event-simulator-pod # podが複数コンテナを持つ場合は、コンテナ名まで指定する必要がある # あくまでdocker logsのラッパーだと思った方が良い。 kubectl logs -f event-simulator-pod event-simulator
5 application lifecycle management
rolling update とrollback
rollout
サービス(デプロイメント)をまるっと更新することをrolloutと呼んでいる。リリースに近い概念。
kubectl rollout status deployment/myapp-deployment kubectl rollout history deployment/myapp-deployment
rolling update
- 新しいバージョンとちょっとづつ入れ替えていく手法。
- recreate : 全部消してから全部作る、の対になる概念
実際にupdateする方法:
kuebctl apply -f deployment-def.yml
で自動的にロールアウトが始まる。- imageをupdateするだけなら、
kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1
でもよいが、edit
と同様、手元のyamlが更新されないことに注意。
動きの違い
- recreate
- replicasetが0にスケールダウン → replica setが元の値までスケールアップ
- rolling update
- 新しい空のreplica setを作る
- →もとのreplica setのpodの数を少しずつ減らし、その分新しいreplica setのpodを個数を増やす
- →最終的に新しいreplicasetのpodの個数が最大数に到達し、元のreplicaset は空になる
- ...最終的に元のreplicasetは削除されない?
kuebctl rollout undo
のために残しておくのか?
- recreate
ロールバックする場合も上記と同様。
- コマンドは
kuebctl rollout undo deployment/myapp-deployment
- コマンドは
コマンドまとめ
# create kubectl create -f deployment-def.yaml # get kubectl get deployments # update kubectl apply -f deployment-def.yaml kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1 # <コンテナ名>=<イメージ名>の順。あまり使わないかも...editで足りる # status kubectl rollout status deployment/myapp-deployment # rolloutの様子をwatchする感じ kubectl rollout history deployment/myapp-deployment # バージョン履歴 #rollback kubectl rollout undo deployment/myapp-deployment
lab
kubectl get deployment frontend -o yaml > frontend.yaml vi frontend.yaml kubectl apply -f frontend.yaml # ただしもともとk8sが知らない(etcdにない?)ものをapplyしようとすると怒られるので注意。 # イメージを変更 kubectl set image deployment <deployment名> <container名>=<新image名> # 普通にeditでもrolloutははじまる。 kubectl edit deployment frontend kubectl rollout status deployment/frontend # 早くしないとrolloutが終わってしまうので素早くたたく
試験には出ないけどやる(?)
- アプリケーションのcommandとargumentsの設定
- 環境変数の設定
- secretの設定
dockerのcommand, argument(引数)
dockerの仕様として、
docker run <image>
された後、実行すべきプロセスを終えたらexit状態になる。コンテナが何を実行するか、はdockerfileの
CMD
に書いてある。仮に
docker run ubuntu
した場合、ubuntuが実行するbash
は自身とくっつくターミナルを見つけられず即終了するので、コンテナとしても即exited状態になる。コマンドを上書きしたい場合、
docker run ubuntu sleep 5
とかする。永続的に実行コマンドを上書きしたい場合。新しいdockerfileを作る。
FROM ubuntu CMD sleep 5 # 無ければ足す、あるなら上書き、という感じなのか? # CMD ["sleep", "5"]でも可
docker build -t ubuntu-sleeper .
docker run ubuntu-sleeper
引数は都度指定したい場合、CMDではなくENTRYPOINTを使う。
FROM ubuntu ENTRYPOINT ["sleep"]
docker build -t ubuntu-sleeper
docker run ubuntu-sleeper 10
エントリポイントのデフォルト値を指定:
FROM ubuntu ENTRYPOINT ["sleep"] CMD ["5"]
要するに・・・
- ENTRYPOINTには、省略しないコマンドを記載
- CMDには、省略時のデフォルト値を記載
ただし、コンテナ実行時にENTRYPOINTをどうしても変えたい場合は、
--entry-point
で上書き可能docker run --entry-point sleep2.0 ubuntu-sleeper 10
k8sのcommand, argument(引数)
まず下記のdockerfileで
docker build ubuntu-sleeper .
を作ったとして、FROM ubuntu ENTRYPOINT ["sleep"] CMD ["5"]
引数の上書き:
docker run ubuntu-sleeper 10
をk8s podでやりたい場合、以下のようにする。apiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-pod spec: containers: - name: ubuntu-sleeper image: ubuntu-sleeper args: ["10"] # dockerfileのCMDを上書き
コマンドの上書き:
docker run --entrypoint sleep2.0 ubuntu-sleeper 10
をやりたい場合apiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-pod spec: containers: - name: ubuntu-sleeper image: ubuntu-sleeper command: ["sleep2.0"] # dockerfileのENTRYPOINTを上書き args: ["10"] # dockerfileのCMDを上書き
ややこしい・・・がdockerfileの書き方がいけてないのが悪い。k8sはよく正した。
lab
sleep 5000
するubuntuのコンテナを作れapiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-2 spec: name: ubuntu-sleeper-2 image: ubuntu-sleeper-2 command: ['sleep', '5000']
apiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-2 spec: name: ubuntu-sleeper-2 image: ubuntu-sleeper-2 command: # リスト形式もOK - "sleep" - "5000"
sleep 2000
に変えろapiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-2 spec: name: ubuntu-sleeper-2 image: ubuntu-sleeper-2 command: # リスト形式もOK - "sleep" - "2000"
kubectl replace --force -f ubuntu-sleeper-3.yaml
コンテナのコマンドライン引数に
--color=green
を渡すpodを作れapiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: webapp-green name: webapp-green spec: containers: - image: kodekloud/webapp-color name: webapp-green args: ["--color=green"] # これだと`--color=green`を渡す # ちなみに・・・ args: - "color" - "green" # とか、 args: ["color","green"] # で指定すると、`--color green`を指定することになるが、コンテナがどちらも受け入れてくれるのでOK
k8s pod の環境変数(environment variable)の設定
containers
にenv
フィールドを書く。環境変数はkey value型であることを思い出す。apiVersion: v1 kind: pod metadata: name: webapp spec: containers: - name: webapp image: webapp ports: - containerPort: 8080 env: - name: APP_COLOR #keyではない... value: pink - name: APP_MODE value: prod
命令的に書くときは、
docker run -e APP_COLOR=pink webapp --image=webapp
環境変数はこのようにpodのyamlに直書きすることもできるが、
configmap から取ってくる
env: - name: APP_COLOR valueFrom: # valueではない! configMapKeyRef:
secretから取ってくる
env: - name: APP_COLOR valueFrom: # これもvalueではない! secretKeyRef:
こともできる。(あくまで環境変数1個だけ入れる場合。configmap, secret丸ごと注入する場合は以下)
configmap
①config mapを作る
命令的(imperative)な方法
# コマンドに環境変数を直書きする場合 # kubectl create configmap <config名> --from-literal=<key>=value kuebctl create configmap app-config --from-literal=APP_COLOR=red # 複数指定するなら、単に--from-literal を連ねればよい kuebctl create configmap app-config --from-literal=APP_COLOR=red --from-literal=APP_MODE=prod # 環境変数をメモ書きしたテキストから作る場合 # kubectl create configmap <config名> --from-file=<file名> kubectl create comfigmap app-config --from-file=app-propety.txt # app-propety.txt APP_COLOR=red APP_MODE=prod
宣言的(declaretive)方法
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: # data!!! specではない APP_COLOR=red APP_MODE=prod
kubectl create -f <ファイル名>
②podに注入する
configMapを丸ごとpodに注入する場合
apiVersion: v1 kind: pod metadata: name: webapp spec: containers: - name: webapp image: webapp ports: - containerPort: 8080 envFrom: - configMapRef: name: app-config # 環境変数の名前ではなく、config mapの名前そのものを書く。
configmapに書いてある環境変数を一個だけ入れたい場合
spec: containers: - name: webapp image: webapp ports: - containerPort: 8080 env: - name: APP_COLOR # configmapのkeyとは別名を付けられるようになっているが二度手間感 valueFrom: configMapRef: name: app-config key: APP_COLOR
volumeのfileとして存在させたい場合
volumes: - name: app-config-volume configMap: name: app-config
各APPに応じていろんなconfigMapを作ることが想定されるので、命名は慎重に...
lab
既存の環境変数直書きpodをconfigmapに直せ
kubectl get pod webapp-color -o yaml > webapp.yaml vi webapp.yaml kubectl replace --force -f webapp.yaml # 出力しなくても、 kubectl edit pod webapp-color # 環境変数は編集禁止なので保存できず/tmp/配下に保存されるが、yamlはこれで手に入るので、 kuebctl replace --force -f /tmp/hoge # でもOK.
secret
- configmapとほぼ同じ。値がそのものではなくハッシュ値が保存されるだけ。
- いくつか種類がある。
- Opaque:ユーザーが勝手に定義したもの
- 他にも、サービスアカウントのトークン、認証の資格証明など...
- ①secretの作成
- 命令的:configmapと同じ
- 宣言的も同じ...だが、パスワードをテキストに書いてしまっては意味がないので、
echo -n "mypassword" | base64
してハッシュ化したものをyamlに書く(?)。- 複合は
echo -n "bfu-ve" | base64 -d
で。
- 複合は
- ②secretをpodに注入
- 略
lab
secret定義されている項目は何個あるか?
kubectl describe secret default-token-tcf7h # secretは値が表示されないので注意。 # valueはNbyteと書かれたり、トークンはそのまま書かれたり...とにかく:の個数を見ればよい。
secretのvalueは何か?
# 実はsecretに設定されている値は見ようと思えば見れてしまう kubectl get secret default-token-tcf7h -o yaml | grep namespace # namespace: ZGVmYXVsdA== # base64でデコード。echo -nで改行を抜くのを忘れずに! echo -n "ZGVmYXVsdA==" | base64 -d # defaultとわかる。
secretを作れ
multi contaier POD
マルチコンテナpodのデザインパターン3種
- sidecar :appと、elasticsearchにログを送るためのsidecar
- adapter
- ambassador
CKADの範囲なので、詳しくは触れない・・・
init container
本命podを起動する前にやっておきたい処理をinitcontaierにやらせる。
DBpodの起動を待ってからApp podを上げたいときなど。
until nslookup myservice; do echo waiting for myservice; sleep 2; done;
で、他のpodの起動を待つことができる
最初にリポジトリからDLしたいときにも。
initContainerが成功するまで、podは再起動してリトライし続ける
複数initコンテナがある場合は、順番に実行される
lab
kubectl get pod
にはinitContainerはカウントされないことに注意。init container実行中はpendig状態で表示される
init containerでcrashしている場合、podのログを見てもわからない。init contaierのログを見ること。
# 以下ではinitcontaierのcrashの原因はわからない kubectl logs orange(pod) # -c でinitcontainerを指定する kubectl logs orange -c init-myservice
Self Healing Applications
- 要するに、replicaset とreplication controller( replicaset の旧式)のおかげで、何かのpodがcrashしてもすぐ代替が生まれるようになっているよ。という話。
- liveness, readinessなどのヘルスチェックの概念。
- これもCKADの範囲のため、割愛。
6 cluster maintenance
OS のアップグレード
- nodeが5分オフラインになると(livenessの応答がなくなると?)、k8sはnodeが死んだと判断し、node上にいたpodは終了させられる
- →replicasetが別のnodeにpodを立ち上げる。
- pod eviction timeout と呼ばれている
- ただし、replica set が設定されていないただのpodは、再スケジュールしてもらえない...
「5分」はkube-controller-managerの設定項目。
ちょっとだけnodeをクラスタから切り離したいときにどうするか。
→`kubectl drain node-1
- replicasetで制御されているpodは、他のnodeに配置されるようになる。
- そうでない単発のpodがいる場合はdrainできない。
--force
で無理やり実行した場合、podは消えて、再スケジュールされることはない。- 既存のpodを全部殺す + 新規のスケジュールも禁止。ドレインという名前なのでnode上のpodを吸い取って他nodeに渡しているように聞こえるが、実際にはpodを殺すだけ。一応良心として、単発のpodがいないかどうか一回確認してくれるのは良いところ。
nodeを復活させる(再スケジュール許可令を出す)には
kubectl uncorden node1
drain
でよそのノードに飛ばされたpodは戻ってこないことに注意。- 新たにpodをスケジュールしようとすると、(リソースに余裕のある)node1に配置されるようになる
# node-1 にいたpodを別のノードに退避させる(厳密にはpodを殺してreplicasetに再スケジュールさせる) & node-1 にはpodがスケジュールされないようにする kubectl drain node-1 # node-1 のreboot等 # node-1のスケジュール禁止令を解く。 kubectl uncordon node-1
# cordenもある。単に、これからノードにpodがスケジュールされることを禁止する。既存podの退避はしない kubectl cordon node-1
lab
node01をメンテナンスしたい。
# 普通にkubectl drain node01 すると怒られる。daemonsetはデフォルトではdrainは手を出せない。 # 通常nodeにはflannel(ネットワークのやつ) + kube-proxyがいる # daemonsetも無理やり消す場合は以下 kubectl drain node01 --ignore-daemonsets # node01にいたpodが全部別nodeに退避される # ただし、無視指定したdaemonsetは、kubectl get 上ではまだnode01にいるように見える。 kubectl get node NAME STATUS ROLES AGE VERSION controlplane Ready control-plane,master 31m v1.23.0 node01 Ready,SchedulingDisabled <none> 30m v1.23.0 # drainするとstatusがscheduling disabledになる。
k8sのバージョン
- 例えば
kubectl get node
すると書いてある。あれはnodeのkubeletのバージョン - 基本は以下のバージョンはそろっている
- kube-apiserver
- controller manager
- kube-scheduler
- kubelet
- kube-proxy
- kubectl
- 他のバージョンはそろっていない
- etcd
- coredns
- 古いkubelet とか
k8sクラスタのアップグレード
- 別に全部のバージョンがそろっている必要はない。
- kube-apiserver が中心。こいつのバージョンが高い分には良いが、kube apiserverが他より低いことは許されない
kube-apiserverが1.10とすると、
- 他のmaster : contorller manager, kube schedulerは1.9 or 1.10 がOK
- node : kube proxy, kubeletは1.8 or 1.9 or 1.10がOK
- kubectlは例外的に1.09~1.11までOK
- アップデートするときの順番に注意!
k8sでは常に3種のリリースしかサポートされていない。
- 1バージョンずつアップデートするのが推奨されている。(というかkubeapiでさえ一気に2つあげるとkube proxyとkube schedulerが違反する)
アップデートする方法は3つ
GKEとかならクリックするだけ
kubeadm upgrade plan
orkubectl upgrade apply
また、まず最初にkubeadmのバージョンを上げる必要がある
各nodeの
kubelet
だけは必ず手動でアップデートする必要があるため注意。- あくまでkubeadmで構築していて、kubeletがmasterにある場合のみ。kubeadmで構築した場合、kube-apiserverとかのcontroll plane上のstatic podとして構築されていたことを思い出そう。
# master : # まずkubeadm apt-get upgrade -y kubeadm=1.12.0-00 # upgrade。kubeapiserver, controllermanager, schedulerが更新 kubeadm upgrade apply v1.12.0 # kubeadmで構築していた場合、masterのkubeletも更新。controll planeはkubeletがstatic podとして生み出したものなのに、kubeletの更新が後なのか... apt-get upgrade -y kubelet=1.12.0-00 systemctl restart kubelet # 次にnode : 一個ずつやる場合 kubectl drain node-1 # drainはmasterでやる ssh node-1 # node-1へ 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 exit # masterへ戻る kubectl uncordo^n node-1 # uncordonもmasterでやる
手動入れ替え(hard way)
アップグレードのセオリー
lab
# 公式サイトを見る前に、もし自分が使っているOSのバージョンがわからなくなったら cat /etc/*release* # クラスタのバージョンを確認 kubectl get node # あくまでノードのkubeletのバージョン。他のバージョンはどうやって確認するのか? -> 例えば単にkubectl describe pod -n kube-system kube-controller-manager でわかる # nodeの数 kubectl get node # podをhostできるnodeの数 kubectl describe node #してtaintが全くないことを確認。 #利用可能なバージョンを確認 kubeadm upgrade plan # 最新版を教えてくれるが、あくまで現時点のkubeadmが知っているバージョンであることに注意。 # kubeadmが知っている内容以上にアップデートするには、まずはkubeadm自体をupgrade する必要がある。 # masterからupgradeする。まずmasterをdrain kubectl drain controllplane --ignore-daemonsets # 作業後、kube-apiserverとかは表示上contollplaneにあるように見える。というか、controllplaneをdrainした時点でkubectlに応答するkube-apiserverが死んでいるので、過去の情報を出しているだけの可能性がある。 # masterのupgrade。 公式サイトの記載の通りに頑張る。 1.kubeadm のupgrade 2.kubeadm upgrade applyでcontrollplaneの各種コンポーネントをアップグレード # 公式ではここでdrainしていた 3.kubeletとkubectlをアップグレード 4.systemctl restart kubectl 5.kubectl uncordon controlplane # workerのupgrade 0.kubectl drain node01 # ssh node01する!!!! 1.kubeadmのアップグレード 2.kubeadm upgrade node # nodeでは、kubectl upgrade applyの代わりにnode。実際にはkubeletの設定を少しいじるだけらしい。 # 公式ではここでdrainしていた 3.kubeletとkubectlをアップグレード 4.systemctl restart kubectl # exitしてmasterに戻る 5.kubectl uncordon node01
バックアップ
一部命令的に作ってしまったリソースがあったとしても、
kubectl get all -A -o yaml > all-deploym-services.yaml
でyamlのバックアップを取れる。
「VELELO」等、バックアップツールに頼るのもよい。
あるいは、etcdそのものをバックアップするのもよい。
etcd起動時のパラメタとして、データを保存する
--data-dir
を定義しているので、このディレクトリ配下をバックアップすればよい。etcd自体の機能として、snapshotを取る機能も存在する
ETCDCTL_API=3 etcdctl snapshot save snapshot.db ETCDCTL_API=3 etcdctl snapshot status snapshot.db # リストア # まずkube-apiserverを止める(etcdが再起動するとapiserverがびっくりしてしまう) service kube-apiserver stop # その後、リストアを実施する。 # 既存のものと混同しないよう、新規でクラスタを作成する。 # data-dirには、リストアして生成されるファイル群の場所を指定。 ETCDCTL_API=3 etcdctl snapshot restore snapshot.db --data-dir /var/lib/etcd-from-backup # daemon-reload後、etcdを再起動して復活。--data-dirには、先ほど指定したリストア用ディレクトリを再び指定。 systemctl daemon-reload service etcd restart # 最後に、止めていたapiserverを再起動する service kube-apiserver restart
etcdctlのtips
ver3のetcdctlを使うため、最初に以下を設定しておくとよい
export ETCDCTL_API=3
- 単に
etcdctl
と打つと、ETCDCTL_API=3
と打ってね、と書いてくれている。
- 単に
もしetcdのTLS暗号化が有効になっている場合、以下のオプションが必須になることに注意。
--cacert : --cert --endpoints --key
lab
etcdのバージョン:
kubectl describe pod -n kube-system etcd-contorlplane
etcdのエンドポイント
kubectl describe pod -n kube-system etcd-contorlplane | grep listen-client-url # --listen-client-urls=https://127.0.0.1:2379,https://10.40.12.3:2379
デフォルトでは
http://127.0.0.1:2379
etcdのサーバー証明書
kubectl describe pod -n kube-system etcd-contorlplane | grep client-cert # --cert-file=/etc/kubernetes/pki/etcd/server.crt
etcdのCA証明書
kubectl descrie pod -n kube-system etcd-controlplane | grep trusted-ca-file # --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
etcdでsnapshot取得
etcdctl snapshot save /opt/snapshot-pre-boot.db \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ --endpoints=https://127.0.0.1:2379 # めんどくさ... # snapshotを取るとき(=etcdにアクセスが必要な時)に証明書が必要。
etcdのバックアップからリストア
# 講義ではkubeapiserverのstop が必要と言っていたが、別にやらなくてもいいらしい... # まずバックアップしたスナップショットからデータをリストア # 今のdata-dirは kubectl describe で判断可能 etcdctl snapshot restore /opt/snapshot-pre-boot.db --data-dir=/var/lib/etcd-restored # etcdのstatic podの定義を書き換えて、data-dirの参照先をもとの参照先/var/lib/etcdから/var/lib/etcd-restoredに書き換える # static podのmanifestの場所:/etc/kubernetes/manifestはkubeletが知っている vi /etc/kubernetes/manifests/etcd.yaml # hostpathの/var/lib/etcd -> /var/lib/etcd-restored # data-dirと、volumemountのmountpathは、別に変える必要はない。etcdのコンテナ内の話のため。変えてもよいが、変えるなら同時に変えること。 # static podなので、勝手にetcdが更新されて、諸々のdeploymentが立ち上がってくる。 # もし立ち上がってこなかった場合、kubectl delete pod etcd-controlplane -n kube-systemする。
7 security
セキュリティ基礎
kube-apiserver の守りを固めることが重要
kube apiserverは各種コンポーネント(etcd, controller manager, scheduler, kube proxy, kubelet等)と通信するが、すべての通信がTLS暗号化されている。
デフォルトでは、すべてのpodはお互いにアクセス可能だが、network policyで制限も可能
authentication
- 前提として、管理者、開発者、botがアクセスしてくることを仮定する
- エンドユーザーはアプリケーション自身で認証する
- 管理者・開発者はuser。
- 設定方法は口述。kubectlではない!
botはservice account。
- これは
kubectl create serviceaccount
で作れる
- これは
userの設定方法
①ID, pw
簡単なID, PWのファイルを用意しておく ```
パスワード, ユーザー名, ユーザーID, (グループ名)
password123, user1, u0001, group1 password123, user2, u0002, group2 ```
このファイルをkube-apiserverの起動オプションに指定しておく。
--basic-auth-file=user-details.csv
- apiserverがstatic podの場合、
/etc/kubernetes/manifests/kube-apiserver.yaml
にある
- apiserverがstatic podの場合、
反映するときはkube apiserverの再起動が必要。
- ただし、kubeadmでk8sを構築していて、apiserverがpodとして実装されている場合は、パスワードファイルを更新すると自動的にapiserverが再起動されるようになっている
curl -v -k https://master-node-ip:6443/api/v1/pods -u "user1:password123"
でcurlに直接ユーザーを指定してアクセスすることもできる。結局、サーバーの認証をどうするか、みたいな話になっている。
②ID, トークン
ID, PWとほぼ同じ。 ```
トークン, ユーザー名, ユーザーID, (グループ名)
aoirehgpaeirg, user10, u0010, group10 ```
これもkube apiserverの起動オプションに指定しておく。
--token-auth-file=user-details.csv
curlで指定するときはヘッダとして書く。ちょっと難しい。ID不要でトークンだけ指定。
bash curl -v -k https://master-node-ip:6443/api/v1/pods -header "Authorization: Bearer aoirehgpaeirg"
TLS in k8s
まず、全部で三種類証明書があることを覚えておこう。
各コンポーネントの役回り(サーバ?クライアント?)に応じて、必要な証明書が異なる(?)、というかサーバ証明書・クライアント証明書がそれぞれ必要になるため、まずはコンポーネント間の通信について確認していく。
まずはサーバー
次、クライアント側
- admin (我々)
- クライアントとしてkube api serverに通信しに行く
- admin.crt
- admin.key
- クライアントとしてkube api serverに通信しに行く
- scheduler
- kube controller manager
- クライアントとしてkube apiserverに通信しに行く。apiserverを利用して各リソースの管理を行っている
- contorller-manager.crt
- contorller-manager.key
- クライアントとしてkube apiserverに通信しに行く。apiserverを利用して各リソースの管理を行っている
- kube-proxy
- これもクライアントとしてkube apiserverに通信しに行く。役割がいまいちよくわからない・・・
- kube-proxy.crt
- kube-proxy.key
- これもクライアントとしてkube apiserverに通信しに行く。役割がいまいちよくわからない・・・
- kube -apiserver
- kubelet
- admin (我々)
-
- ルート証明書
- ca.crt
- ca.key
- 汎用版とmetcd用に特別にもう一つ用意して2つ体制にすることもある。
- ルート証明書
証明書の生成
認証局(CA)
# 秘密鍵の生成 (公開鍵は同時に生成されない) openssl genrsa -out ca.key 2048 # csr作成(まあ自分で署名するのだが...) openssl req -new -key ca.key -subj "/CN=KUBERNETES=CA" -out ca.csr # 自己署名 (認証局なので) openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
note : ブラウザにルート証明書が入っているように、認証局の証明書は各クライアントに配布しておく必要があることに注意。 認証局が具体的に存在するわけではなく、「認証局のルート証明書が発行され、各利用者に配布されていること」が重要。
クライアント証明書
# 秘密鍵の生成 openssl genrsa -out admin.key 2048 # csr作成 openssl req -new -key admin.key -subj "/CN=kube-admin" -out admin.csr # 認証局の証明書で署名 openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt # 通常は認証局に署名を依頼するが、ここではクライアント地震で認証局の秘密鍵を使って署名してしまう。それでいいのか?本来上記のコマンドはCAが実行するコマンド
更に、ユーザーの中でも、管理者と一般ユーザーを差別化するために、管理者かどうかの情報をCSRに含めておく必要がある(後述)。管理者かどうかの情報は証明書の
O
:組織名に書くことになっている。 ```bash管理者用証明書のcsr作成
openssl req -new -key admin.key -subj "/CN=kube-admin/=system:masters" -out admin.csr ```
使い方は2通り
一つは単にコマンドのオプションに含める方式
bash curl https://kube-apiserver:6443/api/v1/pods \ --key admin.key --cert admin.crt --cacert ca.crt
admin.crtは、クライアント証明のために送る。ca.crtは、サーバから送られてきたサーバ証明書のチェーン検証のために送る(サーバはサーバでクライアント証明書の検証にca.crtを使うが、これはサーバにも事前に配布されているはず。)。admin.keyは、サーバがクライアント証明書を使ってレスポンスを暗号化して送付してきた後に、手元でその内容を複合するために使う。
もう一つは、kube-config .yamlに書いておく方式。これならいちいちコマンドに書かなくてよい。(後述)
admin以外、controller managerとかschedulerとかも同様だが、証明書の名前に
system:
接頭辞をつけることに注意。system:controller-manager
など。
-
etcd
- 冗長構成を取るためにpeer-証明書を別途作る必要がある。
bash --key-file --cert-file --trusted-ca-file --peer-key-file --peer-cert-file --peer-trusted-ca-file
- 冗長構成を取るためにpeer-証明書を別途作る必要がある。
kube api server
本当の名前はube apiserverなのだが、k8sで中心的な役割を果たす関係で、いろんな別名で呼ばれることがある
kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local IP address
これを設定するには、
openssl.cnf
の[alt_names]
に別名を全部書いておく必要がある。(実際はSANs: subject alternative names に指定しているだけ)。加えて、csr生成時に-config=openssl.cnf
を指定する。
kube apiserverは、サーバとしての側面と、etcd, kubeletに対するクライアントとしての側面を持つため、計9個の証明書の指定が必要 ```bash --client-ca-file --tls-cert-file --tls-private-key-file
--etcd-cafile --etcd-certfile --etcd-keyfile
--kubelet-certificate-authority --kubelet-client-certificate --kubelet-client-key
あまりにも統一感がない・・・何とかならなかったのか。
```
kubelet
- サーバ証明書
- 証明書の名前はノード名になる
- 各ノードの
kubelet-config.yaml
に、証明書三点セットの所在を書いておく。
- クライアント証明書(apiserverにつなぎに行く)
- 証明書名は
system:node:node01
- グループ名に
SYSTEM:NODES
を付与する(nodeとしての権限付与のため)
- 証明書名は
- サーバ証明書
証明書の確認
kubeadmで構築した場合を想定。
kube apiserver
- static podなので
/etc/kubernets/manifests/kube-apiserver.yaml
を見ると、各証明書のありかがcommand
として書かれている
- static podなので
証明書が適用がうまくいっていないときは、以下で確認できる ```bash kubectl logs hoge
場合によってはkubectl 自体応答しなくなることもあるので、dockerまでレイヤを下げる必要があることも
docker ps -a | grep etcd docker logs
127.0.0.1:2379ときたらetcdだと気がつけるべき!!!
```
lab
証明書の中身を見たいときは
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -text -noout
証明書のAPI
CA はどこにいるのか?
- 実体はない。CAのオレオレ証明書と鍵が本体。CA証明書と鍵の保管は重要。
- masterが証明書の保管場所になっているので、masterがCAサーバ。
certificate API
管理者が増えた・証明書の有効期限が切れた際に、いちいちmasterサーバーにログインして認証局にCSRを提出させていては管理が面倒くさい
# 新規参入者が自分の秘密鍵を作成 openssl genrsa -out jane.key 2048 # CSRを作成 openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr # csrのyamlを作成すると、csrのオブジェクトができる # このときyamlにはbase64 endcodeされたcsrを記載する。もともと読めないのに更に読めなくなる... # 管理者はcsrオブジェクトを閲覧、承認できる kubectl get csr kubectl certificate approve jane
certificate APIを提供しているのはcontroller manager のCSR-APPROVINGとCSR-SIGNING
- contorller manager が実質的な認証局の役割を果たすため、controller managerの起動オプションには、ca.crtとca.keyの位置を指定するオプションが存在する。
lab
csrオブジェクトを作成する
apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest metadata: name: akshay spec: request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZV3R6YUdGNU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTFPcVNZaFZrcDJ2M3RiL21ieEF6SnVaMTdqc1VDQWtvVkhtT3BORGIwdDM2CklpNE03bXdneUpoQ2pla1FLVFRoY2xNYk42Y3QwM00yVFhzLzRUcktlTHpIMFZsd1dGZW1hM20yTGFFWlFYMG0KSGxqWFZxelZJbDF6dStVY2k3QjRkTlZNWnlmMFJHdW9NQk0zMkhHbzBhOUNGWWNnS003RnFPWHRldk1Lc1ZIbQo3UTh1dld6ZUpnNVRYQkcwTVNiL3poSytZUjRTTWtybFdISktPSVlWeWpXWFhweC9rUnZyU3pocE9iNzE3ditCCjdiSy85VkFrVDJoOTJkSkNBMkNSdG5uS3ljTEozSG5zVGQwNTNucHFHMXAwd204UnNnd2h3T3E1N3BWcEVob28Kd0F3WSt6THFqWFcrSmZhOEJQUFNHMEQ0UFVxZXovS2pKMG5qZUZyN01RSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBQmlJS1pmdGFQUkFRWWZZTUsxTUI1am1RSG12ZmtkY3plUVJIWmJhZmVIQWYyVzY1dEdsCkxmbjRvbm1vS3drYjZjVnB3cUtVZHJhYkVQZXErSlVEbnNycEV1WW0xdjlXVVRPMXRMdWVZNzFFdlBnSGwzTHUKbmhiUmQ3NlJRTU1rTHRhd2VmSmlvMllVS1RUMTh3SHYwYW9EZXVQNENGbGdNY08zOW11RjhXbWNwSzVOeWU3UgowN1BKSXVzWVNTNGJOb3REK2hmSm5SUUg1NFJPUVlkdlBtNU1NeHQyNXNCSFgzYk9jYStYeXI3eGdmd05MeW1WCkprT2dYOG95NzZDb3hyZlRVaGZUZzVUL2NxcXZNOXNtMHdIS2cxMUZBdzdaaU5JVWU2bFJDajZhb2xSZ3hMb0oKdmVoN2RJUGtLZnRJK2QyK1NBc0RKUWZWUVpBbklJVkw0Snc9Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo= signerName: kubernetes.io/kube-apiserver-client #たいていこれ expirationSeconds: 86400 # one day usages: - client auth # 固定
requestには、
cat hoge.csr | base64 | tr -d "\n"
した結果を貼る。
生成されたcsrオブジェクトを確認・承認
kubectl get csr # akshayのcsrがpendingになっている # csrの中身を検査 kubectl get csr hoge -o yaml #承認 or 拒否 kubectl certificate approve akshay kubectl certificate deny agent-smith
# kubeconfig
apiserverにcurlするときは、
bash curl https://my-kube-playground :6443/api/v1/pods --key admin.key --cert admin.crt --cacert ca.crt
のように、いちいち証明書三点セットをオプションに書く必要があった。
kubectl
を打つときにも本来証明書三点セット + サーバー名を毎回書くべきだが、いちいち面倒なので、kubeconfigに書いている。
# kubeconfig fileに書かれている情報
--server my-kube-playground:6443 # これと
--cacert ca.crt # これはcluster に書く
--key admin.key # これと
--cert admin.crt # これはusersに書く
kubectl
が参照するkubeconfigファイルを明示的に指定したい場合はkubectl --kubeconfig config
- デフォルトでは、
$HOME/.kube/config
を参照する。
- デフォルトでは、
kubeconfigには、以下3つの情報を記載する
- cluster
- 本番・開発・ステージング等
- user
- 環境ごとのユーザー
- contexts
- 環境ごとにどのユーザーでアクセスできるようにするか。
admin@production, dev@google
等。ロールバインディング的な思想。
- 環境ごとにどのユーザーでアクセスできるようにするか。
- cluster
実際のkubeconfig
apiVersion: v1 kind: Config # コンテキストが複数設定されている場合に備えて、デフォルトで呼び出すコンテキストを指定できる current-context: my-kube-admin@my-kube-playground clusters: - name: my-kube-playground cluster: certificate-authority: ca.crt server: https://my-kube-playground:6443 contexts: - name: my-kube-admin@my-kube-playground # clusterとusersを結びつける。下と一致していなくてもよいが、一致させた方が望ましい context: cluster: my-kube-playground user: my-kube-admin namespace: default # 追加でcontextにnamespaceを指定しておくと、コンテキスト仕様時にデフォルトで使用されるnamespaceを指定しておくこともできる users: - name: my-kube-admin user: client-certificate: admin.crt client-key: admin.key
オブジェクトではないので、いつものように
kubectl create
しなくてよい。書くだけで勝手にkubectl
が読んでくれるmetadata, specがないので一風変わった内容。
certificate-authority
の代わりに、certificate-authoriity-data:
の後ろに証明書そのものを書くのもOK。その場合、いつものようにcat hoge.crt | base64
したデータを書く
kubectl config view
で設定内容を見ることができる。いちいち$HOME/.kube/config
を見る必要はない。- contextを変更したいときは
kubectl config user-context prod-user@production
。これにより、kubeconfigのcurrent-contextの内容も書き換えられる
- contextを変更したいときは
lab
新しくkubeconfigファイルを作成した。current contextを変更する。 ```bash
現在のcurrent contextを確認する
kubectl config view --kubeconfig=my-kube-config current-context
current contextを変更する
予想に反してset current contextではない。set-contextでcurrent contextが書き換えられる
kubectl config --kubeconfig=my-kube-config use-context research ```
新しいkubeconfigファイルをデフォルトで
kubectl
が参照するようにせよ。 ```bash一つが、単にファイルを入れ替える方法
cp -pi new-config $HOME/.kube/config
もう一つが、環境変数を設定する方法
export KUBECONFIG=$HOME/new-config ```
API groups
core api
/api
のこと(?)/api/v1/{namespace, pods, rc, nodes, pv, pvc, services, configmaps, secrets....}
と続く。
named api
- リソース:
/apis
/apps/v1/{deployments, replicasets, statefulsets...}
/networking.k8s.io/v1/networkpolicies
/certificates.k8s.io/v1/certificatesigningrequests
- verb
- 上記の各リソースに紐づくアクションとして定義される。
- たとえば
apps/v1/deployments
リソースに対するverbはlist, get, create, delete, update, watch
等
- リソース:
kube apiserverに直接curlするとき指定する。
# 単にlocalhostだと認証の問題ではじかれてしまう curl http://localhost:6443/apis -k # 認証を突破するには、curl のオプションに証明書を指定するか、 curl http://localhost:6443/apis -k --key admin .key --cert admin.crt --cacert ca.crt # kubectl proxyを経由すると、認証情報を自動で付加してくれる(kube-proxyとは別物!単に便利なプロキシというだけ) kubectl proxy kubectl http://localhost:8001 -k
authorization (認可)
- ユーザーによって、全操作を認可・参照のみ許可等使い分けたい。特定のnamespaceだけ使わせるとか。
- k8sのauthorizationの方式は全部で4つ
- node
- Attribute based AC
- role based AC
- webhook
- node(としてのauthorization)
- node authorizer
- 「nodeが管理目的でapiserverにアクセスする用途」を許可。
- node, podのread,
- pod status , node statusのwrite (apiserverに状況を報告)
- 証明書のgroupにsystem:nodeが入っている人(とnode)が対象。
- ABAC(attribute based access controll)
- ユーザーに直接attribute (属性、ここでは各操作に対する認可のリスト)を紐づける方式。
- RBACの面倒くささを取っ払ったもので簡易的だが、変更時はいちいち手で修正してapisaerverの再起動を行う必要があるなど、メンテナンス性には難がある
- RBAC
- これが標準的
webhook
- 3rd party tool
このほか、以下も存在する
- always-allow
- always-deny
kube apiserverの起動オプションにどの認証方式を有効にするかを設定するオプションがある
# デフォルトではalways allowになっている --authorization-mode=AlwaysAllow # 所望の認証方式を設定。複数設定することも可能 --authorization-mode=Node,RBAC,Webhook # 複数指定した場合、この順でチェックがなされる。もし一つでも許可設定が入っていれば、認可される。
RBAC
まずはroleのyamlを作成
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: development rules: - apiGroups: [""] resources: ["pods"] verbs: ["list", "get", "create", "update", "delete"] -appiGroups: [""] resource: ["ConfigMap"] verbs: ["create"]
yaml作成後、
kubectl create
してオブジェクトを作成
次にrolebindingを作成
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: devuser-developer-binding subjects: - kind: User name: dev-user apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.io
自分が特定の権限を持っているかどうかを確認するコマンドも用意されている
kubectl auth can-i create depolyments kubectl auth can-i delete nodes # ユーザー・namespaceを仮定することも可。いちいちログインしてから叩く必要はない。 kubectl auth can-i delete nodes --as dev-user --namespace test
tips
pod全体に対する認可では飽き足らず、特定のpodに対する認可も指定することができる
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: development rules: - apiGroups: [""] resources: ["pods"] verbs: ["list", "get", "create", "update", "delete"] resourceNames: ["blue", "orange"]
lab
現在のauthorizationの設定
kubectl describe pod -n kube-system kube-apiserver | grep -authorization-mode
role, rolebindingを作れ
kubectl create role developer --namespace=default --verb=list,create,delete --resource=pods kubectl dcreate rolebinding dev-user-binding --namespace=default --role=developer --user=dev-user
それか、サイトを参考にyamlを自分で書く
できたら
kubectl auth can-i delete pods --as dev-user --namespace default
で確認
特定のリソース名を持つpodのみ認可したい
- 確認は
kubectl auth can-i get pods/dark-blue-app --as dev-user --namespace blue
- 確認は
create deployments
を許可するyamlをかけapiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: developer namespace: blue rules: - apigroups: ["apps"] # api refを確認。pod等coreとあるものは[""]で省略可だが、それ以外は省略不可 resources: ["deployments"] # 全部arrayであることに注意 verbs: ["create"]
cluster role binding
roleはnamespaceに属するリソースに対する許可設定を行っていたが、namespaceに属さないリソースに対して同様のことを行うにはどうするか?-> cluster role binding
namespace に属さないresourceというのは、
node
persistent volume
namespaceそのもの
certificatesigningrequests
kubectl api-resources --namespace=true kubectl api-resources --namespace=false # でリソースがnamespaceに属するか属さないかわかる
lab
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: # 「namespace」はClusterRolesがNamespaceに属していないため、省略されています name: secret-reader rules: - apiGroups: [""] # ここに書く値はkubectl api-resourcesで分かる resources: ["secrets"] # ここの正式名称もk api-resourcesで verbs: ["get", "watch", "list"]
service accounts
- userは人が使うもの
- userに相当するリソースは存在しない!!
- クライアント証明書など、自己証明ができることをもってユーザーと見なす。
- service accountはシステムが使うもの。
# サービスアカウント生成と同時に、トークンが生成・関連付けされている kubectl create serviceacount dashboard-sa # トークン自体はsecretとして別途保存されている kubectl describe secret dashnoard-sa-token-kbadm # トークンそのものをcurlのヘッダにつければ、認証されたユーザとしてアクセスもできる
プロメテウスとか、jenkinsをpodとして建てる場合は、単にトークンのsecretがある場所をマウントしておけばよい。
どのnamesapceにもデフォルトで
default
というサービスアカウントがある。(default namespaceにも、blue namespaceにもdeafult serviceaccountがある。ややこしい・・・)pod生成時に、特に何も指定しなくても
default
service accountが/var/run/secrets/kubernetes.io/serviceaoount
に勝手にマウントされるようになっている。default service accountが自動で付けられるのを抑止したい場合
apiVersion: v1 kind: Pod metadata: name: sample spec: containers: - name: httpd image: httpd automountServiceAccountToken: false
kubectl exec -it my-kubernetes-dashboard ls /var/run/secrets/lunernetes.io/servicaccount
すると、トークン本体を見ることができる
podが持つservice accountを明示的に指定したい場合
apiVersion: v1
kind: Pod
metadata:
name: sample
spec:
containers:
- name: httpd
image: httpd
serviceAccountName: dashboard-sa # ここに書き込む!!
# serviceacountはpodに対するリソース。コンテナではない
lab
podに紐づいているservice accountを知りたいときは
kubectl get pod sample-pod -o yaml # で、yamlを見る必要がある。describeでは出てこないので注意
新しいサービスアカウントを作って権限を付与する
kubectl create serviceaccount dashboard-sa kubectl create -f rolebinding.yaml kubectl create -f role.yaml
image security
普通にnginxと書いた場合は、
docker.io/library/nginx
と解釈されるプライベートレジストリを使いたい場合
dockerの場合は、
docker login private-registry.io # 認証情報を入力(ユーザ名/PW) docker run private-registry.io/apps/internal-app
k8sの場合は、
認証情報を指定のsecretに書く。
kubectl create secret docker-registry <name> --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL # docker-registry というsecretのタイプが存在するのでそれを利用する
podのimageをFQDNにし、認証情報を関連付ける
spec: containers: - name: nginx image: private.io/apps/internal-app imagePullSecrets: - name: <secret-name>
lab
k8s公式のimage
のページを読む。だけ
security contexts
docker run --user=1001 ubuntu sleep 3600
とか、docker run --cap-add MAC_ADMIN ubuntu
とかをk8sでやりたいときにどうするか--cap-add
は、capability addの略。OSユーザーの特権をつけたり消したりできるコマンド。wheelとかそういうの?
そこでsecurity context。コンテナにもpod全体にも定義できる
podに定義する場合
apiVersion: v1 kind: Pod metadata: name: web-pod spec: securityContext: runAsUser: 1000 containers: - name: ubuntu image: ubuntu command: ["sleep", "3600"]
containerに定義する場合
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu command: ["sleep", "3600"] securityContext: runAsUser: 1000 capabilities: #capabilityはcontaierにしか定義できない add: ["MAC_ADMIN"]
lab
podの実行ユーザーを知りたいとき
kubectl exec <podname> -- whoami
security contextは、editしても反映してもらえないので、yamlにして書き換え→既存削除→書き換えたyamlをapplyする必要があることに注意。
既存のpodにseciutiry contextをつける場合、すでにyamlにある
securityContext:{}
を編集することに注意。新しく書いてもこれを消さないと上書きされてしまう。
network policy
Ingress / egress netwroking policy
- あるリソースに注目して、入ってくるものはingress, 出ていくものがegress
- 戻りの通信は自動で許可される
k8sは、デフォルトではすべてのpod同士(あるいはservice)が通信可能なall allowの状態。in ,out込みでall allow
裸のpodはall allowだが、何も定義していないnetwork policyをつけるとall block になる。ややこしい・・・
- network policyは要するにk8sのセキュリティグループ
例として、ユーザ→webAPL→API pod→DB podを考える。
ingress network policy の例
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: db-policy spec: podSelector: matchLabels: role: db # ポリシーを適用する対象をラベルで選択 policyTypes: - Ingress - Egress ingress: - from: - podSelector: #- の位置に注意。 matchLabels: name: api-pod # api-podからの通信 namespaceSelector: # namespaceでも絞れる matchLabels: name: prod # namespaceだけ書いた場合、namespace内のpod全部からのアクセスが許可される - ipBlock: #外部ipを直接許可したい場合 cidr: 192.168.5.10/32 ports: - protocol: TCP port: 3306 # 3306ポート宛の通信 egress: - to: - ipBlock: cidr: 192.168.5.10/32 ports: - protocl: TCP port
-
の位置に注意。上記の例だと、podSelectorとnemespaceSelectorはandで効く。(podSelector and namespaceSelector) or ipBlock の意味。
ただし、network policyをサポートしているかどうかは、k8sが採用しているnetworkの実装次第。
- kube-routerは使える
- flannelは使えない(!)
8 storage
Docker storage
k8sのストレージを知るにはまずdockerから。
dockerのストレージには主に2つのコンセプトが存在する
- storage drivers
- volume drivers
dockerのレイヤードアーキテクチャ
FROM ubuntu RUN api-get update && apt-get -y install python RUN pip install flask flask-mysql COPY . /opt/source-code ENTRYPOINT FLASK_APP=/opt/source-code/app.py flask run
で
docker build Dockerfile -t ktakase/myapp
すると、行ごとにレイヤーが作られる。このレイヤーはreadonlyで変更不可。変更したい場合は別途buildする必要がある。FROM ubuntu RUN api-get update && apt-get -y install python RUN pip install flask flask-mysql COPY app2.py /opt/source-code ENTRYPOINT FLASK_APP=/opt/source-code/app2.py flask run
で次に似たようなものを作ろうとした場合、3行目まではキャッシュから前のものが再利用されるようになっている
更に、このイメージを使って
docker run ktakase/myapp
すると、この5行のreadonlyレイヤーの上に、更に6行目のwritableなレイヤーが作製される。- このwritableなレイヤーは、書き込めはするものの、揮発的なものなので、コンテナが終了されると変更は全部消えてしまう。
- 4行目で仕込んだapp.pyについて、コンテナ内でapp.pyを書き換えた場合、イメージの方は当然書き変わらず、コンテナのreadwrite領域にrewrite領域をつくり、app.pyのコピーを作って書き換えてそれを参照する形で実現している。
docker volume (persistent volume)
docker volume create data_volume
すると/var/lib/docker/volumes/data_volume
が作成されるvolume mount
# 永続ボリュームを作成 docker volume create data_volume # data_volumeをコンテナ内の/var/lib/mysqlとしてマウント docker run -v data_volume:/var/lib/mysql mysql # じつは事前に永続ボリュームを作成していなくても、勝手にボリューム作成→マウントまでやってくれる docker run -v data_volume2:/var/lib/mysql mysql
bind mount
# dockerが用意している/var/lib/docker/volumes/data_volumeではなく、自分で用意した記憶領域にマウントしたい場合は、 docker rnu -v /data/mysql:/var/lib/mysql mysql # 最近の書き方は docker run --mount type=bind, source=/data/mysql, target=/var/lib/mysql mysql
docker のレイヤー構造は、dockerに実装されているストレージドライバで管理されている
- AUFS, ZFS, BTRFS, Device Mapper, Overlayとか・・・
- ストレージ、イメージを管理
docker volumes
- 一方docker volume はvolume driverで管理される
- デフォルトは「local」というボリュームドライバ
- 他にもいろいろ・・・Azure FileStoraeg, Convoy,
- volume mountしたときにできる/var/lib/docker/volumes/hoge_volumeを管理している
- デフォルトは「local」というボリュームドライバ
container runtime interface
- k8sが、各コンテナランタイム(docker, rkt...)に依存せずにオーケストレーションに集中できるように、各コンテナランタイムの差分を吸収するcontainer runtimte interfaceがある。
- 同様に、各ネットワークソリューション(flannel等)の差分を吸収するcontainer network interfaceがある
- 同様に、各ストレージソリューションの差分を吸収するcontainer storage interfaceもある
- 逆に、k8sで動作させたいならこの規約を守っていればなんでもいい。ベンダは規約に合わせて実装する
- 具体的には、RPCとしてCreateVolume, DeleteVolumeなどを実装しておけばよい。
volumes
podにvolumeを与えるには、
- ホストのvolumeを確保して、
- 確保したvolumeをpodにマウントさせる
apiVersion: v1 kind: Pod metadata: name: sample-pod spec: containers: - image: alpine name: alpine command: ["/bin/sh","-c"] args: ["shuf -i 0-100 -n 1 >> /opt/num.out;"] volumeMounts: # コンテナ内でどういう名前でマウントするか - mountPath: /opt name: data-volume volumes: # host側で確保するボリューム - name: data-volume hostPath: path: /data type: Directory
persistent volume /claim
- persistent volumeとpersistent volume claimは物理ボリュームと論理ボリュームみたいな関係性。
- 好きに使えるボリュームのpoolをpersistent volumeでとして作成し、ボリュームを使いたいというpodのclaimに応じてその一部を割り当てる方式
- いちいちpodがマウント先 を宣言するより、podは1Mください!というだけで、具体的なマウント先などの詳細はpersistent volume に任せる方式がいい
- persistent volume
apiVersion: v1 kind: PersistentVolume metadata: name: pv-vol1 spec: accessModes: - ReadWriteOnce # ReadOnlyMany, ReadWriteMany capacity: storage: 1Gi hostPath: # これも非推奨。aws ebsとかがいい path: /tmp/data
- persistent volume claim
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 500Mi
- 「podがどのnodeに割り当てられるか」の時と同じように、何もなければ一番余裕があるpersistent volume が使われるし、claimにselectorが指定されていれば条件にあうpersistnnt volumeが選ばれる。
- 上手くできていれば、pvc はbound状態になる。一つもpersistent volume が存在しない場合、persistent volume claim はpending状態になる。
lab
persistent volume を作れ
apiVersion: v1 kind: PersistentVolume metadata: name: pv-log spec: capacity: storage: 100Mi accessModes: - ReadWriteMany hostPath: path: "/pv/log" persistentVolumeReclaimPolicy: Retain
persistent volume claimを作れ
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: claim-log-1 spec: accessModes: - ReadWriteMany # pvとpvcのaccesssModeは一致させる必要がある!!!! resources: requests: storage: 50Mi
作ったpersistenet volume をpodでマウントしろ
apiVersion: v1 kind: Pod metadata: name: testpod spec: volumes: - name: logdir persistentVolumeClaim: # hostpathの代わりにclaim。いずれにしても領域確保宣言が必要。 claimName: claim-log-1 containers: - name: task-pv-container image: nginx ports: - containerPort: 80 name: "http-server" volumeMounts: # claimを使う場合でも、領域確保->マウントの2回記述が必要。 - mountPath: "/usr/share/nginx/html" name: logdir
pvc を他のpodが使っている状態で削除してみろ
- できない!pvをpodが使っている状態でpvcを消そうとすると、コマンドがハングする。使ってるから消せない旨のエラー出してくれればいいのに・・・
storage class
実際persistent volume を使う場合、最初にgceでボリュームを確保する宣言(lvcrteate的な?)をしてからpersistent volumeとして再度領域確保することになるので二度手間感が強い。
→dynamic provisioning, storage class
- googleが巨大なpersistent volume(storage class)を用意しているので、いちいちpersistent volumeの確保とかせず好きに使っていい。的な思想か。
- この場合、persistent volumeのyamlは不要で、storage class (sc)のyamlが取って代わる
- 裏であくまでいちいちpersistent volumeの定義を書かなくてよいというだけで、pvcにstorage class nameを指定してclaimした場合は、裏で毎回gcp(のstorage class?)がpvを作っている。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: google-storage provisioner: kubernetes.io/gce-pd paarameters: # parameterに書く内容はstorage classを実装するベンダ(gcp, azure...)によってさまざま type: pd-standard replication-type: none
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteONce storageClassName: google-storage # StorageClassを指定する文言がpvcのyamlに新設される resources: requests: storage: 500Mi
lab
- pcとpvcのaccess modeとstorage class name は合わせる必要があることに注意
- pvcを作成した直後にpendingになる
- WaitForFirstConsumer:何かしらのpodに割り当てて初めてpvを消費する。
- 指定したstorageclassの設定に依存する。
9 networking
switching routing
- Switch
- router
- 複数のネットワークを結びつける働きをする。
- ルートテーブルは
route
やip route
で確認可能。 ip route add 192.168.2.0/24 via 192.168.2.1
- 静的ルートテーブルを作成できる
- 192.168.2.1はルータのIP
ip route add default via 192.168.2.1
- デフォルトゲートウェイの設定。
ip route add 0.0.0.0 via 192.168.2.1
も同義。
- A - B - C のlinuxを通信させたい場合
- Aに静的ルーティングの設定
ip route add 192.168.2.0/24 visa 192.168.1.6
の設定
- Cにも同様の静的ルーティングの設定設定
- Bにeth0 - eth1 間の転送の設定
/proc/sys/net/ipv4/ip_forward
のファイルに0を書き込むと転送しない、1を書き込むと転送する、になる。- もしくは、
/etc/sysctl.conf
にnet.ipv4.ip_forward=1
と書きこんでも転送OKの設定になる。
- Aに静的ルーティングの設定
ip link # インターフェースの状態を確認 ip addr ip addr 192.168.1.10/24 dev eth0 # インターフェースにIP割り当て ip route ip route add 192.168.1.0/24 via 192.168.2.1 # 静的ルーティング設定 cat /proc/sys/net/ipv4/ip_forward
DNS
hostsの話
- 自身の名前(
hostname
,uname -n
)とは関係ないのが特徴
- 自身の名前(
DNSサーバ
/etc/hostsとDNSの優先度の設定も可能。
/etc/nsswitch.conf
を参照cat /etc/nsswitch.conf | grep hosts hosts: files dns myhostname # 通常はhostsが優先される設定になっている
その他のインターネット上のホストに対して名前解決をしたいときは、DNSサーバの設定に
Forward All to 8.8.8.8
の転送設定を入れておくとよい。会社のサーバのホスト名は
www.mycompany.com
のように設定しておきたいが、会社内からはwww
でアクセスしたいとき/etc/resolve.conf
にsearch mycompany.com
を書いておけばよいec2は最初からそうなっていた...
[ec2-user@ip-172-31-46-152 ~]$ cat /etc/resolv.conf search ap-northeast-1.compute.internal nameserver 172.31.0.2
名前解決を試したいなら、
ping
かnslookup
かdig
nslookup
はhostsを見ないので注意。純粋にDNSサーバにクエリを投げるのみ
coreDNS
Network Namespaces
仮想ネットワーク?
コンテナ内で見えるプロセスは、互いのコンテナ間では見ることが出来ない。一方で、コンテナを動作させているホストからは、すべてのコンテナで動いているプロセスを確認できる必要がある
ホストのインターフェース・ルートテーブル・ARPテーブルとは別に、コンテナが生成されるとコンテナ用のインターフェース・ルートテーブル・ARPテーブルが生成される。
linuxの設定に、Network Namespaceの設定がある。
ip netns add red
で名前空間を追加可能ip netns
で確認
全部のインターフェースを見るときは
ip links
だが、特定の名前空間のインターフェースを見たいときはip netns exec red ip link
かip -n red link
ARP :
ip netns exec red arp
route :
ip netns exec red route
ネットワークの名前空間同士をつなげたいとき
物理的に別のホストとつなげたいときと同様。
# namesapce : red とblueを繋げたいとき # まずインターフェース(ケーブル)を作成する。一個でOK。 ip link add veth-red type veth peer name veth-blue # 次にインターフェースを名前空間(仮想ネットワーク)に割り当てる ip link set veth-red netns red ip link set vveth-blue netns blue # IPの付与 ip -n red addr add 192.168.15.1 dev veth-red ip -n blue addr add 192.168.15.2 dev veth-blue # インターフェースのup ip -n red link set veth-red up ip -n blue link set veth-blue up # 疎通確認 ip netns exec red ping 192.168.15.2 ip netns exec red arp
仮想スイッチを利用する方法:
# 仮想ブリッジの作成 ip link add v-net-0 type bridge # ブリッジにつなげるための仮想インターフェース(ケーブル)の作成 ip link add veth-red type veth peer name v-eth-red-br ip link add veth-blue type veth peer name v-eth-blue-br # インターフェースのアタッチ。ブリッジの設定が特殊なため注意。ブリッジはmaster(デフォルト?)という名前空間にインターフェースを割り当てる。 ip link set veth-red netns red ip link set veth-red-br master v-net-0 ip link set veth-blue netns blue ip link set veth-blue-br 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.2 dev veth-blue # インターフェースのup ip -n red link set veth-red up ip -n blue link set veth-blue up
ホストからもこれらのネットワークにアクセスしたいときは、
ip addr add 192.168.15.5/24 dev v-net-0
のように仮想ブリッジのインターフェースそのものにIPを割り当ててやればよい
仮想ネットワーク(red, blue)から、外のネットワークにアクセスしたい場合、ホストのネットワークに頼る必要があるため、ゲートウェイとして仮想ブリッジのIPを指定しておく必要がある
ip netns exec blue ip route add 192.168.1.0/24 via 192.168.15.5
加えて、外のネットワークにアクセスしたい場合はNATの設定が必要。
iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
docker network
- 一切外と通信しない場合
docker run --network none nginx
- hostsのポートをそのまま使う場合
docker run --network host nginx
- 仮想ネットワークを作成する場合(一番普通な設定)
docker run nginx
- docker自身の仮想ブリッジ(
bridge
という名前)にアタッチされるdocker network ls
で確認可能
- 仮想ブリッジはホストから見ると
docker0
という名前ip link | grep dokcer0
で引っかかるず。- やっていることは
ip link add docker0 type bridge
と同じ。 - 仮想ブリッジ
docker0
のIPはip addr | grep -C 5 docker0
から分かる通り、172.17.0.1/16を利用している
docker run するたび、
- 仮想インターフェースの作成
- 仮想ネットワークの作成
- 仮想ネットワークと仮想ブリッジ(docker0)を仮想インターフェースでつなげる
- をやっている
コンテナのポートをホストのポートにマッピングしたい場合
docker run -p 8080:80 nginx
- 実際には、dockerがiptablesのDNATの設定を行っている
iptables -nvL -t nat
で確認可能
CNI : container networking interface
lab
control planeの仮想インターフェイスは?
# contorl planeのローカルIPを確認 kubectl get node -o wide # 対応するインターフェイス・MACアドレスを確認 ip addr | grep -wF <IP> -C 3 ip link | grep -wF <IP> -C 3
kube-schedulerのポートは?
netstat -tlp # t : only tcp 別に指定しなくてもよい # l : listening 大事。指定しないと大量に出てくる # p : program名で表示。これも必要 netstat -natulp # numeric # all # tcp # udp # listening # program name
etcdの2つあるポートのうち、沢山クライアントのコネクションがつながっているのは?
netstat -pa | grep etcd | grep 2380 | wc netstat -pa | grep etcd | grep 2379 | wc # を比較 若しくは、 ps -aux | grep etcd |grep client で、どっちがクライアント接続用のポートかを見れば、wcするまでもない
pod network
- port の話。
- k8sのビルトインソリューションは存在しないので、各自で実装すること。
- モデルが定義されているので、それに合うプロダクトを入れればよい。
- flanell等
- podを作ったら、本来は
- node内の仮想ブリッジと、仮想インターフェースを使って接続し、
- デフォルトゲートウェイの設定をしなければならない。
- そんなことを毎回やるわけにはいかないので、CNI(container network interface)ミドルを採用する。
- kubelet
--cni-conf-dir=/etc/cni/net.d
と書かれている。
CNI in kubernetes
コンテナを作る機能がCNIを呼び出す必要がある。(作るごとにCNIを利用してネットワークを設定する必要があるため)
kubelet service (worker nodeでコンテナ生成・個数維持をになうサービス) の起動引数にCNIが書かれている
--network-plugin=cni --cni-bin-dir=/opt/cni/bin # bridgeとかvlanとか、executableなバイナリファイルが配置されている --cni-conf-dir=/etc/cni/net.d # 定義したyaml的なものが置かれている。CNIの実装として何を利用しているかがわかる(flannel, weaveなど)
ps -aux | grep kubelet
で引数を見ても確認可能
weave
- nodeにweaveのエージェントを配置する
- ほかのnode宛のパケットは、weaveが更にカプセル化して他のノードに配送する
- k8sの初期設定が済んでいれば、daemonsetとしてweaveを構成可能。
kubectl get pods -n kube-system
にweaveが出てくるはず。kubectl logs weave-net-hoge weave -n kube-system
でログも見れる
lab
weaveを入れてみる。
公式の手順に従うと、
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
ただし、このままだとweave podがErrorになってしまう。
``` kubectl logs -n kube-system weave-net-2s6rp weave を見てみると、 Network 10.32.0.0/12 overlaps with existing route 10.40.56.0/24 on host weaveが利用しようとしているIPのレンジが、ホストに既存に設定されているIPのレンジとかぶっていることがわかる。
* 仕方がないので、weaveが使うIPのレンジをオプションで指定する。
* ```bash
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')&env.IPALLOC_RANGE=10.50.0.0/16"
IPAM (CNI)
- どうやって重複のないIP割り当てを実現しているか?
/etc/cni/net.d/net-script.conf
のipam に書かれている。- type : DHCP or host-local
- subnet : 使用するIPの範囲
lab
# 使われているCNIソリューションは? kubectl get pod -A # でpodを見る ps -aux | grep kubelet # kubeletの定義を見る ll /opt/cni/bin # 配下のバイナリを見てみる。weave とかflannelとかがある ll /etc/cni/net.d # 配下の定義ファイルを確認する # weaveが使っているブリッジの名前は? ip addr | grep weave # weaveが使うiPの範囲は? ip addr show weave # にIPレンジが書いてある kubectl logs weave-net-hoge # にallocateしたIPレンジが書かれている # podに設定されるデフォルトGWは? # やってみるしかない、 kubectl run busybox --image=busybox --dry-run=client -o yaml --command -- sleep 10000 > pod.yaml kubectl create -f pod.yaml kubectl exec busybox -- ip route
service network
- podはnode上に作られるのに対し、service は(仮想的に)すべてのnode上に存在するように構成される
- serviceはクラスター内全体にわたって定義されるリソース。podのように特定のnamespace, nodeを指定することはない
- kube-proxy
- node上に存在するk8s のサービス。今まではpodの生成に注目していたのでkubeletばかり出てきていたが、ネットワークの話となるとkube-proxyの出番
- serviceが作成されるたびにkube-proxyが動く
- 各nodeで、
- 空いているIPを取得し、
- IPのフォワーディング(serviceのIPに来た通信をpodに振り分ける)の設定を入れる
- 各nodeで、
- どのような方式で実現するかはいくつかオプションが存在し、
kube-proxy --proxy-mode [userspace | iptables | ipvs]
から選択可能- デフォルトではiptables
- serviceが使うIPのレンジは
kube-apiserver
のオプションから指定できる。ps aux | grep kube-api-server | grep --service-cluster-ip-range
で確認もできる- kube-proxyの設定ではなく、kube-apiserverの設定であることに注意。
- podの使うIPとは競合しないように設定する。
iptables -L -t nat
で実際の設定を確認することもできる。
lab
# クラスタで利用されているnodeのIPの範囲は ip addr # のeth0を確認 ipcalc -b <IPアドレス> # でサブネットの計算とかできて便利 # podのIPの範囲は # CNIが決めている。weaveの場合は kubectl logs -n kube-system weave-net-hoge weave # でipalloc-range を確認.実際podを作ってみてもよい。割り当てられたIPからどのいんたーフェースが使われているかを逆洗してもよい # serviceが使うIPの範囲は # kube-apiserverが決めている。 ps aux | grep kube-api | grep service # で起動引数を見るもよし、 car /etc/kubernetes/manifests/kube-apiserver.yaml | grep service # でyamlを見るもよし # kube-proxy のモードは? kubectl logs -n kube-system kube-proxy-jhjtg kube-proxy I0901 05:11:21.698869 1 server_others.go:561] "Unknown proxy mode, assuming iptables proxy" proxyMode=""
DNS in k8s
DNSはビルトインのものが存在する。
serviceを作成すると、k8sのDNSがserviceの名前とIPの対応表を自身に書き込む。
ただし、namespaceの情報も一緒に追記されることに注意。
# 例えば、apps namespace内でserviceを作成すると、以下のレコードが生成される hostname : web-service namespace : apps type : svc root : cluster.local ip address : 1.1.1.1 # app内であれば http://web-service # でアクセス可能なのに対し # ほかのnamespaceからのアクセスの場合は http://web-service.apps # とサブドメインまで書かないといけないことに対応 # フルドメインは http://web-service.apps.svc.cluster-local # なので、これに相当する情報を覚えておく必要がある
podのDNSはデフォルトでは生成されない。明示的にレコード生成を指示する必要がある
hostname : 1-1-1-1 # なんかこれになるらしい namespace : default type : pod root cluster.local ip address : 1.1.1.1
DNSの実装
core DNS
- kube-system上に存在するpod
- 冗長性のために、replicasetとして2つ存在
- 設定ファイルは
/etc/coredns/Corefile
各podはcoreDNS podのservice のIPをresolve.confに登録する。
- coreDNSのserviceの名前はデフォルトでkube-dns。(昔k8sでcoreDNSの代わりに使われていたkube-DNSの名前が一緒でややこしい・・・)
- coreDNSも普通にpod・serviceなので、使われるIPの範囲は他と同じ。podはCNIが。serviceはkube-apiserverが範囲を決めている
- いちいち自身でpodにDNSのIPを指定する必要はない。kubeletがpodを生成する際に、podのresolve.confにDNSno IPを書き込んでくれる。
- 逆に言うと、kubeletの設定ファイル
/var/lib/kubelet/config.yaml
にDNSのIPが書かれている。- kubeletはpodではないので、static podのyaml (/etc/kubernetes/manifests)を見てもわからないことに注意。/etc/配下出ないのが厄介・・・
- 逆に言うと、kubeletの設定ファイル
service は
host web-service
と打つだけで、web-service.default.svc.cluster.local
まで探してくれる。- podは最初からフルドメインで指定しないとhostで探してきてくれない(なぜ?)
lab
# kubeletのDNSの設定は /var/lib/kubelet/config.yaml # coreDNSの設定ファイルの場所は coreDNS podの/etc/coredns/Corefile # ただし、coreDNSの中身を見ることはできない。上記はconfigmapとして提供されており、coreDNSpodがこのconfigmapを/etc/coredns/Corefileとしてマウントしているため、こっちを見ると良い kubectl describe configmap -n kube-system coredns # ほかのnamespaceのリソースにアクセスしたいときは、 mysql.payroll でOK。いちいちフルパスで書かなくてよい。
Ingress
- service にnordport を利用しているとして、実際外からwebapp podにアクセスしたい場合には、
- その他の選択肢として、クラウド環境ではload balancer のserviceを使う方法がある
- ロードバランサを勝手に立ててくれる。
- DNSの名前は自分でつけてやる必要がある
- 複数のwebサービスを立ち上げる場合は、同様にクラスタ内に別のデプロイメントを立てて、load balancer タイプのserviceをつければよい
- ただし、
http://myweb/purchace
とhttp://myweb/game
を各デプロイメントに振り分けるために、自前で振り分け用サーバを立てる必要があることに注意。- 多分apache のvirtual serverを立てることになる
- スケールするのが面倒・・・
- ただし、
- 上記のめんどくさい問題を解決してくれるのがingress
- ingress
- 1 deployの構築に必要なのは、deployment, 公開用service, nginx設定を外に切り出すconfigmap, 監視用のserviceaccountの4つ
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-ingress-controller spec: replicas: 1 selector: matchLabels: name: nginx-ingress template: metadata: labels: name: nginx-ingress spec: containers: - name: nginx-ingress-controller image: quay.io.kubernetes-ingress-controller/nginx-ingress-contorller:0.21.0 # ingress用に調整されたnginxイメージが存在する args: - /nginx-ingress-contorller - --configmap=${POD_NAMESPACE}/nginx-configuration env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 80 - name: https containerPort: 443
apiVersion: v1 kind: Service metadata: name: nginx-ingress spec: type: NodePort port: - port: 80 targetPort: 80 protocol: TCP name: http - port: 443 targetPort: 443 protocol: TCP name: http selector: name: nginx-ingress
# configmapでnginxに設定を渡すようにすると後からいじりやすくて便利 kind: ConfigMap apiVersion: v1 metadata: name: nginx-configuration
# ingressの監視のために、適切なserviceaccountも必要になる apiVersion: v1 kind: ServiceAccount
# 一つしかserviceがなければこれでよい apiVersion: extentions/v1beta1 kind: Ingress metadata: name: ingress-wear spec: backend: serviceName: wear-service servicePort: 80
#色々振り分けたい場合。apacheのvirtualserverと同じ apiVersion: extentions/v1beta1 kind: Ingress metadata: name: ingress-wear-watch spec: rules: - host: wear.my-online-store.com http: paths: - path: /wear backend: serviceName: wear-service servicePort: 80 - path: /buy backend: serviceName: buy-service servicePort: 80 - host: wear.my-online-store.com http: - path: /watch backend: serviceName: watch-service servicePort: 80
- デプロイメント作成後は、
kubectl describe ingress ingress-wear-watch
で詳細を確認できる- 定義した/wear, /watchとは別に、/でアクセスしたときのデフォルトバックエンドが勝手に設定されることに注意。sorryとかに渡す必要がある。
lab:ingress resource (virtual serverの設定)を作る
#ingress resource本体は kubectl get ingress #で確認する。 #virtual serverの振り分け設定を確認可能。 #ingressの実体はnginx(ingress controller)なので、ingress controller pod (nginx)のdeploymentと、それにアクセスするためのサービスも存在していて、こっちは kubectl get deployment , service #で確認する。外部に公開するためのものなのでnordportのはず。 # なぜかingressはnamespaceの縛りを受ける。異なるnamespaceにdeploymentを立ててしまうと、複数ingressが必要?? #ingress resourceにアノテーションとして nginx.ingress.kubernetes.io/rewrite-target: / をつけておくと、podに渡される際には/appとかが取り払われて渡される
lab2 : ingress controller(nginx)を作る
# nginx用namespaceを作る kubectl create namespace ingress-space # configmapをつくる(中身は空でOK) kubectl create configmap -n ingress-space nginx-configuration # service accountをつくる kubectl create serviceaccount -n ingress-space ingress-serviceaccount #service accountにrole, rolebindngをくっつける # configmap, ebdpoints, namespace, pods, secrets に対するgetを設定 # ingress controller (特別なnginx)をデプロイする # ingress controllerのserviceをnodeportで作る # 1.yamlを書く # 2. kubectl create service nodeport -n ingress-space ingress --tcp=80:80 --node-port=30080 --dry-run=client -o yaml # してselectorだけ書き換え # 3. kubectl expose deployment ingress-controller -n ingress-space --name=ingress --port=80 --target-port=80 --type=NodePort # してからnodeportを指定。なぜかexposeはnodeportの番号を指定できない # ingress resource を書く kubectl create ingress -n app-space ingress-resource --rule="/wear=wear-service:8080" --rule="/watch=video-service:8080" # rewrite-target: / # ssl-redirect: "false" を忘れずに付け足す。!!!!!
tips
# exec したいとき kubectl exec -it podname -- bash
10 design & install k8s cluster
design a kubernetes cluster
- 勉強目的なら
- minikube
- single node cluster
- デプロイメント・テスト目的なら
- multi node cluster
- kubeadm
- 製品目的
- HA構成・複数ノード
- クラウド or オンプレ
- ストレージ
ノード:仮想vs ベアメタル
master node
- worker podが配置されないようにtaintをつけておくのがベストプラクティス
- etcdをmasterから切り離して別のnodeに置くこともある
choose k8s infrastructure
- minikube
- VMを作ってくれる
- single nodeのみ
kubeadm
- VMは利用可能な状態にしておく必要あり
- single/multi node両方可能
turnkey solution
- hosted solution
config HA
- masterが死んだらどうなる?
- master nodeを2つ用意しておくのがHA構成
- master 状のcontorl planeも丸々2つ用意する
- apiserver
- sheduler
- contorl manager
- etcd
- master 状のcontorl planeも丸々2つ用意する
- kube apiserver をactive acticve構成にする場合、前段にload balancer を入れる
- controller manager とschedulerはやることが被ると2重に作業指示を出してしまうので、active standby 構成にする。
- leader elect optionを使う。
kube-contorller-manager --leader-elect true --leader-elect-lease-duration 15s
- etcdは2種類方式がある
- stacked topology
- master node上にetcdが存在 * 2号機分
- セットアップが簡単
- ノード障害に弱い
- external ETCD topology
- セットアップが面倒だが安全
- どちらにしても、kube-apiserverからetcdを参照できるようにするために、kube-apiserverの起動引数にetcdのエンドポイントを指定しておく必要があることに注意。
--etcd-servers=https://10.240.0.10:2379,https://10.240.0.11:2379
- stacked topology
etcd in HA
- key-value型
- 複数存在する場合、だれか一人がリーダーになり、そのリーダーがだけが書き込みを担当することでデータベースとしての一貫性を保っている
- リーダー以外に書き込み要求が送られた場合、リーダーに書き込み要求を転送する仕組み
- RAFT
- leader electionのやり方
- 各々ランダム時間だけ待ち、待ち時間が終わったものが「リーダになりたい」とリクエストを他のetcdに送る。
- leaderになった後は、一定時間おきに「俺がリーダーだ」というヘルスチェック的なパケットを他に送る
- パケットが途絶えた場合、残ったetcd同士で再びランダム時間待ってリーダーを決める
- 書き込み時の一貫性について
- リーダーが書き込みを受付後、ほかのetcdに書き込み内容を反映する際、majority の数だけ書き込み横展開がなされれば、横展開完了と見なす。
- majority (quorum): 文字通り過半数。(N/2+1).ceil
- 3個なら2、4個なら3。
- 書き込み横展開中にいずれかのetcdが死んでも継続できる仕組み
- majority (quorum): 文字通り過半数。(N/2+1).ceil
- etcdが2個だった場合、quorumは2のため、どっちかが死ぬと書き込みに支障をきたしてしまうため、etcdが1個の時とあんまり変わらない(むしろ全体障害が発生する回数は倍になる)
- 従ってetcdをHA化する場合は3個置くのがベストプラクティス。3個あれば、1個死んでも書き込み処理を継続できる
- etcdが3個の時も4個のときも、許容される障害ノードは1のため、etcdは奇数個で構成するのがお得。
- 突然ネットワークが分割された場合を考えても、etcdが奇数個なら分割された片方は必ずquorumを満たすので業務を継続できる。一方etcdが偶数個の場合、真っ二つに分断されてしまうとどちらもquorumを満たさなくなってしまう。この意味でもetcdは奇数個の方が良い。
- リーダーが書き込みを受付後、ほかのetcdに書き込み内容を反映する際、majority の数だけ書き込み横展開がなされれば、横展開完了と見なす。
11 install k8s the kubeadm way
introduction to deployment with k8s
- まずnodeを用意して、どれがmasterになるかを決める
- (master含む)全てのノードにdocker(コンテナランタイム)を入れる
- (master含む)全てのノードにkubeadm を入れる
- masterノードの初期化を行う
- podのネットワークを設定する。(通常のネットワークではない)
- worker nodeをjoinさせる
lab
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
最初にOSを確認。
cat /etc/*release*
でubuntuであることを確認する。iptablesがブリッジを通過するトラフィックを処理できるようにする
lsmod | grep br_netfilter # 引っかかることを確認 modprobe br_netfilter # 引っかからない場合明示的にロード cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system
iptablesがnftablesバックエンドを使用しないようにする
# レガシーバイナリがインストールされていることを確認してください sudo apt-get install -y iptables arptables ebtables # レガシーバージョンに切り替えてください。 sudo update-alternatives --set iptables /usr/sbin/iptables-legacy sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy sudo update-alternatives --set arptables /usr/sbin/arptables-legacy sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy # やってみたけどよくわからん、エラーが出るけど放置。
必須ポートの確認
- スルー
dockerはすでに入っている想定。
kubeadm、kubelet、kubectlのインストール(master, worker 両方)
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
#sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-get install -y kubelet=1.23.0-00 kubeadm=1.23.0-00 kubectl=1.23.0-00
sudo apt-mark hold kubelet kubeadm kubectl
コントロールプレーンの初期化(master nodeのみ)
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
ip addr | grep eth0 # master nodeのIPを確認。ifconfigでも可 kubeadm init --apiserver-advertise-address=10.42.153.3 --apiserver-cert-extra-sans=controlplane --pod-network-cidr=10.244.0.0/16
# kubeadm実行後に表示される以下を実行 To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
node01をjoinさせる (node01のみ)
公式手順と順序が逆だが・・・(公式はCNI追加が先)
# kubeadm実行後に表示される以下を実行 Then you can join any number of worker nodes by running the following on each as root: kubeadm join 10.42.153.3:6443 --token ur3cqw.9qpcs44qe6hyhur9 \ --discovery-token-ca-cert-hash sha256:cd2276f0e29fd4c724595b06806d2702c3f131ac09277ce076d2871222ba55a2
CNIの追加(masterのみ)
# master のみ。CNIのエージェントは各nodeに配置が必要だが、daemonset のyamlをapplyする処理のため。 kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml # k8sに無いのが難点・・・
13 troubleshooting
application failure
- アプリケーションの構造を端から書いていって、順にみていけばどこが悪いかわかるはず。それはそう
kubectl get pod kubectl describe pod kubectl logs web # コンテナが再起動していて前のログが消えてしまうとき kubectl logs web -f (follow) --previous
control plane failure
kubectl get nodes kubectl get pods kubectl get pods -n kube-system service kube-apiserver status service kubelet status sudo journalctl -u kube-apiserver # ファイルがない→volume mount, volumeを見てみる
worker node failure
kubectl get node kubectl describe node ディスクか、メモリか、プロセス数か・・・ top df -h service kubelet status # kubelet が落ちている可能性もある
lab
kubeletの設定ファイルは/var/lib/kubelet/config.yamlか/etc/kubernetes/kubelet.conf なんで二個あるねん・・・
14 other topics
JSON path
json用のクエリ
- dictionary
{ "car": { "color": "blue", "price": "$100" }, "bus": { "color": "red", "price": "$200" }, "bike": { "color": "red", "price": "$200" } }
なら、
$.car.color -> "blue" $.*.color -> ["blue", "red","red"]
- list
[ "shop1", "shop2", "shop3" ]
なら
$[1] -> shop2 $[*] -> ["shop1", "shop2", "shop3"] $[0,1] -> ["shop1", "shop2"] $[1:2] -> ["shop1", "shop2", "shop3"]
- criteria
$[Check if each item in the array > 40] $[?(each item in the list > 40)] $[?(@>40)] は全部同じ。
$.car.whell[?(@.model == "rear-wheel")].price
k8sでの使い方
kubectl get pod -o=jsonpath='{.items[*].metadata.name}' # $は自動で入れてもらえるので不要 kubectl get pod -o=jsonpath='{.items[*].metadata.name}{"\n"}{.items[*].status.capacity.cpu}' master node01 4 4 kubectl get pod -o=jsonpath='{range .items[*]} {.metadata.name}{"\t"}{.status.capacity.cpu}{end}' master 4 node01 4 kubectl get nodes -o=custom-columns=NODE:.metadata.name, CPU:.status.capacity.cpu NODE CPU master 4 node01 4 kubectl get nodes --sort-by=.metadata.name #便利!!!!!!!
advanced kubectl command
kubectl get pod -o=jsonpath='{}'
lab
15 mockexam
2
- podのDNSはIPベース、serviceのDNSは名前ベース。
- ユーザーの追加 = CSR
- クラスタのアップグレード
- ドレインする対象に注意!!!
- master nodeで、これからアップデートするノードをドレインする。
3
- podにserviceaccountを紐づける方法
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - image: nginx name: nginx serviceAccountName: build-robot ★
そんなことしなくても、
k run
のオプションに--serviceaccount
があるのでこれでOKkubeconfig
k get node --kubeconfig=hoge.conf
のように使う。- デフォルトでは
$HOME/.kube/config
にあるため、これと比較すると良い。
tipsまとめ
core concepts
- podを作って、かつ同名のserviceを作りたいとき
kubectl run httpd --image=httpd:alpine --port=80 --expose
スケジューリング
- 覚えておく単語は、
- taint torrelance
- node name
- node selector
- node affinity
- 特定のラベル
env=dev
を持つpodだけを確認したい場合
k get pod --label-columns env | grep dev k get pod --selector env=dev
- ラベルつけるときは
# editしてもいいが以下が簡単 k label node node01 color=blue # ラベルをはがすときはkey- k label node node01 color-
- 2つ目のshedulerを作りたい
- configmapを作る
- service accountと、それにスケジューラー用cluster roleを紐づけるcluster role bindingを作る
- podを作る。configmapとservice accountを関連付ける
Logging & Monitoring
kubectl top
コマンドを打ちたいときは- metrics server pod を入れる必要あり。
- worker nodeに1台ずつ必要。
- controlplaneには不要?
Application Lifecycle Management
- deploymentのstrategyは、rolling update とrecreateの2択。
- recreateの場合、消してから作るので、サービス断が発生する。
- 作ってから消す、ならrolling update?
- 環境変数configmap(secret)から取得する場合、configmap内の一部の定義を取ってくる方法と全部まとめて記載する方法がある。後者がおすすめ。
- tasksを参照
Cluster Maintenance
- etcdのリストア時はとりあえず別のdata dirを指定しておくのが吉。
- etcd のdata-dirの更新も忘れずに。
- k8sのupgradeはとにかくノード間違いに注意
Security
- 1つ目で苦戦
- image pull secret
- dockerレジストリのsecretを作ったら、これで読む。
- ecurity contextは途中からeditで変えられない。一回podを消す
- capability のセキュリティコンテクストはcontainerにしか付けられない
- runasuserのセキュリティコンテクストはpodとcontaier両方につけられるが、conteinerの方が優先される
星取表
Core Concepts ★Practice Test Introduction (06:08) ★Practice Test – PODs ★Practice Test – ReplicaSets ★Practice Tests – Deployments ★Practice Test Namespaces ★Practice Test Services ★Practice Test – Imperative Commands Scheduling ★Practice Test Manual Scheduling ★Practice Test Labels and Selectors ★Practice Test – Taints and Tolerations ★Practice Test – Node Affinity ★Practice Test Resource Limits ★Practice Test DaemonSets ★Practice Test – Static PODs ★Practice Test Multiple Schedulers Logging & Monitoring ★Practice Test Monitor Cluster Components ★Practice Test Managing Application Logs Application Lifecycle Management ★Practice Test Rolling Updates and Rollbacks ★Practice Test Commands and Arguments ★Practice Test Env Variables ★Practice Test Secrets ★Practice Test – Multi Container PODs ★Practice Test – Init Containers Cluster Maintenance ★Practice Test OS Upgrades ★★Practice Test Cluster Upgrade Process ★★Practice Test Backup and Restore Methods ★★Practice Test Backup and Restore Methods 2 Security ★Practice Test View Certificate Details ★Practice Test Certificates API ★Practice Test KubeConfig ★Practice Test Role Based Access Controls ★Practice Test Cluster Roles ★Practice Test Service Accounts ★Practice Test Image Security ★Practice Test Security Contexts ★Practice Test Network Policies Storage ★Practice Test Persistent Volume Claims ★Practice Test – Storage Class Networking Practice Test – Explore Environment Practice Test CNI weave Practice Test – Deploy Network Solution Practice Test – Networking Weave Practice Test Service Networking Practice Test CoreDNS in Kubernetes Practice Test – CKA – Ingress Networking – 1 Practice Test – CKA – Ingress Networking – 2 Install Practice Test Cluster Installation using Kubeadm Troubleshooting Practice Test Application Failure Practice Test Control Plane Failure Practice Test Worker Node Failure Practice Test – Troubleshoot Network Other Topics Practice JSON PATH Practice Test – Advanced Kubectl Commands Lightning Labs Lightning Lab – 1 Mock Exams Mock Exam – Introduction Mock Exam – 1 Mock Exam – 2 Mock Exam – 3 CKA Mock Exam – 2 Solution (43:52) CKA Mock Exam – 3 – Solution (50:28)
linuxファイル検索tips
深さ制限してfindする
find /opt/jboss -maxdepth 4 -type f -name jboss-service.xml
maxdepthのオプションは、-type よりも先に入れないといけないことに注意。 逆にすると以下のエラーが出る
# find /opt/jboss -type f -name jboss-service.xml -maxdepth 4 find: warning: you have specified the -maxdepth option after a non-option argument -type, but options are not positional (-maxdepth affects tests specified before it as well as those specified after it). Please specify options before other arguments.
深さ制限してgrepする
find /opt/jboss -maxdepth 5 -type f -name *.xml -exec grep --with-filename -n "300" {} \;
深さ制限してtree する
tree -L 2 /lib
Ansible for the Absolute Beginner 勉強メモ
はじめに
Ansible for the Absolute Beginner の勉強メモ。自分用
ansibleのきそ
「誰に」「何をするか」を規定する。
- inventory ファイル(「誰に」)
- 管理するサーバ達の接続先の設定を入れる。ansible版のhostsに近い。複数サーバひとまとめに扱える点が先進的
- デフォルトでは
/etc/ansible/hosts
だが、-i <inventory name>
で任意に指定可能
- playbook(「何をする」)
例えば・・・
# inventory ファイル localhost server1.company.com server2.company.com [mail] server3.company.com server4.company.com [db] server5.company.com server6.company.com
# httpdを立ち上げるplaybook - name: Play 1 hosts: localhost # inventoryファイルにlocalhostが記載されている必要あり。 tasks: - name: Execute command 'date' command: date - name: Execute script on server script: test_script.sh - name: Play 2 hosts: localhosts tasks: - name: Install httpd service yum: name: httpd state: present - name: Start web server service: name: httpd status: started
- taskはリストなので、順序がある。上から順に実行される
# 全てのサーバにpingするplaybook - name: Test connectivity to target servers hosts: all tasks: - name: Ping test ping: # pingモジュールには属性が定義されていないのでこれだけでOK
module
- 上記の
command
,script
,yum
,service
に相当するもの。 例えばhttpdをインストールしたい場合、
- 従来叩くようなコマンド(
yum install httpd
)を記載する代わりに、 - 「どうあって欲しいか」を表現するyamlを書く。
- 従来叩くようなコマンド(
tasks: - name: Install httpd service yum: name: httpd state: present
moduleのyaml記法は別途redhatが定義しているため、残念ながら覚えるか調べる必要がある。
ansible-doc <module name>
で調べられる。ansible-doc ping
など
playbookの実行
ansible-playbook playbook.yml # で実行 (インベントリファイルは/etc/ansible/hostsを利用) ansible-playbook playbook.yml -i inventory.txt # インベントリファイルを明示的に指定したい場合 ansible-playbook --help # でヘルプ確認
ansibleの実行方法
ansible
コマンドを使うやり方と、ansible-playbook
コマンドを使うやり方がある。ansible
コマンドの方がプリミティブなコマンド。- 命令的記法。(
kubectl run busybox --image=busybox
と同じアイデア)
- 命令的記法。(
# ansibleコマンドの例 ansible <hosts> -a <command> -i inventory.txt ansible all -a "/sbin/reboot" -i inventory.txt # allはいつでも利用可能。インベントリファイルに含まれる全てのサーバを表す ansible <hosts> -m <module> -i inventory.txt ansible target1 -m ping -i inventory.txt
ansible-playbook
コマンド- 宣言的記法。こちらが、ansbile的には王道の書き方。
kubectl create -f hoge.yaml
と同じアイデア
# ansible-playbookコマンドの例 ansible-playbook <playbook name> ansible-playbook playbook-webserver.yaml
modules
modulesいろいろ
- system
- commands
- command, expect, raw,script, shell...
- expect :対話的な処理
- script:ローカルホスト上のscriptを直接実行できる
- files
- acl, archive, copy, file, find, replace, template...
- database
- mysql, postgresql...
- cloud
- windows
more...
command
- name: Play 1 hosts: localhost tasks: - name: Execute command 'date' command: date - name: Display reslov.conf content command: cat /etc/resolv.conf - name: Display reslov.conf content command: cat resolv.conf chdir=/etc - name: Make directory # createとあるが、ifNotExistの意味らしい command: mkdir /folder create=/folder
- command moduleは
free form
なので、適当にcat /etc/hosts
とか書いても全体をstringと解釈してくれるが、他moduleでは普通ではないことに注意。 script
ansibleのローカルマシン上のスクリプトを実行する
- いちいちローカル→リモートにコピーしなくていいので便利
- name: Play 1 hosts: localhost tasks: - name: Run a script on remote server script: /some/local/script.sh -arg1 -arg2
service
サービスのstart, stop, restartなど
どちらの記法でもOK
- name: Play 1 hosts: localhost tasks: - name: start the database service service: name=postgresql status=started
- name: Play 1 hosts: localhost tasks: - name: start the database service service: name: postgresql status: started
- name: Play 1 hosts: localhost tasks: - name: add nameserver lineinfile: path: /etc/resolv.conf line: 'nameserver 8.8.8.8'
variable
- inventoryにもplaybookにも別ファイルにも定義できる。
- inventoryのansible_host, ansible_connectionも実はvariable。
- playbookに変数を記載する例
- name: Add DNS server to resolve.conf hosts: localhost vars: # 変数はここで定義 dns_server=8.8.8.8 tasks: - lineinfile: path: /etc/resolv.conf line: 'nameserver {{ dns_server }}' # 2重カッコで括って変数を展開
- inventoryに変数を記載する例
# sample inventory file Web http_port=8081
# playbook - name: ser firewall config hosts: web tasks: - firewalld: service: https permanent: true state: enabled - firewalld: port '{{ http_port }}'/tcp parmanent: true state: disabled
- 別ファイルに変数を記載する例
# sample variable fiel - web.yml http_port: 8081 snmp_port: 161-162
- 2重かっこで変数を展開する方式は、Jinja2 templating と呼ばれている。
source: {{ http_port }} # NG source: '{{ http_port }}' # OK。基本は''で囲む source: Someting{{ http_port }}something # OK。文中のみ''は不要。
conditions (条件文)
- name: install NGINX hosts: all tasks: - name: install NGINX on debian apt: name: nginx state: present when: ansible_os_family == "Debian" and ansible_distribution_version == "16.04" # ビルトイン変数を利用 - name: install NGINX on redhat yum: name: nginx state: present when: ansible_os_family == "RedHat" or ansible_os_family == "SUSE"
- loop とwhenの併用例
- name: Install softwares hosts: all vars: packages: - name: nginx required: True - name: mysql required: True - name: required: False tasks: - name: install '{{item.name}}' on debian # リストのアイテムは文字通りitemで呼び出す。属性の呼び方はjqと同じ apt: name: '{{item.name}}' state: present when: item.required == True # whenと併用も可能。この場合nginxとmysqlだけがインストールされる loop: '{{packages}}' #変数で定義したリストを展開可能。イテレータ。
- registerの利用例 (前に実行したタスクの実行結果を引き継ぐ)
- name: check status of a service and email if its down hosts: localhost tasks: - command: service httpd status register: result # commandタスクの結果をresult変数(?)に格納。 - mail: to: aaa@gmail.com subject: service alert body: httpd service is down when: result.stdout.find('down') != -1 # findは「見つからなければ-1を返す」ので、downが見つかるとtrue
Role
ansibleの宣言的定義は、各サーバに対して、仕事・役割を振っていると考えることができる
その意味で、
role
という単位でtasksをまとめておくことで、mysqlとして動作してほしいサーバにはmysql roleを渡すだけ、webAPlサーバとして動作してほしいサーバんはhttpd roleを渡すだけ、とする。「君(サーバ)にやって欲しいこと」の単位でまとめているのでroleと呼ぶ
ansible galaxyに沢山roleが公開されているので見てみると良い
ansible-galaxy install geerlingguy.mysql
でインストール可能。ansibleが共通で参照するディレクトリ/etc/ansible/roles
に配置される。
- name: install and configure mysql hosts: db-server roles: - geerlingguy.mysql # 普通に使える
roleを作りたいときはansible-galaxy init
role名
でディレクトリを自動生成してくれる
[ec2-user@ip-172-31-3-27 ~]$ ansible-galaxy init mysql - Role mysql was created successfully [ec2-user@ip-172-31-3-27 ~]$ tree mysql mysql ・defaults ・main.yml ・files ・handlers ・main.yml ・meta ・main.yml ・README.md ・tasks ・main.yml ・templates ・tests ・inventory ・test.yml ・vars ・main.yml 8 directories, 8 files
my-playbook ・playbook.yaml ・roles ・mysql ・...
あるいは、
/etc/ansible/roles
に配置しておけば共通にみることができる/etc/ansible/ansible.cfg
のroles_path
から変更も可能- ansible-galaxy上のroleをインストールするとここに置かれる。
advanced topics
- windowsマシンにansibleから指示を出したい場合、windowsマシンでwinRMを有効化しないと使えないことに注意。
- ホスト名には
Host*
とかワイルドカードも利用可能 - custom modules : 自分でpythonで書ける。
Ex
inventory
# Sample Inventory File # ansible内で使用する別名を定義できる。ansible版のhosts。hostsで定義したホスト名を更に上書きで出来るので、注意深く定義しないと後々面倒なことになりそう。 # linuxサーバはansible_connection=ssh, windowsサーバはansible_connection=winrm # linuxサーバのパスワードはansible_ssh_pass=Password123!, windowsサーバのパスワードはansible_password=Password123! # db servers sql_db1 ansible_host=sql01.xyz.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Lin$Pass sql_db2 ansible_host=sql02.xyz.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Lin$Pass # web servers web_node1 ansible_host=web01.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass web_node2 ansible_host=web02.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass web_node3 ansible_host=web03.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass # サーバのグループを定義 [db_nodes] sql_db1 sql_db2 [web_nodes] web_node1 web_node2 web_node3 # 1つのサーバーが複数のグループに所属してもよい。 [boston_nodes] sql_db1 web_node1 [dallas_nodes] sql_db2 web_node2 web_node3 # グループのグループも作成可能。 [us_nodes:children] # ただし:childrenが必要。 boston_nodes dallas_nodes
playbook
- localhostsで
date
、cat /etc/hosts
を実行するplaybook
- name: 'Execute two commands on localhost' hosts: localhost tasks: - name: 'Execute a date command' command: date - name: 'Execute a command to display hosts file' command: cat /etc/hosts
module
- lineinfile, user, script, serviceを使った。
- name: 'Execute a script on all web server nodes and start httpd service' hosts: web_nodes tasks: - name: 'Update entry into /etc/resolv.conf' lineinfile: path: /etc/resolv.conf line: 'nameserver 10.1.250.10' - name: add user user: name: web_user uid: 1040 group: developers - name: 'Execute a script' script: /tmp/install_script.sh - name: 'Start httpd service' service: name: httpd state: present
variables
- name: 'Update nameserver entry into resolv.conf file on localhost' hosts: localhost vars: car_model: BMW M3 country_name: USA title: Systems Engineer tasks: - name: 'Print my car model' command: 'echo "My car''s model is {{car_model}}"' - name: 'Print my country' command: 'echo "I live in the {{country_name}}"' - name: 'Print my title' command: 'echo "I work as a {{title}}"'
conditions
- name: 'Execute a script on all web server nodes' hosts: all_servers tasks: - service: 'name=mysql state=started' when: ansible_host == "server4.company.com" # db1とかで指定したいところ。できそうだが
- name: 'Am I an Adult or a Child?' hosts: localhost vars: age: 25 tasks: - command: 'echo "I am a Child"' when: age < 18 # これは変数展開しないのか・・・ - command: 'echo "I am an Adult"' when: age >= 18 #elseがないのが微妙
- register
- name: 'Add name server entry if not already entered' hosts: localhost tasks: - shell: 'cat /etc/resolv.conf' register: command_output - shell: 'echo "nameserver 10.0.250.10" >> /etc/resolv.conf' when: 'command_output.stdout.find("10.0.250.10") == -1'
loop
- name: 'Print list of fruits' hosts: localhost vars: fruits: - Apple - Banana - Grapes - Orange tasks: - command: 'echo "{{item}}"' with_items: '{{fruits}}' # loopではなくwith_itemsというディレクティブもある。アイテム名を直接使いたいとき用?
tips
ssh fingerprintを無視する方法
atomのremote sync を使うと、
- ローカルのIDEで開発
- 任意のタイミングで内容をansible controllerに同期 できる
- shell モジュールとcommand モジュールはよく似ているが、 shellは専用のシェルが払い出されて、環境変数を使うことができ、コマンドにリダイレクトを含むこともできる。
- 変数の展開がよくわからん・・・・→jinja2 templateを学ぶべき
- デフォルトではamazon linux にはansibleを入れることが出来ない。
amazon-linux-extras | grep ansible # ansible2の設定項目があることを確認。 sudo amazon-linux-extras enable ansible2 sudo yum install -y ansible
PC購入時のセットアップ
はじめに
surface laprop 2 からXPS 13 に乗り換えた。
surface laptop 2は2019年から4年お世話になった。乗り換えた・買い換えた理由は以下。
画面焼けが激しい
- 画面端が黄色っぽくなる。購入して1年ほどしてから症状が出始め、最後のほうはだんだん我慢して使っていたがさすがに厳しくなってきた
充電ケーブルが頻繁に断線する。
接触が悪く、なんだかんだ1年に1回ぐらい買い換えている。金の無駄・・
mini display port の使い勝手が悪い
画面を閉じていても勝手に起動して電力消費してしまう。
- なぜかは謎。タッチ機能が勝手に働いて悪さしている?
- 家でせっかく充電してきたのに画面を開いてみるとほとんど充電がない・・みたいな事象が多発。
いきなりプログラムが消える (これはsurfaceが悪いわけではないが・・・)
- いつからかいきなりOffice(word, excel, powerpoint)やVSCode など、プログラムが勝手に消えた。
- 前者はそもそもプログラム自体が消えている。 一応これはmicrosoft アカウントの「サービスとサブスクリプション」から再ダウンロードして解決。
- 後者はプログラムの保存パスが勝手に書き換えられている。そんなことある?
- 通常は
C:\Users\peinn\AppData\Local\Programs\Microsoft VS Code\code.exe
だが、いつの間にかC:\Users\peinn\AppData\Local\Programs\Microsoft VS Code\_\code.exe
なる謎パスに書き換えられていた。再インストールすれば治ったかもしれないがあきらめ。
- 通常は
ということでだんだんsurfaceが信用できなくなってきたので、(もちろん今もサクサク動くので全然使えるのだが) DELL XPS 13に乗り換え。
以下、XPS購入時にセットアップしたことのリスト。
PC購入時のセットアップ
ソフトのインストール
- Chrome
- VSCode
- Launchy
- Git
- anaconda
- これまでわけのわからないままpythonとanacondaの混在環境を使っていたが、これを機に統一したい
- anaconda のPATHを通した。これしか使う予定がないため。not recommended の”add anaconda 3 to my PATH variable”をON。
- typora
windowsの設定
システム > ディスプレイ
- 「照明が変化した際に明るさを自動的に調節する」をOFF
- 「コンテンツに基づいて明るさを変更する」をOFF
- 「拡大/縮小」を125%に設定
システム > マルチタスク
- 「ウィンドウのスナップ」を、ONのまま、詳細設定の項目をすべてOFF
その他
- インテル・グラフィックス・コマンドセンターで画面の色を調整
- なんとなく黄色っぽく見えるので
rpmコマンド
はじめに
rpmコマンドの覚書。
rpmパッケージ自体は以下から取ってくるなどする必要あり。
https://access.redhat.com/downloads/content/package-browser
参照
### rpmの一覧表示 [root@my-instance ~]# rpm -qa | grep instantclient oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.x86_64 oracle-instantclient19.20-basic-19.20.0.0.0-1.x86_64 ### 特定のrpmの情報を表示 [root@my-instance ~]# rpm -qi oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.x86_64 Name : oracle-instantclient19.20-sqlplus Version : 19.20.0.0.0 Release : 1 Architecture: x86_64 Install Date: Mon 09 Oct 2023 04:05:22 AM GMT Group : Applications/File Size : 3270531 License : Oracle Signature : (none) Source RPM : oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.src.rpm Build Date : Sat 15 Jul 2023 04:34:07 AM GMT Build Host : phxdbfak28.dev3farm1phx.databasede3phx.oraclevcn.com Relocations : (not relocatable) Packager : Nobody <nobody@oracle.com> Vendor : Oracle Corporation URL : https://www.oracle.com/ Summary : Oracle Instant Client SQL*Plus package Description : The Oracle Instant Client SQL*Plus package provides the SQL*Plus command line tool for executing SQL and PL/SQL statements in Oracle Database. ### パッケージの配置ディレクトリを表示 [root@my-instance ~]# rpm -ql oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.x86_64 /usr/bin/sqlplus /usr/bin/sqlplus64 /usr/lib/oracle /usr/lib/oracle/19.20 /usr/lib/oracle/19.20/client64 /usr/lib/oracle/19.20/client64/bin /usr/lib/oracle/19.20/client64/bin/sqlplus /usr/lib/oracle/19.20/client64/lib /usr/lib/oracle/19.20/client64/lib/glogin.sql /usr/lib/oracle/19.20/client64/lib/libsqlplus.so /usr/lib/oracle/19.20/client64/lib/libsqlplusic.so /usr/share/oracle /usr/share/oracle/19.20 /usr/share/oracle/19.20/client64 /usr/share/oracle/19.20/client64/doc /usr/share/oracle/19.20/client64/doc/SQLPLUS_LICENSE /usr/share/oracle/19.20/client64/doc/SQLPLUS_README
変更
### パッケージをインストールする $ rpm -ivh パッケージファイル名 ### パッケージを更新する $ rpm -Uvh パッケージファイル名 ### パッケージをアンインストールする $ rpm -evh パッケージ名
yum,dnfとの違い
- rpmが、パッケージを自分で適用する、依存関係にあるパッケージが不足した場合は自分で取ってくる、という動きだったのに対し、
- yum(とのその後継のdnf)は、リポジトリを登録しておけば、パッケージはそこから自動で取得する、依存関係にあるパッケージもまとめて取得してきてくれる、
というイメージ。
awk文例集
徐々に充実させる予定。
フルパスからファイル名に変換
右端だけもってきたい。
- 元データ
$ find /etc/cron.d /etc/cron.d /etc/cron.d/0hourly /etc/cron.d/sysstat /etc/cron.d/update-motd /etc/cron.d/raid-check
- 案1
- 区切り文字に"/"を指定
- マッチした一番最後の値(=一番右端の文字列)を出力
$ find /etc/cron.d | awk -F "/" '{print $NF}' cron.d 0hourly sysstat update-motd raid-check
- 案2
$ find /etc/cron.d | awk 'match($0, /\/[^\/]*$/,a) {print a[0]}' /cron.d /0hourly /sysstat /update-motd /raid-check