kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

今更ながらkOpsを使ってみる

f:id:kun432:20210120011530p:plain

kubeadm、EKS、GKE、kindといろいろ触ってきましたが、少しkOpsも触ってみようと思います。

目次

準備

kopsをインストールします。MacならHomebrewで。

$ brew install kops

$ kops version
Version 1.22.0

_r sshの鍵を作っておきます。nodeにsshするのに必要になります。~/.ssh/id_rsa、~/.ssh/id_rsa.pubがデフォルトで使用されるようです。こちらの環境だけなのかもですが、明示的に指定しないと公開鍵が設定されていませんでした。記事最後の補足を参照してください。

$ ssh key-gen

S3のバケットを作成します。ここがTerraformでいうところのtfstateの保存場所みたいなものになり、ここにクラスタの状態が保存されるようです。バケット名はkops-sample-202110としました。バージョニングと暗号化を有効にしておきます。

$ aws s3 mb s3://kops-sample-202110 

$ aws s3api put-bucket-versioning --bucket kops-sample-202110 --versioning-configuration Status=Enabled

$ aws s3api put-bucket-encryption --bucket kops-sample-202110 --server-side-encryption-configuration '{
    "Rules": [
        {
            "ApplyServerSideEncryptionByDefault": {
                "SSEAlgorithm": "AES256"
            }
        }
    ]
}'

順番が前後しましたが、IAMの権限は適宜設定しておく必要があります。

バケットを環境変数KOPS_STATE_STOREに設定しておきます。環境変数に設定しておくとkopsコマンドが自動で読み込んでくれます(そうでない場合は--stateで指定する必要があります。)

$ export KOPS_STATE_STORE=s3://kops-sample-202110

クラスタ作成

kops create clusterでクラスタを作成します。まずはdry-runしてみましょう。

$ kops create cluster \
  --name kops-sample.k8s.local \
  --zones "ap-northeast-1a,ap-northeast-1c,ap-northeast-1d" \
  --master-zones "ap-northeast-1a" \
  --dry-run \
  --output=yaml

--nameでクラスタ名を指定します。クラスタ名の末尾を"k8s.local"にしておくと、ノード間通信においてGossipプロトコルを使って名前解決できるように構築してくれます。そうでない場合はRoute53での名前解決となり、少し準備が必要みたいです。

--zones、--master-zonesでそれぞれworkerノード、masterノードが配置されるAZを指定する感じですね。

--dry-runと--outputを組み合わせると、こんな感じでkopsクラスタの構築を行うためのmanifestが出力されます。

apiVersion: kops.k8s.io/v1alpha2
kind: Cluster
metadata:
  creationTimestamp: null
  name: kops-sample.k8s.local
spec:
  api:
    loadBalancer:
      class: Classic
      type: Public
  authorization:
    rbac: {}
  channel: stable
  cloudProvider: aws
  configBase: s3://kops-sample-202110/kops-sample.k8s.local
  etcdClusters:
  - cpuRequest: 200m
    etcdMembers:
    - encryptedVolume: true
      instanceGroup: master-ap-northeast-1a
      name: a
    memoryRequest: 100Mi
    name: main
  - cpuRequest: 100m
    etcdMembers:
    - encryptedVolume: true
      instanceGroup: master-ap-northeast-1a
      name: a
    memoryRequest: 100Mi
    name: events
  iam:
    allowContainerRegistry: true
    legacy: false
  kubelet:
    anonymousAuth: false
  kubernetesApiAccess:
  - 0.0.0.0/0
  - ::/0
  kubernetesVersion: 1.22.2
  masterPublicName: api.kops-sample.k8s.local
  networkCIDR: 172.20.0.0/16
  networking:
    kubenet: {}
  nonMasqueradeCIDR: 100.64.0.0/10
  sshAccess:
  - 0.0.0.0/0
  - ::/0
  subnets:
  - cidr: 172.20.32.0/19
    name: ap-northeast-1a
    type: Public
    zone: ap-northeast-1a
  - cidr: 172.20.64.0/19
    name: ap-northeast-1c
    type: Public
    zone: ap-northeast-1c
  - cidr: 172.20.96.0/19
    name: ap-northeast-1d
    type: Public
    zone: ap-northeast-1d
  topology:
    dns:
      type: Public
    masters: public
    nodes: public

---

apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: master-ap-northeast-1a
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 3
    httpTokens: required
  machineType: t3.medium
  maxSize: 1
  minSize: 1
  nodeLabels:
    kops.k8s.io/instancegroup: master-ap-northeast-1a
  role: Master
  subnets:
  - ap-northeast-1a

---

apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: nodes-ap-northeast-1a
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
  machineType: t3.medium
  maxSize: 1
  minSize: 1
  nodeLabels:
    kops.k8s.io/instancegroup: nodes-ap-northeast-1a
  role: Node
  subnets:
  - ap-northeast-1a

---

apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: nodes-ap-northeast-1c
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
  machineType: t3.medium
  maxSize: 1
  minSize: 1
  nodeLabels:
    kops.k8s.io/instancegroup: nodes-ap-northeast-1c
  role: Node
  subnets:
  - ap-northeast-1c

---

apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: nodes-ap-northeast-1d
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
  machineType: t3.medium
  maxSize: 1
  minSize: 1
  nodeLabels:
    kops.k8s.io/instancegroup: nodes-ap-northeast-1d
  role: Node
  subnets:
  - ap-northeast-1d

