Gateway To Docker API Engine
Tags: docker, api, nginx, vagrant
Published: 2023-03-15 19:04:39

Welcome back gentle reader. Many thanks for your return to refine your wisdom. My name is [Silent-Mobius](https://gitlab.com/silent-mobius), also known as Alex M. Schapelle, your dedicated storyteller. 
In today's query I'd like to talk about containers and a very specific way of using them: accessing via `http` protocol to `Nginx` reverse-proxy that redirects request to `docker engine`'s internal API.

This short projects demonstrates the same idea, but with much simpler execution with Nginx reverse-proxy sending `http` requests to docker engine, in order to start required specific container.

## The motivation
I believe, you remember my [mazin-docker](https://gitlab.com/vaiolabs-io/mazin-docker) project in which I've developed flask based RestAPI application that can run multi-container environment which you'd prefer to use instead of complex tool like #k8s.
The motivation was to implement something in the most easiest way possible, and thus this tutorial was born.

## Before we start

Not everything is fruit of my imagination, and some of things that I work on, are thanks to small team of people that work with me, and in this case, I'd like to thank @antweiss for guidance on this small project. Thanks!

## Plan of work
To begin our short adventure, We'll start by making small choices as follows:
  - Get and install Debian based system. You may set it up with  Virtualbox, KVM, AWS, Linode or Digital-Ocean. Choice is  up to you.
  - We'll install Nginx and #docker on that Debian based system
  - Add configuration for both installed tools
  - Test the access to docker API via Nginx with HTTP
  - Append ssl configuration and test it as well

One question that might pop-up is: "what is the use case for this?" -> 
## Setup

Me being person of Infrastructure As Code (IaC), I prefer to test Proof Of Concepts (POC) in virtual environments, and this is where Virtualbox and Vagrant come in to play. Whether you are using Linux, Mac or Windows, these tools always are useful.
To install [Virtualbox use this link](https://www.virtualbox.org/wiki/Downloads). Download the executable and install on your system.
> Note: you might need to enable virtualization in your computers BIOS/EUFI configuration [check here](https://bfy.tw/ReW8)
Vagrant IaC framework that uses virtual-box to download and run pre defined virtual appliances (zipped virtual operational systems) and create systems faster then doing it manually - [install vagrant from here](https://developer.hashicorp.com/vagrant/downloads)
Once these 2 are installed, let's setup our POC environment, by opening shell or powershell:
```sh
mkdir poc
cd poc
vagrant init # this will create file named Vagrantfile
echo ' ' > Vagrantfile # clearing the initial config
```
Usually vagrant provides an initial template to setup virtual machines inside `Vagrantfile`, yet I prefer to provide you with my own version that configuration:

```ruby
Vagrant.configure("2") do |config|
  config.vm.define "deb" do |deb|
    deb.vm.box = 'debian/bullseye64'
    deb.vm.network "private_network", ip: "192.168.56.4"
    deb.vm.hostname="debian"
    deb.vm.provider :virtualbox
   end
end
```
copy code above to `Vagrantfile` and one done run command:
```sh
vagrant up
```
> Notes: 
>  - you need to be `cd`ed at the same folder where you have created the `Vagrantfile`
>  - in case you are using windows, use notepad or notepad++ to edit the file from power-shell
>  - for more info in regards to [vagrant read here](https://developer.hashicorp.com/vagrant/docs)

The output should look like this:
![vagrant up](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kuejsn6hem3wlefk12q.png)
Once the command runs, it will download **Debian Linux** distribution 11, with code name **bullseye**. The download will work with **ova image**, which is a standard format for packaging virtual appliances, to your device and will run it with virtualbox virtualization provider. It should take about 3 minutes, it also depends on your network connection, thus wait until there is no output from last command. Once done, you should connect to vm.

To connect to the virtual machine (vm), we use ssh command of vagrant:
```sh
vagrant ssh deb
```
Mazal Tov !!! You are using IaC and are connected to `remote` machine.

Lets us finish installation by installing main software for which we came here for: Nginx and docker
```sh
sudo apt-get update && sudo apt-get install -y nginx
curl -L get.docker.com | sudo bash 
```

## Configuration

Docker Engine exposes a REST API which you can use to control your containers without the docker CLI. The API exposes equivalent functionality using HTTP protocol calls. You can script common Docker operations using your favorite programming language or remotely control one of your hosts. The CLI internally relies on the same API to provide its built-in commands.

To expose the service, you need to bind the Docker daemon to a TCP socket as well as, or instead of, the default IP and port:

```sh
sudo vi /lib/systemd/system/docker.service 
```
This is docker daemon service file where you should append ` -H tcp://127.0.0.1:2375` to line 13

It should look like this:
```sh
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://127.0.0.1:2375
```
Due to a fact of us editing service file, `systemd` daemon needs to be reloaded and docker service needs to be restart for the changes to be accepted, thus:
```sh
sudo systemctl daemon-reload
sudo systemcttl restart docker
```
We should test whether the configuration has worked or not, thus:
```sh
docker run -d hello-world
curl localhost:2375/v1.41/images/json
``` 
The `curl` should return json format file that contains details of `hello-world` image that we used command before this.
> Notes:
> - `localhost:2375` is a socket (combination of ip and port) that we enabled for docker engine
> - `v1.41` is api version, which is provided by docker [documentaion](https://docs.docker.com/engine/api/)
> - `images` is something called endpoint. Consider it to be a table name, which obviously lists names of images currently located on the host
> - `json` is format in which the data is presented, AFAIK json is only format at the moment.

Although this works, but it still not what we wanted, thus lets configure other part of this project: 
- Let us configure the Nginx redirect from port 80 to 2375

```sh
echo ' ' > /etc/nginx/sites-enabled/default # removing default config
sudo vi /etc/nginx/sites-enabled/default
```
Paste the default configuration as follows:
```nginx
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	server_name _;

  location / {
	  proxy_pass http://127.0.0.1:2375/v1.41/;
	}

}
```
Each configuration change requires restart, thus:

```sh
sudo systemctl restart nginx
```

Now it is right time to test the changes from nginx point of view:
```sh
curl localhost
    {"message":"page not found"}
curl -s localhost/images/json
    [{"Containers":-1,"Created":1675917382,"Id":"sha256:3f8a00f137a0d2c8a2163a09901e28e2471999fde4efc2f9570b91f1c30acf94","Labels":{"maintainer":"NGINX Docker Maintainers <docker-maint@nginx.com>"},"ParentId":"","RepoDigests":["nginx@sha256:6650513efd1d27c1f8a5351cbd33edf85cc7e0d9d0fcb4ffb23d8fa89b601ba8"],"RepoTags":["nginx:latest"],"SharedSize":-1,"Size":141838619,"VirtualSize":141838619}]
```
## Docker API Use
The use of docker API, may be some what rigid and requires step by step guidance. To create running containers, steps are as follows:
- Pull the image from the registry
- Create the container instance id
- Start container by id

#### Here are commands:
- To pull image:
```sh
 curl -X POST "http://localhost/image/create?fromImage=alpine:latest"
```
- To create container instance:
```sh
curl "http://localhost/containers/create" -X POST  -H "Content-Type: application/json"  -d '{"Image": "alpine", "Cmd": ["echo", "hello world"]}'
```
- this command should have output of hash-id of ready container:
```json
{"Id":"105afe820928621621a5cfc133b98d1fab9419709fa6e0eea6a5a5822cd5dcf7","Warnings":[]}
```
- Start the container
```sh
curl -X POST http://localhost/containers/105afe8209/start
# the hash is output from previous command
```
- List the container running
```sh
curl -X POST http://localhost/containers/json
```
- List the container running
```sh
curl -X POST http://localhost/images/json
```

---

In cases where we need to stop container
```sh
curl -X POST http://localhost/containers/105afe8209/stop
```
---

Print the logs of a specific container
```sh
curl -X POST http://localhost/containers/105afe8209/logs?stdout=1
```

## Conclusion
As you can see, it there are lots done, yet to recap:
- We created virtual machine
- Installed Nginx and Docker
- Opened docker api connection
- Configured Nginx to pass commands to docker engine
- Listed some of the api commands

That would be all, hope you found this article informative and educative. One last thing: Do Try To Have Some Fun