dockerでDjangoの静的ファイルをnginxに配置した！(Dockerfileから外部のファイルを参照する方法)

畑田です。ホワイトデーですね(執筆時)。  
pj-eventの開発において、Djangoのstatic file群のみをHTTPサーバ(今回はnginx)から配信する方針を選択しました。  
ECRにDjangoとnginxのimageを置いておいて、container生成を行っています。  
このとき、nginxのimageの中にDjangoのstatic file群を配置しなければならないのですが、Djangoのstatic file群はnginxのDockerfile以下に無いため、Dockerfile内での記述方法がわからず困ってしまいました。  
基本を勉強し直すことで解決できたので記録に残します。

## 環境
- Docker Version: 20.10.2
- Docker API version: 1.41

## 問題
まず、今回のプロジェクトのディレクトリ構造は以下の様です。(`docker-compose.yml`や`sql/`以下は当記事とは無関係です。)

```
.
├── .gitignore
├── README.md
├── docker-compose.yml
├── nginx
│   ├── Dockerfile <<<<<<<< こいつから
│   ├── conf
│   │   └── default.conf
│   ├── nginx.conf
│   └── uwsgi_params
├── server
│   ├── .dockerignore
│   ├── Dockerfile
│   ├── Dockerfile.prod
│   ├── requirements.txt
│   └── src
│       ├── .env
│       ├── .env.dev
│       ├── event_master
│       ├── manage.py
│       ├── pjevent
│       ├── shop_master
│       ├── static <<<<<<<< こいつを参照したい
│       ├── user_master
│       └── uwsgi.ini
└── sql
    ├── Dockerfile
    ├── init
    │   └── init.sql
    └── my.cnf
```
今回、`nginx/Dockerfile`から`server/src/static/`を指定し、`COPY`によってstatic file群をnginxサーバーに置くことがゴールです。  
しかし、Dockerfileにおいて、local側のpathは**Dockerfileのあるdirectory以下の階層しか指定できない**のです。つまり、local側のpathは`./`以下しかDockerfileには記述できません。  
今回、`nginx/Dockerfile`から`server/src/static/`への相対pathは`../sever/static/`であるため、`nginx/Dockerfile`で管理できないのではないかと悩みました。

## 基本を復習
Dockerfileからimageをビルドするコマンドでよく使うのは以下です。

```sh
$ docker image build -t IMAGE_NAME:IMAGE_TAG .
```
そもそも`-t`オプションは`--tag`オプションの省略形で、imageにnameとtag(tagはオプショナル)をつけるためのものです。  
最後につけてある`.`はこのコマンドの引数であり、**Dockerfileにとってのcurrent directory**を指定します。  
これについて、僕は**参照したいDockerfileの位置するdirectory**を指定するものだと勘違いしていました。(これを勉強し直すことで今回のお悩みは解決されました。)  
さて、よく使うオプションは`--tag`の他に`--file`があります。  
`--file`オプションでは、Dockerfileとして使用するfileへのpathを指定することができます。デフォルトではcurrent directoryの`Dockerfile`を探して読み込みますが、実際はここで指定してやれば、なんという名前のものでも良いです。  
`Dockerfile.prod`という名前で本番環境用のものを用意したり、`Dockerfile.base`という名前でbase imageのためのものを用意したりできます。

## 実際に打ったコマンド
答えです。  
プロジェクトのrootに移動して、以下のコマンドを打ちます。

```sh
$ docker image build --tag pj-event-nginx --file nginx/Dockerfile .
```
`nginx/Dockerfile`の内容は以下です。

```Dockerfile
FROM nginx:1.19.1
COPY ./nginx/conf/default.conf /etc/nginx/conf.d/default.conf
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/uwsgi_params /etc/nginx/uwsgi_params
COPY ./server/src/static /static
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
EXPOSE 80
```

このコマンドの妙は、`nginx/Dockerfile`にとってのcurrent directoryにroot(`.`)を指定しているところです。  
**Dockerfileが位置するdirectoryとDockerfileにとってのcurrent directoryが違う**のです。  
こうすることによって、Dockerfileにとっての`./`が、rootを表すため、`../`を使わずに`server/src/static/`にpathを通すことができる様になります。  
もちろん以下の様なコマンドも同義です。

```sh
$ cd nginx/
$ docker image build --tag pj-event-nginx --file ./Dockerfile ..
```
ここで、`./Dockerfile`とだけ`--file`オプションに指定しているのは、書かないとデフォルトでcurrent directory(引数である`../`)の`Dockerfile`という名前のファイルを探してしまうためです。今回はこのコマンドを打っているdirectoryにDockerfileがあるのでこの様に指定しました。

## 結論
実現したいこと自体はコマンドを正確に使えば十分表現できることでした。  
`docker image build`コマンドについて、以下の様に整理しておきます。  
引数は**Dockerfileにとってのcurrent directory**を指定します。  
`--file`オプションは**参照したいDockerfileへのpathを指定する**もので、デフォルトでは引数で指定したdirectory内の`Dockerfile`という名前のファイルを探して参照します。  
Dockerfileの位置するdirectoryの親階層からしかアクセスできないfileを参照したい場合は、Dockerfileは親階層をcurrent directory(`./`)として記述し、ビルドするときの引数にも親階層を指定してあげれば実現できます。  
その際注意すべきは、Dockerfileが位置するdirectoryとDockerfileにとってのcurrent directoryが違うので`--file`オプションが必要であるということです。  
以上です。

## 参考
https://docs.docker.com/engine/reference/builder/