How to correctly link php-fpm and Nginx Docker containers?
Don't hardcode ip of containers in nginx config, docker link adds the hostname of the linked machine to the hosts file of the container and you should be able to ping by hostname.
EDIT: Docker 1.9 Networking no longer requires you to link containers, when multiple containers are connected to the same network, their hosts file will be updated so they can reach each other by hostname.
Every time a docker container spins up from an image (even stop/start-ing an existing container) the containers get new ip's assigned by the docker host. These ip's are not in the same subnet as your actual machines.
see docker linking docs (this is what compose uses in the background)
but more clearly explained in the docker-compose
docs on links & expose
links
links: - db - db:database - redis
An entry with the alias' name will be created in /etc/hosts inside containers for this service, e.g:
172.17.2.186 db 172.17.2.186 database 172.17.2.187 redis
expose
Expose ports without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified.
and if you set up your project to get the ports + other credentials through environment variables, links automatically set a bunch of system variables:
To see what environment variables are available to a service, run
docker-compose run SERVICE env
.
name_PORT
Full URL, e.g. DB_PORT=tcp://172.17.0.5:5432
name_PORT_num_protocol
Full URL, e.g.
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
name_PORT_num_protocol_ADDR
Container's IP address, e.g.
DB_PORT_5432_TCP_ADDR=172.17.0.5
name_PORT_num_protocol_PORT
Exposed port number, e.g.
DB_PORT_5432_TCP_PORT=5432
name_PORT_num_protocol_PROTO
Protocol (tcp or udp), e.g.
DB_PORT_5432_TCP_PROTO=tcp
name_NAME
Fully qualified container name, e.g.
DB_1_NAME=/myapp_web_1/myapp_db_1
I know it is kind an old post, but I've had the same problem and couldn't understand why your code didn't work. After a LOT of tests I've found out why.
It seems like fpm receives the full path from nginx and tries to find the files in the fpm container, so it must be the exactly the same as server.root
in the nginx config, even if it doesn't exist in the nginx container.
To demonstrate:
docker-compose.yml
nginx:
build: .
ports:
- "80:80"
links:
- fpm
fpm:
image: php:fpm
ports:
- ":9000"
# seems like fpm receives the full path from nginx
# and tries to find the files in this dock, so it must
# be the same as nginx.root
volumes:
- ./:/complex/path/to/files/
/etc/nginx/conf.d/default.conf
server {
listen 80;
# this path MUST be exactly as docker-compose.fpm.volumes,
# even if it doesn't exist in this dock.
root /complex/path/to/files;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Dockerfile
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/
As pointed out before, the problem was that the files were not visible by the fpm container. However to share data among containers the recommended pattern is using data-only containers (as explained in this article).
Long story short: create a container that just holds your data, share it with a volume, and link this volume in your apps with volumes_from
.
Using compose (1.6.2 in my machine), the docker-compose.yml
file would read:
version: "2"
services:
nginx:
build:
context: .
dockerfile: nginx/Dockerfile
ports:
- "80:80"
links:
- fpm
volumes_from:
- data
fpm:
image: php:fpm
volumes_from:
- data
data:
build:
context: .
dockerfile: data/Dockerfile
volumes:
- /var/www/html
Note that data
publishes a volume that is linked to the nginx
and fpm
services. Then the Dockerfile
for the data service, that contains your source code:
FROM busybox
# content
ADD path/to/source /var/www/html
And the Dockerfile
for nginx, that just replaces the default config:
FROM nginx
# config
ADD config/default.conf /etc/nginx/conf.d
For the sake of completion, here's the config file required for the example to work:
server {
listen 0.0.0.0:80;
root /var/www/html;
location / {
index index.php index.html;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
}
which just tells nginx to use the shared volume as document root, and sets the right config for nginx to be able to communicate with the fpm container (i.e.: the right HOST:PORT
, which is fpm:9000
thanks to the hostnames defined by compose, and the SCRIPT_FILENAME
).