この記事は 富士通クラウドテクノロジーズ 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 さんです!お楽しみに!

参考サイト

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.