├── Makefile ├── README.md ├── containerize.pro └── main.cpp /Makefile: -------------------------------------------------------------------------------- 1 | containerize: main.cpp 2 | g++ -std=c++11 main.cpp -o containerize -lboost_system -lboost_program_options -lboost_filesystem 3 | install: 4 | sudo cp ./containerize /usr/bin 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Containerize 2 | Tool for creating minimal Docker containers. License is zlib/libpng. 3 | 4 | It does work automatically in very simple cases but might need manual intervention when executables have runtime dependencies not detected by LDD. This is the case with OpenCL driver dependencies which needs custom ugly haxxes. 5 | --opencl flag is used to collect the minimal OpenCL driver, which currently works with NVIDIA and Intel OpenCL drivers. 6 | 7 | The point is, instead of using some pre-made 700mb large Docker image with crap you don't need, this tool helps you deploy ELF binaries with minimal overhead (20mb or whatever). The point of Docker is to skip the crap you previously needed when deploying complete OS:es in virtual machines. 8 | 9 | ### Usage 10 | ``` 11 | Usage: containerize [options] ELF args.. 12 | 13 | options: 14 | --expose Expose container port 15 | --from (=scratch) Derive from image 16 | --tar Tar container instead of zip 17 | --filename (=container) Output filename without extension 18 | --essentials Base container on busybox, a minimal <3mb 19 | image 20 | --opencl Include /etc/OpenCL/vendors and its 21 | resolved content 22 | --help Show this help 23 | ``` 24 | 25 | Containerize focuses on a Linux executable (ELF) and collects its dependencies (shared objects), packages them with the ELF and writes a Dockerfile. Basic features like exposing a network port and deriving the image from others are supported. 26 | 27 | If you run your local app like: **./my_app arg_1 arg_2** then to create a container of this app you simply add **containerize** as a prefix, like so: **containerize ./my_app arg_1 arg_2**. This will create a container. To expose a network port you have to add the --expose argument accordingly. 28 | -------------------------------------------------------------------------------- /containerize.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | SOURCES += main.cpp 7 | 8 | QMAKE_CXXFLAGS += -std=c++11 9 | 10 | LIBS += -lboost_program_options -lboost_filesystem -lboost_system 11 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2015 Alex Hultman 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgement in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution.*/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | using namespace std; 26 | 27 | #include 28 | #include 29 | #include 30 | using namespace boost::filesystem; 31 | using namespace boost::program_options; 32 | using namespace boost::algorithm; 33 | 34 | vector locate(string elf) 35 | { 36 | FILE *pipe = popen(("locate " + elf).c_str(), "r"); 37 | vector lines; 38 | string line; 39 | for(int c; (c = fgetc(pipe)) != EOF; /*line += (char) c*/) { 40 | if (c == '\n') 41 | { 42 | lines.push_back(line); 43 | line.clear(); 44 | } 45 | else { 46 | line += (char) c; 47 | } 48 | } 49 | pclose(pipe); 50 | return lines; 51 | } 52 | 53 | void depsolve(string elf, set &files) 54 | { 55 | files.insert(elf); 56 | FILE *pipe = popen(("ldd " + elf + " 2> /dev/null").c_str(), "r"); 57 | string line; 58 | for(int c; (c = fgetc(pipe)) != EOF; line += (char) c) 59 | if(c == '\n') 60 | { 61 | size_t start; 62 | if((start = line.find('/')) != string::npos) 63 | files.insert(line.substr(start, line.find(' ', start) - start)); 64 | line.clear(); 65 | } 66 | pclose(pipe); 67 | } 68 | 69 | string resolveCommand(string command) 70 | { 71 | FILE *pipe = popen(("which " + command + " 2> /dev/null").c_str(), "r"); 72 | string line; 73 | for(int c; (c = fgetc(pipe)) != EOF && c != '\n'; line += (char) c); 74 | pclose(pipe); 75 | return line.length() ? line : command; 76 | } 77 | 78 | int main(int argc, char **argv) 79 | { 80 | int expose = -1; 81 | bool tar = false, essentials = false, opencl = false; 82 | string output, from; 83 | vector cmd; 84 | try 85 | { 86 | options_description options("options"), hidden, all; 87 | options.add_options()("expose", value(&expose)->value_name(""), "Expose container port ") 88 | ("from", value(&from)->value_name("")->default_value("scratch"), "Derive from image ") 89 | ("tar", bool_switch(&tar), "Tar container instead of zip") 90 | ("filename", value(&output)->value_name("")->default_value("container"), "Output filename without extension") 91 | ("essentials", bool_switch(&essentials), "Base container on busybox, a minimal <3mb image") 92 | ("opencl", bool_switch(&opencl), "Include /etc/OpenCL/vendors and its resolved content") 93 | ("help", "Show this help"); 94 | 95 | hidden.add_options()("cmd", value>(&cmd)); 96 | all.add(options).add(hidden); 97 | 98 | positional_options_description pd; 99 | pd.add("cmd", -1); 100 | 101 | variables_map vm; 102 | store(command_line_parser(argc, argv).options(all).positional(pd).run(), vm); 103 | notify(vm); 104 | 105 | if(argc == 1 || vm.count("help")) 106 | { 107 | cout << "Usage: containerize [options] ELF args.." << endl << endl; 108 | cout << options << endl; 109 | return 0; 110 | } 111 | } 112 | catch(exception &e) 113 | { 114 | cerr << "error: " << e.what() << "\n"; 115 | return 1; 116 | } 117 | 118 | set files; 119 | string currentPath = current_path().string(); 120 | for(recursive_directory_iterator it(currentPath), end; it != end; it++) 121 | if(is_regular_file(it->status())) 122 | depsolve(it->path().string().substr(currentPath.length() + 1), files); 123 | 124 | if (opencl) { 125 | for(recursive_directory_iterator it("/etc/OpenCL/vendors"), end; it != end; it++) 126 | if(is_regular_file(it->status())) 127 | { 128 | files.insert(it->path().string()); 129 | ifstream fin(it->path().string()); 130 | string driver; 131 | fin >> driver; 132 | 133 | for (string &x : locate(driver)) { 134 | depsolve(x, files); 135 | } 136 | 137 | // Ugly haxx for nvidia driver 138 | if (driver.substr(0, 16) == "libnvidia-opencl") { 139 | for (string &x : locate("libnvidia-compiler.so")) { 140 | files.insert(x); 141 | } 142 | } 143 | 144 | // Ugly haxx for intel driver 145 | if (driver.substr(0, 10) == "/opt/intel") { 146 | for(recursive_directory_iterator it(path(driver).parent_path()), end; it != end; it++) 147 | if(is_regular_file(it->status())) { 148 | files.insert(it->path().string()); 149 | //depsolve(it->path().string(), files); 150 | } 151 | } 152 | 153 | depsolve(driver, files); 154 | } 155 | } 156 | 157 | cmd[0] = resolveCommand(cmd[0]); 158 | 159 | // Strip current path base 160 | if (!cmd[0].compare(0, currentPath.length(), currentPath)) { 161 | cmd[0] = cmd[0].substr(currentPath.length()); 162 | } 163 | 164 | depsolve(cmd[0], files); 165 | ofstream dockerfile("Dockerfile"); 166 | dockerfile << "FROM " << (essentials ? "busybox" : from) << endl 167 | << "ADD . /" << endl << "CMD [\"" << join(cmd, "\", \"") << "\"]"; 168 | if(expose != -1) 169 | dockerfile << endl << "EXPOSE " << expose << endl; 170 | dockerfile.close(); 171 | 172 | system(((tar ? "tar " : "zip ") + output + " " + join(files, " ") + " Dockerfile > /dev/null && rm Dockerfile").c_str()); 173 | } 174 | --------------------------------------------------------------------------------