前書き
Continuous integration(CI)は、開発者がintegrateをできるだけ頻繁にコーディングし、automated buildによって共有リポジトリにマージされる前後にすべてのコミットがテストされる方法を指します。
CIは開発プロセスをスピードアップし、本番環境で重大な問題が発生するリスクを最小限に抑えますが、セットアップは簡単ではありません。自動ビルドは、runtime dependenciesのインストールとexternal servicesの構成がローカル環境や開発環境とは異なる可能性がある、異なる環境で実行されます。
Dockerは、環境標準化の問題を単純化することを目的としたコンテナー化プラットフォームであり、アプリケーションのデプロイメントも標準化できます(find out more about Docker)。 開発者の場合、Dockerを使用すると、ローカルコンテナでアプリケーションコンポーネントを実行することにより、ローカルマシンで運用環境をシミュレートできます。 これらのコンテナは、アプリケーションや基盤となるOSに関係なく、Docker Composeを使用して簡単に自動化できます。
このチュートリアルでは、Docker Composeを使用してCIワークフローの自動化を示します。
Docker化された「Hello world」タイプのPythonアプリケーションとBashテストスクリプトを作成します。 Pythonアプリケーションを実行するには2つのコンテナーが必要です。1つはアプリ自体用で、もう1つはアプリの依存関係として必要なストレージ用のRedisコンテナーです。
次に、テストスクリプトが独自のコンテナーでDocker化され、テスト環境全体がdocker-compose.test.ymlファイルに移動されるため、すべてのテスト実行が新しい均一なアプリケーション環境で実行されていることを確認できます。
このアプローチは、アプリケーションをテストするたびに、依存関係を含めて、アプリケーションの同一の新しいテスト環境を構築する方法を示しています。
したがって、テスト対象のアプリケーションおよび基盤となるインフラストラクチャとは無関係に、CIワークフローを自動化します。
前提条件
開始する前に、次のものが必要です。
-
non-root user with sudo privilegesを持つUbuntu16.04サーバー。 Initial Server Setup with Ubuntu 16.04は、これを設定する方法を説明しています。
-
Docker、How To Install and Use Docker on Ubuntu 16.04のステップ1および2に従ってインストールされます。
-
Docker Compose、How to Install Docker Compose on Ubuntu 16.04からステップ1に従ってインストール
[[step-1 -—- create-the-quot-hello-world-quot-python-application]] ==ステップ1—「HelloWorld」Pythonアプリケーションを作成します
このステップでは、このセットアップでテストできるアプリケーションのタイプの例として、単純なPythonアプリケーションを作成します。
次を実行して、アプリケーションの新しいディレクトリを作成します。
cd ~
mkdir hello_world
cd hello_world
新しいファイルapp.py
をnanoで編集します。
nano app.py
次のコンテンツを追加します。
app.py
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host="redis")
@app.route("/")
def hello():
visits = redis.incr('counter')
html = "Hello World!
" \
"Visits: {visits}" \
"
"
return html.format(visits=visits)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
完了したら、ファイルを保存して終了します。
app.py
は、Redisデータサービスに接続するFlaskに基づくWebアプリケーションです。 行visits = redis.incr('counter')
は訪問数を増やし、この値をRedisに保持します。 最後に、訪問数を含むHello World
メッセージがHTMLで返されます。
このアプリケーションには、Flask
とRedis
の2つの依存関係があり、最初の2行で確認できます。 これらの依存関係は、アプリケーションを実行する前に定義する必要があります。
新しいファイルを開きます:
nano requirements.txt
内容を追加します。
requirements.txt
Flask
Redis
完了したら、ファイルを保存して終了します。 要件を定義したので、後でdocker-compose.yml
に配置します。これで、次のステップの準備が整いました。
[[step-2 -—- dockerize-the-quot-hello-world-quot-application]] ==ステップ2—「HelloWorld」アプリケーションをDocker化する
Dockerは、Dockerfile
というファイルを使用して、特定のアプリケーションのDockerイメージを構築するために必要な手順を示します。 新しいファイルを編集します。
nano Dockerfile
次の内容を追加します。
Dockerfile
FROM python:2.7
WORKDIR /app
ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
ADD app.py /app/app.py
EXPOSE 80
CMD ["python", "app.py"]
各行の意味を分析しましょう:
-
FROM python:2.7
:「HelloWorld」アプリケーションイメージが公式のpython:2.7
Dockerイメージから構築されていることを示します -
WORKDIR /app
:Dockerイメージ内の作業ディレクトリを/app
に設定します -
ADD requirements.txt /app/requirements.txt
:ファイルrequirements.txt
をDockerイメージに追加します -
RUN pip install -r requirements.txt
:アプリケーションのpip
依存関係をインストールします -
ADD app.py /app/app.py
:アプリケーションのソースコードをDockerイメージに追加します -
EXPOSE 80
:アプリケーションがポート80(標準のパブリックWebポート)で到達できることを示します -
CMD ["python", "app.py"]
:アプリケーションを起動するコマンド
ファイルを保存して終了します。 このDockerfile
ファイルには、「HelloWorld」アプリケーションの主要コンポーネントを構築するために必要なすべての情報が含まれています。
依存関係
次に、例のより洗練された部分に進みます。 このアプリケーションでは、外部サービスとしてRedisが必要です。 これは、従来のLinux環境では毎回同じ方法で設定するのが難しいタイプの依存関係ですが、Docker Composeを使用すると毎回繰り返し設定できます。
Docker Composeの使用を開始するためにdocker-compose.yml
ファイルを作成しましょう。
新しいファイルを編集します。
nano docker-compose.yml
次の内容を追加します。
docker-compose.yml
web:
build: .
dockerfile: Dockerfile
links:
- redis
ports:
- "80:80"
redis:
image: redis
このDocker Composeファイルは、2つのDockerコンテナーで「Hello World」アプリケーションをローカルに起動する方法を示しています。
web
とredis
の2つのコンテナを定義します。
-
web
は、build
コンテキストに現在のディレクトリを使用し、作成したばかりのDockerfile
ファイルからPythonアプリケーションを構築します。 これは、Pythonアプリケーション専用に作成したローカルDockerイメージです。redis
コンテナIPにアクセスするために、redis
コンテナへのリンクを定義します。 また、UbuntuサーバーのパブリックIPを使用して、インターネットからポート80をパブリックにアクセスできるようにします -
redis
は、redis
という名前の標準のパブリックDockerイメージから実行されます。
完了したら、ファイルを保存して終了します。
[[step-3 -—- deploy-the-quot-hello-world-quot-application]] ==ステップ3—「HelloWorld」アプリケーションをデプロイします
この手順では、アプリケーションを展開し、最終的にはインターネット経由でアクセスできるようにします。 アプリケーションを何度も同じ方法でデプロイできるため、デプロイメントワークフローの目的上、これを開発環境、ステージング環境、または運用環境のいずれかと見なすことができます。
docker-compose.yml
およびDockerfile
ファイルを使用すると、以下を実行してローカル環境の展開を自動化できます。
docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d
最初の行は、Dockerfile
ファイルからローカルアプリケーションイメージを構築します。 2行目は、docker-compose.yml
ファイルで指定されているように、web
およびredis
コンテナーをデーモンモード(-d
)で実行します。
次を実行して、アプリケーションコンテナが作成されたことを確認します。
docker ps
これにより、helloworld_web_1
とhelloworld_redis_1
という名前の2つの実行中のコンテナーが表示されます。
アプリケーションが起動していることを確認してみましょう。 以下を実行することにより、helloworld_web_1
コンテナのIPを取得できます。
WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
echo $WEB_APP_IP
Webアプリケーションが適切なメッセージを返していることを確認します。
curl http://${WEB_APP_IP}:80
次のような結果が返されます。
出力
Hello World!
Visits: 2
訪問数は、このエンドポイントに到達するたびに増加します。 UbuntuサーバーのパブリックIPアドレスにアクセスして、ブラウザーから「Hello World」アプリケーションにアクセスすることもできます。
独自のアプリケーション用にカスタマイズする方法
独自のアプリケーションを設定するための鍵は、独自のDockerコンテナーにアプリを配置し、独自のコンテナーから各依存関係を実行することです。 次に、例に示すように、Docker Composeを使用してコンテナ間の関係を定義できます。 Docker Composeについては、このDocker Compose articleで詳しく説明しています。
複数のコンテナーでアプリケーションを実行する方法の別の例については、WordPress and phpMyAdmin with Docker Composeの実行に関するこの記事をお読みください。
[[step-4 -—- create-the-test-script]] ==ステップ4—テストスクリプトを作成する
次に、Pythonアプリケーション用のテストスクリプトを作成します。 これは、アプリケーションのHTTP出力をチェックする単純なスクリプトです。 このスクリプトは、継続的インテグレーションの展開プロセスの一部として実行する可能性があるテストの種類の例です。
新しいファイルを編集します。
nano test.sh
次の内容を追加します。
test.sh
sleep 5
if curl web | grep -q 'Visits: '; then
echo "Tests passed!"
exit 0
else
echo "Tests failed!"
exit 1
fi
test.sh
は、「HelloWorld」アプリケーションの基本的なWeb接続をテストします。 cURLを使用して訪問数を取得し、テストに合格したかどうかのレポートを作成します。
[[step-5 --- create-the-testing-environment]] ==ステップ5—テスト環境を作成します
アプリケーションをテストするには、テスト環境を展開する必要があります。 また、Step 3で作成したライブアプリケーション環境と同じであることを確認する必要があります。
まず、新しいDockerfileファイルを作成して、テストスクリプトをDocker化する必要があります。 新しいファイルを編集します。
nano Dockerfile.test
次の内容を追加します。
Dockerfile.test
FROM ubuntu:xenial
RUN apt-get update && apt-get install -yq curl && apt-get clean
WORKDIR /app
ADD test.sh /app/test.sh
CMD ["bash", "test.sh"]
Dockerfile.test
は、公式のubuntu:xenial
イメージを拡張してcurl
依存関係をインストールし、イメージファイルシステムにtests.sh
を追加し、テストスクリプトを実行するCMD
コマンドを示します。バッシュ。
テストがDocker化されると、複製可能で不可知な方法で実行できます。
次のステップは、テストコンテナを「Hello World」アプリケーションにリンクすることです。 Docker Composeが再び助けになります。 新しいファイルを編集します。
nano docker-compose.test.yml
次の内容を追加します。
docker-compose.test.yml
sut:
build: .
dockerfile: Dockerfile.test
links:
- web
web:
build: .
dockerfile: Dockerfile
links:
- redis
redis:
image: redis
Docker Composeファイルの後半では、前のdocker-compose.yml
ファイルと同じ方法で、メインのweb
アプリケーションとそのredis
依存関係をデプロイします。 これは、web
およびredis
コンテナーを指定するファイルの一部です。 唯一の違いは、web
コンテナがポート80を公開しなくなったため、テスト中にアプリケーションがパブリックインターネット経由で利用できなくなることです。 したがって、アプリケーションとその依存関係は、実際の展開とまったく同じ方法で構築されていることがわかります。
docker-compose.test.yml
ファイルは、統合テストの実行を担当するsut
コンテナー(system under testsにちなんで名付けられた)も定義します。 sut
コンテナは、現在のディレクトリをbuild
ディレクトリとして指定し、Dockerfile.test
ファイルを指定します。 web
コンテナにリンクしているため、アプリケーションコンテナのIPアドレスにtest.sh
スクリプトからアクセスできます。
独自のアプリケーション用にカスタマイズする方法
docker-compose.test.yml
には、数十の外部サービスと複数のテストコンテナが含まれる場合があることに注意してください。 Dockerは、すべてのコンテナが基盤となるOSを共有するため、これらすべての依存関係を単一のホストで実行できます。
アプリケーションで実行するテストが他にもある場合は、上記のDockerfile.test
ファイルと同様に、追加のDockerfileを作成できます。
次に、追加のDockerfileを参照して、docker-compose.test.yml
ファイルのsut
コンテナーの下に追加のコンテナーを追加できます。
[[step-6 --- test-the-quot-hello-world-quot-application]] ==ステップ6—「HelloWorld」アプリケーションをテストします
最後に、Dockerのアイデアをローカル環境からテスト環境に拡張すると、次を実行することにより、Dockerを使用してアプリケーションをテストする自動化された方法が得られます。
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build
このコマンドは、docker-compose.test.yml
に必要なローカルイメージを構築します。 -f
を使用してdocker-compose.test.yml
を指し、-p
を使用して特定のプロジェクト名を示していることに注意してください。
次を実行して、新しいテスト環境を起動します。
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
OutputCreating ci_redis_1
Creating ci_web_1
Creating ci_sut_1
以下を実行して、sut
コンテナの出力を確認します。
docker logs -f ci_sut_1
出力
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 42 100 42 0 0 3902 0 --:--:-- --:--:-- --:--:-- 4200
Tests passed!
最後に、sut
コンテナの終了コードをチェックして、テストに合格したかどうかを確認します。
docker wait ci_sut_1
出力
0
このコマンドの実行後、テストに合格した場合、$?
の値は0
になります。 それ以外の場合、アプリケーションテストは失敗しました。
他のCIツールはコードリポジトリのクローンを作成し、これらのいくつかのコマンドを実行して、実行時の依存関係や外部サービスの構成を気にすることなく、アプリケーションの最新ビットでテストが合格するかどうかを確認できます。
それでおしまい! 実稼働環境と同一の新しく構築された環境でテストを正常に実行しました。
結論
DockerとDockerComposeのおかげで、アプリケーションの構築(Dockerfile
)、ローカル環境のデプロイ(docker-compose.yml
)、テストイメージの構築(Dockerfile.test
)、および実行を自動化することができました。 (統合)テスト(docker-compose.test.yml
)任意のアプリケーション。
特に、docker-compose.test.yml
ファイルをテストに使用する利点は、テストプロセスが次のとおりであることです。
-
Automatable:ツールが
docker-compose.test.yml
を実行する方法は、テスト対象のアプリケーションとは無関係です。 -
Light-weight:数百の外部サービスを単一のホストにデプロイして、複雑な(統合)テスト環境をシミュレートできます
-
Agnostic:CIプロバイダーのロックインを回避し、テストは任意のインフラストラクチャおよびDockerをサポートする任意のOSで実行できます
-
Immutable:ローカルマシンで合格したテストはCIツールで合格します
このチュートリアルでは、単純な「Hello World」アプリケーションをテストする方法の例を示します。
次に、独自のアプリケーションファイルを使用し、独自のアプリケーションテストスクリプトをDocker化して、独自のdocker-compose.test.yml
を作成し、新しい不変の環境でアプリケーションをテストします。