複数Composeファイルを用いて同一のdocker network に同一サービス名の別アプリを立ち上げたときの動作

## 前提
誰も気になってないのかもしれないけど、ピンポイントの情報が見つけられなかったので

### 動作確認環境
windows 10
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293810/285d2765-ea9f-9663-c8f4-2b030830ade6.png)

### サービス名
本記事では、以下に示す部分で指定した名称をサービス名と称します

```{yml:docker-compose.yml}
version: '3.8'
services:
  srv1: # <- ここ！
    image: node:14
```

### 単一Composeファイルの場合
単一Composeファイルでは、同じサービス名を定義すると
後に定義された側が優先され、同時に起動することはありません

```{yml:docker-compose.yml}
version: '3.8'
services:
  srv1:
    image: node:14
    container_name: node14
  
  srv1:
    image: node:12
    container_name: node12
```
```{txt:実行結果}
$ docker compose up
[+] Running 2/2
 ⠿ Network compose_demo_default  Created                                                  0.0s
 ⠿ Container node12              Started                                                  2.3s
Attaching to node12
node12 exited with code 0
```

### 複数Composeファイルで、同一のdocker networkに所属させる
同一フォルダ階層にある複数のComposeファイルにて
同名のサービスを立ち上げようとした場合も、
上記と同様の結果となります（後からcompose upしたものにRecreateされる）

そのため、Composeファイルを別フォルダに置くか、
実行時に`-p`オプションを用いて別プロジェクトとして認識させる必要があります。

```{yml:docker-compose.app1.yml}
version: '3.8'
networks:
  default:
    name: local_link
services:
  srv1:
    image: nginx:latest
    container_name: app1
```
```{yml:docker-compose.app2.yml}
version: '3.8'
networks:
  default:
    name: local_link
services:
  srv1:
    image: nginx:latest
    container_name: app2
```

```{txt:実行結果}
$ docker compose -p app1 -f docker-compose.app1.yml up -d
[+] Running 2/2
 ⠿ Network local_link  Created                                                              0.0s
 ⠿ Container app1      Started                                                              0.8s
$ docker compose -p app2 -f docker-compose.app2.yml up -d
[+] Running 1/1
 ⠿ Container app2  Started                                                                  0.9s
```
#### 確認
`docker network inspect`で確認

```{txt:実行結果}
$ docker network inspect local_link
   (略)
        "Containers": {
            "09d61a2de...": {
                "Name": "app2",
                ...
            },
            "0cdf3f601...": {
                "Name": "app1",
                ...
            }
        },
  (略)
```
同一のネットワークに属している。
更に、各コンテナの情報を確認

```{txt:inspect (app1)}
$ docker inspect app1
  (略)
   "Networks": {
     "local_link": {
        "Aliases": [
           "app1",
           "srv1",
           "0cdf3f601f16"
        ],
  (略)
```

```{txt:inspect (app2)}
$ docker inspect app2
  (略)
   "Networks": {
     "local_link": {
        "Aliases": [
          "app2",
          "srv1",
          "09d61a2dea34"
        ],
  (略)
```
ネットワークエイリアスとして
 
   - コンテナ名
   - サービス名
   - コンテナID

が登録されているのがわかる。
別のアプリに同じサービス名が登録されたこの状態で
srv1にアクセスしたらどうなるのか、が今回の主題です。

### auto scaleの話
本題に入る前に、
同じサービス名が複数のコンテナに紐付けらることが望ましい状況として
スケールアウトさせたい場合が考えられます。
確認してみると

```{yml:docker-compose.app1.yml}
version: '3.8'

services:
  srv1:
    image: nginx:latest
    # container_name: app1 # コンテナ名はユニークなのでスケールさせる場合は指定できない

networks:
  default:
    name: local_link
```

```{txt:実行結果}
$ docker compose -p app1 -f docker-compose.app1.yml up -d --scale srv1=2
[+] Running 3/3
 ⠿ Network local_link     Created                                       0.0s
 ⠿ Container app1_srv1_2  Started                                       0.8s
 ⠿ Container app1_srv1_1  Started                                       0.7s

$ docker network inspect local_link
  (略)
  "Containers": {
    "5dd3a0844....": {
      "Name": "app1_srv1_2",
      ...
    },
    "d926584e....": {
      "Name": "app1_srv1_1",
      ...
    }
  },

$ docker inspect app1_srv1_1
  (略)
   "Networks": {
     "local_link": {
        "Aliases": [
          "app1_srv1_1",
          "srv1",
          "d926584e7947"
        ],
  (略)

$ docker inspect app1_srv1_2
  (略)
   "Networks": {
     "local_link": {
        "Aliases": [
          "app1_srv1_2",
          "srv1",
          "5dd3a08445a7"
        ],
  (略)
```
(当然かもしれませんが)想定通り、同じサービス名でエイリアスが登録されています

## 本題
下記の設定で立ち上げたnginxから、それぞれにアクセスしてみます

```{yml:docker-compose.yml}
version: '3.8'

services:
  proxy:
    image: nginx:latest
    container_name: proxy
    volumes:
      - ./default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - 8080:80

networks:
  default:
    name: local_link

```

```{nginx:default.conf}
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location /app1/ {
      proxy_pass http://app1:8080/;
    }

    location /app2/ {
      proxy_pass http://app2:8080/;
    }

    location /srv1/ {
      proxy_pass http://srv1:8080/;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
```

#### app1, app2
今回はgolangで作りました。
app1とapp2の差分がある部分は、同行にコメントで記しました。

```{go:main.go}
package main

import (
	"fmt"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, from app1") // app2では Hello, from app2
}

func main(){
	http.HandleFunc("/", hello)
	http.ListenAndServe(":8080", nil)
}
```



```{Dockerfile:Dockerfile}
FROM golang:1.16 AS build-env
WORKDIR /app
COPY . .
RUN go get && go build -o serve.out

FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build-env /app/serve.out .

CMD ["./serve.out"]
EXPOSE 8080
```

```{yml:docker-compose.yml}
version: '3.8'

services:
  srv1:
    build:
      context: .
    image: app1:latest # app2:latest
    container_name: app1 # app2

networks:
  default:
    name: local_link
```

### 結果
/app1/ へのアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293810/12ebb034-5bf5-3c1d-77c3-f5dbf417a800.png)

/app2/ へのアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293810/3c168a2d-3276-4171-5be5-8295daa39249.png)

/srv1/ へのアクセス(1回目)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293810/6470b01e-b757-c1b7-85d6-a52460359102.png)

/srv1/ へのアクセス（2回目）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293810/5f58b603-47b0-de4b-ee5b-cafc3d4a6938.png)

auto scale の話をした時点でなんとなく予想できていたが、
同じサービス名のコンテナに交互にアクセスしている模様。
スケールさせたときのロードバランシングの仕様を調べれば、どうなっているか分かると思います。

## 結論
別のアプリに同じサービス名を与え、同じネットワーク内に配備していると
予期せぬアクセスが発生する可能性がある。


## さいごに
networksの書き方で下記のようにnameを指定すると、
externalを指定しなくてもCompose外部に定義されているnetwork探してくれる上、
upと同時に無ければ作成され、downと同時に利用されてなければ削除される
という発見があった

```{yml:docker-compose.yml}
version: '3.8'
networks:
  default:
    name: local_link
  tmpname:
    name: my-net1

services:
  srv1:
    image: nginx:latest
    networks:
      - default
      - tmpname
```
networkが作成されたプロジェクトに紐付けされるので、
消す順番によってはnetworkだけが消えずに残ることもあるので注意
(再度downすれば消えるので大きな問題ではないが)