📜 ⬆️ ⬇️

Laziness - the engine of progress or my option to create an environment for web development based on VirtualBox



All web developers somehow need some kind of server to develop their web applications. Someone uses "Denver" , someone OpenServer , more advanced take a virtual server (VPS), and more advanced use Vagrant , and someone just lazy. Under the cut, I'll tell you how I deploy a web application for development using VirtualBox, bash, and some crutches. For those who are lazy and do not want to look under the cat: one bash script is described that mounts shared folders in the guest OS and a half- daemon that runs the first script after launching before stopping the system and implements the daemon interface.

The CentOS 6.5 Linux distribution was chosen as the guest operating system, and Apache 2.2.15 was used as the web server.

At once I will make a reservation: the description of installation and the LAMP-server setup I will not describe for manov on the Internet it is full.
')
The very first version of the script looked like this:

#!/bin/sh mount -t vboxsf $1 $2 


Gradually, it expanded into the following script:
Script first - workhorse - /root/scripts/vbox-sf.sh
 #!/bin/sh # Author: Dmitry Vapelnik # Email: dvapelnik@gmail.com #  - logfile='/var/log/vbox-sf.log' # ,      mountPrefix='/var/www/html/'; # - hn='.'`hostname` #   ,    VirtualBox sharedFolders=`df | egrep "\/media\/sf_\$2[^ ]*" -o | sed -e 's/\/media\/sf_//'` #================ LOG ==========================================================# function log { echo [`date +"%F %T"`] $1 $2 >> $logfile } #================ MOUNTING =====================================================# function mountFn { echo "Mounting...."; for f in $sharedFolders; do mountPath=$mountPrefix$f$hn if cat /proc/mounts | grep vbox | grep $mountPath &> /dev/null; then echo Already mounted. Continue..; else rm -rf $mountPath 2> /dev/null; mkdir -p $mountPath; chown apache:apache $mountPath; if mount -t vboxsf $f $mountPath -o umask=0022,uid=apache,gid=apache; then echo Mounted $f log mounted $mountPath #        #   XDebug mkdir -p /var/www/html/$f$hn/xd_profile_$f$hn mkdir -p /var/www/html/$f$hn/xd_trace_$f$hn if [ -f $mountPath'/httpd.conf' ]; then #   httpd     cat $mountPath'/httpd.conf' | sed -e "s/<%domain%>/$f$hn/g" > /etc/httpd/conf/sf/$f$hn.conf if [ -f $mountPath'/aftermount.sh' ]; then #    ,   #      bash $mountPath'/aftermount.sh' $mountPath; fi fi fi fi done; #  - service httpd restart } #================ UNMOUNTING ===================================================# function umountFn { echo "Unmounting..." #  - service httpd stop for f in $sharedFolders; do mountPath=$mountPrefix$f$hn #   - find $mountPath -type f -name httpd_"$f""$hn"_*.log -exec rm -f {} \; #      rm -rf $mountPath/xd_profile_"$f""$hn" rm -rf $mountPath/xd_trace_"$f""$hn" #    ,   #      if [ -f $mountPath'/beforeumount.sh' ]; then bash $mountPath'/beforeumount.sh' $mountPath; fi #   umount $mountPath #    if [[ $? -eq 0 ]]; then rm -rf $mountPath 2> /dev/null rm -f /etc/httpd/conf/sf/$f$hn.conf 2> /dev/null echo "Unmounted and removed $f" log umounted $mountPath else echo "Not unmounted" fi done; #  - service httpd start } #================ STATUS =======================================================# function statusFn { com=0 for f in $sharedFolders; do mountPath=$mountPrefix$f$hn if df | grep $mountPath &> /dev/null; then com=`expr $com + 1`; if [ $com -eq 1 ]; then echo List of mounted resources: fi df | grep $mountPath | egrep -o '\/.+$' fi done if [ $com -eq 0 ]; then echo No shared storage mounted fi } #===============================================================================# if [ "$1" == "mount" ]; then mountFn; exit 0 elif [ "$1" == "umount" ]; then umountFn; exit 0 elif [ "$1" == "status" ]; then statusFn; exit 0 else cat << EOF No arguments supplied ----------------------------------------------------------------------------- Usage: ----------------------------------------------------------------------------- Using with single argument one of: mount : for mounting all shared folders under /var/www/html directory umount : for unmounting all shared folders under /var/www/html directory status : for checking mount status ----------------------------------------------------------------------------- Using with two argument for mounting or unmounting single folder Example: vbox-sf mount foo : will mount shared folder /media/sf_foo into /var/www/html/foo.domain.com vbox-sf umount foo : will umount shared folder /media/sf_foo from /var/www/html/foo.domain.com EOF exit 1 fi exit 0 


