Categories
Posts in this category
- Automating Deployments: A New Year and a Plan
- Automating Deployments: Why bother?
- Automating Deployments: Simplistic Deployment with Git and Bash
- Automating Deployments: Building Debian Packages
- Automating Deployments: Debian Packaging for an Example Project
- Automating Deployments: Distributing Debian Packages with Aptly
- Automating Deployments: Installing Packages
- Automating Deployments: 3+ Environments
- Architecture of a Deployment System
- Introducing Go Continuous Delivery
- Technology for automating deployments: the agony of choice
- Automating Deployments: New Website, Community
- Continuous Delivery for Libraries?
- Managing State in a Continuous Delivery Pipeline
- Automating Deployments: Building in the Pipeline
- Automating Deployments: Version Recycling Considered Harmful
- Automating Deployments: Stage 2: Uploading
- Automating Deployments: Installation in the Pipeline
- Automating Deployments: Pipeline Templates in GoCD
- Automatically Deploying Specific Versions
- Story Time: Rollbacks Saved the Day
- Automated Deployments: Unit Testing
- Automating Deployments: Smoke Testing and Rolling Upgrades
- Automating Deployments and Configuration Management
- Ansible: A Primer
- Continuous Delivery and Security
- Continuous Delivery on your Laptop
- Moritz on Continuous Discussions (#c9d9)
- Git Flow vs. Continuous Delivery
Mon, 02 May 2016
Automating Deployments: Installation in the Pipeline
Permanent link
As [mentioned before](perlgeek.de/blog-en/automating-deployments/2016-007-installing-packages.html), my tool of choice for automating package installation is [ansible](https://deploybook.com/resources).
The first step is to create an inventory file for ansible. In a real deployment setting, this would contain the hostnames to deploy to. For the sake of this project I just have a test setup consisting of virtual machines managed by vagrant, which leads to a somewhat unusual ansible configuration.
That's the ansible.cfg
:
[defaults]
remote_user = vagrant
host_key_checking = False
And the inventory file called testing
for the testing environment:
[web]
testserver ansible_ssh_host=127.0.0.1 ansible_ssh_port=2345
(The host is localhost here, because I run a vagrant setup to test the pipeline; In a real setting, it would just be the hostname of your test machine).
All code and configuration goes to version control, I created an ansible
directory in the deployment-utils
repo and dumped the files there.
Finally I copied the ssh private key (from vagrant ssh-config
) to
/var/go/.ssh/id_rsa
, adjusted the owner to user go
, and was ready to go.
Plugging it into GoCD
Automatically installing a newly built package through GoCD in the testing environment is just another stage away:
<stage name="deploy-testing">
<jobs>
<job name="deploy-testing">
<tasks>
<exec command="ansible" workingdir="deployment-utils/ansible/">
<arg>--sudo</arg>
<arg>--inventory-file=testing</arg>
<arg>web</arg>
<arg>-m</arg>
<arg>apt</arg>
<arg>-a</arg>
<arg>name=package-info state=latest update_cache=yes</arg>
<runif status="passed" />
</exec>
</tasks>
</job>
</jobs>
</stage>
The central part is an invocation of ansible
in the newly created directory
of the deployment--utils
repository.
Results
To run the new stage, either trigger a complete run of the pipeline by hitting the "play" triangle in the pipeline overview in web frontend, or do a manual trigger of that one stage in the pipe history view.
You can log in on the target machine to check if the package was successfully installed:
vagrant@debian-jessie:~$ dpkg -l package-info
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-============-============-=================================
ii package-info 0.1-0.7.1 all Web service for getting a list of
and verify that the service is running:
vagrant@debian-jessie:~$ systemctl status package-info
● package-info.service - Package installation information via http
Loaded: loaded (/lib/systemd/system/package-info.service; static)
Active: active (running) since Sun 2016-03-27 13:15:41 GMT; 4h 6min ago
Process: 4439 ExecStop=/usr/bin/hypnotoad -s /usr/lib/package-info/package-info (code=exited, status=0/SUCCESS)
Main PID: 4442 (/usr/lib/packag)
CGroup: /system.slice/package-info.service
├─4442 /usr/lib/package-info/package-info
├─4445 /usr/lib/package-info/package-info
├─4446 /usr/lib/package-info/package-info
├─4447 /usr/lib/package-info/package-info
└─4448 /usr/lib/package-info/package-info
and check that it responds on port 8080, as it's supposed to:
vagrant@debian-jessie:~$ curl http://127.0.0.1:8080/|head -n 7
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============================-===========================-============-===============================================================================
ii acl 2.2.52-2 amd64 Access control list utilities
ii acpi 1.7-1 amd64 displays information on ACPI devices
curl: (23) Failed writing body (2877 != 16384)
The last line is simply curl complaining that it can't write the full output,
due to the pipe to head
exiting too early to receive all the contents. We can safely ignore that.
Going All the Way to Production
Uploading and deploying to production works the same as with the testing
environment. So all that's needed is to duplicate the configuration of the
last two pipelines, replace every occurrence of testing
with pproduction
,
and add a manual approval button, so that production deployment remains a
conscious decision:
<stage name="upload-production">
<approval type="manual" />
<jobs>
<job name="upload-production">
<tasks>
<fetchartifact pipeline="" stage="build" job="build-deb" srcdir="package-info">
<runif status="passed" />
</fetchartifact>
<exec command="/bin/bash">
<arg>-c</arg>
<arg>deployment-utils/add-package production jessie package-info_*.deb</arg>
</exec>
</tasks>
<resources>
<resource>aptly</resource>
</resources>
</job>
</jobs>
</stage>
<stage name="deploy-production">
<jobs>
<job name="deploy-production">
<tasks>
<exec command="ansible" workingdir="deployment-utils/ansible/">
<arg>--sudo</arg>
<arg>--inventory-file=production</arg>
<arg>web</arg>
<arg>-m</arg>
<arg>apt</arg>
<arg>-a</arg>
<arg>name=package-info state=latest update_cache=yes</arg>
<runif status="passed" />
</exec>
</tasks>
</job>
</jobs>
</stage>
The only real news here is the second line:
<approval type="manual" />
which makes GoCD only proceed to this stage when somebody clicks the approval arrow in the web interface.
You also need to fill out the inventory file called production
with the list
of your server or servers.
Achievement Unlocked: Basic Continuous Delivery
Let's recap, the pipeline
- is triggered automatically from commits in the source code
- automatically builds a Debian package from each commit
- uploads it to a repository for the testing environment
- automatically installs it in the testing environment
- upon manual approval, uploads it to a repository for the production environment
- ... and automatically installs the new version in production.
So the basic framework for Continuous Delivery is in place.
Wow, that escalated quickly.
Missing Pieces
Of course, there's lots to be done before we can call this a fully-fledged Continuous Delivery pipeline:
- Automatic testing
- Generalization to other software
- version pinning (always installing the correct version, not the newest one).
- Rollbacks
- Data migration
But even as is, the pipeline can provide quite some time savings and shortened feedback cycles. The manual approval before production deployment is a good hook for manual tasks, such as manual tests.
I'm writing a book on automating deployments. If this topic interests you, please sign up for the Automating Deployments newsletter. It will keep you informed about automating and continuous deployments. It also helps me to gauge interest in this project, and your feedback can shape the course it takes.