Certified Kubernetes Administrator (CKA) with Practice Tests 勉強メモ

2CoreConcepts

cluster architecture

  • master
    • 情報の集積:etcd cluster
      • key-value型でいろんな情報が記載されている
    • worker shipにコンテナを置く:kube-scheduler
      • workerノードにコンテナを置く。
      • どのノードにコンテナを置くか。
    • 各リソースに対する管理部隊 XX-controller
      • node-controller
      • replication controller
      • controller-manager
    • kube-apiserver : クラスタ内の通信
      • 管理者向けAPI
      • workerとmasterが相互に通信できるapiを提供
  • workerノード(cargo ship)

    • 船長:kubelet
      • masterと通信。ノードごとに存在
      • masterから指示を受けとる、masterに稼働状況・ログを送る。
    • kube proxy
      • 異なるノード(船)にいるコンテナ同士で通信できるようにするためのサービス
  • 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
    • ディレクトリの形で情報が保存されている。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にあるから、見てみると良い。
  • 逆にスクラッチで構築する場合は、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-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によって実装されていると思ってよい。
    • hogeリソースに対して、hoge-controllerがあるイメージか?
      • 実際、namespce controllerとか、PV-protection controllerとか・・
  • 色々なコントローラーがいるが、実際にはkube-controller-managerという一つのプロセスにまとめられてservice(若しくはpod)として動いている
    • 上記のヘルスチェックがらみの時間がオプションとして指定できる
      • node-monitor-periodとか
    • また、どのコントローラーを有効にするかを指定する--controllersオプションも存在する。
      • デフォルトでは*、つまり全部起動する。
  • kubeadmでk8sを構成した場合、相変わらずkube-controller-managerもkube-system namespace上の1podとして配置されている。
    • yaml/etc/kubernetes/manifests/kube-controller-manager.yamlにあるので、コントローラーが動いていないと思ったら念のためこのyaml--controllerオプションを見てみると良い。
  • 自力でk8sを構築した場合、kube-controller-managerはmaster node上のserviceとして動いている。
    • yaml/etc/systemd/system/kube-controller-manager.yaml
    • serviceなので、起動オプションはps -aux | grep kube-controller-managerでも確認できる

