├── .gitignore ├── INSTALL.md ├── JSON.sh ├── LICENSE ├── MANUAL.md ├── Makefile ├── README.md ├── dockerlite ├── dockerlite.conf ├── lib ├── dockerlite-alias.sh ├── dockerlite-chroot.sh ├── dockerlite-commit.sh ├── dockerlite-diff.sh ├── dockerlite-findpid.sh ├── dockerlite-help.sh ├── dockerlite-init.sh ├── dockerlite-inspect.sh ├── dockerlite-kill.sh ├── dockerlite-lsc.sh ├── dockerlite-lsi.sh ├── dockerlite-metadata.sh ├── dockerlite-mkc.sh ├── dockerlite-mki.sh ├── dockerlite-networking.sh ├── dockerlite-ps.sh ├── dockerlite-pull.sh ├── dockerlite-rmc.sh ├── dockerlite-rmi.sh ├── dockerlite-rootfs.sh ├── dockerlite-run.sh ├── dockerlite-runc.sh ├── dockerlite-runi.sh └── dockerlite-running.sh └── true.c /.gitignore: -------------------------------------------------------------------------------- 1 | true 2 | *~ 3 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # First Steps With Dockerlite 2 | 3 | 4 | ## Installation 5 | 6 | Clone the repository anywhere you like, e.g.: 7 | 8 | ```bash 9 | cd /opt 10 | git clone git://github.com/jpetazzo/dockerlite 11 | ``` 12 | 13 | Then you can either add `/opt/dockerlite` to your `$PATH`, or 14 | create a symlink to `/opt/dockerlite/dockerlite`, e.g.: 15 | 16 | ```bash 17 | ln -s /opt/dockerlite/dockerlite /usr/local/sbin/dockerlite 18 | ``` 19 | 20 | **Note:** Dockerlite requires root privileges. It needs to do call the BTRFS 21 | tools, the LXC tools, and manipulate network interfaces; all that stuff 22 | requires root privileges. 23 | 24 | 25 | ## BTRFS requirements 26 | 27 | You need a BTRFS filesystem in `/btrfs`. There are three main different 28 | ways to do that. 29 | 30 | 31 | ### If your root filesystem in on BTRFS 32 | 33 | Then just create a subvolume in `/btrfs` with `btrfs subvol create btrfs`. 34 | You're done. 35 | 36 | 37 | ### If you have a spare block device or some space on your LVM VG 38 | 39 | Format the spare device/partition/LV using BTRFS and mount it on `/btrfs`. 40 | 41 | 42 | ### If you have none of those 43 | 44 | Loopback to the rescue! 45 | 46 | ```bash 47 | $ sudo -i 48 | dd if=/dev/zero of=/btrfs.img bs=1024k count=1 seek=2048 49 | mkfs -t btrfs /btrfs.img 50 | mount -o loop /btrfs.img /btrfs 51 | ``` 52 | 53 | 54 | ## One-time initialization of Dockerlite 55 | 56 | Just run `dockerlite init` and you're done. 57 | 58 | -------------------------------------------------------------------------------- /JSON.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | throw () { 4 | echo "$*" >&2 5 | exit 1 6 | } 7 | 8 | BRIEF=0 9 | LEAFONLY=0 10 | PRUNE=0 11 | 12 | usage() { 13 | echo 14 | echo "Usage: JSON.sh [-b] [-l] [-p] [-h]" 15 | echo 16 | echo "-p - Prune empty. Exclude fields with empty values." 17 | echo "-l - Leaf only. Only show leaf nodes, which stops data duplication." 18 | echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options." 19 | echo "-h - This help text." 20 | echo 21 | } 22 | 23 | parse_options() { 24 | set -- "$@" 25 | local ARGN=$# 26 | while [ $ARGN -ne 0 ] 27 | do 28 | case $1 in 29 | -h) usage 30 | exit 0 31 | ;; 32 | -b) BRIEF=1 33 | LEAFONLY=1 34 | PRUNE=1 35 | ;; 36 | -l) LEAFONLY=1 37 | ;; 38 | -p) PRUNE=1 39 | ;; 40 | ?*) echo "ERROR: Unknown option." 41 | usage 42 | exit 0 43 | ;; 44 | esac 45 | shift 1 46 | ARGN=$((ARGN-1)) 47 | done 48 | } 49 | 50 | awk_egrep () { 51 | local pattern_string=$1 52 | 53 | gawk '{ 54 | while ($0) { 55 | start=match($0, pattern); 56 | token=substr($0, start, RLENGTH); 57 | print token; 58 | $0=substr($0, start+RLENGTH); 59 | } 60 | }' pattern=$pattern_string 61 | } 62 | 63 | tokenize () { 64 | local GREP 65 | local ESCAPE 66 | local CHAR 67 | 68 | if echo "test string" | egrep -ao --color=never "test" &>/dev/null 69 | then 70 | GREP='egrep -ao --color=never' 71 | else 72 | GREP='egrep -ao' 73 | fi 74 | 75 | if echo "test string" | egrep -o "test" &>/dev/null 76 | then 77 | ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' 78 | CHAR='[^[:cntrl:]"\\]' 79 | else 80 | GREP=awk_egrep 81 | ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' 82 | CHAR='[^[:cntrl:]"\\\\]' 83 | fi 84 | 85 | local STRING="\"$CHAR*($ESCAPE$CHAR*)*\"" 86 | local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?' 87 | local KEYWORD='null|false|true' 88 | local SPACE='[[:space:]]+' 89 | 90 | $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$" 91 | } 92 | 93 | parse_array () { 94 | local index=0 95 | local ary='' 96 | read -r token 97 | case "$token" in 98 | ']') ;; 99 | *) 100 | while : 101 | do 102 | parse_value "$1" "$index" 103 | index=$((index+1)) 104 | ary="$ary""$value" 105 | read -r token 106 | case "$token" in 107 | ']') break ;; 108 | ',') ary="$ary," ;; 109 | *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;; 110 | esac 111 | read -r token 112 | done 113 | ;; 114 | esac 115 | [ "$BRIEF" -eq 0 ] && value=`printf '[%s]' "$ary"` || value= 116 | : 117 | } 118 | 119 | parse_object () { 120 | local key 121 | local obj='' 122 | read -r token 123 | case "$token" in 124 | '}') ;; 125 | *) 126 | while : 127 | do 128 | case "$token" in 129 | '"'*'"') key=$token ;; 130 | *) throw "EXPECTED string GOT ${token:-EOF}" ;; 131 | esac 132 | read -r token 133 | case "$token" in 134 | ':') ;; 135 | *) throw "EXPECTED : GOT ${token:-EOF}" ;; 136 | esac 137 | read -r token 138 | parse_value "$1" "$key" 139 | obj="$obj$key:$value" 140 | read -r token 141 | case "$token" in 142 | '}') break ;; 143 | ',') obj="$obj," ;; 144 | *) throw "EXPECTED , or } GOT ${token:-EOF}" ;; 145 | esac 146 | read -r token 147 | done 148 | ;; 149 | esac 150 | [ "$BRIEF" -eq 0 ] && value=`printf '{%s}' "$obj"` || value= 151 | : 152 | } 153 | 154 | parse_value () { 155 | local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0 156 | case "$token" in 157 | '{') parse_object "$jpath" ;; 158 | '[') parse_array "$jpath" ;; 159 | # At this point, the only valid single-character tokens are digits. 160 | ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;; 161 | *) value=$token 162 | isleaf=1 163 | [ "$value" = '""' ] && isempty=1 164 | ;; 165 | esac 166 | [ "$value" = '' ] && return 167 | [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1 168 | [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1 169 | [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1 170 | [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \ 171 | [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1 172 | [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value" 173 | : 174 | } 175 | 176 | parse () { 177 | read -r token 178 | parse_value 179 | read -r token 180 | case "$token" in 181 | '') ;; 182 | *) throw "EXPECTED EOF GOT $token" ;; 183 | esac 184 | } 185 | 186 | parse_options "$@" 187 | 188 | if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]); 189 | then 190 | tokenize | parse 191 | fi 192 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MANUAL.md: -------------------------------------------------------------------------------- 1 | For now, check the available commands with `dockerlite`. 2 | 3 | I will add more info here later if needed. 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | true: true.c 2 | gcc -Wall --static -o true true.c 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dockerlite: lightweight Linux virtualization with BTRFS and LXC 2 | 3 | Dockerlite lets you run Linux apps in lightweight, isolated environments, 4 | using LXC (Linux Containers). It is inspired by [Docker](http://www.docker.io/) 5 | and it actually reimplements some of its most basic features. 6 | 7 | Using BTRFS snapshots, `dockerlite` can save the state of a given environment 8 | in a frozen "image", and later, create more environments ("containers") out 9 | of that image. 10 | 11 | It was inspired by [Docker](https://www.docker.io/), and aims at being 12 | a sandbox to experiment new concepts linked with the Docker project. 13 | 14 | It is **not** a replacement for Docker. It is **missing** (at least) 15 | the following features: 16 | - registry protocol (i.e. it is not possible to push/pull images) 17 | - index protocol (i.e. it is not possible to search images) 18 | - REST API (i.e. the only way to use Dockerlite is through the CLI) 19 | - Dockerfile (i.e. you cannot `dockerlite build`) 20 | - and many more. 21 | 22 | Its main feature is `HACKABILITY: 9000` since it's shell, and everybody 23 | including your dog can write shell scripts, right? 24 | 25 | 26 | ## Installation 27 | 28 | See [INSTALL.md](INSTALL.md). 29 | 30 | 31 | ## How to use it 32 | 33 | See [MANUAL.md](MANUAL.md). 34 | 35 | 36 | ## What's "lightweight" virtualization? 37 | 38 | A Linux container looks like a virtual machine: it has its own network stack, 39 | IP address, process space; it is isolated from its sibling containers (it can't 40 | see them and can't be seen by them). However, it runs on top of the same 41 | kernel as its host. This means that if your machine runs Linux 3.8, all 42 | containers on this machine will also run Linux 3.8. You cannot run another 43 | kernel (or another OS) within a container. Of course, you could run a full 44 | virtual machine within qemu or kvm within a container, but that's a different 45 | story! 46 | 47 | 48 | ## Where the name `dockerlite` comes from? 49 | 50 | `dockerlite` is a "light" version of [Docker](/dotcloud/docker). 51 | The latter has similar features, but with the following major differences: 52 | - Docker is written in Go, while `dockerlite` is a Posix Shell script; 53 | - Docker storage relies on AUFS, while `dockerlite` uses BTRFS; 54 | - Docker runs as a background daemon, and is operated through a CLI 55 | client, while `dockerlite` does not run in the background. 56 | 57 | Docker also has some extra features to store images in 3rd party services. 58 | 59 | 60 | ## Why `dockerlite`? 61 | 62 | `dockerlite` initially targetted the following goals: 63 | - demonstrate that the core features provided by Docker can be easily 64 | reimplemented with simple, easy-to-audit, shell scripts; 65 | - provide an alternative implementation to Docker, with a strong emphasis 66 | on "hackability", i.e. a lightweight testbed for new features which can 67 | be more cumbersome to implement in a full-blown Go project; 68 | - evaluate BTRFS in the context of Docker. 69 | 70 | The first goal is loosely defined, depending on what you want to put in 71 | the "core features" of Docker. If you just want to create images and 72 | containers, then "misson complete". If you want to push/pull and use 73 | a REST API, it's a long shot. 74 | 75 | The other goals were met. Dockerlite confirmed that BTRFS was an acceptable 76 | option and that there were no unplanned side-effect or shotstopper 77 | preventing its use for Docker containers. It also served to evaluate 78 | different ways to setup the containers networking stack. 79 | 80 | 81 | ## License 82 | 83 | Apache 2 License 84 | 85 | For JSON.sh license, see https://github.com/dominictarr/JSON.sh 86 | -------------------------------------------------------------------------------- /dockerlite: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | _dl_error () { 5 | echo "error: $*" >/dev/stderr 6 | exit 1 7 | } 8 | 9 | _dl_warning () { 10 | echo "warning: $*" >/dev/stderr 11 | } 12 | 13 | _dl_info () { 14 | echo "info: $*" >/dev/stderr 15 | } 16 | 17 | _dl_resolve () { 18 | CLASS=$1 19 | NEEDLE=$2 20 | cd $DOCKERLITE_ROOT/$CLASS 21 | # Check exact match first 22 | [ -d $NEEDLE/rootfs ] && { echo $NEEDLE ; return ; } 23 | # Check prefix match on the ID 24 | FOUND="" 25 | for DIR in $NEEDLE* 26 | do 27 | [ -d $DIR/rootfs ] && FOUND=1$FOUND 28 | done 29 | case "$FOUND" in 30 | 1) 31 | echo $DIR 32 | return 33 | ;; 34 | 1*) 35 | _dl_error "more than one match for $NEEDLE found in $CLASS" 36 | ;; 37 | esac 38 | # Check names 39 | # (if no container has a name tag, grep will show a warning, 40 | # so we send that to /dev/null) 41 | grep -q "^$NEEDLE$" */metadata/name 2>/dev/null && { 42 | ls -t $(grep -l "^$NEEDLE$" */metadata/name) | head -n 1 | cut -d/ -f1 43 | return 44 | } 45 | # Check if we asked LAST 46 | [ "$2" = "LAST" ] && { 47 | ls -t | head -n 1 48 | return 49 | } 50 | _dl_error "no match for $NEEDLE found in $CLASS" 51 | } 52 | 53 | _dl_mkid () { head -c 20 /dev/urandom | sha1sum | cut -d" " -f1 ; } 54 | _dl_zerohash () { sha1sum /dev/null | cut -d" " -f1 ; } 55 | _dl_cgroup () { 56 | CID=$1 57 | NONFATAL=$2 58 | MNT=$(grep ^cgroup.*devices /proc/mounts | cut -d" " -f2 | head -n1) 59 | [ "$MNT" ] || _dl_error "cannot find cgroup mount point" 60 | [ -d $MNT/lxc/$CID ] && { echo $MNT/lxc/$CID ; return ; } 61 | [ -d $MNT/$CID ] && { echo $MNT/$CID ; return ; } 62 | [ "$NONFATAL" ] || _dl_error "cannot find cgroup for container $CID" 63 | } 64 | eval "_dl_sourcedir () { echo $(dirname $(readlink -f "$0")); }" 65 | 66 | _dl_btrfs () { 67 | btrfs "$@" >/dev/null || _dl_error "problem with btrfs" 68 | } 69 | 70 | HELP="dockerlite - a modular containerization engine" 71 | _dl_cmd () { 72 | HELP="$(printf "%s\n%-10s %s\n\n" "$HELP" "$1" "$2")" 73 | } 74 | 75 | cd $(_dl_sourcedir) 76 | . ./dockerlite.conf 77 | for CMD in lib/dockerlite-*.sh 78 | do 79 | . ./$CMD 80 | done 81 | 82 | [ "$DOCKERLITE_DEBUG" ] && set -x 83 | if [ "$1" ] 84 | then 85 | CMD="$1" 86 | shift 87 | else 88 | CMD=help 89 | fi 90 | FUN=_dl_$CMD 91 | type -t $FUN | grep -q function || _dl_error "invalid command" 92 | $FUN "$@" 93 | -------------------------------------------------------------------------------- /dockerlite.conf: -------------------------------------------------------------------------------- 1 | DOCKERLITE_ROOT=/btrfs 2 | #DOCKERLITE_DEBUG=1 3 | DOCKERLITE_BRIDGE=br0 4 | 5 | # Those last two parameters are useful only for testing (with _dl_mkloop) 6 | DOCKERLITE_LOOPFILE=/btrfs.img 7 | DOCKERLITE_LOOPSIZE=2000 # in MB -------------------------------------------------------------------------------- /lib/dockerlite-alias.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd alias "display a convenient alias to run dockerlite" 2 | _dl_alias () { 3 | echo "alias dl='sudo "$(readlink -f "$(dirname "$0")")/dockerlite"'" 4 | } -------------------------------------------------------------------------------- /lib/dockerlite-chroot.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd chroot "get a shell in a container (without containerization)" 2 | _dl_chroot () { 3 | CID=$1 4 | [ "$1" ] || error "must specify container to chroot to" 5 | chroot $(_dl_rootfs $CID) 6 | } -------------------------------------------------------------------------------- /lib/dockerlite-commit.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd commit "alias for mki" 2 | _dl_commit () { 3 | _dl_mki "$@" 4 | } -------------------------------------------------------------------------------- /lib/dockerlite-diff.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd diff "show differences between a container and its base image" 2 | _dl_diff () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container to diff" 5 | cd $DOCKERLITE_ROOT 6 | CID=$(_dl_resolve containers $CID) 7 | IID=$(_dl_getc $CID image) 8 | GEN=$(btrfs subvol list . | grep "path images/$IID/rootfs" | cut -d" " -f4) 9 | _dl_warning "btrfs find-new does not show deleted files or metadata changes" 10 | btrfs subvol find-new containers/$CID/rootfs $GEN | \ 11 | grep -v ^transid | \ 12 | cut -d" " -f17 | \ 13 | sed 's/^/* /' 14 | } -------------------------------------------------------------------------------- /lib/dockerlite-findpid.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd findpid "find pid of a process within given container" 2 | _dl_findpid () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container" 5 | CID=$(_dl_resolve containers $CID) 6 | TASKS=$(_dl_cgroup $CID)/tasks 7 | PID=$(head -n 1 $TASKS) 8 | [ "$PID" ] || _dl_error "cgroup seems to be empty" 9 | echo $PID 10 | } -------------------------------------------------------------------------------- /lib/dockerlite-help.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd help "list available commands and their help" 2 | _dl_help () { 3 | echo "$HELP" 4 | } 5 | -------------------------------------------------------------------------------- /lib/dockerlite-init.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd init "initialize $DOCKERLITE_ROOT and create the root (empty) image" 2 | _dl_init () { 3 | # If for some reason, we cannot "vendor in" JSON.sh, we could download 4 | # it here, when the image store is initialized. 5 | #curl -s https://raw.github.com/dominictarr/JSON.sh/master/JSON.sh > JSON.sh 6 | #chmod +x JSON.sh 7 | 8 | cd $DOCKERLITE_ROOT 9 | mkdir -p images 10 | mkdir -p containers 11 | mkdir -p networks 12 | 13 | EMPTY=empty$RANDOM 14 | ZEROHASH=$(_dl_zerohash) 15 | _dl_btrfs subvol create $EMPTY 16 | mkdir -p images/$ZEROHASH 17 | mkdir -p images/$ZEROHASH/metadata 18 | _dl_seti $ZEROHASH name 'empty image (base of all other images)' 19 | _dl_btrfs subvol snapshot -r $EMPTY images/$ZEROHASH/rootfs 20 | _dl_btrfs subvol delete $EMPTY 21 | } 22 | 23 | _dl_mkloop () { 24 | [ -d $DOCKERLITE_ROOT ] || mkdir -p $DOCKERLITE_ROOT 25 | mountpoint -q $DOCKERLITE_ROOT && return 26 | [ -f $DOCKERLITE_LOOPFILE ] || { 27 | dd if=/dev/zero of=$DOCKERLITE_LOOPFILE bs=1024k count=$DOCKERLITE_LOOPSIZE 28 | mkfs -t btrfs $DOCKERLITE_LOOPFILE 29 | } 30 | mount $DOCKERLITE_LOOPFILE $DOCKERLITE_ROOT 31 | } 32 | -------------------------------------------------------------------------------- /lib/dockerlite-inspect.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd inspect "show information about a container" 2 | _dl_inspect () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container to inspect" 5 | CID=$(_dl_resolve containers $CID) 6 | cd $DOCKERLITE_ROOT/containers/$CID/metadata 7 | grep . * 8 | } -------------------------------------------------------------------------------- /lib/dockerlite-kill.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd kill "terminate a runaway container" 2 | _dl_kill () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container to kill" 5 | CID=$(_dl_resolve containers $CID) 6 | lxc-stop -n $CID 7 | } -------------------------------------------------------------------------------- /lib/dockerlite-lsc.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd lsc "list containers" 2 | _dl_lsc () { 3 | cd $DOCKERLITE_ROOT/containers 4 | printf "%-40s %-30s %-30s\n" "CONTAINER ID" "CONTAINER NAME" "BASE IMAGE" 5 | for CID in * 6 | do 7 | IID=$(_dl_getc $CID image) 8 | printf "%-40s %-30s %-30s\n" \ 9 | $CID "$(_dl_getc $CID name)" "$(_dl_geti $IID name)" 10 | done 11 | } 12 | -------------------------------------------------------------------------------- /lib/dockerlite-lsi.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd lsi "list images" 2 | _dl_lsi () { 3 | cd $DOCKERLITE_ROOT/images 4 | for IID in * 5 | do 6 | echo $IID $(_dl_geti $IID name) 7 | done 8 | } -------------------------------------------------------------------------------- /lib/dockerlite-metadata.sh: -------------------------------------------------------------------------------- 1 | _dl_metadata () { 2 | ACTION=$1 3 | CLASS=$2 4 | OBJECT=$3 5 | KEY=$4 6 | VALUE=$5 7 | JUNK=$6 8 | [ "$JUNK" ] && _dl_error "extra arguments: '$JUNK' (check your quotes)" 9 | MDPATH="$DOCKERLITE_ROOT/$CLASS/$OBJECT/metadata/$KEY" 10 | case "$ACTION" in 11 | get) 12 | [ -f "$MDPATH" ] && cat "$MDPATH" 13 | ;; 14 | set) 15 | echo "$VALUE" > "$MDPATH" 16 | ;; 17 | *) 18 | _dl_error "unknown metadata verb '$ACTION'" 19 | ;; 20 | esac 21 | } 22 | 23 | _dl_seti () { _dl_metadata set images "$@"; } 24 | _dl_geti () { _dl_metadata get images "$@"; } 25 | _dl_setc () { _dl_metadata set containers "$@"; } 26 | _dl_getc () { _dl_metadata get containers "$@"; } 27 | -------------------------------------------------------------------------------- /lib/dockerlite-mkc.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd mkc "create a new container from an image" 2 | _dl_mkc () { 3 | IID=$1 4 | CNAME=$2 5 | [ "$IID" ] || _dl_error "must specify base image for container" 6 | IID=$(_dl_resolve images $IID) 7 | CID=$(_dl_mkid) 8 | cd $DOCKERLITE_ROOT 9 | mkdir containers/$CID containers/$CID/metadata 10 | _dl_setc $CID image $IID 11 | _dl_setc $CID ctime "$(date)" 12 | _dl_btrfs subvol snapshot images/$IID/rootfs containers/$CID/rootfs 13 | [ "$CNAME" ] && _dl_setc $CID name $CNAME 14 | _dl_allocateipaddr $CID >/dev/null 15 | echo $CID 16 | } -------------------------------------------------------------------------------- /lib/dockerlite-mki.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd mki "create a new image from the state of a container" 2 | _dl_mki () { 3 | cd "$DOCKERLITE_ROOT" 4 | CID=$1 5 | INAME=$2 6 | [ "$CID" ] || _dl_error "you must specify container ID to create an image" 7 | CID=$(_dl_resolve containers $CID) 8 | IID=$(_dl_mkid) 9 | mkdir images/$IID 10 | mkdir images/$IID/metadata 11 | _dl_seti $IID parent $CID 12 | _dl_seti $IID ctime "$(date)" 13 | _dl_btrfs subvol snapshot -r containers/$CID/rootfs images/$IID/rootfs 14 | [ "$INAME" ] && _dl_seti $IID name $INAME 15 | } 16 | -------------------------------------------------------------------------------- /lib/dockerlite-networking.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd lsa "list ip addresses allocated to containers" 2 | _dl_lsa () { 3 | cd $DOCKERLITE_ROOT/networks 4 | ls 5 | } 6 | 7 | _dl_gateway () { 8 | echo 10.1.1.1 9 | } 10 | 11 | _dl_allocateipaddr () { 12 | CID=$1 13 | cd $DOCKERLITE_ROOT 14 | [ -d containers/$CID/metadata ] || \ 15 | _dl_error "cannot allocate addr for $CID (not found)" 16 | PREFIX=10.1.1 17 | FIRST=2 18 | LAST=255 19 | for SUFFIX in $(seq $FIRST $LAST) 20 | do 21 | mkdir networks/$PREFIX.$SUFFIX 2>/dev/null || continue 22 | echo $CID > networks/$PREFIX.$SUFFIX/cid 23 | _dl_setc $CID ipaddr $PREFIX.$SUFFIX/24 24 | echo $PREFIX.$SUFFIX/24 25 | return 26 | done 27 | _dl_error "could not allocate addr (network full?)" 28 | } 29 | 30 | _dl_setipaddr () { 31 | CID=$1 32 | IPADDR=$2 33 | cd $DOCKERLITE_ROOT 34 | [ -d containers/$CID/metadata ] || \ 35 | _dl_error "cannot allocate addr for $CID (not found)" 36 | mkdir networks/$IPADDR 2>/dev/null || _dl_error "address already in use" 37 | echo $CID > networks/$IPADDR/cid 38 | _dl_setc $CID ipaddr $IPADDR/24 39 | } 40 | 41 | _dl_cmd seta "manually allocate ip address to container" 42 | _dl_seta () { 43 | CID=$1 44 | IPADDR=$2 45 | [ "$CID" ] || _dl_error "must specify container" 46 | CID=$(_dl_resolve containers $CID) 47 | if [ "$IPADDR" ] 48 | then _dl_setipaddr $CID $IPADDR 49 | else _dl_allocateipaddr $CID 50 | fi 51 | } -------------------------------------------------------------------------------- /lib/dockerlite-ps.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd ps "list containers and their runtime information" 2 | _dl_ps () { 3 | cd $DOCKERLITE_ROOT/containers 4 | printf "%-8s %-30s %-40s %-30s %-30s %-30s %s\n" "ID" "CONTAINER NAME" "BASE IMAGE" "CREATED" "STARTED" "CMD" "RUNNING" 5 | for CID in $(ls -t) 6 | do 7 | SHORTCID=$(echo $CID | cut -c1-8) 8 | IID=$(_dl_getc $CID image) 9 | printf "%-8s %-30s %-40s %-30s %-30s %-30s %s\n" \ 10 | $SHORTCID "$(_dl_getc $CID name)" "$(_dl_geti $IID name)" \ 11 | "$(_dl_getc $CID ctime)" "$(_dl_getc $CID rtime)" \ 12 | "$(_dl_getc $CID cmd)" "$(_dl_running $CID)" 13 | done 14 | } 15 | -------------------------------------------------------------------------------- /lib/dockerlite-pull.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd pull "pull an image from the docker registry (BROKEN)" 2 | _dl_pull () { 3 | _dl_error "sorry, this code is broken for now" 4 | TAG=$1 5 | LATEST=$(_dl_curl library/$TAG | grep '"latest"' | cut -d\" -f4) 6 | CID=$(_dl_mkc $(_dl_zerohash) pull-$TAG-$LATEST) 7 | _dl_layerpull $CID $LATEST 8 | } 9 | 10 | _dl_curl () { 11 | curl --location --silent https://index.docker.io/v1/$1 12 | } 13 | 14 | _dl_jsonextract () { 15 | KEY=$1 16 | $(_dl_sourcedir)/JSON.sh -b | grep -F '["'$KEY'"]' | awk '{print $2}' 17 | } 18 | 19 | _dl_layerpull () { 20 | CID=$1 21 | LAYER=$2 22 | _dl_info "looking at layer $LAYER" 23 | PARENT=$(_dl_curl images/$LAYER/json | _dl_jsonextract parent) 24 | if [ "$PARENT" ] 25 | then 26 | _dl_info "parent is $PARENT" 27 | _dl_layerpull $CID $PARENT 28 | else 29 | _dl_info "no parent" 30 | fi 31 | _dl_info "getting data..." 32 | _dl_curl images/$LAYER/layer | tee /tmp/debug | \ 33 | tar -C $DOCKERLITE_ROOT/containers/$CID/rootfs -zxf- 34 | } 35 | -------------------------------------------------------------------------------- /lib/dockerlite-rmc.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd rmc "destroy a container" 2 | _dl_rmc () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container ID" 5 | CID=$(_dl_resolve containers $CID) 6 | cd $DOCKERLITE_ROOT 7 | _dl_btrfs subvol delete containers/$CID/rootfs 8 | rm -rf containers/$CID 9 | } -------------------------------------------------------------------------------- /lib/dockerlite-rmi.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd rmi "destroy a container" 2 | _dl_rmi () { 3 | IID=$1 4 | [ "$IID" ] || _dl_error "must specify image to destroy" 5 | IID=$(_dl_resolve images $IID) 6 | cd $DOCKERLITE_ROOT 7 | _dl_btrfs subvol delete images/$IID/rootfs 8 | rm -rf images/$IID 9 | } -------------------------------------------------------------------------------- /lib/dockerlite-rootfs.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd rootfs "show root fs of a container" 2 | _dl_rootfs () { 3 | CID=$1 4 | [ "$1" ] || error "must specify container" 5 | CID=$(_dl_resolve containers $CID) 6 | [ -d $DOCKERLITE_ROOT/containers/$CID ] || error "container does not exist" 7 | echo $DOCKERLITE_ROOT/containers/$CID/rootfs 8 | } -------------------------------------------------------------------------------- /lib/dockerlite-run.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd run "alias for runi" 2 | _dl_run () { 3 | _dl_runi "$@" 4 | } -------------------------------------------------------------------------------- /lib/dockerlite-runc.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd runc "run a command in an existing container" 2 | _dl_runc () { 3 | CID=$1 4 | CMD=$2 5 | cd $DOCKERLITE_ROOT 6 | [ "$CID" ] || _dl_error "please specify container to run" 7 | CID=$(_dl_resolve containers $CID) 8 | shift 9 | if [ "$CMD" ] 10 | then shift 11 | else CMD=/bin/bash 12 | fi 13 | 14 | cat > containers/$CID/lxc <> containers/$CID/lxc 50 | else 51 | _dl_warning "no /etc/resolv.conf in container" 52 | fi 53 | 54 | if [ -f containers/$CID/rootfs/bin/true ] 55 | then 56 | echo "lxc.mount.entry=$(_dl_sourcedir)/true $DOCKERLITE_ROOT/containers/$CID/rootfs/bin/true none defaults,bind,ro 0 0" >> containers/$CID/lxc 57 | else 58 | _dl_warning "no /bin/true in container" 59 | fi 60 | 61 | which curl >/dev/null || _dl_error "curl not found, please install it first" 62 | 63 | ( 64 | for RETRY in $(seq 1 10) 65 | do 66 | sleep 1 67 | PID=$(_dl_findpid $CID 2>/dev/null) || continue 68 | mkdir -p /var/run/netns 69 | rm -f /var/run/netns/$CID 70 | ln -s /proc/$PID/ns/net /var/run/netns/$CID 71 | ip netns exec $CID ifconfig eth0 $(_dl_getc $CID ipaddr) 72 | ip netns exec $CID route add default gw $(_dl_gateway) 73 | ip netns exec $CID curl localhost:9 2>/dev/null 74 | exit 0 75 | done 76 | _dl_error "could not setup networking stack" 77 | ) & 78 | 79 | _dl_setc $CID cmd "$CMD $*" 80 | _dl_setc $CID rtime "$(date)" 81 | 82 | lxc-start --name $CID --rcfile $DOCKERLITE_ROOT/containers/$CID/lxc -- \ 83 | /bin/true --stop-hammer-time \ 84 | env TERM=$TERM PATH=/usr/sbin:/usr/bin:/sbin:/bin HOME=/ \ 85 | "$CMD" "$@" 86 | } 87 | -------------------------------------------------------------------------------- /lib/dockerlite-runi.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd runi "create a new container from an image and run a command in it" 2 | _dl_runi() { 3 | IID=$1 4 | [ "$IID" ] || _dl_error "please specify image" 5 | shift 6 | IID=$(_dl_resolve images $IID) 7 | CID=$(_dl_mkc $IID) 8 | _dl_runc $CID "$@" 9 | } -------------------------------------------------------------------------------- /lib/dockerlite-running.sh: -------------------------------------------------------------------------------- 1 | _dl_cmd running "check if a container is running" 2 | _dl_running () { 3 | CID=$1 4 | [ "$CID" ] || _dl_error "must specify container" 5 | CID=$(_dl_resolve containers $CID) 6 | TASKS=$(_dl_cgroup $CID nonfatal)/tasks 7 | if [ -f $TASKS ] 8 | then 9 | echo Y 10 | return 0 11 | else 12 | echo N 13 | return 1 14 | fi 15 | } -------------------------------------------------------------------------------- /true.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char* argv[]) { 11 | if (argc>1 && 0==strcmp(argv[1], "--stop-hammer-time")) { 12 | int fd, afd; 13 | char* debug=getenv("DOCKERLITE_DEBUG"); 14 | struct sockaddr_in addr; 15 | socklen_t len = sizeof(addr); 16 | if (debug) puts("dockerlite: Stopped. Waiting for signal to resume."); 17 | fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 18 | addr.sin_family = AF_INET; 19 | addr.sin_port = htons(9); 20 | addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 21 | if (-1 == bind(fd, (struct sockaddr*)&addr, sizeof(addr))) { 22 | perror("bind"); 23 | } else { 24 | if (-1 == listen(fd, 1)) { 25 | perror("listen"); 26 | } else { 27 | afd = accept(fd, (struct sockaddr*)&addr, &len); 28 | if (-1 == afd) { 29 | perror("accept"); 30 | } 31 | close(afd); 32 | } 33 | } 34 | close(fd); 35 | if (debug) puts("dockerlite: Got signal. Resuming."); 36 | execvp(argv[2], argv+2); 37 | } 38 | exit(0); 39 | } 40 | --------------------------------------------------------------------------------