-
Notifications
You must be signed in to change notification settings - Fork 1
rancher
- Rancher
Rancher is a tool built to simplify Docker orchestration and management. In Rancher, everything (like containers, networks or images ) is an API resource with a process lifecycle. Containers, images, networks, and accounts are all API resources with their own process li
Instead of building a single monstrous, monolithic application, the idea is to split your application into set of smaller, interconnected services.
The rise of microservices has been a remarkable advancement in application development and deployment. With microservices, an application is developed, or refactored, into separate services that “speak” to one another in a well-defined way – via APIs, for instance. Each microservice is self-contained, each maintains its own data store (which has significant implications), and each can be updated independently of others.
Moving to a microservices-based approach makes app development faster and easier to manage, requiring fewer people to implement more new features. Changes can be made and deployed faster and easier. An application designed as a collection of microservices is easier to run on multiple servers with load balancing, making it easy to handle demand spikes and steady increases in demand over time, while reducing downtime caused by hardware or software problems.
The term API stands for Application Programming Interface. The term can be used to describe the features of a library, or how to interact with it. Your favorite library may have "API Documentation" which documents which functions are available, how you call them, which arguments are required, etc.
However, these days, when people refer to an API they are most likely referring to an HTTP API, which can be a way of sharing application data over the internet.
For example, let's say we have an application that allows you to view,create, edit, and delete widgets. We could create an HTTP API that allows you to perform these functions:
http://example.com/view_widgets
http://example.com/create_new_widget?name=Widgetizer
http://example.com/update_widget?id=123&name=Foo
http://example.com/delete_widget?id=123
A problem has started to arise when everyone starts implementing their own APIs. Without a standard way of naming URLs, you always have to refer to the documentation to understand how the API works. One API might have a URL like /view_widgets
whereas another API might use /widget
. REST comes to rescue us from this mess.
REST stands for Representational State Transfer. This is a term to describe a standard way of creating HTTP APIs.
There are technically 8 different HTTP methods:
GET
POST
PUT
DELETE
OPTIONS
HEAD
TRACE
CONNECT
Let's look at a few examples of what makes an API RESTful. Using our widgets example again...
If we wanted to view all widgets, the URL would look like this:
GET http://example.com/widgets
Create a new widget by posting the data:
POST http://example.com/widgets
Data: name = Foobar
To view a single widget we "get" it by specifying that widget's id:
GET http://example.com/widgets/123
Update that widget by "putting" the new data:
PUT http://example.com/widgets/123
Data: name = New name color = blue
Delete that widget:
DELETE http://example.com/widgets/123
In the above examples, REST URLs use a consistent naming scheme. When interacting with an API, we are almost always manipulating some sort of object. In our examples, this is a Widget. In REST terminology, this is called a Resource. The first part of the URL is always the plural form of the resource:
/widgets
This is always used when referring to this collection of resources ("list all" and "add one" actions). When working with a specific resource, we add the ID to the URL:
/widgets/123
Another important part of REST is responding with the correct status code for the type of request that was made. If you're new to HTTP status codes, heres a quick summary. When you make an HTTP request, the server will respond with a code which corresponds to whether or not the request was successful and how the client should proceed. There are four different levels of codes:
2xx = Success
3xx = Redirect
4xx = User error
5xx = Server error
Here's a list of the most important status codes: Success codes:
200 - OK (the default)
201 - Created
202 - Accepted (often used for delete requests)
User error codes:
400 - Bad Request (generic user error/bad data)
401 - Unauthorized (this area requires you to log in)
404 - Not Found (bad URL)
405 - Method Not Allowed (wrong HTTP method)
409 - Conflict (i.e. trying to create the same resource with a PUT request)
JSON has quickly become the format of choice for REST APIs. It has a lightweight, readable syntax that can be easily manipulated. So when a user of our API makes a request and specifies JSON as the format they would prefer:
GET /widgets
Accept: application/json
...our API will return an array of widgets formatted as JSON:
[
{
id: 123,
name: 'Simple Widget'
},
{
id: 456,
name: 'My other widget'
}
]
We use token based authentication. The user logs in with their username and password and the application responds with a unique token that the user can use for future requests. This token can be passed onto the application so that the user can revoke that token later if they choose to deny that application further access.
There is a standard way of doing this that has become very popular. It's called OAuth. Specifically, version 2 of the OAuth standard.
For most microservices‑based applications, it makes sense to implement an API Gateway, which acts as a single entry point into a system. The API Gateway is responsible for request routing, composition, and protocol translation. It provides each of the application's clients with a custom API.
Each microservice would have a public endpoint:
https://serviceName.api.company.name
This URL would map to the microservice’s load balancer, which distributes requests across the available instances.
The Microservices Architecture pattern significantly impacts the relationship between the application and the database. Rather than sharing a single database schema with other services, each service has its own database schema.
The API Gateway needs to know the location (IP address and port) of each microservice with which it communicates. Application services have dynamically assigned locations. Also, the set of instances of a service changes dynamically because of autoscaling and upgrades. Consequently, the API Gateway, like any other service client in the system, needs to use the system’s service discovery mechanism.
Below is an example of a service definition with image emilevauge/whoamI in Rancher:
# kubectl -n default describe service myosinfo
Name: myosinfo
Namespace: default
Labels: <none>
Annotations: field.cattle.io/targetWorkloadIds=["deployment:default:myosinfo"]
targetWorkloadIdNoop=true
Selector: workload.user.cattle.io/workloadselector=deployment-default-myosinfo
Type: ClusterIP
IP: None
Port: default 42/TCP
TargetPort: 42/TCP
Endpoints: 10.42.0.50:42
Session Affinity: None
Events: <none>
NOTES:
- This describes a Service object named “myosinfo” which targets TCP port 42 on any Pod with the
workloadID_MyService=true
selector. - This Service will also be assigned an IP address, called the cluster IP, which is used by the service proxies.
- Service map incoming port 42 (in host) to 42 targetPort (in container).
- Endpoint is
10.42.0.50:42
In Rancher, the service is also called a DNS Record and can be found in the Service Discovery. In our example above, a new service called myosinfo has been created in the service discovery menu.
The API Gateway must be able to query the service registry, which is a database of all microservice instances and their locations. The network location of a service instance is registered with the service registry when it starts up. It is removed from the service registry when the instance terminates.
A service registry needs to be highly available and up to date. Clients can cache network locations obtained from the service registry. However, that information eventually becomes out of date and clients become unable to discover service instances. Consequently,a service registry consists of a cluster of servers that use a replication protocol to maintain consistency.
etcd is a highly available, distributed, consistent, key-value store that is used for shared configuration and service discovery. One notable project that use etcd is Kubernetes.
The client makes a request to a service via a load balancer. The load balancer queries the service registry and routes each request to an available service instance. The AWS Elastic Load Balancer (ELB) is an example of a server-side discovery router. Some deployment environments such as Kubernetes run a proxy on each host in the cluster. The proxy plays the role of a server-side discovery load balancer. In order to make a request to a service, a client routes the request via the proxy using the host’s IP address and the service’s assigned port. The proxy then transparently forwards the request to an available service instance running somewhere in the cluster.
Role-Based Access Control (“RBAC”) uses the “rbac.authorization.k8s.io” API group to drive authorization decisions, allowing admins to dynamically configure policies through the Kubernetes API. It is activated on our cluster and we need to pass the following option to helm
when deploying from catalogue.
rbac.create=true
For example, in case of nginx ingress controller
, this parameter shall be set in the configmap
menu.
Fllow this guide to configure rbac in the cluster. The two common use cases are :
- create a user with limited access
- enable Helm
here is a script to setup Rancher server with Nginx running.
We created a DNS A record, pointing to the IP addresses of our Linux host, dahlia
, for rancher.thetradinghall.com hosted at Hurricane Electric.
- Verify
# nslookup rancher.thetradinghall.com
Server: 192.168.1.254 <-- infomaniak server
Address: 192.168.1.254#53
Non-authoritative answer:
Name: rancher.thetradinghall.com
Address: 83.166.150.131
NOTE:
- Rancher will not have access control configured and your UI and API will be available to anyone who has access to your IP. We recommend configuring access control.
- Most issues may come from misconfigured iptables.
Access Control is how Rancher limits the users who have the access permissions to your Rancher instance. By default, Access Control is not configured. This means anyone who has the IP address of your Rancher instance will be able to use it and access the API.
The first account that authenticates with Rancher will become the admin of the account
When you first log in to Rancher, you are automatically in a Default environment. The default cattle environment template has been selected for this environment to launch infrastructure services. These infrastructure services are required to be launched to take advantage of Rancher’s benefits like dns, metadata, networking, and health checks. These infrastructure stacks can be found in Stacks -> Infrastructure. These stacks will be in an unhealthy state until a host is added into Rancher. After adding a host, it is recommended to wait until all the infrastructure stacks are active before adding services.
When starting Rancher, each environment is based on an environment template. An environment template allows users to define a different combination of infrastructure services to be deployed. The infrastructure services includes but not limited to container orchestration (i.e. cattle, kubernetes, mesos, swarm), networking, rancher services, i.e healthcheck, dns, metadata, scheduling, service discovery and storage.
Rancher supports grouping resources into multiple environments. Each environment starts with a set of infrastructure services defined by the environment template used to create the environment. All hosts and any Rancher resources, such as containers, infrastructure services, and so on are created in and belong to an environment.
An environment template allows users to define a different combination of infrastructure services to be deployed. The infrastructure services includes but not limited to container orchestration (i.e. cattle, kubernetes, mesos, swarm), networking, rancher services (i.e healthcheck, dns, metadata, scheduling, service discovery and storage.
High-availability clusters (also known as HA clusters or fail-over clusters) are groups of computers that support server applications that can be reliably utilized with a minimum amount of down-time. They operate by using high availability software to harness redundant computers in groups or clusters that provide continued service when system components fail.
Below instructions creates a new Kubernetes cluster that’s dedicated to running Rancher in a high-availability (HA) configuration.
We have:
-
1 VM dedicated to Rancher server and Nginx. It is not part of our cluster
-
3 VM dedicated to etcd:
-
2 VM dedicated to controller
-
1 VM dedicated to worker
Follow these instructions to provision the Linux hosts. Pur VM run Fedora.
Most important is to start with a clean and fresh new install, no old docker images or settings. Iptables
and SELinux
are the two more sensitive materials.
A Deployment controller provides declarative updates for Pods and ReplicaSets. In next section we will work with a concrete case, the Hello-world API.
Kubernetes uses a .yaml file for deployment loading informations into the cluster. Rancher UI allow to import/export/modify these files.
1- Under the Workloads menu, click Import YAML
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: hello-world-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: hello-world
spec:
containers:
- image: "gokul93/hello-world:latest"
imagePullPolicy: Always
name: hello-world-container
ports:
- containerPort: 8080
This will create a new Hello-world-deployment
workload.
2- Under the Service Discovery menu, click Import YAML
apiVersion: v1
kind: Service
metadata:
name: hello-world-svc
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: hello-world
type: NodePort
NOTE:
Under the Service Discovery menu, when adding a Record, a good choice is to check The set of pods which match a selector
and when clicking the Add selector
button, add a Label named app with the app name as Value.
In this case, nothing will appear in the GUI dashboard
We run the following command with the hello-world.yaml
:
# kubectl apply -f hello-world.yaml
.
Another solution is to call an URL:
# kubectl create -f https://github.com/gokulchandra/k8s-ingress-setup/blob/master/hello-world.yaml
kubectl get deployments
list all deployments (or workload).
Once the app is deployed, watch its status. Example for Hello-world:
# kubectl get service hello-world-svc --namespace test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world-svc NodePort 10.43.236.218 <none> 8080:30767/TCP 24m
Helm is a tool for managing Kubernetes charts. Charts are packages of pre-configured Kubernetes resources. Once Helm repo is activated, find apps in the Catalog Rancher UI section. Think of it like apt/yum/homebrew for Kubernetes. It also provides other useful features, such as templating and dependency management.
Helm uses a packaging format called charts.
To get all .yaml files for a package, do the following:
# helm search traefik
NAME VERSION DESCRIPTION
stable/traefik 1.29.1 A Traefik based Kubernetes ingress controller w...
# helm fetch stable/traefik
....
# ls
traefik-1.29.1.tgz
Once untar:
# tree
.
├── Chart.yaml
├── README.md
├── templates
│ ├── acme-pvc.yaml
│ ├── configmap.yaml
│ ├── dashboard-ingress.yaml
│ ├── dashboard-service.yaml
│ ├── default-cert-secret.yaml
│ ├── deployment.yaml
│ ├── dns-provider-secret.yaml
│ ├── _helpers.tpl
│ ├── NOTES.txt
│ ├── poddisruptionbudget.yaml
│ ├── rbac.yaml
│ └── service.yaml
└── values.yaml
Helm has two parts: a client called Helm and a service named Tiller running on the Kubernetes cluster.
Tiller uses configmaps to store information for each deployment. To find out what it actually stores, we can run the following command:
# kubectl get configmaps traefik -n ckuster-management -o yaml
apiVersion: v1
data:
traefik.toml: |
# traefik.toml
logLevel = "INFO"
defaultEntryPoints = ["http", "httpn"]
[entryPoints]
[entryPoints.http]
address = ":80"
compress = true
[entryPoints.httpn]
address = ":8880"
compress = true
[kubernetes]
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"traefik.toml":"# traefik.toml\nlogLevel = \"INFO\"\ndefaultEntryPoints = [\"http\", \"httpn\"]\n[entryPoints]\n [entryPoints.http]\n address = \":80\"\n compress = true\n [entryPoints.httpn]\n address = \":8880\"\n compress = true\n[kubernetes]\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"traefik","chart":"traefik-1.28.1","heritage":"Tiller","release":"traefik"},"name":"traefik","namespace":"cluster-management"}}
creationTimestamp: 2018-05-03T15:26:07Z
labels:
app: traefik
chart: traefik-1.28.1
heritage: Tiller
io.cattle.field/appId: traefik
release: traefik
name: traefik
namespace: cluster-management
resourceVersion: "304498"
selfLink: /api/v1/namespaces/cluster-management/configmaps/traefik
uid: 4d299f85-4ee6-11e8-a2b1-fa163e603fa2
Configmaps themselves are stored in etcd. To remove the Helm release completely, use helm delete --purge Myapp
.
Secrets (for confidential data) and ConfigMaps (for non-confidential data).
# kubectl get secret
and # kubectl get configmap
.
Kubernetes let’s you mount ConfigMaps and Secrets as files. Unlike environment variables, if these files change the new files will be pushed to the running pods without needing a restart, so they are a lot more powerful. You can also map multiple files to a single ConfigMap or Secret, and mount them all at once as a directory!
We need a TSL Certificate. A self-signed certificate may be appropriate if you do not have a domain name associated with your server and for instances where the encrypted web interface is not user-facing. If you do have a domain name, in many cases it is better to use a CA-signed certificate (like Let's encrypt).
Note: A self-signed certificate will encrypt communication between your server and any clients. However, because it is not signed by any of the trusted certificate authorities included with web browsers, we cannot use the certificate to validate the identity of our server automatically.
For practical and testing purpose, we will create our own self-signed certificate.
TLS/SSL works by using a combination of a public certificate and a private key. The SSL key is kept secret on the server. It is used to encrypt content sent to clients. The SSL certificate is publicly shared with anyone requesting the content. It can be used to decrypt the content signed by the associated SSL key.
Here are two links which explain in detail: here and there.
Creating OpenSSL x509 certificates
NOTE : the key need to be PEM-encoded PKCS#1 to avoid this bug. In short, there are two formats for keys: "BEGIN RSA PUBLIC KEY" is PKCS#1
"BEGIN PUBLIC KEY" is PKCS#8
This stackoverflow thread explains everything and how to convert one format to the other one.
Build steps:
1- create needed directory
# mkdir /etc/ssl/private
# chmod 700 /etc/ssl/private
2- create a self-signed key and certificate pair with OpenSSL. Below command options will create both a key file and a certificate
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
3- convert the private key from PKCS#8 to PKCS#1 (not needed when bug is corrected)
openssl rsa -in /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/private/nginx-selfsigned-rsa.key
4- create a strong Diffie-Hellman group
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
5- create secret with CLI
# kubectl --namespace=test create secret tls hello-world-cert --key /etc/ssl/private/nginx-selfsigned.key --cert /etc/ssl/certs/nginx-selfsigned.crt
# kubectl --namespace=test create secret generic hello-world-dhparam --from-file=/etc/ssl/certs/dhparam.pem
6- verify
# kubectl --namespace=test get secret hello-world-cert
NAME TYPE DATA AGE
hello-world-cert kubernetes.io/tls 2 27s
# kubectl --namespace=test get secret hello-world-dhparam
NAME TYPE DATA AGE
hello-world-dhparam Opaque 1 2
cert-manager is a Kubernetes add-on to automate the management and issuance of TLS certificates from various issuing sources.
Rancher use the NGINX controller built around the Kubernetes Ingress resource that uses ConfigMap to store the NGINX configuration. Use Helm deployment via the catalogue menu.
NOTE: configurable parameters rbac.create=TRUE
After the controller is deployed, we have:
1- one workload
2- one service
3- a configmap
We need to create a secret with a TLS certificate and a key for the default server in NGINX, as described above.
Default backend is a special service endpoint which will handle the traffic that arrives at the ingress and does not match any of the configured routes in the ingress route map. It is the default service that Nginx falls backs to if it cannot route a request successfully.
Basically a default backend exposes two URLs:
- serves a 404 page at /
- serves 200 on a /healthz
By default, all environment templates have the Network Services enabled.
- list nodes with their public IP
# kubectl -n kube-system get pods --output=wide
NAME READY STATUS RESTARTS AGE IP NODE
canal-265pb 3/3 Running 0 5d 10.52.11.177 etcd1
canal-57ms9 3/3 Running 0 5d 10.52.11.179 etcd2
canal-jkkvl 3/3 Running 0 5d 10.52.11.193 control1
canal-nzzss 3/3 Running 0 5d 10.52.11.191 etcd3
canal-wcth8 3/3 Running 1 5d 10.52.16.22 worker
canal-zwm5c 3/3 Running 0 5d 10.52.11.199 control2
- run
ping
command from one pod
# kubectl -n kube-system exec -it canal-265pb -- ping -c 3 10.52.11.199
Defaulting container name to calico-node.
Use 'kubectl describe pod/canal-265pb -n kube-system' to see all of the containers in this pod.
PING 10.52.11.199 (10.52.11.199) 56(84) bytes of data.
64 bytes from 10.52.11.199: icmp_seq=1 ttl=64 time=0.905 ms
64 bytes from 10.52.11.199: icmp_seq=2 ttl=64 time=0.541 ms
64 bytes from 10.52.11.199: icmp_seq=3 ttl=64 time=0.468 ms
--- 10.52.11.199 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2046ms
rtt min/avg/max/mdev = 0.468/0.638/0.905/0.191 ms
We see above that pods can communicate with their external IP.
Rancher implements a CNIframework, which provides the ability to select different network drivers within Rancher. To leverage the CNI framework, an environment is required to use the Network Services infrastructure service deployed. These services host internal DNS server and manage routing to published ports on the host (via iptables).
Any container launched in managed network will have the Rancher managed IP (default subnet: 10.42.0.0/16). By default, all containers within the same environment are reachable via the managed network. All containers in the managed network are able to communicate with each other regardless of which host the container was deployed on. Most of Rancher’s features, such as load balancers or DNS service, require the service to be in the managed network.
Platforms like Kubernetes assume that each container (pod) has a unique, routable IP inside the cluster. The advantage of this model is that it removes the port mapping complexities that come from sharing a single host IP.
Flannel is responsible for providing a layer 3 IPv4 network between multiple nodes in a cluster. Flannel does not control how containers are networked to the host, only how the traffic is transported between hosts. However, flannel does provide a CNI plugin for Kubernetes and a guidance on integrating with Docker.
https://rancher.com/docs/rancher/v2.x/en/installation/load-balancing-config/
Traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
All below elements are writen on a traefik.yml file
1- Ingress controller deployment
2- one service before Traefik
3- one service for Traefik web UI
4- one configmap
Rancher uses etcd as datastore. The persistent data will be stored in /var/lib/rancher
inside the container. You can bind mount a host volume to this location to preserve data on the host it is running on.
Command to use:
docker run -d -p 80:80 -p 443:443 \
-v /host/rancher:/var/lib/rancher \
rancher/rancher
List existing volumes:
# docker volume ls
DRIVER VOLUME NAME
local 042d1a6dd6...
Inspect volume:
# docker volume inspect VolID
[
{
"CreatedAt": "2018-05-02T12:19:12Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/VolID/_data",
"Name": "VolID",
"Options": {},
"Scope": "local"
}
When mounting folders and files from host they will override the ones in the container. As example, etc/Myconf
in host bind/mounted on /etc/Myconf
on container will then make the container use the configuration file from the host. Thus it allow to write configuration directly from the host.
-
open the volume menu
-
Volune name: VolID
-
Path on the node: /Path/on/host
-
Mount point: /Path/on/container
-
Subpath in Volume: ??
ISCSI is an acronym for Internet Small Computer Systems Interface, an Internet Protocol (IP)-based storage networking standard for linking data storage facilities. It provides block-level access to storage devices by carrying SCSI commands over a TCP/IP network.
Hosts are the most basic unit of resource within Rancher and is represented as any Linux server, virtual or physical.
All consoles except the default (busybox) console are persistent. Persistent console means that the console container will remain the same and preserves changes made to its filesystem across reboots. If a container is deleted/rebuilt, state in the console will be lost except what is in the persisted directories.
/home
/opt
/var/lib/docker
/var/lib/rancher
To see if the setup is correct, here is a little app to run. This app is called whoamI and its image can be found here.
-
In rancher UI workload, add
- name: mywho
- docker image: jwilder/whoami
- port mapping: Nodes running a pod Source: 8080 Container: 8000
-
On container
- run
$ curl $(hostname --all-ip-addresses | awk '{print $1}'):8080
. It will returnI'm mywho-855976b9b5-ddx8g
(in our case,mywho-855976b9b5-ddx8g
is the application name) - run
curl localhost:8080
, same output - on a browser, enter
public IP:8080
, same output
- run
NOTE: you can veryify no service has been created for this app.