Now many developers use vagrant to manage their virtual machine development environment, vagrant is really cool, but there are a number of drawbacks (the main thing is that it takes up too much resources). After container technology, Docker, and more class Docker technologies emerge, it becomes easier to solve the problem.
Legal Disclaimer
Because of the way boot2docker works, the method described in this article may not function correctly in your environment. If you need to share folders to the Docker container in a non-Linux environment, you need to be aware of additional details. Later I will write an article specifically to introduce the actual problems encountered.
How to be a good development environment
First of all, we need to know what is a good development environment, for me, a good development environment needs to have the following characteristics:
- Free to use. I must be free to delete and create a new environment.
- Quick Start. I want to use it to work, it can be used immediately.
- Easy to update. Things are changing very quickly in our industry and it must make it easy for me to update my development environment to a new version of the software.
And Docker all support these characteristics, even more. You can almost instantly destroy and reconstruct the container, and the update environment only needs to reconstruct the image you are currently using.
What is the PHP development environment
Web applications are complex, PHP development environment needs a lot of things, in order to ensure the simplicity of the environment, need to do a variety of restrictions.
We use Nginx, PHP5-FPM, MySQL to run the Synmfony project.
Pet and cattle
The other point we want to discuss is whether we want to deploy the development environment in multiple containers or single containers. There are advantages to each of the two ways:
- Single containers are easy to distribute and maintain. Because they are independent, everything runs in the same container, which is like a virtual machine. But it also means that you need to rebuild the entire container when you want to upgrade something like PHP's new version.
- Multiple containers can provide better modularity when adding components. Because each container contains part of the stack: Web, PHP, MySQL, and so on, you can expand each service individually or add services, and you don't need to rebuild everything.
Because I'm lazy, plus I need to put something else in my notebook, so here we only describe the method of a single container.
initialization of the project
The first thing to do is to initialize a new Symfony project. The recommended method is to use the composer Create-project command. Composer could have been installed on the workstation, but that was too easy. This time we use it through Docker.
I sent an article about the Docker command earlier: Make Docker commands (well, I lied, I wrote it in this article, and thought it would be better to be independent).
Anyway, you can read it. Next, if you don't have the composer command, you can create a composer alias of your own.
$ alias composer= "Docker run-i-t-v \ $PWD:/srv ubermuda/composer"
Now you can initialize the Symfony project:
$ composer Create-project Symfony/framwork-standard-edition Someproject
It's awesome! Here's some real work.
Container
It is fairly easy to build a self-contained container that runs a standard Symfony project, simply by installing the common Nginx, PHP5-FPM, and Mysql-server, and then throwing in the prepared Nginx virtual host configuration file, Copy some of the configuration files and get in.
The source code of this container can be found in the Ubermuda/docker-symfony warehouse on GitHub. Dockerfile is the configuration file that Docker to use to build mirrors, let's look at:
From Debian:wheezy
ENV debian_frontend noninteractive
RUN apt-get update-y
RUN apt-get install-y nginx -FPM php5-mysqlnd php5-cli mysql-server supervisor
RUN sed-e ' s/;d aemonize = yes/daemonize/no/'-i/etc/php5/fpm/ph p-fpm.conf
run sed-e ' s/;listen\.owner/listen.owner/'-i/etc/php5/fpm/pool.d/www.conf
run sed-e ' s/;listen\ . group/listen.group/'-i/etc/php5/fpm/pool.d/www.conf
RUN echo ' \ndaemon off; ">>/etc/nginx/nginx.conf
ADD Vhost.conf/etc/nginx/sites-available/default
Add supervisor.conf/etc/supervisor/conf.d/supervisor.conf
Add init.sh/init.sh
expose 3306
VOLUME ["/srv"]
Workdir/srv
CMD ["/usr/bin/supervisord"]
We started by extending Debian:wheezy this base image and then configured Nginx and PHP5-FPM through a series of SED commands.
Copy Code code as follows:
RUN sed-e ' s/;d aemonize = yes/daemonize = no/'-i/etc/php5/fpm/php-fpm.conf
RUN sed-e ' s/;listen\.owner/listen.owner/'-i/etc/php5/fpm/pool.d/www.conf
RUN sed-e ' s/;listen\.group/listen.group/'-i/etc/php5/fpm/pool.d/www.conf
RUN echo "\ndaemon off;" >>/etc/nginx/nginx.conf
We're going to do two things here. First configure PHP5-FPM and Nginx to run them in the foreground so Supervisord can track them.
The PHP5-FPM is then configured to run Web-server with the specified user and to handle the file permissions.
Next you need to install a set of configuration files, first of all, the Nginx virtual host configuration file vhost.conf:
server {
listen;
server_name _;
Access_log/var/log/nginx/access.log;
Error_log/var/log/nginx/error.log;
Root/srv/web;
Index app_dev.php;
Location/{
try_files $uri $uri//app_dev.php $query _string;
}
Location ~ [^/]\.php (/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
Include Fastcgi_params;
}
}
Since we don't need a domain name, we set the server_name _ (a bit like Perl's $_ placeholder variable) and configure the root directory (document root) as/svr/web, and we'll deploy the application under/srv, and the rest is the standard Mginx + PHP5-FPM configuration.
Because a container can only run one program at a time, we need supervisord (or any other process manager, but I prefer supervisord). Luckily, this process manager will produce all the processes we need! Here is a small section of Supervisord configuration:
[Supervisord]
Nodaemon=true
[Program:nginx]
command=/usr/sbin/nginx
[program:php5-fpm]
command=/usr/sbin/ PHP5-FPM
[Program:mysql]
command=/usr/bin/mysqld_safe
[program:init]
command=/init.sh
Autorestart=false
redirect_stderr=true
redirect_stdout=/srv/app/logs/init.log
What we need to do here is define all the services, plus a special program:init process, which is not a real service, but an original way to run the startup script.
The problem with this startup script is that it usually needs to start some services first. For example, you might want to initialize some database tables, but only if you have to run MySQL first, one possible solution is to start MySQL in the startup script, then initialize the table, and then, to prevent the process from affecting the Supervisord, you need to stop MySQL and finally start Supervisord.
Such a script would look something like the following:
/etc/init.d/mysql start
app/console doctrine:schema:update--force
/etc/init.d/mysql stop
exec/usr/ Bin/supervisord
It looks ugly, there are wood, I change the way, let supervisor to run it and never reboot.
The actual init.sh script is as follows:
#!/bin/bash
ret=1 while
[[Ret-ne 0]]; does sleep
1;
Mysql-e ' exit ' >/dev/null 2>&1; Ret=$?
Done
Db_name=${db_name:-symfony}
mysqladmin-u root create $DB _name
if [-N "$INIT"]; then
/srv/$INIT C10/>fi
The script waits for MySQL to start, then creates DB based on the environment variable db_name, defaults to Symfony, finds the script to run in the INIT environment variable, and tries to run it. The end of this article shows how to use these environment variables.
Building and running mirrors
Everything is only due to the East wind. We also want to build symfony docker mirrors, using the Docker builds command:
$ CD docker-symfony
$ docker build-t symfony.
Now, you can use it to run your Symfony project:
$ CD someproject
$ docker run-i-t-p-v $PWD:/srv symfony
Let's take a look at the sequence of options:
- -I initiate interactive (interactive) mode, that is, STDIO (standard input and output) is connected to your current terminal. It is useful when you want to receive a log or send a signal to a process.
- -T creates a virtual TTY for the container, which is a good base friend with-I and is usually used together.
- -P tells the Docker daemon to publish all the specified ports, in this case, Port 80.
- -V $PWD:/srv mounts The current directory to the/srv directory of the container. Mount a directory to make the contents of the directory available to the target mount point.
Now you remember the db_name and INIT environment variables mentioned earlier, why use them: to customize your environment. Basically you can set the environment variable in the container by Docker Run's-e option, and the startup script gets the environment variable, so if your DB name is Some_project_dev, you can run the container like this:
$ docker run-i-t-p-v $PWD:/srv-e Db_name=some_project_dev symfony
The INIT environment variable is more powerful, allowing you to run the specified script at startup. For example, you have a Bin/setup script that runs the composer Install command and sets the database schema:
#!/bin/bash
Composer Install
app/console doctrine:schema:update--force
Run it with-e:
$ docker run-i-t-p \
-v $PWD:/srv \
e db_name=some_project_dev \
e init=bin/setup
Note that the-e option can be used more than once in Docer run and looks pretty cool. In addition, your startup script requires executable permissions (chmod +x).
Now we send the request through the curl to the container to check if everything is working as expected. First, we need to take the Docker map to the container's 80-port public port, using the Docker Port command:
$ docker Port $ (Docker PS-AQL 1)
0.0.0.0:49153
Docker PS-AQL 1 is a handy command to easily retrieve the ID of the last container, in our case, Docker maps the container's 80 ports to 49153 ports. Let's have a look curl.
$ Curl http://localhost:49153
You are are not allowed to access this file. Check app_dev.php for the more information.
When we do not access the dev controller from localhost (translator Note: container localhost), we get the default error message of Symfony, which is normal, because we are not sending the curl request from inside the container, so You can safely remove these rows from the front-end controller web/app_dev.php.
This check prevents access to the debug front controllers that are deployed by accident to production.
Feel free to remove this, extend it, or make something more sophisticated.
if (Isset ($_server[' http_client_ip ')) |
| isset ($_server[' http_x_forwarded_for '])
| |! ( In_array (@$_server[' remote_addr '), Array (' 127.0.0.1 ', ' fe80::1 ', ':: 1 ')) | | Php_sapi_name () = = = ' Cli-server ')
) {
header (' http/1.0 403 Forbidden ');
Exit (' are not allowed to access this file. Check '. basename (__file__). ' For more information ');
}
These lines prevent any access to the dev controller from anywhere outside localhost.
You can now work properly when you are curl, or visit http://localhost:49153/in a browser: