Docker

Docker 是一個開放原始碼軟體,是一個開放平台,用於開發應用、交付應用、執行應用。 Docker允許用戶將基礎設施中的應用單獨分割出來,形成更小的顆粒,從而提高交付軟體地速度。 Docker容器 與虛擬機器類似,但原理上,容器是將作業系統層虛擬化,虛擬機器則是虛擬化硬體,因此容器更具有可攜式性、高效地利用伺服器。

Secure Docker Network

With SWAG

SWAG - Secure Web Application Gateway

With iptables

對於 Container 不同啟動方式,在 iptables 的腳本設計會有些不同。

for docker-compose)

#!/usr/bin/env bash

# Purpose: Restrict the access to the container from external IPs.
# Author: Alang, 2019/8/14
#

CONTAINER="nginx_mysql_web_1"
EXT_IF="eth0"
BRG_IF="$(ip a | awk '/br-[a-z0-9]+:/{print $0}' | awk '{print $2}' | sed 's/://g')"

if [ ! -x /usr/bin/docker ]; then
    echo "Abort: Unable to run the docker command!"
    echo "Recommendation: Check if the Docker was installed."
    exit 1
fi

if [ -z "$BRG_IF" ]; then
    echo "Abort: The network interface (br-????) with container not found. "
    exit 1
fi

# Check the network interfaces
for i in $EXT_IF $BRG_IF;do
    if ! (ip a show $i >/dev/null 2>&1); then
        echo "Abort: The network interface $i doesn't exist"
        exit 1
    fi
done

# Check if the DOCKER-USER chain exists, if it does flushing the rules.
iptables -C FORWARD -j DOCKER-USER 2> /dev/null

if [ $? -eq 0 ]; then
    # Flush all existing rules
    iptables -F DOCKER-USER
else
    echo "Abort: The chain DOCKER-USER not found!"
    echo "Recommendation: "
    echo "  - Is the Docker daemon running?"
    echo "  - Is the version of Docker 17.06+?"
    exit 1
fi

## Docker container named www-nginx public access policy
## If using docker-compose, change FORMAT to
## --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
##
#WWW_IP="$(/usr/bin/docker inspect --format {{.NetworkSettings.IPAddress}} $CONTAINER 2>/dev/null)"
WWW_IP="$(/usr/bin/docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER 2>/dev/null)"

if [ -z "$WWW_IP" ];then
    echo "Abort: The docker IP isn't detected!"
    echo "Recommendation:"
    echo "  - Is the specified container UP?"
    echo "  - Adjsut the variable \$WWW_IP."
    exit 1
fi

# Default action
iptables -I DOCKER-USER -i $EXT_IF -j DROP   

## Insert web server container filter rules  
## Allow All IPs to access 80 & 443
#iptables -I DOCKER-USER -i $EXT_IF -p tcp -d $WWW_IP --dport 80  -j ACCEPT
#iptables -I DOCKER-USER -i $EXT_IF -p tcp -d $WWW_IP --dport 443 -j ACCEPT
## Allow Cloudflare IPs to acces 443
## Check the IP list of Cloudflare at the URL https://www.cloudflare.com/ips-v4
iptables -I DOCKER-USER -o $BRG_IF -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I DOCKER-USER -i $BRG_IF ! -o $BRG_IF -j ACCEPT
iptables -I DOCKER-USER -m state --state RELATED -j ACCEPT
iptables -I DOCKER-USER -i $BRG_IF -o $BRG_IF -j ACCEPT

iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 173.245.48.0/20  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 103.21.244.0/22  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 103.22.200.0/22  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 103.31.4.0/22    --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 141.101.64.0/18  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 108.162.192.0/18 --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 190.93.240.0/20  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 188.114.96.0/20  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 197.234.240.0/22 --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 198.41.128.0/17  --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 162.158.0.0/15   --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 104.16.0.0/12    --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 172.64.0.0/13    --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 131.0.72.0/22    --dport 443 -j ACCEPT

# My IPs
#iptables -I DOCKER-USER -i $EXT_IF -p tcp  -d $WWW_IP -s 219.70.88.133    --dport 443 -j ACCEPT


[ $? -eq 0 ] && echo "Done."
## EOL

for docker)

#!/usr/bin/env bash

# Usage:
# timeout 10 docker_iptables.sh
#
# Use the builtin shell timeout utility to prevent infinite loop (see below)

CONTAINER="raida17"

if [ ! -x /usr/bin/docker ]; then
    exit
fi

# Check if the PRE_DOCKER chain exists, if it does there's an existing reference to it.
iptables -C FORWARD -o docker0 -j PRE_DOCKER 2> /dev/null

if [ $? -eq 0 ]; then
    # Remove reference (will be re-added again later in this script)
    iptables -D FORWARD -o docker0 -j PRE_DOCKER
    # Flush all existing rules
    iptables -F PRE_DOCKER
else
    # Create the PRE_DOCKER chain
    iptables -N PRE_DOCKER
fi

# Default action
iptables -I PRE_DOCKER -j DROP

# Docker Containers Public Admin access (insert your IPs here)
#iptables -I PRE_DOCKER -i eth0 -s 192.184.41.144 -j ACCEPT
#iptables -I PRE_DOCKER -i eth0 -s 120.29.76.14 -j ACCEPT

# Docker Containers Restricted LAN Access (insert your LAN IP range or multiple IPs here)
#iptables -I PRE_DOCKER -i eth1 -s 192.168.1.101 -j ACCEPT
#iptables -I PRE_DOCKER -i eth1 -s 192.168.1.102 -j ACCEPT

# Docker internal use
iptables -I PRE_DOCKER -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I PRE_DOCKER -i docker0 ! -o docker0 -j ACCEPT
iptables -I PRE_DOCKER -m state --state RELATED -j ACCEPT
iptables -I PRE_DOCKER -i docker0 -o docker0 -j ACCEPT

# Docker container named www-nginx public access policy
# If using docker-compose, change format to
# --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
WWW_IP_CMD="/usr/bin/docker inspect --format {{.NetworkSettings.IPAddress}} $CONTAINER"
WWW_IP=$($WWW_IP_CMD)

# Double check, wait for docker socket (upstart docker.conf already does this)
while [ ! -e "/var/run/docker.sock" ]; do echo "Waiting for /var/run/docker.sock..."; sleep 1; done

# Wait for docker web server container IP
while [ -z "$WWW_IP" ]; do echo "Waiting for $CONTAINER IP..."; WWW_IP=$($WWW_IP_CMD); done

## Insert web server container filter rules
## Allow All IPs to access 80 & 443
#iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP --dport 80  -j ACCEPT
#iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP --dport 443 -j ACCEPT
## Allow Cloudflare IPs to acces 443
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 103.21.244.0/22 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 103.22.200.0/22 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 103.31.4.0/22 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 104.16.0.0/12 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 108.162.192.0/18 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 131.0.72.0/22 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 141.101.64.0/18 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 162.158.0.0/15 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 172.64.0.0/13 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 173.245.48.0/20 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 188.114.96.0/20 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 190.93.240.0/20 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 197.234.240.0/22 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 198.41.128.0/17 --dport 443 -j ACCEPT
iptables -I PRE_DOCKER -i eth0 -p tcp -d $WWW_IP -s 199.27.128.0/21 --dport 443 -j ACCEPT


# Finally insert the PRE_DOCKER table before the DOCKER table in the FORWARD chain.
iptables -I FORWARD -o docker0 -j PRE_DOCKER
With iptables + ipset
With ufw
With Firewalld
# Removing DOCKER-USER CHAIN (it won't exist at first)
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER

# Flush rules from DOCKER-USER chain (again, these won't exist at first; firewalld seems to remember these even if the chain is gone)
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER

# Add the DOCKER-USER chain to firewalld
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

# Add rules (see comments for details)
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment "This allows docker containers to connect to the outside world"
## Change the Docker Subnet address to your network settings (Could be 172.18.0.0/16)
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -j RETURN -s 172.17.0.0/16 -m comment --comment "allow internal docker communication"
## Add an ip to allow access to the docker exposed port
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m multiport --dports 9392 -s 10.18.109.20/32 -j ACCEPT -m comment --comment "my allowed ip address to openvas port"

# Add as many ip or other rules and then run this command to block all other traffic
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -j REJECT -m comment --comment "reject all other traffic"

# restart the services
systemctl stop docker
systemctl stop firewalld
systemctl start firewalld
systemctl start docker

 

 

MySQL Backup

從 Host 執行

backup-db.sh

#!/bin/bash

BKDIR="db_backups"
BKFILE="cloudcoin_raida#17.`date +%y%m%d`.sql"
DBUSER="ThisDBUser"
DBNAME="cloudcoin_raida"
CONTNAME="raida17"
CONTDIR="/opt/data/$BKDIR"
HOSTDIR="/docker_vol/raida/data/$BKDIR"
KEEP=2


[ -d $HOSTDIR ] || mkdir $HOSTDIR
cd $HOSTDIR
start_s=$(date +%s)
echo "*********** START `date "+%F@%T"` **************"

# Full Backup
echo "-> Back up Full DB "
docker exec $CONTNAME bash -c "mysqldump --single-transaction --add-drop-table -u $DBUSER  $DBNAME > $CONTDIR/$BKFILE"
retval=$?

if [ $retval -eq 0 ];then
    echo "... [success]"
else
    echo "... [failure]**"
    exit 1
fi

# Compress the files
echo "-> Compress the dump file "
gzip -f $HOSTDIR/$BKFILE

retval=$?
if [ $retval -eq 0 ];then
    echo "... [success]"
    BKFILE="$BKFILE.gz"
else
    echo "... [*failure*]"
    exit 1
fi

