hive 構築ガイド

ここでは、hive のサイトを構築する方法を説明します。

マザーマシンの構築

hive-builder でサイトを構築するために最初に マザーマシンを構築する必要があります。 マザーマシンの OS は Linux か Mac OS である必要があります。 Windows 10 であれば、Windows Subsystem for Linux を利用していただけます。 マザーマシンにはCPU1個、メモリ1GB、ディスク3GB程度のリソースがあれば十分です。 そのようなマシンを用意し、 インストール を参照して、 hive-builder を インストールしてください。

プロジェクトディレクトリの作成

マザーマシンにプロジェクトディレクトリを作成してください。 サンプルの pdns プロジェクトを例に hive のディレクトリ構造を説明します。

blockdiag pdns inventory lib roles group_vars hive.yml powerdns.yml all.yml servers.yml services.yml powerdns_record.py powerdns_zone.py certbot-runner powerdns powerdns-admin powerdns-initdb proxy-configure

ディレクトリ/ファイル名

必須

説明

pdns

必須

プロジェクトのルートディレクトリ

inventory

必須

インベントリを保持するディレクトリ

group_vars

任意

リソースグループごとの変数を保持するディレクトリ

all.yml

任意

すべてのリソースに共通の変数を保持するディレクトリ

servers.yml

任意

すべてのサーバに共通の変数を保持するディレクトリ

services.yml

任意

すべてのサービスに共通の変数を保持するディレクトリ

hive.yml

必須

hive定義(ファイル名は任意)

powerdns.yml

必須

サービス定義(ファイル名は任意)

lib

任意

ansible モジュールを保持するディレクトリ

powerdns_record.py

必須

powerdns のレコードをプロビジョニングするモジュール

powerdns_zone.py

必須

powerdns のゾーンをプロビジョニングするモジュール

roles

任意

コンテナイメージの構築時に呼び出す role を保持するディレクトリ

certbot-runner

任意

サーバ証明書を自動的に取得する certbot-runner サービスのコンテナイメージを構築する role です

powerdns

任意

権威DNSサーバである powerdns サービスのコンテナイメージを構築する role です

powerdns-admin

任意

権威DNSサーバのWebコンソールである powerdns-admin サービスのコンテナイメージを構築する role です

powerdns-initdb

任意

権威DNSサーバのデータベースを初期化する role です

proxy-configure

任意

リバースプロキシの構成を自動的に行う proxy-configure サービスのコンテナイメージを構築する role です

プロジェクトを新規に開発する際は、まず、上記の必須となっているディレクトリとファイルを作成する必要があります。

role のディレクトリ構造

roles 配下にはrole ごとのディレクトリを作成する必要があります。 role の記述方法については ansible の公式ドキュメント を参照してください。 たとえば、pdnsプロジェクトのpowerdns ロールでは以下のディレクトリ構造を持っています。

blockdiag powerdns files tasks docker-entrypoint.sh main.yml

powerdns/tasks/main.yml の内容は以下の通りです。

---
- name: install powerdns
  apk:
    name:
      - pdns
      - pdns-backend-mysql
      - pdns-backend-lua
    state: present
    repository:
    - http://dl-cdn.alpinelinux.org/alpine/edge/community/
    - http://dl-cdn.alpinelinux.org/alpine/edge/main/
    update_cache: yes
- name: install endpoint shell
  copy: src=docker-entrypoint.sh dest=/ mode=0775
- name: "patch default config file - set default"
  lineinfile:
    path: /etc/pdns/pdns.conf
    regexp: "^(# *)?{{item.key}}=.*"
    line: "{{ item.key }}={{ item.value }}"
  with_items:
    - key: daemon
      value: "no"
    - key: guardian
      value: "no"
    - key: launch
      value: gmysql
    - key: chroot
      value: ""
- name: "patch default config file - comment out"
  lineinfile:
    path: /etc/pdns/pdns.conf
    regexp: "^(# *)?{{ item }}=.*"
    line: "# {{ item }}="
  with_items:
    - use-logfile
    - wildcards

上記の playbook で以下のことを実行しています。

  • PowerDNS のソフトウェアのインストール

  • エントリポイントのシェルスクリプトを追加

  • 設定ファイル /etc/pdns/pdns.conf を編集

基盤の構築

基盤を構築するためには、inventory/hive.yml を作成し、hive 定義を記述する必要があります。 hive 定義の記述方法については インベントリ を参照してください。 最初は private ステージを作成することが推奨されます。 作業をするパソコンのメモリに余裕があれば、 vagrant プロバイダで 4G 以上のメモリをもった サーバを構築するのが良いでしょう。

