読者です 読者をやめる 読者になる 読者になる

gvm使ってCircleCIでGoのバージョン固定

目的

  • バージョンが勝手に変わると、アプリケーション動作の保障ができないので、任意のバージョンのgoをインスコして使う

対象環境

  • ビルド環境 (CircleCI)
  • 実行環境 (AWS EC2)

今回ビルド環境と実行環境を分ける予定であるため、その両方への作業が必要ですが、今回は、ひとつめの、CircleCIでの適用作業をします。

利用するライブラリ

gvm: https://github.com/moovweb/gvm

gvm使ってみる

gvmはgolangのバージョン管理ライブラリです。 あまり気に入ってないけど、他にいいやつあったらおしえてほしいです。

自分のローカルで先にやっても良かったのですが、まだVM環境になってないので、CicleCIのコンテナでためしました。

CircleCIはデバッグ用途で、30min限定でコンテナにsshできます。 ぶっ壊れてもいいので、今回の作業以外でも色々遊ぶといいです。

現在使っているシェルを確認

$ echo $SHELL

/bin/bash

現在使っているgoのバージョンを確認

$ go version

go version go1.6.2 linux/amd64

gvmをインストール

$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)

Cloning from https://github.com/moovweb/gvm.git to /home/ubuntu/.gvm Created profile for existing install of Go at "/usr/local/go" Installed GVM v1.0.22

Please restart your terminal session or to get started right away run source /home/ubuntu/.gvm/scripts/gvm

上記出力の通り、以下を読み込んであげましょう。

source /home/ubuntu/.gvm/scripts/gvm

インストールgo一覧の確認

$ gvm list

gvm gos (installed)

system

現状システムインストールされたgoだけが表示されています。CircleCIで使われてるイメージはデフォルトでgo入ってるので。すなわちsystemですね。

gvmで任意のバージョンのgoインストール

gvm install go1.4 -B

Installing go1.4 from binary source

ちゃんと入ったようなので、先ほどインストールしたバージョンを使います

gvm use go1.4 

Now using version go1.4

--defaultオプションをつけることもできます。たぶん基本的につけてつかうことになるでしょう。

goのバージョン確認

go version

go version go1.4 linux/amd64

ちゃんと1.4に変わってます。

元に戻す

gvm use system

ちなみにgvm経由でインストール可能なgoのバージョンの一覧は以下で得ることができます

gvm listall

バルスコマンド。インストールしたバイナリなどに加え、gvm自身も消え去ります。 けっきょく、${HOME}/.gvm (= ${GVM_ROOT}) をrm -rfすればおなじことです。

gvm implode

さて。もともと、やりたかったことは、CircleCIのビルド時のバージョン固定なので、これをcircle.ymlに落とし込みます。

CircleCIで任意のバージョンのgo使ってビルドする

https://circleci.com/docs/language-go/#environment

Since the install is in the default location, GOROOT is not set. If you install your own version of Go, make sure to set the location in GOROOT

任意のバージョン使いたければちゃんと$GOROOTセットしてねって書いてるけど、これはgvmが勝手にやってくれてました。

echo $GOROOT

これはありがたいのでしたが、しかし、とてもありがた迷惑だったのが、$GOPATHの変更まで勝手にやっちゃってくれます。

echo $GOPATH

e.g. /Users/a13533/.gvm/pkgsets/system/global

ここで結構はまります。 このGOPATHで頑張ってみてもいいのですが、circleciでは$HOME/.go_projectをGOPATHと扱うよう推奨してあります。推奨ではなくデフォルトですが、これをぼくは推奨として受け取ります。公式ドキュメントに沿っておいた方が後続のコーダーが理解しやすいとおもうので良。

https://circleci.com/docs/language-go/#environment

a symlink is placed to your project’s directory at /home/ubuntu/.go_project/src/github.com/\<USER>/\<REPO_NAME>

