Herkese merhaba!

Uzun yıllardır bol miktarda kişisel zaman ve enerji harcayarak bilgimizi hepinizle paylaşıyoruz. Ancak şu andan itibaren bu blogu çalışır durumda tutabilmek için yardımınıza ihtiyacımız var. Yapmanız gereken tek şey, sitedeki reklamlardan birine tıklamak olacaktır, aksi takdirde hosting vb. masraflar nedeniyle maalesef yayından kaldırılacaktır. Teşekkürler.

Bu örneğimizde web uygulamamız için üç tane container (Apache, MySQL, PHP-FPM) yaratacağız.


Mantık


  1. İçerik Apache containera gider.

  2. Apache container PHP-FPM container ile iletişime geçer.

  3. PHP-FPM container MySQL container ile iletişime geçer.

  4. Cevap Apache containera iletilir.

  5. Apache container sayfayı yayınlar.

Kurulum



Buradaki en önemli nokta, mevcut olan containerları silme durumumuzda uygulama dosyalarını, log dosyalarını ve veritabanını kaybetmeyeceğiz çünkü hepsi ana sistemde tutulmaktadır.


Başlangıç yapısı


Listedeki "log" ve "database" klasörlerini manuel olarak yaratmaya gerek yok çünkü ./build.sh dosyasını ilk kez çalıştırdığımızda otomatik olarak yaratılmış olacaklar. Ayrıca chmod +x docker/build.sh komutunu bir kereye mahsus olmak üzere çalıştırmanız gerekecektir.


ubuntu@linux:~/helloworld$ tree -a
.
├── bad.php
├── docker
│ ├── apache
│ │ ├── Dockerfile
│ │ ├── httpd.conf
│ │ └── httpd-vhosts.conf
│ ├── build.sh
│ ├── destroy.sh
│ ├── docker-compose.yml
│ ├── .env
│ ├── mysql
│ │ └── Dockerfile
│ └── php
│ ├── Dockerfile
│ └── www.conf
└── index.php

4 directories, 12 files

Dosyalar


bad.php


ubuntu@linux:~/helloworld$ cat bad.php 
<?php

echo 'This is bad file which will create entry in log file'

index.php


ubuntu@linux:~/helloworld$ cat index.php 
<?php

echo 'Hello World!'.PHP_EOL;

$servername = getenv('MYSQL_IP');
$username = getenv('MYSQL_ROOT_USER');
$password = getenv('MYSQL_ROOT_PASSWORD');

$conn = mysqli_connect($servername, $username, $password);
if (!$conn) {
exit('Connection failed: '.mysqli_connect_error().PHP_EOL);
}

echo 'Successful database connection!'.PHP_EOL;

docker/build.sh


ubuntu@linux:~/helloworld$ cat docker/build.sh 
#!/bin/bash
set -e

if ! [[ -d ../logs/apache ]]; then
mkdir -p ../logs/apache
fi

if ! [[ -d ../logs/mysql ]]; then
mkdir -p ../logs/mysql
fi

if ! [[ -d ../logs/php ]]; then
mkdir -p ../logs/php
fi

if ! [[ -d ../database ]]; then
mkdir ../database
fi

docker-compose up -d --build

docker exec helloworld_apache_con chown -R root:www-data /usr/local/apache2/logs
docker exec helloworld_php_con chown -R root:www-data /usr/local/etc/logs

docker/destroy.sh


ubuntu@linux:~/helloworld$ cat docker/destroy.sh 
#!/bin/bash
set -e

docker-compose down --volumes
docker rmi helloworld_apache_img helloworld_php_img

docker/.env


ubuntu@linux:~/helloworld$ cat docker/.env 
COMPOSE_PROJECT_NAME=helloworld

WEB_USER=www-data
WEB_GROUP=www-data

APACHE_IP=192.168.0.11
APACHE_EXPOSED_PORT=9000
APACHE_ROOT_DIR=/usr/local/apache2

MYSQL_IP=192.168.0.22
MYSQL_CONTAINER_USER=mysql
MYSQL_CONTAINER_GROUP=mysql
MYSQL_ROOT_USER=root
MYSQL_ROOT_PASSWORD=root
MYSQL_DATA_DIR=/var/lib/mysql
MYSQL_LOG_DIR=/var/log/mysql

PHP_IP=192.168.0.33
PHP_APP_DIR=/srv/app
PHP_ROOT_DIR=/usr/local/etc

NETWORK_SUBNET=192.168.0.0/24

docker/docker-compose.yml


ubuntu@linux:~/helloworld$ cat docker/docker-compose.yml 
version: '3'

