Docker, singularity Guide

Docker is an open source platform for building, deploying, and managing containerized applications.

Docker 参考资料
https://www.docker.com/
https://www.docker.org.cn/
Developers Love Docker. Businesses Trust It.
Build safer, share wider, run faster: New updates to our product subscriptions.

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/container-docker-introduction/docker-defined
https://www.ibm.com/cloud/learn/docker

官方镜像中心 https://hub.docker.com/
网易镜像中心 https://c.163yun.com/hub#/home

1. Docker 概念

Docker能把环境打包,实现运行环境的快速、方便迁移。

虚拟化软件运行环境,以最小的代价换取最大的资源。docker 直接利用宿主机的内容加上 docker engine 就能运行容器,极大的节省了性能开销。docker 是秒级,虚拟机是分钟级。

docker 中的三大概念:
仓库(repository):Docker hub,存放在镜像的地方,可配置国内镜像加速下载。
镜像(image):是一个模块,里面是打包的环境,可基于此模板运行处多个容器。
容器(container):镜像运行后会记录数据和状态,叫容器。通过镜像创建的独立运行的一个或一组应用。
  Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。


docker三大功能:构建(build)、运输(ship)、运行(run),只需要记下这三大功能就可以了。

Docker有五个命名空间:进程、网络、挂载、宿主和共享内存。这个用来排错。

2. Docker 安装

本文尝试在Ubuntu 20.04 和 CentOS7.9上分安装最新的 docker 版本。

2.1尝试在Ubuntu上使用 apt-get 安装 docker(推荐)

本文使用 Ubuntu 20.04 作为宿主机,安装 docker 最新版。

全新的 桌面版 Ubuntu 20.04.3 LTS (GNU/Linux 5.11.0-34-generic x86_64),virtualBox。
仅仅安装最基本的软件 install:
 - openssh-server
 - net-tools: ifconfig #前两个local shell安装
 - vim #其余的可以ssh登录后安装
 - htop
 - tmux
 - curl #后来装的

内核版本:
$ uname -r
5.11.0-34-generic
如果snap安装过docker,先卸载。
$ sudo snap remove docker
docker removed
$ docker --version
-bash: /snap/bin/docker: No such file or directory


以下为全新安装。

1. 替换为国内的源
$ sudo mv /etc/apt/sources.list /etc/apt/sources.list-backup
$ sudo vim /etc/apt/sources.list
#清华源 https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/
# 可以把偶数行的 -src 注释掉,如果不看源码的话。会更快。
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse


2. 安装
$ sudo apt update
$ sudo apt upgrade #是否有必要呢?

$ sudo apt install docker.io

$ docker --version
Docker version 20.10.7, build 20.10.7-0ubuntu1~18.04.1
为什么比昨天安装的落后一个版本号20.10.8呢?


(1) 查看版本号 
$ whereis docker
docker: /usr/bin/docker /etc/docker /usr/share/docker.io /usr/share/man/man1/docker.1.gz

$ which docker
/usr/bin/docker
$ ls -lth /usr/bin/docker
-rwxr-xr-x 1 root root 69M Aug  5 03:22 /usr/bin/docker

重启的方式:
$ sudo service docker status #active

查看启动的位置:
$ ps -aux | grep docker
root       19330  0.0  4.4 725440 89884 ?        Ssl  19:48   0:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
wangjl     19481  0.0  0.0  17672   740 pts/1    S+   20:09   0:00 grep --color=auto docker


(2) 通过将用户添加到docker用户组可以将sudo去掉
https://linuxhandbook.com/docker-permission-denied/

$ sudo groupadd docker #添加docker用户组
$ sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
Adding user wangjl to group docker

$ newgrp docker #更新用户组
$ groups #或者 id 命令 查看当前用户所在的组
docker adm cdrom sudo dip plugdev lpadmin lxd sambashare wangjl


把待启动锁改为 docker 组
$ ls -lth /var/run/docker.sock
srw-rw---- 1 root root 0 Sep 14 11:30 /var/run/docker.sock
$ sudo chown root:docker /var/run/docker.sock
$ ls -lth /var/run/docker.sock
srw-rw---- 1 root docker 0 Sep 14 11:30 /var/run/docker.sock


(3) docker 镜像保存的位置
$ docker info
...
 Storage Driver: overlay2
 Docker Root Dir: /var/lib/docker
...

$ sudo ls -lth /var/lib/docker
total 48K
drwx------ 2 root root 4.0K Sep 14 19:48 tmp
drwx-----x 3 root root 4.0K Sep 14 19:48 overlay2
drwx-----x 2 root root 4.0K Sep 14 19:48 volumes
drwx------ 2 root root 4.0K Sep 14 19:48 runtimes
-rwxr-xr-x 1 root root 1.5K Sep 14 19:39 nuke-graph-directory.sh
drwx--x--x 4 root root 4.0K Sep 14 19:16 buildkit
drwx------ 2 root root 4.0K Sep 14 19:16 swarm
drwxr-x--- 3 root root 4.0K Sep 14 19:16 network
drwx------ 2 root root 4.0K Sep 14 19:16 trust
drwx------ 3 root root 4.0K Sep 14 19:16 image
drwx------ 4 root root 4.0K Sep 14 19:16 plugins
drwx-----x 2 root root 4.0K Sep 14 19:16 containers

拉取一个轻量级镜像: 227M
$ docker pull hub.c.163.com/library/node:slim
$ docker tag hub.c.163.com/library/node:slim node:slim
$ docker rmi hub.c.163.com/library/node:slim

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
node         slim      914ef9e2ccb0   4 years ago   227MB


(4) 挂载一块硬盘到 /data 下。

必须先把虚拟机关机才能添加硬盘。
$ sudo shutdown -t now
右击-设置-存储,右击sata-创建新的虚拟硬盘-创建一个10G动态分配大小的磁盘,文件名是 NewVirtualDisk1.vdi

再次双击启动虚拟机。
1)查看新磁盘名字
$ sudo fdisk -l
Device     Boot   Start      End  Sectors  Size Id Type
/dev/sda1  *       2048  1050623  1048576  512M  b W95 FAT32
/dev/sda2       1052670 20969471 19916802  9.5G  5 Extended
/dev/sda5       1052672 20969471 19916800  9.5G 83 Linux

Disk /dev/sdb: 10 GiB, 10737418240 bytes, 20971520 sectors
Disk model: VBOX HARDDISK   
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

2)给新加的硬盘分区 要有root权限
$ sudo fdisk /dev/sdb
键入 m,可看到帮助信息
	command (m for help):m     
增加新分区
	command (m for help):n   
选择基本分区,输入: p
建一个分区
	Partition number(1-4):1 
回车
	First cylinder (1-15908,default 1):Enter
写入并退出
	command (m for help):w
3)用 ext4格式对/dev/sdb1进入格式化
$ sudo mkfs.ext4 /dev/sdb1

4)创建新的挂载点
$ sudo mkdir /data2
5)将新磁盘分区挂载到 /work目录下
$ sudo mount -t ext4 /dev/sdb1 /data2  #后两个参数分别是硬件和挂载点  

5)查看挂载
$ df -h
可以看到新加的硬盘:
/dev/sdb1       9.8G   37M  9.3G   1% /data2

6)开机自动挂载
修改文件 
$ sudo vim /etc/fstab
在最后一行加入:
/dev/sdb1 /data2 ext4 errors=remount-ro 0 1



(5) 运行镜像 
$ docker run -it --mount type=bind,source=/home/,target=/tmp02 --rm node:slim bash #ok
root@b0180129ec0a:/# exit
exit

$ df -h | grep sd
/dev/sda5       9.3G  6.4G  2.5G  73% /
/dev/sda1       511M  4.0K  511M   1% /boot/efi
/dev/sdb1       9.8G   37M  9.3G   1% /data2

$ docker run -it --mount type=bind,source=/data1,target=/tmp02 --rm node:slim bash
root@05beaff3515e:/# exit
exit
挂在 $HOME 以外的目录到docker,没有报错。



docker 包括服务端和客户端。
$ docker version
Client:
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.13.8
 Git commit:        20.10.7-0ubuntu1~18.04.1
 Built:             Wed Aug  4 22:43:25 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.8
  Git commit:       20.10.7-0ubuntu1~18.04.1
  Built:            Wed Aug  4 19:22:59 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.2-0ubuntu1~18.04.2
  GitCommit:        
 runc:
  Version:          1.0.0~rc95-0ubuntu1~18.04.2
  GitCommit:        
 docker-init:
  Version:          0.19.0
  GitCommit:

2.2 尝试在Ubuntu上使用 snap 安装 docker(不推荐)

使用 snap 安装的docker只能挂载$HOME 下的目录,不能挂载/data等目录。

https://docs.docker.com/get-docker/

1. snap 安装
$ sudo snap install docker
docker 20.10.8 from Canonical✓ installed

(1) 查看版本号
$ docker --version
Docker version 20.10.8, build 3967b7d28e

$ whereis docker
docker: /snap/bin/docker.compose /snap/bin/docker /snap/bin/docker.help /snap/bin/docker.machine
$ which docker
/snap/bin/docker
$ ls -lth /snap/bin/docker
lrwxrwxrwx 1 root root 13 9月  14 11:30 /snap/bin/docker -> /usr/bin/snap

$ /usr/bin/snap --version
snap    2.51.3
snapd   2.51.3
series  16
ubuntu  20.04
kernel  5.11.0-34-generic

$ /snap/bin/docker --version
Docker version 20.10.8, build 3967b7d28e


重启的方式
$ sudo snap start docker
Started.
$ sudo snap services
Service         Startup  Current  Notes
docker.dockerd  enabled  active   -


