环境:ubuntu 16.04
网上docker文章都写的太复杂了,实际自己安装一次是入门最快的方法。
关键词简单理解
为了便于理解可以简单的把docker当成虚拟机(当然实际上这两个有很大区别):
系统Image镜像:相当于虚拟机的ISO文件。
容器Container:相当于虚拟机实例。
安装docker
安装:
sudo apt-get update
sudo curl -fsSL https://get.docker.com/ | sh
查看docker是否成功及版本:
docker -v
docker安装redis及启动等
先查找一下Docker上的redis镜像:
sudo docker search redis
可以看到有很多镜像,并且还有star评分。
拉取镜像:
sudo docker pull redis
查看本地镜像仓库:
sudo docker images
创建本地redis目录和下载配置文件redis.conf:
mkdir docker
cd docker
mkdir redis
cd redis
mkdir data
mkdir conf
cd conf
wget http://download.redis.io/redis-stable/redis.conf
cd ..
创建redis容器:
sudo docker run --name redis1 -d -p 6379:6379 -v $PWD/conf/redis.conf:/etc/redis.conf -v $PWD/data:/data redis redis-server /etc/redis.conf --appendonly yes
命令解析:
--name redis1 为容器指定一个名字redis1
-d 后台运行容器,并返回容器ID
-p 6379:6379 将主机(宿主)端口6379映射到容器端口6379
-v $PWD/conf/redis.conf:/etc/redis.conf 意思是把主机中当前目录下conf/redis.conf文件挂载到容器的/etc/redis.conf
-v $PWD/data:/data 意思是把主机中当前目录下data目录挂载到容器的/data目录
redis 就是镜像的名字
redis-server /etc/redis.conf --appendonly yes 这条命令就是redis的启动命令,加了--appendonly yes后redis才会数据持久化。
显示当前正在运行的容器:
sudo docker ps
启动redis-cli客户端连接到docker的redis中:
因为我本机没有安装redis所以没有redis-cli,但是刚运行的redis1容器中有redis-cli,所以可以在容器中启动redis-cli:
sudo docker exec -it redis1 redis-cli
参数it 的意思:
-i :即使没有附加也保持STDIN 打开
-t :分配一个伪终端
停止运行的容器:
sudo docker stop redis1
强制停止容器:
docker kill redis1
启动容器:
sudo docker start redis1
重启容器:
sudo docker restart redis1
查看所有容器实例,包括停止的:
sudo docker ps -a
删除容器,正在运行的不能删除:
sudo docker rm redis1
删除镜像:
sudo docker rmi redis
查看容器信息
sudo docker inspect redis1
进入容器命令行,并且查看容器的linux系统
sudo docker exec -it redis1 /bin/bash
查看linux内核版本,可以看到官方使用的是ubuntu做基础镜像:
cat /proc/version
uname -a
容器和宿主机互相复制文件
宿主机文件复制到容器:
sudo docker cp test.txt redis1:test.txt
容器文件复制到宿主机:
sudo docker cp redis1:test.text test.txt
创建镜像Dockerfile
一开始一直没明白为什么要使用基础镜像,动手做了Dockerfile之后明白了。
使用基础镜像启动的容器可以直接当做是个虚拟机操作系统来使用,而没有使用基础镜像的容器那么里面就只有你的程序了。
所以使用系统基础镜像的容器可以当做一个虚拟操作系统进去做任何事情,比如安装go,安装gcc,git自己的代码然后编译。
甚至可以在这个容器里再安装docker,然后pull基础系统镜像,然后再进去......盗梦空间,无限循环,哈哈哈~~~
如果你的宿主机为ubuntu,但是你的程序需要centos环境,这时使用centos基础镜像一样可以跑。
写一个简单的go程序,名字叫test:
package main
import (
"fmt"
"os"
)
func main(){
for i, args := range os.Args {
fmt.Println(i, args)
}
fmt.Println("Hello world.", len(os.Args))
}
在目录下创建一个名字叫Dockerfile
的文件:
依赖基础系统镜像:
FROM ubuntu
MAINTAINER wbt
RUN mkdir /work
COPY ./test /work
ENTRYPOINT ["/work/test", "wbttest"]
第一行如果改为FROM ubuntu:16.04
则表示指定版本的ubuntu。否则都是ubuntu latest最新版本。
使用基础系统镜像的话做出来的Image镜像非常大,因为包含了基础镜像的内容。
不依赖基础镜像:因为go程序是纯静态没有依赖动态库可以直接添加
FROM scratch
MAINTAINER wbt
#RUN mkdir /work
COPY ./test /
CMD ["/test", "wbttest"]
#ENTRYPOINT ["/work/test", "wbttest"]
RUN mkdir /work
这个需要注释掉是因为mkdir
这条命令现在没有了,而上面使用基础系统镜像的方式可以使用是因为基础镜像里有这条命令。
不依赖基础镜像的Image很小,跟原生的go程序差不多大。
CMD与ENTRYPOINT指令的区别主要在启动容器时覆盖命令和参数有所不同,CMD的命令和参数可以在run时被覆盖,ENTRYPOINT不会被覆盖。
编译Dockerfile:
不带版本号,最后
sudo docker build -t test .
带版本号1.0:
sudo docker build -t test:1.0 .
这个是scratch方式的镜像测试:
后面的/test param1 param2 param3是覆盖了原来Dockerfile指定的命令和参数。
加上--rm之后当容器退出时自动删除容器。
sudo docker run -it --name tt --rm test /test param1 param2 param3
输出:
0 /test
1 param1
2 param2
3 param3
Hello world. 4
查看logs:
sudo docker logs tt
启动加上-a
和-i
参数都可以回显输出:
sudo docker start -a tt
docker start 好像不能重写命令和参数。
如果要做镜像的程序是动态编译有依赖库那么需要把动态库在Dockerfile中指定打包进去。
下回写个c++程序再测试一下。
使用busybox做为基础镜像创建Dockerfile
因为scratch基础镜像是个空镜像,里面没有任何命令,如果需要做mkdir, wget等操作是没有办法的,这时可以选择busybox做为基础镜像,busybox包含了很多的unix基本工具:
Currently defined functions:
[, [[, acpid, add-shell, addgroup, adduser, adjtimex, ar, arch, arp,
arping, ash, awk, base64, basename, bc, beep, blkdiscard, blkid,
blockdev, bootchartd, brctl, bunzip2, bzcat, bzip2, cal, cat, chat,
chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt,
cksum, clear, cmp, comm, conspy, cp, cpio, crond, crontab, cryptpw,
cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod,
devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname,
dos2unix, dpkg, dpkg-deb, du, dumpkmap, dumpleases, echo, ed, egrep,
eject, env, envdir, envuidgid, ether-wake, expand, expr, factor,
fakeidentd, fallocate, false, fatattr, fbset, fbsplash, fdflush,
fdformat, fdisk, fgconsole, fgrep, find, findfs, flock, fold, free,
freeramdisk, fsck, fsck.minix, fsfreeze, fstrim, fsync, ftpd, ftpget,
ftpput, fuser, getopt, getty, grep, groups, gunzip, gzip, halt, hd,
hdparm, head, hexdump, hexedit, hostid, hostname, httpd, hush, hwclock,
i2cdetect, i2cdump, i2cget, i2cset, id, ifconfig, ifdown, ifenslave,
ifplugd, ifup, inetd, init, insmod, install, ionice, iostat, ip,
ipaddr, ipcalc, ipcrm, ipcs, iplink, ipneigh, iproute, iprule,
iptunnel, kbd_mode, kill, killall, killall5, klogd, last, less, link,
linux32, linux64, linuxrc, ln, loadfont, loadkmap, logger, login,
logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lsof,
lspci, lsscsi, lsusb, lzcat, lzma, lzop, makedevs, makemime, man,
md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo,
mkfs.ext2, mkfs.minix, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp,
modinfo, modprobe, more, mount, mountpoint, mpstat, mt, mv, nameif,
nanddump, nandwrite, nbd-client, nc, netstat, nice, nl, nmeter, nohup,
nologin, nproc, nsenter, nslookup, ntpd, nuke, od, openvt, partprobe,
passwd, paste, patch, pgrep, pidof, ping, ping6, pipe_progress,
pivot_root, pkill, pmap, popmaildir, poweroff, powertop, printenv,
printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdate, rdev,
readahead, readlink, readprofile, realpath, reboot, reformime,
remove-shell, renice, reset, resize, resume, rev, rm, rmdir, rmmod,
route, rpm, rpm2cpio, rtcwake, run-init, run-parts, runlevel, runsv,
runsvdir, rx, script, scriptreplay, sed, sendmail, seq, setarch,
setconsole, setfattr, setfont, setkeycodes, setlogcons, setpriv,
setserial, setsid, setuidgid, sh, sha1sum, sha256sum, sha3sum,
sha512sum, showkey, shred, shuf, slattach, sleep, smemcap, softlimit,
sort, split, ssl_client, start-stop-daemon, stat, strings, stty, su,
sulogin, sum, sv, svc, svlogd, svok, swapoff, swapon, switch_root,
sync, sysctl, syslogd, tac, tail, tar, taskset, tc, tcpsvd, tee,
telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr,
traceroute, traceroute6, true, truncate, tty, ttysize, tunctl,
ubiattach, ubidetach, ubimkvol, ubirename, ubirmvol, ubirsvol,
ubiupdatevol, udhcpc, udhcpd, udpsvd, uevent, umount, uname, unexpand,
uniq, unix2dos, unlink, unlzma, unshare, unxz, unzip, uptime, users,
usleep, uudecode, uuencode, vconfig, vi, vlock, volname, w, wall,
watch, watchdog, wc, wget, which, who, whoami, whois, xargs, xxd, xz,
xzcat, yes, zcat, zcip
busybox镜像非常小,只有1.2M。我们可以基于这个基础镜像来写Dockerfile:
FROM busybox
MAINTAINER wbt
RUN mkdir /work
COPY ./test /work
CMD ["/work/test", "wbttest"]
然后编译(如果你没有预先pull busybox的话docker会自动pull busybox镜像):
sudo docker build -t test2 .
运行:
sudo docker run -it --name test2 --rm test2 /work/test test2 param1 param2
甚至可以这样运行,返回busybox的命令提示:
sudo docker run -it --name test2 --rm test2 busybox
或者这样:
查看目录结构:
sudo docker run -it --name test2 --rm test2 ls
sudo docker run -it --name test2 --rm test2 ls /bin
执行这条可以看到我们自己的test程序:
sudo docker run -it --name test2 --rm test2 ls /work
修改容器启动时映射的端口
- 查看需要修改的容器,记住container id
sudo docker ps -a
- 停止容器
sudo docker stop xxx
- 修改容器的配置文件
如果下面出现权限不够,比如:cannot open directory '/var/lib/docker': Permission denied
,那先修改docker目录权限:sudo chmod -R 755 /var/lib/docker
。
sudo vim /var/lib/docker/containers/{container_id}/hostconfig.json
修改:
"PortBindings": {
"4000/tcp": [
{
"HostIp": "",
"HostPort": "4000"//宿主机ip
}
]
},
- 重启docker服务
sudo service docker restart
- 重启容器
参考文章:
docker建立最简单最小的helloworld镜像
Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较
基于Docker部署nodejs应用