10. Kuberenetes

In this example we will be running the Skipper server on the local machine and deploying to minikube also running on the local machine.

[Note]Note

The upgrade approach in 1.0 M1 does not handle correctly the routing of http traffic between versions, so take what is below with a grain of salt. The Spring Cloud Deployer for Kubernetes creates a service, replication controller, and pod for the app (or optionally a Deployment) This is not an issue for apps that communicate via Messaging middleware. Treat the current M1 release as a WIP.

Start the Skipper server with the option --spring.config.location=skipper.yml where the YAML is shown below.

spring:
  cloud:
    skipper:
      server:
        enableLocalPlatform: false
        platform:
          kubernetes:
            accounts:
              minikube:
                namespace: default

The repo list command shows the experimental and local repositories, since they are configured by default.

skipper:>repo list
╔════════════╤═══════════════════════════════════════════════════════════╤═════╤═════╗
║    Name    │                            URL                            │Local│Order║
╠════════════╪═══════════════════════════════════════════════════════════╪═════╪═════╣
║experimental│http://skipper-repository.cfapps.io/repository/experimental│false│0    ║
║local       │http://d4d6d1b6-c7e5-4226-69ec-01d4:7577                   │true │1    ║
╚════════════╧═══════════════════════════════════════════════════════════╧═════╧═════╝

and the search command shows

skipper:>search
╔═════════════════╤═══════╤════════════════════════════════════════════════════════════════════════════════╗
║      Name       │Version│                                  Description                                   ║
╠═════════════════╪═══════╪════════════════════════════════════════════════════════════════════════════════╣
║helloworld       │1.0.1  │The app has two endpoints, /about and /greeting in Portuguese.  Maven resource. ║
║helloworld       │1.0.0  │The app has two endpoints, /about and /greeting in English.  Maven resource.    ║
║helloworld-docker│1.0.1  │The app has two endpoints, /about and /greeting in Portuguese.  Docker resource.║
║helloworld-docker│1.0.0  │The app has two endpoints, /about and /greeting in English.  Docker resource.   ║
╚═════════════════╧═══════╧════════════════════════════════════════════════════════════════════════════════╝

The command platform list shows which platforms the server has been configured with, in this case just one Kubernetes namespace. Disabling the local platform with the property enableLocalPlatform = false is why the default local platform does not appear.

