2017年8月22日火曜日

packer を使って vmx ファイルを作成し vSphere 上に VM を作成してみた

概要

前回 packer を使って AWS 上に AMI を作成してみました
今回は VMware Builder という vSphere 上に VM を作成できる機能を試してみました

環境

  • ESXi 6.5.0
  • Ubuntu 16.04
  • packer 1.0.4

ESXi 準備

今回ビルドは ESXi 上で行います
ローカルでもできるのですが、VMware workstation なるものをインストールしなければいけないらしく自分にとってはハードルが高かったのでビルド用の ESXi を用意しました
ESXi の IP は 192.168.100.6 とします

SSH の有効化

ESXi に SSH できるようにしてください

GuestIPHack の有効化

GuestIPHack という設定を有効にします
これもコマンドから行います

  • esxcli system settings advanced set -o /Net/GuestIPHack -i 1
  • esxcli system settings advanced list | grep -A 9 ‘/Net/GuestIPHack’

で有効になっていれば OK です
再起動は不要です

VNC ポートをオープンする

ESXi に VNC のポート (5900 番) でアクセスできる必要があります
ESXi のファイアウォールを管理するファイルを直接編集してオープンします

  • chmod 644 /etc/vmware/firewall/service.xml
  • chmod +t /etc/vmware/firewall/service.xml
  • vi /etc/vmware/firewall/service.xml
<service id="1000">
  <id>packer-vnc</id>
  <rule id="0000">
    <direction>inbound</direction>
    <protocol>tcp</protocol>
    <porttype>dst</porttype>
    <port>
      <begin>5900</begin>
      <end>6000</end>
    </port>
  </rule>
  <enabled>true</enabled>
  <required>true</required>
</service>
  • chmod 444 /etc/vmware/firewall/service.xml
  • esxcli network firewall refresh

で OK です

ストレージの準備

ストレージを用意しましょう
デフォルトで付いているローカルストレージでも OK です
この領域に iso ファイルをアップロードします
今回は ESXi に datastore1 というストレージをマウントしました

packer インストール

構築した ESXi にアクセスできるサーバを準備します
今回は Ubuntu 16.04 を準備しました
そのサーバに packer をインストールします
インストール方法は以下を参考にしてください

リソースファイルの取得

今回は既存のリソースファイルを拝借します

これに含まれている preseed と scripts を使いまわします

テンプレート json の準備

実行するテンプレート json を準備します
今回は Ubuntu をビルドします

まずは変数を管理するファイルを作成します

  • cd packer-esxi
  • vim variables.json
{
  "esxi_host": "192.168.100.6",
  "esxi_datastore": "datastore1/primary",
  "esxi_username": "root",
  "esxi_password": "root_password"
}

特に説明は不要かなと思います
esxi_datastore は指定したパスにディレクトリを作成します
用意したストレージ上に作成されるように指定してください

次にテンプレート json です
長いです

  • cd packer-esxi
  • vim my-ubuntu-1604-base.json
{
  "builders": [{
    "name": "ubuntu-1604-base",
    "vm_name": "ubuntu-1604-base",
    "type": "vmware-iso",
    "guest_os_type": "ubuntu-64",
    "tools_upload_flavor": "linux",
    "headless": false,

    "iso_url": "http://releases.ubuntu.com/xenial/ubuntu-16.04.3-server-amd64.iso",
    "iso_checksum": "a06cd926f5855d4f21fb4bc9978a35312f815fbda0d0ef7fdc846861f4fc4600",
    "iso_checksum_type": "sha256",

    "ssh_username": "nullgrid",
    "ssh_password": "nullgrid",
    "ssh_timeout": "15m",

    "disk_type_id": "thin",

    "floppy_files": [
      "preseed/ubuntu.cfg"
    ],

    "boot_command": [
      "<enter><wait><f6><esc><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "/install/vmlinuz noapic ",
      "preseed/file=/floppy/ubuntu.cfg ",
      "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
      "hostname={{ .Name }} ",
      "fb=false debconf/frontend=noninteractive ",
      "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
      "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
      "grub-installer/bootdev=/dev/sda ",
      "initrd=/install/initrd.gz -- <enter>"
    ],

    "shutdown_command": "echo 'shutdown -P now' > shutdown.sh; echo 'nullgrid'|sudo -S sh 'shutdown.sh'",

    "remote_type": "esx5",
    "remote_host": "{{user `esxi_host`}}",
    "remote_datastore": "{{user `esxi_datastore`}}",
    "remote_username": "{{user `esxi_username`}}",
    "remote_password": "{{user `esxi_password`}}",
    "keep_registered": true,

    "vmx_data": {
      "ethernet0.networkName": "VM Network"
    },
    "vnc_port_min": "5900",
    "vnc_port_max": "5911",
    "vnc_bind_address": "0.0.0.0",
    "vnc_disable_password": "true"
  }],

  "provisioners": [
    {
      "type": "shell",
      "scripts": [
        "scripts/open-vm-tools.sh"
      ],

      "execute_command": "echo 'nullgrid' | {{ .Vars }} sudo -E -S bash '{{ .Path }}'"
    }
  ]
}

簡単にポイントを説明します
iso_url, iso_checksum でダウンロードする iso を指定します
フロッピーディスクを接続し preseed ディレクトリをマウントしています

そして一番のポイントは boot_command です
普通 iso から OS をインストールする場合 GUI でポチポチしていきますが packer の場合ここに記載したコマンドを自動で実行します
これを VNC を使って実行するため VNC のポートを開放しています
また作成した VM 上でも VNC が立ち上がっています
boot_command 内でフロッピーにマウントした preseed/ubuntu.cfg を元に細かい OS の初期設定を行っています

あとはビルドする ESXi の場所や VNC のポートを指定しています

packer 実行

  • cd packer-esxi
  • packer build -var-file variables.json my-ubuntu-1604-base.json

でビルドが始まります
結構ログが長いですが以下のようになれば成功です

ubuntu-1604-base output will be in this color.

==> ubuntu-1604-base: Downloading or copying ISO
    ubuntu-1604-base: Downloading or copying: http://releases.ubuntu.com/xenial/ubuntu-16.04.3-server-amd64.iso
==> ubuntu-1604-base: Creating floppy disk...
    ubuntu-1604-base: Copying files flatly from floppy_files
    ubuntu-1604-base: Copying file: preseed/ubuntu.cfg
    ubuntu-1604-base: Done copying files from floppy_files
    ubuntu-1604-base: Collecting paths from floppy_dirs
    ubuntu-1604-base: Resulting paths from floppy_dirs : []
    ubuntu-1604-base: Done copying paths from floppy_dirs
==> ubuntu-1604-base: Uploading Floppy to remote machine...
==> ubuntu-1604-base: Uploading ISO to remote machine...
==> ubuntu-1604-base: Creating virtual machine disk
==> ubuntu-1604-base: Building and writing VMX file
==> ubuntu-1604-base: Registering remote VM...
==> ubuntu-1604-base: Starting virtual machine...
==> ubuntu-1604-base: Waiting 10s for boot...
==> ubuntu-1604-base: Connecting to VM via VNC
==> ubuntu-1604-base: Typing the boot command over VNC...
==> ubuntu-1604-base: Waiting for SSH to become available...
==> ubuntu-1604-base: Connected to SSH!
==> ubuntu-1604-base: Provisioning with shell script: scripts/open-vm-tools.sh
    ubuntu-1604-base: Reading package lists...
    ubuntu-1604-base: Building dependency tree...
    ubuntu-1604-base: Reading state information...
    ubuntu-1604-base: The following additional packages will be installed:
    ubuntu-1604-base: ethtool libdumbnet1 libmspack0 zerofree
    ubuntu-1604-base: Suggested packages:
    ubuntu-1604-base: open-vm-tools-desktop
    ubuntu-1604-base: The following NEW packages will be installed:
    ubuntu-1604-base: ethtool libdumbnet1 libmspack0 open-vm-tools zerofree
    ubuntu-1604-base: 0 upgraded, 5 newly installed, 0 to remove and 6 not upgraded.
    ubuntu-1604-base: Need to get 601 kB of archives.
    ubuntu-1604-base: After this operation, 2,224 kB of additional disk space will be used.
    ubuntu-1604-base: Get:1 http://archive.ubuntu.com/ubuntu xenial/main amd64 ethtool amd64 1:4.5-1 [97.5 kB]
    ubuntu-1604-base: Get:2 http://archive.ubuntu.com/ubuntu xenial/main amd64 libdumbnet1 amd64 1.12-7 [25.7 kB]
    ubuntu-1604-base: Get:3 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 libmspack0 amd64 0.5-1ubuntu0.16.04.1 [37.0 kB]
    ubuntu-1604-base: Get:4 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 open-vm-tools amd64 2:10.0.7-3227872-5ubuntu1~16.04.1 [433 kB]
    ubuntu-1604-base: Get:5 http://archive.ubuntu.com/ubuntu xenial/main amd64 zerofree amd64 1.0.3-1 [7,928 B]
    ubuntu-1604-base: [sudo] password for nullgrid: debconf: unable to initialize frontend: Dialog
    ubuntu-1604-base: debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.)
    ubuntu-1604-base: debconf: falling back to frontend: Readline
    ubuntu-1604-base: debconf: unable to initialize frontend: Readline
    ubuntu-1604-base: debconf: (This frontend requires a controlling tty.)
    ubuntu-1604-base: debconf: falling back to frontend: Teletype
    ubuntu-1604-base: dpkg-preconfigure: unable to re-open stdin:
    ubuntu-1604-base: Fetched 601 kB in 1s (348 kB/s)
    ubuntu-1604-base: Selecting previously unselected package ethtool.
    ubuntu-1604-base: (Reading database ... 59034 files and directories currently installed.)
    ubuntu-1604-base: Preparing to unpack .../ethtool_1%3a4.5-1_amd64.deb ...
    ubuntu-1604-base: Unpacking ethtool (1:4.5-1) ...
    ubuntu-1604-base: Selecting previously unselected package libdumbnet1:amd64.
    ubuntu-1604-base: Preparing to unpack .../libdumbnet1_1.12-7_amd64.deb ...
    ubuntu-1604-base: Unpacking libdumbnet1:amd64 (1.12-7) ...
    ubuntu-1604-base: Selecting previously unselected package libmspack0:amd64.
    ubuntu-1604-base: Preparing to unpack .../libmspack0_0.5-1ubuntu0.16.04.1_amd64.deb ...
    ubuntu-1604-base: Unpacking libmspack0:amd64 (0.5-1ubuntu0.16.04.1) ...
    ubuntu-1604-base: Selecting previously unselected package open-vm-tools.
    ubuntu-1604-base: Preparing to unpack .../open-vm-tools_2%3a10.0.7-3227872-5ubuntu1~16.04.1_amd64.deb ...
    ubuntu-1604-base: Unpacking open-vm-tools (2:10.0.7-3227872-5ubuntu1~16.04.1) ...
    ubuntu-1604-base: Selecting previously unselected package zerofree.
    ubuntu-1604-base: Preparing to unpack .../zerofree_1.0.3-1_amd64.deb ...
    ubuntu-1604-base: Unpacking zerofree (1.0.3-1) ...
    ubuntu-1604-base: Processing triggers for man-db (2.7.5-1) ...
    ubuntu-1604-base: Processing triggers for libc-bin (2.23-0ubuntu9) ...
    ubuntu-1604-base: Processing triggers for systemd (229-4ubuntu19) ...
    ubuntu-1604-base: Processing triggers for ureadahead (0.100.0-19) ...
    ubuntu-1604-base: Setting up ethtool (1:4.5-1) ...
    ubuntu-1604-base: Setting up libdumbnet1:amd64 (1.12-7) ...
    ubuntu-1604-base: Setting up libmspack0:amd64 (0.5-1ubuntu0.16.04.1) ...
    ubuntu-1604-base: Setting up open-vm-tools (2:10.0.7-3227872-5ubuntu1~16.04.1) ...
    ubuntu-1604-base: Setting up zerofree (1.0.3-1) ...
    ubuntu-1604-base: Processing triggers for libc-bin (2.23-0ubuntu9) ...
    ubuntu-1604-base: Processing triggers for systemd (229-4ubuntu19) ...
    ubuntu-1604-base: Processing triggers for ureadahead (0.100.0-19) ...
==> ubuntu-1604-base: Gracefully halting virtual machine...
    ubuntu-1604-base: Waiting for VMware to clean up after itself...
==> ubuntu-1604-base: Deleting unnecessary VMware files...
    ubuntu-1604-base: Deleting: /vmfs/volumes/datastore1/primary/output-ubuntu-1604-base/vmware.log
==> ubuntu-1604-base: Compacting the disk image
==> ubuntu-1604-base: Cleaning VMX prior to finishing up...
    ubuntu-1604-base: Unmounting floppy from VMX...
    ubuntu-1604-base: Detaching ISO from CD-ROM device...
    ubuntu-1604-base: Disabling VNC server...
==> ubuntu-1604-base: Keeping virtual machine registered with ESX host (keep_registered = true)
Build 'ubuntu-1604-base' finished.

==> Builds finished. The artifacts of successful builds are:
--> ubuntu-1604-base: VM files in directory: /vmfs/volumes/datastore1/primary/output-ubuntu-1604-base

成功すると ESXi 上に vmx ファイルと vmdk ファイルができています

[root@esxi:~] ls -ltr /vmfs/volumes/datastore1/primary/output-ubuntu-1604-base
total 2373640
-rw-r--r--    1 root     root             0 Aug 21 02:07 ubuntu-1604-base.vmsd
-rw-------    1 root     root           546 Aug 21 02:16 disk.vmdk
-rw-------    1 root     root          8684 Aug 21 02:16 ubuntu-1604-base.nvram
-rw-------    1 root     root   41943040000 Aug 21 02:16 disk-flat.vmdk
-rw-r--r--    1 root     root          2448 Aug 21 02:16 ubuntu-1604-base.vmx

インスタンスはすでに Discoverd virtual machine ディレクトリ配下にあります
試しに起動して中を確認してみましょう
192.168.100.7 は packer で作成した Ubuntu VM です

  • ssh nullgrid@192.168.100.7
nullgrid@unassigned-hostname:~$ dpkg -l | grep open-vm-tools
ii  open-vm-tools                      2:10.0.7-3227872-5ubuntu1~16.04.1          amd64        Open VMware Tools for virtual machines hosted on VMware (CLI)

こんな感じで open-vm-tools がインストールされていれば OK です

最後に

packer の VMware Builder を使って vmx ファイルと vmdk ファイルを作成してみました
前回試した AWS 上に AMI を作成する手順よりも、今回のほうが実行するまでの環境を準備するのが結構大変でした

vmx ファイルではなく ovf ファイルを作成することもできるので別に機会に試してみたいと思います

トラブルシューティング

mkdir: can't create directory '/vmfs/volumes/primary': Operation not permitted が出る場合は variables.json の esxi_datastore のパスにちゃんとデータストアのパスが含まれていることを確認しましょう

参考サイト

2017年8月21日月曜日

Ruby で net-ldap を使ってユーザの登録、削除、取得をやってみた

概要

前回 Ruby から ldap を操作してみました
グループの追加まで確認できたので今回はユーザ周りの操作を試してみました

環境

  • CentOS 7.3.1611
  • openldap 2.4.40
  • Ruby 2.3.1p112
  • net-ldap 0.16.0

ユーザを追加する

  • vim user_add.rb
require 'net/ldap'
require 'pp'

