Docker php cli cmd

Using Docker for running PHP Command Line Applications

A relatively complex command line application written in PHP might require Git, Composer and a few extensions to function properly. Do you know you can ship all those dependencies together to both local and production environment and ensure it is consistent across all platform?

What are PHP Command Line applications?

As the name suggests, its an application that is being run in terminal or command prompt. Usually to perform some CPU intensive background task that executes way too long to run directly via browser.

For example, applications built using the Symfony Console or Laravel Artisan frameworks.

  • curl, json, mbstring, mcrypt, mysql and other php extensions
  • Composer to fetch the dependencies of the application
  • Git so that Composer can function properly
  • Basic editing tool such as vim
  • Basic mysql client

Sometimes, due to high amount of data that needs to be processed, you might need to scale out the processing into multiple machines. Most of the time, those machines has «something else» also installed. Like a RoR application, a Memcached daemon or at worst cases, a legacy application that has very old PHP version installed, which makes installation of Composer for example, not possible.

Читайте также:  Learn about php mysql

Wouldnt it be great if there is a way to deploy your application into multiple environments and ensure all those tools and dependencies you need can be automatically installed in the target machines, while at the same time ensuring it wont conflict with the existing stuffs there?

Introducing Docker

In simple terms, you can think of Docker as an extremely lightweight virtual machine. Though its not exactly like that in lower level, but that is the idea — be able to run isolated processes in the same server w/o conflicting to each other.

You can get Docker in here and make sure you have docker and docker-compose commmands installed.

Once installed, you should create a docker-compose.yml file at the root of your project

and then, create another file called

You are now good to go! just hit up the following command to start fetching all the dependencies

This will start fetching the base image php-production-cli which is basically a pre-built Docker Container that is specialized for running PHP command line applications.

This might take a while for the first time, but should be extremely fast the next time you do need to have another project which uses the same dependency.

Once everything is finish downloading. You can now execute your PHP command line application by

docker-compose exec cli bash

This will open a new terminal inside the container. The container has now everything you need to execute your php app! that is, ranging from Composer, Git, PHP extensions, vim, mysql clients etc.

That means, regardless of you are in a server, local PC, Mac or Windows. You’ll only need two commands and voila, you have an instant environment for PHP application.

Did you find this useful?

I’m always happy to help! You can show your support and appreciation by Buying me a coffee (I love coffee!).

Источник

Create a PHP command based on docker

The purpose of this post is to see if it is possible to avoid installing PHP on development machines. To achieve this, the idea is to create a PHP command based on a docker container.

PHP and docker

PHP can be used with docker. In production, docker brings consistency with other applications coded with different languages. And this can greatly simplify their deployment.

But docker can also be useful in a development environment. There is no doubt that when there is a new developer on a project, docker makes it far easier for him to install all the necessary dependencies. And at the same time it ensures that the environment is exactly the same for everyone, in development but also in production. This could keep you away from strange bugs.

There are good tutorials that explain how to use docker-compose to configure your project, like this one.

To use a production container in development, you just need to create a volume. So the code on your system replaces the one inside the container. This way the code can be modified outside the container in a code editor. The changes are reflected immediately. And this works both ways. You can use docker exec to run some commands in the container and it will affect the code on your machine.

So far so good, no need to install PHP outside docker for this use case.

Need a PHP command after all ?

All this works fine. But there are still cases where PHP is needed outside of the container of the application.

  • If you want to use a linter or a code formatter in your code editor, it will need access to PHP.
  • At some point you may also want to run some small scripts that do not belong to a project.

For this you still need PHP outside docker.

Creating a PHP command

There still is a solution to avoid installing PHP outside docker.

You can use docker run to create a container and execute PHP. You just need to add some volumes to the container. These volumes should include the paths to your code.

# the current directory and the script directory should be included in the home directory for this to work docker run -it --rm -v /home:/home -w $PWD php:cli php /home/my_script.php

Note that this could be included in a script:

#!/bin/bash docker run -it --rm -v /home:/home -w $PWD php:cli php $@

Which can be used like this: ./php.sh arg1 arg2 .

And now you have your php command !

It works but it increases the startup time so much that this makes it unusable in a code editor. At least if your code formatter is called each time you save a file. The delay is due to the creation of the container. So there may be a way to reduce this overhead.

