 Jeg er veldig glad at det er så mange i år. Jeg har aldri givet en tal i fronten av sånne paktene. Jeg er veldig glad til å være her. Det er min første gang på Fastem. Det er probably ikke det leste, men det har vært mye funnere. Jeg er glad til å ha en finne tal på dette stedet. Min navn er Stajen Inge. Jeg kommer fra Norge. Jeg arbeider for en konsultansk company som heter Beck. Det er ingen utgjørning for det, for vi har bare nødvendige kunder, så det er hvor jeg arbeider. Jeg har gitt ganske med meg med Cloud og AWS i telefon. Men jeg har gitt ganske med anspring på prensk projekter. Jeg har gitt ganske med dette tal mange år senere, men jeg har gitt ganske med dette tal på mange kunder. For det ser ut som at folk er veldig interessert i anspring. Det er en fantastisk tool for konfiguration management. For meg er jeg ganske interessert i samarbeid med samarbeid med samarbeid med samarbeid. Jeg har høytte et samarbeid med DevOps i Norge, som har vært en fantastisk success. Vi har også organiseret DevOps-dagen 2x i Norge. For meg, som Walter har sagt, er jeg ganske til å give en introduksjon til Ansible. Det er et eksempel som er ganske simpelt. Det er ikke omkring en tusen maskin i en cloud eller noe. Vi har ganske å bruke fyra maskiner. Denne setupet er ganske ganske utfordrende for en liten environment. Det er ingen kontainere. Det er ingen ny, fanskig cloud teknologi i dette. Det er en veldig god måte til å begynne server og begynne dem omkring. Vi er ikke i denne environmenten. Jeg vil også snakke at mye slags kan begynne online. Det er også en kod av alle eksempler. Og også en slags for en full tutorial. Vi kan gjøre dette tutorial i en halv dag eller en full dag. Du kan se slags og se ut denne sorgkodet. Og du kan gjøre alle disse tasker omkring. Jeg vil starte med en færdig eksempel. Hvis det er en bekymring. Det er mange ting, men det er at least de tre. Det er et tilgjent av server-setup. Det er konfiguration som kod. Du kod det, og så runner du det. Og det er ikke immunerende i seg selv. For det å være immunerende, så må du destrøye serveret og bygge everything again. Hvis du har en bekymring for å begynne noe, og du forgeter å begynne konfigurationen, du vil eventually have snowflake servers. Det er noen eksempler som du kan bruke det for. Du kan creere user på baksene og installe software. Du kan generere og manipule konfigurfilen på serveret. Start og stopp og restart processes. Og du kan sette up dependencies between operations that you want to do. Det er of course a lot of other things you can do with the provisioning framework. But these are some. There's a difference between imperative and declarative ways of provisioning, or running something to change the state on a server. You might do it this way, the imperative way. We write a bash script where you have to do all this kind of handling of unexpected things that can happen when you run the script. Her are installing vim. If it's already installed, we have to check for that first. We won't do it. And then we want to add a line, file indent off, inside this config file. And then you have to check if it's already there. And if something goes wrong, how would you roll back? You can easily see that this way of provisioning can turn into a nightmare. Because these scripts will get really, really big and really, really messy, and there will be a lot of bugs. It can guarantee you. Whereas a declarative way of provisioning servers like Ansible does, you describe a state that you want the machine to be in. And with the declarative way, you can ensure that the machines will be in the state that is described in your declarations. No need for writing rollback code, or error handling, or things like that. That's handled by the framework. So if you can run this a thousand times, first time it will install vim and insert the line, next time it just won't do anything. And if it crashes, it won't do anything either. So you fix your bug and write it again. Some pros. It's in source control, which is, of course, very nice. It's documentation. So code is the best documentation that you can get. All other documentation will be wrong. Refacturing is really easy. You can run your playbooks or your provisioning against servers. And if you want to change something, you add something, then you can do that, and the state will change. So it's easy to refactor. Get, of course, less differences between environments. It's deterministic, meaning that when you run it, you will know what state your machine will be in. It, of course, prevents manual steps, taking the human out of the equation. You know humans are always the problem when we have problems in software. They're always the reason why things go wrong. It's very fast and easy to configure up a new environment if you've done it once. Like we have done, I will use vagrant boxes here for testing my setup. And it's very easy for me to provision server in production. It's easier to test. And there's probably more cons. But it's, it's this, isn't really mutable. Which has bitten me quite many times. Because I'm writing these huge ansible configurations. And then I find out that no, I don't want to do this task. Then I delete it, I run the script again. Problem is that the state hasn't changed on the server. Because that's still there. If I install some software package and just remove the code and run it again, the software package will still be there. So that's a pretty large con and something that you have to think about. So if you install some software package, you have to also write the task to remove it if you want to remove it. And what's special about ansible is that it's SSH-based. All you need is SSH-client and SSH on your machine. It's client only, so no server. So you don't have to install anything on the server to make it work. Like for instance with puppet and chef and stuff. It uses YAML configuration. It pushes your changes to the servers. There's also a possibility for pulling from the servers too. Ansible has that. I've only used the push version. I like to run my playbooks against the servers and see what happens. Instead of something mysterious happening on my servers or something like sync into a state because I want to get notified or know straight away something goes wrong. It supports more than setup and provisioning. For instance, like I'm going to show you, application deployment and also remote command execution. So these days I work mainly with cloud and I use Terraform a lot for provisioning the infrastructure of my cloud. I still use ansible for things like configuration management on servers because ansible is better than Terraform on that. Like for instance configuring services, installing stuff. I can do that after I provision my infrastructure in the cloud or I can do it as a building of my images that I am on beforehand. Before I'm going to deploy new machines to the cloud. So I'll try to do a live demo too. I'm using vagrant and virtualbox. Virtualbox is the virtualization of the machines on my machine that act as servers. I use vagrant for provisioning those boxes. I've already run vagrant up because it takes some time. So if I run it again, it won't take that much time. So I bring up four boxes, a DB, two app servers and a proxy. That's what I need for our example. Let's have that just running in the background. I'll talk a little bit about the layout of ansible repositories. Ansible uses convention over configuration. So you shouldn't try to make your own structure of your source code if you do that, that will just be a lot of work. So just stick to how ansible recommends to structure your code and you'll be fairly well off. Otherwise you will have to write a lot of code to include stuff from everywhere and things like that. It has a general config file where you put some config that should apply for ansible and for all your runs of ansible. It has a hosts file where you specify all the hosts that you're going to provision. I'll have a look at that. It has a site YAML, which is your main playbook. Inansible, your scripts are called playbooks. That's your main playbook where you sort of tie everything together and that's what you run. You can specify variables in many different ways. Group vars would be like all application servers, all DB servers, variables for those. Whereas host vars is per host. So app server 1, app server 2, DB server 1, DB server 2. And you name those files after a group or a specific host. You can even use IP addresses if you want. And then you have roles where your playbooks that does things on your servers are located. You have files. The role has a name and that role is included by your site YAML file. You have files inside each role which are files that you want to copy to the remote servers. You have templates if you want to template files and write them to the server. You have handlers. Handlers are sort of things like restarting a service, for instance. For if you change some config on a service on your server, you want it to be restarted. Then you can call those handlers from your playbook. And tasks are what you want to perform on the servers and vars is variable specific for that special role. So let's try and run Ansible. So all my machines are up on my playbook like this. And this is also something that took some time. So I've already done this before. I'll do the rest of the tasks. I won't do it like this, but downloading Java and stuff took some time. You can see it's all green. Everything is okay on the boxes. Let's have a look at some of the files I mentioned. In the Ansible config we have general configuration like which hosts file should I use. What is my remote user? So who should I SSH into? Us and then su to root usually. Disable host key checking. Private key file you're using vagrant. It has like this insecure private key file that you use when you're testing stuff. Yeah, and some other things that you'll have to look up yourself. So my host file looks like this. I have only one db server, two app servers and one proxy. This is the name of the servers. This is the groups. Have a look at the site file. This is really simple for now. I'm only provisioning the app servers. So I'm saying that the hosts should be the group app servers. And the role is Java. So I'm just installing Java. And installing Java looks like this. I add an app repository and I use apt to install the Java JRE. The runtime environment. Ansible gathers facts from the servers that you provision. So that you can use a lot of information about the server. What's accessible on the server for determining what you want to do. If this is the case, then do this. If it is this Linux distro, run this playbook. You can find all sorts of facts about the servers. I'll show you. You can access those facts in your playbooks and also in templates that you write. So if you want to get to my p addresses for your config files or something, you can use facts. Let's see what that looks like. As you can see, you get all sorts of facts about the machines. There's just so much stuff here that may or may not be used. Go through all those. So now we're getting to the task. What we're going to do in this workshop or this presentation. We're going to create an app user called DevOps with a home directory and an SSH key, which is my public key on my machine. We're going to set up a database and use nginx as a reverse proxy and load balancer. We're going to install the Java application as a service. We're going to deploy this Java application that uses all this stuff that I have provisioned. So our final infrastructure will look like this. Ansible has really, really great documentation. So if you go to this URL, list of all modules, they have a lot of modules, and you'll get very good documentation with examples. So the first thing we're going to do is that simple. We're going to install Vim because Vi, I always type Vim. So it's really frustrating to go into server and I type Vim and I'll command not found. So I always install Vim, and all this code is structured in branches in Git, so it's quite easy to do the workshop just by checking out the branches, and you can see what goes on. This is how the playbook looks like. So this is basically what you saw in the beginning. And just to make sure that I'm not cheating, log on to one of the servers, see that file indent is not there, and then, oops, it's already there, actually. Must have used a newer distro. Anyway, you can see an option that I'm using, tags. And I tag all the tasks that have something to do with Vim, with Vim. As you can see here, I have tags, Vim, which is really useful if you've already provisioned a lot of stuff. You don't want to wait for a long time for it to check all the tasks. You just want to run the Vim tasks. To speed up things. It's up to such a mess. As you can see, Vim is already installed, but the file type indent off is not. So now you can see it. So we've installed software, and we've manipulated some files. Let's talk a little bit about variables in Ansible. It's a very good idea to use variables as much as you can, and don't hard code what could have been a variable inside your playbooks. That's because that's what we call infrastructure as data. Because if you have all the variables, all the data in variable files, then it's a lot easier to, for instance, migrate to a different provisioning framework. Because you have all your data, all your facts, nicely inside some configuration files, and not scattered around your playbooks. The question is, where should you define your variables? Because Ansible has very many options. As you can see here, you can define it in the hosts file, in the group files and host files, in your playbook. You can use facts, local facts or server facts, or you can also specify it on the command line. The way you access them is by double curly brackets and the name of the variable. My recommendation is to use as few of these possibilities as possible, and try to stick to one way of defining variables. There might be some reasons for using different techniques. For instance, if you have variables that are specific to one module or one role, then it might be a good idea to have it there. We'll use a little bit of both. The next task is to create the user. We want it to have a home directory, an SSH key, an SSH into the box, and we're going to use group bars. The first thing I do is that I create a file inside group bars called app servers, the same name as my group, and then I describe some variables. The name of the user, which group, the user name that I'm going to use as a comment for my user. My role looks like this. Ansible has tasks for creating a group and for creating a user. I'm using variables for those tasks. Let's try to run that, and that should be fairly quick. I limit it to the app servers. I don't want to run through all the other servers. I skip tags instead of including tags. I'm skipping app, Vim, and Java, and that was pretty fast. To see that I actually created the user, you can run this command. The vagrant SSH command that you saw me use earlier, like a built-in vagrant thing that lets you SSH in as the vagrant user. Now I'm using plain SSH. As you can see, I have the user. It has the SSH directory with my public key in it. So now we created the user, which we're going to use as an application user, which is going to run the application. Next thing we're going to do is to install and configure our database. In this task we're going to use groupbars all. If you have a file inside groupbars, which is called all, then it goes for all the groups in Ansible by convention. Or you could use groupbars DB servers. If you have all sometimes variables are useful for more than one group. For instance, a DB user and a DB password. I'm going to use a handler to restart Postgres. If I've changed some config between runs, I want it to restart the server. Let's check out Task 3. This is some variables, and here you can see that I specify the variables a little bit differently. Her I use key and values. Her my top level is Postgres, which is the key, and I have more variables under there. And the same with DB. I'll show you how that works. Her I'm also creating a template. Her you can see I'm using the variable db.name db.user and postgresql.address in my template. Her this is my handler. Specify a service. What name of that is. The state restarted. I can call that handler from my playbook. Like this. This just installs Postgres with apps. It copies a file, like the main Postgres config file. And then it uses a template, which I showed you. And then I write notify restart Postgres, which was the name of my handler. So every time this file changes, I notify this handler to restart Postgres. And the nice thing is, I also for this file, the copying of the file, if that's a new file, I also restart Postgres. But if both these tasks have changed, it will wait until the last task has finished, and then restart Postgres. So it will only restart it once, depending on what's changed. But I must ensure that it is started. Is it started, then this task will just pass. And I'm creating a database. Ansible has quite a few tasks for Postgres, MySQL, and other databases. So they have these tasks called PostgresqlDB, PostgresqlUser, and so on. Let's try to run that. For limited to the DB servers, I use the Postgres install tag. So it will just do that. That's the problem. I'll just continue while this runs. So I'll log on and log on to the database later. So the progress so far is that we've installed and configured a database and a database user. So now the next thing we're going to do is that we're going to deploy our application. We created a really small, not fancy Java application with no GUI. Well, it's got a GUI, actually. And then we're going to have a look at some of the files, because there's some stuff here that you need to know about. What I have is that I have a file, which is my service script. So I'm going to install this as a service. So that's actually just a basic service script that starts the Java process. And I have a template where my config, so here goes my database config, which database should I connect to, and so on. And then I'm going to explain about the Serial 1. The database is actually installed. Let's have a look at the site, YAML. You can see that I've checked out the next task, sorry. And then you can see that I've set on the app servers, I've set Serial 1. The reason for this is that Ansible usually does things in parallel. So if it can parallel its operations against more servers, it does that. And that's a good thing. But if you want to deploy applications without downtime, you have to take one application at a time. So I'm saying that it should only take one server at a time. You can also specify, like, do only 10 servers at a time, for instance. In this use case, we use one at a time. Let's have a quick look at Playbook 2. What I do is here is that I use the template of my config properties and place it in the home directory of the DevOps user. There's some credentials. And then I copy the application from the Hello World Java folder to the same destination. Then I'm going to explain the rest of the Playbook after I've run it. So you can see that it fails on this task. But I've actually ignored that because the first time, usually the script will stop if it fails. I want to tag the set ignore this error. And that's because the app hasn't been deployed before, so there's no previous version of this app. Because this special task, it gets the previous release version of the app. That happens for both servers. The app says I'm alive on app one. The DB says an exception occurred. There's no DB yet. So what just happened? On the server it will look like this. I've copied in the config properties file, or the template. Then I've set... This is the file that I uploaded, and I've simlinked to that with current. The reason it failed for setting the previous was that there was no previous application there. What it would have done is to set the other as previous. We'll do that in a minute. Her are the files, and I have some logs, and a service script inside for starting it as a server. That went well. I'll do it again, and then it won't fail, because then there is the previous version of the application there. It does it on one host at a time, and not in parallel. We've deployed the application to two of the app servers and enable it as a service. The next thing we're going to do is deploy the database schema. So that the app doesn't say error from the database. What I do is that I've created a really simple database migration script. Have a look at it, which just does this. It creates a schema called DevOps, and it creates a table called hello, and it inserts hello from database. So that's what's going to turn up in our app after we've run it. Here I'm using an Ansible task called command. In this task you should almost never use, because the command task, it just fires off the command, and Ansible has no way of knowing if it was successful or not. So you should use the other tasks that it has if you can, but sometimes like for instance running a script, if you can't do that with a different task, a proper task, then you have to use the command. You can also write some conditions for when a command should be successful or not, but it's just a big mess, so try to avoid using command. Let's try to deploy the database, which is really fast. Then hopefully DB says hello from database. We've deployed our application and it's working, but there's so much load on this page, so we have to load balance it. It's starting to get crazy. You can also log on to the server and use the command line client there, but we'll not do that, you can see that it works. So the last thing we're going to do is to set up the proxy. As you've seen, I've used templates, and the templating language that we are using is Jinja. Jinja has a lot of functionality that you can use to create quite advanced templates, like loops and stuff like that. I'll show you an example of that. Here is my load balancing code. It's an nginx config file. As you can see, I'm using a for loop. I'm looping over the app server group, and then I'm inserting the name of the app server, which port, and which port. That means I have two app servers, so we'll loop through that and add one config file for each server I have in that group. You can see it? See the for loop at the top there. I'll loop through the app servers. Thank you for letting me know. Stuff is quite straightforward. It installs and configures nginx. So there you have the... I hide it here too. Let's try to run that. The network on my phone is pretty slow, so... Right, so now it's installed nginx. Now our proxy should be on this address. As you can see, it's switching between the two servers. App 1 and app 2. Now we can... Now the load has gone halfway down. It's such a popular site that you'll probably set up more nodes soon. Just a short note about what makes it zero downtime. It's usually the problem with zero downtime deployment is the database, right? Migrating the database, that means that your old version of the application and your new version of your application might not be able to use the same schema. That's why we used the expand contract pattern. There's a lot of stuff that you can do that will make the application compatible both applications compatible with the same schema. Like adding tables, adding columns, tweaking indexes and stuff like that can use that before you deploy. And after both of them are deployed, you can remove a lot of stuff that you don't need anymore. So you can almost all the time do something before you deploy the applications, and then you can clean up after you've deployed it. So it looks a little bit like this. We pre-upgrade the DB with stuff that are compatible with both versions. You upgrade one of them. You upgrade the other. And then we post upgrade. Then we do the stuff that makes the applications incompatible after they've uploaded to the latest version. Ja, I'm actually done, but some suggestions for further investigation is use the expand contract pattern to change the table name of the database and deploy that without downtime and then implement rollback. That was all I had. Do you have time for questions? Can you do stuff in parallel tasks on the same servers in parallel? The answer would be no, you can't do that. You have to do it in sequence. I guess you could have different playbooks and have several runs at the same time, perhaps. That would maybe be a little bit. I could do that, then I had to write another task for doing that. But I could do that. I could check the output of the previous command and say that this is okay. I could do that. Best tasks, yes.