2021年11月21日

[golang] go.mod の pseudo-version の調べ方がわからん

わからんのだ。。。

 

golang で go.mod を作って require にて自分の GitHub リポジトリにあるコードを使う場合、 pseudo-version を書くのだろうと思っている。

https://golang.org/ref/mod#pseudo-versions

フォーマットはこんな感じ。

v0.0.0-<YYYYMMDDhhmmss>-<commit-id>

私が書くときはこういう順番でやっている。

  1. GitHub などで使いたい commit-id を調べる
  2. “YYYYMMDDhhmmss”のところは適当な日時にしておく
  3. “commit-id”のところに調べたcommit-idを書く
  4. go mod tidy
  5. commit-id の桁が長すぎると怒られるので、エラーメッセージに出力された文字列に置き換える
  6. go mod tidy
  7. タイムスタンプが違うといわれるので、エラーメッセージに出力された文字列に置き換える

commit-id は先頭から12文字分とってくればよいので、まだなんとかなると思うのだがタイムスタンプが分からんのだ。
コミットした日時をUTC変換すれば良いのだけど、GitHub だと秒数が出てこない。
clone してくればよいのだけど、めんどくさい。clone するくらいだったら↑でやってしまえばいいや、ということで今に至っているのであった。

go mod edit だとうまいことやってくれないかと期待したのだが、-require は少なくとも書いたとおりに go.mod に書くだけだった。
んー、大した作業ではないのだけど、なんかきれいにできないものか。

[golang][grpc]protocの準備

golang で gRPC をやりたい。

ということは、protobuf がいるので、protoc がいる。
準備しよう。


インストール手順があった。

Protocol Buffer Compiler Installation | gRPC
https://grpc.io/docs/protoc-installation/

これを書いている時点での最新版は v3.19.1。

https://github.com/protocolbuffers/protobuf/releases/tag/v3.19.1

私が動かしている Ubuntu にも protoc はインストールされていたのだが、あんまり新しくない。

$ protoc --version
libprotoc 3.6.1

GitHub の方からコンパイル済みの zipファイルをダウンロードして、$HOME/.local に展開する。

$ protoc --version
libprotoc 3.19.1

新しくなった。


golang で使うには protobuf compiler だけでは足りない。
というか、protoc はプラグインで各言語向けのソースを吐き出してくれるので、その準備がいる。

golang の場合は楽だ。

Quick start | Go | gRPC
https://grpc.io/docs/languages/go/quickstart/

今見ると、 protoc-gen-go@v1.26 と protoc-gen-go-grpc@v1.1 になっているが、protoc-gen-go の latestは v1.27.1 だった(protoc-gen-go-gprc の latestはv1.1.0だった。)。
v1.27.1 にした方がよいのかな? メジャーバージョンアップでもないし新しい方がよいかもしれん。困るまで v1.27.1 にしてみよう。
GOPATH は好きに設定すれば良いが、プロジェクトごとに GOPATH を設定しているならどこかにまとめておいた方がよいだろう。

 

Quick Start の順番にやっているので、次は example だ。
今は grpc-go の v1.41.0 を clone するよう書かれているが、最新は v1.42.0 だったのでそちらにした。

まずサーバ側を起動。

$ go run greeter_server/main.go
go: downloading github.com/golang/protobuf v1.4.3
go: downloading google.golang.org/protobuf v1.25.0
go: downloading golang.org/x/net v0.0.0-20200822124328-c89045814202
go: downloading google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98
go: downloading golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
go: downloading golang.org/x/text v0.3.0
2021/11/21 13:21:11 server listening at [::]:50051

別のターミナルからクライアントを起動すると、たぶんサーバ側と通信している。

$ go run greeter_client/main.go
2021/11/21 13:22:03 Greeting: Hello world

こちらはサーバ側の出力。

2021/11/21 13:22:03 Received: world

うん、なんというか、protoc などを自分でやっていないので、gRPC したって気分になれないね。
というわけで、その次の gRPCサービスの更新をやろう。


さて、ファイルの構成はこんな感じ。

image

サーバとクライアントにそれぞれ main.go があって、あとは protoファイルとコンパイルされた pb.go と _grpc.pb.go がある。
ただの pb.go の方は protobuf のシリアライズ/デシリアライズの処理、_grpc.pb.go の方は gRPCのサーバ/クライアントの処理になっているそうだ(basics tutorial)。

