Docker 環境に Sftp コンテナを構築して Laravel と連携する

[Laravel2 Advent Calendar 2019 - Qiita](https://qiita.com/advent-calendar/2019/laravel-2) の 9日目 の記事です。

## サンプルリポジトリ

https://github.com/ucan-lab/laravel6-sftp

## 前準備

まずはLaravelの環境を用意します。

```
$ git clone git@github.com:ucan-lab/docker-laravel.git laravel6-sftp
$ cd laravel6-sftp
$ make create-project
```

http://127.0.0.1:10080

できました。

詳しい構築方法は過去記事を参考ください。

- [【忙しい人向け】カップ麺より早く作るDockerでLaravel開発環境構築](https://qiita.com/ucan-lab/items/d7752b43a9e89cc16b22)
- [Laravelの開発環境をDockerを使って構築する](https://qiita.com/ucan-lab/items/17c806973e69792ada99)
- [【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン](https://qiita.com/ucan-lab/items/17c806973e69792ada99)

## Sftpコンテナを作成する

ベースコンテナは [atmoz/sftp](https://hub.docker.com/r/atmoz/sftp) 使用します。

### 対向先のディレクトリを作成する

```shell-session
$ mkdir sftp-store
$ echo "hello" > sftp-store/world.txt
```

### 認証用の公開鍵、秘密鍵を作成する

```shell-session
$ mkdir .ssh
$ ssh-keygen -t rsa -b 4096 -N "" -f .ssh/ssh_host_rsa_key
```

### Git管理対象外設定をする

`.gitignore`

```
/.ssh
/sftp-store
```

鍵ファイルやSftp内のデータはGit管理したくないため。

### docker-compose.yml

```yaml
services:
  app:
    volumes:
      - ./.ssh/ssh_host_rsa_key:/root/.ssh/ssh_host_rsa_key

  sftp-server:
    image: atmoz/sftp
    volumes:
      - ./sftp-store:/home/foo/share
      - ./.ssh/ssh_host_rsa_key.pub:/home/foo/.ssh/keys/ssh_host_rsa_key.pub
    command: foo::1001
```

`docker-compose.yml` の差分を抜き出してます。
`sftp-server` に公開鍵を配置して、共有ディレクトリ(`/home/foo/share`)にローカルディレクリ(`./sftp-store`) をマウントします。

### Sftp コンテナを構築

```shell-session
$ docker-compose down
$ docker-compose up -d --build
```

## Laravel で Sftp 設定を行う

```shell-session
$ docker-compose exec app ash
```

`app` コンテナ内で実行します。

### Sftpライブラリ導入

```shell-session
$ composer require league/flysystem-sftp
```

- https://github.com/thephpleague/flysystem-sftp

### SftpServiceProvider を作成する

```shell-session
$ php artisan make:provider SftpServiceProvider
```

`app/Providers/SftpServiceProvider.php`

```php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;

class SftpServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Storage::extend('sftp', function ($app, $config) {
            return new Filesystem(new SftpAdapter($config));
        });
    }
}
```

Filesystem に sftp 用のアダプタを追加します。

### 作成した SftpServiceProvider を登録する

`config/app.php`

```php
    'providers' => [
        // ...

        App\Providers\SftpServiceProvider::class,
    ],
```

### Sftp ディスク設定を追加する

`config/filesystems.php`

```php
    'disks' => [
        'sftp-disk' => [
            'driver' => 'sftp',
            'host' => 'sftp-server',
            'port' => 22,
            'username' => 'foo',
            'privateKey' => '/root/.ssh/ssh_host_rsa_key',
            'root' => 'share',
            'timeout' => 10,
            'directoryPerm' => 0755,
        ],
    ],
```

## お試し

```shell-session
$ php artisan tinker
>>> Storage::disk('sftp-disk')->get('world.txt');
=> "hello\n"
```

## 差分のコード

- https://github.com/ucan-lab/laravel6-sftp/pull/1
- https://github.com/ucan-lab/laravel6-sftp/pull/2

## 参考

- https://hub.docker.com/r/atmoz/sftp
- https://flysystem.thephpleague.com/docs/adapter/sftp
- https://github.com/thephpleague/flysystem-sftp
- https://readouble.com/laravel/6.0/ja/filesystem.html#custom-filesystems