Argo CD, Helm, Renovateを使ったコンポーネントの継続的アップデート

皆さん、こんにちは!
この記事はSRE Advent Calendar 2023の16日目の記事になります。

今回の話は、k8sクラスタでのコンポーネントの継続的アップデートを行うための工夫についての紹介です。
SREとして信頼性や安定性を担保するためにアップデートの話は必要不可欠化と思うので今回の話にしました。

紹介例がおうちクラスタマニフェストになっていますが、実際の業務にも転用できるので参考になれば幸いです。

TL;DR

  • Kubernetes環境でのセキュリティ、機能性、パフォーマンスの維持には継続的なアップデートが必要不可欠
  • Argo CD, Helm Charts, Renovateの組み合わせを組み合わせることでアップデートの自動化、設定管理、依存関係の更新を迅速かつ安全に行う

継続的アップデートの重要性

システムの信頼性や安定性を担保するためにもアプリケーションやサービス、ツールの継続的なアップデートは重要です。
アップデートを行うことで、セキュリティの向上、バグ修正や機能改善、パフォーマンスや安定性の向上が見込めます。

そのため、Kubernetesクラスタでは、クラスタ自体のバージョンアップや、各種コンポーネントのアップデートはこまめに行うことが重要です。

継続的にアップデートを行う場合の課題

Kubernetesコンポーネントで継続的にアップデートを行う場合には以下のような課題が挙げられます。

  • マニフェスト更新の複雑化
  • APIバージョンやリソース変更への対応
  • 依存関係の解決

今回は、Argo CD, Helm, Renovateを使って課題を解消・低減する方法について紹介します。

Argo CD, Helm, Renovateの組み合わせ

Argo CD, Helm Charts, Renovateといったツールを組み合わせることで解消・低減することができます。
これらのツールは、異なる側面からアプローチし、Kubernetesの運用を簡素化し、自動化します。

Argo CD+Helm

Argo CDは、GitOpsなCDツールでリポジトリにあるマニフェストの状態を監視し、クラスタへ手動もしくは自動でリソースの作成や変更を反映してくれます。
そして、Argo CDはKustomizeやHelmといったツールもサポートしており、それらのマニフェストを指定することもできます。

argo-cd.readthedocs.io

この組み合わせでコンポーネントを管理すれば、Argo CDで自動デプロイする仕組みを構築することが可能です。
例として、実際のおうちクラスタで管理しているWordPressのArgo CD Applicationを張っておきます。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: wordpress
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  sources:
    - ref: home
      repoURL: https://github.com/corrupt952/home
    - path: manifests/wordpress/base
      repoURL: https://github.com/corrupt952/home
    - chart: mariadb
      repoURL: https://charts.bitnami.com/bitnami
      targetRevision: 14.1.4
      helm:
        releaseName: mariadb
        valueFiles:
          - $home/manifests/values/mariadb.yaml
    - chart: wordpress
      repoURL: https://charts.bitnami.com/bitnami
      targetRevision: 18.1.27
      helm:
        releaseName: wordpress
        valueFiles:
          - $home/manifests/values/wordpress.yaml
  destination:
    server: "https://kubernetes.default.svc"
    namespace: wordpress
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
    automated:
      prune: true
      selfHeal: true

アップデートは、Helm Chartsのバージョン(例だとtargetRevision)を上げるだけで比較的容易に行うことができます。

この組み合わせの時点で、挙げた課題についてはほぼ解消・低減できていますが、アップデート作業も楽にしたいのでRenovateを組み合わせます。

Renovate

Argo CD+Helmで管理されているリポジトリにRenovateを追加することで、アップデートをRenovateが自動化してくれます。

以下の例では、Argo CDやKubernetesマニフェストを読み取って各種コンポーネントをアップデートするための設定です。

{
  $schema: "https://docs.renovatebot.com/renovate-schema.json",
  extends: [
    "github>corrupt952/.github:default.json5",
  ],

  argocd: {
    fileMatch: ["manifests/argocd-config/base/.*\\.yaml"],
  },
  kubernetes: {
    fileMatch: ["manifests/.*/base/.*\\.yaml"],
  }
}

さらにRenvoateのAutomergeをパッチやマイナーバージョンにのみ適用することで、Pull Requestを手動でマージしなくてもクラスタへ反映することができるようになります。

おうちクラスタでの実践例

実際のマニフェストや設定は以下のリポジトリにまとめてあります。
参考にする場合は以下を見てみてください。

github.com

ただし、このレポはモノレポ運用しているため、AnsibleのPlaybookが混じっていることに注意してください。

まとめ

