2017年8月30日水曜日

DeepSecurity の SOAP API を ruby で嗜む

概要

DeepSecurityManager には REST と SOAP の API があります
現状だと REST よりも SOAP の方ができることが多いです
公式で Java のライブラリが提供されているのですが Java でコールする気にはなれなかったので Ruby からコールしてみました
SOAP 用の Ruby ライブラリは Savon というライブラリを利用しています

環境

  • Ubuntu 16.04
  • ruby 2.3.1p112
  • gem 2.5.1
  • Savon 2.11.2
  • DeepSecuritManager 10.0

事前準備

事前に DeepSecurityManager で API を有効にする必要があります

Administration -> System Settings -> Advanced

で「SOAP Web Service API」と「Status Monitoring API」を Enabled にします
Status Monitoring API は REST API で今回は使用しないので Enabled にしないでも大丈夫です

ライブラリインストール

  • apt install zlib1g-dev
  • bundle init
  • vim Gemfile
gem "savon"
  • bundle install

で vendor 配下にインストールされました

サンプル

まずは DeepSecurityManager にアクセスして wsdl の情報を表示してみます

  • vim show_wsdl.rb
require 'savon'

client = Savon.client(
        :wsdl => 'https://192.168.100.8:4119/webservice/Manager?WSDL',
        :ssl_verify_mode => :none
        )
puts client.operations
  • bundle exec ruby show_wsdl.rb

wsdl は DeepSecurityManager 自体が配信しているのでそれを指定します
今回は SSL 通信ではないので :ssl_verify_mode:none に設定します

これで実行すると SOAP API のオペレーションの一覧がずらーっと表示されると思います
190 ほどあると思います

認証してみる

ユーザ名とパスワードで認証してみましょう

  • vim auth.rb
require 'savon'

client = Savon.client(
        :wsdl => 'https://192.168.100.8:4119/webservice/Manager?WSDL',
        :ssl_verify_mode => :none
        )
response = client.call(:authenticate, message: { username: 'username', password: 'password'})
puts response.body[:authenticate_response][:authenticate_return]
  • bundle exec ruby auth.rb

で他の SOAP API をコールするためのセッショントークンを取得することができます

ポリシーの一覧を取得する

ポリシーの一覧を取得してみましょう

  • vim get_policies.rb
require 'savon'

client = Savon.client(
        :wsdl => 'https://192.168.100.8:4119/webservice/Manager?WSDL',
        :ssl_verify_mode => :none
        )
response = client.call(:authenticate, message: { username: 'username', password: 'password'})
sid = response.body[:authenticate_response][:authenticate_return]

response = client.call(:security_profile_retrieve_all, message: { sID: sid })
ret = response.body
ret[:security_profile_retrieve_all_response][:security_profile_retrieve_all_return].each { |r|
  puts r[:id]
  puts r[:name]
  puts r[:description]
}
  • bundle exec ruby get_policies.rb

でポリシーの一覧から名前と説明を取得することができます

最後に

DeepSecurity の SOAP API を Ruby からコールしてみました
今回は Savon というライブラリを使用しましたが Ruby には他にも SOAP API 用のライブラリがあります
はじめは soap4r というコードジェネレータにもなるツールを使っていたのですがいろいろとエラーが出たのでやめました
最終的には Savon に落ち着きました
現在もメンテナンスはされていそうです

DeepSecurity の SOAP API はかなりたくさんあるのですが、WSDL しか情報がないのでどの API がどの操作に対応しているのか探すのが結構たいへんです
オペレーション名から何ができるか推測して、必要なパラメータを WSDL から生成して実際にコールするしか現状ではなさそうです

2017年8月29日火曜日

QuickTime で iPhone の動画を録画する

概要

LadioCast + soundflower を使った王道です
よく音が出なかったり録画の手順を忘れるので備忘録に残しておきます

環境

  • macOS X 10.12.6
  • QuickTime Player 10.4 (894.12)
  • LadioCast 000012003 (1649)
  • soundflower 2.0b2

手順

  1. iPhone を Mac に接続
  2. マナーモードを解除する
  3. LadioCast 起動
  4. LadioCast の「出力メイン」が内蔵出力になっているかつ音量が Max になっていることを確認
  5. LadioCast の「入力メイン」が Soundflower (2ch) になっていることを確認
  6. Mac のスピーカーの出力を内蔵スピーカーから Soundflower (2ch) に変更する
  7. QuickTime Player 起動
  8. ファイル -> 新規ムービー収録
  9. カメラ -> iPhone を選択
  10. マイク -> iPhone を選択
  11. QuickTime Player の音量を調整する
  12. 録画ボタンを押して録画開始

