Docker的image是只读的,容器运行期间产生的数据是不会在写入到image里面,当容器被删除后,数据也随之被删除。如果想做到数据持久化,Docker使用Data volume或者Bind Mounting来持久化数据。
如果想要简化多容器的部署,则可以使用Docker Compose。
Docker的数据持久化
Data volume
Data volume除了可以持久化数据,还提供以下特性:
Data volume可以在容器之间共享和重用
对Data volume的修改会立马生效
对Data volume的更新,不会影响镜像
Data volume默认会一直存在,即使容器被删除
在Dockerfile中,可以使用VOLUME
指令指定数据的挂载点。如下是mysql官方的Dockerfile,可以看到mysql的VOLUME挂载点:
VOLUME /var/lib/mysql
如下命令,运行一个mysql容器:
docker run -d -v mysql:/var/lib/mysql --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.6
使用docker volume ls
可以看到:
ubuntu@docker-node1:~$ docker volume ls
local mysql
进入mysql的容器,创建一个数据库docker:
ubuntu@docker-node1:~$ docker exec -it mysql /bin/bash
root@937b2ceaf848:/# mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.42 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
mysql> CREATE DATABASE docker;
Query OK, 1 row affected (0.00 sec)
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| docker |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)
mysql>
退出mysql的容器,并删除mysql容器:
ubuntu@docker-node1:~$ docker rm -f mysql
mysql
创建一个mysql-another的容器,并使用原来的mysql的VOLUME:
ubuntu@docker-node1:~$ docker run -d -v mysql:/var/lib/mysql --name mysql-another -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.6
进入mysql-another的容器,可以看到数据库docker还存在:
ubuntu@docker-node1:~$ docker exec -it mysql-another /bin/bash
root@97ea74e7f222:/# mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.42 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| docker |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)
mysql>
Bind Mounting
Bind Mounting是将本地目录和容器目录进行映射,当本地目录中文件更改,对应的容器目录中的文件也会更改。
如下Dockerfile,是拉取一个nginx的image,并把当前目录下的index.html拷贝到容器的/usr/share/nginx/html:
FROM nginx:latest
WORKDIR /usr/share/nginx/html
COPY index.html index.html
index.html的内容为:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
</body>
</html>
构建image:
ubuntu@docker-node1:~/nginx-demo$ docker build -t heqingliang/nginx-demo .
运行容器时,将本地目录挂载到容器的/usr/share/nginx/html
目录:
ubuntu@docker-node1:~/nginx-demo$ docker run -d -v $(pwd):/usr/share/nginx/html -p 80:80 --name web heqingliang/nginx-demo
2d5dd44d42a6a9102ec66adb5e817270ef14b79b6c8755330a8903ffbaa43434
ubuntu@docker-node1:~/nginx-demo$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d5dd44d42a6 heqingliang/nginx-demo "nginx -g 'daemon ..." 3 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp web
在浏览器中输入http://192.168.33.10/
,可以看到如下结果:
进入web容器的/usr/share/nginx/html
目录,创建一个test.txt文件:
ubuntu@docker-node1:~/nginx-demo$ docker exec -it web /bin/bash
root@2d5dd44d42a6:/usr/share/nginx/html# echo "abc" > test.txt
退出web容器,可以看到当前目录也创建了一个test.txt文件,内容和容器中是一样的:
ubuntu@docker-node1:~/nginx-demo$ ls
Dockerfile index.html test.txt
ubuntu@docker-node1:~/nginx-demo$ cat test.txt
abc
修改本地目录的下的index.html文件为:
<h1>Hello World! </h1>
在浏览器中输入http://192.168.33.10/
,可以看到如下结果:
Docker Compose
在多个容器的APP部署会存在以下的问题:
要从Dockerfile build image或者从DockerHub中拉取镜像
要创建多个container
要管理这些container(启动、删除、停止)
Docker Compose是解决上面问题的”批处理”工具,这个工具可以通过一个yml文件定义多容器的docker应用,通过一条命令就可以根据yml文件的定义去创建或者管理多个容器。
在Docker Compose包含services、networks、volumes三大部分:
services:一个service代表一个container,这个container可以从DockerHub中拉取的image来创建,或者从本地的Dockerfile build出来的image来创建,service的启动类似docker run,可以指定network和volume,所以可以给service
networks:代表创建的docker网络
volumes:数据持久化
有如下yml文件:
1 | vsersion: "3" |
定义service相当于执行如下docker命令:
docker run -d --network back-tier -v db-data:/var/lib/postgresql/data postgres:9.4
定义volumes相当于执行如下docker命令:
docker volume create db-data
定义networks相当于执行如下docker命令:
docker network create -d bridge back-tier
安装
在linux系统中,安装docker并没有安装docker-compose,需要单独安装:
ubuntu@docker-node1:~/flask-redis$ sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
ubuntu@docker-node1:~$ sudo chmod +x /usr/local/bin/docker-compose
例子
有如下python程序,使用redis来记录每次访问web页面的次数,在部署时,可以把redis和应用程序部署在不同容器中:
1 | #!/usr/bin/env python3 |
如下是app.py程序的Dockerfile:
FROM python
LABEL maintainer="heqingliang_gzus@163.com"
RUN pip install flask redis
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]
定义如下docker-compose.yml文件:
1 | version: "3" |
启动
执行下面的命令,就可以创建和启动容器:
ubuntu@docker-node1:~/flask-redis$ docker-compose up -d
Creating network "flaskredis_default" with the default driver
Creating flaskredis_web_1 ...
Creating flaskredis_redis_1 ...
Creating flaskredis_web_1
Creating flaskredis_redis_1 ... done
启动默认执行的是当前目录下的docer-compose.yml,如果想要执行指定的docer-compose.yml,可以使用-f
参数指定:
docker-compose -f docker-compose.yml up -d
-d
参数表示在后台执行,如果想要查看启动的信息,可以把-d
参数去掉。
访问127.0.0.1:5000
,可以看到如下结果:
ubuntu@docker-node1:~/flask-redis$ curl 127.0.0.1:5000
<h1>Hello Container, I have been seen b'1' times.</h1>
ubuntu@docker-node1:~/flask-redis$ curl 127.0.0.1:5000
<h1>Hello Container, I have been seen b'2' times.</h1>
ubuntu@docker-node1:~/flask-redis$ curl 127.0.0.1:5000
<h1>Hello Container, I have been seen b'3' times.</h1>
上面的docer-compose.yml
并没有创建自定义的网络,但是也可以直接使用redis容器的名字,直接通信,那是因为docker-compose创建了一个默认的网络flaskredis_default
:
ubuntu@docker-node1:~/flask-redis$ docker network ls
NETWORK ID NAME DRIVER SCOPE
48a159571164 bridge bridge local
ff50d198b5dc docker_gwbridge bridge local
182def1a98fa flaskredis_default bridge local
65e22acf0beb host host local
c305a4b53c3b none null local
查看容器的运行状态
ubuntu@docker-node1:~/flask-redis$ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------
flaskredis_redis_1 docker-entrypoint.sh redis Up 6379/tcp
...
flaskredis_web_1 python app.py Up 0.0.0.0:5000->5000/tcp
查看镜像
ubuntu@docker-node1:~/flask-redis$ docker-compose images
Container Repository Tag Image Id Size
---------------------------------------------------------------------
flaskredis_redis_1 redis latest 415381a6cb81 90.5 MB
flaskredis_web_1 flaskredis_web latest 0a1d5cbead89 889 MB
停止运行的容器
ubuntu@docker-node1:~/flask-redis$ docker-compose stop
Stopping flaskredis_redis_1 ... done
Stopping flaskredis_web_1 ... done
重新启动容器
ubuntu@docker-node1:~/flask-redis$ docker-compose start
Starting web ... done
Starting redis ... done
进入到某个容器中
ubuntu@docker-node1:~/flask-redis$ docker-compose exec redis bash
root@3375d56db073:/data#
停止并删除容器
ubuntu@docker-node1:~/flask-redis$ docker-compose down
Stopping flaskredis_redis_1 ... done
Stopping flaskredis_web_1 ... done
Removing flaskredis_redis_1 ... done
Removing flaskredis_web_1 ... done
Removing network flaskredis_default