UsacloudでエンハンスドロードバランサのLet's Encrypt設定を行う

昨日の記事ではTerraformでエンハンスドロードバランサのLet's Encrypt設定を行えるようになったことを紹介しました。

febc-yamamoto.hatenablog.jp

昨日に続き、本日Usacloud v0.24.0がリリースされ、UsacloudにてエンハンスドロードバランサのLet's Encrypt設定が行えるようになりました。

github.com

本日はUsacloud v0.24で行われたエンハンスドロードバランサ関連の機能拡充について紹介します。

Usacloud v0.24.0

v0.24.0では以下のようなエンハンスドロードバランサ関連の機能拡充が行われました。

  • 100/500CPS対応
  • HTTPSへのリダイレクト対応
  • HTTP/2のサポート
  • Let's Encrypt対応

100/500CPS対応

enhanced-load-balancer createまたはplan-changeで100/500CPSプランを指定可能となりました。

$ usacloud enhanced-load-balancer create --plan 100 ...

HTTPSへのリダイレクト対応

公開ポートの設定時にリダイレクトの有効化ができるようになりました。

$ usacloud enhanced-load-balancer bind-port-add --mode http --port 80 --redirect-to-https <ID or Name>

--modeがhttpの時のみ指定可能ですのでご注意ください。

HTTP/2のサポート

公開ポートの設定時にリダイレクトの有効化ができるようになりました。

$ usacloud enhanced-load-balancer bind-port-add --mode https --port 443 --support-http2 <ID or Name>

こちらは--modehttpsの時のみ指定可能ですのでご注意ください。

Let's Encrypt対応

Let's Encrypt設定の有効化/証明書取得が行えるようになりました。

# Let's Encrypt設定の有効化
$ usacloud enhanced-load-balancer acme-setting --accept-tos --common-name www.example.com <ID or Name>

# 証明書の取得/更新
$ usacloud enhanced-load-balancer acme-renew <ID or Name>

# 証明書の確認
$ usacloud enhanced-load-balancer cert-info <ID or Name>

Let's Encrypt設定を有効化するには--accept-tosというフラグを指定する必要がある点に注意してください。
これはLet's Encrypt側の利用規約に同意するというフラグで、明示的に指定しないと有効化出来ないようにしています。

また、証明書発行対象のコモンネームが名前解決できるようになっている必要があります。

この辺りの詳細はさくらのクラウドのマニュアルを参照してください。

manual.sakura.ad.jp


ということでUsacloudでのエンハンスドロードバランサ関連機能拡充について紹介しました。 Terraformと上手く使い分けてみてくださいね。

以上です。

TerraformでさくらのクラウドのエンハンスドロードバランサのLet's Encrypt設定を行う

Terraform for さくらのクラウド v1.13.0リリース

本日リリースのTerraformさくらのクラウド向けプロバイダー v1.13.0にてエンハンスドロードバランサ関連の機能拡充が行われました。

  • 100/500CPSプランのサポート
  • HTTPSへのリダイレクト機能
  • HTTP/2のサポート
  • Let's Encrypt設定

Release v1.13.0 · sacloud/terraform-provider-sakuracloud · GitHub

新プラン/HTTPSへのリダイレクト/HTTP2のサポート

これらについては以下のようなtfファイルで利用可能となっています。

resource "sakuracloud_proxylb" "foobar" {
  name = "terraform-test-proxylb-acme"

  # 100CPSプランを利用
  plan = 100

  vip_failover = true
  health_check {
    protocol = "http"
    delay_loop = 10
    host_header = "usacloud.jp"
    path = "/"
  }

  bind_ports {
    proxy_mode = "http"
    port       = 80

    # HTTPSへのリダイレクトを有効化
    redirect_to_https = true
  }
  bind_ports {
    proxy_mode = "https"
    port       = 443

    # HTTP/2サポートの有効化
    support_https = true
  }
  servers {
      ipaddress = "${sakuracloud_server.server01.ipaddress}"
      port = 80
  }
}

Let's Encryptの利用

Let's Encryptを利用するには新設されたリソースsakuracloud_proxylb_acmeを利用する必要があります。

# エンハンスドロードバランサでのLet's Encrypt設定
resource sakuracloud_proxylb_acme "foobar" {
  proxylb_id = sakuracloud_proxylb.foobar.id
  accept_tos = true # 規約への同意
  common_name = "www.example.com"
  update_delay_sec = 120
}

注意点としてはさくらのクラウドのマニュアルに記載の条件を満たす必要があるという点があります。

f:id:febc_yamamoto:20190625160913p:plain

manual.sakura.ad.jp

  • 待ち受けポート(bind_ports)を適切に設定すること
  • 対象のコモンネーム(FQDN)がエンハンスドロードバランサのVIP or FQDNに向いていること

