なになれ

IT系のことを記録していきます。

Kubernetesでカナリアリリースを試す

Kubernetesではコンテナイメージを実行するのにDeploymentが使われます。
ただこのDeploymentではカナリアリリースを実現することはできません。
そこでArgo Rolloutsを使って、カナリアリリースを試します。

argoproj.github.io

以下に試した内容を示します。

Argo Rolloutsとは

Kubernetesにおけるデプロイ戦略にブルーグリーンデプロイメントとカナリアリリースを追加します。
今回はこの2つのうち、カナリアリリースを試します。

カナリアリリースを試す

Argo Rolloutsをインストールする

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://raw.githubusercontent.com/argoproj/argo-rollouts/stable/manifests/install.yaml

検証用のイメージを準備する

カナリアリリースによって、指定した割合で新バージョンになっているか確認するためのイメージを用意します。
nginxを使って、表示する内容を変更したイメージを用意します。

Dockerfile

FROM nginx
COPY app /usr/share/nginx/html

version:0.0.1

app/index.html

<html><body>1</body></html>

version:0.0.2

app/index.html

<html><body>2</body></html>

カナリアリリースを実施する

Argo Rolloutsをインストールすることで使用可能になったRolloutリソースでコンテナイメージを実行します。
strategycanaryを指定することでカナリアリリースになります。
setWeightで新バージョンをデプロイした時の割合を決めます。ここでは20%にしています。

nginx.yaml

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: sample
spec:
  replicas: 5
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
        - name: nginx
          image: hi1280/nginx:0.0.1
          ports:
            - containerPort: 80
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}

version:0.0.1のnginxイメージをrolloutリソースでデプロイします。

kubectl apply -f nginx.yaml 

最初のデプロイ時点でのreplicasetの状態です。

$ kubectl get replicaset -o wide
NAME                DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES               SELECTOR
sample-7bf8db5b89   5         5         5       7m51s   nginx        hi1280/nginx:0.0.1   app=sample,rollouts-pod-template-hash=7bf8db5b89

version:0.0.2のnginxイメージをrolloutリソースでデプロイします。

kubectl patch rollout sample -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"hi1280/nginx:0.0.2"}]}}}}' --type merge

デプロイした後のreplicasetの状態です。20%の割合でデプロイされていることが分かります。

$ kubectl get replicaset -o wide
NAME                DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES               SELECTOR
sample-5f8c88ddd4   1         1         1       8s      nginx        hi1280/nginx:0.0.2   app=sample,rollouts-pod-template-hash=5f8c88ddd4
sample-7bf8db5b89   4         4         4       9m47s   nginx        hi1280/nginx:0.0.1   app=sample,rollouts-pod-template-hash=7bf8db5b89

pausedfalseに更新することで、100%の割合でデプロイします。

kubectl patch rollout sample -p '{"spec": {"paused": false}}' --type merge

100%でデプロイした後のreplicasetの状態です。

$ kubectl get replicaset -o wide
NAME                DESIRED   CURRENT   READY   AGE    CONTAINERS   IMAGES               SELECTOR
sample-5f8c88ddd4   5         5         5       7m7s   nginx        hi1280/nginx:0.0.2   app=sample,rollouts-pod-template-hash=5f8c88ddd4
sample-7bf8db5b89   0         0         0       16m    nginx        hi1280/nginx:0.0.1   app=sample,rollouts-pod-template-hash=7bf8db5b89

少ない割合で新バージョンをデプロイして、その後、新バージョンに変更することができました。

指定した時間だけ新バージョンをデプロイする

カナリアリリースの設定によって、指定した時間だけ新バージョンを試して、自動的に旧バージョンに戻すといったことが可能です。

stepsに記述します。
ここでは、30秒間は20%の割合で新バージョンを試して、その後は0%の割合にすることで旧バージョンに戻しています。

nginx.yaml

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: sample
spec:
  replicas: 5
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
        - name: nginx
          image: hi1280/nginx:0.0.2
          ports:
            - containerPort: 80
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause:
          duration: 30
      - setWeight: 0
      - pause: {}

durationの単位は秒数になります。

Rolloutリソースでローリングアップデートを実施する

ローリングアップデートはDeploymentリソースでも実施できますが、リソースが異なるので取り回しが面倒です。
ローリングアップデートも同じリソースでできると便利です。

ローリングアップデートはcanary:{}で実現できます。

