Python for Entrepreneurs Transcripts
Chapter: Deploying to the cloud
Lecture: The initial configuration
Login or
purchase this course
to watch this video and the rest of the course contents.
0:01
Hopefully you took a look through the Ansible documentation, but I found that the best way to learn Ansible and how to do automated deployments
0:09
is just to dig in and start writing your first playbook. The first playbook is going to be one that we only run when we spin up a new server,
0:16
the base Ubuntu image that we selected on Digital Ocean is not harded against attackers who might want to try to lock into your box.
0:23
The main security mechanism that's in place from the start is just that you can only log in with an ssh key, but there are many more steps
0:29
that we need to take in order to protect this box, because it is live with a public IP address on the internet.
0:36
Shift back over into your development environment, and either go into the course demos or move into your own project
0:42
you can start writing the deployment playbooks for your project as we go along through this chapter. Or if you don't have your project set up just yet,
0:51
you can follow along through the course demo. Within the course demo's git repository, switch into chapter 15, deployment,
0:56
there will be another folder within it, blue yellow app deployment. This is the same project that Michael was working on the previous chapter,
1:05
where he set up email, we're now going to take this application and deploy it on our Digital Ocean server.
1:12
The first step is to create a deployment directory, that will store all the files Ansible needs to execute,
1:18
create that directory, and then switch into it. If you haven't already activated your virtual environment, go ahead and do that now.
1:23
You can see mine is activated here, I just have one called entre, and it's also using Python 3. The first thing we want to do, is install Ansible.
1:31
Now typically we would just say pip install Ansible, but since Python 3 support is being actively worked on, in the development branch,
1:38
I recommend installing the development branch rather than the current stable release. As soon as Ansible cuts the 2.3 version
1:46
you can then install Ansible via Ansible 2.3. The way that we install from git, is we instead use the git protocol
1:54
pointed to the repository that we want to install, in our case that's going to be github.com/ansible/ansible.
2:02
Now this is going to clone the git repository, and install it into our virtual environment, in my case, I've already installed Ansible,
2:14
wait for it to finish in your own environment, and then we can proceed. Now that Ansible is installed, when you do pip freeze
2:21
it'll show us that Ansible 2.3.0 is installed, that's the minimum version that we're going to want.
2:27
With Ansible installed, we can now start to write our files. The first file that we need is a hosts file,
2:33
this is where we tell Ansible where our servers are located; open up hosts, file name hosts, under the deploy a directory in your editor.
2:41
In my case, I'm going to use Vim, and we're going to start this file off by naming init config, which is going to correspond
2:52
to the playbook that we're running. Next, we need to specify the IP address of our server. Now, if you don't remember what that is, you can shift over
3:01
into our droplet, click copy and just paste it in. If we had many servers we wanted to deploy too, they would all be listed here
3:10
but in our case we're only going to deploy to a single server. Next up, we need to do one more thing, which is we need to specify
3:16
the Ansible Python interpreter that we want to use when we connect to a remote machine.
3:21
Ansible is actually going to be interacting with the Python environment on each remote machine, so our Digital Ocean droplet
3:27
already has Python installed by default, but it's Python 3 in Ubuntu 16.04, that's the default installation and we want to explicitly specify that,
3:38
otherwise we'll encounter some errors down the road when Ansible is trying to work without remote environment. So type in ansible_Python_interpreter
3:47
and specify under user bin Python 3, save that. Alright, that's all we need to do right now for a host file we'll add to it later,
3:55
when we write our second playbook, this is all we need at the moment. Quit out of your editor, and that's our first file, hosts.
4:02
We're going to write a second file, and that file is init_config.yml. Open that up, and I typically start my files with a comment
4:11
very high level of what this playbook intends to accomplish. So in our case this is just going to lock down the server,
4:20
and set up a non-root user. So what's this non-root user? We're going to create a separate user named deployer
4:29
and we are only going to log into the server with deployer we're actually going to disable the root user as a log in account
4:34
the reason behind this is because many malicious actors will just try to connect to 04:36 servers as root, they won't try any other accounts, and even if they do
4:41
they need to know the username which really could be anything that we want it to be;
4:46
it's just one of the security precautions that we're taking as we set up our server.
4:49
Now we're going to specify which hosts we want to apply this configuration to, and we're just going to give this a name, here we'll just say
4:56
apply initial configuration to server, next up, we're going to specify the hosts,
5:02
now the hosts is going to apply based on what we've set up in our hosts file. And we can't say all here, and that will apply this configuration
5:09
to every single server that we've listed, but in our case, we're going to say init config. For this playbook our user is going to be root,
5:17
because we're going to log in as root as we lock down our server and later on we'll log in as a deployer user,
5:25
and the roles that we'll apply are just going to be init config. Now, the role is a directory that we're going to create
5:33
and that will contain other yaml files with tasks within them that are going to run when we execute this with the Ansible playbook command,
5:41
that's all we need for now in the initial config yaml file. We'll save, quit that, now we're going to create two subdirectories;
5:48
the first one is going to be group vars, this is the variables that we'll use, that are customized for our deployment,
5:56
and the second one is roles, so create those two directories, move into roles, and create a subdirectory init_config,
6:03
move into init_config, and then create a directory named tasks. This is the directory structure than Ansible is going to use
6:13
in order to kick off and execute the playbook. Within tasks, create a new file, main.yml
6:20
this is where we're going to write the playbook with specific tasks each step that we need to lock it on our server,
6:26
again, I always start with a brief comment, now let's write our first task. Again name is just a description of what we're going to do,
6:33
this first one is going to update and upgrade packages, and install a package named fail2ban.
6:41
Sometimes when I'm writing playbooks, I will actually just write out all the tasks
6:44
just to start, so I know what steps I need to take to flesh out the whole playbook, and that's what we'll do here;
6:50
so the first step is, we're going to install fail2ban, which is going to prevent anyone from continually trying to access the server
6:57
it will create a delay between when they try to log into the server and when they are allowed for their next attempt.
7:04
We can also use it to blacklist certain IP addresses, if they're constantly trying to attack our server.
7:09
Now the second step, we're going to create a non-root group so any user in this group is going to get what is called super user privileges
7:16
which allows them to make a system wide changes that are called privileged changes, which is escalated privilege required,
7:26
execute certain applications or access certain resources on a Linux system. After we create our non-root group, were going to create a non-root user,
7:35
this user will be added to that non-root group, we need to add an authorized key, which is the equivalent of what we did
7:41
when we specified our ssh key on Digital Ocean, after we have an authorized key, we will add the non-root group to super user privileges.
7:52
That will give our non-root user the ability to make all the changes that we need on the system, like restarting services,
7:59
installing new packages, those sorts of things. Next, we're going to disable the root ssh logins. That way, no one will be able to ssh in
8:14
by connecting to the root user, once we start ssh. One more step, we're going to make sure that no one can log in via password on ssh,
8:23
we only want to be able to log in via our private key, once we've made those two changes, the last step is to restart the ssh server.
8:31
So that's the high level of what our initial configuration is going to do on our server; now we need to fill in each one of these tasks.
8:40
For this task, we're going to use the apt module, the apt module takes a few parameters,
8:46
first is the name and this is going to be what package we want to install; in our case, fail2ban, we want it to be present,
8:54
so we want it to be installed on the system, and we want to make sure we update the system cash
9:01
so we don't get an old version of fail2ban installed on our system. And we'll specify yes to upgrade packages that are out of date.
9:11
Ok, so how do we know what the apt module does? This is a good time to go take a look at the modules index for Ansible.
9:17
Typically, if you just do a search, the first result that's going to pop up is the Ansible documentation, Ansible and then the package name,
9:23
and then the module name, so this documentation tells us how this module works, as well as any requirements,
9:29
so we're going to be running this with Python 3, and aptitude is installed by default on Ubuntu, that's the package manager for Ubuntu.
9:38
And these are the parameters that can be specified and in some cases we don't need to specify them,
9:43
so for example if we see that something is not required, it will either have a default, or it just won't apply when we run this task.
9:51
So there are a lot more advanced options that we can set here we've only used the basic ones because we're just installing a simple package;
9:57
this is why I keep the Ansible module documentation open because every single time I write a task I want to see
10:04
if there's some sort of additional option that I should be using as a part of writing that task.
10:09
Switch back over to our playbook, let's write our next task. We're going to use the group module in order to create a group
10:14
and the name of the group is going to be the value set by a variable. We haven't set any variables yet,
10:21
but this is what our group vars directory is going to contain, we're going to write a variable separate from our playbook
10:26
within the group vars directory, and one of those variables is going to be a deploy group, the name of the deploy group.
10:32
Now, this should look familiar, state present, because we did the same thing with the apt module
10:37
so what we're saying with this module is create a group named whatever our deployed group variable value is.
10:42
Now, if that variable is not set, Ansible will throw an error. In most cases, I do not set default values, because I actually want
10:48
Ansible to tell me when it's encountered an error. If you're writing a playbook in the future, what you can also do is
10:53
you can specify a default value with default, so in this case, we could say deployers,
11:01
and we need to make sure that we have single quote within the double quotes, and that we use deployers as the value, if deploy group is not set.
11:12
So it's just a handy tip for when you're working with Ansible, if you do want to set default values. In our case, we don't.
11:18
Next what we want to write is the user module, the name of the user is going to be set as deploy user,
11:25
the group will be the deploy group, we'll use the value of that variable
11:30
we'll set a default shell, I always like it to be bash rather than the default just ssh shell, we'll say we want this user to exist.
11:39
For some reason, we were writing a playbook where we wanted a user to be deleted or definitely not exist, we could say absent,
11:45
we need this user, so that's why we would say present. Next up, we use the authorized key module
11:51
and now we're going to set an authorized key for a user, the user is going to be the one that we have specified
11:56
as a value to our deploy user variable, state present again and note that in the previous task, particular present within quotes
12:04
now that shouldn't matter, but let's remove them here Ansible can get a little tricky with the quotes
12:11
but a general rule of thumb is that if you're using a variable you're going to want to surround that with quotes
12:16
because your variable may have spaces or special characters in the value, if we know because we're hardcoding something like present or absent
12:22
that it won't have special characters or spaces we don't need the quotes, so I'll typically leave them out.
12:28
However, spaces and special characters can mess up Ansible's parser which is parsing the yaml, so if you want to be on the safe side
12:35
you can use quotes around everything in your playbook and the only one I don't seem to have trouble with with spaces is the name task.
12:42
So again, typically I won't use quotes around the value to a name. Alright, let's finish up the authorized key, we need one more bit here,
12:50
we need to specify where the key is located on our local system, so that it can be uploaded to the remote machine,
12:56
so we'll use the key argument and then we're going to look for a file we're going to do a lookup for a file, we're going to use another variable
13:03
we'll set ssh dir and we'll add the name of our key which in our case was do deploy, and we particularly want the public key.
13:11
Now real quick, we could leave this and it's probably better to leave this as a variable, so there's multiple ways that we can write our Ansible tasks
13:20
so maybe the whole idea is we want these to be maintainable over time and variables help us to keep our playbooks maintainable.
13:27
So instead of hardcoding do deploy as our name, let's call it ssh key and we'll add the .pub at the end; we'll say ssh key name
13:35
and then we'll make sure that we create a variable for that later. Alright, we're getting pretty close to being able to run this,
13:41
let's fill out the remaining four tasks. The next two tasks we're going to modify the ssh daemon configuration,
13:48
we're going to use another module named lineinfile which allows us to replace certain lines in files
13:55
the destination of this is going to be on our remote machine, and actually, in this case we're going to be modifying
14:01
the Super User, the sudoers file, we need to modify it so that anyone in our deploy group gets Super User privileges when they use the sudo command.
14:11
The state should be present, we use a regular expression to define that we want the deploy group in this file
14:20
so what we're saying here is, if the value of our deploy group is in the file you can skip this step or Ansible can skip this step
14:29
but if it's not located in the file, we want to add a line, and that line is the value of the deploy group, and then some text that specifies
14:38
that the deploy group is getting all privileges. Finally, we'll validate the file, to make sure we didn't mess anything up.
14:54
Next, we're going to use the replace module the file here, it's going to be our ssh server configuration, we'll again use the regular expression
15:08
and if the file says permit root login, then we want to change that
15:12
and replace it with permit root login, no back up- we don't need to back up the file. Let's copy this, next disable ssh logins by password,
15:23
password authentication- yes, it should be replaced with password authentication- no.
15:35
Last task, we just want to restart the ssh service, so we'll use the service module the name of the service is ssh, and state should be restarted.
15:47
Cool, that's our playbook, save it and now we just need one more file to declare our variables, and we'll be able to start executing this.
15:55
So go into the group vars file, and we'll just create a file named all. So this is just going to contain all of our variables for our deployment.
16:07
And if your deployment gets really complicated, you can create many variable files I typically don't have so many variables for single applications
16:13
that I just store them all in one file for convenience. Alright, so there are four variables that we defined in that init_config playbook.
16:20
The first one was deploy group, I'll just say deployers, the second one is deploy user, let's call this deployer;
16:29
The third one is the directory that stores our ssh keys, I just stored the do deploy under my home directory, and then the ssh keyname do deploy.
16:46
That should do it, save the file, and let's see if we can execute this. In order to execute our playbook,
16:55
we're going to use the Ansible playbook command we have to specify a few arguments here, and rather than just typing this over and over again
17:05
what I typically do is I just store them in a shell file, so this is going to be a bash shell script,
17:11
and we are going to use the Ansible playbook command now -vvvv says give me very verbose output sometimes you're going to want that, sometimes not,
17:21
especially when I'm writing a playbook, executing for the first few times I want to see all of the output, just in case I run into any errors
17:27
I want to know immediately what those errors are, so I can fix them. We're going to use the init_config yaml file that we created,
17:34
we're going to specify a private key that is stored under our home directory, do deploy
17:40
these will all be root, they were connecting their own machine with and we're going to use our hosts file,
17:46
save that, let me try that, in order to execute the shell script you need to change its permissions
17:51
so 0700, on init.sh, now if we look at this init.sh file, it has the x for executable, let's kick it off,
18:02
oh no, of course, the first thing we run into is one error so let's modify this and what actually the problem is
18:14
is that it's a private -key, alright, one more time okay so this is typically what you're going to find
18:25
when you work with Ansible and you are just running it for the very first time so let's work through a few of the issues that I ran into.
18:32
This is the verbose output, and this is a good opportunity to just kind of understand the errors we may run into, so what's the problem here?
18:39
What it's telling us is that it couldn't find this public key, now at first this is a little bit confusing, this is definitely our public key,
18:47
but what I realize just by looking at it is this appears incorrect, so we don't want to specify the relative path,
18:55
which we did with a period before the forward slash we just want to specify the absolute path, so let's go ahead and modify this init.sh again
19:04
and in our case it's going to be just home/matt/do_deploy and we'll save this, all right, one more time- awesome, so now it was able to find the file
19:21
but again, we ran into an error, so let's take a look at what this error message is telling us.
19:26
Well, it looks like there's a problem with one of our tasks, now when you're working with Ansible, and you're writing a new playbook
19:32
you can't expect everything to go right, just from the beginning; that's why I think it's really important just to dig into
19:37
what are the errors that are occurring, and not be afraid of them, because the great part is that we're actually getting some midi information here,
19:43
that we may have used two arguments based on what we're seeing here and they're mutually exclusive, you can't put both of them in there at once
19:52
in our case we use both package and upgrade at the same time, now that may not be necessary, so let's go into our roles,
19:59
init_config, tasks, modify_main.yml. So the issue here is we are not actually going to use this upgrade
20:07
we just want to make sure that the fail2ban package is present upgrade is used when we actually want to upgrade existing packages that are there.
20:14
So we'll remove that, save it and now try to run it again. A whole bunch of blue output, that's good so far,
20:27
it looks like we may have gotten past the point that we had earlier and let's let Ansible do its thing.
20:37
Alright, so we got even further, it doesn't quite look like everything worked out all right, one more error that we ran into,
20:44
this one looks fairly easy and straightforward, it looks like the period is missing from deploy.pub
20:50
so it wasn't able to use, to play pub as an authorized key, so this typically happens when you're working with variables
20:58
so if we take a look at what's under our variables file, we're going to see that the ssh key name is do deploy
21:05
but there's no period after it, so if we go under our tasks, and take a look at what we have here, it looks like we use the name
21:15
and then we appended pub but no period after that, so we can save that, and once again, we'll kick it off.
21:21
And this is a fairly standard workflow for how you're going to work with Ansible,
21:25
and once you got the scripts written, you can run them over and over again, it's going to automate your whole deployment
21:31
but there is a little bit of work and just getting it automated to begin with.
21:34
Let's kick this off again, okay, amazing, so it looks like everything worked just fine.
21:39
Now, if it actually did work we should be able to connect to the remote machine as a check with this new deployer user, let's try that right now.
21:48
So we're going to ssh in, and we're going to specify the do deploy key and this time, we're going to use deployer user name as opposed to root,
21:58
and we'll copy and paste our IP address, and let's give this a try- yes, we are online and now our server is more locked down
22:12
because no one can log in with the root user name, and let's just test that out real quick;
22:17
we know we could do it earlier, because we were able to log in but now let's try to log in with the root user.
22:22
No, unable to do so, and it's just going to give a blanked denial which is permission denied due to a public key.
22:30
Really that's our ssh configuration, that is preventing us from logging in via root. With that, we've written our first Ansible playbook,
22:39
this is a playbook that you're going to want to run whenever you spin up a new Digital Ocean instance,
22:44
it's going to lock down create a new user for you, that can handle the rest of the deployment, and now are ready to install our web application
22:52
and make sure everything is up and running.