0、前言

什么是 Docker 镜像 ?

Docker 就像往集装箱里装货物的码头工人那样,它把应用打包成具有某种标准规格的集装箱,用计算机领域的语言来说,这种按照一定规格封装的集装箱叫 「镜像」

Docker 镜像又有什么优势呢 ?

统一的管理服务、持续交付、弹性计算 等等…

如何创建一个镜像呢 ?

下文会带你一起了解如何创建一个 Docker 镜像

1、准备工作

  • 你需要申请一个 Docker hub 账号,了解相关的基本操作

  • 创建一个你自己的 dockerfile 文件夹

  • 一个 OpenRestytar 包, openresty-1.11.2.1.tar.gz

2、步骤

为了避免机器的权限问题,本次演示的所有操作都在 root 下进行

2.1、创建glibc镜像

目的: 修改时区信息的修改

创建Dockerfile的文件夹 alpine_glibc_2.23 ,我暂且先起这个名字,为什么这么起后面我会介绍。

为了避免网络问题,我先把几个必要的 apk 包直接放到本地。

1
2
3
4
5
[root@freeipa101 alpine_glibc_2.23]# ll
total 11768
-rw-r--r-- 1 root root 2943700 Oct 4 15:58 glibc-2.23-r3.apk
-rw-r--r-- 1 root root 1751110 Oct 4 15:58 glibc-bin-2.23-r3.apk
-rw-r--r-- 1 root root 7326212 Oct 4 15:58 glibc-i18n-2.23-r3.apk

然后创建一个 Dockerfile 文件

1
vim Dockerfile
1
2
3
4
5
6
FROM alpine:latest
MAINTAINER shengguocun<hzshengguocun@corp.netease.com>
ENV LANG=C.UTF-8
RUN apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
COPY glibc-*.apk /tmp/
RUN apk upgrade --update && apk add --allow-untrusted /tmp/*.apk && rm -f /tmp/*.apk /var/cache/apk/*

保存退出,进行 build 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[root@freeipa101 alpine_glibc_2.23]# docker build .
Sending build context to Docker daemon 12.03MB
Step 1/6 : FROM alpine:latest
---> 196d12cf6ab1
Step 2/6 : MAINTAINER shengguocun<hzshengguocun@corp.netease.com>
---> Using cache
---> 87a5241dd00b
Step 3/6 : ENV LANG=C.UTF-8
---> Using cache
---> 5529e89cde7e
Step 4/6 : RUN apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
---> Running in 069c8e8cd948
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.1-22-g24d67bab3a [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.1-16-g96e1e57fed [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9539 distinct packages available
(1/1) Installing tzdata (2018d-r1)
Executing busybox-1.28.4-r1.trigger
OK: 8 MiB in 14 packages
Removing intermediate container 069c8e8cd948
---> 1af55928a1c5
Step 5/6 : COPY glibc-*.apk /tmp/
---> 490259033ddd
Step 6/6 : RUN apk upgrade --update && apk add --allow-untrusted /tmp/*.apk && rm -f /tmp/*.apk /var/cache/apk/*
---> Running in b8b7ef2ec402
OK: 8 MiB in 14 packages
(1/4) Installing glibc (2.23-r3)
(2/4) Installing libgcc (6.4.0-r9)
(3/4) Installing glibc-bin (2.23-r3)
(4/4) Installing glibc-i18n (2.23-r3)
Executing glibc-bin-2.23-r3.trigger
OK: 24 MiB in 18 packages
Removing intermediate container b8b7ef2ec402
---> 216a3982623d
Successfully built 216a3982623d

现在查看一下刚刚 build 出来的镜像

1
2
3
4
5
[root@freeipa101 alpine_glibc_2.23]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 216a3982623d 10 seconds ago 34.7MB
nginx latest be1f31be9a87 37 hours ago 109MB
alpine latest 196d12cf6ab1 3 weeks ago 4.41MB

<none> 就是刚刚打出的镜像,这时候我们需要给他进行重命名,通过 docker tag --help 查看相关的使用

1
2
3
4
5
[root@freeipa101 alpine_glibc_2.23]# docker tag --help

Usage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
1
[root@freeipa101 alpine_glibc_2.23]# docker tag 216a3982623d docker.io/ricksheng/apline_glibc:2.23

最后一步,跟 GitHub 提交代码一样将镜像推到 Docker hub

1
2
3
4
5
6
7
8
[root@freeipa101 alpine_glibc_2.23]# docker push docker.io/ricksheng/apline_glibc:2.23
The push refers to repository [docker.io/ricksheng/apline_glibc]
cad05933e86e: Pushing [========================> ] 7.813MB/15.67MB
a531be207876: Pushing [========================================> ] 9.842MB/12.02MB
cad05933e86e: Pushing [==========================================> ] 13.31MB/15.67MB
cad05933e86e: Pushed

2.23: digest: sha256:1823a0b6171a4182416322e0360ef75230cb0491a3d70ab8cc370535c0c035d8 size: 1161

到这里为止,一个简单的镜像就创建完毕了。

2.2、创建gcc镜像

和上面类似,创建一个 alpine_gcc 文件夹,然后创建一个 Dockerfile 文件

1
[root@freeipa101 alpine_gcc]# vim Dockerfile
1
2
3
FROM docker.io/ricksheng/apline_glibc:2.23
MAINTAINER shengguocun<hzshengguocun@corp.netease.com>
RUN apk --no-cahe add make g++ libevent openssl-dev libevent-dev linux-headers && rm -rf /var/cache/apk/*

进行 build 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[root@freeipa101 alpine_gcc]# docker build .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM docker.io/ricksheng/apline_glibc:2.23
---> 216a3982623d
Step 2/3 : MAINTAINER shengguocun<hzshengguocun@corp.netease.com>
---> Running in 88133daefd42
Removing intermediate container 88133daefd42
---> 622169df1d2d
Step 3/3 : RUN apk --no-cahe add make g++ libevent openssl-dev libevent-dev linux-headers && rm -rf /var/cache/apk/*
---> Running in a0cade3d8d68
apk: unrecognized option: no-cahe
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/31) Installing libstdc++ (6.4.0-r9)
(2/31) Installing binutils (2.30-r5)
(3/31) Installing gmp (6.1.2-r1)
(4/31) Installing isl (0.18-r0)
(5/31) Installing libgomp (6.4.0-r9)
(6/31) Installing libatomic (6.4.0-r9)
(7/31) Installing pkgconf (1.5.3-r0)
(8/31) Installing mpfr3 (3.1.5-r1)
(9/31) Installing mpc1 (1.0.3-r1)
(10/31) Installing gcc (6.4.0-r9)
(11/31) Installing musl-dev (1.1.19-r10)
(12/31) Installing libc-dev (0.7.1-r0)
(13/31) Installing g++ (6.4.0-r9)
(14/31) Installing libevent (2.1.8-r5)
(15/31) Installing libbz2 (1.0.6-r6)
(16/31) Installing expat (2.2.5-r0)
(17/31) Installing libffi (3.2.1-r4)
(18/31) Installing gdbm (1.13-r1)
(19/31) Installing ncurses-terminfo-base (6.1_p20180818-r1)
(20/31) Installing ncurses-terminfo (6.1_p20180818-r1)
(21/31) Installing ncurses-libs (6.1_p20180818-r1)
(22/31) Installing readline (7.0.003-r0)
(23/31) Installing sqlite-libs (3.24.0-r0)
(24/31) Installing python2 (2.7.15-r1)
(25/31) Installing libevent-dev (2.1.8-r5)
(26/31) Installing linux-headers (4.4.6-r2)
(27/31) Installing make (4.2.1-r2)
(28/31) Installing zlib-dev (1.2.11-r1)
(29/31) Installing libcrypto1.0 (1.0.2p-r0)
(30/31) Installing libssl1.0 (1.0.2p-r0)
(31/31) Installing openssl-dev (1.0.2p-r0)
Executing busybox-1.28.4-r1.trigger
Executing glibc-bin-2.23-r3.trigger
OK: 255 MiB in 49 packages
Removing intermediate container a0cade3d8d68
---> d7a938e82e4e
Successfully built d7a938e82e4e

查看生成的镜像

1
2
3
4
5
6
[root@freeipa101 alpine_gcc]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> d7a938e82e4e 30 seconds ago 248MB
ricksheng/apline_glibc 2.23 216a3982623d 9 minutes ago 34.7MB
nginx latest be1f31be9a87 37 hours ago 109MB
alpine latest 196d12cf6ab1 3 weeks ago 4.41MB
1
[root@freeipa101 alpine_gcc]# docker tag d7a938e82e4e docker.io/ricksheng/alpine_gcc:6.4.0
1
2
3
4
5
6
7
8
9
[root@freeipa101 alpine_gcc]# docker push  docker.io/ricksheng/alpine_gcc:6.4.0
The push refers to repository [docker.io/ricksheng/alpine_gcc]
c8cb354e6402: Pushing [======> ] 25.93MB/213.2MB
c8cb354e6402: Pushing [==========> ] 44.56MB/2c8cb354e6402: Pushing 47.85MB/213.c8cb354e6402: Pushing [===============> ] 67.65MB/213.2MB
c8cb354e6402: Pushing [=============================> ] 126.9MB/213.2MB
c8cb354e6402: Pushed
df64d3292fd6: Mounted from ricksheng/apline_glibc

6.4.0: digest: sha256:7da0e8489ff7d1e0041b3edfa57f1fb8039fb9cd98d01600471a2f29f9fdd0c6 size: 1373

到这里为止,创建 OpenResty 镜像的准备工作都OK了,下面开始吧。

2.3、创建openresty镜像

老规矩,创建 alpine_openresty/1.11.2.1 目录,同时创建 Dockerfile 文件以及一个 nginx.conf 文件

1
2
3
4
5
[root@freeipa101 1.11.2.1]# ll
total 3852
-rw-r--r-- 1 root root 570 Oct 4 16:31 Dockerfile
-rw-r--r-- 1 root root 1114 Oct 4 16:23 nginx.conf
-rw-r--r-- 1 root root 3930804 Oct 4 16:21 openresty-1.11.2.1.tar.gz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
worker_processes  1;
error_log logs/error.log;
events {
worker_connections 1024;
}

http {
server {
listen 80;
location / {
default_type text/html;

content_by_lua_block {
ngx.say("HelloWorld")
}
}
}
}
1
vim Dockerfile
1
2
3
4
5
6
7
8
9
FROM docker.io/ricksheng/alpine_gcc:6.4.0
MAINTAINER shengguocun<hzshengguocun@corp.netease.com>
RUN apk add --update perl openssl openssl-dev pcre-dev make gcc musl-dev
EXPOSE 80 443
ENV VERSION=1.11.2.1
ADD openresty-$VERSION.tar.gz /usr/local/src
RUN cd /usr/local/src/openresty-$VERSION && ./configure --prefix=/usr/local --with-http_v2_module --with-http_ssl_module && make -j4 && make install && rm -rf /usr/local/src/openresty-$VERSION && rm -f /var/cache/apk/*
ADD nginx.conf /usr/local/nginx/conf
ENTRYPOINT ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

进行 Build 操作( make的输出太长就不贴出来了 ), 完成后查看一下

1
2
3
4
5
6
7
[root@freeipa101 1.11.2.1]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 65b5f5a29c4d 27 seconds ago 336MB
ricksheng/alpine_gcc 6.4.0 d7a938e82e4e 20 minutes ago 248MB
ricksheng/apline_glibc 2.23 216a3982623d 29 minutes ago 34.7MB
nginx latest be1f31be9a87 37 hours ago 109MB
alpine latest 196d12cf6ab1 3 weeks ago 4.41MB
1
[root@freeipa101 1.11.2.1]# docker tag 65b5f5a29c4d docker.io/ricksheng/openresty:1.11.2.1
1
[root@freeipa101 1.11.2.1]# docker push docker.io/ricksheng/openresty:1.11.2.1

到这里为止,我们的镜像就已经打完了,那如何验证呢?

2.4、验证 & 调试

首先你需要做一下身份认证, docker login

1
2
3
4
5
[root@freeipa101 ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username (shengguocun@gmail.com): ricksheng
Password:
Login Succeeded

登录完成之后,你的用户名、密码会以一个 base64 的字符串记录在本地,然后我们需要将镜像拉到本地

1
2
3
4
[root@freeipa101 ~]# docker pull ricksheng/openresty:1.11.2.1
1.11.2.1: Pulling from ricksheng/openresty
Digest: sha256:12067a9651f3148f2f916c19681c1bd95783b172e65adc36dcb303903fede11b
Status: Image is up to date for ricksheng/openresty:1.11.2.1

运行这个镜像

1
2
[root@freeipa101 ~]# docker run -d -p 8887:80 --name demo001 ricksheng/openresty:1.11.2.1
6b798d8b9cbf8393151766ab46bfad37342aaf793402841e36befc99c4a5c2c9

最后验证一下,请求宿主机的 IP + 端口

1
2
[root@freeipa101 ~]# curl 127.0.0.1:8887
HelloWorld

到这一个完整的创建 OpenRestyDocker 镜像的流程就结束了,当然在实际创建镜像的过程中会遇到各种各样的问题,我们需要进入到容器中进行调试。当然新手要注意的一件事 “容器就是容器,不要把容器当虚拟机”