Dockerを基本から復習してみる Part2

今回は前回に引き続きDockerについての復習記事を書いていきます。
前回はDockerfileのみを使ったコンテナの構築について書きましたが、今回はDocker-composeを使ったコンテナの構築について基礎から書いてみます。
Docker-composeを使うことでより簡単かつ便利に複数のDockerコンテナを管理することができます。
では始めます。

前回のPart1で使ったファイルを少し変更して使ってます

使用ファイル解説

ディレクトリ構成

docker_test(作業用ディレクトリ)
| ------ docker-compose.yml (docker-composeの設定ファイル)
| ------ Dockerfile  
| ------ db.dockerfile(DBコンテナ用のDockerfile)
| ------ index.php (トップページ表示のためのファイル)
| ------ nginx.repo (yumのリポジトリにnginxを追加するためのファイル)
| ------ function.php(関数用のファイル)

docker-compose.yml

version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/usr/share/nginx/html
    depends_on:
      db:
        condition: service_healthy
    ports:
      - 8080:80
    environment:
      MYSQL_HOST: db
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password
    privileged: true # 権限の有効化
    command: /sbin/init
  db:
    platform: linux/x86_64 # M1チップ対応のため追記
    build:
      context: .
      dockerfile: db.dockerfile
    healthcheck:
      test: mysqlshow -u $$MYSQL_USER -p$$MYSQL_PASSWORD
      interval: 3s
      retries: 10
    ports:
      - '3308:3306'
    environment: 
      MYSQL_DATABASE: test_container
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password

今回はappコンテナとdbコンテナの2つを立てています。
まずappコンテナについて解説していきます。

  build:
      context: .
      dockerfile: Dockerfile

「context: . 」についてはビルドコンテキストがカレントディレクトリであることを宣言しています。ビルドコンテキストに指定された階層のファイルはDocker Daemonに読み込まれるため、不要なファイルができる限り置かない方が良い。
「dockerfile: Dockerfile」についてはビルドコンテキストで使うdockerfile名を指定しています。
よって今回はカレントディレクトリ上にある Dockerfileを元に イメージが作成されることになります。

  volumes:
      - .:/usr/share/nginx/html

volumesでは : の左側にホストマシンのディレクトリを
:の右側に Dockerコンテナのディレクトリを指定することでこの左右のディレクトリを同期することが可能になります。
よってホストマシンのカレントディレクトリにある6つのファイル(docker-compose.yml,DockerFile,db.dockerfile,function.php,index.php,nginx.repo)が コンテナ内の /usr/share/nginx/html の下に同期されます。
同期されるのでホストマシン側で 同期ファイル内容の変更をしても、コンテナ内のファイル内容が変更されますし、その逆も同様です。

 depends_on:
      db:
        condition: service_healthy

depends_onについてですが、これは dbコンテナの立ち上げが完了してからこのコンテナを立ち上げるという設定になります。
さらに condion: service_healthyを指定することによって 「dbコンテナの立ち上げが完了 かつ dbコンテナに指定したヘルスチェックが OKの場合」起動するという条件をつけることが可能です。
dbコンテナに指定したヘルスチェックの内容については後述します。

  ports:
      - 8080:80

こちらは ホストマシンの 8080番ポートとコンテナの80番ポートを同期しています。

    environment:
      MYSQL_HOST: db
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password

ここでは環境変数を設定することができます。

  privileged: true # 権限の有効化
    command: /sbin/init

この部分についてはこれを指定しないとコンテナ立ち上げ後に systemctlコマンドを実行したときに 「Failed to get D-Bus connection Operation not permitted」エラーが発生して systemctlコマンドを使えないため、設定しています。

次はdbコンテナの内容について説明します。
appコンテナで解説した build,ports,environmentの項目については割愛します。

    platform: linux/x86_64 # M1チップ対応のため追記