master x 1、worker x 3のクラスタができるようですね。また、オプションで指定したとおり、workerは各AZごとに配置されています。

で、実際にクラスタを作る場合は--dry-runを外して--yesをつければよいですが、これだとコード管理できません。

$ kops create cluster \
  --name kops-sample.k8s.local \
  --zones "ap-northeast-1a,ap-northeast-1c,ap-northeast-1d" \
  --master-zones "ap-northeast-1a" \
  --yes

ということで、最初に実行したようにmanifestをファイルに出力してこれをgitで管理すればよいですね。あわせてクラスタ名も環境変数に入れておきましょう。

$ export KOPS_CLUSTER_NAME=kops-sample.k8s.local

$ kops create cluster \
  --name $KOPS_CLUSTER_NAME \
  --zones "ap-northeast-1a,ap-northeast-1c,ap-northeast-1d" \
  --master-zones "ap-northeast-1a" \
  --dry-run \
  --output=yaml > cluster.yaml

manifestから作成する場合はこうなります。

$ kops create -f cluster.yaml

Created cluster/kops-sample.k8s.local
Created instancegroup/master-ap-northeast-1a
Created instancegroup/nodes-ap-northeast-1a
Created instancegroup/nodes-ap-northeast-1c
Created instancegroup/nodes-ap-northeast-1d

To deploy these resources, run: kops update cluster --name kops-sample.k8s.local --yes

これでS3にkopsの設定が保存されます。まだAWS側にリソースは作成されていません。

$ aws s3 ls $KOPS_STATE_STORE --recursive
2021-10-20 01:59:16       1392 kops-sample.k8s.local/config
2021-10-20 01:59:16        527 kops-sample.k8s.local/instancegroup/master-ap-northeast-1a
2021-10-20 01:59:17        523 kops-sample.k8s.local/instancegroup/nodes-ap-northeast-1a
2021-10-20 01:59:17        523 kops-sample.k8s.local/instancegroup/nodes-ap-northeast-1c
2021-10-20 01:59:17        523 kops-sample.k8s.local/instancegroup/nodes-ap-northeast-1d

$ aws ec2 describe-instances
{
    "Reservations": []
}

リソースの作成はkops update clusterになります。ここもまずはdry-runしてみましょう。

$ kops update cluster

作成されるリソースが表示されます。初回は結構なボリュームになりますので詳細は割愛しますが、VPC、Autoscaling Group、ELBなどがまるっと作成されます。

(snip)
Will create resources:
  AutoscalingGroup/master-ap-northeast-1a.masters.kops-sample.k8s.local
    Granularity             1Minute
    InstanceProtection      false
    LaunchTemplate          name:master-ap-northeast-1a.masters.kops-sample.k8s.local
    LoadBalancers           [name:api.kops-sample.k8s.local id:api.kops-sample.k8s.local]
    MaxSize                 1
    Metrics                 [GroupDesiredCapacity, GroupInServiceInstances, GroupMaxSize, GroupMinSize, GroupPendingInstances, GroupStandbyInstances, GroupTerminatingInstances, GroupTotalInstances]
    MinSize                 1
    Subnets                 [name:ap-northeast-1a.kops-sample.k8s.local]
    SuspendProcesses        []
    Tags                    {kops.k8s.io/instancegroup: master-ap-northeast-1a, Name: master-ap-northeast-1a.masters.kops-sample.k8s.local, k8s.io/cluster-autoscaler/node-template/label/kops.k8s.io/kops-controller-pki: , k8s.io/cluster-autoscaler/node-template/label/kops.k8s.io/instancegroup: master-ap-northeast-1a, k8s.io/cluster-autoscaler/node-template/label/kubernetes.io/role: master, k8s.io/cluster-autoscaler/node-template/label/node.kubernetes.io/exclude-from-external-load-balancers: , KubernetesCluster: kops-sample.k8s.local, kubernetes.io/cluster/kops-sample.k8s.local: owned, k8s.io/cluster-autoscaler/node-template/label/node-role.kubernetes.io/control-plane: , k8s.io/cluster-autoscaler/node-template/label/node-role.kubernetes.io/master: , k8s.io/role/master: 1}
    TargetGroups            []

  AutoscalingGroup/nodes-ap-northeast-1a.kops-sample.k8s.local
    Granularity             1Minute
    InstanceProtection      false
    LaunchTemplate          name:nodes-ap-northeast-1a.kops-sample.k8s.local
    LoadBalancers           []
    MaxSize                 1

(snip)

  WarmPool/nodes-ap-northeast-1c.kops-sample.k8s.local
    Enabled                 false
    MinSize                 0

  WarmPool/nodes-ap-northeast-1d.kops-sample.k8s.local
    Enabled                 false
    MinSize                 0

Must specify --yes to apply changes

実際に作成する場合は--yesをつけて実行します。

$ kops update cluster --yes

