We use docker to manage multiple instances of various tools on a server that we control. We have an Nginx server working as a reverse proxy that forwards all requests to the appropriate containers in the configuration. Sometimes, after updating the container images and recreating the containers, we get the error that ports 80 and 443 are already in use by another container. This problem can happen even if no other container asks for them.
The following excerpt demonstrates the problem as mentioned above.
tux@server:~/docker-compose$ docker-compose up -d --remove-orphans;
Recreating container_a ...
Recreating container_a ... done
Recreating container_b ... done
Recreating container_c ...
Recreating nginx_reverse_proxy ... error
Recreating container_d ... done
Recreating container_e ... done
Recreating container_f ... done
WARNING: Host is already in use by another container
ERROR: for nginx_reverse_proxy Cannot start service nginx_reverse_proxy: driver failed programming external connectivity on endpoint nginx_reverse_proxy (5a790ed7e1b24aa36cb88cbd3f49d306efa8fe023bf5b3312655218319f23a35): Bind for 0.0.0.0:443 failed: port is already allocated
ERROR: for nginx_reverse_proxy Cannot start service nginx_reverse_proxy: driver failed programming external connectivity on endpoint nginx_reverse_proxy (5a790ed7e1b24aa36cb88cbd3f49d306efa8fe023bf5b3312655218319f23a35): Bind for 0.0.0.0:443 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.
tux@server:~/docker-compose$ sudo systemctl restart docker.socket docker.service;
To solve this issue, we had to restart two services using the systemctl command:
docker.socket
docker.service
Specifically, on an Ubuntu server, we used the following command:
Please note that the following commands were executed on a system that already had CUDA support so we might be missing a step or two to enable NVIDIA CUDA support.
We chose to install the php-cli package instead of the php as we do not need to install all the additional dependencies php has, like apache2. Since we are working on a development computer, we can skip the required packages for deployment.
We decided to use MySQL in our project, so we installed the php-mysql package that provides the PDO for that database technology.
php-intl and php-mbstring were installed to suppress the following warnings:
Optional recommendations to improve your setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* mb_strlen() should be available
> Install and enable the mbstring extension.
* intl extension should be available
> Install and enable the intl extension (used for validators).
Downloading and installing symfony
Since Symfony version 5, there is a new support application for the development of Symfony projects. Using the following commands:
we downloaded it from the official website,
installed it to our home folder,
and then moved it to /user/local/bin/symfony to be accessible from any terminal without changing the path each time.
In case you do not want to move the binary to /usr/local/bin you can either use it as a local file:
~/.symfony/bin/symfony;
or add it to your $PATH variable:
export PATH="$HOME/.symfony/bin:$PATH";
Creating a new project and making sure dependencies are met
After the above steps are done, we can clone the Symfony 5 skeleton and then use the symfony support application to check that our system has all the needed features.
symfony new symfony_project;
cd symfony_project;
symfony check:req;
If everything is OK, you should get a message similar to the one below:
$ symfony check:req
Symfony Requirements Checker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> PHP is using the following php.ini file:
/etc/php/7.4/cli/php.ini
> Checking Symfony requirements:
...................................
[OK]
Your system is ready to run Symfony projects
Note The command console can use a different php.ini file
~~~~ than the one used by your web server.
Please check that both the console and the web server
are using the same PHP version and configuration.
Starting a minimal web server to see the skeleton application
Using PHP’s built-in server, we can execute the skeleton application and see the result in our browser:
php -S 127.0.0.1:8000 -t public/;
Starting the Symfony minimal web server to see the skeleton application
Another option to check out your application is using the Symfony built-in web server, which is richer in features than the PHP server but lighter than Apache or Nginx. Below we present how to start it as an application in a terminal and how to start it as a detached service (leaving your terminal free for other operations).
#If you start it as it as an application, you will need to press Ctrl + C to kill it.
symfony serve;
Starting Symfony server as a detached service:
symfony serve -d;
#To stop it, use the following
symfony server:stop;
#Please note that the command contains the word server and not serve like before.
Adding more features to our project
To make our project more dynamic and versatile, we need to install a few packages using composer. Composer is a PHP utility for managing dependencies. It allows you to indicate the libraries your project relies on, and it will take care of installing and updating them. To fast install it, open a terminal and type the following command:
curl -Ss getcomposer.org/installer | php;
# Moving the composer into the /usr/local/bin/ folder will allow us to access it from any folder later on as that folder is in the default PATH variable. You could again avoid this step but it makes the process more user friendly.
sudo mv composer.phar /usr/local/bin/composer;
Allowing code annotations in our PHP code
After the composer is successfully installed, we can install the annotations package, which among other features, will allow us to define routes inside our PHP controller files.
composer require annotations;
A code example of that is the following:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class QuestionController extends AbstractController
{
/**
* @Route("/", name="app_homepage")
*/
public function homepage()
{
return new Response('Done');
}
/**
* @Route("/questions/{slug}", name="app_question_show")
*/
public function show($slug)
{
$answers = [
'a',
'b',
'c'
];
dump($this);
return $this->render('question/show.html.twig', [
'question' => ucwords(str_replace('-', ' ', $slug)),
'answers' => $answers
]);
#return new Response(sprintf("The question: %s", $slug));
}
}
Installing the twig package that allows us to work better with HTML templates
To avoid embedding HTML code in our PHP code, we can install twig, which provides a framework of templates to build several sites quickly.
composer require template;
Enriching the development experience
To debug in a better way our applications, we install the following two groups of packages that provide several debugging features, including a logging mechanism.
To avoid hardcoding items in your DOM (and forcing yourself to remember to edit them depending on the deployment options you are using), you can use the asset package that will handle most of those issues.
composer require symfony/asset;
Serializing more items and objects to JSON and XML
To enrich the power of API calls that return JSON or XML objects (like the code below)
return $this->json(/*...*/);
we can install the following serializer:
composer require symfony/serializer;
and be used as follows:
$serializer->serialize(
$myObject,
'json'
);
Develop using HTTPS / SSL for free
Although we are not super happy about installing local Certificate Authorities on our machines, we used the following commands to install the Symfony Certifying Authority certificate and enable HTTPS/SSL development without accepting a non-verified certificate in the browser each time.
If you do not install libnss3-tools, you will get the following warning:
$ symfony server:ca:install
You might have to enter your root password to install the local Certificate Authority certificate
Sudo password:
The local CA is now installed in the system trust store!
WARNING "certutil" is not available, so the CA can't be automatically installed in Firefox and/or Chrome/Chromium!
Install "certutil" with "apt install libnss3-tools" and re-run the command
[OK] The local Certificate Authority is installed and trusted
After you install it, the message will change as follows:
$ symfony server:ca:install
The local CA is now installed in the Firefox and/or Chrome/Chromium trust store (requires browser restart)!
[OK] The local Certificate Authority is installed and trusted
Install Webpack Encore for the assets
To install Webpack encore, we need yarn. To get yarn, we need npm. So we need the following installation steps:
After these steps are successful, in the project folder, execute the following commands to allow the yarn to perform all necessary installations and then use encore to monitor the assets and rebuild its cache. The settings are depended on the file webpack.config.js.
yarn install;
yarn encore dev --watch;
Below, we present an example file of webpack.config.js.
var Encore = require('@symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Add 1 entry for each "page" of your app
* (including one that's included on every page - e.g. "app")
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
// enables @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes(Encore.isProduction())
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery()
// uncomment if you use API Platform Admin (composer req api-admin)
//.enableReactPreset()
//.addEntry('admin', './assets/js/admin.js')
;
module.exports = Encore.getWebpackConfig();
Some settings for PHPStorm by JetBrains
Since the IDE we are using for PHP development is PHPStorm, we installed the recommended plugins for Symfony to it. In the following image, we list the three plugins that we installed.
Specifically, we installed:
Symfony Support
PHP Annotations
PHP Toolbox
After installing the three plugins, we navigated to the Symfony Plugin settings (which you can find either using the search functionality or under the menu: Languages & Frameworks > PHP > Symfony).
From there, we clicked on the Enable Plugin for this Project and then changed the Web Directory from web to public.
This guide will present the steps we followed on a GNU/Linux Ubuntu 20.04LTS to create a new project out of the Symfony website skeleton and then create a new docker application image of it.
Install core dependecies
Install php-cli instead of php as we do not want to install the additional dependencies of php like apache2. p7zip-full is needed for the package manager of composer later on. If it is missing, we will be getting one of the following errors:
Failed to download symfony/requirements-checker from dist: The zip extension and unzip/7z commands are both missing, skipping.
As there is no 'unzip' nor '7z' command installed zip files are being unpacked using the PHP zip extension.
php-xml will be required later on while creating the skeleton project for Symfony. If it is missing, you will get the following error:
symfony/framework-bundle requires ext-xml * -> it is missing from your system. Install or enable PHP's xml extension
sudo apt install php-cli php-xml p7zip-full;
Composer is a PHP utility for managing dependencies. It allows you to indicate the libraries your project relies on, and it will take care of installing and updating them. To fast install it, open a terminal and type the following command:
curl -Ss getcomposer.org/installer | php;
# Moving the composer into the /usr/local/bin/ folder will allow us to access it from any folder later on as that folder is in the default PATH variable.
sudo mv composer.phar /usr/local/bin/composer;
Symfony provides a tool to check if your operating system meets the required requirements rapidly. In addition, if suitable, the tool makes installation recommendations. To install the tool, run the following command:
composer require symfony/requirements-checker;
$ composer require symfony/requirements-checker;
Using version ^2.0 for symfony/requirements-checker
./composer.json has been updated
Running composer update symfony/requirements-checker
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking symfony/requirements-checker (v2.0.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing symfony/requirements-checker (v2.0.1): Extracting archive
Generating autoload files
1 package you are using is looking for funding.
Use the `composer fund` command to find out more!
Once done, you can safely delete the requirements-checker:
composer remove symfony/requirements-checker;
Create the Symfony project
Using the basic skeleton, you can create a minimal Symfony project with the following command. We install the latest version of version 4.4 of the website skeleton project in this example. We found the list of versions here https://packagist.org/packages/symfony/website-skeleton.
When we got the following warning, we typed y, not sure what changes, so we stayed with the default option:
- WARNING symfony/mailer (>=4.3): From github.com/symfony/recipes:master
The recipe for this package contains some Docker configuration.
This may create/update docker-compose.yml or update Dockerfile (if it exists).
Do you want to include Docker configuration from recipes?
[y] Yes
[n] No
[p] Yes permanently, never ask again for this project
[x] No permanently, never ask again for this project
(defaults to y): y
Then you need to run the following commands to install all dependencies and execute the project:
By now, you should see in a browser the landing page of your skeleton project.
# Stop the php webserver and release the port, we will need it later on.
php bin/console server:stop;
Install docker on Ubuntu
First of all, make sure your system is clean and remove any old versions:
sudo apt-get remove docker docker-engine docker.io containerd runc;
# You might want to execute `sudo apt autoremove -y;` as well to cleanup everything. We cannot ask everyone to do so as we are not sure of what complications it might have on each computer+software configurations.
We will be installing docker by adding its repositories to our system:
If the installation was OK, you should see the following message:
n$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:cc15c5b292d8525effc0f89cb299f1804f3a725c8d05e158653a563f15e4f685
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Make the docker application image
Execute the following command on a terminal to get your php version:
php --version;
In case you get something different than version 7.4, please note it and update the contents of the DockerFile below accordingly. In our case, the results for the version were the ones right below and that is why we used the line FROM php:7.4-cli in our DockerFile.
$ php --version
PHP 7.4.3 (cli) (built: Oct 25 2021 18:20:54) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies
If you are not already at the root of your project (e.g., the symfony-skeleton folder), go to that folder and create a new text file with the name Dockerfile in there. The contents of the file should be the following:
# Dockerfile
FROM php:7.4-cli
RUN apt-get update -y && apt-get install -y libmcrypt-dev
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN apt-get update && apt-get install -y libonig-dev
RUN docker-php-ext-install pdo
WORKDIR /app
COPY . /app
RUN composer install
EXPOSE 8000
CMD php bin/console server:run 0.0.0.0:8000
Once you have Docker and Docker Machine installed on your machine, creating the container is a breeze. The command below will seek your Dockerfile and download all of the layers required to execute your container image. It will then complete the commands in the Dockerfile, leaving you with a container that is ready to use.
You’ll use the docker build command to create your php Symfony docker container, and you’ll give it a tag or a name so you can refer to it later when you want to execute it. The command’s final component instructs Docker to build from a specific directory.
sudo docker build -t symfony-project .;
To execute the new application image:
sudo docker run -it -p 8000:8000 symfony-project;
To export the Docker image as a tar file:
sudo docker save -o ~/symfony-skeleton.tar symfony-project;