2番目の条件である、DNSレコードの設定を含めたtfファイルの例は以下の通りです。

# エンハンスドロードバランサの定義
resource "sakuracloud_proxylb" "foobar" {
  name = "terraform-test-proxylb-acme"
  plan = 100
  vip_failover = true
  health_check {
    protocol = "http"
    delay_loop = 10
    path = "/"
  }

  # Let's Encryptを利用するにはhttp/https両方のbind_portsが必要
  bind_ports {
    proxy_mode = "http"
    port       = 80

    redirect_to_https = true
  }
  bind_ports {
    proxy_mode = "https"
    port       = 443

    support_https = true
  }
  servers {
      ipaddress = "${sakuracloud_server.server01.ipaddress}"
      port = 80
  }
}

# エンハンスドロードバランサでのLet's Encrypt設定
resource sakuracloud_proxylb_acme "foobar" {
  proxylb_id = sakuracloud_proxylb.foobar.id
  accept_tos = true # 規約への同意
  common_name = "www.example.com"
  update_delay_sec = 120
}

resource sakuracloud_server "server01" {
  name = "terraform-test-server01"
  graceful_shutdown_timeout = 10
}

# エンハンスドロードバランサのVIP/FQDNを解決するためのDNSレコード設定
data sakuracloud_dns "zone" {
  name_selectors = ["example.com"]
}

resource "sakuracloud_dns_record" "record" {
  dns_id = data.sakuracloud_dns.zone.id
  name   = "www"
  type   = "CNAME"
  value  = "${sakuracloud_proxylb.foobar.fqdn}."
  ttl    = 10
}

この例ではエンハンスドロードバランサのFQDNを証明書発行対象のFQDNのCNAMEとして登録しています。

注意点として、DNSレコード登録直後は証明書発行対象のFQDNが解決できない場合があります。 このためにsakuracloud_proxylb_acmeにはupdate_delay_secという項目を設けています。
これはリソース作成時に指定秒数待つための項目です。手元の環境では120秒くらいに設定すると上手くいっていました。

終わりに

TerraformからLet's Encryptの設定〜設定を行うためのレコードの登録まで一括して行えるのは非常に楽だと思います。
ぜひご活用ください。

以上です。

ConftestでOpenPolicyAgent/Regoを使いTerraformのコードにポリシーを適用してみる

今日はConftestを用いてTerraformでのインフラコードにポリシーを適用してみます。

TerraformでのインフラコードのUnitTest

terraform validateでの構文チェック

Terraformではtfファイルの構文チェックを行ってくれるterraform validateコマンドが提供されています。 実行するとtfファイルの構文誤りやパラメータ名間違いなどを検出してくれます。

$ terraform validate

Error: Unsupported argument ← パラメータ名間違い

  on test.tf line 6, in data "sakuracloud_server" "server":
   6:   name_selectorsa = ["sakura-dev"]

An argument named "name_selectorsa" is not expected here. Did you mean
"name_selectors"?

これを利用すれば最低限tfファイルとして正しく書けているかのテストが行えます。

特にTerraform v0.12からは以下のように変数に型情報を与えることが出来るようになっており、モジュール利用時などにより厳密なチェックが行えるようになりました。

# var.nodesはaddressとuserというフィールドを持つオブジェクトのリストしか指定できない
variable "nodes" {
  type = list(object({
    address = string,
    user    = string,
  }))
}

ポリシーの適用

テストの際、terraform validateでの構文チェック以外にも様々な制約を課したい/ポリシーを適用したいことが多々あります。

例えば、

といった場合です。

Terraform EnterpriseであればSentinelを用いてポリシーの適用が行えるのですが、OSS版の場合、現時点ではSentinelを利用できません。

そこで今回はConftestを用いてポリシーの適用を行ってみました。

Conftest

Conftestについてはこちらの記事が詳しいです。

kenfdev.hateblo.jp

ConftestはYAMLあるいはJSONで定義された設定ファイルに対してテストを書けるというツールです。
面白いのは、テストに使うのがOpen Policy AgentのRegoというポリシー用の言語だという点です。

ConftestでOpenPolicyAgent/Regoを用いてポリシーを定義しておいてCIでConftestを実行すればポリシーの適用/強制ができそうです。

ConftestのリポジトリにはTerraformのコードをテストする例がありますのでそちらを参考にポリシーを書いてみます。

github.com

conftestでのterraformインフラコードのテスト

Conftestはコマンドラインツールとなっており、以下のように実行することでYAML/JSONファイルのテストが行えます。

$ conftest test <file>

