Docker volumeパフォーマンスチューニング

・Docker Desktop for Macでマウントしたボリュームはアクセス性能が低いと言われている。
・Docker 17.03から導入されたマウントオプションを使うことで改善できる。

## そもそもなぜ遅いか
Linuxではホストとコンテナ間でVFSを基盤にしているのでファイル同期によるオーバーヘッドがない。
macOSではホストとコンテナで完全な一貫性を保つために著しいオーバーヘッドがある。

ファイル更新時の同期に時間がかかるだけで読み込みには時間がかからないということか。

## チューニング方針
完全な一貫性を保つとするとオーバーヘッドで遅くなる。
一貫性が必要ないケースは多々ある。そのため一貫性を犠牲にすることで性能を上げる。

## マウント設定
Dockerでマウント指定する際にマウント方法を指定することで一貫性を取るか性能を取るか決められる。

・consistent : ホストとコンテナで完全に同期する。
・cached : ホストを更新した時に遅延してコンテナに反映される
・delegated(委任)　：コンテナの更新時に遅延してホストに反映される

-vオプションのコンテナpathの最後に:をつけて上記のパラメータを付与することで使用可能。

```:例
$ docker run -v /host/hoge:/container/hoge:cached sample
複数のボリュームで別の指定をすることも可能

$ docker run -v /host/1:/container/1:cached -v /host/2:/container/2:delegated sample
```

## delegated
一番性能が上がる。ホストは変更せず、コンテナ側から一方的に変更するものに使用するのが良さそう。
コンテナのファイル状態を正とし、コンテナを修正してもホストに遅れて反映される。
破損してもokな一時的なファイルや再生成可能なビルド物に向いている。
delegatedでマウントしている場所をcachedやconsistentでマウントするとその場所はそれぞれの指定に従う。
ホスト側で変更をしても、コンテナ側の同期によってその変更が消える可能性がある。

## cached
ホストを正にする。ホストの書き込みはコンテナにすぐに同期されるが、コンテナの変更は遅延してホストに同期される。
cachedでマウントしている箇所をconsistentでマウントするとconsistentの挙動になる。

## consistent
ホストとコンテナが完全に同期する。
デフォルトでこの指定。


## パフォーマンス測定
Laravelプロジェクトをマウントしマウント設定を変えてパフォーマンスを計測する。

まずはローカルで直接artisan serveを起動した状態。1秒で29リクエスト捌ける。

```
$ ab -c 5 -n 25 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient).....done


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17473 bytes

Concurrency Level:      5
Time taken for tests:   0.855 seconds
Complete requests:      25
Failed requests:        0
Total transferred:      465475 bytes
HTML transferred:       436825 bytes
Requests per second:    29.22 [#/sec] (mean)
Time per request:       171.097 [ms] (mean)
Time per request:       34.219 [ms] (mean, across all concurrent requests)
Transfer rate:          531.35 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    43  148  35.2    161     179
Waiting:       41  147  35.4    159     177
Total:         43  149  35.2    161     179

Percentage of the requests served within a certain time (ms)
  50%    161
  66%    162
  75%    164
  80%    166
  90%    172
  95%    174
  98%    179
  99%    179
 100%    179 (longest request)
```

コンテナ側に/appでマウントすると、

```
$ tree -L 2
.
├── Dockerfile
└── laravel
    ├── README.md
    ├── app
    ├── artisan
    ├── bootstrap
    ├── composer.json
    ├── composer.lock
    ├── config
    ├── database
    ├── docker-compose.yml
    ├── package.json
    ├── phpunit.xml
    ├── public
    ├── resources
    ├── routes
    ├── server.php
    ├── storage
    ├── tests
    ├── vendor
    └── webpack.mix.js
```

```Dockerfile
FROM centos:centos8

RUN dnf module install -y php:7.4

WORKDIR /app
CMD [ "php", "artisan", "serve", "--port=80", "--host=0.0.0.0" ]
```


マウントする

```
$ docker run -it --rm -v $(pwd)/laravel:/app/ -p 8080:80 ro_test 
```

この状態で5人が5回、合計25アクセスした時のパフォーマンス

```
$ ab -c 5 -n 25 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        17473 bytes

Concurrency Level:      5
Time taken for tests:   8.982 seconds
Complete requests:      25
Failed requests:        0
Total transferred:      465475 bytes
HTML transferred:       436825 bytes
Requests per second:    2.78 [#/sec] (mean)
Time per request:       1796.408 [ms] (mean)
Time per request:       359.282 [ms] (mean, across all concurrent requests)
Transfer rate:          50.61 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   373 1578 436.6   1755    1863
Waiting:      373 1578 436.7   1755    1862
Total:        373 1578 436.6   1755    1863

Percentage of the requests served within a certain time (ms)
  50%   1752
  66%   1769
  75%   1778
  80%   1804
  90%   1810
  95%   1825
  98%   1863
  99%   1863
 100%   1863 (longest request)
```

## パフォーマンス cached
cachedなので若干パフォーマンスが上がるはず

```
$ docker run -it --rm -v $(pwd)/laravel:/app/:cached -p 8080:80 ro_test 
```

計測。あまり変わらない。。。

