0%

Docker应用

安装

  • Debian 安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ~$ sudo apt-get remove docker docker-engine docker.io containerd runc
    ~$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

    ~$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

    ~$ echo \
    "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
    $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

    ~$ sudo apt-get update
    ~$ sudo apt-get install docker-ce docker-ce-cli containerd.io

Docker容器基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7ba1108b9f98 jenkinsci/jenkins "/sbin/tini -- /usr/l" 39 minutes ago Up 39 minutes 50000/tcp, 0.0.0.0:9090->8080/tcp jenkins
~$ docker exec -ti 7ba1108b9f98 /bin/bash #进入容器
jenkins@7ba1108b9f98:/$
jenkins@7ba1108b9f98:/$ exit
~$ docker exec --user root -ti 7ba1108b9f98 /bin/bash #进入容器,root用户.
root@7ba1108b9f98:/#

$ docker ps  
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89abae910d39 gitlab/gitlab-ce "/assets/wrapper" 18 minutes ago Up 18 minutes 0.0.0.0:80->80/tcp, 443/tcp, 0.0.0.0:2222->22/tcp gitlab
7ba1108b9f98 jenkinsci/jenkins "/sbin/tini -- /usr/l" 2 hours ago Up 2 hours 50000/tcp, 0.0.0.0:9090->8080/tcp jenkins
~$ docker stop 89abae910d39 #停止容器
89abae910d39
~$ docker rm 89abae910d39 #删除除容器
89abae910d39
~$ docker rmi gitlab/gitlab-ce #删除镜像

~$ docker image prune -a #清除未使用的镜像,释放磁盤空间.

~$ docker system prune #清除更多的选项的资源.
  • 查看它的运行状态
1
2
3
4
5
6
7
~$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
91fca6eba694 dendrite_kafka 1.50% 357MiB / 31.27GiB 1.11% 1.6MB / 2.09MB 0B / 3.35MB 104
9600de970dfb docker_zookeeper_1 0.04% 84.32MiB / 31.27GiB 0.26% 48.6kB / 27.7kB 1.65MB / 803kB 83
6024799cf47c docker_monolith_1 0.68% 15.75MiB / 31.27GiB 0.05% 2.15MB / 1.69MB 877kB / 328kB 21
0446e52d719d docker_postgres_1 0.00% 50.26MiB / 31.27GiB 0.16% 138kB / 86.1kB 16.5MB / 12.3MB 15
8a986b075043 traefik_reverse-proxy_1 0.00% 23.53MiB / 31.27GiB 0.07% 49.8kB / 1.52MB 66.6MB / 0B 21

