Docker 数据管理

By | 2021年12月31日

1 -v 目录映射

在主机的”/home/admin/dev/aaa“目录和docker中的目录/data 建立map 关系:
$ docker run -it --name 'faltomcat-vim' -v /home/admin/dev/aaa:/data faltomcat-vim bash

2 Volumn 数据卷

数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。
在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷。

// 创建数据卷my-vol
docker volume create my-vol 
// 启动一个挂载数据卷的容器,target是容器中的路径。
docker run --mount source=my-vol,target=/webapp nginx  
// 查看所有数据卷
docker volume ls 
// 查看指定数据卷的信息
docker volume inspect my-vol 
// 删除指定数据卷
docker volume rm my-vol 
// 无主的数据卷可能会占据很多空间,使用这条命令可以清理掉
docker volume prune

对于CentOS7,宿主主机volumn目录再:/var/lib/docker/volumes/
经测试,如果手工删除了 volumn 映射的目录,重启 swarm 的 service 是不会成功的,需要先重启docker才行,如:service docker restart。

3 Dockerfile 中的 VOLUME 与 -v 使用注意

4 提前创建Volumn供Swarm Compose使用

以某项目为例,在执行Dockerfile文件时候,我们使用了COPY/ADD/RUN等命令来创建目录或拷贝文件:

这里compose会自己创建volumn,在多数情况是没问题的。但我们现在有个需求,客户在部署我们的镜像时,需要让他更改系统默认的 hostIP 127.0.0.1,不更改就只能本机访问。若想避免客户每次都去 /volumns/falpolicy_falhome/_data/falpolicy/config/application.properties 中改的话,可以写个脚本,提前创建Volumn,命令:docker volume create falhome。一旦提前创建volumn后,这个Dockfile里红框框圈出来的命令就不会再被执行了,即无法拷贝和创建子目录了在这个volumn里。这可以通过将 mkdir 命令放 entrypoint.sh 中,或者在刚才创建volumn的脚本中来创建。下面提供两种方案具体实现。

4.1 方案1:docker service 还没跑起来就提前先创建 docker volumn

  1. 首次运行 startup.sh,输入IP存储到startup.sh所在目录的 application.properties;第二次调用startup.sh 不让输入IP,若要改IP请直接执行改IP脚本。
  2. 然后创建 docker volumn ,建立 application.properties 所在的目录,并将 application.properties 拷贝进去。
  3. load and run images.

下面是我自己写的 Linux Shell 脚本,set-falpolicy-host-ip.sh:

#!/bin/bash

f () {
    errcode=$? # save the exit code as the first thing done in the trap function
    echo "=================================================="
    echo "error $errorcode"
    echo "the command executing at the time of the error was"
    echo "$BASH_COMMAND"
    echo "on line ${BASH_LINENO[0]}"
    echo "=================================================="
    # do some error handling, cleanup, logging, notification
    # $BASH_COMMAND contains the command that was being executed at the time of the trap
    # ${BASH_LINENO[0]} contains the line number in the script of that command
    # exit the script or return to try again, etc.
    exit $errcode  # or use some other value or do return instead
}
trap f ERR

checkRootPrivileges() {
    if [ $UID -ne 0 ]; then
        echo ">>> 运行此脚本需要管理员权限。"
        echo ">>> e.g. \"sudo $0\""
        exit 1
    fi
}
checkRootPrivileges

# 设置 application.properties 文件路径,值存放在 APP_PROP_FILE 变量中。
APP_PROP_FILE='./application.properties'
setAppPropFile() {
    if [ ! -f $APP_PROP_FILE ];then
        APP_PROP_FILE='./tool/application.properties'
    fi
}
setAppPropFile

# 获取服务器访问IP,值存放在 POLICY_ACCESS_HOST_VALUE 变量中。
POLICY_ACCESS_HOST_VALUE=""
getServerHostIP() {
    POLICY_ACCESS_HOST_VALUE=`cat $1 | grep ${POLICY_ACCESS_HOST_KEY} |awk '{print substr($1,31)}'`
}

setServerHostIP () {
    read -p "请输入 FalPolicy 服务器访问IP: " HOST_IP
    if  [ ! -n "$HOST_IP" ] ;then
        echo "FalPolicy 服务器访问IP 输入有误,2秒后自动退出。"
        sleep 2
        exit
    else
        echo ">>> 您输入的 FalPolicy 服务器访问IP: $HOST_IP"
    fi

    
    getServerHostIP ${APP_PROP_FILE}
    SOURCE_TEXT="fal.policy.server.access.host=${POLICY_ACCESS_HOST_VALUE}"
    TARGET_TEXT="fal.policy.server.access.host=${HOST_IP}"
    sed -i "s/${SOURCE_TEXT}/${TARGET_TEXT}/g" `grep ${SOURCE_TEXT} -rl ${APP_PROP_FILE}`

    getServerHostIP ${APP_PROP_FILE}
    echo ">>> 更改后的 FalPolicy 服务器访问IP: ${POLICY_ACCESS_HOST_VALUE}"

    echo ">>> docker volume create" $FALPOLICY_HOME_VOLUMN
    docker volume create $FALPOLICY_HOME_VOLUMN
     
    if [ ! -d $VOLUMN_APP_PROP_DIR ];then
        echo ">>> mkdir -p" $VOLUMN_APP_PROP_DIR
        mkdir -p $VOLUMN_APP_PROP_DIR
    fi
    echo ">>> sudo cp" $APP_PROP_FILE $VOLUMN_APP_PROP_DIR
    sudo cp $APP_PROP_FILE $VOLUMN_APP_PROP_DIR
}

# 设置 Volumn 根路径,值存放在 VOLUMN_ROOT_PATH 变量中。
VOLUMN_ROOT_PATH='/var/lib/docker/volumes'
setDockerVolumnPath () {
    echo ">>> Docker Volumn 根路径:" ${VOLUMN_ROOT_PATH}
    read -p "是否修改Docker Volumn 根路径(输入1进入修改模式,否则按回车键)? " MODIFY_VOLUMN_PATH
    if  [ "$MODIFY_VOLUMN_PATH" == "1" ] ;then
      read -p "请输入新的 Docker Volumn 根路径:" VOLUMN_PATH
        if  [ ! -n "$VOLUMN_PATH" ] ;then
            echo "Docker Volumn 根路径输入有误,2秒后自动退出。"
            sleep 2
            exit
        else
            echo ">>> 您输入的 Docker Volumn 根路径: $VOLUMN_PATH"
        fi
    fi
}

FAL_HOME=${VOLUMN_ROOT_PATH}'/falpolicy_falhome'
setFalHome() {
    fallogs=${FAL_HOME}'/_data/fallogs'
    configFileInbox=${FAL_HOME}'/_data/configFileInbox'
    if [ ! -d ${configFileInbox} ];then
        echo 'mkdir -p '${fallogs} ${configFileInbox}
        mkdir -p ${fallogs} ${configFileInbox}
        chmod 777 -R ${FAL_HOME}
    fi
}

FALPOLICY_HOME_VOLUMN='falpolicy_falhome'
VOLUMN_APP_PROP_DIR=${VOLUMN_ROOT_PATH}/${FALPOLICY_HOME_VOLUMN}/_data/falpolicy/config
VOLUMN_APP_PROP_FILE=${VOLUMN_APP_PROP_DIR}/application.properties
#VOLUMN_APP_PROP_FILE=${VOLUMN_ROOT_PATH}/falpolicy_solr_home/_data/application.properties
POLICY_ACCESS_HOST_KEY='fal.policy.server.access.host='

if [ -f $VOLUMN_APP_PROP_FILE ];then
    getServerHostIP $VOLUMN_APP_PROP_FILE
    echo ">>> 当前 FalPolicy 服务器访问IP:" ${POLICY_ACCESS_HOST_VALUE}
    read -p "是否修改当前 FalPolicy 服务器访问IP(输入1进入修改模式,否则按回车键)? " MODIFY_HOST_IP
    if  [ "$MODIFY_HOST_IP" == "1" ] ;then
        setServerHostIP
    fi
else
    setServerHostIP
fi
setDockerVolumnPath
setFalHome

4.2 方案2:docker service 跑起来后再修改 volumn 中的 application.properties 文件

#!/bin/bash

