Dockerコンテナで開発時のパーミッションエラーについて

本記事は、[intimatemerger Advent Calendar 2023](https://qiita.com/advent-calendar/2023/intimatemerger) 13 日目の記事です。

# はじめに

こんにちは。
[Intimate Merger](https://corp.intimatemerger.com/) のFukudaです。

今回は、コンテナ開発時に発生するファイルパーミッションの問題に関する記事です。
ローカルのディレクトリをコンテナのディレクトリにマウントして開発することがあると思います。
この時、ファイル編集時にパーミッションの問題により、ファイルを保存できないなどの問題が発生したことはありませんか？

Dockerを使ってまもない頃の私はよく悩まされていました。
本記事ではこの問題を解決していきます。

# 準備するファイル

```text
directory
├── docker-compose.yml
├── src-1
│   ├── app/
│   └── Dockerfile
└── src-2
    ├── app/
    └── Dockerfile
```

```./src-1/Dockerfile
FROM ubuntu:22.04

COPY ./app /usr/src/app
```

```./src-2/Dockerfile
FROM ubuntu:22.04

ARG UID=1000
ARG GID=1000
ARG USERNAME=app
ARG GROUPNAME=app

RUN groupadd -g $GID $GROUPNAME && \
    useradd -m -u $UID -g $GID $USERNAME

COPY --chown=$UID:$GID ./app/ /usr/src/app

WORKDIR /usr/src/app

USER $USERNAME
```

```docker-compose.yml
version: '3'
services:
  app-1:
    build:
      context: src-1
      dockerfile: Dockerfile
    volumes:
      - ./src-1/app:/usr/src/app
    tty: true
  app-2:
    build:
      context: src-2
      dockerfile: Dockerfile
      args:
        UID: 1000
        GID: 1000
        USERNAME: app
        GROUPNAME: app
    volumes:
      - ./src-2/app:/usr/src/app
    tty: true
```

# 検証

docker-composeを使ってコンテナを起動させておきます。

```bash
$ docker-compose up --build
```

コンテナが起動したか確認します。

```bash
$ docker-compose ps
NAME             COMMAND             SERVICE             STATUS              PORTS
advent-app-1-1   "/bin/bash"         app-1               running             
advent-app-2-1   "/bin/bash"         app-2               running
```

## パーミッションエラーを再現

`app-1`でパーミッションエラーを再現することができます。
実際にやってみましょう！

コンテナ側で`text`というファイルを作成します。

```bash
$ docker-compose exec -it app-1 touch text
```

ボリュームを共有しているので、ローカル側の`./src-1/app/`の中にも`text`というファイルが存在します。

```bash
$ tree src-1
src-1
├── app
│   └── text    # 作成したファイル
└── Dockerfile
```

作成した`text`をローカル側から編集してみましょう。
`"XXXXX"`という文字列の書き出すコマンドを実行します。
`Permission denied`と出力されました。

```bash
$ echo "XXXXX" > ./src-1/app/text
bash: ./src-1/app/text: Permission denied
```

## 原因

`ls -l`コマンドを使ってファイルのアクセス権を確認してみます。

```bash
$ ls -l ./src-1/app/text 
-rw-r--r-- 1 root root 0 Dec 11 22:59 ./src-1/app/text
```

このファイルに書き込みできるのはファイルの所有者である`root`のみのようです。
他のユーザーには読み取りのアクセス権のみ与えられています。

`id`コマンドを使って、コンテナ側のユーザーとホスト側のユーザーID（UID）とグループID（GID）を見てみます。

```bash
# ホスト側
$ id
uid=1000(local-user) gid=1000(local-user) groups=1000(local-user)

# コンテナ側
$ id
uid=0(root) gid=0(root) groups=0(root)
```

- ホストのユーザー: `1000(local-user)`
- コンテナのユーザー: `0(root)`

ホスト側とコンテナ側でUID/GIDが異なるため、ファイルのPermissionエラーが発生しているということがわかります。

## 解決策

解決策は、ホストマシンとコンテナのユーザー権限を一致させることです。UID/GIDを同じにし、適切な権限を与えることで問題を解消できます。

### ユーザー・グループを作成する


```Dockerfile
RUN groupadd -g $GID $GROUPNAME && \
    useradd -m -u $UID -g $GID $USERNAME
```

### 作成したユーザーに権限を与える

```Dockerfile
COPY --chown=$UID:$GID ./app/ /usr/src/app
```

### ファイルの編集ができるか確かめる

さきほどと同様にコンテナ側からファイルを作成します。

```bash
$ docker-compose exec -it app-2 touch text
```

ホスト側からファイルに書き込んでみます。
`app-1`と違いファイルへの書き込みが成功しました。

```bash
$ echo "XXXXX" > ./src-2/app/text
```

# まとめ

ローカルのディレクトリをコンテナのディレクトリにマウントして開発する際に生じるパーミッションの課題について取り上げました。

コンテナ内で作成したファイルをローカルで編集しようとすると、UID/GIDの不一致が原因でPermissonエラーが発生します。

ユーザーとグループを作成し、ファイルのアクセス権をローカルと合わせることでパーミッションの問題を解決できました。

# あとがき

読んでいただきありがとうございました！
次回の記事はkeywordについてのようです。

https://qiita.com/advent-calendar/2023/intimatemerger