From fb5e6d9b826eb0d70c6ebd675eba368d0577fb9a Mon Sep 17 00:00:00 2001 From: vikaschoudhary16 Date: Wed, 6 Jul 2016 10:28:37 +0530 Subject: [PATCH] Doc-cleanup: remove redundant documentation Complete documentation, specs and devrefs, is to be maintained at Kuryr, the common kuryr lib. Change-Id: I27a41817ef0def4dd0e9e9929e6019d18f566ac2 Closes-bug: #1599362 --- README.rst | 7 + doc/images/kuryr_logo.png | Bin 66301 -> 0 bytes doc/source/conf.py | 2 +- doc/source/contributing.rst | 4 - doc/source/devref/goals_and_use_cases.rst | 65 - doc/source/devref/index.rst | 48 - doc/source/devref/k8s_api_watcher_design.rst | 1065 ----------------- doc/source/devref/kuryr_mitaka_milestone.rst | 118 -- .../libnetwork_remote_driver_design.rst | 419 ------- doc/source/index.rst | 22 +- doc/source/installation.rst | 12 - doc/source/releasenotes.rst | 5 - doc/source/specs/existing-neutron-network.rst | 176 --- doc/source/specs/index.rst | 49 - doc/source/specs/newton/index.rst | 43 - .../specs/newton/kuryr_k8s_integration.rst | 303 ----- doc/source/specs/newton/nested_containers.rst | 527 -------- doc/source/specs/skeleton.rst | 23 - doc/source/specs/template.rst | 64 - doc/source/usage.rst | 7 - 20 files changed, 9 insertions(+), 2950 deletions(-) delete mode 100644 doc/images/kuryr_logo.png delete mode 100644 doc/source/contributing.rst delete mode 100644 doc/source/devref/goals_and_use_cases.rst delete mode 100644 doc/source/devref/index.rst delete mode 100644 doc/source/devref/k8s_api_watcher_design.rst delete mode 100644 doc/source/devref/kuryr_mitaka_milestone.rst delete mode 100644 doc/source/devref/libnetwork_remote_driver_design.rst delete mode 100644 doc/source/installation.rst delete mode 100644 doc/source/releasenotes.rst delete mode 100644 doc/source/specs/existing-neutron-network.rst delete mode 100644 doc/source/specs/index.rst delete mode 100644 doc/source/specs/newton/index.rst delete mode 100644 doc/source/specs/newton/kuryr_k8s_integration.rst delete mode 100644 doc/source/specs/newton/nested_containers.rst delete mode 100644 doc/source/specs/skeleton.rst delete mode 100644 doc/source/specs/template.rst delete mode 100644 doc/source/usage.rst diff --git a/README.rst b/README.rst index d37e87e1..af6ee368 100644 --- a/README.rst +++ b/README.rst @@ -193,3 +193,10 @@ i.e 10.0.0.0/16, but with different pool name, neutron_pool2: bar 397badb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d786 + +External Resources +------------------ + +The latest and most in-depth documentation is available at: + + diff --git a/doc/images/kuryr_logo.png b/doc/images/kuryr_logo.png deleted file mode 100644 index bca100831eb6ddd662c60e28d479dae354faf913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66301 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 rlY0p(M|`oCMR#UB zO@g zg>J&H0A1A=ug|Ml(q6!4!%g`OW0WxXaqSb`f}=x`Ih0q8o}tx4|6yE42SCzv7*FIi3kZ!J`LbpIqD zb)$Cs9|9??am*PV;4pFXp`npeTunwJ`^dz)6IV{9Q~qU>YX8#43MP^r=kC-eM)uDk zmucJD?HBW1)3>TX@tX(qvv4=^y*zL3Mxm3C*hLcUUE+>z59EtArA5wMajbqKT{&(} z#^=FFZ2daVL{n)D4ISyVok>vHGAhPV_cJ3LEv;s>TXu_7jl*w?~%*nqP4(lXtw#4`Z#NuAtLEkRBeeG->x6dSTPp5Q`r(Zit z?Cx7*l+FkVOSs?!DgQpO`n+Al942`oFqiuf4s5<5gp^L*((a}^uc!EHdxLL{5sbxt z!ScdR1bfJSBNMHOub1cknMkiR9>jYPPdV}>;G`?5lJ0g{!sKzb5@h5jJ<2kb8OgxOtlojfj^B3pNxfGOcUUxC8x0ptd8s-eSQIT->@4^Tqc zgB%7ay}_8k|CwOh-vIQ(HZQ%b>GS?NhWd^!U0LR&*DL-$eLOpCG{v%))c1JC(c{D5 zuHPdV#lNjTqBt60reewEmpdqP3ArKlkbn2}o5w?$i-L#oo>98tMvLKu56%_~jelP^ z+VOognr?jDLYY}g$A3Lx$3~4o^l$C)=mwN#xSV40BSAfCi!<9)4XL#Ui!LIj7M;*B zO~aeT>{G)%`ayAIsQ-#LG+^i>h6(zqTx)BFnEinc7Bt}35+MIOCwTv)(EYGw2_U}FW>G-4Lb$jgM^Z5RNf*Jkp*wu}g5VK?9Q!rF6N+e`jA`@4igg(QEQ z=)`%;#~WTmn*7~Dqe0iZDvqYbjnMUxY|lg4gi5|TD3!RwV#0wa0z_ueyef|LbUn)Z%jo)SBIb6bU8y|^!&hxH~0%KTh z4@%1}4|wwgWdk=A|6W4nw{1cT=SE!_5s??vE@Ll!c(Dy3;RpaG5P;A6MbOVr*g$nN zVPW0)83YMYWbnDQ>Z_)z%`V2ad#@vV4Ijg~767#WqyGlOaiK5%qi%BY^s3%v`;)@i zG+(>xGB?R39k-lx@t3y~C!E6b6@Qq=oyyi?5_09>e|+&d93l%8E6sM@_RjyW1&C_` z`f+UC^m91F+7mv>Vbx;r`QzfRy;-$s`I89SQ^1p_>c>1Vzl&H?%`amsj^g~o>w5P9 zHZ$dG${QjX>!1y-zm0HCiXI3l{NW(|3otfvvB(gXXKnhvja*;QZ zwmyqs$-D)^)B+OIN?O5T$3YIFq&C++9e}nO5Mz~zQO9=s+Vil|d{y=8tb`%XHDTx2 z2MHhgQIKPq>1^2BXh!dg98qQZ`%BUy%+hbbzs|!MbT3>*>zxy4p1J}=@6l6qub;a5 zG3=$MXq$0$+nC-wy!;>pT-f6Hm;UHnPScWVxGC&})YF~rHG>e*n>5Hblm+f|n#VVym>x!g zbO){$d~KY`{rFg$C7WF4b(^|W7{BaJ{?>^ZpB6~Bhpt&^a$f{52u5#i*C|SuiIu3F zZ8tQ~JT!O*-u@22r{0IdhA$<(5Pd@)BH%6QrxkCaQkG{4e?C7~`NAq2^Z8E}bcJns zLqO5oW?+x&%GF=2wPz4}R`5Xyu%q5Q*pjk{OTL3BqoZb{CzCjQWPT9`=I9?T_< z7u0VtWYox~gG+Zg)s-_odPf2K;;A=585xV8<*C;p2%`AeKu^vdweeE=&zeV(kN5;^Hy$RG=$Wa236=upkW`xDp!CZ zC9hD;VN9&RBR_60H9Og9>~L(A@(KL?4TbS~Z>i+uA9GxkW@T1{%O&oFj;&F&bTe2B3umY>8$1 zJ7zIfr4bm32at7&v#L z8dN{&qy*zl0`)qvHlny6NmftMKJQ@ywpi_y&z{JFV4x7F@bqT$d)<8Vhqmcf{B~S- zDjd|}d8`OnsUpPcQFKSsdXnEAP#n6CfNy+d1YdxY-Bl?eDetN>R{QbLz)}KtU zQi^35lxW*~QN=m$*T~++<$MP%0Dj45?=QDF<40ugBz2uhZRB&<2X<#nqIUBu(#wG3 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 diff --git a/doc/source/conf.py b/doc/source/conf.py index 111067cf..e01d54f0 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -38,7 +38,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'kuryr' +project = u'kuryr-libnetwork' copyright = u'2013, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index 1728a61c..00000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,4 +0,0 @@ -============ -Contributing -============ -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/devref/goals_and_use_cases.rst b/doc/source/devref/goals_and_use_cases.rst deleted file mode 100644 index 63512026..00000000 --- a/doc/source/devref/goals_and_use_cases.rst +++ /dev/null @@ -1,65 +0,0 @@ -=================== -Goals And Use Cases -=================== - -Kuryr provides networking to Docker containers by leveraging the Neutron APIs -and services. It also provides containerized images for common Neutron plugins - -Kuryr implements a `libnetwork remote driver`_ and maps its calls to OpenStack -`Neutron`_. It works as a translator between libnetwork's -`Container Network Model`_ (CNM) and `Neutron's networking model`_ -and provides container-host or container-vm (nested VM) binding. - -Using Kuryr any Neutron plugin can be used as a libnetwork remote driver -explicitly. Neutron APIs are vendor agnostic and thus all Neutron plugins will -have the capability of providing the networking backend of Docker with a common -lightweight plugging snippet as they have in nova. - -Kuryr takes care of binding the container namespace to the networking -infrastructure by providing a generic layer for `VIF binding`_ depending on the -port type for example Linux bridge port, Open vSwitch port, Midonet port and so -on. - -Kuryr should be the gateway between containers networking API and use cases and -Neutron APIs and services and should bridge the gaps between the two in both -domains. It will map the missing parts in Neutron and drive changes to adjust -it. - -Kuryr should address `Magnum`_ project use cases in terms of containers -networking and serve as a unified interface for Magnum or any other OpenStack -project that needs to leverage containers networking through Neutron API. -In that regard, Kuryr aims at leveraging Neutron plugins that support VM -nested container's use cases and enhancing Neutron APIs to support these cases -(for example `OVN`_). An etherpad regarding `Magnum Kuryr Integration`_ -describes the various use cases Kuryr needs to support. - -Kuryr should provide containerized Neutron plugins for easy deployment and must -be compatible with OpenStack `Kolla`_ project and its deployment tools. The -containerized plugins have the common Kuryr binding layer which binds the -container to the network infrastructure. - -Kuryr should leverage Neutron sub-projects and services (in particular LBaaS, -FWaaS, VPNaaS) to provide to support advanced containers networking use cases -and to be consumed by containers orchestration management systems (for example -Kubernetes , or even OpenStack Magnum). - -Kuryr also support pre-allocating of networks, ports and subnets, and binding -them to Docker networks/endpoints upon creation depending on specific labels -that are passed during Docker creation. There is a patch being merged in Docker -to support providing user labels upon network creation. you can look at this -`User labels in docker patch`_. - -References ----------- - -.. _libnetwork remote driver: https://github.com/docker/libnetwork/blob/master/docs/remote.md -.. _Neutron: https://wiki.openstack.org/wiki/Neutron -.. _Container Network Model: https://github.com/docker/libnetwork/blob/master/docs/design.md#the-container-network-model -.. _Neutron's networking model: https://wiki.openstack.org/wiki/Neutron/APIv2-specification -.. _VIF binding: https://blueprints.launchpad.net/kuryr/+spec/vif-binding-and-unbinding-mechanism -.. _Magnum: https://wiki.openstack.org/wiki/Magnum -.. _OVN: https://launchpad.net/networking-ovn -.. _Kolla: https://wiki.openstack.org/wiki/Kolla -.. _APIs: https://github.com/docker/libnetwork/blob/master/docs/design.md#api -.. _User labels in docker patch: https://github.com/docker/libnetwork/pull/222/files#diff-2b9501381623bc063b38733c35a1d254 -.. _Magnum Kuryr Integration: https://etherpad.openstack.org/p/magnum-kuryr diff --git a/doc/source/devref/index.rst b/doc/source/devref/index.rst deleted file mode 100644 index e3af5869..00000000 --- a/doc/source/devref/index.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -Design and Developer Docs -========================== - -Kuryr goal is to bring containers networking to Neutron core API -and advanced networking services. -This section contains detailed designs / project integration plans and low level -use cases for the various parts inside Kuryr. - - -Programming HowTos and Tutorials --------------------------------- -.. toctree:: - :maxdepth: 4 - - goals_and_use_cases - libnetwork_remote_driver_design - kuryr_mitaka_milestone - k8s_api_watcher_design - - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/devref/k8s_api_watcher_design.rst b/doc/source/devref/k8s_api_watcher_design.rst deleted file mode 100644 index cfd16c73..00000000 --- a/doc/source/devref/k8s_api_watcher_design.rst +++ /dev/null @@ -1,1065 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -============================= -Kubernetes API Watcher Design -============================= - -This documentation describes the `Kubernetes API`_ watcher daemon component, -**Raven**, of Kuryr. - -What is Raven -------------- - -Raven is a daemon watches the internal Kubernetes (K8s) state through its API -server and receives the changes with the event notifications. Raven then -translate the state changes of K8s into requests against Neutron API and -constructs the virtual network topology on Neutron. - -Raven must act as the centralized component for the translations due to the -constraints come from the concurrent deployments of the pods on worker nodes. -Unless it's centralized, each plugin on each worker node would make requests -against Neutron API and it would lead the conflicts of the requests due to the -race condition because of the lack of the lock or the serialization mechanisms -for the requests against Neutron API. - -Raven doesn't take care of the bindings between the virtual ports and the -physical interfaces on worker nodes. It is the responsibility of Kuryr CNI_ -plugin for K8s and it shall recognize which Neutron port should be bound to the -physical interface associated with the pod to be deployed. So Raven focuses -only on building the virtual network topology translated from the events of the -internal state changes of K8s through its API server. - -Goal -~~~~ - -Through Raven the changes to K8s API server are translated into the appropriate -requests against Neutron API and we can make sure their logical networking states -are synchronized and consistent when the pods are deployed. - -Translation Mapping -------------------- - -The detailed specification of the translation mappings is described in another -document, :doc:`../specs/mitaka/kuryr_k8s_integration`. In this document we touch what -to be translated briefly. - -The main focus of Raven is the following resources. - -* Namespace -* Pod -* Service (Optional) -* Endpoints (Optional) - -Namespaces are translated into the networking basis, Neutron networks and -subnets for the cluster and the service using the explicitly predefined values -in the configuration file, or implicitly specified by the environment -variables, e.g., ``FLANNEL_NET=172.16.0.0/16`` as specified -`in the deployment phase`_. Raven also creates Neutron routers for connecting -the cluster subnets and the service subnets. - -When each namespace is created, a cluster network that contains a cluster -subnet, a service network that contains a service subnet, and a router that -connects the cluster subnet and the service subnet are created through Neutron -API. The apiserver ensures namespaces are created before pods are created. - -Pods contain the information required for creating Neutron ports. If pods are -associated with the specific namespace, the ports are created and associated -with the subnets for the namespace. - -Although it's optional, Raven can emulate kube-proxy_. This is for the network -controller that leverages isolated datapath from ``docker0`` bridge such as -Open vSwitch datapath. Services contain the information for the emulation. Raven -maps kube-proxy to Neutron load balancers with VIPs. In this case Raven also -creates a LBaaS pool member for each Endpoints to be translated coordinating -with the associated service translation. For "externalIPs" type K8s service, -Raven associates a floating IP with a load balancer for enabling the pubilc -accesses. - -================= ================= -Kubernetes Neutron -================= ================= -Namespace Network -(Cluster subnet) (Subnet) -Pod Port -Service LBaaS Pool - LBaaS VIP - (FloatingIP) -Endpoints LBaaS Pool Member -================= ================= - - -.. _k8s-api-behaviour: - -K8s API behaviour ------------------ - -We look at the responses from the pod endpoints as an exmple. - -The following behaviour is based on the 1.2.0 release, which is the latest one -as of March 17th, 2016. - -:: - - $ ./kubectl.sh version - Client Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.0", GitCommit:"5cb86ee022267586db386f62781338b0483733b3", GitTreeState:"clean"} - Server Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.0", GitCommit:"5cb86ee022267586db386f62781338b0483733b3", GitTreeState:"clean"} - -Regular requests -~~~~~~~~~~~~~~~~ - -If there's no pod, the K8s API server returns the following JSON response that -has the empty list for the ``"items"`` property. - -:: - - $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods - HTTP/1.1 200 OK - Content-Type: application/json - Date: Tue, 15 Mar 2016 07:15:46 GMT - Content-Length: 145 - - { - "kind": "PodList", - "apiVersion": "v1", - "metadata": { - "selfLink": "/api/v1/pods", - "resourceVersion": "227806" - }, - "items": [] - } - -We deploy a pod as follow. - -:: - - $ ./kubectl.sh run --image=nginx nginx-app --port=80 - replicationcontroller "nginx-app" created - -Then the response from the API server contains the pod information in -``"items"`` property of the JSON response. - -:: - - $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods - HTTP/1.1 200 OK - Content-Type: application/json - Date: Tue, 15 Mar 2016 08:18:25 GMT - Transfer-Encoding: chunked - - { - "kind": "PodList", - "apiVersion": "v1", - "metadata": { - "selfLink": "/api/v1/pods", - "resourceVersion": "228211" - }, - "items": [ - { - "metadata": { - "name": "nginx-app-o0kvl", - "generateName": "nginx-app-", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/pods/nginx-app-o0kvl", - "uid": "090cc0c8-ea84-11e5-8c79-42010af00003", - "resourceVersion": "228094", - "creationTimestamp": "2016-03-15T08:00:51Z", - "labels": { - "run": "nginx-app" - }, - "annotations": { - "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n" - } - }, - "spec": { - "volumes": [ - { - "name": "default-token-wpfjn", - "secret": { - "secretName": "default-token-wpfjn" - } - } - ], - "containers": [ - { - "name": "nginx-app", - "image": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "volumeMounts": [ - { - "name": "default-token-wpfjn", - "readOnly": true, - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "default", - "serviceAccount": "default", - "nodeName": "10.240.0.4", - "securityContext": {} - }, - "status": { - "phase": "Running", - "conditions": [ - { - "type": "Ready", - "status": "True", - "lastProbeTime": null, - "lastTransitionTime": "2016-03-15T08:00:52Z" - } - ], - "hostIP": "10.240.0.4", - "podIP": "172.16.49.2", - "startTime": "2016-03-15T08:00:51Z", - "containerStatuses": [ - { - "name": "nginx-app", - "state": { - "running": { - "startedAt": "2016-03-15T08:00:52Z" - } - }, - "lastState": {}, - "ready": true, - "restartCount": 0, - "image": "nginx", - "imageID": "docker://sha256:af4b3d7d5401624ed3a747dc20f88e2b5e92e0ee9954aab8f1b5724d7edeca5e", - "containerID": "docker://b97168314ad58404dbce7cb94291db7a976d2cb824b39e5864bf4bdaf27af255" - } - ] - } - } - ] - } - -We get the current snapshot of the requested resources with the regular -requests against the K8s API server. - -Requests with ``watch=true`` query string -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -K8s provides the "watch" capability for the endpoints with ``/watch/`` prefix -for the specific resource name, i.e., ``/api/v1/watch/pods``, or ``watch=true`` -query string. - -If there's no pod, we get only the response header and the connection is kept -open. - -:: - - $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods?watch=true - HTTP/1.1 200 OK - Transfer-Encoding: chunked - Date: Tue, 15 Mar 2016 08:00:09 GMT - Content-Type: text/plain; charset=utf-8 - Transfer-Encoding: chunked - - -We create a pod as we did for the case without the ``watch=true`` query string. - -:: - - $ ./kubectl.sh run --image=nginx nginx-app --port=80 - replicationcontroller "nginx-app" created - -Then we observe the JSON data corresponds to the event is given by each line. -The event type is given in ``"type"`` property of the JSON data, i.e., -``"ADDED"``, ``"MODIFIED"`` and ``"DELETED"``. - -:: - - $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods?watch=true - HTTP/1.1 200 OK - Transfer-Encoding: chunked - Date: Tue, 15 Mar 2016 08:00:09 GMT - Content-Type: text/plain; charset=utf-8 - Transfer-Encoding: chunked - - {"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228082","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","securityContext":{}},"status":{"phase":"Pending"}}} - {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228084","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Pending"}}} - {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228088","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Pending","conditions":[{"type":"Ready","status":"False","lastProbeTime":null,"lastTransitionTime":"2016-03-15T08:00:51Z","reason":"ContainersNotReady","message":"containers with unready status: [nginx-app]"}],"hostIP":"10.240.0.4","startTime":"2016-03-15T08:00:51Z","containerStatuses":[{"name":"nginx-app","state":{"waiting":{"reason":"ContainerCreating","message":"Image: nginx is ready, container is creating"}},"lastState":{},"ready":false,"restartCount":0,"image":"nginx","imageID":""}]}}} - {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228094","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Running","conditions":[{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2016-03-15T08:00:52Z"}],"hostIP":"10.240.0.4","podIP":"172.16.49.2","startTime":"2016-03-15T08:00:51Z","containerStatuses":[{"name":"nginx-app","state":{"running":{"startedAt":"2016-03-15T08:00:52Z"}},"lastState":{},"ready":true,"restartCount":0,"image":"nginx","imageID":"docker://sha256:af4b3d7d5401624ed3a747dc20f88e2b5e92e0ee9954aab8f1b5724d7edeca5e","containerID":"docker://b97168314ad58404dbce7cb94291db7a976d2cb824b39e5864bf4bdaf27af255"}]}}} - -Raven Technical Design Overview -------------------------------- - -Problem Statement -~~~~~~~~~~~~~~~~~ - -To conform to the I/O bound requirement described in :ref:`k8s-api-behaviour`, -the multiplexed concurrent network I/O is demanded. eventlet_ is used in -various OpenStack projects for this purpose as well as other libraries such as -Twisted_, Tornado_ and gevent_. However, it has problems as described in -"`What's wrong with eventlet?`_" on the OpenStack wiki page. - -asyncio and Python 3 by default -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -asyncio_ was introduced as a standard asynchronous I/O library in Python 3.4. -Its event loop and coroutines provide the mechanism to multiplex network I/O -in the asynchronous fashion. Compared with eventlet, we can explicitly mark the -I/O operations asynchronous with ``yield from`` or ``await`` introduced in -Python 3.5. - -Trollius_ is a port of asyncio to Python 2.x. However `Trollius documentation`_ -is describing a list of problems and even promoting the migration to Python 3 -with asyncio. - -Kuryr is still a quite young project in OpenStack Neutron big tent. In addition -to that, since it's a container related project it should be able to be run -inside a container. So do Raven. Therefore we take a path to support for only -Python 3 and drop Python 2. - -With asyncio we can achieve concurrent networking I/O operations required by -watchers watch multiple endpoints and translate their responses into requests -against Neutron and K8s API server. - -Watchers -~~~~~~~~ - -A watcher can be represented as a pair of an API endpoint and a function used -for the translation essentially. That is, the pair of what is translated and -how it is. The API endpoint URI is associated with the stream of the event -notifications and the translation function maps each event coming from the -apiserver into another form such as the request against Neutron API server. - -Watchers can be considered as concerns and reactions. They should be decoupled -from the actual task dispatcher and their consumers. A single or multiple -watchers can be mixed into the single class that leverages them, i.e., Raven, -or even mutliple classes leverage them can have the same concern and the same -reaction. The watchers can be able to be mixed into the single entity of the -watcher user but they should work independently. For instance, ``AliceWatcher`` -does its work and knows nothing about other watchers such as ``BobWatcher``. -They don't work together depending on one or each. - -A minimum watcher can be defined as follow. - -.. code-block:: python - - from kuryr.raven import watchers - - class SomeWatcher(watchers.K8sApiWatcher): - WATCH_ENDPOINT = '/' - - def translate(self, deserialized_json): - pass - -The watcher is defined in the declarative way and ideally doesn't care when it -is called and by whom. However, it needs to recognize the context such as the -event type and behave appropriately according to the situation. - -Raven -~~~~~ - -Raven acts as a daemon and it should be able to be started or stopped by -operators. It delegates the actual watch tasks to the watchers and dispatch -them with the single JSON response corresponds to each endpoint on which the -watcher has its concern. - -Hence, Raven holds one or multiple watchers, opens connections for each -endpoint, makes HTTP requests, gets HTTP responses and parses every event -notification and dispatches the translate methods of the watchers routed based -on their corresponding endpoints. - -To register the watchers to Raven or any class, ``register_watchers`` decorator -is used. It simply inserts the watchers into the dictionary in the class, -``WATCH_ENDPOINTS_AND_CALLBACKS`` and it's up to the class how use the -registerd watchers. The classes passed to ``register_watchers`` are defined in -the configuration file and you can specify only what you need. - -In the case of Raven, it starts the event loop, open connections for each -registered watcher and keeps feeding the notified events to the translate -methods of the watchers. - -Raven is a service implements ``oslo_service.service.Service``. When ``start`` -method is called, it starts the event loop and delegatations of the watch tasks. -If ``SIGINT`` or ``SIGTERM`` signal is sent to Raven, it cancells all watch -tasks, closes connections and stops immediately. Otherwise Raven lets watchers -keep watching the API endpionts until the API server sends EOF strings. When -``stop`` is called, it cancells all watch tasks, closes connections and stops -as well. - -Ideally, the translate method can be a pure function that doesn't depend on the -user of the watcher. However, the translation gets involved in requests against -Neutron and possibly the K8s API server. And it depends on the Neutron client -that shall be shared among the watchers. Hence, Raven calls the translate -methods of the watchers binding itself to ``self``. That is, Raven can -propagate its contexts to the watchers and in this way watchers can share the -same contexts. However, it's responsibilty of the writer of the watchers to -track which variables are defined in Raven and what they are. - -Appendix A: JSON response from the apiserver for each resource --------------------------------------------------------------- - -Namespace -~~~~~~~~~ - -:: - - /api/v1/namespaces?watch=true - -ADDED -+++++ - -:: - - { - "type": "ADDED", - "object": { - "kind": "Namespace", - "apiVersion": "v1", - "metadata": { - "name": "test", - "selfLink": "/api/v1/namespaces/test", - "uid": "f094ea6b-06c2-11e6-8128-42010af00003", - "resourceVersion": "497821", - "creationTimestamp": "2016-04-20T06:41:41Z" - }, - "spec": { - "finalizers": [ - "kubernetes" - ] - }, - "status": { - "phase": "Active" - } - } - } - -MODIFIED -++++++++ - -:: - - { - "type": "MODIFIED", - "object": { - "kind": "Namespace", - "apiVersion": "v1", - "metadata": { - "name": "test", - "selfLink": "/api/v1/namespaces/test", - "uid": "f094ea6b-06c2-11e6-8128-42010af00003", - "resourceVersion": "519095", - "creationTimestamp": "2016-04-20T06:41:41Z", - "deletionTimestamp": "2016-04-21T08:47:53Z" - }, - "spec": { - "finalizers": [ - "kubernetes" - ] - }, - "status": { - "phase": "Terminating" - } - } - } - -DELETED -+++++++ - -:: - - { - "type": "DELETED", - "object": { - "kind": "Namespace", - "apiVersion": "v1", - "metadata": { - "name": "test", - "selfLink": "/api/v1/namespaces/test", - "uid": "f094ea6b-06c2-11e6-8128-42010af00003", - "resourceVersion": "519099", - "creationTimestamp": "2016-04-20T06:41:41Z", - "deletionTimestamp": "2016-04-21T08:47:53Z" - }, - "spec": {}, - "status": { - "phase": "Terminating" - } - } - } - - -Pod -~~~ - -:: - - /api/v1/pods?watch=true - -ADDED -+++++ - -:: - - { - "type": "ADDED", - "object": { - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "my-nginx-y67ky", - "generateName": "my-nginx-", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky", - "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003", - "resourceVersion": "63355", - "creationTimestamp": "2016-02-26T06:04:42Z", - "labels": { - "run": "my-nginx" - }, - "annotations": { - "kubernetes.io/created-by": { - "kind": "SerializedReference", - "apiVersion": "v1", - "reference": { - "kind": "ReplicationController", - "namespace": "default", - "name": "my-nginx", - "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003", - "apiVersion": "v1", - "resourceVersion": "63348" - } - } - } - }, - "spec": { - "volumes": [ - { - "name": "default-token-wpfjn", - "secret": { - "secretName": "default-token-wpfjn" - } - } - ], - "containers": [ - { - "name": "my-nginx", - "image": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "volumeMounts": [ - { - "name": "default-token-wpfjn", - "readOnly": true, - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "default", - "serviceAccount": "default", - "nodeName": "10.240.0.4", - "securityContext": {} - }, - "status": { - "phase": "Pending", - "conditions": [ - { - "type": "Ready", - "status": "False", - "lastProbeTime": null, - "lastTransitionTime": "2016-02-26T06:04:43Z", - "reason": "ContainersNotReady", - "message": "containers with unready status: [my-nginx]" - } - ], - "hostIP": "10.240.0.4", - "startTime": "2016-02-26T06:04:43Z", - "containerStatuses": [ - { - "name": "my-nginx", - "state": { - "waiting": { - "reason": "ContainerCreating", - "message": "Image: nginx is ready, container is creating" - } - }, - "lastState": {}, - "ready": false, - "restartCount": 0, - "image": "nginx", - "imageID": "" - } - ] - } - } - } - -MODIFIED -~~~~~~~~ - -:: - - { - "type": "MODIFIED", - "object": { - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "my-nginx-y67ky", - "generateName": "my-nginx-", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky", - "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003", - "resourceVersion": "63425", - "creationTimestamp": "2016-02-26T06:04:42Z", - "deletionTimestamp": "2016-02-26T06:06:16Z", - "deletionGracePeriodSeconds": 30, - "labels": { - "run": "my-nginx" - }, - "annotations": { - "kubernetes.io/created-by": { - "kind": "SerializedReference", - "apiVersion": "v1", - "reference": { - "kind": "ReplicationController", - "namespace": "default", - "name": "my-nginx", - "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003", - "apiVersion": "v1", - "resourceVersion": "63348" - } - } - } - }, - "spec": { - "volumes": [ - { - "name": "default-token-wpfjn", - "secret": { - "secretName": "default-token-wpfjn" - } - } - ], - "containers": [ - { - "name": "my-nginx", - "image": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "volumeMounts": [ - { - "name": "default-token-wpfjn", - "readOnly": true, - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "default", - "serviceAccount": "default", - "nodeName": "10.240.0.4", - "securityContext": {} - }, - "status": { - "phase": "Pending", - "conditions": [ - { - "type": "Ready", - "status": "False", - "lastProbeTime": null, - "lastTransitionTime": "2016-02-26T06:04:43Z", - "reason": "ContainersNotReady", - "message": "containers with unready status: [my-nginx]" - } - ], - "hostIP": "10.240.0.4", - "startTime": "2016-02-26T06:04:43Z", - "containerStatuses": [ - { - "name": "my-nginx", - "state": { - "waiting": { - "reason": "ContainerCreating", - "message": "Image: nginx is ready, container is creating" - } - }, - "lastState": {}, - "ready": false, - "restartCount": 0, - "image": "nginx", - "imageID": "" - } - ] - } - } - } - -DELETED -+++++++ - -:: - - { - "type": "DELETED", - "object": { - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "my-nginx-y67ky", - "generateName": "my-nginx-", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky", - "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003", - "resourceVersion": "63431", - "creationTimestamp": "2016-02-26T06:04:42Z", - "deletionTimestamp": "2016-02-26T06:05:46Z", - "deletionGracePeriodSeconds": 0, - "labels": { - "run": "my-nginx" - }, - "annotations": { - "kubernetes.io/created-by": { - "kind": "SerializedReference", - "apiVersion": "v1", - "reference": { - "kind": "ReplicationController", - "namespace": "default", - "name": "my-nginx", - "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003", - "apiVersion": "v1", - "resourceVersion": "63348" - } - } - } - }, - "spec": { - "volumes": [ - { - "name": "default-token-wpfjn", - "secret": { - "secretName": "default-token-wpfjn" - } - } - ], - "containers": [ - { - "name": "my-nginx", - "image": "nginx", - "ports": [ - { - "containerPort": 80, - "protocol": "TCP" - } - ], - "resources": {}, - "volumeMounts": [ - { - "name": "default-token-wpfjn", - "readOnly": true, - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "default", - "serviceAccount": "default", - "nodeName": "10.240.0.4", - "securityContext": {} - }, - "status": { - "phase": "Pending", - "conditions": [ - { - "type": "Ready", - "status": "False", - "lastProbeTime": null, - "lastTransitionTime": "2016-02-26T06:04:43Z", - "reason": "ContainersNotReady", - "message": "containers with unready status: [my-nginx]" - } - ], - "hostIP": "10.240.0.4", - "startTime": "2016-02-26T06:04:43Z", - "containerStatuses": [ - { - "name": "my-nginx", - "state": { - "waiting": { - "reason": "ContainerCreating", - "message": "Image: nginx is ready, container is creating" - } - }, - "lastState": {}, - "ready": false, - "restartCount": 0, - "image": "nginx", - "imageID": "" - } - ] - } - } - } - -Service -~~~~~~~ - -:: - - /api/v1/services?watch=true - -ADDED -+++++ - -:: - - { - "type": "ADDED", - "object": { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "redis-master", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/services/redis-master", - "uid": "7aecfdac-d54c-11e5-8cc5-42010af00002", - "resourceVersion": "2074", - "creationTimestamp": "2016-02-17T08:00:16Z", - "labels": { - "app": "redis", - "role": "master", - "tier": "backend" - } - }, - "spec": { - "ports": [ - { - "protocol": "TCP", - "port": 6379, - "targetPort": 6379 - } - ], - "selector": { - "app": "redis", - "role": "master", - "tier": "backend" - }, - "clusterIP": "10.0.0.102", - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - } - -MODIFIED -++++++++ - -The event could not be observed. - -DELETED -+++++++ - -:: - - { - "type": "DELETED", - "object": { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "redis-master", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/services/redis-master", - "uid": "7aecfdac-d54c-11e5-8cc5-42010af00002", - "resourceVersion": "2806", - "creationTimestamp": "2016-02-17T08:00:16Z", - "labels": { - "app": "redis", - "role": "master", - "tier": "backend" - } - }, - "spec": { - "ports": [ - { - "protocol": "TCP", - "port": 6379, - "targetPort": 6379 - } - ], - "selector": { - "app": "redis", - "role": "master", - "tier": "backend" - }, - "clusterIP": "10.0.0.102", - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - } - -Endpoints -~~~~~~~~~ - -:: - - /api/v1/endpoints?watch=true - -ADDED -+++++ - -:: - - { - "type": "ADDED", - "object": { - "apiVersion": "v1", - "kind": "Endpoints", - "subsets": [], - "metadata": { - "creationTimestamp": "2016-06-10T06:26:57Z", - "namespace": "default", - "labels": { - "app": "guestbook", - "tier": "frontend" - }, - "selfLink": "/api/v1/namespaces/default/endpoints/frontend", - "name": "frontend", - "uid": "5542ba6b-2ed4-11e6-8128-42010af00003", - "resourceVersion": "1506396" - } - } - } - -MODIFIED -++++++++ - -:: - - { - "type": "MODIFIED", - "object": { - "apiVersion": "v1", - "kind": "Endpoints", - "subsets": [ - { - "addresses": [ - { - "targetRef": { - "kind": "Pod", - "name": "frontend-ib7ui", - "namespace": "default", - "uid": "554b2924-2ed4-11e6-8128-42010af00003", - "resourceVersion": "1506444" - }, - "ip": "192.168.0.119" - }, - { - "targetRef": { - "kind": "Pod", - "name": "frontend-tt8ok", - "namespace": "default", - "uid": "554b37db-2ed4-11e6-8128-42010af00003", - "resourceVersion": "1506459" - }, - "ip": "192.168.0.120" - }, - { - "targetRef": { - "kind": "Pod", - "name": "frontend-rxsaw", - "namespace": "default", - "uid": "554b43b8-2ed4-11e6-8128-42010af00003", - "resourceVersion": "1506442" - }, - "ip": "192.168.0.121" - } - ], - "ports": [ - { - "port": 80, - "protocol": "TCP" - } - ] - } - ], - "metadata": { - "creationTimestamp": "2016-06-10T06:26:57Z", - "namespace": "default", - "labels": { - "app": "guestbook", - "tier": "frontend" - }, - "selfLink": "/api/v1/namespaces/default/endpoints/frontend", - "name": "frontend", - "uid": "5542ba6b-2ed4-11e6-8128-42010af00003", - "resourceVersion": "1506460" - } - } - } - -DELETED -++++++++ - -The event could not be observed. - -.. _`Kubernetes API`: http://kubernetes.io/docs/api/ -.. _CNI: https://github.com/appc/cni -.. _`in the deployment phase`: https://github.com/kubernetes/kubernetes/search?utf8=%E2%9C%93&q=FLANNEL_NET -.. _kube-proxy: http://kubernetes.io/docs/user-guide/services/#virtual-ips-and-service-proxies -.. _eventlet: http://eventlet.net/ -.. _Twisted: https://twistedmatrix.com/trac/ -.. _Tornado: http://tornadoweb.org/ -.. _gevent: http://www.gevent.org/ -.. _`What's wrong with eventlet?`: https://wiki.openstack.org/wiki/Oslo/blueprints/asyncio#What.27s_wrong_with_eventlet.3F -.. _asyncio: https://www.python.org/dev/peps/pep-3156/ -.. _Trollius: http://trollius.readthedocs.org/ -.. _`Trollius documentation`: http://trollius.readthedocs.org/deprecated.html diff --git a/doc/source/devref/kuryr_mitaka_milestone.rst b/doc/source/devref/kuryr_mitaka_milestone.rst deleted file mode 100644 index 60b08a57..00000000 --- a/doc/source/devref/kuryr_mitaka_milestone.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -===================================== -Kuryr - Milestone for Mitaka -===================================== - -https://launchpad.net/kuryr - - -Kuryr Roles and Responsibilities - First Milestone for Mitaka release ------------------------------------------------------------------------ - -This chapter includes the various use cases that Kuryr aims at solving, -some were briefly described in the introduction chapter. -This list of items will need to be prioritized. - -1) Deploy Kuryr as a libnetwork remote driver (map between libnetwork - API and Neutron API) - -2) Configuration - https://etherpad.openstack.org/p/kuryr-configuration - - Includes authentication to Neutron and Docker (Keystone integration) - -3) VIF Binding - https://etherpad.openstack.org/p/Kuryr_vif_binding_unbinding - https://blueprints.launchpad.net/kuryr/+spec/vif-binding-and-unbinding-mechanism - -4) Containerized neutron plugins + Kuryr common layer (Kolla) - -5) Nested VM - agent less mode (or with Kuryr shim layer) - -6) Magnum Kuryr Integration - https://blueprints.launchpad.net/kuryr/+spec/containers-in-instances - - Create Kuryr heat resources for Magnum to consume - -7) Missing APIs in Neutron to support docker networking model - - Port-Mapping: - Docker port-mapping will be implemented in services and not networks - (libnetwork). - There is a relationship between the two. - Here are some details: - https://github.com/docker/docker/blob/master/experimental/networking.md - https://github.com/docker/docker/blob/master/api/server/server_experimental_unix.go#L13-L16 - - Here is an example of publishing a service on a particular network and attaching - a container to the service: - docker service publish db1.prod cid=$(docker run -itd -p 8000:8000 ubuntu) - docker service attach $cid db1.prod - - Kuryr will need to interact with the services object of the docker - api to support port-mapping. - We are planning to propose a port forwarding spec in Mitaka that - introduces the API and reference implementation of port forwarding - in Neutron to enable this feature. - - Neutron relevant specs: - VLAN trunk ports - ( https://blueprints.launchpad.net/neutron/+spec/vlan-aware-vms) - (Used for nested VM's defining trunk port and sub-ports) - - DNS resolution according to port name - (https://review.openstack.org/#/c/90150/) - (Needed for feature compatibility with Docker services publishing) - -8) Mapping between Neutron identifiers and Docker identifiers - - A new spec in Neutron is being proposed that we can - leverage for this use case: `Adding tags to resources`_ . - Tags are similar in concept to Docker labels. - -9) Testing (CI) - - There should be a testing infrastructure running both unit and functional tests with full - setup of docker + kuryr + neutron. - -10) Packaging and devstack plugin for Kuryr - - -Kuryr Future Scope ------------------- - -1) Kuryr is planned to support other networking backend models defined by Kubernetes - (and not just libnetwork). - -2) In addition to Docker, services are a key component of Kubernetes. - In Kubernetes, I create a pod and optionally create/attach a service to a pod: - https://github.com/kubernetes/kubernetes/blob/master/docs/user-guide/services.md - - Services could be implemented with LBaaS APIs - - An example project that does this for Kubernetes and Neutron LBaaS: - https://github.com/kubernetes/kubernetes/blob/release-1.0/pkg/cloudprovider/openstack/openstack.go - - -References -========== - -.. _libnetwork remote driver: https://github.com/docker/libnetwork/blob/master/docs/remote.md -.. _Neutron: https://wiki.openstack.org/wiki/Neutron -.. _Container Network Model: https://github.com/docker/libnetwork/blob/master/docs/design.md#the-container-network-model -.. _Neutron's networking model: https://wiki.openstack.org/wiki/Neutron/APIv2-specification -.. _Magnum: https://wiki.openstack.org/wiki/Magnum -.. _OVN: https://launchpad.net/networking-ovn -.. _Kolla: https://wiki.openstack.org/wiki/Kolla -.. _APIs: https://github.com/docker/libnetwork/blob/master/docs/design.md#api -.. _plugin discovery mechanism: https://github.com/docker/docker/blob/master/docs/extend/plugin_api.md#plugin-discovery -.. _Neutron client: http://docs.openstack.org/developer/python-neutronclient/ -.. _libkv: https://github.com/docker/libkv -.. _VIF binding: https://blueprints.launchpad.net/kuryr/+spec/vif-binding-and-unbinding-mechanism -.. _Adding tags to resources: https://review.openstack.org/#/c/216021/ -.. _User labels in docker patch: https://github.com/docker/libnetwork/pull/222/files#diff-2b9501381623bc063b38733c35a1d254 diff --git a/doc/source/devref/libnetwork_remote_driver_design.rst b/doc/source/devref/libnetwork_remote_driver_design.rst deleted file mode 100644 index cb363157..00000000 --- a/doc/source/devref/libnetwork_remote_driver_design.rst +++ /dev/null @@ -1,419 +0,0 @@ -======================================= -Libnetwork Remote Network Driver Design -======================================= - -What is Kuryr -------------- - -Kuryr implements a `libnetwork remote network driver`_ and maps its calls to OpenStack -`Neutron`_. It works as a translator between libnetwork's -`Container Network Model`_ (CNM) and `Neutron's networking model`_. Kuryr also acts as -a `libnetwork IPAM driver`_. - -Goal -~~~~ - -Through Kuryr any Neutron plugin can be used as libnetwork backend with no -additional effort. Neutron APIs are vendor agnostic and thus all Neutron -plugins will have the capability of providing the networking backend of Docker -for a similar small plugging snippet as they have in nova. - -Kuryr also takes care of binding one of a veth pair to a network interface on -the host, e.g., Linux bridge, Open vSwitch datapath and so on. - - -Kuryr Workflow - Host Networking --------------------------------- -Kuryr resides in each host that runs Docker containers and serves `APIs`_ -required for the libnetwork remote network driver. It is planned to use the -`Adding tags to resources`_ new Neutron feature by Kuryr, to map between -Neutron resource Id's and Docker Id's (UUID's) - -1. libnetwork discovers Kuryr via `plugin discovery mechanism`_ *before the - first request is made* - - - During this process libnetwork makes a HTTP POST call on - ``/Plugin.Active`` and examines the driver type, which defaults to - ``"NetworkDriver"`` and ``"IpamDriver"`` - - libnetwork also calls the following two API endpoints - - 1. ``/NetworkDriver.GetCapabilities`` to obtain the capability of Kuryr - which defaults to ``"local"`` - 2. ``/IpamDriver.GetDefaultAddressSpcaces`` to get the default address - spaces used for the IPAM - -2. libnetwork registers Kuryr as a remote driver - -3. A user makes requests against libnetwork with the network driver specifier for Kuryr - - - i.e., ``--driver=kuryr`` or ``-d kuryr`` **and** ``--ipam-driver=kuryr`` - for the Docker CLI - -4. libnetwork makes API calls against Kuryr - -5. Kuryr receives the requests and calls Neutron APIs with `Neutron client`_ - -6. Kuryr receives the responses from Neutron and compose the responses for - libnetwork - -7. Kuryr returns the responses to libnetwork - -8. libnetwork stores the returned information to its key/value datastore - backend - - - the key/value datastore is abstracted by `libkv`_ - - -Libnetwork User Workflow (with Kuryr as remote network driver) - Host Networking ---------------------------------------------------------------------------------- -1. A user creates a network ``foo`` with the subnet information - :: - - $ sudo docker network create --driver=kuryr --ipam-driver=kuryr \ - --subnet 10.0.0.0/16 --gateway 10.0.0.1 --ip-range 10.0.0.0/24 foo - 286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364 - - This makes a HTTP POST call on ``/IpamDriver.RequestPool`` with the following - JSON data. - :: - - { - "AddressSpace": "global_scope", - "Pool": "10.0.0.0/16", - "SubPool": "10.0.0.0/24", - "Options": null - "V6": false - } - - The value of ``SubPool`` comes from the value specified in ``--ip-range`` - option in the command above and value of ``AddressSpace`` will be ``global_scope`` or ``local_scope`` depending on value of ``capability_scope`` configuration option. Kuryr creates a subnetpool, and then returns - the following response. - :: - - { - "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376", - "Pool": "10.0.0.0/16", - "Data": {} - } - - If the ``--gateway`` was specified like the command above, another HTTP POST - call against ``/IpamDriver.RequestAddress`` follows with the JSON data below. - :: - - { - "Address": "10.0.0.1", - "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376", - "Options": null, - } - - As the IPAM driver Kuryr allocates a requested IP address and returns the - following response. - :: - - { - "Address": "10.0.0.1/16", - "Data": {} - } - - Finally a HTTP POST call on ``/NetworkDriver.CreateNetwork`` with the - following JSON data. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "IPv4Data": [{ - "Pool": "10.0.0.0/16", - "Gateway": "10.0.0.1/16", - "AddressSpace": "" - }], - "IPv6Data": [], - "Options": {"com.docker.network.generic": {}} - } - - The Kuryr remote network driver will then generate a Neutron API request to - create subnet with pool cidr and an underlying Neutron network. When the - Neutron subnet and network has been created, the Kuryr remote network driver - will generate an empty success response to the docker daemon. Kuryr tags the - Neutron network with the NetworkID from docker. - -2. A user launches a container against network ``foo`` - :: - - $ sudo docker run --net=foo -itd --name=container1 busybox - 78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703 - - This makes a HTTP POST call on ``/IpamDriver.RequestAddress`` with the - following JSON data. - :: - - { - "Address": "", - "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376", - "Options": null, - } - - The IPAM driver Kuryr sends a port creation request to neutron and returns the following response with neutron provided ip address. - :: - - { - "Address": "10.0.0.2/16", - "Data": {} - } - - - Then another HTTP POST call on ``/NetworkDriver.CreateEndpoint`` with the - following JSON data is made. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "Interface": { - "AddressIPv6": "", - "MacAddress": "", - "Address": "10.0.0.2/16" - }, - "Options": { - "com.docker.network.endpoint.exposedports": [], - "com.docker.network.portmap": [] - }, - "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd" - } - - The Kuryr remote network driver then generates a Neutron API request to - fetch port with the matching fields for interface in the request. Kuryr - then updates this port's name, tagging it with endpoint ID. - - Following steps are taken: - - 1) On the endpoint creation Kuryr examines if there's a Port with CIDR - that corresponds to Address or AddressIPv6 requested. - 2) If there's a Port, Kuryr tries to reuse it without creating a new - Port. Otherwise it creates a new one with the given address. - 3) Kuryr tags the Neutron port with EndpointID. - - When the Neutron port has been updated, the Kuryr remote driver will - generate a response to the docker daemon in following form: - (https://github.com/docker/libnetwork/blob/master/docs/remote.md#create-endpoint) - :: - - { - "Interface": {"MacAddress": "08:22:e0:a8:7d:db"} - } - - - On receiving success response, libnetwork makes a HTTP POST call on ``/NetworkDriver.Join`` with - the following JSON data. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "SandboxKey": "/var/run/docker/netns/052b9aa6e9cd", - "Options": null, - "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd" - } - - Kuryr connects the container to the corresponding neutron network by doing - the following steps: - - 1) Generate a veth pair. - 2) Connect one end of the veth pair to the container (which is running in a - namespace that was created by Docker). - 3) Perform a neutron-port-type-dependent VIF-binding to the corresponding - Neutron port using the VIF binding layer and depending on the specific - port type. - - After the VIF-binding is completed, the Kuryr remote network driver - generates a response to the Docker daemon as specified in the libnetwork - documentation for a join request. - (https://github.com/docker/libnetwork/blob/master/docs/remote.md#join) - -3. A user requests information about the network - :: - - $ sudo docker network inspect foo - { - "Name": "foo", - "Id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "Scope": "local", - "Driver": "kuryr", - "IPAM": { - "Driver": "default", - "Config": [{ - "Subnet": "10.0.0.0/16", - "IPRange": "10.0.0.0/24", - "Gateway": "10.0.0.1" - }] - }, - "Containers": { - "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": { - "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd", - "mac_address": "02:42:c0:a8:7b:cb", - "ipv4_address": "10.0.0.2/16", - "ipv6_address": "" - } - } - } - - -4. A user connects one more container to the network - :: - - $ sudo docker network connect foo container2 - d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646 - - $ sudo docker network inspect foo - { - "Name": "foo", - "Id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "Scope": "local", - "Driver": "kuryr", - "IPAM": { - "Driver": "default", - "Config": [{ - "Subnet": "10.0.0.0/16", - "IPRange": "10.0.0.0/24", - "Gateway": "10.0.0.1" - }] - }, - "Containers": { - "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": { - "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd", - "mac_address": "02:42:c0:a8:7b:cb", - "ipv4_address": "10.0.0.2/16", - "ipv6_address": "" - }, - "d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646": { - "endpoint": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc", - "mac_address": "02:42:c0:a8:7b:cc", - "ipv4_address": "10.0.0.3/16", - "ipv6_address": "" - } - } - } - - -5. A user disconnects a container from the network - :: - - $ CID=d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646 - $ sudo docker network disconnect foo $CID - - This makes a HTTP POST call on ``/NetworkDriver.Leave`` with the following - JSON data. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc" - } - - Kuryr remote network driver will remove the VIF binding between the - container and the Neutron port, and generate an empty response to the - Docker daemon. - - Then libnetwork makes a HTTP POST call on ``/NetworkDriver.DeleteEndpoint`` with the - following JSON data. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc" - } - - Kuryr remote network driver generates a Neutron API request to delete the - associated Neutron port, in case the relevant port subnet is empty, Kuryr - also deletes the subnet object using Neutron API and generate an empty - response to the Docker daemon: {} - - Finally libnetwork makes a HTTP POST call on ``/IpamDriver.ReleaseAddress`` - with the following JSON data. - :: - - { - "Address": "10.0.0.3", - "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376" - } - - Kuryr remote IPAM driver generates a Neutron API request to delete the associated Neutron port. - As the IPAM driver Kuryr deallocates the IP address and returns the following response. - :: - - {} - -7. A user deletes the network - :: - - $ sudo docker network rm foo - - This makes a HTTP POST call against ``/NetworkDriver.DeleteNetwork`` with the - following JSON data. - :: - - { - "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364" - } - - Kuryr remote network driver generates a Neutron API request to delete the - corresponding Neutron network and subnets. When the Neutron network and subnets has been deleted, - the Kuryr remote network driver generate an empty response to the docker - daemon: {} - - Then another HTTP POST call on ``/IpamDriver.ReleasePool`` with the - following JSON data is made. - :: - - { - "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376" - } - - Kuryr delete the corresponding subnetpool and returns the following response. - :: - - {} - -Mapping between the CNM and the Neutron's Networking Model ----------------------------------------------------------- - -Kuryr communicates with Neutron via `Neutron client`_ and bridges between -libnetwork and Neutron by translating their networking models. The following -table depicts the current mapping between libnetwork and Neutron models: - -===================== ====================== -libnetwork Neutron -===================== ====================== -Network Network -Sandbox Subnet, Port and netns -Endpoint Port -===================== ====================== - -libnetwork's Sandbox and Endpoint can be mapped into Neutron's Subnet and Port, -however, Sandbox is invisible from users directly and Endpoint is only the -visible and editable resource entity attachable to containers from users' -perspective. Sandbox manages information exposed by Endpoint behind the scene -automatically. - - -Notes on implementing the libnetwork remote driver API in Kuryr ---------------------------------------------------------------- - -1. DiscoverNew Notification: - Neutron does not use the information related to discovery of new resources such - as new nodes and therefore the implementation of this API method does nothing. - -2. DiscoverDelete Notification: - Neutron does not use the information related to discovery of resources such as - nodes being deleted and therefore the implementation of this API method does - nothing. - -.. _libnetwork remote network driver: https://github.com/docker/libnetwork/blob/master/docs/remote.md -.. _libnetwork IPAM driver: https://github.com/docker/libnetwork/blob/master/docs/ipam.md -.. _Neutron: https://wiki.openstack.org/wiki/Neutron -.. _Container Network Model: https://github.com/docker/libnetwork/blob/master/docs/design.md#the-container-network-model -.. _Neutron's networking model: https://wiki.openstack.org/wiki/Neutron/APIv2-specification -.. _Neutron client: http://docs.openstack.org/developer/python-neutronclient/ -.. _plugin discovery mechanism: https://github.com/docker/docker/blob/master/docs/extend/plugin_api.md#plugin-discovery -.. _Adding tags to resources: https://review.openstack.org/#/c/216021/ -.. _APIs: https://github.com/docker/libnetwork/blob/master/docs/design.md#api -.. _libkv: https://github.com/docker/libkv -.. _IPAM blueprint: https://blueprints.launchpad.net/kuryr/+spec/ipam -.. _Neutron's API reference: http://developer.openstack.org/api-ref-networking-v2.html#createSubnet diff --git a/doc/source/index.rst b/doc/source/index.rst index 0a00e2b8..29c4288b 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to kuryr's documentation! +Welcome to kuryr-libnetwork's documentation! ======================================================== Contents: @@ -12,26 +12,6 @@ Contents: :maxdepth: 2 readme - installation - usage - contributing - releasenotes - -Design and Developer Docs -========================== - -.. toctree:: - :maxdepth: 1 - - devref/index - -Kuryr Specs -=========== - -.. toctree:: - :maxdepth: 2 - - specs/index Indices and tables ================== diff --git a/doc/source/installation.rst b/doc/source/installation.rst deleted file mode 100644 index 836bc636..00000000 --- a/doc/source/installation.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Installation -============ - -At the command line:: - - $ pip install kuryr - -Or, if you have virtualenvwrapper installed:: - - $ mkvirtualenv kuryr - $ pip install kuryr diff --git a/doc/source/releasenotes.rst b/doc/source/releasenotes.rst deleted file mode 100644 index c7fc6a15..00000000 --- a/doc/source/releasenotes.rst +++ /dev/null @@ -1,5 +0,0 @@ -=============== - Release Notes -=============== - -.. release-notes:: diff --git a/doc/source/specs/existing-neutron-network.rst b/doc/source/specs/existing-neutron-network.rst deleted file mode 100644 index d0fd8c9a..00000000 --- a/doc/source/specs/existing-neutron-network.rst +++ /dev/null @@ -1,176 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -====================================== -Reuse of the existing Neutron networks -====================================== - -https://blueprints.launchpad.net/kuryr/+spec/existing-neutron-network - -The current Kuryr implementation assumes the Neutron networks, subnetpools, -subnets and ports are created by Kuryr and their lifecycles are completely -controlled by Kuryr. However, in the case where users need to mix the VM -instances and/or the bare metal nodes with containers, the capability of -reusing existing Neutron networks for implementing Kuryr networks becomes -valuable. - - -Problem Description -------------------- - -The main use case being addressed in this spec is described below: - -* Use of existing Neutron network and subnet resources created independent of - Kuryr - -With the addition of Tags to neutron resources -`Add tags to neutron resources spec`_ -the association between container networks and Neutron networks is -implemented by associating tag(s) to Neutron networks. In particular, -the container network ID is stored in such tags. Currently the -maximum size for tags is 64 bytes. Therefore, we currently use two -tags for each network to store the corresponding Docker ID. - - -Proposed Change ---------------- - -This specification proposes to use the ``Options`` that can be specified by -user during the creation of Docker networks. We propose to use either the -Neutron network uuid or name to identify the Neutron network to use. If the -Neutron network uuid or name is specified but such a network does not exist or -multiple such networks exist in cases where a network name is specified, the -create operation fails. Otherwise, the existing network will be used. -Similarly, if a subnet is not associated with the existing network it will be -created by Kuryr. Otherwise, the existing subnet will be used. - -The specified Neutron network is tagged with a well known string such that it -can be verified whether it already existed at the time of the creation of the -Docker network or not. - - -.. NOTE(banix): If a Neutron network is specified but it is already - associated with an existing Kuryr network we may refuse the request - unless there are use cases which allow the use of a Neutron network - for realizing more than one Docker networks. - - -.. _workflow: - -Proposed Workflow -~~~~~~~~~~~~~~~~~ - -1. A user creates a Docker network and binds it to an existing Neutron network - by specifying it's uuid: - :: - - $ sudo docker network create --driver=kuryr --ipam-driver=kuryr \ - --subnet 10.0.0.0/16 --gateway 10.0.0.1 --ip-range 10.0.0.0/24 \ - -o neutron.net.uuid=25495f6a-8eae-43ff-ad7b-77ba57ed0a04 \ - foo - 286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364 - - $ sudo docker network create --driver=kuryr --ipam-driver=kuryr \ - --subnet 10.0.0.0/16 --gateway 10.0.0.1 --ip-range 10.0.0.0/24 \ - -o neutron.net.name=my_network_name \ - foo - 286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364 - - This creates a Docker network with the given name, ``foo`` in this case, by - using the Neutron network with the specified uuid or name. - - If subnet information is specified by ``--subnet``, ``--gateway``, and - ``--ip-range`` as shown in the command above, the corresponding subnetpools - and subnets are created or the exising resources are appropriately reused - based on the provided information such as CIDR. For instance, if the network - with the given UUID in the command exists and that network has the subnet - whose CIDR is the same as what is given by ``--subnet`` and possibly - ``--ip-range``, Kuryr doesn't create a subnet and just leaves the existing - subnets as they are. Kuryr composes the response from the information of - the created or reused subnet. - - It is expected that when Kuryr driver is used, the Kuryr IPAM driver is also - used. - - If the gateway IP address of the reused Neutron subnet doesn't match with - the one given by ``--gateway``, Kuryr returns the IP address set in the - Neutron subnet nevertheless and the command is going to fail because of - Dockers's validation against the response. - -2. A user inspects the created Docker network - :: - - $ sudo docker network inspect foo - { - "Name": "foo", - "Id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364", - "Scope": "global", - "Driver": "kuryr", - "IPAM": { - "Driver": "kuryr", - "Config": [{ - "Subnet": "10.0.0.0/16", - "IPRange": "10.0.0.0/24", - "Gateway": "10.0.0.1" - }] - }, - "Containers": {} - "Options": { - "com.docker.network.generic": { - "neutron.net.uuid": "25495f6a-8eae-43ff-ad7b-77ba57ed0a04" - } - } - } - - A user can see the Neutron ``uuid`` given in the command is stored in the - Docker's storage and can be seen by inspecting the network. - -3. A user launches a container and attaches it to the network - :: - - $ CID=$(sudo docker run --net=foo -itd busybox) - - This process is identical to the existing logic described in `Kuryr devref`_. - libnetwork calls ``/IpamDriver.RequestAddress``, - ``/NetworkDriver.CreateEndpoint`` and then ``/NetworkDriver.Join``. The - appropriate available IP address shall be returned by Neutron through Kuryr - and a port with the IP address is created under the subnet on the network. - -4. A user terminates the container - :: - - $ sudo docker kill ${CID} - - This process is identical to the existing logic described in `Kuryr devref`_ - as well. libnetwork calls ``/IpamDriver.ReleaseAddress``, - ``/NetworkDriver.Leave`` and then ``/NetworkDriver.DeleteEndpoint``. - -5. A user deletes the network - :: - - $ sudo docker network rm foo - - When an existing Neutron network is used to create a Docker network, it is - tagged such that during the delete operation the Neutron network does not - get deleted. Currently, if an existing Neutron network is used, the subnets - associated with it (whether pre existing or newly created) are preserved as - well. In the future, we may consider tagging subnets themselves or the - networks (with subnet information) to decide whether a subnet is to be - deleted or not. - - -Challenges ----------- - -None - -References ----------- - -* `Add tags to neutron resources spec`_ - -.. _Add tags to neutron resources spec: http://docs.openstack.org/developer/neutron/devref/tag.html -.. _Kuryr devref: http://docs.openstack.org/developer/kuryr/devref/index.html diff --git a/doc/source/specs/index.rst b/doc/source/specs/index.rst deleted file mode 100644 index d6ebbb9a..00000000 --- a/doc/source/specs/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - Convention for heading levels: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -Kuryr Specs -=========== - -This section contains detailed specification documents for -different features inside Kuryr. - -.. toctree:: - :maxdepth: 1 - - existing-neutron-network - - -Spec Template --------------- -.. toctree:: - :maxdepth: 3 - - skeleton - template - newton/index - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/specs/newton/index.rst b/doc/source/specs/newton/index.rst deleted file mode 100644 index f2ffb546..00000000 --- a/doc/source/specs/newton/index.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - Convention for heading levels: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -Mitaka Specifications -===================== - -This section contains detailed specification documents for -different features inside Kuryr. - - -Spec ----- -.. toctree:: - :maxdepth: 1 - - nested_containers - kuryr_k8s_integration - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/specs/newton/kuryr_k8s_integration.rst b/doc/source/specs/newton/kuryr_k8s_integration.rst deleted file mode 100644 index 8d30f53e..00000000 --- a/doc/source/specs/newton/kuryr_k8s_integration.rst +++ /dev/null @@ -1,303 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -============================ -Kuryr Kubernetes Integration -============================ - -https://blueprints.launchpad.net/kuryr/+spec/kuryr-k8s-integration - -This spec proposes how to integrate Kubernetes Bare Metal cluster with Neutron -being used as network provider. - -Kubernetes is a platform for automating deployment, scaling and operations of -application containers across clusters of hosts. There are already a number of -implementations of kubernetes network model, such as Flannel, Weave, Linux -Bridge, OpenvSwitch, Calico as well as other vendor implementations. Neutron -already serves as a common way to support various networking providers via -common API. Therefore, using neutron to provide kubernetes networking will -enable different backend support in a common way. - -This approach provides clear benefit for operators who will have variety of -networking choices that already supported via neutron. - - -Problem Description -=================== -Application developers usually are not networking engineers. They should be -able to express the application intent. Currently, there is no integration -between kubernetes and Neutron. Kuryr should bridge the gap between kubernetes -and neutron by using the application intent to infer the connectivity and -isolation requirements necessary to provision the networking entities in a -consistent way. - -Kubernetes Overview -------------------- - -Kubernetes API abstractions: - -**Namespace** - Serves as logical grouping of partition resources. Names of resources need to - be unique within a namespace, but not across namespaces. - -**Pod** - Contains a group of tightly coupled containers that share single network - namespace. Pod models an application-specific "logical host" in a - containerized environment. It may contain one or more containers which are - relatively tightly coupled. Each pod gets its own IP that is also an IP of - the contained Containers. - -**Deployment/Replication Controller** - Ensures the requested number of pods are running at any time. - -**Service** - Is an abstraction which defines a logical set of pods and a policy by which - to access them. The set of service endpoints, usually pods that implement a - given service is defined by the label selector. The default service type - (ClusterIP) is used to provide consistent application inside the kubernetes - cluster. Service receives a service portal (VIP and port). Service IPs are - only available inside the cluster. - Service can abstract access not only to pods. For example, it can be for - external database cluster, service in another namespace, etc. In such case - service does not have a selector and endpoint are defined as part of the - service. The service can be headless (clusterIP=None). For such Services, - a cluster IP is not allocated. DNS should return multiple addresses for the - Service name, which point directly to the Pods backing the Service. - To receive traffic from the outside, service should be assigned an external - IP address. - For more details on service, please refer to [1]_. - -Kubernetes provides two options for service discovery, environments variables -and DNS. Environment variables are added for each active service when pod is -run on the node. DNS is kubernetes cluster add-on that provides DNS server, -more details on this below. - -Kubernetes has two more powerful tools, labels and annotations. Both can be -attached to the API objects. Labels are an arbitrary key/value pairs. Labels -do not provide uniqueness. Labels are queryable and used to organize and to -select subsets of objects. - -Annotations are string keys and values that can be used by external tooling to -store arbitrary metadata. - -More detailed information on k8s API can be found in [2]_ - - -Network Requirements -^^^^^^^^^^^^^^^^^^^^ -k8s imposes some fundamental requirements on the networking implementation: - -* All containers can communicate without NAT. - -* All nodes can communicate with containers without NAT. - -* The IP the containers sees itself is the same IP that others see. - -The kubernetes model is for each pod to have an IP in a flat shared namespace -that allows full communication with physical computers and containers across -the network. The above approach makes it easier than native Docker model to -port applications from VMs to containers. More on kubernetes network model -is here [3]_. - - -Use Cases ---------- -The kubernetes networking should address requirements of several stakeholders: - -* Application developer, the one that runs its application on the k8s cluster - -* Cluster administrator, the one that runs the k8s cluster - -* Network infrastructure administrator, the one that provides the physical - network - -Use Case 1: -^^^^^^^^^^^ -Support current kubernetes network requirements that address application -connectivity needs. This will enable default kubernetes behavior to allow all -traffic from all sources inside or outside the cluster to all pods within the -cluster. This use case does not add multi-tenancy support. - -Use Case 2: -^^^^^^^^^^^ -Application isolation policy support. -This use case is about application isolation policy support as it is defined -by kubernetes community, based on spec [4]_. Network isolation policy will -impose limitations on the connectivity from an optional set of traffic sources -to an optional set of destination TCP/UDP ports. -Regardless of network policy, pods should be accessible by the host on which -they are running to allow local health checks. This use case does not address -multi-tenancy. - -More enhanced use cases can be added in the future, that will allow to add -extra functionality that is supported by neutron. - - -Proposed Change -=============== - - -Model Mapping -------------- - -In order to support kubernetes networking via neutron, we should define how -k8s model maps into neutron model. -With regards to the first use case, to support default kubernetes networking -mode, the mapping can be done in the following way: - -+-----------------+-------------------+---------------------------------------+ -| **k8s entity** | **neutron entity**| **notes** | -+=================+===================+=======================================+ -|namespace | network | | -+-----------------+-------------------+---------------------------------------+ -|cluster subnet | subnet pool | subnet pool for subnets to allocate | -| | | Pod IPs. Current k8s deployment on | -| | | GCE uses subnet per node to leverage | -| | | advanced routing. This allocation | -| | | scheme should be supported as well | -+-----------------+-------------------+---------------------------------------+ -|service cluster | subnet | VIP subnet, service VIP will be | -|ip range | | allocated from | -+-----------------+-------------------+---------------------------------------+ -|external subnet | floating ip pool | To allow external access to services,| -| | external network | each service should be assigned with | -| | router | external (floating IP) router is | -| | | required to enable north-south traffic| -+-----------------+-------------------+---------------------------------------+ -|pod | port | A port gets its IP address from the | -| | | cluster subnet pool | -+-----------------+-------------------+---------------------------------------+ -|service | load balancer | each endpoint (pod) is a member in the| -| | | load balancer pool. VIP is allocated | -| | | from the service cluster ip range. | -+-----------------+-------------------+---------------------------------------+ - -k8s Service Implementation -^^^^^^^^^^^^^^^^^^^^^^^^^^ -Kubernetes default **ClusterIP** service type is used to expose service inside -the cluster. If users decide to expose services to external traffic, they will -assign ExternalIP to the services they choose to expose. Kube-proxy should be -an optional part of the deployment, since it may not work with some neutron -backend solutions, i.e. MidoNet or Contrail. Kubernetes service will be mapped -to the neutron Load Balancer, with ClusterIP as the load balancer VIP and -EndPoints (Pods) are members of the load balancer. -Once External IP is assigned, it will create FIP on external network and -associate it with the VIP. - - -Isolation Policy -^^^^^^^^^^^^^^^^ -In order to support second use case, the application isolation policy mode, -requested policy should be translated into security group that reflects the -requested ACLs as the group rules. This security group will be associated with -pods that policy is applied to. Kubernetes namespace can be used as isolation -scope of the contained Pods. For isolated namespace, all incoming connections -to pods in that namespace from any source inside or outside of the Kubernetes -cluster will be denied unless allowed by a policy. -For non-isolated namespace, all incoming connections to pods in that namespace -will be allowed. -The exact translation details are provided in the [5]_. - -As an alternative, and this goes beyond neutron, it seems that more native way -might be to use policy (intent) based API to request the isolation policy. -Group Based Policy can be considered, but this will be left for the later phase. - -Service Discovery ------------------ -Service discovery should be supported via environment variables. -Kubernetes also offers a DNS cluster add-on to support application services name -resolution. It uses SkyDNS with helper container, kube2sky to bridge between -kubernetes to SkyDNS and etcd to maintain services registry. -Kubernetes Service DNS names can be resolved using standard methods inside the -pods (i.e. gethostbyname). DNS server runs as kubernetes service with assigned -static IP from the service cluster ip range. Both DNS server IP and domain are -configured and passed to the kubelet service on each worker node that passes it -to containers. SkyDNS service is deployed in the kube-system namespace. -This integration should enable SkyDNS support as well as it may add support -for external DNS servers. Since SkyDNS service will be deployed as any other -k8s service, this should just work. -Other alternatives for DNS, such as integration with OpenStack Designate for -local DNS resolution by port name will be considered for later phases. - - -Integration Decomposition -------------------------- - -The user interacts with the system via the kubectl cli or directly via REST API -calls. Those calls define Kubernetes resources such as RC, Pods and services. -The scheduler sees the requests for Pods and assigns them to a specific worker -nodes. - -On the worker nodes, kubelet daemons see the pods that are being scheduled for -the node and take care of creating the Pods, i.e. deploying the infrastructure -and application containers and ensuring the required connectivity. - -There are two conceptual parts that kuryr needs to support: - -API Watcher -^^^^^^^^^^^ -To watch kubernetes API server for changes in services and pods and later -policies collections. -Upon changes, it should map services/pods into the neutron constructs, -ensuring connectivity. It should use neutron client to invoke neutron API to -maintain networks, ports, load balancers, router interfaces and security groups. -The API Watcher will add allocated port details to the Pod object to make it -available to the kubelet process and eventually to the kuryr CNI driver. - -CNI Driver -^^^^^^^^^^ -To enable CNI plugin on each worker node to setup, teardown and provide status -of the Pod, more accurately of the infrastructure container. Kuryr will provide -CNI Driver that implements [6]_. In order to be able to configure and report an -IP configuration, the Kuryr CNI driver must be able to access IPAM to get IP -details for the Pod. The IP, port UUID, GW and port type details should be -available to the driver via **CNI_ARGS** in addition to the standard content:: - - CNI_ARGS=K8S_POD_NAMESPACE=default;\ - K8S_POD_NAME=nginx-app-722l8;\ - K8S_POD_INFRA_CONTAINER_ID=8ceb00926acf251b34d70065a6158370953ab909b0745f5f4647ee6b9ec5c250\ - PORT_UUID=a28c7404-7495-4557-b7fc-3e293508dbc6,\ - IPV4=10.0.0.15/16,\ - GW=10.0.0.1,\ - PORT_TYPE=midonet - -For more details on kuryr CNI Driver, see [7]_. - -Kube-proxy service that runs on each worker node and implements the service in -native implementation is not required since service is implemented via neutron -load balancer. - - -Community Impact ----------------- - -This spec invites community to collaborate on unified solution to support -kubernetes networking by using neutron as a backend via Kuryr. - - -Implementation -============== - -Assignee(s) ------------ - -TBD - -Work Items ----------- - -TBD - - -References -========== -.. [1] http://kubernetes.io/v1.1/docs/user-guide/services.html -.. [2] http://kubernetes.io/docs/api/ -.. [3] http://kubernetes.io/docs/admin/networking/#kubernetes-model -.. [4] https://docs.google.com/document/d/1qAm-_oSap-f1d6a-xRTj6xaH1sYQBfK36VyjB5XOZug -.. [5] https://review.openstack.org/#/c/290172/ -.. [6] https://github.com/appc/cni/blob/master/SPEC.md -.. [7] https://blueprints.launchpad.net/kuryr/+spec/kuryr-cni-plugin diff --git a/doc/source/specs/newton/nested_containers.rst b/doc/source/specs/newton/nested_containers.rst deleted file mode 100644 index a9f526da..00000000 --- a/doc/source/specs/newton/nested_containers.rst +++ /dev/null @@ -1,527 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -============================================================================ -Networking for Nested Containers in OpenStack / Magnum - Neutron Integration -============================================================================= - -Launchpad blueprint: - -https://blueprints.launchpad.net/kuryr/+spec/containers-in-instances - -This blueprint proposes how to integrate Magnum with Neutron based -networking and how the problem of networking for nested containers -can be solved. - - -Problem Description -=================== - -Magnum (containers-as-a-service for OpenStack) provisions containers -inside Nova instances and those instances use standard Neutron -networking. These containers are referred to as nested containers. -Currently, there is no integration between Magnum resources and -Neutron and the nested containers are served networking outside -of that provided by OpenStack (Neutron) today. - -Definitions ------------ - -COE - Container Orchestration Engine - -Bay - A Magnum resource that includes at least one host to run containers on, - and a COE to manage containers created on hosts within the bay. - -Baymodel - An object that stores template information about the bay which is - used to create new bays consistently. - -Pod - Is the smallest deployable unit that can be created, scheduled, and - managed within Kubernetes. - -deviceowner (in Neutron ports) - device_owner is an attribute which is used internally by Neutron. - It identifies the service which manages the port. For example - router interface, router gateway will have their respective - device owners entries. Similarly, Neutron ports attached to Nova - instances have device_owner as compute. - - -Requirements ------------- - -Following are the requirements of Magnum around networking: - -1. Provide networking capabilities to containers running in Nova - instances. - -2. Magnum uses Heat to orchestrate multi-tenant application container - environments. Heat uses user-data scripts underneath. Therefore, - Kuryr must have the ability to be deployed/orchestrated using Heat - via the scripts. - -3. Current Magnum container networking implementations such as Flannel, - provide networking connectivity to containers that reside across - multiple Nova instances. Kuryr must provide multi-instance container - networking capabilities. The existing networking capabilities like - Flannel that Magnum uses will remain and Kuryr to be introduced - in parallel. Decision on default is for later and default may vary - based on the type of Magnum Bay. Magnum currently supports three - types of Bays: Swarm, Kubernetes, and Mesos. They are - referred to as COEs (Container Orchestration Engine). - -4. Kuryr must provide a simple user experience like "batteries included - but replaceable" philosophy. Magnum must have the ability to deploy - Kuryr without any user intervention, but allow more advanced users - to modify Kuryr's default settings as needed. - -5. If something needs to be installed in the Nova VMs used by Magnum, - it needs to be installed in the VMs in a secure manner. - -6. Communication between Kuryr and other services must be secure. For example, - if there is a Kuryr agent running inside the Nova instances, the - communication between Kuryr components (Kuryr, Kuryr Agent), - Neutron-Kuryr, Magnum-Kuryr should all be secure. - -7. Magnum Bays (Swarm, Kubernetes, etc..) must work the same or - better than they do with existing network providers such as Flannel. - -8. Kuryr must scale just as well, if not better, than existing container - networking providers. - - -Use cases ----------- - -* Any container within a nova instance (VM, baremetal, container) - may communicate with any other nova instance (VM, baremetal, container), - or container therein, regardless if the containers are on the same nova - instance, same host, or different hosts within the same Magnum bay. - Such containers shall be able to communicate with any OpenStack cloud - resource in the same Neutron network as the Magnum bay nodes, including - (but not limited to) Load Balancers, Databases, and other Nova instances. - -* Any container should be able to have access to any Neutron resource and - it's capabilities. Neutron resources include DHCP, router, floating IPs etc. - - -Proposed Change -=============== - -The proposal is to leverage the concept of VLAN aware VMs/Trunk Ports [2], -that would be able to discriminate the traffic coming from VM by using -VLAN tags. The trunk port would get attached to a VM and be capable of -receiving both untagged and tagged traffic. Each VLAN would be represented -by a sub port (Neutron ports). A subport must have a network attached. -Each subport will have an additional parameter of VID. VID can be of -different types and VLAN is one of the options. - -Each VM running containers by Magnum would need to have a Kuryr container -agent [3]. Kuryr container agent would be like a CNI/CNM plugin, capable of -assigning IPs to the container interfaces and tagging with VLAN IDs. -Magnum baymodel resource can be passed along information for -network type and kuryr will serve Neutron networking. Based on the baymodel, -Magnum can provision necessary services inside the Nova instance using Heat -templates and the scripts Heat uses. The Kuryr container agent would be -responsible for providing networking to the nested containers by tagging -each container interface with a VLAN ID. Kuryr container agent [3] would be -agnostic of COE type and will have different modes based on the COE. -First implementation would support Swarm and the corresponding container -network model via libnetwork. - -There are two mechanisms in which nested containers will be served networking -via Kuryr: - -1. When user interacts with Magnum APIs to provision containers. -2. Magnum allows end-users to access native COE APIs. It means end-users - can alternatively create containers using docker CLI etc. If the - end-users interact with the native APIs, they should be able to get - the same functionality that is available via Magnum interfaces/orchestration. - COEs use underlying container runtimes tools so this option is also applicable - for non-COE APIs as well. - -For the case, where user interacts with Magnum APIs, Magnum would need to -integrate a 'network' option in the container API to choose Neutron networks -for containers. This option will be applicable for baymodels -running kuryr type networking. For each container launched, Magnum would -pick up a network, and talk to the COE to provision the container(s), Kuryr agent -would be running inside the Nova instance as a driver/plugin to COE networking -model and based on the network UUID/name, Kuryr agent will create a subport on -parent trunk port, where Nova instance is attached to, Kuryr will allocate -a VLAN ID and subport creation be invoked in Neutron and that will allocate the -IP address. Based on the information returned, Kuryr agent will assign IP to -the container/pod and assign a VLAN, which would match VLAN in the subport -metadata. Once the sub-port is provisioned, it will have an IP address and a -VLAN ID allocated by Neutron and Kuryr respectively. - -For the case, where native COE APIs are used, user would be required to specify -information about Kuryr driver and Neutron networks when launching containers. -Kuryr agent will take care of providing networking to the containers in exactly -the same fashion as it would when Magnum talks to the COEs. - -Now, all the traffic coming from the containers inside the VMs would be -tagged and backend implementation of how those containers communicate -will follow a generic onboarding mechanism. Neutron supports several plugins -and each plugin uses some backend technology. The plugins would be -responsible for implementing VLAN aware VMs Neutron extension and onboard -the container based on tenant UUID, trunk port ID, VLAN ID, network UUID -and sub-port UUID. Subports will have deviceowner=kuryr. At this -point, a plugin can onboard the container using unique classification per -tenant to the relevant Neutron network and nested container would be -onboarded onto Neutron networks and will be capable of passing packets. -The plugins/onboarding engines would be responsible for tagging the packets -with the correct VLAN ID on their way back to the containers. - - -Integration Components ------------------------ - -Kuryr: - -Kuryr and Kuryr Agent will be responsible for providing the networking -inside the Nova instances. Kuryr is the main service/utility running -on the controller node and capabilities like segmentation ID allocation -will be performed there. Kuryr agent will be like a CNI/CNM plugin, -capable of allocating IPs and VLANs to container interfaces. Kuryr -agent will be a helper running inside the Nova instances that can -communicate with Neutron endpoint and Kuryr server. This will require -availability of credentials inside the Bay that Kuryr can use to -communicate. There is a security impact of storing credentials and -it is discussed in the Security Impact section of this document. - -More details on the Kuryr Agent can be found here [3]. - - -Neutron: - -vlan-aware-vms and notion of trunk port, sub-ports from Neutron will be -used in this design. Neutron will be responsible for all the backend -networking that Kuryr will expose via its mechanisms. - -Magnum: - -Magnum will be responsible for launching containers on specified/pre-provisioned -networks, using Heat to provisioning Kuryr components inside Nova instances and passing -along network information to the COEs, which can invoke their networking part. - -Heat: - -Heat templates use use-data scripts to launch tools for containers that Magnum -relies on. The scripts will be updated to handle Kuryr. We should not expect -to run scripts each time a container is started. More details can be -found here [4]. - -Example of model:: - -+-------------------------------+ +-------------------------------+ -| +---------+ +---------+ | | +---------+ +---------+ | -| | c1 | | c2 | | | | c3 | | c4 | | -| +---------+ +---------+ | | +---------+ +---------+ | -| | | | -| VM1 | | VM2 | -| | | | -| | | | -+---------+------------+--------+ +---------+------------+--------+ - |Trunk Port1 | |Trunk Port2 | - +------------+ +------------+ - /|\ /|\ - / | \ / | \ - / | \ / | \ - +--+ +-++ +--+ +--+ +-++ +--+ - |S1| |S2| |S3| |S4| |S5| |S6| - +-++ +--+ +-++ +--+ +-++ +-++ - | | | | | - | | | +---+ | | - | | +---+N1+ +-+N2+-----------+ - | | | | | | - +-------------+ | | | - | | | | - + ++ x x +-+ + - N3+--------+x x+-----------+N4 - x x - x Router x - x x - x x - - -C1-4 = Magnum containers -N1-4 = Neutron Networks and Subnets -S1,S3,S4,S6 = Subports -S2,S5 = Trunk ports (untagged traffic) - -In the example above, Magnum launches four containers (c1, c2, c3, c4) -spread across two Nova instances. There are four Neutron -networks(N1, N2, N3, N4) in the deployment and all of them are -connected to a router. Both the Nova instances (VM1 and VM2) have one -NIC each and a corresponding trunk port. Each trunk port has three -sub-ports: S1, S2, S3 and S4, S5, S6 for VM1 and VM2 respectively. -The untagged traffic goes to S2 and S5 and tagged to S1, S3, S4 and -S6. On the tagged sub-ports, the tags will be stripped and packets -will be sent to the respective Neutron networks. - -On the way back, the reverse would be applied and each sub-port to VLAN -mapping be checked using something like following and packets will be -tagged: - -+------+----------------------+---------------+ -| Port | Tagged(VID)/untagged | Packets go to | -+------+----------------------+---------------+ -| S1 | 100 | N1 | -| S2 | untagged | N3 | -| S3 | 200 | N1 | -| S4 | 100 | N2 | -| S5 | untagged | N4 | -| S6 | 300 | N2 | -+------+----------------------+---------------+ - -One thing to note over here is S1.vlan == S4.vlan is a valid scenario -since they are part of different trunk ports. It is possible that some -implementations do not use VLAN IDs, the VID can be something -other than VLAN ID. The fields in the sub-port can be treated as key -value pairs and corresponding support can be extended in the Kuryr agent -if there is a need. - -Example of commands: - -:: - - magnum baymodel-create --name \ - --image-id \ - --keypair-id \ - --external-network-id \ - --dns-nameserver \ - --flavor-id \ - --docker-volume-size \ - --coe \ - --network-driver kuryr - -:: - - neutron port-create --name S1 N1 \ - --device-owner kuryr - -:: - - neutron port-create --name S2 N3 - - -:: - - # trunk-create may refer to 0, 1 or more subport(s). - $ neutron trunk-create --port-id PORT \ - [--subport PORT[,SEGMENTATION-TYPE,SEGMENTATION-ID]] \ - [--subport ...] - -Note: All ports referred must exist. - -:: - - # trunk-add-subport adds 1 or more subport(s) - $ neutron trunk-subport-add TRUNK \ - PORT[,SEGMENTATION-TYPE,SEGMENTATION-ID] \ - [PORT,...] - -:: - - magnum container-create --name \ - --image \ - --bay \ - --command \ - --memory \ - --network network_id - - -Magnum changes --------------- - -Magnum will launch containers on Neutron networks. -Magnum will provision the Kuryr Agent inside the Nova instances via Heat templates. - - -Alternatives ------------- - -None - - -Data Model Impact (Magnum) --------------------------- - -This document adds the network_id attribute to the container database -table. A migration script will be provided to support the attribute -being added. :: - - +-------------------+-----------------+---------------------------------------------+ - | Attribute | Type | Description | - +===================+=================+=============================================+ - +-------------------+-----------------+---------------------------------------------+ - | network_id | uuid | UUID of a Neutron network | - +-------------------+-----------------+---------------------------------------------+ - - -REST API Impact (Magnum) -------------------------- - -This document adds network_id attribute to the Container -API class. :: - - +-------------------+-----------------+---------------------------------------------+ - | Attribute | Type | Description | - +===================+=================+=============================================+ - +-------------------+-----------------+---------------------------------------------+ - | network_id | uuid | UUID of a Neutron network | - +-------------------+-----------------+---------------------------------------------+ - - -Security Impact ---------------- - -Kuryr Agent running inside Nova instances will communicate with OpenStack APIs. For this to -happen, credentials will have to be stored inside Nova instances hosting Bays. - -This arrangement poses a security threat that credentials might be compromised and there -could be ways malicious containers could get access to credentials or Kuryr Agent. -To mitigate the impact, there are multiple options: - -1. Run Kuryr Agent in two modes: primary and secondary. Only primary mode has access to the - credentials and talks to Neutron and fetches information about available resources - like IPs, VLANs. Secondary mode has no information about credentials and performs operations - based on information coming in the input like IP, VLAN etc. Primary mode can be tied to the - Kubernetes, Mesos master nodes. In this option, containers will be running on nodes other - than the ones that talk to OpenStack APIs. -2. Containerize the Kuryr Agent to offer isolation from other containers. -3. Instead of storing credentials in text files, use some sort of binaries - and make them part of the container running Kuryr Agent. -4. Have an Admin provisioned Nova instance that carries the credentials - and has connectivity to the tenant Bays. The credentials are accessible only to the Kuryr - agent via certain port that is allowed through security group rules and secret key. - In this option, operations like VM snapshot in tenant domains will not lead to stolen credentials. -5. Introduce Keystone authentication mechanism for Kuryr Agent. In case of a compromise, this option - will limit the damage to the scope of permissions/roles the Kuryr Agent will have. -6. Use HTTPS for communication with OpenStack APIs. -7. Introduce a mechanism/tool to detect if a host is compromised and take action to stop any further - damage. - -Notifications Impact --------------------- - -None - -Other End User Impact ---------------------- - -None - -Performance Impact ------------------- - -For containers inside the same VM to communicate with each other, -the packets will have to step outside the VMs and come back in. - - -IPv6 Impact ------------ - -None - -Other Deployer Impact ---------------------- - -None - -Developer Impact ----------------- - -Extended attributes in Magnum container API to be used. - -Introduction of Kuryr Agent. - -Requires the testing framework changes. - - -Community Impact ----------------- - -The changes bring significant improvement in the container -networking approach by using Neutron as a backend via Kuryr. - - -Implementation -============== - -Assignee(s) ------------ - - Fawad Khaliq (fawadkhaliq) - -Work Items ----------- - -Magnum: -* Extend the Magnum API to support new network attribute. -* Extend the Client API to support new network attribute. -* Extend baymodel objects to support new container - attributes. Provide a database migration script for - adding the attribute. -* Extend unit and functional tests to support new port attribute - in Magnum. - -Heat: -* Update Heat templates to support the Magnum container - port information. - -Kuryr: -* Kuryr container agent. -* Kuryr VLAN/VID allocation engine. -* Extend unit test cases in Kuryr for the agent and VLAN/VID allocation - engine. -* Other tempest tests. -* Other scenario tests. - - -Dependencies -============ - -VLAN aware VMs [2] implementation in Neutron - - -Testing -======= - -Tempest and functional tests will be created. - - -Documentation Impact -==================== - -Documentation will have to updated to take care of the -Magnum container API changes and use the Kuryr network -driver. - -User Documentation ------------------- - -Magnum and Kuryr user guides will be updated. - -Developer Documentation ------------------------ - -The Magnum and Kuryr developer quickstart documents will be -updated to include the nested container use case and the -corresponding details. - - -References -========== - -[1] https://review.openstack.org/#/c/204686/7 -[2] http://specs.openstack.org/openstack/neutron-specs/specs/mitaka/vlan-aware-vms.html -[3] https://blueprints.launchpad.net/kuryr/+spec/kuryr-agent -[4] https://blueprints.launchpad.net/kuryr/+spec/kuryr-magnum-heat-deployment -[5] http://docs.openstack.org/developer/magnum/ diff --git a/doc/source/specs/skeleton.rst b/doc/source/specs/skeleton.rst deleted file mode 100644 index 7ffc364d..00000000 --- a/doc/source/specs/skeleton.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -========================================== -Title of your RFE -========================================== - - -Problem Description -=================== - - -Proposed Change -=============== - - -References -========== - - diff --git a/doc/source/specs/template.rst b/doc/source/specs/template.rst deleted file mode 100644 index 521e8d7a..00000000 --- a/doc/source/specs/template.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -==================================== -Example Spec - The title of your RFE -==================================== - -Include the URL of your launchpad RFE: - -https://bugs.launchpad.net/kuryr/+bug/example-id - -Introduction paragraph -- why are we doing this feature? A single paragraph of -prose that **deployers, and developers, and operators** can understand. - -Do you even need to file a spec? Most features can be done by filing an RFE bug -and moving on with life. In most cases, filing an RFE and documenting your -design is sufficient. If the feature seems very large or contentious, then -you may want to consider filing a spec. - - -Problem Description -=================== - -A detailed description of the problem: - -* For a new feature this should be use cases. Ensure you are clear about the - actors in each use case: End User vs Deployer - -* For a major reworking of something existing it would describe the - problems in that feature that are being addressed. - -Note that the RFE filed for this feature will have a description already. This -section is not meant to simply duplicate that; you can simply refer to that -description if it is sufficient, and use this space to capture changes to -the description based on bug comments or feedback on the spec. - - -Proposed Change -=============== - -How do you propose to solve this problem? - -This section is optional, and provides an area to discuss your high-level -design at the same time as use cases, if desired. Note that by high-level, -we mean the "view from orbit" rough cut at how things will happen. - -This section should 'scope' the effort from a feature standpoint: how is the -'kuryr end-to-end system' going to look like after this change? What Kuryr -areas do you intend to touch and how do you intend to work on them? The list -below is not meant to be a template to fill in, but rather a jumpstart on the -sorts of areas to consider in your proposed change description. - -You do not need to detail API or data model changes. - - -References -========== - -Please add any useful references here. You are not required to have any -reference. Moreover, this specification should still make sense when your -references are unavailable. diff --git a/doc/source/usage.rst b/doc/source/usage.rst deleted file mode 100644 index c1bc4db4..00000000 --- a/doc/source/usage.rst +++ /dev/null @@ -1,7 +0,0 @@ -======== -Usage -======== - -To use kuryr in a project:: - - import kuryr