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