Tuesday, 11 October 2016

Pushing a Docker image using Docker Hub on Pivotal Cloud Foundry

In this example I will show how to push a Docker image to Pivotal Cloud Foundry (PCF) using Docker Hub. You can use your own private Docker Registry BUT here I am using Docker Hub in this example.

The example spring boot application which can easily created as a Docker Image is per this spring guide below.

https://spring.io/guides/gs/spring-boot-docker/

1. First we need to ensure the docker is enabled on Diego as shown below.

pasapicella@pas-macbook:~$ cf feature-flag diego_docker
Retrieving status of diego_docker as admin...
OK

Features           State
diego_docker   enabled

Note: If it's not enabled you would need ADMIN rights to set it as follows

$ cf enable-feature-flag diego_docker

2. Login to Docker Hub from the command line

pasapicella@pas-macbook:~/pivotal/software/docker$ docker login -u pasapples -p ******
Login Succeeded

3. Push your local Docker image to your public Docker Hub Repository as follows

This assumes you have an IMAGE to push as per below.

pasapicella@pas-macbook:~/pivotal/software/docker$ docker images
REPOSITORY                            TAG                 IMAGE ID            CREATED             SIZE
pasapples/cf                                 0.0.1                b25e9b214774       3 days ago              881.4 MB
pasapples/gs-spring-boot-docker latest               5fc76927eca2        3 days ago              195.5 MB
gregturn/gs-spring-boot-docker   latest               a813439710d3       3 days ago              195.4 MB
ubuntu                                          14.04               f2d8ce9fa988        2 weeks ago            187.9 MB
frolvlad/alpine-oraclejdk8            slim                f8103909759b       2 weeks ago            167.1 MB
springio/gs-spring-boot-docker    latest              688d6c4ab4d3       18 months ago         609.9 MB

** Push to Docker Hub **

$ docker push pasapples/gs-spring-boot-docker
The push refers to a repository [docker.io/pasapples/gs-spring-boot-docker]
1a701a998f45: Layer already exists
0d4e0b525d4f: Layer already exists
a27c88827076: Pushed
58f7b9930e4f: Layer already exists
9007f5987db3: Layer already exists
latest: digest: sha256:6b3ccae43e096b1fa4d288900c6d2328e34f11e286996ffa582961bad599aee9 size: 1375

4. Login to Docker Hub and verify it's loaded as shown below

https://hub.docker.com/


At this point we are ready to Deploy to our PCF instance and it's assumed you have already logged into the instance prior to running this next step

5. Push as shown below to PCF

pasapicella@pas-macbook:~$ cf push springboot-docker --docker-image pasapples/gs-spring-boot-docker --random-route -i 1 -m 512M -t 180
Creating app springboot-docker in org apples-org / space development as papicella@pivotal.io...
OK

Creating route springboot-docker-oological-superseniority.apps.pcfdemo.net...
OK

Binding springboot-docker-oological-superseniority.apps.pcfdemo.net to springboot-docker...
OK


Starting app springboot-docker in org apples-org / space development as papicella@pivotal.io...
Creating container
Successfully created container
Staging...
Staging process started ...
Staging process finished
Exit status 0
Staging Complete
Destroying container
Successfully destroyed container

0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
1 of 1 instances running

App started


OK

App springboot-docker was started using this command `java -Djava.security.egd=file:/dev/./urandom -jar /app.jar `

Showing health and status for app springboot-docker in org apples-org / space development as papicella@pivotal.io...
OK

requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: springboot-docker-oological-superseniority.apps.pcfdemo.net
last uploaded: Tue Oct 11 02:04:42 UTC 2016
stack: unknown
buildpack: unknown

     state     since                    cpu      memory           disk         details
#0   running   2016-10-11 01:07:34 PM   104.3%   309.3M of 512M   1.4M of 1G



You can generate an application.yml as shown below

pasapicella@pas-macbook:~$ cf create-app-manifest springboot-docker
Creating an app manifest from current settings of app springboot-docker ...

OK
Manifest file created successfully at ./springboot-docker_manifest.yml

pasapicella@pas-macbook:~$ cat springboot-docker_manifest.yml
applications:
- name: springboot-docker
  instances: 1
  memory: 512M
  disk_quota: 1024M
  host: springboot-docker-oological-superseniority
  domain: apps.pcfdemo.net
  stack: cflinuxfs2
  timeout: 180