# Purge the old files
echo "-> Purge the old files"
ls_files=($(ls -lt cloudcoin_raida* | awk -F ' ' '{print $9}'))
len=${#ls_files[@]}
i=$KEEP
while (($i < $len));do
        rm -vf "${ls_files[$i]}"
        let i++
done

end_s=$(date +%s)
elapsed=$(( end_s - start_s  ))
echo 
echo "Elapsed: $elapsed seconds"
echo "Output File: $HOSTDIR/$BKFILE"
echo "*********** END `date "+%F@%T"` **************"
echo

 

基本指令操作

常用指令
# List running containers
docker ps

# ssh into the container
docker exec -it <container-name> /bin/sh

# Restart a container
docker restart <container-name>

#Show running container stats
docker stats

# Check docker daemon disk space usage
docker system df

# Purge those unused images, networks, containers and volumes 
docker system prune

# Check the container log
docker logs <container-name>

# Seacrh docker registry for image
docker search <image-name>

# Create and start a container
docker run -it <image-name> /bin/bash

# Check container's exposed ports
docker port {container-name}
線上求助
man docker <command>
man docker build
man docker rmi 
管理 Images
## 搜尋 Docker Hub 上的 image name
docker search lamp

## 顯示已下載所有 image name
docker images

## 檢視既有 image 的詳細資訊
docker inspect <image-name>

## 網路下載 image
docker pull ubuntu:13.10

## 刪除已下載的 image
docker rmi <image-name>

## 刪除所有 images
docker rmi $(docker images -q)

## 刪除所有 images,除了 my-images 以外
docker rmi $(docker images | grep -v 'ubuntu\|my-image' | awk {'print $3'})

## 刪除所有 <none> 的有問題 images
docker rmi $(docker images -f "dangling=true" -q)

## 刪除與 myapp/myimage 相關的 <none> 的 images
docker rmi $(docker images myapp/myimage -f "dangling=true" -q)

## 列出所有 images 之間繼承的關係
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images -t
管理 Containers
## 開啟並進入 container 的 console
docker run -it <image-name> /bin/bash
docker run -it --name <container-name> <image-name> /bin/bash

## 以 daemon 方式啟動 container
docker run -d -p 11180:80 <image-name>
docker run -d --name web <image-name>
TIP: 啟動 container 時可以自訂名稱以方便管理

docker run -d -p 80:80 --rm <image-name>
加上 --rm 時,當停止 container 時,會自動被刪除(與 docker rm 指令相同),且無法使用啟動指令
(docker start) 只能使用 docker run 啟動。 

## 檢查目前已經啟動的 containers
docker ps
docker ps -a

## 檢視開啟中 container 的詳細資訊,包含 Volumes、IP、Hostname 等等
docker inspect <container-id>

## 刪除指定的 container
docker rm <container-id> 

## 刪除所有的 containers
NOTE: 小心,這也會刪除正在執行的 container
docker ps -a -q | xargs -n 1 docker rm
docker rm $(docker ps -aq) 

## 刪除所有已經終止的 container
docker ps -a | grep "Exited" | awk '{print $1}' | xargs docker rm
docker rm $(docker ps --all -q -f status=exited)
NOTE: 這常用於在重新啟動 container 或 rebuild image 時遇到錯誤訊息的解決方法。

## 停止 container
docker stop <container-id>

## Stop all containers
docker stop $(docker ps -aq)
docker ps -aq | xargs docker stop

## 匯出 container
docker export <container-id>  > ubuntu-mysql.tar 

## 匯入 container
cat ubuntu-mysql.tar | docker import - <image-name>

## 跳離目前開啟中的 container
按下 Ctrl P 後再按 Ctrl Q
NOTE: 如果無法成功跳離,原因可能是 Ctrl+P 是 Bash 內定的快捷鍵(回到前一個指令)

## 重新進入開啟中的 container
docker attach <container-id>
或
docker attach <container-name>
如果 container 是以 daemon 啟動,改用以下方式
docker exec -it <container-id/name> /bin/bash

## 儲存開啟中 container 內容
docker commit <container-id> <image-name> 

## 顯示指定 container 的 IP
docker inspect <container-id> | grep IPAddress | cut -d '"' -f 4 

Check Container CPU and RAM Usage

docker stats
docker stats --no-stream
docker stats --no-stream -a
docker stats <container-name>
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemPerc}}"
docker ps --no-trunc --format "{{.Names}}\t{{.ID}}"

複製/搬移 container 至另一部主機
## Stop the container
docker stop <container-name>

## Save container image
docker commit <container-name> mycontainerimage
docker save mycontainerimage | gzip > mycontainerimage.tar.gz

## Load container image to destination host
gunzip -c mycontainerimage.tar.gz | docker load 

## Transfer image without creating a file
docker save mycontainerimage | gzip | ssh root@203.0.113.1 'gunzip | docker load'

TIP:
執行 exit 可以離開目前的 container,回到原先的 Linux

一旦離開 container,所有之前做過的變更,將全部失效,如果要保留做過的變更,必須使用 commit 產生一個新的 image。

-p 將 Host 的 port 11180 轉送至 container 的 port 80

管理 Volumes

Docker 的 Data Volume 是一個很特別的目錄設計,主要用在不同 containers 之間的資料分享,永久保存資料等。

主要特點:

// 啟用 volume
docker run -t -i -p 80:80 -v ${PWD}/webapp:/webapp alang/centos5-lamp_php51

TIP:
格式:-v <host-dir>:<container-dir>

在 container 內會自動新增一個目錄名為 /webapp,儲存到這個目錄的所有資料都會被保留。

被保留的資料會儲存到 host 的某個特定目錄內,即使 container 被移除,這些資料還是會存在,要如何找到這個特定目錄:

docker inspect -f {{.Volumes}} <container-id>

一般預設會是
/var/lib/docker/vfs/dir/bfebd8cb6......


Docker Network
# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                      NAMES
7ccaf6119fa8        nginx:latest        "nginx -g 'daemon of…"   2 days ago          Up 39 hours         0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx_mysql_web_1
81a920bb51a6        nginx_mysql_php     "docker-php-entrypoi…"   2 days ago          Up 2 days           9000/tcp                                   nginx_mysql_php_1
437a7501198f        mariadb:10.3        "docker-entrypoint.s…"   2 days ago          Up 2 days           3306/tcp                                   nginx_mysql_db_1

# docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
852eff02220e        bridge                bridge              local
334d2b8571a4        host                  host                local
b97cae66a977        nginx_mysql_default   bridge              local
40d15afb34b4        none                  null                local

# docker network inspect -f '{{json .IPAM.Config}}' bridge | jq -r .[].Subnet
# docker network inspect -f '{{json .IPAM.Config}}' bridge | jq -r .[].Gateway

# brctl show
bridge name     bridge id               STP enabled     interfaces
br-b97cae66a977         8000.0242569e79ff       no              veth3ce8cbd
                                                        veth5129652
                                                        veth55dcdf7
docker0         8000.0242faff70bb       no

取得 container IP

## Method #1: By inspecting the container
docker inspect <container_id> | grep -i ipaddr
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container_id>

# get an IP address associated with a specific network
# docker container inspect -f '{{ .NetworkSettings.Networks.<NETWORK NAME>.IPAddress }}' <CONTAINER_ID_OR_NAME>
docker container inspect -f '{{ .NetworkSettings.Networks.bridge.IPAddress }}' ubuntu-ip

## Method #2: Using the container's shell
docker exec -it <container-name> sh
> ip
or
> ifconfig

# if you get the errors with 'command not found', following the below steps to install the relevant packages.
> apt update -qq
> apt install iproute2 -yqq

## Method #3: By inspecting the network itself
# docker network inspect <NETWORK NAME>
docker network inspect bridge | jq .[].Containers
docker network inspect bridge | jq '.[].Containers."<CONTAINER ID>".IPv4Address'

docker network inspect -f '{{json .Containers}}' bridge | \
jq '..|if type == "object" and has("Name") then select(.Name=="<CONTAINER NAME>") | .IPv4Address else empty end' -r
管理 Docker

檢查版本資訊

# 檢查 Docker 版本
docker version

# Docker 更多資訊
docker info

host 與 container 間交換檔案

docker cp <container-name>:/etc/nginx/nginx.conf /data/web/conf
docker cp host_source_path my_container:destination_path
docker cp -a host_source_path my_container:destination_path

定期清除沒用的物件

# 這會清除所有已停止的 container,沒有在用的docker層網路介面與 <none> 不完整的 image。
docker images --quiet --filter "dangling=true"
docker system prune

# 上述指令會保留 volume 裡的資料,如果要一併清除,須加上 --volumes
docker system prune -a --volumes

# For volumes only
docker volume ls -f dangling=true
docker volume prune
Restart Policy

自動啟動 container

# Add --restart=unless-stopped
docker run -d -p 4449:4449 --name myst --restart=unless-stopped 
Docker Logging
docker logs {container-name}
docker logs --tail 50 {container-name}
docker logs -f {container-name}
docker logs -f --tail 20 {container-name}

# View timestamp in Docker logs
docker logs -t {container-name}
docker -n=10 -t {container-name}

# Viewing Docker logs in a specified time period
docker logs --since 1440m -t {container-name}
docker logs --until 1440m -t {container-name}
docker logs --since 2021-07-28 -t {container-name} 

Docker system service logs

sudo journalctl -u docker

Where are Docker logs stored

sudo ls -lh /var/lib/docker/containers

Enabling Log Rotation for Docker (JSON)

Edit /etc/docker/daemon.json

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3" 
  }
}

Restart Docker daemon

sudo systemctl restart docker
Disk Space Usage
avimanyu@iborg-desktop:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          4         4         1.065GB   0B (0%)
Containers      4         4         5.705kB   0B (0%)
Local Volumes   7         7         1.108GB   0B (0%)
Build Cache     0         0         0B        0B
avimanyu@iborg-desktop:~$ docker system df -v
Images space usage:

REPOSITORY                               TAG       IMAGE ID       CREATED         SIZE      SHARED SIZE   UNIQUE SIZE   CONTAINERS
ghost                                    4.32.0    b40265427368   8 weeks ago     468.8MB   0B            468.8MB       1
jrcs/letsencrypt-nginx-proxy-companion   latest    037cc4751b5a   13 months ago   24.35MB   0B            24.35MB       1
jwilder/nginx-proxy                      latest    509ff2fb81dd   15 months ago   165MB     0B            165MB         1
mariadb                                  10.5.3    f5d2bcaf057b   20 months ago   407MB     0B            407MB         1

Containers space usage:

CONTAINER ID   IMAGE                                    COMMAND                  LOCAL VOLUMES   SIZE      CREATED        STATUS        NAMES
899cc90e85d9   ghost:4.32.0                             "docker-entrypoint.s…"   1               0B        8 weeks ago    Up 8 weeks    ghost_ghost_6
17b58fdafbce   jrcs/letsencrypt-nginx-proxy-companion   "/bin/bash /app/entr…"   4               571B      3 months ago   Up 2 months   letsencrypt-proxy-companion
58f99f46ee03   jwilder/nginx-proxy                      "/app/docker-entrypo…"   5               5.13kB    3 months ago   Up 2 months   jwilder-nginx-proxy
fb907286b60e   mariadb:10.5.3                           "docker-entrypoint.s…"   1               2B        3 months ago   Up 2 months   ghost_db_1

Local Volumes space usage:

VOLUME NAME                      LINKS     SIZE
ghostdb                          1         434.7MB
jwilder-nginx-with-ssl_acme      2         36.09kB
jwilder-nginx-with-ssl_certs     2         25.12kB
jwilder-nginx-with-ssl_dhparam   1         1.525kB
jwilder-nginx-with-ssl_html      2         1.106kB
jwilder-nginx-with-ssl_vhost     2         556B
ghost                            1         674MB

Build cache usage: 0B

CACHE ID   CACHE TYPE   SIZE      CREATED   LAST USED   USAGE     SHARED
avimanyu@iborg-desktop:~$ docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
busybox       latest    beae173ccac6   6 weeks ago     1.24MB
ubuntu        latest    fb52e22af1b0   5 months ago    72.8MB
alpine        latest    49f356fa4513   10 months ago   5.61MB
hello-world   latest    d1165f221234   11 months ago   13.3kB
avimanyu@iborg-desktop:~$ docker ps --size
CONTAINER ID   IMAGE     COMMAND      CREATED         STATUS         PORTS     NAMES           SIZE
1171dcfb7e06   alpine    "sleep 10"   10 months ago   Up 9 seconds   
# Overlay2 is the default Docker storage driver on Ubuntu. 
# You can confirm this by running the 'docker info' command and looking for the Storage Drive
# To get the <<hash-named-directory> by the command 'docker inspect <image-name>'
sudo du -sh /var/lib/docker/overlay2/<hash-named-directory>/
# Specific Volume Disk Usage
$ docker volume ls
DRIVER    VOLUME NAME
local     d502589845f7ae7775474bc01d8295d9492a6c26db2ee2c941c27f3cac4449d1
local     e71ee3960cfef0a133d323d146a1382f3e25856480a727c037b5c81b5022cb1b
local     test-data

$ sudo du -sh /var/lib/docker/volumes/test-data/_data
4.0K	/var/lib/docker/volumes/test-data/_data

FAQ

無法移除 image

rror response from daemon: conflict: unable to delete dd78a816fb76 (must be forced) - image is referenced in multiple repositories

Solution: 如果同一個 image id 有兩個不同 image 名稱,在刪除這個 image id 時可能會遇到類似的錯誤訊息,刪除指令可以改用 image 名稱試試。

root@greencloud-us-1TB:~/watchtower# docker images
REPOSITORY                         TAG       IMAGE ID       CREATED         SIZE
mysteriumnetwork/myst              latest    5c613786d102   39 hours ago    53.3MB
presearch/node                     latest    27216957eb08   10 days ago     69.8MB
storjlabs/storagenode              latest    0ac3b4808897   3 weeks ago     124MB
lscr.io/linuxserver/transmission   latest    8cad68f9dac4   7 months ago    95.7MB
containrrr/watchtower              latest    333de6ea525a   8 months ago    16.9MB
jellyfin/jellyfin                  latest    0aa773b67433   13 months ago   717MB
presearch/auto-updater             latest    dd78a816fb76   17 months ago   16.4MB    <===
containrrr/watchtower              <none>    dd78a816fb76   17 months ago   16.4MB  <===
storjlabs/watchtower               latest    6af6621e20c1   2 years ago     14.3MB
nate/dockviz                       latest    93b5259c1e18   4 years ago     6.61MB


root@greencloud-us-1TB:~/watchtower# docker rmi dd78a816fb76
Error response from daemon: conflict: unable to delete dd78a816fb76 (must be forced) - image is referenced in multiple repositories


root@greencloud-us-1TB:~/watchtower# docker rmi presearch/auto-updater containrrr/watchtower
Untagged: presearch/auto-updater:latest
Untagged: presearch/auto-updater@sha256:3283e0b5be326d77ff4f4e8b7a91d46aaa1d511c74877b5a32f161548812d00c
Untagged: containrrr/watchtower:latest
Untagged: containrrr/watchtower@sha256:bbf9794a691b59ed2ed3089fec53844f14ada249ee5e372ff0e595b73f4e9ab3
Deleted: sha256:333de6ea525af9137e1f14a5c1bfaa2e730adca97ab97f74d738dfa99967f14f
Deleted: sha256:f493af3d0a518d307b430e267571c926557c85222217a8707c52d1cf30e3577e
Deleted: sha256:62651dc7e144aa8c238c2c2997fc499cd813468fbdc491b478332476f99af159
Deleted: sha256:83fe5af458237288fe7143a57f8485b78691032c8c8c30647f8a12b093d29343
無法存取 localhost 網頁

如果 container 啟用一個本地端的網站,從 host 端無法直接使用 http://localhost:XXX 方式存取;改用 http://host.docker.internal:XXX 網址。

Docker Compose 指令

NOTE: 新版指令改成 docker compose

NOTE: 服務一旦佈署完成,docker-compose.yml 的路徑如果有變更,就不能繼續使用指令 docker-compose 來管理 container,不過已經啟動的服務運行不會影響,但關閉後就無法再被啟動。

新增與啟動所有應用服務

# For all services
docker-compose up -d

# For specified service
docker-compose up -d <service-name>

Build the image of the service

docker-compose build <service-name>
docker-compose up --rebuild <service-name>

目前的應用服務狀態

docker-compose ps

重啟所有應用服務但不要刪除所屬 container

docker-compose stop
docker-compose start 

如果 container 屬性沒有異動,只是所屬的服務需要重啟載入新設定,可以使用這。

