ブログ移転しました。
移転先はこちら。
↓
https://blog.kunst1080.net/
もしお気に入り登録している人がいれば、URLの変更をお願いします。
なお、はてなブログの方は今の所、2020年12月ごろに削除する予定です。
sbtの~/.ivy2の場所を変更する
sbtの起動オプションで指定できるらしい。
たとえばsbtで使用する ~/.sbt
をカレントの .ivy2
に変更したい場合、以下のように環境変数をセットして実行する。
export SBT_OPTS="-Dsbt.ivy.home=.ivy2" sbt
マニュアルを見てみると、 ~/.sbt/boot
とかも変更できるみたい。
まれに変更したくなるタイミングがあるので忘れる前にメモ。
参考URL
AnsibleのDocker Connection Pluginを使って空ファイルをコピーするとジョブがたまにハングする問題
ためした環境
- 実行元
- macOS High Sierra 10.13.6
- Ansible 2.4.3.0
- Python 2.7.10
- 対象サーバー
- Docker for Mac上のコンテナ
- Centos 6.10
- AmazonLinux
- Docker for Mac上のコンテナ
再現手順
1. 空ファイルを作成
$ touch empty
2. site.yml を以下の内容で作成
- hosts: all connection: docker tasks: - name: copy empty file copy: src: empty dest: /empty - name: remove empty file file: state: absent path: /empty
3. 検証スクリプトを以下の内容で作成
test.sh
#!/bin/bash set -eu NAME=$(docker ps -l --format '{{.Names}}') cat <<++EOS>hosts [all] $NAME ++EOS for ((i=0; i<100; i++)); do ansible-playbook -i hosts site.yml done
4 対象サーバーのdockerコンテナを起動
$ docker run -it --rm centos:6.10 bash
5. 4.とは別のターミナルで検証スクリプトを起動
$ ./test.sh
_人人人人人人人人人_
> そのうち止まる <
 ̄YYYYYYYY ̄
止まったときの状態
コンテナのプロセス
# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 11500 2652 pts/0 Ss 15:31 0:00 bash root 2268 0.0 0.0 8396 556 ? Ss 15:33 0:00 dd of=/root/.ansible/tmp/ansible-tmp-1536593616.23-267547494832017/source bs=65536 root 2279 0.0 0.0 13380 1872 pts/0 R+ 15:34 0:00 ps aux
こんな感じで、dd
コマンドで停止しているように見えます。
ansible-playbookの詳細ログ
-vvv
オプションを付けて再実行してみます。
$ ansible-playbook -vvv -i hosts site.yml
ログを全部貼ると長いので、停止した箇所付近だけ貼り付けます。
TASK [copy empty file] ************************************************************************************************************************************************** task path: /Users/kunst/work2/ansible-test/site.yml:4 <goofy_goldstine> ESTABLISH DOCKER CONNECTION FOR USER: root <goofy_goldstine> EXEC ['/usr/local/bin/docker', 'exec', '-i', u'goofy_goldstine', u'/bin/sh', '-c', u"/bin/sh -c 'echo ~ && sleep 0'"] <goofy_goldstine> EXEC ['/usr/local/bin/docker', 'exec', '-i', u'goofy_goldstine', u'/bin/sh', '-c', u'/bin/sh -c \'( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844 `" && echo ansible-tmp-1536593766.1-91378099171844="` echo /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844 `" ) && sleep 0\''] Using module file /Users/kunst/Library/Python/2.7/lib/python/site-packages/ansible/modules/files/stat.py <goofy_goldstine> PUT /var/folders/ys/535_fggj5fb3k98v2xz06b0h0000gn/T/tmpEojaf5 TO /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844/stat.py <goofy_goldstine> EXEC ['/usr/local/bin/docker', 'exec', '-i', u'goofy_goldstine', u'/bin/sh', '-c', u"/bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844/ /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844/stat.py && sleep 0'"] <goofy_goldstine> EXEC ['/usr/local/bin/docker', 'exec', '-i', u'goofy_goldstine', u'/bin/sh', '-c', u"/bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844/stat.py && sleep 0'"] <goofy_goldstine> PUT /Users/kunst/work2/ansible-test/empty TO /root/.ansible/tmp/ansible-tmp-1536593766.1-91378099171844/source
原因調査
最初は copy
モジュールが怪しいなぁと思って site-packages/ansible/modules/files/copy.py
あたりをデバッグしていたのですが、どうやらその前に止まっている模様……
もしやと思い、Docker Connection Pluginのソースを見てみると、そのもの dd
コマンドを投げている箇所がありました。
github.com
そのままissueを検索してみると、bugでissueが立っているのを発見。 github.com
あー、これですね。ログの出方も同じ。
応急処置
- 空ファイルをコピーせず、
touch
コマンドなどで作成する - 空ファイルじゃなければいいので、適当に半角スペースや改行などを入れて1バイトにする
開発環境をセットアップするAnsible Playbookを作成したよ
前回の続き。
VPS上で生活しよう!ということなので、開発用の各種パッケージをインストールしたり設定ファイルをセットアップしたりするPlaybookを作成しました。
今回は量が多く何度もやり直しをしたのと、今後もちょこちょこメンテして再実行をしたいので、冪等性を保つような作りにしています。
軽い解説
Roleについて
今回は Role の機能を使ってみました。 Roleを使ってセットアップ内容を以下のように分割しました。
- common - 共通で使うパッケージのインストール
- dotfiles - dotfilesのインストール
- vim - Vimプラグインマネージャ(Dein)のインストール
- linuxbrew - linuxbrewのインストールと、brewを使ったソフトウェアのインストール
- nodejs - nodebrewのインストール
- rbenv - rbenvのインストール
- python - venvをPythonの環境設定
……なんというか、ほぼ docker-desktop でやっている内容の移植です。
処理の共通化
また、セットアップ処理には同じような作業が多いため、処理を共通化しました。
lib
ディレクトリ以下にタスクをまとめたモジュールを作成し、include
して使うようにしてみました。
これらのモジュールを使うことで、 tasks
には処理の順番を、 vars
には処理の中身やパッケージのリストを宣言するという作りで Playbook を作成できるようになりした。
ControllerとModelに分割したようなイメージです。
- apt-install.yml - 変数
packages
に宣言されているパッケージをapt
でインストールする - execute-commands.yml - 変数
commands
に宣言されているコマンドを、ログインシェルで実行する - get-url.yml - 変数
`remote_urls
に宣言されている url からファイルをダウンロードする - git-clone.yml - 変数
remote_repos
に宣言されているリポジトリをgit clone
する - make-directories.yml - 変数
dirs
に宣言されているディレクトリを作成する - set-environments.yml - 変数
envs
に宣言されている環境変数を~/.bash_profile
に追記する
ちなみにこのやり方はAnsible Galaxyをちょっと真似したような感じです。
例えばわかりやすい例だと、 rbenv
の場合、
- vars - roles/rbenv/vars/main.yml
dir: "{{ cache }}/rbenv" remote_repos: - repo: https://github.com/sstephenson/rbenv.git dest: "{{ dir }}" - repo: https://github.com/sstephenson/ruby-build.git dest: "{{ dir }}/plugins/ruby-build" packages: - libssl-dev - libreadline-dev - zlib1g-dev commands: - "eval '$(rbenv init -)'" - rbenv install -v 2.4.1 - rbenv rehash - rbenv global 2.4.1 envs: - "# RBENV" - "export RBENV_ROOT={{ dir }}" - export PATH=$RBENV_ROOT/bin:$PATH - 'eval "$(rbenv init -)"'
- tasks - roles/rbenv/tasks/
- name: check rbenv shell: "bash -lc 'which rbenv'" register: exists failed_when: false - block: - include: lib/apt-install.yml - include: lib/git-clone.yml - include: lib/set-environments.yml - include: lib/execute-commands.yml when: exists.rc != 0
と書けます。
軽いまとめ
こんな感じで環境を用意できたので、しばらくはブラウザの上で生きていきます。
Cloud9をインストールするAnsible Playbookを作成したよ
前回の続き。
なぜVPSをセットアップしているかというと、Cloud9の環境が欲しかったからなのです。 AWSで使うと高額なので、Conohaで動かそうということなのです。
というわけで、Cloud9のセットアップ手順を Playbook にまとめました。 今回のは短いし何度も使うようなものでもないので、冪等性はないです。
user
や port
は適当に変えて〼
参考URL
Ansible入門しました
今まで食わず嫌いをしてしまっていたAnsibleに、今更ながら1入門しました。
使う前に抱いていた印象と実際に使ってみた感想を並べてみて、最後に書いてみたPlaybookを載せます。 使う前はあまりいいイメージではなかったのですが、使ってみるとなかなか便利では……という使用感でした。
使う前に抱いていた印象
箇条書きで。
- 小規模利用にはオーバースペックなのでは?
- リモートに処理を流し込むだけなら、シェルスクリプトを標準入力経由でsshに流せばいいだけでは……
- Playbookがシェルスクリプトを難しくしただけのラッパーに見えた
- 「実際のコマンドは~だから、Playbookにはこう書いて~」みたいなことを考えるくらいなら直接シェルスクリプトを書いた方がシンプルでわかりやすいのでは。
実際に使ってみた感想
今回は「新規作成したVPSに初期セットアップをする」という用途でPlaybookを作成し、実行しました。 基本的に、シェルスクリプトで同じことをする場合との比較だと思ってください。
使用感を箇条書きで。
- 小規模向きかと言うと微妙だけど普通に使える
- 対象のホスト名を書いたファイルを用意しなくてはいけないのは若干面倒
- とはいえ1ファイル作るだけ
- localhost向けに実行することもできる
- 対象のホスト名を書いたファイルを用意しなくてはいけないのは若干面倒
- 再実行しやすい
- 同じ設定を上書きしない作りになっていたり、回避する手段がちゃんと用意されていたりする2
- 例えば
lineinfile
という、ファイルに行を追加する命令(?)を再実行した場合、内容が既に追加されていれば追記は行われない - "ifだらけ"現象になりにくいため、見通しがよくなる
- 例えば
- 同じ設定を上書きしない作りになっていたり、回避する手段がちゃんと用意されていたりする2
apt
やgit
などよく使う命令(?)が用意されており、書き方を自然に統一できるshell
やcommand
は極力使わないほうがよさそう- ほぼ使わずにできるようになっているし、必要になったら自分の設計が怪しいかもというサインになりそう
実際に書いたPlaybook
VPS(Conoha)に作成したUbuntuへ、毎度やっている初期セットアップ作業をするPlaybookを書きました。
※ユーザー名とsshd_port
は適当に変えてます
※セットアップ直後なのでrootで実行します
- hosts: all vars: - username: user - sshd_port: 12345 vars_prompt: - name: password prompt: "Input user password" encrypt: sha512_crypt private: yes confirm: yes tasks: - name: Add user user: name: "{{ username }}" password: "{{ password }}" groups: sudo shell: /bin/bash - name: Add authorized_keys authorized_key: user: "{{ username }}" key: "{{ lookup('file', '.ssh/authorized_keys') }}" - name: SSHD settings lineinfile: dest: /etc/ssh/sshd_config regexp: "{{ item.regexp }}" line: "{{ item.line }}" with_items: - regexp: "^PasswordAuthentication" line: "PasswordAuthentication no" - regexp: "^ChallengeResponseAuthentication" line: "ChallengeResponseAuthentication no" - regexp: "^Port" line: "Port {{ sshd_port }}" - name: Restart sshd service: name: sshd state: restarted - name: Configure ufw default rules ufw: direction: "{{ item.direction }}" policy: "{{ item.policy }}" with_items: - direction: "incoming" policy: deny - direction: "outgoing" policy: allow - name: Configure ufw rules ufw: rule: "{{ item.rule }}" port: "{{ item.port }}" proto: tcp with_items: - rule: "limit" port: "{{ sshd_port }}" - name: Enable ufw logging ufw: logging: on - name: Enable ufw ufw: state: enabled - name: Restart ufw service: name: ufw state: restarted
作ってから5回くらい使ってますが便利ですね。
まとめ的な
以前「メンテナンスのことを考えるとシェルスクリプトよりAnsibleの方がいい」という言説を見かけたんですが、確かにその通りだなと感じました。
サーバの初期セットアップをする用途で考えると、 型のあるシェルスクリプト と言えるかもしれません。
なんだか「型のあるシェルスクリプト」って感じはします。(イメージ
— くんすと (@kunst1080) 2018年5月2日
食わず嫌いはいけませんね!
~/.gitconfig を切り替えるCLIツールを作りました
解決したかったこと
gitconfigの切り替えが面倒だった
昼休みや空き時間にちょっとしたツールを作るようなことがあり、今までは毎回 ~/.gitconfig
を書き換えたり、対象のリポジトリだけ .git/config
を書き換えたりする運用をしていました。
が、いくつか問題がありました。
- うっかり忘れて、コミットユーザー誤爆する
- 書き換えが面倒くさい
~/.gitconrfig
の方はバージョン管理しているので、そもそもあまり書き換えたくない
こういった問題があったので、 ~/.gitconfig
をシンボリックリンクにして、リンク先を切り替えるツールを作成しました。
設計
ざっくりこのようなつくりにしました。
~/.gitenv/
以下に環境の名前ごとのサブディレクトリを作成し、その中に.gitconfig
を作成~/.gitconfig
へシンボリックリンクを張る- リンク先をコマンドラインで切り替える
使い方の例
※既存の ~/.gitconfig
は退避しておいてください
# ~/.gitconfig を Alice 用の設定にする $ gitenv -c alice # Alice のユーザー設定をする $ git config --global user.name "Alice" $ git config --global user.email alice@example.com $ cat ~/.gitconfig [user] name = Alice email = alice@example.com # ~/.gitconfig を Bob 用の設定に切り替える $ gitenv -c bob # Bob のユーザー設定をする $ git config --global user.name "Bob" $ git config --global user.email bob@example.com $ cat ~/.gitconfig [user] name = Bob email = bob@example.com $ ~/.gitconfig を Alice 用の設定に戻す $ gitenv -c alice $ cat ~/.gitconfig [user] name = Alice email = alice@example.com # 現在の環境の名前を表示する $ gitenv alice (Alice)
実運用
実際に使っている ~/.gitconfig の内容
こんな感じで Include だけを書いてます。
[Include] path = ~/.gitconfig.all path = ~/.gitaliases path = ~/.gitconfig.kunst1080 # この行を環境ごとに変えて〼
プロンプト
普段は zsh を使っているのですが、環境変数 PROMPT
に $(gitenv)
を埋め込むようにしました。
これで今の設定がどれになっているのかわかります。
プロンプト:
hoge@MacBook-Pro.local ~ env2 (kunst1080) $
まとめ
これで誤爆を防げるよ。やったね!