dockerに入門してみる(その9)(終)

前回の記事はこちら↓

ren-opdev.hatenablog.com

今回は"dockerに入門してみる"シリーズの最終回です。最後は、コンテナイメージをビルドする上でのベストプラクティスを見ていきます。

イメージをスキャンする

事前に見つけられる脆弱性があるならば、見つけて潰してしまいたいですよね。

DockerはSnykとパートナーになっているため、docker scanによってコンテナイメージの脆弱性をスキャンすることができます。
試しにtutorialを動かしているgetting-startedをスキャンしてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker scan getting-started
Docker Scan relies upon access to Snyk, a third party provider, do you consent to proceed using Snyk? (y/N)
y

Testing getting-started...

✗ Medium severity vulnerability found in openssl/libcrypto1.1
  Description: NULL Pointer Dereference
  Info: https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1051931
  Introduced through: openssl/libcrypto1.1@1.1.1g-r0, openssl/libssl1.1@1.1.1g-r0, apk-tools/apk-tools@2.10.5-r0, libtls-standalone/libtls-standalone@2.9.1-r0
  From: openssl/libcrypto1.1@1.1.1g-r0
  From: openssl/libssl1.1@1.1.1g-r0 > openssl/libcrypto1.1@1.1.1g-r0
  From: apk-tools/apk-tools@2.10.5-r0 > openssl/libcrypto1.1@1.1.1g-r0
  and 4 more...
  Fixed in: 1.1.1i-r0

✗ Medium severity vulnerability found in musl/musl
  Description: Out-of-bounds Write
  Info: https://snyk.io/vuln/SNYK-ALPINE311-MUSL-1042763
  Introduced through: musl/musl@1.1.24-r2, busybox/busybox@1.31.1-r9, alpine-baselayout/alpine-baselayout@3.2.0-r3, openssl/libcrypto1.1@1.1.1g-r0, openssl/libssl1.1@1.1.1g-r0, zlib/zlib@1.2.11-r3, apk-tools/apk-tools@2.10.5-r0, libtls-standalone/libtls-standalone@2.9.1-r0, busybox/ssl_client@1.31.1-r9, gcc/libgcc@9.3.0-r0, musl/musl-utils@1.1.24-r2, pax-utils/scanelf@1.2.4-r0, libc-dev/libc-utils@0.7.2-r0
  From: musl/musl@1.1.24-r2
  From: busybox/busybox@1.31.1-r9 > musl/musl@1.1.24-r2
  From: alpine-baselayout/alpine-baselayout@3.2.0-r3 > musl/musl@1.1.24-r2
  and 12 more...
  Fixed in: 1.1.24-r3



Organization:      undefined
Package manager:   apk
Project name:      docker-image|getting-started
Docker image:      getting-started
Platform:          linux/amd64

Tested 16 dependencies for known vulnerabilities, found 2 vulnerabilities.

For more free scans that keep your images secure, sign up to Snyk at https://dockr.ly/3ePqVcp

スキャン結果として、見つかった脆弱性の種類やどのバージョンのライブラリで修正されているかを吐き出してくれます。docker scanのオプションについては、下記の公式に詳しく載っています。 docs.docker.com コマンドラインからイメージをスキャンするほか、Docker Hubごとスキャンすることも出来るようです。 docs.docker.com

イメージ構成を見る

docker image historyコマンドを用いると、そのイメージがどのレイヤーで構成されているかを見ることができます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker image history getting-started
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
7d2a760d3274   13 days ago    CMD ["node" "src/index.js"]                     0B        buildkit.dockerfile.v0
<missing>      13 days ago    RUN /bin/sh -c yarn install --production # b…   85.2MB    buildkit.dockerfile.v0
<missing>      13 days ago    COPY . . # buildkit                             4.62MB    buildkit.dockerfile.v0
<missing>      2 weeks ago    WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      5 weeks ago    /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>      5 weeks ago    /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>      5 weeks ago    /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>      5 weeks ago    /bin/sh -c apk add --no-cache --virtual .bui…   7.62MB
<missing>      5 weeks ago    /bin/sh -c #(nop)  ENV YARN_VERSION=1.22.5      0B
<missing>      5 weeks ago    /bin/sh -c addgroup -g 1000 node     && addu…   76.5MB
<missing>      5 weeks ago    /bin/sh -c #(nop)  ENV NODE_VERSION=12.20.0     0B
<missing>      8 months ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      8 months ago   /bin/sh -c #(nop) ADD file:b91adb67b670d3a6f…   5.61MB

出力された各行がレイヤーを表しています。手軽にレイヤーのサイズを見ることができるので、どのレイヤーが肥大化しているかをパッと判別することができます。

レイヤーキャッシュを用いてビルド回数を減らす

Dockerfileの書き方を工夫することで、ビルド回数を減らすことができます。まずは、以前のチュートリアルで触ったDockerfileを引用します。

FROM node:12-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]

docker image historyで見た通り、コマンドそれぞれは一つずつレイヤーになっています。また、イメージに変更を加えるとyarnの依存関係を再インストールする必要があります。
ビルドするたびに同じ依存関係をインストールし直していくのは非効率なので、上手いこと依存関係をキャッシュして解決していきます。

Nodeベースのアプリケーションでは、アプリケーションの依存関係は全てpackage.jsonに記述されます。そのため、まずpackage.jsonをコピーして依存関係をインストールしてしまい、その後に他のソースコードをコピーすれば余計な再インストールを避けられます。

具体的には、以下のようにDockerfileを書きかえます。

FROM node:12-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]

次に、Dockerfileと同じ階層に.dockerignoreファイルを作成します。

node_modules

.dockerignoreはDocker(正確にはビルド時に動作するdaemon)に無視してほしいファイルを指定するためのファイルで、今回node_modulesはRUNコマンドを実行した際に上書きされるため無視する対象とします。

