ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

Terraformを使ってOpenStack VMにKubernetesクラスタをデプロイする

こんにちは。データセンターのサーバを管理しています宮本です。
今回は、先日のOpenStackとKubernetesを利用したマルチプラットフォームへのCI環境で紹介されたCI環境を構築する上で「土台」となるKubernetesの導入方法について紹介したいと思います。
そのためここでは、Kubernetesのデプロイを汎用的な技術として、Kubernetes構築の基本を把握できるよう体系立ててお話していきます。
これを踏まえて、本エントリーで学ぶ内容は以下の通りです。

  • Kubernetesクラスタの構成と動作
  • Packerを使ったVMイメージ作成
  • Terraformの概要と機能
  • Kubernetesデプロイの様子


またこれらを以下の流れでお話していきます。



マルチプラットフォームCI環境と本エントリーのスコープ

Kubernetes on OpenStack
改めて本エントリーでお話する内容について全体像を踏まえて少し具体的に書きます。
OpenStackのVMを使い最初のKubernetesを構築することで、その上でサービスやプラットフォームを展開できます。
少しややこしいですが図1に示しているような、OpenStack on “Kubernetes” on OpenStack も可能です。
これを見据えた構築にはなりますが今回はKubernetesの構築にフォーカスした内容にしたいので、前文””で囲んだところ、図で言うと①に絞ってご紹介します。


図1. OpenStack on Kubernetes on OpenStack

Kubernetesクラスタを構成する要素

それではKubernetesの概要とクラスタの構成についてお話していきます。



Kubernetesプロジェクトの役割とゴール
Kubernetesは、Dockerコンテナを複数のホストにまたがって管理するためのツールです。
以下に示すように、アプリケーションの運用負荷を軽減することをプロジェクトのゴールとして掲げています。

  • アプリケーションの高速なデプロイと柔軟なスケールアウト
  • シームレスなバージョンアップとリリース
  • 定義されたサービス構成を維持するための自動復旧
  • ハードウェアリソースの効率的な利用


Podとコンテナの関係
Kubernetesでコンテナの管理の単位として使われる「Pod」はコンテナの集合を指します。
例えば、クラスタの名前解決機能を提供するSkyDNSのPodは4つのコンテナで構成されます。

  • SkyDNSの構成
コンテナ 役割
skydns DNSサーバ
etcd データを格納する分散KVS
kube2sky クラスタ情報をskydnsに反映するブリッジ
healthz ヘルスチェック


MasterとNode
Kubernetesを利用するためにはKubernetesの核となるMasterと、コンテナのリソースとなるNodeが必要になります(図2)。
また、Kubernetesクラスタの情報を保持するetcdは負荷が高くなりがちなため、別途VMで用意します。


図2. Kubernetesクラスタの構成



Masterを構成するコンポーネント
Kubernetesクラスタを構成するために主要な機能を提供するサーバです。
コンテナの作成命令を受け取ったMasterコンポーネントは、Nodeへコンテナの作成命令を渡します。
コンテナの作成命令を含め、コンテナに関わるすべての操作はこのMasterコンポーネントを通じて行われます。

コンポーネント 役割
kube-controller-manager NodeとPodの状態を監視する
kube-scheduler Podを作成する際に作成先のNodeを決める
kube-apiserver Kubernetesの機能にアクセスするためのフロントエンドAPI
Calico BGPを使用してPod間の通信を可能にする
参考:CalicoによるKubernetesピュアL3ネットワーキング
etcd Kubernetesクラスタの情報を格納する分散KVS
kubelet コンテナの起動や維持を行うエージェント

参考:Kubernetes architecture



Nodeを構成するコンポーネント
Kubernetesのコンテナリソースとなるマシンです。
起動後のコンテナはkubeletを使ってMasterと連携し、クラスタの情報をやりとりします。

コンポーネント 役割
kube-proxy ロードバランスのための仮想IPアドレス(Cluster IP)のルーティング
Calico 同上
kubelet 同上


Kubernetes Addon
オプションのため図には示していませんが、Kubernetesのクラスタ機能を拡張するためのコンポーネントです。

コンポーネント 役割
Kubernetes dashboard Podの操作とモニタリングを行うための機能とUIを提供
Fluentd + Elasticsearch + Kibana クラスタのログ収集と可視化のための機能とUIを提供
Heapster + Influxdb + Grafana CPUやメモリなどのメトリクス監視を行うための機能とUIを提供
SkyDNS クラスタ内のPodの名前を解決


Kubernetes用VMイメージの作成








Packerを使う

Kubernetesクラスタの構築がしやすいよう、あらかじめ主要なパッケージがインストールおよびセットアップされているOpenStackのVMイメージをPackerで作成します(図3)。
Packerを使い、etcdに関するパッケージなど全てのVMで共通で使用するパッケージは事前にイメージに取り込みます。
こうすることで、VMを立ち上げた初期状態でKubernetesを構築するために、ある程度準備された環境が提供され、後のデプロイ作業を効率化します。

図3. PackerによるKubernetes用VM作成



Packerの設定とイメージ作成
OpenStackに上がっているVMイメージをベースにKubernetes用のイメージを生成します。
手元にイメージをダウンロードする必要はありません。
JSON形式の設定ファイルに変数(variables)やイメージ(builders)、スクリプトの定義(provisioners)等を記述した後ビルドして、ベースとなるイメージから新しいイメージを生成します。

  • Packer設定ファイル
# 変数の定義
{
"variables": {
  "os_source_image": "fd25a6a7-dc71-4e41-9cee-2745ffa003a7",
  "os_network": "43113c63-582f-4672-9f02-e84f8d6be9c0",
  "output_name": "centos4k8s"
},

# イメージの定義
"builders": [
  {
    "type": "openstack",
    "ssh_username": "centos",
    "image_name": "{{user `output_name`}}",
    "source_image": "{{user `os_source_image`}}",
    "flavor": "2v-4096M-40G"
    "networks": "{{user `os_network`}}",
    "ssh_pty": true
  }
],

# スクリプトの定義
"provisioners": [
  {
    "type": "shell",
    "script": "scripts/install_common.sh"
  } 
]


  • Kubernetes用VMイメージの作成 packer build
# packer build centos4k8s.json 
〜 省略 〜
==> Builds finished. The artifacts of successful builds are:
--> openstack: An image was created: 2d67f67d-3292-49c4-92d2-2db5b5ac7fe4
#

OpenStack上に新しいVMイメージが生成されます。



Kubernetesクラスタのデプロイ







デプロイに使用するTerraformは実行形式で配布されているためダウンロードしてきて必要に応じてパスを通して使用します。
Terraformの主な特徴は以下の通りです。

  • インフラのコード化:すべての構成情報をファイルに落としこむことで状況把握容易性の向上と属人化を防ぎます
  • 実行計画の作成:ドライランして変更内容をあらかじめ確認することで予期せぬエラーを防ぎます
  • 変更作業の自動化:デプロイに際して極力人が介入しないようにすることでヒューマンエラーを減らします
  • リソースグラフ:構成情報を可視化するためのものです。見づらいですが参考のためサンプルを載せます(図4)


図4. リソースグラフ terraform graph | dot -Tpng > graph.png



Terraform設定ファイル
Terraformはカレントディレクトリの以下のファイルを使ってインフラをデプロイします。

# クラウドプロバイダーとしてOpenStackを指定
provider "openstack" {
    user_name   = "${var.os_user_name}"
    password    = "${var.os_password}"
    tenant_name = "${var.os_tenant_name}"
    auth_url    = "${var.os_auth_url}"
}
# リソースの定義
resource "openstack_compute_instance_v2" "master" {
    count           = "${var.master_count}"
    name            = "${format("master-%03d", count.index+1)}.${var.cluster_domain}"
    image_name      = "${var.image_name}"
    flavor_name     = "${var.master_flavor_name}"
    scheduler_hints = { group = "${openstack_compute_servergroup_v2.master.id}" }
    user_data       = "${element(template_file.cloudinit_master.*.rendered, count.index)}"
    metadata {
        Name        = "${var.cluster_name}-${format("master-%03d", count.index+1)}"
        ShortName   = "${format("master-%03d", count.index+1)}"
        Role        = "kubernetes-master"
        servergroup = "${var.servergroup}"
    }
}


  • variables.tf : 変数とそのデフォルト値を定義します
# Kubernetes設定ディレクトリ
variable "kube_config_dir" {
    default     = "/etc/kubernetes"
    description = "configuration directory"
}


  • terraform.tfvars : 変数を上書きしたいときはコマンドラインの引数かここで指定します
image_name  = "centos4k8s"
flavor_name = "2v-4096M-40G"


  • terraform.tfstate : 最後にTerraformを実行した時のインフラの状態をJSON形式で保存したstateファイル
  • terraform.tfstate.backup : Terraformを実行する前に保存されるstateファイルのバックアップ


デプロイの流れ
デプロイ時の動きは以下の通りです(図5)。

図5. TerraformによるKubernetesクラスタのデプロイ



デプロイ実行
それでは実際にデプロイしていきます。
デプロイするときは実行(apply)の前にドライラン(plan)して変更内容を確認します。
また、パスワードなどのクレデンシャルな情報は設定ファイルに記述せず実行時にvarオプションの引数で渡した方が良いでしょう。

  • ドライラン terraform plan
# terraform plan -var os_password="XXXX" 
Refreshing Terraform state prior to plan...


The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ null_resource.calico_bootstrap
〜 省略 〜
    vars.#:   "" => "<computed>"

Plan: 145 to add, 0 to change, 0 to destroy.
#


  • デプロイ terraform apply
# terraform apply -var os_password="XXXX" 
null_resource.tls_shared: Refreshing state... (ID: 6382157447419249734)
template_file.cloudinit_master.0: Refreshing state... (ID: 825c4aaef363a7fbe0a0ad8119f20efec82f9f3ca8ea9dff7b140774b7bac377)
template_file.cloudinit_master.1: Refreshing state... (ID: 1f3e51f753819c47b1f6e60398046e0adacf6cf95e2b30c441a5fd77140fc1e0)
template_file.openstack_config: Refreshing state... (ID: 8c2fa0701a4fdf99d7d7a5d844700885bc7b8983ebe112725c1eaeedf23d76d8)
openstack_compute_servergroup_v2.master: Refreshing state... (ID: 230287f5-8002-4dce-8074-3122c1167b14)
〜 省略 〜

##BEGIN kube loopback block##
127.0.0.1   master-001.yahoo.co.jp
::1         master-001.yahoo.co.jp
##END kube loopback block##

##BEGIN kube hosts block##
172.23.59.162   master-001.yahoo.co.jp  master-001

172.23.63.254   etcd-001.yahoo.co.jp  etcd-001
172.23.63.253   etcd-002.yahoo.co.jp  etcd-002
172.23.42.112   etcd-003.yahoo.co.jp  etcd-003

172.23.63.251   node-001.yahoo.co.jp  node-001
172.23.63.250   node-002.yahoo.co.jp  node-002
172.23.63.252   node-003.yahoo.co.jp  node-003
172.23.63.247   node-004.yahoo.co.jp  node-004
172.23.63.249   node-005.yahoo.co.jp  node-005

##END kube hosts block##
#

これでKubernetesクラスタのデプロイが完了しました。


デプロイしたクラスタの確認








稼働状況
Terraformを実行した後は各VMが起動し、その上でPodやsystemdのプロセスが立ち上がります(図6)。
今回はデモのためMasterを1台にしていますが、本番環境では冗長構成にしたほうが良いでしょう。


図6. デプロイ後に立ち上がっているPodおよびコンテナとプロセスの一覧


  • Pod一覧 kubectl get pods
# kubectl get pods -o wide  
NAME                                             READY     STATUS    RESTARTS   AGE       NODE
fluentd-elasticsearch-master-001.yahoo.co.jp     1/1       Running   0          1d        master-001.yahoo.co.jp
fluentd-elasticsearch-node-001.yahoo.co.jp       1/1       Running   0          1d        node-001.yahoo.co.jp
fluentd-elasticsearch-node-002.yahoo.co.jp       1/1       Running   0          1d        node-002.yahoo.co.jp
fluentd-elasticsearch-node-003.yahoo.co.jp       1/1       Running   0          1d        node-003.yahoo.co.jp
fluentd-elasticsearch-node-004.yahoo.co.jp       1/1       Running   0          1d        node-004.yahoo.co.jp
fluentd-elasticsearch-node-005.yahoo.co.jp       1/1       Running   0          1d        node-005.yahoo.co.jp
kube-apiserver-master-001.yahoo.co.jp            1/1       Running   0          1d        master-001.yahoo.co.jp
kube-controller-manager-master-001.yahoo.co.jp   1/1       Running   3          1d        master-001.yahoo.co.jp
kube-dns-v11-lz3rr                               4/4       Running   0          18h       node-003.yahoo.co.jp
kube-dns-v11-vnm0s                               4/4       Running   3          18h       node-004.yahoo.co.jp
kube-proxy-node-001.yahoo.co.jp                  1/1       Running   0          1d        node-001.yahoo.co.jp
kube-proxy-node-002.yahoo.co.jp                  1/1       Running   0          1d        node-002.yahoo.co.jp
kube-proxy-node-003.yahoo.co.jp                  1/1       Running   0          1d        node-003.yahoo.co.jp
kube-proxy-node-004.yahoo.co.jp                  1/1       Running   0          1d        node-004.yahoo.co.jp
kube-proxy-node-005.yahoo.co.jp                  1/1       Running   0          1d        node-005.yahoo.co.jp
kube-scheduler-master-001.yahoo.co.jp            1/1       Running   0          1d        master-001.yahoo.co.jp


Podが作成できるか確認
これでkubectlコマンドでコンテナを作成できるようになりました。
試しに簡単なPodを立ち上げてうまくいくことを確認します。

  • Podの作成 kubectl create
# cat pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80
# kubectl create -f pod-nginx.yaml
pod "nginx" created
#

作成成功。

お疲れさまでした!

最後に

今回は、PackerとTerraformの利用方法を交えてOpenStackにKubernetesをデプロイする流れを紹介しました。
ここでの内容が少しでも皆様のお役に立てれば幸いです。
これまでヤフーでは仮想化基盤としてOpenStackの運用実績があったことから、Kubernetesのデプロイ先としてOpenStackを選択しました。
ベースをOpenStackで、そして今度はKubernetesをベースにOpenStackをデプロイしていく仕組みを作ることで、アップデート作業を含めた継続的なデプロイが可能になります。
ヤフーではこれからも世界基準のシステム構築を目指し、可能なところは自動化、人が介入する部分を極力減らす、安全・短時間でリリース作業が完了し、開発エンジニアが開発に集中できる仕組みづくりを積極的に行っていきます。

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました

このページの先頭へ