This commit is contained in:
Jerome Petazzoni
2019-10-28 11:40:06 -05:00
parent 0b80238736
commit e9e650ee48
20 changed files with 696 additions and 403 deletions

View File

@@ -5,3 +5,5 @@
# And this allows to do "git clone https://container.training".
/info/refs service=git-upload-pack https://github.com/jpetazzo/container.training/info/refs?service=git-upload-pack
/ /lisa.html 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,63 +0,0 @@
title: |
Introduction
to Containers
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- self-paced
chapters:
- shared/title.md
- logistics.md
- containers/intro.md
- shared/about-slides.md
- shared/toc.md
- - containers/Docker_Overview.md
#- containers/Docker_History.md
- containers/Training_Environment.md
- containers/Installing_Docker.md
- containers/First_Containers.md
- containers/Background_Containers.md
- containers/Start_And_Attach.md
- - containers/Initial_Images.md
- containers/Building_Images_Interactively.md
- containers/Building_Images_With_Dockerfiles.md
- containers/Cmd_And_Entrypoint.md
- - containers/Copying_Files_During_Build.md
- containers/Exercise_Dockerfile_Basic.md
- containers/Multi_Stage_Builds.md
- containers/Publishing_To_Docker_Hub.md
- containers/Dockerfile_Tips.md
- containers/Exercise_Dockerfile_Advanced.md
- - containers/Naming_And_Inspecting.md
- containers/Labels.md
- containers/Getting_Inside.md
- containers/Resource_Limits.md
- - containers/Container_Networking_Basics.md
- containers/Network_Drivers.md
- containers/Container_Network_Model.md
#- containers/Connecting_Containers_With_Links.md
- containers/Ambassadors.md
- - containers/Local_Development_Workflow.md
- containers/Windows_Containers.md
- containers/Working_With_Volumes.md
- containers/Compose_For_Dev_Stacks.md
- containers/Exercise_Composefile.md
- - containers/Docker_Machine.md
- containers/Advanced_Dockerfiles.md
- containers/Application_Configuration.md
- containers/Logging.md
- - containers/Namespaces_Cgroups.md
- containers/Copy_On_Write.md
#- containers/Containers_From_Scratch.md
- - containers/Container_Engines.md
#- containers/Ecosystem.md
- containers/Orchestration_Overview.md
- shared/thankyou.md
- containers/links.md

View File

@@ -1,63 +0,0 @@
title: |
Introduction
to Containers
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- in-person
chapters:
- shared/title.md
# - shared/logistics.md
- containers/intro.md
- shared/about-slides.md
- shared/toc.md
- - containers/Docker_Overview.md
- containers/Docker_History.md
- containers/Training_Environment.md
- containers/Installing_Docker.md
- containers/First_Containers.md
- containers/Background_Containers.md
- containers/Start_And_Attach.md
- - containers/Initial_Images.md
- containers/Building_Images_Interactively.md
- containers/Building_Images_With_Dockerfiles.md
- containers/Cmd_And_Entrypoint.md
- containers/Copying_Files_During_Build.md
- containers/Exercise_Dockerfile_Basic.md
- - containers/Multi_Stage_Builds.md
- containers/Publishing_To_Docker_Hub.md
- containers/Dockerfile_Tips.md
- containers/Exercise_Dockerfile_Advanced.md
- - containers/Naming_And_Inspecting.md
- containers/Labels.md
- containers/Getting_Inside.md
- - containers/Container_Networking_Basics.md
- containers/Network_Drivers.md
- containers/Container_Network_Model.md
#- containers/Connecting_Containers_With_Links.md
- containers/Ambassadors.md
- - containers/Local_Development_Workflow.md
- containers/Windows_Containers.md
- containers/Working_With_Volumes.md
- containers/Compose_For_Dev_Stacks.md
- containers/Exercise_Composefile.md
- containers/Docker_Machine.md
- - containers/Advanced_Dockerfiles.md
- containers/Application_Configuration.md
- containers/Logging.md
- containers/Resource_Limits.md
- - containers/Namespaces_Cgroups.md
- containers/Copy_On_Write.md
#- containers/Containers_From_Scratch.md
- - containers/Container_Engines.md
- containers/Ecosystem.md
- containers/Orchestration_Overview.md
- shared/thankyou.md
- containers/links.md

