【Chromebook】ChromebookでArduino開発(個人的にはSeeeduinoが使いたい)ができるか?

この記事は 「Seeed UG Advent Calendar 2019 - Qiita」の 16日目 の記事です。

Chomebook(ASUS Chromebook Flip C101PA)を購入したので、どこまでのことが可能か知りたくなるものです。 (注)このエントリ内でChromebookという場合にはASUS Chromebook Flip C101PAと置き換えて読んでいただければと思います。

自分が普段開発関連のことをするときは大概は以下のことを行っています。

  • pythonでの開発
  • Node.jsでの開発
  • ArduinoIDEでの開発
  • Node-REDでの開発
  • RaspberryPi上での開発
  • IBMAWS、Azureなどを使用した開発

この内ChromeLinux(ベータ版)機能があるのでArduinoIDE以外のものに関してはほぼ問題ないのではないかと思います。 ArduinoIDEがうまく行かない可能性としてはArduinoまたはその互換機のUSBドライバーが使えない可能性が考えられることです。 Linux(ベータ版)機能が入っているんだし、Linux版のインストールを行えばいいのでは?という気持ちもありますが、 そこまでするならもう少しお手軽にWindowsやRaspberryPi経由で開発すればいいのかなとも考えてしまいます。

とりあえず、現時点でChromebookArduino開発が可能であるか否かを調べてみました。最近はGroveコネクタのセンサー群を使用していることが多いのでSeeeduino V4.2(以降 Seeeduino)などが使えるかという観点も含めて行ってみたいと思います。

今回使用動作をしてほしいSeeeduinoは以下となります。

続きを読む

【Chromebook】今更ですが、Chromebookを購入しました!

前々から、セカンドPCはWindows7が入っていたノートPC(7年型落ちのAspireOne)でしたが、バッテリーなどもへたってきていることもあったので、 Chromebookを買おうかなとおもってました。

Aspire one 753(2010年製) http://www2.acer.co.jp/catalog/1012/FIX_NBALL_0616.pdf

最近では日本でも取り扱いが増えてきたのですが、割とタフなモデルばかりでそこそこの大きさと重さ(大きさからみれば妥当)のものが 多いイメージです。最近では教育用に特化してきているようなイメージもあるChromebookなので、ボディーの堅牢さは不可欠なのかなと いうことも考えられます。

そうこうしていて、ブラックマンデーなど待っていたりもしたのですが、ようやく購入しました。

続きを読む

【ChromeOSインストール版】Dockerを公式チュートリアル”Get Started with Docker”で学ぶ【挫折編】

前回の続きでChromeOSにDockerをインストールしてみたので公式のドキュメントを見ながら学んで見たいと思います。

【関連エントリ】

uepon.hatenadiary.com

今回はPart3の部分になります。(本当はPart4まで進めたかったが)

ただ、読みすすめるとわかると思いますが、うまくいきません。

原因は未だにわからないのですが、同じPCで別のOS(UbuntuDebianCentOSのそれぞれUSB起動のLive版で確認)では起動できているのでChromeOSならではの問題かと思います。

Get Started, Part 3: Services

ここではServiceに関して学ぶことになります。

事前準備

事前準備としてはDockerのインストールは当然のことながらPart1とPart2を行ってきた環境(コンテナ)があることが必要です。

加えてDocker Composeのインストールが必要になります。Mac用のDockerデスクトップとWindows用のDockerデスクトップにはプレインストールされていますが、Linux系は別途インストールする必要があります。以下のサイトを参考にインストールを行います。

docs.docker.com

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

【インストールログ】

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   617    0   617    0     0    859      0 --:--:-- --:--:-- --:--:--   858
100 11.2M  100 11.2M    0     0  2250k      0  0:00:05  0:00:05 --:--:-- 2910k

$ sudo chmod +x /usr/local/bin/docker-compose

$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01

また、Part2で作成したfriendlyhelloイメージをレジストリにpushして公開することが必須になります。(ここではその共有イメージを使います。) 以下のようにDockerのイメージを実行し、http://localhost:8080/という感じでアクセスできることは確認しておいてください

※原文は4000ポートを使用していますがChromeOSではアクセスがNGなので8080に変更してありますので注意です。

【一般形】

$ docker run -p 8080:80 username/repo:tag

【前回の例で以下のように動作していました(ここでいうhogeは各自のユーザ名に変更してください)】

