Skip to content

Commit cd94ead

Browse files
authored
Merge pull request #40 from Roblox/consul_templates
Add support for consul templates, port forwarding and consul service discovery.
2 parents 6045772 + ebcd470 commit cd94ead

File tree

3 files changed

+88
-16
lines changed

3 files changed

+88
-16
lines changed

README.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,62 @@ You need to install CNI plugins on Nomad client nodes under `/opt/cni/bin` befor
151151
$ sudo mkdir -p /opt/cni/bin
152152
$ sudo tar -C /opt/cni/bin -xzf cni-plugins.tgz
153153
```
154+
Also, ensure your Linux operating system distribution has been configured to allow container traffic through the bridge network to be routed via iptables. These tunables can be set as follows:
155+
156+
```
157+
$ echo 1 > /proc/sys/net/bridge/bridge-nf-call-arptables
158+
$ echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables
159+
$ echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
160+
```
161+
To preserve these settings on startup of a nomad client node, add a file including the following to `/etc/sysctl.d/` or remove the file your Linux distribution puts in that directory.
162+
163+
```
164+
net.bridge.bridge-nf-call-arptables = 1
165+
net.bridge.bridge-nf-call-ip6tables = 1
166+
net.bridge.bridge-nf-call-iptables = 1
167+
```
168+
169+
## Port forwarding
170+
171+
nomad supports both **static** and **dynamic** port mapping.
172+
173+
1. **Static ports**
174+
175+
Static port mapping can be added in the `network` stanza.
176+
```
177+
network {
178+
mode = "bridge"
179+
port "lb" {
180+
static = 8889
181+
to = 8889
182+
}
183+
}
184+
```
185+
Here, `host` port `8889` is mapped to `container` port `8889`.<br/>
186+
**NOTE**: static ports are usually not recommended, except for `system` or specialized jobs like load balancers.
187+
188+
2. **Dynamic ports**
189+
190+
Dynamic port mapping is also enabled in the `network` stanza.
191+
```
192+
network {
193+
mode = "bridge"
194+
port "http" {
195+
to = 8080
196+
}
197+
}
198+
```
199+
Here, nomad will allocate a dynamic port on the `host` and that port will be mapped to `8080` in the container.
200+
201+
You can also read more about `network stanza` in the [`nomad official documentation`](https://www.nomadproject.io/docs/job-specification/network)
202+
203+
## Service discovery
204+
205+
Nomad schedules workloads of various types across a cluster of generic hosts. Because of this, placement is not known in advance and you will need to use service discovery to connect tasks to other services deployed across your cluster. Nomad integrates with Consul to provide service discovery and monitoring.
206+
207+
A [`service`](https://www.nomadproject.io/docs/job-specification/service) stanza can be added to your job spec, to enable service discovery.
208+
209+
The service stanza instructs Nomad to register a service with Consul.
154210

155211
## Tests
156212
```
@@ -174,15 +230,6 @@ This will destroy your vagrant VM.
174230
## Currently supported environments
175231
Ubuntu (>= 16.04)
176232

177-
## Limitations
178-
179-
`nomad-driver-containerd` [`v0.1`](https://github.com/Roblox/nomad-driver-containerd/releases/tag/v0.1) is **not** production ready.
180-
There are some open items which are currently being worked on.
181-
182-
1) **Port forwarding**: The ability to map a host port to a container port. This is currently not supported, but could be supported in future.
183-
184-
2) **Consul connect**: When a user launches a job in `nomad`, s/he can add a [`service stanza`](https://www.nomadproject.io/docs/job-specification/service) which will instruct `nomad` to register the service with `consul` for service discovery. This is currently not supported.
185-
186233
## License
187234

188235
Copyright 2020 Roblox Corporation

containerd/containerd.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (d *Driver) pullImage(imageName string) (containerd.Image, error) {
4141
return d.client.Pull(d.ctxContainerd, imageName, containerd.WithPullUnpack)
4242
}
4343

44-
func (d *Driver) createContainer(image containerd.Image, containerName, containerSnapshotName, containerdRuntime, netnsPath string, env []string, config *TaskConfig) (containerd.Container, error) {
44+
func (d *Driver) createContainer(image containerd.Image, containerName, containerSnapshotName, containerdRuntime, netnsPath, secretsDir, taskDir string, env []string, config *TaskConfig) (containerd.Container, error) {
4545
if config.Command == "" && len(config.Args) > 0 {
4646
return nil, fmt.Errorf("Command is empty. Cannot set --args without --command.")
4747
}
@@ -119,14 +119,22 @@ func (d *Driver) createContainer(image containerd.Image, containerName, containe
119119
return nil, fmt.Errorf("Options cannot be empty for mount type: %s. You need to atleast pass rbind and ro.", mount.Type)
120120
}
121121

122-
m := specs.Mount{}
123-
m.Type = mount.Type
124-
m.Destination = mount.Target
125-
m.Source = mount.Source
126-
m.Options = mount.Options
122+
m := buildMountpoint(mount.Type, mount.Target, mount.Source, mount.Options)
127123
mounts = append(mounts, m)
128124
}
129125

126+
// Setup "/secrets" (NOMAD_SECRETS_DIR) in the container.
127+
if secretsDir != "" {
128+
secretsMount := buildMountpoint("bind", "/secrets", secretsDir, []string{"rbind", "ro"})
129+
mounts = append(mounts, secretsMount)
130+
}
131+
132+
// Setup "/local" (NOMAD_TASK_DIR) in the container.
133+
if taskDir != "" {
134+
taskMount := buildMountpoint("bind", "/local", taskDir, []string{"rbind", "ro"})
135+
mounts = append(mounts, taskMount)
136+
}
137+
130138
if len(mounts) > 0 {
131139
opts = append(opts, oci.WithMounts(mounts))
132140
}
@@ -150,6 +158,16 @@ func (d *Driver) createContainer(image containerd.Image, containerName, containe
150158
)
151159
}
152160

161+
// buildMountpoint builds the mount point for the container.
162+
func buildMountpoint(mountType, mountTarget, mountSource string, mountOptions []string) specs.Mount {
163+
m := specs.Mount{}
164+
m.Type = mountType
165+
m.Destination = mountTarget
166+
m.Source = mountSource
167+
m.Options = mountOptions
168+
return m
169+
}
170+
153171
func (d *Driver) loadContainer(id string) (containerd.Container, error) {
154172
return d.client.LoadContainer(d.ctxContainerd, id)
155173
}

containerd/driver.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,17 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
357357

358358
// Setup environment variables.
359359
var env []string
360+
var secretsDir, taskDir string
360361
for key, val := range cfg.Env {
361362
if skipOverride(key) {
362363
continue
363364
}
365+
if key == "NOMAD_SECRETS_DIR" {
366+
secretsDir = val
367+
}
368+
if key == "NOMAD_TASK_DIR" {
369+
taskDir = val
370+
}
364371
env = append(env, fmt.Sprintf("%s=%s", key, val))
365372
}
366373

@@ -370,7 +377,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
370377
netnsPath = cfg.NetworkIsolation.Path
371378
}
372379

373-
container, err := d.createContainer(image, containerName, containerSnapshotName, d.config.ContainerdRuntime, netnsPath, env, &driverConfig)
380+
container, err := d.createContainer(image, containerName, containerSnapshotName, d.config.ContainerdRuntime, netnsPath, secretsDir, taskDir, env, &driverConfig)
374381
if err != nil {
375382
return nil, nil, fmt.Errorf("Error in creating container: %v", err)
376383
}

0 commit comments

Comments
 (0)