Kubernetesクラスターにインストールした野良アプリケーションの再現性担保とバージョン管理

SREチームの須恵です。 今回は、Kubernetesクラスターの運用にまつわるちょっとした工夫について書くことにしました。

要約

以下のような問題があり、

URLから直接、手動でkubectl applyしたアプリケーションが存在することにより

  • インストールと更新が手動のためクラスターの再現性を低下させる
  • 更新を手動で行う必要がある
  • 誰かが更新に気がつく必要がある

それぞれ、以下のアプローチで解決を試みました。

  • 手元からインストールと更新を行うためクラスターの再現性を低下させる
    • シェルスクリプトとDockerfileをバージョン管理する
  • 更新を手動で行う必要がある
    • install-from-url.shを自動的に実行する
  • 誰かが更新に気がつく必要がある
    • DependabotにDockefileを管理させる

詳しく説明していきます。

何が問題か

Kubernetesクラスターを運用していると、自社で開発しているモノ以外のアプリケーションをクラスターにインストールする場面があるかと思います。 中には、開発元が提供しているマニフェストに手を加える必要がないものもあります。

たとえば、metrics-serverだとこのような具合です。

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

非常に簡単です。kubectl topコマンドが使えるようになりました。

めでたし、めでたし......

ところで、metrics-serverも現在進行系で開発中のアプリケーションであり、バージョンという概念が存在します。 f:id:tsue88oct:20201225140014p:plain

「何を当たり前のことを」と思われるかもしれませんが、 つまり、インストールした後にも更新されることがあるということです。

では、metrics-serverの更新は、どのようにクラスターに適用していくのでしょうか?

今までのやり方

正直に書きますと、

「たまたま気がついたときに」「手動でkubectl applyを実行して更新」

していました。

metrics-server以外にも、いくつか同様に取り扱っているアプリケーションが存在します。

いずれも、 開発元が提供するマニフェストに手を加える必要がないため、 手元でkubectl apply -f https://...することによってインストールして済ませてしまっていました。

手元からapplyしてしまっているため、クラスターの再現性を下げ、むしろペット化に近づけています。

また、何度か更新していると、単純に自分自身「面倒だ……」と思い始めます。

解決策

リポジトリを用意し、以下のような構造でファイルを配置します。

├── docker
│   └── Dockerfile
├── scripts
│   ├── apps
│   │   ├── argo-rollouts.sh
│   │   ├── cert-manager.sh
│   │   └── metrics-server.sh
│   └── install-from-url.sh

(弊社の場合、開発元が提供するマニフェストに「手を加える必要がある」ものを、自社で開発しているアプリケーションと別に管理するリポジトリと、そのリポジトリでプルリクエストをマージすることによって自動的にapplyを行うパイプラインは既に存在したので、そこに参加させることにします)

それぞれのファイルの内容を紹介していきます。

install-from-url.sh

#!/usr/bin/env bash
set -ev
cd `dirname $0`
cd apps
for file_name in *.sh ; do
    . ./$file_name
done

このスクリプトを、何かのツールでプルリクエストがマージされたタイミングなどで実行します。

実行されると、appsディレクトリ内のスクリプトを1つずつ呼び出します。

argo-rollouts.sh

#!/usr/bin/env bash
# https://github.com/argoproj/argo-rollouts
VERSION=$(cat ../../docker/Dockerfile | grep argo-rollouts | sed -e 's/.*://')
URL=https://raw.githubusercontent.com/argoproj/argo-rollouts/$VERSION/manifests/install.yaml
# Namespace作成 作成済みの場合のエラーを回避
kubectl create namespace argo-rollouts || true
kubectl apply -n argo-rollouts -f $URL

cert-manager.sh

#!/usr/bin/env bash
# https://github.com/jetstack/cert-manager
VERSION=$(cat ../../docker/Dockerfile | grep cert-manager-controller | sed -e 's/.*://')
URL=https://github.com/jetstack/cert-manager/releases/download/$VERSION/cert-manager.yaml
kubectl apply -f $URL

metrics-server.sh

