mRuby on Container / helper tools with DSL for your handmade linux containers.
Haconiwa (箱庭
- a miniature garden) is a container builder DSL, by which you can choose any container-related technologies as you like:
- Linux namespace
- Linux control group(cgroup)
- Linux capabilities
- Bind mount / chroot
- Resource limit(rlimit)
- setuid/setgid
- ...
Haconiwa is written in mruby, so you can utilize Ruby DSL for creating your own container.
haconiwa
packages are provided via packagecloud.
Available for: CentOS >= 7 / CentOS ~> 6(Experimental, maybe kernel update required) / Fedora >= 23 / Ubuntu Trusty / Ubuntu Xenial / Debian jessie
(which are supported by best effort...)
(PR: We are welcoming package maintainers for other distro!!!)
Other linuxes users can just download binaries from latest:
VERSION=<specify version>
wget https://github.com/haconiwa/haconiwa/releases/download/v${VERSION}/haconiwa-v${VERSION}.x86_64-pc-linux-gnu.tgz
tar xzf haconiwa-v${VERSION}.x86_64-pc-linux-gnu.tgz
sudo install hacorb hacoirb haconiwa /usr/local/bin
haconiwa
# haconiwa - The MRuby on Container
# commands:
# run - run the container
# attach - attach to existing container
# ...
NOTE: If you'd like using cgroup-related features, install cgroup package such as cgroup-lite
(Ubuntu) or cgroup-bin
(Debian).
If you would not, these installation are not required.
Create mount points:
$ mkdir -p /var/another/root/etc
$ mkdir -p /var/another/root/home
Create the file example.haco
:
Haconiwa.define do |config|
config.name = "new-haconiwa001" # to be hostname
config.bootstrap do |b|
b.strategy = "lxc"
b.os_type = "centos"
end
config.provision do |p|
p.run_shell "yum -y install git"
end
config.cgroup["cpu.shares"] = 2048
config.cgroup["memory.limit_in_bytes"] = 256 * 1024 * 1024
config.cgroup["pids.max"] = 1024
config.add_mount_point "/var/another/root/etc", to: "/var/your_rootfs/etc", readonly: true
config.add_mount_point "/var/another/root/home", to: "/var/your_rootfs/home"
config.mount_independent "procfs"
config.mount_independent "sysfs"
config.chroot_to "/var/your_rootfs"
config.namespace.unshare "ipc"
config.namespace.unshare "uts"
config.namespace.unshare "mount"
config.namespace.unshare "pid"
config.capabilities.allow :all
config.capabilities.drop "cap_sys_admin"
end
Then run the haconiwa create
command to set up container base root filesystem.
$ haconiwa create example.haco
Start bootstrapping rootfs with lxc-create...
...
To re-run provisioning, you can use haconiwa provision
.
Then use haconiwa run
command to make container up.
$ haconiwa run example.haco
When you want to attach existing container:
$ haconiwa attach example.haco
Note: attach
subcommand allows to set PID(--target
) or container name(--name
) for dynamic configuration.
And attach
is not concerned with capabilities which is granted to container. So you can drop or allow specific caps with --drop/--allow
.
config.bootstrap
block support 6 strategies now.
strategy = "lxc"
- needs
lxc-create
command lxc.project_name
to set PJ name. default to the dirnamelxc.os_type
to set OS type installed to
- needs
strategy = "debootstrap"
- needs
debootstrap
command deb.variant
to set Debian variant param to pass debootstrapdeb.debian_release
to set Debian's release name squeeze/jessie/sid and so on...deb.mirror_url
to set mirror URL debootstrap usesdeb.components
to set components installed. eg,'base'
- needs
strategy = "git"
- needs
git
command :) git.git_url
to set the repository URL for clonegit.git_options
to set extragit
options by Array, if necessary
- needs
strategy = "tarball"
- needs
tar
command ;) tb.archive_path
to set the source archive path on your host machinetb.tar_options
to set extratar
options by Array, if necessary
- needs
strategy = "shell" / "mruby"
shell.code
to set a shell or mruby code by string(heredoc is OK). You can pass the mruby code block for"mruby"
config.provision
block support some operations(in the future. now run_shell
only).
run_shell
to set plane shell script(automaticallyset -xe
-ed on run)- We can declare
run_shell
multiple times - Set name by
name:
option, then you can specify provision operation byhaconiwa provision --run-only=...
- We can declare
config.environ
- A hash to pass environment variables to a created container. e.g.config.environ = {"FOO_KEY" => "value", ...}
config.workdir
- The working directory of haconiwa's init commandconfig.command.set_stdout/set_stderr
- Emit command's stdout/err to specified files. This is active only on daemon modeconfig.resource.set_limit
- Set the resource limit of container, usingsetrlimit
config.cgroup
- Assign cgroup parameters via[]=
config.namespace.unshare
- Unshare the namespaces like"mount"
,"ipc"
or"pid" ...
.persist_in
option make the specified namespace persist in a bind-moounted-fileconfig.network
- Specify networking config.Network#namespace
and#container_ip
must be set. Other attributes are#bridge_name
,#bridge_ip
,#veth_host
,#veth_guest
config.capabilities.reset_to_privileged!
- Haconiwa has default capability whitelist to use. If you want to use customized black/whitelist, declare this firstconfig.capabilities.allow
- Allow capabilities on container root. Setting parameters other than:all
should make this acts as whitelistconfig.capabilities.drop
- Drop capabilities of container root. Default to act as blacklistconfig.add_mount_point
- Add the mount point of container. Source directory is resolved from the directory where a user run haconiwaconfig.mount_independent
- Mount the independent filesystems:"procfs", "sysfs", "devtmpfs", "devpts" and "shm"
in the newborn container. Useful if"pid"
or"net"
are unsharedconfig.chroot_to
- The new chroot rootconfig.uid=/config.gid=
- The new container's running uid/gid.groups=
is also respectedconfig.support_reload
- Specify reloadable parameters when invokedhaconiwa reload
command. Only:cgroup
and:resource
are available for now and it is active only when they are defined following configuration blocks. See test cases and examplesconfig.wait_interval
- Specify the sleep interval inwait
andwatchdog
loops by milli secondsconfig.metadata
- Add container's metadata(tagging) by ruby Hashconfig.lxcfs_root
- Set your host'slxcfs
mount point to cooperate with containers
You can pick your own parameters for your use case of container.
e.g. just using mount
namespace unshared, container with common filesystem, limit the cgroups for big resource job and so on.
config.add_general_hook(hookpoint, &block)
- Define hook codes that are invoked through the Haconiwa's spawning process. Hook points are below::before_fork
- Hooked just before the container process is forked:after_fork
- Hooked just after the container process is forked, in forked process:before_chroot
- Hooked just after container settins are applied (e.g. namespace, cgroup, caps, fs mounting) and just before do chroot in forked process:after_chroot
- Hooked just after the chroot is successful, in forked process. This is the last timing before doingexec()
and becoming a new program:before_start_wait
- Hooked before starting towait()
the container process. Hook itself is invoked in the parent process:teardown_container
- Hooked after the container process has quitted, in the parent process.base.exit_status
is set:after_reload
- Hooked just afterhaconiwa reload
is invoked and successful:after_failure
- Hooked just after tha container process is exited with failure.base.exit_status
is set- Every hook can accept one argument
base
, which is Haconiwa::Base object.
- hooks below are system hooks, which are invoked in across-supervisor layer.
:setup
- Hooked just before the supervisor processes are going to be forked:teardown
- Hooked after the supervisor processes have quitted all:system_failure
- Hooked just after tha container process is exited with failure- Either
barn.exit_status
orbarn.system_exception
will be available
- Either
- Every hook can accept one argument
barn
, which is Haconiwa::Barn object.
config.add_async_hook(option, &block)
- Define timer handler. Supported options::msec/:sec/:min/:hour
- First timeout to invoke hook.:interval_msec
- Define the interval timeout hooks, if this paramete exists.- NOTE: Async hook uses POSIX timer and real-time signals internally. Please do not queue real-time signals directly.
config.add_signal_handler(signame, &block)
- Define signal handler at supervisor process(not container itself). Available signals areSIGTTIN/SIGTTOU/SIGUSR1/SIGUSR2
. See handler example.config.validate_real_id(&block{|ruid, rgid| ... })
- Validates the Real UID/GID who invoked haconiwa command. return true if OK, false/nil to mark invalid. This is useful when haconiwa command is set-user-ID root
Please check out sample
directory.
e.g.:
Namespace.unshare(Namespace::CLONE_NEWNS)
Namespace.unshare(Namespace::CLONE_NEWPID)
Mount.make_private "/"
Mount.bind_mount "/var/lib/myroot", "/var/lib/haconiwa/root"
Dir.chroot "/var/lib/haconiwa"
Dir.chdir "/"
c = Process.fork {
Mount.mount "proc", "/proc", :type => "proc"
Exec.exec "/bin/sh"
}
pid, ret = Process.waitpid2 c
puts "Container exited with: #{ret.inspect}"
See dependent gem's READMEs.
rake compile
will create binaries.rake
won't be passed unless you are not on Linux.- This project is built upon great mruby-cli. Please browse its README.
- Versions whose minor versions are even numbers (
0.6, 0.8, 0.10, 1.0...
): Stable release - Versions whose minor versions are odd numbers (
0.7, 0.9, 0.11, 1.1...
): Unstable release. Features added at this version should be broken - I introduced this policy after version
0.5.x
- We create branches as
0.6.x-dev
for release - PRs can be proposed to
master
branch. Maintainers will pick these to stable/unstable dev branches
- Haconiwa DSL compiler
- Networking helpers
- P2P containers
Haconiwa core is under the GPL v3 License: See LICENSE file.
Haconinwa's logo is originally created by @takeshige, and is under Creative Commons License BY-NC-ND 4.0.
Bundled libraries (libcap, libcgroup, libargtable and mruby) are licensed by each authors. See LICENSE_*
file.
For other mgems' licenses, especially ones which are not bundled by mruby-core, please refer their github.com
repository.