Closed44

CKAを取るぞ

alkshmiralkshmir

kube-apiserverはetcdを直接触る唯一のコンポーネント(知らなかった)

  • kube-controller-manager
  • kube-scheduler
  • kubelet
    は、kube-apiserverを介してetcdを操作する
alkshmiralkshmir

kube-controller-manager

  • node-controller

    • 5秒ごとにノードにプローブを送る
    • プローブ失敗からnode-monitor-grace-period (デフォルト40s)経過後、unreachableに移行
    • pod eviction timeout(デフォルト5m)経過後、eviction が始まる
  • 他にもreplication controllerなどたくさんのコントローラがある

    • リソースの種類ごとにあるっぽい?
alkshmiralkshmir

kube-scheduler

  • どのPodをどのノードに置くかを決める
  • 実際に配置するのはkubelet
alkshmiralkshmir

kube-proxy

  • 各ノードで、サービスへのルーティングルールを作成するプロセス
    • 実体はiptables、ipvsなど。最近はCilliumがこの役目をやることもある
alkshmiralkshmir

試験で便利な-o yaml --dry-run=client

kubectl run nginx --image=nginx --dry-run=client -o yaml
kubectl create deployment --image=nginx nginx --replicas=4 --dry-run=client -o yaml > nginx-deployment.yaml
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

前者はnode-portを指定することができず、後者はselectorを指定することができない。

alkshmiralkshmir

kubectl applyは、適用されたファイルと、現在の状態(live object configration)と、最後に適用された状態(last applied configuration)の3つを比較する。
last applied configurationが必要なのは、フィールドの削除を行うため。つまり、適用されたファイルにフィールドがなく、last applied configurationにある場合はlive configurationからそのフィールドを削除する必要がある。

alkshmiralkshmir

taintとtoleration

  • nodeにはtaint(汚れ)がつくことがある
  • podにはtoleration((汚れに対する)耐性)がつくことがある
  • 汚れているnode上に、対応する耐性のないpodは動かない
  • つまり、nodeが汚れている場合、それに対応する耐性のあるpodのみが動作することができる

nodeをtaintする

kubectl taint nodes nodename key=value:taint-effect

taint-effectはこのtaintに対応するtolerationを持っていないpodがスケジュールされようとした時に何が起こるかを指定する。

  • NoSchedule: スケジュールされなくなる
  • NoExecute: 動作しなくなる
    • NoScheduleとの違いは、すでに実行中の場合にはevictionされる点
  • PreferNoSchedule: スケジュールしないようにするが、保証はされない
    • 詳しい動作はよくわからん…

podにtolerationをつける

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Equal"
    value: "example-value"
    effect: "NoSchedule"

operatorはどのようなtaint条件に対してtorelationを付与するのかを決める

  • Equal (デフォルト): keyとvalueが合致するときにつける

  • Exists: keyが存在するときにつける

  • ""で囲まないといけないらしい(なんで?)

podに特定のtaint に対するtolerationがついているからといって、必ずそのノードにスケジュールされるわけではない。tolerationがついていないpodがスケジュールされないことを表す。

alkshmiralkshmir

ノードにラベルをつける

kubectl label nodes node-name key=value

node selector: ラベルがついているノードにスケジュールする

spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    key: value
alkshmiralkshmir

node affinity, node-antiaffinity

node affinity types

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution
  • requiredDuringSchedulingRequiredDuringExecution
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux

You can use the operator field to specify a logical operator for Kubernetes to use when interpreting the rules. You can use In, NotIn, Exists, DoesNotExist, Gt and Lt.

alkshmiralkshmir

Resource Request

デフォルトで0.5CPU、256Miメモリを要求するようになっている(LimitRangeリソースをデプロイすると、そのNamespaceでのデフォルト要求値を変えることができる)

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        memory: "1Gi"
        cpu: 1
      limits:
        memory: "2Gi"
        cpu: 2

requestはスケジュールの際に使用される。

