Recently I had chance to learn about Docker and also played around with it for a while. It’s really amazing technology, which is here to take away all the pain of deployment. In current world of web hosting services, it’s always a pain to move from one host to another. Major reason is that, you will have to configure the new hosting server completely same way right from scratch manually and if that doesn’t go accurately, things gonna break very badly.
With docker, you have each and every step documented/defined(in Dockerfile or as a Docker image) and thus it will always be same, independent of where you are going to host. And most of all, you can automate the creation/configuration of the server, which might take you hours or even days!
Today, I will try to explain how someone can Dockerize an application(which is multi containers based) as a beginner. I will be using PHP and MySQL stack. If you are looking for Dockerizing NodeJS app, then you can follow official example for that. I am using my open source codeigniterplus PHP/MySQL project as the candidate application.
To follow this tutorial you will need to know how docker works. You can get a rough idea from official what is docker web page. You can also watch the video, which is very helpful as well. To get your hand dirty, you can try it on interactive web-based terminal to get used to with some common commands.
Additionally, here are some terms that we will be using throughout this tutorial, which you will need to know.
- Docker Image: Docker image is a ready snapshot of a configured server/service. We are free to use a docker image as it is(if sufficient) or extend it to a new docker image to meet our needs.
- Docker Container: Containers are the running/live/in action server/service of Docker images.
- DockerFile: Dockerfile contains a set of instructions/commands that will be executed to form a new docker image. That Dockerfile can be treated as a well documented schema version of that docker image.
- Fig: It’s probably moving forward with a new name called ‘Docker Compose’, recently announced by a recent post on docker blog
Basically you need to have two things setup on your machine where you are trying this tutorial and on the server machine where you are planning to have this docker based deployment.
- Docker: Docker has comprehensive instruction pages for several different platform about installations. Just follow the one you are interested in.
- Fig: As we will be using fig too, get yourself setup with official instructions.
Step 1: Make Your PHP Project Build-able Completely With Commands:
As we want our solution as much automated as possible, try to have a set of commands(dependency installation with composer, run phpunit tests, change directory/file permissions etc) that will get your application completely ready for deployment.If you have no idea how to do that, you can follow my another tutorial on building php application in single step. You just focus on preparing the application itself, not the environment yet.
Step 2: Create Customize Docker File/Image From An Existing PHP Based Docker Image:
Now its time to get our hands dirty with docker. Basically you will want a ready image that have php installed so that you don’t have to add instruction to install PHP itself on the Dockerfile. If you can’t find anything suitable, you can refer to mine which I created few days ago for exercise/testing purpose.
Now you will may have different configuration files for production environment that you will want to replace or add to the source code. For that you will have to add instructions manually in the Dockerfile to add them from host machine to docker container. In my cases, I have kept those such files in ‘docker-files’ directory(feel free to store them on a directory you are flexible with).
Here is how my Dockerfile looks like.
FROM ranacseruet/php-node #App setup RUN rm -fr /app RUN rm -fr /app && git clone https://github.com/ranacseruet/dockerized-php.git /app ADD docker-files/makefile /app/makefile RUN cd /app && make ADD docker-files/database.php /app/application/config/database.php ADD docker-files/run.sh /run.sh ADD docker-files/.htaccess /app/.htaccess RUN chmod 755 /*.sh RUN rm -fr /etc/supervisor/conf.d/supervisord-mysqld.conf EXPOSE 80 CMD ["/run.sh"]
Just in case if you don’t know, the original docker image(from which I created the php-node one) uses ‘/app’ as the main root web directory for Apache.
Step 3:(Optional) Create Customize DockerFile/Image For Your MySQL Server:
This step is optional if you want to have some advanced customizations on your mysql service. I haven’t done anything regarding thus I didn’t need an additional Dockerfile for this.
In case you are doing this, just remember to have separate directory for a new Dockerfile.
Step 4: Create Shell Script That will run all initialization commands for you:
If you have some special instructions that you will might need to run after docker image created, but before you have that service running(such as apache web server), add them in a shell script.
The image I mentioned above has supervisor process monitoring service installed and applications(apache) runs through it so, only thing I needed, is to run the supervisord. I also needed to create database schema from Doctrine ORM CLI, thus my shell script look likes below:
cd /app && vendor/bin/doctrine orm:schema-tool:create exec supervisord -n
Step 5: Create Fig Configuration:
Now its time to create a fig configuration file. It includes the configuration for our complete application infrastructure, including how they are connected. So, in this case, it includes two docker container configuration, one for PHP application itself, another for MySQL database. It can also includes ports to be exposed(80 for Apache HTTP server) from a container.
Fig also suppose to facilitate mounting host volume inside containers so that on newer deployment it can get access to production data on host machine. But it didn’t work when I tried on my Mac OSX machine. Most probably because of this open issue for fig on github. Thus I had to follow alternative workarounds. But I will encourage you to try yourself too from ‘volumes’ section of fig yml official reference guide.
Here is my basic setup on fig.yml:
## YAML Template. web: build: . ports: - "80:80" links: - db db: image: mysql environment: MYSQL_DATABASE: dockerized-test MYSQL_ROOT_PASSWORD: root
Step 6: Start Up Your Dockerized PHP App!
Well, if you were able to have all the previous steps done properly without any error, now you should be able to run the both containers together in one single fig command:
That’s it! We have our first Dockerized PHP application running with a connection to database. Congratulations!!
I have kept my test version as a git repository, which you can use as ready made scripts.
Extending The Setup With More Containers:
As you are a cool developer, I am sure you are not developing only with a database, rather you might have some more attractive modern services like Redis, Rabbitmq/Gearman message queue, workers etc. Or even more then one PHP application, mix of PHP/Node/Python/Ruby services etc. You can now go forward with whatever quest you are up to from this simple two container setup to as many container setup you want. Just you should properly mention ‘link’ property to specify which container links to which.
tip 1- In production, its critical to mount the user data like uploaded images/files, logs and/or database volumes etc from host. You must not save them inside running container as docker containers are disposable! When there will be a new version of your application, you will be creating a new docker image/container with latest changes, thus loosing all previous db/user data!
tip 2- you don’t have to expose port externally always, only if you want it to be publicly available. Otherwise, internally linked containers can access each other on any port without any external help. For example, if you haven’t noticed, we haven’t expose any port for MySQL database container, because we don’t need to. With this feature of docker, you are even more protected now then ever before!
Well, as disclaimer, I will like to let you know that, I myself is a beginner to Docker and thus my setup shown above to Dockerize an application might not be the most optimized way to have staffs done. As I learn more, I will may be able to share more better ways in future(so, get in touch!). But, it should help you when you are just starting out with a simple setup that you might get excited to see working.
If you are having any trouble/issues with the setups mentioned in this tutorial, please feel free to contact me via commenting below. Happy Dockerizing 🙂 !