查看启动的位置
$ ps -aux | grep docker
root        7491  4.4  3.9 1348260 79800 ?       Ssl  16:31   0:05 dockerd --group docker --exec-root=/run/snap.docker --data-root=/var/snap/docker/common/var-lib-docker --pidfile=/run/snap.docker/docker.pid --config-file=/var/snap/docker/1125/config/daemon.json
root        7538  2.7  2.1 1203244 42736 ?       Ssl  16:31   0:03 containerd --config /run/snap.docker/containerd/containerd.toml --log-level error
wangjl      7738  0.0  0.0  17672   732 pts/1    S+   16:33   0:00 grep --color=auto docker


$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied



(2) 通过将用户添加到docker用户组可以将sudo去掉
https://linuxhandbook.com/docker-permission-denied/

$ sudo groupadd docker #添加docker用户组
$ sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
Adding user wangjl to group docker

$ newgrp docker #更新用户组
$ groups #或者 id 命令 查看当前用户所在的组
docker adm cdrom sudo dip plugdev lpadmin lxd sambashare wangjl


把待启动锁改为 docker 组
$ ls -lth /var/run/docker.sock
srw-rw---- 1 root root 0 Sep 14 11:30 /var/run/docker.sock
$ sudo chown root:docker /var/run/docker.sock
$ ls -lth /var/run/docker.sock
srw-rw---- 1 root docker 0 Sep 14 11:30 /var/run/docker.sock



(3) docker 镜像保存位置
$ docker info
...
 Storage Driver: overlay2
 Docker Root Dir: /var/snap/docker/common/var-lib-docker
...

$ sudo ls -lth /var/snap/docker/common/var-lib-docker
total 48K
drwx------ 2 root root 4.0K Sep 14 16:31 tmp
drwx--x--x 4 root root 4.0K Sep 14 16:31 buildkit
drwx-----x 3 root root 4.0K Sep 14 16:31 overlay2
drwx------ 2 root root 4.0K Sep 14 16:31 swarm
drwxr-x--- 3 root root 4.0K Sep 14 16:31 network
drwx------ 2 root root 4.0K Sep 14 16:31 trust
drwx------ 3 root root 4.0K Sep 14 16:31 image
drwx-----x 2 root root 4.0K Sep 14 16:31 volumes
drwx------ 4 root root 4.0K Sep 14 16:31 plugins
drwx-----x 2 root root 4.0K Sep 14 16:31 containers
drwx------ 2 root root 4.0K Sep 14 16:31 runtimes
drwx--x--x 3 root root 4.0K Sep 14 16:31 containerd

拉取一个轻量级镜像: 227M
$ docker pull hub.c.163.com/library/node:slim
$ docker tag hub.c.163.com/library/node:slim node:slim
$ docker rmi hub.c.163.com/library/node:slim

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
node         slim      914ef9e2ccb0   4 years ago   227MB


(4) 挂载一块硬盘到 /data 下。
见上文。
关闭虚拟机,添加虚拟盘。
开机,格式化,
挂载:
$ sudo mkdir /data1
$ sudo mount -t ext4 /dev/sdb1 /data1  #后两个参数分别是硬件和挂载点  


(5) 运行镜像 
$ docker run -it --mount type=bind,source=/home/,target=/tmp02 --rm node:slim bash #ok
root@7648f5a17624:/# exit
exit

$ df -h | grep sd
/dev/sda5       9.3G  5.9G  3.0G  67% /
/dev/sda1       511M  4.0K  511M   1% /boot/efi
/dev/sdb1       9.8G   37M  9.3G   1% /data2

$ docker run -it --mount type=bind,source=/data2,target=/tmp02 --rm node:slim bash
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /data2.
See 'docker run --help'.

这里报错了!不能挂载 $HOME 以外的目录。

2.3 尝试在 centOS7 安装 docker (推荐)

1.查询老版本docker

$ rpm -qa|grep -i docker

如果有,删掉:
# rpm -e docker-1.13.1-75.git8633870.el7.centos.x86_64
# rpm -e docker-client-1.13.1-75.git8633870.el7.centos.x86_64
# rpm -e docker-common-1.13.1-75.git8633870.el7.centos.x86_64


2.安装docker(跳过)
$ sudo yum install -y docker

$ docker --version
Docker version 1.13.1, build 7d71120/1.13.1
很古老的版本! 不支持 mount


3.重新安装,按照官方文档 https://docs.docker.com/engine/install/centos/
$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum install docker-ce docker-ce-cli containerd.io
根据提示,按两次y。

$ docker --version
Docker version 20.10.9, build c2ea9bc
这是最新版,可以用了。




4. 修改镜像地址,
$ sudo vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://uulfe4bd.mirror.aliyuncs.com"]
}

启动服务
$ sudo systemctl start docker



5. 解决权限
$ sudo groupadd docker #添加docker用户组
$ sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
Adding user wangjl to group docker

$ newgrp docker #更新用户组
$ groups #或者 id 命令 查看当前用户所在的组
docker adm cdrom sudo dip plugdev lpadmin lxd sambashare wangjl

修改执行组为 docker 
$ sudo chown root:docker /var/run/docker.sock
srw-rw---- 1 root docker 0 Oct 11 22:48 /var/run/docker.sock


6. nginx镜像使用实例
拉取镜像
$ docker pull nginx

查看版本号
$ docker run --rm -it nginx nginx -v
nginx version: nginx/1.21.3

查看配置文件
## docker run --rm -it nginx cat /etc/nginx/nginx.conf
$ docker run --rm -it nginx cat /etc/nginx/conf.d/default.conf

模仿 default.conf 文件写一个配置文件
$ cat nginx.config 
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    #access_log  /var/log/nginx/host.access.log  main;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
		
        # list files
        autoindex on; 
        autoindex_exact_size on; 
        autoindex_localtime on; 
    }
}

启动容器:做文件挂载、端口映射。
$ docker run --rm -d -p 8080:80 \
--mount type=bind,source=/home/wangjl/,target=/usr/share/nginx/html/ \
--mount type=bind,source=$PWD/nginx.config,target=/etc/nginx/conf.d/default.conf \
nginx

检查状态
$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                   NAMES
81d91430ec78   87a94228f133   "/docker-entrypoint.…"   50 seconds ago   Up 49 seconds   0.0.0.0:8080->80/tcp, :::8080->80/tcp   reverent_buck

打开浏览器,http://IP:8080
能看到文件列表。

如果无法访问,可能是防火墙。

$ sudo systemctl stop firewalld   关闭防火墙

2.4 移动数据目录

默认 docker 都在根目录,可能随着时间的推移导致系统盘不够用。建议一开始就把docker数据切换到一个大硬盘,这样还能防止经常读写的系统盘坏掉而损失数据。

示例: 把默认的docker目录 /var/lib/docker 改为 /data/docker/lib/docker 。

查看版本
$ docker --version
Docker version 20.10.15, build fd82621

查看数据保存的位置
$ sudo docker info
Docker Root Dir: /var/lib/docker
Registry: https://index.docker.io/v1/


1. 停止docker服务。
$ sudo systemctl stop docker
或 
$ sudo service docker stop
$ service docker status


2. 创建新的docker目录
执行命令df -h,找一个大的磁盘。 我在 /home目录下面建了 /home/docker/lib目录,执行的命令是:
## mkdir -p /home/docker/lib

$ sudo mkdir /data/docker
$ sudo mkdir /data/docker/lib

3. 迁移 /var/lib/docker 目录下面的文件到 /home/docker/lib:
## rsync -avz /var/lib/docker /home/docker/lib/

$ sudo rsync -avz /var/lib/docker /data/docker/lib/
实际位置从 /var/lib/docker 变为 /data/docker/lib/docker
耗时: 14:25-->

还有人用 -P 参数: rsync -avzP /var/lib/docker/ /data/lib/docker


4、(跳过该步骤。可能是老方案,不一定可用了) 配置 /etc/systemd/system/docker.service.d/devicemapper.conf
查看 devicemapper.conf 是否存在。如果不存在,就新建。
$ sudo mkdir -p /etc/systemd/system/docker.service.d/
$ sudo vim /etc/systemd/system/docker.service.d/devicemapper.conf

然后在 devicemapper.conf 写入:(同步的时候把父文件夹一并同步过来,实际上的目录应在 /home/docker/lib/docker )
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd  --graph=/data/docker/lib/docker
#ExecStart=/usr/bin/dockerd  --graph=/home/docker/lib/docker


5. 修改配置目录
如果配置文件不存在,则新建一个。

$ cat /etc/docker/daemon.json 
{
  "data-root": "/data/docker/lib/docker"
}

(可选)添加国内的源:
$ cat /etc/docker/daemon.json 
{
  "data-root": "/data/docker/lib/docker",
  "registry-mirrors": ["http://hub-mirror.c.163.com"]
}

或者 华中科技大学的源:
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]


6.  重新启动 docker 服务
# sudo systemctl daemon-reload  #加载配置文件
# sudo systemctl restart docker.service #重启服务
# sudo systemctl enable docker  #允许开机启动

$ sudo service docker start


7. 配置核查
docker info 命令检查Docker 的根目录.它将被更改为 /home/docker/lib/docker

$ sudo docker info | grep Root
 Docker Root Dir: /data/docker/lib/docker


8. 启动成功后,再确认之前的镜像还在:
$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
AAA/AAA               v2                  7331b8651bcc        27 hours ago        3.85GB
BBB/BBB               v1                  da4a80dd8424        28 hours ago        3.47GB

9、 确定容器没问题后删除 /var/lib/docker/ 目录中的文件。
$ sudo mv /var/lib/docker /var/lib/docker-can-delete  #先重命名文件,放几周再删掉。

$ ls -lth /var/lib/ | grep docker
drwx--x---. 15 root           root            200 May 10 14:04 docker-can-delete
drwxr-xr-x.  2 root           root              6 May  6 13:42 docker-engine

