Tiny PHP Docker images

Docker has been around for a while now and I had my go at a few systems.
With some trial and error I worked up an intermediate knowledge of the inner workings of docker and how it feels to develop with it, from a developer and a devops perspective.

The DevOp in me wants to try out all the new tool, frameworks, scripts to automate as much as possible for my development, however the developer in me is very unhappy having to wait for hundreds of Megabytes (on larger projects it is in the GB range) of images to load and in the worst case I waited an hour for an image build to finish.

Hence I had to do something. I had to find a way to make those images smaller.
On one project we are using eZ Platform, which comes with a docker toolbox.

While developing with it, I don’t notice much, however when I send my project to be built on the CI server, I will always have to pull the base images provided which are quite large:

ezsystems/php 7.0-v1-dev 522.9 MB
ezsystems/php 7.0-v1 451.1 MB

This is a little bit of an overhead, so I searched in my scripts collection and put something together that will build me a stand alone php package, and called it packager which makes use of dockerize.

I can feed packager with a json file which describes which apt packages I want installed, which essential binaries I want and which applications I want to have packaged. Packager then uses an ubuntu:xenial image to execute those instructions, put all the files together and tars it out into the unix pipe.

Contents of package.json

{
"pre-commands": [],
"post-commands": [
"php -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\"",
"php composer-setup.php --install-dir=/tmp/build/usr/bin/ --filename=composer",
"php -r \"unlink('composer-setup.php');\""
],
"essentials": [
"env",
"bash",
"sh",
"dash"
],
"packages": [
"libcurl3",
"libedit-dev",
"libgd-dev",
"libmemcached-dev",
"libldb-dev",
"libxpm-dev",
"libxslt1-dev",
"libicu-dev",
"php",
"php-curl",
"php-fpm",
"php-gd",
"php-intl",
"php-memcached",
"php-mbstring",
"php-mysql",
"php-mysqlnd",
"php-pear",
"php-xml"
],
"bin": ["php", "php-fpm7.0"]
}
cat php.json \
| docker run \
--rm \
--name pckgr \
-ia stdin \
-a stdout \
-a stderr packager \
> build.tar

Then I define a Dockerfile for my image:

FROM busybox
ADD build.tar /

ENTRYPOINT ["php-fpm7.0", "-F"]

docker build -t local/php .

After I switched the PHP images the eZ application image was now only about 82MB small. The CI Server only needs to build the base image (which takes about 2 – 5 minutes to build) once a week and the application builds have cut down in time by about 6 minutes.
I have tried this with PHP and NGINX but failed to get erlang to run with it. Maybe some systems are not supposed to be run in isolation.
Check out my repository and feel free to contribute.

Advertisements

TwigSfHelper2ExtensionsBundle Release

It has been a while since I published some code online, however lately I had some time to look through my things and see what I can package as a plugin/bundle.

Since I have started development with Symfony2 rather than only Symfony 1.x I ran ahead and created my first bundle; the TwigSfHelper2ExtensionsBundle. Simply said this bundle is just a port of some of the Symfony 1.x text and date helpers into Twig filters.

Twig Filters included

  • distance_of_time_in_words: Displays two dates as “time ago”
  • time_ago_in_words: Same as distance_of_time_in_words but shows the difference of now
  • auto_link_text: Turns all urls and email addresses into clickable links. The +link+ parameter can limit what should be linked. Options are :all (default), :email_addresses, and :urls.
  • excerpt_text: Extracts an excerpt from the +text+ surrounding the +phrase+ with a number of characters on each side determined by +radius+. If the phrase isn’t found, nil is returned. Ex: “hello my world” | excerpt(“my”, 3) => “…lo my wo…”
  • simple_format_text: Returns +text+ transformed into html using very simple formatting rules Surrounds paragraphs with <tt>&lt;p&gt;</tt> tags, and converts line breaks into <tt>&lt;br /&gt;</tt> Two consecutive newlines(<tt>nn</tt>) are considered as a paragraph, one newline (<tt>n</tt>) is considered a linebreak, three or more consecutive newlines are turned into two newlines
  • strip_links_text: Turns all links into words, like “<a href=”something”>else</a>” to “else”.

Installation

Step 1: Add bundle to your deps file
[TwigSfHelper2ExtensionsBundle] 
git=http://github.com/mozzymoz/TwigSfHelper2ExtensionsBundle.git 
target=bundles/JustMozzy/TwigSfHelper2ExtensionsBundle
Step 2 Add bundle to your autoload.php and AppKernel:
'JustMozzy' => __DIR__.'/../vendor/bundles'
new JustMozzy\TwigSfHelper2ExtensionsBundle\TwigSfHelper2ExtensionsBundle(),

 

 

Flex 3.0 meets Symfony meets PureMVC: sfFlexymfonyPlugin

I have just released the new sfFlexymfonyPlugin for Symfony.

The sfFlexymfonyPlugin provides means for a developer to easily integrate flex applications into Symfony. The undelying Felx application uses a specific framework aimed to give developers a Symfony-like feel.
Detailed documentation still to follow. Please for now try to have a look at the DefaultModule example in your application. The underlying architecture is based on PureMVC so please also have a look at their documentations. Soon I will update the documentation and provide a full tutorial on how to use the framework. Also browse through the whole source code (there is not much in there so you will have a nice overview of what’s going on).

