Docker Community Forums

Share and learn in the Docker community.

Implement a container for each php code

Hi everyone,
first of all I’m totally new in working with Docker.

As you can see below I set up two docker containers, one for my Apache webserver and one for a MySQL database. By using the webserver I can call my php codes which I store in the src-folder. Now my question is if it is possible to create two containers for two different php-functions. For example: With one Container I can call my php-code “give_me_measure_data_from_device_A.php” and with the other container “give_me_measure_data_from_device_B.php”. Is this possible to use for each function an own container?

This is my docker-compose.yml:

version: '3.1'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/

  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports: 
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

This is my Dockerfile:

FROM php:7.4-apache
RUN docker-php-ext-install mysqli

Thank you very much!

Sure, but they could not both use port 80.

Using yet another container to run a “reverse proxy” such as Nginx that would use port 80, would allow you to direct traffic for some URL to one container, and for another URL to another. The forwarding would then only use “internal” ports, and inter-container networking, without making the PHP containers expose any ports to the host directly. If that makes any sense depends on your use case; you may want to tell a bit more about your actual use case.

(It seems to me that give_me_measure_data is the task for a single container, that would use URL query parameters like ?device=a, or REST URL paths like /devices/a/measurements and /devices/b/measurements, to determine which device is asked for. That by itself is unrelated to using Docker or not.)

1 Like

Thanks for yout reply.

Ok so if I am right I need two different webserver to call each php code from a differnet container?

My case: I have four different measuring devices (www.withings.com). A body scale, a thermometer, a blood pressure monitor and a sleep analyzer. I wrote for every device a php code to get the measure data through the company offering API. Now I want every device to work on an own container. So in the future it would be possible to delete a container or to change a device (for the example change the body scale for a newer one). So my question is if it is possible to add this to my system and just create four containers where each php code is deposited or can get called separately? I hope this made it a little bit clearer. Otherwise pls let me know.

The options as I see it:

  1. A single web server, serving different paths, one for each device. This is really the easiest, as giving each PHP application its own path on the server is easy. This could use four volumes (more specifically “bind mounts”, like in your own example), one for each application: ./src-scale:/var/www/html/my-scale and ./src-thermometer:/var/www/html/my-thermometer, and so on.

  2. Or, four web servers, each running on a different port, like localhost:80 for one device, localhost:81 for the second, and so on. (Or possibly each running on a different domain or sub-domain.) You cannot have multiple servers using the same port.

  3. Or, a single reverse proxy server such as Nginx, which accepts all requests on, say, localhost:80, and four PHP servers. These four do not need to be web servers, but could use PHP-FPM, which is not my cup of tea; Google will be your friend. But, the reverse proxy needs some rules to determine to which server to delegate a request. If that is using a path, like localhost:80/my-scale needs to go to one server, and localhost:80/my-thermometer needs to go to another, then the paths in the browser may be very much like the first option. But using a reverse proxy is a lot more work.

1 Like

(I can see I was slow composing my answer :slight_smile: Yes, @avbentem had a good answer)

You don’t necessarily need different webservers. Using NginX as a reverse proxy is a good advise but for this specific goal, if you are more familiar with Apache HTTPD, you can use one HTTPD and multiple PHP-FPM container. The settings are similar to using one NginX reverse proxy and multiple HTTPD behind it. The third option of course is using one NginX server in front of multiple PHP-FPM container. So you have more than one option. Either way you have to set up some routing rules (in NginX or HTTPD) to use different PHP container for different URLs. If you can use different domain names instead of different paths in the URL, there is also the NginX proxy created by Jason Wilder: https://hub.docker.com/r/jwilder/nginx-proxy Then you don’t have to write the rules manually. To use it with PHP containers instead of Apache HTTPD with PHP module, use the FastCGI backend.

1 Like

Bummer! I shortly saw someone was typing an answer so I stopped. But that notification went away.

Good point. As for searching for documentation, that is also called a reverse proxy in Apache then.

Oh guys thanks for all the help.

If I would try option 1 my docker-compose.yml would look like this?

