Skip to content

Instantly share code, notes, and snippets.

@marilynwaldman
Created December 3, 2017 22:41
Show Gist options
  • Save marilynwaldman/f05920f7aaf16c283f5c8dae4bc44a1c to your computer and use it in GitHub Desktop.
Save marilynwaldman/f05920f7aaf16c283f5c8dae4bc44a1c to your computer and use it in GitHub Desktop.
Instructions for setting up Jenkins on Kubernetes - tested
Continuous deployment with Jenkins
Connection Details
Open Google Console
Username
Password
GCP Project ID
Continuous Deployment with Jenkins
Overview
Set up a continuous delivery pipeline using Jenkins and Google Kubernetes Engine.
Setup
Step 2
Make sure the following APIs are enabled in Cloud Platform Console:
* Google Container Engine API
* Google Container Registry API
To do this, go to APIs & Services and scroll down the list of APIs and confirm they are enabled. If not, click ENABLE API, search for them by name, and enable them.
Step 3
Open Cloud Shell.
Set your zone by running the following command, substituting your zone for <your-zone>.
gcloud compute zones list
//
gcloud config set compute/zone <your-zone>
Use an assigned zone if you have one.
Step 4
A Git repository contains Kubernetes manifests that deploy Jenkins. The manifests and their settings are described in Configuring Jenkins for Kubernetes Engine.
Run the following command to get the sample code.
git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git
Step 5
Start your Kubernetes cluster with 5 nodes.
cd continuous-deployment-on-kubernetes
gcloud container clusters create contdelivery --num-nodes 5 --scopes "https://www.googleapis.com/auth/projecthosting,storage-rw"
Your environment is ready!
Provision Jenkins
Create the Jenkins home volume
You need a disk volume to pre-populate Jenkins with configurations discussed in Jenkins on Kubernetes Engine. Kubernetes Engine will mount this volume into your Jenkins pod.
These steps can take up to several minutes to complete.
Step 1
Run the following command to create a virtual machine from the supplied tarball:
gcloud compute images create jenkins-home-image --source-uri https://storage.googleapis.com/solutions-public-assets/jenkins-cd/jenkins-home-v3.tar.gz
Step 2
Add a disk to the virtual machine.
gcloud compute disks create jenkins-home --image jenkins-home-image
Configure Jenkins credentials
You need to enable authentication for the Jenkins UI.
Step 1
Create a random password. Take note of the password for use later in the lab.
export PASSWORD=`openssl rand -base64 15`; echo "Your password is $PASSWORD"; sed -i.bak s#CHANGE_ME#$PASSWORD# jenkins/k8s/options
Step 2
Create a Kubernetes namespace for Jenkins. Namespaces allow you to use the same resource manifests across multiple environments without needing to give resources unique names. You will include this namespace as a parameter to the commands you send to Kubernetes.
kubectl create ns Jenkins
Step 3
Create a Kubernetes secret. Kubernetes uses this object to provide Jenkins with the default username and password when Jenkins boots.
kubectl create secret generic jenkins --from-file=jenkins/k8s/options --namespace=jenkins
Deploy Jenkins
In this section, you'll create a Jenkins deployment and services based on the Kubernetes resources defined in the jenkins/k8s folder of the sample code.
The kubetcl apply command creates a Jenkins deployment that contains a container for running Jenkins and a persistent disk that contains the Jenkins home directory. Keeping the home directory on the persistent disk ensures that your critical configuration data is maintained, even if the pod running your Jenkins master goes down.
The kubetcl apply command also creates two services that enable your Jenkins master to be accessed by other pods in the cluster:
* A NodePort service on port 8080 that allows pods and external users to access the Jenkins user interface. This type of service can be load balanced by an HTTP Load Balancer.
* A ClusterIP service on port 50000 that the Jenkins executors use to communicate with the Jenkins master from within the cluster.
Step 1
Create the Jenkins deployment and services.
kubectl apply -f jenkins/k8s/
Step 2
Confirm that the pod is running. Look for Running in the STATUS column.
kubectl get pods -n jenkins
Configure HTTP load balancing
Create an ingress resource that manages the external load balancing of the Jenkins user interface service. The ingress resource also acts as an SSL terminator to encrypt communication between users and the Jenkins user interface service.
Step 1
Confirm that the services are set up correctly by listing the services in the Jenkins namespace. Confirm that jenkins-discovery and jenkins-ui display. If not, ensure the steps above were all run.
kubectl get svc -n jenkins
Step 2
Create an SSL certificate and key.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=jenkins/O=jenkins"
Step 3
Upload the certificate to Kubernetes as a secret.
kubectl create secret generic tls --from-file=/tmp/tls.crt --from-file=/tmp/tls.key -n Jenkins
Step 4
Create the HTTPS load balancer using an ingress.
kubectl apply -f jenkins/k8s/lb/ingress.yaml
Connect to Jenkins
Step 1
Check the status of the load balancer's health checks using the following command:
kubectl describe ingress jenkins --namespace jenkins
Repeat this step until you see the backends field (not shown) display HEALTHY.
It can take several minutes for these steps to complete.
Step 2
Once your backends are healthy, you can get the Jenkins URL by running the following command.
echo "Jenkins URL: https://`kubectl get ingress jenkins -n jenkins -o jsonpath='{.status.loadBalancer.ingress[0].ip}'`"; echo "Your username/password: jenkins/$PASSWORD"
Step 3
Visit the URL from the previous command in your browser and log in with the credentials displayed.
Congratulations!
Jenkins is set up in your cluster. You will use Jenkins to drive your automated CI/CD pipelines in the next sections.
Understand the application
You'll deploy the sample application, gceme, in your continuous deployment pipeline. The application is written in the Go language and is located in the repo's sample-app directory. When you run the gceme binary on a Compute Engine instance, the app displays the instance's metadata in an info card as follows:
The application mimics a microservice by supporting two operation modes.
* In backend mode, gceme listens on port 8080 and returns Compute Engine instance metadata in JSON format.
* In frontend mode, gceme queries the backend gceme service and renders the resulting JSON in the user interface.
Deploy the application
You will deploy the application into two different environments:
* Production: The live site that your users access.
* Canary: A smaller-capacity site that receives only percentage of your user traffic. Use this environment to validate your software with live traffic before it's released to all of your users.
Step 1
In Google Cloud Shell, navigate to the sample application directory.
cd sample-app
Step 2
Create the Kubernetes namespace to logically isolate the deployment.
kubectl create ns production
Step 3
Create the production and canary deployments and services using the kubectl apply commands.
kubectl apply -f k8s/production -n production
kubectl apply -f k8s/canary -n production
kubectl apply -f k8s/services -n production
Step 4
Scale up the production environment frontends. By default, only one replica of the frontend is deployed. Use the kubectl scale command to ensure that you have at least 4 replicas running at all times.
kubectl scale deployment gceme-frontend-production -n production --replicas 4
Step 5
Confirm that you have 5 pods running for the frontend: 4 for production traffic and 1 for canary releases. This means that changes to your canary release will only affect 1 out of 5 (20%) of users. You should also have 2 pods for the backend: 1 for production and 1 for canary.
kubectl get pods -n production -l app=gceme -l role=frontend
kubectl get pods -n production -l app=gceme -l role=backend
Step 6
Retrieve the external IP for the production services.
It can take several minutes before you see the load balancer external IP address.
kubectl get service gceme-frontend -n production
Step 7
Store the frontend service load balancer IP in an environment variable for use later.
export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
Step 8
Confirm that both services are working by opening the frontend external IP address in your browser.
Step 9
Check the version output of the service by hitting the /version path. It should read 1.0.0.
curl http://$FRONTEND_SERVICE_IP/version
Congratulations!
You have successfully deployed the sample application! Next you will set up a pipeline for deploying your changes continuously and reliably.
Create the Jenkins pipeline
Create a Repository to host the sample app source code
Step 1
Create a copy of the gceme sample app and push it to Cloud Source Repositories.
Step 2
Initialize the sample-app directory as its own Git repository. Replace [PROJECT_ID] with your current project ID in the following command. To find your current project ID you can run gcloud config list project.
gcloud alpha source repos create default
git init
git config credential.helper gcloud.sh
git remote add origin https://source.developers.google.com/p/[PROJECT_ID]/r/default
Step 3
Set the username and email address for your Git commits. Replace [EMAIL_ADDRESS] with your Git email address. Replace [USERNAME] with your Git username.
git config --global user.email "[EMAIL_ADDRESS]"
git config --global user.name "[USERNAME]"
Step 4
Add, commit, and push the files.
git add .
git commit -m "Initial commit"
git push origin master
Add your service account credentials
Configure your credentials to allow Jenkins to access the code repository. Jenkins will use your cluster's service account credentials in order to download code from the Cloud Source Repositories.
Step 1
In the Jenkins user interface, click Credentials in the left navigation.
Step 2
Click Jenkins in the top group.
Step 3
Click Global Credentials.
Step 4
Click Add Credentials in the left navigation.
Step 5
Select Google Service Account from metadata from the Kind drop-down.
Step 6
Click OK.
Step 7
There are now two global credentials. Make a note of the second credential's name for use later on in this tutorial.
Create the Jenkins job
Navigate to your Jenkins user interface and configure a Pipeline job.
Step 1
Click the Jenkins link in the top left of the interface.
Step 2
Click the New Item link in the left navigation.
Step 3
Name the project sample-app, then choose the Multibranch Pipeline option and click OK.
Step 4
On the next page, click Add Source and select git.
Step 5
Paste the HTTPS clone URL of your sample-app repo in Cloud Source Repositories into the Project Repository field. Replace [PROJECT_ID] with your project ID.
https://source.developers.google.com/p/[PROJECT_ID]/r/default
Step 6
From the Credentials drop-down, select the name of the credentials you created when adding your service account in the previous steps.
Step 7
Under Build Triggers, select the checkbox Build Periodically, and enter five asterisks (* * * * *) into the Schedule field. This ensures that Jenkins checks your code repository for changes once every minute. This field uses the CRON expression syntax to define the schedule.
Step 8
Your job configuration should look like this:
Step 9
Click Save.
After you complete these steps, a job named "Branch indexing" runs. This meta-job identifies the branches in your repository and ensures changes haven't occurred in existing branches. If you click sample-app in the top left, the master job should be seen.
The first run of the master job fails until you make a few code changes in the next step.
Congratulations!
You have successfully created a Jenkins pipeline. Next you will create the development environment for continuous integration.
Create the development environment
Development branches are a set of environments your developers use to test their code changes before submitting them for integration into the live site. These environments are scaled-down versions of your application, but need to be deployed using the same mechanisms as the live environment.
Create a development branch
To create a development environment from a feature branch, you can push the branch to the Git server and let Jenkins deploy your environment.
Create a development branch and push it to the Git server.
git checkout -b new-feature
Modify the pipeline definition
The Jenkinsfile that defines that pipeline is written using the Jenkins Pipeline Groovy syntax. Using a Jenkinsfile allows an entire build pipeline to be expressed in a single file that lives alongside your source code. Pipelines support powerful features like parallelization and requiring manual user approval.
In order for the pipeline to work as expected, you need to modify the Jenkinsfile to set your project ID.
Step 1
Open the Jenkinsfile in your favorite terminal editor. For example using Vi.
vi Jenkinsfile
Step 2
Replace REPLACE_WITH_YOUR_PROJECT_ID with your project ID. To get your project ID, run gcloud config get-value project
def project = 'REPLACE_WITH_YOUR_PROJECT_ID' def appName = 'gceme' def feSvcName = "${appName}-frontend" def imageTag = "gcr.io/${project}/${appName}:${env.BRANCH_NAME}.${env.BUILD_NUMBER}"
Step 3
Save the file and exit the editor. In Vi, enter :wq.
Modify the site
In order to demonstrate changing the application, you will be change the gceme cards from blue to orange.
Step 1
Open html.go and replace the two instances of blue with orange.
Step 2
Open main.go and change the version number from 1.0.0 to 2.0.0. The version is defined in this line:
const version string = "2.0.0"
Kick off deployment
Step 1
Commit and push your changes. This will kick off a build of your development environment.
git add Jenkinsfile html.go main.go
git commit -m "Version 2.0.0"
git push origin new-feature
Step 2
After the change is pushed to the Git repository, navigate to the Jenkins user interface where you can see that your build started for the new-feature branch It can take up to a minute for the changes to be picked up.
Step 3
After the build is running, click the down arrow next to the build in the left navigation and select Console Output.
Step 4
Track the output of the build for a few minutes and watch for the kubectl --namespace=new-feature apply... messages to begin. Your new-feature branch will now be deploying to your cluster.
In a development scenario, you wouldn't use a public-facing load balancer. To help secure your application, you can use kubectl proxy. The proxy authenticates itself with the Kubernetes API and proxies requests from your local machine to the service in the cluster without exposing your service to the Internet.
Step 5
Start the proxy in the background.
kubectl proxy &
Step 6
Verify that your application is accessible by sending a request to localhost and letting kubectl proxy forward it to your service. You should see it respond with 2.0.0, which is the version that is now running.
curl http://localhost:8001/api/v1/proxy/namespaces/new-feature/services/gceme-frontend:80/version
Congratulations!
You have set up the development environment. Next you will build on what you learned in the previous module on Deploying to Kubernetes to deploy a canary release to test out a new feature.
Deploy a Canary release
Now that you have verified that your app is running your latest code in the development environment, deploy that code to the canary environment.
Step 1
Create a canary branch and push it to the Git server.
git checkout -b canary
git push origin canary
Step 2
In Jenkins, you should see the canary pipeline has kicked off. Once complete, you can check the service URL to ensure that some of the traffic is being served by your new version. You should see about 1 in 5 requests returning version 2.0.0.
export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1; done
You can stop this command by pressing Ctrl-C.
Congratulations!
You have deployed a canary release. Next you will deploy the new version to production.
Deploy to production
Now that your canary release was successful and you haven't heard any customer complaints, you can deploy to the rest of your production fleet.
Step 1
Create a canary branch and push it to the Git server.
git checkout master
git merge canary
git push origin master
Step 2
In Jenkins, you should see the master pipeline has kicked off. Once complete, you can check the service URL to ensure that all of the traffic is being served by your new version, 2.0.0. You can also navigate to the site using your browser to see your orange cards.
export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1; done
Step 3
You can stop this command by pressing Ctrl-C.
Congratulations!
You have successfully deployed your application to production!
End
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment