AnsibleでMySQLの環境を作成する(おまけ付き)
やりたいこと
Ansibleでこの辺を自動化する
- MySQLのインストール
- DBユーザの作成
- DBの作成
- 別のDBからデータを取得してリストア
実施環境
- OS: OS X Yosemite (10.11.6)
- Vagrant: 1.8.5
- Virtualbox: 5.1.6 r110634
- Ansible: 2.1.2.0
1. 各ツールの準備(インストール済みの場合不要)
(省略) この辺に書いてあります
step1. Homebrewをインストールする
step2. Homebrew Caskをインストールする
step3. VirtualBoxをインストールする
step4. Vagrantをインストールする
step5. Vagrant-vbguest pluginをインストールする
step6. Ansibleをインストールする
2. Vagrantfileの作成
ここまででこんな感じ
Vagrant.configure("2") do |config|
config.vm.box = "puppetlabs/centos-6.6-64-nocm"
config.vm.box_url = "https://atlas.hashicorp.com/puppetlabs/boxes/centos-6.6-64-nocm/versions/1.0.3/providers/virtualbox.box"
config.ssh.insert_key = false
config.hostmanager.enabled = true
config.hostmanager.manage_host = true
# create db server
config.vm.define :"db" do |host|
host.vm.hostname = "db"
host.vm.network :private_network, ip: "192.168.34.21", netmask: "255.255.255.0"
host.vm.network :private_network, ip: "192.168.33.21", virtualbox__intnet: "mv"
# ansible
host.vm.provision "ansible" do |ansible|
ansible.playbook = "provisioning/dbservers.yml"
ansible.inventory_path = "provisioning/hosts"
ansible.limit = 'all'
end
end
end
3. Ansibleのタスクを作成
step1. ベストプラクティスを参考にディレクトリとファイルを作成する
roles/
hosts
dbservers.yml
common/
tasks/
main.yml
database/
handlers/
main.yml
tasks/
main.yml
mysql.yml
restore.yml
vars/
main.yml
4. AnsibleのPlaybookを記述する
step1. hosts
[dbservers] 127.0.0.1 ansible_ssh_private_key_file=.vagrant/machines/db/virtualbox/private_key ansible_ssh_user=vagrant
step2. dbservers.yml
---
- hosts: dbservers
become: true
roles:
- common
- database
step3. common/tasks/main.yml
--- - name: change timezone command: cp -p /usr/share/zoneinfo/Japan /etc/localtime - name: remove all rules from iptables command: /sbin/iptables -F - name: iptables stop service: name=iptables state=stopped - name: iptables off command: /sbin/chkconfig iptables off
step4. database/handlers/main.yml
--- - name: enable mysql launch settings command: /sbin/chkconfig mysqld on
step5. database/tasks/main.yml
--- - include: mysql.yml - include: restore.yml
step6. database/tasks/mysql.yml
---
- name: MySQL5.1(デフォルト)削除
yum: name=mysql* state=absent
- name: install repository
yum: name=http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm state=present
- name: MySQL5.6インストール
yum: name={{ item }} state=present
with_items:
- mysql
- mysql-devel
- mysql-server
- mysql-utilities
- MySQL-python
notify:
- enable mysql launch settings
- name: start MySQL
service: name=mysqld state=started enabled=yes
- name: データベース作成
mysql_db: db={{ item }} state=present encoding=utf8
with_items: "{{ dbnames }}"
- name: データベースのユーザ作成(権限も付与する)
mysql_user: >
name={{ dbuser }}
password="{{ dbpass }}"
host={{ item[0] }}
priv={{ item[1] }}.*:ALL
append_privs=yes
state=present
with_nested:
- "{{ hosts }}"
- "{{ dbnames }}"
step7. database/tasks/restore.yml
---
- name: ダンプファイル作成(データは別のDBから取ってくる)
mysql_db: >
login_host={{ remotehost }}
login_user={{ dbuser }}
login_password={{ dbpass }}
name={{ item }}
target=/tmp/{{ item }}.dump
state=dump
with_items: "{{ dbnames }}"
- name: リストア
mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
with_items: "{{ dbnames }}"
- name: ダンプファイル削除
file: path=/tmp/{{ item }}.dump state=absent
with_items: "{{ dbnames }}"
step7. database/vars/main.yml
--- dbuser: <DBユーザ名> dbpass: <DBユーザのパスワード> remotehost: <データ取得元のIP> hosts: - "localhost" - "192.168.33.20" dbnames: - <restoreするDB名> - <restoreするDB名>
5. 動作確認
step1. 仮想マシンを作成する
$ vagrant up
step2. 動作確認
DBに接続できればOK
6. おまけ1 --no-data(-d)を併用する
mysql_dbモジュールはdumpのオプションが指定できないようなのでshellで実行する
!commandだと>が使えないので注意
- name: create no-data dump files
shell: mysqldump -h {{ remotehost }} -u {{ dbuser }} -p{{ dbpass }} {{ item }} -d > /tmp/{{ item }}.dump
with_items: "{{ nodatadbs }}"
リストアとかする時は「Jinja2 filters」のunionを使うと良い感じに書けます
- name: restore databases
mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
with_items: "{{ dbnames | union(nodatadbs) }}"
7. おまけ2 Viewを含むDBに対応する
対象のDBにViewが含まれている場合、DEFINERの設定によってはアクセスできなくなるので、リストア前に置換しておく(置換しても問題ない場合に限る)
- name: edit dump files
replace: dest=/tmp/{{ item }}.dump regexp='<置換前>`@`%' replace={{ dbuser }}`@`localhost
with_items: "{{ dbnames }}"
8. おまけ3 Windowsに対応する
WindowsにはAnsibleがインストールできないのでVM側で実行する
step1. $scriptを追記
$script = <<SCRIPT
if ! [ `which ansible` ]; then
yum update -y
yum install -y http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/epel.repo
yum install --enablerepo=epel -y ansible
fi
ansible-playbook -i /vagrant/provisioning/hosts /vagrant/provisioning/dbservers.yml
SCRIPT
step2. provisionを編集する
編集前
host.vm.provision "ansible" do |ansible| ansible.playbook = "provisioning/dbservers.yml" ansible.inventory_path = "provisioning/hosts" ansible.limit = 'all' end
編集後
config.vm.provision "shell", inline: $script
mysql_dbモジュール便利ですね。
【Android】角丸ボタンにRipple Effectをつける
1. 角丸ボタン用のxmlを作る
drawable配下に角丸ボタン用のxmlを作成する。
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/colorWhite"/> <stroke android:width="@dimen/line_small" android:color="@color/colorGrey"/> <corners android:radius="@dimen/corner_radius_normal"/> </shape>
2. Ripple Effect用の色を定義する
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorWhite.focused">#CCCCCC</color> </resources>
3. Ripple Effect用のxmlを作成する
drawable配下にRipple Effect用のxmlを作成する。
<?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/colorWhite.focused"> <item android:drawable="@drawable/button_bg_white"/> </ripple>
4. ボタンを作成する
ボタンの背景に作成したRipple Effect用のxmlを設定する。
<Button android:background="@drawable/button_frame_white" />
【CakePHP】リダイレクト先を動的に設定する
動作環境が結構古くて2.5.6なので3系でもそのままいけるかは不明です。
- メルマガ等にコンテンツのリンクを貼る
- 未ログインの場合はログイン画面に飛ばす
- ログイン完了後にコンテンツに飛ばす
といった動きをしたい時なんかに使えます。
1. リダイレクトURL(パス)を保存するfunctionを用意
親クラス(AppControllerとか)に実装すると良さげです。
private function storeRedirectPath() {
// このパスは保存しない
$exclude = ['/login', '/aaa', '/bbb'];
$currentPath = Router::url();
if (!in_array($currentPath, $allow)) {
$this->Auth->redirectUrl($currentPath);
}
}
2. 1.のfunctionを呼び出す処理を追加
このへん
public function beforeFilter()
{
$this->storeRedirectPath();
}
あるいは
public function beforeRender()
{
$this->storeRedirectPath();
}
※この辺のfunctionは初期表示時以外にも呼ばれるので確認してからの方が良いです。
3. 保存したパスにリダイレクトする処理を追加
protected function redirectToStoredPath() {
$this->redirect($this->Auth->redirectUrl());
}
public function index()
{
// ログイン済みの場合はリダイレクト
if ($this->Auth->login()) {
$this->redirectToStoredPath();
}
}
【PostgreSQL】1:Nのテーブルのカラムを配列で取得する
SQL一発で取れるのは良いですね。
SELECT id, column1, column2, array(SELECT column3 FROM table2 WHERE table2.table1_id = table1.id) as culumn3 FROM table1;
【PostgreSQL】特定のカラムに一括で同じ文字列操作をする小技
使う機会はあまりなさそうだけど、せっかくなのでメモ。
なんらかの理由で一時的にキーワードを追加したい時とかに使えそう。
- 同じ文字列を追加する
UPDATE table1 AS t1 SET column1 = '追加する文字列 ' || t2.column1 FROM (SELECT id, column1 FROM table1) AS t2 WHERE t1.id = t2.id
- 追加した文字列を削除する
UPDATE table1 AS t1 SET column1 = t2.column1 FROM (SELECT id, REPLACE(column1, '追加する文字列 ', '') FROM table1) AS t2 WHERE t1.id = t2.id
t2の抽出に条件を追加すれば、もっと細かい指定も可能。
StoryboardでTableViewの区切り線を左に詰める
Storyboardだけでできたのでメモ。
1. SeparatorのInsetsを変更する。
Custom Insetsにする

Leftを0にする

2. Layout Maginsを変更する
Explicitにする

Leftを0にする

変更前

変更後

xibを使って簡易ローディングを作成する
今回の成果物
こちらに置いてあります
negibouze/SimpleLoadingSample
1. LoadingView.xibを作成する
1. 背景を設定する
alphaで透過させるとsubviewも一緒に透過されてしまうので、opacityで透過させる。

2. Activity Indicatorを配置する
中心に来るようにする。

2. LoadingView.swiftを作成する
class LoadingView: UIView { @IBOutlet var contentView: UIView! @IBOutlet weak var indicator: UIActivityIndicatorView!
3. .xibと.swiftを紐づける
1. File's OwnerにLoadingView.swiftを設定する

2. 各要素を紐づける

4. LoadingViewを実装する
class LoadingView: UIView { private static let sharedInstance = LoadingView() private var isShow = false private override init(frame: CGRect) { super.init(frame: (UIApplication.sharedApplication().delegate?.window??.bounds)!) self.setup() } class func show(duration: Double = 0.5) { if sharedInstance.isShow { return } guard let win = UIApplication.sharedApplication().delegate?.window, let window = win else { return } sharedInstance.isShow = true sharedInstance.indicator.startAnimating() window.addSubview(sharedInstance) UIView.animateWithDuration(duration, animations: { sharedInstance.alpha = 1.0 }, completion: nil ) } class func hide(duration: Double = 0.5) { if !sharedInstance.isShow { return } UIView.animateWithDuration(duration, animations: { sharedInstance.alpha = 0 }, completion: { b in sharedInstance.removeFromSuperview() sharedInstance.indicator.stopAnimating() sharedInstance.isShow = false } ) }