です
いつも忘れるので備忘録を残しておきます
事前に LadioCast (App Store から) と Soundflower (Github から) はインストールしておいてください

2017年8月28日月曜日

SpriteKit で iPad の画面表示に対応する方法

概要

タイトルの通りです
.aspeftFill を使っていたのですが、それで iPad で表示すると上下に黒の帯が表示されてしまいました
どうやらこの状態で審査に出すとリジェクトされるらしいので iPad で黒帯が表示されないように対応してみました

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

対応コード

シーンに遷移する箇所を以下のように修正しました

if let view = self.view as! SKView? {
    if let scene = TitleScene(fileNamed: "TitleScene") {
        if (UIDevice.current.model.range(of: "iPad") != nil) {
            scene.scaleMode = .fill
        } else {
            scene.scaleMode = .aspectFill
        }
        view.presentScene(scene)
    }
    view.ignoresSiblingOrder = true
    view.showsFPS = true
    view.showsNodeCount = true
}

ポイントは iPad だったときに scaleMode を .fill にするところです
それ以外はこれまで通り .aspectFill を使います

こうすることで iPad 時にも画面いっぱいに SKScene を表示することができます

最後に

iPad 時に画面に黒帯が表示されてしまう状況に対応してみました
まだ審査に出してはいないのですが、おそらくこれで iPad 対応として審査が通ると思います

2017年8月26日土曜日

個人的 App Previews とスクリーンショットの作成方法

概要

アプリを審査に出す時に必要になる動画 (App Previews) とスクリーンショットを取得するコツを紹介します
自分は実機を使わずにすべてシミュレータを使っています

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)
  • iMovie 10.1.6

流れ

  1. iPhone 7 Plus のシミュレータでスクリーンショット取得
  2. iPhone 7 Plus のシミュレータで動画撮影
  3. iPad Pro (12.9-inch) のシミュレータでスクリーンショット取得
  4. iPad Pro (12.9-inch) のシミュレータで動画撮影
  5. 2, 4 で撮影した動画を iMovie の App Previews モードで編集 (音を何処かに必ず入れること) -> 書き出し

で、できた成果物を iTunes Connect にアップロードします
動画撮影はシミュレータを使っています
他のデバイスサイズに関しては iTunes Connect の設定で使いまわすことができるので使いまわします
iMovie で動画を書き出す際には 30 秒以内の動画になっていることを確認してください

最後に

App Previews とスクリーンショットを取得するコツを紹介しました
現時点ではこの方法でやることでアプリに必要な情報を作成することができると思います

2017年8月25日金曜日

Xcode のシミュレータで動画を撮影する方法

概要

App Previews 用の動画を撮影するのに iPhone6+ 以上の解像度を持つ端末が必要になることは多々あります
自分は iPhone6 しか持っていなくどうやって 1920 x 1080 の動画を撮影しようか悩んでいました
そんなときシミュレータで動画を撮影する方法を知ったので紹介します

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

撮影方法

  1. Xcode から iPhone7 (or iPhone6+) シミュレータを起動しアプリを起動します
  2. ターミナルで以下のコマンドを実行します
  3. xcrun simctl io booted recordVideo appvideo1.mov
  4. シミュレータを操作して動画を撮影します
  5. 完了したら Ctrl+c で撮影を終了します

すると appvideo1.mov というファイルが作成されており、これが 1920 x 1080 の解像度の動画を撮影することができます
一点注意点としてはシミュレータのサイズは 100% でなければなりません
これを 25% などにして撮影すると動画のサイズが 1920 x 1080 ではなくなってしまいます

最後に

これを覚えておくと App Previews の動画撮影がかなり楽になります
もちろん実機があるならばそれを使ったほうが絶対に良いと思います
実機がない場合にはかなり便利な技だと思います

参考サイト

2017年8月24日木曜日

SpriteKit で UserDefaults を使う方法

概要

SpriteKit でも CoreData は使えるのですが結構頑張る必要があります
簡単なデータ保存であれば UserDefaults を使うのが簡単です

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

使い方

まず didMove(to view: SKView) で初期化します

let defaults = UserDefaults.standard

didMove 以外の関数で使いたい場合はインスタンス変数として定義しておくと良いと思います
あとは好きな場所でデータの操作をします

例えば Int の値を保存して取得する場合は set で保存して

defaults.set(1, forKey: "key")

integer メソッドで取得します

let score = defaults.integer(forKey: "key")

set した際の型によって取得する際のメソッドが変わってくるので注意してください
所謂 kvs 的に使えます

