えいのうにっき

むかしのじぶんのために書いています

CloudGarage の無償インスタンスを利用して kubernetes クラスタを構築してみる

定額型パブリッククラウド という触れ込みで先日ローンチされたCloudGarage

cloudgarage.jp

そのフレーズのとおり、「1Core/ 1GB/ 50GB なクラウド VM インスタンスが3インスタンスで 1,480円/月 固定(転送量など込み)」という月額定額制のサービスメニューが用意されていて画期的。すでに趣味用途で AWSGCP などにインスタンスをいくつか立ててる僕にはとても興味深いサービスなわけなんだけど、さらに魅力的なポイントとして、CloudGarage では「Dev Assist Program」なる、開発者向けインスタンス無償提供制度がある。

cloudgarage.jp

この申請に通ると、上述の「1Core/ 1GB/ 50GB なクラウド VM インスタンス x3」が1年間無料で利用できる。そして先日、めでたく僕もこの制度の適用申請が通った。ありがとうございますありがとうございます。

降ってわいたような3つのサーバーインスタンス、さてこいつで何をやろうかなということでまずは、「せっかくある3台のサーバーを活かせるようなことを」ということで、CloudGarage 上に kubernetes(k8s)クラスタを構築してみることにした。このエントリはその作業メモである。以下目次。

今回せっかくなので、ほぼ全ての作業を itamae のレシピに落としこみながら進めた。なので、このレシピを使えば誰でも簡単に k8s クラスタを CloudGarage 上に構築できるはず。間違ってるところがあったら Pull Request ください。笑

github.com

1. まずはインスタンスの作成

まずは CloudGarage のWebコンソール画面から、3つのインスタンスを作る。

f:id:a-know:20171001121342p:plain

  • インスタンスタイプ
    • 1GBタイプ
  • OS/イメージ選択
    • OSから選択
    • CentOS 7.3 64bit
  • 接続許可ポート
    • 任意のポートを指定することはできないので、以下を選択する。
    • 22
    • 80
    • 443
  • SSH Key 設定
    • ssh ログインできるよう、設定しておく。
  • インスタンス情報

作っている過程で「インスタンスの作成に失敗しました」みたいなエラーになった・だが一覧を見ると出来ている、ということもあったので、その点は注意。

2. ログイン・ユーザーの準備

各サーバに master01 node01 などの名前で ssh できるように、手元の開発機の ~/.ssh/config を編集しておく。

$ ssh root@master01
Last login: Mon Jun 26 13:22:52 2017
[root@master01 ~]# 

ok。

とはいえ、この先の作業をずっと root でやっていくのもあれなので、作業用のユーザーを作成して sudo できるようにしておく。

以降のサーバー上の作業については、僕の書いた itamae レシピを使ってくれてもいい。GitHub - a-know/itamae-recipes-k8s を clone し bundle install したあと、以下のコマンドを実行することで、ユーザー作成ができるようになってる(はず)ので、おすすめ。

$ bundle exec itamae ssh -h master01 -j base/a-know.json -u root base/user.rb
$ bundle exec itamae ssh -h node01  -j base/a-know.json -u root base/user.rb
$ bundle exec itamae ssh -h node02  -j base/a-know.json -u root base/user.rb

base/a-know.json は僕の場合のものなので、同レポジトリ内にある base/user_template.json をコピー・書き換えた上でそれを指定して実行してください。これは以降の手順でもおなじなので、以降は割愛。

$ ssh master01
Last failed login: Sat Sep 30 08:24:26 JST 2017 from xxx on ssh:notty
There was 1 failed login attempt since the last successful login.
[a-know@master01 ~]$ sudo su -
最終ログイン: 2017/09/30 (土) 08:45:27 JST xxxから開始日時 pts/0
[root@master01 ~]#

ok。

3. /etc/hosts の編集

作成した各インスタンス間で、相互にホスト名でアクセスできるように /etc/hosts に追記もする。

$ bundle exec itamae ssh -h master01  -j base/hosts.json -u root base/hosts.rb
$ bundle exec itamae ssh -h node01  -j base/hosts.json -u root base/hosts.rb
$ bundle exec itamae ssh -h node02  -j base/hosts.json -u root base/hosts.rb

確認する。

$ cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.5 master01
192.168.0.6 node01
192.168.0.7 node02

ok。

以上で3台共通のセットアップはおわり。以降、マスターノード/コンテナ稼働ノードごとの作業に移っていく。

4. マスターノードのセットアップ