では、一度イメージをビルドしてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker build -t getting-started .
[+] Building 13.9s (10/10) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 175B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 52B                                                                                   0.0s
 => [internal] load metadata for docker.io/library/node:12-alpine                                                  0.0s
 => [1/5] FROM docker.io/library/node:12-alpine                                                                    0.2s
 => => resolve docker.io/library/node:12-alpine                                                                    0.0s
 => [internal] load build context                                                                                  0.1s
 => => transferring context: 8.65kB                                                                                0.0s
 => [2/5] WORKDIR /app                                                                                             0.0s
 => [3/5] COPY package.json yarn.lock ./                                                                           0.1s
 => [4/5] RUN yarn install --production                                                                           12.1s
 => [5/5] COPY . .                                                                                                 0.1s
 => exporting to image                                                                                             1.3s
 => => exporting layers                                                                                            1.3s
 => => writing image sha256:d71dd886d28378420a1fb3f6782c4d9816f52e4761d08ecca02ca3c893818ebc                       0.0s
 => => naming to docker.io/library/getting-started

Dockerfileの記述通りに処理が進んでいますね。
では次に、src/static/index.htmlを適当にいじってみて再度ビルドしてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker build -t getting-started .
[+] Building 0.3s (10/10) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 32B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 34B                                                                                   0.0s
 => [internal] load metadata for docker.io/library/node:12-alpine                                                  0.0s
 => [1/5] FROM docker.io/library/node:12-alpine                                                                    0.0s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 3.43kB                                                                                0.0s
 => CACHED [2/5] WORKDIR /app                                                                                      0.0s
 => CACHED [3/5] COPY package.json yarn.lock ./                                                                    0.0s
 => CACHED [4/5] RUN yarn install --production                                                                     0.0s
 => [5/5] COPY . .                                                                                                 0.1s
 => exporting to image                                                                                             0.1s
 => => exporting layers                                                                                            0.1s
 => => writing image sha256:2807d4c8a4f026a88ac4005c791d060de6a8a23ae1275046047b1b08499688c3                       0.0s
 => => naming to docker.io/library/getting-started

途中CACHEDとある通り、イメージのキャッシュが上手く働いて余計なインストールを省けたことが分かります。これで、より効率的なビルド作業が実現できますね!

マルチステージビルド

ビルドするだけのステージと、成果物を載せるだけのステージ…と複数のステージを使い分けていくことで、最終的なイメージのサイズを小さくすることができます。

Maven/Tomcatの例

Javaベースのアプリケーションをビルドするには、コンパイル時にJDKが必要となります。しかし、JDK自体はプロダクション環境には必要ないものです。MavenやGradleを使用する際も同様ですね。こういう場合に、マルチステージビルドが有用となります。

FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

この例では、buildステージでMavenを使用したJavaアプリケーションのビルドを行い、tomcatステージでbuildステージからビルド成果物をコピーしています。

Reactの例

Reactアプリケーションをビルドするには、Node環境が必要となります。しかし、サーバーサイドで画面の描画を行わない限りはプロダクションビルドにNode環境は必要ありません。こういう場合にも、マルチステージビルドが有用です。

FROM node:12 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

node:12イメージを用いてビルドし、成果物をnginxコンテナにコピーするだけ…。Nodeまわりのことはよく分かりませんが、確かに効率的っぽいですね。


コンテナイメージのビルドをより速く、より効率的に行う方法を(少し)知ったところで、このチャプターは終わりです。そして、これにてDocker 101 Tutorialも終了です!

Dockerの世界はまだまだ広く、一口にDockerといってもコンテナオーケストレーションCNCFなど様々な領域があります。 landscape.cncf.io 会社ではKubernetesを利用しているため、次は話題のイラストでわかるDockerとKubernetesを読んで更にDocker自体の理解やKubernetesへの理解を深めたいです。

とりあえずは、このチュートリアルを年内に終えられてよかったです。
よいお年をお迎えください!

dockerに入門してみる(その8)

晦日。年内に101 Tutorialを終えられるのでしょうか。

前回の記事はこちら↓ ren-opdev.hatenablog.com

今回は、Docker Composeを利用してApplication Stackを操作していきます。
まずDocker Composeとは

マルチコンテナのアプリケーションを共有したり、定義したりするために使えるツールのこと。
YAMLファイルでサービスを定義することができ、シングルコマンドで立ち上げ・取り壊しが可能である。

Composeを使うメリットは、

  • Application Stackを一つのファイルの中で定義できること
  • そのファイルをリポジトリのルートに置いておけること
  • そうすることで、他の人が開発に参加しやすくなること

です。リポジトリをクローンしてComposeするだけでApplication Stackを整えられる、という点が便利なのですね。

Composeファイルを作成する。

Docker Desktopを利用しているので、既にDocker Composeはインストール済みです。docker-compose versionを実行すると、Composeのバージョンを見ることができます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker-compose version
docker-compose version 1.27.4, build 40524192
docker-py version: 4.3.1
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.1c  28 May 2019

では実際にComposeファイルを作っていきます。まずはリポジトリのルートにdocker-compose.ymlを作成します。
作成したファイルの先頭で、version:スキーマバージョンを定義します。Compose file referenceより、現在の最新バージョンは3.8のようです。

version: "3.8"

次に、services:で実行対象のコンテナリストを定義します。

アプリケーションサービスを定義する

最初にアプリケーション部を定義していきます。前回コンテナ立ち上げに使ったコマンドは以下の通りです。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -dp 3000:3000 `
>>   -w /app -v "$(pwd):/app" `
>>   --network todo-app `
>>   -e MYSQL_HOST=mysql_v2 `
>>   -e MYSQL_USER=root `
>>   -e MYSQL_PASSWORD=secret `
>>   -e MYSQL_DB=todos_ja `
>>   node:12-alpine `
>>   sh -c "yarn install && yarn run dev"