What is needed and how it works


  1. It is necessary to install a web server on the guest machine (I chose apache).
  2. To correctly mount shared folders on the guest OS, you must install Guest Additions .
  3. The folder that we mount should be named briefly, but uniquely - this name will be a subdomain of the domain of our server.
  4. You must put the virtual host config pattern for the web server in the root of the shared folder.
    Approximate virtual host configuration for web server
     <VirtualHost *:80> DocumentRoot /var/www/html/<%domain%> ServerName <%domain%> ServerAlias www.<%domain%> DirectoryIndex index.php <Directory /var/www/html/<%domain%>> AllowOverride All php_admin_value open_basedir /var/www/html/<%domain%>:/tmp:/usr/share:/var/lib </Directory> CustomLog /var/www/html/<%domain%>/httpd_<%domain%>_access.log combined ErrorLog /var/www/html/<%domain%>/httpd_<%domain%>_error.log php_admin_value xdebug.profiler_output_dir /var/www/html/<%domain%>/xd_profile_<%domain%> php_admin_value xdebug.trace_output_dir /var/www/html/<%domain%>/xd_trace_<%domain%> </VirtualHost> 

    The config itself can be changed, but the main points should be kept: <% domain%> - the mask that is replaced with the domain for the web application, log files and folders for profiling and traces (if necessary). Everything else is to taste depending on what is needed in the application.


  5. Folders need to be shared with the automount and preferably with the right to write to it - we can upload something through our application and therefore the files should be saved. And someone can something and caches files. The right to write does not hurt. Auto-mount is important - the script selects the necessary folders from the list of already mounted folders in / media / sf_ * - that is why folders should be mounted with auto-mount. It should look something like the screenshot.
  6. SELinux must be disabled on the guest machine. With SELinux enabled, the web server doesn’t see the submenus in / var / www / html / folders - they are mounted in the context of the vbox, not httpd. I haven’t found how to fix it yet and therefore I put a crutch on it - I disabled SELinux.
  7. The hostname of the guest machine must have a domain view. I have natty.nat and as a result, all the web applications that I launch into this guest machine will look like this: [name of the folder we share]. [Hostname]. for example test.natty.nat . Personally, it was convenient for me to hang on such domains.
  8. You must create a folder / etc / httpd / conf / sf ,
     # mkdir -p /etc/httpd/conf/sf 
    in which configs of virtual hosts of the web server will be stored. At the same time, at the end of the /etc/httpd/conf/httpd.conf file, you need to include all configs that we will store in the above folder:
     include "conf/sf/*.conf" 


If everything is good, then now we can share the folder with our project on our virtual machine, run it and, having executed the following command, get a ready place for development:

 # /path/to/vbox-sf.sh mount 


So we podmauntim all shared folders. If the second argument is the name of our folder, it will only mount or dismount it:

 # /path/to/vbox-sf.sh mount test 

 # /path/to/vbox-sf.sh umount test 


Demon


But now it would be nice to automate this business somehow. I chose the path of the daemon and I believe that it will be more correct than to change /etc/rc.# files by routine. The following script was written for /etc/init.d
The second script - the demon - /etc/init.d/vboxsf
 #!/bin/bash # # Author: Dmitry Vapelnik # Email: dvapelnik@gmail.com ### BEGIN INIT INFO # Required-Start: httpd mysqld vboxadd-service vboxadd # Required-Stop: httpd mysqld vboxadd-service vboxadd # Default-Start: 3 # Default-Stop: 0 6 # Short-Description: Mounting VirtualBox shared folders # Description: This file should be used to mount and umount # VirtualBox shared folders ### END INIT INFO # Get function from functions library . /etc/init.d/functions prog="VBoxSF" lockfile='/var/lock/subsys/vboxsf' # Start the service vbox-sf start() { # initlog -c "echo -n Starting $prog server: " /root/bin/vbox-sf mount &> /dev/null && touch $lockfile success $"$prog: mounted" echo } # Restart the service vbox-sf stop() { # initlog -c "echo -n Umounting $prog: " /root/bin/vbox-sf umount &> /dev/null && rm -f $lockfile success $"$prog: umounted" echo } status() { /root/bin/vbox-sf status } ### main logic ### case "$1" in start) start ;; stop) stop ;; status) status ;; restart|reload|condrestart) stop start ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0 