PORT = 389
HOST = '192.168.100.5'
BASE = 'dc=example,dc=com'
AUTH = {
  :method => :simple,
  :username => "cn=Manager,#{BASE}",
  :password => 'password'
}

ldap = Net::LDAP.new(
  host: HOST,
  port: PORT,
  base: BASE,
  auth: AUTH
)

raise 'bind failed' unless ldap.bind

dn = "uid=hawk,ou=People,#{BASE}"
attr = {
  :sn => 'hawk',
  :cn => 'snowlog',
  :objectclass => ['posixAccount', 'inetOrgPerson'],
  :displayName => 'hawksnowlog',
  :uid => 'hawk',
  :uidNumber => '1001',
  :gidNumber => '1000',
  :homeDirectory => '/home/hawk',
  :loginShell => '/bin/bash',
  :userPassword => '{CRYPT}E1N/hpjy8pfc6',
  :mail => 'hawk@hawksnowlog.cf'
}
ldap.add(
  :dn => dn,
  :attributes => attr
)
pp ldap.get_operation_result

前半の接続部分は前回のコードをそのまま使用しています
ユーザを追加するための処理は dn の定義からです

ポイントは objectclass を配列で指定しているところ
そうしないと後に定義した objectclass に上書きされてしまいます
もし inetOrgPerson を後に記載した場合 'uidNumber' not allowed と言われ uid の指定ができません

#<OpenStruct extended_response=nil, code=65, error_message="attribute 'uidNumber' not allowed", matched_dn="", message="Object Class Violation">

では、posixAccount だけ objectclass に指定するとそんなクラスはないと言われます

#<OpenStruct extended_response=nil, code=65, error_message="no structural object class provided", matched_dn="", message="Object Class Violation">

結局 objectclass を配列にして posixAccount と inetOrgPerson を一緒に指定する必要がありました

あとは実行してエラーが表示されなければ OK です

  • bundle exec ruby user_add.rb
#<OpenStruct extended_response=nil, code=0, error_message="", matched_dn="", message="Success">

userPassword の部分は slappasswd コマンドで作成した暗号化済みのパスワードになります

ユーザを削除する

  • vim user_delete.rb
dn = "uid=hawk,ou=People,#{BASE}"
ldap.delete(
  :dn => dn
)
pp ldap.get_operation_result

削除は dn を指定するだけなので簡単です
add のときと同じように結果が返ってくれば OK です

ユーザを取得する

  • vim user_search.rb
users = ldap.search(base: "ou=People,#{BASE}")
pp users

で以下の表に表示されれば OK です

[#<Net::LDAP::Entry:0x000000015a61e8
  @myhash=
   {:dn=>["ou=People,dc=example,dc=com"],
    :objectclass=>["organizationalUnit"],
    :ou=>["People"]}>,
 #<Net::LDAP::Entry:0x000000015a4258
  @myhash=
   {:dn=>["uid=hawk,ou=People,dc=example,dc=com"],
    :sn=>["hawk"],
    :cn=>["snowlog"],
    :objectclass=>["posixAccount", "inetOrgPerson"],
    :displayname=>["hawksnowlog"],
    :uid=>["hawk"],
    :uidnumber=>["1001"],
    :gidnumber=>["1000"],
    :homedirectory=>["/home/hawk"],
    :loginshell=>["/bin/bash"],
    :userpassword=>["{CRYPT}E1N/hpjy8pfc6"],
    :mail=>["hawk@hawksnowlog.cf"]}>]

特定のグループに所属するユーザを取得する

ということをしたいケースはよくあると思います
search の際に filter という機能を使うことで実現できました
自分は gidNumber を指定してその gidNumber を持つユーザを取得する方法で実現しましたが、他にもやり方はいろいろとあると思います

group = "group1"
groups = ldap.search(base: "cn=#{group},ou=Group,#{BASE}")
groups.each { |group|
  gidNumber = group['gidNumber'][0]
  group_name_filter = Net::LDAP::Filter.eq( "gidNumber", "#{gidNumber}" )
  users = ldap.search(base: "ou=People,#{BASE}", filter: group_name_filter, return_result: true)
  pp users
}

ちょっと複雑なように見えますが一旦 group 名から gidNumber を取得して、取得した gidNumber を元に再度 search をかけています
ポイントは取得した groups をループしたときのクラスが Net::BER::BerIdentifiedArray というクラスなのですが配列になっているので、先頭だけを取得して使っています
今回はグループに 1 つの gidNumber しか持たせていないので先頭を無条件で使っていますが、複数の gidNumber を持つグループの場合は少し考慮が必要になります

最後に

Ruby + net-ldap を使って ldap に基本的なユーザ操作をしてみました
特につまるところはなかった感じです
今回作成したユーザは posixAccount として作成してるので Linux などから SSH ログインすることも可能です

2017年8月20日日曜日

Ruby で ldap を使ってみる

概要

Ruby に net-ldap というライブラリがあり、これを使うと Ruby から ldap の操作ができます
今回は初期設定と接続まで行ってみました
ldap サーバの構築に関しては過去の記事を元にしています

環境

  • CentOS 7.3.1611
  • openldap 2.4.40
  • Ruby 2.3.1p112
  • net-ldap 0.16.0

事前準備

過去の ldap 構築記事で admin のパスワード設定、基本スキーマの登録、dc の登録、 ou の登録までは手動で済ませておいてください
もしかするとこれも Ruby から出来るかもですが今回は触れません

ライブラリインストール

  • bundle init
  • vim Gemfile
gem "net-ldap"
  • bundle install --path vendor/bundle

ldap に接続する

  • vim connect.rb
require 'net/ldap'
require 'pp'

PORT = 389
HOST = '192.168.100.5'
BASE = 'dc=example,dc=com'
AUTH = {
  :method => :simple,
  :username => "cn=Manager,#{BASE}",
  :password => 'password'
}

ldap = Net::LDAP.new(
  host: HOST,
  port: PORT,
  base: BASE,
  auth: AUTH
)

raise 'bind failed' unless ldap.bind

ldap は SSL を使っていないので 389 を指定します
HOST の部分は ldap サーバの IP または URL を入力してください

  • bundle exec ruby connect.rb

でエラーがでなければ OK です

グループを操作してみる

せっかくなのでもう少し操作してみます
まずグループを追加してみましょう
先ほどの接続ロジック後に以下を記載してください

  • group_add.rb
dn = "cn=group1,ou=Group,#{BASE}"
attr = {
  :cn => 'group1',
  :objectClass => 'posixGroup',
  :gidNumber => '1000'
}
ldap.add(
  :dn => dn,
  :attributes => attr
)
pp ldap.get_operation_result

これで #<OpenStruct extended_response=nil, code=0, error_message="", matched_dn="", message="Success"> と表示されれば OK です
作成されたグループ名は group1 になり、ou=Group に属しています

最後の get_operation_result は結構便利でエラーの場合、詳細が見れるのでこれで確認すると良いと思います

そしたらグループの一覧を取得してみましょう

  • group_search.rb
groups = ldap.search(base: "ou=Group,#{BASE}")
pp groups

で以下のように表示されれば OK です

[#<Net::LDAP::Entry:0x000000023d3ec8
  @myhash=
   {:dn=>["ou=Group,dc=example,dc=com"],
    :objectclass=>["organizationalUnit"],
    :ou=>["Group"]}>,
 #<Net::LDAP::Entry:0x000000023d3130
  @myhash=
   {:dn=>["cn=group1,ou=Group,dc=example,dc=com"],
    :cn=>["group1"],
    :objectclass=>["posixGroup"],
    :gidnumber=>["1000"]}>]

あるグループだけ取得したい場合は以下のようにします

groups = ldap.search(base: "ou=Group,#{BASE}")

最後に

Ruby から ldap が操作できる net-ldap を使ってみました
ldap 自体クセが強いのでまずそれに慣れることからかなと思います

