In this tutorial, I’m going to show you how to build a source into a container image from a Dockerfile, by using Google’s kaniko. Finally, we’ll run this container on a private cloud. As many of us in the developer world already know, the private cloud has higher security because a single company or organization is the only designated entity that can access it. For the purposes of this tutorial, I’ll be using IBM Cloud™ Private (ICP) as an example. ICP can satisfy the customization requirements for the users. In this tutorial, we see that the Istio and Knative deployments have already deployed on the IBM Cloud Private system so that users can use kaniko to orchestrate from the source code to the application directly.

So what is kaniko and how does it work? kaniko is a tool that you can use to build container images from a Dockerfile inside a container or Kubernetes cluster.

Prerequisites

Make sure to have the following before beginning this tutorial:

  1. Prepare your IBM Cloud Private environment, which should deploy both Istio and Knative on it.
  2. Install Knative on IBM Cloud Private.(The Knative build uses kaniko as the BuildTemplate to build our image and Knative serving will use the image to start its container.)

Steps

workflow icp kaniko knative

As shown in the workflow image above, we can see kaniko as an image in the Knative build template. After you build your image, the build source will be in the container and executes by using the kaniko executor to build the image. Then it pushes the image to the Dockerhub that you specify.

We can also see a major advantage where the whole process from source code to application runs normally. This can simplify the compilation and deployment environment for the developers. The temporary file, such as the original code, dockerfile, and the compiled images won’t leave anything over in the cluster.

1

Deploy kaniko

  1. kaniko is meant to be run as an image, so in your container execute the binary:

     gcr.io/kaniko-project/executor
    
  2. Deploy kaniko’s build template onto IBM Cloud Private.

     cat <<EOF | kubectl apply -f -
     apiVersion: build.knative.dev/v1alpha1
     kind: BuildTemplate
     metadata:
       name: kaniko
     spec:
       parameters:
       - name: IMAGE
         description: The name of the image to push
       - name: DOCKERFILE
         description: Path to the Dockerfile to build.
         default: /workspace/Dockerfile
    
     steps:
     - name: build-and-push
       image: gcr.io/kaniko-project/executor
       args:
       - --dockerfile=${DOCKERFILE}
       - --destination=${IMAGE}
       env:
     - name: DOCKER_CONFIG
       value: /builder/home/.docker
     EOF
    
2

Orchestrate a source-to-URL deployment on IBM Cloud Private

  1. Create a new Secret manifest, which is used to store your DockerHub credentials.

     apiVersion: v1
     kind: Secret
     metadata:
      name: basic-user-pass
      annotations:
        build.knative.dev/docker-0: https://index.docker.io/v1/
     type: kubernetes.io/basic-auth
     data:
      # Use 'echo -n "username" | base64' to generate this string
      username: BASE64_ENCODED_USERNAME
      # Use 'echo -n "password" | base64' to generate this string
      password: BASE64_ENCODED_PASSWORD
    
  2. Use the following command to generate the base64-encoded values required for the manifest:

     $ echo -n "username" | base64 -w 0
     dXNlcm5hbWU=
     $ echo -n "password" | base64 -w 0
     cGFzc3dvcmQ=
    
  3. Create a new Service Account manifest which is used to link the build process to the secret.

     cat <<EOF |kubectl apply -f -
       apiVersion: v1
       kind: ServiceAccount
       metadata:
         name: build-bot
       secrets:
         - name: basic-user-pass
     EOF
    

    Deploy the sample. This sample uses github.com/mchmarny/simple-app as a basic Go application, but you could replace this GitHub repo with your own. The only requirements are that the repo must contain a Dockerfile with the instructions for how to build a container for the application.

  4. You need to create a service manifest that defines the service to deploy, including where the source code is and which build template to use. Make sure to replace {DOCKER_USERNAME} with your own DockerHub username:

     cat <<EOF | kubectl apply -f -
       apiVersion: serving.knative.dev/v1alpha1
       kind: Service
       metadata:
         name: app-from-source
         namespace: default
      spec:
        runLatest:
          configuration:
            build:
              apiVersion: build.knative.dev/v1alpha1
              kind: Build
              spec:
                serviceAccountName: build-bot
                source:
                  git:
                    url: https://github.com/mchmarny/simple-app.git
                    revision: master
                template:
                  name: kaniko
                  arguments:
                    - name: IMAGE
                    value: docker.io/{DOCKER_USERNAME}/app-from-source:latest
                timeout: 10m
              revisionTemplate:
                spec:
                  container:
                    image: docker.io/{DOCKER_USERNAME}/app-from-source:latest
                    imagePullPolicy: Always
                    env:
                      - name: SIMPLE_MSG
                        value: "Hello from the sample app!"
     EOF
    
  5. Apply this manifest by using kubectl and watch the results. Wait for the pod status Running:

     $ kubectl get pods --watch
     NAME                          READY     STATUS       RESTARTS   AGE
     app-from-source-00001-zhddx   0/1       Init:2/3     0          7s
     app-from-source-00001-zhddx   0/1       PodInitializing   0         37s
     app-from-source-00001-zhddx   0/1       Completed   0         38s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   0/3       Pending   0         0s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   0/3       Pending   0         0s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   0/3       Init:0/1   0         0s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   0/3       Init:0/1   0         2s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   0/3       PodInitializing   0         3s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   2/3       Running   0         6s
     app-from-source-00001-deployment-6d6ff665f9-xfhm5   3/3       Running   0         11s
    
  6. To check on the state of the service, get the service object and examine the status block:

     $ kubectl get ksvc app-from-source --output yaml
    
     [...]
      status:
        conditions:
        - lastTransitionTime: 2018-07-11T20:50:18Z
          status: "True"
          type: ConfigurationsReady
        - lastTransitionTime: 2018-07-11T20:50:56Z
          status: "True"
          type: RoutesReady
        - lastTransitionTime: 2018-07-11T20:50:56Z
          status: "True"
          type: Ready
      domain: app-from-source.default.example.com
      latestCreatedRevisionName: app-from-source-00007
      latestReadyRevisionName: app-from-source-00007
      observedGeneration: 10
      traffic:
        - configurationName: app-from-source
          percent: 100
          revisionName: app-from-source-00007
    

    Verify the pod that you created by checking that the service is running normally:

     $ kubectl get svc istio-ingressgateway --namespace istio-system
     NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
     istio-ingressgateway   LoadBalancer   10.0.238.42   9.30.212.10   80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:31007/TCP,15030:31211/TCP,15031:32043/TCP,15032:31139/TCP,15443:30390/TCP,15020:30332/TCP   6d
    
     $ kubectl get ksvc app-from-source  --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain
     NAME                DOMAIN
     app-from-source     app-from-source.default.example.com
    
     $ curl -H "Host: app-from-source.default.example.com" http://9.30.212.10
     Hello from the sample app!"
    

Now that your service is created, Knative will perform the following steps:

  • Fetch the revision specified from GitHub and use kaniko build it into a container.
  • Push the container to DockerHub.
  • Create a new immutable revision for this version of the app.
  • Network programs to create a route, ingress, service, and load balancer for your app.
  • Automatically scale your pods up and down (including zero active pods).

What’s next

Congratulations, you successfully built a container image onto a private cloud through our example using kaniko and ICP. For more on DockerHub, check out the tutorial “Gain access to your DockerHub public and private repos”. If you’re interested in reading up on Knative, check out the Knative page for more tutorials.