ここでやるべきことは以下。

  1. etcd、kubernetes、flannelのインストール
  2. etcd の設定
    • /etc/etcd/etcd.conf の編集
      • ETCD_LISTEN_CLIENT_URLS 項目の変更
      • ETCD_ADVERTISE_CLIENT_URLS の変更
  3. flannelの設定
    • # etcdctl mk /atomic.io/network/config '{"Network":"172.17.0.0/16"}' の実行
      • すでに追加されている場合もある?
  4. kubernetes の設定
    • 鍵の作成と配置
    • /etc/kubernetes/apiserver への設定の追記
      • KUBE_API_ADDRESS
      • KUBE_API_ARGS
    • /etc/kubernetes/controller-manager への設定の追記
      • KUBE_CONTROLLER_MANAGER_ARGS
    • /etc/kubernetes/config への設定の追記
      • KUBE_MASTER
    • コンポーネントの起動
      • kube-apiserver
      • kube-controller-manager
      • kube-scheduler
      • kube-proxy
  5. kubectl コマンドのための設定
    • ~/.kube/config の作成

とまぁ、多いっすね。ただ itamae を使えば、以下を実行するだけでできちゃう。かがくのちからってすげー!

$ bundle exec itamae ssh -h master01 -j master/config.json master/etcd.rb
$ bundle exec itamae ssh -h master01 master/flanneld.rb
<serviceaccount.key 用の鍵を作成し、鍵ファイルを files 以下に置いてから>
$ bundle exec itamae ssh -h master01 -j master/config.json master/kubernetes.rb
$ bundle exec itamae ssh -h master01 -j master/config.json master/kubectl_config.rb

鍵の作成は openssl genrsa -out /path/to/serviceaccount.key 2048 とかで作成します。

5. コンテナ稼働ノードのセットアップ

コンテナ稼働ノードのための作業は以下のようなものになる。

  1. flannel のインストール・設定(docker のインストール・起動)
    • flannel のインストール
    • /etc/sysconfig/flanneld の設定 FLANNEL_ETCD_ENDPOINTS の変更
    • flanneld を起動したのちに docker を起動する
  2. kubernetes関連の設定
    • /etc/kubernetes/config
      • KUBE_MASTER パラメータでマスターサーバーのIPアドレスを指定
    • /etc/kubernetes/kubelet
      • KUBELET_ADDRESS に自身のIPアドレス
      • KUBELET_HOSTNAME を編集し、自身のホスト名をノード名として用いられるように
      • KUBELET_API_SERVER でapiserverが稼動しているホストを指定
      • kube-proxyおよびkubeletサービスを立ち上げる

itamae によるセットアップをおこなう場合は以下。

$ bundle exec itamae ssh -h node01 -j node/node_config.json node/flanneld.rb
$ bundle exec itamae ssh -h node02 -j node/node_config.json node/flanneld.rb
$ bundle exec itamae ssh -h node01 -j node/node_config.json node/kubernetes.rb
$ bundle exec itamae ssh -h node02 -j node/node_config.json node/kubernetes.rb

以上で k8s クラスタの構築は完了。マスターノードに ssh して以下のようなコマンドを実行してみる。

$ kubectl get nodes
NAME      STATUS    AGE
node01    Ready     3m
node02    Ready     9s

大丈夫そう。これで k8s クラスタの構築はおわり。

6. Pod の作成、起動を試す

ノードを認識しているかどうかだけじゃなく、ちゃんとコンテナを動作させることができるかどうかも確認してみる。以下のような yaml をマスターノード内に作成する。

apiVersion: v1
kind: Pod
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  containers:
  - name: httpd
    image: httpd
    ports:
    - containerPort: 80

Apache HTTP Serverを含んだコンテナイメージを指定している。この yaml ファイルを指定して Pod を起動してみる。

$ kubectl create -f pod.yaml 
pod "httpd" created
$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
httpd     1/1       Running   0          2m

よさそう。コンテナ稼働ノード内でも確認。

$ ssh node02
Last login: Sun Oct  1 09:45:03 2017 from xxx
[a-know@node02 ~]$ sudo docker ps
CONTAINER ID        IMAGE                                                        COMMAND              CREATED              STATUS              PORTS               NAMES
bd2f5f5d2df9        httpd                                                        "httpd-foreground"   About a minute ago   Up About a minute                       k8s_httpd.74972ef5_httpd_default_8ff41316-a641-11e7-b784-fa163e02c2ac_a6103525
a0d18e000506        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/usr/bin/pod"       About a minute ago   Up About a minute                       k8s_POD.a8590b41_httpd_default_8ff41316-a641-11e7-b784-fa163e02c2ac_eb268f31

