🚥

VPSに初めてAPIデプロイしてみた!

2023/11/08に公開

はじめてgoで作成したアプリケーションをVPSにデプロイしました。
それらの手順をまとめましたので、誰かのご参考になれば幸いです。

VPSにgoで作成したAPIを公開する

概要

  • goで作成したAPIをhttps通信でアクセスできるように公開する
  • VPSを借りて、Dockerで各サーバを立ち上げる
  • Vercelにデプロイしているクライアント側のアプリケーションと連携する

背景

デプロイ先としてAWSなどのクラウドサービスでは個人開発のサービスを稼働させるには、コストが高くつくため、VPSにデプロイすることにしました。
VPSにAPIをデプロイするという日本語の記事が少なく色々苦戦したので、デプロイ手順を記事として残したいと思います!

今回デプロイするサービスについて

大まかな手順

  1. VPSを借りる
  2. VPSの設定をする(ssh,セキュリティ等)
  3. Docker,Gitのインストール
  4. ドメイン名取得
  5. docker-compose.ymlを作成
  6. git cloneしてアプリケーションを配置
  7. Dcoker-compseを起動

VPSを借りよう!

まずどのVPSを借りるか選定する必要があります。
日本のVPS業者ですと、さくらインターネット、conoHaVPS、XServerなどありますが、私はコスパ的にContabo VPSを選択しました。
月大体1000円以下でそこそこいいスペックのサーバを借りることができます。
他のVPSですと、同じスペックのものを借りようと思うと2000~3500円/月ぐらいかかります!

購入手順

早速VPSを購入していきます!
まずは何個サーバを借りるか、何カ月借りるかを選択します。

セットアップ代として、月々のお金とは別途お金がかかりますが、借りる期間を12カ月にするとセットアップ代が無料になります!
また、借りる期間によってセットアップ代が割引されます。(私はとりあえず3カ月を選択)
次にリージョンの選択ですが、ドイツ以外は追加料金がかかるためドイツを選択します。

続いて、ストレージとOSイメージを選択します。

ストレージは追加料金がない200GB(SSD)か50GB(NVMe)を選択します。

OSに関しては何でもいいかと思いますが、UbuntuかRedHat系(CentOS, AlmaLinux)を選択するといいと思います!(参考記事、ドキュメントが充実しているため)
私はAlmaLinuxを選択しました。

rootユーザーのパスワードを設定します。

あとはネットワークとオプションの設定がありますが、デフォルトのまま購入に進んでください

VPSの設定

さてVPSの購入ができたので、サーバに接続して色々設定していきます。
まずは先ほど作成したrootユーザーでssh接続します。

sshで接続

コマンドプロンプトでもPowerShellでもなんでもいいので
以下のコマンドを叩きます。パスワードを聞かれるので、先ほどのパスワードを入力してください。

> ssh -l root [IP-address]

sudo権限を持つユーザーを作成する

強力な権限をもつrootユーザーで作業したり、サーバに接続するのはセキュリティ的によろしくありません。そのため一般ユーザーを作成し、そのユーザーにsudo権限を付与します。
今後の作業、アクセスはそのユーザーで行い、rootユーザーがsshで接続できないように設定します。

一般ユーザーの作成

$ useradd [username]
# パスワードを設定
$ passwd [username]
# 確認
$ id [username]
uid=1000(tkruser) gid=1000(tkruser) groups=1000(tkruser)

sudo権限を付与

以下のコマンドでsudoersファイルの設定を確認します。

$ visudo

sudoersファイルの以下部分が同じようになっていることを確認する

/etc/sudoers
## Allows people in group wheel to run all commands
%wheel  ALL=(ALL)       ALL

先ほど作成した一般ユーザーにsudo権限を付与します。

$ usermod -aG wheel [username]
# 確認
$ id [username]
uid=1000(tkruser) gid=1000(tkruser) groups=1000(tkruser),10(wheel)

rootユーザーのssh接続を禁止する

rootユーザーのssh接続を禁止する前に先ほど作成した一般ユーザーでssh接続できるか確認します。

> ssh -l [username] [IP-address]

接続できることが確認できたらrootユーザーでssh接続できないように設定ファイルを変更していきます。sshd_configファイルのPermitRootLoginをnoに変更する。

$ sudo vi /etc/ssh/sshd_config

デフォルトでは以下のようになっている部分を

sshd_config
#PermitRootLogin yes

以下のように変更します。

sshd_config
PermitRootLogin no

変更できたらsshdを再起動して変更を反映させます。

$ sudo systemctl restart sshd

rootユーザーでssh接続できないことを確認してください。

> ssh -l root [IP-address]
Permission denied, please try again.

公開鍵でssh接続する

パスワードが漏洩してしまうと誰でもサーバへアクセスできてしまうため、パスワード認証によるssh接続もセキュリティによろしくありません。
そのためパスワード認証によるssh接続を禁止して、代わりに公開鍵によるssh接続でサーバに接続するように設定します。

ローカル側で公開鍵/秘密鍵生成&sshの設定

まずはローカル側で公開鍵/秘密鍵を生成する必要があります。
以下のコマンドらはコマンドプロンプトで実行してください。
%USERPROFILE%.sshに移動し鍵を生成します。

> cd .ssh
# 鍵の生成
> ssh-keygen.exe -q -t ed25519 -C "" -N "" -f [key-name]

サーバに公開鍵を配置します。

> Get-Content $env:USERPROFILE\.ssh\[key-name].pub | ssh [username]@[IP-address] "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys || exit 1"

sshのconfigファイルを作成し、設定します。

> vim .ssh/config
.ssh/config
Host contabo
    HostName [IP-address]
    port 22
    User [username]
    IdentityFile C:\Users\[ローカル端末のユーザー名]\.ssh\[key-name]

設定できたところで、公開鍵でssh接続できるか確認します。

> ssh contabo

パスワード認証でのssh接続を禁止する

公開鍵でssh接続できるか確認出来れば、次はパスワード認証を許可しないようにssh接続の設定を変更します。ここからはサーバ側で行います。
設定ファイルのパスワード認証を許可している部分をnoにします。

$ sudo vi /etc/ssh/sshd_config
/etc/ssh/sshd_config
PasswordAuthentication no

sshdを再起動して、変更を反映させます。

$ sudo systemctl restart sshd

パスワード認証でのssh接続ができないようになっているか確認します。

> ssh -l [username] [IP-address]
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

SSHの使用ポートを変更する

一般的にsshに使用されるポート番号は22番です。そのため攻撃の対象とされることが多いためセキュリティリスク軽減のため使用ポートを変更します。
well-knownポート(0~1023),レジスタードポート(1024~49151)以外の49152~65535の間で設定します。

サーバ側

まずはサーバ側の設定ファイルを変更します。

$ sudo vi /etc/ssh/sshd_config
/etc/ssh/sshd_config
Port [任意の番号]

再起動して変更を反映させます。

$ sudo systemctl restart sshd

ローカル側

次はローカル側の設定ファイルを変更します。
先ほど作成した.ssh/configのポート番号の部分を変更します。

> vim .ssh/config
.ssh/config
Host contabo
    HostName [IP-address]
    port [任意の番号]
    User [username]
    IdentityFile C:\Users\[ローカル端末のユーザー名]\.ssh\[key-name]

これでポート変更完了です。ssh接続して、正常に接続できるか確認してください

> ssh contabo

ファイアウォールの設定

ファイアウォールの設定をしていきます。
ファイアウォールは許可する通信と拒否する通信を設定し、悪意のあるユーザの通信からサーバーを守ることができます。
今回はhttp,https,sshと各使用ポートを許可する設定を追加します。
まずはファイアウォールの状態を確認します。

$ sudo systemctl status firewalld

Active: inactive (dead)となっている場合はファイアウォールが無効になっています。
デフォルトでは無効になっていると思うので、有効化します。

ファイアウォールの有効化

$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld

ステータスがActive: active (running)となっていればファイアウォールが有効になっています。

許可されているプロトコルの確認

まずは現状、許可されているポート、プロトコルを確認します。

# 許可されているプロトコルの確認
$ sudo firewall-cmd --list-services --zone=public --permanent
# 許可されているポートの確認
$ sudo firewall-cmd --list-ports --zone=public --permanent

特定のポート、プロトコルだけ許可

現状許可されているポート、プロトコルの確認ができたと思うので、許可されていないhttp,https,sshと各使用ポートを許可しています。

使用ポートを許可

今回許可するポートは80,443,sshのポート番号(先ほど設定した)です。
以下のコマンドで許可することができます。

$ sudo firewall-cmd --add-port=[任意の番号]/tcp --zone=public --permanent

環境に合わせて許可されていないポートを許可してください。

# 例:80,443番を許可
$ sudo firewall-cmd --add-port=443/tcp --zone=public --permanent
$ sudo firewall-cmd --add-port=80/tcp --zone=public --permanent

許可できたら、反映のためにファイアウォールをリロードします。

$ sudo firewall-cmd --reload
http,https,sshを許可

筆者の環境ではsshは既に許可されていたので、httpとhttpの許可をしていきます。

# http,httpsを許可
$ sudo firewall-cmd --add-service=https --zone=public --permanent
$ sudo firewall-cmd --add-service=http --zone=public --permanent
# reloadで反映する
$ sudo firewall-cmd --reload

