Part 1: Containerizing the Application.
Ivan BabenkoBlockedUnblockFollowFollowingApr 23About the SeriesIn this series we will talk about migration of an existing Symfony project to Kubernetes (K8s).
No matter where and how you currently publish your application: bare metal machine, VM, container orchestration platform, by following this guide you will be able to deploy it to a K8s cluster.
These how to articles are targeted at those, who already know some basics of Kubernetes, Docker and Continuous Integration (CI), want to learn new tricks or those, who just want to get things done.
Every part of the series is dedicated to one particular topic: building a Docker environment, testing application on different levels, deploying application to Kubernetes with Helm, building CI pipeline.
About the Part 1Since Kubernetes is a container orchestration platform, it requires your application to be containerized in order to be deployed to a cluster.
In this article, we are going to build a Docker setup, which is prepared for K8s deployment and convenient usage in development.
About the Dummy ProjectI prepared a simple Symphony 4 application for this series, which includes:Product entity with three fields: id, price, name.
Product controller with a show action, that renders a given product.
Layout Twig template built with Bootstrap.
Even though you can use CDN for Bootstrap assets, I downloaded the css file and put it in the assets folder of the project, to show you how to deal with static files.
Product Twig template.
Let’s Containerize!To run the dummy project we need the following services:MySQL as a database.
It’s not the best idea to use database container for production environment, but it’s a good option for development and testing.
Nginx as a web server.
PHP-FPM to process PHP code.
MySQL ImageHere is the complete Dockerfile for MySQL image:The argument VERSION allows us to build images with different MySQL versions using a single Dockerfile.
The init folder in the context directory, contains files, that will be executed on container startup.
Let’s create products.
sql in this folder, to initialize products table for our application:Nginx ImageWhen building production ready images, try to:Use alpine images as a basis, they are lightweight.
Avoid mounted volumes for production, make complete images.
Use stages if your dev image differs slightly from production one.
When you build an image, you can specify a target, that represents a stage from your Dockerfile.
The final image will include all the steps up to the target.
Keeping everything above in mind, we can make a Dockerfile for Nginx:You could notice, that we only copy assets in production, but not in development stage.
We did it on purpose: for development we will mount a volume in the container.
This will allow us to see file changes real time, without a need to rebuild the image every time.
The server config, that we copy to the image, almost repeats the one, provided by Symphony tutorial.
The only changes are the document root and FastCGI url, that refers to PHP-FPM service:PHP-FPM ImageThe PHP-FPM Docker file is more complex, than the previous ones:Let’s go though it step by step.
System DependenciesWhen installing system dependencies, try to:Use –no-cache flag.
In this case the package manager doesn’t keep downloaded files in cache, that reduces the image size.
Use –virtual for dependencies, that you want to remove at some point.
We don’t need git (required by Composer) or autoconf gcc g++ make (required for XDebug) in production.
We are going to remove these dependencies, that’s why we marked them as dev-deps.
PHP ExtensionsDocker comes with useful helpers, that simplify installation and configuration of PHP extensions.
Let’s use them to install MySQL PDO driver for database connection and XDebug for code coverage:PHP ConfigurationYou can change PHP settings by copying ini files to the image conf.
d folder:In the default.
ini file we change memory limit and reporting level of the application:ComposerI recommend to install hirak/prestissimo package along with Composer.
It will dramatically reduce building time:We will mount project files in PHP-FPM dev container, that’s why we don’t copy source files and don’t execute composer install in dev stage.
Test StageIt’s better to incapsulate application code and its dependencies inside the image, which will be used for testing:And change the project files owner to default PHP-FPM user:Prod StageWe don’t need dev dependencies in production image:Neither we need XDebug:Nor dev utils:Docker ComposeDocker Compose is the best option to build images and create containers locally.
It allows you to build environment with just one command: docker-compose up.
For your convenience, Compose automatically loads environment variables from .
env file, located in the same directory, where docker-compose command is executed.
Let’s declare some useful variables for our Docker setup in .
env file:We specified the exact versions of software, that we want to use and database credentials.
We are ready to setup services for development environment in docker-compose.
yml file:Looks quite complex at the first sight, but no worries, we will have a closer look at each service.
Nginx ServiceTo utilize Nginx version from .
env file, we need to declare it as a build argument:We use the root directory of our application as a context, because we need access to assets folder:As the context folder is different from the folder where Dockerfile is located, we need to explicitly set Dockerfile location:Now let’s instruct Docker to build Nginx image only for dev stage:As I mentioned before, for dev environment we will mount assets folder in the container:It makes no sense to start Nginx container without PHP-FPM:To access the web server, we need to forward port from our host machine to the container.
To avoid some conflicts we will use 8000:Let’s have a look again at complete Nginx service definition:PHP-FPM ServiceThe PHP-FPM service definition is quite similar to Nginx:I want to clarify just a couple of things here:We mount all the project files in the container app folder, because we want to see changes made in PHP files immediately.
On startup we install the Composer dependencies and start the PHP-FPM daemon.
If we install dependencies in Dockerfile and then mount application files, the vendor directory will be lost, if it doesn’t exist on host machine.
MySQL ServiceThe database definition is a bit different:The context directory is a dedicated mysql folder: this image doesn’t need access to project files.
We used environment variables to specify a database and a root password.
We shared a volume to keep database changes in case we restart the container.
Let’s Run the Application!The Docker setup is ready.
Now we only need to configure the application.
Let’s create .
env.
dev file and set up the database connection:Finally, we can build the dev environment with the following command:It takes some time for PHP-FPM service to start up, because we install Composer dependencies first and only then start PHP-FPM.
When all the services are ready, open http://localhost:8000/products/1 in your browser.
You should see a product page:What’s Next?The application is containerized now and ready to be deployed to K8s.
In the next guide I will show you how to define a Helm package and deploy it to Kubernetes Docker Desktop.
Thank you for reading and see you next time!.. More details