Continuous integration is the first step toward a completely automated development, deployment and operations pipeline. It helps to mitigate integration issues, and catch known issues early via automated testing. If you're new to continuous integration, this course is a great place to start. We'll explore the various tools, technologies, and vocabularies surrounding the continuous integration ecosystem, as well as introduce you to the key tools of the trade that will enable you to get a headstart in your burgeoning DevOps career.
You will gain the following skills by completing this course:
- How to set up your development environment
- How version control works
- How to begin implementing testing in your environment
- The why and how of database schema migrations
- What Jenkins is and why you should care
You should take this course if you are:
- A newcomer to the DevOps or cloud world
- Looking to upgrade your skills from a conventional software development career
This Course Includes
- Expert-guided lectures about continuous integration
- 57 minutes of high-definition video
- Solid foundational knowledge for your explorations into DevOps
What You'll Learn
|Video Lecture||What You'll Learn|
|What Is CI?||What continuous integration is and why it matters.|
Creating a Development Environment
|How to set up your development environment.|
|Version Control||How version control interacts with the CI process.|
|Testing||How to mechanize your testing with CI.|
|Database Schema Changes||How to implement and run database schema changes.|
|Introduction to Jenkins||An overview of Jenkins and how to utilize it within your CI process.|
If you have thoughts or suggestions for this course, please contact Cloud Academy at firstname.lastname@example.org.
Welcome back to Introduction to Continuous Integration. I'm Ben Lambert and I'll be your instructor for this lecture.
Up until now, we haven't really talked about CI in depth. We've talked about the individual things that make up continuous integration, however, I haven't gone into detail on the problem that CI solves and how it solves this, and that's because I wanted you to be familiar with the core concepts around CI before we dive in, so that's what we'll do in this lecture.
We're going to review the problem that CI solves and how it solves it. When we develop software, we're almost always under a deadline. We need to be delivering the software we create to end users as quickly as possible. And one way we go about doing this is to break down projects into small pieces of functionality and have different developers, or even different teams of developers, creating those different pieces. So, in order to move quickly, we spread out the work. Now this can be extremely useful, however, it also poses a problem because now we need to be able to bring all of these pieces together to form a completed piece of software, and the longer we wait to merge these pieces together, the greater the chances are that these pieces won't fit together well. There are a lot of potential problems that can arise here, and we're gonna take a look at just a few of them now.
One of the most common types of integration problem are merge conflicts. Merge conflicts happen when two people edit the same file in such a way that the changes each made would overwrite the other's changes. So if two developers edit the same file at the same time, then one of them is gonna merge their code into the mainline and they're gonna go on developing as if nothing's changed, and the second one will end up needing to deal with that because they'll need to make sure that they fix the conflict before they push their code, or else they'll leave the build in a broken state.
Another type of common integration issue is dependency conflicts. This means that developers make a change to one of the dependencies that a project uses in order to build their functionality and it breaks things for other developers. This can happen when a developer needs to upgrade a dependent library, maybe to fix a bug or to gain some new functionality, and the new version changes the way it does certain things that other developers are basing their code on. So the developer that upgraded the library gets the functionality they need, however they break the logic that other developers are expecting, and these types of problems are tough to locate, but good testing can help.
Next, we have logic conflicts. This is similar to dependency conflicts. When a developer bases some of their code on code from another developer and that code is changed in a way that changes the logic, then that can cause problems that will only be caught by testing or when someone sees the bug and reports it. It's not uncommon for a developer to refactor their own code. Maybe they're trying to make it more efficient or make the code more readable, or whatever the reason, if they change it in a way that changes the logic, it can cause things to break, and this is where good unit tests help out. Next up, we have build conflicts. Now this can happen when multiple developers merge their work, and each individually they work, but the project is left unable to build, and this has, in my experience, usually been caused by some sort of configuration file that didn't quite merge correctly.
Now, there are different types of integration issues that will arise, however, the ones we've just talked about give you an idea of some of the potential integration issues. The more time that passes between integrating changes, the bigger the problem tends to be. If a developer is basing their work on things that have already changed and they just didn't know because those changes haven't been integrated, then that can cause a lot of time refactoring code later on. Or if several teams have all edited some set of files, then someone needs to spend time fixing all of those merge conflicts when they finally merge those back into the mainline. So hopefully the problem is clear, and all this is what continuous integration was born to solve.
However, continuous integration needs a few things in order for it to work well, and we've talked about a few of those things. First, your code has to be under version control. Imagine trying to collaborate on even a small project without a central repository for your code. Developers would be overriding each other's changes, files would be added and removed, and work would just be lost.
So version control helps by having a central repository and it tracks every change to every file. So if something was deleted and it turns out you really needed it, you could bring it back easily. Or if a change happens that breaks things for everybody, you could just revert that change. And if two developers each changed the same file, version control can inform them of the merge conflict so they can resolve it right away. So version control is key. And next, we need an automated way to build and test our software. It's important to ensure that the project will build. Now the term build here is a bit contextual. Depending on the type of project you're working on, it could mean to compile a code, or it could mean to install all of its dependencies, or something similar. But the end result of the build process should be a completely runnable project. And just as the build process is important, so is the testing process.
Running unit tests on each commit helps to reduce logic conflicts and some dependency conflicts, and depending on the speed of our integration tests, they can either be run on each commit, if they're fast enough, or on some sort of regular schedule, and these integration tests will help to catch a lot of the potential issues that arise, especially problems with interaction between components. For example, if a change to your database API occurred, and it's causing problems in your code, that'll be caught here.
Now, once you have these prerequisites for your continuous integration process that we just talked about, that is version control and automated build and test mechanisms, then we can incorporate these into a continuous integration server. We looked at Jenkins in a previous lecture, however there are a lot of great options out there. Some of them are even hosted for you, which means you can focus on the build process and not on running insecure in your own CI server. Codeship and CircleCI are two great options here, and they're both hosted CI platforms.
Now continuous integration is more than just having your code built and tested for you. The goal is to ensure that the code in your mainline is always in a runnable state. This means that if the CI server reports that something is broken, you need to prioritize fixing things over doing new work. Now this isn't always easy. Being under a tight deadline and taking the time to fix a broken build can feel like it's detracting from real work. However, when the build is broken, we're all building off of a defective base, so you need to enforce the rule that keeping the build in a runnable state is the priority.
I want to wrap up the course by showing a simple change being made to some code, committing it, and pushing it to GitHub, and then watching Jenkins grab that code, build the project, and run our tests so we can see how a complete process works. I don't want to bore you by making you watch me write a bunch of code, so I've prewritten the code and I've just commented it out. We'll start by running the tests and ensuring that we're in a known working state. So I'll run the tests, and after a few seconds, we're gonna see that everything is working. There. Six tests were run and they've all run successfully. Now, I'm going to uncomment the test that I've already written for this demo. You may be wondering why I'm starting with a test, and that's because by writing the tests first, we'll know when our task is complete. For this demonstration, we're going to be successful once we have a URL that displays users and their bios on the screen.
Remember, this is just a simple demonstration to show the complete CI process, so the test here is not very comprehensive. Okay, now we need to run the tests so we can see that six tests pass and one will fail, and that's because we haven't actually implemented the functionality to make it pass. So, there we go. We can see that one has failed. Let's uncomment the view method, and we'll also uncomment our URL mapping, and now if I run the tests and give it a few seconds to prepare, we'll see that all the tests have run successfully. Okay, now that we have a working feature, the next step is to commit our changes to version control, and we'll do this with the git commit command and paste in our commit message. And once this is done, we can use the git push command to push the code to GitHub.
So now that our code is in version control, we can take a look at Jenkins and see that it will detect these changes. Once it detects the changes, it can run through the code in our pipeline that we saw in the intro to Jenkins. It will install any dependencies and run our tests, and it will make sure that we didn't break anything, and then it'll package up all of our code into a Debian installer package, and when everything has been run successfully, then our pipeline goes green to let us know that all went well. And that's our complete CI process.
I hope that this Introduction to Continuous Integration has been useful to you. If you're already practicing CI or you're trying to create a CI process, I'd love to hear from you on the Cloud Academy community forums. I'm curious to hear what's working for you, what isn't working, and what kind of projects you're all working on. If there are things about CI that you'd like to have covered further, let us know at email@example.com.
I hope to hear from you. I'm Ben Lambert. Thanks for watching.
Ben Lambert is a software engineer and was previously the lead author for DevOps and Microsoft Azure training content at Cloud Academy. His courses and learning paths covered Cloud Ecosystem technologies such as DC/OS, configuration management tools, and containers. As a software engineer, Ben’s experience includes building highly available web and mobile apps. When he’s not building software, he’s hiking, camping, or creating video games.