- 投稿日:2021-01-13T23:04:22+09:00
glibc, musl libc, go の resolver の違い
先日、resolv.conf で timeout を調整したいなと思うことがありました、しかし、Docker だの Kubernetes だのといった時代です。Linux しか使っていなかったとしても resolver が glibc のそれだとは限らないわけですね。そこで glibc, musl libc (alpine のやつです), go の resolver の違いを調べてみました。
環境
それぞれ docker container を使い、問い合わせの状況は tcpdump で確認しました。
macOS Catalina (10.15.7) docker desktop 3.0.3 (51017)container image は
alpine:3.12.3 / musl-1.1.24-r10 debian:buster-20201209 (10.7) / glibc 2.28-10Go は mac 上で 1.15.6 を使い、クロスコンパイルして上記の alpine 上で実行しました。
クロスコンパイルなのでデフォルトでCGO_ENABLED=0
のはずですが、明示してビルドしました。resolv.conf の options などの対応状況
resolv.confの例nameserver 8.8.8.8 nameserver 8.8.4.4 search default.svc.cluster.local svc.cluster.local cluster.local asia-northeast1-b.c.project-id.internal c.project-id.internal google.internal options ndots:5 timeout:5 attempts:2resolv.conf の options にはいろいろありますが、今回の 3 つの resolver 全てで対応されているのは
ndots
timeout
attempts
の3つでした。
nameserver
は勿論ですがsearch
にも対応しています。
昔の musl libc はsearch
(domain 補完) には対応していませんでしたが 1.1.13 から対応していました。
nameserver
は複数回指定可能ですが、使われるのは3つまでです。4つ以上指定しても3つまでしか使われません。それでは機能の違いを見ていきましょう
ndots
まずは
ndots
です、Kubernetes を使うようになってはじめて知りました。指定するのは問い合わせるドメインに含まれる dot (
.
) の数です、Kubernetes のデフォルト設定では 5 (ndots:5
) となっています。例えば
ping www.google.com
とした場合、dot の数は 2 です。dot の数が ndots 以上の場合
dot の数が
ndots
で指定した以上であればそれは FQDN であろうと判断し、search
で指定したドメインを補完せずに DNS サーバーに問い合わせます。ここで、NXDOMAIN
(そんなドメイン存在しないよ) が返ってきたら glibc と go はsearch
で指定したドメインを補完して問い合わせます。しかし、musl libc は補完した問い合わせを行わないでそんなドメインは存在しないよということで終了してしまいます。その昔、musl はそもそもドメイン補完に対応していなかったので当時から使っている人は常に FQDN で指定しているでしょうから問題ないでしょうが、知らないで
ndots
を小さくすると解決できるはずのドメインが解決できないかもしれません。dot の数が ndots 未満の場合
dot の数が
ndots
未満であった場合はまずsearch
で指定した複数のドメインを順番に補完して問い合わせます、見つかるまで繰り返すため(これの上限を確認するの忘れた)、順序は重要ですかね。ただ、順序以上にndots
で指定する数が重要です、5
というのは結構多いです。Kubernetes でデフォルト設定だとservice.namespace.svc.cluster.local
という FQDN を指定したとしても4
なわけです。search
に並んでいる全てのドメインの補完を試した後にservice.namespace.svc.cluster.local
をそのまま問い合わせてやっと欲しい結果が得られるわけです、DNS サーバー側にネガティブキャッシュがあるとはいえ、DNS サーバーとの通信はその回数行われるわけですから、かなりの無駄です。GKE のデフォルト設定では6つもドメインが列挙されています。EKS は4つ。ndots
を小さくして名前解決ができないドメインが発生してしまうよりも、無駄な問い合わせが増えたとしても名前解決できる値がデフォルトなっているわけですね。ndots
はチューニングの余地がありそうです。ちなみに、末尾に
.
をつけた場合はそれは FQDN だぞということを意味するのでping www.google.com.
などとした場合はどの resolver でもドメインの補完は行いません。nameserver
複数サーバーを指定した場合の扱いが glibc と musl で大きく異なるため、まず説明しておきます。
glibc と Go は一つ目のサーバーに問い合わせて、timeout 秒待っても応答がない場合に、次のサーバーに問い合わせます。
musl libc は複数の nameserver に同時にリクエストを送って最初に返ってきた応答を使います。3つのネームサーバーが指定されていたら3倍の問い合わせが発生してしまうわけですね。timeout と attempts
timeout
とattempts
は関連しているため一緒に扱います。また、nameserver
の数にもよるためそれごとにまとめます。nameserver が1つの場合
glibc と Go は問い合わせを投げては
timeout
秒待ち、応答がなければ再度問い合わせて、合計attempts
回問い合わせます。musl libc は
timeout
秒をattempts
で割った秒数をそれぞれの問い合わせで待ちます。つまり、リトライを含めた合計で最大timeout
秒待つということです。timeout:5 attempts:2
というデフォルト設定では 2.5 秒ずつ待ちます。nameserver が2つの場合
glibc と Go は1つ目の
nameserver
に問い合わせてtimeout
秒待ち、次は2つ目のnameserver
に問い合わせます。これがattempts
の1回分です。timeout:5 attempts:2
というデフォルト設定ではそれぞれのnameserver
に対して2回ずつ、合計4回問い合わせます。その都度5秒待ちます。musl libc は
nameserver
の項で書いた通り、複数のnameserver
に対して同時に問い合わせるため、nameserver
が1つの場合と同じです。nameserver が3つの場合
nameserver
が3つになると glibc のtimeout
の振る舞いが少し変わります。1つ目のnameserver
に対してはtimeout
で指定した秒数待ちますが、2つ目はtimeout
で指定したのよりも短くなり、3つ目はtimeout
で指定したものより長くなります。
timeout:5
の場合は5秒、3秒、6秒となります。このセットをattempts
回繰り返します。Go は常に
timeout
秒待ちます。musl libc はこれまでと同じです。
timeout 時のドメイン補完
何度問い合わせても timeout するような場合はもういろいろダメなのであまり重要ではないですが、動作に違いがあったので書いておきます。
timeout が続く状態で、2つ目以降の search ドメインを補完するのかどうか、glibc は2つ目以降のドメイン補完は行いませんが、補完なしの問い合わせを行います。Go は律儀に全てのドメイン補完を試しますし、補完なしでの問い合わせもします。musl libc は1つ目の補完の問い合わせは行いますが、それで終わりです。補完なしの問い合わせもありません。
Go の動作確認で使ったコード
package main import ( "context" "net" "os" "log" ) func main() { ctx := context.Background() resolver := &net.Resolver{} for _, v := range os.Args[1:] { log.Printf("Resolving %s\n", v) names, err := resolver.LookupHost(ctx, v) if err != nil { log.Fatal(err) } for _, name := range names { log.Printf("%s\n", name) } } }ndots:5 の凶悪さを可視化する
文章でつらつら書いても分かりづらいので、 GKE 環境で
storage.googleapis.com
にアクセスしようとした場合にどんな問い合わせが発生するかを見てみましょう。default
namespace に立てた Pod でping storage.googleapis.com
を実行した際の tcpdump の出力を加工しました。(横幅を抑えるために)Cloud Storage の API にアクセスしようとする度にこの数の問い合わせが発生すると思うとゾッとしますね。マイクロサービスで Keep-Alive などをしていない場合は大量の内部通信でもこれが発生しているかもしれません。JVM などのように DNS のキャッシュをしてくれれば良いですけどね。
ndots:2
として常に FQDN で指定するということが徹底できると良いのかな。14:53:34.706909 ▶︎ A? storage.googleapis.com.default.svc.cluster.local. (66) 14:53:34.707130 ▶︎ AAAA? storage.googleapis.com.default.svc.cluster.local. (66) 14:53:34.708881 ◁ NXDomain 0/1/0 (159) 14:53:34.708940 ◁ NXDomain 0/1/0 (159) 14:53:34.709051 ▶︎ A? storage.googleapis.com.svc.cluster.local. (58) 14:53:34.709133 ▶︎ AAAA? storage.googleapis.com.svc.cluster.local. (58) 14:53:34.709615 ◁ NXDomain 0/1/0 (151) 14:53:34.709689 ◁ NXDomain 0/1/0 (151) 14:53:34.709771 ▶︎ A? storage.googleapis.com.cluster.local. (54) 14:53:34.709816 ▶︎ AAAA? storage.googleapis.com.cluster.local. (54) 14:53:34.712211 ◁ NXDomain 0/1/0 (147) 14:53:34.712280 ◁ NXDomain 0/1/0 (147) 14:53:34.712387 ▶︎ A? storage.googleapis.com.asia-northeast1-b.c.my-project-id.internal. (83) 14:53:34.712479 ▶︎ AAAA? storage.googleapis.com.asia-northeast1-b.c.my-project-id.internal. (83) 14:53:34.716561 ◁ NXDomain 0/1/0 (189) 14:53:34.716623 ◁ NXDomain 0/1/0 (189) 14:53:34.716718 ▶︎ A? storage.googleapis.com.c.my-project-id.internal. (65) 14:53:34.716760 ▶︎ AAAA? storage.googleapis.com.c.my-project-id.internal. (65) 14:53:34.719891 ◁ NXDomain 0/1/0 (162) 14:53:34.720191 ◁ NXDomain 0/1/0 (162) 14:53:34.720304 ▶︎ A? storage.googleapis.com.google.internal. (56) 14:53:34.720390 ▶︎ AAAA? storage.googleapis.com.google.internal. (56) 14:53:34.724145 ◁ NXDomain 0/1/0 (145) 14:53:34.724352 ◁ NXDomain 0/1/0 (145) 14:53:34.724458 ▶︎ A? storage.googleapis.com. (40) 14:53:34.724500 ▶︎ AAAA? storage.googleapis.com. (40) 14:53:34.726930 ◁ 4/0/0 AAAA 2404:6800:4004:813::2010, AAAA 2404:6800:4004:81c::2010, AAAA 2404:6800:4004:81d::2010, AAAA 2404:6800:4004:81e::2010 (152) 14:53:34.726957 ◁ 16/0/0 A 172.217.161.80, A 172.217.175.16, A 172.217.175.48, A 172.217.175.80, A 172.217.175.112, A 216.58.197.144, A 172.217.25.208, A 172.217.25.240, A 172.217.26.48, A 172.217.31.176, A 172.217.161.48, A 172.217.174.112, A 172.217.175.240, A 216.58.220.112, A 216.58.197.208, A 216.58.197.240 (296)
- 投稿日:2021-01-13T22:44:11+09:00
Golang製PHPアプリケーションサーバRoadRunnerでLaravel-adminを動かす
アプリケーションサーバーの新潮流
昨今のPHP界隈では、Swooleに代表されるように、よくあるApache+mod_phpやPHP-FPMによる従来の構成と異なる実行方法を持つPHPのためのHTTPサーバまたはアプリケーションサーバが少しずつ注目を集めるようになっています。
RoadRunner
RoadRunnerもそんな新興のアプリケーションサーバの1つで、Go言語で書かれています。 HTTPのリクエストを前段のGoのHTTPハンドラがさばき、ロードバランサ/プロセスマネージャがPHPのWorkerにリクエストを割り振るという構成になっています。
通常のHTTPサーバだけでなく、RoadRunnerによるgRPCサーバの実装も公開されており、PHPの新しい可能性を見せてくれています。
RoadRunnerを試す
RoadRunnerはLaravelもサーポートしており、Laravel用のパッケージも開発されているので
今回はこれをベースにRoadRunner実装していきたいと思います。
RoadRunnerのインストール
まずは下記コマンドでパッケージのインストールを行います。
# roadrunner composer require spiral/roadrunner "^1.8" # roadrunner-laravel composer require spiral/roadrunner-laravel "^3.4"次に、下記コマンドを実行してパッケージ構成ファイル(./config/roadrunner.php)を作ります。
php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config.rr.ymlの作成
roadrunnerの設定ymlファイルを書きます。
80番ポートで起動するように設定します。
env: APP_REFRESH: true http: address: 0.0.0.0:80 http2: enabled: true h2c: true maxConcurrentStreams: 128 workers: command: 'php ./vendor/bin/rr-worker' static: dir: public reload: interval: 1s patterns: [ '.php' ] services: http: dirs: [ '' ] recursive: true諸々の微修正
logging.php
'stdout' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'with' => [ //php://stdoutにするとエラーになってコケるのでphp://stderrに変える 'stream' => 'php://stderr', ], 'level' => 'debug', ],composer.jsonにscriptを追加する。
"scripts": { "roadrunner:dev": [ "composer dump-autoload", "./rr serve -v -d" ], "roadrunner:prod": [ "./rr serve" ], "roadrunner:setup": [ "rr get-binary" ], }startup.shに起動コマンドを乗せる
# roadrunner composer roadrunner:setup chmod +x rr composer roadrunner:devこれで大体の設定は完了しました。
起動してみる
以下の手順で起動させます。
cp docker-compose.example.yml docker-compose.yml docker-compose up -d無事に起動できました。
まとめ
今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。参考文献
- 投稿日:2021-01-13T22:44:11+09:00
Golang製PHPアプリケーションサーバRoadRunnerを試す
アプリケーションサーバーの新潮流
昨今のPHP界隈では、Swooleに代表されるように、よくあるApache+mod_phpやPHP-FPMによる従来の構成と異なる実行方法を持つPHPのためのHTTPサーバまたはアプリケーションサーバが少しずつ注目を集めるようになっています。
RoadRunner
RoadRunnerもそんな新興のアプリケーションサーバの1つで、Go言語で書かれています。 HTTPのリクエストを前段のGoのHTTPハンドラがさばき、ロードバランサ/プロセスマネージャがPHPのWorkerにリクエストを割り振るという構成になっています。
通常のHTTPサーバだけでなく、RoadRunnerによるgRPCサーバの実装も公開されており、PHPの新しい可能性を見せてくれています。
RoadRunnerを試す
RoadRunnerはLaravelもサーポートしており、Laravel用のパッケージも開発されているので
今回はこれをベースにRoadRunner実装していきたいと思います。
RoadRunnerのインストール
まずは下記コマンドでパッケージのインストールを行います。
# roadrunner composer require spiral/roadrunner "^1.8" # roadrunner-laravel composer require spiral/roadrunner-laravel "^3.4"次に、下記コマンドを実行してパッケージ構成ファイル(./config/roadrunner.php)を作ります。
php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config.rr.ymlの作成
roadrunnerの設定ymlファイルを書きます。
80番ポートで起動するように設定します。
env: APP_REFRESH: true http: address: 0.0.0.0:80 http2: enabled: true h2c: true maxConcurrentStreams: 128 workers: command: 'php ./vendor/bin/rr-worker' static: dir: public reload: interval: 1s patterns: [ '.php' ] services: http: dirs: [ '' ] recursive: true諸々の微修正
logging.php
'stdout' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'with' => [ //php://stdoutにするとエラーになってコケるのでphp://stderrに変える 'stream' => 'php://stderr', ], 'level' => 'debug', ],composer.jsonにscriptを追加する。
"scripts": { "roadrunner:dev": [ "composer dump-autoload", "./rr serve -v -d" ], "roadrunner:prod": [ "./rr serve" ], "roadrunner:setup": [ "rr get-binary" ], }startup.shに起動コマンドを乗せる
# roadrunner composer roadrunner:setup chmod +x rr composer roadrunner:devこれで大体の設定は完了しました。
起動してみる
以下の手順で起動させます。
cp docker-compose.example.yml docker-compose.yml docker-compose up -d無事に起動できました。
まとめ
今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。
- 投稿日:2021-01-13T22:28:01+09:00
docker-composeで立ち上げたコンテナに外から通信する
背景
docker-composeで立ち上げたDBコンテナに他のコンテナからconnectionを貼りたかった
その手順の備忘録手順
以下のコマンドでdocker-composeで立ち上げたnetworkIDとNameを調べる
$ docker network lsdocker runコマンドで先程取得したID or Nameを--netで指定して実行
$ docker run -it -v "$PWD":/path/to/dir --net <network ID or Name> <image名> <command>これで指定したネットワーク先のホストやエイリアスで接続できる
ホスト名がない場合は--linkでエイリアスを作成する
$ docker run -it -v "$PWD":/path/to/dir --net <network ID or Name> --link <container Name>:<host Name> <image名> <command>
- 投稿日:2021-01-13T20:45:12+09:00
Docker x Laravel 脅威の1ファイル!最小構成でローカル開発環境を構築する
docker-compose.yml
の1ファイル、ウェブサーバーコンテナ、データベースサーバーコンテナなしで作成する最小構成のDocker Laravel開発環境です。環境
- PHP: 7.4
- Composer: 2.0
- Docker: 20.10.2
- Docker Compose: 1.27.4
docker-compose.yml
1ファイルのみdocker-compose.yml
docker-compose.ymlversion: "3.8" services: composer: image: composer:2.0 volumes: - ./backend:/work/backend working_dir: /work/backend app: image: php:7.4-cli-buster ports: - 8000:8000 volumes: - ./backend:/work/backend working_dir: /work/backend command: php artisan serve --host=0.0.0.0使い方
Laravelプロジェクトの作成
$ docker-compose run composer create-project --prefer-dist laravel/laravel . # Laravelバージョン指定 $ docker-compose run composer create-project --prefer-dist "laravel/laravel=6.*" .Laravelビルトインサーバーの起動
$ docker-compose up -d appこだわったポイント
Dockerfileのビルドが必要ないこと。
もはやカップ麺を作るより速く、お湯を沸かす時間だけで作れてしまうほどシンプルな構成です。作った理由
Dockerfileのビルドの時間がかかるから。
Laravelのお試し環境を作るのに最強の構成を作るまででもないなという時。GitHub
オススメの記事
- 投稿日:2021-01-13T19:59:35+09:00
docker環境+vscodeでrubocopを有効にする方法
動作環境
- ruby 2.6.6
- rails 6.1.0
- vscode
- docker 20.10.2
この記事が役に立つ人
docker環境上で既にrubocopを導入しており、コマンドライン上ではrubocopのコードチェック機能を使えるようになっていること
上記の条件を満たした上でvscodeでrubocopのコード自動チェック機能を使いたい人
問題
vscodeにruby-rubocopをインストールしたのだが、コードの自動チェック機能が使えなかった。
以下の記事によると、vscodeではdocker上のファイルをみることができないらしい?ので、自分のPC内にdocker環境と同じバージョンのrubyとrubocop関連のgemをインストール必要があるとのことだった.Ruby on Rails とかで docker 環境なのに #VSCode で ruby-rubocop 拡張を有効にするにはどうすれば良いのか? ( #Rails #Ruby )
https://qiita.com/YumaInaura/items/5cb1387bb738349c5d9e解決策
自分のPC内に
ruby(2.6.6)
rails(6.1.0)
rubocop
rubocop-performance
rubocop-rails
をインストールした。するとvscode上で以下のエラーメッセージが出てきた。
- rubocop is not excutable
- execute path is empty! please check ruby.rubocop.executePath
そこで、以下の記事を参考にexecutePathを設定した。
[Mac]VScode で ruby-rubocop が動かなくなったら executePath を設定してみよう
https://qiita.com/tommy_aka_jps/items/ca7ae36717b69a7e390bすると、エラーメッセージが以下のように変わった。
- rubocop.yml: Metrics/LineLength has the wrong name -spaceshould be Layout
そこで、以下の記事を参考にrubocop.ymlを書き換えた。
rubocop.yml# MetricからLayoutに変更 Layout/LineLength: Max: 160 Exclude: - "db/migrate/*.rb"Ruby — rubocop のネームスペース変更に対応する
https://qiita.com/YumaInaura/items/53e14adaa5a00b6e59afこれで完了。
確認方法
最後に、適当なファイルに明らかに間違えた書き方を書いてrubocop側からエラーが返ってきたら導入完了。
- 投稿日:2021-01-13T19:02:36+09:00
vargrantからdockerへの移行
最近 M1 チップのマックになりました。
仮想環境がことごとく動かず、ようやく見つけた docker preview 7。別案件で、preview 7は利用していたのですが、またそれとは別な案件に手を出し、その案件が vargrantだったので、dockerに移行しました。
不満があるとすれば、preview 7は遅い。今までよりも数倍、遅いです。
30分くらいで終わるUnitTestが、5時間かかっても終わらないくらい。DBのアクセスがボトルネックになっているのかなー。という印象でした。さて、Vargrant → docker でハマった部分について書いていきます。
運が良かった
vargrantのセットアップというよりも、実は仮想環境の中の話です。
DBサーバ、WEBサーバ、redisサーバを合いの子でセットアップしているような案件で、セットアップを行うためのシェルスクリプトが存在し、apt-getとか勝手にやってくれます。ヤッホーと思い、そのままシェルを動かしたらdocker環境下では動かなかった部分があります。
ちなみに、OSはubuntuの13?とかだったのですが、ついでに18に移行しています。Dockerfileは、そのシェルスクリプトをコピーして実行すだけの単純なやつです。
全般
- Dockerfileから実行するので、sudo 全般を削除 (sudoが無い)
- Dockerfileの中で、
ENV DEBIAN_FRONTEND=noninteractive
を記述 (そうしないと途中で入力待ちになってしまいとまる)タイムゾーンの変更
タイムゾーンを変更するのに、
timedatectl set-timezone Asia/Tokyoとなっていたのですが、dockerだとこのコマンドが使えないらしいので、
echo "Asia/Tokyo" > /etc/timezone rm /etc/localtime ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime dpkg-reconfigure -f noninteractive tzdataと変更しました。
libpcre3と、dev
perl互換の正規表現エンジンです。
こいつ、こいつ、ubuntu18だと無いんですよね。古くなって使っちゃダメ的なやつ。
apt-getで入れようよすると、
libpcre3 is already the newest version (2:8.39-9).
とエラーになりインストールできませんでした。
代わりに、
libpcre2-32-0
,libpcre2-dev
,libpcre++-dev
を入れました。以上
nginxのビルドでライブラリが足りなかったりもしましたが、その辺は大した話でも無いし面白くも無いので割愛します。
でもやっぱり、docker遅いなー。ディスクアクセスなのかな。
#10 DONE 967.5s
- 投稿日:2021-01-13T18:50:58+09:00
railsとDockerで(Basic認証)環境変数
Docker上でBasic認証
Docker上でBasic認証を実装したがサイトに入ることができなかったため理由を考えてみた。
結論
超簡単絶対に下まで読んでください。Docker上で環境変数を設定してあげる
docker-compose.ymlversion: '3' services: web: environment: BASIC_AUTH_USER: 'admin' #仮のuser BASIC_AUTH_PASSWORD: '0000' #仮のpassword理由として
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]# 環境変数を読み込む記述 end end end大体上のような記述でBASIC認証を実装していると思う。
ターミナル上で環境変数を設定してあげて呼び出しているかもしれないが、Docker上でも環境変数を設定してあげないといけない。
理由としてはDockerは今使っているpcとは別に仮想のマシンとしてコンテナを作りその上でアプリを動かしているから。
感想一安心かと思ったがちょっと待ってほしい。
Gitにあげれなくないか。。?
今の状態でマージしてしまうとGitのリポジトリからBasic認証のuseridとpasswordが丸見えになっていてセキュリティも何もない。
なので
結論
docker-compose.ymlversion: '3' services: web: environment: BASIC_AUTH_USER: ${BASIC_AUTH_USER:-default} BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD-default}上記のように書くことによってサーバーの環境変数を持ってきてセットすることができ、外部からは見えないようになる。
docker-compose.ymlファイルを触ったのでbuildも忘れずに。感想
初学者な為、知ってるわってあったらごめんなさい。。
やばい部分あったら教えていただけると幸いです!
マージする前に気づけてよかったのと、他のAPI使用する際にも活用できそう。
- 投稿日:2021-01-13T18:14:04+09:00
Mysql2::Error: Incorrect string valueのエラーの対処
はじめに
データベースを作成してマイグレーションも実行し、「いざ、データ(日本語)を入れて動かそう!」としたところで、以下のようなエラーが。。。
Mysql2::Error: Incorrect string value: '\xE3\x82\xB2\xE3\x82\xB9...' for column 'name' at row 1どうやら文字コードが不適切らしい。。。
ということで、本記事では、このエラーについて、僕が試したことや解決策を書いていこうと思います!技術・環境
Docker/docker-compose
ruby 2.7.2
rails 6.0.2.3試したこと
①テーブルの文字コードを調べてみました。
まずは、MySQLに接続する。
$ docker-compose run web rails db次に、データベースの文字コードを調べてみる。
> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+やはり、データベースの文字コードが「latin1」だった。。。
※MySQLはdefaultで「latin1」が文字コードに設定されています。②データベースの文字コードをutf8mb4に修正
僕はDockerを使用していたので、以下のようにdocker-compose.ymlを修正。
※以下の公式ドキュメントを参考にしました。
https://hub.docker.com/_/mysqldocker-compose.ymlversion: "3" services: db: image: mysql:5.7 # この下の一行を追記しました command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: password MYSQL_USER: root ports: - "3306:3306" volumes: - ./db/mysql/volumes:/var/lib/mysql web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/share-read ports: - "3000:3000" depends_on: - db volumes: mysql-data: driver: localデータベースの文字コードを再度調べてみる。
> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+「よし!修正完了!」と思ったが、まだエラーが出てしまう。
もしかしたらテーブル自体の文字コードが「latin1」のまま?と思い調べてみると。。。> SHOW CREATE TABLE users; | users | CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, # 中略 PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |やはり、テーブルの文字コードが変わっていなかった。。。
③テーブルの文字コードをutf8mb4に修正
ということで、テーブルの文字コードを修正するのですが、10個くらいテーブルがあって一つ一つ修正するのは大変!
また、まだテーブルにデータを格納してなかったこともあり、今回はマイグレーションをやり直そうことにしました。$ docker-compose run web rails db:reset $ docker-compose run web rails db:migrateということで無事解決しました。
もし同じようなエラーが出てしまった方は、参考にしてみてください。
- 投稿日:2021-01-13T17:44:41+09:00
Django + jsonrpcserver + Docker Compose で作った JSON-RPC API を pytest-django の parametrize でテストする
以前、Django + jsonrpcserver + Docker Compose で作った "JSON-RPC 2.0" 準拠の API 動作環境に関する記事を投稿しました。
→「Python の jsonrpcserver ライブラリを使ったら Django が JSON-RPC の API サーバになった」今回はこの環境に pytest のを追加して parametrize で複数パターンの一括テストを行い、またテストクライアントの IP アドレスを偽装してみます。
pytest を使う(unittest ではなく)
Python / Django では unittest が標準のユニットテストフレームワークですが、ここではサードパーティの pytest と pytest-django をインストールして使用します。
わざわざモジュールや設定を追加してでも pytest を使いたい理由はいろいろありますが、個人的にはテストコードの可読性向上に大きなメリットを感じています。
以下の 2 つのコードは、3 つの関数の戻り値がそれぞれ 0、0 以外、0 以上であることを unittest と pytest でテストする例です。
unittestの場合class TestSamples(unittest.TestCase): def test_sample(self): self.assertEqual(func1(), 0) self.assertNotEqual(func2(), 0) self.assertGreaterEqual(func3(), 0)pytestの場合def test_sample(): assert func1() == 0 assert func2() != 0 assert func3() >= 0unittest のコードは JUnit 由来の伝統的な方式なので、テストクラスと比較条件ごとのメソッドを使用しますが、pytest は assert 文と比較演算子だけで簡潔なテストコードが書けます。
pytest-django のテスト環境を Docker Compose に追加する
以前の記事で作った動作環境に、pytest-django のテスト環境を追加します。
django_jrpc/ ├── code/ │ ├── rpcproject/ │ │ ├── tests/ │ │ │ └── test_rpc.py # 今回追加 │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ ├── views.py # 後で変更 │ │ └── wsgi.py │ ├── manage.py │ └── pytest.ini # 今回追加 ├── docker-compose.yml ├── Dockerfile └── requirements.txt # 変更pytest.ini
pytest の設定ファイル pytest.ini を code/ の直下(manage.py と同じ階層)に作成します。
pytest.ini[pytest] DJANGO_SETTINGS_MODULE = rpcproject.settings addopts = -p no:cacheprovider --cov --cov-branch
DJANGO_SETTINGS_MODULE
: プロジェクトの settings.py ファイル
- パス
rpcproject/settings.py
をrpcproject.settings
と記述addopts
: pytest に追加するオプション
-p no:cacheprovider
: キャッシュ生成を無効化--cov --cov-branch
:カバレッジを出力requirements.txt
下の 3 つのパッケージを追記します。
requirements.txtDjango==3.1.5 django-ipware==3.0.2 jsonrpcserver==4.2.0 pytest pytest-cov pytest-django
- pytest: pytest 本体
- pytest-django: pytest を Django で使うためのプラグイン
- pytest-cov: カバレッジを出力するためのプラグイン
コンテナの再ビルドと起動
requirements.txt を保存したら、コンテナを再ビルドして起動します。
docker-compose up -d --build
django_jrpc/
(docker-compose.yml があるディレクトリ)で実行するWeb ブラウザで
localhost:8000/rpc/
にアクセスして「Invalid JSON」エラーが表示されれば、リクエストが正しくルーティングされてjsonrpc
関数が正常に動作しています。テストコードを作成してリクエストを POST する
今回追加したファイル code/rpcproject/tests/test_rpc.py にテストコードを作成します。
レスポンスのステータスと JSON の戻り値をテストする
前回作った、2 つの整数の商と余りを返す関数 "division" をテストします。
テストクライアントのクラスとして
django.test.Client
を使用します。
参考: Django ドキュメント - テストクライアントtests/test_rpc.pyimport json # テスト関数名は`test_`で始まる任意の名前 def test_division(client): # パラメータ名「client」で django.test.Client のオブジェクトを受け取る # POST する JSON-RPC リクエスト # この時点ではまだ Python の辞書型(dict)なので、「"divisor": 16,」と「"id": 1,」の # 各行末のカンマが許容される(JSON では不可) payload = { "jsonrpc": "2.0", "method": "division", "params": { "dividend": 1080, "divisor": 16, }, "id": 1, } # JSON と下記のオプションを指定して POST リクエストを送り、API のレスポンスを得る response = client.post( path="/rpc/", data=payload, content_type="application/json-rpc", follow=True ) # HTTP レスポンスのステータスコードを期待値(200 OK)と比較する assert response.status_code == 200 # JSON-RPC レスポンスを辞書型に変換してから結果を取り出して、それぞれの期待値と比較する content = json.loads(response.content) assert content["result"]["quotient"] == 67 # 商 assert content["result"]["remainder"] == 8 # 余り
client.post()
のパラメータ
path="/rpc/"
: URL のlocalhost:8000
に続くパスを指定しますdata=payload
: 上で定義した辞書型変数を POST データとして渡しますcontent_type="application/json-rpc"
: payload の辞書を変換して JSON-RPC リクエストを生成しますfollow=True
: リダイレクトが要求された場合に、True で自動的にリダイレクト先までフォローしてくれます。False にするとリダイレクトが要求された時点で終了するので、ステータスコードが 30x に、content が None になります。JSON-RPCレスポンス{ "jsonrpc": "2.0", "result": { "quotient": 67, "remainder": 8, "ipaddress": "127.0.0.1" }, "id": 1 }pytest を実行してテスト結果とカバレッジを確認する
コンテナ名が不明の場合は、コマンド
docker-compose ps
で実行中のコンテナを確認します。docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------- django_jrpc_web_1 python3 manage.py runserve ... Up 0.0.0.0:8000->8000/tcp
表示されたコンテナ名を使って、以下のコマンドでコンテナの中に入ります。
docker exec -it django_jrpc_web_1 bashコンテナの中で
pytest
コマンドを実行して、以下のようにテスト結果とカバレッジが表示されたら成功です。pytestの出力collected 1 item rpcproject/tests/test_rpc.py . [100%] ----------- coverage: platform linux, python 3.9.1-final-0 ----------- Name Stmts Miss Branch BrPart Cover ---------------------------------------------------------------- rpcproject/__init__.py 0 0 0 0 100% rpcproject/settings.py 18 0 0 0 100% rpcproject/tests/test_rpc.py 9 0 0 0 100% rpcproject/urls.py 4 0 0 0 100% rpcproject/views.py 16 1 0 0 94% ---------------------------------------------------------------- TOTAL 47 1 0 0 98% ========================= 1 passed in 0.82s ==========================クライアントの IP アドレスと経路を偽装してテストする
あまり需要はないと思いますが、クライアントの IP アドレスや経路によって結果が変わる場合を想定して、リクエストの HTTP ヘッダを偽装してテストを行います。
偽装しないで同じコンテナ内でテストを実行すると、クライアントの IP アドレスは常に
127.0.0.1
になります。直接接続のリクエストを偽装
tests/test_rpc.pyresponse = client.post( path="/rpc/", data=payload, content_type="application/json-rpc", follow=True, REMOTE_ADDR="192.168.1.2" ) content = json.loads(response.content) assert content["result"]["ipaddress"] == "192.168.1.2"
client.post()
に HTTP ヘッダと同名のパラメータREMOTE_ADDR
を追加します。上記は192.168.1.2
のホストから直接接続された場合の偽装です。中継されたリクエストを偽装
tests/test_rpc.pyresponse = client.post( path="/rpc/", data=payload, content_type="application/json-rpc", follow=True, HTTP_X_FORWARDED_FOR="192.168.1.2,10.20.30.40,127.0.0.1" ) content = json.loads(response.content) assert content["result"]["ipaddress"] == "192.168.1.2"HTTP ヘッダ
HTTP_X_FORWARDED_FOR
と同名のパラメータにカンマ区切りの IP アドレスを指定することで、ロードバランサーなどで中継されたリクエストを偽装することができます。
カンマ区切りの先頭がクライアントの IP アドレスで、以降中継ノードのアドレスが続きます。ここでテストしている関数 "division" が返す
ipaddress
の値はクライアントのアドレスなので、上記のテストではカンマ区切りの先頭のアドレス192.168.1.2
をアサートしています。
→「DjangoでクライアントのIPアドレスを取得する」@pytest.mark.parametrize で複数パターンをテストする
pytest の parametrize を使って、1 つのテストコードで複数のパターンをテストします。
参考: pytestのparametrizeの使い方とその有用性についてその前に views.py を変更して、クライアントの IP アドレスによって分岐する処理を追加します。
以下の例では、アドレスが192.168.1.*
に一致しない場合はHTTP 400 Bad Request
を返して終了し、それ以外はこれまでどおりの動作になります。rpcproject/views.pyfrom django.http import JsonResponse from django.http.response import HttpResponse from django.views.decorators.csrf import csrf_exempt from jsonrpcserver import method, dispatch import ipware @csrf_exempt def jsonrpc(request): ipaddress, _ = ipware.get_client_ip(request) # クライアントの IP アドレスが 192.168.1.* でない場合は、HTTP 400 Bad Request を返す。 if not ipaddress.startswith("192.168.1."): return HttpResponse(status=400) response = dispatch(request=request.body.decode(), context={'ipaddress': ipaddress}) return JsonResponse( response.deserialized(), status=response.http_status, safe=False )この変更によって
192.168.1.*
に一致しないクライアントに正しくBad Request
が返されることを複数のパターンでテストします。以下は追加のテストコードの例です。
クライアントの IP アドレス192.168.1.2
と192.168.1.254
でステータスコード 200 (OK) が、192.168.2.2
と10.20.30.1
で 400 (Bad Request) が返されることをテストします。tests/test_rpc.pyimport pytest # HTTP_X_FORWARDED_FOR の値とレスポンスステータスのパターン # 下のテスト関数で各タプルの値を、パラメータ x_header と http_status で受け取る host_x_headers = ( ("192.168.1.2,127.0.0.1", 200), ("192.168.2.2,127.0.0.1", 400), ("192.168.1.254,127.0.0.1", 200), ("10.20.30.1,192.168.1.1", 400), ) @pytest.mark.parametrize("x_header, http_status", host_x_headers) def test_client_address(client, x_header, http_status): payload = { "jsonrpc": "2.0", "method": "division", "params": {"dividend": 1080, "divisor": 16}, "id": 1, } # タプルの値 x_header をパラメータ HTTP_X_FORWARDED_FOR で指定する response = client.post( path="/rpc/", data=payload, content_type="application/json-rpc", follow=True, HTTP_X_FORWARDED_FOR=x_header ) # タプルの値 http_status がレスポンスステータス値と一致するかをテストする assert response.status_code == http_status
@pytest.mark.parametrize
: テスト関数で pytest の parametrize を使うためのデコレータです
"x_header, http_status"
:host_x_headers
内の各タプルで定義した 2 つの値を受け取る変数名を、カンマ区切りの文字列で定義しますdef test_client_address(client, x_header, http_status)
:
x_header, http_status
: デコレータで定義した名前のパラメータを使って、host_x_headers
の各タプルの値を受け取ります再度コンテナの中で
pytest
コマンドを実行して、テスト結果を確認します。pytestの出力collected 5 items rpcproject/tests/test_rpc.py ..... [100%] ----------- coverage: platform linux, python 3.9.1-final-0 ----------- Name Stmts Miss Branch BrPart Cover ---------------------------------------------------------------- rpcproject/__init__.py 0 0 0 0 100% rpcproject/settings.py 18 0 0 0 100% rpcproject/tests/test_rpc.py 16 0 0 0 100% rpcproject/urls.py 4 0 0 0 100% rpcproject/views.py 19 1 2 0 95% ---------------------------------------------------------------- TOTAL 57 1 2 0 98% ========================= 1 passed in 0.82s ==========================テストが成功し、出力結果が
5 items
に増えたことから、タプルで定義したパターンごとに 1 つのテストとカウントされたことが確認できました。今後の予定
盛り込みすぎると読み返しにくくなるので、以下は今後の予定にします
- VS Code Remote Containers で開発・デバッグ環境をコンテナ化
- pylint-django + black + rope によるリント、フォーマット、リファクタリング環境
- Pylance を使った静的型付け
- Model とデータベースの使用
- フィクスチャを使ったテストデータの投入
- 投稿日:2021-01-13T17:38:50+09:00
docker+laravelでGeneral error: 1812 が発生
初めに
docker + laravelを使って、なんかプログラムを作ろうと
migrateを行った際に$ docker exec -it docker-sample_php-fpm_1 bash root@39aae0ad8da6:/var/www/html# php artisan migrate Illuminate\Database\QueryException SQLSTATE[HY000]: General error: 1812 Tablespace is missing for table `laravel`.`migrations`. (SQL: select `migration` from `migrations` order by `batch` asc, `migration` asc) at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671 667| // If an exception occurs when attempting to run a query, we'll format the error 668| // message to include the bindings with SQL, which will make this exception a 669| // lot more helpful to the developer instead of just the database's errors. 670| catch (Exception $e) { > 671| throw new QueryException( 672| $query, $this->prepareBindings($bindings), $e 673| ); 674| } 675| +27 vendor frames 28 artisan:37 Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))が発生した。
備忘録として、メモしておく何が起こったか?
出ているエラーをよくみてみると
Tablespace is missing for table `laravel`.`migrations`と出ている。
dokcerのmysqlのコンテナーにログインして、テーブルを確認するとmysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | laravel | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.01 sec) mysql> use laravel; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +-------------------+ | Tables_in_laravel | +-------------------+ | failed_jobs | | migrations | | password_resets | | tasks | | users | +-------------------+ 5 rows in set (0.00 sec)migrationsテーブルは存在する。
migrationsテーブルの中身を見てみる。mysql> select * from migrations; ERROR 1812 (HY000): Tablespace is missing for table `laravel`.`migrations`.エラーが出てくる。
解決
色々と調べてみたが、いかが手っ取り早そう
mysql> drop table migrations; Query OK, 0 rows affected (0.02 sec)他のテーブルでもmigrateが詰まったら
drop table tbl_nameを実行すれば良さそう。
それでは素敵なdocker lifeを
- 投稿日:2021-01-13T17:38:42+09:00
Dockerでお手軽JSONデータベース体験 ~ Couchbase Server最新版を試す
はじめに
本記事の対象者
本記事の対象者として、以下の様な人を想定しています。
- JSONデータを格納できるデータベースで開発を始めたい(中でも特に以下の関心がある人)
- JSONデータへの問合せをSQLのようなクエリ言語で行いたい
- ミリ秒以下の応答性能を実現したい
- 開発・検証用にローカル環境にデータベースをインストールしたい
インストール方法について
表題にある様に、ここではDockerを利用します。
Dockerについての知識を前提としなくても、実行できる記述を心がけますが、インストーラーによるインストールの方が馴染みがある方は、下記からダウンロードして実行してください(Mac, Windows, Ubuntuのインストーラがあります)。その後、初期設定などについて、本記事をご参照ください。COUCHBASE FREE NOSQL DATABASE LIST
Dockerについては、環境にインストールされているところから、解説を始めます。
Couchbase Serverについて
Couchbase Serverは、分散アーキテクチャーを持ちますが、ここではローカル環境に、シングルノードのクラスターとして構築します。(Dockerを使えば、ローカル環境で複数のノードからなるクラスターとして構築することも可能です。Dockerに慣れている方には特に難しいところはありませんが、興味のある方は、参考情報のドキュメントをご参考ください)
Couchbase Serverには、Enterprise EditionとCommunity Editionがありますが、ここでは、検証目的として、Enterprise Editionを用います(上記リンクに各エディションの利用できる範囲について記述があります)。
環境構築手順
Dockerによるインストール
初回実行
下記のコマンドを実行します。
$ docker run -d --name db -p 8091-8094:8091-8094 -p 11210-11211:11210-11211 couchbaseCouchbaseのDockerリポジトリから最新版("couchbase:latest")がダウンロードされ、コンテナの実行が開始されます。
http://localhost:8091にアクセスします。
下記の様な画面が表示されます。
インストール完了です!
インストール確認
...というのも味気ないので、インストールされた状態を確認してみます。
ログの確認
db
は、docker run
コマンドの--name
オプションで指定したものです。$ docker logs db Starting Couchbase Server -- Web UI available at http://<ip>:8091 and logs available in /opt/couchbase/var/lib/couchbase/logイメージの確認
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE couchbase latest 8f1b63be0df7 3 weeks ago 1.19GBプロセスの確認
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4bfe9191a55 couchbase "/entrypoint.sh couc…" 6 minutes ago Up 6 minutes 8095-8096/tcp, 0.0.0.0:8091-8094->8091-8094/tcp, 11207/tcp, 0.0.0.0:11210-11211->11210-11211/tcp, 18091-18096/tcp db「couchbase」という名前のDockerイメージがインストールされ、そのイメージから「db」という名前のコンテナが作られ、稼働中の状態であることが確認されました。(最後にこのコンテナの停止と再起動についてもみていきます)
Couchbase Serverの初期設定
クラスター初回構築
ウィザード選択
上記の画面イメージに見える通り、インストール直後にWEBコンソールにアクセスした際の初めの選択肢は、「Setup New Cluster」と、「Join Existing Cluster」との二つです。ここでは、当然前者を選択します。
次の様な画面が表示されるので、適宜入力します。「Next: Accept Terms」を押します。
Terms and Conditions
「Finish With Defaults」で終了することもできますが、「Configure Disk, Memory, Services」をみてみましょう。
クラスター設定
検証目的において、特に変更しなければならないところはないので、「Save & Finish」を押下し、終了します...と言いたいところですが、下記の様なエラーが表示されるかもしれません。
上の画面を見直すと、TOTAL QUOTAが、Max Allowed Quotaを上回っていることがわかります。
適宜、各サービスのQuotaを変更し、Max Allowed Quota以下になる様に調整します。また、不要なサービスのチェックボックスを外すことにより、このノードでは、そのサービスが無効になります。後から有効にすることはできませんが(その場合は、クラスターからの削除、再追加が必要)クラスター全体の構成を柔軟に設計することができます。ここで準備する検証環境としては、特に必要のない限り、Search, Analytics,Eventingを無効にして差し支えないかと思います。
バケット作成
データの格納の単位であるBucketを作るため、「Bucket」リンクをクリックします。
表示されるのは、バケット一覧画面です。右上の「ADD BUCKET」リンクをクリックします。
適当な名前を入力して「Add Bucke」ボタンを押下します。(バケットの設定は後から変更可能です)
こんな感じになったかと思います。「Warning」が表示されていますが、これは、デフォルトでレプリカの設定が有効になっているためです。アラートが表示されない様にするため「Edit」を押下します。
「Advanced bucket settings」を展開し、Replicasのチェックボックスを外します。
ついでに、Flushを有効にしておきます。「Save Changes」を押下します。
「Warning」が消え、「Flush」ボタンが追加されました。
ドキュメント追加
ドキュメントの追加を行ってみます。
上の画面の右上のDocumentsリンクをクリックします。この様な画面が表示されます。ブラウザの画面サイズを十分に広げることで、左のメニューが表示されます。
右上の「ADD DOCUMENT」をクリックします。WEBコンソールからドキュメントを作った際のデフォルトの内容が表示されるので、適宜編集するなりして、「Save」を押下します。(ドキュメント作成確認のためなので、修正は不要です)
ドキュメントの追加が確認されました。
コンテナの停止・起動
コンテナをストップします。
$ docker stop db実行中のコンテナを確認します。ブラウザでWEBコンソールにアクセスできないことも確認します。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES実行されていないコンテナを確認します。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4bfe9191a55 couchbase "/entrypoint.sh couc…" 49 minutes ago Exited (0) About a minute ago dbコンテナを改めて開始してみます。
$ docker start dbWEBコンソールにアクセスできることを確認します。
お疲れ様でした!
...というほどカロリーを使ってはいないと思いますが、新しい技術に初めて触れる時の抵抗感が多少でも緩和されたのであれば幸いです。
この後は、下記の記事なども参考にしてみてください
次のステップ:Couchbase Serverを用いた開発
リアクティブプログラミングをNoSQLで「体験」してみる(Couchbase Reactive APIに触れる)
JavaScript開発にNoSQLデータベースを活用する(CEANスタック紹介) ~ Node.js + Couchbaseアプリ開発 ステップバイステップガイド 2
Go + NoSQL(Couchbase) アプリ開発 ステップバイステップガイド (1)
Node.js + NoSQL(Couchbase) アプリ開発 ステップバイステップガイド (1)
参考情報
Couchbaseローカル環境をDockerで構築
以前のバージョン(4.5.1)のCommunity Editionを使うケースが紹介されています。また、Dockerのカスタマイズについても解説されています。Deploy a Multi-Node Cluster with Containers
本記事冒頭で触れたDockerを使って複数ノードのクラスタをローカル環境で実現する方法(同じことを実現するために、Kubernetesを使う方法もありますが、それはまた別のトピックとして、いずれ記事にできたらと思います)よく使うDockerコマンド
各種Dockerコマンドが簡潔に紹介されています。
- 投稿日:2021-01-13T15:38:37+09:00
【Docker】コマンド一覧
※
[]
内は省略可能
- コンテナを作成し起動 [バックグラウンドで実行]
docker-compose up [-d]
- コンテナ起動(既にコンテナがある状態で)
docker start <コンテナ名>
- コンテナ停止
docker stop <コンテナ名>
- 全てのコンテナ停止
docker stop $(docker ps -q)
- コンテナ起動状況確認 [停止しているコンテナも取得]
docker ps [-a]
- イメージ実行
docker run <イメージ>
- イメージ一覧表示
docker images
- サービスのビルドを実行
docker-compose build
- 実行中コンテナ内で、新しいコマンドを実行
docker exec [オプション] <コンテナ> <コマンド> [引数]
- 投稿日:2021-01-13T10:25:12+09:00
【ARM64】New RelicでDockerサーバー監視 on Docker on RasPi4
最近Raspberry Pi 4B(4GB)上のDockerでサービスを次から次へと立ち上げていて、サーバー監視してみたくなってきたのでやり方をメモ。
- Dockerに対応したサーバー監視ツール
- ARMアーキテクチャに対応
- サーバー監視そのものもDocker上で行いたい
という欲張りな人には役立つはず。
やること
Raspberry Pi 4B(4GB)上で動いているサーバーの状態(CPU・メモリ・プロセス)を監視する。
環境
- Raspberry Pi 4B(4GB)
- Raspberry Pi OS 64bit beta
- Docker 19.03.12
- Docker Compose 1.26.2
nginx-proxyを使って各サーバーへのリクエストを振り分けている。
New Relic について
サーバー監視ツールはいくつもありますが、その中でDockerに対応しているツールとなると限られてくる。サービスの選定はこちらの記事を参考にして、SaaS型で手軽に無料で使えそうなNew Relicを選んだ。
サーバー監視ツールは価格が高く、個人開発でちょろっと公開しているだけのサービスに導入するのは敷居が高い気がしていたが、New Relic Oneでは毎月100GBのデータ取り込みまでは無料ライセンスで使える。個人で使う分であれば十分すぎる容量だ。
New Relicにはアプリのパフォーマンス監視(APM)やPingによる死活監視など複数のサービスがあるが、今回使うのはCPUやメモリの使用量を見られるInfrastructure。アカウント作成・ライセンスキー取得
New Relicのサイトにアクセスして、右上にある「無料サインアップ」を押してアカウントを作る。アカウントが作成できたら、右上のアカウントのドロップダウンから
Account settings
を選択し、左のサイドバーからAPI keys
をクリックする。
Create key
を押して、Key type
をIngest - License
、Name
を任意の名前にしてキーを作成する。Dockerコンテナを立てる
x86-64であれば公式にDockerイメージが提供されているので、それを公式のドキュメントに従ってコンテナを起動すればいいだけなのだが、arm64なので自前でビルドする必要がある。
Dockerイメージのビルド
とはいえ、イメージの中に突っ込まれる
newrelic-infra
、newrelic-infra-ctl
、newrelic-infra-service
の3つのバイナリを差し替えればいいだけ。
バイナリは公式に提供されているので、以下のリンクよりダウンロード(2021年1月現在では1.9.7が最新)。
https://download.newrelic.com/infrastructure_agent/binaries/linux/mkdir ~/newrelic-infra-setup cd ~/newrelic-infra-setup wget https://download.newrelic.com/infrastructure_agent/binaries/linux/arm64/newrelic-infra_linux_1.9.7_arm64.tar.gz tar -zxvf newrelic-infra_linux_1.9.7_arm64.tar.gz次にnewrelic/infrastructure-agentのGitHubリポジトリをCloneする。
git clone https://github.com/newrelic/infrastructure-agent.gitDockerイメージをビルドする方法はこのページを参考にした。まずダウンロードしたバイナリを指定のフォルダにコピーする。
linux_amd64
にarm64バイナリを入れるというのもアレだが、そこはスルーで。mkdir -p infrastructure-agent/target/bin/linux_amd64 cp newrelic-infra/usr/bin/* infrastructure-agent/target/bin/linux_amd64/ cd infrastructure-agent/build/container export IMAGE_VERSION=1.0 # 適当に。指定しなければ0.0になる make build/baseDockerコンテナを立てる
これでDockerイメージが生成されたので、後はx86-64と同じ手順でコンテナを立てる。手順はこのページに従えばいい。
最初に取得したライセンスキーを
newrelic-infra.yml
に入れます。cd ~/newrelic-infra-setup echo "license_key: {YOUR_LICENSE_KEY}" > newrelic-infra.yml touch newrelic-infra.dockerfile vim newrelic-infra.dockerfile # どのエディタでも大丈夫newrelic-infra.dockerfileFROM newrelic/infrastructure:latest ADD newrelic-infra.yml /etc/newrelic-infra.ymltouch docker-compose.yaml vim docker-compose.yaml # どのエディタでも大丈夫docker-compose.yamlversion: '3' services: agent: container_name: newrelic-infra build: context: . dockerfile: newrelic-infra.dockerfile cap_add: - SYS_PTRACE network_mode: host pid: host privileged: true volumes: - "/:/host:ro" - "/var/run/docker.sock:/var/run/docker.sock" restart: unless-stoppeddocker-compose build docker-compose up -d
これでもうサーバー監視がもう始まっているので、New RelicのWebサイト上の上部バーから
Infrastructure
を選んでみると、CPU使用率や空きメモリの割合等がグラフで表示されている。また、上部バーから
Entity explorer
を開いてみると、他にも色々なグラフや表があるので、好きなものをダッシュボードに追加すると良い。
newrelic-infra
が一番CPU使ってますね、草。不具合
- Infrastructure > Hosts > Docker Containers でコンテナごとの情報が取得されていない
docker-compose logs
するとエラーを吐いている("integration exited with error state"
)Docker対応ということでNewRelicを選んだのに使えないのは本末転倒なのでなんとか直したい。
参考文献
New Relic Pricing(日本語)
New Relic license key
Docker container for infrastructure monitoring
Docker instrumentation for infrastructure monitoring
newrelic/infrastructure-agent
newrelic/infrastructure-agent - Containerized Agent
newrelic/infrastructure-agent - ARM64 (aarch64) support #107
Dockerコンテナの監視を行うサービス
「デジタル・ニューノーマル構想」を掲げるNew Relicが製品や価格体系を刷新
- 投稿日:2021-01-13T01:08:24+09:00
Rust公式ドキュメントをDockerコンテナ上で進めてみる(1. 事始め)
はじめに
タイトルの通りです。
Rustは初めてです。
コンテナの使い方として、以下を考えましたが、今回のコンテナをrustの学習以外で使うことはないため、前者でいくことにしました。
- FROM:rustとする
- FROM:ubuntuなどにし、rustをその中でインストールする方法
前者の場合、rustイメージが利用しているosが使われます。使っているosはrustのDockerfileで確認。
https://github.com/rust-lang/docker-rust参考にしたもの
Rust公式ドキュメント(The Rust Programming Language 日本語版):https://doc.rust-jp.rs/book-ja/title-page.html
DokerHub(Rust):https://hub.docker.com/_/rust1.1 インストール
公式ドキュメント:https://doc.rust-jp.rs/book-ja/ch01-01-installation.html
今回はrustイメージを使ってしまうので、ここはスキップ。
代わりに、dockerHub上にrustのimageがあることを確認。DockerHub上のRustのDockerfileのサンプルFROM rust:1.31 WORKDIR /usr/src/myapp COPY . . RUN cargo install --path . CMD ["myapp"]サンプルがあるならと、バージョンのみ変えてこの通りに記述してみましたがbuildでエラーになりましたのでみなかったことにします。
Cargo.tomlがなんだかわからん$ docker build -t my-rust-app . Sending build context to Docker daemon 2.048kB Step 1/5 : FROM rust:latest ---> 8d0d57072949 Step 2/5 : WORKDIR /usr/src/myapp ---> Using cache ---> 6015e37ea342 Step 3/5 : COPY . . ---> 66589dbef629 Step 4/5 : RUN cargo install --path . ---> Running in b3cdc5e8eece error: `/usr/src/myapp` does not contain a Cargo.toml file. --path must point to a directory containing a Cargo.toml file.1.2 Hello, World!
公式ドキュメント:https://doc.rust-jp.rs/book-ja/ch01-02-hello-world.html
Dockerfileを作成します。一部、DockerHub上のuse imageの仕方に合わせています。DockerfileFROM rust:latest WORKDIR /usr/src/projects/hello_world COPY . . RUN cd /usr/src/projects/hello_world && \ rustc main.rs CMD ["./main"]コンテナにコピーするmain.rcを作成します。
main.rcfn main() { // 世界よ、こんにちは println!("Hello, world!"); }ビルドして実行
$ docker build -t my-rust-app . $ docker run -it --rm --name my-running-app my-rust-app Hello, world!1.3 Hello, Cargo!
公式ドキュメント:https://doc.rust-jp.rs/book-ja/ch01-03-hello-cargo.html
Dockerfileを編集していきます。DockerfileFROM rust:latest WORKDIR /usr/src/projects RUN cd /usr/src/projects && \ cargo new hello_cargo --bin && \ cd /usr/src/projects/hello_cargo && \ cargo build CMD ["/usr/src/projects/hello_cargo/target/debug/hello_cargo"]ビルドしたところUSERを指定してと言われる。
$ docker build -t my-rust-app . (中略) Step 5/7 : RUN cd /usr/src/projects && cargo new hello_cargo --bin ---> Running in 1f2404887764 error: Failed to create package `hello_cargo` at `/usr/src/projects/hello_cargo` Caused by: could not determine the current user, please set $USERRUN,CMDをコメントアウトしてコンテナ内に入って実行しても同様。
Dockerはユーザーを指定しないとrootでログインしますが、echo $USERは空。$ docker run -it --rm --name my-running-app my-rust-app root@126e1954fec0:/usr/src/projects# cargo new hello_cargo --bin error: Failed to create package `hello_cargo` at `/usr/src/projects/hello_cargo` Caused by: could not determine the current user, please set $USER root@126e1954fec0:/usr/src/projects# echo $USER環境変数のUSERを定義するようDockerfileを修正します。
DockerfileFROM rust:latest WORKDIR /usr/src/projects ENV USER=root RUN cd /usr/src/projects && \ cargo new hello_cargo --bin && \ cd /usr/src/projects/hello_cargo && \ cargo build CMD ["/usr/src/projects/hello_cargo/target/debug/hello_cargo"]ビルドして実行
$ docker build -t my-rust-app . $ docker run -it --user root --rm --name my-running-app my-rust-app Hello, world!おまけ
1.1 インストールで出ていたDockerHubのrustテンプレートのビルド時に発生した以下のエラーを解消したい場合
FROM rust:latest WORKDIR /usr/src/myapp COPY . . ##追加############### ENV USER=root RUN cargo init . #################### RUN cargo install --path . CMD ["myapp"]参考:https://kajirikajiri.gitbook.io/gitbook/untitled
おわりに
Docker上で言語の勉強をしていますがローカルのパソコンの環境を汚さなくて良いので、なんども作り直しができるのは本当に良いですね。
また、USERの件など、ドキュメントをそのままなぞっているだけでは気付きづらいRustの仕様もDocker上で進めていることによりエラーが出たりして思わぬところで理解が深まる効果が得られています。なお、実際は、FROMとWORKDIRの定義といった最低限のDockerfileのみでコンテナを作成してコンテナに入り、ドキュメントの通りにコマンド実行したり作成されたものの中身など見ながら進めています。
問題なさそうになったら、Dockerfileを更新して最終的にCMDで同じ結果になることを確認する手順を踏んでいます。
Rustの勉強だけならDockerfileを更新する必要ないのですが、Dockerの仕様の勉強にもなるためしばらく続行しようと思います。
- 投稿日:2021-01-13T00:04:32+09:00
Dockerが注目される理由。そもそもDockerとは?その使い方
はじめに
本記事は、Dockerの初学者を対象に基礎的な内容を学ぶための記事。
※追伸:こちらの記事よりも更に良い記事を見つけてしまったので、もっと詳しく知りたい方は以下をご参照ください。
・Dockerでプログラマが最低限知るべきことが、最速でわかるチュートリアルDockerとは
Dockerは仮想化の一つで、コンテナ型仮想化と呼ばれている。
コンテナ型仮想化はOSの上に「コンテナ」と呼ばれる仮想的な空間を作ることでOSの上に仮想的なOSを展開して、更にプロセスの実行空間を区分け(隔離)できる。仮想化サーバーとの違い・長所
- 起動速度がはやい
- 移動が可能
- 稼働が軽い
- リソースが有効活用可能
Dockerが注目されている理由
一番の理由は、移動ができてリソースを有効活用できるということである。未使用のコンピュータ資源をアクティブに入れ替えながら使うことでサーバの効率化ができることから、クラウドサービスといった変動費の費用が発生するものと相性が良い。Google社では全部のソフトウェアをコンテナに乗せており、アクティブに入れ替えながら使うことでサーバの効率化をしている。
他には従来、何かサービスをつくる場合、エンジニアは環境の制約を大きく受けていた。自分が書いたコードが本当にサーバー上で動くのか心配しながら開発を行わなければらなかった。しかし、Dockerを使うことで、自身のPCなどのdockerでコンテナを実行してながら開発をすることで、コードを仕上げ、最終的にコンテナを移動させるだけで、アプリケーションを動く状態にできるのだ。
このことから、Dockerはインフラエンジニアのスキルに止まらず、
→プログラマーの必須科目と言われているのである。Dockerを使う流れ
1Dockerインストール
・OS上にDockerをインストールします。macやWindows、Linuxにインストールできる。
2Dockerfileの作成
・Dockerfileは、全く新しいDocker Imageを作成するための手順を決めるテキストファイル。したがって、インターネットから取ってくる場合など、全く新しいDocker Imageの作成をしない場合は不要。
3Docker Imageの作成
・コンテナイメージを任意の場所から持ってきて作成します。Dockerインストール後初期段階では、当然コンテナイメージは存在しないので、インターネット上からコマンド入力で持ってきます。
・インターネット上の一般的なイメージ格納場所として、「Docker Hub」というところがあります。他には、AWSのECR、さまざまな企業に独自のリポジトリサーバーに構築されて格納・管理されています。
4コンテナ起動実際のコマンド入力
・インターネットからイメージを取得する
$ docker pull [イメージの名前]Docker Hubでアカウントをつくり、サービス名を検索することで、コピ&ペーストできるコマンドを見つけることもできます。Copy and paste to pull this imageのところ。
例)Apacheのイメージ取得
$ docker pull httpd・イメージが取れたのか確認
$ docker images・docker起動
→オプションについて
① -d バックグラウンドでdockerを実行する
② -p ポートの指定。[8088:80]は[ホストのポート:コンテナのポート]の関係。コンテナのポート80番をホスト側の8088で待ち受けるという意味になる。
※docker runコマンドはone liner(一行だけ)で実行でき、イメージを取得するコマンドを省略して実行できる。その場合は、自動で指定のイメージをローカルから参照する。イメージが無かったらDocker Hubから取ってきてくれる。$ docker run -d -p 8088:80 httpd・コンテナの状況を見る
→コンテナIDやSTUTASの項目を見ることができる。docker ps -a・コンテナ停止
$ docker stop [コンテナID]・コンテナ自体の削除
$ docker rm [コンテナID]・イメージの削除
$ docker rmi [コンテナID]おわりに
今回の内容ではDockerの基礎的な知識をまとめた。
応用としては、Dockerfileの作成やdocker-composerを使い複数のコンテナを管理することなど、オーケストレーションツールを使って、複数のDockerのリソース管理をするといった内容がある。
- 投稿日:2021-01-13T00:02:44+09:00
Dockerでngrokを利用する
ngrokとは
ngrokは、ローカルホストを外部に公開(トンネル)できる
やりかた
Docker Hubにwernight/ngrok(https://hub.docker.com/r/wernight/ngrok/)というイメージがあります。
ngrokのイメージの中で多く利用されており、使いやすいです。docker-compose.ymlversion: "3.8" networks: default: services: nginx: image: nginx:stable-alpine container_name: nginx ports: - 80:80 networks: - default depends_on: - app ngrok: image: wernight/ngrok:latest ports: - 4040:4040 environment: NGROK_PROTOCOL: http NGROK_PORT: nginx:80 #NGROK_AUTH: ${NGROK_AUTH} depends_on: - nginx networks: - default上記は、nginxのコンテナをそのままngrokで公開しています。
environment
でNGROK_PORT: nginx:80
のように、公開するコンテナ名とポートを指定するだけです。NGROK_AUTH
は認証が必要なサービスを利用しない場合は不要です。便利だし、サクッと使えるので助かる...!
LinebotのようなSSL必須の自己証明不可の外部APIをローカルで開発する場合にはすごく役立ちそうです。