NextCloudPi renamed to NextCloudPlus, gets a new website, improved ncp-web, docker, languages and more – Own your bits

The latest release of NextCloudPi is out!

Work has been focused on improving user experience during the first setup, and bringing more power to the docker container and ncp-web. Also there is some news!

NextCloudPlus improves everyday thanks to your feedback. Please report any problems here. Also, you can discuss anything in the forums.

Last but not least, please download through bitorrent and share it for a while to help keep hosting costs down.

NextCloudPlus

While originally NCP was only oriented towards running Nextcloud in the Raspberry Pi, it has since extended its scope to include all kinds of boards and architectures. We keep running into people online under the impression that we can’t enjoy ncp-web and the rest in an x86 server or an Odroid HC2, just because of the name.

So we decided for NextCloudPlus: we are still NCP and well, it’s Nextcloud with a plus 😉

New web

It was getting confusing to have the information scattered throughout the blog and the wiki, so a single landing page with a simple design has been created, nextcloudplus.com

No more default passwords

The initial setup process have been improved to make it lazy proof. There are no default passwords anymore, and less steps to follow.

If we navigate to nextcloudplus.local, we will be greeted with an activation page

We can now copy the credentials, or print this page and click the activation button. Our instance will be initialized with these random passwords, and we will be redirected to the first run wizard.

ncp-web

The web panel has received a polish, and a number of new features.

Dashboard

An informative landing page that presents an overview of the system, and some configuration advice.

Nextcloud configuration

An easy way to inspect config.php

Language bar

The language bar has been made more visible

Docker container

The docker container has received a well needed push to make it on par with the SD card version. All remaining ncp-apps have been ported to the docker version, plus we now have in-container upgrades just like the SD card version.

Just set nc-autoupdate-ncp and the container will receive all improvements and fixes just like all other NCP users.

Also, it is now supported to specify any custom external folder to use as a volume, so we can have our data and database in any folder of your choice. For instance, in order to save our files to /media/USBdrive/ncdata, just pass the folder as a volume argument

docker run -d -p 4443:4443 -p 443:443 -p 80:80 -v /media/USBdrive/ncdata:/data –name nextcloudpi ownyourbits/nextcloudplus-x86 $DOMAIN

 

New languages

Spanish has been added to ncp-web, and Chinese is also in the oven

Source

Logging Best Practices for Kubernetes using Elasticsearch, Fluent Bit and Kibana

Logging Best Practices for Kubernetes using Elasticsearch, Fluent Bit and Kibana

Logging is one of the most powerful tools we have as developers. It’s no accident that when things go wrong in production, one of a developer’s first questions is often – “can you send me the logs?”. Raw logs contain useful information but they can be hard to parse. So, when operating systems at scale, using structured logging can greatly increase the usefulness of your logs. Using a common structure it makes the logs easier to search, and also makes automated processing of logs much easier.

At Giant Swarm we use structured logging throughout our control plane to manage Kubernetes clusters for our customers. We use the EFK stack to do this, which consists of Elasticsearch, Fluent Bit and Kibana. The EFK stack is based on the widely used ELK stack which uses Logstash instead of Fluent Bit or Fluentd.

This post explains some of the best practices we follow for structuring our logs, and how we use the EFK stack to manage them. Coming soon we’ll also be providing managed logging infrastructure to our customers as part of the Managed Cloud Native Stack.

How we write logs

Our control plane consists of multiple microservices and Kubernetes operators. As a reminder, an operator in Kubernetes is a custom controller, paired with a CRD (Custom Resource Definition), that extends the Kubernetes API.

For our microservices we develop them in our microkit framework which is based on Go-Kit. Our operators are developed using our operatorkit framework. These frameworks both use our micrologger library. Since all our logs flow through a single library we can enrich the logs with extra data. This also makes the structure of our logs very consistent.

For example, we can add data like a tenant cluster’s ID to the Golang context we pass inside our operator code. We use this to create a self-link to the CR (custom resource) that the operator is processing. This is the same approach as the self-links exposed by the Kubernetes API and makes the logs easier to read.

What we log

We use a JSON format for our logs, which makes it easier for Fluent Bit to process them. It also means the data is more structured when it’s stored in Elasticsearch. We use a pretty standard format with the log level (e.g debug or error) and the log message. For errors, we add a stack entry with the full call stack.

We use the frameworks described earlier to enrich the log messages with extra information, such as the timestamp, self-link or the event the operator is processing (e.g create or update).

Elasticsearch

Elasticsearch is at the heart of the EFK stack. It’s a NoSQL database based on the Lucene search engine. Its origin as a search engine also makes it good at querying log data. It can ingest large volumes of data, store it efficiently and execute queries quickly. In the EFK stack, Elasticsearch is used for log storage, and receives log data from Fluent, which is the log shipper. The log data is stored in an Elasticsearch index and is queried by Kibana.

As you’d expect we deploy Elasticsearch using Kubernetes. Each control plane we manage for our customers has its own deployment of Elasticsearch. This isolates it from all other control planes. On AWS and Azure, we use cloud storage with Persistent Volumes for storing the index data. In on-premise control planes, the data is stored on physical storage.

Fluent Bit

Logging is an area of Cloud Native applications where there are many options. We currently use Fluent Bit but we have previously evaluated many other options, including Logstash (the L in the very popular ELK stack), and Filebeat which is a lightweight log shipper from Elastic.co.

We initially ruled out Logstash and Filebeat, as the integration with Kubernetes metadata was not very advanced. So we started our implementation using Fluentd. Fluentd is a log shipper that has many plugins. It provides a unified logging layer that forwards data to Elasticsearch. It’s also a CNCF project and is known for its Kubernetes and Docker integrations which are both important to us.

Fluentd uses Ruby and Ruby Gems for configuration of its over 500 plugins. Since Ruby is an interpreted language it also makes heavy usage of C extensions for parsing log files and forwarding data to provide the necessary speed. However, due to the volume of logs we ingest we hit performance problems, and so we evaluated the related Fluent Bit project.

Fluent Bit is implemented solely in C and has a restricted set of functionality compared to Fluentd. However, in our case it provides all the functionality we need and we are much happier with the performance. We deploy Fluent Bit as a daemon set to all nodes in our control plane clusters. The Fluent Bit pods on each node mount the Docker logs for the host which gives us access to logs for all containers across the cluster.

A shout out here to Eduardo Silva who is one of the Fluent Bit maintainers, and helped us a lot with answering questions while we worked on the integration.

Log Retention – Curator

Logging is great but it can quickly use up a lot of disk space. So having a good log retention policy is essential. Fluent Bit helps here because it creates daily indices in Elasticsearch. We have a daily cron job in Kubernetes that deletes indices older than n days. The cron job calls the curator component which deletes the old indices.

There is a Curator component from Elastic.co but we use our own simpler version that meets our requirements. It’s available on GitHub giantswarm/curator. Deleting the indices is an intensive process for disk I/O, so another trick we use is to run the cron job at an unusual time like 02:35 rather than at 02:00 – this avoids conflicting with other scheduled tasks.

Kibana

Kibana provides the UI for the stack, with the front end and query engine for querying the logs in Elasticsearch. Kibana supports the Lucene query syntax as well as its own extended Query DSL that uses JSON. Another nice feature is the built-in support for visualizations for use in dashboards.

One challenge we faced was how to configure Kibana. We run an instance of Kibana in each control plane and we want them all to be kept in sync with the same base configuration. This includes setting the index pattern in Kibana for which Elasticsearch indices it should search.

We’re using Kibana 6 and until recently it didn’t have a documented configuration API. So instead we wrote a simple sidecar giantswarm/kibana-sidecar, that sets the configuration. This is working pretty well, but needs to be adapted for each version of Kibana. Elastic.co have recently published documentation for the Saved Objects API for configuration, which may move to this in future.

Conclusion

In this post we’ve shown you how we use structured logging and the EFK stack to manage Kubernetes clusters for our customers. There are many options for logging when building Cloud Native applications. We’ve evaluated several options and found a set of tools that work well for us.

However, one of the benefits of the EFK and ELK stacks is they are very flexible. So if you want to use a different tool for log storage like Kafka – you just configure Fluent to ship to Kafka. This also works for 3rd party log storage providers, like DataDog and Splunk. You can also use a different log shipper like Filebeat or Logstash, if they better suit your needs.

Coming soon we’ll also be offering a Managed Logging infrastructure to our customers as part of the Managed Cloud Native Stack. This will let our customers take advantage of the rich functionality provided by the EFK stack. However, it will be fully managed by us, using our operational knowledge of running the EFK stack in production. Request your free trial of the Giant Swarm Infrastructure here.

Source

Some Admission Webhook Basics – Container Solutions

Jul 10, 2018

by Jason Smith

Admission Webhooks are a new feature in Kubernetes since 1.9 that allows you to intercept manifests prior to them being deployed. This gives you a lot of control to do things like inject sidecars, attach volumes, or validate image repositories before the object gets deployed. I took some time over the last two days to explore this feature and how to implement it. Let me share what I have learned…

As of this writing this you need a cluster running Kubernetes 1.11 or better. You may say, “but it has been supported since… 1.9” You would be correct… BUT prior to 1.11, if you sent a malformed request you could potentially crash your kube-api server. This is probably not the most ideal situation to be in, and could really hinder your development efforts.

So, you need a running cluster. Do you intend to run minikube? Well as of this writing you will need to compile that from source because the latest release is not compatible with 1.11. If you want to get minikube running with Kubernetes 1.11, you can clone the minikube repo and run make, and it should produce ./out/minikube

This entire tutorial is based off this demo repository. So you should clone it and work from inside it.

Finally, I highly recommend you download json-patch cli utility. This is the same package Kubernetes uses to apply its patches, and the cli will help you write your patches before writing your webhook.

Assuming you are using minikube you can run:

$ minikube start –kubernetes-version v1.11.0

Let’s start by just running the pause application. From the demo repository, run:

$ kubectl apply -f test.yaml

So right now Kubernetes only currently supports jsonpatch for mutating objects. We can play around with our patches prior to actually writing our webhook, to see if what we want to do is going to work and that our patch is correct… so we don’t crash our kube-api server.

Currently, we have the pause container running in mwc-test namespace. In the demo repository, you will find a folder titled “jsonpatchtests”. Inside it, we have two patches, one that adds a label to the labels object, and another that creates a labels array under the labels object.

When Kubernetes returns a pod object, if the pod has no labels, the labels object is not passed back in the json of the pod definition. With my limited understanding of jsonpatch if I add an array and one exists, it will overwrite the existing labels array. If I add one to the labels path and it does not have any labels it will complain that the path “/metadata/labels” does not exist.

Feel free to play around, by removing the labels from test.yaml and applying the patches or make your own. Below is an explanation of how to do this.

I am new to jsonpatch, and I am still learning, I welcome commenters suggesting better methods.

Test A Patch

So we can test a patch straight from the command line by just piping an object definition straight into json-patch.

I will be using jq through these commands because it offers pretty output.

So if we run a patch like this:

$ kubectl get pod -n mwc-test pause -o json | json-patch -p jsonpatchtests/patch1.json | jq .

We can see the patch was applied to the json

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 

{

“apiVersion”: “v1”,

“kind”: “Pod”,

“metadata”: {

“labels”: {

“test”: “label”,

“thisisanewlabel”: “hello” <–This was added… Yay!

},

}

}

 

So now we have a working patch, we can copy that directly into our webhook.

The Webhook is a pretty simple setup:

  1. Http server to actually admit or mutate the object
  2. A Deployment for the http server
  3. A Service for the http server
  4. And a MutatingWebhookConfiguration 

2, 3 and 4 can be found in the demo repository in manifest.yaml. 2 and 3 are pretty self-explanatory, so we will focus on 1 and 4.

Anatomy of the Webhook Server

So I based my code off the e2e tests Kubernetes suggests to use as a launching point. I found some cruft in that Go code and weird naming conventions were causing me more confusion than helping. So I wrote a simpler example in Go that had long names with better comments.

We can find this in main.go.

I will not go through line by line but I will explain the basic concepts here.

  1. Kubernetes submits an AdmissionReview to your webhook, containing
    1. an AdmissionRequest, which has
      1. a UID
      2. a Raw Extension carrying full json payload for an object, such as a Pod
      3. And other stuff that you may or may not use
  2. Based on this information you apply your logic and you return a new AdmissionReview
  3. The AdmissionReview contains an
    1. AdmissionResponse which has
      1. the original UID from the AdmissionRequest
      2. A Patch (if applicable)
      3. The Allowed field which is either true or false

