How we deployed OpenStack with OpenStack-Ansible 

OpenStack-Ansible allows you to deploy production-grade OpenStack cloud on LXC containers. OSAD(openstack ansible deployment project) enables you to rollout  hasssle-free OpenStack updates and this is only one of many uses. It directly pulls code from git source rather than packages from distributions. But for now we will only be focusing on the OpesStack deployment.

Hardware we used is as follows.

  • osad   (4 cores, 4GB RAM, 500GB HDD, 2 NICs)
  • osad1 (8 cores, 64GB RAM, two 500GB HDD, 4 NICs )
  • osad2 (8 cores, 32GB RAM, two 500GB HDD, 4 NICs)
  • a vlan enabled switch

 

Openstack Neutron

inside-architecture-of-neutron-9-638
Basic Neutron Deployment
Neutron is an Openstack stand-alone project which aims at providing network connectivity for the compute resources created by nova.

Neutron comprises of multiple services and agents running on multiple nodes. Let us know about the services in the above basic neutron deployment.

  1. neutron-server provides an API layer that acts as an single point of access to manage other neutron services.
  2. L2 agent runs on compute and network nodes which creates various types of networks (flat,local,vlan,vxlan,gre) and provides isolation between tenant networks. It takes care of wiring the VM instances. L2 agent can use Linux bridge or OpenvSwitch or any other vendor technology to perform above tasks.
  3. L3 agent runs on network node allows its users to create routers that connects Layer2 networks. Behind the scenes L3 agent uses linux iptables to perform layer3 forwarding and NAT. It’s possible to create multiple routers with overlapping ip range through network namespaces. Each router creates its own namespace with name based on its UUID.
  4. DHCP agent runs on the network node allocates ip addresses to instances. It uses a dnsmasq instance per network.

Neutron Plugins

Neutron exposes a logical API which defines the network connectivity between the devices created by OpenStack nova. Under the hood all the CRUD operations on an attribute managed by neutron API is being handled by a Neutron Plugin.

As of Mitaka release core API of Neutron manages three kind of entities:

1.Network, representing isolated virtual Layer-2 domains; a network can also be regarded as a virtual (or logical) switch;

2.Subnet, representing IPv4 or IPv6 address blocks from which IPs to be assigned to VMs on a given network are selected;

  1. Port, representing virtual (or logical) switch ports on a given network.

All entities, discussed in detail in the rest of this chapter, support the basic CRUD operations with POST/GET/PUT/DELETE verbs, and have an auto-generated unique identifier

The Modular Layer 2 (ML2) plugin is a python module which providesneutron.neutron_plugin_base_v2.NeutronPluginBaseV2 class with a minimum set of methods that needs to be implemented.

  1. create_network(context, network)

def create_network(self, context, network): result, mech_context = self._create_network_db(context, network) kwargs = {'context': context, 'network': result} registry.notify(resources.NETWORK, events.AFTER_CREATE, self, **kwargs) try: self.mechanism_manager.create_network_postcommit(mech_context) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error(_LE("mechanism_manager.create_network_postcommit " "failed, deleting network '%s'"), result['id']) self.delete_network(context, result['id']) return result

Let’s spend some time in understanding the above code. Motto is to Create a network, which represents an L2 network segment which can have a set of subnets and ports associated with it.

Parameters: context – neutron api request context

network – dictionary describing the network, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated.

Containerizing Virtual Machines

Recently Google joined hands with Mirantis and Intel to distribute Openstack components in docker containers managed with kubernetes. In the above deployment scenario, each and every component of openstack like nova, neutron, keystone etc.. runs in docker containers and are deployed, managed through kubernetes. I wondered if the nova service is running in a container then how is it going to span a vm instance. An another doubt flashed in my mind that is it possible to run a virtual machine inside a docker container? . The answer is yes but with some prerequisites installed and tweaks done on the docker host. In this post i will show you how to run a vm using kvm in a docker container.

As the docker containers don’t have a kernel of its own and it uses hosts kernel, so it’s not possible to insert kvm kernel module. So instead we will add /dev/kvm and /dev/net/tun devices to the container.

