機械学習
docker
Ansible
人工知能
kubernetes

[業務で使える!]Kubernetes(Docker)、Ansibleを使った機械学習基盤の構築自動化(ベアメタル)メモ

はじめに

久しぶりの投稿です。
今回は社内で使っている機械学習機の自動構築基盤の構築メモになります。

twitterやってます。
フォローお願いします。

※※※ 人工知能で競馬予想sivaを運用しています。
連対的中率 : 約 86%
回収率 : 約 136%

今回の環境構築はGAUSSのインフラチームで構築しました!!
@monkeydaichan
@tsukasa1301

今後もどんどん記事更新していきますのでフォローよろしくお願いいたします。
また、アプリチームはTypescript + ReactでAPIとSPAの新フレームワーク構築してますので、そちらもQiitaに書きます!!

目的

以下の二点を目標として環境構築します。
① 機械学習環境構築の自動化する。(MAAS / Ansible)
② エンジニアPoC用のDockerコンテナの利用を目的とする。 (Kubernetes)

[背景(課題)]
サーバ増強するたびに環境構築が大変。
サーバ(端末)によって環境差異が発生してしまう。
社内のエンジニアのサーバーの使用状況がわかりにくい。

機械学習マシン構成

1. スペック

以下のPCを8台準備

   
CPU Intel Core i7-8700K(6コア、12スレッド)
MEM DDR4 32G
GPU ASUS NVIDIA GTX1070ti * 2

自作したサーバ

IMG_1058.JPG

2. ネットワーク

構築するネットワーク構成は以下の図とする。
MachineLearning用のPC(PC)を自動構築する。

network.png

3. ミドルウェア

ミドルウェア構成は以下の様にする。
MAASサーバからMAASクライアント(MLP(機械学習基盤))に自動構築させる。

mw.png

構築フロー

flow.png

MAAS(ベアメタル)作業

① OSのインストール

MAASサーバにするマシンにOSをインストールする。
OSはUbuntu18.04にする。

・ubuntuダウンロード

以下のURLからUbuntu ServerのISOイメージをダウンロードする.

http://releases.ubuntu.com/18.04/

・Ubuntuイメージの起動ディスク作成

手持ちのUSBメモリにUbuntuのISOイメージを焼き付けて、インストール用メディアを作成する.

・USBメモリの確認

MACにUSBメモリを差した後、ターミナルを立ち上げディスクの場所を確認する.

diskutil list

スクリーンショット 2018-05-20 19.18.13.png

コマンド実行時はUSBメモリは/dev/disk2となっていたので、これを一度MS-DOS形式でフォーマットする.

diskutil eraseDisk MS-DOS UNTITLED /dev/disk2

フォーマットされたUSBメモリをアンマウントしddコマンドで起動ディスクを作成する.

diskutil unmountDisk /dev/disk2
sudo dd if=./Downloads/ubuntu-18.04-live-server-amd64.iso of=/dev/disk2 bs=4028

多少時間がかかるため、コーヒータイム.
イメージ作成が完了したら、デバイスを取り外す.

もし刺さったままなら、

diskutil eject /dev/disk2

を実行してUSBメモリを抜く.

・Ubuntuイメージのインストール

MAASサーバ用の端末に先ほど作成したUSBを差し、BOOT画面でUSB起動を指定しインストールを開始.
BOOTデバイスの指定はマシンによって異なるため省略.

インストールのオプションは任意の設定で.

② MAASインストール

サーバーにMAASパッケージをインストールする.

$ sudo apt -y update
$ sudo apt -y upgrade
$ sudo apt -y install maas

インストールが完了したら管理者用ユーザの作成.
必要なユーザー名、パスワード、アドレスなどを設定.
SSHキーの登録は後で行うため、空欄のままエンターでスルー.

$ sudo maas createadmin

作成が完了したら「MAASサーバーのアドレス:5240/MAAS/」でログイン.

③ MAASの設定

ログイン画面が出てくるため、
作成したユーザーでログイン.

スクリーンショット 2018-05-22 14.24.41.png

ログイン後、イントロページが出てくる.
特に設定をいじる必要はない.

後者の画像では、ダウンロードするUbuntuのOSイメージを選択する.
欲しいバージョンにチェックを入れたら、基本的に自動でダウンロードが始まるため、しばらく待つ.

ダウンロードが完了し、「Status」が「Synced」になったらContinueをクリック

スクリーンショット 2018-05-22 14.24.59.png
スクリーンショット 2018-05-22 14.25.20.png

秘密鍵を入力する画面に移るため、Ubuntuサーバー側で鍵を生成する.
特に指定はせずに、空欄のままエンター連打.

$ sudo ssh-keygen -t rsa

サーバーの.sshフォルダに鍵が生成されるため、
そちらの公開鍵の中身をコピーする.

cat .ssh/id_rsa.pub

コピー後はMAASの管理サーバーに戻り、SourceをUploadに変え、隣の入力画面に先ほどコピーした鍵の中身をペーストし、Importをクリック.
問題がなければ「Go to dashboard」をクリック.

スクリーンショット 2018-05-22 14.25.54.png

準備として、MAASトップページ上部の「Subnets」ページ内、展開したいネットワークVLANのuntaggedを選択.

スクリーンショット 2018-05-23 12.28.03.png

画面上部の「Take action」から「Provide DHCP」を選択.

スクリーンショット 2018-05-23 12.32.09.png

MAASサーバーのDHCPレンジが出てくるため、任意の数を指定し、「Provide DHCP」を選択すると、同じページ内のDHCP項目がEnableになり、「Reserved ranges」に追加したネットワーク範囲が表示される.
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f64616464393437352d626531372d663134302d343534662d3738653037616661623833342e706e67.png

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f61386132633162382d666161392d373463342d343033302d6465343265633232393032652e706e67.png

これで一通りの下準備が完了になる.

④ MAASクライアントにOSデプロイ

MAASクライアントとなる端末のBOOT順序でPXE bootの項目を起動順序の一位へする.
その後クライアントの電源を入れると自動でMAASサーバーと通信を開始し、電源が落ちる.
電源が落ちた後は、MAASの管理画面上の「Machines」に端末が追加される.
(端末の名前は仮で動物の名前が入る)
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f39303965386538342d303465642d316430392d623232342d3663613962366132383333352e706e67.png

追加されたマシンを選択し、まずは名前の変更をする.
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f32366162613330652d373034352d313963662d323831332d3630303036356336396365362e706e67.png

「Configration」内の「Power configuration」で電源オプションを選択.
今回は「Manual」を選択する.
スクリーンショット 2018-05-23 16.03.41.png

「Machines」ページで、端末のチェックボックスにチェックを入れ、「Take action」から「Commission」を選択した後、クライアント端末の電源を入れる.
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f66666561643063632d323034382d313363342d386665652d3363303734316634656464362e706e67.png

再びサーバーとの通信を開始するので終了まで待つ.
無事にCommissionが終了したら、サーバーの管理画面では「Status」が「Ready」になる.
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f35316137386634322d373239632d333466652d386265312d6133303934666434366330632e706e67.png

「Take action」から「Deploy」を選択し任意のOSを選択し、デプロイを開始させ、クライアントの電源を入れる.

サーバーからOSがデプロイされるため、しばし待ち、管理画面上でStatus欄がOS名に変われば成功
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3235393334332f66633432313464632d663562382d643934382d653133302d3335616265613531353863312e706e67.png

クライアント端末のネットワーク設定について

MAASのクライアント端末は基本はDHCPだが、Staticに指定することも可能.

その場合はOSのデプロイ前に端末の情報ページ内「Interfaces」内の「Actions」-「Edit Physical」を選択し、「Auto assign」を「Static」に変更し、任意のアドレスを指定する.

