Technical Documentation: docker/containerd
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
containerd is an industry-standard container runtime with an emphasis on simplicity, robustness and portability. It is available as a daemon for Linux and Windows, which can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage and network attachments, etc..
containerd is designed to be embedded into a larger system, rather than being used directly by developers or end-users.
State of the Project
containerd currently has two active branches.
There is a v0.2.x branch for the current release of containerd that is being consumed by Docker and others and the master branch is the development branch for the 1.0 roadmap and feature set.
Any PR or issue that is intended for the current v0.2.x release should be tagged with the same v0.2.x tag.
Communication
For async communication and long running discussions please use issues and pull requests on the github repo.
This will be the best place to discuss design and implementation.
For sync communication we have a community slack with a #containerd channel that everyone is welcome to join and chat about development.
Slack: https://dockr.ly/community
Developer Quick-Start
To build the daemon and ctr simple test client, the following build system dependencies are required:
- Go 1.8.x or above (requires 1.8 due to use of golang plugin(s))
- Protoc 3.x compiler and headers (download at the Google protobuf releases page)
- Btrfs headers and libraries for your distribution. Note that building the btrfs driver can be disabled via build tag removing this dependency.
For proper results, install the protoc release into /usr/local on your build system. For example, the following commands will download and install the 3.1.0 release for a 64-bit Linux host:
$ wget -c https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip
$ sudo unzip protoc-3.1.0-linux-x86_64.zip -d /usr/localWith the required dependencies installed, the Makefile target named binaries will compile the ctr and containerd binaries and place them in the bin/ directory. Using sudo make install will place the binaries in /usr/local/bin. When making any changes to the gRPC API, make generate will use the installed protoc compiler to regenerate the API generated code packages.
Note: A build tag is currently available to disable building the btrfs snapshot driver.
AddingBUILDTAGS=no_btrfsto your environment before calling the binaries
Makefile target will disable the btrfs driver within the containerd Go build.
Vendoring of external imports uses the vndr tool which uses a simple config file, vendor.conf, to provide the URL and version or hash details for each vendored import. After modifying vendor.conf run the vndr tool to update the vendor/ directory contents. Combining the vendor.conf update with the changeset in vendor/ after running vndr should become a single commit for a PR which relies on vendored updates.
Please refer to RUNC.md for the currently supported version of runc that is used by containerd.
Features
- OCI Image Spec support
- OCI Runtime Spec support
- Image push and pull support
- Container runtime and lifecycle support
- Management of network namespaces containers to join existing namespaces
- Multi-tenant supported with CAS storage for global images
Scope and Principles
Having a clearly defined scope of a project is important for ensuring consistency and focus.
These following criteria will be used when reviewing pull requests, features, and changes for the project before being accepted.
Components
Components should not have tight dependencies on each other so that they are able to be used independently.
The APIs for images and containers should be designed in a way that when used together the components have a natural flow but still be useful independently.
An example for this design can be seen with the overlay filesystems and the container execution layer.
The execution layer and overlay filesystems can be used independently but if you were to use both, they share a common Mount struct that the filesystems produce and the execution layer consumes.
Primitives
containerd should expose primitives to solve problems instead of building high level abstractions in the API.
A common example of this is how build would be implemented.
Instead of having a build API in containerd we should expose the lower level primitives that allow things required in build to work.
Breaking up the filesystem APIs to allow snapshots, copy functionality, and mounts allow people implementing build at the higher levels more flexibility.
Extensibility and Defaults
For the various components in containerd there should be defined extension points where implementations can be swapped for alternatives.
The best example of this is that containerd will use runc from OCI as the default runtime in the execution layer but other runtimes conforming to the OCI Runtime specification they can be easily added to containerd.
containerd will come with a default implementation for the various components.
These defaults will be chosen by the maintainers of the project and should not change unless better tech for that component comes out.
Additional implementations will not be accepted into the core repository and should be developed in a separate repository not maintained by the containerd maintainers.
Releases
containerd will be released with a 1.0 when feature complete and this version will be supported for 1 year with security and bug fixes applied and released.
The upgrade path for containerd is that the 0.0.x patch releases are always backward compatible with its major and minor version.
Minor (0.x.0) version will always be compatible with the previous minor release. i.e. 1.2.0 is backwards compatible with 1.1.0 and 1.1.0 is compatible with 1.0.0.
There is no compatibility guarantees with upgrades from two minor releases. i.e. 1.0.0 to 1.2.0.
There are not backwards compatibility guarantees with upgrades to major versions. i.e 1.0.0 to 2.0.0.
Each major version will be supported for 1 year with bug fixes and security patches.
Scope
The following table specifies the various components of containerd and general features of container runtimes.
The table specifies whether or not the feature/component is in or out of scope.
| Name | Description | In/Out | Reason |
|---|---|---|---|
| execution | Provide an extensible execution layer for executing a container | in | Create,start, stop pause, resume exec, signal, delete |
| cow filesystem | Built in functionality for overlay, aufs, and other copy on write filesystems for containers | in | |
| distribution | Having the ability to push and pull images as well as operations on images as a first class API object | in | containerd will fully support the management and retrieval of images |
| metrics | container-level metrics, cgroup stats, and OOM events | in | |
| networking | creation and management of network interfaces | out | Networking will be handled and provided to containerd via higher level systems. |
| build | Building images as a first class API | out | Build is a higher level tooling feature and can be implemented in many different ways on top of containerd |
| volumes | Volume management for external data | out | The API supports mounts, binds, etc where all volumes type systems can be built on top of containerd. |
| logging | Persisting container logs | out | Logging can be build on top of containerd because the container’s STDIO will be provided to the clients and they can persist any way they see fit. There is no io copying of container STDIO in containerd. |
containerd is scoped to a single host and makes assumptions based on that fact.
It can be used to build things like a node agent that launches containers but does not have any concepts of a distributed system.
containerd is designed to be embedded into a larger system, hence it only includes a barebone CLI (ctr) specifically for development and debugging purpose, with no mandate to be human-friendly, and no guarantee of interface stability over time.
Also things like service discovery are out of scope even though networking is in scope.
containerd should provide the primitives to create, add, remove, or manage network interfaces and network namespaces for a container but IP allocation, discovery, and DNS should be handled at higher layers.
How is the scope changed?
The scope of this project is a whitelist.
If it's not mentioned as being in scope, it is out of scope.
For the scope of this project to change it requires a 100% vote from all maintainers of the project.
Development reports.
Weekly summary on the progress and what is being worked on.
https://github.com/containerd/containerd/tree/master/reports
Copyright and license
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code
is released under the Apache 2.0 license. The README.md file, and files in the
"docs" folder are licensed under the Creative Commons Attribution 4.0
International License under the terms and conditions set forth in the file
"LICENSE.docs". You may obtain a duplicate copy of the same license, titled
CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
Overview (`docs/README.md`)
Containerd website
The containerd website is built using Jekyll and published to Github pages.
In order to build and test locally:
docker run -v "$PWD":/usr/src/app -p "4000:4000" starefossen/github-pagesThen browser to localhost:4000 to see the rendered site. The site autorefreshes when you modify files locally.
Overview (`vendor/google.golang.org/grpc/README.md`)
#gRPC-Go
The Go implementation of gRPC: A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. For more information see the gRPC Quick Start guide.
Installation
To install this package, you need to install Go and setup your Go workspace on your computer. The simplest way to install the library is to run:
$ go get google.golang.org/grpcPrerequisites
This requires Go 1.5 or later.
A note on the version used: significant performance improvements in benchmarks
of grpc-go have been seen by upgrading the go version from 1.5 to the latest
1.7.1.
From https://golang.org/doc/install, one way to install the latest version of go is:
$ GO_VERSION=1.7.1
$ OS=linux
$ ARCH=amd64
$ curl -O https://storage.googleapis.com/golang/go${GO_VERSION}.${OS}-${ARCH}.tar.gz
$ sudo tar -C /usr/local -xzf go$GO_VERSION.$OS-$ARCH.tar.gz
$ # Put go on the PATH, keep the usual installation dir
$ sudo ln -s /usr/local/go/bin/go /usr/bin/go
$ rm go$GO_VERSION.$OS-$ARCH.tar.gzConstraints
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the list, you need a discussion with gRPC-Go authors and consultants.
Documentation
See API documentation for package and API descriptions and find examples in the examples directory.
Status
GA
FAQ
Compiling error, undefined: grpc.SupportPackageIsVersion
Please update proto package, gRPC package and rebuild the proto files:
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}go get -u google.golang.org/grpcprotoc --go_out=plugins=grpc:. *.proto
Overview (`vendor/github.com/Azure/go-ansiterm/README.md`)
go-ansiterm
This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
See parser_test.go for examples exercising the state machine and generating appropriate function calls.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
Overview (`vendor/github.com/BurntSushi/toml/README.md`)
TOML parser and encoder for Go with reflection
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
reflection interface similar to Go's standard library json and xml
packages. This package also supports the encoding.TextUnmarshaler andencoding.TextMarshaler interfaces so that you can define custom data
representations. (There is an example of this below.)
Spec: https://github.com/mojombo/toml
Compatible with TOML version
v0.2.0
Documentation: http://godoc.org/github.com/BurntSushi/toml
Installation:
go get github.com/BurntSushi/tomlTry the toml validator:
go get github.com/BurntSushi/toml/cmd/tomlv
tomlv some-toml-file.tomlTesting
This package passes all tests in
toml-test for both the decoder
and the encoder.
Examples
This package works similarly to how the Go standard library handles XML
and JSON. Namely, data is loaded into Go values via reflection.
For the simplest example, consider some TOML file as just a list of keys
and values:
Age = 25
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00ZWhich could be defined in Go as:
type Config struct {
Age int
Cats []string
Pi float64
Perfection []int
DOB time.Time // requires `import time`
}And then decoded with:
var conf Config
if _, err := toml.Decode(tomlData, &conf); err != nil {
// handle error
}You can also use struct tags if your struct field name doesn't map to a TOML
key value directly:
some_key_NAME = "wat"type TOML struct {
ObscureKey string `toml:"some_key_NAME"`
}Using the `encoding.TextUnmarshaler` interface
Here's an example that automatically parses duration strings intotime.Duration values:
[[song]]
name = "Thunder Road"
duration = "4m49s"
[[song]]
name = "Stairway to Heaven"
duration = "8m03s"Which can be decoded with:
type song struct {
Name string
Duration duration
}
type songs struct {
Song []song
}
var favorites songs
if _, err := toml.Decode(blob, &favorites); err != nil {
log.Fatal(err)
}
for _, s := range favorites.Song {
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
}And you'll also need a duration type that satisfies theencoding.TextUnmarshaler interface:
type duration struct {
time.Duration
}
func (d *duration) UnmarshalText(text []byte) error {
var err error
d.Duration, err = time.ParseDuration(string(text))
return err
}More complex usage
Here's an example of how to load the example from the official spec page:
# This is a TOML document. Boom.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]And the corresponding Go types are:
type tomlConfig struct {
Title string
Owner ownerInfo
DB database `toml:"database"`
Servers map[string]server
Clients clients
}
type ownerInfo struct {
Name string
Org string `toml:"organization"`
Bio string
DOB time.Time
}
type database struct {
Server string
Ports []int
ConnMax int `toml:"connection_max"`
Enabled bool
}
type server struct {
IP string
DC string
}
type clients struct {
Data [][]interface{}
Hosts []string
}Note that a case insensitive match will be tried if an exact match can't be
found.
A working example of the above can be found in _examples/example.{go,toml}.
Overview (`vendor/github.com/Microsoft/go-winio/README.md`)
go-winio
This repository contains utilities for efficiently performing Win32 IO operations in
Go. Currently, this is focused on accessing named pipes and other file handles, and
for using named pipes as a net transport.
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
newer operating systems. This is similar to the implementation of network sockets in Go's net
package.
Please see the LICENSE file for licensing information.
This project has adopted the Microsoft Open Source Code of
Conduct. For more information
see the Code of Conduct
FAQ or contact
[email protected] with any additional
questions or comments.
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
for another named pipe implementation.
Overview (`vendor/github.com/Microsoft/hcsshim/README.md`)
hcsshim
This package supports launching Windows Server containers from Go. It is
primarily used in the Docker Engine project,
but it can be freely used by other projects as well.
This project has adopted the Microsoft Open Source Code of
Conduct. For more information
see the Code of Conduct
FAQ or contact
[email protected] with any additional
questions or comments.
Overview (`vendor/github.com/Sirupsen/logrus/README.md`)
Logrus [](https://travis-ci.org/Sirupsen/logrus) [](https://godoc.org/github.com/Sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. Please note the Logrus API is not
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest master on
every build.
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
With log.SetFormatter(&log.JSONFormatter{}), for easy parsing by logstash
or Splunk:
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}With the default log.SetFormatter(&log.TextFormatter{}) when a TTY is not
attached, the output is compatible with the
logfmt format:
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1Example
The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}Note that it's completely api-compatible with the stdlib logger, so you can
replace your log imports everywhere with log "github.com/Sirupsen/logrus"
and you'll now have the flexibility of Logrus. You can customize it all you
want:
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the logrus Logger:
package main
import (
"github.com/Sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log.Out = os.Stderr
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}Fields
Logrus encourages careful, structured logging though logging fields instead of
long, unparseable error messages. For example, instead of: log.Fatalf("Failed to send event %s to topic %s with key %d"), you should log the much more
discoverable:
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The WithFields call is optional.
In general, with Logrus using any of the printf-family functions should be
seen as a hint you should add a field, however, you can still use theprintf-family functions with Logrus.
Hooks
You can add hooks for logging levels. For example to send errors to an exception
tracking service on Error, Fatal and Panic, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
Logrus comes with built-in hooks. Add those, or your custom hook, ininit:
import (
log "github.com/Sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the syslog hook README.
| Hook | Description |
|---|---|
| Airbrake | Send errors to the Airbrake API V3. Uses the official gobrake behind the scenes. |
| Airbrake "legacy" | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses airbrake-go behind the scenes. |
| Papertrail | Send errors to the Papertrail hosted logging service via UDP. |
| Syslog | Send errors to remote syslog server. Uses standard library log/syslog behind the scenes. |
| Bugsnag | Send errors to the Bugsnag exception tracking service. |
| Sentry | Send errors to the Sentry error logging and aggregation service. |
| Hiprus | Send errors to a channel in hipchat. |
| Logrusly | Send logs to Loggly |
| Slackrus | Hook for Slack chat. |
| Journalhook | Hook for logging to systemd-journald |
| Graylog | Hook for logging to Graylog |
| Raygun | Hook for logging to Raygun.io |
| LFShook | Hook for logging to the local filesystem |
| Honeybadger | Hook for sending exceptions to Honeybadger |
| Hook for sending exceptions via mail | |
| Rollrus | Hook for sending errors to rollbar |
| Fluentd | Hook for logging to fluentd |
| Mongodb | Hook for logging to mongodb |
| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
| InfluxDB | Hook for logging to influxdb |
| Octokit | Hook for logging to github via octokit |
| DeferPanic | Hook for logging to DeferPanic |
| Redis-Hook | Hook for logging to a ELK stack (through Redis) |
| Amqp-Hook | Hook for logging to Amqp broker (Like RabbitMQ) |
| KafkaLogrus | Hook for logging to kafka |
| Typetalk | Hook for logging to Typetalk |
| ElasticSearch | Hook for logging to ElasticSearch |
| Sumorus | Hook for logging to SumoLogic |
| Scribe | Hook for logging to Scribe |
| Logstash | Hook for logging to Logstash |
| logz.io | Hook for logging to logz.io, a Log as a Service using Logstash |
| Logmatic.io | Hook for logging to Logmatic.io |
| Pushover | Send error via Pushover |
Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")You can set the logging level on a Logger, then it will only log entries with
that severity or anything above it:
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log.SetLevel(log.InfoLevel)It may be useful to set log.Level = logrus.DebugLevel in a debug or verbose
environment if your application has that.
Entries
Besides the fields added with WithField or WithFields some fields are
automatically added to all logging events:
time. The timestamp when the entry was created.msg. The logging message passed to{Info,Warn,Error,Fatal,Panic}after
theAddFieldscall. E.g.Failed to send event.level. The logging level. E.g.info.
Environments
Logrus has no notion of environment.
If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable Environment, which is a string representation of the environment you
could do:
import (
log "github.com/Sirupsen/logrus"
)
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log.SetFormatter(&log.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
log.SetFormatter(&log.TextFormatter{})
}
}This configuration is how logrus was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
Formatters
The built-in logging formatters are:
logrus.TextFormatter. Logs the event in colors if stdout is a tty, otherwise
without colors.- Note: to force colored output when there is no TTY, set the
ForceColors
field totrue. To force no colored output even if there is a TTY set theDisableColorsfield totrue
- Note: to force colored output when there is no TTY, set the
logrus.JSONFormatter. Logs fields as JSON.
Third party logging formatters:
logstash. Logs fields as Logstash Events.prefixed. Displays log entry source along with alternative layout.zalgo. Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the Formatter interface,
requiring a Format method. Format takes an *Entry. entry.Data is aFields type (map[string]interface{}) with all your fields as well as the
default ones (see Entries section above):
type MyJSONFormatter struct {
}
log.SetFormatter(new(MyJSONFormatter))
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}Logger as an `io.Writer`
Logrus can be transformed into an io.Writer. That writer is the end of an io.Pipe and it is your responsibility to close it.
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is info.
Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like logrotate(8)) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
Tools
| Tool | Description |
|---|---|
| Logrus Mate | Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment. |
| Logrus Viper Helper | An Helper arround Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of Logrus Mate. sample |
Testing
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the test hook and provides:
- decorators for existing logger (
test.NewLocalandtest.NewGlobal) which basically just add thetesthook - a test logger (
test.NewNullLogger) that just records log messages (and does not output any):
logger, hook := NewNullLogger()
logger.Error("Hello error")
assert.Equal(1, len(hook.Entries))
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
hook.Reset()
assert.Nil(hook.LastEntry())Fatal handlers
Logrus can register one or more functions that will be called when any fatal
level message is logged. The registered handlers will be executed before
logrus performs a os.Exit(1). This behavior may be helpful if callers need
to gracefully shutdown. Unlike a panic("Something went wrong...") call which can be intercepted with a deferred recover a call to os.Exit(1) can not be intercepted.
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...Thread safty
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
Situation when locking is not needed includes:
You have no hooks registered, or hooks calling is already thread-safe.
Writing to logger.Out is already thread-safe, for example:
logger.Out is protected by locks.
logger.Out is a os.File handler opened with
O_APPENDflag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
Overview (`vendor/github.com/beorn7/perks/README.md`)
Perks for Go (golang.org)
Perks contains the Go package quantile that computes approximate quantiles over
an unbounded data stream within low memory and CPU bounds.
For more information and examples, see:
http://godoc.org/github.com/bmizerany/perks
A very special thank you and shout out to Graham Cormode (Rutgers University),
Flip Korn (AT&T Labs–Research), S. Muthukrishnan (Rutgers University), and
Divesh Srivastava (AT&T Labs–Research) for their research and publication of
Effective Computation of Biased Quantiles over Data Streams
Thank you, also:
- Armon Dadgar (@armon)
- Andrew Gerrand (@nf)
- Brad Fitzpatrick (@bradfitz)
- Keith Rarick (@kr)
FAQ:
Q: Why not move the quantile package into the project root?
A: I want to add more packages to perks later.
Copyright (C) 2013 Blake Mizerany
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Overview (`vendor/github.com/boltdb/bolt/README.md`)
Bolt [](https://coveralls.io/r/boltdb/bolt?branch=master) [](https://godoc.org/github.com/boltdb/bolt)
Bolt is a pure Go key/value store inspired by Howard Chu's
LMDB project. The goal of the project is to provide a simple,
fast, and reliable database for projects that don't require a full database
server such as Postgres or MySQL.
Since Bolt is meant to be used as such a low-level piece of functionality,
simplicity is key. The API will be small and only focus on getting values
and setting values. That's it.
Project Status
Bolt is stable, the API is fixed, and the file format is fixed. Full unit
test coverage and randomized black box testing are used to ensure database
consistency and thread safety. Bolt is currently used in high-load production
environments serving databases as large as 1TB. Many companies such as
Shopify and Heroku use Bolt-backed services every day.
Table of Contents
- Getting Started
- Resources
- Comparison with other databases
- Caveats & Limitations
- Reading the Source
- Other Projects Using Bolt
Getting Started
Installing
To start using Bolt, install Go and run go get:
$ go get github.com/boltdb/bolt/...This will retrieve the library and install the bolt command line utility into
your $GOBIN path.
Opening a database
The top-level object in Bolt is a DB. It is represented as a single file on
your disk and represents a consistent snapshot of your data.
To open your database, simply use the bolt.Open() function:
package main
import (
"log"
"github.com/boltdb/bolt"
)
func main() {
// Open the my.db data file in your current directory.
// It will be created if it doesn't exist.
db, err := bolt.Open("my.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
...
}Please note that Bolt obtains a file lock on the data file so multiple processes
cannot open the same database at the same time. Opening an already open Bolt
database will cause it to hang until the other process closes it. To prevent
an indefinite wait you can pass a timeout option to the Open() function:
db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second})Transactions
Bolt allows only one read-write transaction at a time but allows as many
read-only transactions as you want at a time. Each transaction has a consistent
view of the data as it existed when the transaction started.
Individual transactions and all objects created from them (e.g. buckets, keys)
are not thread safe. To work with data in multiple goroutines you must start
a transaction for each one or use locking to ensure only one goroutine accesses
a transaction at a time. Creating transaction from the DB is thread safe.
Read-only transactions and read-write transactions should not depend on one
another and generally shouldn't be opened simultaneously in the same goroutine.
This can cause a deadlock as the read-write transaction needs to periodically
re-map the data file but it cannot do so while a read-only transaction is open.
Read-write transactions
To start a read-write transaction, you can use the DB.Update() function:
err := db.Update(func(tx *bolt.Tx) error {
...
return nil
})Inside the closure, you have a consistent view of the database. You commit the
transaction by returning nil at the end. You can also rollback the transaction
at any point by returning an error. All database operations are allowed inside
a read-write transaction.
Always check the return error as it will report any disk failures that can cause
your transaction to not complete. If you return an error within your closure
it will be passed through.
Read-only transactions
To start a read-only transaction, you can use the DB.View() function:
err := db.View(func(tx *bolt.Tx) error {
...
return nil
})You also get a consistent view of the database within this closure, however,
no mutating operations are allowed within a read-only transaction. You can only
retrieve buckets, retrieve values, and copy the database within a read-only
transaction.
Batch read-write transactions
Each DB.Update() waits for disk to commit the writes. This overhead
can be minimized by combining multiple updates with the DB.Batch()
function:
err := db.Batch(func(tx *bolt.Tx) error {
...
return nil
})Concurrent Batch calls are opportunistically combined into larger
transactions. Batch is only useful when there are multiple goroutines
calling it.
The trade-off is that Batch can call the given
function multiple times, if parts of the transaction fail. The
function must be idempotent and side effects must take effect only
after a successful return from DB.Batch().
For example: don't display messages from inside the function, instead
set variables in the enclosing scope:
var id uint64
err := db.Batch(func(tx *bolt.Tx) error {
// Find last key in bucket, decode as bigendian uint64, increment
// by one, encode back to []byte, and add new key.
...
id = newValue
return nil
})
if err != nil {
return ...
}
fmt.Println("Allocated ID %d", id)Managing transactions manually
The DB.View() and DB.Update() functions are wrappers around the DB.Begin()
function. These helper functions will start the transaction, execute a function,
and then safely close your transaction if an error is returned. This is the
recommended way to use Bolt transactions.
However, sometimes you may want to manually start and end your transactions.
You can use the DB.Begin() function directly but please be sure to close
the transaction.
// Start a writable transaction.
tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
// Use the transaction...
_, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return err
}
// Commit the transaction and check for error.
if err := tx.Commit(); err != nil {
return err
}The first argument to DB.Begin() is a boolean stating if the transaction
should be writable.
Using buckets
Buckets are collections of key/value pairs within the database. All keys in a
bucket must be unique. You can create a bucket using the DB.CreateBucket()
function:
db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
return nil
})You can also create a bucket only if it doesn't exist by using theTx.CreateBucketIfNotExists() function. It's a common pattern to call this
function for all your top-level buckets after you open your database so you can
guarantee that they exist for future transactions.
To delete a bucket, simply call the Tx.DeleteBucket() function.
Using key/value pairs
To save a key/value pair to a bucket, use the Bucket.Put() function:
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
err := b.Put([]byte("answer"), []byte("42"))
return err
})This will set the value of the "answer" key to "42" in the MyBucket
bucket. To retrieve this value, we can use the Bucket.Get() function:
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("answer"))
fmt.Printf("The answer is: %s\n", v)
return nil
})The Get() function does not return an error because its operation is
guaranteed to work (unless there is some kind of system failure). If the key
exists then it will return its byte slice value. If it doesn't exist then it
will return nil. It's important to note that you can have a zero-length value
set to a key which is different than the key not existing.
Use the Bucket.Delete() function to delete a key from the bucket.
Please note that values returned from Get() are only valid while the
transaction is open. If you need to use a value outside of the transaction
then you must use copy() to copy it to another byte slice.
Autoincrementing integer for the bucket
By using the NextSequence() function, you can let Bolt determine a sequence
which can be used as the unique identifier for your key/value pairs. See the
example below.
// CreateUser saves u to the store. The new user ID is set on u once the data is persisted.
func (s *Store) CreateUser(u *User) error {
return s.db.Update(func(tx *bolt.Tx) error {
// Retrieve the users bucket.
// This should be created when the DB is first opened.
b := tx.Bucket([]byte("users"))
// Generate ID for the user.
// This returns an error only if the Tx is closed or not writeable.
// That can't happen in an Update() call so I ignore the error check.
id, _ := b.NextSequence()
u.ID = int(id)
// Marshal user data into bytes.
buf, err := json.Marshal(u)
if err != nil {
return err
}
// Persist bytes to users bucket.
return b.Put(itob(u.ID), buf)
})
}
// itob returns an 8-byte big endian representation of v.
func itob(v int) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
type User struct {
ID int
...
}Iterating over keys
Bolt stores its keys in byte-sorted order within a bucket. This makes sequential
iteration over these keys extremely fast. To iterate over keys we'll use aCursor:
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("MyBucket"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})The cursor allows you to move to a specific point in the list of keys and move
forward or backward through the keys one at a time.
The following functions are available on the cursor:
First() Move to the first key.
Last() Move to the last key.
Seek() Move to a specific key.
Next() Move to the next key.
Prev() Move to the previous key.Each of those functions has a return signature of (key []byte, value []byte).
When you have iterated to the end of the cursor then Next() will return anil key. You must seek to a position using First(), Last(), or Seek()
before calling Next() or Prev(). If you do not seek to a position then
these functions will return a nil key.
During iteration, if the key is non-nil but the value is nil, that means
the key refers to a bucket rather than a value. Use Bucket.Bucket() to
access the sub-bucket.
Prefix scans
To iterate over a key prefix, you can combine Seek() and bytes.HasPrefix():
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
c := tx.Bucket([]byte("MyBucket")).Cursor()
prefix := []byte("1234")
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})Range scans
Another common use case is scanning over a range such as a time range. If you
use a sortable time encoding such as RFC3339 then you can query a specific
date range like this:
db.View(func(tx *bolt.Tx) error {
// Assume our events bucket exists and has RFC3339 encoded time keys.
c := tx.Bucket([]byte("Events")).Cursor()
// Our time range spans the 90's decade.
min := []byte("1990-01-01T00:00:00Z")
max := []byte("2000-01-01T00:00:00Z")
// Iterate over the 90's.
for k, v := c.Seek(min); k != nil && bytes.Compare(k, max) <= 0; k, v = c.Next() {
fmt.Printf("%s: %s\n", k, v)
}
return nil
})Note that, while RFC3339 is sortable, the Golang implementation of RFC3339Nano does not use a fixed number of digits after the decimal point and is therefore not sortable.
ForEach()
You can also use the function ForEach() if you know you'll be iterating over
all the keys in a bucket:
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("MyBucket"))
b.ForEach(func(k, v []byte) error {
fmt.Printf("key=%s, value=%s\n", k, v)
return nil
})
return nil
})Please note that keys and values in ForEach() are only valid while
the transaction is open. If you need to use a key or value outside of
the transaction, you must use copy() to copy it to another byte
slice.
Nested buckets
You can also store a bucket in a key to create nested buckets. The API is the
same as the bucket management API on the DB object:
func (*Bucket) CreateBucket(key []byte) (*Bucket, error)
func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error)
func (*Bucket) DeleteBucket(key []byte) errorSay you had a multi-tenant application where the root level bucket was the account bucket. Inside of this bucket was a sequence of accounts which themselves are buckets. And inside the sequence bucket you could have many buckets pertaining to the Account itself (Users, Notes, etc) isolating the information into logical groupings.
// createUser creates a new user in the given account.
func createUser(accountID int, u *User) error {
// Start the transaction.
tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
// Retrieve the root bucket for the account.
// Assume this has already been created when the account was set up.
root := tx.Bucket([]byte(strconv.FormatUint(accountID, 10)))
// Setup the users bucket.
bkt, err := root.CreateBucketIfNotExists([]byte("USERS"))
if err != nil {
return err
}
// Generate an ID for the new user.
userID, err := bkt.NextSequence()
if err != nil {
return err
}
u.ID = userID
// Marshal and save the encoded user.
if buf, err := json.Marshal(u); err != nil {
return err
} else if err := bkt.Put([]byte(strconv.FormatUint(u.ID, 10)), buf); err != nil {
return err
}
// Commit the transaction.
if err := tx.Commit(); err != nil {
return err
}
return nil
}Database backups
Bolt is a single file so it's easy to backup. You can use the Tx.WriteTo()
function to write a consistent view of the database to a writer. If you call
this from a read-only transaction, it will perform a hot backup and not block
your other database reads and writes.
By default, it will use a regular file handle which will utilize the operating
system's page cache. See the Tx
documentation for information about optimizing for larger-than-RAM datasets.
One common use case is to backup over HTTP so you can use tools like cURL to
do database backups:
func BackupHandleFunc(w http.ResponseWriter, req *http.Request) {
err := db.View(func(tx *bolt.Tx) error {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
_, err := tx.WriteTo(w)
return err
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}Then you can backup using this command:
$ curl http://localhost/backup > my.dbOr you can open your browser to http://localhost/backup and it will download
automatically.
If you want to backup to another file you can use the Tx.CopyFile() helper
function.
Statistics
The database keeps a running count of many of the internal operations it
performs so you can better understand what's going on. By grabbing a snapshot
of these stats at two points in time we can see what operations were performed
in that time range.
For example, we could start a goroutine to log stats every 10 seconds:
go func() {
// Grab the initial stats.
prev := db.Stats()
for {
// Wait for 10s.
time.Sleep(10 * time.Second)
// Grab the current stats and diff them.
stats := db.Stats()
diff := stats.Sub(&prev)
// Encode stats to JSON and print to STDERR.
json.NewEncoder(os.Stderr).Encode(diff)
// Save stats for the next loop.
prev = stats
}
}()It's also useful to pipe these stats to a service such as statsd for monitoring
or to provide an HTTP endpoint that will perform a fixed-length sample.
Read-Only Mode
Sometimes it is useful to create a shared, read-only Bolt database. To this,
set the Options.ReadOnly flag when opening your database. Read-only mode
uses a shared lock to allow multiple processes to read from the database but
it will block any processes from opening the database in read-write mode.
db, err := bolt.Open("my.db", 0666, &bolt.Options{ReadOnly: true})
if err != nil {
log.Fatal(err)
}Mobile Use (iOS/Android)
Bolt is able to run on mobile devices by leveraging the binding feature of the
gomobile tool. Create a struct that will
contain your database logic and a reference to a *bolt.DB with a initializing
constructor that takes in a filepath where the database file will be stored.
Neither Android nor iOS require extra permissions or cleanup from using this method.
func NewBoltDB(filepath string) *BoltDB {
db, err := bolt.Open(filepath+"/demo.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
return &BoltDB{db}
}
type BoltDB struct {
db *bolt.DB
...
}
func (b *BoltDB) Path() string {
return b.db.Path()
}
func (b *BoltDB) Close() {
b.db.Close()
}Database logic should be defined as methods on this wrapper struct.
To initialize this struct from the native language (both platforms now sync
their local storage to the cloud. These snippets disable that functionality for the
database file):
Android
String path;
if (android.os.Build.VERSION.SDK_INT >=android.os.Build.VERSION_CODES.LOLLIPOP){
path = getNoBackupFilesDir().getAbsolutePath();
} else{
path = getFilesDir().getAbsolutePath();
}
Boltmobiledemo.BoltDB boltDB = Boltmobiledemo.NewBoltDB(path)iOS
- (void)demo {
NSString* path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES) objectAtIndex:0];
GoBoltmobiledemoBoltDB * demo = GoBoltmobiledemoNewBoltDB(path);
[self addSkipBackupAttributeToItemAtPath:demo.path];
//Some DB Logic would go here
[demo close];
}
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
{
NSURL* URL= [NSURL fileURLWithPath: filePathString];
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
}
return success;
}Resources
For more information on getting started with Bolt, check out the following articles:
- Intro to BoltDB: Painless Performant Persistence by Nate Finch.
- Bolt -- an embedded key/value database for Go by Progville
Comparison with other databases
Postgres, MySQL, & other relational databases
Relational databases structure data into rows and are only accessible through
the use of SQL. This approach provides flexibility in how you store and query
your data but also incurs overhead in parsing and planning SQL statements. Bolt
accesses all data by a byte slice key. This makes Bolt fast to read and write
data by key but provides no built-in support for joining values together.
Most relational databases (with the exception of SQLite) are standalone servers
that run separately from your application. This gives your systems
flexibility to connect multiple application servers to a single database
server but also adds overhead in serializing and transporting data over the
network. Bolt runs as a library included in your application so all data access
has to go through your application's process. This brings data closer to your
application but limits multi-process access to the data.
LevelDB, RocksDB
LevelDB and its derivatives (RocksDB, HyperLevelDB) are similar to Bolt in that
they are libraries bundled into the application, however, their underlying
structure is a log-structured merge-tree (LSM tree). An LSM tree optimizes
random writes by using a write ahead log and multi-tiered, sorted files called
SSTables. Bolt uses a B+tree internally and only a single file. Both approaches
have trade-offs.
If you require a high random write throughput (>10,000 w/sec) or you need to use
spinning disks then LevelDB could be a good choice. If your application is
read-heavy or does a lot of range scans then Bolt could be a good choice.
One other important consideration is that LevelDB does not have transactions.
It supports batch writing of key/values pairs and it supports read snapshots
but it will not give you the ability to do a compare-and-swap operation safely.
Bolt supports fully serializable ACID transactions.
LMDB
Bolt was originally a port of LMDB so it is architecturally similar. Both use
a B+tree, have ACID semantics with fully serializable transactions, and support
lock-free MVCC using a single writer and multiple readers.
The two projects have somewhat diverged. LMDB heavily focuses on raw performance
while Bolt has focused on simplicity and ease of use. For example, LMDB allows
several unsafe actions such as direct writes for the sake of performance. Bolt
opts to disallow actions which can leave the database in a corrupted state. The
only exception to this in Bolt is DB.NoSync.
There are also a few differences in API. LMDB requires a maximum mmap size when
opening an mdb_env whereas Bolt will handle incremental mmap resizing
automatically. LMDB overloads the getter and setter functions with multiple
flags whereas Bolt splits these specialized cases into their own functions.
Caveats & Limitations
It's important to pick the right tool for the job and Bolt is no exception.
Here are a few things to note when evaluating and using Bolt:
Bolt is good for read intensive workloads. Sequential write performance is
also fast but random writes can be slow. You can useDB.Batch()or add a
write-ahead log to help mitigate this issue.Bolt uses a B+tree internally so there can be a lot of random page access.
SSDs provide a significant performance boost over spinning disks.Try to avoid long running read transactions. Bolt uses copy-on-write so
old pages cannot be reclaimed while an old transaction is using them.Byte slices returned from Bolt are only valid during a transaction. Once the
transaction has been committed or rolled back then the memory they point to
can be reused by a new page or can be unmapped from virtual memory and you'll
see anunexpected fault addresspanic when accessing it.Bolt uses an exclusive write lock on the database file so it cannot be
shared by multiple processes.Be careful when using
Bucket.FillPercent. Setting a high fill percent for
buckets that have random inserts will cause your database to have very poor
page utilization.Use larger buckets in general. Smaller buckets causes poor page utilization
once they become larger than the page size (typically 4KB).Bulk loading a lot of random writes into a new bucket can be slow as the
page will not split until the transaction is committed. Randomly inserting
more than 100,000 key/value pairs into a single new bucket in a single
transaction is not advised.Bolt uses a memory-mapped file so the underlying operating system handles the
caching of the data. Typically, the OS will cache as much of the file as it
can in memory and will release memory as needed to other processes. This means
that Bolt can show very high memory usage when working with large databases.
However, this is expected and the OS will release memory as needed. Bolt can
handle databases much larger than the available physical RAM, provided its
memory-map fits in the process virtual address space. It may be problematic
on 32-bits systems.The data structures in the Bolt database are memory mapped so the data file
will be endian specific. This means that you cannot copy a Bolt file from a
little endian machine to a big endian machine and have it work. For most
users this is not a concern since most modern CPUs are little endian.Because of the way pages are laid out on disk, Bolt cannot truncate data files
and return free pages back to the disk. Instead, Bolt maintains a free list
of unused pages within its data file. These free pages can be reused by later
transactions. This works well for many use cases as databases generally tend
to grow. However, it's important to note that deleting large chunks of data
will not allow you to reclaim that space on disk.For more information on page allocation, see this comment.
Reading the Source
Bolt is a relatively small code base (<3KLOC) for an embedded, serializable,
transactional key/value database so it can be a good starting point for people
interested in how databases work.
The best places to start are the main entry points into Bolt:
Open()- Initializes the reference to the database. It's responsible for
creating the database if it doesn't exist, obtaining an exclusive lock on the
file, reading the meta pages, & memory-mapping the file.DB.Begin()- Starts a read-only or read-write transaction depending on the
value of thewritableargument. This requires briefly obtaining the "meta"
lock to keep track of open transactions. Only one read-write transaction can
exist at a time so the "rwlock" is acquired during the life of a read-write
transaction.Bucket.Put()- Writes a key/value pair into a bucket. After validating the
arguments, a cursor is used to traverse the B+tree to the page and position
where they key & value will be written. Once the position is found, the bucket
materializes the underlying page and the page's parent pages into memory as
"nodes". These nodes are where mutations occur during read-write transactions.
These changes get flushed to disk during commit.Bucket.Get()- Retrieves a key/value pair from a bucket. This uses a cursor
to move to the page & position of a key/value pair. During a read-only
transaction, the key and value data is returned as a direct reference to the
underlying mmap file so there's no allocation overhead. For read-write
transactions, this data may reference the mmap file or one of the in-memory
node values.Cursor- This object is simply for traversing the B+tree of on-disk pages
or in-memory nodes. It can seek to a specific key, move to the first or last
value, or it can move forward or backward. The cursor handles the movement up
and down the B+tree transparently to the end user.Tx.Commit()- Converts the in-memory dirty nodes and the list of free pages
into pages to be written to disk. Writing to disk then occurs in two phases.
First, the dirty pages are written to disk and anfsync()occurs. Second, a
new meta page with an incremented transaction ID is written and anotherfsync()occurs. This two phase write ensures that partially written data
pages are ignored in the event of a crash since the meta page pointing to them
is never written. Partially written meta pages are invalidated because they
are written with a checksum.
If you have additional notes that could be helpful for others, please submit
them via pull request.
Other Projects Using Bolt
Below is a list of public, open source projects that use Bolt:
- BoltDbWeb - A web based GUI for BoltDB files.
- Operation Go: A Routine Mission - An online programming game for Golang using Bolt for user accounts and a leaderboard.
- Bazil - A file system that lets your data reside where it is most convenient for it to reside.
- DVID - Added Bolt as optional storage engine and testing it against Basho-tuned leveldb.
- Skybox Analytics - A standalone funnel analysis tool for web analytics.
- Scuttlebutt - Uses Bolt to store and process all Twitter mentions of GitHub projects.
- Wiki - A tiny wiki using Goji, BoltDB and Blackfriday.
- ChainStore - Simple key-value interface to a variety of storage engines organized as a chain of operations.
- MetricBase - Single-binary version of Graphite.
- Gitchain - Decentralized, peer-to-peer Git repositories aka "Git meets Bitcoin".
- event-shuttle - A Unix system service to collect and reliably deliver messages to Kafka.
- ipxed - Web interface and api for ipxed.
- BoltStore - Session store using Bolt.
- photosite/session - Sessions for a photo viewing site.
- LedisDB - A high performance NoSQL, using Bolt as optional storage.
- ipLocator - A fast ip-geo-location-server using bolt with bloom filters.
- cayley - Cayley is an open-source graph database using Bolt as optional backend.
- bleve - A pure Go search engine similar to ElasticSearch that uses Bolt as the default storage backend.
- tentacool - REST api server to manage system stuff (IP, DNS, Gateway...) on a linux server.
- Seaweed File System - Highly scalable distributed key~file system with O(1) disk read.
- InfluxDB - Scalable datastore for metrics, events, and real-time analytics.
- Freehold - An open, secure, and lightweight platform for your files and data.
- Prometheus Annotation Server - Annotation server for PromDash & Prometheus service monitoring system.
- Consul - Consul is service discovery and configuration made easy. Distributed, highly available, and datacenter-aware.
- Kala - Kala is a modern job scheduler optimized to run on a single node. It is persistent, JSON over HTTP API, ISO 8601 duration notation, and dependent jobs.
- drive - drive is an unofficial Google Drive command line client for *NIX operating systems.
- stow - a persistence manager for objects
backed by boltdb. - buckets - a bolt wrapper streamlining
simple tx and key scans. - mbuckets - A Bolt wrapper that allows easy operations on multi level (nested) buckets.
- Request Baskets - A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to RequestBin service
- Go Report Card - Go code quality report cards as a (free and open source) service.
- Boltdb Boilerplate - Boilerplate wrapper around bolt aiming to make simple calls one-liners.
- lru - Easy to use Bolt-backed Least-Recently-Used (LRU) read-through cache with chainable remote stores.
- Storm - Simple and powerful ORM for BoltDB.
- GoWebApp - A basic MVC web application in Go using BoltDB.
- SimpleBolt - A simple way to use BoltDB. Deals mainly with strings.
- Algernon - A HTTP/2 web server with built-in support for Lua. Uses BoltDB as the default database backend.
- MuLiFS - Music Library Filesystem creates a filesystem to organise your music files.
- GoShort - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter.
- torrent - Full-featured BitTorrent client package and utilities in Go. BoltDB is a storage backend in development.
- gopherpit - A web service to manage Go remote import paths with custom domains
- bolter - Command-line app for viewing BoltDB file in your terminal.
- btcwallet - A bitcoin wallet.
- dcrwallet - A wallet for the Decred cryptocurrency.
- Ironsmith - A simple, script-driven continuous integration (build - > test -> release) tool, with no external dependencies
- BoltHold - An embeddable NoSQL store for Go types built on BoltDB
If you are using Bolt in a project please send a pull request to add it to the list.
Overview (`vendor/github.com/containerd/btrfs/README.md`)
go-btrfs
Native Go bindings for btrfs.
Status
These are in the early stages. We will try to maintain stability, but please
vendor if you are relying on these directly.
Contribute
This package may not cover all the use cases for btrfs. If something you need
is missing, please don't hesitate to submit a PR.
Note that due to struct alignment issues, this isn't yet fully native.
Preferrably, this could be resolved, so contributions in this direction are
greatly appreciated.
Overview (`vendor/github.com/containerd/cgroups/README.md`)
cgroups
Go package for creating, managing, inspecting, and destroying cgroups.
The resources format for settings on the cgroup uses the OCI runtime-spec found
here.
Examples
Create a new cgroup
This creates a new cgroup using a static path for all subsystems under /test.
- /sys/fs/cgroup/cpu/test
- /sys/fs/cgroup/memory/test
- etc....
It uses a single hierarchy and specifies cpu shares as a resource constraint and
uses the v1 implementation of cgroups.
shares := uint64(100)
control, err := cgroups.New(cgroups.V1, cgroups.StaticPath("/test"), &specs.LinuxResources{
CPU: &specs.CPU{
Shares: &shares,
},
})
defer control.Delete()Create with systemd slice support
control, err := cgroups.New(cgroups.Systemd, cgroups.Slice("system.slice", "runc-test"), &specs.LinuxResources{
CPU: &specs.CPU{
Shares: &shares,
},
})Load an existing cgroup
control, err = cgroups.Load(cgroups.V1, cgroups.StaticPath("/test"))Add a process to the cgroup
if err := control.Add(cgroups.Process{Pid:1234}); err != nil {
}Update the cgroup
To update the resources applied in the cgroup
shares = uint64(200)
if err := control.Update(&specs.LinuxResources{
CPU: &specs.CPU{
Shares: &shares,
},
}); err != nil {
}Freeze and Thaw the cgroup
if err := control.Freeze(); err != nil {
}
if err := control.Thaw(); err != nil {
}List all processes in the cgroup or recursively
processes, err := control.Processes(cgroups.Devices, recursive)Get Stats on the cgroup
stats, err := control.Stat()Move process across cgroups
This allows you to take processes from one cgroup and move them to another.
err := control.MoveTo(destination)Create subcgroup
subCgroup, err := control.New("child", resources)LICENSE
Copyright (c) 2016-2017 Michael Crosby. [email protected]
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Overview (`vendor/github.com/containerd/console/README.md`)
console
Golang package for dealing with consoles. Light on deps and a simple API.
Modifying the current process
current := console.Current()
defer current.Reset()
if err := current.SetRaw(); err != nil {
}
ws, err := current.Size()
current.Resize(ws)Overview (`vendor/github.com/containerd/continuity/README.md`)
continuity
A transport-agnostic, filesystem metadata manifest system
This project is a staging area for experiments in providing transport agnostic
metadata storage.
Please see https://github.com/opencontainers/specs/issues/11 for more details.
Building Proto Package
If you change the proto file you will need to rebuild the generated Go with go generate.
go generate ./protoOverview (`vendor/github.com/containerd/fifo/readme.md`)
fifo
Go package for handling fifos in a sane way.
// OpenFifo opens a fifo. Returns io.ReadWriteCloser.
// Context can be used to cancel this function until open(2) has not returned.
// Accepted flags:
// - syscall.O_CREAT - create new fifo if one doesn't exist
// - syscall.O_RDONLY - open fifo only from reader side
// - syscall.O_WRONLY - open fifo only from writer side
// - syscall.O_RDWR - open fifo from both sides, never block on syscall level
// - syscall.O_NONBLOCK - return io.ReadWriteCloser even if other side of the
// fifo isn't open. read/write will be connected after the actual fifo is
// open or after fifo is closed.
func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error)
// Read from a fifo to a byte array.
func (f *fifo) Read(b []byte) (int, error)
// Write from byte array to a fifo.
func (f *fifo) Write(b []byte) (int, error)
// Close the fifo. Next reads/writes will error. This method can also be used
// before open(2) has returned and fifo was never opened.
func (f *fifo) Close() errorOverview (`vendor/github.com/containerd/go-runc/README.md`)
go-runc
This is a package for consuming the runc binary in your Go applications.
It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource!
This needs runc @ a9610f2c0
or greater.
Docs
Docs can be found at godoc.org.
Overview (`vendor/github.com/coreos/go-systemd/README.md`)
go-systemd
Go bindings to systemd. The project has several packages:
activation- for writing and using socket activation from Godbus- for starting/stopping/inspecting running services and unitsjournal- for writing to systemd's logging service, journaldsdjournal- for reading from journald by wrapping its C APImachine1- for registering machines/containers with systemdunit- for (de)serialization and comparison of unit files
Socket Activation
An example HTTP server using socket activation can be quickly set up by following this README on a Linux machine running systemd:
https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
Journal
Using the pure-Go journal package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
The sdjournal package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
D-Bus
The dbus package connects to the systemd D-Bus API and lets you start, stop and introspect systemd units. The API docs are here:
http://godoc.org/github.com/coreos/go-systemd/dbus
Debugging
Create /etc/dbus-1/system-local.conf that looks like this:
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow eavesdrop="true"/>
<allow eavesdrop="true" send_destination="*"/>
</policy>
</busconfig>machined
The machine1 package allows interaction with the systemd machined D-Bus API.
Units
The unit package provides various functions for working with systemd unit files.
Overview (`vendor/github.com/davecgh/go-spew/README.md`)
go-spew
[]
(https://travis-ci.org/davecgh/go-spew) ![ISC License]
(http://img.shields.io/badge/license-ISC-blue.svg) [![Coverage Status]
(https://img.shields.io/coveralls/davecgh/go-spew.svg)]
(https://coveralls.io/r/davecgh/go-spew?branch=master)
Go-spew implements a deep pretty printer for Go data structures to aid in
debugging. A comprehensive suite of tests with 100% test coverage is provided
to ensure proper functionality. See test_coverage.txt for the gocov coverage
report. Go-spew is licensed under the liberal ISC license, so it may be used in
open source or commercial projects.
If you're interested in reading about how this package came to life and some
of the challenges involved in providing a deep pretty printer, there is a blog
post about it
here.
Documentation
[]
(http://godoc.org/github.com/davecgh/go-spew/spew)
Full go doc style documentation for the project can be viewed online without
installing this package by using the excellent GoDoc site here:
http://godoc.org/github.com/davecgh/go-spew/spew
You can also view the documentation locally once the package is installed with
the godoc tool by running godoc -http=":6060" and pointing your browser to
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
Installation
$ go get -u github.com/davecgh/go-spew/spewQuick Start
Add this import line to the file you're working in:
import "github.com/davecgh/go-spew/spew"To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)Debugging a Web Application Example
Here is an example of how you can use spew.Sdump() to help debug a web application. Please be sure to wrap your output using the html.EscapeString() function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
package main
import (
"fmt"
"html"
"net/http"
"github.com/davecgh/go-spew/spew"
)
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
fmt.Fprintf(w, "")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}Sample Dump Output
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) {
(string) "one": (bool) true
}
}
([]uint8) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available via the
spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
/* Detailed source-code truncated for AI context efficiency. */Unsafe Package Dependency
This package relies on the unsafe package to perform some of the more advanced
features, however it also supports a "limited" mode which allows it to work in
environments where the unsafe package is not available. By default, it will
operate in this mode on Google App Engine and when compiled with GopherJS. The
"safe" build tag may also be specified to force the package to build without
using the unsafe package.
License
Go-spew is licensed under the copyfree ISC License.
Overview (`vendor/github.com/docker/go-metrics/README.md`)
go-metrics [](https://godoc.org/github.com/docker/go-metrics)
This package is small wrapper around the prometheus go client to help enforce convention and best practices for metrics collection in Docker projects.
Best Practices
This packages is meant to be used for collecting metrics in Docker projects.
It is not meant to be used as a replacement for the prometheus client but to help enforce consistent naming across metrics collected.
If you have not already read the prometheus best practices around naming and labels you can read the page here.
The following are a few Docker specific rules that will help you name and work with metrics in your project.
- Namespace and Subsystem
This package provides you with a namespace type that allows you to specify the same namespace and subsystem for your metrics.
ns := metrics.NewNamespace("engine", "daemon", metrics.Labels{
"version": dockerversion.Version,
"commit": dockerversion.GitCommit,
})In the example above we are creating metrics for the Docker engine's daemon package.engine would be the namespace in this example where daemon is the subsystem or package where we are collecting the metrics.
A namespace also allows you to attach constant labels to the metrics such as the git commit and version that it is collecting.
- Declaring your Metrics
Try to keep all your metric declarations in one file.
This makes it easy for others to see what constant labels are defined on the namespace and what labels are defined on the metrics when they are created.
- Use labels instead of multiple metrics
Labels allow you to define one metric such as the time it takes to perform a certain action on an object.
If we wanted to collect timings on various container actions such as create, start, and delete then we can define one metric called container_actions and use labels to specify the type of action.
containerActions = ns.NewLabeledTimer("container_actions", "The number of milliseconds it takes to process each container action", "action")The last parameter is the label name or key.
When adding a data point to the metric you will use the WithValues function to specify the action that you are collecting for.
containerActions.WithValues("create").UpdateSince(start)- Always use a unit
The metric name should describe what you are measuring but you also need to provide the unit that it is being measured with.
For a timer, the standard unit is seconds and a counter's standard unit is a total.
For gauges you must provide the unit.
This package provides a standard set of units for use within the Docker projects.
Nanoseconds Unit = "nanoseconds"
Seconds Unit = "seconds"
Bytes Unit = "bytes"
Total Unit = "total"If you need to use a unit but it is not defined in the package please open a PR to add it but first try to see if one of the already created units will work for your metric, i.e. seconds or nanoseconds vs adding milliseconds.
Docs
Package documentation can be found here.
Additional Metrics
Additional metrics are also defined here that are not avaliable in the prometheus client.
If you need a custom metrics and it is generic enough to be used by multiple projects, define it here.
Copyright and license
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
Overview (`vendor/github.com/docker/go-units/README.md`)
Introduction
go-units is a library to transform human friendly measurements into machine friendly values.
Usage
See the docs in godoc for examples and documentation.
Copyright and license
Copyright © 2015 Docker, Inc.
go-units is licensed under the Apache License, Version 2.0.
See LICENSE for the full text of the license.
Overview (`vendor/github.com/gogo/protobuf/Readme.md`)
Protocol Buffers for Go with Gadgets
gogoprotobuf is a fork of golang/protobuf with extra code generation features.
This code generation is used to achieve:
- fast marshalling and unmarshalling
- more canonical Go structures
- goprotobuf compatibility
- less typing by optionally generating extra helper code
- peace of mind by optionally generating test and benchmark code
- other serialization formats
Keeping track of how up to date gogoprotobuf is relative to golang/protobuf is done in this
issue
Users
These projects use gogoprotobuf:
- etcd - blog - sample proto file
- spacemonkey - blog
- badoo - sample proto file
- mesos-go - sample proto file
- heka - the switch from golang/protobuf to gogo/protobuf when it was still on code.google.com
- cockroachdb - sample proto file
- go-ipfs - sample proto file
- rkive-go - sample proto file
- dropbox
- srclib - sample proto file
- adyoulike
- cloudfoundry - sample proto file
- kubernetes - go2idl built on top of gogoprotobuf
- dgraph - release notes - benchmarks
- centrifugo - release notes - blog
- docker swarmkit - sample proto file
- nats.io - go-nats-streaming
- tidb - Communication between tidb and tikv
- protoactor-go - vanity command that also generates actors from service definitions
Please lets us know if you are using gogoprotobuf by posting on our GoogleGroup.
Mentioned
- Cloudflare - go serialization talk - Albert Strasheim
- gophercon
- alecthomas' go serialization benchmarks
Getting Started
There are several ways to use gogoprotobuf, but for all you need to install go and protoc.
After that you can choose:
- Speed
- More Speed and more generated code
- Most Speed and most customization
Installation
To install it, you must first have Go (at least version 1.3.3) installed (see http://golang.org/doc/install). Go 1.7.1 is continuously tested.
Next, install the standard protocol buffer implementation from https://github.com/google/protobuf.
Most versions from 2.3.1 should not give any problems, but 2.6.1, 3.0.2 and 3.1.0 are continuously tested.
Speed
Install the protoc-gen-gofast binary
go get github.com/gogo/protobuf/protoc-gen-gofastUse it to generate faster marshaling and unmarshaling go code for your protocol buffers.
protoc --gofast_out=. myproto.protoThis does not allow you to use any of the other gogoprotobuf extensions.
More Speed and more generated code
Fields without pointers cause less time in the garbage collector.
More code generation results in more convenient methods.
Other binaries are also included:
protoc-gen-gogofast (same as gofast, but imports gogoprotobuf)
protoc-gen-gogofaster (same as gogofast, without XXX_unrecognized, less pointer fields)
protoc-gen-gogoslick (same as gogofaster, but with generated string, gostring and equal methods)Installing any of these binaries is easy. Simply run:
go get github.com/gogo/protobuf/proto
go get github.com/gogo/protobuf/{binary}
go get github.com/gogo/protobuf/gogoprotoThese binaries allow you to using gogoprotobuf extensions.
Most Speed and most customization
Customizing the fields of the messages to be the fields that you actually want to use removes the need to copy between the structs you use and structs you use to serialize.
gogoprotobuf also offers more serialization formats and generation of tests and even more methods.
Please visit the extensions page for more documentation.
Install protoc-gen-gogo:
go get github.com/gogo/protobuf/proto
go get github.com/gogo/protobuf/jsonpb
go get github.com/gogo/protobuf/protoc-gen-gogo
go get github.com/gogo/protobuf/gogoprotoGRPC
It works the same as golang/protobuf, simply specify the plugin.
Here is an example using gofast:
protoc --gofast_out=plugins=grpc:. my.protoOverview (`vendor/github.com/golang/protobuf/README.md`)
Go support for Protocol Buffers
Google's data interchange format.
Copyright 2010 The Go Authors.
https://github.com/golang/protobuf
This package and the code it generates requires at least Go 1.4.
This software implements Go bindings for protocol buffers. For
information about protocol buffers themselves, see
https://developers.google.com/protocol-buffers/
Installation
To use this software, you must:
- Install the standard C++ implementation of protocol buffers from
https://developers.google.com/protocol-buffers/ - Of course, install the Go compiler and tools from
https://golang.org/
See
https://golang.org/doc/install
for details or, if you are using gccgo, follow the instructions at
https://golang.org/doc/install/gccgo - Grab the code from the repository and install the proto package.
The simplest way is to rungo get -u github.com/golang/protobuf/{proto,protoc-gen-go}.
The compiler plugin, protoc-gen-go, will be installed in $GOBIN,
defaulting to $GOPATH/bin. It must be in your $PATH for the protocol
compiler, protoc, to find it.
This software has two parts: a 'protocol compiler plugin' that
generates Go source files that, once compiled, can access and manage
protocol buffers; and a library that implements run-time support for
encoding (marshaling), decoding (unmarshaling), and accessing protocol
buffers.
There is support for gRPC in Go using protocol buffers.
See the note at the bottom of this file for details.
There are no insertion points in the plugin.
Using protocol buffers with Go
Once the software is installed, there are two steps to using it.
First you must compile the protocol buffer definitions and then import
them, with the support library, into your program.
To compile the protocol buffer definition, run protoc with the --go_out
parameter set to the directory you want to output the Go code to.
protoc --go_out=. *.protoThe generated files will be suffixed .pb.go. See the Test code below
for an example using such a file.
The package comment for the proto library contains text describing
the interface provided in Go for protocol buffers. Here is an edited
version.
==========
The proto package converts data structures to and from the
wire format of protocol buffers. It works in concert with the
Go source code generated for .proto files by the protocol compiler.
A summary of the properties of the protocol buffer interface
for a protocol buffer variable v:
- Names are turned from camel_case to CamelCase for export.
- There are no methods on v to set fields; just treat
them as structure fields. - There are getters that return a field's value if set,
and return the field's default value if unset.
The getters work even if the receiver is a nil message. - The zero value for a struct is its correct initialization state.
All desired fields must be set before marshaling. - A Reset() method will restore a protobuf struct to its zero state.
- Non-repeated fields are pointers to the values; nil means unset.
That is, optional or required field int32 f becomes F *int32. - Repeated fields are slices.
- Helper functions are available to aid the setting of fields.
Helpers for getting values are superseded by the
GetFoo methods and their use is deprecated.
msg.Foo = proto.String("hello") // set field - Constants are defined to hold the default values of all fields that
have them. They have the form Default_StructName_FieldName.
Because the getter methods handle defaulted values,
direct use of these constants should be rare. - Enums are given type names and maps from names to values.
Enum values are prefixed with the enum's type name. Enum types have
a String method, and a Enum method to assist in message construction. - Nested groups and enums have type names prefixed with the name of
the surrounding message type. - Extensions are given descriptor names that start with E_,
followed by an underscore-delimited list of the nested messages
that contain it (if any) followed by the CamelCased name of the
extension field itself. HasExtension, ClearExtension, GetExtension
and SetExtension are functions for manipulating extensions. - Oneof field sets are given a single field in their message,
with distinguished wrapper types for each possible field value. - Marshal and Unmarshal are functions to encode and decode the wire format.
When the .proto file specifies syntax="proto3", there are some differences:
- Non-repeated fields of non-message type are values instead of pointers.
- Getters are only generated for message and oneof fields.
- Enum types do not get an Enum method.
Consider file test.proto, containing
package example;
enum FOO { X = 17; };
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredField = 5;
}
}To create and play with a Test object from the example package,
package main
import (
"log"
"github.com/golang/protobuf/proto"
"path/to/example"
)
func main() {
test := &example.Test {
Label: proto.String("hello"),
Type: proto.Int32(17),
Reps: []int64{1, 2, 3},
Optionalgroup: &example.Test_OptionalGroup {
RequiredField: proto.String("good bye"),
},
}
data, err := proto.Marshal(test)
if err != nil {
log.Fatal("marshaling error: ", err)
}
newTest := &example.Test{}
err = proto.Unmarshal(data, newTest)
if err != nil {
log.Fatal("unmarshaling error: ", err)
}
// Now test and newTest contain the same data.
if test.GetLabel() != newTest.GetLabel() {
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
}
// etc.
}Parameters
To pass extra parameters to the plugin, use a comma-separated
parameter list separated from the output directory by a colon:
protoc --go_out=plugins=grpc,import_path=mypackage:. *.protoimport_prefix=xxx- a prefix that is added onto the beginning of
all imports. Useful for things like generating protos in a
subdirectory, or regenerating vendored protobufs in-place.import_path=foo/bar- used as the package if no input files
declarego_package. If it contains slashes, everything up to the
rightmost slash is ignored.plugins=plugin1+plugin2- specifies the list of sub-plugins to
load. The only plugin in this repo isgrpc.Mfoo/bar.proto=quux/shme- declares that foo/bar.proto is
associated with Go package quux/shme. This is subject to the
import_prefix parameter.
gRPC Support
If a proto file specifies RPC services, protoc-gen-go can be instructed to
generate code compatible with gRPC (http://www.grpc.io/). To do this, pass
the plugins parameter to protoc-gen-go; the usual way is to insert it into
the --go_out argument to protoc:
protoc --go_out=plugins=grpc:. *.protoCompatibility
The library and the generated code are expected to be stable over time.
However, we reserve the right to make breaking changes without notice for the
following reasons:
- Security. A security issue in the specification or implementation may come to
light whose resolution requires breaking compatibility. We reserve the right
to address such security issues. - Unspecified behavior. There are some aspects of the Protocol Buffers
specification that are undefined. Programs that depend on such unspecified
behavior may break in future releases. - Specification errors or changes. If it becomes necessary to address an
inconsistency, incompleteness, or change in the Protocol Buffers
specification, resolving the issue could affect the meaning or legality of
existing programs. We reserve the right to address such issues, including
updating the implementations. - Bugs. If the library has a bug that violates the specification, a program
that depends on the buggy behavior may break if the bug is fixed. We reserve
the right to fix such bugs. - Adding methods or fields to generated structs. These may conflict with field
names that already exist in a schema, causing applications to break. When the
code generator encounters a field in the schema that would collide with a
generated field or method name, the code generator will append an underscore
to the generated field or method name. - Adding, removing, or changing methods or fields in generated structs that
start withXXX. These parts of the generated code are exported out of
necessity, but should not be considered part of the public API. - Adding, removing, or changing unexported symbols in generated code.
Any breaking changes outside of these will be announced 6 months in advance to
[email protected].
You should, whenever possible, use generated code created by the protoc-gen-go
tool built at the same commit as the proto package. The proto package
declares package-level constants in the form ProtoPackageIsVersionX.
Application code and generated code may depend on one of these constants to
ensure that compilation will fail if the available version of the proto library
is too old. Whenever we make a change to the generated code that requires newer
library support, in the same commit we will increment the version number of the
generated code and declare a new package-level constant whose name incorporates
the latest version number. Removing a compatibility constant is considered a
breaking change and would be subject to the announcement policy stated above.
The protoc-gen-go/generator package exposes a plugin interface,
which is used by the gRPC code generation. This interface is not
supported and is subject to incompatible changes without notice.
Overview (`vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md`)
Go gRPC Interceptors for Prometheus monitoring
Prometheus monitoring for your gRPC Go servers and clients.
A sister implementation for gRPC Java (same metrics, same semantics) is in grpc-ecosystem/java-grpc-prometheus.
Interceptors
gRPC Go recently acquired support for Interceptors, i.e. middleware that is executed
by a gRPC Server before the request is passed onto the user's application logic. It is a perfect way to implement
common patterns: auth, logging and... monitoring.
To use Interceptors in chains, please see go-grpc-middleware.
Usage
There are two types of interceptors: client-side and server-side. This package provides monitoring Interceptors for both.
Server-side
import "github.com/grpc-ecosystem/go-grpc-prometheus"
...
// Initialize your gRPC server's interceptor.
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
)
// Register your gRPC service implementations.
myservice.RegisterMyServiceServer(s.server, &myServiceImpl{})
// After all your registrations, make sure all of the Prometheus metrics are initialized.
grpc_prometheus.Register(myServer)
// Register Prometheus metrics handler.
http.Handle("/metrics", prometheus.Handler())
...Client-side
import "github.com/grpc-ecosystem/go-grpc-prometheus"
...
clientConn, err = grpc.Dial(
address,
grpc.WithUnaryInterceptor(UnaryClientInterceptor),
grpc.WithStreamInterceptor(StreamClientInterceptor)
)
client = pb_testproto.NewTestServiceClient(clientConn)
resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"})
...Metrics
Labels
All server-side metrics start with grpc_server as Prometheus subsystem name. All client-side metrics start with grpc_client. Both of them have mirror-concepts. Similarly all methods
contain the same rich labels:
grpc_service- the gRPC service name, which is the combination of protobufpackageand
thegrpc_servicesection name. E.g. forpackage = mwitkow.testprotoand
service TestServicethe label will begrpc_service="mwitkow.testproto.TestService"grpc_method- the name of the method called on the gRPC service. E.g.grpc_method="Ping"grpc_type- the gRPC type of request.
Differentiating between the two is important especially for latency measurements.unaryis single request, single response RPCclient_streamis a multi-request, single response RPCserver_streamis a single request, multi-response RPCbidi_streamis a multi-request, multi-response RPC
Additionally for completed RPCs, the following labels are used:
grpc_code- the human-readable gRPC status code.
The list of all statuses is to long, but here are some common ones:OK- means the RPC was successfulIllegalArgument- RPC contained bad valuesInternal- server-side error not disclosed to the clients
Counters
The counters and their up to date documentation is in server_reporter.go and client_reporter.go
the respective Prometheus handler (usually /metrics).
For the purpose of this documentation we will only discuss grpc_server metrics. The grpc_client ones contain mirror concepts.
For simplicity, let's assume we're tracking a single server-side RPC call of mwitkow.testproto.TestService,
calling the method PingList. The call succeeds and returns 20 messages in the stream.
First, immediately after the server receives the call it will increment thegrpc_server_started_total and start the handling time clock (if histograms are enabled).
grpc_server_started_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1Then the user logic gets invoked. It receives one message from the client containing the request
(it's a server_stream):
grpc_server_msg_received_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1The user logic may return an error, or send multiple messages back to the client. In this case, on
each of the 20 messages sent back, a counter will be incremented:
grpc_server_msg_sent_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 20After the call completes, it's status (OK or other gRPC status code)
and the relevant call labels increment the grpc_server_handled_total counter.
grpc_server_handled_total{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1Histograms
Prometheus histograms are a great way
to measure latency distributions of your RPCs. However since it is bad practice to have metrics
of high cardinality)
the latency monitoring metrics are disabled by default. To enable them please call the following
in your server initialization code:
grpc_prometheus.EnableHandlingTimeHistogram()After the call completes, it's handling time will be recorded in a Prometheus histogram
variable grpc_server_handling_seconds. It contains three sub-metrics:
grpc_server_handling_seconds_count- the count of all completed RPCs by status and methodgrpc_server_handling_seconds_sum- cumulative time of RPCs by status and method, useful for
calculating average handling timesgrpc_server_handling_seconds_bucket- contains the counts of RPCs by status and method in respective
handling-time buckets. These buckets can be used by Prometheus to estimate SLAs (see here)
The counter values will look as follows:
/* Detailed source-code truncated for AI context efficiency. */Useful query examples
Prometheus philosophy is to provide the most detailed metrics possible to the monitoring system, and
let the aggregations be handled there. The verbosity of above metrics make it possible to have that
flexibility. Here's a couple of useful monitoring queries:
request inbound rate
sum(rate(grpc_server_started_total{job="foo"}[1m])) by (grpc_service)For job="foo" (common label to differentiate between Prometheus monitoring targets), calculate the
rate of requests per second (1 minute window) for each gRPC grpc_service that the job has. Please note
how the grpc_method is being omitted here: all methods of a given gRPC service will be summed together.
unary request error rate
sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service)For job="foo", calculate the per-grpc_service rate of unary (1:1) RPCs that failed, i.e. the
ones that didn't finish with OK code.
unary request error percentage
sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service)
/
sum(rate(grpc_server_started_total{job="foo",grpc_type="unary"}[1m])) by (grpc_service)
* 100.0For job="foo", calculate the percentage of failed requests by service. It's easy to notice that
this is a combination of the two above examples. This is an example of a query you would like to
alert on in your system for SLA violations, e.g.
"no more than 1% requests should fail".
average response stream size
sum(rate(grpc_server_msg_sent_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service)
/
sum(rate(grpc_server_started_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service)For job="foo" what is the grpc_service-wide 10m average of messages returned for all server_stream RPCs. This allows you to track the stream sizes returned by your system, e.g. allows
you to track when clients started to send "wide" queries that ret
Note the divisor is the number of started RPCs, in order to account for in-flight requests.
99%-tile latency of unary requests
histogram_quantile(0.99,
sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary"}[5m])) by (grpc_service,le)
)For job="foo", returns an 99%-tile quantile estimation
of the handling time of RPCs per service. Please note the 5m rate, this means that the quantile
estimation will take samples in a rolling 5m window. When combined with other quantiles
(e.g. 50%, 90%), this query gives you tremendous insight into the responsiveness of your system
(e.g. impact of caching).
percentage of slow unary queries (>250ms)
100.0 - (
sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary",le="0.25"}[5m])) by (grpc_service)
/
sum(rate(grpc_server_handling_seconds_count{job="foo",grpc_type="unary"}[5m])) by (grpc_service)
) * 100.0For job="foo" calculate the by-grpc_service fraction of slow requests that took longer than 0.25
seconds. This query is relatively complex, since the Prometheus aggregations use le (less or equal)
buckets, meaning that counting "fast" requests fractions is easier. However, simple maths helps.
This is an example of a query you would like to alert on in your system for SLA violations,
e.g. "less than 1% of requests are slower than 250ms".
Status
This code has been used since August 2015 as the basis for monitoring of production gRPC micro services at Improbable.
License
go-grpc-prometheus is released under the Apache 2.0 license. See the LICENSE file for details.
Overview (`vendor/github.com/matttproud/golang_protobuf_extensions/README.md`)
Overview
This repository provides various Protocol Buffer extensions for the Go
language (golang), namely support for record length-delimited message
streaming.
| Java | Go |
|---|---|
| MessageLite#parseDelimitedFrom | pbutil.ReadDelimited |
| MessageLite#writeDelimitedTo | pbutil.WriteDelimited |
Because Code Review 9102043 is
destined to never be merged into mainline (i.e., never be promoted to formal
goprotobuf features), this repository
will live here in the wild.
Documentation
We have generated Go Doc documentation here.
Testing
Overview (`vendor/github.com/nightlyone/lockfile/README.md`)
lockfile
Handle locking via pid files.
install
Install Go 1, either from source or with a prepackaged binary.
For Windows suport, Go 1.4 or newer is required.
Then run
go get github.com/nightlyone/lockfileLICENSE
BSD
documentation
package documentation at godoc.org
install
go get github.com/nightlyone/lockfilecontributing
Contributions are welcome. Please open an issue or send me a pull request for a dedicated branch.
Make sure the git commit hooks show it works.
git commit hooks
enable commit hooks via
cd .git ; rm -rf hooks; ln -s ../git-hooks hooks ; cd ..Overview (`vendor/github.com/opencontainers/go-digest/README.md`)
go-digest
Common digest package used across the container ecosystem.
Please see the godoc for more information.
What is a digest?
A digest is just a hash.
The most common use case for a digest is to create a content
identifier for use in Content Addressable Storage
systems:
id := digest.FromBytes([]byte("my content"))In the example above, the id can be used to uniquely identify
the byte slice "my content". This allows two disparate applications
to agree on a verifiable identifier without having to trust one
another.
An identifying digest can be verified, as follows:
if id != digest.FromBytes([]byte("my content")) {
return errors.New("the content has changed!")
}A Verifier type can be used to handle cases where an io.Reader
makes more sense:
rd := getContent()
verifier := id.Verifier()
io.Copy(verifier, rd)
if !verifier.Verified() {
return errors.New("the content has changed!")
}Using Merkle DAGs, this
can power a rich, safe, content distribution system.
Usage
While the godoc is
considered the best resource, a few important items need to be called
out when using this package.
Make sure to import the hash implementations into your application
or the package will panic. You should have something like the
following in the main (or other entrypoint) of your application:goimport ( _ "crypto/sha256" _ "crypto/sha512" )This may seem inconvenient but it allows you replace the hash
implementations with others, such as https://github.com/stevvooe/resumable.Even though
digest.Digestmay be assemable as a string, always
verify your input withdigest.Parseor useDigest.Validate
when accepting untrusted input. While there are measures to
avoid common problems, this will ensure you have valid digests
in the rest of your application.
Stability
The Go API, at this stage, is considered stable, unless otherwise noted.
As always, before using a package export, read the godoc.
Contributing
This package is considered fairly complete. It has been in production
in thousands (millions?) of deployments and is fairly battle-hardened.
New additions will be met with skepticism. If you think there is a
missing feature, please file a bug clearly describing the problem and
the alternatives you tried before submitting a PR.
Reporting security issues
The maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
Please DO NOT file a public issue, instead send your report privately
to [email protected].
Security reports are greatly appreciated and we will publicly thank you
for it. We also like to send gifts—if you're into Docker schwag, make
sure to let us know. We currently do not offer a paid security bounty
program, but are not ruling it out in the future.
Copyright and license
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. This README.md file and the CONTRIBUTING.md file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file LICENSE.docs. You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/.
Overview (`vendor/github.com/opencontainers/image-spec/README.md`)
OCI Image Format Specification
The OCI Image Format project creates and maintains the software shipping container image format spec (OCI Image Format).
The specification can be found here.
This repository also provides Go types, intra-blob validation tooling, and JSON Schema.
The Go types and validation should be compatible with the current Go release; earlier Go releases are not supported.
Additional documentation about how this group operates:
The optional and base layers of all OCI projects are tracked in the OCI Scope Table.
Running an OCI Image
The OCI Image Format partner project is the OCI Runtime Spec project.
The Runtime Specification outlines how to run a "filesystem bundle" that is unpacked on disk.
At a high-level an OCI implementation would download an OCI Image then unpack that image into an OCI Runtime filesystem bundle.
At this point the OCI Runtime Bundle would be run by an OCI Runtime.
This entire workflow supports the UX that users have come to expect from container engines like Docker and rkt: primarily, the ability to run an image with no additional arguments:
- docker run example.com/org/app:v1.0.0
- rkt run example.com/org/app,version=v1.0.0
To support this UX the OCI Image Format contains sufficient information to launch the application on the target platform (e.g. command, arguments, environment variables, etc).
FAQ
Q: Why doesn't this project mention distribution?
A: Distribution, for example using HTTP as both Docker v2.2 and AppC do today, is currently out of scope on the OCI Scope Table.
There has been some discussion on the TOB mailing list to make distribution an optional layer, but this topic is a work in progress.
Q: Why a new project?
A: The first OCI spec centered around defining the run side of a container.
This is generally seen to be an orthogonal concern to the shipping container component.
As practical examples of this separation you see many organizations separating these concerns into different teams and organizations: the Docker Distribution project and the Docker containerd project; Amazon ECS and Amazon EC2 Container Registry, etc.
Q: Why work on this?
A: We are seeing many independent implementations of container image handling including build systems, registries, and image analysis tools.
As an organization we would like to encourage this growth and bring people together to ensure a technically correct and open specification continues to evolve reflecting the OCI values.
Q: What happens to AppC or Docker Image Formats?
A: Existing formats can continue to be a proving ground for technologies, as needed.
The OCI Image Format project strives to provide a dependable open specification that can be shared between different tools and be evolved for years or decades of compatibility; as the deb and rpm format have.
Roadmap
The GitHub milestones lay out the path to the OCI v1.0.0 release in late 2016.
Contributing
Development happens on GitHub for the spec.
Issues are used for bugs and actionable items and longer discussions can happen on the mailing list.
The specification and code is licensed under the Apache 2.0 license found in the LICENSE file of this repository.
Discuss your design
The project welcomes submissions, but please let everyone know what you are working on.
Before undertaking a nontrivial change to this specification, send mail to the mailing list to discuss what you plan to do.
This gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits.
It also guarantees that the design is sound before code is written; a GitHub pull-request is not the place for high-level discussions.
Typos and grammatical errors can go straight to a pull-request.
When in doubt, start on the mailing-list.
Weekly Call
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 2:00 PM (USA Pacific).
Everyone is welcome to participate via UberConference web or audio-only: +1-415-968-0849 (no PIN needed).
An initial agenda will be posted to the mailing list earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
Minutes are posted to the mailing list and minutes from past calls are archived to the wiki for those who are unable to join the call.
Mailing List
You can subscribe and join the mailing list on Google Groups.
IRC
OCI discussion happens on #opencontainers on Freenode (logs).
Markdown style
To keep consistency throughout the Markdown files in the Open Container spec all files should be formatted one sentence per line.
This fixes two things: it makes diffing easier with git and it resolves fights about line wrapping length.
For example, this paragraph will span three lines in the Markdown source.
Git commit
Sign your work
The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch.
The rules are pretty simple: if you can certify the below (from developercertificate.org):
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.then you just add a line to every git commit message:
Signed-off-by: Joe Smith <[email protected]>using your real name (sorry, no pseudonyms or anonymous contributions.)
You can add the sign off when creating the git commit via git commit -s.
Commit Style
Simple house-keeping for clean git history.
Read more on How to Write a Git Commit Message or the Discussion section of git-commit(1).
- Separate the subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
- If there was important/useful/essential conversation or information, copy or include a reference
- When possible, one keyword to scope the change in the subject (i.e. "README: ...", "runtime: ...")
Overview (`vendor/github.com/opencontainers/runc/README.md`)
runc
Introduction
runc is a CLI tool for spawning and running containers according to the OCI specification.
Releases
runc depends on and tracks the runtime-spec repository.
We will try to make sure that runc and the OCI specification major versions stay in lockstep.
This means that runc 1.0.0 should implement the 1.0 version of the specification.
You can find official releases of runc on the release page.
Security
If you wish to report a security issue, please disclose the issue responsibly
to [email protected].
Building
runc currently supports the Linux platform with various architecture support.
It must be built with Go version 1.6 or higher in order for some features to function properly.
In order to enable seccomp support you will need to install libseccomp on your platform.
e.g.
libseccomp-develfor CentOS, orlibseccomp-devfor Ubuntu
Otherwise, if you do not want to build runc with seccomp support you can add BUILDTAGS="" when running make.
# create a 'github.com/opencontainers' in your GOPATH/src
cd github.com/opencontainers
git clone https://github.com/opencontainers/runc
cd runc
make
sudo make installrunc will be installed to /usr/local/sbin/runc on your system.
Build Tags
runc supports optional build tags for compiling support of various features.
To add build tags to the make option the BUILDTAGS variable must be set.
make BUILDTAGS='seccomp apparmor'| Build Tag | Feature | Dependency |
|---|---|---|
| seccomp | Syscall filtering | libseccomp |
| selinux | selinux process and mount labeling | |
| apparmor | apparmor profile support | libapparmor |
| ambient | ambient capability support | kernel 4.3 |
Running the test suite
runc currently supports running its test suite via Docker.
To run the suite just type make test.
make testThere are additional make targets for running the tests outside of a container but this is not recommended as the tests are written with the expectation that they can write and remove anywhere.
You can run a specific test case by setting the TESTFLAGS variable.
# make test TESTFLAGS="-run=SomeTestFunction"Dependencies Management
runc uses vndr for dependencies management.
Please refer to vndr for how to add or update
new dependencies.
Using runc
Creating an OCI Bundle
In order to use runc you must have your container in the format of an OCI bundle.
If you have Docker installed you can use its export method to acquire a root filesystem from an existing Docker container.
# create the top most bundle directory
mkdir /mycontainer
cd /mycontainer
# create the rootfs directory
mkdir rootfs
# export busybox via Docker into the rootfs directory
docker export $(docker create busybox) | tar -C rootfs -xvf -After a root filesystem is populated you just generate a spec in the format of a config.json file inside your bundle.runc provides a spec command to generate a base template spec that you are then able to edit.
To find features and documentation for fields in the spec please refer to the specs repository.
runc specRunning Containers
Assuming you have an OCI bundle from the previous step you can execute the container in two different ways.
The first way is to use the convenience command run that will handle creating, starting, and deleting the container after it exits.
# run as root
cd /mycontainer
runc run mycontaineridIf you used the unmodified runc spec template this should give you a sh session inside the container.
The second way to start a container is using the specs lifecycle operations.
This gives you more power over how the container is created and managed while it is running.
This will also launch the container in the background so you will have to edit the config.json to remove the terminal setting for the simple examples here.
Your process field in the config.json should look like this below with "terminal": false and "args": ["sleep", "5"].
"process": {
"terminal": false,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sleep", "5"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/",
"capabilities": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"rlimits": [
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
],
"noNewPrivileges": true
},Now we can go though the lifecycle operations in your shell.
# run as root
cd /mycontainer
runc create mycontainerid
# view the container is created and in the "created" state
runc list
# start the process inside the container
runc start mycontainerid
# after 5 seconds view that the container has exited and is now in the stopped state
runc list
# now delete the container
runc delete mycontaineridThis adds more complexity but allows higher level systems to manage runc and provides points in the containers creation to setup various settings after the container has created and/or before it is deleted.
This is commonly used to setup the container's network stack after create but before start where the user's defined process will be running.
Rootless containers
runc has the ability to run containers without root privileges. This is called rootless. You need to pass some parameters to runc in order to run rootless containers. See below and compare with the previous version. Run the following commands as an ordinary user:
# Same as the first example
mkdir ~/mycontainer
cd ~/mycontainer
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -
# The --rootless parameter instructs runc spec to generate a configuration for a rootless container, which will allow you to run the container as a non-root user.
runc spec --rootless
# The --root parameter tells runc where to store the container state. It must be writable by the user.
runc --root /tmp/runc run mycontaineridSupervisors
runc can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
An example systemd unit file looks something like this.
[Unit]
Description=Start My Container
[Service]
Type=forking
ExecStart=/usr/local/sbin/runc run -d --pid-file /run/mycontainerid.pid mycontainerid
ExecStopPost=/usr/local/sbin/runc delete mycontainerid
WorkingDirectory=/mycontainer
PIDFile=/run/mycontainerid.pid
[Install]
WantedBy=multi-user.targetOverview (`vendor/github.com/opencontainers/runtime-spec/README.md`)
Open Container Initiative Runtime Specification
The Open Container Initiative develops specifications for standards on Operating System process and application containers.
The specification can be found here.
Table of Contents
Additional documentation about how this group operates:
Use Cases
To provide context for users the following section gives example use cases for each part of the spec.
Application Bundle Builders
Application bundle builders can create a bundle directory that includes all of the files required for launching an application as a container.
The bundle contains an OCI configuration file where the builder can specify host-independent details such as which executable to launch and host-specific settings such as mount locations, hook paths, Linux namespaces and cgroups.
Because the configuration includes host-specific settings, application bundle directories copied between two hosts may require configuration adjustments.
Hook Developers
Hook developers can extend the functionality of an OCI-compliant runtime by hooking into a container's lifecycle with an external application.
Example use cases include sophisticated network configuration, volume garbage collection, etc.
Runtime Developers
Runtime developers can build runtime implementations that run OCI-compliant bundles and container configuration, containing low-level OS and host specific details, on a particular platform.
Releases
There is a loose Road Map.
During the 0.x series of OCI releases we make no backwards compatibility guarantees and intend to break the schema during this series.
Contributing
Development happens on GitHub for the spec.
Issues are used for bugs and actionable items and longer discussions can happen on the mailing list.
The specification and code is licensed under the Apache 2.0 license found in the LICENSE file.
Discuss your design
The project welcomes submissions, but please let everyone know what you are working on.
Before undertaking a nontrivial change to this specification, send mail to the mailing list to discuss what you plan to do.
This gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits.
It also guarantees that the design is sound before code is written; a GitHub pull-request is not the place for high-level discussions.
Typos and grammatical errors can go straight to a pull-request.
When in doubt, start on the mailing-list.
Weekly Call
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 2:00 PM (USA Pacific).
Everyone is welcome to participate via UberConference web or audio-only: 415-968-0849 (no PIN needed.)
An initial agenda will be posted to the mailing list earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
Minutes are posted to the mailing list and minutes from past calls are archived to the wiki.
Mailing List
You can subscribe and join the mailing list on Google Groups.
IRC
OCI discussion happens on #opencontainers on Freenode (logs).
Git commit
Sign your work
The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch.
The rules are pretty simple: if you can certify the below (from http://developercertificate.org):
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.then you just add a line to every git commit message:
Signed-off-by: Joe Smith <[email protected]>using your real name (sorry, no pseudonyms or anonymous contributions.)
You can add the sign off when creating the git commit via git commit -s.
Commit Style
Simple house-keeping for clean git history.
Read more on How to Write a Git Commit Message or the Discussion section of git-commit(1).
- Separate the subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
- If there was important/useful/essential conversation or information, copy or include a reference
- When possible, one keyword to scope the change in the subject (i.e. "README: ...", "runtime: ...")
Overview (`vendor/github.com/pkg/errors/README.md`)
errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
Package errors provides simple error handling primitives.
go get github.com/pkg/errors
The traditional error handling idiom in Go is roughly akin to
if err != nil {
return err
}which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}Retrieving the cause of an error
Using errors.Wrap constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by errors.Cause.
type causer interface {
Cause() error
}errors.Cause will recursively retrieve the topmost error which does not implement causer, which is assumed to be the original cause. For example:
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}Read the package documentation for more information.
Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
Before proposing a change, please discuss your change by raising an issue.
Licence
BSD-2-Clause
Overview (`vendor/github.com/pmezard/go-difflib/README.md`)
go-difflib
Go-difflib is a partial port of python 3 difflib package. Its main goal
was to make unified and context diff available in pure Go, mostly for
testing purposes.
The following class and functions (and related tests) have be ported:
SequenceMatcherunified_diff()context_diff()
Installation
$ go get github.com/pmezard/go-difflib/difflibQuick Start
Diffs are configured with Unified (or ContextDiff) structures, and can
be output to an io.Writer or returned as a string.
diff := UnifiedDiff{
A: difflib.SplitLines("foo\nbar\n"),
B: difflib.SplitLines("foo\nbaz\n"),
FromFile: "Original",
ToFile: "Current",
Context: 3,
}
text, _ := GetUnifiedDiffString(diff)
fmt.Printf(text)would output:
--- Original
+++ Current
@@ -1,3 +1,3 @@
foo
-bar
+bazOverview (`vendor/github.com/prometheus/client_golang/README.md`)
Prometheus Go client library
This is the Go client library for
Prometheus. It has two separate parts, one for
instrumenting application code, and one for creating clients that talk to the
Prometheus HTTP API.
Instrumenting applications
Theprometheus directory
contains the instrumentation library. See the
best practices section of the
Prometheus documentation to learn more about instrumenting applications.
Theexamples directory
contains simple examples of instrumented code.
Client for the Prometheus HTTP API
Theapi/prometheus directory
contains the client for the
Prometheus HTTP API. It allows you
to write Go applications that query time series data from a Prometheus server.
Where is `model`, `extraction`, and `text`?
The model packages has been moved toprometheus/common/model.
The extraction and text packages are now contained inprometheus/common/expfmt.
Contributing and community
See the contributing guidelines and the
Community section of the homepage.
Overview (`vendor/github.com/prometheus/client_model/README.md`)
Background
Under most circumstances, manually downloading this repository should never
be required.
Prerequisites
Base
Java
- Apache Maven
- Prometheus Maven Repository checked out into ../io.prometheus-maven-repository
Go
Ruby
Building
$ makeGetting Started
- The Go source code is periodically indexed: Go Protocol Buffer Model.
- All of the core developers are accessible via the Prometheus Developers Mailinglist.
Overview (`vendor/github.com/prometheus/common/README.md`)
Common
This repository contains Go libraries that are shared across Prometheus
components and libraries.
- config: Common configuration structures
- expfmt: Decoding and encoding for the exposition format
- log: A logging wrapper around logrus
- model: Shared data structures
- route: A routing wrapper around httprouter using
context.Context - version: Version informations and metric
Overview (`vendor/github.com/prometheus/procfs/README.md`)
procfs
This procfs package provides functions to retrieve system, kernel and process
metrics from the pseudo-filesystem proc.
WARNING: This package is a work in progress. Its API may still break in
backwards-incompatible ways without warnings. Use it at your own risk.
Overview (`vendor/github.com/stretchr/testify/README.md`)
Testify - Thou Shalt Write Tests
Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend.
Features include:
Get started:
- Install testify with one line of code, or update it with another
- For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing
- Check out the API Documentation http://godoc.org/github.com/stretchr/testify
- To make your testing life easier, check out our other project, gorc
- A little about Test-Driven Development (TDD)
[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package
The assert package provides some helpful methods that allow you to write better test code in Go.
- Prints friendly, easy to read failure descriptions
- Allows for very readable code
- Optionally annotate each assertion with a message
See it in action:
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
// assert equality
assert.Equal(t, 123, 123, "they should be equal")
// assert inequality
assert.NotEqual(t, 123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(t, object)
// assert for not nil (good when you expect something)
if assert.NotNil(t, object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal(t, "Something", object.Value)
}
}- Every assert func takes the
testing.Tobject as the first argument. This is how it writes the errors out through the normalgo testcapabilities. - Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions.
if you assert many times, use the below:
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
assert := assert.New(t)
// assert equality
assert.Equal(123, 123, "they should be equal")
// assert inequality
assert.NotEqual(123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(object)
// assert for not nil (good when you expect something)
if assert.NotNil(object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal("Something", object.Value)
}
}[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package
The require package provides same global functions as the assert package, but instead of returning a boolean result they terminate current test.
See t.FailNow for details.
[`http`](http://godoc.org/github.com/stretchr/testify/http "API documentation") package
The http package contains test objects useful for testing code that relies on the net/http package. Check out the (deprecated) API documentation for the http package.
We recommend you use httptest instead.
[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package
The mock package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.
An example test function that tests a piece of code that relies on an external object testObj, can setup expectations (testify) and assert that they indeed happened:
package yours
import (
"testing"
"github.com/stretchr/testify/mock"
)
/*
Test objects
*/
// MyMockedObject is a mocked object that implements an interface
// that describes an object that the code I am testing relies on.
type MyMockedObject struct{
mock.Mock
}
// DoSomething is a method on MyMockedObject that implements some interface
// and just records the activity, and returns what the Mock object tells it to.
//
// In the real object, this method would do something useful, but since this
// is a mocked object - we're just going to stub it out.
//
// NOTE: This method is not being tested here, code that uses this object is.
func (m *MyMockedObject) DoSomething(number int) (bool, error) {
args := m.Called(number)
return args.Bool(0), args.Error(1)
}
/*
Actual test functions
*/
// TestSomething is an example of how to use our test object to
// make assertions about some target code we are testing.
func TestSomething(t *testing.T) {
// create an instance of our test object
testObj := new(MyMockedObject)
// setup expectations
testObj.On("DoSomething", 123).Return(true, nil)
// call the code we are testing
targetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were met
testObj.AssertExpectations(t)
}For more information on how to write mock code, check out the API documentation for the mock package.
You can use the mockery tool to autogenerate the mock code against an interface as well, making using mocks much quicker.
[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package
The suite package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.
An example suite is shown below:
// Basic imports
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}For a more complete example, using all of the functionality provided by the suite package, look at our example testing suite
For more information on writing suites, check out the API documentation for the suite package.
Suite object has assertion methods:
// Basic imports
import (
"testing"
"github.com/stretchr/testify/suite"
)
// Define the suite, and absorb the built-in basic suite
// functionality from testify - including assertion methods.
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
suite.Equal(suite.VariableThatShouldStartAtFive, 5)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}Installation
To install Testify, use go get:
* Latest version: go get github.com/stretchr/testify
* Specific version: go get gopkg.in/stretchr/testify.v1This will then make the following packages available to you:
github.com/stretchr/testify/assert
github.com/stretchr/testify/mock
github.com/stretchr/testify/httpImport the testify/assert package into your code using this template:
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
assert.True(t, true, "True is true!")
}Staying up to date
To update Testify to the latest version, use go get -u github.com/stretchr/testify.
Version History
- 1.0 - New package versioning strategy adopted.
Contributing
Please feel free to submit issues, fork the repository and send pull requests!
When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it.
Licence
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Overview (`vendor/github.com/urfave/cli/README.md`)
cli
Notice: This is the library formerly known asgithub.com/codegangsta/cli -- Github will automatically redirect requests
to this repository, but we recommend updating your references for clarity.
cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line
applications in an expressive way.
- Overview
- Installation
- Getting Started
- Examples
- Contribution Guidelines
Overview
Command line apps are usually so tiny that there is absolutely no reason why
your code should not be self-documenting. Things like generating help text and
parsing command flags/options should not hinder productivity when writing a
command line app.
This is where cli comes into play. cli makes command line programming fun,
organized, and expressive!
Installation
Make sure you have a working Go environment. Go version 1.2+ is supported. See
the install instructions for Go.
To install cli, simply run:
$ go get github.com/urfave/cliMake sure your PATH includes the $GOPATH/bin directory so your commands can
be easily used:
export PATH=$PATH:$GOPATH/binSupported platforms
cli is tested against multiple versions of Go on Linux, and against the latest
released version of Go on OS X and Windows. For full details, see./.travis.yml and ./appveyor.yml.
Using the `v2` branch
Warning: The v2 branch is currently unreleased and considered unstable.
There is currently a long-lived branch named v2 that is intended to land as
the new master branch once development there has settled down. The currentmaster branch (mirrored as v1) is being manually merged into v2 on
an irregular human-based schedule, but generally if one wants to "upgrade" tov2 now and accept the volatility (read: "awesomeness") that comes along with
that, please use whatever version pinning of your preference, such as viagopkg.in:
$ go get gopkg.in/urfave/cli.v2...
import (
"gopkg.in/urfave/cli.v2" // imports as package "cli"
)
...Pinning to the `v1` releases
Similarly to the section above describing use of the v2 branch, if one wants
to avoid any unexpected compatibility pains once v2 becomes master, then
pinning to v1 is an acceptable option, e.g.:
$ go get gopkg.in/urfave/cli.v1...
import (
"gopkg.in/urfave/cli.v1" // imports as package "cli"
)
...This will pull the latest tagged v1 release (e.g. v1.18.1 at the time of writing).
Getting Started
One of the philosophies behind cli is that an API should be playful and full of
discovery. So a cli app can be as little as one line of code in main().
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
cli.NewApp().Run(os.Args)
}This app will run and show help text, but is not very useful. Let's give an
action to execute and some help documentation:
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) error {
fmt.Println("boom! I say!")
return nil
}
app.Run(os.Args)
}Running this already gives you a ton of functionality, plus support for things
like subcommands and flags, which are covered below.
Examples
Being a programmer can be a lonely job. Thankfully by the power of automation
that is not the case! Let's create a greeter app to fend off our demons of
loneliness!
Start by creating a directory named greet, and within it, add a file,greet.go with the following code in it:
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "greet"
app.Usage = "fight the loneliness!"
app.Action = func(c *cli.Context) error {
fmt.Println("Hello friend!")
return nil
}
app.Run(os.Args)
}Install our command to the $GOPATH/bin directory:
$ go installFinally run our new command:
$ greet
Hello friend!cli also generates neat help text:
$ greet help
NAME:
greet - fight the loneliness!
USAGE:
greet [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS
--version Shows version informationArguments
You can lookup arguments by calling the Args function on cli.Context, e.g.:
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Action = func(c *cli.Context) error {
fmt.Printf("Hello %q", c.Args().Get(0))
return nil
}
app.Run(os.Args)
}Flags
Setting and querying flags is simple.
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
}
app.Action = func(c *cli.Context) error {
name := "Nefertiti"
if c.NArg() > 0 {
name = c.Args().Get(0)
}
if c.String("lang") == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
}
app.Run(os.Args)
}You can also set a destination variable for a flag, to which the content will be
scanned.
package main
import (
"os"
"fmt"
"github.com/urfave/cli"
)
func main() {
var language string
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Destination: &language,
},
}
app.Action = func(c *cli.Context) error {
name := "someone"
if c.NArg() > 0 {
name = c.Args()[0]
}
if language == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
}
app.Run(os.Args)
}See full list of flags at http://godoc.org/github.com/urfave/cli
Placeholder Values
Sometimes it's useful to specify a flag's value within the usage string itself.
Such placeholders are indicated with back quotes.
For example this:
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Load configuration from `FILE`",
},
}
app.Run(os.Args)
}Will result in help output like:
--config FILE, -c FILE Load configuration from FILENote that only the first placeholder is used. Subsequent back-quoted words will
be left as-is.
Alternate Names
You can set alternate (or short) names for flags by providing a comma-delimited
list for the Name. e.g.
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
},
}
app.Run(os.Args)
}That flag can then be set with --lang spanish or -l spanish. Note that
giving two different forms of the same flag in the same command invocation is an
error.
Ordering
Flags for the application and commands are shown in the order they are defined.
However, it's possible to sort them from outside this library by using FlagsByName
or CommandsByName with sort.
For example this:
package main
import (
"os"
"sort"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "Language for the greeting",
},
cli.StringFlag{
Name: "config, c",
Usage: "Load configuration from `FILE`",
},
}
app.Commands = []cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
return nil
},
},
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
return nil
},
},
}
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
app.Run(os.Args)
}Will result in help output like:
--config FILE, -c FILE Load configuration from FILE
--lang value, -l value Language for the greeting (default: "english")Values from the Environment
You can also have the default value set from the environment via EnvVar. e.g.
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "APP_LANG",
},
}
app.Run(os.Args)
}The EnvVar may also be given as a comma-delimited "cascade", where the first
environment variable that resolves is used as the default.
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
},
}
app.Run(os.Args)
}Values from alternate input sources (YAML, TOML, and others)
There is a separate package altsrc that adds support for getting flag values
from other file input sources.
Currently supported input source formats:
- YAML
- TOML
In order to get values for a flag from an alternate input source the following
code would be added to wrap an existing cli.Flag like below:
altsrc.NewIntFlag(cli.IntFlag{Name: "test"})Initialization must also occur for these flags. Below is an example initializing
getting data from a yaml file below.
command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))The code above will use the "load" string as a flag name to get the file name of
a yaml file from the cli.Context. It will then use that file name to initialize
the yaml input source for any flags that are defined on that command. As a note
the "load" flag used would also have to be defined on the command flags in order
for this code snipped to work.
Currently only the aboved specified formats are supported but developers can
add support for other input sources by implementing the
altsrc.InputSourceContext for their given sources.
Here is a more complete sample of a command using YAML support:
package notmain
import (
"fmt"
"os"
"github.com/urfave/cli"
"github.com/urfave/cli/altsrc"
)
func main() {
app := cli.NewApp()
flags := []cli.Flag{
altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"},
}
app.Action = func(c *cli.Context) error {
fmt.Println("yaml ist rad")
return nil
}
app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
app.Flags = flags
app.Run(os.Args)
}Subcommands
Subcommands can be defined for a more git-like command line app.
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
return nil
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
},
{
Name: "template",
Aliases: []string{"t"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) error {
fmt.Println("new task template: ", c.Args().First())
return nil
},
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) error {
fmt.Println("removed task template: ", c.Args().First())
return nil
},
},
},
},
}
app.Run(os.Args)
}Subcommands categories
For additional organization in apps that have many subcommands, you can
associate a category for each command to group them together in the help
output.
E.g.
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Commands = []cli.Command{
{
Name: "noop",
},
{
Name: "add",
Category: "template",
},
{
Name: "remove",
Category: "template",
},
}
app.Run(os.Args)
}Will include:
COMMANDS:
noop
Template actions:
add
removeExit code
Calling App.Run will not automatically call os.Exit, which means that by
default the exit code will "fall through" to being 0. An explicit exit code
may be set by returning a non-nil error that fulfills cli.ExitCoder, or acli.MultiError that includes an error that fulfills cli.ExitCoder, e.g.:
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.BoolTFlag{
Name: "ginger-crouton",
Usage: "is it in the soup?",
},
}
app.Action = func(ctx *cli.Context) error {
if !ctx.Bool("ginger-crouton") {
return cli.NewExitError("it is not in the soup", 86)
}
return nil
}
app.Run(os.Args)
}Bash Completion
You can enable completion commands by setting the EnableBashCompletion
flag on the App object. By default, this setting will only auto-complete to
show an app's subcommands, but you can write your own completion methods for
the App or its subcommands.
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
app := cli.NewApp()
app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
BashComplete: func(c *cli.Context) {
// This will complete if no args are passed
if c.NArg() > 0 {
return
}
for _, t := range tasks {
fmt.Println(t)
}
},
},
}
app.Run(os.Args)
}Enabling
Source the autocomplete/bash_autocomplete file in your .bashrc file while
setting the PROG variable to the name of your program:
PROG=myprogram source /.../cli/autocomplete/bash_autocomplete
Distribution
Copy autocomplete/bash_autocomplete into /etc/bash_completion.d/ and rename
it to the name of the program you wish to add autocomplete support for (or
automatically install it there if you are distributing a package). Don't forget
to source the file to make it active in the current shell.
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
source /etc/bash_completion.d/<myprogram>Alternatively, you can just document that users should source the genericautocomplete/bash_autocomplete in their bash configuration with $PROG set
to the name of their program (as above).
Customization
The default bash completion flag (--generate-bash-completion) is defined ascli.BashCompletionFlag, and may be redefined if desired, e.g.:
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
cli.BashCompletionFlag = cli.BoolFlag{
Name: "compgen",
Hidden: true,
}
app := cli.NewApp()
app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "wat",
},
}
app.Run(os.Args)
}Generated Help Text
The default help flag (-h/--help) is defined as cli.HelpFlag and is checked
by the cli internals in order to print generated help text for the app, command,
or subcommand, and break execution.
Customization
All of the help text generation may be customized, and at multiple levels. The
templates are exposed as variables AppHelpTemplate, CommandHelpTemplate, andSubcommandHelpTemplate which may be reassigned or augmented, and full override
is possible by assigning a compatible func to the cli.HelpPrinter variable,
e.g.:
package main
import (
"fmt"
"io"
"os"
"github.com/urfave/cli"
)
func main() {
// EXAMPLE: Append to an existing template
cli.AppHelpTemplate = fmt.Sprintf(`%s
WEBSITE: http://awesometown.example.com
SUPPORT: [email protected]
`, cli.AppHelpTemplate)
// EXAMPLE: Override a template
cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if len .Authors}}
AUTHOR:
{{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}}
COMMANDS:
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
{{.Copyright}}
{{end}}{{if .Version}}
VERSION:
{{.Version}}
{{end}}
`
// EXAMPLE: Replace the `HelpPrinter` func
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
fmt.Println("Ha HA. I pwnd the help!!1")
}
cli.NewApp().Run(os.Args)
}The default flag may be customized to something other than -h/--help by
setting cli.HelpFlag, e.g.:
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
cli.HelpFlag = cli.BoolFlag{
Name: "halp, haaaaalp",
Usage: "HALP",
EnvVar: "SHOW_HALP,HALPPLZ",
}
cli.NewApp().Run(os.Args)
}Version Flag
The default version flag (-v/--version) is defined as cli.VersionFlag, which
is checked by the cli internals in order to print the App.Version viacli.VersionPrinter and break execution.
Customization
The default flag may be customized to something other than -v/--version by
setting cli.VersionFlag, e.g.:
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
cli.VersionFlag = cli.BoolFlag{
Name: "print-version, V",
Usage: "print only the version",
}
app := cli.NewApp()
app.Name = "partay"
app.Version = "19.99.0"
app.Run(os.Args)
}Alternatively, the version printer at cli.VersionPrinter may be overridden, e.g.:
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
var (
Revision = "fafafaf"
)
func main() {
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
}
app := cli.NewApp()
app.Name = "partay"
app.Version = "19.99.0"
app.Run(os.Args)
}Full API Example
Notice: This is a contrived (functioning) example meant strictly for API
demonstration purposes. Use of one's imagination is encouraged.
/* Detailed source-code truncated for AI context efficiency. */Contribution Guidelines
Feel free to put up a pull request to fix a bug or maybe add a feature. I will
give it a code review and make sure that it does not break backwards
compatibility. If I or any other collaborators agree that it is in line with
the vision of the project, we will work with you to get the code into
a mergeable state and merge it into the master branch.
If you have contributed something significant to the project, we will most
likely add you as a collaborator. As a collaborator you are given the ability
to merge others pull requests. It is very important that new code does not
break existing code, so be careful about what code you do choose to merge.
If you feel like you have contributed to the project but have not yet been
added as a collaborator, we probably forgot to add you, please open an issue.
Overview (`vendor/github.com/opencontainers/runc/libcontainer/README.md`)
libcontainer
Libcontainer provides a native Go implementation for creating containers
with namespaces, cgroups, capabilities, and filesystem access controls.
It allows you to manage the lifecycle of the container performing additional operations
after the container is created.
Container
A container is a self contained execution environment that shares the kernel of the
host system and which is (optionally) isolated from other containers in the system.
Using libcontainer
Because containers are spawned in a two step process you will need a binary that
will be executed as the init process for the container. In libcontainer, we use
the current binary (/proc/self/exe) to be executed as the init process, and use
arg "init", we call the first step process "bootstrap", so you always need a "init"
function as the entry of "bootstrap".
In addition to the go init function the early stage bootstrap is handled by importing
nsenter.
import (
_ "github.com/opencontainers/runc/libcontainer/nsenter"
)
func init() {
if len(os.Args) > 1 && os.Args[1] == "init" {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
logrus.Fatal(err)
}
panic("--this line should have never been executed, congratulations--")
}
}Then to create a container you first have to initialize an instance of a factory
that will handle the creation and initialization for a container.
factory, err := libcontainer.New("/var/lib/container", libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], "init"))
if err != nil {
logrus.Fatal(err)
return
}Once you have an instance of the factory created we can create a configuration
struct describing how the container is to be created. A sample would look similar to this:
/* Detailed source-code truncated for AI context efficiency. */Once you have the configuration populated you can create a container:
container, err := factory.Create("container-id", config)
if err != nil {
logrus.Fatal(err)
return
}To spawn bash as the initial process inside the container and have the
processes pid returned in order to wait, signal, or kill the process:
process := &libcontainer.Process{
Args: []string{"/bin/bash"},
Env: []string{"PATH=/bin"},
User: "daemon",
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
err := container.Run(process)
if err != nil {
container.Destroy()
logrus.Fatal(err)
return
}
// wait for the process to finish.
_, err := process.Wait()
if err != nil {
logrus.Fatal(err)
}
// destroy the container.
container.Destroy()Additional ways to interact with a running container are:
// return all the pids for all processes running inside the container.
processes, err := container.Processes()
// get detailed cpu, memory, io, and network statistics for the container and
// it's processes.
stats, err := container.Stats()
// pause all processes inside the container.
container.Pause()
// resume all paused processes.
container.Resume()
// send signal to container's init process.
container.Signal(signal)
// update container resource constraints.
container.Set(config)
// get current status of the container.
status, err := container.Status()
// get current container's state information.
state, err := container.State()Checkpoint & Restore
libcontainer now integrates CRIU for checkpointing and restoring containers.
This let's you save the state of a process running inside a container to disk, and then restore
that state into a new process, on the same machine or on another machine.
criu version 1.5.2 or higher is required to use checkpoint and restore.
If you don't already have criu installed, you can build it from source, following the
online instructions. criu is also installed in the docker image
generated when building libcontainer with docker.
Copyright and license
Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license.
Docs released under Creative commons.
Overview (`vendor/github.com/prometheus/client_golang/prometheus/README.md`)
Overview (`vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md`)
nsenter
The nsenter package registers a special init constructor that is called before
the Go runtime has a chance to boot. This provides us the ability to setns on
existing namespaces and avoid the issues that the Go runtime has with multiple
threads. This constructor will be called if this package is registered,
imported, in your go application.
The nsenter package will import "C" and it uses cgo
package. In cgo, if the import of "C" is immediately preceded by a comment, that comment,
called the preamble, is used as a header when compiling the C parts of the package.
So every time we import package nsenter, the C code function nsexec() would be
called. And package nsenter is now only imported in main_unix.go, so every time
before we call cmd.Start on linux, that C code would run.
Because nsexec() must be run before the Go runtime in order to use the
Linux kernel namespace, you must import this library into a package if
you plan to use libcontainer directly. Otherwise Go will not execute
the nsexec() constructor, which means that the re-exec will not cause
the namespaces to be joined. You can import it like this:
import _ "github.com/opencontainers/runc/libcontainer/nsenter"nsexec() will first get the file descriptor number for the init pipe
from the environment variable _LIBCONTAINER_INITPIPE (which was opened
by the parent and kept open across the fork-exec of the nsexec() init
process). The init pipe is used to read bootstrap data (namespace paths,
clone flags, uid and gid mappings, and the console path) from the parent
process. nsexec() will then call setns(2) to join the namespaces
provided in the bootstrap data (if available), clone(2) a child process
with the provided clone flags, update the user and group ID mappings, do
some further miscellaneous setup steps, and then send the PID of the
child process to the parent of the nsexec() "caller". Finally,
the parent nsexec() will exit and the child nsexec() process will
return to allow the Go runtime take over.
NOTE: We do both setns(2) and clone(2) even if we don't have any
CLONE_NEW* clone flags because we must fork a new process in order to
enter the PID namespace.
Index (`docs/index.md`)
title: containerd layout: home
News
- Sign up for containerd Summit at DockerCon in April
- March 15th: Docker to donate containerd to the Cloud Native Computing Foundation
- February 28th: containerd Summit recap
- January 4th: containerd Livestream recap
About containerd
containerd is an industry-standard core container runtime with an emphasis on simplicity, robustness and portability. It is available as a daemon for Linux and Windows, which can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage and network attachments, etc..
containerd is designed to be embedded into a larger system, rather than being used directly by developers or end-users.
containerd includes a daemon exposing gRPC API over a local UNIX socket. The API is a low-level one designed for higher layers to wrap and extend. It also includes a barebone CLI (ctr) designed specifically for development and debugging purpose. It uses runC to run containers according to the OCI specification. The code can be found on GitHub, and here are the contribution guidelines.
containerd is based on the Docker Engine’s core container runtime to benefit from its maturity and existing contributors.
Features and roadmap
- OCI Image Spec support
- OCI Runtime Spec support (aka runC)
- Image push and pull support
- Container runtime and lifecycle support
- Network primitives for creation, modification, and deletion of interfaces
- Management of network namespaces containers to join existing namespaces
- Multi-tenant supported with CAS storage for global images
containerd, currently in version 0.2.4, is a daemon born from extracting the container execution subset of the Docker Engine, and is used internally by Docker since the 1.11 release. containerd will graduate to version 1.0 once its feature scope matches the requirements above, from which it will commit to API stability as well as committing to a 1-year LTS release. Docker Engine commits to upgrading to containerd 1.0 as part of its regular release cycle.
containerd roadmap to implement these features is described in the containerd Github repository.
Architecture
Containerd architecture and API are described in the Github repository.
Principles and Releases
Having a clearly defined scope of a project is important for ensuring consistency and focus.
These following criteria will be used when reviewing pull requests, features, and changes for the project before being accepted.
Components
Components should not have tight dependencies on each other so that they are unable to be used independently.
The APIs for images and containers should be designed in a way that when used together the components have a natural flow but still be useful independently.
An example for this design can be seen with the overlay filesystems and the container execution layer.
The execution layer and overlay filesystems can be used independently but if you were to use both, they share a common Mount struct that the filesystems produce and the execution layer consumes.
Primitives
containerd should expose primitives to solve problems instead of building high level abstractions in the API.
A common example of this is how build would be implemented.
Instead of having a build API in containerd we should expose the lower level primitives that allow things required in build to work.
Breaking up the filesystem APIs to allow snapshots, copy functionality, and mounts allow people implementing build at the higher levels more flexibility.
Extensibility and Defaults
For the various components in containerd there should be defined extension points where implementations can be swapped for alternatives.
The best example of this is that containerd will use runc from OCI as the default runtime in the execution layer but other runtimes conforming to the OCI Runtime specification they can be easily added to containerd.
containerd will come with a default implementation for the various components.
These defaults will be chosen by the maintainers of the project and should not change unless better tech for that component comes out.
Additional implementations will not be accepted into the core repository and should be developed in a separate repository not maintained by the containerd maintainers.
Releases
containerd will be released with a 1.0 when feature complete with Linux and Windows parity and this version will be supported for 1 year with security and bug fixes applied and released.
The upgrade path for containerd is that the 0.0.x patch releases are always backward compatible with its major and minor version.
Minor (0.x.0) version will always be compatible with the previous minor release. i.e. 1.2.0 is backwards compatible with 1.1.0 and 1.1.0 is compatible with 1.0.0.
There is no compatibility guarantees with upgrades from two minor relases. i.e. 1.0.0 to 1.2.0.
There are no backwards compatibility guarantees with upgrades to major versions. i.e 1.0.0 to 2.0.0.
Each major version will be supported for 1 year with bug fixes and security patches.
FAQ
- What is the relationship between containerd and Docker?
Docker is a complete platform and programming environment for containerized applications. containerd is one of dozens of specialized components integrated into Docker. Developers and IT professionals looking to build, ship and run containerized applications should continue to use Docker. Operators and integrators looking for specialized components to swap into their platform should consider containerd.
containerd 0.2.4 used in Docker 1.12 covers only container execution and process management.
containerd's roadmap is to refactor the Docker Engine codebase to extract more of its logic for distribution, networking and storage on a single host into a reusable component that Docker will use, and that can be used by other container orchestration projects or hosted container services.
- What is the relationship between containerd, OCI and runc?
Docker donated the OCI specification to the Linux Foundation in 2015, along with a reference implementation called runc. containerd integrates OCI/runc into a feature-complete, production-ready core container runtime. runc is a component of containerd, the executor for containers. containerd has a wider scope than just executing containers: downloading container images, managing storage and network interfaces, calling runc with the right parameters to run containers. containerd fully leverages the Open Container Initiative’s (OCI) runtime, image format specifications and OCI reference implementation (runc) and will pursue OCI certification when it is available. Because of its massive adoption, containerd is the industry standard for implementing OCI.
- What is the relationship between containerd and container orchestration systems like Kubernetes and Mesos?
Kubernetes today uses Docker directly. In a future version Kubernetes can implement container support in the Kubelet by implementing it's Container Runtime Interface using containerd. Mesos and other orchestration engines can leverage containerd for core container runtime functionality as well.
- What is the relationship between containerd and cloud managed container services?
See answer above: cloud managed container services can leverage containerd instead of Docker in order to provide container functionalities to their users.
Config.Yml (`docs/_config.yml`)
markdown: kramdown
kramdown:
input: GFM
html_to_native: true
hard_wrap: false
syntax_highlighter: rouge
incremental: true
permalink: pretty
safe: false
lsi: false
exclude: [README.md]
gems:
- jekyll-redirect-from
- jekyll-seo-tag
defaults:
scope:
path: ""
type: "pages"
values:
layout: defaultDockercon Summit (`docs/dockercon-summit.md`)
title: Dockercon 2017 Summit layout: home
Dockercon 2017 Summit
This year at Dockercon US 2017 we will be having a containerd Summit on Thursday morning the week of the conference.
We are going to change the format slightly compared to the previous summit that was held in February. We will be allocating more time to the breakout sessions and less time to static talks. However, the group will be much larger than the previous summit so this document serves as a way to add discussion points for the breakout sessions.
If you would like to add a discussion point to the agenda, submit a PR adding it to the list below. A simple one line sentence is enough or expand if needed.
If you have not signed up to attend the summit you can do so in this form.
Discussion Points
The following are proposed discussion points for the containerd summit at Dockercon US 2017:
- Since containerd is one of the bottom bricks in the stack, how can we setup automated integration tests for consumers of containerd?
- We'd like to propose an Authorization plugin to containerd that would allow an external component to police events like container start & stop (and have a discussion about the best way to go about it)
- Should containerd provide image filesystem metrics? If yes, what metrics should be included? How to implement that?
- Support for disk quotas: How? What is the role of containerd? How is it going to be integrated with volume managers that want to be in the same quota group?
- Checkpoint/Restore: how can we support more use cases? One of the big issues here is the large number of options that can be passed to CRIU.
- How to support multi-OS docker images, for example, Linux Vs Windows using one graph driver plugin properly?
--- METRICS ---
- Files Extracted: 43
- Estimated Token Budget: ~52298 tokens
- Canonical Reference: https://codewiki.google/github.com/docker/containerd
