Add development + debugging docker configs

This commit is contained in:
sid palas
2023-02-01 16:15:05 -05:00
parent 15a831b04b
commit bb256c8926
21 changed files with 3752 additions and 26 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
**/node_modules
*.un~
*.json~
.DS_Store
**/tmp

View File

@ -0,0 +1,42 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 0
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

View File

@ -0,0 +1,10 @@
package test
import "testing"
func TestOneEqualsOne(t *testing.T) {
val := 1
if val != 1 {
t.Errorf("1 != %d; want 1", val)
}
}

View File

@ -1,7 +1,12 @@
DATABASE_URL=postgres://postgres:foobarbaz@localhost:5432/postgres
.PHONY: run-local
run-local:
DATABASE_URL=postgres://postgres:foobarbaz@localhost:5432/postgres \
node ./src/index.js
DATABASE_URL=${DATABASE_URL} npm run dev
.PHONY: run-local-debug
run-local-debug:
DATABASE_URL=${DATABASE_URL} npm run debug
.PHONY: build-naive
build-naive:

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,9 @@
"main": "src/index.js",
"scripts": {
"dev": "nodemon src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
"debug": "nodemon --inspect ./src/index.js",
"debug-docker": "nodemon --inspect=0.0.0.0:9229 ./src/index.js",
"test": "jest"
},
"author": "",
"license": "ISC",
@ -15,6 +17,7 @@
"pg": "^8.8.0"
},
"devDependencies": {
"jest": "^29.4.1",
"nodemon": "^2.0.20"
}
}

View File

@ -0,0 +1,3 @@
test('This is a test that always passes', () => {
expect(true).toBe(true);
});

View File

@ -14,6 +14,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^4.24.4",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react-swc": "^3.0.0",
@ -558,21 +559,37 @@
"node": ">=10"
}
},
"node_modules/@tanstack/match-sorter-utils": {
"version": "8.7.6",
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.7.6.tgz",
"integrity": "sha512-2AMpRiA6QivHOUiBpQAVxjiHAA68Ei23ZUMNaRJrN6omWiSFLoYrxGcT6BXtuzp0Jw4h6HZCmGGIM/gbwebO2A==",
"dev": true,
"dependencies": {
"remove-accents": "0.4.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kentcdodds"
}
},
"node_modules/@tanstack/query-core": {
"version": "4.22.4",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.4.tgz",
"integrity": "sha512-t79CMwlbBnj+yL82tEcmRN93bL4U3pae2ota4t5NN2z3cIeWw74pzdWrKRwOfTvLcd+b30tC+ciDlfYOKFPGUw==",
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.24.4.tgz",
"integrity": "sha512-9dqjv9eeB6VHN7lD3cLo16ZAjfjCsdXetSAD5+VyKqLUvcKTL0CklGQRJu+bWzdrS69R6Ea4UZo8obHYZnG6aA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
"version": "4.22.4",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.22.4.tgz",
"integrity": "sha512-e5j5Z88XUQGeEPMyz5XF1V0mMf6Da+6URXiTpZfUb9nuHs2nlNoA+EoIvnhccE5b9YT6Yg7kARhn2L7u94M/4A==",
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.24.4.tgz",
"integrity": "sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==",
"dependencies": {
"@tanstack/query-core": "4.22.4",
"@tanstack/query-core": "4.24.4",
"use-sync-external-store": "^1.2.0"
},
"funding": {
@ -593,6 +610,26 @@
}
}
},
"node_modules/@tanstack/react-query-devtools": {
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.24.4.tgz",
"integrity": "sha512-4mldcR99QDX8k94I+STM9gPsYF+FDAD2EQJvHtxR2HrDNegbfmY474xuW0QUZaNW/vJi09Gak6b6Vy2INWhL6w==",
"dev": true,
"dependencies": {
"@tanstack/match-sorter-utils": "^8.7.0",
"superjson": "^1.10.0",
"use-sync-external-store": "^1.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"@tanstack/react-query": "4.24.4",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@ -663,6 +700,21 @@
"node": ">= 0.8"
}
},
"node_modules/copy-anything": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz",
"integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==",
"dev": true,
"dependencies": {
"is-what": "^4.1.8"
},
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/csstype": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
@ -790,6 +842,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-what": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz",
"integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==",
"dev": true,
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -901,6 +965,12 @@
"react": "^18.2.0"
}
},
"node_modules/remove-accents": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
"integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==",
"dev": true
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -951,6 +1021,18 @@
"node": ">=0.10.0"
}
},
"node_modules/superjson": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz",
"integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==",
"dev": true,
"dependencies": {
"copy-anything": "^3.0.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",

View File

@ -15,6 +15,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^4.24.4",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react-swc": "^3.0.0",

View File

@ -1,9 +1,9 @@
import { useState } from 'react'
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import axios from "axios";
import './App.css'
@ -39,6 +39,7 @@ export function App() {
<h1>Hey Team! 👋</h1>
<Example api="/api/golang/"/>
<Example api="/api/node/"/>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}

View File

@ -23,7 +23,7 @@ Types of improvments:
4) **Specifying a working directory:** Many languages have a convention for how/where applications should be installed. Adhering to that convention will make it easier for developers to work with the container.
5) **Consider layer cache to improve build times:** By undersanding the layered nature of container filesytems and choosing when to copy particular files we can make better use of the Docker caching system.
6) **Use COPY —link where appropriate:** The `--link` option was added to the `COPY` command in march 2022. It allows you to improve cache behavior in certain situations by copying files into an independent image layer not dependent on its predecessors.
7) **Use a non-root user within the container:** While containers can utilize a user namespace to differentiate between root inside the container and root on the host, this feature won't always be leveraged and by using a non-root user we improve the default safety of the container.
7) **Use a non-root user within the container:** While containers can utilize a user namespace to differentiate between root inside the container and root on the host, this feature won't always be leveraged and by using a non-root user we improve the default safety of the container. When using Docker Desktop, the Virtual Machine it runs provides an isolation boundary between containers and the host, but if running Docker Engine it is useful to use a user namespace to ensure container isolation (more info here: https://docs.docker.com/engine/security/userns-remap/). This page also provides a good description for why to avoid running as root: https://cloud.google.com/architecture/best-practices-for-operating-containers#avoid_running_as_root.
8) **Specify the environment correctly:** Only install production dependencies for a production image, and specify any necessary environment variables to configure the language runtime accordingly.
9) **Avoid assumptions:** Using commands like `EXPOSE <PORT>` make it clear to users how the image is intended to be used and avoids the need for them to make assumptions.
10) **Use multi-stage builds where sensible:** For some situations, multi-stage builds can vastly reduce the size of the final image and improve build times. Learn about and use multi-stage builds where appropriate.

