From 6d90643d0c468ad1ba7f5f966f03b12b01d016b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20S=C3=B8lvk=C3=A6r=20Pedersen?= Date: Mon, 28 Sep 2020 12:52:05 +0200 Subject: [PATCH] [Documentation] Update of installation guides (#367) * New example.env file for use with Makefile * Updated name of example.env * Updated info for "Extra" group * Moved OS config to advanced * Added pktgen-example.env * Doc update * Updated text * Updated text * Updated text * Updated docs * Updated text * text update * Fixed link * Updated links * Updated pktgen-env * Added pktgen doc * Fixed linebreak * updated filenames * Updated links * Updated links * Updated CSP use case README * Updated link name * Updated CSC README * Updated link * Added step for removing csp * Added step for removing CSC use case * Added note to README * Added note to README * Added image for IPsec example * Updated IPsec README --- docs/Deploy_cnf_testbed_k8s.md | 145 +++++++++++++++++++++++++++ docs/Deploy_pktgen_cnf_testbed.md | 94 +++++++++++++++++ docs/Deploy_vswitch_cnf_testbed.md | 58 +++++++++++ examples/use_case/3c2n-csc/README.md | 46 ++++----- examples/use_case/3c2n-csp/README.md | 47 ++++----- examples/use_case/ipsec/README.md | 57 +++++------ examples/use_case/ipsec/ipsec.png | Bin 0 -> 24340 bytes tools/k8s-example.env | 37 +++++++ tools/pktgen-example.env | 21 ++++ 9 files changed, 418 insertions(+), 87 deletions(-) create mode 100644 docs/Deploy_cnf_testbed_k8s.md create mode 100644 docs/Deploy_pktgen_cnf_testbed.md create mode 100644 docs/Deploy_vswitch_cnf_testbed.md create mode 100644 examples/use_case/ipsec/ipsec.png create mode 100644 tools/k8s-example.env create mode 100644 tools/pktgen-example.env diff --git a/docs/Deploy_cnf_testbed_k8s.md b/docs/Deploy_cnf_testbed_k8s.md new file mode 100644 index 0000000..e85a725 --- /dev/null +++ b/docs/Deploy_cnf_testbed_k8s.md @@ -0,0 +1,145 @@ +# Deploy CNF Testbed Kubernetes Cluster + +This document will show how to set up a CNF Testbed environment. Everything will be deployed on servers hosted by [Packet.com](https://www.packet.com/). + +## Prerequisites +Before starting the deployment you will need access to a project on Packet. Note down the **PROJECT_NAME** and **PROJECT_ID**, both found through the Packet web portal, as these will be used throughout the deployment for provisioning servers and configuring the network. You will also need a personal **PACKET_AUTH_TOKEN**, which is created and found in personal settings under API Keys. + +You should also make sure that you have a keypair available for SSH access. You can add your public key to the project on Packet through the web portal, which ensures that you will have passwordless SSH access to all servers used for deploying the CNF Testbed. + +## Prepare workstation / jump server +Once the project on Packet has been configured, start by creating a server, e.g. x1.small.x86 with Ubuntu 18.04 LTS, to use as workstation for deploying and managing the CNF Testbed. + +Once the workstation machine is running, start by installing the following dependencies: +``` +$ apt update +$ apt install -y git \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg-agent \ + software-properties-common +``` + +You will also need to install Docker prior to deploying the CNF Testbed: +``` +## Install Docker (from https://docs.docker.com/install/linux/docker-ce/ubuntu/) +$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +$ add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" +$ apt update +$ apt install -y docker-ce docker-ce-cli containerd.io +``` + +At this point you can clone to CNF Testbed: +``` +## Clone CNF Testbed +$ git clone --depth 1 https://github.com/cncf/cnf-testbed.git +``` + +Optionally you can install Kubectl on the workstation, which is used to manage the Kubernetes cluster: +``` +## Install Kubectl (from https://kubernetes.io/docs/tasks/tools/install-kubectl/) +$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl +$ chmod +x ./kubectl +$ mv ./kubectl /usr/local/bin/kubectl +``` + +Then, create a keypair on the workstation: +``` +## Save as default: id_rsa +$ ssh-keygen -t rsa -b 4096 +``` + +Add this key to the project on Packet as well, since it will be used throughout the CNF Testbed installation. + +Change to the CNF Testbed directory created previously (default: cnf-testbed), and use the provided Makefile to install additional dependencies: +``` +$ make deps +``` + +## Deploy CNF Testbed Kubernetes Cluster +This section will show how to deploy one or more K8s clusters on Packet. + +Start by going to the `tools/` directory. Copy or edit the [k8s-example.env](/tools/k8s-example.env) file (for this guide the filename `k8s-example.env` is used). The default content of the file is described below. +``` +##################################### +#### Packet.com Project Settings #### +##################################### +export PACKET_AUTH_TOKEN=your-auth-token +export PACKET_PROJECT_ID=your-project-id +export PACKET_PROJECT_NAME="your-project-name" +## These three values are the ones collected as part of the prerequisites earlier. + +######################################## +#### Packet.com Server Provisioning #### +######################################## +export DEPLOY_NAME=cnftestbed +## Prefix to use for server hostname and VLANs +export VLAN_SEGMENT=${DEPLOY_NAME} +## Prefix of the VLAN segments created during deployment +export FACILITY=ewr1 +## Facility to use for deployment (others can be found through Packet.com web portal) + +#### Kubernetes "Master" Node Group #### +export NODE_GROUP_ONE_NAME=${DEPLOY_NAME}-master +## Name to append "group one" hostnames that are used for K8s master nodes +export NODE_GROUP_ONE_DEVICE_PLAN=c1.small.x86 +## Instance type for nodes (others can be found through Packet.com web portal) +export NODE_GROUP_ONE_COUNT=1 +## Number of nodes to deploy - Use an odd number to avoid errors with K8s deployment + +#### Kubernetes "Worker" Node Group #### +export NODE_GROUP_TWO_NAME=${DEPLOY_NAME}-worker +## Name to append "group two" hostnames that are used for K8s worker nodes +export NODE_GROUP_TWO_DEVICE_PLAN=n2.xlarge.x86 +## Instance type for nodes. Use either 'n2.xlarge.x86' or 'm2.xlarge.x86' +export NODE_GROUP_TWO_COUNT=1 +## Number of nodes to deploy +# export PLAYBOOK=k8s_worker_vswitch_mellanox.yml +## Uncomment PLAYBOOK only if NODE_GROUP_TWO_DEVICE_PLAN=m2.xlarge.x86 (Mellanox NIC) + +#### Extra Kubernetes "Worker" Node Group #### +export NODE_GROUP_THREE_NAME=${DEPLOY_NAME}-extra +## Name to append "group three" hostnames that are used for extra K8s worker nodes +export NODE_GROUP_THREE_DEVICE_PLAN=n2.xlarge.x86 +## Instance type for nodes. Use either 'n2.xlarge.x86' or 'm2.xlarge.x86' +## If planning to install the vSwitch later, group two and three must use the same instance type +export NODE_GROUP_THREE_COUNT=0 +## Number of nodes to deploy - By default the extra group is not used + +########################### +#### Advanced settings #### +########################### +export OPERATING_SYSTEM=ubuntu_18_04 +## Operating system deployed on all provisioned servers +export ISOLATED_CORES=0 +## Number of cores to isolate through the kernel (isolcpus). +## 0 means isolate all cores except one on each socket for the operating system +export STATE_FILE=${PWD}/data/${DEPLOY_NAME}/terraform.tfstate +## Use a non-default STATE_FILE location +export NODE_FILE=${PWD}/data/${DEPLOY_NAME}/kubernetes.env +## Use a non-default NODE_FILE location +``` + +After updating the file, return to the CNF Testbed directory. From here, start server provisioning using the Makefile: +``` +$ make hw_k8s load_envs ${PWD}/tools/k8s-example.env +## Update the path to the environment file if needed +``` + +After a few minutes the servers will be provisioned. Continue with deploying Kubernetes: +``` +$ make k8s load_envs ${PWD}/tools/k8s-example.env +## Update the path to the environment file if needed +``` + +Once completed, the Kubernetes cluster is ready for use. If Kubectl is installed on the workstation machine, the kubeconfig file can be in found from the cnf-testbed directory in `${PWD}/data/${DEPLOY_NAME}/mycluster/artifacts/admin.conf`. Configure Kubectl to use this file, and check that the cluster is ready: +``` +$ export KUBECONFIG="${PWD}/data/${DEPLOY_NAME}/mycluster/artifacts/admin.conf" +$ kubectl get nodes +``` + +Alternatively, kubectl can be used directory from the master node(s), without having to specify KUBECONFIG. diff --git a/docs/Deploy_pktgen_cnf_testbed.md b/docs/Deploy_pktgen_cnf_testbed.md new file mode 100644 index 0000000..476fcc6 --- /dev/null +++ b/docs/Deploy_pktgen_cnf_testbed.md @@ -0,0 +1,94 @@ +# Deploy CNF Testbed Packet Generator + +This document will show how to set up a packet generator for CNF Testbed. Everything will be deployed on servers hosted by [Packet.com](https://www.packet.com/). + +The packet generator can be used to verify and benchmark service chains deployed in a CNF Testbed Kubernetes cluster. + +## Prerequisites +Before starting the deployment you will need access to a project on Packet. Note down the **PROJECT_NAME** and **PROJECT_ID**, both +found through the Packet web portal, as these will be used throughout the deployment for provisioning servers and configuring the network. You will also need a personal **PACKET_AUTH_TOKEN**, which is created and found in personal settings under API Keys. + +You should also make sure that you have a keypair available for SSH access. You can add your public key to the project on Packet through the web portal, which ensures that you will have passwordless SSH access to all servers used for deploying the CNF Testbed. + +## Prepare workstation / jump server +The steps for setting up a workstation can be found [here](/docs/Deploy_cnf_testbed_k8s.md#prepare-workstation--jump-server) + +## Deploy CNF Testbed Packet Generator +Start by going to the `tools/` directory. Copy or edit the [pktgen-example.env](/tools/pktgen-example.env) file (for this guide the filename pktgen-example.env is used). The default content of the file is described below. + +``` +##################################### +#### Packet.com Project Settings #### +##################################### +export PACKET_AUTH_TOKEN=your-auth-token +export PACKET_PROJECT_ID=your-project-id +export PACKET_PROJECT_NAME="your-project-name" +## These three values are the ones collected as part of the prerequisites earlier. + +######################################## +#### Packet.com Server Provisioning #### +######################################## +export DEPLOY_NAME=cnftestbed +## Prefix to use for server hostname and VLANs +## Ideally reuse the same DEPLOY_NAME as for the Kubernetes cluster +export VLAN_SEGMENT=${DEPLOY_NAME} +## Prefix of the VLAN segments created during deployment +## Change this to match the DEPLOY_NAME of the Kubernetes cluster if a different name is used above +export FACILITY=ewr1 +## Facility to use for deployment (others can be found through Packet.com web portal) +## Ideally use the same FACILITY as the Kubernetes cluster +export NODE_GROUP_ONE_NAME=${DEPLOY_NAME}-pktgen +## Hostname of the packet generator server + +########################### +#### Advanced settings #### +########################### +export ISOLATED_CORES=0 +## Number of cores to isolate through the kernel (isolcpus). +## 0 means isolate all cores except one on each socket for the operating system +export STATE_FILE=${PWD}/data/${DEPLOY_NAME}/packet_gen.tfstate +## Use a non-default STATE_FILE location +export NODE_FILE=${PWD}/data/${DEPLOY_NAME}/packet_gen.env +## Use a non-default NODE_FILE location +``` + +After updating the file, return to the CNF Testbed directory. From here, start server provisioning using the Makefile: +``` +$ make hw_pktgen load_envs ${PWD}/tools/pktgen-example.env +## Update the path to the environment file if needed +``` + +After a few minutes the server will be provisioned. By default, the packet generator will be deployed with additional data visualization tools. This can be disabled by updating the vars section in `comparison/ansible/packet_generator.yml`: +``` +visualization: true +## Change to 'false' to skip installing visualization tools +``` + +If the visualization is left enabled, more details on accessing and using this can be found [here](/docs/Visualization.md). + +Once ready, continue with deploying the packet generator: +``` +$ make pktgen load_envs ${PWD}/tools/pktgen-example.env +## Update the path to the environment file if needed +``` + +Once completed, SSH to the packet generator machine, where all the files needed to run the generator can be found in the `/root` directory. Start by having a look at `run_test.sh`, which has a few variables that can be configured: +``` +RATES=( 10Gbps ndr_pdr ) +## An array of tests to run, other examples are '5Mpps', 'pdr' or 'ndr' +ITERATIONS=1 +## Number of iterations to run for the above RATES +DURATION=2 +## Duration in seconds to generate packets. For pdr/ndr tests this is per step in the binary search +``` + +The `nfvbench_config.cfg` file can be used to further modify the configuration. For tests using the provided use cases [3c2n-csc](/examples/use_case/3c2n-csc) and [3c2n-csp](/examples/use_case/3c2n-csp) nothing needs to be changed, but for custom service chains the IP addresses and service chain count values may need to be updated. + +Before use cases can be deployed, the MAC addresses of the packet generator must be collected. Run the generator and wait for the MACs to be printed as shown below: +``` +$ ./run_tests.sh +(...) +Port 0: Ethernet Controller X710 for 10GbE SFP+ speed=10Gbps mac=aa:bb:cc:dd:ee:ff pci=0000:1a:00.1 driver=net_i40e +Port 1: Ethernet Controller X710 for 10GbE SFP+ speed=10Gbps mac=ff:ee:dd:cc:bb:aa pci=0000:1a:00.3 driver=net_i40e +## At this point the generator can be stopped using ctrl+c +``` diff --git a/docs/Deploy_vswitch_cnf_testbed.md b/docs/Deploy_vswitch_cnf_testbed.md new file mode 100644 index 0000000..ba88ed1 --- /dev/null +++ b/docs/Deploy_vswitch_cnf_testbed.md @@ -0,0 +1,58 @@ +# Deploy vSwitch (VPP) in CNF Testbed Kubernetes Cluster + +This document will show how to set up a CNF Testbed environment. Everything will be deployed on servers hosted by Packet.com. + +Before deploying the vSwitch, make sure that a CNF Testbed Kubernetes Cluster has already been deployed. Steps for doing this can be found [here](Deploy_cnf_testbed_k8s.md). The environment file used for deploying the Kubernetes cluster will be used for deploying the vSwitch as well. + +## Prerequisites +Before starting the deployment you will need access to a project on Packet. Note down the **PROJECT_NAME** and **PROJECT_ID**, both +found through the Packet web portal, as these will be used throughout the deployment for provisioning servers and configuring the network. You will also need a personal **PACKET_AUTH_TOKEN**, which is created and found in personal settings under API Keys. + +You should also make sure that you have a keypair available for SSH access. You can add your public key to the project on Packet through the web portal, which ensures that you will have passwordless SSH access to all servers used for deploying the CNF Testbed. + +## Deploy vSwitch in CNF Testbed Kubernetes Cluster + +While no additional configuration is needed, there are a few configuration options that can be modified prior to installing the vSwitch. + +By default, if the 'n2.xlarge.x86' instance type is used, vSwitch installation is done using `cnf-testbed/comparison/ansible/k8s_worker_vswitch_quad_intel.yml`. This file has a few variables that can be changed: +``` +vswitch_container: false +## Run the vSwitch (VPP) in a container. By default (false) it runs directly on the host +corelist_workers: 3 +## Number of cores to use for workload in the vSwitch +rx_queues: 3 +## Number of receive queues per NIC port in the vSwitch +multus_cni: false +## Configure the node for use with SRIOV Network Device Plugin and CNI (examples/workload-infra/multus_sriov) +## Changing this to true disables the vSwitch +``` + +If using the 'm2.xlarge.x86' instance type, with the PLAYBOOK variable uncommented in the environment file, the installation is done using `cnf-testbed/comparison/ansible/k8s_worker_vswitch_mellanox.yml`, which also has a few configuration options: +``` +vswitch_container: false +## Run the vSwitch (VPP) in a container. By default (false) it runs directly on the host +corelist_workers: 3 +## Number of cores to use for workload in the vSwitch +rx_queues: 6 +## Number of receive queues per NIC port in the vSwitch +``` + +Once configured, return to the cnf-testbed directory, and install the vSwitch using the Makefile: +``` +$ make vswitch load_envs ${PWD}/tools/k8s-example.env +## Use the same environment file as for the cluster +``` + +Once completed, and if `multus_cni: false`, SSH to the worker node(s) and verify that the vSwitch is running. + +If `vswitch_container: false`: +``` +$ vppctl show version +``` + +Else, if `vswitch_container: true`: +``` +$ docker exec -it vppcontainer vppctl show version +``` + +If `multus_cni: true` has been configured, the next steps for installing the SRIOV plugins can be found [here](/examples/workload-infra/multus_sriov). diff --git a/examples/use_case/3c2n-csc/README.md b/examples/use_case/3c2n-csc/README.md index acd7985..913e177 100644 --- a/examples/use_case/3c2n-csc/README.md +++ b/examples/use_case/3c2n-csc/README.md @@ -5,46 +5,28 @@ This example installs the snake service chain example on a kubernetes worker nod ![Example "Snake" service chain](snake.png) ### Prerequisites -A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. A guide to deploying K8s can be found in [Deploy_K8s_CNF_Testbed.md](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md) +A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. Guides for setting this up can be found here: +* [Provision HW and deploy CNF Testbed Kubernetes cluster](/docs/Deploy_cnf_testbed_k8s.md) +* [Deploy vSwitch (VPP) in CNF Testbed Kubernetes cluster](/docs/Deploy_vswitch_cnf_testbed.md) You should have a `kubeconfig` file ready on the machine, as it is used to deploy the example on a worker node. -Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh](https://helm.sh/docs/using_helm/#from-script) +Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh/docs/intro/install/](https://helm.sh/docs/intro/install/) ``` -$ curl -LO https://git.io/get_helm.sh +$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 $ chmod 700 get_helm.sh $ ./get_helm.sh -$ helm init --service-account tiller - -## You might need to run the below if versions are mismatched - $ helm init --upgrade ``` -You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#deploy-packet-generator). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example +You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example. **Preparing the K8s worker node** -The host vSwitch (VPP) configuration must be updated prior to running this example. - -On the worker node, start by checking the PCI devices used by VPP: -``` -$ grep dev /etc/vpp/startup.conf | grep -v default -## (example, n2.xlarge) dev 0000:1a:00.1 dev 0000:1a:00.3 -## (example, m2.xlarge) dev 0000:5e:00.1 -## n2.xlarge (Intel) servers have two devices, m2.xlarge (Mellanox) has one device -``` - -Now replace the configuration file with the one for this example as follows: +Before installing this (3c2n-csc) example use case, the vSwitch (VPP) configuration needs to be updated. SSH to the worker node and replace the vSwitch configuration as shown below: ``` $ cp /etc/vpp/templates/3c2n-csc.gate /etc/vpp/setup.gate ``` -Once the filw has been replaced, open it (`/etc/vpp/setup.gate`) with your favorite editor, and make sure the device names match the PCI devices listed previously. Make sure all instances of the name are updated: -``` -## (example, n2.xlarge) TenGigabitEthernet1a/0/1, TenGigabitEthernet1a/0/3 -## (example, m2.xlarge) TwentyFiveGigabitEthernet5e/0/1 -``` - Once that has been done, restart the vSwitch using the below step (depending on how the vSwitch is deployed): ``` ## vSwitch running in host @@ -56,14 +38,22 @@ $ docker restart vppcontainer ### Installing the Snake service chain example -Start by modifying the first line in `./csc/values.yaml` to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: +_Make sure no other example use cases is currently installed - Check using `helm list` and delete using `helm delete ` if necessary + +Start by modifying the first line in [csc/values.yaml](./csc/values.yaml) to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: ``` ## set environment variable for KUBECONFIG (replace path to match your location) $ export KUBECONFIG=//kubeconfig -$ helm install ./csc/ +$ helm install csc ./csc/ ``` ### Testing the Snake service chain example -Follow the steps listed in [Run Traffic Benchmark](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#run-traffic-benchmark). The packet generator should be configured for 3 chains with this example +Follow the steps listed in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). The packet generator should already be configured to work with this example use case. +### Removing the Snake service chain example + +To remove this example use case, run the below command: +``` +$ helm delete csc +``` diff --git a/examples/use_case/3c2n-csp/README.md b/examples/use_case/3c2n-csp/README.md index 12cb8d5..bbe7177 100644 --- a/examples/use_case/3c2n-csp/README.md +++ b/examples/use_case/3c2n-csp/README.md @@ -5,47 +5,28 @@ This example installs the pipeline service chain example on a kubernetes worker ![Example "pipeline" service chain](pipeline.png) ### Prerequisites -A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. A guide to deploying K8s can be found in [Deploy_K8s_CNF_Testbed.md](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md) +A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. Guides for setting this up can be found here: +* [Provision HW and deploy CNF Testbed Kubernetes cluster](/docs/Deploy_cnf_testbed_k8s.md) +* [Deploy vSwitch (VPP) in CNF Testbed Kubernetes cluster](/docs/Deploy_vswitch_cnf_testbed.md) You should have a `kubeconfig` file ready on the machine, as it is used to deploy the example on a worker node. -Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh](https://helm.sh/docs/using_helm/#from-script) +Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh/docs/intro/install/](https://helm.sh/docs/intro/install/) ``` -$ curl -LO https://git.io/get_helm.sh +$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 $ chmod 700 get_helm.sh $ ./get_helm.sh -$ helm init --service-account tiller - -## You might need to run the below if versions are mismatched - $ helm init --upgrade - $ helm init --service-account tiller ``` -You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#deploy-packet-generator). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example +You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example. **Preparing the K8s worker node** -The host vSwitch (VPP) configuration must be updated prior to running this example. - -On the worker node, start by checking the PCI devices used by VPP: -``` -$ grep dev /etc/vpp/startup.conf | grep -v default -## (example, n2.xlarge) dev 0000:1a:00.1 dev 0000:1a:00.3 -## (example, m2.xlarge) dev 0000:5e:00.1 -## n2.xlarge (Intel) servers have two devices, m2.xlarge (Mellanox) has one device -``` - -Now replace the configuration file with the one for this example as follows: +By default the vSwitch (VPP) is configured for this (3c2n-csp) example use case. If a different configuration has been applied, SSH to the worker node and replace the vSwitch configuration as shown below: ``` $ cp /etc/vpp/templates/3c2n-csp.gate /etc/vpp/setup.gate ``` -Once the filw has been replaced, open it (`/etc/vpp/setup.gate`) with your favorite editor, and make sure the device names match the PCI devices listed previously. Make sure all instances of the name are updated: -``` -## (example, n2.xlarge) TenGigabitEthernet1a/0/1, TenGigabitEthernet1a/0/3 -## (example, m2.xlarge) TwentyFiveGigabitEthernet5e/0/1 -``` - Once that has been done, restart the vSwitch using the below step (depending on how the vSwitch is deployed): ``` ## vSwitch running in host @@ -57,14 +38,22 @@ $ docker restart vppcontainer ### Installing the Pipeline service chain example -Start by modifying the first line in `./csp/values.yaml` to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: +_Make sure no other example use cases is currently installed - Check using `helm list` and delete using `helm delete ` if necessary_ + +Start by modifying the first line in [csp/values.yaml](./csp/values.yaml) to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: ``` ## set environment variable for KUBECONFIG (replace path to match your location) $ export KUBECONFIG=//kubeconfig -$ helm install ./csp/ +$ helm install csp ./csp/ ``` ### Testing the Pipeline service chain example -Follow the steps listed in [Run Traffic Benchmark](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#run-traffic-benchmark). The packet generator should be configured for 3 chains with this example +Follow the steps listed in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). The packet generator should already be configured to work with this example use case. +### Removing the Pipeline service chain example + +To remove this example use case, run the below command: +``` +$ helm delete csp +``` diff --git a/examples/use_case/ipsec/README.md b/examples/use_case/ipsec/README.md index ade64d4..f4e383d 100644 --- a/examples/use_case/ipsec/README.md +++ b/examples/use_case/ipsec/README.md @@ -1,49 +1,32 @@ ## Install IPsec Service Chain Example (1 chain of 4 nodes) -This example installs the IPsec service chain example on a kubernetes worker node. All connections are done using Memif interfaces. Connections between 1-2 and 3-4 are direct, while the connection between 2-3 goes through the host vSwitch (VPP). +This example installs the IPsec service chain example on a kubernetes worker node. All connections are done using Memif interfaces. Connections between 1-2 and 3-4 are direct, while the connection between 2-3 uses IPsec encapsulation and goes through the host vSwitch (VPP). + +![Example IPsec service chain](ipsec.png) ### Prerequisites -A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. A guide to deploying K8s can be found in [Deploy_K8s_CNF_Testbed.md](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md) +A Kubernetes deployment with a host vSwitch (VPP) must be deployed prior to installing this example. Guides for setting this up can be found here: +* [Provision HW and deploy CNF Testbed Kubernetes cluster](/docs/Deploy_cnf_testbed_k8s.md) +* [Deploy vSwitch (VPP) in CNF Testbed Kubernetes cluster](/docs/Deploy_vswitch_cnf_testbed.md) You should have a `kubeconfig` file ready on the machine, as it is used to deploy the example on a worker node. -Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh](https://helm.sh/docs/using_helm/#from-script) +Helm must be installed prior to installing this example. The steps listed below are based on [https://helm.sh/docs/intro/install/](https://helm.sh/docs/intro/install/) ``` -$ curl -LO https://git.io/get_helm.sh +$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 $ chmod 700 get_helm.sh $ ./get_helm.sh -$ helm init --service-account tiller - -## You might need to run the below if versions are mismatched - $ helm init --upgrade - $ helm init --service-account tiller ``` -You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#deploy-packet-generator). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example +You will also need to configure a packet generator to test the example. Steps for doing this can be found in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). Be sure to note down the MAC addresses of the ports as mentioned in the section, as these will be needed prior to deploying the example. **Preparing the K8s worker node** -The host vSwitch (VPP) configuration must be updated prior to running this example. - -On the worker node, start by checking the PCI devices used by VPP: -``` -$ grep dev /etc/vpp/startup.conf | grep -v default -## (example, n2.xlarge) dev 0000:1a:00.1 dev 0000:1a:00.3 -## (example, m2.xlarge) dev 0000:5e:00.1 -## n2.xlarge (Intel) servers have two devices, m2.xlarge (Mellanox) has one device -``` - -Now replace the configuration file with the one for this example as follows: +Before installing this (IPsec) example use case, the vSwitch (VPP) configuration needs to be updated. SSH to the worker node and replace the vSwitch configuration as shown below: ``` $ cp /etc/vpp/templates/ipsec.gate /etc/vpp/setup.gate ``` -Once the filw has been replaced, open it (`/etc/vpp/setup.gate`) with your favorite editor, and make sure the device names match the PCI devices listed previously. Make sure all instances of the name are updated: -``` -## (example, n2.xlarge) TenGigabitEthernet1a/0/1, TenGigabitEthernet1a/0/3 -## (example, m2.xlarge) TwentyFiveGigabitEthernet5e/0/1 -``` - Once that has been done, restart the vSwitch using the below step (depending on how the vSwitch is deployed): ``` ## vSwitch running in host @@ -55,14 +38,28 @@ $ docker restart vppcontainer ### Installing the IPsec service chain example -Start by modifying the first line in `./ipsec/values.yaml` to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: +_Make sure no other example use cases is currently installed - Check using `helm list` and delete using `helm delete ` if necessary + +Start by modifying the first line in [ipsec/values.yaml](./ipsec/values.yaml) to include the MAC addresses of the packet generator that were collected as part of the prerequisites. Once that is done, install the example by running the below commands from this directory: ``` ## set environment variable for KUBECONFIG (replace path to match your location) $ export KUBECONFIG=//kubeconfig -$ helm install ./ipsec/ +$ helm install ipsec ./ipsec/ ``` ### Testing the IPsec service chain example -Follow the steps listed in [Run Traffic Benchmark](https://github.com/cncf/cnf-testbed/blob/master/docs/Deploy_K8s_CNF_Testbed.md#run-traffic-benchmark). The packet generator should be configured for 1 chain with this example +Follow the steps listed in [Deploy Packet Generator](/docs/Deploy_pktgen_cnf_testbed.md). + +The configuration will need to be updated prior to running this example. SSH to the packet generator machine and update `/root/nfvbench_config.cfg` to use only one chain: +``` +service_chain_count: 1 +``` + +### Removing the Snake service chain example + +To remove this example use case, run the below command: +``` +$ helm delete ipsec +``` diff --git a/examples/use_case/ipsec/ipsec.png b/examples/use_case/ipsec/ipsec.png new file mode 100644 index 0000000000000000000000000000000000000000..0c01e9f9ecc914a92d842df55c460cd5ebc969e1 GIT binary patch literal 24340 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