この部分についてはM1チップのMacBookを使用している場合はこれがないとエラーが発生しますが、それ以外の場合は必要ありませんので消して大丈夫です。

 healthcheck:
      test: mysqlshow -u $$MYSQL_USER -p$$MYSQL_PASSWORD
      interval: 3s
      retries: 10

上の方のappコンテナ立ち上げの際に行われるdbコンテナのヘルスチェックの設定をここで行っています。
mysqlshowコマンドを使って データベースの一覧表示 が可能かどうかを判断しています。今回は 3秒ごとに 合計10回 mysqlshowコマンドを行なって 問題がなければヘルスチェック合格ということになります。

Dockerfile

# CentOS7のイメージをDocker Hubから取り込む
FROM centos:7

ADD nginx.repo /etc/yum.repos.d/

# nginx & curl をインストール
RUN yum -y install nginx \
yum -y install php-fpm php-mysql php-mbstring php-pear php-gd \
curl

# カレントディレクトリ上の index.htmlファイルを /usr/share/nginx/html/ にコピーする
ADD index.php /usr/share/nginx/html/

# ポート設定
EXPOSE 8080

CMD ["nginx","-g","daemon off;"] 

# /usr/share/nginx/html ディレクトリに移動
WORKDIR /usr/share/nginx/html/

こちらについては前回のPart1で解説しているのでそちらも参照いただければと思います。変更点としては今回はphpを使いたいので php-fpmをインストールしています。

db.dockerfile

FROM mysql:5.7

EXPOSE 3308

Docker Image は mysql:5.7を使用、コンテナの3308ポートを公開するという設定

nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

こちらは yumリポジトリに nginxを追加する設定

index.php

<?php
require('function.php');
?>

<h1> Hello World From Nginx</h1>
<h2>user from Database</h2>
<ul>
   <li><?= getUser(1)['name']; ?></li>
</ul>

トップページで表示予定のファイル。
dbコンテナの usersテーブルから id:1のユーザデータの名前を取得して表示するだけの簡単なもの

function.php

<?php

// DB接続関数
function dbConnect(){
  //DBへの接続準備
  $dsn = "mysql:dbname=test_container;host=docker_test-db-1;charset=utf8;";
  $user = 'root';
  $password = 'password';
  $options = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
    // デフォルトフェッチモードを連想配列形式に設定
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
  );
  // PDOオブジェクト生成
  $dbh = new PDO($dsn,$user,$password,$options);
  return $dbh;
}

function queryPost($dbh,$sql,$data){
    //クエリー作成
    $stmt = $dbh->prepare($sql);
    //プレースフォルダに値をセットし、SQL文を実行
    if(!$stmt->execute($data)){
      return false;
    }
    return $stmt;
}

function getUser($u_id){
  // 例外処理
  try{
    // DBへ接続
    $dbh = dbConnect();
    // SQL文作成
    $sql = 'SELECT * FROM users WHERE id = :u_id';
    $data = array(':u_id' => $u_id);
    // クエリ実行
    $stmt = queryPost($dbh, $sql, $data);

    // クエリ結果のレコードを1レコード返却
    if($stmt){
      return $stmt->fetch(PDO::FETCH_ASSOC);
    }else{
      return false;
    }
  } catch (Exception $e) {
    var_dump($e);
  }
}

PDOオブジェクトを使用して データベースからユーザデータを取得してくるための関数を用意しています。

コンテナ立ち上げ手順

まずdocker-compose を使ってイメージを作成

$ docker-compose build --no-cache

次に イメージから バックグラウンドで コンテナを立ち上げる

$ docker-compose up -d

コンテナが立ち上がってるか確認

$ docker ps
CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS                    PORTS                                                                                                              NAMES
fc5562f19543   docker_test_app     "/sbin/init"             51 seconds ago   Up 6 seconds              8080/tcp, 0.0.0.0:8080->80/tcp                                                                                     docker_test-app-1
e142b74694ae   docker_test_db      "docker-entrypoint.s…"   51 seconds ago   Up 50 seconds (healthy)   3308/tcp, 33060/tcp, 0.0.0.0:3308->3306/tcp