登録系に関しては ldif のフォーマットをそのまま Ruby のハッシュに落とし込んで実行するだけという感じです
取得系に関しては面倒くさいコマンドを発行する必要がなく取得できるのはうれしいです
ただ、クエリというか検索の条件は ldap のフォーマットを使わないといけないのが残念なところです

他の ldap 用のライブラリもあるのでそれを使えばもう少し簡単に使えるやつもあるかもしれません

参考サイト

2017年8月19日土曜日

SpriteKit で物理エンジンを使ってみる

概要

前回 SKAction を使って SKNode を動かしてみました
SpriteKit には協力な物理エンジンが備わっておりデフォルトで簡単に使用することができます
今回は物理エンジン (SKPhysicsBody) を使って SKNode に重力を与えてみました

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

SKNode を配置する

GameScene.sks を編集しましょう
前回から以下のような感じで SKNode を配置しましょう
physicsbody1.png

block1 が転がって最終的に地面に着地します

GameScene.swift の編集

didMove を以下のように編集します

override func didMove(to view: SKView) {
    // 物理エンジンのグローバル設定
    self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
    // block1
    let block1 = self.childNode(withName: "block1") as? SKSpriteNode
    block1?.physicsBody = SKPhysicsBody(texture: (block1?.texture!)!, size:(block1?.size)!)
    // block2
    let block2 = self.childNode(withName: "block2") as? SKSpriteNode
    block2?.physicsBody = SKPhysicsBody(rectangleOf: (block2?.frame.size)!)
    block2?.physicsBody?.isDynamic = false
    // block3
    let block3 = self.childNode(withName: "block3") as? SKSpriteNode
    block3?.physicsBody = SKPhysicsBody(rectangleOf: (block3?.frame.size)!)
    block3?.physicsBody?.isDynamic = false
}

まず SKPhysicsBody.init で物理エンジンの枠を指定します
今回は画面を枠にすることでノードが画面外にいかないようにしています

そして各ノードを取得して physicsBody で物理エンジンを指定します
block1 は落下させるため SKPhysicsBody(texture:) を設定します
他の 2 つは固定させて置いておくだけなので SKPhysicsBody(rectangleOf:) を設定し isDynamic を false に設定します
こうすることで物理エンジンは適用するものの重力の影響は受けなくなります
こうすることで block1 が block2, 3 に衝突する感じにすることができます

動作確認

では実行して確認してみます
以下のように block1 が転がって最終的には地面に到着すれば OK です
physicsbody2.gif

最後に

SpriteKit の物理エンジンを使ってみました
ノードを取得してプロパティに SKPhysicsBody を設定するだけなので非常に簡単です

物理エンジンの当たり判定 (Body Type) や衝突後の動きの変更などいろいろなカスタマイズもできるので試してみると良いと思います
設定は .sks だけでできるものも多いです

参考サイト

2017年8月18日金曜日

SKAction を使って SKNode を動かしてみた

概要

前回 SpriteKit の簡単な使い方を紹介してみました
今回は SKAction を使って SKNode をいろいろと動かしてみたいと思います
環境は前回のものをそのまま利用します

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

SKNode の追加

GemeScene.sks を編集して更に SKSpriteNode を追加しましょう
追加したらインスペクターから以下を編集します
[] は入力している値です

  • Sprite -> Name -> [block2]
  • Sprite -> Texture -> [block2]
  • Sprite -> Position -> Z -> 1

こんな感じで block3 も追加しましょう
以下のような感じになれば OK です
skaction1.png

GameScene.swift でノードを動かす

では 3 つのノードを動かしましょう
GameScene.swift の didMove を以下のように編集します

override func didMove(to view: SKView) {
    // block1
    let block1 = self.childNode(withName: "block1") as? SKSpriteNode
    let action1 = SKAction.moveTo(x: self.frame.width, duration: 1.0)
    let action2 = SKAction.moveTo(x: -(self.frame.width), duration: 1.0)
    let forever1 = SKAction.repeatForever(SKAction.sequence([action1, action2]))
    block1?.run(forever1)
    // block2
    let block2 = self.childNode(withName: "block2") as? SKSpriteNode
    let action3 = SKAction.moveBy(x: 100, y: 100, duration: 1.0)
    block2?.run(action3)
    // block3
    let block3 = self.childNode(withName: "block3") as? SKSpriteNode
    let action4 = SKAction.rotate(toAngle: (CGFloat(45.0 / 180.0 * Double.pi)), duration: 1.0)
    block3?.run(action4)
}

簡単に説明します
まず 3 つのノードを childNode で取得しています
そのあとでアクションをそれぞれ作成します
今回は block1 からそれぞれ move(to:), moveBy, rotate(toAngle:) になります
move(to:) は指定した座標にノードを移動させます
moveBy は現在の座標から指定した座標分移動させます
そして rotate(toAngle:) は指定した角度分回転させることができる Action になります
あとはアクションを作成したらノードの run メソッドに指定すればアクションが実行されます
例えばアクションを無限ループさせたいことがあると思います
その場合は block1 に適用している repeatForever を使うことで実現することができます

動作確認

実行すると以下のように動作します
skaction2.gif

最後に

Swift3 で SKAction を使って SKSpriteNode をいろいろと動かしてみました
他にも SKAction のメソッドはたくさんあるので試してみると良いと思います
https://developer.apple.com/documentation/spritekit/skaction

参考サイト

2017年8月17日木曜日

Swift3 で SpriteKit に入門する

概要

SpriteKit は 2D のゲームを開発するためのフレームワークです
ゲームも作成したいなーと思い入門してみました
基本的なコーディング方法や操作方法について紹介します

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

プロジェクトを作成する

今回は SpriteKit を使うため Game を選択しましょう
sprite_kit1.png

最近 GameplayKit 新しいフレームワークも追加されたのですが、今回は使わないので「Integrate GameplayKit」のチェックは外しておきます
sprite_kit2.png

あとはプロジェクトを作成するディレクトリを選択すれば OK です

開発準備

今回は Xcode が作成してくれた .sks ファイルと .swift ファイルをそのまま使っていきます
他のサイトなどを見ると .sks ファイルを使わないで .swift だけで開発する手法が紹介されていますが個人的には UI とロジックは分けて書きたいので、そのまま使う方法でいきます

とりあえず今回は SKNode と呼ばれるオブジェクトを 1 つ配置してそれを swift ファイルから操作してみます
オブジェクト用のアイコンを用意しましょう
適当なアイコンで OK です
assets.atlas というディレクトリにアイコンを保存してください
spkit3.png

そしてこれをプロジェクトに追加します
フォルダごとドラッグアンドドロップすれば OK です
コピー時の選択は以下の通りです
spkit4.png

以下の追加できれば OK です
spkit5.png

SKNode を追加する

GameScene.sks を開きましょう
すると「helloLabel」というラベルがあるので一旦これを削除します (あっても問題ないです)

右ペインでインスペクターを選択し「Color Sprite」を選択し Scene 配下にドラッグアンドドロップして追加します
すると右側のサンプルにもオブジェクトが表示されるので適当に位置を調整しましょう
こんな感じになれば OK です
spkit7.png

そしたら右ペインのインスペクターで Texture の欄を選択し先ほど追加した assets.atlas 内にある画像のファイル名を選択しましょう
ちゃんと assets.atlas が追加されている場合プルダウンの中に選択肢が登場するはずです
こんな感じで SKSpriteNode が画像に変更されれば OK です
spkit8.png

さらに Name という欄があるのでここに追加したノードの名前を入力しましょう
ここで入力した名前は Swift 側で参照する際の識別子として使われるので他のノードと被らないようにかつわかりやすい名前を付与しましょう
今回は画像と同じ名前の「block1」としました
Name 欄を変更すると左ペインの Scene 側の表示も Name のものに変わると思います

そして更に Scene に Custom Class を設定しましょう
Scene のインスペクターに移動しここの Custom Class の欄にすでにある GameScene.swift を参照するために「GameScene」を入力します
spkit9.png

