Skip to content

Kubernetes Operator to manage Resource in OTC (with Ansible). Showcase for ECS and RDS.

Notifications You must be signed in to change notification settings

eumel8/ansible-otc-operator

Repository files navigation

K8S Operator for ansible-otc

This is the Ansible based K8S Operator for ansible-otc. Following the Operator Quickstart it's easy to generate a Ansible based Operator for Kubernetes:

operator-sdk new ansible-otc-operator --git-init --type ansible --generate-playbook --kind OtcEcs --api-version ansible-otc.net/v1

This will generate most of the content in this repo. The approach is to run Ansible inside Kubernetes and provide resource definitions in Kubernetes API back to the user like this:

kubectl -n ansible get otcecs
NAME       AGE
otcecs     28m
otcecs01   110s

in detail:

# kubectl -n ansible get otcecs otcecs -o yaml
apiVersion: ansible-otc.net/v1
kind: OtcEcs
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"ansible-otc.net/v1","kind":"OtcEcs","metadata":{"annotations":{},"name":"otcecs","namespace":"ansible"},"spec":{"availability_zone":"eu-de-01","ecs_adminkey":"ansible-otc-operator","ecs_name":"ansible-otc-operator","ecs_publicip":"0.0.0.0","ecs_ram":"2048","ecs_vcpus":"2","ecs_volumesize":12,"ecs_volumetype":"SATA","eip_bandwidth_name":"ansible-otc-operator","eip_bandwidth_size":"500","evs_availability_zone":"eu-de-01","evs_multiattach":false,"evs_scsi":false,"image_name":"Standard_Ubuntu_18.04_latest","keypair_file":"/key-file/ansible-operator.key.pub","localaction":"list","public_ip_address":"0.0.0.0","secgroups":["ansible-otc-operator"],"securitygroups":{"ansible-otc-operator":["ingress;IPv4;0;;;0.0.0.0/0","egress;IPv4;0;;;0.0.0.0/0"]},"subnet_dhcp_enable":"true","subnet_gateway":"10.0.0.1","subnet_name":"ansible-otc-operator","subnet_net":"10.0.0.0/26","subnet_primary_dns":"100.125.4.25","subnet_secondary_dns":"8.8.8.8","vpc_name":"ansible-otc-operator","vpc_net":"10.0.0.0/16"}}
  creationTimestamp: "2020-07-08T21:15:22Z"
  generation: 1
  managedFields:
  - apiVersion: ansible-otc.net/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        .: {}
        f:availability_zone: {}
        f:ecs_adminkey: {}
        f:ecs_name: {}
        f:ecs_publicip: {}
        f:ecs_ram: {}
        f:ecs_vcpus: {}
        f:ecs_volumesize: {}
        f:ecs_volumetype: {}
        f:eip_bandwidth_name: {}
        f:eip_bandwidth_size: {}
        f:evs_availability_zone: {}
        f:evs_multiattach: {}
        f:evs_scsi: {}
        f:image_name: {}
        f:keypair_file: {}
        f:localaction: {}
        f:public_ip_address: {}
        f:secgroups: {}
        f:securitygroups:
          .: {}
          f:ansible-otc-operator: {}
        f:subnet_dhcp_enable: {}
        f:subnet_gateway: {}
        f:subnet_name: {}
        f:subnet_net: {}
        f:subnet_primary_dns: {}
        f:subnet_secondary_dns: {}
        f:vpc_name: {}
        f:vpc_net: {}
    manager: kubectl
    operation: Update
    time: "2020-07-08T21:15:22Z"
  - apiVersion: ansible-otc.net/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        .: {}
        f:conditions: {}
    manager: ansible-operator
    operation: Update
    time: "2020-07-08T21:15:41Z"
  name: otcecs
  namespace: ansible
  resourceVersion: "2608884"
  selfLink: /apis/ansible-otc.net/v1/namespaces/ansible/otcecs/otcecs
  uid: f50857be-d274-46f4-a50c-38599488ccd9
spec:
  availability_zone: eu-de-01
  ecs_adminkey: ansible-otc-operator
  ecs_name: ansible-otc-operator
  ecs_publicip: 0.0.0.0
  ecs_ram: "2048"
  ecs_vcpus: "2"
  ecs_volumesize: 12
  ecs_volumetype: SATA
  eip_bandwidth_name: ansible-otc-operator
  eip_bandwidth_size: "500"
  evs_availability_zone: eu-de-01
  evs_multiattach: false
  evs_scsi: false
  image_name: Standard_Ubuntu_18.04_latest
  keypair_file: /key-file/ansible-operator.key.pub
  localaction: list
  public_ip_address: 0.0.0.0
  secgroups:
  - ansible-otc-operator
  securitygroups:
    ansible-otc-operator:
    - ingress;IPv4;0;;;0.0.0.0/0
    - egress;IPv4;0;;;0.0.0.0/0
  subnet_dhcp_enable: "true"
  subnet_gateway: 10.0.0.1
  subnet_name: ansible-otc-operator
  subnet_net: 10.0.0.0/26
  subnet_primary_dns: 100.125.4.25
  subnet_secondary_dns: 8.8.8.8
  vpc_name: ansible-otc-operator
  vpc_net: 10.0.0.0/16
status:
  conditions:
  - ansibleResult:
      changed: 0
      completion: 2020-07-08T21:15:41.456689
      failures: 0
      ok: 25
      skipped: 9
    lastTransitionTime: "2020-07-08T21:15:33Z"
    message: Awaiting next reconciliation
    reason: Successful
    status: "True"
    type: Running

This is an example how to deploy an ECS in OTC with all required resources.

ansible-otc-operator.png

CRDs are expand to the services

  • vpc

  • subnet

  • secgroup

  • eip

  • keypair

  • ecs

  • rds

Configuration variables like in otc_vpc, otc_ecs, and so on.

Prerequisites:

Generate a ssh key pair for upload to OTC and inject in the ECS instance

ssh-keygen -q -N '' -f ansible-operator.key

Quickstart:

Connect to a Kubernetes Cluster and create a namespace

kubectl create namespace ansible

Create a secret with OTC credentials

kubectl create secret generic cloud-credentials -n ansible \
  --from-literal=OS_PROJECT_NAME=eu-de \
  --from-literal=OS_REGION_NAME=eu-de \
  --from-literal=OS_AUTH_URL=https://iam.eu-de.otc.t-systems.com:443/v3 \
  --from-literal=OS_IDENTITY_API_VERSION=3 \
  --from-literal=OS_USER_DOMAIN_NAME=OTC-EU-DE-xxxxxxxxxxxxxxxxx \
  --from-literal=OS_USERNAME=xxxxxxx \
  --from-literal=OS_PASSWORD=xxxxxxx

Create a secret with the ssh public key

kubectl -n ansible create secret generic key-file --from-file=ansible-operator.key.pub=./ansible-operator.key.pub

Apply the Custom Resource definitions

kubectl -n ansible apply -f deploy/crds/*crd.yaml
kubectl -n ansible apply -f deploy/crds/*cr.yaml

Apply the Operators:

kubectl -n ansible apply -f deploy/

Check installation

# kubectl -n ansible get all
NAME                                                READY   STATUS    RESTARTS   AGE
pod/ansible-otc-operator-secgroup-9f46996d6-s7shc   1/1     Running   0          46m
pod/ansible-otc-operator-keypair-69fdf58f5b-mw2vx   1/1     Running   0          46m
pod/ansible-otc-operator-subnet-7f8777b6-4dx8b      1/1     Running   0          46m
pod/ansible-otc-operator-eip-758df7bf4d-g8kvf       1/1     Running   0          46m
pod/ansible-otc-operator-vpc-55f8dbd9d4-7h9bz       1/1     Running   0          46m
pod/ansible-otc-operator-ecs-57b4bb87f7-82lsv       1/1     Running   0          30m

NAME                                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
service/ansible-otc-operatoro-ecs-metrics       ClusterIP   10.43.138.229   <none>        8383/TCP,8686/TCP   46m
service/ansible-otc-operator-subnet-metrics     ClusterIP   10.43.70.120    <none>        8383/TCP,8686/TCP   46m
service/ansible-otc-operator-secgroup-metrics   ClusterIP   10.43.211.139   <none>        8383/TCP,8686/TCP   46m
service/ansible-otc-operator-eip-metrics        ClusterIP   10.43.131.11    <none>        8383/TCP,8686/TCP   46m
service/ansible-otc-operator-keypair-metrics    ClusterIP   10.43.251.77    <none>        8383/TCP,8686/TCP   46m
service/ansible-otc-operator-vpc-metrics        ClusterIP   10.43.6.86      <none>        8383/TCP,8686/TCP   46m

NAME                                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ansible-otc-operator-secgroup   1/1     1            1           46m
deployment.apps/ansible-otc-operator-keypair    1/1     1            1           46m
deployment.apps/ansible-otc-operator-subnet     1/1     1            1           46m
deployment.apps/ansible-otc-operator-eip        1/1     1            1           46m
deployment.apps/ansible-otc-operator-vpc        1/1     1            1           46m
deployment.apps/ansible-otc-operator-ecs        1/1     1            1           46m

NAME                                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/ansible-otc-operator-secgroup-9f46996d6   1         1         1       46m
replicaset.apps/ansible-otc-operator-keypair-69fdf58f5b   1         1         1       46m
replicaset.apps/ansible-otc-operator-subnet-7f8777b6      1         1         1       46m
replicaset.apps/ansible-otc-operator-eip-758df7bf4d       1         1         1       46m
replicaset.apps/ansible-otc-operator-vpc-55f8dbd9d4       1         1         1       46m
replicaset.apps/ansible-otc-operator-ecs-57b4bb87f7       1         1         1       46m

The first job is already runnung in the ansible-otc-operator POD. It's a simple list of resource, so you can check connectivity. Normaly a description of the resources will made in the CRD, so you have a kind of API description.

Next is a full example to deploy a ECS. This will require VPC,Subnet,Secgroup, Keypair, and EIP. Adjust in example/ecs.yaml and ensure the resources are created before. Here only as example create of EIP:

kubectl -n ansible apply -f examples/eip.yaml

You can show the created resource. A kubectl -n ansible patch otceips.otceip.ansible-otc.net otceip -p '{"spec": {"localaction": "show"}}' will not work due the missing patch possibilty, so you can kubectl -n ansible edit otceips.otceip.ansible-otc.net otceip and change localaction: create to localaction: show. The output is in the corresponding EIP Operator POD. The ip-address can you put in public_ip value of examples/ecs.yaml to use this ip-address in the next deployment:

kubectl -n ansible apply -f examples/ecs.yaml

You can check the output in ECS Operator POD log

Login into ECS

ssh -i ansible-operator.key ubuntu@<EIP>

That's all. No, that's not all. This is only an example how to use the Operator with Ansible in Open Telekom Cloud context. With ECS creation we're on level 1 of 5 on Operator Capability Levels. ansible-otc is task-driven and has no object-level approach. There is no app livecycle, monitoring or autopilot zp reach level 5. But you've got an insight and idea, hopefully :)