2017年9月26日火曜日

user: could not determine current user from environment

概要

packer でプラグインを docker 上で動作させる際に発生しました
原因と対処方法を紹介します

環境

  • Ubuntu 16.04.2
  • docker 17.03.0-ce
  • packer 1.1.0

原因

まず原因ですが、USER 環境変数が設定されていないのが原因です
docker コンテナの場合 USER 環境変数はデフォルトで設定されていません
これが殻の場合以下の部分でエラーになります
https://github.com/hashicorp/packer/blob/master/provisioner/ansible/provisioner.go#L146

対処方法

単純に USER を設定してあげれば OK です
Dockerfile ないのどこかで以下を追加してあげましょう

ENV USER root

これで docker コンテナ上でも packer のプラグインが使えるようになります
正直これでいいのかは何ともですがとりあえず今回のエラーを回避したい場合には使えると思います

2017年9月25日月曜日

GitLab CI で自分だけのランナー (Specific Runner) を構築する方法

概要

GitLab CI を使う場合にビルドやテストをするサーバを用意する必要があります
GitLab 自体が持つサーバはみんなで共有する「Shared Runners」というもので、共有リソースなので待ちが発生したり処理が遅くなったりする場合があります
また、Shared Runners に機能やパッケージがないとそもそもビルドできないというケースもあります
そんな時に専用のサーバ「Specific Runners」を追加することで自分だけのランナーを作ることができます
今回は Specific Runner を Ubuntu 上に構築してみました

環境

  • Ubuntu Ubuntu 16.04.2
  • docker 17.03.0-ce
  • gitlab runner 10.0.0

事前手順

今回は Runner Executor に docker を使用します
なので事前に docker をインストールしておきましょう
https://hawksnowlog.blogspot.jp/2016/12/install-docker-on-ubuntu1604lts.html

インストール手順

  • curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
  • apt -y install gitlab-runner

ランナーを登録する

  • gitlab-runner register

いろいろと登録します

Running in system-mode.

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://your.gitlab.local/
Please enter the gitlab-ci token for this runner:
ySdowE9wxxxxxxxxxxxx
Please enter the gitlab-ci description for this runner:
[localhost]: my-runner
Please enter the gitlab-ci tags for this runner (comma separated):
shell
Whether to run untagged builds [true/false]:
[false]: true
Whether to lock the Runner to current project [true/false]:
[true]: true
Registering runner... succeeded                     runner=ySdowE9w
Please enter the executor: shell, ssh, kubernetes, docker, docker-ssh, parallels, virtualbox, docker+machine, docker-ssh+machine:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

上から

  • gitlab の URL を入力
  • プロジェクトに紐付いているトークンを入力
  • ランナーの名称を入力
  • タグの名称を入力
  • タグを持たない CI でもビルドするかどうか
  • ランナーをプロジェクト専用にしようするかどうか
  • Runner Executor を選択します

という感じです
トークンは UI から取得できます
「Settings」->「Pipelines」で真ん中くらいにあります
gitlab_ci_specific_runner1.png

Runner Executor は今回「shell」を選択しました
ここで docker なども選択することができます
今回は docker コマンドを素で実行するため shell を選択しました
Dockerhub にあるイメージを元にビルドしたい場合は「docker」を選択することになります

全部入力できたら完了です
これで GitLab の UI を確認すると Specific Runner に構築した Ubuntu サーバがランナーとして登録されていると思います
gitlab_ci_specific_runner2.png

gitlab-runner ユーザを docker グループに所属させる

  • usermod -g docker gitlab-runner
  • systemctl restart docker.service

これを行わないと以下のエラーとなります
gitlab-runner ユーザに docker コマンドを実行する権限がないためです

  • Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

Shared-runner を disable にする

これは環境によって異なるので Shared-runner がデフォルトで有効になっている場合は無効にしてください
プロエジェクトの Pipelines の設定を見ると Specific-runner と Shared-runner の設定があるのでそこで disable してください

試しに使ってみる

docker executor は .gitlab-ci.yml で Dockerhub にあるイメージを指定してそのイメージでビルドを行うことができるものです
が、今回は直接 docker build を行ってみます

  • vim .gitlab-ci.yml
stages:
    - build
    - test

build_job:
  stage: build
  script:
    - docker build -t my-runner .

test_job:
  stage: test
  script:
    - docker run my-runner

例えばこんな感じで YAML ファイルを定義します
Dockerfile は各自で適当に準備してください
Dockerfile が準備できれば上記のようにビルドをランナーで走らせることができるはずです

最後に

GitLab CI で使うランナーを独自で構築してみました
公式のインストール方法を見ると Ubuntu 以外にも Redhat 系や Windows, Mac までいけるので何でもランナーにすることはできそうです
結構簡単にできるので共有ランナーが遅いときに試してみてはいかがでしょうか

参考サイト

2017年9月24日日曜日

packer + kickstart でフロッピーを使う方法

概要

過去に CentOS7 を kickstart ファイルを使ってビルドしました
その際に kickstart ファイルは http でホスティングして取得しました
今回は kickstart ファイルをフロッピーに格納して、フロッピー上の kickstart ファイルを指定する方法を紹介します

環境

  • Ubuntu Ubuntu 16.04.2
  • packer 1.0.4
  • VMware ESXi 6.0.0
  • CentOS 7 (ビルド対象)

テンプレートファイル

  • vim centos7_with_floppy.json
{
    "builders": [
    {
        "name": "centos7",
        "type": "vmware-iso",

        "vm_name": "centos7_from_packer",
        "guest_os_type": "rhel7-64",
        "ssh_username": "root",
        "ssh_password": "vm_password",
        "ssh_timeout": "15m",

        "iso_url": "http://ftp.jaist.ac.jp/pub/Linux/CentOS/7/isos/x86_64/CentOS-7-x86_64-Minimal-1708.iso",
        "iso_checksum": "bba314624956961a2ea31dd460cd860a77911c1e0a56e4820a12b9c5dad363f5",
        "iso_checksum_type": "sha256",

        "shutdown_command": "echo 'shutdown -P now' > shutdown.sh; echo 'vm_password'|sudo -S sh 'shutdown.sh'",
        "boot_command": [
        "<tab> text ks=hd:fd0:/centos7.ks<enter><wait>"
        ],
        "floppy_files": [
            "centos7.ks"
        ],
        "tools_upload_flavor": "linux",
        "headless": false,
        "keep_registered": true,

        "remote_type": "esx5",
        "remote_host": "192.168.100.105",
        "remote_datastore": "datastore1",
        "remote_username": "root",
        "remote_password": "esxi_password",
        "disk_type_id": "thin",
        "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"
    }
    ]
}

ほとんど前回と変わっていないようですが変わっているのは以下の部分です

"boot_command": [
"<tab> text ks=hd:fd0:/centos7.ks<enter><wait>"
],
"floppy_files": [
    "centos7.ks"
],

centos7.ks はこちらの内容です
ローカルに配置した centos7.ks ファイルを ESXi にアップロードしてそのままフロッピーにマウントすることで VM から参照できるようにしています

あとは実行するだけです
このほうがわざわざ http を立てる必要がないので良さそうです

そしてフロッピードライブはビルドが終了するとちゃんと vmx ファイルからアンマウントしてくれるため VM として登録するときにもフロッピーディスクがないのが嬉しい点です

最後に

CentOS7 の自動インストール時に kickstart ファイルをフロッピードライブから参照する方法を紹介しました
最終的に同じものができあがるのを考えるとフロッピーを使ったほうが何かと便利そうな気がします