あまり深く考えず、proto ファイルに SayHelloAgain()を追加しよう。
そして protoc でコンパイル。
--go_ が pb.go で --go-grpc_ が _grpc.pb.go になる。paths=source_relative は相対パスの場所にファイル生成する。

protoc でコンパイルできたら、それぞれの main.go を変更して同じように実行させると SayHelloAgain が動いていることが確認できるだろう。


ツールとしてはこのくらいあれば golang で gRPC できることがわかった。
あとは・・・知識がいるだけである。

[golang, github]go.modのmoduleを実物と違うのにしてよいのか

go.mod に慣れていないせいかもしれないが(golang自体にも慣れていないが)、非常に難しく感じる。

文句を言っても仕方ない(か、golangのプルリクを出すとか)ので、理解に努めよう。

$ go version
go version go1.17.3 linux/amd64


golang でオリジナルのものを作るときは気にならないかもしれないが、GitHub で fork したり mirror したりして一部を書き換えたいとなると思考が止まってしまう。

 

よくあるのが、git cloneする中でも改造して使いたいのはその一部だけ、というパターンだろう。
例として、fmt.Printf するだけのリポジトリを用意した。

https://github.com/hirokuma/gogo-test1/tree/v1

main.go と gogo/gogo1.go だけだ。

ここの gogoパッケージにある Gogo() だけ使うリポジトリがこちら。

https://github.com/hirokuma/gogo-test2/tree/v1

特に変な動作はしない。

go.mod は自分で記述したわけではなく、 go tidy で勝手に追加されたように思う。

それはよいのだが、gogo-test2 の方を go build . でビルドすると gogo-test2 というバイナリファイルができあがる。
これはなぜかというと、go.mod の module がそういう名前になっているからだ。
もしここで、オリジナルと同じ gogo-test1 という名前にしたくて go.mod の module を github.com/hirokuma/gogo-test1 にすると go build . は失敗する。

$ go build .
main.go:6:2: no required module provides package github.com/hirokuma/gogo-test1/gogo; to add it:
        go get github.com/hirokuma/gogo-test1/gogo

main.go:6 は import "github.com/hirokuma/gogo-test1/gogo" している箇所だ。

ではオリジナルと同じ名前のバイナリファイルを作るのには go build だけで無理なのかというとそうでもない。
あまり理解できていないのだが module path とか module directory という考え方があるようで、うーん、後ろに v2 とか書けばいけるのかな?

https://github.com/hirokuma/gogo-test2/blob/v2/go.mod#L1

いけた。

自分より前に v2 を使っている人がいたら私が v3 にしないといけないかというと、たぶんそういうことではないはずだ。調べるのは無理だろうし。
もし v2 でダメになるのはオリジナルが v2 などにした場合だろう。

 

さて、 gogo-test2 から gogo-test1/gogo にある Gogo() を呼び出していたが、自分で gogo.Gogo() を定義し直したいとしよう。

https://github.com/hirokuma/gogo-test2/blob/v3/gogo/gogo1.go

これを実行すると、gogo-test1 の Gogo() が呼び出される。

$ go run .
gogo-test2!
gogo!

なぜかというと、gogo-test2/main.go で import しているのが gogo-test1/gogo だからだ。
では、go.mod で置き換えてみよう。

https://github.com/hirokuma/gogo-test2/blob/v4/go.mod

うまくいくかと思ったが、これではダメだった。

$ go run .
gogo-test2!
gogo!

main.go の import を変更すればよいと思うのだが、本体の変更をせずに go.mod だけで管理できるようになっていると思うのだよ、私は。

難しいねぇ。続きは明日だ。


明日になった。続けよう。
ここまでを整理しておこう。

gogo-test2/v3 から追加した gogo/gogo1.go を main.go で参照させたいのだが、うまくいっていない。
gogo-test2/v4 で replace を追加したのだが、やっぱり参照してくれない。
もちろん、main.go の import を gogo-test2 に変更すれば参照する。

あれ、もしかしたらそれで問題ないのか?
そもそも gogo を自分で実装したものを使う時点で gogo-test1 とは何の関係もなくなっているのだ。
なので、関係をなくした v5を作った。

https://github.com/hirokuma/gogo-test2/compare/v4...v5

これを gogo-test1 の人が見て、あら私もこっちの gogo を使ってみたいわ、と思ったらこうなる。

https://github.com/hirokuma/gogo-test1/compare/v1...v2

うーん、これでよいような気もする。

 

 

では、gogo-test2 の module を yoshio に変更してみよう。

https://github.com/hirokuma/gogo-test2/compare/v5...v6

go.mod を変更すると main.go の import も変更しないといけなかった。
しかし、これだと git clone しないといけないのだったら gogo-test2.git だとわからないんじゃなかろうか?

gogo-test1/v2 の go.mod を gogo-test2/v6 に変更したらやはりエラーになった。

$ go get
go: github.com/hirokuma/gogo-test2@v0.0.0-20211121012830-b239fb1fd1ae: parsing go.mod:
        module declares its path as: github.com/hirokuma/yoshio
                but was required as: github.com/hirokuma/gogo-test2

こういう場合も replace で対応するようだ。

https://github.com/hirokuma/gogo-test1/compare/v2...v3

なので、module と実物のパスが異なってもなんとかなりそうだ。

2021年11月20日

[git, golang]GitHubのorganizationのprivateリポジトリをgo getする

golang で作業している。
お仕事なのでプライベートリポジトリなのだが、場所が個人ではなくorganizationにある。

さて go getしたいのだが、organization には Personal Access Token (PAT)はない。
どうしたらよかろうか?

$ go version
go version go1.17.3 linux/amd64


検索して出てきたのは2つの方式だった(他にもあると思うけど)。

  • PATでアクセス
  • SSHにする

PAT方式で出てきたのはこちら。

https://zenn.dev/shootacean/articles/go-get-from-github-private-repository

organizationだからPATはないと書いたが、ここでは個人アカウントのPATを使う。
つまり、個人アカウントだろうとorganizationだろうと気にするなということだ。

これでできた。
GOPRIVATEは設定しなくても特にエラーは出なかった。

 

SSH方式で出てきたのはこちら。

https://gist.github.com/magnetikonline/3ceec9ece11375393ab5781d9370ee99

こちらはダメだった。
git clone で https://github.com で始まるリポジトリ名を打ち込むと SSHのパスフレーズを要求されたのでできるかと思ったのだけど、go getの方は「git@github.com: Permission denied (publickey)」としばしば見かけるエラーになった。

表示しているコマンドもcloneしているだけだし、そのまま打ち込んでもやっぱりパスフレーズ要求になったから、stdinを止められてるとかでダメなんだろうか。

~/.netrc で入力を回避するやり方も出ていたので、それと併用するのかもしれんが、試してはいない。

 

というわけで、「GitHubのorganization のプライベートリポジトリを go get するとき、個人アカウントのプライベートリポジトリをPATを使って go getするやり方と同じで取得できた」ということにしておく。

 

GOPRIVATEがいるのは、おそらく go.mod の中にプライベートリポジトリがある場合だと思う。
go get してエラーになるようだったら、GOPRIVATEに github.com/xxx みたいなのを設定すればできた。
全部同じ organizationのプライベートリポジトリだったのだけど、複数の organization にまたがってかつプライベートリポジトリとかだとわからんね。そんなことをするなってことだろうけど。

2021年10月31日

docker再びの再び (5)

docker のネットワークについて勉強中。
前回はドライバの bridge で、今回は host だ。

Networking using the host network | Docker Documentation
https://docs.docker.com/network/network-tutorial-host/


ホストとコンテナの分離を削除する、という表現で混乱したのだが、ホストからコンテナのネットワークにアクセスできるということだと思っている。
アクセスだけなら ping でもできていたのだが、ここではもっとアプリ層に近いところだ。

チュートリアルでは nginx (えんじんえっくす、と読むのを知ったのはずいぶん最近のことだ)のコンテナを立ち上げて、ホスト側のブラウザで 80番ポート(デフォルトの http)にアクセスしている。

この手順では、ポート番号が出てきていないのだが、コンテナの外からホスト環境で nginx が立ち上がっているかのようにアクセスできるのだ。私の場合、Windows10 から VirtualBox で Lubuntu をゲストOS として起動し、ゲストOS 上で docker を立ち上げているのだが、Windows10 側から Lubuntu の IPアドレスをブラウザで指定してもアクセスできる。
実体はコンテナ側で動いているので、ポート番号だけ間借りしているというか転送しているというか、そんな感じなのか。


さて、 デフォルトの bridge を使うと他のコンテナやホストの間で通信ができ、ユーザ定義の bridge を使うと特定のコンテナの間で通信ができた。 1つのコンテナが デフォルトとユーザ定義の bridge の両方に接続?することができるので、そうするとどっちにもアクセスできた。

