Cloud in Action: Practice Docker and its Networking on Ubuntu

  

薛国锋     [email protected]

 

 

VM, LXC, Docker and Libcontainer

 

VMs and LXC (Linux Containers) differ on quite a few dimensions, but primarily LXC is a OS-level virtualization technology that allows creation and running of multiple isolated Linux virtual environments on a single control host, whereas with VMs, the hardware is being virtualized to run multiple OS instances. LXC sits on top of a physical server and its host OS, and each container shares the host OS kernel. The shared components are read-only and each container is able to be written to through a unique mount, which makes containers exceptionally “light”. Containers’ speed, agility and portability make them an important tool for DevOps.

Cloud in Action: Practice Docker and Networking_第1张图片

Docker is a significant improvement and extension of LXC’s capabilities, and acts as a portable container engine, packaging the application and all its dependencies in a virtual container that can run on any Linux server, and making containers more protable and flexible to use. Using Docker containers, you can deploy, replicate, move, and back up a workload even more quickly and easily than you can do so using virtual machines. Basically, Docker brings cloudlike flexibility to any infrastructure capable of running containers.

Cloud in Action: Practice Docker and Networking_第2张图片

Docker/LXC takes advantage of several features of the Linux kernel to deliver its functionality:

Namespace is to wrap a particular global system resource in an abstraction that makes it appear to the process within the namespace that they have their own isolated instance of the global resource. Currently there are 6 namespaces implemented in the Linux Kernel: Mount, UTS, IPC, Network, PID and User.

Cgroups allow allocating resources to user-defined groups of processes running on the system. A cgroup limits an application to a specific set of resources.

UnionFS, Union file systems operate by creating layers, making them very lightweight and fast to privide the building blocks for containers.


LXC was used before Docker 1.10 as one execution driver, and offered a userspace interface for the Linux kernel containment features, which is very specific to Linux. Libcontainer is an abstraction layer being an attempt to standarize the way apps are packed up, delivered, and run in isolation, and support a wider range of isolation technologies, and could transform Docker into a cross-platform technology including Windows. Now Libcontainer is the default Docker execution environment.

Cloud in Action: Practice Docker and Networking_第3张图片

It deserves mentioning that Windows server can’t run Linux containers in Docker format but only Windows containers, because Linux containers require the Linux APIs from the host kernel and Windows Server Containers require the Windows APIs of a host Windows kernel; however, the process of managing Linux and Windows containers are strictly identical. 

Cloud in Action: Practice Docker and Networking_第4张图片


Here is the quick summary: Container - Virtual Machine; Docker - Hypervisor (Xen,KVM); Kubernetes - OpenStack.

 

Practice Namespace

 

// Create a child process with the different namespace on UTS

gset@ubuntu:~$ gedit hello.c

#define _GNU_SOURCE

#include

#include

#include

#include

#include

#include

#define STACK_SIZE (1024 * 1024)

 

static char child_stack[STACK_SIZE];

char * const child_args[] = { "/bin/bash",  NULL};

 

int child_main1(void* args)

{

  printf("Running in the child process!\n");

  sethostname("XGF", 12);

  execv(child_args[0], child_args);

  return 1;

}

 

int main() { 

  printf("Get started in the main process: \n");

  int child_pid1 = clone(child_main1, child_stack+STACK_SIZE,CLONE_NEWUTS | SIGCHLD, NULL);

  waitpid(child_pid1, NULL, 0);             // CLONE_NEWUTScreate a new UTS Namespace

 

  printf("The main process exits\n");

  return 0;

}

 

gset@ubuntu:~$ gcc hello.c -o hello.out

gset@ubuntu:~$

gset@ubuntu:~$ hostname

ubuntu

gset@ubuntu:~$ sudo ./hello.out

Get started in the main process:

Running in the child process!

root@XGF:~# hostname

XGF

root@XGF:~# exit

exit

The main process exits

gset@ubuntu:~$ hostname

ubuntu

 

