(LINUX) LINUX (2021)

Dockerizing Linux server (continue)

1. Install and configure docker

This notes continues page Dockerizing Linux server.

My first task in this server will be add new disk space and move root docker directory to new disk. So:

# lsblk
# sudo fdisk -l
# sudo mkdir /docker
# sudo nano /etc/fstab

Than install docker, check it,

# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# sudo apt-get update
# apt-cache policy docker-ce
# sudo apt-get install -y docker-ce
# sudo systemctl status docker
# sudo docker info
# sudo curl --unix-socket /var/run/docker.sock http://localhost/v1.41/containers/json

and move to the new directory.

# sudo service docker stop
# df
# sudo nano /etc/docker/daemon.json
# sudo cat /etc/docker/daemon.json
# sudo rsync -aP /var/lib/docker/ /docker
# sudo mv /var/lib/docker /var/lib/docker.old
# sudo service docker start
# sudo rm -rf /var/lib/docker.old

This is a benefits of use separate volume to docker.

Best practice is also immediately create login for docker client, this allow authenticated request from docker client to docker server.

After that best practice is to check docker network - availability of host.

# sudo docker run --rm -it busybox

2. Docker config files.

Docker has a couple of config - dockerService config, docker daemon config,

# sudo cat /etc/docker/daemon.json
# sudo cat /lib/systemd/system/docker.service
# sudo systemctl status docker
# journalctl -u docker
# sudo docker info
# sudo docker network ls --no-trunc

and docker client config (contains AU data after docker login).

# docker login
# sudo cat .docker/config.json

3. Main command to manipulate docker containers and images

Main command to manipulate docker containers and images (more Dockerizing Linux server):

list containers
# sudo docker ps 

list all containers
# sudo docker ps -a

list images
# sudo docker images
# sudo docker images --no-trunc

start container as disconnected daemon
# sudo docker run -d -p 5000:5000 --restart always --name 5shitmoney 5-shit-money:latest

check opened ports
# sudo netstat -tunlp

perform one command
# sudo docker exec -it 7dde487b4424 ls -la /var/lib/registry

interactively going inside container
# sudo docker exec -it 7dde487b4424 bash

compose container from YML config file and start as daemon
# sudo docker-compose -f registry-compose.yml up -d

# commit changes
# sudo docker container commit 7bfb7cc3d51c coinbase:latest

backup docker (but without volumes in /var !!)
# sudo docker save 5-shit-money:latest | gzip > 5-shit-money.tar.gz

# pull container from docker.io (or other registry defined in config(
sudo docker pull alexev275/coinbase

rename image
#sudo docker tag 7bfb7cc3d51c localhosh:5000/coinbase

delete all related images (force mode)
# sudo docker images -a | grep "localhosh" | awk '{print $3}' | xargs sudo docker rmi -f

rebuild container
# sudo docker build -f digiwage1.dockerfile -t alexev275/digiwage .

list containers
# sudo docker container ls -a
# sudo docker container ls -a --no-trunc

stop container
# sudo docker container stop 7bfb7cc3d51c

# inspect image 
sudo docker image inspect alexev275/digiwage

list all  volumes
# sudo docker volume ls

get container logs
# sudo docker logs 7bfb7cc3d51c

stop docker service
docker service scale [servicename]=0

4. Docker disk structure

Docker has cool and understandable dick structure.

This is example of container configuration Container-config.json.

Most usable is /volume structure. If you include to dockerfile magic string

VOLUME /root/share

this string allow you to receive shared folder to transfer data with docker host. For example, I have a issue to install ping util inside docker, therefore I simple copy needed files to volume and I receive it inside docker.

5. Connect to Docker API

Docker can connect with remote socket, but firstly need to change docker daemon start command.

Then after reload and restart daemon we can check workable unix socket.

# sudo nano /lib/systemd/system/docker.service
# sudo cat /lib/systemd/system/docker.service
# sudo systemctl daemon-reload
# sudo service docker restart
# sudo curl --unix-socket /var/run/docker.sock http://localhost/v1.41/containers/json