3. Docker 运行常用操作

3.1. 获取和查看镜像(搜索镜像名、标签名)

(1)配置国内镜像加速
$ sudo vim /etc/docker/daemon.json 
{
  "registry-mirrors": [
    "http://hub-mirror.c.163.com",
    "https://registry.cn-hangzhou.aliyuncs.com"
  ]
}

$ sudo systemctl daemon-reload #重启守护进程
$ sudo systemctl restart docker


(2) 下载基本镜像
$ docker login -u 用户名
输入密码 # 登录后增大下载成功几率
$ docker pull ubuntu #下载镜像
$ docker pull nginx


(3)如果不确定镜像名字,可以先搜索
$ docker search ubuntu #搜索镜像

$ docker search seurat
$ docker search monocle
$ docker search rstudio
NAME              DESCRIPTION              STARS     OFFICIAL   AUTOMATED
rocker/rstudio    RStudio Server image     372                  [OK]


2)如何获取tag编号呢?
https://www.jianshu.com/p/f974ec9e7937
$ curl -L -s 'https://registry.hub.docker.com/v2/repositories/library/hello-world/tags?page_size=1024' | jq '.results[]["name"]' | sed 's/\"//g' | sort -u

改成不另装软件的可行方法: 使用v2 API:
$ cat getTags.sh
#!/bin/bash
repo_url=https://registry.hub.docker.com/v2/repositories/
image_name=$1
url=${repo_url}/${image_name}/tags?page_size=1024
echo $url;
curl -L -s $url; 
$ bash getTags.sh biocontainers/vcftools
注意:基础镜像要写前缀 bash getTags.sh library/ubuntu
输出结果复制
==> 打开chrome浏览器,按f12打开控制台console,
> t1= 粘贴刚才的输出。等号右侧最外层是{},不用加引号。
> for(i=0; i<t1.count; i++){ console.log(t1.results[i].name) }
v0.1.16-1-deb_cv1
v0.1.14_cv2
v0.1.15_cv2
v0.1.14_cv1
v0.1.15_cv1
0.1.14
0.1.15

然后就可以下载最新稳定版
$ docker pull biocontainers/vcftools:0.1.15

$ docker run --rm -it biocontainers/vcftools:0.1.15 bash
biodocker@dc1fb0dfabaf:/data$ vcftools --version
VCFtools (0.1.15)


其实有更简单的v1 API,这个输出信息比较少,结果可以直接肉眼看。
$ curl -L -s https://registry.hub.docker.com/v1/repositories/biocontainers/vcftools/tags
[{"layer": "", "name": "0.1.14"}, {"layer": "", "name": "0.1.15"}, {"layer": "", "name": "v0.1.14_cv1"}, {"layer": "", "name": "v0.1.14_cv2"}, {"layer": "", "name": "v0.1.15_cv1"}, {"layer": "", "name": "v0.1.15_cv2"}, {"layer": "", "name": "v0.1.16-1-deb_cv1"}]
==> chrome F12 console:
t2=[...]
for(var i=0; i<t2.length; i++){console.log(t2[i].name)}
结果同上。




(4) 重命名一个容器
老容器没有删除,且他们的id相同
$ docker tag openjdk:9.0.1-11-slim dawneve/openjdk:slim #旧名字:新名字

(5) 查看已经下载的镜像
$ docker images #查看本地存在的镜像
$ docker image ls #新风格的命令: 子命令
REPOSITORY        TAG             IMAGE ID       CREATED        SIZE
dawneve/openjdk   slim            5149033ba93d   3 years ago    374MB
openjdk           9.0.1-11-slim   5149033ba93d   3 years ago    374MB

(6) 移除本地镜像
$ docker rmi dawneve/openjdk:slim

(7) 清理所有未打过标签的本地镜像:
$ docker rmi $(docker images -q -f "dangling=true")
其中 -q 和 -f 是 quiet,–filter 的缩写, 完整的命令其实可以写着下面这样,是不是更容易理解一点?
$ docker rmi $(docker images --quiet --filter "dangling=true")

3.2. 运行一个容器:nginx/java/mysql/Rstudio

进入容器后,很多docker是非常轻量的,默认没有vi/vim,但也不必装,不建议直接在容器中更改配置文件等信息。可以把宿主机编辑好的配置文件挂载到容器内的配置文件上。

(1) 基础命令
$ docker run ubuntu /bin/echo 'hello world' #运行一条命令后直接退出

$ docker run --name myUbt -it ubuntu /bin/bash #进入docker容器
root@e780bf085516:/# exit
exit
-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,
-i 则让容器的标准输入保持打开。
--name 给容器自定义一个名字


$ docker run -d --name mynginx nginx  #启动nginx镜像,没有会自动pull
-d, --detach  Run container in background and print container ID 后台运行容器,并打印容器id
注: 容器是否会长久运行,是和docker run指定的命令有关,和 -d 参数无关。


$ docker run --rm --name myUbt2 -it ubuntu /bin/bash #运行完删除容器文件
--rm   Automatically remove the container when it exits


(2) 端口映射 -p
按照当前政策规定,很多单位的80端口路由器是不转发的,所以想访问只能设置为其他值。
$ docker run -d -p 91:80 --name mynginx2 nginx  # -p 指定端口映射,host的91端口指向docker的80端口
访问宿主机的IP:91即可看到 nginx的欢迎页: http://192.168.2.242:91/


(3) 文件映射 -v,文件挂载 --mount
-v hostPath/:dockerPath/
--mount type=bind,source=/home/wangjl/,target=/test/ #支持挂载文件


实例1: 启动一个docker,使宿主机某个文件夹可以web访问
$ docker exec -it mynginx2 bash 
root@5911b4776952:/# nginx -version
nginx version: nginx/1.21.3 #版本号
root@5911b4776952:/# cat /etc/nginx/conf.d/default.conf #查询根目录地址
	root   /usr/share/nginx/html;
root@5911b4776952:/# ls /usr/share/nginx/html/
50x.html  index.html






做文件映射 
$ docker run --rm -d -p 92:80 -v /home/wangjl/:/usr/share/nginx/html/ nginx
访问本机IP:92,可以看到 http://192.168.2.242:92/outer.html

==> 缺点: 该docker内部没有vim/vi,配置文件无法修改。也外部提供一个?
$ cat nginx.config 
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    #access_log  /var/log/nginx/host.access.log  main;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
		
        # list files
        autoindex on; 
        autoindex_exact_size on; 
        autoindex_localtime on; 
    }
}

$ docker run --rm -d -p 93:80 \
--mount type=bind,source=/home/wangjl/,target=/usr/share/nginx/html/ \
--mount type=bind,source=/home/wangjl/nginx.config,target=/etc/nginx/conf.d/default.conf \
nginx

$ docker ps 
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                               NAMES
bc0a93c4df18   nginx     "/docker-entrypoint.…"   6 seconds ago    Up 3 seconds    0.0.0.0:93->80/tcp, :::93->80/tcp   loving_mirzakhani

宿主机打开 http://192.168.2.242:93/ 能看到文件列表了。
该方法特别适合查看主机上画的图片,只需要设定启动docker时的source目录即可。






实例2: 使用docker 编译java源代码
我主机上没有安装java,但是想编译和运行java怎么办?

查看docker内的java的版本号
$ docker run --rm -v /home/wangjl/:/test/ openjdk:9.0.1-11-slim java --version
openjdk 9.0.1
OpenJDK Runtime Environment (build 9.0.1+11-Debian-1)
OpenJDK 64-Bit Server VM (build 9.0.1+11-Debian-1, mixed mode)

写java代码
$ pwd
/home/wangjl

$ cat Hello.java
public class Hello { 
	public static void main(String args[])  {    
		System.out.println("Hello world, from Java!");  
	}
}

在容器中编译和运行
$ docker run --rm -v /home/wangjl/:/test/ openjdk:9.0.1-11-slim bash -c 'cd /test/ && javac Hello.java'
$ docker run --rm -v /home/wangjl/:/test/ openjdk:9.0.1-11-slim bash -c 'cd /test/ && java Hello'
Hello world, from Java!


文件挂载(推荐) --mount,并运行多个shell命令
该方法是挂载,可以挂载目录和文件。
$ docker run --rm --mount type=bind,source=/home/wangjl/,target=/test/ openjdk:9.0.1-11-slim bash -c 'cd /test/ && java Hello'
Hello world, from Java!



(4) 设置工作目录 --workdir=/home/tom/
i)使用 -v 挂载,在~/test/中编译java文件 ~/Hello.java
$ mkdir test
$ docker run --rm \
--volume /home/wangjl/test:/home/tom/:rw \
--volume /home/wangjl/Hello.java:/home/tom/Hello.java:ro \
--workdir=/home/tom/ \
openjdk:9.0.1-11-slim \
javac Hello.java

$ ls -lth test
total 4.0K
-rw-r--r-- 1 root root 427 Sep 15 10:13 Hello.class
-rwxr-xr-x 1 root root   0 Sep 15 10:13 Hello.java


ii) 使用 --mount 挂载,用docker运行java程序 ~/test/Hello.class
$ docker run --rm \
--mount type=bind,source=/home/wangjl/test,target=/test/,readonly \
--workdir=/test/ \
openjdk:9.0.1-11-slim java Hello
输出: Hello world, from Java!







# 实例3: 运行一个 mysql 实例,做文件映射和端口映射
$ docker pull hub.c.163.com/library/mysql:8.0 #拉镜像
帮助页面: https://c.163.com/hub#/m/repository/?repoId=2955 需要先登录网易账号。
Dockerfile: https://github.com/docker-library/mysql/blob/dc60c4b80f3eb5b7ef8b9ae09f16f6fab7a2fbf5/8.0/Dockerfile

