view release on metacpan or search on metacpan
These disadvantages are compensated by several advantages:
- +
All applications are automatically updated together with the Linux
distribution of the machine.
- +
The applications do not need additional disk space (except for the
configuration files as well as some directories, bind-mounts and symbolic
links - we're writing about 250-2500 additional inodes and 500-2500 kB of
disk space).
- +
The applications do not use additional main memory when compared to
running outside of the LXC container (except for the overhead of a few
scripts and LXC itself).
App::LXC::Container is a toolbox basically providing three commands:
Starting an application inside of a virtual machine is slower than starting
an application container.
_Additional advantages/disadvantages are welcome._
# BEST PRACTICES
Especially external packages often haven't all their real dependencies
configured. For those it is often necessary to manually add some packages
and bind mount points like the following:
## additional packages
Note that the examples are from Debian.
- fontconfig-config (select `/usr/share/fontconfig`)
- locales (select `/usr/share/locale/locale.alias`)
## additional bind mounts
Note that again the examples are from Debian.
- `/usr/share/fonts`
# KNOWN BUGS
Currently the package only supports Debian based distributions. If you're
using something different please get in touch to extend the support. (The
framework is already there, but the specific commands are missing, and
lib/App/LXC/Container.pm view on Meta::CPAN
=over
=item +
All applications are automatically updated together with the Linux
distribution of the machine.
=item +
The applications do not need additional disk space (except for the
configuration files as well as some directories, bind-mounts and symbolic
links - we're writing about 250-2500 additional inodes and 500-2500 kB of
disk space).
=item +
The applications do not use additional main memory when compared to
running outside of the LXC container (except for the overhead of a few
scripts and LXC itself).
=back
lib/App/LXC/Container.pm view on Meta::CPAN
an application container.
=back
I<Additional advantages/disadvantages are welcome.>
=head1 BEST PRACTICES
Especially external packages often haven't all their real dependencies
configured. For those it is often necessary to manually add some packages
and bind mount points like the following:
=head2 additional packages
Note that the examples are from Debian.
=over
=item fontconfig-config (select C</usr/share/fontconfig>)
=item locales (select C</usr/share/locale/locale.alias>)
=back
=head2 additional bind mounts
Note that again the examples are from Debian.
=over
=item C</usr/share/fonts>
=back
=head1 KNOWN BUGS
lib/App/LXC/Container/Data/common.pm view on Meta::CPAN
=cut
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub content_default_mounts($)
{
_check_singleton(shift);
my @output =
('# Some notes to the list of default mounts (mounts that are needed in',
'# every application container):',
'#',
'# 1. Default mounts are read-only bind mounts.',
'# 2. Other mount options must be specified explicitly in field 2.',
'# 3. Special filesystems must be specified explicitly in field 3.',
'#',
'# In addition to directories (for mount-points) this list may also',
'# contain symbolic links, that are simply copied to the created',
'# configuration.',
'',
'# common:');
local $_;
foreach
('/bin',
'/dev/shm create=dir,rw tmpfs',
# the next 3 are needed by su:
'/etc/login.defs',
'/etc/pam.d',
'/etc/security',
'/lib',
'/root create=dir,rw,mode=700 tmpfs',
'/sbin',
# a shared and writable /tmp and extra unshared /usr/tmp and /var/tmp:
'/tmp create=dir,rw,bind',
'/usr/tmp create=dir,rw tmpfs',
'/var/tmp create=dir,rw tmpfs',
)
{
(my $entry = $_) =~ s/\s+.*//;
next if -l $entry;
next unless -d $entry or -f $entry;
push @output, abs_path($_);
}
return @output;
lib/App/LXC/Container/Data/common.pm view on Meta::CPAN
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub content_x11_mounts($)
{
_check_singleton(shift);
my @output =
('# This is an additional mount configuration file for X11 applications.',
'# See 40-MNT-default.mounts for more explanations.',
'',
'# common:',
'/dev/dri create=dir,rw,bind,optional',
'/usr/share/icons',
'/usr/share/mime',
'/usr/share/pixmaps',
'/var/cache/fontconfig',
'/var/lib/dbus');
return @output
}
#########################################################################
lib/App/LXC/Container/Mounts.pm view on Meta::CPAN
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub implicit_mount_lines($$)
{
my ($self, $path) = @_;
my @mount_lines = ();
if ($self->mount_point($path) == IMPLICIT)
{
push @mount_lines,
'lxc.mount.entry = ' . $path . ' ' . substr($path, 1) .
' none create=' . (-d $path ? 'dir' : 'file') . ',ro,bind 0 0';
}
local $_;
foreach ($self->sub_directories($path))
{ push @mount_lines, $self->implicit_mount_lines($_); }
return @mount_lines;
}
#########################################################################
=head2 B<merge_mount_points> - merge mount-points
lib/App/LXC/Container/Setup.pm view on Meta::CPAN
sub _mark2mount($)
{
my ($conf_str) = @_;
my $mark = substr($conf_str, 0, 2);
my $file = substr($conf_str, 3);
local $_ = $file;
if ($mark eq ' ')
{}
elsif ($mark eq 'RW')
{
$_ = sprintf("%-39s create=%s,rw,bind%s",
$_,
(-d $_ ? 'dir' : 'file'),
# relaxed bind-mounting for /dev and /var items:
(m!^/(?:dev|var)/!) ? ',optional' : '');
}
elsif ($mark eq 'OV')
{
$_ = sprintf("%-39s create=%s,rw\t\t%s",
$_, (-d $_ ? 'dir' : 'file'), 'tmpfs');
}
else
{ fatal 'internal_error__1', "bad mark '$mark' in _mark2mount"; }
return $_;
lib/App/LXC/Container/Setup.pm view on Meta::CPAN
$self->{mounts} = [];
local $_;
while (<$file>)
{
next if m/^\s*(?:#|$)/;
s/\s*#.*$//;
if (m|^\s*(\S+)(\s+\S.*)?$|)
{
my ($path, $special) = ($1, $2);
$_ = (! defined $special ? ' '
: $special =~ m/rw,bind/ ? 'RW' : 'OV') . ' ' . $path;
push @{$self->{mounts}}, $_;
}
else
{ error 'ignoring_unknown_item_in__1__2', $path, $.; }
}
close $file;
}
#########################################################################
lib/App/LXC/Container/Update.pm view on Meta::CPAN
if (m|^\s*(/\S+)(?:\s+(\S+)(?:\s+(\S+)\s*)?)?$|)
{
my ($path, $options, $fsys) = ($1, $2, $3);
my $entry = ($fsys
? $fsys . ' ' . substr($path, 1) . ' ' . $fsys
: $path . ' ' . substr($path, 1) . ' none');
$entry .=
' ' .
($options
? $options
: 'create=' . (-d $path ? 'dir' : 'file') . ',ro,bind')
. ' 0 0';
$self->{mount_entry}{$path} = $entry;
$self->{mount_source}{$path} = $source;
push @{$self->{mounts_of_source}{$source}}, $path;
}
else
{ error 'ignoring_unknown_item_in__1__2', $path, $.; }
}
close $in;
}
lib/App/LXC/Container/Update.pm view on Meta::CPAN
'^(?:' . join('|', values %{$self->{users}}) . '):.*:(/[^:]+):[^:]+$';
# Normally this could never fail:
# uncoverable branch true
open my $pwd, '<', PWD or fatal 'can_t_open__1__2', PWD, $!;
my @users = ();
local $_;
while (<$pwd>)
{
next unless m/$re_users/o;
$self->{mount_entry}{$1} =
$1 . ' ' . substr($1, 1) . ' none create=dir,rw,bind';
$self->{mount_source}{$1} = $key;
push @{$self->{mounts_of_source}{$key}}, $1;
}
close $pwd;
}
#########################################################################
=head2 B<_write_lxc_configuration> - write LXC configuration file
t/03-setup.t view on Meta::CPAN
#########################################################################
# now check configuration created by above monster-test:
check_config_against_regexp
('tt-CNF-test.master', '3rd',
qr{^network=1\nx11=1\naudio=1\nusers=$re_user$}m);
check_config_against_regexp
('tt-MNT-test.mounts', '3rd',
qr{\A\#\ mounts\ for\ container\ test$
.*/t/tmp/usr/bin/2something$
.*/t/tmp/usr/bin/2something\s+create=file,rw\s+tmpfs$
.*/t/tmp/usr/bin/2something\s+create=file,rw,bind$}msx);
check_config_against_regexp
('tt-NOT-test.filter', '3rd',
qr{^/var/log\s+empty\n
.*/t/tmp/usr/bin/2something\s+copy\n
.*/t/tmp/usr/bin/2something\s+nomerge\n
.*/t/tmp/usr/bin/2something\s+ignore\n}mx);
check_config_against_regexp
('tt-PKG-test.packages', '3rd',
qr{\A# package list for container test\n.*^chromium\n\Z}ms);
t/03-setup.t view on Meta::CPAN
'reading bad configuration file ' . $file . ' fails';
}
test_bad_config('bd-NOT-bad.filter', 'bad entry');
test_bad_config('bd-CNF-bad.master', 'bad entry');
test_bad_config('bd-MNT-bad.mounts', 'bad-entry ');
test_bad_config('bd-PKG-bad.packages', 'bad entry');
#########################################################################
# simulate some more valid configuration entries:
like(App::LXC::Container::Setup::_mark2mount('RW /dev/disk'),
qr{^/dev/disk\s+create=dir,rw,bind,optional$},
'valid optional device directory');
like(App::LXC::Container::Setup::_mark2mount('RW /dev/somedevice'),
qr{^/dev/somedevice\s+create=file,rw,bind,optional$},
'valid optional device file');
like(App::LXC::Container::Setup::_mark2mount('OV /'),
qr{^/\s+create=dir,rw\s+tmpfs$},
'valid temporary directory');
#########################################################################
# run tests with other maximum screen sizes:
sub test_other_screen_size($$)
{
my ($w, $h) = @_;
t/06-update.t view on Meta::CPAN
is($_->{audio}, 0, 'master test 8 audio is correct');
is($_->{network}, 0, 'master test 8 network is correct');
is_deeply([sort keys %{$_->{users}}], [0],
'master test 8 users are correct');
is_deeply([sort values %{$_->{users}}], ['root'],
'master test 8 users are correct');
is_deeply($_->{users_from}, ['update-test-4'],
'master test 8 users have correct origin');
is($_->{x11}, 0, 'master test 8 X11 is correct');
$_->_parse_users();
like($_->{mount_entry}{'/root'}, qr{^/root\s+root\s+none create=dir,rw,bind$},
'user test 8 created correct mount entry');
is($_->{mount_source}{'/root'}, 'container users',
'user test 8 created correct mount source');
is($_->{mounts_of_source}{'container users'}[0], '/root',
'user test 8 created correct mount source list');
#########################################################################
# test for packages files:
_setup_file('/lxc/conf/u1-PKG-update-test-1.packages',
'# 2 identical', '', 'chromium', 'chromium');
t/06-update.t view on Meta::CPAN
'lxc.namespace.keep=ipc');
$update_object = App::LXC::Container::Update->new('update-test-1');
$update_object->_parse_mounts();
obj_keys_in_range('mount_entry', 8, 12,
'mounts test 1 has correct entry count');
obj_keys_in_range('mount_source', 8, 12,
'mounts test 1 has correct source count');
is($update_object->{mount_entry}{$path2something},
$path2something . ' ' . substr($path2something, 1)
. ' none create=file,ro,bind 0 0',
'mounts test 1 source entry is correct');
is($update_object->{mount_source}{$path2something}, 'u1-MNT-update-test-1.mounts',
'mounts test 1 source entry is correct');
$update_object = App::LXC::Container::Update->new('update-test-2');
$update_object->_parse_master(); # now with X11 mounts!
$update_object->_parse_mounts();
obj_keys_in_range('mount_entry', 18, 22,
'mounts test 2 has correct entry count');
obj_keys_in_range('mount_source', 18, 22,
'mounts test 2 has correct source count');
is($update_object->{mount_entry}{$path2something},
$path2something . ' ' . substr($path2something, 1)
. ' none create=file,ro,bind 0 0',
'mounts test 2 source entry is correct');
is($update_object->{mount_source}{$path2something}, 'u2-MNT-update-test-2.mounts',
'mounts test 2 source entry is correct');
is($update_object->{mount_entry}{'/usr/share/icons'},
'/usr/share/icons usr/share/icons none create=dir,ro,bind 0 0',
'mounts test 2 source entry for X11 is correct');
is($update_object->{mount_source}{'/usr/share/icons'}, '61-MNT-X11.mounts',
'mounts test 2 source entry for X11 is correct');
$update_object = App::LXC::Container::Update->new('update-test-1', 'update-test-2');
$update_object->_parse_mounts();
obj_keys_in_range('mount_entry', 8, 12,
'mounts test 3 has correct entry count');
obj_keys_in_range('mount_source', 8, 12,
'mounts test 3 has correct source count');
is($update_object->{mount_entry}{$path2something},
$path2something . ' ' . substr($path2something, 1)
. ' none create=file,ro,bind 0 0',
'mounts test 3 source entry is correct');
is($update_object->{mount_source}{$path2something}, 'u2-MNT-update-test-2.mounts',
'mounts test 3 source entry is correct');
$update_object = App::LXC::Container::Update->new('update-test-2', 'update-test-1');
$update_object->_parse_mounts();
obj_keys_in_range('mount_entry', 8, 12,
'mounts test 4 has correct entry count');
obj_keys_in_range('mount_source', 8, 12,
'mounts test 4 has correct source count');
is($update_object->{mount_entry}{$path2something},
$path2something . ' ' . substr($path2something, 1)
. ' none create=file,ro,bind 0 0',
'mounts test 4 source entry is correct');
is($update_object->{mount_source}{$path2something}, 'u1-MNT-update-test-1.mounts',
'mounts test 4 source entry is correct');
#########################################################################
# test for filter files:
_setup_file('/lxc/conf/u1-NOT-update-test-1.filter',
'# 2nd overwrites 1st',
TMP_PATH . '/usr/lib nomerge',
TMP_PATH . '/var/log empty',
t/06-update.t view on Meta::CPAN
'',
'#+ special configuration #+',
'lxc.namespace.keep=ipc',
'',
'#+ container users #+',
'',
'#+ 40-MNT-default\.mounts #+',
# distributions may have additional non-symlink directories here,
# some are missing /dev/shm:
'.*(lxc\.mount\.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
')?lxc\.mount\.entry = /etc/login.defs etc/login.defs none create=file,ro,bind 0 0',
'lxc\.mount\.entry = /etc/pam.d etc/pam.d none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /etc/security etc/security none create=dir,ro,bind 0 0',
'.*lxc\.mount\.entry = tmpfs root tmpfs create=dir,rw,mode=700 0 0',
'.*lxc\.mount\.entry = /tmp tmp none create=dir,rw,bind 0 0',
'lxc\.mount\.entry = tmpfs var/tmp tmpfs create=dir,rw 0 0',
'(lxc\.mount\.entry = /etc/debian_version etc/debian_version none create=file,ro,bind 0 0',
')?',
'#+ 41-MNT-network\.mounts #+',
'lxc\.mount\.entry = /etc/ssl/certs etc/ssl/certs none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/lib/ssl usr/lib/ssl none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/share/ca-certificates usr/share/ca-certificates none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/share/ssl-cert usr/share/ssl-cert none create=dir,ro,bind 0 0',
'',
'#+ 61-MNT-X11\.mounts #+',
'[^#]+#+ u2-MNT-update-test-2\.mounts #+',
'',
'#+ u1-MNT-update-test-1\.mounts #+',
'lxc\.mount\.entry = /.+/bin/2something none create=file,ro,bind 0 0',
'lxc\.mount\.entry = /.+/bin/2something none create=file,ro,bind 0 0',
'',
'#+ 30-PKG-default\.packages #+',
'# coreutils',
'# dash',
'# libc-bin',
'# util-linux',
'#+ 31-PKG-network.packages #+',
'# iproute2',
'#+ 60-PKG-X11.packages #+',
'# fontconfig-config',
t/06-update.t view on Meta::CPAN
'#+ u2-PKG-update-test-2\.packages #+',
'# chromium',
'# evince',
'#+ u1-PKG-update-test-1\.packages #+',
'',
'#+ empty filters #+',
'lxc\.mount\.entry = tmpfs .+/tmp/var/log tmpfs create=dir,rw 0 0',
'lxc\.mount\.entry = tmpfs var/log tmpfs create=dir,rw 0 0',
'',
'#+ mounts derived from above packages #+',
'lxc\.mount\.entry = /.+/tmp/usr/bin/1chromium .+/tmp/usr/bin/1chromium none create=file,ro,bind 0 0',
'lxc\.mount\.entry = /.+/tmp/usr/lib/some/directory/with .+/tmp/usr/lib/some/directory/with none create=dir,ro,bind 0 0'
# helper expression to update test after modifying Data/*.pm (move up/down):
#(1?():(
##))
);
my $conf = '';
if (-f CONF_ROOT . '/update-test-1.conf')
{
open my $in, '<', CONF_ROOT . '/update-test-1.conf'
or die "can't open ", CONF_ROOT, '/update-test-1.conf: ', $!;
$conf = join('', <$in>);
t/06-update.t view on Meta::CPAN
'lxc\.mount\.auto = cgroup:ro proc:mixed sys:ro',
'',
'#+ -no privileged users- #+',
'lxc\.idmap = u 0 100000 65536',
'lxc\.idmap = g 0 100000 65536',
'',
'#+ 40-MNT-default\.mounts #+',
# distributions may have additional non-symlink directories here,
# some are missing /dev/shm:
'.*(lxc\.mount\.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
')?lxc\.mount\.entry = /etc/login.defs etc/login.defs none create=file,ro,bind 0 0',
'lxc\.mount\.entry = /etc/pam.d etc/pam.d none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /etc/security etc/security none create=dir,ro,bind 0 0',
'.*lxc\.mount\.entry = tmpfs root tmpfs create=dir,rw,mode=700 0 0',
'.*lxc\.mount\.entry = /tmp tmp none create=dir,rw,bind 0 0',
'lxc\.mount\.entry = tmpfs var/tmp tmpfs create=dir,rw 0 0',
'(lxc\.mount\.entry = /etc/debian_version etc/debian_version none create=file,ro,bind 0 0',
')?',
'#+ 41-MNT-network\.mounts #+',
'lxc\.mount\.entry = /etc/ssl/certs etc/ssl/certs none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/lib/ssl usr/lib/ssl none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/share/ca-certificates usr/share/ca-certificates none create=dir,ro,bind 0 0',
'lxc\.mount\.entry = /usr/share/ssl-cert usr/share/ssl-cert none create=dir,ro,bind 0 0',
'',
'#+ 30-PKG-default\.packages #+',
'# coreutils',
'# dash',
'# libc-bin',
'# util-linux',
'#+ 31-PKG-network.packages #+',
'# iproute2',
'',
'#+ empty filters #+',
'lxc.mount.entry = tmpfs var/log tmpfs create=dir,rw 0 0',
'',
'#+ mounts derived from above packages #+',
'lxc\.mount\.entry = /.+/tmp/usr/bin/2something .+/tmp/usr/bin/2something none create=file,ro,bind 0 0'
# helper expression to update test after modifying Data/*.pm (move up/down):
#(1?():(
#))
);
if (-f $conf_file)
{
open my $in, '<', $conf_file
or die "can't open ", $conf_file . ': ', $!;
$conf = join('', <$in>);
close $in;
_setup_file('/lxc/run-test-1.conf',
'#MASTER:N,-,-',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-1',
'lxc.idmap = u 0 0 1',
'lxc.idmap = u 1 1 1',
'lxc.idmap = u 2 100002 65534',
'lxc.idmap = g 0 0 1',
'lxc.idmap = g 1 1 1',
'lxc.idmap = g 2 100002 65534',
'lxc.mount.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
'lxc.mount.entry = /tmp tmp none create=dir,rw,bind 0 0',
'');
$_ = App::LXC::Container::Run->new('run-test-1', 'root', '/', 'do', 'it');
check_config_object($_,
'valid configuration 1',
[[audio => '-'],
[command => ['do', 'it']],
[dir => '/'],
[gateway => '^$'],
[gids => [1]],
[init => CONF_ROOT . '/run-test-1/lxc-run.sh'],
# tests with 2nd valid configuration:
_setup_dir('/lxc/run-test-2');
_remove_file('/lxc/run-test-2.conf');
_setup_file('/lxc/run-test-2.conf',
'#MASTER:G42,X,A',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-2',
'lxc.net.0.ipv4.address = 10.0.3.42/24',
'lxc.idmap = u 0 100000 65536',
'lxc.idmap = g 0 100000 65536',
'lxc.mount.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
'lxc.mount.entry = /tmp tmp none create=dir,rw,bind 0 0',
'');
_setup_dir('/lxc/run-test-2/etc');
$_ = App::LXC::Container::Run->new('run-test-2', 'root', '/', 'do', 'it');
check_config_object($_,
'valid configuration 2',
[[audio => 'A'],
[command => ['do', 'it']],
[dir => '/'],
[gateway => '^10\.0\.3\.1$'],
[gids => []],