diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..197e8f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +all: ipfs ipfs-cluster +ipfs: + ansible-playbook -i inventory.yml ipfs.yml +ipfs-cluster: + ansible-playbook -i inventory.yml ipfs-cluster.yml +.PHONY = all ipfs ipfs-cluster diff --git a/README.md b/README.md new file mode 100644 index 0000000..26c8738 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Ansible roles for `go-ipfs` and `ipfs-cluster` + +This repository contains Ansible roles to install and run [`go-ipfs`](https://github.com/ipfs/go-ipfs) and [`ipfs-cluster`](https://github.com/ipfs/ipfs-cluster). + +They include Systemd service files for both. + +## Usage + +If you are familiar with Ansible, you can just re-use the modules in the way +that fits you best. Otherwise follow these steps: + +0. Make sure you have ansible installed: `pip install ansible`. +1. Fill in `inventory.yml` and place the hostnames of your nodes under the `[ipfs]` group. +2. Add a file for each hostname (filename is the hostname), to the `host_vars` folder as outlined in [`host_vars/README.txt`](host_vars/README.txt), containing the necessary host-specific variables. +3. Run `make`. + +`make` will run ansible for the `ipfs` and the `ipfs-cluster` roles, which apply to the `[ipfs]` inventory group. Upon successful, both `go-ipfs` and `ipfs-cluster` should be running in the nodes (they are installed under `/usr/local/bin` and run by a created `ipfs` system user). + +You can use `systemctl status ipfs` and `systemctl status ipfs-cluster` to check the status of the new services. diff --git a/host_vars/README.txt b/host_vars/README.txt new file mode 100644 index 0000000..cb0c336 --- /dev/null +++ b/host_vars/README.txt @@ -0,0 +1,13 @@ +Add in this folder one file for each ipfs/ipfs-cluster host with +the local variables like: + +ipfs_peer_id: +ipfs_private_key: +ipfs_cluster_peer_id: +ipfs_cluster_private_key: + +You can use: + +$ ipfs-key | base64 -w 0 + +to generate ids and keys. See https://github.com/whyrusleeping/ipfs-key. diff --git a/inventory.yml b/inventory.yml new file mode 100644 index 0000000..b8167f5 --- /dev/null +++ b/inventory.yml @@ -0,0 +1,2 @@ +[ipfs] +# Write here your ipfs nodes diff --git a/ipfs-cluster.retry b/ipfs-cluster.retry new file mode 100644 index 0000000..601b9dc --- /dev/null +++ b/ipfs-cluster.retry @@ -0,0 +1 @@ +tuvok diff --git a/ipfs-cluster.yml b/ipfs-cluster.yml new file mode 100644 index 0000000..2f0241c --- /dev/null +++ b/ipfs-cluster.yml @@ -0,0 +1,7 @@ +- hosts: ipfs + roles: + - ipfs-cluster + vars: + dist_url: https://dist.ipfs.io + ipfs_cluster_version: v0.0.10 + ipfs_cluster_arch: arm diff --git a/ipfs.yml b/ipfs.yml new file mode 100644 index 0000000..3bab39d --- /dev/null +++ b/ipfs.yml @@ -0,0 +1,7 @@ +- hosts: ipfs + roles: + - ipfs + vars: + dist_url: https://dist.ipfs.io + ipfs_version: v0.4.7 + ipfs_arch: arm diff --git a/roles/ipfs-cluster/files/etc/systemd/system/ipfs-cluster.service b/roles/ipfs-cluster/files/etc/systemd/system/ipfs-cluster.service new file mode 100644 index 0000000..04d3986 --- /dev/null +++ b/roles/ipfs-cluster/files/etc/systemd/system/ipfs-cluster.service @@ -0,0 +1,10 @@ +[Unit] +Description=IPFS Cluster Service +After=network.target + +[Service] +ExecStart=/usr/local/bin/ipfs-cluster-service +User=ipfs + +[Install] +WantedBy=multiuser.target diff --git a/roles/ipfs-cluster/tasks/main.yml b/roles/ipfs-cluster/tasks/main.yml new file mode 100644 index 0000000..3666989 --- /dev/null +++ b/roles/ipfs-cluster/tasks/main.yml @@ -0,0 +1,70 @@ +- name: create download folder for go-ipfs + become: yes + file: + state: directory + owner: root + group: root + dest: /opt/ipfs-cluster/{{ipfs_cluster_version}} + +- name: download and unpack IPFS cluster + become: yes + unarchive: + remote_src: yes + src: "{{ dist_url }}/{{ item }}/{{ipfs_cluster_version}}/{{ item }}_{{ipfs_cluster_version}}_linux-{{ipfs_cluster_arch}}.tar.gz" + dest: /opt/ipfs-cluster/{{ipfs_cluster_version}} + creates: /opt/ipfs-cluster/{{ipfs_cluster_version}}/{{ item }} + with_items: + - ipfs-cluster-service + - ipfs-cluster-ctl + +- name: link ipfs cluster executables + become: yes + file: + state: link + owner: root + group: root + dest: /usr/local/bin/{{ item }} + src: /opt/ipfs-cluster/{{ipfs_cluster_version}}/{{ item }}/{{ item }} + with_items: + - ipfs-cluster-service + - ipfs-cluster-ctl + +- name: install ipfs-cluster init service + become: yes + copy: + src: etc/systemd/system/ipfs-cluster.service + dest: /etc/systemd/system/ipfs-cluster.service + owner: root + group: root + mode: 0644 + +- name: reload systemd + become: yes + shell: systemctl daemon-reload + +- name: make .ipfs-cluster + become: yes + become_user: ipfs + file: + state: directory + mode: 0700 + owner: ipfs + group: ipfs + dest: /home/ipfs/.ipfs-cluster + +- name: copy configuration + become: yes + become_user: ipfs + template: + src: service.json + dest: /home/ipfs/.ipfs-cluster/service.json + mode: 0600 + owner: ipfs + group: ipfs + +- name: enable and start ipfs-cluster + become: yes + service: + name: ipfs-cluster + state: restarted + enabled: yes diff --git a/roles/ipfs-cluster/templates/service.json b/roles/ipfs-cluster/templates/service.json new file mode 100644 index 0000000..7799211 --- /dev/null +++ b/roles/ipfs-cluster/templates/service.json @@ -0,0 +1,19 @@ +{ + "id": "{{ipfs_cluster_peer_id}}", + "private_key" : "{{ipfs_cluster_private_key}}", + "cluster_peers" : [ + {% for host in groups['ipfs'] %} + "/ip4/{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}/tcp/9096/ipfs/{{ hostvars[host]['ipfs_cluster_peer_id'] }}"{% if not loop.last %},{% endif %} + + {% endfor %} + ], + "bootstrap": [], + "leave_on_shutdown": false, + "cluster_multiaddress": "/ip4/0.0.0.0/tcp/9096", + "api_listen_multiaddress": "/ip4/0.0.0.0/tcp/9094", + "ipfs_proxy_listen_multiaddress": "/ip4/0.0.0.0/tcp/9095", + "ipfs_node_multiaddress": "/ip4/127.0.0.1/tcp/5001", + "consensus_data_folder": "/home/ipfs/.ipfs-cluster/data", + "replication_factor": 2, + "monitoring_interval_seconds": 15 +} diff --git a/roles/ipfs/files/etc/init.d/ipfs b/roles/ipfs/files/etc/init.d/ipfs new file mode 100644 index 0000000..d52bdc0 --- /dev/null +++ b/roles/ipfs/files/etc/init.d/ipfs @@ -0,0 +1,97 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start ipfs +# Description: Runs the ipfs daemon. +### END INIT INFO + +dir="/usr/local/bin" +cmd="ipfs daemon" +user="ipfs" + +name=`basename $0` +pid_file="/var/run/$name.pid" +stdout_log="/var/log/$name.log" +stderr_log="/var/log/$name.err" + +get_pid() { + cat "$pid_file" +} + +is_running() { + [ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1 +} + +case "$1" in + start) + if is_running; then + echo "Already started" + else + echo "Starting $name" + cd "$dir" + if [ -z "$user" ]; then + sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & + else + sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & + fi + echo $! > "$pid_file" + if ! is_running; then + echo "Unable to start, see $stdout_log and $stderr_log" + exit 1 + fi + fi + ;; + stop) + if is_running; then + echo -n "Stopping $name.." + kill `get_pid` + for i in {1..10} + do + if ! is_running; then + break + fi + + echo -n "." + sleep 1 + done + echo + + if is_running; then + echo "Not stopped; may still be shutting down or shutdown may have failed" + exit 1 + else + echo "Stopped" + if [ -f "$pid_file" ]; then + rm "$pid_file" + fi + fi + else + echo "Not running" + fi + ;; + restart) + $0 stop + if is_running; then + echo "Unable to stop, will not attempt to start" + exit 1 + fi + $0 start + ;; + status) + if is_running; then + echo "Running" + else + echo "Stopped" + exit 1 + fi + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac + diff --git a/roles/ipfs/files/etc/systemd/system/ipfs.service b/roles/ipfs/files/etc/systemd/system/ipfs.service new file mode 100644 index 0000000..cf3938b --- /dev/null +++ b/roles/ipfs/files/etc/systemd/system/ipfs.service @@ -0,0 +1,10 @@ +[Unit] +Description=IPFS daemon +After=network.target + +[Service] +ExecStart=/usr/local/bin/ipfs daemon --migrate +User=ipfs + +[Install] +WantedBy=multiuser.target diff --git a/roles/ipfs/handlers/main.yml b/roles/ipfs/handlers/main.yml new file mode 100644 index 0000000..d01cb86 --- /dev/null +++ b/roles/ipfs/handlers/main.yml @@ -0,0 +1,5 @@ +- name: restart ipfs + become: yes + service: + name: ipfs + state: restarted \ No newline at end of file diff --git a/roles/ipfs/tasks/main.yml b/roles/ipfs/tasks/main.yml new file mode 100644 index 0000000..515cf32 --- /dev/null +++ b/roles/ipfs/tasks/main.yml @@ -0,0 +1,81 @@ +- name: create download folder for go-ipfs + become: yes + file: + state: directory + owner: root + group: root + dest: /opt/go-ipfs/{{ipfs_version}} + +- name: download and unpack go-ipfs + become: yes + unarchive: + remote_src: yes + src: "{{ dist_url }}/go-ipfs/{{ipfs_version}}/go-ipfs_{{ipfs_version}}_linux-{{ipfs_arch}}.tar.gz" + dest: /opt/go-ipfs/{{ipfs_version}} + creates: /opt/go-ipfs/{{ipfs_version}}/go-ipfs + +- name: link go-ipfs executable + become: yes + file: + state: link + owner: root + group: root + dest: /usr/local/bin/ipfs + src: /opt/go-ipfs/{{ipfs_version}}/go-ipfs/ipfs + + +- name: install ipfs init service + become: yes + copy: + src: etc/systemd/system/ipfs.service + dest: /etc/systemd/system/ipfs.service + owner: root + group: root + mode: 0644 + +- name: reload systemd + become: yes + shell: systemctl daemon-reload + +- name: make ipfs group + become: yes + group: + state: present + name: ipfs + +- name: make ipfs user + become: yes + user: + state: present + name: ipfs + group: ipfs + shell: /bin/bash + home: /home/ipfs + comment: IPFS user + system: yes + +- name: copy default config + become: yes + become_user: ipfs + template: + src: home/ipfs/ipfs_default_config + dest: /home/ipfs/ipfs_default_config + owner: ipfs + group: ipfs + mode: 0644 + notify: + - restart ipfs + +- name: init IPFS + become: yes + become_user: ipfs + command: ipfs init --empty-repo -- /home/ipfs/ipfs_default_config + args: + creates: /home/ipfs/.ipfs/config + +- name: enable and start IPFS + become: yes + service: + name: ipfs + state: restarted + enabled: yes diff --git a/roles/ipfs/templates/home/ipfs/ipfs_default_config b/roles/ipfs/templates/home/ipfs/ipfs_default_config new file mode 100644 index 0000000..8ec356e --- /dev/null +++ b/roles/ipfs/templates/home/ipfs/ipfs_default_config @@ -0,0 +1,82 @@ +{ + "API": { + "HTTPHeaders": null + }, + "Addresses": { + "API": "/ip4/127.0.0.1/tcp/5001", + "Gateway": "/ip4/127.0.0.1/tcp/8080", + "Swarm": [ + "/ip4/0.0.0.0/tcp/4001" + ] + }, + "Bootstrap": [ + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", + "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", + "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", + "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", + "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", + "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx" + ], + "Datastore": { + "BloomFilterSize": 0, + "GCPeriod": "1h", + "HashOnRead": false, + "NoSync": false, + "Params": null, + "Path": "/home/ipfs/.ipfs/datastore", + "StorageGCWatermark": 90, + "StorageMax": "10G", + "Type": "leveldb" + }, + "Discovery": { + "MDNS": { + "Enabled": true, + "Interval": 10 + } + }, + "Gateway": { + "HTTPHeaders": { + "Access-Control-Allow-Headers": [ + "X-Requested-With" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "PathPrefixes": [], + "RootRedirect": "", + "Writable": false + }, + "Identity": { + "PeerID": "{{ ipfs_peer_id }}", + "PrivKey": "{{ ipfs_private_key }}" + }, + "Ipns": { + "RecordLifetime": "", + "RepublishPeriod": "", + "ResolveCacheSize": 128 + }, + "Mounts": { + "FuseAllowOther": false, + "IPFS": "/ipfs", + "IPNS": "/ipns" + }, + "Reprovider": { + "Interval": "12h" + }, + "SupernodeRouting": { + "Servers": null + }, + "Swarm": { + "AddrFilters": null + }, + "Tour": { + "Last": "1.2" + } +}