基盤のテスト

hive コマンドで build-infra フェーズと setup-hosts フェーズがエラーなく成功するように なれば、hive 定義は完成と言えるでしょう。以下のコマンドがエラーなく成功するまで、 hive定義の内容を調整してください。

hive build-infra
hive setup-hosts

サービスの開発

hive の中でサービスを起動するためにはサービスをインベントリに定義する必要があります。

ほとんどのサービス(= docker コンテナ)は以下の5段階の構築が必要です。

  • コンテナイメージのビルド(コンテナへのソフトウェアのインストール)

  • ボリュームのマウント

  • ネットワークの配備

  • サービスのデプロイ(サイト固有パラメータの設定を含む)

  • サイトの初期データのロード

以下に順に説明します。

コンテナイメージのビルド

docker では、ソフトウェアのインストールが終わったコンテナのイメージを リポジトリに登録しておき、これをダウンロードして利用するのがベストプラクティスとなります。 dockerhub などの外部のリポジトリに登録されているコンテナイメージをそのまま 利用する場合は、 hive の中でコンテナイメージを作成する必要はありませんが、 プロジェクト固有のカスタマイズが入ったサービスを開発する場合は、 hive の中で コンテナイメージをビルドし、プロジェクト内部のリポジトリに登録する必要があります。

hive ではコンテナイメージの登録とプロジェクト内部のリポジトリへの登録を build-images フェーズで実行できます。このビルドを行うには、サービス定義の image 属性に from 属性と roles 属性を持ったビルド定義オブジェクトを設定する必要があります。 image 属性にイメージタグの文字列が設定されている場合は、build-images フェーズの対象となりません。

以下のコマンドで、build-images を実行し、コンテナイメージを登録してください。

hive build-images

サービス定義の from 属性、roles 属性の記述方法については インベントリ を参照してください。

リポジトリの掃除

build-images フェーズを実行すると新しいコンテナイメージが登録されますが、 古いコンテナイメージはリポジトリに残ったままになります。 ディスク残量が少なくなってきた場合には、 hive ssh コマンドでリポジトリサーバに ログインし、以下のコマンドを実行してリポジトリを掃除してください。

docker exec -it registry registry garbage-collect -m /etc/docker/registry/config.yml

ボリュームのマウント

docker の通常のボリュームに加えて drbd でサーバ間で複製同期するボリュームを利用できます。 その仕組みについては、概要 の高可用性の節を参照してください。 サーバ間で複製同期しているため、サービスがどのサーバで起動しても同じ内容の ボリュームが見えます。 この複製同期するボリュームを使用する場合は、サービス定義のvolumes 属性に指定する ボリューム定義で drbd 属性を指定してください。 drbd 属性の設定方法については インベントリ を参照してください。

ボリュームの作成は build-volume フェーズで行われます。以下のコマンドで ボリュームを作成できます。

hive build-volumes

ネットワークの配備

hive は docker swarm の overlay ドライバを使用し、クラスタに参加するすべてのサービスが 接続するデフォルトのネットワークを1個作成します。このネットワークの名前は hive_default_network です。 各サービスはこのネットワークを経由して他のサービスにアクセスすることができます。 たとえば、サンプルの powerdns のサービスはデータベースサービス pdnsdb に その名前でアクセスします。docker の内部DNSが pdnsdb の hive_default_network 上のアドレスを解決し、PowerDNS のサーバはデータベースにアクセスできます。 この仕組みは powerdns サービスと pdnsdb サービスがどのホストで動作しているかと 関係なく動作します。 通常は、このデフォルトのネットワークで十分ですので、 特にインベントリにネットワークを定義する必要はありません。

ネットワークの配備は build-networks フェーズで行われます。以下のコマンドで 実行できます。

hive build-networks

サービスのデプロイ

ここでは、 docker swarm サービスをデプロイします。 デプロイ時ににサイト固有のパラメータを渡すために、起動時にコマンドラインを指定したり、 環境変数にパラメータを指定したりするのが一般的です。 また、デプロイしたサービスを外部に対して公開する場合の ポート番号を指定する必要があります。

例えば、サンプルの powerdns サービスでは、以下の指定で、サイト固有パラメータ を指定しています。