これをyamlファイルに書き下していくと、以下の通りになります。

version: "3.8"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql_v2
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos_ja

比較してみると、どこがどう対応しているかが分かりますが、portとvolumeのマッピング記法が少し特殊ですね。また、image: node:12-alpinenodeは、自動的にネットワークエイリアスとして登録されるようです。

MySQLサービスを定義する

同様に、MySQL部を定義していきます。前回コンテナ立ち上げに使ったコマンドは以下の通りです。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -d `
>> --network todo-app --network-alias mysql_v2 `
>> -e MYSQL_ROOT_PASSWORD=secret `
>> -e MYSQL_DATABASE=todos_ja `
>> mysql:5.7 `
>> --character-set-server=utf8mb4 `
>> --collation-server=utf8mb4_unicode_ci

これをyamlファイルに書き下していくと、以下の通りになります。

version: "3.8"
services:
  app:
    # appサービスの定義
  mysql:
    image: mysql_v2:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD=secret
      MYSQL_DATABASE=todos_ja
      
  volumes:
    todo-mysql-data:

ここでの注意点は、docker runのときは自動的に作成された名前付きボリュームを明示的に定義するという点です。それに伴い、MySQLの定義内のvolume:に対応するvolume定義を、MySQL定義の下に加えています。

Application Stackを実行する

では、Composeを利用してApplication Stackを実行しましょう。docker-compose upコマンドで起動できます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker-compose up -d
ERROR: The Compose file '.\docker-compose.yml' is invalid because:
services.mysql.environment contains an invalid type, it should be an object, or an array
Unsupported config option for services.volumes: 'todo-mysql-data'

失敗しました。そしてチュートリアルにも書いてない…。大人しくエラーログを読んでみると

  • mysql配下のenvironmentがおかしい
  • volumesのconfigオプションをサポートしていない

と言われています。environmentについては、appサービスは通ってるのにmysqlは通っていない。。
と思い見比べてみたら、記法を間違えていました。正しくは以下の通りです。

    environment:
-      MYSQL_ROOT_PASSWORD=secret
-      MYSQL_DATABASE=todos_ja

    environment:
+      MYSQL_ROOT_PASSWORD: secret
+      MYSQL_DATABASE: todos_ja

2つ目のエラーはよく分からなかったのでDocker Forumsで検索しました。

forums.docker.com

インデントがおかしい…?
改めてチュートリアルの例文と比較してみると、確かにインデントを余分に加えてしまっていました。

-  volumes:
-    todo-mysql-data:

+volumes:
+  todo-mysql-data:

では、気を取り直してコマンドを実行します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker-compose up -d
Creating network "app_default" with the default driver
Creating volume "app_todo-mysql-data" with default driver
Pulling mysql (mysql_v2:5.7)...
ERROR: The image for the service you're trying to recreate has been removed. If you continue, volume data could be lost. Consider backing up your data before continuing.

Continue with the new image? [yN]y
Pulling mysql (mysql_v2:5.7)...
ERROR: pull access denied for mysql_v2, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

失敗しました。「mysql_v2へのpullアクセスが拒否された」らしいです。
MySQLのイメージを使おうとしていたのにimage: mysql_v2:5.7でイメージの名前を異なるものにしていたのが原因のようです。正しくはimage: mysql:5.7ですね。イメージ名が自動的にネットワークエイリアスになるだけで、ここに任意のネットワークエイリアスを入れていい訳ではなかったですね…。

改めてコマンドを実行します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker-compose up -d
Creating app_mysql_1 ... done       
Creating app_app_1   ... done                 

成功しました!
docker-compose logs -fでログを確かめると、app_1サービスが起動し、mysql_1サービスも紆余曲折を経て起動したことが分かりました。

ダッシュボードを確認してみると、リポジトリ名=appでApplication Stackがまとめられていることが分かります。 f:id:renkataoka:20201231005629p:plain コンテナごとにサービス名などが記載されているため、どのコンテナが何に対応しているかが一目で分かりますね。

Application Stackを停止する

Composeによって起動したApplication Stackを停止するには、docker-compose downコマンドを実行すればよいです。このとき、デフォルトではボリュームをは削除されないため、削除したい場合は--volumesフラグを付けると良いそうです。


Docker ComposeによるApplication Stackの操作方法が分かったところで、このチャプターは終わりです。

次のチャプターでは、コンテナイメージをビルドする際のベストプラクティスを勉強します。

dockerに入門してみる(その7)

前回の記事はこちら↓ ren-opdev.hatenablog.com

チュートリアルを超える内容が少しあったので、目次を付けてみますm(__)m

今回は、「MySQLをApplication Stackに加える」ためにアプリケーションのマルチコンテナ化に挑戦します。
ちなみにApplication Stackとは、「ある目的を達成するために用いるソフトウェア群」を指すようです。www.techopedia.com

マルチコンテナ化をするには幾つか理由があります。

  • フロントエンドとデータベースの実装を切り離せる。
  • コンテナという単位でバージョン管理が出来る。
  • development環境ではローカルを使い、production環境ではマネージドサービスを使う、といった変更が容易である。
  • そもそも一つのコンテナで複数プロセスを回すことが煩雑さの原因となる。

上記の理由から、今回はToDoアプリとデータベースをそれぞれコンテナにパッケージングして実装していきます。

ちなみに、コンテナはデフォルトでは他のコンテナと独立していてやり取りができないため、コンテナ同士を同じネットワークに配置することで、コンテナ同士の疎通を実現します。
コンテナをネットワーク上に配置するには、1)起動時に配置する方法 と、2)既にあるコンテナを接続する方法 があります。今回は、1)の手法を試していきます。

MySQLコンテナを用意する

まず、ネットワークを作ります。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker network create todo-app
02cbb30fa67ef761135ee9ebe21b21ac649f6ba4b2652c72b6f3fff99fe5ea4a

