himdel

Segmentation fault

2013-07-24

Deploying node.js with apache & daemontools

So, I've started developing a node.js app and I have to deploy it somehow.

There are some deployment tutorials, such as How to Deploy Node JS Applications, With Examples (monit, apache and iptables port redirect) and Deploying Node.js with systemd (systemd, duh) but neither really suits me completely.


The Goals:

  • automatic start on boot
  • restart on git push
  • automatic restart on nodejs crash
  • output redirection to syslog
  • multiple node.js apps on arbitrary subdomains/paths
  • should mix well with non-node.js servers
  • no fancy init dependencies, must work with sysvinit but should not depend on it
  • should use packages available in vanilla debian
  • should not rely on firewall hacks

I don't care about socket activation and namespace isolation for now but it may become a concern later so an *inetd compatibile solution is preferred.

Also, I prefer to go with solutions I'm already familiar with, if they fit.

What the "multiple node.." and "should mix well.." requirements really mean is that it should be possible to, for example, have api.example.com/v1/ running PHP and api.example.com/v2/ running node.js.


The How:


The problem splits nicely into several distinct tasks, each of which will be handled separately:

url handler - a tool that listens on a set of domains and hands requests to their proper handlers based on domain/path combination - I'll go with Apache & ProxyPass
process management - start on boot and autorestart, traditionally handled by init but more on that later
a git hook - simply needs to kill the right process on update, pretty much a non-issue, except for the need to kill the right process and hence a pidfile
log redirector - a tool to take process output and log it

Assumptions

For the rest of this post, I'll assume you have node.js installed in /usr/local, bare git repo in /git/node-app and the app itself in /srv/node-app. I'll assume your app is the node.js sample app.js, listening on port 8124 and that you want to serve it on api.example.com/v1.


url handler


First off, we'll set up apache:

apt-get install apache2
a2enmod proxy
a2enmod proxy_http
service apache2 restart



We need mod-proxy for ProxyPass but don't enable ProxyRequests unless you need to.

So let's create a virtualhost:
vim /etc/apache2/sites-available/node-app

<VirtualHost *:80>
  ServerName api.example.com
  ProxyPass /v1 http://localhost:8124
</VirtualHost>


a2ensite node-app
service apache2 reload

As the docs says, balance the slashes, no slash after v1 means no slash after 8124 .. and vice versa.
Obviously you can extend the virtualhost with anything else - more ProxyPass directives, ProxyPassMatch to match paths akin to mod_rewrite, a DocumentRoot for static / apache-native contents...

If you try to access the proxied url and get a 500 Internal Server Error together with [warn] proxy: No protocol handler was valid for the URL /v1. If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule. in the logs, you forgot to enable the proxy_http submodule (or are using different ProxyPass target protocol).


process management


I was really, really tempted to simply go with /etc/inittab here, that's pretty much all I need, simply adding
n0:23:respawn:/usr/local/bin/node /srv/node-app/app.js
and running telinit q does the job.

However, server management via changing inittab doesn't feel quite right, the command would be quite a bit longer because of the need to wrap the invocation with a logger and a pidfile manager, it's incompatible with *inetd and depends on old-init (neither runit, upstart or systemd support inittab).

I'm aware of monit but I've never used it, so I'll go with djb's daemontools, this also removes the need for a separate pidfile manager (but means the git hook will be daemontools-specific).

debian-specific:
apt-get install daemontools daemontools-run
The service dir is /etc/service instead of the non-FHS-compliant /service the djb uses.

The setup is pretty much the setup from daemontools faq.

mkdir /etc/service/node-app
vim /etc/service/node-app/run

#!/bin/bash

# log stderr as well
exec 2>&1

# simply run node js
exec /usr/local/bin/node /srv/node-app/app.js


chmod -R 755 /etc/service/node-app

Note that the exec 2>&1 line is a bashism, /bin/sh need not support it.


a git hook


Deployment with git is not really in the scope of this post and you probably already have your own solution anyway.
If all you need to do is pull the app dir on git push and restart the server, this will do, assuming proper rights:

vim /git/node-app/hooks/post-receive

#!/bin/sh

# pull
unset GIT_DIR
cd /srv/node-app/
git pull

# restart node
svc -t /etc/service/node-app


chmod +x /git/node-app/hooks/post-receive

If you had used a pidfile-base process management solution, the restart line would look more like
kill `cat /run/node-app.pid`
instead.


log redirector


Another easy part - we want to log from a pipe to syslog, that's what logger(1) is for. So we amend the service config with:

mkdir /etc/service/node-app/log
vim /etc/service/node-app/log/run

#!/bin/sh
exec /usr/bin/logger -t node-app


chmod -R 755 /etc/service/node-app/log

