Docker start for frontend devleop

Photo by Ian Taylor on Unsplash

Docker start for frontend devleop

The article is highly inspired by the main "get-started docker" documentation

Pre-requests:

There is not a hard bound necessitated for the rest of course, but knowing some Linux commands can be helpful.
The only need is a GitHub account (GitLab or Bitbucket will also work, but needs some extra config).
Also, the title suggests being a front developer so knowing some basics about package.json and node project is welcomed.

There is also a high recommendation for seeing this video, it is fine if you didn't get all of the concepts, but it will give you an overall overview.

We are not going to install docker on your system, so for the rest of the course, we are using gitpod workspaces.

Click on "Open in Gitpod" in the following repo


From now on, we are respecting the concept of the show-code-then-explain method

Note: there is no concept like the above, but seems a good time to coin the word scte

# Dockerfile
FROM node:12-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000
docker build -t getting-started .

the -t (--tag) flag tags our image. Think of this simply as a human-readable name for the final image. Since we named the image getting started, we can refer to that image when we run a container.

The . at the end of the docker build command tells that Docker should look for the Dockerfile in the current directory.

docker run -p 3000:3000 getting-started

How to edit (update the application)

  1. Let's change the source code for some text

  2. Build like the way we learned till now

docker build -t getting-started .
  1. Run like the way we learned till now, but wait 🥲

what happened? We aren’t able to start the new container because our old container is still running. It is because the container is using the host’s port 3000 and only one processes on the machine (containers included) can listen to a specific port

How to fix that? We need to remove the old container.

  • Replace the old container
  1. Get the ID of the container by using the docker ps command.
docker ps
  1. Use the docker stop command to stop the container.
docker stop <the-container-id>
  1. Once the container has stopped, you can remove it by using the docker rm command.
docker rm <the-container-id>

Note: 🐳
You can stop and remove a container in a single command by adding the “force” flag to the docker rm command. For example:

docker rm -f <the-container-id>

Then Start the updated app container 🤓.


Share the application

  1. Sign up or Sign in to Docker Hub.

  2. Click the Create Repository button.

  3. For the repo name, use getting-started. Make sure the Visibility is Public.

  4. Click the Create button!

Let's push the via the given docker commands

💣 Error.
The push command was looking for an image named <username>/getting-started but didn’t find one.
Didn't find where?
Let's take a closer look

docker image ls
# or
docker images

How to fix it?

  1. Login to the Docker Hub using the command
docker login -u <username>
  1. Use the docker tag command to give the getting-started image a new name.
 docker tag getting-started <username>/getting-started

If you’re copying the value from Docker Hub, you can drop the :tagname portion, as we didn’t add a tag to the image name. If you don’t specify a tag, Docker will use a tag called latest.

docker push <username>/getting-started

🐗 Now that we have that figured out, let’s circle back around to what we noticed at the end of the last section. As a reminder, we noticed that when we restarted the app, we lost all of our to-do list items. That’s not a great user experience, so let’s learn how we can persist the data across restarts!


Persist the DB

Scratch space

Scratch space is space on the hard disk drive dedicated to storing temporary user data. It is unreliable by intention and has no backup. Scratch disks may occasionally be set to erase all data at regular intervals so that the disk space is left free for future use.

When a container runs, it uses the various layers from an image for its filesystem. Each container also gets its own “scratch space” to create/update/remove files. Any changes won’t be seen in another container, even using the same image.

proof

bash docker run -it ubuntu bash
shuf -i 1-10000 -n 1 -o /data.txt

‍‍‍```bash
docker exec <container-id> cat /data.txt

docker run -it ubuntu bash 
# cat /data.txt

docker run -it ubuntu cat /data.txt

Container volumes

Volumes provide the ability to connect specific filesystem paths of the container back to the host machine. If a directory in the container is mounted, changes in that directory are also seen on the host machine.

named volumes

By default, the todo app stores its data in a SQLite Database at /etc/todos/todo.db

TIPS:😁 the data is stored in a single file. While this isn’t the best for large-scale applications, it works for small demos.

By creating a volume and attaching (often called “mounting”) it to the directory the data is stored in, we can persist the data.
Think of a named volume as simply a bucket of data.
Docker maintains the physical location on the disk and you only need to remember the name of the volume. Every time you use the volume, Docker will make sure the correct data is provided.

  1. Create volume
docker volume create todo-db
  1. Stop and remove the todo app container once again in the Dashboard (or with docker rm -f <id>), as it is still running without using the persistent volume.

Start the todo app container, but add the -v flag to specify a volume mount. We will use the named volume and mount it to /etc/todos

docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
  1. Stop and remove the container for the todo app.

  2. Start a new container using the same command from above.

  3. Open edit and it must rename even after restart

Note: 🐳
While named volumes and bind mounts (which we’ll talk about in a minute) are the two main types of volumes supported by a default Docker engine installation, there are many volume driver plugins available to support NFS, SFTP, NetApp, and more! This will be especially important once you start running containers on multiple hosts in a clustered environment with Swarm, Kubernetes, etc.

docker volume inspect todo-db

Bind mounts

comparison

start nodemon dev container

 docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "yarn install && yarn run dev"

see the docker's logs

docker logs -f <container-id>

change the source code, you must see the re-runs


Multiple containers

Each container should do one thing and do it well. A few reasons:

  • There’s a good chance you’d have to scale APIs and front-ends differently than databases

  • Separate containers let you version and update versions in isolation

  • While you may use a container for the database locally, you may want to use a managed service for the database in production. You don’t want to ship your database engine with your app then.

  • Running multiple processes will require a process manager (the container only starts one process), which adds complexity to container startup/shutdown

Note: 🐳
If two containers are on the same network, they can talk to each other. If they aren’t, they can’t.

There are two ways to put a container on a network: 1) Assign it at the start or 2) connect an existing container

  1. Create the network.
docker network create todo-app
  1. Start a MySQL container and attach it to the 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:5.7

Note: 🐳
You’ll notice we’re using a volume named todo-mysql-data here and mounting it at /var/lib/mysql, which is where MySQL stores its data. However, we never ran a docker volume create command. Docker recognizes we want to use a named volume and creates one automatically for us.

  1. To confirm we have the database up and running, connect to the database and verify it connects.
docker exec -it <mysql-container-id> mysql -u root -p
mysql> SHOW DATABASES;
mysql > exit

To be continued...