docs, comments and readability

pull/2/head
Rui Carmo 2016-04-03 11:35:39 +01:00
rodzic fd1801298f
commit 49e51b1a8c
3 zmienionych plików z 41 dodań i 15 usunięć

Wyświetl plik

@ -1,8 +1,12 @@
# Installation # Installation
_TODO: describe the system requirements and installation process._ These installation notes should cover most Debian Linux variants (on any architecture). Very minor changes should be required to deploy on RHEL variants like CentOS, and there is specific emphasis on Raspbian because that's the typical deployment target.
## Setting up the `piku` user You can, however, run `piku` on _any_ POSIX system where [uWSGI][uwsgi] and Python are available.
_TODO: describe the overall installation process._
## Setting up the `piku` user (Debian Linux, any architecture)
_TODO: describe the need for a separate user and why it's configured this way._ _TODO: describe the need for a separate user and why it's configured this way._
@ -12,7 +16,7 @@ If you're impatient, you need to make sure you have a `~/.ssh/authorized_keys` f
command="FINGERPRINT=<your SSH fingerprint, not used right now> NAME=default /home/piku/piku.py $SSH_ORIGINAL_COMMAND",no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding <your ssh key> command="FINGERPRINT=<your SSH fingerprint, not used right now> NAME=default /home/piku/piku.py $SSH_ORIGINAL_COMMAND",no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding <your ssh key>
``` ```
## uWSGI Installation ## uWSGI Installation (Debian Linux variants, any architecture)
[uWSGI][uwsgi] can be installed in a variety of fashions. However, these instructions assume you're installing it from source, and as such may vary from system to system. [uWSGI][uwsgi] can be installed in a variety of fashions. However, these instructions assume you're installing it from source, and as such may vary from system to system.
@ -34,16 +38,19 @@ sudo update-rc.d uwsgi-piku defaults
sudo service uwsgi-piku start sudo service uwsgi-piku start
``` ```
## Go Installation (on Raspberry Pi) ## Go Installation (Debian Linux variants, on Raspberry Pi)
> This is **EXPERIMENTAL** and may not work at all. > This is **EXPERIMENTAL** and may not work at all.
### Raspbian
Since Raspbian's Go compiler is version 1.0.2, we need something more up-to-date. Since Raspbian's Go compiler is version 1.0.2, we need something more up-to-date.
1. Get an [ARM 6 binary tarball][goarm] 1. Get an [ARM 6 binary tarball][goarm]
2. Unpack it under the `piku` user like such: 2. Unpack it under the `piku` user like such:
```bash ```bash
su - piku
cd ~ cd ~
tar -zxvf /tmp/go1.5.3.linux-arm.tar.gz tar -zxvf /tmp/go1.5.3.linux-arm.tar.gz
``` ```
@ -51,6 +58,7 @@ tar -zxvf /tmp/go1.5.3.linux-arm.tar.gz
3. Give it a temporary `GOPATH` and install `godep`: 3. Give it a temporary `GOPATH` and install `godep`:
```bash ```bash
su - piku
cd ~ cd ~
GOROOT=$HOME/go GOPATH=$HOME/golibs PATH=$PATH:$HOME/go/bin go get github.com/tools/godep GOROOT=$HOME/go GOPATH=$HOME/golibs PATH=$PATH:$HOME/go/bin go get github.com/tools/godep
``` ```

Wyświetl plik

