VaporのプロジェクトをFlockを使ってEC2にデプロイする

# はじめに

Server Side Swift の開発をより実践的にできればと思い、CapistranoライクなServer Side Swiftフレームワークのデプロイツールである **Flock** を使ってみました。
今回の投稿では **Vapor** で作ったプロジェクトのEC2サーバーへのデプロイを行います。

### - [Flock](https://github.com/jakeheis/Flock)

- Capistoranoライクな Server Side Swift のフレームワークデプロイツール
- Vapor, Kitura, Zewo, Perfect に対応しているよう

### - [Vapor](https://github.com/vapor/vapor)

- Server Side Swift の Webフレームワーク

# 手順概要

1. Vaporプロジェクトの作成
2. EC2でUbuntuサーバー（デプロイ先サーバー）の作成
3. デプロイ先サーバーでのNginxの設定
4. デプロイ先サーバーでのsupervisorの設定
5. デプロイ先サーバーでのその他デプロイに必要な設定
6. ローカルmacでFlockの設定
7. いざ、デプロイ

# 環境

- **開発機**
    - macOS Sierra 10.12.1
    - Swift 3.0.1
    - Vapor 1.1.13
    - Flock 0.1.1
- **デプロイ先サーバー**
    - Ubuntu 16.04
    - Swift 3.0.1

# 1. Vaporプロジェクトの作成

## ▶︎ ローカルmacにプロジェクト作成

