├── .dockerignore
├── .nginx
└── nginx.conf
├── src
├── client
│ ├── index.html
│ └── index.js
└── server
│ └── index.js
├── LICENSE
├── package.json
├── Dockerfile
├── Dockerfile.client
├── .gitignore
└── README.md
/.dockerignore:
--------------------------------------------------------------------------------
1 | dist
2 | build
3 | node_modules
4 | ./src/server/data/messages.json
5 | ./messages.json
6 |
--------------------------------------------------------------------------------
/.nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | listen [::]:80;
4 | server_name localhost;
5 |
6 | root /var/www;
7 |
8 | location / {
9 | try_files $uri $uri/ $uri.html =404;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Frontend
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/client/index.js:
--------------------------------------------------------------------------------
1 | (async () => {
2 | const res = await fetch('http://localhost:3000');
3 | const data = await res.json();
4 |
5 | const app = document.getElementById('app');
6 |
7 | const ul = document.createElement('ul');
8 | app.appendChild(ul);
9 | data.messages.forEach((item) => {
10 | const li = document.createElement('li');
11 | li.innerHTML = item.name;
12 | ul.appendChild(li);
13 | });
14 |
15 | const button = document.createElement('button');
16 | button.innerHTML = 'Create New Message';
17 | app.appendChild(button);
18 | button.addEventListener('click', async () => {
19 | const res = await fetch('http://localhost:3000', {
20 | method: 'POST',
21 | });
22 | const data = await res.json();
23 | ul.innerHTML = '';
24 | data.messages.forEach((item) => {
25 | const li = document.createElement('li');
26 | li.innerHTML = item.name;
27 | ul.appendChild(li);
28 | });
29 | });
30 | })();
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2023 Lukas Aichbauer, MSc (https://devopscycle.com)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "the-ultimate-docker-cheat-sheet",
3 | "version": "0.1.0",
4 | "description": "Learn Docker",
5 | "main": "./src/server.js",
6 | "scripts": {
7 | "cleanup": "rm -rf node_modules && rm -rf build && rm -rf .cache && rm -rf .parcel-cache && rm -rf dist",
8 | "comment": "echo \"All build and start commands are only example commands and would never be used like this in a procution environment\"",
9 | "start:server": "node ./src/server/index.js",
10 | "build:server": "mkdir -p ./build && cp ./src/server/index.js ./build",
11 | "start:client": "mkdir -p ./dist && cp ./src/client/index.js ./dist && cp ./src/client/index.html ./dist && serve ./dist -p 80",
12 | "build:client": "mkdir -p ./dist && cp ./src/client/index.js ./dist && cp ./src/client/index.html ./dist"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+ssh://git@github.com/aichbauer/the-ultimate-docker-cheat-sheet.git"
17 | },
18 | "keywords": [
19 | "docker cheat sheet"
20 | ],
21 | "author": "Lukas Aichbauer, MSc (https://devopscycle.com)",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/aichbauer/the-ultimate-docker-cheat-sheet/issues"
25 | },
26 | "homepage": "https://github.com/aichbauer/the-ultimate-docker-cheat-sheet#readme",
27 | "dependencies": {
28 | "@fastify/cors": "^8.4.1",
29 | "fastify": "^4.24.3"
30 | },
31 | "devDependencies": {
32 | "serve": "^14.2.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/server/index.js:
--------------------------------------------------------------------------------
1 | const fastify = require('fastify');
2 | const cors = require('@fastify/cors');
3 | const fs = require('fs');
4 | const path = require('path');
5 |
6 | const app = fastify({
7 | logger: true,
8 | });
9 | const port = 3000;
10 | const host = '0.0.0.0';
11 |
12 | app.register(cors);
13 |
14 | app.get('/', () => {
15 | try {
16 | const messages = fs.readFileSync(path.join(__dirname, 'data', 'messages.json'));
17 |
18 | return JSON.parse(messages);
19 | } catch {
20 | return {
21 | messages: [
22 | { name: 'Hello World' },
23 | ],
24 | };
25 | }
26 | });
27 |
28 | app.post('/', () => {
29 | try {
30 | if (!fs.existsSync(path.join(__dirname, 'data'))) {
31 | fs.mkdirSync(path.join(__dirname, 'data'), { recursive: true });
32 | }
33 |
34 | if (!fs.existsSync(path.join(__dirname, 'data', 'messages.json'))) {
35 | fs.writeFileSync(path.join(__dirname, 'data', 'messages.json'), JSON.stringify({
36 | messages: [
37 | { name: 'Hello World' },
38 | ],
39 | }));
40 | }
41 |
42 | const messages = fs.readFileSync(path.join(__dirname, 'data', 'messages.json'));
43 |
44 | const parsedMessages = JSON.parse(messages);
45 |
46 | parsedMessages.messages.push({
47 | name: 'New Message',
48 | });
49 |
50 | fs.writeFileSync(path.join(__dirname, 'data', 'messages.json'), JSON.stringify(parsedMessages));
51 |
52 | return parsedMessages;
53 | } catch (err) {
54 | console.error(err);
55 | return {
56 | messages: [
57 | { name: 'Hello World' },
58 | ],
59 | };
60 | }
61 | });
62 |
63 |
64 | const start = async () => {
65 | try {
66 | app.log.info(`Server listening on port ${port}`);
67 | await app.listen({
68 | port,
69 | host,
70 | });
71 | } catch (err) {
72 | app.log.error(err);
73 | process.exit(1);
74 | }
75 | }
76 | start()
77 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # the next line sets the base image for this image
2 | # the base image is also based on an Dockerfile
3 | # see: https://hub.docker.com/layers/library/node/18-alpine/images/sha256-a0b787b0d53feacfa6d606fb555e0dbfebab30573277f1fe25148b05b66fa097
4 | # node provides offical images for nodejs and alpine
5 | # is used as a lightweight linux distribution
6 | # to reduce image size
7 | FROM node:18-alpine
8 |
9 | # we update the package list so that we always install
10 | # the latest version of a package
11 | RUN apk update && apk upgrade --no-cache
12 | # we install tini to have a proper init process
13 | # see: https://github.com/krallin/tini
14 | # this is needed to properly handle signals
15 | # like SIGTERM and SIGINT to stop and kill the container
16 | RUN apk add --no-cache tini
17 | # Tini is now available at /sbin/tini
18 | ENTRYPOINT ["/sbin/tini", "--"]
19 |
20 | # sets the working directory inside the image
21 | # all commands after this insturction will be
22 | # executed inside this directory
23 | WORKDIR /app
24 |
25 | # copies the package.json and package-lock.json
26 | # from the client (e.g. your server or your development machine)
27 | # into the /app directory inside the image
28 | # this is done before running npm ci to
29 | # get the advantage of layer caching
30 | COPY ./package* .
31 |
32 | # installs all node.js dependencies
33 | # npm ci is similar to npm install but intended to be
34 | # used in continuous integration (CI) environments
35 | # it will do a clean install based on the package-lock.json
36 | RUN npm ci
37 |
38 | # copies the source code into the image
39 | COPY . .
40 |
41 | # this runs the build command specified in the package.json
42 | RUN npm run build:server
43 |
44 | # does nothing
45 | # this is documentation so that we know which port is used for that image
46 | EXPOSE 3000
47 |
48 | # executes the server.js file that is located in the build directory
49 | CMD ["node", "./build/index.js"]
50 |
--------------------------------------------------------------------------------
/Dockerfile.client:
--------------------------------------------------------------------------------
1 | # the base image
2 | # name it builder
3 | # you can reference this stage
4 | # in other stages by this name
5 | FROM node:18-alpine as builder
6 |
7 | # working directory inside the image
8 | WORKDIR /app
9 |
10 | # copies files from the client to the image
11 | COPY ./package* .
12 |
13 | # run a command inside the image
14 | RUN npm ci
15 |
16 | # copies files from the client to the image
17 | COPY . .
18 |
19 | # run a command inside the container
20 | # this will create a new folder in called dist in our app directory
21 | # inside the dist directory, you will find the
22 | # final HTML and JavaScript file
23 | RUN npm run build:client
24 |
25 | # serve stage
26 | # unprivileged nginx base image named as serve
27 | # will start nginx as non root user
28 | FROM nginxinc/nginx-unprivileged:1.24 as serve
29 |
30 | # we can now copy things from the first stage to the second
31 | # we copy the build output to the directory where nginx serves files
32 | COPY --from=builder /app/dist /var/www
33 |
34 | # we overwrite the default config with our own
35 | # if you take a look at the GitHub repository, you
36 | # see the .nginx directory with the nginx.conf
37 | # here we only use the port 80
38 | # in production, you would also want to make sure
39 | # all requests, even in your internal network or Kubernetes cluster
40 | # is served via HTTPS when dealing with sensible data
41 | COPY --from=builder /app/.nginx/nginx.conf /etc/nginx/conf.d/default.conf
42 |
43 | # this instruction does not really expose the port 80
44 | # this is documentation so that we know which port is used for that image
45 | # we nee to expose this port via the --publish flag when running the container
46 | EXPOSE 80
47 |
48 | # The command used when the image is started as a container
49 | # Note: for Docker containers (or for debugging),
50 | # the "daemon off;" directive which is used in this example
51 | # tells nginx to stay in the foreground.
52 | # for containers, this is useful.
53 | # best practice: one container = one process.
54 | # one server (container) has only one service.
55 | CMD ["nginx", "-g", "daemon off;"]
56 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | src/server/data/messages.json
2 | messages.json
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 | .pnpm-debug.log*
12 |
13 | # Diagnostic reports (https://nodejs.org/api/report.html)
14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | node_modules/
46 | jspm_packages/
47 |
48 | # Snowpack dependency directory (https://snowpack.dev/)
49 | web_modules/
50 |
51 | # TypeScript cache
52 | *.tsbuildinfo
53 |
54 | # Optional npm cache directory
55 | .npm
56 |
57 | # Optional eslint cache
58 | .eslintcache
59 |
60 | # Optional stylelint cache
61 | .stylelintcache
62 |
63 | # Microbundle cache
64 | .rpt2_cache/
65 | .rts2_cache_cjs/
66 | .rts2_cache_es/
67 | .rts2_cache_umd/
68 |
69 | # Optional REPL history
70 | .node_repl_history
71 |
72 | # Output of 'npm pack'
73 | *.tgz
74 |
75 | # Yarn Integrity file
76 | .yarn-integrity
77 |
78 | # dotenv environment variable files
79 | .env
80 | .env.development.local
81 | .env.test.local
82 | .env.production.local
83 | .env.local
84 |
85 | # parcel-bundler cache (https://parceljs.org/)
86 | .cache
87 | .parcel-cache
88 |
89 | # Next.js build output
90 | .next
91 | out
92 |
93 | # Nuxt.js build / generate output
94 | .nuxt
95 | dist
96 |
97 | # Gatsby files
98 | .cache/
99 | # Comment in the public line in if your project uses Gatsby and not Next.js
100 | # https://nextjs.org/blog/next-9-1#public-directory-support
101 | # public
102 |
103 | # vuepress build output
104 | .vuepress/dist
105 |
106 | # vuepress v2.x temp and cache directory
107 | .temp
108 | .cache
109 |
110 | # Docusaurus cache and generated files
111 | .docusaurus
112 |
113 | # Serverless directories
114 | .serverless/
115 |
116 | # FuseBox cache
117 | .fusebox/
118 |
119 | # DynamoDB Local files
120 | .dynamodb/
121 |
122 | # TernJS port file
123 | .tern-port
124 |
125 | # Stores VSCode versions used for testing VSCode extensions
126 | .vscode-test
127 |
128 | # yarn v2
129 | .yarn/cache
130 | .yarn/unplugged
131 | .yarn/build-state.yml
132 | .yarn/install-state.gz
133 | .pnp.*
134 |
135 | # MAC OS
136 | # General
137 | .DS_Store
138 | .AppleDouble
139 | .LSOverride
140 |
141 | # Icon must end with two \r
142 | Icon
143 |
144 | # Thumbnails
145 | ._*
146 |
147 | # Files that might appear in the root of a volume
148 | .DocumentRevisions-V100
149 | .fseventsd
150 | .Spotlight-V100
151 | .TemporaryItems
152 | .Trashes
153 | .VolumeIcon.icns
154 | .com.apple.timemachine.donotpresent
155 |
156 | # Directories potentially created on remote AFP share
157 | .AppleDB
158 | .AppleDesktop
159 | Network Trash Folder
160 | Temporary Items
161 | .apdisk
162 |
163 | # WINDOWS
164 | # Windows thumbnail cache files
165 | Thumbs.db
166 | Thumbs.db:encryptable
167 | ehthumbs.db
168 | ehthumbs_vista.db
169 |
170 | # Dump file
171 | *.stackdump
172 |
173 | # Folder config file
174 | [Dd]esktop.ini
175 |
176 | # Recycle Bin used on file shares
177 | $RECYCLE.BIN/
178 |
179 | # Windows Installer files
180 | *.cab
181 | *.msi
182 | *.msix
183 | *.msm
184 | *.msp
185 |
186 | # Windows shortcuts
187 | *.lnk
188 |
189 | # LINUX
190 | *~
191 |
192 | # temporary files which can be created if a process still has a handle open of a deleted file
193 | .fuse_hidden*
194 |
195 | # KDE directory preferences
196 | .directory
197 |
198 | # Linux trash folder which might appear on any partition or disk
199 | .Trash-*
200 |
201 | # .nfs files are created when an open file is removed but is still being accessed
202 | .nfs*
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # The Ultimate Docker Cheat Sheet
8 |
9 | > Learn Docker
10 |
11 | The content of this repository can guide you to learn to use Docker. Here is the accompanying blog article: [The Ultimate Docker Cheat Sheet](https://devopscycle.com/blog/the-ultimate-docker-cheat-sheet) with the [PDF](https://devopscycle.com/wp-content/uploads/sites/4/2023/12/the-ultimate-docker-cheat-sheet-1.pdf) or an [image](https://devopscycle.com/wp-content/uploads/sites/4/2023/11/the-ultimate-docker-cheat-sheet-4.png) to the Docker Cheat Sheet. This repository is intended for corporate trainings, university courses and all people (mainly developers and system administrators, but also QA, security experts) that are interested into learning DevOps and especially in automating their processes and tasks to improve the iteration speed, the quality of their work output, and the overall transparancy in their company.
12 |
13 | ## Why?
14 |
15 | It is hard getting started with the technical implementation of DevOps tools. Sharing Knowledge is an important part in DevOps and this is why this repository exists. This repository should give you some guidance on how you can start. This is by no means a silver bullet and also never finished. Another important part is continuous imporvement. You could use this repository as entrypoint for an internal hackathon at your company or your university. Feel free to share your results and learnings as a pull request to this repository.
16 |
17 | Before you start with automating the product lifecycle and implementation of DevOps tools, you should have the correct foundation.
18 |
19 | Start with the culture and the mindset.
20 |
21 | You get a slighty different definition for DevOps when you look at different websites, but the intersection is always culture or the cultural philosophy. So get the key principles straight, then you will be able to profit from the technical tools as well:
22 |
23 | * Colloboration & Communication
24 | * Continuous Improvement
25 | * Automation of the Product Lifecycle
26 | * Customer Centric Action & Short Feedback Loops
27 |
28 | Here are some good resources to get started with colloboration, communication and continuous imporvment:
29 |
30 | * [https://dora.dev/devops-capabilities/cultural/generative-organizational-culture/](https://dora.dev/devops-capabilities/cultural/generative-organizational-culture/)
31 | * [https://dora.dev/devops-capabilities/cultural/learning-culture/](https://dora.dev/devops-capabilities/cultural/learning-culture/)
32 |
33 | ## Prerequistits
34 |
35 | * [Docker](https://docs.docker.com/)
36 | * [Node.js](https://nodejs.org/)
37 |
38 | ## Apps
39 |
40 | In this section you will get an overview of the applications in this repository.
41 |
42 | ### Client
43 |
44 | The client application consits of a HTML and a JS file. The app makes a HTTP Get request to the server application to get all messages and displays them in an unordered list. The App has also an button, which you can click to make a HTTP POST request to add a new message on the server. The server responds with all messages and the client replaces the unordered list items with the new ones. The client is located at `./src/client`.
45 |
46 | ## Development
47 |
48 | In this section you will get an overview of how you can start the client in development.
49 |
50 | ### Start
51 |
52 | ```sh
53 | # start development
54 | # you need to cancel and restart this cmd
55 | # if you want to see changes that you make to the client
56 | $ npm run start:client
57 | ```
58 |
59 | ## Production
60 |
61 | In this section you will get an overview of how you can start the client in production.
62 |
63 | ### Build
64 |
65 | ```sh
66 | # we use Docker to build the images from the Dockerfile.client
67 | # make sure you are in the directory where the Dockerfile.client is located
68 | $ docker build --file Dockerfile.client --tag examplename/examplerepository-client:0.1.0 .
69 | ```
70 |
71 | ### Start
72 |
73 | ```sh
74 | # we can now run the image and publish the container port 80 to the host machine port 80
75 | $ docker run --rm --publish 80:80 examplename/examplerepository-client:0.1.0
76 | ```
77 |
78 | ### Server
79 |
80 | The server consists of a single JS file. It hosts a simple Fastify app with two routes `GET /` and `POST /`.
81 | The GET route will try to load a json file if it exists and reponds with the JSON. the POST route will create a json file if it does not exists and add a new message to the file. The server app is located in `./src/server` directory.
82 |
83 | The json file is located in `./src/server/data` and is called `messages.json`.
84 |
85 | The messages JSON looks like:
86 |
87 | ```json
88 | {
89 | "messages": [
90 | {
91 | "name": "Hello World"
92 | }
93 | ]
94 | }
95 | ```
96 |
97 | ## Development
98 |
99 | In this section you will get an overview of how you can start the server in development.
100 |
101 | ### Start
102 |
103 | ```sh
104 | # start development
105 | # you need to cancel and restart this cmd
106 | # if you want to see changes that you make to the server
107 | $ npm run start:server
108 | ```
109 |
110 | ## Production
111 |
112 | In this section you will get an overview of how you can start the server in production.
113 |
114 | ### Build
115 |
116 | ```sh
117 | # we use Docker to build the images from the Dockerfile.client
118 | # make sure you are in the directory where the Dockerfile is located
119 | $ docker build --file Dockerfile --tag examplename/examplerepository-server:0.1.0 .
120 | ```
121 |
122 | ### Start
123 |
124 | ```sh
125 | # we can now run the image and publish the container port 3000 to the host machine port 3000
126 | # we also persist the messages.json throughout container starts in a volume called server-volume
127 | $ docker run --rm --volume server-volume:/app/build/data --publish 3000:3000 examplename/examplerepository-server:0.1.0
128 | ```
129 |
130 | ## LICENSE
131 |
132 | MIT @ Lukas Aichbauer
133 |
--------------------------------------------------------------------------------