limitを超えると

  • cpuをスロットリングする
  • memoryはlimitを超えることができるが、その状態が続くとkillされる
alkshmiralkshmir

static pod

kubeletの起動オプションで

--pod-manifest-path

を指定するか、

--config

で指定されたyamlファイルのなかにstatic podのyamlファイルのpathを指定する。

kubeadmでは、--config=/var/lib/kubelet/config.yamlでkubeletを起動し、/var/lib/kubelet/config.yaml

staticPodPath: /etc/kubernetes/manifests

と書いてある。

alkshmiralkshmir

rollout deployment

Deploymentのロールアウトステータスを確認

kubectl rollout status deployment/<deployment-name>

ロールアウト履歴を確認

kubectl rollout history deployment/<deployment-name>

StorategyTypeはRecreateとRollingUpdate(デフォルト)がある

deploymentをroll back

kubectl rollout undo deployment<deployment-name>
alkshmiralkshmir
dockerfile pod definition
ENTORYPOINT command
CMD args

ENTRYPOINTとCMDを同時に指定した場合は、ENTRYPOINT + CMDの形でコマンドが渡される。
dockerfileにCMDが指定されている場合、pod definitionでcommandだけ変えてもCMDが後ろにくっつくので、そこをオーバーライドするにはargsフィールドを指定する必要がある。

alkshmiralkshmir

ConfigMap

命令的

kubectl create configmap <config-name> --from-literal=<key>=<value>

命令的

apiVersion: v1
kind: ConfigMap
metadata:
  name: <config-name>
data:
  <key>: <value>

podでの使用

spec:
  containers:
  - name: nginx
    image: nginx
    envFrom:
      - configMapRef:
          name: <config-name>

envの下のvalueFromを使うとkeyだけ指定できる

spec:
  containers:
  - name: nginx
    image: nginx
    env:
      name: <key>
      valueFrom:
        configMapKeyRef:
          name: <config-name>
          key: <key>
alkshmiralkshmir

Secret

命令的

kubectl create secret generic <secret-name> --from-literal=<key>=<value>

宣言的

apiVersion: v1
kind: Secret
metadata:
  name: <secret-name>
data:
  <key>: <base64-encoded-value>

Secretはbase64 encodeしているだけであり、全く安全ではない。
保存時に暗号化するには下のような工夫が必要。

あるいは、Helm SecretやHashicorp Vaultのような外部ツールを使う。

podに適用

spec:
  containers:
  - name: nginx
    image: nginx
    envFrom:
    - secretRef:
        name: <secret-name>
env:
  - name: <key>
    valueFrom:
      secretKeyRef:
        name: <secret-name>
        key: <key>
alkshmiralkshmir

cluster管理

nodeを退避

node 上のpodを退避する

kubectl drain node-1

daemonsetがあると失敗する。daemonsetを無視する場合は、--ignore-deamonsetsをつける。

kubectl cordon node-1はnode-1をUnschedulableにするだけで、既存のPodの退避を行わない。

退避した後、OSアップグレード、再起動などをおこなってから

kubectl uncordon node-1

でnode-1をReadyに戻す。
以前退避したPodが戻ってくるわけではない

alkshmiralkshmir

kube-apiserverのversionが1.xの時、

  • controller-manager, kube-schedulerは 1.xか1.(x-1)を許容する
  • kube-proxyとkubeletは1.xか1.(x-1)か1.(x-2)を許容する
  • kubeletは1.(x+1)と1.xと1.(x-1)を許容する

cluster upgrade

現在のバージョンと利用可能なバージョンを表示

kubeadm upgrade plan

新しいkubeadmをインストール

apt-get upgrade -y kubeadm=1.xx.0-00

アップグレード

kubeadm upgrade apply v1.xx.0
  • 1つのminor versionしか上げられない

masterにkubeletをデプロイしている場合(しなくてもmasterは作れる)は、kubeletもupgradeする

apt-get upgrade -y kubelet=1.xx.0-00
systemctl restart kubelet

