# Building a clean development environment with Docker Desktop (web application edition)
## Development Ideal and Real
### Different language versions
You would need to install/switch to another version for different projects
### Dependency management (composer for PHP, npm for node.js)
This can't be reused and install them freshly for each project
### Environment variables, PATH, config files
You need to remember which config files to be used, which web server for this project etc
### Virtual Machine
It enable developers to prepare different environments.
#### Software to implement hardware virtualization
* vmqEW
* Hyper-v
* Vagrant
It can run on hypervisor (software that creates and runs virtual machines) as well as normal OS (Windows, Linux)
It is not really ideal to run that on normal personal PC as it requires a lot of resouce (CPU, memory etc)
## What is Docker?
The most famous product for containerization
It prepare a container which contains minimul function to run applications.
To run Linux server on Windows or Mac, prepare Linux VM as hypervisor then run the container on it.
* Hypervisor.framework for Mac
* WSL (Windows Subsystem for Linux) for Windows
### Comparison with VM
* Fast movement
* Easy copy/move
* Dockerfile, Image
* There is a way to share your images over internet
* Fast start up
* For OS point of view, container is like a notmal application.

## Docker Basic

### Docker Image
Docker Image is a template of the container. Like snapshot.
images includes:
* Linux distribution (Debian, apline)
* Appliocation (Java, PHP)
* Library, Framework (mySql, Laravel)
* Program or settings file (PHP source code)
* Process to start containers (start up web server)
The same container can be reproduced as many times as possible as by using images. Therefore you can easily reproduce the same environment as other people or production environment.
### Dockerfile
specification of a docker image. It enables reproductivity as you don't need to specify the options when you build an image
dockerfile includes:
* base image
* command to execute on build (e.g. installing some library)
* port number
* file copy
* volume mount
* comamnd to execute on starting container
You can find base image on DockerHub (OFFICIAL IMAGE) or GitHub.
### Image Registry (Registry)
Place to share images. DockerHub, Amazon ECR, or you can build your own registry.
:::success
:bulb: Choose OFFICIAL IMAGE or VERIFIED PUBLISHER
:::
### Tag
::: warning
<version\>-<function\>-<distribution\>
:::
##### version
The vesrion of the application. e.g. Java version
##### function
What kind of function comes with this container
e.f. fpm, apache etc
##### distribution
Base Linux distributor. e.g. apline, bustor etc
* **bustor**: Typical Debian packet. It includes typical package. If the tag doesn't contain distributor, it normaly comes with bustor
* **apline**: Light weight and famous on Docker user. As it comes with minimal function, you would need to install libraries as necessary (e.g. bash)
### Container
Substantial the image.
It has status of executed/stopped.
## Docker image command
The commands are updated since docker version 1.13. You can still use the old command but better use the new command.
| command | old command | explanation |
|:-------------------- |:-------------- |:----------------------------- |
| docker image ls | docker images | show all images |
| docker image rm | docker rmi | delete image. |
| docker image prune | - | delete all unnecessary images |
| docker image pull | docker pull | get image from DockerHub |
| docekr image push | docker push | registor image to DockerHub |
| docekr image inspect | docker inspect | show details of the image |
### rm: delete image
You can also specify <imageName: tag\> instead of imageID
```bash
docekr image rm 28d825479be4
docker image rm php
docker image php
```
If tag is not specified, latest is applied
For force delete, use [-f] option
```bash
docker image rm -f 28d825479be4
```
### prune
unnecessary images means images which left like a trush by unexpected error. To delete all images which are not referred to the container, use -a option
```bash
docker image prune -a
```
## Docker containedr command
| command | old command | explanation |
| ------------------------ | -------------- |:---------------------------------------------- |
| docker container ls | docker ps | list all containers |
| docker container run | docker run | create and start container |
| docker container logs | docker logs | get container logs |
| docker container inspect | docker inspect | get info and details of the container |
| docker container stats | docker stats | show resouce usage |
| docker container stop | docker stop | stop container |
| docker container create | docker create | create container |
| docker container start | docker start | start contaienr |
| docker restart | docker restart | restart container |
| docker container attach | docker attach | connect to container |
| docker container exec | docker exec | connect to container |
| docker container rm | docker rm | delete container (container should be stopped) |
| docker container prune | - | delete all the stopped containers |
### ls: list all containers
show all started containers
#### option: -a
show all containers (both stopped and started)
#### option: -q
show all container IDs
### run: create and start container
Use the downloaded image if exist, pull it from DockerHub if not exist.
(combination of docker container create and docker container start)
#### Frequently used options:
| option | explanation |
|:------ |:------------------------------- |
| -name | name the container |
| -it | execute interactive mode |
| -d | execute it on background mode |
| -p | specify the port |
| -mount | folder mount |
| -rm | delete container after finished |
examples
```bash
docker container run -it --rm php:7.4-apache /bin/bash
```
It create a container from php:7.4-apache image, execute shell of /bin/bash on startup. The shell is executed on root user.
After exit from the container, it will be deleted automatically.
### attach/exec connect to container
docker exec executes a new command / create a new process in the container’s environment
docker attach just connects the standard input/output/error of the main process(with PID 1) inside the container to corresponding standard input/output/error of current terminal(the terminal you are using to run the command).
When exit from the shell, it stops container with attach command while it doesn't stop the container with exec command
To do something, use -it action (better use it always)
## Folder mount
mount can remain folder/files on the actual OS so that the data leaves even though containers are deleted
### volume (<- recommended)
Create a named volume by host and apply to the container. No need to think about where the volume is created (should not be controlled by host).
faster access than bind
| Command | explanation |
|:-------------------- |:--------------------- |
| docker volume create | create volume |
| docker volume ls | list volumes |
| docker volume rm | delete volume |
| docker volume prune | delete unused volumes |
Following creates volume named voltest at /tmp/voltest directory in the container.
src=<volume name/>
dst=<directory/>
Volume gets created when not exists
```bash
docker container run -it --rm --mount src=voltest,dst=/tmp/voltest php:7.4-apache /bin/bash
```
### bind
Apply mount specified by the host. It's used when host wants to control the content of the mount.
Following the command create mount at physical folder ($(pwd)/test) when anything created under /tmp/test in the container
```bash
mkdir test
docker container run -it --rm --mount type=bind,src=$(pwd)/test,dst=/tmp/test php:7.4-apache /bin/bash
```
### tmpfs
Use PC memory as a temporary mount. When the container stops, the tmpfs mount is removed and files written there won’t be persisted.
Cannot share contents between containers
## Dockerfile
A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
| keyword | explanation |
|:---------- |:------------------------------------------------------------- |
| FROM | specify base image |
| LABEL | add metadata |
| RUN | command on build |
| EXPOSE | specify network ports on which container listens at runtime |
| COPY | copy file/folder to container |
| ADD | with copy. Used when copying from unfreze zip or external URL |
| CMD | Only once executed command when container runs |
| ENTRYPOINT | command on run |
| ARG | temp variable |
| ENV | environmental variable |
| USER | switch user |
| WORKDIR | specify working direcotry |
### ENV
Variables which can be used in the container. To use it within Dokcerfile, write as **${TS}**
### ARG
Variables which only can be used in Dockerfile. Refer as **%TZ**
To specify the value, use ```--build-arg```
```bash
docker image build --build-arg wdir=/var/www/html .
```
Or specify default within the Dockerfile
```bash
ARG wdir="/var/www/html"
```
### EXPOSE
specify network ports on which container listens at runtime.
You can specify whether the port listens on TCP or UDP, and the default is TCP if the protocol is not specified.
for example
nginx uses port 80 for web server and can execute nginx like that
```bash
docker run -d -p 8080:80 nginx
```
So you can specify in Dockerfile
```dockerfile
EXPORT 80
```
### CMD
Write command which can be executed on docker run
If multiple CMDs are written, only the last CMD gets executed
For example if you write CMD like
```dockerfile
# separate command with comma
CMD["nginx", "-g", "deamon off;"]
```
These command is equivalent of
```bash
nginx -g deamon off
```
### ENTRYPOINT vs CMD
When you run docker like this: `docker run -i -t ubuntu bash` the entrypoint is the default `/bin/sh -c`, the image is ubuntu and the command is bash.
The command is run via the entrypoint. i.e., the actual thing that gets executed is `/bin/sh -c bash`. This allowed Docker to implement RUN quickly by relying on the shell's parser.
Later on, people asked to be able to customize this, so **`ENTRYPOINT`** and **`--entrypoint`** were introduced.
Everything after the image name, ubuntu in the example above, is the command and is passed to the entrypoint. When using the CMD instruction, it is exactly as if you were executing
`docker run -i -t ubuntu <cmd>`
The parameter of the entrypoint is `<cmd>`
You will also get the same result if you instead type this command `docker run -i -t ubuntu`: a bash shell will start in the container because in the ubuntu Dockerfile a default CMD is specified:
`CMD ["bash"]`.
As everything is passed to the entrypoint, you can have a very nice behavior from your images.
An example would be to have any cli as entrypoint. For instance, if you have a redis image, instead of running `docker run redisimg redis -H something -u toto get key`, you can simply have `ENTRYPOINT ["redis", "-H", "something", "-u", "toto"]` and then run like this for the same result: `docker run redisimg get key`.
### USER
Default user is root. USER command switches user.
* Linux normally specify execution user.
* If a file gets created by root user, it becomes troublesome when normal user works on this file. To avoid that normal user creates a file on Dockerfile would be beneficial.
## Docker Compose
Tool to build/start/stop container by using docker-compose.yml file instead of command to make sure the quality of the container is always the same.
### docker compose command
| Command | Explanation |
|:--------------------- |:--------------------------------- |
| docker-compose up | service initiate/re-initiate |
| docker-compose exec | execute command |
| docker-compose logs | show logs |
| docker-compose ps | list containers |
| docker-compose stop | service stop |
| docker-compose start | service start |
| docker-compose down | service stop and delete container |
| docker-compose build | image build |
| docker-compose create | create contaienr |
| docker-compose run | start service container |
### docker-compose.yml
| Key | Explanation |
|:-------------- |:-------------------------------------------------------- |
| image: | base image |
| container_name | container name |
| expose: | Expose ports without publishing them to the host machine |
| ports: | Expose ports which are exposed to the host machine |
| environment: | environmental variable |
| build: | Dockerfile path |
| working_dir: | working directory |
| volumes: | volume mount/ named volume |
| depends_on: | service dependency |
| stdin_open: | allocated stdin |
| tty: | service container to run with a TTY |
| command: | overwrite default command |
#### docker-compose example
```dockerfile
version: '3.8'
services:
db:
image: postgres:13.1-apline
container_name: 'ex11_db'
expose:
- "5432"
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- HOGE=hoge
web:
image: ex11/web:1.0
build: ./web
container_name: 'ex11_web'
ports:
- 8080:80
working_dir: '/var/www/html'
volumes:
- ./web/php.ini:/usr/local/etc/php/php/ini
- type: bind
source: ./web/src
target: /var/www/html
depends_on:
- db
```
### version
docker-compose.yml file format version. You can use different command by versions
### services
specify service (db and web in that saimple). It can be used to specify dependeicy or to refer service name on docker-compose command. You can have the different name as container_name but it's better to use the same one.
### image
base image.
### container_name
name of the container. You can access to the container by using that name.
If the container name not specified,
when it's docker-compose up: `folderName_serviceName_sequenceNumber`
*i.e. ex11web1*
If it's docker-compose run: `folderName_serviceName_run_identificationValue`
*i.e. ex11webrun164e66b2b48e*
### expose
Expose ports without publishing them to the host machine. On the following example specifying the default PstgreSQL database port: 5432.
This specified port cannot be accessed from host, only from the other services.
### build
specify where Dockerfile stays. If you specify build, the value at `image` will become an image name.
### port
container port to host port.
`host_port:container_port`
same as `docker run -p option`. If you specify 8080:80, then the apache container can be aceessed on port 8080
### working_dir
Working directry when starting up the container
### volumes
volume mount. Same as `docker run --mount option`.
You can specify the relative path from the current position.
you can use **volume**, **bind**, **tmpfs**.
Normally you can specify the setting file as volume
like
```dockerfile
- ./web/php.ini:/usr/local/etc/php/php/ini
```
### long version and short version
The following is long version (version 3.2+)
```dockerfile
volumes:
- type: bind
source: ./web/src
target: /var/www/html
```
this is equiavent of
```dockerfile
volumes:
- ./web/src:/var/www/html
```
If you want to use a named volume, then volumes needs to be described.
e.g.
```dockerfile
web:
volumes:
- type: volume
source: mydata
target: /data
volumes:
mydata:
```
see [official document](https://docs.docker.com/compose/compose-file/#volumes) for more details
### depends_on
It specifies relations. For the above sample, DB has to be executed to run Web.
### default command
If you start up the service with `docker-compose up`, the application should keep executing otherwise the container will finish immediately. For example if the default command is bash, you need to specify option.
```dockerfile
stdin_open: true
tty: true
command: /bin/bash
```
#### stdin_open, tty
equiavent of docker run -it option.
No need to specify this option if executing `docker-compose run` command as it is executed with -it command automatically
#### command
overwrite default command. You can overwrite this command by specifying the command with docker-compose run.
### docker-compose down
stop & delete services. If the container runs it stops then deletes.
`--rmi all` to delete images. `--volumes` will delete
## Remote Development
1. install Remote Development extension on VSCode
2. open .devcontainer/devcontaier.json file
- devcontainer.json contains editor's setting and Docker Compose/Dockerfile
3. .vscode/launch.json can be used for debugging.
## Enable SSL/TSL
1. install mkcert
on Windows, install Chocoloatery then
```bash
choco install mkcert
mkcert -install
```
on Mac use brew
```bash
brew install mkcert
mkcert -install
```
2. create SSL/TSL self certificate
run
```bash
mkcert localhost 127.0.0.1
```
To create following files
```bash
localhost+1-key.pem
localhost+1.pem
```
You can (or you should) rename the file such as `localhost-key.pem`
Then you can use it to set up as ssl_certificate
e.g. for nginx project conf file
```dockerfile
listen 443 ssl;
ssl_certificate /etc/certs/localhost.pem
ssl_certificate_key /etc/certs/localhost-key.pem
```