Skip to content

Commit 83a33bc

Browse files
committed
Merge from Dev
2 parents 2b61a10 + afd3fe2 commit 83a33bc

60 files changed

Lines changed: 1423 additions & 240 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

KUBERNETES.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Kubernetes 101
2+
## Docker vs. Kubernetes
3+
Docker helps you package applications into images, and execute them in containers. Kubernetes is a robust platform for containerized applications. It abstracts away the underlying network infrastructure and hardware required to run them, simplifying their deployment, scaling, and management.
4+
5+
## Kubernetes from the container up
6+
### Pods
7+
The basic unit of a Kubernetes deployment is the **Pod**. A Pod encapsulates one or more containers. For example, the `basket` Pod specifies two containers:
8+
>`deployments.yaml`
9+
>
10+
>The first container runs the `eshop/basket.api` image:
11+
>```yaml
12+
>spec:
13+
> containers:
14+
> - name: basket
15+
> image: eshop/basket.api
16+
> env:
17+
> - name: ConnectionString
18+
> value: 127.0.0.1
19+
>```
20+
>Note the `ConnectionString` environment variable: containers within a Pod are networked via `localhost`. The second container runs the `redis` image:
21+
>```yaml
22+
>- name: basket-data
23+
> image: redis:3.2-alpine
24+
> ports:
25+
> - containerPort: 6379
26+
>```
27+
Placing `basket` and `basket-data` in the same Pod is reasonable here because the former requires the latter, and owns all its data. If we wanted to scale the service, however, it would be better to place the containers in separate Pods because the basket API and redis scale at different rates.
28+
29+
If the containers were in separate Pods, they would no longer be able to communicate via `localhost`; a **Service** would be required.
30+
31+
### Services
32+
Services expose Pods to external networks. For example, the `basket` Service exposes Pods with labels `app=eshop` and `component=basket` to the cluster at large:
33+
>`services.yaml`
34+
>```yaml
35+
>kind: Service
36+
>metadata:
37+
> ...
38+
> name: basket
39+
>spec:
40+
> ports:
41+
> - port: 80
42+
> selector:
43+
> app: eshop
44+
> component: basket
45+
>```
46+
Kubernetes's built-in DNS service resolves Service names to cluster-internal IP addresses. This allows the nginx frontend to proxy connections to the app's microservices by name:
47+
>`nginx.conf`
48+
>```
49+
>location /basket-api {
50+
> proxy_pass http://basket;
51+
>```
52+
The frontend Pod is different in that it needs to be exposed outside the cluster. This is accomplished with another Service:
53+
>`frontend.yaml`
54+
>```yaml
55+
>spec:
56+
> ports:
57+
> - port: 80
58+
> targetPort: 8080
59+
> selector:
60+
> app: eshop
61+
> component: frontend
62+
> type: LoadBalancer
63+
>```
64+
`type: LoadBalancer` tells Kubernetes to expose the Service behind a load balancer appropriate for the cluster's platform. For Azure Container Service, this creates an Azure load balancer rule with a public IP.
65+
66+
### Deployments
67+
Kubernetes uses Pods to organize containers, and Services to network them. It uses **Deployments** to organize creating, and modifying, Pods. A Deployment describes a state of one or more Pods. When a Deployment is created or modified, Kubernetes attempts to realize that state.
68+
69+
The Deployments in this project are basic. Still, `deploy.ps1` shows some more advanced Deployment capabilities. For example, Deployments can be paused. Each Deployment of this app is paused at creation:
70+
>`deployments.yaml`
71+
>```yaml
72+
>kind: Deployment
73+
>spec:
74+
> paused: true
75+
>```
76+
This allows the deployment script to change images before Kubernetes creates the Pods:
77+
>`deploy.ps1`
78+
>```powershell
79+
>kubectl set image -f deployments.yaml basket=$registry/basket.api ...
80+
>kubectl rollout resume -f deployments.yaml
81+
>```
82+
83+
### ConfigMaps
84+
A **ConfigMap** is a collection of key/value pairs commonly used to provide configuration information to Pods. The deployment script uses one to store the frontend's configuration:
85+
>`deploy.ps1`
86+
>```
87+
>kubectl create configmap config-files from-file=nginx-conf=nginx.conf
88+
>```
89+
This creates a ConfigMap named `config-files` with key `nginx-conf` whose value is the content of nginx.conf. The frontend Pod mounts that value as `/etc/nginx/nginx.conf`:
90+
>`frontend.yaml`
91+
>```yaml
92+
>spec:
93+
> containers:
94+
> - name: nginx
95+
> ...
96+
> volumeMounts:
97+
> - name: config
98+
> mountPath: /etc/nginx
99+
> volumes:
100+
> - name: config
101+
> configMap:
102+
> name: config-files
103+
> items:
104+
> - key: nginx-conf
105+
> path: nginx.conf
106+
>```
107+
This facilitates rapid iteration better than other techniques, e.g. building an image to bake in configuration.
108+
109+
The script also stores public URLs for the app's components in a ConfigMap:
110+
>`deploy.ps1`
111+
>```powershell
112+
>kubectl create configmap urls --from-literal=BasketUrl=http://$($frontendUrl)/basket-api ...
113+
>```
114+
>Here's how the `webspa` Deployment uses it:
115+
>
116+
>`deployments.yaml`
117+
>```yaml
118+
>spec:
119+
> containers:
120+
> - name: webspa
121+
> ...
122+
> env:
123+
> ...
124+
> - name: BasketUrl
125+
> valueFrom:
126+
> configMapKeyRef:
127+
> name: urls
128+
> key: BasketUrl
129+
>```
130+
131+
### Further reading
132+
* [Kubernetes Concepts](https://kubernetes.io/docs/concepts/)
133+
* [kubectl for Docker Users](https://kubernetes.io/docs/user-guide/docker-cli-to-kubectl/)
134+
* [Kubernetes API reference](https://kubernetes.io/docs/api-reference/v1.5/)

README.k8s.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# eShopOnContainers on Kubernetes
2+
The k8s directory contains Kubernetes configuration for the eShopOnContainers app and a PowerShell script to deploy it to a cluster. Each eShopOnContainers microservice has a deployment configuration in `deployments.yaml`, and is exposed to the cluster by a service in `services.yaml`. The microservices are exposed externally on individual routes (`/basket-api`, `/webmvc`, etc.) by an nginx reverse proxy specified in `frontend.yaml` and `nginx.conf`.
3+
4+
## Prerequisites
5+
* A Kubernetes cluster. Follow Azure Container Service's [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough) to create one.
6+
* A private Docker registry. Follow Azure Container Registry's [guide](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) to create one.
7+
* Optionally, previous steps can be skipped if you run gen-k8s-env.ps1 script to automatically create the azure environment needed for kubernetes deployment. Azure cli 2.0 must be previously installed [installation guide](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). For example:
8+
>```
9+
>./gen-k8s-env -resourceGroupName k8sGroup -location westeurope -registryName k8sregistry -orchestratorName k8s-cluster -dnsName k8s-dns
10+
>```
11+
* A Docker development environment with `docker` and `docker-compose`.
12+
* Visit [docker.com](https://docker.com) to download the tools and set up the environment. Docker's [installation guide](https://docs.docker.com/engine/getstarted/step_one/#step-3-verify-your-installation) covers verifying your Docker installation.
13+
* The Kubernetes command line client, `kubectl`.
14+
* This can be installed with the `az` tool as described in the Azure Container Service [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough). `az` is also helpful for getting the credentials `kubectl` needs to access your cluster. For other installation options, and information about configuring `kubectl` yourself, see the [Kubernetes documentation](https://kubernetes.io/docs/tasks/kubectl/install/).
15+
16+
## Deploy the application with the deployment script
17+
1. Open a PowerShell command line at the `k8s` directory of your local eShopOnContainers repository.
18+
1. Ensure `docker`, `docker-compose`, and `kubectl` are on the path, and configured for your Docker machine and Kubernetes cluster.
19+
1. Run `deploy.ps1` with your registry information. The Docker username and password are provided by Azure Container Registry, and can be retrieved from the Azure portal. Optionally, ACR credentials can be obtained by running the following command:
20+
>```
21+
>az acr credential show -n eshopregistry
22+
>```
23+
24+
Once the user and password are retrieved, run the following script for deployment. For example:
25+
>```
26+
>./deploy.ps1 -registry myregistry.azurecr.io -dockerUser User -dockerPassword SecretPassword
27+
>```
28+
The script will build the code and corresponding Docker images, push the latter to your registry, and deploy the application to your cluster. You can watch the deployment unfold from the Kubernetes web interface: run `kubectl proxy` and open a browser to [http://localhost:8001/ui](http://localhost:8001/ui)

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ https://github.com/dotnet/eShopOnContainers/wiki/04.-Setting-eShopOnContainer-so
9292
The <b>Windows Containers scenario is currently being implemented/tested yet</b>. The application should be able to run on Windows Nano Containers based on different Docker base images, as well, as the .NET Core services have also been tested running on plain Windows (with no Docker).
9393
The app was also partially tested on "Docker for Mac" using a development MacOS machine with .NET Core and VS Code installed, which is still a scenario using Linux containers running on the VM setup in the Mac by the "Docker for Windows" setup. But further testing and feedback on Mac environments and Windows Containers, from the community, will be appreciated.
9494

95+
## Kubernetes
96+
The k8s directory contains Kubernetes configuration for the eShopOnContainers app and a PowerShell script to deploy it to a cluster. Each eShopOnContainers microservice has a deployment configuration in `deployments.yaml`, and is exposed to the cluster by a service in `services.yaml`. The microservices are exposed externally on individual routes (`/basket-api`, `/webmvc`, etc.) by an nginx reverse proxy specified in `frontend.yaml` and `nginx.conf`.
97+
98+
### Prerequisites
99+
* A Kubernetes cluster. Follow Azure Container Service's [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough) to create one.
100+
* A private Docker registry. Follow Azure Container Registry's [guide](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) to create one.
101+
* A Docker development environment with `docker` and `docker-compose`.
102+
* Visit [docker.com](https://docker.com) to download the tools and set up the environment. Docker's [installation guide](https://docs.docker.com/engine/getstarted/step_one/#step-3-verify-your-installation) covers verifying your Docker installation.
103+
* The Kubernetes command line client, `kubectl`.
104+
* This can be installed with the `az` tool as described in the Azure Container Service [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough). `az` is also helpful for getting the credentials `kubectl` needs to access your cluster. For other installation options, and information about configuring `kubectl` yourself, see the [Kubernetes documentation](https://kubernetes.io/docs/tasks/kubectl/install/).
105+
106+
### Deploy the application with the deployment script
107+
1. Open a PowerShell command line at the `k8s` directory of your local eShopOnContainers repository.
108+
1. Ensure `docker`, `docker-compose`, and `kubectl` are on the path, and configured for your Docker machine and Kubernetes cluster.
109+
1. Run `deploy.ps1` with your registry information. The Docker username and password are provided by Azure Container Registry, and can be retrieved from the Azure portal. For example:
110+
>```
111+
>./deploy.ps1 -registry myregistry.azurecr.io -dockerUser User -dockerPassword SecretPassword
112+
>```
113+
The script will build the code and corresponding Docker images, push the latter to your registry, and deploy the application to your cluster. You can watch the deployment unfold from the Kubernetes web interface: run `kubectl proxy` and open a browser to [http://localhost:8001/ui](http://localhost:8001/ui)
114+
95115
## Sending feedback and pull requests
96116
As mentioned, we'd appreciate to your feedback, improvements and ideas.
97117
You can create new issues at the issues section, do pull requests and/or send emails to **eshop_feedback@service.microsoft.com**

cli-mac/build-bits.sh

100644100755
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ projectList=(
1010
"../src/Web/WebStatus"
1111
)
1212

13+
14+
pushd $(pwd)/../src/Web/WebSPA
15+
npm install
16+
npm rebuild node-sass
17+
popd
18+
1319
for project in "${projectList[@]}"
1420
do
1521
echo -e "\e[33mWorking on $(pwd)/$project"

cli-windows/build-bits-simple.ps1

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ if ([string]::IsNullOrEmpty($rootPath)) {
1111
}
1212
Write-Host "Root path used is $rootPath" -ForegroundColor Yellow
1313

14-
15-
$SolutionFilePath = $rootPath + "eShopOnContainers-ServicesAndWebApps.sln"
14+
$SolutionFilePath = [IO.Path]::Combine($rootPath, "eShopOnContainers-ServicesAndWebApps.sln")
1615

1716
dotnet restore $SolutionFilePath
1817

docker-compose.ci.build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ services:
66
volumes:
77
- .:/src
88
working_dir: /src
9-
command: /bin/bash -c "dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
10-
9+
command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
10+

docker-compose.override.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ services:
5656
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
5757
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
5858
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
59-
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
59+
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
60+
- CatalogUrlHC=http://catalog.api:5101/hc
61+
- OrderingUrlHC=http://ordering.api:5102/hc
62+
- IdentityUrlHC=http://identity.api:5105/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
63+
- BasketUrlHC=http://basket.api:5103/hc
6064
ports:
6165
- "5104:5104"
6266

docker-compose.prod.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ services:
6161
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
6262
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
6363
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
64-
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
64+
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
65+
- CatalogUrlHC=http://catalog.api:5101/hc
66+
- OrderingUrlHC=http://ordering.api:5102/hc
67+
- IdentityUrlHC=http://identity.api:5105/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
68+
- BasketUrlHC=http://basket.api:5103/hc
6569
ports:
6670
- "5104:5104"
6771

eShopOnContainers-ServicesAndWebApps.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26403.7
4+
VisualStudioVersion = 15.0.26403.3
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
77
EndProject

k8s/deploy.ps1

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
Param(
2+
[parameter(Mandatory=$true)][string]$registry,
3+
[parameter(Mandatory=$true)][string]$dockerUser,
4+
[parameter(Mandatory=$true)][string]$dockerPassword
5+
)
6+
7+
$requiredCommands = ("docker", "docker-compose", "kubectl")
8+
foreach ($command in $requiredCommands) {
9+
if ((Get-Command $command -ErrorAction SilentlyContinue) -eq $null) {
10+
Write-Host "$command must be on path" -ForegroundColor Red
11+
exit
12+
}
13+
}
14+
15+
Write-Host "Logging in to $registry" -ForegroundColor Yellow
16+
docker login -u $dockerUser -p $dockerPassword $registry
17+
if (-not $LastExitCode -eq 0) {
18+
Write-Host "Login failed" -ForegroundColor Red
19+
exit
20+
}
21+
22+
# create registry key secret
23+
kubectl create secret docker-registry registry-key `
24+
--docker-server=$registry `
25+
--docker-username=$dockerUser `
26+
--docker-password=$dockerPassword `
27+
--docker-email=not@used.com
28+
29+
# start sql, rabbitmq, frontend deployments
30+
kubectl create configmap config-files --from-file=nginx-conf=nginx.conf
31+
kubectl label configmap config-files app=eshop
32+
kubectl create -f sql-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml
33+
34+
Write-Host "Building and publishing eShopOnContainers..." -ForegroundColor Yellow
35+
dotnet restore ../eShopOnContainers-ServicesAndWebApps.sln
36+
dotnet publish -c Release -o obj/Docker/publish ../eShopOnContainers-ServicesAndWebApps.sln
37+
38+
Write-Host "Building Docker images..." -ForegroundColor Yellow
39+
docker-compose -p .. -f ../docker-compose.yml build
40+
41+
Write-Host "Pushing images to $registry..." -ForegroundColor Yellow
42+
$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "webmvc", "webspa")
43+
foreach ($service in $services) {
44+
docker tag eshop/$service $registry/eshop/$service
45+
docker push $registry/eshop/$service
46+
}
47+
48+
Write-Host "Waiting for frontend's external ip..." -ForegroundColor Yellow
49+
while ($true) {
50+
$frontendUrl = kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"
51+
if ([bool]($frontendUrl -as [ipaddress])) {
52+
break
53+
}
54+
Start-Sleep -s 15
55+
}
56+
57+
kubectl create configmap urls `
58+
--from-literal=BasketUrl=http://$($frontendUrl)/basket-api `
59+
--from-literal=CatalogUrl=http://$($frontendUrl)/catalog-api `
60+
--from-literal=IdentityUrl=http://$($frontendUrl)/identity `
61+
--from-literal=OrderingUrl=http://$($frontendUrl)/ordering-api `
62+
--from-literal=MvcClient=http://$($frontendUrl)/webmvc `
63+
--from-literal=SpaClient=http://$($frontendUrl)
64+
kubectl label configmap urls app=eshop
65+
66+
Write-Host "Creating deployments..."
67+
kubectl apply -f deployments.yaml
68+
69+
# update deployments with the private registry before k8s tries to pull images
70+
# (deployment templating, or Helm, would obviate this)
71+
kubectl set image -f deployments.yaml `
72+
basket=$registry/eshop/basket.api `
73+
catalog=$registry/eshop/catalog.api `
74+
identity=$registry/eshop/identity.api `
75+
ordering=$registry/eshop/ordering.api `
76+
webmvc=$registry/eshop/webmvc `
77+
webspa=$registry/eshop/webspa
78+
kubectl rollout resume -f deployments.yaml
79+
80+
Write-Host "WebSPA is exposed at http://$frontendUrl, WebMVC at http://$frontendUrl/webmvc" -ForegroundColor Yellow

0 commit comments

Comments
 (0)