【Docker Compose】プロジェクト名を指定していなくてハマった話

デフォルトに甘えてComposeのプロジェクト管理を意識せずにいたら、いざという時に意図通りのコンテナ操作ができずにハマったという話です。

# 前のコンテナを止められない
ある日、僕はとあるサービスのデプロイをしました。
プロジェクトをDocker Composeでまとめ、ssh,pull等のデプロイ作業を1行のコマンドにまとめた。
はじめは動いていましたが…

次のデプロイでコンテナ起動時に問題発生。

```sh
ERROR: {省略} Bind for 0.0.0.0:3000 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.
# 本番にsshして確認
$ docker ps
{省略} days ago  0.0.0.0:3000->3000/tcp                     0200122040412_1
```
前のコンテナが起動したままになっており、ポートが被って起動できないというエラーです。
原因はわかりませんが、とりあえずstop。

```sh
$ docker-compose stop
$ docker ps
{省略} days ago  0.0.0.0:3000->3000/tcp                     0200122040412_1
```
 **残ってる…** 
## コンテナとdocker-compose.yamlの関係はどうやって決まる？
ここで`docker-compose`ではなく`docker`コマンドでコンテナを止めるのは簡単ですが、ちょっと待ってください。
普通は同じプロジェクトの`docker-compose.yaml`を使えば、再起動時に自動で前のコンテナをシャットダウンしてくれます。くれてました。
なぜその対応関係が切れてしまったんでしょうか。

コンテナかDockerが記憶しているのかと思いましたが、そんな記述は見当たりません。
現実は**もっとシンプルだった。**


# Docker Composeのプロジェクト名とは
Docker Composeはプロジェクト単位でコンテナを操作します。
プロジェクト（とコンテナ）にはプロジェクト名が付き、コマンド操作(up,run,stop,etc)はそれを参照して行われる。

で、このプロジェクト名は**別に設定しなくても動く。** 
>このプロジェクト名の設定はオプションです。設定をしなければ、 COMPOSE_PROJECT_NAME （Composeのプロジェクト名）は、デフォルトではプロジェクトのディレクトリを ベース名 にします。
http://docs.docker.jp/v1.12/compose/reference/envvars.html

デフォルトでディレクトリ名になる。

## プロジェクト名を設定しないとどうなる？
[1] 複数のプロジェクトでディレクトリ名がダブると、意図しないコンテナを操作してしまう
（例：日付、バージョン、"app", etc…）
[2] ディレクトリ名を変えるとプロジェクト名が変わり、以前そのディレクトリ内の`docker-compose.yaml`で起動したコンテナを操作できなくなる

こういう問題が発生します！！！

## 実例：Capistranoでデプロイ
Capistranoはruby製のデプロイ自動化gemで、標準でバージョン管理もしてくれる優れもの。
別にRails専用というわけではないので今回はssh→pull→up -d --buildとかする用途で使っていた。
…が、今回このバージョン管理機能が仇になった。

### Capistranoのバージョン管理機能とは
Capistranoのデプロイは、Datetime名のディレクトリを都度作成して行われる。

```bash
releases]$ ls
20200122033238  20200122033501  20200122040412  20200122041011  20200122041121
```

その上で、デプロイ先の指定ディレクトリ「標準では`current`」は最新バージョンへのsymlinkになる。

```bash
app]$ ls
current # ←これが実際には20200122041121へのsymlink
```
まァ、それは良いのだが、この状態で`docker-compose up`すると…

```bash
current]$ docker-compose up -d
Starting 20200122033501_db_1    ... done
Starting 20200122033501_web_1         ... done
```

 **symlink先の正式なディレクトリ名（＝日時のほう）が付けられて起動する。** 

### こうなるともうだめだ
次にCapでデプロイを行うと、それはまた先の日時のディレクトリ名に置かれる。
つまり、 **もう別のプロジェクト名になってしまう。** 
`docker-compose up`しても古いコンテナを自動終了してくれず、このようにポートダブりエラーが出る。

```bash
(前略 )connectivity on endpoint 20200122041121_web_1 (中略): Bind for 0.0.0.0:3000 failed: port is already allocated
```

今まで何も考えずに使っていても勝手に古いコンテナを操作できていたのは、ディレクトリ名が同じだったからなんですね。
Capが日時でディレクトリを切るから、それが転記された「プロジェクト名」が固有のものって感覚が全然なかったよ…（言い訳、責任転嫁）

 **プロジェクト名はちゃんと設定しよう。** 

ちなみに、コンテナをすべて削除するコマンドはこう。

```bash
$ docker rm $(docker ps -qa) -f
```

-fをつけなかった場合は起動中のものは生き残る。

# 解決策：プロジェクト名の設定方法
というわけでプロジェクト名を設定します。 yamlファイルに直接は書けません。

## .envファイル
docker-compose.yamlと同じ場所に置けばOK。

```:.env
COMPOSE_PROJECT_NAME=hogefuga_project
```

ただ、.envファイルはそうgit pushするものではないし、docker-composeだけが使うものでもないことに注意。

## コマンドライン引数
docker-composeコマンドに`-p`を付けることで、プロジェクト名を指定できる。

```bash
docker-compose -p hoge_project up
```

ただ、毎回引数にプロジェクト名を付けるのは冗長なので、このコマンド自体を何かに登録する前提だろう。
Capistranoのようなデプロイ自動化スクリプトを使用する場合はどんどん使っていこう。

```ruby:deploy.rb
namespace :container do
  desc 'Up containers'
  task :up do
    on roles(:docker) do
      execute "cd #{release_path} && docker-compose -p hoge_project up -d"
    end
  end
end
```