6. Setup Basic AU to control docker.

This is simplest way without SSL. So, firstly need to create password file.

# sudo apt-get install nginx
# sudo apt-get install apache2-utils
# sudo htpasswd -c /etc/nginx/.htpasswd docker
# cat /etc/nginx/.htpasswd

NGINX after installation working with account www_data, this account don't allow access to docker, therefore first step is change NGINX account to ROOT.

# sudo nano /etc/nginx/nginx.conf

Nginx automatically activate sites from directory sites-enabled/sites-available, so no need to additionally changing something in main config, we can create additionally config.

# sudo tee /etc/nginx/sites-enabled/docker <<EOF 
# upstream docker {
#   server unix:/var/run/docker.sock;
# }
# server {
#   listen 4444 default_server;
#   location / {
#     proxy_pass http://docker;
#     auth_basic_user_file /etc/nginx/.htpasswd;
#     auth_basic "Access restricted";
#   }
#  }
# sudo service nginx restart

Than, if we made always without mistake, we rich finally goal - Basic AU for docker control.

# curl -i http://docker:[email protected]:4444/info

Of course, more secure way is use SSL to protect transfer password in clear form. You can use even self-signed certificate for this purpose - Protect Docker Registry by Nginx. Of course, for use self-signed certificate in client software we need to avoid certificate chain - HttpClient with JSON communication to server with avoid check SSL-certificates chain .

7. Protect docker password with SSL.

Usually first step is check certificate. As you can see in the screen below one certificate is correct, however another one is wrong.

If certificate is correct we can use it to protect password for docker management.

8. Client software to manage docker based on Docker.DotNet.

Unfortunately directly use https://github.com/dotnet/Docker.DotNet to manage docker software with basic AU is impossible because packages don't prepared correctly.

Therefore I have create second package from source code. Workable result I have uploaded to https://github.com/Alex-1347/DockerDotNetWrapper.

