Docker php fpm error log

Using apache + php-fpm containers in docker-compose, I can't get the php-fpm container to display any errors. docker-compose.yml version: '3' services: php: build: context: ./php ...

Using apache + php-fpm containers in docker-compose, I can’t get the php-fpm container to display any errors.

docker-compose.yml

version: '3'

services:
  php:
    build:
      context: ./php
    ports:
      - 9000:9000
    volumes:
      - ./code:/code
      - ./php/www.conf:/usr/local/etc/php-fpm.d/www.conf
    environment:
      ENVIRONMENT: local
  web:
    image: httpd:2.4
    depends_on:
      - php
    ports:
      - 80:80
    volumes:
      - ./code:/usr/local/apache2/htdocs
      - ./web/httpd.conf:/usr/local/apache2/conf/httpd.conf
    depends_on:
      - php

php-fpm Dockerfile:

FROM php:5.6-fpm 

php-fpm www.conf:

[global]
error_log = /proc/self/fd/2

[www]

user = www-data
group = www-data

listen = nginx:9000

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

; Logging

; if we send this to /proc/self/fd/1, it never appears
access.log = /proc/self/fd/2

clear_env = no

; Ensure worker stdout and stderr are sent to the main error log.
catch_workers_output = yes

php_flag[display_errors] = on
php_admin_flag[log_errors] = on
;php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_DEPRECATED
php_admin_value[display_startup_errors] = on

docker-compose logs only shows php-fpm access logs, no error logs.

Tried all solutions proposed in post proposed as possible duplicate: PHP-FPM doesn’t write to error log
None of them worked for me, check my comments down below.

asked Nov 21, 2018 at 17:25

syst0m's user avatar

syst0msyst0m

3511 gold badge4 silver badges12 bronze badges

11

In my case it was the /usr/local/etc/php-fpm.d/docker.conf which caused the troubles:

[global]
error_log = /proc/self/fd/2

; https://github.com/docker-library/php/pull/725#issuecomment-443540114
log_limit = 8192

[www]
; if we send this to /proc/self/fd/1, it never appears
access.log = /proc/self/fd/2

clear_env = no

; Ensure worker stdout and stderr are sent to the main error log.
catch_workers_output = yes
decorate_workers_output = no

The error_log directive’s value of /proc/self/fd/2 causes the log being written into the terminal/console where the docker-compose up is running.

I just had to create a custom global fpm config file within the /usr/local/etc/php-fpm.d/ directory and make it execute as the last one by naming it zz-global.conf, so it can override the docker.conf values. Then I just needed to set the value for the error_log to a custom path within the php-fpm container e.g.:

[global]
error_log = /var/www/logs/php-fpm-error.log

That’s it.

Maybe worth mentioning: my php.ini (modified php.ini-development) has following custom error logging related values:

; https://stackoverflow.com/a/10546138/4721232
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Default Value: no
catch_workers_output = yes
php_admin_flag[log_errors] = on
php_admin_flag[display_errors] = on
php_admin_value[error_reporting] = E_ALL
php_admin_value[error_log] = /var/log/error.log
access.log = /var/www/logs/php-fpm-access.log

answered Oct 9, 2021 at 19:04

FullStack Alex's user avatar

Struggle hours to get this working.

In the file docker-compose.yml I mounted my logs (so they persist)

        volumes:
        - ./phpSettings.conf:/usr/local/etc/php-fpm.d/zzz-phpSettings.conf
        - ./logs/php-error.log:/var/log/error.log
        - ./logs/php-access.log:/var/log/access.log

In the phpSettings.conf file I have this:

[www]
user = www-data
group = www-data
listen = 0.0.0.0:9000
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_admin_flag[log_errors] = on
php_admin_flag[display_errors] = off
php_admin_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_DEPRECATED
php_admin_value[error_log] = /var/log/error.log
access.log = /var/log/access.log
php_value[memory_limit] = 512M
php_value[post_max_size] = 24M
php_value[upload_max_filesize] = 24M