$ docker run -p 8080:80 hoge/get-started:part2

サービスとは?

このPartではアプリケーションを拡張し負荷分散を行います。そのために分散アプリケーションの階層を1レベル上に上げ、サービスという概念が必要になります。

階層的にはこんな感じ

  • Stack
  • Services (←いまここ)
  • Container (part2(前回)でやった部分)

分散アプリケーションはサービスという概念で構成されていて、例えばビデオ共有サイトでは、データベースサービス、ビデオをトランスコーディングするサービス、フロントエンド用のサービスから構成されているようなイメージになります。

サービスはコンテナから構成されます。その設定でイメージの実行方法、使用するポート、コンテナのレプリカをいくつ実行する必要があるかなどの体系化を行う必要があります。これらの設定を行う場合にはdocker-compose.ymlを記述することでこれらの定義、実行、拡張することが可能になります。

docker-compose.ymlを書いてみる

docker-compose.ymlでコンテナの動作を設定してみます。

以下に記載してymlファイルdocker-compose.ymlとして作成します。 このとき、Part2で作成したイメージをレジストリにpushしていることを確認して、自分の作成した username/repo:tagをイメージの詳細に置き換えてこの.ymlを更新します。 以下の例ではhoge/get-started:part2として設定しています。またポートも4000から8080へ変更しています。

【変更前(原文のまま)】

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

【今回の変更後】

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: hoge/get-started:part2
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "8080:80"
    networks:
      - webnet
networks:
  webnet:

このdocker-compose.ymlでは次のような動きを記述しています。

  1. Part2で作成したイメージをリポジトリから取得。
  2. 取得したイメージから5つのインスタンスを生成して、webと呼ばれるサービスとして名づけます。それぞれに対してCPU資源は10%、50MBのRAMを割当て。
  3. コンテナでエラーがあった場合には再起動。
  4. webサービスの各インスタンスの80ポートはローカルホストからは8080ポート経由からアクセス可能。(オリジナルの記述は4000ポート)
  5. webnetと呼ばれる負荷分散ネットワークを生成し、ポート80を共有するようにwebのコンテナに指示します。 (内部的にはコンテナ自体が一時ポートでwebのポート80に発行)
  6. webnetネットワークをデフォルト設定(負荷分散オーバーレイネットワーク)で定義。

負荷分散ネットワークアプリの起動

まずは以下のコマンドを起動します。コマンドの意味はPart4で説明がありますが、これを実行しないとエラーが発生します。

$ docker swarm init

つづいて以下のように実行します。この実行ではアプリはgetstartedlabと設定しています。

$ docker stack deploy -c docker-compose.yml getstartedlab

【ログ】

$ docker swarm init
Swarm initialized: current node (lj9h2t6lfbifgh9xs3ade0d5g) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-15hfgnp2gthm9vvlu3sk7l628c8q8dthz639bpv3f1ziqogx6w-1djxiz46bjblng15l3qhno4cq 100.115.92.198:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

$ docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web

サービスでは5つのコンテナを起動しているのでそれを確認します。確認方法は以下のようになります。

$ docker service ls

【ログ】

$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                      PORTS
padrydlvz757        getstartedlab_web   replicated          0/5                 hoge/get-started:part2   *:4000->80/tcp

おや?0/5ってなってる…起動できていない…いつまでたってもこの状態…

サービス内で実行されているコンテナはタスクと呼ばれます。タスクには、docker-compose.ymlで定義したレプリカの数まで、数値が増加する固有のIDが与えられます。 ってことなんですが、起動と再起動を繰り返しているっぽい。

