1. 背景
现在环境在聊Docker有点不太合适,因为国内的镜像基本被封了,能用极少。本来我们应用也不适合做镜像,但是接到一个任务,需要对第三方平台封装,运行在Docker上,严格上来说要和我们管理平台兼容,安装在一台机器上,后续我也未对其完全做镜像,因为中间实在是遇到太多的问题。
2. 准备镜像的过程
由于这个第三方平台里面是包含安装docker的(采用复制安装),所以好处就可以直接利用这个docker了不用重新操作了。
docker --version
Docker version 17.12.0-ce, build c97c6d6
查看了下版本为17.12.0-ce版本,相对来说比较老的版本,注意不同docker版本构建的镜像无法通用,这也是我遇到的一个坑之一,所以后面的镜像都在这个版本上拉取和构建。
2.1 Nginx镜像拉取和保存
docker pull nginx:1.15.12
docker save nginx:1.15.12 | gzip > my-nginx.tar.gz
使用的时候通过下面命令将镜像导入到docker中:
docker load -i docker-images/my-nginx.tar.gz
说明通过gzip可以减少镜像的大小。
另外注意的用的是load命令加载,docker还有个import命令,区别如下图(图来自互联网)
docker load 会将整个镜像加载到 Docker 中,包括镜像的历史、标签、配置等元数据。
docker import 创建的镜像只包括文件系统快照和一些基本元数据,不包含原始镜像的完整元数据(如历史、标签等),尺寸更小,通过(docker export导出的)启动必须加/bin/bash或/bin/sh.
我理解是镜像不用改,直接备份和还原用save导出和load导入;如果在基础镜像上修改,可以通过运行镜像为容器后,通过export导出,在通过import导入,import的时候指定新镜像名称和标签。
如命令:docker import mycontainer.tar mynewimage:latest
还有一个docker commit命令也是将运行的容器经过改变后创建为一个新的镜像,并未导出成文件。
举一个场景例子,如果你的容器运行一段时间了,想迁移,一般的动作是docker commit 生成一个新的镜像,然后再通过docker save命令导出为tar文件,拷贝到另外一台主机上通过docker load导入这个镜像再运行即可。
讲解比较好的网址:https://cloud.tencent.com/developer/article/2027894
2.2 Nginx镜像的运行
运行还是比较简单,主要做的是目录的映射,需要注意的是nginx.conf 如果里面有配置固定目录的,要改过来:
docker run -p 443:443 -p 8080:8080 -d --name nms-nginx --restart=always -v /home/mywebtest/nginx/html:/usr/local/nginx/html -v /home/mywebtest/www/nms:/usr/share/nginx/html -v /home/mywebtest/nginx/conf:/etc/nginx -v /home/mywebtest/logs:/var/log/nginx nginx:1.15.12
映射端口前面为宿主主机端口,后面为容器的端口,目录也是前面为宿主主机的目录,后面是容器的内部目录,nginx.conf配置用的目录是容器的目录。
删除容器 docker rm 容器名
删除镜像 docker rmi 镜像id
3. Mysql 容器的拉取和运行
docker pull mysql:5.7.23
docker save mysql:5.7.23 | gzip > nms-mysql.tar.gz
docker run -d --name nms-mysql -e MYSQL_ROOT_PASSWORD='1234' -p 3308:3306 mysql:5.7.23
# 简单测试
mysql -uroot -p'1234' -P3308
这个目前没遇到什么坑。
4. clickhouse 容器拉取和运行
本来打算自己构建的,通过Dockerfile发现不少坑,比如基础容器版本不对,运行命令找不到,对于自己构建的情况,如果遇到报错,可以用基础镜像作为测试入口,去测试命令,没问题了再写到Dockerfile中。
docker run --rm -it ubuntu:18.04 /bin/bash
我用来构建ck的Dockerfile如下:
# 基础镜像 有坑有的docker容器只支持低版本高的下载不了
FROM ubuntu:18.04
# 设置环境变量只在构建过程中使用,容器运行后没有,ENV设置环境变量在构建过程和运行中都有
ARG repository="deb http://repo.yandex.ru/clickhouse/deb/stable/ main/"
ARG version=19.4.3
ARG gosu_ver=1.10
# run即运行命令,最好合在一起,可以减少容器的大小
RUN apt-get update && apt-get install --yes --no-install-recommends locales
RUN mkdir -p /etc/apt/sources.list.d && echo $repository > /etc/apt/sources.list.d/clickhouse.list && apt-get install gnupg gnupg1 gnupg2 --yes
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4 && apt-get clean && apt-get update && apt-get install --allow-unauthenticated --yes --no-install-recommends \
clickhouse-common-static \
clickhouse-client \
clickhouse-server \
libgcc-7-dev \
locales \
tzdata \
wget \
&& rm -rf \
/var/lib/apt/lists/* \
/var/cache/debconf \
/tmp/* \
&& apt-get clean
# 添加文件,最好用COPY 不过ADD可以添加网络文件,
# ADD https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 /bin/gosu
RUN locale-gen en_US.UTF-8
#设置环境变量运行容器时候会生效
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN mkdir /docker-entrypoint-initdb.d
COPY docker_related_config.xml /etc/clickhouse-server/config.d/
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x \
/entrypoint.sh \
/bin/gosu
# 设置暴漏端口
EXPOSE 9000 8123 9009
# 创建命名卷,可以共享和保存数据
VOLUME /var/lib/clickhouse
ENV CLICKHOUSE_CONFIG /etc/clickhouse-server/config.xml
# 容器启动时候执行,也可以用CMD,CMD可以被覆盖而ENTRYPOINT不会
ENTRYPOINT ["/entrypoint.sh"]
运行构建命令:
docker build --no-cache -t as/nms-ck:19.4.3.1 -f Dockerfile .
构建比较麻烦,不如直接拉取一个:
docker pull bitnami/clickhouse:latest
简单的运行命令,设置空密码:
docker run --privileged=true --user=root -p 9003:9000 --env ALLOW_EMPTY_PASSWORD=yes --env CLICKHOUSE_MOUNTED_DIR=/home/ck-test -v clickhouse_config:/etc/clickhouse-server bitnami/clickhouse:latest
clickhouse端口:
CLICKHOUSE_HTTP_PORT:HTTP 端口。默认为8123.
CLICKHOUSE_TCP_PORT:TCP 端口。默认为9000.
CLICKHOUSE_MYSQL_PORT:MySQL 端口。默认为9004.
CLICKHOUSE_POSTGRESQL_PORT:PostgreSQL 端口。默认为9005.
CLICKHOUSE_INTERSERVER_HTTP_PORT:服务器间 HTTP 端口。默认为9009.
5. docker的网络
5.1 网络访问问题
首先要解决的就是网络访问问题,容器化后,涉及的是python应用的容器,访问mysql的容器,这时候地址用127.0.0.1是不行的,因为容器做了网络的隔离的,相当于不同的网络空间,如果直接访问127.0.0.1是其容器内部的地址,而不是宿主主机的127.0.0.1。
[root@nms ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
352e0d4e06f0 bridge bridge local
ba29380786f2 host host local
c3d37cbc3825 none null local
bridge : 默认方式,网桥方式,在docker安装时候会创建一个docker0的网桥,可以为启动的容器自动分配网络地址,docker与外部网络通信时候通过NAT转换;
host: 不进行网络隔离,和主机使用同一个协议栈,将获得和主机相同的ip和端口空间,性能好,不过不推荐;
none: 不进行任何网络设置,容器只有lo,可以再次基础上对容器进行定制,需要手工加网卡配置ip等。
5.2 虚拟网桥
默认在安装docker后都会有个虚拟网桥叫docker0通过ifconfig可以看到,用专门的工具也可以查看:
[root@nms ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:58ff:feb1:6467 prefixlen 64 scopeid 0x20<link>
ether 02:42:58:b1:64:67 txqueuelen 0 (Ethernet)
RX packets 37527 bytes 7718780 (7.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 63429 bytes 6404028 (6.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@nms ~]# yum install bridge-utils
[root@nms ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024258b16467 no veth058c5f5
veth3367706
vethb7d3531
可以看到网桥上有三个虚拟网卡连接过来的,刚好对应我启动的三个容器:
[root@nms ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b629f199cc28 nginx:1.15.12 "nginx -g 'daemon of…" 27 hours ago Up 27 hours 0.0.0.0:443->443/tcp, 80/tcp, 0.0.0.0:8080->8080/tcp nms-nginx
93f54a232383 mysql:5.7.23 "docker-entrypoint.s…" 2 days ago Up 2 days 33060/tcp, 0.0.0.0:3307->3306/tcp nms-mysql
86f57b1ff24a splos:5.1nms "/bin/sh /etc/rcS_do…" 8 days ago Up 2 days 5.1nms
[root@nms ~]# docker inspect b629f199cc28
...
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.4",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:04",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "352e0d4e06f0eb18e438a0f650abef85a05b5dd96eb326eac176205362b0d08c",
"EndpointID": "963e7047721b99851af0cbf110310cbeb29a0d1d9c4ec71248cafe276c216212",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:04",
"DriverOpts": null
}
}
}
}
]
可以看到这个容器对应的ip为:172.17.0.4 对应的网关地址为:172.17.0.1
可以看到容器通过将虚拟网桥作为通道,经过NAT转换访问外部网络,返回的数据也通过这个通道到达容器,docker的容器ip由虚拟网桥来分配,分配的网段为172.17.0.0/16
5.3 docker的容器如何互访
如上面所说,如果采用默认的方式,虚拟网桥会给容器分配一个固定网段的ip,如果我们使用这个ip,可以实现同一个docker下面的容器互访,实验如下:
# 启动两个容器,容器名为 os1 和os2
docker run -it --rm --name os1 ubuntu:18.04 bash
docker run -it --rm --name os2 ubuntu:18.04 bash
# 然后分别执行
apt-get update
apt-get install iproute2
apt-get install iputils-ping
然后分别查看两个容器的ip地址:
root@c6dfb4213c15:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
24262: eth0@if24263: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:05 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.5/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
root@77e7eb3fe195:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
24258: eth0@if24259: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
两个ip分别是172.17.0.4 和172.17.0.5 然后相互ping的结果是通的,也就是容器在默认安装的情况下可以通过内部的ip来相互访问,但是问题来了,如果容器重启了,会导致ip产生变化,所以在配置的时候这样写并不行。
5.4 最常用的容器网络互通的方法
想想类似的场景,比如我们配置DNS的时候,可以将同一个域名对应不同的IP,这样用户记住我们的域名就可以访问我们的网站,就算我们更换了IP,也不影响用户的访问,同理,我们也可以创建个虚拟网桥,然后将启动的容器配置不同的网络别名,通过别名来实现容器之间的网络互通。
创建虚拟网桥:
docker network create mynet
通过下面命令可以看到它的详细信息,和docker0 类似。
[root@opengauss-master spiderflow]# docker inspect mynet
[
{
"Name": "mynet",
"Id": "f3d317bc5b9a2029a5abeec5a93177619b5554249697b83199aaba681c376c64",
"Created": "2024-09-06T18:29:30.054557781+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
以下命令启动容器,加入到网桥,分别用不同的别名:
docker run -it --rm --name os2 --network mynet --network-alias os2 ubuntu:18.04 bash
docker run -it --rm --name os1 --network mynet --network-alias os1 ubuntu:18.04 bash
执行ping命令测试,可以看到ping的通,这样无论重启多少次,只要用别名,在同一个网络下,即接入同一个网桥下都可以ping的通。
root@e4374d44b39a:/# ping os1
PING os1 (172.18.0.2) 56(84) bytes of data.
64 bytes from os1.mynet (172.18.0.2): icmp_seq=1 ttl=64 time=0.123 ms
64 bytes from os1.mynet (172.18.0.2): icmp_seq=2 ttl=64 time=0.055 ms
64 bytes from os1.mynet (172.18.0.2): icmp_seq=3 ttl=64 time=0.075 ms
^C
--- os1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.055/0.084/0.123/0.029 ms
root@e4374d44b39a:/# ping os2
PING os2 (172.18.0.3) 56(84) bytes of data.
64 bytes from e4374d44b39a (172.18.0.3): icmp_seq=1 ttl=64 time=0.023 ms
64 bytes from e4374d44b39a (172.18.0.3): icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from e4374d44b39a (172.18.0.3): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from e4374d44b39a (172.18.0.3): icmp_seq=4 ttl=64 time=0.032 ms
我们可以继续用inspect看下,会发现容器多了个域名信息。
[root@opengauss-master drex]# docker inspect e4374d44b39a|grep DNSNames -A 3
"DNSNames": [
"os2",
"e4374d44b39a"
]
都是网桥,是否也可以直接加入docker0 其不同的别名那,结果测试如下:
[root@opengauss-master spiderflow]# docker run -it --rm --name os2 --network docker0 --network-alias os2 ubuntu:18.04 bash
docker: Error response from daemon: network docker0 not found.
默认的网桥不能这么玩,咱也不知道为什么了.
如何删除网桥那,需要两个步骤,一是将容器从这个网络移除,二是删除:
docker network disconnect mynet 容器ID
docker network rm mynet
6. 准备时候的坑
6.1 操作系统下载软件问题
这个坑不能算docker的,我们OS是centos7.9版本,不配置下,软件都无法下载,每次都要做一次,简单的记录下:(配置了DNS和更改了源)
vim /ete/resolv.conf
nameserver 8.8.8.8
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clear all
yum makecache
6.2 制作镜像基础镜像找不到问题
由于国内镜像被封的原因,所以下不了镜像了,幸好从一个老哥那找到一个方法,希望大家知道就好,谨慎传播,不要搞乱七八糟的再被封了就惨了。
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://dockerhub.xianfish.site"],
}
6.3 容器特性
一般一个容器只启动一个程序,这样比较好管理,哪个应用停了通过容器运行状态就判断了;如果一个容器要启动多个程序,则将前面的应用放在后台启动,最后的应用前台启动。
重要:如果容器没有一个前台应用,容器将会自动停止运行
其实还有一个python的管理程序再加上Supervisor的docker化,不过现在这篇文章够长了回头再看看有没有必要补上。