Welcome to Mashykom WebSite



Docker 入門


 Dockerとは、1台のコンピュータ上に、複数種類の開発環境を構築できるソフトウェアです。Docker はアプリケーションをパッケージ化して実行するために、隔離された環境となるコンテナを提供します。開発の実行環境のことをコンテナ(containers)と言います。Dockerでは、カーネルの上にDocker Engineという実行エンジンが搭載され、その上でDockerコンテナが実行されています。隔離してセキュリティを保つことから、実行するホスト上に複数のコンテナを同時に実行することができます。

 コンテナは非常に軽量なものとなります。なぜならハイパーバイザーを別途ロードする必要などなく、ホストマシンのカーネルを使って動作するからです。このことは手元にあるハードウェアの中から、必要なものを使ってより多くのコンテナが実行できることを意味します。それは仮想マシンを使う以上のことです。さらに、Docker コンテナを動作させるホストマシンは、それ自体が仮想マシンであっても構わないのです。

mac_docker.png
Dockerホスト(Docker デーモン、コンテナ)とmacOSの関係図

 コンテナの中身は、1本のファイルに書き出すことができます。これをDockerイメージと言います。コンテナをイメージ化ファイルに書き出しておけば、それはコンテナのバックアップになるだけでなく、別のコンピュータにコピーして、コンテナにして実行することもできます。開発チームリーダーなどが開発に必要なソフトウェアやフレームワークをまとめてセットアップしたDockerイメージを作り、それを開発チーム各自のローカル・コンピュータにインストールして使うことが出来ます。

 Dockerでは、こうしたイメージを1カ所に登録しておくDockerリポジトリという仕組みがあります。DockerリポジトリにDockerイメージを登録しておくと、それをダウンロードして、自分のサーバーやPCで簡単に実行できます。Dockerリポジトリには、誰もが利用できる公共版があります。それがDocker Hubです。Docker Hubには、例えば、TensorflowやNVIDIAの開発チームが登録したDockerイメージなどが利用可能になっています。PytorchのDockerイメージはこちらにあります。NVIDIA JetsonのJetson Docker ContainersはJetson Zooにあります。最近では、ロボット操作のミドルウエアとして有名な ROS(Robot Operating System) のdocker-images も利用可能になっています。

Last updated: 2022.4.17


Docker Desktop のインストール


 Docker DesktopはWindowsやMacなどのローカル環境に簡単にDockerコンテナを用いた開発環境を導入できるソフトウェアです。実際にDockerをインストールして、基本的なコマンドの使い方を説明します。インストールが完了するとDockerのデーモンが起動した状態になりますので、その時点でdockerコマンドが利用できる、つまりDocker Engineが利用できる状態となります。

 Docker Desktopは、個人利用、教育機関、非商用のオープンソースプロジェクトであれば引き続き無料で利用可能です。ここでは、macOSで行なっていますので、macOSでのインストールの説明します。まずは、公式サイトからインストーラーを入手します。「Download Docker Desktop for Mac」をクリックしてインストーラーをダウンロードします。

 ダウンロードされたファイルをダブルクリックすることで、通常のアプリケーションと同様にインストールを進めることができます。Macの時は、ダウンロードされたファイルはdmgファイルなので、ダブルクリックをすることで通常のアプリケーションと同様にインストールを進めることができます。MacのLaunchpadでDockerを起動します。ステータスバーにDockerマークが表示されていれば、Docker Engineが起動している証しです。

 Windows の時は、「Download Docker Desktop on Windows」をクリックしてインストーラーをダウンロードします。「Docker Desktop Installer.exe」をダウンロードした後、ブラウザの「downloads bar」からこのファイルを起動してインストールを実行します。通常のアプリケーションと同様にインストールを進めることができます。

 Docker Desktop自身が占有するサイズは約1.6GBであり、複数のイメージおよびコンテナを含めると20GB,30GBになります。Dockerの利用は、ホストマシンのストレージのかなり大きな部分を占有することに注意が必要です。

 コマンドラインを用いた簡単な操作について、公式マニュアルの日本語版に沿って説明します。Macを使用しているときはターミナル、Windowsのときはコマンドラインを開きコマンドを実行します。

 インストールされたDockerのバージョンを確認するときは、


$ docker version

Docker version 20.10.14, build a224086

 と入力します。現在インストールしているDocker Engineのバージョン情報等が表示されます。動作しているコンテナを確認するときは、


$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

 とします。動作しているコンテナの一覧が表示されます。停止しているコンテナを表示させたい場合は、-aのオプションを付けてコマンドを実行させてください。


$ docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5fb74edf70fc docker/getting-started "/docker-entrypoint.…" 21 hours ago Exited (0) 21 hours ago peaceful_cannon
07d830f43619 docker/getting-started "/docker-entrypoint.…" 21 hours ago Exited (255) 54 seconds ago 0.0.0.0:80->80/tcp
serene_moser
f578d9af8252 docker/dev-environments-go:stable-1 "sleep infinity" 22 hours ago Exited (255) 21 hours ago adoring_mestorf
b9409d8ab0bd hello-world "/hello" 24 hours ago Exited (0) 24 hours ago distracted_matsumoto
4e5a0f20478c hello-world "/hello" 4 weeks ago Exited (0) 22 hours ago serene_tesla
2264e438f13f docker/getting-started:latest "/docker-entrypoint.…" 4 weeks ago Exited (255) 2 weeks ago 80/tcp

 例えば、上記のような表示がなされます。

 イメージファイルを確認するときは、


$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
docker/getting-started latest bd9a9f733898 2 months ago 28.8MB
docker/desktop-git-helper 6d03332b2f0c03ace31c9bca98de4830908e5bcc cf20a5cdae3b 5 months ago 43.7MB
crazymax/linguist 7.17.0 c3eb05776155 5 months ago 71.2MB
hello-world latest feb5d9fea6a5 6 months ago 13.3kB
docker/dev-environments-go stable-1 23b78684a282 8 months ago 1.38GB
docker/desktop-git-helper 5a4fca126aadcd3f6cc3a011aa991de982ae7000 efe2d67c403b 8 months ago 44.2MB

 このコマンドを実行させることで、現在取得済みのイメージの一覧を表示させることができます。なお、以下のコマンドにてイメージをローカルに取得することができます。


$ docker pull [Image名]

 ここで、公式に用意されているhello-worldのイメージファイルを使い、Dockerのコンテナを動作させてみましょう。hello-world Docker イメージを実行し、インストールが正常に行われたかどうかを確認します。以下のコマンドを打ちます。


$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

 「Hello from Docker!」という文言が表示されると成功です。これでDockerコンテナの動作は完了しました。この run コマンドでは、以下の3つが順番に実行されたことになります。

  1. hello-worldのDockerイメージの取得
  2. 取得したイメージを元にコンテナの作成
  3. 作成されたコンテナの実行

 こうして、docker run の1行で動作したhello-worldでは、上述の3つの処理が走っています。この内容については以下で説明します。コンテナの元はイメージです。このイメージがダウンロードされていないとコンテナを作成することはできません。そのため、まずはイメージをダウンロードする必要があります。「docker run hello-world」では、ローカルPCにhello-worldのイメージが存在しない場合、Docker Hubからイメージの取得を行う処理が走ります。

 以下のコマンドを実行してみましょう。


$ docker pull hello-world

 上記の「docker run hello-world」を実行した後であれば、事前に実行済みのため、このコマンドは不要になります。他のイメージを使いたい場合には「docker pull」コマンドにてイメージを取得します。

 コンテナを利用することで、環境の差異を気にせずアプリケーションの開発を進めることができます。そのため、Dockerの利用を前提とした開発は加速すると予想されます。Docker HubやDocker Composeだけでなく、Kubernetesなどと連携するためのツールも増え来ています。既に、Tensorflowの公式サイトでは、 Dockerのイメージをダウンロードする手順が説明されています。TensorflowのDocker Hub リポジトリ、NVIDIAのDocker Containersなどがあります。

 また、Docker Desktop ダッシュボードは、コンテナー、アプリケーション、イメージに直接アクセスできるようなシンプルなインターフェースを提供します。 CLI を使って難しい操作を行う必要がなくなります。

 Containers/Apps 画面では、コンテナーやアプリケーションの実行状態を表示します。この画面においてはコンテナーやアプリケーションとのやりとりを行うことが可能であり、手元のマシンからアプリケーションのライフサイクルを直接制御することができます。 この画面では、コンテナーや Docker Composeベースのアプリケーションに含まれる Docker オブジェクトに対して、主要な操作、つまり詳細確認、対話指示、管理を直感的なインターフェースにより提供します。

Images 画面には Docker イメージが一覧表示されます。 ここからイメージをコンテナーとして実行したり、Docker Hub からの最新版イメージのプル、イメージの詳細確認を行ったりすることができます。 また Snykを利用したぜい弱性スキャン報告の概要が表示されます。 さらにイメージ削除の機能もあり、不要となったイメージをディスク上から削除して容量を確保することができます。 Docker Hub にログインできていれば、Docker Hub上において自分や組織が共有しているイメージを参照することもできます。

Volumes 画面にはボリュームの一覧が表示されるので、ボリュームの生成削除や、どれが利用中であるかの確認が簡単にできます。

 さらに Docker ダッシュボード では以下のことが可能です。Preferences から(Windows では Settings)メニューから簡単に Docker Desktop の設定を行うことができます。Troubleshoot メニューを使ってデバッグや再起動操作を行うことができます。Docker ID を使って Docker Hub にサインインすることができます。Docker ダッシュボードにアクセスするには、Docker メニューの Dashboard を実行します。


Raspberry Pi への Docker のインストール


 ここでは、Raspberry Pi に Docker をインストールするための手続きについて説明します。Raspberry Pi 4 のcpuのアーキテクチャは ARM64です。OSとして、Devian Bullseye 64bit がインストールされているとします。Docker-Desktopと異なり、ターミナルから docker-engine を操作します。MacのターミナルからRaspberry Pi にssh接続します。このサイトにおける説明に沿って説明します。

 古い docker がインストールされているときは、削除します。


