docker基础篇-----07-----Docker将镜像打包成文件、重新上传到Docker、配置容器开机自启、添加脚本使程序伴随容器开机自启(C++开发常用流程)

1 编写Dockfile

首先我们先使用Dockerfile生成一个自己想要的镜像。

FROM ubuntu
MAINTAINER tyy<tyy@126.com>

# Time synchronization between host and container
#ENV TimeZone=Asia/Shanghai  
#RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
# copy Shanghai in host to /etc/localtime in container
#RUN sudo cp /usr/share/zoneinfo/$TimeZone ./
#COPY Shanghai /etc/localtime
#RUN rm Shanghai
# The above approach may not be successful on Linux systems
COPY Shanghai /etc/localtime

RUN apt-get update && apt-get install sudo
RUN sudo apt-get install -y vim
# net-tool include ifconfig,netstat so on...
RUN sudo apt-get install -y net-tools
RUN sudo apt-get install -y systemctl
# DEBIAN_FRONTEND=noninteractive means what yes/no is not appeared.
RUN DEBIAN_FRONTEND=noninteractive sudo apt-get install -y inetutils-ping
RUN DEBIAN_FRONTEND=noninteractive sudo apt-get install -y supervisor
RUN sudo apt-get install -y make
# install gcc,g++
RUN DEBIAN_FRONTEND=noninteractive sudo apt-get install -y build-essential
RUN DEBIAN_FRONTEND=noninteractive sudo apt-get install -y gcc
RUN DEBIAN_FRONTEND=noninteractive sudo apt-get install -y g++

EXPOSE 8893

CMD echo "success    ok"
CMD /bin/bash

注意:

  • 1)我在同步宿主机和容器的时间时,百度了看其他人的做法是使用软链接,但是我这里使用软链接没有成功,其它人也有可能出现这种情况,说明这种软链接的方法不是必定成功的。所以我这里手动先把/usr/share/zoneinfo/Asia/Shanghai文件拷贝到Dockfile所在目录下,然后执行COPY Shanghai /etc/localtime即可。
  • 2)sudo apt-get install当你安装时,可能会出现yes/no这种需要与终端交互的情况,DEBIAN_FRONTEND=noninteractive就是表示我不交互,不加的话会报错。同理-y也需要加,不然可能也会出现其它错误。

2 构建

去到Dockerfile的目录下,执行下面的构建命令生成镜像。

sudo docker build -t myubuntu:1.0.0 .

在这里插入图片描述

如果你此时run一个容器,然后date查看时间,可以看到是与宿主机的时间一样的。
在这里插入图片描述

3 将镜像打包成文件

语法:docker save -o 要保存的文件名 要保存的镜像。

docker save -o myubuntu:1.0.0.tar  myubuntu:1.0.0

在这里插入图片描述

4 将打包好的文件上传到Docker

上面打包镜像成文件后,我们经常会使用xftp进行拷贝到指定的生产环境的服务器上面,然后重新上传到Docker进行使用。

当然有些公司会直接将生成好的镜像push到阿里云上面,然后再从阿里云上面pull下来使用,这个在第6篇文章说过了,但是这种情况是针对有外网的情况下。

因为很多公司的生产环境下都是内网的,所以我们只能使用这种打包成文件的方法。

语法:
docker load --input 文件。
或者
docker load < 文件名

例如:

# 假设已经将该压缩包上传到生产环境后,那么进行上传到Docker
sudo docker load < myubuntu:1.0.0.tar

可以看到,另一台服务器上面已经有这个镜像了。
在这里插入图片描述

5 创建容器并使用命令创建容器数据卷

本来我们是可以直接在Dockerfile中直接创建的,但是因为Dockerfile无法指定宿主机的路径,只能指定容器的路径,所以我这里喜欢使用命令去创建数据卷。

# -d表示后台执行,-it表示前台交互运行。--network host表示开放所有端口,可以使用-p来开放多个端口,例如-p8893:8893 -p8080:8080。
# 如果不开放所有端口,可能在docker想apt-get安装软件,可能会失败,即使能上外网。
# 并且注意,如果只给了-d而没有-it,那么如果此时容器没有前台进程,docker会自动把该容器停止掉。所以加上-it选项是有必要的。
# -v(volume)表示添加容器数据卷。privileged=true表示如果权限不够则给它权限。
sudo docker run -d -it --network host --name=docker_ubuntu_gw -v /宿主机绝对路径:/容器内目录 --privileged=true 镜像名。

# 看到容器创建成功
sudo docker ps

此时可以通过docker inspect 容器名去看是否挂载容器卷成功。
在这里插入图片描述

6 进入容器

此时,容器以及对应的容器卷以及创建完成,那么剩下的就是将程序拷贝到容器卷的目录下面,进行编译,编译成功后,那么就没问题了,当然这一步应该在公司就完成。

编译的时候我们应该进入容器编译,方便去适配docker里面的环境。进入容器的命令:

sudo docker exec -it 容器ID /bin/bash

然后后续缺少什么命令、软件的话,就继续安装,然后将myubuntu:1.0.0镜像不断更新即可。

