読者です 読者をやめる 読者になる 読者になる

exlar's IT note

ITやスマートデバイスを中心とした趣味情報の寄せ集め

シンプルな Docker 実行環境 CentOS 7.2 に構築する

本記事の目標

Docker実行環境を CentOS で整えるにあたり、2016/4/23 現在での情報をまとめるものです。

f:id:exlair:20160423224522p:plain

OSのバージョンは、CentOS Linux release 7.2.1511 (Core) を利用しています。

背景(余談)

以前、CentOS6.6 から CentOS 7.0 へアップグレードするにあたり、色々と依存関係にひっかかっかりつつ強引なアップグレードを強行したのですが、その弊害でいま新たなパッケージをインストールすると失敗するケースが度々発生してしまうおかしい状態になっていました。

当該マシンでは Docker 1.2 を利用してましたが、現在はAPIバージョンも変更されており新たな Docker Image の取得さえ困難に…ということで、新規インストールすることとしました。

前提:マシンの準備

ESXiで作成した仮想マシン上にインストールすることを想定しています。 もちろん、物理マシンでも手順は大きく変わりませんが、余計な作業をしています。

OS イメージの入手

Download CentOS

http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1511.iso

OSインストール直後の設定

パッケージを最新化

とりあえず何も考えずに各パッケージを最新化します。

yum update

仮想マシン環境下におけるポイント

仮想マシンを作成する際に NIC を VMXNET3 でしか作成しなかった場合、VMwareTooolsをインストールしていない状態ではIPアドレスの割当が行われず、ネットワークへの接続ができなくなります。そして、VMwareTools を利用しようにも Minimal 環境下では Perl もインストールされていませんので詰んでしまいます。

その場合、一度シャットダウンした後、仮想マシンの編集で NIC を E1000 で追加して再度起動すると、DHCPでアドレスが取得されるはずです。

VMwareTools もどきの open-vm-tools をインストール

レポジトリを追加する

vi /etc/yum.repos.d/vmware-tools.repo

# 記載内容は以下のとおり 
[vmware-tools]
name = VMware Tools
baseurl = http://packages.vmware.com/packages/rhel7/x86_64/
enabled = 1
gpgcheck = 1

open-vm-tools をインストールする。

# 鍵の取得
rpm --import http://packages.vmware.com/tools/keys/VMWARE-PACKAGING-GPG-RSA-KEY.pub

# アップデートしたうえで open-vm-tools が見つかるか確認
yum update && yum search open-vm-tools

# 問題なければインストール
yum install open-vm-tools

インストール前の ip a show (ifconfig相当)

inet6など若干省略しています。ens160(VMXNET3で作成したNIC)にIPアドレスが割り振られていないことが分かります。なお、数字は環境により異なります。

[root@yourhost ~]# ip a show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff
3: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:XX:XX:XX:YY brd ff:ff:ff:ff:ff:ff
    inet 192.168.4.56/24 brd 10.0.4.255 scope global dynamic ens33
       valid_lft 85504sec preferred_lft 85504sec

ネットワーク設定の変更

先ほど割り当てたNICは暫定的なものなので、VMXNET3 のNICに対し固定IPを設定します。(もちろん、DHCPで運用を続ける場合は必要ありません)

従来 /etc/sysconfig/network-scriptsifcfg-xxxx を直接変更してきましたが、CentOS 7 からは nmcli コマンドが推奨されているようなので、コンフィグを直接編集する代わりにコマンドを利用してみます。

CentOS 7.1.1503ではnmcliでIPアドレスを固定する方法が変更された - Qiita

# nmcli connection modify ens160 ipv4.method manual ipv4.addresses 10.0.4.13/24 ipv4.gateway 10.0.4.1 ipv4.dns "10.0.4.12 10.0.4.15"

すると、/etc/sysconfig/network-scripts でも変更した対象のコンフィグが変更されます。その後rebootにて固定IPが設定されていることを確認しましょう。

# systemctl restart NetworkManager

こちらのコマンドでもよいみたいですが。 なお、ONBOOT が yes になっているかしっかり確認しましょう。

- ONBOOT=no
+ ONBOOT=yes

OSインストール後の定番の設定

各自好みもでてきますので、必要な設定を行ってください。 ここでは以下の状態を目指します。

  • docker コマンドは一般ユーザーで行えるようにする (<--Dockerインストール後の作業)
  • root には原則スイッチしない。sudoで行う
  • SSHは鍵認証のみに制限し、かつログイン可能ユーザーも限定する
  • SELinuxはOFFに…せず、本当に面倒くさくなった時にOFFにする
    • 最初っからセキュリティレベルを下げすぎる意味はないという意味
    • Dockerでの利用が主ならば、それで済むことも多いはず
    • 参考記事:SELinux と Docker と OpenShift v3 - Qiita
  • シェルはzsh。コンフィグには Prezto を利用して楽をする

その他インストールパッケージ群

  • sudo yum install -y git
  • sudo yum install -y zsh

OSバージョンアップなど今後の運用負担を考慮し、できるだけ最小限にしています。

各種リンク

Docker のインストール

$ yum info docker

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

インストール直後は起動していません。

● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: http://docs.docker.com

念のため再起動して、自動起動するか確認します。

● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since 土 2016-04-23 21:43:01 JST; 15s ago
     Docs: http://docs.docker.com
 Main PID: 891 (sh)
   CGroup: /system.slice/docker.service
           ├─891 /bin/sh -c /usr/bin/docker daemon $OPTIONS            $DOCKER_STORAGE_OPTIONS            $DOCKER_NETWORK_OPTIONS    ...
           ├─894 /usr/bin/docker daemon --selinux-enabled
           └─895 /usr/bin/forward-journald -tag docker

上がってこない場合は、有効化しましょう。

$ sudo systemctl enable docker

一般ユーザーで Docker コマンドを利用する設定

利用したいユーザーを Docker のグループに追加します。 CentOS 7 の場合、グループは dockerroot のようですね。docker とやると、対象は存在しないと警告されてしまいます。

で、グループ追加するだけでOK...のはずだったのですが。

$ sudo gpasswd -a user_onamae dockerroot
$ docker ps
Cannot connect to the Docker daemon. Is the docker daemon running on this host?

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED   ...

ここでは、sudo にて docker ps コマンド(動作確認を目的として、稼働中のコンテナを表示するコマンド)を叩くときちんと動作していますが、通常ユーザーでは警告が表示され上手くいかないという状態を表しています。

とりあえず対処策として、下記対処で様子を見ることとしました。

# Modify these options if you want to change the way the docker daemon runs
#OPTIONS='--selinux-enabled'
OPTIONS='--selinux-enabled -G dockerroot'

via Ec2 Centos7 + Docker 1.6 · Issue #13098 · docker/docker

修正前の動作 docker.sock の状態

$ ls -l /var/run/ | grep docker
srw-rw----.  1 root root    0  4月 23 21:49 docker.sock

修正後の動作 docker.sock の状態

$ ls -l /var/run/ | grep docker
srw-rw----.  1 root dockerroot    0  4月 23 21:57 docker.sock

動作確認:

$ docker pull busybox

Using default tag: latest
Trying to pull repository docker.io/library/busybox ... latest: Pulling from library/busybox
4b51ded9aed1: Pull complete 
307ac631f1b5: Pull complete 
Digest: sha256:4a887a2326ec9e0fa90cce7b4764b0e627b5d6afcb81a3f73c85dc29cea00048
Status: Downloaded newer image for docker.io/busybox:latest


$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
docker.io/busybox   latest              307ac631f1b5        5 weeks ago         1.113 MB

$ docker run -it --rm busybox
Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.
/ # 
/ # exit

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

ひとまず動いているようです。 ここでは、

  1. 軽量のイメージ busybox をダウンロード-
  2. その後 busybox Image から docker run にてコンテナを起動(終了したらコンテナを即削除する --rm オプション付)
  3. exit にてコンテナを終了
  4. docker ps -a (終了しているコンテナを含め全てを表示) し、何も表示されない=コンテナが削除されていること

を一連のステップで確認しています。

これで Docker の実行環境が整いました。

$ docker version
Client:
 Version:         1.9.1
 API version:     1.21
 Package version: docker-1.9.1-25.el7.centos.x86_64
 Go version:      go1.4.2
 Git commit:      78ee77d/1.9.1
 Built:           
 OS/Arch:         linux/amd64

Server:
 Version:         1.9.1
 API version:     1.21
 Package version: docker-1.9.1-25.el7.centos.x86_64
 Go version:      go1.4.2
 Git commit:      78ee77d/1.9.1
 Built:           
 OS/Arch:         linux/amd64

ここまでで消費しているディスクサイズは 1.5GB ほどで、とてもスッキリしています。

$ df -h
ファイルシス               サイズ  使用  残り   使用%  マウント位置
/dev/mapper/centos-root    48G  1.3G   47G    3%  /
tmpfs                     921M  8.5M  912M    1%  /run
/dev/sda1                 497M  166M  332M   34%  /boot
 ..(一部省略)

なお、ここでは触れていませんが Docker Compose のインストールもほぼ必須です。ないことで何かができなくなるということはありませんが、Dockerコンテナの扱いが非常に楽になります。Dockerで実行可能な各種ソフトウェア群の Quick Start なんかでは Docker Compose のコンフィグがベタ書きされていることも多くあります。

Install Compose

まとめ

Docker を使うための最低限のシンプルな環境構築ということで一通りの手順を記載しました。

この頃、OSSなど多くのものが Docker Image を配布してくれているおかげで、「ちょっと試してみたいな」と思った時の敷居が非常に小さくなり嬉しいです。 とりあえず docker pull && docker run すればいいのですから。

WordPressを無料のSSL証明書 Let's Encrypt でHTTPS化する

無料のSSL証明書 Let's Encrypt とは

WebサイトのHTTPS化が加速しているなか、個人利用においては証明書の取得コストが若干の障壁になっていましたが、無料のSSL証明書発行の仕組み Lets's Encrypt が正式版になったので利用してみます。

Let's Encrypt - Free SSL/TLS Certificates

SSLを用いたHTTP通信の暗号化を誰でも手軽に行えるようにするために立ち上がったのがEFF、Mozilla、Cisco Systems、Akamai Technologies、IdenTrust、ミシガン大学の研究者などで、これらのメンバーがHTTPS普及のためにスタートした取り組みが「Let's Encrypt」です。これまで手間がかかり金銭的な負担も大きいと言われてきたサーバー証明書の発行を無料で行えるようになるのですが、同取り組みはついにベータ版から正式版にサービスを移行しています。

無料で証明書を発行してHTTPSの導入をサポートする「Let’s Encrypt」がベータ版から正式版に - GIGAZINE