This script should be put in the /etc/init.d directory and name the file vboxsf . In fact, the name of the file is not critical here. Just when we add a new daemon using chkconfig , we will need to specify the name of this file. Further I will use vboxsf .

So we added the file. Now we need to link our script to / root / bin :

 # mkdir -p /root/bin # ln -s /root/scripts/vbox-sf.sh /root/bin/vbox-sf 


Add to chkconfig :

 # chkconfig --add vboxsf 


Check if everything is added:

 # chkconfig | grep vboxsf 


If everything is good, we should be shown at what levels our script will run.



As a result, we can now simply use the following commands:

 # service vboxsf start # service vboxsf stop # service vboxsf restart # service vboxsf status 


If not, then we see what we did wrong. In principle, at this point everything should work: be mounted at startup and dismantled before shutting down.

And now yummy


Remember the scripts that run after mounting and before unmounting a folder? So, for more convenient work, I also expand the database from the dump to the MySQL server and before the dismount happens, I merge the database back into the dump. Thus, we have a current database dump after we turn off the machine and we have an actual database after we turn on the machine.

These scripts are:
The third script - restore the database from the dump - aftermount.sh
 #!/bin/sh # Author: Dmitry Vapelnik # Email: dvapelnik@gmail.com if [ $# -eq 0 ]; then echo 'No arguments supplied'; echo 'Exit'; exit 0; fi ###################################################### dbAdminUser='ourDbAdminLogin' dbAdminPass='ourDbAdminPassword' dbName='ourDbName' dbUser='ourDbUser' dbPass='ourDbPassword' dbHost='localhost' dbDump=$1'/db.sql' ###################################################### queryCreateUser="CREATE USER '$dbUser'@'$dbHost' IDENTIFIED BY '$dbPass'; CREATE DATABASE IF NOT EXISTS \`$dbName\`; GRANT ALL PRIVILEGES ON \`$dbName\`.* TO '$dbUser'@'$dbHost'; FLUSH PRIVILEGES;" echo Creating new user... if mysql -u$dbAdminUser -p$dbAdminPass -e "$queryCreateUser"; then echo User added echo Using MySQL dump if mysql -u$dbAdminUser -p$dbAdminPass $dbName < $dbDump; then echo MySQL dump loaded into $dbName else echo MySQL dump not loaded fi else echo Error: user not added exit fi 


Important! Achtung! dbAdminUser and dbAdminPass is the login and password of the database administrator who can create / delete a user, create a database and upload a dump into it.
ourDbName, ourDbUser and ourDbPassword are the database name and the username and password of the MySQL user that is used in our mountable web application being developed.

What this script does:
  1. Creates a user;
  2. Creates a DB;
  3. Fills the contents of our dump into this database.


What is necessary:

  1. So that the DBA could add a user, create a database and upload our dump into it (I use MySQL root user and some short password - no one will break my VirtualBox server);
  2. The dump of our database should be in the root of the shared folder in the db.sql file. We can call it differently and put it in another place, but then it will be necessary to correct it and the script is also not a problem if desired;
  3. To maintain cleanliness, neither the created user, nor the database should be available before the deployment of our entire business.