7 容器内的时间与宿主机同步

如果在上面Dockerfile成功同步了,那么这一步就不需要再处理,这里只是写出来让大家知道也可以使用这种方法去同步,原理是一样的。

有时我们创建容器后,可能时间和宿主机是不一致的,所以我们需要进行统一。统一也很简单,就是将宿主机下面的/usr/share/zoneinfo/Asia/Shanghai拷贝到容器的/etc并改名成localtime即可。

# 个人建议使用方法三,比较安全,因为有时方法一二即使拷贝进容器成功,但是时间并未同步。
# 方法一:
# sudo docker cp /usr/share/zoneinfo/Asia/Shanghai 容器ID:/etc/localtime

# 上面可能报错,报错的话可以试试
# 方法二:
sudo docker cp /etc/localtime 容器ID:/etc/localtime

# 如果上面都不行,那么就将宿主机的文件先拷贝到容器卷目录,然后进入容器再改名成/etc/localtime即可。
# 方法三:
cp /usr/share/zoneinfo/Asia/Shanghai .
# 进入容器后的容器卷目录:
mv ./Shanghai /etc/localtime

在这里插入图片描述

如果你项目中没有程序开机自启的需求,那么下面就不需要再看。如果有则继续往下看。

8 使用docker-compose使容器开机自启

本小点的所有内容均是按照第5步的那个run命令来使用docker-compose创建容器的。

8.1 下载docker-compose

wget -c https://github.com/docker/compose/releases/download/v2.3.3/docker-compose-linux-x86_64
# 剪切到/usr/local/bin目录并改名为docker-compose,方便可以直接使用该命令
sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose
# 添加权限
cd /usr/local/bin
sudo chmod 777  docker-compose

8.2 编写项目文件docker-compose.yml

该文件建议放在项目目录下。

vim docker-compose.yml

添加内容:
# version代表这个docker-compose.yml的版本号
version: "3"
# services表示可以有多个应用服务,这个服务你可以理解为容器。
services:
  # 服务PTZ-RTSP_URL-HK是我的程序名:
  PTZ-RTSP_URL-HK:
    container_name: docker_ubuntu_gw	# 容器名,等价于--name
    image: myubuntu:1.0.0		# 镜像名
    restart: always			# 设置开机启动
    ports:				# 宿主机与容器内的端口映射,规则为:宿主机端口:容器端口,该项是一个数组,可以添加多个映射。等价于-p。
      - 8893:8893
    volumes:				# 容器卷,等价于-v。
      - /xxx:/xxx

    # 避免容器开启后自动退出
    privileged: true
    #interactive: true
    tty: true

8.3 创建该容器

注意,因为我们在上面第5步的时候创建了一个名字为docker_ubuntu_gw 的容器,所以需要先把它删除掉,防止出错。没有就不需要删除。

# 先把第5步创建的容器名为docker_ubuntu_gw 的先删掉。
sudo docker rm -f 容器id

# 1. 使用docker-compose创建容器。
# 注意,必须要去到docker-compose.yml目录下执行,否则会报错。不想去该路径执行需要加上-f选项指定该yml文件的全部路径,例如-f xxx/docker-compose.yml。
# -d表示后台运行。
sudo docker-compose up -d
sudo docker-compose ps	# 查看使用成功启动,看到STATUS为running表示成功,如果为RESTARTING为启动失败或者成功启动但自动退出了。

如果 docker-compose 版本比较低的话,STATUS显示为Up,说明正常启动。
在这里插入图片描述

8.4 测试开机后该容器是否会自启

# 1. 首先把该容器关掉。
# 注意不需要加上容器名或者容器ID。并且同样需要去到yml目录下执行,不去的话需要加上-f选项指定该yml文件的全部路径,例如-f xxx/docker-compose.yml。
docker-compose stop

# 2. 关机
sudo shutdown now

# 3. 重新开启该服务器(我的是虚拟机,所以方便开机关机)。

# 4. 查看是否开机自启。
docker-compose ps	# 或者使用docker ps,但是还是建议统一使用docker-compose的命令,因为你的服务都已经交给docker-compose管理了,避免再使用docker命令,防止出现未知问题。

# 5. 进入该容器。
docker exec -it docker_ubuntu_gw /bin/bash

下面看到,容器开机自启成功,使用exec进入容器也是没有问题的。
在这里插入图片描述

9 为程序添加脚本,使程序伴随着容器开机自启

9.1 添加脚本文件并编写脚本

首先我们在上面的docker-compose.yml中额外添加多一个选项:即入口点entrypoint选项,以此来执行外部命令。这里命令指脚本。

# 添加shell脚本,进入容器后自动执行该脚本,使想要的程序在容器中运行起来。该脚本一般放在该项目目录下,方便管理.
# 注意,这个/xxx/start.sh是指的是容器下面的路径,这个路径必须在容器中存在,否则会报start.sh找不到的错误。
entrypoint:
  - /xxx/start.sh

这是我的程序的start.sh内容,我是在容器使用supervisord服务来管理程序的,方便管理程序。
supvisord安装:ubuntu直接 apt-get install supervisord 即可。
如果是离线安装Supervisor,可以参考我这篇文章,内容都是类似的:Ubuntu下,python3下离线安装Supervisor