skipper:>platform list
╔════════╤══════════╤══════════════════════════════════════════════════════════════════════════════════════╗
║  Name  │   Type   │                                     Description                                      ║
╠════════╪══════════╪══════════════════════════════════════════════════════════════════════════════════════╣
║minikube│kubernetes│master url = [https://192.168.99.100:8443/], namespace = [default], api version = [v1]║
╚════════╧══════════╧══════════════════════════════════════════════════════════════════════════════════════╝

Let’s install the Hello World app, specifically, the Docker based artifact.

skipper:>install --release-name helloworldk8s --package-name helloworld-docker --package-version 1.0.0 --platform-name minikube --properties spec.deploymentProperties.spring.cloud.deployer.kubernetes.createNodePort=32123
Released helloworldk8s. Now at version v1.

If you do not specify --platform-name minikube the command will fail since the command property defaults to a platform named default. Instead of specifying it, you can register this Kubernetes Platform account in the manifest with the name default instead of minikube.

You can monitor the process using the status command.

skipper:>status --release-name helloworldk8s
╔═══════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════╗
║Last Deployed  │Wed Oct 25 17:34:24 EDT 2017                                                                      ║
║Status         │DEPLOYED                                                                                          ║
║Platform Status│The applications are being deployed.                                                              ║
║               │[helloworldk8s-helloworld-docker-v1], State = [helloworldk8s-helloworld-docker-v1-cch68=deploying]║
╚═══════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════╝

Eventually the Platform Status will say All applications have been successfully deployed.

Note that the status DEPLOYED above indicates that Skipper has told the platform to deploy. Skipper does not keep track of the intermediate states 'deploying' or 'deleting'.

A kubectl pods command will now have a new listing for this deployed application

$ kubectl get pods
NAME                                       READY     STATUS    RESTARTS   AGE
helloworldk8s-helloworld-docker-v1-g8j39   0/1       Running   0          37s

$ kubectl get service
NAME                                 CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
helloworldk8s-helloworld-docker-v1   10.0.0.202   <nodes>       8080:32123/TCP   41s
kubernetes                           10.0.0.1     <none>        443/TCP          57m

To get the URL of this app on minikube

$ minikube service --url helloworldk8s-helloworld-docker-v1
http://192.168.99.100:32123

You can now curl the greeting endpoint and the about endpoint.

$ curl http://192.168.99.100:32123/greeting
Hello World!
$ curl http://192.168.99.100:32123/about
Hello World v1.0.0.RELEASE

The name of the application is based on the convention <release-name>-<package-name>-v<incrementing-counter>. This will need to change in future releases in order to handle routing correctly.

The package provides a means to template the application version, application properties and deployment properties that are used to deploy the application to Kubernetes. The manifest get command shows the final YAML file which is passed off to the Spring Cloud Deployer Library.

skipper:>manifest get --release-name helloworldk8s

---
# Source: template.yml
apiVersion: skipper/v1
kind: SpringBootApp
metadata:
  name: helloworld-docker
spec:
  resource: docker:springcloud/spring-cloud-skipper-samples-helloworld:1.0.0.RELEASE
  applicationProperties:
  deploymentProperties:
    spring.cloud.deployer.kubernetes.createNodePort: 32123

The manifest is in a Kubernetes Resource file inspired format. By looking at the manifest you can see which Docker images was used and which properties were set before the final push to Kubernetes. The metadata values that are present will be used in the next release to support searching for releases based on those values.

Since it is somewhat awkward to specify multiple flattened out YAML values for the --properties argument in the shell, you can also specify the location of a YAML file when installing or upgrading. We will use a YAML file when we will update the release. This application contains a Spring Boot @ConfigurationProperty named helloworld.greeting, so we will set that along with a standard Spring Boot property endpoints.sensitive=false. We will also bump the memory up to 2G.

spec:
  applicationProperties:
    endpoints.sensitive: false
    helloworld.greeting: yo
  deploymentProperties:
    spring.cloud.deployer.kubernetes.createNodePort: 32124
    spring.cloud.deployer.memory: 2048m

The upgrade command

skipper:>upgrade --release-name helloworldk8s --package-name helloworld-docker --package-version 1.0.0 --file /home/mpollack/helloworld-upgrade-k8s.yml
helloworldk8s has been upgraded.  Now at version v2.

This will start another instance of the hello world application. If you do not specify --package-version it will pick the latest version of the helloworld-docker package. You do not need to specify the --platform-name as it will always be where the current application was deployed.

the kubectl get all command shows

$ kubectl get all
NAME                                          READY     STATUS    RESTARTS   AGE
po/helloworldk8s-helloworld-docker-v1-g8j39   1/1       Running   0          2m
po/helloworldk8s-helloworld-docker-v2-jz85l   0/1       Running   0          50s

NAME                                    DESIRED   CURRENT   READY     AGE
rc/helloworldk8s-helloworld-docker-v1   1         1         1         2m
rc/helloworldk8s-helloworld-docker-v2   1         1         0         50s

NAME                                     CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
svc/helloworldk8s-helloworld-docker-v1   10.0.0.202   <nodes>       8080:32123/TCP   2m
svc/helloworldk8s-helloworld-docker-v2   10.0.0.154   <nodes>       8080:32124/TCP   51s
svc/kubernetes                           10.0.0.1     <none>        443/TCP          59m

At this point Skipper is looking to see if the health endpoint of the Boot application is ok. The property spring.cloud.skipper.server.strategies.healthcheck.timeoutInMillis is the maximum time the upgrade process will wait for a healthy app. The default value is 5 minutes. Skipper will fail the deployment if it is not healthy within that time. The property spring.cloud.skipper.server.strategies.healthcheck.sleepInMillis is how long to sleep between health checks.

The current upgrade strategy is very simple, if the new app is healthy, the old app is removed. There is not a rolling upgrade option, all new apps are deployed, checked for health, and then previous versions removed. More flexible upgrade strategies are planned along with the introduction of the Spring Cloud State Machine project to orchestrate the update process.

You can now curl the greeting endpoint and the about endpoint.

$ curl http://192.168.99.100:32124/greeting
yo
$ curl http://192.168.99.100:32124/about
Hello World v1.0.0.RELEASE

The list command shows you the current DEPLOYED and DELETED release for every release name. In this case there is just one entry

skipper:>list
╔═════════════╤═══════╤════════════════════════════╤════════╤═════════════════╤═══════════════╤═════════════╤═══════════════╗
║    Name     │Version│        Last updated        │ Status │  Package Name   │Package Version│Platform Name│Platform Status║
╠═════════════╪═══════╪════════════════════════════╪════════╪═════════════════╪═══════════════╪═════════════╪═══════════════╣
║helloworldk8s│2      │Wed Oct 25 17:36:16 EDT 2017│DEPLOYED│helloworld-docker│1.0.0          │minikube     │               ║
╚═════════════╧═══════╧════════════════════════════╧════════╧═════════════════╧═══════════════╧═════════════╧═══════════════╝

You can get the full history of the release using the history command.

skipper:>history --release-name helloworldk8s
╔═══════╤════════════════════════════╤════════╤═════════════════╤═══════════════╤════════════════╗
║Version│        Last updated        │ Status │  Package Name   │Package Version│  Description   ║
╠═══════╪════════════════════════════╪════════╪═════════════════╪═══════════════╪════════════════╣
║2      │Wed Oct 25 17:36:16 EDT 2017│DEPLOYED│helloworld-docker│1.0.0          │Upgrade complete║
║1      │Wed Oct 25 17:34:24 EDT 2017│DELETED │helloworld-docker│1.0.0          │Delete complete ║
╚═══════╧════════════════════════════╧════════╧═════════════════╧═══════════════╧════════════════╝

A more typical upgrade process is not to change application properties, but to change the version of the application because the code has change. We will now upgrade the release to use a new Docker artifact, version 1.0.1, which also corresponds to version 1.0.1 of the helloworld Skipper package. In this case we will not add any additional properties other than the NodePort.

skipper:>upgrade --release-name helloworldk8s --package-name helloworld-docker --package-version 1.0.1 --properties spec.deploymentProperties.spring.cloud.deployer.kubernetes.createNodePort=32125
Released helloworldk8s. Now at version v3.

Note that the the current release’s property values such as using 2G, or the greeting being yo are not carried over. In a future release we will introduce a --reuse-properties command that will carry the current release properties over to the next release to be made. You can monitor the status of the upgrade using the status command

skipper:>status --release-name helloworldk8s
╔═══════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════╗
║Last Deployed  │Wed Oct 25 17:41:33 EDT 2017                                                                      ║
║Status         │DEPLOYED                                                                                          ║
║Platform Status│All applications have been successfully deployed.                                                 ║
║               │[helloworldk8s-helloworld-docker-v3], State = [helloworldk8s-helloworld-docker-v3-sb59j=deployed] ║
╚═══════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════╝

And a curl command shows

$ curl http://192.168.99.100:32125/greeting
Olá Mundo!

$ curl http://192.168.99.100:32125/about
Hello World v1.0.1.RELEASE

Our history now looks like

skipper:>history --release-name helloworldk8s
╔═══════╤════════════════════════════╤════════╤═════════════════╤═══════════════╤════════════════╗
║Version│        Last updated        │ Status │  Package Name   │Package Version│  Description   ║
╠═══════╪════════════════════════════╪════════╪═════════════════╪═══════════════╪════════════════╣
║3      │Wed Oct 25 17:41:33 EDT 2017│DEPLOYED│helloworld-docker│1.0.1          │Upgrade complete║
║2      │Wed Oct 25 17:36:16 EDT 2017│DELETED │helloworld-docker│1.0.0          │Delete complete ║
║1      │Wed Oct 25 17:34:24 EDT 2017│DELETED │helloworld-docker│1.0.0          │Delete complete ║
╚═══════╧════════════════════════════╧════════╧═════════════════╧═══════════════╧════════════════╝

Next we will use the rollback command to deploy an older version of the application. Since we have the manifest for that version, we have all we need to redeploy an earlier release.

skipper:>rollback --release-name helloworldk8s --release-version 2
helloworldk8s has been rolled back.  Now at version v4.

The history now shows a new v4 version, even though it is identical to the v2 version.

skipper:>history --release-name helloworldk8s
╔═══════╤════════════════════════════╤════════╤═════════════════╤═══════════════╤════════════════╗
║Version│        Last updated        │ Status │  Package Name   │Package Version│  Description   ║
╠═══════╪════════════════════════════╪════════╪═════════════════╪═══════════════╪════════════════╣
║4      │Wed Oct 25 17:44:25 EDT 2017│DEPLOYED│helloworld-docker│1.0.0          │Upgrade complete║
║3      │Wed Oct 25 17:41:33 EDT 2017│DELETED │helloworld-docker│1.0.1          │Delete complete ║
║2      │Wed Oct 25 17:36:16 EDT 2017│DELETED │helloworld-docker│1.0.0          │Delete complete ║
║1      │Wed Oct 25 17:34:24 EDT 2017│DELETED │helloworld-docker│1.0.0          │Delete complete ║
╚═══════╧════════════════════════════╧════════╧═════════════════╧═══════════════╧════════════════╝

The curl commands show

$ curl http://192.168.99.100:32124/greeting
yo
$ curl http://192.168.99.100:32124/about
Hello World v1.0.0.RELEASE