docker
docker-compose
rancher
letsencrypt
docker-volume-netshare

nfsマウントで証明書をマルチホストコンテナ間共有してみる

dockerちょう初心者です。
前回でrancherのバックエンドはなんとなくHAにしたけど、
フロントエンドのHAをletsencryptでhttpsでやるにはどうすっかなと悩んだ結果以下の2択という話にとりあえずしました。(ちなみに要件としてインスタンスがAzureしばりプレイ)

・ApplicationGatewayにletsencryptの証明書をazコマンドとかで投げ込むやつ
http://level69.net/archives/24281

・マスタでnginx-proxy+letsencryptでnfs公開してるとこに証明書を置きスレーブでnfsマウントしてそれを読む+route53でhelthcheckでフェイルオーバー仕込むか愚直にラウンドロビンか(使う予定のdomainがAWS側のzoneになんか登録されてたので)

で、LBお金かかるし今回の構成に要る感じでもないかなあと思ってnfs側をとりあえずやってみました。

ちなみによくわかってなくて一応試したけど(当たり前のように)失敗したやつ
 ・AzureFileのcifs/sambaマウントをコンテナで試したけど全然無理だった。
  →rancherストレージなPRはみかけたのでそのうち?→ https://github.com/rancher/storage/pull/53
 ・AzureFileをLinuxにcifs/sambaマウントしたとこをnfs公開、も明示的なエラーで全然無理だった。

構成情報など

rancher01マスタ
rancher02スレーブ
(正確には、letsencryptとmysqld的には01がマスタでnginxとrancherはマルチマスタで、
 rancherからみさしてるdbは01側のマスタです。)
Azure上のホストでOSはUbuntu16LTS
etcdでオーバレイネットワークを一応組んである状態

nfsの準備

・nfsサーバを01に入れてディレクトリ公開

apt-get install nfs-kernel-server
vi /etc/exports
/nfsdata 10.0.0.0/22(rw,sync,no_subtree_check,no_root_squash) 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash)
exportfs -ra
rpcinfo -p

・nfsクライアントを02に入れてマウントできるか一応試しておく

apt-get install nfs-common
mkdir /tmp/test
mount -t nfs 10.0.0.4:/nfsdata /tmp/test/ -o hard,intr
mount
df -h

centosとubuntsuでパッケージ名違うパターンでややこしかったのであとでansible化して忘れたい気がしました。

docker volumeまわりの常識風?な知らなかったことなど

3分くらいで分かるdockerのdata volume - Qiita
何もボリュームドライバ指定しないとlocalマウントになる。
シングルホストならコンテナ間でvolumes_fromとかでvolumeを共有することが可能だがマルチホストだとnfsなどのlocalじゃないvolumeドライバが要る
ボリュームは明示的に消さないと消えないっぽく取っとかれたやつを再度マウントすることも可能
docker volume lsで現在あるボリュームを確認できる
コンテナが使ってるボリュームを調べるには以下でボリューム名を見る
docker inspect -f "{{range .Mounts}}{{.Name}}{{end}}" ${container_name}
未使用ボリューム一括削除は以下のようになる
docker volume rm $(docker volume ls -qf dangling=true)
ローカル側のマウントパス指定しないと/var/lib/docker下のコンテナ名等がついた長ったらしいパスにマウントされる模様
オーバレイネットワークでマルチホストだと別のホストでもコンテナ名が重複するとコンテナ起動できない

docker-volume-netshareプラグインを入れてsystemd的に操れるようにして自動起動有効化

公式リポジトリに色々かいてありその通りにやると大体OKですね

・パッケージ落としてきて入れる

$ wget https://github.com/ContainX/docker-volume-netshare/releases/download/v0.17/docker-volume-netshare_0.17_amd64.deb
$ sudo dpkg -i docker-volume-netshare_0.17_amd64.deb

・unitファイルを置いて設定して起動

なんか普通に入れるとinitの起動スクリプトしか置かれてなかったのと、
公式のUnitファイルがなんかRedhadファミリー向けしか置いてなかったのでEnvironmentFileのpathとかをUbuntuでも動くように変えました。

# vi /etc/systemd/system/docker-volume-netshare.service
[Unit]
Description=Docker volume plugin supporting NFS, EFS and CIFS
Documentation=https://github.com/gondor/docker-volume-netshare
Before=docker.service

