## 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]]