特にログはなく、ネットワークに固有の値っぽいものが吐き出されました。
次に、下記コマンドでMySQLコンテナを起動し、ネットワークに配置します。ここで、データベース初期化に必要な環境変数も定義します。

docker run -d `
    --network todo-app --network-alias mysql `
    -v todo-mysql-data:/var/lib/mysql `
    -e MYSQL_ROOT_PASSWORD=secret `
    -e MYSQL_DATABASE=todos `
    mysql:5.7

-v todo-mysql-data:/var/lib/mysqlで、まだ定義していないボリュームをマウントしようとしていますが、このように書くだけでDockerが自動的にボリュームを作り、マウントしてくれるようです。スゴイですね。

データベースが構成され立ち上がったのかどうかを確かめるため、コンテナ内でmysqlコマンドを叩きます。パスワードを求められるので、上記で指定したsecretを入力します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker exec -it 18d797997125 mysql -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.32 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

続いて、データベースを確認してみます。

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| todos              |
+--------------------+
5 rows in set (0.00 sec)

データベースが構成されていることが確認できましたね。

MySQLコンテナと接続する

ネットワーク内のコンテナにアクセスするには、そのコンテナのIPアドレスを知る必要があります。
そのために、nicolaka/netshootコンテナを用います。これは、ネットワーク関連の便利ツールがまとまっているコンテナです。READMEにめちゃくちゃ使い方が書いてあってスゴイですね。

下記の通り、実行してみました。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -it --network todo-app nicolaka/netshoot
Unable to find image 'nicolaka/netshoot:latest' locally
latest: Pulling from nicolaka/netshoot
cbdbe7a5bc2a: Already exists                                                                                            fa7edde5704a: Pull complete                                                                                             d142e371ed28: Pull complete                                                                                             db6c3597a95e: Pull complete                                                                                             468b5d8bd548: Pull complete                                                                                             1540e3cf45e1: Pull complete                                                                                             676bb9b891dd: Pull complete                                                                                             bc4557056759: Pull complete                                                                                             Digest: sha256:52dcf922fc8d419c23f677f07cb0f2467f2157b92448f35bda15f471026ad476
Status: Downloaded newer image for nicolaka/netshoot:latest
                    dP            dP                           dP
                    88            88                           88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88'  `88 88ooood8   88   Y8ooooo. 88'  `88 88'  `88 88'  `88   88
88    88 88.  ...   88         88 88    88 88.  .88 88.  .88   88
dP    dP `88888P'   dP   `88888P' dP    dP `88888P' `88888P'   dP

Welcome to Netshoot! (github.com/nicolaka/netshoot)
root @ /
 [1] 🐳  →     

クジラマークのプロンプト! 🐳 こんなこともできるんですね~。
ここで、ネットワーク作成時に--network-alias mysqlと設定していたmysqlエイリアスを用いて、IPアドレスを調べてみます。

root @ /
 [1] 🐳  → dig mysql

; <<>> DiG 9.14.12 <<>> mysql
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18996
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;mysql.                         IN      A

;; ANSWER SECTION:
mysql.                  600     IN      A       172.18.0.2

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Tue Dec 29 07:26:37 UTC 2020
;; MSG SIZE  rcvd: 44

ANSWER SECTIONにある172.18.0.2が、目当てのIPアドレスのようです。
IPアドレスを知ることができましたが、今回は上述の通りmysqlというエイリアスを被せているため、このIPアドレス自体を打ち込むのではなくmysqlというホストネームを使用すればデータベースに接続することが可能です。

MySQLと共にアプリケーションを実行する

では、いよいよToDoアプリコンテナとMySQLコンテナを紐づけていきます。
まず、以下で使用していく環境変数について説明します。

環境変数 意味
MYSQL_HOST 起動中のMySQLサーバーのホストネーム
MYSQL_USER MySQLサーバーへの接続に使うユーザーネーム
MYSQL_PASSWORD MySQLサーバーへの接続に使うパスワード
MYSQL_DB 接続後に使用するデータベース

下記コマンドより、コンテナを実行します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -dp 3000:3000 `
>>   -w /app -v "$(pwd):/app" `
>>   --network todo-app `
>>   -e MYSQL_HOST=mysql `
>>   -e MYSQL_USER=root `
>>   -e MYSQL_PASSWORD=secret `
>>   -e MYSQL_DB=todos `
>>   node:12-alpine `
>>   sh -c "yarn install && yarn run dev"
10c150eded9cd36d707ef2e65d47dcc1909ed805f8a0f24b727311706dc31717

コンテナログを見てみるとConnected to mysql db at host mysqlとあり、実際に接続されたことが分かります。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker logs 10c150eded9c
yarn install v1.22.5
[1/4] Resolving packages...
success Already up-to-date.
Done in 0.46s.
yarn run v1.22.5
$ nodemon src/index.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node src/index.js`
Waiting for mysql:3306.
Connected!
Connected to mysql db at host mysql
Listening on port 3000

http://localhost:3000/ にアクセスし、データを入れてみます。

f:id:renkataoka:20201229170346p:plain

そして、先ほどと同様にMySQLコンテナに入り、正しくレコードが登録されたかを確認します。

mysql> select * from todo_items;
+--------------------------------------+-----------+-----------+
| id                                   | name      | completed |
+--------------------------------------+-----------+-----------+
| a7ef25b8-11e3-456d-9770-f329c90a7b4b | sample!   |         0 |
| 786c96d1-b708-4905-b03f-72527a5fce5e | CHAPTER 7 |         0 |
+--------------------------------------+-----------+-----------+
2 rows in set (0.01 sec)

アプリケーションコンテナからMySQLコンテナに接続し、データを保存できたことを確認できました。

日本語を入力できない問題

チュートリアルの通り進めていくと、MySQLが日本語をIncorrect string valueだ!と言って受け付けてくれません。実際、MySQLの状態を見てみると

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.7.32, for Linux (x86_64) using  EditLine wrapper

Connection id:          8
Current database:       todos
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.7.32 MySQL Community Server (GPL)
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    latin1
Conn.  characterset:    latin1
UNIX socket:            /var/run/mysqld/mysqld.sock
Uptime:                 1 hour 16 min 4 sec

Threads: 2  Questions: 68  Slow queries: 0  Opens: 109  Flush tables: 1  Open tables: 102  Queries per second avg: 0.014
--------------

Server characterset: latin1のように、文字コードlatin1になっています。
文字コードを変えれば良さそうなのですが、cnfファイルをいじらなきゃいけない…??

qiita.com

と思ったら、Docker公式のMySQLリポジトリ内にConfiguration without a cnf fileというパラグラフが見つかりました。

公式が「cnfファイルを使わなくてもこのオプション使えばUTF-8にできるよ」というオプションを用意してくれているようです。感謝。。
というわけで、新しいコンテナを起動していきます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -d `
>> --network todo-app --network-alias mysql_v2 `
>> -e MYSQL_ROOT_PASSWORD=secret `
>> -e MYSQL_DATABASE=todos_ja `
>> mysql:5.7 `
>> --character-set-server=utf8mb4 `
>> --collation-server=utf8mb4_unicode_ci
ff39d7d156c04fdf37d020891b483df44537ba8ae00d8810bd42b276d193a4f2

無事起動できているようなので、細かい確認手順は省いて、アプリケーションコンテナと再度結び付けてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -dp 3000:3000 `
>>   -w /app -v "$(pwd):/app" `
>>   --network todo-app `
>>   -e MYSQL_HOST=mysql_v2 `
>>   -e MYSQL_USER=root `
>>   -e MYSQL_PASSWORD=secret `
>>   -e MYSQL_DB=todos_ja `
>>   node:12-alpine `
>>   sh -c "yarn install && yarn run dev"
9e1a01749ef688a05fdf43e30ad7efcb32403c987e76ee94fbd8cce8cb9de88b

無事コンテナを実行できたようです。

f:id:renkataoka:20201229173732p:plain

データベースの状態↓

mysql> select * from todo_items;
+--------------------------------------+------------------------------+-----------+
| id                                   | name                         | completed |
+--------------------------------------+------------------------------+-----------+
| c719ab12-0f2b-4ced-985b-abfd0d3982e0 | ??????????????               |         0 |
| f4948b12-3dea-4e34-bc5c-25b9cbef7af3 | ????????                     |         0 |
| 71b71092-42c5-4d04-88d0-512d280f7891 | Happy New Year!              |         0 |
| f11a222b-71ce-4438-8648-53e6862eb09f |  You can also enter English! |         0 |
+--------------------------------------+------------------------------+-----------+
4 rows in set (0.00 sec)

部分的にutf-8に変更したため、完全に対応できたとは言えませんが、一旦は日本語入力もいけましたね。。!
(また違うタイミングでデータベースのことをちゃんと勉強したいです)


データ保持専用のコンテナを作りコンテナ同士を結びつけられたところで、このチャプターは終わりです。
今はまだコンテナごとに立ち上げていますが、次のチャプターではDocker Composeを勉強して、Application Stackという単位で操作する手段を身に着けられるそうです。

マルチコンテナをググった際に日本語版のページが出てきてビックリしましたが、翻訳が機械っぽいし、このチュートリアルもあともう少しなので、
感想を交えたこの一連のブログが101 Tutorialの日本人向け資料として誰かの役に立つことを信じてやり進めます。。

dockerに入門してみる(その6)

2021年まであと少しですね。

前回の記事はこちら↓ ren-opdev.hatenablog.com

Bind Mountsを使う

前回は、名前付きボリュームを使いデータを保持しました。名前付きボリュームの利点は、どこにデータがあるのかを心配せずにデータを保持できる点ですね。 そのとき引用した画像を再掲します。

f:id:renkataoka:20201222005214p:plain
https://docs.docker.com/storage/volumes/ より

今回は、この画像の左側にあるbind mountという概念を勉強していきます。volumeは直接Filesystemの内部を参照しているのに対し、bind mountはFilesystemという単位を参照しているように見えます。

bind mountsはホスト上のマウントポイントをコントロールする概念で、データの保持にも使えますが、コンテナに何かデータを加える用途によく用いられるようです。
つまり、ソースを修正してその差分をコンテナにマウントすることができるようです。

名前付きボリュームとBind Mountsの比較

名前付きボリュームとBind Mountsは、それぞれDockerを利用する上で主要となるボリュームタイプです。他にも色々種類はありますが、ここで2つの比較を整理します。

名前付きボリューム Bind Mounts
ホスト上の場所 Dockerが選ぶ 自分で選ぶ
-vオプションを使用したマウントの例 my-volume:/usr/local/data /path/to/data:/usr/local/data
新しいボリュームにコンテナの中身を入力するか 入力する 入力しない
ボリュームドライバをサポートするか サポートする サポートしない

こうしてみると、Bind Mountsの方が自由度が高いのかな…みたいに思えますね。

コンテナと共に開発する

では、実際の開発フローを模してコンテナを操作していきます。具体的には、

  1. ソースコードをコンテナにマウントする。
  2. "dev" dependencies含め、すべての依存関係をインストールする。
  3. ファイルシステム監視のために、nodemon*1を起動する。

となります。

では、まず以下のコマンドよりコンテナを実行します。