$ docker tag hub.c.163.com/library/mysql:8.0 mysql:8.0 #改名字


运行该镜像:使用 -e 设置参数。
$ docker run -p 8088:3306 -d -it -e MYSQL_ROOT_PASSWORD=123456 -v /home/wangjl/dbFile:/var/lib/mysql mysql:8.0
0c9e8a4e20
$ docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS         PORTS                                       NAMES
0c9e8a4e206f   mysql:8.0   "docker-entrypoint.s…"   10 seconds ago   Up 8 seconds   0.0.0.0:8088->3306/tcp, :::8088->3306/tcp   serene_carson

$ ls -lth dbFile/
已经有很多东西了,这些文件都在宿主机上,不会随着docker的删除而消失。


可以尝试从网络中其他主机,连接宿主机的8088端口

(2) 使用 ubuntu 20.04
$ mysql -V
mysql  Ver 8.0.26-0ubuntu0.20.04.3 for Linux on x86_64 ((Ubuntu))

$ mysql -h 192.168.2.242 -P 8088 -uroot -p
Enter password: 
ERROR 2026 (HY000): SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol

$ telnet 192.168.2.242 8088 #可以访问。

搜了一下 
i) 方法1 
https://www.cnblogs.com/milton/p/12831113.html
修改  /etc/ssl/openssl.cnf  
找到  oid_section = new_oids 这行,并在其下 补充以下内容
openssl_conf = default_conf

[default_conf]
ssl_conf = ssl_sect
 
[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1
MaxProtocol = None
CipherString = DEFAULT:@SECLEVEL=1

重启ssh
$ sudo service ssh restart 
还是不行。



ii) 或者加上参数 --ssl-mode=disabled
https://stackoverflow.com/questions/61649764/mysql-error-2026-ssl-connection-error-ubuntu-20-04
$ mysql -h 192.168.2.242 -P 8088 --ssl-mode=disabled -uroot -p
mysql> use mysql 
mysql> select Host, User from user;
+-----------+-----------+
| Host      | User      |
+-----------+-----------+
| %         | root      |
| localhost | mysql.sys |
| localhost | root      |
+-----------+-----------+
3 rows in set (0.00 sec)




(3) 从windows登录呢?
G:\xampp\mysql\bin>mysql -V
mysql  Ver 15.1 Distrib 10.1.30-MariaDB, for Win32 (AMD64)

G:\xampp\mysql\bin>
G:\xampp\mysql\bin>mysql -h 192.168.2.242 -P 8088 -uroot -p
Enter password: ******
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.1-dmr MySQL Community Server (GPL)
MySQL [(none)]>

运行 docker 版的 Rstudio + Seurat

能找到 Rstudio 和 Seurat 的官方镜像。如何一起使用呢?

本测试使用的宿主机是一个硬件厉害的 centOS7 主机。

1.参考 Seurat 官网 install 目录下最后几行。
https://satijalab.org/seurat/articles/install.html
https://hub.docker.com/r/satijalab/seurat

We provide docker images for Seurat via dockerhub.
rstudio 专栏: https://environments.rstudio.com/docker

(1)To pull the latest image from the command line:
docker pull satijalab/seurat:latest

查看可用标签
https://hub.docker.com/r/satijalab/seurat
最新式 4.0.3
$ docker pull satijalab/seurat:4.0.3

$ docker images
REPOSITORY                   TAG             IMAGE ID       CREATED        SIZE
satijalab/seurat             4.0.3           8852687a5c21   3 months ago   3.61GB

2) 如果拉取失败,只能让其他人拉取后打包发过来,再导入。
$ docker save -o seurat_4.0.3.tar satijalab/seurat:4.0.3
另一台导入主机
$ docker load < seurat_4.0.3.tar

(2)To use as a base image in a new Dockerfile:
FROM satijalab/seurat:latest



2. 运行 
(1) 运行
$ docker run -d -p 5000:8787 satijalab/seurat:4.0.0 bash #这个是测试语句,无法实际使用。

要保证端口没被占用: -p 20180:80
保证文件夹挂载合理: /home/wangjl/data/dockerHome/

$ docker run -it -d \
--mount type=bind,source=/home/wangjl/data/dockerHome/,target=/work/ \
--workdir=/work/ \
-p 20180:80 \
satijalab/seurat:4.0.3 bash

为了防止停止时数据被删除,最好不适用--rm参数。

就是个黑窗口。$ R 打开R。
只能在黑窗口下使用,不知道怎么用Rstudio。

加 GUI 有2个思路
- 在这个 seurat 镜像中安装一个 rstudio。尝试失败。
- 在 rstudio 镜像中安装 seurat 包。测试成功。


(2)貌似没有 Rstudio,安装进去?
$ docker exec -it 071a bash
# apt-get update
# apt-get install gdebi-core
# wget https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.4.1717-amd64.deb
# gdebi rstudio-server-1.4.1717-amd64.deb

## 检查效果
# service rstudio-server restart
没有效果,搞不定。
可能是没有export 端口?


2)不过里面的包可以搞出来
容器内部
# ls /usr/local/lib/R/site-library/ |wc
    219     219    1850

容器外
$ mkdir /data/rawdata/R4.1_pkgs
$ docker cp 36db:/usr/local/lib/R/site-library/ /data/rawdata/R4.1_pkgs/

这些包大概有1G
$ du -sh /data/rawdata/R4.1_pkgs/site-library/
1.1G	/data/rawdata/R4.1_pkgs/site-library/

复制完包,这个容器就可以关闭/删除了。
$ docker stop 071a


3. 尝试从头建立镜像
(1) 文件准备
- 拉基础镜像 satijalab/seurat:4.0.3
- 准备配置文件
$ cat Dockerfile 
FROM satijalab/seurat:4.0.3
RUN apt-get update && apt-get install gdebi-core -y
# ADD rstudio-server-1.4.1717-amd64.deb /home/
# RUN cd /home && gdebi rstudio-server-1.4.1717-amd64.deb && rm rstudio-server-1.4.1717-amd64.deb
EXPOSE 8787
CMD ["bash"]

(2) 开始构建
由于 gdebi 命令后需要输入 y,不知道怎么实现,只好采用注释掉,进容器安装后,采用 commit 提交修改。

$ docker build -t r1 ./

安装rstudio
$ docker run --rm -it -v /home/wangjl/data/dockerHome:/home/work --workdir=/home/work/ r1 bash
root@906918b93347:/home/work# gdebi rstudio-server-1.4.1717-amd64.deb
y

新建用户
# usr=rstudio
# useradd -s /bin/bash -d /home/${usr} -m ${usr}
# passwd rstudio #密码是123456
加入sudo
# echo "rstudio ALL=(ALL:ALL) ALL" >> /etc/sudoers

提交修改 
$ docker ps # 查看容器id
把该容器提交为新镜像
$ docker commit -m 'Seurat4.0.3+Rstudio' 002 r2
r2                       latest              0f9887056dfc        4 seconds ago       4.53GB

加上可见意义的标签
$ docker tag r2 dawneve/seurat:4.0.3a

dawneve/seurat           4.0.3a              0f9887056dfc        7 minutes ago       4.53GB



(3) 启动,加上文件映射
$ docker run --rm -it -d -p 20180:8787 \
--mount type=bind,source=/home/wangjl/data/dockerHome/,target=/home/rstudio/data/ \
--workdir=/home/rstudio/ \
dawneve/seurat:4.0.3a

进入容器内部
$ docker exec -it 208 bash

重启rstudio
# service rstudio-server restart
# exit
exit


浏览器登陆
http://172.18.5.193:20180/
用户 rstudio
密码 123456
注: 该用户有sudo权限

注意:
- 宿主机文件夹 /home/wangjl/data/dockerHome/ 是和容器共享数据的,要设置 other 可写。
- 容器内仅 /home/rstudio/data/ 数据会保留,其他数据都会随着容器的停止而被删除。










4. 换个思路,从Rstudio 基础镜像,安装 Seurat包
https://hub.docker.com/r/rocker/rstudio/tags?page=1&ordering=last_updated
https://github.com/rocker-org/rocker-versioned2/issues

(1) 最新版是 4.1.1
$ docker pull rocker/rstudio:4.1.1

$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
rocker/rstudio           4.1.1               1878e29db52f        13 days ago         1.93GB

(2) 改造
文件映射,直接说使用刚才Seurat镜像的R包。

$ docker run --rm -it -p 20181:8787 \
--mount type=bind,source=/data/rawdata/R4.1_pkgs/site-library/,target=/usr/local/lib/R/site-library/ \
--mount type=bind,source=/home/wangjl/data/dockerHome/,target=/home/rstudio/data/ \
--workdir=/home/rstudio/ \
-e PASSWORD=123 rocker/rstudio:4.1.1


还需要进入容器,安装几个包。
$ docker exec -it xxx bash
# apt update
# apt install libxml2 -y
# apt install libglpk-dev -y #比较大
## 可以载入 Seurat 了。
# apt install libxt-dev -y #解决一个warning


把该容器提交为新镜像
$ docker commit -m 'Rstudio with Seurat4.0.3' 21d dawneve/seurat:4.0.3b

$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
dawneve/seurat           4.0.3b              7b5fc23f2fd7        2 hours ago         2.1GB
这个小,是因为R包在宿主机上有单独的文件夹,不统计大小。


(4) 使用该新镜像,启动容器 
$ docker run --rm -it -d -p 20180:8787 \
--mount type=bind,source=/data/rawdata/R4.1_pkgs/site-library/,target=/usr/local/lib/R/site-library/ \
--mount type=bind,source=/home/wangjl/data/dockerHome/,target=/home/rstudio/data/ \
--workdir=/home/rstudio/ \
-e PASSWORD=yourpasswordhere dawneve/seurat:4.0.3b

