Operators are clients of the Kubernetes API that act as controllers for
a Custom Resource.
choosing a leader for a distributed application without an internal
member election process
publishing a Service to applications that don't support Kubernetes APIs to
discover them
The core of the Operator is code to tell the API server how to make
reality match the configured resources.
If you add a new SampleDB, the operator sets up PersistentVolumeClaims
to provide durable database storage, a StatefulSet to run SampleDB and
a Job to handle initial configuration.If you delete it, the Operator takes a snapshot, then makes sure that
the StatefulSet and Volumes are also removed.
to deploy an Operator is to add the
Custom Resource Definition and its associated Controller to your cluster.
Once you have an Operator deployed, you'd use it by adding, modifying or
deleting the kind of resource that the Operator uses.
"Recording a terminal session may be important in helping someone learn a process, sharing information in an understandable way, and also presenting a series of commands in a proper manner. Whatever the purpose, there are many times when copy-pasting text from the terminal won't be very helpful while capturing a video of the process is quite far-fetched and may not be always possible. In this quick guide, we will take a look at the easiest way to record and share a terminal session in .gif format."
"In computing, the Two Generals Problem is a thought experiment meant to illustrate the pitfalls and design challenges of attempting to coordinate an action by communicating over an unreliable link. In the experiment, two generals are only able to communicate with one another by sending a messenger through enemy territory. The experiment asks how they might reach an agreement on the time to launch an attack, while knowing that any messenger they send could be captured."
learn about is SSHKit and the various methods it provides
SSHKit was actually developed and released with Capistrano 3, and it’s basically a lower-level tool that provides methods for connecting and interacting with remote servers
on(): specifies the server to run on
within(): specifies the directory path to run in
with(): specifies the environment variables to run with
run on the application server
within the path specified
with certain environment variables set
execute(): the workhorse that runs the commands on your server
upload(): uploads a file from your local computer to your remote server
capture(): executes a command and returns its output as a string
upload() has the bang symbol (!) because that’s how it’s defined in SSHKit, and it’s just a convention letting us know that the method will block until it finishes.
But in order to ensure rake runs with the proper environment variables set, we have to use rake as a symbol and pass db:seed as a string
This format will also be necessary whenever you’re running any other Rails-specific commands that rely on certain environment variables being set
I recommend you take a look at SSHKit’s example page to learn more
make sure we pushed all our local changes to the remote master branch
run this task before Capistrano runs its own deploy task
actually creates three separate tasks
I created a namespace called deploy to contain these tasks since that’s what they’re related to.
we’re using the callbacks inside a namespace to make sure Capistrano knows which tasks the callbacks are referencing.
custom recipe (a Capistrano term meaning a series of tasks)
/shared: holds files and directories that persist throughout deploys
When you run cap production deploy, you’re actually calling a Capistrano task called deploy, which then sequentially invokes other tasks
your favorite browser (I hope it’s not Internet Explorer)
Deployment is hard and takes a while to sink in.
the most important thing is to not get discouraged
I didn’t want other people going through the same thing
taking advantage of the flexibility of NGINX access logging is application performance monitoring (APM).
it’s simple to get detailed visibility into the performance of your applications by adding timing values to your code and passing them as response headers for inclusion in the NGINX access log.
$request_time – Full request time, starting when NGINX reads the first byte from the client and ending when NGINX sends the last byte of the response body
each action also maps to particular CRUD operations in a database
resource :photo and resources :photos creates both singular and plural routes that map to the same controller (PhotosController).
One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions.
to only build routes with the minimal amount of information to uniquely identify the resource
The shallow method of the DSL creates a scope inside of which every nesting is shallow
These concerns can be used in resources to avoid code duplication and share behavior across routes
add a member route, just add a member block into the resource block
You can leave out the :on option, this will create the same member route except that the resource id value will be available in params[:photo_id] instead of params[:id].
Singular Resources
use a singular resource to map /profile (rather than /profile/:id) to the show action
Passing a String to get will expect a controller#action format
workaround
organize groups of controllers under a namespace
route /articles (without the prefix /admin) to Admin::ArticlesController
route /admin/articles to ArticlesController (without the Admin:: module prefix)
Nested routes allow you to capture this relationship in your routing.
helpers take an instance of Magazine as the first parameter (magazine_ads_url(@magazine)).
Resources should never be nested more than 1 level deep.
via the :shallow option
a balance between descriptive routes and deep nesting
:shallow_path prefixes member paths with the specified parameter
Routing Concerns allows you to declare common routes that can be reused inside other resources and routes
Rails can also create paths and URLs from an array of parameters.
use url_for with a set of objects
In helpers like link_to, you can specify just the object in place of the full url_for call
insert the action name as the first element of the array
This will recognize /photos/1/preview with GET, and route to the preview action of PhotosController, with the resource id value passed in params[:id]. It will also create the preview_photo_url and preview_photo_path helpers.
pass :on to a
route, eliminating the block:
Collection Routes
This will enable Rails to recognize paths such as /photos/search with GET, and route to the search action of PhotosController. It will also create the search_photos_url and search_photos_path route helpers.
simple routing makes it very easy to map legacy URLs to new Rails actions
add an alternate new action using the :on shortcut
When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request.
:controller maps to the name of a controller in your application
:action maps to the name of an action within that controller
optional parameters, denoted by parentheses
This route will also route the incoming request of /photos to PhotosController#index, since :action and :id are
use a constraint on :controller that matches the namespace you require
dynamic segments don't accept dots
The params will also include any parameters from the query string
:defaults option.
set params[:format] to "jpg"
cannot override defaults via query parameters
specify a name for any route using the :as option
create logout_path and logout_url as named helpers in your application.
Inside the show action of UsersController, params[:username] will contain the username for the user.
should use the get, post, put, patch and delete methods to constrain a route to a particular verb.
use the match method with the :via option to match multiple verbs at once
Routing both GET and POST requests to a single action has security implications
'GET' in Rails won't check for CSRF token. You should never write to the database from 'GET' requests
use the :constraints option to enforce a format for a dynamic segment
constraints
don't need to use anchors
Request-Based Constraints
the same name as the hash key and then compare the return value with the hash value.
constraint values should match the corresponding Request object method return type
reuse dynamic segments from the match in the path to redirect
this redirection is a 301 "Moved Permanently" redirect.
root method
put the root route at the top of the file
The root route only routes GET requests to the action.
root inside namespaces and scopes
For namespaced controllers you can use the directory notation
Only the directory notation is supported
use the :constraints option to specify a required format on the implicit id
specify a single constraint to apply to a number of routes by using the block
non-resourceful routes
:id parameter doesn't accept dots
:as option lets you override the normal naming for the named route helpers
use the :as option to prefix the named route helpers that Rails generates for a rout
prevent name collisions
prefix routes with a named parameter
This will provide you with URLs such as /bob/articles/1 and will allow you to reference the username part of the path as params[:username] in controllers, helpers and views
:only option
:except option
generate only the routes that you actually need can cut down on memory use and speed up the routing process.
alter path names
http://localhost:3000/rails/info/routes
rake routes
setting the CONTROLLER environment variable
Routes should be included in your testing strategy
In my opinion, understanding how a technology works under the hood is the best way to achieve learning speed and to build confidence that you are using the tool in the correct way.
The top-level layer may be read by a union-ing file system (AUFS on my docker implementation) to present a single cohesive view of all the changes as one read-only file system
Each subnet
must
reside entirely within one Availability Zone and cannot span zones.
Availability
Zones
are distinct locations that are engineered to be isolated from failures in other
Availability Zones
If a subnet's traffic is routed to an internet gateway, the subnet is known as a
public subnet.
If a subnet doesn't have a route to the internet gateway, the subnet is known as a
private subnet.
If a subnet doesn't have a route to the internet gateway, but has its traffic routed
to a
virtual private gateway for a VPN connection, the subnet is known as a
VPN-only subnet.
By default, all VPCs and subnets must have IPv4 CIDR blocks—you can't change
this behavior.
The allowed
block size is between a /16 netmask (65,536 IP addresses) and
/28 netmask (16 IP addresses).
The first four IP addresses and the last IP address in each subnet CIDR block are
not
available for you to use
The allowed block size is between a /28 netmask and
/16 netmask
The CIDR block must not overlap with any existing CIDR block that's associated with
the
VPC.
Each subnet must be associated with a route table
Every subnet that you create is automatically
associated with the main route table for the VPC
Security groups control inbound and outbound traffic for your
instances
network ACLs control inbound and outbound traffic for your subnets
each subnet must be associated with a network ACL
You can create a flow log on your VPC or subnet to capture the traffic that flows
to and
from the network interfaces in your VPC or subnet.
A VPC peering connection enables you to route traffic between the VPCs
using
private IP addresses
you cannot create a VPC peering connection between
VPCs that
have overlapping CIDR blocks
recommend that you create a VPC with a CIDR range large enough for expected
future growth, but not one that overlaps with current or expected future subnets anywhere
in
your corporate or home network, or that overlaps with current or future VPCs
{{ ... }} for Expressions to print to the template output
use a dot (.) to access attributes of a variable
the outer double-curly braces are not part of the
variable, but the print statement.
If you access variables inside tags don’t
put the braces around them.
If a variable or attribute does not exist, you will get back an undefined
value.
the default behavior is to evaluate to an empty string if
printed or iterated over, and to fail for every other operation.
if an object has an item and attribute with the same
name. Additionally, the attr() filter only looks up attributes.
Variables can be modified by filters. Filters are separated from the
variable by a pipe symbol (|) and may have optional arguments in
parentheses.
Multiple filters can be chained
Tests can be used
to test a variable against a common expression.
add is plus the name of the test after the variable.
to find out if a variable is defined, you can do name is defined,
which will then return true or false depending on whether name is defined
in the current template context.
strip whitespace in templates by hand. If you add a minus
sign (-) to the start or end of a block (e.g. a For tag), a
comment, or a variable expression, the whitespaces before or after
that block will be removed
not add whitespace between the tag and the minus sign
mark a block raw
Template inheritance
allows you to build a base “skeleton” template that contains all the common
elements of your site and defines blocks that child templates can override.
The {% extends %} tag is the key here. It tells the template engine that
this template “extends” another template.
access templates in subdirectories with a slash
can’t define multiple {% block %} tags with the same name in the
same template
use the special
self variable and call the block with that name
self.title()
super()
put the name of the block after the end tag for better
readability
if the block is replaced by
a child template, a variable would appear that was not defined in the block or
passed to the context.
setting the block to “scoped” by adding the scoped
modifier to a block declaration
If you have a variable that may
include any of the following chars (>, <, &, or ") you
SHOULD escape it unless the variable contains well-formed and trusted
HTML.
Jinja2 functions (macros, super, self.BLOCKNAME) always return template
data that is marked as safe.
With the default syntax, control structures appear inside
{% ... %} blocks.
the dictsort filter
loop.cycle
Unlike in Python, it’s not possible to break or continue in a loop
use loops recursively
add the recursive modifier
to the loop definition and call the loop variable with the new iterable
where you want to recurse.
The loop variable always refers to the closest (innermost) loop.
whether the value changed at all,
use it to test if a variable is defined, not
empty and not false
Macros are comparable with functions in regular programming languages.
If a macro name starts with an underscore, it’s not exported and can’t
be imported.
pass a macro to another macro
caller()
a single trailing newline is stripped if present
other whitespace (spaces, tabs, newlines etc.) is returned unchanged
a block tag works in “both”
directions. That is, a block tag doesn’t just provide a placeholder to fill
- it also defines the content that fills the placeholder in the parent.
Python dicts are not ordered
caller(user)
call(user)
This is a simple dialog rendered by using a macro and
a call block.
Filter sections allow you to apply regular Jinja2 filters on a block of
template data.
Assignments at
top level (outside of blocks, macros or loops) are exported from the template
like top level macros and can be imported by other templates.
using namespace
objects which allow propagating of changes across scopes
use block assignments to
capture the contents of a block into a variable name.
The extends tag can be used to extend one template from another.
Blocks are used for inheritance and act as both placeholders and replacements
at the same time.
The include statement is useful to include a template and return the
rendered contents of that file into the current namespace
Included templates have access to the variables of the active context by
default.
putting often used code into macros
imports are cached
and imported templates don’t have access to the current template variables,
just the globals by default.
Macros and variables starting with one or more underscores are private and
cannot be imported.
By default, included templates are passed the current context and imported
templates are not.
imports are often used just as a module that holds macros.
Integers and floating point numbers are created by just writing the
number down
Everything between two brackets is a list.
Tuples are like lists that cannot be modified (“immutable”).
A dict in Python is a structure that combines keys and values.
//
Divide two numbers and return the truncated integer result
The special constants true, false, and none are indeed lowercase
all Jinja identifiers are lowercase
(expr)
group an expression.
The is and in operators support negation using an infix notation
in
Perform a sequence / mapping containment test.
|
Applies a filter.
~
Converts all operands into strings and concatenates them.
use inline if expressions.
always an attribute is returned and items are not
looked up.
default(value, default_value=u'', boolean=False)¶
If the value is undefined it will return the passed default value,
otherwise the value of the variable
dictsort(value, case_sensitive=False, by='key', reverse=False)¶
Sort a dict and yield (key, value) pairs.
format(value, *args, **kwargs)¶
Apply python string formatting on an object
groupby(value, attribute)¶
Group a sequence of objects by a common attribute.
grouping by is stored in the grouper
attribute and the list contains all the objects that have this grouper
in common.
indent(s, width=4, first=False, blank=False, indentfirst=None)¶
Return a copy of the string with each line indented by 4 spaces. The
first line and blank lines are not indented by default.
join(value, d=u'', attribute=None)¶
Return a string which is the concatenation of the strings in the
sequence.
map()¶
Applies a filter on a sequence of objects or looks up an attribute.
pprint(value, verbose=False)¶
Pretty print a variable. Useful for debugging.
reject()¶
Filters a sequence of objects by applying a test to each object,
and rejecting the objects with the test succeeding.
replace(s, old, new, count=None)¶
Return a copy of the value with all occurrences of a substring
replaced with a new one.
round(value, precision=0, method='common')¶
Round the number to a given precision
even if rounded to 0 precision, a float is returned.
select()¶
Filters a sequence of objects by applying a test to each object,
and only selecting the objects with the test succeeding.
sort(value, reverse=False, case_sensitive=False, attribute=None)¶
Sort an iterable. Per default it sorts ascending, if you pass it
true as first argument it will reverse the sorting.
striptags(value)¶
Strip SGML/XML tags and replace adjacent whitespace by one space.
tojson(value, indent=None)¶
Dumps a structure to JSON so that it’s safe to use in <script>
tags.
trim(value)¶
Strip leading and trailing whitespace.
unique(value, case_sensitive=False, attribute=None)¶
Returns a list of unique items from the the given iterable
urlize(value, trim_url_limit=None, nofollow=False, target=None, rel=None)¶
Converts URLs in plain text into clickable links.
defined(value)¶
Return true if the variable is defined
in(value, seq)¶
Check if value is in seq.
mapping(value)¶
Return true if the object is a mapping (dict etc.).
number(value)¶
Return true if the variable is a number.
sameas(value, other)¶
Check if an object points to the same memory address than another
object
undefined(value)¶
Like defined() but the other way round.
A joiner is
passed a string and will return that string every time it’s called, except
the first time (in which case it returns an empty string).
namespace(...)¶
Creates a new container that allows attribute assignment using the
{% set %} tag
The with statement makes it possible to create a new inner scope.
Variables set within this scope are not visible outside of the scope.
activate and deactivate the autoescaping from within
the templates
With both trim_blocks and lstrip_blocks enabled, you can put block tags
on their own lines, and the entire block line will be removed when
rendered, preserving the whitespace of the contents
deployment.yaml: A basic manifest for creating a Kubernetes deployment
using the suffix .yaml for YAML files and .tpl for helpers.
It is just fine to put a plain YAML file like this in the templates/ directory.
helm get manifest
The helm get manifest command takes a release name (full-coral) and prints
out all of the Kubernetes resources that were uploaded to the server. Each file
begins with --- to indicate the start of a YAML document
Names should be unique to a release
The name: field is limited to 63 characters because of limitations to
the DNS system.
release names are limited to 53 characters
{{ .Release.Name }}
A template directive is enclosed in {{ and }} blocks.
The values that are passed into a template can be thought of as namespaced objects, where a dot (.) separates each namespaced element.
The leading dot before Release indicates that we start with the top-most namespace for this scope
The Release object is one of the built-in objects for Helm
When you want to test the template rendering, but not actually install anything, you can use helm install ./mychart --debug --dry-run
Using --dry-run will make it easier to test your code, but it won’t ensure that Kubernetes itself will accept the templates you generate.
Objects are passed into a template from the template engine.
create new objects within your templates
Objects can be simple, and have just one value. Or they can contain other objects or functions.
Release is one of the top-level objects that you can access in your templates.
Release.Namespace: The namespace to be released into (if the manifest doesn’t override)
Values: Values passed into the template from the values.yaml file and from user-supplied files. By default, Values is empty.
Chart: The contents of the Chart.yaml file.
Files: This provides access to all non-special files in a chart.
Files.Get is a function for getting a file by name
Files.GetBytes is a function for getting the contents of a file as an array of bytes instead of as a string. This is useful for things like images.
Template: Contains information about the current template that is being executed
BasePath: The namespaced path to the templates directory of the current chart
The built-in values always begin with a capital letter.
Go’s naming convention
use only initial lower case letters in order to distinguish local names from those built-in.
If this is a subchart, the values.yaml file of a parent chart
Individual parameters passed with --set
values.yaml is the default, which can be overridden by a parent chart’s values.yaml, which can in turn be overridden by a user-supplied values file, which can in turn be overridden by --set parameters.
While structuring data this way is possible, the recommendation is that you keep your values trees shallow, favoring flatness.
If you need to delete a key from the default values, you may override the value of the key to be null, in which case Helm will remove the key from the overridden values merge.
Kubernetes would then fail because you can not declare more than one livenessProbe handler.
When injecting strings from the .Values object into the template, we ought to quote these strings.
quote
Template functions follow the syntax functionName arg1 arg2...
While we talk about the “Helm template language” as if it is Helm-specific, it is actually a combination of the Go template language, some extra functions, and a variety of wrappers to expose certain objects to the templates.
Drawing on a concept from UNIX, pipelines are a tool for chaining together a series of template commands to compactly express a series of transformations.
pipelines are an efficient way of getting several things done in sequence
The repeat function will echo the given string the given number of times
default DEFAULT_VALUE GIVEN_VALUE. This function allows you to specify a default value inside of the template, in case the value is omitted.
all static default values should live in the values.yaml, and should not be repeated using the default command
Operators are implemented as functions that return a boolean value.
To use eq, ne, lt, gt, and, or, not etcetera place the operator at the front of the statement followed by its parameters just as you would a function.
if and
if or
with to specify a scope
range, which provides a “for each”-style loop
block declares a special kind of fillable template area
A pipeline is evaluated as false if the value is:
a boolean false
a numeric zero
an empty string
a nil (empty or null)
an empty collection (map, slice, tuple, dict, array)
incorrect YAML because of the whitespacing
When the template engine runs, it removes the contents inside of {{ and }}, but it leaves the remaining whitespace exactly as is.
{{- (with the dash and space added) indicates that whitespace should be chomped left, while -}} means whitespace to the right should be consumed.
Newlines are whitespace!
an * at the end of the line indicates a newline character that would be removed
Be careful with the chomping modifiers.
the indent function
Scopes can be changed. with can allow you to set the current scope (.) to a particular object.
Inside of the restricted scope, you will not be able to access the other objects from the parent scope.
range
The range function will “range over” (iterate through) the pizzaToppings list.
Just like with sets the scope of ., so does a range operator.
The toppings: |- line is declaring a multi-line string.
not a YAML list. It’s a big string.
the data in ConfigMaps data is composed of key/value pairs, where both the key and the value are simple strings.
The |- marker in YAML takes a multi-line string.
range can be used to iterate over collections that have a key and a value (like a map or dict).
In Helm templates, a variable is a named reference to another object. It follows the form $name
Variables are assigned with a special assignment operator: :=
{{- $relname := .Release.Name -}}
capture both the index and the value
the integer index (starting from zero) to $index and the value to $topping
For data structures that have both a key and a value, we can use range to get both
Variables are normally not “global”. They are scoped to the block in which they are declared.
one variable that is always global - $ - this variable will always point to the root context.
$.
$.
Helm template language is its ability to declare multiple templates and use them together.
A named template (sometimes called a partial or a subtemplate) is simply a template defined inside of a file, and given a name.
when naming templates: template names are global.
If you declare two templates with the same name, whichever one is loaded last will be the one used.
you should be careful to name your templates with chart-specific names.
templates in subcharts are compiled together with top-level templates
naming convention is to prefix each defined template with the name of the chart: {{ define "mychart.labels" }}