Swift 側で参照する

では追加したノードを Swift 側で参照しましょう

GameViewController.swift の編集

まず GameViewController に .sks に指定した Custom Class を指定します
GameViewController の viewDidLoad でシーンを呼び出す部分があります
そこの SKScene を GameScene に変更します

override func viewDidLoad() {
    super.viewDidLoad()

    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.sks'
        if let scene = GameScene(fileNamed: "GameScene") {
            // Set the scale mode to scale to fit the window
            scene.scaleMode = .aspectFill

            // Present the scene
            view.presentScene(scene)
        }

        view.ignoresSiblingOrder = true

        view.showsFPS = true
        view.showsNodeCount = true
    }
}

GameScene.swift の編集

GameScene.swift を開きましょう
デフォルトでいろいろと記載があると思いますが、今回はそのままで追記していきます
とりあえず今回は .sks 側に追加したノードを取得して位置を変更してみます
GameScene.swift の didMove メソッドの最後に以下を追加しましょう
既存のコードでいろいろ書いていますが、それは .sks にもあったラベルを表示する処理などが書かれています

  • GameScene.swift
let block1 = self.childNode(withName: "block1") as? SKSpriteNode
block1?.position = CGPoint(x: 0, y: 0)

ポイントは childNode です
これに .sks で指定したノードの名前を指定することで SKSpriteNode としてノードを取得することができます
あとはそのノードのフィールド (今回であれば position) を編集するだけです
今回は CGPoint で中央指定しています
SpriteKit の場合座標の中心が画面の中心になります
なので 0 以上のプラスを指定した場合ノードは右上へと移動しマイナスを指定すると左下へと移動します

動作確認

実行するとブロックが中央に移動していると思います
spkit10.png

最後に

2D ゲーム開発するために Swift + SpriteKit に入門してみました
2D ゲームを作成するためのフレームワークは他にもたくさんありますが、個人的に iOS アプリを出しているということもあり SpriteKit を選択しました

SpriteKit には他にも物理エンジンやノードを動かすための Action が豊富に用意されているので今後それらも紹介できればと思います

参考サイト

2017年8月16日水曜日

packer で AWS に AMI イメージを作成してみた

概要

packer を使って AWS 上に AMI のイメージを自動で作成してみました
AWS 側の VPC 設定などではまりポイントがあったので合わせて紹介します

環境

  • CentOS 7.3.1611
  • packer 1.0.3

packer インストール

https://www.packer.io/downloads.html から Linux 64-bit 版をダウンロードします

  • unzip packer_1.0.3_linux_amd64.zip
  • mv packer /usr/local/bin/

PATH が通っているところに適当に移動してください

  • packer -v
1.0.3

でインストール完了です

テンプレートファイルの作成

作成するイメーを定義するテンプレートファイルを作成します

  • example.json
{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [{
    "type": "amazon-ebs",
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "ap-southeast-2",
    "source_ami_filter": {
      "filters": {
        "virtualization-type": "hvm",
        "name": "*ubuntu-xenial-16.04-amd64-server-*",
        "root-device-type": "ebs"
      },
      "owners": ["099720109477"],
      "most_recent": true
    },
    "instance_type": "t2.micro",
    "ssh_username": "ubuntu",
    "ami_name": "packer-example {{timestamp}}",
    "subnet_id": "subnet-4b31382f",
    "security_group_id": "sg-c33edba5"
  }]
}

aws_access_key と aws_secret_key の部分は各自のものを入力してください
あとは subnet_id と security_group_id の部分も各自で作成したものに変更してください

  • packer validate example.json

でエラーがでなければビルドを実行できます

ビルドする

ビルドが成功すると最終的には AMI のイメージが作成されて完了になります
AWS 側の設定ミスで結構エラーが出たので以下にまとめています
packer 側というよりか AWS 側の設定のほうが自分は大変でした

  • packer build example.json
amazon-ebs output will be in this color.

==> amazon-ebs: Prevalidating AMI Name...
    amazon-ebs: Found Image ID: ami-546d7437
==> amazon-ebs: Creating temporary keypair: packer_598a4b64-8997-7032-bcee-a3113fdde266
==> amazon-ebs: Launching a source AWS instance...
    amazon-ebs: Instance ID: i-0eeb422db2057ff58
==> amazon-ebs: Waiting for instance (i-0eeb422db2057ff58) to become ready...
==> amazon-ebs: Adding tags to source instance
    amazon-ebs: Adding tag: "Name": "Packer Builder"
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Stopping the source instance...
    amazon-ebs: Stopping instance, attempt 1
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating the AMI: packer-example 1502235491
    amazon-ebs: AMI: ami-79e7fe1a
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Cleaning up any extra volumes...
==> amazon-ebs: No volumes to clean up, skipping
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:

ap-southeast-2: ami-79e7fe1a

エラー集

  • Error launching source instance: VPCResourceNotSpecified: The specified instance type can only be used in a VPC. A subnet ID or ne
    twork interface ID is required to carry out the request.

テンプレート内で subnet_id パラメータを使って vpc を指定する必要があります

  • InvalidSubnetID.NotFound: The subnet ID 'vpc-a08048c4' does not exist

指定したリージョン内に vpc -> subnet が存在しません

  • Error launching source instance: InvalidParameter: Security group sg-0d6ccd6a and subnet subnet-d3fe41a5 belong to different networks.

指定したセキュリティグループに指定した vpc が所属していません

  • Timeout waiting for SSH.

指定したサブネットで「自動割り当てパブリック IP」を有効にする必要があります
指定したサブネットの VPC で「DNS 解決」と「DNS ホスト名」を有効にする必要があります
指定したサブネットの VPC にインターネットゲートウェイをアタッチする必要があります
指定したサブネットの VPC にルートテーブルを新規で作成する必要があります
指定したサブネットの VPC に作成したルートテーブルのルートにインターネットゲートウェイを指定する必要があります (送信先: 0.0.0.0/0, ターゲット: 作成したインターネットゲートウェイ)
指定したサブネットの VPC にサブネット関連付けで新規に作成したサブネットを割り当てる必要があります

タイムアウトのエラーに関しては VPC 内に作成されたインスタンスがインターネットに接続できないためにエラーとなるケースがほとんどです
なので、対象の VPC がインターネットに接続できるようになれば OK です

プロビジョニングしてみる

事前に OS 上に必要なパッケージや設定を行うことができます
先程の example.json にプロビジョニング用の設定を追加してみましょう

vim example_provisioning.json

{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [{
    "type": "amazon-ebs",
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "ap-southeast-2",
    "source_ami_filter": {
      "filters": {
      "virtualization-type": "hvm",
      "name": "*ubuntu-xenial-16.04-amd64-server-*",
      "root-device-type": "ebs"
      },
      "owners": ["099720109477"],
      "most_recent": true
    },
    "instance_type": "t2.micro",
    "ssh_username": "ubuntu",
    "ami_name": "packer-example {{timestamp}}",
    "subnet_id": "subnet-4b31382f",
    "security_group_id": "sg-c33edba5"
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sleep 30",
      "sudo apt-get update",
      "sudo apt-get install -y redis-server"
    ]
  }]
}

variables, builders の部分はそのままで、新たに provisioners の項目を追加しています
今回は shell を使って apt コマンドで redis-server をインストールしているだけです

これもビルドしてみましょう
ビルド中に表示されるデバッグ情報に apt install のログも表示されると思います
AMI ができたあとにその AMI から EC2 インスタンスを作成して SSH ログインしてみます
すると redis がすでにインストールされている状態でインスタンスが作成されるのがわかると思います
一点注意が必要なのが SSH ユーザはテンプレートの ssh_username で定義した ubuntu になります

  • ssh -i "your_key.pem" ubuntu@ec2-xx-xxx-xxx-xxx.ap-southeast-2.compute.amazonaws.com

