2021/10/21

docker再びの再び (3)

前回、Dockerfile を使って簡単な、本当に簡単なイメージを作るところまでやった。
RUN で実行した内容までイメージに含まれるから、準備はそこでやっておけというところだろう。

つまり、Dockerfile をもらった場合は、それで docker build してイメージを作って、docker run してやればよいということか。
しかし、Dockerfile だけでイメージを作ることができれば良いのだけど、GitHub に上がってるやつなんかは他のファイルもいるだろうから、コードとか気にせず使いたいだけだったらイメージそのものがほしいところだ。

そこで Docker Hubの出番か。


docker run するときも、ローカルにイメージがなければ docker pull でダウンロードしてくると書いてあった。
なので、私も動かしたいものを Docker Hub から探してきて、docker pull して docker run すれば動かせると思う。

 

困ったことに、これを動かしたい、というものが思いつかない。。。
仕方ないので、これにした。

https://hub.docker.com/r/btcpayserver/lightning/tags

$ docker pull btcpayserver/lightning:v0.10.1-2
v0.10.1-2: Pulling from btcpayserver/lightning
a330b6cecb98: Pull complete
d18683af84c4: Pull complete
d089f3521651: Pull complete
00b9a3bf1da8: Pull complete
c6394cf1c296: Pull complete
241733e1daf6: Pull complete
de9614f35f2b: Pull complete
735eb65d54fb: Pull complete
8047f9f53e51: Pull complete
c9ed1993582a: Pull complete
Digest: sha256:929386072091408c8574fdda9c5e059cf2846fdef82b194f810ddb2281e09ac9
Status: Downloaded newer image for btcpayserver/lightning:v0.10.1-2
docker.io/btcpayserver/lightning:v0.10.1-2

$ docker run btcpayserver/lightning:v0.10.1-2
Invalid combinaion of LIGHTNINGD_NETWORK and LIGHTNINGD_CHAIN. LIGHTNINGD_CHAIN should be btc or ltc. LIGHTNINGD_NETWORK should be mainnet, testnet or regtest.
ltc regtest is not supported

イメージは取ってくることができたが、単に docker run するだけではダメだった。
環境変数かな?

Docker run reference | Docker Documentation
https://docs.docker.com/engine/reference/run/#env-environment-variables

docker run の後に -e "xxx=yyy" の形か、export された環境変数なら -e xxx で指定できるようだ。

$ docker run -e "LIGHTNINGD_NETWORK=regtest" -e "LIGHTNINGD_CHAIN=btc" btcpayserver/lightning:v0.10.1-2
network=regtest added in /root/.lightning/config
rpc-file=/root/.lightning/lightning-rpc added to /root/.lightning/config
Installing bundled plugins
C-Lightning starting, listening on port 9735
2021-10-20T10:31:52.693Z UNUSUAL lightningd: Creating configuration directory /root/.lightning/regtest
2021-10-20T10:31:52.693Z UNUSUAL lightningd: Creating configuration directory /root/.lightning/regtest
2021-10-20T10:31:52.920Z INFO    database: Creating database
2021-10-20T10:31:53.000Z UNUSUAL hsmd: HSM: created new hsm_secret file

ちゃんとした動作環境を用意していないからここで止まってしまうが、 -e で指定すれば動くことは分かった。
ついでに bitcoind も動かせることを確認しよう。

https://hub.docker.com/r/btcpayserver/bitcoin/tags

$ docker pull btcpayserver/bitcoin:22.0-1-amd64
22.0-1-amd64: Pulling from btcpayserver/bitcoin
07aded7c29c6: Pull complete
d17b51f7cfe8: Pull complete
6d6d52f12809: Pull complete
e47859bdfcf2: Pull complete
a57e01ab8b33: Pull complete
Digest: sha256:aacdcf9419dc88d80fd130fc7d51dda46b1be323967798c113ff77b6b6eea74c
Status: Downloaded newer image for btcpayserver/bitcoin:22.0-1-amd64
docker.io/btcpayserver/bitcoin:22.0-1-amd64

