Edited at

ニフクラでもできる!Kubernetes クラスタ

More than 1 year has passed since last update.

この記事は 富士通クラウドテクノロジーズ Advent Calendar 2017 の15日目の記事です。

昨日は mini_act4 さんの 認定スクラムプロダクトオーナー研修を受けて気づいた大切なこと でした。

mini_act4 さんがオーナーをするプロダクトやチームに期待が高まります!


TL;DR


はじめに

Serverlessconf TokyoKubernetes Meetpup Tokyo #8 に参加して、物理サーバーやVMといったインフラを抽象化し、設定ファイルやプログラムといったコードによってシステムを管理・運用するのすげー!と感じて Kubernetes について色々調べる中で、何はともあれクラスタ作らないとな、と言うことで仕事で触る機会の多いニフクラで簡単に構築できるように試してみました。

Kubernetes についての概要は MahoTakara さんの 今さら人に聞けない Kubernetes とは? が分かりやすかったかなと思いました。

Kubernetes クラスタのデプロイについては albatross さんの kubespray (kargo-cli) で20分で Kubernetes クラスタを作る を参考にさせていただきました。ありがとうございます。

kubespray を使うとサーバーさえ用意すればサクッとクラスタを構築できちゃうので、すごい世の中です。


やること


  • サーバーを準備する



  • クラスタをデプロイする



    • kubespray-cli を使ってクラスタをデプロイします。

    • 私はとりあえず試してみたいというモチベーションなので、運用性とかセキュリティーは一旦無視しちゃいます。
      なので諸々のクラスタ設定はデフォルト.....




構築するクラスタの情報

クラスタイメージ

※ 引用:https://github.com/kubespray/kubespray-cli


  • node:3台


    • OSイメージ:CentOS 7.1

    • インスタンスタイプ:Small2



    • ネットワーク:共通グローバルは無し/適当に作ったプライベートLAN


      • 共通グローバルにルーティング(インターネットに出れない環境では試してないです)





    • 内訳


      • master:2台

      • etcd:3台






ansible-role-nifcloud でサーバー作成



  • ansible-playbook のディレクトリ構成は下記のような感じです。


    tree

    ├── group_vars
    
    │   └── all.yml
    ├── roles
    │   └── nifcloud_vm
    │   ├── tasks
    │   │   └── main.yml
    │   └── templates
    │   └── startup.sh
    └── site.yml