// Create a network namespace - xgf, and connect it to Host with a pair of veth interfaces


 Cloud in Action: Practice Docker and Networking_第5张图片


gset@ubuntu:~$ sudo ip netns add xgf   // create a network namespace - xgf

gset@ubuntu:~$ sudo ip link add veth-a type veth peer name veth-b // create a pair of veth interfaces: veth-a, veth-b

gset@ubuntu:~$ sudo ip link set veth-b netns xgf  // move veth-b to the network namespace - xgf

 

gset@ubuntu:~$ sudo ip link set dev veth-a up

gset@ubuntu:~$ sudo ip addr add 10.110.0.1/24 dev veth-a

 

gset@ubuntu:~$ sudo ip netns exec xgf ip link set dev veth-b up  // execute commands in the network namespace - xgf

gset@ubuntu:~$ sudo ip netns exec xgf ip addr add 10.110.0.2/24 dev veth-b

gset@ubuntu:~$ sudo ip netns exec xgf ip link set dev lo up

 

gset@ubuntu:~$ ip link/addr/route

gset@ubuntu:~$ sudo ip netns exec xgf ip link/addr/route

 

gset@ubuntu:~$ sudo ip netns list

 

gset@ubuntu:~$ ping 10.110.0.1

PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.

64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.032 ms

gset@ubuntu:~$ ping 10.110.0.2

PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.

64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.038 ms

 

gset@ubuntu:~$ sudo ip netns exec xgf ping 10.110.0.1

PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.

64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.093 ms

gset@ubuntu:~$ sudo ip netns exec xgf ping 10.110.0.2

PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.

64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.075 ms

 

gset@ubuntu:~$ ifconfig

veth-a    Link encap:Ethernet  HWaddr d6:8e:09:5a:ed:4a 

          inet addr:10.110.0.1  Bcast:0.0.0.0  Mask:255.255.255.0

          inet6 addr: fe80::d48e:9ff:fe5a:ed4a/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:25 errors:0 dropped:0 overruns:0 frame:0

          TX packets:48 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:1818 (1.8 KB)  TX bytes:5455 (5.4 KB)

  

gset@ubuntu:~$ sudo ip netns exec xgf bash   // enter into the network namespace - xgf

root@ubuntu:~# ifconfig

veth-b    Link encap:Ethernet  HWaddr 1a:ed:45:0f:1b:36 

          inet addr:10.110.0.2  Bcast:0.0.0.0  Mask:255.255.255.0

          inet6 addr: fe80::18ed:45ff:fe0f:1b36/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:47 errors:0 dropped:0 overruns:0 frame:0

          TX packets:24 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:5368 (5.3 KB)  TX bytes:1748 (1.7 KB)

root@ubuntu:~# ip route

10.110.0.0/24 dev veth-b  proto kernel  scope link  src 10.110.0.2

root@ubuntu:~# ping 10.110.0.1

PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.

64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.039 ms

root@ubuntu:~# ping 10.110.0.2

PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.

64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.024 ms

 

gset@ubuntu:~$ sudo ip link del veth-a ( or veth-b )  // delete veth intefaces

gset@ubuntu:~$ sudo ip netns del xgf                  // delete the network namespace – xgf

 

Docker Installation

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04

 

The Docker installation package available in the official Ubuntu 16.04 repository may not be the latest version. To get the latest and greatest version, install Docker from the official Docker repository:

 

gset@ubuntu:~$ uname –rso

gset@ubuntu:~$ cat /etc/lsb-release

gset@ubuntu:~$ sudo apt-get install curl

gset@ubuntu:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –

//  add the GPG key for the official Docker repository to the system

gset@ubuntu:~$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" // add the Docker repository to APT sources

gset@ubuntu:~$ sudo apt-get update

gset@ubuntu:~$ apt-cache policy docker-ce

gset@ubuntu:~$ sudo apt-get install -y docker-ce

gset@ubuntu:~$ sudo systemctl status docker

gset@ubuntu:~$ sudo docker run hello-world

 

