├── .gitignore ├── traffic-generator ├── requirements.txt ├── README.md └── locustfile.py ├── acmeshop.png ├── aws-fargate ├── wavefront.png ├── cf-template.png ├── README.md ├── wavefront-dashboard.json └── acme-fitness-shop.yaml ├── docker-compose ├── README.md └── docker-compose.yml ├── LICENSE ├── kubernetes-manifests ├── users-db-initdb-configmap.yaml ├── point-of-sales-total.yaml ├── payment-total.yaml ├── order-db-total.yaml ├── frontend-total.yaml ├── users-redis-total.yaml ├── cart-redis-total.yaml ├── users-db-total.yaml ├── catalog-db-total.yaml ├── cart-total.yaml ├── catalog-total.yaml ├── users-total.yaml ├── order-total.yaml ├── catalog-db-initdb-configmap.yaml ├── jaeger-all-in-one.yml └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .DS_Store -------------------------------------------------------------------------------- /traffic-generator/requirements.txt: -------------------------------------------------------------------------------- 1 | locust==2.4.1 2 | -------------------------------------------------------------------------------- /acmeshop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmwarecloudadvocacy/acme_fitness_demo/HEAD/acmeshop.png -------------------------------------------------------------------------------- /aws-fargate/wavefront.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmwarecloudadvocacy/acme_fitness_demo/HEAD/aws-fargate/wavefront.png -------------------------------------------------------------------------------- /aws-fargate/cf-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmwarecloudadvocacy/acme_fitness_demo/HEAD/aws-fargate/cf-template.png -------------------------------------------------------------------------------- /traffic-generator/README.md: -------------------------------------------------------------------------------- 1 | # Traffic Generator 2 | 3 | ## Requirements 4 | 5 | 1. Needs Python3.6 and above 6 | 7 | 2. Locust - [Locust](https://docs.locust.io/en/stable/installation.html) 8 | 9 | 3. ACME Fitness App 10 | 11 | ## Steps 12 | 13 | 1. pip3 install -r requirements.txt 14 | 15 | 2. locust --host=http://{IP ADDRESS}:{PORT}, where IP_ADDRESS and PORT are the address on which the ACME SHOP App is running. 16 | 17 | 3. Navigate to http://localhost:8089 18 | 19 | 4. Click on 'New Test' and provide the number of users to simulate. 20 | -------------------------------------------------------------------------------- /docker-compose/README.md: -------------------------------------------------------------------------------- 1 | ## Steps for deployment with docker-compose 2 | 3 | 1. Run the following command 4 | 5 | ``` 6 | sudo docker-compose pull && docker-compose up 7 | 8 | ``` 9 | 10 | 2. This should pull the images from gcr.io/vmwarecloudadvocacy container repository and run the images 11 | 12 | 3. Access the application on default port **3000** at ```http://{MACHINE_IP}:3000/ 13 | 14 | 4. To stop the application, use the following command 15 | 16 | ``` sudo docker-compose stop ``` 17 | 18 | 5. To completely remove all containers, use the following command 19 | 20 | ``` sudo docker-compose down --remove-orphans ``` 21 | 22 | **IMPORTANT** 23 | 24 | While running steps 1 and 4 make sure you are in the same directory as the docker-compose.yml file. 25 | 26 | ## Additional Info 27 | 28 | 1. Make sure no other container is running on the ports mapped by this application. You might notice that containers will fail to start if such other containers exists. Kill those containers before deploying this application. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 VMware Cloud Advocacy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /kubernetes-manifests/users-db-initdb-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: users-initdb-config 5 | data: 6 | seed.js: | 7 | db.users.insertMany([ 8 | {"firstname":"Walter","lastname":"White","email":"walter@acmefitness.com","username":"walter","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 9 | ,{"firstname":"Dwight","lastname":"Schrute","email":"dwight@acmefitness.com","username":"dwight","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 10 | ,{"firstname":"Eric","lastname":"Cartman","email":"eric@acmefitness.com","username":"eric","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 11 | ,{"firstname":"Han","lastname":"Solo","email":"han@acmefitness.com","username":"han","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 12 | ,{"firstname":"Phoebe","lastname":"Buffay","email":"phoebe@acmefitness.com","username":"phoebe","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 13 | ,{"firstname":"Elaine","lastname":"Benes","email":"elaine@acmefitness.com","username":"elaine","password":"6837ea9b06409112a824d113927ad74fabc5c76e","salt":""} 14 | ]); 15 | -------------------------------------------------------------------------------- /kubernetes-manifests/point-of-sales-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pos 5 | labels: 6 | app: acmefit 7 | service: pos 8 | spec: 9 | ports: 10 | - name: http-pos 11 | protocol: TCP 12 | port: 7777 13 | targetPort: 7777 14 | nodePort: 30431 15 | selector: 16 | app: acmefit 17 | service: pos 18 | type: NodePort 19 | --- 20 | apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 21 | kind: Deployment 22 | metadata: 23 | name: pos 24 | labels: 25 | app: acmefit 26 | service: pos 27 | spec: 28 | selector: 29 | matchLabels: 30 | app: acmefit 31 | service: pos 32 | strategy: 33 | type: Recreate 34 | replicas: 1 35 | template: 36 | metadata: 37 | labels: 38 | app: acmefit 39 | service: pos 40 | spec: 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-pos:v0.1.0-beta 43 | name: pos 44 | env: 45 | - name: HTTP_PORT 46 | value: '7777' 47 | - name: DATASTORE 48 | value: 'REMOTE' 49 | - name: FRONTEND_HOST 50 | value: 'frontend.default.svc.cluster.local' 51 | ports: 52 | - containerPort: 7777 53 | name: pos -------------------------------------------------------------------------------- /kubernetes-manifests/payment-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: payment 5 | labels: 6 | app: acmefit 7 | service: payment 8 | spec: 9 | ports: 10 | - name: http-payment 11 | protocol: TCP 12 | port: 9000 13 | selector: 14 | app: acmefit 15 | service: payment 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: payment 21 | labels: 22 | app: acmefit 23 | service: payment 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: payment 29 | strategy: 30 | type: Recreate 31 | replicas: 1 32 | template: 33 | metadata: 34 | labels: 35 | app: acmefit 36 | service: payment 37 | spec: 38 | containers: 39 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-payment:latest 40 | name: payment 41 | env: 42 | - name: PAYMENT_PORT 43 | value: '9000' 44 | - name: USERS_HOST 45 | value: 'users' 46 | - name: USERS_PORT 47 | value: '8083' 48 | - name: JAEGER_AGENT_HOST 49 | value: 'localhost' 50 | - name: JAEGER_AGENT_PORT 51 | value: '6832' 52 | ports: 53 | - containerPort: 9000 54 | name: payment 55 | -------------------------------------------------------------------------------- /kubernetes-manifests/order-db-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: order-postgres 5 | labels: 6 | app: acmefit 7 | service: order-db 8 | spec: 9 | ports: 10 | - port: 5432 11 | name: postgres-order 12 | protocol: TCP 13 | selector: 14 | app: acmefit 15 | service: order-db 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: order-postgres 21 | labels: 22 | app: acmefit 23 | service: order-db 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: order-db 29 | strategy: 30 | rollingUpdate: 31 | maxSurge: 1 32 | maxUnavailable: 1 33 | type: RollingUpdate 34 | replicas: 1 35 | template: 36 | metadata: 37 | labels: 38 | app: acmefit 39 | service: order-db 40 | spec: 41 | containers: 42 | - name: postgres 43 | image: postgres:9.5 44 | imagePullPolicy: "Always" 45 | ports: 46 | - containerPort: 5432 47 | name: order-postgres 48 | protocol: "TCP" 49 | env: 50 | - name: POSTGRES_USER 51 | value: pgbench 52 | - name: POSTGRES_PASSWORD 53 | valueFrom: 54 | secretKeyRef: 55 | name: order-postgres-pass 56 | key: password 57 | - name: PGBENCH_PASSWORD 58 | valueFrom: 59 | secretKeyRef: 60 | name: order-postgres-pass 61 | key: password 62 | - name: PGDATA 63 | value: /var/lib/postgresql/data/pgdata 64 | volumeMounts: 65 | - mountPath: /var/lib/postgresql/data 66 | name: postgredb 67 | volumes: 68 | - name: postgredb 69 | emptyDir: {} 70 | -------------------------------------------------------------------------------- /kubernetes-manifests/frontend-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: frontend 5 | labels: 6 | app: acmefit 7 | service: frontend 8 | spec: 9 | ports: 10 | - name: http-frontend 11 | protocol: TCP 12 | port: 80 13 | targetPort: 3000 14 | # nodePort: 30430 15 | selector: 16 | app: acmefit 17 | service: frontend 18 | type: LoadBalancer 19 | --- 20 | apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 21 | kind: Deployment 22 | metadata: 23 | name: frontend 24 | labels: 25 | app: acmefit 26 | service: frontend 27 | spec: 28 | selector: 29 | matchLabels: 30 | app: acmefit 31 | service: frontend 32 | strategy: 33 | type: Recreate 34 | replicas: 1 35 | template: 36 | metadata: 37 | labels: 38 | app: acmefit 39 | service: frontend 40 | spec: 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-front-end:latest 43 | name: frontend 44 | env: 45 | - name: FRONTEND_PORT 46 | value: '3000' 47 | - name: USERS_HOST 48 | value: 'users' 49 | - name: CATALOG_HOST 50 | value: 'catalog' 51 | - name: ORDER_HOST 52 | value: 'order' 53 | - name: CART_HOST 54 | value: 'cart' 55 | - name: USERS_PORT 56 | value: '8083' 57 | - name: CATALOG_PORT 58 | value: '8082' 59 | - name: CART_PORT 60 | value: '5000' 61 | - name: ORDER_PORT 62 | value: '6000' 63 | - name: JAEGER_AGENT_HOST 64 | value: 'localhost' 65 | - name: JAEGER_AGENT_PORT 66 | value: '6832' 67 | ports: 68 | - containerPort: 3000 69 | name: frontend 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demo of ACME Fitness Shop 2 | 3 | ## Getting Started 4 | 5 | These instructions will allow you to run entire ACME Fitness Shop 6 | 7 | ## Requirements 8 | 9 | Based on the type of deployment the requirements will vary 10 | 11 | 1. **docker-compose** - Needs docker-compose version 1.23.1+ 12 | 2. **kubernetes** 13 | 3. **AWS Fargate** 14 | 15 | Other deployment modes coming soon 16 | 17 | ## Overview 18 | 19 | ![Acmeshop Architecture](./acmeshop.png) 20 | 21 | ## Instructions 22 | 23 | 1. Clone this repository 24 | 25 | 2. You will notice the following directory structure 26 | 27 | ```text 28 | ├── README.md 29 | ├── acmeshop.png 30 | ├── aws-fargate 31 | │ ├── README.md 32 | │ ├── acme-fitness-shop.yaml 33 | │ └── cf-template.png 34 | ├── docker-compose 35 | │ ├── README.md 36 | │ └── docker-compose.yml 37 | ├── kubernetes-manifests 38 | │ ├── README.md 39 | │ ├── cart-redis-total.yaml 40 | │ ├── cart-total.yaml 41 | │ ├── catalog-db-initdb-configmap.yaml 42 | │ ├── catalog-db-total.yaml 43 | │ ├── catalog-total.yaml 44 | │ ├── catalog-v2-total.yaml 45 | │ ├── frontend-total.yaml 46 | │ ├── order-db-total.yaml 47 | │ ├── order-total.yaml 48 | │ ├── payment-total.yaml 49 | │ ├── users-db-initdb-configmap.yaml 50 | │ ├── users-db-total.yaml 51 | │ └── users-total.yaml 52 | └── traffic-generator 53 | ├── README.md 54 | ├── locustfile.py 55 | └── requirements.txt 56 | ``` 57 | 58 | 3. Switch to the appropriate directory for deployment 59 | 60 | * [docker-compose](./docker-compose) 61 | * [kubernetes-manifest](./kubernetes-manifests) 62 | * [aws-fargate](./aws-fargate) 63 | 64 | ### Additional Info 65 | 66 | The [traffic-generator](./traffic-generator) is based on **locust** and can be used to create various traffic patterns, if you need it for other demos associated with **Monitoring and Observability.** 67 | -------------------------------------------------------------------------------- /kubernetes-manifests/users-redis-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: users-redis 5 | labels: 6 | app: acmefit 7 | service: users-redis 8 | spec: 9 | ports: 10 | - port: 6379 11 | name: redis-users 12 | selector: 13 | app: acmefit 14 | service: users-redis 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: users-redis 20 | labels: 21 | app: acmefit 22 | service: users-redis 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: acmefit # has to match .spec.template.metadata.labels 27 | service: users-redis 28 | replicas: 1 29 | template: 30 | metadata: 31 | labels: 32 | app: acmefit # has to match .spec.selector.matchLabels 33 | service: users-redis 34 | spec: 35 | containers: 36 | - name: users-redis 37 | image: bitnami/redis 38 | imagePullPolicy: Always 39 | resources: 40 | requests: 41 | cpu: "100m" 42 | memory: "100Mi" 43 | ports: 44 | - name: redis 45 | containerPort: 6379 46 | protocol: "TCP" 47 | env: 48 | - name: REDIS_HOST 49 | value: 'users-redis' 50 | - name: REDIS_PASSWORD 51 | valueFrom: 52 | secretKeyRef: 53 | name: users-redis-pass 54 | key: password 55 | volumeMounts: 56 | - mountPath: /var/lib/redis 57 | name: users-redis-data 58 | # - mountPath: /etc/redis 59 | # name: redis-config 60 | volumes: 61 | - name: users-redis-data 62 | emptyDir: {} 63 | # - name: redis-config 64 | # configMap: 65 | # name: redis-config 66 | # items: 67 | # - key: redis-config 68 | # path: redis.conf 69 | -------------------------------------------------------------------------------- /kubernetes-manifests/cart-redis-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cart-redis 5 | labels: 6 | app: acmefit 7 | service: cart-redis 8 | spec: 9 | ports: 10 | - port: 6379 11 | name: redis-cart 12 | selector: 13 | app: acmefit 14 | service: cart-redis 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: cart-redis 20 | labels: 21 | app: acmefit 22 | service: cart-redis 23 | version: 1.0.0 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit # has to match .spec.template.metadata.labels 28 | service: cart-redis 29 | replicas: 1 30 | template: 31 | metadata: 32 | labels: 33 | app: acmefit # has to match .spec.selector.matchLabels 34 | service: cart-redis 35 | spec: 36 | containers: 37 | - name: cart-redis 38 | image: bitnami/redis 39 | imagePullPolicy: Always 40 | resources: 41 | requests: 42 | cpu: "100m" 43 | memory: "100Mi" 44 | ports: 45 | - name: redis 46 | containerPort: 6379 47 | protocol: "TCP" 48 | env: 49 | - name: REDIS_HOST 50 | value: 'cart-redis' 51 | - name: REDIS_PASSWORD 52 | valueFrom: 53 | secretKeyRef: 54 | name: cart-redis-pass 55 | key: password 56 | volumeMounts: 57 | - mountPath: /var/lib/redis 58 | name: cart-redis-data 59 | # - mountPath: /etc/redis 60 | # name: redis-config 61 | volumes: 62 | - name: cart-redis-data 63 | emptyDir: {} 64 | # - name: redis-config 65 | # configMap: 66 | # name: redis-config 67 | # items: 68 | # - key: redis-config 69 | # path: redis.conf 70 | -------------------------------------------------------------------------------- /kubernetes-manifests/users-db-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: users-mongo 5 | labels: 6 | app: acmefit 7 | service: users-mongo 8 | spec: 9 | ports: 10 | - port: 27017 11 | name: mongo-users 12 | protocol: TCP 13 | selector: 14 | app: acmefit 15 | service: users-mongo 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: users-mongo 21 | labels: 22 | app: acmefit 23 | service: users-mongo 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit # has to match .spec.template.metadata.labels 28 | service: users-mongo 29 | replicas: 1 30 | template: 31 | metadata: 32 | labels: 33 | app: acmefit # has to match .spec.selector.matchLabels 34 | service: users-mongo 35 | spec: 36 | containers: 37 | - name: users-mongo 38 | image: mongo:4 39 | resources: 40 | {} 41 | ports: 42 | - name: users-mongo 43 | containerPort: 27017 44 | protocol: "TCP" 45 | env: 46 | - name: MONGO_INITDB_ROOT_USERNAME 47 | value: 'mongoadmin' 48 | - name: MONGO_INITDB_DATABASE 49 | value: 'acmefit' 50 | - name: MONGO_INITDB_ROOT_PASSWORD 51 | valueFrom: 52 | secretKeyRef: 53 | name: users-mongo-pass 54 | key: password 55 | volumeMounts: 56 | - mountPath: /data/db 57 | name: mongodata 58 | - mountPath: /docker-entrypoint-initdb.d 59 | name: mongo-initdb 60 | volumes: 61 | - name: mongodata 62 | emptyDir: {} 63 | - name: mongo-initdb 64 | configMap: 65 | name: users-initdb-config 66 | # - name: mongodata 67 | # persistentVolumeClaim: 68 | # claimName: mongodata 69 | -------------------------------------------------------------------------------- /kubernetes-manifests/catalog-db-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: catalog-mongo 5 | labels: 6 | app: acmefit 7 | service: catalog-db 8 | spec: 9 | ports: 10 | - port: 27017 11 | name: mongo-catalog 12 | protocol: TCP 13 | selector: 14 | app: acmefit 15 | service: catalog-db 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: catalog-mongo 21 | labels: 22 | app: acmefit 23 | service: catalog-db 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit # has to match .spec.template.metadata.labels 28 | service: catalog-db 29 | replicas: 1 30 | template: 31 | metadata: 32 | labels: 33 | app: acmefit # has to match .spec.selector.matchLabels 34 | service: catalog-db 35 | spec: 36 | containers: 37 | - name: catalog-mongo 38 | image: mongo:4 39 | resources: 40 | {} 41 | ports: 42 | - name: catalog-mongo 43 | containerPort: 27017 44 | protocol: "TCP" 45 | env: 46 | - name: MONGO_INITDB_ROOT_USERNAME 47 | value: 'mongoadmin' 48 | - name: MONGO_INITDB_DATABASE 49 | value: 'acmefit' 50 | - name: MONGO_INITDB_ROOT_PASSWORD 51 | valueFrom: 52 | secretKeyRef: 53 | name: catalog-mongo-pass 54 | key: password 55 | volumeMounts: 56 | - mountPath: /data/db 57 | name: mongodata 58 | - mountPath: /docker-entrypoint-initdb.d 59 | name: mongo-initdb 60 | volumes: 61 | - name: mongodata 62 | emptyDir: {} 63 | - name: mongo-initdb 64 | configMap: 65 | name: catalog-initdb-config 66 | # - name: mongodata 67 | # persistentVolumeClaim: 68 | # claimName: mongodata 69 | -------------------------------------------------------------------------------- /kubernetes-manifests/cart-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cart 5 | labels: 6 | app: acmefit 7 | service: cart 8 | spec: 9 | ports: 10 | - name: http-cart 11 | protocol: TCP 12 | port: 5000 13 | selector: 14 | app: acmefit 15 | service: cart 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: cart 21 | labels: 22 | app: acmefit 23 | service: cart 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: cart 29 | strategy: 30 | type: Recreate 31 | replicas: 1 32 | template: 33 | metadata: 34 | labels: 35 | app: acmefit 36 | service: cart 37 | spec: 38 | volumes: 39 | - name: acmefit-cart-data 40 | emptyDir: {} 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-cart:latest 43 | imagePullPolicy: "Always" 44 | name: cart 45 | env: 46 | - name: REDIS_HOST 47 | value: 'cart-redis' 48 | - name: REDIS_PASSWORD 49 | valueFrom: 50 | secretKeyRef: 51 | name: cart-redis-pass 52 | key: password 53 | - name: REDIS_PORT 54 | value: '6379' 55 | - name: CART_PORT 56 | value: '5000' 57 | - name: USER_HOST 58 | value: 'users' 59 | - name: USER_PORT 60 | value: '8083' 61 | - name: JAEGER_AGENT_HOST 62 | value: 'localhost' 63 | - name: JAEGER_AGENT_PORT 64 | value: '6831' 65 | - name: AUTH_MODE 66 | value: '1' 67 | ports: 68 | - containerPort: 5000 69 | name: cart 70 | volumeMounts: 71 | - mountPath: "/data" 72 | name: "acmefit-cart-data" 73 | resources: 74 | requests: 75 | memory: "64Mi" 76 | cpu: "100m" 77 | limits: 78 | memory: "256Mi" 79 | cpu: "500m" 80 | -------------------------------------------------------------------------------- /kubernetes-manifests/catalog-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: catalog 5 | labels: 6 | app: acmefit 7 | service: catalog 8 | spec: 9 | ports: 10 | - name: http-catalog 11 | protocol: TCP 12 | port: 8082 13 | selector: 14 | app: acmefit 15 | service: catalog 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: catalog 21 | labels: 22 | app: acmefit 23 | service: catalog 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: catalog 29 | strategy: 30 | type: Recreate 31 | replicas: 1 32 | template: 33 | metadata: 34 | labels: 35 | app: acmefit 36 | service: catalog 37 | spec: 38 | volumes: 39 | - name: acmefit-catalog-data 40 | emptyDir: {} 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-catalog:latest 43 | imagePullPolicy: "Always" 44 | name: catalog 45 | env: 46 | - name: CATALOG_DB_HOST 47 | value: 'catalog-mongo' 48 | - name: CATALOG_DB_PASSWORD 49 | valueFrom: 50 | secretKeyRef: 51 | name: catalog-mongo-pass 52 | key: password 53 | - name: CATALOG_DB_PORT 54 | value: '27017' 55 | - name: CATALOG_DB_USERNAME 56 | value: 'mongoadmin' 57 | - name: CATALOG_PORT 58 | value: '8082' 59 | - name: CATALOG_VERSION 60 | value: 'v1' 61 | - name: USERS_HOST 62 | value: 'users' 63 | - name: USERS_PORT 64 | value: '8083' 65 | - name: JAEGER_AGENT_HOST 66 | value: 'localhost' 67 | - name: JAEGER_AGENT_PORT 68 | value: '6831' 69 | ports: 70 | - containerPort: 8082 71 | name: catalog 72 | volumeMounts: 73 | - mountPath: "/data" 74 | name: "acmefit-catalog-data" 75 | resources: 76 | requests: 77 | memory: "64Mi" 78 | cpu: "100m" 79 | limits: 80 | memory: "256Mi" 81 | cpu: "500m" 82 | -------------------------------------------------------------------------------- /kubernetes-manifests/users-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: users 5 | labels: 6 | app: acmefit 7 | service: users 8 | spec: 9 | ports: 10 | - name: http-users 11 | protocol: TCP 12 | port: 8083 13 | selector: 14 | app: acmefit 15 | service: users 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: users 21 | labels: 22 | app: acmefit 23 | service: users 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: users 29 | strategy: 30 | type: Recreate 31 | replicas: 1 32 | template: 33 | metadata: 34 | labels: 35 | app: acmefit 36 | service: users 37 | spec: 38 | volumes: 39 | - name: acmefit-users-data 40 | emptyDir: {} 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-user:latest 43 | imagePullPolicy: "Always" 44 | name: users 45 | env: 46 | - name: USERS_DB_HOST 47 | value: 'users-mongo' 48 | - name: USERS_DB_PASSWORD 49 | valueFrom: 50 | secretKeyRef: 51 | name: users-mongo-pass 52 | key: password 53 | - name: USERS_DB_PORT 54 | value: '27017' 55 | - name: USERS_DB_USERNAME 56 | value: 'mongoadmin' 57 | - name: USERS_PORT 58 | value: '8083' 59 | - name: REDIS_HOST 60 | value: 'users-redis' 61 | - name: REDIS_PORT 62 | value: '6379' 63 | - name: REDIS_PASSWORD 64 | valueFrom: 65 | secretKeyRef: 66 | name: users-redis-pass 67 | key: password 68 | - name: JAEGER_AGENT_HOST 69 | value: 'localhost' 70 | - name: JAEGER_AGENT_PORT 71 | value: '6831' 72 | ports: 73 | - containerPort: 8083 74 | name: users 75 | volumeMounts: 76 | - mountPath: "/data" 77 | name: "acmefit-users-data" 78 | resources: 79 | requests: 80 | memory: "64Mi" 81 | cpu: "100m" 82 | limits: 83 | memory: "256Mi" 84 | cpu: "500m" 85 | -------------------------------------------------------------------------------- /kubernetes-manifests/order-total.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: order 5 | labels: 6 | app: acmefit 7 | service: order 8 | spec: 9 | ports: 10 | - name: http-order 11 | protocol: TCP 12 | port: 6000 13 | selector: 14 | app: acmefit 15 | service: order 16 | --- 17 | apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 18 | kind: Deployment 19 | metadata: 20 | name: order 21 | labels: 22 | app: acmefit 23 | service: order 24 | spec: 25 | selector: 26 | matchLabels: 27 | app: acmefit 28 | service: order 29 | strategy: 30 | type: Recreate 31 | replicas: 1 32 | template: 33 | metadata: 34 | labels: 35 | app: acmefit 36 | service: order 37 | spec: 38 | volumes: 39 | - name: acmefit-order-data 40 | emptyDir: {} 41 | containers: 42 | - image: gcr.io/vmwarecloudadvocacy/acmeshop-order:latest 43 | # orderpostgres:latest 44 | name: order 45 | env: 46 | - name: ORDER_DB_HOST 47 | value: order-postgres 48 | - name: ORDER_DB_PASSWORD 49 | valueFrom: 50 | secretKeyRef: 51 | name: order-postgres-pass 52 | key: password 53 | - name: ORDER_DB_PORT 54 | value: '5432' 55 | - name: AUTH_MODE 56 | value: '1' 57 | - name: ORDER_DB_USERNAME 58 | value: pgbench 59 | - name: PGPASSWORD 60 | valueFrom: 61 | secretKeyRef: 62 | name: order-postgres-pass 63 | key: password 64 | - name: ORDER_AUTH_DB 65 | value: postgres 66 | - name: ORDER_PORT 67 | value: '6000' 68 | - name: PAYMENT_PORT 69 | value: '9000' 70 | - name: PAYMENT_HOST 71 | value: 'payment' 72 | - name: USER_HOST 73 | value: 'users' 74 | - name: USER_PORT 75 | value: '8083' 76 | - name: JAEGER_AGENT_HOST 77 | value: 'localhost' 78 | - name: JAEGER_AGENT_PORT 79 | value: '6831' 80 | ports: 81 | - containerPort: 6000 82 | name: order 83 | volumeMounts: 84 | - mountPath: "/data" 85 | name: "acmefit-order-data" 86 | resources: 87 | requests: 88 | memory: "64Mi" 89 | cpu: "100m" 90 | limits: 91 | memory: "256Mi" 92 | cpu: "500m" 93 | -------------------------------------------------------------------------------- /kubernetes-manifests/catalog-db-initdb-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: catalog-initdb-config 5 | data: 6 | seed.js: | 7 | db.catalog.insertMany([ 8 | {"name":"Yoga Mat","shortdescription":"Magic Yoga Mat!","description":"Our Yoga Mat is magic. You will twist into a human pretzel with the greatest of ease. Never done Yoga before? This mat will turn you into an instant professional with barely any work. It’s the American way!. Namaste!","imageurl1":"/static/images/yogamat_square.jpg","imageurl2":"/static/images/yogamat_thumb2.jpg","imageurl3":"/static/images/yogamat_thumb3.jpg","price":62.5,"tags":["mat"]} 9 | ,{"name":"Water Bottle","shortdescription":"The last Water Bottle you'll ever buy!","description":"Our Water Bottle only has to be filled once! That's right. ONCE. Unlimited water, for the rest of your life. Doesn't that $34.99 seem a lot more reasonable now? Stop buying all those other water bottles that you have to keep refilling like a sucker. Get the ACME bottle today!","imageurl1":"/static/images/bottle_square.jpg","imageurl2":"/static/images/bottle_thumb2.jpg","imageurl3":"/static/images/bottle_thumb3.jpg","price":34.9900016784668,"tags":["bottle"]} 10 | ,{"name":"Fit Bike","shortdescription":"Get Light on our Fit Bike!", "description":"Ride like the wind on your very own ACME Fit Bike. Have you ever wanted to travel as fast as a MotoGP racer on a bicycle with tiny tires?! Me too! Get the Fit Bike, and you'll vroom your way into fitness in 30 seconds flat!","imageurl1":"/static/images/bicycle_square.jpg","imageurl2":"/static/images/bicycle_thumb2.jpg","imageurl3":"/static/images/bicycle_thumb3.jpg", "price":499.99,"tags":["bicycle"]} 11 | ,{"name":"Basket Ball","shortdescription":"World's Roundest Basketball!","description":"That's right. You heard me correctly. The worlds ROUNDEST basketball. Are you tired of your current basketball simply not being round enough. Then it's time to step up to the ACME Basketball. Get your round on!","imageurl1":"/static/images/basketball_square.jpg","imageurl2":"/static/images/basketball_thumb2.jpg","imageurl3":"/static/images/basketball_thumb3.jpg","price":110.75,"tags":["basketball"]} 12 | ,{"name":"Smart Watch","shortdescription":"The watch that makes you smarter!","description":"Do you have trouble remembering things? Can you not remember what day it is? Do you need a robot with a cute women's voice to tell you when to stand up and walk around? Then boy do we have the watch for you! Get the ACME Smart Watch, and never have to remember anything ever again!","imageurl1":"/static/images/smartwatch_square.jpg","imageurl2":"/static/images/smartwatch_thumb2.jpg","imageurl3":"/static/images/smartwatch_thumb3.jpg","price":399.5899963378906,"tags":["watch"]} 13 | ,{"name":"Red Pants","shortdescription":"Because who doesn't need red pants??", "description":"Have you found yourself walking around tech conferences in the same old jeans and vendor t-shirt? Do you need to up your pants game? ACME Red Pants are 100% GUARANTEED to take you to a whole new level. Women will want to meet you. Men will want to be you. You are... Fancy Pants. What are you waiting for??","imageurl1":"/static/images/redpants_square.jpg","imageurl2":"/static/images/redpants_thumb2.jpg","imageurl3":"/static/images/redpants_thumb3.jpg", "price":99.0,"tags":["clothing"]} 14 | ,{"name":"Running shoes","shortdescription":"Mama says they was magic shoes!", "description":"And she was right! Are you slow? Out of shape? But still ready to take on Usain Bolt in the 100? Then strap up your ACME Running Shoes and Run Forest, Run! These shoes will make you run the 100 in 2.5 flat!","imageurl1":"/static/images/shoes_square.jpg","imageurl2":"/static/images/shoes_thumb2.jpg","imageurl3":"/static/images/shoes_thumb3.jpg", "price":120.00,"tags":["running"]} 15 | ,{"name":"Weights","shortdescription":"Get ripped without breaking a sweat!","description":"Are you ready to get Pumped Up with Hanz and Franz? Or get swole like Arnold? It's time to hit the Add to Cart button on the ACME Weights. Just 45 seconds a day, 3 days a week, and you'll be showing those Muscle Beach clowns how it's done in no time!","imageurl1":"/static/images/weights_square.jpg","imageurl2":"/static/images/weights_thumb2.jpg","imageurl3":"/static/images/weights_thumb3.jpg", "price":49.99,"tags":["weight"]} ]); 16 | -------------------------------------------------------------------------------- /kubernetes-manifests/jaeger-all-in-one.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017-2019 The Jaeger Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | # in compliance with the License. You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software distributed under the License 10 | # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | # or implied. See the License for the specific language governing permissions and limitations under 12 | # the License. 13 | # 14 | 15 | apiVersion: v1 16 | kind: List 17 | items: 18 | - apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: jaeger 22 | labels: 23 | app: jaeger 24 | app.kubernetes.io/name: jaeger 25 | app.kubernetes.io/component: all-in-one 26 | spec: 27 | selector: 28 | matchLabels: 29 | app: jaeger 30 | app.kubernetes.io/name: jaeger 31 | app.kubernetes.io/component: all-in-one 32 | replicas: 1 33 | strategy: 34 | type: Recreate 35 | template: 36 | metadata: 37 | labels: 38 | app: jaeger 39 | app.kubernetes.io/name: jaeger 40 | app.kubernetes.io/component: all-in-one 41 | annotations: 42 | prometheus.io/scrape: "true" 43 | prometheus.io/port: "16686" 44 | spec: 45 | containers: 46 | - env: 47 | - name: COLLECTOR_ZIPKIN_HTTP_PORT 48 | value: "9411" 49 | image: jaegertracing/all-in-one 50 | name: jaeger 51 | ports: 52 | - containerPort: 5775 53 | protocol: UDP 54 | - containerPort: 6831 55 | protocol: UDP 56 | - containerPort: 6832 57 | protocol: UDP 58 | - containerPort: 5778 59 | protocol: TCP 60 | - containerPort: 16686 61 | protocol: TCP 62 | - containerPort: 9411 63 | protocol: TCP 64 | readinessProbe: 65 | httpGet: 66 | path: "/" 67 | port: 14269 68 | initialDelaySeconds: 5 69 | - apiVersion: v1 70 | kind: Service 71 | metadata: 72 | name: jaeger-query 73 | labels: 74 | app: jaeger 75 | app.kubernetes.io/name: jaeger 76 | app.kubernetes.io/component: query 77 | spec: 78 | ports: 79 | - name: query-http 80 | port: 80 81 | protocol: TCP 82 | targetPort: 16686 83 | selector: 84 | app.kubernetes.io/name: jaeger 85 | app.kubernetes.io/component: all-in-one 86 | type: LoadBalancer 87 | - apiVersion: v1 88 | kind: Service 89 | metadata: 90 | name: jaeger-collector 91 | labels: 92 | app: jaeger 93 | app.kubernetes.io/name: jaeger 94 | app.kubernetes.io/component: collector 95 | spec: 96 | ports: 97 | - name: jaeger-collector-tchannel 98 | port: 14267 99 | protocol: TCP 100 | targetPort: 14267 101 | - name: jaeger-collector-http 102 | port: 14268 103 | protocol: TCP 104 | targetPort: 14268 105 | - name: jaeger-collector-zipkin 106 | port: 9411 107 | protocol: TCP 108 | targetPort: 9411 109 | selector: 110 | app.kubernetes.io/name: jaeger 111 | app.kubernetes.io/component: all-in-one 112 | type: ClusterIP 113 | - apiVersion: v1 114 | kind: Service 115 | metadata: 116 | name: jaeger-agent 117 | labels: 118 | app: jaeger 119 | app.kubernetes.io/name: jaeger 120 | app.kubernetes.io/component: agent 121 | spec: 122 | ports: 123 | - name: agent-zipkin-thrift 124 | port: 5775 125 | protocol: UDP 126 | targetPort: 5775 127 | - name: agent-compact 128 | port: 6831 129 | protocol: UDP 130 | targetPort: 6831 131 | - name: agent-binary 132 | port: 6832 133 | protocol: UDP 134 | targetPort: 6832 135 | - name: agent-configs 136 | port: 5778 137 | protocol: TCP 138 | targetPort: 5778 139 | clusterIP: None 140 | selector: 141 | app.kubernetes.io/name: jaeger 142 | app.kubernetes.io/component: all-in-one 143 | - apiVersion: v1 144 | kind: Service 145 | metadata: 146 | name: zipkin 147 | labels: 148 | app: jaeger 149 | app.kubernetes.io/name: jaeger 150 | app.kubernetes.io/component: zipkin 151 | spec: 152 | ports: 153 | - name: jaeger-collector-zipkin 154 | port: 9411 155 | protocol: TCP 156 | targetPort: 9411 157 | clusterIP: None 158 | selector: 159 | app.kubernetes.io/name: jaeger 160 | app.kubernetes.io/component: all-in-one 161 | 162 | -------------------------------------------------------------------------------- /docker-compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | catalog-db: 5 | image: gcr.io/vmwarecloudadvocacy/acmeshop-catalog-db:latest 6 | hostname: catalog-db 7 | environment: 8 | - MONGO_INITDB_ROOT_USERNAME=mongoadmin 9 | - MONGO_INITDB_ROOT_PASSWORD=secret 10 | - MONGO_INITDB_DATABASE=acmefit 11 | cap_add: 12 | - CHOWN 13 | - SETGID 14 | - SETUID 15 | catalog: 16 | image: gcr.io/vmwarecloudadvocacy/acmeshop-catalog:1.2.1 17 | hostname: catalog 18 | restart: always 19 | ports: 20 | - '8082:8082' 21 | cap_add: 22 | - NET_BIND_SERVICE 23 | environment: 24 | - CATALOG_DB_USERNAME=mongoadmin 25 | - CATALOG_DB_PASSWORD=secret 26 | - CATALOG_DB_HOST=catalog-db 27 | - CATALOG_PORT=8082 28 | - CATALOG_DB_PORT=27017 29 | - CATALOG_VERSION=v1 30 | - USERS_HOST=user 31 | - USERS_PORT=8083 32 | - JAEGER_AGENT_HOST=jaeger 33 | - JAEGER_AGENT_PORT=6831 34 | depends_on: 35 | - catalog-db 36 | user-db: 37 | image: gcr.io/vmwarecloudadvocacy/acmeshop-user-db:latest 38 | hostname: user-db 39 | environment: 40 | - MONGO_INITDB_ROOT_USERNAME=mongoadmin 41 | - MONGO_INITDB_ROOT_PASSWORD=secret 42 | - MONGO_INITDB_DATABASE=acmefit 43 | cap_add: 44 | - CHOWN 45 | - SETGID 46 | - SETUID 47 | tmpfs: 48 | - /tmp:rw,noexec,nosuid 49 | user-redis-db: 50 | image: bitnami/redis 51 | hostname: user-redis-db 52 | restart: always 53 | environment: 54 | - REDIS_PASSWORD=secret 55 | ports: 56 | - '6379' 57 | user: 58 | image: gcr.io/vmwarecloudadvocacy/acmeshop-user:2.0.1 59 | hostname: user 60 | restart: always 61 | ports: 62 | - '8083:8083' 63 | cap_add: 64 | - NET_BIND_SERVICE 65 | environment: 66 | - USERS_DB_USERNAME=mongoadmin 67 | - USERS_DB_PASSWORD=secret 68 | - USERS_DB_HOST=user-db 69 | - USERS_DB_PORT=27017 70 | - USERS_PORT=8083 71 | - REDIS_DB_HOST=user-redis-db 72 | - REDIS_DB_PORT=6379 73 | - REDIS_DB_PASSWORD=secret 74 | - JAEGER_AGENT_HOST=jaeger 75 | - JAEGER_AGENT_PORT=6831 76 | redis-db: 77 | image: bitnami/redis 78 | hostname: redis-db 79 | restart: always 80 | environment: 81 | - REDIS_PASSWORD=secret 82 | ports: 83 | - '6379:6379' 84 | cart: 85 | image: gcr.io/vmwarecloudadvocacy/acmeshop-cart:1.3.1 86 | hostname: cart 87 | restart: always 88 | environment: 89 | - REDIS_HOST=redis-db 90 | - REDIS_PORT=6379 91 | - REDIS_PASSWORD=secret 92 | - CART_PORT=5000 93 | - AUTH_MODE=1 94 | - USER_HOST=user 95 | - USER_PORT=8083 96 | - JAEGER_AGENT_HOST=jaeger 97 | - JAEGER_AGENT_PORT=6831 98 | ports: 99 | - '5000:5000' 100 | depends_on: 101 | - redis-db 102 | front-end: 103 | image: gcr.io/vmwarecloudadvocacy/acmeshop-front-end:2.2.0 104 | hostname: front-end 105 | restart: always 106 | ports: 107 | - '3000:3000' 108 | cap_add: 109 | - NET_BIND_SERVICE 110 | environment: 111 | - PORT=3000 112 | - USERS_HOST=user 113 | - CATALOG_HOST=catalog 114 | - CART_HOST=cart 115 | - ORDER_HOST=order 116 | - USERS_PORT=8083 117 | - CATALOG_PORT=8082 118 | - CART_PORT=5000 119 | - ORDER_PORT=6000 120 | - JAEGER_AGENT_HOST=jaeger 121 | - JAEGER_AGENT_PORT=6832 122 | jaeger: 123 | image: jaegertracing/all-in-one:1.11 124 | hostname: jaeger 125 | restart: always 126 | ports: 127 | - '14268:14268' 128 | - '16686:16686' 129 | - '5778:5778' 130 | - '6831-6832:6831-6832/udp' 131 | - '5775:5775/udp' 132 | postgres: 133 | image: postgres:12.1-alpine 134 | hostname: postgres 135 | restart: always 136 | ports: 137 | - '5432' 138 | environment: 139 | - POSTGRES_PASSWORD=password 140 | - POSTGRES_USER=postgres 141 | - POSTGRES_DB=postgres 142 | order: 143 | image: gcr.io/vmwarecloudadvocacy/order-postgres-tracing:3.0 144 | hostname: order 145 | restart: always 146 | ports: 147 | - '6000:6000' 148 | environment: 149 | - JAEGER_AGENT_HOST=jaeger 150 | - JAEGER_AGENT_PORT=6831 151 | - ORDER_DB_USERNAME=postgres 152 | - ORDER_AUTH_DB=postgres 153 | - ORDER_DB_PASSWORD=password 154 | - ORDER_DB_HOST=postgres 155 | - ORDER_DB_PORT=5432 156 | - ORDER_PORT=6000 157 | - PAYMENT_PORT=9000 158 | - PAYMENT_HOST=payment 159 | - AUTH_MODE=1 160 | - USER_PORT=8083 161 | - USER_HOST=user 162 | depends_on: 163 | - postgres 164 | payment: 165 | image: gcr.io/vmwarecloudadvocacy/acmeshop-payment:4.6 166 | hostname: payment 167 | restart: always 168 | ports: 169 | - '9000' 170 | environment: 171 | - JAEGER_AGENT_HOST=jaeger 172 | - JAEGER_AGENT_PORT=6832 173 | - PAYMENT_PORT=9000 174 | - USERS_HOST=user 175 | - USERS_PORT=8083 176 | -------------------------------------------------------------------------------- /aws-fargate/README.md: -------------------------------------------------------------------------------- 1 | # ACME Fitness Shop on AWS Fargate 2 | 3 | ***WARNING** You will be billed for the AWS resources used if you create a stack from this template.* 4 | 5 | ## Pre-requisites 6 | 7 | To run this CloudFormation stack, you'll need to have an AWS account. With permission to create: 8 | 9 | * Log groups 10 | * ECS Clusters 11 | * Cloud Maps 12 | * Execution Roles 13 | * ECS Task Definitions 14 | * ECS Services 15 | * EC2 Security Groups 16 | 17 | ## Deploy stack 18 | 19 | You can deploy a new CloudFormation stack using 20 | 21 | ```bash 22 | aws cloudformation create-stack \ 23 | --capabilities CAPABILITY_NAMED_IAM \ 24 | --stack-name mystack \ 25 | --parameters ParameterKey=User,ParameterValue= \ 26 | ParameterKey=Team,ParameterValue= \ 27 | ParameterKey=SourceSecurityGroup,ParameterValue= \ 28 | ParameterKey=Subnets,ParameterValue= \ 29 | ParameterKey=VPC,ParameterValue= \ 30 | --template-body file://///acme-fitness-shop.yaml 31 | ``` 32 | 33 | ## End result 34 | 35 | This CloudFormation template will create 44 resources. The _Resource ID_ in the table below is how it is listed in the YAML file. The _Type_ is what is created in your AWS account 36 | 37 | | Resource ID | Type | 38 | |-------------------------------|--------------------------------------------| 39 | | ACMELogGroup | AWS::Logs::LogGroup | 40 | | CartService | AWS::ECS::Service | 41 | | CartServiceDiscovery | AWS::ServiceDiscovery::Service | 42 | | CartServiceSecurityGroup | AWS::EC2::SecurityGroup | 43 | | CartTaskDefinition | AWS::ECS::TaskDefinition | 44 | | CatalogDbService | AWS::ECS::Service | 45 | | CatalogDbServiceDiscovery | AWS::ServiceDiscovery::Service | 46 | | CatalogDbServiceSecurityGroup | AWS::EC2::SecurityGroup | 47 | | CatalogDbTaskDefinition | AWS::ECS::TaskDefinition | 48 | | CatalogService | AWS::ECS::Service | 49 | | CatalogServiceDiscovery | AWS::ServiceDiscovery::Service | 50 | | CatalogServiceSecurityGroup | AWS::EC2::SecurityGroup | 51 | | CatalogTaskDefinition | AWS::ECS::TaskDefinition | 52 | | Cluster | AWS::ECS::Cluster | 53 | | FrontEndService | AWS::ECS::Service | 54 | | FrontEndServiceDiscovery | AWS::ServiceDiscovery::Service | 55 | | FrontEndServiceSecurityGroup | AWS::EC2::SecurityGroup | 56 | | FrontEndTaskDefinition | AWS::ECS::TaskDefinition | 57 | | OrderDbService | AWS::ECS::Service | 58 | | OrderDbServiceDiscovery | AWS::ServiceDiscovery::Service | 59 | | OrderDbServiceSecurityGroup | AWS::EC2::SecurityGroup | 60 | | OrderDbTaskDefinition | AWS::ECS::TaskDefinition | 61 | | OrderService | AWS::ECS::Service | 62 | | OrderServiceDiscovery | AWS::ServiceDiscovery::Service | 63 | | OrderServiceSecurityGroup | AWS::EC2::SecurityGroup | 64 | | OrderTaskDefinition | AWS::ECS::TaskDefinition | 65 | | PaymentService | AWS::ECS::Service | 66 | | PaymentServiceDiscovery | AWS::ServiceDiscovery::Service | 67 | | PaymentServiceSecurityGroup | AWS::EC2::SecurityGroup | 68 | | PaymentTaskDefinition | AWS::ECS::TaskDefinition | 69 | | RedisDbService | AWS::ECS::Service | 70 | | RedisDbServiceDiscovery | AWS::ServiceDiscovery::Service | 71 | | RedisDbServiceSecurityGroup | AWS::EC2::SecurityGroup | 72 | | RedisDbTaskDefinition | AWS::ECS::TaskDefinition | 73 | | ServiceDiscovery | AWS::ServiceDiscovery::PrivateDnsNamespace | 74 | | TaskExecutionRole | AWS::IAM::Role | 75 | | UserDbService | AWS::ECS::Service | 76 | | UserDbServiceDiscovery | AWS::ServiceDiscovery::Service | 77 | | UserDbServiceSecurityGroup | AWS::EC2::SecurityGroup | 78 | | UserDbTaskDefinition | AWS::ECS::TaskDefinition | 79 | | UserService | AWS::ECS::Service | 80 | | UserServiceDiscovery | AWS::ServiceDiscovery::Service | 81 | | UserServiceSecurityGroup | AWS::EC2::SecurityGroup | 82 | | UserTaskDefinition | AWS::ECS::TaskDefinition | 83 | 84 | ![chart](./cf-template.png) 85 | 86 | ## Delete stack 87 | 88 | To delete a stack you've built, run 89 | 90 | ```bash 91 | aws cloudformation delete-stack \ 92 | --stack-name mystack 93 | ``` 94 | 95 | ## Monitoring the stack 96 | 97 | Optionally, you can use [Wavefront](https://wavefront.com) to monitor your stack. To get started, we've added a sample dashboard (`wavefront.json`). To use this dashboard, first create a new dashboard ans select the **JSON** button on the top right hand side of the screen. In that window, you can copy/paste the contents of `wavefront.json` (just be sure to update the variables _AWS Account ID_ and _ECS Cluster_). 98 | 99 | ![dashboard](./wavefront.png) 100 | -------------------------------------------------------------------------------- /traffic-generator/locustfile.py: -------------------------------------------------------------------------------- 1 | # This program will generate traffic for ACME Fitness Shop App. It simulates both Authenticated and Guest user scenarios. You can run this program either from Command line or from 2 | # the web based UI. Refer to the "locust" documentation for further information. 3 | from time import sleep 4 | from locust import HttpUser, task, SequentialTaskSet, between 5 | import random 6 | import logging 7 | 8 | # List of users (pre-loaded into ACME Fitness shop) 9 | users = ["eric", "phoebe", "dwight", "han", "elaine", "walter"] 10 | 11 | # GuestUserBrowsing simulates traffic for a Guest User (Not logged in) 12 | class UserBrowsing(SequentialTaskSet): 13 | def on_start(self): 14 | self.getProducts() 15 | def listCatalogItems(self): 16 | products = [] 17 | response = self.client.get("/products") 18 | if response.ok: 19 | items = response.json()["data"] 20 | for item in items: 21 | products.append(item["id"]) 22 | return products 23 | def getProductDetails(self, id): 24 | """Get details of a specific product""" 25 | details = {} 26 | response = self.client.get("/products/"+id) 27 | if response.ok: 28 | details = response.json()["data"] 29 | logging.debug("getProductDetails: " + str(details)) 30 | return details 31 | def getProductImages(self,id): 32 | """Gets all three image URLs for a product""" 33 | details = self.getProductDetails(id) 34 | if details: 35 | for x in range(1, 4): 36 | self.client.get(details["imageUrl"+str(x)]) 37 | def getProductName(self, id): 38 | name = "" 39 | details = self.getProductDetails(id) 40 | if details: 41 | name = details["name"] 42 | logging.debug("NAME: "+name+ " for id: "+id) 43 | return name 44 | 45 | @task 46 | def getProducts(self): 47 | logging.debug("User - Get Products") 48 | self.client.get("/products") 49 | @task(2) 50 | def getProduct(self): 51 | """Get details of a specific product""" 52 | logging.debug("User - Get a product") 53 | products = self.listCatalogItems() 54 | id = random.choice(products) 55 | response = self.client.get("/products/"+ id) 56 | if response.ok: 57 | product = response.json() 58 | logging.debug("Product info - " + str(product)) 59 | @task 60 | def getImages(self): 61 | """Get images of a random product""" 62 | logging.debug("User - Get images of random product") 63 | products = self.listCatalogItems() 64 | id = random.choice(products) 65 | self.getProductImages(id) 66 | @task(2) 67 | def index(self): 68 | self.client.get("/") 69 | 70 | # AuthUserBrowsing simulates traffic for Authenticated Users (Logged in) 71 | class AuthUserBrowsing(UserBrowsing): 72 | """ 73 | AuthUserBrowsing extends the base UserBrowsing class as an authenticated user 74 | interacting with the cart and making orders 75 | """ 76 | Order_Info = { "userid":"8888", 77 | "firstname":"Eric", 78 | "lastname": "Cartman", 79 | "address":{ 80 | "street":"20 Riding Lane Av", 81 | "city":"San Francisco", 82 | "zip":"10201", 83 | "state": "CA", 84 | "country":"USA"}, 85 | "email":"jblaze@marvel.com", 86 | "delivery":"UPS/FEDEX", 87 | "card":{ 88 | "type":"amex/visa/mastercard/bahubali", 89 | "number":"349834797981", 90 | "expMonth":"12", 91 | "expYear": "2022", 92 | "ccv":"123" 93 | }, 94 | "cart":[ 95 | {"id":"1234", "description":"redpants", "quantity":"1", "price":"4"}, 96 | {"id":"5678", "description":"bluepants", "quantity":"1", "price":"4"} 97 | ], 98 | "total":"100"} 99 | 100 | def on_start(self): 101 | self.login() 102 | def removeProductFromCart(self, userid, productid): 103 | """Removes a specific product from the cart by setting the quantity of the product to 0""" 104 | response = self.client.post("/cart/item/modify/"+userid, json={"itemid": productid, "quantity": 0}) 105 | if response.ok: 106 | logging.debug("Auth User - Removed item: "+productid+" for user: "+userid) 107 | else: 108 | logging.warning("failed to remove cart entry. item: "+productid+" for user: "+userid) 109 | 110 | @task 111 | def login(self): 112 | """Login a random user""" 113 | user = random.choice(users) 114 | logging.debug("Auth User - Login user " + user) 115 | response = self.client.post("/login/", json={"username": user, "password":"vmware1!"}) 116 | if response.ok: 117 | body = response.json() 118 | self.user.userid = body["token"] 119 | @task(2) 120 | def addToCart(self): 121 | """Randomly adds 1 or 2 of a random product to the cart""" 122 | products = self.listCatalogItems() 123 | productid = random.choice(products) 124 | if not self.user.userid: 125 | logging.warning("Not logged in, skipping 'Add to Cart'") 126 | return 127 | logging.debug("Add to Cart for user " + self.user.userid) 128 | details = self.getProductDetails(productid) 129 | cart = self.client.post("/cart/item/add/" + self.user.userid, json={ 130 | "name": details["name"], 131 | "price": details["price"], 132 | "shortDescription": "Test add to cart", 133 | "quantity": random.randint(1,2), 134 | "itemid": productid 135 | }) 136 | @task 137 | def removeFromCart(self): 138 | """Remove a random product from the cart. Helps prevent the cart from overflowing""" 139 | products = self.listCatalogItems() 140 | productid = random.choice(products) 141 | self.removeProductFromCart(self.user.userid, productid) 142 | @task 143 | def checkout(self): 144 | if not self.user.userid: 145 | logging.warning("Not logged in, skipping 'Add to Checkout'") 146 | return 147 | userCart = self.client.get("/cart/items/" + self.user.userid).json() 148 | order = self.client.post("/order/add/"+ self.user.userid, json=self.Order_Info) 149 | class UserBehavior(SequentialTaskSet): 150 | tasks = [AuthUserBrowsing, UserBrowsing] 151 | class WebSiteUser(HttpUser): 152 | sleep(3) # Sleep on start of a user incase the target app isn't completely accessible yet. 153 | tasks = [UserBehavior] 154 | userid = "" 155 | wait_time = between(0.5, 3) 156 | -------------------------------------------------------------------------------- /kubernetes-manifests/README.md: -------------------------------------------------------------------------------- 1 | # ACMEFIT K8s 2 | 3 | This repo contains a Polyglot demo application comprised of (presently) 6 microservices and 4 datastores. 4 | 5 | The contents here are the necessary YAML files to deploy the ACMEFIT application in a kubernetes cluster. 6 | 7 | This app is developed by team behind www.cloudjourney.io 8 | 9 | The current version of the application passes JSON Web Tokens (JWT) for authentication on certain API calls. The application will not work as expected if the `users` service is not present to issue / authenticate these tokens. 10 | 11 | ## Datastore Dependent Services 12 | 13 | This section covers the deployment of the datastore dependent microservices. It is recommended to deploy these services first. 14 | 15 | ### Cart Service 16 | 17 | Before deploying the cart datastore (Redis) and cart service please add a secret for the service to use in authenticating with the cache. 18 | *Note: Please replace 'value' in the command below with the desired password text. Changing the name of the secret object or the 'password' key may cause deployment issues* 19 | 20 | ``` 21 | kubectl create secret generic cart-redis-pass --from-literal=password= 22 | ``` 23 | 24 | Once the secret object is created, deploy the redis cache and cart service: 25 | 26 | ``` 27 | kubectl apply -f cart-redis-total.yaml 28 | kubectl apply -f cart-total.yaml 29 | ``` 30 | 31 | ### Catalog Service 32 | 33 | Before deploying the catalog datastore (mongo) and catalog service please add a secret for the service to use in authenticating with the cache. 34 | *Note: Please replace 'value' in the command below with the desired password text. Changing the name of the secret object or the 'password' key may cause deployment issues* 35 | 36 | ``` 37 | kubectl create secret generic catalog-mongo-pass --from-literal=password= 38 | ``` 39 | 40 | Run the following command to initialize the catalog database with items: 41 | 42 | ``` 43 | kubectl create -f catalog-db-initdb-configmap.yaml 44 | ``` 45 | 46 | Finally, deploy the mongo instance and catalog service: 47 | 48 | ``` 49 | kubectl apply -f catalog-db-total.yaml 50 | kubectl apply -f catalog-total.yaml 51 | ``` 52 | ### Payment Service 53 | 54 | The payment service does not have an associated datastore. It can be deployed with the following command: 55 | 56 | ``` 57 | kubectl apply -f payment-total.yaml 58 | ``` 59 | NOTE: PAYMENT SERVICE MUST BE UP FIRST IN ORDER FOR ORDER SERVICE TO PROPERLY COMPLETE TRANSACTIONS 60 | 61 | ### Order Service 62 | 63 | Before deploying the orders datastore (postgres) and order service please add a secret for the service to use in authenticating with the cache. 64 | *Note: Please replace 'value' in the command below with the desired password text. Changing the name of the secret object or the 'password' key may cause deployment issues* 65 | 66 | Before running order please add the following secret: 67 | 68 | ``` 69 | kubectl create secret generic order-postgres-pass --from-literal=password= 70 | ``` 71 | 72 | Once the secret object is created, deploy the mongo instance and order service: 73 | 74 | ``` 75 | kubectl apply -f order-db-total.yaml 76 | kubectl apply -f order-total.yaml 77 | ``` 78 | 79 | ### Users Service 80 | 81 | Before deploying the users datastore (mongo), users cache (redis) and users service please add secrets for the service to use in authenticating with the database and cache. 82 | *Note: Please replace 'value' in the command below with the desired password text. Changing the name of the secret object or the 'password' key may cause deployment issues* 83 | 84 | Before running order please add the following secret: 85 | 86 | ``` 87 | kubectl create secret generic users-mongo-pass --from-literal=password= 88 | kubectl create secret generic users-redis-pass --from-literal=password= 89 | ``` 90 | 91 | Next you need to run the following to initialize the database with an initial set of users: 92 | 93 | ``` 94 | kubectl create -f users-db-initdb-configmap.yaml 95 | ``` 96 | 97 | Once the secret object is created, and the users database is seeded, deploy the users database and users service: 98 | 99 | ``` 100 | kubectl apply -f users-db-total.yaml 101 | kubectl apply -f users-redis-total.yaml 102 | kubectl apply -f users-total.yaml 103 | ``` 104 | **_NOTE: The base set of users is preconfigured. For now, please login as one of this set (eric, dwight, han, or phoebe). The password for these users is 'vmware1!'_** 105 | 106 | ## Datastore Independent Services 107 | 108 | ### Front End Service 109 | 110 | The front end service also functions without an associated datastore. The manifests in this repository deploy the front end service as a NodePort type for testing purposes. If suitable for the deployment environment, the service type could be changed to 'LoadBalancer' in the `frontend-total.yaml` manifest in this repository. 111 | 112 | To deploy the front end service, run the following command: 113 | 114 | ``` 115 | kubectl apply -f frontend-total.yaml 116 | ``` 117 | 118 | To find the external port on which to access the site in browser, run the following command: 119 | 120 | ``` 121 | kubectl get services -l service=frontend 122 | ``` 123 | 124 | The output of the above command should be similar to this: 125 | 126 | ``` 127 | $ kubectl get services -l service=frontend 128 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 129 | frontend NodePort 10.0.0.81 3000:30430/TCP 3d 130 | ``` 131 | 132 | The external value appears under 'PORT(S)'. It is after the '3000:' and before the '/TCP' portion of the string. Appending it to the public address of the Kubernetes cluster (or loadbalancer fronting the cluster) to access the site. 133 | 134 | ### Point-of-Sales 135 | 136 | Just like the front end service, the Point-of-Sales app functions without any associated datastores. The only prerequisite is that the FrontEnd service is deployed. The manifests in this repository deploy the Point-of-Sales service as a NodePort type for testing purposes. If you're running the Point-of-Sales app on a different Kubernetes cluster, or as a standalone container, you'll have to update the value of `FRONTEND_HOST` (set to `frontend.default.svc.cluster.local` by default) to match the IP or FQDN of the front end service. 137 | 138 | To deploy the service, run the following command: 139 | 140 | ``` 141 | kubectl apply -f point-of-sales-total.yaml 142 | ``` 143 | 144 | To find the external port on which to access the site in browser, run the following command: 145 | 146 | ``` 147 | kubectl get services -l service=pos 148 | ``` 149 | 150 | The output of the above command should be similar to this: 151 | 152 | ``` 153 | $ kubectl get services -l service=frontend 154 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 155 | pos NodePort 10.0.0.81 3000:30431/TCP 3d 156 | ``` 157 | 158 | The external value appears under 'PORT(S)'. It is after the '3000:' and before the '/TCP' portion of the string. Appending it to the public address of the Kubernetes cluster (or loadbalancer fronting the cluster) to access the Point-of-Sales app. 159 | 160 | ## Distributed Tracing 161 | 162 | **Note: Distributed tracing is advanced functionality which requires additional configuration to use successfully. Please read this section carefully before attempting to test / demonstrate tracing** 163 | 164 | The current version of the application has been augmented with distributed tracing funcionality. Each of the services has two relevant environment vairables `JAEGER_AGENT_HOST` and `JAEGER_AGENT_PORT`. Regardless of the span aggregator being used, the code expects that these two values to be populates with the hostname and port of whichever span collecter is being used *likely the jaeger agent*. 165 | 166 | To avoid issues with unresolvable hostnames, `JAEGER_AGENT_HOST` is set to `localhost` in all of the manifests in this repo. To use tracing, this value will need to be replaced. If using the `jaeger-all-in-one.yml` manifest included in this repo, this value should be changed to `jaeger.`. 167 | 168 | It is strongly recommended that the `JAEGER_AGENT_PORT` values not be modified as the tracing library implementations for specific languages favor certain ports. 169 | -------------------------------------------------------------------------------- /aws-fargate/wavefront-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ACME Fitness Shop - Fargate", 3 | "url": "ACME-Fitness-Shop--Fargate", 4 | "eventFilterType": "BYCHART", 5 | "displayDescription": false, 6 | "displaySectionTableOfContents": true, 7 | "displayQueryParameters": true, 8 | "sections": [ 9 | { 10 | "rows": [ 11 | { 12 | "charts": [ 13 | { 14 | "chartSettings": { 15 | "type": "markdown-widget", 16 | "plainMarkdownContent": "| | |\n| --- | --- |\n| ![aws ecs fargate](https://retgits-img.s3-us-west-2.amazonaws.com/fargate-logo.png) | This dashboard provides real-time visibility into the **ACME Fitness Shop** running on **Amazon Elastic Container Service (ECS)** using the **Fargate** launch type. AWS Fargate is a compute engine for Amazon ECS that allows you to run containers without having to manage servers or clusters. The metrics in this dashboard come from [CloudWatch](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-metrics.html \"_blank\") and doesn't provide metrics on individual containers. If you need detailed container metrics, check out the [Wavefront docs](https://docs.wavefront.com/integrations_aws_ecs.html \"_blank\").|", 17 | "autoColumnTags": false, 18 | "sparklineDisplayColor": null, 19 | "sparklineLineColor": null, 20 | "sparklineFillColor": null 21 | }, 22 | "includeObsoleteMetrics": false, 23 | "interpolatePoints": false, 24 | "name": "", 25 | "units": "Units", 26 | "base": 0, 27 | "chartAttributes": { 28 | "dashboardLayout": { 29 | "x": 0, 30 | "y": 0, 31 | "h": 6, 32 | "w": 6 33 | } 34 | }, 35 | "sources": [ 36 | { 37 | "name": "A", 38 | "query": "", 39 | "secondaryAxis": false, 40 | "scatterPlotSource": "Y", 41 | "querybuilderSerialization": "{\"_v\":1,\"metric\":\"\",\"filters\":[[],\"and\"],\"functions\":[]}", 42 | "querybuilderEnabled": true, 43 | "sourceDescription": "" 44 | } 45 | ], 46 | "noDefaultEvents": false 47 | }, 48 | { 49 | "chartSettings": { 50 | "type": "markdown-widget", 51 | "plainMarkdownContent": "The CloudFormation template to deploy the ACME Fitness Shop on AWS Fargate is available on [GitHub](https://github.com/vmwarecloudadvocacy/acme_fitness_demo/tree/master/aws-fargate). The CloudFormation stack will create:\n\n* Log groups\n* ECS Clusters\n* Cloud Maps\n* Execution Roles\n* ECS Task Definitions\n* ECS Services\n* EC2 Security Groups", 52 | "autoColumnTags": false, 53 | "sparklineDisplayColor": null, 54 | "sparklineLineColor": null, 55 | "sparklineFillColor": null 56 | }, 57 | "includeObsoleteMetrics": false, 58 | "interpolatePoints": false, 59 | "name": "Deployment", 60 | "units": "Units", 61 | "base": 0, 62 | "chartAttributes": { 63 | "dashboardLayout": { 64 | "x": 6, 65 | "y": 0, 66 | "h": 6, 67 | "w": 6 68 | } 69 | }, 70 | "sources": [ 71 | { 72 | "name": "A", 73 | "query": "", 74 | "secondaryAxis": false, 75 | "scatterPlotSource": "Y", 76 | "querybuilderSerialization": "{\"_v\":1,\"metric\":\"\",\"filters\":[[],\"and\"],\"functions\":[]}", 77 | "querybuilderEnabled": true, 78 | "sourceDescription": "" 79 | } 80 | ], 81 | "noDefaultEvents": false 82 | } 83 | ], 84 | "heightFactor": 120 85 | } 86 | ], 87 | "name": "Overview" 88 | }, 89 | { 90 | "rows": [ 91 | { 92 | "charts": [ 93 | { 94 | "chartSettings": { 95 | "y1ScaleSIBy1024": false, 96 | "fixedLegendEnabled": false, 97 | "fixedLegendFilterField": "CURRENT", 98 | "y0ScaleSIBy1024": false, 99 | "sparklineDisplayFontSize": "200", 100 | "fixedLegendFilterLimit": 5, 101 | "sparklineValueColorMapColors": [], 102 | "fixedLegendPosition": "RIGHT", 103 | "customTags": [], 104 | "lineType": "linear", 105 | "sparklineValueColorMapApplyTo": "TEXT", 106 | "windowing": "full", 107 | "sparklineSize": "NONE", 108 | "fixedLegendFilterSort": "TOP", 109 | "sparklineValueColorMapValuesV2": [], 110 | "y1Units": "", 111 | "sparklineDisplayValueType": "VALUE", 112 | "type": "sparkline", 113 | "y1UnitAutoscaling": false, 114 | "sparklineValueTextMapText": [ 115 | "new highest" 116 | ], 117 | "tagMode": "all", 118 | "fixedLegendUseRawStats": false, 119 | "y0UnitAutoscaling": false, 120 | "fixedLegendDisplayStats": [ 121 | "CURRENT" 122 | ], 123 | "sparklineDisplayHorizontalPosition": "MIDDLE", 124 | "sparklineDecimalPrecision": 0, 125 | "autoColumnTags": false, 126 | "numTags": 4, 127 | "sparklineDisplayColor": null, 128 | "sparklineLineColor": null, 129 | "sparklineFillColor": null 130 | }, 131 | "includeObsoleteMetrics": false, 132 | "interpolatePoints": false, 133 | "name": "", 134 | "units": "", 135 | "summarization": "MEAN", 136 | "base": 0, 137 | "chartAttributes": { 138 | "singleStat": { 139 | "sparklineDisplayLabel": "A", 140 | "sparklineDisplaySubLabel": "RUNNING SERVICES" 141 | }, 142 | "dashboardLayout": { 143 | "x": 0, 144 | "y": 0, 145 | "h": 8, 146 | "w": 4 147 | } 148 | }, 149 | "sources": [ 150 | { 151 | "name": "A", 152 | "query": "count(mavg(5m, ts(aws.ecs.cpuutilization, ClusterName=${ECSCluster})))", 153 | "disabled": false, 154 | "secondaryAxis": false, 155 | "querybuilderEnabled": false, 156 | "sourceDescription": "" 157 | } 158 | ], 159 | "noDefaultEvents": false 160 | }, 161 | { 162 | "chartSettings": { 163 | "y1ScaleSIBy1024": false, 164 | "fixedLegendEnabled": false, 165 | "fixedLegendFilterField": "CURRENT", 166 | "y0ScaleSIBy1024": false, 167 | "fixedLegendFilterLimit": 5, 168 | "fixedLegendPosition": "RIGHT", 169 | "customTags": [ 170 | "ServiceName" 171 | ], 172 | "showRawValues": false, 173 | "lineType": "linear", 174 | "showHosts": true, 175 | "groupBySource": false, 176 | "windowing": "full", 177 | "fixedLegendFilterSort": "TOP", 178 | "y1Units": "", 179 | "sortValuesDescending": true, 180 | "type": "table", 181 | "y1UnitAutoscaling": false, 182 | "tagMode": "custom", 183 | "fixedLegendUseRawStats": false, 184 | "y0UnitAutoscaling": false, 185 | "fixedLegendDisplayStats": [ 186 | "CURRENT" 187 | ], 188 | "autoColumnTags": false, 189 | "numTags": 4, 190 | "sparklineDisplayColor": null, 191 | "sparklineLineColor": null, 192 | "sparklineFillColor": null 193 | }, 194 | "includeObsoleteMetrics": false, 195 | "interpolatePoints": false, 196 | "name": "Average CPU Utilization (last 5 mins)", 197 | "units": "%", 198 | "summarization": "MEAN", 199 | "base": 0, 200 | "chartAttributes": { 201 | "dashboardLayout": { 202 | "x": 4, 203 | "y": 0, 204 | "h": 8, 205 | "w": 4 206 | } 207 | }, 208 | "sources": [ 209 | { 210 | "name": "A", 211 | "query": "mavg(5m, ts(aws.ecs.cpuutilization, ClusterName=${ECSCluster}))", 212 | "disabled": false, 213 | "secondaryAxis": false, 214 | "querybuilderEnabled": false, 215 | "sourceDescription": "" 216 | } 217 | ], 218 | "noDefaultEvents": false 219 | }, 220 | { 221 | "chartSettings": { 222 | "y1ScaleSIBy1024": false, 223 | "fixedLegendEnabled": false, 224 | "fixedLegendFilterField": "CURRENT", 225 | "y0ScaleSIBy1024": false, 226 | "fixedLegendFilterLimit": 5, 227 | "fixedLegendPosition": "RIGHT", 228 | "customTags": [ 229 | "ServiceName" 230 | ], 231 | "showRawValues": false, 232 | "lineType": "linear", 233 | "showHosts": true, 234 | "groupBySource": false, 235 | "windowing": "full", 236 | "fixedLegendFilterSort": "TOP", 237 | "y1Units": "", 238 | "sortValuesDescending": true, 239 | "type": "table", 240 | "y1UnitAutoscaling": false, 241 | "tagMode": "custom", 242 | "fixedLegendUseRawStats": false, 243 | "y0UnitAutoscaling": false, 244 | "fixedLegendDisplayStats": [ 245 | "CURRENT" 246 | ], 247 | "autoColumnTags": false, 248 | "numTags": 4, 249 | "sparklineDisplayColor": null, 250 | "sparklineLineColor": null, 251 | "sparklineFillColor": null 252 | }, 253 | "includeObsoleteMetrics": false, 254 | "interpolatePoints": false, 255 | "name": "Average Memory Utilization (last 5 mins)", 256 | "units": "%", 257 | "summarization": "MEAN", 258 | "base": 0, 259 | "chartAttributes": { 260 | "dashboardLayout": { 261 | "x": 8, 262 | "y": 0, 263 | "h": 8, 264 | "w": 4 265 | } 266 | }, 267 | "sources": [ 268 | { 269 | "name": "A", 270 | "query": "mavg(5m, ts(aws.ecs.memoryutilization, ClusterName=${ECSCluster}))", 271 | "disabled": false, 272 | "secondaryAxis": false, 273 | "querybuilderEnabled": false, 274 | "sourceDescription": "" 275 | } 276 | ], 277 | "noDefaultEvents": false 278 | } 279 | ], 280 | "heightFactor": 160 281 | } 282 | ], 283 | "name": "Services" 284 | } 285 | ], 286 | "parameterDetails": { 287 | "AWSAccountID": { 288 | "parameterType": "SIMPLE", 289 | "order": 1, 290 | "defaultValue": "Label", 291 | "hideFromView": false, 292 | "value": "", 293 | "label": "AWS Account ID", 294 | "valuesToReadableStrings": { 295 | "Label": "" 296 | } 297 | }, 298 | "ECSCluster": { 299 | "parameterType": "SIMPLE", 300 | "order": 2, 301 | "defaultValue": "Label", 302 | "hideFromView": false, 303 | "value": "", 304 | "label": "ECS Cluster", 305 | "valuesToReadableStrings": { 306 | "Label": "" 307 | } 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /aws-fargate/acme-fitness-shop.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: 'AWS CloudFormation Template for the ACME Fitness Shop: This template deploys the 3 | CloudJourney.io ACME Fitness Shop using AWS Fargate. 4 | 5 | **WARNING** You will be billed for the AWS resources used if you create a stack from this 6 | template.' 7 | 8 | # The below parameters allow you to control some of the input values of the template each time you 9 | # create or update the stack. 10 | # The ACME Fitness Shop is a collection of AWS resources that work together. All resources are 11 | # tagged with five tags to make it easier to identify them later. It is mandatory to provide values 12 | # for the UserTag (your username) and TeamTag (the team you work on). The parameters "VersionTag" 13 | # and "FeatureTag" shouldn't be changed. 14 | Parameters: 15 | TemplateVersionTag: 16 | Type: String 17 | Default: v0.1.0 18 | Description: "The version of the CloudFormation Template (shouldn't be changed)." 19 | UserTag: 20 | Type: String 21 | Description: "The name of the user deploying the ACME Fitness Shop." 22 | TeamTag: 23 | Type: String 24 | Description: "The team the user deploying the ACME Fitness Shop works on." 25 | FeatureTag: 26 | Type: String 27 | Description: "The name of the project that is deployed, ACME Fitness Shop (shouldn't be changed)." 28 | Default: acme-fitness-shop 29 | SourceSecurityGroup: 30 | Type: AWS::EC2::SecurityGroup::Id 31 | Description: 'A security group controls the inbound traffic that is allowed to reach the instances 32 | that are associated with the security group. The security group you specify here will 33 | have access to all AWS Fargate instances that are created.' 34 | Subnets: 35 | Type: List 36 | Description: 'You can launch AWS resources into a specified subnet. The subnet you specify here 37 | should be allowed to auto-assign public IPv4 address.' 38 | VPC: 39 | Type: AWS::EC2::VPC::Id 40 | Description: 'A VPC is logically isolated from other virtual networks in the AWS Cloud. This 41 | CloudFormation template will launch AWS Fargate instances into the VPC you specify here.' 42 | 43 | # The resources section declares the AWS resources that are created by the CloudFormation template 44 | Resources: 45 | # The AWS::Logs::LogGroup resource specifies a log group. A log group defines common properties for 46 | # log streams, such as their retention and access control rules. Each log stream must belong to one 47 | # log group. All AWS resources will log to the same group, making troubleshooting easier. 48 | ACMELogGroup: 49 | Type: AWS::Logs::LogGroup 50 | Properties: 51 | LogGroupName: !Sub ${FeatureTag} 52 | 53 | # The AWS::ECS::Cluster resource creates an Amazon Elastic Container Service (Amazon ECS) cluster. 54 | # This cluster will have all AWS Fargate instances. 55 | Cluster: 56 | Type: AWS::ECS::Cluster 57 | Properties: 58 | ClusterName: !Ref AWS::StackName 59 | Tags: 60 | - Key: version 61 | Value: !Ref TemplateVersionTag 62 | - Key: author 63 | Value: !Ref UserTag 64 | - Key: team 65 | Value: !Ref TeamTag 66 | - Key: feature 67 | Value: !Ref FeatureTag 68 | - Key: region 69 | Value: !Ref AWS::Region 70 | 71 | # Creates a private namespace based on DNS, which will be visible only inside a specified Amazon VPC. 72 | # The namespace defines our service naming scheme. In this particular case, all services will be part 73 | # of the fitness.acme domain 74 | ServiceDiscovery: 75 | Type: AWS::ServiceDiscovery::PrivateDnsNamespace 76 | Properties: 77 | Name: fitness.acme 78 | Vpc: !Ref VPC 79 | 80 | # Services in the ServiceDiscovery are complex types that contains information about a service. The 81 | # name of the service is used together with the ServiceDiscovery namespace, to form the complete 82 | # domain name. As an example, a service with the name 'mongo' will have the full domain name 83 | # mongo.fitness.acme. 84 | 85 | # user-db 86 | UserDbServiceDiscovery: 87 | Type: AWS::ServiceDiscovery::Service 88 | Properties: 89 | Name: user-db 90 | DnsConfig: 91 | DnsRecords: 92 | - Type: A 93 | TTL: "10" 94 | NamespaceId: !Ref ServiceDiscovery 95 | HealthCheckCustomConfig: 96 | FailureThreshold: 1 97 | 98 | # order-db 99 | OrderDbServiceDiscovery: 100 | Type: AWS::ServiceDiscovery::Service 101 | Properties: 102 | Name: order-db 103 | DnsConfig: 104 | DnsRecords: 105 | - Type: A 106 | TTL: "10" 107 | NamespaceId: !Ref ServiceDiscovery 108 | HealthCheckCustomConfig: 109 | FailureThreshold: 1 110 | 111 | # catalog-db 112 | CatalogDbServiceDiscovery: 113 | Type: AWS::ServiceDiscovery::Service 114 | Properties: 115 | Name: catalog-db 116 | DnsConfig: 117 | DnsRecords: 118 | - Type: A 119 | TTL: "10" 120 | NamespaceId: !Ref ServiceDiscovery 121 | HealthCheckCustomConfig: 122 | FailureThreshold: 1 123 | 124 | # front-end 125 | FrontEndServiceDiscovery: 126 | Type: AWS::ServiceDiscovery::Service 127 | Properties: 128 | Name: front-end 129 | DnsConfig: 130 | DnsRecords: 131 | - Type: A 132 | TTL: "10" 133 | NamespaceId: !Ref ServiceDiscovery 134 | HealthCheckCustomConfig: 135 | FailureThreshold: 1 136 | 137 | # user 138 | UserServiceDiscovery: 139 | Type: AWS::ServiceDiscovery::Service 140 | Properties: 141 | Name: user 142 | DnsConfig: 143 | DnsRecords: 144 | - Type: A 145 | TTL: "10" 146 | NamespaceId: !Ref ServiceDiscovery 147 | HealthCheckCustomConfig: 148 | FailureThreshold: 1 149 | 150 | # catalog 151 | CatalogServiceDiscovery: 152 | Type: AWS::ServiceDiscovery::Service 153 | Properties: 154 | Name: catalog 155 | DnsConfig: 156 | DnsRecords: 157 | - Type: A 158 | TTL: "10" 159 | NamespaceId: !Ref ServiceDiscovery 160 | HealthCheckCustomConfig: 161 | FailureThreshold: 1 162 | 163 | # redis-db 164 | RedisDbServiceDiscovery: 165 | Type: AWS::ServiceDiscovery::Service 166 | Properties: 167 | Name: redis-db 168 | DnsConfig: 169 | DnsRecords: 170 | - Type: A 171 | TTL: "10" 172 | NamespaceId: !Ref ServiceDiscovery 173 | HealthCheckCustomConfig: 174 | FailureThreshold: 1 175 | 176 | # cart 177 | CartServiceDiscovery: 178 | Type: AWS::ServiceDiscovery::Service 179 | Properties: 180 | Name: cart 181 | DnsConfig: 182 | DnsRecords: 183 | - Type: A 184 | TTL: "10" 185 | NamespaceId: !Ref ServiceDiscovery 186 | HealthCheckCustomConfig: 187 | FailureThreshold: 1 188 | 189 | # order 190 | OrderServiceDiscovery: 191 | Type: AWS::ServiceDiscovery::Service 192 | Properties: 193 | Name: order 194 | DnsConfig: 195 | DnsRecords: 196 | - Type: A 197 | TTL: "10" 198 | NamespaceId: !Ref ServiceDiscovery 199 | HealthCheckCustomConfig: 200 | FailureThreshold: 1 201 | 202 | # payment 203 | PaymentServiceDiscovery: 204 | Type: AWS::ServiceDiscovery::Service 205 | Properties: 206 | Name: payment 207 | DnsConfig: 208 | DnsRecords: 209 | - Type: A 210 | TTL: "10" 211 | NamespaceId: !Ref ServiceDiscovery 212 | HealthCheckCustomConfig: 213 | FailureThreshold: 1 214 | 215 | # The AWS::IAM::Role creates a new role for your AWS account. This role will be able to run 216 | # all containers and have access to the default policy called AmazonECSTaskExecutionRolePolicy 217 | TaskExecutionRole: 218 | Type: AWS::IAM::Role 219 | Properties: 220 | RoleName: !Sub 'AmazonECS${AWS::StackName}ExecutionRole' 221 | Description: !Sub 'Allow ${AWS::StackName} to manage Amazon ECS on your behalf' 222 | AssumeRolePolicyDocument: 223 | Version: 2012-10-17 224 | Statement: 225 | - Action: "sts:AssumeRole" 226 | Effect: Allow 227 | Principal: 228 | Service: ecs-tasks.amazonaws.com 229 | ManagedPolicyArns: 230 | - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy 231 | Tags: 232 | - Key: version 233 | Value: !Ref TemplateVersionTag 234 | - Key: author 235 | Value: !Ref UserTag 236 | - Key: team 237 | Value: !Ref TeamTag 238 | - Key: feature 239 | Value: !Ref FeatureTag 240 | - Key: region 241 | Value: !Ref AWS::Region 242 | 243 | # The AWS::ECS::TaskDefinition resource describes the container and volume definitions of an 244 | # Amazon Elastic Container Service (Amazon ECS) task. It specifies which Docker images to use, 245 | # the required resources, and other configurations related to launching the task definition 246 | # through an Amazon ECS service. Each service will have it's own task definition 247 | 248 | # user-db 249 | UserDbTaskDefinition: 250 | Type: AWS::ECS::TaskDefinition 251 | Properties: 252 | ContainerDefinitions: 253 | - Name: user-db 254 | Environment: 255 | - Name: MONGO_INITDB_ROOT_USERNAME 256 | Value: mongoadmin 257 | - Name: MONGO_INITDB_ROOT_PASSWORD 258 | Value: secret 259 | - Name: MONGO_INITDB_DATABASE 260 | Value: acmefit 261 | Essential: true 262 | #Image: gcr.io/vmwarecloudadvocacy/acmeshop-user-db:latest 263 | Image: mongo:4 264 | LogConfiguration: 265 | LogDriver: awslogs 266 | Options: 267 | awslogs-region: !Ref AWS::Region 268 | awslogs-group: !Ref ACMELogGroup 269 | awslogs-stream-prefix: user-db 270 | Cpu: "256" 271 | Family: !Sub ${AWS::StackName}-user-db 272 | Memory: "512" 273 | NetworkMode: awsvpc 274 | RequiresCompatibilities: 275 | - FARGATE 276 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 277 | Tags: 278 | - Key: version 279 | Value: !Ref TemplateVersionTag 280 | - Key: author 281 | Value: !Ref UserTag 282 | - Key: team 283 | Value: !Ref TeamTag 284 | - Key: feature 285 | Value: !Ref FeatureTag 286 | - Key: region 287 | Value: !Ref AWS::Region 288 | 289 | # order-db 290 | OrderDbTaskDefinition: 291 | Type: AWS::ECS::TaskDefinition 292 | Properties: 293 | ContainerDefinitions: 294 | - Name: order-db 295 | Environment: 296 | - Name: MONGO_INITDB_ROOT_USERNAME 297 | Value: mongoadmin 298 | - Name: MONGO_INITDB_ROOT_PASSWORD 299 | Value: secret 300 | Essential: true 301 | Image: mongo:4 302 | LogConfiguration: 303 | LogDriver: awslogs 304 | Options: 305 | awslogs-region: !Ref AWS::Region 306 | awslogs-group: !Ref ACMELogGroup 307 | awslogs-stream-prefix: order-db 308 | Cpu: "256" 309 | Family: !Sub ${AWS::StackName}-order-db 310 | Memory: "512" 311 | NetworkMode: awsvpc 312 | RequiresCompatibilities: 313 | - FARGATE 314 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 315 | Tags: 316 | - Key: version 317 | Value: !Ref TemplateVersionTag 318 | - Key: author 319 | Value: !Ref UserTag 320 | - Key: team 321 | Value: !Ref TeamTag 322 | - Key: feature 323 | Value: !Ref FeatureTag 324 | - Key: region 325 | Value: !Ref AWS::Region 326 | 327 | # catalog-db 328 | CatalogDbTaskDefinition: 329 | Type: AWS::ECS::TaskDefinition 330 | Properties: 331 | ContainerDefinitions: 332 | - Name: catalog-db 333 | Environment: 334 | - Name: MONGO_INITDB_ROOT_USERNAME 335 | Value: mongoadmin 336 | - Name: MONGO_INITDB_ROOT_PASSWORD 337 | Value: secret 338 | - Name: MONGO_INITDB_DATABASE 339 | Value: acmefit 340 | Essential: true 341 | #Image: gcr.io/vmwarecloudadvocacy/acmeshop-catalog-db:latest 342 | Image: mongo:4 343 | LogConfiguration: 344 | LogDriver: awslogs 345 | Options: 346 | awslogs-region: !Ref AWS::Region 347 | awslogs-group: !Ref ACMELogGroup 348 | awslogs-stream-prefix: catalog-db 349 | Cpu: "256" 350 | Family: !Sub ${AWS::StackName}-catalog-db 351 | Memory: "512" 352 | NetworkMode: awsvpc 353 | RequiresCompatibilities: 354 | - FARGATE 355 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 356 | Tags: 357 | - Key: version 358 | Value: !Ref TemplateVersionTag 359 | - Key: author 360 | Value: !Ref UserTag 361 | - Key: team 362 | Value: !Ref TeamTag 363 | - Key: feature 364 | Value: !Ref FeatureTag 365 | - Key: region 366 | Value: !Ref AWS::Region 367 | 368 | # front-end 369 | FrontEndTaskDefinition: 370 | Type: AWS::ECS::TaskDefinition 371 | Properties: 372 | ContainerDefinitions: 373 | - Name: front-end 374 | Environment: 375 | - Name: PORT 376 | Value: 3000 377 | - Name: USERS_HOST 378 | Value: user.fitness.acme 379 | - Name: CATALOG_HOST 380 | Value: catalog.fitness.acme 381 | - Name: CART_HOST 382 | Value: cart.fitness.acme 383 | - Name: ORDER_HOST 384 | Value: order.fitness.acme 385 | - Name: USERS_PORT 386 | Value: 8081 387 | - Name: CATALOG_PORT 388 | Value: 8082 389 | - Name: CART_PORT 390 | Value: 5000 391 | - Name: ORDER_PORT 392 | Value: 6000 393 | Essential: true 394 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-front-end:latest 395 | LogConfiguration: 396 | LogDriver: awslogs 397 | Options: 398 | awslogs-region: !Ref AWS::Region 399 | awslogs-group: !Ref ACMELogGroup 400 | awslogs-stream-prefix: front-end 401 | Cpu: "256" 402 | Family: !Sub ${AWS::StackName}-front-end 403 | Memory: "512" 404 | NetworkMode: awsvpc 405 | RequiresCompatibilities: 406 | - FARGATE 407 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 408 | Tags: 409 | - Key: version 410 | Value: !Ref TemplateVersionTag 411 | - Key: author 412 | Value: !Ref UserTag 413 | - Key: team 414 | Value: !Ref TeamTag 415 | - Key: feature 416 | Value: !Ref FeatureTag 417 | - Key: region 418 | Value: !Ref AWS::Region 419 | 420 | # user 421 | UserTaskDefinition: 422 | Type: AWS::ECS::TaskDefinition 423 | Properties: 424 | ContainerDefinitions: 425 | - Name: user 426 | Environment: 427 | - Name: USERS_DB_USERNAME 428 | Value: mongoadmin 429 | - Name: USERS_DB_PASSWORD 430 | Value: secret 431 | - Name: USERS_DB_HOST 432 | Value: user-db.fitness.acme 433 | - Name: USERS_DB_PORT 434 | Value: 27017 435 | - Name: USERS_PORT 436 | Value: 8081 437 | Essential: true 438 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-user:latest 439 | LogConfiguration: 440 | LogDriver: awslogs 441 | Options: 442 | awslogs-region: !Ref AWS::Region 443 | awslogs-group: !Ref ACMELogGroup 444 | awslogs-stream-prefix: user 445 | Cpu: "256" 446 | Family: !Sub ${AWS::StackName}-user 447 | Memory: "512" 448 | NetworkMode: awsvpc 449 | RequiresCompatibilities: 450 | - FARGATE 451 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 452 | Tags: 453 | - Key: version 454 | Value: !Ref TemplateVersionTag 455 | - Key: author 456 | Value: !Ref UserTag 457 | - Key: team 458 | Value: !Ref TeamTag 459 | - Key: feature 460 | Value: !Ref FeatureTag 461 | - Key: region 462 | Value: !Ref AWS::Region 463 | 464 | # catalog 465 | CatalogTaskDefinition: 466 | Type: AWS::ECS::TaskDefinition 467 | Properties: 468 | ContainerDefinitions: 469 | - Name: catalog 470 | Environment: 471 | - Name: CATALOG_DB_USERNAME 472 | Value: mongoadmin 473 | - Name: CATALOG_DB_PASSWORD 474 | Value: secret 475 | - Name: CATALOG_DB_HOST 476 | Value: catalog-db.fitness.acme 477 | - Name: CATALOG_DB_PORT 478 | Value: 27017 479 | - Name: CATALOG_PORT 480 | Value: 8082 481 | Essential: true 482 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-catalog:latest 483 | LogConfiguration: 484 | LogDriver: awslogs 485 | Options: 486 | awslogs-region: !Ref AWS::Region 487 | awslogs-group: !Ref ACMELogGroup 488 | awslogs-stream-prefix: catalog 489 | Cpu: "256" 490 | Family: !Sub ${AWS::StackName}-catalog 491 | Memory: "512" 492 | NetworkMode: awsvpc 493 | RequiresCompatibilities: 494 | - FARGATE 495 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 496 | Tags: 497 | - Key: version 498 | Value: !Ref TemplateVersionTag 499 | - Key: author 500 | Value: !Ref UserTag 501 | - Key: team 502 | Value: !Ref TeamTag 503 | - Key: feature 504 | Value: !Ref FeatureTag 505 | - Key: region 506 | Value: !Ref AWS::Region 507 | 508 | # redis-db 509 | RedisDbTaskDefinition: 510 | Type: AWS::ECS::TaskDefinition 511 | Properties: 512 | ContainerDefinitions: 513 | - Name: redis-db 514 | Essential: true 515 | Image: redis:5.0.3-alpine 516 | LogConfiguration: 517 | LogDriver: awslogs 518 | Options: 519 | awslogs-region: !Ref AWS::Region 520 | awslogs-group: !Ref ACMELogGroup 521 | awslogs-stream-prefix: redis-db 522 | Cpu: "256" 523 | Family: !Sub ${AWS::StackName}-redis-db 524 | Memory: "512" 525 | NetworkMode: awsvpc 526 | RequiresCompatibilities: 527 | - FARGATE 528 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 529 | Tags: 530 | - Key: version 531 | Value: !Ref TemplateVersionTag 532 | - Key: author 533 | Value: !Ref UserTag 534 | - Key: team 535 | Value: !Ref TeamTag 536 | - Key: feature 537 | Value: !Ref FeatureTag 538 | - Key: region 539 | Value: !Ref AWS::Region 540 | 541 | # cart 542 | CartTaskDefinition: 543 | Type: AWS::ECS::TaskDefinition 544 | Properties: 545 | ContainerDefinitions: 546 | - Name: cart 547 | Environment: 548 | - Name: REDIS_HOST 549 | Value: redis-db.fitness.acme 550 | - Name: REDIS_PORT 551 | Value: 6379 552 | - Name: CART_PORT 553 | Value: 5000 554 | Essential: true 555 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-cart 556 | LogConfiguration: 557 | LogDriver: awslogs 558 | Options: 559 | awslogs-region: !Ref AWS::Region 560 | awslogs-group: !Ref ACMELogGroup 561 | awslogs-stream-prefix: cart 562 | Cpu: "256" 563 | Family: !Sub ${AWS::StackName}-cart 564 | Memory: "512" 565 | NetworkMode: awsvpc 566 | RequiresCompatibilities: 567 | - FARGATE 568 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 569 | Tags: 570 | - Key: version 571 | Value: !Ref TemplateVersionTag 572 | - Key: author 573 | Value: !Ref UserTag 574 | - Key: team 575 | Value: !Ref TeamTag 576 | - Key: feature 577 | Value: !Ref FeatureTag 578 | - Key: region 579 | Value: !Ref AWS::Region 580 | 581 | # order 582 | OrderTaskDefinition: 583 | Type: AWS::ECS::TaskDefinition 584 | Properties: 585 | ContainerDefinitions: 586 | - Name: order 587 | Environment: 588 | - Name: PAYMENT_HOST 589 | Value: payment.fitness.acme 590 | - Name: ORDER_PORT 591 | Value: 6000 592 | - Name: ORDER_DB_HOST 593 | Value: order-db.fitness.acme 594 | - Name: ORDER_DB_PASSWORD 595 | Value: secret 596 | - Name: ORDER_DB_USERNAME 597 | Value: mongoadmin 598 | Essential: true 599 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-order:latest 600 | LogConfiguration: 601 | LogDriver: awslogs 602 | Options: 603 | awslogs-region: !Ref AWS::Region 604 | awslogs-group: !Ref ACMELogGroup 605 | awslogs-stream-prefix: order 606 | Cpu: "256" 607 | Family: !Sub ${AWS::StackName}-order 608 | Memory: "512" 609 | NetworkMode: awsvpc 610 | RequiresCompatibilities: 611 | - FARGATE 612 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 613 | Tags: 614 | - Key: version 615 | Value: !Ref TemplateVersionTag 616 | - Key: author 617 | Value: !Ref UserTag 618 | - Key: team 619 | Value: !Ref TeamTag 620 | - Key: feature 621 | Value: !Ref FeatureTag 622 | - Key: region 623 | Value: !Ref AWS::Region 624 | 625 | # payment 626 | PaymentTaskDefinition: 627 | Type: AWS::ECS::TaskDefinition 628 | Properties: 629 | ContainerDefinitions: 630 | - Name: payment 631 | Essential: true 632 | Image: gcr.io/vmwarecloudadvocacy/acmeshop-payment:latest 633 | LogConfiguration: 634 | LogDriver: awslogs 635 | Options: 636 | awslogs-region: !Ref AWS::Region 637 | awslogs-group: !Ref ACMELogGroup 638 | awslogs-stream-prefix: payment 639 | Cpu: "256" 640 | Family: !Sub ${AWS::StackName}-payment 641 | Memory: "512" 642 | NetworkMode: awsvpc 643 | RequiresCompatibilities: 644 | - FARGATE 645 | ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn 646 | Tags: 647 | - Key: version 648 | Value: !Ref TemplateVersionTag 649 | - Key: author 650 | Value: !Ref UserTag 651 | - Key: team 652 | Value: !Ref TeamTag 653 | - Key: feature 654 | Value: !Ref FeatureTag 655 | - Key: region 656 | Value: !Ref AWS::Region 657 | 658 | # A security group controls the inbound traffic that is allowed to reach the instances 659 | # that are associated with the security group. Each service will have their own security 660 | # group. The SourceSecurityGroup will have access to each group, along with any dependent 661 | # services. 662 | 663 | # user-db 664 | UserDbServiceSecurityGroup: 665 | Type: AWS::EC2::SecurityGroup 666 | Properties: 667 | GroupDescription: !Sub 'Security group for user-db in ${AWS::StackName}' 668 | SecurityGroupIngress: 669 | - ToPort: 27017 670 | FromPort: 27017 671 | IpProtocol: tcp 672 | SourceSecurityGroupId: !Ref SourceSecurityGroup 673 | - ToPort: 27017 674 | FromPort: 27017 675 | IpProtocol: tcp 676 | SourceSecurityGroupId: !Ref UserServiceSecurityGroup 677 | VpcId: !Ref VPC 678 | 679 | # order-db 680 | OrderDbServiceSecurityGroup: 681 | Type: AWS::EC2::SecurityGroup 682 | Properties: 683 | GroupDescription: !Sub 'Security group for order-db in ${AWS::StackName}' 684 | SecurityGroupIngress: 685 | - ToPort: 27017 686 | FromPort: 27017 687 | IpProtocol: tcp 688 | SourceSecurityGroupId: !Ref SourceSecurityGroup 689 | - ToPort: 27017 690 | FromPort: 27017 691 | IpProtocol: tcp 692 | SourceSecurityGroupId: !Ref OrderServiceSecurityGroup 693 | VpcId: !Ref VPC 694 | 695 | # catalog-db 696 | CatalogDbServiceSecurityGroup: 697 | Type: AWS::EC2::SecurityGroup 698 | Properties: 699 | GroupDescription: !Sub 'Security group for catalog-db in ${AWS::StackName}' 700 | SecurityGroupIngress: 701 | - ToPort: 27017 702 | FromPort: 27017 703 | IpProtocol: tcp 704 | SourceSecurityGroupId: !Ref SourceSecurityGroup 705 | - ToPort: 27017 706 | FromPort: 27017 707 | IpProtocol: tcp 708 | SourceSecurityGroupId: !Ref CatalogServiceSecurityGroup 709 | VpcId: !Ref VPC 710 | 711 | # front-end 712 | FrontEndServiceSecurityGroup: 713 | Type: AWS::EC2::SecurityGroup 714 | Properties: 715 | GroupDescription: !Sub 'Security group for front-end in ${AWS::StackName}' 716 | SecurityGroupIngress: 717 | - ToPort: 3000 718 | FromPort: 3000 719 | IpProtocol: tcp 720 | SourceSecurityGroupId: !Ref SourceSecurityGroup 721 | VpcId: !Ref VPC 722 | 723 | # user 724 | UserServiceSecurityGroup: 725 | Type: AWS::EC2::SecurityGroup 726 | Properties: 727 | GroupDescription: !Sub 'Security group for user in ${AWS::StackName}' 728 | SecurityGroupIngress: 729 | - ToPort: 8081 730 | FromPort: 8081 731 | IpProtocol: tcp 732 | SourceSecurityGroupId: !Ref FrontEndServiceSecurityGroup 733 | - ToPort: 8081 734 | FromPort: 8081 735 | IpProtocol: tcp 736 | SourceSecurityGroupId: !Ref SourceSecurityGroup 737 | VpcId: !Ref VPC 738 | 739 | # catalog 740 | CatalogServiceSecurityGroup: 741 | Type: AWS::EC2::SecurityGroup 742 | Properties: 743 | GroupDescription: !Sub 'Security group for catalog in ${AWS::StackName}' 744 | SecurityGroupIngress: 745 | - ToPort: 8082 746 | FromPort: 8082 747 | IpProtocol: tcp 748 | SourceSecurityGroupId: !Ref FrontEndServiceSecurityGroup 749 | - ToPort: 8082 750 | FromPort: 8082 751 | IpProtocol: tcp 752 | SourceSecurityGroupId: !Ref SourceSecurityGroup 753 | VpcId: !Ref VPC 754 | 755 | # redis-db 756 | RedisDbServiceSecurityGroup: 757 | Type: AWS::EC2::SecurityGroup 758 | Properties: 759 | GroupDescription: !Sub 'Security group for redis-db in ${AWS::StackName}' 760 | SecurityGroupIngress: 761 | - ToPort: 6379 762 | FromPort: 6379 763 | IpProtocol: tcp 764 | SourceSecurityGroupId: !Ref CartServiceSecurityGroup 765 | - ToPort: 6379 766 | FromPort: 6379 767 | IpProtocol: tcp 768 | SourceSecurityGroupId: !Ref SourceSecurityGroup 769 | VpcId: !Ref VPC 770 | 771 | # cart 772 | CartServiceSecurityGroup: 773 | Type: AWS::EC2::SecurityGroup 774 | Properties: 775 | GroupDescription: !Sub 'Security group for cart in ${AWS::StackName}' 776 | SecurityGroupIngress: 777 | - ToPort: 5000 778 | FromPort: 5000 779 | IpProtocol: tcp 780 | SourceSecurityGroupId: !Ref FrontEndServiceSecurityGroup 781 | - ToPort: 5000 782 | FromPort: 5000 783 | IpProtocol: tcp 784 | SourceSecurityGroupId: !Ref SourceSecurityGroup 785 | VpcId: !Ref VPC 786 | 787 | # order 788 | OrderServiceSecurityGroup: 789 | Type: AWS::EC2::SecurityGroup 790 | Properties: 791 | GroupDescription: !Sub 'Security group for Order in ${AWS::StackName}' 792 | SecurityGroupIngress: 793 | - ToPort: 6000 794 | FromPort: 6000 795 | IpProtocol: tcp 796 | SourceSecurityGroupId: !Ref FrontEndServiceSecurityGroup 797 | - ToPort: 6000 798 | FromPort: 6000 799 | IpProtocol: tcp 800 | SourceSecurityGroupId: !Ref SourceSecurityGroup 801 | VpcId: !Ref VPC 802 | 803 | # payment 804 | PaymentServiceSecurityGroup: 805 | Type: AWS::EC2::SecurityGroup 806 | Properties: 807 | GroupDescription: !Sub 'Security group for payment in ${AWS::StackName}' 808 | SecurityGroupIngress: 809 | - ToPort: 9000 810 | FromPort: 9000 811 | IpProtocol: tcp 812 | SourceSecurityGroupId: !Ref OrderServiceSecurityGroup 813 | - ToPort: 9000 814 | FromPort: 9000 815 | IpProtocol: tcp 816 | SourceSecurityGroupId: !Ref SourceSecurityGroup 817 | VpcId: !Ref VPC 818 | 819 | # The AWS::ECS::Service resource creates an Amazon Elastic Container Service (Amazon ECS) service 820 | # that runs and maintains the requested number of tasks and associated load balancers. Each service 821 | # will be started with one instance and the appropriate security groups 822 | 823 | # payment 824 | # From an EC2 instance on the same subnet you can test this service using 825 | # curl -s http://payment.fitness.acme:9000/live 826 | PaymentService: 827 | Type: AWS::ECS::Service 828 | Properties: 829 | Cluster: !Ref Cluster 830 | LaunchType: FARGATE 831 | DesiredCount: 1 832 | ServiceName: !Sub '${AWS::StackName}-payment-svc' 833 | ServiceRegistries: 834 | - RegistryArn: !GetAtt PaymentServiceDiscovery.Arn 835 | TaskDefinition: !Ref PaymentTaskDefinition 836 | NetworkConfiguration: 837 | AwsvpcConfiguration: 838 | AssignPublicIp: ENABLED 839 | Subnets: !Ref Subnets 840 | SecurityGroups: 841 | - !GetAtt PaymentServiceSecurityGroup.GroupId 842 | 843 | # order-db 844 | # From an EC2 instance on the same subnet you can test this service using 845 | # nc -vz order-db.fitness.acme 27017 846 | OrderDbService: 847 | Type: AWS::ECS::Service 848 | Properties: 849 | Cluster: !Ref Cluster 850 | LaunchType: FARGATE 851 | DesiredCount: 1 852 | ServiceName: !Sub '${AWS::StackName}-order-db-svc' 853 | ServiceRegistries: 854 | - RegistryArn: !GetAtt OrderDbServiceDiscovery.Arn 855 | TaskDefinition: !Ref OrderDbTaskDefinition 856 | NetworkConfiguration: 857 | AwsvpcConfiguration: 858 | AssignPublicIp: ENABLED 859 | Subnets: !Ref Subnets 860 | SecurityGroups: 861 | - !GetAtt OrderDbServiceSecurityGroup.GroupId 862 | 863 | # catalog-db 864 | # From an EC2 instance on the same subnet you can test this service using 865 | # nc -vz catalog-db.fitness.acme 27017 866 | CatalogDbService: 867 | Type: AWS::ECS::Service 868 | Properties: 869 | Cluster: !Ref Cluster 870 | LaunchType: FARGATE 871 | DesiredCount: 1 872 | ServiceName: !Sub '${AWS::StackName}-catalog-db-svc' 873 | ServiceRegistries: 874 | - RegistryArn: !GetAtt CatalogDbServiceDiscovery.Arn 875 | TaskDefinition: !Ref CatalogDbTaskDefinition 876 | NetworkConfiguration: 877 | AwsvpcConfiguration: 878 | AssignPublicIp: ENABLED 879 | Subnets: !Ref Subnets 880 | SecurityGroups: 881 | - !GetAtt CatalogDbServiceSecurityGroup.GroupId 882 | 883 | # user-db 884 | # From an EC2 instance on the same subnet you can test this service using 885 | # nc -vz user-db.fitness.acme 27017 886 | UserDbService: 887 | Type: AWS::ECS::Service 888 | Properties: 889 | Cluster: !Ref Cluster 890 | LaunchType: FARGATE 891 | DesiredCount: 1 892 | ServiceName: !Sub '${AWS::StackName}-user-db-svc' 893 | ServiceRegistries: 894 | - RegistryArn: !GetAtt UserDbServiceDiscovery.Arn 895 | TaskDefinition: !Ref UserDbTaskDefinition 896 | NetworkConfiguration: 897 | AwsvpcConfiguration: 898 | AssignPublicIp: ENABLED 899 | Subnets: !Ref Subnets 900 | SecurityGroups: 901 | - !GetAtt UserDbServiceSecurityGroup.GroupId 902 | 903 | # redis-db 904 | # From an EC2 instance on the same subnet you can test this service using 905 | # nc -vz redis-db.fitness.acme 6379 906 | RedisDbService: 907 | Type: AWS::ECS::Service 908 | Properties: 909 | Cluster: !Ref Cluster 910 | LaunchType: FARGATE 911 | DesiredCount: 1 912 | ServiceName: !Sub '${AWS::StackName}-redis-db-svc' 913 | ServiceRegistries: 914 | - RegistryArn: !GetAtt RedisDbServiceDiscovery.Arn 915 | TaskDefinition: !Ref RedisDbTaskDefinition 916 | NetworkConfiguration: 917 | AwsvpcConfiguration: 918 | AssignPublicIp: ENABLED 919 | Subnets: !Ref Subnets 920 | SecurityGroups: 921 | - !GetAtt RedisDbServiceSecurityGroup.GroupId 922 | 923 | # order 924 | # From an EC2 instance on the same subnet you can test this service using 925 | # curl -s http://order.fitness.acme:6000/order/8888 926 | OrderService: 927 | Type: AWS::ECS::Service 928 | Properties: 929 | Cluster: !Ref Cluster 930 | LaunchType: FARGATE 931 | DesiredCount: 1 932 | ServiceName: !Sub '${AWS::StackName}-order-svc' 933 | ServiceRegistries: 934 | - RegistryArn: !GetAtt OrderServiceDiscovery.Arn 935 | TaskDefinition: !Ref OrderTaskDefinition 936 | NetworkConfiguration: 937 | AwsvpcConfiguration: 938 | AssignPublicIp: ENABLED 939 | Subnets: !Ref Subnets 940 | SecurityGroups: 941 | - !GetAtt OrderServiceSecurityGroup.GroupId 942 | 943 | # cart 944 | # From an EC2 instance on the same subnet you can test this service using 945 | # curl -s http://cart.fitness.acme:5000/cart/all 946 | CartService: 947 | Type: AWS::ECS::Service 948 | Properties: 949 | Cluster: !Ref Cluster 950 | LaunchType: FARGATE 951 | DesiredCount: 1 952 | ServiceName: !Sub '${AWS::StackName}-cart-svc' 953 | ServiceRegistries: 954 | - RegistryArn: !GetAtt CartServiceDiscovery.Arn 955 | TaskDefinition: !Ref CartTaskDefinition 956 | NetworkConfiguration: 957 | AwsvpcConfiguration: 958 | AssignPublicIp: ENABLED 959 | Subnets: !Ref Subnets 960 | SecurityGroups: 961 | - !GetAtt CartServiceSecurityGroup.GroupId 962 | 963 | # catalog 964 | # From an EC2 instance on the same subnet you can test this service using 965 | # curl -s http://catalog.fitness.acme:8082/products 966 | CatalogService: 967 | Type: AWS::ECS::Service 968 | Properties: 969 | Cluster: !Ref Cluster 970 | LaunchType: FARGATE 971 | DesiredCount: 1 972 | ServiceName: !Sub '${AWS::StackName}-catalog-svc' 973 | ServiceRegistries: 974 | - RegistryArn: !GetAtt CatalogServiceDiscovery.Arn 975 | TaskDefinition: !Ref CatalogTaskDefinition 976 | NetworkConfiguration: 977 | AwsvpcConfiguration: 978 | AssignPublicIp: ENABLED 979 | Subnets: !Ref Subnets 980 | SecurityGroups: 981 | - !GetAtt CatalogServiceSecurityGroup.GroupId 982 | 983 | # user 984 | # From an EC2 instance on the same subnet you can test this service using 985 | # curl -s http://user.fitness.acme:8081/users 986 | UserService: 987 | Type: AWS::ECS::Service 988 | Properties: 989 | Cluster: !Ref Cluster 990 | LaunchType: FARGATE 991 | DesiredCount: 1 992 | ServiceName: !Sub '${AWS::StackName}-user-svc' 993 | ServiceRegistries: 994 | - RegistryArn: !GetAtt UserServiceDiscovery.Arn 995 | TaskDefinition: !Ref UserTaskDefinition 996 | NetworkConfiguration: 997 | AwsvpcConfiguration: 998 | AssignPublicIp: ENABLED 999 | Subnets: !Ref Subnets 1000 | SecurityGroups: 1001 | - !GetAtt UserServiceSecurityGroup.GroupId 1002 | 1003 | # front-end 1004 | # From an EC2 instance on the same subnet you can test this service using 1005 | # curl -s http://front-end.fitness.acme:300 1006 | FrontEndService: 1007 | Type: AWS::ECS::Service 1008 | Properties: 1009 | Cluster: !Ref Cluster 1010 | LaunchType: FARGATE 1011 | DesiredCount: 1 1012 | ServiceName: !Sub '${AWS::StackName}-front-end-svc' 1013 | ServiceRegistries: 1014 | - RegistryArn: !GetAtt FrontEndServiceDiscovery.Arn 1015 | TaskDefinition: !Ref FrontEndTaskDefinition 1016 | NetworkConfiguration: 1017 | AwsvpcConfiguration: 1018 | AssignPublicIp: ENABLED 1019 | Subnets: !Ref Subnets 1020 | SecurityGroups: 1021 | - !GetAtt FrontEndServiceSecurityGroup.GroupId --------------------------------------------------------------------------------