workerは

apt-get upgrade -y kubeadm=1.xx.0-00
apt-get upgrade -y kubelet=1.xx.0-00
kubeadm upgrade node config --kubelet-version v1.xx.0
systemctl restart kubelet
alkshmiralkshmir

クラスタのバックアップ

  • yamlを全てGitHubに置く
  • kubectl get all -A -o yaml > all-deploy-services.yaml
    • 命令的に指定したリソース情報もバックアップできる
  • Velelo
  • etcdをバックアップ
    • ECTDCTL_API=3 etcdctl snapshot save shapshot.db
    • リストア
      • stop kube-apiserver
      • ECTDCTL_API=3 etcdctl snapshot restore shapshot.db --data-dir <etcd-data-dir>
      • etcdの--data-dirを書き換える
        • data directoryを新しく作る場合、ディレクトリの所有者をetcd:etcdにしておく
      • systemctl daemon-reload && systemctl restart etcd (systemctlでetcdを動かしている場合)
      • start kube-apiserver
alkshmiralkshmir

kube-apiserverへの認証

  • 静的パスワードファイル
    • BASIC認証
    • kube-apiserverの起動オプションに--basic-auth-fileをつける
    • 1.20以降使用不可
  • 静的トークンファイル
    • Bearer認証
    • kube-apiserverの起動オプションに--token-auth-fileをつける
    • ノードに平文で認証情報が入るので?安全でない
  • クライアント証明書
  • 外部IDサービス (LDAP, Kerberosなど)
alkshmiralkshmir

kube-apiserverに対する新しいクライアント証明書への署名はCertificates APIを使う

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."
kubectl get csr
kubectl certificates approve <csr-name>
alkshmiralkshmir

kubectlのデフォルトのクライアント証明書の場所は、$HOME/.kube/configに書かれている

  • clusters: 対象のクラスタ
  • users: ユーザ
  • contexts: クラスタとユーザのペア
    の3つの要素からなる
apiVersion: v1
kind: Config

clusters:
- cluster:
    proxy-url: http://proxy.example.org:3128
    server: https://k8s.example.org/k8s/clusters/c-xxyyzz
  name: development

users:
- name: developer

contexts:
- context:
  name: development

kubectl config use-context <context>でコンテクストを切り替える

contextにはnamespaceを指定することもできる

alkshmiralkshmir

kubernetesのapi groupのリストを取得

curl localhost:6443 -k

pathを指定するとresource group が取得できる

curl localhost:6443/apis -k | grep name

なお、エンドポイントにはクライアント証明書が必要だが、kubectl proxyでkube-apiserverを認証なしで叩けるようになる(kube-proxyとは別物)

alkshmiralkshmir

kube-apiserverの認可
起動オプションの--authorizationで指定する。カンマ区切りで指定して、前のものから順に評価する

  • AlwaysAllow
  • Node (kubelet用)
  • ABAC
  • RBAC
  • Webhook (3rd partyの認可サービス(Open Policy Agentなど)に訊く)
  • AlwaysDeny
    のポリシーがある。デフォルトはAlwaysAllow

RBACの設定

Roleを作成

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" はコアのAPIグループを示します
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

作成したRoleをユーザに紐付ける

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane # 「name」は大文字と小文字が区別されます
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # 「roleRef」はRole/ClusterRoleへのバインドを指定します
  kind: Role #RoleまたはClusterRoleである必要があります
  name: pod-reader 
  apiGroup: rbac.authorization.k8s.io

kubectlで権限を持っているかを確認するにはkubectl auth can-iを使う

kubectl auth can-i delete pod

--as <username>をつけるとcontextを切り替えなくてもテストできる

alkshmiralkshmir

namespaceに対して定義されるリソースへの権限はRoleRoleBindingで表される。
clusterに対して定義されるリソースへの権限は、ClusterRoleClusterRoleBindingで表す。
namespaced resourceに対してClusterRoleを付与した場合は、全てのnamespaceに対して権限を付与する。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # nameは大文字、小文字を区別します
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
alkshmiralkshmir