That is pretty much it. The above objects have a lot more data, but I am just focusing on the ones I am using in the main.go example.

The MutatingWebhookConfiguration

The configuration is pretty straightforward

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

 

apiVersion: admissionregistration.k8s.io/v1beta1

kind: MutatingWebhookConfiguration

metadata:

name: mwc-example

webhooks:

– name: mwc-example.jasonrichardsmith.com # this needs to be unique

clientConfig:

Service: # The below targets the service we deployed

name: mwc-example

namespace: mwc-example

path: “/mutating-pods”

caBundle: “$” # This will be inserted from ca files that we generate

rules:

– operations: [“CREATE”,”UPDATE”] # This is the actions to trigger on

apiGroups: [“”]

apiVersions: [“v1”]

resources: [“pods”] # These are the objects to trigger for

failurePolicy: Fail # This is what happens if something goes wrong

namespaceSelector:

matchLabels:

mwc-example: enabled # The label a namespace must have to invoke the webhook

 

So let’s try it out.

I am going to assume we are working on minikube. To deploy to a real cluster, you will want to change the REPO to one you own in the Makefile and the image referenced in the deployment in manifest.yaml.

So to run this we will do the following.

First, tear down the pause pod you deployed.

$ kubectl delete -f test.yaml

 

Build the webhook image

This only requires make, minikube and Docker. This will build the image on the minikube server so we do not have to push it to a repo.

If you are using your own repo you can run the below command, after editing the Makefile REPO variable.

Secrets and Certs

This whole process will require Secrets and Certs. I stole and slightly altered a bash script from the Istio sidecar injector to demonstrate this. I am not going to get into what this is doing, because it is out of scope.

First, create the namespace:

$ kubectl apply -f ns.yaml

 

Then generate certs and secrets

We will take the cert we created and stick it into the manifest for the webhook.

This will produce a new manifest manifest-ca.yaml. We can deploy that.

$ kubectl apply -f manifest-ca.yaml

 

If everything went well you should see this:

service/mwc-example created

deployment.apps/mwc-example created

mutatingwebhookconfiguration.admissionregistration.k8s.io/mwc-example created

 

Now we can deploy the test.yaml. Which if you inspect you will see the namespace has a label which was required for our MutatingWebhookConfiguration to apply the webhook to a namespace.

$ kubectl apply -f test.yaml

 

You should get this response

namespace/mwc-test created

pod/pause created

 

The Big question is … Did it Work?

$ kubectl get pods -n mwc-test -o json | jq .items[0].metadata.labels

 

Did you get this?

{

“test”: “label”,

“thisisanewlabel”: “hello”

}

 

This blog post stole from, was inspired by, the following content:

IBM’s article on Mutating Admission Webhooks

The Istio repo

Kubernetes e2e test

The Kubernetes Docs

Thanks for reading!

Read more about where we think Kubernetes is in its lifecycle in our whitepaper.

Download Whitepaper

Source

Setup a basic Kubernetes cluster with ease using RKE

 

Expert Training in Kubernetes and Rancher

Join our free online training sessions to learn how to manage Kubernetes workloads with Rancher.

Sign up here

In this post, you will go from 3 Ubuntu 16.04 nodes to a basic Kubernetes cluster in a few simple steps. To accomplish this, you will be using Rancher Kubernetes Engine (RKE). To be able to use RKE, you will need 3 Linux nodes with Docker installed (see Requirements below).

This won’t be a production ready cluster, but enough to get you familiar with RKE, some Kubernetes and be able to play around with the cluster. Keep an eye out for the post for building a production ready cluster.

Requirements

  • RKE

You will be using RKE from your workstation. Download the latest version for your platform at:
https://github.com/rancher/rke/releases/latest

  • kubectl

After creating the cluster, we will use the default Kubernetes command-line tool called kubectl to interact with the cluster.
Get the latest version for your platform at:
https://kubernetes.io/docs/tasks/tools/install-kubectl/

  • 3 Ubuntu 16.04 nodes with 2(v)CPUs, 4GB of memory and with swap disabled

Most commonly used Linux distribution is Ubuntu 16.04, this is what will be used in this post. Make sure swap is disabled by running swapoff -a and removing any swap entry in /etc/fstab. You must be able to access the node using SSH. As this is a multi-node cluster, the required ports need to be opened before proceeding.

  • Docker installed on each Linux node