You can create a container that runs in the background. Instead of executing a script, the container sleeps indefinitely. Then you can use docker exec to execute a PHP script in the container.

# create a sleeping container docker run -d -i --name php_worker -v /home:/home php:cli bash -c "while true; do sleep 1; done;" # execute the php script docker exec -it -w $PWD php_worker php /home/my_script.php # remove the container docker kill php_worker && docker rm php_worker

On my machine I get these results for php -v :

Method Execution time
docker run ~ 900 ms
docker exec ~ 180 ms
php ~ 15 ms

The docker exec version saves a few millisecond compared to docker run . It is not perfect but it is enough to make it usable.

In practice

Creating the container and then using docker exec is not really practical. So I wrote a python script that uses this idea.

It uses docker exec to run the PHP command. Before running the exec command, it checks to see if the sleeping PHP container exists. If it does not, it creates it. This container will run for a defined duration, so the overhead induced by its creation will only impact the first call. It also can write logs to help debug eventual issues.

Beware that the script may need to download the docker image, so it can take time on the first call.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
#!/usr/bin/python import os import sys import subprocess import datetime #--------------------- # configuration #--------------------- volumes = ["/tmp", "/opt", "/home"] # add the directories where your php files are located imageName = "php:cli" # set imageName to php:5.6-cli to use php 5.6 command = "php" containerName = "php_cmd_worker" # name of the sleeping container containerLifetime = 12 * 60 * 60 # clear the container after 12 hours logFile = "" # set logFile to log the output in a file, useful for debugging if the script does not work debug = False # set debug to True to log the output even if it is not an error #--------------------- # execute command #--------------------- def main(): # ensure the php container is running if not isRunning(): restartContainer() # executes the php command inside the php container output = execCmd( ["docker", "exec", "-i", "-w", os.getcwd(), containerName, command] + sys.argv[1:], "execute command") sys.stdout.write(output) # isRunning returns true if the php container is running. def isRunning(): output = execCmd([ "docker", "ps", "-f", "status=running", "-f", "name=" + containerName ], "check if container is running") length = len(output.split('\n')) return length == 3 # restartContainer starts (or restarts) the php container. def restartContainer(): sleepCmd = "sleep " + str(containerLifetime) if containerLifetime  0: sleepCmd = "while true; do sleep 1; done;" execCmd(["docker", "kill", containerName], "kill existing container") execCmd(["docker", "rm", containerName], "remove existing container") execCmd(["docker", "run", "-d", "-i", "--name", containerName] + formatVolumes(volumes) + [imageName, "bash", "-c", sleepCmd], "create container") # formatVolumes returns a list of string # containing the volume arguments for the docker run command. def formatVolumes(volumes): vols = [] for v in volumes: vols += ["-v", v + ":" + v] return vols # execCmd runs a unix command. def execCmd(cmd, descr=""): child = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = child.communicate()[0] if debug or child.returncode > 0: log(output, descr) return output # log logs a message to the logFile. def log(msg, descr): if logFile == "": return with open(logFile, "a") as f: f.write("### " + datetime.datetime.now().isoformat() + " [" + descr + "]" + "\n" + msg + "\n") if __name__ == "__main__": main()

Copy the script in /usr/local/bin/php (or any other destination included in your $PATH) and you have a PHP command that works with docker:

sudo vim /usr/local/bin/php # I used vim here but you can use your favorite editor sudo chmod +x /usr/local/bin/php

The script has two restrictions:

  • It must be called from a directory that is shared with the docker container.
  • The script and its dependencies must be in the directories shared with the container.

Why use this ?

With this script it is possible to avoid installing PHP on your system. But using a package manager like apt is still a good choice. So what are the advantages of this docker solution ?

  • Depending on your package manager repositories, it may take time to get the latest version of PHP. On the other hand, the official PHP docker images are updated frequently. So you may have the latest version more quickly.
  • With this script you can run different versions of PHP with ease. You just need to change the name of the images in the script.
  • You can create and use your own PHP image with additional extensions. Share the image with the rest of your team so they can have exactly the same environment.
  • You can also use the image of one of your PHP project.

In practice you probably will want to install PHP with a package manager. But this script can be a good complement for specific use cases.

Other applications

In this example the script is used for PHP. But it is not restricted to that. It can be adapted to be used with other scripting languages like Node.js or ruby for example. You just need to change the configuration parameters.

Источник

Оцените статью