CloudFormationでCoreOS + Kubernetesクラスタをつくるkube-awsのまとめ

kube-awsという、CoreOS社が公式にKubernetesクラスタをAWS上に構築する方法として開発・公開しているツールがあります。

これがなかなか良くできてるので、Kubernetes on AWSをこれから始める人がkube-awsにたどり着くことがあるだろうな、という想像のもと、自分含めそんな人が(試行錯誤する前に)最初に知っておくとよいことをつらつらと書いていきます。

// …ということを2016年4月頃に書いたのがこの記事の始まりなのですが、いまは筆者がkube-awsのメンテナになってしまったので、ちょっとバイアスがかかってる感は否めないです :smiley: 

おおまかな利用手順から、kube-awsのコードを書き換えてコントリビュートするところまでをカバーします。

## kube-awsとは

CoreOSのドキュメントにもある通り、CoreOS社でCoreOS+Kubernetesクラスタをデプロイするために使っているツールです。後述しますが、2015/10時点の情報だと、Tectonicという商用サービスでサポートも受けられるようです。

> At CoreOS, we use the kube-aws CLI tool to automate cluster deployment to AWS.
> 
> 引用元: https://coreos.com/kubernetes/docs/latest/kubernetes-on-aws.html

kube-awsはCoreOSさんがGitHub上で公開している[coreos-kubernetes](https://github.com/coreos/coreos-kubernetes)の一部でしたが、2016年10月頃は独立したGitHubレポジトリに昇格して、同時にコミュニティでメンテされることになり、色々あって筆者(@mumoshu)がメンテナをしています。

https://github.com/coreos/kube-aws.git

### 保守されてるの?

2015年のre:Invent直前に公開されたCoreOSのブログ記事によると、

参照: [Official CloudFormation and kube-aws tool for installing Kubernetes on AWS](https://coreos.com/blog/kubernetes-on-aws-cloudformation/)

* 定期的にConformance Test(文脈的から普通に考えると、KubernetesのConformance Testのことかな)される(ので、私見ですが、あまりテストされてないOSSにありがちな「普通に動かない」問題は少ないかも)
* CoreOS社のCoreOS+Kubernetesの商用サービスであるTectonicでサポートを受けられる

ということで、同じくDocker関連企業が公開している[kubernetes-anywhere](https://github.com/weaveworks/kubernetes-anywhere)と比較すると、保守されているほうではないかなと思います。

余談ですが、Docker用の仮想ネットワークソリューションWeaveを提供されているWeaveworksさんが2015年末にKubernetesブログに投稿された「[How Weave built a multi-deployment solution for Scope using Kubernetes](http://blog.kubernetes.io/2015/12/how-Weave-built-a-multi-deployment-solution-for-Scope-using-Kubernetes.html)」で述べられている知見が、weaveworks/kubernetes-anywhereにも反映されたらkube-awsに対抗できそうだなーと思ってワクテカしております。

> After a couple weeks of fighting with the machines, we were able to resolve all of our integration issues, and have rolled out a reasonably robust Kubernetes-based system to production.
>
> * Provisioning Kubernetes is difficult, owing to a complex architecture and young provisioning story. This shows all signs of improving. 
> * Kubernetes’ non-optional security model takes time to get right. 
The Kubernetes domain language is a great match to the problem domain. 
> * We have a lot more confidence in operating our application (It's a lot faster, too.). 
>
> 引用元: http://blog.kubernetes.io/2015/12/how-Weave-built-a-multi-deployment-solution-for-Scope-using-Kubernetes.html

2016年10月頃からはぼくがメンテをしていますが、基本的に問題のないプルリクエストは平日なら即マージ、何かあれば１〜２日でコメントがつく、11月には「ノードプール」というGKEにもあるような機能が追加されたり…という状態です。かなりアクティブなほうではないかと思います。

### kube-awsの仕組み

kube-awsは、以下の4ステップでCloudFormationスタックとしてCoreOS+Kubernetesクラスタを構築します。

* init: YAML形式の設定ファイル生成
* render: その設定ファイル由来の変数を埋んで使うJSONテンプレートファイル（CloudFormationテンプレートのテンプレート、と考えてもらえるとよいと思います）、鍵、EC2インスタンス初期化用userdataなどの生成
* validate: 設定がcloud-config的に、またCloudFormation的に正しいか、という検証
* up: CloudFormationスタック作成

第１ステップで生成された設定ファイルを変更すれば基本的なカスタマイズができます。
また、第３ステップでCloudFormationテンプレートをつくれるので、テンプレートをカスタマイズすればCloudFormationで表現できる範囲で自分好みのクラスタをつくることができます。

2016年4月頃はまだHA構成に対応していなかったりして、そのまま本番運用するのは怖い、でもカスタマイズすることで実際本番運用している人もいた…という状態でしたが、2016年10月頃にリリースされたv0.9.0からは、それまで既にH/A対応だったworkerノードに加えて、etcd、controllerノードもH/A構成がサポートされました。

### kube-aws alternatives

[Kubernetesのデプロイツールまとめ - Qiita](http://qiita.com/mumoshu/items/72ae0f07a58a37626204)

## kube-awsのインストール

観測範囲内だと、二通りの選択肢があります。

* 公式には、[CoreOSのドキュメントにかかれているように](https://coreos.com/kubernetes/docs/latest/kubernetes-on-aws.html)GitHub上の[coreos-kubernetes Releasesページ](https://github.com/coreos/kube-aws/releases)からバイナリをダウンロード、`chmod +x`して`PATH`が通るところに置く
* 非公式には、[danielwhatmuff/kube-aws-dockerイメージでdocker runする](https://github.com/danielwhatmuff/kube-aws-docker)
  * 最近更新されているかは不明

## おすすめの設定

### cluster.yaml

kube-awsはクラスタのDNS名や利用するAZ、リージョン等の設定を`cluster.yaml`という設定ファイルでカスタマイズできるようになっています。
基本的には各自の要件に応じて設定してほしいのですが、これだけはやっておいたほうがいい（やっておかないとハマる）という点だけここで抑えておきましょう。

#### releaseChannel: stable

CoreOSにはリリースチャンネルという、安定度の異なるリリースが３種類あります。

※kube-awsにかぎらず、CoreOSクラスタを自動的に構築するツールでは大抵このリリースチャンネル(release channel)を設定する機能があるので、覚えておくとよいです。

- プロダクション用のstable
- CoreOS利用者がテスト環境などで常時テストしておくべきbeta
- CoreOS自体の開発をする人やもっとアグレッシブな利用者が使うalpha

kube-awsの場合、CoreOSのリリースチャンネルは、`cluster.yaml`の`releaseChannel`という設定で変えられます。

2016/04/26に確認した時点ではstableがまだKubernetesに公式対応していなかったため、releaseChannelとして選択できませんでした。今はstableが使えるようになっているので、プロダクションの場合は基本的にstableを選択したほうがいいです。

```cluster.yaml
# CoreOS release channel to use. Currently supported options: [ alpha, beta ]
# See coreos.com/releases for more information
releaseChannel: stable
```

#### controllerCount

2016/04/26の時点ではControllerノードは1台限定、つまりH/Aではない構成でした。現在では1台以上の任意のノードを作成することができます。具体的には、cluster.yamlでこのcontrollerCountに指定した台数のControllerノードがASGで作成されます。H/Aにしたい場合は、最低限2にしましょう。

## クラスタをつくる

事前にAWS KMSでキーを作成してある前提で、
ap-northeast-1リージョンのap-northeast-1cアベイラビリティゾーン内にクラスタ名mykubeでクラスタを作成する場合は以下のようなコマンドになります。

```
CLUSTER_NAME=mykube

kube-aws init --region ap-northeast-1 \
  --availability-zone ap-northeast-1c \
  --key-name $YOUR_PRIVATE_KEY --kms-key-arn arn:aws:kms:ap-northeast-1:$YOUR_KMS_KEY \
  --cluster-name $CLUSTER_NAME \
  --external-dns-name $CLUSTER_NAME.example.com && \
  bin/kube-aws render && \\
  bin/kube-aws up
```

最後の`kube-aws up`にexportオプションをつけて`kube-aws up --export`とすると、実際にはCloudFormationスタックを作成せず、テンプレート(JSONファイル)を出力するだけになります。
出力されたテンプレートはユーザ側で自由に改変して、それをawscli等に食わせることでkube-awsを使わずにクラスタを構築することができます。既にawscliやCloudFormationを使ったワークフローを構築している場合や、もしくはkube-awsが提供していない機能を追加したい、という場合に便利です。

## クラスタをテストする

ssh接続してみて、Unitが失敗してなければOK。

```
$ ssh -i $YOUR_KEY core@$CONTROLLER_IP
CoreOS beta (991.2.0)
Update Strategy: No Reboots
core@ip-10-0-0-50 ~ $
```

何か問題があるときは、以下のように失敗したUnitがFailed Unitsとして出力されるはずです(↑の例の数日後にbetaでクラスタを作りなおしてみたところ、エラーがでた状態です。CoreOSのバージョンが991.2.0と1010.1.0で異なるところにご注目ください)。

```
CoreOS beta (1010.1.0)
Update Strategy: No Reboots
Failed Units: 1
  locksmithd.service
```

CoreOSはsystemdでサービスの管理をしている。`/etc/systemd/system`以下にサービスの定義がある。

```
core@ip-10-0-0-50 ~ $ cd /etc/systemd/system
core@ip-10-0-0-50 /etc/systemd/system $ ls
decrypt-tls-assets.service  flanneld.service.d           kubelet.service           multi-user.target.wants   user-configvirtfs.service
docker.service.d            install-kube-system.service  kubelet.service.requires  user-configdrive.service
```

そういえば、CoreOSの分散systemd的なものであるfleetに関するサービスが定義されていない。coreos-kubernetesはfleetを使わない構成のようですね。

[Deploying fleet](https://github.com/coreos/fleet/blob/master/Documentation/deployment-and-configuration.md)

ちょっと構成は違うが、coreos-kubernetesを利用せずに[自分で３ノードCore OSクラスタにKubernetesをインストールする記事](https://www.airpair.com/docker/posts/installing-and-configuring-kubernetes-on-coreos)に必要なsystemdサービスの例が書いてあるので、比較してみるとよいです。

### LISTENしてるポート

正常に起動したkube-awsクラスタのマスタは、以下の様なポートをLISTENしているはずです。

```
core@ip-10-0-0-50 ~ $ ss -lnt
State       Recv-Q Send-Q                                                        Local Address:Port                                                                       Peer Address:Port
LISTEN      0      128                                                               127.0.0.1:10248                                                                                 *:*
LISTEN      0      128                                                               127.0.0.1:10249                                                                                 *:*
LISTEN      0      128                                                               127.0.0.1:8080                                                                                  *:*
LISTEN      0      128                                                                      :::4194                                                                                 :::*
LISTEN      0      128                                                                      :::10250                                                                                :::*
LISTEN      0      128                                                                      :::10251                                                                                :::*
LISTEN      0      128                                                                      :::2379                                                                                 :::*
LISTEN      0      128                                                                      :::10252                                                                                :::*
LISTEN      0      128                                                                      :::2380                                                                                 :::*
LISTEN      0      128                                                                      :::10255                                                                                :::*
LISTEN      0      128                                                                      :::22                                                                                   :::*
LISTEN      0      128                                                                      :::443                                                                                  :::*
```

### etcdの状態

controllerにSSH接続して、journalctlでetcdのログを見てみましょう。etcdが正常に起動しているなら、以下の様な出力が得られるはずです。

```
$ sudo journalctl _PID=1 -e -u etcd2
-- Logs begin at Fri 2016-04-29 03:37:50 UTC, end at Fri 2016-04-29 06:42:09 UTC. --
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Starting etcd2...
Apr 29 03:38:27 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Started etcd2.
```

### ローカルマシンのkubectlからkube-apiserverに接続できるようにする

そのマスタがKubernetesのWeb　APIを提供してくれる。

coreos-kubernetesが生成したkubeconfigというkubectl用の設定ファイルは、
External DNS NameがマスタのパブリックIPアドレスに解決できる前提で書かれているので、
取り急ぎそうするためにローカル環境の/etc/hostsを変更する。

```
$ sudo emacs /etc/hosts
Password:
# $CONTROLLER_IP <External DNS Name>のようなエントリを追加する
```

### クラスタ内のノード一覧

`kubectl get nodes`で、クラスタ化されたノードの一覧が想定通りかみてみる。
ノードが想定より少なければ、クラスタがうまく組めていない可能性があるということがわかるので、すごく単純なテストだけど大事。
今回はマスタ１台、ワーカー１台で計２台のノードが返ってくることが想定される。
実際、コマンドを実行してみる。

```
$ kubectl --kubeconfig=kubeconfig get nodes
NAME                                           STATUS                     AGE
ip-10-0-0-50.ap-northeast-1.compute.internal   Ready,SchedulingDisabled   49s
ip-10-0-1-51.ap-northeast-1.compute.internal   Ready                      53s
```

masterがReady,SchedulingDisabled、workerがReady(こちらはSchedulingDisabledでないので、Scheduleされる）となっていればOK。これが、masterしかいなかったりすると、podを作成・実行できないのでクラスタをつくりなおすべし。

### 使い捨てのDockerコンテナ上で任意のコマンドを実行してみる

```
# kubectl run --tty -i shなどと実行すると、docker runのようにk8sクラスタ内でDockerコンテナ一つを含む
$ kubectl run mytest2 --kubeconfig=kubeconfig --tty -i --image=alpine:edge sh
/
/ # Session ended, resume using 'kubectl attach mytest2-1822908544-upx83 -c mytest2 -i -t' command when the pod is running
# 上記メッセージの通り、C-dでデタッチしたあともPodが動き続ける模様。
# kubectl attachを以下のように実行すると、Podに再接続できる。
$ kubectl --kubeconfig=kubeconfig attach mytest2-1822908544-upx83 -c mytest2 -i -t

Hit enter for command prompt

/ #
```

### kubernetes/examplesを試してみる

基本的にKubernetesのConformance Testに通る普通のKubernetes環境[^1]なので、
kube-awsだからどう、ということはなくて、例えば以下にあるKubernetes公式のサンプルを色々試してみるとよいと思います。

https://github.com/kubernetes/kubernetes/tree/v1.2.3/examples

[^1]: という認識が甘かったら誰がこっそり教えて下さい！Conformance Test通っても全然安心できないよ、とかあれば・・

## kube-awsをカスタマイズする

* パラメータを変えたいだけなら、`cluster.yaml` を変更します
* CloudFormationテンプレートを今だけカスタマイズしたいときは、`kube-aws up --export`して出力された`<スタック名>-template.json`ファイルを変更します
* 上記で対応できない内容の場合は、kube-aws自体のコードを変更・ビルドします

３つめのkube-aws自体のコード変更については方法がわかりづらいかもしれないので、以下で説明します。

### kube-awsのコードを変更する

`pkg`以下にあるgoソースを変更して、kube-awsをビルドしなおせばOKです。

goが初めての方でも、[templateパッケージのリファレンス](https://golang.org/pkg/text/template/)を読みながらならできるかも。

#### ビルド方法

ビルドしたいときは、`cd multi-node/aws/ && ./build`を実行します。すると、`bin/kube-aws`にバイナリが生成されます。

kube-awsは、

- `kube-aws init` → cluster.yaml生成
- `kube-aws render` →　 `userdata/`、`credentials/`、`stack-template.json`(goの`text/template`形式のCloudFormationテンプレートのテンプレート)の生成
- 必要に応じて `kube-aws up --export` → CloudFormationテンプレートの生成（作成はしない)
- `kube-aws up` →　CloudFormationスタックの作成（事前に`--export`する必要はない)

という流れで利用します。変更箇所によって、手動テストまでの流れが異なります。筆者は場合に応じて以下の様なワンライナーで動作確認しています。

#### cluster.yamlの生成からやり直したいとき

例えば、`config.go`を書き換えるなどして、`cluster.yaml`の形式自体をカスタマイズした場合です。

```
./build && rm -rf cluster.yaml userdata/ credentials/ && bin/kube-aws init --region ap-northeast-1 --availability-zone ap-northeast-1c --key-name $YOUR_PRIVATE_KEY --kms-key-arn arn:aws:kms:ap-northeast-1:$YOUR_KMS_KEY --cluster-name $CLUSTER_NAME --external-dns-name $CLUSTER_NAME.example.com && bin/kube-aws render && bin/kube-aws up --export
```

#### stack-template.jsonの生成からやり直したいとき

`cluster.yaml` の構造には手を加えていない場合は、以下のようにそれ以降の手順を再実行すればOKです。

```shell-session
$ ./build && rm -rf userdata/ credentials/ && bin/kube-aws render && bin/kube-aws up --export
```

#### 単体テストを手元で実行したいとき

```
make test
```

### kube-awsにコントリビュートする

#### go fmtでコードフォーマッティング

コードスタイルはgofmtでチェックされています。
規約にそっていないスタイルだと[ぷるりを出してもビルドエラーになってしまう](https://travis-ci.org/coreos/coreos-kubernetes/jobs/126006199)ので、
ぷるりを出す前に以下のように自動整形してしまいましょう。

まず、`gofmt -d`で規約に沿っていない部分のBEFORE・AFTERを出力します。

```
$ cd multi-node/aws
$ test -z "$(find . -path ./vendor -prune -type f -o -name '*.go' -exec gofmt -d {} + | tee /dev/stderr)"
```

BEFORE・AFTERがわかったら、`gofmt -w`で規約に沿う形に自動的に整形します(実際にソースファイルを上書きします)。

```
$ test -z "$(find . -path ./vendor -prune -type f -o -name '*.go' -exec gofmt -w {} + | tee /dev/stderr)"
```

…ということをいちいちやるのが面倒なので、今ではmakeコマンド一発でできるようになりました。

```
make format
```

#### コントリビューションガイドラインを読む

[How to Contributeのコミットメッセージのフォーマットについては、最低限読んでおいたほうがよさそうです。](https://github.com/coreos/kube-aws/blob/master/CONTRIBUTING.md#format-of-the-commit-message)

## 動かないとき

経験上、「定められた手順どおりにやっても動かない」というときは、CoreOSのリリースチャンネルがalphaになってることが原因なことが多いです。が、いずれにせよ何が原因かわからないと、対処のしようもないので、原因の切り分けに役立ちそうな手順を書いておきます。

### SSHログインしたらFailed Unitsの表示

#### 例1

```
$ ssh -i $PATH_TO_YOUR_KEY core@$NODE_IP
CoreOS alpha (1029.0.0)
Update Strategy: No Reboots
Failed Units: 6
  docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope
  docker-7a7961c868d5c747f35270d6c1dc8630ee726288bcee0040c799af49fe157b67.scope
  docker-c80252f5a00cd1506474411f0937d2464bb0897c7534fe6f2df8da5bdb33015a.scope
  docker-d4ae5c3fb3898d4316602354761c49029843386138983e2181b4fe8856920554.scope
  docker-d5df7fb5fcb6dd2e3442b99d9c135f0d298276314f56710ce280625da8711510.scope
  docker-dd2624fc1c51c17d8dc7c6afc0b286224197bab268ffc1114c6f01e3912cac19.scope
```

systemctl statusで状態を確認してみます。

```
core@ip-10-0-0-50 ~ $ systemctl status docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope
● docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope - docker container 57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545
   Loaded: loaded
Transient: yes
  Drop-In: /run/systemd/system/docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope.d
           └─50-BlockIOAccounting.conf, 50-CPUAccounting.conf, 50-CPUShares.conf, 50-DefaultDependencies.conf, 50-Description.conf, 50-MemoryAccounting.conf, 50-Slice.conf
   Active: failed (Result: resources)

Apr 27 02:25:18 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope: Failed to add PIDs to scope's control group: No suc
Apr 27 02:25:18 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Failed to start docker container 57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.
Apr 27 02:25:18 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: docker-57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.scope: Unit entered failed state.
Apr 27 02:25:18 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Stopped docker container 57a415add508bd1c8181811a1007a6284e7534e0b5f4403fad0b6c7ac92ce545.
```

上記のように起動に失敗してるサービスがある場合は、経験上はCoreOSのalphaリリースの問題です。

#### 例2: locksmithdがコケてる

locksmithdだけのログを見てみる。

```
CoreOS beta (1010.1.0)
Update Strategy: No Reboots
Failed Units: 1
  locksmithd.service

$ sudo journalctl _PID=1 -e -u locksmithd
```

```
-- Logs begin at Fri 2016-04-29 03:37:50 UTC, end at Fri 2016-04-29 06:52:48 UTC. --
Apr 29 03:38:18 localhost systemd[1]: Started Cluster reboot manager.
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Main process exited, code=exited, status=1/FAILURE
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Unit entered failed state.
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Failed with result 'exit-code'.
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Service hold-off time over, scheduling restart.
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Failed to schedule restart job: Unit locksmithd.service is masked.
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Unit entered failed state.
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Failed with result 'resources'.
Apr 29 03:39:13 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Stopped locksmithd.service.
```

前後関係がわからないので、システム全体のログをlocksmithdに関するメッセージの前後だけ切り出して見てみる。

```
$ journalctl -b | grep -C5 locksmithd
```

```
core@ip-10-0-0-50 ~ $ journalctl -b | grep -C5 locksmithd
Apr 29 03:38:21 localhost dbus[693]: [system] Activating via systemd: service name='org.freedesktop.PolicyKit1' unit='polkit.service'
Apr 29 03:38:21 localhost systemd[1]: Starting Authorization Manager...
Apr 29 03:38:21 localhost systemd-resolved[726]: Defaulting to hostname 'linux'.
Apr 29 03:38:21 localhost systemd[1]: Started Network Name Resolution.
Apr 29 03:38:21 localhost update_engine[708]: [0429/033821:INFO:main.cc(155)] CoreOS Update Engine starting
Apr 29 03:38:21 localhost locksmithd[687]: Cannot get update engine status: The name com.coreos.update1 was not provided by any .service files
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Main process exited, code=exited, status=1/FAILURE
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Unit entered failed state.
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Failed with result 'exit-code'.
Apr 29 03:38:22 localhost systemd-timesyncd[641]: Network configuration changed, trying to establish connection.
Apr 29 03:38:22 localhost systemd-timesyncd[641]: Synchronized to time server 106.185.48.114:123 (0.coreos.pool.ntp.org).
Apr 29 03:38:22 localhost systemd[1]: Started Update Engine.
Apr 29 03:38:22 localhost update_engine[708]: [0429/033822:INFO:update_check_scheduler.cc(82)] Next update check in 8m26s
Apr 29 03:38:22 localhost polkitd[745]: Started polkitd version 0.113
--
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Ensuring runtime unit file "oem-cloudinit.service" is unmasked
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 /run/systemd/system/oem-cloudinit.service is not null or empty, refusing to unmask
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Ensuring runtime unit file "etcd.service" is unmasked
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Ensuring runtime unit file "etcd2.service" is unmasked
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Ensuring runtime unit file "fleet.service" is unmasked
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Ensuring runtime unit file "locksmithd.service" is unmasked
Apr 29 03:38:22 localhost systemd[1]: Reloading.
Apr 29 03:38:22 localhost systemd[1]: Reached target Multi-User System.
Apr 29 03:38:22 localhost coreos-cloudinit[701]: 2016/04/29 03:38:22 Calling unit command "restart" on "oem-cloudinit.service"'
Apr 29 03:38:22 localhost systemd[1]: Stopped Cloudinit from EC2-style metadata.
Apr 29 03:38:22 localhost systemd[1]: Starting Cloudinit from EC2-style metadata...
```

CoreOSクラスタが一度にリブートしすぎて壊れないように、少数ずつアップデート・リブートするための分散ロックを実現する[locksmithd](https://github.com/coreos/locksmith)が、restartに失敗しているみたい。上記エラーはこのサーバのリブートじゃなくて初回起動時に出てるが、なんでstartじゃなくてrestartになってしまっているのか・・・と思ったら、システム全体のログを見る限り、

```
Apr 29 03:38:21 localhost update_engine[708]: [0429/033821:INFO:main.cc(155)] CoreOS Update Engine starting
Apr 29 03:38:21 localhost locksmithd[687]: Cannot get update engine status: The name com.coreos.update1 was not provided by any .service files
Apr 29 03:38:21 localhost systemd[1]: locksmithd.service: Main process exited, code=exited, status=1/FAILURE
```

update_engineのステータスを取れないがためにlocksmithdがstartに失敗して、

```
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Service hold-off time over, scheduling restart.
Apr 29 03:38:23 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: locksmithd.service: Failed to schedule restart job: Unit locksmithd.service is masked.
```

2秒後にlocksmithdをrestartしようとしたけど、locksmithdがmaskされているで再起動に失敗した、ということのようです。

なんでmaskされてるのかなーと思ったら、自動アップデート・リブートを無効化してあるからのようですね。

```
core@ip-10-0-0-50 ~ $ curl -s http://169.254.169.254/2009-04-04/user-data | gunzip - | grep reboot
    reboot-strategy: "off"
```

[locksmithd failed with result exit-code · Issue #1011 · coreos/bugs](https://github.com/coreos/bugs/issues/1011)

わかりづらいですが、問題はないようです（そもそも自動アップデートを有効化したかった場合は、reboot-strategyを変更する必要はありますが)。

ちょっと脱線しますが、reboot-strategy周りは混乱してる人が多いのかな…
[locksmith with reboot-strategy off doesn't remove lock · Issue #1219 · coreos/bugs](https://github.com/coreos/bugs/issues/1219)

### k8sのDebugging FAQを読む

https://github.com/kubernetes/kubernetes/wiki/Debugging-FAQ