以上でファイアウォールの設定完了です!
最後にファイアウォールの設定ができているか確認しましょう

# 許可されているプロトコルの確認
$ sudo firewall-cmd --list-services --zone=public --permanent
# 許可されているポートの確認
$ sudo firewall-cmd --list-ports --zone=public --permanent

Docker, Gitのインストール&設定

今回はDockerでコンテナを立てて、各サーバを構築するのでdocker, docker-composeのインストールを行います。また、サーバにアプリケーションのコードを配置するためにgitを使用するためこちらもインストールします。

Gitのインストール

gitをインストールします。また後から使うwgetも一緒にインストールしておきます。

$ dnf -y install git wget

Gitの設定

githubからサーバに自身のリポジトリからコードを取得したいので、必要な設定を行います。
以下のコマンドでGithubユーザ名、emailアドレスを登録します。

$ git config --global user.name [Githubユーザ名]
$ git config --global user.email [Github Emailアドレス]

githubとsshするために公開鍵/秘密鍵の生成と自身のgithubリポジトリに公開鍵を登録する必要があります。
まずは公開鍵/秘密鍵の生成を行います。

公開鍵/秘密鍵生成

$ ssh-keygen -t ed25519 -f ~/.ssh/[key-name]
# 権限付与
$ sudo chmod 600 ~/.ssh/[key-name(秘密鍵のファイル)](.pubが付いてない方)

Github側の設定

github.comに公開鍵を追加します。
以下のコマンドで公開鍵の内容を出力します。
出力されたものをコピーしてください。

$ cat ~/.ssh/[key-name].pub (公開鍵のファイル)

github.comへログインします。
settingsから

SSH and GPG keysを選択します。

New SSH Keyを押して鍵を追加します。

Keyの部分に先ほどコピーした公開鍵の内容を貼り付けます。Add SSH Keyを押して鍵を追加し、完了です!

GitHubのSSH接続情報を登録

sshの設定ファイルを作成します。

$ vi ~/.ssh/config

以下のように設定します。

~/.ssh/config
Host github
HostName github.com
IdentityFile ~/.ssh/[key-name(秘密鍵のファイル)]
User git

設定が上手くできているか確認します。
以下のコマンドを実行して、同じようなメッセージが出力されればOKです!

$ ssh -T github
Hi [ユーザ名]! You've successfully authenticated, but GitHub does not provide shell access.

Dockerのインストール

Dockerのリポジトリを登録してそこからインストールするという方法が推奨されているので、その方法でインストールしていきます。

Docker Engineのインストール

# Dockerのリポジトリを登録
$ sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
# Docker Engineのインストール
$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

インストールができたら起動して確認しましょう
Hello from Docker!が出力されればOKです!

# docker 起動
$ sudo systemctl start docker
# 起動確認
$ sudo docker run hello-world
Hello from Docker!

Docker-compose インストール

続いて、docker-composeのインストールをします。
docker-composeのバージョンが表示されたらインストール成功です!

# docker-compose インストール
$ sudo wget -O /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/v2.4.1/docker-compose-linux-x86_64
$ sudo chmod +x /usr/local/bin/docker-compose
# docker-compose 確認
$ docker-compose --version

Dockerの設定

Dockerデーモンは常にrootユーザーとして実行されます。
一般ユーザーでDockerを管理したいので、dockerというUnixグループを作成し、そこにユーザーを追加します。

# docker グループ追加
$ sudo usermod -aG docker [user-name]
# 変更を有効にする
$ newgrp docker

最後にDockerが自動起動するように設定します。

# docker自動起動
$ sudo systemctl enable docker

ドメイン名取得

Let's Encryptを使用したSSL証明書発行ではドメイン名が必要であるため、ドメイン名を取得します。
ドメイン名が取得できればどの業者でもいいです。(お名前ドットコムなど)
私はXServerDomainでドメインを取得しました。
適当に名前を検索して空いているドメイン名を取得してください。
このドメイン名は自身だけが分かっていればいいものなので、特段気を使う必要はありません。

DNS Zoneに登録

ドメイン名が取得できたらDNS Zoneにドメインを登録して、ドメイン名とIPアドレスを紐づけます。
Contaboの管理コンソール画面のDNS Zone Managementに遷移します。
Domainに取得したドメイン名を、Target IP addressでサーバのIPアドレスを選択して、create zoneを押すことで登録できます。

ネームサーバーの登録

次にネームサーバーとドメイン名を紐づけます。
XServerDomainにログインして個人ページに遷移します。
ネームサーバーの設定からContaboのネームサーバーを登録してください。

確認

以下のサイトでドメイン名を入力することで適切に設定できているか確認できます。
https://dnschecker.org/
また、コマンドプロンプトでも確認できます。

> nslookup [Domain Name]

