Docker 镜像 (Harbor/BuildKit/buildx)

1. Docker 镜像 (Harbor/BuildKit/buildx)

1.1. 常用镜像

1.2. 打包、压缩

1.2.1. 基于 docker 容器 Commit 命令打包

第一步: 拉项目镜像

docker pull 镜像名

第二步: 基于镜像生成 docker(红色背景是项目的端口映射, 数据卷挂载, 定制化自启动和 root 登录权限), 这里按照自己的项目需求来:

docker run -dit -p 222:22 -p 8081:8080 -p 80:80 -p 3307:3306 -p 6380:6379 -p 15673:15672 -p 22123:22122 -p 23001:23000 -v /d/usr:/home -v /c/Windows/identification:/data/identification --privileged --restart=always -h 用户名 --name=容器名 镜像名: 版本号  /bin/bash  /etc/rc.d/enable

第三步: 进入 docker, 添加或修改 docker(这里看需求修改: 我个人添加表 sql, 备份文件, 脚本文件迁移到 docker 上)用到了以下指令:

1、进入 docker

docker start 容器名
docker exec -it 容器名  bash

2、宿主机复制文件到 docker(当然也可以用 Xftp):

docker cp 文件路径 容器长 ID:docker 容器中的路径

3、连接数据库执行 sql 文件

①连接 MySQL: mysql -u 用户 -p 密码

②选择数据库: use 数据库名;

③执行 sql 文件: source 脚本文件全路径 (/data/xxx.sql)

第四步: docker commit 命令生成副本镜像

sudo docker commit -m "备注" -a "修改人" 容器 id  镜像名: 新版本号

第五步: 验证副本镜像(重复第二步骤、第三步骤, 查看自己的修改)

第六步: docker push 命令上传至 Docker Hub 上

sudo docker push 镜像名: 新版本号

第七步: 登录 Docker Hub 查看自己的提交

1.2.2. 基于 docker 快照打包镜像 (export 导出, import 导入命令)

第一步: 基于容器导出 tar 包(这里 LZ 的 tar 包名: luntek-ic-platform3d5.tar), export 命令 (-o: 指向导出 tar 文件, 也可以用 “>” 大于号替代)

# container 表示容器 id 或容器名
docker export [options] container
# 如果没有启动的容器, 则需要启动容器
docker run -dit luntek/ic-platform:3.5

# 使用 export 导出容器, 使用的是容器 id
docker export > luntek-ic-platform3d5.tar 容器 ID
# 或者
docker export -o luntek-ic-platform3d5.tar 容器 ID 

第二步: docker import 将 tar 包解压导入为镜像(这里 LZ 的镜像名: luntek/ic-platform:3.5)

docker import [options] file|URL|- [REPOSITORY[:TAG]]
docker import luntek-ic-platform3d5.tar luntek/ic-platform:3.5
# 或者
cat luntek-ic-platform3d5.tar | docker import - luntek/ic-platform:3.5

第三步: docker 上传至 docker hub

sudo docker push luntek/ic-platform:3.5

1.2.3. 基于镜像导出导入 (save 导出, load 导入命令)

第一步: 基于镜像(可多个镜像)中导出文件(这里 LZ 的文件名: luntek-ic-platform3d5, 镜像名: luntek/ic-platform:3.5), save 命令 (-o: 指向导出文件, 也可以用 “>” 大于号替代)

# images [images...] 可以有多个 images 镜像
docker save [options] images [images...]
docker save -o /root/luntek-ic-platform3d5 luntek/ic-platform:3.5
# 或者
docker save > /root/luntek-ic-platform3d5 luntek/ic-platform:3.5

第二步: 基于文件解压导入镜像(这里 LZ 的绝对路径文件名: /root/luntek-ic-platform3d5), docker load 命令 (-i: 指向解压导入文件, 也可以用 “<” 小于号替代)

docker load -i /root/luntek-ic-platform3d5
# 或者
docker load < /root/luntek-ic-platform3d5

第三步: docker 上传至 docker hub

sudo docker push luntek/ic-platform:3.5

1.2.4. How to save all Docker images and copy to another machine

  • export all images at once, create one big tar file: docker save $(docker images -q) -o /path/to/save/mydockersimages.tar
  • load (import) the images: docker load -i /path/to/save/mydockersimages.tar