I1020 02:16:04.464719   10704 apply_cluster.go:486] Gossip DNS: skipping DNS validation
I1020 02:16:05.438371   10704 executor.go:111] Tasks: 0 done / 109 total; 45 can run
W1020 02:16:05.689388   10704 vfs_castore.go:377] CA private key was not found
I1020 02:16:05.721809   10704 keypair.go:213] Issuing new certificate: "apiserver-aggregator-ca"
I1020 02:16:05.722095   10704 keypair.go:213] Issuing new certificate: "etcd-peers-ca-main"
I1020 02:16:05.752876   10704 keypair.go:213] Issuing new certificate: "etcd-manager-ca-events"
I1020 02:16:05.755179   10704 keypair.go:213] Issuing new certificate: "etcd-manager-ca-main"
I1020 02:16:05.759375   10704 keypair.go:213] Issuing new certificate: "etcd-clients-ca"
I1020 02:16:05.759390   10704 keypair.go:213] Issuing new certificate: "etcd-peers-ca-events"
W1020 02:16:05.860249   10704 vfs_castore.go:377] CA private key was not found
I1020 02:16:05.895603   10704 keypair.go:213] Issuing new certificate: "service-account"
I1020 02:16:05.902369   10704 keypair.go:213] Issuing new certificate: "kubernetes-ca"
I1020 02:16:06.982864   10704 executor.go:111] Tasks: 45 done / 109 total; 20 can run
I1020 02:16:08.345336   10704 executor.go:111] Tasks: 65 done / 109 total; 28 can run
I1020 02:16:10.324305   10704 executor.go:111] Tasks: 93 done / 109 total; 4 can run
I1020 02:16:10.534874   10704 executor.go:111] Tasks: 97 done / 109 total; 8 can run
I1020 02:16:11.526088   10704 executor.go:111] Tasks: 105 done / 109 total; 4 can run
I1020 02:16:12.875095   10704 executor.go:155] No progress made, sleeping before retrying 4 task(s)
I1020 02:16:22.878792   10704 executor.go:111] Tasks: 105 done / 109 total; 4 can run
I1020 02:16:24.920557   10704 executor.go:111] Tasks: 109 done / 109 total; 0 can run
I1020 02:16:24.969647   10704 update_cluster.go:326] Exporting kubeconfig for cluster
kOps has set your kubectl context to kops-sample.k8s.local
W1020 02:16:25.252095   10704 update_cluster.go:350] Exported kubeconfig with no user authentication; use --admin, --user or --auth-plugin flags with `kops export kubeconfig`

Cluster is starting.  It should be ready in a few minutes.

Suggestions:
 * validate cluster: kops validate cluster --wait 10m
 * list nodes: kubectl get nodes --show-labels
 * ssh to the master: ssh -i ~/.ssh/id_rsa ubuntu@api.kops-sample.k8s.local
 * the ubuntu user is specific to Ubuntu. If not using Ubuntu please use the appropriate user based on your OS.
 * read about installing addons at: https://kops.sigs.k8s.io/operations/addons.

作成が開始されました。完了までには少し時間がかかりますので、しばらく経ってから上記出力されている通り確認してみましょう。なお、KUBECONFIGもこの時点でセットされています。

$ kops validate cluster --wait 10m
Validating cluster kops-sample.k8s.local

INSTANCE GROUPS
NAME            ROLE    MACHINETYPE MIN MAX SUBNETS
master-ap-northeast-1a  Master  t3.medium   1   1   ap-northeast-1a
nodes-ap-northeast-1a   Node    t3.medium   1   1   ap-northeast-1a
nodes-ap-northeast-1c   Node    t3.medium   1   1   ap-northeast-1c
nodes-ap-northeast-1d   Node    t3.medium   1   1   ap-northeast-1d

NODE STATUS
NAME                            ROLE    READY
ip-172-20-114-156.ap-northeast-1.compute.internal   node    True
ip-172-20-52-226.ap-northeast-1.compute.internal    node    True
ip-172-20-56-170.ap-northeast-1.compute.internal    master  True
ip-172-20-77-187.ap-northeast-1.compute.internal    node    True

Your cluster kops-sample.k8s.local is ready

$ kubectl get nodes
NAME                                                STATUS   ROLES                  AGE   VERSION
ip-172-20-114-156.ap-northeast-1.compute.internal   Ready    node                   14m   v1.22.2
ip-172-20-52-226.ap-northeast-1.compute.internal    Ready    node                   14m   v1.22.2
ip-172-20-56-170.ap-northeast-1.compute.internal    Ready    control-plane,master   16m   v1.22.2
ip-172-20-77-187.ap-northeast-1.compute.internal    Ready    node                   14m   v1.22.2

クラスタが動作していますね。何かしらデプロイしてみましょう。

$ kubectl create deployment nginx --image=nginx
$ kubectl create service loadbalancer nginx --tcp=80:80
$ kubectl get all
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-6799fc88d8-zdwz9   1/1     Running   0          13m

NAME                 TYPE           CLUSTER-IP     EXTERNAL-IP                                                                  PORT(S)        AGE
service/kubernetes   ClusterIP      100.64.0.1     <none>                                                                       443/TCP        59m
service/nginx        LoadBalancer   100.71.13.67   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXX.ap-northeast-1.elb.amazonaws.com   80:30641/TCP   13m

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           13m

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-6799fc88d8   1         1         1       13m

serviceに表示されているELBのDNS名にアクセスできればOKです。

クラスタの設定変更

クラスタの設定変更は2つの方法があるようです。

  • kops editで変更、kops updateで反映
  • kops replace で 変更済manifestを適用する

manifestをコード管理するのであれば後者が良いですね。ということで少し修正してみましょう。worker nodeの設定のmaxSize/minSIzeを変更して増やします。今回は3AZにしたのでAZごとに3箇所あります。

(snip)
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: nodes-ap-northeast-1a
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
  machineType: t3.medium
  maxSize: 2   ★
  minSize: 2   ★
  nodeLabels:
    kops.k8s.io/instancegroup: nodes-ap-northeast-1a
  role: Node
  subnets:
  - ap-northeast-1a
