20210305のdockerに関する記事は14件です。

コンテナチェリーなのでDockerを基礎から学び始めた①

こんにちは、コンテナ童貞のすずきです。業務でDockerなど使ったことはありません。

この時点で、この記事を読んだ9割の現役エンジニアの方々は、私のことをコンテナも知らないレガシー雑魚野郎と心の中でバカにしたことでしょう。

生憎、私はドMなのでバカにされることに快感を感じるタイプではあるのですが、悔しい気持ちも2割くらいはあります...

言われっぱなしにならないためにDockerを基礎から学び、手始めに、Dockerって何?というところから、Dockerfileからイメージをビルドしてコンテナを起動するまでの流れを記事にまとめました。

Dockerって何?

いろんなサイトや文献をみるとDockerの定義がばらばらで頭が混乱するのですが、コンテナ作成や実行のためのプラットフォーム(管理ツール)であるDocker Engine*としての意味。もしくは以下のツールの総称として使われることが大半です。

  • Docker Client(Docker CLI): Dockerコマンドを実行するコマンドラインツール
  • Docker Server(Docker Daemon): イメージの作成、コンテナの実行などを行うもの
  • Docker Machine: Mac, Windows上でDockerを使えるようにするもの
  • Docker Images: コンテナの素となるもの
  • Docker Hub: ユーザーが作成したコンテナをアップロードして公開・共有できるサービス
  • Docker Compose: 複数のコンテナで構成されるアプリケーションについて、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール

*Docker EngineはホストOS(カーネル)の上にあり、アプリケーション本体とそれを実行するための環境を1つのDockerイメージにまとめたり、Dockerイメージに基づいてコンテナを実行させ、簡単かつ高速にアプリケーションを起動できます。

そもそもコンテナって何?

コンテナは、仮想マシンと同様に、1つのサーバ上で複数のサーバ環境を分離して実行する仮想化の技術です。

仮想マシンにはホストOS上にハイパーバイザーと呼ばれる管理層があり、ハイパーバイザーが提供する仮想的なハードウェアで複数の異なるOS(ゲストOS)を並列に実行します。一方、コンテナは1つのOSの中を複数の区画に分離する技術であり、1つのカーネルの上で複数のOS環境が動きます。そのため、「OSレベルの仮想化」とも呼ばれます。

仮想マシンが「イメージ」をもとにして作成されるように、コンテナにも「コンテナイメージ」というものが必要です。コンテナイメージには、コンテナで実行するアプリケーションの実行形式ファイルや、その実行のために必要なファイル群しか含まれません。このように、コンテナイメージは非常に軽量(数十~数百MB程度)です。GB級の容量の仮想マシンイメージと比べて少ないリソースでのアプリケーション実行が可能となるため、起動が早く、メモリやCPUリソースを余分に使うことがありません。ネットワークでの共有も迅速に行なえます。

出典: What is Docker

コンテナにおけるカーネルの役割

仮想マシンと違ってコンテナではOS(カーネル)が共有されますが、コンテナが仮想マシンのように独立した環境をもつためには、ハードウェア上のリソースもコンテナごとに制限されている必要があります。コンテナが必要以上のリソースを使わないために、カーネルの以下の機能が使われています。

  • Namespacing
  • Control Groups(cgroups)

NameSpacing

Namespacingはコンテナを1つの仮想マシンのように占有されたシステムとして見せるためのカーネルの機能です。ハードウェア内にセグメントをつくり、プロセスごとにリソースが隔離されます。

プロセスごとに環境を構築することができるため、他のLinux上で同じ環境を再構築することができます。

Control Groups(cgroups)

Dockerでは、コンテナ内からアクセス可能なシステムのリソース(CPUやメモリなど)を制限しています。これを実現させるために、カーネルのControll Groups(cgroups)という機能を使います。

Namespacingではホスト名やPIDなどのリソースを制御しますが、cgroupsはCPUやメモリなどの物理的なリソースを制御します。
スクリーンショット 2021-03-03 22.06.07.png

改めて、コンテナというのはプロセスごとにリソースが隔離された状態であり、図にすると以下のようになります。
スクリーンショット 2021-03-03 22.47.32.png

コンテナイメージとは

コンテナを実行するためには、コンテナの素となるイメージが必要となります。
素の中身には以下の2つのものがあります。

  • ファイルシステム
  • メタ情報

ファイルシステム(FS)

ファイルシステムというのは、コンテナ上のプロセス(実行アプリケーション)が必要とするファイルのことです。
DockerはLinuxカーネルで動作するため、ディレクトリ階層もLinux規格(/ディレクトリ以下に/etc /bin /sbin /usrなどがある)となります。

メタ情報

アプリケーションの実行に必要なデフォルトのコマンドや引数の指定、外に公開するポート番号の情報、ボリューム領域などをまとめてメタ情報と呼びます。

以下の図のように、イメージからFSやメタ情報がコピーされることでコンテナが実行されます。
スクリーンショット 2021-03-04 8.05.34.png

簡単な動作確認

Dockerとコンテナについてわかってきたので、Dockerを導入して試しにコンテナを起動してみます。

Docker Desktopの導入

DockerはLinuxカーネル上で動くので、WindowsやMacのPCだとそのままでは動きません。そのため、Linux環境を簡単に構築できるDocker Desktop (for Windows or Mac)をインストールします。インストール後に起動して、"Docker Desktop is running"となっていれば大丈夫です。

docker versionでバージョン情報を調べてみると、確かにOS/Arch:linux/amd64となっています。

(base) [20:52:08] → docker version                                                                                                                                       ~
Client: Docker Engine - Community
 Cloud integration: 1.0.4
 Version:           20.10.2
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        2291f61
 Built:             Mon Dec 28 16:12:42 2020
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.2
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8891c58
  Built:            Mon Dec 28 16:15:23 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

イメージからコンテナを起動

docker run [image]でイメージからコンテナを作成して起動することができます。
hello worldイメージで試しにコマンドを実行してみます。

(base) [21:14:40] → docker run hello-world                                                                                                                               ~

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

docker run [image]を実行すると、docker serverはイメージがローカルのイメージキャッシュ内にあるかどうかをまず確かめます。キャッシュ内にイメージがなければDocker Hubの中を探し、ローカルにイメージを読み込みます。

dockerコマンドをいろいろ実行してみる

docker run [image]

イメージからコンテナを作成して起動します。
コンテナの作成はdocker create [image]、起動はdocker start -a [container id]で個別に行うことができ、これらの組み合わせがdocker run [image]となります。

docker run [image] [Linuxコマンド]

コンテナ作成起動後にLinuxコマンドを実行することができます。
例えば、lsコマンドで出力される結果はコンテナ内のディレクトリ構成となります。

(base) [21:25:09] → docker run busybox ls                                                                                                                                ~
bin
dev
etc
home
proc
root
sys
tmp
usr
var

docker ps

起動中のコンテナを全て表示します。

(base) [21:32:27] → docker ps                                                                                                                                            ~
CONTAINER ID   IMAGE                       COMMAND                  CREATED        STATUS                          PORTS                    NAMES
66eaeff67b15   prismagraphql/prisma:1.12   "/bin/sh -c /app/sta…"   6 weeks ago    Up 6 weeks                      0.0.0.0:4466->4466/tcp   prisma_prisma_1
1cb282826d22   complex_nginx               "/docker-entrypoint.…"   3 months ago   Restarting (1) 14 seconds ago                            complex_nginx_1
8eac3688a342   visits_node-app             "docker-entrypoint.s…"   3 months ago   Up Less than a second           0.0.0.0:4001->8081/tcp   visits_node-app_1

docker ps --all

今までに作成したコンテナを全て表示します。

(base) [21:33:16] → docker ps --all                                                                                                                                      ~
CONTAINER ID   IMAGE                                 COMMAND                  CREATED          STATUS                          PORTS                                                                                                      NAMES
7fc44dbef90d   busybox                               "ls -a"                  5 minutes ago    Exited (0) 5 minutes ago                                                                                                                   lucid_raman
86d2d24c54cb   busybox                               "ls"                     8 minutes ago    Exited (0) 7 minutes ago
...

docker system prune

今まで作成したコンテナを全削除します。

docker logs [container id]

コンテナが起動されたときのログを出力します。

docker stop [container id]

起動中のコンテナを停止します。

docker kill [container id]

コンテナを強制停止します。

docker exec -it [container id] [command]

コンテナに追加のプロセスを実行させることができます。ターミナルへのアクセスなどを行います。

例えば、docker run redisでredis-serverをコンテナに構築し、redis-serverを操作したくなったとします。そんなときに、docker exec -it [container id] redis-cliを実行することで、ターミナルからredis-cliのコマンドをうてるようになります。
-iでredis-cliの標準入力と接続し、-tで標準出力と接続することができます。

docker exec -it [container id] shでコンテナのシェルに接続し、Linuxコマンドがうてるようになります。
また、docker run -it [image] shでコンテナの作成起動と同時にコマンドがうてるようになります。

Dockerイメージを作成してコンテナを起動してみる

コンテナの素となるDockerイメージは、以下の順序で作られます。

  1. Dockerfileの作成(Docker Serverへの命令内容を記述)
  2. イメージのビルド(docker build .の実行)

Dockerfileの作成

Dockerfileにも記述の順序があります。

  1. ベースイメージの指定
  2. 追加プログラムをインストールするためのコマンドを実行
  3. コンテナプロセスの起動コマンドの指定

例えば、redisイメージ用のDockerファイルは以下のように記述します。

# Use an existing docker image as a base
FROM alpine

# Download and install a dependency
RUN apk add --update redis

# Tell the image what to do then it starts
# as a container
CMD ["redis-server"]

*Alpine: Linuxのディストリビューション(Linuxカーネルとその他ソフトウェア群を1つにまとめ、容易にインストール・利用できるようにしたもの)
*apk: Alpineのパッケージマネージャ
*redis: インメモリデータベース

Dockerfileの中身は、FROM, RUN, CMDのようなDocker Serverへの命令内容に引数を付加するような構成になっています。

イメージのビルド

docker build .でDockerfileからコンテナイメージを作成(ビルド)します。最後の.ではDockerfileのあるディレクトリを指定しています。今回はカレントディレクトリに置いてあるので.のみとしています。

以下がビルドの流れです。

1. コンテナが起動しapk add --update redisでredis関連のプログラムをインストール

Alpineイメージのファイルシステム(FS)スナップショットがコンテナ内のディレクトリにコピーされコンテナが起動します。その後にapk add --update redisでredis関連のプログラムがインストールされます。
スクリーンショット 2021-03-05 21.47.28.png

2. コンテナが停止し新しいイメージAが作られる

redisがインストールされたファイルシステムのスナップショットがつくられ、コンテナは停止します。コンテナが停止した後に、FSスナップショットから新しいイメージAがつくられます。
スクリーンショット 2021-03-05 21.33.43.png

3. 再度起動したコンテナに起動時のコマンドredis-serverを設定する

スクリーンショット 2021-03-05 21.34.05.png

4. コンテナが停止し起動コマンドが設定された最終的なイメージBが作られる

redis関連プログラムとコンテナ起動時のコマンドが設定された最終的なイメージがこれで完成します。
スクリーンショット 2021-03-05 21.34.23.png

完成したイメージからコンテナを作成・起動する

docker run [container id]でイメージからコンテナの作成と起動を行うことができます。しかし、この方法だとイメージをビルドしたときにcontainer idを控えておかなければなりません。

そんなとき、ビルド時にdocker build -t [タグ名] .とすることでイメージにタグを付けることができ、docker run [タグ名]でコンテナを実行することができます。
タグ名としては、[DockerID]/[Project名]:Versionとつけるのが一般的です。

以上がDockerfileの作成からコンテナを起動するまでの流れです。

ちなみに、以下のコマンドを実行すれば、Dockerfileを使わずにこれまでの流れを実施することができます。
docker run -it alpine sh
apk add --update redis
docker commit -c 'CMD['redis-server']' [id]
docker run ~

参考文献

おわりに

記事でアウトプットしたことでコンテナとDockerの理解が深まりました。次は実際のプロジェクトにDockerを導入したときの記事を書きたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コンテナ未経験なのでDockerを基礎から学んでみた

こんにちは、業務でDockerなど使ったことがないコンテナ童貞のすずきです。

この時点で、この記事を読んだ9割の現役エンジニアの方々は、私のことをコンテナも知らないレガシー雑魚野郎と心の中でバカにしたことでしょう。

生憎、私はドMなのでバカにされることに快感を感じるタイプではあるのですが、悔しい気持ちも2割くらいはあります...

言われっぱなしにならないためにDockerを基礎から学び、手始めに、Dockerって何?というところから、Dockerfileからイメージをビルドしてコンテナを起動するまでの流れを記事にまとめました。

Dockerって何?

いろんなサイトや文献をみるとDockerの定義がばらばらで頭が混乱するのですが、コンテナ作成や実行のためのプラットフォーム(管理ツール)であるDocker Engine*としての意味。もしくは以下のツールの総称として使われることが大半です。

  • Docker Client(Docker CLI): Dockerコマンドを実行するコマンドラインツール
  • Docker Server(Docker Daemon): イメージの作成、コンテナの実行などを行うもの
  • Docker Machine: Mac, Windows上でDockerを使えるようにするもの
  • Docker Images: コンテナの素となるもの
  • Docker Hub: ユーザーが作成したコンテナをアップロードして公開・共有できるサービス
  • Docker Compose: 複数のコンテナで構成されるアプリケーションについて、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール

*Docker EngineはホストOS(カーネル)の上にあり、アプリケーション本体とそれを実行するための環境を1つのDockerイメージにまとめたり、Dockerイメージに基づいてコンテナを実行させ、簡単かつ高速にアプリケーションを起動できます。

そもそもコンテナって何?

コンテナは、仮想マシンと同様に、1つのサーバ上で複数のサーバ環境を分離して実行する仮想化の技術です。

仮想マシンにはホストOS上にハイパーバイザーと呼ばれる管理層があり、ハイパーバイザーが提供する仮想的なハードウェアで複数の異なるOS(ゲストOS)を並列に実行します。一方、コンテナは1つのOSの中を複数の区画に分離する技術であり、1つのカーネルの上で複数のOS環境が動きます。そのため、「OSレベルの仮想化」とも呼ばれます。

仮想マシンが「イメージ」をもとにして作成されるように、コンテナにも「コンテナイメージ」というものが必要です。コンテナイメージには、コンテナで実行するアプリケーションの実行形式ファイルや、その実行のために必要なファイル群しか含まれません。このように、コンテナイメージは非常に軽量(数十~数百MB程度)です。GB級の容量の仮想マシンイメージと比べて少ないリソースでのアプリケーション実行が可能となるため、起動が早く、メモリやCPUリソースを余分に使うことがありません。ネットワークでの共有も迅速に行なえます。

出典: What is Docker

コンテナにおけるカーネルの役割

仮想マシンと違ってコンテナではOS(カーネル)が共有されますが、コンテナが仮想マシンのように独立した環境をもつためには、ハードウェア上のリソースもコンテナごとに制限されている必要があります。コンテナが必要以上のリソースを使わないために、カーネルの以下の機能が使われています。

  • Namespacing
  • Control Groups(cgroups)

NameSpacing

Namespacingはコンテナを1つの仮想マシンのように占有されたシステムとして見せるためのカーネルの機能です。ハードウェア内にセグメントをつくり、プロセスごとにリソースが隔離されます。

プロセスごとに環境を構築することができるため、他のLinux上で同じ環境を再構築することができます。

Control Groups(cgroups)

Dockerでは、コンテナ内からアクセス可能なシステムのリソース(CPUやメモリなど)を制限しています。これを実現させるために、カーネルのControll Groups(cgroups)という機能を使います。

Namespacingではホスト名やPIDなどのリソースを制御しますが、cgroupsはCPUやメモリなどの物理的なリソースを制御します。
スクリーンショット 2021-03-03 22.06.07.png

改めて、コンテナというのはプロセスごとにリソースが隔離された状態であり、図にすると以下のようになります。
スクリーンショット 2021-03-03 22.47.32.png

コンテナイメージとは

コンテナを実行するためには、コンテナの素となるイメージが必要となります。
素の中身には以下の2つのものがあります。

  • ファイルシステム
  • メタ情報

ファイルシステム(FS)

ファイルシステムというのは、コンテナ上のプロセス(実行アプリケーション)が必要とするファイルのことです。
DockerはLinuxカーネルで動作するため、ディレクトリ階層もLinux規格(/ディレクトリ以下に/etc /bin /sbin /usrなどがある)となります。

メタ情報

アプリケーションの実行に必要なデフォルトのコマンドや引数の指定、外に公開するポート番号の情報、ボリューム領域などをまとめてメタ情報と呼びます。

以下の図のように、イメージからFSやメタ情報がコピーされることでコンテナが実行されます。
スクリーンショット 2021-03-04 8.05.34.png

簡単な動作確認

Dockerとコンテナについてわかってきたので、Dockerを導入して試しにコンテナを起動してみます。

Docker Desktopの導入

DockerはLinuxカーネル上で動くので、WindowsやMacのPCだとそのままでは動きません。そのため、Linux環境を簡単に構築できるDocker Desktop (for Windows or Mac)をインストールします。インストール後に起動して、"Docker Desktop is running"となっていれば大丈夫です。

docker versionでバージョン情報を調べてみると、Client側(Docker CLIでコマンドを実行した側)はdarwin/amd64で、Server側(Dockerコマンドによる命令を実行する側)はOS/Arch:linux/amd64となっています。

(base) [20:52:08] → docker version                                                                                                                                       ~
Client: Docker Engine - Community
 Cloud integration: 1.0.4
 Version:           20.10.2
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        2291f61
 Built:             Mon Dec 28 16:12:42 2020
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.2
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8891c58
  Built:            Mon Dec 28 16:15:23 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

イメージからコンテナを起動