1.2.5. Save Docker Container As Image

To create an image from a container, you can use the docker commit command:

$ docker commit <container> <image>

Where:

  • container is the name or the ID of the container that can be obtained using the docker ps command.
  • image is the name of the image you want to create.

For example:

$ docker commit 4b2386a65651 node-server:beta

Note that if the container you are creating an image from is running, Docker will automatically pause it while the image is committed, in order to reduce the likelihood of encountering data corruption during this process.

Overwriting Dockerfile Instructions

A Dockerfile is a text file that contains all the necessary instructions for building a Docker image. These instructions can be used for installing and configuring command line tools, declaring environment variables, exposing ports, copying files from the local environment, and so on.

When creating an image from a container, these instructions can be overwritten using the -c flag (short for change).

For example, to change an environment variable:

$ docker commit -c 
 "ENV NODE_ENV=development" <container> <image>

Note it is possible to change multiple instructions at once:

$ docker commit -c 'WORKDIR /app' -c 
 'CMD ["node", "app.js"]' <container> <image>

Following could be useful:

  • tag the Image: docker tag 4b2386a65651 hello_world_node
  • set the author: docker commit --author amit.sharma@sentinelone.com nginx_base authored
  • create Commit Messages: docker commit --message 'this is a basic nginx image' nginx_base mmm
  • commit Without Pause (When you use the commit command, the container will be paused): docker commit --pause=false nginx_base wo_pause

More: docker commit docs

1.2.6. Docker import/export vs. load/save

$ docker --help | grep -E "(export|import|load|save)"

export      Export a container's filesystem as a tar archive
import      Import the contents from a tarball to create a filesystem image

load        Load an image from a tar archive or STDIN
save        Save one or more images to a tar archive (streamed to STDOUT by default)
  • docker save will indeed produce a tarball, but with all parent layers, and all tags + versions.
  • docker export does also produce a tarball, but without any layer/history.

It is often used when one wants to “flatten” an image, as illustrated in “Flatten a Docker container or image” from Thomas Uhrig:

docker export <CONTAINER ID> | docker import - some-image-name:latest

However, once those tarballs are produced, load/import are there to:

  • docker import creates one image from one tarball which is not even an image (just a filesystem you want to import as an image)

Create an empty filesystem image and import the contents of the tarball

By itself, this imported image will not be able to be run from docker run, since it has no metadata associated with it (e.g. what the CMD to run is.)

  • docker load creates potentially multiple images from a tarred repository (since docker save can save multiple images in a tarball).

Loads a tarred repository from a file or the standard input stream

By using load you can import the image(s) in the same way they were originally created with the metadata from the Dockerfile, so you can directly run them with docker run.

To summarize what we’ve learned, we now know the following:

  • save works with Docker images. It saves everything needed to build a container from scratch. Use this command if you want to share an image with others.

  • load works with Docker images. Use this command if you want to run an image exported with save. Unlike pull, which requires connecting to a Docker registry, load can import from anywhere (e.g. a file system, URLs).

  • export works with Docker containers, and it exports a snapshot of the container’s file system. Use this command if you want to share or back up the result of building an image.

  • import works with the file system of an exported container, and it imports it as a Docker image. Use this command if you have an exported file system you want to explore or use as a layer for a new image.

1.3. 创建镜像

1.3.1. 清理镜像