Argo CD, Helm, Renovateを組み合わせてコンポーネントの継続的なアップデートを行う方法を紹介しました。

より楽により安全にアップデートするためにも今回紹介した方法が参考になれば幸いです。

それではよい週末・年末を :)

おうちクラスタにCDIをデプロイしてUbuntuを動かす

前回の続きでContainerized Data Importer(以下、CDI)を導入してUbuntu仮想マシンを動かします。

khasegawa.hatenablog.com

マニフェストを用意する

マニフェストの取得コマンドは省きしますが、マニフェストをkustomization.yamlから参照するように定義しておきます。

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: cdi
resources:
  - https://github.com/kubevirt/containerized-data-importer/releases/download/v1.57.0/cdi-operator.yaml
  - https://github.com/kubevirt/containerized-data-importer/releases/download/v1.57.0/cdi-cr.yaml

クラスタにデプロイする

前回同様にArgo CD Applicationを定義して、CDIをデプロイします。
マニフェストは以下のようになります。

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cdi
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  sources:
    - path: manifests/cdi/base
      repoURL: https://github.com/corrupt952/home
  destination:
    server: "https://kubernetes.default.svc"
    namespace: cdi
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
    automated:
      prune: true
      selfHeal: true

ボリュームの定義をする

PVCを定義する方法もありますが、今回はDataVolumeを定義してUbuntuのイメージをベースとしたデータボリュームを定義しておきます。
今回はUbuntu 22.04を使うので以下のような書き方にしました。
urlstorageClassNameは環境に合わせて変更してください。

---
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
  name: ubuntu-volume
spec:
  source:
    http:
      url: "https://cloud-images.ubuntu.com/jammy/20231027/jammy-server-cloudimg-amd64.img"
  pvc:
    storageClassName: nfs
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 8Gi

Argo CDでデプロイしても良いのですが、動作確認のためだけなのでkubectl applyを直接実行して反映します。
この方法でもPVCは作成されるので作成されたかどうかは、以下のコマンドで確認できます。

# DataVolumeの確認
kubectl get dv

# PersistentVolumeClaimの確認
kubectl get pvc

DataVolumeの方でSuccessedと出ていれば問題ありません。
PVCがPendingのままの場合は、storageClassNameクラスタ内で利用可能なStorageClass`になっているのかは確認しましょう。

仮想マシンを定義する

VirtualMachineを定義します。
DataVolumeであるubuntu-volumedisk0という名前でマウントして使います。
また今回はbeestrapというISUCON向けのBootstrappingレポの動作確認も行いたいので Serviceを定義してCockpitやNetdataのポートを解放しておきました。

---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: ubuntu
  labels:
    kubevirt.io/os: linux
spec:
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/size: small
        kubevirt.io/domain: ubuntu
    spec:
      domain:
        cpu:
          cores: 2
        devices:
          disks:
            - name: disk0
              disk:
                bus: virtio
            - name: cloudinitdisk
              cdrom:
                bus: sata
                readonly: true
          interfaces:
            - name: default
              masquerade: {}
        machine:
          type: q35
        resources:
          requests:
            memory: 2048M
      networks:
        - name: default
          pod: {}
      volumes:
        - name: disk0
          persistentVolumeClaim:
            claimName: ubuntu-volume
        - name: cloudinitdisk
          cloudInitNoCloud:
            userData: |
              #cloud-config
              hostname: ubuntu
              ssh_pwauth: true
              password: ubuntu
              chpasswd:
                expire: false

---
apiVersion: v1
kind: Service
metadata:
  name: ubuntu
  labels:
    kubevirt.io/os: linux
spec:
  ports:
    - name: ssh
      port: 22
      targetPort: 22
    - name: cockpit
      port: 9090
      targetPort: 9090
    - name: netdata
      port: 19999
      targetPort: 19999
  selector:
    kubevirt.io/domain: ubuntu

こちらもDataVolumeと同様にkubectl applyで直接反映します。
1分ほど起動に時間がかかるかもしれませんが、起動できればsshで接続することもできます。

kubectl virt ssh --local-ssh ubuntu@ubuntu 

またVNCで接続したい場合はプロキシーとしてコマンドを実行するか、VNCクライアントを直接起動することもできます。
以下の例では、tigervncのクライアントがインストールされているWSL2の環境でクライアントを起動します。

kubectl virt vnc ubuntu

これで諸々の動作確認が終わりました。 Serviceで定義したポートに関しては仮想マシン内にCockpitなどをインストール後にport-forwardなどで動作確認すると接続できます。

おうちクラスタにKubeVirtをデプロイする

おうちクラスタにKubeVirtをデプロイした時の話を書いておきます。

実際に変更したコミットは以下にあるので参考にしてください。

github.com

マニフェストを用意する

公式のInstalling KubeVirt on Kubernetesを元に必要なファイルを書いていきます。 まずは以下のコマンドをkustomization.yamlに書き換えてArgo CDでデプロイできるようにします。

# Point at latest release
$ export RELEASE=$(curl https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt)
# Deploy the KubeVirt operator
$ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml
# Create the KubeVirt CR (instance deployment request) which triggers the actual installation
$ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml
# wait until all KubeVirt components are up
$ kubectl -n kubevirt wait kv kubevirt --for condition=Available

↓ kustomization.yamlに書き換える

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kubevirt
resources:
  - https://github.com/kubevirt/kubevirt/releases/download/v1.1.0/kubevirt-operator.yaml
  - https://github.com/kubevirt/kubevirt/releases/download/v1.1.0/kubevirt-cr.yaml

クラスタにデプロイする

おうちクラスタではArgo CDを使ってマニフェストを反映しています。 なので、Argo CD Applicationを作成してデプロイします。

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: kubevirt
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  sources:
    - path: manifests/kubevirt/base
      repoURL: https://github.com/corrupt952/home
  destination:
    server: "https://kubernetes.default.svc"
    namespace: kubevirt
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
    automated:
      prune: true
      selfHeal: true

後はデフォルトブランチにプッシュすれば、弊宅では反映されます。

これで諸々の作業は完了です。 krew経由でvirtctlをインストールしておくと楽なのでオススメしておきます。

余談

弊宅のクラスタネットワークはサブネットが異なるため、ただのsshだけでは接続ができないのでvirtctlを使ってsshをしています。

kubectl virt ssh cirros@testvm

似たようなネットワーク構成の人がいれば参考にまでにどうぞ

Rails 7.0.8でdatetimeなカラムの精度が修正された件について

はじめに

少し前にRails 7.0.8がリリースされましたが、その前のバージョンである7.0.7.2でマイグレーションした結果のdb/schema.rbが異なるということについて質問されたので、金曜夜に解説した内容を文章にまとめたものになります。

前提

  • Rails 7.0.7.2 -> 7.0.8へアップグレードした時に起きた現象
  • DBはMySQL
  • 発生する現象は、6.1なマイグレーションファイルでchange_columnしているdatetimeなカラムのprecisionがdb/schema.rbについたりつかなかったりする

TL; DR

カラムの精度に関する変更

7.0.8からマイグレーションファイルのバージョンが6.1の場合、datetimeなカラムの精度は指定なしでマイグレーションされるように修正されました。

github.com

これにより、Rails 7.0.7.2と7.0.8の間でカラムの定義に違いが生じる場合があります。

7.0.7.2以前の7系と7.0.8とでdatetimeなカラムの作成方法が異なります。

  • Rails 7.0.7.2 ... datetime(6) で作成
  • Rails 7.0.8 ... datetime で作成

そのため、7.0.8の修正が入る前の7系のRailsと、7.0.8のRailsのどちらのタイミングでマイグレーションしたかによって、カラムの定義が異なる可能性があります。

以下のようなマイグレーションファイルが定義されている場合、それぞれのバージョンでスキーマにどういった影響があるのかを確認してみます。

class Hoge< ActiveRecord::Migration[6.1]
  def change
    change_column :users, :inactive_at, :datetime, default: '2023-10-27 14:50:04'
  end
end

それぞれのバージョンでマイグレーションした場合のテーブル定義は以下のようになっており、inactive_atのdatetimeの精度指定が異なることが分かります。

# Rails 7.0.7.2でマイグレーションした場合のdatetimeなカラムの定義
>  show create table users \G;
*************************** 1. row ***************************
       Table: users
Create Table: CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `inactive_at` datetime(6) DEFAULT '2023-10-27 14:50:04.000000',
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

# Rails 7.0.8でマイグレーションした場合のdatetimeなカラムの定義
>  show create table users \G;
*************************** 1. row ***************************
       Table: users
Create Table: CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `inactive_at` datetime DEFAULT '2023-10-27 14:50:04',
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.000 sec)

カラム定義の違いの影響

このカラム定義の違いが、実際のアプリケーションにどのような影響を与えるのでしょうか?
例えば、7.0.7.2でマイグレーションしていたAさんの環境と、7.0.8にアップグレードした後にデータベースを作り直したBさんの環境とでは、rails db:schema:dumpの結果が異なります。
この挙動によっては、ローカルだけではなく特定のテスト環境や場合によっては本番環境を含めて、書く環境ごとにdatetimeの精度が異なるという可能性がありえます。

おわりに

本記事では、Rails 7.0.8と7.0.7.2の間でdatetimeなカラムのschema.rbが異なることについて解説しました。
アプリケーション開発やマイグレーションを行う際には、Railsのバージョンによるカラム定義の違いに注意する必要があります。
正確なカラムの定義を把握し、適切にマイグレーションを行うことで、問題なくアプリケーションを運用することができます。

Login with NotionをSinatraで実装する 

主に個人向けで作っているサービスでLogin with Notionを実装する必要があったので、その時の動作検証を行った時の作業ログです。
Login with Notionと仰々しく言っていますが、端的に言えばNotionと連携するためにOAuthを利用するだけの話です。

Public Integrationを作成する

Notion公式ドキュメントに従って、Public Integrationを作成します。

developers.notion.com

今回の動作検証時のIntegrationの設定は、以下のようになっています。
適当と書いてあるところは適当に値を設定しています。

  • Basic Information
    • Name ... 適当
  • Capabilities
    • Content Capabilities ... Read content
    • User Capabilities ... Read user information including email addresses
  • Organization Information
    • Company name ... 適当
    • Website or homepage ... 適当
    • Privacy policy ... 適当
    • Terms of use ... 適当
    • Support email ... 適当
  • OAuth Domain & URIs
    • Redirect URIs ... 動作確認のためにhttp://localhost:3000/callback

実装

Sinatraを使って簡易的に検証します。
今回は使っていないですが、OmniAuthが使える状況であればomniauth-notionというGemが存在するので、それを使うと楽かもしれません。

Gem

Gemfileは以下のようにSinatraを使うためにsinatrapumaを導入し、Sinatra::Reloaderを使いたいのでsinatra-contribも導入しておきます。
APIリクエストはNet::HTTPを使いますが、Faradayなどを使った方が間違いなく楽なのでお好みで使ってください。

# frozen_string_literal: true

source "https://rubygems.org"

gem 'sinatra'
gem 'sinatra-contrib'
gem 'puma'

仕様

本格的な実装の前に今回作成するアプリケーションの大まかな仕様を決めておきます。 

  • Endpoints
    • / ... ログインしているユーザーの名前を表示。ログインしていない場合は/loginへリダイレクト
    • /login ... Public IntegrationのAuthorization URLのリンクを配置
    • /logout ... セッションをクリアし、/へリダイレクト
    • /callback ... Authorization URLアクセス後に呼び出されるコールバックなので、渡された情報を元にアクセストークンとセッションの生成を行う
  • Credentials
    • Client ID ... NOTION_CLIENT_IDという環境変数に設定
    • Client Secret ... NOTION_CLINET_SECRETという環境変数に設定

Sinatraの設定

app.rbというファイルを作成して、Sinatraの設定を書きます。

# frozen_string_literal: true

require 'uri'
require 'net/http'
require 'sinatra'
require 'sinatra/reloader'

class Application < Sinatra::Base
  CLIENT_ID = ENV['NOTION_CLIENT_ID']
  CLIENT_SECRET = ENV['NOTION_CLIENT_SECRET']
  REDIRECT_URI = 'http://localhost:3000/callback' # Notionで設定したRedirect URLsと同じである必要アリ

  configure do
    # セッションを有効化
    # Cookieストアなので注意
    enable :sessions

    set :port, ENV.fetch('PORT', 3000)
    set :bind, ENV.fetch('BIND', '0.0.0.0') # ローカルで実行するなら不要

    # Hot reloadを有効化
    register Sinatra::Reloader
  end

  def authorization_url
    uri = URI.parse('https://api.notion.com/v1/oauth/authorize')
    uri.query = URI.encode_www_form(
      client_id: CLIENT_ID,
      redirect_uri: REDIRECT_URI,
      response_type: 'code',
      scope: 'all'
    )
    uri.to_s
  end
end

Application.run!

ログイン画面を実装する

ログイン画面を実装します。
とはいっても、Authorization URLするだけなので、/へアクセスされた場合に/loginへリダイレクトするのも実装しておきます。

class Application < Sinatra::Base
  ...

  get '/' do
    redirect '/login' if session[:access_token].nil? # セッションにアクセストークンがない場合は/loginへリダイレクト

    'TBD'
  end

  get '/login' do
    "<a href=\"#{authorization_url}\">Login with Notion</a>" # Authorization URLのリンクを表示
  end
end

この状態でアプリケーションを ruby app.rb で起動し、localhost:3000へアクセスすると、/loginへリダイレクトされます。

ログアウトを実装する

Notionからのコールバックを実装する前に先にログアウト処理を実装しておきます。
今回はセッションをクリアするだけでOKです。

class Application < Sinatra::Base
  ...

  # POSTの方がよいがGETのが動作検証が楽なのでGET
  get '/logout' do
    session.clear
    redirect '/'
  end
end

コールバックを実装する

Notionからのコールバックを実装します。 アクセストークンを発行したいので Create a token に従います。

class Application < Sinatra::Base
  ...

  # FYI: https://developers.notion.com/reference/create-a-token
  get '/callback' do
    code = params[:code]
    uri = URI.parse('https://api.notion.com/v1/oauth/token')
    bearer_token = Base64.strict_encode64("#{CLIENT_ID}:#{CLIENT_SECRET}") # Client ID,Secetを結合してBase64でエンコード
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    response = http.post(
      uri.path,
      # Payload
      URI.encode_www_form(
        grant_type: 'authorization_code',
        redirect_uri: REDIRECT_URI,
        code: code
      ),
      # Headers
      {
        'Authorization' => "Basic #{bearer_token}",
        'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
        'Notion-Version' => '2022-06-28',
      }
    )

    # 各種情報をセッションに保存
    session[:access_token] = JSON.parse(response.body)['access_token'] # アクセストークン
    session[:user_id] = JSON.parse(response.body)['owner']['id'] # ユーザID

    redirect '/'
  end
end

これでログイン処理の実装は終わりです。
後はユーザ名を表示すれば検証は終わりです。

ユーザ名を表示する

/でユーザ名を表示するようにします。
ユーザ情報を取得するには、Retrieve a userに従ってAPIリクエストします。

class Application < Sinatra::Base
  ...

  get '/' do
    redirect '/login' if session[:access_token].nil?

    uri = URI.parse("https://api.notion.com/v1/users/#{session[:user_id]}")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    response = http.get(
      uri.path,
      # Headers
      {
        'Authorization' => "Bearer #{session[:access_token]}",
        'Notion-Version' => '2022-06-28',
      }
    )
    results = JSON.parse(response.body)['results']
    user = results.first
    <<~HTML
      Hello, <b>#{user['name']}</b>!
      <br />
      <a href="/logout">Logout</a>
    HTML
  end

  ...
end

ここまで実装すれば一連の流れを画面上で操作でき、動作検証ができることが分かりました。

おわりに

Login with Notionを使ったサンプルをあまり見かけないので放流だけしておきました。
検証目的での実装なのでセキュリティは非常に脆弱なコードなので、間違ってもそのまま流用しないでください。

SRE NEXT 参加

先週末に開催されたSRE NEXTに参加してきました。

sre-next.dev

個人スポンサーとして参加していたのでノベルティもいただきました。

これ、ボトル保温もできるし、直接ステンレスマグとしても使えると思ってるんですけど、どうなんですかね? めちゃくちゃノベルティのマグ使いやすくて自宅では圧倒的な使用率を誇っています。

印象に残ったセッション

個人的にはやはりキーノートで樽石さんが話してくれた ギークがイオンに飛び込んだ結果がやばい〜Reliabilityと経営〜 が印象的でした。

www.youtube.com

SREっぽいプラクティスなのか?と言われると難しいところではあるものの、アーキテクチャ選定としての審美眼や 経歴を踏まえつつどういった戦略をとってきたのかが面白く、 また現状の私としては共感を得ることも多くて分かりみが深かったです。

感想

感想としては、会場も和気あいあいとして雰囲気が良かったことを覚えています。 また参加者層もかなり若い印象を受けました。

ただ私用による午後にはオンラインに切り替えてしまったので、懇親会に参加できなかったのが心残りです。 次回は予定調整しつつ、近くにホテルでもとっておこうかと思っています。 後は、ちゃんとCFPも出しましょうね。

おうちクラスタのcloudflaredをDaemonSetに変更する

おうちクラスタのcloudflaredはDeploymentとしてデプロイしていましたが、 DaemonSetとしてデプロイするように変更しました。

Why?

これといった強い理由はないですが、おうちクラスタの特定のノードにcloudfalredのPodが偏ってデプロイされて、 かつそのノードが何らかの理由で通信できない場合に外部からのアクセスが不可能になってしまうためです。

Deploymentでも特定のノードに偏らないように定義すること自体はできるのですが、 それをやるぐらいならDaemonSetにしておいた方が素直で良さそうだったので DaemonSetに変更しました。

DaemonSetとしてデプロイする

実際に変更する場合には、以下2つだけを実施すれば良いです。

  • kind: Deploymentkind: DaemonSetに変更する
  • DaemonSetで使えないパラメータを削除する(replicasなど)

github.com

これだけでDaemonSetに切り替えることができました。