2014年10月16日

CentOS 7 と Docker のメモ

さくらインターネットの VPS で OS を CentOS 7 にしたのでメモしておきます。 VPS のデフォルトは CentOS 6 なので、手動でカスタム OS を選択しています。 ディスクのパーティション設定や管理者ユーザーの設定はインストーラーで終わっているものとします。

パッケージの更新

SSH でログインしたら、まずは既存パッケージの更新を確認します。

$ sudo yum -y update
$ sudo yum -y upgrade

次に、管理用に必要そうなツールをインストールします。 curlgit はデフォルトでインストールされているかもしれませんが、更新確認のため指定しています。

$ sudo yum install -y vim tmux zsh curl git

その他、必要なものは好みでインストールしましょう。 ログインシェルを zsh に変えたり、環境設定ファイル (.zshrc など) を配置すると良いでしょう。

基本設定の確認

何はともあれ、この辺の内容を読みます。

起動時に有効になっているサービスの一覧を確認します。 chkconfig から systemctl に変更になり、出力結果もガラッと変わっています。

$ sudo systemctl list-unit-files
(出力省略)

ファイヤーウォールを管理するサービス (Features/firewalld - FedoraProject) を確認しておきます。

$ sudo systemctl list-unit-files | grep firewall
firewalld.service                                                       enabled

マニュアルに一通り目を通してみる (man firewalld) と、システム設定は /etc/firewalld に、 デフォルト設定は /usr/lib/firewalld に配置されているようです。 同名ファイルが存在する場合や読み込み順序が問題となる場合、細かい設定が気になる場合はこれらの中を見ておきましょう。

マニュアルの SEE ALSO セクションには、付属コマンドが列挙されています。 現在有効な設定を確認しておきます。オプションを付けるとサービスの追加、削除もできます。

$ sudo firewall-cmd --list-all

firewalld では「ゾーン」という単位で設定が管理されます。 新規にポートを開放したい場合も firewall-cmd でゾーンごとの設定を変更します。 従来の iptables を直接は使いませんので注意しましょう。 firewalld はバックエンドで iptables を利用していますが、併用はできないそうです。 設定間違いをなくすために iptables サービスを mask しておくと良さそうです。 この mask 機能も systemctl で慣れた方が良いと言えます。

仕組みと使い方はこちらが参考になります。

SSH の設定

SSH の設定をします。 Initial Server Setup with CentOS 7 | DigitalOcean の記事が参考になります。 今回は以下のことを実施します。

  • root でのアクセスを禁止
  • パスワード認証を禁止
  • 公開鍵交換の場合のみ許可
  • ログインできるユーザーを限定

/etc/ssh/sshd_config に以下の項目を設定します。 その他の項目は必要に応じて設定します。 ポート番号を22番以外にしたい場合もここで設定します。 なお、Red Hat Enterprise Linux では "UsePAM no" に設定すると良くないそうです。 /var/log/messages にもメッセージが出力されます。

AllowUsers {接続用のユーザー名}
AuthorizedKeysFile      .ssh/authorized_keys
ChallengeResponseAuthentication no
HostKey /etc/ssh/ssh_host_rsa_key
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
PubkeyAuthentication yes

変更を反映させます。

$ sudo systemctl reload sshd.service

次に、クライアント側から疎通を確認します。 SSH鍵を作成してパスワード認証無しでログインできること、鍵を未指定だとそもそもパスワード認証にならないことを確認します。 設定を確認できたらクライアントマシンの $HOME/.ssh/config に書き込んでおきましょう。

新規にSSH鍵を作成する場合は ssh-keygen を使います。 たとえば以下のようにして、生成された id_rsa をクライアントマシンにコピーします。 テスト用のコンテナとして利用していて、既存のSSH鍵がある場合はそれを流用しても良いでしょう。

$ ssh-keygen -t rsa && cd $HOME/.ssh &&
      cat id_rsa.pub >>authorized_keys && chmod 600 authorized_keys

続いて Web サーバーや DB サーバーもインストールしていきたいところですが、 せっかくなので Docker を使っていきます。

Docker のインストール

Docker をインストールして起動します。 CentOS 7 からは EPEL の docker-io は削除され、docker に名前を変えて標準レポジトリに追加されています。 なお、 yum でインストールできるのはちょっと古いバージョンなので、最新版が良い場合はバイナリを別途ダウンロードして差し替えましょう。

$ sudo yum install -y docker
$ sudo systemctl start docker