(snip)

変更をS3上にまず反映します。

$ kops replace -f cluster.yaml

反映します。まずはdry-runから。

$ kops update cluster

(snip)

Will modify resources:
  AutoscalingGroup/nodes-ap-northeast-1a.kops-sample.k8s.local
    MaxSize                  1 -> 2
    MinSize                  1 -> 2

  AutoscalingGroup/nodes-ap-northeast-1c.kops-sample.k8s.local
    MaxSize                  1 -> 2
    MinSize                  1 -> 2

  AutoscalingGroup/nodes-ap-northeast-1d.kops-sample.k8s.local
    MaxSize                  1 -> 2
    MinSize                  1 -> 2

(snip)

Must specify --yes to apply changes

nodeが増えるということがわかりますね。では反映しましょう。

$ kops update cluster --yes

確認してみます。

$ kops validate cluster
Validating cluster kops-sample.k8s.local

INSTANCE GROUPS
NAME            ROLE    MACHINETYPE MIN MAX SUBNETS
master-ap-northeast-1a  Master  t3.medium   1   1   ap-northeast-1a
nodes-ap-northeast-1a   Node    t3.medium   2   2   ap-northeast-1a
nodes-ap-northeast-1c   Node    t3.medium   2   2   ap-northeast-1c
nodes-ap-northeast-1d   Node    t3.medium   2   2   ap-northeast-1d

NODE STATUS
NAME                            ROLE    READY
ip-172-20-114-156.ap-northeast-1.compute.internal   node    True
ip-172-20-52-226.ap-northeast-1.compute.internal    node    True
ip-172-20-56-170.ap-northeast-1.compute.internal    master  True
ip-172-20-77-187.ap-northeast-1.compute.internal    node    True

VALIDATION ERRORS
KIND    NAME            MESSAGE
Machine i-054dfeb9d396bd9ba machine "i-054dfeb9d396bd9ba" has not yet joined cluster
Machine i-06556522774c42413 machine "i-06556522774c42413" has not yet joined cluster
Machine i-0b561cb80abe171f3 machine "i-0b561cb80abe171f3" has not yet joined cluster

Validation Failed
Error: Validation failed: cluster not yet healthy

新しいnodeが3台追加されましたがまだクラスタには登録されていないようです。この時点ではkubectl get nodeでも表示されません。しばらく待つとこうなります。

$ kops validate cluster
Validating cluster kops-sample.k8s.local

INSTANCE GROUPS
NAME            ROLE    MACHINETYPE MIN MAX SUBNETS
master-ap-northeast-1a  Master  t3.medium   1   1   ap-northeast-1a
nodes-ap-northeast-1a   Node    t3.medium   2   2   ap-northeast-1a
nodes-ap-northeast-1c   Node    t3.medium   2   2   ap-northeast-1c
nodes-ap-northeast-1d   Node    t3.medium   2   2   ap-northeast-1d

NODE STATUS
NAME                            ROLE    READY
ip-172-20-114-156.ap-northeast-1.compute.internal   node    True
ip-172-20-122-241.ap-northeast-1.compute.internal   node    True
ip-172-20-52-226.ap-northeast-1.compute.internal    node    True
ip-172-20-56-170.ap-northeast-1.compute.internal    master  True
ip-172-20-61-191.ap-northeast-1.compute.internal    node    True
ip-172-20-77-187.ap-northeast-1.compute.internal    node    True
ip-172-20-89-255.ap-northeast-1.compute.internal    node    True

Your cluster kops-sample.k8s.local is ready

$ kubectl get node
NAME                                                STATUS   ROLES                  AGE   VERSION
ip-172-20-114-156.ap-northeast-1.compute.internal   Ready    node                   74m   v1.22.2
ip-172-20-122-241.ap-northeast-1.compute.internal   Ready    node                   41s   v1.22.2
ip-172-20-52-226.ap-northeast-1.compute.internal    Ready    node                   74m   v1.22.2
ip-172-20-56-170.ap-northeast-1.compute.internal    Ready    control-plane,master   76m   v1.22.2
ip-172-20-61-191.ap-northeast-1.compute.internal    Ready    node                   54s   v1.22.2
ip-172-20-77-187.ap-northeast-1.compute.internal    Ready    node                   74m   v1.22.2
ip-172-20-89-255.ap-northeast-1.compute.internal    Ready    node                   41s   v1.22.2

もう一つ別のパターンの変更も行ってみましょう。

先程はworker nodeを追加しました。つまり既存のworker nodeには特に変更はありません。既存のリソースに対する変更はローリングアップデートが必要になります。インスタンスタイプの変更で試してみましょう。

(snip)
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
  creationTimestamp: null
  labels:
    kops.k8s.io/cluster: kops-sample.k8s.local
  name: nodes-ap-northeast-1a
spec:
  image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20211001
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
  machineType: t3.large  ★
  maxSize: 2
  minSize: 2
  nodeLabels:
    kops.k8s.io/instancegroup: nodes-ap-northeast-1a
  role: Node
  subnets:
  - ap-northeast-1a
(snip)

先ほどと同じようにreplaceしてupdateして確認します。

$ kops replace -f cluster.yaml
$ kops update cluster