デフォルトではカレントディレクトリのpolicyディレクトリ配下を参照するようになっていますのでこちらにポリシーファイル(.rego)を作成していきます。 (この挙動は-pまたは--policyオプションで変更可能です)

tfファイルをどうやってテストするの?

TerraformでのインフラコードはJSONでも記載できますが、通常はHCL(.tf)で記載していると思います。 conftestはHCLを読んでくれませんのでどうにかしてJSON/YAMLに変換する必要があります。

ConftestのリポジトリにあるTerraformの例ではplanファイルを出力した上でterraform showJSON出力オプションを指定して実行することでtfファイルを間接的にテストするという方法を取っています。

例えば、以下のようなtfファイルがある場合、

resource sakuracloud_server "server" {
  name   = "example"
  core   = 1
  memory = 1
}

これを元にplanファイル生成〜terraform showJSON出力すると以下のようになります。

# planファイルを出力
$ terraform plan --out plan.tfplan

# terraform showをJSONで出力
$ terraform show -json plan.tfplan | jq .
{
  "format_version": "0.1",
  "terraform_version": "0.12.1",
  "planned_values": {
    "root_module": {
      "resources": [
        {
          "address": "sakuracloud_server.server",
          "mode": "managed",
          "type": "sakuracloud_server",
          "name": "server",
          "provider_name": "sakuracloud",
          "schema_version": 1,
          "values": {
            "additional_nics": null,
            "commitment": "standard",
            "core": 2,
            "description": null,
            "disable_pw_auth": null,
            "graceful_shutdown_timeout": 60,
            "hostname": null,
            "icon_id": null,
            "interface_driver": "virtio",
            "memory": 4,
            "name": "test",
            "nic": "shared",
            "note_ids": null,
            "password": null,
            "private_host_id": null,
            "ssh_key_ids": null
          }
        }
      ]
    }
  },
  "resource_changes": [
    {
      "address": "sakuracloud_server.server",
      "mode": "managed",
      "type": "sakuracloud_server",
      "name": "server",
      "provider_name": "sakuracloud",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "additional_nics": null,
          "commitment": "standard",
          "core": 2,
          "description": null,
          "disable_pw_auth": null,
          "graceful_shutdown_timeout": 60,
          "hostname": null,
          "icon_id": null,
          "interface_driver": "virtio",
          "memory": 4,
          "name": "test",
          "nic": "shared",
          "note_ids": null,
          "password": null,
          "private_host_id": null,
          "ssh_key_ids": null
        },
        "after_unknown": {
          "additional_display_ipaddresses": true,
          "cdrom_id": true,
          "disks": true,
          "display_ipaddress": true,
          "dns_servers": true,
          "gateway": true,
          "id": true,
          "ipaddress": true,
          "macaddresses": true,
          "nw_address": true,
          "nw_mask_len": true,
          "packet_filter_ids": true,
          "private_host_name": true,
          "tags": true,
          "vnc_host": true,
          "vnc_password": true,
          "vnc_port": true,
          "zone": true
        }
      }
    }
  ],
  "configuration": {
    "root_module": {
      "resources": [
        {
          "address": "sakuracloud_server.server",
          "mode": "managed",
          "type": "sakuracloud_server",
          "name": "server",
          "provider_config_key": "sakuracloud",
          "expressions": {
            "core": {
              "constant_value": 2
            },
            "memory": {
              "constant_value": 4
            },
            "name": {
              "constant_value": "test"
            }
          },
          "schema_version": 1
        }
      ]
    }
  }
}

これをconftestコマンドに読ませることでテストを行います。

ポリシーの作成

今回は試しにサーバのコア数は2以上、メモリは4GB以上を指定しないといけないというポリシーにしてみます。

ポリシーファイルは以下の内容でpolicy/instance_type.regoというファイルを作成します。

package main

# コア数は2以上であること
deny[msg] {
  resource := input.resource_changes[index]
  resource.type == "sakuracloud_server"
  resource.change.after.core < 2
  msg = "sakuracloud_server.core must be greater than 2"
}

# メモリは4GB以上であること
deny[msg] {
  resource := input.resource_changes[index]
  resource.type == "sakuracloud_server"
  resource.change.after.memory < 4
  msg = "sakuracloud_server.memory must be greater than 4"
}

テスト実行(ポリシー適用)

それでは早速conftestでテストを実行(ポリシーを適用)してみます。

Makefileの作成

プランファイルの出力~JSONへの変換は毎回コマンド入力するのも大変なのでMakefileでまとめておきます。

NAME := myproject

COMMAND := terraform
PLAN = $(NAME)-plan.tfplan
SHOW = $(NAME)-show.json
CODE = $(NAME).tf


all: test

plan: $(PLAN)

$(PLAN): $(CODE)
    $(COMMAND) plan -out $(PLAN)

show: $(SHOW)

$(SHOW): plan
    $(COMMAND) show -json $(PLAN) > $(SHOW)

test: show
    cat $(SHOW) | conftest test -

clean:
    @rm -f $(PLAN) $(SHOW)

.PHONY: plan show test all clean

これでmakeするだけでテスト可能になります。 なお、このMakefileだと中間ファイルが作成されますので必要に応じてcleanを実行したり.gitignoreに追記したりしておいてください。

実行

現時点でのtfファイルは以下の通りです。

resource sakuracloud_server "server" {
  name   = "example"
  core   = 1
  memory = 1
}

ポリシーである2コア以上かつ4GBメモリ以上に反しているのでエラーとなるはずです。

$ make

# [中略]
cat example-show.json | conftest test -
   sakuracloud_server.core must be greater than 2
   sakuracloud_server.memory must be greater than 4
make: *** [test] Error 1

エラーになりましたね!

今度はtfファイルを修正して実行してみます。

resource sakuracloud_server "server" {
  name   = "example"
  core   = 2
  memory = 4
}
# [中略]

cat example-show.json | conftest test -

今度はエラーとなりませんでしたね。

あとは必要に応じてポリシーを充実させ、CIに組み込めば良さそうです。

終わりに

Open Policy Agent/Regoを初めて使ってみましたがなかなか面白いですね。 TerraformのようなインフラコードのテストでShift left testing出来ると効率がグッと上がりますのでどんどん活用していきたいです。

以上です。

参考情報

TerraformでのプロビジョニングにVNCを使う

TerraformでVNCでのプロビジョニングを行えるようにするプラグインterraform-provisioner-vncを公開しました。

github.com

Terraformでのプロビジョニング

Terraformではリソースの初期設定や削除時のクリーンアップ処理などを行えるようにプロビジョナーという仕組みが用意されています。

www.terraform.io

Terraform v0.12.1の時点では以下のようなプロビジョナーが利用可能となっています。

  • chef Provisioner
  • file Provisioner
  • habitat Provisioner
  • local-exec Provisioner
  • remote-exec Provisioner
  • salt-masterless Provisioner

SSH/WinRMが利用できないリソースのプロビジョニングは?

プロビジョナーのうちlocal-exec以外はSSHまたはWinRM(SSHのみの場合もある)でリモートに接続してなんらかの処理を行うようになっています。 これはSSH/WinRMが利用できないリソース、例えば初期状態ではリモート接続を許可していないようなリソースではこれらを利用できないということです。

この問題の解決方法の一つとして、VNC経由でコマンドを実行するのが今回公開したterraform-provisioner-vncです。 Packerでいうboot_commandを実行するものとなっており、主にSSH/WinRM出来るまでの環境を整えることを目的としています。

terraform-provisioner-vncの使い方

通常のプロビジョナーと同じく、対象のリソースやnull_resourceにprovisionerブロックを記載することで利用します。 例えばさくらのクラウドのサーバだと以下のように使います。

resource sakuracloud_server "example" {
  name     = "example"
  # ...中略

  provisioner vnc {
    host      = self.vnc_host
    port      = self.vnc_port
    password  = self.vnc_password
    boot_wait = "10s"

    # ここに実行したいコマンドを記載していく
    inline = [
      "<tab><wait>",
      "text ks=http://${var.ip}:${var.port}/anaconda-ks.cfg<enter>",
    ]
  }
}

remote-execと同じく、inline(文字列リストで指定)、script(スクリプトファイルのパスで指定)、またはscripts(ファイルパスのリストで指定)で実行するスクリプトを指定できます。
記載できる内容はPackerでのboot_commandと互換性があり、<enter>などの特殊キーもサポートしています。(入力処理はPackerそのものを利用してます)

指定できるスクリプトについてはPackerのQEMU Builderのboot_commandについてのドキュメントを参照してください。

www.packer.io

terraform-provisioner-vncのインストール

リリースページからバイナリファイルをダウンロードし~/.terraform.d/plugins配下に配置すればOKです。

利用例

利用例として、さくらのクラウド上のサーバでk3OSを起動〜ディスクへのインストールまでを行う例を挙げておきます。

参考: k3os on さくらのクラウド - febc技術メモ

k3OSをISOイメージから起動した場合、公開鍵を登録する or rancherユーザーのパスワードを設定するまでSSH接続できません。
このため、VNCプロビジョナーを用いてOSのインストール処理〜GitHubから公開鍵の取得/登録などを行います。
OSのインストール後はSSHプロビジョナーで接続して任意のプロビジョニングが実行できます。 この例ではkubectlコマンドを実行してみています。

variable server_name {
  default = "example"
}

variable github_username {
  default = "yamamoto-febc"
}

