documents/dev/docker.md

Docker

Mac Instructions

use colima

brew install colima
colima start

Commands

Container vs Image. A Docker container is a self-contained, runnable software application or service. On the other hand, a Docker image is the template loaded onto the container to run it, like a set of instructions. You store images for sharing and reuse, but you create and destroy containers over an application's lifecycle.

General

docker info
docker ps -a

Container

# runs a new command in a running conainer. interactive. tty (allocate a pseudo-TTY)
docker exec -it mycontainer sh

# creates (touch) a new file (/tmp/execWorks) inside container mycontainer in the background (-d for detached)
docker exec -d mycontainer touch /tmp/execWorks

# runs a command in a new container, pulling the image if needed and starting the container
# -d runs contaienr in detached/bg mode
# -p 8080:80 maps port 8080 of host to 80 of container
docker run -dp 8080:80 --rm docker/getting-started

# stop & remove container
docker stop <container-id>
docker rm <container-id>

Images

# uses the Dockerfile to build a new container image.
# "-t" tags the image -> human-readable name for the final image.
# "." at the end tells where to look for Dockerfile
docker build -t mycontainer .

# list & remove
docker image list
docker image rm IMAGE_ID

# tags are just human-readable aliases for the image ID
docker tag # alias to docker image tag

# create another tag using image ID
docker image tag IMAGE_ID myname/server:latest

# Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
docker image tag source_image:tag target_image:tag

# tag with ECR
docker tag ventz/whisper:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/whisper:latest

# removing a tag
docker rmi myname/server 

# sharing the app / pushing an image via Elastic Container Registry (ECR)
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/whisper:latestdocker tag ventz/whisper:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/whisper:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/whisper:latest

AWS ECR Containers

# build container
docker build --rm=true --force-rm=true -t scriptsync/tts container

# tag it with ECR url (create container called "tts" in ECR first)
docker tag scriptsync/tts:latest 992382755464.dkr.ecr.us-east-1.amazonaws.com/tts:latest

# push the image
docker push 992382755464.dkr.ecr.us-east-1.amazonaws.com/tts:latest

# deploy function
aws lambda create-function --region us-east-1 --function-name tts \
  --package-type Image \
  --code ImageUri=992382755464.dkr.ecr.us-east-1.amazonaws.com/tts:latest \
  --role arn:aws:iam::992382755464:role/transcribe-lambda-role

# update function
aws lambda update-function-code --function-name tts --image-uri 992382755464.dkr.ecr.us-east-1.amazonaws.com/tts:latest

Running dockerized AWS lambda locally

Assumes Dockerfile has CMD ["app.handler"] / Additional info to test locally: https://aws.amazon.com/blogs/compute/a-guide-to-locally-testing-containers-with-amazon-ecs-local-endpoints-and-docker-compose/

# first run the docker image
docker run --env-file .env -p 9000:8080 --rm scriptsync/tts:latest 

# then test by making a post request (path is very specific)
curl -XPOST "http://localhost:9001/2015-03-31/functions/function/invocations" -d '{"foo": "bar"}'

# to run without having to provide the path, add this nginx config
server {
  server_name tts.scriptsync.app;
  location / {
    proxy_pass http://localhost:9000/2015-03-31/functions/function/invocations;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

# then invoke using
curl -XPOST "https://tts.scriptsync.app" -d '{"foo": "bar"}'

Starting a Dev-Mode Container

To run our container to support a development workflow, we will do the following:

  • Mount our source code into the container
  • Install all dependencies, including the "dev" dependencies
  • Start nodemon to watch for filesystem changes
cd /path/to/getting-started/app

# for Unix
# `-w /app` sets container's present working directory where command will run from
# `-v "$(pwd):/app"` bind mount (link) the host's present getting-started/app directory to the container's /app directory. Docker requires absolute paths for binding mounts, so we use pwd for printing abs path of working dir
# `node:18-alpine` - the image to use (base image for app from Dockerfile)
# `sh -c "yarn install && yarn run dev"` - start shell using sh and running yarn install, then run dev
docker run -dp 3000:3000 \
    -w /app -v "$(pwd):/app" \
    node:18-alpine \
    sh -c "yarn install && yarn run dev"

# to see when app is ready
docker logs -f <container-id>

# for powershell
docker run -dp 3000:3000 `
    -w /app -v "$(pwd):/app" `
    node:18-alpine `
    sh -c "yarn install && yarn run dev"

Multi-container Apps

Put container on a network. Assign it at start or connect an existing container.

# create teh network
docker network create todo-app

# start a MySQL container and attach it to network
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:8.0

# connect to database and verify it connects / password is "secret"
docker exec -it <mysql-container-id> mysql -p
mysql> SHOW DATABASES;

# start a new container using netshoot image and connect to the same network
docker run -it --network todo-app nicolaka/netshoot
dig mysql # inside the container, run dig to verify / look up IP of the hostname mysql

# run app with mysql info
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:18-alpine \
  sh -c "yarn install && yarn run dev"

TRANSCRIBE

lambda: upload mp3

trigger: api gateway - user upload function: save file to s3 return: job_id

lambda: TTS

trigger: api gateway - text/voice/service function:

  • save file to s3
  • transcribe lambda return:
{
  mp3_signed_s3_url: "",
  transcription: {}
}

lambda: on s3 file upload event - run whisper transcribe

trigger: s3 - mp3 file put (extracted mp3 or TTS) function: whisper transcribe, save transcription file to s3 return: job_id