この時点で http://localhost:8080 に接続しても下の画像のようにうまくいかない

次に立ち上げた appコンテナに入って設定を変更していく

vi /etc/php.ini
;date.timezone =
date.timezone = Asia/Tokyo

タイムゾーンを Asia/Tokyoに設定

vi /etc/php-fpm.d/www.conf
user = nginx
group = nginx

;listen.owner = nobody
;listen.group = nobody
listen.owner = nginx
listen.group = nginx

;listen = 127.0.0.1:9000
listen = /var/run/php-fpm/php-fpm.sock

user,groupはデフォルトでは apacheになっているので両方ともnginxに修正

$ systemctl start php-fpm
$ systemctl enable php-fpm
Created symlink from /etc/systemd/system/multi-user.target.wants/php-fpm.service to /usr/lib/systemd/system/php-fpm.service.

php-fpmデーモンをsystemctl経由で起動してphp-fpmの自動起動を有効に設定。

$ systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2022-03-10 07:09:32 UTC; 3min 39s ago
 Main PID: 90 (php-fpm)
   Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec"
   CGroup: /docker/fc5562f195438a78548bd478b20cbaaa480a4b0f550a4be2683c7ebb7fe1a3a0/system.slice/php-fpm.service
           ├─90 php-fpm: master process (/etc/php-fpm.conf)
           ├─92 php-fpm: pool www
           ├─93 php-fpm: pool www
           ├─94 php-fpm: pool www
           ├─95 php-fpm: pool www
           └─96 php-fpm: pool www
           ‣ 90 php-fpm: master process (/etc/php-fpm.conf)

Mar 10 07:09:32 fc5562f19543 systemd[1]: Starting The PHP FastCGI Process Manager...
Mar 10 07:09:32 fc5562f19543 systemd[1]: Started The PHP FastCGI Process Manager.

PHPデーモンの起動状態を確認。activeになっているのがわかる

/etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  localhost;

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

 
    location ~ \.php$ {
        root           /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param SCRIPT_FILENAME        $document_root/$fastcgi_script_name;
        include        fastcgi_params;
    }

 
}

/etc/nginx/conf.d/default.conf を編集して phpが動くように設定

$ systemctl restart nginx

ここまで編集したら nginxをリスタートしてコンテナを一旦抜けます。

次にdbコンテナ内にusersテーブルを作成する

$ docker exec -it  docker_test-db-1 /bin/bash

まずdbコンテナに入る

root@e142b74694ae:/# mysql -u root -p

rootユーザでデータベースに接続。パスワードを求められるので正しいパスワードを入力すると接続できる。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test_container     |
+--------------------+
5 rows in set (0.00 sec)

show databases を実行すると5つのデータベースが存在するので test_containerを選択する。

mysql> use test_container
Database changed
mysql> create table users (id int, name varchar(10));
Query OK, 0 rows affected (0.06 sec)

usersテーブルを作成(今回はnameのみの簡易的なもの、本当はidにprimary_key んどもつけないといけないが、あくまでappコンテナとdbコンテナでやりとりができるか確認するためのものなので省略)

mysql> INSERT INTO users VALUES (1,'test1');
Query OK, 1 row affected (0.02 sec)

一つレコードを挿入する

mysql> select * from users;
+------+-------+
| id   | name  |
+------+-------+
|    1 | test1 |
+------+-------+
1 row in set (0.02 sec)

usersテーブルにレコードが入っているのが確認できる

この状態で http://localhost:8080/ にアクセスすると データベースからユーザデータを取得できているのが確認できました!

次回は ECS(起動タイプ : EC2)について書こうと思います。今度はLaravelの簡易的なアプリを動かせるようにしたいです。その次は ECS(起動タイプ : Fargate)とか書きたいと思います。

Related article

おすすめ関連記事