Will modify resources:
  LaunchTemplate/nodes-ap-northeast-1a.kops-sample.k8s.local
    InstanceType             t3.medium -> t3.large

  LaunchTemplate/nodes-ap-northeast-1c.kops-sample.k8s.local
    InstanceType             t3.medium -> t3.large

  LaunchTemplate/nodes-ap-northeast-1d.kops-sample.k8s.local
    InstanceType             t3.medium -> t3.large

Must specify --yes to apply changes

反映します。

$ kops update cluster --yes
(snip)
Cluster changes have been applied to the cloud.


Changes may require instances to restart: kops rolling-update cluster

クラスタの設定は変更されていますが、既存のnodeはまだ置き換わっていません。kops rolling-updateしてみます。もちろんdry-runで。

$ kops rolling-update cluster
NAME            STATUS      NEEDUPDATE  READY   MIN TARGET  MAX NODES
master-ap-northeast-1a  Ready       0       1   1   1   1   1
nodes-ap-northeast-1a   NeedsUpdate 2       0   2   2   2   2
nodes-ap-northeast-1c   NeedsUpdate 2       0   2   2   2   2
nodes-ap-northeast-1d   NeedsUpdate 2       0   2   2   2   2

Must specify --yes to rolling-update.

各nodeでアップデートが必要となっていますね。ではローリングアップデートします。

$ kops rolling-update cluster --yes
NAME            STATUS      NEEDUPDATE  READY   MIN TARGET  MAX NODES
master-ap-northeast-1a  Ready       0       1   1   1   1   1
nodes-ap-northeast-1a   NeedsUpdate 2       0   2   2   2   2
nodes-ap-northeast-1c   NeedsUpdate 2       0   2   2   2   2
nodes-ap-northeast-1d   NeedsUpdate 2       0   2   2   2   2
I1020 03:47:05.474509   14129 instancegroups.go:470] Validating the cluster.
I1020 03:47:06.664522   14129 instancegroups.go:503] Cluster validated.
I1020 03:47:06.664548   14129 instancegroups.go:306] Tainting 2 nodes in "nodes-ap-northeast-1a" instancegroup.
I1020 03:47:07.020806   14129 instancegroups.go:568] Detaching instance "i-XXXXXXXXXXXX71f3", node "ip-172-20-61-191.ap-northeast-1.compute.internal", in group "nodes-ap-northeast-1a.kops-sample.k8s.local".
I1020 03:47:07.477186   14129 instancegroups.go:167] waiting for 15s after detaching instance
I1020 03:47:22.482042   14129 instancegroups.go:470] Validating the cluster.
I1020 03:47:24.200364   14129 instancegroups.go:526] Cluster did not pass validation, will retry in "30s": machine "i-XXXXXXXXXXXX0367" has not yet joined cluster
(snip)

新しいec2インスタンスが作成され、古いものが削除される感じで、更新されていきます。台数が多いとそれなりに時間かかります。なお、podはevictされて別のnodeに移ります。

ローリングアップデートが完了するとこうなります。

$ kops rolling-update cluster
NAME            STATUS  NEEDUPDATE  READY   MIN TARGET  MAX NODES
master-ap-northeast-1a  Ready   0       1   1   1   1   1
nodes-ap-northeast-1a   Ready   0       2   2   2   2   2
nodes-ap-northeast-1c   Ready   0       2   2   2   2   2
nodes-ap-northeast-1d   Ready   0       2   2   2   2   2

No rolling-update required.

クラスタの削除

削除はkops delete clusterで。dry-runから。

$ kops delete cluster

TYPE            NAME                            ID
autoscaling-config  master-ap-northeast-1a.masters.kops-sample.k8s.local    lt-XXXXXXXXXXXXXXXX
autoscaling-config  nodes-ap-northeast-1a.kops-sample.k8s.local     lt-XXXXXXXXXXXXXXXX
autoscaling-config  nodes-ap-northeast-1c.kops-sample.k8s.local     lt-XXXXXXXXXXXXXXXX
autoscaling-config  nodes-ap-northeast-1d.kops-sample.k8s.local     lt-XXXXXXXXXXXXXXXX
(snip)

Must specify --yes to delete cluster

削除対象のリソースが表示されます。問題なければ削除します。

$ kops delete cluster --yes

補足

nodeにsshするために鍵を作っていましたが、実際にはログインできず。instance connectでつないでみるとautorized_keysが空になっていました。以下のように実行すると設定できましたが、これだけでrolling-updateが必要になるのは微妙ですね。

$ kops create secret --name ${KOPS_CLUSTER_NAME} sshpublickey admin -i ~/.ssh/id_rsa.pub
$ kops update cluster --yes
$ kops rolling-update cluster --yes

クラスタmanifest作成時に明示的に指定してあげることができるっぽいけど。

$ kops create cluster \
  --name $KOPS_CLUSTER_NAME \
  --zones "ap-northeast-1a,ap-northeast-1c,ap-northeast-1d" \
  --master-zones "ap-northeast-1a" \
  --ssh-public-key ~/.ssh/id_rsa.pub \
  --dry-run \
  --output=yaml > cluster.yaml

こんな感じで出力されてるのでいけてそうにみえるけど。

I1020 11:35:17.386822   17947 create_cluster.go:827] Using SSH public key: /Users/kun432/.ssh/id_rsa.pub

やっぱりauthorized_keysは空だった。多分kops create clusterで直接クラスタ作成すればできるんだろうけど、manifest作ってからkops create -fする場合は、manifestには何も記載がないし、kops create -f --ssh-public-keyは指定できないっぽいので、作成されないんだと思います。

manifestベースでやるとするならば事前にキーペアを作っておいて、manifest内でキーペア名を指定するのが良さそうかなと。

$ aws ec2 create-key-pair --key-name kops-sample-202110 --query 'KeyMaterial' --output text > kops-sample.pem
$ chmod 600 kops-sample.pem
$ kops create cluster \
  --name $KOPS_CLUSTER_NAME \
  --zones "ap-northeast-1a" \
  --master-zones "ap-northeast-1a" \
  --dry-run \
  --output=yaml > cluster.yaml

Clusterリソースのspec.sshKeyNameでキーペア名を追記。

apiVersion: kops.k8s.io/v1alpha2
kind: Cluster
metadata:
  creationTimestamp: null
  name: kops-sample.k8s.local
spec:
  sshKeyName: kops-sample-202110

あとはこれでクラスタ作ればOK。authorized_keysに公開鍵が含まれていることを確認できました。

まとめ

kopsで構築されたクラスタを触ったことはあるけど、ちゃんと理解してなかったので一通り触れてみて雰囲気をつかめたのがよかったです。manifestでコード管理できるのもよいですし、あと、今回はVPCから作成しましたが、既に存在するVPCのリソース上に作成することもできるみたいなので、追加導入もしやすそうですね。

参考

ChromeでGoogle Meetを開いたら「ON AIR」ライトを点灯、家族に「リモート会議中」を知らせてみた

f:id:kun432:20211008151507j:plain

以前にやったこの記事

悪くはないんですけど、使っていくと音声で呼び出すことすら面倒になる。もっとシームレスにやりたいというのはみんな考えることみたい。ということで色々調べてみた。ちなみにうちの会社ではGoogle Meetを使っています。


会議中にカメラがONになると、カメラのプロセスのCPU使用率があがるのを検出する方法。残念ながら会社はWindowsなのでこれは採用できず。PowerShellとか使えばできるのかもしれないけどPowerShell知らん。


Google Meetをブラウザで開いてマイクかカメラがONになったらwebhookを投げるというChrome拡張。見つけたときは歓喜したのだけど、どうもうまく動かなかった。CORSかな。ちなみにコードはこちらで公開されている(アップデートはされているけど試してない)


数日前にTwitterのタイムラインで見つけた。Chromeでタブの一覧にMeetのURLがあればHTTPリクエスト投げる自前Chrome拡張を書かれた様子。こういう事ができるのね、なるほど。

上記の記事の中ではTP-Linkのスマートプラグを使っててIFTTT経由で操作されている。うちはSwitchBotを使っていて、前の記事でも書いたとおりSwitchBotはAPI経由で操作ができるので、少し書き換えるだけでかんたんに動く。

const sb_token = "SwitchBotのトークン";
const device_id = "デバイスID";

var sb_url = `https://api.switch-bot.com/v1.0/devices/${device_id}/commands`;

const on_json =
{
  "command": "turnOn",
  "parameter": "default",
  "commandType": "command"
};

const off_json =
{
  "command": "turnOff",
  "parameter": "default",
  "commandType": "command"
};

var isTurnOn = 0;

function watchTabs() {
  chrome.tabs.query({}, (tabs) => {
    const isOpeningMeets = tabs.some((tab) => {
      return tab.url.match(/meet.google.com/);
    });

    if (isOpeningMeets && !isTurnOn) {
      request(sb_url, on_json);
      isTurnOn = 1;
    }

    if (!isOpeningMeets && isTurnOn) {
      request(sb_url, off_json);
      isTurnOn = 0;
    }
  });
}

function request(url, data) {
  var req = new XMLHttpRequest();
  req.open("POST", url);
  req.setRequestHeader('Authorization', `${sb_token}`);
  req.setRequestHeader('Content-Type', 'application/json');
  req.send(JSON.stringify(data));
}

chrome.tabs.onUpdated.addListener(watchTabs);
chrome.tabs.onRemoved.addListener(watchTabs);

トークンの取得やデバイスIDの確認は前回の記事にリンクがあるのでそちらを参照。

manifest.jsonはpermissionsのところをSwitchBot APIのURLに置き換えるだけ。

(snip)
  "permissions": ["tabs", "*://api.switch-bot.com/*"]
(snip)

この2ファイルを同じフォルダに入れておいて、Chromeの拡張機能→デベロッパーモード有効→パッケージ化されていない拡張機能を読み込む、で使えるようになります。こんな感じです。

ChromeでMeetのURLを開くと点灯、閉じると消灯しているのがわかりますでしょうか。

音声でコントロールできるのも悪くないんだけど、Meetに参加するには当然ブラウザを開くので、これならとてもシームレス、かなり満足度が高い!よい記事をありがとうございます!>magamingさん。

いろいろ応用もききそうなので遊んでみよう。

VyOSでBGPを試してみる

f:id:kun432:20211003125250p:plain

前回、KubernetesのDynamic PVのためにCephを試してみたのに続いて、今度はLoadBalancer serviceのためにMetalLBを試したい、そしてできればL2モードじゃなくてBGPモードで試したい、ということで、VyOSでBPGをやってみました。

目次

必要なもの

  • virtualbox
  • vagrant
  • vagrant-vyosプラグイン
    • Vagrantfile内でconfig.vagrant.pluginsを指定してるのでvagrant upすればインストールされると思います。

VMが6台ぐらい立ち上がるので多少はマシンパワー必要かもです。

構成