本エントリの目的

  • (#1) 手始めに、WordPressで構築しているBLOGをHTTPS化します
  • (#2) 当該サイトは、R-PROXY(nginx)による多段構成でWordPressに接続します

構成は以下のとおりとします。

+--------------+            +----------+            +--------------+
| Client(User) | ---------> | R-Proxy  | ---------> | Apache       |
|              | HTTPS(#1)  | (nginx)  |  HTTP(#2)  | (wordpress)  |
+--------------+  http2     +----------+            +--------------+

ハマりポイント1. WordPressがHTTPのURLを吐きつづける

(#1)のR-PROXY(nginx)に対してSSL証明書を適用する部分は証明書が Let's Encrypt 発行になるだけで、適用プロセスそのものは通常の証明書と同様、特別なことはありません。むしろCSR発行が作業不要なので楽です。しかし(#2)の部分でHTTPを利用しているところが曲者で、WordPress本体としてはHTTPでアクセスを受け付けているのでHTMLに書き出される各種リソースURLの部分がHTTPのままになってしまい、ユーザーアクセスがいつまでたってもHTTPSに置き換わりません。

対処として以下のとおりコンフィグレーションします。

バックエンドのWordPressに対してプロトコルを通知する

httpsであることを通知するために X-Forwarded-Proto ヘッダがよく利用されています。nginxでもそれを連携できるようします。

前提として、リバースプロキシ構成を取る時の一般的なコンフィグ location / { ... } にてバックエンドのWordPressに中継している場合とします。

    location / {
        proxy_pass http://wordpress;
        ...中略
        proxy_set_header X-Forwarded-Proto $scheme;
    }

WordPress側でのHTTPS出力設定

wp-config.php に以下の記載を追加します。

  • WP_HOME および WP_SITEURL でのアドレスは各自ご利用のものに書き換えます。HTTPS化を前提としていますので、もちろん https://xxxx で記載します
  • FORCE_SSL_LOGINFORCE_SSL_ADMIN は、管理画面のみをSSL化する場合のオプションです。サイト全体をSSL化する場合は必要ありませんが明示のために気分で付与しています
  • if文で HTTP_X_FORWARDED_PROTO に関する記載があります。ここで、WORDPRESSがコンテンツ出力する時のURL群に適用する scheme を動的に変化させています。つまり、ユーザーからHTTPSアクセスが発生した祭には nginx にて X-Forwarded-Proto: https がWordPressのサーバーへ連携され、WordPressではそれを見てBODY出力の scheme をHTTPSにします
define('WP_HOME',    'https://example.com');
define('WP_SITEURL', 'https://example.com');
define('FORCE_SSL_LOGIN', true);
define('FORCE_SSL_ADMIN', true);
if ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
  $_SERVER['HTTPS']='on';
}

HTTP_X_FORWARDED_PROTO の設定があれば、わざわざ WP_HOME, WP_SITEURL に対して設定は必要ないようにも見えるのですが、http のままにしておくと以下のように この接続は安全ではありません。このページの一部(画像など)は安全ではありません。 と警告が発生します。

f:id:exlair:20160417181300p:plain

f:id:exlair:20160417181305p:plain

なお、WP_HOME, WP_SITEURL は WordPress 管理者画面 -> 設定 -> 一般設定にある項目と同じ役割を示しています。

wp-config.php上の表現 管理者画面上の表現
WP_SITEURL WordPress アドレス (URL)
WP_HOME サイトアドレス (URL)

f:id:exlair:20160417181309p:plain

WP_SITEURL = サイトアドレス(URL)のように見えますが入れ子になっているので要注意

管理者画面上から変更してもよいのですが、環境によっては設定を失敗すると無限ループが発生して管理者画面へアクセスできなくなり、その場合はSQLを直接叩いて修正する必要がでてきます。wp-config.phpの方が効力が強いので上書き可能ですが、構成検証のし易さの観点で、ここでは初めから wp-config.php に記載しておきます。

無限ループが発生してしまった場合

wp_optionsテーブルに対して、以下のSQLで対処可能です。 現在利用しているバージョンのWordPressでも同様であるかは念のための確認を推奨します。

各キーが存在するか、そしてその内容が現在の遭遇している状態と正しいかを確認する。

select * from wp_options where option_name = 'home';
select * from wp_options where option_name = 'siteurl';

値を書き換える。

update wp_options set option_value = '(正しいURL)' where option_id = (確認したoption_id);

ハマりポイント2. 一部URLがHTTPのまま残り続ける

恐らく理由としては2通りです。

  1. plugin に http 固定値での文字列が埋め込まれている
  2. 自分で作成したwidgetやテーマなど何らかの形でhttpの固定値が埋め込まれている

この状態だと、いつまで経ってもブラウザのHTTPS暗号化マーク(鍵マーク)が表示されません。これは、対象のページ内で利用されているコンテンツが全てHTTPSで保護された接続になっている必要があるためです。

地道ですが、Chromeのデベロッパーツール(Google 検索)などを利用しながらHTTPアクセスとなっている部分を確認し、スクリプトの修正が、場合によってはPluginの見直し(廃止やその他同類プラグインへの置き換え)を実施します。

Let's Encrypt 証明書適用方法

ハマりポイントを先に記載したところで、以下通常の適用方法を記載します。 以下の点を予め抑えておくと理解が捗ります。

  • CSR作成や証明書発行機関への送付などの面倒臭い処理は、すべてLet's Encryptが配布しているスクリプトにより自動化されている
  • 証明書などの各種ファイルは、/etc/letsencrypt/へドメイン毎に格納されている
  • 発行された証明書の有効期限は90日と短め。自動更新のcronを設定することが望ましい
  • 発行時にはDV認証(ドメインの保有者であることの確認。組織の認証は行わない)が行われる。その際、スクリプト実行時のサーバーの80または443が利用される
  • 更新(renew)コマンドは、証明書有効期限30日未満の場合は実行されない

80または443をどういう優先順で利用されるのかは調べておらず、把握していません。 DV認証時、既に動いているWebサーバーがある場合はポートが競合しますので、対応として2種類の方法が存在します。

共通作業:Let's Encryptが配布するスクリプトをダウンロード

gitを利用します。保存場所は任意で構いません。

$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ ./letsencrypt-auto --help

実行時にsudoをする必要はありません。必要な時はスクリプト内でパスワードを求められます。

稼働中のサーバーを落として認証用のサーバーを起動 (standalone)

sandaloneモードでスクリプトを起動します。

$ ./letsencrypt-auto certonly -a standalone -d example.com
                                               (ドメイン部分は適宜修正)

メッセージと共に /etc/letsencrypt/live/(ドメイン)/fullchain.pem が生成されていれば成功です。

DV認証そのものにあたっては既存Webサーバーに対して設定変更が不要ですが、cronでの更新自動化を行うには、nginxの停止・起動を含めて処理を組む必要があります。

現在稼働中サーバーに認証用のアクセスを許容する(webroot)

$ ./letsencrypt-auto certonly --webroot -w /path/to/doc_root/ -d example.com
                                                       (ドメイン部分は適宜修正)

webrootの仕組み
/path/to/doc_root/で設定した部分に、スクリプトが /.well-known/acme-challenge/xxxxxx(←ランダム文字) という構造でファイルを作成し認証しているようです。従って、WordPressをリバースプロキシしているnginxに対しては、以下のコンフィグを適用する必要があります。

    location ^~ /.well-known/acme-challenge {
        root /path/to/doc_root/;
    }

standaloneモードに対し、こちらの場合は一度設定してしまえばnginxの停止・起動は不要になります。但し、SSL証明書を更新後の再読み込みにはreloadが必要ですので、その処理は残ります。

nginxへのSSL証明書適用

ここは一般的なノウハウとなります。証明書は以下のとおり。

    ssl_certificate /etc/letsencrypt/live/(ドメイン)/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/(ドメイン)/privkey.pem;

しばらくは 80, 443 いずれも残した状態で動作確認を行い、443ポートでの展開ができる状態になったら、元々のHTTP(80ポート)へのアクセスは全てHTTPSページで誘導するよう、リダイレクト設定を行います。

server {
    listen 80; # httpへのアクセスをhttpsへリダイレクトする
    listen [::]:80;
    return 301 https://$host$request_uri; 
}

server {
    listen 443 ssl;
    ...
    省略
}

まとめ

証明書発行は楽だし、更新も自動化できるし、ドメイン数を気にせず発行できるし、良い仕組みだと思います。

PHP-FPM インストール時に UnicodeDecodeError 発生(Ubuntu 14.04)

PHP-FPM をインストールする

Docker上でPHP-FPM用を構築しようとすると下記コマンドでインストールを試みた。

apt-get install -y php5-fpm

しかし、下記の通り依存関係ではまってしまった。

php5-common (= 5.5.9+dfsg-1ubuntu4.11) but 5.5.9+dfsg-1ubuntu4.12 is to be installed

手っ取り早く解決したかったので、外部レポジトリを追加。

add-apt-repository -y ppa:ondrej/php5

※ もし add-apt-repository が使えない場合は先にこちらを投入。

apt-get install -y software-properties-common

UnicodeDecodeError: 'ascii' codec can't decode byte

ここで一応使えるようになったのだが、レポジトリ追加時にエンコードのエラーが発生した。

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 92: ordinal not in range(128)

海外のフォーラム等で調べたところ locale の問題ので、下記方法で解決する。

locale-gen en_US.UTF-8
LC_ALL=en_US.UTF-8 add-apt-repository -y ppa:ondrej/php5