MAASのクライアント端末は基本はDHCPだが、Staticに指定することも可能.

その場合はOSのデプロイ前に端末の情報ページ内「Interfaces」内の「Actions」-「Edit Physical」を選択し、「Auto assign」を「Static」に変更し、任意のアドレスを指定する.

スクリーンショット 2018-05-22 13.54.10.png
スクリーンショット 2018-05-22 13.54.26.png

⑤ KVM(仮想環境)のインストール

Ansibleサーバー、k8sサーバーを作るためのKVMを土台となるMAASサーバーにインストールする
インストール後はlibvirtグループに参加し、sudoなしでも実行できるようにする

$ sudo apt install -y qemu-kvm libvirt0 libvirt-bin virt-manager bridge-utils
$ sudo systemctl enable libvirt-bin
$ sudo gpasswd libvirtd -a <username>

KVMの作成はデスクトップ環境を使用した方が便利なため、MAASサーバーにデスクトップをインストールする

$ sudo apt -y install ubuntu-desktop

デスクトップのインストールは時間がかかるためしばし待つ
インストールが完了して再起動すれば自動でデスクトップが表示される

デスクトップ上のターミナルでKVMを起動すれば作成用ウィンドウが起動される

$ virt-manager

0001_New-Virtual-Machine.png

ここで下記スペックのKVMを新規作成する

ホスト名:Ansible
メモリ:4GB
CPU:2
ストレージ:30GB

ホスト名:k8s-master
メモリ:8GB
CPU:4
ストレージ:40GB

⑥ Ansibleの構築

KVMで作成したAnsibleサーバーに実際にAnsibleをインストールする

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

⑦ Kubernetes Master(k8s-master)インストール

k8sのクライントが配置されるネットワークの準備をする

k8s-masterとworkerは別のネットワークにするため、Nicを追加して別セグメントを用意する。

/etc/netplan/50-cloud-init.yaml_bkの記述だけでいけるはずだったが、ブリッジの設定がうまくいかなかったため今回は2種類のファイルに記述している

$ sudo vi /etc/netplan/50-cloud-init.yaml_bk
network:
    ethernets:
        enp0s31f6:
            addresses:
            - 192.168.100.152/24
            gateway4: 192.168.100.1
            nameservers:
                addresses:
                - 8.8.8.8
                search:
                - 8.8.4.4
        enp2s0:
            addresses:
            - 192.168.200.1/24
            gateway4: 192.168.100.1
            nameservers:
                addresses:
                - 8.8.8.8
                search:
                - 8.8.4.4
    version: 2

$ sudo vi /etc/network/interfaces
auto lo
iface lo inet loopback

auto enp0s31f6
iface enp0s31f6 inet manual

auto br0
iface br0 inet static
address 192.168.100.152
netmask 255.255.255.0
gateway 192.168.100.1
dns-nameservers 8.8.8.8
bridge_ports enp0s31f6
bridge_maxwait 0
bridge_df 0
bridge_stp off


auto enp2s0
iface enp2s0 inet static
address 192.168.200.1
netmask 255.255.255.0
gateway 192.168.100.1
dns-nameservers 8.8.8.8

$ ifconfig
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.152  netmask 255.255.255.0  broadcast 192.168.100.255
        inet6 fe80::329c:23ff:feac:5570  prefixlen 64  scopeid 0x20<link>
        ether 30:9c:23:ac:55:70  txqueuelen 1000  (Ethernet)
        RX packets 9579059  bytes 16579553543 (16.5 GB)
        RX errors 0  dropped 657286  overruns 0  frame 0
        TX packets 6047022  bytes 936298283 (936.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s31f6: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::329c:23ff:feac:5570  prefixlen 64  scopeid 0x20<link>
        ether 30:9c:23:ac:55:70  txqueuelen 1000  (Ethernet)
        RX packets 21689196  bytes 26237413396 (26.2 GB)
        RX errors 0  dropped 475  overruns 0  frame 0
        TX packets 6555651  bytes 4057603928 (4.0 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 16  memory 0xdf100000-df120000

enp2s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.200.1  netmask 255.255.255.0  broadcast 192.168.200.255
        inet6 fe80::6a05:caff:fe66:a834  prefixlen 64  scopeid 0x20<link>
        ether 68:05:ca:66:a8:34  txqueuelen 1000  (Ethernet)
        RX packets 6867754  bytes 970026556 (970.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13304857  bytes 15246678579 (15.2 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 17  memory 0xdf0c0000-df0e0000

また2つのネットワークで通信できるようにNATの設定をする

sudo iptables -t nat -A POSTROUTING -s 192.168.200.0/24 -j SNAT --to 192.168.100.152

k8s-masterの構築(playbook作成〜実行)

k8s-masterにk8sをインストールするためのplaybookを作成する

$ sudo vi k8s-master.yaml

---
- hosts: k8s-master
  remote_user: $user名
  become: yes
  tasks:
    - name: Install prerequisites and Docker.io #dockerインストール
      become: yes
      apt: name={{item}} update_cache=yes
      with_items:
        - apt-transport-https
        - ca-certificates
        - curl
        - software-properties-common
        - docker.io
    - name: user add to docker group
      user: name=gauss group=docker append=yes
    - name: Add K8S GPG key #k8sインストール準備
      apt_key:
        url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
    - name: Add K8S APT repository
      apt_repository:
        repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
    - name: Install K8S
      apt: name={{item}} update_cache=yes
      with_items:
        - kubelet
        - kubeadm
        - kubectl
    - name: Remove swapfile from /etc/fstab #swapを消しておかないと失敗する
      mount:
        name: swap
        fstype: swap
        state: absent
    - name: Disable swap
      command: swapoff -a
      when: Ansible_swaptotal_mb > 0
    - name: Set docker service to start on boot. #再起動後もdockerを自動起動させる
      service: name=docker enabled=yes
    - name: Set kubelet service to start on boot. #再起動後もk8sを自動起動させる
      service: name=kubelet enabled=yes
    - name: Init k8s-master #k8smの初期化
      become: yes
      shell: kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.200.2
    - name: Make Directory .kube
      file:
        path: /.kube
        state: directory
        owner: $オーナー
        group: docker
        mode: 0755
    - name: Copy the .kube config
      become: yes
      file:
        src: /home/$ユーザ名/Ansible/admin.conf
        dest: ~/.kube/config
        owner: $オーナー
        group: docker
        mode: 0600
    - name: Export Kubernetes
      lineinfile:
        path: /home/$ユーザ名/.kube/config
        state: absent
        regexp: '^%KUBECONFIG'
    - name: Apply Flannel $flannelのネットワークを作成
      sudo: yes
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml

次はAnsibleのhostsを追加設定

$ sudo vi /etc/Ansible/hosts
[master]
k8s-master

$ sudo vi /etc/hosts
192.168.100.2 k8s-master

準備ができたらAnsibleのplaybook実行
python3をしようする場合は"-e"以降のオプションが必要

~/Ansible$ sudo Ansible-playbook --private-key=id_rsa_common k8s-master.yml -e 'Ansible_python_interpreter=/usr/bin/python3'

エラーが出なければ完了

⑧ Kubernetes Clientのインストール(workerの追加)

k8sのworkerを追加するにはマスターの場合とほとんど変わらないがflannelネットワークに参加させる最後に起動させるコマンドが必要になる

そのコマンドはkubeadm initを行なった際の出力内容に表示されるが、下記コマンドでも確認することができる

(k8s-master)$ kubeadm token create --print-join-command

出てきたコマンドを使ってworker参加用のplaybookを作成する

(Ansible)$ sudo vi mlp.yml
---
- hosts: mlp01
  remote_user: ubuntu
  become: yes
  tasks:
    - name: Install prerequisites and Docker.io , nfs-common
      apt: name={{item}} update_cache=yes
      with_items:
        - nfs-common
        - apt-transport-https
        - ca-certificates
        - curl
        - software-properties-common
        - docker.io
    - name: user add to docker group
      user: name=ubuntu group=docker append=yes
    - name: Add K8S GPG key
      apt_key:
        url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
    - name: Add K8S APT repository
      apt_repository:
        repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
    - name: Install K8S
      apt: name={{item}} update_cache=yes
      with_items:
        - kubelet
        - kubeadm
        - kubectl
    - name: Remove swapfile from /etc/fstab
      mount:
        name: swap
        fstype: swap
        state: absent
    - name: Disable swap
      command: swapoff -a
      when: Ansible_swaptotal_mb > 0
    - name: Set docker service to start on boot.
      service: name=docker enabled=yes
    - name: Set kubelet service to start on boot.
      service: name=kubelet enabled=yes
    - name: Join k8s-master
      become: yes
      shell: kubeadm join 192.168.100.3:6443 ~~ #上記のコマンドで出力したものを記述

マスターの時と同様にhostsの編集

$ sudo vi /etc/Ansible/hosts
[master]
k8s-master

[mlp]
mlp01

$ sudo vi /etc/hosts
192.168.100.3 k8s-master
192.168.200.2 mlp01

Ansibleのplaybook実行
python3をしようする場合は"-e"以降のオプションが必要

~/Ansible$ sudo Ansible-playbook --private-key=id_rsa_common mlp.yml -e 'Ansible_python_interpreter=/usr/bin/python3'

playbookが成功した場合、以下の状態になる

(k8s-master)$ kubectl get node
NAME         STATUS    ROLES     AGE       VERSION
k8s-master   Ready     master    3d        v1.10.3
mlp01        Ready     <none>    3d        v1.10.2

ここでインストール(構築作業)は完了です!!

k8sでdockerイメージを走らせる(テスト)

nginxイメージの起動

nodeが追加された後は、ひとまずnginxのdockerイメージを走らせる
理由は後述する

$ sudo vi nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
      - containerPort: 80

$ kubectl apply nginx-pod.yaml
create
$ kubectl get pod
NAME           READY     STATUS    RESTARTS   AGE
nginx-pod      1/1       Running   0          3d

nginxがRunningになれば問題ない

Dash-boardのインストール

Dash-boardはなくても、kubernetesを使う上ではなんとかなるがせっかくなら使いたい
というわけでインストールをしていく

$ kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

エラーが出ず、「created」と表示されればOK
動いているかを確認

kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
default       nginx-pod                               1/1       Running   0          3d
kube-system   kubernetes-dashboard-7d5dcdb6d9-7hptz   1/1       Running   0          3d

kubeのproxyを起動

$ kubectl proxy --address 0.0.0.0 --accept-hosts '.*'
Starting to serve on [::]:8001

この状態になったらブラウザでアクセスして見る
ログイン画面が出てくる

http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login

スクリーンショット 2018-05-29 14.24.06.png

ログインするためのトークンを発行する

$ kubectl -n kube-system get secret

発行されたトークンを使えば入れる

毎回トークンを発行するのが大変という人向けの設定は以下のもの
これを実行した後は、ログイン画面のSKIPで入れるようになる
ただしセキュリティ的にはガバガバなため、社内オンリーの環境など外部の人がいない条件での使用推奨

$ cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system
EOF

お疲れ様でした!!

IMG_1073.JPG

さいごに

この環境構築はGAUSSインフラチームで構築しました!!
@monkeydaichan
@tsukasa1301

今後もどんどん記事更新していきますのでフォローよろしくお願いいたします。
なお、アプリチームはTypescript + ReactでAPIとSPAの新フレームワーク構築してますのでそちらも継続して見てください!

twitterやってます。
フォローお願いします。

※※※ 人工知能で競馬予想sivaを運用しています。
連対的中率 : 約 86%
回収率 : 約 136%