provider sakuracloud {
  zone = "is1b"
}

data sakuracloud_cdrom k3os {
  name_selectors = ["k3OS"]
}

resource sakuracloud_disk "disk" {
  name = var.server_name
}

resource sakuracloud_server "k3os" {
  name     = var.server_name
  core     = 2
  memory   = 4
  disks    = [sakuracloud_disk.disk.id]
  cdrom_id = data.sakuracloud_cdrom.k3os.id

  # VNC経由でOSのインストール処理
  provisioner vnc {
    host      = self.vnc_host
    port      = self.vnc_port
    password  = self.vnc_password
    boot_wait = "40s"

    inline = [
      "rancher<enter>",
      "<wait5>",

      "sudo os-config<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "y<enter>",
      "<wait5>",

      "${var.github_username}<enter>", # terraformの$記法も使えるしコメントも書ける
      "<wait10s>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait10s>",

      "y<enter>",
      "<wait5>",
    ]
  }

  # VNCでプロビジョニング後はSSHで接続してプロビジョニング
  connection {
    type        = "ssh"
    host        = self.ipaddress
    user        = "rancher"
    private_key = file("/Users/kaz/.ssh/id_rsa")
    script_path = "/home/rancher/bootstrap.sh"
  }
  provisioner "remote-exec" {
    inline = [
      "echo ${self.ipaddress} `hostname` | sudo tee -a /etc/hosts", # /etc/hostsに自身のホスト名を追記
      "kubectl cluster-info",
      "kubectl get cs",
      "kubectl version",
    ]
  }
}

終わりに

使い所は限られますが、Packerを使うまでもないちょっとしたコマンドを実行しておきたいケースに便利だと思います。

なお、TerraformでのCustom Provisionerの仕組みはこちらにコメントされているように将来的に大きく変わる可能性もある点は注意しておいてください。
(利用する側としてはあまり関係ないかもしれませんが)

github.com

以上です。

Rioにビルトインされているサービスを確認する - GrafanaとKiali

f:id:febc_yamamoto:20190603214650p:plain

前回RioでGitリポジトリを指定してのRunを試してみました。

febc-yamamoto.hatenablog.jp

rio runの引数としてGitリポジトリを指定するだけでリポジトリの変更検知〜イメージのビルド〜公開という仕組みが利用できました。

今回はそれらを実現している、Rioにビルドインされているサービスを確認していきます。

なお、今回はこちらのTerraformモジュールを使ってさくらのクラウド+RKEクラスタ上にRioをインストールしました。

github.com

Rioでどのようなサービスが動いているか確認する。

RioKubernetes上にデプロイされるため、kubectlなどでワークロードを確認(例えばkubectl get pod -n rio-systemを実行)することでどのようなサービスが動いているか調べることができます。
が、より簡単に確認できるコマンドrio --system psが提供されていますので今回はこちらを利用します。 (なお、この--systemオプションは省略形(-s)も利用可能です。またrio logsなどにも対応しています。)

ということで早速確認してみます。

rio --system psの実行

実行結果は以下の通りでした。

$ rio --system ps
NAME                          CREATED         ENDPOINT                                           REVISIONS   SCALE     WEIGHT    DETAIL
rio-system/autoscaler         3 minutes ago                                                      v0          1         100%      
rio-system/build-controller   2 minutes ago                                                      v0          1         100%      
rio-system/buildkit           3 minutes ago                                                      v0          1         100%      
rio-system/cert-manager       2 minutes ago                                                      v0          1         100%      
rio-system/grafana            2 minutes ago   https://grafana-rio-system.6y9oo6.on-rio.io:9443   v0          1         100%      
rio-system/istio-citadel      2 minutes ago                                                      v0          1         100%      
rio-system/istio-gateway      2 minutes ago                                                      v0          1         100%      
rio-system/istio-pilot        2 minutes ago                                                      v0          1         100%      
rio-system/istio-telemetry    2 minutes ago                                                      v0          1         100%      
rio-system/kiali              2 minutes ago   https://kiali-rio-system.6y9oo6.on-rio.io:9443     v0          1         100%      
rio-system/prometheus         3 minutes ago                                                      v0          1         100%      
rio-system/registry           2 minutes ago   https://registry-rio-system.6y9oo6.on-rio.io:9443  v0          1         100%      
rio-system/webhook            2 minutes ago   https://webhook-rio-system.6y9oo6.on-rio.io:9443   v0          1         100% 

ENDPOINTにURLが表示されているものがありますね。

Grafana

まずは試しにgrafanaのENDPOINTをブラウザで開いてみます。

お、grafanaのダッシュボードが表示されましたね。

f:id:febc_yamamoto:20190603212655p:plain

Istio関連のダッシュボードがありますね。

