From 6b9de178f354ae41c63042b55c7f5c9418026fe0 Mon Sep 17 00:00:00 2001 From: Jon Chen Date: Wed, 8 Nov 2023 11:27:55 -0500 Subject: [PATCH 1/2] update blank service with gke and cloudrun files --- pyproject.toml | 2 +- .../{{component_name}}/requirements-test.txt | 6 + .../{{component_name}}/requirements.txt | 7 + .../{{component_name}}/skaffold.yaml | 125 ++++++++++++++++++ .../base/deployment.yaml | 50 +++++++ .../base/kustomization.yaml | 9 ++ .../base/properties.env | 2 + .../base/service.yaml | 14 ++ .../hpa/hpa.yaml | 19 +++ .../hpa/kustomization.yaml | 6 + .../cloudrun-service.yaml | 0 .../blank_service/1.0/skaffold.yaml.patch | 4 + .../modules/blank_service/copier.yaml | 48 ++++++- .../cloudrun-service.yaml | 11 ++ 14 files changed, 296 insertions(+), 7 deletions(-) create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements-test.txt create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements.txt create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/skaffold.yaml create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/deployment.yaml create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/kustomization.yaml create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/properties.env create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/service.yaml create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/hpa.yaml create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/kustomization.yaml rename solutions_builder/modules/{restful_service/components/{{component_name}}/manifests => blank_service/1.0/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}}/cloudrun-service.yaml (100%) create mode 100644 solutions_builder/modules/blank_service/1.0/skaffold.yaml.patch create mode 100644 solutions_builder/modules/restful_service/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml diff --git a/pyproject.toml b/pyproject.toml index ea2bfc87..526c3744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "solutions-builder" -version = "1.17.18" +version = "1.17.19" description = "A solution framework to generate a project with built-in structure and modules" authors = ["Jon Chen "] license = "Apache" diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements-test.txt b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements-test.txt new file mode 100644 index 00000000..1458948f --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements-test.txt @@ -0,0 +1,6 @@ +backports-unittest-mock +mock +pylint +pytest +pytest-cov +pytest-custom_exit_code \ No newline at end of file diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements.txt b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements.txt new file mode 100644 index 00000000..563801d2 --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/requirements.txt @@ -0,0 +1,7 @@ +fastapi +fastapi-restful +fireo +google-cloud-bigquery +google-cloud-storage +python-multipart +uvicorn diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/skaffold.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/skaffold.yaml new file mode 100644 index 00000000..1f7116d9 --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/skaffold.yaml @@ -0,0 +1,125 @@ +apiVersion: skaffold/v4beta1 +kind: Config +metadata: + name: {{component_name}} + +{% if depend_on_common == true -%} +# Requires the common image for shared data models or utils. +requires: +- configs: + - common + path: ../common +{%- endif %} + +build: + artifacts: + - image: {{resource_name}} + sync: + infer: + - '**/*.py' + - '**/*.json' + docker: + cacheFrom: + - {{resource_name}} + - {{resource_name}}:latest + + {% if depend_on_common == true -%} + requires: + - image: common + alias: COMMON_IMAGE + {%- endif %} + googleCloudBuild: {} + +# Portforwarding when running `skaffold dev` locally. +portForward: +- resourceType: service + resourceName: {{resource_name}} + port: 80 + localPort: {{local_port}} # Change this when adding other microservice. + +profiles: +# Profile for building images locally. +- name: local_build + build: + artifacts: + - image: {{resource_name}} + {% if depend_on_common == true -%} + requires: + - image: common + alias: COMMON_IMAGE + {%- endif %} + sync: + infer: + - '**/*.py' + - '**/*.json' + tagPolicy: + gitCommit: {} + local: + concurrency: 0 + +{% if deploy_gke == true -%} +# Profile for GKE deployment, building images via CloudBuild +- &gke-profile # YAML anchor used by "default" profile. + name: gke + manifests: + # Loading kustomize base file for deployment. + kustomize: + paths: + - ./kustomize/base + # Substitute system's environment vars to properties.rendered.env + hooks: + before: + - host: + dir: ./kustomize/base + command: ["sh", "-c", + "envsubst < properties.env > properties.rendered.env"] + after: + - host: + dir: ./kustomize/base + command: ["sh", "-c", "rm *.rendered.env"] + # Simple deployment using kubectl. + deploy: + kubectl: {} + +# Profile for GKE Horizontal Pod Autoscaler. +# This profile only works with `gke` profile together. +# E.g. skaffold run -p gke,hpa +- name: gke-hpa + manifests: + kustomize: + paths: + - ./kustomize/hpa + # Simple deployment using kubectl. + deploy: + kubectl: {} +{%- endif %} + +{% if deploy_cloudrun == true -%} +# Profile for Cloud Run deployment, building images via CloudBuild +- &cloudrun-profile # YAML anchor used by "default" profile. + name: cloudrun + manifests: + rawYaml: + - manifests/cloudrun-service.yaml + deploy: + cloudrun: + projectid: {{project_id}} + region: {{gcp_region}} + portForward: + - resourceType: service + resourceName: {{resource_name}} + port: 80 + localPort: {{local_port}} # Change this when adding other microservice. +{%- endif %} + +{% if default_deploy == "cloudrun" -%} +# The default-deploy profile refer to cloudrun profile above. +- <<: *cloudrun-profile + name: default-deploy +{%- endif %} + +{% if default_deploy == "gke" -%} +# The default-deploy profile refer to gke profile above. +- <<: *gke-profile + name: default-deploy +{%- endif %} diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/deployment.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/deployment.yaml new file mode 100644 index 00000000..9315a19e --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{resource_name}} +spec: + replicas: 1 + selector: + matchLabels: + app: {{resource_name}} + template: + metadata: + labels: + app: {{resource_name}} + spec: + serviceAccountName: gke-sa + automountServiceAccountToken: true + containers: + - name: {{resource_name}} + image: {{resource_name}} + imagePullPolicy: IfNotPresent + envFrom: + - configMapRef: + name: env-vars + resources: + requests: + cpu: "250m" + memory: "100Mi" + limits: + memory: "5000Mi" + cpu: "2000m" + ports: + - containerPort: 80 + livenessProbe: + failureThreshold: 5 + httpGet: + path: /ping + port: 80 + scheme: HTTP + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 10 + readinessProbe: + failureThreshold: 5 + httpGet: + path: /ping + port: 80 + scheme: HTTP + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 10 diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/kustomization.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/kustomization.yaml new file mode 100644 index 00000000..14dab40b --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ./deployment.yaml + - ./service.yaml +configMapGenerator: + - name: env-vars + envs: + - properties.rendered.env diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/properties.env b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/properties.env new file mode 100644 index 00000000..643deab7 --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/properties.env @@ -0,0 +1,2 @@ +PROJECT_ID=${PROJECT_ID} +DATABASE_PREFIX=${DATABASE_PREFIX} diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/service.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/service.yaml new file mode 100644 index 00000000..a69c31dd --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/base/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{resource_name}} + labels: + app: {{resource_name}} +spec: + type: NodePort + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + app: {{resource_name}} diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/hpa.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/hpa.yaml new file mode 100644 index 00000000..499fefbc --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/hpa.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: cpu-pod-scaling-{{component_name}} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{component_name}} + minReplicas: 3 + maxReplicas: 110 + metrics: + - type: Resource + resource: + name: memory + targetAverageValue: 500Mi + resource: + name: cpu + targetAverageUtilization: 60 diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/kustomization.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/kustomization.yaml new file mode 100644 index 00000000..6fc04e97 --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'kustomize' if deploy_gke else ''}}/hpa/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +bases: +- ../base +resources: +- hpa.yaml diff --git a/solutions_builder/modules/restful_service/components/{{component_name}}/manifests/cloudrun-service.yaml b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml similarity index 100% rename from solutions_builder/modules/restful_service/components/{{component_name}}/manifests/cloudrun-service.yaml rename to solutions_builder/modules/blank_service/1.0/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml diff --git a/solutions_builder/modules/blank_service/1.0/skaffold.yaml.patch b/solutions_builder/modules/blank_service/1.0/skaffold.yaml.patch new file mode 100644 index 00000000..4946745c --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/skaffold.yaml.patch @@ -0,0 +1,4 @@ +requires: + - configs: + - {{component_name}} + path: ./components/{{component_name}} diff --git a/solutions_builder/modules/blank_service/copier.yaml b/solutions_builder/modules/blank_service/copier.yaml index 2a078ee2..30713cdc 100644 --- a/solutions_builder/modules/blank_service/copier.yaml +++ b/solutions_builder/modules/blank_service/copier.yaml @@ -4,10 +4,15 @@ _metadata: destination_path: . # questions +module_version: + type: str + help: Module version (Default to 1.0)? + default: "1.0" + component_name: type: str help: What is the name of this component (snake_case)? - default: blank_service + default: my_service validator: "{% if not component_name %}Required{% endif %}" resource_name: @@ -26,15 +31,44 @@ gcp_region: help: Which Google Cloud region? default: us-central1 +deploy_cloudrun: + type: bool + help: Add Cloud Run to deployment methods (using Skaffold)? + default: yes + +cloudrun_neg: + type: bool + help: Create network endpoint group (NEG) for serverless ingress? + default: yes + when: "{{deploy_cloudrun}}" + +deploy_gke: + type: bool + help: Add GKE to deployment methods (using Skaffold)? + default: yes + +default_deploy: + type: str + help: Default deploy method? (cloudrun or gke) + choices: + Cloud Run: cloudrun + GKE: gke + default: cloudrun + depend_on_common: type: bool help: Does this component require the Common image? - default: no + default: yes -module_version: +local_port: type: str - help: Module version? - default: "1.0" + help: What's the port for local port forwarding? + default: 9001 + +use_github_action: + type: bool + help: Use Github Action as the default CI/CD? + default: yes _subdirectory: "{{module_version}}" @@ -42,6 +76,9 @@ _answers_file: ".st/module_answers/{{component_name}}.yaml" _templates_suffix: "" +_patch: + - "skaffold.yaml" + _exclude: - "README.md" - "copier.yaml" @@ -59,4 +96,3 @@ _jinja_extensions: - jinja2_strcase.StrcaseExtension - copier_templates_extensions.TemplateExtensionLoader - ../../copier_extensions/sb_helpers.py:SolutionsTemplateHelpersExtension - # - ../../copier_extensions/context.py:ContextUpdater diff --git a/solutions_builder/modules/restful_service/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml b/solutions_builder/modules/restful_service/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml new file mode 100644 index 00000000..b6994f6c --- /dev/null +++ b/solutions_builder/modules/restful_service/components/{{component_name}}/{{'manifests' if deploy_cloudrun else ''}}/cloudrun-service.yaml @@ -0,0 +1,11 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: {{resource_name}} +spec: + template: + spec: + containers: + - image: {{resource_name}} + ports: + - containerPort: 80 From d6f9d2a349ac991e50c5c8a3dc4824604a250585 Mon Sep 17 00:00:00 2001 From: Jon Chen Date: Wed, 8 Nov 2023 11:57:14 -0500 Subject: [PATCH 2/2] update module readmes --- pyproject.toml | 2 +- .../components/{{component_name}}/README.md | 69 +++++++++++++++++++ .../components/{{component_name}}/README.md | 62 +++++++++-------- 3 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 solutions_builder/modules/blank_service/1.0/components/{{component_name}}/README.md diff --git a/pyproject.toml b/pyproject.toml index 526c3744..378fdb70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "solutions-builder" -version = "1.17.19" +version = "1.17.20" description = "A solution framework to generate a project with built-in structure and modules" authors = ["Jon Chen "] license = "Apache" diff --git a/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/README.md b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/README.md new file mode 100644 index 00000000..bc81e21c --- /dev/null +++ b/solutions_builder/modules/blank_service/1.0/components/{{component_name}}/README.md @@ -0,0 +1,69 @@ +# {{component_name}} Microservice + +## Development + +### Run locally + +Create a virtualenv and install dependencies. +``` +cd components/{{component_name}} +python -m virtualenv .venv +pip install -r requirements.txt +``` + +If this service depends on **components/common**, run the following +to install all dependencies from common. + +``` +pip install -r ../common/requirements.txt +``` + +Run `main.py`. +``` +cd components/{{component_name}}/src +PYTHONPATH=../common/src python main.py +``` + +### Deploy to remote GKE cluster with livereload + +Use Solutions Builder CLI to deploy: +``` +st deploy . -m {{component_name}} --dev +``` + +Alternatively, deploy with Skaffold command: +``` +skaffold dev -p default -m {{component_name}} --default-repo="gcr.io/{{project_id}}" +``` + +## Test + +If this microservice uses Firestore, run Firebase emulator in a separate terminial. +``` +firebase emulators:start --only firestore --project fake-project +``` + +Install test related dependencies +``` +pip install -r requirements.txt +``` + +Run pytest +``` +pytest +``` + +## Deploy + +### Deploy to remote GKE cluster or Cloud Run + +Use Solutions Builder CLI to deploy: +``` +st deploy . -m {{component_name}} +``` + +Alternatively, deploy with Skaffold command: +``` +skaffold run -p default -m {{component_name}} --default-repo="gcr.io/{{project_id}}" +``` + diff --git a/solutions_builder/modules/restful_service/components/{{component_name}}/README.md b/solutions_builder/modules/restful_service/components/{{component_name}}/README.md index 741d4b04..a704b7d7 100644 --- a/solutions_builder/modules/restful_service/components/{{component_name}}/README.md +++ b/solutions_builder/modules/restful_service/components/{{component_name}}/README.md @@ -1,29 +1,45 @@ -# RESTful API microservice for {{data_model_plural | capitalize}} data model. +# {{component_name}} Microservice -## Deploy +## Development -Use Solutions Builder CLI to deploy: +### Run locally + +Create a virtualenv and install dependencies. ``` -st deploy . --component {{component_name}} +cd components/{{component_name}} +python -m virtualenv .venv +pip install -r requirements.txt ``` -Alternatively, deploy with Skaffold commandline: +If this service depends on **components/common**, run the following +to install all dependencies from common. + ``` -skaffold run -p default -m {{component_name}} --default-repo="gcr.io/{{project_id}}" +pip install -r ../common/requirements.txt ``` -## Run locally +Run `main.py` locally. -Create a virtualenv and install dependencies. ``` -cd components/{{component_name}} -python -m virtualenv .venv -pip install -r requirements.txt +cd components/{{component_name}}/src +PYTHONPATH=../common/src python main.py +``` + +### Deploy to remote GKE cluster with livereload + +Use Solutions Builder CLI to deploy: +``` +st deploy . -m {{component_name}} --dev +``` + +Alternatively, deploy with Skaffold command: +``` +skaffold dev -p default -m {{component_name}} --default-repo="gcr.io/{{project_id}}" ``` ## Test -Run Firebase emulator in a separate terminial. +If this microservice uses Firestore, run Firebase emulator in a separate terminial. ``` firebase emulators:start --only firestore --project fake-project ``` @@ -38,27 +54,17 @@ Run pytest pytest ``` -## GKE configuration +## Deploy -### Deploy to GKE cluster +### Deploy to remote GKE cluster or Cloud Run -Connect to the GKE cluster +Use Solutions Builder CLI to deploy: ``` -gcloud container clusters get-credentials main-cluster --region {{gcp_region}} --project {{project_id}} +st deploy . -m {{component_name}} ``` -Deploy with Skaffold using `gke` profile. - +Alternatively, deploy with Skaffold command: ``` -skaffold run -p gke -m {{component_name}} --default-repo="gcr.io/{{project_id}}" +skaffold run -p default -m {{component_name}} --default-repo="gcr.io/{{project_id}}" ``` -## Cloud Run configuration - -### Update Cloud Run service to accept unauthenticated traffic - -``` -gcloud run services add-iam-policy-binding {{resource_name}} \ - --member="allUsers" \ - --role="roles/run.invoker" -```