流行に従って、Dockerを使い始めている最近です。どうせなら、CoreOS使ってやろうとCoreOS+Dockerで、chef/Serverspecの開発環境を作るに至ったのわけだけれども、systemd を初めて触ったり、/etc 配下をみてもサッパリ意味が分からなかったり、構成変更どうやったらいいのか、まるで宇宙に取り残された気分になりました。
そんな宇宙に取り残された状態でもNet上に散らばった情報をまとめていって、なんとか自分で満足のいくCoreOS+Docker環境が完成しました。
ので、自分のためだけに書き残しておきます。
CoreOSをインストールしよう
CDブートからCoreOSをインストールする方法を選択します。この1週間で10回以上はインストールしていますが、全てこの方法でクリーンインストールを繰り返しています。しかし、cloud-config.ymlという素敵な構成ファイルのお陰で、何度やりなおしても苦痛がないので助かっています。ホント素晴らしい。
さて、まずはメディアを準備しましょう。
ブートCDを準備する
Booting CoreOS from an ISO より好きなメディアを落としてきましょう、どうせイメージはネットからダウンロードしてインストールするので、どれでもいい気がします(個人的見解)。
仮想環境でも実機でもどこでもいいので、ISOのまま使うか、CDに焼くかは皆さんのお好み次第です。私は Proxmoxの仮想環境に入れてます。
起動するだけなら、ディスクサイズは2GBと必要ないでしょう。とは言え、イメージをおいたりするので、20-40GB と自分がどれだけのイメージを準備するかによって割り当てましょう。
我が家では、外部NFSをマウントしたりして、共有やデータの外部保管も実現してるので、16GBと少なめです。イメージ一つだけですしね。
cloud-config.yml を準備する
インストールする前に、まず設定ファイルを準備しておきます。面倒ごとは先にする主義です。実現してないけど。
内容はコレだ(ババン)長い
#cloud-config
hostname: docker01.oshiire.to
coreos:
units:
- name: etcd.service
command: stop
- name: fleet.service
command: stop
- name: docker-tcp.socket
command: start
enable: yes
content: |
[Unit]
Description=Docker Socket for the Remote API
[Socket]
ListenStream=0.0.0.0:2375
Service=docker.service
BindIPv6Only=both
[Install]
WantedBy=sockets.target
- name: enable-docker-tcp.service
command: start
content: |
[Unit]
Description=Enable Docker Socket for Remote API
[Service]
type=oneshot
ExecStart=/usr/bin/systemctl enable docker-tcp.socket
- name: timezone.service
command: start
content: |
[Unit]
Description=timezone
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/ln -sf ../usr/share/zoneinfo/Japan /etc/localtime
- name: 10-static.network
runtime: no
content: |
[Match]
Name=ens*
[Network]
Address=...
Gateway=...
DNS=...
- name: rpc-statd.service
command: start
- name: home.mount
command: start
content: |
[Unit]
Description=Home Directory on NFS
Before=rpc-statd.service
Conflicts=umount.target
[Mount]
What=...:/mnt/home
Where=/home
Options=rw,rsize=4096,wsize=4096,hard,intr,async,nodev,nosuid
Type=nfs
update:
reboot-strategy: best-effort
users:
- name: core
passwd: ...
groups:
- sudo
- docker
ssh-authorized-keys:
- ssh-rsa AAAA.....==
拡張子から分かるとおり、YAMLで記述されています。
個人的に見られたくない部分は「...」としています。主に書き換え必須な部分はそこだろうと当たりをつけてください。また、indent はスペースでないとダメらしいです。こわいですね。
さて、自分のために、全部説明書きを、残すんです(川平風)
#cloud-config
#cloud-config
おまじないです。絶対に入れておいてください。ないと死にます。
hostname
hostname: docker01.oshiire.to
家庭内で使っているホスト名なので、外から叩いても何も起きません。とりあえず、好きにつけてください。名前解決できるようにしておけるものが望ましいです。
coreos: units:
systemd で処理されるメインの部分です。
name: ごとに各daemon や service が割り当てられていると判断しました。(systemdがよく分かってない)
coreos:
units:
おまじないです。忘れずに書きましょう。
- name: etcd.service
command: stop
- name: fleet.service
command: stop
etcd と fleet を使わないと心に誓った想いがここに現れています。
CoreOSとしては etcd/fleet がメインみたいなので、勝手に起動するように設定されそうなので先に排除しました。今回は Docker
に絞ったのでこんな感じにしてます。
- name: docker-tcp.socket
command: start
enable: yes
content: |
[Unit]
Description=Docker Socket for the Remote API
[Socket]
ListenStream=0.0.0.0:2375
Service=docker.service
BindIPv6Only=both
[Install]
WantedBy=sockets.target
- name: enable-docker-tcp.service
command: start
content: |
[Unit]
Description=Enable Docker Socket for Remote API
[Service]
type=oneshot
ExecStart=/usr/bin/systemctl enable docker-tcp.socket
メイン部分の Docker
起動部分ですね。Docker プロセス自体は勝手に起動されてくるようなんですが、remote APIが使えないので、寂しすぎるので tcp socket
サービスを入れて、外部からも docker コマンド
を使えるようにしています。
ListenStream=0.0.0.0:2375
部分がキーポイントです。ListenするIPアドレスとポートを指定できます。このケースだと、IPv4 で割り当てられた全I/F から 2375/tcp
ポートで待ち受けることを示してます。
timezone.service
どこからかパクってきました。最低です。
ここは日本であることを示して、Timezoneを設定してます。時刻は正しく表示してもらいたいですもんね!
- name: timezone.service
command: start
content: |
[Unit]
Description=timezone
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/ln -sf ../usr/share/zoneinfo/Japan /etc/localtime
10-static.network
数字始まりでなにやら異質な部分ですが、ネットワーク設定部分です。
- name: 10-static.network
runtime: no
content: |
[Match]
Name=ens*
[Network]
Address=...
Gateway=...
DNS=...
[Network]
以下の Address=xxx.xxx.xxx.xxx/xx
にIPアドレスを、Gateway=xxx.xxx.xxx.xxx
に Default gatewayアドレスを、DNS=xxx.xxx.xxx.xxx
に DNSサーバのIPアドレスを指定します。DNSが複数あるときは、もう1行 DNS=...
を追加すれば良さそうです。
home.mount
我が家では慣習的に /home を nfsマウントして運用している手前、nfs マウントを追加しています。お好みですが、なかなか情報が無かったので、systemd の書き方探してきて、自分なりにアレンジしました。オレカッコイイ。
- name: rpc-statd.service
command: start
- name: home.mount
command: start
content: |
[Unit]
Description=Home Directory on NFS
Before=rpc-statd.service
Conflicts=umount.target
[Mount]
What=...:/mnt/home
Where=/home
Options=rw,rsize=4096,wsize=4096,hard,intr,async,nodev,nosuid
Type=nfs
nfs の lock サービスを使いたいので、rpc-statd
を起動するようにしています。最初の2行がそれですね。
systemd の慣習として、xxx.mount
の xxx
はマウント先を書くようなので、今回は home.mount
としています。要するに好きにしろってコトです。
[Unit]
以下には、先に rpc-statd.service
が起動していて欲しい、と言うことと、umount.target
の仲間にして下さいと言うことで Conflicts
を指定してます。詳しいことはよく分かりません。
[Mount]
には、/etc/fstab
に書いているようなことを記載します。
What
は、マウント元です。...
はマウント元のサーバ名またはIPアドレスで、:
をはさんでディレクトリを示します。この例だと /mnt/home
がマウント元のディレクトリですね。
Where
は、このCoreOS
でマウントする先になります。先にも説明したとおり、home
を共有しているので、/home
としています。この場合、CoreOS
のホームディレクトリが /home
配下になりますので、その辺はうまいことやりましょう。
Options
は、nfsのマウントオプションです。うちはこんな感じです。
Type
には、マウントタイプを指定します。nfs じゃなくて smbfs や ext4 なども指定できますので、適宜読みかえましょう。
update
CoreOS はアップデートを見つけると勝手に更新して、勝手に再起動します。素敵ですね。
その時の再起動方針を指定します。
update:
reboot-strategy: best-effort
Using Cloud-Config の update
セクションに詳しいので、そちらを見てください。
なお、ぱくってくるとこんなコト書いてあります。
One of "reboot", "etcd-lock", "best-effort" or "off" for controlling when reboots are issued after an update is performed.
- reboot: Reboot immediately after an update is applied.
- etcd-lock: Reboot after first taking a distributed lock in etcd, this > guarantees that only one host will reboot concurrently and that the cluster will remain available during the update.
- best-effort - If etcd is running, "etcd-lock", otherwise simply "reboot".
- off - Disable rebooting after updates are applied (not recommended).
いつでもすぐに再起動して欲しければreboot
、再起動して欲しくなければoff
、なんだからよく分からなければbest-effort
、etcd 使ってる人は etcd-lock
とbest-effort
あたりで悩んでください。
users
長かった(個人的に)...。最後に usersです。CoreOSで利用するユーザをここに指定します。書かないとログインで困ります。
users:
- name: core
passwd: ...
groups:
- sudo
- docker
ssh-authorized-keys:
- ssh-rsa AAAA.....==
name
欄にユーザ名を記載します
passwd
には、Using Cloud-Config の Generating a password hash
セクションにある方法で password のハッシュを作って、それを記載します。
# On Debian/Ubuntu (via the package "whois")
mkpasswd --method=SHA-512 --rounds=4096
# OpenSSL (note: this will only make md5crypt. While better than antext it should not be considered fully secure)
openssl passwd -1
# Python (change password and salt values)
python -c "import crypt, getpass, pwd; print crypt.crypt('password', '\$6\$SALT\$')"
# Perl (change password and salt values)
perl -e 'print crypt("password","\$6\$SALT\$") . "\n"'
groups
には所属グループを記載しますが、sudo
に入れとかないとむせび泣くと思います
ssh-authorized-keys
には、ssh の公開鍵をそのままぺたっと貼り付けます。我が家ではRSA鍵使ってるので、ssh-rsa から始まるあれが貼り付けてあります。これがそのまま ~/.ssh/authorized_keys
になります。
長かった...。
インストールしよう!
の前に、コンソールで、上記 cloud-config.yml
を入力するのは至難の業なので、次の順に進めていきます。
-
core
ユーザにパスワードをつける - IPアドレスを知る
- 自由の利くクライアントから ssh ログインする
-
cloud-config.yml
をコピペするか、scp でコピーする - インストールコマンドを打つ
1. core
ユーザにパスワードをつける
起動した CoreOSでおもむろに次のコマンドを打ちましょう。
sudo passwd core
パスワードは好きにつけてください
2. IPアドレスを知る
ip -f inet addr
実は Enterキーを押しても IPアドレス分かります。何故こんなコトするかというと、dhcp で勝手に拾ってくるからです。dhcp ない人は諦めてください(やり方知らない)。
3. 自由の利くクライアントから ssh ログインする
先の二つの経験を元に
ssh core@[ipアドレス]
でどうぞ。~/.ssh/known_hosts
が汚されたくない人は、-o "StrictHostKeyChecking no"
つけてください。コレによる影響は多少は気にしたほうがいいです。
4. cloud-config.yml
をコピペするか、scp でコピーする
多分 vi
できますので、vi ~/cloud-config.yml
したり、scp ./cloud-config.yml core@xxx.xxx.xxx.xxx:~/
で、先ほどの cloud-config.yml
をなんとかしましょう。
5. インストールコマンドを打つ
最後です。これを打てばインストールできます。上記の設定で。素敵。
sudo coreos-install -d /dev/sda -C stable -c ~/cloud-config.yml -V 493.0.0
各オプションは次の通り
-d
導入先のディスクを指定します。
-C
導入する CoreOS
のチャネルを指定します。stable
(安定版) beta
(β版) alpha
(開発最新版) とあり、安定志向の人はstable
を、最新版がとにかく使いたい人はalpha
を、引っ込み思案で優柔不安なあなたはbeta
を指定しましょう。
-c
大文字と小文字に注意。これまでがんばって作り上げたcloud-config.yml
ファイルを指定します。
-V
導入するバージョンを指定します。個人的には指定した方が良いと思いますが、各チャネルの最新バージョンはRelease Channelsで確認できますので、ここを見ながら指定しましょう。
再起動
導入完了の合図がきたら、あとは再起動するのみです。
おめでとうございます、CoreOSの導入はここで完了です!
長かったなー。とお思いでしょうが、コレでは終わりません。大変です。
Debian用の chef/serverspec 用 Dockerイメージを準備する
なんだよ、フツーに docker build すればいいんだろと思われるかも知れませんが、実はちょっと注意事項があるんです。
私の Dockerfile
をご覧に入れましょう。大盤振る舞いです。
FROM debian:wheezy
MAINTAINER sho kisaragi <sho@oshiire.to>
RUN echo "deb http://ftp.jp.debian.org/debian/ wheezy main" > /etc/apt/sources.list
RUN echo "deb http://security.debian.org/ wheezy/updates main" >> /etc/apt/sources.list
RUN echo "deb http://ftp.jp.debian.org/debian/ wheezy-updates main" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y ca-certificates dialog locales openssh-server sudo curl rsync net-tools --no-install-recommends
RUN apt-get upgrade -y && apt-get clean
RUN mkdir /var/run/sshd
RUN groupadd --gid 1000 chef
RUN useradd --uid 1000 --gid 1000 -m chef
RUN mkdir -p /home/chef/.ssh
RUN chmod 0700 /home/chef/.ssh
RUN chown chef.chef /home/chef/.ssh
RUN echo 'chef ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/chef
ADD id_rsa.pub /home/chef/.ssh/authorized_keys
RUN chmod 0400 /home/chef/.ssh/authorized_keys
RUN chown chef.chef /home/chef/.ssh/authorized_keys
RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d
RUN curl -L https://www.opscode.com/chef/install.sh | sudo bash
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
何の変哲もない気がします。流れとしては、chefを実行するために必要なパッケージを入れることと、専用のユーザを準備しているだけに見えます。
残念、そうはいかないのです。
invoke-rc.d: policy-rc.d denied execution of start の罠
なんとなく docker上で chefを運用していると、invoke-rc.d: policy-rc.d denied execution of start.
に巡り会い、service が起動できない自体に巡り会うことがあります。その理由は Docker, Openstack, policy-rc.d, mysqld にもある通り、systemd 化された CoreOSのために、policy-rc.d
コマンドが exit 101
を強制的に返すことによる影響を受けるためです。分かっていればいいわけですが、地味にいやな気持ちになるので、Dockerfile
内に次の 1行を入れています。
RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d
これでいいのか的な対応ですが、いいんです(川平風)
ここまでやって、やっと 「debian向けchef/serverspec開発用環境 on CoreOS」の完成です。
文章は無駄に長いですが、やってみると 5分で終わります。是非どうぞ。