その他

あとは Getting Started に Parallel Build のやり方や Vagrant Box の作成方法や Atlas と連携する方法が紹介されています
興味があれば触ってみると良いかなと思います

最後に

packer と AWS を連携して AMI を自動生成する方法を紹介しました
今回の場合はどちらかというと AWS 側の設定でハマった部分が多かったです
packer 自体は非常に簡単に使えるので便利かなと思います

次回は VMware 上で動作するベースイメージを作成してみたいと思います

参考サイト

2017年8月15日火曜日

EC2 System Manager をちょろっと試してみた

概要

EC2 Sysytem Manager は EC2 に専用のエージェントをインストールすることで外部からインスタンスの操作をできるようにする機能です
今回はエージェントのインストール方法と機能の一つである「コマンド実行」という機能を試してみました

環境

  • ssm-agent 2.0.913.0-1

ロール作成

事前に「AmazonEC2RoleforSSM」ポリシーがアタッチされているロールを作成しておきます

EC2 インスタンス作成

EC2 インスタンスを作成します
作成するときのロールの設定先ほど作成したロールをインスタンスに割り当てるようにしてください

OS は Amazon Linux AMI 2017.03.1 (HVM), SSD Volume Type を選択します
VPC に入れる場合はインターネットからアクセスできるように DNS やゲートウェイ、サブネットの設定を行っておいてください
念のためインスタンスに SSH して動作を確認するためです

ssm-agent のインストール

System Manager を使うには専用のエージェントをインストールする必要があります
yum コマンドで簡単にインストール可能です

  • ssh -i your_key.pem ec2-user@ec2-xx-xxx-xxx-xx.ap-southeast-2.compute.amazonaws.com
  • sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm

インストールが完了すると /usr/bin/amazon-ssm-agent というプロセスが起動しているのが確認できます

コマンドを実行する

エージェントのインストールができたらコンソールからシェルコマンドを実行してみましょう
今回はマネージメントコンソールから実行します

EC2 -> SYSTEM MANAGER SERVICES -> コマンドの実行を選択します
AWS-RunShellScript を選択しコマンドを実行するインスタンス選択します
タグもでも選択できるのコマンドを複数のインスタンスに一斉に実行することも可能なようです
そして、Commands の欄でシェルスクリプトを記載します
今回は以下とします

echo "hoge" > hoge.txt

これでコマンドを実行するとルート直下に /hoge.txt というファイルが作成されているのが確認できると思います

aws CLI をインストールしている場合は CLI からでも実行することができます

aws ssm send-command --document-name "AWS-RunShellScript" --instance-ids "i-0a61f7fc18311b89a" --parameters '{"commands":["echo \"hoge\" > hoge.txt"]}' --timeout-seconds 600 --region ap-southeast-2

最後に 

簡単ですが、EC2 System Manager を試してみました
エージェントをインストールすればコンソールや CLI からインスタンスを操作できるのでわざわざ踏み台サーバや SSH ログインしないでもオペレーションできるようになるという機能かなと思います

他にも実行できるコマンドはたくさんあるので興味があれば試してみると良いかと思います

ちなみに ssm-agent は Github で公開されています
https://github.com/aws/amazon-ssm-agent

あと AWS に Opsworks という機能がありこれでも同じようにコマンド実行することができます
おそらく Opsworks の機能をインスパイアして EC2 で更に使いやすくした機能という位置づけなんじゃないかなと思います

参考サイト

2017年8月14日月曜日

kapacitor を使って Slack に通知を送ってみた

概要

前回 までに InfluxDB, telegraf, Chronograf と試してきました
今回はアラートを送るための kapacitor を使って見ました
デフォルトで Slack に通知できる機能があったのでアラート情報を Slack に送るところまでやってみました

環境

  • macOS X 10.12.6
  • influxdb 1.3.1
  • telegraf 1.3.5
  • chronograf 1.3.3.4
  • kapacitor 1.3.1

インストール

  • brew install kapacitor
  • kapacitor version
Kapacitor v1.3.1

Slack 情報の設定

事前に Incomming Webhooks を作成しておきます

  • vim /usr/local/etc/kapacitor.conf
[slack]
  enabled = true
  url = "https://hooks.slack.com/services/Txxxxxxxx/Bxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx"
  channel = "#general"
  global = false
  state-changes-only = false

[slack] の部分だけ抜粋しています
また不要なコメントも削除しています

enabled は true にしましょう
url, channel に関しては取得した Incomming Webhook の URL を指定し channel には通知したい Slack のチャネルを指定してください

global は true に設定するとすべてのチャネルに通知が行くようで、チャネルを制御したい場合は後述する TICKscript 内で制御するようです

state-changes-only はアラートの状態が変化したときにだけ通知をするかしないかのフラグです
今回はテストのなのでアラートの条件に合うたびに通知するため false に設定します

起動

  • kapacitord -config /usr/local/etc/kapacitor.conf

設定ファイルを指定して起動します

TICKscript の作成

プロセスを起動したらアラート情報を定義し追加していきます
kapacitor は DSL を使ってアラート情報を定義します
DSL なので結構覚えるのも大変なので今回は CPU を監視する簡単なサンプルを作成しました
また、公式でいくつかサンプルも用意しているみたいなので独自の作成する tick ファイルを作成する際に参考にしてください

  • vim cpu_alert_slack.tick
stream
    // Select just the cpu measurement from our example database.
    |from()
        .measurement('cpu')
        .where(lambda: "cpu" == 'cpu-total')
    |alert()
        .id('{{ index .Tags "host"}}/usage_idel')
        .crit(lambda: "usage_idle" < 99)
        // Whenever we get an alert write it to a file.
        .slack()

かなり簡単な tick ファイルにしました
telegraf が自動で作成したcpu という measurement に対してアラートをかけます
アラート情報として取得するレコードを where で絞込します
今回は cpu というフィールドがありそれの cpu-total というレコードだけ取得します
ここでポイントですが lambda 式の右辺の 'cpu-total' はシングルクオートでなければなりません
どうやら内部的にダブルクオートとシングルクオートは明確な違いがあり文字列のフィールドを比較する場合には値側はシングルクオートでなければなりませんでした

あとは alert() ノードで実際のアラート情報を定義しています
id はアラートを通知した際にどのホストなのかなど識別できる情報を設定します
今回は .Tags という slice に送られてくる host の情報を設定しています
アラートの条件は usage_idle が 99% 以下のときに Slack に通知するので (ほぼ) 確実にアラートが発砲するようにしました
あくまでもテストとして設定しているので実際にはもっと低い数値を設定しましょう

そして最後に .slack() を呼ぶことで kapacitor.conf に指定した Incomming Webhook の URL に対して通知を行います

アラートを定義する

作成した TICKscript を追加します
追加するには kapacitor define コマンドを使います

  • kapacitor define cpu_alert -type stream -dbrp telegraf.default -tick ./cpu_alert_stream.tick

名前、タイプ、使用する DB と .tick ファイルを指定しましょう
定義が追加できたら確認してみましょう

  • kapacitor list tasks
ID        Type      Status    Executing Databases and Retention Policies
cpu_alert stream    disabled  false     ["telegraf"."default"]

動作確認

アラートの定義ははじめは無効になっています
いきなり有効にしてもいいのですが、kapacitor ではアラートをテストする record という機能があります

  • kapacitor record stream -task cpu_alert -duration 20s

これで InfluxDB に入ってくるデータを 20 秒間監視することができます
監視したデータには ID が振られます
list recordings コマンドを使って監視したデータを確認することができます

  • rid=cd158f21-02e6-405c-8527-261ae6f26153
  • kapacitor list recordings $rid

そしてこの ID を指定することで監視したデータを再生することができます

  • kapacitor replay -recording $rid -task cpu_alert