よさそう。

$ kubectl get pod httpd -o yaml コマンドの実行で、対象の Pod に関する詳細な情報が yaml 形式で取得できる。その中の podIP がそのPodに割り当てられたIPアドレスになる。そのアドレス宛にリクエストをすると、コンテナとして起動している Apache からの応答を確認することができる。

$ curl <Pod IPアドレス>
<html><body><h1>It works!</h1></body></html>

Pod の削除は $ kubectl delete pod httpd

$ kubectl delete pod httpd
pod "httpd" deleted

7. オマケ

最後に、仕事柄、全てのノードで mackerel-agent に動いていてほしいのでそれのセットアップをする。全てのノートで動かしたいようなものの場合は Pod ではなく DaemonSet を指定する。

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  labels:
    name: mackerel-agent
  name: mackerel-agent
spec:
  template:
    metadata:
      labels:
        app: mackerel-agent
    spec:
      containers:
        - name: mackerel-agent
          image: mackerel/mackerel-agent:latest
          imagePullPolicy: Always
          env:
          - name: apikey
            value: <API Key>
          - name: opts
            value: -role=<service-name>:<role-name>
          - name: enable_docker_plugin
            value: "1"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/bin/mackerel-agent", "retire", "-force"]
          volumeMounts:
          - name: docker-sock
            mountPath: /var/run/docker.sock
          - name: mackerel-id
            mountPath: /var/lib/mackerel-agent/
      volumes:
        - name: docker-sock
          hostPath:
            path: /var/run/docker.sock
        - name: mackerel-id
          hostPath:
            path: /var/lib/mackerel-agent/

API Key などのセットを忘れずに。

で、これを指定して起動。

$ kubectl create -f mackerel-agent-daemonset.yaml 
daemonset "mackerel-agent" created
$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
mackerel-agent-gcxgf   1/1       Running   0          47s
mackerel-agent-l1b4r   1/1       Running   0          47s

Mackerel の画面上では以下のように見える。

f:id:a-know:20171001121627p:plain

f:id:a-know:20171001121640p:plain

ちなみにマスターノードには docker はインストールしてないので、こちらには普通に mackerel-agent をインストール。

f:id:a-know:20171001121754p:plain

$ curl -fsSL https://mackerel.io/file/script/setup-all-yum-v2.sh | MACKEREL_APIKEY='xxxx' sh
This script requires superuser authority to setup Mackerel agent:
++ mktemp
+ gpgkey_path=/tmp/tmp.kpBsMfuLIk
+ curl -fsS -o /tmp/tmp.kpBsMfuLIk https://mackerel.io/file/cert/GPG-KEY-mackerel-v2
+ rpm --import /tmp/tmp.kpBsMfuLIk
+ rm /tmp/tmp.kpBsMfuLIk
+ cat
+ yum install -y mackerel-agent
<中略>
インストール:
  mackerel-agent.x86_64 0:0.45.0-1.el7.centos                                                                                                                                     

完了しました!
+ mackerel-agent init -apikey=xxxx
+ systemctl start mackerel-agent
*************************************

     Done! Welcome to Mackerel!

*************************************

......って、この作業だけ itamae の recipe を書かずにやっちゃった。まぁいいか。。

f:id:a-know:20171001121817p:plain

f:id:a-know:20171001121830p:plain

Mackerel も5台までなら無料の範囲でモニタリング可能なので、CloudGarage との相性はよさそう。

まとめ

開発者(特にWebサービスとか作って公開したい!それを通じてサーバー運用とか勉強したい!とか思っている人)にとって「サーバーをどうするか」というのは結構悩みのタネになっていると思っていて、AWSあるやん・GCPあるやん、といっても、毎月の出費としては無視できない額のコストが掛かることを考えると、泣く泣くローカルマシン上のVMとかで自分を慰めていた人も多いんじゃないかと思う(僕は泣きながらパブリッククラウドに幾ばくかのお金を払って使っている)。ましてや、複数インスタンス + ロードバランサを使っての負荷分散をさせたいようなユースケースではなおさら。

そんななか、CloudGarage の提供してくれているサービスメニューは、まさに開発者の味方といってもいいものだと思う。今回は出てこなかったけれど、CloudGarage はロードバランサーもひとつ、無料で提供してくれる

この「もれなく3台」という特長をいかした様々な遊びを、今後もやっていきたい。ということで、次は何をしよっかな!

参考

knowledge.sakura.ad.jp



follow us in feedly