f:id:febc_yamamoto:20190603212907p:plain

いい感じです。

f:id:febc_yamamoto:20190603212931p:plain

f:id:febc_yamamoto:20190603213032p:plain

kiali

kialiはIstio向けの可視化ツールです。

www.kiali.io

これもENDPOINTをブラウザで開いてみます。

f:id:febc_yamamoto:20190603213112p:plain

これも表示できましたね。早速ログインしてみましょう。 ユーザー名/パスワードともadminでログインできます。

f:id:febc_yamamoto:20190603213146p:plain

f:id:febc_yamamoto:20190603213323p:plain

f:id:febc_yamamoto:20190603213354p:plain

いい感じですね。

その他のENDPOINT

他にはイメージレジストリとWebhookにENDPOINTが表示されてますね。 レジストリは試したらイメージのpushなどもできました。

# 適当なイメージをビルド
$ docker image build -t registry-rio-system.6y9oo6.on-rio.io:9443/test .
# pushしてみる
$ docker push registry-rio-system.6y9oo6.on-rio.io:9443/test

Webhookは試そうとしたのですが疲れちゃったのでまた今度。

終わりに

今回はここまで。以上です。

sakura-cloud-controller-managerのデプロイ&トラブルシューティング

さくらのクラウドKubernetesを利用する場合、マネージドなLBなどを扱うためのcloud-controller-manager実装として sakura-cloud-controller-managerというものがあります。

febc-yamamoto.hatenablog.jp

こちらのデプロイでよく引っかかる点やトラブルシューティング方法についてメモを発掘したので整理がてら残しておきます。

さくらのクラウド上のリソースの構成

Kubernetesクラスタで利用するさくらのクラウド上のリソースの構成は以下の点に注意が必要です。

  • ネットワーク構成
  • スイッチ(ルータ)への@k8sタグ設定

ネットワーク構成

sakura-cloud-controller-managerはサーバをスイッチ+ルータ or スイッチに接続する必要があります。
共有セグメント(共有回線)はサポートしていませんので注意してください。

スイッチ(ルータ)への@k8sタグ設定

sakura-cloud-controller-managerはさくらのクラウドAPIで取得したスイッチ(ルータ)の情報を元にどのスイッチを利用するかを決定します。
その際に@k8sタグの付けられたリソースかで判定を行なっています。

タグの付け忘れにご注意ください。

クラスタの構成

次にKubernetesクラスタの構成についてですが、以下2点に注意する必要があります。

順番にみていきます。

--cloud-providerオプションを適切に指定すること

Kubernetesでexternalなcloud-controller-managerを起動するには--cloud-providerオプションを適切に指定する必要があります。

参考: Kubernetes Cloud Controller Manager - Kubernetes

具体的には以下2点です。

  • kubeletのパラメータとして--cloud-provider=externalを指定しておくこと
  • kube-apiserverとkube-controller-managerには--cloud-providerを指定しないこと

kubeadmを利用してクラスタのデプロイを行う場合は/var/lib/kubelet/kubeadm-flags.envファイルや/etc/default/kubeletファイルなどで適切に指定しましょう。

参考: Installing kubeadm - Kubernetes

Nodeに付与されるtaintsへの対応

先ほどの参考ドキュメントに書いてありますが、kubeletに--cloud-provider=externalを指定するとノードに対し以下のようなtaintsが設定されます。

- apiVersion: v1
  kind: Node
  spec:
    taints:
    - effect: NoSchedule
      key: node.cloudprovider.kubernetes.io/uninitialized
      value: "true"

sakura-cloud-controller-managerをデプロイする際はこのtaintsに対するtolerationsが適切に設定されている必要があります。

# tolerationsの設定例
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sakura-cloud-controller-manager
  namespace: kube-system
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: sakura-cloud-controller-manager
        image: "sacloud/sakura-cloud-controller-manager:0.3.0"
      tolerations:
        # tolerationsを設定しておく
        - key: node.cloudprovider.kubernetes.io/uninitialized
          value: "true"
          effect: NoSchedule

もしkubeletに--cloud-providerを指定しなかったらどうなるの?

この場合でもsakura-cloud-controller-managerのデプロイ自体はうまくいくように見えます。
が、ノードのExternalIPが適切に設定されないため、type: LoadBalancerなサービスを作ってもロードバランサが作成されるものの実サーバが登録されないという状態になります。

serviceは作成されるけど、、、

f:id:febc_yamamoto:20190603165301p:plain

実サーバは登録されていない

f:id:febc_yamamoto:20190603165314p:plain

クラスタ内のノード名とさくらのクラウド上でのサーバ名が一致していること

