Immutable Emacs! AWS 上の Ubuntu サーバに ansible で Emacs 環境を自動構築する

はじめに

去年, Immutable Infrastructure という言葉を知った.

この概念を Emacs の管理に利用できないかなとという話.

これをやろうと考えたのは, 去年の 6 月だった. この記事の延長が本記事.

なかなか時間がとれなくて, 結局半年後の冬休みにようやくできた.

問題の分析

問題

Emacs を家でも会社でも利用している.以下が簡単な環境.

  • 自宅
    • ArchLinux
    • Widows 8 (GUI 版)
  • 会社
    • Widows 7 (GUI 版)
    • Windows 7 (Cygwin 版)

設定を github にあげて, 設定を共有している. 会社で設定ファイルを更新するのは社内規程的に NG なので, いつもは以下のような運用.

  • 自宅で設定ファイルを git push
  • 会社で設定ファイルを git pull

しかし, 自宅で設定したものが会社で動かなかったりすることがよくある.

会社で Emacs の設定ファイルを更新した後は, 朝の 30 分は毎回 Emacs と戯れているのはここだけのないしょだ.

しかし, 設定の同期作業も, 面倒なのでなんとか時間を短縮できないもの か… というのが今回の解決したい問題.

また, 1 年に 1 回 くらいの頻度で Emacs 環境を一から再構築することがある. このとき, Emacs の パッケージ管理は el-get を利用して管理しているので, 自動ですべてのパッケージを ネットから落としてきて動作するはず.

しかし, 必ず el-get のダウンロードで失敗するパッケージがたくさんでる.

会社だと, proxy という厄介なものがある. また, 設定の問題とは思うが, MELPA へアクセスできなく github からしか elisp を取得できない.

まとめると,

  • 会社で Emacs の設定ファイルを更新したときに 起動時のエラーを発生させなくするためには?
  • Emacs 環境を一から再構築するときに エラーを発生させなくするためには?

課題

よくあるあたふたケースは,

  • 自宅の Linux 環境で push したものを 会社の windows 環境で pull して動かなくなる.
  • 会社で pull をするのが怖いのでしばらく放置した後, 久しぶりに pull すると 大量のエラーが発生する.

エラーがあっても放置してしまうのは, 頻繁に エラーの確認をしないから. それは面倒だからしない. エラーの確認作業を自動化できたらうれしい.

ということで, 課題は以下.

  • Linux 環境で push したら Windows 環境で pull してエラーチェックを する作業を自動化する.
  • 定期的に一から Emacs の設定を再構築する作業を自動化する.

解決策

この 自動でエラーをチェックするところを Immutable Infrastracture の概念と 組み合わせたらおもしろそうだと思った. 無駄な勉強にもなるし.

Immutable Infrastructure とは

簡単に調べてみた.

一度サーバーを構築したらその後はサーバーのソフトウェアに変更を加えないこと.

以下の説明がとてもわかりやすい.

Windows って調子が悪くなると再インストールしてすっきりさせるじゃないで すか. OS にいろんなソフトウェアをインストールしたりカスタマイズしたり すると調子が悪くなりますが, 再インストールすれば戻ります.

だったら, そもそも毎回作り直せばシステムはすっきりするよね, というのが 簡単な Immutable Infrastructure の説明です.

Action Plan

以下のようなアクションプランを考えた.

  • AWS 上のサーバで Emacs 環境を構築する.
  • AWS 上のサーバで エラチェックーを走らせる.
  • チェックがおわったらサーバを破棄して終了.

さらにこの手順の自動化ができたらいいけど, これは大変そうなので今度.

  • git push したら AWS 上で動く サーバで エラチェックーを走らせる.
  • 毎週深夜に, Emacs 環境を自動で構築する.

解決へのとりくみ

Windows 環境はなにかと難しいとおもったので, まずは, Linux 環境を構築することにした.

というわけで, この記事のゴールは,

AWS 上に Emacs と自分の Emacs の設定を自動でインストールすること

名づけて,

Immutable Emacs! 大作戦.

響きがとても気に入った!

AWS を借りる

別記事に独立.

ここでは, Ubuntu Server 14.04 LTS (HVM), SSD Volume Type をつかう.

ansible で Amazon AWS に SSH 接続

なれている, ansible を利用する.

[sourcecode language=”bash” title=””]
mkdir -p ~/.emacs.d/playbook
cd ~/.emacs.d/playbook
echo ‘54.65.121.127’ >> hosts
[/sourcecode]

以下の作業はすべて,~/.emacs.d/playbook で実施.

ansible.cfg ファイルを作成.

[sourcecode language=”bash” title=””]
[defaults]
hostfile = ./hosts
remote_user = ubuntu
private_key_file = ~/.ssh/archlinux.pem
host_key_checking = False
[/sourcecode]

ping する.

[sourcecode language=”bash” title=””]
ansible all -m ping -i hosts
[/sourcecode]

[sourcecode language=”bash” title=””]
54.64.58.11 | success >> {
"changed": false,
"ping": "pong"
}
[/sourcecode]

ansible playbook

org-mode で書いたものは, 以下のスクリプトで yaml に変換する.

[sourcecode language=”bash” title=””]
#!/bin/sh
# -*- mode: shell-script -*-
#
# tangle files with org-mode
# see: https://orgmode.org/manual/Batch-execution.html
#
DIR=`pwd`
FILE=""

for i in $@; do
# tangle org to yaml
emacs -Q –batch –eval "(progn
(add-to-list ‘load-path \"/usr/share/emacs/site-lisp/org\")
(require ‘org) (require ‘ob) (require ‘ob-tangle)
(setq org-src-preserve-indentation t)
(find-file (expand-file-name \"$i\" \"$DIR\"))
(org-babel-tangle)
(kill-buffer))" 2>&1 | grep tangled

# execute ansible command to yml file
string_filename=${i##*/}
string_filename_without_extension=${string_filename%.*}
string_path=${i%/*}

if test $string_filename = $i ; then
ansible-playbook ${string_filename_without_extension}.yaml -i hosts
else
ansible-playbook ${string_path}/${string_filename_without_extension}.yaml -i hosts
fi
done
[/sourcecode]

ここからは実際の ansible playbook を書く.

ansible settig

[sourcecode language=”text” title=””]

– hosts: defaults
sudo: yes
vars:
home_dir: /home/ubuntu
emacs_dir: emacs
dotfiles_dir: dotfiles
tasks:
[/sourcecode]

initial tools

  • libncurses5-dev は tputs error 対策
  • gnutls-bin for el-get
  • texinfo for makeinfo

[sourcecode language=”text” title=””]
– name: apt-get update
command: apt-get update
– name: apt-get install git
apt: pkg=git
– name: apt-get install build tools
apt: pkg=build-essential,autoconf,automake,libncurses5-dev,gnutls-bin,texinfo
[/sourcecode]

install emacs

[sourcecode language=”text” title=””]
– name: get emacs from git repository
git: repo=git://git.savannah.gnu.org/emacs.git dest={{home_dir}}/{{emacs_dir}} accept_hostkey=yes
– name: make emacs configure file
command: ./autogen.sh chdir={{home_dir}}/{{emacs_dir}}
– name: make emacs Makefile
command: ./configure –prefix=/usr/local –without-makeinfo –with-x-toolkit=no –without-all chdir={{home_dir}}/{{emacs_dir}}
– name: make emacs
command: make chdir={{home_dir}}/{{emacs_dir}}
– name: install emacs
command: make install chdir={{home_dir}}/{{emacs_dir}}
[/sourcecode]

install dotfiles

[sourcecode language=”text” title=””]
– name: clone dotfiles
git: repo=https://github.com/tsu-nera/dotfiles.git dest={{home_dir}}/{{dotfiles_dir}}
– name: make lnlinks
command: ./make_lnlink chdir={{home_dir}}/{{dotfiles_dir}}
sudo: yes
sudo_user: ubuntu
[/sourcecode]

install additinal tools for emacs

[sourcecode language=”text” title=””]
– name: install additinal tools for pdf-tools
apt: pkg=libpng-dev,libz-dev,libpoppler-glib-dev,libpoppler-private-dev
– name: install additinal tools for magit
apt: pkg=texinfo
– name: install additinal tools for org2blog
apt: pkg=bzr
– name: install additinal tools for exectable-find
apt: pkg=mercurial
– name: install additinal tools for cmigemo
apt: pkg=cmigemo
– name: install additinal tools for ag
apt: pkg=silversearcher-ag
[/sourcecode]

boot emacs

Emacs の boot に, el-get を利用してパッケージを次々にダウンロードする.

[sourcecode language=”text” title=””]
– name: boot emacs first
command: emacs –daemon
sudo: yes
sudo_user: ubuntu
# – name: reboot emacs
# command: emacsclient -e ‘(progn (defun yes-or-no-p (p) t) (kill-emacs))’ && emacs –daemon
# command: emacs –daemon
# sudo: yes
[/sourcecode]

おわりに

これで AWS 上で動く Ubuntu サーバに Emacs をインストールすることができた.

まだまだ, 改良の余地は大ありだ.まだ, スタート地点.

dotfiles を育てるように, ansible のコードも育てていきたい.