特に Docker 連携をする際に良さそう (IP を気にしなくていいので)
packer を docker 上で動かす方法もわかったら紹介したいと思います

参考サイト

2017年9月23日土曜日

Ubuntu16.04 に ovftool コマンドをインストールする

概要

Ubuntu に ovftool をインストールしてみました

環境

  • Ubuntu 16.04.2
  • ovftool 4.2.0

インストール方法

まず ovftool を MyVMware からダンロードしましょう
VMware のアカウントが必要になります
https://www.vmware.com/support/developer/ovf/

Windows 版や Mac 版がありますので Linux 版をダウンロードしましょう
今回ダウンロードしたファイルは以下になります
VMware-ovftool-4.2.0-5965791-lin.x86_64.bundle

ダウンロードできたらファイルをサーバにアップロードします
アップロードできたらインストーラを実行しインストールします

  • ./VMware-ovftool-4.2.0-5965791-lin.x86_64.bundle

まず EULA を読んで同意しましょう
最後まで読んで yes をタイプします

これでインストール完了です

ovftool コマンドが実行できるようになっています

2017年9月22日金曜日

packer のビルドフロー中に serverspec でテストする方法

概要

packer の provisioners でインストールしたパッケージなどを serverspec でテストしたということはよくあると思います
イメージなどを作成後に VM として起動して serverspec を実行する手法はよくあると思いますが少し面倒くさいです
今回は packer build 内で serverspec を実行する方法を紹介したいと思います

環境

  • macOS X 10.12.6
  • packer 1.0.4
  • go 1.8
  • serverspec 2.40.0

serverspec 準備

今回は serverspec のテストも 1 から作成してみたいと思います

  • bundle init
  • vim Gemfile
gem "serverspec"
  • bundle install
  • serverspec-init
  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 2

 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

ここで 1 つポイントですが実行するホストは localhost にしましょう
要するにビルド中に作成された VM (localhost) に対して実行するようにします

  • vim spec/localhost/sample_spec.rb
require 'spec_helper'

describe package('httpd'), :if => os[:family] == 'redhat' do
  it { should be_installed }
end
  • vim spec/spec_helper.rb
require 'serverspec'
require 'net/ssh'

options = Net::SSH::Config.for(host, [])
options[:user] = ENV['TARGET_USER']
options[:keys] = ENV['TARGET_KEY']
options[:host_name] = ENV['TARGET_HOST']
options[:port] = ENV['TARGET_PORT']
options[:paranoid] = false unless ENV['SERVERSPEC_HOST_KEY_CHECKING'] =~ (/^(true|t|yes|y|1)$/i)

set :host,         options[:host_name]
set :ssh_options,  options
set :backend,      :ssh
set :display_sudo, true
set :request_pty,  true

sample_spec.rb と spec_helper.rb はとりあえず上記をそのまま利用してください

プラグインのインストール

今回 Github で公開されている packer-provisioner-serverspec を使います

  • go get github.com/unifio/packer-provisioner-serverspec
  • mkdir $HOME/.packer.d/plugins
  • cp $GOPATH/bin/packer-provisioner-serverspec $HOME/.packer.d/plugins

で OK です

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

  • vim centos7_with_serverspec.json
{
    "builders": [
    {
        "type": "virtualbox-iso",
        "vboxmanage": [
        [ "modifyvm", "{{.Name}}", "--memory", "2048" ],
        [ "modifyvm", "{{.Name}}", "--nic1", "nat" ],
        [ "modifyvm", "{{.Name}}", "--nic2", "hostonly" ],
        [ "modifyvm", "{{.Name}}", "--hostonlyadapter2", "vboxnet0"]
        ],
        "iso_checksum": "27bd866242ee058b7a5754e83d8ee8403e216b93d130d800852a96f41c34d86a",
        "iso_checksum_type": "sha256",
        "iso_url": "http://ftp.riken.jp/Linux/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1611.iso",
        "ssh_username": "root",
        "ssh_password": "password",
        "ssh_wait_timeout": "40m",
        "disk_size": "8000",
        "guest_os_type": "RedHat_64",
        "shutdown_command": "/sbin/shutdown -h now",
        "shutdown_timeout": "20s",
        "vm_name": "centos7_from_packer",
        "boot_command": [
        "<tab> text ks=http://192.168.56.101/centos.ks<enter><wait>"
        ]
    }
    ],
    "provisioners": [
    {
        "type": "shell",
        "inline": ["yum install -y vim httpd"]
    },
    {
        "type": "serverspec",
        "rake_file": "Rakefile",
        "rake_task": "spec:all",
        "rake_env_vars": "$BUNDLE_GEMFILE=Gemfile"
    }
    ]
}

基本は過去に CentOS7 をビルドしたテンプレートを使っています
またテンプレートを作成する場所は serverspec-init を実行したパスと同じ場所に配置してください

ポイントは provisioners に "type": "serverspec" が追加されている点です
プラグインをインストールすることでこのタイプが使えるようになります

また、今回は OS のインストールも自動化することを想定しているので kickstart サーバも使っています
kickstart サーバの構築に関してはこちらを御覧ください

動作確認

  • packer build centos7_with_serverspec.json

でいろいろとログが流れます
長いので割愛しますが流れとしては

ISO 取得 -> VM 起動 -> OS インストール -> SSH 接続 -> provisioner 実行 (yum) -> provisioner 実行 (serverspec)

になります
最終的に serverspec が実行されてエラーが出なければ成功です

試しに作成されたイメージを一度起動して VM の中を確認してみましょう
(今回は Virtualbox を使っているので仮想アプライアンスのインポートから作成された ovf を選択すれば OK です)
今回の場合であれば httpd がインストールされていれば OK となります
また、今回の手順の場合作成されたイメージないに serverspec コマンドはインストールされません
packer_with_serverspec1.png

最後に

プラグインを使って packer + serverspec でビルド中にイメージのテストを実施してみました
こっちのほうが手順としても packer build 1 回で済むのでスマートかなと思います

いろいろと調べてみると packer + serverspec を連携する方法は出てきますがビルドするイメージ内に serverspec をインストールする方法が結構多いです
それでも特に問題はないですが ruby など使わないのであれば、あまり入れたいとは思いません
今回の手順はイメージ内に serverspec をインストール必要がないのも嬉しい点かなと思います

少し心配な点があるとすれば今回使ったプラグインがあまりメンテナンスされていない点でしょうか、、、

参考サイト

2017年9月21日木曜日

個人メモ 日本語入りの keynote を slideshare にアップロードする手順

概要

いつも忘れるのでメモ
こちらの手順を使っています

環境

  • macOS X 10.12.6
  • keynote 7.2 (4582)

手順

  1. keynote を使って pdf を書き出し
  2. ターミナルを開く
  3. LANG=C LC_ALL=C sed -i '' s'|/Registry (Adobe) /Ordering (Japan1) /Supplement [0-9]|/Registry(Adobe) /Ordering(Identity) /Supplement 0|g' output.pdf
  4. アップロード
  5. メタデータ (タイトルや説明、タグ、公開フラグ) を設定

2017年9月20日水曜日

Sinatra で Encoding::CompatibilityError - incompatible character encodings: UTF-8 and ASCII-8BIT:

概要

Sinatra で erb を使って HTML を出力するときに発生しました
対応策を紹介します

環境

  • CentOS 7.3.1611
  • Ruby 2.3.1p112

対応方法

res.body.force_encoding("UTF-8")

res.body は文字列です
要するに erb で表示しようとしている文字列のエンコード情報がおかしいので UTF-8 に変更してあげる感じです

