tag:blogger.com,1999:blog-83567854547477024272024-03-22T03:25:39.010+01:00PEMPartageons nos connaissances.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.comBlogger116125tag:blogger.com,1999:blog-8356785454747702427.post-79447589108079304242016-11-27T09:34:00.001+01:002016-11-27T09:34:57.722+01:00Analyzing Meteor's bundle size<a href="https://www.npmjs.com/package/source-map-explorer">source-map-explorer</a> is an helpful tool for analyzing your browser payload.<br />
<br />
For installing it:<br />
<pre class="prettyprint">
npm i -g source-map-explorer
</pre>
<br />
And for using it while developing your apps:<br />
<pre class="prettyprint">
source-map-explorer .meteor/local/build/programs/web.browser/app/app.js{,.map}
</pre>
<br />
Thanks to "Totally tooling tips":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe width="320" height="266" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/7aY9BoMEpG8/0.jpg" src="https://www.youtube.com/embed/7aY9BoMEpG8?feature=player_embedded" frameborder="0" allowfullscreen></iframe></div>
<br />
<br />
<br />pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.6894645 2.0294984000000178 49.0237635 2.6749454000000177tag:blogger.com,1999:blog-8356785454747702427.post-11489763409301619642016-07-03T18:39:00.001+02:002016-07-03T18:42:28.274+02:00OSX: What to do when Meteor build never stops?When your app starts to get big, you may end up using more than 256 files. On OSX, the build process may never finish the build owing to this nasty bug: <a href="https://github.com/meteor/meteor/issues/6952">https://github.com/meteor/meteor/issues/6952</a>.<br />
<br />
If you hit this limit, simply increase your kernel capabilities by creating a file named <code>/Library/LaunchDaemons/limit.maxfiles.plist</code> with the following content and reboot:
<br />
<pre class="prettyprint">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>524288</string>
<string>524288</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
</pre>
<br />
You can check your new settings on the CLI:<br />
<pre class="prettyprint">$ ulimit -n
524288
</pre>
<br />
See this thread for more informations: <a href="http://unix.stackexchange.com/questions/108174/how-to-persist-ulimit-settings-in-osx-mavericks">http://unix.stackexchange.com/questions/108174/how-to-persist-ulimit-settings-in-osx-mavericks</a><br />
<pre></pre>
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.6894645 2.0294984000000178 49.0237635 2.6749454000000177tag:blogger.com,1999:blog-8356785454747702427.post-82466305484085235022016-07-01T16:20:00.001+02:002016-07-01T16:20:28.757+02:00Tip: Open URL with iTerm2In iTerm2, when you CLI displays an URL, simply hover it with <kbd>⌘</kbd> and click on it. It opens up a tab in your default browser.
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrC9ybRHPN2QkXIjLpLSCwYMo4fC5NwXDSs2h3338Lr3DItmWdWxcNtv2pQf_P1F39hK8OkAAjExOjlxikCmUCRAGjmFdqKX4Zj1v_Tr7e-DWC_gkgkrdu0BxaZnvbC9MUrI-DqZz8vtq1/s1600/open_url.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrC9ybRHPN2QkXIjLpLSCwYMo4fC5NwXDSs2h3338Lr3DItmWdWxcNtv2pQf_P1F39hK8OkAAjExOjlxikCmUCRAGjmFdqKX4Zj1v_Tr7e-DWC_gkgkrdu0BxaZnvbC9MUrI-DqZz8vtq1/s1600/open_url.gif" /></a></div>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.6894645 2.0294984000000178 49.0237635 2.6749454000000177tag:blogger.com,1999:blog-8356785454747702427.post-51812078339425372232016-06-14T10:06:00.003+02:002016-06-14T10:22:38.572+02:00Meteor 1.3 - Simulate latency on publicationsSnippet revamped for Meteor 1.3 and based on : <a href="https://www.snip2code.com/Snippet/212616/Meteor--Simulate-high-latency-publish-fu">Meteor Simulate high latency publish function</a><br />
<br />
<br />
<pre class="prettyprint">
import { Meteor } from 'meteor/meteor';
import Future from 'fibers/future';
import Todo from '../Todo';
Meteor.publish('todo.public', function() {
const future = new Future();
Meteor.setTimeout(() => future.return(Todo.find()), 5000);
return future.wait();
});
</pre>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.856614 2.3522219000000177 48.856614 2.3522219000000177tag:blogger.com,1999:blog-8356785454747702427.post-28763670757775793592016-02-21T16:01:00.001+01:002016-02-21T16:01:42.583+01:00Staggered Menu with React and VelocityThis little animation integrates React and Velocity in Codepen for displaying a Menu with staggered items.
<div class="codepen" data-default-tab="result" data-height="400" data-slug-hash="NxZbxR" data-theme-id="9517" data-user="PEM--">
See the Pen <a href="http://codepen.io/PEM--/pen/NxZbxR/">React & Velocity - Staggered menu</a> by PEM (<a href="http://codepen.io/PEM--">@PEM--</a>) on <a href="http://codepen.io/">CodePen</a>.</div>
<script async="" src="//codepen.io/assets/embed/ei.js"></script>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-15725905809652358682016-02-21T12:41:00.002+01:002016-02-21T16:03:48.012+01:00Simple hamburger buttonSet on a real <code>button</code> for better browser compatibility.
<div class="codepen" data-default-tab="result" data-height="100" data-slug-hash="jWjMdr" data-theme-id="9517" data-user="PEM--">
See the Pen <a href="http://codepen.io/PEM--/pen/jWjMdr/">Simple hamburger</a> by PEM (<a href="http://codepen.io/PEM--">@PEM--</a>) on <a href="http://codepen.io/">CodePen</a>.</div>
<script async="" src="//codepen.io/assets/embed/ei.js"></script>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-32568575887851955542016-02-20T12:16:00.002+01:002016-02-20T12:18:37.256+01:00Simple Ken Burns<div class="codepen" data-default-tab="result" data-height="430" data-slug-hash="ZQNdqE" data-theme-id="9517" data-user="PEM--">
See the Pen <a href="http://codepen.io/PEM--/pen/ZQNdqE/">Simple Ken Burns</a> by PEM (<a href="http://codepen.io/PEM--">@PEM--</a>) on <a href="http://codepen.io/">CodePen</a>.</div>
<script async="" src="//codepen.io/assets/embed/ei.js"></script>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-64890945639688171142015-11-09T06:40:00.001+01:002015-11-09T06:40:39.781+01:00Efficient Static Assets Pipeline with WebpackA must seen video on WebPack:<br /><br />
<iframe allowfullscreen="" frameborder="0" height="344" src="https://www.youtube.com/embed/w1dAb_Umt8o" width="459"></iframe>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0tag:blogger.com,1999:blog-8356785454747702427.post-60917360029718646202015-09-08T23:37:00.001+02:002015-09-10T12:41:21.745+02:00Configure ESLint globally for Meteor 1.2 and React in AtomStart by installing Atom's <a href="http://eslint.org/">ESLint</a> package <a href="https://atom.io/packages/linter-eslint">linter-eslint</a> via:<br />
<pre class="prettyprint">apm install linter linter-eslint
</pre>
<br />
Now, install <a href="http://eslint.org/">ESLint</a> globally:<br />
<pre class="prettyprint">npm i -g eslint
</pre>
<br />
Within Atom, use the settings of <a href="http://eslint.org/">ESLint</a> to check <b>Use Global Eslint</b> and set the <b>Global Node Path</b> to <code>/usr/local</code>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPJ7AVasy4pWsvihQ9SoMp8pkk0RenQTqTkr3LKnTfKIKWpoiJ_TfEjDS5CgqQtOMWdKcRUObX76L7QvvrpIxZjGpL7EbrqyW9gzi0FJtTYb7JWb_x6dWHdaOA_Pdkp8ps-cc2HjYgm3Hz/s1600/Capture+d%25E2%2580%2599e%25CC%2581cran+2015-09-08+a%25CC%2580+23.36.20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPJ7AVasy4pWsvihQ9SoMp8pkk0RenQTqTkr3LKnTfKIKWpoiJ_TfEjDS5CgqQtOMWdKcRUObX76L7QvvrpIxZjGpL7EbrqyW9gzi0FJtTYb7JWb_x6dWHdaOA_Pdkp8ps-cc2HjYgm3Hz/s320/Capture+d%25E2%2580%2599e%25CC%2581cran+2015-09-08+a%25CC%2580+23.36.20.png" width="320" /></a></div>
<br />
To taylor <a href="http://eslint.org/">ESLint</a> to our needs, we are using its simple YAML grammar in <code>~/.eslintrc</code>:<br />
<pre class="prettyprint">---
env:
browser: true
node: true
es6: true
meteor: true
mongo: true
jquery: true
ecmaFeatures:
arrowFunctions: true
blockBindings: true
classes: true
defaultParams: true
forOf: true
jsx: true
objectLiteralShorthandMethods: true
objectLiteralShorthandProperties: true
spread: true
superInFunctions: true
</pre>
<br />
Nice and readable.
<br />
<br />
<b>[Edited: 09/10/2015]</b><br />
The full source of this <code>~/.eslintrc</code> file is available on my Github account: <a href="https://github.com/PEM--/dotfiles/blob/master/eslintrc">https://github.com/PEM--/dotfiles/blob/master/eslintrc</a>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-80460816243180653012015-09-08T00:05:00.002+02:002015-09-08T00:09:44.446+02:00Exposing ES2015 classes with MeteorJust a little post on exposing <a href="http://babeljs.io/" target="_blank">ES2015</a> classes with <a href="https://www.meteor.com/" target="_blank">Meteor</a> outside of the scope of its file. Pretty useful for <a href="http://facebook.github.io/react/" target="_blank">React</a>.<br />
<pre class="prettyprint">Footer = class Footer extends React.Component {
render() {
return (
<footer>
<section className='ui container'>
<article>
<ul className='fa'>
<li><a className='fa-facebook' href='' target='_blank'></a></li>
<li><a className='fa-twitter' href='' target='_blank'></a></li>
<li><a className='fa-envelope' href='' target='_blank'></a></li>
</ul>
</article>
</section>
</footer>
);
}
};
</pre>
Simple solution from <a href="https://github.com/rogchap" target="_blank">Roger Chapman</a>. Thank you guy.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-87903326725885977452015-09-07T17:17:00.001+02:002015-09-07T17:17:10.997+02:00Meteor Devops on OSX with Docker set for Ubuntu 15.04<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#introduction" id="user-content-introduction"><span class="octicon octicon-link"></span></a>Introduction</h3>
While using Meteor in development is an easy task and deploying it on Meteor's
infrastructure is a no brainer, things may start to get messy if you need to
deploy it, secure it and scale it on your cloud. Especially if your customer
imposes you a specific constraint on cloud sovereignty. The best way to
achieve easy deployment is using the excellent
<a href="https://github.com/arunoda/meteor-up">Meteor Up</a> tool. But if it fails or
if you need to go a bit further in your infrastructure deployment,
I recommend that you start using <a href="https://www.docker.com/">Docker</a> to get
familiar with this handy DevOps tool.<br />
I hope that this tutorial will lead you on the appropriate tracks.<br />
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#versions-applied-in-this-tutorial" id="user-content-versions-applied-in-this-tutorial"><span class="octicon octicon-link"></span></a>Versions applied in this tutorial</h3>
As you may need to update this tutorial for your own DevOps use cases, here is
the complete list of versions used in this tutorial:<br />
<ul>
<li>OSX 10.10.5 as the development platform</li>
<li>Ubuntu 15.04 as Docker host system</li>
<li>Debian Jessie 7 with latest updates as Docker container system</li>
<li>Docker 1.8.1</li>
<li>Docker Registry 2</li>
<li>Docker Machine 0.4.1</li>
<li>Docker Compose 1.4.0</li>
<li>VirtualBox 5.0.2</li>
<li>Meteor 1.1.0.3</li>
<li>NGinx 1.9.4-1</li>
<li>NodeJS 0.10.40</li>
<li>Mongo 3.0.6 - WiredTiger</li>
</ul>
<a href="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/software_architecture.png" target="_blank"><img alt="Software architecture" src="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/software_architecture.png" style="max-width: 100%;" /></a><br />
<blockquote>
Why Debian Jessie instead of Debian Wheezie? Simple, a gain of 30MB of
footprint. Note that we could have set this tutorial on other even smaller
Linux distributions for our Docker Images, like Alpine Linux. But as time of
this writing, these smaller distributions do not offer the package required
for installing Meteor (namely, MongoDB and node-fibers).</blockquote>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#installing-the-tooling" id="user-content-installing-the-tooling"><span class="octicon octicon-link"></span></a>Installing the tooling</h3>
If you have never done it before install Homebrew and its plugin Caskroom.<br />
<pre class="prettyprint">ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install caskroom/cask/brew-cask
</pre>
Then install VirtualBox and Vagrant:<br />
<pre class="prettyprint">brew cask install virtualbox vagrant
</pre>
Now install Docker and its tools:<br />
<pre class="prettyprint">brew install docker docker-machine docker-compose
</pre>
For easing the access to VM and servers, we are using an SSH key installer:<br />
<pre class="prettyprint">brew install ssh-copy-id
</pre>
For parsing and querying JSON produced by Docker, we are using <code>./jq</code>:<br />
<pre class="prettyprint">brew install jq</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#some-file-structure" id="user-content-some-file-structure"><span class="octicon octicon-link"></span></a>Some file structure</h3>
For differentiating the Meteor project from the DevOps project, we
store our files like so:<br />
<pre class="prettyprint">.
├── app
└── docker
</pre>
The <code>app</code> folder contains the root of Meteor sources and the <code>docker</code>
folder contains the root of DevOps sources.<br />
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#create-your-virtual-machines-as-docker-machine" id="user-content-create-your-virtual-machines-as-docker-machine"><span class="octicon octicon-link"></span></a>Create your virtual machines as Docker Machine</h3>
Create a <code>Vagrantfile</code> that matches your production environment.
Here, we are using an Ubuntu 15.04 with Docker pre-installed.<br />
<pre class="prettyprint">hosts = {
"dev" => "192.168.1.50",
"pre" => "192.168.1.51"
}
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/vivid64"
config.ssh.insert_key = false
hosts.each do |name, ip|
config.vm.define name do |vm|
vm.vm.hostname = "%s.example.org" % name
#vm.vm.network "private_network", ip: ip
vm.vm.network "public_network", bridge: "en0: Wi-Fi (AirPort)", ip: ip
vm.vm.provider "virtualbox" do |v|
v.name = name
end
vm.vm.provision "shell", path: "provisioning.sh"
end
end
end
</pre>
<blockquote>
I've provided 2 network configurations here. The first one is a private network
leading to 2 virtual machines that are not accessible to your local network (
only your local OSX). The second bridges your local OSX network driver so that
your VMs gain public access within your LAN. Note that for both of these
network configurations, I've used static IPs.</blockquote>
Before creating our virtual machine, we need to setup a <code>provisioning.sh</code>:<br />
<pre class="prettyprint">#!/bin/bash
# Overriding bad Systemd default in Docker startup script
sudo mkdir -p /etc/systemd/system/docker.service.d
echo -e '[Service]\n# workaround to include default options\nEnvironmentFile=-/etc/default/docker\nExecStart=\nExecStart=/usr/bin/docker -d -H fd:// $DOCKER_OPTS' | sudo tee /etc/systemd/system/docker.service.d/ubuntu.conf
# Set Docker daemon with the following properties:
# * Daemon listen to external request and is exposed on port 2376, the default Docker port.
# * Docker uses the AUFS driver for file storage.
# * Daemon uses Docker's provided certification chain.
# * Dameon has a generic label.
# * Daemon is able to resolve DNS query using Google's DNS.
echo 'DOCKER_OPTS="-H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver aufs --tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic --dns 8.8.8.8 --dns 8.8.4.4"' | sudo tee /etc/default/docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# Enable Docker on server reboot
sudo systemctl enable docker
# Remove and clean unused packages
sudo apt-get autoremove -y
sudo apt-get autoclean -y
</pre>
Now, we are starting our virtual hosts and declare it as a Docker Machine:<br />
<pre class="prettyprint">vagrant up --no-provision
</pre>
Throughout this terminal sessions, we need some environment variables.
We store them in a <code>local_env.sh</code> file that we fill step by step and source
each time we open a new terminal session:<br />
<pre class="prettyprint">export HOST_IP_DEV='192.168.1.50'
export HOST_IP_PRE='192.168.1.51'
# Use preferably your FQDN (example.org)
export HOST_IP_PROD='X.X.X.X'
</pre>
If you are using <a href="http://fishshell.com/">Fish</a> like me, use the following content:<br />
<pre class="prettyprint">export HOST_IP_DEV='192.168.1.50'
export HOST_IP_PRE='192.168.1.51'
# Use preferably your FQDN (example.org)
export HOST_IP_PROD='X.X.X.X'
</pre>
This should provide an easy access to all parts of the following network architecture:
<a href="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/network_architecture.png" target="_blank"><img alt="Network architecture" src="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/network_architecture.png" style="max-width: 100%;" /></a><br />
Open 3 terminal sessions. In the first session, launch the following commands:<br />
<pre class="prettyprint">docker-machine -D create -d generic \
--generic-ip-address $HOST_IP_DEV \
--generic-ssh-user vagrant \
--generic-ssh-key ~/.vagrant.d/insecure_private_key \
dev
</pre>
In the second session, launch the following commands:<br />
<pre class="prettyprint">docker-machine -D create -d generic \
--generic-ip-address $HOST_IP_PRE \
--generic-ssh-user vagrant \
--generic-ssh-key ~/.vagrant.d/insecure_private_key \
pre
</pre>
Now, in the last session, wait for the 2 previous sessions to be blocked
on the following repeated message
<code>Daemon not responding yet: dial tcp XX.XX.XX.XX:2376: connection refused</code>
and issue the following command:<br />
<pre class="prettyprint">vagrant provision
</pre>
<blockquote>
<strong>What's going on here?</strong> Actually, the current state of Docker for Ubuntu 15.04
doesn't support <code>DOCKER_OPTS</code>. This is due to the transition in Ubuntu from
<strong>upstart</strong> to <strong>Systemd</strong>. Plus, when we are creating our Docker Machine in
our local OSX, Docker Machine re-install Docker on the host. Thus, we end up
with a screwed installation on the host unable to speak to the outside world
(leading to the message <code>Daemon not responding yet: dial tcp XX.XX.XX.XX:2376: connection refused</code>).
Basically, the vagrant provisioning script patches both vagrant virtual servers.
You can reuse the content of this script on your production server when you
create the associated Docker Machine. For this, you can use the following command:<br />
<code>ssh root@$HOST_IP_PROD "bash -s" < ./provisioning.sh</code></blockquote>
In this last section, we will finish our configuration of our development and
pre-production hosts by installing Docker Machine and securing their open ports
with simple firewall rules. The script that we are using is named <code>postProvisioning.sh</code>.<br />
<pre class="prettyprint">#!/bin/bash
# Install Docker Machine
curl -L https://github.com/docker/machine/releases/download/v0.4.0/docker-machine_linux-amd64 | sudo tee /usr/local/bin/docker-machine > /dev/null
sudo chmod u+x /usr/local/bin/docker-machine
# Install Firewall
sudo apt-get install -y ufw
# Allow SSH
sudo ufw allow ssh
# Allow HTTP and WS
sudo ufw allow 80/tcp
# Allow HTTPS and WSS
sudo ufw allow 443/tcp
# Allow Docker daemon port and forwarding policy
sudo ufw allow 2376/tcp
sudo sed -i -e "s/^DEFAULT_FORWARD_POLICY=\"DROP\"/DEFAULT_FORWARD_POLICY=\"ACCEPT\"/" /etc/default/ufw
# Enable and reload
yes | sudo ufw enable
sudo ufw reload
</pre>
We execute this script on both VM using simple SSH commands like so:<br />
<pre class="prettyprint">ssh -i ~/.vagrant.d/insecure_private_key vagrant@$HOST_IP_DEV "bash -s" < ./postProvisioning.sh
ssh -i ~/.vagrant.d/insecure_private_key vagrant@$HOST_IP_PRE "bash -s" < ./postProvisioning.sh
</pre>
Now you can access your VM either via Docker, Vagrant and plain SSH. To finish
our VM configuration, we are going to allow full root access to the VM without
requiring to use password. For that, you need a public and a private SSH keys
on your local machine. If you haven't done it before simply use the following
command:<br />
<pre class="prettyprint">ssh-keygen -t rsa
</pre>
Now, using Vagrant, copy the content of your <code>~/.ssh/id_rsa.pub</code> in each of the
VM's <code>/root/.ssh/authorized_key</code>.<br />
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#reference-your-production-host-as-a-docker-machine" id="user-content-reference-your-production-host-as-a-docker-machine"><span class="octicon octicon-link"></span></a>Reference your production host as a Docker Machine</h3>
In this example, we are using a VPS from OVH with a pre-installed Ubuntu 15.04
with Docker. These VPS starts at 2.99€ (around $3.5) per month and comes with
interesting features such as Anti-DDos, real time monitoring, ...<br />
Preinstalled VPS comes with an OpenSSH access. Therefore, we will be using
the <strong>generic-ssh</strong> driver for our Docker Machine just like we did for the
Vagrant VM for development and pre-production. And like before, we are using
2 terminal sessions to overcome the Docker installation issue on Ubuntu 15.04.<br />
In the first terminal session, we setup a root SSH access without password like so:<br />
<pre class="prettyprint">ssh-copy-id root@$HOST_IP_PROD
# Now, you should check if your key is properly copied
ssh root@$HOST_IP_PROD "cat /root/.ssh/authorized_keys"
cat ~/.ssh/id_rsa.pub
# These 2 last commands should return the exact same key
</pre>
<blockquote>
I've been tricked by some <code>ssh-user-agent</code> issue there. Docker wasn't reporting
any issue even in debug mode and was just exiting with a default error code.
So, be careful that your public key is exactly the same on your local machine,
your VM and your production host.</blockquote>
Next and still on the same terminal session, we declare our production host :<br />
<pre class="prettyprint">docker-machine -D create -d generic \
--generic-ip-address $HOST_IP_PROD \
--generic-ssh-user root \
prod
</pre>
And on the second terminal session, when the message
<code>Daemon not responding yet: dial tcp X.X.X.X:2376: connection refused</code> appears
on the first session, we launch:<br />
<pre class="prettyprint">ssh root@$HOST_IP_PROD "bash -s" < ./provisioning.sh
</pre>
The last remaining step consists into solidifying our security by enabling
a firewall on the host and removing the old packages:<br />
<pre class="prettyprint">ssh root@$HOST_IP_PROD "bash -s" < ./postProvisioning.sh
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#creating-your-own-registry" id="user-content-creating-your-own-registry"><span class="octicon octicon-link"></span></a>Creating your own registry</h3>
Basically, what we want to achieve is micro-services oriented to stick to
a multi-tiers architecture:
<a href="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/docker_architecture.png" target="_blank"><img alt="Docker architecture" src="https://raw.githubusercontent.com/PEM--/devops-tuts/master/doc/docker_architecture.png" style="max-width: 100%;" /></a><br />
This architecture could then be spread over a Docker Swarm of multiple servers
or kept on a single one. But playing with multiple containers in development
is quickly a pain. We can leverage the power of Docker Compose and a local
registry to fasten our development of Docker images.<br />
In your first terminal session, activate your development Docker Machine:<br />
<pre class="prettyprint">eval "$(docker-machine env dev)"
# In Fish
eval (docker-machine env dev)
</pre>
Create a Docker Compose file <code>registry.yml</code>:<br />
<pre class="prettyprint">registry:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
volumes:
- /var/lib/registry:/var/lib/registry
</pre>
Now, we will use the development Docker Machine as our local registry:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "mkdir /var/lib/registry"
docker-compose -f registry.yml up -d
</pre>
For making it visible to our preproduction VM, we need to update our default
firewall rules:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV ufw allow 5000
</pre>
Now we are editing our <code>/etc/default/docker</code> configuration file for adding this
insecure registry in both our development and preproduction VM with this new
flag:<br />
<pre class="prettyprint"># On 192.168.1.50 & 192.168.1.51, in /etc/default/docker, we add in DOCKER_OPTS:
--insecure-registry 192.168.1.50:5000
</pre>
We need to restart our Docker daemon and restart the Docker registry on the
development VM:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV systemctl restart docker
ssh root@$HOST_IP_PRE systemctl restart docker
eval "$(docker-machine env dev)"
</pre>
Our final step in the registry management is to login your preproduction VM and
your production server to Docker Hub using your Docker credential.<br />
<pre class="prettyprint">eval "$(docker-machine env pre)"
docker login
eval "$(docker-machine env prod)"
docker login
</pre>
<blockquote>
Note that our registry isn't published outside our LAN. This makes it unusable
for our production host. This development chain uses Docker Hub for publishing
your images. Exposing this private registry to the outside world would require
some additional configurations to tighten its security and server with a
publicly exposed IP. While you could solely rely on Docker Hub for publishing
your images, pushing and pulling to the outside world of your LAN are lengthy
operations though lighten since Docker 1.6 and Docker Registry 2.</blockquote>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#building-mongo" id="user-content-building-mongo"><span class="octicon octicon-link"></span></a>Building Mongo</h3>
Our <code>mongo/Dockerfile</code> is based on Mongo's official one. It adds to the
picture the configuration of small ReplicaSet for making OPLOG available:<br />
<pre class="prettyprint"># Based on: https://github.com/docker-library/mongo/blob/d5aca073ca71a7023e0d4193bd14642c6950d454/3.0/Dockerfile
FROM debian:wheezy
MAINTAINER Pierre-Eric Marchandet <your_docker_hub_login gmail.com="">
# Update system
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && \
apt-get upgrade -y -qq --no-install-recommends && \
apt-get install -y -qq --no-install-recommends apt-utils && \
apt-get install -y -qq --no-install-recommends \
ca-certificates curl psmisc apt-utils && \
apt-get autoremove -y -qq && \
apt-get autoclean -y -qq && \
rm -rf /var/lib/apt/lists/*
# Grab gosu for easy step-down from root
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
curl -sS -o /usr/local/bin/gosu -L "https://github.com/tianon/gosu/releases/download/1.2/gosu-$(dpkg --print-architecture)" && \
curl -sS -o /usr/local/bin/gosu.asc -L "https://github.com/tianon/gosu/releases/download/1.2/gosu-$(dpkg --print-architecture).asc" && \
gpg --verify /usr/local/bin/gosu.asc && \
rm /usr/local/bin/gosu.asc && \
chmod +x /usr/local/bin/gosu
# Install MongoDB
ENV MONGO_MAJOR 3.0
ENV MONGO_VERSION 3.0.6
RUN groupadd -r mongodb && \
useradd -r -g mongodb mongodb && \
apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 492EAFE8CD016A07919F1D2B9ECBEC467F0CEB10 && \
echo "deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/$MONGO_MAJOR main" > /etc/apt/sources.list.d/mongodb-org.list && \
apt-get update && \
apt-get install -y -qq --no-install-recommends \
mongodb-org=$MONGO_VERSION \
mongodb-org-server=$MONGO_VERSION \
mongodb-org-shell=$MONGO_VERSION \
mongodb-org-mongos=$MONGO_VERSION \
mongodb-org-tools=$MONGO_VERSION && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /var/lib/mongodb && \
mv /etc/mongod.conf /etc/mongod.conf.orig && \
apt-get autoremove -y -qq && \
apt-get autoclean -y -qq && \
rm -rf /var/lib/apt/lists/* && \
# Prepare environment for Mongo daemon: Use a Docker Volume container
mkdir -p /db && chown -R mongodb:mongodb /db
# Launch Mongo
COPY mongod.conf /etc/mongod.conf
CMD ["gosu", "mongodb", "mongod", "-f", "/etc/mongod.conf"]
</your_docker_hub_login></pre>
We need a configuration file for this Docker image to be built <code>mongo/mongod.conf</code>:<br />
<pre class="prettyprint">storage:
dbPath: "/db"
engine: "wiredTiger"
wiredTiger:
engineConfig:
cacheSizeGB: 1
collectionConfig:
blockCompressor: snappy
replication:
oplogSizeMB: 128
replSetName: "rs0"
net:
port: 27017
wireObjectCheck : false
unixDomainSocket:
enabled : true
</pre>
We could build this image and run it, but I prefer using a Docker Compose file.
These file eases the process of build, run and deploys of your Docker images
acting as a project file when multiple Docker images are required to work
together for an application. Here's the minimal <code>docker/docker-compose.yml</code>
that we will enrich in the next steps of this tutorial:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
build: mongo
volumes:
- /var/db:/db
expose:
- "27017"
</pre>
Before building or launching this Docker image, we need to prepare the
volume on each host that receives and persists Mongo's data:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "rm -rf /var/db; mkdir /var/db; chmod go+w /var/db"
ssh root@$HOST_IP_PRE "rm -rf /var/db; mkdir /var/db; chmod go+w /var/db"
ssh root@$HOST_IP_PROD "rm -rf /var/db; mkdir /var/db; chmod go+w /var/db"
</pre>
For building our Mongo Docker image:<br />
<pre class="prettyprint">docker-compose build db
# Or even faster, for building and running
docker-compose up -d db
</pre>
And once it's running, initialize a single instance ReplicaSet for making
Oplog tailing available:<br />
<pre class="prettyprint">docker-compose run db mongo db:27017/admin --quiet --eval "rs.initiate(); rs.conf();"
</pre>
Some useful commands while developing a container:<br />
<pre class="prettyprint"># Access to a container in interactive mode
docker run -ti -P docker_db
# Delete all stopped containers
docker rm $(docker ps -a -q)
# Delete all images that are not being used in a running container
docker rmi $(docker images -q)
# Delete all images that failed to build (untagged images)
docker rmi $(docker images -f "dangling=true" -q)
# In Fish
# Delete all stopped containers
docker rm (docker ps -a -q)
# Delete all images that are not being used in a running container
docker rmi (docker images -q)
# Delete all images that failed to build (dangling images)
docker rmi (docker images -f "dangling=true" -q)
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#building-meteor" id="user-content-building-meteor"><span class="octicon octicon-link"></span></a>Building Meteor</h3>
Meteor is fairly easy to build. It's a simple NodeJS app. We start by creating
our <code>docker/meteor/Dockerfile</code>:<br />
<pre class="prettyprint"># Based on: https://github.com/joyent/docker-node/blob/master/0.10/wheezy/Dockerfile
FROM debian:wheezy
MAINTAINER Pierre-Eric Marchandet <your_docker_hub_login gmail.com="">
# Update system
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && \
apt-get upgrade -qq -y --no-install-recommends && \
apt-get install -qq -y --no-install-recommends \
# CURL
ca-certificates curl wget \
# SCM
bzr git mercurial openssh-client subversion \
# Build
build-essential && \
apt-get autoremove -qq -y && \
apt-get autoclean -qq -y && \
rm -rf /var/lib/apt/lists/*
# Install NodeJS
ENV NODE_VERSION 0.10.40
ENV NPM_VERSION 2.13.3
RUN curl -sSLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" && \
tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 && \
rm "node-v$NODE_VERSION-linux-x64.tar.gz" && \
npm install -g npm@"$NPM_VERSION" && \
npm cache clear
# Add PM2 for process management and PhantomJS
RUN npm install -g pm2 phantomjs
# Import sources
COPY bundle /app
# Install Meteor's dependencies
WORKDIR /app
RUN (cd programs/server && npm install)
# Launch application
COPY startMeteor.sh /app/startMeteor.sh
CMD ["./startMeteor.sh"]
</your_docker_hub_login></pre>
Before building this Docker image, we need to prepare the
volume on each host that receives its <code>settings.json</code> used for storing your
secrets in Meteor:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "mkdir /etc/meteor"
ssh root@$HOST_IP_PRE "mkdir /etc/meteor"
ssh root@$HOST_IP_PROD "mkdir /etc/meteor"
</pre>
Now copy your <code>settings.json</code> files on each hosts using a regular SCP. Mine
are slightly different depending on the target where I deploy my Meteor apps.<br />
<pre class="prettyprint"># Just an exammple, adapt it to suit your needs
scp ../app/development.json root@$HOST_IP_DEV:/etc/meteor/settings.json
scp ../app/development.json root@$HOST_IP_DEV:/etc/meteor/settings.json
scp ../app/production.json root@$HOST_IP_DEV:/etc/meteor/settings.json
</pre>
<blockquote>
Note that we do not include our secrets, nor in our code repository by
using a <code>.gitgignore</code> file, nor in our Docker Images.</blockquote>
We import our Meteor sources using a shared script <code>docker/buildMeteor.sh</code> for
the Meteor container and the NGinx container:<br />
<pre class="prettyprint">#!/bin/bash
rm -rf meteor/bundle nginx/bundle
cd ../app
meteor build --architecture os.linux.x86_64 --directory ../docker/meteor
cd -
cp -R meteor/bundle nginx
</pre>
In order to avoid importing too much files in our Docker image, we create
a <code>docker/meteor/.dockerignore</code> file which removes the parts dedicated to
the clients wich will be serverd by NGinx:<br />
<pre class="prettyprint">bundle/README
bundle/packages/*/.build*
bundle/packages/*/.styl
bundle/*/*.md*
bundle/programs/web.browser/app
</pre>
Our last required file is a script <code>docker/meteor/startMeteor.sh</code> for starting
Meteor with the private settings that we add as a specific volume:<br />
<pre class="prettyprint">#!/bin/bash
METEOR_SETTINGS=$(cat /etc/meteor/settings.json) pm2 start -s --no-daemon --no-vizion main.js
</pre>
<blockquote>
Note that we launch Meteor with <a href="https://github.com/Unitech/pm2">PM2</a>. As we
will see it, it's not a mandatory step as we are using Docker's restart
policy in our Docker images. However this process management utility could be
used to get some metrics on NodeJS's status.</blockquote>
For building and launching, we are extending our <code>/docker/docker-compose.yml</code> file:<br />
<pre class="prettyprint"># Application server: NodeJS (Meteor)
server:
build: meteor
environment:
MONGO_URL: "mongodb://db:27017"
MONGO_OPLOG_URL: "mongodb://db:27017/local"
PORT: 3000
ROOT_URL: "https://192.168.1.50"
volumes:
- /etc/meteor:/etc/meteor
expose:
- "3000"
</pre>
For building and launching our Meteor Docker image:<br />
<pre class="prettyprint">docker-compose up -d db server
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#building-nginx" id="user-content-building-nginx"><span class="octicon octicon-link"></span></a>Building NGinx</h3>
It's up to our front container to be created. Let's start with our <code>docker/nginx/Dockerfile</code>:<br />
<pre class="prettyprint"># Based on: https://github.com/nginxinc/docker-nginx/blob/master/Dockerfile
FROM debian:wheezy
MAINTAINER Pierre-Eric Marchandet <your_docker_hub_login gmail.com="">
# Add NGinx official repository
RUN apt-key adv --keyserver pgp.mit.edu --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
RUN echo "deb http://nginx.org/packages/mainline/debian/ wheezy nginx" >> /etc/apt/sources.list
ENV NGINX_VERSION 1.9.4-1~wheezy
# Update system
ENV DEBIAN_FRONTEND noninteractive
RUN groupadd -r www && \
useradd -r -g www www && \
apt-get update && \
apt-get upgrade -qq -y --no-install-recommends && \
apt-get install -qq -y --no-install-recommends \
ca-certificates nginx=${NGINX_VERSION} && \
apt-get autoremove -qq -y && \
apt-get autoclean -qq -y && \
rm -rf /var/lib/apt/lists/*
# Forward request and error logs to Docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
# Configuration files
COPY nginx.conf /etc/nginx/nginx.conf
COPY conf host-specific /etc/nginx/conf/
# Mount points for volumes
RUN mkdir -p /etc/certs /var/cache/nginx /var/tmp
# Source
# Raw source files exposed as HTTP and HTTPS
COPY raw /www/
# Project files exposed as HTTPS
COPY bundle/programs/web.browser/*.js \
bundle/programs/web.browser/*.css \
bundle/programs/web.browser/packages \
bundle/programs/web.browser/app \
/www/
# Ensure proper rights on static assets
RUN chown -R www:www /www /var/cache /var/tmp
# Launch NGinx
COPY startNginx.sh /startNginx.sh
RUN chmod u+x /startNginx.sh
CMD ["/startNginx.sh"]
</your_docker_hub_login></pre>
Like the Meteor container, we are using the same import script. This time,
we remove the server part of our container using the same technic in the
<code>docker/nginx/.dockerignore</code>:<br />
<pre class="prettyprint">bundle/README
bundle/packages/*/.build*
bundle/packages/*/.styl
bundle/*/*.md*
bundle/programs/server
</pre>
For building it, we enhance pour <code>docker/docker-compose.yml</code> file:<br />
<pre class="prettyprint"># Front layer, static file, SSL, proxy cache: NGinx
front:
build: nginx
links:
- server
environment:
# Can be: dev, pre, prod
HOST_TARGET: "dev"
volumes:
- /etc/certs:/etc/certs
- /var/cache:/var/cache
- /var/tmp:/var/tmp
ports:
- "80:80"
- "443:443"
</pre>
Our NGinx requires certificates set on the hosts in <code>/etc/certs</code>. For the
production host, you require SSL certificates from a certificate authority
know by the browser vendors. For the development and the preproduction hosts,
we can use self signed certificate that we create on our hosts:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "mkdir -p /etc/certs; openssl req -nodes -new -x509 -keyout /etc/certs/server.key -out /etc/certs/server.crt -subj '/C=FR/ST=Paris/L=Paris/CN=$HOST_IP_DEV'"
ssh root@$HOST_IP_PRE "mkdir -p /etc/certs; openssl req -nodes -new -x509 -keyout /etc/certs/server.key -out /etc/certs/server.crt -subj '/C=FR/ST=Paris/L=Paris/CN=$HOST_IP_PRE'"
</pre>
We need 2 additional volumes exposed on each host, one for NGinx's cache and
another one for NGinx temporary files:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "mkdir /var/cache; chmod go+w /var/cache; mkdir /var/tmp; chmod go+w /var/tmp"
ssh root@$HOST_IP_PRE "mkdir /var/cache; chmod go+w /var/cache; mkdir /var/tmp; chmod go+w /var/tmp"
ssh root@$HOST_IP_PROD "mkdir -p /etc/certs; mkdir /var/cache; chmod go+w /var/cache; mkdir /var/tmp; chmod go+w /var/tmp"
</pre>
In our Docker Container, we have already imported the static part of our Meteor
app that will be exposed through HTTPS. Our NGinx server will also act as a
static file server in HTTP.Simply put your static assets in the <code>docker/nginx/raw</code>
folder for that.<br />
<blockquote>
While serving HTTP file for our Meteor application has no interest, it could be
usefull to expose some static assets without protection (this is sometime required
by SSL certificate provider).</blockquote>
We now need the configuration files our front. This configuration is mostly
forked and customized from <a href="https://github.com/h5bp/server-configs-nginx">HTML5's boilerplate for NGinx servers</a>.
I will not explained all of them, simply the interesting parts that Meteor
and our multi hosts configuration require. Our entry points is <code>docker/nginx/nginx.conf</code>.<br />
<pre class="prettyprint"># Run as a less privileged user for security reasons.
user www www;
# How many worker threads to run;
# The maximum number of connections for Nginx is calculated by:
# max_clients = worker_processes * worker_connections
worker_processes 1;
# Maximum open file descriptors per process;
# should be > worker_connections.
worker_rlimit_nofile 8192;
events {
# When you need > 8000 * cpu_cores connections, you start optimizing your OS,
# and this is probably the point at which you hire people who are smarter than
# you, as this is *a lot* of requests.
worker_connections 8000;
}
# Default error log file
# (this is only used when you don't override error_log on a server{} level)
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# Main configuration
http {
# Hide nginx version information.
server_tokens off;
# Proxy cache definition
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:8m max_size=3000m inactive=600m;
proxy_temp_path /var/tmp;
# Define the MIME types for files.
include conf/mimetypes.conf;
default_type application/octet-stream;
# Update charset_types due to updated mime.types
charset_types text/xml text/plain text/vnd.wap.wml application/x-javascript application/rss+xml text/css application/javascript application/json;
# Format to use in log files
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Default log file
# (this is only used when you don't override access_log on a server{} level)
access_log /var/log/nginx/access.log main;
# How long to allow each connection to stay idle; longer values are better
# for each individual client, particularly for SSL, but means that worker
# connections are tied up longer. (Default: 65)
keepalive_timeout 20;
# Speed up file transfers by using sendfile() to copy directly
# between descriptors rather than using read()/write().
sendfile on;
# Tell Nginx not to send out partial frames; this increases throughput
# since TCP frames are filled up before being sent out. (adds TCP_CORK)
tcp_nopush on;
# GZip Compression
include conf/gzip.conf;
# Error pages redirections
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# HTTP server
server {
# Server name
include conf/servername.conf;
# Protocol HTTP
listen [::]:80 ipv6only=on;
listen 80;
# Static files with fallback to HTTPS redirect
include conf/staticfile-with-fallback.conf;
# Redirect non-SSL to SSL
location @fallback {
rewrite ^ https://$server_name$request_uri? permanent;
}
}
# Upstream server for the web application server and load balancing
include conf/upstream-server-and-load-balancing.conf;
# Upgrade proxy web-socket connections
include conf/websocket-upgrade.conf;
# HTTPS server
server {
# Server name
include conf/servername.conf;
# Protocols HTTPS, SSL, SPDY
listen [::]:443 ipv6only=on ssl spdy;
listen 443 ssl spdy;
# SSL configuration
include conf/ssl.conf;
# SPDY configuration
include conf/spdy.conf;
# Static files with fallback to proxy server
include conf/staticfile-with-fallback.conf;
# Proxy pass to server node with websocket upgrade
location @fallback {
include conf/proxy-pass-and-cache.conf;
}
}
}
</pre>
Depending on which host launches NGinx, we need a method to set a proper
sever name. For this, we create 3 files:<br />
<ul>
<li><code>docker/nginx/host-specific/servername-dev.conf</code>:
<code>sh
# Server name
server_name 192.168.1.50;
</code></li>
<li><code>docker/nginx/host-specific/servername-pre.conf</code>:
<code>sh
# Server name
server_name 192.168.1.51;
</code></li>
<li><code>docker/nginx/host-specific/servername-prod.conf</code>:
<code>sh
# Server name (the real FQDN of your production server)
server_name example.org;
</code></li>
</ul>
For accessing the static files exposed over HTTP, we use simply declare the root
of the front and we use a <code>@fallback</code> function in case no file has been found.
This is declared in the <code>docker/nginx/staticfile-with-fallback.conf</code>:<br />
<pre class="prettyprint"># Serve static file and use a fallback otherwise
location / {
charset utf-8;
root /www;
# Basic rules
include conf/basic.conf;
# Try static files and redirect otherwise
try_files $uri @fallback;
# Expiration rules
include conf/expires.conf;
}
</pre>
In our HTTP part of our main configuration, you can see that the trafic is
redirected to HTTPS via URL rewriting technic. Our SSL configuration
<code>docker/nginx/conf/ssl.conf</code> uses the exposed Docker Volume <code>/etc/certs</code>:<br />
<pre class="prettyprint"># SSL configuration
ssl on;
# SSL key paths
ssl_certificate /etc/certs/server.crt;
ssl_certificate_key /etc/certs/server.key;
# Trusted cert must be made up of your intermediate certificate followed by root certificate
# ssl_trusted_certificate /path/to/ca.crt;
# Optimize SSL by caching session parameters for 10 minutes. This cuts down on the number of expensive SSL handshakes.
# The handshake is the most CPU-intensive operation, and by default it is re-negotiated on every new/parallel connection.
# By enabling a cache (of type "shared between all Nginx workers"), we tell the client to re-use the already negotiated state.
# Further optimization can be achieved by raising keepalive_timeout, but that shouldn't be done unless you serve primarily HTTPS.
ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 1m;
# Use a higher keepalive timeout to reduce the need for repeated handshakes
keepalive_timeout 300; # up from 75 secs default
# Protect against the BEAST and POODLE attacks by not using SSLv3 at all. If you need to support older browsers (IE6) you may need to add
# SSLv3 to the list of protocols below.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla (Intermediate Set)
# - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DES-CBC3-SHA:!ADH:!AECDH:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# OCSP stapling...
ssl_stapling on;
ssl_stapling_verify on;
# DNS resolution on Google's DNS and DynDNS
resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s;
resolver_timeout 2s;
# HSTS (HTTP Strict Transport Security)
# This header tells browsers to cache the certificate for a year and to connect exclusively via HTTPS.
add_header Strict-Transport-Security "max-age=31536000;";
# This version tells browsers to treat all subdomains the same as this site and to load exclusively over HTTPS
#add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
add_header X-Frame-Options DENY;
</pre>
We have also added SPDY to our HTTPS configuration in the <code>docker/nginx/conf/spdy.conf</code>:<br />
<pre class="prettyprint"># SPDY configuration
add_header Alternate-Protocol 443:npn-spdy/3;
# Adjust connection keepalive for SPDY clients:
spdy_keepalive_timeout 300; # up from 180 secs default
# enable SPDY header compression
spdy_headers_comp 9;
</pre>
<blockquote>
HTTP/2 support is on its way. When integrated to NGinx, this configuration will
be upgraded for taking advantage of it.</blockquote>
No that SSL and SPDY are set, we can serve the static file exposed via HTTPS with
the same configuration as before for HTTP. But this time, the fallback mecanism
redirect the trafic to our Meteor application (our server container).
If no static file is found, the trafic is send to our Meteor application using a
proxy with cache:<br />
<pre class="prettyprint">proxy_http_version 1.1;
proxy_pass http://server;
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
proxy_redirect off;
# Upgrade proxy web-socket connections
proxy_set_header Upgrade $http_upgrade; # allow websockets
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forward-Proto http;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-Proto http;
proxy_set_header X-Nginx-Proxy true;
proxy_cache one;
proxy_cache_key prj$request_uri$scheme;
proxy_cache_bypass $http_upgrade;
# Expiration rules
if ($uri != '/') {
expires 30d;
}
</pre>
Our proxy cache needs to upgrade the HTTPS connections to WSS. This is achieved
in our <code>docker/nginx/conf/upstream-server-and-load-balancing.conf</code>:<br />
<pre class="prettyprint"># Upstream server for the web application server
upstream server {
# server is included in each dynamic /etc/hosts by Docker
server server:3000;
# Load balancing could be done here, if required.
}
</pre>
For directing our NGinx on the appropriate configuration, we use an
simple environment variables <code>HOST_TARGET</code> that can be <code>dev</code>, <code>pre</code> or
<code>prod</code> and a script <code>docker/nginx/startNginx.sh</code> for using this variable:<br />
<pre class="prettyprint">#!/bin/bash
if [ ! -f /etc/nginx/conf/servername.conf ]
then
ln -s /etc/nginx/conf/servername-$HOST_TARGET.conf /etc/nginx/conf/servername.conf
fi
nginx -g "daemon off;"
</pre>
Like before for the other containers, we build it and launch it with:<br />
<pre class="prettyprint">docker-compose up -d
</pre>
You should now have a full development host.<br />
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#application-logging" id="user-content-application-logging"><span class="octicon octicon-link"></span></a>Application logging</h3>
When launching, stopping, refreshing our services, Docker produces a log
for each container that you can easily access in your CLI:<br />
<pre class="prettyprint">docker-compose logs
# Or only for the db
docker-compose logs db
# Or only for the server
docker-compose logs server
# Or only for the server and the front...
docker-compose logs server front
# ...
</pre>
As you can see it, it can start to be a bit verbose. Still, you can inspect
any Docker container log with a tail like this:<br />
<pre class="prettyprint">$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
82a7489e41a0 docker_front "/startNginx.sh" 4 hours ago Up 4 hours 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp docker_front_1
4b0656669213 docker_server "./startMeteor.sh" 27 hours ago Up 4 hours 3000/tcp docker_server_1
fe6a7238328a docker_db "mongod -f /etc/mongo" 45 hours ago Up 4 hours 27017/tcp docker_db_1
1a878c646094 registry:2 "/bin/registry /etc/d" 46 hours ago Up 46 hours 0.0.0.0:5000->5000/tcp docker_registry_1
$ docker logs --tail 4 -f docker_db_1
2015-09-03T12:20:49.298+0000 I NETWORK [initandlisten] connection accepted from 172.17.0.64:49051 #22 (18 connections now open)
2015-09-03T12:20:49.314+0000 I NETWORK [initandlisten] connection accepted from 172.17.0.64:49052 #23 (19 connections now open)
2015-09-03T12:20:49.315+0000 I NETWORK [initandlisten] connection accepted from 172.17.0.64:49053 #24 (20 connections now open)
2015-09-03T16:36:13.666+0000 I QUERY [conn10] g...
</pre>
Docker logs are not regular <code>/var/log</code> entries. They are specific to each ones of
your container. There's an important risk to fill up your disk pretty fast depending
on you log usages. Fortunately, since Docker 1.8, specific log driver can be added
to our running container. We are using <code>logrotate</code> here but you could setup a
specific server for an <a href="https://www.elastic.co/webinars/introduction-elk-stack">ELK stack</a>
or any other of your favorite log solution. For configuring our <code>logrotate</code> on
each host, add a new configuration for Docker:<br />
<pre class="prettyprint">ssh root@$HOST_IP_DEV "echo -e '/var/lib/docker/containers/*/*.log { \n rotate 7\n daily\n compress\n size=1M\n missingok\n delaycompress\n copytruncate\n}' > /etc/logrotate.d/docker"
ssh root@$HOST_IP_PRE "echo -e '/var/lib/docker/containers/*/*.log { \n rotate 7\n daily\n compress\n size=1M\n missingok\n delaycompress\n copytruncate\n}' > /etc/logrotate.d/docker"
ssh root@$HOST_IP_PROD "echo -e '/var/lib/docker/containers/*/*.log { \n rotate 7\n daily\n compress\n size=1M\n missingok\n delaycompress\n copytruncate\n}' > /etc/logrotate.d/docker"
</pre>
Now, we are updating our <code>docker/docker-compose.yml</code> and set our Docker
containers to use the json-file log driver so that it doesn't stay buried
in <code>/var/lib/docker/containers/[CONTAINER ID]/[CONTAINER_ID]-json.log</code>:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
build: mongo
log_driver: "json-file"
volumes:
- /var/db:/db
expose:
- "27017"
# Application server: NodeJS (Meteor)
server:
build: meteor
log_driver: "json-file"
environment:
MONGO_URL: "mongodb://db:27017"
MONGO_OPLOG_URL: "mongodb://db:27017/local"
PORT: 3000
ROOT_URL: "https://192.168.1.50"
volumes:
- /etc/meteor:/etc/meteor
expose:
- "3000"
# Front layer, static file, SSL, proxy cache: NGinx
front:
build: nginx
log_driver: "json-file"
links:
- server
environment:
# Can be: dev, pre, prod
HOST_TARGET: "dev"
volumes:
- /etc/certs:/etc/certs
- /var/cache:/var/cache
- /var/tmp:/var/tmp
ports:
- "80:80"
- "443:443"
log_driver: "json-file"
</pre>
For taking this new logging configuration, just issue the following commands:<br />
<pre class="prettyprint"># This stops the current running containers
docker-compose stop
# This rebuild all images
docker-compose build
# This starts all containers
docker-compose up -d
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#push-to-your-local-registry" id="user-content-push-to-your-local-registry"><span class="octicon octicon-link"></span></a>Push to your local registry</h3>
When your are satisfied with the development of your container, you can save
your Docker images into your local registry for deploying them to preproduction.<br />
For Mongo:<br />
<pre class="prettyprint">docker tag -f docker_db $HOST_IP_DEV:5000/mongo:v1.0.0
docker push $HOST_IP_DEV:5000/mongo:v1.0.0
docker tag -f docker_db $HOST_IP_DEV:5000/mongo:latest
docker push $HOST_IP_DEV:5000/mongo:latest
</pre>
For Meteor:<br />
<pre class="prettyprint">docker tag -f docker_server $HOST_IP_DEV:5000/meteor:v1.0.0
docker push $HOST_IP_DEV:5000/meteor:v1.0.0
docker tag -f docker_server $HOST_IP_DEV:5000/meteor:latest
docker push $HOST_IP_DEV:5000/meteor:latest
</pre>
For NGinx:<br />
<pre class="prettyprint">docker tag -f docker_front $HOST_IP_DEV:5000/nginx:v1.0.0
docker push $HOST_IP_DEV:5000/nginx:v1.0.0
docker tag -f docker_front $HOST_IP_DEV:5000/nginx:latest
docker push $HOST_IP_DEV:5000/nginx:latest
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#deployment-in-pre-production" id="user-content-deployment-in-pre-production"><span class="octicon octicon-link"></span></a>Deployment in pre-production</h3>
For deploying to production, we are going to refactor our <code>docker/docker-compose.yml</code>
a bit to avoid repetition on Docker Compose file depending on the host that
you're tageting.<br />
We create a <code>docker/common.yml</code> file which centralized value used for all hosts:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
build: mongo
log_driver: "json-file"
volumes:
- /var/db:/db
expose:
- "27017"
# Application server: NodeJS (Meteor)
server:
build: meteor
log_driver: "json-file"
environment:
MONGO_URL: "mongodb://db:27017"
MONGO_OPLOG_URL: "mongodb://db:27017/local"
PORT: 3000
volumes:
- /etc/meteor:/etc/meteor
expose:
- "3000"
# Front layer, static file, SSL, proxy cache: NGinx
front:
log_driver: "json-file"
build: nginx
volumes:
- /etc/certs:/etc/certs
- /var/cache:/var/cache
- /var/tmp:/var/tmp
ports:
- "80:80"
- "443:443"
</pre>
Now, we can refactor our <code>docker/docker-compose.yml</code> to only set the remaining
Docker command required for development:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
extends:
file: common.yml
service: db
# Application server: NodeJS (Meteor)
server:
extends:
file: common.yml
service: server
links:
- db
environment:
ROOT_URL: "https://192.168.1.50"
# Front layer, static file, SSL, proxy cache: NGinx
front:
extends:
file: common.yml
service: front
links:
- server
environment:
# Can be: dev, pre, prod
HOST_TARGET: "dev"
</pre>
Now for easing the deployment on the pre-production hosts, we are using our
common configuration in a <code>docker/deploy-pre.yml</code> file that ease the pull and
launch of your services:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
image: 192.168.1.50:5000/mongo:v1.0.0
extends:
file: common.yml
service: db
restart: always
# Application server: NodeJS (Meteor)
server:
image: 192.168.1.50:5000/meteor:v1.0.0
extends:
file: common.yml
service: server
links:
- db
environment:
ROOT_URL: "https://192.168.1.51"
restart: always
# Front layer, static file, SSL, proxy cache: NGinx
front:
image: 192.168.1.50:5000/nginx:v1.0.0
extends:
file: common.yml
service: front
links:
- server
environment:
# Can be: dev, pre, prod
HOST_TARGET: "pre"
restart: always
</pre>
Connect Docker Machine to your pre-production host, start your services
and ensure that your ReplicationSet creation is applied:<br />
<pre class="prettyprint">eval "$(docker-machine env pre)"
docker-compose -f deploy-pre.yml up -d
docker-compose -f deploy-pre.yml run --rm db mongo db:27017/admin --quiet --eval "rs.initiate(); rs.conf();"
</pre>
Once you are satisfied with you containers, it's time to make them
available to your production server.<br />
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#push-to-docker-hub" id="user-content-push-to-docker-hub"><span class="octicon octicon-link"></span></a>Push to Docker Hub</h3>
Now we go back on our development host for publishing these container on
the public Docker Hub:<br />
<pre class="prettyprint">eval "$(docker-machine env dev)"
</pre>
And we publish our containers for Mongo:<br />
<pre class="prettyprint">docker tag -f docker_db YOUR_DOCKER_HUB_LOGIN/mongo:v1.0.0
docker push YOUR_DOCKER_HUB_LOGIN/mongo:v1.0.0
docker tag -f docker_db YOUR_DOCKER_HUB_LOGIN/mongo:latest
docker push YOUR_DOCKER_HUB_LOGIN/mongo:latest
</pre>
For Meteor:<br />
<pre class="prettyprint">docker tag -f docker_server YOUR_DOCKER_HUB_LOGIN/meteor:v1.0.0
docker push YOUR_DOCKER_HUB_LOGIN/meteor:v1.0.0
docker tag -f docker_server YOUR_DOCKER_HUB_LOGIN/meteor:latest
docker push YOUR_DOCKER_HUB_LOGIN/meteor:latest
</pre>
For NGinx:<br />
<pre class="prettyprint">docker tag -f docker_front YOUR_DOCKER_HUB_LOGIN/nginx:v1.0.0
docker push YOUR_DOCKER_HUB_LOGIN/nginx:v1.0.0
docker tag -f docker_front YOUR_DOCKER_HUB_LOGIN/nginx:latest
docker push YOUR_DOCKER_HUB_LOGIN/nginx:latest
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#deployment-in-production" id="user-content-deployment-in-production"><span class="octicon octicon-link"></span></a>Deployment in production</h3>
Like the deployment in pre-production, we are leveraging the capabilities
of Docker Compose for easing the pulling and running of Docker containers.
For this, we create a <code>docker/deploy-prod.yml</code> file:<br />
<pre class="prettyprint"># Persistence layer: Mongo
db:
image: YOUR_DOCKER_HUB_LOGIN/mongo:v1.0.0
extends:
file: common.yml
service: db
restart: always
# Application server: NodeJS (Meteor)
server:
image: YOUR_DOCKER_HUB_LOGIN/meteor:v1.0.0
extends:
file: common.yml
service: server
links:
- db
environment:
ROOT_URL: "https://YOUR_SITE_FQDN"
restart: always
# Front layer, static file, SSL, proxy cache: NGinx
front:
image: YOUR_DOCKER_HUB_LOGIN/nginx:v1.0.0
extends:
file: common.yml
service: front
links:
- server
environment:
# Can be: dev, pre, prod
HOST_TARGET: "prod"
restart: always
</pre>
Before running everything in production, we must pull our images. Behind the
scene so that our users doesn't notice the changes, then we will stop our current
running containers, launch our new ones and finish by a ReplicaSet configuration.<br />
<pre class="prettyprint">eval "$(docker-machine env prod)"
docker-compose -f deploy-prod.yml pull
docker stop "$(docker ps -a -q)"
docker-compose -f deploy-prod.yml up -d
docker-compose -f deploy-prod.yml run --rm db mongo db:27017/admin --quiet --eval "rs.initiate(); rs.conf();"
</pre>
<br />
<h3>
<a aria-hidden="true" class="anchor" href="https://www.blogger.com/blogger.g?blogID=8356785454747702427#links" id="user-content-links"><span class="octicon octicon-link"></span></a>Links</h3>
Sources for this tutorial:<br />
<ul>
<li><a href="https://github.com/PEM--/devops-tuts">Github's repository</a></li>
</ul>
Informations used for this tutorial:<br />
<ul>
<li><a href="http://brew.sh/">Homebrew</a></li>
<li><a href="https://github.com/caskroom/homebrew-cask">Caskroom</a></li>
<li><a href="http://pem-musing.blogspot.fr/2014/05/easy-sending-your-public-ssh-key-to.html">Easy sending your public SSH key to your remote servers</a></li>
<li><a href="https://docs.docker.com/">Docker documentation</a></li>
<li><a href="https://docs.docker.com/installation/ubuntulinux">Docker Installation on Ubuntu</a></li>
<li><a href="https://docs.docker.com/articles/https/">Secure Docker</a></li>
<li><a href="http://blog.viktorpetersson.com/post/101707677489/the-dangers-of-ufw-docker">The dangers of UFW + Docker</a></li>
<li><a href="https://www.madboa.com/geek/openssl/">OpenSSL Howto</a></li>
<li><a href="https://docs.docker.com/articles/systemd/">Control and configure Docker with Systemd</a></li>
<li><a href="http://nknu.net/how-to-configure-docker-on-ubuntu-15-04/">How to configure Docker on Ubuntu 15.04 (workaround)</a></li>
<li><a href="https://hub.docker.com/r/ulexus/meteor/">Ulexus/Meteor: A Docker container for Meteor</a></li>
<li><a href="https://www.ovh.com/fr/vps/vps-ssd.xml">VPS SSD at OVH</a></li>
<li><a href="https://docs.docker.com/docker-hub/accounts/">Your Docker Hub account</a></li>
<li><a href="https://blog.kayla.com.au/creating-a-single-instance-mongodb-replica-set-for-meteor/">Creating a single instance MongoDB replica set for Meteor</a></li>
<li><a href="https://stedolan.github.io/jq/">jq is a lightweight and flexible command-line JSON processor</a></li>
<li><a href="https://gist.github.com/xaviervia/6adea3ddba269cadb794">How to add environment variables to nginx.conf</a></li>
<li><a href="http://docs.mongodb.org/manual/reference/configuration-options/">MongoDB configuration options</a></li>
<li><a href="http://dba.stackexchange.com/questions/82591/sample-yaml-configuration-files-for-mongodb">MongoDB sample YAML files</a></li>
<li><a href="http://projectricochet.com/blog/magic-meteor-oplog-tailing#.Vd3eRlNRQVw">The magic of Meteor oplog tailing</a></li>
<li><a href="http://patg.net/containers,virtualization,docker/2014/06/10/using-docker/">Docker: Containers for the Masses -- using Docker</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-create-an-ssl-certificate-on-nginx-for-ubuntu-14-04">How To Create an SSL Certificate on Nginx for Ubuntu 14.04</a></li>
<li><a href="http://joshowens.me/ssl-and-meteor-js/?utm_content=buffera7818&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer">SSL and Meteor.js</a></li>
<li><a href="https://github.com/h5bp/server-configs-nginx">HTML5's boilerplate for NGinx servers</a></li>
</ul>
Going further:<br />
<ul>
<li><a href="https://www.tollmanz.com/http2-nghttp2-nginx-tls/">Deploying HTTP/2 and Strong TLS with Nghttp2 and Nginx</a></li>
<li><a href="https://timnash.co.uk/http2-0-with-nginx-nghttp2/">HTTP/2.0 with Nginx & NGHTTP2</a></li>
<li><a href="http://pierrepironin.fr/docker-et-mongodb/">Un serveur MongoDB sécurisé sur Docker</a></li>
<li><a href="http://blog.zol.fr/2015/08/06/travailler-avec-docker-sans-utilisateur-root/">Docker sans utilisateur root sur l'hôte et dans les containers</a></li>
</ul>
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-3720546032827146322015-06-27T19:53:00.002+02:002015-06-27T22:26:40.640+02:00PDF your collections, client side<h3>
Introduction</h3>
There are many ways to produce PDF on a website. You can use server side solutions for creating PDF, create CSS styles for print (and cross your finger waiting that all browser vendors will implement it) or create PDF on the client side. The two last solutions having the advantage not to take too much CPU on your infrastructure.<br />
<br />
<h3>
Leveraging PDFKit and SimpleSchema</h3>
<a href="http://pdfkit.org/" target="_blank">PDFKit</a> provides an almost complete isomorphic solution for that. It allows you to choose where you want to implement your PDF creation. Very nice. Unfortunately (or not), it only comes with low drawing primitives and for creating your PDF you will need some serious work on it.<br />
<br />
When creating templates and forms for our collections, most of us are relying on <a href="http://autoform.meteor.com/" target="_blank">Autoform</a>. This solution is so nice that we tend to forget how incredible the amount of work this solution is doing for us. We just create a <a href="https://github.com/aldeed/meteor-simple-schema" target="_blank">SimpleSchema</a> of our collection and now we can display them, fill them, check the integrity of data provided by our users, and so forth. An incredibly productive package suite.<br />
<br />
What if we could do the same for our PDF? That's what I was needing for a client. A solution that could take collections and create PDF out of them.<br />
<br />
<h3>
PDF Renderer</h3>
I've outsourced this solution in a package called <a href="https://atmospherejs.com/pierreeric/pdfrenderer" target="_blank">pierreeric:pdfrenderer</a>. Let's see a simple example on how to use it.<br />
<br />
First, we start creating our classic collection with a <a href="https://github.com/aldeed/meteor-simple-schema" target="_blank">SimpleSchema</a>. We tag the fields that we want to see in our PDF using a simple <span style="font-family: Courier New, Courier, monospace;">pdf: true</span> attribute:
<pre class="prettyprint">CustomerSchema = new SimpleSchema
name:
type: String
label: TAPi18n.__ 'name'
images:
type: String
label: TAPi18n.__ 'images'
optional: true
autoform: afFieldInput:
type: 'fileUpload'
collection: 'Images'
address:
type: Object
label: TAPi18n.__ 'address'
'address.street':
type: String
label: TAPi18n.__ 'street'
pdf: true
'address.city':
type: String
label: TAPi18n.__ 'city'
pdf: true
Customers = new Mongo.Collection 'customers'
Customers.attachSchema CustomerSchema
if Meteor.isServer
if Customers.find().count() is 0
Customers.insert
name: 'Mathilde Charpentier'
address:
street: '227, rue Camille de Richelieu'
city: 'Strasbourg'
Meteor.publish 'customers', -> Customers.find()
if Meteor.isClient
Template.svgTest.onCreated ->
sub = @subscribe 'customers'
@autorun =>
if sub.ready()
@customer = Customers.findOne()
Template.svgTest.helpers
customer: -> Template.instance().customer
</pre>
Now for creating the PDF when the user click on a button, we can pass to the PdfRenderer some fields or the complete collection:
<pre class="prettyprint">if Meteor.isClient
Template.svgTest.events
'click button': (e, t) ->
# Create the initial PDF document
pdf = new PdfRenderer size: 'a4'
# Load all required assets
pdf.addAsset "/cfs/files/images/#{t.customer.images}" if t.customer.images
# Use reactivity for loading assets if any
t.autorun ->
if pdf.ready()
# Customer image if exists
if t.customer.images?
pdf.img "/cfs/files/images/#{t.customer.images}", 'RIGHT',
width: 100
# Customer's name
pdf.h1 t.customer.name
# Address of customer
pdf.h2 TAPi18n.__ 'address'
pdf.schema CustomerSchema, 'address', t.customer
# End the PDF document, display it and enable back the PDF button
pdf.finish "file-#{t.customer.name}.pdf", ->
console.log 'PDF finished'
</pre>
You can see a rendered <a href="https://github.com/PEM--/pdfrenderer/blob/master/doc/file-mathilde-charpentier-pdf" target="_blank">PDF in the Github repository</a>.<br />
<br />
<h3>
Conclusion</h3>
This example is pretty simple. It could be enhance with theming and templating. Coupled with a CMS like <a href="http://orionjs.org/docs/introduction" target="_blank">Orion</a>, this could be an interesting block for covering e-commerce solution with automatic catalogue creation, web magazines with press capabilities, bookkeeping with automatic invoices generation, ... Hope you will like it and share your contributions on this little package. Happy coding.
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-6037560062327938012015-05-25T23:23:00.001+02:002015-05-25T23:27:30.158+02:00The Best Meteor IDEGeorge McKnight shows us how to install Atom for Meteor development.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="270" src="https://www.youtube.com/embed/QSg3mKjhiws" width="480"></iframe><br />
<br />
Thanks George for talking about our little plugins.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-41066182774085401352015-05-05T21:22:00.002+02:002015-05-05T21:22:15.948+02:00Meteor Ottawa and Ottawa Famo.us MeetupGadi grants us with a nice intro on using <a href="http://meteor.com/" target="_blank">Meteor</a> and <a href="http://famo.us/">Famo.us</a>. Tutorial inside! Thank Gadi and thank to Ottawa's community.<br />
<iframe allowfullscreen="" frameborder="0" height="270" src="https://www.youtube.com/embed/9lW3kOy2mAQ" width="480"></iframe>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-8005651579462189852015-04-19T17:10:00.002+02:002015-04-19T17:10:08.723+02:00Fullscreen D3 graphs<h3>
What are we going to tackle?</h3>
When creating D3 graphs, the more graphs you put in your page, the less readable it tends to be. A friend of mine advise me to set my graphs in fullscreen. I immediately loved the idea. That would definitely help users to focus.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-BV9JAz8D4f0N1srKOtjBG7vPK_OmOfhGZL8pfMD3cX41iQBsGKAgU_730S8FiO-Az0AlVtYeppi5lSzrq_25F2HuTpUS9uhV5ftURefq_O6rcbfULZI8XT2pCzIBs2IfKjfN_ARQZCQq/s1600/fullscreen.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-BV9JAz8D4f0N1srKOtjBG7vPK_OmOfhGZL8pfMD3cX41iQBsGKAgU_730S8FiO-Az0AlVtYeppi5lSzrq_25F2HuTpUS9uhV5ftURefq_O6rcbfULZI8XT2pCzIBs2IfKjfN_ARQZCQq/s1600/fullscreen.gif" /></a></div>
<br />
<h3>
What do we need to solve?</h3>
<div>
<ol>
<li>Our graphs need to be responsive: when they will be toggled on fullscreen, they need to expand and take as much space as possible.</li>
<li>When setting up fullscreen, the viewport is black despite all your already setup styles.</li>
<li>Tooltips on your graphs needs to be included in the fullscreen's viewport.</li>
<li>As your viewport is changing so are the references on where your tooltips needs to be displayed.</li>
</ol>
<h3>
Piece by piece solution</h3>
</div>
<div>
First, let's setup our markup in <b>Jade</b>. <span style="font-family: Courier New, Courier, monospace;">figure</span> is our SVG container, the <span style="font-family: Courier New, Courier, monospace;">div</span>, <span style="font-family: Courier New, Courier, monospace;">.svg-content</span>, is where D3 will place our SVG content and the <span style="font-family: Courier New, Courier, monospace;">div</span>, <span style="font-family: Courier New, Courier, monospace;">.tip</span>, is our tooltip for this graph:<br />
<pre class="prettyprint">figure
.svg-content
.tip
span Point
.arrow
</pre>
Now, we need to setup our style so that our SVG content takes the available width of its container or the width of the viewport when it will be set on fullscreen. Note that we also need to set the background color of the container so that we don't end up with a black viewport. Here is the relevant extract of the <b>Sylus</b> file:<br />
<pre class="prettyprint">// Container for responsive SVG
// Mixin for creating SVG container with specific aspect ratio
svgContainer()
display inline-block
position relative
width 100%
vertical-align middle
overflow hidden
figure
display inline-block
absolute top left
height 100%
margin 0
// This container targets 1:1 SVG
svgContainer()
.svg-content
// Here are the properties for the fullscreen content
&:fullscreen
size 100%
background white
</pre>
For the tooltip, we use a fixed position that will be modified using our logic. The style is pretty straightforward in <b>Stylus</b>:<br />
<pre class="prettyprint">.tip
fixed top left
</pre>
Now, for creating a responsive D3 graph, we need to use the <span style="font-family: Courier New, Courier, monospace;">preserveAspectRatio</span> and the <span style="font-family: Courier New, Courier, monospace;">viewBox</span> attributes on the graph instead of the regular <span style="font-family: Courier New, Courier, monospace;">width</span> and <span style="font-family: Courier New, Courier, monospace;">height</span> on the SVG tag. This is what is done by the following logic in <b>Coffee </b>on a 100x100 graph:<br />
<pre class="prettyprint">svgWidth = svgHeight = 100
svg = d3.select '.svg-content'
.append 'svg:svg'
.attr 'preserveAspectRatio', 'xMinYMin meet'
.attr 'viewBox', "0 0 #{svgWidth} #{svgHeight}"
</pre>
Setting the graph in fullscreen is eased thanks to <a href="https://github.com/sindresorhus/screenfull.js/" target="_blank">screenfull.js</a>. This library provide a cross browser API of the vanilla JavaScript Fullscreen API:<br />
<pre class="prettyprint">Template.home.rendered = ->
@fullscreen = ->
return screenfull.exit() if screenfull.isFullscreen
target = (@$ '.svg-content')[0]
screenfull.request target
Template.home.events
'click button': (e, t) ->
$button = t.$ e.target
role = $button.attr 'data-role'
t.fullscreen() if role is 'fullscreen'
</pre>
Now, we need to adjust the tooltips so that they are positioned properly. In our example, we are using a Voronoï diagram. Hovering on the <span style="font-family: Courier New, Courier, monospace;">path</span> elements of the Voronoï is triggering the tooltip of its points modeled by <span style="font-family: Courier New, Courier, monospace;">circle</span> elements. Getting the position is achieved thank to the DOM API <span style="font-family: Courier New, Courier, monospace;">getBoundingClientRect</span>:<br />
<pre class="prettyprint"> tip = @$ '.tip'
@positionSetTip = (circle) ->
rect = circle[0].getBoundingClientRect()
tip.css 'transform', "translate3d(\
#{rect.left + .5 * (rect.width - tip.width())}px,\
#{rect.top - tip.height()}px, 0)"
@showHideTip = -> tip.toggleClass 'show'
@showTip = -> Meteor.setTimeout (-> tip.addClass 'show'), 300
# A debouncing function is used for transitioning over path
@lazyShowHideTip = _.debounce @showHideTip, 300
path = svg.append 'g'
.selectAll 'path'
svg.append 'g'
.selectAll 'circle'
.data vertice
.enter()
.append 'circle'
.attr 'transform', (d) -> "translate(#{d.toString()})"
.attr 'r', 1
data = path.data (voronoi vertice)
data
.enter()
.append 'path'
.attr 'd', polygon
.order()
.on 'mouseover', (d, i) =>
circle = $ "circle:nth-child(#{i + 1})"
@positionSetTip circle, i
@showTip()
.on 'mouseleave', =>
@lazyShowHideTip()
data.exit().remove()</pre>
</div>
<h3>
Some links</h3>
<div>
<ul>
<li><a href="http://fullscreengraphs.meteor.com/" target="_blank">Demo of a Voronoï diagram set in fullscreen</a>.</li>
<li><a href="https://github.com/PEM--/FullscreenGraphs" target="_blank">Source of the demo is available on Github</a>.</li>
<li>The library used for easing the cross browser setup of the Fullscreen API: <a href="https://github.com/sindresorhus/screenfull.js/" target="_blank">screenfull.js</a>.</li>
<li><a href="http://creativepure.meteor.com/" target="_blank">Creative & Pure</a> is the used CSS framework for this little demo.</li>
</ul>
<div>
<h3>
Bonus</h3>
<div>
On the demo, I've also added some features like:</div>
<div>
<ul>
<li>An animated tooltip: when entering or leaving a small animation is done.</li>
<li>The Voronoï graph is animated using a simple random function.</li>
<li>Key events are handle to let you set the graph in fullscreen or to animate it.</li>
</ul>
<div>
Happy coding!</div>
</div>
</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-88915548054172518022014-12-29T00:07:00.003+01:002014-12-29T00:07:42.331+01:00Access all famo.us examples without downloading the starter kitOne neat trick that we have used on <a href="http://famous-views.meteor.com/" target="_blank">famous-views</a> to set some links to <a href="http://famo.us/">famo.us</a> examples is to use <a href="http://rawgit.com/" target="_blank">rawgit</a>. A nice service that allow exposing a <a href="https://github.com/" target="_blank">Github</a> page. Simply choose an <code>index.html</code> file from <a href="https://github.com/" target="_blank">Github</a> and make it available using <a href="http://rawgit.com/" target="_blank">rawgit</a>. Here is the link created for <a href="http://famo.us/">famo.us</a> examples: <a href="http://rawgit.com/Famous/famous/master/examples/index.html">http://rawgit.com/Famous/famous/master/examples/index.html</a>.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-3857930755753738212014-12-28T11:53:00.001+01:002014-12-28T17:51:07.706+01:00Debugging server side REST calls with mitmproxy in Meteor<h3>
Introduction</h3>
<div>
Isomorphic javascript offers a great flexibility for developper letting them choose where the code will get executed. On the clients or on the servers. While the Chrome DevTools have evolved at an impressive pace allowing to easily debug and see what is going on with your client's REST calls, the state of server side debugging may be a little bit deceptive in this field. Hence, when you need to put data fetching within your server, checking the flows of requests / responses could be a bit cumbersome if you solely rely on transaction logs or node-inspector. You could use technics like packet sniffing using Wireshark but setting a proper environment when using TLS with your REST call is a huge configuration process.</div>
<div>
<br /></div>
<div>
This is where <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> comes to the rescue. Its name says it all: Man In The Middle PROXY. This incredible tool relies on the same technics as the infamous security attack, this time, for a greater purpose. For this article, I'm using <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> as a local proxy server allowing me to trace all outgoing and incoming transactions from my server side Meteor app. Note that it is not the only use case that <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> offers. Actually, this tool is incredibly agile allowing you to debug your mobile apps with great ease. Let us put our white hat on.</div>
<div>
<br /></div>
<h3>
Installation on OSX</h3>
Using <a href="http://brew.sh/" target="_blank">homebrew</a> for installing <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> is a one command line call:
<br />
<pre class="prettyprint">brew install mitmproxy
</pre>
Note that this steps actually installs 2 tools <code>mitmproxy</code> and <code>mitmdump</code> as well as a set of certificates in <code>~/.mitmproxy</code>. We only use the first tool in this article but the second one could be handy when you need a lightweight display of your transaction flow and an easy way to save them in a replayable log. Note also that if you also intend to use <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> for debugging other applications like native client ones, you can accept the certificate <code>~/.mitmproxy/mitmproxy-ca-cert.pemcertificate</code> in OSX that <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> has created just for you. Just a caveat here. When you set your proxy settings in OSX, be sure to remove the local filters that Apple gently put for you if you want to see your native apps talking to your local servers.
<br />
<br />
Here's a little screenshot of a debugging session using <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a>:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLgLYsqlfxI7gE__ypXd5aBtz4fCoGTVnBEBOYoWGsxoQzkAxhFFrlXXIAsrrruWtKQzNNXNzXYBthVpX6Rst3h9oMwp_MCyjt9sUWf3Hn8wPsGGM1zkoAo6ZonVIqK6H_SNA99X5uMjHO/s1600/mitmproxy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLgLYsqlfxI7gE__ypXd5aBtz4fCoGTVnBEBOYoWGsxoQzkAxhFFrlXXIAsrrruWtKQzNNXNzXYBthVpX6Rst3h9oMwp_MCyjt9sUWf3Hn8wPsGGM1zkoAo6ZonVIqK6H_SNA99X5uMjHO/s320/mitmproxy.png" height="270" width="400" /></a></div>
<h3>
Not that isomorphic
</h3>
<div>
For REST calls, <a href="https://www.meteor.com/" target="_blank">Meteor</a> comes with its own isomorphic library: <a href="http://docs.meteor.com/#/full/http_call" target="_blank">HTTP</a>. On the client, it acts as basic AJAX calls, while on the server, it relies on the <a href="https://www.npmjs.com/package/request" target="_blank">request</a> module. Like all isomorphic libraries, this allows you to put your code logic in your clients or in your server with a nice and uniform API. But this comes at a price, it only covers the features that makes sense for both the clients and the server.</div>
<div>
<br /></div>
<div>
A server is not a browser. Indeed, while having cookies on the browser make sense for retaining states in your application for each user, cookies on the server starts to be a weird concept. Are these cookies used for the clients that you will serve or should the server could be seen as a client to another server? The same goes for the proxy settings on a server. Why would you need a proxy on a production server? Therefore, what is available on the client side of your app may not be available on the server side. </div>
<div>
<br /></div>
<div>
Unfortunately, you could have use cases where the REST API that you are using within your server may need cookies (which is a bit weird when speaking of REpresentational State Transfer which should rely on state-less operations ☺). Additionally, for debugging our flows, we also need the proxy capabilities that are offered out of the box by our browsers. This is where comes the nice package<a href="https://atmospherejs.com/dandv/http-more" target="_blank"> http-more</a> from <a href="https://atmospherejs.com/dandv" target="_blank">dandv</a>. It exposes some already baked in feature of the <a href="https://www.npmjs.com/package/request" target="_blank">request</a> module for your server side REST calls.<br />
<br />
You install it by replacing <a href="https://www.meteor.com/" target="_blank">Meteor</a>'s default package like so:<br />
<pre class="prettyprint">meteor remove http
meteor add dandv:http-more
</pre>
<br />
<h3>
Proxyfy your REST calls in your server</h3>
Now that our toolbox is setup, it is time to orient our server so that it takes advantages of <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a>. This is achieved via the freshly exposed options in the <a href="http://docs.meteor.com/#/full/http_call" target="_blank">HTTP</a> library.<br />
<br />
For orienting your request to the proxy, play nice with your TLS layer and setting a cookie jar:<br />
<pre class="prettyprint">HTTP.get url,
proxy: 'http://127.0.0.1:8080'
strictSSL: false
jar: true
, (e, r) ->
console.log 'GET sent through the proxy :-)'
</pre>
Now, you should see all your flows of your server on <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a>.<br />
<br />
<h3>
Some nice mitmproxy shortcuts and moves</h3>
<a href="http://mitmproxy.org/" target="_blank">mitmproxy</a>'s documentation is clear and very helpful. But before let you dive in, I'm sharing some of my most used shortcuts:<br />
<ul>
<li><kbd>C</kbd> to clear the list of flows.</li>
<li><kbd>↑</kbd> and <kbd>↓</kbd> for selecting a flow.</li>
<li><kbd>↵</kbd> for seeing the details of an exchange and <kbd>q</kbd> to go back to the list.</li>
<li><kbd>⇥</kbd> in detail of a flow to switch between the request and the response.</li>
</ul>
When you need to save a <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a> session for comparing it with other ones, you can use the save feature:<br />
<pre class="prettyprint">mitmproxy -w FILENAME
</pre>
<br />
Once saved, you can reread it and modify it to only spare the relevant parts of your flows:<br />
<pre class="prettyprint">mitmproxy -nr FILENAME
</pre>
Where:<br />
<ul>
<li><code>n</code> prevents to launch a proxy server.</li>
<li><code>r</code> read the previous saved file.</li>
</ul>
For the editing your flows, use the following shortcuts:<br />
<ul>
<li><kbd>d</kbd> for deleting flows.</li>
<li><kbd>e</kbd> for editing a request or a response.</li>
<li><kbd>w</kbd> then <kbd>a</kbd> for saving your all your modifications of the flows for the session.</li>
<li><kbd>q</kbd> then <kbd>y</kbd> for quitting edition mode.</li>
</ul>
Another nice move is <a href="http://mitmproxy.org/" target="_blank">mitmproxy</a>'s capabilities to replay a session. For instance, you can replay the client part of your spared session or the server part just by specifying -c or -s respectively. Very handy for debugging without assaulting the REST service that your implementing or using:<br />
<pre class="prettyprint">mitmproxy -c FILENAME
</pre>
</div>
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-11410760235799121722014-12-14T13:04:00.000+01:002014-12-14T13:14:21.346+01:00Put your HTML, CSS and JS in a single file using MeteorIt could be a bit cumbersome to switch from your HTML template file to your JS logic to your CSS stylesheets. I consider that the method exposed hereafter isn't tailored for novice users. Still, it can help you understanding how <a href="https://www.meteor.com/" target="_blank">Meteor</a> works.<br />
<br />
Actually, the method is quite simple. It all drills down to how the HTML files are generated using <a href="https://www.meteor.com/blaze" target="_blank">Blaze, the reactive UI</a>. During the build process of your app, <a href="https://www.meteor.com/" target="_blank">Meteor</a> builds JS file out of your HTML template file and exposes / sends them to your clients. This method short-circuits the build process. If you write directly your HTML using <a href="https://www.meteor.com/blaze" target="_blank">Blaze, the reactive UI</a>, you remove the HTML file. The second step is to use JS to write your CSS. Here, I use the <a href="https://atmospherejs.com/pierreeric/cssc" target="_blank">CSSC package</a> for Meteor.<br />
<br />
Here is the results for an equivalent of the default <a href="https://www.meteor.com/" target="_blank">Meteor</a> app.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTqbjQPy_eItyZGKMy4QGAA1WZdCZD7crUk-m9GBuIRwAJeaET-hvo0fLwQqbUYZu7R_i3Kx6kYgCIsqTdG1cQQ0e_Hcei9cAkSLOYvkpc713pYlV-7rSn1dEQJ6nS-7oj6hI3px6s52ZS/s1600/singlefile.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTqbjQPy_eItyZGKMy4QGAA1WZdCZD7crUk-m9GBuIRwAJeaET-hvo0fLwQqbUYZu7R_i3Kx6kYgCIsqTdG1cQQ0e_Hcei9cAkSLOYvkpc713pYlV-7rSn1dEQJ6nS-7oj6hI3px6s52ZS/s1600/singlefile.gif" /></a></div>
You could think of it as a small component into a larger app.<br />
<br />
And now, here is the code to create this small component. As promised, a single file, in CoffeeScript for brevity:<br />
<pre class="prettyprint"># Render the body when Meteor is ready
Meteor.startup Template.body.renderToDocument
# HTML
# ----
# Instantiate a main template
Template['main'] = new Template 'Template.main', ->
# Return a table of DOM elements
[
# A 'p' tag with a reactive variable
HTML.P 'Count ', Blaze.View => Spacebars.mustache @lookup 'count'
# A 'button' tag
HTML.BUTTON '+1'
]
# JS logic
# --------
# Declare a reactive variable
Session.set 'count', 0
# Expose the reactive variable to the template
Template.main.helpers 'count': -> Session.get 'count'
# Handle template events
Template.main.events
'click button': (e,t) ->
e.preventDefault()
Session.set 'count', 1 + Session.get 'count'
# Add the template to the document's body
Template.body.addContent ->
Spacebars.include @lookupTemplate 'main'
# CSS
# ---
# Instantiate a stylesheet
css = new CSSC
# Style the body and the text
css.add 'body',
margin: CSSC.px 20
backgroundColor: CSSC.navy
color: CSSC.aqua
# Style the button
.add 'button',
width: CSSC.px 50
border: "#{CSSC.px 1} #{CSSC.maroon} solid"
borderRadius: CSSC.px 3
backgroundColor: CSSC.red
color: CSSC.yellow
</pre>
<br />
If you want to test it yourself, here is the set of required commands:<br />
<pre class="prettyprint"># Start by creating a simple app.
meteor create singlefile
cd singlefile
# Remove the Meteor's default app.
rm *
# We are going to focus only on the front.
# Our single file is being placed in the client directory.
mkdir client
# Add the necessary packages.
meteor add coffeescript pierreeric:cssc pierreeric:cssc-colors pierreeric:cssc-normalize
# Create your single file and start editing it.
touch client/main.coffee
</pre>
<br />
At the beginning, it may look a bit weird. But this could be useful from time to time. Note that you can put some nice additions to this sample. For instance, instead of using the events helpers which will scan you DOM for the button, you could directly affect the click event logic when you are creating the button element. If you are looking for performances on some parts of your app, this could be an interesting technic.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-15750153658735973282014-12-10T11:53:00.002+01:002014-12-10T11:53:53.786+01:00The fastest loading screen for Meteor<h3>
Introduction</h3>
<a href="https://www.meteor.com/" target="_blank">Meteor</a> offers a great way to build web apps at an incomparable speed. It leverages the power of Javascript anywhere. In production, its powerful server side build system named <a href="https://www.meteor.com/isobuild" target="_blank">Isobuild</a> transpiles all your HTML files into JS files, thanks to <a href="https://www.meteor.com/blaze">Blaze</a>, and packs them with your JS files. This operation leads to a single JS file loaded by a tiny initial HTML loader that bootstraps your app.
<br />
<br />
Depending on your development strategy, this single JS file can end up very fat. This could seriously annoy your first time users who will wait many seconds before seing something on their screen. Of course, proxying your servers with Gzip or <a href="http://pem-musing.blogspot.com/2014/03/spdy-page-load-time-shortens-like-no.html" target="_blank">SPDY</a> capable fronts greatly helps lowering this initial loading. Truncating this JS file could also help. Using a multiple domains strategy and CDNs also offer great loading time cuts. Still, on mobile, with a bad network access like GPRS, these several seconds rapidly become a problem. To understand the ins and outs of web performance optimization, WPO, I strongly recommend reading the book <a href="http://pem-musing.blogspot.fr/2013/12/un-livre-de-chevet-sur-le-metier-wpo.html" target="_blank">High Performance Browser Networking by Ilya Grigorik</a>.<br />
<br />
<h3>
What is this about?</h3>
Not to be confused with the great masterpiece <a href="https://github.com/meteorhacks/fast-render/" target="_blank">fast render</a> from <a href="https://twitter.com/arunoda" target="_blank">Arunoda Susiripala</a> which is used for fastening the initial data required from your collections, the technic that I expose hereafter tackles the initial page load time, PLT. The first paints on the screen, if you prefer. This first paint comes even before the tiny initial HTML loader is fully loaded by your users's terminal.<br />
<br />
On a regular Meteor app, while your users connect to your site and till the initial load of the JS file, their browser remains blank. Of course, they could see that something is happening. Almost all browser display at least a progress indicator. But users may not notice it and think that your site is either dead or hosted on a distant other planet. That is just very bad for user acquisition, isn't it? If you want to better understand how this affects user's perceptions as well as your search engine optimization strategy, SEO, Paul Irish has covered this in great details in his incredibly informative talk: <a href="http://timkadlec.com/2014/01/fast-enough" target="_blank">Fast enough</a> (check the comments for the slides and the Youtube video). What you need to know is that your PLT will be nice if your initial screen painting is under 14k. That seems like an impossible goal to achieve with an out-of-the-box <a href="https://www.meteor.com/" target="_blank">Meteor</a> app with an average weight of ~50k of compressed initial JS file.<br />
<br />
But <a href="https://www.meteor.com/" target="_blank">Meteor</a> is far from being a classic website approach and is definitely not a low end build tool. You can use the raw power of its <a href="https://www.meteor.com/isobuild" target="_blank">Isobuild</a> to ask your servers for an injection of your initial screen content directly within the tiny initial HTML loader. It is the key to unlock the 14k barrier.<br />
<br />
<h3>
Performing the injection</h3>
This could be a bit scary for the majority of <a href="https://www.meteor.com/" target="_blank">Meteor</a> devs. But once again, <a href="https://twitter.com/arunoda" target="_blank">Arunoda</a> has you well covered. By using another great masterpiece from <a href="https://twitter.com/arunoda" target="_blank">Arunoda</a> and <a href="https://github.com/gadicc" target="_blank">Gadi Cohen</a>, this injection process is a no brainer: <a href="https://github.com/meteorhacks/meteor-inject-initial" target="_blank">inject initial</a>. Let us jump in the code.<br />
<br />
In your app, installing <a href="https://github.com/meteorhacks/meteor-inject-initial" target="_blank">inject initial</a> is the one step classical stance:
<br />
<pre class="prettyprint">meteor add meteorhacks:inject-initial
</pre>
For the sake of brevity, the codes hereafter are in CoffeeScript.<br />
<br />
In your server code, you can perform the initial injection. Hereafter, I'm just putting a little CSS spinner. Nothing fancy.<br />
<br />
<strong>/server/startup.coffee</strong>
<br />
<pre class="prettyprint">Inject.rawHead 'loader-style',
# Force the initial scale for Android and iOS as our spinner may be
# distorted by their default viewport values.
'<meta name="viewport" content="width=device-width,maximum-scale=1,' +
'initial-scale=1,user-scalable=no">' +
# The loading spinner needs some theming.
'<style>' +
'html{background-color: #36342e;}' +
'body{color:#ddd;overflow:hidden;width:100%;}' +
'.spinner {' +
'bottom:0;height:80px;left:0;margin:auto;position:absolute;' +
'top:0;right:0;width:80px;' +
'-webkit-animation: rotation .6s infinite linear;' +
'animation: rotation .6s infinite linear;' +
'border-left:6px solid rgba(255,194,0,.20);' +
'border-right:6px solid rgba(255,194,0,.20);' +
'border-bottom:6px solid rgba(255,194,0,.20);' +
'border-top:6px solid rgba(255,194,0,.9);' +
'border-radius:100%;' +
'}' +
'@-webkit-keyframes rotation {' +
'from {-webkit-transform: rotate(0deg);}' +
'to {-webkit-transform: rotate(359deg);}' +
'}' +
'@-moz-keyframes rotation {' +
'from {-moz-transform: rotate(0deg);}' +
'to {-moz-transform: rotate(359deg);}' +
'}' +
'@-o-keyframes rotation {' +
'from {-o-transform: rotate(0deg);}' +
'to {-o-transform: rotate(359deg);}' +
'}' +
'@keyframes rotation {' +
'from {transform: rotate(0deg);}' +
'to {transform: rotate(359deg);}' +
'}' +
'</style>'
# The loading spinner is a CSS animation.
# /!\ WARNING: The trick is to create a fake body by injecting data
# in the HTML's head as Meteor is requesting JS file in a blocking
# fashion and mobile only allow 1 HTTP request at a time on a GPRS network.
Inject.rawHead 'loader-body2', '<body><div class="spinner"></div></body>'
</pre>
Here we have somewhat altered, the DOM structure of our app. Indeed, we have just created a <strong>body</strong> while the <strong>head</strong> of your your initial HTML loader wasn't even finished to get build. Is that a big problem? No. Your server will carry on filling the rest of your app in the <strong>body</strong> section that you have created (the styles, the scripts, ...).<br />
<br />
At this point, you could tell me: "But wait? There is now a <strong>title</strong> and <strong>meta</strong> tags in the <strong>body</strong> of my app. That's just a disgusting code.". You are right. But the browsers are really not impacted by this. I could reply back: "Who care?". But, indeed, I care. Simply because one of the biggest interest of web technology is that all your codes are readable. It is important to let other developers having the possibility to understand how stuff works. It is part of the learning legacy that we owe to the web. So let us put things in the correct order.<br />
<br />
Cleaning our loaded codes is done directly on the client side. We do that once everything is loaded. No hurry as it has no impact except a good readability of our DOM structure.<br />
<br />
<strong>/client/startup.coffee</strong>
<br />
<pre class="prettyprint">Meteor.startup ->
# Reshape DOM: put back title and meta elements in the head.
# style and script tags can leave in the body tag.
$head = $ 'head'
for tag in ['meta', 'title']
$tags = $ tag
$head.append $tags.clone()
$tags.remove()
</pre>
That's it. Nice and clean.
<br />
<br />
<h3>
Testing it</h3>
There are several ways of testing these results and their behavior. On Mac, I use <strong>Network link conditionner</strong>, a handy tool provided by Apple. It allows you to modify the behavior of you TCP stack and use GPRS, 3G, ... profiles. Very neat when you are testing hybrid or native apps.<br />
<br />
If you are testing full web apps, I strongly recommend using Chrome DevTools. The mobile emulator as a special feature allowing to change the network throttling.
<br />
<br />
<h3>
Some constraints you need to be aware of</h3>
When your initial HTML loader and your initial JS file are being loaded, the JS engine and the SVG rendering engine are paused. I've always been annoyed by this browser behavior but we have to live with it. Thus, you can't put JS loaders or SVG animated loaders. You should favor CSS loader and choose the one that will be the less CPU intensive. Here, you should favor CSS3 animated loader which are animated by the GPU as the CPU will be intensively interpreting what it is finishing pumping from you servers.<br />
<br />
A side note to finish this little article. This technic is not here to bypass the necessary work of making your app as lightweight as possible. It is still a good practice to ensure that your app will avoid consuming the data subscription of your users ;-)pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-8017570839696323132014-12-08T11:07:00.001+01:002014-12-08T11:07:36.898+01:00What is famous-views?<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="394" src="https://www.youtube.com/embed/wMmllcFMEio?loop=1" width="700"></iframe></div>pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-23862727455771544102014-12-07T23:20:00.002+01:002014-12-07T23:20:34.001+01:003D hardware accelerated SVG using famo.us and MeteorOne of the main problem with SVG for Android or Chrome, desktop and mobile, is the lack of hardware acceleration. Multiple issues also prevents some nice effects like skewing for instance. Making smooth animated SVG in a browser or in Cordova tends to be tricky.<br />
<br />
As stated in one of my former <a href="http://pem-musing.blogspot.fr/2014/11/responsive-svg-devices-for-famous-and.html">blog post</a>, <a href="http://famo.us/">famo.us</a> makes a good candidate for removing this pain and <a href="https://www.meteor.com/">Meteor</a>, with its <a href="http://famous-views.meteor.com/">famous-views</a> package, makes a good candidate for tying up everything together.<br />
<br />
This weekend, I've started working on a <a href="http://famous-views.meteor.com/">famous-views</a>'s plugin named <a href="https://github.com/PEM--/fview-svg">fview-svg</a>. Basically, it reads your SVG as template and create <a href="http://famo.us/">famo.us</a>'s <code>Surface</code> and <code>StateModifier</code> out of it. One of the sensitive parts of the process was the inverted coordinate system of SVG compare to DOM and SVG's responsive nature. But the beauty of making it a plugin with <a href="http://famous-views.meteor.com/">famous-views</a> is that it will be available for anyone. Nice.<br />
<br />
Here is a little video that demonstrates my first demo with <a href="https://github.com/PEM--/fview-svg">fview-svg</a>:<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/WBndazVqHQs" width="560"></iframe></div>
<br />
As you can check it in the source code, without the comments, this demo is less than 20 lines of codes. And there is room for improvements.<br />
<br />
I've deployed a live demo and you can play with it: <a href="http://fview-svg.meteor.com/">fview-svg</a>.<br />
<br />
I've tested on desktop in Safari, Chrome, Firefox and Opera. On iOS8, I've tested it, an iPad Mini, an iPad 2, an iPhone 5S. On Android, I've tested it on release 4.4.2 on a Samsung Galaxy Tab and on release 4.0.3 on an HTC Desire C. And this is this last test that just surprised me. You may not even know what this terminal is. It's a low end smarphone. Very cheap. A single core under 1GHz. What Android call a <code>midpi</code>. These terminals still equip a large portion of Android's market share. Just to give you a glimpse on these terminals, it doesn't even run the first Angry birds properly... And here, the SVG animation was perfect. Fluid. I almost shed a tear.<br />
<br />
The source codes of the plugin as well as a tutorial for recreating this demo are available on Github: <a href="https://github.com/PEM--/fview-svg">fview-svg</a>.<br />
<br />
<b>Note</b>: The plugin is not already available on <a href="https://atmospherejs.com/">Atmosphere</a>, the official <a href="https://www.meteor.com/">Meteor</a> repository. I wand to test it against several additional demos to check which API would be good to export for easing the integration. Still, you can start cloning it and import it in your project if you want to contribute or cannot wait few days.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-87618969394875366582014-12-01T15:15:00.002+01:002014-12-01T15:15:27.208+01:00Some Famo.us demo with MeteorLately with <a href="http://famous-views.meteor.com/">famous-views</a>, a package for <a href="https://www.meteor.com/">Meteor</a>, we have added some plugins for easing developments. To better demonstrate how to use them and what they bring, I've added some live demo for each one that I've created.<br />
<br />
Here is a fast access to these demo:<br />
<ul>
<li><a href="http://fview-bksurfaceimage.meteor.com/">fview-bksurfaceimage</a>: Adaptive images.</li>
<li><a href="http://fview-devices.meteor.com/">fview-devices</a>: Responsive SVG devices working as container for demoing apps.</li>
<li><a href="http://fview-dotloader.meteor.com/">fview-dotloader</a>: Simple and customizable loader.</li>
<li><a href="http://meetupfamousslides.meteor.com/">fview-slidedeck</a>: Slide deck engine à la Bespoke, Reveal, Impress, Prezzi.</li>
<li><a href="http://fview-flexgrid.meteor.com/">fview-flexgrid</a>: Flexible grid.</li>
</ul>
<br />
<br />pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0Paris, France48.856614 2.352221900000017748.688561 2.0281254000000177 49.024667 2.6763184000000177tag:blogger.com,1999:blog-8356785454747702427.post-46270608110110707182014-11-30T21:18:00.003+01:002014-11-30T21:18:33.461+01:00Responsive SVG devices for famo.us and Meteor<h3>
Introduction</h3>
When creating a landing page or when demonstrating an app, there are often some smartphone, tablet or desktop screenshots. This pattern of demonstration could be a bit enhanced if instead of simple screenshots, the real app could be demonstrated. That would be the power of HTML5 sets in motion.<br />
<br />
<h3>
The base component</h3>
<a href="http://famo.us/">Famo.us</a> has a nice component that could be leverage for that: the <span style="font-family: Courier New, Courier, monospace;">ContainerSurface</span>. It is basically a small context that can be clipped. By living in the same space of your main web site, it allows interesting demonstration patterns where you could demonstrate your app. The problem is to display this context exactly on the device that you want to present.<br />
<br />
By using an adaptive or a responsive SVG, you can extract coordinates of the device screen where you want to present your app. You can then instantiates a <span style="font-family: Courier New, Courier, monospace;">ContainerSurface</span> on this same coordinates and put you app as its content.<br />
<br />
In this little plugin <a href="https://github.com/PEM--/fview-devices">fview-devices</a> for <a href="http://famous-views.meteor.com/">famous-views</a>, a <a href="https://www.meteor.com/">Meteor</a> package, this is exactly what is done. Here is a little demonstration of it:<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/O3iTpqSpjqU" width="560"></iframe></div>
<br />
You can also play with it in this live demo: <a href="http://fview-devices.meteor.com/">fview-devices</a>.<br />
<br />
<h3>
Conclusion</h3>
Beyond this little example, some interesting points emerge such as the capability to mix SVG and <a href="http://famo.us/">Famo.us</a>. It paves the way to further enhancements. For instance, CSS Transform and Animation are possible in SVG. Unfortunately, they are not hardware accelerated. Worst, some browsers have very uneasy bugs to circumvent. By extracting portions of SVG and putting them into <a href="http://famo.us/">Famo.us</a> surfaces, you regain hardware acceleration and circumvent issues from wrong implementations. Basically, you open up wider the capability to use SVG to the mobile platforms.<br />
<br />
It seems to me that a new story of apps using SVG, <a href="http://famo.us/">Famo.us</a> and <a href="https://www.meteor.com/">Meteor</a> is about to begin. Happy coding.<br />
<br />pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0tag:blogger.com,1999:blog-8356785454747702427.post-73564905412083693392014-11-30T18:18:00.003+01:002014-11-30T18:23:15.632+01:00Famo.us dot loader animation using Meteor's Famous-ViewsA simple customizable dot loader ported for <a href="http://famous-views.meteor.com/">famous-views</a>:<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/u3gHui88WnM" width="560"></iframe></div>
<br />
Source code: <a href="https://github.com/PEM--/fview-dotloader">fview-dotloader</a><br />
<br />
Demo site: <a href="http://fview-dotloader.meteor.com/">fview-dotloader</a><br />
<br />
Original work: <a href="http://famousco.de/2014/07/animated-dots-icon/">LeXXik and Talves</a>.pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0tag:blogger.com,1999:blog-8356785454747702427.post-26144982762652603822014-11-25T23:57:00.000+01:002014-11-26T00:13:46.968+01:00Famo.us FlexGrid in CoffeeScript with Meteor<div style="text-align: justify;">
Did I just post something similar few hours ago? <a href="http://pem-musing.blogspot.fr/2014/11/famous-flexgrid-in-coffeescript.html">Indeed very similar</a>. But this time, this goes on a very different scale. The same demonstration is now reactive with datasources coming from a MongoDB, your session or any reactive dictionary that Meteor can provide. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/NhPXZZ-iZ1E" width="560"></iframe></div>
<br />
<div style="text-align: justify;">
But wait... There's more. Actually, this is not a piece of code that you will paste into your Meteor project. It's a plugin. One command and one line of templating and it is set. One line of templating means: <b><span style="font-size: x-large;">in just one single line of code.</span></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
This is all made possible thanks to the efforts of Gadi Cohen's excellent package's <a href="http://famous-views.meteor.com/">Famous-Views</a> and his community (in which I contribute a bit).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://famous-views.meteor.com/"><img border="0" src="http://famous-views.meteor.com/meteor_famous_view_logo.svg" height="165" width="200" /></a></div>
<br /></div>
<div style="text-align: justify;">
Basically, here is how it works. Famo.us components are registered in the Meteor's templating engine. It can be regular components or home made ones. Using your favorite templating language, being Spacebar or Jade, you create the layout of your app. You connect your reactive datasource in your templating if they are already available or via small template helpers à la Meteor. It's like Famo.us/Angular with a full stack JS framework. And if you are using Jade, it is like drawing the Famo.us rendering tree with your code and connect it with reactive data source.<br />
<br />
As an example, here is the code required for creating the example displayed in the video:</div>
<pre class="prettyprint">// This is pretty basic for every mobile app.
head
title FlexGrid test
meta(charset='utf-8')
meta(name='viewport', content='width=device-width, maximum-scale=1, user-scalable=no')
meta(name='apple-mobile-web-app-capable', content='yes')
meta(name='apple-mobile-web-app-status-bar-style', content='black')
meta(name='apple-mobile-web-app-capable', content='yes')
meta(name='mobile-web-app-capable', content='yes')
body
// Every Famo.us apps starts with a context.
+famousContext id="mainCtx"
// The code is divided into reusable templates
// looking very much like web components.
+flexGridExample
template(name='flexGridExample')
// Now we create our 'render tree'.
+Scrollview id='scrollview'
// Here is the FlexGrid: it's indeed a single line of code!
+FlexGrid id='flexgrid'
// Here, I connect my datasource: a reactive array.
+famousEach items
// I use the available values in each item of the array
// Note that CSS styles can be passed as reactive value,
+Surface class='surf' properties=color
// Or as reactive content.
p= name
</pre>
<div style="text-align: justify;">
<br />
Simple. The community has also brought some others nice plugins that speeds up the creation of your app. The number of plugins available is growing fast.</div>
pemarchandet@gmail.comhttp://www.blogger.com/profile/03147107118904598760noreply@blogger.com0