じゃあ、host とか使わずにデフォルトの bridge を使ってもいいんじゃ無いかと思ったのだが、これだと ss -nat | grep :80 などとしても 80番ポートが見当たらない。
docker ps で見ると PORTS に 80/tcp と出てくるので、80番ポートが使われているのは間違いないだろう。
しかし、 host を使った場合に docker ps で見ても PORTS には何も出てこない。

 

これだけだと bridge の意味がよくわからんのだが。。。
と思ったら、ブラウザでコンテナの IPアドレスにアクセスすると ss コマンドで現れた。 Listen していないのにアクセスできる・・・?
いや 80番ポートで Listenしているわけではないというだけかもしれない。
ss -l | grep docker とすると6つくらい出てきたので、今はそういうことにしておこう。


私の今のイメージでは、bridge と host はこんな感じだ。
丸いのがコンテナね。

image

  • bridge に名前を付けて作成すると、それと接続しているコンテナ間で LANを形成できる。
  • コンテナは複数の bridge に接続できる
  • host を指定するとホスト環境のネットワークとして見える

bridge と host も同時にできるかと思ったが、それはエラーになった。

bridge接続済みのコンテナを host につなごうとしたとき
container cannot be disconnected from host network or connected to host network

host接続済みのコンテナを bridge につなごうとしたとき
container sharing network namespace with another container or host cannot be connected to any other network

ホストのネットワークなら bridge のコンテナ内からでもアクセスできるので必要ないのだろう。

Connect a container to a user-defined bridge
https://docs.docker.com/network/bridge/#connect-a-container-to-a-user-defined-bridge

--publish を使うことで特定のポートをホスト側に見せることができる。だから host を使わなくてもよいというか、なるべく bridge を使って公開するポートを限定した方が不安が少ないと思う。


他にもいろいろあるだろうが、テスト環境を作るのであればこのくらいの知識でなんとかなるんじゃなかろうか。

2021年10月30日

docker再びの再び (4)

4回もやっていると何でこんなタイトルだったのか忘れてしまいそうになるが、今回で dockerを勉強するのが3回目になるからだった。


今回はネットワークについて。

Docker コンテナ・ネットワークの理解 — Docker-docs-ja 19.03 ドキュメント
http://docs.docker.jp/engine/userguide/networking/dockernetworks.html

日本語だ。
多少情報が古いかもしれないが、基本的なことは変わってないんじゃなかろうか。まあ、後で調べよう。

 

ifconfig で見えるのは docker0 だけだが、docker コマンドでは書いてあるように 3つ見える。

$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
cc02564a80e1   bridge    bridge    local
4dee0c98d161   host      host      local
51c798d9077c   none      null      local

うーん、少し読んだけどさっぱりわからんよ。
もっと概要から説明してほしいのだが。

Networking overview | Docker Documentation
https://docs.docker.com/network/

Dockerのnetworkingサブシステムは、driver を使って追加/削除できる(プラグイン方式)らしい。
デフォルトで存在したりコアネットワークとして提供されているドライバはこれら。

  • bridge
  • host
  • overlay
  • macvlan
  • none

さっきの network ls で出てきたものでも DRIVER として表示されているな。

 

bridge

これがデフォルトのネットワークdriver。
固有名詞じゃなさそうだから「ドライバ」でよいのかな。
どういう意味でデフォルトかというと、普通はこれが使われる、というデフォルトのようだ。そりゃそうだろうと言われそうだが「デフォルトで付属している(けどあまり使われない)」みたいなこともあるので気にしたのだ。

名前からすると、docker コンテナ内のネットワークとリアル環境のネットワークを橋渡ししてくれるのだろうか。
なんというか、そうだったら bridge 以外はいらないような気がするのだが、他は何をするんだろう?

 

host

   うーん?

For standalone containers, remove network isolation between the container and the Docker host, and use the host’s networking directly.
スタンドアロンコンテナの場合、コンテナとDockerホスト間のネットワーク分離を削除し、ホストのネットワークを直接使用します。

通常はコンテナとホストが分離されているけど、それを削除するということは、分離しなくなるということか?

 

overlay

複数の docker daemonをそれぞれ接続し、 swarm service?がそれぞれ通信できるようにする もの らしい。
うん、よくわからん。