Make sure the mounted log files exist before starting the docker.

the trick seems to be naming my settings zzz-phpSettings.conf so is the last one to load. I overwrite the default docker php image www.conf settings with the very same settings (except the listen 0.0.0.0:9000).

In this php-fpm+Nginx setup I managed to have nginx (error/access) logs to its own location + php-fpm error/access logs to its own place too, so loggins looks really nice.

answered Aug 25, 2019 at 17:11

mrbarletta's user avatar

mrbarlettamrbarletta

86211 silver badges16 bronze badges

4

Mateusz Cholewka

If you are using docker and cloud services to run your application live, you should manage your logs.

The most common method to store them is to put them in the text file. It’s the default configuration for most backend frameworks. This option is ok if you run your application locally or on the VPS server for test.

When you run your application in a production environment, you should choose a better option to manage your logs. Almost every cloud has a tool for rotating logs or if not, you can use for example Grafana Loki or ELK stack. Those solutions are better because give you interfaces to rotate and search your logs. Also, you have easy access to them, you no need to connect to your server to review them.

If you are using Docker containers, and you running your application in cloud services, often they will be automatically writing the logs of your containers to tools like AWS CloudWatch or GCloud Stackdriver.

But first, you need to redirect your log streams to the output of the Docker container to be able to use them.

Linux streams

Docker containers are running the Linux processes. In linux every running process has 3 streams, STDIN, STDOUT, STDERR. STDIN it’s command input stream, that you can provide for ex. by your keyboard. STDOUT is the stream where the running command may print the output. STDERR is the standard error stream, but the name I think is a bit confusing, because it is basically intended for diagnostic output.

When you run the docker logs [container] command in your terminal, you will see the output of STDOUT and STDERR streams. So our goal is to redirect our logs to one of those streams.

Official docker documentation page

PHP-FPM

In PHP we are often running our application using the PHP-FPM (Process Manager). If you run your docker with FPM inside a docker container, and you run the docker logs command, you should see the output with processed requests, or errors.

So the PHP-FPM is already writing its output to STDOUT.

The PHP-FPM allow us to catch workers output and forward them to the STDOUT. To do that we need to make sure that the FPM is configured properly. You can create new config file, and push it for example to the /usr/local/etc/php-fpm.d/logging.conf file:

[global]
error_log = /proc/self/fd/2

[www]
access.log = /proc/self/fd/2

catch_workers_output = yes
decorate_workers_output = no

Enter fullscreen mode

Exit fullscreen mode

The error_log and access.log parameters are configuration of streams of logs output.

The catch_workers_output option is turning on the worker’s output caching. The decorate_workers_output is the option that turns off the output decoration. If you leave this option turned on, FPM will decorate your application output like this:

[21-Mar-2016 14:10:02] WARNING: [pool www] child 12 said into stdout: "[your log line]"

Enter fullscreen mode

Exit fullscreen mode

Remember that decorate_workers_output option is available only for PHP 7.3.0 and higher.

If you are using official docker php-fpm image, this configuration is already set in the /usr/local/etc/php-fpm.d/docker.conf file, so you no need to do anything more 😎

PHP application configuration

Right now everything that will be put to the stdout from PHP workers will be shown in our docker logs. But when logs are forwarded to that stream in PHP?

To write something to STDIN on PHP level, we need to just write to the php://stdout stream.

In the simplest way you can do this like that:

<?php

file_put_contents('php://stdout', 'Hello world');

Enter fullscreen mode

Exit fullscreen mode

When you execute this code in php cli, you will get the Hello world text on the output.

But it’s not the optimal way to push your logs to the STDOUT. Every modern framework should have a PSR-3 Logger. I think that the most popular now is the monolog, so I will show you how to configure it in Symfony, Laravel, and in pure usage.

Monolog

Monolog is great library to handle logs in your application. It’s easy and elastic in configuration.

Basic monolog configuration

If you are using monolog in your project with manual configuration, you need to configure handler in this way:

(Modified documentation example)

<?php

use MonologLogger;
use MonologHandlerStreamHandler;

$log = new Logger('stdout');
$log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));

$log->debug('Foo');

Enter fullscreen mode

Exit fullscreen mode

You just need to configure StreamHandler, to write to the php://stdout file.

Symfony

Symfony Kernel since the Flex was provided, is using minimalist PSR-3 logger, that logs everything to php://stderr by default.

In Symfony, monolog as other components is configured in YAML files. So the same configuration will look like this:

# config/packages/monolog.yaml
monolog:
    handlers:
        stdout:
            type: stream
            path: "php://stdout"
            level: debug

Enter fullscreen mode

Exit fullscreen mode

Laravel

Laravel use the arrays for configuration so the same thing will look like this:

# config/logging.php
<?php

use MonologHandlerStreamHandler;

return [
    'channels' =>
        'stdout' => [
            'driver' => 'monolog',
            'handler' => StreamHandler::class,
            'level' => env('LOG_LEVEL', 'debug'),
            'with' => [
                'stream' => 'php://stdout',
            ],
        ],
];

Enter fullscreen mode

Exit fullscreen mode

STDERR or STDOUT

In some articles on the internet, you can read that someone uses stderr, and someone uses stdout streams to write logs there. Right now I cannot fin any reasons to choose one of them which is better.

The only information that I found on this topic is that post.

I think that stderr is more popular in some examples, also Fabien Potencier set it as default in his minimalistic logger, so I think we can assume that this one is better.

Personally, I always used the stdout, so that’s the reason why I use it in this post’s examples. If I will find a great reason for using one of them over another I will update this post.

Originally posted on https://mateuszcholewka.com

I have php-fpm in a docker container and in the Dockerfile I edit the fpm config file (/etc/php5/fpm/pool.d/www.conf) to set up access logs to go to /var/log/fpm-access.log and error logs to go to /var/log/fpm-php.www.log:

# Do some php-fpm config
#  Redirect worker stdout and stderr into main error log
#  Activate the fpm access log
#  Enable display errors
#  Enable the error log
RUN sed -i '/^;catch_workers_output/ccatch_workers_output = yes' /etc/php5/fpm/pool.d/www.conf && 
    sed -i '/^;access.log/caccess.log = /var/log/fpm-access.log' /etc/php5/fpm/pool.d/www.conf && 
    sed -i '/^;php_flag[display_errors]/cphp_flag[display_errors] = off' /etc/php5/fpm/pool.d/www.conf && 
    sed -i '/^;php_admin_value[error_log]/cphp_admin_value[error_log] = /var/log/fpm-php.www.log' /etc/php5/fpm/pool.d/www.conf && 
    sed -i '/^;php_admin_flag[log_errors]/cphp_admin_flag[log_errors] = on' /etc/php5/fpm/pool.d/www.conf

This works fine — I can get a shell into the container to see the logs. But… it is not best-practice.

The problem is when I try to use the docker log collector — I need php-fpm to log to stdout or stderr so docker can capture them and provide them to the docker logs command.

I tried to do this in the Dockerfile (which is a idea I copied from the official nginx docker image):

# Redirect fpm logs to stdout and stderr so they are forwarded to the docker log collector
RUN ln -sf /dev/stdout /var/log/fpm-access.log && 
    ln -sf /dev/stderr /var/log/fpm-php.www.log

This is not working — no access logs are seen from docker logs — I’m trying to figure out why? Did anyone else that uses fpm in docker manage to get logging working to the docker log collector?

this is my docker-compose.yml:

services:
 nginx:
 image: nginx:stable-alpine
 container_name: nginx
 ports:
            - "8088:8088"
            - "80:80" 
 volumes:            
            - ./php-fpm:/var/www/html 
            - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
php:        
 build:
 context: ./php-fpm
 dockerfile: Dockerfile
 container_name: php
 user: "1000:1000"
 volumes:
            - ./php-fpm:/var/www/html
 ports:
            - "9000:9000""

This is my nginx default.conf:

server {
    listen 8088;
    index index.php index.html;    
    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/public;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ .php$ {        
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    
}

}

Can’t find the error log inside the containers…

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.

Already on GitHub?
Sign in
to your account


Closed

keltik85 opened this issue

Apr 1, 2016

· 10 comments


Closed

Where is the error.log?

#212

keltik85 opened this issue

Apr 1, 2016

· 10 comments

Comments

@keltik85

Hi,

I need some advice from more seasoned docker users.

I am extending the official php-image in my Dockerfile like this:

 FROM php:5.6-apache
        MAINTAINER "John Doe <Johndoe@dough.com>"
        COPY ./templates/apache2files/piwik /var/www/html/
        COPY ./templates/php.ini /usr/local/etc/php/
        RUN chmod 777 -R /var/www/html/
        RUN docker-php-ext-install mysqli
        RUN docker-php-ext-install mbstring

I build the dockerfile with this:

I run the container like this:

docker run -d --name mypiwikcontainer -p "8888:80" mypiwik:latest

I can see the access.log using this:

docker logs mypiwikcontainer

How can I see the error.log?????

DMW007, supersv, gabrielef, jszoja, CrlsMrls, strarsis, sushrest, Amunak, 0redd, QuinnBast, and 3 more reacted with thumbs up emoji

@tdutrion

Hi,

What have you got in your logs? (docker logs mypiwikcontainer)

Currently I have the following (version 7.0-apache):

[core:warn] [pid 1] AH00111: Config variable ${APACHE_RUN_DIR} is not defined

Therefore, there might be no file, unless I actually set this variable (currently investigation).

Also, you do not need to access the file. You can probably read it by connecting with ssh (docker exec -it mypiwikcontainer /bin/bash), and then access the right directory and use less or whatever is installed. I would personally use docker logs and am trying to get into the log drivers to centralise logs to a central graylog instance.

@blueimp

@keltik85
I had the same issue and solved it the following way:

In your php.ini, add the following lines:

log_errors = On
error_log = /dev/stderr

Now you’ll be able to see any PHP error logs as part of the docker container log stream:

docker logs -f your_php_apache_container

To display only errors and hide the access log, you can pipe stdout to /dev/null:

docker logs -f your_php_apache_container >/dev/null

To follow only the access log, you can pipe stderr to /dev/null:

docker logs -f your_php_apache_container 2>/dev/null
keltik85, Denys-Bushulyak, DonovanChan, jmcvetta, traneHead, max3-05, droath, hypnoboutique, shamil8124, Skysplit, and 131 more reacted with thumbs up emoji
afonsocarlos, rubik, HadesArchitect, and oliverll1 reacted with hooray emoji
nathanmartins, dambrogia, sklochko-id, ksm2, ChristophWurst, fezfez, Mykola-Veryha, mohsentm, rivetmichael, QuentinouLeTwist, and 21 more reacted with heart emoji

@povils

It’s cool, but how to get actual log files?

@Raaghu

check that 000-default site is enabled for your apache,
in my case /etc/apache2/sites-enabled/ does not contained softlink to 000-default.conf

so I ran

a2ensite 000-default
apache2ctl restart

then i started seeing logs in access.log and error.log

@mikesparr

@povils Container apps shouldn’t write log files in the container, as its lifecycle is meant to be «disposable», unless you mount a volume and want them on your file system. This is a core tenet of the 12-factor-app methodology.

  • see: https://12factor.net/logs

If you docker exec -it app-name ls -alh /var/log/apache2/ you will see the log files are symlinked to /dev/stderr and /dev/stdout as they should. This allows the container engine to expose logs via the docker logs -f app-name command or an orchestrator like Swarm or Kubernetes to expose them to centralized logging like ELK stack or Stackdriver.

vadimslu, vphantom, fredericomartini, redhat-raptor, alwinmarkcf, oradwell, craigiswayne, raknjak, abdulladev1995, derekrprice, and 4 more reacted with thumbs up emoji

@povils

@mikesparr Thanks for the answer. I needed log files because I am using Elastic Filebeat which harvests log files and streams that to our ELK

@mikesparr

I believe you enable fluentd and configure logging output. The ephemeral nature of containers is meant to stream logs not write them so you need to capture the stream one layer up, then ship em.

Sent from my iPhone

On Oct 20, 2017, at 2:51 AM, Povilas Susinskas ***@***.***> wrote:

@mikesparr Thanks for the answer. I needed log files because I am using Elastic Filebeat which harvests log files and streams that to our ELK


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@okainov

@blueimp thanks, works perfectly! I wonder, why isn’t it included into default PHP Docker image….

@buster95

I solved this problem adding next command in dockerfile

RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini

@Videles

@keltik85 I had the same issue and solved it the following way:

In your php.ini, add the following lines:

log_errors = On
error_log = /dev/stderr

Now you’ll be able to see any PHP error logs as part of the docker container log stream:

docker logs -f your_php_apache_container

To display only errors and hide the access log, you can pipe stdout to /dev/null:

docker logs -f your_php_apache_container >/dev/null

To follow only the access log, you can pipe stderr to /dev/null:

docker logs -f your_php_apache_container 2>/dev/null

This works like a charms, thanks a lot!

If you want to change PHP-FPM and Nginx container log format to JSON while keeping same style, you can use example below.

Structure

.
├── docker
│   ├── docker-compose.yml
│   ├── Makefile
│   ├── nginx
│   │   ├── app.conf
│   │   ├── Dockerfile
│   │   └── nginx.conf
│   └── php
│   ├── Dockerfile
│   ├── php.ini
│   └── www.conf
└── index.php

Files

docker/docker-compose.yml

version: "3.4"

services:

wait_php:
build:
context: "./php"
hostname: "wait-php"
volumes:
- "..:/app"
environment:
PS1: "\u@\h:\w\$$ "

wait_nginx:
build:
context: "./nginx"
hostname: "wait-nginx"
ports:
- "1080:80"
volumes:
- "..:/app"
depends_on:
- "wait_php"
environment:
PS1: "\u@\h:\w\$$ "

docker/nginx/Dockerfile

FROM nginx:1.15.8-alpine

WORKDIR /app

COPY app.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/nginx.conf

docker/nginx/app.conf

We had to add fastcgi_param HTTP_X_REQUEST_ID $request_id; for PHP-FPM logs.

server {
listen 80 default_server;

server_name localhost;

root /app;

index index.php;

location ~ .php$ {
try_files $uri =404;
fastcgi_pass wait_php:9000;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTP_X_REQUEST_ID $request_id;
}
}

docker/nginx/nginx.conf

If you remove error_log off; the PHP related coding errors will be displayed by Nginx container rather than the PHP-FPM container. I like my PHP-FPM container to own its own error so I am keeping it.

user nginx;

worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format json_combined escape=json
'{'
'"time_local":"$time_iso8601",'
'"client_ip":"$http_x_forwarded_for",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"request_id":"$request_id"'
'}';

access_log /var/log/nginx/access.log json_combined;

error_log off;

sendfile on;

keepalive_timeout 65;