$ docker run btcpayserver/bitcoin:22.0-1-amd64
2021-10-20T10:40:41Z Ignoring unknown configuration value mainnet
2021-10-20T10:40:41Z Bitcoin Core version v22.0.0 (release build)
2021-10-20T10:40:41Z Assuming ancestors of block 00000000000000000008a89e854d57e5667df88f1cdef6fde2fbca1de5b639ad have valid signatures.
(略)

立ち上がる。
立ち上がるのだが、設定ファイルを書き換えて動かしたい。しかしイメージは既にできあがった状態だ。
たしか、ボリュームをマウントする方法があったはずだ。


docker runのオプションにある。

VOLUME (shared filesystems)
https://docs.docker.com/engine/reference/run/#volume-shared-filesystems

-v=ローカルのディレクトリ:コンテナ側のディレクトリ、という形だ。
指定すると、確かにローカルのディレクトリにコンテナ側で作成されたファイルが入ってくることが分かった。

が、どうもこの Dockerfile というかイメージは設定ファイルも一緒に作るようで、設定ファイルを先に置いていても上書きされてしまう。

こういうのはどうしようもないな。
幸いなことに、設定ファイルを作る部分は Dockerfile の RUN ではなく CMD 側で実行されているようなので、例えば regtest用にしたいならこんな感じで変数を与えてやると良い。

$ docker run -e "BITCOIN_NETWORK=regtest" -v=`pwd`/bitcoin:/home/bitcoin/.bitcoin btcpayserver/bitcoin:22.0-1-amd64

"entrypoint.sh" のような名前のファイルは、コンテナ起動時の設定をするためによく使われると Dockerの本にも書いてあったような気がする。今思えば、会社にあったオライリーの Docker本はなかなかよさそうだったのでもっと読んでおけば良かった。

ともかく、docker pull して楽ができるとしても、カスタマイズをしたいならオリジナルが何をしているのかを知っていないといかん。
とは思ったものの、今回は Docker Hubのリポジトリとほぼ同じ名前で GitHub のリポジトリがあったからたどり着けたのだが、普通はどうするのだろうか? IMAGE LAYERのところを見ても詳細はわからんし。


あとは、ポート番号だ。
単に docker run だけした場合、どうもコンテナで起動したアプリのポート番号はホスト側には出てこないようなのだ。

EXPOSE (incoming ports)
https://docs.docker.com/engine/reference/run/#expose-incoming-ports

こういう「incomming」って、誰に向けての説明か悩むよね。コンテナを起動するコマンドが主体なのか、コマンドを実行するのはホストだからホストが主体なのか。

オプションは -p ホスト:コンテナ らしい。「Publish a container's port or a range of ports to the host」なので、コンテナのポートをホストに公開するオプションになるようだ。

$ docker run -e "BITCOIN_NETWORK=regtest" -v=`pwd`/bitcoin:/home/bitcoin/.bitcoin -p 18444:18444 -p 18443:18443 btcpayserver/bitcoin:22.0-1-amd64

こうするとホスト側で ss -nat などでポート番号の使用状況を確認すると 18443, 18444 が出てくるようになる。

ちなみに、実行中のコンテナにログインしてコマンドを実行したいと思うなら docker exec を使うとよいだろう。

$ docker ps
$ docker exec -it d33e6ab4e04f /bin/bash

"d33...04f"は実行中のコンテナIDで、doker ps などで確認できる。

 

ネットワーク関係は次回だな。

2021/10/20

docker再びの再び (2)

さて、docker本体のことをやっていこう。

あれからちょっと本を読んで勉強したのだが、私は「まずDockerfileから!」と思っていたのだが、コマンドラインからでもできるけどいちいち面倒だからDockerfileにする、という感じなのだな。
まあ、そういう意味ではDockerfileから始めるのも間違いではないのか。

今回はこういうバージョンで進めます。

