项目作者: redhat-france-sa

项目描述 :
Companion of openshift-by-example for discovering and learning Kubernetes operators
高级语言: Go
项目地址: git://github.com/redhat-france-sa/openshift-by-example-operators.git
创建时间: 2020-08-13T08:09:24Z
项目社区:https://github.com/redhat-france-sa/openshift-by-example-operators

开源协议:MIT License

下载


openshift-by-example-operators

What is it?

This is a companion project to openshift-by-example and the associated article that now tackle the topic of Operators development and packaging using tools and practices from the Operator Framework.



More precisely, this repository contains samples on how to develop Operators using the Operator SDK and the 3 different technologies embedded: Helm, Ansible and Go language. We hope it will help you understand the pro and cons of different approaches and how they can map on the Operators Maturity Model phases.

Operators Maturity Model

The fruits-catalog application

The operators we have developed here are made to deploy the fruits-catalog application we usually use. This application is made of 2 components:

  • A WebApp component made using Quarkus. You can find the code within the fruits-catalog/ folder,
  • A MongoDB database.

The container image for the WebApp part of the application was made available as quay.io/lbroudoux/fruits-catalog:latest.

Pre-requisites

  • operator-sdk should be installed and present into your $PATH. We use latest 1.0.0 version and previous 0.19.2version. Just check https://master.sdk.operatorframework.io/docs/installation/install-operator-sdk/ on how to install it,
  • docker (or another tool compatible with multi-stage Dockerfiles) should be installed and present into your $PATH. Minimum version is 17.03+,
  • go should be installed and present into your $PATH. Minimum version is Go 1.13,
  • kubectl shoud be installed and present into your $PATH. v1.16.0+ is the minimum version,

Helm Operator

Operator SDK allows creating Operator using/re-using Helm Charts. Whilst they’re great starting points, we tend to think that Helm only allows you to support the first phases of the maturity model.

Discover how to develop such Operator on the dedicated Helm page.

Pros Cons
Templating and basic controls (if) Unable to discover cluster capabilities
Simplicity Custom Resource is not typed
Great to start with YAML hell ;-)

Ansible Operator

Discover how to develop such Operator on the dedicated Ansible page.

Pros Cons
Templating + Control flow structures Role or Playbook is fully evaluated at each reconciliation
Ability to discover cluster capabilities Custom Resource is not typed
Hybrid Operators: in and out the cluster YAML hell ;-)
Development loop and CI/CD with modelcule
Huge Ansible ecosystem for any equipment, device, stack, …

Go Operator

Discover how to develop such Operator on the dedicated Go page.

Pros Cons
Go is full language Complex Kube resources created with Go APIs are verbose
Ability to access and leverage Kubernetes API (discovery, types, schemes, …) Personal note: coming from Java, I hate Go error management style ;-)
Custom Resource are fully typed
Development loop with IDE support, local testing, CI/CD
Very fine grained control on reconciliation loop
Optimized for few resources (CPU, memory)

OLM manifests, Bundles and Scorecard

The Operator Lifecycle Manager (OLM) is a set of cluster resources that manage the lifecycle of an Operator. The Operator SDK supports both creating manifests for OLM deployment, and testing your Operator on an OLM-enabled Kubernetes cluster.

The OLM defines some mandatory metadata manifests that can be generated and then completed before having them packages into what will define an Operator bundle.

So start, generating this famous manifests and everything we’ll need to create a bundle!

Generating manifests metadata

