Use Travis CI to automate the deployment of a Python app to IBM Code Engine
I always enjoyed working with Cloud Foundry. It is easy to use and quick to get your code running. All you need to do is push code to Cloud Foundry, include a couple of config files to tell CF what type of code its running, and off you go. Then came Kubernetes (K8s). There is no denying the power of K8s and the flexibility containerized software has. The downside is complexity. Now IBM has created Code Engine. Code Engine takes the power of K8s but removes the complexity, adds in the easy pushing your code and it just runs, the portability of containers, plus a sprinkle of serverless to keep cost down. Lets get going and try this out.
Some prerequisites if you want to follow along.
1) An IBM Cloud Account.
2) A GitHub account (https://github.com/)
3) Docker installed on your workstation (for creating the container and testing)
4) python3 installed on your system
5) A travis-ci account (https://travis-ci.com)
6) A Docker Hub account (https://hub.docker.com/)
Step 1 — Get app containerized and running on local system, then push to Docker Hub
First lets layout our directory structure and create a few files.
mkdir mycoolapp
mkdir mycoolapp/src
cd mycoolapp
touch src/server.py
touch Dockerfile
touch requirements.txt
touch .dockerignore
touch README.md
Now lets create a simple python Flask app for testing. Edit the src/server.py file in your preferred editor.
import os
from flask import Flask
from datetime import datetimeapp = Flask(__name__)@app.route("/")
def index():
today = datetime.now()
return "Hi. The date and time is: {}".format(today.strftime("%Y-%m-%d %H:%M:%S"))if __name__ == "__main__":
SERVER_PORT = os.environ.get("SERVER_PORT", "8000")
app.run(host="", port=int(SERVER_PORT))
Now edit the requirements.txt file. This tells the system what python packages to install.
Flask>=1.1.2
gevent>=1.5.0
greenlet>=0.4.13
gunicorn>=20.0.4
Edit the Dockerfile. This file tells docker how to build your image. In this example I am going to use Red Hat Universal Base Image (UBI) as the base for this container. I am also going to use a multi-stage docker build to keep the image small. I am not going to go into details about that here. The summary is you have a build image that contains everything needed to do the build, then copy what is needed to a minimal sized image. Doing this I was able to reduce the image size from 800+ mb to 220mb
# The Build Image
FROM registry.access.redhat.com/ubi8/python-38 as builder
WORKDIR /app
ADD . /app
RUN pip install --user -r requirements.txt
COPY ./src .# The Production image
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
RUN microdnf install -y python38
COPY --from=builder /opt/app-root/ /opt/app-root/
COPY --from=builder /app/ /app/EXPOSE 5000
ENV PATH $PATH:/opt/venv/bin:/opt/app-root/bin
CMD gunicorn --chdir /app/src --bind 0.0.0.0:5000 app:app gevent --worker-connections 1000 --workers 4
Time to build the container
docker build -t <dockerUser>:mycoolapp:latest .
We can now test that it works on out workstation
docker run -p 5000:5000 <dockerUser>:mycoolapp:latest
You should be able to connect to http://localhost:5000
Assuming all looks good you can now login to docker hub and push the image
docker login
docker push <dockerUser>/mycoolapp
Step 2 — Setup Code Engine and deploy manually
Login to https://cloud.ibm.com/
Navigate to Code Engine and select projects. https://cloud.ibm.com/codeengine/projects Here you can click Create. You will be presented with a form for your new project. Select the location you want your project to run in and give your project a name.
When you click create Code Engine starts setting up your project. This may take a few minutes. Once done you can click on your project to start configuring the apps that are part of the project.
Once you are in your project area you can click create application. Here you can give the app a name set the port your code is listing on (5000 for our test app). You can also update the runtime setting for the number and size of your instances. I am not going to cover that here.
The most important part will be configuring the image. Click the Configure image button. Select docker.io for the registry server. Select if access is public or private. If it is private you will be prompted to provide your dockerhub account information. After that Namespace is <dockerUser> and Repository is mycoolapp. Set the Tag to match the image you want pulled in. We tagged our image with latest, so that that in this field.
TIP:
You can apply more than one tag to your docker images. We will be having Travis tag our images with the build number and latest. When you do this the latest image is overwritten but you keep a history of your images tagged with the build number. You will want to clean up this images from time to time.
Now you can click Done and then Create. Code Engine will pull in this image and start running it. You can click the Open URL link and should be able to access your app.
Step 2 — travis.yml
The .travis.yml tells Travis CI what it needs to do. These can go from mild to wild. For this project I am going to keep it simple. In the real world you should have some testing and other validation built into this.
With this we are telling the Travis environment to install the ibmcloud CLI and then install the Code Engine plugin. Then Travis build the image and pushs it to dockerhub. Finally Travis tell Code Engine to pull the updated image. There are a number of Travis variables that we will setup in the Travis CI website. TRAVIS_BUILD_NUMBER is generated automatically by Travis.
language: shell
os: linux
dist: xenialbranches:
only:
- masterservices:
- dockerbefore_install:
- curl -sL https://raw.githubusercontent.com/IBM-Cloud/ibm-cloud-developer-tools/master/linux-installer/idt-installer | bash
- ibmcloud login --no-region --apikey $API_KEY
- ibmcloud plugin install code-engine
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker build -t $IMAGE_NAME .
- docker tag "$IMAGE_NAME" "${IMAGE_NAME}:latest"
- docker tag "$IMAGE_NAME" "${IMAGE_NAME}:${TRAVIS_BUILD_NUMBER}"
- docker push "${IMAGE_NAME}:latest"
- docker push "${IMAGE_NAME}:${TRAVIS_BUILD_NUMBER}"
- ibmcloud target -g default -r us-south
- ibmcloud ce project select -n mycoolapp
- ibmcloud ce application update -n mycoolappName
Step 3— Create git repo and push files
Make sure you create the repo in github first
git init .
git remote add origin https://github.com/<gitUser>/mycoolapp
git add --all
git commit -m "initial commit"
git push -u origin master
Step 4— Setup Travis ci
Link travis to github or your repo of choice
https://docs.travis-ci.com/user/tutorial/?utm_source=help-page&utm_medium=travisweb#to-get-started-with-travis-ci-using-github
In Travis go to the settings for your repo. Here you can add environment variables.
DOCKER_PASSWORD = Your Docker Hub password
DOCKER_USERNAME = Your Docker Hub user
IMAGE_NAME =<dockerUser>/mycoolapp
At this point we should be all done. You can push a change to your repo and watch Travis get to work. You will notice that Code Engine shuts down your app when it’s not in use. The first time you access your app after it has shut down it will take a few seconds to respond while Code Engine is starting it up again. As long as traffic is coming to your app it will keep running.
Good luck and have fun.