docker run -dp 3000:3000 `
    -w /app -v "$(pwd):/app" `
    node:12-alpine `
    sh -c "yarn install && yarn run dev"

新出オプションの説明を下記にまとめます。

  • -w /app : /appを、コマンドを実行するワーキングディレクトリとして設定する。
  • -v "$(pwd):/app" : 現在のディレクトリを、コンテナ内のホストから/appディレクトリに bind mountする。
  • sh -c "yarn install && yarn run dev" : yarn installyarn run devというshellコマンドを実行する。

コンテナが出力するログを確認すると、nodemonが既に動いていることが分かります。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker logs -f b3eedb11f9c4
yarn install v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.9: The platform "linux" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 39.05s.
yarn run v1.22.5
$ nodemon src/index.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node src/index.js`
Using sqlite database at /etc/todos/todo.db
Listening on port 3000

nodemonがファイル差分を監視していることが分かったので、ソースをいじってみます。
"Add Item"というボタンの文言を"Add"に変えます。

-                         {submitting ? 'Adding...' : 'Add Item'}
+                         {submitting ? 'Adding...' : 'Add'}

http://localhost:3000/ を更新してみると、ちゃんと変更が反映されていますね。 nodemonがファイル差分を感知して、サーバーを再起動したことが分かります。

f:id:renkataoka:20201229145223p:plain


ソース修正後の状態をnodemonを利用して見ることができたところで、このチャプターは終わりです。
次のチャプターでは、production環境に向けてMySQLを操作する方法を勉強するようです。

年内に終わらせたい!

*1:Nodeベースのアプリケーション用ファイル差分監視ツール

merpay Tech Talk|QAx DevOps/マイクロサービス/Backend vol.2【参加レポ】

昨日行われたconnpassのmerpay Tech Talk|QAx DevOps/マイクロサービス/Backend vol.2に参加したら、感激するほど今聞きたかった話が聞けたので、参加レポートを書きます。

参加したきっかけ

  • 新卒入社してから半年ほどQA業務に携わり、段々とQAは開発側の成果物をテストするだけではないことが分かってきた。
  • ここ1,2カ月で開発工程の上流からQAとして参加しているが、QAとしての立ち回りがイマイチ分からない。
    • (元々Androidエンジニアとして配属され、現在もQAと並行して業務を行っている中、視点の切り替えに困っている)
  • 開発工程全てにおいて、QAがどう関わっていけるのか、その結果どのような文化が醸成されるのかを知りたかった。

というのが、参加したきっかけです。

merpayのQAチームの姿勢

特に良いなと思った部分を書いていきます。

どう品質を作っていくか

チームで品質を作る上で、(プロダクト, プロセス, チーム)という3つの軸を重視しています。

  • 1.プロダクト
    • 当たり前品質と魅力的品質をQAチームの大きな方向として定義し、それを各プロダクトに適用していく。
  • 2.プロセス
    • DevOpsの全ての活動の中でQA/カイゼンを行う。
  • 3.チーム
    • 会社組織の状況におり、個々でもチームでも活躍できるチーム作りを行う。
    • プロダクトの規模や状況により、アジャイルウォーターフォールを選んでいる。

Full stack QA engineerを目指す

どういう品質保証のエンジニアが必要なのか、という問いへのmerpayの答えがフルスタックQAエンジニアです。
これには「上流から下流までDevOpsの全ての局面で活躍できるようになろう」、「金融系テックカンパニーのQAとして、マニュアル的な働きかけだけではなくテクノロジーを活用していこう」 というメッセージが込められています。

merpayではEspressoやTestRail、DataDogなどを用いて品質向上、品質保証の効率化に取り組んでいるそうです。
このあたりは弊社でも取り組みはありますが、QAが行うというよりは開発側のリソースに余裕が出てきたときに取り組んでいるという感じで、QAチームとしての効率化はもっとこれからできるのかなと思います。

「チームで品質を作り込むためにできることを全てやる」を地で行っていて、merpayさんの取り組みを参考に自分ももっとできそう!と思いました。

パネルディスカッションで、現場の取り組みを更に深堀り

merpayさんの取り組み紹介の後、第1回目のイベントのときに出た質問について、現場で活躍しているQAエンジニアの方々のパネルディスカッションが行われました。
twitteryoutubeのチャット欄からも質問を随時募集していただき、自分の質問も拾っていただけました。

バックエンドのテスト自動化

Postmanと内製のツールを使い分けて、テスト自動化に取り組んでいるみたいです。内製ツールScenarigoは以下のリポジトリから見られます。

github.com

元々Postmanを主に使っていたそうですが、成果物を開発者と共有しづらく拡張性も弱いというデメリットを感じ、自動化に特化したツールを作ったそうです。スゴイ。。
単発的なテストはPostmanの方が早くできるので、場合に応じて使い分けているそうです。

webのことはまだあまり分かりませんが、弊社でもPostmanを使っていたような…?web側の同期に聞いてみよう。

フルスタックQAエンジニアとして、上手くいっていない領域

QAに限った話ではないですが、リソース不足で手に負えないタスクが出てきてしまい、そこにストレスを感じることがあるようです。
個人の得手不得手によってタスクを振っていく、も採用しつつ、属人化を排除していく取り組みを今後やっていきたいとのことでした。

品質管理の考え方、アジャイル開発でのQAの関わり方

アジャイルだからこう、ということはなく、開発チーム間で異なるチームの課題に対して、QAの立場からどう取り組んでいけるかを考えているそうです。
例えば「開発している機能、サービスがお客様に価値を届けているか分からない」という課題を持っているチームでは、ユーザビリティの視点を持ち込んだり。

これを聞いていて、「QAの知識をプロセスやチームに適用していく」という方向性が自分の中で見えてきた気がしました。

QAが非QAに対して啓蒙していくようなことはあるか

これは自分が質問しました。今の開発チームでは「メンバー間で目標とする品質レベルの理解がバラバラである」という課題を持っており、ここに対してどう取り組んでいくのが有効なのだろうと思い質問しました。