RUN apt-get --no-install-recommends install -y libaio-dev \
  && rm -rf /var/lib/apt/lists/*

1.3.2. Docker 镜像操作

创建镜像

前提: 当前文件有一个 Dockerfile, 用于指示创建镜像的流程。本博客有很多 Dockerfile 的案例代码供查询。

创建镜像命令: docker build -t charlywan/hexo_blog:latest .

查看镜像

docker images

删除镜像

docker rmi 67a7f3c78c5c
# 2. 或者
docker rmi 67a

上面的 rmi 是 remove image 的简写。

docker 镜像 mac 下保存路径

mac 下 docker 的镜像保存位置:

/Users/{YourUserName}/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2

1.3.3. 问题解决: 构建 Docker 镜像时处理 “Configuring tzdata” 交互输入

在 Dockerfile 中安装 deb 软件包时, 某些软件将 tzdata 作为依赖项安装。

tzdata 会以交互方式提醒用户选择使用位置。

    Configuring tzdata
    ------------------
    Please select the geographic area in which you live. Subsequent configuration
    questions will narrow this down by presenting a list of cities, representing
    the time zones in which they are located.
     1. Africa      4. Australia  7. Atlantic  10. Pacific  13. Etc
     2. America     5. Arctic     8. Europe    11. SystemV
     3. Antarctica  6. Asia       9. Indian    12. US
    Geographic area:

可能一直会卡在这个界面(我就遇到了)。

为了解决这个问题, 我们需要将 tzdata 设置为非交互方式。

首选的方法是在 Dockerfile 的第一条 RUN 之前加入以下配置:

ENV DEBIAN_FRONTEND=noninteractive

第二个方法是, 在 DEBIAN_FRONTEND=noninteractive 条件下使用命令 apt install 或 apt-get install 配置安装 tzdata:

RUN DEBIAN_FRONTEND=noninteractive apt install -y tzdata

这将自动选择默认配置安装 tzdata。

1.3.4. Docker 用 alphine 来创建镜像

安装常用软件

Alphine 默认不带 Bash 的, 所以要另外安装:

RUN apk --update add --no-cache git openssh bash && \
    rm -rf /var/lib/apt/lists/* && \
    rm -rf /var/cache/apk/*

配置 SSH

RUN sed -i "s/UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config && sed -i "s/UsePAM.*/UsePAM no/g" /etc/ssh/sshd_config && sed -i "s/PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config && sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /etc/ssh/sshd_config

遇到的问题

  • sshd: no hostkeys available -- exiting
# 3. 生成 host key, 只针对 alpine, 其它的默认就有
RUN ssh-keygen -f /etc/ssh/ssh_host_rsa_key     -N '' -t rsa -b 4096 &&\
    ssh-keygen -f /etc/ssh/ssh_host_dsa_key     -N '' -t dsa &&\
    ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key   -N '' -t ecdsa &&\
    ssh-keygen -f /etc/ssh/ssh_host_ed25519_key -N '' -t ed25519
RUN chmod 600 /etc/ssh/ssh_host_rsa_key &&\
    chmod 600 /etc/ssh/ssh_host_dsa_key &&\
    chmod 600 /etc/ssh/ssh_host_ecdsa_key &&\
    chmod 600 /etc/ssh/ssh_host_ed25519_key

1.3.5. 示例: Docker 创建 hexo 镜像并运行 hexo 容器

Docker 的官方并没有提供 hexo 镜像, 所以我们需要自己做。我们根据网友制作的 Dockerfile 我们知道可以基于 Node 的官方镜像制作。

制作可以基于 Debian 和 alphine, 这两者的 Dockfile 我都写好了。

其中为了以后 hexo 的开发我还加了 bash, git, ssh 等常规服务。

Alphine 版

FROM node:10.1.0-alpine
MAINTAINER CharlyWan "xxx@xxx.com"

# 4. prepare work directory
WORKDIR /blog

# 5. 修改 root 密码, 便于远程登录
RUN echo root:xxx | chpasswd

# 6. install ssh
# 7. RUN apt-get update
# 8. RUN apk add --no-cach openssh-server
RUN apk --update add --no-cache git openssh bash && \
    rm -rf /var/lib/apt/lists/* && \
    rm -rf /var/cache/apk/*
RUN mkdir -p /var/run/sshd
RUN mkdir -p /root/.ssh/

# 9. 生成 ssh key
# 10. RUN ssh-keygen -q -t rsa -b 2048 -f /root/.ssh/id_rsa -P '' -N 'xxx'

# 11. 复制与移动 key
# 12. RUN mv /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
# 13. RUN chmod 600 /root/.ssh/authorized_keys  
COPY ["authorized_keys", "/root/.ssh/authorized_keys"]
COPY ["oschina", "/root/.ssh/id_rsa"]
RUN chmod 600 /root/.ssh/authorized_keys &&\
    chmod 600 /root/.ssh/id_rsa
# 14. 生成 host key, 只针对 alpine, 其它的默认就有
RUN ssh-keygen -f /etc/ssh/ssh_host_rsa_key     -N '' -t rsa -b 4096 &&\
    ssh-keygen -f /etc/ssh/ssh_host_dsa_key     -N '' -t dsa &&\
    ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key   -N '' -t ecdsa &&\
    ssh-keygen -f /etc/ssh/ssh_host_ed25519_key -N '' -t ed25519
RUN chmod 600 /etc/ssh/ssh_host_rsa_key &&\
    chmod 600 /etc/ssh/ssh_host_dsa_key &&\
    chmod 600 /etc/ssh/ssh_host_ecdsa_key &&\
    chmod 600 /etc/ssh/ssh_host_ed25519_key

# 15. git config
RUN git config --global user.name "chuanwan"  &&\
    git config --global user.email "xxx@xxx.com"  &&\
    git config --global gui.encoding utf-8

# 16. 禁止密码登录等 SSH 设置
RUN sed -i "s/UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config && sed -i "s/UsePAM.*/UsePAM no/g" /etc/ssh/sshd_config && sed -i "s/PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config && sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /etc/ssh/sshd_config