これはsakura-cloud-controller-manager側の制約です。
sakura-cloud-controller-managerではノード名を条件にさくらのクラウドAPIで対象サーバの情報を取得しています。
このため、ノード名とサーバ名が異なるとうまくいきません。

kubeletの--hostname-overrideオプションなどで適切に設定しましょう。 (kubeadmの場合は--node-nameフラグが利用できます。 参考: kubeadm init - Kubernetes)

もしノード名とサーバ名が違うとどうなるの?

sakura-cloud-controller-managerでのノード情報の取得が行えず、ノードのtaintsが残り続けます。
このためPodを起動しようとしてもPendingのままとなります。
(まずkube-dnsなどの主要コンポーネントについてもPendingのままとなっているはずです。)

# Podを起動しようとしてもPendingのまま
$ kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7cdbd8cdc9-8g8mw   0/1     Pending   0          3m1s

# 主要コンポーネントもPendingのまま
$ kubectl get pod -n kube-system --selector k8s-app=kube-dns
NAME                        READY   STATUS    RESTARTS   AGE
kube-dns-58bd5b8dd7-swrlg   0/3     Pending   0          7m39s

さらにこの状態だとノードのInternalIP/ExternalIPが設定されず、kubectl logsを実行するとError from server: no preferred addresses found; known addresses: []などというエラーになります。

sakura-cloud-controller-managerのデプロイ

DeploymentまたはDaemonSetとしてデプロイしてください。
(現在Helmでデプロイする場合はDaemonSetのみサポートしています)

Deploymentとする場合のマニフェスト例は以下の通りです。 (この例はRKEを使ってデプロイしたKubernetesクラスタを利用しています。tolerationsやnodeSelectorsは環境に応じて適切に指定してください)

apiVersion: v1
kind: Secret
metadata:
  name: sakuracloud-api-keys
  namespace: kube-system
type: Opaque
data:
  access-token: "<APIアクセストークン>"
  access-token-secret: "<APIアクセスシークレット>"
---
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sakura-cloud-controller-manager
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  name: system:sakura-cloud-controller-manager
rules:
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
      - update
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - '*'
  - apiGroups:
      - ""
    resources:
      - nodes/status
    verbs:
      - patch
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - services/status
    verbs:
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - serviceaccounts
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - persistentvolumes
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - create
      - get
      - list
      - watch
      - update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: system:sakura-cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:sakura-cloud-controller-manager
subjects:
  - kind: ServiceAccount
    name: sakura-cloud-controller-manager
    namespace: kube-system
---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: sakura-cloud-controller-manager
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      dnsPolicy: Default
      hostNetwork: true
      serviceAccountName: sakura-cloud-controller-manager
      containers:
      - name: sakura-cloud-controller-manager
        image: "sacloud/sakura-cloud-controller-manager:0.3.0"
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 256m
            memory: 256Mi
        command:
          - /usr/local/bin/sakura-cloud-controller-manager
          - --cloud-provider=sakuracloud
          - --allocate-node-cidrs=false
          - --configure-cloud-routes=false
        env:
          - name: SAKURACLOUD_ACCESS_TOKEN
            valueFrom:
              secretKeyRef:
                name: sakuracloud-api-keys
                key: access-token
          - name: SAKURACLOUD_ACCESS_TOKEN_SECRET
            valueFrom:
              secretKeyRef:
                name: sakuracloud-api-keys
                key: access-token-secret
          - name: SAKURACLOUD_ZONE
            value: "<対象ゾーン(is1a or is1b or tk1a)>"
          - name: SAKURACLOUD_CLUSTER_ID
            value: "default"
      tolerations:
        - key: node.cloudprovider.kubernetes.io/uninitialized
          value: "true"
          effect: NoSchedule

        - key: "CriticalAddonsOnly"
          operator: "Exists"
      nodeSelector:
        node-role.kubernetes.io/controlplane: "true"

実行時によくあるトラブル

よくあるトラブルとして、Serviceを作成してもいつまでもPendingのままというものがあります。

この場合、sakura-cloud-controller-managerのログを参照すると何かヒントが得られることがあります。

# ログの参照例
$ kubectl logs -f -n kube-system sakura-cloud-controller-managerのpod名

私も引っかかったことがあるのは次のようなエラーメッセージが出るケースです。

Improper request. The parameters of the specified error or input rule violation. Please check your entries.\n実サーバのIPアドレス(xxx.xxx.xxx.xxx)は同一ネットワークである必要があります

このケースは@k8sタグのついたスイッチ+ルータが複数存在している場合に発生します。
複数のスイッチ+ルータを使い分けたい場合はそれぞれのスイッチに対し識別用のタグを付与した上でk8s.usacloud.jp/router-selectorアノテーションをServiceに対して指定するようにすればOKです。