なので、gvmが決めるGOPATHにこちらが指定したいGOPATHを追加する方法で解決しました。

そもそもどこで環境変数を上書きしているのかを探します。結論は、当該シェルのrcファイルを改変してくれていたのでした。 bashインスコしたなら.bashrczshインスコしたなら.zshrcの末尾に以下が追記されていると思います。

[[-s "$HOME/.gvm/scripts/gvm"]] && source "$HOME/.gvm/scripts/gvm"

このなかを追って読むと、デフォルトのpkgsetに書かれてある環境変数がロードされていることがわかるとおもいます。 そこで、デフォルトのpkgsetの設定ファイル末尾に追記してGOPATHをoverrideします。

echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

これで、こちらが設定したGOPATH_ORGがGOPATHに追加されるようになります。 以下、今回やった範囲の設定です。

machine:
  environment:
    GOROOT: ""
    PATH: "/usr/local/go/bin:/usr/local/go_workspace/bin:~/.go_workspace/bin:${PATH}"
    GOPATH_ORG: "${HOME}/.go_workspace:/usr/local/go_workspace:${HOME}/.go_project"
    GVM_GOS: "go1.6.2"
  post:
    - bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) && source ${HOME}/.gvm/scripts/gvm
    - gvm install ${GVM_GOS} -B
    - gvm use ${GVM_GOS} --default 
    # `gvm` arbitrarily changes GOPATH value :( so forcibly override it with its original value.
    - echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

dependencies:
  pre:
    - go get github.com/golang/lint/golint
    - sudo add-apt-repository ppa:masterminds/glide -y && sudo apt-get update && sudo apt-get install glide -y
    - mkdir -p ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}
    - ln -sfnv ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}
  override:
    - glide install
    - cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go build -v -race -o build/output

test:
  override:
      - cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go test -v -race -cover `glide nv`

今回なかなか強引に対応しましたがほかにいいやりかたないかな。 ghq使っている自分としてはGOPATH問題がなかなかめんどうなためやりづらい。 普通に単体で使えばちゃんと便利なんだけど。

参考

AWS EC2にansibleでmysqlインストール

golangAPIサーバー構築のためにmysql使いたいからansibleでやってみるかってノリだけなので、簡易的にやります。 ansibleのaの字くらいでやります。nsibleは後で学ぶ。

このへんみながら

EC2

省略。無料のmicroインスタンスを立てる。

ansibleをインストール

ひとまず。

brew install ansible

鍵指定なしでsshできるようにしておく

pemファイルをダウンロードして~/.ssh/ec2test/に配置

キーを指定せずsshできるようにconfigに以下を追記。

Host 52.198.155.206
  User ec2-user
  IdentityFile ~/.ssh/ec2test/ec2test.pem

Inventory

[ec2-52-198-155-206.ap-northeast-1.compute.amazonaws.com]
52.198.155.206

疎通確認

~ ❯❯❯ ansible ec2-52-198-155-206.ap-northeast-1.compute.amazonaws.com -m ping                                                                                                     master
52.198.155.206 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

実行

ansible-playbook -i hosts site.yml

mysqlの起動時にエラー発生

MySQL server PID file could not be found!

とりあえず、エラーログ見てみる。

2016-10-14 05:15:55 12056 [ERROR] Fatal error: Can't open and lock privilege tables: Table 'mysql.user' doesn't exist

データ格納するディレクトリがどこかわからなくなってるので指定してあげる。

mysql_install_db --datadir=/var/lib/mysql --user=mysql

FATAL ERROR: please install the following Perl modules before executing /usr/bin/mysql_install_db: Data::Dumper

Perlのサブパッケージが足りないらしいのでこれもタスクに追記。 出力補助系のライブラリみたい。

そして、最後。

ansibleのmysqlタスク実行時にエラー発生

msg: the python mysqldb module is required というエラーメッセージが出る。

これ、MySQL-pythonをインストールすればokらしいけど、インストールしてなお止まない。

結構はまって、ようやく解決。 http://akira-junkbox.blogspot.jp/2016/04/ansiblemsg-python-mysqldb-module-is.html

インストールしたにもかかわらず、msg: the python mysqldb module is requiredが出続ける場合、サーバ上のPythonのバージョンを確認してください。 実はOSによっては、Pythonのバージョンによって、MySQL-Python27のようにパッケージ名が異なるものがあります。

ということでした〜。

無事、すべてのタスクが通ったら、 当該ec2インスタンスsshして、

mysql -u root testuser -ptestpass

でWelcome~

以下、修正された後の最終的なメインファイル。

---
- hosts: ec2-52-198-155-206.ap-northeast-1.compute.amazonaws.com
  #user: ec2-user
  sudo: yes
  vars:
    mysql_url: http://dev.mysql.com/get/Downloads/MySQL-5.6
    mysql_ver: "5.6.26-1"
    mysql_items:
      - rpm: MySQL-client-{{ mysql_ver }}.el6.x86_64.rpm
      - rpm: MySQL-devel-{{ mysql_ver }}.el6.x86_64.rpm
      - rpm: MySQL-server-{{ mysql_ver }}.el6.x86_64.rpm
      - rpm: MySQL-shared-{{ mysql_ver }}.el6.x86_64.rpm
      - rpm: MySQL-shared-compat-{{ mysql_ver }}.el6.x86_64.rpm
  tasks:
    - name: install mysql
      yum: name={{ mysql_url }}/{{ item.rpm }} state=present
      with_items: mysql_items
    - name: install dependency packages
      yum: name={{ item }} state=present
      with_items:
        - perl-Data-Dumper
        - MySQL-python27
    - name: setup mysql datadir
      shell: mysql_install_db --datadir=/var/lib/mysql --user=mysql
    - name: start mysql
      service: name=mysql state=restarted enabled=yes
    - name: get mysql password
      shell: cat /root/.mysql_secret | awk '{print $18}'
      register: mysql_root_password
    - name: copy .my.cnf
      template: src=.my.cnf.j2 dest=/root/.my.cnf owner=root mode=0600
    - name: setup mysql database
      mysql_db: name=testdb state=present
    - name: setup mysql user
      mysql_user: name=testuser password=testpass priv=*.*:ALL,GRANT state=present

今回はいきなり実践的に使ってみたなので、別途ansible基本から勉強すること。

Vim歴2年の自分の成長止まりを憂う

vim使い始めて2年くらい経つけど、結構クセついてきてしまいたまに焦りがきます。

例えば、diw -> i とやり慣れてる人が、 ciwに移行するのって意識して努力しないと変わらない。

以下は、僕自身が習得しなければならない、より便利な機能をリスト化してみました。結構前にまとめたので、もう改善済みのも結構ある。

とりあえず、最近vim使えてきたな〜って人は、t/f,T/Fの高速移動やText Objectを覚えると、さらに1.1倍くらい楽しくなります。

Keys

