483 views
# 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. ![Rootless Podman container](https://md.inetdoc.net/uploads/6e5bbf8e-da84-4449-8759-5c694cb31fe6.png) ### 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. ![Sample app in browser](https://md.inetdoc.net/uploads/0088dec1-ad66-4e51-aea3-ab4f97bad07c.png) - 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.