Running a container
Running a container in Docker PHP like it would be done with the docker client docker run image command
is not a
single call to api, even with the docker run command, it involves multiple calls to the API.
Creating the container
First step is to create a container and its associated configuration, by creating a ContainerConfig
instance and
passing to the create
api endpoint.
<?php
use Docker\API\Model\ContainersCreatePostBody;
use Docker\Docker;
$docker = Docker::create();
$containerConfig = new ContainersCreatePostBody();
$containerConfig->setImage('busybox:latest');
$containerConfig->setCmd(['echo', 'I am running a command']);
$containerCreateResult = $docker->containerCreate($containerConfig);
This will return a ContainersCreatePostResponse201
object with the id of the created container. If you don't want to use
container id you can also specify a unique name for this container:
$containerCreateResult = $docker->containerCreate($containerConfig, ['name' => 'my-container-unique-name']);
Be aware that the container is immutable if you need to change a configuration for a container, you will need to remove the existing one and create it again with the new configuration.
Starting the container
Once a container has been created, you can start it, this will in fact, only launch the process inside the isolated
container. This is done with the containerStart
method, you can use the id of the container or the
name:
$docker->containerStart($containerCreateResult->getId());
// Or
$docker->containerStart('my-container-unique-name');
The start method will always return the raw PSR7 Response, but you don't need to check it as on failure it will throw an exception.
Waiting for the container to end
Once your container is started you can wait for it end by using the containerWait
method, be aware that your PHP script will
block until the container has stopped or that the default timeout set on the client has been reached (default to 60
seconds)
$docker->containerWait('my-container-unique-name');
Stopping the container
Once your container is started you can stop it by using the containerStop
method. You can use the id of the container or the name:
$docker->containerStop($containerCreateResult->getId());
// Or
$docker->containerStop('my-container-unique-name');
Reading logs in real time
Sometimes you will need to read logs in real time for a container. You can use the containerAttach
method for that.
Be aware that you will only receive them if you configure the container with
json log driver, which is the default configuration.
$attachStream = $docker->containerAttach('my-container-unique-name');
The $attachStream
returned will be an instance of a DockerRawStream
. You can use this object afterwards to add
callbacks on the different streams:
addStdin
to add a callback on the stdin streamaddStdout
for the stdout streamaddStderr
for the stderr stream
The callback for each of this method takes a string for the first argument which correspond to the log line.
Use then the wait method to activate real time logging, this method will only stop when then stream is closed which correspond to when the container has stopped.
$attachStream->onStdout(function ($stdout) {
echo $stdout;
});
$attachStream->onStderr(function ($stderr) {
echo $stderr;
});
$attachStream->wait();
If you follow all the example, you will not see the log and this normal. In fact the container and the call to the attach method need extra configuration:
$containerConfig = new ContainersCreatePostBody();
$containerConfig->setImage('busybox:latest');
$containerConfig->setCmd(['echo', 'I am running a command']);
// You need to attach stream of the container to docker
$containerConfig->setAttachStdin(true);
$containerConfig->setAttachStdout(true);
$containerConfig->setAttachStderr(true);
$docker->containerCreate($containerConfig, ['name' => 'my-container-unique-name']);
// You also need to set stream to true to get the logs, and tell which stream you want to attach
$attachStream = $docker->containerAttach('my-container-unique-name', [
'stream' => true,
'stdin' => true,
'stdout' => true,
'stderr' => true
]);
$docker->containerStart('my-container-unique-name');
$attachStream->onStdout(function ($stdout) {
echo $stdout;
});
$attachStream->onStderr(function ($stderr) {
echo $stderr;
});
$attachStream->wait();
If you read the following example, you will notice that we call the containerAttach
method before starting the container with
containerStart
. This is normal, as otherwise during the time the container is started and the call to the containerAttach
endpoint
some logs may have been processed and you will loose this information. That's why it is strongly recommended to attach
the container before starting it.
Interacting with a container
WIth the last example we can now read the log a container in real time. However you may need to send input to this
container. This can be done by attaching a websocket with the containerAttachWebsocket
method:`
$webSocketStream = $docker->containerAttachWebsocket('my-container-unique-name', [
'stream' => true,
'stdout' => true,
'stderr' => true,
'stdin' => true,
]);
The returned stream will be an instance of AttachWebsocketStream
and it can be used to both reading and writing to
the container:
$line = $webSocketStream->read();
$websocketStream->write('i send input to the container');
If the $line
is null
then the stream is no longer available (container is certainly stopped), if it's equal to
false
then the stream is still available but no output was received, otherwise it will return output from the container.
To actually write on the stream (having the stdin stream open) you will, again, need extra configuration when creating the container:
// Open the stdin stream from docker engine to the container
$containerConfig->setOpenStdin(true);
// Needed if you want to use process that rely on a tty, be aware as there is, in fact, no tty this may cause bug to
// the underlying process in your container
$containerConfig->setTty(true);
Be aware that there is no distinction between stdout and stderr in this mode.
Port Mapping
This example shows how you can map port 8080 on your host machine to port 80 on the container.
<?php
use Docker\API\Model\ContainersCreatePostBody;
use Docker\API\Model\HostConfig;
use Docker\API\Model\PortBinding;
use Docker\Docker;
$docker = Docker::create();
$containerConfig = new ContainersCreatePostBody();
$containerConfig->setTty(true);
$containerConfig->setExposedPorts(['80/tcp' => new \stdClass]);
$portBinding = new PortBinding();
$portBinding->setHostPort('8080');
$portBinding->setHostIp('0.0.0.0');
$portMap = new \ArrayObject();
$portMap['80/tcp'] = [$portBinding];
$hostConfig = new HostConfig();
$hostConfig->setPortBindings($portMap);
$containerConfig->setHostConfig($hostConfig);