This is my code.

   1:  Imports Docker.DotNet.Models
   2:  Partial Module Program
   3:      Public Class DockerReport
   4:          Public Property SystemInfo As SystemInfoResponse
   5:          Public Property Volumes As VolumesListResponse
   6:          Public Property Images As IList(Of ImagesListResponse)
   7:          Public Property Containers As IList(Of ContainerListResponse)
   8:          Public Property Networks As IList(Of NetworkResponse)
   9:      End Class
  10:  End Module

   1:  Imports System
   2:  Imports Docker.DotNet
   3:  Imports Docker.DotNet.BasicAuth
   4:  Imports Docker.DotNet.Models
   5:  Imports Newtonsoft.Json
   6:  Imports Newtonsoft.Json.Linq
   8:  'https://github.com/dotnet/Docker.DotNet
  10:  Partial Module Program
  12:      Sub Main(args As String())
  13:          'Console.WriteLine("Pass to be encrypted >")
  14:          'Dim pass1 = Console.ReadLine
  15:          'Console.WriteLine("Encrypted string >")
  16:          'Dim pass2 = Console.ReadLine
  17:          'Console.WriteLine(EncryptString(pass1, pass2))
  18:          'End
  19:          Dim DockerReport1 As New DockerReport
  20:          Console.Write("Get Password" & vbCrLf & ">")
  21:          Dim PassStr = ReadPassword()
  22:          Console.WriteLine()
  23:          Dim Credential = New BasicAuthCredentials(My.Resources.Login, DecryptString(My.Resources.Pass, PassStr))
  24:          Dim DockerHub = New DockerClientConfiguration(New Uri(My.Resources.Url), Credential).CreateClient()
  25:          Console.WriteLine($"Docker {My.Resources.Url} connected.")
  27:          DockerReport1.SystemInfo = DockerHub.System.GetSystemInfoAsync().Result
  28:          Dim JSystemInfo As JObject = JToken.FromObject(DockerReport1.SystemInfo)
  29:          Console.WriteLine($"{ DockerReport1.SystemInfo.Name} { DockerReport1.SystemInfo.DockerRootDir} ")
  30:          Console.WriteLine()
  32:          DockerReport1.Volumes = DockerHub.Volumes.ListAsync.Result
  33:          Console.WriteLine($"Found {DockerReport1.Volumes.Volumes.Count} volumes")
  34:          DockerReport1.Volumes.Volumes.ToList.ForEach(Sub(X) Console.WriteLine($"{X.Name}"))
  35:          Console.WriteLine()
  37:          DockerReport1.Images = DockerHub.Images.ListImagesAsync(New ImagesListParameters With {.All = True}).Result
  38:          Console.WriteLine($"Found {DockerReport1.Images.Count} images")
  39:          DockerReport1.Images.ToList.ForEach(Sub(X)
  40:                                                  X.RepoTags.ToList.ForEach(Sub(Y) Console.WriteLine(Y.ToString))
  41:                                              End Sub)
  42:          Console.WriteLine()
  44:          DockerReport1.Networks = DockerHub.Networks.ListNetworksAsync().Result
  45:          Console.WriteLine($"Found {DockerReport1.Networks.Count} networks")
  46:          DockerReport1.Networks.ToList.ForEach(Sub(X) Console.WriteLine($"{X.ID} : {X.Name}"))
  47:          Console.WriteLine()
  49:          DockerReport1.Containers = DockerHub.Containers.ListContainersAsync(New ContainersListParameters With {.Limit = 10}).Result
  50:          Console.WriteLine($"Found {DockerReport1.Containers.Count} containers")
  51:          DockerReport1.Containers.ToList.ForEach(Sub(X) Console.WriteLine(X.ID))
  52:          Console.WriteLine()
  54:          Dim Plugins As IList(Of Plugin) = DockerHub.Plugin.ListPluginsAsync(New PluginListParameters).Result
  55:          Console.WriteLine($"Found {Plugins.Count} plugins")
  56:          Plugins.ToList.ForEach(Sub(X) Console.WriteLine(X.Name))
  57:          Console.WriteLine()
  58:          Try
  59:              Dim Secrets As IList(Of Secret) = DockerHub.Secrets.ListAsync().Result
  60:              Console.WriteLine($"Found {Secrets.Count} secrets")
  61:              Secrets.ToList.ForEach(Sub(X) Console.WriteLine(X.ID))
  62:          Catch ex As Exception
  63:              Console.WriteLine("DockerHub.Secrets: " & ex.Message)
  64:          End Try
  66:          Try
  67:              Dim Swarms As IEnumerable(Of SwarmService) = DockerHub.Swarm.ListServicesAsync().Result
  68:              Console.WriteLine($"Found {Swarms.Count} swarms")
  69:              Swarms.ToList.ForEach(Sub(X) Console.WriteLine(X.ID))
  70:              Console.WriteLine()
  71:          Catch ex As Exception
  72:              Console.WriteLine("DockerHub.Swarm: " & ex.Message)
  73:          End Try
  75:          Try
  76:              Dim Tasks As IList(Of TaskResponse) = DockerHub.Tasks.ListAsync().Result
  77:              Console.WriteLine($"Found {Tasks.Count} tasks")
  78:              Tasks.ToList.ForEach(Sub(X) Console.WriteLine(X.ID))
  79:              Console.WriteLine()
  80:          Catch ex As Exception
  81:              Console.WriteLine("DockerHub.Tasks: " & ex.Message)
  82:          End Try
  84:          Dim JReport As JObject = JToken.FromObject(DockerReport1)
  85:          Dim DockerReportString As String = JsonConvert.SerializeObject(JReport, New JsonSerializerSettings With {.Formatting = Formatting.Indented})
  86:          Dim DockerReportFileName As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DockerReport.json")
  87:          IO.File.WriteAllText(DockerReportFileName, DockerReportString)
  88:          Process.Start("C:\Program Files\Mozilla Firefox\firefox.exe", DockerReportFileName)
  89:      End Sub
 115:  End Module

And this is a result of my test DockerReport.json.

Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: http://www.vb-net.com/DockerizingLinux2/Index.htm