The second script simply saves the contents of our database to a file, cleans up the user and deletes the database.
The fourth script - saving the database to dump - beforeumount.sh
 #!/bin/sh # Author: Dmitry Vapelnik # Email: dvapelnik@gmail.com if [ $# -eq 0 ]; then echo 'No arguments supplied'; echo 'Exit'; exit 0; fi ###################################################### dbAdminUser='dbAdminUser' dbAdminPass='dbAdminPass' dbName='ourDbName' dbUser='ourDbUser' dbHost='localhost' dbDump=$1'/db.sql' ###################################################### queryRemove="DROP DATABASE \`$dbName\`; DROP USER '$dbUser'@'$dbHost'; FLUSH PRIVILEGES;" echo Dumping MySQL DB into file.. if mysqldump -u$dbAdminUser -p$dbAdminPass $dbName > $dbDump; then echo DB dumped into file echo Removing user and database if mysql -u$dbAdminUser -p$dbAdminPass -e "$queryRemove"; then echo User and DB was removed else echo Error on removing fi fi 

Settings as in the previous script. At this stage, everything should be transparent.

After all these manipulations, we get the following: we have a virtual server running under VirtualBox; after we turn it on, it automatically mounts our folders, creates a user, a database in MySQL, uploads our previously prepared dump into it - you can work - the logs are actually on our machine, the profiling files and the traces are also on our machine ; When the work is completed, we turn off the server: the logs are deleted, the profiling files and the traces are deleted along with the folder, the contents of the database are dumped back to our file, the folders are unmounted and the machine is turned off. In fact, our server plays the role of some kind of surrogate or donor - nothing is completely preserved and lying on it, but it only reproduces what we are throwing at it.

A nice and sometimes useful bonus: we can configure several servers with different versions of the software and at the same time check how our web application works on each version of php, MySQL, Apache. Logs, traces and profiling files will be in separate files depending on the domain name, so as not to interfere with everything in a heap.

If necessary, you can hang other goodies and convenience, but this is to the taste and needs of each developer separately. All that is needed, we push into the scripts that are executed after mounting and before dismounting.

Yes, another important detail. In order not to register the entire zoo of subdomains that can be used for development, you need to install it on the server machine (on the machine on which we are developing and where VirtualBox is installed, I have a laptop) Dnsmasq is a lightweight DNS, DHCP, TFTP (BOOTP, PXE ) server. It is tuned to the utterly contrary. My cleaned config looks like this (yes, I have three machines: 5.3, 5.4, 5.5):

 $ cat /etc/dnsmasq.conf | egrep -v "(^#.*|^$)" listen-address=127.0.0.1 address=/natty.nat/192.168.191.160 address=/ketty.nat/192.168.191.161 address=/betsy.nat/192.168.191.162 


Thus, all subdomains of these domains will be sent to the specified IP addresses.

It may still be useful to someone a piece of /etc/php.ini , which is responsible for XDebug.
A piece from /etc/php.ini
 ;      -    XDebug zend_extension="xdebug.so" xdebug.cli_color=1 xdebug.remote_enable=true xdebug.remote_host="192.168.191.1" xdebug.remote_port=9000 xdebug.remote_handler="dbgp" xdebug.remote_connect_back=1 ; Profiler xdebug.profiler_enable = 0 xdebug.profiler_enable_trigger = 1 xdebug.profiler_append = 0 ; Trace options xdebug.collect_includes = 1 xdebug.collect_params = 4 xdebug.collect_vars = 0 xdebug.dump.REQUEST = * xdebug.dump.SESSION = * ;xdebug.dump.SERVER = REMOTE_ADDR,REQUEST_METHOD xdebug.dump.SERVER = * xdebug.dump_globals = 1 xdebug.dump_once = 1 xdebug.dump_undefined = 0 xdebug.show_mem_delta = Off ;xdebug.file_link_format = '' xdebug.manual_url = http://www.php.net xdebug.show_exception_trace = 1 xdebug.show_local_vars = 1 xdebug.show_mem_delta = 1 ; Traces xdebug.auto_trace = 0 xdebug.collect_assignments = 1 xdebug.collect_return = 1 xdebug.trace_enable_trigger = 1 ; 0 for parsing ; 1 human readable xdebug.trace_format = 1 xdebug.trace_options = 0 xdebug.trace_output_name = trace.%c 


If anyone does not know, there is a convenient extension for working with XDebug - The easiest Xdebug , which allows you to enable / disable the breakpoint, profiling, and tracing.

I created everything on the network 192.168.191.0/24 and then, my IP from this subnet. If you have another subnet, change as you like.

Source: https://habr.com/ru/post/236573/


All Articles