merpayの中では「啓蒙」がハッキリあるわけではなく、QAがプロダクト開発の全工程に入っていくことで、
PMもデザイナーも企画も含めてQAの考え方を提案することができ、結果としてチームの品質意識の向上に繋がっているそうです。日常の取り組みがベースとなり、全員品質みたいなチーム意識が生まれていくのですね。

また、アジャイルの中で重視される「相互理解」がここでも重視され、スプリントレビューで開発・QA交えてQA的なレビューをしたり、
ドッグフーディングをして開発チーム以外にも自分たちの課題を認識してもらっているようです。

開発側のペアプロにQAも参加したり、QA同士でペアプロのようなことをやったり、というような取り組みも行っているようです。
一聴して「良さそう、やりたい!」と思いました。

おわりに

DevOpsにおけるQAの具体的な関わり方や、現場のQAエンジニアの方の考えを聞けてとても良かったです。
mastuさんが最後に「CI環境をゼロから構築できるようになりたい」と仰っていて、"分かる自分も超できるようになりたい"と共感の嵐でした。

merpayさんの取り組みが良すぎて、また自分の中でも次に取り組むべき方向が見えたので、一気にモヤモヤが晴れました。
QAだからこうしなきゃいけない、ということではなく、QAだからできることは何か、という視点でチーム開発に取り組んでいきたいです。

dockerに入門してみる(その5)

自分のキャリアが描けてきているような、ぼやけてきているような、とにかくエンジニアリング超楽しい。そんな日々を過ごしています。

前回の記事はこちら↓ ren-opdev.hatenablog.com 今日は、コンテナのライフサイクルを超えてDBを保持するというタスクに挑戦してみます。

コンテナのファイルシステムの独立性

DB保持の前に、コンテナのファイルシステムってどんなだったっけを見てみます。

コンテナを実行するときは、イメージから沢山のレイヤーを使用します。それぞれのコンテナはCRUD処理のために"scratch space"*1も持っているらしいです。また、まったく同一のコンテナを使用していたとしても、他のコンテナからはファイルの変更は見られないみたいです。ほう…。

実際に動かしてみる。

ファイルシステムについての挙動を確かめるため、ubuntuコンテナを動かしてみます。
このとき、1~10000の中のランダムな数を書き込んだ/data.txtを生成し、コンテナを動かし続けるためにファイルを監視するコマンドも実行します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
da7391352a9b: Pull complete                                                                                                                                     
14428a6d4bcd: Pull complete                                                                                                            
2c2d948710f2: Pull complete                                                                                                                     
Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Status: Downloaded newer image for ubuntu:latest
9fd91cf81b48ae334c88d24a4c8cc737f971e685b352ddbcf140a6f1c45b9fe7

成功しました。
正しく実行できたかを確かめるため、ダッシュボードからコンテナのCLIを起動します。 そして、そのCLIから/data.txtの中身を確認します。

# cat /data.txt
1081

1081が入力されているので、正しく実行できていそうです。

次に、全く同じコンテナイメージを用いてもう一つのubuntuコンテナを動かしてみます。
このとき、-itオプションを付けてubuntuイメージのlsを実行します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -it ubuntu ls /
bin   dev  home  lib32  libx32  mnt  proc  run   srv  tmp  var
boot  etc  lib   lib64  media   opt  root  sbin  sys  usr

ルート配下にdata.txtがないですね。つまり、同じコンテナイメージからコンテナを起動しても、それぞれ独立した別のコンテナとして起動することが分かりました。

コンテナボリューム

同じコンテナイメージのscratch spaceがコンテナに閉じているということは、そのコンテナの削除によって、コンテナに対するそれまでの変更も全て削除されてしまうということです。
これではコンテナに変更を加えたいときに不便ですね。

この不便を解決してくれるのが、Volumesです。
コンテナボリュームとは、ホストマシンまで遡ってそのコンテナのファイルシステムに接続する機能を提供するものです。

f:id:renkataoka:20201222005214p:plain
https://docs.docker.com/storage/volumes/ より

ちょっと何言ってるか分からないですね。
とにかく、手を動かしてみていきます。

ToDoデータを保持する。

サンプルアプリのToDoデータは/etc/todos/todo.dbSQLiteデータベースに保存されています。 一つのファイルに保存されているということは、このファイルをホストマシン上に置いて次のコンテナでも使えるようにすれば、コンテナを変更してもデータを保持し続けることが可能となります。なるほど。データ保持の概念を理解できてきた気がする。

ボリュームを作成し、それをデータが保存されているディレクトリに取り付ければ、データ保持を実現できるようです。
そしてこの、ディレクトリにボリュームを取り付けることをマウントするというようです。カッコイイ。

ボリュームを作成し、マウントしてみる。

今回は名前付きボリュームというものを使っていきます。これはシンプルなデータ容器みたいなもので、ボリュームの名前さえ覚えておけばあとはDockerが良しなにディスクをいじってくれるという。素晴らしいですね。
さらに素晴らしいことに、ボリュームを使う度、正しいデータが提供されることをDockerが保証してくれるようです。素晴らしいですね。

ではまず、ボリュームを作成します。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker volume create todo-db
todo-db

特にログはなく、名前が返されただけです。
次に、一度ToDoアプリのコンテナを止め、ボリュームをマウントした状態で再起動してみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
5f1bae1c1be6ddab33156b9ad77819080d5c097635379e476504767595dee98c

ここで-vオプションを使用し、todo-dbというボリュームを/etc/todosにマウントしています。
http://localhost:3000/ を開き、適当にデータを入れてみます。

f:id:renkataoka:20201222012432p:plain

コンテナを1回削除し、再度コマンドで起動してみます。
おお!今入れたデータがそのまま残っている!!(全く同じなので、スクショは割愛)

今回は名前付きボリュームを使いましたが、Dockerのボリュームには色々種類があるみたいです。おいおい知っていきたいですね。

ボリュームの所在