docker-compose.ymlの作成

docker-compose.ymlに今回必要なコンテナの設定を記述していきます。

docker-compose.yml を書く

docker-compose.yml
docker-compose.yml
version: '3.1'
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    privileged: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro
      - ./vhost.d:/etc/nginx/vhost.d
      - ./html:/usr/share/nginx/html
    labels:
      com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
    networks:
      - my-network
  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt-nginx
    depends_on:
      - nginx-proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./certs:/etc/nginx/certs:rw
      - ./vhost.d:/etc/nginx/vhost.d
      - ./html:/usr/share/nginx/html
    networks:
     - my-network
  app:
    container_name: backend_app
    build: 
      context: .
      dockerfile: Dockerfile
    env_file:
      - .env
    environment:
      - TZ=Asia/Tokyo
      - VIRTUAL_HOST=xxxxx.com [取得したドメイン名に置き換えてください]
      - LETSENCRYPT_HOST=xxxxx.com [取得したドメイン名に置き換えてください]
      - LETSENCRYPT_EMAIL=xxxxx@gmail.com [ドメインに登録してるメールアドレス]
    volumes:
      - ./src:/go/src/
    tty: true
    depends_on:
      - nginx-proxy
      - mongo
      - redis
    networks:
      - my-network
  mongo:
    container_name: mongo
    image: mongo
    restart: always
    env_file:
      - .env
    expose:
      - "27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE}
      TZ: Asia/Tokyo
    volumes:
      - ./mongo/data:/data/db
    networks:
      - my-network
  redis:
    image: redis
    env_file:
      - .env
    environment:
      - TZ=Asia/Tokyo
    expose:
      - "6379"
    restart: always
    volumes:
      - ./redis/data:/data
      - ./redis/redis.conf:/redis.conf
    networks:
      - my-network
networks:
  my-network:
    driver: bridge
volumes:
  certs:
  vhost.d:
  html:
  mongo:
  redis:

Nginxについて

Nginx(エンジンエックス)は主にリバースプロキシサーバー、Webサーバーとして使用されています。
今回はクライアントアプリケーションとhttps通信するためのHTTPSリバースプロキシとして使用します。

jwilder/nginx-proxyについて

上記のリバースプロキシサーバーを簡単に構築できるDocker-Imageです。
本来であれば、色々複雑な設定をしなければならないのですが、ポンと簡単に構築できてしまいます!
Let's Encryptと組み合わせて使用することで、自動的にSSL証明書を取得し、セキュアなHTTPS接続を提供します。
https://hub.docker.com/r/jwilder/nginx-proxy

Let's Encryptについて

無料で自動でSSL/TLS証明書を提供してくれる認証機関(CA)です。
https://letsencrypt.org/ja/
今回はjrcs/letsencrypt-nginx-proxy-companionというDocker-Imageを使用することで、Let's EncryptでSSL証明書を発行してもらいます。
https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion/
docker-compose.ymlの作成が完了したら、githubにpushします。

サーバにコードを配置する

githubからアプリケーションのコードをサーバに配置します。

# git clone (初回)
$ git clone -b [任意のブランチ] ssh://github/[githubユーザ名]/[任意のリポジトリ名].git
# git pull (2回目以降)
$ git pull origin [任意のブランチ]

初回はgit cloneを使用してください。コードの変更があってその変更を取り込みたいときはgit pullを使用します。

起動!

各コンテナで使用する環境変数を.envで設定していきます。

$ vi ~/[任意のディレクトリ]/.env
.env
MONGODB_USERNAME=XXXXX
MONGODB_PASSWORD=XXXXXX
MONGO_INITDB_DATABASE=XXXXXX
...

それでは全ての準備が完了したので、各コンテナを起動してみましょう!
各コンテナが正常に起動してることを確認できたら、成功です!🎉

$ docker-compose up -d
# 確認
$ docker-compose ps

お疲れ様でした。これでAPIのデプロイ作業は完了です!
クライアント側のアプリケーションをデプロイしている場合は、正常にAPIと通信が行えているか確認しましょう!

最後に

初めてのVPSへのデプロイ作業&技術ブログ執筆であったため、多くの時間をかけましたがいい勉強になったと思います。

参考にさせていただいたサイト

https://mebee.info/2021/04/23/post-33176/
https://nishinatoshiharu.com/initial-vps-security-settings/
https://qiita.com/overflowfl/items/14a2486df85fd7efac85
https://zenn.dev/su8ru/articles/vultr-nginx-proxy-frourio
https://tech.shiroshika.com/webarena-multidomain-django/
https://higmasan.com/docker/almalinux-9-1にdockerをインストールする/
https://create-it-myself.com/know-how/clone-github-private-ripogitory-on-conoha-vps/

Discussion