注意2点:
- 不能覆盖掉 /home/rstudio/ 文件夹,因为很多缓存文件在里面。
- 要在容器外,对可写文件夹设置可写操作 $ chmod 777 dockerHome/
- 容器内,除了 /home/rstudio/data/ 能保留,其他都会随着 docker 的停止而销毁!

打开浏览器 http://172.18.5.193:20180/
用户名: rstudio
密码: yourpasswordhere
-e DISABLE_AUTH=true \ 加上这一句就不用输入密码了,不过不安全。

3.3. 查看、暂停和启动容器

(1) 查看容器状态
$ docker ps     #查看正在运行的容器
$ docker ps -a  #查看所有容器,再运行的和已经停止的

查看容器资源使用情况
$ docker stats 5911b


(2)停止、重新运行、删除一个容器
$ docker stop bfd0 #停止一个容器,根据容器 id
$ docker start bfd0 #开始一个停止的容器
$ docker rm bfd0 #删除一个容器。默认并不会删除运行中的容器,需要先stop

重命名容器
$ docker rename CONTAINER NEW_NAME


(3) 有关容器和镜像的底层信息
i) 镜像
$ docker inspect nginx:latest
可以查看:
	容器实例的IP地址端口绑定列表
	特定端口映射的搜索
	收集配置的详细信息

ii) 查看容器的ip
$ docker inspect 5911b
...
"IPAddress": "172.17.0.3",
...
此时通过curl命令可以访问到,而浏览器则访问不到
$ curl 172.17.0.3  #内容略。
$ ping 172.17.0.3 #ttl=64 time=0.070 ms



(4) 进入容器内部
$ docker container exec -it container_id /bin/bash  进入到容器

$ docker exec -it 2ad bash
root@2ad472e991ba:/# exit #退出(docker容器中的命令,键入此命令,退回宿主机命令行)


(5) exec 向正在运行的容器下发命令
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Options:
    -d, --detach 在后台运行命令
    -e, --env list 设置环境变量
    -i, --interactive 以交互模式运行
    -t, --tty 分配一个伪终端
    -u, --user string 执行命令的用户
    -w, --workdir string 工作目录


(6) 显示容器日志
Usage: docker logs [OPTIONS] CONTAINER
Options:
  --details 显示详细日志
  -f, --follow 跟随日志输出
  --tail string 显示行数
  -t, --timestamps 显示时间戳

$ docker logs 5911b
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
...
192.168.2.199 - - [15/Sep/2021:01:50:06 +0000] "GET /outer.html HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36" "-"

3.4. 镜像的转移和运输

(1) 通过 tar 包导出、导入

打包镜像为tar包:
Usage:	docker save [OPTIONS] IMAGE [IMAGE...]
Save one or more images to a tar archive (streamed to STDOUT by default)

$ docker save -o ubuntu_14.04.tar ubuntu:14.04
$ docker save -o jdk_9.0.1.tar openjdk:9.0.1-11-slim

使用 scp 命令跨主机传输
$ scp wangjl@y.biomooc.com:/home/wangjl/data/test/testCWL/jdk_9.0.1.tar .

新主机从tar包载入镜像:
$ docker load < jdk_9.0.1.tar 
## 或者使用
$ cat jdk_9.0.1.tar| docker import - test/java:v9.0  #从tar包导入镜像,支持重命名镜像
## 其他形式 
  docker import "jdk_9.0.1.tar" test/java:v9.0
  docker import jdk_9.0.1.tar test/java:v9.0 #推荐



(2) 还可以gz压缩
宿主机
$ docker save myRstudioIMG | gzip -c > myRstudioIMG.tar.gz

myRstudioIMG.tar.gz 传输到新机器上,再在新机器上导入gz包
$ gunzip -c myRstudioIMG.tar.gz | docker load



(3) 推送镜像到仓库 docker hub 
之前我们一直从docker hub下载镜像,如果有本地制作好的docker,也可以上传。
前提是要注册一个docker账号: https://www.docker.com/

$ docker login -u 用户名
输入密码

登录后就可以推送了,只能推送到自己的仓库:https://hub.docker.com/u/dawneve
$ docker push dawneve/mysql
The push refers to a repository [docker.io/dawneve/mysql]




(4) export 将容器文件系统导出为一个tar包
Usage:	docker export [OPTIONS] CONTAINER
Export a container's filesystem as a tar archive
Options:
    -o, --output string tar包名称

(5) import 从压缩文件导入文件系统的镜像
Usage:	docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
Import the contents from a tarball to create a filesystem image

3.5. 宿主机和容器间文件的复制

i) 从容器内复制文件到宿主机指定的路径上
$ docker cp container:path hostpath

给nginx容器内放网页文件
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                               NAMES
5911b4776952   nginx     "/docker-entrypoint.…"   20 minutes ago   Up 20 minutes   0.0.0.0:91->80/tcp, :::91->80/tcp   mynginx2
$ docker exec -it 591 bash
root@5911b4776952:/# cat /etc/nginx/conf.d/default.conf  | grep root
        root   /usr/share/nginx/html;
root@5911b4776952:/# exit
exit

$ echo "this is out.html" > outer.html
$ docker cp outer.html 5911:/usr/share/nginx/html/
http://192.168.2.242:91/outer.html 能看到内容:this is out.html


ii) 从容器中复制文件到外面:
$ docker cp 5911:/usr/share/nginx/html/50x.html .
$ wc 50x.html
 19  68 497 50x.html

3.6. 镜像的制作

制作docker镜像主要包括 docker commit 方法和 Dockerfile 两种方法。对于运行的容器,如果有文件有变动,则可以使用commit命令提交为新的镜像。 推荐用Dockerfile文件建立镜像,因为该方法更可重复。下一章讲解 Dockerfile 法。

这里主要介绍前者。

commit制作镜像: 基于容器来制作image
docker build: dockerfile 制作镜像 
tar包制作镜像: import 基于容器来制作image

1. 在已有的docker镜像上提交修改。
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                               NAMES
5911b4776952   nginx     "/docker-entrypoint.…"   13 hours ago     Up 13 hours     0.0.0.0:91->80/tcp, :::91->80/tcp   mynginx2

如果一个容器,我们复制进去一个文件,该变动可以提交为一个新镜像。
$ docker cp outer.html 5911:/usr/share/nginx/html/
示例命令:docker commit -m '注释' 容器id 自定义镜像名
$ docker commit -m 'a new nginx container with outer.html' 5911 dawneve/static_web:addOuterFile
sha256:8899f25a8afd1992e4fbb29c368886c45e9ca5d955a07dab8c19811233315722

$ docker images
REPOSITORY           TAG             IMAGE ID       CREATED          SIZE
dawneve/static_web   addOuterFile    8899f25a8afd   18 seconds ago   133MB
nginx                latest          ad4c705f24d3   5 days ago       133MB

该镜像运行,可以打包成tar文件,可以推送到远端docker hub。

4. Dockerfile 制作docker镜像

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。每一行就是声明镜像的每层内容。

通过docker history nginx 可以看到一层层的内容。

编写的注意事项:

#——表示注释 第一个非注释行一定要是FROM指令
编写dockerfile必须在一个目录下进行,这个目录就是dockerfile的工作目录
Dockerfile的文件名的首字母必须是大写:Dockerfile
制作镜像所要使用的文件必须放在工作目录之下,或者工作目录的子目录下面,不能放到其他目录之中。
基于dockerfile制作镜像本质上还是基于现有镜像去制作镜像。
可以通过隐藏文件.dockerignore来指定不要放到镜像中的文件,一行是一个文件。

4.1. 使用 Dockerfile 构建一个镜像

$ docker build [OPTIONS] PATH | URL | -
--rm   Remove intermediate containers after a successful build (default true)
	--rm=true 表示构建成功后,移除所有中间容器
--no-cache  Do not use cache when building the image
	–no-cache=false表示在构建过程中不使用缓存


$ vim Dockerfile 
FROM ubuntu:20.04
RUN apt-get update
ADD run.sh /
VOLUME /data
CMD ["./run.sh"]

# FROM 命令是 Dockerfile 中唯一不可缺少的命令,它为最终构建出的镜像设定了一个基础镜像(base image)。

准备脚本
$ vim run.sh
echo "run now..."


开始构建
$ docker build -t="my_new_image" ./
Sending build context to Docker daemon  5.632kB
Step 1/5 : FROM ubuntu:20.04  # 第一步 下载ubuntu:20.04
20.04: Pulling from library/ubuntu
Digest: sha256:9d6a8699fb5c9c39cf08a0871bd6219f0400981c570894cd8cbea30d3424a31f
Status: Downloaded newer image for ubuntu:20.04
 ---> fb52e22af1b0
Step 2/5 : RUN apt-get update #第二步 更新系统源
 ---> Running in 4989d5ad814f
Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
...
Get:18 http://archive.ubuntu.com/ubuntu focal-backports/main amd64 Packages [2668 B]
Fetched 19.1 MB in 26s (742 kB/s)
Reading package lists...
Removing intermediate container 4989d5ad814f
 ---> a814eb9f4bf0
Step 3/5 : ADD run.sh / # 第三步 复制宿主机文件到镜像内 / 下。
 ---> fea27e209490
Step 4/5 : VOLUME /data  # 第四步 添加目录 
 ---> Running in 9ca454a8e5eb
Removing intermediate container 9ca454a8e5eb
 ---> 02feb24f1a08
Step 5/5 : CMD ["./run.sh"]  # 第五步 执行该脚本
 ---> Running in 082ca5572878
Removing intermediate container 082ca5572878
 ---> af246f32db94
Successfully built af246f32db94
Successfully tagged my_new_image:latest #最后添加标签

检查产生的镜像
$ docker images
REPOSITORY           TAG             IMAGE ID       CREATED          SIZE
my_new_image         latest          af246f32db94   48 seconds ago   103MB

$ docker run --rm -it my_new_image bash
root@80f829d85f8a:/# bash run.sh
run now..
root@80f829d85f8a:/# exit
exit


看第二列的时间,Docker Daemon 新创建了 4 层镜像,除了 FROM 命令,其余的 RUN、ADD、VOLUME 以及 CMD 命令都会创建一层新的镜像。
$ docker history my_new_image
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
af246f32db94   8 minutes ago   /bin/sh -c #(nop)  CMD ["./run.sh"]             0B        
02feb24f1a08   8 minutes ago   /bin/sh -c #(nop)  VOLUME [/data]               0B        
fea27e209490   8 minutes ago   /bin/sh -c #(nop) ADD file:08a4b2c0a3a1ce10d…   18B       
a814eb9f4bf0   8 minutes ago   /bin/sh -c apt-get update                       30.4MB    
fb52e22af1b0   2 weeks ago     /bin/sh -c #(nop)  CMD ["bash"]                 0B        
<missing>      2 weeks ago     /bin/sh -c #(nop) ADD file:d2abf27fe2e8b0b5f…   72.8MB

书写 Dockerfile 时,应该将更多静态的安装、配置命令尽可能地放在 Dockerfile 的较前位置。

实例2: 构建一个带 gcc 编译器的docker,顺便带上 gdb
最佳实践
- Require 明确:需要什么镜像
- 步骤精简:变化较少的 Step 优先
- 版本明确:镜像命名明确
- 说明文档:整个镜像打包步骤可以重现
$ cat Dockerfile
FROM ubuntu:20.04
WORKDIR /app
RUN apt-get update && apt-get install gcc gdb -y

注意:RUN 命令不更新不让安装包
$ docker build -t gcc ./
...
Successfully built ca894de5b7c5
Successfully tagged gcc:latest

查看生成的镜像
$ docker images
REPOSITORY    TAG        IMAGE ID       CREATED          SIZE
gcc           latest     ca894de5b7c5   28 minutes ago   399MB
x             x          88d6f843beb8   3 minutes ago    233MB #没有 gdb时

查看版本
$ docker run --rm -it gcc:latest gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

$ docker run --rm -it -v /home/wangjl/test:/app --workdir=/app gcc:latest gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0


编写一个c程序
$ cat hello.c 
#include <stdio.h>
int main(){
  printf("hello, world! from c\n");
}


编译: docker内编译
$ docker run --rm -it -v /home/wangjl/test:/app --workdir=/app gcc:latest gcc hello.c

运行:直接在宿主机运行二进制文件
$ ./a.out 
hello, world! from c

4.2. 知名 Dockerfile 及最佳实践

上文用到的 nginx 镜像的构建文件:
https://github.com/nginxinc/docker-nginx/blob/d496baf859613adfe391ca8e7615cc7ec7966621/mainline/debian/Dockerfile

上文用到的 mysql 镜像的构建文件:
https://github.com/docker-library/mysql/blob/dc60c4b80f3eb5b7ef8b9ae09f16f6fab7a2fbf5/8.0/Dockerfile


Best practices for writing Dockerfiles
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

Dockerfile reference:https://docs.docker.com/engine/reference/builder/

4.3. Dockerfile 命令详解

常用的是11个命令。

1.ARG
设置参数,该参数值可以从 --build-arg <varname>=<value> 接收值

在FROM之前的ARG参数只在FROM语句中生效,若在FROM之后想要继续使用ARG,需要再次设置

Usage:
  ARG <name>[=<default value>]
例:ARG version="1.0.0"
FROM
指定基础镜像,FROM必须为Dockerfile非注释行的第一行。

Usage:
  FROM <image>
  FROM <image>:<tag>
  FROM <image>@<digest>
例:FROM ubuntu:14.04
LABEL
设置镜像标签

Usage:
  LABEL <key>=<value> <key>=<value> <key>=<value> ...
例:LABEL maintainer="demo@demo.com"
例:LABEL version="1.0" description="这是一个web应用"
ENV
设置环境变量

建议:不论用哪种书写方式,在实际使用中,一行都只写一个环境变量,方便阅读。
特别地:在使用docker run命令添加参数 --env 时,若有相同的环境变量,以run命令为准。
Usage:
  ENV <key> <value>
  ENV <key>=<value> ...
ENV <key> <value>此方法一次只能设置一个
ENV <key>=<value> ... 该方法一次可以设置多个环境变量
例:ENV JAVA_HOME=/home/jdk-8
ADD
将主机构建环境(上下文)目录中的文件和目录、或URL标记的文件 拷贝到镜像中。
Usage:
  ADD [--chown=<user>:<group>] <src>... <dest>
  ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
例:ADD ${WORKSPACE}/target /usr/local/tomcat/webapps/ROOT
ADD t2.txt.gz /app/ #txt.gz 不会被解压
ADD all.tar /app/  #tar 会被解压
ADD txt.tar.gz /app/ #.tar.gz 会被解压
ADD http://www.baidu.com/index.html B1.html #支持url下载
ADD data1  data1 #支持目录拷贝
COPY
添加文件,将宿主机的文件添加到镜像中。

Usage:
  COPY [--chown=<user>:<group>] <src>... <dest>
  COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
USER
指定运行容器的用户

Usage:
  USER <user>[:<group>]
  USER <UID>[:<GID>]
例:USER root
WORKDIR
工作目录,进入容器后,以 WORKDIR 为当前路径
设置工作目录后 ,RUN、CMD、COPY、ADD 的工作目录都会同步变更

Usage:
  WORKDIR /workdir
例:WORKDIR /home
WORKDIR /app/test
EXPOSE
说明容器暴露的端口,默认协议为 tcp ,若是 udp 协议,则需要在后面添加 udp ,如 80/udp

Usage:
  EXPOSE <port> [<port>/<protocol>...]
例:EXPOSE 8080,表明容器在运行时提供8080端口,在启动该容器时需端口映射。
VOLUME
设置挂载点,将容器内的路径挂载到宿主机,该挂载方式是将容器内的路径挂载到docker数据路径下

Usage: 
  VOLUME <url>
例:VOLUME /var/log # 指定容器中需要被挂载的目录,会把这个目录映射到宿主机的一个随机目录上,实现数据的持久化和同步。

VOLUME ["/var/log", "/var/test" …..] 指定容器中多个需要被挂载的目录,会把这些目录映射到宿主机的多个随机目录上,实现数据的持久化和同步

VOLUME /var/data var/log 指定容器中的 var/log 目录挂载到宿主机上的 /var/data 目录,这种形式可以手动指定宿主机上的目录
RUN
执行命令并创建新的镜像层,通常用于更新或安装软件。
Usage:
  RUN <command>
  RUN ["executable", "param1", "param2"]
例:RUN yum -y install git
CMD
设置容器启动后默认执行的命令,CMD命令会被docker run的参数覆盖。

Usage:
  CMD <command>
  CMD ["executable","param1","param2"]
例:CMD systemclt start docker.service //启动容器时启动docker服务
ENTRYPOINT
和CMD一样,设置容器启动后默认执行的命令,但是该命令不会被docker run覆盖,会始终执行,CMD会被docker run传入的命令覆盖。

Usage:
  ENTRYPOINT <command>
  ENTRYPOINT ["executable", "param1", "param2"]
例:ENTRYPOINT /usr/local/apache-tomcat-8.5.33/bin/startup.sh
Demo
FROM tomcat:9.0
LABEL maintainer="demo@demo.com"
ADD ${WORKSPACE}/target/cip-file-1.0.0-SNAPSHOT /opt/apache-tomcat- 9.0.12/webapps/file
ENV LC_ALL en_US.UTF-8 EXPOSE 8080
ENTRYPOINT /opt/apache-tomcat-9.0.12/bin/startup.sh && tail -f /opt/apache- tomcat-9.0.12/logs/catalina.out

5. Singularity 简介

Singularity是劳伦斯伯克利国家实验室专门为大规模、跨节点HPC和DL工作负载而开发的容器化技术。具备轻量级、快速部署、方便迁移等诸多优势,且支持从Docker镜像格式转换为Singularity镜像格式。

https://sylabs.io/docs/
https://sylabs.io/guides/3.8/user-guide/
http://hpc.pku.edu.cn/_book/guide/soft_env/Singularity.html

https://github.com/hpcng/singularity
https://singularity.hpcng.org/

5.1 安装 singularity

使用虚拟机中的 ubuntu20.04 再次安装
$ wget https://github.com/sylabs/singularity/releases/download/v3.8.3/singularity-ce-3.8.3.tar.gz
-rw-rw-r--  1 wangjl wangjl 8.1M Sep  2 01:25 singularity-ce-3.8.3.tar.gz

$ tar zxvf singularity-ce-3.8.3.tar.gz 
$ cd singularity-ce-3.8.3/
$ vim README.md 
安装请看 INSTALL.md
$ vim INSTALL.md


(1)安装开发工具和包
$ sudo apt-get update
$ sudo apt-get install -y build-essential \
  libseccomp-dev pkg-config squashfs-tools cryptsetup

The following packages have unmet dependencies:
 build-essential : Depends: libc6-dev but it is not going to be installed or
                            libc-dev
                   Depends: g++ (>= 4:7.2) but it is not going to be installed
                   Depends: dpkg-dev (>= 1.17.11) but it is not going to be installed
 libseccomp-dev : Depends: libseccomp2 (= 2.5.1-1ubuntu1~18.04.1) but 2.5.1-1ubuntu1~20.04.1 is to be installed
E: Unable to correct problems, you have held broken packages.  ##这是个啥?