Running the docker command requires root privileges and we have to prefix the command with sudo. It can also be run by a user in the docker group, which is automatically created during the installation of Docker. If you attempt to run the docker command without prefixing it with sudo or without being in the docker group, you need finish the following confiurations:

 

sudo usermod -aG docker ${USER}   // add the current username to the docker group:

su - ${USER}    // apply the new group membership

id -nG   // confirm the membership of docker group

 

gset@ubuntu:~$ docker version

gset@ubuntu:~$ docker info


Cloud in Action: Practice Docker and Networking_第6张图片

  

Use Case 1: Create a web server with port mapping


Cloud in Action: Practice Docker and Networking_第7张图片

 

// Download the Ubuntu 14.04 image from Docker Hub

gset@ubuntu:~$ docker pull ubuntu:14.04

gset@ubuntu:~$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

ubuntu              14.04               d6ed29ffda6b        8 days ago          221MB

 

// Create the xgfwebserver container with the docker image ubuntu 14.04

// Map the host port - 8000 to the xgfwebserver port - 80

gset@ubuntu:~$ docker run -it -p 8000:80 --name xgfwebserver ubuntu:14.04

 

// Install python-pip and Flask, and run the web service on the 80 port

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# ip addr

1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000

    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

    inet 127.0.0.1/8 scope host lo

       valid_lft forever preferred_lft forever

7: eth0@if8: mtu 1500 qdisc noqueue state UP group default

    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff

    inet 172.17.0.2/16 scope global eth0

       valid_lft forever preferred_lft forever

root@62b4975b2a8d:/# sudo apt-get update

root@62b4975b2a8d:/# sudo apt-get install python-pip

root@62b4975b2a8d:/# sudo pip install Flask

 

root@62b4975b2a8d:/# vi hello.py

from flask import Flask

app = Flask(__name__)

@app.route('/')

def hello_world():

    return 'Hello World, this is Richard Xue \r\n'

if __name__ == '__main__':

app.run(host='0.0.0.0',port=80)

 

root@62b4975b2a8d:/# python hello.py

 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

10.0.0.129 - - [26/Nov/2017 20:29:26] "GET / HTTP/1.1" 200 -

10.0.0.1 - - [26/Nov/2017 20:29:27] "GET / HTTP/1.1" 200 -

10.0.0.137 - - [26/Nov/2017 20:29:32] "GET / HTTP/1.1" 200 –

 

root@62b4975b2a8d:/# ps -ef

UID         PID   PPID  C STIME TTY          TIME CMD

root          1      0  0 23:29 pts/0    00:00:00 /bin/bash

root         15      1  0 23:29 pts/0    00:00:00 ps -ef

 

// Check the web service in the host

gset@ubuntu:~$ curl 10.0.0.137:8000

Hello World, this is Richard Xue

 

gset@ubuntu:~$ docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES

62b4975b2a8d        ubuntu:14.04        "/bin/bash"         6 minutes ago       Up 6 minutes        0.0.0.0:8000->80/tcp   xgfwebserver

 

gset@ubuntu:~$ docker stop xgfwebserver

gset@ubuntu:~$ docker restart xgfwebserver

gset@ubuntu:~$ docker start xgfwebserver

gset@ubuntu:~$ docker attach xgfwebserver

gset@ubuntu:~$ docker exec -it 62b4975b2a8d /bin/bash

gset@ubuntu:~$ docker exec -it 62b4975b2a8d python hello.py

 

Use Case 2: Share the data between Host and containers

 

// Create the xgf directory in the host and share with its containers

gset@ubuntu:~$ mkdir xgf

gset@ubuntu:~$ cd xgf

gset@ubuntu:~/xgf$ gedit hello1.py

from flask import Flask

app = Flask(__name__)

@app.route('/',methods=['GET'])

def root_get():

                return 'GET - This is Richard Xue\r\n'

@app.route('/hello/')

def hello_world(id):

                return 'Hello World: {}\r\n'.format(id)

@app.route('/add/')