```
$ ab -c 5 -n 25 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        17473 bytes

Concurrency Level:      5
Time taken for tests:   8.948 seconds
Complete requests:      25
Failed requests:        0
Total transferred:      465475 bytes
HTML transferred:       436825 bytes
Requests per second:    2.79 [#/sec] (mean)
Time per request:       1789.501 [ms] (mean)
Time per request:       357.900 [ms] (mean, across all concurrent requests)
Transfer rate:          50.80 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   353 1571 435.7   1750    1813
Waiting:      353 1571 435.8   1749    1813
Total:        353 1571 435.7   1750    1814

Percentage of the requests served within a certain time (ms)
  50%   1749
  66%   1762
  75%   1777
  80%   1791
  90%   1795
  95%   1812
  98%   1814
  99%   1814
 100%   1814 (longest request)
```

## パフォーマンス delegated

```
$ docker run -it --rm -v $(pwd)/laravel:/app/:delegated -p 8080:80 ro_test 
```

計測。これもあんまり変わらない。むしろ遅くなっている。

```
$ ab -c 5 -n 25 http://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        17473 bytes

Concurrency Level:      5
Time taken for tests:   9.187 seconds
Complete requests:      25
Failed requests:        0
Total transferred:      465475 bytes
HTML transferred:       436825 bytes
Requests per second:    2.72 [#/sec] (mean)
Time per request:       1837.459 [ms] (mean)
Time per request:       367.492 [ms] (mean, across all concurrent requests)
Transfer rate:          49.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   391 1582 403.0   1740    1843
Waiting:      391 1581 402.9   1739    1843
Total:        391 1582 403.0   1740    1843

Percentage of the requests served within a certain time (ms)
  50%   1739
  66%   1753
  75%   1765
  80%   1770
  90%   1841
  95%   1842
  98%   1843
  99%   1843
 100%   1843 (longest request)
```

## コードをコンテナの中に入れる

```dockerfile
FROM centos:centos8

RUN dnf module install -y php:7.4

COPY ./laravel /app

WORKDIR /app
CMD ["php", "artisan", "serve", "--host=0.0.0.0"]

```

計測。コンテナ使う上ではこれが一番速い。秒間15リクエスト捌ける。

```
ab -c 5 -n 25 http://localhost:8080/  
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        17473 bytes

Concurrency Level:      5
Time taken for tests:   1.682 seconds
Complete requests:      25
Failed requests:        0
Total transferred:      465475 bytes
HTML transferred:       436825 bytes
Requests per second:    14.86 [#/sec] (mean)
Time per request:       336.380 [ms] (mean)
Time per request:       67.276 [ms] (mean, across all concurrent requests)
Transfer rate:          270.27 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    94  295  70.8    316     355
Waiting:       93  294  71.0    315     354
Total:         94  295  70.7    316     355

Percentage of the requests served within a certain time (ms)
  50%    315
  66%    328
  75%    331
  80%    333
  90%    345
  95%    348
  98%    355
  99%    355
 100%    355 (longest request)
```

## 書き込みパフォーマンス
ファイル書き込み時のパフォーマンスをチェックする

ローカルで計測した場合

```
$ time (for i in $(seq 1 10000);do echo $i >> a.txt; done)
========================
Program : ( for i in $(seq 1 10000); do; echo $i >> a.txt; done; )
CPU     : 88%
user    : 0.110s
system  : 0.550s
total   : 0.746s
========================

```

consistentでマウントした場合。
コンテナで書き込むと0.220s。とても遅い。
ホストで書き込むと0.119s。
同じくらいかかると思ったけどコンテナの方が遅い。

```
$ docker run -it --rm -v $(pwd)/fuga:/fuga centos:centos8

[root@29e3d05941ce fuga]# time (for i in $(seq 1 10000);do echo $i >> a.txt; done)

real	0m11.249s
user	0m0.220s
sys	0m0.700s

$ time (for i in $(seq 1 10000);do echo $i >> b.txt; done)

========================
Program : ( for i in $(seq 1 10000); do; echo $i >> b.txt; done; )
CPU     : 88%
user    : 0.119s
system  : 0.608s
total   : 0.817s
========================
```

## 書き込みパフォーマンス cached

```
$ docker run -it --rm -v $(pwd)/fuga:/fuga:cached centos:centos8

[root@53b5e0d31c60 fuga]# time (for i in $(seq 1 10000);do echo $i >> aa.txt; done)

real	0m13.132s
user	0m0.271s
sys	0m0.804s
```

↓ホスト側。consistentの時より若干早くなっている

```
time (for i in $(seq 1 10000);do echo $i >> ab.txt; done)


========================
Program : ( for i in $(seq 1 10000); do; echo $i >> ab.txt; done; )
CPU     : 89%
user    : 0.119s
system  : 0.597s
total   : 0.800s
========================
```

## 書き込みパフォーマンス delegated
cachedより速くなってる。

```
$ docker run -it --rm -v $(pwd)/fuga:/fuga:delegated centos:centos8
[root@e8334765081e fuga]# time (for i in $(seq 1 10000);do echo $i >> ddd.txt; done)

real	0m11.714s
user	0m0.191s
sys	0m0.780s
```

↓ホスト側。cchedより遅い・・・？

```
$ time (for i in $(seq 1 10000);do echo $i >> affff.txt; done)

========================
Program : ( for i in $(seq 1 10000); do; echo $i >> affff.txt; done; )
CPU     : 87%
user    : 0.123s
system  : 0.616s
total   : 0.849s
========================
```


参考
https://docs.docker.jp/docker-for-mac/osxfs-caching.html