@ -14,11 +14,12 @@ From the bottom up:
- [ ] `chroot`/namespace isolation - [ ] `chroot`/namespace isolation
- [ ] Proxy deployments to other nodes (build on one box, deploy to many) - [ ] Proxy deployments to other nodes (build on one box, deploy to many)
- [ ] Support Clojure/Java deployments - [ ] Support Clojure/Java deployments
- [ ] Support Go deployments
- [ ] Support barebones binary deployments
- [ ] CLI command documentation - [ ] CLI command documentation
- [ ] Complete installation instructions (see `INSTALL.md` for a working draft) - [ ] Complete installation instructions (see `INSTALL.md` for a working draft)
- [ ] Installation helper/SSH key add - [ ] Installation helper/SSH key add
- [ ] Support barebones binary deployments
- [ ] Sample Go app
- [ ] Support Go deployments
- [x] Worker scaling - [x] Worker scaling
- [x] Remote CLI commands for changing/viewing applied/live settings - [x] Remote CLI commands for changing/viewing applied/live settings
- [x] Remote tailing of all logfiles for a single application - [x] Remote tailing of all logfiles for a single application
@ -27,7 +28,7 @@ From the bottom up:
- [X] `Procfile` support (`wsgi` and `worker` processes for now, `web` processes being tested) - [X] `Procfile` support (`wsgi` and `worker` processes for now, `web` processes being tested)
- [x] Basic CLI commands to manage apps - [x] Basic CLI commands to manage apps
- [x] `virtualenv` isolation - [x] `virtualenv` isolation
- [x] Support Python deployments (currently hardcoded until `Procfile` is implemented) - [x] Support Python deployments
- [x] Repo creation upon first push - [x] Repo creation upon first push
- [x] Basic understanding of [how `dokku` works](http://off-the-stack.moorman.nu/2013-11-23-how-dokku-works.html) - [x] Basic understanding of [how `dokku` works](http://off-the-stack.moorman.nu/2013-11-23-how-dokku-works.html)
@ -37,6 +38,7 @@ From the bottom up:
* `git push paas master` your code * `git push paas master` your code
* `piku` determines the runtime and installs the dependencies for your app (building whatever's required) * `piku` determines the runtime and installs the dependencies for your app (building whatever's required)
* For Python, it segregates each app's dependencies into a `virtualenv` * For Python, it segregates each app's dependencies into a `virtualenv`
* For Go, it defines a separate `GOPATH` for each app
* It then looks at a `Procfile` and starts the relevant workers using [uWSGI][uwsgi] as a generic process manager * It then looks at a `Procfile` and starts the relevant workers using [uWSGI][uwsgi] as a generic process manager
Later on, I intend to do fancier `dokku`-like stuff like reconfiguring `nginx`, but a twist I'm planning on doing is having one `piku` machine act as a build box and deploy the finished product to another. Later on, I intend to do fancier `dokku`-like stuff like reconfiguring `nginx`, but a twist I'm planning on doing is having one `piku` machine act as a build box and deploy the finished product to another.
@ -61,6 +63,10 @@ I intend to support Python, Go, Node and Clojure (Java), but will be focusing on
**A:** Partly because it's supposed to run on a [Pi][pi], because it's Japanese onomatopeia for 'twitch' or 'jolt', and because I know the name will annoy some of my friends. **A:** Partly because it's supposed to run on a [Pi][pi], because it's Japanese onomatopeia for 'twitch' or 'jolt', and because I know the name will annoy some of my friends.
**Q:** Why Python/why not Go?
**A:** I actually thought about doing this in Go right off the bat, but [click][click] is so cool and I needed to have [uWSGI][uwsgi] running anyway, so I caved in. But I'm very likely to take something like [suture](https://github.com/thejerf/suture) and port this across, doing away with [uWSGI][uwsgi] altogether.
**Q:** Does it run under Python 3? **Q:** Does it run under Python 3?
**A:** It should. `click` goes a long way towards abstracting the simpler stuff, and I tried to avoid most obvious incompatibilities (other than a few differences in `subprocess.call` and the like). However, this targets Python 2.7 first, since that's the default on Raspbian. Pull requests are welcome. **A:** It should. `click` goes a long way towards abstracting the simpler stuff, and I tried to avoid most obvious incompatibilities (other than a few differences in `subprocess.call` and the like). However, this targets Python 2.7 first, since that's the default on Raspbian. Pull requests are welcome.
@ -69,6 +75,7 @@ I intend to support Python, Go, Node and Clojure (Java), but will be focusing on
**A:** I use `dokku` daily, and for most of my personal stuff. But the `dokku` stack relies on a number of `x64` containers that need to be completely rebuilt for ARM, and when I decided I needed something like this (March 2016) that was barely possible - `docker` itself is not fully baked for ARM yet, and people are still trying to get `herokuish` and `buildstep` to build on ARM. **A:** I use `dokku` daily, and for most of my personal stuff. But the `dokku` stack relies on a number of `x64` containers that need to be completely rebuilt for ARM, and when I decided I needed something like this (March 2016) that was barely possible - `docker` itself is not fully baked for ARM yet, and people are still trying to get `herokuish` and `buildstep` to build on ARM.
[click]: http://click.poocoo.org
[pi]: http://www.raspberrypi.org [pi]: http://www.raspberrypi.org
[dokku]: https://github.com/dokku/dokku [dokku]: https://github.com/dokku/dokku
[raspi-cluster]: https://github.com/rcarmo/raspi-cluster [raspi-cluster]: https://github.com/rcarmo/raspi-cluster

27
piku.py
Wyświetl plik

@ -125,6 +125,8 @@ def do_deploy(app):
if exists(join(app_path, 'requirements.txt')): if exists(join(app_path, 'requirements.txt')):
echo("-----> Python app detected.", fg='green') echo("-----> Python app detected.", fg='green')
deploy_python(app) deploy_python(app)
# if exists(join(app_path, 'Godeps')) or len(glob(join(app_path),'*.go')):
# Go deployment
else: else:
echo("-----> Could not detect runtime!", fg='red') echo("-----> Could not detect runtime!", fg='red')
# TODO: detect other runtimes # TODO: detect other runtimes
@ -174,6 +176,8 @@ def spawn_app(app, deltas={}):
live = join(ENV_ROOT, app, 'LIVE_ENV') live = join(ENV_ROOT, app, 'LIVE_ENV')
# Scaling # Scaling
scaling = join(ENV_ROOT, app, 'SCALING') scaling = join(ENV_ROOT, app, 'SCALING')
# Bootstrap environment
env = { env = {
'PATH': os.environ['PATH'], 'PATH': os.environ['PATH'],
'VIRTUAL_ENV': virtualenv_path, 'VIRTUAL_ENV': virtualenv_path,
@ -184,6 +188,7 @@ def spawn_app(app, deltas={}):
# Load environment variables shipped with repo (if any) # Load environment variables shipped with repo (if any)
if exists(env_file): if exists(env_file):
env.update(parse_settings(env_file, env)) env.update(parse_settings(env_file, env))
# Override with custom settings (if any) # Override with custom settings (if any)
if exists(settings): if exists(settings):
env.update(parse_settings(settings, env)) env.update(parse_settings(settings, env))
@ -211,6 +216,7 @@ def spawn_app(app, deltas={}):
for w in v: for w in v:
enabled = join(UWSGI_ENABLED, '%s_%s.%d.ini' % (app, k, w)) enabled = join(UWSGI_ENABLED, '%s_%s.%d.ini' % (app, k, w))
if not exists(enabled): if not exists(enabled):
echo("-----> Spawning '%s:%s.%d'" % (app, kind, ordinal), fg='green')
spawn_worker(app, k, workers[k], env, w) spawn_worker(app, k, workers[k], env, w)
# Remove unnecessary workers (leave logfiles) # Remove unnecessary workers (leave logfiles)
@ -260,15 +266,13 @@ def spawn_worker(app, kind, command, env, ordinal=1):
for k, v in settings: for k, v in settings:
h.write("%s = %s\n" % (k, v)) h.write("%s = %s\n" % (k, v))
if exists(enabled):
os.unlink(enabled)
echo("-----> Spawning '%s:%s.%d'" % (app, kind, ordinal), fg='green')
shutil.copyfile(available, enabled) shutil.copyfile(available, enabled)
def multi_tail(app, filenames): def multi_tail(app, filenames, catch_up=20):
"""Tails multiple log files""" """Tails multiple log files"""
# Seek helper
def peek(handle): def peek(handle):
where = handle.tell() where = handle.tell()
line = handle.readline() line = handle.readline()
@ -281,6 +285,7 @@ def multi_tail(app, filenames):
files = {} files = {}
prefixes = {} prefixes = {}
# Set up current state for each log file
for f in filenames: for f in filenames:
prefixes[f] = splitext(basename(f))[0] prefixes[f] = splitext(basename(f))[0]
files[f] = open(f) files[f] = open(f)
@ -288,12 +293,15 @@ def multi_tail(app, filenames):
files[f].seek(0, 2) files[f].seek(0, 2)
longest = max(map(len, prefixes.values())) longest = max(map(len, prefixes.values()))
# Grab a little history (if any)
for f in filenames: for f in filenames:
for line in deque(open(f), 20): for line in deque(open(f), catch_up):
yield "%s | %s" % (prefixes[f].ljust(longest), line) yield "%s | %s" % (prefixes[f].ljust(longest), line)
while True: while True:
updated = False updated = False
# Check for updates on every file
for f in filenames: for f in filenames:
line = peek(files[f]) line = peek(files[f])
if not line: if not line:
@ -301,8 +309,10 @@ def multi_tail(app, filenames):
else: else:
updated = True updated = True
yield "%s | %s" % (prefixes[f].ljust(longest), line) yield "%s | %s" % (prefixes[f].ljust(longest), line)
if not updated: if not updated:
sleep(1) sleep(1)
# Check if logs rotated
for f in filenames: for f in filenames:
if exists(f): if exists(f):
if os.stat(f).st_ino != inodes[f]: if os.stat(f).st_ino != inodes[f]:
@ -316,7 +326,8 @@ def multi_tail(app, filenames):
@group() @group()
def piku(): def piku():
"""Initialize paths""" """The smallest PaaS you've ever seen"""
# Initialize paths
for p in [APP_ROOT, GIT_ROOT, ENV_ROOT, UWSGI_ROOT, UWSGI_AVAILABLE, UWSGI_ENABLED, LOG_ROOT]: for p in [APP_ROOT, GIT_ROOT, ENV_ROOT, UWSGI_ROOT, UWSGI_AVAILABLE, UWSGI_ENABLED, LOG_ROOT]:
if not exists(p): if not exists(p):
os.makedirs(p) os.makedirs(p)
@ -363,7 +374,7 @@ def deploy_app(app, setting):
@argument('app') @argument('app')
@argument('settings', nargs=-1) @argument('settings', nargs=-1)
def deploy_app(app, settings): def deploy_app(app, settings):
"""Show application configuration""" """Set a configuration setting"""
app = sanitize_app_name(app) app = sanitize_app_name(app)
config_file = join(ENV_ROOT, app, 'ENV') config_file = join(ENV_ROOT, app, 'ENV')
@ -384,7 +395,7 @@ def deploy_app(app, settings):
@piku.command("config:live") @piku.command("config:live")
@argument('app') @argument('app')
def deploy_app(app): def deploy_app(app):
"""Show current application settings""" """Show live configuration settings"""
app = sanitize_app_name(app) app = sanitize_app_name(app)
live_config = join(ENV_ROOT, app, 'LIVE_ENV') live_config = join(ENV_ROOT, app, 'LIVE_ENV')