in this repo we follow this course
- ensure you follow the installation for your OS
- https://docs.docker.com/engine/install/fedora/#install-using-the-repository
- we chose fedora 37 at this point in time
- create a docker group to avooid the
sudo
hustle all the time - https://docs.docker.com/engine/install/linux-postinstall/
docker login
sudo systemctl start docker
sudo docker run hello-world
- to starup docker on systemstart run
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
docker run
inits our container and fetches it if possible from the docker-hub -so it is more like a create AND rundocker run
=docker create
+docker start
consider:
docker create hello-world
// returns an id like1e7b5eb62bfb875d23bb94de574a7136c9cf7594151ea0f143a96b2405c8984e
docker start -a <idFromAbove>
// the hello-world container will run of the id where-a
says give me all output- an exited container from
docker ps -all
can be started up again! =>docker start -a <id>
docker run hello-world
will download from the docker hub if image is not there locally- syntax in depth
docker run <image_name>
docker run <image_name> <command>
docker run busybox echo hi there
whereecho hi there
overrides the defautl command, BUT we can only use commands, that exist as programs/executables inside the container!docker run hello-world ls
will lead into an error, since we do not have ls installed
- syntax in depth
docker images
to check which local files we have on our machinedocker ps
--lists all running containers
only running containers will be shown
docker run busy-box ping google.com
with lifetime
docker ps --all
shows all containers ever created on this machine and when the randocker system prune
which will delete
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
docker logs <containerId>
//will retrive all emited output from a container, but it does NOT rerun or restart the container- some docs for us https://linuxhandbook.com/sigterm-vs-sigkill/
- consider:
docker create busyboxping ping google.com
returns<id>
docker start <id>
docker logs <id>
docker ps
returns container id<id>
is runningdocker stop <id>
will now stop sends SIGTERM message in a window of 10 seconds, after this SIGKILL is fired, since ping runs forever ORdocker kill <id>
will send SIGKILL message
docker run redis
which will start the redis server BUT will not allow us to make use of the redis-clidocker exec -it <containerId> <command>
=>-i -t
send-input
and-text
formattingdocker exec -it a5e0ae785adf redis-cli
e.g.docker exec -it <containerId> sh
// opens a terminal inside the docker-container wheresh
is the command to gain shell, but you can also usebash
orzsh
depending on the completness of the container- with this one can move around inside the container and even start
redis-cli
, if the process is not stopped fromstrg-c
usestrg-d
to send end shell command and come back to hostsystem docker run -it busybox sh
// one could also start therun
with ash
but it would prevent other logs or infos propagating topwards- so it is more common to
docker run
the desired container and open another shell todocker exec -it
on it, like shown above
#####NOTE! BEWARE!
containers do NOT share a filesystem to each other, which means that they are encapsulated by the id, if one adds a connection between containers there is no possible to access from one instance to another, even if they are based on the same image!!
- flow will always be like this
- specify a base image
- run soime commands to install additional programs in the container
- specify a command to run on startup container
- to excersise we will create an image that runs redis-server
- consider the dockerfile in
/redis-image
cd redis-image
followed bydocker build .
where.
is the entryoint which is clearlyDockerfile
Analogy: writing a dockerfile is like given a brand new computer and beeing told to install some programs onto it
- some src about offical supported base images
- each step of the Dockerfile will create itself a temporary image
- after the step is completed, the intermediate image will be deleted
- read this mindfully as output of our small 3step docker file
docker build .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM alpine
latest: Pulling from library/alpine
ca7dd9ec2225: Pull complete
Digest: sha256:b95359c2505145f16c6aa384f9cc74eeff78eb36d308ca4fd902eeeb0a0b161b
Status: Downloaded newer image for alpine:latest
---> bfe296a52501
Step 2/3 : RUN apk add --update redis
---> Running in 17fcb04d5d75
fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/community/x86_64/APKINDEX.tar.gz
(1/1) Installing redis (7.0.5-r0)
Executing redis-7.0.5-r0.pre-install
Executing redis-7.0.5-r0.post-install
Executing busybox-1.35.0-r17.trigger
OK: 9 MiB in 15 packages
Removing intermediate container 17fcb04d5d75
---> 5cd2718149c4
Step 3/3 : CMD ["redis-server"]
---> Running in d0c925dbc152
Removing intermediate container d0c925dbc152
---> 9696ab73986e
Successfully built 9696ab73986e
- on adding more build command with like
RUN
docker is so fast, because it can access a cache that holds temp build steps! quite nice - tl:dr cached images from build steps are >9000!
Step 2/4 : RUN apk add --update redis
---> Using cache
---> 82d8800b5796
- BUT order matters, if we move the RUN to the top, the image hash is different and we need to build from scratch
- for gaining an image name/tag we need to
docker build -t hagbard/redis:latest .
- there is also the possibilty to create an image out of an existing container
docker run -it alpine sh
starts and emtpy docker with an alpine image- inside the shell
apk add --update redis
=> brings a running container with a modified filessystem, redis is added - in another terminal we now
docker ps
to know the<containerId>
docker commit -c 'CMD ["redis-server"]'
// where-c
allows us to run a command, which is inside'...'
which will return another<containerId2>
- we run this now
docker run <containerId2
note this error: idealTreeAlreadyExists
- we need to:
left is entry point seen from
dockerfile build .
right is target inside container filesystem
-
WORKDIR /usr/app
specifiy a workdir inside the container -
COPY ./ /usr/app/
and copy them into the container -
now we are mapping container ports
-
where left 8080 means, all coming from this localnetwork port are redirected to other port insided the container, consider
-p 8080:1337
would need the express to expose on:1337
docker run -p 8080:8080 hagbardc3line/simple-webapp
-
after the build we could also verify about the correctness of our WORKDIR with this
docker run -it hagbardc3line/simple-webapp sh
which should be the same asWORKDIR /usr/simple-webapp
-
on rebuilding, because changing the js in
index.js
docker detects a change and then also wants to redonpm install
, how could we prevent this? -
we wil split the
COPY
step
# copy assets to container
COPY ./package.json /usr/simple-webapp/
# install depts
RUN npm install
# now copy all of the rest, to avoid another npm run install
COPY ./ ./usr/simple-webapp
- and gained build cache
Step 4/6 : RUN npm install
---> Using cache
---> 7a03967d8363
- we have 2 options here
- docker CLI use (issue here is one needs to do it every time!!, which we dont want)
- docker-compose as tool (which we needed to install on fedora37 just now) -> introducing docker-compose.yml
- by building via docker compose docker will allow connection in between this containers, so they can exchange data
- inside the
index.js
for the redis client, we can specifyhost: redis-server
which is given by the naming in thedocker-compose.yml
- consider
docker-compose up --build
Creating network "visitor-counter_default" with the default driver
docker run myImage
->docker-compose up
docker build .
&&docker run myImage
->docker-compose up --build
docker run id
->docker-compose up -d
runs containers in the backgrounddocker stop id
would be unhandy fopr each single image ->docker compose down
- we modify our vistors
index.js
by adding a second route, which will end the servers runtime - we can check on correctnes by
docker ps
only redis should run now, visitors is exited(0) - note on nodejs Status Codes
0->everything is ok
BUT1,2,3..etc -> Exit because something went wrong
keep this in mind, when writing your automatic container restarts - docker-compose offers
restart policies
with 4 states"no"
- never restart (no-te the quotes) because yml will readno
asfalse
, curios and funnyalways
-> container stopped for any reason, restart iton-failure
-> only restarts if the container stops with an error codeunless-stopped
-> likealways
unless we DEVELOPERS forcibly stopped it
docker ps
->docker-compose ps
to inspect running containers, inside the /dir we are in, when there is also adocker-compose.yml
given in that root
-
we want to create a flow of actions:
- pull from git feature branches
- make changes on it
- push back to feature
- create pull request to merge into main
- will trigger TRAVIS CI
- will run some tests
- if tests are passing
- deploy to AWS Elastic Beanstack
check this in depth split repo
- runs
npx create-react-app my-app
to gain an example project cd my-app
go into root of frontend proj
touch Dockerfile
-> production dockertouch Dockerfile.dev
-> development dockerdocker build -f Dockerfile.dev .
/-f = file
- consider we want to make changes on the
index.js
but want it to be reflected on ourdocker run -p 3000:3000 <ebee06b498a6_anyId>
- docker volumes map a folder from the local machine to the inner folder of the container
- empty expression example
docker run -p 3000:3000 -v /app/node_modules -v$(pwd):/app ebee06b498a6
// -> (pwd) is short for present working directory
nasty error on linux
Failed to compile.
[eslint] EACCES: permission denied, mkdir '/app/node_modules/.cache'
ERROR in [eslint] EACCES: permission denied, mkdir '/app/node_modules/.cache'
solution is to alter Dockerfile with USER
and chown
possibly:
FROM node:alpine
USER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/app
COPY --chown=node:node ./package.json ./
RUN npm install
COPY --chown=node:node ./ ./
CMD ["npm", "start"]
Explanation of changes:
We are specifying that the USER which will execute RUN, CMD, or ENTRYPOINT instructions will be the node user, as opposed to root (default).
https://docs.docker.com/engine/reference/builder/#user
We are then creating a directory of /home/node/app prior to the WORKDIR instruction. This will prevent a permissions issue since WORKDIR by default will create a directory if it does not exist and set ownership to root.
The inline chown commands will set ownership of the files you are copying from your local environment to the node user in the container.
The end result is that some files and directories will no longer be owned by root, and no npm processes will be run by the root user. Instead, they will all be owned and run by the node user.
-
the final
docker run
with docker volumesdocker run -p 3000:3000 -v /home/node/app/node_modules -v $(pwd):/home/node/app 47f680845db3
-
please take a note, create-react-app is still the one hotreloading, but with docker volumes we can get the update reflected
-
we could still improve further on, for this we use docker-compose
-
consider this
docker-compose.yaml
version: '3'
services:
react-app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- /home/node/app/node_modules #bookmarking node_modules
- .:/home/node/app #mount localfilesystem
restart: unless-stopped
- run it with
docker-compose up --build
tl:dr there is no ideal solution :-P
- assumes you have the container id in hand
- the CLI way is
docker run <ContainerId> npm run test -it
to also connect our machine to STDIN - while developing also
docker exec -it <containerID> npm run test
could be of use, by doint so, we attach our self to the already running docker and gain hotlreload on changes // not a perfect solution - we refactored
docker-compose.yaml
to run tests on a secondservices
- commands are provide to
docker-compose.yaml
like thiscommand: ["npm", "run", "test"]
- pro of this, it is encapsulated, BUT con is that all tests are rereun on changes all the time, and we dont have STDIN from
docker exec
, which allowed us top trigger tests manually - we tried
docker ps
&&docker attach <containerId>
but we are not able to send to STDIN here - another way is
docker exec -it <containerId> sh
which let's us slip directly into the running container and would allow us in another terminal window tonpm run test
and gain the runner options with STDIN
- since we don't have a dev web server here, we go with nginx
- flow is
- use node:alpine
- copy package.josn
- install all depts // Deps ony need to execute build, we would need it, to save 150mb of depts
npm run build
- copy stuff around || serve build directory
- start nginx