# DevNet Lab 11 -- Build a Sample Web App in a Podman Container
[toc]
---
### Scenario
In this lab, you will review basic Bash scripting techniques, as Bash scripting is a prerequisite for the rest of the lab. You will then create and modify a Python script for a simple web application. Next, you will create a Bash script to automate the process of creating a Dockerfile, building the Podman container, and running the container.
Finally, you will use the **podman** command to explore the intricacies of the **Podman Container** instance.

### Objectives
After completing the manipulations in this lab, students will be able to:
- Write and run Bash scripts to automate tasks, including creating and modifying files and directories.
- Develop a simple Python-based web application using the Flask framework.
- Build and deploy containerized applications using Podman, including creating Dockerfiles.
- Manage and interact with containers, including starting, stopping, and accessing their environments.
## Part 1: Create a Simple Bash Script
Bash knowledge is crucial for working with continuous integration, continuous deployment, containers, and with your development environment. Bash scripts help programmers automate a variety of tasks in one script file.
In this part, you will briefly review how to create a bash script. Later in the lab, you will use a bash script to automate the creation of a web app inside of a Podman container.
### Step 1: Create an empty bash script file
1. Change your working directory to `~/labs/lab11/sample-app` and add a new file called `user-input.sh`.
```bash
mkdir -p ~/labs/lab11/sample-app && cd ~/labs/lab11
```
2. Open the file a text editor.
Open one a text editor such as vim, Visual Studio Code with remote SSH extension.
3. Add the ‘she-bang’ to the top of the script.
From here you can enter commands for your bash script. Add the ‘she-bang’ which tells the system that this file includes commands that need to be run in the bash shell.
```bash
#!/bin/bash
```
> Note: You can use a graphical text editor such as Visual Studio Code. However, you should be familiar with command-line text editors like nano and vim. Search the internet for tutorials to refresh your skill or learn more about them.
4. Add simple bash commands to the script.
Enter some simple bash commands for your script. The following commands will ask the user for a name, set the name to a variable called userName, and display a string of text with the user’s name.
```bash=
#!/bin/bash
echo -n "Enter your name: "
read -r user_name
echo "Hello, ${user_name}. Be my friend!"
exit 0
```
5. Save your script and exit your text editor.
Press ESC, enter :wq to exit vim and save your script.
6. Run your script from the command line.
You can run it directly from the command line using the following command.
```bash
bash user-input.sh
```
```bash=
Enter your name: Alice
Hello, Alice. Be my friend!
```
### Step 2: Change the mode of the script to an executable file for all users
Change the mode of the script to an executable using the `chmod` command. Set the options to `a+x` to make the script executable (x) by all users (a). After using chmod, notice permissions have been modified for users, groups, and others to include the "x" (executable).
```bash
ls -l user-input.sh
-rw-rw-r-- 1 etu etu 108 févr. 8 14:20 user-input.sh
```
```bash
chmod a+x user-input.sh
ls -l user-input.sh
-rwxrwxr-x 1 etu etu 108 févr. 8 14:20 user-input.sh
```
```bash
./user-input.sh
```
```bash=
Enter your name: Bob
Hello, Bob. Be my friend!
```
### Step 3: Investigate other bash scripts
If you have little or no experience creating bash scripts, take some time to search the internet for bash tutorials, bash examples, and bash games.
## Part 2: Create a Sample Web App
Before we can launch an application in a **Podman container**, we first need to have the app. In this part, you will create a very simple Python script that will display the IP address of the client when the client visits the web page.
### Step 1: Install Flask and open a port on the Virtual Machine
Web application developers using Python typically use a framework. A framework is a library of code that makes it easier for developers to build reliable, scalable, and maintainable web applications. Flask is a web application framework written in Python.
You will use this framework to create the sample web app. Flask receives requests and then provides a response to the user in the web app. This is useful for dynamic web applications because it allows for user interaction and dynamic content. What makes your sample web app dynamic is that it will display the IP address of the client.
> Note: Understanding Flask's functions, methods, and libraries is beyond the scope of this course. It is used in this lab to show how quickly you can get a web application up and running. If you want to learn more, search the web for more information and tutorials on the Flask framework.
We use a Python virtual environment to run the Flask service.
In the command list below, the working directory (VSCode workspace) is located in `$HOME/labs/lab11`.
```bash
python3 -m venv lab11
source ./lab11/bin/activate
pip3 install flask
```
```bash=
Collecting flask
Downloading flask-3.1.0-py3-none-any.whl.metadata (2.7 kB)
Collecting Werkzeug>=3.1 (from flask)
Downloading werkzeug-3.1.3-py3-none-any.whl.metadata (3.7 kB)
Collecting Jinja2>=3.1.2 (from flask)
Using cached jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB)
Collecting itsdangerous>=2.2 (from flask)
Downloading itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
Collecting click>=8.1.3 (from flask)
Using cached click-8.1.8-py3-none-any.whl.metadata (2.3 kB)
Collecting blinker>=1.9 (from flask)
Downloading blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB)
Collecting MarkupSafe>=2.0 (from Jinja2>=3.1.2->flask)
Using cached MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.0 kB)
Downloading flask-3.1.0-py3-none-any.whl (102 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 103.0/103.0 kB 1.8 MB/s eta 0:00:00
Downloading blinker-1.9.0-py3-none-any.whl (8.5 kB)
Using cached click-8.1.8-py3-none-any.whl (98 kB)
Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
Downloading werkzeug-3.1.3-py3-none-any.whl (224 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 224.5/224.5 kB 5.0 MB/s eta 0:00:00
Using cached MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (23 kB)
Installing collected packages: MarkupSafe, itsdangerous, click, blinker, Werkzeug, Jinja2, flask
Successfully installed Jinja2-3.1.6 MarkupSafe-3.0.2 Werkzeug-3.1.3 blinker-1.9.0 click-8.1.8 flask-3.1.0 itsdangerous-2.2.0
```
### Step 2: Open the sample-app.py file
1. Open a new `sample-app.py` file located in the `sample-app/` directory.
2. Add the commands to import the methods from flask.
Add the following commands to import the required methods from the flask library.
```python=
from flask import Flask, request
```
3. Create an instance of the Flask class.
Create an instance of the Flask class and name it sample. Be sure to use two underscores before and after the "name".
```python=
sample = Flask(__name__)
```
4. Define a method to get the client's IP address.
Next, configure Flask to display a message with the client's IP address when a user visits the default page (root directory).
```python=
@sample.route("/")
def main():
return "You are calling me from " + request.remote_addr + "\n"
```
> Note the @sample.route("/") Flask statement. Frameworks like Flask use a routing technique (.route) to point to an application URL (this not to be confused with network routing).
Here the "/" (root directory) is bound to the main() function. So, when the user goes to the `http://localhost:8081/` (root directory) URL, the output of the return statement will be displayed in the browser.
5. Configure the app to run locally.
Finally, configure Flask to run the application locally at `http://0.0.0.0:8081`, which is also `http://localhost:8081`. Make sure you use two underscores before and after `name`, and before and after `main`.
```python=
if __name__ == "__main__":
sample.run(host="::", port=8081)
```
### Step 3: Save and run your sample web app
Save your script and run it from the command line. You should see the following output, indicating that your “sample-app” server is running. If you do not see the following output, or if you get an error message, check your `sample-app.py` script carefully.
```bash
python3 $HOME/labs/lab11/sample-app/sample-app.py
```
```bash=
* Serving Flask app 'sample-app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (::)
* Running on http://[::1]:8081
* Running on http://[2001:678:3fc:34:baad:caff:fefe:0]:8081
Press CTRL+C to quit
````
### Step 4: Verify the server is running
You can verify the server is running in one of two ways.
* Open a web browser and enter `localhost:8081` in the URL field. You should get the following output:
```bash=
You are calling me from ::ffff:127.0.0.1
```
If you receive an "HTTP 400 Bad Request" response, check your `sample-app.py` script carefully.
* Open another terminal window and use the command-line URL tool (cURL) to verify the server’s response.
```bash
curl http://localhost:8081
```
```bash=
You are calling me from ::1
```
You can also list the open sockets listening on port 8081.
```bash
lsof -i tcp:8081
```
```bash=
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 6475 etu 3u IPv6 23039 0t0 TCP *:tproxy (LISTEN)
```
### Step 5: Stop the server
Return to the terminal window where the server is running and press CTRL+C to stop the server.
## Part 3: Configure the Web App to use Website Files
In this part, build out the sample web app to include an `index.html` page and `style.css` specification. The `index.html` is typically the first page loaded in a client’s web browser when visiting your website. The `style.css` is a style sheet used to customize the look of the web page.
### Step 1: Explore the directories that will be used by the web app
In this section, you will extend the sample web application to include an `index.html` page and a `style.css` specification. The `index.html` is typically the first page loaded in a client's web browser when they visit your site. The `style.css` is a style sheet used to customize the appearance of the web page.
```bash
mkdir templates
```
```bash=
cat << 'EOF' > templates/index.html
<html>
<head>
<title>Sample app</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<h1>You are calling me from {{request.remote_addr}}</h1>
</body>
</html>
EOF
```
```bash
mkdir static
```
```bash=
cat << EOF > static/style.css
body {
background: rgba(0, 128, 32, 0.3); /* Green background with 30% opacity */
}
EOF
```
### Step 2: Update the Python code for the sample web app
Now that you have explored the basic website files, you will need to update the `sample-app.py` file to render the `index.html` file instead of just returning data. Generating HTML content from Python code can be tedious, especially when using conditional statements or repeating structures.
The HTML file can be automatically rendered in Flask using the `render_template` function. This requires importing the `render_template` method from the Flask library and editing it to include the return function.
```python=
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template
sample = Flask(__name__)
@sample.route("/")
def main():
return render_template("index.html")
if __name__ == "__main__":
sample.run(host="::", port=8081)
```
### Step 3: Save and run your script
Save and run your `sample-app.py` script. You should get output like the following:
```bash
python3 ./sample-app.py
```
```bash=
* Serving Flask app 'sample-app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (::)
* Running on http://[::1]:8081
* Running on http://[2001:678:3fc:1c:baad:caff:fefe:0]:8081
Press CTRL+C to quit
```
> Note: If you got traceback output and an error message with something like `OSError: [Errno 98] Address already in use`, then you have not shut down your previous server. Return to the terminal window where this server is running and press CTRL+C to kill the server process. Run your script again.
### Step 4: Verify your program is running
Again, there are two ways to verify that your program is running.
- Open a web browser and type `localhost:8081` in the URL field. You should see the same output as before. However, your background color will not be white and the text will be formatted as H1.

- Open another terminal window and use the curl command to check the server's response. Here you will see the result of the HTML code automatically rendered by the `render_template` function. In this case, you get all the HTML content. However, the dynamic Python code is replaced with the value for {{request.remote_addr}}. Also note that your prompt is on the same line as the last line of HTML output. Press ENTER to get a new line.
```bash
curl http://localhost:8081
```
```bash=
<html>
<head>
<title>Sample app</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<h1>You are calling me from ::1</h1>
</body>
</html>
```
### Step 5: Stop the server
Return to the terminal window where the server is running and press CTRL+C to stop the server.
## Part 4: Create a Bash Script to Build and Run a Podman Container
An application can be deployed on a bare-metal server (physical server dedicated to a single-tenant environment) or in a virtual machine, as you just did in the previous part. It can also be deployed in a containerized solution such as **Podman**.
In this part, you will create a bash script and add commands to it to perform the following tasks to create and run a Podman container:
* Create temporary directories to store the website files
* Copy the website directories and sample-app.py into the temporary directory
* Create a docker file
* Build the Podman container
* Run the container and verify that it works
### Step 1: Create temporary directories to store the website files
Open the `sample-app.sh` bash script file in the `~/labs/lab11/sample-app` directory. Add the "she-bang" and the commands to create a directory structure with tempdir as the parent folder.
```bash=
#!/bin/bash
set -e
mkdir -p tempdir
```
Copy the website directories and sample-app.py to the temporary directory.
In the `sample-app.sh` file, add the commands to copy the website directory and script to tempdir.
```bash=
cp sample-app.py tempdir/
cp -r templates tempdir/
cp -r static tempdir/
```
### Step 2: Run the shell script
In order to create the directory and copy the website files, run the script.
```bash
bash ./sample-app.sh
```
```bash=
ls -l tempdir/
total 4
-rw-rw-r-- 1 etu etu 295 févr. 8 15:27 sample-app.py
drwxrwxr-x 1 etu etu 18 févr. 8 15:27 static
drwxrwxr-x 1 etu etu 20 févr. 8 15:27 templates
```
### Step 3: Create a Dockerfile
In this step, you add the necessary commands in the `sample-app.sh` file to create a **Dockerfile** in the `tempdir`. This Dockerfile will be used to build the container.
1. You will need Python to run inside the container, so add the Docker FROM command to install Python inside the container.
```bash
FROM docker.io/library/python
```
2. Your `sample-app.py` script requires Flask, so add the Docker RUN command to install Flask in the container. Note that we add the `PIP_ROOT_USER_ACTION` environment variable because the container's current user is root.
```bash
ENV PIP_ROOT_USER_ACTION=ignore
RUN pip3 install flask
```
3. Your container will need the website folders and the `sample-app.py` script to run the app, so add the Docker COPY commands to add them to a directory inside the Podman container. In this example, you will create `/home/myapp` as a parent directory inside the Podman container. Besides copying the `sample-app.py` file to the Dockerfile, you will also copy the `index.html` file from the `templates` directory and the `style.css` file from the `static` directory.
```bash
COPY ./static /home/myapp/static/
COPY ./templates /home/myapp/templates/
COPY sample-app.py /home/myapp/
```
4. Use the `Docker EXPOSE` command to expose port 8081 for use by the web server.
```bash
EXPOSE 8081
```
5. Finally, add the `Docker CMD` command to execute the Python script.
```bash
CMD python3 /home/myapp/sample-app.py
```
Instead of adding instructions line by line, it is much more convenient to use a Heredoc command to add the whole set of instructions at once.
```bash=
cat << EOF > tempdir/Dockerfile
FROM docker.io/library/python
ENV PIP_ROOT_USER_ACTION=ignore
RUN pip3 install --upgrade flask
COPY ./static /home/myapp/static/
COPY ./templates /home/myapp/templates/
COPY sample-app.py /home/myapp/
EXPOSE 8081
CMD python3 /home/myapp/sample-app.py
EOF
```
### Step 4: Build the Podman container
Add the command set to the `sample-app.sh` file to change to the tempdir directory and build the Podman container. The `-t` option of the `podman build` command allows you to specify the name of the container.
1. Install packages to manage containers
```bash
sudo apt update
sudo apt -y install podman runc
```
2. Run podman to build the container
```bash
cd tempdir
podman build -f Dockerfile -t sampleapp
```
```bash=
STEP 1/8: FROM docker.io/library/python
STEP 2/8: ENV PIP_ROOT_USER_ACTION=ignore
--> Using cache deae775642f81c04a7c9987bfd89eb03f8bbad6621fecca6b53e4602262645b6
--> deae775642f8
STEP 3/8: RUN pip3 install --upgrade flask
--> Using cache 39d0492d95b2dee11cbca1c7ce25fe7fdb93915013efa3e94e568bd39d1f08f6
--> 39d0492d95b2
STEP 4/8: COPY ./static /home/myapp/static/
--> Using cache d2dfc7b377204096bb03fd9bba6a95bb7fca8b3fc779414b174bddd1c25769a5
--> d2dfc7b37720
STEP 5/8: COPY ./templates /home/myapp/templates/
--> Using cache 260428a8f91147fc4f937083fd303ae769fb75e4e87f3fc36a89ed389aed800b
--> 260428a8f911
STEP 6/8: COPY sample-app.py /home/myapp/
--> Using cache 56103e5380a876d90f6aee340e4270fbe490103d035dac399a20e101d93b0ee1
--> 56103e5380a8
STEP 7/8: EXPOSE 8081
--> Using cache 41701c091ade5134da33576cd54a5555111c0c567140c8b6adc026547d5be2b0
--> 41701c091ade
STEP 8/8: CMD python3 /home/myapp/sample-app.py
--> Using cache 429422739eb071ba9f6db501bf807b273cb3f0d7f19c6726192412921ca23f8c
COMMIT sampleapp
--> 429422739eb0
Successfully tagged localhost/sampleapp:latest
429422739eb071ba9f6db501bf807b273cb3f0d7f19c6726192412921ca23f8c
```
## Step 5: Start the container and verify it is running
1. Test the podman run command to start the container.
```bash
podman run -t -d -p 8081:8081 --name samplerunning sampleapp
```
```bash=
aac8d3ab7f75f744999e822f3e445d2cbdf3f99950a7b7f088943769b807007b
```
The docker run options indicate the following:
`-t`
: We want to create a terminal for the container so that we can access it from the command line.
`-d`
: We want the container to run in the background and print the container ID when we run the `podman ps -a` command.
`-p`
: We want to expose the container's internal port to the host. The first `8081` refers to the port for the application running inside the Podman container (our sample application). The second `8081` tells podman to use this port on the host. These values do not have to be the same. For example, an internal port 80 to an external 800 (80:800).
`--name`
: We want to call the instance of the container (samplerunning) and then call the container image that the instance is based on (sampleapp). The instance name can be anything. However, the image name must match the container name you specified in the Docker build command (sampleapp).
2. Add the `podman ps -a` command to display all currently running Docker containers. This command will be the last one executed by the bash script.
```bash
podman ps -a
```
```bash=
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aac8d3ab7f75 localhost/sampleapp:latest /bin/sh -c python... 7 minutes ago Up 7 minutes 0.0.0.0:8081->8081/tcp samplerunning
```
```bash
lsof -i tcp:8081
```
```bash=
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rootlessp 44843 etu 11u IPv6 131947 0t0 TCP *:tproxy (LISTEN)
```
```bash
curl http://localhost:8081
```
```html=
<html>
<head>
<title>Sample app</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<h1>You are calling me from ::ffff:10.0.2.100</h1>
</body>
</html>
```
## Part 5: Build, Run, and Verify the Podman Container
In this part, you will run a bash script that will create the directories, copy the files, create a docker file, build the podman container, run an instance of the podman container.
Then you will investigate the container, stop the container from running, and remove the container.
> Note: Be sure you stopped any other web server processes you may still have running from the previous parts of this lab.
```bash
podman stop samplerunning # stops container named samplerunning
podman rm samplerunning # remove container named samplerunning
podman ps -a # empty output
lsof -i tcp:8081 # empty output
```
### Step 1: Execute the bash script
Here is a copy of the `sample-app.sh` script with all the instructions:
```bash=
#!/bin/bash
set -e
mkdir -p tempdir
cp sample-app.py tempdir/
cp -r templates tempdir/
cp -r static tempdir/
cat <<EOF >tempdir/Dockerfile
FROM docker.io/library/python
ENV PIP_ROOT_USER_ACTION=ignore
RUN pip3 install --upgrade flask
COPY ./static /home/myapp/static/
COPY ./templates /home/myapp/templates/
COPY sample-app.py /home/myapp/
EXPOSE 8081
CMD python3 /home/myapp/sample-app.py
EOF
cd tempdir
podman build -f Dockerfile -t sampleapp
podman run -t -d -p 8081:8081 --name samplerunning sampleapp
exit 0
```
Run the bash script from the command line. You should see output similar to the following. After creating the tempdir directories, the script executes the commands to build the container.
> Notice that the output executes the `sample-app.py` that creates the web server. Also, notice the container ID. You will see this in the podman command prompt later in the lab.
```bash
bash ./sample-app.sh
```
```bash=
STEP 1/8: FROM docker.io/library/python
STEP 2/8: ENV PIP_ROOT_USER_ACTION=ignore
--> Using cache deae775642f81c04a7c9987bfd89eb03f8bbad6621fecca6b53e4602262645b6
--> deae775642f8
STEP 3/8: RUN pip3 install --upgrade flask
--> Using cache 39d0492d95b2dee11cbca1c7ce25fe7fdb93915013efa3e94e568bd39d1f08f6
--> 39d0492d95b2
STEP 4/8: COPY ./static /home/myapp/static/
--> Using cache d2dfc7b377204096bb03fd9bba6a95bb7fca8b3fc779414b174bddd1c25769a5
--> d2dfc7b37720
STEP 5/8: COPY ./templates /home/myapp/templates/
--> Using cache 260428a8f91147fc4f937083fd303ae769fb75e4e87f3fc36a89ed389aed800b
--> 260428a8f911
STEP 6/8: COPY sample-app.py /home/myapp/
--> Using cache 56103e5380a876d90f6aee340e4270fbe490103d035dac399a20e101d93b0ee1
--> 56103e5380a8
STEP 7/8: EXPOSE 8081
--> Using cache 41701c091ade5134da33576cd54a5555111c0c567140c8b6adc026547d5be2b0
--> 41701c091ade
STEP 8/8: CMD python3 /home/myapp/sample-app.py
--> Using cache 429422739eb071ba9f6db501bf807b273cb3f0d7f19c6726192412921ca23f8c
COMMIT sampleapp
--> 429422739eb0
Successfully tagged localhost/sampleapp:latest
429422739eb071ba9f6db501bf807b273cb3f0d7f19c6726192412921ca23f8c
4df45eb0d8f2f08d02d9f22494c56ee3c88f5cf80563c285450108023bbf8ae2
```
### Step 2: Investigate the running Podman container and the web app
1. The creation of the tempdir directories is not shown in the output for the script. You could add echo commands to print messages when they are successfully created. You can also use the `ls` command to verify that they exist. Remember that this directory contains the files and folders used to build the container and launch the web application. It is not the container that was built.
```bash
ls tempdir/
```
```bash=
Dockerfile sample-app.py static templates
```
2. Notice the Dockerfile created by your bash script. Open this file to see what it looks like in its final form.
```bash
cat tempdir/Dockerfile
```
```bash=
FROM docker.io/library/python
ENV PIP_ROOT_USER_ACTION=ignore
RUN pip3 install --upgrade flask
COPY ./static /home/myapp/static/
COPY ./templates /home/myapp/templates/
COPY sample-app.py /home/myapp/
EXPOSE 8081
CMD python3 /home/myapp/sample-app.py
```
3. The output for the `podman ps -a` command may be difficult to read, depending on the width of your terminal display. You can redirect it to a text file where you can see it better without line breaks.
```bash
podman ps -a
```
```bash=
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4df45eb0d8f2 localhost/sampleapp:latest /bin/sh -c python... 4 minutes ago Up 4 minutes 0.0.0.0:8081->8081/tcp samplerunning
```
```bash
lsof -i tcp:8081
```
```bash=
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rootlessp 45931 etu 11u IPv6 136676 0t0 TCP *:tproxy (LISTEN)
```
4. By default, podman runs containers as user processes
```bash
systemctl status --user
```
```bash=
systemctl status --user
● devnet25
State: running
Units: 209 loaded (incl. loaded aliases)
Jobs: 0 queued
Failed: 0 units
Since: Mon 2025-03-31 18:08:13 CEST; 4h 21min ago
systemd: 255.4-1ubuntu8.6
CGroup: /user.slice/user-1000.slice/user@1000.service
├─init.scope
│ ├─860 /usr/lib/systemd/systemd --user
│ └─861 "(sd-pam)"
├─session.slice
│ └─dbus.service
│ └─1413 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
└─user.slice
├─libpod-4df45eb0d8f2f08d02d9f22494c56ee3c88f5cf80563c285450108023bbf8ae2.scope
│ ├─45962 /bin/sh -c "python3 /home/myapp/sample-app.py"
│ └─45974 python3 /home/myapp/sample-app.py
├─libpod-conmon-4df45eb0d8f2f08d02d9f22494c56ee3c88f5cf80563c285450108023bbf8ae2.scope
│ └─45948 /usr/bin/conmon --api-version 1 -c 4df45eb0d8f2f08d02d9f22494c56ee3c88f5cf80563c285450108023bbf8ae2 -u 4df45eb0d8f2f08d02d9f22494c56ee3c88>
├─podman-45917.scope
│ ├─45929 /usr/bin/slirp4netns --disable-host-loopback --mtu=65520 --enable-sandbox --enable-seccomp --enable-ipv6 -c -r 3 -e 4 --netns-type=path /r>
│ ├─45931 rootlessport
│ └─45937 rootlessport-child
└─podman-pause-2a239771.scope
└─11697 catatonit -P
```
* Line 13: new user slice
* Line 19: Python script that runs the dynamic web page
### Step 3: Access and explore the running container
Remember that a Podman container is a way to encapsulate everything you need to run your application so that it can be easily deployed in a variety of environments - not just in your virtual machine.
1. To access the running container, issue the `podman exec -it` command, specifying the name of the running container (samplerunning) and that you want a bash shell (/bin/bash). The `-i` option indicates that you want to be interactive and the `-t` option indicates that you want terminal access. The prompt will change to root@containerID. Your container ID will be different from the one shown below. Note that the container ID matches the ID shown in the output of `podman ps -a`.
```bash
podman exec -it samplerunning /bin/bash
```
```bash=
root@4df45eb0d8f2:/#
```
2. You have now root access to the samplerunning Podman container. From here, you can use Linux commands to explore the Podman container. Type `ls` to see the directory structure at the root level.
```bash
root@4df45eb0d8f2:/# ls
```
```bash=
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
```
3. Remember that you added commands to the docker file in your bash script that copied your app directories and files to the `/home/myapp` directory. Run the `ls` command on this folder again to see your `sample-app.py` script and directories. To get a better understanding of what is inside your Podman container, you can use the `ls` command to examine other directories such as `/etc` and `/bin`.
```bash
root@4df45eb0d8f2:/# ls /home/myapp/*
/home/myapp/sample-app.py
/home/myapp/static:
style.css
/home/myapp/templates:
index.html
```
4. Exit the Podman container to return to the DevNet virtual machine shell.
```bash
root@40001345ddc3:/# exit
exit
```
### Step 4: Stop and remove the Podman container
1. You can stop the Podman container with the `podman stop` command specifying the name of the running container. It will take a few seconds to clean up and cache the container. You can see that it still exists by entering the `podman ps -a` command. However, if you refresh the web page for `http://localhost:8081`, you will see the web app is no longer running.
```bash
podman stop samplerunning
podman ps -a
```
```bash=
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4df45eb0d8f2 localhost/sampleapp:latest /bin/sh -c python... 14 minutes ago Exited (137) Less than a second ago 0.0.0.0:8081->8081/tcp samplerunning
```
The `STATUS` of the last command shows the container process has exited.
2. You can restart a stopped container with the podman start command. The container will immediately spin up.
```bash
podman start samplerunning
```
```bash=
samplerunning
```
3. To permanently remove the container, first stop it and then remove it with the `podman rm` command. You can always rebuild it again executing the sample-app program. Use the `podman ps -a` command to verify the container has been removed.
```bash
podman stop samplerunning
```
```bash=
WARN[0010] StopSignal SIGTERM failed to stop container samplerunning in 10 seconds, resorting to SIGKILL
samplerunning
```
```bash
podman rm samplerunning
```
```bash=
samplerunning
```
```bash
podman ps -a
```
```bash=
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
```
4. To manage container images, you can use the `podman image` command:
```bash
podman image ls
```
```bash=
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/sampleapp latest 429422739eb0 46 minutes ago 1.05 GB
docker.io/library/python latest e6c8fe2e1108 7 weeks ago 1.04 GB
```
```bash
podman image rm localhost/sampleapp:latest
```
```bash=
Untagged: localhost/sampleapp:latest
Deleted: 429422739eb071ba9f6db501bf807b273cb3f0d7f19c6726192412921ca23f8c
Deleted: 41701c091ade5134da33576cd54a5555111c0c567140c8b6adc026547d5be2b0
Deleted: 56103e5380a876d90f6aee340e4270fbe490103d035dac399a20e101d93b0ee1
Deleted: 260428a8f91147fc4f937083fd303ae769fb75e4e87f3fc36a89ed389aed800b
Deleted: d2dfc7b377204096bb03fd9bba6a95bb7fca8b3fc779414b174bddd1c25769a5
Deleted: 39d0492d95b2dee11cbca1c7ce25fe7fdb93915013efa3e94e568bd39d1f08f6
Deleted: deae775642f81c04a7c9987bfd89eb03f8bbad6621fecca6b53e4602262645b6
```
## Conclusion
This lab provided hands-on experience with scripting, web application development, and containerization using Podman. Students learned to automate workflows with Bash scripts, build dynamic web applications in Python, and deploy them in containers. These skills are fundamental to modern software development and DevOps practices. As an introduction, students are encouraged to explore advanced container orchestration tools such as Kubernetes to further scale their applications.