diff --git a/README.CICD.k8s.md b/README.CICD.k8s.md
new file mode 100644
index 000000000..182880fc5
--- /dev/null
+++ b/README.CICD.k8s.md
@@ -0,0 +1,48 @@
+# Kubernetes CI/CD VSTS
+For k8s CI/CD pipeline delivery a series of tasks must be created in VSTS to deploy k8s in Azure
+
+## Prerequisites
+* 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.
+* 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.
+* 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:
+>```
+>./gen-k8s-env -resourceGroupName k8sGroup -location westeurope -registryName k8sregistry -orchestratorName k8s-cluster -dnsName k8s-dns
+>```
+* An `Azure Blob storage`. It is needed for storing the kubernetes config file used by the hosted agent to access to Kubernetes cluster. Example:
+
+
+
+* Upload the `kubernetes config file` to the blob storage previously created. Execute the following command which will download the config file into the directory `c:\Users\\.kube\` and then, upload it to your blob storage:
+>```
+>https://eshopk8s.blob.core.windows.net/k8s-config/config
+>```
+## Create the VSTS tasks
+1. Create a `Download File` task to download the kubernetes binary `kubectl` to the hosted agent. For example:
+>```
+>https://storage.googleapis.com/kubernetes-release/release/v0.0.1.7.0-alpha.0/bin/windows/386/kubectl.exe
+>```
+
+
+2. Create a Download File task to download the kubernetes config file to the hosted agent. For example:
+>```
+>https://eshopk8s.blob.core.windows.net/k8s-config/config
+>```
+
+
+3. Create a powershell task to execute the k8s deployment script. For example:
+
+* Deployment script path
+>```
+>$(System.DefaultWorkingDirectory)/All Microservices/docker-compose/deploy.ps1
+>```
+
+* Deployment script path arguments. Where:
+ - userDockerHub: indicates if Docker Hub is used instead of ACR
+ - deployCI: indicates that it is a CI/CD deployment
+ - execPath: path where the k8s binary is stored
+ - kubeconfigPath: path where the k8s config file is stored
+>```
+>-deployCI $true -useDockerHub $true -execPath '$(System.DefaultWorkingDirectory)/' -kubeconfigPath '$(System.DefaultWorkingDirectory)/'
+>```
+
+
diff --git a/img/k8s/blob_creation.png b/img/k8s/blob_creation.png
new file mode 100644
index 000000000..a9e386ead
Binary files /dev/null and b/img/k8s/blob_creation.png differ
diff --git a/img/k8s/deploy_script_task.png b/img/k8s/deploy_script_task.png
new file mode 100644
index 000000000..917625f3e
Binary files /dev/null and b/img/k8s/deploy_script_task.png differ
diff --git a/img/k8s/get_kubectlbin_task.png b/img/k8s/get_kubectlbin_task.png
new file mode 100644
index 000000000..423aceca8
Binary files /dev/null and b/img/k8s/get_kubectlbin_task.png differ
diff --git a/img/k8s/get_kubectlconfig_task.png b/img/k8s/get_kubectlconfig_task.png
new file mode 100644
index 000000000..594e68ba7
Binary files /dev/null and b/img/k8s/get_kubectlconfig_task.png differ
diff --git a/k8s/deploy.ps1 b/k8s/deploy.ps1
index 61c7bd909..255243f34 100644
--- a/k8s/deploy.ps1
+++ b/k8s/deploy.ps1
@@ -1,80 +1,117 @@
Param(
- [parameter(Mandatory=$true)][string]$registry,
- [parameter(Mandatory=$true)][string]$dockerUser,
- [parameter(Mandatory=$true)][string]$dockerPassword
+ [parameter(Mandatory=$false)][string]$registry,
+ [parameter(Mandatory=$false)][string]$dockerUser,
+ [parameter(Mandatory=$false)][string]$dockerPassword,
+ [parameter(Mandatory=$false)][bool]$deployCI,
+ [parameter(Mandatory=$false)][bool]$useDockerHub,
+ [parameter(Mandatory=$false)][string]$execPath,
+ [parameter(Mandatory=$false)][string]$kubeconfigPath
)
-$requiredCommands = ("docker", "docker-compose", "kubectl")
-foreach ($command in $requiredCommands) {
- if ((Get-Command $command -ErrorAction SilentlyContinue) -eq $null) {
- Write-Host "$command must be on path" -ForegroundColor Red
- exit
+function ExecKube($cmd) {
+ if($deployCI) {
+ $kubeconfig = $kubeconfigPath + 'config';
+ $exp = $execPath + 'kubectl ' + $cmd + ' --kubeconfig=' + $kubeconfig
+ Invoke-Expression $exp
+ }
+ else{
+ $exp = $execPath + 'kubectl ' + $cmd
+ Invoke-Expression $exp
}
}
-Write-Host "Logging in to $registry" -ForegroundColor Yellow
-docker login -u $dockerUser -p $dockerPassword $registry
-if (-not $LastExitCode -eq 0) {
- Write-Host "Login failed" -ForegroundColor Red
- exit
+# Not used when deploying through CI VSTS
+if(-not $deployCI) {
+ $requiredCommands = ("docker", "docker-compose", "kubectl")
+ foreach ($command in $requiredCommands) {
+ if ((Get-Command $command -ErrorAction SilentlyContinue) -eq $null) {
+ Write-Host "$command must be on path" -ForegroundColor Red
+ exit
+ }
+ }
}
-# create registry key secret
-kubectl create secret docker-registry registry-key `
+# Use ACR instead of DockerHub as image repository
+if(-not $useDockerHub) {
+ Write-Host "Logging in to $registry" -ForegroundColor Yellow
+ docker login -u $dockerUser -p $dockerPassword $registry
+ if (-not $LastExitCode -eq 0) {
+ Write-Host "Login failed" -ForegroundColor Red
+ exit
+ }
+
+ # create registry key secret
+ ExecKube -cmd 'create secret docker-registry registry-key `
--docker-server=$registry `
--docker-username=$dockerUser `
--docker-password=$dockerPassword `
- --docker-email=not@used.com
+ --docker-email=not@used.com'
+}
-# start sql, rabbitmq, frontend deployments
-kubectl create configmap config-files --from-file=nginx-conf=nginx.conf
-kubectl label configmap config-files app=eshop
-kubectl create -f sql-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml
+# Removing previous services & deployments
+Write-Host "Removing existing services & deployments.." -ForegroundColor Yellow
+ExecKube -cmd 'delete -f sql-data.yaml -f rabbitmq.yaml'
+ExecKube -cmd 'delete -f services.yaml -f frontend.yaml -f deployments.yaml'
+ExecKube -cmd 'delete configmap config-files'
+ExecKube -cmd 'delete configmap urls'
-Write-Host "Building and publishing eShopOnContainers..." -ForegroundColor Yellow
-dotnet restore ../eShopOnContainers-ServicesAndWebApps.sln
-dotnet publish -c Release -o obj/Docker/publish ../eShopOnContainers-ServicesAndWebApps.sln
+# start sql, rabbitmq, frontend deploymentsExecKube -cmd 'delete configmap config-files'
+ExecKube -cmd 'create configmap config-files --from-file=nginx-conf=nginx.conf'
+ExecKube -cmd 'label configmap config-files app=eshop'
+ExecKube -cmd 'create -f sql-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml'
-Write-Host "Building Docker images..." -ForegroundColor Yellow
-docker-compose -p .. -f ../docker-compose.yml build
+# building and publishing docker images not necessary when deploying through CI VSTS
+if(-not $deployCI) {
+ Write-Host "Building and publishing eShopOnContainers..." -ForegroundColor Yellow
+ dotnet restore ../eShopOnContainers-ServicesAndWebApps.sln
+ dotnet publish -c Release -o obj/Docker/publish ../eShopOnContainers-ServicesAndWebApps.sln
-Write-Host "Pushing images to $registry..." -ForegroundColor Yellow
-$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "webmvc", "webspa")
-foreach ($service in $services) {
- docker tag eshop/$service $registry/eshop/$service
- docker push $registry/eshop/$service
+ Write-Host "Building Docker images..." -ForegroundColor Yellow
+ docker-compose -p .. -f ../docker-compose.yml build
+
+ Write-Host "Pushing images to $registry..." -ForegroundColor Yellow
+ $services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "webmvc", "webspa")
+ foreach ($service in $services) {
+ docker tag eshop/$service $registry/eshop/$service
+ docker push $registry/eshop/$service
+ }
}
Write-Host "Waiting for frontend's external ip..." -ForegroundColor Yellow
while ($true) {
- $frontendUrl = kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"
+ $frontendUrl = & ExecKube -cmd 'get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"'
if ([bool]($frontendUrl -as [ipaddress])) {
break
}
Start-Sleep -s 15
}
-kubectl create configmap urls `
+ExecKube -cmd 'create configmap urls `
--from-literal=BasketUrl=http://$($frontendUrl)/basket-api `
--from-literal=CatalogUrl=http://$($frontendUrl)/catalog-api `
--from-literal=IdentityUrl=http://$($frontendUrl)/identity `
--from-literal=OrderingUrl=http://$($frontendUrl)/ordering-api `
--from-literal=MvcClient=http://$($frontendUrl)/webmvc `
- --from-literal=SpaClient=http://$($frontendUrl)
-kubectl label configmap urls app=eshop
+ --from-literal=SpaClient=http://$($frontendUrl)'
+
+ExecKube -cmd 'label configmap urls app=eshop'
Write-Host "Creating deployments..."
-kubectl apply -f deployments.yaml
-
-# update deployments with the private registry before k8s tries to pull images
-# (deployment templating, or Helm, would obviate this)
-kubectl set image -f deployments.yaml `
- basket=$registry/eshop/basket.api `
- catalog=$registry/eshop/catalog.api `
- identity=$registry/eshop/identity.api `
- ordering=$registry/eshop/ordering.api `
- webmvc=$registry/eshop/webmvc `
- webspa=$registry/eshop/webspa
-kubectl rollout resume -f deployments.yaml
+ExecKube -cmd 'create -f deployments.yaml'
+
+# not using ACR for pulling images when deploying through CI VSTS
+if(-not $deployCI) {
+ # update deployments with the private registry before k8s tries to pull images
+ # (deployment templating, or Helm, would obviate this)
+ ExecKube -cmd 'set image -f deployments.yaml `
+ basket=$registry/eshop/basket.api `
+ catalog=$registry/eshop/catalog.api `
+ identity=$registry/eshop/identity.api `
+ ordering=$registry/eshop/ordering.api `
+ webmvc=$registry/eshop/webmvc `
+ webspa=$registry/eshop/webspa'
+}
+
+ExecKube -cmd 'rollout resume -f deployments.yaml'
Write-Host "WebSPA is exposed at http://$frontendUrl, WebMVC at http://$frontendUrl/webmvc" -ForegroundColor Yellow