$ docker service ps getstartedlab_web
ID                  NAME                      IMAGE                      NODE                DESIRED STATE       CURRENT STATE                 ERROR                              PORTS
so2dl24jsz9g        getstartedlab_web.1       hoge/get-started:part2   penguin             Ready               Assigned 3 seconds ago                                           
zl2amqebzsaq         \_ getstartedlab_web.1   hoge/get-started:part2   penguin             Shutdown            Failed 5 seconds ago          "starting container failed: fa…"   
8ok1l8k9t5qc         \_ getstartedlab_web.1   hoge/get-started:part2   penguin             Shutdown            Rejected 41 seconds ago       "error creating external conne…"   
miejmm2yf3r6         \_ getstartedlab_web.1   hoge/get-started:part2   penguin             Shutdown            Rejected about a minute ago   "error creating external conne…"   
tlhz1mzxkw4g         \_ getstartedlab_web.1   hoge/get-started:part2   penguin             Shutdown            Failed about a minute ago     "starting container failed: fa…"   
t0a3hi9trnev        getstartedlab_web.2       hoge/get-started:part2   penguin             Ready               Assigned 3 seconds ago                                           
ajg4c4ws13f7         \_ getstartedlab_web.2   hoge/get-started:part2   penguin             Shutdown            Failed 5 seconds ago          "starting container failed: fa…"   
klph7llfrxzd         \_ getstartedlab_web.2   hoge/get-started:part2   penguin             Shutdown            Failed 33 seconds ago         "starting container failed: fa…"   
lxb65skkypfp         \_ getstartedlab_web.2   hoge/get-started:part2   penguin             Shutd

コンテナ数を変更しても、Dockerfileを編集して構成を変えてもだめ。ChromeOSではこれ以降はうまくできないようです。(2019.03.18現在) Swarm系の動作はダメなんでしょうか…

現状進めることはできないので、とりあえず以下のコマンドで終了させます。

$ docker stack rm getstartedlab
$ docker swarm leave --force

挫折してみて…

原因はまだよくわかっていませんが、本来の目的はDockerを学ぶことなので放置します。 ChromeOSに関してはやっぱり通常のLinuxとは違うのでしょうか。sshなターミナルマシンとしてはいい感じなんですけどなー

次はOSをUbuntu18.04 LTSのUSB Live版に変更して継続していこうと思います。 もう一回同じPartから進めていこうと思います。

【ChromeOSインストール版】Dockerを公式チュートリアル”Get Started with Docker”で学ぶ【前編】

ChromeOSにDockerをインストールしてみたので公式のドキュメントを見ながら学んで見たいと思います。

【参考】 uepon.hatenadiary.com

正直QiitaなどではDockerのMAC版の資料は検索に引っかかってくるのですが、LinuxというかChromeOSでの情報が見つかりにくい状況だったので、 いいタイミングなので公式のチュートリアルをみながら学んで見ようと思います。

【公式チュートリアル

docs.docker.com

f:id:ueponx:20190227225438p:plain

Get Started, Part 1: Orientation and setup

このパートではDockerの説明とセットアップに関して記載されています。

チュートリアルの流れは以下のようになっているようです。

  1. Docker環境を設定する
  2. 1つのコンテナとしてイメージを構築する
  3. 複数のコンテナを実行するようにアプリをスケールする
  4. クラスタ全体にアプリを分散する
  5. バックエンドデータベースを追加してスタックサービス化
  6. アプリをプロダクションに

個人的には3つ目ぐらいで理解の限界がきそうなのですが、できるだけ頑張ってみます。

この章の最初の方は以下の説明がされています。

  • Dockerとはどんなものなのか?
  • イメージとコンテナとは?
  • コンテナとVMの違い

そのあたりは結構わかりやすいサイトもあるのでググっていただければ…とにかく利点はいっぱいあるのでとりあえず進めていきましょう。

インストール

DockerにはCEとEEの2つのエディションがあります。個人で使うとなるとCE(Community Edition )になると思います。EEはEnterprise Editionの略です。 (注)インストールするときにKubernetesを使う前提であればバージョンに注意が必要のようです。

インストールに関しては過去のエントリを参考にしてください。WindowsとかOSXな方はインストーラがあるので大丈夫でしょう。

【参考:Dockerのインストール】

uepon.hatenadiary.com

参考のエントリ内で、インストール後にバージョンの確認やhello-worldイメージの実行などは行っていますので、このパートはこれで終わりとなります。

Get Started, Part 2: Containers

次のパートででは実際にコンテナを作成して行くお話になります。 前提としてはDockerのインストールと動作に関しては事前確認しておくことですが、前パートが動作できていればOKです。

Dockerで考えるアプリはコンテナ、サービス、スタックというレイヤーがあり、その一番下層にくるのがコンテナになるそうです。

  • Stack
  • Services
  • Container (←ここをやる)

これまでの開発では環境づくりが結構めんどくせー感じだったので、それを解決するんだ〜って感じの内容になっています。 Dockerを使えば、開発環境はイメージの取得だけで、インストールは不要。開発側のアプリも、依存関係、そしてランタイムを含めて移行できるみたいです。

