-
Notifications
You must be signed in to change notification settings - Fork 3
ansible k8s
To interact with the Kubernetes cluster using the kubernetes.core Ansible collection. The kubernetes.core collection is not included in the Ansible Core, which includes only the ansible.builtin collection.
You can verify the presence of the kubernetes.core Ansible collection using the following ansible-galaxy command:
$ ansible-galaxy collection list
The collection requires Python 3 installed in your system and the kubernetes Python library.
pip3 install PyYAML jsonpatch kubernetes
to interact with the Helm package manager, you also need the helm Python library.
pip3 install --use-pep517 helm
- manually
ansible-galaxy collection install kubernetes.core
- automatic: Require creating a requirements.yml file, a particular YAML file with the list of all the Ansible collections to install in the target system.
$ cat requirements.yml
---
collections:
- name: cloud.common
- name: kubernetes.core
ansible-galaxy install -r collections/requirements.yml
$ python3 -m venv <env-name>
$ source <env-name>/bin/activate
(<env-name>) $ pip3 install --upgrade pip setuptools
$ python -m ensurepip --upgrade # if pip not exist in instalation
(<env-name>) $ pip3 install PyYAML jsonpatch kubernetes
(<env-name>) $ pip3 install --use-pep517 helm
(<env-name>) $ pip3 freeze > requirements.txt
(<env-name>) $ ansible-galaxy collection install cloud.common
(<env-name>) $ ansible-galaxy collection install kubernetes.core
$ deactivate
After the manual or automatic Ansible collection installation process, your system is configured to interact with the kubernetes.core Ansible collection.
**Tip **the kubernetes.core ansible collection supports the ansible turbo mode. by default, this feature is disabled. it requires an additional cloud.common ansible collection installed. the AnsibleTurboModule class is inherited from the standard AnsibleModule class, which spawns a little python daemon, and the module logic runs inside this python daemon. the result is a drastic improvement in performance because python modules are loaded one time, and ansible can reuse an existing authenticated session. the daemon run in a single process exposes a local UniX socket for communication and kills itself after 15 seconds. You can set the ENABLE_TURBO_MODE variable to true or 1 using the environment statement in the play section of the ansible playbooks.
Once you have successfully installed the Python and Ansible dependencies, you need to execute the authentication to your cluster in order to execute API requests via the command-line tool.
For the Kubernetes cluster, you need the kubectl command-line utility. For the OpenShift cluster, the oc command-line utility is needed instead.
In a Kubernetes cluster, obtaining a valid authentication token requires the setup of a context for your cluster with the kubectrl command-line utility.
$ kubectl config set-credentials developer/foo.example.com –-username=developer --password=developer
-
set-credentials developer/foo.example.com
Creates a new credential named developer/foo.example.com (prefer to use the same as cluster name) Then you need to configure the cluster connection server:
$ kubectl config set-cluster foo.example.com –-insecure-skip-tls-verify=true –-server=https://foo.example.com
-
–-insecure-skip-tls-verify=true
: Skips TLS certificates validation -
–-server=https://foo.example.com
: Specifies the cluster URI
Then you can join in a context the cluster and credentials information:
$ kubectl config set-context default/foo.example.com/developer --user=developer/foo.example.com
--namespace=default --cluster=foo.example.com
- --set-context default/foo.example.com/developer: Creates a new context named default/foo.example.com/developer (you can specify any name)
- --user=developer/foo.example.com: Skips TLS certificates validation
- --namespace=default: Specifies the default namespace (in this case, default)
- --cluster=foo.example.com: Specifies the cluster configuration name
To obtain the OpenShift cluster using the oc command and specifying the username, password, cluster URI and the port
$ oc login -u developer -p developer https://api.openshift.example.com:6443
As an alternative to a username and password, you can authenticate using a token obtained via the OpenShift console. After successfully logging in to the web user interface, just choose Copy Login Command under your username in the top-right menu area. Once generated, the token is usable via the command-line oc command like the following:
$ oc login --token=sha256~xxxxxx --server=https://api.openshift.example.com:6443
After successfully authenticating, an authentication token is stored in the .kube directory under the config file on the local machine. You can customize the filename and path using the KUBECONFIG environment variable in the terminal.
$ export KUBECONFIG=/home/devops/.kube/config-developer
In the same way, you should instruct Ansible to read the environment variable by adding the following environment lines in the Play area of your Ansible Playbook:
[...]
environment:
KUBECONFIG: "/home/devops/.kube/config-developer"
[...]
$ oc login -u developer -p developer --server=https://api.openshift.example.com:6443
If needed add the additional certification authority file with the
--certificate-authority
parameter followed by the path on disk of the certificate.
You can acquire more information by increasing the verbosity level to six, the highest
available, with the --loglevel 6 parameter.
A successful login procedure can be verified using this OpenShift command:
$ oc whoami
To list all the namespaces can be easily achieved using Ansible Playbook with two tasks. The first task uses the Ansible query lookup plugin and invokes the Ansible kubernetes.core.k8s
module to interact with the Kubernetes API and return a list of all the namespaces in the cluster. The result of the Ansible lookup plugin is saved in the projects runtime variable using the ansible.builtin.set_fact
Ansible module. You can use this variable to perform further tasks, such as print the result onscreen using the ansible.builtin.debug
Ansible module.
$cat ns_list.yml
---
- name: k8s ns
hosts: all
tasks:
- name: list all namespaces
ansible.builtin.set_fact:
projects: "{{ query('kubernetes.core.k8s', api_version='v1',
kind='Namespace') }}"
- name: display the result
ansible.builtin.debug:
msg: "{{ projects }}"
The k8s
Ansible module is part of the kubernetes.core collection
; it’s very powerful and allows you to interact with the Kubernetes API like the kubectl
or oc
command-line utilities.
- The
name
parameter specifies the namespace that you want to create or verify if present. - The
api_version
parameter specifies the Kubernetes API version; the default is v1 for version 1. - The
kind
parameter specifies an object model, which is namespace for this use case. - The
state
parameter determines if an object should be created (the- present
option), updated (the- patched
option), or deleted (the- absent
option)
$cat ns_create.yml
---
- name: k8s ns
hosts: all
vars:
myproject: "ansible-examples"
tasks:
- name: namespace present
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ myproject }}"
state: present
The Inventory File
localhost ansible_connection=local
$ ansible-playbook -i inventory ns_create.yml
Verify
$ kubectl get namespace | grep ansible-examples
# openshift
$ oc projects | grep ansible-examples
It’s possible to perform the authentication step in the Ansible Playbook using the k8s_auth Ansible module, which is part of the kubernetes.core
Ansible collection. If you want to execute the authentication directly in the Ansible Playbook, you need to add a task before executing any command to the cluster. The get access token task connects to the host address with the specified username and password credentials and saves the result in the k8s_auth_results
runtime variable. The variable contains the token under the k8s_auth.api_key variable. Every time you use the k8s module, the extra parameter api_key is specified with the value of the token registered from the previous task.
$ cat ns_create_auth.yml
---
- name: k8s ns
hosts: all
vars:
myproject: "ansible-examples"
k8s_username: "kubeadmin"
k8s_password: "password"
k8s_host: "https://api.k8s:6443"
k8s_validate: true
tasks:
- name: get access token
kubernetes.core.k8s_auth:
username: "{{ k8s_username }}"
password: "{{ k8s_password }}"
host: "{{ k8s_host }}"
validate_certs: "{{ k8s_validate }}"
register: k8s_auth_results
- name: namespace present
kubernetes.core.k8s:
api_key: "{{ k8s_auth_results.k8s_auth.api_key }}"
api_version: v1
kind: Namespace
name: "{{ myproject }}"
state: present
Store any variables with sensitive data in an Ansible vault that’s encrypted and protected. For OpenShift authentication, use the community.okd.openshift_auth
Ansible module in the first task.
To achieved using Ansible Playbook with two tasks. The first task uses the Ansible query lookup plugin and invokes the Ansible kubernetes.core.k8s
module to interact with the Kubernetes API and return a list of all the namespaces in the cluster. The result of the Ansible lookup plugin is saved in the projects runtime variable using the ansible.builtin.set_fact
Ansible module. You can use this variable to perform further tasks, such as print the result onscreen using the ansible.builtin.debug
Ansible module.
$cat ns_list.yml
---
- name: k8s ns
hosts: all
tasks:
- name: list all namespaces
ansible.builtin.set_fact:
projects: "{{ query('kubernetes.core.k8s', api_version='v1',
kind='Namespace') }}"
- name: display the result
ansible.builtin.debug:
msg: "{{ projects }}"
To filter the output in a more synthetic and useful way, use the community.general.json_query
filter plugin and JMESPath
.
The pod definition is inside the Deployment YAML manifest document in the following example. The deployment.yaml document is a simple Deployment YAML file that deploys the latest tag of the container named nginx with two replicas listening on port TCP 3000 under the ansible-examples namespace
$cat deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: ansible-examples
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.22
imagePullPolicy: Always
ports:
- containerPort: 3000
Manually create deployment
$ kubectl apply -f deployment.yaml
$ kubectl get deployment nginx
$ kubectl get pods
$ kubectl get pods -n ansible-examples
cat deployment.yml
---
- name: k8s deployment
hosts: all
vars:
myproject: "ansible-examples"
tasks:
- name: namespace present
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ myproject }}"
state: present
- name: deployment present
kubernetes.core.k8s:
src: deployment.yaml
namespace: "{{ myproject }}"
state: present
Note You’ll receive a Kubernetes return code 404 error when the namespace is not present and a 403 error when the namespace is terminated.
Note When Kubernetes cannot download the Container image from the Container
registry; it reports the statuses ImagePullBackOff
and CrashLoopBackOff
.
these two statuses are connected, meaning that the Kubernetes cluster tried to
download the image from the registry but without success. the full workflow is
from ErrImagePull
, then ImagePullBackOff
, and then keep trying. the root
cause could be a misspelled image name or tag, a registry issue, a rate limit in
the registry (see free vs. pro accounts in Docker hub), a private registry without
specifying the secret for the pod, or simply a bit of bad luck about connectivity.
Ansible query lookup plugin invokes the kubernetes.core.k8s
module to interact with the Kubernetes API and return a list of all the deployments in the cluster. The query lookup plugin (sometimes shortened as q) invokes the kubernetes.core.k8s
module, specifying the deployment as the kind parameter and ansible-examples as a namespace parameter. You can list other Kubernetes objects by changing the kind parameter value or other namespaces by changing the namespace parameter value. The lookup plug returns the full JSON received from the Kubernetes cluster
You need to filter the output to extract a single element: the name of the deployment in the namespace. The most useful Ansible filter plugin to extract a single element is the Ansible json_query filter plugin. It’s specifically designed to select a single element or a data structure from a JSON output. Its full name is community.general.json_query
, which means that you need the additional community.general Ansible collection installed in your system. The community.general.json_query.
You can install the collection using the ansible-galaxy command-line tool:
$ ansible-galaxy collection install community.general
The Ansible module requires the Python jmespath library to be installed in the system:
$ pip3 install jmespath
With the Ansible json_query filter plugin, you can use the JMESPath popular query language for JSON. Considering the Kubernetes output, you apply the [].metadata.name filter pattern which means extracting for each element of the list (the star between brackets []) the key metadata, and displaying the subkey name specified using the dot notation. The result of the Ansible lookup plugin and the Ansible filter is saved in the deployments runtime variable using the ansible.builtin.set_fact
Ansible module. It’s displayed onscreen using the Ansible debug
module.
$cat deployment_list.yml
---
- name: k8s deployments
hosts: all
tasks:
- name: list all deployments
ansible.builtin.set_fact:
deployments: "{{ query('kubernetes.core.k8s', kind='Deployment',
namespace='ansible-examples') | community.general.json_query('[*].
metadata.name') }}"
- name: display the result
ansible.builtin.debug:
msg: "{{ deployments }}"
Output
TASK [display the result]
ok: [localhost] => {
"msg": [
"nginx"
]
}
Kubernetes file
$cat pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Ansible file
$cat pod.yml
---
- name: k8s pod
hosts: all
vars:
myproject: "ansible-examples"
tasks:
- name: namespace present
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ myproject }}"
state: present
- name: pod present
kubernetes.core.k8s:
src: pod.yaml
namespace: "{{ myproject }}"
state: present
Check
$ kubectl get pods -n ansible-examples
$ oc get pods -n ansible-examples
Secrets are not encrypted; they are encoded in base64 and stored in etcd on an encrypted volume. Note that when you decide to encrypt your data, you can’t return to the non-encrypted status.
Kubernetes file
$cat secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: ansible-examples
type: Opaque
data:
username: YWRtaW4=
password: bXlzdXBlcnNlY3JldHBhc3N3b3Jk
Ansible Playbook File
$cat secret.yml
---
- name: k8s secret
hosts: all
tasks:
- name: namespace present
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ myproject }}"
state: present
- name: secret present
kubernetes.core.k8s:
src: secret.yaml
state: present
You can create two data fields: username and password, with the value. The value must be encoded as base64.
$ kubectl get secrets -n ansible-examples
$ oc get secrets -n ansible-examples
The Kubernetes Service is continuously updated by the pod statuses with of healthy pods. The Kubernetes Service provides a constant IP address and port that act as an entry point to a group of pods. This information doesn’t change for as long as the service exists. Internal and external clients can reach your application running in a group of pods by connecting to the service IP and ports. These connections are routed to one of the pods behind the Kubernetes Service.
$cat service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: nginx-example
spec:
selector:
app: nginx-example
ports:
- protocol: TCP
port: 80
targetPort: 3000
$ kubectl apply -f service.yaml
$ kubectl get service nginx-example -n ansible-examples
$ oc get services -n ansible-examples
Ansible Playbook
---
- name: k8s service
hosts: all
vars:
myproject: "ansible-examples"
tasks:
- name: k8s service
kubernetes.core.k8s:
src: service.yaml
namespace: "{{ myproject }}"
state: present
Kubernetes uses five techniques to track pod status and direct traffic to the appropriate pods:
- ClusterIP: Accessible only internally within the cluster.
- NodePort: Exposes a static port (the NodePort) on each node’s IP that can be accessed from outside the cluster by the address NodeIP:NodePort. The NodePort service connects to a ClusterIP service that is created automatically for your NodePort.
- LoadBalancer: Exposes a load balancer externally. The LoadBalancer service connects to the NodePort and ClusterIP, automatically created.
- Ingress: Reduces the amount of load balancer for HTTP and HTTPS defining traffic routes.
- ExternalName: Maps to a DNS name. It returns a CNAME record with its value.
The simplest network configuration is the ClusterIP service type. This is accessible from within the cluster only. A ClusterIP service IP address is assigned from the cluster range. The ClusterIP service routes the traffic to the nodes. Behind the scene, each node runs a kube-proxy container for this task. The kube-proxy container creates the appropriate IPtables firewall rules to redirect the ClusterIP traffic to the appropriate Pod IP address. This type of service is used by frontend pods to redirect traffic to the backend pods or to act like load balancers. In the Kubernetes YAML for ClusterIP service, the most important field is the selector. It determines which pods serve as endpoints for this service.
Kubernetes file
cat ClusterIP.yaml
---
apiVersion: v1
kind: Service
metadata:
name: "nginx-example"
namespace: "mynamespace"
spec:
type: ClusterIP
selector:
app: "nginx"
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
The NodePortservice type is similar to a ClusterIP service, but it also opens a port on each node. Opening a port allows access to the service from inside the cluster (using the ClusterIP). External users can connect directly to the node on the NodePort.
A random port is opened on the local node for each service unless the nodeport property specifies a specific port. The kube-proxy container forwards traffic from the port to the service’s cluster IP and then to the pod that’s updating the IPtable rules.
---
apiVersion: v1
kind: Service
metadata:
name: "nginx-example"
namespace: "mynamespace"
spec:
type: NodePort
selector:
app: "nginx"
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
nodeport: 25000
The LoadBalancer service type extends the NodePort service type by adding a load balancer in front of all nodes. This means Kubernetes requests a load balancer and registers all the nodes. The load balancer doesn’t detect where the pods for a specific service are running. Consequently, the load balancer adds all worker nodes as backend instances. The Load Balancer service type uses the classic load balancers by default. This means there is a classic load balancer in front of all instances listening to your requests. The load balancer routes the requests to the nodes through an exposed port. You can also use the network load balancer instead of the classic load balancer. The classic load balancer processes the requests one by one when they arrive from the Internet. It then forwards the request to one of the Kubernetes instances on a specific port. When there is a service listening to a specific port, it acts like a second-layer load balancer for the backend pods that handle the requests.
$cat LoadBalancer.yaml
---
apiVersion: v1
kind: Service
metadata:
name: "nginx-example"
namespace: "mynamespace"
spec:
type: LoadBalancer
selector:
app: "nginx"
ports:
- name: http
port: 80
targetPort: 9376
You can reduce the number of load balancers using the Kubernetes ingress object. An ingress object exposes HTTP and HTTPS routes outside the cluster and routes traffic to your services according to defined traffic rules. Ingress objects use ingress controllers, which fulfill the ingress rules and requests (usually using a load balancer). Using the ingress objects and controllers, you can transition from one load balancing per service to one load balancer per ingress and route to multiple services. Traffic can be routed to the proper service using path-based routing. When using Kubernetes, you have many ingress controller options, for example, Envoy Controllers and NIGNX controllers. The load balancer picks the proper target group based on the path. The load balancer then forwards the request to one of the Kubernetes instances on the application ports. The service listens at a specific port and balances the requests to one of the pods of the application or service
$cat Ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "nginx-example"
namespace: "mynamespace"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- http:
paths:
- path: /webapp
backend:
service:
serviceName: webapp
servicePort: 9376
The ExternalName service type is used to connect to a resource outside of the cluster. For example, this could be a database or file system resource that is in the network outside the cluster. The pods that require access to the external resource service require access to the resource to connect to the resource service, which returns the external resource endpoints. This type of service is helpful when you decide to migrate the external resource to the Kubernetes cluster. When the resource is deployed in the cluster, you can update the service type to ClusterIP. The application can continue to use the same resource endpoint. The most important property in the YAML manifest is the externalName field, which specifies where the service maps to.
$cat ExternalName.yaml
---
apiVersion: v1
kind: Service
metadata:
name: "nginx-example"
namespace: "mynamespace"
spec:
type: ExternalName
externalName: mydb.example.com
Since Kubernetes version 1.21, PodDisruptionBudget (PDB) is a way to limit the number of concurrent disruptions that your application experiences, thus allowing for high availability. Meanwhile, it allows the cluster administrator to manage the nodes of the cluster by manually draining a node or preventing an upgrade from taking too many copies of the application offline. PDB allows you to specify and overprovision in order to guarantee the high availability of your service. PDB is different from auto-scaling because it overprovisions the application on purpose.
$ kubectl scale deployment nginx --replicas=3
$ kubectl get deployments
$ kubectl scale deployment nginx --replicas=1
With Ansible, you can scale a deployment, ReplicaSet, replication controller, and job using the kubernetes.core.k8s_scale Ansible module. The k8s_scale Ansible module is part of the kubernetes.core collection and it interacts with the Kubernetes API like the kubectl or oc command-line utilities.
$cat scale_up.yml
---
- name: k8s scale
hosts: all
vars:
myproject: "ansible-examples"
mydeployment: "nginx"
myreplica: 10
mytimeout: 120
tasks:
- name: scale Deployment
kubernetes.core.k8s_scale:
api_version: v1
kind: Deployment
name: "{{ mydeployment }}"
namespace: "{{ myproject }}"
replicas: "{{ myreplica }}"
wait_timeout: "{{ mytimeout }}"
The Ansible Playbook scale_up.yml
changes the replica count to ten of the deployment nginx in the namespace ansible-examples. The Ansible Playbook waits for a timeout maximum of 120 seconds (two minutes) for the operation to complete.
When the pod count is changed by a big number and the time is not sufficient, you might receive a failed status with the following message on the screen: Resource scaling timed out
. This simply means that the Kubernetes cluster is taking longer than
expected to execute the operation; it’s not a failure message. A more deep analysis of the
fatal error message, for example, reveals the operation in progress:
"message": "ReplicaSet \"nginx-689b466988\" is progressing."
- Horizontal scaling adds/remove pods of the same size capacity.
- Vertical scaling keeps the same number of pods, but you change the size of capacity. Kubernetes Cluster Autoscaler (CA) allocates the right amount of resources to guarantee that your application meets the demands of the traffic. The change is performed for your pods in the auto-scale group in your cluster in order to enable Horizontal Pod Autoscaler (HPA) or Vertical Pod Autoscaler (VPA).
Kubernetes Cluster Autoscaler can be deployed, but it requires permission to interact with your cluster. The Kubernetes Cluster Autoscaler needs a service account to interact with the auto-scaling group.
Name | Description |
---|---|
helm | Add, update, and delete Kubernetes packages using the package manager Helm |
helm_info | Obtain information about a Helm package deployed into the Kubernetes cluster |
helm_plugin | Add, update, and delete Helm plugins |
helm_plugin_info | Obtain information about your Helm plugins |
helm_plugin_info | Obtain information about your Helm plugins |
helm_repository | Add, update, and delete Helm repositories |
helm_template | Render Helm chart templates |
k8s | Interact with the Kubernetes (K8s) objects |
k8s_cluster_info | Obtain Kubernetes clusters, any APIs available, and their respective versions |
k8s_cp | Copy files and directories to and from a pod |
k8s_drain | Set drain, cordon, or uncordon for a node in the Kubernetes cluster |
k8s_exec | Execute a command in a pod |
k8s_info | Obtain information about Kubernetes objects |
k8s_json_patch | Apply JSON patch operations to existing Kubernetes objects |
k8s_log | Fetch logs from a Kubernetes resource |
K8s_rollback | Roll back Kubernetes object deployments and DaemonSets |
k8s_scale | Set the scale parameter for a deployment, ReplicaSet, replication controller, or job |
k8s_service | Add, update, and delete services on Kubernetes |
k8s_taint | Set the taint attribute for a node in a Kubernetes cluster |
kubectl | Execute commands in Pods on a Kubernetes cluster (connection plugin) |
k8s_config_resource_namer | Generate resource names for the given resource of type ConfigMap, Secret (filter plugin) |
k8s | Fetch containers and services for one or more Kubernetes clusters and group by cluster name, namespace, namespace_services, namespace_pods, and labels (inventory plugin) |
k8s | Provide access to the full range of Kubernetes APIs (lookup plugin) |
kustomize | Use the kustomization.yaml file to build a set of Kubernetes resources (lookup plugin) |
Test