停止所有應用服務並刪除所屬 container

# For all services
docker-compose down

# For a specified service
docker-compose stop <service-name>
docker-compose rm -f <service-name>

NOTE:

- 如果有修改 docker-compose.yml,必須使用 down 關閉服務後,用 up -d 重新啟動。

- 停止服務後,重新啟動只能使用 docker-compose up -d

檢視服務啟動失敗的日誌

docker-compose logs

執行某個應用服務的特定程序

docker-compose exec <service-name> sh -c "pwd"

環境變數檔 --env-file

docker compose --env-file .env up -d
docker-compose.yml

environment + healthcheck

services:
  db:
    image: pgvector/pgvector:0.6.2-pg15
    restart: always
    ports:
      - '5432:5432'
    environment:
      POSTGRES_USER: talkdai
      POSTGRES_PASSWORD: talkdai
      POSTGRES_DB: talkdai
    volumes:
       - ./etc/db-extensions.sql:/docker-entrypoint-initdb.d/db-extensions.sql
    healthcheck:
      test: ["CMD", "pg_isready", "-d", "talkdai", "-U", "talkdai"]
      interval: 10s
      timeout: 5s
      retries: 5
  dialog:
    image: ghcr.io/talkdai/dialog:latest
    volumes:
      - ./data/:/app/data/
    ports:
      - '8000:8000'
    depends_on:
      db:
        condition: service_healthy
    environment:
      - PORT=8000
      - DATABASE_URL=postgresql://talkdai:talkdai@db:5432/talkdai
      - OPENAI_API_KEY=sk-your-openai-api-key
      - STATIC_FILE_LOCATION=/app/static
      - DIALOG_DATA_PATH=../data/your.csv
      - PROJECT_CONFIG=../data/your.toml

healthcheck(web) + build + networks

networks:
  net:
    driver: bridge

services:
  server:
    image: server
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      # Be aware that indexed data are located in "/chroma/chroma/"
      # Default configuration for persist_directory in chromadb/config.py
      # Read more about deployments: https://docs.trychroma.com/deployment
      - chroma-data:/chroma/chroma
    command: "--workers 1 --host 0.0.0.0 --port 8000 --proxy-headers --log-config chromadb/log_config.yml --timeout-keep-alive 30"
    environment:
      - IS_PERSISTENT=TRUE
      - CHROMA_SERVER_AUTHN_PROVIDER=${CHROMA_SERVER_AUTHN_PROVIDER}
      - CHROMA_SERVER_AUTHN_CREDENTIALS_FILE=${CHROMA_SERVER_AUTHN_CREDENTIALS_FILE}
      - CHROMA_SERVER_AUTHN_CREDENTIALS=${CHROMA_SERVER_AUTHN_CREDENTIALS}
      - CHROMA_AUTH_TOKEN_TRANSPORT_HEADER=${CHROMA_AUTH_TOKEN_TRANSPORT_HEADER}
      - PERSIST_DIRECTORY=${PERSIST_DIRECTORY:-/chroma/chroma}
      - CHROMA_OTEL_EXPORTER_ENDPOINT=${CHROMA_OTEL_EXPORTER_ENDPOINT}
      - CHROMA_OTEL_EXPORTER_HEADERS=${CHROMA_OTEL_EXPORTER_HEADERS}
      - CHROMA_OTEL_SERVICE_NAME=${CHROMA_OTEL_SERVICE_NAME}
      - CHROMA_OTEL_GRANULARITY=${CHROMA_OTEL_GRANULARITY}
      - CHROMA_SERVER_NOFILE=${CHROMA_SERVER_NOFILE}
    restart: unless-stopped # possible values are: "no", always", "on-failure", "unless-stopped"
    ports:
      - "8000:8000"
    healthcheck:
      # Adjust below to match your container port
      test: [ "CMD", "curl", "-f", "http://localhost:8000/api/v1/heartbeat" ]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - net

volumes:
  chroma-data:
    driver: local

Installation

Docker Compose

Updated: 新版 Docker Compose 已經整合進 Docker 核心程式

安裝方式

sudo apt-get install docker-compose-plugin

使用方式

docker compose version

The latest download: https://docs.docker.com/compose/install/

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# If you can't run the command, creating a symblic link as follows.
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

docker-compose --version
ARM64 Linux
# Download: https://github.com/docker/compose/releases
sudo curl -L --fail https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-aarch64 -o /usr/local/bin/docker-compose
sudo chmod 0755 /usr/local/bin/docker-compose

Docker

Latest: https://docs.docker.com/engine/install/

Fedora 41
sudo dnf -y install dnf-plugins-core
sudo dnf-3 config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo

sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Ubuntu 20.04/22.04
sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
    
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Debian 9/10
sudo apt-get update

sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl gnupg \
    lsb-release
    

# Add Docker's GPG key
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg

# Set up the repository
echo \
  "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Verify that Docker is installed correctly.
sudo dokcer version
sudo docker run hello-world
RedHat 8

RHEL 8 只有 IBM Z(s390x) 架構才有支援 Docker,如果是 x86_64 可以使用 CentOS 版本來安裝。

預設不支援 docker-ce 安裝,必須手動加上套件庫位址

系統更新 (需有訂閱並完成註冊 RHN)

dnf update

手動新增 docker-ce 套件庫位址