Make sure that you installed docker and kvm on the host. Kvm installation can be tested by

$ kvm-ok

INFO: /dev/kvm exists
KVM acceleration can be used

Run an ubuntu kvm image by the following command

$ docker run -e “RANCHER_VM=true” –cap-add NET_ADMIN -v \
/var/lib/rancher/vm:/vm –device /dev/kvm:/dev/kvm \
–device /dev/net/tun:/dev/net/tun rancher/vm-ubuntu -m 1024m -smp 1

Ubuntu vm which is spannned inside a container gets the ip address of your container. First know the ip address of docker container through the following command

$ docker inspect

ssh into the vm you created above

$ ssh ubuntu@

password: ubuntu

User Namespaces in Docker

The support for User Namespaces in docker exists from version 1.10 onward and i consider it as one of the most important docker’s security enhancements.

What are User namespaces ?

User namespaces is feature of Linux fully implemented from kernel 3.12 onward that can be used to isolate User ID’s and Group ID’s between the host and containers. It can provide better isolation and security. For example a privileged user root in the container can be mapped to a normal user on the host.

By setting user namespaces in docker daemon, a process running inside a container as root will not be running as root on the docker host Operating System, which makes it harder for a rogue process to break out of the container.

Let’s dive deep into the problem which arises by not enabling user namespaces option in docker daemon.

# Run a container and mount host’s /etc onto /root/etc
$ docker run –rm -v /etc:/root/etc -it ubuntu

# Make some changes to /root/etc/hosts
root@34ef23438542:/# vi /root/etc/hosts

# Exit from the container
root@34ef23438542:/# exit

# Check /etc/hosts on host.
$ cat /etc/hosts

You can easily notice that the changes which you have done on container are exactly reflected back onto the host.

This is because the command

$ docker run –rm -v /etc:/root/etc -it ubuntu

should run the bash shell inside the container as root and simultaneously when you run  ps -ef on the host you will notice

root 17889 17873 0 17:47 pts/0 00:00:00 /bin/bash

So in this scenario, rogue processes inside the container can do whatever they want on the host because they have access to root privileges on the host(actually root user on the host launches the bash shell in the container).

In order to avoid this kind of security loopholes in your system let’s activate User Namespaces.

# Create a user called “dockremap”
$ sudo adduser dockremap

The above username “dockremap” must exist in /etc/passwd file. You also need to have Sub UID and GID ranges specified in
/etc/subuid and /etc/subgid files repectively.

# Setup subuid and subgid
$ sudo sh -c ‘echo dockremap:500000:65536 > /etc/subuid’
$ sudo sh -c ‘echo dockremap:500000:65536 > /etc/subgid’

Also,in the above example I reserved a range of 65536 UIDs (the numbers in the subuid file are the starting UID and thenumber of UIDs available to that user) but Docker Engine will only use the first one in the range (for now, Engine is
only capable of remapping to a single UID).

copy the base systemd config file to /etc/systemd/system

sudo cp /lib/systemd/system/docker.service /etc/systemd/system/

In /etc/systemd/system/docker.service file edit

ExecStart=/usr/bin/docker daemon -H fd://

to

ExecStart=/usr/bin/docker daemon –userns-remap=dockremap -H fd://

and then restart docker with

sudo systemctl daemon-reload

sudo systemctl restart docker

The first thing you will notice at this point is that any images you had originally pulled will be gone.

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE

Let investigate /var/lib/docker folder

$ sudo ls -lF /var/lib/docker/
total 40
drwx—— 10 165536 165536 4096 okt 24 13:32 165536.165536/
drwx—— 10 500000 500000 4096 okt 24 14:02 500000.500000/
drwxrwxrwx 5 root root 4096 okt 21 22:40 aufs/
drwxrwxrwx 8 root root 4096 okt 24 13:46 containers/
drwxrwxrwx 3 root root 4096 okt 21 22:40 image/
drwxrwxrwx 3 root root 4096 okt 21 22:40 network/
drwxrwxrwx 2 root root 4096 okt 21 22:40 swarm/
drwxrwxrwx 2 root root 4096 okt 24 13:41 tmp/
drwxrwxrwx 2 root root 4096 okt 21 22:40 trust/
drwxrwxrwx 5 root root 4096 okt 23 14:48 volumes/

