Compare commits
1 Commits
atomist/pi
...
vscode
Author | SHA1 | Date | |
---|---|---|---|
|
2ef123150d |
12
.github/workflows/build.yml
vendored
@@ -1,12 +0,0 @@
|
|||||||
name: Build
|
|
||||||
on: [push, pull_request]
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Build
|
|
||||||
uses: docker/build-push-action@v1
|
|
||||||
with:
|
|
||||||
push: false
|
|
19
Dockerfile
@@ -1,28 +1,23 @@
|
|||||||
# Install the base requirements for the app.
|
# Install the base requirements for the app.
|
||||||
# This stage is to support development.
|
# This stage is to support development.
|
||||||
FROM python:alpine@sha256:c9d3c11e89887c82efeb4f4fee8771a406cf42f41aebbd23148906d5fe3c1426 AS base
|
FROM python:alpine AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
FROM node:12-alpine@sha256:d4b15b3d48f42059a15bd659be60afe21762aae9d6cbea6f124440895c27db68 AS app-base
|
# Run tests to validate app
|
||||||
|
FROM node:12-alpine AS app-base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY app/package.json app/yarn.lock ./
|
COPY app/package.json app/yarn.lock ./
|
||||||
|
RUN yarn install
|
||||||
COPY app/spec ./spec
|
COPY app/spec ./spec
|
||||||
COPY app/src ./src
|
COPY app/src ./src
|
||||||
|
|
||||||
# Run tests to validate app
|
|
||||||
FROM app-base AS test
|
|
||||||
RUN apk add --no-cache python3 g++ make
|
|
||||||
RUN yarn install
|
|
||||||
RUN yarn test
|
RUN yarn test
|
||||||
|
|
||||||
# Clear out the node_modules and create the zip
|
# Clear out the node_modules and create the zip
|
||||||
FROM app-base AS app-zip-creator
|
FROM app-base AS app-zip-creator
|
||||||
COPY app/package.json app/yarn.lock ./
|
RUN rm -rf node_modules && \
|
||||||
COPY app/spec ./spec
|
apk add zip && \
|
||||||
COPY app/src ./src
|
|
||||||
RUN apk add zip && \
|
|
||||||
zip -r /app.zip /app
|
zip -r /app.zip /app
|
||||||
|
|
||||||
# Dev-ready container - actual files will be mounted in
|
# Dev-ready container - actual files will be mounted in
|
||||||
@@ -36,6 +31,6 @@ RUN mkdocs build
|
|||||||
|
|
||||||
# Extract the static content from the build
|
# Extract the static content from the build
|
||||||
# and use a nginx image to serve the content
|
# and use a nginx image to serve the content
|
||||||
FROM nginx:alpine@sha256:082f8c10bd47b6acc8ef15ae61ae45dd8fde0e9f389a8b5cb23c37408642bf5d
|
FROM nginx:alpine
|
||||||
COPY --from=app-zip-creator /app.zip /usr/share/nginx/html/assets/app.zip
|
COPY --from=app-zip-creator /app.zip /usr/share/nginx/html/assets/app.zip
|
||||||
COPY --from=build /app/site /usr/share/nginx/html
|
COPY --from=build /app/site /usr/share/nginx/html
|
||||||
|
2
Jenkinsfile
vendored
@@ -10,7 +10,7 @@ pipeline {
|
|||||||
when {
|
when {
|
||||||
branch 'master'
|
branch 'master'
|
||||||
}
|
}
|
||||||
sh "docker build -t docker/getting-started ."
|
sh "docker build docker/getting-started ."
|
||||||
|
|
||||||
steps {
|
steps {
|
||||||
withDockerRegistry([url: "", credentialsId: "dockerbuildbot-index.docker.io"]) {
|
withDockerRegistry([url: "", credentialsId: "dockerbuildbot-index.docker.io"]) {
|
||||||
|
@@ -37,6 +37,6 @@ docker-compose up
|
|||||||
|
|
||||||
If you find typos or other issues with the tutorial, feel free to create a PR and suggest fixes!
|
If you find typos or other issues with the tutorial, feel free to create a PR and suggest fixes!
|
||||||
|
|
||||||
If you have ideas on how to make the tutorial better or new content, please open an issue first before working on your idea. While we love input, we want to keep the tutorial scoped to newcomers.
|
If you have ideas on how to make the tutorial better or new content, please open an issue first before working on your idea. While we love input, we want to keep the tutorial is scoped to new-comers.
|
||||||
As such, we may reject ideas for more advanced requests and don't want you to lose any work you might
|
As such, we may reject ideas for more advanced requests and don't want you to lose any work you might
|
||||||
have done. So, ask first and we'll gladly hear your thoughts!
|
have done. So, ask first and we'll gladly hear your thoughts!
|
||||||
|
@@ -9,15 +9,13 @@
|
|||||||
"dev": "nodemon src/index.js"
|
"dev": "nodemon src/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"mysql": "^2.17.1",
|
"mysql": "^2.17.1",
|
||||||
"sqlite3": "^5.0.0",
|
"sqlite3": "^4.1.0",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^3.3.3",
|
||||||
"wait-port": "^0.2.2"
|
"wait-port": "^0.2.2"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
|
||||||
"ansi-regex": "5.0.1"
|
|
||||||
},
|
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
@@ -26,8 +24,8 @@
|
|||||||
"singleQuote": true
|
"singleQuote": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^27.2.5",
|
"jest": "^24.9.0",
|
||||||
"nodemon": "^2.0.13",
|
"nodemon": "^1.19.2",
|
||||||
"prettier": "^1.18.2"
|
"prettier": "^1.18.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
const db = require('../../src/persistence/sqlite');
|
const db = require('../../src/persistence/sqlite');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const location = process.env.SQLITE_DB_LOCATION || '/etc/todos/todo.db';
|
|
||||||
|
|
||||||
const ITEM = {
|
const ITEM = {
|
||||||
id: '7aef3d7c-d301-4846-8358-2a91ec9d6be3',
|
id: '7aef3d7c-d301-4846-8358-2a91ec9d6be3',
|
||||||
@@ -9,8 +8,8 @@ const ITEM = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
if (fs.existsSync(location)) {
|
if (fs.existsSync('/etc/todos/todo.db')) {
|
||||||
fs.unlinkSync(location);
|
fs.unlinkSync('/etc/todos/todo.db');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ const addItem = require('./routes/addItem');
|
|||||||
const updateItem = require('./routes/updateItem');
|
const updateItem = require('./routes/updateItem');
|
||||||
const deleteItem = require('./routes/deleteItem');
|
const deleteItem = require('./routes/deleteItem');
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(require('body-parser').json());
|
||||||
app.use(express.static(__dirname + '/static'));
|
app.use(express.static(__dirname + '/static'));
|
||||||
|
|
||||||
app.get('/items', getItems);
|
app.get('/items', getItems);
|
||||||
|
@@ -29,12 +29,11 @@ async function init() {
|
|||||||
user,
|
user,
|
||||||
password,
|
password,
|
||||||
database,
|
database,
|
||||||
charset: 'utf8mb4',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((acc, rej) => {
|
return new Promise((acc, rej) => {
|
||||||
pool.query(
|
pool.query(
|
||||||
'CREATE TABLE IF NOT EXISTS todo_items (id varchar(36), name varchar(255), completed boolean) DEFAULT CHARSET utf8mb4',
|
'CREATE TABLE IF NOT EXISTS todo_items (id varchar(36), name varchar(255), completed boolean)',
|
||||||
err => {
|
err => {
|
||||||
if (err) return rej(err);
|
if (err) return rej(err);
|
||||||
|
|
||||||
|
5034
app/yarn.lock
2
build.sh
@@ -9,6 +9,6 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/amd64,linux/arm64 \
|
--platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 \
|
||||||
-t docker/getting-started:latest \
|
-t docker/getting-started:latest \
|
||||||
$( (( $WILL_PUSH == 1 )) && printf %s '--push' ) .
|
$( (( $WILL_PUSH == 1 )) && printf %s '--push' ) .
|
||||||
|
Before Width: | Height: | Size: 125 KiB |
@@ -1,46 +1,3 @@
|
|||||||
## Security Scanning
|
|
||||||
|
|
||||||
When you have built an image, it is good practice to scan it for security vulnerabilities using the `docker scan` command.
|
|
||||||
Docker has partnered with [Snyk](http://snyk.io) to provide the vulnerability scanning service.
|
|
||||||
|
|
||||||
For example, to scan the `getting-started` image you created earlier in the tutorial, you can just type
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker scan getting-started
|
|
||||||
```
|
|
||||||
|
|
||||||
The scan uses a constantly updated database of vulnerabilities, so the output you see will vary as new
|
|
||||||
vulnerabilities are discovered, but it might look something like this:
|
|
||||||
|
|
||||||
```plaintext
|
|
||||||
✗ Low severity vulnerability found in freetype/freetype
|
|
||||||
Description: CVE-2020-15999
|
|
||||||
Info: https://snyk.io/vuln/SNYK-ALPINE310-FREETYPE-1019641
|
|
||||||
Introduced through: freetype/freetype@2.10.0-r0, gd/libgd@2.2.5-r2
|
|
||||||
From: freetype/freetype@2.10.0-r0
|
|
||||||
From: gd/libgd@2.2.5-r2 > freetype/freetype@2.10.0-r0
|
|
||||||
Fixed in: 2.10.0-r1
|
|
||||||
|
|
||||||
✗ Medium severity vulnerability found in libxml2/libxml2
|
|
||||||
Description: Out-of-bounds Read
|
|
||||||
Info: https://snyk.io/vuln/SNYK-ALPINE310-LIBXML2-674791
|
|
||||||
Introduced through: libxml2/libxml2@2.9.9-r3, libxslt/libxslt@1.1.33-r3, nginx-module-xslt/nginx-module-xslt@1.17.9-r1
|
|
||||||
From: libxml2/libxml2@2.9.9-r3
|
|
||||||
From: libxslt/libxslt@1.1.33-r3 > libxml2/libxml2@2.9.9-r3
|
|
||||||
From: nginx-module-xslt/nginx-module-xslt@1.17.9-r1 > libxml2/libxml2@2.9.9-r3
|
|
||||||
Fixed in: 2.9.9-r4
|
|
||||||
```
|
|
||||||
|
|
||||||
The output lists the type of vulnerability, a URL to learn more, and importantly which version of the relevant library
|
|
||||||
fixes the vulnerability.
|
|
||||||
|
|
||||||
There are several other options, which you can read about in the [docker scan documentation](https://docs.docker.com/engine/scan/).
|
|
||||||
|
|
||||||
As well as scanning your newly built image on the command line, you can also [configure Docker Hub](https://docs.docker.com/docker-hub/vulnerability-scanning/)
|
|
||||||
to scan all newly pushed images automatically, and you can then see the results in both Docker Hub and Docker Desktop.
|
|
||||||
|
|
||||||
{: style=width:75% }
|
|
||||||
{: .text-center }
|
|
||||||
|
|
||||||
## Image Layering
|
## Image Layering
|
||||||
|
|
||||||
@@ -265,6 +222,6 @@ into an nginx container. Cool, huh?
|
|||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
By understanding a little bit about how images are structured, we can build images faster and ship fewer changes.
|
By understanding a little bit about how images are structured, we can build images faster and ship fewer changes.
|
||||||
Scanning images gives us confidence that the containers we are running and distributing are secure.
|
|
||||||
Multi-stage builds also help us reduce overall image size and increase final container security by separating
|
Multi-stage builds also help us reduce overall image size and increase final container security by separating
|
||||||
build-time dependencies from runtime dependencies.
|
build-time dependencies from runtime dependencies.
|
||||||
|
|
||||||
|
@@ -25,19 +25,19 @@ You'll notice a few flags being used. Here's some more info on them:
|
|||||||
docker run -dp 80:80 docker/getting-started
|
docker run -dp 80:80 docker/getting-started
|
||||||
```
|
```
|
||||||
|
|
||||||
## The Docker Dashboard
|
## The VSCode Extension
|
||||||
|
|
||||||
Before going too far, we want to highlight the Docker Dashboard, which gives
|
Before going too far, we want to highlight the Docker VSCode Extension, which gives
|
||||||
you a quick view of the containers running on your machine. It gives you quick
|
you a quick view of the containers running on your machine. It gives you quick
|
||||||
access to container logs, lets you get a shell inside the container, and lets you
|
access to container logs, lets you get a shell inside the container, and lets you
|
||||||
easily manage container lifecycle (stop, remove, etc.).
|
easily manage container lifecycle (stop, remove, etc.).
|
||||||
|
|
||||||
To access the dashboard, follow the instructions in the
|
To access the extension, follow the instructions
|
||||||
[Docker Desktop manual](https://docs.docker.com/desktop/). If you open the dashboard
|
[here](https://code.visualstudio.com/docs/containers/overview). If you open the extension
|
||||||
now, you will see this tutorial running! The container name (`jolly_bouman` below) is a
|
now, you will see this tutorial running! The container name (`angry_taussig` below) is a
|
||||||
randomly created name. So, you'll most likely have a different name.
|
randomly created name. So, you'll most likely have a different name.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## What is a container?
|
## What is a container?
|
||||||
|
@@ -4,12 +4,12 @@ application stack. The following question often arises - "Where will MySQL run?
|
|||||||
container or run it separately?" In general, **each container should do one thing and do it well.** A few
|
container or run it separately?" In general, **each container should do one thing and do it well.** A few
|
||||||
reasons:
|
reasons:
|
||||||
|
|
||||||
- There's a good chance you'd have to scale APIs and front-ends differently than databases.
|
- There's a good chance you'd have to scale APIs and front-ends differently than databases
|
||||||
- Separate containers let you version and update versions in isolation.
|
- Separate containers let you version and update versions in isolation
|
||||||
- While you may use a container for the database locally, you may want to use a managed service
|
- While you may use a container for the database locally, you may want to use a managed service
|
||||||
for the database in production. You don't want to ship your database engine with your app then.
|
for the database in production. You don't want to ship your database engine with your app then.
|
||||||
- Running multiple processes will require a process manager (the container only starts one process),
|
- Running multiple processes will require a process manager (the container only starts one process),
|
||||||
which adds complexity to container startup/shutdown.
|
which adds complexity to container startup/shutdown
|
||||||
|
|
||||||
And there are more reasons. So, we will update our application to work like this:
|
And there are more reasons. So, we will update our application to work like this:
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ For now, we will create the network first and attach the MySQL container at star
|
|||||||
docker network create todo-app
|
docker network create todo-app
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Start a MySQL container and attach it to the network. We're also going to define a few environment variables that the
|
1. Start a MySQL container and attach it the network. We're also going to define a few environment variables that the
|
||||||
database will use to initialize the database (see the "Environment Variables" section in the [MySQL Docker Hub listing](https://hub.docker.com/_/mysql/)).
|
database will use to initialize the database (see the "Environment Variables" section in the [MySQL Docker Hub listing](https://hub.docker.com/_/mysql/)) (replace the ` \ ` characters with `` ` `` in Windows PowerShell).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
@@ -49,17 +49,6 @@ For now, we will create the network first and attach the MySQL container at star
|
|||||||
mysql:5.7
|
mysql:5.7
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using PowerShell then use this command.
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
docker run -d `
|
|
||||||
--network todo-app --network-alias mysql `
|
|
||||||
-v todo-mysql-data:/var/lib/mysql `
|
|
||||||
-e MYSQL_ROOT_PASSWORD=secret `
|
|
||||||
-e MYSQL_DATABASE=todos `
|
|
||||||
mysql:5.7
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll also see we specified the `--network-alias` flag. We'll come back to that in just a moment.
|
You'll also see we specified the `--network-alias` flag. We'll come back to that in just a moment.
|
||||||
|
|
||||||
!!! info "Pro-tip"
|
!!! info "Pro-tip"
|
||||||
@@ -67,20 +56,6 @@ For now, we will create the network first and attach the MySQL container at star
|
|||||||
where MySQL stores its data. However, we never ran a `docker volume create` command. Docker recognizes we want
|
where MySQL stores its data. However, we never ran a `docker volume create` command. Docker recognizes we want
|
||||||
to use a named volume and creates one automatically for us.
|
to use a named volume and creates one automatically for us.
|
||||||
|
|
||||||
!!! info "Troubleshooting"
|
|
||||||
If you see a `docker: no matching manifest` error, it's because you're trying to run the container in a different
|
|
||||||
architecture than amd64, which is the only supported architecture for the mysql image at the moment. To solve this
|
|
||||||
add the flag `--platform linux/amd64` in the previous command. So your new command should look like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
--network todo-app --network-alias mysql --platform linux/amd64 \
|
|
||||||
-v todo-mysql-data:/var/lib/mysql \
|
|
||||||
-e MYSQL_ROOT_PASSWORD=secret \
|
|
||||||
-e MYSQL_DATABASE=todos \
|
|
||||||
mysql:5.7
|
|
||||||
```
|
|
||||||
|
|
||||||
1. To confirm we have the database up and running, connect to the database and verify it connects.
|
1. To confirm we have the database up and running, connect to the database and verify it connects.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -111,8 +86,6 @@ For now, we will create the network first and attach the MySQL container at star
|
|||||||
|
|
||||||
Hooray! We have our `todos` database and it's ready for us to use!
|
Hooray! We have our `todos` database and it's ready for us to use!
|
||||||
|
|
||||||
To exit the sql terminal type `exit` in the terminal.
|
|
||||||
|
|
||||||
|
|
||||||
## Connecting to MySQL
|
## Connecting to MySQL
|
||||||
|
|
||||||
@@ -183,7 +156,7 @@ The todo app supports the setting of a few environment variables to specify MySQ
|
|||||||
|
|
||||||
A more secure mechanism is to use the secret support provided by your container orchestration framework. In most cases,
|
A more secure mechanism is to use the secret support provided by your container orchestration framework. In most cases,
|
||||||
these secrets are mounted as files in the running container. You'll see many apps (including the MySQL image and the todo app)
|
these secrets are mounted as files in the running container. You'll see many apps (including the MySQL image and the todo app)
|
||||||
also support env vars with a `_FILE` suffix to point to a file containing the variable.
|
also support env vars with a `_FILE` suffix to point to a file containing the file.
|
||||||
|
|
||||||
As an example, setting the `MYSQL_PASSWORD_FILE` var will cause the app to use the contents of the referenced file
|
As an example, setting the `MYSQL_PASSWORD_FILE` var will cause the app to use the contents of the referenced file
|
||||||
as the connection password. Docker doesn't do anything to support these env vars. Your app will need to know to look for
|
as the connection password. Docker doesn't do anything to support these env vars. Your app will need to know to look for
|
||||||
@@ -192,11 +165,11 @@ The todo app supports the setting of a few environment variables to specify MySQ
|
|||||||
|
|
||||||
With all of that explained, let's start our dev-ready container!
|
With all of that explained, let's start our dev-ready container!
|
||||||
|
|
||||||
1. We'll specify each of the environment variables above, as well as connect the container to our app network.
|
1. We'll specify each of the environment variables above, as well as connect the container to our app network (replace the ` \ ` characters with `` ` `` in Windows PowerShell).
|
||||||
|
|
||||||
```bash hl_lines="3 4 5 6 7"
|
```bash hl_lines="3 4 5 6 7"
|
||||||
docker run -dp 3000:3000 \
|
docker run -dp 3000:3000 \
|
||||||
-w /app -v "$(pwd):/app" \
|
-w /app -v ${PWD}:/app \
|
||||||
--network todo-app \
|
--network todo-app \
|
||||||
-e MYSQL_HOST=mysql \
|
-e MYSQL_HOST=mysql \
|
||||||
-e MYSQL_USER=root \
|
-e MYSQL_USER=root \
|
||||||
@@ -206,34 +179,6 @@ With all of that explained, let's start our dev-ready container!
|
|||||||
sh -c "yarn install && yarn run dev"
|
sh -c "yarn install && yarn run dev"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you updated your docker file in the Bind Mount section of the tutorial use the updated command:
|
|
||||||
|
|
||||||
```bash hl_lines="3 4 5 6 7"
|
|
||||||
docker run -dp 3000:3000 \
|
|
||||||
-w /app -v "$(pwd):/app" \
|
|
||||||
--network todo-app \
|
|
||||||
-e MYSQL_HOST=mysql \
|
|
||||||
-e MYSQL_USER=root \
|
|
||||||
-e MYSQL_PASSWORD=secret \
|
|
||||||
-e MYSQL_DB=todos \
|
|
||||||
node:12-alpine \
|
|
||||||
sh -c "apk --no-cache --virtual build-dependencies add python2 make g++ && yarn install && yarn run dev"
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are using PowerShell then use this command.
|
|
||||||
|
|
||||||
```powershell hl_lines="3 4 5 6 7"
|
|
||||||
docker run -dp 3000:3000 `
|
|
||||||
-w /app -v "$(pwd):/app" `
|
|
||||||
--network todo-app `
|
|
||||||
-e MYSQL_HOST=mysql `
|
|
||||||
-e MYSQL_USER=root `
|
|
||||||
-e MYSQL_PASSWORD=secret `
|
|
||||||
-e MYSQL_DB=todos `
|
|
||||||
node:12-alpine `
|
|
||||||
sh -c "yarn install && yarn run dev"
|
|
||||||
```
|
|
||||||
|
|
||||||
1. If we look at the logs for the container (`docker logs <container-id>`), we should see a message indicating it's
|
1. If we look at the logs for the container (`docker logs <container-id>`), we should see a message indicating it's
|
||||||
using the mysql database.
|
using the mysql database.
|
||||||
|
|
||||||
@@ -254,7 +199,7 @@ With all of that explained, let's start our dev-ready container!
|
|||||||
is **secret**.
|
is **secret**.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec -it <mysql-container-id> mysql -p todos
|
docker exec -ti <mysql-container-id> mysql -p todos
|
||||||
```
|
```
|
||||||
|
|
||||||
And in the mysql shell, run the following:
|
And in the mysql shell, run the following:
|
||||||
@@ -271,10 +216,10 @@ With all of that explained, let's start our dev-ready container!
|
|||||||
|
|
||||||
Obviously, your table will look different because it has your items. But, you should see them stored there!
|
Obviously, your table will look different because it has your items. But, you should see them stored there!
|
||||||
|
|
||||||
If you take a quick look at the Docker Dashboard, you'll see that we have two app containers running. But, there's
|
If you take a quick look at the Docker extension, you'll see that we have two app containers running. But, there's
|
||||||
no real indication that they are grouped together in a single app. We'll see how to make that better shortly!
|
no real indication that they are grouped together in a single app. We'll see how to make that better shortly!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
|
BIN
docs/tutorial/multi-container-apps/vs-multi-container-app.png
Normal file
After Width: | Height: | Size: 46 KiB |
@@ -38,8 +38,6 @@ see a few flaws in the Dockerfile below. But, don't worry! We'll go over them.
|
|||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM node:12-alpine
|
FROM node:12-alpine
|
||||||
# Adding build tools to make yarn install work on Apple silicon / arm64 machines
|
|
||||||
RUN apk add --no-cache python2 g++ make
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN yarn install --production
|
RUN yarn install --production
|
||||||
@@ -99,10 +97,10 @@ command (remember that from earlier?).
|
|||||||
At this point, you should have a running todo list manager with a few items, all built by you!
|
At this point, you should have a running todo list manager with a few items, all built by you!
|
||||||
Now, let's make a few changes and learn about managing our containers.
|
Now, let's make a few changes and learn about managing our containers.
|
||||||
|
|
||||||
If you take a quick look at the Docker Dashboard, you should see your two containers running now
|
If you take a quick look at the VSCode extension, you should see your two containers running now
|
||||||
(this tutorial and your freshly launched app container)!
|
(this tutorial and your freshly launched app container)!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
BIN
docs/tutorial/our-application/vs-two-containers.png
Normal file
After Width: | Height: | Size: 68 KiB |
@@ -54,10 +54,7 @@ What you'll see is that the files created in one container aren't available in a
|
|||||||
And look! There's no `data.txt` file there! That's because it was written to the scratch space for
|
And look! There's no `data.txt` file there! That's because it was written to the scratch space for
|
||||||
only the first container.
|
only the first container.
|
||||||
|
|
||||||
1. Go ahead and remove the first container using the `docker rm -f <container-id>` command.
|
1. Go ahead and remove the first container using the `docker rm -f` command.
|
||||||
```bash
|
|
||||||
docker rm -f <container-id>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Container Volumes
|
## Container Volumes
|
||||||
|
|
||||||
@@ -94,7 +91,7 @@ Every time you use the volume, Docker will make sure the correct data is provide
|
|||||||
docker volume create todo-db
|
docker volume create todo-db
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Stop the todo app container once again in the Dashboard (or with `docker rm -f <container-id>`), as it is still running without using the persistent volume.
|
1. Stop the todo app container once again in the Dashboard (or with `docker rm -f <id>`), as it is still running without using the persistent volume.
|
||||||
|
|
||||||
1. Start the todo app container, but add the `-v` flag to specify a volume mount. We will use the named volume and mount
|
1. Start the todo app container, but add the `-v` flag to specify a volume mount. We will use the named volume and mount
|
||||||
it to `/etc/todos`, which will capture all files created at the path.
|
it to `/etc/todos`, which will capture all files created at the path.
|
||||||
@@ -108,7 +105,7 @@ Every time you use the volume, Docker will make sure the correct data is provide
|
|||||||
{: style="width: 55%; " }
|
{: style="width: 55%; " }
|
||||||
{: .text-center }
|
{: .text-center }
|
||||||
|
|
||||||
1. Remove the container for the todo app. Use the Dashboard or `docker ps` to get the ID and then `docker rm -f <container-id>` to remove it.
|
1. Remove the container for the todo app. Use the Dashboard or `docker ps` to get the ID and then `docker rm -f <id>` to remove it.
|
||||||
|
|
||||||
1. Start a new container using the same command from above.
|
1. Start a new container using the same command from above.
|
||||||
|
|
||||||
|
@@ -58,7 +58,7 @@ an example command that you will need to run to push to this repo.
|
|||||||
Now that our image has been built and pushed into a registry, let's try running our app on a brand
|
Now that our image has been built and pushed into a registry, let's try running our app on a brand
|
||||||
new instance that has never seen this container image! To do this, we will use Play with Docker.
|
new instance that has never seen this container image! To do this, we will use Play with Docker.
|
||||||
|
|
||||||
1. Open your browser to [Play with Docker](https://labs.play-with-docker.com/).
|
1. Open your browser to [Play with Docker](http://play-with-docker.com).
|
||||||
|
|
||||||
1. Log in with your Docker Hub account.
|
1. Log in with your Docker Hub account.
|
||||||
|
|
||||||
|
@@ -74,17 +74,16 @@ ways that we can remove the old container. Feel free to choose the path that you
|
|||||||
|
|
||||||
### Removing a container using the Docker Dashboard
|
### Removing a container using the Docker Dashboard
|
||||||
|
|
||||||
If you open the Docker dashboard, you can remove a container with two clicks! It's certainly
|
If you open the VSCode Extension, you can remove a container with two clicks! It's certainly
|
||||||
much easier than having to look up the container ID and remove it.
|
much easier than having to look up the container ID and remove it.
|
||||||
|
|
||||||
1. With the dashboard opened, hover over the app container and you'll see a collection of action
|
1. With the extension opened, navigate to the container and right click.
|
||||||
buttons appear on the right.
|
|
||||||
|
|
||||||
1. Click on the trash can icon to delete the container.
|
1. Click on the remove container option
|
||||||
|
|
||||||
1. Confirm the removal and you're done!
|
1. Confirm the removal and you're done!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
### Starting our updated app container
|
### Starting our updated app container
|
||||||
|
BIN
docs/tutorial/updating-our-app/vs-removing-container.png
Normal file
After Width: | Height: | Size: 153 KiB |
@@ -14,7 +14,7 @@ changes and then restart the application. There are equivalent tools in most oth
|
|||||||
## Quick Volume Type Comparisons
|
## Quick Volume Type Comparisons
|
||||||
|
|
||||||
Bind mounts and named volumes are the two main types of volumes that come with the Docker engine. However, additional
|
Bind mounts and named volumes are the two main types of volumes that come with the Docker engine. However, additional
|
||||||
volume drivers are available to support other use cases ([SFTP](https://github.com/vieux/docker-volume-sshfs), [Ceph](https://ceph.com/geen-categorie/getting-started-with-the-docker-rbd-volume-plugin/), [NetApp](https://netappdvp.readthedocs.io/en/stable/), [S3](https://github.com/elementar/docker-s3-volume), and more).
|
volume drivers are available to support other uses cases ([SFTP](https://github.com/vieux/docker-volume-sshfs), [Ceph](https://ceph.com/geen-categorie/getting-started-with-the-docker-rbd-volume-plugin/), [NetApp](https://netappdvp.readthedocs.io/en/stable/), [S3](https://github.com/elementar/docker-s3-volume), and more).
|
||||||
|
|
||||||
| | Named Volumes | Bind Mounts |
|
| | Named Volumes | Bind Mounts |
|
||||||
| - | ------------- | ----------- |
|
| - | ------------- | ----------- |
|
||||||
@@ -36,42 +36,18 @@ So, let's do it!
|
|||||||
|
|
||||||
1. Make sure you don't have any previous `getting-started` containers running.
|
1. Make sure you don't have any previous `getting-started` containers running.
|
||||||
|
|
||||||
1. Also make sure you are in app source code directory, i.e. `/path/to/getting-started/app`. If you aren't, you can `cd` into it, .e.g:
|
1. Run the following command (replace the ` \ ` characters with `` ` `` in Windows PowerShell). We'll explain what's going on afterwards:
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /path/to/getting-started/app
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Now that you are in the `getting-started/app` directory, run the following command. We'll explain what's going on afterwards:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -dp 3000:3000 \
|
docker run -dp 3000:3000 \
|
||||||
-w /app -v "$(pwd):/app" \
|
-w /app -v ${PWD}:/app \
|
||||||
node:12-alpine \
|
node:12-alpine \
|
||||||
sh -c "yarn install && yarn run dev"
|
sh -c "yarn install && yarn run dev"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using PowerShell then use this command.
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
docker run -dp 3000:3000 `
|
|
||||||
-w /app -v "$(pwd):/app" `
|
|
||||||
node:12-alpine `
|
|
||||||
sh -c "yarn install && yarn run dev"
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are using an Apple Silicon Mac or another ARM64 device then use this command.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -dp 3000:3000 \
|
|
||||||
-w /app -v "$(pwd):/app" \
|
|
||||||
node:12-alpine \
|
|
||||||
sh -c "apk add --no-cache python2 g++ make && yarn install && yarn run dev"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `-dp 3000:3000` - same as before. Run in detached (background) mode and create a port mapping
|
- `-dp 3000:3000` - same as before. Run in detached (background) mode and create a port mapping
|
||||||
- `-w /app` - sets the container's present working directory where the command will run from
|
- `-w /app` - sets the "working directory" or the current directory that the command will run from
|
||||||
- `-v "$(pwd):/app"` - bind mount (link) the host's present `getting-started/app` directory to the container's `/app` directory. Note: Docker requires absolute paths for binding mounts, so in this example we use `pwd` for printing the absolute path of the working directory, i.e. the `app` directory, instead of typing it manually
|
- `-v ${PWD}:/app` - bind mount the current directory from the host in the container into the `/app` directory
|
||||||
- `node:12-alpine` - the image to use. Note that this is the base image for our app from the Dockerfile
|
- `node:12-alpine` - the image to use. Note that this is the base image for our app from the Dockerfile
|
||||||
- `sh -c "yarn install && yarn run dev"` - the command. We're starting a shell using `sh` (alpine doesn't have `bash`) and
|
- `sh -c "yarn install && yarn run dev"` - the command. We're starting a shell using `sh` (alpine doesn't have `bash`) and
|
||||||
running `yarn install` to install _all_ dependencies and then running `yarn run dev`. If we look in the `package.json`,
|
running `yarn install` to install _all_ dependencies and then running `yarn run dev`. If we look in the `package.json`,
|
||||||
@@ -93,7 +69,7 @@ So, let's do it!
|
|||||||
When you're done watching the logs, exit out by hitting `Ctrl`+`C`.
|
When you're done watching the logs, exit out by hitting `Ctrl`+`C`.
|
||||||
|
|
||||||
1. Now, let's make a change to the app. In the `src/static/js/app.js` file, let's change the "Add Item" button to simply say
|
1. Now, let's make a change to the app. In the `src/static/js/app.js` file, let's change the "Add Item" button to simply say
|
||||||
"Add". This change will be on line 109 - remember to save the file.
|
"Add". This change will be on line 109.
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
- {submitting ? 'Adding...' : 'Add Item'}
|
- {submitting ? 'Adding...' : 'Add Item'}
|
||||||
|
@@ -33,13 +33,13 @@ docker-compose version
|
|||||||
for the current schema versions and the compatibility matrix.
|
for the current schema versions and the compatibility matrix.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Next, we'll define the list of services (or containers) we want to run as part of our application.
|
1. Next, we'll define the list of services (or containers) we want to run as part of our application.
|
||||||
|
|
||||||
```yaml hl_lines="3"
|
```yaml hl_lines="3"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
```
|
```
|
||||||
@@ -49,11 +49,11 @@ And now, we'll start migrating a service at a time into the compose file.
|
|||||||
|
|
||||||
## Defining the App Service
|
## Defining the App Service
|
||||||
|
|
||||||
To remember, this was the command we were using to define our app container.
|
To remember, this was the command we were using to define our app container (replace the ` \ ` characters with `` ` `` in Windows PowerShell).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -dp 3000:3000 \
|
docker run -dp 3000:3000 \
|
||||||
-w /app -v "$(pwd):/app" \
|
-w /app -v ${PWD}:/app \
|
||||||
--network todo-app \
|
--network todo-app \
|
||||||
-e MYSQL_HOST=mysql \
|
-e MYSQL_HOST=mysql \
|
||||||
-e MYSQL_USER=root \
|
-e MYSQL_USER=root \
|
||||||
@@ -63,25 +63,11 @@ docker run -dp 3000:3000 \
|
|||||||
sh -c "yarn install && yarn run dev"
|
sh -c "yarn install && yarn run dev"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using PowerShell then use this command.
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
docker run -dp 3000:3000 `
|
|
||||||
-w /app -v "$(pwd):/app" `
|
|
||||||
--network todo-app `
|
|
||||||
-e MYSQL_HOST=mysql `
|
|
||||||
-e MYSQL_USER=root `
|
|
||||||
-e MYSQL_PASSWORD=secret `
|
|
||||||
-e MYSQL_DB=todos `
|
|
||||||
node:12-alpine `
|
|
||||||
sh -c "yarn install && yarn run dev"
|
|
||||||
```
|
|
||||||
|
|
||||||
1. First, let's define the service entry and the image for the container. We can pick any name for the service.
|
1. First, let's define the service entry and the image for the container. We can pick any name for the service.
|
||||||
The name will automatically become a network alias, which will be useful when defining our MySQL service.
|
The name will automatically become a network alias, which will be useful when defining our MySQL service.
|
||||||
|
|
||||||
```yaml hl_lines="4 5"
|
```yaml hl_lines="4 5"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -92,7 +78,7 @@ docker run -dp 3000:3000 `
|
|||||||
So, let's go ahead and move that into our file.
|
So, let's go ahead and move that into our file.
|
||||||
|
|
||||||
```yaml hl_lines="6"
|
```yaml hl_lines="6"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -102,11 +88,11 @@ docker run -dp 3000:3000 `
|
|||||||
|
|
||||||
|
|
||||||
1. Let's migrate the `-p 3000:3000` part of the command by defining the `ports` for the service. We will use the
|
1. Let's migrate the `-p 3000:3000` part of the command by defining the `ports` for the service. We will use the
|
||||||
[short syntax](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-1) here, but there is also a more verbose
|
[short syntax](https://docs.docker.com/compose/compose-file/#short-syntax-1) here, but there is also a more verbose
|
||||||
[long syntax](https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-1) available as well.
|
[long syntax](https://docs.docker.com/compose/compose-file/#long-syntax-1) available as well.
|
||||||
|
|
||||||
```yaml hl_lines="7 8"
|
```yaml hl_lines="7 8"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -116,13 +102,13 @@ docker run -dp 3000:3000 `
|
|||||||
- 3000:3000
|
- 3000:3000
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Next, we'll migrate both the working directory (`-w /app`) and the volume mapping (`-v "$(pwd):/app"`) by using
|
1. Next, we'll migrate both the working directory (`-w /app`) and the volume mapping (`-v ${PWD}:/app`) by using
|
||||||
the `working_dir` and `volumes` definitions. Volumes also has a [short](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-3) and [long](https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-3) syntax.
|
the `working_dir` and `volumes` definitions. Volumes also has a [short](https://docs.docker.com/compose/compose-file/#short-syntax-3) and [long](https://docs.docker.com/compose/compose-file/#long-syntax-3) syntax.
|
||||||
|
|
||||||
One advantage of Docker Compose volume definitions is we can use relative paths from the current directory.
|
One advantage of Docker Compose volume definitions is we can use relative paths from the current directory.
|
||||||
|
|
||||||
```yaml hl_lines="9 10 11"
|
```yaml hl_lines="9 10 11"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -138,7 +124,7 @@ docker run -dp 3000:3000 `
|
|||||||
1. Finally, we need to migrate the environment variable definitions using the `environment` key.
|
1. Finally, we need to migrate the environment variable definitions using the `environment` key.
|
||||||
|
|
||||||
```yaml hl_lines="12 13 14 15 16"
|
```yaml hl_lines="12 13 14 15 16"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -159,7 +145,7 @@ docker run -dp 3000:3000 `
|
|||||||
|
|
||||||
### Defining the MySQL Service
|
### Defining the MySQL Service
|
||||||
|
|
||||||
Now, it's time to define the MySQL service. The command that we used for that container was the following:
|
Now, it's time to define the MySQL service. The command that we used for that container was the following (replace the ` \ ` characters with `` ` `` in Windows PowerShell):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
@@ -170,22 +156,11 @@ docker run -d \
|
|||||||
mysql:5.7
|
mysql:5.7
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using PowerShell then use this command.
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
docker run -d `
|
|
||||||
--network todo-app --network-alias mysql `
|
|
||||||
-v todo-mysql-data:/var/lib/mysql `
|
|
||||||
-e MYSQL_ROOT_PASSWORD=secret `
|
|
||||||
-e MYSQL_DATABASE=todos `
|
|
||||||
mysql:5.7
|
|
||||||
```
|
|
||||||
|
|
||||||
1. We will first define the new service and name it `mysql` so it automatically gets the network alias. We'll
|
1. We will first define the new service and name it `mysql` so it automatically gets the network alias. We'll
|
||||||
go ahead and specify the image to use as well.
|
go ahead and specify the image to use as well.
|
||||||
|
|
||||||
```yaml hl_lines="6 7"
|
```yaml hl_lines="6 7"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -197,10 +172,10 @@ docker run -d `
|
|||||||
1. Next, we'll define the volume mapping. When we ran the container with `docker run`, the named volume was created
|
1. Next, we'll define the volume mapping. When we ran the container with `docker run`, the named volume was created
|
||||||
automatically. However, that doesn't happen when running with Compose. We need to define the volume in the top-level
|
automatically. However, that doesn't happen when running with Compose. We need to define the volume in the top-level
|
||||||
`volumes:` section and then specify the mountpoint in the service config. By simply providing only the volume name,
|
`volumes:` section and then specify the mountpoint in the service config. By simply providing only the volume name,
|
||||||
the default options are used. There are [many more options available](https://docs.docker.com/compose/compose-file/compose-file-v3/#volume-configuration-reference) though.
|
the default options are used. There are [many more options available](https://docs.docker.com/compose/compose-file/#volume-configuration-reference) though.
|
||||||
|
|
||||||
```yaml hl_lines="8 9 10 11 12"
|
```yaml hl_lines="8 9 10 11 12"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -217,7 +192,7 @@ docker run -d `
|
|||||||
1. Finally, we only need to specify the environment variables.
|
1. Finally, we only need to specify the environment variables.
|
||||||
|
|
||||||
```yaml hl_lines="10 11 12"
|
```yaml hl_lines="10 11 12"
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -238,7 +213,7 @@ At this point, our complete `docker-compose.yml` should look like this:
|
|||||||
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3.8"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
@@ -318,19 +293,15 @@ Now that we have our `docker-compose.yml` file, we can start it up!
|
|||||||
|
|
||||||
1. At this point, you should be able to open your app and see it running. And hey! We're down to a single command!
|
1. At this point, you should be able to open your app and see it running. And hey! We're down to a single command!
|
||||||
|
|
||||||
## Seeing our App Stack in Docker Dashboard
|
## Seeing our App Stack in the Docker Extension
|
||||||
|
|
||||||
If we look at the Docker Dashboard, we'll see that there is a group named **app**. This is the "project name" from Docker
|
If we look at the Docker Extension, we can changing our grouping options using the 'cog' and 'group by'. In this instance we want to see containers sharing a network.
|
||||||
Compose and used to group the containers together. By default, the project name is simply the name of the directory that the
|
|
||||||
`docker-compose.yml` was located in.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you twirl down the app, you will see the two containers we defined in the compose file. The names are also a little
|
If you twirl down the network, you will see the two containers we defined in the compose file.
|
||||||
more descriptive, as they follow the pattern of `<project-name>_<service-name>_<replica-number>`. So, it's very easy to
|
|
||||||
quickly see what container is our app and which container is the mysql database.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Tearing it All Down
|
## Tearing it All Down
|
||||||
|
BIN
docs/tutorial/using-docker-compose/vs-app-project-collapsed.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
docs/tutorial/using-docker-compose/vs-app-project-expanded.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/tutorial/vs-tutorial-in-extension.png
Normal file
After Width: | Height: | Size: 316 KiB |
@@ -1,5 +1,5 @@
|
|||||||
mkdocs==1.3.0
|
mkdocs==1.0.4
|
||||||
mkdocs-material==4.6.3
|
mkdocs-material==4.6.3
|
||||||
mkdocs-minify-plugin==0.2.3
|
mkdocs-minify-plugin==0.2.3
|
||||||
pygments==2.7.4
|
pygments==2.6.1
|
||||||
pymdown-extensions==7.0
|
pymdown-extensions==7.0
|
||||||
|