## Tools - [[Portainer]] - [[Docker Compose]] ## Tips And Tricks ### Modifying a Docker Container's Filesystem ```bash docker exec-it container bash ``` > From here, you are free to use normal Linux commands. If you want to do this remotely, you can [install an SSH server in your container](https://www.cloudsavvyit.com/13937/how-to-ssh-into-a-docker-container/), and bind port 22 to another port on the host. ## Docker File ### Tips - consider each line of the docker a layer of the abstraction this comes into play in the next example. - Docker files MUST start with `FROM` but after that you can also add data like: ```docker FROM python:3.9.7 MAINTAINER ``` - When you build your docker image from a docker file with [[Docker#build]] each command in the file create a new image and the layers are plastered on top, but each layer is cached so when you change things iteratively, only the changed items onward get re-ran. Essentially lazy loading. ### Base example of having to install all your dependencies and everything Negates the utility of docker if the image is not just "good to go" but here's how to do this --- ```docker FROM python:3.9.7 WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] ``` --- - `FROM python:3.9.7` - Specify what python version to run in the base image - "python" is the image - "3.9.7" is the version of that image - `WORKDIR /usr/src/app` - "/usr/src" is a valid path in the image - "/app" is the path we want to tack on and create for our app code - `COPY requirements.txt ./` - Grab the requirements file into the image - `RUN pip install --no-cache-dir -r requirements.txt` - Install dependencies - `COPY . .` - "." first dot is current directory, second "." is `WORKDIR` - Move our source code into the image - This is where layering comes into play, when you change source code files - The only thing that changed was source code so this step and below is what gets re-ran - Each layers results are cached, so when you change something it only re-runs steps where something has changed compared to the cached image layers. - Only changed source code? then only re-run the step where we copy over source code files - `CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]` - The array of commands to run once the image is spun up - each array items is either a switch or a param - essentially any time you have a space between anything in the command that is when you make a separate item in the array for it. At this point you can now create the image using [[Docker#build]] ### Security Specify a `USER` in the docker file like `USER 1000` so that processes do not run as root and prevent potential security risk ### Multi-Stage Reduce container size by not building on top of a big image ```docker FROM golang:1.14.2-alpine3.11 AS builder ENV GOPATH /go WORKDIR /$GOPATH/src/croc-hunter/ COPY croc-hunter.go /go/src/croc-hunter/ RUN go get -d -v RUN go build -o /go/bin/croc-hunter FROM alpine:3.11 AS runtime USER 1000 WORKDIR /app COPY static/ static/ COPY --from=builder /go/bin/croc-hunter /app/croc-hunter EXPOSE 8080 CMD [ "/app/croc-hunter" ] ``` ### Dynamic Port exposure you can set the docker file to use `EXPOSE ${PORT}` in the docker file and then pass in that value during the `docker run` command. It also helps to have the deployed code rely on the environmental variable as well so everything is dynamic based on the arguments fed to the container: ```bash docker run -e PORT=3000 -p 3000:3000 --name croc-hunter croc-hunter-port:1 ``` ## Registries https://youtu.be/ZK-3H8_bHKY Like the github of docker images. Can host your own for private image storage on portainer with an app template registry docker image To utilize you will need to make sure it is open on port 5000 and then on the portainer host edit the `/etc/docker/daemon.json` file to contain: ```json { "insecure-reigstries": ["ip-address:port"] } ``` Then save and close then run ```bash systelctl restart docker ``` ## Best Practices 1. Use official and verified Docker Images as Base Image 2. Use Specific Docker Image Versions 3. Use Small-Sized Official Images 4. Optimize Caching Image Layers 5. Use .dockerignore file 6. Make use of Multi-Stage Builds 7. Use the Least Privileged User 8. Scan your Images for Security Vulnerabilities ## Commands ### build ```bash docker build -t fastapi . ``` - `docker build` - build the image - `-t fastapi ` - an optional tag for the image - `.` - where to place the built image ### exec ```bash docker exec -it fastapi_api bash ``` - Enters `docker` interactive mode (`-it`) on image named `fastapi_api` and runs the command `bash` for the interactive session - `bash` overrides the default command of the docker file with is usually the `CMD` to start the image ### image #### ls ```bash docker image ls # OR docker images ``` ##### Example Output ```markdown REPOSITORY TAG IMAGE ID CREATED SIZE mcr.microsoft.com/mssql/server 2019-latest 6db3c5ebc331 2 months ago 1.55GB ``` #### prune ##### Prunes containers that do not have a reference ```bash docker image prune ``` ##### To prune all old images not used by existing containers run it with the -a flag: ```bash docker image prune -a ``` #### tag ```bash docker image tag <image name> tallguyjenks/<hub-repo-name>:<optional tag> ``` Optional tag if not stated just defaults to `latest` but you can also put a custom tag there ### inspect ```bash docker inspect <containerID> ``` Give you [[json]] output of the container's data ### login ```bash docker login ``` opens interactive session prompt for your <https://www.dockerhub.com> username and password ### logs ```bash docker logs <image name> ``` Show the logs from the image ### push like git push this will push a docker image to <https://www.dockerhub.com> after authenticating with [[#login]] ```bash docker push tallguyjenks/<hub-repo-name>:<local-tag-name> ``` To rename an existing image for this purpose of pushing use [[#tag]]