普段はDockerを使った開発環境で、ホストとコンテナのポートをバインドして他の端末から繋いでいる
公衆無線LANの環境で作業する必要が出てきて、これ他の接続してるユーザーから見えてダメじゃんと思ったのでIPを縛るように変えてみる
とりあえず普段使う感じで確認
0.0.0.0
にバインドされているのがわかる
$ docker run --rm -d -p 80:80 nginx:alpine
$ docker ps --format "table {{.Image}}\t{{.Ports}}"
IMAGE PORTS
nginx:alpine 0.0.0.0:80->80/tcp
リファレンスに書いてある通りホストのIPを指定する
これだとホストの外からはアクセスできない
$ docker run --rm -d -p 80:80 nginx:alpine
$ docker ps --format "table {{.Image}}\t{{.Ports}}"
IMAGE PORTS
nginx:alpine 127.0.0.1:80->80/tcp
Docker Composeでも同じようにホストのIPを指定できるので、場所に応じて適切に使い分けたい
Arrayのdeep copyはどう作るのかなと思ってArray.prototype.map()を触っていたら、パッと思ったのと挙動が違うのでメモ
環境はSafari 13.0.5
まずArray.prototype.map()はMDNには以下のように記載されている
map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。
ということで雑にcallbackFnで引数をそのまま返してみる
const a = [{a: 'aaa'}];
const b = a.map(v => v);
b[0].a = 'bbb';
console.log(a, b);
// Array (1)
// 0 {a: "bbb"}
// Array (1)
// 0 {a: "bbb"}
あれ? 新しいArrayが生成されるのに変数aの値も変わってる?
いや、Arrayとしては新しく生成されたけど、要素単位ではobjectだからshallow copyされてる?
ということでプリミティブ値のArrayで実験
const a = ['aaa'];
const b = a.map(v => v);
b[0] = 'bbb';
console.log(a, b);
// Array (1)
// 0 "aaa"
// Array (1)
// 0 "bbb"
変数aの方には影響が出なかったので、やっぱり要素がobjectだとそこだけはshallow copyみたい
ということは型の混ざるArrayだと?
const a = ['aaa', {a: 'aaa'}];
const b = a.map(v => v);
b[0] = 'bbb';
b[1].a = 'ccc';
console.log(a, b);
// Array (2)
// 0 "aaa"
// 1 {a: "ccc"}
// Array (2)
// 0 "bbb"
// 1 {a: "ccc"}
思った通りに混ざった
代入の仕様そのままだけど思い込みで違和感たっぷり
Laravel Mixを使うとBabelのトランスパイルやSASSのコンパイルが簡単にできるけど、webpackが何してるかさっぱりなので設定を出力してみる
setup/webpack.mix.jsにAPIが書いてあったので以下の通りにすると出力される
const mix = require('laravel-mix');
const util = require('util');
mix.override(webpackConfig => {
console.log(
util.inspect(webpackConfig, false, null, true)
);
});
Vue.js + Vue Routerを普段使わないIISで配信する必要に迫られて、web.configでCache-Controlヘッダーの出力設定をしたのでメモ
Vue Routerはhashモードなのでrewriteはいらなかった
web.configのリファレンスはどこをどう探したらいいのかさっぱり分からない
ディレクトリごとの設定をまとめてできないのが不便
<configuration>
<system.webServer>
<defaultDocument enabled="true">
<files>
<clear />
<add value="index.html" />
</files>
</defaultDocument>
<httpProtocol>
<customHeader>
<add name="Cache-Control" value="no-cache" />
</customHeader>
</httpProtocol>
</system.webServer>
<location path="js">
<system.webServer>
<httpProtocol>
<customHeader>
<remove name="Cache-Control" />
<add name="Cache-Control" value="max-age=86400" />
</customHeader>
</httpProtocol>
</system.webServer>
</location>
<location path="css">
<system.webServer>
<httpProtocol>
<customHeader>
<remove name="Cache-Control" />
<add name="Cache-Control" value="max-age=86400" />
</customHeader>
</httpProtocol>
</system.webServer>
</location>
<location path="images">
<system.webServer>
<httpProtocol>
<customHeader>
<remove name="Cache-Control" />
<add name="Cache-Control" value="max-age=86400" />
</customHeader>
</httpProtocol>
</system.webServer>
</location>
</configuration>
Nginxのリバースプロキシで他のサーバにあるファイルをキャッシュする
まずhttpブロックでキャッシュファイルを保存するパスとキャッシュのパラメータを設定する
http {
proxy_cache_path /var/nginx/cache keys_zone=static_assets:1m max_size=1g;
}
keys_zoneが1MBで8,000個のデータに関する情報を保存できるので, 1ファイル100KBと仮定すると800MB位になる
そんなに大きくないのでmax_sizeを全て格納できそうな1GBにする
次にserverブロックにリバースプロキシの設定を記述していく
upstream resource_server {
server resource.example.com;
}
server {
location /(css|js)/ {
proxy_pass http://resource_server;
proxy_cache static_assets;
proxy_cache_valid 200 10m;
add_header X-Cache-Hit $upstream_cache_status;
}
}
proxy_cache_pathの keys_zone
で指定したzone名を proxy_cache
に設定し, proxy_passに記述したサーバからのレスポンスを格納する
proxy_cache_validではレスポンスのステータスコードごとにキャッシュ時間の制御ができる
ここでは200が返って来たら10分間キャッシュするように設定している
キャッシュが使われたか等の情報は $upstream_cache_status
変数が持っているのでヘッダーとして出力すると確認できる
ドキュメントには値の意味が見当たらなかったが, Nginxのブログには書いてあった
ちょっと作りたいものがあるので, AndroidからDocker Desktop for Macで動かしているPostgreSQLにJDBC Driverで接続できるか実験
環境
- Android Studio 3.5.2
- Android Emulator 29.2.1
- Nexus 5
- PostgreSQL JDBC Driver 42.2.8
- postgres:11.5 Docker image
実験用ソース
Android Studioの Start a new Android Studio Project
から, Empty ActivityでMinimum API level 23なプロジェクトを作る
これに必要な分をちょっとだけいれたものを使う
まずはapp/build.gradleのdependenciesにドライバを追加する
dependencies {
// 略
implementation 'org.postgresql:postgresql:42.2.8'
// 略
}
次に適当な接続してSQLを実行するコードを, MainActivity.ktに追加する
override fun onResume() {
super.onResume()
thread {
val c = DriverManager.getConnection("jdbc:postgresql://10.0.2.2/postgres?user=postgres&password=password")
val s = c.createStatement()
val r = s.executeQuery("CREATE TABLE IF NOT EXISTS sample (id SERIAL PRIMARY KEY, number INTEGER)")
r.close()
s.close()
c.close()
}
}
ネットワークアクセスするのでパーミッションの記述をAndroidManifest.xmlに追加する
+ <uses-permission android:name="android.permission.INTERNET" />
Emulator と実機の比較
最近の端末を持っていないのでエミュレータを使うとして, 実機と同じように動作するかテストする
Android 6.0.1がインストールされたNexusとAPI 23のエミュレータで実行したところ, 同じように以下のExceptionが出力された
カスタマイズして出荷されている可能性があるので一概に言えないが, とりあえずエミュレータが実機と同じように動作すると判断する
E/AndroidRuntime: FATAL EXCEPTION: Thread-147
Process: com.example.sample, PID: 2812
java.sql.SQLException: No suitable driver
at java.sql.DriverManager.getConnection(DriverManager.java:186)
at java.sql.DriverManager.getConnection(DriverManager.java:144)
at com.example.sample.MainActivity$onResume$1.invoke(MainActivity.kt:19)
at com.example.sample.MainActivity$onResume$1.invoke(MainActivity.kt:8)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
実験
いろいろなバージョンで試してみる
とりあえず上の実験でAPI 23はダメだったので, それ以降を順次試す
API 24, 25はクラスが見つからないのでダメ
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/time/Duration;
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.time.Duration" on path: DexPathList[[zip file "/data/app/com.example.sample-1/base.apk"],nativeLibraryDirectories=[/data/app/com.example.sample-1/lib/x86_64, /system/lib64, /vendor/lib64]]
API 26から29はExceptionがthrowされているものの, SQLを実行した結果なのでOK
org.postgresql.util.PSQLException: No results were returned by the query.
追加実験 (追記: 2019/12/30)
API 24のExceptionはJava 8およびAPI 26で追加された java.time.Duration
を使っているからダメなだけで,
DriverをJava 7用にしたら問題なかったのではと思い立ったので実験
app/build.gradleのdependenciesを以下のように書き換える
dependencies {
// 略
- implementation 'org.postgresql:postgresql:42.2.8'
+ implementation 'org.postgresql:postgresql:42.2.8.jre7'
// 略
}
他は変更せずに動かしてみるとAPI 26以降と同じ org.postgresql.util.PSQLException
が投げられたので接続OK
結論
Android 7.0 NougatからJDBC DriverでPostgreSQLに接続できる
なおAndroid 7.0, 7.1 はJava 7用を使う必要があるが, Android 8.0 OreoからはJava 8用で構わない
ただし端末による
ネット上でGoogle App EngineのGo 1.9やPython 2.7 Runtimeの記事を見かけたが, Go 1.12ではどうにも思った通りにhandlerが設定できないのでいろいろ実験した記録
達成したいのは http://sample.appspot.com/
へのアクセスに対し, app.yamlからの相対パスで public/index.html
を返すこと
ドキュメントのサンプルに従えば以下のようにしておけばできるはず
handlers:
- url: /$
static_files: public/index\.html
upload: public/.*
- url: /(.*)
script: auto
ここではscript: autoのプログラムは Response From Go
と返し, index.htmlは Static File
と記載しているものとする
デプロイしてアクセスしてみると Response From Go
と表示される
おかしい…
デプロイしたものがおかしいのかGCPのウェブコンソールでApp Engineのバージョンページに行き, 設定を表示してみる
handlers:
- url: /$
static_files: public/index\.html
require_matching_file: false
upload: public/.*
- url: /(.*)
script: auto
- url: .*
script: auto
デプロイしたものに require_matching_file: false
が書き足されている
urlの正規表現がマッチしたら評価するが, 該当するファイルがなければ他のhandlerに処理を流すという要素なのだろうか…?
ドキュメントにもない要素でよくわからないので環境の方の調査をしてみる
Stackdriver Loggingでログを見てみると, /var/log/google_init.log に Waiting for network connection open. Subject:"nginx" Address:127.0.0.1:8080"
と記述がある
app.yamlはNginxの設定ファイルに変換されていそうなので実験してみる
今回は static_files
を指定していて url
は正規表現パターンマッチになるため, NginxではLocationディレクティブの正規表現パターンマッチと考えられる
static_files
はアップロードしたファイルパスの関連付けを行うためrootかaliasどちらかのディレクティブになるが, 正規表現でキャプチャした値を使用できるためaliasが該当する
そして実際の挙動と require_matching_file
という名称から try_files
でファイル指定と内部リダイレクトをしていると推測される
以上を元に以下のように変換した設定をDocker ComposeでNginxとGolangコンテナを使って実験してみる
http {
location ~ /$ {
alias public/index\.html;
try_files $uri @fallback;
}
location ~ /(.*)$ {
proxy_pass http://golang:8080;
}
location @fallback {
proxy_pass http://golang:8080;
}
}
App Engineと同じ出力になるため変換をしているという点は的外れではなさそう
意図しない挙動をした部分について踏み込んでみる
try_files
には他に適当な変数が見当たらないためとりあえず$uriを設定したが, 実際の値がわからないためログに出してみると /
になっている
変数を展開すると以下のようになることから, 該当するファイルが見つけられず @fallback
に内部リダイレクトされているものと思われる
$uriも含めて変換が上記の実験通りであれば意図した通りの動作に設定できない気がするので, 結局 http.FileServer()
で解決することにした
Nginxのaliasとtry_filesの部分を設定できるようになれば, App Engineのインスタンスを起動しなくてよくなるのに
Lighthouseでサイトの評価は見たかったものの, なんとなくChromeを端末にインストールしたくなかったのでコマンドラインで使ってみた
せっかくなのでコンテナとして動かしてみた
環境は
- macOS 10.15
- Docker Desktop 2.1.0.4
実践
LighthouseのCLIはnpmで提供されているので, とりあえず以下のDockerfileを使って
FROM node:10.17.0
RUN npm install -g lighthouse
こんなDocker Composeの構成にして
version: '3'
services:
nginx:
image: nginx:1.17.5
lighthouse:
build: ./
tty: yes
使ってみる
$ docker-compose up -d
$ docker-compose exec lighthouse lighthouse http://nginx/
~~~ 略 ~~~
Runtime error encountered: The environment variable CHROME_PATH must be set to executable of a build of Chromium version 54.0 or later.
~~~ 略 ~~~
実行にはChromeが必要だったので, ChromiumをインストールするようにDockerfileを修正し
- RUN npm install -g lighthouse
+ RUN apt-get update && \
+ apt-get install -y chromium && \
+ npm install -g lighthouse
リトライ
$ docker-compose down
$ docker rmi ${ディレクトリ名}_lighthouse:latest
$ docker-compose up -d
$ docker-compose exec lighthouse lighthouse http://nginx/
~~~ 略 ~~~
ChromeLauncher Waiting for browser....................................................................................................... +503ms
ChromeLauncher:error connect ECONNREFUSED 127.0.0.1:34953 +1ms
ChromeLauncher:error Logging contents of /tmp/lighthouse.luZObZw/chrome-err.log +0ms
ChromeLauncher:error [23:23:1027/054400.061948:ERROR:zygote_host_impl_linux.cc(89)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
ChromeLauncher:error +0ms
Chromeをrootで使う場合のオプション不足とエラーに出ているので追加してリトライ
ついでにGUIで起動しようとして失敗していると思ったのでheadlessのフラグも追加
$ docker-compose exec lighthouse lighthouse --chrome-flags="--no-sandbox --headless" http://nginx/
~~~ 略 ~~~
ChromeLauncher Waiting for browser.....✓ +3ms
config:warn IFrameElements gatherer requested, however no audit requires it. +171ms
config:warn MainDocumentContent gatherer requested, however no audit requires it. +1ms
status Connecting to browser +10ms
status Resetting state with about:blank +17ms
status Benchmarking machine +55ms
status Initializing… +512ms
status Resetting state with about:blank +44ms
status Setting up network for the pass trace +15ms
status Cleaning browser cache +4ms
status Beginning devtoolsLog and trace +10ms
method <= browser ERR:error Tracing.start +2ms
status Disconnecting from browser... +2ms
ChromeLauncher Killing Chrome instance 22 +1ms
Runtime error encountered: Protocol error (Tracing.start): 'Tracing.start' wasn't found
Error: Protocol error (Tracing.start): 'Tracing.start' wasn't found
~~~ 略 ~~~
Chromeが起動して実行できたけどエラーが出る
検索してみるとDebianの問題とのこと
Node.jsのコンテナイメージがDebianベースなので, Ubuntuベースにした以下のDockerfileに切り替えて
FROM ubuntu:bionic-20191010
RUN apt-get update && \
apt-get install -y curl ca-certificates && \
curl -sL https://deb.nodesource.com/setup_10.x | bash - && \
apt-get install -y nodejs chromium-browser && \
npm install -g lighthouse
再チャレンジ
$ docker-compose down
$ docker rmi ${ディレクトリ名}_lighthouse:latest
$ docker-compose up -d
$ docker-compose exec lighthouse lighthouse --chrome-flags="--no-sandbox --headless" http://nginx/
~~~ 略 ~~~
status Generating results... +1ms
Printer html output written to /nginx_2019-10-28_20-00-00.report.html +121ms
CLI Protip: Run lighthouse with `--view` to immediately open the HTML report in your browser +0ms
ChromeLauncher Killing Chrome instance 32 +0ms
正常終了したので結果のHTMLをコンテナから回収して見てみるとちゃんと評価できていた
$ docker-compose exec lighthouse cat /nginx_2019-10-28_20-00-00.report.html > ./report.html
感想
雑にDockerfileを書いた割にNode.jsのイメージよりコンテナが小さかったのは意外