$ docker --version
Docker version 20.10.7, build 20.10.7-0ubuntu1~20.04.2


基本に忠実にいこう。
というよりも、本家のドキュメントが一番わかりやすい気がする。

Docker overview | Docker Documentation
https://docs.docker.com/get-started/overview/

Docker objectsのところに動かし方の例が載っていた。 "docker run" でいろいろやってくれるそうだ

$ docker rm `docker ps -qa`
$ docker rmi `docker images -qa`
$ docker run -i -t ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:latest
root@972d70ba7c5b:/#

最初の2コマンドは、残っていたコンテナやイメージを全部消したかっただけだ。
3つめのコマンドがサイトに書いてあった例である。
Dockerfile がなくても使えるのだが、引数が多くなると面倒だから Dockerfileにしよう、というところだろう。

コンテナはメインプロセス?が終了すると実行が終わると言うことだった(本に書いてあった)。
ここでは /bin/bash がそれに当たる。シェルだからユーザが何もしないと生き続ける。 exit などで終了するとコンテナも終了する。

root@972d70ba7c5b:/# exit
exit
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS                      PORTS     NAMES
972d70ba7c5b   ubuntu    "/bin/bash"   14 minutes ago   Exited (0) 27 seconds ago             kind_austin

docker ps は結果が長くなって折り返して見づらいのだが、STATUS が Exited になっていることがわかる。

 

docker runのオプション

-i ... Keep STDIN open even if not attached
-t ... Allocate a pseudo-TTY

シェルを動かしているから、ターミナルを動かさないといかんし、入力させるから STDIN を開いたままにしないといかんし、ということかな。


イメージを自分で作るのはいずれやるとして、コマンドラインの使い方を覚えるのが面倒そうだから Dockerfile の書き方を先に調べよう。

Best practices for writing Dockerfiles | Docker Documentation
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

Dockerfile はイメージを自動的にビルドするためのテキストファイルと書いてあるので、実行を簡単にするものではない?
でも「contains all commands」と書いてあるので、なんとかなるのか?

$ cat Dockerfile
FROM ubuntu:20.04
CMD /bin/bash

こんな Dockerfileを作った。
CMD と RUN の違いがよくわからんが、RUN が事前準備で CMD が実行するメインプロセスだと思う。

$ docker build -t test:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM ubuntu:20.04
20.04: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:20.04
  ---> ba6acccedd29
Step 2/2 : CMD /bin/bash
  ---> Running in 4500cd01b011
Removing intermediate container 4500cd01b011
  ---> b3180f0f44d8
Successfully built b3180f0f44d8
Successfully tagged test:v1

これで終わりだ。
CMD で書いたものが実行されたりはしていない・・・? いや Step 2/2 でやろうとしているな。

サンプル通りにやってみよう。

$ cat Dockerfile
FROM ubuntu:20.04
COPY /hello.txt /
RUN cat /hello.txt

$ cat hello.txt
Hello, World!

hello.txt はあらかじめカレントディレクトリに作ってある。

$ docker build -t test:v1 .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM ubuntu:20.04
  ---> ba6acccedd29
Step 2/3 : COPY /hello.txt /
  ---> cbc39019824a
Step 3/3 : RUN cat /hello.txt
  ---> Running in 178ad7dff873
Hello, World!
Removing intermediate container 178ad7dff873
  ---> b66dc3af8f2a
Successfully built b66dc3af8f2a
Successfully tagged test:v1

おお、実行されているではないか!
シェルはちょっと特殊だと思うから、なんかオプションがあったような気もする。

docker run したのと違い、これで実行すると docker images で test というリポジトリが見えた。

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
test         v1        b66dc3af8f2a   14 minutes ago   72.8MB
< none>       <none>    9cdcb8314e60   16 minutes ago   72.8MB
< none>       <none>    b3180f0f44d8   20 minutes ago   72.8MB
ubuntu       20.04     ba6acccedd29   4 days ago       72.8MB