$ sudo apt-get remove docker docker-engine docker-io containerd runc

 repositoryをセットアップします。


$ sudo apt-get update
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

 Docker’s official GPG keyを入手します。


$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

 セットアップを続けます。


$ echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
    $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > dev/null

 Docker Engine をインストールします。


$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

 最新のバージョンを知るために、以下のコマンドを打ちます。


$ apt-cache madison docker-ce

-----------
docker-ce | 5:20.10.17~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable arm64 Packages
docker-ce | 5:20.10.16~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable arm64 Packages
docker-ce | 5:20.10.14~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable arm64 Packages
docker-ce | 5:20.10.13~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable arm64 Packages

...

 最新バージョンをインストールします。
sudo apt-get install docker-ce={VERSION_STRING} docker-ce-cli={VERSION_STRING} containerd.io docker-compose-plugin
の{VERSION_STRING}の箇所に最新バージョンの5:20.10.17~3-0~debian-bullseyeを入れます。以下のコマンドでインストールします。


$ sudo apt-get install docker-ce=5:20.10.17~3-0~debian-bullseye docker-ce-cli=5:20.10.17~3-0~debian-bullseye containerd.io docker-compose-plugin

 Docker Engine が正しくインストールできたのを確認します。

$ sudo docker run hello-world

------
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(arm64v8)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

 この状態では まだpiユーザーでは Dockerコンテナを起動できませんが、dockerグループに piユーザを追加すればsudoを付けずに実行できるようになります。


$ sudo usermod -aG docker pi

 reboot すると、sudo は必要ないので


$ docker run hello-world

 とします。

 Nginx コンテナを用いて、Macから Raspberry Pi のブラウザにアクセスして、動作確認してみましょう。


$ docker run --name some-nginx -d -p 8080:80 nginx

-----
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
3b157c852f27: Pull complete
b0badd78900d: Pull complete
7cea0199de6e: Pull complete
1574e1772b11: Pull complete
09af57e2ac73: Pull complete
61db1e71029d: Pull complete
Digest: sha256:10f14ffa93f8dedf1057897b745e5ac72ac5655c299dade0aa434c71557697ea
Status: Downloaded newer image for nginx:latest
a41e097b3943b2833d805e5e0346455631b1bc7113c2fb227272cf6f048452e4

$ docker ps

-----
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a41e097b3943 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, :::8080->80/tcp
some-nginx

 Macのブラウザから {Raspberry Pi のIPアドレス}:8080 にアクセスすると、以下の表示が見られます。


Welcome to nginx!
If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

 このコンテナを削除します。


$ docker stop a41e097b3943
$ docker rm a41e097b3943
$ docker ps

 次に、docker-compose をインストールします。


$ sudo pip3 install docker-compose
$ docker-compose -v

-----
docker-compose version 1.29.2, build unknown

 docker-compose version 1.29.2 が新たにインストールされています。この docker-compose を用いた例がclassmethod.jpのサイトに解説されています。


Docker イメージの構築とコンテナの実行


 移動可能なイメージを生成するための環境は Dockerfile と呼ばれるtxt形式のファイルで設定します。Dockerfileでは、コンテナ内の環境で何をするかを定義します。以下に例を示します。ネットワーク・インターフェースとディスク・ドライバのようなリソースは、システム上の他の環境からは隔離された環境内に仮想化されています。このようなリソースに接続するには、ポートを外の世界にマッピングする必要がありますし、どのファイルを環境にコピーするかを指定する必要もあります。これらの作業を Dockerfile における構築時の定義で済ませておけば、どこで実行しても同じ挙動となります。

一般的に、開発ワークフローとは以下のようなものです。

  1. アプリケーションの各コンポーネントに対する個々のコンテナを Docker イメージから作成し、作成およびテストします。
  2. コンテナと支える基盤(インフラ)を組み合わせ、アプリケーションを完成します。
  3. 完成したコンテナ化アプリケーションをテスト、共有、デプロイします。

コンテナのベースとなるイメージを作成します。Dockerイメージが扱うプライベート・ファイルシステムとは、コンテナ化したプロセスを実行する場所です。つまり、アプリケーションを実行するために必要な何かを含むイメージを作成する必要があります。

 最初にDockerfileの作成の簡単な例を挙げます。まず、Dockerfileを置くディレクトリを作成します。


$ mkdir docker_example && cd docker_example
$ nano Dockerfile

 以下の内容のDockefileを作成します。

# FROM OSの名前:バージョン
FROM ubuntu:22.04

# RUN 実行コマンド
# 記載例
RUN apt-get update && apt-get install -y nano
RUN echo "hello wrold" > sample

 Dockerfileがあるディレクトリで以下のコマンドを実行してみます。


$ docker build -t ubuntu-sample:22.04 .
$ docker images

 ubuntu-sample:22.04という名前のDockerイメージができます。以下のコマンドでDockerコンテナを起動してみます。


$ docker run -it ubuntu-sample:22.04 bash
root@8123daf773d8:/# ls
bin dev home media opt root sample srv tmp var
boot etc lib mnt proc run sbin sys usr
root@8123daf773d8:/# cat sample
hello wrold

 次に、少し複雑な node-bulletin-board プロジェクトの例をダウンロードしましょう。これは Node.js で書かれたシンプルな掲示板アプリケーションです。


$ curl -LO https://github.com/dockersamples/node-bulletin-board/archive/master.zip
$ unzip master.zip
$ cd node-bulletin-board-master/bulletin-board-app

 プロジェクトのダウンロード後は、掲示板アプリケーション内にある Dockerfile (ドッカーファイル)と呼ぶファイルを見てみましょう。Dockerfileにはコンテナに対するプライベート・ファイルシステムを、どのようにして構成するかの記述があります。また、このイメージをベースとして、コンテナをどのようにして実行するかといった、複数のメタデータも含められます。

 ダウンロードしたdockerfileの中身は以下の通りです。


FROM node:current-slim

WORKDIR /usr/src/app
COPY package.json .
RUN npm install

EXPOSE 8080
CMD [ "npm", "start" ]

COPY . .

 Docker は Dockerfile から命令を読み込み、自動的にイメージをビルドできます。 Dockerfile はテキストファイルであり、イメージを作り上げるために実行するコマンドライン命令はすべてこのファイルに含められます。docker build を実行すると、順次コマンドライン命令を自動化した処理を行い、ビルド結果となるイメージを生成します。

 サンプルの Dockerfile 定義は、以下の手順を踏みます。FROM では既存の node:current-slim イメージで始めます。これは公式イメージ( official image )であり、node.js ベンダーによって構築され、Dockerが認定した高品質なイメージであり、Node.js 長期間サポート(LTS)インタプリタと基本的な依存関係を含みます。

 WORKDIR を使い、以降に続く処理すべてを、イメージのファイルシステムで(決してホスト上ではありません)指定したディレクトリ /usr/src/app 内で行うよう指定します。COPY は package.json をホスト上からイメージ内の現在の場所( .)にコピーします(今回の例では、 /usr/src/app/package.json にコピーします)。

 RUN はイメージ・ファイルシステム内で npm install コマンドを実行します(これにより、 package.json を読み込み、アプリケーションの node 依存関係を解決するため、必要なものをインストールします )。

 COPY によって、残りのアプリケーション・ソースコードをホストからイメージのファイルシステムへコピーします。

 Dockerファイルの詳しい説明は、Dockerfile リファレンスを参照ください。

 ソースコードと Dockerfile を準備しましたので、初めてのイメージを構築(ビルド)します。そして、コンテナが期待通りに動作するかどうか確かめましょう。

掲示板イメージを構築するためには、bulletin-board-app ディレクトリで以下のコマンドを実行します。


$ docker build -t bulletinboard:1.0 .

 Docker でimagesを見ると、bulletinboardというイメージファイルが作成されています。

 作成した新しいイメージをベースとするコンテナを起動するために、以下のコマンドを実行します。


$ docker run --publish 8000:8080 --detach --name bb bulletinboard:1.0

 ここではいくつかのフラグを指定しています。

 docker psしてコンテナが動いているか確認してみましょう


$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2eaeea83c733 bulletinboard:1.0 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:8000->8080/tcp bb

 確かに作動しています。

 ブラウザで localhost:8000を開きます。そうすると、掲示板アプリケーションが稼働しているのが見えるでしょう。この段階で、コンテナがすべて期待通りに動作しているのを確認できます。つまり、これでユニットテスト等を実行できるようになります。

docker.png
掲示板アプリの表示例

 稼働中のコンテナの中に入ってみましょう。/bin/bash/または /bin/sh/を実行するということが必要になります。稼働中のコンテナに対して以下のコマンドを実行します。


$ docker exec -it 2eaeea83c733 /bin/sh
#

 プロンプト # がでました。コンテナに入れたので、中身を見てみましょう。


#ls
Dockerfile app.js fonts node_modules package.json server.js
LICENSE backend index.html package-lock.json readme.md site.css

 確かに、Dockerfileとindex.htmlファイルがあります。コンテナから抜けるには、「exit」または「Ctrl +D」を入力します。コンテナの中に、エディタがないのでファイルの中が読めません。

 掲示板コンテナが正しく動作するのを確認したら、コンテナを削除できます。


$ docker rm --force bb

 --force オプションは、実行中のコンテナを停止させて、コンテナが削除されることを可能にします。最初にコンテナを docker stop bb で停止したら、 --force を使って削除する必要はありません。

 イメージを削除するには、docker rmi コマンドを使います。


$ docker rmi [OPTIONS] IMAGE [IMAGE...]
$ docker rmi 2eaeea83c733

 削除したいイメージの名前を引数に設定します。また、TAGを付けて削除するイメージを指定することができます。TAGが省略された場合はlatestが指定されます。

 最後に重要なことは、Docker イメージを作成するためには、コンテナを実行するために必要な環境設定を行うDockerfileの作成が必要ということです。ちなみに、PytorchのDockerイメージを作成するためのDockerfileはこのファイルです。Docker入門の解説記事はweb上に多数ありますが、Docker ドキュメントの日本語解説記事は docs.docker.jpの「Get started - 始めましょう」にあります。

 最後に、マウントと呼ばれる操作について説明します。

 通常、コンテナはそのコンテナの中にあるデータだけを利用できます。あるコンテナから、別のコンテナの中にあるデータや、Dockerホストのデータを利用することはできません。例えば、コンテナの中で ls /home というコマンドを実行しても、出力されるのはコンテナの中の /home の情報だけです。別のコンテナの /home や、Dockerホストの /home の情報はまったく出力されません。

 Docker における「マウント」とは、コンテナの外にあるデータを、コンテナの中で利用できる状態にすることを意味します。マウントには、大きく分けて、ボリュームマウントとバインドマウントがあります。ボリュームマウントは、Dockerホストのファイルやディレクトリのうち、Dockerが管理しているものを利用できるようにする機能です。docker engineの管理下にvolumeを確保してそこにファイルを管理します。

 バインドマウントは、Dockerホストのファイルやディレクトリを利用できるようにする機能です。バインドマウントを行うと、コンテナの外にある Dockerホストのファイルを、コンテナの中から読み書きできるようになります。

 マウントのオプションは、--volume(省略形で、-v) または --mount が使用されます。バインドマウントの例を以下に挙げます。

 実行する前に、ディレクトリ「dockers」を作成して、その中に、以下の内容の「Hello.java」というファイルを作成して下さい。

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

 以下のコマンド実行して下さい。


# --mount オプションのケース
$ docker run -d -it --rm --name devtest \
    --mount type=bind,source="$(pwd)"/dockers,target=/app \
    -p 8080:80 nginx:latest

$ docker exec -it da29b95b9b9a /bin/sh
#cat app/Hello.java

 source はマウント元、Jetson 内にあるファイルシステムです。sourceあるいはsrcといった指定がよく用いられます。targetには、コンテナー上にてマウントするファイルまたはディレクトリのパスを指定します。destination, dst, targetといった指定がよく用いられます。コンテナ内のディレクトリ app はホストのディレクトリ dockers への入り口になります。

 マウントが正しく生成されたかどうかを docker inspect devtest により確認します。出力の中でMountsの項目を見てみます。