8
slides/lisa.html Normal file
View File

@@ -0,0 +1,8 @@
<a href="talk.yml.html">Slides for the talk (Monday)</a>
|
<a href="#">Slides for the tutorial (Wednesday)</a>
<p>
Tutorial slides will be available in a few days.
Thanks for understanding!

235
slides/lisa/begin.md Normal file
View File

@@ -0,0 +1,235 @@
class: title
@@TITLE@@
---
## Outline
- Introductions
- Kubernetes anatomy
- Building a 1-node cluster
- Connecting to services
- Adding more nodes
- What's missing
---
class: title
Introductions
---
## Hi!
- Jérôme Petazzoni ([@jpetazzo](https://twitter.com/jpetazzo))
- 🇫🇷🇺🇸🇩🇪
- 📦🧔🏻
- 🐋(📅📅📅📅📅📅📅)
- 🔥🧠😢💊 ([1], [2], [3])
- 👨🏻‍🏫✨☸️💰
- 😄👍🏻
[1]: http://jpetazzo.github.io/2018/09/06/the-depression-gnomes/
[2]: http://jpetazzo.github.io/2018/02/17/seven-years-at-docker/
[3]: http://jpetazzo.github.io/2017/12/24/productivity-depression-kanban-emoji/
???
I'm French, living in the US, with also a foot in Berlin (Germany).
I'm a container hipster: I was running containers in production,
before it was cool.
I worked 7 years at Docker, which according to Corey Quinn,
is "long enough to be legally declared dead".
I also struggled a few years with depressed and burn-out.
It's not what I'll discuss today, but it's a topic that matters
a lot to me, and I wrote a bit about it, check my blog if you'd like.
After a break, I decided to do something I love:
teaching witchcraft. I deliver Kubernetes training.
As you can see, I love emojis, but if you don't, it's OK.
(There will be much less emojis on the following slides.)
---
## Why this talk?
- One of my goals in 2018: pass the CKA exam
--
- Things I knew:
- kubeadm
- kubectl run, expose, YAML, Helm
- ancient container lore
--
- Things I didn't:
- how Kubernetes *really* works
- deploy Kubernetes The Hard Way
---
## Scope
- Goals:
- learn enough about Kubernetes to ace that exam
- learn enough to teach that stuff
- Non-goals:
- set up a *production* cluster from scratch
- build everything from source
---
## Why are *you* here?
--
- Need/want/must build Kubernetes clusters
--
- Just curious about Kubernetes internals
--
- The Zelda theme
--
- (Other, please specify)
---
class: title
TL,DR
---
class: title
*The easiest way to install Kubernetes
is to get someone else to do it for you.*
(Me, after extensive research.)
???
Which means that if any point, you decide to leave,
I will not take it personally, but assume that you
eventually saw the light, and that you would like to
hire me or some of my colleagues to build your
Kubernetes clusters. It's all good.
---
class: talk-only
## This talk is also available as a tutorial
- Wednesday, October 30, 2019 - 11:00 am12:30 pm
- Salon ABCD
- Same content
- Everyone will get a cluster of VMs
- Everyone will be able to do the stuff that I'll demo today!
---
class: title
The Truth¹ About Kubernetes
.footnote[¹Some of it]
---
## What we want to do
```bash
kubectl run web --image=nginx --replicas=3
```
*or*
```bash
kubectl create deployment web --image=nginx
kubectl scale deployment web --replicas=3
```
*then*
```bash
kubectl expose deployment web --port=80
curl http://...
```
---
## What really happens
- `kubectl` generates a manifest describing a Deployment
- That manifest is sent to the Kubernetes API server
- The Kubernetes API server validates the manifest
- ... then persists it to etcd
- Some *controllers* wake up and do a bunch of stuff
.footnote[*The amazing diagram on the next slide is courtesy of [Lucas Käldström](https://twitter.com/kubernetesonarm).*]
---
class: pic
![Diagram showing Kubernetes architecture](images/k8s-arch4-thanks-luxas.png)
???
What we're really doing, is storing a bunch of objects in etcd.
But etcd, unlike a SQL database, doesn't have schemas or types.
So to prevent us from dumping any kind of trash data in etcd,
We have to read/write to it through the API server.
The API server will enforce typing and consistency.
Etcd doesn't have schemas or types, but it has the ability to
watch a key or set of keys, meaning that it's possible to subscribe
to updates of objects.
The controller manager is a process that has a bunch of loops,
each one responsible for a specific type of object.
So there is one that will watch the deployments, and as soon
as we create, updated, delete a deployment, it will wake up
and do something about it.

167
slides/lisa/cni.md Normal file
View File

@@ -0,0 +1,167 @@
class: title
Beyond kubenet
---
## When kubenet is not enough (1/2)
- IP address allocation is rigid
(one subnet per node)
- What about DHCP?
- What about e.g. ENI on AWS?
(allocating Elastic Network Interfaces to containers)
---
## When kubenet is not enough (1/2)
- Containers are connected to a Linux bridge
- What about:
- Open vSwitch
- VXLAN
- skipping layer 2
- using directly a network interface (macvlan, SR-IOV...)
---
## The Container Network Interface
- Allows us to decouple network configuration from Kubernetes
- Implemented by plugins
- Plugins are executables that will be invoked by kubelet
- Plugins are responsible for:
- allocating IP addresses for containers
- configuring the network for containers
- Plugins can be combined and chained when it makes sense
---
## Combining plugins
- Interface could be created by e.g. `vlan` or `bridge` plugin
- IP address could be allocated by e.g. `dhcp` or `host-local` plugin
- Interface parameters (MTU, sysctls) could be tweaked by the `tuning` plugin
The reference plugins are available [here].
Look in each plugin's directory for its documentation.
[here]: https://github.com/containernetworking/plugins/tree/master/plugins
---
## How plugins are invoked
- Parameters are given through environment variables, including:
- CNI_COMMAND: desired operation (ADD, DEL, CHECK, or VERSION)
- CNI_CONTAINERID: container ID
- CNI_NETNS: path to network namespace file
- CNI_IFNAME: what the network interface should be named
- The network configuration must be provided to the plugin on stdin
(this avoids race conditions that could happen by passing a file path)
---
## Setting up CNI
- We are going to use kube-router
- kube-router will provide the "pod network"
(connectivity with pods)
- kube-router will also provide internal service connectivity
(replacing kube-proxy)
- kube-router can also function as a Network Policy Controller
(implementing firewalling between pods)
---
## How kube-router works
- Very simple architecture
- Does not introduce new CNI plugins
(uses the `bridge` plugin, with `host-local` for IPAM)
- Pod traffic is routed between nodes
(no tunnel, no new protocol)
- Internal service connectivity is implemented with IPVS
- kube-router daemon runs on every node
---
## What kube-router does
- Connect to the API server
- Obtain the local node's `podCIDR`
- Inject it into the CNI configuration file
(we'll use `/etc/cni/net.d/10-kuberouter.conflist`)
- Obtain the addresses of all nodes
- Establish a *full mesh* BGP peering with the other nodes
- Exchange routes over BGP
- Add routes to the Linux kernel
---
## What's BGP?
- BGP (Border Gateway Protocol) is the protocol used between internet routers
- It [scales](https://www.cidr-report.org/as2.0/)
pretty [well](https://www.cidr-report.org/cgi-bin/plota?file=%2fvar%2fdata%2fbgp%2fas2.0%2fbgp-active%2etxt&descr=Active%20BGP%20entries%20%28FIB%29&ylabel=Active%20BGP%20entries%20%28FIB%29&with=step)
(it is used to announce the 700k CIDR prefixes of the internet)
- It is spoken by many hardware routers from many vendors
- It also has many software implementations (Quagga, Bird, FRR...)
- Experienced network folks generally know it (and appreciate it)
- It also used by Calico (another popular network system for Kubernetes)
- Using BGP allows us to interconnect our "pod network" with other systems
---
class: pic
![Demo time!](images/demo-with-kht.png)

56
slides/lisa/dmuc.md Normal file
View File

@@ -0,0 +1,56 @@
class: title
Building a 1-node cluster
---
## Requirements
- Linux machine (x86_64)
2 GB RAM, 1 CPU is OK
- Root (for Docker and Kubelet)
- Binaries:
- etcd
- Kubernetes
- Docker
---
## What we will do
- Create a deployment
(with `kubectl create deployment`)
- Look for our pods
- If pods are created: victory
- Else: troubleshoot, try again
.footnote[*Note: the exact commands that I run will be available
in the slides of the tutorial.*]
---
class: pic
![Demo time!](images/demo-with-kht.png)
---
## What have we done?
- Started a basic Kubernetes control plane
(no authentication; many features are missing)
- Deployed a few pods

128
slides/lisa/end.md Normal file
View File

@@ -0,0 +1,128 @@
class: title
What's missing?
---
## What's missing?
- Mostly: security
- Notably: RBAC
- Also: availabilty
---
## TLS! TLS everywhere!
- Create certs for the control plane:
- etcd
- API server
- controller manager
- scheduler
- Create individual certs for nodes
- Create the service account key pair
---
## Service accounts
- The controller manager will generate tokens for service accounts
(these tokens are JWT, JSON Web Tokens, signed with a specific key)
- The API server will validate these tokens (with the matching key)
---
## Nodes
- Enable NodeRestriction admission controller
- authorizes kubelet to update their own node and pods data
- Enable Node Authorizer
- prevents kubelets from accessing data that they shouldn't
- only authorize access to e.g. a configmap if a pod is using it
- Bootstrap tokens
- add nodes to the cluster safely+dynamically
---
## Consequences of API server outage
- What happens if the API server goes down?
- kubelet will try to reconnect (as long as necessary)
- our apps will be just fine (but autoscaling will be broken)
- How can we improve the API server availability?
- redundancy (the API server is stateless)
- achieve a low MTTR
---
## Improving API server availability
- Redundancy implies to add one layer
(between API clients and servers)
- Multiple options available:
- external load balancer
- local load balancer (NGINX, HAProxy... on each node)
- DNS Round-Robin
---
## Achieving a low MTTR
- Run the control plane in highly available VMs
(e.g. many hypervisors can do that, with shared or mirrored storage)
- Run the control plane in highly available containers
(e.g. on another Kubernetes cluster)
---
class: title
Thank you!
---
## A word from my sponsor
- If you liked this presentation and would like me to train your team ...
Contact me: jerome.petazzoni@gmail.com
- Thank you! ♥️
- Also, slides👇🏻
![QR code to the slides](images/qrcode-lisa.png)

39
slides/lisa/kubenet.md Normal file
View File

@@ -0,0 +1,39 @@
class: title
Adding more nodes
---
## What do we need to do?
- More machines!
- Can we "just" start kubelet on these machines?
--
- We need to update the kubeconfig file used by kubelet
- It currently uses `localhost:8080` for the API server
- We need to change that!
---
## What we will do
- Get more nodes
- Generate a new kubeconfig file
(pointing to the node running the API server)
- Start more kubelets
- Scale up our deployment
---
class: pic
![Demo time!](images/demo-with-kht.png)

34
slides/lisa/kubeproxy.md Normal file
View File

@@ -0,0 +1,34 @@
class: title
Pod-to-service networking
---
## What we will do
- Create a service to connect to our pods
(with `kubectl expose deployment`)
- Try to connect to the service's ClusterIP
- If it works: victory
- Else: troubleshoot, try again
.footnote[*Note: the exact commands that I run will be available
in the slides of the tutorial.*]
---
class: pic
![Demo time!](images/demo-with-kht.png)
---
## What have we done?
- Started kube-proxy
- ... which created a bunch of iptables rules

View File

@@ -157,7 +157,7 @@ def processchapter(chapter, filename):
return processchapter(chapter.encode("utf-8"), filename)
if isinstance(chapter, str):
if "\n" in chapter:
titles = re.findall("^# (.*)", chapter, re.MULTILINE)
titles = [] # re.findall("^# (.*)", chapter, re.MULTILINE)
slidefooter = ".debug[{}]".format(makelink(filename))
chapter = chapter.replace("\n---\n", "\n{}\n---\n".format(slidefooter))
chapter += "\n" + slidefooter

View File

@@ -1,65 +0,0 @@
title: |
Container Orchestration
with Docker and Swarm
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- self-paced
- snap
- btp-auto
- benchmarking
- elk-manual
- prom-manual
chapters:
- shared/title.md
- logistics.md
- swarm/intro.md
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
- shared/connecting.md
- swarm/versions.md
- shared/sampleapp.md
- shared/composescale.md
- shared/hastyconclusions.md
- shared/composedown.md
- swarm/swarmkit.md
- shared/declarative.md
- swarm/swarmmode.md
- swarm/creatingswarm.md
#- swarm/machine.md
- swarm/morenodes.md
- - swarm/firstservice.md
- swarm/ourapponswarm.md
- swarm/hostingregistry.md
- swarm/testingregistry.md
- swarm/btp-manual.md
- swarm/swarmready.md
- swarm/stacks.md
- swarm/cicd.md
- swarm/updatingservices.md
- swarm/rollingupdates.md
- swarm/healthchecks.md
- - swarm/operatingswarm.md
- swarm/netshoot.md
- swarm/ipsec.md
- swarm/swarmtools.md
- swarm/security.md
- swarm/secrets.md
- swarm/encryptionatrest.md
- swarm/leastprivilege.md
- swarm/apiscope.md
- - swarm/logging.md
- swarm/metrics.md
- swarm/gui.md
- swarm/stateful.md
- swarm/extratips.md
- shared/thankyou.md
- swarm/links.md

View File

@@ -1,64 +0,0 @@
title: |
Container Orchestration
with Docker and Swarm
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
#chat: "[Gitter](https://gitter.im/jpetazzo/workshop-yyyymmdd-city)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- self-paced
- snap
- btp-manual
- benchmarking
- elk-manual
- prom-manual
chapters:
- shared/title.md
- logistics.md
- swarm/intro.md
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
- shared/connecting.md
- swarm/versions.md
- shared/sampleapp.md
- shared/composescale.md
- shared/hastyconclusions.md
- shared/composedown.md
- swarm/swarmkit.md
- shared/declarative.md
- swarm/swarmmode.md
- swarm/creatingswarm.md
#- swarm/machine.md
- swarm/morenodes.md
- - swarm/firstservice.md
- swarm/ourapponswarm.md
#- swarm/hostingregistry.md
#- swarm/testingregistry.md
#- swarm/btp-manual.md
#- swarm/swarmready.md
- swarm/stacks.md
- swarm/cicd.md
- swarm/updatingservices.md
#- swarm/rollingupdates.md
#- swarm/healthchecks.md
- - swarm/operatingswarm.md
#- swarm/netshoot.md
#- swarm/ipsec.md
#- swarm/swarmtools.md
- swarm/security.md
#- swarm/secrets.md
#- swarm/encryptionatrest.md
- swarm/leastprivilege.md
- swarm/apiscope.md
- swarm/logging.md
- swarm/metrics.md
#- swarm/stateful.md
#- swarm/extratips.md
- shared/thankyou.md
- swarm/links.md

View File

@@ -1,73 +0,0 @@
title: |
Container Orchestration
with Docker and Swarm
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- in-person
- btp-auto
chapters:
- shared/title.md
#- shared/logistics.md
- swarm/intro.md
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
- shared/connecting.md
- swarm/versions.md
- |
name: part-1
class: title, self-paced
Part 1
- shared/sampleapp.md
- shared/composescale.md
- shared/hastyconclusions.md
- shared/composedown.md
- swarm/swarmkit.md
- shared/declarative.md
- swarm/swarmmode.md
- swarm/creatingswarm.md
#- swarm/machine.md
- swarm/morenodes.md
- - swarm/firstservice.md
- swarm/ourapponswarm.md
- swarm/hostingregistry.md
- swarm/testingregistry.md
- swarm/btp-manual.md
- swarm/swarmready.md
- swarm/stacks.md
- swarm/cicd.md
- |
name: part-2
class: title, self-paced
Part 2
- - swarm/operatingswarm.md
- swarm/netshoot.md
- swarm/swarmnbt.md
- swarm/ipsec.md
- swarm/updatingservices.md
- swarm/rollingupdates.md
- swarm/healthchecks.md
- swarm/nodeinfo.md
- swarm/swarmtools.md
- - swarm/security.md
- swarm/secrets.md
- swarm/encryptionatrest.md
- swarm/leastprivilege.md
- swarm/apiscope.md
- swarm/logging.md
- swarm/metrics.md
- swarm/stateful.md
- swarm/extratips.md
- shared/thankyou.md
- swarm/links.md

View File

@@ -1,72 +0,0 @@
title: |
Container Orchestration
with Docker and Swarm
chat: "[Slack](https://dockercommunity.slack.com/messages/C7GKACWDV)"
gitrepo: github.com/jpetazzo/container.training
slides: http://container.training/
exclude:
- in-person
- btp-auto
chapters:
- shared/title.md
#- shared/logistics.md
- swarm/intro.md
- shared/about-slides.md
- shared/toc.md
- - shared/prereqs.md
- shared/connecting.md
- swarm/versions.md
- |
name: part-1
class: title, self-paced
Part 1
- shared/sampleapp.md
- shared/composescale.md
- shared/hastyconclusions.md
- shared/composedown.md
- swarm/swarmkit.md
- shared/declarative.md
- swarm/swarmmode.md
- swarm/creatingswarm.md
#- swarm/machine.md
- swarm/morenodes.md
- - swarm/firstservice.md
- swarm/ourapponswarm.md
- swarm/hostingregistry.md
- swarm/testingregistry.md
- swarm/btp-manual.md
- swarm/swarmready.md
- swarm/stacks.md
- |
name: part-2
class: title, self-paced
Part 2
- - swarm/operatingswarm.md
#- swarm/netshoot.md
#- swarm/swarmnbt.md
- swarm/ipsec.md
- swarm/updatingservices.md
- swarm/rollingupdates.md
#- swarm/healthchecks.md
- swarm/nodeinfo.md
- swarm/swarmtools.md
- - swarm/security.md
- swarm/secrets.md
- swarm/encryptionatrest.md
- swarm/leastprivilege.md
- swarm/apiscope.md
#- swarm/logging.md
#- swarm/metrics.md
- swarm/stateful.md
- swarm/extratips.md
- shared/thankyou.md
- swarm/links.md

22
slides/talk.yml Normal file
View File

@@ -0,0 +1,22 @@
title: |
Deep Dive into
Kubernetes Internals
for Builders and Operators
(LISA2019)
chat: ""
gitrepo: ""
slides: ""
exclude:
- tutorial-only
chapters:
- lisa/begin.md
- k8s/deploymentslideshow.md
- lisa/dmuc.md
- lisa/kubeproxy.md
- lisa/kubenet.md
- lisa/cni.md
- lisa/end.md

View File

@@ -109,8 +109,8 @@ div.pic p {
div.pic img {
display: block;
margin: auto;
max-width: 1210px;
max-height: 550px;
max-width: 100%;
max-height: 100%;
}
div.pic h1, div.pic h2, div.title h1, div.title h2 {
text-align: center;

View File

@@ -28,6 +28,8 @@
var slideshow = remark.create({
ratio: '16:9',
highlightSpans: true,
slideNumberFormat: '@jpetazzo — %current%/%total%',
countIncrementalSlides: false,
excludedClasses: [@@EXCLUDE@@]
});
</script>