version: '3.1'

services:
  php_bodyscale:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src-bodyscale:/var/www/html/

php_thermometer:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src-thermometer:/var/www/html/

php_blood_pressure_monitor:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src-blood-pressure-monitor:/var/www/html/

php_sleep_analyzer:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src-sleep-analyzer:/var/www/html/

  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports: 
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data

And then I should past every php code from a device in the corresponding direction right?

Again thank you!

No, you cannot have multiple containers using the same port (unless using different host names). The part for a single web server for all four applications would be something like:

  php:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src-bodyscale:/var/www/html/bodyscale/
      - ./src-thermometer:/var/www/html/thermometer/
      - ./src-blood-pressure-monitor:/var/www/html/blood-pressure-monitor/
      - ./src-sleep-analyzer:/var/www/html/sleep-analyzer/

Of course, if all source folders are in the same parent folder, then the following would suffice too, basically just like you already had in your first post:

    volumes:
      - ./some/shared/source/folder:/var/www/html/

If the subfolder for the source of the body scale is then simply called bodyscale then the above would make http://localhost/bodyscale/ available. The name of the source folder would then also determine the URL.

Note that Apache PHP will by default serve the file index.php for http://localhost/bodyscale/. So the above assumes each device has a file named just like that. You don’t need to rely on that; you could also request give_me_measure_data_from_device_B.php if the file is named like that, from your first post.

Ok so first option worked and I understand what you meant with your solution. But in my case I wanted to work every device on a single container. I think in this case only solution two or three from your second post will work for me.

Edit: I forgot something to ask. Is it possible with your first solution from your second post to have shared php data. In my example there is an authorization.php which will output an access token. This access token allows your system to ask for the measure data of every device. Is there a possibility that the four volumnes can communicate which each other (for exampe using the command: “insert name_of_function.php”)

In the first solution, a single web server (which you do not want), all is shared already. Regardless if you use four volumes that mount into a subfolder of the container’s /var/www/html/, or if you use a single volume that mounts some shared parent folder into that location, Apache/PHP will simply see sub-directories and files in /var/www/html/ without knowing anything about different source volumes.

You can make any shared data more explicit by adding yet another volume (maybe an in-memory volume if you’re on Linux) through which your devices can share data. (Also, you’re already sharing a database.)

If you meant the second solution: the four separate web servers can also share a dedicated volume to access each other’s data. However, as in the second solution they will be running on different port numbers and/or (sub)domains, browsers will not share data. Like: browsers will not share cookies between the four servers, as they are considered to be different servers.

I don’t understand what you’re trying to say here.

Aside, I know all is new to you, but maybe the first solution is clearer if you create an image for that. That’s what Docker is about after all! Like, not tested:

FROM php:7.4-apache
RUN docker-php-ext-install mysqli

# Copy some overview page that links to the next devices
COPY ./start-page/index.html /var/www/html/

# Copy the current versions of the current devices into the
# web root, all having an index.php file as the entry point
COPY ./body-scale-v1/ /var/www/html/weight
COPY ./thermometer/src/php/ /var/www/html/temperature
COPY ./sleep-analyzer/ /var/www/html/sleep
COPY ./blood-pressure/v3/ /var/www/html/bp

Above, I used a few different naming strategies for the source paths, like body-scale-v1 and blood-pressure/v3/, just to explain how you could copy current (versions of) devices into the image, and still end up with the same URL like http://localhost/weight and http://localhost/bp whenever you update something. (Aside: versioning should probably be done in source control. Different topic.)

With the above Dockerfile, you would copy different sources into a single image which you could use in your docker-compose.yml from your first post, but without the volume (the “bind mount”) ./src:/var/www/html/ that obscures the contents of /var/www/html/. Without that bind mount, instead of looking at your file system for the source files, the container would use the files that you copied into your own image.

Also, though using a container per device is nice, I’d recommend implementing a much easier solution first, to get to know Docker and maybe even PHP. This will also make clear if you have additional requirements, like you’ve now mentioned sharing data between devices.