編輯 /etc/yum.repos.d/docker-ce.repo

[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg

或直接執行

dnf install yum-utils
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

更新 docker-ce 相依性套件

dnf update

TIP: 如果遇到一些套件相依性的錯誤,執行下一步的移除衝突套件後,再執行一次 dnf update

移除不用的衝突套件

dnf remove podman buildah

安裝 docker-ce

dnf install docker-ce docker-ce-cli containerd.io

啟動與自動啟動

systemctl start docker.service
systemctl enable docker.service

檢查 docker 版本

[root@mydocker ~]# docker version
Client: Docker Engine - Community
 Version:           20.10.14
 API version:       1.41
 Go version:        go1.16.15
 Git commit:        a224086
 Built:             Thu Mar 24 01:47:44 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.14
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.15
  Git commit:       87a90dc
  Built:            Thu Mar 24 01:46:10 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.11
  GitCommit:        3df54a852345ae127d1fa3092b95168e4a88e2f8
 runc:
  Version:          1.0.3
  GitCommit:        v1.0.3-0-gf46b6ba
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

[Optional] non-root 用戶帳戶

usermod -aG docker $USER
Raspberry Pi OS
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker Pi
docker version
docker info
docker run hello-world

Rootless Mode

Ubuntu 22.04

Stop system-wide Docker daemon

sudo systemctl disable --now docker.service docker.socket

Install

sudo apt-get install docker-ce-rootless-extras uidmap
dockerd-rootless-setuptool.sh install
docker info

Optional: Configure the systemd

systemctl --user start docker
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)

Optional: Client

# Optional #1: with socket path
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker run -d -p 8080:80 nginx
# Optional #2: with CLI context
docker context use rootless
docker run -d -p 8080:80 nginx

Dockerfile

Build Image

Basic build command

cd /path/to/Dockerfile
docker build -t your-tag/your-name .
Set the version

More details: docker-bookstack/Dockerfile at master · linuxserver/docker-bookstack · GitHub

FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.17

# set version label
ARG BUILD_DATE
ARG VERSION
ARG BOOKSTACK_RELEASE
LABEL build_version="Linuxserver.io version:- ${VERSION} Build-date:- ${BUILD_DATE}"
LABEL maintainer="homerr"

使用方法:

Build the image

docker build \
  --build-arg BUILD_DATE=$( date +"%FT%T%z" ) \
  --build-arg VERSION=v22.07.3-ls36 \
  --no-cache \
  --pull \
  -t lscr.io/linuxserver/bookstack:latest .

Get the version of container

docker inspect -f '{{ index .Config.Labels "build_version" }}' <container-name>
Examples

Chroma DB

