Skip to content
源代码提交(2)
  • zhou liang's avatar
    Initial commit · 89db5047
    zhou liang 创作于
    89db5047
  • zhou liang's avatar
    Merge branch 'main' into 'dev' · 7664f635
    zhou liang 创作于
    # Conflicts:
    #   helm-chart/templates/adservice.yaml
    #   helm-chart/templates/cartservice.yaml
    #   helm-chart/templates/productcatalogservice.yaml
    #   src/adservice/gradle-6.3/bin/gradle
    #   src/adservice/gradle-6.3/bin/gradle.bat
    #   src/adservice/src/main/java/hipstershop/AdService.java
    #   src/cartservice/Dockerfile
    #   src/cartservice/Program.cs
    #   src/cartservice/Startup.cs
    #   src/cartservice/cartservice.csproj
    #   src/cartservice/cartstore/RedisCartStore.cs
    #   src/currencyservice/server.js
    #   src/paymentservice/index.js
    7664f635
.idea/workspace.xml
*.xml
/.idea/*
<p align="center">
<img src="src/frontend/static/icons/Hipster_HeroLogoCyan.svg" width="300"/>
</p>
**Online Boutique** is a cloud-native microservices demo application.
Online Boutique consists of a 10-tier microservices application. The application is a
web-based e-commerce app where users can browse items,
add them to the cart, and purchase them.
**Google uses this application to demonstrate use of technologies like
Kubernetes/GKE, Istio, Stackdriver, gRPC and OpenCensus**. This application
works on any Kubernetes cluster (such as a local one), as well as Google
Kubernetes Engine. It’s **easy to deploy with little to no configuration**.
If you’re using this demo, please **★Star** this repository to show your interest!
## Tracing Module
We instrument OpenTelemetry API for each service to equip tracing ability for Hipster-Shop.
[![trace result](./docs/img/tracing.png)](./docs/img/tracing.png)
## Screenshots
| Home Page | Checkout Screen |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [![Screenshot of store homepage](./docs/img/online-boutique-frontend-1.png)](./docs/img/online-boutique-frontend-1.png) | [![Screenshot of checkout screen](./docs/img/online-boutique-frontend-2.png)](./docs/img/online-boutique-frontend-2.png) |
## Service Architecture
**Online Boutique** is composed of many microservices written in different
languages that talk to each other over gRPC.
[![Architecture of
microservices](./docs/img/architecture-diagram.png)](./docs/img/architecture-diagram.png)
Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
| Service | Language | Description |
| ---------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| [frontend](./src/frontend) | Go | Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically. |
| [cartservice](./src/cartservice) | C# | Stores the items in the user's shopping cart in Redis and retrieves it. |
| [productcatalogservice](./src/productcatalogservice) | Go | Provides the list of products from a JSON file and ability to search products and get individual products. |
| [currencyservice](./src/currencyservice) | Node.js | Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service. |
| [paymentservice](./src/paymentservice) | Node.js | Charges the given credit card info (mock) with the given amount and returns a transaction ID. |
| [shippingservice](./src/shippingservice) | Go | Gives shipping cost estimates based on the shopping cart. Ships items to the given address (mock) |
| [emailservice](./src/emailservice) | Python | Sends users an order confirmation email (mock). |
| [checkoutservice](./src/checkoutservice) | Go | Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification. |
| [recommendationservice](./src/recommendationservice) | Python | Recommends other products based on what's given in the cart. |
| [adservice](./src/adservice) | Java | Provides text ads based on given context words. |
| [loadgenerator](./src/loadgenerator) | Python/Locust | Continuously sends requests imitating realistic user shopping flows to the frontend. |
## Features
- **[Kubernetes](https://kubernetes.io)/[GKE](https://cloud.google.com/kubernetes-engine/):**
The app is designed to run on Kubernetes (both locally on "Docker for
Desktop", as well as on the cloud with GKE).
- **[gRPC](https://grpc.io):** Microservices use a high volume of gRPC calls to
communicate to each other.
- **[Istio](https://istio.io):** Application works on Istio service mesh.
- **[OpenCensus](https://opencensus.io/) Tracing:** Most services are
instrumented using OpenCensus trace interceptors for gRPC/HTTP.
- **[Stackdriver APM](https://cloud.google.com/stackdriver/):** Many services
are instrumented with **Profiling**, **Tracing** and **Debugging**. In
addition to these, using Istio enables features like Request/Response
**Metrics** and **Context Graph** out of the box. When it is running out of
Google Cloud, this code path remains inactive.
- **[Skaffold](https://skaffold.dev):** Application
is deployed to Kubernetes with a single command using Skaffold.
- **Synthetic Load Generation:** The application demo comes with a background
job that creates realistic usage patterns on the website using
[Locust](https://locust.io/) load generator.
## Installation
We offer the following installation methods:
1. **Running locally** (~20 minutes) You will build
and deploy microservices images to a single-node Kubernetes cluster running
on your development machine. There are three options to run a Kubernetes
cluster locally for this demo:
- [Minikube](https://github.com/kubernetes/minikube). Recommended for
Linux hosts (also supports Mac/Windows).
- [Docker for Desktop](https://www.docker.com/products/docker-desktop).
Recommended for Mac/Windows.
- [Kind](https://kind.sigs.k8s.io). Supports Mac/Windows/Linux.
1. **Running on Google Kubernetes Engine (GKE)”** (~30 minutes) You will build,
upload and deploy the container images to a Kubernetes cluster on Google
Cloud.
1. **Using pre-built container images:** (~10 minutes, you will still need to
follow one of the steps above up until `skaffold run` command). With this
option, you will use pre-built container images that are available publicly,
instead of building them yourself, which takes a long time).
### Prerequisites
- kubectl (can be installed via `gcloud components install kubectl`)
- Local Kubernetes cluster deployment tool:
- [Minikube (recommended for
Linux)](https://kubernetes.io/docs/setup/minikube/)
- [Docker for Desktop (recommended for Mac/Windows)](https://www.docker.com/products/docker-desktop)
- It provides Kubernetes support as [noted
here](https://docs.docker.com/docker-for-mac/kubernetes/)
- [Kind](https://github.com/kubernetes-sigs/kind)
- [skaffold]( https://skaffold.dev/docs/install/) ([ensure version ≥v1.10](https://github.com/GoogleContainerTools/skaffold/releases))
- Enable GCP APIs for Cloud Monitoring, Tracing, Debugger:
```
gcloud services enable monitoring.googleapis.com \
cloudtrace.googleapis.com \
clouddebugger.googleapis.com
```
### Option 1: Running locally
> 💡 Recommended if you're planning to develop the application or giving it a
> try on your local cluster.
1. Launch a local Kubernetes cluster with one of the following tools:
- To launch **Minikube** (tested with Ubuntu Linux). Please, ensure that the
local Kubernetes cluster has at least:
- 4 CPU's
- 4.0 GiB memory
- 32 GB disk space
```shell
minikube start --cpus=4 --memory 4096 --disk-size 32g
```
- To launch **Docker for Desktop** (tested with Mac/Windows). Go to Preferences:
- choose “Enable Kubernetes”,
- set CPUs to at least 3, and Memory to at least 6.0 GiB
- on the "Disk" tab, set at least 32 GB disk space
- To launch a **Kind** cluster:
```shell
kind create cluster
```
2. Run `kubectl get nodes` to verify you're connected to “Kubernetes on Docker”.
3. Run `skaffold run` (first time will be slow, it can take ~20 minutes).
This will build and deploy the application. If you need to rebuild the images
automatically as you refactor the code, run `skaffold dev` command.
4. Run `kubectl get pods` to verify the Pods are ready and running.
5. Access the web frontend through your browser
- **Minikube** requires you to run a command to access the frontend service:
```shell
minikube service frontend-external
```
- **Docker For Desktop** should automatically provide the frontend at http://localhost:80
- **Kind** does not provision an IP address for the service.
You must run a port-forwarding process to access the frontend at http://localhost:8080:
```shell
kubectl port-forward deployment/frontend 8080:8080
```
### Option 2: Running on Google Kubernetes Engine (GKE)
> 💡 Recommended if you're using Google Cloud Platform and want to try it on
> a realistic cluster.
1. Create a Google Kubernetes Engine cluster and make sure `kubectl` is pointing
to the cluster.
```sh
gcloud services enable container.googleapis.com
```
```sh
gcloud container clusters create demo --enable-autoupgrade \
--enable-autoscaling --min-nodes=3 --max-nodes=10 --num-nodes=5 --zone=us-central1-a
```
```
kubectl get nodes
```
2. Enable Google Container Registry (GCR) on your GCP project and configure the
`docker` CLI to authenticate to GCR:
```sh
gcloud services enable containerregistry.googleapis.com
```
```sh
gcloud auth configure-docker -q
```
3. In the root of this repository, run `skaffold run --default-repo=gcr.io/[PROJECT_ID]`,
where [PROJECT_ID] is your GCP project ID.
This command:
- builds the container images
- pushes them to GCR
- applies the `./kubernetes-manifests` deploying the application to
Kubernetes.
**Troubleshooting:** If you get "No space left on device" error on Google
Cloud Shell, you can build the images on Google Cloud Build: [Enable the
Cloud Build
API](https://console.cloud.google.com/flows/enableapi?apiid=cloudbuild.googleapis.com),
then run `skaffold run -p gcb --default-repo=gcr.io/[PROJECT_ID]` instead.
4. Find the IP address of your application, then visit the application on your
browser to confirm installation.
kubectl get service frontend-external
**Troubleshooting:** A Kubernetes bug (will be fixed in 1.12) combined with
a Skaffold [bug](https://github.com/GoogleContainerTools/skaffold/issues/887)
causes load balancer to not to work even after getting an IP address. If you
are seeing this, run `kubectl get service frontend-external -o=yaml | kubectl apply -f-`
to trigger load balancer reconfiguration.
### Option 3: Using Pre-Built Container Images
> 💡 Recommended if you want to deploy the app faster in fewer steps to an
> existing cluster.
**NOTE:** If you need to create a Kubernetes cluster locally or on the cloud,
follow "Option 1" or "Option 2" until you reach the `skaffold run` step.
This option offers you pre-built public container images that are easy to deploy
by deploying the [release manifest](./release) directly to an existing cluster.
**Prerequisite**: a running Kubernetes cluster (either local or on cloud).
1. Clone this repository, and go to the repository directory
1. Run `kubectl apply -f ./release/kubernetes-manifests.yaml` to deploy the app.
1. Run `kubectl get pods` to see pods are in a Ready state.
1. Find the IP address of your application, then visit the application on your
browser to confirm installation.
```sh
kubectl get service/frontend-external
```
### Option 4: Deploying on a Istio-enabled GKE cluster
> **Note:** if you followed GKE deployment steps above, run `skaffold delete` first to delete what's deployed.
1. Create a GKE cluster (described in "Option 2").
2. Use the [Istio on GKE add-on](https://cloud.google.com/istio/docs/istio-on-gke/installing)
to install Istio to your existing GKE cluster.
```sh
gcloud beta container clusters update demo \
--zone=us-central1-a \
--update-addons=Istio=ENABLED \
--istio-config=auth=MTLS_PERMISSIVE
```
3. (Optional) Enable Stackdriver Tracing/Logging with Istio Stackdriver Adapter
by [following this guide](https://cloud.google.com/istio/docs/istio-on-gke/installing#enabling_tracing_and_logging).
4. Install the automatic sidecar injection (annotate the `default` namespace
with the label):
```sh
kubectl label namespace default istio-injection=enabled
```
5. Apply the manifests in [`./istio-manifests`](./istio-manifests) directory.
(This is required only once.)
```sh
kubectl apply -f ./istio-manifests
```
6. In the root of this repository, run `skaffold run --default-repo=gcr.io/[PROJECT_ID]`,
where [PROJECT_ID] is your GCP project ID.
This command:
- builds the container images
- pushes them to GCR
- applies the `./kubernetes-manifests` deploying the application to
Kubernetes.
**Troubleshooting:** If you get "No space left on device" error on Google
Cloud Shell, you can build the images on Google Cloud Build: [Enable the
Cloud Build
API](https://console.cloud.google.com/flows/enableapi?apiid=cloudbuild.googleapis.com),
then run `skaffold run -p gcb --default-repo=gcr.io/[PROJECT_ID]` instead.
7. Run `kubectl get pods` to see pods are in a healthy and ready state.
8. Find the IP address of your Istio gateway Ingress or Service, and visit the
application.
```sh
INGRESS_HOST="$(kubectl -n istio-system get service istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
echo "$INGRESS_HOST"
```
```sh
curl -v "http://$INGRESS_HOST"
```
### Cleanup
If you've deployed the application with `skaffold run` command, you can run
`skaffold delete` to clean up the deployed resources.
If you've deployed the application with `kubectl apply -f [...]`, you can
run `kubectl delete -f [...]` with the same argument to clean up the deployed
resources.
## Conferences featuring Online Boutique
- [Google Cloud Next'18 London – Keynote](https://youtu.be/nIq2pkNcfEI?t=3071)
showing Stackdriver Incident Response Management
- Google Cloud Next'18 SF
- [Day 1 Keynote](https://youtu.be/vJ9OaAqfxo4?t=2416) showing GKE On-Prem
- [Day 3 – Keynote](https://youtu.be/JQPOPV_VH5w?t=815) showing Stackdriver
APM (Tracing, Code Search, Profiler, Google Cloud Build)
- [Introduction to Service Management with Istio](https://www.youtube.com/watch?v=wCJrdKdD6UM&feature=youtu.be&t=586)
- [KubeCon EU 2019 - Reinventing Networking: A Deep Dive into Istio's Multicluster Gateways - Steve Dake, Independent](https://youtu.be/-t2BfT59zJA?t=982)
---
This is not an official Google project.
# This configuration file is used to build and deploy the app into a
# GKE cluster using Google Cloud Build.
#
# PREREQUISITES:
# - Cloud Build service account must have role: "Kubernetes Engine Developer"
# USAGE:
# GCP zone and GKE target cluster must be specified as substitutions
# Example invocation:
# `gcloud builds submit --config=cloudbuild.yaml --substitutions=_ZONE=us-central1-b,_CLUSTER=demo-app-staging .`
steps:
- id: 'Deploy application to cluster'
name: 'gcr.io/k8s-skaffold/skaffold:v0.20.0'
entrypoint: 'bash'
args:
- '-c'
- >
gcloud container clusters get-credentials --zone=$_ZONE $_CLUSTER;
skaffold run -f=skaffold.yaml --default-repo=gcr.io/$PROJECT_ID;
# Add more power, and more time, for heavy Skaffold build
timeout: '3600s'
options:
machineType: 'N1_HIGHCPU_8'
\ No newline at end of file
CREATE TABLE IF NOT EXISTS `product`(
`id` VARCHAR(100) NOT NULL COMMENT '唯一不重复',
`name` VARCHAR(100) NOT NULL,
`description` VARCHAR(200) NOT NULL,
`picture` VARCHAR(512) CHARACTER SET 'ascii' COLLATE 'ascii_general_ci' NOT NULL,
`priceUsd` JSON,
`categories` JSON,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'L9ECAV7KIM',
'Terrarium',
'This terrarium will looks great in your white painted living room.',
'/static/img/products/terrarium.jpg',
'{"currencyCode":"USD","units":36,"nanos":450000000}',
'["gardening"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'OLJCESPC7Z',
'Vintage Typewriter',
'This typewriter looks good in your living room.',
'/static/img/products/typewriter.jpg',
'{"currencyCode": "USD", "units": 67, "nanos": 990000000}',
'["vintage"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'66VCHSJNUP',
'Vintage Camera Lens',
'You won't have a camera to use it and it probably doesn't work anyway.',
'/static/img/products/camera-lens.jpg',
'{"currencyCode": "USD", "units": 12, "nanos": 490000000}',
'["photography", "vintage"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'1YMWWN1N4O',
'Home Barista Kit',
'Always wanted to brew coffee with Chemex and Aeropress at home?',
'/static/img/products/barista-kit.jpg',
'{"currencyCode": "USD", "units": 124}',
'["cookware"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'L9ECAV7KIM',
'Terrarium',
'This terrarium will looks great in your white painted living room.',
'/static/img/products/terrarium.jpg',
'{"currencyCode": "USD", "units": 36, "nanos": 450000000}',
'["gardening"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'2ZYFJ3GM2N',
'Film Camera',
'This camera looks like it's a film camera, but it's actually digital.',
'/static/img/products/film-camera.jpg',
'{"currencyCode": "USD", "units": 2245}',
'["photography", "vintage"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'0PUK6V6EV0',
'Vintage Record Player',
'It still works.',
'/static/img/products/record-player.jpg',
'{"currencyCode": "USD", "units": 65, "nanos": 500000000}',
'["music", "vintage"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'LS4PSXUNUM',
'Metal Camping Mug',
"You probably don't go camping that often but this is better than plastic cups.",
'/static/img/products/camp-mug.jpg',
'{"currencyCode": "USD", "units": 24, "nanos": 330000000}',
'["cookware"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'9SIQT8TOJO',
'City Bike',
'This single gear bike probably cannot climb the hills of San Francisco.',
'/static/img/products/city-bike.jpg',
'{"currencyCode": "USD", "units": 789, "nanos": 500000000}',
'["cycling"]'
);
INSERT INTO product
(`id`, `name`, `description`, `picture`, `priceUsd`, `categories`)
VALUES (
'6E92ZMYYFZ',
'Air Plant',
'Have you ever wondered whether air plants need water? Buy one and figure out.',
'/static/img/products/air-plant.jpg',
'{"currencyCode": "USD", "units": 12, "nanos": 300000000}',
'["gardening"]'
);
----------------------------------------------------------------
CREATE DATABASE addatabase;
use addatabase;
CREATE TABLE aditems(
item_name VARCHAR(40) NOT NULL,
redirect_url VARCHAR(100) NOT NULL,
text VARCHAR(100) NOT NULL,
PRIMARY KEY ( item_name )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("camera", "/product/2ZYFJ3GM2N", "Film camera for sale. 50% off.");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("lens", "/product/66VCHSJNUP", "Vintage camera lens for sale. 20% off.");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("recordPlayer", "/product/0PUK6V6EV0", "Vintage record player for sale. 30% off.");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("bike", "/product/9SIQT8TOJO", "City Bike for sale. 10% off.");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("baristaKit", "/product/1YMWWN1N4O", "Home Barista kitchen kit for sale. Buy one, get second kit for free");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("airPlant", "/product/6E92ZMYYFZ", "Air plants for sale. Buy two, get third one for free");
INSERT INTO aditems
(item_name, redirect_url, text)
VALUES
("terrarium", "/product/L9ECAV7KIM", "Terrarium for sale. Buy one, get second one for free");
# product db
CREATE DATABASE Productdb;
use Productdb;
CREATE TABLE products(
product_id VARCHAR(40) NOT NULL,
item_name VARCHAR(40) NOT NULL,
description VARCHAR(100) NOT NULL,
picture_path VARCHAR(100) NOT NULL,
categorise_list VARCHAR(100) NOT NULL,
PRIMARY KEY ( product_id )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE price(
product_id VARCHAR(40) NOT NULL,
currencyCode VARCHAR(10) NOT NULL,
units INT NOT NULL,
nanos INT,
PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("OLJCESPC7Z", "Vintage Typewriter", "This typewriter looks good in your living room.", "/static/img/products/typewriter.jpg", "vintage");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("OLJCESPC7Z", "USD", 67, 990000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("66VCHSJNUP", "Antique Camera", "It probably doesn't work anyway.", "/static/img/products/camera-lens.jpg", "photography;vintage");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("66VCHSJNUP", "USD", 12, 490000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("1YMWWN1N4O", "Home Barista Kit", "Always wanted to brew coffee with Chemex and Aeropress at home?", "/static/img/products/barista-kit.jpg", "cookware");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("1YMWWN1N4O", "USD", 124, 0);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("L9ECAV7KIM", "Terrarium", "This terrarium will looks great in your white painted living room.", "/static/img/products/terrarium.jpg", "gardening");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("L9ECAV7KIM", "USD", 36, 450000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("2ZYFJ3GM2N", "Film Camera", "This camera looks like it's a film camera, but it's actually digital.", "/static/img/products/film-camera.jpg", "photography;vintage");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("2ZYFJ3GM2N", "USD", 2245, 0);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("0PUK6V6EV0", "Vintage Record Player", "It still works.", "/static/img/products/record-player.jpg", "music;vintage");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("0PUK6V6EV0", "USD", 65, 500000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("LS4PSXUNUM", "Metal Camping Mug", "You probably don't go camping that often but this is better than plastic cups.", "/static/img/products/camp-mug.jpg", "cookware");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("LS4PSXUNUM", "USD", 24, 330000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("9SIQT8TOJO", "City Bike", "This single gear bike probably cannot climb the hills of San Francisco.", "/static/img/products/city-bike.jpg", "cycling");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("9SIQT8TOJO", "USD", 789, 500000000);
INSERT INTO products
(product_id, item_name, description, picture_path, categorise_list)
VALUES
("6E92ZMYYFZ", "Air Plant", "Have you ever wondered whether air plants need water? Buy one and figure out.", "/static/img/products/air-plant.jpg", "gardening");
INSERT INTO price
(product_id, currencyCode, units, nanos)
VALUES
("6E92ZMYYFZ", "USD", 12, 300000000);
\ No newline at end of file
此差异已折叠。
apiVersion: v1
kind: Service
metadata:
name: basic-tidb-external
namespace: tidb-cluster
labels:
app.kubernetes.io/component: tidb
app.kubernetes.io/instance: basic
app.kubernetes.io/managed-by: tidb-operator
app.kubernetes.io/name: tidb-cluster
app.kubernetes.io/used-by: end-user
spec:
ports:
- name: mysql-client
protocol: TCP
port: 4000
targetPort: 4000
nodePort: 23333
- name: status
protocol: TCP
port: 10080
targetPort: 10080
selector:
app.kubernetes.io/component: tidb
app.kubernetes.io/instance: basic
app.kubernetes.io/managed-by: tidb-operator
app.kubernetes.io/name: tidb-cluster
type: NodePort
sessionAffinity: None
# IT IS NOT SUITABLE FOR PRODUCTION USE.
# This YAML describes a basic TiDB cluster with minimum resource requirements,
# which should be able to run in any Kubernetes cluster with storage support.
apiVersion: pingcap.com/v1alpha1
kind: TidbCluster
metadata:
name: basic
namespace: tidb-cluster
spec:
version: v5.3.0
timezone: UTC
pvReclaimPolicy: Retain
enableDynamicConfiguration: true
configUpdateStrategy: RollingUpdate
discovery: {}
helper:
image: busybox:1.34.1
pd:
baseImage: pingcap/pd
maxFailoverCount: 0
replicas: 1
# if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used
storageClassName: nfs-client
requests:
storage: "1Gi"
config: {}
tikv:
baseImage: pingcap/tikv
maxFailoverCount: 0
# If only 1 TiKV is deployed, the TiKV region leader
# cannot be transferred during upgrade, so we have
# to configure a short timeout
evictLeaderTimeout: 1m
replicas: 1
# if storageClassName is not set, the default Storage Class of the Kubernetes cluster will be used
storageClassName: nfs-client
requests:
storage: "1Gi"
config:
storage:
# In basic examples, we set this to avoid using too much storage.
reserve-space: "0MB"
rocksdb:
# In basic examples, we set this to avoid the following error in some Kubernetes clusters:
# "the maximum number of open file descriptors is too small, got 1024, expect greater or equal to 82920"
max-open-files: 256
raftdb:
max-open-files: 256
tidb:
baseImage: pingcap/tidb
maxFailoverCount: 0
replicas: 1
service:
type: ClusterIP
config: {}
apiVersion: pingcap.com/v1alpha1
kind: TidbMonitor
metadata:
name: basic
namespace: tidb-cluster
spec:
clusters:
- name: basic
prometheus:
baseImage: prom/prometheus
version: v2.18.1
grafana:
baseImage: grafana/grafana
version: 7.5.7
initializer:
baseImage: uhub.service.ucloud.cn/pingcap/tidb-monitor-initializer
version: v5.1.1
reloader:
baseImage: uhub.service.ucloud.cn/pingcap/tidb-monitor-reloader
version: v1.0.1
imagePullPolicy: IfNotPresent
# Development Principles
> **Note:** This document outlines guidances behind some development decisions
> behind the Hipster Shop demo application.
### Minimal configuration
Running the demo locally or on GCP should require minimal to no
configuration unless absolutely necessary to run critical parts of the demo.
Configuration that takes multiple steps, especially such as creating service
accounts should be avoided.
### App must work well outside GCP
Demo application should work reasonably well when it is not deployed to GCP
services. The experience of running the application locally or on GCP should
be close.
For example:
- OpenCensus prints the traces to stdout when it cannot connect to GCP.
- Stackdriver Debugging tries connecting to GCP multiple times, eventually gives
up.
### Running on GCP must not reduce functionality
Running the demo on the GCP must not reduce/lose any of the capabilities
developers have when running locally.
For example: Logs should still be printed to stdout/stderr even though logs are
uploaded to Stackdriver Logging when on GCP, so that developers can use "kubectl
logs" to diagnose each container.
### Microservice implementations should not be complex
Each service should provide a minimal implementation and try to avoid
unnecessary code and logic that's not executed.
Keep in mind that any service implementation is a decent example of “a GRPC
application that runs on Kubernetes”. Keeping the source code short and
navigable will serve this purpose.
It is okay to have intentional inefficiencies in the code as they help
illustrate the capabilities of profiling and diagnostics offerings.
## `hack/`
This directory provides scripts for building and pushing Docker images, and tagging new demo
releases.
### env variables
- `TAG` - git release tag / Docker tag.
- `REPO_PREFIX` - Docker repo prefix to push images. Format: `$user/$project`. Resulting images will be of the
format `$user/$project/$svcname:$tag` (where `svcname` = `adservice`, `cartservice`,
etc.)
### scripts
1. `./make-docker-images.sh`: builds and pushes images to the specified Docker repository.
2. `./make-release-artifacts.sh`: generates a combined YAML file with image $TAG at:
`./release/kubernetes-manifests/demo.yaml`.
3. `./make-release.sh`: runs scripts 1 and 2, then runs `git tag` / pushes updated manifests to master.
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Builds and pushes docker image for each demo microservice.
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
log() { echo "$1" >&2; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
while IFS= read -d $'\0' -r dir; do
# build image
svcname="$(basename "${dir}")"
image="${REPO_PREFIX}/$svcname:$TAG"
(
cd "${dir}"
log "Building: ${image}"
docker build -t "${image}" .
log "Pushing: ${image}"
docker push "${image}"
)
done < <(find "${SCRIPTDIR}/../src" -mindepth 1 -maxdepth 1 -type d -print0)
log "Successfully built and pushed all images."
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script compiles manifest files with the image tags and places them in
# /release/...
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
[[ -n "${DEBUG:-}" ]] && set -x
log() { echo "$1" >&2; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
OUT_DIR="${OUT_DIR:-${SCRIPTDIR}/../release}"
print_license_header() {
cat "${SCRIPTDIR}/license_header.txt"
echo
}
print_autogenerated_warning() {
cat<<EOF
# ----------------------------------------------------------
# WARNING: This file is autogenerated. Do not manually edit.
# ----------------------------------------------------------
EOF
}
# define gsed as a function on Linux for compatibility
[ "$(uname -s)" == "Linux" ] && gsed() {
sed "$@"
}
read_manifests() {
local dir
dir="$1"
while IFS= read -d $'\0' -r file; do
# strip license headers (pattern "^# ")
awk '
/^[^# ]/ { found = 1 }
found { print }' "${file}"
echo "---"
done < <(find "${dir}" -name '*.yaml' -type f -print0)
}
mk_kubernetes_manifests() {
out_manifest="$(read_manifests "${SCRIPTDIR}/../kubernetes-manifests")"
# replace "image" repo, tag for each service
for dir in ./src/*/
do
svcname="$(basename "${dir}")"
image="$REPO_PREFIX/$svcname:$TAG"
pattern="^(\s*)image:\s.*$svcname(.*)(\s*)"
replace="\1image: $image\3"
out_manifest="$(gsed -r "s|$pattern|$replace|g" <(echo "${out_manifest}") )"
done
print_license_header
print_autogenerated_warning
echo "${out_manifest}"
}
mk_istio_manifests() {
print_license_header
print_autogenerated_warning
read_manifests "${SCRIPTDIR}/../istio-manifests"
}
main() {
mkdir -p "${OUT_DIR}"
local k8s_manifests_file istio_manifests_file
k8s_manifests_file="${OUT_DIR}/kubernetes-manifests.yaml"
mk_kubernetes_manifests > "${k8s_manifests_file}"
log "Written ${k8s_manifests_file}"
istio_manifests_file="${OUT_DIR}/istio-manifests.yaml"
mk_istio_manifests > "${istio_manifests_file}"
log "Written ${istio_manifests_file}"
}
main
#!/usr/bin/env bash
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script creates a new release by:
# - 1. building/pushing images
# - 2. injecting tags into YAML manifests
# - 3. creating a new git tag
# - 4. pushing the tag/commit to master.
set -euo pipefail
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
[[ -n "${DEBUG:-}" ]] && set -x
log() { echo "$1" >&2; }
fail() { log "$1"; exit 1; }
TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified e.g. gcr.io\/google-samples\/microservices-demo}"
if [[ "$TAG" != v* ]]; then
fail "\$TAG must start with 'v', e.g. v0.1.0 (got: $TAG)"
fi
# build and push images
"${SCRIPTDIR}"/make-docker-images.sh
# update yaml
"${SCRIPTDIR}"/make-release-artifacts.sh
# create git release / push to new branch
git checkout -b "release/${TAG}"
git add "${SCRIPTDIR}/../release/"
git commit --allow-empty -m "Release $TAG"
log "Pushing k8s manifests to release/${TAG}..."
git tag "$TAG"
git push --set-upstream origin "release/${TAG}"
git push --tags
log "Successfully tagged release $TAG."
......@@ -45,10 +45,11 @@ spec:
# - name: DISABLE_TRACING
# value: "1"
- name: JAEGER_HOST
# value: "jaeger-collector"
value: "jaeger-collector.observability.svc.cluster.local"
- name: ZIPKIN_PORT
value: "9411"
# value: "14268"
# value: "14268"
- name: POD_IP
valueFrom:
fieldRef:
......