nginx.yaml

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: sample
spec:
  replicas: 5
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
        - name: nginx
          image: hi1280/nginx:0.0.2
          ports:
            - containerPort: 80
  strategy:
    canary: {}

まとめ

カナリアリリースは既に一般的なデプロイ戦略だと思いますが、Kubernetesではそれができないのが残念なところでした。
Argo Rolloutsを使うとKubernetesで簡単にカナリアリリースを実現することができます。

AnsibleとVagrantでKubernetesをセットアップする

以下を試してみた内容です。 kubernetes.io

2019年9月25日現在、この内容を試すとうまく動作しない部分があるので、直しつつ進めました。
なお、ローカルのOSはmacOS 10.14.6になります。
以下、Stepごとに修正点を補記します。

Step 1: Creating a Vagrantfile

変更なし

Step 2.1: Install Docker and its dependent components.

変更なし

Step 2.2: Kubelet will not start if the system has swap enabled, so we are disabling swap using the below code.

変更なし

Step 2.3: Installing kubelet, kubeadm and kubectl using the below code.

変更なし

Step 2.3: Initialize the Kubernetes cluster with kubeadm using the below code (applicable only on master node).

kubeletの設定ファイルを作成する記述を追加

    - name: Configure node ip
      lineinfile:
        create: yes # <- 追記
        path: /etc/default/kubelet
        line: KUBELET_EXTRA_ARGS=--node-ip={{ node_ip }}

Step 2.4: Setup the kube config file for the vagrant user to access the Kubernetes cluster using the below code.

変更なし

Step 2.5: Setup the container networking provider and the network policy engine using the below code.

calicoのバージョンを最新にする

    - name: Install calico pod network
      become: false
      command: kubectl apply -f https://docs.projectcalico.org/v3.9/manifests/calico.yaml  # <- 変更

Step 2.6: Generate kube join command for joining the node to the Kubernetes cluster and store the command in the file named join-command.

join-commandのコピー時に非root権限で実施する

    - name: Copy join command to local file
      become: false
      local_action: copy content="{{ join_command.stdout_lines[0] }}" dest="./join-command"

Step 2.7: Setup a handler for checking Docker daemon using the below code.

変更なし

Step 3: Create the Ansible playbook for Kubernetes node.

変更なし

Step 3.1: Start adding the code from Steps 2.1 till 2.3.

変更なし

Step 3.2: Join the nodes to the Kubernetes cluster using below code.

変更なし

Step 3.3: Add the code from step 2.7 to finish this playbook.

変更なし

Step 4: Upon completing the Vagrantfile and playbooks follow the below steps.

変更なし

全内容

gist.github.com

ISUCONに初参加しました

ISUCON9の予選が9/7,8の日程で開催されました。
会社の同僚と一緒にチームを組んで、初めてISUCONに参加しました。

isucon.net

2人チームでチーム名は「isuconなにもわからない」です。仕事ではNode.jsを使っているのでNode.jsで挑戦しました。
利用言語の比率が公開されていますが、Node.jsの利用者はそんなに多くなかったみたいです。

isucon.net

参考値ですが80番目くらいのスコアでした。

isucon.net

事前の準備

本番でやったこと

  • 効果あり

    • キャンペーンの値を0から4に変更する
    • itemsテーブルにindexを追加する
    • 複数台構成にする
  • あまり効果がなかった

    • ts-nodeからtscコンパイルしてnode.jsでアプリを実行する
    • 外部APIをPromise.allで並列実行する
    • 無駄なFOR UPDATEを除去
  • 改善できなかった

    • POST buyのロック問題

参加してみて気づいたこと

  • 計測の設定などのセットアップをするのが遅い
    • 午前中をほとんど使ってしまった
  • アプリレベルでの計測をシュッとできるようにする
    • 1つ1つconsole.timeを埋め込んで時間がかかった
  • tsc化するのに時間がかかった
    • typescriptに慣れる必要を感じた
  • 改善策がほとんど思いつかなかった
    • SQLは改善ポイントがあまりなかった
    • キャッシュする観点が思いつかなかった
    • 改善のパターンをもっと身につける必要を感じた
  • 人数は3名必要
    • 2名だとアプリとインフラで分かれることになって個人の力がないと辛い
    • 逆に1名でも本戦出場チームがそれなりにいるのですごいと思います

まとめ

準備や本番も含めて良い経験になりました。次回も参加したいと思います。