イメージを自動的に作るという、あれだろう。
サイズが全部同じなのは、これらは ubuntu という layer の上に全部載っかってるからということでよいのかな?
わからん。

 

まず、簡単に分かることからやろう。
さっきは RUN で cat したが、 CMD でもいいんじゃなかろうか。

$ docker build -t test:v1 .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM ubuntu:20.04
20.04: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:20.04
  ---> ba6acccedd29
Step 2/3 : COPY /hello.txt /
  ---> 4522c706bb17
Step 3/3 : CMD cat /hello.txt
  ---> Running in e50e77c5baa6
Removing intermediate container e50e77c5baa6
  ---> e71d4d3c2fe5
Successfully built e71d4d3c2fe5
Successfully tagged test:v1

うーん、Running と出力されているので、動いていないことは無いのだと思う。
こちら側の標準出力に出力させる理由がなかったとかだろうか。

RUN
https://docs.docker.com/engine/reference/builder/#run
RUN命令は、現在のイメージの上にある新しいレイヤーでコマンドを実行し、結果をコミットします。

CMD
https://docs.docker.com/engine/reference/builder/#cmd
CMDの主な目的は、実行中のコンテナーにデフォルトを提供することです。

CMDのデフォルトは、上で書いたメインプロセスと同じ意味だろう。
RUNの「結果をコミットします」は、イメージを作るときに実行結果もイメージに含めますよ、ということだろう。

$ docker run test:v1
Hello, World!

ほら、動いた。

2021/10/03

docker再びの再び (1) - 無料プランの話

2016年、2019年とdockerのことを勉強しては忘れる日々。

hiro99ma blog: docker ? (1)
https://blog.hirokuma.work/2016/08/docker-1.html

hiro99ma blog: docker ? (2)
https://blog.hirokuma.work/2016/08/docker-2.html

hiro99ma blog: Docker再び (1)
https://blog.hirokuma.work/2019/05/docker-1.html

hiro99ma blog: Docker再び (2)
https://blog.hirokuma.work/2020/03/docker-2.html

今も忘れた状態を維持している。
コマンドであれば、メモを残しておいてまねすれば良いのだが、dockerみたいなのはそうもいかないと思う。

いや、dockerもそういうレベルで落とし込まないと忘れるに決まっている。忘れないという前提で勉強するのに意味が無いことは、今までの記事でも分かる。

今シリーズは、なるべくそういう記事になるようがんばろう。


記事の前提だが、Linuxのみとする。
うちだとWindows10がホストで Virtual Boxに Ubuntuをインストールしていることが多い。今週には Windows11 がリリースされるようだが、うちのPCたちはどれも推奨環境を満たせなかった。ThinkPad T460sは期待したのだが、まさかCPUが引っかかるとは。。。
そういうのはともかく、Windows上での dockerではなく、Ubuntu上での dockerとする。

あとは、ハードウェアを操作するようなのはあまり考えないことにする。
VirtualBox でハードウェアとなると USBデバイスを動かしたいこともあるのだが、そういうのは無しだ。


こちらがGet Startedページ。

Get Started with Docker | Docker
https://www.docker.com/get-started

Windowsで見ているので、Docker Desptop(for Windows)と Docker Hubが見えている。
Ubuntuで動かすときにはコマンドしか使ったことがないのだが、そうなると Linux Engineというのがそれになるのか?

image

Linux Engineのページに行くと、Fedora, CentOS, AWS, Azure, Ubuntu, Debianが一覧に出てきた。

Explore Docker's Container Image Repository | Docker Hub
https://hub.docker.com/search?offering=community&operating_system=linux&q=&type=edition

ちなみに Desktop は、Windows と Macが 1つずつだ。

Dockerの利用が企業規模によっては有料になったという記事を見たのだが、2021年8月31日のブログに書いてあるのは Docker Desktopについてだけのように見える。

Docker is Updating and Extending Our Product Subscriptions - Docker Blog
https://www.docker.com/blog/updating-product-subscriptions/

