Exploring Dockerfiles

I’d like to continue the previous entry on Docker a little further. Last time we talked about the installation process & a little more, so this time we’re going to talk about the next part of getting started with Docker – writing a Dockerfile.

Here’s what we talked about last time, and with one odd little exception (why did I promise to talk about load testing…) we’re going to cover all these things!

So, next steps, make the container persistent – it isn’t yet, and play around with Dockerfiles, and just do a little more spying on the produced container itself & probably try to do some babby’s frist load testing things in there & spy on the container as a process without the box & all its processes within!

First let’s take a look at the Docker process we created last time. Just like at your native command line, docker commands all resemble low-level Linux commands, so just like you’d use ps to look at the processes running at any given time on your machine, you can use docker ps to see all the Docker processes it is managing at any given time. If you followed along last time you’ll see some that have been exited but which you don’t have access to – each time you run the docker run -it bash you get a new process. But the old ones are still there! The all flag will show us these Exited boxes with a docker ps -a.

rachel $ docker ps -a
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS                     PORTS               NAMES
b5de9583d7b3        fedora                     "bash"                   10 minutes ago      Exited (0) 3 seconds ago                       pedantic_morse
35192bfa05d4        images/cowsay-dockerfile   "/usr/games/cowsay *P"   2 hours ago         Exited (0) 2 hours ago                         gigantic_goldberg
a0e40d55125a        images/cowsayimage         "/usr/games/cowsay 'D"   3 hours ago         Exited (0) 3 hours ago                         jovial_mcnulty
d32381833772        debian                     "bash"                   3 hours ago         Exited (0) 3 hours ago                         cowsay

You’ll notice a few things, first that the names are a mix of adjective_noun, except one – the cowsay container example is from the excellent Using Docker where I’ve gained a lot of my recent Docker information. Their status is all Exited. Some of the container-specific commands are similar to the init.d service commands, like start, stop, and rm, so let’s start the desired container in that list up there. The container we’re going to start up is similar to the one we made before & is Fedora, though it is true that I only made it ~10m ago!

docker start pedantic_morse

So now the output of docker ps includes the container we just made. So how do we keep it? We commit it, just like with Git! Replace pedantic_morse with whatever name yours has been assigned beneath the NAMES column.

rachel $ docker commit pedantic_morse images/morse
sha256:b398fe28d7fd26a52e0947fc8eebb7614b8a8d6d19a5332359df167c9296c04f

So what we’ve done here is create an image from which we can create containers. images/morse is the image, pedantic_morse is the Docker process that we crafted it from. For every time we run the image images/morse, it creates a new Docker process, so at this point it’s still not persistent in ONE image, HOWEVER we can use this image to perform one-offs.

Clearly we’re not getting into the strength of Docker, yet. So now it’s time for a very basic Dockerfile. Just like Vagrantfile and Procfile & probably a few other similarly intended setup files, the D in Dockerfile is capitalized and there’s no extension to it, because remember – Linux doesn’t care about file extensions!

The main piece to know with Dockerfiles is that their syntax can be as minimal as you like, and personally I recommend making them non-complex – major structural pieces, and insert kickoff scripts or use some config management in the container itself for anything much more complicated. I reserve the right to change my mind on this later! And this is also more for next time to learn. But the way it looks, the RUN command will run any bash you put in it, but if you need anything more complex, the contents become a lot more murky, in my opinion. Simple is better than complex, but complex is better than complicated, so let’s do what we need to here.

For posterity and a simplistic example, here’s the first Dockerfile I ever wrote. (ed note: I trimmed this down because each line of a Dockerfile creates a new filesystem – try to truncate Dockerfile lines as much as possible)

FROM fedora:23
RUN /bin/bash
RUN echo "the dockerfile took!"

RUN dnf install -y wget tar man

MAINTAINER Rachel!

The output of this, which is a bit long to post, pulls down version 23 of Fedora, uses bash for the following commands, prints “the dockerfile took!” to stdout, and then installs those three packages. I’m unsure why some of those aren’t present in a base Fedora image, but it doesn’t appear to be related to what I’m working on in this blog post, so we’ll leave it be for now.

This is about ten times longer than I thought it would be, woohoo! I hope you learned something, please please let me know if I’ve missed the mark on anything, cheers!

Tune in next time and we’ll talk about a more complicated Dockerfile, and syncing it up to… something 🙂 come back and you’ll find out what!

Advertisements

Beachmeals

It’s been a month – the longest I’ve gone since I started this blog back in, hm, September 2013? About a month ago, my friends and I decided to rent a house for a weekend. There were nine of us, so I wrote a program to assign people randomly to each of the four meals we would be preparing that weekend – Saturday brunch, weekend snacks, Saturday dinner, and Sunday breakfast.

At first, and I knew this would not be my final draft, I made a loop to ask how many people would attend & another to ask all their names. Then, I made an empty list for each meal. Then I made a for loop to assign each person to a given meal, for the number of people attending. Each chunk of the code looked much like this:

appender = friendForIndex[-1]
brunch.append(appender)
print "adding guy above to brunch"
list.pop(friendForIndex)
newIndex = newIndex - 1
print "brunch folxxx: %s" % brunch

Later grawnkps (a very technical term, that) had an altered meal, so rather than brunch of course it would have breakfast or whatever.

It worked, which is fine, but you know me, I had to keep fiddling – 80-odd lines is too many for something that seems so much simpler than this! And since each grawnkp was essentially the same block of code over and over, well, obviously SOMETHING can be done about that!

So I set to re-writing. Another week or so of off-hours fiddling led to the final portion of the code as follows, a (sometimes) triply-nested conditional loop, that rather than needing one individual grawnkp for each meal, it iterates over a list of meals and picks the next one if the indexing value is over 0, and after each iteration it subtracts from the index within the bottom-most conditional, so it doesn’t iterate unnecessarily, which is what I was nervous about, but I did it right!! Check it out, I am quite proud. meals is a list of meals with brunch, snax, dinner, and breakfast as empty lists declared within. and shuffledFriends is a copy of the list of friends which has then been randomized:

k = len(shuffledFriends)
while k > 0:
	for j in meals:
		appender = shuffledFriends[-1]
		j.append(appender)
		list.pop(shuffledFriends)
		k = k - 1
		if k > 0:
			pass
		elif k <= 0:
			print "brunch fixers: %s \n" % brunch
			print "snax fixers: %s \n" % snax
			print 'dinner fixers: %s \n"; % dinner
			print "breakfast fixers: %s \n" % breakfast
			print "good job now make the food you dooks"
			exit(0)
		else:
			print "error"

ed: fixed tablature error with [ code language = “somelang!” ] codehere [ / code ]

So I am really excited! The next step for something like this could be to import a file with some of this information, but it works REALLY WELL for what I was trying to accomplish. Here’s the repo if you’re interested in how the whole thing fits together. The first version is the master branch in there so take a look if you’re so inclined, though like I said, it’s not nearly as flashy!

yahoo! & by way of an update I think I’m done with Learn Python the Hard Way. I have learned a lot & now am finally getting into some of my other projects.

Lesson 38

In this exercise, we play with splitting, adding to lists, & the join function, referred to in this stack overflow post as the inverse of the split function.

split cuts up any old text string, like mary had a little lamb and turns it into a formal list, like ["mary", "had", "a", "little", "lamb"], with each word made into an indexable (numerically anyway) item in a list, via the format of stringofwords.split(" "). I’m not toooootally square on the quotes in the formatting of the use of split, but I can punch it in well enough til I grok it fully.

join depends on the same structure of listofitems.join(" ") to de-string-ify it. Handy!

There’s a bit also with, hmm, how do you call it, positional list comprehension, maybe? For example in the previous given list, the ["mary", "had", "a", "little"} et cetera, position 0 is the very first one, mary, where position -1 is the very last, little.

This was a good one! I think the exercises will stay challenging through the next thirteen : )

Lesson 36

Lessons 32, 33, and 34 were list comprehension and conditionals like if/elif/else, for, and while, all of which I feel fairly comfortable with. Exercise 35 was an application of these ideas in the form of, woohoo, a text-based adventure game! This one came in a bit longer at 76 lines, which I honestly love copying in. He had a few handy tricks that he hadn’t talked about, like the exit(0) which kicks you out of the program, and a few clever nestings and conditionals, like the combination of the boolean in one of the “rooms” with a while and an if/else.

Rather than give each of those its own post, since I don’t have much to add having learned those functionalities long ago, I moved over them so I could advance a bit more quickly. In exercise 36, he asks for a unique game! So I made one! Woo-hoo! After a not too long trial and error period, I came up with the following game, of which the following is probably the most representative output that I got when I had someone else play it. Check the repo if you’re interested in the code! the output is more fun anyway : )

WELCOME TO THE DUNDJEL
you can go north and south. maybe other directions?
> north
You carry on your dumb way north, dummy
You went north! That's probably fine.
You see an A and a B, and also a button.
> a
yeah, you only have so many options, here.
nice that you think I'm smart enough to come
up with more stuff, but I haven't.
> b
yeah, you only have so many options, here.
nice that you think I'm smart enough to come
up with more stuff, but I haven't.
> A
consider all of your options my friend.
but not today, for today you die.
you lose. you see a howling hag:
SHE'S A WIIIIIITCH
BOOOOO. BOOOOOOOO!!

It makes me laugh which is really all that I’m looking for in my own text-based adventures. There IS a way to win, but it doesn’t look like they made it!

Hey, quick sidenote, have you seen Depression Quest? It’s eligible for Steam Greenlight, and it, well, my description won’t do it justice, the concept is amazing & you should just go take a look.