こうすることで監視したデータを使ってアラートのテストを行うことができます
実際に監視したデータ内にアラートに引っかかる情報があると Slack に通知されます

もし問題なく通知が来るようであれば監視を有効にすれば OK です

  • kapacitor enable cpu_alert

.tick ファイルなどに構文エラーがある場合 kapacitor のログファイルにエラーログなども吐いてくれるので、実際にアラートを追加するときはちゃんと record 機能を使ったほうが良いかと思います

最後に

kapacitor を使って InfluxDB 内にあるデータを監視し Slack に通知するところまでやってみました
DSL で監視情報を定義する必要があるので、少し学習コストが必要になりそうです

大変だったのは本当にちゃんと監視できているか確認することでしたが、record の機能に気づいてからは結構簡単にできました

Tips

ログの位置は kapacitor.conf 内で定義されています
Mac のデフォルトのログ保存場所は以下でした

  • tail -f /usr/local/var/log/kapacitor.log

またログレベルが最初は INFO になっているので debug に変更したい場合はコマンドで可能です

  • kapacitor level debug

アラートを削除する場合は delete コマンドを使用すれば OK です

  • kapacitor delete cpu_alert

参考サイト

2017年8月13日日曜日

Chronograf を使ってみる

概要

前回 までに InfluxDB と telegraf を試しました
データがいろいろと入ってくると UI で見たくなるのは当然です
InfluxDB には Chronograf という専用の UI がありこれを使うことで InfluxDB に貯まったデータを簡単に閲覧することができます
今回はインストールと簡単な操作を試してみました

環境

  • macOS X 10.12.6
  • influxdb 1.3.1
  • telegraf 1.3.5
  • chronograf 1.3.3.4

インストール

  • brew install chronograf
  • chronograf --version
2017/08/07 10:09:03 Chronograf 1.3.3.4

起動

  • chronograf

http://localhost:8888 で起動します
ブラウザでアクセスしてみましょう

触ってみる

まずアクセスするとソースを作成する画面になります
最低限必要な情報は「Connection String」と「Name」になります
Connection String は InfluxDB の URL になります
アドレスやポートがデフォルトとは異なる場合は別のものを入力してください
Name は何でも OK です、今回は「Influx 1」というソース名にしました
chronograf1.png

ソースを作成すると左メニューに

  • Status・・・アラートの状態を確認することができます、他にニュースフィードと Getting Started へのリンクがあります
  • Host List・・・telegraf がインストールされているホストの一覧が表示されます、ホストを選択するとメトリックの情報を確認することができます
  • Data Explorer・・・自分でカスタムしたクエリを発行することができます、すぐにその場でグラフがすることも可能です
  • Dashboards・・・様々なグラフを 1 つのビューで確認することができるダッシュボードを作成することができます
  • Alert History・・・アラートの履歴を確認できます
  • Admin・・・存在するデータベースやユーザの一覧を確認することができます
  • Configuration・・・作成したソースの一覧を確認することができます

があります

例えば前回 telegraf をインストールしたホストのメトリックを確認するには Host List からホストを選択することで確認できます
chronograf2.png

エージェントをインストールして Chronograf を起動するだけでこれだけのメトリック情報を確認することができます

ただ、ここにはすべての情報はありません
なので別の情報を確認したい場合には Data Explorer を使って自分でカスタムクエリを使ってグラフを作成します
chronograf3.png

クエリを作成するのが面倒くさいという場合であれば「Query Templates」からサンプルクエリを取得することができます
また、measurements の一覧からフィールドをポチポチするだけでもクエリを自動で作成してくれるので簡単です

最後に

Chronograf をインストールして使ってみました
基本的には InfluxDB に貯まったデータに対してクエリを発行することで簡単にグラフを作成して可視化することができるツールです

Kibana や Grafana と比較すると操作感はかなりシンプルかなと思います
ただ、執筆時点では Chronograf にはグラフの種類が少し不足している感があります
Geolocation を使った地図グラフや棒グラフ、円グラフなどの基本的なグラフもないようです
なので、そういったグラフを使いたい場合には Kibana や Grafana を使わざるを得ないのが現状かなと思います

Chronograf は InfluxDB などと比べて最近開発が始まったプロダクトなので、まだまだ開発も進むと思います
なので今後そういったグラフなどが追加されることは十分にありえると思います

参考サイト

2017年8月12日土曜日

telegraf を使ってみる

概要

前回 Mac 上に InfluxDB をインストールして簡単な SQL 操作をしてみました
大抵こういったツールはログやメトリックと連携してデータを蓄積していくのですが調べてみたところ InfluxDB 専用のエージェントツールで telegraf というものがありました
今回は telegraf のインストールと簡単な設定を行い実際に InfluxDB にデータを投入するところまで試してみました
また、今回も前回に引き続き Mac 上で試していきます

環境

  • macOS X 10.12.6
  • influxdb 1.3.1
  • telegraf 1.3.5

インストール

  • brew install telegraf
  • telegraf --version
Telegraf v1.3.5

設定

  • telegraf config > telegraf.conf
  • vim telegraf.con

で 94行目あたりにある [[outputs.influxdb]] の項目に urls = ["http://localhost:8086"] という設定があるので InfluxDB の URL を設定します
InfluxDB もローカルで動作していて特に設定を変更していない場合は conf ファイルは何もしないで OK です

起動

  • telegraf --config telegraf.conf

で起動します
起動すると 10秒おきに InfluxDB にメトリック情報が格納されていきます

うまくデータが入っていない場合は influxd プロセスが起動しているか確認してください

動作確認

influx コマンドを使って SQL を発行してデータを確認してみましょう

  • influx

ここでインタラクティブモードになります

  • show databases

telegraf という measurement が作成されていることを確認します

  • use telegraf
  • show measurements

で measurement (テーブル) の一覧を確認すると以下のメトリック情報が送信されていることが確認できます

name: measurements
name
----
cpu
disk
diskio
mem
processes
swap
system
  • precision rfc3339
  • select * from cpu
time                 cpu       host         usage_guest usage_guest_nice usage_idle        usage_iowait usage_irq usage_nice usage_softirq usage_steal usage_system       usage_user
----                 ---       ----         ----------- ---------------- ----------        ------------ --------- ---------- ------------- ----------- ------------       ----------
2017-08-07T00:24:30Z cpu-total host1.local 0           0                85.7              0            0         0          0             0           3.65               10.65
2017-08-07T00:24:30Z cpu3      host1.local 0           0                94.5              0            0         0          0             0           2                  3.5
2017-08-07T00:24:30Z cpu1      host1.local 0           0                94.6              0            0         0          0             0           1.7                3.7
2017-08-07T00:24:30Z cpu0      host1.local 0           0                74.2              0            0         0          0             0           6.7                19.1
2017-08-07T00:24:30Z cpu2      host1.local 0           0                79.5              0            0         0          0             0           4.2                16.3

自分の Mac は 4 コアの CPU を使っているのですが各 CPU ごとにデータを送信してくれるようです
あとは全コアの平均値も出してくれます

こんな感じで他にもメモリやディスク使用量などの情報も格納してくれます

最後に

InfluxDB 専用のエージェントツール telegraf を使ってみました
ログパーサも使えるためログから情報を送信することも可能なようです

td-agent にも InfluxDB 用のプラグインがあるようでログなどはそっちで代用することもできそうです

余裕があったらカスタムメトリックの取得方法やログのパースなどもやってみたいと思います

参考サイト

2017年8月11日金曜日

macOS X で InfluxDB 1.3 に入門してみた

概要

InfluxDB は時系列データを格納し専用の SQL でデータを取得したり専用の UI でグラフを表示したりすることができるツールです
ElasticSearch みたいなツールです
golang でかかれており Github でオープンソースとして公開されています
今回は macOS 上に influxdb をインストールし簡単なデータの登録や取得を行ってみました

環境

  • macOS X 10.12.6
  • influxdb 1.3.1

インストール

