@@ -13,26 +13,28 @@ In this section, you will write, build, run, and share an app, the Docker way.
13
13
14
14
## Your development environment
15
15
16
- Normally if you were to start writing a Python app on your laptop, your first
17
- order of business would be to install a Python runtime onto your machine. But,
18
- that creates a situation where the environment on your machine has to be just so
19
- in order for your app to run as expected.
16
+ In the past, if you were to start writing a Python app, your first
17
+ order of business was to install a Python runtime onto your machine. But,
18
+ that creates a situation where the environment on your machine has to be just
19
+ so in order for your app to run as expected; ditto for the server that runs
20
+ your app.
20
21
21
- In Docker, you can just grab an image of Python runtime that is already set up,
22
- and use that as a base for creating your app . Then, your build can include the
23
- base Python image right alongside your app code, ensuring that your app and the
24
- runtime it needs to run all travel together.
22
+ With Docker, you can just grab a portable Python runtime as an image, no
23
+ installation necessary . Then, your build can include the base Python image
24
+ right alongside your app code, ensuring that your app, its dependencies, and the
25
+ runtime, all travel together.
25
26
26
- It's done with something called a Dockerfile.
27
+ These builds are configured with something called a ` Dockerfile ` .
27
28
28
29
## Your first Dockerfile
29
30
30
- Create a folder and put this file in it, with the name ` Dockerfile ` (no
31
- extension). This Dockerfile defines what goes on in the environment inside your
32
- container. Things are virtualized inside this environment, which is isolated
33
- from the rest of your system, so you have to map ports to the outside world, and
31
+ Create an empty directory and put this file in it, with the name ` Dockerfile ` .
32
+ ` Dockerfile ` will define what goes on in the environment inside your
33
+ container. Access to resources like networking interfaces and disk drives is
34
+ virtualized inside this environment, which is isolated from the rest of your
35
+ system, so you have to map ports to the outside world, and
34
36
be specific about what files you want to "copy in" to that environment. However,
35
- after doing that, you can expect that the build of your app with this
37
+ after doing that, you can expect that the build of your app defined in this
36
38
` Dockerfile ` will behave exactly the same wherever it runs.
37
39
38
40
{% gist johndmulhausen/c31813e076827178216b74e6a6f4a087 %}
@@ -41,34 +43,30 @@ This `Dockerfile` refers to a couple of things we haven't created yet, namely
41
43
` app.py ` and ` requirements.txt ` . We'll get there. But here's what this
42
44
` Dockerfile ` is saying:
43
45
44
- - Go get the base Python 2.7 runtime
46
+ - Download the official image of the Python 2.7 runtime and include it here.
45
47
- Create ` /app ` and set it as the current working directory inside the container
46
- - Copy the contents of my current directory ( on my machine) into ` /app ` (in this container image)
47
- - Install any Python packages that I list inside what is now ` /app/ requirements.txt` inside the container
48
- - Ensure that this container has port 80 open when it runs
48
+ - Copy the contents of the current directory on my machine into ` /app ` inside the container
49
+ - Install any Python packages that I list inside ` requirements.txt `
50
+ - Ensure that port 80 is exposed to the world outside this container
49
51
- Set an environment variable within this container named ` NAME ` to be the string ` World `
50
- - Finally, when the container runs, execute ` python ` and pass in what is now ` /app/app.py `
51
-
52
- This paradigm is how developing with Docker essentially works. Make a
53
- ` Dockerfile ` that includes the base image, grabs your code, installs
54
- dependencies, initializes variables, and runs the command.
52
+ - Finally, execute ` python ` and pass in ` app.py ` as the "entry point" command,
53
+ the default command that is executed at runtime.
55
54
56
55
### The app itself
57
56
58
- Grab these two files that were referred to in the above ` Dockerfile ` and place
59
- them together with ` Dockerfile ` , all in the same folder.
57
+ Grab these two files and place them in the same folder as ` Dockerfile ` .
60
58
61
59
{% gist johndmulhausen/074cc7f4c26a9a8f9164b20b22602ad7 %}
62
60
{% gist johndmulhausen/8728902faede400c057f3205392bb9a8 %}
63
61
64
- You're probably getting the picture by now. In ` Dockerfile ` we told the ` pip `
65
- package installer to install whatever was in ` requirements.txt ` , which we
66
- now see is the Flask and Redis libraries for Python. The app itself is going to
67
- print the environment variable of ` NAME ` , which we set as ` World ` , as well as
62
+ Now we see that the ` Dockerfile ` command ` pip install requirements.txt ` installs
63
+ the Flask and Redis libraries for Python. We can also see that app itself
64
+ prints the environment variable of ` NAME ` , which we set as ` World ` , as well as
68
65
the output of a call to ` socket.gethostname() ` , which the Docker runtime is
69
- going to answer with the container ID. Finally, because Redis isn't running
70
- (we've only installed the Python library), we should expect that the attempt to
71
- use it here will fail and show the error message.
66
+ going to answer with the container ID, which is sort of like the process ID for
67
+ an executable. Finally, because Redis isn't running
68
+ (as we've only installed the Python library, and not Redis itself), we should
69
+ expect that the attempt to use it here will fail and produce the error message.
72
70
73
71
## Build the App
74
72
@@ -77,24 +75,21 @@ That's it! You don't need to have installed Python or anything in
77
75
your system. It doesn't seem like you've really set up an environment with
78
76
Python and Flask, but you have. Let's build and run your app and prove it.
79
77
80
- Make sure you're in the directory where you saved the three files we've shown,
81
- and you've got everything.
78
+ 7Here's what ` ls ` should show:
82
79
83
80
``` shell
84
81
$ ls
85
82
Dockerfile app.py requirements.txt
86
83
```
87
84
88
85
Now run the build command. This creates a Docker image, which we're going to
89
- tag using ` -t ` so it has a friendly name, which you can use interchangeable
90
- with the image ID in commands.
86
+ tag using ` -t ` so it has a friendly name.
91
87
92
88
``` shell
93
- docker build -t " friendlyhello" .
89
+ docker build -t friendlyhello .
94
90
```
95
91
96
- In the output spew you can see everything defined in the ` Dockerfile ` happening,
97
- including the installation of the packages we specified in ` requirements.txt ` .
92
+ In the output spew you can see everything defined in the ` Dockerfile ` happening.
98
93
Where is your built image? It's in your machine's local Docker image registry.
99
94
Check it out:
100
95
@@ -106,22 +101,25 @@ friendlyhello latest 326387cea398 47 seconds ago
106
101
107
102
## Run the app
108
103
109
- We're going to run the app and route traffic from our machine's port 80 to the
110
- port 80 we exposed
104
+ Run the app, mapping our machine's port 4000 to the container's exposed port 80
105
+ using ` -p ` :
111
106
112
107
``` shell
113
- docker run -p 80 :80 friendlyhello
108
+ docker run -p 4000 :80 friendlyhello
114
109
```
115
110
116
111
You should see a notice that Python is serving your app at ` http://0.0.0.0:80 ` .
117
- You can go there, or just to ` http://localhost ` , and see your app, "Hello World"
118
- text, the container ID, and the Redis error message, all printed out in
119
- beautiful Times New Roman.
112
+ But that message coming from inside the container, which doesn't know you
113
+ actually want to access your app at: ` http://localhost:4000 ` . Go there, and
114
+ you'll see the "Hello World" text, the container ID, and the Redis error
115
+ message, all printed out in beautiful Times New Roman.
116
+
117
+ Hit ` CTRL+C ` in your terminal to quit.
120
118
121
- Hit ` CTRL+C ` and let's run the app in the background, in detached mode.
119
+ Now let's run the app in the background, in detached mode:
122
120
123
121
``` shell
124
- docker run -d -p 80 :80 friendlyhello
122
+ docker run -d -p 4000 :80 friendlyhello
125
123
```
126
124
127
125
You get a hash ID of the container instance and then are kicked back to your
@@ -133,25 +131,22 @@ CONTAINER ID IMAGE COMMAND CREATED
133
131
1fa4ab2cf395 friendlyhello " python app.py" 28 seconds ago Up 25 seconds
134
132
```
135
133
136
- You'll see that ` CONTAINER ID ` matches what's on ` http://localhost ` , if you
137
- refresh the browser page. You can't ` CTRL+C ` now, so let's kill the process this
138
- way. Use the value you see under ` CONTAINER ID ` :
134
+ You'll see that ` CONTAINER ID ` matches what's on ` http://localhost:4000 ` , if you
135
+ refresh the browser page. Now use ` docker stop ` to end the process, using
136
+ ` CONTAINER ID ` , like so :
139
137
140
138
``` shell
141
- docker kill (containerID)
139
+ docker stop 1fa4ab2cf395
142
140
```
143
141
144
- ## Share the App
145
-
146
- Now let's test how portable this app really is.
142
+ ## Share your image
147
143
148
- Sign up for Docker Hub at [ https:// hub.docker.com/ ] ( https://hub.docker.com/ ) .
144
+ Sign up a Docker account at [ hub.docker.com] ( https://hub.docker.com/ ) .
149
145
Make note of your username. We're going to use it in a couple commands.
150
146
151
147
Docker Hub is a public registry. A registry is a collection of accounts and
152
- their various repositories. A repository is a collection of assets associated
153
- with your account - like a GitHub repository, except the code is already built.
154
-
148
+ their various repositories. A repository is a collection of tagged images like a
149
+ GitHub repository, except the code is already built.
155
150
156
151
Log in your local machine to Docker Hub.
157
152
@@ -187,9 +182,40 @@ and run this command:
187
182
docker run YOURUSERNAME/YOURREPO:ARBITRARYTAG
188
183
```
189
184
185
+ > Note: If you don't specify the ` :ARBITRARYTAG ` portion of these commands,
186
+ the tag of ` :latest ` will be assumed, both when you build and when you run
187
+ images.
188
+
190
189
You'll see this stranger of a machine pull your image, along with Python and all
191
190
the dependencies from ` requirements.txt ` , and run your code. It all travels
192
191
together in a neat little package, and the new machine didn't have to install
193
192
anything but Docker to run it.
194
193
194
+ ## Recap and cheat sheet for images and containers
195
+
196
+ To recap: After calling ` docker run ` , you created and ran a container, based on
197
+ the image created when you called ` docker build ` . Images are defined in a
198
+ ` Dockerfile ` . A container is an instance of an image, and it has any package
199
+ installations, file writes, etc that happen after you call ` docker run ` and run
200
+ the app. And lastly, images are shared via a registry.
201
+
202
+ ``` shell
203
+ docker build -t friendlyname . # Create image using this directory's Dockerfile
204
+ docker run -p 4000:80 friendlyname # Run image "friendlyname" mapping port 4000 to 80
205
+ docker run -d -p 4000:80 friendlyname # Same thing, but in detached mode
206
+ docker ps # See a list of all running containers
207
+ docker stop < hash> # Gracefully stop the specified container
208
+ docker ps -a # See a list of all containers on this machine, even the ones not running
209
+ docker kill < hash> # Force shutdown of the specified container
210
+ docker rm < hash> # Remove the specified container from this machine
211
+ docker rm $( docker ps -a -q) # Remove all containers from this machine
212
+ docker images -a # Show all images that have been built or downloaded onto this machine
213
+ docker rmi < imagename> # Remove the specified image from this machine
214
+ docker rmi $( docker images -q) # Remove all images from this machine
215
+ docker login # Log in this CLI session using your Docker credentials (to Docker Hub by default)
216
+ docker tag < image> username/repository:tag # Tag <image> on your local machine for upload
217
+ docker push username/repository:tag # Upload tagged image to registry (Docker Hub by default)
218
+ docker run username/repository:tag # Run image from a registry (Docker Hub by default)
219
+ ```
220
+
195
221
[ On to "Getting Started, Part 3: Stateful, Multi-container Applications" >>] ( part3.md ) {: class="button darkblue-btn"}
0 commit comments