出错:
虚拟机恢复Ubuntu20.04到出厂设置,使用原版源安装一切正常。
莫名其妙... 可能是源在同步中?之前的源是tsinghua,现在是默认cn.ubuntu源。

(2) 安装Go语言 install and configure golang: https://golang.org/doc/install
$ sudo apt install golang-go

$ which go
/usr/bin/go

$ go version
go version go1.13.8 linux/amd64

(3) 回去安装
$ cd singularity-ce-3.8.3/
$ ./mconfig && \
  cd ./builddir && \
  make  ##特别耗时,30min?
$ sudo make install #也耗时很久

(4) 检查版本号
$ singularity --version
singularity-ce version 3.8.3

表示安装成功。

(5) 查看帮助
$ singularity
$ singularity --help

5.2 singularity 实例: 使用 samtools/ ubuntu/ mysql /java 的sig镜像

实例1: 使用 samtools 的镜像查看bam文件、并转为sam文件

生信常用的镜像
https://singularityhub.github.io/singularity-hpc/

https://github.com/pscedu/singularity-bowtie2
https://github.com/pscedu/singularity-samtools
https://singularityhub.github.io/singularity-hpc/r/biocontainers-samtools/
1. 命令行搜索
$ singularity search samtools
Found 15 container images for amd64 matching "samtools":

	library://alesr13/default/samtools_bwa_picard:v0.1
		Signed by: 6e23de6eac622dd1d96f304af7b50e57f44404e7

	library://btmiller/default/testimg-bowtie2-samtools:latest

	library://daanjg98/rnaseq/samtools:1.11


2. 从搜索结果中选一个靠谱的,建立sif镜像
$ TMPDIR=$PWD singularity build samtools.sif library://daanjg98/rnaseq/samtools:1.11
INFO:    Build complete: samtools.sif
-rwxr-xr-x 1 wangjl wangjl 170M Sep 16 17:43 samtools.sif

查看版本号:
$ singularity exec samtools.sif samtools --version
samtools 1.11
Using htslib 1.11
Copyright (C) 2020 Genome Research Ltd.

据说还能这样执行:
$ ./samtools.sif samtools --version
samtools 1.11
Using htslib 1.11
Copyright (C) 2020 Genome Research Ltd.



3. 拷贝过来一个 bam 文件 

不认识其他目录,只认识 $HOME 目录。
$ ls /data
lost+found  wangjl

$ singularity exec samtools.sif bash
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Singularity> cd /data
bash: cd: /data: No such file or directory
Singularity> ls /|grep data

Singularity> cd ~/test
Singularity> samtools view cgm-90.sort.bam | wc
  57814  994108 22848219
Singularity> exit
exit


4. 查看bam,并把 bam 转 sam
容器启动时所在的工作目录
$ singularity exec samtools.sif pwd 
/home/wangjl

(1) 查看bam文件
完整运行模式: 要使用完整路径:
$ singularity exec samtools.sif samtools view ~/test/cgm-90.sort.bam | wc -l #57814

简化运行,就像一个local命令一样
$ ./samtools.sif samtools view ~/test/cgm-90.sort.bam | wc -l  #57814


$ ./samtools.sif samtools view ~/test/cgm-90.sort.bam | head -n 2
A00582:646:H7CJTDSX2:4:1603:3884:9627	147	chr1	10000	0	53S91M	=	10004	-87	AGATCTAGACTATCCTCTTCGTCGGCAGAGTAAGATGTTTATAAGAGACAGACATAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCC	F:FF,FF,:,F,F::FFFFFFF,,:,,:,:F,F:FF:F,F:F,,F,F,,F:,F,FF:FF:FF::F,:F::FFFFFF,FFFFF,FF:F,F::FFF,F:FFF:::F:FFFF:::F:::FFFFFFF:::FFF::FFFFFFFFFFFFF	NM:i:0	MD:Z:91	MC:Z:93M	AS:i:91	XS:i:90	RG:Z:cgm-90
A00582:646:H7CJTDSX2:4:1603:3884:9627	99	chr1	10004	0	93M	=	10000	87	CCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCC	FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FF:FFFFF:FFFFFFFFFFF,FFFFF:FFFFF,F,,F,	NM:i:0	MD:Z:93	MC:Z:53S91M	AS:i:93	XS:i:93	RG:Z:cgm-90
wangj



(2) bam to sam
$ singularity exec -B /home/wangjl/test:/home/wangjl samtools.sif samtools view -h cgm-90.sort.bam -o xx.sam

$ ls -lth ~/test
total 24M
-rw-rw-r-- 1 wangjl wangjl  22M Sep 16 21:09 xx.sam
-rw-rw-r-- 1 wangjl wangjl 2.1M Sep 16 19:51 cgm-90.sort.bam

实例2: 一个很小的镜像 ubuntu:20.04

$ docker pull ubuntu:20.04

1. 生成 sif 镜像
有多种方法:

(1)获取预编译镜像 (会在当前目录下下载 ubuntu_tag.sif 文件)
$ singularity pull docker://ubuntu:20.04  #虽然docker下过,还要下载

(2)从 Docker Hub build 镜像
$ singularity -d build ubuntu.sif docker://ubuntu:20.04 #很多屏的输出,没看出是否下载

(3)从本地缓存生成 镜像
$ singularity build ubuntu_20.04-v2.sif docker-daemon://ubuntu:20.04

(4)从镜像tar文件开始
$ docker save ubuntu:20.04 -o ubuntu20.04.tar
-rw------- 1 wangjl wangjl  72M Sep 16 15:15 ubuntu20.04.tar

$ singularity build ubuntu_20.04-v3.sif docker-archive://ubuntu20.04.tar


结果4个文件 大小一样,md5互不一样。
-rwxr-xr-x 1 wangjl wangjl  27M Sep 16 10:55 ubuntu.sif
-rwxrwxr-x 1 wangjl wangjl  27M Sep 16 10:53 ubuntu_20.04.sif
-rwxr-xr-x 1 wangjl wangjl  27M Sep 16 15:10 ubuntu_20.04-v2.sif
-rwxr-xr-x 1 wangjl wangjl  27M Sep 16 15:16 ubuntu_20.04-v3.sif



2. 运行 
(1) 交互模式运行 singularity shell
  shell       Run a shell within a container

$ singularity shell ubuntu_20.04.sif
Singularity> pwd
/home/wangjl
Singularity> id
uid=1000(wangjl) gid=1000(wangjl) groups=1000(wangjl),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),134(docker)

Singularity> vim ReadMe 
bash: vim: command not found
Singularity> ping baidu.com
bash: ping: command not found
Singularity> exit
exit

怎么看着就是宿主机呢?目录、用户继承宿主机。
宿主机有命令ping、vim, 内部没有。
难道只是虚拟的命令环境,但是文件系统还是宿主机的?如果这样,就特别适合做 java/C的编译了!


(2) 执行一个命令并退出 singularity exec
  exec        Run a command within a container

$ singularity exec ubuntu.sif bash -c  "pwd && id" 
/usr/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
/home/wangjl
uid=1000(wangjl) gid=1000(wangjl) groups=1000(wangjl),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),134(docker)
$


(3) 运行一个容器 singularity run,感觉和 shell 一样?
  run         Run the user-defined default command within a container

使用run只执行一句,效果类似 exec
$ singularity run ~/data/ubuntu_20.04.sif echo "hello"
hello


如果不接命令,则和 shell 子命令一样,进入交互状态。
$ singularity run ubuntu.sif
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Singularity> pwd
/home/wangjl
Singularity> groups
wangjl adm cdrom sudo dip plugdev lpadmin lxd sambashare docker
Singularity> exit
exit
$ 


在内部写一个文件 01.txt
$ singularity run ubuntu.sif
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Singularity> echo "line1">01.txt
Singularity> exit
exit
$ cat 01.txt 
line1

$ ls -lth
total 184M
-rw-rw-r-- 1 wangjl wangjl    6 Sep 16 11:11 01.txt



(4) 后台运行容器实例,作为一个服务
  instance    Manage containers running as services

启动实例
$ singularity instance start ubuntu.sif test1
$ singularity instance start ubuntu.sif test2

查看实例:只有IP,没有端口列 (类似于docker ps)
$ singularity instance list
INSTANCE NAME    PID      IP    IMAGE
test1            62541          /home/wangjl/ubuntu.sif
test2            62581          /home/wangjl/ubuntu.sif

操作实例
可以通过 shell, exec, run 命令来连到容器中运行命令

使用 shell 命令连入容器
$ singularity shell instance://test1
Singularity> cat 01.txt 
line1
Singularity> exit
exit

使用 exec 执行命令
$ singularity exec instance://test2 cat 01.txt
line1

停止实例
$ singularity instance stop test1
$ singularity instance stop test2
关闭(并删除)容器,更像是 docker stop + docker rm,所以轻易不要stop,只用 exit 就行了。



(5) 绑定目录 -B 
在 Singularity 中也可以在 shell, run, instance.start 等命令中通过 “-B” 选项来实现 Docker 中 “-v” 选项提供挂载卷的功能,比如:
$ singularity shell -B /apps:/apps ubuntu.sif

挂在多个用逗号隔开:  -B /home/tom:/home/tom,/home/tom/dataset:/home/dataset \


$ singularity shell --help
Run a shell within a container

  -B, --bind strings           a user-bind path specification.  
		spec has the format src[:dest[:opts]], where src and dest are outside and inside paths.  路径内外的映射?
		If dest is not given, it is set equal to src. 
		Mount options ('opts') may be specified as 'ro' (read-only) or 'rw' (read/write, which is the default). Multiple bind paths can be given by a comma separated list.

$ singularity shell -B /tmp:/apps ubuntu.sif
Singularity> ls -lth /apps #虚拟的目录
total 48K
drwx------ 2 wangjl wangjl 4.0K Sep 16 11:11 tracker-extract-files.1000
Singularity> exit
exit

