Dockerfile实战

docker最大的使用感受就是改变了运维部署的方式,把原本虚拟机的部署流程,打包后成为image的产物,以image作为部署介质。

如果获取、构建镜像就是重点了。

部署私有registry

如下私有仓库,只作为了解,体验仓库意义。

# 创建 Docker Registry 认证文件目录
mkdir /var/lib/registry_auth

# 使用 htpasswd 来创建加密文件
yum install -y httpd-tools
#创建账密
htpasswd -Bbn admin admin > /var/lib/registry_auth/htpasswd

## 使用docker镜像启动镜像仓库服务
# 重启docker服务,该容器会自动再运行
docker run -p 5000:5000 \
--restart=always \
--name registry \
-v /var/lib/registry:/var/lib/registry \
-v /var/lib/registry_auth/:/auth/ \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
-d registry

推送本地镜像

推送本地镜像到镜像仓库中,走ip地址即可

# 先修改镜像tag,指定往哪推送
[root@es-node1 ~]#docker tag nginx:alpine 10.0.0.18:5000/nginx:alpine

# docker不允许http仓库,必须走https
[root@es-node1 ~]#docker push 10.0.0.18:5000/nginx:alpine
The push refers to repository [10.0.0.18:5000/nginx]
Get "https://10.0.0.18:5000/v2/": http: server gave HTTP response to HTTPS client

# 1-加证书 2-修改配置忽略证书

[root@es-node1 ~]#cat /etc/docker/daemon.json 
{
  "registry-mirrors" : [
    "https://ms9glx6x.mirror.aliyuncs.com"
  ],
"insecure-registries":[
"10.0.0.18:5000"
]
}


# 重启,推送
[root@es-node1 ~]#systemctl restart docker
[root@es-node1 ~]#
[root@es-node1 ~]#
[root@es-node1 ~]#docker push 10.0.0.18:5000/nginx:alpine
The push refers to repository [10.0.0.18:5000/nginx]
419df8b60032: Preparing 
0e835d02c1b5: Preparing 
5ee3266a70bd: Preparing 
3f87f0a06073: Preparing 
1c9c1e42aafa: Preparing 
8d3ac3489996: Waiting 
no basic auth credentials
[root@es-node1 ~]#


#又出现认证错误
#docker logout 清除本地认证配置

[root@es-node1 ~]#docker login 10.0.0.18:5000
Username: admin
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@es-node1 ~]#cat ~/.docker/
buildx/      config.json  
[root@es-node1 ~]#cat ~/.docker/config.json 
{
    "auths": {
        "10.0.0.18:5000": {
            "auth": "YWRtaW46YWRtaW4="
        }
    }
}[root@es-node1 ~]#


# 再次推送
[root@es-node1 ~]#docker push 10.0.0.18:5000/nginx:alpine
The push refers to repository [10.0.0.18:5000/nginx]
419df8b60032: Pushed 
0e835d02c1b5: Pushed 
5ee3266a70bd: Pushed 
3f87f0a06073: Pushed 
1c9c1e42aafa: Pushed 
8d3ac3489996: Pushed 
alpine: digest: sha256:544ba2bfe312bf2b13278495347bb9381ec342e630bcc8929af124f1291784bb size: 1568
[root@es-node1 ~]#

# 查看registry所有镜像
[root@es-node1 ~]#curl -u admin:admin -X GET http://10.0.0.18:5000/v2/_catalog
{"repositories":["nginx"]}

# 查看镜像版本列表
[root@es-node1 ~]#curl -u admin:admin -X GET http://10.0.0.18:5000/v2/nginx/tags/list
{"name":"nginx","tags":["alpine"]}



# 下载镜像
[root@es-node1 ~]#docker pull 10.0.0.18:5000/nginx:alpine

Dockerfile文档

基础文档

http://ebook-p6.yuchaoit.cn/%E5%AE%B9%E5%99%A8/04_dockerfile%E6%9E%84%E5%BB%BA%E9%95%9C%E5%83%8F.html

实战java镜像

镜像构建优化

image-20230302160447411

调试镜像

下载一些镜像,测测它的环境如何

  • docker search
  • docekrhub搜索
# 技巧
docker search maven

# 调试镜像好与坏,启动后,结束自动删除
[root@es-node1 ~]#docker run -it --rm srinivasansekar/javamvn  bash

普通Dockerfile写法

能用,解决问题

FROM srinivasansekar/javamvn

WORKDIR /opt/springboot-app
COPY  . .
COPY settings.xml /usr/share/maven/conf/settings.xml
RUN mvn clean package -DskipTests=true

CMD [ "sh", "-c", "java -jar /opt/springboot-app/target/sample.jar" ]

具体操作

[root@es-node1 /opt/springboot-app]#ll
total 20
drwxr-xr-x 3 root root   50 Mar  2 17:26 ansible
-rw-r--r-- 1 root root  220 Mar  2 17:26 deploy.sh
-rw-r--r-- 1 root root  183 Mar  2 17:26 Dockerfile
-rw-r--r-- 1 root root  247 Mar  2 17:26 Dockerfile.multi
drwxr-xr-x 2 root root   22 Mar  2 17:26 jmeter
-rw-r--r-- 1 root root 3095 Mar  2 17:26 pom.xml
drwxr-xr-x 2 root root   24 Mar  2 17:26 robot
-rw-r--r-- 1 root root 2087 Mar  2 17:28 settings.xml
drwxr-xr-x 4 root root   30 Mar  2 17:26 src
[root@es-node1 /opt/springboot-app]#

# 构建镜像
[root@es-node1 /opt/springboot-app]#docker build .  -t example-java:v1 -f Dockerfile

# 运行镜像
[root@es-node1 /opt/springboot-app]#docker run -it --rm example-java:v1 bash
root@753080bc5dcd:/opt/springboot-app# ls target/sample.jar -lh
-rw-r--r-- 1 root root 21M Mar  2 09:36 target/sample.jar

image-20230302175132734

base image构建

maven镜像

FROM java:8-alpine

RUN apk add --update ca-certificates && rm -rf /var/cache/apk/* && \
  find /usr/share/ca-certificates/mozilla/ -name "*.crt" -exec keytool -import -trustcacerts \
  -keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts -storepass changeit -noprompt \
  -file {} -alias {} \; && \
  keytool -list -keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts --storepass changeit

ENV MAVEN_VERSION 3.5.4
ENV MAVEN_HOME /usr/lib/mvn
ENV PATH $MAVEN_HOME/bin:$PATH

RUN wget http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz && \
  tar -zxvf apache-maven-$MAVEN_VERSION-bin.tar.gz && \
  rm apache-maven-$MAVEN_VERSION-bin.tar.gz && \
  mv apache-maven-$MAVEN_VERSION /usr/lib/mvn

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

基础镜像就是构建运行环境,编译环境,会产生太多加工过程的数据。

好比蒸包子,这个过程有太多的素材,最终给客户的,只是成品。

例如我们运行一个go程序,只需要一个二进制命令,而不需要编译打包的过程。

前端镜像

前端是nginx+静态文件。

FROM nginx:1.19.0-alpine

LABEL maintainer="mritd <mritd@linux.com>"

ARG TZ='Asia/Shanghai'
ENV TZ ${TZ}

RUN apk upgrade --update \
    && apk add bash tzdata curl wget ca-certificates \
    && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && rm -rf /usr/share/nginx/html /var/cache/apk/*

COPY dist /usr/share/nginx/html

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

java镜像

运行后端jar包的容器,最终交付运行的镜像了。

FROM java:8u111

ENV JAVA_OPTS "\
-Xmx4096m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m"
ENV JAVA_HOME /usr/java/jdk
ENV PATH ${PATH}:${JAVA_HOME}/bin

COPY target/myapp.jar myapp.jar

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

EXPOSE 9000
CMD java ${JAVA_OPTS} -jar myapp.jar

多阶段构建

多阶段构建从Docker 17.05及更高版本的守护进程与客户端的新功能, 对于那些努力优化Dockerfile同时保持可阅读性和可维护性的人来说,多阶段构建是非常有用的。

多阶段构建之前

构建镜像最有挑战性之一的就是使用镜像尽可能小。Dockerfile中的每一个指令都会向镜像添加一个新的层, 在移动到下一个图层之前,你需要记得清理所有不再需要的历史遗留。要编写一个非常高效的Dockerfile, 传统思维是采用shell技巧或其他方法使层尽可能小,并确保每个层都能从上一层拿到需要的数据,并且不会多拿。

一个Dockerfile用于开发环境,其中包含构建应用程序所需的一切, 另一个精简版的Dockerfile,只包含你的应用程序及运行所需的内容,用于生产环境, 这种情况实际上非常普遍,这被称为”构建器模式”。

多阶段构建语法

在多阶段构建下,你可以在Dockerfile中使用多个FROM声明,每个FROM声明可以使用不同的基础镜像, 并且每个FROM都使用一个新的构建阶段。

你只需要一个Dockerfile文件即可,也不需要单独的构建脚本,只需要运行docker build

最终的结果是与前面一样的极小的结果,但是复杂性大大降低,你不需要创建任何中间镜像, 也根本不需要将任何工件(artifacts)提取到本地系统。

它是如何工作的?第二个FROM指令使用alpine:latest镜像作为基础开始一个新的构建阶段, COPY --from=0的行将前一个阶段的结果复制到新的阶段,GO SDK及所有中间产物被抛弃,并没有保存在最终镜像中。

命名构建阶段

默认情况下,构建阶段没有命名,使用它们的整数编号引用它们,从第一个FORM以0开始计数。 但是你可以使用给FORM指令添加一个as <NAME>为其构建阶段命名。以下示例通过命名构建阶段并在COPY指令中使用名称来改进上一个示例。 这意味着即使Dockerfile中的指令稍后发生顺序变化,COPY指令也不会出问题。

java

下载代码

git clone https://gitee.com/yuco/springboot-app.git
[root@es-node1 ~/devops-docker]#cd springboot-app/
[root@es-node1 ~/devops-docker/springboot-app]#ls
ansible  deploy.sh  Dockerfile  Dockerfile.multi  jmeter  pom.xml  README.md  robot  src
[root@es-node1 ~/devops-docker/springboot-app]#docker build . -t sample:v2 -f Dockerfile.multi

目的是提供一个java环境,运行jar包而已,将以前编译的过程,独立出去。

FROM maven as builder

WORKDIR /opt/springboot-app
COPY  . .
RUN mvn clean package -DskipTests=true

FROM openjdk:8-jdk-alpine
COPY --from=builder /opt/springboot-app/target/sample.jar sample.jar
CMD [ "sh", "-c", "java -jar /sample.jar" ]

如上是2个镜像的构建

docker build . -t sample:v2 -f Dockerfile.multi

产物

[root@es-node1 ~/devops-docker/springboot-app]#docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
sample       v2        81134b82c95c   46 minutes ago   126MB

go

git clone https://gitee.com/yuco/href-counter.git

原始构建

FROM golang:1.13

WORKDIR /go/src/github.com/alexellis/href-counter/

COPY vendor vendor
COPY app.go .
ENV GOPROXY https://goproxy.cn
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

CMD ["./app"]

构建

docker build . -t href-counter:v1 -f Dockerfile

[root@es-node1 ~/devops-docker/href-counter]#docker images
REPOSITORY     TAG       IMAGE ID       CREATED             SIZE
href-counter   v1        c0f6f7d75c9e   28 seconds ago      839MB

多阶段构建

FROM golang:1.13 AS builder

WORKDIR /go/src/github.com/alexellis/href-counter/

COPY vendor vendor
COPY app.go    .
ENV GOPROXY https://goproxy.cn

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:3.10
RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=builder  /go/src/github.com/alexellis/href-counter/app    .

CMD ["./app"]

构建

docker build . -t href-counter:v2 -f Dockerfile.multi

查看镜像区别

[root@es-node1 ~/devops-docker/href-counter]#docker images
REPOSITORY     TAG       IMAGE ID       CREATED              SIZE
href-counter   v2        d071c2b448de   About a minute ago   13.4MB
href-counter   v1        c0f6f7d75c9e   3 minutes ago        839MB
sample         v2        81134b82c95c   About an hour ago    126MB

image-20230303182748385

镜像构建小结

原则:

不必要的内容不要放在镜像中
减少不必要的层文件,例如多条linux命令,合并一条RUN
减少网络传输操作,尽可能少的,如curl,wget等
可以适当的包含一些调试命令,如ps,ss等基础环境的加入,否则难以维护

理解容器1号进程

[root@es-node1 ~]#docker run -it nginx:alpine sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps aux

也可以主动传入命令,覆盖镜像默认的CMD指令

[root@es-node1 ~]#docker run -it nginx:alpine ping www.yuchaoit.cn
PING www.yuchaoit.cn (82.157.248.168): 56 data bytes
64 bytes from 82.157.248.168: seq=0 ttl=127 time=10.501 ms

本质上讲容器是利用namespace和cgroup等技术在宿主机中创建的独立的虚拟空间,这个空间内的网络、进程、挂载等资源都是隔离的。

一号进程结束,等于老大挂了,小弟都得结束

[root@es-node1 ~]#docker run --name test1 -it nginx:alpine echo '超哥带你学k8s';sleep 5;
超哥带你学k8s
[root@es-node1 ~]#docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS     NAMES
2362a0a03a07   nginx:alpine   "/docker-entrypoint.…"   11 seconds ago   Exited (0) 10 seconds ago             test1

演示2

image-20230304151330108

Copyright © www.yuchaoit.cn 2025 all right reserved,powered by Gitbook作者:于超 2024-03-31 19:23:02

results matching ""

    No results matching ""