erb にマジックコメントを入れることで対応できるという紹介記事もあったのですが自分はそれではできなかったので上記の対応をしました

2017年9月19日火曜日

packer + esxi で CentOS7 の ISO から vmx と vmdk ファイルを作成してみた

概要

前回 Ubuntu で試しました
今回は CentOS で試したので JSON テンプレートの紹介をしたいと思います

環境

  • Ubuntu Ubuntu 16.04.2
  • packer 1.0.4
  • VMware ESXi 6.0.0

事前準備

JSON テンプレート

  • vim centos7.json
{
    "builders": [
    {
        "name": "centos7",
        "type": "vmware-iso",

        "vm_name": "centos7_from_packer",
        "guest_os_type": "rhel7-64",
        "ssh_username": "root",
        "ssh_password": "vm_password",
        "ssh_timeout": "15m",

        "iso_url": "http://ftp.jaist.ac.jp/pub/Linux/CentOS/7/isos/x86_64/CentOS-7-x86_64-Minimal-1708.iso",
        "iso_checksum": "bba314624956961a2ea31dd460cd860a77911c1e0a56e4820a12b9c5dad363f5",
        "iso_checksum_type": "sha256",

        "shutdown_command": "echo 'shutdown -P now' > shutdown.sh; echo 'vm_password'|sudo -S sh 'shutdown.sh'",
        "boot_command": [
        "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos7.ks<enter><wait>"
        ],
        "http_directory": "./",
        "tools_upload_flavor": "linux",
        "headless": false,
        "keep_registered": true,

        "remote_type": "esx5",
        "remote_host": "192.168.100.105",
        "remote_datastore": "datastore1",
        "remote_username": "root",
        "remote_password": "esxi_password",
        "disk_type_id": "thin",
        "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"
    }
    ]
}

詳細に説明

メタデータ

  • name・・・このビルドの名称、packer は並列ビルドが可能でどのビルドかをこの name で識別します
  • type・・・どのビルドタイプを使うか指定します、今回は「vmware-iso」を使いましたが他にも docker や amazon-ebs などたくさんのビルドタイプがあります、今回のタイプはダウンロードした ISO を ESXi でビルドし vSphere 環境で動作する vmx ファイルと vmdk ファイルを作成するためのタイプです

作成する VM の情報

  • vm_name・・・VM の名前
  • guest_os_type・・・vSphere 環境で扱われる OS のタイプを指定します、今回は RedHat 系の OS なので「rhel7-64」を指定しています、他にも ubuntu-64 や other などがあります
  • ssh_username・・・プロビジョニングするために作成した VM に SSH ログインするためのユーザ名です、OS インストール時に作成したユーザ名と同じにする必要があります
  • ssh_password・・・プロビジョニングするために作成した VM に SSH ログインするためのパスワードです、OS インストール時と設定したパスワードと同じにする必要があります
  • ssh_timeout・・・SSH のタイムアウト時間です、この時間内に VM と SSH のコネクションが確立されないとビルドエラーとなります

ISO 情報

  • iso_url・・・ダウンロードする ISO の URLです、ここで指定した ISO をダウンロードして packer はビルドします
  • iso_checksum・・・ダウンロードする ISO のチェックサムです、チェックサムが間違っているとビルドエラーとなります
  • iso_checksum_type・・・チェックサムのタイプを指定します

ビルドオプション

  • shutdown_command・・・すべてのプロビジョニングが完了した後に実行するシャットダウンコマンドを指定します
  • boot_command・・・ISO から OS をインストールする際のオプションを指定します、GRUB 画面を直接制御することができる特殊なコマンドが用意されており、ここで kickstart サーバを指定します
  • http_directory・・・これを指定することで packer 自信が HTTP サーバになることができます、HTTP サーバとして立ち上がった際のバインドされる IP とポートはそれぞれ {{ .HTTPIP }}{{ .HTTPPort }} に格納されます、このオプションでは HTTP サーバの DocumentRoot となるパスを指定します
  • tools_upload_flavor・・・VMware tools のタイプを指定します、他に windows や darwin が指定できます
  • headless・・・ビルド時にコンソールを使うか使わないか指定できます、今回の場合は kickstart を使って OS をインストールするため true でも問題ないです、false にすることでコンソールを確認すると OS のインストール画面が確認できるのでデバッグに役立ちます
  • keep_registered・・・ビルド完了後にサーバを削除するかしないか指定できます、true にするとサーバはシャットダウン後削除されません

ESXi 情報

  • remote_type・・・「esx5」を絶対指定しましょう、それ以外の指定ではエラーとなります
  • remote_host・・・ビルドする ESXi の IP を指定します
  • remote_datastore・・・ダウンロードする ISO を格納したり、成果物となる vmx や vmdk を格納する ESXi のデータストアを指定します
  • remote_password・・・ESXi に SSH ログインするための root パスワードを指定します
  • disk_type_id・・・VM を作成する際のディスクタイプを指定します、thin の場合はシンプロビジョニングが適用されます、他には zeroedthick, eagerzeroedthick などが指定できます、0 - 5 の数字でも指定できます
  • vmx_data・・・VM の構成を細かく設定することができます、CPU やメモリネットワークや接続する外部記憶デバイスを指定することができます、指定するフォーマットは VMware に準拠するため詳細は VMware のドキュメントを確認する必要があります、ethernet0.networkName は 1 枚目の NIC を指定することができます

VNC 関係

  • vnc_port_min・・・ビルド時に作成される VM が LISTEN する VNC サーバに使用される最小ポート番号を指定します
  • vnc_port_max・・・ビルド時に作成される VM が LISTEN する VNC サーバに使用される最大ポート番号を指定します、今回の場合 VM は 5900 から 5911 番の間で VNC サーバを LISTEN します
  • vnc_bind_address・・・ビルド時に作成される VM が LISTEN する VNC サーバのバインド IP を指定します
  • vnc_disable_password・・・VNC サーバのパスワード認証を使うか使わないか指定します、true の場合認証を使いません

VNC に関して少し補足として vSphere 環境に作成される VM にはオプションで VNC サーバを立ち上げることができます
もし VM に VNC を立ち上げるとコンソールの情報を VNC でも確認することができます
packer はデフォルトで VNC 接続を使うので VNC 関連のオプションを指定する必要があります

ビルド

  • packer build centos7.json

成果物の保存場所

  • ls /vmfs/volumes/datastore12/output-centos7

ちなみに再度ビルドする場合 output-centos7 フォルダがあるとビルドエラーになるので事前に削除しておきましょう

最後に

packer + esxi で CentOS をビルドしてみました
基本的な操作を確認したかったので、変数の展開やプロビジョニングはしませんでした
興味があればドキュメントを確認しながら実施すれば簡単にできると思います

参考サイト

2017年9月18日月曜日

Swift3 で UITableView を使う方法

概要

基本中の基本なのですが、delete とか dataSource の使い方を忘れてしまうのでメモとして残しておきます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

プロジェクト作成

Single View Application で作成しましょう

Main.storyboard で UI を作成する

すでに作成されている ViewController に追加していきます

UITableView を追加

まず View があるので、その配下に UITableView を追加します
上下左右の Constraints を 0 で設定しましょう
swift3_basic_uitableview1.png

UITableViewCell を追加

その配下に UITableViewCell を追加しましょう
Identifier にセル識別する ID を設定しておきます
今回は「cell」としました
swift3_basic_uitableview2.png

またセルの高さもここで設定できるので設定しておきます
今回は「100」としました

ContentView 配下にコンテンツを追加