No changes to Docker Engine」とも書いてあるので、Linux Engineを使うだけなら気にしなくてよいかもしれない。

 

ただ、Docker Hubというものについても今年アナウンスが出ていた記憶がある。
実のところ、Docker Hubのこともよく知らないのだが、Dockerのコンテナ?イメージ?を公開して置いておいたり、自分の開発しているものを置いておいたりできるようなものじゃなかったか。

ふだん、よく「dockerからコンテナを取ってきて」みたいな感じで、どこからか動かすものを取得しているのだけれども、あれは Docker Hubに公開してくれている誰かのリポジトリを使わせてもらっているのだと思っている。

とりあえず、1つアカウントを作ってみた。もちろん無料版だ。

image

このアンダーラインを引いたのが、Docker Hubに自分のリポジトリを持つことができる上限なのだろう。
「public」については unlimited としか書いていないが、設定を見るとこうなっていたので「private」は1つまでしか持てないのだろう。

image

お仕事で使うとなると、なんでもかんでも public にするわけにはいかんので、有料プランを選択するという選択もあるのだろう。


Dockerが、いや Docker社がここまで一般的に使用できる環境を提供し続けているというのは、非常に驚くべきことだと思っている。

デファクトスタンダードというか、dockerを利用しても環境縛りに思われないくらいの立ち位置にいるというのはすごい。
環境を作るということに特化しているのに「コンパイルはgccでやります」みたいなノリで「環境はdockerでつくります」になっているので、そのことに恐怖すら覚えるところだ。だいたい数年するといくつも同じようなサービスが立ち上がるものだけど、Dockerがこの立ち位置を維持できているのは不思議な気がする。

2021/09/04

[js]JSONっぽいデータをJavaScriptで読みたい

JSONのデータはこんな感じだ。

{
  "name": "Yoshio",
  "age": 92
}

 

JSONっぽいデータと書いたが、今回はこういうデータのことである。

{
  name: "Yoshio",
  age: 92
}

「key: value」と呼ぶとしたら、keyの部分にダブルクォーテーションがないタイプのデータである。
JavaScriptのコード中に書くようなデータと言えばよいか。

 

何でか知らんが、サーバがこういうデータを返してくるようなのだ。
それをJavaScriptで読んで処理をしたい。


一応取り込めたのだが、この方法はあまりよろしくないとさんざん書かれている。

const VALUE = "{name: 'Yoshio', age: 92}";
const result = eval(`(${VALUE})`);
console.log(JSON.stringify(result));

$ node ev.js
{"name":"Yoshio","age":92}

 

よろしくない理由は eval() を使うと危険なためである。

置き換えの例としてFunctionを使うよう書かれていた。

const VALUE = "{name: 'Yoshio', age: 92}";
const result = Function(`return ${VALUE}`)();
console.log(JSON.stringify(result));

$ node ev.js
{"name":"Yoshio","age":92}

 

このシンプルなデータだとFunctionで置き換えられたのだが、複雑になるとそうもいかないようなのだが、どうしたらよいかわからん。。。

2021/07/04

[js]import * は普通

前回、namespace にして export default して import させていた。
namespace は無しにして import * でも同じことはできるのだが、わざわざ namespace にしてみたのだ。

その理由は「ワイルドカードよりも特定の namespace を使った方がよいのではないか」という気持ちがあったからだ。ほら、不特定なものよりも特定した方が好まれるじゃないか。

しかし。。。

TypeScript: Documentation - Namespaces and Modules
https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html

太字で

we recommended modules over namespaces in modern code.

最新のコードでは、名前空間よりもモジュールを推奨しました。
(Google翻訳)

と書いてある。
「最新のコードでは」だから将来変わるのかもしれんが、2021/07/04 19:23 ではそうなっていた。

 

なので、ワイルドカードで指定しているとかそういうのはあんまあり考えなくて良いんだろうね。変に気を回して時間を掛けてしまったよ。