起動設定も有効にしておきます。 systemctl コマンドに統一されたのは覚えやすいと思います。

$ sudo systemctl enable docker

ファイヤーウォール設定を確認してみると、public ゾーンに "docker" が追加されているはずです。 これでコンテナから EXPOSE してマッピングされたポートが開放されます。たぶん。

$ sudo firewall-cmd --list-all

よく使いそうなベースイメージを取得しておきます。 回線速度にも依りますが、数GBはダウンロードしますので時間があるときにバックグラウンドで実行させましょう。

$ sudo docker pull ubuntu:14.04
$ sudo docker pull python:3.4
$ sudo docker pull python:3.4-onbuild
$ sudo docker pull node:0.10
$ sudo docker pull node:0.10-onbuild
$ sudo docker pull postgres:9.3
$ sudo docker pull nginx:1.7

それぞれの簡単な使い方は公式レポジトリに記載されています。 必要に応じて自分で Dockerfile を書いて拡張していきましょう。

docker コマンドを実行するときに sudo のパスワード確認が煩雑な場合には、ユーザーやグループを限定して利用可にしておくと良いでしょう。 /etc/sudoers.d 配下にファイルを配置しておきます。 例えば以下のような感じです。ここでは、管理者ユーザーは wheel グループに属しているものとします。

$ sudo cat /etc/sudoers.d/docker
%wheel     ALL=NOPASSWD: /usr/bin/docker

データベースサーバーの起動

Docker で PostgreSQL を起動します。

$ sudo docker run --name dbserver -d postgres:9.3

クライアントから接続できることを確認します。 --link オプションを使うことで、既存のコンテナが EXPOSE している接続先を環境変数で参照できます。 ここではIPアドレスとポート番号を使います。

$ sudo docker run -it --link dbserver:postgres --rm postgres:9.3 \
        sh -c 'exec psql -h "$POSTGRES_PORT_5432_TCP_ADDR" -p "$POSTGRES_PORT_5432_TCP_PORT" -U postgres'

サーバーのコンテナでは環境変数 PGDATA/var/lib/postgresql/data が指定されています。 ここに設定ファイル (postgresql.conf, pg_hba.conf) が配置されていますので、 自前の設定ファイルを利用するには Dockerfile を作成してビルドするか、別のストレージ用コンテナを --volume-from で参照しましょう。

コンテナ同士のデータ参照についてこれらの記事が参考になります。 テスト用のコンテナ作成、データのバックアップとリストアに役立つはずです。 "data only container" で検索すると関連記事がたくさん見つかると思います。

異なるバージョンを使うときはタグ名を変えます。 MySQL や Redis を使う場合も基本はだいたい一緒のはずです。

Web サーバーの起動

Docker で Nginx を起動します。 静的ファイル置き場はホストマシンのディレクトリをマウントします。今回は /opt/nginx/html を作成しておきます。 ポート番号のマッピングは動作環境に応じて実施します。 ホスト側でも Nginx を起動してプロキシすると、複数コンテナを起動したときにリクエストを振り分けられます。 外部から参照できない場合は、ファイヤーウォール設定も見直しましょう。

$ sudo mkdir -p /opt/nginx/html
$ sudo docker run --name webserver -d -p 80:80 \
        -v /opt/nginx/html:/usr/share/nginx/html:ro nginx:1.7

コンテナ内の設定ファイルは /etc/nginx/nginx.conf になります。 自前の設定ファイルを利用するには Dockerfile を作成してビルドするか、別のコンテナを --volume-from で参照しましょう。

デフォルトの設定ではアクセスログは標準出力 (/dev/stdout)、エラーログは標準エラー出力 (/dev/stderr) に出力されます。 テストでアクセスしたら確認しておきましょう。

$ sudo docker logs webserver

コンテナで生成されるログの管理方法は悩ましい問題ですが、こちらの記事が参考になると思います。

logspout (progrium/logspout) を使う場合は以下のようにテストできます。 rsyslog にログを投げる場合は送信先を引数で指定すれば良いそうです。 送信先 (routes) は後からも HTTP 経由で設定できます。

$ sudo docker pull progrium/logspout
$ sudo docker run --name logspout -d -v=/var/run/docker.sock:/tmp/docker.sock --privileged=true progrium/logspout

ソケットファイルを共有するので、パーミッションエラーが発生するかもしれません。 --privileged=true オプションを付けているか確認しましょう。 解決しない場合は、デーモンではなく対話的に起動してエラーメッセージを読みます。 -d オプションを -it オプションに変えて実行してみましょう。 手元の環境で --privileged=true オプションを付けなかった場合は、以下のエラーが出力されていました。

