diff --git a/README.md b/README.md
index 5bd5e31..6b17a6f 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
:information_source: This repo contains questions and exercises on various technical topics, sometimes related to DevOps and SRE :)
-:bar_chart: There are currently **1825** questions
+:bar_chart: There are currently **1840** questions
:books: To learn more about DevOps and SRE, check the resources in [devops-resources](https://github.com/bregman-arie/devops-resources) repository
@@ -6033,6 +6033,16 @@ It specifies the base layer of the image to be used. Every other instruction is
* WORKDIR: sets the working directory inside the image filesystems for all the instructions following it
* EXPOSE: exposes the specified port (it doesn't adds a new layer, rather documented as image metadata)
* ENTRYPOINT: specifies the startup commands to run when a container is started from the image
+ * ENV: sets an environment variable to the given value
+ * USER: sets the user (and optionally the user group) to use while running the image
+
+
+
+What are some of the best practices regarding writing Dockerfiles that you are following?
+
+ * Include only the packages you are going to use. Nothing else.
+ * Specify a tag in FROM instruction. Not using a tag means you'll always pull the latest, which changes over time and might result in unexpected result.
+ * Do not use environment variables to share secrets
@@ -6333,6 +6343,16 @@ you with more options/features compared to Docker Hub. One example is
Swarm management which means you can create new swarms in Docker Cloud.
+#### Containers - Security
+
+
+A container can cause a kernel panic and bring down the whole host. What preventive actions can you apply to avoid it?
+
+ * Install only the necessary packages in the container
+ * Set volumes and container's filesystem to read only
+ * DO NOT run containers with `--privilged` flag
+
+
#### Containers - Docker in Production
diff --git a/exercises/devops/ci_for_open_source_project.md b/exercises/devops/ci_for_open_source_project.md
index 2012224..4e71194 100644
--- a/exercises/devops/ci_for_open_source_project.md
+++ b/exercises/devops/ci_for_open_source_project.md
@@ -9,4 +9,18 @@
### Bonus
-Containerize the app of the project you forked using any containerization technology you would like.
+Containerize the app of the project you forked using any container engine you would like (e.g. Docker, Podman).
+Once you successfully ran the application in a container, submit the Dockerfile to the original project (but be prepared that the maintainer might not need/want that).
+
+### Suggestions for Projects
+
+The following is a list of projects without CI (at least at the moment):
+
+Note: I wrote a script to find these (except the first project on the list, of course) based on some parameters in case you wonder why these projects specifically are listed.
+
+* [This one](https://github.com/bregman-arie/devops-exercises) - We don't have CI! help! :)
+* [image retrieval platform](https://github.com/skx6/image_retrieval_platform)
+* [FollowSpot](https://github.com/jenbrissman/FollowSpot)
+* [Pyrin](https://github.com/mononobi/pyrin)
+* [food-detection-yolov5](https://github.com/lannguyen0910/food-detection-yolov5)
+* [Lifely](https://github.com/sagnik1511/Lifely)
diff --git a/exercises/devops/containerize_app.md b/exercises/devops/containerize_app.md
index d8ecd5c..424b1d3 100644
--- a/exercises/devops/containerize_app.md
+++ b/exercises/devops/containerize_app.md
@@ -6,18 +6,9 @@
https://github.com/bregman-arie/node-hello-world
https://github.com/bregman-arie/flask-hello-world
```
-
-`git clone https://github.com/bregman-arie/node-hello-world`
-
2. Write a Dockerfile you'll use for building an image of the application (you can use any base image you would like)
-
-```
-FROM alpine
-LABEL maintainer="your name/email"
-RUN apk add --update nodejs nodejs-npm
-COPY . /src
-WORKDIR /src
-RUN npm install
-EXPOSE 8080
-ENTRYPOINT ["node", "./app.js"]
-```
+3. Build an image using the Dockerfile you've just wrote
+4. Verify the image exists
+5. [Optional] Push the image you've just built to a registry
+6. Run the application
+7. Verify the app is running
diff --git a/exercises/devops/solutions/containerize_app.md b/exercises/devops/solutions/containerize_app.md
new file mode 100644
index 0000000..784c089
--- /dev/null
+++ b/exercises/devops/solutions/containerize_app.md
@@ -0,0 +1,54 @@
+## Containerize an Application
+
+1. Clone an open source project you would like to containerize. A couple of suggestions:
+
+```
+https://github.com/bregman-arie/node-hello-world
+https://github.com/bregman-arie/flask-hello-world
+```
+
+`git clone https://github.com/bregman-arie/node-hello-world`
+
+2. Write a Dockerfile you'll use for building an image of the application (you can use any base image you would like)
+
+```
+FROM alpine
+LABEL maintainer="your name/email"
+RUN apk add --update nodejs npm
+COPY . /src
+WORKDIR /src
+RUN npm install
+EXPOSE 3000
+ENTRYPOINT ["node", "./app.js"]
+```
+
+3. Build an image using the Dockerfile you've just wrote
+
+`docker image build -t web_app:latest .`
+
+4. Verify the image exists
+
+`docker image ls`
+
+5. [Optional] Push the image you've just built to a registry
+
+```
+docker login
+docker image tag web_app:latest /web_app:latest
+# Verify with "docker image ls"
+docker image push /web_app:latest
+```
+
+6. Run the application
+
+```
+docker container run -d -p 80:3000 web_app:latest
+```
+
+7. Verify the app is running
+
+```
+docker container ls
+docker logs
+# In the browser, go to 127.0.0.1:80
+```
diff --git a/scripts/random_question.py b/scripts/random_question.py
index 691125d..a0e2b7e 100644
--- a/scripts/random_question.py
+++ b/scripts/random_question.py
@@ -3,48 +3,51 @@ import optparse
def main():
- """ Reads through README.md for question/answer pairs and adds them to a list to randomly select from and quiz yourself.
- - supports skipping quesitons with no documented answer with the -s flag
+ """Reads through README.md for question/answer pairs and adds them to a
+ list to randomly select from and quiz yourself.
+ Supports skipping quesitons with no documented answer with the -s flag
"""
parser = optparse.OptionParser()
- parser.add_option("-s", "--skip", action="store_true",help="skips questions without an answer.", default=False)
+ parser.add_option("-s", "--skip", action="store_true",
+ help="skips questions without an answer.",
+ default=False)
options, args = parser.parse_args()
-
+
with open('README.md', 'r') as f:
text = f.read()
-
+
questions = []
-
+
while True:
question_start = text.find('') + 9
question_end = text.find('
')
answer_end = text.find(' ')
-
+
if answer_end == -1:
break
-
+
question = text[question_start: question_end].replace('
', '').replace('', '')
answer = text[question_end + 17: answer_end]
questions.append((question, answer))
text = text[answer_end + 1:]
-
+
num_questions = len(questions)
-
+
while True:
try:
question, answer = questions[random.randint(0, num_questions)]
-
+
if options.skip and not answer.strip():
continue
-
+
if input(f'Q: {question} ...Show answer? "y" for yes: ').lower() == 'y':
print('A: ', answer)
-
+
except KeyboardInterrupt:
break
-
+
print("\nGoodbye! See you next time.")
-
+
if __name__ == '__main__':
main()