これらのイメージの設定はDockerfileと呼ばれるものによって定義します。 Dockerファイルで定義されていればどの環境に行っても同じ実行環境を再構築できるというのがメリットです。環境構築するのが目的ではないのでこれは便利!

今回のチュートリアルではpythonのアプリケーションを通してコンテナの扱い方を学びます。 まずは作業を行うディレクトリを作成して、移動します。

$ mkdir friendlyhello
$ cd friendlyhello

移動したディレクトリでdockerfileを作成していきます。チュートリアルのdockerfileのテキストをそのまま使用すればいいのですが、今回はこれを少し変えて進めます。 チュートリアルではdockerfileに記述するベースイメージがpython:2.7-slimとなっていますが、ChromeOSではリソースが小さいため Alpine Linuxpythonイメージを使用することになりました。これでかなりのディスクスペースの軽量化も図れます。

alpinelinux.org

【dockerfile】

# Use an official Python runtime as a parent image
# FROM python:2.7-slim # オリジナルの記述
FROM python:2.7-alpine

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

これで定義できました。あとは、開発するアプリの本体app.pyと依存関係の部分(pipのインストール設定)requirements.txtを設定します。 基本的にはチュートリアルそのままで大丈夫です。

【requirements.txt】

Flask
Redis

今回のアプリではRedisというデータベースを使用しています。