Dockerが良しなにディスクをいじってくれると言いましたが、実際、どこにデータが格納されているのでしょうか。ボリュームについての情報は、docker volume inspectコマンドで知ることができるようです。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker volume inspect todo-db
[
    {
        "CreatedAt": "2020-12-21T16:23:40Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

/var/lib/docker/volumes/todo-db/_dataに格納されていることと、めっちゃローカルなことは分かりましたが、今日は一旦深追いせず"へ~"で終えます。


ボリュームをマウントすることでコンテナを破棄してもデータを保持できることが分かったところで、このチャプターは終わりです。
AndroidのRoomやSQLiteHelperを勉強したときもそうでしたが、データ保持・操作の領域は"一寸先は沼"という感じがしてまだ少し苦手意識がありますが、焦らず一つずつ理解していきたいです。

次のチャプターでは、Bind Mountsという概念を利用してコンテナイメージの効率的な修正の仕方を学ぶみたいです。

参考

*1:scratch spaceとは、一時的なユーザーデータなどを保持するためのストレージ

dockerに入門してみる(その4)

しばらく前に、「次の新卒に教えたい、入社前に勉強しておくと良いこととかありますか?」という質問を人事から受けましたが、今なら声を大にしてDocker!と言いたい。そんな日々を過ごしています。

前回の記事はこちら↓ ren-opdev.hatenablog.com

リポジトリを作成する。

コンテナイメージを共有するには、Docker HubのようなDocker registryを使う必要があります。

まず、https://hub.docker.com/へログインし、Create Repositoryボタンをクリックします。
次に、リポジトリの名前をgetting-startedとし、公開範囲がPublicであることを確認したらCreateボタンをクリックします。

これだけでリポジトリが作れました。 f:id:renkataoka:20201218195449p:plain 右側にあるDocker commandsに書いてあるコマンドを使えば、このリポジトリにpushできるようです。

リポジトリにpushする。

では、このコマンドを使ってpushしてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker push 530lotus/getting-started
Using default tag: latest
The push refers to repository [docker.io/530lotus/getting-started]
An image does not exist locally with the tag: 530lotus/getting-started

失敗した…と思いきや、またTutorialの方にも「失敗したよね?」と書いてありますね。。
僕の場合ですと530lotus/getting-startedが見つからないと言われているので、タグ付けをして解決していきます。

まずは、Docker Hubにログインします。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker login -u 530lotus
Password:
Login Succeeded

そして、イメージにタグ付けをします。今回はタグ付けというより、ローカルの'getting-started'を530lotus/getting-startedに名称変更する操作といった感じですね。

docker tag getting-started 530lotus/getting-started

もう一度pushしてみます。

PS C:\Users\530lo\Documents\docker\tutorial\app\app> docker push 530lotus/getting-started
Using default tag: latest
The push refers to repository [docker.io/530lotus/getting-started]
e7c0df786fa9: Pushed                                                                                                                                                                                                                                                           a0063b632c32: Pushed                                                                                                                                                                                                                                                           164df51f3404: Pushed                                                                                                                                                                                                                                                           7df30886016b: Mounted from library/node                                                                                                                                                                                                                                        f2253cc2dbde: Mounted from library/node                                                                                                                                                                                                                                        996fa7c6680b: Mounted from library/node                                                                                                                                                                                                                                        3e207b409db3: Mounted from library/node                                                                                                                                                                                                                                        latest: digest: sha256:af721457dd7adc13203207ee664d44f70e68498b029d009092faba6a69e8b63d size: 1788

成功しました!リポジトリにも追加されていますね。おー f:id:renkataoka:20201218202751p:plain

新しいインスタンスでイメージを実行する。

新しいインスタンスでコンテナイメージを実行してみるみたいです。新しいインスタンス…?

とりあえず、言われた通りPlay with Dockerにいき、Loginボタンをクリックします。
ほどなくしてLoginボタンがStartボタンに変わりました。こういう、rails serverのときにも出てくるような初期画面好きです。ワクワクの感じ。 f:id:renkataoka:20201218203735p:plain

そのままクリックしてみると、カウントダウンだけされている画面に。 f:id:renkataoka:20201218203949p:plain 左側の+ ADD NEW INSTANCEをクリックすると、ターミナルが出てきました。このターミナルにdocker runを打ち込むと、無事イメージがpullされ、新しいインスタンスに立ち上がったみたいです。
"新しいインスタンス"は、ローカルじゃない別の環境…くらいの意味だったのかな。実行環境のことをインスタンスと呼ぶのかもしれない。

$ docker run -dp 3000:3000 530lotus/getting-started
Unable to find image '530lotus/getting-started:latest' locally
latest: Pulling from 530lotus/getting-started
cbdbe7a5bc2a: Pull complete 
da41a38d96d0: Pull complete 
3d6d69ed0edb: Pull complete 
13618797e148: Pull complete 
693a276ece8b: Pull complete 
f9fca3fade83: Pull complete 
35fed245150d: Pull complete 
Digest: sha256:af721457dd7adc13203207ee664d44f70e68498b029d009092faba6a69e8b63d
Status: Downloaded newer image for 530lotus/getting-started:latest
169dfe908efffb6a18c401c4458d357d99f1fb1320de0e4870d9a0cdf42b1d96

IPアドレスの右のほうにも3000番のボタンが出てきています。これをクリックすると、ちゃんとローカルでも見られていたアプリケーションを確認することができました。 f:id:renkataoka:20201218205534p:plain


コンテナイメージの共有方法と、共有したイメージがちゃんと実行できることを確かめたところでこのチャプターは終わりです。
新しいイメージを作って、それをregistryにpushし、本番環境で使えるようにする…。これがまさにCIパイプラインで行っていることなんですね。すごい。

次のチャプターでは、前回課題に残していた「リスタートしたらデータ飛ぶ問題」を解決できるようです。