As an example, we did this from within the fruits-catalog-ansible-operator-1.0.0 folder

  1. $ export IMG=quay.io/lbroudoux/fruits-catalog-ansible-operator:0.0.2
  2. $ make bundle IMG=$IMG
  3. operator-sdk generate kustomize manifests -q
  4. Display name for the operator (required):
  5. > fruits-catalog-ansible-operator-1.0.0
  6. Description for the operator (required):
  7. > Operator for the FruitsCatalog app using Operator Ansible SDK 1.0.0
  8. Provider's name for the operator (required):
  9. > Laurent Broudoux
  10. Any relevant URL for the provider name (optional):
  11. > https://github.com/redhat-france-sa/openshift-by-example-operators
  12. Comma-separated list of keywords for your operator (required):
  13. > sample,operator-sdk,fruits-catalog
  14. Comma-separated list of maintainers and their emails (e.g. 'name1:email1, name2:email2') (required):
  15. > lbroudoux:laurent.broudoux@redhat.com
  16. cd config/manager && /Users/lbroudou/Development/go-workspace/bin/kustomize edit set image controller=quay.io/lbroudoux/fruits-catalog-ansible-operator:0.0.2
  17. /Users/lbroudou/Development/go-workspace/bin/kustomize build config/manifests | operator-sdk generate bundle -q --overwrite --version 0.0.1
  18. INFO[0000] Building annotations.yaml
  19. INFO[0000] Writing annotations.yaml in /Users/lbroudou/Development/github/openshift-by-example-operators/fruits-catalog-ansible-operator-1.0.0/bundle/metadata
  20. INFO[0000] Building Dockerfile
  21. INFO[0000] Writing bundle.Dockerfile in /Users/lbroudou/Development/github/openshift-by-example-operators/fruits-catalog-ansible-operator-1.0.0
  22. operator-sdk bundle validate ./bundle
  23. INFO[0000] Found annotations file bundle-dir=bundle container-tool=docker
  24. INFO[0000] Could not find optional dependencies file bundle-dir=bundle container-tool=docker
  25. INFO[0000] All validation tests have completed successfully

The main metadata manifests generated is a Cluster Service Version. It is indeed an aggregate of your Operator CRD, deployment informations, RBAC permissions + a number of metadata allowing to identify owner, provider and categorize operators. More information on CSV here.

The previous command generates 2 things:

  • Aggregation rules of elements using kustomize within the config/manifests folder of the project,
  • Result of aggregation of elements within the bundle/ folder of the project.

If you need to make adjustements to the generated CSV, you’ll have to edit the config/manifests/base file and then re-run the following:

  1. $ operator-sdk generate bundle -q --overwrite --version 0.0.1

0.0.1 being here the version of the generated CSV.

Running the Scorecard tool

The Scorecard command within Operator SDK executes tests on your operator manifests and future bundle based upon a configuration file and test images. Tests are implemented within test images that are configured and constructed to be executed by scorecard.

In order to run, Scorecard need you to be connected to a Kubernetes cluster. You just ask Scorecard to eevaluate the manifest that have been previously generated and are present into the bundle/ folder of your project like this:

  1. $ operator-sdk scorecard bundle
  2. --------------------------------------------------------------------------------
  3. Image: quay.io/operator-framework/scorecard-test:master
  4. Entrypoint: [scorecard-test olm-crds-have-resources]
  5. Labels:
  6. "suite":"olm"
  7. "test":"olm-crds-have-resources-test"
  8. Results:
  9. Name: olm-crds-have-resources
  10. State: pass
  11. Log:
  12. Loaded ClusterServiceVersion: fruits-catalog-ansible-operator.v0.0.1
  13. --------------------------------------------------------------------------------
  14. Image: quay.io/operator-framework/scorecard-test:master
  15. Entrypoint: [scorecard-test olm-crds-have-validation]
  16. Labels:
  17. "test":"olm-crds-have-validation-test"
  18. "suite":"olm"
  19. Results:
  20. Name: olm-crds-have-validation
  21. State: fail
  22. Suggestions:
  23. Add CRD validation for spec field `name` in FruitsCatalogA1/v1beta1
  24. Add CRD validation for spec field `webapp` in FruitsCatalogA1/v1beta1
  25. Log:
  26. Loaded 1 Custom Resources from alm-examples
  27. Loaded CustomresourceDefinitions: [&CustomResourceDefinition{ObjectMeta:{fruitscataloga1s.redhat.com 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] [] []},Spec:CustomResourceDefinitionSpec{Group:redhat.com,Names:CustomResourceDefinitionNames{Plural:fruitscataloga1s,Singular:fruitscataloga1,ShortNames:[],Kind:FruitsCatalogA1,ListKind:FruitsCatalogA1List,Categories:[],},Scope:Namespaced,Versions:[]CustomResourceDefinitionVersion{CustomResourceDefinitionVersion{Name:v1beta1,Served:true,Storage:true,Schema:&CustomResourceValidation{OpenAPIV3Schema:&JSONSchemaProps{ID:,Schema:,Ref:nil,Description:FruitsCatalogA1 is the Schema for the fruitscataloga1s API,Type:object,Format:,Title:,Default:nil,Maximum:nil,ExclusiveMaximum:false,Minimum:nil,ExclusiveMinimum:false,MaxLength:nil,MinLength:nil,Pattern:,MaxItems:nil,MinItems:nil,UniqueItems:false,MultipleOf:nil,Enum:[]JSON{},MaxProperties:nil,MinProperties:nil,Required:[],Items:nil,AllOf:[]JSONSchemaProps{},OneOf:[]JSONSchemaProps{},AnyOf:[]JSONSchemaProps{},Not:nil,Properties:map[string]JSONSchemaProps{apiVersion: { <nil> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources string nil <nil> false <nil> false <nil> <nil> <nil> <nil> false <nil> [] <nil> <nil> [] nil [] [] [] nil map[] nil map[] map[] nil map[] nil nil false <nil> false false [] <nil> <nil>},kind: { <nil> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds string nil <nil> false <nil> false <nil> <nil> <nil> <nil> false <nil> [] <nil> <nil> [] nil [] [] [] nil map[] nil map[] map[] nil map[] nil nil false <nil> false false [] <nil> <nil>},metadata: { <nil> object nil <nil> false <nil> false <nil> <nil> <nil> <nil> false <nil> [] <nil> <nil> [] nil [] [] [] nil map[] nil map[] map[] nil map[] nil nil false <nil> false false [] <nil> <nil>},spec: { <nil> Spec defines the desired state of FruitsCatalogA1 object nil <nil> false <nil> false <nil> <nil> <nil> <nil> false <nil> [] <nil> <nil> [] nil [] [] [] nil map[] nil map[] map[] nil map[] nil nil false 0xc000236f10 false false [] <nil> <nil>},status: { <nil> Status defines the observed state of FruitsCatalogA1 object nil <nil> false <nil> false <nil> <nil> <nil> <nil> false <nil> [] <nil> <nil> [] nil [] [] [] nil map[] nil map[] map[] nil map[] nil nil false 0xc000236f11 false false [] <nil> <nil>},},AdditionalProperties:nil,PatternProperties:map[string]JSONSchemaProps{},Dependencies:JSONSchemaDependencies{},AdditionalItems:nil,Definitions:JSONSchemaDefinitions{},ExternalDocs:nil,Example:nil,Nullable:false,XPreserveUnknownFields:nil,XEmbeddedResource:false,XIntOrString:false,XListMapKeys:[],XListType:nil,XMapType:nil,},},Subresources:&CustomResourceSubresources{Status:&CustomResourceSubresourceStatus{},Scale:nil,},AdditionalPrinterColumns:[]CustomResourceColumnDefinition{},},},Conversion:nil,PreserveUnknownFields:false,},Status:CustomResourceDefinitionStatus{Conditions:[]CustomResourceDefinitionCondition{},AcceptedNames:CustomResourceDefinitionNames{Plural:,Singular:,ShortNames:[],Kind:,ListKind:,Categories:[],},StoredVersions:[],},}]
  28. --------------------------------------------------------------------------------
  29. Image: quay.io/operator-framework/scorecard-test:master
  30. Entrypoint: [scorecard-test olm-status-descriptors]
  31. Labels:
  32. "suite":"olm"
  33. "test":"olm-status-descriptors-test"
  34. Results:
  35. Name: olm-status-descriptors
  36. State: fail
  37. Log:
  38. Loaded ClusterServiceVersion: fruits-catalog-ansible-operator.v0.0.1
  39. Loaded 1 Custom Resources from alm-examples
  40. --------------------------------------------------------------------------------
  41. Image: quay.io/operator-framework/scorecard-test:master
  42. Entrypoint: [scorecard-test basic-check-spec]
  43. Labels:
  44. "suite":"basic"
  45. "test":"basic-check-spec-test"
  46. Results:
  47. Name: basic-check-spec
  48. State: pass
  49. --------------------------------------------------------------------------------
  50. Image: quay.io/operator-framework/scorecard-test:master
  51. Entrypoint: [scorecard-test olm-spec-descriptors]
  52. Labels:
  53. "suite":"olm"
  54. "test":"olm-spec-descriptors-test"
  55. Results:
  56. Name: olm-spec-descriptors
  57. State: fail
  58. Suggestions:
  59. Add a spec descriptor for webapp
  60. Errors:
  61. webapp does not have a spec descriptor
  62. Log:
  63. Loaded ClusterServiceVersion: fruits-catalog-ansible-operator.v0.0.1
  64. Loaded 1 Custom Resources from alm-examples
  65. --------------------------------------------------------------------------------
  66. Image: quay.io/operator-framework/scorecard-test:master
  67. Entrypoint: [scorecard-test olm-bundle-validation]
  68. Labels:
  69. "suite":"olm"
  70. "test":"olm-bundle-validation-test"
  71. Results:
  72. Name: olm-bundle-validation
  73. State: pass
  74. Log:
  75. time="2020-08-31T07:52:00Z" level=debug msg="Found manifests directory" name=bundle-test
  76. time="2020-08-31T07:52:00Z" level=debug msg="Found metadata directory" name=bundle-test
  77. time="2020-08-31T07:52:00Z" level=debug msg="Getting mediaType info from manifests directory" name=bundle-test
  78. time="2020-08-31T07:52:00Z" level=info msg="Found annotations file" name=bundle-test
  79. time="2020-08-31T07:52:00Z" level=info msg="Could not find optional dependencies file" name=bundle-test

The Scorecard output gives you advices and suggestions for making your Cluster Service Version and aggregated manifests compliant with best practices.

Building and validating the Bundle

Once you’re happy with the Scorecard results, it’s time to package and distribute your Operator bundle. The previous commands have also generated a bundle.Dockerfile at project root and you can simply use it for building a contaienr image for your bundle:

  1. $ docker build -f bundle.Dockerfile -t quay.io/lbroudoux/fruits-catalog-ansible-operator-bundle:v0.0.1 .
  2. $ docker push quay.io/lbroudoux/fruits-catalog-ansible-operator-bundle:v0.0.1

Last step before going is to validate the published bundle with this final command:

  1. $ operator-sdk bundle validate quay.io/lbroudoux/fruits-catalog-ansible-operator-bundle:v0.0.1
  2. INFO[0000] Unpacking image layers
  3. INFO[0000] running /usr/local/bin/docker pull quay.io/lbroudoux/fruits-catalog-ansible-operator-bundle:v0.0.1
  4. INFO[0001] running docker create
  5. INFO[0001] running docker cp
  6. INFO[0002] running docker rm
  7. INFO[0002] Found annotations file bundle-dir=/var/folders/wy/lb2qd3m51ld_k8ds2b6xkz180000gn/T/bundle-845300542 container-tool=docker
  8. INFO[0002] Could not find optional dependencies file bundle-dir=/var/folders/wy/lb2qd3m51ld_k8ds2b6xkz180000gn/T/bundle-845300542 container-tool=docker
  9. INFO[0002] All validation tests have completed successfully

Operators Registry and Catalog

The very last part of this sample/tutorials projets is on the topic of easily distributing your Operator to customers, partners through the concept of market place as we have in OpenShift ;-)

For this, just have a look at the Fruits Catalog Registry.