services:
apache_img:
container_name: ${COMPOSE_PROJECT_NAME}_apache_con
build:
context: ./apache
args:
- WEB_USER=${WEB_USER}
- WEB_GROUP=${WEB_GROUP}
- APACHE_ROOT_DIR=${APACHE_ROOT_DIR}
volumes:
- ../logs/apache:${APACHE_ROOT_DIR}/logs
ports:
- ${APACHE_EXPOSED_PORT}:80
networks:
public_net:
ipv4_address: ${APACHE_IP}
environment:
- APACHE_EXPOSED_PORT=${APACHE_EXPOSED_PORT}
- APACHE_ROOT_DIR=${APACHE_ROOT_DIR}
- PHP_IP=${PHP_IP}
- PHP_APP_DIR=${PHP_APP_DIR}
- WEB_USER=${WEB_USER}
- WEB_GROUP=${WEB_GROUP}
mysql_img:
container_name: ${COMPOSE_PROJECT_NAME}_mysql_con
build:
context: ./mysql
args:
- MYSQL_CONTAINER_USER=${MYSQL_CONTAINER_USER}
- MYSQL_CONTAINER_GROUP=${MYSQL_CONTAINER_GROUP}
volumes:
- ../logs/mysql:${MYSQL_LOG_DIR}
- ../database:${MYSQL_DATA_DIR}
networks:
public_net:
ipv4_address: ${MYSQL_IP}
environment:
- MYSQL_CONTAINER_USER=${MYSQL_CONTAINER_USER}
- MYSQL_CONTAINER_GROUP=${MYSQL_CONTAINER_GROUP}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
php_img:
container_name: ${COMPOSE_PROJECT_NAME}_php_con
build:
context: ./php
args:
- WEB_USER=${WEB_USER}
- WEB_GROUP=${WEB_GROUP}
- PHP_ROOT_DIR=${PHP_ROOT_DIR}
working_dir: ${PHP_APP_DIR}
volumes:
- ..:${PHP_APP_DIR}
- ../logs/php:${PHP_ROOT_DIR}/logs
depends_on:
- apache_img
- mysql_img
networks:
public_net:
ipv4_address: ${PHP_IP}
environment:
- PHP_ROOT_DIR=${PHP_ROOT_DIR}
- APACHE_IP=${APACHE_IP}
- APACHE_EXPOSED_PORT=${APACHE_EXPOSED_PORT}
- WEB_USER=${WEB_USER}
- WEB_GROUP=${WEB_GROUP}
- MYSQL_IP=${MYSQL_IP}
- MYSQL_ROOT_USER=${MYSQL_ROOT_USER}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}

networks:
public_net:
driver: bridge
ipam:
driver: default
config:
- subnet: ${NETWORK_SUBNET}

docker/apache/Dockerfile


ubuntu@linux:~/helloworld$ cat docker/apache/Dockerfile 
FROM httpd:2.4

ARG WEB_USER
ARG WEB_GROUP
ARG APACHE_ROOT_DIR

COPY httpd-vhosts.conf ${APACHE_ROOT_DIR}/conf/extra/httpd-vhosts.conf
COPY httpd.conf ${APACHE_ROOT_DIR}/conf/httpd.conf

RUN chgrp -R ${WEB_GROUP} ${APACHE_ROOT_DIR}/conf/httpd.conf \
&& chgrp -R ${WEB_GROUP} ${APACHE_ROOT_DIR}/conf/extra/httpd-vhosts.conf

RUN usermod -u 1000 ${WEB_USER} \
&& groupmod -g 1000 ${WEB_GROUP} \
&& chgrp -R ${WEB_GROUP} ${APACHE_ROOT_DIR}

docker/apache/httpd.conf


ubuntu@linux:~/helloworld$ cat docker/apache/httpd.conf 
ServerRoot ${APACHE_ROOT_DIR}
Listen 80

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
<IfModule !mpm_prefork_module>
</IfModule>
<IfModule mpm_prefork_module>
</IfModule>
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so

<IfModule unixd_module>
User daemon
Group daemon
</IfModule>

ServerAdmin you@example.com

<Directory />
AllowOverride none
Require all denied
</Directory>

DocumentRoot ${APACHE_ROOT_DIR}/htdocs
<Directory ${APACHE_ROOT_DIR}/htdocs>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>

<Files ".ht*">
Require all denied
</Files>

ErrorLog /proc/self/fd/2

LogLevel info

<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common

<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>

CustomLog /proc/self/fd/1 common
</IfModule>

<IfModule alias_module>
ScriptAlias /cgi-bin/ ${APACHE_ROOT_DIR}/cgi-bin/
</IfModule>

