You've already forked devops-exercises
Rename exercises dir
Name it instead "topics" so it won't be strange if some topics included "exercises" directory.
This commit is contained in:
107
topics/containers/solutions/commit_image.md
Normal file
107
topics/containers/solutions/commit_image.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Create Images on The Fly
|
||||
|
||||
## Requirements
|
||||
|
||||
Have at least one image locally (run `podman image ls` to confirm).<br>
|
||||
If you don't have images locally, run simply `podman pull nginx:alpine`.
|
||||
|
||||
## Objectives
|
||||
|
||||
1. Run a container using a web server image (e.g. httpd, nginx, ...)
|
||||
- Bind container's port 80 to local port 80
|
||||
- Run it in detached mode
|
||||
- Name should nginx_container
|
||||
2. Verify the web server runs and accessible
|
||||
3. Create an HTML file with the following content and copy it to the container to the container to path where it will be accessed as an index file
|
||||
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<title>It's a me</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Mario</h1>
|
||||
</body>
|
||||
```
|
||||
|
||||
4. Create an image out of the running container and call it "nginx_mario"
|
||||
5. Tag the container with "mario" tag
|
||||
6. Remove the original container (container_nginx) and verify it was removed
|
||||
7. Create a new container out of the image you've created (the same way as the original container)
|
||||
8. Run `curl 127.0.0.1:80`. What do you see?
|
||||
9. Run `podman diff` on the new image. Explain the output
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
# Run the container
|
||||
podman run --name nginx_container -d -p 80:80 nginx:alpine
|
||||
|
||||
# Verify web server is running
|
||||
curl 127.0.0.1:80
|
||||
# <!DOCTYPE html>
|
||||
# <html>
|
||||
# <head>
|
||||
# <title>Welcome to nginx!</title>
|
||||
|
||||
# Create index.html file
|
||||
cat <<EOT >>index.html
|
||||
<html>
|
||||
<head>
|
||||
<title>It's a me</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Mario</h1>
|
||||
</body>
|
||||
EOT
|
||||
|
||||
# Copy index.html to the container
|
||||
podman cp index.html nginx_container:/usr/share/nginx/html/index.html
|
||||
|
||||
# Create a new image out of the running container
|
||||
podman commit nginx_container nginx_mario
|
||||
|
||||
# Tag the image
|
||||
podman image ls
|
||||
# localhost/nginx_mario latest dc7ed2343521 52 seconds ago 25 MB
|
||||
podman tag dc7ed2343521 mario
|
||||
|
||||
# Remove the container
|
||||
podman stop nginx_container
|
||||
podman rm nginx_container
|
||||
podman ps -a # no container 'nginx_container'
|
||||
|
||||
# Create a container out of the image
|
||||
podman run -d -p 80:80 nginx_mario
|
||||
|
||||
# Check the container created from the new image
|
||||
curl 127.0.0.1:80
|
||||
#<html>
|
||||
#<head>
|
||||
#<title>It's a me</title>
|
||||
#</head>
|
||||
#<body>
|
||||
#<h1>Mario</h1>
|
||||
#</body>
|
||||
|
||||
# Run diff
|
||||
podman diff nginx_mario
|
||||
|
||||
C /etc
|
||||
C /etc/nginx/conf.d
|
||||
C /etc/nginx/conf.d/default.conf
|
||||
A /run/nginx.pid
|
||||
C /usr/share/nginx/html
|
||||
C /usr/share/nginx/html/index.html
|
||||
C /var/cache/nginx
|
||||
C /var
|
||||
C /var/cache
|
||||
A /var/cache/nginx/client_temp
|
||||
A /var/cache/nginx/fastcgi_temp
|
||||
A /var/cache/nginx/proxy_temp
|
||||
A /var/cache/nginx/scgi_temp
|
||||
A /var/cache/nginx/uwsgi_temp
|
||||
|
||||
# We've set new index.html which explains why it's changed (C)
|
||||
# We also created the image while the web server is running, which explains all the files created under /var
|
||||
```
|
||||
26
topics/containers/solutions/containerized_db.md
Normal file
26
topics/containers/solutions/containerized_db.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Containerized DB
|
||||
|
||||
1. Run a container with a database of any type of you prefer (MySql, PostgreSQL, Mongo, etc.)
|
||||
2. Verify the container is running
|
||||
3. Access the container and create a new table (or collection, depends on which DB type you chose) for students
|
||||
4. Insert a row (or document) of a student
|
||||
5. Verify the row/document was added
|
||||
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
# Run the container
|
||||
podman run --name mysql -e MYSQL_USER=mario -e MYSQL_PASSWORD=tooManyMushrooms -e MYSQL_DATABASE=university -e MYSQL_ROOT_PASSWORD=MushroomsPizza -d mysql
|
||||
|
||||
# Verify it's running
|
||||
podman ps
|
||||
|
||||
# Add student row to the database
|
||||
podman exec -it mysql /bin/bash
|
||||
mysql -u root
|
||||
use university;
|
||||
CREATE TABLE Students (id int NOT NULL, name varchar(255) DEFAULT NULL, PRIMARY KEY (id));
|
||||
insert into Projects (id, name) values (1,'Luigi');
|
||||
select * from Students;
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# Containerized DB with Persistent Storage
|
||||
|
||||
1. Run a container with a database of any type of you prefer (MySql, PostgreSQL, Mongo, etc.)
|
||||
1. Use a mount point on the host for the database instead of using the container storage for that
|
||||
2. Explain why using the host storage instead of the container one might be a better choice
|
||||
2. Verify the container is running
|
||||
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
# Create the directory for the DB on host
|
||||
mkdir -pv ~/local/mysql
|
||||
sudo semanage fcontext -a -t container_file_t '/home/USERNAME/local/mysql(/.*)?'
|
||||
sudo restorecon -R /home/USERNAME/local/mysql
|
||||
|
||||
# Run the container
|
||||
podman run --name mysql -e MYSQL_USER=mario -e MYSQL_PASSWORD=tooManyMushrooms -e MYSQL_DATABASE=university -e MYSQL_ROOT_PASSWORD=MushroomsPizza -d mysql -v /home/USERNAME/local/mysql:/var/lib/mysql/db
|
||||
|
||||
# Verify it's running
|
||||
podman ps
|
||||
```
|
||||
|
||||
It's better to use the storage host because in case the container ever gets removed (or storage reclaimed) you have the DB data still available.
|
||||
24
topics/containers/solutions/containerized_web_server.md
Normal file
24
topics/containers/solutions/containerized_web_server.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Containerized Web Server
|
||||
|
||||
1. Run a containerized web server in the background and bind its port (8080) to a local port
|
||||
2. Verify the port (8080) is bound
|
||||
3. Reach the webserver from your local host
|
||||
4. Now run the same web application but bound it to the local port 8080
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
$ podman run -d -p 8080 httpd # run the container and bind the port 8080 to a local port
|
||||
$ podman port -l 8080 # show to which local port the port 8080 on the container, binds to
|
||||
0.0.0.0:41203
|
||||
$ curl http://0.0.0.0:41203 # use the port from the output of the previous command
|
||||
|
||||
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Test Page for the HTTP Server on Red Hat Enterprise Linux</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
$ podman run -d -p 8080:8080 httpd
|
||||
```
|
||||
54
topics/containers/solutions/image_layers.md
Normal file
54
topics/containers/solutions/image_layers.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## Layer by Layer
|
||||
|
||||
### Objective
|
||||
|
||||
Learn about image layers
|
||||
|
||||
### Requirements
|
||||
|
||||
Make sure Docker is installed on your system and the service is started
|
||||
|
||||
```
|
||||
# Fedora/RHEL/CentOS
|
||||
rpm -qa | grep docker
|
||||
systemctl status docker
|
||||
```
|
||||
|
||||
### Instructions
|
||||
|
||||
1. Write a Dockefile. Any Dockefile! :) (just make sure it's a valid one)
|
||||
|
||||
```
|
||||
FROM ubuntu
|
||||
EXPOSE 212
|
||||
ENV foo=bar
|
||||
WORKDIR /tmp
|
||||
RUN dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024
|
||||
RUN dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024
|
||||
RUN dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024
|
||||
```
|
||||
|
||||
2. Build an image using the Dockerfile you've wrote
|
||||
|
||||
`docker image build -t super_cool_app:latest .`
|
||||
|
||||
3. Which of the instructions you've used, created new layers and which added image metadata?
|
||||
|
||||
```
|
||||
FROM, RUN -> new layer
|
||||
EXPOSE, ENV, WORKDIR -> metadata
|
||||
```
|
||||
|
||||
4. What ways are there to confirm your answer to the last question?
|
||||
|
||||
You can run `docker image history super_cool_app`. It will show you each instruction and its size. Usually instructions that create new layers has non-zero size, but this is not something you can rely on by itself since, some run commands can have size of zero in `docker image history` output (e.g. `ls -l`).
|
||||
|
||||
You can also use `docker image inspect super_cool_appl` and see if in the output, under "RootFS", there are the number of layers that matches the instructions that should create new layers.
|
||||
|
||||
5. Can you reduce the size of the image you've created?
|
||||
|
||||
yes, for example, use all the RUN instructions as a single RUN instruction this way:
|
||||
|
||||
`RUN dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024 && dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024 && dd if=/dev/zero of=some_file bs=1024 count=0 seek=1024`
|
||||
|
||||
The change in size might not be dramatic in this case, but in some cases it will make a big impact on the image size.
|
||||
58
topics/containers/solutions/multi_stage_builds.md
Normal file
58
topics/containers/solutions/multi_stage_builds.md
Normal file
@@ -0,0 +1,58 @@
|
||||
## Multi-Stage Builds
|
||||
|
||||
### Objective
|
||||
|
||||
Learn about multi-stage builds
|
||||
|
||||
### Instructions
|
||||
|
||||
1. Without actually building an image or running any container, use the following Dockerfile and convert it to use multi-stage:
|
||||
|
||||
```
|
||||
FROM nginx
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y curl python build-essential \
|
||||
&& apt-get install -y nodejs \
|
||||
&& apt-get clean -y
|
||||
RUN mkdir -p /my_app
|
||||
ADD ./config/nginx/docker.conf /etc/nginx/nginx.conf
|
||||
ADD ./config/nginx/k8s.conf /etc/nginx/nginx.conf.k8s
|
||||
ADD app/ /my_cool_app
|
||||
WORKDIR /my_cool_app
|
||||
RUN npm install -g ember-cli
|
||||
RUN npm install -g bower
|
||||
RUN apt-get update && apt-get install -y git \
|
||||
&& npm install \
|
||||
&& bower install \
|
||||
RUN ember build — environment=prod
|
||||
CMD [ “/root/nginx-app.sh”, “nginx”, “-g”, “daemon off;” ]
|
||||
```
|
||||
|
||||
2. What are the benefits of using multi-stage builds?
|
||||
|
||||
### Solution
|
||||
|
||||
1. One possible solution (the emphasize is on passing the app from the first stage):
|
||||
|
||||
```
|
||||
FROM node:6
|
||||
RUN mkdir -p /my_cool_app
|
||||
RUN npm install -g ember-cli
|
||||
RUN npm install -g bower
|
||||
WORKDIR /my_cool_app
|
||||
RUN npm install
|
||||
ADD app/ /my_cool_app
|
||||
RUN bower install
|
||||
RUN ember build — environment=prod
|
||||
|
||||
FROM nginx
|
||||
RUN mkdir -p /my_cool_app
|
||||
ADD ./config/nginx/docker.conf /etc/nginx/nginx.conf
|
||||
ADD ./config/nginx/k8s.conf /etc/nginx/nginx.conf.k8s
|
||||
# Copy build artifacts from the first stage
|
||||
COPY — from=0 /my_cool_app/dist /my_cool_app/dist
|
||||
WORKDIR /my_cool_app
|
||||
CMD [ “/root/nginx-app.sh”, “nginx”, “-g”, “daemon off;” ]
|
||||
```
|
||||
|
||||
2. Multi-stages builds allow you to produce smaller container images by splitting the build process into multiple stages as we did above. The app image doesn't contain anything related to the build process except the actual app.
|
||||
71
topics/containers/solutions/run_forest_run.md
Normal file
71
topics/containers/solutions/run_forest_run.md
Normal file
@@ -0,0 +1,71 @@
|
||||
## Run, Forest, Run!
|
||||
|
||||
### Objective
|
||||
|
||||
Learn what restart policies do and how to use them
|
||||
|
||||
### Requirements
|
||||
|
||||
Make sure Docker is installed on your system and the service is started
|
||||
|
||||
```
|
||||
# Fedora/RHEL/CentOS
|
||||
rpm -qa | grep docker
|
||||
systemctl status docker
|
||||
```
|
||||
|
||||
### Instructions
|
||||
|
||||
1. Run a container with the following properties:
|
||||
* image: alpine
|
||||
* name: forest
|
||||
* restart policy: always
|
||||
* command to execute: sleep 15
|
||||
|
||||
`docker run --restart always --name forest alpine sleep 15`
|
||||
|
||||
2. Run `docker container ls` - Is the container running? What about after 15 seconds, is it still running? why?
|
||||
|
||||
|
||||
It runs even after it completes to run `sleep 15` because the restart policy is "always". This means that Docker will keep restarting the **same** container even after it exists.
|
||||
|
||||
|
||||
3. How then can we stop the container from running?
|
||||
|
||||
The restart policy doesn't apply when the container is stopped with the command `docker container stop`
|
||||
|
||||
4. Remove the container you've created
|
||||
|
||||
```
|
||||
docker container stop forest
|
||||
docker container rm forest
|
||||
```
|
||||
|
||||
5. Run the same container again but this time with `sleep 600` and verify it runs
|
||||
|
||||
```
|
||||
docker run --restart always --name forest alpine sleep 600
|
||||
docker container ls
|
||||
```
|
||||
|
||||
6. Restart the Docker service. Is the container still running? why?
|
||||
|
||||
```
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
Yes, it's still running due to the restart policy `always` which means Docker will always bring up the container after it exists or stopped (not with the stop command).
|
||||
|
||||
8. Update the policy to `unless-stopped`
|
||||
|
||||
`docker update --restart unless-stopped forest`
|
||||
|
||||
9. Stop the container
|
||||
|
||||
`docker container stop forest`
|
||||
|
||||
10. Restart the Docker service. Is the container running? why?
|
||||
|
||||
```
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
No, the container is not running. This is because we changed the policy to `unless-stopped` which will run the container unless it was in stopped status. Since before the restart we stopped the container, Docker didn't continue running it after the restart.
|
||||
18
topics/containers/solutions/running_containers.md
Normal file
18
topics/containers/solutions/running_containers.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## Running Containers
|
||||
|
||||
### Objective
|
||||
|
||||
Learn how to run, stop and remove containers
|
||||
|
||||
### Requirements
|
||||
|
||||
Make sure Podman or Docker (or any other containers engine) is installed on your system
|
||||
|
||||
### Instructions
|
||||
|
||||
1. Run a container using the latest nginx image - `podman container run nginx:latest`
|
||||
2. List the containers to make sure the container is running - `podman container ls`
|
||||
3. Run another container but this time use ubuntu latest and attach to the terminal of the container - `podman container run -it ubuntu:latest /bin/bash`
|
||||
4. List again the containers. How many containers are running? - `podman container ls` -> 2
|
||||
5. Stop the containers - WARNING: the following will stop all the containers on the host: `podman stop $(podman container ls -q)` or for each container `podman stop [container id/name]`
|
||||
6. Remove the containers - WARNING: the following will remove other containers as well if such are running: `podman rm $(podman container ls -q -a)` or for each container `podman rm [container id/name]`
|
||||
35
topics/containers/solutions/sharing_images.md
Normal file
35
topics/containers/solutions/sharing_images.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Sharing Images
|
||||
|
||||
## Requirements
|
||||
|
||||
Have at least one image locally (run `podman image ls` to confirm).<br>
|
||||
If you don't have images locally, run simply `podman pull httpd`.
|
||||
|
||||
## Objectives
|
||||
|
||||
1. Choose an image and create an archive out of it
|
||||
2. Check the archive size. Is it different than the image size? If yes, what's the difference? If not, why?
|
||||
3. Copy the generated archive to a remote host
|
||||
4. Load the image
|
||||
5. Verify it was loaded and exists on the remote host
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
# Save image as an archive
|
||||
podman save -o httpd.tar httpd
|
||||
|
||||
# Check archive and image sizes
|
||||
du -sh httpd.tar # output: 143MB
|
||||
podman image ls | grep httpd # output: 149MB
|
||||
# The archive is obviously smaller than the image itself (6MB difference)
|
||||
|
||||
# Copy the archive to a remote host
|
||||
rsync -azc httpd.tar USER@REMOTE_HOST_FQDN:/tmp/
|
||||
|
||||
# Load the image
|
||||
podman load -i /tmp/httpd.tar
|
||||
|
||||
# Verify it exists on the system after loading
|
||||
podman image ls
|
||||
```
|
||||
17
topics/containers/solutions/working_with_images.md
Normal file
17
topics/containers/solutions/working_with_images.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## Working with Images - Solution
|
||||
|
||||
### Objective
|
||||
|
||||
Learn how to work with containers images
|
||||
|
||||
### Requirements
|
||||
|
||||
Make sure Podman, Docker (or any other containers engine) is installed on your system
|
||||
|
||||
### Instructions
|
||||
|
||||
1. List the containers images in your environment - `podman image ls`
|
||||
2. Pull the latest ubuntu image - `podman image pull ubuntu:latest`
|
||||
3. Run a container with the image you just pulled - `podman container run -it ubuntu:latest /bin/bash`
|
||||
4. Remove the image. Did it work? - No. There is a running container which is using the image we try to remove
|
||||
5. Do whatever is needed in order to remove the image - `podman rm <container_id>; podman image rm ubuntu`
|
||||
Reference in New Issue
Block a user