[Service]
EnvironmentFile=/etc/default/docker-volume-netshare
ExecStart=/usr/bin/docker-volume-netshare $DKV_NETSHARE_OPTS
StandardOutput=syslog

[Install]
WantedBy=multi-user.target


# vi /etc/default/docker-volume-netshare
## NFS
DKV_NETSHARE_OPTS="nfs" 

# systemctl enable docker-volume-netshare.service
# systemctl start docker-volume-netshare.service
# systemctl status docker-volume-netshare.service

docker.service再起動しとく

# systemctl restart docker.service
# systemctl status docker.service

docker-compose.ymlの準備

rancher01のdocker-compose.yml
version: '2'
services:
  rancher-server:
    image: rancher/server:latest
    container_name: "rancher"
    depends_on:
      - mysql-master
    restart: unless-stopped
    command: --db-host rancher01 --db-port 3306 --db-name ${DB_SCHEMA} --db-user root --db-pass ${MYSQL_ROOT_PASSWORD} --advertise-address rancher01
    environment:
      VIRTUAL_PORT: 8080
      VIRTUAL_HOST: my-rancher.example.com
      LETSENCRYPT_HOST: my-rancher.example.com
      LETSENCRYPT_EMAIL: myaddr@example.net
    ports:
      - 8080:8080
      - 9345:9345
  mysql-master:
    image: mysql:latest
    container_name: "mysql-master"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_SCHEMA}
    ports:
      - 3306:3306
    volumes:
      - /home/docker/rancher-server/mysql:/var/lib/mysql:rw
      - /home/docker/rancher-server/mycnf/master:/etc/mysql/conf.d
      - /home/docker/rancher-server/init-sql-master:/docker-entrypoint-initdb.d
  nginx-proxy:
    image: jwilder/nginx-proxy:latest
    container_name: "nginx-proxy"
    restart: always
    depends_on:
      - rancher-server
    ports:
     - "80:80"
     - "443:443"
    volumes:
      - /nfsdata/ssl:/etc/nginx/certs:ro ##★nfs公開directory★
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
  letsencrypt-nginx-proxy-companion:
    image: jrcs/letsencrypt-nginx-proxy-companion:latest
    container_name: "letsencrypt"
    depends_on:
      - rancher-server
      - nginx-proxy
    volumes_from:
      - nginx-proxy
    volumes:
      - /nfsdata/ssl:/etc/nginx/certs:rw ##★nfs公開directory★
      - /var/run/docker.sock:/var/run/docker.sock:ro
networks:
  default:
    external:
      name: common_link
rancher02のdocker-compose.yml
version: '2'
volumes:    ##★volumeドライバとオプションを指定★
  nfsdata:
    driver: 'nfs'
    driver_opts:
      share: 10.0.0.4:/nfsdata/ssl
services:
  rancher-server:
    image: rancher/server:latest
    container_name: "rancher2"
    depends_on:
      - mysql-slave
    restart: unless-stopped
    command: --db-host rancher01 --db-port 3306 --db-name ${DB_SCHEMA} --db-user root --db-pass ${MYSQL_ROOT_PASSWORD} --advertise-address rancher02
    environment:
      VIRTUAL_PORT: 8080
      VIRTUAL_HOST: my-rancher.example.com
    ports:
      - 8080:8080
      - 9345:9345
  mysql-slave:
    image: mysql:latest
    container_name: "mysql-slave"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_SCHEMA}
    ports:
      - 3306:3306
    volumes:
      - '/home/docker/rancher-server/mysql:/var/lib/mysql:rw'
      - '/home/docker/rancher-server/mycnf/slave:/etc/mysql/conf.d'
      - '/home/docker/rancher-server/init-sql-slave:/docker-entrypoint-initdb.d'
  nginx-proxy:
    image: jwilder/nginx-proxy:latest
    container_name: "nginx-proxy2"
    restart: always
    depends_on:
      - rancher-server
    ports:
     - "80:80"
     - "443:443"
    volumes:
      - nfsdata:/etc/nginx/certs:ro ##★証明書をマウントしたとこから読む★
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
networks:
  default:
    external:
      name: common_link

docker-compose up -d したあとのnfsマウントほかの状況

rancher02のマウント状況など
# df -h
Filesystem                                         Size  Used Avail Use% Mounted on
~略~
10.0.0.4:/nfsdata/ssl                               30G   13G   17G  45% /var/lib/docker-volumes/netshare/nfs/rancherserver_nfsdata
# ls /var/lib/docker-volumes/netshare/nfs/rancherserver_nfsdata/
dhparam.pem  my-rancher.example.net

docker-compose_ps
rancher01:/home/docker/rancher-server# docker-compose ps
          Name                     Command                     State                      Ports           
---------------------------------------------------------------------------------------------------------
letsencrypt                /bin/bash                  Up                                                  
                           /app/entrypoint. ...                                                           
mysql-master               docker-entrypoint.sh       Up                         0.0.0.0:3306->3306/tcp   
                           mysqld                                                                         
nginx-proxy                /app/docker-               Up                         0.0.0.0:443->443/tcp,    
                           entrypoint.sh  ...                                    0.0.0.0:80->80/tcp       
rancher                    /usr/bin/entry --db-host   Up                         3306/tcp,                
                           i ...                                                 0.0.0.0:8080->8080/tcp,  
                                                                                 0.0.0.0:9345->9345/tcp   

rancher02:/home/docker/rancher-server# docker-compose ps
    Name                  Command               State                            Ports                           
----------------------------------------------------------------------------------------------------------------
mysql-slave    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp                                   
nginx-proxy2   /app/docker-entrypoint.sh  ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp                 
rancher2       /usr/bin/entry --db-host i ...   Up      3306/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:9345->9345/tcp 

とりあえずローカルPCのhosts登録してどっちでもrancher表示できることは確認しました。
route53はhealthcheck設定したらたぶん追記するとは思いますが↓LBと仕様の違いを見かけたので備忘禄。
https://dev.classmethod.jp/cloud/aws/health-check-spec-elb-route53/

跡形もなくやりなおす場合の備忘録

## 全てのコンテナを一括削除
# docker rm -f $(docker ps -aq)
# dokcer ps

## docker-composeでたてたコンテナを消す
# docker-compose down
# docker-compose ps

## 未使用volume一括削除
# docker volume rm $(docker volume ls -qf dangling=true)
# docker volume ls

## systemdでdockerを再起動
# systemctl restart docker.service
# systemctl status docker.service

## mysqlのデータディレクトリを消す(残したかったらmysqldumpしといて入れなおすといいと思う)
# rm -rf ./path_to_mysql_datadir/*
# ls -lh ./path_to_mysql_datadir/

## OS再起動(iptables設定が変に残ってキモい時など)
# shutdown -r now

ハマりどころと感想と疑問点など

けっこうdocker何の関係もないのも多かったですね。
→nfs公開範囲のサブネットのレンジ書き間違えて接続できなくて悩んだ
→nfs組むとしてHA化するならDRBDが割と手間なのでクラウドストレージコンテナマウントしたいとやってみたが無理だった
→unitファイルの作り方を自分でやって失敗したが公式サイトに普通に書いてあったり
→mysqlのレプリケーションが1872エラーで失敗するのでreset slave all;してchange masterやり直した(スレーブ起動前にマスタを待つ時間が短すぎたのかもしれない)
→オーバレイとか組んだりHA組んだりしたけどrancher的にはホスト登録すると独立コンテナ表示になってるようなのでスタックっぽくするにはどうしたらいいのかよくわかってない(オレオレカタログ生成するようになったらわかるんでしょうか)。

参考サイト

Netshare - Docker volume plugin for NFS 3/4, EFS and CIFS/SMB
NFS 3/4、EFS、CIFS / SMB用のNetshare - Dockerボリュームプラグイン
DockerコンテナにNFSボリュームをマウントする---Netshare Pluginを使ってみる - Qiita
Dockerのボリュームプラグインとストレージドライバ(Dockerの最新機能を使ってみよう:第2回) | さくらのナレッジ
rancher-cifs volume driver for Azure file storage by mfar-bcgdv · Pull Request #53 · rancher/storage
Docker の Volume plugin で NFS や Glusterfs を利用する - Qiita
Dockerチートシート~便利で使えるコマンドまとめました~ | GMOクラウドアカデミー
nfsマウント - Qiita
DockerのVolume機能について実験してみたことをまとめます - Qiita
Azure へのプライベート Docker レジストリのデプロイメント – Microsoft Azure Japan Team Blog (ブログ)
Rancher Meetup Tokyo#3 Storageについて
Azure Application Gateway で Let’s Encrypt も使えます。Let’s Try ! | 技術的な何か。
3分くらいで分かるdockerのdata volume - Qiita