まずは開発用のローカルmacにVaporプロジェクトを作成します。
今回は、デプロイ部分を中心に見ていくので、VaporのインストールやVapor自体の説明は割愛します。
（詳しくは[VaporのDocument](https://vapor.github.io/documentation/)をご覧ください。）

```bash
$ vapor new vaporDeploySample
```

※`vaporDeploySample`部分はプロジェクト名になるので、適宜変更してください。

## ▶︎ GithubへPush

今回は特にプロジェクトはいじらずにそのままデプロイ設定の手順に進むので、一旦GithubにPushしておきます。

GithubのリポジトリのURLを後述のデプロイ設定の際に使用します。

# 2. EC2でUbuntuサーバー（デプロイ先サーバー）の作成

EC2でなくても大丈夫なのですが、Vagrantで`Ubuntu14.04`で試した時に何かと面倒があったので、EC2で`Ubuntu16.04`を立てて行いました。

## ▶︎ EC2の作成

### AMIの選択

`Ubuntu Server 16.04 ...`のものを選択して作成を進めていきます。

![スクリーンショット 2016-12-11 14.14.09.png](https://qiita-image-store.s3.amazonaws.com/0/11914/4268a399-9921-8001-2885-97592ca7b7e4.png)

### キーペア

キーペアを新しく作成する場合は、

- 作成したキーをダウンロード
- 手元のmacの`~/.ssh`の下に配置
- `chmod 600 ~/.ssh/your-key-pair.pem`

としておきます。

### セキュリティグループの設定

セキュリティ設定では、今回の作業ではSSHとHTTPが開いていれば十分です。
下の画像では送信元が「任意の場所」になっていますが、手元で試すだけなら「マイIP」を選択して自分のIPからのみアクセスできる状態にしておいた方が安心です。

![スクリーンショット 2016-12-11 14.17.59.png](https://qiita-image-store.s3.amazonaws.com/0/11914/be89e17b-f484-8de4-9658-e22cc372d385.png)


# 3. デプロイ先サーバーでのNginxの設定

## ▶︎ サーバーへ接続

先ほど作成したサーバーへ接続して、作業を行います。

```bash
$ ssh -i ~/.ssh/your-key-pair.pem ubuntu@publicdns
```

`publicdns`部分は作成したインスタンスのパブリックDNSが入ります。


## ▶︎ Nginxインストール

サーバーへログインできたら、Nginxをインストールします。

```
$ sudo apt-get update
$ sudo apt-get install nginx
```

## ▶︎ Nginxの設定

`/etc/nginx/sites-enabled/default`ファイルに以下のような設定を書きます。

```:/etc/nginx/sites-enabled/default
server {
    server_name localhost;
    listen 80;

    try_files $uri @proxy;

    location @proxy {
        proxy_pass http://127.0.0.1:8080;
        proxy_pass_header Server;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass_header Server;
        proxy_connect_timeout 3s;
        proxy_read_timeout 10s;
    }
}
```

```bash
$ sudo service nginx restart
```


これで、localhostの80番ポートへのアクセスをVaporのプロジェクトがデフォルトでバインドする8080番へ流してくれます。

# 4. デプロイ先サーバーでのsupervisorの設定

続いて、プロセス監視のため、supervisorをインストールします。

```bash
$ sudo apt-get update
$ sudo apt-get install supervisor
```

こちらの設定は後述のFlockを使って行うので、まずはインストールまででokです。

（※このタイミングでインストールしていなくても、後述の`flock tools`タスクの中でインストールされそうですが、試行錯誤していたので確認できていません。もし良ければお試しください。）

# 5. デプロイ先サーバーでのその他デプロイに必要な設定

## ▶︎ `/var/www` ディレクトリの作成

Flockでは `/var/www` ディレクトリにデプロイされるので、もしない場合は作っておきます。
（EC2ではデフォルトであるようなので、この工程は不要です。）
また、後述の設定でデプロイ先のディレクトリの変更も可能です。

```bash
$ sudo mkdir /var/www
# ↑本来はwwwユーザーとか作ってそのユーザーのみにwww以下を触らせるのが良さそうですが、一旦そのまま動かします
```

## ▶︎ rootでのログイン

あまり気が進まないですが、Flockを使っていてrootでのログインができないと困ることが多々でてきたので、rootでのログイン設定をします。
（適切なパーミッションを持ったユーザーを使ったり、Flock自体の改善でrootでのログインは回避できるかもしれませんが、一旦簡単のためにそうします。）

```bash
$ mv /root/.ssh/authorized_keys /root/.ssh/authorized_keys_bak
$ cp -f /home/ubuntu/.ssh/authorized_keys /root/.ssh/authorized_keys
```

authorized_keysをコピーすることで、手元のmacからキーペアを使ってrootユーザーでログインすることができます。

# 6. ローカルmacでFlockの設定

それでは、いよいよデプロイをする準備をしていきます。

## ▶︎ Flockのインストール

```bash
$ brew install jakeheis/repo/flock
```

## ▶︎ プロジェクト直下でFlockの初期化

```bash
$ cd /path/to/your/project
$ flock --init
```

```bash
Creating Flock files...
Creating .flock
Creating config/deploy
Writing .flock/Package.swift
Writing config/deploy/FlockDependencies.json
Writing Flockfile
Linking Flockfile to .flock/main.swift
Creating environment `always`
Writing config/deploy/Always.swift
Linking config/deploy/Always.swift to .flock/Always.swift
Creating environment `production`
Writing config/deploy/Production.swift
Linking config/deploy/Production.swift to .flock/Production.swift
Creating environment `staging`
Writing config/deploy/Staging.swift
Linking config/deploy/Staging.swift to .flock/Staging.swift
Successfully created Flock files
Downloading and building dependencies...
Successfully downloaded dependencies
Adding Flock files to .gitignore...
Successfully added Flock files to .gitignore
Successfully initialized Flock!

IN ORDER FOR FLOCK TO WORK CORRECTLY
Follow these steps:
1. Update the required fields in config/deploy/Always.swift
2. Add your production and staging servers to config/deploy/Production.swift and config/deploy/Staging.swift respectively

To add Flock dependencies:
1. Add the url and version of the dependency to config/deploy/FlockDependencies.json
2. in your Flockfile, at the top of the file import your dependency and below add `Flock.use(Dependency)`

```

これによって、`Flockfile`というファイルと`Config/`下に`deploy`というディレクトリが作られ、deployに必要な設定を行うことができるようになります。


## ▶︎ `Config/deploy/FlockDependencies.json` の編集

Vaporデプロイ用に[VaporFlock](https://github.com/jakeheis/VaporFlock)を使うので、追記します。

以下のファイルが編集後のものです。
`https://github.com/jakeheis/VaporFlock`のブロックを追加しています。

```json:Config/deploy/FlockDependencies.json
{
   "dependencies" : [ 
       {   
           "url" : "https://github.com/jakeheis/Flock",
           "version": "0.1.1"
       },  
       {   
           "url" : "https://github.com/jakeheis/VaporFlock",
           "version": "0.0.4"
       }   
   ]   
}
```

ここでの依存関係のバージョンが食い違うとエラーが起こるので、注意が必要です。

例えば、VaporFlockのversionを0.0.3とすると、VaporFlock v0.0.3 はソース上(Package.swift)では Flock v0.0.x に依存しているため、このファイルの記述と食い違いが起こります。


## ▶︎ `Flockfile` の編集

以下が編集後の`Flockfile`。

```swift:Flockfile
import Flock
import VaporFlock

Flock.use(Flock.Tools)
Flock.use(Flock.Deploy)
Flock.use(Flock.Vapor)

Flock.configure(.always, with: Always()) // Located at config/deploy/Always.swift
Flock.configure(.env("production"), with: Production()) // Located at config/deploy/Production.swift
Flock.configure(.env("staging"), with: Staging()) // Located at config/deploy/Staging.swift

Flock.run()
```

## ▶︎ `Config/deploy/Always.swift` の編集

環境に依らない、共通の設定をこのファイルに書きます。

- Config.projectName
- Config.executableName
- Config.repoURL
- Config.swiftVersion

の値をプロジェクトに応じて変更します。

```swift:Config/deploy/Always.swift
import Flock

class Always: Configuration {
    func configure() {
        // UPDATE THESE VALUES BEFORE USING FLOCK:
        Config.projectName = "vaporDeploySample"
        Config.executableName = "App" // Same as Config.projectName unless your project is divided into modules
        Config.repoURL = "https://github.com/taji-taji/vaporDeploySample"
    
        // Optional config:
        // Config.deployDirectory = "/var/www"
        Config.swiftVersion = "https://swift.org/builds/swift-3.0.1-release/ubuntu1510/swift-3.0.1-RELEASE/swift-3.0.1-RELEASE-ubuntu15.10.tar.gz"
    }   
}
```

`Config.executableName`は、プロジェクトがビルドされて生成される実行ファイルの名前を指すようで、コメントには `Same as Config.projectName unless your project is divided into modules` と書いていますが、私が行った際には実行ファイルは`App`になっていたので、そのようにしました。
フレームワークによって違うのかもしれません。
（これが分からなくてしばらくハマりました、、）

## ▶︎ Ubuntuサーバーに接続するsshキーを環境変数に設定

Flockのコード内からUbuntuサーバー作成時に作成したキーペアを環境変数を通して読み込むので、環境変数への設定を行います。

```bash
$ export FLOCK_KEY=$HOME/.ssh/your-key-pair.pem
```


## `Config/deploy/Production.swift` の編集

ここで、デプロイ先のIPやそこに接続するためのsshキー（先ほど設定したもの）の設定を行います。

```swift:Config/deploy/Production.swift
import Flock
import Foundation

class Production: Configuration {
    func configure() {
        if let key = ProcessInfo.processInfo.environment["FLOCK_KEY"] {
            Config.SSHAuthMethod = .key(key)
        }   
        // using vagrant private network ip
        Servers.add(ip: "ec2-ip", user: "root", roles: [.app, .db, .web])
    }   
}
```

`"ec2-ip"`部分にはEC2のパブリックIPを設定します。
`ProcessInfo.processInfo.environment["FLOCK_KEY"]`の部分で先ほど環境変数に設定したキーペアの場所が読まれています。

### 注意点
デフォルトで`Foundation`フレームワークがimportされていなかったため、`ProcessInfo`の使用で怒られました。
環境変数を読むときは`import Foundation`を追記しましょう。


# デプロイ

## ▶︎ `flock tools` コマンド

初回は`flock tools`コマンドでデプロイ先のサーバーにSwiftと依存するライブラリをインストールします。

```bash
$ flock tools
```

この`flock tools`コマンドでは、

1. デプロイ先サーバーにデプロイに必要な依存ライブラリをインストール
- デプロイ先サーバーにswiftenvのインストールおよびswiftenvによるSwiftをインストール

という２つのフェーズに分かれており、それぞれ、

```bash
flock tools:dependencies
flock tools:swift
```

というコマンドで別々に実行することが可能です。


## ▶︎ `flock deploy`コマンド

いよいよデプロイです。

```bash
$ flock deploy
```

こちらも

1. gitからソースコードをcloneする
2. コードをbuildする
3. デプロイした最新のコードがあるディレクトリにリンクを貼る

というフェーズに分かれており、それぞれ下のコマンドで別々に実行できます。

```bash
flock deploy:git
flock deploy:build
flock deploy:link
```


### 補足：デプロイ先ディレクトリについて

```bash
/var/www/vaporDeploySample/
├── current -> /var/www/vaporDeploySample/releases/20161211181262
└── releases
    ├── 20161211181234
    └── 20161211181262
```

Capistranoを使っている方には馴染みがあるかと思いますが、デプロイ先のディレクトリ構成には **`current`** と **`releases`** というディレクトリがあります。
デプロイしたコードを`releases`に貯めていき、最新のものを`current`にリンクするようにしています。
もしデプロイしたものに異常があってもリンクを差し替えるだけでデプロイ前の状態に戻すことが可能です。

---

以上でデプロイの手順は完了となります。
お疲れ様でした。

一度上記の設定を行えば、以降は`flock deploy`コマンドのみでデプロイができます。

# 今回作成したサンプルアプリケーション

今回作成したサンプルアプリケーションです。
Vaporプロジェクトはデフォルトのテンプレートのまま、「`Flockfile`」および「`Config/deploy`」以下のdeploy用の設定ファイルが追加された形になります。

https://github.com/taji-taji/vaporDeploySample


# 感想

普段、Capistranoを使っている方にとっては学習コストも低く、とても便利に使えるツールだと思います。
一方で、Server Side Swift そのものがそうであるように、このような付随するツールもまだ安定していないものが多かったりします。
なので、こういったツールへの貢献ができるようにというのが今後の自分の課題です。

Server Side でも Swift を楽しめるといいですね！:grinning:


# 最後に

とりあえず、今回は「デプロイをする」ことのみに絞ってみましたが、これから、もう少し機能的にどんなことができるかなどを深掘りしてみたいと思います。