Packer, Terraform Ansible 및 Docker가 있는 개인 DNS 및 VPN 노드
내 개인 노드는 Hetzner에 위치하고 CX11 클라우드 실례 유형을 사용한다.VCPU 1개 및 2GB RAM을 기반으로 하며 20GB SSD 및 20TB 대역폭을 제공합니다.모두 2.66유로(부가가치세 포함)입니까?
나는 이 설정에 사용되는 도구에 대해 기본적으로 알고 있다고 가정할 것이다.그래서 우리가 해야 할 첫 번째 일은 기본적인 이미지 스냅샷을 구축하는 것이다. 우리는 이를 바탕으로 우리의 응용 서버 이미지를 구축할 것이다. 나는 JARVIS라고 부른다.이를 위해 Packer를 사용하겠습니다.나는 Ansible 부분에 대해 토론할 생각이 없다. 왜냐하면 그것은 네가 스냅샷에 무엇을 표시하느냐에 달려 있기 때문이다. 너는 직접
playbook_file
너의 대본을 가리킬 수 있기 때문이다.포장공
기본 이미지
기본 이미지를 작성하려면 Hetzner API 태그를 생성하고 환경 변수로 설정해야 합니다.이 설계도는 Debian 10에 이미지를 생성하여
debian-base-snapshot
로 저장합니다.주의해야 할 두 가지는 location
와 server_type
를 사용하여 최종 서버를 실행해야 한다는 것입니다.ansible playbook에서 저를 위해 설치한 것은 ssh 키를 복제하고 docker와 제가 필요로 하는 개발 도구를 설치하는 것입니다.구축하려면 다음 명령을 실행하십시오
packer build packer.json
{
"variables": {
"hcloud_token": "{{env `HCLOUD_TOKEN`}}"
},
"builders": [
{
"token": "{{ user `hcloud_token` }}",
"server_name": "base-packer",
"snapshot_name": "debian-base-snapshot",
"snapshot_labels": { "name": "debian-base-snapshot" },
"type": "hcloud",
"image": "debian-10",
"location": "nbg1",
"server_type": "cx11",
"ssh_username": "root"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 30",
"apt-get update",
"apt-get -y upgrade",
"apt-get update && apt-get install -y wget curl gcc make python python-dev python-setuptools python-pip libffi-dev libssl-dev libyaml-dev"
]
},
{
"type": "ansible",
"extra_arguments": ["--vault-password-file=~/.helsing_ansible_vault_pass"],
"playbook_file": "../../../../ansible/base.yml"
}
]
}
가비스 이미지
기본 스냅숏이 생기면, 헤츠나가 제공한 스냅숏 대신 서버 스냅숏을 구축할 것입니다.이전 단계에서 사용한 동일한 영패를 사용할 수 있습니다.이 레시피는 기본 스냅숏에 사용되는 레시피와 매우 비슷하다.이 그림에 필요한 물건을 자신의 플레이북으로 설치합니다. 제가 하는 일 중 하나는 이 단계에서git복제원repo를 사용하는 것입니다.
packer build packer.json
{
"variables": {
"hcloud_token": "{{env `JARVIS_HCLOUD_TOKEN`}}"
},
"builders": [
{
"token": "{{ user `hcloud_token` }}",
"server_name": "jarvis-packer",
"snapshot_name": "debian-jarvis-snapshot-base",
"type": "hcloud",
"image_filter": {
"with_selector": [
"name==debian-base-snapshot"
],
"most_recent": true
},
"snapshot_labels": { "name": "debian-jarvis-snapshot-base" },
"location": "nbg1",
"server_type": "cx11",
"ssh_username": "root"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 30",
"apt-get update",
"apt-get -y upgrade",
"apt-get update && apt-get install -y wget curl gcc make python python-dev python-setuptools python-pip libffi-dev libssl-dev libyaml-dev"
]
},
{
"type": "ansible",
"extra_arguments": ["--vault-password-file=~/.helsing_ansible_vault_pass"],
"playbook_file": "../../../../ansible/jarvis.yml"
}
]
}
지형.
내가 Terraform을 마지막으로 사용한 것은 설치할 때였다. 당시의 최신 버전은
0.12
였기 때문에 최신 버전0.14.8
으로 업그레이드해서 설치해야 했다version.tf
version.tf
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.25.2"
}
}
required_version = "~> 0.14"
}
다음은 앞서 만든 스냅샷을 사용한 인프라를 만드는 방법입니다.Hetzner는 최근 관리 포트를 단순화하는 제품Firewall
을 출시했습니다.이것은 cx11 서버의 실례와 방화벽을 만들 것입니다. 이 방화벽은 유량에 사용할 정의된 규칙을 첨부합니다.provider.tf
tarraform plan -out=jarvis.out
tarraform apply "jarvis.out"
variable "JARVIS_HCLOUD_TOKEN" {}
provider "hcloud" {
token = var.JARVIS_HCLOUD_TOKEN
}
data "hcloud_image" "jarvis_image" {
with_selector = "name=debian-jarvis-snapshot-base"
}
data "hcloud_ssh_keys" "all_keys" {
}
resource "hcloud_firewall" "jarvis_firewall" {
name = "jarvis-firewall"
rule { // SSH
direction = "in"
protocol = "tcp"
port = "22"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // HTTP
direction = "in"
protocol = "tcp"
port = "80"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // DNS
direction = "in"
protocol = "udp"
port = "53"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // DNS
direction = "in"
protocol = "tcp"
port = "53"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // HTTPS
direction = "in"
protocol = "tcp"
port = "443"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // WIREGUARD
direction = "in"
protocol = "udp"
port = "52828"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // GRAFANA
direction = "in"
protocol = "tcp"
port = "13443"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // SCOPE
direction = "in"
protocol = "tcp"
port = "15443"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
rule { // PORTAINER
direction = "in"
protocol = "tcp"
port = "19443"
source_ips = [
"0.0.0.0/0",
"::/0"
]
}
}
resource "hcloud_server" "jarvis_server" {
name = "jarvis-hetzner"
image = data.hcloud_image.jarvis_image.id
server_type = "cx11"
labels = { "name" = "jarvis-hetzner" }
location = "nbg1"
ssh_keys = data.hcloud_ssh_keys.all_keys.ssh_keys.*.name
firewall_ids = [hcloud_firewall.jarvis_firewall.id]
}
이것은 우리의 인프라 시설을 정리했다. 현재 중요한 부분은 우리가 무엇을 운행하고 어떻게 운행하는지이다.Docker Compose를 사용하여 12개의 서비스를 실행합니다.Docker Compose
우선, docker compose를 만들어야 합니다.yml 파일, 용기 이미지를 구축한 다음, 이 이미지를 표시하고, 이를 등록표에 전송해서 실행할 수 있도록 합니다.
이 파일은 각 이미지에 필요한 모든 구성 및 설정 이미지를 생성하는 데 사용되며 레지스트리에 레이블을 지정하고 밀어넣기 전에 첫 번째 단계로 이미지를 실행할 수 있습니다.이 파일을 docker composeci라고 부릅니다.yml와 우리가 구축한 이미지는 다음과 같다
docker-compose -f docker-compose-ci.yml build
version: "3.7"
services:
#OPENRESTY 10.0.3.2
openresty:
build:
context: ./openresty
dockerfile: ./Dockerfile
image: jarvis/openresty
#HAPROXY 10.0.3.6
haproxy:
build:
context: ./haproxy
dockerfile: ./Dockerfile
args:
BASIC_AUTH_USERNAME: ${BASIC_AUTH_USERNAME}
BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD}
BASIC_AUTH_REALM: ${BASIC_AUTH_REALM}
HAPROXY_HTTP_SCHEME: ${HAPROXY_HTTP_SCHEME}
HAPROXY_STATS_URI: ${HAPROXY_STATS_URI}
HAPROXY_STATS_REFRESH: ${HAPROXY_STATS_REFRESH}
image: jarvis/haproxy
#PIHOLE 10.0.3.3
pihole:
build:
context: ./pihole
dockerfile: ./Dockerfile
args:
TZ: ${TZ}
WEBPASSWORD: ${WEBPASSWORD}
DNS1: 10.0.3.4#5053 # cloudflared IP Address
DNS2: 10.0.3.5#5053 # DNSCrypt IP Address
image: jarvis/pihole
#CLOUDFLARED 10.0.3.4
cloudflared:
build:
context: ./cloudflared
dockerfile: ./Dockerfile
args:
TZ: ${TZ}
TUNNEL_DNS_UPSTREAM: ${TUNNEL_DNS_UPSTREAM}
image: jarvis/cloudflared
#DNSCRYPT 10.0.3.5
dnscrypt:
build:
context: ./dnscrypt
dockerfile: ./Dockerfile
image: jarvis/dnscrypt
#WIREGUARD 10.0.3.7
wireguard:
build:
context: ./wireguard
dockerfile: ./Dockerfile
args:
PUID: ${PUID}
PGID: ${PGID}
TZ: ${TZ}
PEERS: ${PEERS}
PEERDNS: 10.0.3.3#53 #pi-hole IP Address
image: jarvis/wireguard
#GRAFANA 10.0.3.8
grafana:
build:
context: ./grafana
dockerfile: ./Dockerfile
args:
GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD}
image: jarvis/grafana
#LOKI 10.0.3.9
loki:
build:
context: ./loki
dockerfile: ./Dockerfile
image: jarvis/loki
#PROMTAIL 10.0.3.10
promtail:
build:
context: ./promtail
dockerfile: ./Dockerfile
image: jarvis/promtail
#SCOPE 10.0.3.11
scope:
build:
context: ./scope
dockerfile: ./Dockerfile
args:
ENABLE_BASIC_AUTH: ${ENABLE_BASIC_AUTH}
BASIC_AUTH_USERNAME: ${BASIC_AUTH_USERNAME}
BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD}
image: jarvis/scope
#PROMETHEUS 10.0.3.12
prometheus:
build:
context: ./prometheus
dockerfile: ./Dockerfile
image: jarvis/prometheus
#PORTAINER 10.0.3.13
portainer:
build:
context: ./portainer
dockerfile: ./Dockerfile
image: jarvis/portainer
일단 우리가 모든 이미지를 구축하고, 표시하고, 전송하면 (배치를 볼 때, 우리는 표시와 전송 부분에 들어갈 것이다. 나는 Gitlab repos를 사용하기 때문에 개인 등록표를 가지고 있다.)우리는 모든 서비스를 시작하기 위해 docker compose와 정확한 포트, 볼륨, 명령 맵을 사용합니다.서류를 한 번 통독해서 좀 편안하게 해라docker-compose up
version: "3.7"
services:
#OPENRESTY 10.0.3.2
openresty:
image: registry.gitlab.com/jarvis/openresty:amd64
container_name: jarvis_openresty
networks:
network:
ipv4_address: 10.0.3.2
aliases:
- jarvis_openresty
depends_on:
- haproxy
expose:
- "80"
- "443"
ports:
- "80:80"
- "443:443"
- "13443:13443"
- "15443:15443"
- "19443:19443"
volumes:
- ./openresty/ssl:/usr/local/openresty/nginx/conf/config/ssl
#HAPROXY 10.0.3.6
haproxy:
image: registry.gitlab.com/jarvis/haproxy:amd64
container_name: jarvis_haproxy
networks:
network:
ipv4_address: 10.0.3.6
aliases:
- jarvis_haproxy
depends_on:
- pihole
- grafana
- prometheus
- scope
expose:
- "80"
- "18081"
- "18443"
- "13000"
- "15000"
- "19000"
#PIHOLE 10.0.3.3
pihole:
image: registry.gitlab.com/jarvis/pihole:amd64
container_name: jarvis_pihole
volumes:
- "pihole_data:/etc/pihole"
- "pihole_dnsmasq_data:/etc/dnsmasq.d"
- "/dev/null:/var/log/pihole.log:ro"
depends_on:
- cloudflared
- dnscrypt
expose:
- "80/tcp"
- "67/udp"
ports:
- "53:53/tcp"
- "53:53/udp"
environment:
- DNSMASQ_LISTENING=all
- IPv6=false
- PIHOLELOG=/dev/null
networks:
network:
ipv4_address: 10.0.3.3
aliases:
- jarvis_pihole
dns:
- 127.0.0.1
- 1.1.1.1
cap_add:
- NET_ADMIN
#CLOUDFLARED 10.0.3.4
cloudflared:
image: registry.gitlab.com/jarvis/cloudflared:amd64
container_name: jarvis_cloudflared
expose:
- "49312/tcp"
- "5053/udp"
networks:
network:
ipv4_address: 10.0.3.4
aliases:
- jarvis_cloudflared
#DNSCRYPT 10.0.3.5
dnscrypt:
image: registry.gitlab.com/jarvis/dnscrypt:amd64
container_name: jarvis_dnscrypt
expose:
- "5053/tcp"
- "5053/udp"
volumes:
- "dnscrypt_data:/config"
networks:
network:
ipv4_address: 10.0.3.5
aliases:
- jarvis_dnscrypt
#WIREGUARD 10.0.3.7
wireguard:
image: registry.gitlab.com/jarvis/wireguard:amd64
container_name: jarvis_wireguard
volumes:
- "wireguard_data:/config"
- "/lib/modules:/lib/modules"
depends_on:
- pihole
ports:
- "52828:51820/udp"
cap_add:
- NET_ADMIN
- SYS_MODULE
networks:
network:
ipv4_address: 10.0.3.7
aliases:
- jarvis_wireguard
depends_on:
- pihole
#GRAFANA 10.0.3.8
grafana:
image: registry.gitlab.com/jarvis/grafana:amd64
container_name: jarvis_grafana
volumes:
- "grafana_data:/var/lib/grafana:rw"
- ./grafana/config/datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml
expose:
- "3000"
networks:
network:
ipv4_address: 10.0.3.8
aliases:
- jarvis_grafana
#LOKI 10.0.3.9
loki:
image: registry.gitlab.com/jarvis/loki:amd64
container_name: jarvis_loki
expose:
- "3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
network:
ipv4_address: 10.0.3.9
aliases:
- jarvis_loki
#PROMTAIL 10.0.3.10
promtail:
image: registry.gitlab.com/jarvis/promtail:amd64
container_name: jarvis_promtail
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
network:
ipv4_address: 10.0.3.10
aliases:
- jarvis_promtail
# SCOPE 10.0.3.11
scope:
image: registry.gitlab.com/jarvis/scope:amd64
container_name: jarvis_scope
networks:
network:
ipv4_address: 10.0.3.11
aliases:
- jarvis_scope
expose:
- "4040"
pid: "host"
privileged: true
labels:
- "works.weave.role=system"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:rw"
command:
- "--probe.docker=true"
- "--weave=false"
#PROMETHEUS 10.0.3.12
prometheus:
image: registry.gitlab.com/jarvis/prometheus:amd64
container_name: jarvis_prometheus
volumes:
- "prometheus_data:/var/lib/prometheus:rw"
expose:
- "9090"
networks:
network:
ipv4_address: 10.0.3.12
aliases:
- jarvis_prometheus
#PORTAINER 10.0.3.13
portainer:
image: registry.gitlab.com/jarvis/portainer:amd64
container_name: jarvis_portainer
restart: always
networks:
network:
ipv4_address: 10.0.3.13
aliases:
- jarvis_portainer
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
expose:
- "9000"
networks:
network:
driver: bridge
ipam:
config:
- subnet: 10.0.3.0/24
volumes:
wireguard_data: {}
pihole_data: {}
pihole_dnsmasq_data: {}
cloudflared_data: {}
dnscrypt_data: {}
grafana_data: {}
prometheus_data: {}
portainer_data: {}
자동화 배치 단계를 위해서, 서버에 들어가서 모든 docker 명령을 실행할 필요가 없습니다. 아주 좋은 OSS를 찾았습니다. Stack Up
긴박히 / 훌쩍거리다
아주 간단한 배치 도구 - 서버 네트워크의 "make"로 상상
쌓이다
Stack Up은 여러 호스트에서 지정된 명령 세트를 병렬로 실행할 수 있는 간단한 배포 도구입니다.이 파일은 네트워크(호스트 그룹), 명령 및 대상을 정의하는 YAML 구성 파일입니다.
데모
참고: 데모 기반this example Supfile.
장치 $ go get -u github.com/pressly/sup/cmd/sup
사용법 $ sup [OPTIONS] NETWORK COMMAND [...]
옵션
선택권
묘사-f Supfile
Supfile에 대한 사용자 지정 경로-e
, --env=[]
환경 변수 설정--only REGEXP
regexp와 일치하는 호스트 선별--except REGEXP
regexp와 일치하는 호스트 선별--debug
, -D
디버그/상세 모드 사용--disable-prefix
호스트 이름 접두어 비활성화--help
, -h
도움말/사용 표시--version
, -v
인쇄판
네트워킹
한 무리의 주인.# Supfile
networks
production
hosts:
- api1.example.com
- api2.example.com
- api3.example.com
staging:
# fetch dynamic list of hosts
inventory: curl http://example.com/latest/meta-data/hostname
참고: 데모 기반this example Supfile.
장치 $ go get -u github.com/pressly/sup/cmd/sup
사용법 $ sup [OPTIONS] NETWORK COMMAND [...]
옵션
선택권
묘사-f Supfile
Supfile에 대한 사용자 지정 경로-e
, --env=[]
환경 변수 설정--only REGEXP
regexp와 일치하는 호스트 선별--except REGEXP
regexp와 일치하는 호스트 선별--debug
, -D
디버그/상세 모드 사용--disable-prefix
호스트 이름 접두어 비활성화--help
, -h
도움말/사용 표시--version
, -v
인쇄판
네트워킹
한 무리의 주인.# Supfile
networks
production
hosts:
- api1.example.com
- api2.example.com
- api3.example.com
staging:
# fetch dynamic list of hosts
inventory: curl http://example.com/latest/meta-data/hostname
$ go get -u github.com/pressly/sup/cmd/sup
$ sup [OPTIONS] NETWORK COMMAND [...]
옵션
선택권
묘사
-f Supfile
Supfile에 대한 사용자 지정 경로-e
, --env=[]
환경 변수 설정--only REGEXP
regexp와 일치하는 호스트 선별--except REGEXP
regexp와 일치하는 호스트 선별--debug
, -D
디버그/상세 모드 사용--disable-prefix
호스트 이름 접두어 비활성화--help
, -h
도움말/사용 표시--version
, -v
인쇄판네트워킹
한 무리의 주인.
# Supfile networks production hosts: - api1.example.com - api2.example.com - api3.example.com staging: # fetch dynamic list of hosts inventory: curl http://example.com/latest/meta-data/hostname
$ sup production COMMAND
will run COMMAND on api1
, api2
and…
It makes things quite convenient, it is similar to ansible in a sense that you just have to define instructions in YAML and it executes it over SSH. Always remember no matter what new kid on the block software there might be BASH is always the King of the hood. In below file you can see different stages defined that can be grouped together in a target to create a pipeline. Its a quick and simple CI/CD pipeline that you can control from your command line.
version: 0.5
env:
ENV: <set var>
PWD: <set var>
CR_USER: <set var>
CR_PAT: <set var>
CONTAINER_REGISTRY: <set var>
PROJ_ID: <set var>
networks:
production:
hosts:
- root@server
commands:
connect:
desc: Check host connectivity
run: uname -a; date; hostname
once: true
build:
desc: Build Docker image
run: cd $PWD/$PROJ_ID && git pull && source ~/.bashrc && docker-compose -f docker-compose-ci.yml build
once: true
docker_login:
desc: Login to Gitlab container registry
run: docker login $CONTAINER_REGISTRY -u $CR_USER -p $CR_PAT
once: true
docker_tag:
desc: Tag images for CI registry
run: >-
docker images | grep "^${PROJ_ID}_" | awk '{print $1}' | xargs -I {} echo {} |
xargs -I {} docker image tag {} $CONTAINER_REGISTRY/{}:latest
once: true
docker_push:
desc: Push images to CI registry
run: >-
docker images | grep "^${CONTAINER_REGISTRY}" | awk '{print $1}' | xargs -I {}
echo {} | xargs -I {} docker image push {}:latest
once: true
restart:
desc: Restart docker containers
run: systemctl restart docker-compose@$PROJ_ID
once: true
docker_ps:
desc: List docker process
run: sleep 30; docker ps
once: true
test:
desc: test
run: uname -a; date; hostname
once: true
targets:
deploy:
- connect
- build
- docker_login
- docker_tag
- docker_push
- restart
- docker_ps
sup production deploy
지금은 과도한 기록과 감시가 있어 전 세계 어느 곳에서든 운행할 수 있다.Grafana는 대단합니다. 비록 보기만 해도 두려워질 수 있지만, 당신의 삶을 더욱 편안하게 해 줍니다.Loki와promtail은 흐르는 전송 로그에 있어 매우 효율적이며,prometheus를 사용하여HAProxy와OpenResty에서 도량 값을 얻어 로그를 초월하는 좋은 견해를 제공합니다.또한 원한다면 프로메테우스 지표나 로기 일지에 계기판을 설치할 수 있다.하프로시
nginx
로키
용기 모니터링에 있어서 Scope와 Portainer는 모두 지나친 기교이다. P 그러나 나는 Scope의 UI와 도량 표시를 좋아한다. Portainer가 Docker stack에 대한 관리는 비길 데가 없다. 이것은 내가 어떤 이유로도 서버에 접근하지 않고 브라우저에서 용기를 디버깅할 수 있게 한다. 왜냐하면 브라우저에서 실행 중인 용기를 실행할 수 있기 때문이다.
범위
때때로, 우리는 우리가 이렇게 많은 서비스를 운영하는 데 필요한 자원이 얼마나 적고, 놀라운 OSS 커뮤니티와 그들의 공헌에서 어떻게 이렇게 많은 새로운 것을 배울 수 있는지 과소평가할 뿐이다.
나는 네가 이것이 매우 유용하다고 생각하고, 너의 프라이버시를 향상시키고, 싫은 광고와 추적을 막거나, 게임을 배워서, 자신의 클라우드 인프라를 구축하기를 바란다.
로키
용기 모니터링에 있어서 Scope와 Portainer는 모두 지나친 기교이다. P 그러나 나는 Scope의 UI와 도량 표시를 좋아한다. Portainer가 Docker stack에 대한 관리는 비길 데가 없다. 이것은 내가 어떤 이유로도 서버에 접근하지 않고 브라우저에서 용기를 디버깅할 수 있게 한다. 왜냐하면 브라우저에서 실행 중인 용기를 실행할 수 있기 때문이다.
범위
때때로, 우리는 우리가 이렇게 많은 서비스를 운영하는 데 필요한 자원이 얼마나 적고, 놀라운 OSS 커뮤니티와 그들의 공헌에서 어떻게 이렇게 많은 새로운 것을 배울 수 있는지 과소평가할 뿐이다.
나는 네가 이것이 매우 유용하다고 생각하고, 너의 프라이버시를 향상시키고, 싫은 광고와 추적을 막거나, 게임을 배워서, 자신의 클라우드 인프라를 구축하기를 바란다.
때때로, 우리는 우리가 이렇게 많은 서비스를 운영하는 데 필요한 자원이 얼마나 적고, 놀라운 OSS 커뮤니티와 그들의 공헌에서 어떻게 이렇게 많은 새로운 것을 배울 수 있는지 과소평가할 뿐이다.
나는 네가 이것이 매우 유용하다고 생각하고, 너의 프라이버시를 향상시키고, 싫은 광고와 추적을 막거나, 게임을 배워서, 자신의 클라우드 인프라를 구축하기를 바란다.
Reference
이 문제에 관하여(Packer, Terraform Ansible 및 Docker가 있는 개인 DNS 및 VPN 노드), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/darnahsan/personal-dns-and-vpn-node-with-packer-terraform-ansible-and-docker-1bg텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)