kube-scheduler

  • podとnodeのスケジューリングを担当
  • 船の例だとコンテナを船に乗せる吊り機みたいな書かれ方をしているが、あくまでkube-schedulerが担当しているのは「podをどのnodeに載せるかの決定」だけで、実際にpodをnodeに載せるのはkube-apiserverがやっている
    • schedulerが根なし草のpodの行き先をkube-apiseverに伝えて、api serverがnodeのkubeletにpod(のyaml)を送る、という話が前に出てきた
  • 「この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の情報を報告
  • 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に振り分けてもらえる。
    • 直接DB podのIPを指定したとしても、つくっては壊すコンテナの性質上、DB podのIPが一定である保証はない
    • 上記のPODnetworkとは別物のため注意
    • serviceの実態はk8sのメモリ上にいる仮想コンポーネント
  • 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)

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の順に指定。
  • 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にフォワードされる仕組み
    • 設定内容:
      • target port : フォワードしたいpodが開放しているポート
        • 省略可。省略するとportと同じになる。
      • port : 単にportと言った場合、serviceの(pod向かいの?)ポート。80とか。
        • 省略不可。どこにフォワードするかわからなくなるので。
      • nodeport : nodeが外部に公開しているポート。30000~32767までのどれか。
        • 省略可。省略すると30000 ~ 32767のどれかになる
    • その他tips:
      • cluster IP
        • service自身が持つIP。勝手に設定される。serviceの種類としてのIPとややこしい・・・
      • node のIPは設定不要。各nodeで、nodeportに設定したIPが確保される
  • clusterIP
    • クラスタ内にvirtualIPを作って他のserviceと通信できるようにする。クラスタ内で通信するとき向け
    • nodeportと比較すると、文字通りnodeportは不要
  • loadBalancer

    • nodeportをエンドユーザにどう開放するか?を考えた際に、
      • エンドユーザとk8sの間にload balancer(HA proxyとか)を立てて、名前解決してもらう
      • 若しくは、loadbalancerを使う。yamlのtypeをLoadbalancerに変えるだけ。ほんとに?
      • クラウドプラットフォームしかサポートしていないことに注意。普通に自前のVirtualBox上でやると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
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を見てみる。
    • 実はkube-apiaserverに対するserviceと思われる。エンドユーザ(管理者)に対してkube-apiserverを公開している。
    • endpoint: serviceがロードバランスするpodのIP:portの組は、単にserviceのyamlを見てもわからず、kubectl describe serviceすると表示してくれる。k8sが自分でlabelとselectorを参照して探してきてくれるので便利。

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時に指定は不要
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
      
  • 宣言的:

    • 最終的なゴールのみを指示する

    • yamlにゴールを書いて、人間はただkubectl apply -f def.ymlするだけ。

    • 何をすべきかはk8sが判断してくれる

    • # create, replaceのように現在の状態を事前に把握しておく必要はない
      # 作る時も・更新するときも以下でOK。
      kubectl apply -f nginx.yaml
      kubectl  apply -f /path/to/config-files
      
  • 試験役立ち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 image httpd:alpine in the default namespace. Next, create a service of type ClusterIP by the same name (httpd). The target port for the service should be 80.

  • # 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 file
      • localに保存
    • 最後にapplyされたconfig(json)
      • 実はlive configのannotationとして保存されている
        • kubectl create replaceはこのlast applied configを更新しないため注意
    • 今動いているlive config
      • k8sのメモリ上
  • 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.ymlyamlを生成できるので、これをひな型にして、編集したものをkubectl create -f hogehoge.ymlとすると試験に役立つ。
    • podのyaml生成時はrunでyaml作るみたいな空気があった。
      • run(pull + create + start)はそもそもpodを動かすためのものなので。helpにpodの話しか書いてない
    • deploymentの生成時はcreateでやるっぽい...?
      • --replicasはcreateじゃないと指定できないことに注意。
      • 無理やりkubectl run deploymentyamlを作るとpodができてた
  • kubectlに使えるリソースの略称はkubectl api-resourceから確認できる
  • yamlを定義する際に書いてほしい内容はkubectl explain <resource>(deploymentとか)に書いてある。特にapiVersionとkindのチェックに便利。

  • podの仕様のうち

    • オートスケーリングの機能に相当するのが、deployment とreplica set
      • replicasetは単に個数を保証するだけだが
      • deploymentはローリングアップデート・ロールバックまで実施してくれる
    • ロードバランスの機能に相当するのがservice。
  • 参照するなら

    • get -o wide
    • describe
    • get -o yaml
  • yamlがわからなくなったら、公式ページでserviceとか調べると、yamlの書き方まで乗っているので便利。

  • 命令的に書くとき

    • podはrun。run = pull + create + start。

      • podしか使わないので、kubectl run pod mypodではなくkubectl run mypodでOK
    • 他は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を見てみると良い。
  • 配置するnodeを自分で決めたい場合は、自分でpodのyamlnodeName: 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(何?)に投げる。

    • apiVersion: v1
      kind: Binding
      metadata: 
        name: nginx
      target: 
        apiVersion: v1
        kind: Node
        name: node02  #bindするノード名はわかるが、podの情報はcurlの送り先ディレクトリで指定
      
    • これをjsonにしてcurlする。

    • curl --header "Content-Type:application/json" --request POST --data {json...} http://$SERVER/api/v1/namespace/default/pods/$PODNAME/binding

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を利用すること。
  • 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する。

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

    • podそのもののrequests, limtsは、pod起動後はedit不可。やりたいならpodを消して再作成する必要あり。
      • editしようとしても保存できない。tmpファイルは残るので、既存podを消してtmpのyamlをcreateする。
      • あるいは、既存のpodを-o yamlで出力後、既存podを消してからcreate する
    • deployment支配下のpodであれば、deploymentのyamlをedit可能。deploymentの機能として、勝手にpodのdelete -> createをやってくれる

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不在の前提で話していたが、ふつうに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名が名前につく。
  • 用途:
    • controll plane(master?)そのものをpodとして作るとき。
      • masterとなるnodeにkubeletを入れて、/etc/kubernetes/manifests
      • controller-manager.yaml,
      • apiserver.yaml
      • etcd.yaml
    • を配置すれば、あとは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は何個?
    • kubectl get pod -Aして末尾がノード名になっているpodを探す
    • あるいは、yamlを見て、ownerReferenceのkindがNodeになっているものを探す
      • ふつうはreplicasetとかになっている。pod単体runしていた場合はownerReferenceは表示されない
    • 若しくは、contollplaneの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 the busybox image and the command sleep 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 ecfecfコンテナに関するログが見れる
    • -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のために残しておくのか?
  • ロールバックする場合も上記と同様。

    • コマンドは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に書いてある。

    • 例えば、nginxのイメージならnginx
    • ubuntuのイメージならbash
  • 仮に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 10k8s 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はよく正した。

  • k8sのcommand・argsは素直にyamlを見ればよい。

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)の設定

  • containersenvフィールドを書く。環境変数は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を作れ

    • kubectl create secret generic db-secret --from-literal=DB_Host=sql01 --from-literal=DB_User=root --from-literal=DB_Password=password123
      # secret genericであることに注意。
      
    • コマンドライン直書きの時はこれでよいが、yamlでcreateするときはあらかじめエンコードしたものを書いておかないといけない?

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 or kubectl 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)

  • アップグレードのセオリー

    • まずmasterのバージョンを上げる
      • この間、クラスタにアクセスする手段はないがnodeにいるpod達は動き続ける。
    • 次にnode
      • 一斉にupgradeすることもできるが、ダウンタイムが発生する
      • 一個ずつアップグレードすると時間はかかるがダウンタイムは生じない
      • あるいは、クラウド環境なら、新しいバージョンのk8sを入れたnodeと入れ替えてしまうのも手

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