# 17. install hexo
RUN npm install hexo-cli -g

# 18. replace this with your application's default port
EXPOSE 4000
EXPOSE 22

# 19. 以下注释掉, 多条 CMD 只有最后一条 CMD 有效
# 20. run ssh deamon
CMD ["/usr/sbin/sshd", "-D"]

# 21. run hexo server
# 22. CMD ["hexo", "server","-i","0.0.0.0"]

# 23. COPY ["run.sh", "/root/run.sh"]
# 24. RUN chmod +x /root/run.sh

# 25. ENTRYPOINT ["/root/run.sh"]
# 26. ENTRYPOINT ["/bin/bash"]

Debian 版

做是做了, 但是建议还是用 alphine 吧, 因为大牛好像都倾向使用它。

FROM node:latest
MAINTAINER CharlyWan "xxx@xxx.com"

# 27. prepare work directory
WORKDIR /blog

# 28. 修改 root 密码, 便于远程登录
RUN echo root:xxx | chpasswd

# 29. install ssh
RUN apt-get update &&\
    apt-get install -y openssh-server
RUN mkdir -p /var/run/sshd &&\
    mkdir -p /root/.ssh/

# 30. 生成 ssh key
# 31. RUN ssh-keygen -q -t rsa -b 2048 -f /root/.ssh/id_rsa -P '' -N 'xxx'

# 32. 复制与移动 key
# 33. RUN mv /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
# 34. RUN chmod 600 /root/.ssh/authorized_keys  
COPY ["authorized_keys", "/root/.ssh/authorized_keys"]
COPY ["oschina", "/root/.ssh/id_rsa"]
RUN chmod 600 /root/.ssh/authorized_keys &&\
    chmod 600 /root/.ssh/id_rsa

# 35. git config
RUN git config --global user.name "chuanwan" &&\
    git config --global user.email "xxx@xxx.com" &&\
    git config --global gui.encoding utf-8

# 36. 禁止密码登录
RUN sed -i "s/.*PasswordAuthentication.*/PasswordAuthentication no/g" /etc/ssh/sshd_config

# 37. install hexo
RUN npm install hexo-cli -g

# 38. replace this with your application's default port
EXPOSE 4000
EXPOSE 22

# 39. 以下注释掉, 多条 CMD 只有最后一条 CMD 有效
# 40. run ssh deamon
# 41. CMD ["/usr/sbin/sshd", "-D"]

# 42. run hexo server
# 43. CMD ["hexo", "server","-i","0.0.0.0"]

COPY ["run.sh", "/root/run.sh"]
RUN chmod +x /root/run.sh

ENTRYPOINT ["/root/run.sh"]

run.sh:

# 44. !/bin/bash
# 45. start ssh deamon
/usr/sbin/sshd -D
# 46. /usr/sbin/sshd -D & 不能使用 &, 因为如果到后台去运行了, 整个 container 就退出了
# 47. start hexo server
# 48. hexo server -i 0.0.0.0

启动 hexo 容器

docker-compose.yml

version: '2'
services:
    hexo: 
      image: charlywan/hexo_blog:latest
      container_name: hexo
      ports: 
        - "4000:4000/tcp"
        - "2222:22/tcp"
      volumes:
        - /data/hexo_blog/hexo_blog_src:/blog

1.3.6. 示例: Docker 创建 Nginx 镜像并运行 Nginx 容器

创建 Nginx 镜像

由于 Docker 的官方 Nginx 镜像并未暴露 443 端口, 而我的网页需要 https 的支持, 所以创建新的 Nginx 镜像很有必要。

基于 Docker 的特性, 我们只需要在其官方镜像的基础上稍加些东西暴露 443 端口即可, 我们尽量选择 Alpine 的镜像, 占用空间真的小。

Dockerfile:

FROM nginx:1.13.12-alpine

LABEL maintainer="charlywan <1@qq.com>"

EXPOSE 443

STOPSIGNAL SIGTERM

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

修改 Nginx 的默认配置文件

因为我们是在 Docker 的官方 Nginx 镜像基础上做的, 所以无需在镜像里面提取, 直接到 https://hub.docker.com/_/nginx/ 里面的 Github 中去找并修改。

根据官方的 Dockerfile 我们只需要 2 个文件: /etc/nginx/nginx.conf/etc/nginx/conf.d/default.conf。(依据是什么? 因为这两个文件有 COPY 操作。)

/etc/nginx/nginx.conf:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;
    server_tokens off; # 关闭显示 Nginx 版本号提升安全性

    gzip  on; # 启用压缩节约带宽

    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/default.conf:

server {
    listen       443 ssl http2;
    server_name  xxx.net www.xxx.net blog.xxx.net;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/xxx.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xxx.net/privkey.pem;
	
    #rewrite /.git/COMMIT_EDITMSG http://xxx.net permanent;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    # Protect ~ files
    location ~ ~$ 
    { 
        access_log off; 
        log_not_found off; 
        deny all; 
        return 404;
    }
 
    # Protect .git files
    location ~ /\.git 
    { 
        access_log off; 
        log_not_found off; 
        deny all; 
        return 404;
    }
	
    # Don't expose hidden files to the web
    location ~ /\. {
        return 404;
    }
	
    location ~ /.git/ {
        deny all;
        return 404;
    }
	
    #rewrite ^/(.*)\.(git|asp|aspx|asa|asax|dll|jsp|cgi|fcgi|sh|bash)(.*)$ http://xxx.net permanent;
    #rewrite ^/(.git) http://xxx.net permanent;
	
    location / {
        # 当发现 URL 里面含有 .git 时, 返回 "error"
        if ($request_uri ~* ".git") {
            return 200 "error";
        }
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    # 缓冲保留时间
    location ~ .*\.(?:js|css)$
    {
        expires      7d;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

server {
    listen 80;
    server_name xxx.net www.xxx.net blog.xxx.net;
    rewrite ^(.*) https://$server_name$1 permanent;
}

让 Nginx 使用 HTTPS 连接

从上面的配置文件我们可以看到, 我们是强制了使用 HTTPS 的。Nginx 中使用 HTTPS 非常简单, 只需要在 /etc/nginx/conf.d/default.conf 里面加入三行即可:

ssl on;
ssl_certificate <PATH TO A fullchain.pem FILE>;
ssl_certificate_key <PATH TO A privkey.pem>;

启动 Nginx 容器

docker-compose.yml:

version: '2'
services:
    hexo: 
      image: nginx:1.13.12-alpine
      container_name: nginx
      ports: 
        - "80:80"
        - "443:443"
      volumes:
        - /data/hexo_blog/public:/usr/share/nginx/html
        - /home/ubuntu/nginx/container/nginx.conf:/etc/nginx/nginx.conf
        - /home/ubuntu/nginx/container/default.conf:/etc/nginx/conf.d/default.conf
        - /home/ubuntu/certbot/etc/letsencrypt:/etc/letsencrypt

$ sudo docker-compose up -d

1.4. Docker Habor 一个比 Register 更加好用的仓库

Registry 是 Dcoker 官方的一个私有仓库镜像, 可以用来存储和管理自己的镜像

Harbor 是 VMWare 公司提供的一个用于存储和分发 Docker 镜像的企业级 Registry 服务器, 通过添加一些企业必需的功能特性, 例如安全、标识和管理等, 扩展了开源 Docker Distribution。支持多租户、 可扩展的 API 和 Web UI、支持跨多个注册表(包括 Harbor) 进行复制、支持身份集成和基于角色的访问控制。

Harbor 是 Docker Registry 的更高级封装, 提供分层传输机制, 优化网络传输; 提供 WEB 界面, 优化用户体验; 支持水平扩展集群; 有良好的安全机制; 提供了基于角色的访问控制机制等等, 比 Registry 强大太多了, 推荐使用 Harbor。

构建 Docker 仓库方式除了使用 Registry 之外, 还可以使用 Harbor, 如下为 Registry 方式缺点:

  1. 缺少认证机制, 任何人都可以随意拉取及上传镜像, 安全性缺失
  2. 缺乏镜像清理机制, 镜像可以 push 却不能删除, 日积月累, 占用空间会越来越大(如果要删除找到宿主机目录下面对应文件进行删除)
  3. 缺乏相应的扩展机制
  4. 鉴于以上缺点, 我们通常在生产环境中, 不会直接使用 docker registry 来实现提供镜像服务。

Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器, 通过添加一些企业必需的功能特性, 例如安全、标识和管理等, 扩展了开源 Docker Distribution。

作为一个企业级私有 Registry 服务器, Harbor 提供了更好的性能和安全。提升用户使用 Registry 构建和运行环境传输镜像的效率。Harbor 支持安装在多个 Registry 节点的镜像资源复制, 镜像全部保存在私有 Registry 中, 确保数据和知识产权在公司内部网络中管控。另外, Harbor 也提供了高级的安全特性, 诸如用户管理, 访问控制和活动审计等。

Harbor 仓库部署两种方式, 一种是 off-line , 一种是 on-line, 即离线和在线安装, 此处选择离线安装:

  • 安装 Docker-Compose 快速编排工具
curl -L https://github.com/docker/compose/releases/download/1.8.0/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
  • 下载 Habor 并且解压:
[root@www ~]# wget -c https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.0.tgz
  • 修改 Habor 配置文件 harbor.cfg, 修改 hostname 为本机 IP 地址, 下所示:
[root@www harbor]# vim harbor.cfg 
hostname = 192.168.179.100
  • 安装 Habor, 命令如下: ./install.sh

  • 登陆 Habor WEB 平台, 默认用户名: admin, 默认密码: Harbor12345, 可以在 habor.cnf 自己设置密码

# 49. 下面可以看到安装完 docker hub 的机器上面跑着很多容器, 所以不建议部署了 docker hub 的机器上跑别的应用, 只跑 docker hub 来为我们提供镜像
[root@www harbor]# docker ps
CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS                   PORTS                                                              NAMES
4db9f3a2c228        goharbor/nginx-photon:v1.7.0             "nginx -g 'daemon of??   2 minutes ago       Up 2 minutes (healthy)   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp   nginx
fbb0fb69d986        goharbor/harbor-jobservice:v1.7.0        "/harbor/start.sh"       2 minutes ago       Up 2 minutes                                                                                harbor-jobservice
794e29619d74        goharbor/harbor-portal:v1.7.0            "nginx -g 'daemon of??   2 minutes ago       Up 2 minutes (healthy)   80/tcp                                                             harbor-portal
1768ed74ea87        goharbor/harbor-core:v1.7.0              "/harbor/start.sh"       2 minutes ago       Up 2 minutes (healthy)                                                                      harbor-core
baa0ff5a77c8        goharbor/harbor-db:v1.7.0                "/entrypoint.sh post??   3 minutes ago       Up 2 minutes (healthy)   5432/tcp                                                           harbor-db
a4957591eb25        goharbor/harbor-registryctl:v1.7.0       "/harbor/start.sh"       3 minutes ago       Up 2 minutes (healthy)                                                                      registryctl
fae0c9ccf7bd        goharbor/harbor-adminserver:v1.7.0       "/harbor/start.sh"       3 minutes ago       Up 2 minutes (healthy)                                                                      harbor-adminserver
82bda7680cce        goharbor/redis-photon:v1.7.0             "docker-entrypoint.s??   3 minutes ago       Up 2 minutes             6379/tcp                                                           redis
acf4f076c2f4        goharbor/registry-photon:v2.6.2-v1.7.0   "/entrypoint.sh /etc??   3 minutes ago       Up 2 minutes (healthy)   5000/tcp                                                           registry
7e08dab0f44b        goharbor/harbor-log:v1.7.0               "/bin/sh -c /usr/loc??   3 minutes ago       Up 3 minutes (healthy)   127.0.0.1:1514->10514/tcp                                          

# 50. 可以看到数据也持久化在本地了
[root@www harbor]# ll /data/
total 8
drwxr-xr-x  2 root    root        6 Aug 14 21:28 ca_download
drwxr-xr-x  2   10000    10000    6 Aug 14 21:28 config
drwx------ 19 polkitd ssh_keys 4096 Aug 14 21:28 database
drwxr-xr-x  2   10000    10000    6 Aug 14 21:27 job_logs
drwxr-xr-x  2 root    root        6 Aug 14 21:28 psc
drwxr-xr-x  2 polkitd root       22 Aug 14 21:33 redis
drwxr-xr-x  3   10000    10000   20 Aug 14 10:11 registry
-rw-------  1   10000    10000   16 Aug 14 21:27 secretkey
  • 创建私有仓库用户名 lulei, 并且设置密码, 并且绑定 library 仓库
[root@localhost ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd  -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry=192.168.179.100
[root@localhost ~]# systemctl daemon-reload 
[root@localhost ~]# systemctl restart docker 

这里告诉你了怎么推送镜像, 需要修改你的镜像 tag, 保持和下面提示的一致

[root@localhost ~]# docker tag nginx:latest 192.168.179.100/library/nginx

创建用户已经授予权限, 该用户是用于登入仓库验证并且上传镜像的用户 , 将之前创建的用户加入进去

  • 登入到镜像仓库, 并且上传/下载 镜像

使用上面的用户名提前登入(输入你的密码), 然后再去上传镜像

[root@localhost ~]# docker login  192.168.179.100/library/nginx
Authenticating with existing credentials...
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

Login Succeeded

[root@localhost ~]# docker push 192.168.179.100/library/nginx 
The push refers to repository [192.168.179.100/library/nginx]
f978b9ed3f26: Pushed 
9040af41bb66: Pushed 
7c7d7f446182: Pushed 
d4cf327d8ef5: Pushed 
13cb14c2acd3: Pushed 
latest: digest: sha256:0efad4d09a419dc6d574c3c3baacb804a530acd61d5eba72cb1f14e1f5ac0c8f size: 1362

[root@localhost ~]# docker tag 02aedead27dd  192.168.179.100/library/tomcat:v1
[root@localhost ~]# docker push   192.168.179.100/library/tomcat:v1

下载你上传的镜像, 这里通过图形界面已经为我们提供了 push 命令, 点击然后在你的 shell 终端粘贴就行, 是不是很方便

[root@localhost ~]# docker rmi -f 2622e6cca7eb

[root@localhost ~]# docker pull 192.168.179.100/library/nginx:latest
latest: Pulling from library/nginx
8559a31e96f4: Already exists 
8d69e59170f7: Already exists 
3f9f1ec1d262: Already exists 
d1f5ff4f210d: Already exists 
1e22bfa8652e: Already exists 
Digest: sha256:0efad4d09a419dc6d574c3c3baacb804a530acd61d5eba72cb1f14e1f5ac0c8f
Status: Downloaded newer image for 192.168.179.100/library/nginx:latest
192.168.179.100/library/nginx:latest
  • 最后下载完镜像之后退出仓库
[root@localhost ~]# docker logout 192.168.179.100
Removing login credentials for 192.168.179.100
[root@localhost ~]# docker tag 688353a31fde  192.168.179.100/library/ansible/centos7-ansible

# 51. 退出之后, 如果需要上传镜像需要重新输入用户名密码, 可以看到只有登入才能上传镜像
[root@localhost ~]# docker push   192.168.179.100/library/ansible/centos7-ansible
The push refers to repository [192.168.179.100/library/ansible/centos7-ansible]
cf4eb7184a66: Preparing 
596e51307fcb: Preparing 
7794e20d52b7: Preparing 
f8c414e271fb: Preparing 
0d1585b29470: Preparing 
34e7b85d83e4: Waiting 
denied: requested access to the resource is denied

[root@localhost ~]# docker login 192.168.179.100
Username: lulei
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

2. 使用 Multi-stage builds

Multi-stage builds are useful to anyone who has struggled to optimize Dockerfiles while keeping them easy to read and maintain.

2.1. Use multi-stage builds

With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.

The following Dockerfile has two separate stages: one for building a binary, and another where we copy the binary into.

# 52. syntax=docker/dockerfile:1
FROM golang:1.21
WORKDIR /src
COPY <<EOF ./main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
COPY --from=0 /bin/hello /bin/hello
CMD ["/bin/hello"]

You only need the single Dockerfile. No need for a separate build script. Just run docker build.

docker build -t hello .

The end result is a tiny production image with nothing but the binary inside. None of the build tools required to build the application are included in the resulting image.

How does it work? The second FROM instruction starts a new build stage with the alpine:latest image as its base. The COPY --from=0 line copies just the built artifact from the previous stage into this new stage. The Go SDK and any intermediate artifacts are left behind, and not saved in the final image.

2.2. Name your build stages

By default, the stages aren’t named, and you refer to them by their integer number, starting with 0 for the first FROM instruction. However, you can name your stages, by adding an AS <NAME> to the FROM instruction. This example improves the previous one by naming the stages and using the name in the COPY instruction. This means that even if the instructions in your Dockerfile are re-ordered later, the COPY doesn’t break.

# 53. syntax=docker/dockerfile:1
FROM golang:1.21 as build
WORKDIR /src
COPY <<EOF /src/main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
COPY --from=build /bin/hello /bin/hello
CMD ["/bin/hello"]

2.3. Stop at a specific build stage

When you build your image, you don’t necessarily need to build the entire Dockerfile including every stage. You can specify a target build stage. The following command assumes you are using the previous Dockerfile but stops at the stage named build:

docker build --target build -t hello .

A few scenarios where this might be useful are:

  • Debugging a specific build stage
  • Using a debug stage with all debugging symbols or tools enabled, and a lean production stage
  • Using a testing stage in which your app gets populated with test data, but building for production using a different stage which uses real data

2.4. Use an external image as a stage

When using multi-stage builds, you aren’t limited to copying from stages you created earlier in your Dockerfile. You can use the COPY --from instruction to copy from a separate image, either using the local image name, a tag available locally or on a Docker registry, or a tag ID. The Docker client pulls the image if necessary and copies the artifact from there. The syntax is:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

2.5. Use a previous stage as a new stage

You can pick up where a previous stage left off by referring to it when using the FROM directive. For example:

# 54. syntax=docker/dockerfile:1

FROM alpine:latest AS builder
RUN apk --no-cache add build-base

FROM builder AS build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp

FROM builder AS build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp

2.6. Differences between legacy builder and BuildKit

The legacy Docker Engine builder processes all stages of a Dockerfile leading up to the selected --target. It will build a stage even if the selected target doesn’t depend on that stage.

BuildKit only builds the stages that the target stage depends on.

For example, given the following Dockerfile:

# 55. syntax=docker/dockerfile:1
FROM ubuntu AS base
RUN echo "base"

FROM base AS stage1
RUN echo "stage1"

FROM base AS stage2
RUN echo "stage2"

With BuildKit enabled, building the stage2 target in this Dockerfile means only base and stage2 are processed. There is no dependency on stage1, so it’s skipped.

 DOCKER_BUILDKIT=1 docker build --no-cache -f Dockerfile --target stage2 .
 [internal] load build definition from Dockerfile                                            0.0s
 => transferring dockerfile: 36B                                                             0.0s
 [internal] load .dockerignore                                                               0.0s
 => transferring context: 2B                                                                 0.0s
 [internal] load metadata for docker.io/library/ubuntu:latest                                0.0s
 CACHED [base 1/2] FROM docker.io/library/ubuntu                                             0.0s
 [base 2/2] RUN echo "base"                                                                  0.1s
 [stage2 1/1] RUN echo "stage2"                                                              0.2s
 exporting to image                                                                          0.0s
 => exporting layers                                                                         0.0s
 => writing image sha256:f55003b607cef37614f607f0728e6fd4d113a4bf7ef12210da338c716f2cfd15    0.0s
On the other hand, building the same target without BuildKit results in all stages being processed:

 DOCKER_BUILDKIT=0 docker build --no-cache -f Dockerfile --target stage2 .
 a7870fd478f4
 Running in e850d0e42eca
 d9f69f23cac8
 Running in 758ba6c1a9a3
 396baa55b8c3
 d9f69f23cac8
 Running in bbc025b93175
 09fc3770a9c4

The legacy builder processes stage1, even if stage2 doesn’t depend on it.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云满笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值