def add(id):

                return 'Sum: %d\r\n' %id

if __name__ == '__main__':

    app.run(host='0.0.0.0',port=80)

 

// Create ‘xgf’ in the container and map ‘/home/gset/xgf’ in the host to the ‘/xgf’ of container

gset@ubuntu:~$ docker run -it -v /home/gset/xgf:/xgf -p 8080:80 --name xgfwebserver2 flaskweb

// If ready-only

gset@ubuntu:~$ docker run -it -v /home/gset/xgf:/xgf:ro -p 8080:80 --name xgfwebserver2 flaskweb

 

root@770058c45a94:/# python ./xgf/hello1.py

 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

10.0.0.129 - - [26/Nov/2017 21:23:59] "GET / HTTP/1.1" 200 –

10.0.0.137 - - [26/Nov/2017 21:49:32] "GET / HTTP/1.1" 200 -

10.0.0.137 - - [26/Nov/2017 21:49:38] "GET /hello/10 HTTP/1.1" 200 -

10.0.0.137 - - [26/Nov/2017 21:49:55] "GET /add/100 HTTP/1.1" 200 -

 

// Check the web service in the host

gset@ubuntu:~$ curl 10.0.0.137:8080

GET - This is Richard Xue

gset@ubuntu:~$ curl 10.0.0.137:8080/hello/10

Hello World: 10

gset@ubuntu:~$ curl 10.0.0.137:8080/add/100

Sum: 100

 

// Create ‘/xgf’ in ‘data-provider’, and share data between two the containers of ‘data-provider’ and ‘data consumer’

gset@ubuntu:~$ docker run -ti -v /xgf --name "data-provider" ubuntu:14.04

gset@ubuntu:~$ docker run -ti --volumes-from "data-provider" --name "data-consumer" ubuntu:14.04

gset@ubuntu:~$ docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES

b3275e7357f4        ubuntu:14.04        "/bin/bash"         2 minutes ago       Up 2 minutes                               data-consumer

ed8643c1f926        ubuntu:14.04        "/bin/bash"         3 minutes ago       Up 3 minutes                               data-provider

 

 

Use Case 3: Create images

 

gset@ubuntu:~$ docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES

62b4975b2a8d        ubuntu:14.04        "/bin/bash"         6 minutes ago       Up 6 minutes        0.0.0.0:8000->80/tcp   xgfwebserver

 

// Create a new image with the running or stoped container

gset@ubuntu:~$ docker commit -m "flask web server" -a "Richard Xue" 62b4975b2a8d flaskweb

sha256:5589ca92a091801dd8e35bcc1238a92a10b977dd32eb99b98422680fc0806203

gset@ubuntu:~$ docker inspect flaskweb

gset@ubuntu:~$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

flaskweb            latest              5589ca92a091        6 seconds ago       394MB

ubuntu              14.04               d6ed29ffda6b        8 days ago          221MB

 

// Create a new image with the docker file

gset@ubuntu:~$ cd xgf

gset@ubuntu:~/xgf$ gedit dockerfile

FROM flaskweb

CMD python hello.py

gset@ubuntu:~/xgf$  docker build -t flaskweb:0.0.1 .

 

gset@ubuntu:~$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

flaskweb            0.0.1               d3855d7f874b        50 seconds ago      394MB

flaskweb            latest              4532c3c18668        2 minutes ago       394MB

ubuntu              14.04               d6ed29ffda6b        9 days ago          221MB

 

// Create a container with the new image: flaskweb:0.0.1

gset@ubuntu:~$ docker run -it -p 8001:80 --name xgfwebserver3 flaskweb:0.0.1

 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

10.0.0.137 - - [26/Nov/2017 22:31:48] "GET / HTTP/1.1" 200 -

10.0.0.137 - - [26/Nov/2017 22:31:57] "GET / HTTP/1.1" 200 –

 

// Check the web service in the host

gset@ubuntu:~$ curl 10.0.0.137:8001

Hello World, this is Richard Xue

 