バックアップ

  • 行儀よくyamlを書いていた場合は、コードをgithubに入れればOK.

  • 一部命令的に作ってしまったリソースがあったとしても、

    • 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 の守りを固めることが重要

    • 誰がアクセスできるか?→5種
      • ユーザー名, PW
      • ユーザー名, トーク
      • 証明書
      • LDAP
      • サービスアカウント
    • 何をできるか?
      • RBAC : role based access control
      • ABAC : attribute based access control
      • node authentication
      • webhook mode
  • 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にある
      • 反映するときは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

  • まず、全部で三種類証明書があることを覚えておこう。

  • コンポーネントの役回り(サーバ?クライアント?)に応じて、必要な証明書が異なる(?)、というかサーバ証明書・クライアント証明書がそれぞれ必要になるため、まずはコンポーネント間の通信について確認していく。

  • まずはサーバー

    • kube apiserver
    • etcd
      • これもサーバ。クラスタの情報を提供する
        • etcdserver.crt
        • etcdserver.key
    • kubelet
      • これもサーバ。というか、https通信を指定できるものはすべて証明書が必要
        • kubelet.crt
        • kubelet.key
  • 次、クライアント側

    • admin (我々)
      • クライアントとしてkube api serverに通信しに行く
        • admin.crt
        • admin.key
    • scheduler
      • api serverにスケジュールされていないpodがないかを確認しに行き、api server経由でpodの配置を指示。kube apiserverから見るとadminもschedulerも同じ。
        • scheduler.crt
        • scheduler.key
    • kube controller manager
      • クライアントとしてkube apiserverに通信しに行く。apiserverを利用して各リソースの管理を行っている
        • contorller-manager.crt
        • contorller-manager.key
    • kube-proxy
      • これもクライアントとしてkube apiserverに通信しに行く。役割がいまいちよくわからない・・・
        • kube-proxy.crt
        • kube-proxy.key
    • kube -apiserver
      • 基本的にはapiserverなのでサーバーなのだが、etcd server, kubeletにアクセスしに行く時だけはクライアントとして振舞う。
        • サーバー証明書をそのままクライアント証明書として利用可能。
        • あるいは、サーバー証明書とは別にクライアント証明書を生成して利用することも可能。
    • kubelet
      • kubeletもapi serverと通信するときはクライアントとして動作する。
        • この場合も、サーバ証明書をクライアント証明書として転用してもよいし、新たにクライアント証明書を生成してもよい
  • k8sでは、クラスタ内に最低一つの認証局(CA)が必要。

    • ルート証明書
      • 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
    • 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として書かれている
  • 証明書が適用がうまくいっていないときは、以下で確認できる ```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を提出させていては管理が面倒くさい

    • 新規ユーザーから、管理者がCSRを受け付け、管理者が認証局の代わりに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等。ロールバインディング的な思想。
  • 実際の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の内容も書き換えられる

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生成時に、特に何も指定しなくてもdefaultservice 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と解釈される

    • レジストリ/ユーザー・アカウント名/リポジトリ名の順。
    • libraryはdocker公式のアカウント名。
    • docker.ioはdocker公式のレジストリFQDN
  • プライベートレジストリを使いたい場合

    • 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を考える。

    • DBはAPI podからの通信しか受け付けないようにしたい

      • ingress networking policyをDB podに適用して制限する

      • 適用の仕方はラベルを利用。

      • # DB podにラベルを付ける
        labels: 
          role: db
        
        # network policy のセレクタにラベルを指定する
        podSelector: 
          matchLabels: 
            role: db
        
  • 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を管理している

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
  • ただし、上記は単一nodeの時は良いが、そうでないなら微妙な設定。
    • 各nodeで共有するNASを用意してNFSマウントするとかが良い。

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
    • ip link
      • バイス上で有効になっているインターフェースを確認できる
    • ip addr add 192.168.1.0/24
      • ipを追加。
      • 複数の機器でIPアドレスを設定すれば、同一のネットワーク内で通信が可能。
  • router
    • 複数のネットワークを結びつける働きをする。
    • ルートテーブルはrouteip 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
  • 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.confnet.ipv4.ip_forward=1と書きこんでも転送OKの設定になる。
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サーバ

    • 自身のhostsをDNSサーバに移管
    • /etc/resolve.confDNSを設定
  • /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.confsearch 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
      
  • 名前解決を試したいなら、pingnslookupdig

    • nslookup はhostsを見ないので注意。純粋にDNSサーバにクエリを投げるのみ

coreDNS

  • DNSサーバの実装の一つ。
    • BINDと同じようなもの。
  • 一番簡単な構成は、
    • DNSサーバーのhostsを書く。
    • coreDNSの設定に、/etc/hostsを見に行く設定を入れる。
    • 完・・・

Network Namespaces

  • 仮想ネットワーク?

  • コンテナ内で見えるプロセスは、互いのコンテナ間では見ることが出来ない。一方で、コンテナを動作させているホストからは、すべてのコンテナで動いているプロセスを確認できる必要がある

  • ホストのインターフェース・ルートテーブル・ARPテーブルとは別に、コンテナが生成されるとコンテナ用のインターフェース・ルートテーブル・ARPテーブルが生成される。

    • ルートテーブルはルーティングの設定
    • ARPテーブルはIP-MACの紐づけの設定
  • linuxの設定に、Network Namespaceの設定がある。

    • ip netns add red名前空間を追加可能
    • ip netnsで確認
  • 全部のインターフェースを見るときはip linksだが、特定の名前空間のインターフェースを見たいときはip netns exec red ip linkip -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

  • 仮想ネットワークを整備する場合、
    • 名前空間(仮想ネットワーク)を作成
    • 仮想ブリッジの作成
    • 仮想インターフェースを作成
    • インターフェースを仮想ネットワークにアタッチ
    • IPアドレスを付与
    • インターフェースのUP
    • NATの設定
  • をする必要があった。通常、コンテナランタイムはCNIプラグインと強調して上記を実行するが、コンテナランタイムとCNIプラグインの役割分担は公式に明言されている
    • コンテナランタイム:k8s
      • dockerは使えないのだ・・・
    • CNIプラグイン:flannelとか

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に振り分ける)の設定を入れる
    • どのような方式で実現するかはいくつかオプションが存在し、kube-proxy --proxy-mode [userspace | iptables | ipvs]から選択可能
    • 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を作成すると、k8sDNSが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の実装

    • podにはhostsは登録しない。代わりにresolve.confDNSサーバのIPを入れておく。
    • k8sではcoreDNSを利用している。
  • core DNS

    • kube-system上に存在するpod
    • 冗長性のために、replicasetとして2つ存在
    • 設定ファイルは/etc/coredns/Corefile
      • kubernetes cluster.localと書かれている。k8sは全てこのcluster localの配下にある、と解釈される
      • pods insecure の項目を変更することで、podのDNS登録可否を検討することができる。
      • proxy . /etc/resolve.confの項目で、coreDNSが知らないホスト名が登場したときにどこにDNS問い合わせを転送するかを決めることができる。典型的には、自身の/etc/resolve.conf に、外部のネームサーバーを書いておけば事足りる。
      • configmap としても提供されている。どっちを編集するのでもOK?
    • 各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.yamlDNSのIPが書かれている。
          • kubeletはpodではないので、static podのyaml (/etc/kubernetes/manifests)を見てもわからないことに注意。/etc/配下出ないのが厄介・・・
    • 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にアクセスしたい場合には、
    • webapp serviceのIPにDNSをつけてあげる必要がある。
    • node port 30080 だとやりづらいので、ユーザーとk8sの間にプロキシを立てて80と30080でフォワードさせてやる必要がある。
  • その他の選択肢として、クラウド環境ではload balancer のserviceを使う方法がある
    • ロードバランサを勝手に立ててくれる。
    • DNSの名前は自分でつけてやる必要がある
  • 複数のwebサービスを立ち上げる場合は、同様にクラスタ内に別のデプロイメントを立てて、load balancer タイプのserviceをつければよい
    • ただし、http://myweb/purchacehttp://myweb/gameを各デプロイメントに振り分けるために、自前で振り分け用サーバを立てる必要があることに注意。
      • 多分apache のvirtual serverを立てることになる
      • スケールするのが面倒・・・
  • 上記のめんどくさい問題を解決してくれるのがingress
    • 複数のサービスを提供する際に、それぞれでnodeport loadbalancerを立てて、クラスタの外でも管理が必要な形式から脱却できる
    • 外にはingress一つのnodeportを公開すればよい
      • virtual serverを外に持つか中に持つか、というだけ。
    • 働きとしてはlayer 7 LB
  • ingress
      1. deploy : ingress controllerと呼ばれる
      1. config : ingress resourceと呼ばれる
    • デフォルトではデプロイされないので、手動でやる必要がある。
    • 1 deployの候補はGCEのload balancer かnginx
  • 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
  • 2 configの設定に必要なのはingressyaml
    • まずホスト名で振り分けて、そのごurlで振り分ける
# 一つしか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とかが取り払われて渡される
  • namespaceごとにingressを立てることがベストプラクティス

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

    • VMを作るところから自分でやる
    • 構築ツールをうまく使う
      • aws のkops
      • openshift
      • cloud foundry container runtime,
      • VMware cloud PKS
      • Vagrant
  • hosted solution
    • k8s as a service
    • VMはベンダが管理してくれる。
      • GKE
      • openshift online
      • Azure k8s service
      • EKS
    • ユーザーはyamlを配置

config HA

  • masterが死んだらどうなる?
    • nodeはスタンドアロンで動き続けるが、新しくpodの配置、スケジューリングはできない
    • kube apiserverが死んでいるので外からapi経由でアクセスできない
  • master nodeを2つ用意しておくのがHA構成
    • master 状のcontorl planeも丸々2つ用意する
      • apiserver
      • sheduler
      • contorl manager
      • etcd
  • 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

etcd in HA

  • key-value
    • 複数存在する場合、だれか一人がリーダーになり、そのリーダーがだけが書き込みを担当することでデータベースとしての一貫性を保っている
    • リーダー以外に書き込み要求が送られた場合、リーダーに書き込み要求を転送する仕組み
  • RAFT
    • leader electionのやり方
    • 各々ランダム時間だけ待ち、待ち時間が終わったものが「リーダになりたい」とリクエストを他のetcdに送る。
    • leaderになった後は、一定時間おきに「俺がリーダーだ」というヘルスチェック的なパケットを他に送る
      • パケットが途絶えた場合、残ったetcd同士で再びランダム時間待ってリーダーを決める
  • 書き込み時の一貫性について
    • リーダーが書き込みを受付後、ほかのetcdに書き込み内容を反映する際、majority の数だけ書き込み横展開がなされれば、横展開完了と見なす。
      • majority (quorum): 文字通り過半数。(N/2+1).ceil
        • 3個なら2、4個なら3。
      • 書き込み横展開中にいずれかのetcdが死んでも継続できる仕組み
    • etcdが2個だった場合、quorumは2のため、どっちかが死ぬと書き込みに支障をきたしてしまうため、etcdが1個の時とあんまり変わらない(むしろ全体障害が発生する回数は倍になる)
      • 従ってetcdをHA化する場合は3個置くのがベストプラクティス。3個あれば、1個死んでも書き込み処理を継続できる
    • etcdが3個の時も4個のときも、許容される障害ノードは1のため、etcdは奇数個で構成するのがお得。
      • 突然ネットワークが分割された場合を考えても、etcdが奇数個なら分割された片方は必ずquorumを満たすので業務を継続できる。一方etcdが偶数個の場合、真っ二つに分断されてしまうとどちらもquorumを満たさなくなってしまう。この意味でもetcdは奇数個の方が良い。

11 install k8s the kubeadm way

introduction to deployment with k8s

  1. まずnodeを用意して、どれがmasterになるかを決める
  2. (master含む)全てのノードにdocker(コンテナランタイム)を入れる
  3. (master含む)全てのノードにkubeadm を入れる
  4. masterノードの初期化を行う
  5. podのネットワークを設定する。(通常のネットワークではない)
  6. 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は名前ベース。
    • service : my-svc.my-namespace.svc.cluster.local
    • pod : 172-17-0-3.default.pod.cluster.local
    • 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があるのでこれでOK

  • kubeconfig

    • 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
  • 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 勉強メモ

はじめに

www.udemy.com

Ansible for the Absolute Beginner の勉強メモ。自分用

ansibleのきそ

「誰に」「何をするか」を規定する。

  • inventory ファイル(「誰に」)
    • 管理するサーバ達の接続先の設定を入れる。ansible版のhostsに近い。複数サーバひとまとめに扱える点が先進的
    • デフォルトでは/etc/ansible/hostsだが、-i <inventory name>で任意に指定可能
  • playbook(「何をする」)
    • サーバに対して何をするかを規定する
    • yamlファイル
    • taskが実際に実行するコマンド(=module)に相当
      • コマンド・スクリプトの実行
      • パッケージのインストール
      • 起動・停止・再起動
    • play
      • 一連のtaskをまとめたもの。
      • 実際に実行されるのはplay。task単位での実行はできない

例えば・・・

# 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いろいろ

- 
  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
  • lineinfile
    • 行がなければ追加する(ansible的には「行があることを確認する」)

      • sed的なイメージ
    • /etc/hosts ・ DNSの追加とかに便利

- 
  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 (条件文)

  • 通常、ソフトウェアのインストールを行う際は、debian系はapy, redhat系はyumを利用するが・・・ 両方同じplaybookでインストールをできると助かる。
  • conditionの使用例
- 
  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
  • 上記で生成したロールを、rolesディレクトリに配置しておけば、playbook.yamlから参照できるようになる。
my-playbook
・playbook.yaml
・roles
 ・mysql
  ・...
  • あるいは、/etc/ansible/rolesに配置しておけば共通にみることができる

    • /etc/ansible/ansible.cfgroles_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でdatecat /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を無視する方法

    • 初めてサーバにsshする際、fingerprintを記憶しますか?というプロンプトが出るが、ansibleがサーバにsshしようとしてこれを検知すると、エラーで止まってしまう。
    • これは面倒なので、自動でfingerprintを追加する方法が用意されている。(セキュリティ的には微妙だが)
    • /etc/ansible/ansible.cfghost_key_checkingをFalseにすればOK。
  • 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
    • 正規表現でマッチさせる。
    • 読みづらいが、
      • awk中の正規表現/正規表現/のようにスラッシュで囲む
      • 一文字目が/ (エスケープ込みで\/)
      • 二文字目以降が[^\/]*\/以外の文字、という意味
      • 文末$
    • 一つしかマッチしない読みでa[0] を出力する。
$ find  /etc/cron.d |  awk 'match($0, /\/[^\/]*$/,a) {print a[0]}'
/cron.d
/0hourly
/sysstat
/update-motd
/raid-check