<IfModule cgid_module>
</IfModule>

<Directory ${APACHE_ROOT_DIR}/cgi-bin>
AllowOverride None
Options None
Require all granted
</Directory>

<IfModule headers_module>
RequestHeader unset Proxy early
</IfModule>

<IfModule mime_module>
TypesConfig conf/mime.types

AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
</IfModule>

Include conf/extra/httpd-vhosts.conf

<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

ServerName localhost

docker/apache/httpd-vhosts.conf


ubuntu@linux:~/helloworld$ cat docker/apache/httpd-vhosts.conf 
<VirtualHost *:80>
ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://${PHP_IP}:${APACHE_EXPOSED_PORT}${PHP_APP_DIR}/$1

DocumentRoot ${APACHE_ROOT_DIR}/htdocs

<Directory ${APACHE_ROOT_DIR}/htdocs>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

ErrorLog ${APACHE_ROOT_DIR}/logs/error.log
CustomLog ${APACHE_ROOT_DIR}/logs/access.log common
</VirtualHost>

docker/mysql/Dockerfile


ubuntu@linux:~/helloworld$ cat docker/mysql/Dockerfile 
FROM mysql:5.7

ARG MYSQL_CONTAINER_USER
ARG MYSQL_CONTAINER_GROUP

RUN sed -i "s/#log-error/log-error/g" /etc/mysql/mysql.conf.d/mysqld.cnf

RUN usermod -u 1000 ${MYSQL_CONTAINER_USER} \
&& groupmod -g 1000 ${MYSQL_CONTAINER_GROUP}

docker/php/Dockerfile


ubuntu@linux:~/helloworld$ cat docker/php/Dockerfile 
FROM php:7.1-fpm

ARG WEB_USER
ARG WEB_GROUP
ARG PHP_ROOT_DIR

COPY www.conf ${PHP_ROOT_DIR}/php-fpm.d/www.conf

RUN docker-php-ext-install mysqli

RUN usermod -u 1000 ${WEB_USER} \
&& groupmod -g 1000 ${WEB_GROUP} \
&& chgrp -R staff ${PHP_ROOT_DIR}/php-fpm.d/www.conf

docker/php/www.conf


ubuntu@linux:~/helloworld$ cat docker/php/www.conf 
[www]
user = ${WEB_USER}
group = ${WEB_GROUP}
listen = 80
listen.allowed_clients = ${APACHE_IP}

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

catch_workers_output = yes
php_flag[display_errors] = off
php_admin_flag[log_errors] = on
php_admin_value[error_log] = ${PHP_ROOT_DIR}/logs/error.log

Onaylama


Mevcut olan "docker-compose.yml" dosyasını aşağıdaki gibi onaylayıp, ilişkilendirmeyi görelim.


ubuntu@linux:~/helloworld/docker$ docker-compose config
networks:
public_net:
driver: bridge
ipam:
config:
- subnet: 192.168.0.0/24
driver: default
services:
apache_img:
build:
args:
APACHE_ROOT_DIR: /usr/local/apache2
WEB_GROUP: www-data
WEB_USER: www-data
context: /home/ubuntu/helloworld/docker/apache
container_name: helloworld_apache_con
environment:
APACHE_EXPOSED_PORT: '9000'
APACHE_ROOT_DIR: /usr/local/apache2
PHP_APP_DIR: /srv/app
PHP_IP: 192.168.0.33
WEB_GROUP: www-data
WEB_USER: www-data
networks:
public_net:
ipv4_address: 192.168.0.11
ports:
- 9000:80/tcp
volumes:
- /home/ubuntu/helloworld/logs/apache:/usr/local/apache2/logs:rw
mysql_img:
build:
args:
MYSQL_CONTAINER_GROUP: mysql
MYSQL_CONTAINER_USER: mysql
context: /home/ubuntu/helloworld/docker/mysql
container_name: helloworld_mysql_con
environment:
MYSQL_CONTAINER_GROUP: mysql
MYSQL_CONTAINER_USER: mysql
MYSQL_ROOT_PASSWORD: root
networks:
public_net:
ipv4_address: 192.168.0.22
volumes:
- /home/ubuntu/helloworld/logs/mysql:/var/log/mysql:rw
- /home/ubuntu/helloworld/database:/var/lib/mysql:rw
php_img:
build:
args:
PHP_ROOT_DIR: /usr/local/etc
WEB_GROUP: www-data
WEB_USER: www-data
context: /home/ubuntu/helloworld/docker/php
container_name: helloworld_php_con
depends_on:
- apache_img
- mysql_img
environment:
APACHE_EXPOSED_PORT: '9000'
APACHE_IP: 192.168.0.11
MYSQL_IP: 192.168.0.22
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_USER: root
PHP_ROOT_DIR: /usr/local/etc
WEB_GROUP: www-data
WEB_USER: www-data
networks:
public_net:
ipv4_address: 192.168.0.33
volumes:
- /home/ubuntu/helloworld:/srv/app:rw
- /home/ubuntu/helloworld/logs/php:/usr/local/etc/logs:rw
working_dir: /srv/app
version: '3.0'

