Merge pull request #2513 from amscanne:website-integrated
PiperOrigin-RevId: 311184385
@@ -1,9 +1,48 @@
|
||||
load("//tools:defs.bzl", "build_test", "gazelle", "go_path")
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
doc(
|
||||
name = "contributing",
|
||||
src = "CONTRIBUTING.md",
|
||||
category = "Project",
|
||||
permalink = "/contributing/",
|
||||
visibility = ["//website:__pkg__"],
|
||||
weight = "20",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "security",
|
||||
src = "SECURITY.md",
|
||||
category = "Project",
|
||||
permalink = "/security/",
|
||||
visibility = ["//website:__pkg__"],
|
||||
weight = "30",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "governance",
|
||||
src = "GOVERNANCE.md",
|
||||
category = "Project",
|
||||
permalink = "/community/governance/",
|
||||
subcategory = "Community",
|
||||
visibility = ["//website:__pkg__"],
|
||||
weight = "91",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "code_of_conduct",
|
||||
src = "CODE_OF_CONDUCT.md",
|
||||
category = "Project",
|
||||
permalink = "/community/code_of_conduct/",
|
||||
subcategory = "Community",
|
||||
visibility = ["//website:__pkg__"],
|
||||
weight = "99",
|
||||
)
|
||||
|
||||
# The sandbox filegroup is used for sandbox-internal dependencies.
|
||||
package_group(
|
||||
name = "sandbox",
|
||||
|
||||
@@ -87,6 +87,5 @@ harassment or threats to anyone's safety, we may take action without notice.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
|
||||
available at
|
||||
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the
|
||||
[Contributor Covenant, version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html).
|
||||
|
||||
@@ -39,8 +39,8 @@ Dependencies can be added by using `go mod get`. In order to keep the
|
||||
|
||||
All Go code should conform to the [Go style guidelines][gostyle]. C++ code
|
||||
should conform to the [Google C++ Style Guide][cppstyle] and the guidelines
|
||||
described for [tests][teststyle]. Note that code may be automatically formatted
|
||||
per the guidelines when merged.
|
||||
described for tests. Note that code may be automatically formatted per the
|
||||
guidelines when merged.
|
||||
|
||||
As a secure runtime, we need to maintain the safety of all of code included in
|
||||
gVisor. The following rules help mitigate issues.
|
||||
@@ -131,4 +131,3 @@ one above, the
|
||||
[github]: https://github.com/google/gvisor/compare
|
||||
[gvisor-dev-list]: https://groups.google.com/forum/#!forum/gvisor-dev
|
||||
[gostyle]: https://github.com/golang/go/wiki/CodeReviewComments
|
||||
[teststyle]: ./test/
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# Governance
|
||||
|
||||
## Projects
|
||||
|
||||
A *project* is the primary unit of collaboration. Each project may have its own
|
||||
repository and contribution process.
|
||||
|
||||
All projects are covered by the [Code of Conduct](CODE_OF_CONDUCT.md), and
|
||||
should include an up-to-date copy in the project repository or a link here.
|
||||
|
||||
## Contributors
|
||||
|
||||
Anyone can be a *contributor* to a project, provided they have signed relevant
|
||||
Contributor License Agreements (CLAs) and follow the project's contribution
|
||||
guidelines. Contributions will be reviewed by a maintainer, and must pass all
|
||||
applicable tests.
|
||||
|
||||
Reviews check for code quality and style, including documentation, and enforce
|
||||
other policies. Contributions may be rejected for reasons unrelated to the code
|
||||
in question. For example, a change may be too complex to maintain or duplicate
|
||||
existing functionality.
|
||||
|
||||
Note that contributions are not limited to code alone. Bugs, documentation,
|
||||
experience reports or public advocacy are all valuable ways to contribute to a
|
||||
project and build trust in the community.
|
||||
|
||||
## Maintainers
|
||||
|
||||
Each project has one or more *maintainers*. Maintainers set technical direction,
|
||||
facilitate contributions and exercise overall stewardship.
|
||||
|
||||
Maintainers have write access to the project repository. Maintainers review and
|
||||
approve changes. They can also assign issues and add additional reviewers.
|
||||
|
||||
Note that some repositories may not allow direct commit access, which is
|
||||
reserved for administrators or automated processes. In this case, maintainers
|
||||
have approval rights, and a separate process exists for merging a change.
|
||||
|
||||
Maintainers are responsible for upholding the code of conduct in interactions
|
||||
via project communication channels. If comments or exchanges are in violation,
|
||||
they may remove them at their discretion.
|
||||
|
||||
### Repositories requiring synchronization
|
||||
|
||||
For some projects initiated by Google, the infrastructure which synchronizes and
|
||||
merges internal and external changes requires that merges are performed by a
|
||||
Google employee. In such cases, Google will initiate a rotation to merge changes
|
||||
once they pass tests and are approved by a maintainer. This does not preclude
|
||||
non-Google contributors from becoming maintainers, in which case the maintainer
|
||||
holds approval rights and the merge is an automated process. In some cases,
|
||||
Google-internal tests may fail and have to be fixed: the Google employee will
|
||||
work with the submitter to achieve this.
|
||||
|
||||
### Becoming a maintainer
|
||||
|
||||
The list of maintainers is defined by the list of people with commit access or
|
||||
approval authority on a repository, typically via a Gerrit group or a GitHub
|
||||
team.
|
||||
|
||||
Existing maintainers may elevate a contributor to maintainer status on evidence
|
||||
of previous contributions and established trust. This decision is based on lazy
|
||||
consensus from existing maintainers. While contributors may ask maintainers to
|
||||
make this decision, existing maintainers will also pro-actively identify
|
||||
contributors who have demonstrated a sustained track record of technical
|
||||
leadership and direct contributions.
|
||||
|
||||
## Special Interest Groups (SIGs)
|
||||
|
||||
From time-to-time, a SIG may be formed in order to solve larger, more complex
|
||||
problems across one or more projects. There are many avenues for collaboration
|
||||
outside a SIG, but a SIG can provide structure for collaboration on a single
|
||||
topic.
|
||||
|
||||
Each group will be established by a charter, and governed by the Code of
|
||||
Conduct. Some resources may be provided to the group, such as mailing lists or
|
||||
meeting space, and archives will be public.
|
||||
|
||||
## Security disclosure
|
||||
|
||||
Projects may maintain security mailing lists for vulnerability reports and
|
||||
internal project audits may occasionally reveal security issues. Access to these
|
||||
lists and audits will be limited to project *maintainers*; individual
|
||||
maintainers should opt to participate in these lists based on need and
|
||||
expertise. Once maintainers become aware of a potential security issue, they
|
||||
will assess the scope and potential impact. If reported externally, maintainers
|
||||
will determine a reasonable embargo period with the reporter.
|
||||
|
||||
During the embargo period, the maintainers will prioritize a fix for the
|
||||
security issue. They may choose to disclose the issue to additional trusted
|
||||
contributors in order to facilitate a fix, subjecting them to the embargo, or
|
||||
notify affected users in order to give them an advanced opportunity to mitigate
|
||||
the issue. The inclusion of specific users in this disclosure is left to the
|
||||
discretion of the maintainers and contributors involved, and depends on the
|
||||
scale of known project use and exposure.
|
||||
|
||||
Once a fix is widely available or the embargo period ends, the maintainers will
|
||||
make technical details about the vulnerability and associated fixes available.
|
||||
|
||||
## Mailing lists
|
||||
|
||||
There are four key mailing lists that span projects.
|
||||
|
||||
* [gvisor-users](mailto:[email protected]): general purpose user
|
||||
list.
|
||||
* [gvisor-dev](mailto:[email protected]): general purpose
|
||||
development list.
|
||||
* [gvisor-security](mailto:[email protected]): private security
|
||||
list. Access to this list is restricted to maintainers of the core gVisor
|
||||
project, subject to the security disclosure policy described above.
|
||||
* [gvisor-syzkaller](mailto:[email protected]): private
|
||||
syzkaller bug tracking list. Access to this list is not limited to
|
||||
maintainers, but will be granted to those who can credibly contribute to
|
||||
fixes.
|
||||
@@ -108,7 +108,7 @@ runsc: ## Builds the runsc binary.
|
||||
.PHONY: runsc
|
||||
|
||||
smoke-test: ## Runs a simple smoke test after build runsc.
|
||||
@$(MAKE) run DOCKER_RUN_OPTIONS="" ARGS="--alsologtostderr --network none --debug --TESTONLY-unsafe-nonroot=true --rootless do true"
|
||||
@$(MAKE) run DOCKER_PRIVILEGED="" ARGS="--alsologtostderr --network none --debug --TESTONLY-unsafe-nonroot=true --rootless do true"
|
||||
.PHONY: smoke-tests
|
||||
|
||||
unit-tests: ## Runs all unit tests in pkg runsc and tools.
|
||||
@@ -119,6 +119,38 @@ tests: ## Runs all local ptrace system call tests.
|
||||
@$(MAKE) test OPTIONS="--test_tag_filter runsc_ptrace test/syscalls/..."
|
||||
.PHONY: tests
|
||||
|
||||
##
|
||||
## Website & documentation helpers.
|
||||
##
|
||||
## The website is built from repository documentation and wrappers, using
|
||||
## using a locally-defined Docker image (see images/jekyll). The following
|
||||
## variables may be set when using website-push:
|
||||
## WEBSITE_IMAGE - The name of the container image.
|
||||
## WEBSITE_SERVICE - The backend service.
|
||||
## WEBSITE_PROJECT - The project id to use.
|
||||
## WEBSITE_REGION - The region to deploy to.
|
||||
##
|
||||
WEBSITE_IMAGE := gcr.io/gvisordev/gvisordev
|
||||
WEBSITE_SERVICE := gvisordev
|
||||
WEBSITE_PROJECT := gvisordev
|
||||
WEBSITE_REGION := us-central1
|
||||
|
||||
website-build: load-jekyll ## Build the site image locally.
|
||||
@$(MAKE) run TARGETS="//website:website"
|
||||
.PHONY: website-build
|
||||
|
||||
website-server: website-build ## Run a local server for development.
|
||||
@docker run -i -p 8080:8080 gvisor.dev/images/website
|
||||
.PHONY: website-server
|
||||
|
||||
website-push: website-build ## Push a new image and update the service.
|
||||
@docker tag gvisor.dev/images/website $(WEBSITE_IMAGE) && docker push $(WEBSITE_IMAGE)
|
||||
.PHONY: website-push
|
||||
|
||||
website-deploy: website-push ## Deploy a new version of the website.
|
||||
@gcloud run deploy $(WEBSITE_SERVICE) --platform=managed --region=$(WEBSITE_REGION) --project=$(WEBSITE_PROJECT) --image=$(WEBSITE_IMAGE)
|
||||
.PHONY: website-push
|
||||
|
||||
##
|
||||
## Development helpers and tooling.
|
||||
##
|
||||
|
||||
@@ -5,7 +5,7 @@ the [gvisor-security mailing list][gvisor-security-list]. You should receive a
|
||||
prompt response, typically within 48 hours.
|
||||
|
||||
Policies for security list access, vulnerability embargo, and vulnerability
|
||||
disclosure are outlined in the [community][community] repository.
|
||||
disclosure are outlined in the [governance policy](GOVERNANCE.md).
|
||||
|
||||
[community]: https://gvisor.googlesource.com/community
|
||||
[gvisor-security-list]: https://groups.google.com/forum/#!forum/gvisor-security
|
||||
|
||||
@@ -380,15 +380,15 @@ go_repository(
|
||||
go_repository(
|
||||
name = "org_uber_go_atomic",
|
||||
importpath = "go.uber.org/atomic",
|
||||
version = "v1.6.0",
|
||||
sum = "h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=",
|
||||
version = "v1.6.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "org_uber_go_multierr",
|
||||
importpath = "go.uber.org/multierr",
|
||||
version = "v1.5.0",
|
||||
sum = "h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=",
|
||||
version = "v1.5.0",
|
||||
)
|
||||
|
||||
# BigQuery Dependencies for Benchmarks
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(
|
||||
default_visibility = ["//website:__pkg__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "index",
|
||||
src = "README.md",
|
||||
category = "Project",
|
||||
permalink = "/docs/",
|
||||
weight = "0",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "roadmap",
|
||||
src = "roadmap.md",
|
||||
category = "Project",
|
||||
permalink = "/roadmap/",
|
||||
weight = "10",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "community",
|
||||
src = "community.md",
|
||||
category = "Project",
|
||||
permalink = "/community/",
|
||||
subcategory = "Community",
|
||||
weight = "95",
|
||||
)
|
||||
@@ -1,2 +1,27 @@
|
||||
The gVisor logo files are licensed under CC BY-SA 4.0 (Creative Commons
|
||||
Attribution-ShareAlike 4.0 International).
|
||||
# What is gVisor?
|
||||
|
||||
gVisor is a user-space kernel, written in Go, that implements a substantial
|
||||
portion of the [Linux system call interface][linux]. It provides an additional
|
||||
layer of isolation between running applications and the host operating system.
|
||||
|
||||
gVisor includes an [Open Container Initiative (OCI)][oci] runtime called `runsc`
|
||||
that makes it easy to work with existing container tooling. The `runsc` runtime
|
||||
integrates with Docker and Kubernetes, making it simple to run sandboxed
|
||||
containers.
|
||||
|
||||
gVisor takes a distinct approach to container sandboxing and makes a different
|
||||
set of technical trade-offs compared to existing sandbox technologies, thus
|
||||
providing new tools and ideas for the container security landscape.
|
||||
|
||||
gVisor can be used with Docker, Kubernetes, or directly using `runsc`. Use the
|
||||
links below to see detailed instructions for each of them:
|
||||
|
||||
* [Docker](./user_guide/quick_start/docker/): The quickest and easiest way to
|
||||
get started.
|
||||
* [Kubernetes](./user_guide/quick_start/kubernetes/): Isolate Pods in your K8s
|
||||
cluster with gVisor.
|
||||
* [OCI Quick Start](./user_guide/quick_start/oci/): Expert mode. Customize
|
||||
gVisor for your environment.
|
||||
|
||||
[linux]: https://en.wikipedia.org/wiki/Linux_kernel_interfaces
|
||||
[oci]: https://www.opencontainers.org
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(
|
||||
default_visibility = ["//website:__pkg__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "index",
|
||||
src = "README.md",
|
||||
category = "Architecture Guide",
|
||||
data = [
|
||||
"Layers.png",
|
||||
"Layers.svg",
|
||||
"Machine-Virtualization.png",
|
||||
"Machine-Virtualization.svg",
|
||||
"Rule-Based-Execution.png",
|
||||
"Rule-Based-Execution.svg",
|
||||
"Sentry-Gofer.png",
|
||||
"Sentry-Gofer.svg",
|
||||
],
|
||||
permalink = "/docs/architecture_guide/",
|
||||
weight = "0",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "platforms",
|
||||
src = "platforms.md",
|
||||
category = "Architecture Guide",
|
||||
data = [
|
||||
"Sentry-Gofer.png",
|
||||
"Sentry-Gofer.svg",
|
||||
],
|
||||
permalink = "/docs/architecture_guide/platforms/",
|
||||
weight = "40",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "resources",
|
||||
src = "resources.md",
|
||||
category = "Architecture Guide",
|
||||
permalink = "/docs/architecture_guide/resources/",
|
||||
weight = "30",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "security",
|
||||
src = "security.md",
|
||||
category = "Architecture Guide",
|
||||
data = [
|
||||
"Layers.png",
|
||||
"Layers.svg",
|
||||
],
|
||||
permalink = "/docs/architecture_guide/security/",
|
||||
weight = "10",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "performance",
|
||||
src = "performance.md",
|
||||
category = "Architecture Guide",
|
||||
permalink = "/docs/architecture_guide/performance/",
|
||||
weight = "20",
|
||||
)
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,80 @@
|
||||
# Overview
|
||||
|
||||
gVisor provides a virtualized environment in order to sandbox untrusted
|
||||
containers. The system interfaces normally implemented by the host kernel are
|
||||
moved into a distinct, per-sandbox user space kernel in order to minimize the
|
||||
risk of an exploit. gVisor does not introduce large fixed overheads however, and
|
||||
still retains a process-like model with respect to resource utilization.
|
||||
|
||||
## How is this different?
|
||||
|
||||
Two other approaches are commonly taken to provide stronger isolation than
|
||||
native containers.
|
||||
|
||||
**Machine-level virtualization**, such as [KVM][kvm] and [Xen][xen], exposes
|
||||
virtualized hardware to a guest kernel via a Virtual Machine Monitor (VMM). This
|
||||
virtualized hardware is generally enlightened (paravirtualized) and additional
|
||||
mechanisms can be used to improve the visibility between the guest and host
|
||||
(e.g. balloon drivers, paravirtualized spinlocks). Running containers in
|
||||
distinct virtual machines can provide great isolation, compatibility and
|
||||
performance (though nested virtualization may bring challenges in this area),
|
||||
but for containers it often requires additional proxies and agents, and may
|
||||
require a larger resource footprint and slower start-up times.
|
||||
|
||||

|
||||
|
||||
**Rule-based execution**, such as [seccomp][seccomp], [SELinux][selinux] and
|
||||
[AppArmor][apparmor], allows the specification of a fine-grained security policy
|
||||
for an application or container. These schemes typically rely on hooks
|
||||
implemented inside the host kernel to enforce the rules. If the surface can be
|
||||
made small enough (i.e. a sufficiently complete policy defined), then this is an
|
||||
excellent way to sandbox applications and maintain native performance. However,
|
||||
in practice it can be extremely difficult (if not impossible) to reliably define
|
||||
a policy for arbitrary, previously unknown applications, making this approach
|
||||
challenging to apply universally.
|
||||
|
||||

|
||||
|
||||
Rule-based execution is often combined with additional layers for
|
||||
defense-in-depth.
|
||||
|
||||
**gVisor** provides a third isolation mechanism, distinct from those above.
|
||||
|
||||
gVisor intercepts application system calls and acts as the guest kernel, without
|
||||
the need for translation through virtualized hardware. gVisor may be thought of
|
||||
as either a merged guest kernel and VMM, or as seccomp on steroids. This
|
||||
architecture allows it to provide a flexible resource footprint (i.e. one based
|
||||
on threads and memory mappings, not fixed guest physical resources) while also
|
||||
lowering the fixed costs of virtualization. However, this comes at the price of
|
||||
reduced application compatibility and higher per-system call overhead.
|
||||
|
||||

|
||||
|
||||
On top of this, gVisor employs rule-based execution to provide defense-in-depth
|
||||
(details below).
|
||||
|
||||
gVisor's approach is similar to [User Mode Linux (UML)][uml], although UML
|
||||
virtualizes hardware internally and thus provides a fixed resource footprint.
|
||||
|
||||
Each of the above approaches may excel in distinct scenarios. For example,
|
||||
machine-level virtualization will face challenges achieving high density, while
|
||||
gVisor may provide poor performance for system call heavy workloads.
|
||||
|
||||
### Why Go?
|
||||
|
||||
gVisor is written in [Go][golang] in order to avoid security pitfalls that can
|
||||
plague kernels. With Go, there are strong types, built-in bounds checks, no
|
||||
uninitialized variables, no use-after-free, no stack overflow, and a built-in
|
||||
race detector. (The use of Go has its challenges too, and isn't free.)
|
||||
|
||||
### What about Gofers?
|
||||
|
||||
<a name="gofer"></a> <!-- For deep linking. -->
|
||||
|
||||
[apparmor]: https://wiki.ubuntu.com/AppArmor
|
||||
[golang]: https://golang.org
|
||||
[kvm]: https://www.linux-kvm.org
|
||||
[seccomp]: https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt
|
||||
[selinux]: https://selinuxproject.org
|
||||
[uml]: http://user-mode-linux.sourceforge.net/
|
||||
[xen]: https://www.xenproject.org
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,274 @@
|
||||
# Performance Guide
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor is designed to provide a secure, virtualized environment while preserving
|
||||
key benefits of containerization, such as small fixed overheads and a dynamic
|
||||
resource footprint. For containerized infrastructure, this can provide a
|
||||
turn-key solution for sandboxing untrusted workloads: there are no changes to
|
||||
the fundamental resource model.
|
||||
|
||||
gVisor imposes runtime costs over native containers. These costs come in two
|
||||
forms: additional cycles and memory usage, which may manifest as increased
|
||||
latency, reduced throughput or density, or not at all. In general, these costs
|
||||
come from two different sources.
|
||||
|
||||
First, the existence of the [Sentry](../) means that additional memory will be
|
||||
required, and application system calls must traverse additional layers of
|
||||
software. The design emphasizes [security](../security/) and therefore we chose
|
||||
to use a language for the Sentry that provides benefits in this domain but may
|
||||
not yet offer the raw performance of other choices. Costs imposed by these
|
||||
design choices are **structural costs**.
|
||||
|
||||
Second, as gVisor is an independent implementation of the system call surface,
|
||||
many of the subsystems or specific calls are not as optimized as more mature
|
||||
implementations. A good example here is the network stack, which is continuing
|
||||
to evolve but does not support all the advanced recovery mechanisms offered by
|
||||
other stacks and is less CPU efficient. This is an **implementation cost** and
|
||||
is distinct from **structural costs**. Improvements here are ongoing and driven
|
||||
by the workloads that matter to gVisor users and contributors.
|
||||
|
||||
This page provides a guide for understanding baseline performance, and calls out
|
||||
distint **structural costs** and **implementation costs**, highlighting where
|
||||
improvements are possible and not possible.
|
||||
|
||||
While we include a variety of workloads here, it’s worth emphasizing that gVisor
|
||||
may not be an appropriate solution for every workload, for reasons other than
|
||||
performance. For example, a sandbox may provide minimal benefit for a trusted
|
||||
database, since _user data would already be inside the sandbox_ and there is no
|
||||
need for an attacker to break out in the first place.
|
||||
|
||||
## Methodology
|
||||
|
||||
All data below was generated using the [benchmark tools][benchmark-tools]
|
||||
repository, and the machines under test are uniform [Google Compute Engine][gce]
|
||||
Virtual Machines (VMs) with the following specifications:
|
||||
|
||||
Machine type: n1-standard-4 (broadwell)
|
||||
Image: Debian GNU/Linux 9 (stretch) 4.19.0-0
|
||||
BootDisk: 2048GB SSD persistent disk
|
||||
|
||||
Through this document, `runsc` is used to indicate the runtime provided by
|
||||
gVisor. When relevant, we use the name `runsc-platform` to describe a specific
|
||||
[platform choice](../platforms/).
|
||||
|
||||
**Except where specified, all tests below are conducted with the `ptrace`
|
||||
platform. The `ptrace` platform works everywhere and does not require hardware
|
||||
virtualization or kernel modifications but suffers from the highest structural
|
||||
costs by far. This platform is used to provide a clear understanding of the
|
||||
performance model, but in no way represents an ideal scenario. In the future,
|
||||
this guide will be extended to bare metal environments and include additional
|
||||
platforms.**
|
||||
|
||||
## Memory access
|
||||
|
||||
gVisor does not introduce any additional costs with respect to raw memory
|
||||
accesses. Page faults and other Operating System (OS) mechanisms are translated
|
||||
through the Sentry, but once mappings are installed and available to the
|
||||
application, there is no additional overhead.
|
||||
|
||||
{% include graph.html id="sysbench-memory"
|
||||
url="/performance/sysbench-memory.csv" title="perf.py sysbench.memory
|
||||
--runtime=runc --runtime=runsc" %}
|
||||
|
||||
The above figure demonstrates the memory transfer rate as measured by
|
||||
`sysbench`.
|
||||
|
||||
## Memory usage
|
||||
|
||||
The Sentry provides an additional layer of indirection, and it requires memory
|
||||
in order to store state associated with the application. This memory generally
|
||||
consists of a fixed component, plus an amount that varies with the usage of
|
||||
operating system resources (e.g. how many sockets or files are opened).
|
||||
|
||||
For many use cases, fixed memory overheads are a primary concern. This may be
|
||||
because sandboxed containers handle a low volume of requests, and it is
|
||||
therefore important to achieve high densities for efficiency.
|
||||
|
||||
{% include graph.html id="density" url="/performance/density.csv" title="perf.py
|
||||
density --runtime=runc --runtime=runsc" log="true" y_min="100000" %}
|
||||
|
||||
The above figure demonstrates these costs based on three sample applications.
|
||||
This test is the result of running many instances of a container (50, or 5 in
|
||||
the case of redis) and calculating available memory on the host before and
|
||||
afterwards, and dividing the difference by the number of containers. This
|
||||
technique is used for measuring memory usage over the `usage_in_bytes` value of
|
||||
the container cgroup because we found that some container runtimes, other than
|
||||
`runc` and `runsc`, do not use an individual container cgroup.
|
||||
|
||||
The first application is an instance of `sleep`: a trivial application that does
|
||||
nothing. The second application is a synthetic `node` application which imports
|
||||
a number of modules and listens for requests. The third application is a similar
|
||||
synthetic `ruby` application which does the same. Finally, we include an
|
||||
instance of `redis` storing approximately 1GB of data. In all cases, the sandbox
|
||||
itself is responsible for a small, mostly fixed amount of memory overhead.
|
||||
|
||||
## CPU performance
|
||||
|
||||
gVisor does not perform emulation or otherwise interfere with the raw execution
|
||||
of CPU instructions by the application. Therefore, there is no runtime cost
|
||||
imposed for CPU operations.
|
||||
|
||||
{% include graph.html id="sysbench-cpu" url="/performance/sysbench-cpu.csv"
|
||||
title="perf.py sysbench.cpu --runtime=runc --runtime=runsc" %}
|
||||
|
||||
The above figure demonstrates the `sysbench` measurement of CPU events per
|
||||
second. Events per second is based on a CPU-bound loop that calculates all prime
|
||||
numbers in a specified range. We note that `runsc` does not impose a performance
|
||||
penalty, as the code is executing natively in both cases.
|
||||
|
||||
This has important consequences for classes of workloads that are often
|
||||
CPU-bound, such as data processing or machine learning. In these cases, `runsc`
|
||||
will similarly impose minimal runtime overhead.
|
||||
|
||||
{% include graph.html id="tensorflow" url="/performance/tensorflow.csv"
|
||||
title="perf.py tensorflow --runtime=runc --runtime=runsc" %}
|
||||
|
||||
For example, the above figure shows a sample TensorFlow workload, the
|
||||
[convolutional neural network example][cnn]. The time indicated includes the
|
||||
full start-up and run time for the workload, which trains a model.
|
||||
|
||||
## System calls
|
||||
|
||||
Some **structural costs** of gVisor are heavily influenced by the
|
||||
[platform choice](../platforms/), which implements system call interception.
|
||||
Today, gVisor supports a variety of platforms. These platforms present distinct
|
||||
performance, compatibility and security trade-offs. For example, the KVM
|
||||
platform has low overhead system call interception but runs poorly with nested
|
||||
virtualization.
|
||||
|
||||
{% include graph.html id="syscall" url="/performance/syscall.csv" title="perf.py
|
||||
syscall --runtime=runc --runtime=runsc-ptrace --runtime=runsc-kvm" y_min="100"
|
||||
log="true" %}
|
||||
|
||||
The above figure demonstrates the time required for a raw system call on various
|
||||
platforms. The test is implemented by a custom binary which performs a large
|
||||
number of system calls and calculates the average time required.
|
||||
|
||||
This cost will principally impact applications that are system call bound, which
|
||||
tend to be high-performance data stores and static network services. In general,
|
||||
the impact of system call interception will be lower the more work an
|
||||
application does.
|
||||
|
||||
{% include graph.html id="redis" url="/performance/redis.csv" title="perf.py
|
||||
redis --runtime=runc --runtime=runsc" %}
|
||||
|
||||
For example, `redis` is an application that performs relatively little work in
|
||||
userspace: in general it reads from a connected socket, reads or modifies some
|
||||
data, and writes a result back to the socket. The above figure shows the results
|
||||
of running [comprehensive set of benchmarks][redis-benchmark]. We can see that
|
||||
small operations impose a large overhead, while larger operations, such as
|
||||
`LRANGE`, where more work is done in the application, have a smaller relative
|
||||
overhead.
|
||||
|
||||
Some of these costs above are **structural costs**, and `redis` is likely to
|
||||
remain a challenging performance scenario. However, optimizing the
|
||||
[platform](../platforms/) will also have a dramatic impact.
|
||||
|
||||
## Start-up time
|
||||
|
||||
For many use cases, the ability to spin-up containers quickly and efficiently is
|
||||
important. A sandbox may be short-lived and perform minimal user work (e.g. a
|
||||
function invocation).
|
||||
|
||||
{% include graph.html id="startup" url="/performance/startup.csv" title="perf.py
|
||||
startup --runtime=runc --runtime=runsc" %}
|
||||
|
||||
The above figure indicates how total time required to start a container through
|
||||
[Docker][docker]. This benchmark uses three different applications. First, an
|
||||
alpine Linux-container that executes `true`. Second, a `node` application that
|
||||
loads a number of modules and binds an HTTP server. The time is measured by a
|
||||
successful request to the bound port. Finally, a `ruby` application that
|
||||
similarly loads a number of modules and binds an HTTP server.
|
||||
|
||||
> Note: most of the time overhead above is associated Docker itself. This is
|
||||
> evident with the empty `runc` benchmark. To avoid these costs with `runsc`,
|
||||
> you may also consider using `runsc do` mode or invoking the
|
||||
> [OCI runtime](../../user_guide/quick_start/oci/) directly.
|
||||
|
||||
## Network
|
||||
|
||||
Networking is mostly bound by **implementation costs**, and gVisor's network
|
||||
stack is improving quickly.
|
||||
|
||||
While typically not an important metric in practice for common sandbox use
|
||||
cases, nevertheless `iperf` is a common microbenchmark used to measure raw
|
||||
throughput.
|
||||
|
||||
{% include graph.html id="iperf" url="/performance/iperf.csv" title="perf.py
|
||||
iperf --runtime=runc --runtime=runsc" %}
|
||||
|
||||
The above figure shows the result of an `iperf` test between two instances. For
|
||||
the upload case, the specified runtime is used for the `iperf` client, and in
|
||||
the download case, the specified runtime is the server. A native runtime is
|
||||
always used for the other endpoint in the test.
|
||||
|
||||
{% include graph.html id="applications" metric="requests_per_second"
|
||||
url="/performance/applications.csv" title="perf.py http.(node|ruby)
|
||||
--connections=25 --runtime=runc --runtime=runsc" %}
|
||||
|
||||
The above figure shows the result of simple `node` and `ruby` web services that
|
||||
render a template upon receiving a request. Because these synthetic benchmarks
|
||||
do minimal work per request, must like the `redis` case, they suffer from high
|
||||
overheads. In practice, the more work an application does the smaller the impact
|
||||
of **structural costs** become.
|
||||
|
||||
## File system
|
||||
|
||||
Some aspects of file system performance are also reflective of **implementation
|
||||
costs**, and an area where gVisor's implementation is improving quickly.
|
||||
|
||||
In terms of raw disk I/O, gVisor does not introduce significant fundamental
|
||||
overhead. For general file operations, gVisor introduces a small fixed overhead
|
||||
for data that transitions across the sandbox boundary. This manifests as
|
||||
**structural costs** in some cases, since these operations must be routed
|
||||
through the [Gofer](../) as a result of our [security model](../security/), but
|
||||
in most cases are dominated by **implementation costs**, due to an internal
|
||||
[Virtual File System][vfs] (VFS) implementation that needs improvement.
|
||||
|
||||
{% include graph.html id="fio-bw" url="/performance/fio.csv" title="perf.py fio
|
||||
--engine=sync --runtime=runc --runtime=runsc" log="true" %}
|
||||
|
||||
The above figures demonstrate the results of `fio` for reads and writes to and
|
||||
from the disk. In this case, the disk quickly becomes the bottleneck and
|
||||
dominates other costs.
|
||||
|
||||
{% include graph.html id="fio-tmpfs-bw" url="/performance/fio-tmpfs.csv"
|
||||
title="perf.py fio --engine=sync --runtime=runc --tmpfs=True --runtime=runsc"
|
||||
log="true" %}
|
||||
|
||||
The above figure shows the raw I/O performance of using a `tmpfs` mount which is
|
||||
sandbox-internal in the case of `runsc`. Generally these operations are
|
||||
similarly bound to the cost of copying around data in-memory, and we don't see
|
||||
the cost of VFS operations.
|
||||
|
||||
{% include graph.html id="httpd100k" metric="transfer_rate"
|
||||
url="/performance/httpd100k.csv" title="perf.py http.httpd --connections=1
|
||||
--connections=5 --connections=10 --connections=25 --runtime=runc
|
||||
--runtime=runsc" %}
|
||||
|
||||
The high costs of VFS operations can manifest in benchmarks that execute many
|
||||
such operations in the hot path for serving requests, for example. The above
|
||||
figure shows the result of using gVisor to serve small pieces of static content
|
||||
with predictably poor results. This workload represents `apache` serving a
|
||||
single file sized 100k from the container image to a client running
|
||||
[ApacheBench][ab] with varying levels of concurrency. The high overhead comes
|
||||
principally from the VFS implementation that needs improvement, with several
|
||||
internal serialization points (since all requests are reading the same file).
|
||||
Note that some of some of network stack performance issues also impact this
|
||||
benchmark.
|
||||
|
||||
{% include graph.html id="ffmpeg" url="/performance/ffmpeg.csv" title="perf.py
|
||||
media.ffmpeg --runtime=runc --runtime=runsc" %}
|
||||
|
||||
For benchmarks that are bound by raw disk I/O and a mix of compute, file system
|
||||
operations are less of an issue. The above figure shows the total time required
|
||||
for an `ffmpeg` container to start, load and transcode a 27MB input video.
|
||||
|
||||
[ab]: https://en.wikipedia.org/wiki/ApacheBench
|
||||
[benchmark-tools]: https://github.com/google/gvisor/tree/master/benchmarks
|
||||
[gce]: https://cloud.google.com/compute/
|
||||
[cnn]: https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/convolutional_network.py
|
||||
[docker]: https://docker.io
|
||||
[redis-benchmark]: https://redis.io/topics/benchmarks
|
||||
[vfs]: https://en.wikipedia.org/wiki/Virtual_file_system
|
||||
@@ -0,0 +1,86 @@
|
||||
# Platform Guide
|
||||
|
||||
A gVisor sandbox consists of multiple processes when running. These processes
|
||||
collectively comprise a shared environment in which one or more containers can
|
||||
be run.
|
||||
|
||||
Each sandbox has its own isolated instance of:
|
||||
|
||||
* The **Sentry**, A user-space kernel that runs the container and intercepts
|
||||
and responds to system calls made by the application.
|
||||
|
||||
Each container running in the sandbox has its own isolated instance of:
|
||||
|
||||
* A **Gofer** which provides file system access to the container.
|
||||
|
||||

|
||||
|
||||
## runsc
|
||||
|
||||
The entrypoint to running a sandboxed container is the `runsc` executable.
|
||||
`runsc` implements the [Open Container Initiative (OCI)][oci] runtime
|
||||
specification. This means that OCI compatible _filesystem bundles_ can be run by
|
||||
`runsc`. Filesystem bundles are comprised of a `config.json` file containing
|
||||
container configuration, and a root filesystem for the container. Please see the
|
||||
[OCI runtime spec][runtime-spec] for more information on filesystem bundles.
|
||||
`runsc` implements multiple commands that perform various functions such as
|
||||
starting, stopping, listing, and querying the status of containers.
|
||||
|
||||
## Sentry
|
||||
|
||||
The Sentry is the largest component of gVisor. It can be thought of as a
|
||||
userspace OS kernel. The Sentry implements all the kernel functionality needed
|
||||
by the untrusted application. It implements all of the supported system calls,
|
||||
signal delivery, memory management and page faulting logic, the threading model,
|
||||
and more.
|
||||
|
||||
When the untrusted application makes a system call, the currently used platform
|
||||
redirects the call to the Sentry, which will do the necessary work to service
|
||||
it. It is important to note that the Sentry will not simply pass through system
|
||||
calls to the host kernel. As a userspace application, the Sentry will make some
|
||||
host system calls to support its operation, but it will not allow the
|
||||
application to directly control the system calls it makes.
|
||||
|
||||
The Sentry aims to present an equivalent environment to (upstream) Linux v4.4.
|
||||
|
||||
File system operations that extend beyond the sandbox (not internal /proc files,
|
||||
pipes, etc) are sent to the Gofer, described below.
|
||||
|
||||
## Platforms
|
||||
|
||||
gVisor requires a platform to implement interception of syscalls, basic context
|
||||
switching, and memory mapping functionality.
|
||||
|
||||
### ptrace
|
||||
|
||||
The ptrace platform uses `PTRACE_SYSEMU` to execute user code without allowing
|
||||
it to execute host system calls. This platform can run anywhere that ptrace
|
||||
works (even VMs without nested virtualization).
|
||||
|
||||
### KVM (experimental)
|
||||
|
||||
The KVM platform allows the Sentry to act as both guest OS and VMM, switching
|
||||
back and forth between the two worlds seamlessly. The KVM platform can run on
|
||||
bare-metal or in a VM with nested virtualization enabled. While there is no
|
||||
virtualized hardware layer -- the sandbox retains a process model -- gVisor
|
||||
leverages virtualization extensions available on modern processors in order to
|
||||
improve isolation and performance of address space switches.
|
||||
|
||||
## Gofer
|
||||
|
||||
The Gofer is a normal host Linux process. The Gofer is started with each sandbox
|
||||
and connected to the Sentry. The Sentry process is started in a restricted
|
||||
seccomp container without access to file system resources. The Gofer provides
|
||||
the Sentry access to file system resources via the 9P protocol and provides an
|
||||
additional level of isolation.
|
||||
|
||||
## Application
|
||||
|
||||
The application (aka the untrusted application) is a normal Linux binary
|
||||
provided to gVisor in an OCI runtime bundle. gVisor aims to provide an
|
||||
environment equivalent to Linux v4.4, so applications should be able to run
|
||||
unmodified. However, gVisor does not presently implement every system call,
|
||||
/proc file, or /sys file so some incompatibilities may occur.
|
||||
|
||||
[oci]: https://www.opencontainers.org
|
||||
[runtime-spec]: https://github.com/opencontainers/runtime-spec
|
||||
@@ -0,0 +1,143 @@
|
||||
# Resource Model
|
||||
|
||||
[TOC]
|
||||
|
||||
The resource model for gVisor does not assume a fixed number of threads of
|
||||
execution (i.e. vCPUs) or amount of physical memory. Where possible, decisions
|
||||
about underlying physical resources are delegated to the host system, where
|
||||
optimizations can be made with global information. This delegation allows the
|
||||
sandbox to be highly dynamic in terms of resource usage: spanning a large number
|
||||
of cores and large amount of memory when busy, and yielding those resources back
|
||||
to the host when not.
|
||||
|
||||
Some of the details here may depend on the [platform](../platforms/), but in
|
||||
general this page describes the resource model used by gVisor. If you're not
|
||||
familiar with the terms here, uou may want to start with the [Overview](../).
|
||||
|
||||
## Processes
|
||||
|
||||
Much like a Virtual Machine (VM), a gVisor sandbox appears as an opaque process
|
||||
on the system. Processes within the sandbox do not manifest as processes on the
|
||||
host system, and process-level interactions within the sandbox requires entering
|
||||
the sandbox (e.g. via a [Docker exec][exec]).
|
||||
|
||||
## Networking
|
||||
|
||||
Similarly to processes, the sandbox attaches a network endpoint to the system,
|
||||
but runs it's own network stack. All network resources, other than packets in
|
||||
flight, exist only inside the sandbox, bound by relevant resource limits.
|
||||
|
||||
You can interact with network endpoints exposed by the sandbox, just as you
|
||||
would any other container, but network introspection similarly requires entering
|
||||
the sandbox.
|
||||
|
||||
## Files
|
||||
|
||||
Files may be backed by different implementations. For host-native files (where a
|
||||
file descriptor is available), the Gofer may return a file descriptor to the
|
||||
Sentry via [SCM_RIGHTS][scmrights][^1].
|
||||
|
||||
These files may be read from and written to through standard system calls, and
|
||||
also mapped into the associated application's address space. This allows the
|
||||
same host memory to be shared across multiple sandboxes, although this mechanism
|
||||
does not preclude the use of side-channels (see the
|
||||
[security model](../security/)).
|
||||
|
||||
Note that some file systems exist only within the context of the sandbox. For
|
||||
example, in many cases a `tmpfs` mount will be available at `/tmp` or
|
||||
`/dev/shm`, which allocates memory directly from the sandbox memory file (see
|
||||
below). Ultimately, these will be accounted against relevant limits in a similar
|
||||
way as the host native case.
|
||||
|
||||
## Threads
|
||||
|
||||
The Sentry models individual task threads with [goroutines][goroutine]. As a
|
||||
result, each task thread is a lightweight [green thread][greenthread], and may
|
||||
not correspond to an underlying host thread.
|
||||
|
||||
However, application execution is modelled as a blocking system call with the
|
||||
Sentry. This means that additional host threads may be created, *depending on
|
||||
the number of active application threads*. In practice, a busy application will
|
||||
converge on the number of active threads, and the host will be able to make
|
||||
scheduling decisions about all application threads.
|
||||
|
||||
## Time
|
||||
|
||||
Time in the sandbox is provided by the Sentry, through its own [vDSO][vdso] and
|
||||
timekeeping implementation. This is divorced from the host time, and no state is
|
||||
shared with the host, although the time will be initialized with the host clock.
|
||||
|
||||
The Sentry runs timers to note the passage of time, much like a kernel running
|
||||
on hardware (though the timers are software timers, in this case). These timers
|
||||
provide updates to the vDSO, the time returned through system calls, and the
|
||||
time recorded for usage or limit tracking (e.g. [RLIMIT_CPU][rlimit]).
|
||||
|
||||
When all application threads are idle, the Sentry disables timers until an event
|
||||
occurs that wakes either the Sentry or an application thread, similar to a
|
||||
[tickless kernel][tickless]. This allows the Sentry to achieve near zero CPU
|
||||
usage for idle applications.
|
||||
|
||||
## Memory
|
||||
|
||||
The Sentry implements its own memory management, including demand-paging and a
|
||||
Sentry internal page cache for files that cannot be used natively. A single
|
||||
[memfd][memfd] backs all application memory.
|
||||
|
||||
### Address spaces
|
||||
|
||||
The creation of address spaces is platform-specific. For some platforms,
|
||||
additional "stub" processes may be created on the host in order to support
|
||||
additional address spaces. These stubs are subject to various limits applied at
|
||||
the sandbox level (e.g. PID limits).
|
||||
|
||||
### Physical memory
|
||||
|
||||
The host is able to manage physical memory using regular means (e.g. tracking
|
||||
working sets, reclaiming and swapping under pressure). The Sentry lazily
|
||||
populates host mappings for applications, and allow the host to demand-page
|
||||
those regions, which is critical for the functioning of those mechanisms.
|
||||
|
||||
In order to avoid excessive overhead, the Sentry does not demand-page individual
|
||||
pages. Instead, it selects appropriate regions based on heuristics. There is a
|
||||
trade-off here: the Sentry is unable to trivially determine which pages are
|
||||
active and which are not. Even if pages were individually faulted, the host may
|
||||
select pages to be reclaimed or swapped without the Sentry's knowledge.
|
||||
|
||||
Therefore, memory usage statistics within the sandbox (e.g. via `proc`) are
|
||||
approximations. The Sentry maintains an internal breakdown of memory usage, and
|
||||
can collect accurate information but only through a relatively expensive API
|
||||
call. In any case, it would likely be considered unwise to share precise
|
||||
information about how the host is managing memory with the sandbox.
|
||||
|
||||
Finally, when an application marks a region of memory as no longer needed, for
|
||||
example via a call to [madvise][madvise], the Sentry *releases this memory back
|
||||
to the host*. There can be performance penalties for this, since it may be
|
||||
cheaper in many cases to retain the memory and use it to satisfy some other
|
||||
request. However, releasing it immediately to the host allows the host to more
|
||||
effectively multiplex resources and apply an efficient global policy.
|
||||
|
||||
## Limits
|
||||
|
||||
All Sentry threads and Sentry memory are subject to a container cgroup. However,
|
||||
application usage will not appear as anonymous memory usage, and will instead be
|
||||
accounted to the `memfd`. All anonymous memory will correspond to Sentry usage,
|
||||
and host memory charged to the container will work as standard.
|
||||
|
||||
The cgroups can be monitored for standard signals: pressure indicators,
|
||||
threshold notifiers, etc. and can also be adjusted dynamically. Note that the
|
||||
Sentry itself may listen for pressure signals in its containing cgroup, in order
|
||||
to purge internal caches.
|
||||
|
||||
[goroutine]: https://tour.golang.org/concurrency/1
|
||||
[greenthread]: https://en.wikipedia.org/wiki/Green_threads
|
||||
[scheduler]: https://morsmachine.dk/go-scheduler
|
||||
[vdso]: https://en.wikipedia.org/wiki/VDSO
|
||||
[rlimit]: http://man7.org/linux/man-pages/man2/getrlimit.2.html
|
||||
[tickless]: https://en.wikipedia.org/wiki/Tickless_kernel
|
||||
[memfd]: http://man7.org/linux/man-pages/man2/memfd_create.2.html
|
||||
[scmrights]: http://man7.org/linux/man-pages/man7/unix.7.html
|
||||
[madvise]: http://man7.org/linux/man-pages/man2/madvise.2.html
|
||||
[exec]: https://docs.docker.com/engine/reference/commandline/exec/
|
||||
[^1]: Unless host networking is enabled, the Sentry is not able to create or
|
||||
open host file descriptors itself, it can only receive them in this way
|
||||
from the Gofer.
|
||||
@@ -0,0 +1,253 @@
|
||||
# Security Model
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor was created in order to provide additional defense against the
|
||||
exploitation of kernel bugs by untrusted userspace code. In order to understand
|
||||
how gVisor achieves this goal, it is first necessary to understand the basic
|
||||
threat model.
|
||||
|
||||
## Threats: The Anatomy of an Exploit
|
||||
|
||||
An exploit takes advantage of a software or hardware bug in order to escalate
|
||||
privileges, gain access to privileged data, or disrupt services. All of the
|
||||
possible interactions that a malicious application can have with the rest of the
|
||||
system (attack vectors) define the attack surface. We categorize these attack
|
||||
vectors into several common classes.
|
||||
|
||||
### System API
|
||||
|
||||
An operating system or hypervisor exposes an abstract System API in the form of
|
||||
system calls and traps. This API may be documented and stable, as with Linux, or
|
||||
it may be abstracted behind a library, as with Windows (i.e. win32.dll or
|
||||
ntdll.dll). The System API includes all standard interfaces that application
|
||||
code uses to interact with the system. This includes high-level abstractions
|
||||
that are derived from low-level system calls, such as system files, sockets and
|
||||
namespaces.
|
||||
|
||||
Although the System API is exposed to applications by design, bugs and race
|
||||
conditions within the kernel or hypervisor may occasionally be exploitable via
|
||||
the API. This is common in part due to the fact that most kernels and
|
||||
hypervisors are written in [C][clang], which is well-suited to interfacing with
|
||||
hardware but often prone to security issues. In order to exploit these issues, a
|
||||
typical attack might involve some combination of the following:
|
||||
|
||||
1. Opening or creating some combination of files, sockets or other descriptors.
|
||||
1. Passing crafted, malicious arguments, structures or packets.
|
||||
1. Racing with multiple threads in order to hit specific code paths.
|
||||
|
||||
For example, for the [Dirty Cow][dirtycow] privilege escalation bug, an
|
||||
application would open a specific file in `/proc` or use a specific `ptrace`
|
||||
system call, and use multiple threads in order to trigger a race condition when
|
||||
touching a fresh page of memory. The attacker then gains control over a page of
|
||||
memory belonging to the system. With additional privileges or access to
|
||||
privileged data in the kernel, an attacker will often be able to employ
|
||||
additional techniques to gain full access to the rest of the system.
|
||||
|
||||
While bugs in the implementation of the System API are readily fixed, they are
|
||||
also the most common form of exploit. The exposure created by this class of
|
||||
exploit is what gVisor aims to minimize and control, described in detail below.
|
||||
|
||||
### System ABI
|
||||
|
||||
Hardware and software exploits occasionally exist in execution paths that are
|
||||
not part of an intended System API. In this case, exploits may be found as part
|
||||
of implicit actions the hardware or privileged system code takes in response to
|
||||
certain events, such as traps or interrupts. For example, the recent
|
||||
[POPSS][popss] flaw required only native code execution (no specific system call
|
||||
or file access). In that case, the Xen hypervisor was similarly vulnerable,
|
||||
highlighting that hypervisors are not immune to this vector.
|
||||
|
||||
### Side Channels
|
||||
|
||||
Hardware side channels may be exploitable by any code running on a system:
|
||||
native, sandboxed, or virtualized. However, many host-level mitigations against
|
||||
hardware side channels are still effective with a sandbox. For example, kernels
|
||||
built with retpoline protect against some speculative execution attacks
|
||||
(Spectre) and frame poisoning may protect against L1 terminal fault (L1TF)
|
||||
attacks. Hypervisors may introduce additional complications in this regard, as
|
||||
there is no mitigation against an application in a normally functioning Virtual
|
||||
Machine (VM) exploiting the L1TF vulnerability for another VM on the sibling
|
||||
hyperthread.
|
||||
|
||||
### Other Vectors
|
||||
|
||||
The above categories in no way represent an exhaustive list of exploits, as we
|
||||
focus only on running untrusted code from within the operating system or
|
||||
hypervisor. We do not consider other ways that a more generic adversary may
|
||||
interact with a system, such as inserting a portable storage device with a
|
||||
malicious filesystem image, using a combination of crafted keyboard or touch
|
||||
inputs, or saturating a network device with ill-formed packets.
|
||||
|
||||
Furthermore, high-level systems may contain exploitable components. An attacker
|
||||
need not escalate privileges within a container if there’s an exploitable
|
||||
network-accessible service on the host or some other API path. *A sandbox is not
|
||||
a substitute for a secure architecture*.
|
||||
|
||||
## Goals: Limiting Exposure
|
||||
|
||||
gVisor’s primary design goal is to minimize the System API attack vector while
|
||||
still providing a process model. There are two primary security principles that
|
||||
inform this design. First, the application’s direct interactions with the host
|
||||
System API are intercepted by the Sentry, which implements the System API
|
||||
instead. Second, the System API accessible to the Sentry itself is minimized to
|
||||
a safer, restricted set. The first principle minimizes the possibility of direct
|
||||
exploitation of the host System API by applications, and the second principle
|
||||
minimizes indirect exploitability, which is the exploitation by an exploited or
|
||||
buggy Sentry (e.g. chaining an exploit).
|
||||
|
||||
The first principle is similar to the security basis for a Virtual Machine (VM).
|
||||
With a VM, an application’s interactions with the host are replaced by
|
||||
interactions with a guest operating system and a set of virtualized hardware
|
||||
devices. These hardware devices are then implemented via the host System API by
|
||||
a Virtual Machine Monitor (VMM). The Sentry similarly prevents direct
|
||||
interactions by providing its own implementation of the System API that the
|
||||
application must interact with. Applications are not able to to directly craft
|
||||
specific arguments or flags for the host System API, or interact directly with
|
||||
host primitives.
|
||||
|
||||
For both the Sentry and a VMM, it’s worth noting that while direct interactions
|
||||
are not possible, indirect interactions are still possible. For example, a read
|
||||
on a host-backed file in the Sentry may ultimately result in a host read system
|
||||
call (made by the Sentry, not by passing through arguments from the
|
||||
application), similar to how a read on a block device in a VM may result in the
|
||||
VMM issuing a corresponding host read system call from a backing file.
|
||||
|
||||
An important distinction from a VM is that the Sentry implements a System API
|
||||
based directly on host System API primitives instead of relying on virtualized
|
||||
hardware and a guest operating system. This selects a distinct set of
|
||||
trade-offs, largely in the performance, efficiency and compatibility domains.
|
||||
Since transitions in and out of the sandbox are relatively expensive, a guest
|
||||
operating system will typically take ownership of resources. For example, in the
|
||||
above case, the guest operating system may read the block device data in a local
|
||||
page cache, to avoid subsequent reads. This may lead to better performance but
|
||||
lower efficiency, since memory may be wasted or duplicated. The Sentry opts
|
||||
instead to defer to the host for many operations during runtime, for improved
|
||||
efficiency but lower performance in some use cases.
|
||||
|
||||
### What can a sandbox do?
|
||||
|
||||
An application in a gVisor sandbox is permitted to do most things a standard
|
||||
container can do: for example, applications can read and write files mapped
|
||||
within the container, make network connections, etc. As described above,
|
||||
gVisor's primary goal is to limit exposure to bugs and exploits while still
|
||||
allowing most applications to run. Even so, gVisor will limit some operations
|
||||
that might be permitted with a standard container. Even with appropriate
|
||||
capabilities, a user in a gVisor sandbox will only be able to manipulate
|
||||
virtualized system resources (e.g. the system time, kernel settings or
|
||||
filesystem attributes) and not underlying host system resources.
|
||||
|
||||
While the sandbox virtualizes many operations for the application, we limit the
|
||||
sandbox's own interactions with the host to the following high-level operations:
|
||||
|
||||
1. Communicate with a Gofer process via a connected socket. The sandbox may
|
||||
receive new file descriptors from the Gofer process, corresponding to opened
|
||||
files. These files can then be read from and written to by the sandbox.
|
||||
1. Make a minimal set of host system calls. The calls do not include the
|
||||
creation of new sockets (unless host networking mode is enabled) or opening
|
||||
files. The calls include duplication and closing of file descriptors,
|
||||
synchronization, timers and signal management.
|
||||
1. Read and write packets to a virtual ethernet device. This is not required if
|
||||
host networking is enabled (or networking is disabled).
|
||||
|
||||
### System ABI, Side Channels and Other Vectors
|
||||
|
||||
gVisor relies on the host operating system and the platform for defense against
|
||||
hardware-based attacks. Given the nature of these vulnerabilities, there is
|
||||
little defense that gVisor can provide (there’s no guarantee that additional
|
||||
hardware measures, such as virtualization, memory encryption, etc. would
|
||||
actually decrease the attack surface). Note that this is true even when using
|
||||
hardware virtualization for acceleration, as the host kernel or hypervisor is
|
||||
ultimately responsible for defending against attacks from within malicious
|
||||
guests.
|
||||
|
||||
gVisor similarly relies on the host resource mechanisms (cgroups) for defense
|
||||
against resource exhaustion and denial of service attacks. Network policy
|
||||
controls should be applied at the container level to ensure appropriate network
|
||||
policy enforcement. Note that the sandbox itself is not capable of altering or
|
||||
configuring these mechanisms, and the sandbox itself should make an attacker
|
||||
less likely to exploit or override these controls through other means.
|
||||
|
||||
## Principles: Defense-in-Depth
|
||||
|
||||
For gVisor development, there are several engineering principles that are
|
||||
employed in order to ensure that the system meets its design goals.
|
||||
|
||||
1. No system call is passed through directly to the host. Every supported call
|
||||
has an independent implementation in the Sentry, that is unlikely to suffer
|
||||
from identical vulnerabilities that may appear in the host. This has the
|
||||
consequence that all kernel features used by applications require an
|
||||
implementation within the Sentry.
|
||||
1. Only common, universal functionality is implemented. Some filesystems,
|
||||
network devices or modules may expose specialized functionality to user
|
||||
space applications via mechanisms such as extended attributes, raw sockets
|
||||
or ioctls. Since the Sentry is responsible for implementing the full system
|
||||
call surface, we do not implement or pass through these specialized APIs.
|
||||
1. The host surface exposed to the Sentry is minimized. While the system call
|
||||
surface is not trivial, it is explicitly enumerated and controlled. The
|
||||
Sentry is not permitted to open new files, create new sockets or do many
|
||||
other interesting things on the host.
|
||||
|
||||
Additionally, we have practical restrictions that are imposed on the project to
|
||||
minimize the risk of Sentry exploitability. For example:
|
||||
|
||||
1. Unsafe code is carefully controlled. All unsafe code is isolated in files
|
||||
that end with "unsafe.go", in order to facilitate validation and auditing.
|
||||
No file without the unsafe suffix may import the unsafe package.
|
||||
1. No CGo is allowed. The Sentry must be a pure Go binary.
|
||||
1. External imports are not generally allowed within the core packages. Only
|
||||
limited external imports are used within the setup code. The code available
|
||||
inside the Sentry is carefully controlled, to ensure that the above rules
|
||||
are effective.
|
||||
|
||||
Finally, we recognize that security is a process, and that vigilance is
|
||||
critical. Beyond our security disclosure process, the Sentry is fuzzed
|
||||
continuously to identify potential bugs and races proactively, and production
|
||||
crashes are recorded and triaged to similarly identify material issues.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Is this more or less secure than a Virtual Machine?
|
||||
|
||||
The security of a VM depends to a large extent on what is exposed from the host
|
||||
kernel and user space support code. For example, device emulation code in the
|
||||
host kernel (e.g. APIC) or optimizations (e.g. vhost) can be more complex than a
|
||||
simple system call, and exploits carry the same risks. Similarly, the user space
|
||||
support code is frequently unsandboxed, and exploits, while rare, may allow
|
||||
unfettered access to the system.
|
||||
|
||||
Some platforms leverage the same virtualization hardware as VMs in order to
|
||||
provide better system call interception performance. However, gVisor does not
|
||||
implement any device emulation, and instead opts to use a sandboxed host System
|
||||
API directly. Both approaches significantly reduce the original attack surface.
|
||||
Ultimately, since gVisor is capable of using the same hardware mechanism, one
|
||||
should not assume that the mere use of virtualization hardware makes a system
|
||||
more or less secure, just as it would be a mistake to make the claim that the
|
||||
use of a unibody alone makes a car safe.
|
||||
|
||||
### Does this stop hardware side channels?
|
||||
|
||||
In general, gVisor does not provide protection against hardware side channels,
|
||||
although it may make exploits that rely on direct access to the host System API
|
||||
more difficult to use. To minimize exposure, you should follow relevant guidance
|
||||
from vendors and keep your host kernel and firmware up-to-date.
|
||||
|
||||
### Is this just a ptrace sandbox?
|
||||
|
||||
No: the term “ptrace sandbox” generally refers to software that uses the Linux
|
||||
ptrace facility to inspect and authorize system calls made by applications,
|
||||
enforcing a specific policy. These commonly suffer from two issues. First,
|
||||
vulnerable system calls may be authorized by the sandbox, as the application
|
||||
still has direct access to some System API. Second, it’s impossible to avoid
|
||||
time-of-check, time-of-use race conditions without disabling multi-threading.
|
||||
|
||||
In gVisor, the platforms that use ptrace operate differently. The stubs that are
|
||||
traced are never allowed to continue execution into the host kernel and complete
|
||||
a call directly. Instead, all system calls are interpreted and handled by the
|
||||
Sentry itself, who reflects resulting register state back into the tracee before
|
||||
continuing execution in user space. This is very similar to the mechanism used
|
||||
by User-Mode Linux (UML).
|
||||
|
||||
[dirtycow]: https://en.wikipedia.org/wiki/Dirty_COW
|
||||
[clang]: https://en.wikipedia.org/wiki/C_(programming_language)
|
||||
[popss]: https://nvd.nist.gov/vuln/detail/CVE-2018-8897
|
||||
@@ -0,0 +1,31 @@
|
||||
# Participation
|
||||
|
||||
To contribute code, please read the [contributing guide](../CONTRIBUTING.md).
|
||||
|
||||
Please note that the [Code of Conduct](../CODE_OF_CONDUCT.md) applies to
|
||||
community forums as well as technical participation.
|
||||
|
||||
## Communication channels
|
||||
|
||||
The project maintains two mailing lists:
|
||||
|
||||
* [gvisor-users][gvisor-users] for accouncements and general discussion.
|
||||
* [gvisor-dev][gvisor-dev] for development and contribution.
|
||||
|
||||
We also have a [chat room hosted on Gitter][gitter-chat].
|
||||
|
||||
We'd love to hear from you!
|
||||
|
||||
## Community meetings
|
||||
|
||||
The community calendar shows upcoming public meetings and opportunities to
|
||||
collaborate or discuss the project. Meetings are planned and announced ahead of
|
||||
time via the [gvisor-users][gvisor-users] mailing list.
|
||||
|
||||
These meetings are public: anyone can join.
|
||||
|
||||
<iframe src="https://calendar.google.com/calendar/b/1/embed?showTitle=0&height=600&wkst=1&bgcolor=%23FFFFFF&src=bd6f4k210u3ukmlj9b8vl053fk%40group.calendar.google.com&color=%23AB8B00&ctz=America%2FLos_Angeles" style="border-width:0" width="600" height="400" frameborder="0" scrolling="no"></iframe>
|
||||
|
||||
[gitter-chat]: https://gitter.im/gvisor/community
|
||||
[gvisor-dev]: https://groups.google.com/forum/#!forum/gvisor-dev
|
||||
[gvisor-users]: https://groups.google.com/forum/#!forum/gvisor-users
|
||||
@@ -0,0 +1 @@
|
||||
The gVisor logo files are licensed under CC BY-SA 4.0 (Creative Commons Attribution-ShareAlike 4.0 International).
|
||||
@@ -0,0 +1,49 @@
|
||||
# Roadmap
|
||||
|
||||
gVisor [GitHub Issues][issues] serve as the source-of-truth for most work in
|
||||
flight. Specific performance and compatibility issues are generally tracked
|
||||
there. [GitHub Milestones][milestones] may be used to track larger features that
|
||||
span many issues. However, labels are also used to aggregate cross-cutting
|
||||
feature work.
|
||||
|
||||
## Core Improvements
|
||||
|
||||
Most gVisor work is focused on four areas.
|
||||
|
||||
* [Performance][performance]: overall sandbox performance, including platform
|
||||
performance, is a critical area for investment. This includes: network
|
||||
performance (throughput and latency), file system performance (metadata and
|
||||
data I/O), application switch and fault costs, etc. The goal of gVisor is to
|
||||
provide sandboxing without a material performance or efficiency impact on
|
||||
all but the most performance-sensitive applications.
|
||||
|
||||
* [Compatibility][compatibility]: supporting a wide range of applications
|
||||
requires supporting a large system API, including special system files (e.g.
|
||||
proc, sys, dev, etc.). The goal of gVisor is to support the broad set of
|
||||
applications that depend on a generic Linux API, rather than a specific
|
||||
kernel version.
|
||||
|
||||
* [Infrastructure & tooling][infrastructure]: the above goals require
|
||||
aggressive testing and coverage, and well-established processes. This
|
||||
includes adding appropriate system call coverage, end-to-end suites and
|
||||
runtime tests.
|
||||
|
||||
* [Integration][integration]: Container infrastructure is evolving rapidly and
|
||||
becoming more complex, and gVisor must continuously implement relevant and
|
||||
popular features to ensure that integration points remain robust and
|
||||
feature-complete while preserving security guarantees.
|
||||
|
||||
## Releases
|
||||
|
||||
Releases are available on [GitHub][releases].
|
||||
|
||||
As a convenience, binary packages are also published. Instructions for their use
|
||||
are available via the [Installation instructions](./user_guide/install.md).
|
||||
|
||||
[issues]: https://github.com/google/gvisor/issues
|
||||
[milestones]: https://github.com/google/gvisor/milestones
|
||||
[releases]: https://github.com/google/gvisor/releases
|
||||
[performance]: https://github.com/google/gvisor/issues?q=is%3Aopen+is%3Aissue+label%3A%22area%3A+performance%22
|
||||
[integration]: https://github.com/google/gvisor/issues?q=is%3Aopen+is%3Aissue+label%3A%22area%3A+integration%22
|
||||
[compatibility]: https://github.com/google/gvisor/issues?q=is%3Aopen+is%3Aissue+label%3A%22area%3A+compatibility%22
|
||||
[infrastructure]: https://github.com/google/gvisor/issues?q=is%3Aopen+is%3Aissue+label%3A%22area%3A+tooling%22
|
||||
@@ -0,0 +1,70 @@
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(
|
||||
default_visibility = ["//website:__pkg__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "compatibility",
|
||||
src = "compatibility.md",
|
||||
category = "Compatibility",
|
||||
permalink = "/docs/user_guide/compatibility/",
|
||||
weight = "0",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "checkpoint_restore",
|
||||
src = "checkpoint_restore.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/checkpoint_restore/",
|
||||
weight = "60",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "debugging",
|
||||
src = "debugging.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/debugging/",
|
||||
weight = "70",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "FAQ",
|
||||
src = "FAQ.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/FAQ/",
|
||||
weight = "90",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "filesystem",
|
||||
src = "filesystem.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/filesystem/",
|
||||
weight = "40",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "networking",
|
||||
src = "networking.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/networking/",
|
||||
weight = "50",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "install",
|
||||
src = "install.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/install/",
|
||||
weight = "10",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "platforms",
|
||||
src = "platforms.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/platforms/",
|
||||
weight = "30",
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
# FAQ
|
||||
|
||||
[TOC]
|
||||
|
||||
### What operating systems are supported? {#supported-os}
|
||||
|
||||
Today, gVisor requires Linux.
|
||||
|
||||
### What CPU architectures are supported? {#supported-cpus}
|
||||
|
||||
gVisor currently supports [x86_64/AMD64](https://en.wikipedia.org/wiki/X86-64)
|
||||
compatible processors. Preliminary support is also available for
|
||||
[ARM64](https://en.wikipedia.org/wiki/ARM_architecture#AArch64).
|
||||
|
||||
### Do I need to modify my Linux application to use gVisor? {#modify-app}
|
||||
|
||||
No. gVisor is capable of running unmodified Linux binaries.
|
||||
|
||||
### What binary formats does gVisor support? {#supported-binaries}
|
||||
|
||||
gVisor supports Linux
|
||||
[ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) binaries.
|
||||
|
||||
Binaries run in gVisor should be built for the
|
||||
[AMD64](https://en.wikipedia.org/wiki/X86-64) or
|
||||
[AArch64](https://en.wikipedia.org/wiki/ARM_architecture#AArch64) CPU
|
||||
architectures.
|
||||
|
||||
### Can I run Docker images using gVisor? {#docker-images}
|
||||
|
||||
Yes. Please see the [Docker Quick Start][docker].
|
||||
|
||||
### Can I run Kubernetes pods using gVisor? {#k8s-pods}
|
||||
|
||||
Yes. Please see the [Kubernetes Quick Start][k8s].
|
||||
|
||||
### What's the security model? {#security-model}
|
||||
|
||||
See the [Security Model][security-model].
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### My container runs fine with `runc` but fails with `runsc` {#app-compatibility}
|
||||
|
||||
If you’re having problems running a container with `runsc` it’s most likely due
|
||||
to a compatibility issue or a missing feature in gVisor. See
|
||||
[Debugging][debugging].
|
||||
|
||||
### When I run my container, docker fails with: `open /run/containerd/.../<containerid>/log.json: no such file or directory` {#memfd-create}
|
||||
|
||||
You are using an older version of Linux which doesn't support `memfd_create`.
|
||||
|
||||
This is tracked in [bug #268](https://gvisor.dev/issue/268).
|
||||
|
||||
### When I run my container, docker fails with: `flag provided but not defined: -console` {#old-docker}
|
||||
|
||||
You're using an old version of Docker. See [Docker Quick Start][docker].
|
||||
|
||||
### I can’t see a file copied with: `docker cp` {#fs-cache}
|
||||
|
||||
For performance reasons, gVisor caches directory contents, and therefore it may
|
||||
not realize a new file was copied to a given directory. To invalidate the cache
|
||||
and force a refresh, create a file under the directory in question and list the
|
||||
contents again.
|
||||
|
||||
As a workaround, shared root filesystem can be enabled. See
|
||||
[Filesystem][filesystem].
|
||||
|
||||
This bug is tracked in [bug #4](https://gvisor.dev/issue/4).
|
||||
|
||||
Note that `kubectl cp` works because it does the copy by exec'ing inside the
|
||||
sandbox, and thus gVisor's internal cache is made aware of the new files and
|
||||
directories.
|
||||
|
||||
### I'm getting an error like: `panic: unable to attach: operation not permitted` or `fork/exec /proc/self/exe: invalid argument: unknown` {#runsc-perms}
|
||||
|
||||
Make sure that permissions and the owner is correct on the `runsc` binary.
|
||||
|
||||
```bash
|
||||
sudo chown root:root /usr/local/bin/runsc
|
||||
sudo chmod 0755 /usr/local/bin/runsc
|
||||
```
|
||||
|
||||
### I'm getting an error like `mount submount "/etc/hostname": creating mount with source ".../hostname": input/output error: unknown.` {#memlock}
|
||||
|
||||
There is a bug in Linux kernel versions 5.1 to 5.3.15, 5.4.2, and 5.5. Upgrade
|
||||
to a newer kernel or add the following to
|
||||
`/lib/systemd/system/containerd.service` as a workaround.
|
||||
|
||||
```
|
||||
LimitMEMLOCK=infinity
|
||||
```
|
||||
|
||||
And run `systemctl daemon-reload && systemctl restart containerd` to restart
|
||||
containerd.
|
||||
|
||||
See [issue #1765](https://gvisor.dev/issue/1765) for more details.
|
||||
|
||||
### My container cannot resolve another container's name when using Docker user defined bridge {#docker-bridge}
|
||||
|
||||
This is normally indicated by errors like `bad address 'container-name'` when
|
||||
trying to communicate to another container in the same network.
|
||||
|
||||
Docker user defined bridge uses an embedded DNS server bound to the loopback
|
||||
interface on address 127.0.0.10. This requires access to the host network in
|
||||
order to communicate to the DNS server. runsc network is isolated from the host
|
||||
and cannot access the DNS server on the host network without breaking the
|
||||
sandbox isolation. There are a few different workarounds you can try:
|
||||
|
||||
* Use default bridge network with `--link` to connect containers. Default
|
||||
bridge doesn't use embedded DNS.
|
||||
* Use [`--network=host`][host-net] option in runsc, however beware that it
|
||||
will use the host network stack and is less secure.
|
||||
* Use IPs instead of container names.
|
||||
* Use [Kubernetes][k8s]. Container name lookup works fine in Kubernetes.
|
||||
|
||||
[security-model]: /docs/architecture_guide/security/
|
||||
[host-net]: /docs/user_guide/networking/#network-passthrough
|
||||
[debugging]: /docs/user_guide/debugging/
|
||||
[filesystem]: /docs/user_guide/filesystem/
|
||||
[docker]: /docs/user_guide/quick_start/docker/
|
||||
[k8s]: /docs/user_guide/quick_start/kubernetes/
|
||||
@@ -0,0 +1,101 @@
|
||||
# Checkpoint/Restore
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor has the ability to checkpoint a process, save its current state in a
|
||||
state file, and restore into a new container using the state file.
|
||||
|
||||
## How to use checkpoint/restore
|
||||
|
||||
Checkpoint/restore functionality is currently available via raw `runsc`
|
||||
commands. To use the checkpoint command, first run a container.
|
||||
|
||||
```bash
|
||||
runsc run <container id>
|
||||
```
|
||||
|
||||
To checkpoint the container, the `--image-path` flag must be provided. This is
|
||||
the directory path within which the checkpoint state-file will be created. The
|
||||
file will be called `checkpoint.img` and necessary directories will be created
|
||||
if they do not yet exist.
|
||||
|
||||
> Note: Two checkpoints cannot be saved to the same directory; every image-path
|
||||
> provided must be unique.
|
||||
|
||||
```bash
|
||||
runsc checkpoint --image-path=<path> <container id>
|
||||
```
|
||||
|
||||
There is also an optional `--leave-running` flag that allows the container to
|
||||
continue to run after the checkpoint has been made. (By default, containers stop
|
||||
their processes after committing a checkpoint.)
|
||||
|
||||
> Note: All top-level runsc flags needed when calling run must be provided to
|
||||
> checkpoint if --leave-running is used.
|
||||
|
||||
> Note: --leave-running functions by causing an immediate restore so the
|
||||
> container, although will maintain its given container id, may have a different
|
||||
> process id.
|
||||
|
||||
```bash
|
||||
runsc checkpoint --image-path=<path> --leave-running <container id>
|
||||
```
|
||||
|
||||
To restore, provide the image path to the `checkpoint.img` file created during
|
||||
the checkpoint. Because containers stop by default after checkpointing, restore
|
||||
needs to happen in a new container (restore is a command which parallels start).
|
||||
|
||||
```bash
|
||||
runsc create <container id>
|
||||
|
||||
runsc restore --image-path=<path> <container id>
|
||||
```
|
||||
|
||||
## How to use checkpoint/restore in Docker:
|
||||
|
||||
Currently checkpoint/restore through `runsc` is not entirely compatible with
|
||||
Docker, although there has been progress made from both gVisor and Docker to
|
||||
enable compatibility. Here, we document the ideal workflow.
|
||||
|
||||
Run a container:
|
||||
|
||||
```bash
|
||||
docker run [options] --runtime=runsc <image>`
|
||||
```
|
||||
|
||||
Checkpoint a container:
|
||||
|
||||
```bash
|
||||
docker checkpoint create <container> <checkpoint_name>`
|
||||
```
|
||||
|
||||
Create a new container into which to restore:
|
||||
|
||||
```bash
|
||||
docker create [options] --runtime=runsc <image>
|
||||
```
|
||||
|
||||
Restore a container:
|
||||
|
||||
```bash
|
||||
docker start --checkpoint --checkpoint-dir=<directory> <container>
|
||||
```
|
||||
|
||||
### Issues Preventing Compatibility with Docker
|
||||
|
||||
- **[Moby #37360][leave-running]:** Docker version 18.03.0-ce and earlier
|
||||
hangs when checkpointing and does not create the checkpoint. To successfully
|
||||
use this feature, install a custom version of docker-ce from the moby
|
||||
repository. This issue is caused by an improper implementation of the
|
||||
`--leave-running` flag. This issue is fixed in newer releases.
|
||||
- **Docker does not support restoration into new containers:** Docker
|
||||
currently expects the container which created the checkpoint to be the same
|
||||
container used to restore which is not possible in runsc. When Docker
|
||||
supports container migration and therefore restoration into new containers,
|
||||
this will be the flow.
|
||||
- **[Moby #37344][checkpoint-dir]:** Docker does not currently support the
|
||||
`--checkpoint-dir` flag but this will be required when restoring from a
|
||||
checkpoint made in another container.
|
||||
|
||||
[leave-running]: https://github.com/moby/moby/pull/37360
|
||||
[checkpoint-dir]: https://github.com/moby/moby/issues/37344
|
||||
@@ -0,0 +1,93 @@
|
||||
# Applications
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor implements a large portion of the Linux surface and while we strive to
|
||||
make it broadly compatible, there are (and always will be) unimplemented
|
||||
features and bugs. The only real way to know if it will work is to try. If you
|
||||
find a container that doesn’t work and there is no known issue, please
|
||||
[file a bug][bug] indicating the full command you used to run the image. You can
|
||||
view open issues related to compatibility [here][issues].
|
||||
|
||||
If you're able to provide the [debug logs](../debugging/), the problem likely to
|
||||
be fixed much faster.
|
||||
|
||||
## What works?
|
||||
|
||||
The following applications/images have been tested:
|
||||
|
||||
* elasticsearch
|
||||
* golang
|
||||
* httpd
|
||||
* java8
|
||||
* jenkins
|
||||
* mariadb
|
||||
* memcached
|
||||
* mongo
|
||||
* mysql
|
||||
* nginx
|
||||
* node
|
||||
* php
|
||||
* postgres
|
||||
* prometheus
|
||||
* python
|
||||
* redis
|
||||
* registry
|
||||
* tomcat
|
||||
* wordpress
|
||||
|
||||
## Utilities
|
||||
|
||||
Most common utilities work. Note that:
|
||||
|
||||
* Some tools, such as `tcpdump` and old versions of `ping`, require explicitly
|
||||
enabling raw sockets via the unsafe `--net-raw` runsc flag.
|
||||
* Different Docker images can behave differently. For example, Alpine Linux
|
||||
and Ubuntu have different `ip` binaries.
|
||||
|
||||
Specific tools include:
|
||||
|
||||
<!-- mdformat off(don't wrap the table) -->
|
||||
|
||||
| Tool | Status |
|
||||
|:--------:|:-----------------------------------------:|
|
||||
| apt-get | Working. |
|
||||
| bundle | Working. |
|
||||
| cat | Working. |
|
||||
| curl | Working. |
|
||||
| dd | Working. |
|
||||
| df | Working. |
|
||||
| dig | Working. |
|
||||
| drill | Working. |
|
||||
| env | Working. |
|
||||
| find | Working. |
|
||||
| gdb | Working. |
|
||||
| gosu | Working. |
|
||||
| grep | Working (unless stdin is a pipe and stdout is /dev/null). |
|
||||
| ifconfig | Works partially, like ip. Full support [in progress](https://gvisor.dev/issue/578). |
|
||||
| ip | Some subcommands work (e.g. addr, route). Full support [in progress](https://gvisor.dev/issue/578). |
|
||||
| less | Working. |
|
||||
| ls | Working. |
|
||||
| lsof | Working. |
|
||||
| mount | Works in readonly mode. gVisor doesn't currently support creating new mounts at runtime. |
|
||||
| nc | Working. |
|
||||
| nmap | Not working. |
|
||||
| netstat | [In progress](https://gvisor.dev/issue/2112). |
|
||||
| nslookup | Working. |
|
||||
| ping | Working. |
|
||||
| ps | Working. |
|
||||
| route | Working. |
|
||||
| ss | [In progress](https://gvisor.dev/issue/2114). |
|
||||
| sshd | Partially working. Job control [in progress](https://gvisor.dev/issue/154). |
|
||||
| strace | Working. |
|
||||
| tar | Working. |
|
||||
| tcpdump | [In progress](https://gvisor.dev/issue/173). |
|
||||
| top | Working. |
|
||||
| uptime | Working. |
|
||||
| vim | Working. |
|
||||
| wget | Working. |
|
||||
|
||||
<!-- mdformat on -->
|
||||
|
||||
[bug]: https://github.com/google/gvisor/issues/new?title=Compatibility%20Issue:
|
||||
[issues]: https://github.com/google/gvisor/issues?q=is%3Aissue+is%3Aopen+label%3A%22area%3A+compatibility%22
|
||||
@@ -0,0 +1,131 @@
|
||||
# Debugging
|
||||
|
||||
[TOC]
|
||||
|
||||
To enable debug and system call logging, add the `runtimeArgs` below to your
|
||||
[Docker](../quick_start/docker/) configuration (`/etc/docker/daemon.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--debug-log=/tmp/runsc/",
|
||||
"--debug",
|
||||
"--strace"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Note: the last `/` in `--debug-log` is needed to interpret it as a directory.
|
||||
> Then each `runsc` command executed will create a separate log file. Otherwise,
|
||||
> log messages from all commands will be appended to the same file.
|
||||
|
||||
You may also want to pass `--log-packets` to troubleshoot network problems. Then
|
||||
restart the Docker daemon:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
Run your container again, and inspect the files under `/tmp/runsc`. The log file
|
||||
ending with `.boot` will contain the strace logs from your application, which
|
||||
can be useful for identifying missing or broken system calls in gVisor. If you
|
||||
are having problems starting the container, the log file ending with `.create`
|
||||
may have the reason for the failure.
|
||||
|
||||
## Stack traces
|
||||
|
||||
The command `runsc debug --stacks` collects stack traces while the sandbox is
|
||||
running which can be useful to troubleshoot issues or just to learn more about
|
||||
gVisor. It connects to the sandbox process, collects a stack dump, and writes it
|
||||
to the console. For example:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --rm -d alpine sh -c "while true; do echo running; sleep 1; done"
|
||||
63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b
|
||||
|
||||
sudo runsc --root /var/run/docker/runtime-runsc/moby debug --stacks 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b
|
||||
```
|
||||
|
||||
> Note: `--root` variable is provided by docker and is normally set to
|
||||
> `/var/run/docker/runtime-[runtime-name]/moby`. If in doubt, `--root` is logged
|
||||
> to `runsc` logs.
|
||||
|
||||
## Debugger
|
||||
|
||||
You can debug gVisor like any other Golang program. If you're running with
|
||||
Docker, you'll need to find the sandbox PID and attach the debugger as root.
|
||||
Here is an example:
|
||||
|
||||
```bash
|
||||
# Get a runsc with debug symbols (download nightly or build with symbols).
|
||||
bazel build -c dbg //runsc:runsc
|
||||
|
||||
# Start the container you want to debug.
|
||||
docker run --runtime=runsc --rm --name=test -d alpine sleep 1000
|
||||
|
||||
# Find the sandbox PID.
|
||||
docker inspect test | grep Pid | head -n 1
|
||||
|
||||
# Attach your favorite debugger.
|
||||
sudo dlv attach <PID>
|
||||
|
||||
# Set a breakpoint and resume.
|
||||
break mm.MemoryManager.MMap
|
||||
continue
|
||||
```
|
||||
|
||||
## Profiling
|
||||
|
||||
`runsc` integrates with Go profiling tools and gives you easy commands to
|
||||
profile CPU and heap usage. First you need to enable `--profile` in the command
|
||||
line options before starting the container:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc-prof": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--profile"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Note: Enabling profiling loosens the seccomp protection added to the sandbox,
|
||||
> and should not be run in production under normal circumstances.
|
||||
|
||||
Then restart docker to refresh the runtime options. While the container is
|
||||
running, execute `runsc debug` to collect profile information and save to a
|
||||
file. Here are the options available:
|
||||
|
||||
* **--profile-heap:** Generates heap profile to the speficied file.
|
||||
* **--profile-cpu:** Enables CPU profiler, waits for `--duration` seconds and
|
||||
generates CPU profile to the speficied file.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc-prof --rm -d alpine sh -c "while true; do echo running; sleep 1; done"
|
||||
63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b
|
||||
|
||||
sudo runsc --root /var/run/docker/runtime-runsc-prof/moby debug --profile-heap=/tmp/heap.prof 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b
|
||||
sudo runsc --root /var/run/docker/runtime-runsc-prof/moby debug --profile-cpu=/tmp/cpu.prof --duration=30s 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b
|
||||
```
|
||||
|
||||
The resulting files can be opened using `go tool pprof` or [pprof][]. The
|
||||
examples below create image file (`.svg`) with the heap profile and writes the
|
||||
top functions using CPU to the console:
|
||||
|
||||
```bash
|
||||
go tool pprof -svg /usr/local/bin/runsc /tmp/heap.prof
|
||||
go tool pprof -top /usr/local/bin/runsc /tmp/cpu.prof
|
||||
```
|
||||
|
||||
[pprof]: https://github.com/google/pprof/blob/master/doc/README.md
|
||||
@@ -0,0 +1,60 @@
|
||||
# Filesystem
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor accesses the filesystem through a file proxy, called the Gofer. The gofer
|
||||
runs as a separate process, that is isolated from the sandbox. Gofer instances
|
||||
communicate with their respective sentry using the 9P protocol. For a more
|
||||
detailed explanation see [Overview > Gofer](../../architecture_guide/#gofer).
|
||||
|
||||
## Sandbox overlay
|
||||
|
||||
To isolate the host filesystem from the sandbox, you can set a writable tmpfs
|
||||
overlay on top of the entire filesystem. All modifications are made to the
|
||||
overlay, keeping the host filesystem unmodified.
|
||||
|
||||
> Note: All created and modified files are stored in memory inside the sandbox.
|
||||
|
||||
To use the tmpfs overlay, add the following `runtimeArgs` to your Docker
|
||||
configuration (`/etc/docker/daemon.json`) and restart the Docker daemon:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--overlay"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Shared root filesystem
|
||||
|
||||
The root filesystem is where the image is extracted and is not generally
|
||||
modified from outside the sandbox. This allows for some optimizations, like
|
||||
skipping checks to determine if a directory has changed since the last time it
|
||||
was cached, thus missing updates that may have happened. If you need to `docker
|
||||
cp` files inside the root filesystem, you may want to enable shared mode. Just
|
||||
be aware that file system access will be slower due to the extra checks that are
|
||||
required.
|
||||
|
||||
> Note: External mounts are always shared.
|
||||
|
||||
To use set the root filesystem shared, add the following `runtimeArgs` to your
|
||||
Docker configuration (`/etc/docker/daemon.json`) and restart the Docker daemon:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--file-access=shared"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,160 @@
|
||||
# Installation
|
||||
|
||||
[TOC]
|
||||
|
||||
> Note: gVisor supports only x86\_64 and requires Linux 4.14.77+
|
||||
> ([older Linux](./networking.md#gso)).
|
||||
|
||||
## Versions
|
||||
|
||||
The `runsc` binaries and repositories are available in multiple versions and
|
||||
release channels. You should pick the version you'd like to install. For
|
||||
experimentation, the nightly release is recommended. For production use, the
|
||||
latest release is recommended.
|
||||
|
||||
After selecting an appropriate release channel from the options below, proceed
|
||||
to the preferred installation mechanism: manual or from an `apt` repository.
|
||||
|
||||
### HEAD
|
||||
|
||||
Binaries are available for every commit on the `master` branch, and are
|
||||
available at the following URL:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/master/latest/runsc`
|
||||
|
||||
Checksums for the release binary are at:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/master/latest/runsc.sha512`
|
||||
|
||||
For `apt` installation, use the `master` as the `${DIST}` below.
|
||||
|
||||
### Nightly
|
||||
|
||||
Nightly releases are built most nights from the master branch, and are available
|
||||
at the following URL:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc`
|
||||
|
||||
Checksums for the release binary are at:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc.sha512`
|
||||
|
||||
Specific nightly releases can be found at:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/nightly/${yyyy-mm-dd}/runsc`
|
||||
|
||||
Note that a release may not be available for every day.
|
||||
|
||||
For `apt` installation, use the `nightly` as the `${DIST}` below.
|
||||
|
||||
### Latest release
|
||||
|
||||
The latest official release is available at the following URL:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/release/latest`
|
||||
|
||||
For `apt` installation, use the `release` as the `${DIST}` below.
|
||||
|
||||
### Specific release
|
||||
|
||||
A given release release is available at the following URL:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/release/${yyyymmdd}`
|
||||
|
||||
See the [releases][releases] page for information about specific releases.
|
||||
|
||||
For `apt` installation of a specific release, which may include point updates,
|
||||
use the date of the release, e.g. `${yyyymmdd}`, as the `${DIST}` below.
|
||||
|
||||
> Note: only newer releases may be available as `apt` repositories.
|
||||
|
||||
### Point release
|
||||
|
||||
A given point release is available at the following URL:
|
||||
|
||||
`https://storage.googleapis.com/gvisor/releases/release/${yyyymmdd}.${rc}`
|
||||
|
||||
Note that `apt` installation of a specific point release is not supported.
|
||||
|
||||
## Install from an `apt` repository
|
||||
|
||||
First, appropriate dependencies must be installed to allow `apt` to install
|
||||
packages via https:
|
||||
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y \
|
||||
apt-transport-https \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg-agent \
|
||||
software-properties-common
|
||||
```
|
||||
|
||||
Next, the key used to sign archives should be added to your `apt` keychain:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://gvisor.dev/archive.key | sudo apt-key add -
|
||||
```
|
||||
|
||||
Based on the release type, you will need to substitute `${DIST}` below, using
|
||||
one of:
|
||||
|
||||
* `master`: For HEAD.
|
||||
* `nightly`: For nightly releases.
|
||||
* `release`: For the latest release.
|
||||
* `${yyyymmdd}`: For a specific releases (see above).
|
||||
|
||||
The repository for the release you wish to install should be added:
|
||||
|
||||
```bash
|
||||
sudo add-apt-repository "deb https://storage.googleapis.com/gvisor/releases ${DIST} main"
|
||||
```
|
||||
|
||||
For example, to install the latest official release, you can use:
|
||||
|
||||
```bash
|
||||
sudo add-apt-repository "deb https://storage.googleapis.com/gvisor/releases release main"
|
||||
```
|
||||
|
||||
Now the runsc package can be installed:
|
||||
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get install -y runsc
|
||||
```
|
||||
|
||||
If you have Docker installed, it will be automatically configured.
|
||||
|
||||
## Install directly
|
||||
|
||||
The binary URLs provided above can be used to install directly. For example, the
|
||||
latest nightly binary can be downloaded, validated, and placed in an appropriate
|
||||
location by running:
|
||||
|
||||
```bash
|
||||
(
|
||||
set -e
|
||||
URL=https://storage.googleapis.com/gvisor/releases/nightly/latest
|
||||
wget ${URL}/runsc
|
||||
wget ${URL}/runsc.sha512
|
||||
sha512sum -c runsc.sha512
|
||||
rm -f runsc.sha512
|
||||
sudo mv runsc /usr/local/bin
|
||||
sudo chown root:root /usr/local/bin/runsc
|
||||
sudo chmod 0755 /usr/local/bin/runsc
|
||||
)
|
||||
```
|
||||
|
||||
**It is important to copy this binary to a location that is accessible to all
|
||||
users, and ensure it is executable by all users**, since `runsc` executes itself
|
||||
as user `nobody` to avoid unnecessary privileges. The `/usr/local/bin` directory
|
||||
is a good place to put the `runsc` binary.
|
||||
|
||||
After installation, the`runsc` binary comes with an `install` command that can
|
||||
optionally automatically configure Docker:
|
||||
|
||||
```bash
|
||||
runsc install
|
||||
```
|
||||
|
||||
[releases]: https://github.com/google/gvisor/releases
|
||||
@@ -0,0 +1,85 @@
|
||||
# Networking
|
||||
|
||||
[TOC]
|
||||
|
||||
gVisor implements its own network stack called [netstack][netstack]. All aspects
|
||||
of the network stack are handled inside the Sentry — including TCP connection
|
||||
state, control messages, and packet assembly — keeping it isolated from the host
|
||||
network stack. Data link layer packets are written directly to the virtual
|
||||
device inside the network namespace setup by Docker or Kubernetes.
|
||||
|
||||
The IP address and routes configured for the device are transferred inside the
|
||||
sandbox. The loopback device runs exclusively inside the sandbox and does not
|
||||
use the host. You can inspect them by running:
|
||||
|
||||
```bash
|
||||
docker run --rm --runtime=runsc alpine ip addr
|
||||
```
|
||||
|
||||
## Network passthrough
|
||||
|
||||
For high-performance networking applications, you may choose to disable the user
|
||||
space network stack and instead use the host network stack, including the
|
||||
loopback. Note that this mode decreases the isolation to the host.
|
||||
|
||||
Add the following `runtimeArgs` to your Docker configuration
|
||||
(`/etc/docker/daemon.json`) and restart the Docker daemon:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--network=host"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Disabling external networking
|
||||
|
||||
To completely isolate the host and network from the sandbox, external networking
|
||||
can be disabled. The sandbox will still contain a loopback provided by netstack.
|
||||
|
||||
Add the following `runtimeArgs` to your Docker configuration
|
||||
(`/etc/docker/daemon.json`) and restart the Docker daemon:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--network=none"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Disable GSO {#gso}
|
||||
|
||||
If your Linux is older than 4.14.17, you can disable Generic Segmentation
|
||||
Offload (GSO) to run with a kernel that is newer than 3.17. Add the
|
||||
`--gso=false` flag to your Docker runtime configuration
|
||||
(`/etc/docker/daemon.json`) and restart the Docker daemon:
|
||||
|
||||
> Note: Network performance, especially for large payloads, will be greatly
|
||||
> reduced.
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--gso=false"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[netstack]: https://github.com/google/netstack
|
||||
@@ -0,0 +1,119 @@
|
||||
# Platforms (KVM)
|
||||
|
||||
[TOC]
|
||||
|
||||
This document will help you set up your system to use a different gVisor
|
||||
platform.
|
||||
|
||||
## What is a Platform?
|
||||
|
||||
gVisor requires a *platform* to implement interception of syscalls, basic
|
||||
context switching, and memory mapping functionality. These are described in more
|
||||
depth in the [Platform Design](../../architecture_guide/platforms/).
|
||||
|
||||
## Selecting a Platform
|
||||
|
||||
The platform is selected by the `--platform` command line flag passed to
|
||||
`runsc`. By default, the ptrace platform is selected. To select a different
|
||||
platform, modify your Docker configuration (`/etc/docker/daemon.json`) to pass
|
||||
this argument:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--platform=kvm"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You must restart the Docker daemon after making changes to this file, typically
|
||||
this is done via `systemd`:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
## Example: Using the KVM Platform
|
||||
|
||||
The KVM platform is currently experimental; however, it provides several
|
||||
benefits over the default ptrace platform.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You will also to have KVM installed on your system. If you are running a Debian
|
||||
based system like Debian or Ubuntu you can usually do this by installing the
|
||||
`qemu-kvm` package.
|
||||
|
||||
```bash
|
||||
sudo apt-get install qemu-kvm
|
||||
```
|
||||
|
||||
If you are using a virtual machine you will need to make sure that nested
|
||||
virtualization is configured. Here are links to documents on how to set up
|
||||
nested virtualization in several popular environments:
|
||||
|
||||
* Google Cloud: [Enabling Nested Virtualization for VM Instances][nested-gcp]
|
||||
* Microsoft Azure:
|
||||
[How to enable nested virtualization in an Azure VM][nested-azure]
|
||||
* VirtualBox: [Nested Virtualization][nested-virtualbox]
|
||||
* KVM: [Nested Guests][nested-kvm]
|
||||
|
||||
***Note: nested virtualization will have poor performance and is historically a
|
||||
cause of security issues (e.g.
|
||||
[CVE-2018-12904](https://nvd.nist.gov/vuln/detail/CVE-2018-12904)). It is not
|
||||
recommended for production.***
|
||||
|
||||
### Configuring Docker
|
||||
|
||||
Per above, you will need to configure Docker to use `runsc` with the KVM
|
||||
platform. You will remember from the Docker Quick Start that you configured
|
||||
Docker to use `runsc` as the runtime. Docker allows you to add multiple runtimes
|
||||
to the Docker configuration.
|
||||
|
||||
Add a new entry for the KVM platform entry to your Docker configuration
|
||||
(`/etc/docker/daemon.json`) in order to provide the `--platform=kvm` runtime
|
||||
argument.
|
||||
|
||||
In the end, the file should look something like:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc"
|
||||
},
|
||||
"runsc-kvm": {
|
||||
"path": "/usr/local/bin/runsc",
|
||||
"runtimeArgs": [
|
||||
"--platform=kvm"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You must restart the Docker daemon after making changes to this file, typically
|
||||
this is done via `systemd`:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
## Running a container
|
||||
|
||||
Now run your container using the `runsc-kvm` runtime. This will run the
|
||||
container using the KVM platform:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc-kvm --rm hello-world
|
||||
```
|
||||
|
||||
[nested-azure]: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/nested-virtualization
|
||||
[nested-gcp]: https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances
|
||||
[nested-virtualbox]: https://www.virtualbox.org/manual/UserManual.html#nested-virt
|
||||
[nested-kvm]: https://www.linux-kvm.org/page/Nested_Guests
|
||||
@@ -0,0 +1,33 @@
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(
|
||||
default_visibility = ["//website:__pkg__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "docker",
|
||||
src = "docker.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/quick_start/docker/",
|
||||
subcategory = "Quick Start",
|
||||
weight = "11",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "oci",
|
||||
src = "oci.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/quick_start/oci/",
|
||||
subcategory = "Quick Start",
|
||||
weight = "12",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "kubernetes",
|
||||
src = "kubernetes.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/user_guide/quick_start/kubernetes/",
|
||||
subcategory = "Quick Start",
|
||||
weight = "13",
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
# Docker
|
||||
|
||||
> Note: This guide requires Docker version 17.09.0 or greater. Refer to the
|
||||
> [Docker documentation][docker] for how to install it.
|
||||
|
||||
This guide will help you quickly get started running Docker containers using
|
||||
gVisor.
|
||||
|
||||
First, follow the [Installation guide][install].
|
||||
|
||||
If you use the `apt` repository or the `automated` install, then you can skip
|
||||
the next section and proceed straight to running a container.
|
||||
|
||||
## Configuring Docker
|
||||
|
||||
First you will need to configure Docker to use `runsc` by adding a runtime entry
|
||||
to your Docker configuration (`/etc/docker/daemon.json`). You may have to create
|
||||
this file if it does not exist. Also, some Docker versions also require you to
|
||||
[specify the `storage-driver` field][storage-driver].
|
||||
|
||||
In the end, the file should look something like:
|
||||
|
||||
```json
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/local/bin/runsc"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You must restart the Docker daemon after making changes to this file, typically
|
||||
this is done via `systemd`:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
## Running a container
|
||||
|
||||
Now run your container using the `runsc` runtime:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --rm hello-world
|
||||
```
|
||||
|
||||
You can also run a terminal to explore the container.
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --rm -it ubuntu /bin/bash
|
||||
```
|
||||
|
||||
Many docker options are compatible with gVisor, try them out. Here is an
|
||||
example:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --rm --link backend:database -v ~/bin:/tools:ro -p 8080:80 --cpus=0.5 -it busybox telnet towel.blinkenlights.nl
|
||||
```
|
||||
|
||||
## Verify the runtime
|
||||
|
||||
You can verify that you are running in gVisor using the `dmesg` command.
|
||||
|
||||
```text
|
||||
$ docker run --runtime=runsc -it ubuntu dmesg
|
||||
[ 0.000000] Starting gVisor...
|
||||
[ 0.354495] Daemonizing children...
|
||||
[ 0.564053] Constructing home...
|
||||
[ 0.976710] Preparing for the zombie uprising...
|
||||
[ 1.299083] Creating process schedule...
|
||||
[ 1.479987] Committing treasure map to memory...
|
||||
[ 1.704109] Searching for socket adapter...
|
||||
[ 1.748935] Generating random numbers by fair dice roll...
|
||||
[ 2.059747] Digging up root...
|
||||
[ 2.259327] Checking naughty and nice process list...
|
||||
[ 2.610538] Rewriting operating system in Javascript...
|
||||
[ 2.613217] Ready!
|
||||
```
|
||||
|
||||
Note that this is easily replicated by an attacker so applications should never
|
||||
use `dmesg` to verify the runtime in a security sensitive context.
|
||||
|
||||
Next, look at the different options available for gVisor: [platform][platforms],
|
||||
[network][networking], [filesystem][filesystem].
|
||||
|
||||
[docker]: https://docs.docker.com/install/
|
||||
[storage-driver]: https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-storage-driver
|
||||
[install]: /docs/user_guide/install/
|
||||
[filesystem]: /docs/user_guide/filesystem/
|
||||
[networking]: /docs/user_guide/networking/
|
||||
[platforms]: /docs/user_guide/platforms/
|
||||
@@ -0,0 +1,36 @@
|
||||
# Kubernetes
|
||||
|
||||
gVisor can be used to run Kubernetes pods and has several integration points
|
||||
with Kubernetes.
|
||||
|
||||
## Using Minikube
|
||||
|
||||
gVisor can run sandboxed containers in a Kubernetes cluster with Minikube. After
|
||||
the gVisor addon is enabled, pods with `io.kubernetes.cri.untrusted-workload`
|
||||
set to true will execute with `runsc`. Follow [these instructions][minikube] to
|
||||
enable gVisor addon.
|
||||
|
||||
## Using Containerd
|
||||
|
||||
You can also setup Kubernetes nodes to run pods in gvisor using the
|
||||
[containerd][containerd] CRI runtime and the `gvisor-containerd-shim`. You can
|
||||
use either the `io.kubernetes.cri.untrusted-workload` annotation or
|
||||
[RuntimeClass][runtimeclass] to run Pods with `runsc`. You can find instructions
|
||||
[here][gvisor-containerd-shim].
|
||||
|
||||
## Using GKE Sandbox
|
||||
|
||||
[GKE Sandbox][gke-sandbox] is available in [Google Kubernetes Engine][gke]. You
|
||||
just need to deploy a node pool with gVisor enabled in your cluster, and it will
|
||||
run pods annotated with `runtimeClassName: gvisor` inside a gVisor sandbox for
|
||||
you. [Here][wordpress-quick] is a quick example showing how to deploy a
|
||||
WordPress site. You can view the full documentation [here][gke-sandbox-docs].
|
||||
|
||||
[containerd]: https://containerd.io/
|
||||
[minikube]: https://github.com/kubernetes/minikube/blob/master/deploy/addons/gvisor/README.md
|
||||
[gke]: https://cloud.google.com/kubernetes-engine/
|
||||
[gke-sandbox]: https://cloud.google.com/kubernetes-engine/sandbox/
|
||||
[gke-sandbox-docs]: https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods
|
||||
[gvisor-containerd-shim]: https://github.com/google/gvisor-containerd-shim
|
||||
[runtimeclass]: https://kubernetes.io/docs/concepts/containers/runtime-class/
|
||||
[wordpress-quick]: /docs/tutorials/kubernetes/
|
||||
@@ -0,0 +1,45 @@
|
||||
# OCI
|
||||
|
||||
This guide will quickly get you started running your first gVisor sandbox
|
||||
container using the runtime directly with the default platform.
|
||||
|
||||
First, follow the [Installation guide][install].
|
||||
|
||||
## Run an OCI compatible container
|
||||
|
||||
Now we will create an [OCI][oci] container bundle to run our container. First we
|
||||
will create a root directory for our bundle.
|
||||
|
||||
```bash
|
||||
mkdir bundle
|
||||
cd bundle
|
||||
```
|
||||
|
||||
Create a root file system for the container. We will use the Docker hello-world
|
||||
image as the basis for our container.
|
||||
|
||||
```bash
|
||||
mkdir rootfs
|
||||
docker export $(docker create hello-world) | tar -xf - -C rootfs
|
||||
```
|
||||
|
||||
Next, create an specification file called `config.json` that contains our
|
||||
container specification. We will update the default command it runs to `/hello`
|
||||
in the `hello-world` container.
|
||||
|
||||
```bash
|
||||
runsc spec
|
||||
sed -i 's;"sh";"/hello";' config.json
|
||||
```
|
||||
|
||||
Finally run the container.
|
||||
|
||||
```bash
|
||||
sudo runsc run hello
|
||||
```
|
||||
|
||||
Next try [using CNI to set up networking](../../../tutorials/cni/) or
|
||||
[running gVisor using Docker](../docker/).
|
||||
|
||||
[oci]: https://opencontainers.org/
|
||||
[install]: /docs/user_guide/install
|
||||
@@ -0,0 +1,37 @@
|
||||
load("//website:defs.bzl", "doc")
|
||||
|
||||
package(
|
||||
default_visibility = ["//website:__pkg__"],
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "docker",
|
||||
src = "docker.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/tutorials/docker/",
|
||||
subcategory = "Tutorials",
|
||||
weight = "21",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "cni",
|
||||
src = "cni.md",
|
||||
category = "User Guide",
|
||||
permalink = "/docs/tutorials/cni/",
|
||||
subcategory = "Tutorials",
|
||||
weight = "22",
|
||||
)
|
||||
|
||||
doc(
|
||||
name = "kubernetes",
|
||||
src = "kubernetes.md",
|
||||
category = "User Guide",
|
||||
data = [
|
||||
"add-node-pool.png",
|
||||
"node-pool-button.png",
|
||||
],
|
||||
permalink = "/docs/tutorials/kubernetes/",
|
||||
subcategory = "Tutorials",
|
||||
weight = "33",
|
||||
)
|
||||
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,172 @@
|
||||
# Using CNI
|
||||
|
||||
This tutorial will show you how to set up networking for a gVisor sandbox using
|
||||
the
|
||||
[Container Networking Interface (CNI)](https://github.com/containernetworking/cni).
|
||||
|
||||
## Install CNI Plugins
|
||||
|
||||
First you will need to install the CNI plugins. CNI plugins are used to set up a
|
||||
network namespace that `runsc` can use with the sandbox.
|
||||
|
||||
Start by creating the directories for CNI plugin binaries:
|
||||
|
||||
```
|
||||
sudo mkdir -p /opt/cni/bin
|
||||
```
|
||||
|
||||
Download the CNI plugins:
|
||||
|
||||
```
|
||||
wget https://github.com/containernetworking/plugins/releases/download/v0.8.3/cni-plugins-linux-amd64-v0.8.3.tgz
|
||||
```
|
||||
|
||||
Next, unpack the plugins into the CNI binary directory:
|
||||
|
||||
```
|
||||
sudo tar -xvf cni-plugins-linux-amd64-v0.8.3.tgz -C /opt/cni/bin/
|
||||
```
|
||||
|
||||
## Configure CNI Plugins
|
||||
|
||||
This section will show you how to configure CNI plugins. This tutorial will use
|
||||
the "bridge" and "loopback" plugins which will create the necessary bridge and
|
||||
loopback devices in our network namespace. However, you should be able to use
|
||||
any CNI compatible plugin to set up networking for gVisor sandboxes.
|
||||
|
||||
The bridge plugin configuration specifies the IP address subnet range for IP
|
||||
addresses that will be assigned to sandboxes as well as the network routing
|
||||
configuration. This tutorial will assign IP addresses from the `10.22.0.0/16`
|
||||
range and allow all outbound traffic, however you can modify this configuration
|
||||
to suit your use case.
|
||||
|
||||
Create the bridge and loopback plugin configurations:
|
||||
|
||||
```
|
||||
sudo mkdir -p /etc/cni/net.d
|
||||
|
||||
sudo sh -c 'cat > /etc/cni/net.d/10-bridge.conf << EOF
|
||||
{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isGateway": true,
|
||||
"ipMasq": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.22.0.0/16",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF'
|
||||
|
||||
sudo sh -c 'cat > /etc/cni/net.d/99-loopback.conf << EOF
|
||||
{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "lo",
|
||||
"type": "loopback"
|
||||
}
|
||||
EOF'
|
||||
```
|
||||
|
||||
## Create a Network Namespace
|
||||
|
||||
For each gVisor sandbox you will create a network namespace and configure it
|
||||
using CNI. First, create a random network namespace name and then create the
|
||||
namespace.
|
||||
|
||||
The network namespace path will then be `/var/run/netns/${CNI_CONTAINERID}`.
|
||||
|
||||
```
|
||||
export CNI_PATH=/opt/cni/bin
|
||||
export CNI_CONTAINERID=$(printf '%x%x%x%x' $RANDOM $RANDOM $RANDOM $RANDOM)
|
||||
export CNI_COMMAND=ADD
|
||||
export CNI_NETNS=/var/run/netns/${CNI_CONTAINERID}
|
||||
|
||||
sudo ip netns add ${CNI_CONTAINERID}
|
||||
```
|
||||
|
||||
Next, run the bridge and loopback plugins to apply the configuration that was
|
||||
created earlier to the namespace. Each plugin outputs some JSON indicating the
|
||||
results of executing the plugin. For example, The bridge plugin's response
|
||||
includes the IP address assigned to the ethernet device created in the network
|
||||
namespace. Take note of the IP address for use later.
|
||||
|
||||
```
|
||||
export CNI_IFNAME="eth0"
|
||||
sudo -E /opt/cni/bin/bridge < /etc/cni/net.d/10-bridge.conf
|
||||
export CNI_IFNAME="lo"
|
||||
sudo -E /opt/cni/bin/loopback < /etc/cni/net.d/99-loopback.conf
|
||||
```
|
||||
|
||||
Get the IP address assigned to our sandbox:
|
||||
|
||||
```
|
||||
POD_IP=$(sudo ip netns exec ${CNI_CONTAINERID} ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
|
||||
```
|
||||
|
||||
## Create the OCI Bundle
|
||||
|
||||
Now that our network namespace is created and configured, we can create the OCI
|
||||
bundle for our container. As part of the bundle's `config.json` we will specify
|
||||
that the container use the network namespace that we created.
|
||||
|
||||
The container will run a simple python webserver that we will be able to connect
|
||||
to via the IP address assigned to it via the bridge CNI plugin.
|
||||
|
||||
Create the bundle and root filesystem directories:
|
||||
|
||||
```
|
||||
sudo mkdir -p bundle
|
||||
cd bundle
|
||||
sudo mkdir rootfs
|
||||
sudo docker export $(docker create python) | sudo tar --same-owner -pxf - -C rootfs
|
||||
sudo mkdir -p rootfs/var/www/html
|
||||
sudo sh -c 'echo "Hello World!" > rootfs/var/www/html/index.html'
|
||||
```
|
||||
|
||||
Next create the `config.json` specifying the network namespace. `sudo
|
||||
/usr/local/bin/runsc spec sudo sed -i 's;"sh";"python", "-m", "http.server";'
|
||||
config.json sudo sed -i "s;\"cwd\": \"/\";\"cwd\": \"/var/www/html\";"
|
||||
config.json sudo sed -i "s;\"type\": \"network\";\"type\":
|
||||
\"network\",\n\t\t\t\t\"path\": \"/var/run/netns/${CNI_CONTAINERID}\";"
|
||||
config.json`
|
||||
|
||||
## Run the Container
|
||||
|
||||
Now we can run and connect to the webserver. Run the container in gVisor. Use
|
||||
the same ID used for the network namespace to be consistent:
|
||||
|
||||
```
|
||||
sudo runsc run -detach ${CNI_CONTAINERID}
|
||||
```
|
||||
|
||||
Connect to the server via the sandbox's IP address:
|
||||
|
||||
```
|
||||
curl http://${POD_IP}:8000/
|
||||
```
|
||||
|
||||
You should see the server returning `Hello World!`.
|
||||
|
||||
## Cleanup
|
||||
|
||||
After you are finished running the container, you can clean up the network
|
||||
namespace .
|
||||
|
||||
```
|
||||
sudo runsc kill ${CNI_CONTAINERID}
|
||||
sudo runsc delete ${CNI_CONTAINERID}
|
||||
|
||||
export CNI_COMMAND=DEL
|
||||
|
||||
export CNI_IFNAME="lo"
|
||||
sudo -E /opt/cni/bin/loopback < /etc/cni/net.d/99-loopback.conf
|
||||
export CNI_IFNAME="eth0"
|
||||
sudo -E /opt/cni/bin/bridge < /etc/cni/net.d/10-bridge.conf
|
||||
|
||||
sudo ip netns delete ${CNI_CONTAINERID}
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
# WorkPress with Docker
|
||||
|
||||
This page shows you how to deploy a sample [WordPress][wordpress] site using
|
||||
[Docker][docker].
|
||||
|
||||
### Before you begin
|
||||
|
||||
[Follow these instructions][docker-install] to install runsc with Docker. This
|
||||
document assumes that the runtime name chosen is `runsc`.
|
||||
|
||||
### Running WordPress
|
||||
|
||||
Now, let's deploy a WordPress site using Docker. WordPress site requires two
|
||||
containers: web server in the frontend, MySQL database in the backend.
|
||||
|
||||
First, let's define a few environment variables that are shared between both
|
||||
containers:
|
||||
|
||||
```bash
|
||||
export MYSQL_PASSWORD=${YOUR_SECRET_PASSWORD_HERE?}
|
||||
export MYSQL_DB=wordpress
|
||||
export MYSQL_USER=wordpress
|
||||
```
|
||||
|
||||
Next, let's start the database container running MySQL and wait until the
|
||||
database is initialized:
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --name mysql -d \
|
||||
-e MYSQL_RANDOM_ROOT_PASSWORD=1 \
|
||||
-e MYSQL_PASSWORD="${MYSQL_PASSWORD}" \
|
||||
-e MYSQL_DATABASE="${MYSQL_DB}" \
|
||||
-e MYSQL_USER="${MYSQL_USER}" \
|
||||
mysql:5.7
|
||||
|
||||
# Wait until this message appears in the log.
|
||||
docker logs mysql |& grep 'port: 3306 MySQL Community Server (GPL)'
|
||||
```
|
||||
|
||||
Once the database is running, you can start the WordPress frontend. We use the
|
||||
`--link` option to connect the frontend to the database, and expose the
|
||||
WordPress to port 8080 on the localhost.
|
||||
|
||||
```bash
|
||||
docker run --runtime=runsc --name wordpress -d \
|
||||
--link mysql:mysql \
|
||||
-p 8080:80 \
|
||||
-e WORDPRESS_DB_HOST=mysql \
|
||||
-e WORDPRESS_DB_USER="${MYSQL_USER}" \
|
||||
-e WORDPRESS_DB_PASSWORD="${MYSQL_PASSWORD}" \
|
||||
-e WORDPRESS_DB_NAME="${MYSQL_DB}" \
|
||||
-e WORDPRESS_TABLE_PREFIX=wp_ \
|
||||
wordpress
|
||||
```
|
||||
|
||||
Now, you can access the WordPress website pointing your favorite browser to
|
||||
<http://localhost:8080>.
|
||||
|
||||
Congratulations! You have just deployed a WordPress site using Docker.
|
||||
|
||||
### What's next
|
||||
|
||||
[Learn how to deploy WordPress with Kubernetes][wordpress-k8s].
|
||||
|
||||
[docker]: https://www.docker.com/
|
||||
[docker-install]: /docs/user_guide/quick_start/docker/
|
||||
[wordpress]: https://wordpress.com/
|
||||
[wordpress-k8s]: /docs/tutorials/kubernetes/
|
||||
@@ -0,0 +1,134 @@
|
||||
# WordPress with Kubernetes
|
||||
|
||||
This page shows you how to deploy a sample [WordPress][wordpress] site using
|
||||
[GKE Sandbox][gke-sandbox].
|
||||
|
||||
### Before you begin
|
||||
|
||||
Take the following steps to enable the Kubernetes Engine API:
|
||||
|
||||
1. Visit the [Kubernetes Engine page][project-selector] in the Google Cloud
|
||||
Platform Console.
|
||||
1. Create or select a project.
|
||||
|
||||
### Creating a node pool with gVisor enabled
|
||||
|
||||
Create a node pool inside your cluster with option `--sandbox type=gvisor` added
|
||||
to the command, like below:
|
||||
|
||||
```bash
|
||||
gcloud beta container node-pools create sandbox-pool --cluster=${CLUSTER_NAME} --image-type=cos_containerd --sandbox type=gvisor
|
||||
```
|
||||
|
||||
If you prefer to use the console, select your cluster and select the **ADD NODE
|
||||
POOL** button:
|
||||
|
||||

|
||||
|
||||
Then select the **Image type** with **Containerd** and select **Enable sandbox
|
||||
with gVisor** option. Select other options as you like:
|
||||
|
||||

|
||||
|
||||
### Check that gVisor is enabled
|
||||
|
||||
The gvisor RuntimeClass is instantiated during node creation. You can check for
|
||||
the existence of the gvisor RuntimeClass using the following command:
|
||||
|
||||
```bash
|
||||
kubectl get runtimeclasses
|
||||
```
|
||||
|
||||
### Wordpress deployment
|
||||
|
||||
Now, let's deploy a WordPress site using GKE Sandbox. WordPress site requires
|
||||
two pods: web server in the frontend, MySQL database in the backend. Both
|
||||
applications use PersistentVolumes to store the site data data. In addition,
|
||||
they use secret store to share MySQL password between them.
|
||||
|
||||
First, let's download the deployment configuration files to add the runtime
|
||||
class annotation to them:
|
||||
|
||||
```bash
|
||||
curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
|
||||
curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
|
||||
```
|
||||
|
||||
Add a **spec.template.spec.runtimeClassName** set to **gvisor** to both files,
|
||||
as shown below:
|
||||
|
||||
**wordpress-deployment.yaml:** ```yaml apiVersion: v1 kind: Service metadata:
|
||||
name: wordpress labels: app: wordpress spec: ports: - port: 80 selector: app:
|
||||
wordpress tier: frontend
|
||||
|
||||
## type: LoadBalancer
|
||||
|
||||
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wp-pv-claim labels:
|
||||
app: wordpress spec: accessModes: - ReadWriteOnce resources: requests:
|
||||
|
||||
## storage: 20Gi
|
||||
|
||||
apiVersion: apps/v1 kind: Deployment metadata: name: wordpress labels: app:
|
||||
wordpress spec: selector: matchLabels: app: wordpress tier: frontend strategy:
|
||||
type: Recreate template: metadata: labels: app: wordpress tier: frontend spec:
|
||||
runtimeClassName: gvisor # ADD THIS LINE containers: - image:
|
||||
wordpress:4.8-apache name: wordpress env: - name: WORDPRESS_DB_HOST value:
|
||||
wordpress-mysql - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name:
|
||||
mysql-pass key: password ports: - containerPort: 80 name: wordpress
|
||||
volumeMounts: - name: wordpress-persistent-storage mountPath: /var/www/html
|
||||
volumes: - name: wordpress-persistent-storage persistentVolumeClaim: claimName:
|
||||
wp-pv-claim ```
|
||||
|
||||
**mysql-deployment.yaml:** ```yaml apiVersion: v1 kind: Service metadata: name:
|
||||
wordpress-mysql labels: app: wordpress spec: ports: - port: 3306 selector: app:
|
||||
wordpress tier: mysql
|
||||
|
||||
## clusterIP: None
|
||||
|
||||
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pv-claim
|
||||
labels: app: wordpress spec: accessModes: - ReadWriteOnce resources: requests:
|
||||
|
||||
## storage: 20Gi
|
||||
|
||||
apiVersion: apps/v1 kind: Deployment metadata: name: wordpress-mysql labels:
|
||||
app: wordpress spec: selector: matchLabels: app: wordpress tier: mysql strategy:
|
||||
type: Recreate template: metadata: labels: app: wordpress tier: mysql spec:
|
||||
runtimeClassName: gvisor # ADD THIS LINE containers: - image: mysql:5.6 name:
|
||||
mysql env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-pass
|
||||
key: password ports: - containerPort: 3306 name: mysql volumeMounts: - name:
|
||||
mysql-persistent-storage mountPath: /var/lib/mysql volumes: - name:
|
||||
mysql-persistent-storage persistentVolumeClaim: claimName: mysql-pv-claim ```
|
||||
|
||||
Note that apart from `runtimeClassName: gvisor`, nothing else about the
|
||||
Deployment has is changed.
|
||||
|
||||
You are now ready to deploy the entire application. Just create a secret to
|
||||
store MySQL's password and *apply* both deployments:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic mysql-pass --from-literal=password=${YOUR_SECRET_PASSWORD_HERE?}
|
||||
kubectl apply -f mysql-deployment.yaml
|
||||
kubectl apply -f wordpress-deployment.yaml
|
||||
```
|
||||
|
||||
Wait for the deployments to be ready and an external IP to be assigned to the
|
||||
Wordpress service:
|
||||
|
||||
```bash
|
||||
watch kubectl get service wordpress
|
||||
```
|
||||
|
||||
Now, copy the service `EXTERNAL-IP` from above to your favorite browser to view
|
||||
and configure your new WordPress site.
|
||||
|
||||
Congratulations! You have just deployed a WordPress site using GKE Sandbox.
|
||||
|
||||
### What's next
|
||||
|
||||
To learn more about GKE Sandbox and how to run your deployment securely, take a
|
||||
look at the [documentation][gke-sandbox-docs].
|
||||
|
||||
[gke-sandbox-docs]: https://cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods
|
||||
[gke-sandbox]: https://cloud.google.com/kubernetes-engine/sandbox/
|
||||
[project-selector]: https://console.cloud.google.com/projectselector/kubernetes
|
||||
[wordpress]: https://wordpress.com/
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -1,11 +1,16 @@
|
||||
FROM fedora:31
|
||||
# Install bazel.
|
||||
RUN dnf install -y dnf-plugins-core && dnf copr enable -y vbatts/bazel
|
||||
RUN dnf install -y git gcc make golang gcc-c++ glibc-devel python3 which python3-pip python3-devel libffi-devel openssl-devel pkg-config glibc-static libstdc++-static patch
|
||||
RUN pip install pycparser
|
||||
RUN dnf install -y bazel3
|
||||
# Install gcloud.
|
||||
RUN curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-289.0.0-linux-x86_64.tar.gz | \
|
||||
tar zxvf - google-cloud-sdk && \
|
||||
google-cloud-sdk/install.sh && \
|
||||
ln -s /google-cloud-sdk/bin/gcloud /usr/bin/gcloud
|
||||
# Install Docker client for the website build.
|
||||
RUN dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
||||
RUN dnf install -y docker-ce-cli
|
||||
WORKDIR /workspace
|
||||
ENTRYPOINT ["/usr/bin/bazel"]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM jekyll/jekyll:4.0.0
|
||||
USER root
|
||||
RUN gem install \
|
||||
html-proofer:3.10.2 \
|
||||
nokogiri:1.10.1 \
|
||||
jekyll-autoprefixer:1.0.2 \
|
||||
jekyll-inline-svg:1.1.4 \
|
||||
jekyll-paginate:1.1.0 \
|
||||
kramdown-parser-gfm:1.1.0 \
|
||||
jekyll-relative-links:0.6.1 \
|
||||
jekyll-feed:0.13.0
|
||||
CMD ["/usr/gem/gems/jekyll-4.0.0/exe/jekyll", "build", "-t", "-s", "/input", "-d", "/output"]
|
||||
@@ -22,7 +22,7 @@ BRANCH_NAME := $(shell (git branch --show-current 2>/dev/null || \
|
||||
# Bazel container configuration (see below).
|
||||
USER ?= gvisor
|
||||
DOCKER_NAME ?= gvisor-bazel-$(shell readlink -m $(CURDIR) | md5sum | cut -c1-8)
|
||||
DOCKER_RUN_OPTIONS ?= --privileged
|
||||
DOCKER_PRIVILEGED ?= --privileged
|
||||
BAZEL_CACHE := $(shell readlink -m ~/.cache/bazel/)
|
||||
GCLOUD_CONFIG := $(shell readlink -m ~/.config/gcloud/)
|
||||
DOCKER_SOCKET := /var/run/docker.sock
|
||||
@@ -30,10 +30,19 @@ DOCKER_SOCKET := /var/run/docker.sock
|
||||
# Non-configurable.
|
||||
UID := $(shell id -u ${USER})
|
||||
GID := $(shell id -g ${USER})
|
||||
USERADD_OPTIONS :=
|
||||
FULL_DOCKER_RUN_OPTIONS := $(DOCKER_RUN_OPTIONS)
|
||||
FULL_DOCKER_RUN_OPTIONS += -v "$(BAZEL_CACHE):$(BAZEL_CACHE)"
|
||||
FULL_DOCKER_RUN_OPTIONS += -v "$(GCLOUD_CONFIG):$(GCLOUD_CONFIG)"
|
||||
FULL_DOCKER_RUN_OPTIONS += -v "/tmp:/tmp"
|
||||
ifneq ($(DOCKER_PRIVILEGED),)
|
||||
FULL_DOCKER_RUN_OPTIONS += -v "$(DOCKER_SOCKET):$(DOCKER_SOCKET)"
|
||||
DOCKER_GROUP := $(shell stat -c '%g' $(DOCKER_SOCKET))
|
||||
ifneq ($(GID),$(DOCKER_GROUP))
|
||||
USERADD_OPTIONS += --groups $(DOCKER_GROUP)
|
||||
FULL_DOCKER_RUN_OPTIONS += --group-add $(DOCKER_GROUP)
|
||||
endif
|
||||
endif
|
||||
SHELL=/bin/bash -o pipefail
|
||||
|
||||
##
|
||||
@@ -53,15 +62,14 @@ bazel-server-start: load-default ## Starts the bazel server.
|
||||
docker run -d --rm \
|
||||
--init \
|
||||
--name $(DOCKER_NAME) \
|
||||
--user 0:0 \
|
||||
--user 0:0 $(DOCKER_GROUP_OPTIONS) \
|
||||
-v "$(CURDIR):$(CURDIR)" \
|
||||
--workdir "$(CURDIR)" \
|
||||
--tmpfs /tmp:rw,exec \
|
||||
--entrypoint "" \
|
||||
$(FULL_DOCKER_RUN_OPTIONS) \
|
||||
gvisor.dev/images/default \
|
||||
sh -c "groupadd --gid $(GID) --non-unique $(USER) && \
|
||||
useradd --uid $(UID) --non-unique --no-create-home --gid $(GID) -d $(HOME) $(USER) && \
|
||||
useradd --uid $(UID) --non-unique --no-create-home --gid $(GID) $(USERADD_OPTIONS) -d $(HOME) $(USER) && \
|
||||
bazel version && \
|
||||
exec tail --pid=\$$(bazel info server_pid) -f /dev/null"
|
||||
@while :; do if docker logs $(DOCKER_NAME) 2>/dev/null | grep "Build label:" >/dev/null; then break; fi; sleep 1; done
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
load("//tools:defs.bzl", "pkg_tar")
|
||||
load("//website:defs.bzl", "doc", "docs")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
# website is the full container image. Note that this actually just collects
|
||||
# other dependendcies and runs Docker locally to import and tag the image.
|
||||
sh_binary(
|
||||
name = "website",
|
||||
srcs = ["import.sh"],
|
||||
data = [":files"],
|
||||
tags = [
|
||||
"local",
|
||||
"manual",
|
||||
],
|
||||
)
|
||||
|
||||
# files is the full file system of the generated container.
|
||||
#
|
||||
# It must collect the all tarballs (produced by the rules below), and run it
|
||||
# through the Dockerfile to generate the site. Note that this checks all links,
|
||||
# and therefore requires all static content to be present as well.
|
||||
#
|
||||
# Note that this rule violates most aspects of hermetic builds. However, this
|
||||
# works much more reliably than depending on the container_image rules from
|
||||
# bazel itself, which are convoluted and seem to have a hard time even finding
|
||||
# the toolchain.
|
||||
genrule(
|
||||
name = "files",
|
||||
srcs = [
|
||||
":config",
|
||||
":css",
|
||||
":docs",
|
||||
":static",
|
||||
":syscallmd",
|
||||
"//website/blog:posts",
|
||||
"//website/cmd/server",
|
||||
],
|
||||
outs = ["files.tgz"],
|
||||
cmd = "set -x; " +
|
||||
"T=$$(mktemp -d); " +
|
||||
"mkdir -p $$T/input && " +
|
||||
"mkdir -p $$T/output/_site && " +
|
||||
"tar -xf $(location :config) -C $$T/input && " +
|
||||
"tar -xf $(location :css) -C $$T/input && " +
|
||||
"tar -xf $(location :docs) -C $$T/input && " +
|
||||
"tar -xf $(location :syscallmd) -C $$T/input && " +
|
||||
"tar -xf $(location //website/blog:posts) -C $$T/input && " +
|
||||
"find $$T/input -type f -exec chmod u+rw {} \\; && " +
|
||||
"docker run -i --user $$(id -u):$$(id -g) " +
|
||||
"-v $$(readlink -m $$T/input):/input " +
|
||||
"-v $$(readlink -m $$T/output/_site):/output " +
|
||||
"gvisor.dev/images/jekyll && " +
|
||||
"tar -xf $(location :static) -C $$T/output/_site && " +
|
||||
"docker run -i --user $$(id -u):$$(id -g) " +
|
||||
"-v $$(readlink -m $$T/output/_site):/output " +
|
||||
"gvisor.dev/images/jekyll " +
|
||||
"/usr/gem/bin/htmlproofer " +
|
||||
"--disable-external " +
|
||||
"--check-html " +
|
||||
"/output && " +
|
||||
"cp $(location //website/cmd/server) $$T/output/server && " +
|
||||
"tar -zcf $@ -C $$T/output . && " +
|
||||
"rm -rf $$T",
|
||||
tags = [
|
||||
"local",
|
||||
"manual",
|
||||
"nosandbox",
|
||||
],
|
||||
)
|
||||
|
||||
# static are the purely static parts of the site. These are effectively copied
|
||||
# in after jekyll generates all the dynamic content.
|
||||
pkg_tar(
|
||||
name = "static",
|
||||
srcs = [
|
||||
"archive.key",
|
||||
] + glob([
|
||||
"performance/**",
|
||||
]),
|
||||
strip_prefix = "./",
|
||||
)
|
||||
|
||||
# main.scss requires front-matter to be processed.
|
||||
genrule(
|
||||
name = "css",
|
||||
srcs = glob([
|
||||
"css/**",
|
||||
]),
|
||||
outs = [
|
||||
"css.tar",
|
||||
],
|
||||
cmd = "T=$$(mktemp -d); " +
|
||||
"mkdir -p $$T/css && " +
|
||||
"for file in $(SRCS); do " +
|
||||
"echo -en '---\\n---\\n' > $$T/css/$$(basename $$file) && " +
|
||||
"cat $$file >> $$T/css/$$(basename $$file); " +
|
||||
"done && " +
|
||||
"tar -C $$T -czf $@ . && " +
|
||||
"rm -rf $$T",
|
||||
)
|
||||
|
||||
# config is "mostly" static content. These are parts of the site that are
|
||||
# present when jekyll runs, but are not dynamically generated.
|
||||
pkg_tar(
|
||||
name = "config",
|
||||
srcs = [
|
||||
":css",
|
||||
"_config.yml",
|
||||
"//website/blog:index.html",
|
||||
] + glob([
|
||||
"assets/**",
|
||||
"_includes/**",
|
||||
"_layouts/**",
|
||||
"_plugins/**",
|
||||
"_sass/**",
|
||||
]),
|
||||
strip_prefix = "./",
|
||||
)
|
||||
|
||||
# index is the index file.
|
||||
doc(
|
||||
name = "index",
|
||||
src = "index.md",
|
||||
layout = "base",
|
||||
permalink = "/",
|
||||
)
|
||||
|
||||
# docs is the dynamic content of the site.
|
||||
docs(
|
||||
name = "docs",
|
||||
deps = [
|
||||
":index",
|
||||
"//:code_of_conduct",
|
||||
"//:contributing",
|
||||
"//:governance",
|
||||
"//:security",
|
||||
"//g3doc:community",
|
||||
"//g3doc:index",
|
||||
"//g3doc:roadmap",
|
||||
"//g3doc/architecture_guide:index",
|
||||
"//g3doc/architecture_guide:performance",
|
||||
"//g3doc/architecture_guide:platforms",
|
||||
"//g3doc/architecture_guide:resources",
|
||||
"//g3doc/architecture_guide:security",
|
||||
"//g3doc/user_guide:FAQ",
|
||||
"//g3doc/user_guide:checkpoint_restore",
|
||||
"//g3doc/user_guide:compatibility",
|
||||
"//g3doc/user_guide:debugging",
|
||||
"//g3doc/user_guide:filesystem",
|
||||
"//g3doc/user_guide:install",
|
||||
"//g3doc/user_guide:networking",
|
||||
"//g3doc/user_guide:platforms",
|
||||
"//g3doc/user_guide/quick_start:docker",
|
||||
"//g3doc/user_guide/quick_start:kubernetes",
|
||||
"//g3doc/user_guide/quick_start:oci",
|
||||
"//g3doc/user_guide/tutorials:cni",
|
||||
"//g3doc/user_guide/tutorials:docker",
|
||||
"//g3doc/user_guide/tutorials:kubernetes",
|
||||
],
|
||||
)
|
||||
|
||||
# Generate JSON for system call tables
|
||||
genrule(
|
||||
name = "syscalljson",
|
||||
outs = ["syscalls.json"],
|
||||
cmd = "$(location //runsc) -- help syscalls -format json -filename $@",
|
||||
tools = ["//runsc"],
|
||||
)
|
||||
|
||||
# Generate markdown from the json dump.
|
||||
genrule(
|
||||
name = "syscallmd",
|
||||
srcs = [":syscalljson"],
|
||||
outs = ["syscallsmd"],
|
||||
cmd = "T=$$(mktemp -d) && " +
|
||||
"$(location //website/cmd/syscalldocs) -in $< -out $$T && " +
|
||||
"tar -C $$T -czf $@ . && " +
|
||||
"rm -rf $$T",
|
||||
tools = ["//website/cmd/syscalldocs"],
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
destination: _site
|
||||
markdown: kramdown
|
||||
kramdown:
|
||||
syntax_highlighter: rouge
|
||||
toc_levels: "2,3"
|
||||
highlighter: rouge
|
||||
paginate: 5
|
||||
paginate_path: "/blog/page:num/"
|
||||
plugins:
|
||||
- jekyll-paginate
|
||||
- jekyll-autoprefixer
|
||||
- jekyll-inline-svg
|
||||
- jekyll-relative-links
|
||||
- jekyll-feed
|
||||
site_url: https://gvisor.dev
|
||||
feed:
|
||||
path: blog/index.xml
|
||||
svg:
|
||||
optimize: true
|
||||
defaults:
|
||||
- scope:
|
||||
path: ""
|
||||
values:
|
||||
layout: default
|
||||
analytics: "UA-150193582-1"
|
||||
authors:
|
||||
jsprad:
|
||||
name: Jeremiah Spradlin
|
||||
email: [email protected]
|
||||
zkoopmans:
|
||||
name: Zach Koopmans
|
||||
email: [email protected]
|
||||
igudger:
|
||||
name: Ian Gudger
|
||||
email: [email protected]
|
||||
@@ -0,0 +1,18 @@
|
||||
By
|
||||
{% assign last_pos=include.authors.size | minus: 1 %}
|
||||
{% assign and_pos=include.authors.size | minus: 2 %}
|
||||
{% for i in (0..last_pos) %}
|
||||
{% assign author_id=include.authors[i] %}
|
||||
{% assign author=site.authors[author_id] %}
|
||||
{% if author %}
|
||||
<a href="mailto:{{ author.email }}">{{ author.name }}</a>
|
||||
{% else %}
|
||||
{{ author_id }}
|
||||
{% endif %}
|
||||
{% if i == and_pos %}
|
||||
and
|
||||
{% elsif i < and_pos %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
on <span class="text-muted">{{ include.date | date_to_long_string }}</span>
|
||||
@@ -0,0 +1,43 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<p>About</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="/roadmap/">Roadmap</a></li>
|
||||
<li><a href="/contributing/">Contributing</a></li>
|
||||
<li><a href="/security/">Security</a></li>
|
||||
<li><a href="/community/governance/">Governance</a></li>
|
||||
<li><a href="https://policies.google.com/privacy">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<p>Support</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://github.com/google/gvisor/issues">Issues</a></li>
|
||||
<li><a href="/docs">Documentation</a></li>
|
||||
<li><a href="/docs/user_guide/FAQ">FAQ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<p>Connect</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://github.com/google/gvisor">GitHub</a></li>
|
||||
<li><a href="https://groups.google.com/forum/#!forum/gvisor-users">User Mailing List</a></li>
|
||||
<li><a href="https://groups.google.com/forum/#!forum/gvisor-dev">Developer Mailing List</a></li>
|
||||
<li><a href="https://gitter.im/gvisor/community">Gitter Chat</a></li>
|
||||
<li><a href="/blog">Blog</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-3"></div>
|
||||
<div class="hidden-xs hidden-sm col-md-3">
|
||||
<a href="https://cloud.google.com/run">
|
||||
<img style="float: right;" src="/assets/logos/powered-gvisor.png" alt="Powered by gVisor"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p>© {{ 'now' | date: "%Y" }} The gVisor Authors</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,72 @@
|
||||
<footer class="footer">
|
||||
{% include footer-links.html %}
|
||||
</footer>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.1/js/all.min.js" integrity="sha256-Z1Nvg/+y2+vRFhFgFij7Lv0r77yG3hOvWz2wI0SfTa0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js" integrity="sha256-hYXbQJK4qdJiAeDVjjQ9G0D6A0xLnDQ4eJI9dkm7Fpk=" crossorigin="anonymous"></script>
|
||||
|
||||
{% if site.analytics %}
|
||||
<script type="application/javascript">
|
||||
var doNotTrack = false;
|
||||
if (!doNotTrack) {
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', '{{ site.analytics }}', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
var shiftWindow = function() {
|
||||
if (location.hash.length !== 0) {
|
||||
window.scrollBy(0, -50);
|
||||
}
|
||||
};
|
||||
window.addEventListener("hashchange", shiftWindow);
|
||||
|
||||
var highlightCurrentSidebarNav = function() {
|
||||
var href = location.pathname;
|
||||
var item = $('#sidebar-nav [href$="' + href + '"]');
|
||||
if (item) {
|
||||
var li = item.parent();
|
||||
li.addClass("active");
|
||||
|
||||
if (li.parent() && li.parent().is("ul")) {
|
||||
do {
|
||||
var ul = li.parent();
|
||||
if (ul.hasClass("collapse")) {
|
||||
ul.collapse("show");
|
||||
}
|
||||
li = ul.parent();
|
||||
} while (li && li.is("li"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
// Scroll to anchor of location hash, adjusted for fixed navbar.
|
||||
window.setTimeout(function() {
|
||||
shiftWindow();
|
||||
}, 1);
|
||||
|
||||
// Flip the caret when submenu toggles are clicked.
|
||||
$(".sidebar-submenu").on("show.bs.collapse", function() {
|
||||
var toggle = $('[href$="#' + $(this).attr('id') + '"]');
|
||||
if (toggle) {
|
||||
toggle.addClass("dropup");
|
||||
}
|
||||
});
|
||||
$(".sidebar-submenu").on("hide.bs.collapse", function() {
|
||||
var toggle = $('[href$="#' + $(this).attr('id') + '"]');
|
||||
if (toggle) {
|
||||
toggle.removeClass("dropup");
|
||||
}
|
||||
});
|
||||
|
||||
// Highlight the current page on the sidebar nav.
|
||||
highlightCurrentSidebarNav();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,205 @@
|
||||
{::nomarkdown}
|
||||
{% assign fn = include.id | remove: " " | remove: "-" | downcase %}
|
||||
<figure><a href="{{ include.url }}"><svg id="{{ include.id }}" width=500 height=200 onload="render_{{ fn }}()"><title>{{ include.title }}</title></svg></a></figure>
|
||||
<script type="text/javascript">
|
||||
function render_{{ fn }}() {
|
||||
d3.csv("{{ include.url }}", function(d, i, columns) {
|
||||
return d; // Transformed below.
|
||||
}, function(error, data) {
|
||||
if (error) throw(error);
|
||||
|
||||
// Create a new data that pivots on runtime.
|
||||
//
|
||||
// To start, we have:
|
||||
// runtime, ..., result
|
||||
// runc, ..., 1
|
||||
// runsc, ..., 2
|
||||
//
|
||||
// In the end we want:
|
||||
// ..., runsc, runc
|
||||
// ..., 1, 2
|
||||
|
||||
// Filter by metric, if required.
|
||||
if ("{{ include.metric }}" != "") {
|
||||
orig_columns = data.columns;
|
||||
data = data.filter(d => d.metric == "{{ include.metric }}");
|
||||
data.columns = orig_columns;
|
||||
}
|
||||
|
||||
// Filter by method, if required.
|
||||
if ("{{ include.method }}" != "") {
|
||||
orig_columns = data.columns;
|
||||
data = data.filter(d => d.method == "{{ include.method }}");
|
||||
data.columns = orig_columns.filter(key => key != "method");
|
||||
}
|
||||
|
||||
// Enumerate runtimes.
|
||||
var runtimes = Array.from(new Set(data.map(d => d.runtime)));
|
||||
var metrics = Array.from(new Set(data.map(d => d.metric)));
|
||||
if (metrics.length < 1) {
|
||||
console.log(data);
|
||||
throw("need at least one metric");
|
||||
} else if (metrics.length == 1) {
|
||||
metric = metrics[0];
|
||||
data.columns = data.columns.filter(key => key != "metric");
|
||||
} else {
|
||||
metric = ""; // Used for grouping.
|
||||
}
|
||||
|
||||
var isSubset = function(a, sup) {
|
||||
var ap = Object.getOwnPropertyNames(a);
|
||||
for (var i = 0; i < ap.length; i++) {
|
||||
if (a[ap[i]] !== sup[ap[i]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Execute a pivot to include runtimes as attributes.
|
||||
var new_data = data.map(function(data_item) {
|
||||
// Generate a prototype data item.
|
||||
var proto_item = Object.assign({}, data_item);
|
||||
delete proto_item.runtime;
|
||||
delete proto_item.result;
|
||||
var next_item = Object.assign({}, proto_item);
|
||||
|
||||
// Find all matching runtime items.
|
||||
data.forEach(function(d) {
|
||||
if (isSubset(proto_item, d)) {
|
||||
// Add the result result.
|
||||
next_item[d.runtime] = d.result;
|
||||
}
|
||||
});
|
||||
return next_item;
|
||||
});
|
||||
|
||||
// Remove any duplication.
|
||||
new_data = Array.from(new Set(new_data));
|
||||
new_data.columns = data.columns;
|
||||
new_data.columns = new_data.columns.filter(key => key != "runtime" && key != "result");
|
||||
new_data.columns = new_data.columns.concat(runtimes);
|
||||
data = new_data;
|
||||
|
||||
// Slice based on the first key.
|
||||
if (data.columns.length != runtimes.length) {
|
||||
x0_key = new_data.columns[0];
|
||||
var x1_domain = data.columns.slice(1);
|
||||
} else {
|
||||
x0_key = "runtime";
|
||||
var x1_domain = runtimes;
|
||||
}
|
||||
|
||||
// Determine varaible margins.
|
||||
var x0_domain = data.map(d => d[x0_key]);
|
||||
var margin_bottom_pad = 0;
|
||||
if (x0_domain.length > 8) {
|
||||
margin_bottom_pad = 50;
|
||||
}
|
||||
|
||||
// Use log scale if required.
|
||||
var y_min = 0;
|
||||
if ({{ include.log | default: "false" }}) {
|
||||
// Need to cap lower end of the domain at 1.
|
||||
y_min = 1;
|
||||
}
|
||||
|
||||
if ({{ include.y_min | default: "false" }}) {
|
||||
y_min = "{{ include.y_min }}";
|
||||
}
|
||||
|
||||
var svg = d3.select("#{{ include.id }}"),
|
||||
margin = {top: 20, right: 20, bottom: 30 + margin_bottom_pad, left: 50},
|
||||
width = +svg.attr("width") - margin.left - margin.right,
|
||||
height = +svg.attr("height") - margin.top - margin.bottom,
|
||||
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var x0 = d3.scaleBand()
|
||||
.rangeRound([margin.left / 2, width - (4 * margin.right)])
|
||||
.paddingInner(0.1);
|
||||
|
||||
var x1 = d3.scaleBand()
|
||||
.padding(0.05);
|
||||
|
||||
var y = d3.scaleLinear()
|
||||
.rangeRound([height, 0]);
|
||||
if ({{ include.log | default: "false" }}) {
|
||||
y = d3.scaleLog()
|
||||
.rangeRound([height, 0]);
|
||||
}
|
||||
|
||||
var z = d3.scaleOrdinal()
|
||||
.range(["#262362", "#FBB03B", "#286FD7", "#6b486b"]);
|
||||
|
||||
// Set all domains.
|
||||
x0.domain(x0_domain);
|
||||
x1.domain(x1_domain).rangeRound([0, x0.bandwidth()]);
|
||||
y.domain([y_min, d3.max(data, d => d3.max(x1_domain, key => parseFloat(d[key])))]).nice();
|
||||
|
||||
// The data.
|
||||
g.append("g")
|
||||
.selectAll("g")
|
||||
.data(data)
|
||||
.enter().append("g")
|
||||
.attr("transform", function(d) { return "translate(" + x0(d[x0_key]) + ",0)"; })
|
||||
.selectAll("rect")
|
||||
.data(d => x1_domain.map(key => ({key, value: d[key]})))
|
||||
.enter().append("rect")
|
||||
.attr("x", d => x1(d.key))
|
||||
.attr("y", d => y(d.value))
|
||||
.attr("width", x1.bandwidth())
|
||||
.attr("height", d => y(y_min) - y(d.value))
|
||||
.attr("fill", d => z(d.key));
|
||||
|
||||
// X0 ticks and labels.
|
||||
var x0_axis = g.append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(d3.axisBottom(x0));
|
||||
if (x0_domain.length > 8) {
|
||||
x0_axis.selectAll("text")
|
||||
.style("text-anchor", "end")
|
||||
.attr("dx", "-.8em")
|
||||
.attr("dy", ".15em")
|
||||
.attr("transform", "rotate(-65)");
|
||||
}
|
||||
|
||||
// Y ticks and top-label.
|
||||
if (metric == "default") {
|
||||
metric = ""; // Don't display.
|
||||
}
|
||||
g.append("g")
|
||||
.attr("class", "axis")
|
||||
.call(d3.axisLeft(y).ticks(null, "s"))
|
||||
.append("text")
|
||||
.attr("x", -30.0)
|
||||
.attr("y", y(y.ticks().pop()) - 10.0)
|
||||
.attr("dy", "0.32em")
|
||||
.attr("fill", "#000")
|
||||
.attr("font-weight", "bold")
|
||||
.attr("text-anchor", "start")
|
||||
.text(metric);
|
||||
|
||||
// The legend.
|
||||
var legend = g.append("g")
|
||||
.attr("font-family", "sans-serif")
|
||||
.attr("font-size", 10)
|
||||
.attr("text-anchor", "end")
|
||||
.selectAll("g")
|
||||
.data(x1_domain.slice().reverse())
|
||||
.enter().append("g")
|
||||
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
|
||||
legend.append("rect")
|
||||
.attr("x", width - 19)
|
||||
.attr("width", 19)
|
||||
.attr("height", 19)
|
||||
.attr("fill", z);
|
||||
legend.append("text")
|
||||
.attr("x", width - 24)
|
||||
.attr("y", 9.5)
|
||||
.attr("dy", "0.32em")
|
||||
.text(function(d) { return d; });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{:/}
|
||||
@@ -0,0 +1,19 @@
|
||||
<nav class="navbar navbar-expand-sm navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a href="/">
|
||||
<img src="/assets/logos/logo_solo_on_dark.svg" height="25px" class="d-inline-block align-top" style="margin-right: 10px;" alt="logo"/>
|
||||
gVisor
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="/docs">Documentation</a></li>
|
||||
<li><a href="/blog">Blog</a></li>
|
||||
<li><a href="/community/">Community</a></li>
|
||||
<li><a href="https://github.com/google/gvisor">GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1,30 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{% if page.title %}
|
||||
<title>{{ page.title }} - gVisor</title>
|
||||
{% else %}
|
||||
<title>gVisor</title>
|
||||
{% endif %}
|
||||
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site_root }}">
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.1/css/all.min.css" integrity="sha256-fdcFNFiBMrNfWL6OcAGQz6jDgNTRxnrLEd4vJYFWScE=" crossorigin="anonymous" />
|
||||
|
||||
<!-- Our own style sheet. -->
|
||||
<link rel="stylesheet" type="text/css" href="/css/main.css">
|
||||
<link rel="icon" type="image/png" href="/assets/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/assets/favicons/favicon-16x16.png" sizes="16x16">
|
||||
|
||||
{% if page.title %}
|
||||
<meta name="og:title" content="{{ page.title }}">
|
||||
{% else %}
|
||||
<meta name="og:title" content="gVisor">
|
||||
{% endif %}
|
||||
{% if page.description %}
|
||||
<meta name="og:description" content="{{ page.description }}">
|
||||
{% endif %}
|
||||
<meta name="og:image" content="{{ site.site_url }}/assets/logos/logo_solo_on_white_bordered.svg">
|
||||
</head>
|
||||
@@ -0,0 +1,10 @@
|
||||
<nav aria-label="...">
|
||||
<ul class="pager">
|
||||
{% if paginator.previous_page %}
|
||||
<li class="previous"><a href="{{ paginator.previous_page_path }}"><span aria-hidden="true">←</span> Newer</a></li>
|
||||
{% endif %}
|
||||
{% if paginator.next_page %}
|
||||
<li class="next"><a href="{{ paginator.next_page_path }}">Older <span aria-hidden="true">→</span></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -0,0 +1,2 @@
|
||||
> Note: gVisor supports only x86\_64 and requires Linux 4.14.77+
|
||||
> ([older Linux](/docs/user_guide/networking/#gso)).
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/WebPage">
|
||||
{% include header.html %}
|
||||
<body>
|
||||
{% include header-links.html %}
|
||||
{{ content }}
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: base
|
||||
---
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-2"></div>
|
||||
<div class="col-lg-8">
|
||||
<h1>{{ page.title }}</h1>
|
||||
{% if page.feed %}
|
||||
<a class="btn-inverse" href="/blog/index.xml">Feed <i class="fas fa-rss ml-2"></i></a>
|
||||
{% endif %}
|
||||
{{ content }}
|
||||
</div>
|
||||
<div class="col-lg-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: base
|
||||
---
|
||||
{% if page.title %}
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>{{ page.title }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="container">
|
||||
{{ content }}
|
||||
</div>
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
layout: base
|
||||
categories:
|
||||
- Project
|
||||
- User Guide
|
||||
- Architecture Guide
|
||||
- Compatibility
|
||||
---
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<nav class="sidebar" id="sidebar-nav">
|
||||
{% for category in layout.categories %}
|
||||
<h3>{{ category }}</h3>
|
||||
<ul class="sidebar-nav">
|
||||
{% assign sorted_pages = site.pages | where: 'layout', 'docs' | where: 'category', category | sort: 'weight' | sort: 'subcategory' %}
|
||||
{% assign subcategory = nil %}
|
||||
{% for p in sorted_pages %}
|
||||
{% if p.subcategory != subcategory %}
|
||||
{% if subcategory != nil %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% assign subcategory = p.subcategory %}
|
||||
{% if subcategory != nil %}
|
||||
{% assign ac = "aria-controls" %}
|
||||
{% assign cid = p.category | remove: " " | downcase %}
|
||||
{% assign sid = p.subcategory | remove: " " | downcase %}
|
||||
<li>
|
||||
<a class="sidebar-nav-heading" data-toggle="collapse" href="#{{ cid }}-{{ sid }}" aria-expanded="false" {{ ac }}="{{ cid }}-{{ sid }}">{{ subcategory }}<span class="caret"></span></a>
|
||||
<ul class="collapse sidebar-nav sidebar-submenu" id="{{ cid }}-{{ sid }}">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li><a href="{{ p.url }}">{{ p.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% if subcategory != nil %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<h1>{{ page.title }}</h1>
|
||||
{% if page.editpath %}
|
||||
<p>
|
||||
<a href="https://github.com/google/gvisor/edit/master/content/{{page.editpath}}" target="_blank"><i class="fa fa-edit fa-fw"></i> Edit this page</a>
|
||||
<a href="https://github.com/google/gvisor/issues/new?title={{page.title | url_encode}}" target="_blank"><i class="fab fa-github fa-fw"></i> Create issue</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: blog
|
||||
---
|
||||
|
||||
<div class="blog-meta">
|
||||
{% include byline.html authors=page.authors date=page.date %}
|
||||
</div>
|
||||
<div class="blog-content">
|
||||
{{ content }}
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
require 'webrick'
|
||||
include WEBrick
|
||||
WEBrick::HTTPUtils::DefaultMimeTypes.store 'svg', 'image/svg+xml'
|
||||
@@ -0,0 +1,15 @@
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
|
||||
a {
|
||||
color: $inverse-link-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $inverse-link-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.jumbotron {
|
||||
background-image: url(/assets/images/background.jpg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-blend-mode: darken;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.navbar-inverse {
|
||||
background-color: $primary;
|
||||
border-bottom: 1px solid $primary;
|
||||
|
||||
.navbar-brand > a {
|
||||
color: #fff;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav > li > a {
|
||||
color: $inverse-link-color;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: $inverse-link-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav .nav-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
$sidebar-border-color: #fff;
|
||||
$sidebar-hover-border-color: #66bb6a;
|
||||
|
||||
.sidebar {
|
||||
margin-top: 40px;
|
||||
|
||||
ul.sidebar-nav {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
transition: height 0.01s;
|
||||
|
||||
li {
|
||||
&.sidebar-nav-heading {
|
||||
padding: 10px 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 4px 0;
|
||||
display: block;
|
||||
border-right: 2px solid $sidebar-border-color;
|
||||
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.caret {
|
||||
float: right;
|
||||
margin-top: 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
a {
|
||||
border-left: 2px solid $sidebar-hover-border-color;
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.sidebar-nav {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.sidebar-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
&.collapse {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
$primary: #262362;
|
||||
$secondary: #fff;
|
||||
$link-color: #286fd7;
|
||||
$inverse-link-color: #fff;
|
||||
|
||||
$link-hover-color: darken($link-color, 10%);
|
||||
$inverse-link-hover-color: darken($inverse-link-color, 10%);
|
||||
|
||||
$text-color: #444;
|
||||
|
||||
$body-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$code-font-family: 'Source Code Pro', monospace;
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: $text-color;
|
||||
font-family: $body-font-family;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $link-hover-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
code {
|
||||
color: $link-color;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: $text-color;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h1 code,
|
||||
h2 code,
|
||||
h3 code,
|
||||
h4 code,
|
||||
h5 code,
|
||||
h6 code {
|
||||
color: $text-color;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
code {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
code {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
code {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $code-font-family;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: $text-color;
|
||||
background-color: $inverse-link-color;
|
||||
}
|
||||
|
||||
.btn-inverse {
|
||||
color: $text-color;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.well {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table td,
|
||||
table th {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
table tr:nth-child(even) {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
table th {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
background-color: $primary;
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
.blog-meta {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF0meAYBEACcBYPOSBiKtid+qTQlbgKGPxUYt0cNZiQqWXylhYUT4PuNlNx5
|
||||
s+sBLFvNTpdTrXMmZ8NkekyjD1HardWvebvJT4u+Ho/9jUr4rP71cNwNtocz/w8G
|
||||
DsUXSLgH8SDkq6xw0L+5eGc78BBg9cOeBeFBm3UPgxTBXS9Zevoi2w1lzSxkXvjx
|
||||
cGzltzMZfPXERljgLzp9AAfhg/2ouqVQm37fY+P/NDzFMJ1XHPIIp9KJl/prBVud
|
||||
jJJteFZ5sgL6MwjBQq2kw+q2Jb8Zfjl0BeXDgGMN5M5lGhX2wTfiMbfo7KWyzRnB
|
||||
RpSP3BxlLqYeQUuLG5Yx8z3oA3uBkuKaFOKvXtiScxmGM/+Ri2YM3m66imwDhtmP
|
||||
AKwTPI3Re4gWWOffglMVSv2sUAY32XZ74yXjY1VhK3bN3WFUPGrgQx4X7GP0A1Te
|
||||
lzqkT3VSMXieImTASosK5L5Q8rryvgCeI9tQLn9EpYFCtU3LXvVgTreGNEEjMOnL
|
||||
dR7yOU+Fs775stn6ucqmdYarx7CvKUrNAhgEeHMonLe1cjYScF7NfLO1GIrQKJR2
|
||||
DE0f+uJZ52inOkO8ufh3WVQJSYszuS3HCY7w5oj1aP38k/y9zZdZvVvwAWZaiqBQ
|
||||
iwjVs6Kub76VVZZhRDf4iYs8k1Zh64nXdfQt250d8U5yMPF3wIJ+c1yhxwARAQAB
|
||||
tCpUaGUgZ1Zpc29yIEF1dGhvcnMgPGd2aXNvci1ib3RAZ29vZ2xlLmNvbT6JAlQE
|
||||
EwEKAD4WIQRvHfheOnHCSRjnJ9VvxtVU4yvZQwUCXSZ4BgIbAwUJA8JnAAULCQgH
|
||||
AgYVCgkICwIEFgIDAQIeAQIXgAAKCRBvxtVU4yvZQ5WFD/9VZXMW5I2rKV+2gTHT
|
||||
CsW74kZVi1VFdAVYiUJZXw2jJNtcg3xdgBcscYPyecyka/6TS2q7q2fOGAzCZkcR
|
||||
e3lLzkGAngMlZ7PdHAE0PDMNFaeMZW0dxNH68vn7AiA1y2XwENnxVec7iXQH6aX5
|
||||
xUNg2OCiv5f6DJItHc/Q4SvFUi8QK7TT/GYE1RJXVJlLqfO6y4V8SeqfM+FHpHZM
|
||||
gzrwdTgsNiEm4lMjWcgb2Ib4i2JUVAjIRPfcpysiV5E7c3SPXyu4bOovKKlbhiJ1
|
||||
Q1M9M0zHik34Kjf4YNO1EW936j7Msd181CJt5Bl9XvlhPb8gey/ygpIvcicLx6M5
|
||||
lRJTy4z1TtkmtZ7E8EbJZWoPTaHlA6hoMtGeE35j3vMZN1qZYaYt26eFOxxhh7PA
|
||||
J0h1lS7T2O8u1c2JKhKvajtdmbqbJgI8FRhVsMoVBnqDK5aE9MOAso36OibfweEL
|
||||
8iV2z8JnBpWtbbUEaWro4knPtbLJbQFvXVietm3cFsbGg+DMIwI6x6HcU91IEFYI
|
||||
Sv4orK7xgLuM+f6dxo/Wel3ht18dg3x3krBLALTYBidRfnQYYR3sTfLquB8b5WaY
|
||||
o829L2Bop9GBygdLevkHHN5It6q8CVpn0H5HEJMNaDOX1LcPbf0CKwkkAVCBd9YZ
|
||||
eAX38ds9LliK7XPXdC4c+zEkGA==
|
||||
=x8TG
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 926 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
srcs := $(wildcard *.svg)
|
||||
dsts := $(patsubst %.svg,%.png,$(srcs))
|
||||
|
||||
all: $(dsts)
|
||||
.PHONY: all
|
||||
|
||||
%.png %-16.png %-128.png %-1024.png: %.svg
|
||||
@inkscape -z -e $*.png $<
|
||||
@inkscape -z -w 16 -e $*-16.png $<
|
||||
@inkscape -z -w 128 -e $*-128.png $<
|
||||
@inkscape -z -w 1024 -e $*-1024.png $<
|
||||
@@ -0,0 +1,10 @@
|
||||
# Logos
|
||||
|
||||
This directory contains logo assets.
|
||||
|
||||
The colors used are:
|
||||
|
||||
* Background (blue): #262262
|
||||
* Highlight (yellow): #FBB03B
|
||||
|
||||
Use `make` to generate sized PNGs from SVGs.
|
||||
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="175.35599"
|
||||
height="193.20036"
|
||||
viewBox="0 0 175.35599 193.20036"
|
||||
sodipodi:docname="logo_solo_monochrome.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath20"><path
|
||||
d="M 0,821.614 H 1366 V 0 H 0 Z"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.43085925"
|
||||
inkscape:cx="374.99057"
|
||||
inkscape:cy="88.483321"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="9"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="gvisor_final-logo_20190313"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,-614.45037,638.9628)"><g
|
||||
id="g48"
|
||||
transform="translate(548.2423,363.2485)"><path
|
||||
d="m 0,0 c 16.655,21.121 22.696,44.433 18.328,70.995 3.068,0 5.743,-0.023 8.417,0.007 2.222,0.025 4.443,0.102 6.664,0.175 4.79,0.154 4.818,0.165 5.88,-4.582 3.145,-14.051 2.18,-28.09 -0.179,-42.118 -0.25,-1.492 -0.7,-2.956 -0.864,-4.454 C 37.05,9.081 30.089,3.645 20.165,1.097 13.787,-0.54 7.323,-0.829 0,0"
|
||||
style="fill:#262262;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g52"
|
||||
transform="translate(544.6891,396.1763)"><path
|
||||
d="M 0,0 C 0,2.594 -3.457,4.322 -3.457,4.322 -0.864,5.187 0,8.644 0,8.644 0,8.644 0.865,5.187 3.458,4.322 3.458,4.322 0,2.594 0,0 m -17.099,6.454 c 0,6.742 -8.989,11.236 -8.989,11.236 6.742,2.248 8.989,11.238 8.989,11.238 0,0 2.247,-8.99 8.99,-11.238 0,0 -8.99,-4.494 -8.99,-11.236"
|
||||
style="fill:#262262;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path54"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g56"
|
||||
transform="translate(485.0861,429.7925)"><path
|
||||
d="m 0,0 c 0,0 -2.214,3.359 -9.736,2.059 0,0 5.987,28.738 36.298,38.806 C 26.562,40.865 -2.82,24.275 0,0 M 0.583,-33.208 0.58,-33.211 c -1.297,-2.026 -2.821,-3.579 -4.53,-4.616 -1.515,-0.933 -3.178,-1.425 -4.743,-1.425 -0.528,0 -1.044,0.057 -1.539,0.17 l -2.209,0.507 2.184,0.603 c 1.63,0.451 3.063,1.347 4.259,2.664 1.014,1.108 1.856,2.485 2.577,4.213 1.175,2.82 1.784,6.162 1.81,9.936 -0.049,3.718 -0.683,7.054 -1.886,9.902 -0.703,1.654 -1.585,3.056 -2.621,4.163 -1.227,1.311 -2.671,2.178 -4.294,2.576 l -2.187,0.538 2.179,0.572 c 0.48,0.126 0.998,0.196 1.539,0.209 h 0.031 l 1.688,-0.153 c 1.045,-0.206 2.104,-0.616 3.061,-1.185 1.755,-1.031 3.302,-2.567 4.598,-4.565 2.155,-3.374 3.315,-7.536 3.357,-12.042 -0.028,-4.548 -1.159,-8.717 -3.271,-12.064 m 101.949,51.176 c 0,0 -0.075,0.05 -0.226,0.136 -0.541,0.327 -1.113,0.603 -1.715,0.815 -3.044,1.241 -9.881,3.186 -21.906,2.623 -0.029,0 -0.056,0 -0.085,-0.001 C 52.916,21.123 30.022,10.922 30.022,10.922 c 0,0 1.439,1.761 3.453,3.692 10e-4,0 10e-4,10e-4 0.002,10e-4 1.052,0.974 2.355,2.076 3.912,3.227 0.046,0.031 0.088,0.063 0.124,0.093 8.708,6.384 25.34,14.163 51.625,9.541 -0.989,1.124 -2.002,2.192 -3.036,3.215 -1.112,0.883 -2.231,1.693 -3.354,2.456 0.02,-0.012 0.039,-0.023 0.059,-0.036 0,0 -17.016,19.415 -48.683,15.891 C 30.19,48.622 25.983,47.867 21.66,46.564 21.653,46.563 21.646,46.562 21.64,46.56 L 21.638,46.558 C 11.48,43.492 0.683,37.387 -8.719,25.911 -9.29,25.193 -9.841,24.479 -10.376,23.77 c -0.065,-0.099 -0.141,-0.202 -0.226,-0.307 -1.182,-1.581 -2.271,-3.14 -3.279,-4.674 -3.266,-5.427 -5.631,-11.665 -6.311,-13.545 -10.58,-32.401 2.586,-57.55 5.144,-61.967 8.93,-15.158 24.565,-32.355 50.771,-37.327 0.197,-0.047 0.382,-0.101 0.582,-0.147 1.723,-0.367 4.864,-0.929 8.908,-1.196 1.524,-0.069 3.088,-0.094 4.699,-0.067 1.548,-0.009 2.999,0.017 4.335,0.064 0.396,0.028 0.74,0.041 1.044,0.044 5.102,0.238 8.272,0.775 8.272,0.775 -26.543,1.299 -39.847,13.409 -45.691,21.142 -1.325,1.648 -2.46,3.42 -3.377,5.316 -0.361,0.71 -0.523,1.115 -0.523,1.115 8.459,-7.181 20.294,-13.362 20.294,-13.362 10.611,-4.993 21.737,-7.451 33.524,-5.837 0,0 24.645,2.263 34.464,25.09 -0.423,0.322 -0.366,0.278 -0.79,0.6 -0.652,-0.526 -1.294,-1.023 -1.926,-1.496 -0.991,-0.651 -1.964,-1.357 -2.937,-2.07 -5.265,-3.485 -9.561,-5.128 -12.12,-5.879 -19.359,-4.887 -37.273,-1.252 -52.93,12.455 -7.253,6.349 -11.754,14.352 -11.944,24.291 -0.124,6.465 -0.19,12.935 -0.136,19.4 0.085,10.181 7.246,17.921 17.394,19.284 20.561,2.759 41.234,3.71 61.947,4.193 3.174,0.073 5.981,0.037 7.458,-3.356 0.025,-0.058 0.363,0.02 0.552,0.035 1.4,4.461 -0.617,9.27 -4.29,11.624"
|
||||
style="fill:#262262;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path58"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="175.35599"
|
||||
height="193.19984"
|
||||
viewBox="0 0 175.35599 193.19985"
|
||||
sodipodi:docname="logo_solo_on_dark.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath20"><path
|
||||
d="M 0,821.614 H 1366 V 0 H 0 Z"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1278"
|
||||
inkscape:window-height="699"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.8617185"
|
||||
inkscape:cx="257.20407"
|
||||
inkscape:cy="172.193"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="gvisor_final-logo_20190313"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,-614.45037,638.96254)"><g
|
||||
id="g48"
|
||||
transform="translate(548.2423,363.2484)"><path
|
||||
d="m 0,0 c 16.655,21.121 22.696,44.434 18.328,70.995 3.068,0 5.743,-0.023 8.417,0.007 2.222,0.025 4.443,0.102 6.664,0.175 4.79,0.154 4.818,0.165 5.88,-4.582 3.145,-14.05 2.18,-28.089 -0.179,-42.118 -0.25,-1.492 -0.7,-2.956 -0.864,-4.454 C 37.05,9.081 30.089,3.645 20.165,1.097 13.787,-0.54 7.323,-0.829 0,0"
|
||||
style="fill:#fbb03b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g52"
|
||||
transform="translate(544.6891,396.1771)"><path
|
||||
d="M 0,0 C 0,2.593 -3.457,4.321 -3.457,4.321 -0.864,5.186 0,8.644 0,8.644 0,8.644 0.865,5.186 3.458,4.321 3.458,4.321 0,2.593 0,0 m -17.099,6.453 c 0,6.742 -8.989,11.237 -8.989,11.237 6.742,2.247 8.989,11.237 8.989,11.237 0,0 2.247,-8.99 8.99,-11.237 0,0 -8.99,-4.495 -8.99,-11.237"
|
||||
style="fill:#fbb03b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path54"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g56"
|
||||
transform="translate(485.0861,429.7923)"><path
|
||||
d="m 0,0 c 0,0 -2.214,3.359 -9.736,2.059 0,0 5.987,28.738 36.298,38.806 C 26.562,40.865 -2.82,24.275 0,0 M 0.583,-33.208 0.58,-33.211 c -1.297,-2.026 -2.821,-3.578 -4.53,-4.615 -1.515,-0.934 -3.178,-1.425 -4.743,-1.425 -0.528,0 -1.044,0.056 -1.539,0.169 l -2.209,0.507 2.184,0.603 c 1.63,0.451 3.063,1.347 4.259,2.664 1.014,1.108 1.856,2.485 2.577,4.214 1.175,2.819 1.784,6.161 1.81,9.935 -0.049,3.719 -0.683,7.054 -1.886,9.902 -0.703,1.655 -1.585,3.057 -2.621,4.163 -1.227,1.311 -2.671,2.178 -4.294,2.576 l -2.187,0.538 2.179,0.572 c 0.48,0.127 0.998,0.196 1.539,0.209 l 0.031,10e-4 1.688,-0.154 c 1.045,-0.206 2.104,-0.615 3.061,-1.184 1.755,-1.032 3.302,-2.568 4.598,-4.565 2.155,-3.374 3.315,-7.537 3.357,-12.043 -0.028,-4.548 -1.159,-8.717 -3.271,-12.064 m 101.949,51.176 c 0,0 -0.075,0.051 -0.226,0.137 -0.541,0.326 -1.113,0.602 -1.715,0.814 -3.044,1.241 -9.881,3.187 -21.906,2.623 -0.029,0 -0.056,0 -0.085,-0.001 C 52.916,21.123 30.022,10.923 30.022,10.923 c 0,0 1.439,1.76 3.453,3.691 10e-4,10e-4 10e-4,10e-4 0.002,0.002 1.052,0.973 2.355,2.076 3.912,3.226 0.046,0.032 0.088,0.063 0.124,0.094 8.708,6.383 25.34,14.162 51.625,9.54 -0.989,1.124 -2.002,2.193 -3.036,3.215 -1.112,0.884 -2.231,1.694 -3.354,2.456 0.02,-0.012 0.039,-0.023 0.059,-0.036 0,0 -17.016,19.415 -48.683,15.891 C 30.19,48.622 25.983,47.868 21.66,46.564 21.653,46.563 21.646,46.562 21.64,46.56 L 21.638,46.558 C 11.48,43.492 0.683,37.387 -8.719,25.911 c -0.571,-0.718 -1.122,-1.431 -1.657,-2.14 -0.065,-0.1 -0.141,-0.202 -0.226,-0.307 -1.182,-1.582 -2.271,-3.141 -3.279,-4.674 -3.266,-5.427 -5.631,-11.666 -6.311,-13.546 -10.58,-32.401 2.586,-57.549 5.144,-61.967 8.93,-15.157 24.565,-32.355 50.771,-37.327 0.197,-0.046 0.382,-0.101 0.582,-0.146 1.723,-0.367 4.864,-0.929 8.908,-1.197 1.524,-0.069 3.088,-0.094 4.699,-0.066 1.548,-0.01 2.999,0.017 4.335,0.064 0.396,0.027 0.74,0.041 1.044,0.044 5.102,0.237 8.272,0.774 8.272,0.774 -26.543,1.3 -39.847,13.41 -45.691,21.142 -1.325,1.648 -2.46,3.421 -3.377,5.316 -0.361,0.711 -0.523,1.115 -0.523,1.115 8.459,-7.18 20.294,-13.361 20.294,-13.361 10.611,-4.993 21.737,-7.452 33.524,-5.838 0,0 24.645,2.264 34.464,25.09 -0.423,0.322 -0.366,0.278 -0.79,0.6 -0.652,-0.526 -1.294,-1.022 -1.926,-1.496 -0.991,-0.65 -1.964,-1.357 -2.937,-2.07 -5.265,-3.485 -9.561,-5.127 -12.12,-5.879 -19.359,-4.887 -37.273,-1.252 -52.93,12.455 -7.253,6.349 -11.754,14.353 -11.944,24.291 -0.124,6.466 -0.19,12.935 -0.136,19.4 0.085,10.181 7.246,17.921 17.394,19.284 20.561,2.759 41.234,3.71 61.947,4.193 3.174,0.074 5.981,0.037 7.458,-3.356 0.025,-0.058 0.363,0.02 0.552,0.035 1.4,4.462 -0.617,9.27 -4.29,11.624"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path58"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 900 B |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="246.17325"
|
||||
height="246.17325"
|
||||
viewBox="0 0 246.17325 246.17326"
|
||||
sodipodi:docname="logo_solo_on_dark_full.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath20"><path
|
||||
d="M 0,821.614 H 1366 V 0 H 0 Z"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1278"
|
||||
inkscape:window-height="699"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.8617185"
|
||||
inkscape:cx="291.21659"
|
||||
inkscape:cy="198.28704"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="gvisor_final-logo_20190313"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,-580.43785,665.8419)"><circle
|
||||
id="path83"
|
||||
cx="527.64337"
|
||||
cy="-407.06647"
|
||||
r="92.314972"
|
||||
transform="scale(1,-1)"
|
||||
style="fill:#262262;fill-opacity:1;stroke-width:0.48076925" /><g
|
||||
id="g48"
|
||||
transform="translate(548.2423,363.2484)"><path
|
||||
d="m 0,0 c 16.655,21.121 22.696,44.434 18.328,70.995 3.068,0 5.743,-0.023 8.417,0.007 2.222,0.025 4.443,0.102 6.664,0.175 4.79,0.154 4.818,0.165 5.88,-4.582 3.145,-14.05 2.18,-28.089 -0.179,-42.118 -0.25,-1.492 -0.7,-2.956 -0.864,-4.454 C 37.05,9.081 30.089,3.645 20.165,1.097 13.787,-0.54 7.323,-0.829 0,0"
|
||||
style="fill:#fbb03b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g52"
|
||||
transform="translate(544.6891,396.1771)"><path
|
||||
d="M 0,0 C 0,2.593 -3.457,4.321 -3.457,4.321 -0.864,5.186 0,8.644 0,8.644 0,8.644 0.865,5.186 3.458,4.321 3.458,4.321 0,2.593 0,0 m -17.099,6.453 c 0,6.742 -8.989,11.237 -8.989,11.237 6.742,2.247 8.989,11.237 8.989,11.237 0,0 2.247,-8.99 8.99,-11.237 0,0 -8.99,-4.495 -8.99,-11.237"
|
||||
style="fill:#fbb03b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path54"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g56"
|
||||
transform="translate(485.0861,429.7923)"><path
|
||||
d="m 0,0 c 0,0 -2.214,3.359 -9.736,2.059 0,0 5.987,28.738 36.298,38.806 C 26.562,40.865 -2.82,24.275 0,0 M 0.583,-33.208 0.58,-33.211 c -1.297,-2.026 -2.821,-3.578 -4.53,-4.615 -1.515,-0.934 -3.178,-1.425 -4.743,-1.425 -0.528,0 -1.044,0.056 -1.539,0.169 l -2.209,0.507 2.184,0.603 c 1.63,0.451 3.063,1.347 4.259,2.664 1.014,1.108 1.856,2.485 2.577,4.214 1.175,2.819 1.784,6.161 1.81,9.935 -0.049,3.719 -0.683,7.054 -1.886,9.902 -0.703,1.655 -1.585,3.057 -2.621,4.163 -1.227,1.311 -2.671,2.178 -4.294,2.576 l -2.187,0.538 2.179,0.572 c 0.48,0.127 0.998,0.196 1.539,0.209 l 0.031,10e-4 1.688,-0.154 c 1.045,-0.206 2.104,-0.615 3.061,-1.184 1.755,-1.032 3.302,-2.568 4.598,-4.565 2.155,-3.374 3.315,-7.537 3.357,-12.043 -0.028,-4.548 -1.159,-8.717 -3.271,-12.064 m 101.949,51.176 c 0,0 -0.075,0.051 -0.226,0.137 -0.541,0.326 -1.113,0.602 -1.715,0.814 -3.044,1.241 -9.881,3.187 -21.906,2.623 -0.029,0 -0.056,0 -0.085,-0.001 C 52.916,21.123 30.022,10.923 30.022,10.923 c 0,0 1.439,1.76 3.453,3.691 10e-4,10e-4 10e-4,10e-4 0.002,0.002 1.052,0.973 2.355,2.076 3.912,3.226 0.046,0.032 0.088,0.063 0.124,0.094 8.708,6.383 25.34,14.162 51.625,9.54 -0.989,1.124 -2.002,2.193 -3.036,3.215 -1.112,0.884 -2.231,1.694 -3.354,2.456 0.02,-0.012 0.039,-0.023 0.059,-0.036 0,0 -17.016,19.415 -48.683,15.891 C 30.19,48.622 25.983,47.868 21.66,46.564 21.653,46.563 21.646,46.562 21.64,46.56 L 21.638,46.558 C 11.48,43.492 0.683,37.387 -8.719,25.911 c -0.571,-0.718 -1.122,-1.431 -1.657,-2.14 -0.065,-0.1 -0.141,-0.202 -0.226,-0.307 -1.182,-1.582 -2.271,-3.141 -3.279,-4.674 -3.266,-5.427 -5.631,-11.666 -6.311,-13.546 -10.58,-32.401 2.586,-57.549 5.144,-61.967 8.93,-15.157 24.565,-32.355 50.771,-37.327 0.197,-0.046 0.382,-0.101 0.582,-0.146 1.723,-0.367 4.864,-0.929 8.908,-1.197 1.524,-0.069 3.088,-0.094 4.699,-0.066 1.548,-0.01 2.999,0.017 4.335,0.064 0.396,0.027 0.74,0.041 1.044,0.044 5.102,0.237 8.272,0.774 8.272,0.774 -26.543,1.3 -39.847,13.41 -45.691,21.142 -1.325,1.648 -2.46,3.421 -3.377,5.316 -0.361,0.711 -0.523,1.115 -0.523,1.115 8.459,-7.18 20.294,-13.361 20.294,-13.361 10.611,-4.993 21.737,-7.452 33.524,-5.838 0,0 24.645,2.264 34.464,25.09 -0.423,0.322 -0.366,0.278 -0.79,0.6 -0.652,-0.526 -1.294,-1.022 -1.926,-1.496 -0.991,-0.65 -1.964,-1.357 -2.937,-2.07 -5.265,-3.485 -9.561,-5.127 -12.12,-5.879 -19.359,-4.887 -37.273,-1.252 -52.93,12.455 -7.253,6.349 -11.754,14.353 -11.944,24.291 -0.124,6.466 -0.19,12.935 -0.136,19.4 0.085,10.181 7.246,17.921 17.394,19.284 20.561,2.759 41.234,3.71 61.947,4.193 3.174,0.074 5.981,0.037 7.458,-3.356 0.025,-0.058 0.363,0.02 0.552,0.035 1.4,4.462 -0.617,9.27 -4.29,11.624"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path58"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 10 KiB |