├── .github └── workflows │ └── build.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── config ├── crd │ ├── bases │ │ └── plugins.cloudminds.com_lokis.yaml │ └── kustomization.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── manager │ ├── controller_manager_config.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── loki_editor_role.yaml │ ├── loki_viewer_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml ├── samples │ ├── kustomization.yaml │ └── plugins_v1_loki.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml └── testing │ ├── debug_logs_patch.yaml │ ├── kustomization.yaml │ ├── manager_image.yaml │ └── pull_policy │ ├── Always.yaml │ ├── IfNotPresent.yaml │ └── Never.yaml ├── deploy ├── crd_loki_spec.md └── loki-operator.yaml ├── molecule ├── default │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── kustomize.yml │ ├── molecule.yml │ ├── prepare.yml │ ├── tasks │ │ └── loki_test.yml │ └── verify.yml └── kind │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ └── molecule.yml ├── playbooks ├── .placeholder └── loki.yml ├── requirements.yml ├── roles ├── .placeholder └── loki │ ├── README.md │ ├── defaults │ └── main.yml │ ├── files │ └── .placeholder │ ├── handlers │ └── main.yml │ ├── meta │ └── main.yml │ ├── tasks │ ├── loki-frontend.yaml │ ├── loki-gateway.yaml │ ├── loki-system.yaml │ ├── main.yml │ ├── memcached.yaml │ └── redis.yaml │ ├── templates │ ├── .placeholder │ ├── frontend │ │ ├── service.yaml │ │ ├── servicemonitor.yaml │ │ └── statefulset.yaml │ ├── gateway │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── loki.config.yaml │ ├── loki │ │ ├── headless.yaml │ │ ├── service.yaml │ │ ├── servicemonitor.yaml │ │ └── statefulset.yaml │ ├── memcached │ │ ├── metrics.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ ├── servicemonitor.yaml │ │ └── statefulset.yaml │ └── redis │ │ ├── configmap.yaml │ │ ├── metrics.yaml │ │ ├── pvc.yaml │ │ ├── service.yaml │ │ ├── servicemonitor.yaml │ │ └── statefulset.yaml │ └── vars │ └── main.yml └── watches.yaml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "[0-9]+.[0-9]+.[0-9]+" 9 | env: 10 | PLATFORMS: linux/amd64 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-18.04 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2.3.3 18 | 19 | - name: Repo metadata 20 | id: repo 21 | uses: actions/github-script@v3 22 | with: 23 | script: | 24 | const repo = await github.repos.get(context.repo) 25 | return repo.data 26 | - name: Prepare 27 | id: prepare 28 | run: | 29 | VERSION=latest 30 | if [[ $GITHUB_REF == refs/tags/* ]]; then 31 | VERSION=${GITHUB_REF#refs/tags/} 32 | elif [[ $GITHUB_REF == refs/heads/* ]]; then 33 | VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g') 34 | if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then 35 | VERSION=latest 36 | fi 37 | elif [[ $GITHUB_REF == refs/pull/* ]]; then 38 | VERSION=pr-${{ github.event.number }} 39 | fi 40 | TAGS="${VERSION}" 41 | echo ::set-output name=version::${VERSION} 42 | echo ::set-output name=tags::${TAGS} 43 | echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ') 44 | - name: Set up QEMU 45 | uses: docker/setup-qemu-action@v1 46 | 47 | - name: Docker Setup Buildx 48 | uses: docker/setup-buildx-action@v1 49 | 50 | - name: Login to Quay.io 51 | if: ${{ github.event_name }} != 'pull_request' 52 | uses: docker/login-action@v1.6.0 53 | with: 54 | registry: quay.io 55 | username: ${{ secrets.QUARIO_USERNAME }} 56 | password: ${{ secrets.QUARIO_PASSWORD }} 57 | - name: Build image and push 58 | uses: docker/build-push-action@v2 59 | with: 60 | context: . 61 | file: ./Dockerfile 62 | platforms: linux/amd64 63 | push: ${{ github.event_name != 'pull_request' }} 64 | tags: quay.io/cloudminds/loki-operator:${{ steps.prepare.outputs.tags }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | 10 | # editor and IDE paraphernalia 11 | .idea 12 | *.swp 13 | *.swo 14 | *~ 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/operator-framework/ansible-operator:v1.7.2 2 | 3 | COPY requirements.yml ${HOME}/requirements.yml 4 | RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ 5 | && chmod -R ug+rwx ${HOME}/.ansible 6 | 7 | COPY watches.yaml ${HOME}/watches.yaml 8 | COPY roles/ ${HOME}/roles/ 9 | COPY playbooks/ ${HOME}/playbooks/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # VERSION defines the project version for the bundle. 2 | # Update this value when you upgrade the version of your project. 3 | # To re-generate a bundle for another specific version without changing the standard setup, you can: 4 | # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) 5 | # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) 6 | VERSION ?= 0.0.1 7 | 8 | # CHANNELS define the bundle channels used in the bundle. 9 | # Add a new line here if you would like to change its default config. (E.g CHANNELS = "preview,fast,stable") 10 | # To re-generate a bundle for other specific channels without changing the standard setup, you can: 11 | # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=preview,fast,stable) 12 | # - use environment variables to overwrite this value (e.g export CHANNELS="preview,fast,stable") 13 | ifneq ($(origin CHANNELS), undefined) 14 | BUNDLE_CHANNELS := --channels=$(CHANNELS) 15 | endif 16 | 17 | # DEFAULT_CHANNEL defines the default channel used in the bundle. 18 | # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") 19 | # To re-generate a bundle for any other default channel without changing the default setup, you can: 20 | # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) 21 | # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") 22 | ifneq ($(origin DEFAULT_CHANNEL), undefined) 23 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) 24 | endif 25 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) 26 | 27 | # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. 28 | # This variable is used to construct full image tags for bundle and catalog images. 29 | # 30 | # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both 31 | # cloudminds.com/loki-operator-bundle:$VERSION and cloudminds.com/loki-operator-catalog:$VERSION. 32 | IMAGE_TAG_BASE ?= quay.io/cloudminds/loki-operator 33 | 34 | # BUNDLE_IMG defines the image:tag used for the bundle. 35 | # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) 36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) 37 | 38 | # Image URL to use all building/pushing image targets 39 | IMG ?= $(IMAGE_TAG_BASE):latest 40 | 41 | all: docker-build 42 | 43 | ##@ General 44 | 45 | # The help target prints out all targets with their descriptions organized 46 | # beneath their categories. The categories are represented by '##@' and the 47 | # target descriptions by '##'. The awk commands is responsible for reading the 48 | # entire set of makefiles included in this invocation, looking for lines of the 49 | # file as xyz: ## something, and then pretty-format the target and help. Then, 50 | # if there's a line with ##@ something, that gets pretty-printed as a category. 51 | # More info on the usage of ANSI control characters for terminal formatting: 52 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 53 | # More info on the awk command: 54 | # http://linuxcommand.org/lc3_adv_awk.php 55 | 56 | help: ## Display this help. 57 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 58 | 59 | ##@ Build 60 | 61 | run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config 62 | $(ANSIBLE_OPERATOR) run 63 | 64 | docker-build: ## Build docker image with the manager. 65 | docker build -t ${IMG} . 66 | 67 | docker-push: ## Push docker image with the manager. 68 | docker push ${IMG} 69 | 70 | ##@ Deployment 71 | 72 | install: kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. 73 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 74 | 75 | uninstall: kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. 76 | $(KUSTOMIZE) build config/crd | kubectl delete -f - 77 | 78 | deploy: kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. 79 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 80 | $(KUSTOMIZE) build config/default | kubectl apply -f - 81 | 82 | undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. 83 | $(KUSTOMIZE) build config/default | kubectl delete -f - 84 | 85 | OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') 86 | ARCH := $(shell uname -m | sed 's/x86_64/amd64/') 87 | 88 | .PHONY: kustomize 89 | KUSTOMIZE = $(shell pwd)/bin/kustomize 90 | kustomize: ## Download kustomize locally if necessary. 91 | ifeq (,$(wildcard $(KUSTOMIZE))) 92 | ifeq (,$(shell which kustomize 2>/dev/null)) 93 | @{ \ 94 | set -e ;\ 95 | mkdir -p $(dir $(KUSTOMIZE)) ;\ 96 | curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.5.4/kustomize_v3.5.4_$(OS)_$(ARCH).tar.gz | \ 97 | tar xzf - -C bin/ ;\ 98 | } 99 | else 100 | KUSTOMIZE = $(shell which kustomize) 101 | endif 102 | endif 103 | 104 | .PHONY: ansible-operator 105 | ANSIBLE_OPERATOR = $(shell pwd)/bin/ansible-operator 106 | ansible-operator: ## Download ansible-operator locally if necessary, preferring the $(pwd)/bin path over global if both exist. 107 | ifeq (,$(wildcard $(ANSIBLE_OPERATOR))) 108 | ifeq (,$(shell which ansible-operator 2>/dev/null)) 109 | @{ \ 110 | set -e ;\ 111 | mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\ 112 | curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.7.2/ansible-operator_$(OS)_$(ARCH) ;\ 113 | chmod +x $(ANSIBLE_OPERATOR) ;\ 114 | } 115 | else 116 | ANSIBLE_OPERATOR = $(shell which ansible-operator) 117 | endif 118 | endif 119 | 120 | .PHONY: bundle 121 | bundle: kustomize ## Generate bundle manifests and metadata, then validate generated files. 122 | operator-sdk generate kustomize manifests -q 123 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 124 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 125 | operator-sdk bundle validate ./bundle 126 | 127 | .PHONY: bundle-build 128 | bundle-build: ## Build the bundle image. 129 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 130 | 131 | .PHONY: bundle-push 132 | bundle-push: ## Push the bundle image. 133 | $(MAKE) docker-push IMG=$(BUNDLE_IMG) 134 | 135 | .PHONY: opm 136 | OPM = ./bin/opm 137 | opm: ## Download opm locally if necessary. 138 | ifeq (,$(wildcard $(OPM))) 139 | ifeq (,$(shell which opm 2>/dev/null)) 140 | @{ \ 141 | set -e ;\ 142 | mkdir -p $(dir $(OPM)) ;\ 143 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$(OS)-$(ARCH)-opm ;\ 144 | chmod +x $(OPM) ;\ 145 | } 146 | else 147 | OPM = $(shell which opm) 148 | endif 149 | endif 150 | 151 | # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). 152 | # These images MUST exist in a registry and be pull-able. 153 | BUNDLE_IMGS ?= $(BUNDLE_IMG) 154 | 155 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). 156 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) 157 | 158 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. 159 | ifneq ($(origin CATALOG_BASE_IMG), undefined) 160 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) 161 | endif 162 | 163 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. 164 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: 165 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator 166 | .PHONY: catalog-build 167 | catalog-build: opm ## Build a catalog image. 168 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) 169 | 170 | # Push the catalog image. 171 | .PHONY: catalog-push 172 | catalog-push: ## Push a catalog image. 173 | $(MAKE) docker-push IMG=$(CATALOG_IMG) 174 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: cloudminds.com 2 | layout: 3 | - ansible.sdk.operatorframework.io/v1 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: loki-operator 8 | resources: 9 | - api: 10 | crdVersion: v1 11 | namespaced: true 12 | domain: cloudminds.com 13 | group: plugins 14 | kind: Loki 15 | version: v1 16 | version: "3" 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Loki-Operator - a kubernetes operator for loki management 2 | 3 | Loki Operator是基于[Operator-SDK](https://github.com/operator-framework/operator-sdk)构建的一个Ansible控制器,playbook利用[kubernetes.community](https://github.com/ansible-collections/community.kubernetes)模块与kubernetes实现交互。 4 | 5 | Loki Operator完整的实现Loki在kubernetes环境下的生命周期和配置管理,适合在云原生场景多租户模式下的部署与管理。 6 | 7 | > 🔔🔔🔔 Loki Operator还未经过大规模测试,目前还不建议直接用于生产环境. 8 | 9 | 最后,感谢Operator-SDK️项目 🌈 10 | 11 | ## Supported System 12 | 13 | Kubernetes: 1.18+ 14 | 15 | ## Features 16 | - [x] Loki Architecture 17 | - [x] Single Node 18 | - [x] Cluster / HA [ 🔔 What is Loki Ha mode](https://github.com/grafana/loki/tree/main/production/docker) 19 | - [x] Loki 20 | - [x] Loki Frontent 21 | - [x] Loki Gateway 22 | - [ ] Cluster / MicroService 23 | - [x] Ring 24 | - [x] Memberlist 25 | - [ ] Consul 26 | - [ ] Etcd 27 | - [x] Cached 28 | - [x] Redis 29 | - [x] Memcached 30 | - [x] StorageSchema 31 | - [x] S3 32 | - [x] boltdb-shipper 33 | - [ ] Cansandra 34 | - [ ] GCS 35 | - [x] metrics 36 | - [x] Redis 37 | - [x] Memcache 38 | - [x] Loki 39 | - [x] LokiStack 40 | - [ ] Grafana 41 | - [ ] Promtail 42 | 43 | ## Installation 44 | 45 | 1. 执行以下命令安装loki operator 46 | 47 | ``` 48 | kubtctl apply -f https://raw.githubusercontent.com/CloudmindsRobot/loki-operator/main/deploy/loki-operator.yaml 49 | ``` 50 | 51 | 2. 执行以下命令验证部署结果 52 | 53 | ``` 54 | kubectl get pod -n loki-operator-system 55 | NAME READY STATUS RESTARTS AGE 56 | loki-operator-controller-manager-56c5547b9-d8v5j 2/2 Running 0 4h2m 57 | ``` 58 | 59 | ## Deployment 60 | 61 | 1. 部署一个Loki单实例,使用boltdb-shipper本地磁盘做数据持久化 62 | 63 | ``` 64 | apiVersion: plugins.cloudminds.com/v1 65 | kind: Loki 66 | metadata: 67 | name: loki 68 | annotations: 69 | ansible.operator-sdk/reconcile-period: "30s" 70 | spec: 71 | version: 2.2.1 72 | multitenancy: false 73 | servicemonitor: false 74 | service: 75 | mode: single 76 | single: 77 | loki: 78 | image: grafana/loki 79 | schemaconfig: 80 | index: boltdb-shipper 81 | chunk: filesystem 82 | 83 | storageconfig: 84 | boltdb_shipper: 85 | shared_store: filesystem 86 | 87 | filesystem: 88 | directory: /loki/chunks 89 | ``` 90 | 91 | 2. 在上述的Loki部署中启用redis缓存,并启用servicemonitor采集监控指标 92 | 93 | ``` 94 | apiVersion: plugins.cloudminds.com/v1 95 | kind: Loki 96 | metadata: 97 | name: loki 98 | annotations: 99 | ansible.operator-sdk/reconcile-period: "30s" 100 | spec: 101 | version: 2.2.1 102 | multitenancy: false 103 | servicemonitor: true 104 | service: 105 | mode: single 106 | single: 107 | loki: 108 | image: grafana/loki 109 | cacheconfig: 110 | enabled: true 111 | expiration: 1h 112 | type: redis 113 | redis: 114 | image: redis 115 | tag: 5.0.6 116 | schemaconfig: 117 | index: boltdb-shipper 118 | chunk: filesystem 119 | 120 | storageconfig: 121 | boltdb_shipper: 122 | shared_store: filesystem 123 | 124 | filesystem: 125 | directory: /loki/chunks 126 | ``` 127 | 128 | 3. 在上述的Loki集群HA模式,并使用boltdb-shipper和S3作为日志存储,同时打开缓存和监控指标,启用Ruler组件,并开启多租户 129 | 130 | ``` 131 | apiVersion: plugins.cloudminds.com/v1 132 | kind: Loki 133 | metadata: 134 | name: loki 135 | annotations: 136 | ansible.operator-sdk/reconcile-period: "30s" 137 | spec: 138 | version: 2.2.1 139 | grafana: 140 | enabled: true 141 | multitenancy: true 142 | servicemonitor: true 143 | service: 144 | mode: cluster 145 | single: 146 | loki: 147 | image: grafana/loki 148 | cluster: 149 | type: ha 150 | replication_factor: 2 151 | ring: 152 | type: memberlist 153 | gateway: 154 | image: nginx 155 | tag: 1.15.1-alpine 156 | replicas: 2 157 | frontend: 158 | image: grafana/loki 159 | replicas: 2 160 | loki: 161 | image: grafana/loki 162 | replicas: 3 163 | cacheconfig: 164 | enabled: true 165 | expiration: 1h 166 | type: memcached 167 | redis: 168 | image: redis 169 | tag: 5.0.6 170 | 171 | ruler: 172 | enabled: true 173 | ring: memberlist 174 | alertmanager: http://xxxxxxx 175 | storage: s3 176 | s3: 177 | bucket: loki-ruler-operator 178 | 179 | schemaconfig: 180 | index: boltdb-shipper 181 | chunk: s3 182 | 183 | storageconfig: 184 | boltdb_shipper: 185 | shared_store: s3 186 | 187 | filesystem: 188 | directory: /loki/chunks 189 | 190 | s3: 191 | address: x.x.x.x 192 | secret_key: xPfIfXBj1Mnt625ZA9c2wXXgmLVPaMUOmMBt3M6H 193 | access_key: g373dR9D1ZAsUD6FWEO 194 | bucket: loki-operator 195 | ``` 196 | 197 | 更多关于CRD的描述请参考[Loki CRD Spec](deploy/crd_loki_spec.md) 198 | 199 | -------------------------------------------------------------------------------- /config/crd/bases/plugins.cloudminds.com_lokis.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: lokis.plugins.cloudminds.com 6 | spec: 7 | group: plugins.cloudminds.com 8 | names: 9 | kind: Loki 10 | listKind: LokiList 11 | plural: lokis 12 | singular: loki 13 | scope: Namespaced 14 | versions: 15 | - name: v1 16 | schema: 17 | openAPIV3Schema: 18 | description: Loki is the Schema for the lokis API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of Loki 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of Loki 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/plugins.cloudminds.com_lokis.yaml 6 | #+kubebuilder:scaffold:crdkustomizeresource 7 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: loki-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: loki-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 20 | #- ../prometheus 21 | 22 | patchesStrategicMerge: 23 | # Protect the /metrics endpoint by putting it behind auth. 24 | # If you want your controller-manager to expose the /metrics 25 | # endpoint w/o any authn/z, please comment the following line. 26 | - manager_auth_proxy_patch.yaml 27 | 28 | # Mount the controller config file for loading manager configurations 29 | # through a ComponentConfig type 30 | #- manager_config_patch.yaml 31 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | name: https 22 | - name: manager 23 | args: 24 | - "--health-probe-bind-address=:6789" 25 | - "--metrics-bind-address=127.0.0.1:8080" 26 | - "--leader-elect" 27 | - "--leader-election-id=loki-operator" 28 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :6789 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | 8 | leaderElection: 9 | leaderElect: true 10 | resourceName: 811c9dc5.cloudminds.com 11 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - name: manager-config 9 | files: 10 | - controller_manager_config.yaml 11 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | securityContext: 26 | runAsNonRoot: true 27 | containers: 28 | - args: 29 | - --leader-elect 30 | - --leader-election-id=loki-operator 31 | image: controller:latest 32 | name: manager 33 | env: 34 | - name: ANSIBLE_GATHERING 35 | value: explicit 36 | securityContext: 37 | allowPrivilegeEscalation: false 38 | livenessProbe: 39 | httpGet: 40 | path: /healthz 41 | port: 6789 42 | initialDelaySeconds: 15 43 | periodSeconds: 20 44 | readinessProbe: 45 | httpGet: 46 | path: /readyz 47 | port: 6789 48 | initialDelaySeconds: 5 49 | periodSeconds: 10 50 | serviceAccountName: controller-manager 51 | terminationGracePeriodSeconds: 10 52 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/loki-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/loki_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit lokis. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: loki-editor-role 6 | rules: 7 | - apiGroups: 8 | - plugins.cloudminds.com 9 | resources: 10 | - lokis 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - plugins.cloudminds.com 21 | resources: 22 | - lokis/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/loki_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view lokis. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: loki-viewer-role 6 | rules: 7 | - apiGroups: 8 | - plugins.cloudminds.com 9 | resources: 10 | - lokis 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - plugins.cloudminds.com 17 | resources: 18 | - lokis/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | ## 8 | ## Base operator rules 9 | ## 10 | - apiGroups: 11 | - "" 12 | - apps 13 | - batch 14 | - extensions 15 | - policy 16 | - rbac.authorization.k8s.io 17 | resources: 18 | - persistentvolumeclaims 19 | - pods 20 | - services 21 | - replicationcontrollers 22 | - persistentvolumeclaims 23 | - persistentvolumes 24 | - endpoints 25 | - secrets 26 | - configmaps 27 | - serviceaccounts 28 | - daemonsets 29 | - deployments 30 | - deployments/scale 31 | - replicasets 32 | - statefulsets 33 | - statefulsets/scale 34 | - jobs 35 | verbs: 36 | - '*' 37 | 38 | - apiGroups: 39 | - monitoring.coreos.com 40 | resources: 41 | - servicemonitors 42 | - podmonitors 43 | verbs: 44 | - '*' 45 | 46 | ## 47 | ## Rules for plugins.cloudminds.com/v1, Kind: Loki 48 | ## 49 | - apiGroups: 50 | - plugins.cloudminds.com 51 | resources: 52 | - lokis 53 | - lokis/status 54 | - lokis/finalizers 55 | verbs: 56 | - create 57 | - delete 58 | - get 59 | - list 60 | - patch 61 | - update 62 | - watch 63 | #+kubebuilder:scaffold:rules 64 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - plugins_v1_loki.yaml 4 | #+kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /config/samples/plugins_v1_loki.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: plugins.cloudminds.com/v1 2 | kind: Loki 3 | metadata: 4 | name: loki 5 | annotations: 6 | ansible.operator-sdk/reconcile-period: "30s" 7 | spec: 8 | version: 2.2.1 9 | grafana: 10 | enabled: true 11 | multitenancy: true 12 | servicemonitor: true 13 | service: 14 | #mode: single,cluster 15 | mode: cluster 16 | single: 17 | loki: 18 | image: grafana/loki 19 | cluster: 20 | #type: ha, microservices 21 | type: ha 22 | replication_factor: 2 23 | ring: 24 | #memberlist, consul 25 | type: memberlist 26 | #consul: 27 | # deploy: true 28 | gateway: 29 | image: nginx 30 | tag: 1.15.1-alpine 31 | replicas: 2 32 | frontend: 33 | image: grafana/loki 34 | replicas: 2 35 | loki: 36 | image: grafana/loki 37 | replicas: 3 38 | #microservices: 39 | # ingester: 40 | # image: grafana/loki 41 | # replicas: 3 42 | # distributor: 43 | # image: grafana/loki 44 | # replicas: 3 45 | # querier: 46 | # image: grafana/loki 47 | # replicas: 3 48 | # ruler: 49 | # image: grafana/loki 50 | # replicas: 2 51 | # table-manager: 52 | # image: grafana/loki 53 | # replicas: 2 54 | cacheconfig: 55 | enabled: false 56 | expiration: 1h 57 | type: memcached 58 | redis: 59 | image: redis 60 | tag: 5.0.6 61 | #external: 62 | # address: xxxx 63 | # port: 6379 64 | memcached: 65 | image: docker.io/bitnami/memcached 66 | tag: 1.6.9-debian-10-r114 67 | #external: 68 | # address: xxxx 69 | # port: 11211 70 | 71 | ruler: 72 | enabled: true 73 | ring: memberlist 74 | alertmanager: http://xxxxxxx 75 | storage: s3 76 | s3: 77 | bucket: loki-ruler-operator 78 | 79 | schemaconfig: 80 | #index: boltdb-shipper, cassandra 81 | index: boltdb-shipper 82 | #chunk: filesystem, s3 83 | chunk: s3 84 | 85 | storageconfig: 86 | boltdb_shipper: 87 | #shared_store: s3, filesystem 88 | shared_store: s3 89 | 90 | filesystem: 91 | directory: /loki/chunks 92 | 93 | s3: 94 | address: 10.51.201.22 95 | secret_key: xPfIfXBjqMnt62dZA9c2wXXCmLVPaMUOmMBt3M6H 96 | access_key: 5373OR9D1ZA5UD6FWE6O 97 | bucket: loki-operator 98 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.7.2 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.7.2 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.7.2 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.7.2 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.7.2 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.7.2 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /config/testing/debug_logs_patch.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | env: 13 | - name: ANSIBLE_DEBUG_LOGS 14 | value: "TRUE" 15 | -------------------------------------------------------------------------------- /config/testing/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: osdk-test 3 | 4 | namePrefix: osdk- 5 | 6 | # Labels to add to all resources and selectors. 7 | #commonLabels: 8 | # someName: someValue 9 | 10 | patchesStrategicMerge: 11 | - manager_image.yaml 12 | - debug_logs_patch.yaml 13 | - ../default/manager_auth_proxy_patch.yaml 14 | 15 | apiVersion: kustomize.config.k8s.io/v1beta1 16 | kind: Kustomization 17 | resources: 18 | - ../crd 19 | - ../rbac 20 | - ../manager 21 | images: 22 | - name: testing 23 | newName: testing-operator 24 | -------------------------------------------------------------------------------- /config/testing/manager_image.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | image: testing 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Always.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Always 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/IfNotPresent.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: IfNotPresent 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Never.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Never 13 | -------------------------------------------------------------------------------- /deploy/crd_loki_spec.md: -------------------------------------------------------------------------------- 1 | ## LokiSpec 2 | 3 | **LokiSpec**定义了部署Loki的行为和配置. 4 | 5 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 6 | | -------------- | ------------------ | -------- | ----------------------------------------- | 7 | | version | string | yes | Loki版本,用于镜像的TAG | 8 | | multitenancy | bool | yes | Loki多租户 | 9 | | servicemonitor | bool | yes | 启用监控指标,依赖prometheus-operator服务 | 10 | | service | *serviceSpec | yes | Loki服务架构 | 11 | | cacheconfig | *cacheconfigSpec | yes | Loki缓存配置 | 12 | | ruler | *rulerSpec | yes | Loki Ruler组件配置 | 13 | | shemaconfig | *shemaconfigSpec | yes | Index、Chunks的持久化类型 | 14 | | storageconfig | *storageconfigSpec | yes | 数据持久化类型配置 | 15 | 16 | 17 | 18 | **serviceSpec** 19 | 20 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 21 | | ------------- | ------------ | -------- | ------------------------------------------------------------ | 22 | | mode | string | yes | Loki运行模式,支持单实例和集群两种类型
single / cluster | 23 | | single | *single | yes | Loki单实例配置 | 24 | | cluster | *clusterSpec | yes | Loki集群配置 | 25 | 26 | **clusterSpec** 27 | 28 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 29 | | ------------------ | -------------- | -------- | ------------------------------------------------------------ | 30 | | type | string | yes | Loki集群类型,支持HA和微服务两种类型
ha / microservice | 31 | | replication_factor | int | yes | 复制因子 | 32 | | ring | *ring | yes | 哈希环类型,当前只支持memberlist | 33 | | gateway | *gateway | yes | 网关服务配置 | 34 | | frontend | *frontend | yes | frontend服务配置 | 35 | | loki | *loki | yes | Loki配置(主要为ha模式下的ingester、distributor、querier三合一服务) | 36 | | microservices | *microservices | yes | Loki微服务模式下的配置(当前还不支持) | 37 | | | | | | 38 | 39 | **cacheSpec** 40 | 41 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 42 | | ------------- | ---------- | -------- | ------------------------------ | 43 | | enabled | bool | yes | 开启Loki缓存 | 44 | | expiration | string | yes | 缓存key持久化时间 | 45 | | type | string | yes | 缓存类型
redis / memcached | 46 | | redis | *redis | yes | redis服务配置 | 47 | | memcached | *memcached | yes | memcached服务配置 | 48 | 49 | **schemaconfig** 50 | 51 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 52 | | ------------- | ------ | -------- | ------------------------------- | 53 | | index | string | yes | index存储
boltdb-shipper | 54 | | chunk | string | yes | chunk存储
filesystem / s3 | 55 | 56 | **storageconfig** 57 | 58 | | VARIABLE NAME | TYPE | REQUIRED | DESCRIPTION | 59 | | --------------------------- | --------------- | -------- | ------------------------------------------ | 60 | | boltdb-shipper | *boltdb-shipper | yes | | 61 | | boltdb-shipper.shared_store | string | yes | boltdb-shipper后端存储
s3 / filesystem | 62 | | filesystem | *filesystem | yes | | 63 | | filesystem.directory | | | filesystem写入路径 | 64 | | s3 | *s3 | yes | | 65 | | s3.address | string | yes | s3地址 | 66 | | s3.secret_key | string | yes | secret key | 67 | | s3.access_key | string | yes | access key | 68 | | s3.bucket | string | yes | bucket名 | 69 | | | | | | 70 | 71 | -------------------------------------------------------------------------------- /deploy/loki-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: loki-operator-system 7 | --- 8 | apiVersion: apiextensions.k8s.io/v1 9 | kind: CustomResourceDefinition 10 | metadata: 11 | name: lokis.plugins.cloudminds.com 12 | spec: 13 | group: plugins.cloudminds.com 14 | names: 15 | kind: Loki 16 | listKind: LokiList 17 | plural: lokis 18 | singular: loki 19 | scope: Namespaced 20 | versions: 21 | - name: v1 22 | schema: 23 | openAPIV3Schema: 24 | description: Loki is the Schema for the lokis API 25 | properties: 26 | apiVersion: 27 | description: 'APIVersion defines the versioned schema of this representation 28 | of an object. Servers should convert recognized schemas to the latest 29 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 30 | type: string 31 | kind: 32 | description: 'Kind is a string value representing the REST resource this 33 | object represents. Servers may infer this from the endpoint the client 34 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 35 | type: string 36 | metadata: 37 | type: object 38 | spec: 39 | description: Spec defines the desired state of Loki 40 | type: object 41 | x-kubernetes-preserve-unknown-fields: true 42 | status: 43 | description: Status defines the observed state of Loki 44 | type: object 45 | x-kubernetes-preserve-unknown-fields: true 46 | type: object 47 | served: true 48 | storage: true 49 | subresources: 50 | status: {} 51 | --- 52 | apiVersion: v1 53 | kind: ServiceAccount 54 | metadata: 55 | name: loki-operator-controller-manager 56 | namespace: loki-operator-system 57 | --- 58 | apiVersion: rbac.authorization.k8s.io/v1 59 | kind: Role 60 | metadata: 61 | name: loki-operator-leader-election-role 62 | namespace: loki-operator-system 63 | rules: 64 | - apiGroups: 65 | - "" 66 | resources: 67 | - configmaps 68 | verbs: 69 | - get 70 | - list 71 | - watch 72 | - create 73 | - update 74 | - patch 75 | - delete 76 | - apiGroups: 77 | - coordination.k8s.io 78 | resources: 79 | - leases 80 | verbs: 81 | - get 82 | - list 83 | - watch 84 | - create 85 | - update 86 | - patch 87 | - delete 88 | - apiGroups: 89 | - "" 90 | resources: 91 | - events 92 | verbs: 93 | - create 94 | - patch 95 | --- 96 | apiVersion: rbac.authorization.k8s.io/v1 97 | kind: ClusterRole 98 | metadata: 99 | name: loki-operator-manager-role 100 | rules: 101 | - apiGroups: 102 | - "" 103 | - apps 104 | - batch 105 | - extensions 106 | - policy 107 | - rbac.authorization.k8s.io 108 | resources: 109 | - persistentvolumeclaims 110 | - pods 111 | - services 112 | - replicationcontrollers 113 | - persistentvolumeclaims 114 | - persistentvolumes 115 | - endpoints 116 | - secrets 117 | - configmaps 118 | - serviceaccounts 119 | - daemonsets 120 | - deployments 121 | - deployments/scale 122 | - replicasets 123 | - statefulsets 124 | - statefulsets/scale 125 | - jobs 126 | verbs: 127 | - '*' 128 | - apiGroups: 129 | - monitoring.coreos.com 130 | resources: 131 | - servicemonitors 132 | - podmonitors 133 | verbs: 134 | - '*' 135 | - apiGroups: 136 | - plugins.cloudminds.com 137 | resources: 138 | - lokis 139 | - lokis/status 140 | - lokis/finalizers 141 | verbs: 142 | - create 143 | - delete 144 | - get 145 | - list 146 | - patch 147 | - update 148 | - watch 149 | --- 150 | apiVersion: rbac.authorization.k8s.io/v1 151 | kind: ClusterRole 152 | metadata: 153 | name: loki-operator-metrics-reader 154 | rules: 155 | - nonResourceURLs: 156 | - /metrics 157 | verbs: 158 | - get 159 | --- 160 | apiVersion: rbac.authorization.k8s.io/v1 161 | kind: ClusterRole 162 | metadata: 163 | name: loki-operator-proxy-role 164 | rules: 165 | - apiGroups: 166 | - authentication.k8s.io 167 | resources: 168 | - tokenreviews 169 | verbs: 170 | - create 171 | - apiGroups: 172 | - authorization.k8s.io 173 | resources: 174 | - subjectaccessreviews 175 | verbs: 176 | - create 177 | --- 178 | apiVersion: rbac.authorization.k8s.io/v1 179 | kind: RoleBinding 180 | metadata: 181 | name: loki-operator-leader-election-rolebinding 182 | namespace: loki-operator-system 183 | roleRef: 184 | apiGroup: rbac.authorization.k8s.io 185 | kind: Role 186 | name: loki-operator-leader-election-role 187 | subjects: 188 | - kind: ServiceAccount 189 | name: loki-operator-controller-manager 190 | namespace: loki-operator-system 191 | --- 192 | apiVersion: rbac.authorization.k8s.io/v1 193 | kind: ClusterRoleBinding 194 | metadata: 195 | name: loki-operator-manager-rolebinding 196 | roleRef: 197 | apiGroup: rbac.authorization.k8s.io 198 | kind: ClusterRole 199 | name: loki-operator-manager-role 200 | subjects: 201 | - kind: ServiceAccount 202 | name: loki-operator-controller-manager 203 | namespace: loki-operator-system 204 | --- 205 | apiVersion: rbac.authorization.k8s.io/v1 206 | kind: ClusterRoleBinding 207 | metadata: 208 | name: loki-operator-proxy-rolebinding 209 | roleRef: 210 | apiGroup: rbac.authorization.k8s.io 211 | kind: ClusterRole 212 | name: loki-operator-proxy-role 213 | subjects: 214 | - kind: ServiceAccount 215 | name: loki-operator-controller-manager 216 | namespace: loki-operator-system 217 | --- 218 | apiVersion: v1 219 | data: 220 | controller_manager_config.yaml: | 221 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 222 | kind: ControllerManagerConfig 223 | health: 224 | healthProbeBindAddress: :6789 225 | metrics: 226 | bindAddress: 127.0.0.1:8080 227 | leaderElection: 228 | leaderElect: true 229 | resourceName: 811c9dc5.cloudminds.com 230 | kind: ConfigMap 231 | metadata: 232 | name: loki-operator-manager-config 233 | namespace: loki-operator-system 234 | --- 235 | apiVersion: v1 236 | kind: Service 237 | metadata: 238 | labels: 239 | control-plane: controller-manager 240 | name: loki-operator-controller-manager-metrics-service 241 | namespace: loki-operator-system 242 | spec: 243 | ports: 244 | - name: https 245 | port: 8443 246 | targetPort: https 247 | selector: 248 | control-plane: controller-manager 249 | --- 250 | apiVersion: apps/v1 251 | kind: Deployment 252 | metadata: 253 | labels: 254 | control-plane: controller-manager 255 | name: loki-operator-controller-manager 256 | namespace: loki-operator-system 257 | spec: 258 | replicas: 1 259 | selector: 260 | matchLabels: 261 | control-plane: controller-manager 262 | template: 263 | metadata: 264 | labels: 265 | control-plane: controller-manager 266 | spec: 267 | containers: 268 | - args: 269 | - --secure-listen-address=0.0.0.0:8443 270 | - --upstream=http://127.0.0.1:8080/ 271 | - --logtostderr=true 272 | - --v=10 273 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 274 | name: kube-rbac-proxy 275 | ports: 276 | - containerPort: 8443 277 | name: https 278 | - args: 279 | - --health-probe-bind-address=:6789 280 | - --metrics-bind-address=127.0.0.1:8080 281 | - --leader-elect 282 | - --leader-election-id=loki-operator 283 | env: 284 | - name: ANSIBLE_GATHERING 285 | value: explicit 286 | image: quay.io/cloudminds/loki-operator:latest 287 | livenessProbe: 288 | httpGet: 289 | path: /healthz 290 | port: 6789 291 | initialDelaySeconds: 15 292 | periodSeconds: 20 293 | name: manager 294 | readinessProbe: 295 | httpGet: 296 | path: /readyz 297 | port: 6789 298 | initialDelaySeconds: 5 299 | periodSeconds: 10 300 | securityContext: 301 | allowPrivilegeEscalation: false 302 | securityContext: 303 | runAsNonRoot: true 304 | serviceAccountName: loki-operator-controller-manager 305 | terminationGracePeriodSeconds: 10 -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - name: Create Namespace 11 | k8s: 12 | api_version: v1 13 | kind: Namespace 14 | name: '{{ namespace }}' 15 | 16 | - import_tasks: kustomize.yml 17 | vars: 18 | state: present 19 | -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: [] 7 | -------------------------------------------------------------------------------- /molecule/default/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - import_tasks: kustomize.yml 11 | vars: 12 | state: absent 13 | 14 | - name: Destroy Namespace 15 | k8s: 16 | api_version: v1 17 | kind: Namespace 18 | name: '{{ namespace }}' 19 | state: absent 20 | 21 | - name: Unset pull policy 22 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 23 | args: 24 | chdir: '{{ config_dir }}/testing' 25 | -------------------------------------------------------------------------------- /molecule/default/kustomize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Build kustomize testing overlay 3 | # load_restrictor must be set to none so we can load patch files from the default overlay 4 | command: '{{ kustomize }} build --load_restrictor none .' 5 | args: 6 | chdir: '{{ config_dir }}/testing' 7 | register: resources 8 | changed_when: false 9 | 10 | - name: Set resources to {{ state }} 11 | k8s: 12 | definition: '{{ item }}' 13 | state: '{{ state }}' 14 | wait: yes 15 | loop: '{{ resources.stdout | from_yaml_all | list }}' 16 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | lint: | 7 | set -e 8 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 9 | platforms: 10 | - name: cluster 11 | groups: 12 | - k8s 13 | provisioner: 14 | name: ansible 15 | lint: | 16 | set -e 17 | ansible-lint 18 | inventory: 19 | group_vars: 20 | all: 21 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 22 | host_vars: 23 | localhost: 24 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 25 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 26 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 27 | operator_image: ${OPERATOR_IMAGE:-""} 28 | operator_pull_policy: ${OPERATOR_PULL_POLICY:-"Always"} 29 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 30 | env: 31 | K8S_AUTH_KUBECONFIG: ${KUBECONFIG:-"~/.kube/config"} 32 | verifier: 33 | name: ansible 34 | lint: | 35 | set -e 36 | ansible-lint 37 | -------------------------------------------------------------------------------- /molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | 7 | tasks: 8 | - name: Ensure operator image is set 9 | fail: 10 | msg: | 11 | You must specify the OPERATOR_IMAGE environment variable in order to run the 12 | 'default' scenario 13 | when: not operator_image 14 | 15 | - name: Set testing image 16 | command: '{{ kustomize }} edit set image testing={{ operator_image }}' 17 | args: 18 | chdir: '{{ config_dir }}/testing' 19 | 20 | - name: Set pull policy 21 | command: '{{ kustomize }} edit add patch pull_policy/{{ operator_pull_policy }}.yaml' 22 | args: 23 | chdir: '{{ config_dir }}/testing' 24 | 25 | - name: Set testing namespace 26 | command: '{{ kustomize }} edit set namespace {{ namespace }}' 27 | args: 28 | chdir: '{{ config_dir }}/testing' 29 | -------------------------------------------------------------------------------- /molecule/default/tasks/loki_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create the plugins.cloudminds.com/v1.Loki 3 | k8s: 4 | state: present 5 | namespace: '{{ namespace }}' 6 | definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" 7 | wait: yes 8 | wait_timeout: 300 9 | wait_condition: 10 | type: Running 11 | reason: Successful 12 | status: "True" 13 | vars: 14 | cr_file: 'plugins_v1_loki.yaml' 15 | 16 | - name: Add assertions here 17 | assert: 18 | that: false 19 | fail_msg: FIXME Add real assertions for your operator 20 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - community.kubernetes 8 | 9 | vars: 10 | ctrl_label: control-plane=controller-manager 11 | 12 | tasks: 13 | - block: 14 | - name: Import all test files from tasks/ 15 | include_tasks: '{{ item }}' 16 | with_fileglob: 17 | - tasks/*_test.yml 18 | rescue: 19 | - name: Retrieve relevant resources 20 | k8s_info: 21 | api_version: '{{ item.api_version }}' 22 | kind: '{{ item.kind }}' 23 | namespace: '{{ namespace }}' 24 | loop: 25 | - api_version: v1 26 | kind: Pod 27 | - api_version: apps/v1 28 | kind: Deployment 29 | - api_version: v1 30 | kind: Secret 31 | - api_version: v1 32 | kind: ConfigMap 33 | register: debug_resources 34 | 35 | - name: Retrieve Pod logs 36 | k8s_log: 37 | name: '{{ item.metadata.name }}' 38 | namespace: '{{ namespace }}' 39 | container: manager 40 | loop: "{{ q('k8s', api_version='v1', kind='Pod', namespace=namespace, label_selector=ctrl_label) }}" 41 | register: debug_logs 42 | 43 | - name: Output gathered resources 44 | debug: 45 | var: debug_resources 46 | 47 | - name: Output gathered logs 48 | debug: 49 | var: item.log_lines 50 | loop: '{{ debug_logs.results }}' 51 | 52 | - name: Re-emit failure 53 | vars: 54 | failed_task: 55 | result: '{{ ansible_failed_result }}' 56 | fail: 57 | msg: '{{ failed_task }}' 58 | -------------------------------------------------------------------------------- /molecule/kind/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | 7 | tasks: 8 | - name: Build operator image 9 | docker_image: 10 | build: 11 | path: '{{ project_dir }}' 12 | pull: no 13 | name: '{{ operator_image }}' 14 | tag: latest 15 | push: no 16 | source: build 17 | force_source: yes 18 | 19 | - name: Load image into kind cluster 20 | command: kind load docker-image --name osdk-test '{{ operator_image }}' 21 | register: result 22 | changed_when: '"not yet present" in result.stdout' 23 | 24 | - import_playbook: ../default/converge.yml 25 | -------------------------------------------------------------------------------- /molecule/kind/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: 7 | - name: Create test kind cluster 8 | command: kind create cluster --name osdk-test --kubeconfig {{ kubeconfig }} 9 | -------------------------------------------------------------------------------- /molecule/kind/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - name: Destroy test kind cluster 11 | command: kind delete cluster --name osdk-test --kubeconfig {{ kubeconfig }} 12 | 13 | - name: Unset pull policy 14 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 15 | args: 16 | chdir: '{{ config_dir }}/testing' 17 | -------------------------------------------------------------------------------- /molecule/kind/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | lint: | 7 | set -e 8 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 9 | platforms: 10 | - name: cluster 11 | groups: 12 | - k8s 13 | provisioner: 14 | name: ansible 15 | playbooks: 16 | prepare: ../default/prepare.yml 17 | verify: ../default/verify.yml 18 | lint: | 19 | set -e 20 | ansible-lint 21 | inventory: 22 | group_vars: 23 | all: 24 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 25 | host_vars: 26 | localhost: 27 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 28 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 29 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 30 | project_dir: ${MOLECULE_PROJECT_DIRECTORY} 31 | operator_image: testing-operator 32 | operator_pull_policy: "Never" 33 | kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" 34 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 35 | env: 36 | K8S_AUTH_KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 37 | KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 38 | verifier: 39 | name: ansible 40 | lint: | 41 | set -e 42 | ansible-lint 43 | -------------------------------------------------------------------------------- /playbooks/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CloudmindsRobot/loki-operator/24c436ee740ccd22fa663e11ea908113e360a499/playbooks/.placeholder -------------------------------------------------------------------------------- /playbooks/loki.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | gather_facts: no 4 | collections: 5 | - community.kubernetes 6 | - operator_sdk.util 7 | tasks: 8 | - import_role: 9 | name: "loki" 10 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: community.kubernetes 4 | version: "1.2.1" 5 | - name: operator_sdk.util 6 | version: "0.2.0" 7 | -------------------------------------------------------------------------------- /roles/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CloudmindsRobot/loki-operator/24c436ee740ccd22fa663e11ea908113e360a499/roles/.placeholder -------------------------------------------------------------------------------- /roles/loki/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, 10 | if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 11 | 12 | Role Variables 13 | -------------- 14 | 15 | A description of the settable variables for this role should go here, including any variables that are in 16 | defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables 17 | that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well 18 | 19 | Dependencies 20 | ------------ 21 | 22 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set 23 | for other roles, or variables that are used from other roles. 24 | 25 | Example Playbook 26 | ---------------- 27 | 28 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for 29 | users too: 30 | 31 | - hosts: servers 32 | roles: 33 | - { role: username.rolename, x: 42 } 34 | 35 | License 36 | ------- 37 | 38 | BSD 39 | 40 | Author Information 41 | ------------------ 42 | 43 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 44 | -------------------------------------------------------------------------------- /roles/loki/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for Loki 3 | -------------------------------------------------------------------------------- /roles/loki/files/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CloudmindsRobot/loki-operator/24c436ee740ccd22fa663e11ea908113e360a499/roles/loki/files/.placeholder -------------------------------------------------------------------------------- /roles/loki/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for Loki 3 | - name: Scale down single loki 4 | when: '{{service.mode == "single"}}' 5 | community.kubernetes.k8s_scale: 6 | api_version: v1 7 | kind: StatefulSet 8 | name: '{{ansible_operator_meta.name}}-loki-system' 9 | namespace: '{{ansible_operator_meta.namespace}}' 10 | replicas: 0 11 | wait: yes 12 | 13 | - name: Scale up single loki 14 | when: '{{service.mode == "single"}}' 15 | community.kubernetes.k8s_scale: 16 | api_version: v1 17 | kind: StatefulSet 18 | name: '{{ansible_operator_meta.name}}-loki-system' 19 | namespace: '{{ansible_operator_meta.namespace}}' 20 | replicas: 1 21 | wait: no 22 | 23 | - name: Scale down ha loki system 24 | when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 25 | community.kubernetes.k8s_scale: 26 | api_version: v1 27 | kind: StatefulSet 28 | name: '{{ansible_operator_meta.name}}-loki-system' 29 | namespace: '{{ansible_operator_meta.namespace}}' 30 | replicas: 0 31 | wait: yes 32 | 33 | - name: Scale up ha loki system 34 | when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 35 | community.kubernetes.k8s_scale: 36 | api_version: v1 37 | kind: StatefulSet 38 | name: '{{ansible_operator_meta.name}}-loki-system' 39 | namespace: '{{ansible_operator_meta.namespace}}' 40 | replicas: '{{service.cluster.loki.replicas}}' 41 | wait: no 42 | 43 | - name: Scale down ha loki frontend 44 | when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 45 | community.kubernetes.k8s_scale: 46 | api_version: v1 47 | kind: StatefulSet 48 | name: '{{ansible_operator_meta.name}}-loki-frontend' 49 | namespace: '{{ansible_operator_meta.namespace}}' 50 | replicas: 0 51 | wait: yes 52 | 53 | - name: Scale up ha loki frontend 54 | when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 55 | community.kubernetes.k8s_scale: 56 | api_version: v1 57 | kind: StatefulSet 58 | name: '{{ansible_operator_meta.name}}-loki-system' 59 | namespace: '{{ansible_operator_meta.namespace}}' 60 | replicas: '{{service.cluster.frontend.replicas}}' 61 | wait: no 62 | 63 | #- name: Scale down microservices loki 64 | # when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 65 | # community.kubernetes.k8s_scale: 66 | # resource_definition: "{{ lookup('template', item.name) | from_yaml }}" 67 | # replicas: 0 68 | # lookup: 69 | # - name: frontend/statefulset.yaml 70 | # - name: loki/statefulset.yaml 71 | # wait: yes 72 | # 73 | #- name: Scale up microservices loki 74 | # when: '{{service.mode == "cluster" and service.cluster.type == "ha"}}' 75 | # community.kubernetes.k8s: 76 | # definition: "{{ lookup('template', 'item.name') | from_yaml }}" 77 | # lookup: 78 | # - name: frontend/statefulset.yaml 79 | # - name: loki/statefulset.yaml 80 | -------------------------------------------------------------------------------- /roles/loki/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: your name 4 | description: your description 5 | company: your company (optional) 6 | 7 | # If the issue tracker for your role is not on github, uncomment the 8 | # next line and provide a value 9 | # issue_tracker_url: http://example.com/issue/tracker 10 | 11 | # Some suggested licenses: 12 | # - BSD (default) 13 | # - MIT 14 | # - GPLv2 15 | # - GPLv3 16 | # - Apache 17 | # - CC-BY 18 | license: license (GPLv2, CC-BY, etc) 19 | 20 | min_ansible_version: 2.9 21 | 22 | # If this a Container Enabled role, provide the minimum Ansible Container version. 23 | # min_ansible_container_version: 24 | 25 | # Optionally specify the branch Galaxy will use when accessing the GitHub 26 | # repo for this role. During role install, if no tags are available, 27 | # Galaxy will use this branch. During import Galaxy will access files on 28 | # this branch. If Travis integration is configured, only notifications for this 29 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 30 | # (usually master) will be used. 31 | #github_branch: 32 | 33 | # 34 | # Provide a list of supported platforms, and for each platform a list of versions. 35 | # If you don't wish to enumerate all versions for a particular platform, use 'all'. 36 | # To view available platforms and versions (or releases), visit: 37 | # https://galaxy.ansible.com/api/v1/platforms/ 38 | # 39 | # platforms: 40 | # - name: Fedora 41 | # versions: 42 | # - all 43 | # - 25 44 | # - name: SomePlatform 45 | # versions: 46 | # - all 47 | # - 1.0 48 | # - 7 49 | # - 99.99 50 | 51 | galaxy_tags: [] 52 | # List tags for your role here, one per line. A tag is a keyword that describes 53 | # and categorizes the role. Users find roles by searching for tags. Be sure to 54 | # remove the '[]' above, if you add tags to this list. 55 | # 56 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 57 | # Maximum 20 tags per role. 58 | 59 | dependencies: [] 60 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 61 | # if you add dependencies to this list. 62 | collections: 63 | - operator_sdk.util 64 | - community.kubernetes 65 | -------------------------------------------------------------------------------- /roles/loki/tasks/loki-frontend.yaml: -------------------------------------------------------------------------------- 1 | - name: Loki Operator | Frontend | StatefulSet 2 | community.kubernetes.k8s: 3 | state: '{{"present" if service.mode == "cluster" else "absent"}}' 4 | definition: "{{ lookup('template', 'frontend/statefulset.yaml') | from_yaml }}" 5 | 6 | - name: Loki Operator | Frontend | Service 7 | community.kubernetes.k8s: 8 | state: '{{"present" if service.mode == "cluster" else "absent"}}' 9 | definition: "{{ lookup('template', 'frontend/service.yaml') | from_yaml }}" 10 | 11 | - name: Loki Operator | Frontend | ServiceMonitor 12 | community.kubernetes.k8s: 13 | state: '{{"present" if service.mode == "cluster" and servicemonitor else "absent"}}' 14 | definition: "{{ lookup('template', 'frontend/servicemonitor.yaml') | from_yaml }}" 15 | when: '{{servicemonitor is defined}}' -------------------------------------------------------------------------------- /roles/loki/tasks/loki-gateway.yaml: -------------------------------------------------------------------------------- 1 | - name: Loki Operator | Gateway | ConfigMap 2 | community.kubernetes.k8s: 3 | state: '{{"present" if service.mode == "cluster" else "absent"}}' 4 | definition: "{{ lookup('template', 'gateway/configmap.yaml') | from_yaml }}" 5 | 6 | - name: Loki Operator | Gateway | Deployment 7 | community.kubernetes.k8s: 8 | state: '{{"present" if service.mode == "cluster" else "absent"}}' 9 | definition: "{{ lookup('template', 'gateway/deployment.yaml') | from_yaml }}" 10 | 11 | - name: Loki Operator | Gateway | Service 12 | community.kubernetes.k8s: 13 | state: '{{"present" if service.mode == "cluster" else "absent"}}' 14 | definition: "{{ lookup('template', 'gateway/service.yaml') | from_yaml }}" -------------------------------------------------------------------------------- /roles/loki/tasks/loki-system.yaml: -------------------------------------------------------------------------------- 1 | - name: Loki Operator | Loki | StatefulSet 2 | community.kubernetes.k8s: 3 | state: '{{"present" if service.mode == "single" or service.cluster.type == "ha" else "absent"}}' 4 | definition: "{{ lookup('template', 'loki/statefulset.yaml') | from_yaml }}" 5 | 6 | - name: Loki Operator | Loki | Service 7 | community.kubernetes.k8s: 8 | state: '{{"present" if service.mode == "single" or service.cluster.type == "ha" else "absent"}}' 9 | definition: "{{ lookup('template', 'loki/service.yaml') | from_yaml }}" 10 | 11 | - name: Loki Operator | Loki | Service Headless 12 | community.kubernetes.k8s: 13 | state: '{{"present" if service.mode == "single" or service.cluster.type == "ha" else "absent"}}' 14 | definition: "{{ lookup('template', 'loki/headless.yaml') | from_yaml }}" 15 | 16 | - name: Loki Operator | Loki | ServiceMonitor 17 | community.kubernetes.k8s: 18 | state: '{{"present" if servicemonitor else "absent"}}' 19 | definition: "{{ lookup('template', 'loki/servicemonitor.yaml') | from_yaml }}" 20 | when: '{{servicemonitor is defined}}' -------------------------------------------------------------------------------- /roles/loki/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for Loki 3 | - include_tasks: redis.yaml 4 | 5 | - include_tasks: memcached.yaml 6 | 7 | - include_tasks: loki-frontend.yaml 8 | when: '{{service.mode == "cluster"}}' 9 | 10 | - include_tasks: loki-gateway.yaml 11 | when: '{{service.mode == "cluster"}}' 12 | 13 | - include_tasks: loki-system.yaml 14 | when: '{{service.mode == "single" or service.cluster is defined and service.cluster.type == "ha"}}' 15 | 16 | - name: Loki Operator | Loki | ConfigMap 17 | community.kubernetes.k8s: 18 | definition: "{{ lookup('template', 'loki.config.yaml') | from_yaml }}" 19 | merge_type: json 20 | notify: 21 | - Scale down single loki 22 | - Scale up single loki 23 | - Scale down ha loki frontend 24 | - Scale down ha loki system 25 | - Scale up ha loki system 26 | - Scale up ha loki frontend 27 | #- Scale down microservices loki 28 | #- Scale up microservices loki -------------------------------------------------------------------------------- /roles/loki/tasks/memcached.yaml: -------------------------------------------------------------------------------- 1 | - name: Loki Operator | Memcached | ServiceAccount 2 | community.kubernetes.k8s: 3 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" else "absent"}}' 4 | definition: "{{ lookup('template', 'memcached/serviceaccount.yaml') | from_yaml }}" 5 | 6 | - name: Loki Operator | Memcached | StatefulSet 7 | community.kubernetes.k8s: 8 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" else "absent"}}' 9 | definition: "{{ lookup('template', 'memcached/statefulset.yaml') | from_yaml }}" 10 | 11 | - name: Loki Operator | Memcached | Service 12 | community.kubernetes.k8s: 13 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" else "absent"}}' 14 | definition: "{{ lookup('template', 'memcached/service.yaml') | from_yaml }}" 15 | 16 | - name: Loki Operator | Memcached | Metrics 17 | community.kubernetes.k8s: 18 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" and servicemonitor else "absent"}}' 19 | definition: "{{ lookup('template', 'memcached/metrics.yaml') | from_yaml }}" 20 | when: '{{servicemonitor is defined}}' 21 | 22 | - name: Loki Operator | Memcached | ServicesMonitor 23 | ignore_errors: yes 24 | community.kubernetes.k8s: 25 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" and servicemonitor else "absent"}}' 26 | definition: "{{ lookup('template', 'memcached/servicemonitor.yaml') | from_yaml }}" 27 | when: '{{servicemonitor is defined}}' 28 | -------------------------------------------------------------------------------- /roles/loki/tasks/redis.yaml: -------------------------------------------------------------------------------- 1 | - name: Loki Operator | Reids Cache | PersistentVolumeClaims 2 | community.kubernetes.k8s: 3 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" else "absent"}}' 4 | definition: "{{ lookup('template', 'redis/pvc.yaml') | from_yaml }}" 5 | 6 | - name: Loki Operator | Reids Cache | ConfigMap 7 | community.kubernetes.k8s: 8 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" else "absent"}}' 9 | definition: "{{ lookup('template', 'redis/configmap.yaml') | from_yaml }}" 10 | 11 | - name: Loki Operator | Reids Cache | StatefulSet 12 | community.kubernetes.k8s: 13 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" else "absent"}}' 14 | definition: "{{ lookup('template', 'redis/statefulset.yaml') | from_yaml }}" 15 | 16 | - name: Loki Operator | Reids Cache | Service 17 | community.kubernetes.k8s: 18 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" else "absent"}}' 19 | definition: "{{ lookup('template', 'redis/service.yaml') | from_yaml }}" 20 | 21 | - name: Loki Operator | Redis Cache | Metrics 22 | community.kubernetes.k8s: 23 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" and servicemonitor else "absent"}}' 24 | definition: "{{ lookup('template', 'redis/metrics.yaml') | from_yaml }}" 25 | when: '{{servicemonitor is defined}}' 26 | 27 | - name: Loki Operator | Redis Cache | ServicesMonitor 28 | ignore_errors: yes 29 | community.kubernetes.k8s: 30 | state: '{{"present" if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" and servicemonitor else "absent"}}' 31 | definition: "{{ lookup('template', 'redis/servicemonitor.yaml') | from_yaml }}" 32 | when: '{{servicemonitor is defined}}' -------------------------------------------------------------------------------- /roles/loki/templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CloudmindsRobot/loki-operator/24c436ee740ccd22fa663e11ea908113e360a499/roles/loki/templates/.placeholder -------------------------------------------------------------------------------- /roles/loki/templates/frontend/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-frontend' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-frontend 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - name: http-3100 13 | port: 3100 14 | protocol: TCP 15 | targetPort: 3100 16 | - name: grpc-9095 17 | port: 9095 18 | protocol: TCP 19 | targetPort: 9095 20 | selector: 21 | plugins.cloudminds.com/loki: loki-frontend 22 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 23 | -------------------------------------------------------------------------------- /roles/loki/templates/frontend/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-frontend' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-frontend 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 9 | spec: 10 | jobLabel: jobLabel 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-frontend 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 15 | endpoints: 16 | - port: http-3100 17 | path: /metrics 18 | relabelings: 19 | - sourceLabels: [__meta_kubernetes_pod_node_name] 20 | targetLabel: host 21 | namespaceSelector: 22 | matchNames: 23 | - '{{ansible_operator_meta.namespace}}' -------------------------------------------------------------------------------- /roles/loki/templates/frontend/statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-frontend' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-frontend 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 9 | spec: 10 | replicas: {{ service.cluster.frontend.replicas }} 11 | serviceName: '{{ansible_operator_meta.name}}-loki-frontend' 12 | selector: 13 | matchLabels: 14 | plugins.cloudminds.com/loki: loki-frontend 15 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 16 | template: 17 | metadata: 18 | labels: 19 | plugins.cloudminds.com/loki: loki-frontend 20 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 21 | spec: 22 | affinity: 23 | podAntiAffinity: 24 | preferredDuringSchedulingIgnoredDuringExecution: 25 | - podAffinityTerm: 26 | labelSelector: 27 | matchLabels: 28 | plugins.cloudminds.com/loki: loki-frontend 29 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-frontend' 30 | namespaces: 31 | - '{{ansible_operator_meta.namespace}}' 32 | topologyKey: kubernetes.io/hostname 33 | weight: 1 34 | nodeAffinity: 35 | containers: 36 | - name: loki-frontend 37 | image: '{{service.cluster.frontend.image}}:{{version}}' 38 | args: 39 | - "-config.file=/etc/loki/local-config.yaml" 40 | - "-target=query-frontend" 41 | imagePullPolicy: IfNotPresent 42 | ports: 43 | - containerPort: 3100 44 | name: http-3100 45 | protocol: TCP 46 | - containerPort: 9095 47 | name: http-9095 48 | protocol: TCP 49 | resources: 50 | limits: 51 | cpu: "4" 52 | memory: 8Gi 53 | requests: 54 | cpu: "1" 55 | memory: "2Gi" 56 | volumeMounts: 57 | - name: loki-config 58 | mountPath: /etc/loki/ 59 | readOnly: true 60 | livenessProbe: 61 | failureThreshold: 3 62 | initialDelaySeconds: 120 63 | periodSeconds: 30 64 | successThreshold: 1 65 | tcpSocket: 66 | port: 3100 67 | timeoutSeconds: 5 68 | securityContext: 69 | capabilities: 70 | add: [] 71 | drop: 72 | - ALL 73 | readOnlyRootFilesystem: false 74 | runAsNonRoot: true 75 | runAsUser: 1001 76 | securityContext: 77 | fsGroup: 100 78 | volumes: 79 | - name: loki-config 80 | configMap: 81 | name: '{{ansible_operator_meta.name}}-loki-conf' -------------------------------------------------------------------------------- /roles/loki/templates/gateway/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | labels: 5 | plugins.cloudminds.com/loki: loki-gateway 6 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 7 | name: '{{ ansible_operator_meta.name }}-loki-gateway-conf' 8 | namespace: '{{ ansible_operator_meta.namespace }}' 9 | data: 10 | nginx.conf: |- 11 | worker_processes 8; 12 | error_log /dev/stderr; 13 | pid /tmp/nginx.pid; 14 | worker_rlimit_nofile 8192; 15 | events { 16 | worker_connections 4096; 17 | } 18 | http { 19 | client_max_body_size 1024M; 20 | resolver kube-dns.kube-system.svc.cluster.local valid=30s; 21 | default_type application/octet-stream; 22 | access_log /dev/stdout; 23 | sendfile on; 24 | tcp_nopush on; 25 | server { 26 | listen 3100 default_server; 27 | location = / { 28 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100/ready; 29 | } 30 | location = /ring { 31 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 32 | } 33 | location = /api/prom/push { 34 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 35 | } 36 | location = /api/prom/tail { 37 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 38 | proxy_set_header Upgrade $http_upgrade; 39 | proxy_set_header Connection "upgrade"; 40 | } 41 | location ~ /api/prom/.* { 42 | proxy_pass http://{{ansible_operator_meta.name}}-loki-frontend.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 43 | } 44 | location = /loki/api/v1/push { 45 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 46 | } 47 | location = /loki/api/v1/tail { 48 | proxy_pass http://{{ansible_operator_meta.name}}-loki-system.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 49 | proxy_set_header Upgrade $http_upgrade; 50 | proxy_set_header Connection "upgrade"; 51 | } 52 | location ~ /loki/api/.* { 53 | proxy_pass http://{{ansible_operator_meta.name}}-loki-frontend.{{ansible_operator_meta.namespace}}.svc.cluster.local:3100$request_uri; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /roles/loki/templates/gateway/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-gateway' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-gateway 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 9 | spec: 10 | replicas: {{ service.cluster.gateway.replicas }} 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-gateway 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 15 | strategy: 16 | rollingUpdate: 17 | maxSurge: 25% 18 | maxUnavailable: 25% 19 | type: RollingUpdate 20 | template: 21 | metadata: 22 | labels: 23 | plugins.cloudminds.com/loki: loki-gateway 24 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 25 | spec: 26 | affinity: 27 | podAffinity: 28 | podAntiAffinity: 29 | preferredDuringSchedulingIgnoredDuringExecution: 30 | - podAffinityTerm: 31 | labelSelector: 32 | matchLabels: 33 | plugins.cloudminds.com/loki: loki-gateway 34 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 35 | namespaces: 36 | - '{{ansible_operator_meta.namespace}}' 37 | topologyKey: kubernetes.io/hostname 38 | weight: 1 39 | nodeAffinity: 40 | containers: 41 | - image: '{{service.cluster.gateway.image}}:{{service.cluster.gateway.tag}}' 42 | imagePullPolicy: IfNotPresent 43 | livenessProbe: 44 | failureThreshold: 3 45 | initialDelaySeconds: 120 46 | periodSeconds: 30 47 | successThreshold: 1 48 | tcpSocket: 49 | port: 3100 50 | timeoutSeconds: 5 51 | name: loki-gateway 52 | ports: 53 | - containerPort: 3100 54 | name: http-3100 55 | protocol: TCP 56 | resources: 57 | limits: 58 | cpu: "1" 59 | memory: 2Gi 60 | requests: 61 | cpu: 200m 62 | memory: 256Mi 63 | volumeMounts: 64 | - mountPath: /etc/nginx/nginx.conf 65 | name: loki-gateway-config 66 | readOnly: true 67 | subPath: nginx.conf 68 | dnsPolicy: ClusterFirst 69 | restartPolicy: Always 70 | volumes: 71 | - configMap: 72 | defaultMode: 420 73 | name: '{{ ansible_operator_meta.name }}-loki-gateway-conf' 74 | name: loki-gateway-config -------------------------------------------------------------------------------- /roles/loki/templates/gateway/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-gateway' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-gateway 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' 9 | spec: 10 | type: NodePort 11 | ports: 12 | - name: http-3100 13 | port: 3100 14 | protocol: TCP 15 | targetPort: 3100 16 | selector: 17 | plugins.cloudminds.com/loki: loki-gateway 18 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-gateway' -------------------------------------------------------------------------------- /roles/loki/templates/loki.config.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: '{{ansible_operator_meta.name}}-loki-conf' 6 | namespace: '{{ansible_operator_meta.namespace}}' 7 | labels: 8 | plugins.cloudminds.com/loki: loki-conf 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-conf' 10 | data: 11 | local-config.yaml: > 12 | auth_enabled: {{multitenancy | lower }} 13 | 14 | server: 15 | http_listen_port: 3100 16 | http_listen_address: 0.0.0.0 17 | grpc_listen_port: 9095 18 | grpc_listen_address: 0.0.0.0 19 | grpc_server_max_recv_msg_size: 10000000 20 | grpc_server_max_send_msg_size: 10000000 21 | grpc_server_max_concurrent_streams: 0 22 | 23 | {% if service.mode == "cluster" %} 24 | {% if service.cluster.ring.type == "memberlist" %} 25 | memberlist: 26 | join_members: 27 | {% for num in range(service.cluster.loki.replicas) %} 28 | - {{ansible_operator_meta.name}}-loki-system-{{num}}.{{ansible_operator_meta.name}}-loki-system-headless.{{ansible_operator_meta.namespace}}.svc.cluster.local 29 | {% endfor %} 30 | dead_node_reclaim_time: 30s 31 | gossip_to_dead_nodes_time: 15s 32 | left_ingesters_timeout: 30s 33 | bind_addr: ['0.0.0.0'] 34 | bind_port: 7946 35 | {% endif %} 36 | {% endif %} 37 | 38 | querier: 39 | query_ingesters_within: 2h 40 | 41 | {% if service.mode == "cluster" %} 42 | frontend: 43 | compress_responses: true 44 | log_queries_longer_than: 5s 45 | downstream_url: http://{{ansible_operator_meta.name}}-loki-system:3100 46 | {% endif %} 47 | 48 | query_range: 49 | split_queries_by_interval: 24h 50 | align_queries_with_step: true 51 | max_retries: 5 52 | {% if cacheconfig is defined %} 53 | {% if cacheconfig.enabled %} 54 | results_cache: 55 | cache: 56 | {{cacheconfig.type}}: 57 | {% if cacheconfig.type == "redis" and cacheconfig.redis.external is defined %} 58 | endpoint: {{cacheconfig.redis.external.address}}:{{cacheconfig.redis.external.port}} 59 | {% elif cacheconfig.type == "redis" %} 60 | endpoint: {{ansible_operator_meta.name}}-redis-0.{{ansible_operator_meta.name}}-redis.{{ansible_operator_meta.namespace}}.svc.cluster.local:6379 61 | {% endif %} 62 | expiration: {{cacheconfig.expiration}} 63 | {% if cacheconfig.type == "memcached" %} 64 | memcached_client: 65 | {% if cacheconfig.memcached.external is defined %} 66 | host: {{cacheconfig.memcached.external.address}} 67 | service: memcached 68 | {% else %} 69 | host: {{ansible_operator_meta.name}}-memcached.{{ansible_operator_meta.namespace}}.svc.cluster.local 70 | service: memcached 71 | {% endif %} 72 | {% endif %} 73 | cache_results: true 74 | {% endif %} 75 | {% endif %} 76 | 77 | ingester: 78 | lifecycler: 79 | join_after: 60s 80 | observe_period: 5s 81 | ring: 82 | {% if service.mode == "cluster" %} 83 | replication_factor: {{service.cluster.replication_factor}} 84 | kvstore: 85 | store: {{service.cluster.ring.type}} 86 | {% else %} 87 | replication_factor: 1 88 | kvstore: 89 | store: inmemory 90 | {% endif %} 91 | final_sleep: 0s 92 | chunk_idle_period: 1h 93 | max_chunk_age: 1h 94 | chunk_retain_period: 1m 95 | max_transfer_retries: 1 96 | chunk_encoding: snappy 97 | chunk_target_size: 0 98 | chunk_block_size: 262144 99 | 100 | ingester_client: 101 | grpc_client_config: 102 | max_recv_msg_size: 20971520 103 | remote_timeout: 1s 104 | 105 | schema_config: 106 | configs: 107 | - from: 2021-04-25 108 | store: {{schemaconfig.index}} 109 | {% if schemaconfig.chunk == "s3" %} 110 | object_store: aws 111 | {% elif schemaconfig.chunk == "filesystem" %} 112 | object_store: filesystem 113 | {% endif %} 114 | schema: v11 115 | index: 116 | prefix: index_ 117 | period: 24h 118 | 119 | {% if service.mode == "single" and schemaconfig.index == "boltdb-shipper" %} 120 | compactor: 121 | working_directory: /loki/compactor 122 | {% if storageconfig.boltdb_shipper.shared_store == "s3" %} 123 | shared_store: aws 124 | {% elif storageconfig.boltdb_shipper.shared_store == "filesystem" %} 125 | shared_store: filesystem 126 | {% endif %} 127 | {% endif %} 128 | 129 | storage_config: 130 | {% if storageconfig.boltdb_shipper is defined %} 131 | boltdb_shipper: 132 | {% if storageconfig.boltdb_shipper.shared_store == "s3" %} 133 | shared_store: aws 134 | {% elif storageconfig.boltdb_shipper.shared_store == "filesystem" %} 135 | shared_store: filesystem 136 | {% endif %} 137 | active_index_directory: /loki/index 138 | cache_location: /loki/boltdb-cache 139 | {% endif %} 140 | 141 | {% if storageconfig.boltdb_shipper.shared_store == "filesystem" or schemaconfig.chunk == "filesystem" %} 142 | filesystem: 143 | directory: {{storageconfig.filesystem.directory}} 144 | {% endif %} 145 | 146 | {% if storageconfig.s3 is defined %} 147 | aws: 148 | s3: s3://{{storageconfig.s3.access_key}}:{{storageconfig.s3.secret_key}}@{{storageconfig.s3.address}}/{{storageconfig.s3.bucket}} 149 | s3forcepathstyle: true 150 | insecure: true 151 | {% endif %} 152 | {% if cacheconfig is defined %} 153 | {% if cacheconfig.enabled %} 154 | index_queries_cache_config: 155 | {{cacheconfig.type}}: 156 | {% if cacheconfig.type == "redis" and cacheconfig.redis.external is defined %} 157 | endpoint: {{cacheconfig.redis.external.address}}:{{cacheconfig.redis.external.port}} 158 | {% elif cacheconfig.type == "redis" %} 159 | endpoint: {{ansible_operator_meta.name}}-redis-0.{{ansible_operator_meta.name}}-redis.{{ansible_operator_meta.namespace}}.svc.cluster.local:6379 160 | {% endif %} 161 | expiration: {{cacheconfig.expiration}} 162 | {% if cacheconfig.type == "memcached" %} 163 | memcached_client: 164 | {% if cacheconfig.memcached.external is defined %} 165 | host: {{cacheconfig.memcached.external.address}} 166 | service: memcached 167 | {% else %} 168 | host: {{ansible_operator_meta.name}}-memcached.{{ansible_operator_meta.namespace}}.svc.cluster.local 169 | service: memcached 170 | {% endif %} 171 | {% endif %} 172 | {% endif %} 173 | {% endif %} 174 | 175 | {% if cacheconfig is defined %} 176 | {% if cacheconfig.enabled %} 177 | chunk_store_config: 178 | chunk_cache_config: 179 | {{cacheconfig.type}}: 180 | {% if cacheconfig.type == "redis" and cacheconfig.redis.external is defined %} 181 | endpoint: {{cacheconfig.redis.external.address}}:{{cacheconfig.redis.external.port}} 182 | {% elif cacheconfig.type == "redis" %} 183 | endpoint: {{ansible_operator_meta.name}}-redis-0.{{ansible_operator_meta.name}}-redis.{{ansible_operator_meta.namespace}}.svc.cluster.local:6379 184 | {% endif %} 185 | expiration: {{cacheconfig.expiration}} 186 | {% if cacheconfig.type == "memcached" %} 187 | memcached_client: 188 | {% if cacheconfig.memcached.external is defined %} 189 | host: {{cacheconfig.memcached.external.address}} 190 | service: memcached 191 | {% else %} 192 | host: {{ansible_operator_meta.name}}-memcached.{{ansible_operator_meta.namespace}}.svc.cluster.local 193 | service: memcached 194 | {% endif %} 195 | {% endif %} 196 | max_look_back_period: 0 197 | write_dedupe_cache_config: 198 | {{cacheconfig.type}}: 199 | {% if cacheconfig.type == "redis" and cacheconfig.redis.external is defined %} 200 | endpoint: {{cacheconfig.redis.external.address}}:{{cacheconfig.redis.external.port}} 201 | {% elif cacheconfig.type == "redis" %} 202 | endpoint: {{ansible_operator_meta.name}}-redis-0.{{ansible_operator_meta.name}}-redis.{{ansible_operator_meta.namespace}}.svc.cluster.local:6379 203 | {% endif %} 204 | expiration: {{cacheconfig.expiration}} 205 | {% if cacheconfig.type == "memcached" %} 206 | memcached_client: 207 | {% if cacheconfig.memcached.external is defined %} 208 | host: {{cacheconfig.memcached.external.address}} 209 | service: memcached 210 | {% else %} 211 | host: {{ansible_operator_meta.name}}-memcached.{{ansible_operator_meta.namespace}}.svc.cluster.local 212 | service: memcached 213 | {% endif %} 214 | {% endif %} 215 | {% endif %} 216 | {% endif %} 217 | 218 | {% if ruler is defined %} 219 | {% if ruler.enabled %} 220 | ruler: 221 | storage: 222 | type: {{ruler.storage}} 223 | {% if ruler.storage == "s3" %} 224 | s3: 225 | s3: s3://{{storageconfig.s3.access_key}}:{{storageconfig.s3.secret_key}}@{{storageconfig.s3.address}}/{{ruler.s3.bucket}} 226 | s3forcepathstyle: true 227 | insecure: true 228 | http_config: 229 | insecure_skip_verify: true 230 | {% endif %} 231 | enable_api: true 232 | enable_alertmanager_v2: true 233 | alertmanager_url: "{{ruler.alertmanager}}" 234 | ring: 235 | kvstore: 236 | {% if service.mode == "cluster" and ruler.ring is defined %} 237 | store: {{ruler.ring}} 238 | {% else %} 239 | store: inmemory 240 | {% endif %} 241 | {% endif %} 242 | {% endif %} 243 | 244 | limits_config: 245 | enforce_metric_name: false 246 | reject_old_samples: true 247 | reject_old_samples_max_age: 168h 248 | ingestion_rate_mb: 64 249 | ingestion_burst_size_mb: 128 250 | max_entries_limit_per_query: 50000 251 | 252 | table_manager: 253 | chunk_tables_provisioning: 254 | inactive_read_throughput: 0 255 | inactive_write_throughput: 0 256 | provisioned_read_throughput: 0 257 | provisioned_write_throughput: 0 258 | index_tables_provisioning: 259 | inactive_read_throughput: 0 260 | inactive_write_throughput: 0 261 | provisioned_read_throughput: 0 262 | provisioned_write_throughput: 0 263 | retention_deletes_enabled: false 264 | retention_period: 0 -------------------------------------------------------------------------------- /roles/loki/templates/loki/headless.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: '{{ansible_operator_meta.name}}-loki-system-headless' 6 | namespace: '{{ansible_operator_meta.namespace}}' 7 | labels: 8 | plugins.cloudminds.com/loki: loki-system 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 10 | type: ClusterIP 11 | spec: 12 | clusterIP: None 13 | ports: 14 | - name: http-3100 15 | port: 3100 16 | protocol: TCP 17 | targetPort: 3100 18 | - name: grpc-9095 19 | port: 9095 20 | protocol: TCP 21 | targetPort: 9095 22 | {% if service.mode == "cluster" and service.cluster.ring.type == "memberlist" %} 23 | - name: tcp-7946 24 | port: 7946 25 | protocol: TCP 26 | targetPort: 7946 27 | {% endif %} 28 | selector: 29 | plugins.cloudminds.com/loki: loki-system 30 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 31 | -------------------------------------------------------------------------------- /roles/loki/templates/loki/service.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: '{{ansible_operator_meta.name}}-loki-system' 6 | namespace: '{{ansible_operator_meta.namespace}}' 7 | labels: 8 | plugins.cloudminds.com/loki: loki-system 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 10 | type: ClusterIP 11 | spec: 12 | ports: 13 | - name: http-3100 14 | port: 3100 15 | protocol: TCP 16 | targetPort: 3100 17 | - name: grpc-9095 18 | port: 9095 19 | protocol: TCP 20 | targetPort: 9095 21 | {% if service.mode == "cluster" and service.cluster.ring.type == "memberlist" %} 22 | - name: tcp-7946 23 | port: 7946 24 | protocol: TCP 25 | targetPort: 7946 26 | {% endif %} 27 | selector: 28 | plugins.cloudminds.com/loki: loki-system 29 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 30 | -------------------------------------------------------------------------------- /roles/loki/templates/loki/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-loki-system' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-system 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 9 | spec: 10 | jobLabel: jobLabel 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-system 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 15 | endpoints: 16 | - port: http-3100 17 | path: /metrics 18 | relabelings: 19 | - sourceLabels: [__meta_kubernetes_pod_node_name] 20 | targetLabel: host 21 | namespaceSelector: 22 | matchNames: 23 | - '{{ansible_operator_meta.namespace}}' -------------------------------------------------------------------------------- /roles/loki/templates/loki/statefulset.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: '{{ansible_operator_meta.name}}-loki-system' 6 | namespace: '{{ansible_operator_meta.namespace}}' 7 | labels: 8 | plugins.cloudminds.com/loki: loki-system 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 10 | spec: 11 | {% if service.mode == "cluster" %} 12 | {% if service.cluster.type == "ha" %} 13 | replicas: {{ service.cluster.loki.replicas }} 14 | {% endif %} 15 | {% else %} 16 | replicas: 1 17 | {% endif %} 18 | serviceName: '{{ansible_operator_meta.name}}-loki-system-headless' 19 | selector: 20 | matchLabels: 21 | plugins.cloudminds.com/loki: loki-system 22 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 23 | template: 24 | metadata: 25 | labels: 26 | plugins.cloudminds.com/loki: loki-system 27 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 28 | spec: 29 | affinity: 30 | podAffinity: 31 | 32 | podAntiAffinity: 33 | preferredDuringSchedulingIgnoredDuringExecution: 34 | - podAffinityTerm: 35 | labelSelector: 36 | matchLabels: 37 | plugins.cloudminds.com/loki: loki-system 38 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 39 | namespaces: 40 | - '{{ansible_operator_meta.namespace}}' 41 | topologyKey: kubernetes.io/hostname 42 | weight: 1 43 | nodeAffinity: 44 | containers: 45 | - name: loki-system 46 | {% if service.mode == "cluster" %} 47 | image: '{{service.cluster.loki.image | default('grafana/loki')}}:{{version | default('2.2.1')}}' 48 | {% elif service.mode == "single" %} 49 | image: '{{service.single.loki.image | default('grafana/loki')}}:{{version | default('2.2.1')}}' 50 | {% endif %} 51 | imagePullPolicy: IfNotPresent 52 | ports: 53 | - containerPort: 3100 54 | name: http-3100 55 | protocol: TCP 56 | - containerPort: 9095 57 | name: grpc-9095 58 | protocol: TCP 59 | {% if service.mode == "cluster" %} 60 | {% if service.cluster.ring.type == "memberlist" %} 61 | - containerPort: 7946 62 | name: tcp-7946 63 | protocol: TCP 64 | {% endif %} 65 | {% endif %} 66 | resources: 67 | limits: 68 | cpu: "8" 69 | memory: 24Gi 70 | requests: 71 | cpu: "4" 72 | memory: "8Gi" 73 | volumeMounts: 74 | - name: loki-config 75 | mountPath: /etc/loki/ 76 | readOnly: true 77 | - name: data 78 | mountPath: /loki/ 79 | livenessProbe: 80 | failureThreshold: 3 81 | initialDelaySeconds: 120 82 | periodSeconds: 30 83 | successThreshold: 1 84 | tcpSocket: 85 | port: 3100 86 | timeoutSeconds: 5 87 | securityContext: 88 | capabilities: 89 | add: [] 90 | drop: 91 | - ALL 92 | readOnlyRootFilesystem: false 93 | runAsNonRoot: true 94 | runAsUser: 1001 95 | securityContext: 96 | fsGroup: 100 97 | volumes: 98 | - name: loki-config 99 | configMap: 100 | name: '{{ansible_operator_meta.name}}-loki-conf' 101 | volumeClaimTemplates: 102 | - metadata: 103 | name: data 104 | labels: 105 | plugins.cloudminds.com/loki: loki-system 106 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-loki-system' 107 | spec: 108 | accessModes: ["ReadWriteOnce"] 109 | resources: 110 | requests: 111 | storage: "100Gi" -------------------------------------------------------------------------------- /roles/loki/templates/memcached/metrics.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-memcached-metrics' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-memcached 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - name: metrics 13 | port: 9150 14 | targetPort: metrics 15 | selector: 16 | plugins.cloudminds.com/loki: loki-memcached 17 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 18 | -------------------------------------------------------------------------------- /roles/loki/templates/memcached/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-memcached' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-memcached 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 9 | spec: 10 | type: ClusterIP 11 | clusterIP: None 12 | ports: 13 | - name: memcached 14 | port: 11211 15 | targetPort: memcached 16 | nodePort: null 17 | selector: 18 | plugins.cloudminds.com/loki: loki-memcached 19 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' -------------------------------------------------------------------------------- /roles/loki/templates/memcached/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: '{{ansible_operator_meta.name}}-memcached' 5 | namespace: '{{ansible_operator_meta.namespace}}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-memcached 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' -------------------------------------------------------------------------------- /roles/loki/templates/memcached/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: '{{ ansible_operator_meta.name }}-memcached' 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-memcached 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 9 | spec: 10 | jobLabel: jobLabel 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-memcached 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 15 | endpoints: 16 | - port: metrics 17 | path: /metrics 18 | relabelings: 19 | - sourceLabels: [__meta_kubernetes_pod_node_name] 20 | targetLabel: host 21 | namespaceSelector: 22 | matchNames: 23 | - '{{ ansible_operator_meta.namespace }}' -------------------------------------------------------------------------------- /roles/loki/templates/memcached/statefulset.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: '{{ansible_operator_meta.name}}-memcached' 6 | namespace: '{{ansible_operator_meta.namespace}}' 7 | labels: 8 | plugins.cloudminds.com/loki: loki-memcached 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 10 | spec: 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-memcached 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 15 | replicas: 1 16 | serviceName: '{{ansible_operator_meta.name}}-memcached' 17 | template: 18 | metadata: 19 | labels: 20 | plugins.cloudminds.com/loki: loki-memcached 21 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 22 | spec: 23 | affinity: 24 | podAffinity: 25 | podAntiAffinity: 26 | preferredDuringSchedulingIgnoredDuringExecution: 27 | - podAffinityTerm: 28 | labelSelector: 29 | matchLabels: 30 | plugins.cloudminds.com/loki: loki-memcached 31 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 32 | namespaces: 33 | - '{{ansible_operator_meta.namespace}}' 34 | topologyKey: kubernetes.io/hostname 35 | weight: 1 36 | nodeAffinity: 37 | securityContext: 38 | fsGroup: 1001 39 | runAsUser: 1001 40 | containers: 41 | - name: memcached 42 | lifecycle: 43 | preStop: 44 | exec: 45 | command: ["/bin/sh", "-c", "/usr/bin/pkill -10 memcached ; sleep 60s"] 46 | image: '{{cacheconfig.memcached.image | default('docker.io/bitnami/memcached')}}:{{cacheconfig.memcached.tag | default('1.6.9-debian-10-r114')}}' 47 | imagePullPolicy: "IfNotPresent" 48 | args: 49 | - /run.sh 50 | - -e/cache-state/memory_file 51 | ports: 52 | - name: memcached 53 | containerPort: 11211 54 | livenessProbe: 55 | tcpSocket: 56 | port: memcached 57 | initialDelaySeconds: 30 58 | timeoutSeconds: 5 59 | failureThreshold: 6 60 | readinessProbe: 61 | tcpSocket: 62 | port: memcached 63 | initialDelaySeconds: 5 64 | timeoutSeconds: 3 65 | periodSeconds: 5 66 | resources: 67 | limits: 68 | cpu: "1" 69 | memory: 8Gi 70 | requests: 71 | cpu: 250m 72 | memory: 256Mi 73 | securityContext: 74 | readOnlyRootFilesystem: false 75 | volumeMounts: 76 | - name: data 77 | mountPath: /cache-state 78 | - name: tmp 79 | mountPath: /tmp 80 | {% if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "memcached" and servicemonitor %} 81 | - name: metrics 82 | image: docker.io/bitnami/memcached-exporter:0.8.0-debian-10-r105 83 | imagePullPolicy: "IfNotPresent" 84 | ports: 85 | - name: metrics 86 | containerPort: 9150 87 | livenessProbe: 88 | httpGet: 89 | path: /metrics 90 | port: metrics 91 | initialDelaySeconds: 15 92 | timeoutSeconds: 5 93 | readinessProbe: 94 | httpGet: 95 | path: /metrics 96 | port: metrics 97 | initialDelaySeconds: 5 98 | timeoutSeconds: 1 99 | resources: 100 | limits: {} 101 | requests: {} 102 | {% endif %} 103 | volumes: 104 | - name: tmp 105 | emptyDir: {} 106 | volumeClaimTemplates: 107 | - metadata: 108 | name: data 109 | labels: 110 | plugins.cloudminds.com/loki: loki-memcached 111 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-memcached' 112 | spec: 113 | accessModes: 114 | - "ReadWriteOnce" 115 | resources: 116 | requests: 117 | storage: "10Gi" -------------------------------------------------------------------------------- /roles/loki/templates/redis/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | labels: 5 | plugins.cloudminds.com/loki: loki-redis 6 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 7 | name: '{{ ansible_operator_meta.name }}-redis-conf' 8 | namespace: '{{ ansible_operator_meta.namespace }}' 9 | data: 10 | redis.conf: |- 11 | bind 0.0.0.0 12 | port 6379 13 | tcp-backlog 511 14 | tcp-keepalive 300 15 | loglevel notice 16 | databases 2 17 | save 900 1 18 | save 300 10 19 | save 60 10000 20 | dir /var/lib/redis 21 | appendonly no 22 | maxmemory 858993459 -------------------------------------------------------------------------------- /roles/loki/templates/redis/metrics.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | plugins.cloudminds.com/loki: loki-redis 6 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 7 | name: '{{ ansible_operator_meta.name }}-redis-metrics' 8 | namespace: '{{ ansible_operator_meta.namespace }}' 9 | spec: 10 | type: ClusterIP 11 | selector: 12 | plugins.cloudminds.com/loki: loki-redis 13 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 14 | ports: 15 | - port: 9121 16 | name: metrics 17 | protocol: TCP 18 | targetPort: 9121 -------------------------------------------------------------------------------- /roles/loki/templates/redis/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: '{{ ansible_operator_meta.name }}-redis-data' 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-redis 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 9 | spec: 10 | resources: 11 | requests: 12 | storage: 10Gi 13 | volumeMode: Filesystem 14 | accessModes: 15 | - ReadWriteOnce -------------------------------------------------------------------------------- /roles/loki/templates/redis/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | plugins.cloudminds.com/loki: loki-redis 6 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 7 | name: '{{ ansible_operator_meta.name }}-redis' 8 | namespace: '{{ ansible_operator_meta.namespace }}' 9 | spec: 10 | type: ClusterIP 11 | selector: 12 | plugins.cloudminds.com/loki: loki-redis 13 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 14 | ports: 15 | - port: 6379 16 | name: tcp-6379 17 | protocol: TCP 18 | targetPort: 6379 -------------------------------------------------------------------------------- /roles/loki/templates/redis/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: '{{ ansible_operator_meta.name }}-redis' 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | labels: 7 | plugins.cloudminds.com/loki: loki-redis 8 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 9 | spec: 10 | jobLabel: jobLabel 11 | selector: 12 | matchLabels: 13 | plugins.cloudminds.com/loki: loki-redis 14 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 15 | endpoints: 16 | - port: metrics 17 | path: /metrics 18 | relabelings: 19 | - sourceLabels: [__meta_kubernetes_pod_node_name] 20 | targetLabel: host 21 | namespaceSelector: 22 | matchNames: 23 | - '{{ ansible_operator_meta.namespace }}' -------------------------------------------------------------------------------- /roles/loki/templates/redis/statefulset.yaml: -------------------------------------------------------------------------------- 1 | #jinja2:lstrip_blocks: True 2 | kind: StatefulSet 3 | apiVersion: apps/v1 4 | metadata: 5 | labels: 6 | plugins.cloudminds.com/loki: loki-redis 7 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 8 | name: '{{ ansible_operator_meta.name }}-redis' 9 | namespace: '{{ ansible_operator_meta.namespace }}' 10 | spec: 11 | replicas: 1 12 | selector: 13 | matchLabels: 14 | plugins.cloudminds.com/loki: loki-redis 15 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 16 | serviceName: '{{ ansible_operator_meta.name }}-redis' 17 | template: 18 | metadata: 19 | labels: 20 | plugins.cloudminds.com/loki: loki-redis 21 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}-redis' 22 | spec: 23 | containers: 24 | - name: redis 25 | image: '{{cacheconfig.redis.image | default('redis')}}:{{cacheconfig.redis.tag | default('5.0.6')}}' 26 | imagePullPolicy: IfNotPresent 27 | args: ["/etc/redis.conf"] 28 | ports: 29 | - containerPort: 6379 30 | name: tcp-6379 31 | protocol: TCP 32 | resources: 33 | limits: 34 | cpu: "1" 35 | memory: 8Gi 36 | requests: 37 | cpu: 200m 38 | memory: 4Gi 39 | volumeMounts: 40 | - name: redis-config 41 | mountPath: /etc/redis.conf 42 | subPath: redis.conf 43 | readOnly: true 44 | - name: data 45 | mountPath: /var/lib/redis 46 | livenessProbe: 47 | failureThreshold: 3 48 | initialDelaySeconds: 120 49 | periodSeconds: 30 50 | successThreshold: 1 51 | tcpSocket: 52 | port: 6379 53 | timeoutSeconds: 5 54 | securityContext: 55 | capabilities: 56 | add: [] 57 | drop: 58 | - ALL 59 | readOnlyRootFilesystem: false 60 | runAsNonRoot: true 61 | runAsUser: 1001 62 | {% if cacheconfig is defined and cacheconfig.enabled and cacheconfig.type == "redis" and servicemonitor %} 63 | - name: redis-exporter 64 | image: oliver006/redis_exporter:v1.23.1-alpine 65 | imagePullPolicy: IfNotPresent 66 | args: 67 | - -redis.addr=127.0.0.1:6379 68 | ports: 69 | - containerPort: 9121 70 | name: metrics 71 | protocol: TCP 72 | resources: 73 | limits: 74 | cpu: "50m" 75 | memory: 32Mi 76 | requests: 77 | cpu: 10m 78 | memory: 16Mi 79 | {% endif %} 80 | securityContext: 81 | fsGroup: 100 82 | volumes: 83 | - name: redis-config 84 | configMap: 85 | name: '{{ ansible_operator_meta.name }}-redis-conf' 86 | - persistentVolumeClaim: 87 | claimName: '{{ ansible_operator_meta.name }}-redis-data' 88 | name: data -------------------------------------------------------------------------------- /roles/loki/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for Loki 3 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use the 'create api' subcommand to add watches to this file. 3 | - version: v1 4 | group: plugins.cloudminds.com 5 | kind: Loki 6 | playbook: playbooks/loki.yml 7 | #+kubebuilder:scaffold:watch 8 | --------------------------------------------------------------------------------