|
| 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 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 | +## Create a new project |
| 41 | + |
| 42 | +Use the CLI to create a new Ansible-based memcached-operator project: |
| 43 | + |
| 44 | +```sh |
| 45 | +$ mkdir -p $GOPATH/src/github.com/example-inc/ |
| 46 | +$ cd $GOPATH/src/github.com/example-inc/ |
| 47 | +$ operator-sdk new memcached-operator --api-version=cache.example.com/v1alpha1 --kind=Memcached --type=ansible |
| 48 | +$ cd memcached-operator |
| 49 | +``` |
| 50 | + |
| 51 | +This creates the memcached-operator project specifically for watching the |
| 52 | +Memcached resource with APIVersion `cache.example.com/v1apha1` and Kind |
| 53 | +`Memcached`. |
| 54 | + |
| 55 | +To learn more about the project directory structure, see [project |
| 56 | +layout][layout_doc] doc. |
| 57 | + |
| 58 | +## Customize the operator logic |
| 59 | + |
| 60 | +For this example the memcached-operator will execute the following |
| 61 | +reconciliation logic for each `Memcached` Custom Resource (CR): |
| 62 | +- Create a memcached Deployment if it doesn't exist |
| 63 | +- Ensure that the Deployment size is the same as specified by the `Memcached` |
| 64 | +CR |
| 65 | + |
| 66 | +### Watch the Memcached CR |
| 67 | + |
| 68 | +By default, the memcached-operator watches `Memcached` resource events as shown |
| 69 | +in `watches.yaml` and executes Ansible Role `Memached`: |
| 70 | + |
| 71 | +```yaml |
| 72 | +--- |
| 73 | +- version: v1alpha1 |
| 74 | + group: cache.example.com |
| 75 | + kind: Memcached |
| 76 | +``` |
| 77 | +
|
| 78 | +#### Options |
| 79 | +**Role** |
| 80 | +Specifying a `role` option in `watches.yaml` will configure the operator to use |
| 81 | +this specified path when launching `ansible-runner` with an Ansible Role. By |
| 82 | +default, the `new` command will fill in an absolute path to where your role |
| 83 | +should go. |
| 84 | +```yaml |
| 85 | +--- |
| 86 | +- version: v1alpha1 |
| 87 | + group: cache.example.com |
| 88 | + kind: Memcached |
| 89 | + role: /opt/ansible/roles/Memcached |
| 90 | +``` |
| 91 | + |
| 92 | +**Playbook** |
| 93 | +Specifying a `playbook` option in `watches.yaml` will configure the operator to |
| 94 | +use this specified path when launching `ansible-runner` with an Ansible |
| 95 | +Playbook |
| 96 | +```yaml |
| 97 | +--- |
| 98 | +- version: v1alpha1 |
| 99 | + group: cache.example.com |
| 100 | + kind: Memcached |
| 101 | + playbook: /opt/ansible/playbook.yaml |
| 102 | +``` |
| 103 | + |
| 104 | +## Building the Memcached Ansible Role |
| 105 | + |
| 106 | +The first thing to do is to modify the generated Ansible role under |
| 107 | +`roles/Memcached`. This Ansible Role controls the logic that is executed when a |
| 108 | +resource is modified. |
| 109 | + |
| 110 | +### Define the Memcached spec |
| 111 | + |
| 112 | +Defining the spec for an Ansible Operator can be done entirely in Ansible. The |
| 113 | +Ansible Operator will simply pass all key value pairs listed in the Custom |
| 114 | +Resource spec field along to Ansible as |
| 115 | +[variables](https://docs.ansible.com/ansible/2.5/user_guide/playbooks_variables.html#passing-variables-on-the-command-line). |
| 116 | +It is recommended that you perform some type validation in Ansible on the |
| 117 | +variables to ensure that your application is receiving expected input. |
| 118 | + |
| 119 | +First, set a default in case the user doesn't set the `spec` field by modifying |
| 120 | +`roles/Memcached/defaults/main.yml`: |
| 121 | +```yaml |
| 122 | +size: 1 |
| 123 | +``` |
| 124 | + |
| 125 | +### Defining the Memcached deployment |
| 126 | + |
| 127 | +Now that we have the spec defined, we can define what Ansible is actually |
| 128 | +executed on resource changes. Since this is an Ansible Role, the default |
| 129 | +behavior will be to execute the tasks in `roles/Memcached/tasks/main.yml`. We |
| 130 | +want Ansible to create a deployment if it does not exist which runs the |
| 131 | +`memcached:1.4.36-alpine` image. Ansible 2.5+ supports the [k8s Ansible |
| 132 | +Module](https://docs.ansible.com/ansible/2.6/modules/k8s_module.html) which we |
| 133 | +will leverage to control the deployment definition. |
| 134 | + |
| 135 | +Modify `roles/Memcached/tasks/main.yml` to look like the following: |
| 136 | +```yaml |
| 137 | +--- |
| 138 | +- name: start memcached |
| 139 | + k8s: |
| 140 | + definition: |
| 141 | + kind: Deployment |
| 142 | + apiVersion: apps/v1 |
| 143 | + metadata: |
| 144 | + name: '{{ meta.name }}-memcached' |
| 145 | + namespace: '{{ meta.namespace }}' |
| 146 | + spec: |
| 147 | + replicas: "{{size}}" |
| 148 | + selector: |
| 149 | + matchLabels: |
| 150 | + app: memcached |
| 151 | + template: |
| 152 | + metadata: |
| 153 | + labels: |
| 154 | + app: memcached |
| 155 | + spec: |
| 156 | + containers: |
| 157 | + - name: memcached |
| 158 | + command: |
| 159 | + - memcached |
| 160 | + - -m=64 |
| 161 | + - -o |
| 162 | + - modern |
| 163 | + - -v |
| 164 | + image: "docker.io/memcached:1.4.36-alpine" |
| 165 | + ports: |
| 166 | + - containerPort: 11211 |
| 167 | +
|
| 168 | +``` |
| 169 | + |
| 170 | +It is important to note that we used the `size` variable to control how many |
| 171 | +replicas of the Memcached deployment we want. We set the default to `1`, but |
| 172 | +any user can create a Custom Resource that overwrites the default. |
| 173 | + |
| 174 | +### Build and run the operator |
| 175 | + |
| 176 | +Before running the operator, Kubernetes needs to know about the new custom |
| 177 | +resource definition the operator will be watching. |
| 178 | + |
| 179 | +Deploy the CRD: |
| 180 | + |
| 181 | +```sh |
| 182 | +$ kubectl create -f deploy/crd.yaml |
| 183 | +``` |
| 184 | + |
| 185 | +Once this is done, there are two ways to run the operator: |
| 186 | + |
| 187 | +- As a pod inside a Kubernetes cluster |
| 188 | +- As a go program outside the cluster using `operator-sdk` |
| 189 | + |
| 190 | +#### 1. Run as a pod inside a Kubernetes cluster |
| 191 | + |
| 192 | +Running as a pod inside a Kubernetes cluster is preferred for production use. |
| 193 | + |
| 194 | +Build the memcached-operator image and push it to a registry: |
| 195 | +``` |
| 196 | +$ operator-sdk build quay.io/example/memcached-operator:v0.0.1 |
| 197 | +$ docker push quay.io/example/memcached-operator:v0.0.1 |
| 198 | +``` |
| 199 | + |
| 200 | +Kubernetes deployment manifests are generated in `deploy/operator.yaml`. The |
| 201 | +deployment image in this file needs to be modified from the placeholder |
| 202 | +`REPLACE_IMAGE` to the previous built image. To do this run: |
| 203 | +``` |
| 204 | +$ sed -i 's|REPLACE_IMAGE|quay.io/example/memcached-operator:v0.0.1|g' deploy/operator.yaml |
| 205 | +``` |
| 206 | + |
| 207 | +Deploy the memcached-operator: |
| 208 | + |
| 209 | +```sh |
| 210 | +$ kubectl create -f deploy/rbac.yaml |
| 211 | +$ kubectl create -f deploy/operator.yaml |
| 212 | +``` |
| 213 | + |
| 214 | +**NOTE**: `deploy/rbac.yaml` creates a `ClusterRoleBinding` and assumes we are |
| 215 | +working in namespace `default`. If you are working in a different namespace you |
| 216 | +must modify this file before creating it. |
| 217 | + |
| 218 | +Verify that the memcached-operator is up and running: |
| 219 | + |
| 220 | +```sh |
| 221 | +$ kubectl get deployment |
| 222 | +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE |
| 223 | +memcached-operator 1 1 1 1 1m |
| 224 | +``` |
| 225 | + |
| 226 | +#### 2. Run outside the cluster |
| 227 | + |
| 228 | +This method is preferred during the development cycle to speed up deployment and testing. |
| 229 | + |
| 230 | +**Note**: Ensure that [Ansible Runner][ansible_runner_tool] and [Ansible Runner |
| 231 | +HTTP Plugin][ansible_runner_http_plugin] is installed or else you will see |
| 232 | +unexpected errors from Ansible Runner when a Custom Resource is created. |
| 233 | + |
| 234 | +It is also important that the `role` path referenced in `watches.yaml` exists |
| 235 | +on your machine. Since we are normally used to using a container where the Role |
| 236 | +is put on disk for us, we need to manually copy our role to the configured |
| 237 | +Ansible Roles path (e.g `/etc/ansible/roles`. |
| 238 | + |
| 239 | +Run the operator locally with the default kubernetes config file present at |
| 240 | +`$HOME/.kube/config`: |
| 241 | + |
| 242 | +```sh |
| 243 | +$ operator-sdk up local |
| 244 | +INFO[0000] Go Version: go1.10 |
| 245 | +INFO[0000] Go OS/Arch: darwin/amd64 |
| 246 | +INFO[0000] operator-sdk Version: 0.0.5+git |
| 247 | +``` |
| 248 | + |
| 249 | +Run the operator locally with a provided kubernetes config file: |
| 250 | + |
| 251 | +```sh |
| 252 | +$ operator-sdk up local --kubeconfig=config |
| 253 | +INFO[0000] Go Version: go1.10 |
| 254 | +INFO[0000] Go OS/Arch: darwin/amd64 |
| 255 | +INFO[0000] operator-sdk Version: 0.0.5+git |
| 256 | +``` |
| 257 | + |
| 258 | +### Create a Memcached CR |
| 259 | + |
| 260 | +Modify `deploy/cr.yaml` as shown and create a `Memcached` custom resource: |
| 261 | + |
| 262 | +```sh |
| 263 | +$ cat deploy/cr.yaml |
| 264 | +apiVersion: "cache.example.com/v1alpha1" |
| 265 | +kind: "Memcached" |
| 266 | +metadata: |
| 267 | + name: "example-memcached" |
| 268 | +spec: |
| 269 | + size: 3 |
| 270 | +
|
| 271 | +$ kubectl apply -f deploy/cr.yaml |
| 272 | +``` |
| 273 | + |
| 274 | +Ensure that the memcached-operator creates the deployment for the CR: |
| 275 | + |
| 276 | +```sh |
| 277 | +$ kubectl get deployment |
| 278 | +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE |
| 279 | +memcached-operator 1 1 1 1 2m |
| 280 | +example-memcached 3 3 3 3 1m |
| 281 | +``` |
| 282 | + |
| 283 | +Check the pods to confirm 3 replicas were created: |
| 284 | + |
| 285 | +```sh |
| 286 | +$ kubectl get pods |
| 287 | +NAME READY STATUS RESTARTS AGE |
| 288 | +example-memcached-6fd7c98d8-7dqdr 1/1 Running 0 1m |
| 289 | +example-memcached-6fd7c98d8-g5k7v 1/1 Running 0 1m |
| 290 | +example-memcached-6fd7c98d8-m7vn7 1/1 Running 0 1m |
| 291 | +memcached-operator-7cc7cfdf86-vvjqk 1/1 Running 0 2m |
| 292 | +``` |
| 293 | + |
| 294 | +### Update the size |
| 295 | + |
| 296 | +Change the `spec.size` field in the memcached CR from 3 to 4 and apply the |
| 297 | +change: |
| 298 | + |
| 299 | +```sh |
| 300 | +$ cat deploy/cr.yaml |
| 301 | +apiVersion: "cache.example.com/v1alpha1" |
| 302 | +kind: "Memcached" |
| 303 | +metadata: |
| 304 | + name: "example-memcached" |
| 305 | +spec: |
| 306 | + size: 4 |
| 307 | +
|
| 308 | +$ kubectl apply -f deploy/cr.yaml |
| 309 | +``` |
| 310 | + |
| 311 | +Confirm that the operator changes the deployment size: |
| 312 | + |
| 313 | +```sh |
| 314 | +$ kubectl get deployment |
| 315 | +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE |
| 316 | +example-memcached 4 4 4 4 5m |
| 317 | +``` |
| 318 | + |
| 319 | +### Cleanup |
| 320 | + |
| 321 | +Clean up the resources: |
| 322 | + |
| 323 | +```sh |
| 324 | +$ kubectl delete -f deploy/cr.yaml |
| 325 | +$ kubectl delete -f deploy/operator.yaml |
| 326 | +``` |
| 327 | + |
| 328 | +[layout_doc]:./project_layout.md |
| 329 | +[dep_tool]:https://golang.github.io/dep/docs/installation.html |
| 330 | +[git_tool]:https://git-scm.com/downloads |
| 331 | +[go_tool]:https://golang.org/dl/ |
| 332 | +[docker_tool]:https://docs.docker.com/install/ |
| 333 | +[kubectl_tool]:https://kubernetes.io/docs/tasks/tools/install-kubectl/ |
| 334 | +[minikube_tool]:https://github.com/kubernetes/minikube#installation |
| 335 | +[ansible_tool]:https://docs.ansible.com/ansible/latest/index.html |
| 336 | +[ansible_runner_tool]:https://ansible-runner.readthedocs.io/en/latest/install.html |
| 337 | +[ansible_runner_http_plugin]:https://github.com/ansible/ansible-runner-http |
0 commit comments