Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weeezes rootless + changes #42

Merged
merged 23 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
180e9d1
Add varlink socket path as a configurable setting
weeezes May 10, 2020
7722d64
Try to guess the socket path for the user in case socket_path hasn't …
weeezes Jun 19, 2020
e81ddc9
Inspect image data and set defaults based on it
weeezes Jun 18, 2020
97bb813
Set error message in case of container error
weeezes May 24, 2020
cac7a26
Search for catatonit from $PATH too
weeezes May 24, 2020
2c3c479
Don't require root at this point, rootless works too
weeezes May 24, 2020
51eea52
OOM test is flaky, make it fail more reliably
weeezes May 24, 2020
b228417
Link to busybox image used in the tests
weeezes May 29, 2020
b00b7bd
Handle allocation local directories
weeezes Jun 18, 2020
18dd24c
Set environment by using image default environment variables
weeezes Jun 18, 2020
068fb08
Set swappiness only if we're running on cgroupv1
weeezes Jun 18, 2020
595e238
Pull image if it doesn't exist locally
weeezes May 30, 2020
a12369b
Update readme to contain support for rootless, examples of entrypoint…
weeezes Jun 16, 2020
34f3705
Remove debug logs
weeezes Jun 29, 2020
490232c
fix bad merge conflict
drewbailey Jun 30, 2020
dd8cd32
keep env intact
drewbailey Jun 30, 2020
d1718eb
use img, fmt
drewbailey Jun 30, 2020
25389b8
fix log statements
drewbailey Jun 30, 2020
f55180a
use docker pattern for volumes
drewbailey Jun 30, 2020
f94a4ab
fix linting errors
drewbailey Jul 1, 2020
1e84ef6
go mod tidy
drewbailey Jul 1, 2020
d42e98a
noisy logs to trace
drewbailey Jul 1, 2020
af7d476
readme blurb for rootless ubuntu
drewbailey Jul 1, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ changelogfmt:
check: ## Lint the source code
@echo "==> Linting source code..."
@golangci-lint run
@echo "==> vetting hc-log statements"
@hclogvet .

.PHONY: lint-deps
lint-deps: ## Install linter dependencies
## Keep versions in sync with tools/go.mod (see https://github.com/golang/go/issues/30515)
@echo "==> Updating linter dependencies..."
GO111MODULE=on cd tools && go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.24.0
GO111MODULE=on cd tools && go get github.com/client9/misspell/cmd/misspell@v0.3.4
GO111MODULE=on cd tools && go get github.com/hashicorp/go-hclog/hclogvet@master
156 changes: 134 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ this plugin to Nomad!

## Features