セルに表示するコンテンツを追加していきます
今回はよくある定番の画像とラベルを追加します
UILabel と UIImageView を右ペインの一覧から引っ張ってきて配置しましょう
でここでポイントですが、ContentView 内に配置したコンテンツには Constrainsts をしっかり設定しましょう
今回は左に画像を右にラベルを配置するように Constraints を設定しました

ここの Constraints を設定するのが結構たいへんでセルが狭いと Constraints を設定したときに UIImageView と UILabel が重なっているせいで制約が親の View になってしまったりします
なので初めに手動で各種コンテンツを配置したい場所にだいたい配置して、そのあとで Constraints を設定することで Xcode が良い感じに設定してくれるようになります (言葉で説明するのが難しい、、、)

あと設定しなければいかないのは Tag です
各コンテンツに数字のタグを付与することができます
swift ファイル側で各コンテンツを取得する際に必要になるので必ず設定しましょう
またタグはデフォルトが 0 になっていて 0 は使えないので必ず 1 から設定するようにしてください
今回は UIImageView を 1、UILabel を 2 にしました

最終的には以下のようになれば OK です
swift3_basic_uitableview3.png

セルの高さを 100 にしたので UIImageView の高さも Constraints で 100 にしました

delegate と dataSource を紐付ける

ちょっとややこしいので UITableView を選択した状態で Ctrl を押しながら黄色の丸にドロップアンドドロップしてください
すると delegate と dataSource という項目が出るので 2 つとも UITableView に紐付けましょう
swift3_basic_uitableview5.png

紐付けできると右ペインの Outlets の項目に delegate と dataSource が追加されているのがわかると思います

ViewController.swift でテーブル情報を操作する

これもデフォルトである ViewController を使っていきます
Single View Application でプロジェクトを作成した場合 Main.storyboard にある UIViewController のクラスがデフォルトで ViewController.swift になっています

tableView を swift に追加する

画面を分割して storyboard 側から Ctrl を押したまま UITableView の情報をドラックアンドドロップすれば OK です
以下の 1 行が swift 側に追加されれば OK です

@IBOutlet weak var tableView: UITableView!

swift3_basic_uitableview4.png

UITableViewDelegate を継承して必要な関数を実装する

あとは実際にテーブル情報にコンテンツを表示するロジックを swift 側に記載していきます

まず全体のコードはこちら

  • ViewController.swift
import UIKit

class ViewController: UIViewController, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!
    let pokemons = [
        [
            "name": "モクロー",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_01/portrait.png"
        ],
        [
            "name": "ニャビー",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_02/portrait.png"
        ],
        [
            "name": "アシマリ",
            "image": "http://www.pokemon.co.jp/ex/sun_moon/common/images/pokemon/partner_03/portrait.png"
        ]
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // Cell の数を返却します
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return pokemons.count
    }

    // Cell の情報を生成します
    func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
        let pokemon = pokemons[indexPath.row]
        // Identifier を元にセルを取得
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
        // タグ番号 1 で UIImageView インスタンスの生成
        let url = URL(string: pokemon["image"]!)
        let data = try? Data(contentsOf: url!)
        let image: UIImage = UIImage(data: data!)!
        let imageView = cell.viewWithTag(1) as! UIImageView
        imageView.image = image
        // タグ番号 2 で UILabel インスタンスの生成
        let name = cell.viewWithTag(2) as! UILabel
        name.text = pokemon["name"]
        return cell
    }

    // Cell の高さを返却します, StoryBoard の TableView の高さと同じにする必要があります
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
}

まず UITableViewDelegate を継承します
そして最低限必要なメソッドを 3 つ定義します「numberOfRowsInSection」「cellForRowAtIndexPath」「heightForRowAt」

numberOfRowsInSection はテーブル内に含まれるセルの数を返却する関数です
基本はテーブルに表示する配列などのサイズを返します

cellForRowAtIndexPath はテーブル内のセルにコンテンツを表示する関数です
index が引数で受け取れるのでこの引数を元に UITableViewCell の情報を取得します
そして取得したセル情報のタグ情報をもとに viewWithTag メソッドを使ってコンテンツ表示用の View を取得します
あとはコンテンツ情報をそれぞれの View にセットし最後に cell を返却することでセルにコンテンツが表示されます

heightForRowAt はセルの高さを返す関数です
Main.storyboard 側でも定義していると思いますが関数も定義しましょう

他にも使える関数はたくさんあるので詳しくは参考サイトにある Apple 公式のドキュメントを御覧ください

ATS について

今回はとりあえず ATS を off にしました
Targets を開いて Info に「App Transport Security Settings」->「Allow Arbitrary Loads」を「YES」に設定しています

本当ならちゃんと http するドメインを登録する必要があります

動作確認

あとは実行するだけです
今回は 3 つしかコンテンツがありませんが画面外の場合もちゃんとスクロールすれば表示されます
swift3_basic_uitableview6.png

最後に

Swift3 での UITableView の使い方を紹介しました
iOS アプリの開発手法としてはかなり初歩的なやり方になるかなと思います
結構複雑そうには見えますが、慣れれば簡単に使えるようになると思います

ちなみに今回のようにセルで画像を表示する方法はダメな方法です
これだとセルが表示されるたびに通信をするので大量にスクロールするとアプリがダウンすると思います
本来であれば画像を一度ダウンロードしたらメモリなどにキャッシュしてキャッシュがある場合はそれを表示するようなロジックが必要になります

過去にそれを解決する手法も紹介しているので興味があれば御覧ください

参考サイト

2017年9月17日日曜日

Swift3 で YoutubePlayer を使って動画を再生する方法

概要

Swift3 で YouTube の動画を再生する方法を紹介します
今回は YouTubePlayer というライブラリを使って再生させてみます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)
  • YouTubePlayer 0.4.0

ライブラリインストール

cocoapods でインストールすることができます

  • vim Podfile
pod 'YouTubePlayer'
  • pod install

View を設置して YouTubePlayerView をカスタムクラスに設定する

まず Main.storyboard を編集します
ViewController に 1 つ View を配置し Class に「YouTubePlayerView」を設定し Module に「YouTubePlayer」を設定しましょう
Constraints は適当に設定してください
swift3_with_youtubeplayer1.png

IBOutlet で .swift ファイルに接続する

設置した YouTubePlayerView を Control + ドラッグで .swift ファイル上に接続しましょう
.swift ファイルには import が必要なので以下を追記します

import YouTubePlayer

うまく接続できると以下の変数が自動で追加になります

@IBOutlet weak var player: YouTubePlayerView!

swift3_with_youtubeplayer2.png

こんな感じになれば OK です

YouTube の動画を再生するロジックを追記すうる

.swift ファイル側で追加した View を制御して動画を再生してみましょう
といっても以下の 1 行を追加するだけです

player.loadVideoID("1rlB8HSuWks")

今回は viewDidLoad 内でコールしています
ここで指定している ID は動画にアクセスしたときに URL に含まれる watch?v=1rlB8HSuWks の部分です

動作確認

あとは実行するだけです
シミュレータでも動作します
swift3_with_youtubeplayer3.gif

再生、停止はもちろん早送りや早戻し、全画面表示も対応しています

最後に

Swift3 で YouTubePlayer を使って YouTube の動画を再生してみました
今回試したシミュレータの iOS のバージョンが 10.3.1 だったので iOS11 でも動作するかは不明です
Github で公開されているコードは一応メンテされているっぽいですが今後も続くかは不明です
おそらく内部的には AVFoundation の AVPlayer などを使っていると思うので自分でやろうと思えば同じことはできますが結構簡単に使えるので、今後もエンハンスしてほしいなと思います

