I'm working on a project where I have an iOs app connecting to a PHP API. I want to log all incoming requests to the website service for development purposes (ie I need a solution that can turn off and based on an environment variable). The API is run in a docker container, which is launched as a docker-compose service.
The PHP API is not using any sort of MVC framework.
My PHP experience is limited, so I know I've got some research ahead of me, but in the meantime, I'd appreciate any jump start to the following questions:
Is there a composer library that I can plug into my PHP code that will write to a tailed log?
Can I plug anything at the nginx or php-fpm container level so that requests to those containers are logged before even getting to the PHP code?
Is there anything I need to configure to in either nginx or php-fpm containers to ensure that logs are tailed when I run docker-compose up?
Here are my logging needs:
request method
request URL
GET query parameters, PUT and POST parameters (these will be in JSON format)
response code
response body
The logs I'm interested are all application/json. However, I don't mind the kitchen sink option where anything out gets logged.
request and response headers
I will not need these 99% of the time, so they aren't a need. But it'd be nice to configure them off/on.
Below is the docker-compose.yml file.
version: '2'
services:
gearman:
image:gearmand
redis:
image: redis
database:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: ******
MYSQL_DATABASE: database
volumes:
- dbdata:/var/lib/mysql
ports:
- 3306:3306
sphinx:
image:sphinx
links:
- database
memcached:
image: memcached
ports:
- "11211:11211"
php_fpm:
image:php-fpm
links:
- redis
- memcached
- database
environment:
REDIS_SERVER: redis
DATABASE_HOST: database
RW_DATABASE_HOST: database
RO_DATABASE_HOST0: database
DATABASE_USER: root
DATABASE_PASS: ******
volumes:
- ./website:/var/www/website/
- /var/run
nginx:
image:nginx
links:
- php_fpm
ports:
- "80:80"
volumes_from:
- php_fpm
volumes:
dbdata:
driver: local
The container was logging using all the default settings, but my client was pointing to another server, but just to leave a trail.
Inside php_fpm container (docker exec -it dev_php_fpm_1 /bin/bash), you can cat /etc/php5/fpm/php.ini, which shows the default error_log settings:
; Log errors to specified file. PHP's default behavior is to leave this value
; empty.
; http://php.net/error-log
; Example:
;error_log = php_errors.log
; Log errors to syslog (Event Log on Windows).
;error_log = syslog
Just user error_log function to write to default OS logger.
Log your the php_fpm service with docker logs -f -t dev_php_fpm_1.
Update:
In case error_log is truncated for your purposes, you can also simply write a function
file_put_contents(realpath(dirname(__FILE__)) . './requests.log', $msg, FILE_APPEND);
and then tail it: tail -f ./requests.log - either from inside the container or from outside the container if you are using a local volumes.
Related
I have a php app dockerized.
My issue is how to capture errors from php service into a dedicated file on the host.
docker file looks is next:
version: "3.9"
services:
web:
image: nginx:latest
ports:
- "3000:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./public:/public
php:
build:
context: .
dockerfile: PHP.Dockerfile
environment:
APP_MODE: 'development'
env_file:
- 'dev.env'
volumes:
- ./app:/app
- ./public:/public
- ./php.conf:/usr/local/etc/php-fpm.d/zz-log.conf
mysql:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: <pass here>
env_file:
- 'dev.env'
volumes:
- mysqldata:/var/lib/mysql
- ./app:/app
ports:
- '3306:3306'
volumes:
mysqldata: {}
my php.conf that maps as /usr/local/etc/php-fpm.d/zz-log.conf inside php service looks like bellow:
php_admin_value[error_log] = /app/php-error.log
php_admin_flag[log_errors] = on
catch_workers_output = yes
My intention is using php error_log() function and have all the logs recorded in php-error.log which is a file inside volume app.
Now, all logs from containers are shown on terminal only.
I have been struggling with this several hours and have no ideea how to continue. Thank you
I don't know what is your source image. I assume some official docker image for PHP like https://hub.docker.com/_/php
All containerized applications are usually configured to log to stdout so you must override that behaviour. This is really PHP specific and I'm no PHP expert. From what you let us know it looks like you know how to override that behaviour (by using some error_log() function and php_admin_value[error_log] = /app/php-error.log property.
If the behaviour is overridden you should ensure the file app/php-error.log exists inside of the PHP container (i.e. get inside the container by something like docker exec -it my-container-id /bin/bash and then do ls /app/php-error.log and cat /app/php-error.log to see if the file is created.
Because you're mounting the ./app directory from the host to /app directory in container you already have them mirrored. Whatever is inside container's /app you will find in also your /path/to/docker/compose/app directory. You can check if file exists and some content is inside. If not you failed to override the default behaviour of where PHP is logging to.
I'm learning PDO now and I found it better to learn it in a LEMP docker stack (Nginx, php-fpm, MariaDB, phpMyadmin) on my Ubuntu 18.04LTS.
This is my php file:
<?php
try {
$mydb = new PDO('mysql:host=database;dbname=mysql;charset=utf8', 'root', 'admin');
} catch (Exception $e) {
die('Error : ' . $e->getMessage());
}
?>
As you can see, I try to make a PDO in my php code to recover some datas from my db.
But everytime I got that message on my browser (Firefox 69.0.2):
Error : could not find driver
I saw that post here: "Docker can't connect to mariadb with PHP". The problem was quite similar to mine but it didn't work for me.
Note: php-fmp and Nginx work perfeclty together. Same for MariaDB and phpMyAdmin.
Here is my docker-compose.yml file:
version: "3"
services:
nginx:
image: tutum/nginx
ports:
- "7050:80"
links:
- phpfpm
volumes:
- ./nginx/default:/etc/nginx/sites-available/default
- ./nginx/default:/etc/nginx/sites-enabled/default
- ./logs/nginx-error.log:/var/log/nginx/error.log
- ./logs/nginx-access.log:/var/log/nginx/access.log
phpfpm:
image: php:fpm
links:
- database:mysql
ports:
- "7051:9000"
volumes:
- ./public:/usr/share/nginx/html
database:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: admin
ports:
- "7052:3306"
phpmyadmin:
image: phpmyadmin/phpmyadmin
restart: always
links:
- database:mysql
ports:
- "7053:80"
environment:
PMA_HOST: mysql
PMA_USER: root
PMA_PASSWORD: admin
PMA_ARBITRARY: 1
If it is possible to solve this without building my own Dockerfiles, it would be great.
But if I must, I will. This isn't a problem.
docker-compose is an "api" of sorts for a Dockerfile. You need to add those libraries (apt-get etc...) in the Dockerfile
Dockerfile is your friend!
Is your PHP file inside a docker container or is it running outside docker, in the host machine?
If it is running inside the docker container, which service is it in? Please note that the nginx service does not have the "links" configuration, meaning it only accesses the database through the "database" hostname. Check the port as well (in the end of this post).
If your PHP file is running outside, then you have to use localhost instead of mysql in your connection string, like so: 'mysql:host=localhost;dbname=mysql;charset=utf8'. This is because docker's internal DNS is just that: internal. You can't access this hostname (database or mysql) outside docker.
Equally important, your connection string is not specifying the port, which is 7052 in your case. Since you're redirecting from 7052 to 3306, I think 3306 is mysql's default port, and the driver assumes 3306 if you do not specify it. It's always a good idea to be explicit about hosts and ports. Check the documentation on PHP databse connection strings about it (as I know nothing about php). It's probably ...;port=7052 or something.
Also, read up on docker-compose links, which you are using. It's deprecated now, so I advise to not use it in future projects, I even advise to spend some time removing it. Should take like 30 seconds to 5 minutes if everything goes well, and it won't haunt you anymore.
A found the solution.
First of all, the host must be mysql and not the name of my container (which is database):
$mydb = new PDO('mysql:host=mysql;dbname=mysql;charset=utf8', 'root', 'admin');
Inside the phpfpm container (accessible via the command docker-compose run --rm <container-name> bash), I had to enable the extension=php_pdo_msql line in my config file php.ini by removing the semicolon at the beginning of its line.
To avoid doing this manually every time after a docker-compose up, I replaced the phpfpm service in my docker-compose.yml file the following Dockerfile:
FROM php:fpm
RUN docker-php-ext-install pdo pdo_mysql
Finally, just build the image with the command docker-compose build . (replace the . by the path to the directory containing the docker-compose.yml file).
It works perfectly for me.
I am developing a Laravel application. I am trying to use Laravel Websocket in my application, https://docs.beyondco.de/laravel-websockets. I am using Docker/ docker-compose.yml. Since the Laravel Websocket run locally on port 6001, I am having problem with integrating it with docker-compose. Searching solution I found this link, https://github.com/laradock/laradock/issues/2002. I tried it but not working. Here is what I did.
I created a folder called workspace under the project root directory. Inside that folder, I created a file called, Dockerfile.
This is the content of Dockerfile
EXPOSE 6001
In the docker-compose.yml file, I added this content.
workspace:
port:
- 6001:6001
My docker-compose.yml file looks something like this
version: "3"
services:
workspace:
port:
- 6001:6001
apache:
container_name: web_one_apache
image: webdevops/apache:ubuntu-16.04
environment:
WEB_DOCUMENT_ROOT: /var/www/public
WEB_ALIAS_DOMAIN: web-one.localhost
WEB_PHP_SOCKET: php-fpm:9000
volumes: # Only shared dirs to apache (to be served)
- ./public:/var/www/public:cached
- ./storage:/var/www/storage:cached
networks:
- web-one-network
ports:
- "80:80"
- "443:443"
php-fpm:
container_name: web-one-php
image: php-fpm-laravel:7.2-minimal
volumes:
- ./:/var/www/
- ./ci:/var/www/ci:cached
- ./vendor:/var/www/vendor:delegated
- ./storage:/var/www/storage:delegated
- ./node_modules:/var/www/node_modules:cached
- ~/.ssh:/root/.ssh:cached
- ~/.composer/cache:/root/.composer/cache:delegated
networks:
- web-one-network
When I run "docker-compose up --build -d", it is giving me the following error.
ERROR: The Compose file './docker-compose.yml' is invalid because:
Unsupported config option for services.workspace: 'port' (did you mean 'ports'?)
What is wrong and how can I fix it? How can I use Laravel Web Socket with docker-compose?
I tried changing from 'port' to 'ports', then I got the following error message instead.
ERROR: The Compose file is invalid because:
Service workspace has neither an image nor a build context specified. At least one must be provided.
Your Dockerfile is wrong. A Dockerfile must start with a FROM <image> directive as explained in the documentation. In your case it might be sufficient to run an existing php:<version>-cli image though, avoiding the whole Dockerfile stuff:
workspace:
image: php:7.3-cli
command: ["php", "artisan", "websockets:serve"]
Of course you will also need to add a volume with the application code and a suitable network. If you add a reverse proxy like Nginx in front of your services, you don't need to export the ports on your workspace either. Services may access other services as long as they are in the same network.
How do I deliver the code of a containerized PHP application, whose image is based on busybox and contains only the code, between separate NGINX and PHP-FPM containers? I use the 3rd version of docker compose.
The Dockerfile of the image containing the code would be:
FROM busybox
#the app's code
RUN mkdir /app
VOLUME /app
#copy the app's code from the context into the image
COPY code /app
The docker-compose.yml file would be:
version: "3"
services:
#the application's code
#the volume is currently mounted from the host machine, but the code will be copied over into the image statically for production
app:
image: app
volumes:
- ../../code/cms/storage:/storage
networks:
- backend
#webserver
web:
image: web
depends_on:
- app
- php
networks:
- frontend
- backend
ports:
- '8080:80'
- '8081:443'
#php
php:
image: php:7-fpm
depends_on:
- app
networks:
- backend
networks:
cms-frontend:
driver: "bridge"
cms-backend:
driver: "bridge"
The solutions I thought of, neither appropriate:
1) Use the volume from the app's container in the PHP and NGINX containers, but compose v3 doesn't allow it (the volumes_from directive). Can't use it.
2) Place the code in a named volume and connect it to the containers. Going this way I can't containerize the code. Can't use. (I'll also have to manually create this volume on every node in a swarm?)
3) Copy the code twice directly into images based on NGINX and PHP-FPM. Bad idea, I'll have to maintain them to be in concert.
Got stuck with this. Any other options? I might have misunderstood something, only beginning with Docker.
I too have been looking around to solve a similar issue and it seems Nginx + PHP-FPM is one of those exceptions when it is better to have both services running in one container for production. In development you can bind mount the project folder to both nginx and php containers. As per Bret Fisher's guide for good defaults for php: php-docker-good-defaults
So far, the Nginx + PHP-FPM combo is the only scenario that I recommend using multi-service containers for. It's a rather unique problem that doesn't always fit well in the model of "one container, one service". You could use two separate containers, one with nginx and one with php:fpm but I've tried that in production, and there are lots of downsides. A copy of the PHP code has to be in each container, they have to communicate over TCP which is much slower than Linux sockets used in a single container, and since you usually have a 1-to-1 relationship between them, the argument of individual service control is rather moot.
You can read more about setting up multiple service containers on the docker page here (it's also listed in the link above): Docker Running Multiple Services in a Container
The way I see it, you have two options:
(1) Using Docker-compose : (this is for very simplistic development env)
You will have to build two separate container from nginx and php-fpm images. And then simply serve app folder from php-fpm on a web folder on nginx.
# The Application
app:
build:
context: ./
dockerfile: app.dev.dockerfile
working_dir: /var/www
volumes:
- ./:/var/www
expose:
- 9000
# The Web Server
web:
build:
context: ./
dockerfile: web.dev.dockerfile
working_dir: /var/www
volumes_from:
- app
links:
- app:app
ports:
- 80:80
- 443:443
(2) Use a single Dockerfile to build everything in it.
Start with some flavor of linux or php image
install nginx
Build your custom image
And serve multi services docker container using supervisord
I'm trying to deploy a very simple Symfony application using nginx & php-fpm via Docker.
Two docker services :
1. web : running nginx
2. php : running php-fpm; containing application source.
I want to build images that can be deployed without any external dependency.
That's why I'm copying source code within the php container.
On development process; i'm overriding /var/www/html volume with local path.
# file: php-fpm/Dockerfile
FROM php:7.1-fpm-alpine
COPY ./vendor /var/www/html
COPY . /var/www/html
VOLUME /var/www/html
Now the docker-compose configuration file.
# file : docker-compose-prod.yml
version: '2'
services:
web:
image: "private/web"
ports:
- 80:80
volumes_from:
- php
php:
image: "private/php"
ports:
- 9000:9000
The problem is about permissions.
When accessing localhost, Symfony is botting up, but cache / logs / sessions folders are not writable.
nginx is using /var/www/html to serve static files.
php-fpm is using /var/www/html to execute php files.
I'm not sure about the problem.
But how can I be sure about the following:
/var/www/html have to be readable for nginx ?
/var/www/html have to be writable for php-fpm ?
Note: I'm building images from MacbookPro; cache / logs / sessions are 777.
docker-compose.yml supports a user directive under services. The docs only mention it in the run command, but it works the same.
I have a similar setup and this is how I do it:
# file : docker-compose-prod.yml
version: '2'
services:
web:
image: "private/web"
ports:
- 80:80
volumes_from:
- php
php:
image: "private/php"
ports:
- 9000:9000
user: "$UID"
I have to run export UID before running docker-compose and then that sets the default user to my current user. This allows logging / caching etc. to work as expected.
I am using this solution "Docker for Symfony" https://github.com/StaffNowa/docker-symfony
New features on
./d4d start
./d4s stop
./d4d help
I've found a solution;
But if someone can explain best practices, it will be appreciate !
Folders cache / logs / sessions from docker context where not empty (on host).
Now that folders have been flushed, Symfony creates them with good permissions.
I've found people using usermod to change UID, ie: 1000 for www-data / nginx ...
But it seems to be an ugly hack. What do you think about ?