大家需要根据自己的程序来编写:

#!/bin/bash

# file or directory handle...
if [ ! -f "/etc/supervisor/conf.d/ptz-rtsp_url-hk.conf" ]; then
	touch /etc/supervisor/conf.d/ptz-rtsp_url-hk.conf
	echo "create file: /etc/supervisor/conf.d/ptz-rtsp_url-hk.conf"
else
	echo "the file: /etc/supervisor/conf.d/ptz-rtsp_url-hk.conf has exist"

fi
if [ ! -d "/var/ptz-rtsp_url-hk_log/" ]; then
	mkdir -p "/var/ptz-rtsp_url-hk_log/"
	echo "create dir: /var/ptz-rtsp_url-hk_log/"
else
	echo "the dir: /var/ptz-rtsp_url-hk_log/ has exist"
fi

if [ ! -f "/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk.log" ]; then
        touch "/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk.log"
        echo "create file: /var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk.log"
else
        echo "the file: /var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk.log has exist"

fi

if [ ! -f "/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk_error.log" ]; then
        touch "/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk_error.log"
        echo "create file: /var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk_error.log"
else
        echo "the file: /var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk_error.log has exist"

fi

echo "-----------------file or directory check handle success-----------------"

echo "# 1
[program:ptz-rtsp_url-hk]
# 2 程序在该路径下运行
directory=容器卷路径/PTZ-RTSP_URL-HK/bin/
#运行用户root
user = root
# 3 运行
command=容器卷路径/PTZ-RTSP_URL-HK/bin/ptz-rtsp_url-hk
#在supervisord 启动的时候也自动启动
autostart = true
#启动 1 秒后没有异常退出,就当作已经正常启动了
startsecs = 0
# 停止等待时间
stopwaitsecs=10
#程序异常退出后自动重启
autorestart = true
#启动失败自动重试次数,默认是 3
startretries = 100
# 4 日志文件若文件夹不存在需新建
stdout_logfile=/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk.log
# 5 错误日志
stderr_logfile=/var/ptz-rtsp_url-hk_log/ptz-rtsp_url-hk_error.log
#设置环境变量
environment=LOG_DIR="/var/ptz-rtsp_url-hk_log"
" >> /etc/supervisor/conf.d/ptz-rtsp_url-hk.conf

echo "-----------------configure file handle success-----------------"

# start supervisord service and dynamically viewing logs
/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
supervisorctl tail -f ptz-rtsp_url-hk

echo "-----------------start.sh handle success-----------------"

添加stop.sh:

#!/bin/bash
supervisorctl stop ptz-rtsp_url-hk
killall supervisord

然后添加权限。

chmod 755 start.sh
chmod 755 stop.sh

9.2 重新编译程序及配置动态库路径

然后把自己的整个程序目录拷贝到容器卷所在目录。

# 1. 先把未加上脚本命令的容器删除,然后更新该容器。
cd 容器卷下的项目目录。
sudo docker-compose rm 	# 需要去到yml目录下执行
# 2. 创建容器。
sudo docker-compose up -d
sudo docker-compose ps

容器成功启动。
在这里插入图片描述

虽然启动成功,但是由于程序未重新编译,所以进入容器会看到程序开启失败。
所以需要进入容器后,重新编译程序,然后vim /etc/ld.so.conf配置动态库路径, 然后source /etc/ld.so.conf更新配置。
如果不重新编译,即使配置了动态库路径,程序可能仍会链接不到对应的动态库。

# 1. 进入容器
sudo docker exec -it docker_ubuntu_gw /bin/bash
# 2,. 在容器中进入到项目所在目录,然后使用ldd查看,可以看到动态库是没有链接进来的。
cd 项目所在目录
ldd bin/ptz-rtsp_url-hk | grep not

在这里插入图片描述

解决:

# 1. 先停止服务。
supervisorctl stop ptz-rtsp_url-hk

# 2. 添加你使用到的动态库目录
vim /etc/ld.so.conf

# 3. 更新配置
ldconfig /etc/ld.so.conf
ldd bin/ptz-rtsp_url-hk | grep not	# 此时再看,已经没有not found

# 4. 开启服务。
supervisorctl start ptz-rtsp_url-hk
supervisorctl status ptz-rtsp_url-hk
supervisorctl tail -f ptz-rtsp_url-hk

查看日志,程序开启成功。
在这里插入图片描述

9.3 测试开机启动

通过上面后,我们即可将程序伴随着容器的开机自启而启动。所以此时我们可以进行重启测试,重启之前最好把容器先stop掉,这样更方便看到是否开机自启,不过关不关问题都不大。

下图看到,重启后,容器是自启的。然后进入容器使用top查看后,容器里的程序也是开启成功的。
在这里插入图片描述

既然程序开机自启成功,那么我们可以尝试一下自己程序的接口是否能访问成功。

下图是使用 supervisorctl tail -f 程序名 查看日志,然后请求我程序的http接口,同样是正常返回的,说明大功告成。
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值