View File

@ -38,7 +38,7 @@ docker-run-all:
# Stop and remove all running containers to avoid name conflicts
$(MAKE) docker-stop
$(MAKE) docker-remove
$(MAKE) docker-rm
docker run -d \
--name db \
@ -81,6 +81,7 @@ docker-run-all:
--link=api-golang \
client-react-ngnix
.PHONY: docker-stop
docker-stop:
-docker stop db
-docker stop api-node
@ -88,7 +89,8 @@ docker-stop:
-docker stop client-react-vite
-docker stop client-react-nginx
docker-remove:
.PHONY: docker-rm
docker-rm:
-docker container rm db
-docker container rm api-node
-docker container rm api-golang

View File

@ -10,14 +10,12 @@ export default defineConfig({
server: {
proxy: {
'/api/golang': {
// TODO: Make this inside and outside of docker (e.g. localhost vs api-golang)
target: 'http://api-golang:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/golang/, ''),
secure: false,
},
'/api/node': {
// TODO: Make this inside and outside of docker (e.g. localhost vs api-node)
target: 'http://api-node:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/node/, ''),

View File

@ -1,13 +1,4 @@
services:
client-react-vite:
build:
context: ../05-example-web-application/client-react/
dockerfile: ../../06-building-container-images/client-react/Dockerfile.3
ports:
- 5173:5173
volumes:
- ${PWD}/client-react/vite.config.js:/usr/src/app/vite.config.js
restart: unless-stopped
client-react-nginx:
build:
context: ../05-example-web-application/client-react/

View File

@ -0,0 +1,39 @@
DEV_COMPOSE_FILE=docker-compose-dev.yml
DEBUG_COMPOSE_FILE=docker-compose-debug.yml
### DOCKER COMPOSE COMMANDS
.PHONY: compose-build
compose-build:
docker compose -f $(DEV_COMPOSE_FILE) build
.PHONY: compose-up
compose-up:
docker compose -f $(DEV_COMPOSE_FILE) up
.PHONY: compose-up-build
compose-up-build:
docker compose -f $(DEV_COMPOSE_FILE) up --build
.PHONY: compose-up-debug-build
compose-up-debug-build:
docker compose -f $(DEV_COMPOSE_FILE) -f $(DEBUG_COMPOSE_FILE) up --build
.PHONY: compose-down
compose-down:
docker compose -f $(DEV_COMPOSE_FILE) down
###
DOCKERCONTEXT_DIR:=../05-example-web-application/
DOCKERFILE_DIR:=../10-development-workflow/
.PHONY: docker-build-all
docker-build-all:
docker build -t api-node -f ${DOCKERFILE_DIR}/api-node/Dockerfile.dev ${DOCKERCONTEXT_DIR}/api-node/
docker build -t api-golang -f ${DOCKERFILE_DIR}/api-golang/Dockerfile.dev ${DOCKERCONTEXT_DIR}/api-golang/
.PHONY: run-tests
run-tests:
docker run -t api-golang go test -v ./...
docker run -it api-node npm run test

View File

@ -0,0 +1,22 @@
# Pin specific version for stability
# using bullseye instead of alpine because of:
## runtime/cgo
## cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in $PATH
FROM golang:1.19-bullseye
WORKDIR /app
# Install air for hot reload
RUN go install github.com/cosmtrek/air@latest
# Install delve for debugging
RUN go install github.com/go-delve/delve/cmd/dlv@latest
# Copy only files required to install dependencies (better layer caching)
COPY go.mod go.sum ./
RUN go mod download
COPY . .
CMD ["air", "-c", ".air.toml"]

View File

@ -0,0 +1,19 @@
Remote debugging setup (vscode `launch.json`):
```json
{
"name": "Docker: Attach to Golang",
"type": "go",
"debugAdapter": "dlv-dap",
"mode": "remote",
"request": "attach",
"port": 4000,
"remotePath": "/app",
"substitutePath": [
{
"from": "${workspaceFolder}/docker-course/devops-directive-docker-course/05-example-web-application/api-golang",
"to": "/app"
}
]
}
```

View File

@ -0,0 +1,25 @@
# Pin specific version for stability
# Use alpine for reduced image size
FROM node:19.4-alpine as dev
# Specify working directory other than /
WORKDIR /usr/src/app
# Copy only files required to install
# dependencies (better layer caching)
COPY package*.json ./
# Install only production dependencies
# Use cache mount to speed up install of existing dependencies
RUN --mount=type=cache,target=/usr/src/app/.npm \
npm set cache /usr/src/app/.npm && \
npm install
# Copy remaining source code AFTER installing dependencies.
# Again, copy only the necessary files
COPY . .
# Indicate expected port
EXPOSE 3000
CMD [ "npm", "run", "dev" ]

View File

@ -0,0 +1,12 @@
Remote debugging setup (vscode `launch.json`):
```json
{
"name": "Docker: Attach to Node",
"type": "node",
"request": "attach",
"localRoot": "${workspaceFolder}/docker-course/devops-directive-docker-course/05-example-web-application/api-node",
"remoteRoot": "/usr/src/app",
"port": 9229
},
```

View File

@ -0,0 +1,27 @@
# Overlay configuration to enable debuggers
services:
api-node:
command:
- "npm"
- "run"
- "debug-docker"
ports:
- "3000:3000"
# inspect debug port
- "9229:9229"
api-golang:
command:
- "dlv"
- "debug"
- "/app/main.go"
- "--listen=:4000"
- "--headless=true"
- "--log=true"
- "--log-output=debugger,debuglineerr,gdbwire,lldbout,rpc"
- "--accept-multiclient"
- "--continue"
- "--api-version=2"
ports:
- "8080:8080"
# delve debug port
- "4000:4000"

View File

@ -0,0 +1,58 @@
services:
client-react-vite:
build:
context: ../05-example-web-application/client-react/
dockerfile: ../../06-building-container-images/client-react/Dockerfile.3
ports:
- 5173:5173
volumes:
- type: bind
source: ../05-example-web-application/client-react/src
target: /usr/src/app/src
- type: bind
source: ../08-running-containers/client-react/vite.config.js
target: /usr/src/app/vite.config.js
restart: unless-stopped
api-node:
build:
context: ../05-example-web-application/api-node/
dockerfile: ../../10-development-workflow/api-node/Dockerfile.dev
target: dev
volumes:
- type: bind
source: ../05-example-web-application/api-node/src/
target: /usr/src/app/src/
init: true
depends_on:
- db
environment:
- DATABASE_URL=postgres://postgres:foobarbaz@db:5432/postgres
ports:
- "3000:3000"
restart: unless-stopped
api-golang:
build:
context: ../05-example-web-application/api-golang/
dockerfile: ../../10-development-workflow/api-golang/Dockerfile.dev
volumes:
- type: bind
source: ../05-example-web-application/api-golang/
target: /app/
init: true
depends_on:
- db
environment:
- DATABASE_URL=postgres://postgres:foobarbaz@db:5432/postgres
ports:
- "8080:8080"
restart: unless-stopped
db:
image: postgres:15.1-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=foobarbaz
ports:
- 5432:5432
volumes:
pgdata: