github.com/fabianvf/ocp-release-operator-sdk@v0.0.0-20190426141702-57620ee2f090/doc/ansible/user-guide.md (about)

     1  # User Guide
     2  
     3  This guide walks through an example of building a simple memcached-operator
     4  powered by Ansible using tools and libraries provided by the Operator SDK.
     5  
     6  ## Prerequisites
     7  
     8  - [git][git_tool]
     9  - [docker][docker_tool] version 17.03+.
    10  - [kubectl][kubectl_tool] version v1.9.0+.
    11  - [ansible][ansible_tool] version v2.6.0+
    12  - [ansible-runner][ansible_runner_tool] version v1.1.0+
    13  - [ansible-runner-http][ansible_runner_http_plugin] version v1.0.0+
    14  - [dep][dep_tool] version v0.5.0+. (Optional if you aren't installing from source)
    15  - [go][go_tool] version v1.10+. (Optional if you aren't installing from source)
    16  - Access to a Kubernetes v.1.9.0+ cluster.
    17  
    18  **Note**: This guide uses [minikube][minikube_tool] version v0.25.0+ as the
    19  local Kubernetes cluster and [quay.io][quay_link] for the public registry.
    20  
    21  ## Install the Operator SDK CLI
    22  
    23  The Operator SDK has a CLI tool that helps the developer to create, build, and
    24  deploy a new operator project.
    25  
    26  Checkout the desired release tag and install the SDK CLI tool:
    27  
    28  ```sh
    29  $ mkdir -p $GOPATH/src/github.com/operator-framework
    30  $ cd $GOPATH/src/github.com/operator-framework
    31  $ git clone https://github.com/operator-framework/operator-sdk
    32  $ cd operator-sdk
    33  $ git checkout master
    34  $ make dep
    35  $ make install
    36  ```
    37  
    38  This installs the CLI binary `operator-sdk` at `$GOPATH/bin`.
    39  
    40  Alternatively, if you are using [Homebrew][homebrew_tool], you can install the SDK CLI tool with the following command:
    41  
    42  ```sh
    43  $ brew install operator-sdk
    44  ```
    45  
    46  ## Create a new project
    47  
    48  Use the CLI to create a new Ansible-based memcached-operator project:
    49  
    50  ```sh
    51  $ operator-sdk new memcached-operator --api-version=cache.example.com/v1alpha1 --kind=Memcached --type=ansible
    52  $ cd memcached-operator
    53  ```
    54  
    55  This creates the memcached-operator project specifically for watching the
    56  Memcached resource with APIVersion `cache.example.com/v1apha1` and Kind
    57  `Memcached`.
    58  
    59  To learn more about the project directory structure, see [project
    60  layout][layout_doc] doc.
    61  
    62  #### Operator scope
    63  
    64  A namespace-scoped operator (the default) watches and manages resources in a single namespace, whereas a cluster-scoped operator watches and manages resources cluster-wide. Namespace-scoped operators are preferred because of their flexibility. They enable decoupled upgrades, namespace isolation for failures and monitoring, and differing API definitions. However, there are use cases where a cluster-scoped operator may make sense. For example, the [cert-manager](https://github.com/jetstack/cert-manager) operator is often deployed with cluster-scoped permissions and watches so that it can manage issuing certificates for an entire cluster.
    65  
    66  If you'd like to create your memcached-operator project to be cluster-scoped use the following `operator-sdk new` command instead:
    67  ```
    68  $ operator-sdk new memcached-operator --cluster-scoped --api-version=cache.example.com/v1alpha1 --kind=Memcached --type=ansible
    69  ```
    70  
    71  Using `--cluster-scoped` will scaffold the new operator with the following modifications:
    72  * `deploy/operator.yaml` - Set `WATCH_NAMESPACE=""` instead of setting it to the pod's namespace
    73  * `deploy/role.yaml` - Use `ClusterRole` instead of `Role`
    74  * `deploy/role_binding.yaml`:
    75    * Use `ClusterRoleBinding` instead of `RoleBinding`
    76    * Use `ClusterRole` instead of `Role` for roleRef
    77    * Set the subject namespace to `REPLACE_NAMESPACE`. This must be changed to the namespace in which the operator is deployed.
    78  
    79  ### Watches file
    80  
    81  The Watches file contains a list of mappings from custom resources, identified
    82  by it's Group, Version, and Kind, to an Ansible Role or Playbook. The Operator
    83  expects this mapping file in a predefined location: `/opt/ansible/watches.yaml`
    84  
    85  * **group**:  The group of the Custom Resource that you will be watching.
    86  * **version**:  The version of the Custom Resource that you will be watching.
    87  * **kind**:  The kind of the Custom Resource that you will be watching.
    88  * **role** (default):  This is the path to the role that you have added to the
    89    container.  For example if your roles directory is at `/opt/ansible/roles/`
    90    and your role is named `busybox`, this value will be
    91    `/opt/ansible/roles/busybox`. This field is mutually exclusive with the
    92    "playbook" field.
    93  * **playbook**:  This is the path to the playbook that you have added to the
    94    container. This playbook is expected to be simply a way to call roles. This
    95    field is mutually exclusive with the "role" field.
    96  * **reconcilePeriod** (optional): The reconciliation interval, how often the
    97    role/playbook is run, for a given CR.
    98  * **manageStatus** (optional): When true (default), the operator will manage
    99    the status of the CR generically. Set to false, the status of the CR is
   100    managed elsewhere, by the specified role/playbook or in a separate controller.
   101  
   102  An example Watches file:
   103  
   104  ```yaml
   105  ---
   106  # Simple example mapping Foo to the Foo role
   107  - version: v1alpha1
   108    group: foo.example.com
   109    kind: Foo
   110    role: /opt/ansible/roles/Foo
   111  
   112  # Simple example mapping Bar to a playbook
   113  - version: v1alpha1
   114    group: bar.example.com
   115    kind: Bar
   116    playbook: /opt/ansible/playbook.yml
   117  
   118  # More complex example for our Baz kind
   119  # Here we will disable requeuing and be managing the CR status in the playbook
   120  - version: v1alpha1
   121    group: baz.example.com
   122    kind: Baz
   123    playbook: /opt/ansible/baz.yml
   124    reconcilePeriod: 0
   125    manageStatus: false
   126  ```
   127  
   128  ## Customize the operator logic
   129  
   130  For this example the memcached-operator will execute the following
   131  reconciliation logic for each `Memcached` Custom Resource (CR):
   132  - Create a memcached Deployment if it doesn't exist
   133  - Ensure that the Deployment size is the same as specified by the `Memcached`
   134  CR
   135  
   136  ### Watch the Memcached CR
   137  
   138  By default, the memcached-operator watches `Memcached` resource events as shown
   139  in `watches.yaml` and executes Ansible Role `Memcached`:
   140  
   141  ```yaml
   142  ---
   143  - version: v1alpha1
   144    group: cache.example.com
   145    kind: Memcached
   146  ```
   147  
   148  #### Options
   149  **Role**
   150  Specifying a `role` option in `watches.yaml` will configure the operator to use
   151  this specified path when launching `ansible-runner` with an Ansible Role. By
   152  default, the `new` command will fill in an absolute path to where your role
   153  should go.
   154  ```yaml
   155  ---
   156  - version: v1alpha1
   157    group: cache.example.com
   158    kind: Memcached
   159    role: /opt/ansible/roles/memcached
   160  ```
   161  
   162  **Playbook**
   163  Specifying a `playbook` option in `watches.yaml` will configure the operator to
   164  use this specified path when launching `ansible-runner` with an Ansible
   165  Playbook
   166  ```yaml
   167  ---
   168  - version: v1alpha1
   169    group: cache.example.com
   170    kind: Memcached
   171    playbook: /opt/ansible/playbook.yaml
   172  ```
   173  
   174  ## Building the Memcached Ansible Role
   175  
   176  The first thing to do is to modify the generated Ansible role under
   177  `roles/memcached`. This Ansible Role controls the logic that is executed when a
   178  resource is modified.
   179  
   180  ### Define the Memcached spec
   181  
   182  Defining the spec for an Ansible Operator can be done entirely in Ansible. The
   183  Ansible Operator will simply pass all key value pairs listed in the Custom
   184  Resource spec field along to Ansible as
   185  [variables](https://docs.ansible.com/ansible/2.5/user_guide/playbooks_variables.html#passing-variables-on-the-command-line).
   186  The names of all variables in the spec field are converted to snake_case
   187  by the operator before running ansible. For example, `serviceAccount` in
   188  the spec becomes `service_account` in ansible.
   189  It is recommended that you perform some type validation in Ansible on the
   190  variables to ensure that your application is receiving expected input.
   191  
   192  First, set a default in case the user doesn't set the `spec` field by modifying
   193  `roles/memcached/defaults/main.yml`:
   194  ```yaml
   195  size: 1
   196  ```
   197  
   198  ### Defining the Memcached deployment
   199  
   200  Now that we have the spec defined, we can define what Ansible is actually
   201  executed on resource changes. Since this is an Ansible Role, the default
   202  behavior will be to execute the tasks in `roles/memcached/tasks/main.yml`. We
   203  want Ansible to create a deployment if it does not exist which runs the
   204  `memcached:1.4.36-alpine` image. Ansible 2.5+ supports the [k8s Ansible
   205  Module](https://docs.ansible.com/ansible/2.6/modules/k8s_module.html) which we
   206  will leverage to control the deployment definition.
   207  
   208  Modify `roles/memcached/tasks/main.yml` to look like the following:
   209  ```yaml
   210  ---
   211  - name: start memcached
   212    k8s:
   213      definition:
   214        kind: Deployment
   215        apiVersion: apps/v1
   216        metadata:
   217          name: '{{ meta.name }}-memcached'
   218          namespace: '{{ meta.namespace }}'
   219        spec:
   220          replicas: "{{size}}"
   221          selector:
   222            matchLabels:
   223              app: memcached
   224          template:
   225            metadata:
   226              labels:
   227                app: memcached
   228            spec:
   229              containers:
   230              - name: memcached
   231                command:
   232                - memcached
   233                - -m=64
   234                - -o
   235                - modern
   236                - -v
   237                image: "docker.io/memcached:1.4.36-alpine"
   238                ports:
   239                  - containerPort: 11211
   240  
   241  ```
   242  
   243  It is important to note that we used the `size` variable to control how many
   244  replicas of the Memcached deployment we want. We set the default to `1`, but
   245  any user can create a Custom Resource that overwrites the default.
   246  
   247  ### Build and run the operator
   248  
   249  Before running the operator, Kubernetes needs to know about the new custom
   250  resource definition the operator will be watching.
   251  
   252  Deploy the CRD:
   253  
   254  ```sh
   255  $ kubectl create -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
   256  ```
   257  
   258  Once this is done, there are two ways to run the operator:
   259  
   260  - As a pod inside a Kubernetes cluster
   261  - As a go program outside the cluster using `operator-sdk`
   262  
   263  #### 1. Run as a pod inside a Kubernetes cluster
   264  
   265  Running as a pod inside a Kubernetes cluster is preferred for production use.
   266  
   267  Build the memcached-operator image and push it to a registry:
   268  ```
   269  $ operator-sdk build quay.io/example/memcached-operator:v0.0.1
   270  $ docker push quay.io/example/memcached-operator:v0.0.1
   271  ```
   272  
   273  Kubernetes deployment manifests are generated in `deploy/operator.yaml`. The
   274  deployment image in this file needs to be modified from the placeholder
   275  `REPLACE_IMAGE` to the previous built image. To do this run:
   276  ```
   277  $ sed -i 's|REPLACE_IMAGE|quay.io/example/memcached-operator:v0.0.1|g' deploy/operator.yaml
   278  ```
   279  
   280  If you created your operator using `--cluster-scoped=true`, update the service account namespace in the generated `ClusterRoleBinding` to match where you are deploying your operator.
   281  ```
   282  $ export OPERATOR_NAMESPACE=$(kubectl config view --minify -o jsonpath='{.contexts[0].context.namespace}')
   283  $ sed -i "s|REPLACE_NAMESPACE|$OPERATOR_NAMESPACE|g" deploy/role_binding.yaml
   284  ```
   285  
   286  **Note**  
   287  If you are performing these steps on OSX, use the following commands instead:
   288  ```
   289  $ sed -i "" 's|REPLACE_IMAGE|quay.io/example/memcached-operator:v0.0.1|g' deploy/operator.yaml
   290  $ sed -i "" "s|REPLACE_NAMESPACE|$OPERATOR_NAMESPACE|g" deploy/role_binding.yaml
   291  ```
   292  
   293  Deploy the memcached-operator:
   294  
   295  ```sh
   296  $ kubectl create -f deploy/service_account.yaml
   297  $ kubectl create -f deploy/role.yaml
   298  $ kubectl create -f deploy/role_binding.yaml
   299  $ kubectl create -f deploy/operator.yaml
   300  ```
   301  
   302  Verify that the memcached-operator is up and running:
   303  
   304  ```sh
   305  $ kubectl get deployment
   306  NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
   307  memcached-operator       1         1         1            1           1m
   308  ```
   309  
   310  #### 2. Run outside the cluster
   311  
   312  This method is preferred during the development cycle to speed up deployment and testing.
   313  
   314  **Note**: Ensure that [Ansible Runner][ansible_runner_tool] and [Ansible Runner
   315  HTTP Plugin][ansible_runner_http_plugin] is installed or else you will see
   316  unexpected errors from Ansible Runner when a Custom Resource is created.
   317  
   318  It is also important that the `role` path referenced in `watches.yaml` exists
   319  on your machine. Since we are normally used to using a container where the Role
   320  is put on disk for us, we need to manually copy our role to the configured
   321  Ansible Roles path (e.g `/etc/ansible/roles`.
   322  
   323  Run the operator locally with the default Kubernetes config file present at
   324  `$HOME/.kube/config`:
   325  
   326  ```sh
   327  $ operator-sdk up local
   328  INFO[0000] Go Version: go1.10
   329  INFO[0000] Go OS/Arch: darwin/amd64
   330  INFO[0000] operator-sdk Version: 0.0.5+git
   331  ```
   332  
   333  Run the operator locally with a provided Kubernetes config file:
   334  
   335  ```sh
   336  $ operator-sdk up local --kubeconfig=config
   337  INFO[0000] Go Version: go1.10
   338  INFO[0000] Go OS/Arch: darwin/amd64
   339  INFO[0000] operator-sdk Version: 0.0.5+git
   340  ```
   341  
   342  ### Create a Memcached CR
   343  
   344  Modify `deploy/crds/cache_v1alpha1_memcached_cr.yaml` as shown and create a `Memcached` custom resource:
   345  
   346  ```sh
   347  $ cat deploy/crds/cache_v1alpha1_memcached_cr.yaml
   348  apiVersion: "cache.example.com/v1alpha1"
   349  kind: "Memcached"
   350  metadata:
   351    name: "example-memcached"
   352  spec:
   353    size: 3
   354  
   355  $ kubectl apply -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
   356  ```
   357  
   358  Ensure that the memcached-operator creates the deployment for the CR:
   359  
   360  ```sh
   361  $ kubectl get deployment
   362  NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
   363  memcached-operator       1         1         1            1           2m
   364  example-memcached        3         3         3            3           1m
   365  ```
   366  
   367  Check the pods to confirm 3 replicas were created:
   368  
   369  ```sh
   370  $ kubectl get pods
   371  NAME                                  READY     STATUS    RESTARTS   AGE
   372  example-memcached-6fd7c98d8-7dqdr     1/1       Running   0          1m
   373  example-memcached-6fd7c98d8-g5k7v     1/1       Running   0          1m
   374  example-memcached-6fd7c98d8-m7vn7     1/1       Running   0          1m
   375  memcached-operator-7cc7cfdf86-vvjqk   2/2       Running   0          2m
   376  ```
   377  
   378  ### View the Ansible logs
   379  
   380  The `memcached-operator` deployment creates a Pod with two containers, `operator` and `ansible`.
   381  The `ansible` container exists only to expose the standard Ansible stdout logs that most Ansible
   382  users will be familiar with. In order to see the logs from a particular container, you can run
   383  
   384  ```sh
   385  kubectl logs deployment/memcached-operator -c ansible
   386  kubectl logs deployment/memcached-operator -c operator
   387  ```
   388  
   389  The `ansible` logs contain all of the information about the Ansible run and will make it much easier to debug issues within your Ansible tasks,
   390  whereas the `operator` logs will contain much more detailed information about the Ansible Operator's internals and interface with Kubernetes.
   391  
   392  ### Update the size
   393  
   394  Change the `spec.size` field in the memcached CR from 3 to 4 and apply the
   395  change:
   396  
   397  ```sh
   398  $ cat deploy/crds/cache_v1alpha1_memcached_cr.yaml
   399  apiVersion: "cache.example.com/v1alpha1"
   400  kind: "Memcached"
   401  metadata:
   402    name: "example-memcached"
   403  spec:
   404    size: 4
   405  
   406  $ kubectl apply -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
   407  ```
   408  
   409  Confirm that the operator changes the deployment size:
   410  
   411  ```sh
   412  $ kubectl get deployment
   413  NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
   414  example-memcached    4         4         4            4           5m
   415  ```
   416  
   417  ### Cleanup
   418  
   419  Clean up the resources:
   420  
   421  ```sh
   422  $ kubectl delete -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
   423  $ kubectl delete -f deploy/operator.yaml
   424  $ kubectl delete -f deploy/role_binding.yaml
   425  $ kubectl delete -f deploy/role.yaml
   426  $ kubectl delete -f deploy/service_account.yaml
   427  $ kubectl delete -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
   428  ```
   429  
   430  [layout_doc]:./project_layout.md
   431  [homebrew_tool]:https://brew.sh/
   432  [dep_tool]:https://golang.github.io/dep/docs/installation.html
   433  [git_tool]:https://git-scm.com/downloads
   434  [go_tool]:https://golang.org/dl/
   435  [docker_tool]:https://docs.docker.com/install/
   436  [kubectl_tool]:https://kubernetes.io/docs/tasks/tools/install-kubectl/
   437  [minikube_tool]:https://github.com/kubernetes/minikube#installation
   438  [ansible_tool]:https://docs.ansible.com/ansible/latest/index.html
   439  [ansible_runner_tool]:https://ansible-runner.readthedocs.io/en/latest/install.html
   440  [ansible_runner_http_plugin]:https://github.com/ansible/ansible-runner-http
   441  [quay_link]:https://quay.io