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("//tools:defs.bzl", "build_test", "gazelle", "go_path")
|
||||||
|
load("//website:defs.bzl", "doc")
|
||||||
|
|
||||||
package(licenses = ["notice"])
|
package(licenses = ["notice"])
|
||||||
|
|
||||||
exports_files(["LICENSE"])
|
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.
|
# The sandbox filegroup is used for sandbox-internal dependencies.
|
||||||
package_group(
|
package_group(
|
||||||
name = "sandbox",
|
name = "sandbox",
|
||||||
|
|||||||
@@ -87,6 +87,5 @@ harassment or threats to anyone's safety, we may take action without notice.
|
|||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
|
This Code of Conduct is adapted from the
|
||||||
available at
|
[Contributor Covenant, version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html).
|
||||||
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
|
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
|
should conform to the [Google C++ Style Guide][cppstyle] and the guidelines
|
||||||
described for [tests][teststyle]. Note that code may be automatically formatted
|
described for tests. Note that code may be automatically formatted per the
|
||||||
per the guidelines when merged.
|
guidelines when merged.
|
||||||
|
|
||||||
As a secure runtime, we need to maintain the safety of all of code included in
|
As a secure runtime, we need to maintain the safety of all of code included in
|
||||||
gVisor. The following rules help mitigate issues.
|
gVisor. The following rules help mitigate issues.
|
||||||
@@ -131,4 +131,3 @@ one above, the
|
|||||||
[github]: https://github.com/google/gvisor/compare
|
[github]: https://github.com/google/gvisor/compare
|
||||||
[gvisor-dev-list]: https://groups.google.com/forum/#!forum/gvisor-dev
|
[gvisor-dev-list]: https://groups.google.com/forum/#!forum/gvisor-dev
|
||||||
[gostyle]: https://github.com/golang/go/wiki/CodeReviewComments
|
[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
|
.PHONY: runsc
|
||||||
|
|
||||||
smoke-test: ## Runs a simple smoke test after build 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
|
.PHONY: smoke-tests
|
||||||
|
|
||||||
unit-tests: ## Runs all unit tests in pkg runsc and tools.
|
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/..."
|
@$(MAKE) test OPTIONS="--test_tag_filter runsc_ptrace test/syscalls/..."
|
||||||
.PHONY: tests
|
.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.
|
## 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.
|
prompt response, typically within 48 hours.
|
||||||
|
|
||||||
Policies for security list access, vulnerability embargo, and vulnerability
|
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
|
[community]: https://gvisor.googlesource.com/community
|
||||||
[gvisor-security-list]: https://groups.google.com/forum/#!forum/gvisor-security
|
[gvisor-security-list]: https://groups.google.com/forum/#!forum/gvisor-security
|
||||||
|
|||||||
@@ -380,15 +380,15 @@ go_repository(
|
|||||||
go_repository(
|
go_repository(
|
||||||
name = "org_uber_go_atomic",
|
name = "org_uber_go_atomic",
|
||||||
importpath = "go.uber.org/atomic",
|
importpath = "go.uber.org/atomic",
|
||||||
version = "v1.6.0",
|
|
||||||
sum = "h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=",
|
sum = "h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=",
|
||||||
|
version = "v1.6.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
go_repository(
|
go_repository(
|
||||||
name = "org_uber_go_multierr",
|
name = "org_uber_go_multierr",
|
||||||
importpath = "go.uber.org/multierr",
|
importpath = "go.uber.org/multierr",
|
||||||
version = "v1.5.0",
|
|
||||||
sum = "h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=",
|
sum = "h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=",
|
||||||
|
version = "v1.5.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
# BigQuery Dependencies for Benchmarks
|
# 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
|
# What is gVisor?
|
||||||
Attribution-ShareAlike 4.0 International).
|
|
||||||
|
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
|
FROM fedora:31
|
||||||
|
# Install bazel.
|
||||||
RUN dnf install -y dnf-plugins-core && dnf copr enable -y vbatts/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 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 pip install pycparser
|
||||||
RUN dnf install -y bazel3
|
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 | \
|
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 && \
|
tar zxvf - google-cloud-sdk && \
|
||||||
google-cloud-sdk/install.sh && \
|
google-cloud-sdk/install.sh && \
|
||||||
ln -s /google-cloud-sdk/bin/gcloud /usr/bin/gcloud
|
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
|
WORKDIR /workspace
|
||||||
ENTRYPOINT ["/usr/bin/bazel"]
|
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).
|
# Bazel container configuration (see below).
|
||||||
USER ?= gvisor
|
USER ?= gvisor
|
||||||
DOCKER_NAME ?= gvisor-bazel-$(shell readlink -m $(CURDIR) | md5sum | cut -c1-8)
|
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/)
|
BAZEL_CACHE := $(shell readlink -m ~/.cache/bazel/)
|
||||||
GCLOUD_CONFIG := $(shell readlink -m ~/.config/gcloud/)
|
GCLOUD_CONFIG := $(shell readlink -m ~/.config/gcloud/)
|
||||||
DOCKER_SOCKET := /var/run/docker.sock
|
DOCKER_SOCKET := /var/run/docker.sock
|
||||||
@@ -30,10 +30,19 @@ DOCKER_SOCKET := /var/run/docker.sock
|
|||||||
# Non-configurable.
|
# Non-configurable.
|
||||||
UID := $(shell id -u ${USER})
|
UID := $(shell id -u ${USER})
|
||||||
GID := $(shell id -g ${USER})
|
GID := $(shell id -g ${USER})
|
||||||
|
USERADD_OPTIONS :=
|
||||||
FULL_DOCKER_RUN_OPTIONS := $(DOCKER_RUN_OPTIONS)
|
FULL_DOCKER_RUN_OPTIONS := $(DOCKER_RUN_OPTIONS)
|
||||||
FULL_DOCKER_RUN_OPTIONS += -v "$(BAZEL_CACHE):$(BAZEL_CACHE)"
|
FULL_DOCKER_RUN_OPTIONS += -v "$(BAZEL_CACHE):$(BAZEL_CACHE)"
|
||||||
FULL_DOCKER_RUN_OPTIONS += -v "$(GCLOUD_CONFIG):$(GCLOUD_CONFIG)"
|
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)"
|
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
|
SHELL=/bin/bash -o pipefail
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -53,15 +62,14 @@ bazel-server-start: load-default ## Starts the bazel server.
|
|||||||
docker run -d --rm \
|
docker run -d --rm \
|
||||||
--init \
|
--init \
|
||||||
--name $(DOCKER_NAME) \
|
--name $(DOCKER_NAME) \
|
||||||
--user 0:0 \
|
--user 0:0 $(DOCKER_GROUP_OPTIONS) \
|
||||||
-v "$(CURDIR):$(CURDIR)" \
|
-v "$(CURDIR):$(CURDIR)" \
|
||||||
--workdir "$(CURDIR)" \
|
--workdir "$(CURDIR)" \
|
||||||
--tmpfs /tmp:rw,exec \
|
|
||||||
--entrypoint "" \
|
--entrypoint "" \
|
||||||
$(FULL_DOCKER_RUN_OPTIONS) \
|
$(FULL_DOCKER_RUN_OPTIONS) \
|
||||||
gvisor.dev/images/default \
|
gvisor.dev/images/default \
|
||||||
sh -c "groupadd --gid $(GID) --non-unique $(USER) && \
|
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 && \
|
bazel version && \
|
||||||
exec tail --pid=\$$(bazel info server_pid) -f /dev/null"
|
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
|
@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 |