Redisは、ネットワーク接続された永続化可能なインメモリデータベース。連想配列、リスト、セットなどのデータ構造を扱える。いわゆるNoSQLデータベースの一つ。オープンソースソフトウェアプロジェクトであり、Redis Labsがスポンサーとなって開発されている。(Wikipediaより

redis.io

自分も初めて使用するものなのであんまりよくわかっていないですが、自分が前々から欲しかった機能はこれかも。今後はRedisを使っていこうかな。

【app.py】

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

ここまでできて、ディレクトリの内容が以下のような状態になったことを確認します

$ ls
Dockerfile      app.py          requirements.txt

続いてはdockerfileの情報をもとにビルドを行います。

$ docker build --tag=friendlyhello .

※ 末尾のドット.も忘れずに!

--tagオプションまたは-tオプションで作成するイメージにタグ付けができるので、今回はfriendlyhelloという名前をつけています。

【ログ】

$ docker build --tag=friendlyhello .
Sending build context to Docker daemon   5.12kB
Step 1/7 : FROM python:2.7-alpine
 ---> 028b1c040d1e
Step 2/7 : WORKDIR /app
 ---> Using cache
 ---> eae50d1926e5
Step 3/7 : COPY . /app
 ---> 5769976c4b92
Step 4/7 : RUN pip install --trusted-host pypi.python.org -r requirements.txt
 ---> Running in 19bcbd6b1e65
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.
Collecting itsdangerous>=0.24 (from Flask->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting Jinja2>=2.10 (from Flask->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->Flask->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
Building wheels for collected packages: MarkupSafe
  Building wheel for MarkupSafe (setup.py): started
  Building wheel for MarkupSafe (setup.py): finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/f2/aa/04/0edf07a1b8a5f5f1aed7580fffb69ce8972edc16a505916a77
Successfully built MarkupSafe
Installing collected packages: Werkzeug, click, itsdangerous, MarkupSafe, Jinja2, Flask, Redis
Successfully installed Flask-1.0.2 Jinja2-2.10 MarkupSafe-1.1.1 Redis-3.2.0 Werkzeug-0.14.1 click-7.0 itsdangerous-1.1.0
Removing intermediate container 5d012755c159
 ---> 9fbdfb77b37c
Step 5/7 : EXPOSE 80
 ---> Running in cc219cd8b3ab
Removing intermediate container cc219cd8b3ab
 ---> 4b2f986a13a9
Step 6/7 : ENV NAME World
 ---> Running in 69f49359148c
Removing intermediate container 69f49359148c
 ---> a602cc6d6905
Step 7/7 : CMD ["python", "app.py"]
 ---> Running in 005ad4362edb
Removing intermediate container 005ad4362edb
 ---> 1ad3fbdd80bf
Successfully built 1ad3fbdd80bf
Successfully tagged friendlyhello:latest

Dockerファイルでは以下のようなことを行っています。

  1. イメージの取得(ローカルにない場合にはDocker Hubから取得)
  2. 作業用ディレクトリの設定
  3. ローカルで開発したアプリのファイルをイメージにコピー
  4. pipでアプリが依存するモジュール環境構築
  5. 公開ポートの設定
  6. 環境変数の設定
  7. アプリの実行

イメージ作成結果の確認すると…

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              1ad3fbdd80bf        40 minutes ago      73MB
python              2.7-alpine          028b1c040d1e        4 days ago          61.2MB

イメージが2つになっていますが、Alpain Linuxのベースイメージを取得してから、それに追加する形でオリジナルのイメージを作成するので、 ローカル側には実質2つのイメージが保存されます。

タグがlatestになっています。 tagオプションの完全な構文は--tag = friendlyhello:v0.0.1と記述するようです。 ベースにAlpine Linuxのイメージを利用していることもあり、イメージのサイズも非常に小さくなっています。

ここまででimageの作成が完了できたので、以降は実行となります。

注意点

Proxyサーバ経由の環境の場合

Proxyサーバの設定が必要な場合にはdockerfileに以下の行を追加してProxyのポートを設定する必要があります。

# Set proxy server, replace host:port with values for your servers
ENV http_proxy host:port
ENV https_proxy host:port

DNSの設定ミスがあった場合

DNSの設定ミスでpipの実行がエラーになった場合には /etc/docker/daemon.jsonに以下のようにDNSの設定を追加してdockerのプロセスを再起動し、再度buildを行ってください。

/etc/docker/daemon.json

{
  "dns": ["your_dns_address", "8.8.8.8"]
}

【dockerサービスの再起動】

$ sudo service docker restart

アプリケーションの実行

いよいよアプリケーションを実行します。実行にはdocker runコマンドを使用します。 その際、-pオプションを使用して、ローカルマシンのポート4000をアプリケーションのコンテナマシンのポート80にマッピングします。 この指定を行うことでローカルマシンのhttp://localhost:4000/のアクセスを行うとコンテナマシンのhttp://localhost:80/にアクセスしたことと同じように動作させることができます。多分Linuxでこの動作を行う際はポート番号に注意が必要のようです。port4000ではエラーが発生したので、port8080などの比較的大きな番号のものに差し替えたほうがいいかと思います。

$ docker run -p 8080:80 friendlyhello

【ログ】

$ docker run -p 8080:80 friendlyhello
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

動作後にローカルPC側のブラウザからhttp://localhost:8080/へアクセスします。

f:id:ueponx:20190227225542p:plain

【ログ】

$ docker run -p 8080:80 friendlyhello
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
172.17.0.1 - - [26/Feb/2019 15:12:43] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [26/Feb/2019 15:12:45] "GET /favicon.ico HTTP/1.1" 404 -

動作が確認できたらコンソールで【Ctrl+C】として終了します。

アプリはデタッチモードでも起動することもできます。(イメージ起動後がバックグラウンドで行われています。)

$ docker run -d -p 8080:80 friendlyhello

実行を確認するためにはdocker container lsを実行して状況を確認します。

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
56b0547a29b9        friendlyhello       "python app.py"     37 seconds ago      Up 33 seconds       0.0.0.0:8080->80/tcp   determined_joliot

このように表示されます。この状態で先ほどと同様にブラウザからのアクセスにも応答され動作を確認することもできます。

デタッチモードでの動作を終了する場合には、docker container lsコマンド実行時に表示されたCONTAINER IDを使用してdocker container stopコマンドで停止を行います。

【ログ】

$ docker run -d -p 8080:80 friendlyhello
56b0547a29b91f3ec0c14f02ce5beea34b67d7db24e415419270e25547c59d97

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
56b0547a29b9        friendlyhello       "python app.py"     37 seconds ago      Up 33 seconds       0.0.0.0:8080->80/tcp   determined_joliot

$ docker container stop 56b0547a29b9
56b0547a29b9

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

f:id:ueponx:20190227225819p:plain

あれ?

先程、Dockerのコンテナの実行を行いましたが、なにか変な感じしませんでしたか?

f:id:ueponx:20190227225542p:plain

counter disabledってなんだよw なんと!公式のドキュメント内でも同じ表示になっています。

f:id:ueponx:20190228003531p:plain

この部分はapp.pyから使用されているRedisが動作できていないことに起因しているようです。 簡単に言うとRedisはインストールしているのにredis-serverを起動していないので、Redisの処理に失敗しているというわけです。

こりゃいかんということでdockerfileの見直しとアプリを起動するシェルスクリプトを作成します。 一見dockerfile内の末尾にあるCMDのエントリにredis-serverの起動を追加すればいいのではないかと思うのですが、CMDは設定に含まれる最後の1行のみが実行されるので 起動シェルを作成するのがよさそうです。

【startup.sh ファイル作成】

#!/bin/sh
redis-server &
python app.py

作成したファイルに実行権限付与をします。

chmod +x ./startup.sh

【dockerfile 要修正】

# Use an official Python runtime as a parent image
# FROM python:2.7-slim # オリジナルの記述
FROM python:2.7-alpine

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt \
    && apk --update --no-cache add redis

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
# CMD ["python", "app.py"] # オリジナルの記述
CMD ["./startup.sh"]

アプリ側の記述も修正を行います。Redisのホストとポート名の引数を変更・追加しています。

【app.py 要修正】

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
#redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
redis = Redis(host="localhost", port=6379, db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

再ビルド、再実行を行う

f:id:ueponx:20190301082302p:plain

ブラウザでアクセスすると…

f:id:ueponx:20190301082319p:plain

無事動作できました。

作成したイメージの共有

こうして作成したイメージは別環境でも使用できるようにレジストリに登録して共有を行えるようにすれば利便性が上がります。 Gitに似たようなものだと思います。Dockerにもオリジナルのレジストリがありますが、それ以外のものも使用することは可能です。

まずは以下のサイトでDocker.Hubのユーザー登録を行います。

hub.docker.com

ユーザー登録が終わったら、コマンドラインで以下を実行し、Dockerの公開レジストリにログインを行います。

f:id:ueponx:20190303113325p:plain

無事にログインができたらレジストリへのレポジトリの関連付けを行うタグ登録を行います。 推奨のタグ付けの規則としてはusername/repository:tagという形式を使うようです。

【一般化】

$ docker tag image username/repository:tag

【例:ユーザー名がhogeの場合】

$ docker tag friendlyhello hoge/get-started:part2

実行がうまく行くと新しいタグイメージが生成されます。(確認はdocker image lsで行えます)

$ docker tag friendlyhello hoge/get-started:part2
$ docker image ls
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
hoge/get-started   part2               6321d6a7a1cd        2 days ago          74.4MB
friendlyhello        latest              6321d6a7a1cd        2 days ago          74.4MB
python               2.7-alpine          028b1c040d1e        8 days ago          61.2MB

f:id:ueponx:20190303113503p:plain

つづいてはイメージの公開になります。

【一般化】

$ docker push username/repository:tag

【例:ユーザー名がhogeの場合】

$ docker push hoge/get-started:part2

f:id:ueponx:20190303113423p:plain

この処理が完了するとpushしたイメージは一般に公開されます。以下のように実行すればローカルにイメージがなければ ネットからイメージを取得して実行が行われます。

$ docker run -p 8080:80 hoge/get-started:part2

実際にイメージを削除して実行すると以下のようになります。

f:id:ueponx:20190303113130p:plain

f:id:ueponx:20190303113020p:plain

とりあえず、これでパート2は終了です。

おわりに

長くなったので一旦ここで終了です。実際に触ってみた感じですが、ぼっち開発な自分でもかなり使えそうな感じです。 問題があるとすればネット回線が太いところでないとむずかしいのかなと。

【関連】 uepon.hatenadiary.com

ChromeOSにDockerをインストールしてみる

自分の中で旬になっているChromeOSネタです。 今回はChromeOSに折角入っているDebianがあるので、そこにDockerをインストールしてみたというものです。 Dockerがあれば、ある程度開発もできるかな?と。ただ、Dockerをあまり知らないのでこれからのお勉強になりますけどね。

Dockerのインストールを行う

ChromeOSにインストールされているのはDebianなので、Debianのインストールの手順を参考にしていきます。 Dockerのページに既にあるのでこれを参考にしてインストールをしていきます。

$ uname -a
Linux penguin 4.19.4-02480-gd44d301822f0 #1 SMP PREEMPT Thu Dec 6 17:45:27 PST 2018 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 9.7 (stretch)
Release:        9.7
Codename:       stretch

【参考】 docs.docker.com

手順としては

  1. 必要となるパッケージをインストール
  2. aptリポジトリへの追加の事前作業(apt-keyはパッケージを認証するのに使用するキーの一覧を管理するコマンドで、このキーで認証されたパッケージは信頼するできるものになります。)
  3. aptリポジトリへ追加
  4. aptリポジトリ更新(apt update
  5. aptリポジトリの追加
  6. dockerパッケージのインストール

公式ページの手順を参考にインストールを行っていきます。

$ sudo apt update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io

インストールができたか確認してみます。

$ 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.
    (amd64)
 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/

f:id:ueponx:20190213223314p:plain

無事に動作しました!

インストールが終わったのでhttpサーバを起動してみる!

では、nginxのコンテナイメージを使ってhttpサーバーを起動してみます。 使用するコンテナは以下のものです。

hub.docker.com

では起動してみます。

$ docker run -d -p 8080:80 --name webserver nginx
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/create?name=webserver: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.

permissionエラーがでてしまいます。その他のコマンド(docker inforやdocker versionなど)でも同様のエラーがでてしまうので、一般ユーザーでもdockerを使用できるように設定をします。 permissionエラーの理由はDockerではUnixポートを使用していますが、このUNIXポートがrootユーザのみアクセスできるためというのが理由になります。 そのため、新たにdockerを使用するユーザーグループを作成し、そのグループに使用するユーザーを追加する必要があります。

公式ページにも一般ユーザーで使用するための設定方法が記述されています。

docs.docker.com

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

公式ページにはコンソールのログアウトを行えばいいと記載されていましたが、実際にはログアウトした後再ログインする必要がありました。(コンソールからlogoutコマンドではだめでした) この部分は、ChromeOS固有の部分だと思います。

ログインしたあとにコンソールで先程エラーになっていたコマンドを実行してみると…

$ docker version
Client:
 Version:           18.09.1
 API version:       1.39
 Go version:        go1.10.6
 Git commit:        4c52b90
 Built:             Wed Jan  9 19:35:59 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.1
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.6
  Git commit:       4c52b90
  Built:            Wed Jan  9 19:02:44 2019
  OS/Arch:          linux/amd64
  Experimental:     false

無事に動作するようになりました。

改めてnginxを使用してwebserverを起動します。その際、ポートフォワードをしてローカルからの8080へのアクセスをコンテナの80ポートにattachするようにしています。

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

オプション

  • --name 【使用したいイメージ名】 : 使用したいイメージ名を指定する
  • -d : コンテナ実行時にバックグラウンドで起動する
  • -p 【ホストのポート番号】:【コンテナのポート番号】 : ポートフォワード設定

イメージがない場合には自動的にダウンロードが行われます。

f:id:ueponx:20190213223644p:plain

docker psで確認するとコンテナの動作が確認できます。

f:id:ueponx:20190213223849p:plain

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

$ docker run -d -p 8080:80 --name webserver nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
6ae821421a7d: Pull complete 
da4474e5966c: Pull complete 
eb2aec2b9c9f: Pull complete 
Digest: sha256:dd2d0ac3fff2f007d99e033b64854be0941e19a2ad51f174d9240dda20d9f534
Status: Downloaded newer image for nginx:latest
59a28a1f0da119d574ecf910be18bbeebe32a5a0f86542d906b264e32ca40e26

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
59a28a1f0da1        nginx               "nginx -g 'daemon of…"   7 seconds ago       Up 3 seconds        0.0.0.0:8080->80/tcp   webserver

docker psコマンドで動作を確認できていますが、実際にこのコンテナへWebブラウザからアクセスを行います。ポートフォワードの設定を行っているのでローカルの8080ポートへアクセスします。

f:id:ueponx:20190213223914p:plain

問題なくアクセスできるようになりました。

おわりに

ChromeOSにもDockerは動作できました。今回は非力なPCなので重たい処理はさせられませんが、 Webのチェックなどでは使用できないこともなさそうです。

ここまでで、Chrome拡張でVNCの動作、Linuxのコンテナ内でのNode.js、Python、Docker、Visual Studio Codeまでインストール・動作確認できているので ライトな開発環境としてもわりと本格的に考えてもいいのかなと思いはじめています。あとはBonjour(Avahi)が使えると本当にいいのですけど…

【関連記事】

uepon.hatenadiary.com

uepon.hatenadiary.com

/* -----codeの行番号----- */