すでに Swft3 の環境でビルドすると YouTubePlayer のライブラリの警告が 10 個ほど出るので心配な気もしますが、、、

参考サイト

2017年9月16日土曜日

SpriteKit で AVPlayer + SKVideoNode を使って動画を再生してみた

概要

SpriteKit で動画を再生した場合はあると思います
SKVideoNode という SpriteKit で動画を再生するためのノードがありこれと AVFoundation の AVPlayer というモジュールを使うと動画を再生することができます

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

再生する動画のダウンロード

今回はこれを利用します
ダウンロードしたら Targets -> Build Phases -> Copy Bundle Resources にダウンロードしたファイルを追加します
spritekit_with_avplayer1.png

Swift ファイルの編集

.sks ファイルの編集は省略します
動画をスタートする用のボタンと停止する用のボタンを配置しているだけです

import SpriteKit
import AVFoundation

class VideoScene: SKScene {

    var play = SKSpriteNode()
    var pause = SKSpriteNode()
    var video = SKVideoNode()

    override func didMove(to view: SKView) {
        play = self.childNode(withName: "play") as! SKSpriteNode
        pause = self.childNode(withName: "pause") as! SKSpriteNode
        video = {
            let urlString = Bundle.main.path(forResource: "mp4_h264_aac", ofType: "mp4")
            let url = URL(fileURLWithPath: urlString!)
            let item = AVPlayerItem(url: url)
            let player = AVPlayer(playerItem: item)
            return SKVideoNode(avPlayer: player)
        }()
        self.addChild(video)
    }

    func touchDown(atPoint pos : CGPoint) {
        if let node = atPoint(pos) as? SKSpriteNode {
            if(node == play){
                if let _ = self.view {
                    print("play")
                    video.play()
                }
            } else if(node == pause){
                if let _ = self.view {
                    print("pause")
                    video.pause()
                }
            }

        }
    }

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

コード中で SKVideoNode を作成しています
あとはノードをタップしたときに playpause を実行しています

動作確認

必ず実機で行うようにしてください
シミューレータだとなぜか動画のノードが表示されませんでした (音はするんですが)
各種ボタンをタップして動画の再生と停止ができれば OK です

動かしてみるとわかりますがループ再生できません
一度最後まで行ったら再度アプリを起動しないと動画が始めに戻りません

これが SKVideoNode を使った場合の最大の弱点です
一応ループしたりシークバーを出したりする方法はあるみたいなので興味があれば調べてみてください

また今回は Web にある音源をダウンロードしてから再生しました
直接 URL を指定して再生できるかやってみたのですがうまくできませんでした
今回の組み合わせだとローカルにダウンロードしてからでないと再生できないのかもしれません

参考サイト

2017年9月15日金曜日

SpriteKit で segue を使って別の UIView に遷移する方法

概要

UIKit を使っていると storyboard + segue で各 View の遷移を行います
SpriteKit を使っていてもこの segue が使うことができます
今回はその方法を紹介します

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)

遷移する先の ViewController を作成する

Main.storyboard を編集して ViewController を追加しましょう (OtherViewControoler)
とりあえず元の View に戻るためにナビゲーションバーを追加してボタンを設置しました

あとはその ViewController 用のクラスも作成します

  • OtherViewController.swift
import UIKit

class OtherViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func unwindToTop(segue: UIStoryboardSegue) {
        print("push")
    }

    @IBAction func back(_ sender: Any) {
        print("push")
        self.dismiss(animated: true, completion: nil)
    }
}

設置したボタンに IBAction を登録しましょう
そして dismiss をコールすることで元の View に戻るようにしています
最終的に以下のようにしました

spritekit_with_segue1.png

SKScene から segue をコールして遷移する

まず Main.storyboard で segue を作成します
先ほど作成した先の UIViewController に対して segue を張りましょう

そして segue に ID を振ります
今回は to_other_vc としました

spritekit_with_segue2.png

そしてこの segue を SKScene 側で使うことで画面遷移させることができます

  • MoveScene.swift
import SpriteKit

class MoveScene: SKScene {

    var move = SKSpriteNode()

    override func didMove(to view: SKView) {
        move = self.childNode(withName: "move") as! SKSpriteNode
    }

    func touchDown(atPoint pos : CGPoint) {
        if let node = atPoint(pos) as? SKSpriteNode {
            if(node == move){
                if let view = self.view {
                    let vc = view.window?.rootViewController;
                    vc?.performSegue(withIdentifier: "to_other_vc", sender: nil)
                }
            }
        }
    }

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

画面を遷移するノードをタップしたら view から rootViewController を取得します
それが ViewController になっているのであとは segue を使って画面を遷移することができる performSegue をコールするだけです
その際に先ほど segue に設定した ID を指定します

動作確認

こんな感じで動くと思います
spritekit_with_segue3.gif

最後に

SpriteKit で segue を使ってデフォルトの GameViewController 以外の UIViewController に遷移させてみました

ライブラリによっては UIViewController 上で動くように作られているものもあり SpriteKit 上では動かすのが大変な場合があります
そんな場合に今回の方法でゲーム以外の用途の別の UIViewController を呼び出してそちらで描画など行うことができます

知っておくと結構便利な機能かなと思います

参考サイト

2017年9月14日木曜日

Xvfb だけが動作している環境に VNC で接続して GUI の挙動を確認する方法

概要

Xvfb は仮想デスクトップ用のプロセスです
これが動作していればヘッドレスブラウザができるのですが目視で確認することはできません
そんなときに VNC と連携することで仮想デスクトップの挙動を目視できるようになります

環境

  • CentOS 7.3.1611
  • Xvfb 1.17.2-22
  • Firefox 52.1.0
  • x11vnc 0.9.13-11

インストール

  • yum -y install epel-release
  • yum -y install fluxbox
  • yum -y install x11vnc

fluxbox はウィンドウマネージャ
x11vnc は VNC サーバです

プロセス起動

  • Xvfb :1 -screen 0 1024x768x24 > /dev/null &
  • fluxbox &
  • x11vnc -display :1 -bg -nopw -listen 0.0.0.0 -xkb

Xvfb は仮想デスクトッププロセス
fluxbox ウィンドウマネージャのプロセス
x11vnc は VNC サーバのプロセス

5900 で LISTEN すれば OK です

動作確認

VNC Viewer などを使って接続しましょう
すると何もない画面が起動されます
確認として firefox を起動してみましょう

  • firefox -display :1 -width 1024 -height 800 > /dev/null &

すると何もなかった画面に firefox が登場すると思います

ちなみに Xvfb と firefox も yum でインストール可能です

  • yum -y install xorg-x11-server-Xvfb
  • yum -y install firefox

参考サイト

2017年9月13日水曜日

packer と kickstart を連携して OS のインストールを自動化してみた

概要

前回 packer + Virtualbox を使って ISO から ovf + vmdk ファイルを作成してみました
その中で OS のインストールは手動でやらなければいけないという問題がありました
実は以前 Virtualbox 上に kickstart サーバを構築しており今回はこの kickstart サーバを使って OS のインストール作業も自動化してみました

環境

  • macOS X 10.12.6
  • packer 1.0.4
  • Virtualbox 5.1.26 r117224 (Qt5.6.2)

kickstart サーバの構築

これは過去の記事を参考にしてください
今回の記事では過去に作成した kickstart サーバをそのまま流用します

テンプレートファイルの編集

packer には boot_command という機能がありこれを使うことで VM が起動した際の GRUB の画面などを操作することができます

これを使って OS インストール時に kickstart サーバを指定します

