記事の目次

    みなさんこんにちは。五月下旬にCOMPUTEX TAIPEI 2019が開催され、新しいハードウェアが続々と発表されました。今年の話題No.1は何といってもAMDのRyzenシリーズの発表でしょう。ほんの数年前までは12コア/24スレッドのCPUが$500なんて価格で手に入るとはとても考えられなかったですし、基本性能も今の時代になってそんなに上がる!?という数字が出てきて驚きの連続です。AMDの最近の躍進はすさまじく、新製品発表のたびにワクワクさせてくれるこの感覚はとても久しぶりです。7/7発売ということなので今から楽しみです。

    TEXT_痴山紘史 / Hiroshi Chiyama(日本CGサービス
    EDIT_尾形美幸 / Miyuki Ogata(CGWORLD)

    クラウド環境構築の前準備

    前回の予告の通り、今回からはクラウド環境を活用するためのお話に進んでいきます。クラウドだからといって何か特別なことをするわけではなく、これまで行なってきたことの延長線上の内容として話を進めていきます。そのため、いきなりクラウドの世界に飛び出してしまうのではなく、今回から数回に渡って社内サーバ運用からクラウド運用に移行するためのトピックをご紹介していきます。


    ・Dockerのおさらい

    Dockerは本連載でずっと活用してきました。もちろんクラウド環境でもフルに活用していく予定ですが、そのためには、これまできちんと扱ってこなかったいくつかのトピックを確認する必要があるので、まずはそこから進めていきます。

    本連載中では最初にDockerのコンテナを単体で起動し、単独のアプリケーションを起動することから始め、続いて複数のコンテナを起動し、それぞれを連携させる方法を見てきました。

    これまで話のながれ上、あえてあまり詳しく触れず、「とりあえず、そういうふうにやるものだ」と割り切って進めた内容がいくつかあります。今後の話を進める上で必要になってきそうなので、ここでまとめてご紹介します。


    ・データの永続化

    Dockerは、コンテナを実行してイメージの内容を更新しても、コンテナを終了すると更新内容は消えてしまい、再度コンテナを実行すると、初期の状態から実行されるという特徴があります。最初は「エエッ!?」と思うかもしれませんが、一度つくったイメージを元に複数のコンテナを起動したときに、起動した順番によって異なる環境ができてしまうと大きなトラブルの要因になってしまいます。そのため、毎回同じ状態に戻るのは正しい仕様といえます。

    変更内容を保存したい場合、2つの対応方法があります。

    ひとつは、元のイメージから新しいイメージを作成し、それを使用する方法です。この場合はDockerfileを書いて専用のイメージをビルドします。もうひとつは、アプリケーションが使用するデータを保存する場合です。この場合は、コンテナが停止してもデータが残る場所を保存先として指定します。

    前者はわれわれもずっと活用していました。例えば第3回:USD環境の構築で使用したAnimalLogic/docker-usdでも、CY2018環境を構築するためにDockerfileを用意しています。このDockerfile内ではcentos/devtoolset-7-toolchain-centos7というイメージをベースに追加で環境構築を行なっています。

    後者はlogstashで行なっていて、-vオプションで指定していました。logstashを起動するためのコマンドは以下の通りでした。

    $ sudo docker run -d --name logstash --rm -it -v /root/pipeline/:/usr/share/logstash/pipeline/ docker.elastic.co/logstash/logstash:6.6.0
    

    この場合、ホストの/root/pipelineをコンテナの/usr/share/logstash/pipelineとしてアクセスできるようになります。データは/root/pipelineに置かれているので、コンテナが終了しても存在し続けます。

    後者のように、コンテナが終了しても残しておきたいデータが消えないようにすることをデータの永続化といいます。永続化をきちんとしておかないと、メトリクスを一生懸命取得して蓄積しても、ある日コンテナを再起動する必要が出たときに全てがなかったことになってしまいます。


    ・Dockerコンテナの管理

    これまで、Dockerコンテナを立ち上げる際には以下のような感じでコマンドを入力していました。

    $ sudo docker run -d --name graphite --restart=always -p 9080:80 -p 2003-2004:2003-2004 -p 2023-2024:2023-2024 -p 8125:8125/udp -p 8126:8126 graphiteapp/graphite-statsd
    

    コンテナの数が少なく、動作確認のために実行するだけならひとつひとつコンテナを立ち上げてもいいですが、Fifemonのように複数のコンテナが連携して処理を行うようになると、とてもじゃないですが管理しきれなくなってしまいます。また、時間が経ってから改めて同じ手順でコンテナを起動しなければいけなくなった場合、オプションを正確に覚えておくのは難しいです。

    このような複数のコンテナをまとめて管理するためのツールがあります。今回はDocker Composeを使用して、Fifemon環境を構築してみます。


    ・Docker Composeを使用したコンテナの管理

    Docker Composeを使用したコンテナの管理は、大きく2つの部分に分かれます。ひとつめが各コンテナ(イメージ)を管理する部分、ふたつめが複数のコンテナをまとめてひとつのシステムを構築するための部分です。

    イメージはdocker hubにあるものなど、既存のものを使用する方法と、Dockerfileを定義して自分で作成する方法があります。

    Fifemonでは、Elasticsearchとgrafana、graphiteでオリジナルイメージのまま使用することができます。Elasticsearchで日本語に対応する必要がある場合は、kuromojiプラグインのインストールなどが必要ですが、Fifemonでは使用しないのでオリジナルのイメージを使用します。logstashはちょっと複雑で、基本は公式イメージを使用するのですが、probeを使用するための環境を構築する必要があります。また、probeはlogstash以外にもsupervisordを使用してメトリクスの取得を行うので、そのための環境も必要です。

    まとめると

    -Elasticsearch、grafana、graphite → オリジナルイメージを使用しつつ、永続化が必要なデータは永続化する
    - logstash → オリジナルイメージをベースに独自のイメージを作成
    - probe → supervisordを使用するようにイチから環境構築

    ということになります。


    ・Docker Composeのインストール

    DockerとDocker Composeは別のプロダクトなので、インストールをする必要があります。

    $ sudo yum -y install docker-compose
    

    ・Elasticsearch、graphiteの設定

    Docker Composeを使用する際には、docker-compose.xmlファイルを作成します。ディレクトリ構成は以下のようになります。

    $ tree .
    .
    ├── docker-compose.yml
    └── Elasticsearch
        ├── config
        │   └── elasticsearch.yml
        ├── data
        └── logs
    
    4 directories, 2 files
    $
    

    docker-compose.ymlは、servicesで起動するコンテナの定義、networksでネットワークを定義しています。

    $ cat docker-compose.yml
    version: '3.3'
    
    services:
      elasticsearch:
        container_name: es01
        image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0
        environment:
          - discovery.type=single-node
          - xpack.security.enabled=false
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
          memlock:
            soft: -1
            hard: -1
          nofile:
            soft: 65536
            hard: 65536
        volumes:
          - ./Elasticsearch/data:/usr/share/elasticsearch/data
          - ./Elasticsearch/logs:/usr/share/elasticsearch/logs
          - ./Elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
        networks:
          - default
    
      graphite:
        container_name: graphite
        image: graphiteapp/graphite-statsd
        links:
          - elasticsearch
        networks:
          - default
    
    networks:
        default:
    
    $
    

    services中ではelasticsearchとgraphiteの2つのサービスが定義されています。最も単純なgraphiteの中身を見てみます。

      graphite:
        container_name: graphite
        image: graphiteapp/graphite-statsd
        links:
          - elasticsearch
        networks:
          - default
    

    container_nameでコンテナ名を指定し、imageでコンテナを実行する際に使用するイメージ名を指定します。実行環境にイメージがなければ自動的にダウンロードして環境を構築します。

    linksは、このコンテナを実行する際に必要なほかのサービスを指定することができます。graphiteを実行するためには、elasticsearchが実行されている必要があることがわかります。

    networksはこのコンテナが接続するネットワークを指定します。Dockerがデフォルトで用意しているネットワーク(bridge)ではDNSが有効になっていないため、別のコンテナにアクセスするためにはIPアドレスで指定する必要がありましたが、新たに定義したネットワークではDNSが有効になっているため、コンテナ名でアクセスすることができます。

    続いて、Elasticsearchの中身を見てみます。

      elasticsearch:
        container_name: es01
        image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0
        environment:
          - discovery.type=single-node
          - xpack.security.enabled=false
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
          memlock:
            soft: -1
            hard: -1
          nofile:
            soft: 65536
            hard: 65536
        volumes:
          - ./Elasticsearch/data:/usr/share/elasticsearch/data
          - ./Elasticsearch/logs:/usr/share/elasticsearch/logs
          - ./Elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
        networks:
          - default
    

    environmentは、コンテナにセットする環境変数になります。docker runコマンドで-eオプションで指定していた内容です。

    ulimitsは、Elasticsearchを動かすために必要な設定です。

    volumes が、永続化したいデータ置き場を用意するための項目です。例えば、

          - ./Elasticsearch/data:/usr/share/elasticsearch/data
    

    は、Elasticsearch/dataディレクトリをコンテナの/usr/share/elasticsearch/dataディレクトリにマウントしています。docker runコマンドの-vオプションで指定していた内容です。

    これで一度コンテナを起動してみます。docker-compose.ymlのあるディレクトリで、docker-compose upを実行します。

    $ sudo docker-compose up -d
    Creating es01 ... done
    Creating graphite ... done
    Creating graphite ...
    

    コンテナの状態は docker-compose psで確認できます。

    $ sudo docker-compose ps
      Name                Command               State                         Ports
    -----------------------------------------------------------------------------------------------------
    es01       /usr/local/bin/docker-entr ...   Up      9200/tcp, 9300/tcp
    graphite   /entrypoint                      Up      2003/tcp, 2004/tcp, 2013/tcp, 2014/tcp, 2023/tcp,
                                                        2024/tcp, 80/tcp, 8080/tcp, 8125/tcp, 8125/udp,
                                                        8126/tcp
    $
    

    ご覧の通り、コマンドひとつで必要な設定が行われたコンテナが全て立ち上がりました。設定を変更してコンテナを立ち上げ直したいときはdocker-compose restartが使用できます。

    $ sudo docker-compose restart
    Restarting graphite ... done
    Restarting es01     ... done
    $
    

    ひとつひとつのコンテナを管理するよりもとても楽ですね。

    次ページ:
    ・grafanaの設定

    [[SplitPage]]

    ・grafanaの設定

    grafanaはユーザーがブラウザ経由で蓄積されたメトリクスを確認するために使用するので、外部から接続できるようにホストのポートと接続する必要があります。docker-compose.ymlのservicesに以下の設定を追加します。

      grafana:
        container_name: grafana
        image: grafana/grafana
        volumes:
          - ./grafana/data:/var/lib/grafana
          - ./grafana/plugins:/var/lib/grafana/plugins
        links:
          - elasticsearch
        ports:
          - "3000:3000"
        environment:
          - GF_SECURITY_ADMIN_PASSWORD=secret
        networks:
          - default
    

    portsで3000番ポートをホストと接続しています。docker runで-pオプションで指定していた内容です。こうすれば、コンテナ外からもホストマシンの3000番ポートにアクセスすることでgrafanaにアクセスできます。これまではElasticsearchも9200番ポートを公開していましたが、コンテナ間で通信を行うだけなので、defaultネットワーク内で通信すればいいため、削除しています。動作確認のために外部からアクセスする必要があれば、同様にportsで指定します。


    ・logstashの設定

    続いてlogstashの設定をします。docker-compose.ymlのservicesに以下の設定を追加します。

      logstash:
        container_name: logstash
        build:
          context: ./logstash
          dockerfile: Dockerfile
        links:
          - elasticsearch
        volumes:
          - /etc/condor:/etc/condor
          - ./logstash/pipeline:/usr/share/logstash/pipeline
          - ./logstash/log:/opt/probes/log
        networks:
          - default
    

    これまでとちがうのは、使用するイメージの指定をするためにimagesを指定する代わりに、buildになっているところです。これにより、logstash/Dockerfileを用いて新たなイメージを作成するようにしています。また、logstashの設定ファイルはイメージにもたせるのではなく、volumesを使用してマウントしています。

    logstash/Dockerfileの内容は以下の通りです。

    $ cat logstash/Dockerfile
    FROM docker.elastic.co/logstash/logstash:6.6.0
    
    USER root
    
    RUN yum -y install curl git
    RUN curl -O https://bootstrap.pypa.io/get-pip.py
    RUN python get-pip.py
    RUN rm get-pip.py
    
    RUN pip install virtualenv
    RUN pip install htcondor==8.7.10
    
    RUN cd /opt && git clone https://github.com/fifemon/probes
    RUN cd /opt/probes && virtualenv --system-site-packages venv
    RUN /opt/probes/venv/bin/pip install supervisor influxdb
    $
    

    FROMで、ベースにするイメージを指定します。今回はdocker.elastic.co/logstash/logstash:6.6.0です。RUNは、イメージを作成するときに実行するコマンドです。RUNを読み飛ばせば、通常のコマンドと同じであることがわかります。

    オリジナルイメージにはcurl、git、pipといったツールが含まれていなかったのでインストールしています。

    RUN yum -y install curl git
    RUN curl -O https://bootstrap.pypa.io/get-pip.py
    RUN python get-pip.py
    RUN rm get-pip.py
    

    必要なツールがインストールできたら、さらにvirtualenv、htcondorをインストールします。

    RUN pip install virtualenv
    RUN pip install htcondor==8.7.10
    

    続いてprobeを使用するためのvirtualenvの作成を行います。

    RUN cd /opt && git clone https://github.com/fifemon/probes
    RUN cd /opt/probes && virtualenv --system-site-packages venv
    RUN /opt/probes/venv/bin/pip install supervisor influxdb
    

    以上で、logstashでhtcondorモジュールを使用して、htcondorの情報を取得することのできるイメージが作成できます。


    ・supervisordを使用したコンテナ

    続いて、probesのもうひとつの機能、supervisordを使用してメトリクスを取得するためのコンテナを作成します。docker-compose.ymlのservicesに以下の設定を追加します。

      probes:
        container_name: probes
        build:
          context: ./probes
          dockerfile: Dockerfile
        container_name: probes
        volumes:
          - ./probes/log:/opt/probes/log
        networks:
          - default
    

    ここはもう何も説明の必要はないです。続いてDockerfileを見てみます。

    $ cat probes/Dockerfile
    FROM python:2.7
    
    USER root
    
    RUN pip install virtualenv
    RUN pip install htcondor==8.7.10
    
    RUN cd /opt && git clone https://github.com/fifemon/probes
    RUN cd /opt/probes && virtualenv --system-site-packages venv
    RUN /opt/probes/venv/bin/pip install supervisor influxdb
    
    COPY etc/awsmonitor.cfg /etc/
    COPY etc/condor-probe.cfg /etc/
    COPY etc/supervisord.conf /etc/
    
    CMD . /opt/probes/venv/bin/activate && exec supervisord
    $
    

    probesがPython3で動作しなかったので、python2.7環境をベースにイメージを作成します。このDockerfileの中で、いくつか新しいコマンドが使用されています。

    COPYは、イメージ作成時にホストからファイルをコピーします。今回はファイルコピーをしましたが、volumesでマウントしてもいいかなとも思います。

    CMDは、コンテナ実行時に実行されるコマンドです。ここでは、virtualenv環境の上でsupervisordを実行するようにしています。ただ、supervisordはデフォルトではデーモンとして実行されてしまうため、そのままではコンテナ実行時に起動されても即座に処理が返ってきて、そのままコンテナが終了してしまいます。それを避けるためにsupervisord.confでデーモンとして動作しないように設定をします。

    $ cat probes/etc/supervisord.conf
    ## Supervisor
    
    [supervisord]
    logfile = /opt/probes/log/supervisord.log
    childlogdir = /opt/probes/log
    nodaemon = true # <== ここでsupervisord実行時にデーモン化しないようにしている
    
    (以下略)
    $
    

    また、設定ファイル中で指定するパスもコンテナ化した環境に合わせて適切なものに変更しています。

    最終的なディレクトリ構成は以下のようになります。

    $ tree .
    .
    ├── docker-compose.yml
    ├── Elasticsearch
    │   ├── config
    │   │   └── elasticsearch.yml
    │   ├── data
    │   └── logs
    ├── grafana
    │   ├── conf
    │   ├── data
    │   └── plugins
    ├── logstash
    │   ├── Dockerfile
    │   ├── log
    │   └── pipeline
    │       └── logstash-fifemon.conf
    └── probes
        ├── Dockerfile
        ├── etc
        │   ├── awsmonitor.cfg
        │   ├── condor-probe.cfg
        │   └── supervisord.conf
        └── log
    
    14 directories, 8 files
    $
    

    また、docker-compose.ymlは以下のようになりました。

    $ cat docker-compose.yml
    version: '3.3'
    
    services:
      elasticsearch:
        container_name: es01
        image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0
        environment:
          - discovery.type=single-node
          - xpack.security.enabled=false
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
          memlock:
            soft: -1
            hard: -1
          nofile:
            soft: 65536
            hard: 65536
        volumes:
          - ./Elasticsearch/data:/usr/share/elasticsearch/data
          - ./Elasticsearch/logs:/usr/share/elasticsearch/logs
          - ./Elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
        networks:
          - default
    
      logstash:
        container_name: logstash
        build:
          context: ./logstash
          dockerfile: Dockerfile
        links:
          - elasticsearch
        volumes:
          - /etc/condor:/etc/condor
          - ./logstash/pipeline:/usr/share/logstash/pipeline
          - ./logstash/log:/opt/probes/log
        networks:
          - default
    
      graphite:
        container_name: graphite
        image: graphiteapp/graphite-statsd
        links:
          - elasticsearch
        networks:
          - default
    
      grafana:
        container_name: grafana
        image: grafana/grafana
        volumes:
          - ./grafana/data:/var/lib/grafana
          - ./grafana/plugins:/var/lib/grafana/plugins
        links:
          - elasticsearch
        ports:
          - "3000:3000"
        environment:
          - GF_SECURITY_ADMIN_PASSWORD=secret
        networks:
          - default
    
      probes:
        container_name: probes
        build:
          context: ./probes
          dockerfile: Dockerfile
        container_name: probes
        volumes:
          - ./probes/log:/opt/probes/log
        networks:
          - default
    
    networks:
        default:
    
    $
    

    ・fifemon起動

    準備ができたので fifemonを起動します。Docker Composeで管理しているので、コマンドひとつで環境が立ち上がります。

    $ sudo docker-compose up -d
    Creating grafana ... done
    Creating probes ...
    Creating es01 ...
    Creating graphite ...
    Creating grafana ...
    

    ここからさらにGrafana上でData Sourceの設定やダッシュボードの登録という作業は残っていますが、それは前回までで紹介済みなので省略します。

    紙面の関係で細かい部分はかなり省略してしまったため、実際に手を動かしてみると「アレアレッ??」と思うところが出てきてしまうかもしれません。今回はクラウド環境を活用するために必要な知識の導入ということで、fifemon構築のための細かいお話というより、DockerfileやDocker Composeを用いたシステム構築ってこんな感じでやるんだなということを知っていただければと思います。

    次回予告

    次回は、クラウド環境構築のための前準備の続きとして、社内環境をクラウド環境とシームレスに統合するための準備についてご説明していきます。



    第14回の公開は、2019年7月を予定しております。

    プロフィール

    • 痴山紘史
      日本CGサービス(JCGS) 代表

      大学卒業後、株式会社IMAGICA入社。放送局向けリアルタイムCGシステムの構築・運用に携わる。その後、株式会社リンクス・デジワークスにて映画・ゲームなどの映像制作に携わる。2010年独立、現職。映像制作プロダクション向けのパイプラインの開発と提供を行なっている。新人パパ。娘かわいい。
      @chiyama