include /etc/nginx/conf.d/*.conf;
}

docker/php/Dockerfile

FROM php:7.2.13-fpm-alpine3.8

WORKDIR /app

COPY php.ini /usr/local/etc/php/conf.d/php.override.ini
COPY www.conf /usr/local/etc/php-fpm.d/www.conf

CMD ["php-fpm", "--nodaemonize"]

docker/php/php.ini

[PHP]
date.timezone=UTC
log_errors=On
error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors=Off
max_execution_time=60
memory_limit=256M

docker/php/www.conf

You may need to add clear_env=no to see how it changes the logs or if it helps to access more environmental variables.

[global]
daemonize=no

[www]
user=www-data
group=www-data

listen=wait_nginx:9000

pm=dynamic
pm.max_children=40
pm.start_servers=2
pm.min_spare_servers=2
pm.max_spare_servers=4
pm.max_requests=500

access.format='{"time_local":"%{%Y-%m-%dT%H:%M:%S%z}T","client_ip":"%{HTTP_X_FORWARDED_FOR}e","remote_addr":"%R","remote_user":"%u","request":"%m %{REQUEST_URI}e %{SERVER_PROTOCOL}e","status":"%s","body_bytes_sent":"%l","request_time":"%d","http_referrer":"%{HTTP_REFERER}e","http_user_agent":"%{HTTP_USER_AGENT}e","request_id":"%{HTTP_X_REQUEST_ID}e"}'

Docker

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
96d7f6c16904 docker_wait_nginx "nginx -g 'daemon of…" 4 minutes ago Up 4 minutes 0.0.0.0:1080->80/tcp docker_wait_nginx_1
f8d0c3367925 docker_wait_php "docker-php-entrypoi…" 4 minutes ago Up 4 minutes 9000/tcp docker_wait_php_1

Configuration information

The PHP access log format has C, d, e, f, l, m, M, n, o, p, P, q, Q, r, R, s, t, T, u options and they are used with % prefix — e.g. %C. However, e (represents Nginx server fastcgi_param vars and PHP-FPM server $_SERVER vars) and o (represents «header» output) are a bit different so they are used as %{VARIABLE_NAME}e — e.g. %{REQUEST_URI}e. Also the list of Nginx config file variables can be found here.

The PHP default access log format is "%R - %u %t "%m %r" %s" and the log output is 172.22.0.3 - 26/May/2019:10:57:14 +0000 "GET /index.php" 200.

For demonstration purposes, if you have used all options in log format, your log would look like below.

access.format = '{"C":"%C","d":"%d","f":"%f","l":"%l","m":"%m","M":"%M","n":"%n","P":"%P","p":"%p","q":"%q","Q":"%Q","r":"%r","R":"%R","s":"%s","T":"%T","t":"%t","u":"%u"}'

$ curl -i 0.0.0.0:1080

{
"C": "0.00", # CPU usage
"d": "5.001", # Request processing duration (5 sec 1 milsec)
"f": "/app/index.php", # Script run
"l": "0", # Content length
"m": "GET", # Method
"M": "2097152", # Memory usage (byte)
"n": "www", # Pool name
"P": "1", # PID (master process)
"p": "6", # PID (child process - pm.start_servers)
"q": "", # Query string
"Q": "", # The '?' character if query string exists
"r": "/index.php", # Script run
"R": "172.22.0.3", # IP of requester (Nginx)
"s": "200", # HTTP status code
"T": "2019-06-12T20:26:05+00:00", # Response time
"t": "2019-06-12T20:26:00+00:00", # Request time
"u": "" # Authenticated/remote user
}

# YOU MUST READ "/usr/local/etc/php-fpm.d/www.conf" file for detailed explanation and better usage of these parameters

Tests

PHP-FPM error log

wait_php_1    | [26-May-2019 17:49:02] WARNING: [pool www] child 7 said into stderr: "NOTICE: PHP message: PHP Parse error:  syntax error, unexpected end of file in /app/index.php on line 4"

wait_php_1 | [26-May-2019 17:49:49] WARNING: [pool www] child 8 said into stderr: "NOTICE: PHP message: PHP Fatal error: Uncaught Error: Class 'Tomato' not found in /app/index.php:5"
wait_php_1 | [26-May-2019 17:49:49] WARNING: [pool www] child 8 said into stderr: "Stack trace:"
wait_php_1 | [26-May-2019 17:49:49] WARNING: [pool www] child 8 said into stderr: "#0 {main}"
wait_php_1 | [26-May-2019 17:49:49] WARNING: [pool www] child 8 said into stderr: " thrown in /app/index.php on line 5"

Access logs

I did my best to match both log formats in configs but it’s up to you to improve it.

# PHP-FPM - wait_php_1

{
"time_local": "2019-06-12T20:26:01+00:00",
"client_ip": "2.28.107.100",
"remote_addr": "172.22.0.3",
"remote_user": "",
"request": "GET / HTTP/1.1",
"status": "200",
"body_bytes_sent": "0",
"request_time": "0.002",
"http_referrer": "-",
"http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
"request_id": "98bb9fbfb3bc710422d87b22d380a4e6"
}

# Nginx - wait_nginx_1

{
"time_local": "2019-06-12T20:26:00+00:00",
"client_ip": "2.28.107.100",
"remote_addr": "192.168.99.1",
"remote_user": "",
"request": "GET / HTTP/1.1",
"status": "200",
"body_bytes_sent": "2509",
"request_time": "0.004",
"http_referrer": "",
"http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
"request_id": "98bb9fbfb3bc710422d87b22d380a4e6"
}

Before we start…

The previous post about dockerising a PHP application (which you should read if you haven’t) got a lot more attention than I ever imagined, which is AWESOME. However, while introducing some of  the suggested improvements a number discrepencies between the contents of the blog post and the GitHub repository emerged. Buuut…

The death of php-docker.local

The initial setup included a step that required you to update your hosts file and add an entry for php-docker.local. I received feedback from several people that this step is not completely clear and they ended up skipping it. It turns out this step can be removed easily, so why don’t we just go ahead and do it :)

Two things are needed to achieve this. First, we have to update the site.conf in order to handle the connections to localhost. Second, the default configuration in the Nginx image should be replaced with our new config.
For the first part we have to replace the server_name setting with:

    server_name localhost;

and add the following setting:

    listen 80;

Now our site.conf will look like this:

server {
    listen 80;
    index index.php index.html;
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /code;

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

In order to replace the default Nginx config we have to mount our site.conf in its place. To achieve that we have to tweak our docker-compose.yml a bit. The end result will look like this:

web:
    image: nginx:latest
    ports:
        - "8080:80"
    volumes:
        - ./code:/code
        - ./site.conf:/etc/nginx/conf.d/default.conf
    links:
        - php
php:
    image: php:7-fpm
    volumes:
        - ./code:/code

For those who missed it, we changed one of the volumes for the web container with the following:

        - ./site.conf:/etc/nginx/conf.d/default.conf

Now your PHP application will be accessible on any domain pointing to your Docker host.

Docker-compose v2

For a while now docker-compose supports version 2 for the docker-compose files, which adds some improvements to the setup. Let’s see how the docker-compose.yml will look like using the new format:

version: 2

services:
    web:
        image: nginx:latest
        ports:
            - "8080:80"
        volumes:
            - ./code:/code
            - ./site.conf:/etc/nginx/conf.d/site.conf
    php:
        image: php:7-fpm
        volumes:
            - ./code:/code

This doesn’t look much different, except that we don’t have to specify the links between the containers. Docker-compose adds all the containers to the same network and they are “linked” by default. This is especially useful when you add more containers to the setup (e.g. database, cache, queue, etc.) since you don’t have to worry about specifing the links between containers.

Another thing the we can do using version 2 of the docker-compose files is to specify networks for the containers. For example:

version: 2

services:
    web:
        image: nginx:latest
        ports:
            - "8080:80"
        volumes:
            - ./code:/code
            - ./site.conf:/etc/nginx/conf.d/default.conf
        networks:
            - code-network
    php:
        image: php:fpm
        volumes:
            - ./code:/code
        networks:
            - code-network

networks:
    code-network:
        driver: bridge

This option allows grouping different containers in different networks based on the services they need to connect to. In our setup this is not needed, but I believe it’s an important feature to be aware of, especially when expanding the setup with more services.

Special thanks to cipriantepes, who contributed to the repository with this improvement.

PHP logging to stdout

This is something trivial but I never really thought it’s needed until I got several requests and even a PR for it. The issue is that the default php-fpm Docker image is not configured to log the errors. The fix is to enable logging.

First, let’s add a log.conf file with the following content:

php_admin_flag[log_errors] = on
php_flag[display_errors] = off

Next, add this to the configuration of the PHP container:

 version: '2'

services:
    web:
        image: nginx:latest
        ports:
            - "8080:80"
        volumes:
            - ./code:/code
            - ./site.conf:/etc/nginx/conf.d/default.conf
        networks:
            - code-network
    php:
        image: php:fpm
        volumes:
            - ./code:/code
            - ./log.conf:/usr/local/etc/php-fpm.d/zz-log.conf
        networks:
            - code-network

networks:
    code-network:
        driver: bridge

So… what happened? Well, we mounted our new log file in the PHP container, but we added a zz- prefix to it. Why? Because we want to have this configuration loaded last, so that it’s not overriden by the rest of the configs.
Here’s the line for the curious:

            - ./log.conf:/usr/local/etc/php-fpm.d/zz-log.conf

Since this wasn’t part of the initial blog post, it resides in a separate branch – feature/log-to-stdout.

Final summation

I guess no matter how good you make something, there’s always room for improvement. In that sense, any remarks and PRs are more than welcome :)

You can find the updated code here – https://github.com/mikechernev/dockerised-php

June 12, 2018

We cover the docker logs command.

We cover the docker logs command. We need to make a tweak so everything outputs to stdout or stderr. If we do that successfully, we can use Docker’s logging mechanism to see output from Nginx, PHP, and Supervisord.

The only change we need to make:

PHP-FPM

In our php-fpm.conf file, adjust the error_log to error_log = /proc/self/fd/2.

Nginx

Nginx is easier — we can just adjust the Dockerfile by symlinking the nginx error and access logs to stdout and stderr:

FROM ubuntu:18.04

LABEL maintainer="Chris Fidao"

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update 
    && apt-get install -y gnupg tzdata 
    && echo "UTC" > /etc/timezone 
    && dpkg-reconfigure -f noninteractive tzdata

RUN apt-get update 
    && apt-get install -y curl zip unzip git supervisor sqlite3 
       nginx php7.2-fpm php7.2-cli 
       php7.2-pgsql php7.2-sqlite3 php7.2-gd 
       php7.2-curl php7.2-memcached 
       php7.2-imap php7.2-mysql php7.2-mbstring 
       php7.2-xml php7.2-zip php7.2-bcmath php7.2-soap 
       php7.2-intl php7.2-readline php7.2-xdebug 
       php-msgpack php-igbinary 
    && php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer 
    && mkdir /run/php 
    && apt-get -y autoremove 
    && apt-get clean 
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 
    && echo "daemon off;" >> /etc/nginx/nginx.conf

RUN ln -sf /dev/stdout /var/log/nginx/access.log 
    && ln -sf /dev/stderr /var/log/nginx/error.log

ADD default /etc/nginx/sites-available/default
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ADD php-fpm.conf /etc/php/7.2/fpm/php-fpm.conf

CMD ["nginx"]

And of course, we need to rebuild the image:

docker build -t shippingdocker/app:latest 
    -f docker/app/Dockerfile 
    docker/app

Looking for a deeper dive into Docker?

Sign up here to get a preview of the Shipping Docker course! Learn how to integrate Docker into your applications and develop a workflow to make using Docker a breeze!

Понравилась статья? Поделить с друзьями:
  • Docker login error cannot perform an interactive login from a non tty device
  • Docker level error msg stream copy error reading from a closed fifo
  • Docker iptables error
  • Docker initdb error directory var lib postgresql data exists but is not empty
  • Docker gpg error