"Mounts": [
    {
        "Type": "bind",
        "Source": "/Users/koichi/dockers"
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

 となっています。


$ docker exec -it da29b95b9b9a /bin/sh
#cat app/Hello.java

 と打つと、ホストのdockersにあるHello.java というファイル内が見れます。

 このコンテナを削除します。「docker rm --force da29b95b9b9a」削除しないと以下のコマンドでエラーが出ます。


# -volume オプションのケース
$ docker run -d -it --rm --name devtest -v "$(pwd)"/dockers:/app \
    -p 8080:80 nginx:latest

$ docker exec -it da29b95b9b9a /bin/sh
#cat app/Hello.java

 上記のどちらのケースでも、マウントが実行され、ホストファイルシステム内の Hello.java の中身が見れました。

 ボリュームマウントの例を挙げます。--volume(省略形で、-v) または --mount のどちらかで実行して下さい。


# -v オプション
$ docker run -d --rm --name devtest -v mymount:/app nginx:latest

# -mount オプション
$ docker run -d --rm --name devtest --mount source=myvol2,target=/app nginx:latest

$ docker inspect devtest

        "Mounts": [
            {
                "Type": "volume",
                "Name": "mymount",
                "Source": "/var/lib/docker/volumes/mymount/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }

 ホストシステム内にmymount というディレクトリが作成され、コンテナ内のディレクトリ /app からアクセスできます。ただし、このmymountはコンテナからしかアクセスできません。


Jetson Nano に Docker コンテナを作成


 Jetson Nano に Docker イメージをダウンロードして、コンテナを起動してアプリを実行することを取り上げます。

 JetPack には、Docke engine はインストール済みです。バージョンを確認しましょう。


$ sudo docker version

 sudo なして実行するために、


$ sudo usermod -aG docker [user_name]
$ sudo reboot

 とします。ここで、Docker コンテナを実行してみましょう。


$ docker run hello-world

 JetPackに対して提供された機械学習用のl4t-ml dockerイメージには、TensorFlow、PyTorch、JupyterLab、およびPython3.6環境にプリインストールされたscikit-learn、scipy、Pandasなどの他の一般的なMLおよびデータサイエンスフレームワークが含まれています。これらのコンテナは、Jetson Nano、TX1 / TX2、Xavier NX、および、 AGXXavier用のJetPackのリリース、JetPack 4.6 (L4T R32.6.1)、JetPack 4.5 (L4T R32.5.0)などをサポートしています。

 JetPack-L4Tの各バージョンに対応したl4t-mlコンテナのさまざまなタグを使用でき、それぞれがPython3.6をサポートしています。JetsonにインストールしたJetPack-L4Tのバージョンと一致するタグを必ず使用して、インストールします。最新版はJetPack 4.6 (L4T R32.6.1)です。このパッケージには以下のものが含まれています。

JetPack 4.6 (L4T R32.6.1)

l4t-ml:r32.6.1-py3
TensorFlow 1.15.5
PyTorch v1.9.0
torchvision v0.10.0
torchaudio v0.9.0
onnx 1.8.0
CuPy 9.2.0
numpy 1.19.5
numba 0.53.1
OpenCV 4.5.0 (with CUDA)
pandas 1.1.5
scipy 1.5.4
scikit-learn 0.23.2
JupyterLab 2.2.9

 Tensorflow のバージョンが古いです。詳細は、nvidiaの公式サイトを読んでください。

 まず、JetsonにインストールしたJetPack-L4Tのバージョンに対応する、l4t-mlコンテナタグの1つをdocker pull します。 最新のJetPack 4.6(L4T R32.6.1)リリースを実行している場合には


$ docker pull nvcr.io/nvidia/l4t-ml:r32.6.1-py3

 とします。次に、コンテナを起動します。


$ docker run -it --rm --runtime nvidia --network host nvcr.io/nvidia/l4t-ml:r32.6.1-py3

root@jetson-desktop:/# ls
bin dev etc lib mnt proc run srv tmp var
boot dst home media opt root sbin sys usr
root@jetson-desktop:/# python3
Python 3.6.9 (default, Jan 26 2021, 15:33:00)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.9.0'
>>> import cv2
>>> cv2.__version__
'4.5.0'
...

root@jetson-desktop:/#

 以上のように、Pythonを起動して、梱包されているパッケージを import できます。

 JupyterLab Server は自動的に起動します。ブラウザから「http:// localhost:8888」にアクセスして接続できます。リモートホストから接続する場合、Jetson Nano のIPアドレスに置き換えます。 JupyterLabへのログインに使用されるデフォルトのパスワードはnvidiaです。

 Jetsonのスクリプト、データなどをマウントすると便利です。Jetsonのファイルシステムにあるスクリプトなどをコンテナ内で利用するときは、Dockerインスタンスを起動するときにDockerの -v フラグを使用します。


$ docker run -it --rm --runtime nvidia --network host -v /home/user/app:/location/in/container nvcr.io/nvidia/l4t-ml:r32.6.1-py3

 Jetson Nano 内にあるディレクトリdockers 内のファイルにアクセスする時は、通常、以下のように、コンテナ内のディレクトリ /app を使います。


$ docker run -it --rm --runtime nvidia --network host -v "$(pwd)"/dockers:/app nvcr.io/nvidia/l4t-ml:r32.6.1-py3

root@jetson-desktop:/# cd /app
root@jetson-desktop:/# ls #ホストファイルのdockers の中が読めます

 最後に、Hello AI World の docker コンテナを作成します。dusty-nv/jetson-inference/での説明に沿って、以下のコマンドを打ちます。


$ git clone --recursive https://github.com/dusty-nv/jetson-inference
$ cd jetson-inference
$ docker/run.sh

----
reading L4T version from /etc/nv_tegra_release
L4T BSP Version: L4T R32.6.1
size of data/networks: 971683071 bytes
Downloading pytorch-ssd base model...
python/training/detection/ssd/models/mobilenet-v1-ssd-mp-0_675.pth: そのようなファイルやディレクトリはありません
CONTAINER: dustynv/jetson-inference:r32.6.1
DATA_VOLUME: --volume /home/jetson/jetson-inference/data:/jetson-inference/data --volume
/home/jetson/jetson-inference/python/training/classification/data:/jetson-inference/python/training/classification/data
--volume
/home/jetson/jetson-inference/python/training/classification/models:/jetson-inference/python/training/classification/models
--volume
/home/jetson/jetson-inference/python/training/detection/ssd/data:/jetson-inference/python/training/detection/ssd/data
--volume
/home/jetson/jetson-inference/python/training/detection/ssd/models:/jetson-inference/python/training/detection/ssd/models
USER_VOLUME:
USER_COMMAND:
V4L2_DEVICES: --device /dev/video0 --device /dev/video1
xhost: unable to open display ""
Unable to find image 'dustynv/jetson-inference:r32.6.1' locally
r32.6.1: Pulling from dustynv/jetson-inference
...

root@nano:/jetson-inference#

 学習済みモデルのダウンロードの設定画面が表示されるので、必要なモデルを選択します。このダウンロードに相当な時間がかかります。追加のモデルをダウンロードするときは、以下のコマンドを使用します。


$ cd /jetson-inference/tools
$ ./download-models.sh

 自動的に dustynv/jetson-inference:r32.6.1 のdockerイメージがダウンロードされて、コンテナが実行されます。

 このコンテナは以下のファイルパスに対してマウントされています。

 このコンテナが削除されてもこれらのファイルはホストに残ります。また、自身でマウントを設定したいときは、


$ docker/run.sh --volume /my/host/path:/my/container/path # these should be absolute paths

 とします。コンテナが起動しているならば、以下のようにして、物体検出などのプログラムを実行できます。


# cd build/aarch64/bin
# ./video-viewer /dev/video2
# ./imagenet images/jellyfish.jpg images/test/jellyfish.jpg
# ./detectnet images/peds_0.jpg images/test/peds_0.jpg
# (press Ctrl+D to exit the container)

 初回の実行では事前処理に時間がかかります。辛抱して待ちましょう。Docker を使用せずに実行するときのケースは、Jetson Nano で機械学習入門のページを見て下さい。実行の手続きは全く同じです。

 コンテナから exit すると、自動的にコンテナは消去されます。これ以降、このイメージのコンテナを起動しても、マウントが切断されてしまうようで、上記のプログラムは実行できません。再度行うためには、dustynv/jetson-inference:r32.6.1 のイメージを削除して、docker/run.sh を実行する必要があります。やはり、Docker を利用すると手間がかかります。

 なお、Docker で作成したディレクトリjetson-inference は、コンテナの外からは管理できません。Docker で新規作成する以前に、jetson-inferenceがインストールされているケースはこの限りではありません。


ROS2の docker image を用いたシミュレーション


 PC(または Raspberry Pi)にROSのDockerイメージをインストールする手続きについて説明します。どちらのケースでも基本的に同じ手順になります。ただ、リモートPCから、ROS2のDocker イメージをRaspberry Pi にインストールする場合には、リモートPCからssh接続をする必要があります。ROS の Docker image はこのOSRFのgithubに多数用意されています。ここでは、ブラウザからROSを操作したいので、Tiryohさんが用意したdocker-ros2-desktop-vncを利用します。サイズは約4.7GBあります。

 Dockerイメージをダウンロードして、コンテナを実行します。PCのターミナルから


$ docker run --rm -p 6080:80 --shm-size=512m tiryoh/ros2-desktop-vnc:foxy

----------
* enable custom user: ubuntu
useradd: user 'ubuntu' already exists
set default password to "ubuntu"
cp: cannot stat '/root/.config': No such file or directory
2022-06-28 01:37:21,802 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in
the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2022-06-28 01:37:21,802 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
2022-06-28 01:37:21,818 INFO RPC interface 'supervisor' initialized
2022-06-28 01:37:21,819 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2022-06-28 01:37:21,820 INFO supervisord started with pid 20
2022-06-28 01:37:22,824 INFO spawned: 'nginx' with pid 22
2022-06-28 01:37:22,829 INFO spawned: 'web' with pid 23
2022-06-28 01:37:22,834 INFO spawned: 'xvfb' with pid 24
2022-06-28 01:37:22,839 INFO spawned: 'wm' with pid 25
2022-06-28 01:37:22,844 INFO spawned: 'lxpanel' with pid 26
2022-06-28 01:37:22,850 INFO spawned: 'pcmanfm' with pid 27
2022-06-28 01:37:22,856 INFO spawned: 'x11vnc' with pid 28
2022-06-28 01:37:22,863 INFO spawned: 'novnc' with pid 29
2022-06-28 01:37:23,679 INFO Listening on http://localhost:6079 (run.py:87)
2022-06-28 01:37:23,828 INFO success: nginx entered RUNNING state, 
...

 と入力すると、VNCが起動して、ブラウザからアクセスが可能になります。MacにROS2 のdockerイメージをインストールしたときは、Macのブラウザから VNCのアドレス「http://127.0.0.1:6080/」にアクセスします。リモートPCのブラウザから、Raspberry Pi を操作しているときは、「{Raspberry Pi's IP address}:6080/」にアクセスします。以下の画像はリモートPCのブラウザから、Raspberry Pi のVNCにアクセスしたケースに対応しています。

ros_vnc.png

 このdocker containerは ROS2-Foxy-desktop バージョンに対応しています。なので、Turtlesim ノードが利用できます。早速、Turtlesim を駆動してみましょう。

 VNCウインドウ内のlaunchpad の System Tools からLXTerminalを起動して、


$ ros2 run turtlesim turtlesim_node

 と入力すると、ウインドウに亀が登場します。

ros2_turtle.png

 同じく、VNCウインドウ内のlaunchpadから2個目のターミナルを起動して、


$ ros2 run turtlesim turtle_teleop_key

 と打ちます。このターミナルで、亀の移動が可能になります。カーソル移動キーで操作できます。「🔼」キーを打つと、前方に移動します。「🔽」キーを打つと、後ろに後退します。「▶️」を打つと右回転します。

 ROSのイメージファイルを直接ダウンロードせずに、Dockerfileからビルドしたい時は以下のようにします。Dockerfile を配置したいディレクトリを作成して、そこに移動して、以下のコマンドを入力します。


$ git clone https://github.com/Tiryoh/docker-ros2-desktop-vnc.git

# build for foxy
$ cd foxy && docker build -t tiryoh/ros2-desktop-vnc:foxy .

 イメージファイルがビルドできたので、以下のコマンドを打ってコンテナを実行します。


$ docker run --rm -p 6080:80 --shm-size=512m tiryoh/ros2-desktop-vnc:foxy

 PC内のエディタなどでコピーしたテキストはVNCウインドウのターミナルに直接貼り付けることができません。VNCウインド内の左端にある「clipboard」を開いて、これに一度貼り付けます。これを再度コピペするという手順が必要です。

 次に、Gazebo を利用した Turtlebot3 のシミュレーションの方法について説明します。(残念ながら、以下のシミュレーションは Raspberry Pi 4 で実行可能ですが、Raspberry Pi 3 では実行できません。)tiryoh/ros2-desktop-vnc:foxy を起動して、VNCウインドウにアクセスします。VNC画面の左下メニューのSystem Tools から LXTerminal を起動します。以下のコマンド打ちます。


$ gazebo /opt/ros/foxy/share/gazebo_plugins/worlds/gazebo_ros_camera_demo.world

Gazebo の軌道には時間がかかります。Gazebo が起動してモデルが動いていれば成功です。

 次に、Navigation2とTurtlebot3パッケージをインストールします。VNC画面のターミナルを起動して入力して下さい。シミュレーションを実行する前には以下の入力は毎回必要です。コンテナ内で行うので手間がかかります。


# Navigation2のインストール

$ sudo apt update
$ sudo apt install ros-foxy-navigation2 ros-foxy-nav2-bringup

# Turtlebot3パッケージのインストール
$ sudo apt install ros-foxy-turtlebot3*

 Turtlebot3の環境変数の設定を行います。この環境設定は tb3_simulation_launch.py を用いた シミュレーションを実行する前に行います。


$ source /opt/ros/foxy/setup.bash
$ export TURTLEBOT3_MODEL=waffle
$ export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/foxy/share/turtlebot3_gazebo/models

 TURTLEBOT3_MODEL=waffle としていますが、TURTLEBOT3_MODEL=burger とすることもできます。

 以下を実行してTurtlebot3 GazeboワールドとNavigation2を起動します。最初はモデルの読み込みに時間がかかります。


$ ros2 launch nav2_bringup tb3_simulation_launch.py

 RViz2とGazeboが起動したら、 Navigation2のドキュメントにある説明に沿ってシミュレーションを行います。

gazebo_world.png
gazebo-world:中央下にある灰色の丸がロボットです

 次に、少し異なるシミュレーション用のTurtleBot3環境を設定しましょう。TurtleBot3のシミュレーション環境は3種類あります。Gazeboを起動するとき、以下の3種類のコマンドが使用可能です。


$ source /opt/ros/foxy/setup.bash
$ export TURTLEBOT3_MODEL=burger
$ export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/foxy/share/turtlebot3_gazebo/models

# Empty World
$ ros2 launch turtlebot3_gazebo empty_world.launch.py

# TurtleBot3 World
$ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

# TurtleBot3 House
$ ros2 launch turtlebot3_gazebo turtlebot3_house.launch.py

 通常は、turtlebot3_world.launch.py を実行します。Gazeboが起動するまで結構時間(数分)がかかります。これらの3種類の環境をみて、好きなものを選択して下さい。Gazeboが起動したら、2番目のターミナルを起動して、Gazeboをkeyboardで操作するために、以下のコマンド打ちます。


$ source /opt/ros/foxy/setup.bash
$ export TURTLEBOT3_MODEL=burger

# Operate TurtleBot3
$ ros2 run turtlebot3_teleop teleop_keyboard

---- 表示

Control Your TurtleBot3!
---------------------------
Moving around:
  w
a s d
  x

w/x : increase/decrease linear velocity (Burger : ~ 0.22, Waffle and Waffle Pi : ~ 0.26)
a/d : increase/decrease angular velocity (Burger : ~ 2.84, Waffle and Waffle Pi : ~ 1.82)

space key, s : force stop

CTRL-C to quit

 Turtlesimに比較して、Gazeboのレスポンスが非常に遅い。Turtlesim でのシミュレーションで利用されるキーボード操作用のノードが実行されます。キーの操作は以下の通りです。
 i : 前進
 j : 左旋回
 k : 停止
 l : 右旋回
 < : 後退

 「k」を入力すると停止するとなっていますが、動き続けます。「s」キーを打つとすぐに停止します。「w」キーを打ち続けると、進行速度がスピードアップします。このキーボードの操作に対応して、(teleop_keyboard)ノードからトピック・メッセージが配信されます。TurtleBot3のモーター・ノードがこのデータを受信します。

 障害物から一定の距離を保ち、衝突を回避するために曲がる単純な衝突回避ノードが用意されています。TurtleBot3の世界でTurtleBot3を自律的に駆動するには、2番目のターミナルに以下のコマンドを入力します。


# auto drive
$ ros2 run turtlebot3_gazebo turtlebot3_drive

 RViz2は、シミュレーションの実行中に公開されたトピックを視覚化します。上記のコマンドを打った後で、 3番目のターミナルを起動して、以下のコマンドを入力すると、新しいウィンドウにRViz2を起動できます。ロボットの衝突回避運動が視覚的に観察できます。


$ source /opt/ros/foxy/setup.bash
$ ros2 launch turtlebot3_bringup rviz2.launch.py

 以上のように、Docker を用いれば、PCのプラットホームに関係なく、ROS2でのロボット操作のシミュレーションが簡単にできます。

このページの先頭に戻る

C++ 言語入門のページに行く

トップ・ページに行く