More Information

http://docs.pivotal.io/pivotalcf/1-8/adminguide/docker.html

Monday, 10 October 2016

Displaying Pivotal Cloud Foundry application Instances Buildpacks or Docker Images using CF CLI

I was recently asked how you could display all PCF application instances detected buildpack OR docker image being used from the command line. The CF REST API gives you all of this information and more as per the documentation below to list all applications.

https://apidocs.cloudfoundry.org/244/apps/list_all_apps.html

This API call gives you lots of information so to filter that a fellow work college created this script to get just the output we want. You need to be logged into your PCF instance with "cf login" prior to running this script because it's using "CF CURL" rather then calling the REST API directly

guids=$(cf curl /v2/apps?q=space_guid:`cf space development --guid` | jq -r ".resources[].metadata.guid")
echo -e "App Name, Buildpack, Docker"
for guid in $guids; do
appName=$(cf curl /v2/apps/$guid/summary | jq -r ".name")
buildpack=$(cf curl /v2/apps/$guid/summary | jq -r ".detected_buildpack")
docker_image=$(cf curl /v2/apps/$guid/summary | jq -r ".docker_image")
echo -e "$appName," "$buildpack," "$docker_image"
done

Output:

App Name, Buildpack, Docker
guestbook-backend, null, jamesclonk/guestbook-backend:latest
springboot-docker, null, pasapples/gs-spring-boot-docker:latest
pas-albums, java-buildpack=v3.8.1-offline-https://github.com/cloudfoundry/java-buildpack.git#29c79f2 java-main java-opts open-jdk-like-jre=1.8.0_91-unlimited-crypto open-jdk-like-memory-calculator=2.0.2_RELEASE spring-auto-reconfiguration=1.10.0_RELEASE, null

To use the REST API directly replace

guids=$(cf curl /v2/apps?q=space_guid:`cf space development --guid` | jq -r ".resources[].metadata.guid")

WITH

guids=$(curl -k https://api.run.pivotal.io/v2/apps?q=space_guid:`cf space development --guid` -X GET -H "Authorization: `cf oauth-token`" | jq -r ".resources[].metadata.guid")

Thursday, 6 October 2016

Reading VCAP_SERVICES and VCAP_APPLICATION from a Spring Boot Rest Controller in PCF

Note for myself: Reading PCF System and ENV variables

  
package com.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class DemoRest
{
    private static final Logger logger = LoggerFactory.getLogger(DemoRest.class);

    @RequestMapping(value = "/version", method = RequestMethod.GET)
    public String version()
    {
        return "1.0";
    }

    @RequestMapping(value = "/vcapapplication", method = RequestMethod.GET)
    public Map vcapApplication() throws Exception
    {
        return Utils.getEnvMap("VCAP_APPLICATION");
    }

    @RequestMapping(value = "/vcapservices", method = RequestMethod.GET)
    public Map vcapServices() throws Exception
    {
        return Utils.getEnvMap("VCAP_SERVICES");
    }

    @RequestMapping(value = "/vcapservices_json", method = RequestMethod.GET)
    public String vcapServicesJSON() throws Exception
    {
        return System.getenv().get("VCAP_SERVICES");
    }


    @RequestMapping(value = "/appindex", method = RequestMethod.GET)
    public String appIndex() throws Exception
    {
        String instanceIndex = "N/A";

        try
        {
            instanceIndex =
                    Utils.getEnvMap("VCAP_APPLICATION").getOrDefault("instance_index", "N/A").toString();
        }
        catch (Exception ex)
        {
           logger.info("Exception getting application index : " + ex.getMessage());
        }

        return instanceIndex;
    }

    @RequestMapping(value = "/getEnvVariable/{env_var}", method = RequestMethod.GET)
    public String getEnvVariable(@PathVariable String env_var)
    {
        return System.getenv().get(env_var);
    }

} 

Utils.java (Referenced in Code above)
  
package com.example;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class Utils
{
    public static Map getEnvMap(String vcap) throws Exception
    {
        String vcapEnv = System.getenv(vcap);
        ObjectMapper mapper = new ObjectMapper();

        if (vcapEnv != null) {
            Map<String, ?> vcapMap = mapper.readValue(vcapEnv, Map.class);
            return vcapMap;
        }

        return new HashMap<String, String>();
    }
}