最後に

SpriteKit で UserDefaults を使用する方法を紹介しました
UserDefaults はアプリが削除するまたはアプリのデータが削除されない限り残り続けます
当然ですがデータを永続化したい場合は Firebase などの mBaaS サービスを使いましょう

とりあえずローカルでデータを保存したい場合にはこれで十分かなと思います
CoreData は便利なんですが、ManagedObject のクラスなどを生成したりする必要があるので少し大変です

2017年8月23日水曜日

SpriteKit を使って画面遷移する方法

概要

シングルページビューアプリケーションなどは Storyboard を使って Segue で画面遷移を UIView 同士をつなぐことで画面遷移を実現します
SpriteKit も SKView の親クラスが UIView なので Segue も使えるのですが、ちょっとややこしいです
SpriteKit の場合はシーンファイルを変更することで画面遷移を実現することができます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

遷移元と遷移先のシーンファイルを作成する

何でも OK です
.sks ファイルと .swift ファイルを作成しましょう

  • File -> New -> File -> SpriteKit Scene
  • File -> New -> File -> Swift File

で今回は FirstScene.sks と FirstScene.swift および SecondScene.sks と SecondScene.swift および を作成しました
作成した .sks ファイルの Scene の Custom Class にそれぞれ FirstScene.swift と SecondScene.swift を設定しましょう

そして画面に遷移するボタンの SKSpriteNode を追加しましょう
このノードをタップしたら別のシーンに遷移するようにします
spritekit_scene_change1.png

.sks ファイルはこんな感じになっていれば OK です
SecondSecne.sks も同様にノードと Custom Class を設定すれば OK です

.swift ファイルの編集

.sks ファイルに配置したノードを Swift 側で扱いましょう
FirstScene.swift は以下のようになります

  • FirstScene.swift
import SpriteKit

class FirstScene: SKScene {

    // second ボタン
    var second = SKSpriteNode()

    override func didMove(to view: SKView) {
        // second ボタンの初期化
        second = self.childNode(withName: "second") as! SKSpriteNode
    }

    func touchDown(atPoint pos : CGPoint) {
        if let node = atPoint(pos) as? SKSpriteNode {
            if(node == second){
                if let view = self.view {
                    if let scene = SecondScene(fileNamed: "SecondScene") {
                        scene.scaleMode = .aspectFill
                        view.presentScene(scene)
                    }
                    view.ignoresSiblingOrder = true
                    view.showsFPS = true
                    view.showsNodeCount = true
                }
            }
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchDown(atPoint: t.location(in: self)) }
    }
}

まず .sks ファイルで定義したノードを childNode で取得します
そして touchesBegan -> touchDown メソッドでタッチを判定します
もしタッチした箇所のノードが遷移用のノードだった場合は SecondScene を作成して view.presentScene で画面遷移させています

その後の showsFPS はデバッグ用に表示しているだけなので不要であれば削除してください

これとほぼ同じことを SecondScene.swift でも実装すれば OK です

  • SecondScene.swift
import SpriteKit

class SecondScene: SKScene {

    // first ボタン
    var first = SKSpriteNode()

    override func didMove(to view: SKView) {
        // first ボタンの初期化
        first = self.childNode(withName: "first") as! SKSpriteNode
    }

    func touchDown(atPoint pos : CGPoint) {
        if let node = atPoint(pos) as? SKSpriteNode {
            if(node == first){
                if let view = self.view {
                    if let scene = FirstScene(fileNamed: "FirstScene") {
                        scene.scaleMode = .aspectFill
                        view.presentScene(scene)
                    }
                    view.ignoresSiblingOrder = true
                    view.showsFPS = true
                    view.showsNodeCount = true
                }
            }
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchDown(atPoint: t.location(in: self)) }
    }
}

動作確認

では動作確認してみます
spritekit_scene_change2.gif

こんな感じでノードをタップしたときに画面遷移すれば OK です

最後に

SpriteKit で画面遷移を実現してみました
UIKit を使った場合の実装方法とは少し異なりますが慣れれば簡単にできます
他に UIKit と違う点をざっと上げると

  • NavigationController を使っていないので画面端のスワイプでは元の画面にもどれない
  • NavitationController を使っていないので戻るボタンなどがない
  • ボタンを押したときのアクションがデフォルトだと何もない

あたりかなと思います
上 2 つは問題ない可能性が高いですが下の何もアクションがないのは UX 的にまずい気がします
SKAction を駆使すればうまくできるとは思います

参考サイト

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 用のプラグインがあるようでログなどはそっちで代用することもできそうです

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

参考サイト