容器网络

  • 查看网络

    1
    2
    3
    4
    5
    ~$ docker network ls
    NETWORK ID NAME DRIVER SCOPE
    108d6f126660 bridge bridge local
    1f2d1f3f0ebf host host local
    b17831300bb4 none null local
  • 查看详情

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    docker network inspect bridge
    [
    {
    "Name": "bridge",
    "Id": "108d6f126660ffa74a81eebdb65ebf5e982cfcc0a0e982cdf832de602e462dca",
    "Created": "2020-11-18T09:42:07.001834136Z",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": true,
    "IPAM": {
    "Driver": "default",
    "Options": null,
    "Config": [
    {
    "Subnet": "172.17.0.0/16",
    "Gateway": "172.17.0.1"
    },
    {
    "Subnet": "2600:3c03:xx:x/64",
    "Gateway": "2600:3c03:xx:x1"
    }
    ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
    "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
    "3ca33d64a42243f5ac77dcf61eeb8262ff8f6a30e341b34a4f5352adc5f98ca9": {
    "Name": "ss-server",
    "EndpointID": "9230ae11d665b088d23ef939ee959a9ad512a2f3667106688f38e63f85f603a1",
    "MacAddress": "02:42:ac:11:00:02",
    "IPv4Address": "172.17.0.2/16",
    "IPv6Address": "2600:3c03::242:xx:x/64"
    },
    "893078d9926bd8f41d669975c48be945472feaad5daed23c937ce8ab517b5a18": {
    "Name": "trojan",
    "EndpointID": "0c3650579b8029b9ce940459a7ac795828cb76eaec331ac179ea9708adaba8ae",
    "MacAddress": "02:42:ac:11:00:03",
    "IPv4Address": "172.17.0.3/16",
    "IPv6Address": "2600:3c03::242:xx:x/64"
    }
    },
    "Options": {
    "com.docker.network.bridge.default_bridge": "true",
    "com.docker.network.bridge.enable_icc": "true",
    "com.docker.network.bridge.enable_ip_masquerade": "true",
    "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
    "com.docker.network.bridge.name": "docker0",
    "com.docker.network.driver.mtu": "1500"
    },
    "Labels": {}
    }
    ]

  • 创建网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
~$ docker network create --ipv6 --driver=bridge --subnet=172.20.0.0/24 --subnet=2600:3c03:1111::/64 --gateway=172.20.0.1 ipv6_bridge
7fa02d4275d7a5165ed4169161895d6ead1fc55a785b1d39bf0da129ec40cff8
docker network inspect ipv6_bridge
[
{
"Name": "ipv6_bridge",
"Id": "7fa02d4275d7a5165ed4169161895d6ead1fc55a785b1d39bf0da129ec40cff8",
"Created": "2020-11-19T05:15:23.263134245Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": true,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/24",
"Gateway": "172.20.0.1"
},
{
"Subnet": "2600:3c03:1111::/64"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

  • 把现有的容器连接到新的网络
1
~$ docker network  connect ipv6_bridge <container>

代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
~$ sudo mkdir -p /etc/systemd/system/docker.service.d

# "HTTP_PROXY=socks5://127.0.0.1:1080" for socks5 proxy
~$ cat <<EOF >/etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://user01:password@10.10.10.10:8080/"
Environment="HTTPS_PROXY=https://user01:password@10.10.10.10:8080/"
Environment="NO_PROXY= hostname.example.com,172.10.10.10"
EOF

~$ systemctl daemon-reload
~$ systemctl restart docker
~$ systemctl show docker --property Environment
Environment=HTTP_PROXY=socks5://127.0.0.1:1080 HTTPS_PROXY=socks5://127.0.0.1:1080

使用HTTP与Docker交互

1
2
3
4
5
6
~$ curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" \
-d '{"Image": "alpine", "Cmd": ["echo", "hello world"]}' \
-X POST http://localhost/v1.41/containers/create
{"Id":"1c6594faf5","Warnings":null}

~$ curl --unix-socket /var/run/docker.sock -X POST http://localhost/v1.41/containers/1c6594faf5/start

配置存储驱动

  • Manage data in Docker
  • Docker支持多种存储驱动,默认是使用devicemapperloopback-lvm方式,它是零配置,但是性能差,不推荐在生产环境中使用.生产中建议使用direct-lvm的方式.默认的存储会有以下的警告:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 docker info
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)

Server:
Containers: 7
Running: 5
Paused: 0
Stopped: 2
Images: 268
Server Version: 20.10.5
Storage Driver: devicemapper
Pool Name: docker-8:18-38141955-pool
Pool Blocksize: 65.54kB
Base Device Size: 107.4GB
Backing Filesystem: ext4
Udev Sync Supported: true
Data file: /dev/loop0
Metadata file: /dev/loop1
Data loop file: /home/michael/3TB-DISK/docker/devicemapper/devicemapper/data
Metadata loop file: /home/michael/3TB-DISK/docker/devicemapper/devicemapper/metadata
Data Space Used: 94.71GB
Data Space Total: 107.4GB
Data Space Available: 12.66GB
Metadata Space Used: 84.49MB
Metadata Space Total: 2.147GB
Metadata Space Available: 2.063GB
Thin Pool Minimum Free Space: 10.74GB
Deferred Removal Enabled: true
Deferred Deletion Enabled: true
Deferred Deleted Device Count: 0
Library Version: 1.02.155 (2018-12-18)
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
Default Runtime: runc
Init Binary: docker-init
containerd version: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e
runc version: 12644e614e25b05da6fd08a38ffa0cfe1903fdec
init version: de40ad0
Security Options:
seccomp
[....]
WARNING: the devicemapper storage-driver is deprecated, and will be removed in a future release.
WARNING: devicemapper: usage of loopback devices is strongly discouraged for production use.
Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.
  • 如果磁盘镜像文件太多了,如有如下错误:
    1
    2
    docker: Error response from daemon: devmapper: Thin Pool has 486 free data blocks which is less than minimum required 163840 free data blocks. Create more free space in thin pool or use dm.min_free_space option to change behavior.
    See 'docker run --help'.
  • 可以使用下面命令清除docker内的临时文件
    1
    ~$ docker system prune

Overlay2驱动

1
2
3
4
5
6
7
8
9
10
11
~$ sudo mkfs.xfs -n ftype=1 /dev/sdb1
meta-data=/dev/sdb1 isize=512 agcount=4, agsize=18310464 blks
= sectsz=4096 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=0
data = bsize=4096 blocks=73241856, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=35762, version=2
= sectsz=4096 sunit=1 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
  • 配置daemon.json,参考daemon-configuration-file

    1
    2
    ~$ sudo systemctl stop docker
    ~$ echo '{ "storage-driver": "overlay2" }' | jq '.' | sudo tee /etc/docker/daemon.json
  • 重启

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    ~$ sudo systemctl start docker
    ~$ docker info
    Client:
    Context: default
    Debug Mode: false
    Plugins:
    app: Docker App (Docker Inc., v0.9.1-beta3)
    buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)

    Server:
    Containers: 0
    Running: 0
    Paused: 0
    Stopped: 0
    Images: 0
    Server Version: 20.10.5
    Storage Driver: overlay2
    Backing Filesystem: xfs
    Supports d_type: true
    Native Overlay Diff: true
    Logging Driver: json-file
    Cgroup Driver: cgroupfs
    Cgroup Version: 1
    [...]
  • Export/Import容器

  • 这里是在原来的devicemapper驱动里的容器导出,再切换到overlay2驱动导入.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ~$ docker save sickcodes/docker-osx > ~/3TB-DISK/docker-osx.tar
    ~$ docker load < ~/3TB-DISK/docker-osx.tar
    e4f1ca1ca7a8: Loading layer [==================================================>] 731.1MB/731.1MB
    ee2075d8bcfb: Loading layer [==================================================>] 35.84kB/35.84kB
    a98393dc6c96: Loading layer [==================================================>] 59.95MB/59.95MB
    116b71590f7b: Loading layer [==================================================>] 166MB/166MB
    d3c6d5a7aa4e: Loading layer [==================================================>] 59.95MB/59.95MB
    6dd5cd2c75b5: Loading layer [==================================================>] 2.048kB/2.048kB
    908b86edf5d9: Loading layer [==================================================>] 2.56kB/2.56kB
    c706cc5ac18e: Loading layer [==================================================>] 6.144kB/6.144kB
    [...]
1
2
3
4
5
6
~$ for item in 6ffe5fbf69b7  92527f264cc9  a15038d35f57 d10b5c313d05; do
docker export $item > docker-export-${item}.tar ;
done
~$ for item in `ls docker-export*.tar`; do
docker import $item ;
done

使用multi-stage功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
FROM golang:1.16 AS builder
RUN go version

ARG upx_version=3.96
ARG GOPROXY

RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://goproxy.io,direct

COPY . /go/src/github.com/xindalcy/frp
WORKDIR /go/src/github.com/xindalcy/frp
#RUN set -x && \
# go get github.com/golang/dep/cmd/dep && \
# dep ensure -v

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags="-w -s" -o bin/frps ./cmd/frps


SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends xz-utils && \
curl -Ls https://github.com/upx/upx/releases/download/v${upx_version}/upx-${upx_version}-amd64_linux.tar.xz -o - | tar xvJf - -C /tmp && \
cp /tmp/upx-${upx_version}-amd64_linux/upx /usr/local/bin/ && \
chmod +x /usr/local/bin/upx

RUN /usr/local/bin/upx -9 /go/src/github.com/xindalcy/frp/bin/frps

FROM scratch
COPY --from=builder /go/src/github.com/xindalcy/frp/bin/frps /go/bin/frps

EXPOSE 7001/tcp
EXPOSE 7500/tcp
EXPOSE 7001/udp
EXPOSE 7002/udp

VOLUME /etc/frp/

ENTRYPOINT ["/go/bin/frps","-c","/etc/frp/frps.ini"]
  • 上面这个脚本,演示如何在一个Dockerfile里实现分阶段创建镜像,最终的Docker镜像体积非常小,因为它是从scratch上构建的。

搭建基于DokkuPaaS平台

服务端安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 参考位置 http://dokku.viewdocs.io/dokku/getting-started/install/debian/ 改编的文件
- name: 安装Dokku
hosts: DB001
become: yes
# user: root 这里可以直接用root,但是关闭root远程登录后要使用sudo.
tasks:
# 参照文档 https://docs.ansible.com/ansible/latest/modules/apt_module.html
- name: 更新并安装
apt:
name: ['apt-transport-https', 'wget']
allow_unauthenticated: yes
update_cache: yes

- name: 添加公钥
apt_key:
url: https://packagecloud.io/gpg.key
validate_certs: no
state: present

# 参考文档 https://docs.ansible.com/ansible/latest/modules/command_module.html#command-module
- name: 读取系统发行版本号
command: lsb_release -sc
register: result

# https://docs.ansible.com/ansible/latest/modules/stat_module.html?highlight=stat 读取文件的stat
- stat:
path: /etc/apt/sources.list.d/dokku_dokku.list
register: dokku_repo

# https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html
# 把几个任务组合起来,用条件判断.
- block:
# https://docs.ansible.com/ansible/latest/modules/get_url_module.html?highlight=get_url
- name: 下载安装脚本
get_url:
url: https://packagecloud.io/install/repositories/dokku/dokku/script.deb.sh
dest: /tmp/script.deb.sh
validate_certs: no
force: yes
mode: 0755

# https://docs.ansible.com/ansible/latest/modules/script_module.html
# 也可以从这里直接下载deb安装包. https://packagecloud.io/dokku/dokku/
- name: 运行安装Dokku脚本
command: bash /tmp/script.deb.sh
when: dokku_repo.stat.exists is not defined or dokku_repo.stat.size == 0

- name: 安装dokku
apt:
name: ['dokku']
allow_unauthenticated: yes
update_cache: yes

# https://docs.ansible.com/ansible/latest/modules/debconf_module.html
- name: unattended installation of dokku
debconf:
name: dokku
question: dokku/vhost_enable
value: 'true'
vtype: select

- name: 安装Dokku依赖
command: dokku plugin:install-dependencies --core
  • 安装完成后会启动一个http-server提供web方式的配置,浏览器输入服务器ip打开页面,输入公钥和域名(或者公网 IP)就 OK.

客户端安装

  • dokku-client这个客户端很久没有更新的了,不支持 python3.
  • 官方客户端,这里基于不同语言有比较全面的客户端介绍.
  • Deploying Django on Dokku
  • 这里使用官方的提供的客户端脚本,如下面所示,运行脚本前,先要确定几个重点,一是添加DOKKU_HOST环境变里,或者直接写入$HOME/.dokku/contrib/dokku_client.sh这个脚本里面.二是能使用公钥登录目标dokku服务器.
  • dokku服务器端加入一个可信的客户端公钥,操作如下:
1
2
3
4
5
6
7
8
9
# 列出本机加入的可信公钥
~$ dokku ssh-keys:list
SHA256:Lr2N48k+1UDb0IxxWm0AhI0fzpdpnEZtalGc+XXXXX NAME="admin1" SSHCOMMAND_ALLOWED_KEYS="no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding"
# 添加一个客户端的公钥
~$ sudo dokku ssh-keys:add admin2 /fullpath/id_rsa.pub
SHA256:diDyzHYBmugZoGkWw1RaqzGkLfdCUpmBOxXFnf/XXXX
~$ dokku ssh-keys:list
SHA256:Lr2N48k+1UDb0IxxWm0AhI0fzpdpnEZtalGc+Pf7Rvo NAME="admin1" SSHCOMMAND_ALLOWED_KEYS="no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding"
SHA256:diDyzHYBmugZoGkWw1RaqzGkLfdCUpmBOxXFnf/XXXX NAME="admin2" SSHCOMMAND_ALLOWED_KEYS="no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding"
  • 下载客户端仓库.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ~$ git clone git@github.com:dokku/dokku.git ~/.dokku

    # optional: make sure that the dokku_client.sh version matches your Dokku version
    ~$ cd ~/.dokku
    ~$ git checkout <tag/branch>

    # add the following to either your
    # .bashrc, .bash_profile, or .profile file
    ~$ alias dokku='$HOME/.dokku/contrib/dokku_client.sh'
  • 正常的客户端运行如下,与heroku的客户端命令极其相似.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    ~$ dokku
    Usage: dokku [--quiet|--trace|--rm-container|--rm|--force] COMMAND <app> [command-specific-options]

    Primary help options, type "dokku COMMAND:help" for more details, or dokku help --all to see all commands.

    Commands:

    apps Manage Dokku apps
    certs Manage Dokku apps SSL (TLS) certs
    checks Manage zero-downtime settings
    config Manages global and app-specific config vars
    docker-options Pass options to Docker the various stages of an app
    domains Manage vhost domains used by the Dokku proxy
    enter Connect to a specific app container
    events Show the last events (-t follows)
    git Manages the git integration for an app
    help Print the list of commands
    logs Output app logs
    network Manages network settings for an app
    nginx Interact with Dokku\'s Nginx proxy
    proxy Manage the proxy used by dokku on a per app
    ps List processes running in app container(s)
    repo Runs commands that interact with the app\'s repo
    run Run a command in a new container using the current application image
    scheduler-docker-local Manages the scheduler-docker-local integration for an app
    shell Spawn dokku shell
    ssh-keys Manage public ssh keys that are allowed to connect to Dokku
    storage Mount local volume / directories inside containers
    tags List all app image tags
    tar Deploy applications via tarball instead of git
    trace Enable dokku tracing
    url Show the first URL for an application (compatibility)
    urls Show all URLs for an application
    version Print dokku\'s version

    ~$ dokku plugin:list
    ! Deprecated: Please use plugin:list
    plugn: 0.3.0
    00_dokku-standard 0.14.1 enabled dokku core standard plugin
    20_events 0.14.1 enabled dokku core events logging plugin
    app-json 0.14.1 enabled dokku core app-json plugin
    apps 0.14.1 enabled dokku core apps plugin
    build-env 0.14.1 enabled dokku core build-env plugin
    certs 0.14.1 enabled dokku core certificate management plugin
    checks 0.14.1 enabled dokku core checks plugin
    common 0.14.1 enabled dokku core common plugin
    config 0.14.1 enabled dokku core config plugin
    docker-options 0.14.1 enabled dokku core docker-options plugin
    domains 0.14.1 enabled dokku core domains plugin
    enter 0.14.1 enabled dokku core enter plugin
    git 0.14.1 enabled dokku core git plugin
    logs 0.14.1 enabled dokku core logs plugin
    network 0.14.1 enabled dokku core network plugin
    nginx-vhosts 0.14.1 enabled dokku core nginx-vhosts plugin
    plugin 0.14.1 enabled dokku core plugin plugin
    proxy 0.14.1 enabled dokku core proxy plugin
    ps 0.14.1 enabled dokku core ps plugin
    repo 0.14.1 enabled dokku core repo plugin
    scheduler-docker-local 0.14.1 enabled dokku core scheduler-docker-local plugin
    shell 0.14.1 enabled dokku core shell plugin
    ssh-keys 0.14.1 enabled dokku core ssh-keys plugin
    storage 0.14.1 enabled dokku core storage plugin
    tags 0.14.1 enabled dokku core tags plugin
    tar 0.14.1 enabled dokku core tar plugin

部署Django工程实例

  • 链接:
  • 下面的环境基于Pyenv+Pipenv.Pyenv:python版本管理器.Pipenv:python包管理器,更好用的pip.
  • pyenv的一些基本操作.
  • pyenv安装的位置
    1
    2
    ~$ which pyenv
    /home/lcy/.pyenv/bin/pyenv
  • pyenv可提供的安装版本.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ~$ pyenv install --list
    Available versions:
    2.1.3
    2.2.3
    2.3.7
    2.4
    2.4.1
    2.4.2
    2.4.3
    2.4.4
    [...]
  • 本机已经安装的版本
    1
    2
    3
    4
    5
    6
    7
    ~$ pyenv versions
    system
    * 3.6.6 (set by /home/lcy/.python-version)
    3.6.6/envs/dokku-py3
    3.6.6/envs/py3dev
    dokku-py3
    py3dev
  • 本机默认使用的版本.
    1
    2
    ~$ pyenv version
    3.6.6 (set by /home/lcy/.python-version)
  • 为这当前版pyenv本安装pipenv
    1
    2
    ~$ pip install pipenv
    Collecting pipenv
  • 列出当前安装的包.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ~$ pip list
    Package Version
    ---------------- ----------
    autopep8 1.4
    certifi 2018.11.29
    pip 18.1
    pipenv 2018.11.26
    pycodestyle 2.4.0
    setuptools 39.0.1
    virtualenv 16.2.0
    virtualenv-clone 0.4.0
    yapf 0.23.0
  • 通过上述命令安装了pipenv,下面是它的一些基本操作.官方文档
  • 下面是创建python3的虚拟环境,也可以用pipenv --python 3.7指定具体的版本号.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ~$ pipenv --three
    Creating a virtualenv for this project…
    Pipfile: /home/lcy/workspace/dokku-test/crm/Pipfile
    Using /home/lcy/.pyenv/versions/3.6.6/bin/python3.6 (3.6.6) to create virtualenv…
    ⠸ Creating virtual environment...Already using interpreter /home/lcy/.pyenv/versions/3.6.6/bin/python3.6
    Using base prefix '/home/lcy/.pyenv/versions/3.6.6'
    New python executable in /home/lcy/.local/share/virtualenvs/crm-GMqHkluo/bin/python3.6
    Also creating executable in /home/lcy/.local/share/virtualenvs/crm-GMqHkluo/bin/python
    Installing setuptools, pip, wheel...
    done.

    ✔ Successfully created virtual environment!
    Virtualenv location: /home/lcy/.local/share/virtualenvs/crm-GMqHkluo
    Creating a Pipfile for this project…
    # 查看环境位置.
    ~$ pipenv --venv
    /home/lcy/.local/share/virtualenvs/crm-GMqHkluo

    ~$ pipenv --py
    /home/lcy/.local/share/virtualenvs/crm-GMqHkluo/bin/python
  • 安装django依赖包.当前工程目录下的Pipfile.lock文件,就等同于传统的requirements.txt文件.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ~$ pipenv install django django-toolbelt
    Installing django…
    Adding django to Pipfile\'s [packages]…
    ✔ Installation Succeeded
    Installing django-toolbelt…
    Adding django-toolbelt to Pipfile\'s [packages]…
    ✔ Installation Succeeded
    Pipfile.lock not found, creating…
    Locking [dev-packages] dependencies…
    Locking [packages] dependencies…
    ✔ Success!
    Updated Pipfile.lock (c14d8c)!
    Installing dependencies from Pipfile.lock (c14d8c)…
    🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 8/8 — 00:00:01
    To activate this project\'s virtualenv, run pipenv shell.
    Alternatively, run a command inside the virtualenv with pipenv run.
  • 列出安装包的的依赖.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ~$ pipenv graph
    django-toolbelt==0.0.1
    - dj-database-url [required: Any, installed: 0.5.0]
    - dj-static [required: Any, installed: 0.0.6]
    - static3 [required: Any, installed: 0.7.0]
    - django [required: Any, installed: 2.1.5]
    - pytz [required: Any, installed: 2018.9]
    - gunicorn [required: Any, installed: 19.9.0]
    - psycopg2 [required: Any, installed: 2.7.6.1]
  • 进入pipenv虚拟环境shell
    1
    2
    3
    ~$ pipenv shell
    Launching subshell in virtual environment…
    . /home/lcy/.local/share/virtualenvs/cms-uhqZcysp/bin/activate

创建Django的示例项目.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
~$ mkdir dokkutest && cd dokkutest
~$ pipenv --three
Creating a Pipfile for this project…
# 会在本机目录下生成一个Pipfile
~$ cat Pipfile
[[source]]
name = "pypi"
#url = "https://pypi.org/simple"
#使用阿里云源
url = "http://mirrors.aliyun.com/pypi/simple"
verify_ssl = true

# 使用阿里云源必需加这一行.
[install]
trusted-host = mirrors.aliyun.com

[dev-packages]

[packages]

[requires]
python_version = "3.6"
# 安装相应的包.
~$ pipenv install django django-toolbelt
~$ pipenv shell
~$ django-admin.py startproject dokkutest .
~$ echo "web: gunicorn dokkutest.wsgi" > Procfile
~$ echo "venv" > .gitignore
# 新建APP
~$ django-admin.py startapp webui
# 安装APP
~$ sed -i "/django.contrib.staticfiles/ a\ 'webui'," dokkutest/settings.py
# 如果不加这两行,就要设置 dokku config:set DISABLE_COLLECTSTATIC=1,不然会出错,强烈建议加下面一行.
~$ sed -i "/STATIC_URL/ a\STATIC_ROOT = os.path.join(BASE_DIR, 'static')" dokkutest/settings.py
# 本地合并
~$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
[....]
# 本的运行测度
$ python manage.py runserver 0.0.0.0:9000
Performing system checks...

System check identified no issues (0 silenced).
January 14, 2019 - 09:03:32
Django version 2.1.5, using settings 'dokkutest.settings'
Starting development server at http://0.0.0.0:9000/
Quit the server with CONTROL-C.
# 收集依赖包的列表,这里会收集当前虚环境的所有包,建议新建工程时新建一个新的虚拟环境,这样可以保证requirements.txt是最小的依赖列表.
~$ pip freeze -l > requirements.txt
# 添加Procfile文件.
~$ echo "web: gunicorn dokkutest.wsgi" > Procfile
# 指定python的版本
~$ echo "python-3.6.7" > runtime.txt
# 加入git版本管理,这里假设我的dokku服务器的<域名>地址是172.16.10.100
~$ git init
~$ git add .
~$ git commit -m 'first commit'
~$ git remote add dokku dokku@172.16.10.100:dokkutest
# push代码后,就触编译与发布.
~$ git push dokku master
Delta compression using up to 6 threads.
Compressing objects: 100% (29/29), done.
Writing objects: 100% (33/33), 8.11 KiB | 0 bytes/s, done.
Total 33 (delta 5), reused 0 (delta 0)
-----> Cleaning up...
-----> Building dokkutest from herokuish...
-----> Adding BUILD_ENV to build environment...
-----> Python app detected
Skipping installation, as Pipfile.lock hasn\'t changed since last deploy.
[....]
DOKKU_APP_RESTORE: 1
=====> Renaming container (b7192f0ff943) dreamy_pascal to dokkutest.web.1
=====> Application deployed:
http://172.16.10.100:51903 # 到此程序的窗口与宿主机的端口映射已经生成了.

To 172.16.10.100:dokkutest
* [new branch] master -> master

~$ dokku apps:list
=====> My Apps
dokkutest
# dokku 服务器里的docker对应的容器运行起来
~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7192f0ff943 dokku/dokkutest:latest "/start web" 23 hours ago Up 23 hours dokkutest.web.1
# 端口映射OK
~$ netstat -tnlp
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:51903 0.0.0.0:* LISTEN -

# 应用在服务器的位置,一些配置文件与变量可以直接进入服务器里的相关app目录下去修改.
~$ tree -L 1 /home/dokku
/home/dokku
├── bazi
├── ENV
├── HOSTNAME
├── phpcms
├── VERSION
└── dokkutest

Dokku其它指南

  • 设置APP域名.如上面所述,app映射了一个很大端口值http://172.16.10.100:51903,只能通过IP+端口的方式访问,这有可能不能满足我们的实际应用需求,怎样在同一个IP里使用不同的域名访问不同的应用呢?或者说,同一个服务器里的应用都想通过80端口对外提供服务.这里就要通过nginx代理不同的域名.命令如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ~$ dokku domains:help
    Usage: dokku domains[:COMMAND]

    Manage vhost domains used by the Dokku proxy.

    Additional commands:
    domains:add <app> <domain> [<domain> ...] Add domains to app
    domains:add-global <domain> [<domain> ...] Add global domain names
    domains [<app>] [DEPRECATED] Alternative for domains:report
    domains:clear <app> Clear all domains for app
    domains:disable <app> Disable VHOST support
    domains:enable <app> Enable VHOST support
    domains:remove <app> <domain> [<domain> ...] Remove domains from app
    domains:remove-global <domain> [<domain> ...] Remove global domain names
    domains:report [<app>] [<flag>] Displays a domains report for one or more apps
    domains:set <app> <domain> [<domain> ...] Set domains for app
    domains:set-global <domain> [<domain> ...] Set global domain names
  • dokkutest这个应用设置test.example.com这个域名,后面就可以通过个域名 80 端就能访问到这个应用了.

1
2
3
4
5
~$ dokku domains:test.example.com  dokkutest
[...]
=====> 734565b29c3d49d4e9ae129b79c8011353300f45380a4b1fdce0df9604c2d31b
=====> Application deployed:
http://test.example.com
  • 访问静态资源.其实上面的部署是一个基本的应用,做一个Restful API服务器是没有问题的.如果做成网页端,它的static下的静态资源是不能访问的.这里要用到一些其它包与设置.具体参照这里 Django and Static Assets.具体解决如下:
1
2
3
4
5
6
7
8
9
# 安装whitenoise
~$ pip install whitenoise
# 下面修改settings.py
MIDDLEWARE_CLASSES = (
# 放在最上层一行.
# https://warehouse.python.org/project/whitenoise/
'whitenoise.middleware.WhiteNoiseMiddleware',
# 添加这一行
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Dokku插件应用

letsencrypt

1
2
3
4
# 安装
~$ sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
# 更新
~$ sudo dokku plugin:update letsencrypt
  • APP安装Lets'Encrypt,这里需要注意,这个app设置了三个域名,其中只有一个是在DNS上设置的h5.lcy.wiki,所以才会出现下面的错误.只要把其它两个域名从这个app中删除就可以了.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ~$ dokku letsencrypt h5dev
    =====> Let\'s Encrypt h5dev
    -----> Updating letsencrypt docker image...
    [...]
    -----> Enabling ACME proxy for h5dev...
    -----> Getting letsencrypt certificate for h5dev...
    - Domain 'h5dev.www.lcy.wiki'
    - Domain 'h5.lcy.wiki'
    - Domain 'h5dev'
    [...]
    ACME server returned an error: urn:acme:error:malformed :: The request message was malformed :: Error creating new authz :: DNS name does not have enough labels

    Debugging tips: -v improves output verbosity. Help is available under --help.
    -----> Certificate retrieval failed!
    -----> Disabling ACME proxy for h5dev...
    done
    # 正常应该如下所示

    ~$ dokku domains:report h5dev
    =====> h5dev domains information
    Domains app enabled: true
    Domains app vhosts: h5.lcy.wiki

    ~$ dokku letsencrypt:ls
    -----> App name Certificate Expiry Time before expiry Time before renewal
    h5dev 2019-06-18 18:42:21 89d, 20h, 14m, 52s 59d, 20h, 14m, 52s

    # 自动刷新证书
    ~$ dokku letsencrypt:auto-renew h5dev
    h5dev still has 59d, 20h, 12m, 43s days left before renewal
  • 错误
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     dokku letsencrypt yfh5
    =====> Let's Encrypt yfh5
    -----> Updating letsencrypt docker image...
    0.1.0: Pulling from dokku/letsencrypt
    Digest: sha256:af5f8529c407645e97821ad28eba328f4c59b83b2141334f899xxxxxxxxxx
    Status: Image is up to date for dokku/letsencrypt:0.1.0
    docker.io/dokku/letsencrypt:0.1.0
    Done updating
    -----> Enabling ACME proxy for yfh5...
    -----> Getting letsencrypt certificate for yfh5...
    /var/lib/dokku/plugins/available/letsencrypt/functions: line 145: get_app_domains: command not found
    ^[[1;3Rdarkhttpd/1.12, copyright (c) 2003-2016 Emil Mikulic.
    listening on: http://0.0.0.0:80/
    You must set at least one -d/--vhost

    Debugging tips: -v improves output verbosity. Help is available under --help.
    -----> Certificate retrieval failed!
    -----> Disabling ACME proxy for yfh5...
    done

redis插件应用

  • dokku-redis
  • How to connect to redis with dokku and flask?
  • 安装插件,并创建数据库
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    ~$ sudo dokku plugin:install https://github.com/dokku/dokku-redis.git redis
    ~$ dokku redis:create dokku-redis
    ~$ dokku redis:info dokku-redis
    =====> Container Information
    # 这两行是映射到宿主机的实际目录下面.
    Config dir: /var/lib/dokku/services/redis/dokku-redis/config
    Data dir: /var/lib/dokku/services/redis/dokku-redis/data
    Dsn: redis://dokku-redis:df40bbae8555a06d52f937fd4072ad98ec6e58563f84686254b43ff946605e4c@dokku-redis-dokku-redis:6379
    Exposed ports: -
    Id: e535a815b6bdc6898c9e6615d2e3fe0dd7387598bc6795aaf0529ad998b2f114
    Internal ip: 172.17.0.8
    Links:
    Service root: /var/lib/dokku/services/redis/dokku-redis
    Status: running
    Version: redis:4.0.11
  • 连接redis与容器应用服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ~$ dokku redis:link dokku-redis  yfh5
    -----> Setting config vars
    REDIS_URL: redis://dokku-redis:df40bbae8555a06d52f937fd4072ad98ec6e58563f84686254b43ff946605e4c@dokku-redis-dokku-redis:6379
    -----> Restarting app yfh5
    -----> Releasing yfh5 (dokku/yfh5:latest)...
    [...]

    ~$ dokku redis:info dokku-redis
    [...]
    Internal ip: 172.17.0.8
    Links: yfh5
    Service root: /var/lib/dokku/services/redis/dokku-redis
    [...]
  • 添加到Django项目中用作 Sessiong Cache ,修改settings.py的内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    CACHES = {
    "default": {
    "BACKEND":
    "django_redis.cache.RedisCache",
    "LOCATION":
    "redis://dokku-redis:df40bbae8555a06d52f937fd4072ad98ec6e58563f84686254b43ff946605e4c@dokku-redis-dokku-redis:6379",
    "OPTIONS": {
    "DB":
    0,
    "PASSWORD":
    "df40bbae8555a06d52f937fd4072ad98ec6e58563f84686254b43ff946605e4c",
    "CONNECTION_POOL_KWARGS": {
    "max_connections": 65535
    },
    }
    }
    }

设置Settings.py

  • 链接:
  • 因为Django开发测试需要与数据库连接,且开发环境与生产(测试)部署的环境是不一样的,这里可以通过在settings.py判断系统环境变量来做出一些自动化.上面那个链很有参考意.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    # We should get this from the environment, never store them in git.
    # 为了安全建义不要SECRET_KEY直接写入settings.py,它会保存在git上面记录.可以使用 dokku config:set appname SECRET_KEY='xxxxxx'
    SECRET_KEY = os.environ.get("SECRET_KEY", 'secret')

    # Set DEBUG to False if the NODEBUG env var has been set.
    DEBUG = True if os.environ.get("NODEBUG") is None else False

    # Set the allowed hosts based on the environment.
    ALLOWED_HOSTS = ["web", "localhost"] if os.environ.get("NODEBUG") is None else [".yourdomain.com"]

    if os.environ.get("IN_DOCKER"):
    # Stuff for when running in Docker-compose.
    # 使用Docker-compose会有IN_DOCKER这个环境变量

    CELERY_BROKER_URL = 'redis://redis:6379/1'
    CELERY_RESULT_BACKEND = 'redis://redis:6379/1'

    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': "postgres",
    'USER': 'postgres',
    'PASSWORD': 'password',
    'HOST': "db",
    'PORT': 5432,
    }
    }
    elif os.environ.get("DATABASE_URL"):
    # Stuff for when running in Dokku.

    # Parse the DATABASE_URL env var.
    USER, PASSWORD, HOST, PORT, NAME = re.match("^postgres://(?P<username>.*?)\:(?P<password>.*?)\@(?P<host>.*?)\:(?P<port>\d+)\/(?P<db>.*?)$", os.environ.get("DATABASE_URL", "")).groups()

    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': NAME,
    'USER': USER,
    'PASSWORD': PASSWORD,
    'HOST': HOST,
    'PORT': int(PORT),
    }
    }

    CELERY_BROKER_URL = os.environ.get("REDIS_URL", "") + "/1"
    CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL", "") + "/1"
    else:
    # Stuff for when running locally.

    CELERY_TASK_ALWAYS_EAGER = True
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
    }

Mysql插件应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
~$ sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql
# 创建数据库服务
~$ sudo dokku mysql:create dyt
Waiting for container to be ready
=====> MySQL container created: dyt
=====> Container Information
Config dir: /var/lib/dokku/services/mysql/dyt/config
Data dir: /var/lib/dokku/services/mysql/dyt/data
Dsn: mysql://mysql:42b0d88fb443bab7@dokku-mysql-dyt:3306/dyt
Exposed ports: -
Id: 45171e69490d59524728846297870ca3010d0066e9972f00448b95bf8bbe86d2
Internal ip: 172.17.0.10
Links: -
Service root: /var/lib/dokku/services/mysql/dyt
Status: running
Version: mysql:5.7.12
# 把应用与数库服务连接起来
~$ sudo dokku mysql:link dyt phpenroll
-----> Setting config vars
DATABASE_URL: mysql://mysql:42b0d88fb443bab7@dokku-mysql-dbname:3306/dyt
-----> Restarting app phpenroll

# 备价与恢复
~$ dokku mysql:export [db_name] > [db_name].dump
~$ dokku mysql:import [db_name] < [db_name].dump
# 进入数据库
~$ dokku mysql:connect dyt
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| dyt |
+--------------------+
2 rows in set (0.00 sec)

# 注意,dokku 创建的数据服务,里面只有一个与服务名相同的数据库,同时也不能通过mysql命令进去创建新的数据库,用户与授权.

Persistent Storage

  • 参考链接
  • 这里需要在主机上先建一个目录,供给容器内挂载,官方推荐是/var/lib/dokku/data/storage,这里可以是其它文件系统目录,
    • 如:cephfs,nfs等.关于volume与主机共享数据使用,也可以参考http://dokku.viewdocs.io/dokku/advanced-usage/docker-options/.
    • 如: dokku docker-options:add node-js-app deploy,run "-v /var/log/node-js-app:/app/logs"
      1
      2
      ~$ sudo dokku plugin:install storage
      ~$ dokku storage:mount node-js-app /var/lib/dokku/data/storage/node-js-app:/storage
  • 为了配合Linux的权限,需要把storage下的node-js-app目录设置成chown -R 32767:dokku node-js-app权限.

常见的错误

1
2
3
~$ -----> Python app detected
! Requested runtime (python-3.6.7) is not available for this stack (heroku-16).
! Aborting. More info: https://devcenter.heroku.com/articles/python-support
  • 如果出现上述错误,请重新提交.

与 WebPack 集成

谢谢支持

  • 微信二维码: