Using Docker-outside-of-Docker and Flask to use easily Docker inside the containers

## Disclaimer

Note that the approach presented in this article is **only meant to be used within a trusted network!**:

1. [Command injection](https://owasp.org/www-community/attacks/Command_Injection) codes are contained (as pointed out by my colleague and supervisor [E. Torii](https://qiita.com/eitaro-torii-dtws)).
1. Exposing `/var/run/docker.sock` gives the container root privileges of host (as pointed out by my colleague and supervisor [E. Torii](https://qiita.com/eitaro-torii-dtws)).

Again, I would like to emphasize that this is **NOT** a solution I would suggest to use in production environments at one's workplace.

## Problem Statement

It is often desirable to use Docker containers inside another Docker container.
One example is the [Docker-baised-Airflow deployments](https://github.com/puckel/docker-airflow) mentioned below,
where `docker` command does not work out-of-box with `LocalExecutor` (hence, [DockerOperator](https://airflow.apache.org/docs/apache-airflow-providers-docker/stable/_api/airflow/providers/docker/operators/docker/index.html)s do not work as well).

One solution would be to build a privileged container and run Docker within it, however this is [known](https://www.trendmicro.com/en_us/research/19/l/why-running-a-privileged-container-in-docker-is-a-bad-idea.html#:~:text=Running%20a%20container%20with%20privileged,necessarily%20entail%20remote%20code%20execution.) to pose a security threat (moreover, I personally find them difficult to setup).

Besides, if you already use Docker this necessarily means that you have at least one working Docker deployment -- on your host machine! So why not try to leverage it? That is the basic idea.

## Solution

I have put together an image, which I will call `dood-flask` in subsequent ([GitHub repo](https://github.com/nailbiter/dood-flask), [Dockerfile](https://github.com/nailbiter/dood-flask/blob/master/Dockerfile), [Dockerhub Image](https://hub.docker.com/r/nailbiter/dood-flask)). It can be easily hooked with your existing Docker installation (see the sample [docker-compose.yml](https://github.com/nailbiter/dood-flask/blob/master/docker-compose.yml)) and comes equipped with a simple Flask server so it can run Docker images triggered via 
HTTP requests and hence can be easily added to existing infrastructures (more on that later).

Let us take it for a spin:

```
> git clone https://github.com/nailbiter/dood-flask/
> cd dood-flask
> docker-compose up --build -d
> docker-compose run --rm busybox ash
/home # curl -X POST -H 'Content-type: application/json' --data '{"image":"hello-world"}' web:5000
{"error_code": 0, "output": "\nHello from Docker!\nThis message shows that your installation appears to be working correctly.\n\nTo gene
rate this message, Docker took the following steps:\n 1. The Docker client contacted the Docker daemon.\n 2. The Docker daemon pulled th
e \"hello-world\" image from the Docker Hub.\n    (amd64)\n 3. The Docker daemon created a new container from that image which runs the\
n    executable that produces the output you are currently reading.\n 4. The Docker daemon streamed that output to the Docker client, wh
ich sent it\n    to your terminal.\n\nTo try something more ambitious, you can run an Ubuntu container with:\n $ docker run -it ubuntu b
ash\n\nShare images, automate workflows, and more with a free Docker ID:\n https://hub.docker.com/\n\nFor more examples and ideas, visit
:\n https://docs.docker.com/get-started/\n"}
/home # curl -X POST -H 'Content-type: application/json' --data '{"image":"busybox","cmd":"echo hi"}' web:5000
{"error_code": 0, "output": "hi"}
/home # exit
```

The server's output is in JSON, so it may be a bit difficult to understand, but the point is that (inside the `docker-compose`'s infrastructure) a POST-request to `web:5000` allows us to run containers (provided via `image` in JSON payload) 
and optional commands (provided via `cmd`)! Server runs the image and return error code and text output (`error_code` and `output` respectively).

## Applications

### Docker-in-Docker-based-Airflow

One application would be enabling Docker in Docker-based-Airflow (as briefly mentioned above):

```
> git clone https://github.com/nailbiter/docker-airflow
> cd docker-airflow
> docker-compose -f docker-compose-LocalExecutor.yml up --build -d
> docker-compose -f docker-compose-LocalExecutor.yml  run --rm webserver airflow unpause tutorial_docker
```

Then, go to `localhost:8080` (may need to wait for a few minutes), and you will see the Airflow's WebUI. In it, you will see two DAGs: `tuto` and `tutorial_docker`. 
Click on the `tutorial_docker` and you should see two dagruns each with a single task: `run_docker`. Wait till it finishes (it should become green) and zoom in it. In the output you should see
the result of running the command `docker run hello-world`. Output should be similar to below:

![screen1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588288/60a0f8a2-c543-dfde-50ae-9dc6cf5dafb4.png)


By changing the optional kwarg `docker_image` you should be easily able to run other images within Airflow. Have fun!

## Future Work

1. eliminate the danger of [command injection](https://owasp.org/www-community/attacks/Command_Injection) (perhaps, switch to [Docker Python SDK](https://docker-py.readthedocs.io/en/stable/) instead of `os.system`)

## References
1. [https://blog.nijohando.jp/post/docker-in-docker-docker-outside-of-docker/](https://blog.nijohando.jp/post/docker-in-docker-docker-outside-of-docker/)
1. https://github.com/nailbiter/dood-flask