Mac の場合 Homebrew で簡単にインストールすることができます

  • brew install influxdb

インストールできたら以下のコマンドでバージョンが確認できれば OK です

  • influx -version
InfluxDB shell version: v1.3.1

influxQL で遊んでみる

influxdb には influxQL と呼ばれる独自のクエリ言語を発行することでデータを操作することができます

まずは influxQL が発行できるモードに変更します

  • influx -precision rfc3339

「-precision rfc3339」オプションを指定することでタイムスタンプのフォーマットを「YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ」にすることができます

データベース作成

まずはデータベースを作成しましょう
この辺は MySQL などと同じ操作かなと思います

  • CREATE DATABASE mydb

コマンドの部分は大文字でも小文字でもどっちでも OK です
データベースが作成されたか確認して問題なければデータベースを使用します

  • SHOW databases
  • USE mydb

データ投入

そしたら早速データを追加してみます

  • INSERT cpu,host=server1,region=us_west value=0.64

ここが少しクセのあるコマンドになります
フォーマットは

INSERT measurement,tag_key1=tag_value1,tag_key2=tag_value2 value=n

になります
measurement は所謂テーブルになります
そしてその次にある tag_key と tag_value はカラムとそのカラムに設定する値になります
意味合いとしてタグになるので基本的には文字列情報を設定します

そして 1 つスペースを開けて value=n で数値情報を設定します
この部分は必ず数値である必要があります
今回は value は 1 つしか指定していませんが、value もカンマ区切りで複数していすることができます

  • INSERT temperature,machine=unit42,type=assembly external=25,internal=37

結構フォーマットがシビアで measurement のあとのカンマの後にスペースなどは含められません
タグ情報が複数ある場合のカンマの後にもスペースを含めることはできません

データ取得

データが格納できたら取得してみましょう

  • SELECT * from cpu

ですべてのタグ情報と値の情報が取得できます

name: cpu
time                           host    region  value
----                           ----    ------  -----
2017-08-03T06:44:04.126616931Z server1 us_west 0.64
2017-08-03T07:22:28.122064267Z server2 us_west 0.99

もちろん表示するタグ情報を絞り込むこともできます

  • SELECT region, value FROM cpu
name: cpu
time                           region  value
----                           ------  -----
2017-08-03T06:44:04.126616931Z us_west 0.64
2017-08-03T07:22:28.122064267Z us_west 0.99

データの削除と更新

ついでにデータの削除もやってみましょう

  • DELETE FROM cpu WHERE host='server1'

という感じで消せます
更新 (UPDATE) に関してはコマンドがないようで INSERT を使うことで上書きすることができるようです

  • INSERT cpu,host=server2,region=us_west value=0.99 1501746397569246614

フォーマットは普通の INSERT コマンドの最後にスペースで区切って time を指定します
time の指定方法はデフォルトのタイムスタンプ方式なので「-precision rfc3339」を指定しないで influx コマンドを使うことで確認できます
もしくはインタラクティブモードで

  • precision ns

とすることでタイムスタンプのフォーマットをデフォルトに変更することができるのでこれで SELECT し直しても良いと思います
再度フォーマットを rfc3339 にしたい場合は

  • precision rfc3339

へ戻すことができます

クエリ中で正規表現を使う

またクエリ中に golang の正規表現を使用することができます

  • SELECT * FROM /.*/

上記の場合すべての measurement に対して SELECT 文を発行することができます

管理 UI について

どうやら InfluxDB 1.3 からはデフォルトで付属していた管理 UI がなくなったようです

代わりに Chronograf という InfluxDB 専用の UI プロジェクトができたのでそれを別途インストールする必要があります

最後に

Mac 上で InfluxDB を試してみました
ダウンロードページを見るとわかりますが、さまざまなプラットフォームで簡単にインストールすることができます
Docker 環境がある人は Docker コンテナとしても動作させられるので更に簡単に動かせると思います

次回は Chronograf と連携して UI から InfluxDB の情報を見てみたいと思います

参考サイト

2017年8月10日木曜日

Ubuntu16.04 で zfs を使ってみた

概要

Ubuntu16.04 で zfs を試してみました
セットアップの方法から zpool を使った簡単な操作方法まで紹介します

環境

  • Ubuntu 16.04 (kernel 4.4.0-89-generic)
  • zfsutils-linux 0.6.5.6

事前準備

100 GB の HDD を別途追加しておきます
fdisk -l で /dev/sdb が見えている状態を想定しています

有効化

Ubuntu 16.04 では zfs のモジュールがすでにインストールはされていますが有効化されていません
なので、有効化するだけで使えるようになります

  • modprobe zfs

で有効化できます

  • lsmod | grep zfs
  • find /lib -name “zfs.ko”

でモジュールが有効化されていることを確認します

  • echo “zfs” >> /etc/modules

で再起動時にも zfs モジュールが有効になるようにしておきます
zfs を操作するには zpool というコマンドが必要になるのでインストールします

  • apt update && apt install zfsutils-linux

これで有効化は完了です

パーティション作成

追加した HDD のパーティションを行います

  • fdisk /dev/sdb

で全領域を使って新規パーティションを作成しましょう (/dev/sdb1)

zfs プールの作成

zfs は複数のディスク領域をプールとして登録することで操作することができます
今回は 1 HDD のみですが複数パーティションを作成すれば複数パーティションを 1 つのプールとして登録することもできます
今回は pool1 というプールを作成します

  • zpool create pool1 /dev/sdb1
  • zpool status
NAME    SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
pool1  99.5G    50K  99.5G         -     0%     0%  1.00x  ONLINE  -
root@ubuntu:~# zpool status
  pool: pool1
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        pool1       ONLINE       0     0     0
          sdb1      ONLINE       0     0     0

errors: No known data errors  
  • zpool list
NAME    SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
pool1  99.5G  62.5K  99.5G         -     0%     0%  1.00x  ONLINE  -
  • zpool export pool1
  • zpool import pool1
  • df -h

一度 export することでプールをマウントできる状態にします
そして import することでマウントすることができます
再度 export するとアンマウントすることができます
export したプールは再度 import 可能な状態になります
再度 import 可能なプールは list や status コマンドでは表示されません
再度 import 可能なプールを表示する場合は import コマンドを引数なしで使用します

ちなみに zfs の場合一度 import してしまえば再起動しても自動で import してくれます
/etc/fstab などにマウント情報を記載しなくても OK です

zfs プールを削除する

  • zpool destroy pool1

で可能です
マウントした状態でも実行できます (たぶんデバイスが busy の状態だとできないと思います)

destroy 後にやっぱりもう一度使いたいという場合は「-D」オプションを指定して import します

  • zpool import -D
  • zpool import -D pool1

ディスク上にあるデータも元通り復元されます
destroy 後に再度 create するとディスクのデータもクリアされて再度プールが作成されます

ディスクを追加してみる

サーバを停止して /dev/sdc に更に 100GB のディスクを追加します
このディスクをすでに作成済みに pool1 に追加してプール領域を拡張してみます

  • fdisk /dev/sdc
  • zpool add pool1 /dev/sdc1

これだけです
これで容量を確認すると先程の 100GB が 200GB に拡張されているのがわかると思います

  • df -h /pool1
Filesystem      Size  Used Avail Use% Mounted on
pool1           193G     0  193G   0% /pool1

逆に領域を削除したい場合は remove を使います
が、100GB 以上すでに使用している場合や書き込み中の場合にどうなるかは検証していないので不明です
(たぶん busy でできないか、できてもデータが破損しそうな気がします)

最後に

Ubuntu16.04 で zfs を試してみました
インストール等も不要で簡単に使えました

同じようなことをするのに LVM という仕組みがありますが
LVM よりかは単純な操作で使えるような印象です

あとは性能比較などもしてみないと何とも言えないところはあります

Tips

プールの情報を取得する

  • zpool get all

参考サイト