* use the jobs driver config to define the image for your container
* start/stop containers with default or customer entrypoint and arguments
* Use the jobs driver config to define the image for your container
* Start/stop containers with default or customer entrypoint and arguments
* [Nomad runtime environment](https://www.nomadproject.io/docs/runtime/environment.html) is populated
* use nomad alloc data in the container.
* bind mount custom volumes into the container
* publish ports
* monitor the memory consumption
* monitor CPU usage
* task config cpu value is used to populate podman CpuShares
* Container log is forwarded to [Nomad logger](https://www.nomadproject.io/docs/commands/alloc/logs.html)
* utilize podmans --init feature
* set username or UID used for the specified command within the container (podman --user option).
* fine tune memory usage: standard [nomad memory resource](https://www.nomadproject.io/docs/job-specification/resources.html#memory) plus additional driver specific swap, swappiness and reservation parameters, OOM handling
* Use nomad alloc data in the container.
* Bind mount custom volumes into the container
* Publish ports
* Monitor the memory consumption
* Monitor CPU usage
* Task config cpu value is used to populate podman CpuShares
* Container log is forwarded to [Nomad logger](https://www.nomadproject.io/docs/commands/alloc/logs.html)
* Utilize podmans --init feature
* Set username or UID used for the specified command within the container (podman --user option).
* Fine tune memory usage: standard [nomad memory resource](https://www.nomadproject.io/docs/job-specification/resources.html#memory) plus additional driver specific swap, swappiness and reservation parameters, OOM handling
* Supports rootless containers with cgroup V2


## Building The Driver from source
Expand All @@ -44,11 +45,12 @@ cd nomad-driver-podman

- [Nomad](https://www.nomadproject.io/downloads.html) 0.9+
- Linux host with `podman` installed
- For rootless containers you need a system supporting cgroup V2 and a few other things, follow [this tutorial](https://github.com/containers/libpod/blob/master/docs/tutorials/rootless_tutorial.md)

You need a varlink enabled podman binary and a system socket activation unit,
see https://podman.io/blogs/2019/01/16/podman-varlink.html.
see https://podman.io/blogs/2019/01/16/podman-varlink.html.

nomad agent, nomad-driver-podman and podman will reside on the same host, so you
nomad agent, nomad-driver-podman and podman will reside on the same host, so you
do not have to worry about the ssh aspects of podman varlink.

Ensure that nomad can find the plugin, see [plugin_dir](https://www.nomadproject.io/docs/configuration/index.html#plugin_dir)
Expand All @@ -57,7 +59,7 @@ Ensure that nomad can find the plugin, see [plugin_dir](https://www.nomadproject

* volumes stanza:

* enabled - Defaults to true. Allows tasks to bind host paths (volumes) inside their container.
* enabled - Defaults to true. Allows tasks to bind host paths (volumes) inside their container.
* selinuxlabel - Allows the operator to set a SELinux label to the allocation and task local bind-mounts to containers. If used with _volumes.enabled_ set to false, the labels will still be applied to the standard binds in the container.

```
Expand Down Expand Up @@ -85,8 +87,8 @@ plugin "nomad-driver-podman" {
}
```

* recover_stopped (bool) Defaults to true. Allows the driver to start and resuse a previously stopped container after
a Nomad client restart.
* recover_stopped (bool) Defaults to true. Allows the driver to start and resuse a previously stopped container after
a Nomad client restart.
Consider a simple single node system and a complete reboot. All previously managed containers
will be reused instead of disposed and recreated.

Expand All @@ -98,16 +100,34 @@ plugin "nomad-driver-podman" {
}
```

* socket_path (string) Defaults to `"unix://run/podman/io.podman"` when running as root or a cgroup V1 system, and `"unix://run/user/<USER_ID>/podman/io.podman"` for rootless cgroup V2 systems


```
plugin "nomad-driver-podman" {
config {
socket_path = "unix://run/podman/io.podman"
}
}
```
## Task Configuration

* **image** - The image to run,
* **image** - The image to run,

```
config {
image = "docker://redis"
}
```

* **entrypoint** - (Optional) The entrypoint for the container. Defaults to the entrypoint set in the image.

```
config {
entrypoint = "/entrypoint.sh"
}
```

* **command** - (Optional) The command to run when starting the container.

```
Expand All @@ -127,7 +147,15 @@ config {
}
```

* **volumes** - (Optional) A list of host_path:container_path strings to bind host paths to container paths.
* **working_dir** - (Optional) The working directory for the container. Defaults to the default set in the image.

```
config {
working_dir = "/data"
}
```

* **volumes** - (Optional) A list of host_path:container_path strings to bind host paths to container paths.

```
config {
Expand All @@ -137,7 +165,7 @@ config {
}
```

* **tmpfs** - (Optional) A list of /container_path strings for tmpfs mount points. See podman run --tmpfs options for details.
* **tmpfs** - (Optional) A list of /container_path strings for tmpfs mount points. See podman run --tmpfs options for details.

```
config {
Expand Down Expand Up @@ -188,7 +216,7 @@ config {
}
```

* **memory_swap** - A limit value equal to memory plus swap. The swap LIMIT should always be larger than the [memory value](https://www.nomadproject.io/docs/job-specification/resources.html#memory).
* **memory_swap** - A limit value equal to memory plus swap. The swap LIMIT should always be larger than the [memory value](https://www.nomadproject.io/docs/job-specification/resources.html#memory).

Unit can be b (bytes), k (kilobytes), m (megabytes), or g (gigabytes). If you don't specify a unit, b is used. Set LIMIT to -1 to enable unlimited swap.

Expand Down Expand Up @@ -266,6 +294,90 @@ nomad run redis.nomad

podman ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d2d700cbce6 docker.io/library/redis:latest docker-entrypoint... 16 seconds ago Up 16 seconds ago redis-60fdc69b-65cb-8ece-8554-df49321b3462
```

### Rootless on ubuntu

edit `/etc/default/grub` to enable cgroups v2
```
GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=1"
```

`sudo update-grub`

ensure that podman varlink is running
```
$ systemctl --user status io.podman
● io.podman.service - Podman Remote API Service
Loaded: loaded (/usr/lib/systemd/user/io.podman.service; disabled; vendor preset: enabled)
Active: active (running) since Wed 2020-07-01 16:01:41 EDT; 7s ago
TriggeredBy: ● io.podman.socket
Docs: man:podman-varlink(1)
Main PID: 25091 (podman)
Tasks: 29 (limit: 18808)
Memory: 17.5M
CPU: 184ms
CGroup: /user.slice/user-1000.slice/user@1000.service/io.podman.service
├─25091 /usr/bin/podman varlink unix:/run/user/1000/podman/io.podman --timeout=60000 --cgroup-manager=systemd
├─25121 /usr/bin/podman varlink unix:/run/user/1000/podman/io.podman --timeout=60000 --cgroup-manager=systemd
└─25125 /usr/bin/podman
```

ensure that you have a recent version of [crun](https://github.com/containers/crun/)

```
crun -V
crun version 0.13.227-d38b
commit: d38b8c28fc50a14978a27fa6afc69a55bfdd2c11
spec: 1.0.0
+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL
```

`nomad job run example.nomad`
```
job "example" {
datacenters = ["dc1"]
type = "service"

group "cache" {
count = 1
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
task "redis" {
driver = "podman"

config {
image = "redis"

port_map {
db = 6379
}
}

resources {
cpu = 500 # 500 MHz
memory = 256 # 256MB

network {
# mbits = 10
port "db" {}
}
}
}
}
}
```

verify `podman ps`

```
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2423ae3efa21 docker.io/library/redis:latest redis-server 7 seconds ago Up 6 seconds ago 127.0.0.1:21510->6379/tcp, 127.0.0.1:21510->6379/udp redis-b640480f-4b93-65fd-7bba-c15722886395
```
91 changes: 89 additions & 2 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ import (
"github.com/hashicorp/nomad-driver-podman/iopodman"
"github.com/varlink/go/varlink"

"bufio"
"context"
"encoding/json"
"fmt"
"net"
"os"
"os/user"
"strings"
"time"
)

Expand All @@ -43,6 +47,8 @@ type PodmanClient struct {

// logger will log to the Nomad agent
logger hclog.Logger

varlinkSocketPath string
}

// withVarlink calls a podman varlink function and retries N times in case of network failures
Expand Down Expand Up @@ -221,9 +227,90 @@ func (c *PodmanClient) InspectContainer(containerID string) (iopodman.InspectCon
return ret, err
}

// PullImage takes a name or ID of an image and pulls it to local storage
// returning the name of the image pulled
func (c *PodmanClient) PullImage(imageID string) (string, error) {
var ret string
c.logger.Debug("Pull image", "image", imageID)
err := c.withVarlink(func(varlinkConnection *varlink.Connection) error {
moreResponse, err := iopodman.PullImage().Call(c.ctx, varlinkConnection, imageID)
if err == nil {
ret = moreResponse.Logs[len(moreResponse.Logs)-1]
if err != nil {
c.logger.Error("failed to unmarshal image pull logs", "err", err)
return err
}

}
return err
})
return ret, err
}

// InspectImage data takes a name or ID of an image and returns the inspection
// data as iopodman.InspectImageData.
func (c *PodmanClient) InspectImage(imageID string) (iopodman.InspectImageData, error) {
var ret iopodman.InspectImageData
c.logger.Debug("Inspect image", "image", imageID)
err := c.withVarlink(func(varlinkConnection *varlink.Connection) error {
inspectJSON, err := iopodman.InspectImage().Call(c.ctx, varlinkConnection, imageID)
if err == nil {
err = json.Unmarshal([]byte(inspectJSON), &ret)
if err != nil {
c.logger.Error("failed to unmarshal inspect image", "err", err)
return err
}

}
return err
})
return ret, err
}

func guessSocketPath(user *user.User, procFilesystems []string) string {
rootVarlinkPath := "unix://run/podman/io.podman"
if user.Uid == "0" {
return rootVarlinkPath
}

cgroupv2 := isCGroupV2(procFilesystems)

if cgroupv2 {
return fmt.Sprintf("unix://run/user/%s/podman/io.podman", user.Uid)
}

return rootVarlinkPath
}

func isCGroupV2(procFilesystems []string) bool {
cgroupv2 := false
for _, l := range procFilesystems {
if strings.HasSuffix(l, "cgroup2") {
cgroupv2 = true
}
}

return cgroupv2
}

func getProcFilesystems() ([]string, error) {
file, err := os.Open("/proc/filesystems")
if err != nil {
return nil, err
}
defer file.Close()

var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}

return lines, scanner.Err()
}

// getConnection opens a new varlink connection
func (c *PodmanClient) getConnection() (*varlink.Connection, error) {
// FIXME: a parameter for the socket would be nice
varlinkConnection, err := varlink.NewConnection(c.ctx, "unix://run/podman/io.podman")
varlinkConnection, err := varlink.NewConnection(c.ctx, c.varlinkSocketPath)
return varlinkConnection, err
}
Loading