  • vim centos7_with_ks.json
{
  "builders": [
    {
      "type": "virtualbox-iso",
      "vboxmanage": [
        [ "modifyvm", "{{.Name}}", "--memory", "2048" ],
        [ "modifyvm", "{{.Name}}", "--nic1", "nat" ],
        [ "modifyvm", "{{.Name}}", "--nic2", "hostonly" ],
        [ "modifyvm", "{{.Name}}", "--hostonlyadapter2", "vboxnet0"]
      ],
      "iso_checksum": "27bd866242ee058b7a5754e83d8ee8403e216b93d130d800852a96f41c34d86a",
      "iso_checksum_type": "sha256",
      "iso_url": "http://ftp.riken.jp/Linux/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1611.iso",
      "ssh_username": "root",
      "ssh_password": "password",
      "ssh_wait_timeout": "40m",
      "disk_size": "8000",
      "guest_os_type": "RedHat_64",
      "shutdown_command": "/sbin/shutdown -h now",
      "shutdown_timeout": "20s",
      "vm_name": "centos7_from_packer",
      "boot_command": [
        "<tab> text ks=http://192.168.56.101/centos.ks<enter><wait>"
      ]
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "nmcli con mod enp0s3 connection.autoconnect 'yes'",
        "systemctl restart NetworkManager",
        "systemctl restart network"
      ]
    },
    {
      "type": "shell",
      "inline": ["yum install -y vim"]
    }
  ]
}

ポイントは boot_command の部分です

"boot_command": [
  "<tab> text ks=http://192.168.56.101/centos.ks<enter><wait>"
]

boot_command では特殊なタグを使用してカーソルの移動やキーストロークを行います
今回は Tab キーを一度押した後「ks=http://192.168.56.101/centos.ks」というテキストを打ち込み、Enter を入力して待つという処理を行わせています
実際に Virtualbox のコンソールを眺めているとわかるのですが自動で入力されているのがわかると思います

CentOS7 の Minimal ISO の場合、OS インストール時のデフォルトのカーソルがイメージのテストをしてからインストールという流れになっているので一度 <up> してからコマンドを打ち込んでも良いかなと思います

実行

  • packer build centos7_with_ks.json

実行すると ISO のダウンロード -> VM の作成 -> 起動 (Virtualbox のコンソールも起動) という流れで VM の作成が始まります
今回は kickstart サーバを指定しているので、OS のインストール画面も自動で進みます

OS のインストールが完了すると自動で再起動されます (これは kicksatrt で指定しています)
そして再起動後 IP が取得できるとプロビジョニングの処理がはじまり SSH を開始します
そしてプロビジョニングも問題なく終了すると output-virtualbox-iso 配下に vmdk と ovf ができあがっています

最後に

packer と kicksatrt を連携して ISO から ovf の作成の流れを完全に自動化してみました
今回は本当に単純な処理しかしていないので、時間も 20 分から 30 分程度で終了します
が、プロビジョニングが長かったりダウンロードする ISO が非常に大きかったりすると 1 回のビルドで 1 時間や 2 時間平気でかかってくると思います
その辺はかなり辛いポイントかなと思います

結局 packer もトライアンドエラーを繰り返してテンプレートを作成していく感じになると思うので、次のトライまでに数時間待たないといけないとなると作業効率も下がってくるかなと思います

OS のインストール処理が絡んでくるのでどうしても長くなりがちなので仕方ないと言えば仕方ないのかもしれませんが、、、

参考サイト

2017年9月12日火曜日

packer + Virtualbox で ISO から ovf ファイルを作成してみる

概要

Virtualbox で VM を作成する場合 ISO が必要になります
そして Virtualbox 上で作成した VM は ovf 形式にエクスポートすることができます
この流れは Virtualbox だけでも可能ですが packer を使うことで一連の流れを自動化することができます

環境

  • macOS X 10.12.6
  • packer 1.0.4

事前準備

packer のインストール Virtualbox のインストールを行っておいてください
自分は Mac 上でビルドするので packer は homebrew と Virtualbox は公式のインストーラを使ってインストールしました

テンプレート Json の作成

今回は CentOS7 の ovf ファイルを最終的に作成します
CentOS7 のインストール用 ISO をダウンロードしてそこから ovf を作成します

  • vim centos7.json
{
    "builders": [{
        "type": "virtualbox-iso",
        "vboxmanage": [
            ["modifyvm", "{{.Name}}", "--memory", "2048"],
            ["modifyvm", "{{.Name}}", "--nic1", "nat"],
            ["modifyvm", "{{.Name}}", "--nic2", "hostonly"],
            ["modifyvm", "{{.Name}}", "--hostonlyadapter2", "vboxnet0"]
        ],
        "iso_checksum": "27bd866242ee058b7a5754e83d8ee8403e216b93d130d800852a96f41c34d86a",
        "iso_checksum_type": "sha256",
        "iso_url": "http://ftp.riken.jp/Linux/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1611.iso",
        "ssh_username": "root",
        "ssh_password": "password",
        "ssh_wait_timeout": "40m",
        "disk_size": "8000",
        "guest_os_type": "RedHat_64",
        "shutdown_command": "/sbin/shutdown -h now",
        "shutdown_timeout": "20s",
        "vm_name": "centos7_from_packer"
    }],
    "provisioners": [{
        "type": "shell",
        "inline": ["yum install -y vim"]
    }]
}

簡単な解説

builder の type に virtualbox-iso を指定することで Virtualbox 上で ISO から VM を作成し ovf を作成することができます
ISO はネットからダウンロードできる Minimal の ISO を使います
ダウンロードされた ISO は packer のキャッシュとして以下のディレクトリに保存されます

./packer_cache/c01a0f674526c96fe4c941774a9f761f6c51e5346004457aa50aa802d978a772.iso

vboxmanage を使って Virtualbox 上に作成される VM の設定を変更する必要があります
今回 yum install をするので VM がインターネットに接続できる必要があります
かつ、packer を実行している Mac から VM に対して SSH 接続できる必要があります
なので「nat」と「hostonly」のネットワークを接続しています

あと、ssh_username と ssh_password はこのあと設定する OS のインストールで使用します
CentOS7 のインストールの設定の際に root ユーザのパスワードを決める必要がありますが、それをテンプレート内と同じ値にしないと VM に SSH できなくなるので気をつけてください

実行してみる

では実行してみます

packer build

  • packer build centos7.json

実行するとまず ISO のダウンロードが始まります
ダウンロードが完了すると Virtualbox 上に VM が作成されて OS のインストール画面が起動します

手動で OS のインストールを行う

今回の場合、残念ながらここはどうしても手動で行う必要があります
この手動の作業を自動化したい場合は別途 kickstart サーバを用意する必要があります
packer + kickstart を連携して OS のインストールを自動化する方法は別記事で紹介したいと思います

今回の場合、手動で設定する項目もそれほど多くないのでちゃちゃっと手動で設定してしまいましょう
設定するのはシステムのインストール先のパーティションと root パスワードとネットワーク設定になります 
ただ、特に注意しなければならないのはネットワークの設定です
デフォルトだと追加した 2 つのネットワークは切断状態になっています
そのため再起動後に SSH 経由で実行するプロビジョニングが失敗してしまいます
なので、OS インストール時の設定で接続した 2 つのネットワークは必ず接続するようにしてください

※どうしても SSH できない、ネットワークが上がらない場合は再起動して VM が上がったらコンソールからログインして接続してあげましょう

