CKAを取るぞ
CKAを取るまでに学んだことをメモしていく
kube-apiserverはetcdを直接触る唯一のコンポーネント(知らなかった)
- kube-controller-manager
- kube-scheduler
- kubelet
は、kube-apiserverを介してetcdを操作する
kube-controller-manager
-
node-controller
- 5秒ごとにノードにプローブを送る
- プローブ失敗から
node-monitor-grace-period
(デフォルト40s)経過後、unreachableに移行 -
pod eviction timeout
(デフォルト5m)経過後、eviction が始まる
-
他にもreplication controllerなどたくさんのコントローラがある
- リソースの種類ごとにあるっぽい?
kube-scheduler
- どのPodをどのノードに置くかを決める
- 実際に配置するのはkubelet
kube-proxy
- 各ノードで、サービスへのルーティングルールを作成するプロセス
- 実体はiptables、ipvsなど。最近はCilliumがこの役目をやることもある
試験で便利な-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を指定することができない。
kubectl apply
は、適用されたファイルと、現在の状態(live object configration)と、最後に適用された状態(last applied configuration)の3つを比較する。
last applied configurationが必要なのは、フィールドの削除を行うため。つまり、適用されたファイルにフィールドがなく、last applied configurationにある場合はlive configurationからそのフィールドを削除する必要がある。
kubectlで複数のselectorを指定するときはコンマ区切り
kubectl get pods -l environment=production,tier=frontend
- 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がスケジュールされないことを表す。
ノードにラベルをつける
kubectl label nodes node-name key=value
node selector: ラベルがついているノードにスケジュールする
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
key: value
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
andLt
.
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される
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
と書いてある。
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>
dockerfile | pod definition |
---|---|
ENTORYPOINT | command |
CMD | args |
ENTRYPOINTとCMDを同時に指定した場合は、ENTRYPOINT + CMDの形でコマンドが渡される。
dockerfileにCMDが指定されている場合、pod definitionでcommandだけ変えてもCMDが後ろにくっつくので、そこをオーバーライドするにはargsフィールドを指定する必要がある。
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>
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しているだけであり、全く安全ではない。
保存時に暗号化するには下のような工夫が必要。
-
https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
また、podを作成する権限のある人はSecretをマウントすることで中身を見れてしまうので、RBACによる制御が必要(CKSの範囲っぽい)
あるいは、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>
livenessProbeとReadinessProbeはCKADでカバーするらしい
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が戻ってくるわけではない
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
クラスタのバックアップ
- 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
にしておく
- data directoryを新しく作る場合、ディレクトリの所有者を
-
systemctl daemon-reload && systemctl restart etcd
(systemctlでetcdを動かしている場合) - start kube-apiserver
kube-apiserverへの認証
- 静的パスワードファイル
- BASIC認証
- kube-apiserverの起動オプションに
--basic-auth-file
をつける - 1.20以降使用不可
- 静的トークンファイル
- Bearer認証
- kube-apiserverの起動オプションに
--token-auth-file
をつける - ノードに平文で認証情報が入るので?安全でない
- クライアント証明書
- 外部IDサービス (LDAP, Kerberosなど)
kube-apiserverに対する新しいクライアント証明書への署名はCertificates APIを使う
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."
kubectl get csr
kubectl certificates approve <csr-name>
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を指定することもできる
kubernetesのapi groupのリストを取得
curl localhost:6443 -k
pathを指定するとresource group が取得できる
curl localhost:6443/apis -k | grep name
なお、エンドポイントにはクライアント証明書が必要だが、kubectl proxy
でkube-apiserverを認証なしで叩けるようになる(kube-proxy
とは別物)
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を切り替えなくてもテストできる
namespaceに対して定義されるリソースへの権限はRole
とRoleBinding
で表される。
clusterに対して定義されるリソースへの権限は、ClusterRole
とClusterRoleBinding
で表す。
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
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は生成されなくなった。
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
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の実行ユーザどちらも指定可能
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.policyTypes
にegress
を加える
- Egressを制限するには、
- FlannelではNetwork Policyを使えないが、エラーも出ずに定義はできるので注意
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
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を指定する。
pvcはPVの容量分だけ(requests以上でも)使える
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で指定する
/proc/sys/net/ipv4/ip_forward
はデフォルトで0であり、あるデバイスから来たパケットを別のデバイスに転送することはない。これを1にすると転送する。
-
/etc/nsswitch.conf
に/etc/hosts
を優先するか/etc/resolv.conf
のDNSを優先するかが書いてある -
/etc/resolv.conf
にsearch
にドメインを書くとそのドメインのサブドメインをつけてDNSクエリを出すようになる
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
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ルールを作っている
- コンテナどうしを仮想スイッチ(
serviceの実体は、kube-proxyが各ノードに作る転送ルール
userspace, iptables, ipvsの実装がある(デフォルトはiptables)
iptables -L -t nat | grep <service name>
で作成されたiptablesルールを確認できる。
また、kube-proxyのログでもルールが作成されたことを確認できる。
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.conf
のsearch
エントリには次の値が入るので、サービスはFQDNでなくても解決できるsearch <namespace>.svc.cluster.local svc.cluster.local cluster.local
- PodはFQDNでないと解決できないらしい?
受かっていたのでクローズ
- 登録名を英語にしていたら英語のid cardを持っていなくてはじかれた。登録名を日本語に変えて再リトライ
- windowsのデスクトップで受けようとしたらwebカメラがカスすぎてidカードにピントが合わず、サポートに電話しろって言われたに。
- 拙い英語で海の向こうにいるサポートとやりとりしたけど、結局MacBook Proでつなぎ直したらなんとかなった。
- 受験環境は正直ブラウザ画面が非常に小さく小さく、使いにくすぎてめちゃくちゃ時間かかった。
- ホストのブラウザを使っていいか聞いたけど受験要項を確認してとしか言われなかった。どうやって受験要項を確認するのかわからなかったので安牌とって受験環境の小さいブラウザを使い続けた。
- 部屋を片付けるのが1番大変だった。