$ ls -lth /tmp #宿主机的目录
total 48K
drwx------ 2 wangjl wangjl 4.0K Sep 16 11:11 tracker-extract-files.1000


(6) --fakeroot 啥意思?在容器内有root权限 //todo
The --fakeroot option is available with the following singularity commands:
shell
exec
run
instance start
build

$ singularity exec --fakeroot docker://ubuntu:20.04 ls -lth
$ singularity exec --fakeroot ~/data/ubuntu_20.04.sif ls -lth
INFO:    Converting SIF file to temporary sandbox...
...
INFO:    Cleaning up image...

实例3: 运行一个试试 mysql 镜像 | ok,不支持端口映射

https://uh.edu/rcdc/resources/software/Singularity.php

1. Get the docker image and convert it to singularity image,
$ singularity pull docker://dawneve/mysql  #缺点:singularity 没法做端口映射,那就只能开一个
INFO:    Converting OCI blobs to SIF format
INFO:    Starting build...
Getting image source signatures
Copying blob d08a2c3112d4 done 
...
Writing manifest to image destination
Storing signatures
2021/09/16 10:13:20  info unpack layer: sha256:d08a2c3112d431c43e02458a285501d289333380438e760962619c8672bcbe0b
...
INFO:    Creating SIF file...

$ ls -lth
total 131M
-rwxrwxr-x 1 wangjl wangjl 131M Sep 16 10:17 mysql_latest.sif


2. To open an interactive shell inside the container,
$ singularity shell mysql_latest.sif
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Singularity> mysql --version
mysql  Ver 14.14 Distrib 5.7.18, for Linux (x86_64) using  EditLine wrapper
Singularity> exit
exit



3. To directly execute a command inside your container environment,
## singularity exec shub://singularityhub/ubuntu cat /etc/os-release

$ pwd 
/home/wangjl/data/dbFile

$ mkdir -p ${PWD}/mysql/var/lib/mysql ${PWD}/mysql/run/mysqld
$ singularity instance start --bind ${HOME} \
    --bind ${PWD}/mysql/var/lib/mysql/:/var/lib/mysql \
    --bind ${PWD}/mysql/run/mysqld:/run/mysqld \
    mysql_latest.sif mysql
## INFO:    instance started successfully

$ singularity instance list
INSTANCE NAME    PID     IP    IMAGE
mysql            3115          /home/wangjl/data/dbFile/mysql_latest.sif


$ singularity run instance://mysql #报错
error: database is uninitialized and password option is not specified 
  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD
这个报错怎么解决?
docker的-e参数,这里是什么?是 --env 
$ singularity run --env MYSQL_ROOT_PASSWORD=123456 instance://mysql




4.连接该数据库,并新建库和表
$ ifconfig #查宿主机的ip
192.168.2.242
不支持端口映射,那就是 3306 端口本身了。

从能访问宿主机的地方登陆mysql。
$ mysql -h 192.168.2.242 -P 3306 -uroot -p
mysql> create database wang;
mysql> use wang;
Database changed

CREATE TABLE `think_weibo` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `uid` int(20) NOT NULL,
  `content` text,
  `add_time` varchar(30) DEFAULT NULL,
  `cid` int(10) DEFAULT NULL,
  `archive` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into think_weibo values(1,101,"ctx1", "20210901",1, 0),(2,202,"ctx2", "20210902",2, 1);

mysql> select * from think_weibo;
+----+-----+---------+----------+------+---------+
| id | uid | content | add_time | cid  | archive |
+----+-----+---------+----------+------+---------+
|  1 | 101 | ctx1    | 20210901 |    1 |       0 |
|  2 | 202 | ctx2    | 20210902 |    2 |       1 |
+----+-----+---------+----------+------+---------+
2 rows in set (0.00 sec)

mysql> exit
Bye



5. 查看文件
$ ls -lth dbFile/mysql/var/lib/mysql/wang
total 112K
-rw-r----- 1 wangjl wangjl  96K Sep 16 16:32 think_weibo.ibd
-rw-r----- 1 wangjl wangjl 8.6K Sep 16 16:29 think_weibo.frm
-rw-r----- 1 wangjl wangjl   65 Sep 16 16:28 db.opt

6.退出该容器
$ singularity instance stop mysql
INFO:    Stopping mysql instance of /home/wangjl/data/dbFile/mysql_latest.sif (PID=3115)

实例4:运行一个 java 镜像

1. 如前教程,安装docker 
$ docker pull openjdk:9.0.1-11-slim

$ docker images
REPOSITORY   TAG             IMAGE ID       CREATED       SIZE
openjdk      9.0.1-11-slim   5149033ba93d   3 years ago   374MB

$ pwd
/home/wangjl/test

$ cat Hello.java
public class Hello { 
	public static void main(String args[])  {    
		System.out.println("Hello world, from Java!");  
	}
}

$ docker run --rm -v /home/wangjl/test:/test/ openjdk:9.0.1-11-slim bash -c 'cd /test/ && javac Hello.java && java Hello'
Hello world, from Java!


2. 构建镜像文件
(1)从本地缓存的镜像构建 sif 文件
$ singularity build openjava9-v1.sif docker-daemon://openjdk:9.0.1-11-slim
注意:从原来的docker变成了docker-daemon;必须加tag标签;

如果撑爆 /tmp 临时文件夹,则把临时文件夹改在当前文件夹内:
$ TMPDIR=$PWD singularity build openjdk9.sif docker-daemon://openjdk:9.0.1-11-slim
INFO:    Build complete: openjdk9.sif
-rwxr-xr-x 1 wangjl wangjl 174M Sep 16 22:46 openjdk9.sif

(2)从 tar 文件生成 sif 
$ docker save openjdk:9.0.1-11-slim -o jdk9.tar
$ singularity build openjdk9-v2.sif docker-archive://jdk9.tar

生成的文件大小一样,md5不同


3. 编译和运行
$ singularity run -B /home/wangjl/test:/app/ openjdk9.sif bash
Singularity> cd /app
Singularity> javac Hello.java
Singularity> java Hello 
Hello world, from Java!
Singularity> exit
exit


$ ls -lht #注意所有人和组的区别
total 12K
-rw-rw-r-- 1 wangjl wangjl 427 Sep 16 15:45 Hello.class   #在 Singularity 中生成的
-rw-r--r-- 1 root   root   427 Sep 16 14:58 Hello.class2  #在docker中生成的,改名2


(2) 或者执行一行命令
$ singularity exec -B /home/wangjl/test:/app/ --workdir=/app /data/wangjl/openjdk9.sif bash -c "cd /app && ls -lth"
/usr/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
total 12K
-rw-rw-r-- 1 wangjl wangjl 427 Sep 16 15:45 Hello.class
-rw-r--r-- 1 root   root   427 Sep 16 14:58 Hello.class2
-rw-rw-r-- 1 wangjl wangjl 134 Sep 16 14:57 Hello.java
可见,地址输出是对的。不过设置工作目录无效。

$ singularity exec -B /home/wangjl/test:/app/ /data/wangjl/openjdk9.sif bash -c "cd /app && javac Hello.java && java Hello"
/usr/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Hello world, from Java!

5.3 从定义文件构建 nginx 的sif镜像

1.定义文件
$ cat nginx.def
Bootstrap: docker
From: nginx
Includecmd: no

%startscript
   nginx

执行构建:
$ sudo singularity build nginx.sif nginx.def #必须使用root权限
-rwxr-xr-x 1 wangjl wangjl  50M Sep 16 16:54 nginx.sif


2. 测试,必须使用sudo权限
$ singularity instance start --writable-tmpfs nginx.sif web 
$ curl localhost
curl: (7) Failed to connect to localhost port 80: Connection refused
#网页不能访问,curl也不行,可能默认的80端口被封了?测试发现,加sudo就好了。

$ singularity instance list
INSTANCE NAME    PID     IP    IMAGE
web              3578          /home/wangjl/data/nginx.sif
$ singularity instance stop web




3. 修改配置文件,改端口,再次测试
查配置文件的位置
$ singularity shell nginx.sif 
Singularity> cat /etc/nginx/conf.d/default.conf | grep -i root
        root   /usr/share/nginx/html;
Singularity> exit
exit


自定义配置文件:
$ cat nginx.config 
server {
    listen       8008;
    listen  [::]:8008;
    server_name  localhost;
    #access_log  /var/log/nginx/host.access.log  main;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
		
        # list files
        autoindex on; 
        autoindex_exact_size on; 
        autoindex_localtime on; 
    }
}

不加sudo访问不了。可能是网络、端口是系统资源,必须特权用户才能调用。
$ sudo singularity instance start -B /home/wangjl/data/nginx.config:/etc/nginx/conf.d/default.conf --writable-tmpfs nginx.sif web 

$ sudo singularity instance list
INSTANCE NAME    PID     IP    IMAGE
web              4457          /data/wangjl/nginx.sif
$ sudo singularity instance stop web

6. Kubernetes (K8s)

容器化技术必将是云时代不可或缺的技能之一,而 Docker 只是沧海一粟。随之而来的还有集群容器管理 K8s、Service Mesh 、Istio 等技术。打开 Docker 的大门,不断抽丝剥茧,逐层深入,你将感受到容器化的无穷魅力。

Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications.

https://kubernetes.io/
https://www.kubernetes.org.cn/k8s

疑难杂症 / 参考资料

1.临时文件夹 /tmp 装不下怎么办?
$ TMPDIR=$PWD singularity build openjdk9-V2.sif docker-archive://jdk9.tar #能改变临时文件夹位置的命令


https://www.cnblogs.com/getbird/p/11650467.html
https://www.docker.org.cn/docker/docker-206.html