Linodeでdocker swarmクラスターを作り直した

CloudGarageが終了するので、VPSを乗り換える準備をしている。
Docker swarmのクラスターを作り直したから、手順をメモしておく。


VPS選定

VultrLinodeで迷った。

Vultr

  • ファイアウォールがついてる

Linode

  • 転送量を契約インスタンスで合算できる
  • 設定しなくても自動的にプライベートIPアドレスが振ってくる

Linodeで行くことにした。

manager用のStackScript

を作る。
TARGET_USER は自分のGitHubアカウントに書き換える。

#!/bin/bash
TARGET_USER=mohemohe

groupadd -f wheel
useradd -G wheel -m -s /bin/bash ${TARGET_USER}
mkdir -p /home/${TARGET_USER}/.ssh
chmod 700 /home/${TARGET_USER}/.ssh
curl https://github.com/${TARGET_USER}.keys > /home/${TARGET_USER}/.ssh/authorized_keys
chmod 600 /home/${TARGET_USER}/.ssh/authorized_keys
chown ${TARGET_USER}: -R /home/${TARGET_USER}

sed -i 's|#PasswordAuthentication yes|PasswordAuthentication no|' /etc/ssh/sshd_config
sed -i 's|#PermitRootLogin yes|PermitRootLogin no|' /etc/ssh/sshd_config
systemctl restart sshd

echo '%wheel ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers

作ったら1vCPU / 1GBで立てる。
最低1インスタンスでOK。

swarm manager 1

ローカルネットワークのIPアドレスを確認して docker swarm init する

 ❯ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether f2:3c:91:da:be:21 brd ff:ff:ff:ff:ff:ff
    inet XXX.XXX.XXX.XXX/24 brd XXX.XXX.XXX.XXX scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.146.91/17 brd 192.168.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2400:XXXX::XXXX:XXXX:XXXX:XXXX/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591989sec preferred_lft 604789sec
    inet6 fe80::f03c:91ff:feda:be21/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:22:de:30:26 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
 ❯ docker swarm init --advertise-addr="192.168.146.91" --listen-addr=192.168.146.91:2377
Swarm initialized: current node (yiyyxj710x4gyd58ek0ty9qy2) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYY 192.168.146.91:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

worker用のStackScript

を作る。
TARGET_USER は自分のGitHubアカウントに書き換える。

#!/bin/bash
TARGET_USER=mohemohe

groupadd -f wheel
useradd -G wheel -m -s /bin/bash ${TARGET_USER}
mkdir -p /home/${TARGET_USER}/.ssh
chmod 700 /home/${TARGET_USER}/.ssh
curl https://github.com/${TARGET_USER}.keys > /home/${TARGET_USER}/.ssh/authorized_keys
chmod 600 /home/${TARGET_USER}/.ssh/authorized_keys
chown ${TARGET_USER}: -R /home/${TARGET_USER}

sed -i 's|#PasswordAuthentication yes|PasswordAuthentication no|' /etc/ssh/sshd_config
sed -i 's|#PermitRootLogin yes|PermitRootLogin no|' /etc/ssh/sshd_config
systemctl restart sshd

echo '%wheel ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers

apt update
apt install -y docker.io
docker swarm join --token SWMTKN-1-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYY 192.168.146.91:2377

作ったら1vCPU / 2GBで立てる。
いっぱい立てていいぞ。
dockerの自動インストールに少し時間がかかるから気楽に待て✋

swarm manager 1

クラスターの状態を見る。
managerとwarkerの数だけ表示されていればOK。
HOSTNAME は気にするな。

 ❯ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
eo44u8ob53fli8pd9mklrzwdw     localhost           Ready               Active                                  18.09.7
yiyyxj710x4gyd58ek0ty9qy2 *   localhost           Ready               Active              Leader              18.09.7

雑に使うingressネットワークも作っておく。

  • driver: overlay
  • scope: swarm

になっているのを確認する。

 ❯ docker network create --driver=overlay --attachable lan
6gzz8p0sz8wen8vh58bchy6ot

 ❯ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
9bf6dbe1b775        bridge              bridge              local
68b7bd2a9300        docker_gwbridge     bridge              local
8e80af7eee4c        host                host                local
m7dsxn8pq7ag        ingress             overlay             swarm
6gzz8p0sz8we        lan                 overlay             swarm
f89b23e436b9        none                null                local

portainer

Dockerの思想うんぬんは聞き飽きた。
そんなもんいいから便利だからブチ込め。

https://www.portainer.io/installation/ には

$ curl -L https://downloads.portainer.io/portainer-agent-stack.yml -o portainer-agent-stack.yml
$ docker stack deploy --compose-file=portainer-agent-stack.yml portainer

とか書いてあるが、やめておけ。
host management features を有効化したほうが絶対にいい。
https://portainer.readthedocs.io/en/latest/agent.html#enable-host-management-features

version: '3.2'

services:
  agent:
    image: portainer/agent
    environment:
      AGENT_CLUSTER_ADDR: tasks.agent
      CAP_HOST_MANAGEMENT: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/docker/volumes:/var/lib/docker/volumes
      - /:/host
    networks:
      - portainer_network
    deploy:
      mode: global
      placement:
        constraints: [node.platform.os == linux]

  portainer:
    image: portainer/portainer
    command: -H tcp://tasks.agent:9001 --tlsskipverify
    volumes:
      - portainer_data:/data
    networks:
      - portainer_network
      - lan
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

networks:
  portainer_network:
    driver: overlay
    attachable: true
  lan:
    external: true

volumes:
  portainer_data:

で、デプロイ。

 ❯ docker stack deploy --compose-file docker-compose.yml portainer
Creating network portainer_portainer_network
Creating service portainer_portainer
Creating service portainer_agent

caddy

traefikでもいいんだけど、もっとサブドメイン周りを細かく制御したりしたいのでcaddyをmanagerに入れてLB代わりに使う。
だからmanagerは複数台のほうがいいかも。

アクメチャレンジでDNS-01を使うからCloudflare系の環境変数を突っ込んでおく。

version: '3.2'


services:
  caddy:
    image: abiosoft/caddy:no-stats
    command: >
      -conf /mnt/Caddyfile
      -quic
      -log stdout
    environment:
      - ACME_AGREE=true
      - CLOUDFLARE_EMAIL=
      - CLOUDFLARE_API_KEY=
    volumes:
      - caddy_data:/root/.caddy
      - caddy_cache:/data/cache
      - ./mnt:/mnt
    networks:
      - wan
      - lan
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: udp
        mode: host
    logging:
      driver: syslog
      options:
        tag: caddy
    deploy:
      placement:
        constraints:
          - node.role == manager

volumes:
  caddy_data:
  caddy_cache:

networks:
  wan:
  lan:
    external: true

Caddyfileはシンプル。

hogehoge.example.com {
  tls {
    dns cloudflare
  }
  log stdout
  errors stderr
  proxy / portainer_portainer:9000 {
    transparent
    websocket
  }
}

デプロイする。

 ❯ docker stack deploy --compose-file docker-compose.yml caddy
Creating network caddy_wan
Creating service caddy_caddy

運用の勘所

Linodeにはファイアウォールがないので絶対にポートを公開しない。
overlayネットワークのlanを使うようにすれば、少なくともcaddyからアクセスできる。