Kubernetes only validates Docker up to 17.03.2 (See https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.11.md#external-dependencies).
You can use https://docs.docker.com/install/linux/docker-ce/ubuntu/ to install Docker (make sure you install 17.03.2) or use this one-liner to install the correct version:
curl https://releases.rancher.com/install-docker/17.03.sh | sh

Make sure the requirements listed above are fulfilled before you proceed.

How RKE works

RKE can be run from any platform (the binary is available for MacOS/Linux/Windows), in this example it will run on your workstation/laptop/computer. The examples in this post are based on MacOS/Linux.

RKE will connect to the nodes using a configured SSH private key (the nodes should have the matching SSH public key installed for the SSH user) and setup a tunnel to access the Docker socket (/var/run/docker.sock by default, but configurable). This means that the configured SSH user must have access to the Docker socket on the machine, we will go over this in Creating the Linux user account.

Creating the Linux user account

Note: Make sure Docker is installed following the instructions in the Requirements section above.

The following steps need to be executed on every node. If you need to use sudo, prefix each command with sudo. If you already have users that can access the machine using a SSH key and can access the Docker socket, you can skip this step.

# Login to the node
$ ssh [email protected]
# Create a Linux user called rke, create home directory, and add to docker group
$ useradd -m -G docker rke
# Switch user to rke and create SSH directories
$ su – rke
$ mkdir $HOME/.ssh
$ chmod 700 $HOME/.ssh
$ touch $HOME/.ssh/authorized_keys
# Test Docker socket access
$ docker version
Client:
Version: 17.03.2-ce
API version: 1.27
Go version: go1.7.5
Git commit: f5ec1e2
Built: Tue Jun 27 03:35:14 2017
OS/Arch: linux/amd64

Server:
Version: 17.03.2-ce
API version: 1.27 (minimum version 1.12)
Go version: go1.7.5
Git commit: f5ec1e2
Built: Tue Jun 27 03:35:14 2017
OS/Arch: linux/amd64
Experimental: false

Configuring SSH keys

In this post we will create new keys but feel free to use your existing keys. Just make sure you specify them correctly when we configure the keys in RKE.

Note: If you want to use SSH keys with a passphrase, you will need to have ssh-agent running with the key added and specify –ssh-agent-auth when running RKE.

Creating SSH key pair
Creating an SSH key pair can be done by using ssh-keygen , you can execute this on your workstation/laptop/computer. It is highly recommended to put a passphrase on your SSH private key. If you lose your SSH private key (and not have a passphrase on it), anyone can use it to access your nodes.

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key ($HOME/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in $HOME/.ssh/id_rsa.
Your public key has been saved in $HOME/.ssh/id_rsa.pub.
The key fingerprint is:
xxx

After creating the SSH key pair, you should have the following files:

  • $HOME/.ssh/id_rsa (SSH private key, keep this secure)
  • $HOME/.ssh/id_rsa.pub (SSH public key)

Copy the SSH public key to the nodes
To be able to access the nodes using the created SSH key pair, you will need to install the SSH public key onto the nodes.

Execute this for every node (where hostname is the IP/hostname of the node):

# Install the SSH public key on the node
$ cat $HOME/.ssh/id_rsa.pub | ssh hostname “sudo tee -a /home/rke/.ssh/authorized_keys”

Note: This post is demonstrating how you create a separate user for RKE. Because of this, we can’t use ssh-copy-id as it only works for installing keys to the same user as is used for the SSH connection.

Setup ssh-agent

Note: If you chose not to put a passphrase on your SSH private key, you can skip this step.

This needs to be executed on your workstation/laptop/computer:

# Run ssh-agent and configure the correct environment variables
$ eval $(ssh-agent)
Agent pid 5151
# Add the private key to the ssh-agent
$ ssh-add $HOME/.ssh/id_rsa
Identity added: $HOME/.ssh/id_rsa ($HOME/.ssh/id_rsa)

Test SSH connectivity
Last step is to test if we can access the node using the SSH private key. This needs to be executed on your workstation/laptop/computer, replacing hostname with each of the nodes IP/hostname):

$ ssh -i $HOME/.ssh/id_rsa [email protected] docker version
Client:
Version: 17.03.2-ce
API version: 1.27
Go version: go1.7.5
Git commit: f5ec1e2
Built: Tue Jun 27 03:35:14 2017
OS/Arch: linux/amd64

Server:
Version: 17.03.2-ce
API version: 1.27 (minimum version 1.12)
Go version: go1.7.5
Git commit: f5ec1e2
Built: Tue Jun 27 03:35:14 2017
OS/Arch: linux/amd64
Experimental: false

Configuring and running RKE

Get RKE for your platform at:
https://github.com/rancher/rke/releases/latest.

RKE will run on your workstation/laptop/computer.

For this post I’ve renamed the RKE binary to rke, to make the commands generic for each platform. You can do the same by running:

Test if RKE can be successfully executed by using the following command:

# Download RKE for MacOS (Darwin)
$ wget https://github.com/rancher/rke/releases/download/v0.1.9/rke_darwin-amd64
# Rename binary to rke
mv rke_darwin-amd64 rke
# Make RKE binary executable
$ chmod +x rke
# Show RKE version
$ ./rke –version
rke version v0.1.9

Next step is to create a cluster configuration file (by default it will be cluster.yml). This contains all information to build the Kubernetes cluster, like node connection info, what roles to apply to what node etcetera. All configuration options can be found in the documentation. You can create the cluster configuration file by running ./rke config and answering the questions. For this post, you will create a 3 node cluster with every role on each node (answer y for every role), and we will add the Kubernetes Dashboard as addon (Using https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml). To access the Kubernetes Dashboard, you need a Service Account token which will be created by adding https://gist.githubusercontent.com/superseb/499f2caa2637c404af41cfb7e5f4a938/raw/930841ac00653fdff8beca61dab9a20bb8983782/k8s-dashboard-user.yml to the addons.

Regarding answering the question to create the cluster configuration file:

  • The values in brackets, for instance [22] for SSH Port, are defaults and can just be used by pressing the Enter key.
  • The default SSH Private Key would do, if you have another key, please change it.

    $ ./rke config
    [+] Cluster Level SSH Private Key Path [~/.ssh/id_rsa]: ~/.ssh/id_rsa
    [+] Number of Hosts [1]: 3
    [+] SSH Address of host (1) [none]: ip_or_dns_host1
    [+] SSH Port of host (1) [22]:
    [+] SSH Private Key Path of host (ip_or_dns_host1) [none]:
    [-] You have entered empty SSH key path, trying fetch from SSH key parameter
    [+] SSH Private Key of host (ip_or_dns_host1) [none]:
    [-] You have entered empty SSH key, defaulting to cluster level SSH key: ~/.ssh/id_rsa
    [+] SSH User of host (ip_or_dns_host1) [ubuntu]: rke
    [+] Is host (ip_or_dns_host1) a Control Plane host (y/n)? [y]: y
    [+] Is host (ip_or_dns_host1) a Worker host (y/n)? [n]: y
    [+] Is host (ip_or_dns_host1) an etcd host (y/n)? [n]: y
    [+] Override Hostname of host (ip_or_dns_host1) [none]:
    [+] Internal IP of host (ip_or_dns_host1) [none]:
    [+] Docker socket path on host (ip_or_dns_host1) [/var/run/docker.sock]:
    [+] SSH Address of host (2) [none]: ip_or_dns_host2
    [+] SSH Port of host (2) [22]:
    [+] SSH Private Key Path of host (ip_or_dns_host2) [none]:
    [-] You have entered empty SSH key path, trying fetch from SSH key parameter
    [+] SSH Private Key of host (ip_or_dns_host2) [none]:
    [-] You have entered empty SSH key, defaulting to cluster level SSH key: ~/.ssh/id_rsa
    [+] SSH User of host (ip_or_dns_host2) [ubuntu]: rke
    [+] Is host (ip_or_dns_host2) a Control Plane host (y/n)? [y]: y
    [+] Is host (ip_or_dns_host2) a Worker host (y/n)? [n]: y
    [+] Is host (ip_or_dns_host2) an etcd host (y/n)? [n]: y
    [+] Override Hostname of host (ip_or_dns_host2) [none]:
    [+] Internal IP of host (ip_or_dns_host2) [none]:
    [+] Docker socket path on host (ip_or_dns_host2) [/var/run/docker.sock]:
    [+] SSH Address of host (3) [none]: ip_or_dns_host3
    [+] SSH Port of host (3) [22]:
    [+] SSH Private Key Path of host (ip_or_dns_host3) [none]:
    [-] You have entered empty SSH key path, trying fetch from SSH key parameter
    [+] SSH Private Key of host (ip_or_dns_host3) [none]:
    [-] You have entered empty SSH key, defaulting to cluster level SSH key: ~/.ssh/id_rsa
    [+] SSH User of host (ip_or_dns_host3) [ubuntu]: rke
    [+] Is host (ip_or_dns_host3) a Control Plane host (y/n)? [y]: y
    [+] Is host (ip_or_dns_host3) a Worker host (y/n)? [n]: y
    [+] Is host (ip_or_dns_host3) an etcd host (y/n)? [n]: y
    [+] Override Hostname of host (ip_or_dns_host3) [none]:
    [+] Internal IP of host (ip_or_dns_host3) [none]:
    [+] Docker socket path on host (ip_or_dns_host3) [/var/run/docker.sock]:
    [+] Network Plugin Type (flannel, calico, weave, canal) [canal]:
    [+] Authentication Strategy [x509]:
    [+] Authorization Mode (rbac, none) [rbac]:
    [+] Kubernetes Docker image [rancher/hyperkube:v1.11.1-rancher1]:
    [+] Cluster domain [cluster.local]:
    [+] Service Cluster IP Range [10.43.0.0/16]:
    [+] Enable PodSecurityPolicy [n]:
    [+] Cluster Network CIDR [10.42.0.0/16]:
    [+] Cluster DNS Service IP [10.43.0.10]:
    [+] Add addon manifest URLs or YAML files [no]: yes
    [+] Enter the Path or URL for the manifest [none]: https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
    [+] Add another addon [no]: yes
    [+] Enter the Path or URL for the manifest [none]: https://gist.githubusercontent.com/superseb/499f2caa2637c404af41cfb7e5f4a938/raw/930841ac00653fdff8beca61dab9a20bb8983782/k8s-dashboard-user.yml
    [+] Add another addon [no]: no

When the last question is answered, the cluster.yml file will be created in the same directory as RKE was run:

ls -la cluster.yml
-rw-r—– 1 user user 3688 Sep 17 12:50 cluster.yml

You are now ready to build your Kubernetes cluster. This can be done by running rke up. Before you run the command, make sure the ports required are opened between your workstation/laptop/computer and the nodes, and between each of the nodes. You can now build your cluster using the following command:

$ ./rke up
INFO[0000] Building Kubernetes cluster

INFO[0151] Finished building Kubernetes cluster successfully

If all went well, you should have a lot of output from the command but it should end with Finished building Kubernetes cluster successfully. It will also write a kubeconfig file as kube_config_cluster.yml . You can use that file to connect to your Kubernetes cluster.

Exploring your Kubernetes cluster

Make sure you have kubectl installed, see https://kubernetes.io/docs/tasks/tools/install-kubectl/ how to get it for your platform.

Note: When running kubectl, it automatically tries to use a kubeconfig from the default location; $HOME/.kube/config. In the examples, we explicitly specify the kubeconfig file using –kubeconfig kube_config_cluster.yml. If you don’t want to specify the kubeconfig file every time, you can copy the file kube_config_cluster.yml to $HOME/.kube/config. (you probably need to create the directory $HOME/.kube first)

Start with querying the server for its version:

$ kubectl –kubeconfig kube_config_cluster.yml version

Client Version: version.Info
Server Version: version.Info

One of the first things to check, is if all nodes are in Ready state:

$ kubectl –kubeconfig kube_config_cluster.yml get nodes
NAME STATUS ROLES AGE VERSION
host1 Ready controlplane,etcd,worker 11m v1.11.1
host2 Ready controlplane,etcd,worker 11m v1.11.1
host3 Ready controlplane,etcd,worker 11m v1.11.1

When you generated the cluster configuration file, you added the Kubernetes dashboard addon to be deployed on the cluster. You can check the status of the deployment using:

$ kubectl –kubeconfig kube_config_cluster.yml get deploy -n kube-system -l k8s-app=kubernetes-dashboard

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-dashboard 1 1 1 1 17m

By default, the deployments are not exposed to the outside. If you want to visit the Kubernetes Dashboard in your browser, you will need to expose the deployment externally (which we will do in our demo application later) or use the built-in proxy functionality of kubectl. This will open the 127.0.0.1:8001 (your local machine on port 8001) and tunnel it to the Kubernetes cluster.

Before you can visit the Kubernetes Dashboard, you need to retrieve the token to login to the dashboard. By default, it runs under a very limited account and will not be able to show you all the resources in your cluster. The second addon we added when creating the cluster configuration file created the account and token we need (this is based upon https://github.com/kubernetes/dashboard/wiki/Creating-sample-user)

You can retrieve the token by running:

$ kubectl –kubeconfig kube_config_cluster.yml -n kube-system describe secret $(kubectl –kubeconfig kube_config_cluster.yml -n kube-system get secret | grep admin-user | awk ”) | grep ^token: | awk ‘{ print $2 }’

eyJhbGciOiJSUzI1NiIs….<more_characters>

The string that is returned, is the token you need to login to the dashboard. Copy the whole string.

Set up the kubectl proxy as follows:

$ kubectl –kubeconfig kube_config_cluster.yml proxy

Starting to serve on 127.0.0.1:8001

And open the following URL:

http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/

When prompted for login, choose Token, paste the token and click Sign In.

Note: When you don’t get a login screen, open it manually by clicking Sign In on the top right.

Run a demo application

Last step of this post, running a demo application and exposing it. For this example you will run a demo application superseb/rancher-demo, which is a web UI showing the scale of a deployment. It will be exposed using an Ingress, which is handled by the NGINX Ingress controller that is deployed by default. If you want to know more about Ingress, please see https://kubernetes.io/docs/concepts/services-networking/ingress/

Start by deploying and exposing the demo application (which runs on port 8080):

$ kubectl –kubeconfig kube_config_cluster.yml run –image=superseb/rancher-demo rancher-demo –port 8080 –expose
service/rancher-demo created
deployment.apps/rancher-demo created

Check the status of your deployment:

$ kubectl –kubeconfig kube_config_cluster.yml rollout status deployment/rancher-demo

deployment “rancher-demo” successfully rolled out

The command kubectl run is the easiest way to get a container running on your cluster. It takes an image parameter to specify the Docker image and a name at minimum. In this case, we also want to configure the port that this container exposes (internally), and expose it. What happened was that there was a Deployment created (and a ReplicaSet) with a scale of 1 (default), and a Service was created to abstract access to the pods (which can contain one or more containers, in this case 1). For more information on these subjects check the following links:

RKE deploys the NGINX Ingress controller by default on every node. This opens op port 80 and port 443, and can serve as main entrypoint for any created Ingress. An Ingress can contain a single host or multiple, multiple paths, and you can configure SSL certificates. In this post you will configure a basic Ingress, making our demo application accessible on a certain hostname. In the example we will use rancher-demo.domain.test as hostname to access the demo application.

Note: To access our test domain you have to add the domain name to /etc/hosts to visit the UI, as it’s not a valid DNS name. If you have access to your own domain, you can add a DNS A record pointing to each of the nodes.

The only part that is not created, is the Ingress. Let’s create an Ingress calledrancher-demo-ingress, having a host specification to match requests to our test domain (rancher-demo.domain.test), and pointing it to our Service called rancher-demo on port 8080. Save the following content to a file called ingress.yml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: rancher-demo-ingress
spec:
rules:
– host: rancher-demo.domain.test
http:
paths:
– path: /
backend:
serviceName: rancher-demo
servicePort: 8080

Create this Ingress using kubectl:

$ kubectl –kubeconfig kube_config_cluster.yml apply -f ingress.yml
ingress.extensions/rancher-demo-ingress created

It is time to test accessing the demo application. You can try it on the command-line first, instruct curl to resolve the test domain to each of the nodes:

# Get node IP addresses
$ kubectl –kubeconfig kube_config_cluster.yml get nodes
NAME STATUS ROLES AGE VERSION
10.0.0.1 Ready controlplane,etcd,worker 3h v1.11.1
10.0.0.2 Ready controlplane,etcd,worker 3h v1.11.1
10.0.0.3 Ready controlplane,etcd,worker 3h v1.11.1
# Test accessing the demo application
$ curl –resolve rancher-demo.domain.test:80:10.0.0.1 [http://rancher-demo.domain.test/ping](http://rancher-demo.domain.test/ping)

{“instance”:”rancher-demo-5cbfb4b4-thmbh”,”version”:”0.1″}
$ curl –resolve rancher-demo.domain.test:80:10.0.0.2 [http://rancher-demo.domain.test/ping](http://rancher-demo.domain.test/ping)

{“instance”:”rancher-demo-5cbfb4b4-thmbh”,”version”:”0.1″}
$ curl –resolve rancher-demo.domain.test:80:10.0.0.3
[http://rancher-demo.domain.test/ping](http://rancher-demo.domain.test/ping)

{“instance”:”rancher-demo-5cbfb4b4-thmbh”,”version”:”0.1″}

If you use the test domain, you will need to add it to your machine’s /etc/hosts file to be able to reach it properly.

echo “10.0.0.1 rancher-demo.domain.test” | sudo tee -a /etc/hosts

Now visit http://rancher-demo.domain.test in your browser.

If this has all worked out, you can fill up the demo application a bit more by scaling up your Deployment:

$ kubectl –kubeconfig kube_config_cluster.yml scale deploy/rancher-demo –replicas=10
deployment.extensions/rancher-demo scaled

Note: Make sure to clean up the /etc/hosts entry when you are done.

Closing words

This started as a post how to create a Kubernetes cluster in under 10 minutes, but along the way I tried to add some useful information how certain parts work. To avoid having a post that takes a day to read (explaining every part), there will be other posts describing certain parts. For now, I’ve linked as much resources as possible to existing documentation where you can learn more.

Sebastiaan van Steenis

Sebastiaan van Steenis

Support Engineer

github

Source

OpenFaaS round-up: CloudEvents, AWS Fargate and OpenFaaS Operator

In this post I’ll round-up the highlights of the OpenFaaS community meeting from 25th May.

The all-hands community meetings are a great way to remain connected to the project and community where we have a chance to share our finest work and importantly get face-to-face time with each other. One of the things I like the most is hearing about the various backgrounds people have with our community covering both the users, developers and operators of OpenFaaS.

Highlights

I kicked-off the meeting with a project update and an announcement of a new core contributor Lucas Roesler. Lucas has shown a commitment to the project and willingness to take responsibility and ownership for features like the secrets management for functions.

The Core contributors group helps to build and maintain OpenFaaS, taking responsibility for and pride in the project.

We then had three demos from the community.

1. OpenFaaS on AWS Fargate/ECS

I’ve known Ed Wilde for some time and it was great to see him getting into the Cloud Native world with his start-up. Ed works in London and has been putting together a Proof-of-Concept provider for OpenFaaS that allows functions to be managed on AWS Fargate/ECS. He’s going to release his code when it’s feature complete, but here’s a preview:

Community meeting – excited to see the progress @ewilde is making with faas-fargate – @OpenFaaS on AWS Fargate. Why is Ed interested in building this? Because he’s in a small team and they want to manage all their services in the same way. #TeamServerless #FaaSFriday pic.twitter.com/zUnjx90xBe

— Alex Ellis (@alexellisuk) May 25, 2018

2. CloudEvents integration

CloudEvents is a designed-by-committee spec for how clouds should publish events about their managed services such as S3 CRUD events or changes to DB rows such as when a new document is added to DynamoDB.

John McCabe gave us background on the CloudEvents 0.1 spec and showed us a demo of how he was able to integrate with events from Azure EventGrid in the CloudEvents 0.1 format without changing any code in OpenFaaS.

We’re learning about how @mccabejohn was able to consume CloudEvents 0.1 within @openfaas without changing any code on the #FaaSFriday community call. Good attendance for a bank-holiday weekend! #teamserverless pic.twitter.com/suL29AM71Y

— Alex Ellis (@alexellisuk) May 25, 2018

3. The OpenFaaS Operator with Weave Flux

Stefan Prodan showed us the culmination of a week’s work on a blog post that combines Flux and Weave Cloud from Weaveworks with Heptio Contour, Bitnami SealedSecrets and the OpenFaaS Kubernetes Operator to deliver hardened CD with GitOps on GKE.

In our community call for @OpenFaaS – we’re getting a demo of OpenFaaS Operator, @HeptioContour, @bitnami SealedSecrets, @weaveworks Flux and Cloud – for a hardened GitOps on GKE with NetworkPolicy. Thanks @stefanprodan #FaaSFriday https://t.co/CXDDZsPHj3 pic.twitter.com/thuSWncTtI

— Alex Ellis (@alexellisuk) May 25, 2018

Bonus community material

I want to give a shout-out for Robert Malmstein from Holberton School in San Francisco. We met in April earlier this year at Cisco’s DevNet Create conference, since I’ve been meeting with and mentoring Robert with his studies. We got together for coffee to talk about his latest project and that got me thinking about how functions could be applied.

Great to meet up with my new friends from @holbertonschool @fallenicicle and @RobertMalmstein – we met at @DevNetCreate in April. Today had coffee today, started some mentoring and hacking for Robert’s project – locate a trash can in SF – keep SF tidy. pic.twitter.com/wQ2qMPQQdB

— Alex Ellis (@alexellisuk) May 29, 2018

Robert’s idea is simple – making public asset data available on mobile devices such as the location of the nearest trash can or park bench for some R&R in the city between busy meetings. Robert’s project can be taken further to crowd-source and vote up new locations to gamify the experience.

Check out this GitHub repository for the sample code implemented with OpenFaaS functions.

Video recording

You can view the whole video recording here on YouTube:

Get involved

  • Try the OpenFaaS Operator

OpenFaaS supports Kubernetes through the faas-netes project, but the OpenFaaS Operator offers a deeper extension into the Kubernetes API meaning you can manage your functions both through the RESTful API and with kubectl.

Try it out on GitHub

  • Experiment with CloudEvents

If you’re an Azure user then there’s already support for publishing events from EventGrid in the CloudEvents JSON format with inbound webhooks. Check out John’s GitHub account.

  • Join the Community

Whether you’re new to this space, have questions or want to start contributing to Open Source you’re welcome to join our community.

The easiest way to get connected into the project is to join us over on the OpenFaaS Slack workspace and on GitHub: https://github.com/openfaas/

Source

A Practical Introduction to Container Terminology

By Scott McCarty  February 22, 2018July 16, 2018

You might think containers seem like a pretty straightforward concept, so why do I need to read about container terminology? In my work as a container technology evangelist, I’ve encountered misuse of container terminology that causes people to stumble on the road to mastering containers. Terms like containers and images are used interchangeably, but there are important conceptual differences. In the world of containers, repository has a different meaning than what you’d expect. Additionally, the landscape for container technologies is larger than just docker. Without a good handle on the terminology, It can be difficult to grasp the key differences between docker and (pick your favorites, CRI-O, rkt, lxc/lxd) or understand what the Open Container Initiative is doing to standardize container technology.

It is deceptively simple to get started with Linux Containers. It takes only a few minutes to install a container engine like docker and run your first commands. Within another few minutes, you are building your first container image and sharing it. Next, you begin the familiar process of architecting a production-like container environment, and have the epiphany that it’s necessary to understand a lot of terminology and technology behind the scenes. Worse, many of the following terms are used interchangeably… often causing quite a bit of confusion for newcomers.

  • Container
  • Image
  • Container Image
  • Image Layer
  • Registry
  • Repository
  • Tag
  • Base Image
  • Platform Image
  • Layer

Understanding the terminology laid out in this technical dictionary will provide you a deeper understanding of the underlying technologies. This will help you and your teams speak the same language and also provide insight into how to better architect your container environment for the goals you have. As an industry and wider community, this deeper understanding will enable us to build new architectures and solutions. Note, this technical dictionary assumes that the reader already has an understanding of how to run containers. If you need a primer, try starting with A Practical Introduction to Docker Containers on the Red Hat Developer Blog.

Containers 101

To understand container terminology, it’s important to understand exactly what a container is – with technical precision. A container is really two different things. Like a normal Linux program, containers really have two states – rest and running. When at rest, a container is a file (or set of files) that is saved on disk. This is referred to as a Container Image or Container Repository. When you type the command to start a container, the Container Engine unpacks the required files and meta-data, then hands them off to the the Linux kernel. Starting a container is very similar to starting a normal Linux process and requires making an API call to the Linux kernel. This API call typically initiates extra isolation and mounts a copy of the files that were in the container image. Once running, Containers are just a Linux process. The process for starting containers, as well as the image format on disk, are defined and governed by standards.

There are several competing Container Image formats (Docker, Appc, LXD), but the industry is moving forward with a standard governed under the Open Container Initiative – sometimes referred to simply as Open Containers or the OCI. The scope of the OCI includes a Container Image Format Specification, which defines the on-disk format for container images as well as the meta-data which defines things like hardware architecture and the operating system (Linux, Windows, etc). An industry wide container image format enables ecosystems of software to flourish – different individual contributors, projects, and vendors are able to build images and tooling, which are interoperable. Users want interoperability between tools for signing, scanning, building, running, moving and managing container images.

There are also several competing Container Engines including Docker, CRI-O, Railcar, RKT, LXC. These Container Engines take a Container Image and turn it into a Container (aka running processes). How this happens is governed by the scope of the OCI which includes a Container Runtime Specification and a Reference Runtime Implementation called RunC. This reference implementation is open source, governed by a community development model, and commonly used by many container engines to communicate with the host kernel when creating containers.

Tools which target the OCI Container Image Format Specification and Container Runtime Specification ensure portability between a broad ecosystem of container platforms, container engines, and supporting tools across cloud providers and on premise architectures. Understanding the nomenclature, container standards, and the architecture of the building blocks of containers, will ensure that you can communicate with other architects to build scalable & supportable containerized applications and environments to productively run containers for years to come.

Background

Containers 101

Basic Vocabulary

Container Image

Container Image Format

Container Engine

Container

Container Host

Registry Server

Container Orchestration

Advanced Vocabulary

Container Runtime

Image Layer

Tag

Repository

Namespace

Kernel Namespace

Graph Driver

Container Use Cases

Application Containers

Operating System Containers

Pet Containers

Super Privileged Containers

Tools & Operating System Software

Architecture of Containers

Application Images

Base Images

Builder Images

Containerized Components

Deployer Images

Intermediate Images

Intermodal Container Images

System Containers

Conclusion

Container Image

See also Repository.

A container image, in its simplest definition, is a file which is pulled down from a Registry Server and used locally as a mount point when starting Containers. The container community uses “container image” quite a bit, but this nomenclature can be quite confusing. Docker, RKT, and even LXD, operate on the concept of pulling remote files and running them as a Container. Each of these technologies treats container images in different ways. LXD pulls a single container image (single layer), while docker and RKT use OCI-based images which can be made up of multiple layers.

Technically, it is much more complicated than a single file on a Registry Server. When people use the term “container image,” they often mean to imply Repository and referring to a bundle of multiple container Image Layers as well as metadata which provides extra information about the layers.

Implicit in the concept of a container image is the concept of a Container Image Format.

Container Image Format

See Container Image and Background.

Historically, each Container Engine had its container images format. LXD, RKT, and Docker all had their own image formats. Some were made up of a single layer, while others were made up of a bunch of layers in a tree structure. Today, almost all major tools and engines have moved to a format defined by the Open Container Initiative (OCI).This image format defines the layers and metadata within a container image. Essentially, the OCI image format defines a container image composed of tar files for each layer, and a manifest.json file with the metadata.

The Open Container Initiative (OCI), which was originally based on the Docker V2 image format, has successfully unified a wide ecosystem of container engines, cloud providers and tools providers (security scanning, signing, building and moving). This will help protect users as the invest in knowledge, and tooling in their environments.

Container Engine

See also Container Runtime.

A container engine is a piece of software that accepts user requests, including command line options, pulls images, and from the end user’s perspective runs the container. There are many container engines, including docker, RKT, CRI-O, and LXD. Also, many cloud providers, Platforms as a Service (PaaS), and Container Platforms have their own built-in container engines which consume Docker or OCI compliant Container Images. Having an industry standard Container Image Format allows interoperability between all of these different platforms.

Going one layer deeper, most container engines don’t actually run the containers, they rely on an OCI compliant runtime like runc. Typically, the container runtime is responsible:

  • Handling user input
  • Handling input over an API often from a Container Orchestrator
  • Pulling the Container Images from the Registry Server
  • Expanding decompressing and expanding the container image on disk using a Graph Driver (block, or file depending on driver)
  • Preparing a container mount point, typically on copy-on-write storage (again block or file depending on driver)
  • Preparing the metadata which will be passed to the container Container Runtime to start the Container correctly
    • Using some defaults from the container image (ex.ArchX86)
    • Using user input to override defaults in the container image (ex. CMD, ENTRYPOINT)
    • Using defaults specified by the container image (ex. SECCOM rules)
  • Calling the Container Runtime

For an even deeper understanding, please see Understanding the Container Standards. See also Container Runtime.

Container

Containers have existed within operating systems for quite a long time. A container is the runtime instantiation of a Container Image. A container is a standard Linux process typically created through a clone() system call instead of fork() or exec(). Also, containers are often isolated further through the use of cgroups, SELinux or AppArmor.

Container Host

The container host is the system that runs the containerized processes, often simply called containers. This could be, for example, RHEL Atomic Host running in a VM, as an instance in the public cloud, or on bare metal in your data center. Once a container image (aka repository) is pulled from a Registry Server to the local container host, it is said to be in the local cache.

Determining which repositories are synchronized to the local cache can be done with the following command:

[root@rhel7 ~]# docker images -a

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
registry.access.redhat.com/rhel7 latest 6883d5422f4e 3 weeks ago 201.7 MB

Registry Server

A registry server is essentially a fancy file server that is used to store docker repositories. Typically, the registry server is specified as a normal DNS name and optionally a port number to connect to. Much of the value in the docker ecosystem comes from the ability to push and pull repositories from registry servers.

image01

When a docker daemon does not have a locally cached copy of a repository, it will automatically pull it from a registry server. Most Linux distributions have the docker daemon configured to pull from docker.io but it is configurable on some Linux distributions. For example, Red Hat Enterprise Linux is configured to pull repositories from registry.access.redhat.com first, then it will try docker.io (Docker Hub).

It is important to stress that there is implicit trust in the registry server. You must determine how much you trust the content provided by the registry and you may want to allow or block certain registries. In addition to security, there are other concerns such as users having access to licensed software and compliance issues. The simplicity with which docker allows users to pull software makes it critical that you trust upstream content.

In Red Hat Enterprise Linux, the default docker registry is configurable. Specific registry servers can be added or blocked in RHEL7 and RHEL7 Atomic by modifying the configuration file:

vi /etc/sysconfig/docker

In RHEL7 and RHEL 7 Atomic, Red Hat’s registry server is configured out of the box:

ADD_REGISTRY=’–add-registry registry.access.redhat.com’

As a matter of security, it may be useful to block public docker repositories such as DockerHub:

# BLOCK_REGISTRY=’–block-registry’

Red Hat also offers an integrated Registry Server with OpenShift Container Platform, a standalone enterprise Registry Server with Quay Enterprise, as well as cloud based, public and private repositories on Quay.io.

Container Orchestration

Often teams start with installing a Container Host, then pulling some Container Images. Then they move on to building some new Container Images and pushing them to a Registry Server to share with others on their team. After a while they want to wire a few containers together and deploy them as a unit. Finally, at some point, they want to push that unit into a pipeline (Dev/QA/Prod) leading towards production. This is the path towards the realization that orchestration is needed.

A container orchestrator really does two things:

  1. Dynamically schedules container workloads within a cluster of computers. This is often referred to as distributed computing.
  2. Provides a standardized application definition file (kube yaml, docker compose, etc)

The above two features provide many capabilities:

  1. Allows containers within an application to be scheduled completely separately. This is useful if:
    • Allows the utilization of large clusters of Container Hosts
    • Individual containers fail (process hang, out of memory (OOM))
    • Container Hosts fail (disk, network, reboot)
    • Container Engines fail (corruption, restart)
    • Individual containers need to be scaled up, or scaled down
  2. It’s easy to deploy new instances of the same application into new environments. In a cloud native world, or traditional world, there are many reasons why you might want to do this including:
    • On developers laptop with a container orchestrator running
    • On a shared development environment in a private namespace
    • On a shared development environment in an internally public namespace for visibility and testing
    • On a quality assurance (QA) environment internally
    • On a load test environment dynamically provisioned and deprovisioned in the cloud
    • On a gold environment to test compatibility with production
    • On a production environment
    • On a disaster recovery environment
    • On a new production environment which has upgraded Container Hosts, Container Engines, or container orchestrators
    • On a new production environment with the same versions of Container Host, Container Engine, and container orchestrator in a new geolocation (APAC, EMEA, etc)

There are many container schedulers being developed in the community and by vendors. Historically, Swarm, Mesos, and Kubernetes were the big three, but recently even Docker and Mesosphere have announced support for Kubernetes – as has almost every major cloud service provider. Kubernetes has become the defacto standard in container orchestration, similar to Linux before it. If you are looking at container orchestration, Red Hat recommends our enterprise distribution called OpenShift.

Container Runtime

A container runtime a lower level component typically used in a Container Engine but can also be used by hand for testing. The Open Containers Initiative (OCI) Runtime Standard reference implementation is runc. This is the most widely used container runtime, but there are others OCI compliant runtimes, such as crun, railcar, and katacontainers. Docker, CRI-O, and many other Container Engines rely on runc.

The container runtime is responsible for:

  • Consuming the container mount point provided by the Container Engine (can also be a plain directory for testing)
  • Consuming the container metadata provided by the Container Engine (can be a also be a manually crafted config.json for testing)
  • Communicating with the kernel to start containerized processes (clone system call)
  • Setting up cgroups
  • Setting up SELinux Policy
  • Setting up App Armor rules

To provide a bit of history, when the Docker engine was first created it relied on LXC as the container runtime. Later, the Docker team developed their own library called libcontainer to start containers. This library was written in Golang, and compiled into the original Docker engines. Finally, when the OCI was created, Docker donated the libcontainer code and turned it into a standalone utility called runc. Now, runc is the reference implementation and used by other Container Engines such as CRI-O. At the lowest level, this provides the ability to start a container consistently, no matter the container engine. Runc is a very terse utility and expects a mount point (directory) and meta-data (config.json) to be provided to it. See this tutorial for more on runc.

For an even deeper understanding, please see Understanding the Container Standards. See also Container Runtime.

Image Layer

Repositories are often referred to as images or container images, but actually they are made up of one or more layers. Image layers in a repository are connected together in a parent-child relationship. Each image layer represents changes between itself and the parent layer.

Below, we are going to inspect the layers of a repository on the local container host. Since Docker 1.7, there is no native tooling to inspect image layers in a local repository (there are tools for online registries). With the help of a tool called Dockviz, you can quickly inspect all of the layers. Notice that each layer has tag and a Universally Unique Identifier (UUID). The following command will returned shortened versions of the UUID that are typically unique enough to work with on a single machine. If you need to the full UUID, use the –no-trunc option.

docker run –rm –privileged -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images -t

├─2332d8973c93 Virtual Size: 187.7 MB
│ └─ea358092da77 Virtual Size: 187.9 MB
│ └─a467a7c6794f Virtual Size: 187.9 MB
│ └─ca4d7b1b9a51 Virtual Size: 187.9 MB
│ └─4084976dd96d Virtual Size: 384.2 MB
│ └─943128b20e28 Virtual Size: 386.7 MB
│ └─db20cc018f56 Virtual Size: 386.7 MB
│ └─45b3c59b9130 Virtual Size: 398.2 MB
│ └─91275de1a5d7 Virtual Size: 422.8 MB
│ └─e7a97058d51f Virtual Size: 422.8 MB
│ └─d5c963edfcb2 Virtual Size: 422.8 MB
│ └─5cfc0ce98e02 Virtual Size: 422.8 MB
│ └─7728f71a4bcd Virtual Size: 422.8 MB
│ └─0542f67da01b Virtual Size: 422.8 MB Tags: docker.io/registry:latest

Notice, that the “docker.io/registry” repository is actually made up of many images layers. More importantly, notice that a user could potentially “run” a container based off of any one of these layers. The following command is perfectly valid, though not guaranteed to have been tested or actually even work correctly. Typically, an image builder will tag (create a name for) specific layers that you should use:

docker run -it 45b3c59b9130 bash

Repositories are constructed this way because whenever an image builder creates a new image, the differences are saved as a layer. There are two main ways that new layers are created in a repository. First, if building an image manually, each “commit” creates a new layer. If the image builder is building an image with a Dockerfile, each directive in the file creates a new layer. It is useful to have visibility into what has changed in a container repository between each layer.

Tag

Even though a user can designate that a container mount and start from any layer in a repository, they shouldn’t necessarily do that. When an image builder creates a new repository, they will typically label the best image layers to use. These are called tags and are a tool for container image builders to communicate to container image consumers which layers are best to consume. Typically, tags are used to designate versions of software within in the repository. This is by convention only – in reality, the OCI nor any other standard mandates what tags can be used for, and they can be abused for anything that a user wants. Be careful dong this because it can create a lot of confusion in development, operations and architecture teams, so document it it well if you use it for anything other than software version.

There is one special tag – latest – which typically points to the layer containing the latest version of software in the repository. This special tag still just points to an image layer, like any other tag, so it can be abused.

To remotely view the tags available in a repository, run the following command (the jq utility makes the output a lot more readable):

curl -s registry.access.redhat.com/v1/repositories/rhel7/tags | jq
{
“7.0-21”: “e1f5733f050b2488a17b7630cb038bfbea8b7bdfa9bdfb99e63a33117e28d02f”,
“7.0-23”: “bef54b8f8a2fdd221734f1da404d4c0a7d07ee9169b1443a338ab54236c8c91a”,
“7.0-27”: “8e6704f39a3d4a0c82ec7262ad683a9d1d9a281e3c1ebbb64c045b9af39b3940”,
“7.1-11”: “d0a516b529ab1adda28429cae5985cab9db93bfd8d301b3a94d22299af72914b”,
“7.1-12”: “275be1d3d0709a06ff1ae38d0d5402bc8f0eeac44812e5ec1df4a9e99214eb9a”,
“7.1-16”: “82ad5fa11820c2889c60f7f748d67aab04400700c581843db0d1e68735327443”,
“7.1-24”: “c4f590bbcbe329a77c00fea33a3a960063072041489012061ec3a134baba50d6”,
“7.1-4”: “10acc31def5d6f249b548e01e8ffbaccfd61af0240c17315a7ad393d022c5ca2”,
“7.1-6”: “65de4a13fc7cf28b4376e65efa31c5c3805e18da4eb01ad0c8b8801f4a10bc16”,
“7.1-9”: “e3c92c6cff3543d19d0c9a24c72cd3840f8ba3ee00357f997b786e8939efef2f”,
“7.2”: “6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e”,
“7.2-2”: “58958c7fafb7e1a71650bc7bdbb9f5fd634f3545b00ec7d390b2075db511327d”,
“7.2-35”: “6883d5422f4ec2810e1312c0e3e5a902142e2a8185cd3a1124b459a7c38dc55b”,
“7.2-38”: “6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e”,
“latest”: “6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e”
}

Repository

When using the docker command, a repository is what is specified on the command line, not an image. In the following command, “rhel7” is the repository.

docker pull rhel7

This is actually expanded automatically to:

docker pull registry.access.redhat.com/rhel7:latest

This can be confusing, and many people refer to this as an image or a container image. In fact, the docker images sub-command is what is used to list the locally available repositories. Conceptually, these repositories can be thought of as container images, but it’s important to realize that these repositories are actually made up of layers and include metadata about in a file referred to as the manifest (manifest.json):

docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
registry.access.redhat.com/rhel7 latest 6883d5422f4e 4 weeks ago 201.7 MB
registry.access.redhat.com/rhel latest 6883d5422f4e 4 weeks ago 201.7 MB
registry.access.redhat.com/rhel6 latest 05c3d56ba777 4 weeks ago 166.1 MB
registry.access.redhat.com/rhel6/rhel latest 05c3d56ba777 4 weeks ago 166.1 MB

When we specify the repository on the command line, the Container Engine is doing some extra work for you. In this case, the docker daemon (not the client tool) is configured with a list of servers to search. In our example above, the daemon will search for the “rhel7” repository on each of the configured servers.

In the above command, only the repository name was specified, but it’s also possible to specify a full URL with the docker client. To highlight this, let’s start with dissecting a full URL.

image02

Another way you will often see this specified is:

REGISTRY/NAMESPACE/REPOSITORY[:TAG]

The full URL is made up of a standard server name, a namespace, and optionally a tag. There are actually many permutations of how to specify a URL and as you explore the docker ecosystem, you will find that many pieces are optional. The following commands are all valid and all pull some permutation of the same repository:

docker pull registry.access.redhat.com/rhel7/rhel:latest
docker pull registry.access.redhat.com/rhel7/rhel
docker pull registry.access.redhat.com/rhel7
docker pull rhel7/rhel:latest

Namespace

A namespace is a tool for separating groups of repositories. On the public DockerHub, the namespace is typically the username of the person sharing the image, but can also be a group name, or a logical name.

Red Hat uses the namespace to separate groups of repositories based on products listed on the Red Hat Federated Registry server. Here are some example results returned by registry.access.redhat.com. Notice, the last result is actually listed on another registry server. This is because Red Hat works to also list repositories on our partner’s registry servers:

registry.access.redhat.com/rhel7/rhel
registry.access.redhat.com/openshift3/mongodb-24-rhel7
registry.access.redhat.com/rhscl/mongodb-26-rhel7
registry.access.redhat.com/rhscl_beta/mongodb-26-rhel7
registry-mariadbcorp.rhcloud.com/rhel7/mariadb-enterprise-server:10.0

Notice, that sometimes the full URL does not need to be specified. In this case, there is a default repository for a given namespace. If a user only specifies the fedora namespace, the latest tag from the default repository will be pulled to the local server. So, running the following commands is essentially the same, each one more specific:

docker pull fedora
docker pull docker.io/fedora
docker pull docker.io/library/fedora:latest

Kernel Namespace

A kernel namespace is completely different than the namespace we are referring to when discussing Repositories and Registry Servers. When discussing containers, Kernel namespaces are perhaps the most important data structure, because they enable containers as we know them today. Kernel namespaces enable each container to have it’s own mount points, network interfaces, user identifiers, process identifiers, etc.

When you type a command in a Bash terminal and hit enter, Bash makes a request to the kernel to create a normal Linux process using a version of the exec() system call. A container is special because when you send a request to a container engine like docker, the docker daemon makes a request to the kernel to create a containerized process using a different system call called clone(). This clone() system call is special because it can create a process with its own virtual mount points, process ids, user ids, network interfaces, hostname, etc

While, technically, there is no single data structure in Linux that represents a container, kernel namespaces and the clone() system call are as close as it comes.

Graph Driver

When the end user specifies the Tag of a container image to run – by default this is the latest Tag – the graph driver unpacks all of the dependent Image Layers necessary to construct the data in the selected Tag. The graph driver is the piece of software that maps the necessary image layers in the Repository to a piece of local storage. The container image layers can be mapped to a directory using a driver like Overlay2 or in block storage using a driver like Device Mapper. Drivers include: aufs, devicemapper, btrfs, zfs, and overlayfs.

When the container is started, the image layers are mounted read-only with a kernel namespace. The Image Layers from the Repository are always mounted read only but by default, a separate copy-on-write layer is also set up. This allows the containerized process to write data within the container. When data is written, it is stored in the copy-on-write layer, on the underlying host. This copy-on-write layer can be disabled by running the container with an option such as –readonly.

The docker daemon has it’s own set of Graph Drivers and there are other open source libraries which provide Graph Drivers such as containers/images which is used in tools like CRI-O, Skopeo and other container engines.

Determining which graph driver you are using can be done with the docker info command:

[root@rhel7 ~]# docker info


Storage Driver: devicemapper
Pool Name: docker-253:1-884266-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: xfs
Data file: /dev/loop0
Metadata file: /dev/loop1
Data Space Used: 3.037 GB
Data Space Total: 107.4 GB
Data Space Available: 2.56 GB
Metadata Space Used: 2.707 MB
Metadata Space Total: 2.147 GB
Metadata Space Available: 2.145 GB
Udev Sync Supported: true
Deferred Removal Enabled: false
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.107-RHEL7 (2015-10-14)

There are many types of Container design patterns forming. Since containers are the run time version of a container image, the way it is built is tightly coupled to how it is run.

Some Container Images are designed to be run without privilege, while others are more specialized and require root-like privileges. There are many dimensions in which patterns can be evaluated and often users will see multiple patterns or use cases tackled together in one container image/container.

This section will delve into some of the common use cases that users are tackling with containers.

Application Containers

Application containers are the most popular form of container. These are what developers and application owner’s care about. Application containers contain the code that developers work on. They also include things like MySQL, Apache, MongoDB, and or Node.js.

There is a great ecosystem of application containers forming. Projects like Software Collections are providing secure and supportable applications container images for use with Red Hat Enterprise Linux. At the same time, Red Hat community members are driving some great cutting edge applications containers.

Red Hat believes that Application Containers should not typically require special privileges to run their workloads. That said, production container environments typically require much more than just non-privileged application containers to provide other supporting services.

Operating System Containers

See also System Containers

Operating System Containers are containers which are treated more like a full virtual operating system. Operating System Containers still share a host kernel, but run a full init system which allows them to easily run multiple processes. LXC and LXD are examples of Operating System Containers because they are treated much like a full virtual machine.

It is also possible to approximate an Operating System Container with docker/OCI based containers, but requires running systemd inside the container. This allows an end user to install software like they normally would and treat the container much more like a full operating system.

yum install mysql
systemctl enable mysql

This makes it easier to migrate existing applications. Red Hat is working hard to make Operating System Containers easier by enabling systemd to run inside a container and by enabling management with machined. While many customers aren’t (yet) ready to adopt micro-services, they can still gain benefits from adopting image based containers as a software delivery model.

Pet Containers

While Red Hat certainly recommends, supports and evangelizes the use cloud native patterns for new application development, in reality not all existing applications will be rewritten to take advantage of new patterns. Many existing applications are one of a kind, and one of kind applications are often referred to as Pets. Containers built specifically to handle these pet applications are sometimes referred to as Pet Containers

Pet containers provide users with the portability and convenience of a standardized container infrastructure relying on registry servers, container images, and standard container hosts for infrastructure, but provide the flexibility of a traditional environment within the container. The idea is to make things easier to containerize existing applications, such as using systemd in a container. The goal is to reuse existing automation, installers, and tools to easily create a container image that just runs.

Super Privileged Containers

When building container infrastructure on dedicated container hosts such as Red Hat Enterprise Linux Atomic Host, systems administrators still need to perform administrative tasks. Whether used with distributed systems, such as Kubernetes or OpenShift or standalone container hosts, Super Privileged Containers (SPC) are a powerful tool. SPCs can even do things like load specialized kernel modules, such as with systemtap.

In an infrastructure that is built to run containers, administrators will most likely need SPCs to do things like monitoring, backups, etc. It’s important to realize that there is typically a tighter coupling between SPCs and the host kernel, so administrators need to choose a rock solid container host and standardize on it, especially in a large clustered/distributed environment where things are more difficult to troubleshoot. They then need to select a user space in the SPC that is compatible with the host kernel.

Tools & Operating System Software

Linux distributions have always provided users with system software such as Rsyslogd, SSSD, sadc, etc. Historically, these pieces of system software were installed through RPM or DEB packages. But with the advent of containers as a packaging format, it has become both convenient and easy to install system software through containers images. Red Hat provides some pre-packaged containers for things like the Red Hat Virtualization tools, rsyslog, sssd, and sadc.

New design patterns are forming as more and more people deliver software with containers. Red Hat engineering is leveraging and driving many of these patterns in the community. The goal of this section is to help highlight and define some of these patterns.

The way a container is saved on disk (i.e. its image format) can have a dramatic affect on how it is run. For example, a container which is designed to run sssd needs to have special privileges whenever it’s run, or it can’t do its job. The following is a short list of patterns that are forming in the container community:

Application Images

These images are what end users consume. Use cases range from databases and web servers, to applications and services buses. These can be built in house or delivered to a customer from an ISV. Often end users will investigate and care about what bits were used to create a standalone image. Standalone images are the easiest kind of image to consume, but the hardest to design, build, and patch.

Base Images

A base image is one of the simplest types of images, but you will find a lot of definitions. Sometimes users will refer to corporate standard build, or even an application image as the “base image.” Technically this is not a base image. These are Intermediate images.

Simply put, a base image is an image that has no parent layer. Typically, a base image contains a fresh copy of an operating system. Base images normally include the tools (yum, rpm, apt-get, dnf, microdnf) necessary to install packages / make updates to the image over time. While base images can be “hand crafted”, in practice they are typically produced and published by open source projects (like Debian, Fedora or CentOS) and vendors (like Red Hat). The provenance of base images is critical for security. In short, the sole purpose of a base image is to provide a starting place for creating your derivative images. When using a dockerfile, the choice of which base image you are using is explicit:

FROM registry.access.redhat.com/rhel7-atomic

Builder Images

These are a specialized form of container image which produce application container images as offspring. They include everything but a developer’s source code. Builder images include operating system libraries, language runtimes, middleware, and the source-to-image tooling.

When a builder image is run, it injects the developers source code and produces a ready-to-run offspring application container image. This newly created application container image can then be run in development or production.

For example, if a developer has PHP code and they want to run it in a container, they can use a PHP builder image to produce a ready to run application container image. The developer passes the GitHub URL where the code is stored and the builder image does the rest of the work for them. The output of a Builder container is an Application container image which includes Red Hat Enterprise Linux, PHP from Software Collections, and the developer’s code, all together, ready to run.

Builder images provide a powerful way to go from code to container quickly and easily, building off of trusted components.

Containerized Components

A container is meant to be deployed as part of a larger software system, not on its own. Two major trends are driving this.

First, microservices are driving the use of best of breed components – this is also driving the use of more components combined together to build a single application. Containerized components are meeting the need to deploy an expanding quantity of complex software more quickly and easily. Each of these components can have different revisions, and container images help enable this. Application definitions such as Kubernetes/OpenShift deployments yaml/json, open service broker, OpenShift Templates, and Helm Charts are all making it possible to define applications at a higher level.

Second, not all pieces of software are easy to deploy as containers. Sometimes, it makes sense to containerize only certain components which are easier to move to containers or provide more value to the overall project. With multi-service application, some services may be deployed as containers, while others may be deployed through traditional a traditional methodology such as an RPM or installer script – see Pet Containers. But, other components might be difficult to put into containers because they are too tightly coupled to break up, need access to special hardware, or perhaps requires lower level kernel APIs, etc. Within a larger application there will likely be parts of the application that can be containerized, and parts that can’t. Containerized components represent the parts that can and are containerized. Containerized components are intended to be ran as part of a specific application, not standalone. It’s important to understand that containerized components are not designed to function on their own. They provide value to a larger piece of software, but provide very little value on their own.

For example, when OpenShift Enterprise 3.0 was released, most of the core code was deployed using RPMs, but after installation administrators deployed the router and registry as containers. With the release of OpenShift 3.1 an option was added to the installer to deploy the master, node, openvswitch and etcd components as containers – after installation, administrators were given the option to deploy elasticsearch, fluentd, and kibana as containers.

While the OpenShift installer still makes modifications to a server’s file system, all of the major software components can now be installed using container images. What makes these containerized components is that, for example, an instance of the etcd image built into OpenShift should and would never be used to store data for your customer facing application code, because it is a containerized component designed to be run as part of OpenShift Container Platform.

With the latest releases of OpenShift, there is a trend towards more and more containerized components. The containerized component pattern is becoming more and more common and other software vendors are seeing an advantage to deploying as containerized components.

Deployer Images

A deployer image is a specialized kind of container which, when run, deploys or manages other containers. This pattern enables sophisticated deployment techniques such as mandating the start order of containers, or first run logic such as populating schema or data.

As an example, the “image/container type” pattern is used to deploy the logging and metrics in OpenShift. Deploying these components with a deployer container allows the OpenShift engineering team to control start order of the different components and make sure they are all up and running together.

Intermediate Images

An Intermediate image is any container image that relies on a base image. Typically, core builds, middleware and language runtimes are built as layers on “top of” a base image. These images are then referenced in the FROM directive of another image. These images are not used on their own, they are typically used as a building block to build a standalone image.

It is common to have different teams of specialists own different layers of an image. Systems administrators may own the core build layer, while “developer experience” may own the middleware layer. Intermediate Images are built to be consumed by other teams building images, but can sometimes be run standalone too, especially for testing.

Intermodal Container Images

Intermodal container images, analogous to intermodal shipping containers, are images that have hybrid architectures. For example, many Red Hat Software Collections images can be used in two ways. First, they can be used as simple Application Containers running a fully contained Ruby on Rails and Apache server. Second, they can be used as Builder Images inside of OpenShift Container Platform. In this case, the output child images which contain Ruby on Rails, Apache, and the application code which the source to image process was pointed towards during the build phase.

The intermodal pattern is becoming more and more common to solve two business problems with one container image.

System Containers

When system software is distributed as a container, it often needs to run super privileged. To make this deployment easier, and to allow these containers to start before the container runtime or orchestration, Red Hat developed a special container pattern called System Containers. System Containers start early in the boot process and rely on the atomic command and systemd to be started independent of any container runtime or orchestration. Red Hat provides System Containers for many pieces of software including rsyslog, cockpit, etcd, and flanneld. In the future, Red Hat will expand the list.

This design pattern will make it easier for administrators to add these services to Red Hat Enterprise Linux and Atomic Host in a modular way.

Containers are quite easy to consume, but when building a production container environment, it shifts the complexity behind the scenes. To be able to discuss architectures, and how you will build your environment, it’s important to have shared nomenclature. There are a lot of pitfalls as you dig deeper into building and architecting your environment. We leave you with a couple of critical ones to remember.

People often use the words container image and repository interchangeably and the docker sub-commands don’t make a distinction between an image and a repository. The commands are quite easy to use, but once architecture discussions start, it’s important to understand that a repository is really the central data structure.

It’s also quite easy to misunderstand the difference between a namespace, repository, image layer, and tag. Each of these has an architectural purpose. While different vendors, and users are using them for different purposes, they are tools in our toolbox.

image00

The goal of this article is to leave you with the ability to command this nomenclature so that more sophisticated architectures can be created. For example, imagine that you have just been charged with building an infrastructure that limits, based on role, which namespaces, repositories, and even which image layers and tags can be pushed and pulled based on business rules. Finally, remember that how a container image is built will have profound effect on how it is to be run (orchestrated, privileged, etc).

For further reading, check out the Architecting Containers series:

As always, if you have comments or questions, please leave a message below.

Source

Fully automated blue/green deployments in Kubernetes with Codefresh

Out of the box, Kubernetes supports some interesting deployment strategies. From those, the most useful for production deployments is the rolling update. This deployment strategy allows for zero-downtime application upgrades and also offers a gradual rollout of the new application to each pod instance.

Even though rolling updates sound great in theory, in practice, there are several drawbacks. The major one is the lack of easy rollbacks. Attempting to go to the previous version after a deployment has finished is not a straightforward process. A minor disadvantage is also the fact that not all applications are capable of having multiple versions active at the same time.

An alternative way of deployment comes in the form or blue/green (a.ka. red/black) deployments. With this strategy, a full set of both old and new instances exist at the same time. Active traffic is decided via the loadbalancer that selects one of the two sets. This means that doing a rollback is as simple as switching back the load balancer.

Blue green deploymentsBlue green deployments

Unfortunately, Kubernetes does not support blue/green deployments out of the box. It is the responsibility of an external tool or automation solution to implement such deployment. Hopefully, doing blue/green deployments is not that hard. Kubernetes already comes with the basic building blocks (deployments and services) that make a blue/green deployment very easy using plain kubectl commands. The challenge for a sound CI/CD solution is how to automate those kubectl commands so that blue/green deployments happen in a well controlled and repeatable manner.

We have already covered blue/green deployments in a previous Codefresh blog post. There, we explained a way to script a Codefresh pipeline to perform blue/green deployments. This approach works well for people that already know how kubectl works, but is not the most user-friendly way for designing a pipeline.

Blue/Green deployments with a declarative syntax

In this post, we will take blue/green deployments one step further by packaging the kubectl invocations into a pre-packaged Docker image offering a declarative way to do blue/green deployments.

The final result is that in order to deploy using blue/green you can just insert the following build step in your codefresh.yml:

blueGreenDeploy:

title: “Deploying new version ${}”

image: codefresh/k8s-blue-green:master

environment:

– SERVICE_NAME=my-service

– DEPLOYMENT_NAME=my-app

– NEW_VERSION=${}

– HEALTH_SECONDS=30

– NAMESPACE=colors

– KUBE_CONTEXT=myDemoAKSCluster

Here blue/green deployments happen in a completely declarative manner. All kubectl commands are abstracted.

The Blue/Green deploy step is essentially a docker image with a single executable that takes the following parameters as environment variables:

Environment Variable Description
KUBE_CONTEXT Name of your cluster in Codefresh dashboard
SERVICE_NAME Existing K8s service
DEPLOYMENT_NAME Existing k8s deployment
NEW_VERSION Docker tag for the next version of the app
HEALTH_SECONDS How many seconds both colors should coexist. After that new version pods will be checked for restarts
NAMESPACE K8s Namespace where deployments happen

Prerequisites

The blue/green deployments steps expect the following assumptions:

  • An initial service and the respective deployment should already exist in your cluster.
  • The service and the deployment need to use labels that define their version.

The second assumption is very important, as this is how the blue/green step detects the current version and can switch the load balancer to the next version.

You can use anything you want as a “version”, but the recommended approach is to use GIT hashes and tag your Docker images with them. In Codefresh this is very easy because the built-in variable CF_SHORT_REVISION gives you the git hash of the commit that was pushed.

The build step of the main application that creates the Docker image that will be used in the blue/green step is a standard build step that tags the Docker image with the git hash

BuildingDockerImage:

title: Building Docker Image

type: build

image_name: trivial-web

working_directory: ./example/

tag: ‘${}’

dockerfile: Dockerfile

For more details, you can look at the example application
that also contains a service and deployment with the correct labels as well as the full codefresh.yml file.

How to perform Blue/Green deployments

When you run a deployment in Codefresh the pipeline step will print information message on its actions.

Blue/Green deployment logsBlue/Green deployment logs

The blue/green step copies your existing deployment and changes its version, creating a second one with the updated Docker image. At this point, BOTH version (old and new) of your application are deployed in the Kubernetes cluster. All live traffic is still routed to the old application.

There is a waiting period (configurable as an environment parameter as we have seen in the previous section). During this period you are free to do any external checks on your own (e.g. check your health dashboard or run some kind of smoke testing). Once that period is finished, the script checks for the number of restarts in the pods of the new application. If there are any errors, it destroys the new deployment and your cluster is back to the initial state (your users are not affected in any way).

If there are are no pod restarts, the service is switched to point to the new deployment and the old deployment is discarded.

You can also see the changes in the Codefresh Kubernetes dashboard. I am using an Azure Kubernetes cluster, but any cluster will work as long as the labels are present in the manifest files.

Kubernetes DashboardKubernetes Dashboard

And there you have it! Now you can deploy your own application using the blue/green strategy. The blue/green Docker image is also
available in Dockerhub.

New to Codefresh? Create Your Free Account Today!

Source

Introducing Volume Snapshot Alpha for Kubernetes

Author: Jing Xu (Google) Xing Yang (Huawei), Saad Ali (Google)

Kubernetes v1.12 introduces alpha support for volume snapshotting. This feature allows creating/deleting volume snapshots, and the ability to create new volumes from a snapshot natively using the Kubernetes API.

What is a Snapshot?

Many storage systems (like Google Cloud Persistent Disks, Amazon Elastic Block Storage, and many on-premise storage systems) provide the ability to create a “snapshot” of a persistent volume. A snapshot represents a point-in-time copy of a volume. A snapshot can be used either to provision a new volume (pre-populated with the snapshot data) or to restore the existing volume to a previous state (represented by the snapshot).

Why add Snapshots to Kubernetes?

The Kubernetes volume plugin system already provides a powerful abstraction that automates the provisioning, attaching, and mounting of block and file storage.

Underpinning all these features is the Kubernetes goal of workload portability: Kubernetes aims to create an abstraction layer between distributed systems applications and underlying clusters so that applications can be agnostic to the specifics of the cluster they run on and application deployment requires no “cluster specific” knowledge.

The Kubernetes Storage SIG identified snapshot operations as critical functionality for many stateful workloads. For example, a database administrator may want to snapshot a database volume before starting a database operation.

By providing a standard way to trigger snapshot operations in the Kubernetes API, Kubernetes users can now handle use cases like this without having to go around the Kubernetes API (and manually executing storage system specific operations).

Instead, Kubernetes users are now empowered to incorporate snapshot operations in a cluster agnostic way into their tooling and policy with the comfort of knowing that it will work against arbitrary Kubernetes clusters regardless of the underlying storage.

Additionally these Kubernetes snapshot primitives act as basic building blocks that unlock the ability to develop advanced, enterprise grade, storage administration features for Kubernetes: such as data protection, data replication, and data migration.

Which volume plugins support Kubernetes Snapshots?

Kubernetes supports three types of volume plugins: in-tree, Flex, and CSI. See Kubernetes Volume Plugin FAQ for details.

Snapshots are only supported for CSI drivers (not for in-tree or Flex). To use the Kubernetes snapshots feature, ensure that a CSI Driver that implements snapshots is deployed on your cluster.

As of the publishing of this blog, the following CSI drivers support snapshots:

Snapshot support for other drivers is pending, and should be available soon. Read the “Container Storage Interface (CSI) for Kubernetes Goes Beta” blog post to learn more about CSI and how to deploy CSI drivers.

Kubernetes Snapshots API

Similar to the API for managing Kubernetes Persistent Volumes, Kubernetes Volume Snapshots introduce three new API objects for managing snapshots:

  • VolumeSnapshot
    • Created by a Kubernetes user to request creation of a snapshot for a specified volume. It contains information about the snapshot operation such as the timestamp when the snapshot was taken and whether the snapshot is ready to use.
    • Similar to the PersistentVolumeClaim object, the creation and deletion of this object represents a user desire to create or delete a cluster resource (a snapshot).
  • VolumeSnapshotContent
    • Created by the CSI volume driver once a snapshot has been successfully created. It contains information about the snapshot including snapshot ID.
    • Similar to the PersistentVolume object, this object represents a provisioned resource on the cluster (a snapshot).
    • Like PersistentVolumeClaim and PersistentVolume objects, once a snapshot is created, the VolumeSnapshotContent object binds to the VolumeSnapshot for which it was created (with a one-to-one mapping).
  • VolumeSnapshotClass
    • Created by cluster administrators to describe how snapshots should be created. including the driver information, the secrets to access the snapshot, etc.

It is important to note that unlike the core Kubernetes Persistent Volume objects, these Snapshot objects are defined as CustomResourceDefinitions (CRDs). The Kubernetes project is moving away from having resource types pre-defined in the API server, and is moving towards a model where the API server is independent of the API objects. This allows the API server to be reused for projects other than Kubernetes, and consumers (like Kubernetes) can simply install the resource types they require as CRDs.

CSI Drivers that support snapshots will automatically install the required CRDs. Kubernetes end users only need to verify that a CSI driver that supports snapshots is deployed on their Kubernetes cluster.

In addition to these new objects, a new, DataSource field has been added to the PersistentVolumeClaim object:

type PersistentVolumeClaimSpec struct {
AccessModes []PersistentVolumeAccessMode
Selector *metav1.LabelSelector
Resources ResourceRequirements
VolumeName string
StorageClassName *string
VolumeMode *PersistentVolumeMode
DataSource *TypedLocalObjectReference
}

This new alpha field enables a new volume to be created and automatically pre-populated with data from an existing snapshot.

Kubernetes Snapshots Requirements

Before using Kubernetes Volume Snapshotting, you must:

  • Ensure a CSI driver implementing snapshots is deployed and running on your Kubernetes cluster.
  • Enable the Kubernetes Volume Snapshotting feature via new Kubernetes feature gate (disabled by default for alpha):
    • Set the following flag on the API server binary: –feature-gates=VolumeSnapshotDataSource=true

Before creating a snapshot, you also need to specify CSI driver information for snapshots by creating a VolumeSnapshotClass object and setting the snapshotter field to point to your CSI driver. In the example of VolumeSnapshotClass below, the CSI driver is com.example.csi-driver. You need at least one VolumeSnapshotClass object per snapshot provisioner. You can also set a default VolumeSnapshotClass for each individual CSI driver by putting an annotation snapshot.storage.kubernetes.io/is-default-class: “true” in the class definition.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
name: default-snapclass
annotations:
snapshot.storage.kubernetes.io/is-default-class: “true”
snapshotter: com.example.csi-driver

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
name: csi-snapclass
snapshotter: com.example.csi-driver
parameters:
fakeSnapshotOption: foo
csiSnapshotterSecretName: csi-secret
csiSnapshotterSecretNamespace: csi-namespace

You must set any required opaque parameters based on the documentation for your CSI driver. As the example above shows, the parameter fakeSnapshotOption: foo and any referenced secret(s) will be passed to CSI driver during snapshot creation and deletion. The default CSI external-snapshotter reserves the parameter keys csiSnapshotterSecretName and csiSnapshotterSecretNamespace. If specified, it fetches the secret and passes it to the CSI driver when creating and deleting a snapshot.

And finally, before creating a snapshot, you must provision a volume using your CSI driver and populate it with some data that you want to snapshot (see the CSI blog post on how to create and use CSI volumes).

Creating a new Snapshot with Kubernetes

Once a VolumeSnapshotClass object is defined and you have a volume you want to snapshot, you may create a new snapshot by creating a VolumeSnapshot object.

The source of the snapshot specifies the volume to create a snapshot from. It has two parameters:

  • kind – must be PersistentVolumeClaim
  • name – the PVC API object name

The namespace of the volume to snapshot is assumed to be the same as the namespace of the VolumeSnapshot object.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
name: new-snapshot-demo
namespace: demo-namespace
spec:
snapshotClassName: csi-snapclass
source:
name: mypvc
kind: PersistentVolumeClaim

In the VolumeSnapshot spec, user can specify the VolumeSnapshotClass which has the information about which CSI driver should be used for creating the snapshot . When the VolumeSnapshot object is created, the parameter fakeSnapshotOption: foo and any referenced secret(s) from the VolumeSnapshotClass are passed to the CSI plugin com.example.csi-driver via a CreateSnapshot call.

In response, the CSI driver triggers a snapshot of the volume and then automatically creates a VolumeSnapshotContent object to represent the new snapshot, and binds the new VolumeSnapshotContent object to the VolumeSnapshot, making it ready to use. If the CSI driver fails to create the snapshot and returns error, the snapshot controller reports the error in the status of VolumeSnapshot object and does not retry (this is different from other controllers in Kubernetes, and is to prevent snapshots from being taken at an unexpected time).

If a snapshot class is not specified, the external snapshotter will try to find and set a default snapshot class for the snapshot. The CSI driver specified by snapshotter in the default snapshot class must match the CSI driver specified by the provisioner in the storage class of the PVC.

Please note that the alpha release of Kubernetes Snapshot does not provide any consistency guarantees. You have to prepare your application (pause application, freeze filesystem etc.) before taking the snapshot for data consistency.

You can verify that the VolumeSnapshot object is created and bound with VolumeSnapshotContent by running kubectl describe volumesnapshot:

  • Ready should be set to true under Status to indicate this volume snapshot is ready for use.
  • Creation Time field indicates when the snapshot is actually created (cut).
  • Restore Size field indicates the minimum volume size when restoring a volume from the snapshot.
  • Snapshot Content Name field in the spec points to the VolumeSnapshotContent object created for this snapshot.

Importing an existing snapshot with Kubernetes

You can always import an existing snapshot to Kubernetes by manually creating a VolumeSnapshotContent object to represent the existing snapshot. Because VolumeSnapshotContent is a non-namespace API object, only a system admin may have the permission to create it. Once a VolumeSnapshotContent object is created, the user can create a VolumeSnapshot object pointing to the VolumeSnapshotContent object. The external-snapshotter controller will mark snapshot as ready after verifying the snapshot exists and the binding between VolumeSnapshot and VolumeSnapshotContent objects is correct. Once bound, the snapshot is ready to use in Kubernetes.

A VolumeSnapshotContent object should be created with the following fields to represent a pre-provisioned snapshot:

  • csiVolumeSnapshotSource – Snapshot identifying information.
    • snapshotHandle – name/identifier of the snapshot. This field is required.
    • driver – CSI driver used to handle this volume. This field is required. It must match the snapshotter name in the snapshot controller.
    • creationTime and restoreSize – these fields are not required for pre-provisioned volumes. The external-snapshotter controller will automatically update them after creation.
  • volumeSnapshotRef – Pointer to the VolumeSnapshot object this object should bind to.
    • name and namespace – It specifies the name and namespace of the VolumeSnapshot object which the content is bound to.
    • UID – these fields are not required for pre-provisioned volumes.The external-snapshotter controller will update the field automatically after binding. If user specifies UID field, he/she must make sure that it matches with the binding snapshot’s UID. If the specified UID does not match the binding snapshot’s UID, the content is considered an orphan object and the controller will delete it and its associated snapshot.
  • snapshotClassName – This field is optional. The external-snapshotter controller will update the field automatically after binding.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotContent
metadata:
name: static-snapshot-content
spec:
csiVolumeSnapshotSource:
driver: com.example.csi-driver
snapshotHandle: snapshotcontent-example-id
volumeSnapshotRef:
kind: VolumeSnapshot
name: static-snapshot-demo
namespace: demo-namespace

A VolumeSnapshot object should be created to allow a user to use the snapshot:

  • snapshotClassName – name of the volume snapshot class. This field is optional. If set, the snapshotter field in the snapshot class must match the snapshotter name of the snapshot controller. If not set, the snapshot controller will try to find a default snapshot class.
  • snapshotContentName – name of the volume snapshot content. This field is required for pre-provisioned volumes.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
name: static-snapshot-demo
namespace: demo-namespace
spec:
snapshotClassName: csi-snapclass
snapshotContentName: static-snapshot-content

Once these objects are created, the snapshot controller will bind them together, and set the field Ready (under Status) to True to indicate the snapshot is ready to use.

Provision a new volume from a snapshot with Kubernetes

To provision a new volume pre-populated with data from a snapshot object, use the new dataSource field in the PersistentVolumeClaim. It has three parameters:

  • name – name of the VolumeSnapshot object representing the snapshot to use as source
  • kind – must be VolumeSnapshot
  • apiGroup – must be snapshot.storage.k8s.io

The namespace of the source VolumeSnapshot object is assumed to be the same as the namespace of the PersistentVolumeClaim object.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-restore
Namespace: demo-namespace
spec:
storageClassName: csi-storageclass
dataSource:
name: new-snapshot-demo
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
– ReadWriteOnce
resources:
requests:
storage: 1Gi

When the PersistentVolumeClaim object is created, it will trigger provisioning of a new volume that is pre-populated with data from the specified snapshot.

As a storage vendor, how do I add support for snapshots to my CSI driver?

To implement the snapshot feature, a CSI driver MUST add support for additional controller capabilities CREATE_DELETE_SNAPSHOT and LIST_SNAPSHOTS, and implement additional controller RPCs: CreateSnapshot, DeleteSnapshot, and ListSnapshots. For details, see the CSI spec.

Although Kubernetes is as minimally prescriptive on the packaging and deployment of a CSI Volume Driver as possible, it provides a suggested mechanism for deploying an arbitrary containerized CSI driver on Kubernetes to simplify deployment of containerized CSI compatible volume drivers.

As part of this recommended deployment process, the Kubernetes team provides a number of sidecar (helper) containers, including a new external-snapshotter sidecar container.

The external-snapshotter watches the Kubernetes API server for VolumeSnapshot and VolumeSnapshotContent objects and triggers CreateSnapshot and DeleteSnapshot operations against a CSI endpoint. The CSI external-provisioner sidecar container has also been updated to support restoring volume from snapshot using the new dataSource PVC field.

In order to support snapshot feature, it is recommended that storage vendors deploy the external-snapshotter sidecar containers in addition to the external provisioner the external attacher, along with their CSI driver in a statefulset as shown in the following diagram.

In this example deployment yaml file, two sidecar containers, the external provisioner and the external snapshotter, and CSI drivers are deployed together with the hostpath CSI plugin in the statefulset pod. Hostpath CSI plugin is a sample plugin, not for production.

What are the limitations of alpha?

The alpha implementation of snapshots for Kubernetes has the following limitations:

  • Does not support reverting an existing volume to an earlier state represented by a snapshot (alpha only supports provisioning a new volume from a snapshot).
  • Does not support “in-place restore” of an existing PersistentVolumeClaim from a snapshot: i.e. provisioning a new volume from a snapshot, but updating an existing PersistentVolumeClaim to point to the new volume and effectively making the PVC appear to revert to an earlier state (alpha only supports using a new volume provisioned from a snapshot via a new PV/PVC).
  • No snapshot consistency guarantees beyond any guarantees provided by storage system (e.g. crash consistency).

What’s next?

Depending on feedback and adoption, the Kubernetes team plans to push the CSI Snapshot implementation to beta in either 1.13 or 1.14.

How can I learn more?

Check out additional documentation on the snapshot feature here: http://k8s.io/docs/concepts/storage/volume-snapshots and https://kubernetes-csi.github.io/docs/

How do I get involved?

This project, like all of Kubernetes, is the result of hard work by many contributors from diverse backgrounds working together.

In addition to the contributors who have been working on the Snapshot feature:

We offer a huge thank you to all the contributors in Kubernetes Storage SIG and CSI community who helped review the design and implementation of the project, including but not limited to the following:

If you’re interested in getting involved with the design and development of CSI or any part of the Kubernetes Storage system, join the Kubernetes Storage Special Interest Group (SIG). We’re rapidly growing and always welcome new contributors.

Source

Istio at 1.0 – Why should you care? /

14/Sep 2018

By James Munnelly

Businesses operating at scale face several challenges. Not only must many applications be maintained – running in different environments and built in different languages – but application behavior should be monitored closely, whilst adhering to strict security policies. There is a lot to juggle.

The open service-mesh platform Istio – founded in April 2017 by Google, IBM & Lyft – provides users with the ability to connect, manage, and secure microservices. It takes care of monitoring by providing detailed logging and visibility, connectivity by giving the ability to control traffic flow between services, and also security by providing flexible authorisation policies and transparent mutual authentication/encryption.

istio

Given that Istio can be deployed on Kubernetes, we at Jetstack have already worked with a large media customer to increase their ability to introspect network traffic and application behaviour. They can also utilise Istio’s telemetry capabilities to gain a detailed view of application behaviour and swiftly respond to issues.

This summer brought us the release of Istio 1.0. This version introduces many new features that make the product even more appealing. These improve security and make Istio more language agnostic. The Jetstack engineers working on our Istio project have reviewed the new release, and have picked out some of the key aspects that they believe will offer value.

Istio 1.0

  • Policy can be enforced centrally. This means there’s no more relying on developers or third party applications to enforce authentication, saving time and additional costs. Policy control is also fine-grained, and based on specific application attributes.
  • Business-level constructs no longer have to be tied to individual languages. For instance, you can implement resource quotas, load balancing policies, and authentication without modifying applications.
  • Moving from traditional infrastructure has been made easier in this release. Simply deploy your application, manage important security-related or performance-related behaviour centrally without messing with ‘legacy’ apps.
  • There is a significant push towards ‘zero trust networks’ in this release. There is no more firewalling from public internet, and central control plane allows visualisation of these boundaries. Security is a priority.
  • Built-in telemetry gives the user a detailed insight into application behaviour, and the ability to monitor the application for any issues.
  • Service discovery and communication across clusters and regions: Finally, true multi-region application deployments!
  • There are now clearly defined roles and purposes within the organisation. These include defining who manages Ingress Gateways, application quotas, rate limits, and traffic policies.

laptop

In addition to these new features, it must be noted that Istio 1.0 lays a solid foundation for a platform upon which to build more. For this reason, we have a lot to look forward to, not just in the upcoming release, but in the roadmap of the platform.
You can see evidence of this in the recent announcement of Knative – which makes heavy use of the advanced traffic management features in Istio to build a higher level interface for users to interact with.

We see Kubernetes being adopted at an astounding rate with many customers already investigating service mesh options. Given how far we’ve come since v1 of Kubernetes, imagine what Istio 1.11 will look like in a couple of years!

If you want to get started with Istio today, head on over to the getting started guide. You can get set up and running using the Helm chart deployment, which will take care of getting you from zero to Istio in just a few minutes!

Interested in exploring Istio for your business? Reach out to our team at hello@jetstack.io.

Source

Improving the multi-team Kubernetes ingress experience with Heptio Contour 0.6

Kubernetes has a variety of primitives that make it a great platform for running workloads submitted by multiple teams. Features like Role Based Access Control (RBAC) and Namespaces make it possible to divide clusters across multiple teams in a safe way. There are some challenges however, and one of the most important ones our enterprise customers have encountered lies in the Ingress API. In this post, we will explore how a bad Ingress resource can break your ingress layer, and walk through our novel approach to multi-team ingress using Heptio Contour’s new IngressRoute resource.

Multi-team Ingress on Kubernetes
Most organizations typically have more than one team interacting with a given cluster. Cluster operators assign one or more namespaces to each team and use RBAC to ensure that no team can mess with another team’s resources.
Even though Ingress is a namespaced resource that can be locked down with RBAC, it poses a challenge in multi-team clusters because it controls cluster-level configuration: the hosts and paths on which to serve application traffic.
Let us imagine a scenario where the marketing team owns www.example.com/blog. They are responsible for the organization’s blog and they have configured an Ingress resource that looks like this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
namespace: marketing
spec:
rules:
– host: www.example.com
http:
paths:
– path: /blog
backend:
serviceName: blog
servicePort: 80

Now, the engineering team is looking to run their own engineering-focused blog, and they mistakenly apply the following Ingress resource into the engineering namespace:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
namespace: engineering
spec:
rules:
– host: www.example.com
http:
paths:
– path: /blog
backend:
serviceName: engineering-blog
servicePort: 80

We now have two conflicting Ingress configurations that point www.example.com/blog to different services. The Ingress API does not define how to handle this conflict and the behavior of Ingress Controllers frequently differs — this results in a negative user experience affecting multiple parties. The engineering team is completely unaware that they have taken down the company blog, while the avid blog readers are unable to access their favorite blog.
As you can see in this example, the Ingress resource can become the Achilles’ heel of a multi-team cluster. We have heard from multiple customers that have been bitten by this in production, and thus we decided to address this issue in Contour.
IngressRoute delegation to the rescue
One of the most exciting features introduced in the latest version of Heptio Contour is the IngressRoute Custom Resource Definition (CRD). Among the many improvements available in this new custom resource is delegation support, which allow you to delegate the configuration of a specific host or path to another IngressRoute.
The crux of the problem with the Ingress resource in a multi-team cluster is that operators do not have a way to prevent teams from claiming hosts and paths at will. The ability to create root IngressRoutes in a specific namespace, as well as the ability to do cross-namespace delegation is our answer to this problem.
Using the delegation feature of the IngressRoute, cluster operators get full control of the roots of their ingress layer by limiting which namespaces are authorized to create root IngressRoutes. This eliminates the possibility for two teams to create configurations that collide. The IngressRoute roots specify the top level domains and TLS configuration, while delegating the configuration of specific subdomains or paths to other IngressRoutes in other namespaces. In this way, each team gets the ability to use and configure the slice of the ingress space that has been delegated to their team’s namespace.
Let us revisit the problematic scenario we outlined above. The cluster operator creates a “roots” namespace, and configures Contour to only accept root IngressRoutes from this namespace. Then, the cluster operator creates a root IngressRoute for www.example.com and delegates the /blog path to the marketing team:

apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
name: example-com-root
namespace: roots
spec:
virtualhost:
fqdn: www.example.com
routes:
– match: /blog
delegate:
name: blog
namespace: marketing

The marketing team creates an IngressRoute that sets up the company blog. Note that the virtualhost is missing, as this is not a root IngressRoute.

apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
name: blog
namespace: marketing
spec:
routes:
– match: /blog
services:
– name: blog
port: 80

As you might imagine, if the engineering team were to create a conflicting IngressRoute, the company’s blog would remain accessible as there is no delegation path that points to the engineering team IngressRoute. Instead of producing an outage, Contour ignores the orphaned route and sets its status field accordingly:

apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
name: blog
namespace: engineering
spec:
routes:
– match: /blog
services:
– name: engineering-blog
port: 80
status:
currentStatus: orphaned
description: this IngressRoute is not part of a delegation chain from a root IngressRoute

What’s next?
We have explored the new IngressRoute and more specifically, the delegation model that enables you to run multi-team Kubernetes clusters in a safe way; this is one of the exciting features available in the latest version of Heptio Contour. But, there’s more.
In future posts, we will explore other patterns enabled by the IngressRoute, including blue/green deployments, canary deployments and load balancing strategies. If you have any questions, or are interested in learning more, feel to reach us via the #contour channel on the Kubernetes community Slack, or follow us on Twitter.
Source