serviceaccount はbot用のUserみたいなもので、JWTトークンを使ってkube-apiserverに接続する。
デフォルトで、defaultというserviceaccountのための認証情報がSecretとしてpodにマウントされる。
他のserviceaccountをマウントするには、spec.serviceAccountNameを指定する。

v1.22からJWTトークンのaudienceと有効期限が設定されるようになった。
Secretとしてマウントされるのではなく、Podの作成時にTokenRequestApiを介してJWTトークンが生成されるように変わった。
v1.24からSeviceaccountを作成してもSecretは生成されなくなった。

alkshmiralkshmir

docker registryへの認証情報はkubectl create secretで生成できる

kubectl create secret docker-registry secret-tiger-docker \
  --docker-email=tiger@acme.example \
  --docker-username=tiger \
  --docker-password=pass1234 \
  --docker-server=my-registry.example:5000

podのspecにimagePullSecretsを指定すると、指定したsecretでdocker pullする。

spec:
  imagePullSecrets:
    - name: myregistrykey
alkshmiralkshmir

Podとコンテナにセキュリティコンテキストを設定する

Podの実行ユーザを変えるにはsecurityContextを設定する

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: gcr.io/google-samples/node-hello:1.0
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

podの実行ユーザとcontainerの実行ユーザどちらも指定可能

alkshmiralkshmir

Network Policy

api-podラベルのついているPodからのみのアクセスを許可する

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-policy
spec:
  podSelector:
    matchLables:
      role: db
  policyTypes:
    - ingress
  ingress:
    - from:
         podSelector:
           matchLables:
             name: api-pod
         ports:
         - protocol: TCP
           port: 3306
  • この定義ではEgressは影響を受けない
    • Egressを制限するには、spec.policyTypesegressを加える
  • FlannelではNetwork Policyを使えないが、エラーも出ずに定義はできるので注意
alkshmiralkshmir

EBSを使ったvolumeの例

  • VolumeはPodの定義(spec.volumes)にかく
volumes:
- name: data-volume
  awsElasticBlockStore:
    volumeID: <volume-id>
    fsType: ext4

ストレージを集中管理し、管理者が大きな領域を用意して開発者はそのプールから小さい領域を取り出して使うようにするのがPersistentVolume

  • 管理者がPersistentVolumeを用意
  • 開発者はPersistentVolumeClaimから使う
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vol1
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 1Gi
  hostPath:
    path: /tmp/data
alkshmiralkshmir

PVCとPVは一対一対応で、余ったPVは使えない

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

persistentVolumeReclaimPolicyはデフォルトでRetainであり、データはPVCを削除しても保持される。ほかのPVCが該当するPVをマウントすることはできなくなる。
DeleteにするとPVCを消した瞬間にデータが消える。
RecycleにするとほかのPVCがマウントしたら上書きされる。

Pod定義ではspec.volumes[].persistentVoluemeClaimでPVCを指定する。

alkshmiralkshmir

StorageClassは動的にPVを作ってくれるリソース。
指定したprovisionerを使ってPVCの宣言時に動的にPVが作られる

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: google-storage
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  peplication-type: none
  • parametersはprovisionerに特有

PVCではspec.storageClassNameで指定する

alkshmiralkshmir

/proc/sys/net/ipv4/ip_forwardはデフォルトで0であり、あるデバイスから来たパケットを別のデバイスに転送することはない。これを1にすると転送する。

alkshmiralkshmir
  • /etc/nsswitch.conf/etc/hostsを優先するか/etc/resolv.confのDNSを優先するかが書いてある
  • /etc/resolv.confsearchにドメインを書くとそのドメインのサブドメインをつけてDNSクエリを出すようになる
alkshmiralkshmir

LinuxでNetworkを分離するのにNetwork Namespaceを使う

  • 作成
ip netns add <ns-name>
  • Namespace内のデバイスを表示
