介紹
有點像 VM 但又不全然是 VM, 最主要它跟一般 VM 的差別在於 VM 所需的 OS 及 bins/libs 是安裝在 VM 本身裡面的
但 docker 的 container 概念會共用 OS 及 bins/libs, 但他們仍是獨立的, 由 Docker Engine 管理
最大好處可以快速啟動環境並且設定好, 很適合用在自動化測試或開發上
基本觀念
名詞解釋
- image : 用來建立 container 的東西, 每一個 image 都只能讀且不能被改變
- container : 是由一個 image 建立的一個的正在執行中的環境, 可以把它想成一台主機
- 官方 registry : 就是 Docker Hub
Image
- 一個完整 image 的名稱是 : username/image_name:tag
- 一個 image 可以跑多個 container
- 可以 commit 這個 container 的變動製作新的 image
- image 想當於是 AWS EC2 的 AMI
Container
- 把它想像 AWS EC2 的 Instances 清單 (相當於
docker ps -a
)
- 每一個主機都是一個 container,有些是 stopped 有些是 running (相當於
docker ps
, 只列出 running container)
- 只要被 Stop 的主機 (相當於
docker stop
) 都是關機的狀態但還在列表上 (相當於還存在在 list 上 docker ps -a
)
- 被 Stop 的主機你可以再對它執行 Start (相當於
docker start
)
- 你可以對主機打成 image (相當於
docker commit
)
- 而被 Terminate 的主機就會被刪除,你無法在 Instances 上看到 (相當於
docker rm
後你無法在 docker ps -a
上再看到它)
- 比較不一樣的是,執行中的 container 可以是直接進入(就像執行 ssh 到主機一樣)也可以是背景執行
- 停止 container 不只是關機這麼簡單,連修改過的資料都會不見 (但可使用 mount 外部的檔案或資料夾來解決)
Quick start based on particular scenarios
ngrok: forward port 8080 from inside the Docker container to your local machine
docker run --rm -it -p 4040:4040 ngrok/ngrok http host.docker.internal:8080
- it’s not necessary and it also blocks the port you want to listen on your local machine
localhost:4040
is the dashboard of ngrok
Run the local daemon listening port 8080
Then open the domain that ngrok generates for you on browser, the request should be forwarded to local daemon
安裝
mac
可從官網下載
ubuntu
sudo apt-get update
sudo apt-get install docker.io
sudo service docker start
執行 sudo adduser <username> docker
, 將你加入到 docker group
windows
下載頁面
Exec : C:\Program Files\Boot2Docker for Windows\start.sh
putty / xshell connect to docker@127.0.0.1:2022
- 預設的 VM VirtualBox 就已經設定好 port forwarding 了, 預設是 127.0.0.1:2022
- Default account (docker / tcuser)
測試是否安裝成功
docker run hello-world
開始試玩
將 mysql 跑起來
Pull image mysql
from docker hub
docker pull mysql
Run container mysql
docker run -t -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql
Test
mysql -h 127.0.0.1 -u root -p
將 redis 跑起來
Pull Docker Hub 上的 redis image
docker pull redis
將 redis image 啟動成 container, 並且將 6379 port 接出來
docker run -t -d -p 6379:6379 redis:latest
測試用 redis-cli 是否可以連進去
將 ubuntu image 跑起來 (建立一個 container)
Pull Docker Hub 上的 ubuntu image
docker pull ubuntu
以 ubuntu image 建立一個 container
docker run -t -d ubuntu
看看執行中的 container 有哪些,你可以看到剛剛建立的 container
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bba4d449e053 ubuntu "/bin/bash" 52 seconds ago Up 51 seconds sharp_mestorf
Stop 後 docker ps
就看不到它了
docker stop bba4d449e053
但你顯示全部的 container 就看的到了
docker ps -a
你可以完全地刪除它
docker rm bba4d449e053
How can i run docker command inside a docker container?
在一台 container 裡面安裝 docker 啟動失敗:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
必須要 mount 外部的 docker socket 進去
docker run -td -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04
docker exec -it a1b6a80b0bb8 bash
or
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:latest sh -c "apt-get update ; apt-get install docker.io -y ; bash"
常用指令
build : 建立寫好的 Dockerfile
docker build -t jxxx/golang-env .
without cache
docker build --no-cache .
images : image list
docker images
rmi : 刪除 image
docker rmi eg_sshd
// 刪除全部 image
docker rmi $(docker images -q)
inspect : 查看此 container 的 info (不常用)
docker inspect f244dc51e1d9
ps : running container list (有哪些主機正在跑)
docker ps
-a
: 所有 container ,即使是被 stop 的都會列出來
-q
: 只顯示 CONTAINER ID
stop : 停掉在跑的 container (相當於關機)
docker stop {CONTAINER ID}
// 停掉全部在跑的 container
docker stop $(docker ps -a -q)
start : 將一個已經被 stop 的 container 再跑起來 (相當於開機)
docker start {CONTAINER ID}
rm : 刪除 container (相當於刪除這台主機)
docker rm {CONTAINER ID}
// 刪除全部 container
docker rm $(docker ps -a -q)
stop vs rm
- stop : 將這個 container 關機並保存它的狀態, 只能在
docker ps -a
看到它, 你可以對它 commit 製作新的 image
- stop : It sends SIGTERM first, then, after a grace period, SIGKILL.
- rm : 將這個 container 真的刪除, 無法在
docker ps -a
看到它
- rm : 你沒辦法 remove 一個 running container,必須先 stop 再 remove
start vs run
- start : 將一個被 stop 的 container 重新啟動,就像開機一樣
- run : 主機(container) 還沒建立,所以要將一個 image 建立一個 container
run
以 ubuntu image 建立 container 並進入到 container 裡面 (有點像 SSH 連進去,但 exit
後就是 stopped 的狀態了)
docker run -t -i ubuntu:14.04 /bin/bash
- 如果沒有指定版本,預設會用最新的
ubuntu:latest
- flag 可簡化為
-it
- bash 也可以指定絕對路徑
/bin/bash
背景執行 container
docker run -t -d ubuntu:14.04
在 container 裡執行指令
docker run jxxx/golang-env touch ff.md
本機與 container 所有的 port
docker run -d -P jxxx/golang-env
-P
flag to publish all of the exposed ports.
50080 port -> 80 port
docker run -d -p 50080:80 jxxx/golang-env
-p
flag to publish a range of ports
將 domain 對應到 image name
docker run -d --link my_mysql:db.example.com jxxx/golang-env
將目錄名稱 folder_src mount 到 container 家目錄下(?)
docker run -v `pwd`/folder_src:/home/test/into_container jxxx/golang-env ls -al into_container
Exec
進入正在執行中的container
docker exec -it <containerIdOrName> bash
Cp
將外部檔案複製到 container 裡
docker cp foo.txt {CONTAINER ID}:/foo.txt
將檔案從 container 裡複製出來
docker cp {CONTAINER ID}:/foo.txt ./
Commit
docker commit -m "Add ff.md" e3015 jxxx/golang-env
Push
docker push jxxx/golang-env
Attach
- docker attach [CONTAINER ID] : 進入 container
Logs
將 container 的標準輸出接出來
docker logs {CONTAINER ID}
在 Docker config 設定將 log 接到標準輸出
RUN ln -sf /dev/stdout /var/log/test.log
container
Remove the stopped containers
docker container prune
其他指令
expose
add/copy
volume
attach/detach
pause/unpause
link between containers
prune
docker container prune
docker image prune
docker volume prune
docker network prune
docker system prune -a
Docker machine command
docker-machine is used to connect to VirtualBox and create virtual machine
Show version
docker-machine version
Create machine
docker-machine create --driver virtualbox {name}
Show machine list
docker-machine ls
Show environment variables
docker-machine env {name}
SSH into machine
docker-machine ssh {name}
Start machine
docker-machine start {name}
Stop machine
docker-machine {name}
Remove machine
docker-machine {name}
Show machine status
docker-machine status {name}
Dockerfile 怎麼寫
FROM : 使用每個 image 為基礎
FROM ubuntu
FROM ubuntu:14.04
MAINTAINER : 設定產生的 imgage 的 Author
MAINTAINER test "xxxxxxx@gmail.com"
USER : 使用哪個 user 去執行 RUN
, CMD
etc.
USER daemon
RUN : 執行指令 (使用預設的 shell /bin/sh -c
)
RUN apt-get update && apt-get -y upgrade
CMD : 執行 command
CMD echo "This is a test." | wc - // 預設是使用 shell 執行 /bin/sh -c
CMD ["/usr/bin/wc","--help"] // 執行其他指令不使用 shell 的話要使用 JSON format
ENV : 設定環境變數
ENV HOME /root
WORKDIR 設定工作目錄 (for any RUN
, CMD
, ENTRYPOINT
, COPY
and ADD
in the Dockerfile)
WORKDIR /root
或
WORKDIR ${HOME}
ADD : 新增檔案
ADD xx.md /root
ADD xx.md $HOME
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # adds "test" to /absoluteDir/
COPY : 複製檔案
COPY \$HOME /tmp
COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/ # adds "test" to /absoluteDir/
EXPOSE : 開放哪些 Port
EXPOSE 80 443
HEALTHCHECK (https://docs.docker.com/engine/reference/builder/#/healthcheck)
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
SHELL : 更改預設的 shell (linux default : /bin/sh -c
)
SHELL ["executable", "parameters"]
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
docker-compose
To start and run the app
docker-compose up -d
Build/rebuild the service
docker-compose build
Build/rebuild the service and run the app
docker-compose up --build
As long as Dockerfile is changed, run docker build --no-cache .
first
Allow 2 services from different docker-compose config to connect the same service
For example, allow ‘worker’ and ‘api’ connect to the same db server
In order to achieve this, it needs to use docker network
Create the network first
docker network create mynetwork
docker-compose.yml
from repo 1
services:
db:
image: mysql:latest
networks:
- mynetwork
worker:
build: .
depends_on:
- db
networks:
- mynetwork
networks:
mynetwork:
external: true
docker-compose.yml
from repo 2
services:
api:
build: .
networks:
- mynetwork
networks:
mynetwork:
external: true
Now, these 2 services are able to connect to db (root:@tcp(db:3306)/mytable
)
mysql server and client login
docker-compose.yml
version: "3"
services:
app:
build: .
volumes:
- .:/usr/src/app
depends_on:
- db
db:
image: mysql:latest
restart: always
environment:
MYSQL_DATABASE: mydb
MYSQL_ALLOW_EMPTY_PASSWORD: yes
ports:
- "3306:3306"
volumes:
- ./mysql:/var/lib/mysql
networks:
- mynetwork
networks:
mynetwork:
Run
docker-compose up --build
Find the network name
docker network ls
NETWORK ID NAME DRIVER SCOPE
2e31601337aa myproject_mynetwork bridge local
Connect to mysql server
docker run -it --network myproject_mynetwork mysql:latest mysql -hdb -uroot -p
(no need to enter password)
config for mysql, redis and phpmyadmin
docker-compose.yml
version: "2"
services:
test-redis:
container_name: test-redis
image: redis
expose:
- "6379"
ports:
- "6379:6379"
volumes:
- redis-data-test:/data-test
test-mysql:
container_name: test-mysql
image: mysql/mysql-server:5.7
expose:
- "3306"
ports:
- "3306:3306"
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
volumes:
- dbdata-test:/var/lib/mysql
phpmyadmin:
container_name: phpmyadmin
image: phpmyadmin/phpmyadmin
links:
- test-mysql:db
ports:
- "8888:80"
volumes:
redis-data-test:
dbdata-test:
phpmyadmin: http://localhost:8888
Docker Swarm
A distributed system tool,used to manage a cluster of Docker engines, allows you to deploy your services.
Features
- Decentralised design: you can define its optimal state (number of replicas, network, ports, etc.)
- Scaling (easy to scale or shrink your service)
- Load Balancing (use overlay network)
- Security (nodes communicate with each other over TLS)
- Support roll-back
- built in docker
Concepts
- nodes & roles
- manager leader: manager nodes elec a single leader to conduct orchestration tasks
- manager
- deploy your application by assigning tasks
- maintain the desired state of the swarm
- manager node is also a worker node by default, you can exclude it
- worker
- execute tasks
- report current state of its assigned tasks to manager
- one or more nodes can run on a server
- a node can be both of a manger and a worker
- services & tasks
- a service: the definition of the tasks e.g. 1 service = 3 nginx replicas = 3 task
- a task: a task is a running container on available node e.g. 1 task = 1 nginx
- docker swarm use VXLAN (Virtual extensible LAN) to connect each workers just like in the LAN.
- network
- ingress: Exposes services to the external network.
- overlay network manages communications among the Docker daemons.
- docker_gwbridge: Created by docker, and it allows the containers to connect to the host.
- IPVS: It is a load balancer implementation in the Linux kernel.
Routing Mesh
Request -> ingress network on published port -> any node (use IPVS to decide which service to go) -> ingress network (outer LB)-> overlay network (inner LB) -> node
Docker swarm command
Init
docker swarm init --advertise-addr {ip} // local ip
- This machine will be a manager (leader) by deafault.
- This will generate a token command, you can execute this command at any machine you want to assign it as a worker node.
Show node list
docker node ls
Show network list
docker network ls
Create an overlay network
docker network create --opt encrypted --subnet 100.0.0.0/24 -d overlay {name}
Create service
docker service create --name nginx --network {network name} -p {ex_port:in_port} {image_name}
docker service create --name nginx --network {network name} -p 1080:80 nvbeta/swarm_nginx
Get started
- Use docker machine command to create 3 machines.
- SSH into one of machines and do swarm initialisation.
- SSH into the rest of machines and execute token command that is generated by step 2.
- Show node list to see if expected.
Setting
Open protocols and ports between the hosts
- TCP port 2377 for cluster management communications (manager <-> worker)
- TCP and UDP port 7946 for communication among nodes (worker <-> worker, any node participates in the swarm)
- UDP port 4789 for overlay network traffic (worker <-> worker, nodes under the same overlay network)
Troubleshootings
dial unix /var/run/docker.sock: permission denied
2014/12/23 14:14:33 Get http:///var/run/docker.sock/v1.12/images/json: dial unix /var/run/docker.sock: permission denied
sudo adduser <username> docker
exec: "./wait-for-it.sh": permission denied: unknown
If adding below into Dockerfile doesn’t work, make sure running docker build --no-cache .
before docker-compose up --build
RUN chmod +x /usr/src/app/wait-for-it.sh
If docker is loading forever and can’t be launched:
- Move docker from application to trash bin
- Remove this file
rm -rf ~/Library/Containers/com.docker.docker
rm -rf ~/Library/Application\ Support/Docker\ Desktop
rm -rf ~/Library/Group\ Containers/group.com.docker