自動でプロビジョニングが始まるのを確認する

OS のインストールが完了し VM を手動で再起動したらプロビジョニングが始まります
今回は簡単な yum install だけなのですぐ終わると思います

確認のため終わったら VM を起動して SSH でログインしてみましょう
packer のテンプレートで指定した yum install -y vim がちゃんと実行されているはずです

成果物 (ovf) を確認する

packer build が完了したら目的の ovf + vmdk ができあがっているはずです

  • ls -ltr output-virtualbox-iso/
total 1157712
-rw-------  1 hawksnowlog  staff       7878  9 11 16:38 centos7_from_packer.ovf
-rw-------  1 hawksnowlog  staff  592738304  9 11 16:39 centos7_from_packer-disk001.vmdk

あとはこれを Virtualbox や vSpehre 環境にデプロイすれば VM として使うことができます

最後に

packer + Virtualbox を使って vmdk + ovf ファイルを作成してみました
自分がいちばん躓いたのはプロビジョニングのところで OS インストールし再起動後 VM に接続できない現象にハマリました
なぜかネットワークが disconnected になってしまったので最後はコンソールからログインして SSH のタイムアウトになる前にネットワークを起動しました

次回は今回の構成に kickstart サーバを連携して OS のインストール部分を自動化してみたいと思います

参考サイト

2017年9月11日月曜日

VirtualBox で kickstart サーバを構築してみた

概要

kickstart は ISO などから VM に OS をインストール際にそのインストールの設定を自動化するための仕組みです
まずは kickstart サーバを構築する必要があるので今回はサーバの構築まで行ってみました

環境

  • macOS X 10.12.6
  • Virtualbox 5.1.26 r117224 (Qt5.6.2)
  • CentOS 7.3.1611

事前準備

今回は Mac 上の Virtualbox で進めていきます
なので、まず Virtualbox をインストールしましょう
もちろん他の仮想化環境や物理マシンでも問題ないです

kickstart サーバには CentOS サーバを採用します
なので、ISO ファイルを公式からダウンロードしましょう
今回は「CentOS-7-x86_64-Minimal-1611.iso」という ISO ファイルをダウンロードしました

kickstart サーバ用の CentOS の構築

Virtualbox で作業します
新規から VM を作成します
タイプに「Linux」を選択しバージョンに「Red Hat (64-bit)」を選択します
kickstart1.png

以下各設定項目になります

  • メモリサイズは今回 2048MB にしました
  • ハードディスクは「仮想ハードディスクを作成する」にしました
  • ハードディスクのタイプは VDI にしました
  • サイズのタイプは「可変サイズ」にしました
  • ファイルの場所は好きな場所を選択してください、サイズは 8GB にしました

作成できたら「設定」から「ストレージ」で「コントローラ IDE 」が空になっているのを確認します
ここに事前にダウンロードしておいた ISO をマウントしておきます
kickstart2.png

あとは起動してコンソール画面から CentOS をインストールすれば OK です
設定する項目は殆どないと思います
自分は OS をインストールするパーティションと root のパスワードを設定しました

設定が完了したらインストールができるまで待ちましょう

※うまく NAT ネットワークに接続できない場合は Virtualbox の「環境設定」から「ネットワーク」で「NAT ネットワーク」と「ホストオンリーネットワーク」を追加しましょう
※ NAT 側がうまく接続できない場合は以下のコマンドを実行してみたください

  • nmcli con mod enp0s3 connection.autoconnect "yes"
  • systemctl restart NetworkManager
  • systemctl restart network

kickstart サーバの構築

では kickstart サーバを構築します
とは行っても https or https 経由で .ks ファイルを配信できる環境を構築すれば完了です

  • yum -y update
  • yum -y install httpd vim

ks ファイルの作成

次に .ks ファイルを作成します
各項目の設定はコメントで説明しています
必須のオプションもあればそうでないオプションもあります
別のオプションについては参考サイトにある RedHat の kickstart オプションの一覧を参照してください

  • vim centos.ks
# 新規インストールします
install

# CD にマウントしてある ISO からインストールします
cdrom

# 言語を設定します
lang ja_JP.utf8

# キーボードレイアウトを設定します
keyboard us

# タイムゾーンを設定します
timezone Asia/Tokyo

# 認証オプションを設定します (必須)
auth --useshadow --passalgo=sha512

# selinux を無効にします
selinux --disabled

# iptables を無効にします
firewall --disabled

# 自動起動するサービスを指定します
services --enabled=NetworkManager,sshd

# インストール後再起動させます
reboot

# ブートローダオプションを指定します (必須)
bootloader --location=mbr --driveorder=sda

# パーティションをクリアします
clearpart --linux --drives=sda

# パーティション作成で確認を求められないようにします、これを指定しないと ISO からインストールする画面のシステムのインストール先を選択する画面で止まってしまいます
zerombr

# パーティションを自動で設定します
autopart

# ルートパスワードを設定します
# 「openssl passwd -1」コマンドでパスワードは生成できます
rootpw --iscrypted $1$kuGjySFy$1EqDH0PdmNx7fyDBTvyHN.

%packages --nobase --ignoremissing
@core
%end

ks ファイルの配信

では作成した ks ファイルを配信します

  • cd /var/www/html
  • cp centos.ks .
  • systemctl httpd start

で curl などでアクセスしてファイルが取得できれば OK です

  • curl localhost/centos.ks

selinux, iptables firewalld の無効化

とりあえずテストなのでそれぞれ無効にしました

  • vim /etc/selinux/config
SELINUX=disabled
  • systemctl disable firewalld

動作確認

kickstart ファイルの配信準備ができたら、実際に CentOS のインストールを kickstart 経由で行ってみます

先ほどと同様に VM を 1 つ作成して ISO をマウントします
このとき先ほどとは違い kickstart サーバを指定します

タブを押すと以下のようにインストールコマンドを指定することができるので追加で kickstart サーバを指定します

inst.stage2 の設定項目がある場合は削除して OK です
192.168.56.101 はホストオンリーアダプターに付与されている先ほど構築して kickstart サーバの IP になります
kickstart3.png

これでインストールを開始してみましょう
kickstart サーバを構築したときは対話的にインストールしていたのがなくなりすべて自動でインストールされると思います

試しに kickstart ファイルを複数用意して lang などの項目を別なものに変更して起動後に $LANG などを確認してみると良いと思います

最後に

kickstart サーバを構築して CentOS のインストールの自動化を行ってみました
仕組み自体は設定ファイルを http で配信するだけなので簡単です
ただ、新たに構築する VM が kickstart サーバにアクセスできる必要があります
Github などで公開してグローバルからならどこでもつながるようにすることで解決する方法もあるようです

今回はパーティションなど細かい設定はほとんど行いませんでしたが、パーティションを細かく設定したり LVM 構成を組むこともできるようです
詳しくは RedHat の公式サイトにある kickstart のオプションを紹介しているドキュメントを確認してみてください

参考サイト

2017年9月6日水曜日

vSphere 環境でサクっとコンソール画面のスクリーンショットを取得する方法

概要

vSphere Client で VM のサマリ情報を確認するとコンソールのスクリーンショットを見ることができると思います
実はスクリーンショットだけをサクっと取得する方法があるので紹介します

環境

  • vCenter Server 5.5.0

取得方法

ブラウザで取得

以下のような感じで moid を指定してブラウザでアクセスすれば確認できます

curl で取得

認証は vSphere Client と同様ベーシック認証なので URL にそのまま含まています