Check it out here

New Symfony Plugin: sfFileTrunkPlugin

I have finally finished a new plugin for Symfony: sfFileTrunkPlugin

sfFileTrunk plugin

The sfFileTrunkPlugin provides a central point for file uploads and handling. Not only does it take care of all of your file uploads but also for content delivery easily implemented using partials and components.

Installation

This plugin requires sfThumbnailPlugin. If you want to make use of the components and actions of this plugin don’t forget to enable the sfFileTrunk module in your application settings.yml

Contents

This plugin contains several classes for easy file upload and devlivery.

Creating a file upload using sfFileTrunkPlugin

In order to make use of the plugin’s functionalities all you basically need to do is to create a file input widget in your form and pass the sfValidatedFileTrunk class to the sfValidatorFile.

See for example following form:

class TestForm extends BaseForm
{
    public function configure()
    {
        $this->setWidget('file', new sfWidgetFormInputFile());
        $this->setValidator('file', new sfValidatorFile(array(
            'validated_file_class' => 'sfValidatedFileTrunk', 
            'path' => FileTrunk::getPath()
        )));

        $this->getWidgetSchema()->setNameFormat('test[%s]');
    }
}

It as easy as that. Your action to handle the form could look like the following:

    public function executeIndex(sfWebRequest $request)
    {
        $this->form = new TestForm();

        if ($request->isMethod('post'))
        {
            $this->form->bind($request->getParameter('test'), $request->getFiles('test'));

            if ($this->form->isValid())
            {
                $file = $this->form->getValue('file');
                $file->save();
            }
        }
    }

That’s it. Everything else is already done in the background. The file is saved with a unique name and a reference is inserted in the database.

This was just a very simple example. The real use of the plugin comes through if you combine it with your model. Take the following schema for example:

propel:
  test_table:
    id:
    title:
      type: varchar
      size: 80
      required: true
    file_trunk_id:
      type: integer
      required: true
      foreignTable: file_trunk
      foreignReference: id

A reference to the file_trunk table is needed so that we can store the file_trunk_id in our table (this table could for example the images table for a gallery). Now all you need todo is to change the TestTable’s form a little bit like this:

class TestTableForm extends BaseTestTableForm
{
    public function configure()
    {
        $this->setWidget('file_trunk_file', new sfWidgetFormInputFile());
        $this->setValidator('file_trunk_file', new sfValidatorFile(array(
            'validated_file_class' => 'sfValidatedFileTrunk', 
            'path' => sfConfig::get('sf_upload_dir')
        )));

        unset($this->widgetSchema['file_trunk_id']);
        unset($this->validatorSchema['file_trunk_id']);
    }

    public function save($con = null)
    {

        $file = $this->getValue('file_trunk_file');
        $file->save();
        $this->getObject()->setFileTrunkId($file->getFileTrunk()->getId());
        return parent::save($con);
    }

}

And voila! You have now enabled file upload with reference to the file trunk in your model.

Content delivery with sfFileTrunkPlugin

To put out content is very easy. You can put out content with two components:

<?php include_component('sfFileTrunk', 'filetrunk_file', array('id' => 1)); ?>
<hr/>
<?php include_component('sfFileTrunk', 'filetrunk_image', array('id' => 1, 'width' => 100)); ?>

The filetrunk_image component supports thumbnailing of images. Minimum parameters to be passed so that it works is the width parameter. You can optionally add the height parameter. If you set width to 0 or leave them completely out the original image will be used.

NOTE: When not in the dev environment new thumbnail generation is limited to a 30 minutes interval. That means if you already generated a thumbnail of 30×30 you will have to wait another 30 minutes before you can generate a thumbnail with 40×40.

Symfony Plugin Install Script

This is a very short post.

Currently I am using Symfony 1.4.1 for my development but I have noticed that I could not install most if not all plug-ins through the PEAR packaging system. Since I am using Ubuntu I quickly wrote myself a short script to handle the download and unpacking of plug-ins that I want to install. When calling the script you just give it the URL to the .tgz package of the plug-in. Enjoy

#!/bin/sh
url=$1
#extract the package filename from the URL
package_name=$(echo $1 | sed 's/.*\///')
#extract the raw plug-in name (with version number)
plugin_raw=$(echo $package_name | sed 's/\(.*\?\)\.tgz/\1/')
#extract the actual name of the plug-in (without the version number)
plugin_name=$(echo $package_name | sed 's/\([^-]*\).*/\1/')

#go to the plug-in directory
cd plugins

#check if we already have downloaded the package. You may change the destination to anything you like.
#I store the packages in /usr/share
if [ -f /usr/share/$package_name ]
then
 sudo cp /usr/share/$package_name ./$package_name
else
 wget $1
 sudo cp ./$package_name /usr/share/$package_name
fi

# untar
tar zxpf ./$package_name
mv ./$plugin_raw ./$plugin_name
sudo rm ./$package_name

# remove package.xml file
if [ -f ./package.xml ]
then
 rm package.xml
fi

exit 0