おまけ: RKEでデプロイするTerraformモジュール

こちらにTerraformのさくらのクラウドプロバイダー+RKEプロバイダーでクラスタ構築やsakura-cloud-controller-managerのデプロイまで行うモジュールを置いておきます。

github.com

以上です。

k3OSのISOイメージがさくらのクラウドに追加された

f:id:febc_yamamoto:20190529153900p:plain

さくらのクラウドパブリックISOイメージk3OSが追加されましたね!

cloud-news.sakura.ad.jp

前回の記事ではk3OSのISOイメージをダウンロード&アップロードして試しました。

febc-yamamoto.hatenablog.jp

これが本日パブリックISOイメージが追加されたことでISOイメージのダウンロード/アップロードが不要となりより簡単にk3OSを試せるようになりました。

パブリックISOイメージから起動してみる(ディスクレス)

前回はCLIを利用した方法を紹介しましたので、今回はコントロールパネルからの操作を紹介します。
前回はディスクにインストールする手順でしたが、今回はディスクレスでISOイメージから起動してそのまま使う方法にしてみました。 (ディスクを作成しない分お安くなっています)

コントロールパネルからサーバの追加

まずコントロールパネルからサーバ追加画面へ移動します。

サーバ追加画面ではシンプルモードのチェックを外すのがポイントです。
(サーバ作成時にISOイメージを挿入するには非シンプルモードである必要があります)

その後、ディスクレスを選択した上で挿入するISOイメージとしてk3OS v0.2.0 amd64を選択します。

f:id:febc_yamamoto:20190529115651p:plain

もしディスクを接続したい場合は新規ディスクを作成を選択し、ディスクソースにブランクを指定すればISOイメージ選択欄が出てきます。

次のNICの設定はデフォルトのインターネットに接続を選択しておきます。

f:id:febc_yamamoto:20190529120303p:plain

これは共有セグメント(共有回線)経由でインターネット接続を行うという指定です。 共有セグメントではDHCPによるグローバルIPの割り当てが利用可能となっています。

manual.sakura.ad.jp

なお、k3OSのドキュメントによると、サーバのコア数/メモリはデフォルトの1コア/1GBで大丈夫なようです。

Live install (boot from ISO) requires at least 1GB of RAM.

GitHub - rancher/k3os: Purpose built OS for Kubernetes, fully managed by Kubernetes.

しかし1GBメモリだとイメージのpullなどでエラーになる場合があったため手元の環境ではメモリ2GBで試しました。 (他の要因もあるかもしれませんが調べてません)

参考: CLIでサーバ作成する場合

CLIの場合は以下のコマンドでOKです。

$ usacloud server build --name k3os --disk-mode diskless --iso-image-id `usacloud iso-image read -q k3OS` --memory 2

起動してコンソール接続

サーバを作成&起動後、コントロールパネルからコンソールを開きます。

f:id:febc_yamamoto:20190529120551p:plain

後は前回と同じくユーザー名rancher(パスワードなし)でログイン可能です。

ログイン後の設定

SSH

このまま試しても良いのですが、コンソール接続だとコピペができない(ペーストはコンパネ経由でできる)など色々不便なのでSSH接続できるようにします。

今回はGitHubからユーザーの公開鍵を取得して利用します。
以下のコマンドをコンソールで実行します。ユーザー名部分は任意のものに変更してください。

# GitHubのユーザー名が"yamamoto-febc"の場合
$ curl -L -o /home/rancher/.ssh/authorized_keys https://github.com/yamamoto-febc.keys
$ chmod 0600 /home/rancher/.ssh/authorized_keys

なお、コンソール接続している時点ではキーボードがUS配列になっていると思います。 普段US配列以外をお使いの場合は記号の入力の際にご注意ください。

この後SSHで接続できるようになっているはずです。

$ ssh rancher@サーバのグローバルIPアドレス

なお公開鍵を用意するのが面倒な方は以下のコマンドでrancherユーザーにパスワードを設定する方法もあります。

$ sudo passwd rancher

/etc/hosts

/etc/hostsを編集し自身のホスト名を解決できるようにします。 ホスト名はhostnameコマンドなどで、IPアドレスip a show dev eth0コマンドなどで確認しておきます。

今回は/etc/hostsを以下のようにしました。

127.0.0.1       localhost localhost.localdomain
127.0.1.1       k3os-14500 k3os-14500.localdomain

::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# これを追記(IPアドレス/ホスト名は適宜修正)
153.120.168.44 sv-113100883194

これでひとまず利用できる環境が整いました。 後は色々動かしてみましょう。

終わりに

ISOイメージを用意してくれてるのは楽ですね。 とりあえず雰囲気を確認したいというような場合に手軽に使えて良いと思います。

以上です。