curl で取得することもできます

  • curl -u vcenter-name:vcenter-password -k 'https://192.168.100.101/screen?id=vm-100' -o sc.png

png 形式で保存してそれを開けばスクリーンショットを確認することができます

HTML に組み込む

ブラウザ取得する方法を応用して HTML に組み込むこともできます

<html>
  <head></head>
  <body>
    <img src="https://vcenter-name:vcenter-password@192.168.100.101/screen?id=vm-100"></img>
  </body>
</html>

最後に

vSphere 環境で VM のスクリーンショットを取得する方法を紹介しました
実はちゃんと API が用意されており CreateScreenshot_Task を使うことができます
これを使ったほうが認証やプログラムとの親和性が高いのでプログラムから操作する場合はこれを使いましょう
とりあえずシェルスクリプトレベルであれば今回の手順でも良いかと思います

参考サイト

2017年9月5日火曜日

govmomi を使って VirtualMachine の moid を取得してみた

概要

govmomi を使って VM の情報を取得します
その際に ManagedObjectReference の moid を取得してみます

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

事前準備

ライブラリのインストールなどは過去の記事を参考にしてください
https://hawksnowlog.blogspot.jp/2017/04/try-vmware-govmomi.html

ソースコード

  • vim get_vm_moid.go
package main

import (
    "context"
    "flag"
    "fmt"
    "net/url"
    "os"

    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/find"
    "github.com/vmware/govmomi/vim25/types"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
)

var envURL = "https://192.168.100.101/sdk"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)

var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)

func main() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        u.User = url.UserPassword("vcenter-user", "vcenter-password")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        f := find.NewFinder(c.Client, true)
        dc, err := f.DefaultDatacenter(ctx)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        f.SetDatacenter(dc)
        fmt.Println(dc)

        // VM の検索
        pc := property.DefaultCollector(c.Client)
        refs := []types.ManagedObjectReference{}
        vss, err := f.VirtualMachineList(ctx, "/dc/vm/folder/folder2/target_vm")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, vs := range vss {
                fmt.Println(vs.Common.InventoryPath)
                refs = append(refs, vs.Reference())
        }
        var vdst []mo.VirtualMachine
        err = pc.Retrieve(ctx, refs, nil, &vdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, vs := range vdst {
        fmt.Println(vs.Self.Value) // <- これで moid を取得できる
                fmt.Println(vs.Summary.Guest.GuestId)
                fmt.Println(vs.Summary.Guest.GuestFullName)
                fmt.Println(vs.Summary.Guest.HostName)
                fmt.Println(vs.Summary.Guest.IpAddress)
                fmt.Println(vs.Summary.Config.Name)
                fmt.Println(vs.Summary.Config.VmPathName)
                fmt.Println(vs.Summary.Config.MemorySizeMB)
                fmt.Println(vs.Summary.Config.NumCpu)
        }
}

ポイントは最後の vs.Self.Value です
これで moid が取得できます
フィールド名が Value なので moid だと名前から推測できないのでわかりづらいです

参考サイト

2017年9月4日月曜日

yum コマンドが使えない場合にローカルに nc コマンドをインストール方法

概要

root 権限が与えられずサーバに yum install できない場合があると思います
そんな場合にホームディレクトリ配下に nc コマンドをインストールして使えるようにしてみます

環境

  • CentOS 6.3 Final
  • netcat 0.7.1

インストール方法

http://netcat.sourceforge.net/
から最新のソースを取得します
取得できたバージョンは netcat-0.7.1.tar.gz でした

あとは展開してビルドしてコンパイルするだけです

  • tar zvxf netcat-0.7.1.tar.gz
  • cd netcat-0.7.1
  • ./configure
  • make

で完了です
make install しても /usr/local 配下にコマンドを配置しようとしてエラーになるので実施しません

netcat コマンドは

  • netcat-0.7.1/src/netcat --help

で使えます
あとはフルパスで指定すれば .ssh/config などでも使うことができるようになると思います

ProxyCommand ssh excloudgate /home/user001/netcat-0.7.1/src/netcat %h %p

最後に

netcat をホームディレクトリ配下にインストールしてみました
今回は依存するパッケージがすでにあったので特に追加でインストール必要はなかったのですが、もしパッケージが足りない場合は rpm を持ってきたりソースをコンパイルしてインストールしたりと yak shaving する可能性は高いです、、、
あまりに面倒な場合は素直に諦めて別の方法を考えましょう

2017年9月3日日曜日

SpriteKit で Realm を使ってみる

概要

前回 SpriteKit で UserDefaults の使い方を紹介しました
実は UserDefaults には罠があってアプリを更新するとデータが消えてしまいます
なので、アプリが更新した際にデータが来ててほしくないようなデータを UserDefaults で管理すると大変なことになります
ということで今回は SpriteKit で Realm を使ってみました

環境

  • macOS X 10.12.6
  • Xcode 8.3.3 (8E3004b)
  • RealmSwift 2.8.3

ライブラリインストール

  • vim Podfile
pod 'RealmSwift'
  • pod install

Score.swift を作成

データを管理するクラスを作成します

import RealmSwift

class Score: Object{
    dynamic var name: String? = nil
    dynamic var score = 0
    dynamic var created = Date()
}

データを保存する関数をシーンファイルに追記

データを保存する関数を追加します
シーンファイルで定義するのが良いと思います

func insert() {
    let score: Score = Score()
    score.name = "hoge"
    score.score = 100
    score.created = Date()
    let realm = try! Realm()
    try! realm.write {
        realm.add(score)
    }
}

データを取得する関数をシーンファイルに追加

今度は取得する関数を定義します

func select() {
    let realm = try! Realm()
    let scoreList = realm.objects(Score.self).filter("score > 0").sorted(byKeyPath: "score", ascending: false)
    print(scoreList)
}

取得する条件はスコアが 0 以上かつスコアの大きい順となります
scoreList[0] とすれば最高スコアを取得することができます

ちなみに RealmSwift ではメソッドチェーンにより複数カラムでのソートはサポートしておらず例えば .sorted(byKeyPath: "created", ascending: false) を追加して作成日時でもソートしたいと思ってもできません

どうやらその場合は SortDescriptor を使ってソートするようです

追加した関数をコールする

あとはシーンファイルの touchDown などでノードを判定してコールするだけです

func touchDown(atPoint pos : CGPoint) {
    if let node = atPoint(pos) as? SKSpriteNode {
        if(node == show){
            if let _ = self.view {
                select()
            }
        } else if(node == save){
            if let _ = self.view {
                insert()
            }
        }
    }
}

動作確認

ちゃんとデータが保存されていると取得した際にコンソールに以下のようなデータ表示されるはずです

Results<Score> (
    [0] Score {
        name = hoge;
        score = 100;
        created = 2017-09-02 06:04:07 +0000;
    },
    [1] Score {
        name = hoge;
        score = 100;
        created = 2017-09-02 06:04:06 +0000;
    },
    [2] Score {
        name = hoge;
        score = 100;
        created = 2017-09-02 06:03:55 +0000;
    },
    [3] Score {
        name = hoge;
        score = 100;
        created = 2017-09-02 06:03:54 +0000;
    },
    [4] Score {
        name = hoge;
        score = 100;
        created = 2017-09-02 06:03:53 +0000;
    }
)

最後に

SpriteKit で Realm を使ってみました
結構簡単に使える感じです
気になったのはライブラリ自体のサイズがかなり大きそうなのでこれを使うだけで ipa ファイルの容量が増えそうです

SpriteKit では CoreData がなかなか難しいのでそういった意味では代替として Realm を採用するのには良いと思います

参考サイト