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

Port to CNI networking #21

Merged
merged 1 commit into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@
[[constraint]]
name = "github.com/sethvargo/go-password"
version = "0.1.3"

[[constraint]]
branch = "master"
name = "github.com/containerd/go-cni"

[[constraint]]
name = "github.com/google/uuid"
version = "1.1.1"
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ GitCommit := $(shell git rev-parse HEAD)
LDFLAGS := "-s -w -X main.Version=$(Version) -X main.GitCommit=$(GitCommit)"
CONTAINERD_VER := 1.3.2
FAASC_VER := 0.4.0
CNI_VERSION := v0.8.4
ARCH := amd64

.PHONY: all
all: local
Expand All @@ -23,6 +25,8 @@ prepare-test:
sudo systemctl daemon-reload && sudo systemctl start containerd
sudo curl -fSLs "https://github.com/genuinetools/netns/releases/download/v0.5.3/netns-linux-amd64" --output "/usr/local/bin/netns" && sudo chmod a+x "/usr/local/bin/netns"
sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
sudo mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/$(CNI_VERSION)/cni-plugins-linux-$(ARCH)-$(CNI_VERSION).tgz | sudo tar -xz -C /opt/cni/bin
sudo curl -sSLf "https://github.com/alexellis/faas-containerd/releases/download/$(FAASC_VER)/faas-containerd" --output "/usr/local/bin/faas-containerd" && sudo chmod a+x "/usr/local/bin/faas-containerd" || :
sudo cp $(GOPATH)/src/github.com/alexellis/faasd/bin/faasd /usr/local/bin/
cd $(GOPATH)/src/github.com/alexellis/faasd/ && sudo /usr/local/bin/faasd install
Expand Down
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,15 @@ Other operations are pending development in the provider.
For Windows users, install [Git Bash](https://git-scm.com/downloads) along with multipass or vagrant. You can also use WSL1 or WSL2 which provides a Linux environment.

* Installation steps as per [faas-containerd](https://github.com/alexellis/faas-containerd) for building and for development
* [netns](https://github.com/genuinetools/netns/releases) binary in `$PATH`
* [containerd v1.3.2](https://github.com/containerd/containerd)
* [CNI plugins v0.8.4](https://github.com/containernetworking/plugins)

* [faas-cli](https://github.com/openfaas/faas-cli) (optional)

## Backlog

Pending:

* [ ] Use CNI to create network namespaces and adapters
* [ ] Monitor and restart any of the core components at runtime if the container stops
* [ ] Bundle/package/automate installation of containerd - [see bootstrap from k3s](https://github.com/rancher/k3s)
* [ ] Provide ufw rules / example for blocking access to everything but a reverse proxy to the gateway container
Expand All @@ -67,6 +66,7 @@ Done:
* [x] Determine armhf/arm64 containers to run for gateway
* [x] Configure `basic_auth` to protect the OpenFaaS gateway and faas-containerd HTTP API
* [x] Setup custom working directory for faasd `/run/faasd/`
* [x] Use CNI to create network namespaces and adapters

## Tutorial: Get started on armhf / Raspberry Pi

Expand All @@ -76,6 +76,23 @@ You can run this tutorial on your Raspberry Pi, or adapt the steps for a regular

## Hacking (build from source)

Install the CNI plugins:

```sh
export CNI_VERSION=v0.8.4
```

* For PC run `export ARCH=amd64`
* For RPi/armhf run `export ARCH=arm`
* For arm64 run `export ARCH=arm64`

Then run:

```sh
mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz | tar -xz -C /opt/cni/bin
```

First run faas-containerd

```sh
Expand Down Expand Up @@ -125,21 +142,23 @@ Look in `hosts` in the current working folder or in `/run/faasd/` to get the IP

```sh
127.0.0.1 localhost
172.19.0.1 faas-containerd
172.19.0.2 prometheus
10.62.0.1 faas-containerd

172.19.0.3 gateway
172.19.0.4 nats
172.19.0.5 queue-worker
10.62.0.2 prometheus
10.62.0.3 gateway
10.62.0.4 nats
10.62.0.5 queue-worker
```

Since faas-containerd uses containerd heavily it is not running as a container, but as a stand-alone process. Its port is available via the bridge interface, i.e. netns0.
The IP addresses are dynamic and may change on every launch.

Since faas-containerd uses containerd heavily it is not running as a container, but as a stand-alone process. Its port is available via the bridge interface, i.e. openfaas0.

* Prometheus will run on the Prometheus IP plus port 8080 i.e. http://172.19.0.2:9090/targets
* Prometheus will run on the Prometheus IP plus port 8080 i.e. http://[prometheus_ip]:9090/targets

* faas-containerd runs on 172.19.0.1:8081
* faas-containerd runs on 10.62.0.1:8081

* Now go to the gateway's IP address as shown above on port 8080, i.e. http://172.19.0.3:8080 - you can also use this address to deploy OpenFaaS Functions via the `faas-cli`.
* Now go to the gateway's IP address as shown above on port 8080, i.e. http://[gateway_ip]:8080 - you can also use this address to deploy OpenFaaS Functions via the `faas-cli`.

* basic-auth

Expand Down
5 changes: 0 additions & 5 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ func runInstall(_ *cobra.Command, _ []string) error {
return err
}

err = binExists("/usr/local/bin/", "netns")
if err != nil {
return err
}

err = systemd.InstallUnit("faas-containerd", map[string]string{
"Cwd": faasContainerdwd,
"SecretMountPath": path.Join(faasdwd, "secrets")})
Expand Down
84 changes: 84 additions & 0 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
Expand All @@ -26,6 +27,32 @@ var upCmd = &cobra.Command{
RunE: runUp,
}

// defaultCNIConf is a CNI configuration that enables network access to containers (docker-bridge style)
var defaultCNIConf = fmt.Sprintf(`
{
"cniVersion": "0.4.0",
"name": "%s",
"plugins": [
{
"type": "bridge",
"bridge": "%s",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "%s",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
},
{
"type": "firewall"
}
]
}
`, pkg.DefaultNetworkName, pkg.DefaultBridgeName, pkg.DefaultSubnet)

const secretMountDir = "/run/secrets"

func runUp(_ *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -53,6 +80,10 @@ func runUp(_ *cobra.Command, _ []string) error {
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files")
}

if makeNetworkErr := makeNetworkConfig(); makeNetworkErr != nil {
return errors.Wrap(makeNetworkErr, "error creating network config")
}

services := makeServiceDefinitions(clientSuffix)

start := time.Now()
Expand Down Expand Up @@ -269,3 +300,56 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
},
}
}

func makeNetworkConfig() error {
netConfig := path.Join(pkg.CNIConfDir, pkg.DefaultCNIConfFilename)
log.Printf("Writing network config...\n")

if !dirExists(pkg.CNIConfDir) {
if err := os.MkdirAll(pkg.CNIConfDir, 0755); err != nil {
return fmt.Errorf("cannot create directory: %s", pkg.CNIConfDir)
}
}

if err := ioutil.WriteFile(netConfig, []byte(defaultCNIConf), 644); err != nil {
return fmt.Errorf("cannot write network config: %s", pkg.DefaultCNIConfFilename)

}
return nil
}

func dirEmpty(dirname string) (b bool) {
if !dirExists(dirname) {
return
}

f, err := os.Open(dirname)
if err != nil {
return
}
defer func() { _ = f.Close() }()

// If the first file is EOF, the directory is empty
if _, err = f.Readdir(1); err == io.EOF {
b = true
}
return
}

func dirExists(dirname string) bool {
exists, info := pathExists(dirname)
if !exists {
return false
}

return info.IsDir()
}

func pathExists(path string) (bool, os.FileInfo) {
info, err := os.Stat(path)
if os.IsNotExist(err) {
return false, nil
}

return true, info
}
Loading