environment:
  MYSQL_PASSWORD: "{{db_password}}"
  MYSQL_HOST: pdnsdb
  MYSQL_DNSSEC: "yes"
  PDNSCONF_DEFAULT_SOA_NAME: "{{ (groups['first_hive'] | intersect(groups[hive_stage]) | first) + '.' + domain }}"
command:
- "--api=yes"
- "--api-key={{db_password}}"
- "--webserver=yes"
- "--webserver-address=0.0.0.0"
- "--webserver-allow-from=0.0.0.0/0"
ports:
- "53:53/tcp"
- "8081"
- "53:53/udp"

環境変数(environments の配下)で DBサーバへの接続パラメータを渡しています。ここでは、DBにアクセスする ためのパスワード(MYSQL_PASSWOWRD)は動的に生成したものを ansible のテンプレート機能で展開しています。 また、コマンド引数(command の配下)でPOWERDNS の API を有効化しています。 さらに ports でサービスの公開仕様を定義しています。この例では udp/tcp DNSサービスを 53 番ポートで公開し、 APIサービスのポート 8081 を自動的に割当られるポート番号で公開しています。

ただし、 hive は 10000 以上の番号は外部に公開しないようになっています。 IaaS のファイアウォールおよび iptables (未実装)で外部からのアクセスを 遮断しています。上記であれば、APIサービスは 内部からのみアクセスでき、外部には公開されません。

このようにして、定義されたサービスを以下のコマンドで起動することができます。

hive deploy-services

サイトの初期データのロード

複数のマイクロサービスが連携して機能を実装する場合、build-images や deploy-services では 初期データをロードできない場合があります。たとえば、サンプルの powerdns では、 ゾーンやAレコードをAPIから登録しようとすると、 Power DNS とバックエンドの データベースの両方を稼働させる必要があります。

hive では、 initialize-services フェーズですべてのサービスを稼働させた状態で 初期データを登録できます。 initialize-services フェーズで初期データを登録するためには、 サービス定義の initialize_roles プロパティにデータを初期化するためのrole を指定し、 その role を定義する必要があります。例えば、サンプルでは Power DNS のモジュールを使って 初期データを登録しています。サービス定義で initialize_roles にpowerdns-init を 指定しており、 roles/powerdns-init/tasks/main.yml の内容は以下のとおりです。

---
- name: get my public IP
  ipify_facts:
  delegate_to: "{{item}}"
  delegate_facts: True
  when: hive_provider not in ['vagrant']
  loop: "{{ groups['hives'] | intersect(groups[hive_stage]) }}"
- name: set published
  set_fact:
    published_ip: "{% if hive_provider not in ['vagrant'] %}{{ hostvars['p-hive0.pdns'].hive_private_ip }}{% else %}{{ hostvars['p-hive0.pdns'].ansible_facts.ipify_public_ip }}{% endif %}"
  delegate_to: "{{item}}"
  delegate_facts: True
  loop: "{{ groups['hives'] | intersect(groups[hive_stage]) }}"
- name: install pip
  apk:
    name: py-pip
- name: install requests module
  pip:
    name: requests
- name: wait for powerdns api available
  wait_for:
    host: localhost
    port: 8081
- name: add zone
  powerdns_zone:
    name: "{{ hive_name }}.{{ domain }}."
    nameservers: "{{ groups['hives'] | intersect(groups[hive_stage]) | map('regex_replace', '^(.*)$', '\\1.' + domain +'.' ) | list }}"
    kind: native
    state: present
    pdns_api_key: "{{ hostvars['powerdns'].db_password }}"
- name: add records for hives
  powerdns_record:
    name: "{{ item + '.' + domain + '.' }}"
    zone: "{{ hive_name }}.{{ domain }}"
    type: A
    content: "{{ hostvars[item].published_ip }}"
    ttl: 3600
    pdns_api_key: "{{ hostvars['powerdns'].db_password }}"
  loop: "{{ groups['hives'] | intersect(groups[hive_stage]) }}"
- name: add records for web services
  powerdns_record:
    name: "{{ item + '.' }}"
    zone: "{{ hive_name }}.{{ domain }}"
    type: LUA
    content: A "ifportup(80, {'{{ groups['hives'] | intersect(groups[hive_stage]) | map('extract', hostvars, ['published_ip']) | join(delimiter)}}'})"
    ttl: 20
    pdns_api_key: "{{ hostvars['powerdns'].db_password }}"
  loop: "{{ groups['services'] | intersect(groups[hive_stage]) | map('extract', hostvars, 'hive_labels') | select('defined') | map(attribute='published_fqdn') | select('defined') | list }}"

最初の2つのタスクで各コンテナ収容サーバ(グループ名= hives)のグローバルIPを調べて、 host変数の published_ip に設定しています。この role は powerdns サービスの initialize_roles を定義されているので、対象が powerdns サービスのコンテナとなることに 注意してください。最初の2つのタスクではコンテナ収容サーバに対象を切り替えるために delegate_to, delegate_facts を使用しています。

続くタスクでゾーンとレコードを登録しています。 ここで使用している powerdns_zone モジュールと powerdns_record モジュールは ansible の オフィシャルモジュールではありません。 hive ではlibディレクトリの下に置くことでカスタムモジュールを使用できます。 サンプルでは、 github の https://github.com/Nosmoht/ansible-module-powerdns で公開されているモジュールを ダウンロードして、lib の下に配置しています。 また、このモジュールは pdns_port プロパティにAPIのポート番号を指定する必要がありますが、 サンプルでは、 hive-builder が自動的に割り当てたポート番号を powerdns サービスのホスト変数 hive_ports からポート番号 8081 を公開しているものを検索し、 公開されるポート番号を取得しています。

サービスのログの閲覧

サービスのログはデフォルト(サービス定義で logging 属性を指定しなければ)ではリポジトリサーバに収集されます。 サービスのログを採取するためには

hive ssh

コマンドでリポジトリサーバにログインし /var/log/services/サービス名 のファイルを参照してください。

スタンドアロン型サービスのログ収集

スタンドアロン型サービスではサービス自身のログは initプロセス(=systemd)の出力となり、何も出力されません。 スタンドアロン型サービスのログをリポジトリサーバに収集する場合は、image のビルド時にビルトイン role の hive-syslog を指定してください。

外部リポジトリへのログイン

build-images、および deploy-services フェーズでイメージをダウンロードする際に外部リポジトリを利用することができます。 外部リポジトリにアクセスする際にログインが必要な場合、 hive_ext_repositories にログインに必要な情報を設定してください。 例えば、dockerhub にアクセスする場合、インベントリ(例えば、inventory/group_vars/all.yml)に以下のように設定してください。

credentials: "{{ lookup('file', lookup('env', 'HOME') + '/.hive/credentials.yml') | from_yaml }}"
hive_ext_repositories:
  - login_user: "{{ credentials.dockerhub_login_user}}"
    password: "{{ credentials.dockerhub_login_password}}"

上記では、ログインユーザとパスワードを秘密情報をまとめたファイル ~/.hive/credentials.yml から読み込んでます。

サーバにソフトウェアを追加インストール

hive-builder では、 addon という名前の role を追加することで、サーバにソフトウェアを追加インストールすることができます。 addon ロールは setup-hosts フェーズで呼ばれ、デフォルトでは become: True, become-user: root で実行されます。 アドミニストレータユーザで実行したい場合は、 become: False を指定してください。 例えば、サーバに zsh をインストールし、アドミニストレータのシェルを変更するのであれば、 roles/addon/tasks/main.yml を以下のように作成します。

---
- name: install zsh
  yum:
    name: zsh
    state: present
- name: set zsh as login shell for administrator
  user:
    name: "{{hive_safe_admin}}"
    shell: /bin/zsh

構築済みのサイトに addon ロールを追加した際には、以下のコマンドで反映します。

hive setup-hosts -T addon

separate_repository: True の状態で、リポジトリサーバを除いてコンテナ収容サーバにのみインストールしたいときは、以下のような when 条件を付与してください。

when: inventory_hostname in groups['hives']

グループを使った切り分け

hive のインベントリには以下のグループが定義されています。

グループ名

説明

drbd_volumes

DRBDのボリューム

first_hive

1台目のコンテナ収容サーバ

hives

コンテナ収容サーバ

hosts

全サーバ(mother + servers)

images

ビルドイメージ

mother

motherマシン

networks

仮想ネットワーク

private

privateステージでビルド対象となるもの

production

productionステージでビルド対象となるもの

repository

リポジトリサーバ

servers

サーバ(repsoitory ∪ hives)

services

サービス

staging

stagingステージでビルド対象となるもの

注釈

separate_repository: False の場合は最後の1台はコンテナ収容サーバ兼 リポジトリサーバとなるため、 hives, repository の両方に属します。

これらのグループを使って、when でビルド対象がグループに含まれる場合に 限定したり、 inventory/group_vars/グループ名.yml に変数定義することで 変数のスコープを限定したりすることができます。