ip netns exec <ns-name> ip link

または

ip -n <ns-name> link

arpコマンドもip netns exec ns arpでできる

pipeを使うと異なるNamespaceどうしを接続することができる。Namespaceが増えてくると全部を相互接続するのはめんどいので、仮想スイッチを使いたくなる。
Linux BridgeはNamespaceにまたがる仮想スイッチ機能を提供する。

  • ip link add <dev-name> type bridgeでブリッジデバイスを追加
  • ip link add veth-red type veth perr name veth-red-brでpipeを作成
  • ip link set veth-red netns red
  • ip link set veth-red-br master v-net-0
    • master をつけるとブリッジに接続することになる?
  • vethデバイスにIPアドレスをつける

bridgeにIPアドレスをつけると、ホストからbridgeに接続されたNamespaceに接続できる。

ip addr add 192.168.xxx.xxx/24 dev n-net-0

ホスト内のNamespaceから、別のホストのIPアドレスへ接続するには、Namespace内にルーティングルールを作ってから、ホストへNATルールを作成する

iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE

別のホストから、自ホストのNamespaceへ接続させるには、2つの方法がある

  • 送信元ホストにNamespaceのIPへのルーティングルールを追加する(物理ホストを経由するルール)
  • ホストにNATルールを追加
iptables -t nat -A PREROUTING --dport 80 --to-destination 192.168.xxx.xxx:80 -j DNAT
alkshmiralkshmir

docker network options

  • None network
    • コンテナは完全に隔離されており、ホストやほかのコンテナと通信することはできない。
docker run --network none nginx
  • host network
    • ホストのネットワークを共有する。
docker run --network host nginx
  • bridge network
    • コンテナどうしを仮想スイッチ(docker0デバイス)につなげる
    • コンテナには172.17.0.0/16のIPが振られる
    • デフォルトの実行モードである
    • docker network lsではbridgeの名前で表示される
    • docker run -pオプションは、内部でiptablesを用いてホストの特定ポートへのパケットを特定のNamespaceに転送するNATルールを作っている
alkshmiralkshmir

serviceの実体は、kube-proxyが各ノードに作る転送ルール
userspace, iptables, ipvsの実装がある(デフォルトはiptables)
iptables -L -t nat | grep <service name>で作成されたiptablesルールを確認できる。
また、kube-proxyのログでもルールが作成されたことを確認できる。

alkshmiralkshmir

CoreDNSはkube-system namespaceにdeploymentとしてデプロイされる

  • Corefileという設定ファイルを使う (kube-systemのcorednsというconfigmapとして渡される)
  • kube-dnsサービスを作りpodの作成時にkubeletが/etc/resolv.confにそのIPを書いておく
  • /var/lib/kubelet/config.yamlにCoreDNSのサービスIPをあらかじめ指定しておく
  • resolv.confsearchエントリには次の値が入るので、サービスはFQDNでなくても解決できる
    • search <namespace>.svc.cluster.local svc.cluster.local cluster.local
    • PodはFQDNでないと解決できないらしい?
alkshmiralkshmir

受かっていたのでクローズ

  • 登録名を英語にしていたら英語のid cardを持っていなくてはじかれた。登録名を日本語に変えて再リトライ
  • windowsのデスクトップで受けようとしたらwebカメラがカスすぎてidカードにピントが合わず、サポートに電話しろって言われたに。
  • 拙い英語で海の向こうにいるサポートとやりとりしたけど、結局MacBook Proでつなぎ直したらなんとかなった。
  • 受験環境は正直ブラウザ画面が非常に小さく小さく、使いにくすぎてめちゃくちゃ時間かかった。
  • ホストのブラウザを使っていいか聞いたけど受験要項を確認してとしか言われなかった。どうやって受験要項を確認するのかわからなかったので安牌とって受験環境の小さいブラウザを使い続けた。
  • 部屋を片付けるのが1番大変だった。
このスクラップは2023/05/04にクローズされました