構成については以下を参考にさせてもらいました。

上記ではhigebuさんのvyos boxを使っていて1.1系なのだけど、ネットワークのお勉強のために以前に1.3系のvagrant box作ってルータ作ったりしたので1.3系でやってます。

1.3のvagrant boxはvagrant cloudに公開しているのでVagrantfileで参照するだけで使えます。1.2ぐらいからコマンドが結構変わってみたいなので、BGPの知識がないこともあって結構調べるのが大変だった(そして合っているか自信がないので、間違ってたら指摘してください)

あと、多少自分的にわかりやすいようにAS番号とかネットワークセグメントは変えてます。こんな感じ。

f:id:kun432:20211003125517p:plain

実際のルーティングも確認したいので各ルータのネットワーク内に端末をおきました。

使い方

Vagrantfileや各ルータのセットアップを行うスクリプトなどはGitHubで公開しています。

クローンします。

$ git clone https://github.com/kun432/vagrant-vyos-bgp-sample.git
$ cd vagrant-vyos-bgp-sample

upします。普通にアップしてもいいんだけど、vagrantでvirtualbox providerの場合、シーケンシャルにしか起動しないので時間がかかる。並列でできるようにシェルスクリプトを書きました。中身は単純で、vm一覧を取得してxargsでvagrantに渡してるだけ。parallel増やすと並列度があがります。

#!/bin/bash

parallel=3
subcmds="$@"

vagrant status --machine-readable | \
  perl -wnlaF"," -e 'print $F[1] if $F[2] =~ /metadata/;' | \
  xargs -P${parallel} -I {} vagrant $subcmds {}

ということで、VM起動。

$ ./vagrant-ctrl.sh up

ルータ3台とPC3台が起動します。

$ vagrant status
(snip)

rt-1                      running (virtualbox)
rt-2                      running (virtualbox)
rt-3                      running (virtualbox)
pc-1                      running (virtualbox)
pc-2                      running (virtualbox)
pc-3                      running (virtualbox)

(snip)

ルータについてはscripts以下にあるスクリプトでセットアップしてます。rt-1の場合だとこういう感じ。

#!/bin/vbash

source /opt/vyatta/etc/functions/script-template

set interfaces loopback lo address 1.1.1.1/32

set protocols bgp 65001 neighbor 10.10.1.2 remote-as '65002'
set protocols bgp 65001 neighbor 10.10.1.2 update-source '10.10.1.1'
set protocols bgp 65001 address-family ipv4-unicast network '192.168.1.0/24'
set protocols bgp 65001 parameters router-id '1.1.1.1'

set protocols static route 192.168.1.0/24 blackhole distance '254'

commit
save

BGPの細かい設定の意味がまだよくわかってないけど、1.3の公式ドキュメントにあるサンプルをもとに作ってます。

docs.vyos.io

vyos1.1の場合だとこんな感じらしい。

set protocols bgp 65001 neighbor 10.10.1.2 remote-as '65122'
set protocols bgp 65001 neighbor 10.10.1.2 soft-reconfiguration inbound
set protocols bgp 65001 neighbor 10.10.1.2 update-source  '10.10.1.1'
set protocols bgp 65001 network 11.11.11.11/32
set protocols bgp 65001 parameters router-id '1.1.1.1'

広報する自分のネットワークを設定する箇所は、1.1だと

set protocols bgp 65001 network 11.11.11.11/32

だけど、1.3ではエラーになるのでこうなる。

set protocols bgp 65001 address-family ipv4-unicast network '192.168.1.0/24'

あと、今回は指定していないけど、経路情報を保存しておく以下の設定についても、1.1ではこうだけど、

set protocols bgp 65001 neighbor 10.10.1.2 soft-reconfiguration inbound

1.3だとこうなるみたい。1.3の情報がほとんどないのでこの辺は苦しい。

set protocols bgp 65001 neighbor 10.10.1.2 address-family ipv4-unicast network soft-reconfiguration inbound 

なにはともあれVMが上がってきたら疎通確認してみましょう。

vagrant@pc-1:~$ ping 192.168.2.11
PING 192.168.2.11 (192.168.2.11) 56(84) bytes of data.
64 bytes from 192.168.2.11: icmp_seq=1 ttl=62 time=1.30 ms
64 bytes from 192.168.2.11: icmp_seq=2 ttl=62 time=1.42 ms
64 bytes from 192.168.2.11: icmp_seq=3 ttl=62 time=1.56 ms
(snip)

vagrant@pc-1:~$ ping 192.168.3.11
PING 192.168.3.11 (192.168.3.11) 56(84) bytes of data.
64 bytes from 192.168.3.11: icmp_seq=1 ttl=61 time=9.71 ms
64 bytes from 192.168.3.11: icmp_seq=2 ttl=61 time=2.14 ms
64 bytes from 192.168.3.11: icmp_seq=3 ttl=61 time=1.77 ms
(snip)
vagrant@pc-2:~$ ping 192.168.1.11
PING 192.168.1.11 (192.168.1.11) 56(84) bytes of data.
64 bytes from 192.168.1.11: icmp_seq=1 ttl=62 time=1.24 ms
64 bytes from 192.168.1.11: icmp_seq=2 ttl=62 time=1.75 ms
64 bytes from 192.168.1.11: icmp_seq=3 ttl=62 time=2.38 ms
(snip)

