Etiqueta: Virtualización

  • Docker o VM ¿Cuál es mejor para PHP?

    Estás montando tu entorno de trabajo local para trabajar en un proyecto PHP.

    Hay muchas cosas por definir pero una es segura: no vas a usar XAMPP ni nada parecido… después del último fiasco fue suficiente, hay que virtualizar sí o sí.

    Y la pregunta es: ¿voy con Docker o con una VM?

    Creeme que te entiendo, estuve mucho tiempo en tus zapatos.

    Hoy por hoy mi elección es clara: Docker. Pero no te pido que me creas así sin más, dejame darte una breve explicación para que saques tu conclusión.

    En qué se parecen y en qué se diferencian Docker y las Máquinas Virtuales

    Docker es un sistema de administración de contenedores. Sin entrar en mucho detalle, diré que es un método para crear y administrar entornos de ejecución aislados del resto.

    Las máquinas virtuales son sistemas de simulación de hardware.

    ¿Entonces…?

    Las máquinas virtuales son, en cierta medida, más poderosas ya que permiten crear un entorno de computación «completamente» separado del host, donde puede instalarse, por ejemplo, un sistema operativo diferente de aquel del host.

    Docker (o algún otro sistema de contenedores) en cambio no virtualiza hardware, si no software. Esto significa que sus posibilidades son algo más limitadas (Por ejemplo, montar un contenedor Linux en un host Windows es algo que, en principio no es posible).

    Hasta aquí la balanza parece inclinarse hacia las VMs, ¿no? Entonces… ¿por qué deberías preferir Docker?

    Los contenedores Docker son mucho más livianos que las VMs. Esto implica que:

    • Se inician/frenan mucho más rápido
    • Ocupan mucho menos espacio en el disco
    • Consumen muchos menos recursos del host

    Todo esto hace que sea mucho más viable tener un gran número de contenedores Docker ejecutando en un mismo host, a diferencia de lo que ocurre con las máquinas virtuales.

    Dejando estos detalles de implementación de lado, en la práctica las diferencias son mínimas. Cambia un poco la forma de configurar el entorno pero ambos se prestan muy bien para la automatización.

    Tanto Docker como los sistemas de virtualización cuentan con interfaces de línea de comandos muy potentes y, sobre esas herramientas básicas se montan algunas más avanzadas (Vagrant para el caso de VMs y docker-compose para Docker) o incluso algunas con interface gráfica, lo cual facilita mucho su uso.

    De modo que, siendo que el costo de implementar una o la otra es prácticamente igual, ¿por qué ir con la que más recursos consume?

    Por supuesto que la realidad es más compleja, hay que analizar el caso por caso pero, en líneas generales, mi recomendación es ir con Docker.

    Cómo arrancar con Docker

    Bueno, este tema excede un poco el objetivo de este post, pero voy a intentar hacer un breve resúmen y dejarte algunas referencias que puedan ayudarte:

    1. Instalar Docker
    2. Buscar una imagen que puedas usar como prueba
    3. Crear un contenedor
    4. Correr el «Hola Mundo!» 🙂

    Esto es una visión muy reducida. Si querés más detalles te invito a leer este post.

    Y si ya diste tus primeros pasos pero tenés dudas, te recomiendo mirar la Guía Práctica de Dockerización de PHP.

  • Cómo usar el intérprete de PHP de una VM desde PhpStorm

    Cómo usar el intérprete de PHP de una VM desde PhpStorm

    Una práctica muy buena a la hora de programar (y ahorrarse dolores de cabeza) es el uso de máquinas virtuales.

    Muchos desarrolladores usan este esquema, sin embargo, también es bastante común que tengan sus propias versiones del software usado para ejecutar sus aplicaciones instalado en su máquina física (por ejemplo PHP).

    Si estás trabajando en varios proyectos a la vez, los cuales están deployados en servidores diversos, es muy probable que en cada VM tengas instalada una versión diferente de PHP (La misma que tenés en el servidor de producción), con lo cual, al menos una VM debe tener instalada una versión diferente de PHP que la que tenés instalada en tu máquina.

    Como usás una VM no deberías tener mayores dificultades a la hora de deployar, pero a la hora de desarrollar, también sería bueno que tu IDE estuviese ejecutando la misma versión de PHP que va a dar vida a tu código…

    Una característica muy interesante de mi IDE preferido (PhpStorm) es la posibilidad de analizar el código (mientras lo escribo) usando el intérprete de PHP que está en la VM (y no el que tiene el propio IDE o el que tengo instalado en mi computadora).

    Para configurarlo tenés que ir a la pantalla de settings:

    Y configurar el intérprete de línea de comandos (CLI Interpreter):

    Por defecto vas a ver a la izquierda los intérpretes que tengas instalados en tu computadora, pero podés agregar uno:

    Que puede ser accedido remotamente (Por ejemplo a través de Vagrant).

    Obviamente, para que esto funcione la VM de la que querés sacar el intérprete tiene que estar encendida…

    Y listo, con esto te asegurás de tener un IDE que entienda exactamente la versión de PHP que vas a usar en tu entorno de desarrollo y en producción.

    Si querés ver más detalles podés consultar acá.

  • Por qué usar una máquina virtual para proyectos PHP

    Por qué usar una máquina virtual para proyectos PHP

    Respuesta rápida: para evitar sorpresas desagradables.

    Paso a detallar un poco de qué estoy hablando con un par de historias que me pasaron hace unos cuantos años durante el desarrollo de un proyecto personal.

    Como la mayoría de los desarrolladores que están empezando, tenía instalado en mi computadora (Windows en aquel entonces) el paquete XAMPP y mi forma de trabajo era programar y probar en mi propia computadora y luego, mediante FTP, subir mis cambios al hosting que había contratado con la empresa de un amigo.

    Un día una de las últimas actualizaciones que había realizado a mi código funcionaba perfectamente en mi casa, pero al subirla a mi hosting las cosas eran un poco diferentes (léase: no andaba nada :p).

    Hago la historia corta: yo había escrito los nombres de las tablas de mis consultas en mayúsculas (algo como SELECT * FROM USERS), cuando los nombres en la base estaban en minúsculas. Claro, mientras estaba en un entorno incapaz de distinguir mayúsculas de minúsculas (Windows) no había problema, pero al deployar sobre un BSD que sí lo era… aparecieron los problemas.

    Tardé unos cuantos días en darme cuenta del problema (Y unas cuantas veces pensé en cambiar de profesión).

    Otro problema similar que tuve fue todavía más difícil de encontrar: mi versión de MySQL era un poquitín superior a la que estaba usando el hosting (Con lo cual, una función de la que mi código dependía simplemente no estaba disponible en Producción).

    Conclusión:

    El modo más seguro de evitar que aparezcan sorpresas al llevar tu código a Producción (O a cualquier otro ambiente para el caso), es programar en un entorno lo más parecido posible al productivo.

    Hay dos opciones básicas para lograr esto:

    1. Tener tu propio servidor que puedas configurar de acuerdo a las necesidades de tu aplicación (Probablemente un VPS)
    2. Configurar tu entorno de desarrollo de acuerdo a las capacidades que te brindará el servidor donde vayas a montar tu aplicación

    Independientemente de cuál sea tu elección, tener una máquina virtual específica para tu proyecto es la forma más práctica de generar código que corra sin problemas en el ambiente productivo.

    Así que en definitiva, la pregunta sería «¿Por qué NO usar una máquina virtual para proyectos PHP?»

  • Una máquina virtual lista para PHP+Symfony2+XDebug

    Charlando con algunos amigos desarrolladores php surgió un tema que les estaba resultando complicado, así que decidí poner mi pequeño granito de arena (para ellos y para otros que tal vez estén pasando por lo mismo).

    Ya habíamos hablado del por qué usar una máquina virtual para proyectos PHP. Ya estaba claro que usar un framework es más conveniente que no usarlo (independientemente de cuál fuera) y les había comentado sobre mis herramientas favoritas de automatización (Ansible y Vagrant).

    Todos estábamos de acuerdo «en la teoría», pero a la hora de pasar a la práctica se veían algo frustrados por no poder lograr tener una máquina virtual que fuese fácil de usar y que soportara, entre otras cosas, el uso de XDebug.

    Lo que te voy a mostrar a continuación son los archivos de configuración que yo usé en el último proyecto que hice en php:

    El archivo playbook.yml:

    ---
    - hosts: all
     sudo: true
     tasks:
    
    - name: create /var/www
     file: path=/var/www state=directory
    
    - name: create site symlink
     file: src=/vagrant dest=/var/www/site state=link
     notify: restart apache
    
    - name: install misc packages
     apt: name={{ item }} state=latest update_cache=true
     with_items:
     - ruby2.0
     - ruby2.0-dev
     - git
     - curl
     - unzip
     - vim
    
    - name: Symlink exists for Ruby 2.0
     file: src=/usr/bin/ruby2.0 dest=/usr/local/bin/ruby state=link
    
    - name: Symlink exists for Ruby Gems 2.0
     file: src=/usr/bin/gem2.0 dest=/usr/local/bin/gem state=link
    
    - name: install language packs for locale support
     apt: name={{ item }} state=latest
     with_items:
     - language-pack-de-base
     - language-pack-es-base
    
    # Apache2
    
    - name: ensure apache is installed
     apt: name=apache2 state=present
    
    - name: make sure apache is running
     action: service name=apache2 state=started enabled=true
    
    - file: src=/etc/apache2/mods-available/rewrite.load dest=/etc/apache2/mods-enabled/rewrite.load state=link
     notify: restart apache
    
    - file: src=/etc/apache2/mods-available/headers.load dest=/etc/apache2/mods-enabled/headers.load state=link
     notify: restart apache
    
    - copy: src=/vagrant/ansible/templates/site.conf dest=/etc/apache2/sites-available/site.conf remote_src=true
     notify: restart apache
    
    - file: src=/etc/apache2/sites-available/site.conf dest=/etc/apache2/sites-enabled/site.conf state=link
     notify: restart apache
    
    - file: path=/etc/apache2/sites-enabled/000-default.conf state=absent
     notify: restart apache
    
    - file: path=/etc/apache2/conf.d state=directory
    
    - copy: src=/vagrant/ansible/templates/fqdn.conf dest=/etc/apache2/conf.d/fqdn.conf remote_src=true
     notify: restart apache
    
    - copy: src=/vagrant/ansible/templates/nosendfile.conf dest=/etc/apache2/conf.d/nosendfile.conf remote_src=true
     notify: restart apache
    
    # MySQL
    
    - name: install MySQL
     apt: name={{ item }} state=latest
     with_items:
     - mysql-server
     - mysql-client
     - python-mysqldb
    
    - name: add mysql user
     mysql_user: name=vagrant
     host={{ item }}
     password=vagrant priv=*.*:ALL,GRANT
     login_user=root
     login_password=
     with_items:
     - '%'
     - localhost
    
    - name: create mysql databases
     mysql_db: name={{ item }}
     state=present
     with_items:
     - site_development
     - site_development_stats
     - site_testing
     - site_testing_stats
    
    - file: path=/etc/mysql/conf.d state=directory
     - name: Set MySQL number of connections
     copy: src=/vagrant/ansible/templates/max_connections.cnf dest=/etc/mysql/conf.d/max_connections.cnf remote_src=true
     notify: restart mysql
    
    - name: Install mysql command line client configuration file
     copy: src=/vagrant/ansible/templates/my.cnf dest=/home/vagrant/.my.cnf owner=vagrant group=vagrant remote_src=true
    
    # PHP
    
    - name: add php5.6 ppa
     apt_repository: repo='ppa:ondrej/php'
    
    - name: install PHP5.6 packages
     apt: name={{ item }} state=latest
     with_items:
     - php5.6
     - libapache2-mod-php5.6
     - php5.6-cli
     - php5.6-dev
     - php5.6-mysql
     - php-pear
     - php5.6-mcrypt
     - php5.6-gd
     - php5.6-curl
     - php5.6-xdebug
     - php5.6-memcache
     - php5.6-memcached
     - php5.6-readline
     - php5.6-xml
     - php5.6-mbstring
     - php5.6-zip
    
    - file: path=/etc/php5.6/conf.d state=directory
     - file: path=/etc/php5.6/cli/conf.d state=directory
     - file: path=/etc/php5.6/apache2/conf.d state=directory
    
    - copy: src=/vagrant/ansible/templates/php-site.ini dest=/etc/php5.6/conf.d/php-site.ini remote_src=true
     notify: restart apache
    
    - name: configure xdebug
     copy: src=templates/xdebug.ini dest=/etc/php/5.6/mods-available/xdebug.ini
     notify: restart apache
    
    - name: symlink common php configuration for cli handler
     file: src=/etc/php5.6/conf.d/php-site.ini dest=/etc/php5.6/cli/conf.d/php-site.ini state=link
     notify: restart apache
    
    - name: symlink common php configuration for apache2 handler
     file: src=/etc/php5.6/conf.d/php-site.ini dest=/etc/php5.6/apache2/conf.d/php-site.ini state=link
     notify: restart apache
    
    # phpmyadmin
    
    - name: install phpmyadmin
     apt: name=phpmyadmin state=latest
    
    # Assets compilation
    
    - name: add nodejs ppa
     apt_repository: repo='ppa:chris-lea/node.js'
    
    - name: install nodejs
     apt: name=nodejs state=latest
    
    # Set up site
    
    - file: src=/vagrant dest=/var/www/site state=link
     - file: path={{ item }} owner=vagrant group=vagrant mode=0777 state=directory
     with_items:
     - /var/cache/site
     - /var/cache/site/cache
     - /var/cache/site/clockwork
     - /var/cache/site/logs
     - /var/cache/site/meta
     - /var/cache/site/sessions
     - /var/cache/site/views
    
    - name: ensure once more that 000-default.conf is deleted
     file: path=/etc/apache2/sites-enabled/000-default.conf state=absent
     notify: restart apache
    
    - name: ensure that phpmyadmin's stock config is deleted
     file: path=/etc/apache2/conf.d/phpmyadmin.conf state=absent
    
    - name: set proper permissions for app/reports directory
     file: path=/vagrant/app/reports group=www-data owner=vagrant mode=0775 state=directory
    
    # Common stuff
    
    - name: Install Composer
     shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer creates=/usr/local/bin/composer
    
    handlers:
     - name: restart apache
     action: service name=apache2 state=restarted
     - name: restart mysql
     action: service name=mysql state=restarted
     - name: restart beanstalkd
     action: service name=beanstalkd state=restarted

    Los archivos templates (deben estar en el directorio ansible/templates dentro de tu proyecto):

    site.conf:

    <VirtualHost *:80>
     ServerName myApp
     DocumentRoot /var/www/site/web
    
    <Directory />
     Options FollowSymLinks
     AllowOverride None
     </Directory>
    
    <Directory /var/www/site/web>
     DirectoryIndex app_dev.php
     Options Indexes FollowSymLinks MultiViews
     AllowOverride All
     Order allow,deny
     allow from all
     </Directory>
    
    ErrorLog /var/log/apache2/error.log
     LogLevel warn
     CustomLog /var/log/apache2/access.log combined
    
    ## enable phpmyadmin
    
    Alias /phpmyadmin /usr/share/phpmyadmin
    
    <Directory /usr/share/phpmyadmin>
     Options FollowSymLinks
     DirectoryIndex index.php
    
    <IfModule mod_php5.c>
     AddType application/x-httpd-php .php
    
    php_flag magic_quotes_gpc Off
     php_flag track_vars On
     php_flag register_globals Off
     php_admin_flag allow_url_fopen Off
     php_value include_path .
     php_admin_value upload_tmp_dir /var/lib/phpmyadmin/tmp
     php_admin_value open_basedir /usr/share/phpmyadmin/:/etc/phpmyadmin/:/var/lib/phpmyadmin/
     </IfModule>
    
    </Directory>
    </VirtualHost>

    fqdn.conf

    ServerName Localhost

    nosendfile.conf

    EnableSendfile off

    max_connections.cnf

    [mysqld]
    max_connections = 400

    my.cnf

    [client]
    user=vagrant
    password=vagrant

    php-site.ini

    max_execution_time = 30000
    memory_limit = 512M
    display_errors = On
    disable_functions =

    xdebug.ini

    zend_extension=xdebug.so
    xdebug.remote_enable=On
    xdebug.remote_connect_back=On

    Para iniciar el proyecto, basta con copiar y pegar esto en un archivo llamado Vagrantfile (en la raíz de tu proyecto):

    # -*- mode: ruby -*-
     # vi: set ft=ruby :
    
    # All Vagrant configuration is done below. The "2" in Vagrant.configure
     # configures the configuration version (we support older styles for
     # backwards compatibility). Please don't change it unless you know what
     # you're doing.
     Vagrant.configure("2") do |config|
     # The most common configuration options are documented and commented below.
     # For a complete reference, please see the online documentation at
     # https://docs.vagrantup.com.
    
    # Every Vagrant development environment requires a box. You can search for
     # boxes at https://atlas.hashicorp.com/search.
     config.vm.box = "ubuntu/trusty64"
    
    # Disable automatic box update checking. If you disable this, then
     # boxes will only be checked for updates when the user runs
     # `vagrant box outdated`. This is not recommended.
     # config.vm.box_check_update = false
    
    # Create a forwarded port mapping which allows access to a specific port
     # within the machine from a port on the host machine. In the example below,
     # accessing "localhost:8080" will access port 80 on the guest machine.
     config.vm.network "forwarded_port", guest: 80, host: 8080
    
    # Create a private network, which allows host-only access to the machine
     # using a specific IP.
     config.vm.network "private_network", ip: "192.168.33.10"
    
    # Create a public network, which generally matched to bridged network.
     # Bridged networks make the machine appear as another physical device on
     # your network.
     # config.vm.network "public_network"
    
    # Share an additional folder to the guest VM. The first argument is
     # the path on the host to the actual folder. The second argument is
     # the path on the guest to mount the folder. And the optional third
     # argument is a set of non-required options.
     # config.vm.synced_folder "../data", "/vagrant_data"
    
    # Provider-specific configuration so you can fine-tune various
    
    # backing providers for Vagrant. These expose provider-specific options.
     # Example for VirtualBox:
     #
     config.vm.provider "virtualbox" do |vb|
     # # Display the VirtualBox GUI when booting the machine
     # vb.gui = true
     #
     # # Customize the amount of memory on the VM:
     vb.memory = "2048"
     end
     #
     # View the documentation for the provider you are using for more
     # information on available options.
    
    # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
     # such as FTP and Heroku are also available. See the documentation at
     # https://docs.vagrantup.com/v2/push/atlas.html for more information.
     # config.push.define "atlas" do |push|
     # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
     # end
    
    # Enable provisioning with a shell script. Additional provisioners such as
     # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
     # documentation for more information about their specific syntax and use.
     # config.vm.provision "shell", inline: <<-SHELL
     # apt-get update
     # apt-get install -y apache2
     # SHELL
    
    #
     # Run Ansible from the Vagrant Host
     #
     config.vm.provision "ansible" do |ansible|
       ansible.playbook = "ansible/playbook.yml"
       ansible.verbose = "vv"
     end
    
    # Symfony needs to be able to write to it's cache, logs and sessions directory in var/
     config.vm.synced_folder "./var", "/vagrant/var",
       :owner => 'vagrant',
       :group => 'www-data',
       :mount_options => ["dmode=777","fmode=777"]
     end

    y ejecutar el comando (Obviamente, habiendo instalado Vagrant previamente…):

     vagrant up

    Y comienza la magia 🙂

    Un ratito después (Dependiendo de la conexión que tengas) vas a tener una máquina lista para desarrollar tu aplicación basada en Symfony2.

    Y ahora… ¡a codear!

    ¿Me olvidé de algo? ¡Avisame en los comentarios!