docker run [image]でイメージからコンテナを作成して起動することができます。
hello worldイメージで試しにコマンドを実行してみます。

(base) [21:14:40] → docker run hello-world                                                                                                                               ~

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

docker run [image]を実行すると、docker serverはイメージがローカルのイメージキャッシュ内にあるかどうかをまず確かめます。キャッシュ内にイメージがなければDocker Hubの中を探し、ローカルにイメージを読み込みます。

dockerコマンドをいろいろ実行してみる

docker run [image]

イメージからコンテナを作成して起動します。
コンテナの作成はdocker create [image]、起動はdocker start -a [container id]で個別に行うことができ、これらの組み合わせがdocker run [image]となります。

docker run [image] [Linuxコマンド]

コンテナ作成起動後にLinuxコマンドを実行することができます。
例えば、lsコマンドで出力される結果はコンテナ内のディレクトリ構成となります。

(base) [21:25:09] → docker run busybox ls                                                                                                                                ~
bin
dev
etc
home
proc
root
sys
tmp
usr
var

docker ps

起動中のコンテナを全て表示します。

(base) [21:32:27] → docker ps                                                                                                                                            ~
CONTAINER ID   IMAGE                       COMMAND                  CREATED        STATUS                          PORTS                    NAMES
66eaeff67b15   prismagraphql/prisma:1.12   "/bin/sh -c /app/sta…"   6 weeks ago    Up 6 weeks                      0.0.0.0:4466->4466/tcp   prisma_prisma_1
1cb282826d22   complex_nginx               "/docker-entrypoint.…"   3 months ago   Restarting (1) 14 seconds ago                            complex_nginx_1
8eac3688a342   visits_node-app             "docker-entrypoint.s…"   3 months ago   Up Less than a second           0.0.0.0:4001->8081/tcp   visits_node-app_1

docker ps --all

今までに作成したコンテナを全て表示します。

(base) [21:33:16] → docker ps --all                                                                                                                                      ~
CONTAINER ID   IMAGE                                 COMMAND                  CREATED          STATUS                          PORTS                                                                                                      NAMES
7fc44dbef90d   busybox                               "ls -a"                  5 minutes ago    Exited (0) 5 minutes ago                                                                                                                   lucid_raman
86d2d24c54cb   busybox                               "ls"                     8 minutes ago    Exited (0) 7 minutes ago
...

docker system prune

今まで作成したコンテナを全削除します。

docker logs [container id]

コンテナが起動されたときのログを出力します。

docker stop [container id]

起動中のコンテナを停止します。

docker kill [container id]

コンテナを強制停止します。

docker exec -it [container id] [command]

コンテナに追加のプロセスを実行させることができます。ターミナルへのアクセスなどを行います。

例えば、docker run redisでredis-serverをコンテナに構築し、redis-serverを操作したくなったとします。そんなときに、docker exec -it [container id] redis-cliを実行することで、ターミナルからredis-cliのコマンドをうてるようになります。
-iでredis-cliの標準入力と接続し、-tで標準出力と接続することができます。

docker exec -it [container id] shでコンテナのシェルに接続し、Linuxコマンドがうてるようになります。
また、docker run -it [image] shでコンテナの作成起動と同時にコマンドがうてるようになります。

Dockerイメージを作成してコンテナを起動してみる

コンテナの素となるDockerイメージは、以下の順序で作られます。

  1. Dockerfileの作成(Docker Serverへの命令内容を記述)
  2. イメージのビルド(docker build .の実行)

Dockerfileの作成

Dockerfileにも記述の順序があります。

  1. ベースイメージの指定
  2. 追加プログラムをインストールするためのコマンドを実行
  3. コンテナプロセスの起動コマンドの指定

例えば、redisイメージ用のDockerファイルは以下のように記述します。

# Use an existing docker image as a base
FROM alpine

# Download and install a dependency
RUN apk add --update redis

# Tell the image what to do then it starts
# as a container
CMD ["redis-server"]

*Alpine: Linuxのディストリビューション(Linuxカーネルとその他ソフトウェア群を1つにまとめ、容易にインストール・利用できるようにしたもの)
*apk: Alpineのパッケージマネージャ
*redis: インメモリデータベース

Dockerfileの中身は、FROM, RUN, CMDのようなDocker Serverへの命令内容に引数を付加するような構成になっています。

イメージのビルド

docker build .でDockerfileからコンテナイメージを作成(ビルド)します。最後の.ではDockerfileのあるディレクトリを指定しています。今回はカレントディレクトリに置いてあるので.のみとしています。

以下がビルドの流れです。

1. コンテナが起動しapk add --update redisでredis関連のプログラムをインストール

Alpineイメージのファイルシステム(FS)スナップショットがコンテナ内のディレクトリにコピーされコンテナが起動します。その後にapk add --update redisでredis関連のプログラムがインストールされます。
スクリーンショット 2021-03-05 21.47.28.png

2. コンテナが停止し新しいイメージAが作られる

redisがインストールされたファイルシステムのスナップショットがつくられ、コンテナは停止します。コンテナが停止した後に、FSスナップショットから新しいイメージAがつくられます。
スクリーンショット 2021-03-05 21.33.43.png

3. 再度起動したコンテナに起動時のコマンドredis-serverを設定する

スクリーンショット 2021-03-05 21.34.05.png

4. コンテナが停止し起動コマンドが設定された最終的なイメージBが作られる

redis関連プログラムとコンテナ起動時のコマンドが設定された最終的なイメージがこれで完成します。
スクリーンショット 2021-03-05 21.34.23.png

完成したイメージからコンテナを作成・起動する

docker run [container id]でイメージからコンテナの作成と起動を行うことができます。しかし、この方法だとイメージをビルドしたときにcontainer idを控えておかなければなりません。

そんなとき、ビルド時にdocker build -t [タグ名] .とすることでイメージにタグを付けることができ、docker run [タグ名]でコンテナを実行することができます。
タグ名としては、[DockerID]/[Project名]:Versionとつけるのが一般的です。

以上がDockerfileの作成からコンテナを起動するまでの流れです。

ちなみに、以下のコマンドを実行すれば、Dockerfileを使わずにこれまでの流れを実施することができます。
docker run -it alpine sh
apk add --update redis
docker commit -c 'CMD['redis-server']' [id]
docker run ~

おわりに

記事でアウトプットしたことでコンテナとDockerの理解が深まりました。次は実際のプロジェクトにDockerを導入したときの記事を書きたいと思います。

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者がDockerで、Django+gunicorn+nginx+PostgreSQLで環境を構築する

Dockerfileを作成

Dockerfile
FROM python:3.7

RUN apt-get update && apt-get install -y \
    nano
WORKDIR  /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt

RUN mkdir -p /var/run/gunicorn
#CMD ["gunicorn", "conf.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

requirements.txtを作成

requirements.txt
Django
psycopg2
gunicorn

docker-compose.ymlを作成

docker-compose.yml
version: '3'

volumes:
  gunicorn:
    driver: local
  dbdata:

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: django
    ports:
      - '8000:8000'
    volumes:
      - './web:/code'
      - './web/static:/static'
      - gunicorn:/var/run/gunicorn
    tty: true
    stdin_open: true

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - '80:80'
    volumes:
      - './nginx/conf:/etc/nginx/conf.d'
      - './web/static:/static'
      - gunicorn:/var/run/gunicorn
    depends_on:
      - web

  db:
    image: postgres
    container_name: db
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    environment:
        - 'POSTGRES_DB=postgres'
        - 'POSTGRES_USER=postgres'
        - 'POSTGRES_PASSWORD=password'
        - 'POSTGRES_HOST_AUTH_METHOD=trust'

ディレクトリを作る

構成
.
├── Dockerifle
├── requirements.txt
├── docker-compose.yml
├── nginx
│   └── conf.d    # gunicornの設定ファイルを格納するため
└── web           # djangoプロジェクトを格納するため

gunicornの設定ファイルを作成

構成
.
├── docker-compose.yml
├── Dockerifle
├── requirements.txt
├── nginx
│   └── conf
│       └── gunicorn.conf    # コレを作成
└── web       

gunicorn.confの中身はこのような感じです。

gunicorn.conf
upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}

server {
    listen 80;
    server_name localhost;

    location /static {
        alias /static/;
    }

    location / {
        try_files $uri @gunicorn;
    }

    location @gunicorn {
        proxy_pass http://gunicorn-django;
    }
}

docker-composeでイメージをビルドし、コンテナを起動する

$ docker-compose up -d

$ docker-compose ps でコンテナが二つともUpになっていることを確認します。

djangoプロジェクトを作成

まずはdjangoコンテナに入ります。

$ docker-compose exec web bash

djangoプロジェクトを作成します。

$ django-admin startproject conf .

settings.pyを変更

先ほど作成したdjangoプロジェクト内にあるsettings.pyのALLOWED_HOSTSを変更します。とりあえずどんなサーバーアドレスからのアクセスも受理することにしますが、必要に応じて変更した方が良いような気がします。。。

settings.py
ALLOWED_HOSTS = ['*']

exitでコンテナから抜けます。 そして、一旦、コンテナをダウンさせます。stopでも良いかもしれません。

$ docker-compose down

次にDockerfileに追記します。

Dockerfile
# 末尾に追加
CMD ["gunicorn", "conf.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

再びDockerをupさせます。

$ docker-compose up --build -d

$ docker-compose psでコンテナがUpになっていることを確認したら、ブラウザからlocalhost:80にアクセスし、ロケットが飛び立っていることを確認します。
スクリーンショット 2021-02-23 11.43.02.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者がDockerを使ってDjango+gunicorn+nginx+PostgreSQLの環境を構築する

Dockerfileを作成

Dockerfile
FROM python:3.7

RUN apt-get update && apt-get install -y \
    nano
WORKDIR  /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt

RUN mkdir -p /var/run/gunicorn
#CMD ["gunicorn", "conf.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

requirements.txtを作成

requirements.txt
Django
psycopg2
gunicorn

docker-compose.ymlを作成

docker-compose.yml
version: '3'

volumes:
  gunicorn:
    driver: local
  dbdata:

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: django
    ports:
      - '8000:8000'
    volumes:
      - './web:/code'
      - './web/static:/static'
      - gunicorn:/var/run/gunicorn
    tty: true
    stdin_open: true

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - '80:80'
    volumes:
      - './nginx/conf:/etc/nginx/conf.d'
      - './web/static:/static'
      - gunicorn:/var/run/gunicorn
    depends_on:
      - web

  db:
    image: postgres
    container_name: db
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    environment:
        - 'POSTGRES_DB=postgres'
        - 'POSTGRES_USER=postgres'
        - 'POSTGRES_PASSWORD=password'
        - 'POSTGRES_HOST_AUTH_METHOD=trust'

ディレクトリを作る

構成
.
├── Dockerifle
├── requirements.txt
├── docker-compose.yml
├── nginx
│   └── conf.d    # gunicornの設定ファイルを格納するため
└── web           # djangoプロジェクトを格納するため

gunicornの設定ファイルを作成

構成
.
├── docker-compose.yml
├── Dockerifle
├── requirements.txt
├── nginx
│   └── conf
│       └── gunicorn.conf    # コレを作成
└── web       

gunicorn.confの中身はこのような感じです。

gunicorn.conf
upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}

server {
    listen 80;
    server_name localhost;

    location /static {
        alias /static/;
    }

    location / {
        try_files $uri @gunicorn;
    }

    location @gunicorn {
        proxy_pass http://gunicorn-django;
    }
}

docker-composeでイメージをビルドし、コンテナを起動する

$ docker-compose up -d

$ docker-compose ps でコンテナが全てUpになっていることを確認します。

djangoプロジェクトを作成

まずはdjangoコンテナに入ります。

$ docker-compose exec web bash

djangoプロジェクトを作成します。

$ django-admin startproject conf .

settings.pyを変更

先ほど作成したdjangoプロジェクト内にあるsettings.pyのALLOWED_HOSTSを変更します。とりあえずどんなサーバーアドレスからのアクセスも受理することにしますが、必要に応じて変更した方が良いような気がします。。。

settings.py
ALLOWED_HOSTS = ['*']

exitでコンテナから抜けます。 そして、一旦、コンテナをダウンさせます。stopでも良いかもしれません。

$ docker-compose down

次にDockerfileに追記します。

Dockerfile
# 末尾に追加
CMD ["gunicorn", "conf.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

再びDockerをupさせます。

$ docker-compose up --build -d

$ docker-compose psでコンテナがUpになっていることを確認したら、ブラウザからlocalhost:80にアクセスし、ロケットが飛び立っていることを確認します。
スクリーンショット 2021-02-23 11.43.02.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker buildできない【echoコマンド】

前提

ホストOS:Windows(touchコマンドが使えない)

起こったこと

下記イメージを作ろうとしたところ…

Dockerfile
FROM ubuntu:latest
RUN touch hoge
$docker build .

エラーがでました。

failed to solve with frontend dockerfile.v0: failed to create LLB definition: dockerfile parse error line 1: unknown instruction: 
��F R O M

原因

Dockerfileのファイルを作成する際に、
WindowsOSのためtouchコマンドが使えず、
新規ファイル作成の際、echoコマンドを使っていましたが、
その使い方がNGでした。

具体的には下記がNG 

echo "" > Dockerfile

理屈は
①Dockerfileに空の文字列を挿入する。
②Dockerfileがないので空の新規ファイルが作られる。

なんですが、echoコマンドで出力すると、デフォルトで出力文字の最後が改行されるようで
それが原因なのか微妙(入力して修正するので)ですが、
-nオプションをつけると解決しました。

echo -n > Dockerfile
docker build .

原因曖昧であれなのですが、、
とにかく、echo "" > Dockerfileという書き方は使わないようにします。

参考:echoコマンドの詳細まとめました【Linuxコマンド集】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker buildできない際の原因【echoコマンド】

前提

ホストOS:Windows(touchコマンドが使えない)

起こったこと

下記イメージを作ろうとしたところ…

Dockerfile
FROM ubuntu:latest
RUN touch hoge
$docker build .

エラーがでました。

failed to solve with frontend dockerfile.v0: failed to create LLB definition: dockerfile parse error line 1: unknown instruction: 
��F R O M

原因

Dockerfileのファイルを作成する際に、
WindowsOSのためtouchコマンドが使えず、
新規ファイル作成の際、echoコマンドを使っていましたが、
その使い方がNGでした。

具体的には下記がNG 

echo "" > Dockerfile

理屈は
①Dockerfileに空の文字列を挿入する。
②Dockerfileがないので空の新規ファイルが作られる。

なんですが、echoコマンドで出力すると、デフォルトで出力文字の最後が改行されるようで
それが原因なのか微妙(入力して修正するので)ですが、
-nオプションをつけると解決しました。

echo -n > Dockerfile
docker build .

原因曖昧であれなのですが、、
とにかく、echo "" > Dockerfileという書き方は使わないようにします。

参考:echoコマンドの詳細まとめました【Linuxコマンド集】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

開発環境のLaravelアプリケーションにphp-graph-sdkを使用してFacebookログイン機能を組み込む時に詰まったこと

LaravelでFacebookログインボタンを設置して、AccessTokenを取得する処理を書きたい

とりあえず以下のライブラリを使うことに。

Dockerfileとdocker-compose.ymlを使用して開発環境を作成。http://localhost:3000/facebook/loginでボタンを表示するようにしました。
以下にコードを記しますが、Facebookのドキュメントのまんまです。

Controllers/FacebookController.phpの一部

//ログインボタンを表示する処理

public function login()
{
  $fb = new Facebook\Facebook([
    'app_id' => '{app-id}',
    'app_secret' => '{app-secret}',
    'default_graph_version' => 'v2.10',
  ]);

  $helper = $fb->getRedirectLoginHelper();

  $permissions = ['email']; // Optional permissions

  //callback先はhttp://localhost:3000/facebook/callbackに
  $loginUrl = $helper->getLoginUrl('http://localhost:3000/facebook/callback', $permissions);

  echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';
}
//callback先でAccessTokenを表示する処理

public function callback()
{
  $fb = new Facebook\Facebook([
    'app_id' => '{app-id}',
    'app_secret' => '{app-secret}',
    'default_graph_version' => 'v2.10',
  ]);

  $helper = $fb->getRedirectLoginHelper();

  try {
    //後々ここでエラー発生!!!!!!!!!
    $accessToken = $helper->getAccessToken();
  } catch(Facebook\Exception\ResponseException $e) {
    echo 'Graph returned an error: ' . $e->getMessage();
    exit;
  } catch(Facebook\Exception\SDKException $e) {
    echo 'Facebook SDK returned an error: ' . $e->getMessage();
    exit;
  }

  $oAuth2Client = $fb->getOAuth2Client();
  $tokenMetadata = $oAuth2Client->debugToken($accessToken);
  $tokenMetadata->validateAppId($config['app_id']);
  $tokenMetadata->validateExpiration();

  if (! $accessToken->isLongLived()) {
    try {
      $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
    } catch (Facebook\Exception\SDKException $e) {
      echo "<p>Error getting long-lived access token: " . $e->getMessage() . "</p>\n\n";
      exit;
    }

    echo '<h3>AccessToken</h3>';
    var_dump($accessToken->getValue());
  }
}

意気揚々とログインを実行するとエラーが発生...

エラー内容はこちらです。

Graph returned an error:
Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request

Googleログインなどでもよくある、APIの管理画面でリダイレクトURLを設定忘れだと思い管理画面へアクセス。

ドキュメント読むとなんとFacebookログインのリダイレクト先はhttps必須とのこと..

2018年10月6日から、すべてのアプリでHTTPSの使用が必須になります。

しかし!!
管理画面を見てみると以下の記述がありました!
http://localhost のリダイレクトは開発モードでのみ自動的に許可され、ここに追加する必要はありません」
スクリーンショット 2021-03-03 18.54.56.png

開発モードの場合はhttp://localhost からもログインできる!

Facebookはリダイレクト先のhttps強制。
しかし、localhostは別とのこと。良かった!!

開発モードについてはこちらを参照。

ざっくりまとめると以下のようなことが書かれている。

アプリのモードは、誰がアプリを使用できるかを決定します。
開発モードのアプリは、アプリ上の役割を持つアプリユーザー、またはアプリを主張しているビジネスの役割を持つアプリユーザーのみが使用できます。

ってことは自分のアプリ開発モードになっていない説..?

調べたところ..
自分のはビジネスアプリとして作成したてのアプリだったので確かに開発モードになっていました。
厳密に言うと、ビジネスアプリで作成したアプリには開発モードという概念は無いそうですが、一般に公開をしていない限り開発モードと同じらしい。

新しく作成されたアプリはすべて開発モードでスタートし、アプリの開発が完了するまでライブモードに切り替えないでください。
ビジネスアプリにはアプリモードがなく、同じ機能を提供するアクセスレベルに依存していることに注意してください。

ということはなぜリダイレクトエラー出るの??解決法

エラーが出ている箇所は上記のFacebookController.phpのcallback()内の以下の一文でした。
$accessToken = $helper->getAccessToken();

結論ですが、以下のようにcallback先のURLを明示的に渡してあげると解決しました。

$accessToken = $helper->getAccessToken(url(/facebook/callback)');
  or
$accessToken = $helper->getAccessToken(http://localhost:3000/facebook/callback);

なぜこの方法で解決できた??

ライブラリ内のgetAccessToken()を覗いてみます。こちらの行に関数が書かれています。

getAccessToken()の引数はnullを渡しても、ログインボタンに埋め込まれているURLからリダイレクトして、現在地のURLをよしなに取得してくれるはず。

//ログインボタンの所はこう書きました
$loginUrl = $helper->getLoginUrl('http://localhost:3000/facebook/callback', $permissions);
echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';
//ライブラリの中身抜粋 (現在地からURLを取得してくれる処理)
$redirectUrl = $redirectUrl ?: $this->urlDetectionHandler->getCurrentUrl();
$redirectUrl = FacebookUrlManipulator::removeParamsFromUrl($redirectUrl, ['code', 'enforce_https', 'state']);
echo $redirectUrl

この関数を手元で実行してみると....
以下が返ってきました。ポート番号の:3000が消えてる...これだとredirectUrlが確かに一致していません。

http://localhost/facebook/callback

理想は以下のURLを返したい

理想:http://localhost:3000/facebook/callback

さらに探っていくと原因が分かりました。
ポート番号を取得する処理はこんな感じで書かれていました。

$port = isset($_SERVER["HTTP_X_FORWARDED_PORT"]) ? $_SERVER["HTTP_X_FORWARDED_PORT"] : '';

これを実行すると、''が返ってきました。
$_SERVER["HTTP_X_FORWARDED_PORT"]に値が入っていないようです。

HTTP_X_FORWARDED_PORTがよく分かっていないですが、、調べてみると

If you are serving from behind a proxy server, you will almost certainly save time by looking at what these $_SERVER variables do on your machine behind the proxy.

日本語訳: プロキシサーバの後ろからサービスを提供している場合、これらの $_SERVER 変数がプロキシの後ろのマシンで何をしているかを見れば、ほぼ確実に時間を節約することができます。

X-Forwarded-Port リクエストヘッダーは、ロードバランサーへの接続にクライアントが使用した送信先ポートを識別するために役立つそうです。

なるほど、今回はDockerを使っています。ポート番号3000をブラウザのURLの入力欄に入力すると、nginxの80番にポートフォワードするようにしています。プロキシサーバーは使用していないので、$_SERVER["HTTP_X_FORWARDED_PORT"]に値が入っていないのですね。おそらく。

$port = isset($_SERVER["HTTP_X_FORWARDED_PORT"]) ? $_SERVER["HTTP_X_FORWARDED_PORT"] : $_SERVER["HTTP_HOST"];

こう書くと"localhost:3000"を返してくれますが..
どうするのがベストなのだろう??

ということでプロキシサーバーを使用せずに、このライブラリを使用したFacebookログイン機能で詰まるかもしれません。

そんな場合は以下のように書いてみてください!

$accessToken = $helper->getAccessToken(url(/facebook/callback)');
   or
$accessToken = $helper->getAccessToken(http://localhost:3000/facebook/callback);

以上です。
コメントや指摘等お待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerによる開発環境を立ち上げるまで【Rails + Nginx + Unicorn + MySQL】

Dockerを導入し、コンテナを立ち上げVSCodeで開発を行うところまで環境構築ができました。
備忘として導入した流れを簡単にまとめておきたいと思います。

インストール

Docker公式サイトからDocker Desctopをダウンロード。
以下の記事を参考にしてインストールしました。

参考記事:Dockerインストール手順<macOS向け>

フォルダ構成

Dockerfileは記事によって配置場所が異なりますが、個人的にはこの構成が一番綺麗にまとまってると思いました。
Dockerfileやdocker-compose.yml、それにNginxやUnicornの設定ファイルは自動生成されないので自分で作ります。

app
├ config
│ ├ database.yml
│ └ unicorn_development.conf.rb
├ docker
│ ├ web
│ │ └ Dockerfile
│ └ nginx
│   ├ Dockerfile
│   └ revorite.conf
└ docker-compose.yml

Docker関連ファイルの記述

以下記事を参考にさせていただきました。
参考記事:DockerでNginx+unicorn+rails+Mysqlの開発環境を作ってみた

docker/web/Dockerfile

FROM ruby:2.7.1
# dockerizeパッケージダウンロード用環境変数
ENV DOCKERIZE_VERSION v0.6.1

# パッケージの取得
RUN apt-get update && \
    apt-get install -y --no-install-recommends\
    nodejs  \
    vim \
    mariadb-client  \
    build-essential  \
    wget \
    && wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN echo "alias cp='cp -i'" >> /root/.bashrc
RUN echo "alias mv='mv -i'" >> /root/.bashrc
RUN echo "alias rm='rm -i'" >> /root/.bashrc
RUN echo "alias la='ls -al'" >> /root/.bashrc
RUN echo "alias ll='ls -l'" >> /root/.bashrc

RUN gem install bundler
RUN bundle install

COPY . /app

docker/nginx/Dockerfile

FROM nginx:stable
# デフォルトのNginxの設定ファイルを削除し、作成しておいた設定ファイルをコピー
RUN rm -f /etc/nginx/conf.d/*
COPY ./docker/nginx/revorite.conf /etc/nginx/conf.d/revorite.conf

RUN echo "alias cp='cp -i'" >> /root/.bashrc
RUN echo "alias mv='mv -i'" >> /root/.bashrc
RUN echo "alias rm='rm -i'" >> /root/.bashrc
RUN echo "alias la='ls -al'" >> /root/.bashrc
RUN echo "alias ll='ls -l'" >> /root/.bashrc

# -c以降の設定ファイルを指定して起動 daemon offでフォアグラウンドで起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

docker/nginx/revorite.conf (Nginx設定ファイル)

docker/nginx/revorite.conf
# log directory
error_log  /var/log/nginx.error.log;
access_log /var/log/nginx.access.log;
# max body size
client_max_body_size 2G;
upstream unicorn {
  # for UNIX domain socket setups
  server unix:/app/tmp/sockets/.unicorn.sock fail_timeout=0; 
}
server {
  listen 80;
  server_name localhost; 
  # nginx so increasing this is generally safe...
  keepalive_timeout 5;
  # path for static files
  root /app/public; 
  location @unicorn {
    # HTTP headers
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }
  location / {
    try_files $uri/index.html $uri.html $uri @unicorn;
    include /etc/nginx/mime.types;
  }
  location ~ ^/assets/(.*) {
    alias /app/public/assets/$1;
  }
  # Rails error pages
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /app/public; 
  }
}

docker-compose.yml

docker-compose.yml
version: '3'
services:
  web:
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    # dockerizeを使い、DBの起動を待ってからUnicornを起動する。
    command: dockerize -wait tcp://db:3306 -timeout 20s bundle exec unicorn -p 3000 -c /app/config/unicorn_development.conf.rb
    environment:
      TZ: Asia/Tokyo
    tty: true         # binding.pryを利用可能にするための2行
    stdin_open: true  # binding.pryを利用可能にするための2行
    depends_on:
      - db 
    ports:
      - "3000:3000"
    volumes:
      - .:/app:cached 
      # ソケット通信用ファイルをnginxコンテナと共有
      - tmp-data:/app/tmp/sockets
      # アセットファイルをnginxと共有
      - public-data:/app/public

  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports: 
      - "4306:3306"
    environment:
      MYSQL_DATABASE: revorite_development
      MYSQL_ROOT_PASSWORD: password
    # dbのデータを永続化しておく
    volumes:
      - mysql-data:/var/lib/mysql

  nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports:
      - 80:80
    restart: always #明示的にstopさせるまでリスタートする。(失敗するたび遅延あり)
    volumes:
      - tmp-data:/app/tmp/sockets
      - public-data:/app/public
    depends_on:
      - web 

volumes:
  public-data:
  tmp-data:
  mysql-data:

config/database.yml

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  host: db
  username: root
  password: password
  database: revorite_development

config/unicorn_development.conf.rb

config/unicorn_development.conf.rb
# set lets
$worker  = 2
$timeout = 30
$app_dir = "/app"
$listen  = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
$pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
$std_log = File.expand_path 'log/unicorn.log', $app_dir
# set config
worker_processes  $worker
working_directory $app_dir
stderr_path $std_log
stdout_path $std_log
timeout $timeout
listen  $listen
pid $pid
# loading booster
preload_app true
# before starting processes
before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      Process.kill "QUIT", File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end
# after finishing processes
after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

コンテナ作成・起動

$ docker-compose build  # イメージの作成
$ docker-compose up -d  # コンテナの作成・起動

詰まった点

他の記事ではあまり語られておらず、自分が詰まった点をいくつか。

Unicornの起動で失敗する

関連ファイルを作り終え、いざdocker-compose builddocker-compose up -dを叩く。
そしてdocker psを叩くと・・・

CONTAINER ID   IMAGE            COMMAND                  CREATED          STATUS          PORTS                               NAMES
11e605b70c51   revorite_nginx   "/docker-entrypoint.…"   21 minutes ago   Up 21 minutes   0.0.0.0:80->80/tcp                  revorite_nginx_1
313d53cbbc27   mysql:5.7        "docker-entrypoint.s…"   21 minutes ago   Up 21 minutes   33060/tcp, 0.0.0.0:4306->3306/tcp   revorite_db_1

あれ?アプリサーバは???
で、docker-compose logsでログを見てみると

web_1    | 2021/03/04 11:01:27 Command exited with error: exit status 1

exit status 1とは一体・・・
exitしたということは一度起動して即落ちた、と読めるので、落ちたコンテナに入ってログも見てみたのですがそれらしいログは出ていない。
(落ちたコンテナに入る方法はこちらを参考にしました)

最終的に、database.ymlhost: dbの記述が漏れているためだということが判明。一文追加し、無事起動しました。
こんな単純なミスなのですが一日使ってしまったので、Dockerfileとdocker-composeだけを書いて満足しないように注意です。

コンテナ内でGitが使えない

GitをインストールしSSH認証もできるようにしたのですが、どうしてもgit pushだけが弾かれてしまうんですよね。(commitまではできる)

git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

仕方ないのでgit関連の操作はホスト側で実行しています(ホスト側でコンテナ内のソースコードに対しマウントしている)。git操作もコンテナ内で完結できたらベストだったのですが。
 

コンテナ内で開発を行う(VSCode: Remote Development)

コンテナ内で開発を行うには、VSCodeでコンテナ内のソースを参照・開発できるRemote Developmentという拡張機能を使います。
参考記事:VS Code Remote Development で Docker 開発環境を利用する

前述の起動コマンドで起動できていれば、参考記事の通りに起動するだけで特に問題は起きないはず。

その他補足

docker関連のコマンドは長いので、~/.bashrcにエイリアスを設定すると便利。
またエラーが起きる度にコンテナやイメージの削除コマンドを打つのが大変のため、こちらも設定しておくと快適です。

# 例
alias dco='docker-compose'
alias docker-purge='docker stop $(docker ps -q) && docker rm $(docker ps -a -q) && docker rmi $(docker images -q -a) -f'

改善点や誤りなどありましたらコメントで指摘いただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Komposeを使ってdocker-compose.ymlからk8sマニフェストファイルを作成する

はじめに

この記事の環境は以下のGitHubにあります。
https://github.com/bocci-farm/docker-rails

インストール

% brew install kompose

元になるdocker-composeを作成

versionは"3"や"3.8", "3.9"だと後のkomposeの工程でエラーになったので"3.7"にしています。

docker-compose.yml
version: "3.7"
services:
  db:
    image: boccifarm/mysql:8.0.23
    build:
      context: ./docker/db/
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - 3306:3306
      - 33060:33060
    volumes:
      - type: volume
        source: db
        target: /var/lib/mysql
  app:
    image: boccifarm/rails:6.1.3-ruby3.0.0-alpine3.13
    build:
      context: ./app/
      dockerfile: ../docker/app/Dockerfile
    environment:
      RAILS_DATABASE_HOST: db
      RAILS_DATABASE_USERNAME: root
      RAILS_DATABASE_PASSWORD: password
    ports:
      - 3000:3000
    depends_on:
      - db
  web:
    image: boccifarm/nginx:1.18.0-alpine
    build:
      context: ./docker/web/
    ports:
      - 80:80
    depends_on:
      - app
volumes:
  db:

まずはdocker-composeが正常に起動することを確認

% DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build
% docker-compose up -d

ブラウザから確認します。
http://localhost/
スクリーンショット 2021-03-05 15.54.17.png

問題なければdocker-composeを落としておきます。

% docker-compose stop

komposeを使ってk8sマニフェストファイルを作成

% mkdir -p k8s/overlays/local
% kompose convert --volumes hostPath -o k8s/overlays/local

k8sマニフェストファイルの編集

作成されるのはあくまで雛形なので自分の環境に合うように微調整します。

hostPathのpathにdocker volumeを指定する

こうすることでdocker-composeで使用していたdocker volumeを共用できます。

k8s/overlays/local/db-deployment.yaml
 41       volumes:
 42         - hostPath:
 43             path: docker-rails_db  # <= docker volumeに変更

ローカルのイメージを使用するようにimagePullPolicy: Neverを追加

imagePullPolicy: Neverを設定するとローカルにあるイメージを使うように強制できます。

k8s/overlays/local/app-deployment.yaml
 34           image: boccifarm/rails:6.1.3-ruby3.0.0-alpine3.13
 35           imagePullPolicy: Never  # <= 追加
k8s/overlays/local/db-deployment.yaml
 31           image: boccifarm/mysql:8.0.23
 32           imagePullPolicy: Never  # <= 追加
k8s/overlays/local/web-deployment.yaml
 27         - image: boccifarm/nginx:1.18.0-alpine
 28           imagePullPolicy: Never  # <= 追加

serviceのTypeをNodePortにする

ローカル環境なのでserviceのTypeをNodePortにします。
今回はweb(nginx)だけ外からアクセスできればいいので、web-service.yamlのみ編集します。

k8s/overlays/local/web-service.yaml
 11 spec:
 12   ports:
 13     - name: "80"
 14       port: 80
 15       targetPort: 80
 16   selector:
 17     io.kompose.service: web
 18   type: NodePort  # <= 追加

k8sの起動

% kubectl apply -f k8s/overlays/local
% kubectl get po,deploy,svc
NAME                       READY   STATUS    RESTARTS   AGE
pod/app-b75844fb8-24fqx    1/1     Running   0          17s
pod/db-9cb76c59d-tvmtq     1/1     Running   0          17s
pod/web-785bb88999-nkxkd   1/1     Running   0          17s

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/app   1/1     1            1           17s
deployment.apps/db    1/1     1            1           17s
deployment.apps/web   1/1     1            1           17s

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)              AGE
service/app          ClusterIP   10.102.65.9      <none>        3000/TCP             17s
service/db           ClusterIP   10.98.6.248      <none>        3306/TCP,33060/TCP   17s
service/kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP              16d
service/web          NodePort    10.110.198.177   <none>        80:31427/TCP         17s

ブラウザから確認します。ポートはNodePort(今回は31427)を指定します。
http://localhost:31427/
スクリーンショット 2021-03-05 16.22.58.png

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのMySQLデータベースをXAMPPからDockerに切り替え

自分はこのやり方でできたという記録用です。私はXAMPPを使っていましたが、Dockerに切り替えたいと思います。
既にアプリの中身は完成している状態から始めます。

プロジェクトのターミナルでDocker imageをダウンロード

docker pull mysql

mysqlサーバーを立てる

docker run --name mysql-server -e MYSQL_ROOT_PASSWORD="admin" -d -p 53306:3306 mysql

mysqlへログイン

mysql -uroot -padmin -h 127.0.0.1 -P 53306

XAMPPで作成していたデータベースをDockerのmysqlでも作成します。
新しいデータベースをXAMPPのものと同じように作り変えます。

php artisan migrate:fresh

エラー

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client (SQL: SHOW FULL TABLES WHERE table_type = 'BASE TABLE')

認証方式を変えます。

SELECT user, host, plugin FROM mysql.user;

//私の結果
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| root             | %         | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+

私の場合rootの二つを変えました。

alter user 'root'@'%' identified with mysql_native_password by 'pass';
alter user 'root'@'localhost' identified with mysql_native_password by 'pass';

envファイルを書き換えます。

DB_CONNECTION=mysql
DB_HOST=localhost  //変更
DB_PORT=53306    //変更
DB_DATABASE=database_name  //自分が新しく作ったデータベース名
DB_USERNAME=root
DB_PASSWORD=pass  //先程のパスワード

ここまでできたらマイグレーションします。

php artisan migrate:fresh

php artisan serveと打つとデータベースにデータが入っていない状態で立ち上がると思います。シーダーファイルの中身を入れます。

php artisan db:seed

これでデータベースの移行が完了しました。

参考
https://tyablog.net/2017/06/10/docker-setup-mysql/
https://symfoware.blog.fc2.com/blog-entry-2160.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsのDockerイメージの作り方

0. はじめに

以下のようなディレクトリ構成にします。

.
├── app/       # Railsアプリ
└── docker/    # Dockerfile置き場
    └── app/
        └── Dockerfile

1. ローカル環境にRailsをインストール

% rails new app

2. Dockerfileの作成

docker/app/Dockerfile
# syntax=docker/dockerfile:1.2

# base
FROM ruby:3.0.0-alpine3.13 AS base
WORKDIR /app
RUN \
  --mount=type=cache,target=/var/cache/apk \
  apk add -U \
    build-base \
    git \
    nodejs \
    sqlite-dev \
    tzdata \
    yarn \
    ;

# bundle
FROM base AS bundle
COPY Gemfile Gemfile.lock /app/
RUN \
  --mount=type=cache,target=/app/vendor/cache \
  bundle install && \
  bundle cache

# yarn
FROM base AS node_modules
COPY package.json yarn.lock /app/
RUN \
  --mount=type=cache,target=/usr/local/share/.cache/yarn/v4 \
  yarn install

# main
FROM base
ARG RAILS_MASTER_KEY
ENV RAILS_MASTER_KEY $RAILS_MASTER_KEY
COPY --from=bundle /usr/local/bundle/ /usr/local/bundle/
COPY --from=node_modules /app/node_modules/ /app/node_modules/
COPY . /app/
EXPOSE 3000
CMD ["bundle", "exec", "rails", "s", "-b", "0.0.0.0"]

3. .dockerignoreの用意

不要なファイルをイメージに含めないために.dockerignoreを作成します

app/.dockerignore
/.bundle/
/.dockerignore
/.git/
/.git*
/.ruby-version
/README.md
/config/master.key
/log/
/node_modules/
/storage/
/tmp/
/vendor/bundle/

4. Dockerイメージのビルド

% DOCKER_BUILDKIT=1 docker image build --build-arg RAILS_MASTER_KEY=<RAILS_MASTER_KEY> -t boccifarm/rails:6.1.3-ruby3.0.0-alpine3.13 -f docker/app/Dockerfile app

5. Dockerイメージのプッシュ

% docker login
% docker image push boccifarm/rails:6.1.3-ruby3.0.0-alpine3.13
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Docker】Ruby2.6.5とRails6.0.0とmysql DockerComposeで環境構築

はじめに

Ruby2.6.5とRails6.0.0とmysqlでDockerComposeで環境構築をしたので記録として記事を書きます。
もし誰かのお役に立てたら幸いです。

Docker環境でアプリケーション開発する手順

1、RailsのDockerfileを作成してDockerをbuild
2、DockerCompose.ymlを作成しコンテナを作る
3、Rails のセットアップ
4、データベースを作成する

開発環境

Docker version 20.10.0
docker-compose version 1.27.4

1、RailsのDockerfile作成

新しいデレクトリを作成します。
今回はデレクトリ名をexamとしてます。

ターミナル
mkdir [新規デレクトリ名]
cd [新規デレクトリ名]

Dockerfile

新規で作ったデレクトリに「Dockerfile」をいう名前でDockerファイルを作成します。
VSコードをお使いの方はcode Dockerfileでターミナルから直接ファイルの作成&移動をしてくれます。
もちろんディレクトリを呼び込んでファイルを作成しても大丈夫です。

exam/Dockerfile
FROM ruby:2.6.5
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs\
    yarn \
    vim 

WORKDIR /新規デレクトリ名
COPY Gemfile Gemfile.lock /新規デレクトリ名/
RUN bundle install

Gemファイルを作成する

ターミナル
touch Gemfile Gemfile.lock

Gemfileにファイルを持ってくるソースとrailsのバージョンを記述します。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'

Dockerを建てます。

ターミナル
docker build .
.
.
.
Installing rails 6.1.2.1
Bundle complete! 1 Gemfile dependency, 42 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Removing intermediate container 06448279a57c
 ---> c21a3ec7e3a4
Successfully built c21a3ec7e3a4

#イメージが出来てるか調べる事も出来ます。
docker images 

#もし使っていないイメージがあれば下記のコマンドで削除できます。
docker rmi [IMAGE ID]

2、DockerCompose.ymlを作成しコンテナを作る

dockerc-compose.ymlファイルを作成し記述する

ターミナル
code docker-compose.yml

これで新しいdocker-compose.ymlが作成されます。

docker-compose.yml
version: '3'

services:
  web:
    build: .
    ports:
      - 3000:3000
    volumes:
      - '.:/[作成したディレクトリ名]'
    tty: true
    stdin_open: true

dockerc-ompose.ymlの確認をして実行します

#確認します
cat docker-compose.yml

#実行します
docker-compose up -d


#コンテナが動いてる事を確認します。
docker ps
docker-compose ps

#コンテナの中に入ります。
docker-compose exec web bash

3、Rails のセットアップ

Railsの環境でコンテナができのでその中でrails newをしてセットアップして行きます。

#新しいrailsを作ります
rails new . --force --database=mysql --skip-bundle

#一度コンテナから抜けます
exit

#今のコンテナとイメージを削除します
docker-compose down

#Downになってるか確認します
docker ps

#新しいイメージでコンテナを建てます
docker-compose up --build -d

#確認します
docker-compose ps

#コンテナに入ります
docker-compose exec web bash

#Railsを起動します
rails s -b 0.0.0.0 #現段階ではエラーが出ます

現段階ではデータベースの記述を Dockerfileに記述していないので、mysql2のエラーが出ると思います。
下記の対応を試して下さい。

【Docker】エラー Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile
https://qiita.com/AKI3/items/8009b5218be0ad67b6b7

エラーが解決したらrails s -b 0.0.0.0でサーバーが立ち上がります。
実際にlocalhost:3000でアクセスします。
しかしこのままではデータベースが無いのでエラー画面が表示されます。
スクリーンショット 2021-02-15 17.02.13.png

4、データベースを作成する。

ローカルであればrails db:createをするとデータベースを作れるのですが、コンテナ内では現状の設定では作れません。
Dockerではサービス(環境)ごとにコンテナを作る事が推奨されております。
今回であればRubyとmysqlのコンテナを別々に作り、それぞれのコンテナどうしを接続する必要があります。
その為、detabase.ymlの設定ファイルを編集して正しい指定をし、docker-compose.ymlの設定ファイルの編集します。

先ずdetabase.ymlを編集します。

config/detabase.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: db

development:
  <<: *default
  database: 新規デレクトリ名_development
  username: root
  password:

省略

次にdocker-compose.ymlを編集します。

docker-compose.yml
version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    ports:
      - 3000:3000
    volumes:
      - '.:/新規デレクトリ名'
    environment:
      - 'MYSQL_ROOT_PASSWORD=1'
    tty: true
    stdin_open: true
    depends_on:
      - db
    links:
      - db

  db:
    image: mysql:8
    volumes:
      - 'db-data:/var/lib/mysql'
    # このenvironmentはテスト環境でのみ使用可能
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1

    # command: bundle exec rails s -p 3000 -b '0.0.0.0' #こちらはお好みで

docker-compose.ymlを編集したので新しくコンテナを作ります。
今回はweb(Raisl)とdb(mysql)2つのコンテナを作る事になります。

#再びコンテナを建てます
docker-compose up -d

#upになってるか確認します
docker-compose ps

#コンテナに入ります
docker-compose exec web bash

#データベースを作成します
rails db:create #現段階ではエラーが出ます

Rails側のコンテナ内で作業を進めていきます。
先程作ったRails側のコンテナとmysql側のコンテナがしっかり繋がっていればrails db:createのコマンドは通りますが、現段階では下記のエラーが発生します。

Mysql2::Error::ConnectionError: Plugin caching_sha2_password could not be loaded: /usr/lib/x86_64-linux-gnu/mariadb19/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory

これはwebコンテナが mysql 8.0 のcaching_sha2_passwordが認証方式に対応していないためだそうです。

詳しくは、こちらの記事で解決できます。
https://blog.toshimaru.net/rails-on-docker-compose/

コマンドだけこちらに記載します。

ターミナル
 #データベース側のコンテナに入る
docker-compose exec db bash

 #mysqlを操作する
mysql -u root

 #ユーザー一覧とその認証方式が閲覧
select User,Host,plugin from mysql.user;

 #root@% のユーザー設定を変更
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '';

 #再度ユーザー一覧とその認証方式が閲覧
select User,Host,plugin from mysql.user;

エラーが解決できたら最後サーバーを立ち上げます。

ターミナル
rails s -b 0.0.0.0

=> Booting Puma
=> Rails 6.1.2.1 application starting in development 
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.2.1 (ruby 2.6.5-p114) ("Fettisdagsbulle")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 29
* Listening on http://0.0.0.0:3000
Use Ctrl-C to stop
.
.
.
省略

出来ました!
localhost3000にアクセスすると-Yay! You’re on Rails!-の画面が表示されます。
スクリーンショット 2021-02-17 17.16.29.png

最後に

手探りの対処療法でサーバー起動まで達成しましので、これからも継続学習が必要です。
なんとか環境構築まで至りましたが、Dockerについて完全に理解できておりません、万が一情報が間違っている場合ご指摘していただけると幸いです。

エンジニア初学者ですが、同じ様に悩んでる方々の助けになればと思い記事を投稿しております。
万が一情報が間違っている場合ご指摘していただけると幸いです。

参考

今回Udemyの教材とやっすんのエンジニア大学を主に参考にさせていただきました。
https://www.udemy.com/course/aidocker/
https://github.com/yassun-youtube/docker-compose-sample/blob/master/docker-compose.yml

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPUnitでHTMLコードガバレッジを出すまで(Docker使用)

はじめに

以下記事の【phpunitを少し使ってみる】までは行っている前提で話します。

最終的表示までのものgithubにあげているのでもしよかったら見てください。
どこに何を書けばいいかもここのファイル等見てもらった方が早いかもです。

最終的な見た目は以下のような感じです。

スクリーンショット 2021-03-04 23.20.21.png
スクリーンショット 2021-03-04 23.20.33.png

上記の【phpunitさわるまで】以降にすること箇条書き

※HTMLでコードガバレッジを表示するのに必要な設定

  • 【step1】 tokenizer(拡張モジュール)追加
  • 【step2】 Xdebug(拡張モジュール)追加
  • 【step3】 php.iniの設定、反映(Xdebug(拡張モジュール)有効化)
  • 【step4】 phpunit.xml(コードガバレッジ出力のための設定ファイル的なもの)の作成
  • 【step5】 メソッドの修正
  • 【step6】 最後に出力コマンド

【step1】 tokenizer(拡張モジュール)追加

Dockerfileに以下追記
※ですがもしかしたらこれいらないかもしれないです、、すでに入ってる的なログ出てた気がするので念のため書いておきます、、

Dockerfile
RUN docker-php-ext-install tokenizer

【step2】 Xdebug(拡張モジュール)追加

Dockerfileに以下追記。すでに記述してある処理はそのままで。
ちなみに『apt-get』使用でインストール出来なかったのでこのインストールの方法で行っています。
peclのインストールがwget
インストールしたpeclでxdebugをインストールするという感じです。

インストール方法は以下の記述参考
https://pear.php.net/manual/en/installation.getting.php

peclでインストールできるもの以下
https://pecl.php.net/package-stats.php

Dockerfile
RUN apt-get update \
&& apt-get install -y \
wget

# コードジェネレーターで必要
RUN pecl install xdebug

【step3】 php.iniの設定、反映(Xdebug(拡張モジュール)有効化)

xdebug使えるようにするためphp.iniを自分で作成してDockerの環境に反映するをしました。

ちなみにextension=xdebug.soの記述書け!は以下リンク先に書かれていたもので
http://xdebug.org/docs/install

xdebug.mode=coverageの記述書け!は失敗ログで言われたものです。

Warning:       XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

Dockerfileに以下追記

Dockerfile
COPY html/php.ini /usr/local/etc/php/conf.d

php.iniを以下場所に作成

html/php.ini
extension=xdebug.so

xdebug.mode=coverage

【step4】 phpunit.xml(コードガバレッジ出力のための設定ファイル的なもの)の作成

:sun_with_face:『docker-compose.yml』ある場所で$ docker-compose up --build -dを叩きます。

:sun_with_face:以下コマンド今使用しているコンテナIDの確認を行う
$ docker ps

:sun_with_face:コンテナ内に以下コマンドで入る、 ※『1999d283b4f9』は上記で出力したコンテナIDを使用してください
$ docker exec -i -t 1999d283b4f9 bash

:sun_with_face:phpunit.xmlの作成コマンドを打つ、そして聞かれた内容に答えていく

$ vendor/bin/phpunit --generate-configuration

【step5】 メソッドの修正

テストメソッド上の何のテストをするかを記述(@coversの部分)
これないとThis test does not have a @covers annotation but is expected to have oneと言われます。

    /**
     *  @covers sample\Sample::a
     **/
    public function testA()

全体
https://github.com/sachiko-kame/samplePHPTest/blob/main/html/TEST/SampleTest.php

【step6】 最後に出力コマンド

これで『TestResult』フォルダ内に無事index.htm等々作成出来て表示に問題がなければOKです!
うまくいかない場合『phpunit.xml』が同じでない可能性が高いと思うので以下と見合わせていただければと思います。
https://github.com/sachiko-kame/samplePHPTest/blob/main/html/phpunit.xml

$ vendor/bin/phpunit --coverage-html TestResult

参考

https://tech-blog.rakus.co.jp/entry/20200908/docker
https://docs.docker.jp/engine/reference/builder.html#copy
その他所々にリンク貼ってます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPUnitでHTMLコードカバレッジを出すまで(Docker使用)

はじめに

以下記事の【phpunitを少し使ってみる】までは行っている前提で話します。

最終的表示までのものgithubにあげているのでもしよかったら見てください。
どこに何を書けばいいかもここのファイル等見てもらった方が早いかもです。

最終的な見た目は以下のような感じです。

スクリーンショット 2021-03-04 23.20.21.png
スクリーンショット 2021-03-04 23.20.33.png

上記の【phpunitさわるまで】以降にすること箇条書き

※HTMLでコードカバレッジを表示するのに必要な設定

  • 【step1】 tokenizer(拡張モジュール)追加
  • 【step2】 Xdebug(拡張モジュール)追加
  • 【step3】 php.iniの設定、反映(Xdebug(拡張モジュール)有効化)
  • 【step4】 phpunit.xml(コードカバレッジ出力のための設定ファイル的なもの)の作成
  • 【step5】 メソッドの修正
  • 【step6】 最後に出力コマンド

【step1】 tokenizer(拡張モジュール)追加

Dockerfileに以下追記
※ですがもしかしたらこれいらないかもしれないです、、すでに入ってる的なログ出てた気がするので念のため書いておきます、、

Dockerfile
RUN docker-php-ext-install tokenizer

【step2】 Xdebug(拡張モジュール)追加

Dockerfileに以下追記。すでに記述してある処理はそのままで。
ちなみに『apt-get』使用でインストール出来なかったのでこのインストールの方法で行っています。
peclのインストールがwget
インストールしたpeclでxdebugをインストールするという感じです。

インストール方法は以下の記述参考
https://pear.php.net/manual/en/installation.getting.php

peclでインストールできるもの以下
https://pecl.php.net/package-stats.php

Dockerfile
RUN apt-get update \
&& apt-get install -y \
wget

# コードジェネレーターで必要
RUN pecl install xdebug

【step3】 php.iniの設定、反映(Xdebug(拡張モジュール)有効化)

xdebug使えるようにするためphp.iniを自分で作成してDockerの環境に反映するをしました。

ちなみにextension=xdebug.soの記述書け!は以下リンク先に書かれていたもので
http://xdebug.org/docs/install

xdebug.mode=coverageの記述書け!は失敗ログで言われたものです。

Warning:       XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

Dockerfileに以下追記

Dockerfile
COPY html/php.ini /usr/local/etc/php/conf.d

php.iniを以下場所に作成

html/php.ini
extension=xdebug.so

xdebug.mode=coverage

【step4】 phpunit.xml(コードカバレッジ出力のための設定ファイル的なもの)の作成

:sun_with_face:『docker-compose.yml』ある場所で$ docker-compose up --build -dを叩きます。

:sun_with_face:以下コマンド今使用しているコンテナIDの確認を行う
$ docker ps

:sun_with_face:コンテナ内に以下コマンドで入る、 ※『1999d283b4f9』は上記で出力したコンテナIDを使用してください
$ docker exec -i -t 1999d283b4f9 bash

:sun_with_face:phpunit.xmlの作成コマンドを打つ、そして聞かれた内容に答えていく

$ vendor/bin/phpunit --generate-configuration

【step5】 メソッドの修正

テストメソッド上の何のテストをするかを記述(@coversの部分)
これないとThis test does not have a @covers annotation but is expected to have oneと言われます。

    /**
     *  @covers sample\Sample::a
     **/
    public function testA()

全体
https://github.com/sachiko-kame/samplePHPTest/blob/main/html/TEST/SampleTest.php

【step6】 最後に出力コマンド

これで『TestResult』フォルダ内に無事index.htm等々作成出来て表示に問題がなければOKです!
うまくいかない場合『phpunit.xml』が同じでない可能性が高いと思うので以下と見合わせていただければと思います。
https://github.com/sachiko-kame/samplePHPTest/blob/main/html/phpunit.xml

$ vendor/bin/phpunit --coverage-html TestResult

参考

https://tech-blog.rakus.co.jp/entry/20200908/docker
https://docs.docker.jp/engine/reference/builder.html#copy
その他所々にリンク貼ってます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む