vagrant@pc-2:~$ ping 192.168.3.11
PING 192.168.3.11 (192.168.3.11) 56(84) bytes of data.
64 bytes from 192.168.3.11: icmp_seq=1 ttl=62 time=1.04 ms
64 bytes from 192.168.3.11: icmp_seq=2 ttl=62 time=1.40 ms
64 bytes from 192.168.3.11: icmp_seq=3 ttl=62 time=2.43 ms
(snip)
vagrant@pc-3:~$ ping 192.168.1.11
PING 192.168.1.11 (192.168.1.11) 56(84) bytes of data.
64 bytes from 192.168.1.11: icmp_seq=1 ttl=61 time=1.66 ms
64 bytes from 192.168.1.11: icmp_seq=2 ttl=61 time=2.38 ms
64 bytes from 192.168.1.11: icmp_seq=3 ttl=61 time=1.90 ms
(snip)

vagrant@pc-3:~$ ping 192.168.2.11
PING 192.168.2.11 (192.168.2.11) 56(84) bytes of data.
64 bytes from 192.168.2.11: icmp_seq=1 ttl=62 time=1.43 ms
64 bytes from 192.168.2.11: icmp_seq=2 ttl=62 time=1.25 ms
64 bytes from 192.168.2.11: icmp_seq=3 ttl=62 time=1.44 ms
64 bytes from 192.168.2.11: icmp_seq=4 ttl=62 time=1.28 ms
(snip)

各ルータは以下のネットワーク間の疎通はOKですね。では各ルータもみていきます。rt-1から。

vagrant@rt-1.internal:~$ show ip bgp
BGP table version is 3, local router ID is 1.1.1.1, vrf id 0
Default local pref 100, local AS 65001
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 192.168.1.0/24   0.0.0.0                  0         32768 i
*> 192.168.2.0/24   10.10.1.2                0             0 65002 i
*> 192.168.3.0/24   10.10.1.2                              0 65002 65003 i

Displayed  3 routes and 3 total paths

rt-2, rt-3のネットワークがBGPで広報されているようです。そして実際のルーティングで、rt-2, rt-3のネットワークへの経路が10.10.1.2経由になっているのがわかりますね。

vagrant@rt-1.internal:~$ show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

S>* 0.0.0.0/0 [210/0] via 10.0.2.2, eth0, 03:31:36
C>* 1.1.1.1/32 is directly connected, lo, 03:30:27
C>* 10.0.2.0/24 is directly connected, eth0, 03:31:36
C>* 10.10.1.0/24 is directly connected, eth2, 03:30:31
S   192.168.1.0/24 [254/0] unreachable (blackhole), 03:30:27
C>* 192.168.1.0/24 is directly connected, eth1, 03:30:31
B>* 192.168.2.0/24 [20/0] via 10.10.1.2, eth2, 03:30:25
B>* 192.168.3.0/24 [20/0] via 10.10.1.2, eth2, 03:30:24

rt-2とrt-3も貼っときます。

vagrant@rt-2.internal:~$ show ip bgp
BGP table version is 3, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 65002
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 192.168.1.0/24   10.10.1.1                0             0 65001 i
*> 192.168.2.0/24   0.0.0.0                  0         32768 i
*> 192.168.3.0/24   10.10.2.2                0             0 65003 i

Displayed  3 routes and 3 total paths

vagrant@rt-2.internal:~$ show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

S>* 0.0.0.0/0 [210/0] via 10.0.2.2, eth0, 04:41:13
C>* 2.2.2.2/32 is directly connected, lo, 04:40:02
C>* 10.0.2.0/24 is directly connected, eth0, 04:41:13
C>* 10.10.1.0/24 is directly connected, eth2, 04:40:07
C>* 10.10.2.0/24 is directly connected, eth3, 04:40:06
B>* 192.168.1.0/24 [20/0] via 10.10.1.1, eth2, 04:39:58
S   192.168.2.0/24 [254/0] unreachable (blackhole), 04:40:01
C>* 192.168.2.0/24 is directly connected, eth1, 04:40:06
B>* 192.168.3.0/24 [20/0] via 10.10.2.2, eth3, 04:39:58
vagrant@rt-3.internal:~$ show ip bgp
BGP table version is 3, local router ID is 3.3.3.3, vrf id 0
Default local pref 100, local AS 65003
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 192.168.1.0/24   10.10.2.1                              0 65002 65001 i
*> 192.168.2.0/24   10.10.2.1                0             0 65002 i
*> 192.168.3.0/24   0.0.0.0                  0         32768 i

Displayed  3 routes and 3 total paths

vagrant@rt-3.internal:~$ show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

S>* 0.0.0.0/0 [210/0] via 10.0.2.2, eth0, 04:44:21
C>* 3.3.3.3/32 is directly connected, lo, 04:43:11
C>* 10.0.2.0/24 is directly connected, eth0, 04:44:21
C>* 10.10.2.0/24 is directly connected, eth2, 04:43:15
B>* 192.168.1.0/24 [20/0] via 10.10.2.1, eth2, 04:43:05
B>* 192.168.2.0/24 [20/0] via 10.10.2.1, eth2, 04:43:05
S   192.168.3.0/24 [254/0] unreachable (blackhole), 04:43:11
C>* 192.168.3.0/24 is directly connected, eth1, 04:43:15

一応やりたかったことはできてますかね。

まとめ

VyOSでBGPを試してみました。とりあえず雰囲気はつかめたかなぁというところ。ネットワーク、もう少しちゃんと勉強しないとダメですね。

次はいよいよMetalLBだ。