The author of this article is Geoffrey, a PHP web developer who loves DevOps and Docker. This article mainly describes how to use Docker to build PHP development environment, the author also discusses the construction of the Docker-based development environment should use a single container or a multi-container, each have what pros and cons. Recommended for PHP developers to read.
Many developers now 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 Docker technologies emerge, it's easier to solve this problem.
Disclaimer
Because of the way Boot2docker works, the methods described in this article may not work correctly in your environment. If you need to share folders to Docker containers in a non-Linux environment, you need to pay more attention to the 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. When I want to work with it, it can be used immediately.
Easy to update. Things are changing very quickly in our industry, and it must be easy for me to update my development environment to a new version of the software.
And Docker supports these features, and even more. You can destroy and rebuild containers almost instantly, while updating your environment requires only rebuilding the image you are currently using.
What is a PHP development environment
At present, the Web application is complex, the 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, and MySQL to run the Synmfony project this time. Because it is more complex to run the command line in a container, I'll put it in the next blog post.
Pet and cattle
Another point we're going to discuss is whether we want to deploy the development environment in a multi-container or a single container. Each of the two approaches has advantages:
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 when you want to upgrade something (like a new version of PHP), you need to rebuild the entire container.
Multiple containers can provide better modularity when adding components. Because each container contains a portion of the stack: Web, PHP, MySQL, and so on, you can extend 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'll just describe a single container method.
Initialize Project
The first thing to do is to initialize a new Symfony project. The recommended method is to use the composer Create-project command. You could have installed composer on your workstation, but that would be too easy. This time we use Docker for it.
I've previously sent an article about Docker commands: Make Docker commands (well, I lied, I wrote it in this article, and I thought it would be better to separate it out).
Anyway, you can read it. Next, if you don't have a composer command, you can create a composer alias that belongs to you.
$ 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
That's awesome! Here's some real work. (Omit the bloggers to entertain a bunch of balabla ....) Original: Awesome. Give yourself a high-five, get a cup of coffee or whatever is your liquid drug of choice, and get ready for the real work. )
Container
It's fairly easy to build a self-contained container that runs a standard Symfony project, just install the usual nginx, php5-fpm, and Mysql-server, and then throw the pre-prepared Nginx virtual host configuration file in, 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 to use for the Docker build image, let's take a look at:
From debian:wheezyenv debian_frontend noninteractiverun apt-get update-yrun apt-get install-y nginx php5-fpm php5-mysqln D php5-cli mysql-server supervisorrun sed-e ' s/;d aemonize = yes/daemonize = no/'-i/etc/php5/fpm/php-fpm.confrun sed-e ' s/;listen\.owner/listen.owner/'-i/etc/php5/fpm/pool.d/www.confrun sed-e ' s/;listen\.group/listen.group/'-i/etc/ Php5/fpm/pool.d/www.confrun echo "\ndaemon off;" >>/etc/nginx/nginx.confadd vhost.conf/etc/nginx/ Sites-available/defaultadd Supervisor.conf/etc/supervisor/conf.d/supervisor.confadd Init.sh/init.shexpose 80 3306VOLUME ["/srv"]workdir/srvcmd ["/usr/bin/supervisord"]
We started by extending the base image of Debian:wheezy and then configuring Nginx and PHP5-FPM through a series of SED commands.
RUN sed-e ' s/;d aemonize = yes/daemonize = no/'-i/etc/php5/fpm/php-fpm.confrun sed-e ' s/;listen\.owner/listen.owner/'- I/etc/php5/fpm/pool.d/www.confrun sed-e ' s/;listen\.group/listen.group/'-i/etc/php5/fpm/pool.d/www.confrun echo ' \ Ndaemon off; ">>/etc/nginx/nginx.conf
We're going to do two things here. First configure PHP5-FPM and nginx to let them run in the foreground so that Supervisord can track them.
Then, configure PHP5-FPM to run Web-server with the specified user and to handle the file permissions.
Next you need to install a set of configuration files, the first is Nginx virtual host profile 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; }}
Because we don't need a domain name, so we set server_name to _ (a bit like Perl's $_ placeholder variable), and configure the root directory (document root) to/svr/web, we will deploy the application under/srv, 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). Fortunately, this process manager will produce all the processes we need! Here is a small section of the 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.shautorestart=falseredirect_stderr= Trueredirect_stdout=/srv/app/logs/init.log
What we need to do here is to define all the services, plus a special program:init process, which is not an actual service, but an ingenious 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 if you have to run MySQL first, one possible workaround is to start MySQL in the startup script, initialize the table, and then, to prevent the process management from affecting the Supervisord, you need to stop MySQL, Finally, start Supervisord again.
Such a script looks like this:
/etc/init.d/mysql startapp/console doctrine:schema:update--force/etc/init.d/mysql stopexec/usr/bin/ Supervisord looks ugly. There is wood there, let me change the way, so that supervisor to run it and never restart. The actual init.sh script is as follows: #!/bin/bashret=1while [[Ret-ne 0]]; do sleep 1; Mysql-e ' exit ' >/dev/null 2>&1; Ret=$?donedb_name=${db_name:-symfony}mysqladmin-u Root Create $DB _nameif [-N "$INIT"]; Then /srv/$INITfi
The script waits for MySQL to start, then creates the DB based on the environment variable db_name, defaults to Symfony, and then finds the script to run in the INIT environment variable and tries to run it. The end of this article explains how to use these environment variables.
Build and run the image
Everything is ready only to the east wind. We'll also build the Symfony Docker image, using the Docker build 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 series of options for what they do:
-I initiates the interactive (interactive) mode, which means that 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 used in conjunction with-I as a good base friend.
-P tells the Docker daemon to publish all specified ports, in this case port 80.
-V $PWD:/srv mounts The current directory to the/srv directory of the container. Mount a directory so that directory content is available to the destination mount point.
Now you remember the db_name and INIT environment variables that you mentioned earlier, what do you do with it: to customize your environment. Basically you can set the environment variable in the container via the Docker Run's-e option, and the startup script will get 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 even more powerful, allowing you to run the specified script when you start it. For example, you have a Bin/setup script that runs the composer Install command and sets the database schema:
#!/bin/bashcomposer installapp/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 multiple times in docer run and looks pretty cool. In addition, your startup script requires executable permissions (chmod +x).
Now we send the request to the container via curl to check if everything works as expected. First, we need to take the public port that Docker maps to port 80 of the container, using the Docker Port command:
$ docker Port $ (Docker PS-AQL 1) 800.0.0.0:49153
Docker PS-AQL 1 is a handy command to easily retrieve the ID of the last container, and in our case Docker maps the container's 80 port to port 49153. Let's curl up and see.
$ Curl Http://localhost:49153You is not allowed to access the this file. Check app_dev.php for more information.
When we do not access the dev controller from localhost (translator Note: container localhost), we get the default error message for Symfony, which is normal because we are not sending a curl request from inside the container, so You can safely remove these rows from the front-end controller web/app_dev.php.
When we do not access the dev controller from localhost (translator Note: container localhost), we get the default error message for Symfony, which is normal because we are not sending a 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 debug front controllers that is deployed by accident to production servers.//feel free To remove this, extend it, or make something more Sophisticated.if (isset ($_server[' http_client_ip ')) | | isset ($_SERV er[' 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 (' is not a allowed to access the this file. Check '. basename (__file__). ' For more information. ');}
These lines prevent any access to the dev controller from anywhere other than localhost.
Now you can work properly with curl, or access http://localhost:49153/with your browser:
It's easy! Now we can start, update the environment quickly, but there are still a lot of places to improve.