ansible-playbook のファイルたち



  • site.yml

    ansible-playbook コマンドで直接実行するymlファイルです。

    nifcloud.nifcloud という名前の ansible-role-nifcloud を読みこんだり、

    自作の nifcloud_vm ロールを読みこんで実行したりします。

    ちなみに、nifcloud.nifcloud をモジュールとして使うために、site.yml で読み込ませないと動かないのですが、

    その情報に辿りつけずに数日ハマりました...


    site.yml

    - name: provision vms
    
    hosts: all
    connection: local # nifcloud.nifcloud はAPIを使うだけなので connection は local です
    roles:
    - nifcloud.nifcloud # デプロイ用ロールの前に、nifcloud.nifcloud を読ませるのが必要です。
    - nifcloud_vm # 後述




  • group_vars/all.yml

    全ホスト共通の設定です。


    all.yml

    computing:
    
    region: <YOUR_CLUSTER_REGION> # クラスタをデプロイするリージョン:jp-east-1 とか
    availability_zone: <YOUR_CLUSTER_ZONE> # クラスタをデプロイするゾーン:jp-east-14 とか
    instance_type: <INSTANCE_TYPE_NAME> # サーバータイプ:kubespray-cli は 2GB以上のメモリが必須なので small2 とか
    image_id: <YOUR_IMAGE_IE> # イメージID:コントロールパネルのOSイメージメニューを確認 CentOS 7.1 なら 68
    key_name: <YOUR_SSH_KEY_NAME> # 登録したSSHキーの名前
    security_group: <YOUR_SECURITY_GROUP_NAME> # 適用するファイアウォールの名前
    root_password: <YOUR_ROOT_PASSWORD> # インスタンスに設定するルートパスワード
    credentials:
    access_key: <YOUR_ACCESS_KEY> # アクセスキー
    secret_key: <YOUR_SECRET_ACCESS_LEY> # シークレットキー
    private_network:
    id: <YOUR_PLAN_ID> # クラスタをデプロイするネットワークのID:net-XXXXXXXXX みたいなやつ
    address: <YOUR_PLAN_ADDRESS> # プライベートLANのネットワークアドレス:10.0.0.0/24 とか
    gateway: <YOUR_PLAN_GATEWAY> # ゲートウェイ: 10.0.0.1 とか




  • roles/nifcloud_vm

    サーバーをプロビジョニングするansibleロールです。



    • roles/nifcloud_vm/tasks/main.yml

      GitHubのサンプルを参考に作成しました。


      main.yml

      - name: Install (Requests) python package
      
      local_action:
      module: pip
      name: requests
      - name: Start server
      local_action:
      module: nifcloud
      access_key: "{{ credentials.access_key }}"
      secret_access_key: "{{ credentials.secret_key }}"
      endpoint: "computing.{{ computing.region }}.api.cloud.nifty.com"
      instance_id: "{{ inventory_hostname }}"
      state: "running"
      image_id: "{{ computing.image_id }}"
      key_name: "{{ computing.key_name }}"
      instance_type: "{{ computing.instance_type }}"
      availability_zone: "{{ computing.availability_zone }}"
      security_group: "{{ computing.security_group}}"
      accounting_type: "2"
      ip_type: "none"
      startup_script: "roles/nifcloud_vm/templates/startup.sh"
      startup_script_vars: # 後述のサーバー起動時スクリプトに渡す値
      root_password: "{{ computing.root_password }}"
      ip_addr: "{{ ansible_ssh_host }}/24"
      network_addr: "{{ private_network.address }}"
      gateway: "{{ private_network.gateway }}"
      network_interface:
      - network_id: "{{ private_network.id }}"
      ipAddress: "static"




    • roles/nifcloud_vm/templates/startup.sh


      • ニフクラの サーバー起動時スクリプト の機能を使って諸々設定します


        • インスタンスIDをホスト名に設定

        • root のパスワードを設定(やらなくても良い)

        • プライベートLANのインターフェースに任意のアドレスを設定



      • ysaotome さんの Gist を参考に作成しました


      starup.sh

      #!/bin/sh

      (
      #===============================================
      # Settings
      #===============================================
      ##rootのパスワード
      ROOT_PASSWORD='{root_password}'
      #===============================================
      ARC=$(/bin/uname -m)
      SALT=$(/usr/bin/uuidgen| /usr/bin/tr -d '-')

      ## hostname変更
      HOSTNAME=$(/usr/bin/vmtoolsd --cmd 'info-get guestinfo.hostname')
      /bin/hostname ${{HOSTNAME}}
      /bin/sed -i.org -e 's/localhost.localdomain/'${{HOSTNAME}}'/' /etc/hostname

      ## ROOTパスワード設定
      /usr/sbin/usermod -p $(/usr/bin/perl -e 'print crypt(${{ARGV[0]}}, ${{ARGV[1]}})' ${{ROOT_PASSWORD}} ${{SALT}}) root

      ## swap領域 無効化
      /usr/sbin/swapoff `awk '{{print $1}}' /proc/swaps | grep -v 'Filename'`
      /usr/bin/sed -i -e "/swap/s/^/#/g" /etc/fstab

      ## network 設定
      IPADDR={ip_addr} # yaml から渡された値
      CONNECTION="System ens160" # 注意!:共通グローバルが有効な場合はプライベートLANのコネクションは ens192 になります。
      DISABLE_CONNECTION="System ens192"
      NETWORK_ADDR={network_addr} # yaml から渡された値
      GATEWAY={gateway} # yaml から渡された値
      /usr/bin/nmcli connection modify "${{DISABLE_CONNECTION}}" connection.autoconnect no
      /usr/bin/nmcli connection modify "${{CONNECTION}}" ipv4.addresses ${{IPADDR}}
      /usr/bin/nmcli connection modify "${{CONNECTION}}" ipv4.gateway ${{GATEWAY}}
      /usr/bin/nmcli connection modify "${{CONNECTION}}" ipv4.dns ${{GATEWAY}}
      /usr/bin/nmcli connection modify "${{CONNECTION}}" ipv4.method manual
      /usr/bin/nmcli connection down "${{CONNECTION}}"
      /usr/bin/nmcli connection up "${{CONNECTION}}"
      /bin/systemctl restart NetworkManager.service
      /bin/systemctl restart network.service
      ) 2>&1 | /usr/bin/tee /var/log/niftycloud-init.log







  • インベントリファイルは?



    • kubespray-cli prepare コマンドを使うと、 .kubespray/inventory/inventory.cfg にansible のインベントリファイルが作成されるため、コレを使います。



      • 例えばこうすると、

        $ kubespray prepare --nodes \
        
        node1[ansible_ssh_host=10.0.0.101] \
        node2[ansible_ssh_host=10.0.0.102] \
        node3[ansible_ssh_host=10.0.0.103]

        こうなります。


        .kubespray/inventory/inventory.cfg

        [kube-master]
        
        node1
        node2

        [all]
        node1 ansible_ssh_host=10.0.0.101
        node2 ansible_ssh_host=10.0.0.102
        node3 ansible_ssh_host=10.0.0.103

        [k8s-cluster:children]
        kube-node
        kube-master

        [kube-node]
        node1
        node2
        node3

        [etcd]
        node1
        node2
        node3









サーバー作成

インベントリファイルに↑のやつを指定すれば良いので、例えば↓のように実行します。

$ ansible-playbook -i .kubespray/inventory/inventory.cfg site.yml



  • 実行結果サンプル

    bash-4.2# time ansible-playbook -i .kubespray/inventory/inventory.cfg site.yml
    
    --diff

    PLAY [provision vms]
    ***********************************************************************************************************

    TASK [Gathering Facts]
    *********************************************************************************************************
    ok: [node3]
    ok: [node1]
    ok: [node2]

    TASK [nifcloud.nifcloud : Gathering facts from localhost]
    **********************************************************************
    ok: [node1 -> localhost]

    TASK [nifcloud.nifcloud : Import EPEL GPG Key (for CentOS)]
    ********************************************************************
    ok: [node1 -> localhost]

    TASK [nifcloud.nifcloud : Check EPEL repository (for CentOS)]
    ******************************************************************
    ok: [node1 -> localhost]

    TASK [nifcloud.nifcloud : Add EPEL repository (for CentOS)]
    ********************************************************************
    skipping: [node1]

    TASK [nifcloud.nifcloud : Install python-pip (for CentOS)]
    *********************************************************************
    ok: [node1 -> localhost]

    TASK [nifcloud.nifcloud : Install python-pip (for Ubuntu)]
    *********************************************************************
    skipping: [node1]

    TASK [nifcloud.nifcloud : Install "Requests" python package]
    *******************************************************************
    ok: [node1 -> localhost]

    TASK [nifcloud_vm : Install (Requests) python package]
    *************************************************************************
    ok: [node1 -> localhost]
    ok: [node2 -> localhost]
    ok: [node3 -> localhost]

    TASK [nifcloud_vm : Start server]
    **********************************************************************************************
    changed: [node2 -> localhost]
    changed: [node1 -> localhost]
    changed: [node3 -> localhost]

    PLAY RECAP
    *********************************************************************************************************************
    node1 : ok=8 changed=1 unreachable=0 failed=0
    node2 : ok=3 changed=1 unreachable=0 failed=0
    node3 : ok=3 changed=1 unreachable=0 failed=0

    real 2m22.832s
    user 0m21.668s
    sys 0m6.400s
    bash-4.2#

    という感じでサーバーが作成されます。(node1, node2, node3)


    Screenshot from 2017-12-15 00-35-46.v01.png




kubespray-cliKubernetes のデプロイ

サーバーができたら kubespray-cli を実行するだけです。

サーバーにSSH接続ができるホストで下記を実行します。( time で時間をはかってみています。)

-u オプションでユーザー、 -k オプションでSSHキーを指定しています。

-n オプションではネットワークプラグインを指定できますが、今回は flannel を使ってみました。他にも weave,calico,canal,contiv,cloud が指定できるみたいです。何も指定しないと calico になるそうです。

bash-4.2# time kubespray deploy -u root -k .ssh/kubernetes_rsa -n flannel

Identity added: /root/.ssh/kubernetes_rsa (/root/.ssh/kubernetes_rsa)

CHECKING SSH CONNECTIONS *******************************************************************************************************
/usr/bin/ansible --ssh-extra-args -o StrictHostKeyChecking=no -u root -b --become-user=root -m ping all -i /root/.kubespray/inventory/inventory.cfg
node2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
node3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
node1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
All hosts are reachable
/usr/bin/ansible-playbook --ssh-extra-args -o StrictHostKeyChecking=no -u root -b --become-user=root -i /root/.kubespray/inventory/inventory.cfg /root/.kubespray/cluster.yml -e kube_network_plugin=flannel
Run kubernetes cluster deployment with the above command ? [Y/n]
...途中割愛...
PLAY RECAP *********************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
node1 : ok=359 changed=111 unreachable=0 failed=0
node2 : ok=316 changed=105 unreachable=0 failed=0
node3 : ok=277 changed=85 unreachable=0 failed=0

Kubernetes deployed successfuly

real 11m15.910s
user 6m40.516s
sys 2m1.336s
bash-4.2#

という感じでクラスタのデプロイが完了します。


動作確認

kubectl を使ってクラスタの状態を確認しますが、

その前に Kubernetes のAPIを利用するための鍵と設定を取得(デプロイしたノードから拝借)します。

bash-4.2# scp -r -i .ssh/kubernetes_rsa root@10.0.0.101:~/.kube .

クラスタの状態を確認すると...

bash-4.2# kubectl cluster-info

Kubernetes master is running at https://10.0.0.101:6443
KubeDNS is running at https://10.0.0.101:6443/api/v1/namespaces/kube-system/services/kube-dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
bash-4.2#

どうやら動いているみたいなので、nginx のポッドをデプロイしてみます。

bash-4.2# kubectl run nginx --image=nginx:latest

deployment "nginx" created
bash-4.2# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-5db977d67c-zctf9 1/1 Running 0 43s 10.233.66.4 node3
bash-4.2#

node3 にデプロイされました。


ポッドにIPが振ってありますが、クラスタ内に閉じたIPなので、ノードのポートにエクスポーズして外からアクセスしてみます。

bash-4.2# kubectl expose deployment nginx --port 80 --type=NodePort

service "nginx" exposed
bash-4.2# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 19m <none>
nginx NodePort 10.233.21.221 <none> 80:32251/TCP 3s run=nginx
bash-4.2#

32551 ポートにマッピングされたのでブラウザでアクセスしてみます。

Screenshot from 2017-12-15 02-14-28.png

どのノードにアクセスしても同じ画面が得られるので、ちゃんと node3 にルーティングされているようです。


まとめ

ニフクラでも Kubernetes クラスタを作ることが出来ました。

ansible-role-nifcloudkubespray を使うことで、かなり簡単にクラスタをデプロイすることが出来ます。

簡単な構成で実験しているので、本番運用に向けた凝った構成にするにはもう少し工夫が必要そうです。

GitHubに上記の諸々を公開 しています。実行環境のコンテナを作る諸々も一緒に置いてありますのでご参考いただければ幸いです。

Kubernetes の勉強は始まったばかりなので、クラスタを壊したり直したりしながら理解を深めて行きたいと思います。

明日は clutter さんです!お楽しみに!


参考サイト