Kurulum


ubuntu@linux:~/helloworld/docker$ ./build.sh

Creating network "helloworld_public_net" with driver "bridge"
Building apache_img
Successfully built 756224de2345
Successfully tagged helloworld_apache_img:latest

Building mysql_img
Successfully built cbb571547e11
Successfully tagged helloworld_mysql_img:latest

Building php_img
Successfully built 6214b34aec76

Creating helloworld_apache_con ... done
Creating helloworld_php_con ... done
Creating helloworld_mysql_con ...
Creating helloworld_php_con ...

Doğrulama


Eğer tüm elementleri detayları ile görmek isterseniz docker inspect komutunu çalıştırabilirsiniz.


Imajlar


ubuntu@linux:~/helloworld/docker$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld_php_img latest 6214b34aec76 2 minutes ago 383MB
helloworld_mysql_img latest cbb571547e11 3 minutes ago 409MB
helloworld_apache_img latest 756224de2345 3 minutes ago 185MB
...


ubuntu@linux:~/helloworld/docker$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a7bbc7d71e55 helloworld_public_net bridge local
...

Containerlar


ubuntu@linux:~/helloworld/docker$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e8adf3e917fb helloworld_php_img "docker-php-entryp..." 4 minutes ago Up 4 minutes 9000/tcp helloworld_php_con
30d7405b37dc helloworld_mysql_img "docker-entrypoint..." 4 minutes ago Up 4 minutes 3306/tcp helloworld_mysql_con
40d387973713 helloworld_apache_img "httpd-foreground" 4 minutes ago Up 4 minutes 0.0.0.0:9000->80/tcp helloworld_apache_con

Yapı


Aşağıda'da gördüğümüz gibi, "logs" ve "database" klasörleri yaratılmış durumda ve içlerinde bir takım dosyalar vardır. Uygulamamızı kullanmaya başladığımız zaman daha fazla dosya ve log girdileri oluşacak. Ayrıca "php" log klasöründe herhangi bir dosya olmamasının nedeni ise, uygulamayı kullanmaya başlamamamızdır. Kullanmaya başladığımızda yapacağımız ilk kodlama hatası, gerekli log dosyasının yaratılmasına sebep olacaktır.


ubuntu@linux:~/helloworld$ tree -a
.
├── database
│ ├── ...
│ ├── mysql
│ │ ├── ...
│ │ └── ...
│ ├── performance_schema
│ │ ├── ...
│ │ └── ...
│ └── sys
│ ├── ...
│ └── ...
└── logs
├── apache
│ ├── access.log
│ ├── error.log
│ └── httpd.pid
├── mysql
│ └── error.log
└── php

12 directories, 299 files

Test


Başarı


ubuntu@linux:~/helloworld$ curl 192.168.0.11
Hello World!
Successful database connection!

ubuntu@linux:~/helloworld$ curl localhost:9000
Hello World!
Successful database connection!

İletişim logları yenilenmiş durumda.


ubuntu@linux:~/helloworld$ cat logs/apache/access.log 
192.168.0.1 - - [03/Feb/2018:16:13:16 +0000] "GET / HTTP/1.1" 200 45
192.168.0.1 - - [03/Feb/2018:16:13:37 +0000] "GET / HTTP/1.1" 200 45

Hata


Bu komutu kullandığınızda uygulama sessiz bir şekilde kırılacak ve gerekli olan log dosyası yaratılacaktır çünkü "bad.php" içinde hatalı kodlama mevcut.


ubuntu@linux:~/helloworld$ curl 192.168.0.11/bad.php

ubuntu@linux:~/helloworld$ cat logs/php/error.log 
[03-Feb-2018 16:24:06 UTC] PHP Parse error: syntax error, unexpected end of file, expecting ',' or ';' in /srv/app/bad.php on line 4

Bilgi


Cantainer içindeki dosya ve klasörlerin izinlerini yönetmek size kalmıştır. Her ne kadar ben "build.sh" içine iki satır eklediysemde (en altta), siz isterseniz onları silip kendinize göre değiştirebilirsiniz. Bu şekilde ana sistemdeki ve containerlar içindeki izinlerle oynayabilirsiniz.