Docker化一个小产品

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 import和load区别

举一个场景例子,如果你的容器运行一段时间了,想迁移,一般的动作是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化,不过现在这篇文章够长了回头再看看有没有必要补上。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容