[Debian] chef/Serverspec のためだけに CoreOSのDockerを構成する [Wheezy]

前に Qiita で「[Debian] chef/Serverspec のためだけに CoreOSのDockerを構成する [Wheezy] – Qiita」を書いたのを、「MarsEdit 3」で、どんだけうまくできるかどうかのテストです。 手抜きじゃないんです。

流行に従って、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 を準備する

インストールする前に、まず設定ファイルを準備しておきます。面倒ごとは先にする主義です。実現してないけど。

内容はコレだ(ババン)長い

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#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

1
#cloud-config

おまじないです。絶対に入れておいてください。ないと死にます。

hostname

1
hostname: docker01.oshiire.to

家庭内で使っているホスト名なので、外から叩いても何も起きません。とりあえず、好きにつけてください。名前解決できるようにしておけるものが望ましいです。

coreos: units:

systemd で処理されるメインの部分です。 name: ごとに各daemon や service が割り当てられていると判断しました。(systemdがよく分かってない)

1
2
coreos:
  units:

おまじないです。忘れずに書きましょう。

1
2
3
4
    - name: etcd.service
      command: stop
    - name: fleet.service
      command: stop

etcd と fleet を使わないと心に誓った想いがここに現れています。 CoreOSとしては etcd/fleet がメインみたいなので、勝手に起動するように設定されそうなので先に排除しました。今回は Docker に絞ったのでこんな感じにしてます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    - 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を設定してます。時刻は正しく表示してもらいたいですもんね!

1
2
3
4
5
6
7
8
9
    - 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

数字始まりでなにやら異質な部分ですが、ネットワーク設定部分です。

1
2
3
4
5
6
7
8
9
10
    - 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 の書き方探してきて、自分なりにアレンジしました。オレカッコイイ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    - 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.mountxxx はマウント先を書くようなので、今回は 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 はアップデートを見つけると勝手に更新して、勝手に再起動します。素敵ですね。 その時の再起動方針を指定します。

1
2
  update:
    reboot-strategy: best-effort

Using Cloud-Configupdate セクションに詳しいので、そちらを見てください。 なお、ぱくってくるとこんなコト書いてあります。

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-lockbest-effortあたりで悩んでください。

users

長かった(個人的に)…。最後に usersです。CoreOSで利用するユーザをここに指定します。書かないとログインで困ります。

1
2
3
4
5
6
7
8
users:
  - name: core
    passwd: ...
    groups:
      - sudo
      - docker
    ssh-authorized-keys:
      - ssh-rsa AAAA.....==

name 欄にユーザ名を記載します passwd には、Using Cloud-ConfigGenerating a password hash セクションにある方法で password のハッシュを作って、それを記載します。

1
2
3
4
5
6
7
8
9
10
11
# 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 を入力するのは至難の業なので、次の順に進めていきます。

  1. core ユーザにパスワードをつける
  2. IPアドレスを知る
  3. 自由の利くクライアントから ssh ログインする
  4. cloud-config.yml をコピペするか、scp でコピーする
  5. インストールコマンドを打つ

1. core ユーザにパスワードをつける

起動した CoreOSでおもむろに次のコマンドを打ちましょう。

1
sudo passwd core

パスワードは好きにつけてください

2. IPアドレスを知る

1
ip -f inet addr

実は Enterキーを押しても IPアドレス分かります。何故こんなコトするかというと、dhcp で勝手に拾ってくるからです。dhcp ない人は諦めてください(やり方知らない)。

3. 自由の利くクライアントから ssh ログインする

先の二つの経験を元に

1
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. インストールコマンドを打つ

最後です。これを打てばインストールできます。上記の設定で。素敵。

1
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 をご覧に入れましょう。大盤振る舞いです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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/shnexit 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行を入れています。

1
RUN echo "#!/bin/shnexit 0" > /usr/sbin/policy-rc.d

これでいいのか的な対応ですが、いいんです(川平風)

ここまでやって、やっと 「debian向けchef/serverspec開発用環境 on CoreOS」の完成です。 文章は無駄に長いですが、やってみると 5分で終わります。是非どうぞ。