FROM python:3.11-slim-bookworm AS builder
ARG REBUILD_HNSWLIB
RUN apt-get update --fix-missing && apt-get install -y --fix-missing \
    build-essential \
    gcc \
    g++ \
    cmake \
    autoconf && \
    rm -rf /var/lib/apt/lists/* && \
    mkdir /install

WORKDIR /install

COPY ./requirements.txt requirements.txt

RUN pip install --no-cache-dir --upgrade --prefix="/install" -r requirements.txt
RUN if [ "$REBUILD_HNSWLIB" = "true" ]; then pip install --no-binary :all: --force-reinstall --no-cache-dir --prefix="/install" chroma-hnswlib; fi

FROM python:3.11-slim-bookworm AS final

RUN mkdir /chroma
WORKDIR /chroma

COPY --from=builder /install /usr/local
COPY ./bin/docker_entrypoint.sh /docker_entrypoint.sh
COPY ./ /chroma

RUN apt-get update --fix-missing && apt-get install -y curl && \
    chmod +x /docker_entrypoint.sh && \
    rm -rf /var/lib/apt/lists/*

ENV CHROMA_HOST_ADDR "0.0.0.0"
ENV CHROMA_HOST_PORT 8000
ENV CHROMA_WORKERS 1
ENV CHROMA_LOG_CONFIG "chromadb/log_config.yml"
ENV CHROMA_TIMEOUT_KEEP_ALIVE 30

EXPOSE 8000

ENTRYPOINT ["/docker_entrypoint.sh"]
CMD [ "--workers ${CHROMA_WORKERS} --host ${CHROMA_HOST_ADDR} --port ${CHROMA_HOST_PORT} --proxy-headers --log-config ${CHROMA_LOG_CONFIG} --timeout-keep-alive ${CHROMA_TIMEOUT_KEEP_ALIVE}"]

Learning

Learning

教學文章
Management Tools

Portainer

Backup

Resilio Sync

Network
Container
Docker Security Essentials

Download: Linode_eBook_HackerSploit_DockerSecurityEssentials.pdf

Content is structured and organized as follows:

  • In The Docker Platform section, we will begin the process by explaining the various components that make up the Docker platform.
  • In the Auditing Docker Security section, we will explore the process of performing a security audit of the Docker platform. An audit identifies vulnerabilities in the configuration of the components that make up the platform.
  • In the next two sections, we will begin the process of securing the Docker host and the Docker daemon to ensure that we have a secure base to operate from:
    • Securing the Docker Host
    • Securing the Docker Daemon
  • The remaining sections of the guide will conclude by taking a look at the various ways of securing containers and the process of building secure Docker images:
    • Container Security Best Practices
    • Controlling Container Resource Consumption with Control Groups (cgroups)
    • Implementing Access Control with AppArmor
    • Limiting Container System Calls with seccomp
    • Vulnerability Scanning for Docker Containers
    • Building Secure Docker Images
Docker Monitoring
Other Tools


SWAG - Secure Web Application Gateway

SWAG is a rebirth of the letsencrypt docker image, a full fledged web server and reverse proxy that includes Nginx, Php7, Certbot (Let's Encrypt client) and Fail2ban.

Tutorials

 

 

Transfer/Move Docker

Sample: osslab-dekiwiki
docker run -d -p 8880:80 \
-v /etc/localtime:/etc/localtime:ro \
-v $PWD/vol/data:/data \
-v $PWD/vol/var-lib-mysql:/var/lib/mysql \
-v $PWD/vol/var-log-httpd:/var/log/httpd \
-v $PWD/vol/var-log-dekiwiki:/var/log/dekiwiki \
-v $PWD/vol/var-www-dekiwiki-attachments:/var/www/dekiwiki/attachments \
-v $PWD/vol/var-www-dekiwiki-bin-cache:/var/www/dekiwiki/bin/cache \
--name $NAME \
osslab-dekiwiki \
/bin/bash
Using Save Command

Save Image

# osslab-dekiwiki is image name
docker save osslab-dekiwiki > osslab-dekiwiki.tar

ls -l
-rw-r--r-- 1 root root 2519701504 Feb 27 19:19 osslab-dekiwiki.tar

Load Image

docker load < osslab-dekiwiki.tar
Using Export Command

Run the instance in detach mode

docker run -it --detach --name osslab-mig osslab-dekiwiki

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                            NAMES
673f45c27a8b        osslab-dekiwiki     "/bin/sh -c /startup…"   5 seconds ago       Up 4 seconds        80/tcp                           osslab-mig

Export Docker Container

docker export 673f45c27a8b > osslab-mig.tar

ls -l
-rw-r--r-- 1 root root 1143085568 Feb 27 14:21 osslab-mig.tar

Import Docker Container to the target machine

tar -c osslab-mig.tar | docker import - osslab-dekiwiki
Move Container
## Stop the container
docker stop <container-name>

## Save container image
docker commit <container-name> mycontainerimage
docker save mycontainerimage | gzip > mycontainerimage.tar.gz

## Load container image to destination host
gunzip -c mycontainerimage.tar.gz | docker load 

## Transfer image without creating a file
docker save mycontainerimage | gzip | ssh root@203.0.113.1 'gunzip | docker load'
Backup & Restore Volume

Backup volume

# Backup
cd /root/osslab
docker stop osslab-dekiwiki
docker rm osslab-dekiwiki
tar czf osslab-val.tar.gz vol

# Restore to target machine
cd /root/osslab
tar xpzf osslab-val.tar.gz
FAQ

Q: Import 完成,但無法啟動 container。

Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown
Error: failed to start containers: osslab

Solution: 不要使用 Export-Import 改成 Save-Load 方式移轉。

Q: 執行 docker load 發生錯誤。

open /var/lib/docker/tmp/docker-import-863867335/bin/json: no such file or directory 

Solution: 只有 Save 的 image 檔才能執行 docker load;Export 的 image 檔只能用 docker import

Portainer

Introduction

https://www.portainer.io/ 

A centralized service delivery platform for containerized apps.

Portainer can be deployed on top of any K8s, Docker or Swarm environment. It works seamlessly in the cloud, on prem and at the edge to give you a consolidated view of all your containers.

Portainer is available in 2 versions: Business Edition (BE) and Community Edition (CE). Portainer BE is a fully supported, service delivery platform suitable for use organization-wide, whilst CE is open source and suitable for use by small, self-supporting teams.

Installation
Learning

Watchtower

Setup

run.sh

docker stop watchtower-myst-presearch-storj
docker rm watchtower-myst-presearch-storj

docker pull containrrr/watchtower
docker run -d \
    --name watchtower-myst-presearch-storj \
    -v /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower \
    myst presearch-node storagenode
docker ps
Tutorials

進階應用

存取 Container 網路

有個 Container 服務只需要與同個 Docker Network 的其他 Container 做通訊,平常不需要對外開放通訊埠,只有在開發或 Debug 時才需要從外部存取這個 Container 的網路,但又不想每次都要重啟 container 來開啟關閉需要的 port。

使用一個 socat 的 forwarder container,將外部 port 導入內部目的 container 的 port。

socat-forwarder.png

# 以 Nginx Web 為例
$ docker run -d --name target nginx

$ TARGET_IP=$(
  docker inspect \
    -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
    target
)

$ NETWORK=$(
  docker container inspect \
    -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s" $net}}{{end}}' \
    target
)

$ docker run -d \
  --publish ${HOST_PORT}:${TARGET_PORT} \
  --network ${NETWORK} \
  --name forwarder nixery.dev/socat \
    socat TCP-LISTEN:${TARGET_PORT},fork TCP-CONNECT:${TARGET_IP}:${TARGET_PORT}
    
$ curl localhost:${TARGET_PORT}
不停機做 Container 更新

 

Linux Distros for Running Container

Photon OS

Default account credentials:

Run Docker

# To run Docker from the command prompt
systemctl start docker

# To ensure Docker daemon service runs on every subsequent VM reboot
systemctl enable docker

 

Dockerize Custom Application

Tutorials