【Linux標準教科書】Dockerfileを使ってCentOS環境を構築する

## 前提

[前回の記事](https://qiita.com/ysk91_engineer/items/e2a444f8cb4379bd94d5)では、Dockerを使用してM1 Mac上にCentOS環境を構築しました。
しかし、この環境は中身が空っぽのため今後の学習には不向きであることが分かりました。

そこで、必要な設定をおこないLinuxの学習をするのに適した環境を再構築していきます。

## 勘違いしてたこと

立ち上げたコンテナに対して設定を変更しようとしたのですが、Dockerコンテナは**イミュータブル**（不変）の性質を持っているため、コンテナ内で行った変更はコンテナの再起動時には持続しないことに気が付きました。

つまり、せっかく設定をおこなってもそのコンテナを停止して再起動すると、変更は失われてしまいます。

これは、コンテナがその元となるイメージから生成されるときに、毎回「クリーンな状態」で開始されるためです。

## どうやって実装するか？

前回の記事ではDockerHubからCentOSイメージを取得しましたが、今回はDockerfileを作成し環境構築していきます。



## 実装

### Dockerfileの中身を確認

今回構築するDockerfileは、LPI-Japan公式で紹介されている以下のファイルを使用します。

https://raw.githubusercontent.com/tokifumi/docker/main/Dockerfile

環境構築自体は
```
$ docker build ./ -t centos7c

$ docker run –it centos7c
```

で完了するのですが、せっかくなのでDockerfileの中身を読み解いてみようと思います。

```
# ベースイメージ
FROM centos:7
```
ここでは、ベースとなるDockerイメージを`FROM`で指定しています。
前回構築した、DockerHub上のCentOS7を選択しています。

```
# ロケールを日本語に変更とyesコマンドの無効化
RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 && \
	ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
	echo -e "export LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja TZ=Asia/Tokyo\nalias yes='printf '\'''\'''" >> /etc/bashrc && \
	sed -i 's/LANG="en_US.UTF-8"/LANG="ja_JP.UTF-8"/g' /etc/locale.conf && \
	# yum.confで日本語環境、ドキュメントをインストールするように変更
	sed -i -e '/override_install_langs.*/d' -e '/tsflags.*/d' /etc/yum.conf
```

`RUN`は、イメージビルドプロセス中に実行するコマンドを指定しています。
このプロセスにより、必要な設定やソフトウェアがプリインストールされた状態のイメージが作成されます。

```
# 各種コマンドインストール
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 && \
	yum clean all && \
	yum install -y less sudo psmisc which wget httpd mlocate bzip2\
	vim-enhanced vim-filesystem vim-common \
	man-db man-pages-ja man-pages man-pages-overrides && \
	# 日本語化、マニュアル作成のため全パッケージの再インストール
	yum -y reinstall $(yum list installed | cut -f1 -d " " | sed 's/\..*$//g' | grep -v '^[L|I].*ed$' | sed -z 's/\n/ /g')
```

ここでは、DockerHubからpullするだけではインストールされないパッケージをインストールしています。

<details><summary>インストールしたパッケージ一覧</summary>

less: ファイルの内容をページ単位で閲覧するためのコマンドラインツールです。moreコマンドに似ていますが、前後の移動が自由にできるなどの機能が追加されています。

sudo: 他のユーザー（通常はrootユーザー）の権限でコマンドを実行するためのプログラムです。セキュリティ上の理由から、一般ユーザーが管理者権限を必要とする操作を行う際に使用されます。

psmisc: killall, pstree など、プロセスを管理するための便利なツールが含まれています。

which: コマンドのフルパスを検索して表示するコマンドです。コマンドがどこにインストールされているかを知るのに便利です。

wget: ネットワーク経由でファイルをダウンロードするための非対話型コマンドラインツールです。

httpd: Apache HTTP Serverのパッケージです。Webサーバーとして広く使用されています。

mlocate: ファイルの検索を高速に行うためのコマンドラインツールです。データベースに基づいてファイルシステムを索引化し、高速な検索を実現します。

bzip2: ファイルの圧縮・解凍を行うためのプログラムです。

vim-enhanced, vim-filesystem, vim-common: Vimエディタの拡張版とその関連ファイルです。テキストファイルの編集に高機能なオプションを提供します。

man-db: オンラインマニュアルページを表示するためのツールです。

man-pages-ja: 日本語のマニュアルページです。

man-pages: 標準のマニュアルページです。

man-pages-overrides: 標準のマニュアルページに対する補足や修正を提供します。
</details>

```
# manドキュメント追加
RUN mandb
```

`mandb`コマンドは、システム上の全てのマニュアルページを検索し、それらの内容からデータベースを構築または更新します。
システムに新たにパッケージをインストールしたり、既存のパッケージを更新したりすると、それに伴い新しいマニュアルページが追加されたり既存のものが更新されたりします。
しかし、これらの変更が即座にmanコマンドで参照できるわけではありません。
manコマンドは、効率的な検索を行うために、マニュアルページの内容を事前に処理してデータベースに格納しておく必要があります。

```
# コンテナ内ルートユーザーにパスワード設定
RUN echo "root:centos" | chpasswd

# ログインユーザーを作成
ARG UID=1000
RUN useradd -m -u ${UID} usera && \
# 作成したログインユーザーにパスワードを設定
echo "usera:usera" | chpasswd

# 作成したログインユーザーに切り替える
USER ${UID}
```

この流れにより、Dockerイメージ内にはセキュリティを考慮したユーザー環境が構築されます。
ルートユーザーではなく、限定された権限を持つuseraユーザーでコンテナが実行されるようになります。
これは、コンテナ内での作業をより安全に行うための一般的なベストプラクティスです。

```
WORKDIR /home/usera/
RUN mkdir ダウンロード デスクトップ ビデオ 画像 テンプレート ドキュメント 音楽 公開
```

WORKDIRはDockerfileにおけるコマンドの一つで、Dockerイメージ内の作業ディレクトリ（カレントディレクトリ）を設定するために使用されます。
このコマンドによって指定されたディレクトリは、それ以降のRUN、CMD、ENTRYPOINT、COPY、ADDといった指令の実行時の基準ディレクトリとなります。
上の例では、`/home/usera/`ディレクトリ内で`mkdir`をすることで複数のディレクトリを作成しています。

```
# コンテナ実行時のデフォルトコマンド
CMD ["/bin/bash"]
```

CMD指令は、Dockerコンテナが起動する際にデフォルトで実行されるコマンドを指定するためにDockerfile内で使用されます。
CMDで指定したコマンドは、docker runコマンドで新しいコマンドを指定することによってオーバーライドすることができます。

### `--rm`を使って起動する

Dockerイメージを再度立ち上げるときに

```
docker run --rm -it centos7c
```

のように、`--rm`を指定することができます。
これにより、このコンテナからexitしたときに、コンテナが削除されます。
Dockerデスクトップ内で複数の使用済みコンテナが残るのを防ぐことができます。

## まとめ

これで、Dockerfileを使用した環境構築をすることができました。
Dockerfileは苦手意識がありましたが、自動化シェルスクリプトと似た構成であることが分かりました。
インフラの知識が増えることで

- どのOSに
- どのコマンドを使って
- どんな設定をおこない
- どんなパッケージをインストールするか

を正しく構築することができるのかなと思いました。