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