#!/usr/bin/env bash
# https://github.com/kubernetes-sigs/metrics-server
VERSION=$(cat ../../docker/Dockerfile | grep metrics-server | sed -e 's/.*://')
URL=https://github.com/kubernetes-sigs/metrics-server/releases/download/$VERSION/components.yaml
kubectl apply -f $URL

apps下のスクリプトについて、以下はどのアプリにも共通しています。

  • Dockerfileから当該アプリの行を抽出し、タグを抽出する
  • 最後にkubectl apply -f $URL する

一方でアプリごとに異なる事情も存在します。

  • argo-rolloutsは、argo-rolloutsというNamespaceにインストールされることを想定されているが、マニフェスト内にNamespace定義がないので、別途作成が必要
  • cert-managerは、cert-managerというNamespaceにインストールされることを想定されているが、マニフェストにNamespace定義も含むため、作成は不要
  • metrics-serverは、Namespace定義を含まないが、kube-systemにインストールされる想定のため、作成は不要

このような個別の事情は個別のスクリプトで吸収します。

Dockerfile

# THIS IS DUMMY
FROM k8s.gcr.io/metrics-server/metrics-server:v0.4.1
FROM argoproj/argo-rollouts:v0.10.1
FROM quay.io/jetstack/cert-manager-controller:v1.1.0

このDockerfileですが、各アプリケーションのマニフェストで使用されているイメージを、ただベースイメージとして列挙しています。

このファイルを自分でビルドして何かに使用するわけではないので、ダミーです。

ここまでで実現できたこと

「たまたま気がついたときに」「Dockerfileのイメージタグを更新したプルリクエストを作成・マージすれば」

install-from-url.shが実行され、自動的にクラスターに対してapplyされるようになりました。

つまり……

「手動でkubectl applyして更新」

する必要はなくなりました。

また、

「どんなアプリケーションの」「どのバージョン」

がインストールされているかがリポジトリでバージョン管理され、ツールによる自動applyも行われるようになったので、クラスターの再現性向上にも寄与しています。

しかし、まだ

「たまたま気がつく」

必要があります。ある意味、一番大変なところと言えるかもしれません。

この大変さも、どうにかなくして、楽をしたいです。

そこでDependabotを利用します。

Dependabotで、もうひと工夫

Dependabotは、リポジトリが依存するパッケージを更新するためのプルリクエストを自動的に作成してくれます。

現在Dependabotは、GitHubの公式機能として提供されています。

Keep all your packages up to date with Dependabot - The GitHub Blog

GitHub版のドキュメントはこちらです。

依存関係を自動的に更新する - GitHub Docs

弊社ではGitHubを利用していますので、これを利用しない手はありません。

Dependabotのサポート対象にはDockerfileも含まれます。これを利用して、先ほどのダミーDockerfileをDependabotに監視・更新させます。

これにより、Dependabotが更新を知らせてくれるようになったので、

「たまたま気がつく」

必要もなくなりました。

f:id:tsue88oct:20201225141354p:plain
Dependabotが作成したプルリクエスト

やらなければならないことは、「プルリクエストをマージする」だけになりました。

余談

Dependabotは2つの点で非常に便利です。

  • 更新があることを知らせてくれる
  • 更新を取り込むために必要なファイルを変更してくれる

自分でやることはDependabotが作ってくれたプルリクエストをマージするだけなので楽ちんです(リリースノートも確認しましょう!)。

Dockerfileに対応していることは前述の通りですが、Kubernetesマニフェストの直接更新には対応していません。

記事中にこのようなことをポロリと書きました。

開発元が提供するマニフェストに「手を加える必要がある」ものを管理するリポジトリと、プルリクエストをマージすることによって自動的にapplyを行うパイプラインは既に存在

このようなアプリケーションの場合でも、先ほどの「ダミーDockerfile」にイメージを列挙するだけはしておくと、「更新があることを知らせてくれる」恩恵だけは受けることができます。

ごあんない

現在ANDPADでは、本格的にマイクロサービス化の波が来ており、Kubernetesの利用がさらに本格化することになりそうです。

マイクロサービス開発にご興味をお持ちの方、またプラットフォームの強化に力をお貸しいただけるKubernetes Ninjaの方、ご応募をお待ちしています。

engineer.andpad.co.jp