Images are gone because the remapped docker engine operates in
500000.500000 directory [format XXX.YYY where XXX is the subordinate UID and YYY is the subordinate GID]not in the default directory.

$ sudo ls -F /var/lib/docker/500000.500000/
aufs/ containers/ image/ network/ swarm/ tmp/ trust/ volumes/

$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu

Digest: sha256:2d44ae143feeb36f4c898d32ed2ab2dffeb3a573d2d8928646dfc9cb7deb1315
Status: Image is up to date for ubuntu:latest

run a container

$ docker run -it ubuntu top

b60ed71fdcfbad3b86c004b89367c03ec27f3de71ab9893acf3cef1564bd9c09

$ ps -ef | grep top

nobody 1289 800 0 12:00 ? 00:00:00 /usr/sbin/dnsmasq –no-resolv –keep-in-foreground –no-hosts –bind-interfaces –pid-file=/var/run/NetworkManager/dnsmasq.pid –listen-address=127.0.1.1 –cache-size=0 –conf-file=/dev/null –proxy-dnssec –enable-dbus=org.freedesktop.NetworkManager.dnsmasq –conf-dir=/etc/NetworkManager/dnsmasq.d
dilip 26915 30985 0 18:41 pts/3 00:00:00 docker run -it ubuntu top
500000 26955 26937 0 18:41 pts/0 00:00:00 top
dilip 31649 27127 0 18:44 pts/1 00:00:00 grep –color=auto top

Once the daemon is running with user namespacing enabled, the processes run as the remapped subordinate UID (in this case, 500000)instead of root.

But what do those processes look like inside the container?

$ docker run -it ubuntu /bin/sh
# ps
PID TTY TIME CMD
1 ? 00:00:00 sh
7 ? 00:00:00 ps

[^P^Q]

$ ps au | grep [b]in/sh
500000 452 0.0 0.0 4508 748 pts/0 Ss+ 18:48 0:00 /bin/sh

In the above example, the /bin/sh process is owned by root inside the container,
but it’s owned by the subordinate UID [500000] outside of the container.

This is all. Feel free to comment.

Source: docker documentation

 

 

Running your own private docker registry service

I’m talking about a service like docker hub for your organization. This can be achieved by using docker’s registry image. I assume that you have docker running on your host.Let’s dive deep into its implementation.

Pull the registry image from docker hub

docker pull registry

Docker’s private registry image serves on port 5000. So we will forward to port 5000 on host.

docker run -d -p 5000:5000 –name dilipregistry registry

Lets pull an image locally and then push it to your docker registry

docker pull ubuntu

Tag the image like <registry_server_ip>:<port>/<name_of_image>

 docker tag ubuntu localhost:5000/ubuntu

push it to your docker registry

 docker push localhost:5000/ubuntu

You can search for the image which you just pushed to private registry.

 docker images localhost:5000

docker stop registry && docker rm -v registry

By default, your registry container uses a docker volume on the host filesystem.

Specifically, you might want to point your volume location to a specific place in order to more easily access your registry data. To do so you can:

docker run -d -p 5000:5000 –restart=always –name registry \
-v `pwd`/data:/var/lib/registry \
registry:2

For wider adoption of your docker private registry over the network you need to give TLS certs to docker daemon.

creating self-signed TLS certificates 

mkdir -p certs

openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt

Make sure you stopped your registry from the previous steps, then start your registry again with TLS enabled:

 docker run -d -p 5000:5000 –restart=always –name registry \
-v `pwd`/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2

You should now be able to access your registry from another docker host:

 docker pull ubuntu
docker tag ubuntu <registry_server_ip>:5000/ubuntu
docker push <resistry_server_ip>:5000/ubuntu
docker pull <registry_server_ip>:5000/ubuntu

Reference: docker documentation

Feel free to comment and always open for advises.