Aand that's it really. You may want to setup your syslog to use a separate file and logrotate it, or you can use daemontools' multilog if you don't want to use syslog.

If your daemon runs fine but fails to log anything, you need to restart the appropriate supervise process. Apparently supervise doesn't check for new /log subdir if it's already running, even if you call svc -d and svc -u.


2009-04-12

Migrating request-tracker (from rt-3.4.5 on mysql to rt-3.8.2 on postgres)

I've recently had the "pleasure" of upgrading our company's request tracker system and migrating it from MySQL to Postgres and I'd like to share what I found out.

The process of installing and configuring the request-tracker-3.8 package from Debian experimental is considered obvious (and user-specific) and won't be covered here.

First of all, all the data had to be migrated, except for the 'sessions' table which should be created empty (what would we need old sessions for).


export LC_ALL=C
TABLES=ACL Attachments Attributes CachedGroupMembers CustomFieldValues CustomFields GroupMembers Groups Links ObjectCustomFieldValues ObjectCustomFields Principals Queues ScripActions ScripConditions Scrips Templates Tickets Transactions Users
for foo in $TABLES; do echo $foo; mysqldump rtdb "$foo" --default-character-set=utf8 --compatible=postgresql --compact -t -u rtuser --password=omitted -r rtdb-"$foo"-`date +%s`.sql; done


I chose to export each table into a different file to ease handling and because vim takes too long on 400MB files.

Mysqldump may have the --compatible=postgresql option, but don't expect the output will be postgresql compatible .. but maybe it used to at some point, nowadays, Postgres won't accept string fields 'like \' \\this', but only 'like '' \this' or you have to explicitly tell it to see escaping .. E'like \' \\this'.

Also, postgres won't handle the quotes in INSERT INTO "Table" .. .


perl -i -npe 's/^INSERT INTO "(\w+)"/INSERT INTO \L$1/; '"s/,'/,E'/g;" rtdb-*.sql


Worked just fine.

Seeing the inserts, I really didn't want to handle transmogrifying the CREATE TABLE statements and such, so I exported a freshly created pg-8.3 database from rt-3.8.2, removed any data from the dump and split it into the HEAD (the part before data) and TAILS (the part after).

The only schema difference that had to be handled was in the table CustomFields, where order of columns was changed.


perl -i -npe 's/INSERT INTO customfields VALUES/INSERT INTO customfields (id,name,type,maxvalues,pattern,repeated,description,sortorder,lookuptype,creator,created,lastupdatedby,lastupdated,disabled) VALUES/' rtdb-CustomFields-*.sql


Explicit insert helped :).

This made the data ready to be loaded to a new, empty database. Almost.

For some reason RT uses text fields for saving mail content so non-utf8 mails couldn't be imported into the UTF-8 database .. after struggling long and hard, I didn't actually find a proper way to convert it so I used a quick and dirty hack and simply stripped all diacritics from the input data .. the language was Czech and cp1250, iso-8859-2 and utf-8 encodings were used .. I wrote a simple diacritics stripper for Czech that doesn't need to know the source encoding (because various were mixed in a single table dump) .. so that's what the zz_subs filter does in the following command.


psql rtdb < ok/HEADS
for foo in rtdb-*.sql; do echo $foo; cat "$foo" | ../zz_subs | psql rtdb; done
psql rtdb < ok/TAILS


Thus the data was migrated but RT still kept falling when I tried to insert anything new .. the culprit were the postgresql sequences (used to generate ids) - they didn't get updated because all entried were inserted with explicit id .. so all sequences still tried to use 1 as a new id, which didn't work. (rant mode: is it just me or is this behaviour incredibly stupid? should they at least keep incrementing if the next value is already there?).


psql rtdb <<EOF
select setval('acl_id_seq', (select max(id) from acl));
select setval('attachments_id_seq', (select max(id) from attachments));
select setval('attributes_id_seq', (select max(id) from attributes));
select setval('cachedgroupmembers_id_seq', (select max(id) from cachedgroupmembers));
select setval('customfields_id_seq', (select max(id) from customfields));
select setval('customfieldvalues_id_seq', (select max(id) from customfieldvalues));
select setval('groupmembers_id_seq', (select max(id) from groupmembers));
select setval('groups_id_seq', (select max(id) from groups));
select setval('links_id_seq', (select max(id) from links));
select setval('objectcustomfields_id_s', (select max(id) from objectcustomfields));
select setval('objectcustomfieldvalues_id_s', (select max(id) from objectcustomfieldvalues));
select setval('principals_id_seq', (select max(id) from principals));
select setval('queues_id_seq', (select max(id) from queues));
select setval('scripactions_id_seq', (select max(id) from scripactions));
select setval('scripconditions_id_seq', (select max(id) from scripconditions));
select setval('scrips_id_seq', (select max(id) from scrips));
select setval('templates_id_seq', (select max(id) from templates));
select setval('tickets_id_seq', (select max(id) from tickets));
select setval('transactions_id_seq', (select max(id) from transactions));
select setval('users_id_seq', (select max(id) from users));
EOF


Deep magic, but works.

Now, rt has it's own thingy for upgrading database for rt upgrades, so I wanted to run it so everything is in order. After skimming through, it does seem to do some useful stuff. Unfortunately there's a schema change in 3.7.3 which tried to remove all tables so 3.7.3 has to be skipped.


(echo 3.4.5; echo 3.7.1; echo y) | rt-setup-database --action upgrade --dba-password=omitted
(echo 3.7.3; echo; echo y) | rt-setup-database --action upgrade --dba-password=omitted


And really, that's all you need to do to migrate a rt.

You might also want to put something like this in your crontab:

0 4 * * * postgres /bin/echo "delete from sessions where LastUpdated < (now() - '24 hour'::interval);" | /usr/bin/psql rtdb


Rant mode on:
Except, it didn't really work. And why not, you ask? Because of ****** cpan putting his sleazy libraries where they don't belong. Why, oh why can't you set cpan to install to /usr/local or somewhere that has less priority than /usr/share/ where debian puts them. It didn't work because we had Net::LDAP from 1999 in /usr/lib (and a current version in /usr/share that didn't get used).
Oh well :)

2008-12-26

useful greasemonkey scripts list

These are all the greasemonkey scripts I'm currently using. Greasemonkey is a firefox addon that allows you to have custom scripts that change the behaviour of any site. Most of them are rather useful so enjoy! :)


  • Google Reader: Show Feed Favicons - shows favicons of the site the feed comes from instead of the generic rss icon in google reader

  • Convert hCalendar to Google Calendar - does what it says

  • mbank login - the mBank login page has autocomplete=off on the login form fields .. and I hate to have to look up my customer id every time .. so just replace the "foobar" in the script with your number and there you go. (It'd work for password too.)

  • Show Password on Click - shows the password when you click on a password input field

  • YouTube Enhancer - so the videos on youtube don't start playing automatically (which sucks when you open many videos at once) and load the higher quality version by default

useful firefox extensions list

These are all the firefox extensions I'm currently using .. and I'd be lost without many of them.
Enjoy! :)


  • AutoAuth - so you don't have to press enter on sites using HTTP authentication if you've already saved a password

  • Delicious Bookmarks - to synchronize ff bookmarks with my del.icio.us

  • Download Statusbar - so you can see current downloads in the statusbar

  • DownloadHelper - for saving youtube videos and image/video galleries

  • Facebook Toolbar - shows notifications about facebook events

  • Fast Dial - thumbnails on an empty tab .. just like in Opera

  • Firebug - to debug javascript apps and change stuff on loaded sites

  • FireStatus - shows what's new on my twitter + to post new items

  • Flashblock - shows a big play button instead of any flash on the site .. and you can start each flash thingy manually or allow flash for whole site

  • Forecastfox - so you know the weather outside

  • GCal Popup - overlays the current site with a google calendar view

  • Google Gears - for offline apps such as Google Docs, Google Reader and Remember The Milk

  • Google Notebook - shows a small Google Notebook so you can save snippets of a page

  • Google Reader Watcher - to see how many unread posts you have

  • Greasemonkey - to customize various sites (see my next post)

  • gui:config - well, easier than tweaking about:config items

  • Image Zoom - right click + wheel scroll on an image resizes it .. great for viewing comics in Google Reader

  • Open in Browser - adds an option to the open/save dialog so you can view stuff on any mimetype in the browser (great for all those javascripts with Content-Type: application/data)

  • Open IT Online - adds an option to the open/save dialog so you can open documents directly in Google Docs (and others)

  • Operator - shows any microformats on current page

  • Perspectives - to get rid of some of the annoying Invalid SSL Certificate warnings

  • ReloadEvery - to reload the current tab every n seconds/minutes

  • Resizeable Textarea - so you can grab and resize any textareas

  • Snap Links - middleclick and drag to open many links simultaneously

  • Tab History - preserves history of the parent tab in the new tab when you open a link in a new tab

  • Mobile Barcoder - shows the current tab url as QR-Code

2008-11-08

fbxkb flags patch

My primary keyboard layout is US (qwerty), but from time to time, one needs a different one (usually cz_qwerty or hu for me). So I have this handy line in my ~/.xinitrc that let's me switch to the alternate layout by using Right Alt:
setxkbmap -option grp:switch,grp:alts_toggle,grp_led:scroll us,cz_qwerty