attacher: dial unix /tmp/docker.sock: permission denied

関係しそうな Issue もありますので、環境によっては解決が困難な場合もあるのかもしれません。

各種コンテナを起動できたら、nginx が標準出力に書き出すアクセスログを logspout に拾わせて、rsyslog サーバーに投げます。 logspout は8000番ポートで HTTP サーバーが起動しており、 /routes に JSON を POST することで配送先を追加できます。 curl を使って以下のような感じで実行できます。IPアドレスなどは実行環境に応じて調整します。

$ sudo docker inspect logspout | grep -i ipaddress
(出力さたIPアドレスをコピー)
$ DOCKER_LOGSPOUT={コピーしたIPアドレス}
$ curl $DOCKER_LOGSPOUT:8000/routes -X POST \
        -d '{"source": {"filter": "web", "types": ["stdout"]}, "target": {"type": "syslog", "addr": "{rsyslogサーバー}:514"}}'

ルーティングは GET リクエストで確認できます。 削除するときは ID をパスに追加して DELETE リクエストを投げます。

$ curl $DOCKER_LOGSPOUT:8000/routes
(出力省略)
$ curl $DOCKER_LOGSPOUT:8000/routes/{削除したい配送経路ID} -X DELETE

ログの配送

logspout の送信先はホストマシンの rsyslog でも構いませんが、保存や解析をスムーズに行うため Fluentd を使います。 Fluentd を使う場合、概要から設定と動作確認に関してと、syslog の扱いはこれらの記事が参考になります。

同じような手順を記載した Dockerfile を準備してビルドしましょう。 なお、TreasureData の td-agent は CentOS 7 に対応中のようで、2014年10月16日現在はインストーラーを使えません。 systemd 対応などは面倒なのかもしれません。 ベースイメージには CentOS 6 系か Ubuntu, Debian などを使いましょう。

設定ファイル (td-agent の場合は /etc/td-agent/td-agent.conf) には以下のセクションを用意しておきます。

<source>
  type syslog
  port 5140
  bind 0.0.0.0
  tag syslog
  # tag debug.syslog
  # Be sure to match your syslog sender date/time format.
  format /^(?<time>[^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
</source>

format の指定方法はマニュアル (syslog Input Plugin | Fluentd - 英語版) にありますが、優先度 pri を含まないように注意が必要です。 また、 RFC 5424 - The Syslog ProtocolRFC 3164 - The BSD Syslog Protocol を更新するものですが、時刻の表現形式が変わっています。 パースできない場合は Fluentd がエラーメッセージを出しているはずなので確認しましょう。

あとは Fluentd プラグインの fluent-plugin-parser で "message" 部分を正規表現でマッチさせて JSON にしたものを S3 に投げます。 既存のログ集約サーバーがある場合は、さらに Fluentd ⇒ Fluentd で転送します。

ここまでできたら、logspout が回収した nginx の標準出力を Fluentd に転送します。 HTTP 経由で配送経路を追加しましょう。

$ sudo docker inspect fluentd | grep -i ipaddress
(出力さたIPアドレスをコピー)
$ DOCKER_FLUENTD={コピーしたIPアドレス}
$ curl $DOCKER_LOGSPOUT:8000/routes -X POST \
        -d '{"source": {"filter": "web", "types": ["stdout"]}, "target": {"type": "syslog", "addr": "$DOCKER_FLUENTD:5140"}}'

構成を図にすると以下のような感じになります。

終わりに

OS をアップデートする機会は多くありませんので、基本的なことをメモしました。 CentOS 7 ではサービス起動処理とファイヤーウォール設定が大幅に変わっています。 まずはコマンドの使い方と出力の読み方、そしてファイル配置には慣れていかないといけないようです。

CentOS の Docker 公式イメージもありますので、気軽に試す場合はこれが良いと思います。 タグを指定することで好みのバージョンを利用できます。 また、AWS でも CentOS 7 が標準的に利用可能になっているようです。

OS のバージョンを上げると使い勝手が変わりますが、Docker を活用してコンテナ化しておくと今後の移行も簡単になりそうです。 firewalld のゾーンも、コンテナ管理とは相性が良いのではないかと思います。 あとは監視系の設定もしないとなぁと実感しました。

Windows 関係でも Docker サポートが進むそうなので、規模の大小に関わらずコンテナ型のサーバー管理は必須になっていきそうです。

コメントを投稿