key action
Ctrl + j,k,h,l インサートモード中に移動
ca" "ごと"内を削除
ge 前の単語の末尾に移動
S 現在の行を消してインサートモード
s カーソル下の一文字を消してインサートモード
C カーソルより右を消してインサートモード
ZZ :wqと同じ
dG 現在行以下をすべて削除
Ctrl + p インサートモード時に前方単語補完
Ctrl + n インサートモード時に後方単語補完
:r! + CMD コマンド実行結果を貼り付け
gJ スペースを含まず行結合
5J 現在行以下5行を一括行結合
]m 次のメソッドの始めに移動
]M 次のメソッドの終わりに移動
[m 前のメソッドの始めに移動
]M 前のメソッドの終わりに移動
{ 前の空行へ移動
} 次の空行へ移動
% 対応する括弧や予約語に移動
* カーソル下の単語を後方検索
# カーソル下の単語を前方検索
g* カーソル下の単語を後方部分検索
g# カーソル下の単語を後方部分検索
+ 下の行頭に移動
- 上の行頭に移動
g; 直前の変更箇所に戻る
g, 直前の変更箇所に進む
:noh 検索結果のハイライトを解除
W,B,E,gE
q + 文字 レコード開始(qで終了)
@ + 文字 レコード再生
q: コマンド履歴を表示
q/, q? 検索履歴を表示
gg=G 全行インデント(V+G+=と動きは同じ)
R 上書きモード
Ctrl + [ コマンドモードに戻る(=esc)
Ctrl + z 一時停止(vimで戻る)
yy5p 行をコピーし、下に5行分ペースト
gv 直前の選択を再選択
ma マークaをつける
`a マークaに移動
'a マークaのある行の先頭に移動
y`a 現在のカーソル位置からマークaまでをコピー
v`a 現在のカーソル位置からマークaまでを選択
:marks マーク一覧を表示
"ayy レジスタaにコピー(aをAにすると現在保持しているテキストの末尾に追加される)
"ap レジスタaをペースト
:reg レジスタ一覧を表示
:reg a b レジスタa,bを表示
@: 直前に実行したコマンドを実行
:g/^$/d 空行削除
u 小文字に変換
U 大文字に変換
~ 小文字は大文字に、大文字は小文字に変換
gQ Exモード(連続でコマンドを使用できる)
:e ファイルを開く
:r ファイルを読み込み現在編集中のファイルにペースト
gt 次のタブに切替
gT 前のタブに切替

Text Object

dcと組み合わせて使用する。

key action
iw 単語(以降iの部分はaでも代用可能)
iW 単語(空白含む)
is
it タグ
i] 括弧("",'',(),<>,{})なども対応可能)

以上。 他におすすめあったら是非教えて欲しいです!

以下は、おまけ。 vimの標準ファイルツリー。

netrw.vim

Vimの便利なファイルブラウザ。

Keys

key action
:h netrw 使い方をみる
:Explore(Ex)[!] [dir] カレントファイルのディレクトリを開く。:e .でも同じ。
:Hexplore[!] [dir] 水平分割 & Explore
:Nexplore[!] [dir] 垂直分割 & Explore
:Pexplore[!] [dir] 垂直分割 & Explore
:Rexplore Explorer に戻る
:Sexplore[!] [dir] ウィンドウ分割 & Explore
:Texplore[!] [dir] タブ & Explore
:Vexplore[!] [dir] 垂直分割 & Explore
i 表示方法を切り替える (thin, long, wide, tree)
s ソート方法を変える: name, time, file size
r ソート順を逆にする
a 隠しファイルの表示を切り替える
gh ドットファイルの表示/非表示を切り替える
p ファイルをプレビューする
qf ファイル情報を表示する
- 一つ上のディレクトリに移動する
u 最近訪れたディレクトリに移動する(undo的な)
U 次に訪れたディレクトリに移動する(redo的な)
t カーソル位置のファイル/ディレクトリを新しいタブで開く
o カーソル位置のファイル/ディレクトリを新しいウィンドウで開く。水平分割。
v カーソル位置のファイル/ディレクトリを新しいウィンドウで開く。垂直分割。
gt 次のタブに切替
gT 前のタブに切替
P 直前に使用していたウィンドウで開く
d ディレクトリを作成する
D ファイル/ディレクトリ(複数可)を削除する(マークしたファイルがある場合には、マークしたファイルを削除)
R ファイル/ディレクトリ(複数可)の名前を変える
mf ファイルをマーク/アンマークする
md マークしたファイルの差分を表示する (3ファイルまで)
mz マークしたファイルを圧縮/伸長する
mr シェル形式正規表現 (regexp) でファイルをマークする
mt 表示中のディレクトリをターゲットディレクトリにする
mc マークしたファイルをターゲットディレクトリにコピー
mm マークしたファイルをターゲットディレクトリに移動
mv マークしたファイルに対して任意のVimコマンドを実行
mx マークしたファイルに対して任意のシェルコマンドを実行
mX マークしたファイルに対して任意のシェルコマンドを一括実行
mu すべてのマークを解除する
mb カレントディレクトリをブックマークする
qb ブックマークと履歴の一覧を表示する
gb 前のブックマークされたディレクトリに移動

インタフェースと抽象クラスどっち使ったらいいんだ?

どういうケースでどちらを使うかとか知りたかった。 まず結論からいうと多様な議論があるようだった。 だからこれを読んだかたは鵜呑みにせずにいっしょに考えてもらいたい。

インタフェースと抽象クラス

結論、インタフェースって?

クラスの型(仕様)を定義するもの。 カプセル化多態性を要求する意味あいが強い。

結論、抽象クラスって?

継承関係をもつ実装の再利用をできる。 継承多態性を要求する意味あいが強い。

インタフェースとは

  • インタフェースのメンバ変数は必ず定数。自動でfinal public staticとなる。
  • 抽象メソッドのみ記述可能。自動でpublic abstractとなる。
  • 実装クラスは、全ての抽象メソッドを実装する必要ある
  • 多重継承できる。ミックスインというらしい。
  • 多重継承できるけど、メンバ変数は一意でなければならん
  • メソッドの場合は実装をもたないのでダブってても競合せず問題なし
  • 実装クラスはアップキャストしてインタフェース型変数に代入可能

抽象クラスとは

  • 抽象メソッドは必ず子クラスで実装されねばならない
  • 実装を持つメソッドも記述できる
  • クラスなので単一継承のみ。多重継承は不可
  • 共通機能を実装し、個別機能は抽象定義しておく
  • 抽象クラス使わないで継承するとアップキャストしたときにメソッドが使えなくなるとか起こりうる

結局どっちを使えばいいのか?

クラス仕様としての型定義したいならインタフェース。 継承関係にあって処理の再利用したいなら抽象クラス。

ってかんじか。

抽象クラスと派生クラスは「継承」の関係はIS A関係と呼ばれている対して、 インターフェイスと実装クラスの「実装」の関係はCAN DO関係と呼ばれているそうです。

(たぶん)たとえば、

public abstract class Animal {}
public class Rabbit extends Animal {}

はよい。Rabbit is an Animalであるから。=な関係。

public interface Jumpable {}
public class Rabbit implements Jumpable {}

はよい。Rabbit can jumpであるから。≠の関係。

超個人的感覚的な説明になるが、

実装クラスからみて、 抽象クラスは垂直に上な関係。 インタフェースは斜め上な関係、

はて。

参考URL

LVM作成してスナップショット機能を検証する

LVM作成してスナップショット機能を検証する

環境

VMWare, CentOs6

参考

検証内容

ゴール

LVM snapshot機能の確認

プロセス

  • CentOSVMに5GBのHDDを2つ追加する
  • 追加した2台のHDDを、LVMを利用して2GBと6GBの論理ボリュームを作成する
  • LVMスナップショット機能を利用し、/shareの静止点を作ったのうちtarコマンドでバックアップを取得する

    1. /shareにファイル用意
    2. スナップショット作成
    3. /shareのデータを追加、変更
    4. バックアップ作成(tar,rsync,cpio,,,)
    5. 1の時点のデータであることを確認

手順

デバイス登録

仮想マシンの設定でHDを2つ追加する。

追加してリブートしてでデバイスが認識されていることを確認。

$ fdisk -l

sdb, sdcが追加されている。

追加したデバイスをパーティションに登録。

$ fdisk /dev/sdb
$ fdisk /dev/sdc

LVM作成

PV作成

物理ボリュームを作成。

$ pvcreate /dev/sdb1
$ pvcreate /dev/sdc1

確認。

$ pvdisplay

VG作成

vg1というボリュームグループを作成。

$ vgcreate Vg1 /dev/sdb1 /dev/sdc1

確認。

$ vgdisplay

LV作成

vg1から2GBのlv1という論理ボリューム,6GBのlv2という論理ボリュームをそれぞれ作成。

$ lvcreate -L 2G -n lv2 vg1
$ lvcreate -L 6G -n lv1 vg1

確認。

$ lvdisplay // or lvscan

ファイルシステム構築。

mkfs.ext3 /dev/vg1/lv1
mkfs.ext3 /dev/vg1/lv2

適当なディレクトリにマウント

mkdir /data
mount -t ext3 /dev/vg1/lv1 /data

mkdir /share
mount -t ext3 /dev/vg1/lv2 /share

スナップショット作成

lv2論理ボリュームで、32MB容量のlv2ss1というスナップショットを作成。

lvcreate -s -L 32M -n lv2ss1 /dev/vg1/lv2

Bonding構築

参考

[VMWare] VMWareのVMのNICの増設方法(CentOSの場合) - eji著

意外と知らない?NICを冗長化するボンディング(bonding) - うさぎ文学日記

CentOS/NICの冗長化(bonding設定) - maruko2 Note.

http://open-groove.net/linux/linux-bonding-fail-over-test/

環境

VMware, CentOS6

目標

2NICに1IPを割り当てる

手順

現状確認

ifconfig

ネットワークアダプタがいまんところeth0だけであることを確認。

ネットワークアダプタを追加

VMWare work stationにて、ネットワークアダプタを2個増設する。

設定>デバイス>「追加」>ネットワークアダプタ

ブリッジを選択して完了

再起動して再度 ifconfig

eth1とeth2が増えていることを確認。

問題なく通信できることを確認

ping yahoo.co.jp

NICの設定

以下の設定ファイルを作成/変更。

# /etc/sysconfig/network-scripts/ifcfg-bond0

DEVICE=bond0
BOOTPROTO=none
ONBOOT=yes
NETWORK=192.168.1.0
NETMASK=255.255.255.0
IPADDR=192.168.1.11

# /etc/sysconfig/network-scripts/ifcfg-eth1

DEVICE=eth1
BOOTPROTO=none
ONBOOT=yes
MASTER=bond0
SLAVE=yes

# /etc/sysconfig/network-scripts/ifcfg-eth2

DEVICE=eth2
BOOTPROTO=none
ONBOOT=yes
MASTER=bond0
SLAVE=yes

VM再起動

これでいちおう完了。

確認

cat /proc/net/bonding/bond0

または、

ifconfig

RAID5構築

参考

CentOSでソフトウェアRAIDの構築 - maruko2 Note.

環境

VMWare, CentOS6

目標

SCSI ハードディスクを 3個増設し、その 3個のディスクを使ってRAID アレイを構築する

手順

仮想HDD追加

VMWare work stationにて、 SCSI ハードディスクを 3個増設する。

設定>デバイス>「追加」>ハードディスク

すると、/dev/にsdb, sdc, sdd,,,とディスクが追加されている。

RAID用にパーティション作成

fdisk /dev/sdb

:w でEnter.

他二つsdc, sddも同様に。

RAIDアレイ作成

mdadm --create /dev/md0 --level=5 --raid-devices=3 /dev/sd[bcd]

構築完了まで時間がかかるので、プロセスを確認。

cat /proc/mdstat

RAIDアレイの設定

/etc/mdadm.confRAIDアレイの情報を記述する

mdadm --detail --scan >> /etc/mdadm.conf

RAIDアレイにファイルシステムを作成

mkfs -t ext3 /dev/md0

RAIDアレイをマウント

/dev/md0 デバイスを、例えば /data ディレクトリにマウントする。

/etc/fstab を編集

/dev/md0  /data  ext3  defaults 1 2

/data ディレクトリを作成し、mount する。

mkdir /data

mount -a

アンマウント。

umount /data

マウント状況の確認

mount -v

とか

df