1台の PCでは、docker daemonは 1つ動いているということになるんだろうか。
bridge と host では「standalone containers」と書いてあったのだが、それぞれのコンテナで docker daemonが 1つ動いているという扱いにするのなら、これがコンテナ間を直接つないでくれるのかもしれん。
swarm というのは知らん。後回し。

 

macvlan

MacOS 専用のドライバかと思ったが、MACアドレスの方を表しているようだ。
MACアドレスをコンテナに割り当てることができるらしい。ということはマシン外とアクセスするときに使えるということだろうか。それに MACアドレスということなら物理層とか下の方の階層になるはずなので他のドライバと併用できるとかだろうか。

まあ、用途として特殊になりそうだから後回しでいいかな。

 

none

host が外部と接続しないようにするドライバだったが、 none はネットワーク接続をしないというドライバらしい。
ネットワークドライバなのにネットワークに接続しないとは、なんか面白いものだ。

カスタムネットワークドライバと一緒に使うのが普通らしい。
よくわからんが、後回しで良い気がする。ただ、初期設定で動作しているのでよく使うものなのかも。

 

 

説明の中で「standalone containers」という言葉がしばしば出てくる。
スタンドアローンなのにコンテナは複数形なのだ。だから、スタンドアローンのコンテナが複数あって、それを bridge とかで接続するとか、それぞれが host とかで自分の中だけで運用するとか、そういう話なんじゃなかろうか。

あと、「swarm service」という言葉も出てくる。

【swarm】
群れ、群衆。群がる。

swarm mode というページがある。

Current versions of Docker include swarm mode for natively managing a cluster of Docker Engines called a swarm.
Dockerの現在のバージョンには、スウォームと呼ばれるDockerエンジンのクラスターをネイティブに管理するためのスウォームモードが含まれています。

クーベネイティス、だったか、複数のコンテナを使ったシステムをうまいこと立ち上げるのがあったと思うが、それで使うのだろうか? わからんことは後回しだ。


Networking with standalone containers | Docker Documentation
https://docs.docker.com/network/network-tutorial-standalone/

bridge ドライバがデフォルトだとあるが、ここでは「デフォルト bridge ネットワーク」と「ユーザ定義 bridge ネットワーク」の2つが書かれている。ドライバとは書いていないので別の話なのか。
デフォルトの bridge ネットワークは製品版には推奨しないそうだ。

このチュートリアルではどちらも見せてくれるそうなので、やってみるのが良かろう。

 

まず、デフォルトの方。
特に何か特別なことをするわけでもなく、コンテナを2つ立ち上げると IPアドレスがそれぞれホストとは異なるプライベートアドレスが割り当てられた(ホストは 192.168.0.0 だが、コンテナは 172.17.0.0 だった)。
そのコンテナの中からグローバルなIPアドレスにアクセスできるし、相手のコンテナにもアクセスできる。

そういえばホストにもアクセスできるのかは確認していなかった。
docker stop でコンテナを終わらせた状態なのだが「再開」はできるのだろうか? チュートリアルと同じ docker run のコマンドを打ち込んでも名前が既にあるとエラーになった。
コンテナを削除すれば良いそうだが、単に再開させるだけなら docker start でよいようだ。

で、コンテナの中からホストに向けて ping しても成功したし、ホストからコンテナに向けて ping しても成功した。
私の記憶では、ネットワークのことをやるのにポート番号をどうのこうの設定していた気がするのだが... まあ ping だからポート番号は関係ないというだけで、後から出てくるのか。
わからん。

 

次はユーザ定義の方。
docker network create で bridge をベースにして(?)新たなドライバを作っているのか。
この方式だと、ping 先としてコンテナ名を使うことができる(bridge の場合は IPアドレスだけだった)。

デフォルトがなんで製品に向かないかというと、コンテナを立ち上げてしまうだけでネットワークにアクセスできてしまうからだと思う。他にも理由はあるとは思うが、システムごとに network create して作るのが無難だろう。


長くなったので、Hostのチュートリアルは次回にしよう。

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月3日

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

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

hiro99ma blog: docker ? (1)
https://hiro99ma.blogspot.com/2016/08/docker-1.html

hiro99ma blog: docker ? (2)
https://hiro99ma.blogspot.com/2016/08/docker-2.html

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

hiro99ma blog: Docker再び (2)
https://hiro99ma.blogspot.com/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年9月4日

[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で置き換えられたのだが、複雑になるとそうもいかないようなのだが、どうしたらよいかわからん。。。