And I use fbxkb to see the current layout in the tray. The only problem is, it doesn't work .. instead of the us flag, it shows question marks and when i switch to the us,hu layout combo, it shows question marks for both layouts.

Well, no more. It turns out, it was quite an easy bug to fix so here's a patch against fbxkb-0.6.

---Show the patch---

2008-10-24

pose (Palm Emulator) for amd64 debian / ubuntu

Some time ago, I moved from 32bit debian to 64bit debian on my notebook. So far, having configured flash and installed firefox from ubuntu, I haven't run into any problems. Until I tried to run Padict (Japanese dictionary for palms) under pose .. there is no pose package for amd64! And it's not so easy to compile either.

So now there is. The creation was relatively straightforward, first I downloaded i386 versions of pose and recursively all its dependencies that aren't architecture-independent from packages.debian.org. That's: pose (3.5-9.1), libdrm2 (2.3.1-1), libexpat1 (2.0.1-4), libfltk1.1 (1.1.9-6), libfontconfig1 (2.6.0-1), libfreetype6 (2.3.7-2), libgcc1 (1:4.3.2-1), libgl1-mesa-glx (7.0.3-6), libjpeg62 (6b-14), libpng12-0 (1.2.27-2), libstdc++6 (4.3.2-1), libx11-6 (2:1.1.5-2), libxau6 (1:1.0.3-3), libxcb-xlib0 (1.1-1.1), libxcb1 (1.1-1.1), libxdamage1 (1:1.1.1-4), libxdmcp6 (1:1.0.2-3), libxext6 (2:1.0.4-1), libxfixes3 (1:4.0.3-2), libxft2 (2.1.12-3), libxinerama1 (2:1.0.3-2), libxrender1 (1:0.9.4-2), libxxf86vm1 (1:1.0.2-1), zlib1g (1:1.2.3.3.dfsg-12).
And then I only extracted them (for foo in *deb; do dpkg-deb -X $foo .; done), renamed lib and usr/lib to lib32 and usr/lib32 respectively, removed crud from usr/share, renamed the binary, manpage and menu entry to pose32, created a Makefile, ran dh_make -n -c artistic -s -p pose32, edited debian/control a bit and finaly typed debuild and waited :).

You still need pose-skins and a rom image.
No, this version works, I see no reason to continually update it.

2007-08-08

claws-mail trayicon patch

My mailer is claws-mail (formerly known as sylpheed-claws, a fork of sylpheed) and I love it.
But there's one thing I miss: I can't make the trayicon plugin count only messages in the folders I want instead of all folders. (So it'd tell me about new messages in inbox + important but not in newsgroups and spam.)

Well, here's a quick and dirty proto patch for 2.10.0. The folder names are hardcoded into trayicon.c and necesary changes were made to folder.c . It works for me but of course I should pass a list of folders including paths instead of just a string like "folder1 folder2 ...folderN" and there should be a way to change the folders in the GUI. I'll do it later.

-- see claws-mail-2.10.0.trayicon.patch --

EDIT: As of claws-3.something, vanilla claws has this option in the menu, so the patch is no longer needed.

2007-05-09

picasaweb and f-spot

I've tried out Picasa some time ago, I found it enjoyable but rather impractical, I usually know where my pictures are, not when. One feature that's great is that you can upload albums to Picasa Web Albums (http://picasaweb.google.com). I'd use Flickr (http://www.flickr.com) but hey, Yahoo things feel uncomfortable.
I've just come from a trip to Italy so I wanted to upload some photos. Unfortunately, the current linux version of Picasa doesn't support this feature yet. After some googling I found F-Spot (http://www.f-spot.org) does. I've been meaing to give it a try anyway so I installed it.
Now, I should mention I'm using fvwm-crystal as my window manager, no desktop manager, no gnome or kde stuff. Well, F-Spot crashed some time befor I installed all the necessary stuff like gnome keyring a dbus and figured out I have to run it with 'dbus-launch f-spot'. That way, it starts just fine. I used the --view interface since it's simpler and more closer to what I like, unfortunately it doesn't have any way to sort the files (I've found no such way in the non-view interface either) and because it runs the images through a filter (convert to jpg, resize), it uploaded the files with a randomish filename. No sorting on PicasaWeb either, then. (Before I got even there, I had to install the SVN version instead of the stable one since there was a change of protocol.)
Being curious about C#, the language f-spot is written in, I decided to write a patch. Took a relatively short time, you can find the results at http://bugzilla.gnome.org/show_bug.cgi?id=437044 . I think I'm definitely going to play more with it. I should probably add a way to sort in the --view UI.

Labels

himdel's shared items