gset@ubuntu:~$ docker rmi dc63ece8d22e  // delete the image - dc63ece8d22e 

gset@ubuntu:~$ docker rm -f 0d020af8693a // delete the running container -  0d020af8693a


 

Use Case 4: Connect containers in different hosts with VxLAN 


Cloud in Action: Practice Docker and Networking_第8张图片

// Configuration in Host 1

gset@ubuntu:~$ docker start xgfwebserver

gset@ubuntu:~$ sudo ls -la /var/run/netns

gset@ubuntu:~$ sudo ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.0.0.139 local 10.0.0.137

gset@ubuntu:~$ ifconfig –a

gset@ubuntu:~$ sudo docker inspect --format '{{.State.Pid}}' xgfwebserver

2748

gset@ubuntu: ~$ sudo ln -s /proc/2748/ns/net /var/run/netns/xgfwebserver

gset@ubuntu: ~$ sudo ip netns list

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link

gset@ubuntu:~$ sudo ip link set vxlan0 netns xgfwebserver

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link set dev vxlan0 up

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip addr add 10.110.0.1/24 dev vxlan0

 

gset@ubuntu:~$ docker attach xgfwebserver

root@62b4975b2a8d:/# ifconfig

vxlan0    Link encap:Ethernet  HWaddr 06:a5:2d:28:03:b8 

          inet addr:10.110.0.1  Bcast:0.0.0.0  Mask:255.255.255.0

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# ping 10.110.0.1

PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.

root@62b4975b2a8d:/# ping 10.110.0.2

PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.

64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.840 ms

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# python hello.py

 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

10.110.0.2 - - [27/Nov/2017 02:17:30] "GET / HTTP/1.1" 200 -

10.110.0.2 - - [27/Nov/2017 02:17:41] "GET / HTTP/1.1" 200 -

root@62b4975b2a8d:/# route

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

default         172.17.0.1      0.0.0.0         UG    0      0        0 eth0

10.110.0.0      *               255.255.255.0   U     0      0        0 vxlan0

172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

root@62b4975b2a8d:/# ip route

default via 172.17.0.1 dev eth0

10.110.0.0/24 dev vxlan0  proto kernel  scope link  src 10.110.0.1

172.17.0.0/16 dev eth0  proto kernel  scope link  src 172.17.0.2

 

// Configuration in Host 2

gset@ubuntu:~$ docker start xgfwebserver

gset@ubuntu:~$ sudo ls -la /var/run/netns

gset@ubuntu:~$ sudo ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.0.0.137 local 10.0.0.139

gset@ubuntu:~$ ifconfig -a

gset@ubuntu:~$ sudo docker inspect --format '{{.State.Pid}}' xgfwebserver

2751

gset@ubuntu: ~$ sudo ln -s /proc/2751/ns/net /var/run/netns/xgfwebserver

gset@ubuntu: ~$ sudo ip netns list

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link

gset@ubuntu:~$ sudo ip link set vxlan0 netns xgfwebserver

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link set dev vxlan0 up

gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip addr add 10.110.0.2/24 dev vxlan0

 

gset@ubuntu:~$ docker attach xgfwebserver

root@62b4975b2a8d:/# ifconfig

vxlan0    Link encap:Ethernet  HWaddr 96:66:72:e7:1e:42 

          inet addr:10.110.0.2  Bcast:0.0.0.0  Mask:255.255.255.0

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# ping 10.110.0.1

PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.

64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.301 ms

root@62b4975b2a8d:/# ping 10.110.0.2

PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.

64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.029 ms

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# apt-get install curl

root@62b4975b2a8d:/# curl 10.110.0.1:80

root@62b4975b2a8d:/#

root@62b4975b2a8d:/#

root@62b4975b2a8d:/# ip route

default via 172.17.0.1 dev eth0

10.110.0.0/24 dev vxlan0  proto kernel  scope link  src 10.110.0.2

172.17.0.0/16 dev eth0  proto kernel  scope link  src 172.17.0.2