f () {
    errcode=$? # save the exit code as the first thing done in the trap function
    echo "=================================================="
    echo "error $errorcode"
    echo "the command executing at the time of the error was"
    echo "$BASH_COMMAND"
    echo "on line ${BASH_LINENO[0]}"
    echo "=================================================="
    # do some error handling, cleanup, logging, notification
    # $BASH_COMMAND contains the command that was being executed at the time of the trap
    # ${BASH_LINENO[0]} contains the line number in the script of that command
    # exit the script or return to try again, etc.
    exit $errcode  # or use some other value or do return instead
}
trap f ERR

# 设置IP
read -p "Please enter the serve host ip: " HOST_IP
if  [ ! -n "$HOST_IP" ] ;then
    echo "You have not input the serve host ip!"
else
    echo "The ip you input is $HOST_IP."
fi
FILE_PATH='/var/lib/docker/volumes/falpolicy_falhome/_data/falpolicy/config/application.properties'
POLICY_ACCESS_HOST_KEY='fal.policy.server.access.host='
POLICY_ACCESS_HOST_VALUE=`cat ${FILE_PATH} | grep ${POLICY_ACCESS_HOST_KEY} |awk '{print substr($1,31)}'`
echo "Current falpolicy server access host is:" ${POLICY_ACCESS_HOST_VALUE}

SOURCE_TEXT=fal.policy.server.access.host=${POLICY_ACCESS_HOST_VALUE}
TARGET_TEXT=fal.policy.server.access.host=${HOST_IP}
sed -i "s/${SOURCE_TEXT}/${TARGET_TEXT}/g" `grep ${SOURCE_TEXT} -rl ${FILE_PATH}`
echo "IP change completed."

echo '>>> docker service update --force falpolicy_falpolicy'
docker service update --force falpolicy_falpolicy

这个文件可以单独调用,也可以在镜像部署脚本,如 startup.sh 中调用。脚本中涉及的 application.properties 文件在脚本同目录下。

5 获取volumn根目录(不限CentOS)

Fal_HOME=`docker volume inspect falpolicy_falhome --format "{{.Mountpoint}}"`
# 输出:/var/lib/docker/volumes/falpolicy_falhome/_data
echo ${FAL_HOME}
VOLUMN_ROOT_PATH=${FAL_HOME%%volumes*}volumes
# 输出:/var/lib/docker/volumes
echo ${VOLUMN_ROOT_PATH}

6 挂载主机目录或主机文件

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读。

docker run --mount type=bind,source=/src/webapp,target=/opt/webapp,readonly nginx
docker run --mount type=bind,source=/src/webapp/index.html,target=/opt/webapp/index.html nginx

7 尽量保持容器存储层不发生写操作

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

8 Volumn数据共享

8.1 compose 中共享

如果要授权一个容器访问另一个容器的Volume,在 compose 下很简单,创建一个 volumn 就共享了。例如下面的 volumn fastdfs-storage-path,fastdfs-storage 服务负责往这个volumn里生产数据,nginx负责从这个 volumn里读数据,nginx映射的 /fastdfs 目录,在nginx docker中是可以直接访问这个路径的:

8.2 使用参数 -volumes-from 共享

我们可以使用 -volumes-from 参数来执行docker run,以共享 volumn。
$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash

root@NEWCONTAINER:/# ls /data
test-file
root@NEWCONTAINER:/#

9 备份、恢复或者迁移数据卷

数据卷还可以用来备份、恢复或迁移数据。为此我们使用--volumes-from参数来创建一个挂载数据卷的容器,像这样:

$ sudo docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

这里我们启动了一个挂载dbdata卷的新容器,并且挂载了一个本地目录作为/backup卷。最后,我们通过使用tar命令将dbdata卷的内容备份到容器中的/backup目录下的backup.tar文件中。当命令完成或者容器停止,我们会留下我们的dbdata卷的备份。

然后,你可以在同一容器或在另外的容器中恢复此数据。创建一个新的容器

$ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

然后在新的容器中的数据卷里un-tar此备份文件。

$ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar

你可以对熟悉的目录应用此技术,来测试自动备份、迁移和恢复。

10 Compose Volumn 中的 external

volumes:
hostip:
external: true

表示使用外部 volumn,compose不再去创建它。

发表评论

您的电子邮箱地址不会被公开。