get-started: refresh python lang specfic guide (#17852)

* update python lang spec guide

Signed-off-by: Craig Osterhout <craig.osterhout@docker.com>
This commit is contained in:
Craig Osterhout 2023-08-09 14:48:19 -07:00 committed by GitHub
parent b2972b1ee4
commit bec47fdcd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 282 additions and 344 deletions

View File

@ -1,61 +1,44 @@
---
title: "Build your Python image"
keywords: python, build, images, dockerfile
description: Learn how to build your first Docker image by writing a Dockerfile
description: Learn how to build an image of your Python application
---
{% include_relative nav.html selected="1" %}
## Prerequisites
* You understand basic [Docker concepts](../../get-started/overview.md).
* You're familiar with the [Dockerfile format](../../build/building/packaging.md#dockerfile).
* You have [enabled BuildKit](../../build/buildkit/index.md#getting-started)
on your machine.
* You have installed the latest version of [Docker Desktop](../../get-docker.md).
* You have completed the walkthroughs in the Docker Desktop [Learning Center](../../desktop/get-started.md) to learn about Docker concepts.
* You have a [git client](https://git-scm.com/downloads). The examples in this section use a command-line based git client, but you can use any client.
## Overview
Now that you have a good overview of containers and the Docker platform, lets take a look at building your first image. An image includes everything needed to run an application - the code or binary, runtime, dependencies, and any other file system objects required.
To complete this tutorial, you need the following:
- Python version 3.8 or later. [Download Python](https://www.python.org/downloads/){: target="_blank" rel="noopener" class="_"}
- Docker running locally. Follow the instructions to [download and install Docker](../../desktop/index.md)
- An IDE or a text editor to edit files. We recommend using [Visual Studio Code](https://code.visualstudio.com/Download){: target="_blank" rel="noopener" class="_"}.
This guide walks you through building your first Python image. An image
includes everything needed to run an application - the code or binary, runtime,
dependencies, and any other file system objects required.
## Sample application
The sample application uses the popular [Flask](https://flask.palletsprojects.com/) framework.
The sample application uses the popular [Flask](https://flask.palletsprojects.com/){: target="_blank" rel="noopener" class="_"} framework.
Create a directory on your local machine named `python-docker` and follow the steps below to activate a Python virtual environment, install Flask as a dependency, and create a Python code file.
Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository:
```console
$ git clone https://github.com/docker/python-docker
```
## Test the application without Docker (optional)
You can test the application locally without Docker before you continue building and running the application with Docker. This section requires you to have Python 3.11 or later installed on your machine. Download and install [Python](https://www.python.org/downloads/){: target="_blank" rel="noopener" class="_"}.
Open your terminal and navigate to the working directory you created. Create an environment, install the dependencies, and start the application to make sure its running.
```console
$ cd /path/to/python-docker
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python3 -m pip install Flask
(.venv) $ python3 -m pip freeze > requirements.txt
(.venv) $ touch app.py
```
Add code to handle simple web requests. Open the `python-docker` directory in your favorite IDE and enter the following code into the `app.py` file.
```python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, Docker!'
```
## Test the application
Start the application and make sure its running. Open your terminal and navigate to the working directory you created.
```console
$ cd /path/to/python-docker
$ source .venv/bin/activate
(.venv) $ python3 -m pip install -r requirements.txt
(.venv) $ python3 -m flask run
```
@ -69,87 +52,42 @@ Switch back to the terminal where the server is running and you should see the f
## Create a Dockerfile for Python
Now that the application is running, you can create a Dockerfile from it.
Now that you have an application, you can use `docker init` to create a Dockerfile for it. Inside the `python-docker` directory, run the `docker init` command. Refer to the following example to answer the prompts from `docker init`.
Inside the `python-docker` directory create a `Dockerfile` and add a line that tells Docker what base image to use for the application.
```console
$ docker init
Welcome to the Docker Init CLI!
```dockerfile
# syntax=docker/dockerfile:1
This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
FROM python:3.8-slim-buster
Let's get started!
? What application platform does your project use? Python
? What version of Python do you want to use? 3.11.4
? What port do you want your app to listen on? 5000
? What is the command to run your app? python3 -m flask run --host=0.0.0.0
```
Docker images can inherit from other images. Therefore, instead of creating your own base image, you can use the official Python image that has all the tools and packages needed to run a Python application.
You should now have the following 3 new files in your `python-docker`
directory:
- Dockerfile
- `.dockerignore`
- `compose.yaml`
> **Note**
>
> To learn more about creating your own base images, see [Creating base images](../../build/building/base-images.md).
The Dockerfile is used to build the image. Open the Dockerfile
in your favorite IDE or text editor and see what it contains. To learn more
about Dockerfiles, see the [Dockerfile reference](../../engine/reference/builder.md).
To make things easier when running the remaining commands, create a working directory. This instructs Docker to use this path as the default location for all subsequent commands. This means you can use relative file paths based on the working directory instead of full file paths.
## .dockerignore file
```dockerfile
WORKDIR /app
```
Usually, the first thing you do with a project written in Python is to install `pip` packages to ensure the application has all its dependencies installed.
Before running `pip3 install`, you need the `requirements.txt` file into the image. Use the `COPY` command to do this.
The `COPY` command takes two parameters. The first parameter tells Docker what file(s) you would like to copy into the image. The second parameter tells Docker where to copy that file(s) to. For this example, copy the `requirements.txt` file into the working directory `/app`.
```dockerfile
COPY requirements.txt requirements.txt
```
With the `requirements.txt` file inside the image, you can use the `RUN` command to run `pip3 install`. This works exactly the same as running `pip3 install` locally on a machine, but this time pip installs the modules into the image.
```dockerfile
RUN pip3 install -r requirements.txt
```
At this point, you have an image based on Python version 3.8 and have installed the dependencies. The next step is to add the source code into the image. Use the `COPY` command as with the `requirements.txt` file. This `COPY` command takes all the files located in the current directory and copies them into the image.
```dockerfile
COPY . .
```
Now, tell Docker what command to run when the image is executed inside a container using the `CMD` command. Note that you need to make the application externally visible (i.e. from outside the container) by specifying `--host=0.0.0.0`.
```dockerfile
CMD ["python3", "-m" , "flask", "run", "--host=0.0.0.0"]
```
Here's the complete Dockerfile.
```dockerfile
# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD ["python3", "-m" , "flask", "run", "--host=0.0.0.0"]
```
### Directory structure
Just to recap, you created a directory on your local machine called `python-docker` and created a simple Python application using the Flask framework. You used the `requirements.txt` file to gather requirements, and created a Dockerfile containing the commands to build an image. The Python application directory structure should now look like the following:
```shell
python-docker
|____ app.py
|____ requirements.txt
|____ Dockerfile
```
When you run `docker init`, it also creates a [`.dockerignore`](../../engine/reference/builder.md#dockerignore-file) file. Use the `.dockerignore` file to specify patterns and paths that you don't want copied into the image in order to keep the image as small as possible. Open up the `.dockerignore` file in your favorite IDE or text editor and see what's inside already.
## Build an image
Now that youve created the Dockerfile, lets build the image. To do this, use the `docker build` command. The `docker build` command builds Docker images from a Dockerfile and a “context”. A builds context is the set of files located in the specified PATH or URL. The Docker build process can access any of the files located in this context.
Now that youve created the Dockerfile, you can build the image. To do this, use the `docker build` command. The `docker build` command builds Docker images from a Dockerfile and a build context. A build context is the set of files that the build has access to.
The build command optionally takes a `--tag` flag. The tag sets the name of the image and an optional tag in the format `name:tag`. Leave off the optional `tag` for now to help simplify things. If you don't pass a tag, Docker uses “latest” as its default tag.
@ -157,23 +95,29 @@ Build the Docker image.
```console
$ docker build --tag python-docker .
[+] Building 2.7s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 203B
```
You should get output similar to the following.
```console
[+] Building 1.3s (12/12) FINISHED
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/python:3.8-slim-buster
=> [1/6] FROM docker.io/library/python:3.8-slim-buster
=> => transferring context: 680B
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 1.59kB
=> resolve image config for docker.io/docker/dockerfile:1
=> CACHED docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4
=> [internal] load metadata for docker.io/library/python:3.11.4-slim
=> [base 1/5] FROM docker.io/library/python:3.11.4-slim@sha256:36b544be6e796eb5caa0bf1ab75a17d2e20211cad7f66f04f
=> [internal] load build context
=> => transferring context: 953B
=> CACHED [2/6] WORKDIR /app
=> [3/6] COPY requirements.txt requirements.txt
=> [4/6] RUN pip3 install -r requirements.txt
=> [5/6] COPY . .
=> [6/6] CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0"]
=> => transferring context: 63B
=> CACHED [base 2/5] WORKDIR /app
=> CACHED [base 3/5] RUN adduser --disabled-password --gecos "" --home "/nonexistent" --shell
=> CACHED [base 4/5] RUN --mount=type=cache,target=/root/.cache/pip --mount=type=bind,source=requirements.tx
=> CACHED [base 5/5] COPY . .
=> exporting to image
=> => exporting layers
=> => writing image sha256:8cae92a8fbd6d091ce687b71b31252056944b09760438905b726625831564c4c
=> => writing image sha256:37f9294069a95e5b34bb9e9035c6ea6ad16657818207c9d0dc73594f70144ce4
=> => naming to docker.io/library/python-docker
```
@ -195,7 +139,7 @@ You should see at least one image listed, including the image you just built `py
As mentioned earlier, an image name is made up of slash-separated name components. Name components may contain lowercase letters, digits, and separators. A separator can include a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.
An image is made up of a manifest and a list of layers. Don't worry too much about manifests and layers at this point other than a “tag” points to a combination of these artifacts. You can have multiple tags for an image. Lets create a second tag for the image you built and take a look at its layers.
An image is made up of a manifest and a list of layers. Don't worry too much about manifests and layers at this point other than a “tag” points to a combination of these artifacts. You can have multiple tags for an image. Create a second tag for the image you built and take a look at its layers.
To create a new tag for the image you built, run the following command.
@ -212,7 +156,7 @@ $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
python-docker latest 8cae92a8fbd6 4 minutes ago 123MB
python-docker v1.0.0 8cae92a8fbd6 4 minutes ago 123MB
python 3.8-slim-buster be5d294735c6 9 days ago 113MB
...
```
You can see that two images start with `python-docker`. You know they're the same image because if you take a look at the `IMAGE ID` column, you can see that the values are the same for the two images.
@ -230,15 +174,24 @@ Note that the response from Docker tells you that Docker didn't remove the image
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
python-docker latest 8cae92a8fbd6 6 minutes ago 123MB
python 3.8-slim-buster be5d294735c6 9 days ago 113MB
...
```
Docker removed the image tagged with `:v1.0.0`, but the `python-docker:latest` tag is available on your machine.
## Summary
This section showed how you can use `docker init` to create a Dockerfile and .dockerignore file for a Python application. It then showed you how to build an image. And finally, it showed you how to tag an image and list all images.
Related information:
- [Dockerfile reference](../../engine/reference/builder.md)
- [.dockerignore file reference](../../engine/reference/builder.md#dockerignore-file)
- [docker init CLI reference](../../engine/reference/commandline/init.md)
- [docker build CLI reference](../../engine/reference/commandline/build.md)
- [Build with Docker guide](../../build/guide/index.md)
## Next steps
This module looked at setting up an example Python application used for the rest of the tutorial, and created a Dockerfile used to build the Docker image. It also looked at tagging and removing images.
In the next section learn how to run your image as a container.
The next module will take a look at how to:
- [Run the image as a container](run-containers.md){: .button .primary-btn}
[Run the image as a container](run-containers.md){: .button .primary-btn}

View File

@ -10,14 +10,12 @@ description: Learn how to Configure CI/CD for your application
{% include gha-tutorial.md %}
## Summary
In this section, you have learned how to set up GitHub Actions workflow to an existing Docker project, optimize your workflow to improve build times and reduce the number of pull requests, and finally, you learned how to push only specific versions to Docker Hub. You can also set up nightly tests against the latest tag, test each PR, or do something more elegant with the tags you are using and make use of the Git tag for the same tag in your image.
## Next steps
In this module, you have learnt how to set up GitHub Actions workflow to an existing Docker project, optimize your workflow to improve build times and reduce the number of pull requests, and finally, we learnt how to push only specific versions to Docker Hub. You can also set up nightly tests against the latest tag, test each PR, or do something more elegant with the tags we are using and make use of the Git tag for the same tag in our image.
Next, learn how you can deploy your application.
You can also consider deploying your application. For detailed instructions, see:
[Deploy your app](deploy.md){: .button .primary-btn}
## Feedback
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs]({{ site.repo }}/issues/new?title=[Python%20docs%20feedback]){:target="_blank" rel="noopener" class="_"} GitHub repository. Alternatively, [create a PR]({{ site.repo }}/pulls){:target="_blank" rel="noopener" class="_"} to suggest updates.
[Deploy your app](deploy.md){: .button .primary-btn}

View File

@ -6,8 +6,4 @@ description: Learn how to deploy your application
{% include_relative nav.html selected="5" %}
{% include deploy.md %}
## Feedback
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs]({{ site.repo }}/issues/new?title=[Python%20docs%20feedback]){:target="_blank" rel="noopener" class="_"} GitHub repository. Alternatively, [create a PR]({{ site.repo }}/pulls){:target="_blank" rel="noopener" class="_"} to suggest updates.
{% include deploy.md %}

View File

@ -12,224 +12,211 @@ Work through the steps to build an image and run it as a containerized applicati
## Introduction
In this module, well walk through setting up a local development environment for the application we built in the previous modules. Well use Docker to build our images and Docker Compose to make everything a whole lot easier.
In this section, youll learn how to use volumes and networking in Docker. Youll also use Docker to build your images and Docker Compose to make everything a whole lot easier.
First, youll take a look at running a database in a container and how you can use volumes and networking to persist your data and let your application to talk with the database. Then youll pull everything together into a Compose file which lets you to set up and run a local development environment with one command.
## Run a database in a container
First, well take a look at running a database in a container and how we use volumes and networking to persist our data and allow our application to talk with the database. Then well pull everything together into a Compose file which allows us to setup and run a local development environment with one command.
Instead of downloading PostgreSQL, installing, configuring, and then running the PostgreSQL database on your system directly, you can use the Docker Official Image for PostgreSQL and run it in a container.
Instead of downloading MySQL, installing, configuring, and then running the MySQL database as a service, we can use the Docker Official Image for MySQL and run it in a container.
Before you run PostgreSQL in a container, create a volume that Docker can manage to store your persistent data and configuration.
Before we run MySQL in a container, we'll create a couple of volumes that Docker can manage to store our persistent data and configuration. Lets use the managed volumes feature that Docker provides instead of using bind mounts. You can read all about [Using volumes](../../storage/volumes.md) in our documentation.
Lets create our volumes now. Well create one for the data and one for configuration of MySQL.
Run the following command to create your volume.
```console
$ docker volume create mysql
$ docker volume create mysql_config
$ docker volume create db-data
```
Now well create a network that our application and database will use to talk to each other. The network is called a user-defined bridge network and gives us a nice DNS lookup service which we can use when creating our connection string.
Now create a network that your application and database will use to talk to each other. The network is called a user-defined bridge network and gives you a nice DNS lookup service which you can use when creating your connection string.
```console
$ docker network create mysqlnet
$ docker network create postgresnet
```
Now we can run MySQL in a container and attach to the volumes and network we created above. Docker pulls the image from Hub and runs it for you locally.
In the following command, option `-v` is for starting the container with volumes. For more information, see [Docker volumes](../../storage/volumes.md).
Now you can run PostgreSQL in a container and attach to the volume and network that you created above. Docker pulls the image from Hub and runs it for you locally.
In the following command, option `--mount` is for starting the container with a volume. For more information, see [Docker volumes](../../storage/volumes.md).
```console
$ docker run --rm -d -v mysql:/var/lib/mysql \
-v mysql_config:/etc/mysql -p 3306:3306 \
--network mysqlnet \
--name mysqldb \
-e MYSQL_ROOT_PASSWORD=p@ssw0rd1 \
mysql
$ docker run --rm -d --mount \
"type=volume,src=db-data,target=/var/lib/postgresql/data" \
-p 5432:5432 \
--network postgresnet \
--name db \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_DB=example \
postgres
```
Now, lets make sure that our MySQL database is running and that we can connect to it. Connect to the running MySQL database inside the container using the following command and enter "p@ssw0rd1" when prompted for the password:
Now, make sure that your PostgreSQL database is running and that you can connect to it. Connect to the running PostgreSQL database inside the container.
```console
$ docker exec -ti mysqldb mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.33 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
$ docker exec -it db psql -U postgres
```
### Connect the application to the database
In the above command, we logged in to the MySQL database by passing the mysql command to the `mysqldb` container. Press CTRL-D to exit the MySQL interactive terminal.
Next, we'll update the sample application we created in the [Build images](build-images.md#sample-application) module. To see the directory structure of the Python app, see [Python application directory structure](build-images.md#directory-structure).
Okay, now that we have a running MySQL, lets update the `app.py` to use MySQL as a datastore. Lets also add some routes to our server. One for fetching records and one for creating our database and table.
```python
import mysql.connector
import json
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, Docker!'
@app.route('/widgets')
def get_widgets():
mydb = mysql.connector.connect(
host="mysqldb",
user="root",
password="p@ssw0rd1",
database="inventory"
)
cursor = mydb.cursor()
cursor.execute("SELECT * FROM widgets")
row_headers=[x[0] for x in cursor.description] #this will extract row headers
results = cursor.fetchall()
json_data=[]
for result in results:
json_data.append(dict(zip(row_headers,result)))
cursor.close()
return json.dumps(json_data)
@app.route('/initdb')
def db_init():
mydb = mysql.connector.connect(
host="mysqldb",
user="root",
password="p@ssw0rd1"
)
cursor = mydb.cursor()
cursor.execute("DROP DATABASE IF EXISTS inventory")
cursor.execute("CREATE DATABASE inventory")
cursor.execute("USE inventory")
cursor.execute("DROP TABLE IF EXISTS widgets")
cursor.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")
cursor.close()
return 'init database'
if __name__ == "__main__":
app.run(host ='0.0.0.0')
```
Weve added the MySQL module and updated the code to connect to the database server, created a database and table. We also created a route to fetch widgets. We now need to rebuild our image so it contains our changes.
First, lets add the `mysql-connector-python` module to our application using pip.
You should see output like the following.
```console
$ python3 -m pip install mysql-connector-python
$ python3 -m pip freeze | grep mysql-connector-python >> requirements.txt
psql (15.3 (Debian 15.3-1.pgdg110+1))
Type "help" for help.
postgres=#
```
Now we can build our image.
In the previous command, you logged in to the PostgreSQL database by passing the `psql` command to the `db` container. Press ctrl-d to exit the PostgreSQL interactive terminal.
```console
$ docker build --tag python-docker-dev .
## Get and run the sample application
You'll need to clone a new repository to get a sample application that includes logic to connect to the database.
1. Change to a directory where you want to clone the repository and run the following command.
```console
$ git clone https://github.com/docker/python-docker-dev
```
2. In the cloned repository's directory, run `docker init` to create the necessary Docker files. Refer to the following example to answer the prompts from `docker init`.
```console
$ docker init
Welcome to the Docker Init CLI!
This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
Let's get started!
? What application platform does your project use? Python
? What version of Python do you want to use? 3.11.4
? What port do you want your app to listen on? 5000
? What is the command to run your app? python3 -m flask run --host=0.0.0.0
```
If you have any containers running from the previous sections using the name `rest-server` or port 8000, [stop and remove](./run-containers.md/#stop-start-and-name-containers) them now.
3. In the cloned repository's directory, run `docker build` to build the image.
```console
$ docker build -t python-docker-dev .
```
Now, lets add the container to the database network and then run our container. This allows us to access the database by its container name.
4. If you have any containers running from the previous sections using the name `rest-server` or port 8000, [stop and remove](./run-containers.md/#stop-start-and-name-containers) them now.
```console
$ docker run \
--rm -d \
--network mysqlnet \
--name rest-server \
-p 8000:5000 \
python-docker-dev
```
5. Run `docker run` with the following options to run the image as a container on the same network as the database.
Lets test that our application is connected to the database and is able to add a note.
```console
$ docker run \
--rm -d \
--network postgresnet \
--name rest-server \
-p 8000:5000 \
-e POSTGRES_PASSWORD=mysecretpassword \
python-docker-dev
```
```console
$ curl http://localhost:8000/initdb
$ curl http://localhost:8000/widgets
```
6. Test that your application is connected to the database and is able to list the widgets.
You should receive the following JSON back from our service.
```console
$ curl http://localhost:8000/initdb
$ curl http://localhost:8000/widgets
```
```json
[]
```
You should receive the following JSON back from your service.
```json
[]
```
This is because your database is empty.
## Use Compose to develop locally
In this section, well create a [Compose file](../../compose/index.md) to start our python-docker and the MySQL database using a single command.
When you run `docker init`, in addition to a `Dockerfile`, it also creates a `compose.yaml` file.
Open the `python-docker` directory in your IDE or a text editor and create a new file named `docker-compose.dev.yml`. Copy and paste the following commands into the file.
This Compose file is super convenient as you don't have to type all the parameters to pass to the `docker run` command. You can declaratively do that using a Compose file.
In the cloned repository's directory, open the `compose.yaml` file in an IDE or text editor. `docker init` handled creating most of the instructions, but you'll need to update it for your unique application.
In the `compose.yaml` file, you need to uncomment all of the database instructions. In addition, you need to add the database password as an environment variable to the server service.
The following is the updated `compose.yaml` file.
```yaml
version: '3.8'
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker compose reference guide at
# https://docs.docker.com/compose/compose-file/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
web:
build:
context: .
ports:
- 8000:5000
volumes:
- ./:/app
mysqldb:
image: mysql
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=p@ssw0rd1
volumes:
- mysql:/var/lib/mysql
- mysql_config:/etc/mysql
server:
build:
context: .
ports:
- 5000:5000
environment:
- POSTGRES_PASSWORD=mysecretpassword
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
depends_on:
db:
condition: service_healthy
db:
image: postgres
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: [ "CMD", "pg_isready" ]
interval: 10s
timeout: 5s
retries: 5
volumes:
mysql:
mysql_config:
db-data:
secrets:
db-password:
file: db/password.txt
```
This Compose file is super convenient as we do not have to type all the parameters to pass to the `docker run` command. We can declaratively do that using a Compose file.
Note that the file doesn't specify a network for those 2 services. Compose automatically creates a network and connects the services to it. For more information see [Networking in Compose](../../compose/networking.md).
We expose port 8000 so that we can reach the dev web server inside the container. We also map our local source code into the running container to make changes in our text editor and have those changes picked up in the container.
Before you run the application using Compose, notice that this Compose file specifies a `password.txt` file to hold the database's password. You must create this file as it's not included in the source repository.
Another really cool feature of using a Compose file is that we have service resolution set up to use the service names. Therefore, we are now able to use “mysqldb” in our connection string. The reason we use “mysqldb” is because that is what we've named our MySQL service as in the Compose file.
In the cloned repository's directory, create a new directory named `db` and inside that directory create a file named `password.txt` that contains the password for the database. Using your favorite IDE or text editor, add the following contents to the `password.txt` file.
Note that we did not specify a network for those 2 services. When we use Docker Compose it automatically creates a network and connect the services to it. For more information see [Networking in Compose](../../compose/networking.md)
If you have any containers running from the previous sections, [stop](./run-containers.md/#stop-start-and-name-containers) them now.
Now, to start our application and to confirm that it is running properly, run the following command:
```console
$ docker compose -f docker-compose.dev.yml up --build
```
mysecretpassword
```
We pass the `--build` flag so Docker will compile our image and then start the containers.
If you have any other containers running from the previous sections, [stop](./run-containers.md/#stop-start-and-name-containers) them now.
Now lets test our API endpoint. Open a new terminal then make a GET request to the server using the curl commands:
Now, run the following `docker compose up` command to start your application.
```console
$ curl http://localhost:8000/initdb
$ curl http://localhost:8000/widgets
$ docker compose up --build
```
The command passes the `--build` flag so Docker will compile your image and then start the containers.
Now test your API endpoint. Open a new terminal then make a request to the server using the curl commands:
```console
$ curl http://localhost:5000/initdb
$ curl http://localhost:5000/widgets
```
You should receive the following response:
@ -238,16 +225,18 @@ You should receive the following response:
[]
```
This is because our database is empty.
This is because your database is empty.
# Summary
In this section, you took a look at setting up your Compose file to run your Python application and database with a single command.
Related information:
- [Volumes](../../storage/volumes.md)
- [Compose overview](../../compose/index.md)
## Next steps
In this module, we took a look at creating a general development image that we can use pretty much like our normal command line. We also set up our Compose file to map our source code into the running container.
In the next module, well take a look at how to set up a CI/CD pipeline using GitHub Actions. See:
In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions.
[Configure CI/CD](configure-ci-cd.md){: .button .primary-btn}
## Feedback
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs]({{ site.repo }}/issues/new?title=[Python%20docs%20feedback]){:target="_blank" rel="noopener" class="_"} GitHub repository. Alternatively, [create a PR]({{ site.repo }}/pulls){:target="_blank" rel="noopener" class="_"} to suggest updates.

View File

@ -6,9 +6,8 @@ toc_min: 1
toc_max: 2
---
The Python getting started guide teaches you how to create a containerized Python application using Docker. In this guide, you'll learn how to:
The Python getting started guide teaches you how to containerize a Python application using Docker. In this guide, you'll learn how to:
* Create a sample Python application
* Create a new Dockerfile which contains instructions required to build a Python image
* Build an image and run the newly built image as a container
* Set up volumes and networking

View File

@ -12,45 +12,45 @@ Work through the steps to build a Python image in [Build your Python image](buil
## Overview
In the previous module, we created our sample application and then we created a Dockerfile that we used to produce an image. We created our image using the docker command `docker build`. Now that we have an image, we can run that image and see if our application is running correctly.
In the previous module, you got the sample application and then created a Dockerfile that you used to produce an image. You created your image using the Docker command `docker build`. Now that you have an image, you can run that image and see if your application is running correctly.
A container is a normal operating system process except that this process is isolated in that it has its own file system, its own networking, and its own isolated process tree separate from the host.
To run an image inside of a container, we use the `docker run` command. The `docker run` command requires one parameter which is the name of the image. Lets start our image and make sure it is running correctly. Run the following command in your terminal.
To run an image inside of a container, use the `docker run` command. The `docker run` command requires one parameter which is the name of the image. Start your image and make sure it is running correctly. Run the following command in your terminal.
```console
$ docker run python-docker
```
After running this command, youll notice that you were not returned to the command prompt. This is because our application is a REST server and runs in a loop waiting for incoming requests without returning control back to the OS until we stop the container.
After running this command, youll notice that you were not returned to the command prompt. This is because your application is a REST server and runs in a loop waiting for incoming requests without returning control back to the OS until you stop the container.
Lets open a new terminal then make a `GET` request to the server using the `curl` command.
Open a new terminal then make a `GET` request to the server using the `curl` command.
```console
$ curl localhost:5000
curl: (7) Failed to connect to localhost port 5000: Connection refused
```
As you can see, our `curl` command failed because the connection to our server was refused. This means, we were not able to connect to the localhost on port 5000. This is expected because our container is running in isolation which includes networking. Lets stop the container and restart with port 5000 published on our local network.
As you can see, your `curl` command failed because the connection to your server was refused. This means you were not able to connect to localhost on port 5000. This is expected because your container is running in isolation which includes networking. Stop the container and restart with port 5000 published on your local network.
To stop the container, press ctrl-c. This will return you to the terminal prompt.
To publish a port for our container, well use the `--publish` flag (`-p` for short) on the `docker run` command. The format of the `--publish` command is `[host port]:[container port]`. So, if we wanted to expose port 5000 inside the container to port 3000 outside the container, we would pass `3000:5000` to the `--publish` flag.
To publish a port for your container, use the `--publish` flag (`-p` for short) on the `docker run` command. The format of the `--publish` command is `[host port]:[container port]`. So, if you wanted to expose port 5000 inside the container to port 3000 outside the container, you would pass `3000:5000` to the `--publish` flag.
We did not specify a port when running the flask application in the container and the default is 5000. If we want our previous request going to port 5000 to work we can map the host's port 8000 to the container's port 5000:
You did not specify a port when running the flask application in the container and the default is 5000. If you want your previous request going to port 5000 to work, you can map the host's port 8000 to the container's port 5000:
```console
$ docker run --publish 8000:5000 python-docker
```
Now, lets rerun the curl command from above. Remember to open a new terminal.
Now, open a new terminal window and re-run the `curl` command.
```console
$ curl localhost:8000
Hello, Docker!
```
Success! We were able to connect to the application running inside of our container on port 8000. Switch back to the terminal where your container is running and you should see the `GET` request logged to the console.
Success! You were able to connect to the application running inside of your container on port 8000. Switch back to the terminal where your container is running and you should see the `GET` request logged to the console.
```shell
[31/Jan/2021 23:39:31] "GET / HTTP/1.1" 200 -
@ -60,16 +60,16 @@ Press ctrl-c to stop the container.
## Run in detached mode
This is great so far, but our sample application is a web server and we don't have to be connected to the container. Docker can run your container in detached mode or in the background. To do this, we can use the `--detach` or `-d` for short. Docker starts your container the same as before but this time will “detach” from the container and return you to the terminal prompt.
This is great so far, but your sample application is a web server and you don't have to be connected to the container. Docker can run your container in detached mode or in the background. To do this, you can use the `--detach` or `-d` for short. Docker starts your container the same as before but this time will “detach” from the container and return you to the terminal prompt.
```console
$ docker run -d -p 8000:5000 python-docker
ce02b3179f0f10085db9edfccd731101868f58631bdf918ca490ff6fd223a93b
```
Docker started our container in the background and printed the Container ID on the terminal.
Docker started your container in the background and printed the container ID on the terminal.
Again, lets make sure that our container is running properly. Run the same curl command from above.
Again, make sure that your container is running properly. Run the same curl command from above.
```console
$ curl localhost:8000
@ -78,7 +78,7 @@ Hello, Docker!
## List containers
Since we ran our container in the background, how do we know if our container is running or what other containers are running on our machine? Well, to see a list of containers running on our machine, run `docker ps`. This is similar to how the ps command is used to see a list of processes on a Linux machine.
Since you ran your container in the background, how do you know if your container is running or what other containers are running on your machine? Well, to see a list of containers running on your machine, run `docker ps`. This is similar to how the ps command is used to see a list of processes on a Linux machine.
```console
$ docker ps
@ -86,9 +86,9 @@ CONTAINER ID IMAGE COMMAND CREATED
ce02b3179f0f python-docker "python3 -m flask ru…" 6 minutes ago Up 6 minutes 0.0.0.0:8000->5000/tcp wonderful_kalam
```
The `docker ps` command provides a bunch of information about our running containers. We can see the container ID, the image running inside the container, the command that was used to start the container, when it was created, the status, ports that were exposed, and the name of the container.
The `docker ps` command provides a bunch of information about your running containers. You can see the container ID, the image running inside the container, the command that was used to start the container, when it was created, the status, ports that were exposed, and the name of the container.
You are probably wondering where the name of our container is coming from. Since we didnt provide a name for the container when we started it, Docker generated a random name. Well fix this in a minute, but first we need to stop the container. To stop the container, run the `docker stop` command which does just that, stops the container. You need to pass the name of the container or you can use the container ID.
You are probably wondering where the name of your container is coming from. Since you didnt provide a name for the container when you started it, Docker generated a random name. Youll fix this in a minute, but first you need to stop the container. To stop the container, run the `docker stop` command which does just that, stops the container. You need to pass the name of the container or you can use the container ID.
```console
$ docker stop wonderful_kalam
@ -104,7 +104,7 @@ CONTAINER ID IMAGE COMMAND CREATED
## Stop, start, and name containers
You can start, stop, and restart Docker containers. When we stop a container, it is not removed, but the status is changed to stopped and the process inside the container is stopped. When we ran the `docker ps` command in the previous module, the default output only shows running containers. When we pass the `--all` or `-a` for short, we see all containers on our machine, irrespective of their start or stop status.
You can start, stop, and restart Docker containers. When you stop a container, it is not removed, but the status is changed to stopped and the process inside the container is stopped. When you ran the `docker ps` command in the previous module, the default output only shows running containers. When you pass the `--all` or `-a` for short, you see all containers on your machine, irrespective of their start or stop status.
```console
$ docker ps -a
@ -114,9 +114,9 @@ ec45285c456d python-docker "python3 -m flask ru…" 28 minutes
fb7a41809e5d python-docker "python3 -m flask ru…" 37 minutes ago Exited (0) 36 minutes ago goofy_khayyam
```
You should now see several containers listed. These are containers that we started and stopped but have not been removed.
You should now see several containers listed. These are containers that you started and stopped but have not been removed.
Lets restart the container that we just stopped. Locate the name of the container we just stopped and replace the name of the container below in the restart command.
Restart the container that you just stopped. To do this, locate the name of the container you just stopped and replace the name of the container below in the restart command.
```console
$ docker restart wonderful_kalam
@ -132,16 +132,16 @@ ec45285c456d python-docker "python3 -m flask ru…" 31 minutes
fb7a41809e5d python-docker "python3 -m flask ru…" 40 minutes ago Exited (0) 39 minutes ago goofy_khayyam
```
Notice that the container we just restarted has been started in detached mode and has port 8000 exposed. Also, observe the status of the container is “Up X seconds”. When you restart a container, it starts with the same flags or commands that it was originally started with.
Notice that the container you just restarted has been started in detached mode and has port 8000 exposed. Also, observe the status of the container is “Up X seconds”. When you restart a container, it starts with the same flags or commands that it was originally started with.
Now, lets stop and remove all of our containers and take a look at fixing the random naming issue. Stop the container we just started. Find the name of your running container and replace the name in the command below with the name of the container on your system.
Now, stop and remove all of your containers and take a look at fixing the random naming issue. Stop the container you just started. Find the name of your running container and replace the name in the command below with the name of the container on your system.
```console
$ docker stop wonderful_kalam
wonderful_kalam
```
Now that all of our containers are stopped, lets remove them. When you remove a container, it is no longer running, nor it is in the stopped status, but the process inside the container has been stopped and the metadata for the container has been removed.
Now that all of your containers are stopped, remove them. When you remove a container, it is no longer running, nor it is in the stopped status, but the process inside the container has been stopped and the metadata for the container has been removed.
```console
$ docker ps --all
@ -162,9 +162,9 @@ goofy_khayyam
Run the `docker ps --all` command again to see that all containers are removed.
Now, lets address the random naming issue. Standard practice is to name your containers for the simple reason that it is easier to identify what is running in the container and what application or service it is associated with.
Now, address the random naming issue. Standard practice is to name your containers for the simple reason that it is easier to identify what is running in the container and what application or service it is associated with.
To name a container, we just need to pass the `--name` flag to the `docker run` command.
To name a container, you just need to pass the `--name` flag to the `docker run` command.
```console
$ docker run -d -p 8000:5000 --name rest-server python-docker
@ -174,14 +174,17 @@ CONTAINER ID IMAGE COMMAND CREATED
1aa5d46418a6 python-docker "python3 -m flask ru…" 3 seconds ago Up 3 seconds 0.0.0.0:8000->5000/tcp rest-server
```
Thats better! We can now easily identify our container based on the name.
Thats better! You can now easily identify your container based on the name.
## Summary
In this section, you took a look at running containers. You also took a look at managing containers by starting, stopping, and restarting them. And finally, you looked at naming your containers so they are more easily identifiable.
Related information:
- [docker run CLI reference](../../engine/reference/commandline/run.md)
## Next steps
In this module, we took a look at running containers, publishing ports, and running containers in detached mode. We also took a look at managing containers by starting, stopping, and restarting them. We also looked at naming our containers so they are more easily identifiable. In the next module, well learn how to run a database in a container and connect it to our application. See:
In the next section, youll learn how to run a database in a container and connect it to a Rust application.
[How to develop your application](develop.md){: .button .primary-btn}
## Feedback
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs]({{ site.repo }}/issues/new?title=[Python%20docs%20feedback]){:target="_blank" rel="noopener" class="_"} GitHub repository. Alternatively, [create a PR]({{ site.repo }}/pulls){:target="_blank" rel="noopener" class="_"} to suggest updates.
[How to develop your application](develop.md){: .button .primary-btn}